How to Route Multi-Agent Tasks Without a Bottleneck Agent
Most multi-agent failures are dispatch failures, not model failures. A task arrives, nobody wrote down which skill it needs, so it goes to the strongest agent available, and that agent becomes a queue. Good routing is mostly paperwork: three fields per task, one rule list you can read out loud, and a check that can fail.
The three fields every task needs
Who is a capability, not an agent name. copy.edit survives a model swap; editor-pro does not. If tasks name agents, your routing table rots the first time you change a deployment.
What is a deliverable with a shape. “Improve the landing page” cannot be routed or verified. “Edited markdown at drafts/landing.md, H2 sections only, length within 10% of the input” can be both.
Verify is a check that returns pass or fail without a human reading the output: a command with an exit code, a schema validation, a required-keys assertion. If you cannot name the check, the task is not ready to route. It is ready to think about.
id: tsk-0417
capability: copy.edit
deliverable: drafts/post-12.md
accept: # instructions for the agent
- "frontmatter keeps title, description, slug"
- "no new H2 sections"
verify: # gate for the router
run: python tools/check_post.py drafts/post-12.md
expect_exit: 0
budget:
max_minutes: 20
max_attempts: 2
Keep accept and verify separate. The first is a prompt, and prompts can be argued around. The second runs after the agent returns and does not care what the agent believed it did. That separation is what makes routing reversible: a misrouted task costs one failed run, not a corrupted document. The layers around this one โ context, storage, review โ are covered in the AI automation guide.
Write routing rules you can read back
A rule list is readable when a teammate can predict where a task lands without running it. Three properties buy that: ordered rules, first match wins, and a reason string on every decision. If your router sorts agents by a capability score, you cannot answer “why did task 417 go to editor-pro?” without stepping through the scorer, which means you cannot fix it either.
# routing.yaml โ top to bottom, first match wins
default: fallback.general
agents:
editor-pro:
capabilities: ["copy.edit", "copy.rewrite"]
max_concurrent: 1
copy.light:
capabilities: ["copy.edit"]
max_concurrent: 3
indexer:
capabilities: ["data.*"]
max_concurrent: 4
fallback.general:
capabilities: ["*"]
max_concurrent: 2
rules:
- match: {capability: "data.*"}
route: indexer
reason: "bulk data work; cheap and parallel"
- match: {capability: "copy.*", priority: "high"}
route: editor-pro
reason: "high-priority copy needs style memory"
- match: {capability: "copy.*"}
route: copy.light
reason: "routine edits pass a linter, not an editor"
import fnmatch, yaml
CFG = yaml.safe_load(open("routing.yaml"))
def route(task: dict) -> tuple[str, str]:
for rule in CFG["rules"]:
if all(fnmatch.fnmatch(str(task.get(k, "")), v)
for k, v in rule["match"].items()):
return rule["route"], rule["reason"]
return CFG["default"], "no rule matched"
Log both return values on every dispatch. “Why did this go to copy.light” then costs one grep instead of an afternoon. If you are still deciding whether this layer should be a workflow builder or a plain script, that tradeoff is covered in workflow builder vs a plain script.
| Router style | Picks by | Breaks when | Use it when |
|---|---|---|---|
| Static map | task type to agent, literal | you add a type and forget the entry | few, stable task types |
| Capability match | first rule whose glob matches | rules overlap and order is undocumented | most multi-agent setups |
| Score or rank | highest capability score | one agent scores high on everything | you have real per-agent cost data |
| LLM judge | a model reads the task | you cannot reproduce the choice | low-volume, one-off triage |
The over-capable agent bottleneck
The failure looks like this: one agent appears in most of the run history, its queue depth climbs while the others idle, and short-timeout tasks fail only to succeed an hour later. Nothing is broken. The router is doing what “be safe” told it to do.
Causes, roughly in order of frequency:
- Unmatched tasks fall back to the strongest agent, because it will “probably handle it”.
- The broad
*capability is claimed by the best model rather than the cheapest adequate one. - No per-agent concurrency limit, so the queue grows where nobody is looking.
- Cheap work never got an explicit rule, so it inherits the default.
The fix is a capability ceiling. Give the strongest agent a deliberately narrow list โ the things only it can do โ and let a weaker agent claim the broad glob. Fallback should be the cheapest agent that can pass the verify check, not the smartest one you have. Then make overflow a written policy instead of an accident:
agents:
editor-pro:
max_concurrent: 1
overflow: spill
spill_target: copy.light
spill_after: 10m
Queue or spill is a judgment call about quality risk, and either is defensible. Silent upgrade is not: it hides a routing bug inside a successful run. A blunt check is to disable the strongest agent in a staging config and see which tasks have no route left. Those are the rules you never wrote. The same discipline applies further out โ one job per layer, as argued in wiring an AI dev stack so each layer has one job.
Read the history before you add an agent
Before you introduce a fifth agent, read the run history for four things: runs per agent, verify pass rate per route, median queue wait per agent, and how often the default rule fired. Pass rate per route is the one people skip. A route with a poor pass rate is a rule sending work to an agent that cannot do it, and retries do not fix a mismatch โ change the rule, or split the capability into two narrower ones.
If one agent holds most of the runs, routing is decoration. If the default fires often, your rule list is missing a case; your agents are fine. Because the envelope carries everything the router reads, you can also dry-run new rules against stored task envelopes before switching them on, which is far cheaper than discovering a bad overlap in production.
What to do next
- Add
capability,deliverableandverifyto your task template, and reject any task missing one of the three. - Replace agent names with capabilities in the router, then put every rule in one ordered file where the first match wins and each rule carries a written reason.
- Give your strongest agent a capability ceiling, an explicit
max_concurrent, and an overflow policy of either queue or spill. - Log route and reason on every dispatch, and read the run history once a week for pass rate per route and how often the default fired.
- If you would rather keep routing and run history in a dashboard than in a YAML file plus a grep, Cowork Pro is a $59 one-time dashboard for organising and orchestrating multiple AI agents on real projects, with task routing and run history. It is available at https://slashmaster6.gumroad.com/l/xfhfps.
Get Cowork Pro
Cowork Pro โ $59, one-time payment, instant download. See the full breakdown on the review page.
Related reading
- The Ultimate Guide to AI Automation 2026
- How I Built a 10-Product Digital Business
- Building an AI Content Factory
๐ Want to try before you buy?
Both samples are 100% free. Enter your email to unlock the download links and get our 3-day AI toolkit drip in your inbox.
No spam. Unsubscribe anytime. We respect your privacy.
Or browse the full catalogue in the Slashman Tools storefront.
Published by slashman413 โ writing practical, evergreen guides on money, productivity, developer tooling and the web. More about this site โ
Frequently Asked Questions
How many agents can one routing table cover before it gets unreadable?
Readability tracks capabilities, not agent count. A handful of glob patterns with two or three agents each stays readable as long as the rules are ordered and first match wins; the table gets unusable when rules overlap and nobody documented the order.
Can I just let an LLM decide which agent gets the task?
Only for low-volume triage, and only if you log its reasoning. Use the model to propose a rule, then encode that rule in a file so the same task routes the same way twice.
What if the agent passes the verify check but the output is still bad?
The check is too weak. Tighten verify or move the missing requirement into it โ do not add retries, which just produce more bad output that already passes.
๐ Recommended Tools
๐ Related Articles
- Every Product We Sell on Gumroad: The Complete CatalogEvery live Slashman Tools product on Gumroad in one place โ AI prompts, agent workflows, dashboards, courses โฆ
- What Makes a Feishu Template Worth Paying ForA breakdown of Feishu template parts: data model, views, permission scopes, automation flows โ and which ones โฆ
- Buying a Template vs Building Your Own: Variables That DecideA practical decision guide for buying a template versus building your own workspace: data model stability, โฆ
- n8n vs Custom Multi-Agent Workflows: When to Stop Dragging Nodes and Write Code (2026)n8n or a custom code multi-agent workflow? An honest 2026 comparison of versioning, testing, cost, LLM โฆ
๐ฌ Free Weekly AI Product Guides
New tools, templates and automation walkthroughs โ plus hands-on updates on the Slashman Tools catalogue. One email a week, zero fluff.
Free forever ยท No spam ยท Unsubscribe anytime ยท Sent instantly