TL;DR
-
A handoff is a controlled transfer of ownership from one agent to a specialist - not just "mentioning" another agent in chat.
-
Routing decides who should act next; the handoff package decides what they need to succeed (goal, constraints, artifacts, tool results).
-
Structured context beats transcript dumping - pass summaries, IDs, and typed fields; truncate or abstract raw histories.
-
Ownership must be exclusive at a time - ambiguous dual control causes duplicate tool calls and conflicting user messages.
-
Handoffs compose with HITL and durability - approve before privileged specialists act; checkpoint across transfers so crashes do not lose the case.
Why This Matters
Single agents degrade when tool counts and responsibilities grow. The practical fix is specialization: a triage agent, a billing agent, a technical support agent. Specialization only works if transfer between them is reliable.
Poor handoffs look like this: Agent A dumps a 40-message thread into Agent B; B re-asks questions the user already answered; both call issue_refund; the user sees contradictory replies. Good handoffs look like ticket systems: clear owner, structured case file, explicit reason for transfer, and a path back when the specialist is done.
Frameworks now expose handoffs as first-class primitives (OpenAI Agents SDK handoff, CrewAI task delegation, LangGraph supervisor edges). Understanding the pattern matters more than any one API - the failure modes are the same everywhere.
The Problem Multi-Agent Handoffs Solve
Multi-agent systems need a way to move work across boundaries when:
-
Tools and prompts should stay focused - billing tools do not belong on the coding agent.
-
Different models or temperatures fit different roles - creative drafting vs. strict policy checks.
-
Context windows fill up - specialists start with a compressed case, not the entire org history.
-
Permissions differ - only the refunds agent can call payment APIs; triage cannot.
-
Parallelism or staged pipelines - research hands off to writer hands off to reviewer.
Without an explicit handoff protocol, teams fake it with shared chat rooms. That scales poorly: loops, role confusion, and unreadable traces. Handoffs make delegation an engineered interface.
How We Got Here
Multi-agent handoffs evolved from helpdesk routing and supervisor patterns into first-class SDK primitives:
Diagram: From one agent to explicit handoffs
flowchart LR
A[Single mega-agent] --> B[Role prompts in one chat]
B --> C[Supervisor + workers]
C --> D[Typed handoff tools]
D --> E[Owned specialists + shared case]
Major components and how control or data moves between them.
| Era | Pattern | Gap |
|---|---|---|
| One agent, many tools | Prompt + huge registry | Tool selection and prompt conflict |
| Shared group chat | Agents talk freely | Ping-pong, unclear ownership |
| Supervisor graphs | Manager assigns workers | Still needs a transfer contract |
| Handoff primitives | OpenAI Agents SDK handoffs, LangGraph multi-agent | Typed package + exclusive owner |
| Production | + HITL + durable case state | Permissions enforced outside the model |
Anthropic — Building effective agents favors simple, composable patterns over swarms; handoffs earn their complexity only when specialization boundaries are real.
What Is a Multi-Agent Handoff?
A handoff is an orchestration event that:
- Selects a target agent (by router policy or model decision).
- Packages a handoff payload (goal, constraints, artifacts, user-visible summary).
- Transfers active ownership so only the target agent may act toward the user or call tools (unless designed as a consult).
- Optionally defines return semantics - report back to supervisor, close the case, or chain to the next specialist.
| Handoff style | Behavior | Typical use |
|---|---|---|
| Transfer | Source stops; target owns the user thread | Support escalation |
| Delegate & return | Target does subtask; result returns to supervisor | Manager-worker |
| Consult | Target advises; source keeps ownership | Optional specialist opinion |
| Broadcast / swarm | Multiple agents act (needs strong merge rules) | Rare in production UX |
Engineering Insight
💡 Key Idea - The hard part is not spawning agents. The hard part is the contract: what must be true when ownership moves, and what the new owner is allowed to do.
How Multi-Agent Handoffs Work
Routing
Routing answers "who next?" Options:
- Rules - intent classifier / keywords → agent (fast, testable)
- LLM router - small model picks from an agent catalog
- Supervisor agent - capable model plans and assigns
- User-driven - explicit "talk to billing"
Context transfer
What to pass:
- Case identity -
thread_id, customer_id, ticket_id - Goal & success criteria - what "done" means
- Constraints - policy, budget, language, tone
- Artifacts - order records, prior tool results, draft text
- Handoff reason - why triage escalated (helps the specialist and audit)
What not to pass blindly:
- Full raw tool dumps with secrets
- Entire unrelated conversation history
- Conflicting instructions from multiple system prompts
Diagram: Handoff sequence between specialists
flowchart LR
U[User] --> T[Triage]
T -->|to billing| B[Billing]
T -->|to tech| S[Tech support]
B -->|return| T
S -->|return| T
B -->|approve| H[Human review]
H --> B
T --> U
Major components and how control or data moves between them.
The diagram shows triage as a router/owner for the user-facing thread, specialists receiving structured handoffs, optional return to triage, and a HITL gate on privileged billing actions.
Handoff lifecycle
Diagram: State lifecycle
stateDiagram-v2
[*] --> Triage
Triage --> Routing: classify
Routing --> Specialist: handoff
Specialist --> Specialist: act
Specialist --> Triage: return
Specialist --> NextSpecialist: chain
Specialist --> HumanGate: write gate
HumanGate --> Specialist: decided
Triage --> Done: reply
Specialist --> Done: close
Done --> [*]
Valid states and transitions for this control-plane pattern.
Lifecycle notes: only one agent holds user-facing ownership at a time. Every hop increments a budget; A→B→A without new evidence is a defect. Privileged specialists should interrupt for HITL before writes.
Framework mapping
| Framework | Handoff mechanism |
|---|---|
| OpenAI Agents SDK | handoff(agent) tools the model can invoke |
| CrewAI | Task delegation / agent assignment in crews |
| LangGraph | Conditional edges, supervisor nodes, Send API for fan-out |
| AutoGen | Speaker selection / handoff in group chats |
| Mastra | Network/routing primitives for agent graphs |
Architecture
Diagram: Handoff state and ownership model
flowchart TB
User[User channel] --> Owner[Active owner lock]
Owner --> Router[Router / supervisor]
Router --> Catalog[Agent catalog]
Router -->|handoff| Spec[Specialist agent]
Spec --> Tools[Per-agent tool allowlist]
Spec --> Case[(Shared case store)]
Case --> Durability[Durable run_id]
Spec -->|privileged| HITL[HITL gate]
Spec --> Trace[Hop trace / spans]
Major components and how control or data moves between them.
| Component | Responsibility |
|---|---|
| Agent catalog | Name, description, tools, model, permissions per specialist |
| Router / supervisor | Chooses next agent; enforces max hops |
| Handoff schema | Typed payload validated before transfer |
| Shared state store | Case file durable across agents (durable execution) |
| Ownership lock | Ensures one active writer to the user channel |
| Guardrails | Per-agent tool allowlists (guardrails) |
| Trace graph | Parent run + child spans for each handoff |
Keep the catalog small. Routing accuracy collapses when you expose twenty vaguely named specialists. Compare frameworks that expose handoffs cleanly in Best AI Agent Frameworks and OpenAI Agents SDK vs LangGraph.
Step-by-Step Flow
Customer message: "My invoice looks wrong and the API returns 401 after we rotated keys."
-
Triage receives message - Classifies as mixed billing + technical; policy says pick primary intent or split.
-
Router decision - Primary: technical (service broken). Billing follow-up after auth restored. Handoff to Tech agent with
{customer_id, symptom: 401 after key rotation}. -
Ownership transfer - Triage stops calling tools; Tech agent becomes active.
-
Tech investigates - Calls
check_api_logs, finds invalid key; walks customer through rotation. Marks tech issue resolved in shared state. -
Return or chain - Tech returns summary to supervisor:
{tech_status: resolved, notes: "..."}. -
Second handoff - Supervisor hands off to Billing with invoice dispute fields + tech resolution note so Billing does not re-debug auth.
-
Billing proposes adjustment - Hits HITL before credit note.
-
Close - Supervisor synthesizes user-facing reply; ownership returns to triage or session ends.
-
Trace - Auditors see hop list: triage → tech → triage → billing → human → billing → done.
Real Production Example
OpenAI Agents SDK–style handoffs with a typed context package (conceptual, production-shaped):
from __future__ import annotations
from dataclasses import dataclass, asdict
from typing import Callable, Literal
@dataclass
class HandoffPackage:
case_id: str
goal: str
reason: str
customer_id: str
artifacts: dict
constraints: dict
return_to: str | None = "triage"
AgentName = Literal["triage", "billing", "tech"]
@dataclass
class Agent:
name: AgentName
description: str
tools: dict[str, Callable[..., str]]
instruct: str
llm: Callable[[str, list[dict], list[str]], dict]
def build_handoff_tool(target: Agent, package_builder: Callable[..., HandoffPackage]):
def handoff_to_target(**fields) -> str:
pkg = package_builder(target=target.name, **fields)
# Runtime swaps active agent + injects pkg into specialist context.
return f"HANDOFF:{target.name}:{pkg.case_id}"
handoff_to_target.__name__ = f"handoff_to_{target.name}"
handoff_to_target.__doc__ = (
f"Transfer ownership to {target.name} ({target.description}). "
"Use when the user's need matches that specialist."
)
return handoff_to_target
class HandoffRuntime:
def __init__(self, agents: dict[str, Agent], max_hops: int = 6):
self.agents = agents
self.max_hops = max_hops
self.active = "triage"
self.hops = 0
self.case: dict = {}
def run(self, user_message: str, case_id: str) -> str:
self.case = {
"case_id": case_id,
"messages": [{"role": "user", "content": user_message}],
"artifacts": {},
"handoff_log": [],
}
while self.hops <= self.max_hops:
agent = self.agents[self.active]
tool_names = list(agent.tools) + [
n for n in self.agents if n != self.active # handoff tools registered separately in practice
]
action = agent.llm(agent.instruct, self.case["messages"], tool_names)
if action["type"] == "reply":
return action["content"]
if action["type"] == "tool":
result = agent.tools[action["name"]](**action["args"])
self.case["messages"].append({"role": "tool", "content": result})
continue
if action["type"] == "handoff":
self._transfer(action["target"], action["package"])
continue
raise RuntimeError(f"unknown action {action}")
raise RuntimeError("max handoff hops exceeded")
def _transfer(self, target: str, package: HandoffPackage):
if target not in self.agents:
raise ValueError(target)
self.hops += 1
self.case["handoff_log"].append(asdict(package))
# Replace noisy history with structured specialist brief.
brief = (
f"You are {target}. Case {package.case_id}. Goal: {package.goal}. "
f"Reason: {package.reason}. Constraints: {package.constraints}. "
f"Artifacts: {package.artifacts}."
)
self.case["messages"] = [
{"role": "system", "content": brief},
{"role": "user", "content": self.case["messages"][0]["content"]},
]
self.active = target
# Specialists with narrow tools
billing = Agent(
name="billing",
description="Invoices, credits, refunds",
tools={"get_invoice": lambda invoice_id, **_: f"invoice {invoice_id} ..."},
instruct="Resolve billing issues only. Handoff back if the issue is technical.",
llm=lambda *a, **k: {"type": "reply", "content": "..."},
)
tech = Agent(
name="tech",
description="API auth, outages, SDK errors",
tools={"check_api_logs": lambda customer_id, **_: "401 invalid_api_key"},
instruct="Resolve technical issues only.",
llm=lambda *a, **k: {"type": "reply", "content": "..."},
)
Production additions: schema validation on HandoffPackage, durable case store, hop budgets, and per-agent tool allowlists enforced outside the model.
Design Decisions
| Decision | Option A | Option B | When to choose |
|---|---|---|---|
| Who routes | Rules / classifier | LLM supervisor | Rules when intents are stable; LLM when long-tail |
| Ownership | Hard transfer | Delegate & return | Transfer for user-facing specialists; return for subtasks |
| Context | Full transcript | Structured package + summary | Structured for production; transcript only when short |
| Hop limit | 3–4 | 8+ | Prefer low; investigate loops if you need more |
| User visibility | Silent transfer | "Connecting you to billing" | Disclose when humans expect a department switch |
| Specialist count | 2–4 | 10+ | Start small; split only with clear tool/permission boundaries |
Comparisons
Handoffs vs Tool Calling
| Dimension | Handoff (transfer) | Tool calling another agent |
|---|---|---|
| Ownership | Moves to the specialist | Caller keeps ownership |
| User thread | Specialist drives next turns | Caller synthesizes the reply |
| Context | Structured package + brief | Tool args / return value |
| Best for | Department-style escalation | Consult / subtask / lookup |
| Risk | Ping-pong hops | Nested agent loops without hop caps |
If the specialist should own the conversation, use a handoff. If you only need a result back, call it as a tool - see tool calling. OpenAI Agents SDK handoffs make this distinction explicit.
Handoffs vs Supervisor pattern
| Dimension | Direct handoffs | Supervisor pattern |
|---|---|---|
| Who routes | Source agent or rules | Central supervisor plans/assigns |
| Return path | Optional / chain | Usually delegate & return |
| Complexity | Lower for 2–3 specialists | Better for 3+ roles and multi-step plans |
| Failure mode | Peer ping-pong | Supervisor bottleneck / vague assignments |
Supervisors and handoffs compose: the supervisor chooses the target; the handoff package is still the contract. LangGraph supervisor graphs and OpenAI handoff tools are two implementations of the same idea - see LangGraph multi-agent.
Handoffs vs Shared Memory
| Dimension | Handoffs | Shared memory |
|---|---|---|
| Primary job | Transfer ownership + brief | Persist facts across agents/sessions |
| Payload | Goal, reason, IDs, artifacts | Profiles, embeddings, long-term notes |
| Without the other | Specialist lacks case facts | No clear owner; duplicate tool calls |
| Compose | Package points at memory IDs | Memory does not imply who may act |
Agent memory stores knowledge; handoffs decide who acts next. Production systems use a durable shared case file plus a typed transfer - not a vector store alone.
⚠ Common Mistakes
-
Transcript dumping - Flooding the specialist with noise and secrets. Summarize and pass IDs.
-
No hop limit - Agents ping-pong forever (
billing↔tech) until tokens die. -
Overlapping tools - Both agents can refund; handoffs do not prevent double charges.
-
Vague agent descriptions - Router cannot choose; model invents random escalations.
-
Losing case IDs - Specialist cannot fetch systems of record; re-asks the user for data already known.
-
No return path - Work finishes in a black hole; supervisor never closes the loop with the user.
-
Handoff as free-form chat between agents - Unstructured debates are hard to test and audit. Prefer typed payloads.
Where It Breaks Down
-
Tasks that are truly atomic - One agent with 4 tools is simpler; do not multi-agent for fashion.
-
Poor intent boundaries - If every ticket is "mixed," handoffs thrash. Improve triage taxonomy first.
-
Real-time voice / ultra-low latency - Extra model calls for routing add seconds; use rules.
-
Missing shared state - Handoffs without a durable case file fail after process restarts.
-
Organizational politics in prompts - Specialists with conflicting policies (sales vs. compliance) need a supervisor or human gate, not endless negotiation.
Decision tree: handoff or stay put?
Decision tree: When to hand off to another agent
flowchart TD
A[Current agent missing tools or policy?] -->|No| B[Continue in-place]
A -->|Yes| C{Clear specialist owner?}
C -->|No| D[Escalate to human / supervisor]
C -->|Yes| E[Typed handoff payload]
E --> F{Irreversible write at destination?}
F -->|Yes| G[HITL before write]
F -->|No| H[Resume specialist with shared case id]
G --> H
Hand off for specialization and policy boundaries - not to paper over a bloated tool registry.
When NOT to Use Multi-Agent Handoffs
Prefer a single agent (or a thin router) when:
- One agent with a small tool set already meets quality and latency targets - handoffs add hops without specialization wins.
- Intent boundaries are mushy - every ticket is "mixed"; fix triage taxonomy before adding specialists.
- You need sub-second replies - each hop is another LLM turn; use rules or one agent.
- Tools and permissions are identical across "roles" - renaming prompts is not a handoff architecture.
- You lack a durable shared case store - transfers lose IDs across restarts; add durable execution first.
- Eval coverage cannot assert hop sequences and forbidden tools - multi-agent bugs hide without traces.
Anthropic's guidance is pragmatic: start simple; earn multi-agent complexity with measurable boundaries.
Running in Production
Best Practice
✅ Best Practices - Typed handoff schemas, exclusive ownership, max hops, per-agent tool allowlists, durable case IDs, and traces that show the hop graph.
| Dimension | Consideration |
|---|---|
| Scaling | Stateless specialists + shared case store. Route by case_id affinity if caching helps. |
| Latency | Each hop adds an LLM turn. Cache triage classifications; skip hops when confidence is high for a single specialist. |
| Cost | More agents ≠ more cost if context is smaller per hop. Ping-pong is what burns money. |
| Monitoring | Hop count, loop rate (A→B→A), handoff reason distribution, time-in-specialist, re-asks after handoff. |
| Evaluation | Golden tickets with expected hop sequences and forbidden tools per agent. |
| Security | Enforce tool allowlists in runtime, not prompts. Redact secrets in handoff packages. |
Important
Enforce permissions in the tool layer. A handoff description that says "billing only" does nothing if the tech agent still has
issue_refundin its bound tool list.
Common Mistake
Measuring only final answer quality. Many handoff bugs show up as duplicate actions or user re-prompts. Trace hops and tool ownership in evals.
Related Guides
- OpenAI Agents SDK - Explicit
handoffprimitives between agents. - CrewAI - Role-based crews with delegated tasks.
- LangGraph - Supervisor patterns, conditional edges, and
Sendfor multi-agent graphs. - AutoGen - Group chat and speaker selection for multi-agent dialogues.
- Mastra - TypeScript-first agent networks and routing.
Explore and compare:
-
AI Agents - Single-agent loops that handoffs compose.
-
Multi-Agent Systems - Broader orchestration patterns (hierarchical, peer-to-peer).
-
Human-in-the-Loop - Approval gates on privileged specialists.
-
Durable Execution - Persist case state across hops and crashes.
-
Agent Memory - What to keep across sessions vs. per-handoff packages.
-
Tool Calling - Capabilities you partition across specialists.
-
Guardrails - Runtime limits that keep handoffs from escalating privilege.
If you understood this topic, read next:
Diagram: Learning path for handoffs
flowchart LR
A[Durable] --> B[HITL]
B --> C[Handoffs]
C --> D[Memory]
D --> E[Multi-agent]
Prerequisites: AI Agents · Multi-Agent Systems
Next topics: Human-in-the-Loop · Durable Execution · Agent Memory
Estimated time: 50 min · Difficulty: Intermediate
Key Takeaways
- A handoff is a typed ownership transfer, not a free-form multi-agent chat dump.
- Separate routing (who) from package design (what); keep exclusive user-facing ownership.
- Cap hops, partition tools per agent in the runtime, and persist a shared case via durable execution.
- Prefer consult-via-tool calling when the caller should keep ownership; hand off when the specialist should drive.
- Gate privileged specialists with HITL; do not rely on prompt-only permission stories.
- Start with two or three crisp roles; expand only when single-agent tool/prompt limits show up in metrics.
- Compare stacks via Best AI Agent Frameworks, LangGraph vs CrewAI, and OpenAI Agents SDK vs LangGraph.
FAQs
What is the difference between a handoff and calling another agent as a tool?
A tool-call consult usually returns data while the caller keeps ownership. A transfer handoff moves ownership so the specialist drives the next user turns and tool calls.
How many specialists should I start with?
Two or three with crisp boundaries (e.g., triage + billing + tech). Split further only when tool overload or prompt conflict is measurable.
Should the user see handoffs?
For customer support, yes - set expectations ("routing you to billing"). For internal pipelines (research → writer), silent handoffs are fine.
How do I stop agent ping-pong?
Max hops, require new evidence before bouncing back, and give supervisors merge authority. Log A→B→A patterns as defects.
What belongs in the handoff package?
Goal, reason, case IDs, constraints, and key artifacts. Not full secret-bearing tool transcripts.
Can handoffs run in parallel?
Fan-out is possible (research agents), but user-facing ownership should remain single-writer. Merge results in a supervisor before replying.
Which framework is best for handoffs?
Depends on stack. OpenAI Agents SDK makes handoff tools explicit; LangGraph excels at graph-shaped supervisors; CrewAI fits role/task crews. Compare via Best AI Agent Frameworks.
How do handoffs interact with memory?
Write durable case facts to shared memory/store; keep specialist working memory small. See Agent Memory.
Do I need a supervisor agent?
Helpful when there are 3+ specialists or multi-step plans. For two agents, a rules router may be enough.
How should permissions work across handoffs?
Each agent gets a tool allowlist enforced by the runtime. Handoff never grants new privileges implicitly.
What metrics matter?
Hops per case, loop rate, specialist success rate, post-handoff clarification rate, and forbidden-tool attempts.
Can handoffs include human agents?
Yes - escalate to a human queue as a terminal or intermediate owner. Same package idea: structured case file, clear ownership.
How do I test handoffs?
Fixture dialogues with expected hop sequences, assert package schema, assert only the active agent can call tools, and assert max-hop failures are graceful.
When should I avoid multi-agent handoffs?
When one agent with a small tool set already meets quality/latency targets. Handoffs add moving parts; earn them with clear specialization wins.