Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Openai Openai agents python Bidirectional Handoff Pattern

From Leeroopedia
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)

Bidirectional with Shared Context

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_turns on Runner.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_description values 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

GitHub URL

Source: handoffs/__init__.py

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment