Handoff Design: Passing State and Control Between Agents
A deep dive into agent handoff patterns — how to pass state and control between agents cleanly, with SDK implementations and failure mode analysis.
Published on • September 9, 2026
AI Assistant

Handoff Design: Passing State and Control Between Agents
The handoff is the hardest part of multi-agent systems. The agent that understands billing hands off to the agent that handles refunds — and somehow, critical context gets lost. The customer repeats themselves. The refund agent doesn’t know what billing already verified. The user experience falls apart.
Why Handoffs Fail
Most agent frameworks expose “start” and “kill” as first-class controls. Pause, resume, and cancel are afterthoughts. Without a proper interruption and handoff surface, autonomous loops produce a binary “let it finish or lose everything” experience that destroys user trust.
Three Handoff Patterns
Sequential Handoff
Agent A completes its task, packages results, and passes a structured packet to Agent B. Simple, predictable, but creates latency when Agent B needs Agent A’s intermediate reasoning.
Agent A (billing) → HandoffPacket → Agent B (refund)
Shared-Context Handoff
Both agents read from a shared state store. Agent A writes its findings; Agent B picks up where A left off without waiting for a full handoff.
# Shared context via state store
context = {
"customer_id": "cust_123",
"billing_verified": True,
"refund_amount": 50.00,
"reason": "duplicate_charge"
}
Supervisor-Routed Handoff
A supervisor agent decides which specialist should handle each step, maintaining full context and routing dynamically.
The Handoff Contract
What to pass in a handoff:
| Field | Purpose |
|---|---|
context_summary | What was done, what remains |
customer_state | Verified facts, pending items |
decision_trail | Why specific actions were taken |
tool_cache | Pre-fetched data for the next agent |
provenance | Which agent produced each piece of info |
What NOT to pass:
- Raw conversation history (noise)
- Internal reasoning traces (confuse the next agent)
- Credentials or secrets
OpenAI Agents SDK Implementation
from agents import Agent, handoff
billing_agent = Agent(
name="billing_specialist",
instructions="Verify billing issues and resolve payment problems.",
)
refund_agent = Agent(
name="refund_specialist",
instructions="Process refunds based on verified billing information.",
)
triage_agent = Agent(
name="triage",
instructions="Route customers to the right specialist.",
handoffs=[billing_agent, refund_agent],
)
Input Filtering for Handoffs
Control what context travels with the handoff:
from agents import handoff, HandoffInputFilter
class BillingToRefundFilter(HandoffInputFilter):
def filter(self, input_data):
# Only pass verified facts, not raw messages
return {
"customer_id": input_data.get("customer_id"),
"billing_verified": input_data.get("billing_verified"),
"refund_amount": input_data.get("refund_amount"),
}
handoff(
agent=refund_agent,
input_type=RefundRequest,
input_filter=BillingToRefundFilter(),
)
Common Failure Modes
Context Bleed — Agent B sees Agent A’s internal reasoning and gets confused about what’s been communicated to the user.
State Staleness — Agent B acts on data that Agent A modified after the handoff packet was created.
Circular Delegation — Agent A hands to Agent B, which hands back to Agent A, creating an infinite loop.
Lost Tool Results — Agent A fetched data with a tool call, but the result doesn’t travel with the handoff.
Premature Handoff — Triage agent routes too early before the specialist has enough context to help.
Framework Comparison
| Framework | Handoff Primitive | Context Passing |
|---|---|---|
| OpenAI Agents SDK | handoff() + input_filter | RunState serialization |
| LangGraph | Command.PARENT + subgraphs | Checkpointer state |
| PydanticAI | Agent delegation | Typed context objects |
| CrewAI | Process orchestration | Shared task context |
When Handoffs Are the Wrong Tool
Not every handoff is necessary. Consider:
- Tool use instead — If the agent just needs data from another system, use a tool call, not a handoff
- Sub-agent instead — If the sub-task is well-defined and returns a result, use
Agent.as_tool() - Single agent with tools — If one agent can handle everything with the right tools, avoid the handoff complexity
The Takeaway
Handoffs are necessary when different agents own different parts of a workflow, but they’re expensive in context and complexity. Use them sparingly, define a clear contract for what travels, and always test the “user repeated themselves” scenario.
💡 Start with
handoff()in the OpenAI Agents SDK andinput_filterto control what context crosses agent boundaries.