Multi-Agent Orchestration: When to Split the Agent, and How Not to Pay 15x for It
Multi-agent systems cost roughly 15x a chat turn, so the most valuable orchestration decision is usually the split you refuse to make, and the coordinator-specialist boundary is how you survive the splits you do.

Most multi-agent failures are not coordination problems. They are split decisions that never should have been made. Here is the gate that stops them, and the coordinator-specialist boundary that keeps the splits you do make from eating your budget.
Your multi-agent system is burning tokens negotiating with itself instead of finishing the job, and the fix is not better coordination. Most multi-agent failures are not coordination problems.
In this article: You will learn why a naive multi-agent system costs about 15x the tokens of a single chat turn, the three-part gate that decides whether a split is ever worth it, the hub-and-spoke pattern that keeps a real split near 4-6x instead of 15x, and the three coordinator layers (orchestration, delegation, synthesis) you have to keep separate. We build the same coordinator-and-specialist twice: once in the Claude Agent SDK, once in LangChain Deep Agents.
The travel-booking harness looked fine in staging: one agent, one window, and a clean sequence of tool calls. Then someone scaled it up by wiring in a second agent for hotel loyalty lookups. There was no protocol, no state machine, just two subagents passing task descriptions back and forth as strings. Seven thousand tokens in, they were still negotiating who owned the reservation number. By 47,000 tokens and a $6.20 inference bill, they had looped through the handoff nine times without completing a single booking. Marcus had closed the tab twenty minutes earlier and booked directly with the hotel.
That is the failure this article is about, and it carries a counterintuitive lesson. The fix for Marcus is not better coordination between the two agents. Often it is one agent, and when it genuinely is two, it is a strict discipline about what each one sees.
Multi-agent orchestration is the harness function that decides when to split work and keeps the split from eating your budget. Most of its value lives in the splits it refuses to make.
The 15x tax is real, and it is a tax
Start with the number, because it governs the whole decision. A single-agent loop costs roughly 4x the tokens of a plain chat turn. A naive multi-agent system costs about 15x. On agentic benchmarks, token usage alone has explained the large majority of performance variance, because coordination chatter accumulates at every agent boundary. The 15x is not a footnote; it is the dominant line item.
The worst bills come from context explosion: every agent in the network accumulates the full workflow history instead of a scoped slice, and over a long task the agents drift off the original goal. That is exactly Marcus's nine-loop spiral. Two agents, each carrying the entire conversation, each re-deriving who owns the booking, and neither finishing.
So the first orchestration control is a gate, not a feature. Go multi-agent only when all three of these conditions hold:
- The task decomposes into genuinely independent subtasks that can run in parallel.
- The combined value of higher quality and faster wall-clock time exceeds the 15x cost.
- The task exceeds one agent's capacity: more context than one window, or more tool diversity than one configuration.
If any condition fails, run the single agent.

The 15x is not a problem to engineer around; it is a price that only makes sense when the output justifies it. If you cannot defend that bill in a code review, you do not have a multi-agent system, you have an outage waiting. The honest default, after hardening a single agent through every prior harness function, is to keep it single.
When it is worth it: coordinator and specialists
When the gate opens, the pattern that keeps you near 4-6x instead of 15x is hub-and-spoke: one coordinator holds the strategy and the final merge, and each specialist runs a narrowly scoped task in its own context window, returning a condensed result. The spokes never talk to each other. All coordination flows through the hub.

This is context isolation applied across agents. A specialist with a clean window cannot have its reasoning contaminated by the coordinator's history, and a coordinator that receives only a terse summary never inherits the specialist's intermediate chatter. The contamination that sank Marcus is structurally impossible when the boundary is drawn this way.
Both frameworks give you the boundary as a first-class construct. Here is the same coordinator-plus-loyalty-specialist, built twice.
In the Claude Agent SDK, a specialist is an AgentDefinition registered under agents, and the coordinator delegates to it through the Agent tool. The specialist gets its own prompt, its own narrower toolset, and its own window.
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
options = ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Agent"], # ① "Agent" lets the hub delegate
agents={
"loyalty-specialist": AgentDefinition( # ② a scoped spoke
description="Looks up hotel loyalty tier and points for one account.",
prompt=("You check one hotel loyalty account. Return ONLY: tier, points "
"balance, expiring points. No prose, no chain of thought."), # ③
tools=["Read", "Grep"], # ④ narrower than the hub
model="sonnet",
),
},
system_prompt="You rebook flights and apply loyalty benefits. Delegate the "
"loyalty lookup; do not perform it yourself.",
)
# query(prompt="Rebook Marta and apply any hotel loyalty benefits.", options=options)
① The Agent tool is what enables delegation; without it in allowed_tools the hub has
no way to call a spoke.
② The specialist is registered under agents, not inlined into the hub's prompt, so it
becomes a routable spoke with its own identity.
③ The prompt forces a compressed, structured output, so the hub receives a few facts, not
a transcript.
④ A tighter toolset keeps the spoke's window clean; the coordinator never sees the
lookup's intermediate steps, only the result.
Note: The full extracted listing at code/harness_engineering/part-6-orchestration/listings/01-sdk-coordinator-specialist.py shows the same configuration with the marker comments removed.
Deep Agents expresses the same thing as a subagents list. Each subagent is a dict with its own name, description, prompt, and scoped tools; the coordinator routes to it.
from langchain.tools import tool
from deepagents import create_deep_agent
@tool
def lookup_loyalty(account_id: str) -> str:
"""Fetch hotel loyalty tier and points for an account."""
return loyalty_api.get(account_id)
loyalty_specialist = { # ① a scoped spoke, as data
"name": "loyalty-specialist",
"description": "Looks up hotel loyalty tier and points for one account.",
"system_prompt": ("You check one hotel loyalty account. Return ONLY tier, points "
"balance, and expiring points. Be terse."), # ② compressed output
"tools": [lookup_loyalty], # ③ scoped toolset
}
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
tools=[search_flights, book_flight],
subagents=[loyalty_specialist], # ④ hub delegates to the spoke
system_prompt="You rebook flights and apply loyalty benefits. Delegate the "
"loyalty lookup to the specialist.",
)
① The spoke is plain data, a dict the framework reads, not a class instance you construct.
② The "be terse" instruction is the condensed-result discipline, the thing that keeps the
hub's window from filling with the lookup's prose.
③ A scoped toolset mirrors the SDK specialist, so the spoke can reach only the loyalty
tool and nothing else.
④ The coordinator routes the loyalty task to the spoke through subagents and keeps its
own window small. Two frameworks, one boundary: scoped context in, compressed result out.
Note: The full extracted listing at code/harness_engineering/part-6-orchestration/listings/02-deepagents-coordinator-specialist.py shows the same configuration with the marker comments removed.
The delegation cycle is the same shape in both frameworks: the coordinator hands a scoped task down, the specialist works in isolation, and only a terse result comes back up.

