Optional capability · SDK main

Specialist routing: one agent front door, many experts behind it

Give JevAgent a team of specialist agents, each with its own instructions, model, and tools, and let Jev hand every request to the one whose job covers it. When no specialist fits, your main agent handles the request, and every run tells you which agent answered.

Write focused agents, let Jev pick the right one

One set of instructions that tries to cover billing, debugging, and release notes does each job less well. Build one focused agent per kind of work, describe what each handles, and your app still talks to just one agent.

At the start of every run, Jev reads the user's request and your specialist descriptions, then picks the one specialist whose job covers the main goal of the request, or none. The chosen specialist handles the entire task with its own instructions, model, tools, and permissions. If none fits, your main agent handles it.

One request, one best-fit agent

Illustrative · no Jev output shown

Request

Refund a double charge

Jev · System 2

Which scope covers the request's main outcome?

  1. Best-fit specialist

    Billing agent runs the whole task

    Uses its own prompt, model, tools, and permissions.

  2. Reserved option · None

    Main JevAgent keeps the task

    Used when no specialist scope covers the main outcome.

This is an illustrative route, not recorded Jev output. Jev chooses its top-ranked option; there is no match threshold. When no single scope covers the request's main outcome, `none` keeps the task on the main JevAgent. Illustrative request: refund a double charge. Jev compares the request's main outcome with specialist scopes. A best-fit specialist (here, billing) runs the whole task using its own prompt, model, tools, and permissions. If none fits, the reserved `none` option keeps the task on the main JevAgent (`agent.response.specialist` is None). No Jev output or probabilities are shown.

Set up specialist routing

Three steps: build each specialist, describe the work it handles, and add the specialists to a JevAgent. Setting up specialists never calls Jev; routing needs a TypeSafe API key only when a run starts.

  1. Build each specialist as an ordinary agent

    A specialist is just a regular agent. Give each one focused instructions and only the tools its job needs. Specialists don't have to share a provider or a model, so you can put a low-cost model on routine work and a stronger one on hard problems.

    Looks up invoices and handles refunds, payment failures, and plan changes.

    Create a focused agent

    Python
    1from vidbyte import Agent, tool 2 3@tool 4def lookup_invoice(invoice_id: str) -> dict[str, str]: 5 """Return the status, amount, and due date for one invoice.""" 6 return billing_api.invoice(invoice_id) 7 8billing = Agent( 9 name="billing", 10 system_prompt=( 11 "You resolve invoices, refunds, and plan changes. " 12 "Always look up the invoice before quoting an amount." 13 ), 14 provider="openai", 15 model_name="gpt-4.1-mini", 16 tools=[lookup_invoice], 17)

    When Jev picks a specialist, that agent handles the request exactly as it would on its own.

  2. Describe what each specialist handles

    Give each specialist a unique name and a description of the work it handles. Jev routes using only the request and these descriptions; it never reads a specialist's instructions and doesn't guess from the name.

    Describe the work, not the persona

    Python
    1from vidbyte import JevSpecialist 2 3specialists = ( 4 JevSpecialist( 5 title="billing", 6 description="Invoices, refunds, payment failures, and plan upgrades or downgrades for an existing account.", 7 agent=billing, 8 ), 9 JevSpecialist( 10 title="debugger", 11 description="Diagnosing errors, stack traces, failed API requests, and unexpected responses from the public API.", 12 agent=debugger, 13 ), 14 JevSpecialist( 15 title="release_notes", 16 description="Writing or editing release notes and changelog entries from merged pull requests.", 17 agent=release_writer, 18 ), 19) 20 21# Weak descriptions Jev cannot route on reliably: 22# description="Billing agent." -> no scope to compare against 23# description="You are a friendly expert. Always..." -> persona instructions, not scope
    Name
    RuleRequired, unique, and never “none”.
    Why“none” is reserved for keeping a request on your main agent.
    Description
    RuleRequired.
    WhyJev reads it on every run to decide where requests go.
    Agent
    RuleAny regular agent.
    WhyThe chosen specialist handles the request just as it would on its own.
    Number of specialists
    RuleUp to 254 per JevAgent.
    WhyOne more slot is reserved for “none”.
  3. Add your specialists to JevAgent

    Add the specialists to your JevAgent's agent settings. The JevAgent's own instructions and model make up your main agent, which answers whenever no specialist fits, so write its instructions for a capable generalist, not a router.

    One front door

    Python
    1from vidbyte import JevAgent, JevAgentSettings, JevRuntimeSettings 2from vidbyte.lib.config import DecisionModelConfig 3 4# load_secret stands in for your own secret store. 5 6agent = JevAgent( 7 JevAgentSettings( 8 name="support-desk", 9 system_prompt=( 10 "You are a general product support agent. Answer questions about the product " 11 "and hand off clearly when you cannot resolve something." 12 ), 13 provider="openai", 14 model_name="gpt-4.1-mini", 15 agents=specialists, 16 ), 17 JevRuntimeSettings( 18 decision=DecisionModelConfig(api_key=load_secret("typesafe")), # or set TYPESAFE_API_KEY 19 ), 20)

    Routing uses Jev, so it needs a TypeSafe API key. With no specialists added, JevAgent skips routing entirely.

