// lesson: routing
Routing โ The Coordinator
Your team has three specialists now, but every task marches through the same fixed pipeline. Real systems add a coordinator: something that looks at the incoming task and decides which agent (or pipeline) should handle it. "Find sources on X" should go straight to the researcher; "tighten this paragraph" needs only the writer; running the full research-write-review machine on either would waste three model calls.
Anthropic's agents article calls this pattern routing โ classify the incoming task, then dispatch it to a specialized handler. That classification step splits into two standard implementations:
- A classifier LLM call. Ask a model "which of these agents should handle this task? Reply with exactly one name." โ an agent whose entire job is dispatch. This is what LangGraph's conditional edges usually wrap and how CrewAI's hierarchical process uses its manager agent.
- Deterministic rules. Keyword or pattern matching on the task. No extra API call, no latency, no chance of the router itself hallucinating โ and trivially testable.
Production systems very often start with (2), then graduate specific hard
cases to (1). We'll build (2) as the graded function, with (1) as a
one-liner on top of make_agent for your real app.
The routing table declares each agent's capabilities as keywords, and the router picks the first agent whose capability appears in the task:
def route(task, agents, default):
haystack = task.lower()
for name, keywords, agent in agents:
if any(keyword.lower() in haystack for keyword in keywords):
return name, agent
return default
Three deliberate decisions, all of which the tests below pin down:
- Case-insensitive matching, both sides โ users type "Research", your table says "research"; that must not matter.
- First match wins, in declaration order. When a task matches two agents, the table's order is the tiebreak โ deterministic and visible in one place, instead of emergent from dict ordering or scoring.
- A
defaultyou choose explicitly. The interesting design question in any router is what happens when nothing matches. Silently dropping the task is a bug; crashing is rude. Declaring a fallback agent makes the policy explicit โ ship a generalist, or an agent whose role is to ask a clarifying question.
Wire it into team.py:
ROUTES = [
("researcher", ["research", "find", "sources", "facts"], researcher),
("writer", ["write", "draft", "article", "rewrite"], writer),
("critic", ["review", "critique", "feedback"], critic),
]
generalist = make_agent(
"generalist",
"You are a capable general assistant. Answer directly and concisely.",
anthropic_client,
)
if __name__ == "__main__":
while True:
task = input("task> ").strip()
if task in ("quit", "exit"):
break
if not task:
continue
name, agent = route(task, ROUTES, ("generalist", generalist))
print(f"[routed to {name}]")
print(agent(task))
Run it. Type "find sources on HNSW indexes" and watch it hit the
researcher; type "what's 2+2" and watch it fall through to the generalist.
The [routed to ...] line is your friend โ routing bugs are invisible
without it.
And the LLM-router upgrade, when keyword rules stop being enough? It's an agent like any other:
router = make_agent(
"router",
"Classify the task. Reply with exactly one word โ researcher, "
"writer, critic, or generalist โ naming the best agent for it.",
anthropic_client,
)
Call it, match its reply against your table (with the same fallback when it
names nobody โ models misspell), and you've reproduced the dispatch layer
of a hierarchical CrewAI crew. Note that route itself only selects โ
it returns the agent without calling it. The caller decides what to do with
the choice: call it, log it, or route into a whole pipeline. Selection and
execution are separate concerns, and the tests enforce that separation.
โบ Route the Task
15 ptsImplement route(task, agents, default), where agents is a list of
(name, keywords, agent) triples and default is a (name, agent) pair:
- Return
(name, agent)for the first entry (in list order) where any keyword occurs as a case-insensitive substring oftask. - If no entry matches, return
default. - Only select โ never call any agent.
Log in to submit a solution and earn points.