Skip to content

All lessons

Area 03 // AI AgentsLesson 56 / 14015 min read

Multi-agent handoffs and orchestrator-worker

Multi-agent systems split work between several agents, either by handing a conversation from one specialist to another (handoffs) or by having an orchestrator break a job into parts that worker sub-agents do in parallel, each with its own focused context.

Think of it like this

Analogy

Handoff: you call your bank. The receptionist (triage) works out you need the mortgage team and transfers your call. The mortgage advisor now has the conversation; the receptionist is done.

Orchestrator-worker: a newspaper editor needs a big story on an election. They assign reporters to five cities at once, each researches on their own, sends back a short report, and the editor writes the final piece from the five reports.

The problem it solves

A single agent with dozens of tools and a long, mixed context gets confused: it picks the wrong tool, mixes up instructions for different domains, and slows down as the context fills with everything it has read. Some jobs also have many independent parts that could run at the same time.

Splitting work across specialised agents with focused instructions and contexts can help. But each extra agent adds cost and coordination, so it's a tool to use deliberately.

How it works: multi-agent handoffs

  1. Define specialist agents, each with focused instructions and a small tool set: triage, billing, technical support.
  2. Give each agent handoff options: the agents it may transfer to, with descriptions of when.
  3. A handoff is usually a tool call like transfer_to_billing. When the model calls it, your runtime switches the active agent.
  4. The receiving agent sees the conversation (optionally filtered or summarised) and continues with its own instructions and tools.
  5. Hand back or escalate when needed (to triage, or to a human).
  6. Frameworks such as the OpenAI Agents SDK (handoffs=[...]) and LangGraph make this explicit.

How it works: the orchestrator-worker pattern

  1. The orchestrator (lead agent) plans: it breaks the job into sub-tasks that can be done independently.
  2. It delegates each sub-task to a worker with clear instructions: goal, scope, output format, budget.
  3. Workers run in parallel, each in its own context with the tools it needs.
  4. Workers return short, structured results, not their whole transcripts.
  5. The orchestrator combines the results, checks gaps, and may launch another round.
  6. Limit it: cap the number of workers, their steps and tokens; this pattern can multiply cost quickly.

