Chapter 21 of 22 · advanced
Multi-agent orchestration
What this session covers
A single agent has a ceiling. Ask one to "write this, then critique it, then rewrite it, and fact-check it, and keep the tone friendly" and the instructions start elbowing each other — a model can only hold so many roles in its head at once. Multi-agent is the obvious fix: split the work, give each role its own agent with its own narrow prompt, and pass the output down the line. A writer that only writes and a critic that only critiques beat one agent trying to do both, for the same reason a team beats one person wearing every hat.
The thing to see this week is that orchestration isn't a framework. It's control
flow. Passing one agent's output to the next is a function call. A router is an
if over a classification. A debate is three calls and a comparison at the end.
Frameworks hand you retries, tracing, and parallelism — real conveniences, I'm
not knocking them — but the core underneath is Python you can read top to bottom,
and building it by hand is how you find out what those frameworks are doing on
your behalf.
OpenAI
Each agent is a system prompt with exactly one job. The narrowness is the whole advantage — a role you can state in a single sentence is a role the model actually performs well.
# Each agent is just a system prompt with one job. The narrowness is the point —
# a role you can state in a sentence is a role the model does well.
WRITER = "You are a concise technical writer. Write a short, clear explanation for a beginner."
CRITIC = ("You are a sharp editor. List 2-3 specific, actionable problems with the draft "
"(accuracy, clarity, or missing context). If it's already excellent, say so briefly.")
The orchestration is just Python shuttling text between them — draft, critique the draft, rewrite against the critique. You can read exactly what each agent got handed, which is why hand-rolled orchestration is easier to debug than a framework's message-passing you can't see.
# Orchestration is just Python passing outputs between agents. Here: draft, then
# critique the draft, then rewrite addressing the critique. No framework — the
# control flow IS the orchestration, and you can see exactly what each agent saw.
def collaborate(task: str) -> tuple[str, str, str]:
draft = agent(WRITER, task)
critique = agent(CRITIC, f"TASK: {task}\n\nDRAFT:\n{draft}")
final = agent(WRITER, f"TASK: {task}\n\nYour draft was critiqued:\n{critique}\n\n"
f"Rewrite the explanation, fixing every issue raised.")
return draft, critique, final
Gemini
Same team, same control flow, Gemini's calls underneath. The pattern doesn't lean on the provider, because the pattern is nothing but composition.
def collaborate(task: str) -> tuple[str, str, str]:
draft = agent(WRITER, task)
critique = agent(CRITIC, f"TASK: {task}\n\nDRAFT:\n{draft}")
final = agent(WRITER, f"TASK: {task}\n\nYour draft was critiqued:\n{critique}\n\n"
f"Rewrite the explanation, fixing every issue raised.")
return draft, critique, final
One dose of realism before you go build a swarm. More agents means more calls, more latency, and a bigger bill — a three-agent pipeline costs three times what one call costs — so reach for multiple agents when the quality gain earns it, not as a reflex. And these systems fail in a way a single agent can't: agents talking past each other, a critic the writer quietly ignores, a router that funnels everything to "general." The evaluation harness from week 15 is how you catch it — score the final output, not your faith in the diagram.
Put it to work
Three docker-compose apps under code/showcase/<slug>/, each one a classic
multi-agent shape. Same drill as always: bash bootstrap-secrets.sh, docker compose up --build, http://localhost:3000.
Showcase 1 — Writer + critic
The pipeline pattern — draft, critique, revise, with all three stages shown so you can watch what the critic bought you. This is the multi-agent move that pays off most reliably: a dedicated critic catches what a self-critiquing writer talks itself out of flagging.
Showcase 2 — Router
The routing pattern — a cheap classifier agent picks a specialist, and the specialist answers. It's how you spread a support bot across domains without one bloated prompt that's mediocre at all of them. The reply names the specialist it landed on, so the routing decision is right there in front of you.
Showcase 3 — Debate
The adversarial pattern — for, against, and a judge. Disagreement is the whole point: two agents shoving in opposite directions surface tradeoffs a single agent would smooth over, and the judge forces a decision instead of a shrug.
Pipeline, router, debate — three ways to compose agents, all of them plain control flow over one-job prompts.
Run it
The README in this folder lists the Python version, the install line, the two
environment variables, and the exact commands. Keys come from the untracked
.env at the course root. The basic scripts print every stage, so you can watch
the agents hand off to each other.
Takeaways
When one agent hits its ceiling, split the roles. Multi-agent orchestration is
focused, one-job agents composed with plain control flow — a pipeline is function
calls, a router is an if, a debate is three calls and a comparison, and the
frameworks only pile convenience on top of that. Three patterns are worth
carrying: the pipeline (draft, critique, revise), the router (classify, then
dispatch to a specialist), and the adversarial panel (for, against, judge). Pay
for the extra agents only when the quality earns the extra calls and latency, and
judge the final output rather than trusting the architecture — the new way this
breaks is agents miscommunicating. Next week we put the whole course together in
one capstone application.