Implementation:Openai Openai agents python Bidirectional Handoff Pattern
| Knowledge Sources | |
|---|---|
| Domains | AI_Agents, Multi_Agent, Orchestration, Handoffs |
| Page Type | Pattern Doc |
| Last Updated | 2026-02-11 15:00 GMT |
Overview
This Pattern Doc documents the bidirectional handoff pattern in the OpenAI Agents Python SDK, where agents can delegate back and forth to each other. The pattern uses post-construction mutation of the handoffs list to establish circular references between agents.
Import
from agents import Agent, handoff
Interface
The bidirectional handoff pattern uses two approaches to establish back-references after agent creation:
Approach 1: Append with handoff() Helper
# After creating agents, add back-references:
agent_a.handoffs.append(handoff(agent_b))
agent_b.handoffs.append(handoff(agent_a))
Approach 2: Direct Agent References
# Or pass agents directly (auto-converted to handoffs):
agent_a.handoffs = [agent_b]
agent_b.handoffs = [agent_a]
Both approaches work because the SDK automatically converts raw Agent objects in the handoffs list to Handoff objects during processing.
Usage Examples
Basic Triage with Back-Reference
from agents import Agent, handoff
billing_agent = Agent(
name="billing",
instructions="Handle billing. If the question is unrelated to billing, transfer back to triage.",
handoff_description="For billing issues",
)
triage_agent = Agent(
name="triage",
instructions="Route to the right agent.",
handoffs=[billing_agent],
)
# Add back-reference so billing can escalate back
billing_agent.handoffs.append(triage_agent)
Multi-Specialist with Cross-Referencing
from agents import Agent, handoff
billing_agent = Agent(
name="billing",
instructions="Handle billing. Transfer to account for account issues, or back to triage for anything else.",
handoff_description="For billing and payment issues",
)
account_agent = Agent(
name="account",
instructions="Handle account management. Transfer to billing for payment issues, or back to triage.",
handoff_description="For account settings and profile changes",
)
support_agent = Agent(
name="support",
instructions="Handle technical issues. Transfer back to triage for non-technical questions.",
handoff_description="For technical support and troubleshooting",
)
triage_agent = Agent(
name="triage",
instructions="Route customer requests to billing, account, or support agents.",
handoffs=[billing_agent, account_agent, support_agent],
)
# Add back-references and cross-references
billing_agent.handoffs.extend([triage_agent, account_agent])
account_agent.handoffs.extend([triage_agent, billing_agent])
support_agent.handoffs.append(triage_agent)
from dataclasses import dataclass, field
from agents import Agent, Runner, RunContextWrapper, handoff
@dataclass
class SupportContext:
user_id: str
visited_agents: list[str] = field(default_factory=list)
async def on_billing_handoff(ctx: RunContextWrapper[SupportContext]) -> None:
ctx.context.visited_agents.append("billing")
async def on_triage_handoff(ctx: RunContextWrapper[SupportContext]) -> None:
ctx.context.visited_agents.append("triage")
billing_agent = Agent[SupportContext](
name="billing",
instructions="Handle billing questions.",
handoff_description="For billing issues",
)
triage_agent = Agent[SupportContext](
name="triage",
instructions="Route customer requests.",
handoffs=[handoff(billing_agent, on_handoff=on_billing_handoff)],
)
# Bidirectional: billing can hand back to triage
billing_agent.handoffs.append(
handoff(triage_agent, on_handoff=on_triage_handoff)
)
# Run with shared context
ctx = SupportContext(user_id="user_456")
result = await Runner.run(triage_agent, "I have a billing question.", context=ctx)
print(f"Agents visited: {ctx.visited_agents}")
Safety with max_turns
from agents import Agent, Runner
# Bidirectional handoffs can create cycles, so always set max_turns
result = await Runner.run(
triage_agent,
"Help me with my account.",
context=my_ctx,
max_turns=10, # Prevents infinite handoff loops
)
Key Considerations
- Forward reference problem: You cannot reference an agent in a constructor before it is created. Use post-construction
.append()or.extend()to add back-references. - Cycle safety: Always set
max_turnsonRunner.run()when using bidirectional handoffs to prevent infinite loops. - Agent instructions: Each agent should include clear instructions about when to hand back (e.g., "if the question is unrelated to your domain, transfer back to triage").
- Handoff descriptions: Good
handoff_descriptionvalues help the triage agent make correct routing decisions.
Source Reference
Source: src/agents/agent.py:L252 (handoffs field), src/agents/handoffs/__init__.py:L211-320
Related Pages
- Principle: Openai_Openai_agents_python_Bidirectional_Handoffs -- theory of circular handoff patterns.
- Implementation: Openai_Openai_agents_python_Handoff_Helper -- the
handoff()function used to create handoff objects. - Principle: Openai_Openai_agents_python_Handoff_Configuration -- foundational handoff theory.
- Workflow: Openai_Openai_agents_python_Multi_Agent_Handoff -- end-to-end multi-agent workflow.