What happens when a request comes in

Run your agent as usual. Routing happens once, before any work starts, and it is decided together with any preflight checks you've turned on, so it never adds an extra Jev call. Pick a scenario to trace its path.

If a preflight check stops the run to ask the user a question, no agent runs. Otherwise, if Jev picks a specialist, that specialist handles the request and its reply is returned. Every other path leads to your main agent. Jev only looks at the current request.

Trace one run

Illustrative

“Refund one of the two charges on INV-2291” → Jev ranks billing first. The billing agent runs with its lookup_invoice tool.

  1. 1

    One combined Jev request

    Your enabled preflight questions plus the specialist Choice question, in a single TypeSafe call.

    ↳ missing key, provider error, or no answer → main JevAgent
  2. 2

    Preflight presets

    An enabled preset such as CLARITY can stop the run to ask the user a clarifying question.

    ↳ gate stops → no agent runs
  3. 3

    Catalog check

    With agents=() the request carried no specialist question.

    ↳ empty catalog → main JevAgent
  4. 4

    Jev's top-ranked option

    One specialist title, or the reserved none.

    ↳ none → main JevAgent

Specialist runs the whole task

Runs the task

specialist.agent.arun(prompt) with its own prompt, model, tools, and loop

Main JevAgent loop

JevAgent's own prompt, model, tools, and tool selector

Every exit that leads to the main JevAgent is fail-open: the task still runs, and agent.response.specialist is None. Only a preflight preset can stop a run.

Combine routing with preflight checks

You can turn on preflight checks alongside routing, and all of Jev's questions are still asked together in one go.

  • Clarity check — An unclear request stops before any specialist runs, and the reply is a short list of clarifying questions.

  • Tool selector — Jev trims your main agent's tools for each run. A chosen specialist always keeps its own tools.

Routing plus preflight

Python
1from vidbyte import JevAgent, JevAgentSettings, JevPreflightPreset, JevRuntimeSettings 2 3# search_docs and open_ticket stand in for your own @tool functions. 4 5agent = JevAgent( 6 JevAgentSettings( 7 name="support-desk", 8 system_prompt="You are a general product support agent.", 9 provider="openai", 10 model_name="gpt-4.1-mini", 11 tools=[search_docs, open_ticket], 12 agents=specialists, 13 ), 14 JevRuntimeSettings( 15 preflight=(JevPreflightPreset.CLARITY, JevPreflightPreset.TOOL_SELECTOR), 16 tool_selector_threshold=0.2, # the default; applies to the main agent's tools only 17 ), 18)

See which agent answered

The reply comes back like any other. After each run, the response tells you which specialist handled the task, or that your main agent did, along with Jev's usage and whether the run stopped for clarification.

Inspect the response record

