Course ES

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.