Agent Handoffs: Seamlessly Transferring Conversations Between Specialized Agents
Learn how to implement clean agent handoffs using the OpenAI Agents SDK, including handoff triggers, context transfer, conversation continuity, and patterns for preserving user experience across agent boundaries.
What Is an Agent Handoff?
An agent handoff is the moment when one agent transfers control of a conversation to another agent. The first agent stops processing, the second agent takes over, and the user ideally notices nothing — the conversation continues naturally.
Handoffs are the fundamental building block of multi-agent systems. Without them, you either have a single monolithic agent or completely disconnected agents that cannot collaborate. Handoffs enable specialization while maintaining conversation continuity.
Handoffs in the OpenAI Agents SDK
The SDK provides the handoff() function to declare that one agent can transfer to another. When the model decides a handoff is appropriate, it calls a special tool that the SDK intercepts to perform the transfer:
from agents import Agent, Runner, handoff
billing_agent = Agent(
name="Billing Agent",
instructions="""You handle billing questions: invoices, payments,
refunds, and subscription changes. If the user asks about technical
issues, hand off to the Technical Agent.""",
handoffs=[], # Will be set after technical_agent is defined
)
technical_agent = Agent(
name="Technical Agent",
instructions="""You handle technical support: bugs, configuration,
API issues, and troubleshooting. If the user asks about billing,
hand off to the Billing Agent.""",
handoffs=[handoff(billing_agent)],
)
# Now set billing agent's handoffs
billing_agent.handoffs = [handoff(technical_agent)]
triage_agent = Agent(
name="Triage Agent",
instructions="""You are the first point of contact. Determine
whether the user needs billing help or technical help, and hand off
to the appropriate specialist immediately.""",
handoffs=[handoff(billing_agent), handoff(technical_agent)],
)
result = Runner.run_sync(triage_agent, "I was charged twice for my subscription")
print(result.final_output)
The triage agent reads the message, recognizes it as a billing issue, and hands off to the billing agent. The billing agent receives the full conversation history and responds directly.
How the SDK Manages Handoffs Internally
When you declare handoffs=[handoff(billing_agent)], the SDK registers a special tool for each handoff target. The tool name follows the pattern transfer_to_<agent_name>. When the model calls this tool, the SDK does not execute a normal function — instead, it swaps the active agent to the target, passes the accumulated conversation history forward, and continues the agent loop with the new agent.
This means the target agent sees the entire conversation that preceded the handoff. It knows what the user said, what the previous agent said, and why the handoff was triggered — all from the conversation history.
Customizing Handoff Behavior
You can provide additional context during a handoff by passing a description or an on_handoff callback:
from agents import Agent, handoff
def prepare_billing_context(ctx):
"""Called when handing off to billing. Can enrich context."""
# You could fetch the customer's billing record here
# and inject it into the conversation
pass
triage_agent = Agent(
name="Triage Agent",
instructions="Route users to the right specialist.",
handoffs=[
handoff(
billing_agent,
tool_description_override="Transfer to billing for payment, invoice, or refund questions",
on_handoff=prepare_billing_context,
),
handoff(
technical_agent,
tool_description_override="Transfer to tech support for bugs, errors, or configuration help",
),
],
)
The tool_description_override helps the model make better routing decisions by providing explicit criteria for when to trigger each handoff. The on_handoff callback lets you run setup logic — like fetching user-specific data — before the target agent starts processing.
Bidirectional Handoffs
Agents can hand off back and forth. A billing agent might discover that a payment failure is actually caused by a technical issue and hand off to the technical agent. The technical agent might find that the fix requires a subscription change and hand back to billing.
See AI Voice Agents Handle Real Calls
Book a free demo or calculate how much you can save with AI voice automation.
The SDK supports this naturally, but you must be careful about infinite loops. If both agents keep handing off to each other, the conversation will ping-pong indefinitely. Prevent this with clear instructions:
billing_agent = Agent(
name="Billing Agent",
instructions="""Handle billing questions. If you discover the issue
is technical (API errors, integration bugs), hand off to Technical.
IMPORTANT: If Technical already handed this conversation to you,
do NOT hand it back. Instead, acknowledge the complexity and ask
the user if they'd like to escalate to a human.""",
handoffs=[handoff(technical_agent)],
)
You can also set max_turns on the Runner to enforce a hard ceiling on the total number of agent turns:
result = Runner.run_sync(triage_agent, user_message, max_turns=10)
Maintaining Conversation Continuity
The biggest risk with handoffs is a jarring user experience. The user is talking to one agent, and suddenly the tone, vocabulary, or knowledge level shifts. Three practices help maintain continuity:
1. Consistent persona framing. Give all agents the same base personality traits. If your brand voice is friendly and concise, every agent should reflect that.
2. Invisible handoffs. Do not announce "I am now transferring you to the billing department." Instead, the new agent should pick up naturally: "I can see you were charged twice on March 12. Let me look into that refund."
3. Context summaries. For long conversations where full history transfer is impractical, use a summarization step during handoff to compress the relevant context.
Handoff Triggers
There are several patterns for when to trigger a handoff:
- Intent-based: The user's intent does not match the current agent's specialty
- Capability-based: The current agent lacks a tool needed for the request
- Confidence-based: The current agent is uncertain about its response
- Escalation-based: The conversation has exceeded a complexity threshold
The model handles intent-based and capability-based triggers naturally through the handoff tool descriptions. For confidence-based triggers, you can instruct the agent explicitly: "If you are less than 80% confident in your answer, hand off to a senior specialist."
FAQ
What happens to the conversation history during a handoff?
The full conversation history is passed to the target agent. The target agent sees all user messages, all previous agent messages, and all tool call results from before the handoff. This means the target agent has complete context without the user repeating themselves.
Can I hand off to an agent running on a different model?
Yes. Each agent in the OpenAI Agents SDK can specify its own model parameter. The triage agent could run on GPT-4o-mini for fast routing, while the specialist agents run on GPT-4o for deeper reasoning. Handoffs work seamlessly across models.
How do I test handoff behavior?
Write test cases where the input clearly belongs to a specific specialist and verify the final output comes from the correct agent. The SDK's tracing system shows which agents were active during a run, making it easy to assert that the expected handoff path was followed.
#AgentHandoffs #MultiAgentSystems #OpenAIAgentsSDK #ConversationDesign #ContextTransfer #AgenticAI #LearnAI #AIEngineering
CallSphere Team
Expert insights on AI voice agents and customer communication automation.
Try CallSphere AI Voice Agents
See how AI voice agents work for your industry. Live demo available -- no signup required.