Keep the three layers separate
The reason naive multi-agent runs 15x is that its authors conflated three concerns that must stay apart. Every coordinator has them whether or not you designed them.

- Orchestration decides what work to do, in what order, and with which specialist. This is the expensive LLM reasoning. It should never see raw protocol or transport state.
- Delegation handles the handoff: invoking the specialist, managing its lifecycle, and collecting its result. The frameworks own most of this for in-process subagents.
- Synthesis merges the specialist outputs into the final answer, resolves conflicts, and annotates where each fact came from.
Conflate orchestration with delegation and the coordinator's window fills with handoff noise. Conflate synthesis with either and you waste reasoning tokens on metadata. The contamination flows both ways, and it is the exact mechanism behind the 15x.
Synthesis deserves a closer look, because the tempting shortcut is the dangerous one. When two specialists disagree, the lazy merge silently picks a winner. A harness-grade synthesis surfaces the conflict instead, and records provenance, so a wrong fact is visible rather than laundered into the final answer.
def synthesize(results: list[dict]) -> dict:
"""Merge specialist outputs; surface conflicts instead of silently picking."""
merged, conflicts = {}, []
for r in results:
for key, value in r["facts"].items():
if key in merged and merged[key] != value:
conflicts.append({"field": key, "a": merged[key], # ① do not overwrite
"b": value, "source_b": r["source"]})
else:
merged[key] = value
return {
"facts": merged,
"conflicts": conflicts, # ② first-class output
"provenance": {r["source"]: list(r["facts"]) for r in results}, # ③ who said what
}
① A disagreement is recorded, not resolved by overwrite, so a clash between two specialists is preserved rather than silently flattened. ② Conflicts are part of the returned result, so the coordinator can re-query or escalate instead of guessing. ③ Provenance ties every fact to the specialist that produced it, which is what lets you audit a bad answer later. This applies the same discipline as tool contracts and provenance tagging, now at the merge point.
Note: The full extracted listing at code/harness_engineering/part-6-orchestration/listings/03-synthesize-conflicts.py shows the same function with the marker comments removed.
The synthesis path is worth tracing end to end: every fact either lands cleanly or registers a conflict, and the presence of any conflict is what decides whether the coordinator can trust the merge or has to escalate.

The coordinator has its own failure mode
One caution, because hub-and-spoke is not free of the disease it cures. The coordinator's window accumulates the results of every delegation, and routing accuracy degrades after roughly eight to twelve subagent round trips as that history grows. The mitigation is the same instinct as the spokes: do not copy large outputs through the hub's conversation. Store a specialist's bulky artifact externally and pass a lightweight reference back, so the coordinator reasons over a pointer and a summary, not the full payload. Apply the same context discipline to the hub itself, and the orchestrator stays sharp across a long run.
Do this today
- Run a multi-agent task as a single agent first. Measure the token cost. Only then split it, and confirm the split actually clears the three-part gate before you keep it.
- Loosen a specialist's prompt so it returns full prose instead of three facts. Watch the coordinator's window and token bill climb, and the routing degrade.
- Make two specialists return conflicting facts. Confirm that your
synthesizestep surfaces the conflict rather than silently choosing, and that provenance names both sources. - Audit your hub for bulky outputs flowing through its conversation. Replace one large payload with an external artifact plus a lightweight reference.
The discipline is restraint
Marcus's agent did not fail because two agents are worse than one. It failed because two agents with no boundary share one swollen context and drift. Multi-agent orchestration is the harness function that decides when a split earns its 15x, then draws the boundary so each specialist works in a clean window and hands back a condensed result, while the coordinator owns strategy and a synthesis that surfaces conflicts instead of hiding them.
Most of the discipline is restraint: split only when the gate opens, and keep the layers clean when it does. The frameworks differ in surface, an AgentDefinition under agents versus a dict in subagents, but the boundary is identical. Scoped context in, compressed result out. Get that boundary right and the 15x becomes a price you can defend; get it wrong and you have rebuilt Marcus's nine-loop spiral with better syntax.
When you next reach for a second agent, ask the harder question first: does this task actually need two minds, or does it need one mind with a cleaner window?