Codetail

Article 8 of 15

Multi-Agent Systems

When one agent stops being enough.

22 min read

Every extra agent is a coordination problem a single one doesn't have

Before the patterns in this article, the case against them. A single agent with a wide toolset and a clear system prompt handles more than it gets credit for, and it has no coordination problem at all: one context, one history, one place decisions get made.

Splitting that into multiple agents adds a question that didn't exist before: who talks to whom, in what order, and who has the final say when two agents disagree. That question doesn't go away because the system got more sophisticated-sounding, it just moves from "inside one prompt" to "across a boundary between two processes," which is a strictly harder place to debug it.

Reach for multiple agents when a single one's context or toolset genuinely can't hold what the task needs, not because splitting a problem into named roles sounds more like how a real team would do it. A team of agents inherits a team's coordination overhead along with its capabilities.

One orchestrator, several narrow specialists

The pattern that shows up most often in practice looks less like a team of peers and more like a manager with a small set of specialists it can delegate to. One orchestrator agent plans and decides who handles what. Each worker has a narrow toolset and a system prompt written for exactly one job, and reports back a result rather than continuing the conversation itself.

Workers exposed to the orchestrator as if they were tools

Python
1orchestrator_tools = [
2 {"type": "function", "function": {"name": "research_agent", "description": "..."}},
3 {"type": "function", "function": {"name": "billing_agent", "description": "..."}},
4]
5
6def research_agent(query):
7 return run_agent_loop(system="You research and summarize.", tools=[web_search], input=query)
8
9def billing_agent(query):
10 return run_agent_loop(system="You look up billing records.", tools=[get_invoice], input=query)

From the orchestrator's perspective, each worker is just another tool call, the same mechanism from Structured Output and Tool Calling, decide, call, get a result back. The worker underneath happens to be its own full agent loop instead of a plain function, but that's an implementation detail the orchestrator doesn't need to know about.

What crosses the boundary between two agents matters more than the boundary itself

The orchestrator and a worker don't share a context window. Whatever the orchestrator knows, the worker only knows if it was explicitly passed across, and the two obvious ways to pass it have very different failure modes.

Handing over the entire conversation so far is the simplest option and the most expensive one, every token of it counts against the worker's own context budget, and most of that history is irrelevant to the narrow job the worker actually has to do. A structured handoff, a small, explicit set of fields the orchestrator decided the worker actually needs, costs less and forces a decision about what's relevant instead of deferring that decision to "just include everything."

The failure mode of the structured version is real too: a field the worker actually needed wasn't included, because the orchestrator's idea of "what's relevant" was wrong. That's a bug to catch with the same golden-set testing this series covers for prompts generally, not a reason to default back to passing everything, the same way one under-specified API request isn't a reason to stop specifying request schemas at all.

Signs it's time to collapse back down to one

A few concrete signals, worth checking against honestly rather than assuming the architecture is fine because it was designed carefully.

If the "agents" always run in the same fixed order with no branching based on what the previous one returned, that's not a multi-agent system, it's a regular pipeline of function calls wearing agent framing, and it should probably be written as one. If debugging a bad outcome requires reasoning about the interaction between two agents rather than either one individually, that's an emergent failure mode, and emergent failure modes are exactly what a single-agent system doesn't have to worry about. If latency and cost have multiplied across the extra hops without a matching improvement in output quality, the coordination overhead is a pure cost with no offsetting benefit.

Any of these showing up is a reason to collapse back to one agent with a bigger toolset, or a fixed pipeline with the model called once or twice inside it, not a reason to add a third agent to fix what the second one is doing wrong.