How it works: subagents

  1. A subagent is an agent started by another agent for a self-contained task, usually as a tool call ("research X and report back").
  2. It gets a fresh, clean context with only what it needs, so reading 50 files or 30 web pages doesn't pollute the parent's context (context engineering).
  3. It returns a concise result (a summary, a list, a decision) to the parent.
  4. Typical uses: exploration and search, reviewing (a separate reviewer subagent checks the main agent's work), parallel research, and isolating risky tools in a more restricted subagent.
  5. Coding agents commonly use subagents to search a codebase or review a change without filling the main session.
Left: a handoff passes control. Right: an orchestrator fans work out to parallel workers and gathers results.

See it in code

Needs
Nothing: plain Python 3. Workers are stubs; in real systems each is its own LLM loop.

Orchestrator-worker with parallel workers (asyncio) and a triage handoff.

multi_agent.py
import asyncio
 
# ORCHESTRATOR-WORKER: one agent splits the job, workers run in parallel with their own small context.
async def worker(company: str) -> str:
    await asyncio.sleep(0.01)                          # stand-in for a worker agent's own LLM loop
    facts = {"Acme": "revenue up 12%", "Globex": "revenue down 3%", "Initech": "revenue flat"}
    return f"{company}: {facts[company]}"              # workers return SHORT results, not their whole transcript
 
async def orchestrator(task: str) -> str:
    subtasks = ["Acme", "Globex", "Initech"]           # a real orchestrator asks an LLM to plan these
    results = await asyncio.gather(*(worker(c) for c in subtasks))   # fan out, then gather
    return f"{task}\n- " + "\n- ".join(results)        # synthesise (another LLM call in real life)
 
print(asyncio.run(orchestrator("Compare Q3 for our three competitors")))
 
# HANDOFF: the active agent passes the WHOLE conversation to a specialist and steps aside.
AGENTS = {"triage": "routes questions", "billing": "handles invoices and refunds", "tech": "fixes login problems"}
def triage(message: str) -> str:
    return "billing" if "refund" in message or "invoice" in message else "tech" if "login" in message else "triage"
for msg in ["I need a refund", "My login fails"]:
    print(f"{msg!r}: triage -> handoff to {triage(msg)} ({AGENTS[triage(msg)]})")
Output
Compare Q3 for our three competitors
- Acme: revenue up 12%
- Globex: revenue down 3%
- Initech: revenue flat
'I need a refund': triage -> handoff to billing (handles invoices and refunds)
'My login fails': triage -> handoff to tech (fixes login problems)

The OpenAI Agents SDK lesson shows real handoffs; LangGraph shows the same structures as graphs.

A real example

Example

A research assistant that went parallel

A consultancy's research agent answered "summarise how 15 competitors price their enterprise plans" in about 25 minutes with one agent, and often lost track of which company said what as its context filled with web pages.

They switched to orchestrator-worker: the lead agent listed the 15 companies and dispatched a worker per company (each limited to 8 steps), each returning a fixed JSON summary with source URLs. The lead compiled a table and sent two workers back for missing data. Wall-clock time dropped to about 5 minutes and accuracy rose, but token use was roughly 4× higher, which was acceptable for client work and not for their free tier.

The easy way

Start here

Start with one agent. When it gets confused by too many tools or domains, split into a triage agent with handoffs to two or three specialists. When a task has many independent parts, add a subagent tool ("research this one item and return a short summary") before building a full orchestrator.

When you grow: the production setup

Production multi-agent systems define clear contracts (inputs, output schemas, budgets) per agent, trace across agents with shared trace IDs, cap fan-out and depth, use cheaper models for workers, evaluate end-to-end and per agent, and guard handoffs so sensitive context isn't passed to agents that shouldn't see it.

Trade-offs

What you gain

  • Focused agents with small tool sets make fewer mistakes.
  • Parallel workers finish big research tasks faster.
  • Subagents keep the main context clean.

What it costs you

  • Much higher token cost (several agents' contexts).
  • Coordination failures: duplicated work, missed parts, conflicting results.
  • Harder to debug and evaluate.

When not to use it

  • One agent with a few tools handles the task well.
  • Sub-tasks depend heavily on each other's details (constant back-and-forth is slow and error-prone).

Common mistakes

  • Building a "team of agents" for a task one agent or a workflow could do.
  • Workers returning entire transcripts instead of short results.
  • No limits on how many workers can be spawned.
  • Passing private data in handoffs to agents that don't need it.
  • No cross-agent tracing.

Interview questions

Try answering out loud first, then open the model answer.

When would you use a multi-agent architecture?

Model answer

When a task decomposes into independent sub-tasks that benefit from parallelism (broad research), when distinct domains need different instructions and tools (support triage to billing or technical), or when context isolation is valuable (subagents reading large amounts of material). I'd weigh this against higher token cost and coordination complexity, and start from a single agent or workflow.

Compare handoffs and orchestrator-worker.

Model answer

Handoffs transfer control of the conversation from one agent to another specialist, which then interacts with the user directly; good for routing by domain. Orchestrator-worker keeps a lead agent in control that delegates sub-tasks to workers (often in parallel) and synthesises their outputs; good for decomposable jobs. Handoffs are sequential and conversational; orchestration is hierarchical and often parallel.

What are subagents and why use them?

Model answer

Subagents are agents spawned by another agent for a scoped task, each with its own context window and tools, returning a concise result. They prevent context pollution in the parent, allow parallel exploration, can use cheaper models, and can run with tighter permissions. The trade-offs are extra cost and the risk that summaries omit details the parent needed.

Quiz

Pick an answer to see whether it’s right and why.

  1. 01What is a handoff between agents?
  2. 02In the orchestrator-worker pattern, what does the orchestrator do?
  3. 03Why give a subagent its own context window?
  4. 04What's a major downside of multi-agent systems?
  5. 05Which task suits orchestrator-worker best?

0 of 5 answered

Key terms

Multi-agent system
An application where several agents with different roles, instructions or tools cooperate on a task.
Handoff
One agent transferring control of a conversation to another, more specialised agent.
Orchestrator-worker pattern
A lead agent splits a job into sub-tasks, worker agents do them (often in parallel), and the lead combines the results.
Subagent
An agent started by another agent for a scoped task, with its own context window, that returns a short result.
Context engineering
Deciding exactly what goes into the model's context at each step (instructions, tools, knowledge, memory, history, results) within a budget.

Before and after

Progress is saved in this browser only.