Skip to content

6. Routed agents

Build a help desk with three specialists, for maths, cooking and travel, and a router in front of them. Ask it anything: the router picks the specialist, and the specialist answers. Each specialist keeps its own short, focused instructions, instead of one agent carrying every subject in one long prompt.

You’ll learn

  • what a router is, and how it picks a specialist
  • how to write the summaries it picks by
  • how to read the router’s choices, and its reasons, from the run
  • when a router is worth it, and when one agent is enough

New concepts: routers

Builds on: 2. Prompts and inputs

Time: ~15 min · Cost: under $0.01 on DeepSeek per question (a small routing call, then the answer) · Needs: nothing beyond Get started

Each specialist is an ordinary agent. Its prompt says how it behaves; its summary says what it knows about. Until now summary did nothing. Here it matters, because it’s what the router reads:

maitento/agents/maths-tutor.mtr
model = @ref("deepseek.deepseek-v4-flash")
prompt = "You are a patient maths tutor. Explain the steps, briefly."
summary = "Maths: sums, algebra, percentages and geometry"
maitento/agents/cooking-coach.mtr
model = @ref("deepseek.deepseek-v4-flash")
prompt = "You are a friendly home-cooking coach. Give practical, simple advice."
summary = "Cooking: recipes, ingredients, techniques and substitutions"
maitento/agents/travel-guide.mtr
model = @ref("deepseek.deepseek-v4-flash")
prompt = "You are a well-travelled guide. Give concise, practical travel tips."
summary = "Travel: destinations, transport, packing and itineraries"

Write each summary as a subject and a list of what it covers. The router has nothing else to go on.

A router is an agent with routedSubAgents: the specialists it may hand a turn to. It never answers itself.

maitento/agents/help-desk.mtr
// A router never answers itself. On each turn it reads the specialists' summaries
// and hands the turn to the one whose subject fits. The first one is the fallback.
model = @ref("deepseek.deepseek-v4-flash")
prompt = "Choose the specialist whose subject the question is about."
summary = "Help desk for maths, cooking and travel questions"
routedSubAgents = [
@ref("maths-tutor"),
@ref("cooking-coach"),
@ref("travel-guide")
]

How it picks, on every turn:

  1. Maitento makes one small model call with the router’s model. It lists each specialist by name with its summary, adds the router’s prompt as the rule for choosing, and shows the conversation so far.
  2. The model replies with one specialist’s name and a one-sentence reason.
  3. That specialist takes the turn, with its own prompt, model and tools.

If the reply can’t be used twice in a row (no reply, or no name from the list), the first specialist in routedSubAgents takes the turn instead, and the choice is marked as a fallback.

The interaction seats the router like any other agent. The question is the whole task:

maitento/interactions/one-shot/ask-help-desk.mtr
agent = {
name = "HelpDesk"
agent = @ref("help-desk")
}
task = "%%question%%"
inputs.prompts = [
{
name = "question"
required = true
}
]

Done when: your project has five files: four agents (maths-tutor, cooking-coach, travel-guide, help-desk) and interactions/one-shot/ask-help-desk.mtr.

The script asks a cooking question, then prints what the router chose and the answer. Pass your own question as an argument to ask something else:

import { Maitento } from "@maitento/sdk";
const mt = new Maitento();
await mt.deploy("../maitento");
// Ask your own question with: node helpdesk.ts "your question"
const question = process.argv[2] ?? "What can I use instead of buttermilk in pancakes?";
// The router hands the question to the specialist whose subject fits.
const run = await mt.interactions.run("ask-help-desk", { inputs: { question } });
// Each choice the router made is kept on the run: which agent took the turn, and why.
for (const decision of run.routingDecisions) {
const chosen = await mt.agents.get(decision.chosen.id);
const fallback = decision.isFallback ? " (fallback)" : "";
console.log(`Router chose ${chosen.name}${fallback}: ${decision.reason}\n`);
}
console.log(run.output);

Run it from the language’s folder in examples/06-routed-agents:

cd examples/06-routed-agents/typescript
npm install
node helpdesk.ts

Done when: the router chose the cooking coach, and the cooking coach answered:

