Multi-agent systems
Coordinating many agents on one large job.
Create a free account Try part of it here
Why it matters
In Phase 1 you built single agents: one model calling tools in a loop. Bigger jobs are now often split across many agents that work at the same time. Think of researching a market from dozens of sources, checking every page of a large website, or working through a quarter's campaign data. Done well, a team of agents is faster and more thorough. Done badly, it multiplies cost and mistakes. This lesson shows you how to tell the two apart.
What you'll be able to do
- Describe the main multi-agent patterns: orchestrator-workers, parallel sections, pipelines, evaluator loops
- Estimate the speedup from parallel subagents, and see how dependencies cap it
- Calculate how errors compound across agent hand-offs, and what a verifier buys
- Decide when several agents beat one, given their extra cost and coordination problems
What it covers
- A team instead of one worker
- How much faster?
- Errors add up
- Worked example: a three-step chain
- A checker at each hand-off
- When one agent is better
- What the code looks like
A team instead of one worker
Picture a big house move. One person can do it alone, but it takes all weekend. With friends, one person packs the kitchen while another packs the books. Someone has to say who does what. Someone also has to check that nothing got left behind.
An AI agent is a model that calls tools in a loop until a task is done. A multi-agent system puts several agents on one job. Most fit one of four patterns:
- Orchestrator-workers: a lead agent, the orchestrator, splits the job into smaller tasks. It hands each one to a subagent, a helper agent with its own context window and tools. Then it combines what they find. This suits open-ended research.
- Parallel sections: the same instructions run on separate pieces at the same time. That might be one agent per competitor, per web page or per region. The results are merged at the end.
- Pipelines: specialist agents work in a line, like a production line. Research, then draft, then edit, then fact-check. Each one hands its output to the next.
- Evaluator loops: one agent writes and a second one critiques the work against a checklist. Then the first one revises. It works like the critique-and-revise step in the constitutional AI lesson.
Why go to the trouble? There are three main gains:
- Speed. Agents that work in parallel, meaning at the same time, finish sooner by the clock.
- Separate context windows. A context window is everything a model can read in one request. Each subagent can read deeply without filling up the lead agent's window.
- Specialization. Each role can have its own prompt, its own tools, or even its own model.
It can pay off. Anthropic built a research system in which a lead agent directs subagents working in parallel. The company reported that it did about 90% better than a single agent on an internal research test. It also used roughly 15 times as many tokens as a chat conversation. You pay for tokens, so that matters.
Show the answer
Parallel sections
The same instructions run on separate pieces at the same time. A pipeline would pass the work along a line, one agent after another.
How much faster?
Think about cooking a big dinner with friends. Extra cooks can chop vegetables and set the table at the same time. But the roast still needs its full time in the oven. No number of helpers changes that.
Agent work behaves the same way. Here's a research job as a list of tasks. Each task has a length in minutes. It also has prerequisites, the tasks that must finish before it can start:
- plan comes first and takes 3 minutes.
- Six research threads each need only the plan: pricing, reviews, social, ads, SEO and partners.
- compare needs pricing and reviews to be done.
- synthesis, the final write-up, needs everything else.
The code below simulates agents, where is the number of agents working at once. Whenever an agent is free, it picks up any task whose prerequisites are done. With more than one agent, each task also costs an extra half minute for handing the work over.
tasks = { # name: (minutes, prerequisites)
"plan": (3, []),
"pricing": (9, ["plan"]), "reviews": (12, ["plan"]),
"social": (8, ["plan"]), "ads": (10, ["plan"]),
"seo": (11, ["plan"]), "partners": (7, ["plan"]),
"compare": (6, ["pricing", "reviews"]),
"synthesis": (5, ["compare", "social", "ads", "seo", "partners"]),
}
def finish_time(agents, overhead_per_task=0.5):
done, running, clock = {}, [], 0.0 # done: name -> finish time
while len(done) < len(tasks):
ready = [t for t, (_, pre) in tasks.items()
if t not in done and t not in [r for r, _ in running] and all(p in done for p in pre)]
while ready and len(running) < agents: # start ready tasks on free agents
t = ready.pop(0)
extra = overhead_per_task if agents > 1 else 0 # handing work over costs a little
running.append((t, clock + tasks[t][0] + extra))
running.sort(key=lambda r: r[1])
t, clock = running.pop(0) # jump to the next task to finish
done[t] = clock
return clock
total = sum(m for m, _ in tasks.values())
for agents in [1, 2, 4, 8]:
t = finish_time(agents)
label = f"{agents} agent{'s' if agents > 1 else ''}:"
print(f"{label:<10}{t:5.1f} minutes speedup {total / t:.1f}x")
print(f"\nthe critical path (plan -> reviews -> compare -> synthesis) alone takes {3 + 12 + 6 + 5} minutes")Speedup is how many times faster the job gets. It's the one-agent time divided by the new time. With 4 agents, that's 71 ÷ 29, about 2.4.
Going from 1 agent to 4 cuts the job from 71 minutes to 29. But 8 agents save only one more minute. Look at the chain plan → reviews → compare → synthesis. Each step has to wait for the one before it. That chain takes 3 + 12 + 6 + 5 = 26 minutes, however many agents you add. Add the half-minute hand-off for each of its four tasks, and you get the 28 minutes that 8 agents need.
The longest chain of tasks that depend on each other is called the critical path. It can't be split up. The same holds for any team, human or AI. Split work only where the pieces are truly independent, and expect the longest chain to set the pace.
Show the answer
The remaining time is a chain of dependent steps (the critical path) that can't run in parallel
Once the independent work is spread out, the longest chain of dependent steps sets the finish time.
Show the answer
34 minutes
The chain plan → reviews → compare → synthesis now takes 3 + 20 + 6 + 5 = 34 minutes. The other chains are shorter: plan → pricing → compare → synthesis is 23, and plan → SEO → synthesis is 19.
Errors add up
Think of the party game telephone. A message passes from person to person. Each person usually gets it right, but small slips pile up. By the end, the message is often garbled.
Agent hand-offs work the same way. Each one is a chance to drop or bend information. Say each step in a chain works with probability . That's a number between 0 and 1, so 0.95 means 95% of the time. Now say the chain has steps, and each one succeeds or fails on its own. Then the chance that the whole chain works is:
In plain words: write down the step's success rate once for each step, then multiply them all together. Two steps at 90% give 0.9 × 0.9 = 0.81. So the pair works only 81% of the time, even though each step works 90% of the time.
This is a simplification. Real steps don't always succeed or fail on their own, so treat it as a rough guide.
Three 90% steps only give 73%. Long chains need a much better step, or a checker at each hand-off.
Worked example: a three-step chain
Each step in a chain gets it right 90% of the time. How often does a chain of one, two or three steps get everything right?
p = 0.9 # each step succeeds 90% of the time for n in [1, 2, 3]: print("steps:", n, " chance all succeed:", round(p ** n, 3))
One step works 0.9 of the time. Two steps: 0.9 × 0.9 = 0.81. Three steps: 0.81 × 0.9 = 0.729.
In Python, p ** n means to the power . So 0.9 ** 3 is 0.9 × 0.9 × 0.9. Each extra step takes away another 10% of whatever chance was left.
print(0.5 ** 3)
Show the answer
0.125
0.5 × 0.5 × 0.5 = 0.125. The whole chain works only 1 time in 8.
A checker at each hand-off
A verifier is a checker placed at a hand-off. It might be another agent or a set of tests. It might also check that the output fits a fixed format, called a schema. When it spots a mistake, the step runs again. That second try is a retry.
Here's how much one checker helps. Take a step that's right 95% of the time. The checker catches 80% of the mistakes, and the step gets one retry. Now the step can work in two ways:
- right the first time: 95%;
- wrong, caught, then right on the retry: 5% × 80% × 95% = 3.8%.
Together that's 98.8%, up from 95%. The code below works this out for chains of different lengths. Its catch setting is the share of mistakes the checker spots. Its retries setting is how many retries a step gets.
def chain_success(p, steps, catch=0.0, retries=0): # one step: right first time, or wrong-but-caught and retried (each retry has the same chances) step = p miss = 1 - p for _ in range(retries): step += miss * catch * p miss = miss * catch * (1 - p) return step ** steps for steps in [1, 3, 6, 10]: plain = chain_success(0.95, steps) checked = chain_success(0.95, steps, catch=0.8, retries=1) print(f"{steps:>2} steps at 95% each: {plain:.0%} end to end with a verifier (catches 80%, one retry): {checked:.0%}")
Ten steps at 95% each work together only 60% of the time. With a checker and one retry, that rises to 89%. So well-built multi-agent systems put checks at their hand-offs. They use schemas for structured outputs and tests for code. They use citations that can be checked. And an evaluator agent reviews the work before anything reaches a person.
Go deeper The formula behind the checker
With one retry, the chance that a single step works is:
Here is the catch rate, the share of mistakes the checker spots.
In plain words: the step works if it's right the first time (). It also works if it's wrong (), the mistake is caught (), and the retry is right ( again).
With and , that's 0.95 + 0.05 × 0.8 × 0.95 = 0.988. For ten steps, multiply 0.988 by itself ten times. You get about 0.886, the 89% in the table.
p, c = 0.95, 0.8 step = p + (1 - p) * c * p print(round(step, 3), round(step ** 10, 3))
In the chain_success code, the line step += miss * catch * p adds this second route once per retry. After each retry, miss becomes the chance of needing yet another one: wrong, caught, and wrong again.
Show the answer
94.5%
Right the first time is 90%. Wrong, caught, then right on the retry is 10% × 50% × 90% = 4.5%. Together that's 94.5%.
When one agent is better
More agents aren't always better. Watch for three warning signs:
- Tightly coupled work. This is work where every decision depends on every other one, like most coding and writing. Split it up, and each agent works from only part of the picture, so the pieces clash. Cognition, the company behind the Devin coding agent, argued in 2025 for single-threaded agents for this reason. Single-threaded means one agent doing the steps in order.
- Cost. Each subagent re-reads its instructions and context. So token use, and the bill, rises sharply. Use several agents only when the job is worth the extra spend.
- Debugging. When something fails, it's harder to trace across many agents. Log every agent's inputs, tool calls and outputs.
Show the answer
Each subagent re-reads its own instructions and context, so token use rises sharply
You pay for every token each agent reads and writes. Five agents reading their own instructions and context means many more tokens than one agent.
What the code looks like
A typical orchestrator gives the lead model a tool for starting subagents. Each subagent is a separate model call with its own instructions. The sketch below shows the idea. run_agent makes one model call. A thread pool runs three research subagents at the same time, each with a fresh context. A final call combines their findings into one briefing.
import anthropic from concurrent.futures import ThreadPoolExecutor client = anthropic.Anthropic() def run_agent(instructions, task): reply = client.messages.create(model="claude-opus-5-5", max_tokens=4000, system=instructions, messages=[{"role": "user", "content": task}]) return next(b.text for b in reply.content if b.type == "text") subtasks = ["pricing pages of our 5 main competitors", "their review-site ratings", "their ad messaging"] with ThreadPoolExecutor() as pool: # subagents run in parallel, each with a fresh context findings = list(pool.map(lambda t: run_agent("Research thoroughly. Cite sources.", t), subtasks)) report = run_agent("Combine the findings into a competitor briefing; flag contradictions.", "\n\n".join(findings))
This sketch is for reading, not running, because it calls a real model API. In practice the subagents would also get tools, such as web search or the MCP servers from Phase 1. And the lead would choose the subtasks itself.
For marketers: some jobs are a good fit. Think of competitive research across many brands, auditing hundreds of landing pages or tagging a big archive. Each splits into independent pieces that you merge at the end. A single campaign concept is not a good fit. One agent, or one person, holding the whole picture does better.
Practice what you just read
- 5 code examples you can edit and run
- a practice exercise that checks your code
- a 5-question quiz
- 5 review cards for spaced repetition
Try a quiz question
Which task is the best fit for an orchestrator with parallel subagents?