Python
1reply = await agent.arun("I was charged twice for invoice INV-2291. Can you refund one of them?") 2print(reply.content) 3 4response = agent.response 5if response.needs_clarification: 6 print("Jev asked the user to clarify first") 7elif response.specialist is not None: 8 print(f"{response.specialist} answered") 9else: 10 print("the main JevAgent answered") 11 12if response.usage: 13 print("Jev decision tokens:", response.usage.input_tokens, response.usage.output_tokens)
A specialist's name
WhenJev picked that specialist, and it handled the whole task.
None
WhenJev picked none, Jev was unavailable, no specialists are set up, or a preflight check stopped the run.

The response always describes the most recent run. Check whether the run stopped for clarification to tell a stopped run apart from one your main agent handled.

Isolation and failure behavior

Routing is designed so the work always gets done and a task never runs twice.

  • One decision per run — Routing is decided once, before any work starts. Specialists never hand work to each other partway through.

  • No score to tune — Jev's top pick always wins. Clear, narrow descriptions are how you control routing.

  • Fails open — If Jev is unavailable, your main agent handles the task, so your app keeps answering even without a TypeSafe key.

  • No silent retry — If a specialist fails after it starts, you get the error. The task isn't retried on your main agent, because the specialist may already have taken actions.

  • Tools stay separate — Each specialist keeps its own tools. The tool selector only applies when your main agent handles the task.

Example: a support desk with three specialists

One agent fronts billing, API debugging, and account security, and each specialist gets only the tools its job needs. Only the billing specialist can issue refunds, so a request that mixes kinds of work never reaches that tool: it goes to the main agent, which has no tools.

support_desk.py

Python
1import asyncio 2 3from vidbyte import Agent, JevAgent, JevAgentSettings, JevRuntimeSettings, JevSpecialist, ToolPermission, tool 4from vidbyte.tools.security import PermissionPolicy 5 6# billing_api, logs_api, and auth_api stand in for your own service clients. 7 8@tool 9def lookup_invoice(invoice_id: str) -> dict[str, str]: 10 """Return the status, amount, and due date for one invoice.""" 11 return billing_api.invoice(invoice_id) 12 13@tool(permission=ToolPermission.WRITE) 14def issue_refund(invoice_id: str, amount_cents: int) -> str: 15 """Refund part or all of one paid invoice.""" 16 return billing_api.refund(invoice_id, amount_cents) 17 18@tool 19def search_request_logs(request_id: str) -> list[dict[str, str]]: 20 """Return gateway log lines for one API request ID.""" 21 return logs_api.search(request_id) 22 23@tool 24def list_active_sessions(account_id: str) -> list[dict[str, str]]: 25 """List signed-in devices and API keys for one account.""" 26 return auth_api.sessions(account_id) 27 28billing = Agent( 29 name="billing", 30 system_prompt="You resolve invoices and refunds. Look up the invoice before quoting or refunding any amount.", 31 provider="openai", 32 model_name="gpt-4.1-mini", 33 tools=[lookup_invoice, issue_refund], 34 permission_policy=PermissionPolicy.allow_all(), # billing may call its WRITE refund tool 35) 36 37debugger = Agent( 38 name="debugger", 39 system_prompt="You diagnose failed API calls from request IDs and stack traces, then propose the smallest fix.", 40 provider="anthropic", 41 model_name="claude-sonnet-5", 42 tools=[search_request_logs], 43) 44 45security = Agent( 46 name="security", 47 system_prompt="You help users secure their account: review sessions and explain how to rotate keys. Never reveal secrets.", 48 provider="openai", 49 model_name="gpt-4.1", 50 tools=[list_active_sessions], 51) 52 53desk = JevAgent( 54 JevAgentSettings( 55 name="support-desk", 56 system_prompt="You are a general support agent. Answer product questions and say clearly when a human is needed.", 57 provider="openai", 58 model_name="gpt-4.1-mini", 59 agents=( 60 JevSpecialist( 61 title="billing", 62 description="Invoices, charges, refunds, and payment failures on an existing account.", 63 agent=billing, 64 ), 65 JevSpecialist( 66 title="api_debugging", 67 description="Diagnosing failed or unexpected public API requests from request IDs, status codes, or stack traces.", 68 agent=debugger, 69 ), 70 JevSpecialist( 71 title="account_security", 72 description="Reviewing signed-in sessions and API keys, suspected account compromise, and key rotation.", 73 agent=security, 74 ), 75 ), 76 ), 77 JevRuntimeSettings(), # TypeSafe key read from TYPESAFE_API_KEY 78) 79 80TICKETS = [ 81 "I was charged twice for INV-2291. Please refund the duplicate.", 82 "Request req_8f2c returned 500 from /v1/roadmaps. What broke?", 83 "I don't recognize a device signed in to my account. What should I do?", 84 "What's the difference between the Pro and Team plans?", 85] 86 87async def main(): 88 for ticket in TICKETS: 89 reply = await desk.arun(ticket) 90 handler = desk.response.specialist or "main agent" 91 print(f"[{handler}] {ticket}") 92 print(" ", reply.content[:120], "...") 93 94asyncio.run(main())
What a run might print