Router chose cooking-coach: The question is about a cooking ingredient substitution for a recipe.
Several easy substitutes work well in pancakes. For each cup of buttermilk, use one of the following:
- **Milk + acid (best all-purpose swap):** Stir 1 tablespoon of lemon juice or white vinegar into 1 cup of milk and let it stand for 5–10 minutes until slightly thickened.
- **Plain yogurt:** Thin 1 cup of plain yogurt with 2–3 tablespoons of milk or water until it reaches a pourable consistency.
…

Every choice a router makes is saved on the run, in routingDecisions, oldest first. Each one holds:

FieldWhat it is
participantNamethe seat whose turn was routed: HelpDesk
routerthe router that chose: its id and version
chosenthe agent that took the turn: its id and version. The script looks up its name
reasonthe router’s one-sentence reason, or why it fell back
isFallbacktrue when the router’s reply couldn’t be used and the first specialist answered
depthhow many routers chose on the way (a router can route to another router)
datewhen it chose

To everyone else, the help desk is still one participant. The answer is credited to HelpDesk, and it’s the run’s output. Only routingDecisions says which specialist really answered.

The routing call has a cost, and it’s on the run too: the run’s token usage has an entry for HelpDesk (routing), here about 200 tokens in and 25 out, beside the specialist’s answer.

Use one agent when the questions are all of one kind and need the same instructions and tools. A router adds a model call to every turn.

Use a router when questions come in different kinds that each need their own instructions, tools or model. Each specialist stays short and focused, and you can give only one of them a tool (say, a booking tool for the travel guide) or a bigger model (only for maths). The router picks one specialist per turn. When a job needs several agents working on it together, see managed teams.

Two more things to know:

  • A router always picks someone. There’s no “none of these”. Asked who won the 1966 football World Cup, this one said: “This question isn’t about maths, cooking, or travel, but of the available specialists the maths tutor is the closest fit for a factual trivia question.” If that matters, put a general-purpose agent in the list.
  • A router chooses on every turn. In a conversation (tutorial 7), each new message can go to a different specialist.

On GitHub: examples/06-routed-agents · TypeScript · Python · C#

All the files in this project (5)
model = @ref("deepseek.deepseek-v4-flash")
prompt = "You are a friendly home-cooking coach. Give practical, simple advice."
summary = "Cooking: recipes, ingredients, techniques and substitutions"

Ask a question that fits two specialists:

node helpdesk.ts "How much pasta should I pack for 4 people on a 5-day hiking trip?"

Packing is in the travel guide’s summary, but the router chose the cooking coach, and its reason says why:

Router chose cooking-coach: The question is about how much pasta to pack, which is a food quantity and meal-planning matter.

It chose the same in every run we tried, in all three languages. Neither answer is wrong, but if your help desk should send it to the travel guide, say so in the router’s prompt, which is its rule for choosing:

prompt = "Choose the specialist whose subject the question is about. Anything about packing for a trip, food included, goes to the travel guide."

Deploy and ask again:

Router chose travel-guide: The question is about packing food quantities for a hiking trip, which falls under travel logistics.

The reason is your debugging tool: when a router picks the wrong specialist, read why, then sharpen the summaries or the router’s prompt.

  • Unable to resolve resource reference for agent 'travel-guid' (help-desk.mtr): a name in routedSubAgents doesn’t match an agent file in the project.
  • Every question goes to the first specialist, marked (fallback): the router’s model isn’t replying with a name from the list. The reason says what was wrong with the reply. Try a stronger model for the router.
  • The wrong specialist answers: read the reason, then make the summaries say clearly what each one covers, or add a rule to the router’s prompt.
  • No namespace, Project folder not found: ../maitento, [api:model-keys:none-for-provider]: see tutorial 1.

You built:

  • three specialists, each with a focused prompt and a summary that says what it covers;
  • a router that hands each question to one of them, seated in an interaction like any agent;
  • a script that reads, from the run, which specialist answered and why.

The concept:

  • Routers: an agent with routedSubAgents. On each turn a small model call picks one specialist by its summary, following the router’s prompt; the first specialist is the fallback. Every choice is saved in routingDecisions.

Challenge: add a fourth specialist, a gardener, and ask the help desk “When should I plant tulip bulbs?”. The router should choose the gardener without being told anything else. The solution is in examples/06-routed-agents/solution.

Next: 7. A chat lets a person take part.