๐ Route: send each request to the right place
A coding question, a request for a poem and a 40-page contract need very different handling. Routing works out what kind of request has arrived and passes it to the tool, workflow or specialised agent best suited to it.
Routing is a general design pattern that you build in your own code. There is no "router" setting in the Claude API. Claude is often used as the classifier inside a router. Anthropic describes the pattern as routing in Building effective agents.
What is it?
Routing is a two-part process: first classify the request (decide what type it is), then dispatch it (hand it to the matching handler). Each handler can have its own instructions, tools, skills or even its own model.
- Intent
- What the user is trying to achieve, such as "fix my code" or "write an email".
- Classification
- Sorting something into one of a fixed set of categories.
- Route / handler
- The destination that deals with one category: a tool, a workflow or a specialised agent.
- Confidence
- How sure the classifier is about its choice.
- Fallback route
- The safe default used when no route fits or confidence is low, for example "ask a clarifying question".
Why is it important?
- Specialisation: one prompt that tries to handle every kind of request does each of them less well. Separate handlers can each be tuned for their job.
- Cost and speed: simple requests can go to a cheaper, faster model or straight to a tool, and only hard ones go to the most capable model.
- Safety: sensitive requests, such as refunds or account changes, can always be sent down a path that includes human review.
- Easier maintenance: improving the "coding" route can't accidentally break the "writing" route.
Real-world analogy
At a large office, a receptionist greets every visitor, asks what they need, and directs them: "Accounts is on floor 2, IT support is on floor 3." The receptionist doesn't fix laptops or do anyone's taxes. They just get each visitor to the right department. If a visitor's request is unclear, the receptionist asks a question instead of guessing. If nothing fits, they send the visitor to the general enquiries desk, which is the fallback.
How does it work?
- Define the routes: a short, fixed list of categories with clear descriptions.
- Classify: use simple rules (keywords, form fields) or ask Claude to choose one category.
- Check the decision: is the category valid? Is the confidence high enough? Is required information missing?
- Dispatch to the chosen handler, or to the fallback.
- Respond and log which route was used, so you can measure accuracy later.
What can a router base its decision on?
๐ฏ User intent
"Help me debug" โ coding. "Draft a letter" โ writing.
๐ฆ Task type or input
Has a PDF attached โ document analysis.
โ๏ธ Conditions
Order value over $500 โ human review. Customer is a VIP โ priority queue.
๐งฐ Required capabilities
Needs live data โ a route with web search. Needs code execution โ a coding agent.
Workflow diagram
Try it: watch the router decide
Practical example: a help-desk assistant
| Route | Example request | What the handler has |
|---|---|---|
| Research | "What are the latest trends in solar panels?" | Web search tool, research-brief skill |
| Coding | "Why does my loop never stop?" | Code execution tool, code-review skill |
| Writing | "Draft a thank-you note to a client" | House style skill, no tools needed |
| Document analysis | "List the deadlines in this contract" (PDF attached) | PDF input, document-analysis skill |
| Fallback | "Can you sort this out?" | Asks one clarifying question |
Code example
Version 1: rule-based router. This runs with plain Python, with no API key and no AI. Rules are fast and free, but brittle.
# simple_router.py: route requests using keyword rules. Run: python simple_router.py
ROUTES = {
"coding": ["code", "bug", "error", "python", "function", "crash"],
"research": ["research", "find", "studies", "trends", "latest"],
"writing": ["write", "draft", "email", "letter", "poem"],
"document": ["pdf", "contract", "document", "attached"],
}
def route(request: str, has_attachment: bool = False) -> str:
words = request.lower()
scores = {name: sum(k in words for k in keys) for name, keys in ROUTES.items()}
best = max(scores, key=scores.get)
if scores[best] == 0:
return "fallback" # nothing matched: ask the user to clarify
if best == "document" and not has_attachment:
return "missing_info" # right route, but the file is missing
return best
for text, attached in [("My Python code has a bug", False),
("Summarise this contract", False),
("Help me with the thing", False)]:
print(f"{text!r:35} -> {route(text, attached)}")
'My Python code has a bug' -> coding
'Summarise this contract' -> missing_info
'Help me with the thing' -> fallback
Version 2: Claude as the classifier. Claude understands meaning, not just keywords. Structured outputs guarantee that the reply is valid JSON in the shape we ask for. It needs pip install anthropic and the ANTHROPIC_API_KEY environment variable on your computer or server.
# claude_router.py: Claude classifies, our code checks and dispatches.
import json
import anthropic
client = anthropic.Anthropic()
ROUTES = ["research", "coding", "writing", "document_analysis", "unclear"]
def classify(request: str) -> dict:
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=4096,
system=("You route requests for a help desk. Pick exactly one route. "
"Use 'unclear' when the request is ambiguous. "
"If key information is missing (e.g. a document that was not attached), describe it in missing_info."),
messages=[{"role": "user", "content": request}],
output_config={
"effort": "low", # classification is easy: think briefly
"format": {"type": "json_schema", "schema": {
"type": "object",
"properties": {
"route": {"type": "string", "enum": ROUTES}, # only these values are allowed
"confidence": {"type": "number"}, # 0.0 to 1.0
"missing_info": {"type": "string"}, # "" if nothing is missing
},
"required": ["route", "confidence", "missing_info"],
"additionalProperties": False,
}},
},
)
return json.loads(next(b.text for b in response.content if b.type == "text"))
# Handlers: in a real app each would call its own prompt, tools or agent.
HANDLERS = {
"research": lambda r: f"[research agent] searching sources for: {r}",
"coding": lambda r: f"[coding agent] debugging: {r}",
"writing": lambda r: f"[writing agent] drafting: {r}",
"document_analysis": lambda r: f"[document agent] analysing: {r}",
}
def handle(request: str) -> str:
decision = classify(request)
print("decision:", decision)
if decision["missing_info"]:
return f"Before I start, could you provide: {decision['missing_info']}?"
if decision["route"] == "unclear" or decision["confidence"] < 0.6:
return "Could you tell me a bit more? Is this about research, code, writing or a document?"
return HANDLERS[decision["route"]](request)
print(handle("My Python script crashes with a KeyError on line 12"))
print(handle("Can you sort this out?"))
decision: {'route': 'coding', 'confidence': 0.95, 'missing_info': ''}
[coding agent] debugging: My Python script crashes with a KeyError on line 12
decision: {'route': 'unclear', 'confidence': 0.3, 'missing_info': ''}
Could you tell me a bit more? Is this about research, code, writing or a document?
Step-by-step explanation
- Version 1 counts keyword matches for each route with
sum(k in words for k in keys)and picks the highest score usingmax(..., key=scores.get). - A score of zero means nothing matched, so it returns
"fallback". A document request with no attachment returns"missing_info". - Version 2 puts the routing rules in the
systemprompt, which is standing instructions that apply to the whole conversation. output_config.formatwith ajson_schemaforces the reply into our shape. Theenummeans Claude can only choose one of our five routes."effort": "low"tells Claude it doesn't need to think deeply, which makes classification faster and cheaper.handle()checks the decision before acting: missing information โ ask for it, unclear or low confidence โ clarify, otherwise call the matching handler.HANDLERSis a dictionary of destinations. Adding a new route means adding one entry toROUTESand one handler.
Handling unclear requests, missing information and fallbacks
- Always include an "unclear" option. If the classifier has to choose a real route, it will guess.
- Set a confidence threshold such as 0.6. Below it, ask instead of guessing. Self-reported confidence is only a rough guide, so test it on real examples.
- Ask one specific question ("Is this about code or writing?"), not "Please clarify".
- Check for missing inputs, such as files, order numbers or dates, before starting expensive work.
- Have a general fallback handler for anything left over, and log these cases. They show you which new routes you need.
Routing and orchestration compared
| Routing | Orchestration | |
|---|---|---|
| Question it answers | "Who should handle this?" | "How do we split this big job, and who does each part?" |
| Handlers used per request | Usually one | Several, often in combination |
| After the hand-off | The router's job is done | The orchestrator monitors, checks and combines the results |
| Analogy | Receptionist | Project manager |
Use cases
- Customer service: billing, technical support, refunds, general questions
- Choosing a model: easy questions to a fast, cheap model, hard ones to the most capable model
- Multi-purpose assistants with research, coding and writing modes
- Safety paths: sensitive topics always routed to careful handling or a human
Advantages and limitations
โ Advantages
- Each route can be specialised and tested separately
- Saves cost by sending easy work to cheaper handlers
- Clear place to add safety rules
โ ๏ธ Limitations
- A wrong classification sends the request down the wrong path
- The classification step adds a little time and cost
- Requests that mix two types are tricky
๐ซ Common mistakes
- No "unclear" option or fallback
- Overlapping categories
- Too many routes
- Never checking routing accuracy
Best practices
- Start with 3โ5 clearly different routes and describe each in one sentence.
- Constrain the classifier's answer to a fixed list, for example with an
enumin structured outputs. - Use code rules for obvious cases (an attachment means a document) and Claude for the rest.
- Keep a test set of real requests with the correct route, and re-check accuracy after changes.
- Log every routing decision.
Knowledge check
1. What are the two parts of routing?
Routing decides what kind of request it is, then sends it to the right place.
2. A user writes "Can you help with the thing?" What should a good router do?
Unclear requests go to the fallback. Guessing wastes effort and frustrates users.
3. Why add an enum list of routes to the classifier's output?
A fixed list prevents made-up categories that your code can't handle.
4. What is the main difference between routing and orchestration?
Receptionist versus project manager.
5. Is routing a built-in Claude API feature?
Routing is an architecture pattern. Claude features such as structured outputs help you build it.
Mini project
Build a three-route school helper.
- Routes:
maths,essay,unclear. - Start with
simple_router.pyand change the keywords. - Write 10 test requests and the correct route for each. Count how many your router gets right.
- Then try
claude_router.pywith the same 10 requests and compare the accuracy. - Bonus: if a maths request has no numbers in it, return "missing_info".
Key takeaways
- Routing = classify + dispatch. Each request goes to the one handler that suits it best.
- Base routes on intent, task type, conditions or required capabilities.
- Always provide an "unclear" option, a confidence check and a fallback.
- Routing picks who; orchestration plans how and coordinates many.
- It's a general pattern. Claude is often the classifier inside it.
Further learning
- Building effective agents, the "Routing" section (Anthropic engineering blog)
- Structured outputs (official docs)
- Prompt engineering overview (official docs)