Illustrative output: each ticket goes to the specialist whose description covers it, and the plan question stays on the main agent.

What a run might print

[billing] I was charged twice for INV-2291. Please refund the duplicate.
    I found two charges of $49.00 on INV-2291 and refunded the second one ...
[api_debugging] Request req_8f2c returned 500 from /v1/roadmaps. What broke?
    The gateway log shows an upstream timeout after 30s while generating module 4 ...
[account_security] I don't recognize a device signed in to my account. What should I do?
    There are three active sessions. The one from an unfamiliar Linux device ...
[main agent] What's the difference between the Pro and Team plans?
    Pro is for one person; Team adds shared workspaces and seat management ...

Example: an engineering copilot with clarity preflight

The specialists run on different model providers, and one of them can query a copy of the production database. Its description is kept narrow, so only questions that need a metrics answer reach that tool, and the clarity check asks about unclear requests before any agent runs.

eng_copilot.py

Python
1import asyncio 2 3from vidbyte import Agent, JevAgent, JevAgentSettings, JevPreflightPreset, JevRuntimeSettings, JevSpecialist, tool 4from vidbyte.lib.config import DecisionModelConfig 5 6# replica and load_secret stand in for your own database client and secret store. 7 8@tool 9def run_readonly_sql(query: str) -> list[dict[str, object]]: 10 """Run one SELECT statement against the analytics replica and return at most 200 rows.""" 11 return replica.select(query, limit=200) 12 13reviewer = Agent( 14 name="code-reviewer", 15 system_prompt=( 16 "You review diffs for correctness, security, and readability. " 17 "Rank findings by severity and quote the exact lines." 18 ), 19 provider="anthropic", 20 model_name="claude-sonnet-5", 21) 22 23analyst = Agent( 24 name="sql-analyst", 25 system_prompt="You answer product-metrics questions by writing and running read-only SQL, then explain the result.", 26 provider="openai", 27 model_name="gpt-4.1", 28 tools=[run_readonly_sql], 29 max_iterations=6, 30) 31 32scribe = Agent( 33 name="incident-scribe", 34 system_prompt="You turn incident timelines and chat logs into blameless postmortems with action items.", 35 provider="openai", 36 model_name="gpt-4.1-mini", 37) 38 39copilot = JevAgent( 40 JevAgentSettings( 41 name="eng-copilot", 42 system_prompt="You are a senior engineer who answers general engineering questions concisely.", 43 provider="openai", 44 model_name="gpt-4.1-mini", 45 agents=( 46 JevSpecialist( 47 title="code_review", 48 description="Reviewing a pasted diff or code snippet for bugs, security issues, and readability.", 49 agent=reviewer, 50 ), 51 JevSpecialist( 52 title="metrics_sql", 53 description="Answering questions about product usage metrics that require querying the analytics database. Not schema design or database advice.", 54 agent=analyst, 55 ), 56 JevSpecialist( 57 title="postmortem", 58 description="Writing a postmortem or incident summary from a pasted timeline or chat log.", 59 agent=scribe, 60 ), 61 ), 62 ), 63 JevRuntimeSettings( 64 decision=DecisionModelConfig(api_key=load_secret("typesafe")), 65 preflight=(JevPreflightPreset.CLARITY,), 66 ), 67) 68 69async def ask(prompt: str) -> None: 70 reply = await copilot.arun(prompt) 71 response = copilot.response 72 if response.needs_clarification: 73 print("→ Jev asked for clarification first") 74 else: 75 print(f"→ {response.specialist or 'main agent'}") 76 print(reply.content, "\n") 77 78async def main(): 79 await ask("How many workspaces created a roadmap in the last 7 days?") 80 await ask("Review this diff:\n- if user.is_admin:\n+ if user.is_admin or DEBUG:") 81 await ask("Should we pick Postgres or DynamoDB for an append-only event log?") 82 83asyncio.run(main())

