2.4 Routing & handoffs
So far we’ve had one agent doing everything. Give one agent a refund tool, a log-search tool, a ticket tool, an email tool, and a knowledge base, and ask it to handle whatever walks in the door. That’s failure mode #4 — one agent does everything — and it fails in a specific way: it grabs the wrong tool and blurs unrelated goals.
The more tools an agent holds, the more ways it has to pick wrong. A generalist with twenty tools is twenty guesses waiting to happen.
Route first, then specialize
Section titled “Route first, then specialize”The fix borrows an idea from an org chart: a receptionist doesn’t solve your problem — they figure out who should, and send you there with the relevant details.
- Routing is a control-plane decision: read the incoming task’s intent and choose the right specialist. (Rules and keywords first; fall back to the model for fuzzy cases; escalate to a human when it’s genuinely unclear.)
- A handoff is a typed transfer: the router passes the specialist a structured payload — intent plus extracted details — not a vague “you deal with this.”
Each specialist then holds only the few tools its job needs. Smaller surface, fewer wrong guesses.
Watch a task get routed
Section titled “Watch a task get routed”Pick an incoming task and follow it through the router to a specialist:
Pick a task above to watch it get routed.
Notice two things each time. The router hands over a typed payload — intent plus
the entities it pulled out — so the specialist starts with structure, not raw text. And
the specialist it lands on carries a tiny toolset. The billing agent can’t search
logs; the support agent can’t issue refunds. They don’t need to, so they can’t get it
wrong.
What that looks like in code
Section titled “What that looks like in code”function route(task) { // Cheap deterministic rules first; fall back to the model for the fuzzy ones. if (/refund|order|charge/i.test(task)) return 'billing'; if (/crash|bug|error|broken/i.test(task)) return 'support'; if (/policy|how do|what is/i.test(task)) return 'faq'; return askModelToClassify(task); // fallback — then escalate to a human if still unsure}
const SPECIALISTS = { billing: { tools: [lookupOrder, issueRefund] }, support: { tools: [searchLogs, createTicket] }, faq: { tools: [searchKnowledgeBase] },};
async function handle(task) { const to = route(task); const payload = { intent: to, task, entities: extract(task) }; // typed handoff return runSpecialist(SPECIALISTS[to], payload);}The generalist is gone. route picks the specialist; the handoff payload carries
structure; each specialist runs with a minimal toolset.
In production
Section titled “In production”- LangGraph, Mastra, and the agent SDKs model exactly this: a graph of agents with routing edges and typed handoffs between them.
You built the receptionist and the specialists by hand.