OpenAI Agents SDK Handoffs in Python (2026) — let a triage agent transfer the conversation to specialists instead of stuffing every skill into one prompt.
You already build typed agents with Pydantic AI, ship tools via FastMCP, and talk agent-to-agent with Google A2A. This tutorial covers the official OpenAI Agents SDK handoff pattern: Agent + Runner + peer specialists.
TL;DR
- Handoffs appear to the model as tools like
transfer_to_billing_agent - Pass agents (or
handoff(...)) inAgent(..., handoffs=[...]) Runner.runloops model → tools → handoffs until a final output- Use
handoff_description/ recommended prompt prefixes so the model knows when to transfer
Install
pip install openai-agents
# or: uv add openai-agents
export OPENAI_API_KEY=sk-...
Example 1 — triage with two specialists
import asyncio
from agents import Agent, Runner, handoff
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
billing_agent = Agent(
name="Billing agent",
handoff_description="Handles invoices, charges, and payment questions.",
instructions=f"{RECOMMENDED_PROMPT_PREFIX}\nYou answer billing questions briefly.",
)
faq_agent = Agent(
name="FAQ agent",
handoff_description="Handles product FAQs and how-to questions.",
instructions=f"{RECOMMENDED_PROMPT_PREFIX}\nYou answer FAQs briefly.",
)
triage_agent = Agent(
name="Triage agent",
instructions=(
f"{RECOMMENDED_PROMPT_PREFIX}\n"
"Route the user to Billing or FAQ. Do not invent policy."
),
handoffs=[billing_agent, handoff(faq_agent)],
)
async def main():
result = await Runner.run(triage_agent, "I was charged twice for order 4421.")
print(result.final_output)
print("last agent:", result.last_agent.name)
asyncio.run(main())
When triage decides this is billing, the SDK transfers control. The specialist continues with conversation history unless you filter it.
Example 2 — handoff with typed input
from pydantic import BaseModel
from agents import Agent, handoff, RunContextWrapper
class EscalationData(BaseModel):
reason: str
async def on_handoff(ctx: RunContextWrapper[None], data: EscalationData):
print("escalating:", data.reason)
escalation = Agent(name="Escalation agent", instructions="Handle escalations calmly.")
escalation_handoff = handoff(
agent=escalation,
on_handoff=on_handoff,
input_type=EscalationData,
)
When to use this vs other stacks
- OpenAI Agents SDK — first-party Runner/handoffs/tools for OpenAI-centric apps
- Pydantic AI — typed deps + tools across providers
- LangGraph — explicit graphs and durable state machines
Production tips
- Give each specialist a clear
handoff_description - Prefer one handoff per destination; let the model choose
- Use
input_filterwhen the next agent should not see prior tool spam - Keep secrets in env; set
max_turnsdeliberately on long runs
Wrap-up
In 2026, multi-skill support bots should hand off — not monologue. The OpenAI Agents SDK makes that a first-class Agent + Runner pattern with tools the model can call to transfer control.