Example: a study coach that routes every message

A learning app can route each message on its own. Jev reads only the current message, and the chosen specialist only sees that message, so each message should carry its own topic.

A bare follow-up like "now quiz me on it" still reaches the quiz maker, but that agent never saw the earlier conversation, so include the subject in the message.

study_coach.py

Python
1import asyncio 2 3from vidbyte import Agent, JevAgent, JevAgentSettings, JevRuntimeSettings, JevSpecialist 4 5socratic_tutor = Agent( 6 name="socratic-tutor", 7 system_prompt=( 8 "You coach learners through problems with guiding questions. " 9 "Never state the final answer; ask one question at a time." 10 ), 11 provider="anthropic", 12 model_name="claude-sonnet-5", 13) 14 15explainer = Agent( 16 name="concept-explainer", 17 system_prompt="You explain a concept from first principles in under 200 words, then give one concrete example.", 18 provider="openai", 19 model_name="gpt-4.1-mini", 20) 21 22quiz_maker = Agent( 23 name="quiz-maker", 24 system_prompt="You write 5 retrieval-practice questions on the topic being studied, with answers hidden at the end.", 25 provider="openai", 26 model_name="gpt-4.1-mini", 27) 28 29coach = JevAgent( 30 JevAgentSettings( 31 name="study-coach", 32 system_prompt="You are a friendly study coach. Help plan study sessions and answer general learning questions.", 33 provider="openai", 34 model_name="gpt-4.1-mini", 35 agents=( 36 JevSpecialist( 37 title="homework_help", 38 description="Helping a learner work through a specific homework or practice problem they are stuck on.", 39 agent=socratic_tutor, 40 ), 41 JevSpecialist( 42 title="explain_concept", 43 description="Explaining what a concept, term, or theorem means.", 44 agent=explainer, 45 ), 46 JevSpecialist( 47 title="make_quiz", 48 description="Creating practice questions or a quiz on a topic.", 49 agent=quiz_maker, 50 ), 51 ), 52 ), 53 JevRuntimeSettings(), 54) 55 56CONVERSATION = [ 57 "What does the chain rule actually say?", 58 "I'm stuck on d/dx of sin(x^2). Can you help me get there myself?", 59 "Now quiz me on the chain rule.", 60 "How should I split 6 hours of study across calculus and physics this week?", 61] 62 63async def main(): 64 for message in CONVERSATION: 65 reply = await coach.arun(message) 66 who = coach.response.specialist or "general coach" 67 print(f"you: {message}") 68 print(f"{who}: {reply.content[:100]} ...\n") 69 70asyncio.run(main())
Where each message went

Illustrative routing for the four messages in the script.

Where each message went

"What does the chain rule actually say?"            → explain_concept
"I'm stuck on d/dx of sin(x^2)..."                   → homework_help   (asks, never tells)
"Now quiz me on the chain rule."                     → make_quiz       (the topic is in the message)
"How should I split 6 hours of study..."             → general coach   (none)

Next steps