Sub-Agents and Handoffs
Instead of an agent doing the work itself, it acts like a dispatcher. When a user asks for a refund, the Main Agent transfers the conversation to a specialized 'Refund Agent', handing over the context seamlessly.
Why Does This Exist?
In a Multi-Agent System, you have a team of highly specialized agents. But when a user logs into your website and types "I need help", which agent should answer them?
You need a way to route the user to the correct specialist, exactly like calling a corporate customer service hotline (Press 1 for Sales, Press 2 for Support).
Sub-Agents and Handoffs is the architectural pattern of using a primary "Triage Agent" that greets the user, figures out what they want, and then seamlessly transfers the conversation to a specialized "Sub-Agent".
Think of It Like This
The Hospital Triage Desk
Without Handoffs: A patient walks into the hospital with a broken arm. A heart surgeon is standing at the front door. The heart surgeon tries to fix the arm, realizes they don't have the right tools, and the patient suffers.
With Handoffs: A patient walks in and talks to the Triage Nurse (The Main Agent). The nurse asks, "What's wrong?" The patient says, "My arm is broken." The nurse says, "Please wait," and physically transfers the patient and their medical chart to the Orthopedics Wing (The Sub-Agent). The Orthopedic doctor fixes the arm perfectly.
How It Actually Works
A Handoff is quite literally implemented as an Agent Tool.
Just like an agent has a tool called get_weather(city), a Triage Agent is given a suite of tools called transfer_to_sales() or transfer_to_refunds().
The Execution Flow
- User: "I want to return my shoes."
- Triage Agent: Analyzes intent. It sees the
transfer_to_refunds()tool in its prompt. - Action: The LLM outputs
Action: transfer_to_refunds(). - The Backend Orchestrator: This is where the magic happens. The Python code intercepts this tool call. Instead of calling an API, the Python code halts the Triage Agent's execution loop.
- Context Injection: The Python code takes the chat history, injects it into a brand new System Prompt for the
Refund Agent, and starts a new execution loop. - Sub-Agent: The Refund Agent takes over the chat: "I can help you return those shoes. What is your order number?"
Show Me the Code
Frameworks like OpenAI's Swarm (an experimental multi-agent framework) are designed explicitly around this Handoff pattern.
# Conceptual example of a Handoff mechanismimport openai
# 1. Define our Sub-Agent Logicdef run_refund_agent(chat_history): print("\n[System] Handoff successful. Refund Agent taking over.") prompt = "You are a Refund Specialist. Ask the user for their Order ID." # We pass the existing chat history so the user doesn't have to repeat themselves! messages = [{"role": "system", "content": prompt}] + chat_history response = openai.chat.completions.create( model="gpt-4o", messages=messages ) return response.choices[0].message.content
# 2. Define the Main Triage Agentdef run_triage_agent(user_message): history = [{"role": "user", "content": user_message}] # The Triage agent has NO tools other than routing tools tools = [{ "type": "function", "function": { "name": "transfer_to_refunds", "description": "Call this immediately if the user mentions returns, refunds, or broken items." } }] print("[System] Triage Agent analyzing intent...") response = openai.chat.completions.create( model="gpt-4o", messages=[{"role": "system", "content": "You are a receptionist. Route the user."}] + history, tools=tools ) message = response.choices[0].message # 3. The Orchestrator handles the routing if message.tool_calls and message.tool_calls[0].function.name == "transfer_to_refunds": # Execute the handoff! Pass the context. return run_refund_agent(history) else: # Fallback if no routing needed return message.content
# --- Execution ---user_input = "These shoes don't fit, I want my money back."final_response = run_triage_agent(user_input)
print(f"\nResponse to User: {final_response}")
# -> [System] Triage Agent analyzing intent...# -> [System] Handoff successful. Refund Agent taking over.# -> # -> Response to User: I can certainly help you with returning those shoes. # -> Could you please provide your Order ID so I can pull up your account?Watch Out For
Lost in Translation
The entire point of a handoff is to prevent the user from having to repeat themselves. If a user tells the Triage Agent their name and account number, and the Triage Agent transfers them to the Refund Agent without passing the chat history (or a summary of the facts), the Refund Agent will say "Hello, what is your name?" This creates a terrible, frustrating user experience. You must ensure your orchestration code seamlessly passes the State or the Chat History into the Sub-Agent's context window.
The Quick Version
- Large systems require multiple specialized agents.
- The user cannot talk to all of them at once. They must be routed.
- A "Triage Agent" acts as a router. It uses NLP to determine intent.
- Handoffs are implemented as Agent Tools (e.g.,
transfer_to_x()). - When the tool is called, the Python backend halts the main agent, transfers the conversation context, and spins up the specialized Sub-Agent to continue the chat.
What to Read Next
- Read Supervisor Pattern to see how this handoff mechanism is used to build rigid corporate-style hierarchies.
- Read Agent-to-Agent Protocols to see the exact JSON structures used when passing context between these sub-agents.