Welcome to another lesson about agentic patterns! In the previous lesson, you mastered orchestrating agents as tools, where a central planner agent could dynamically delegate tasks to specialist agents and receive their results back. Today, we're exploring a fundamentally different approach called the handoff pattern, where agents can completely transfer control to other specialized agents rather than just calling them as tools.
In this lesson, you'll extend the Agent class constructor to support handoff targets, create a handoff tool schema that enables control transfers, and implement the core handoff logic that passes conversation context between agents. We'll build a practical example with a general assistant that can hand off mathematical problems to a specialized calculator assistant, demonstrating how agents make intelligent decisions about when to transfer control versus handling tasks themselves.
The handoff pattern represents a different philosophy of agent collaboration compared to the tool delegation approach you learned previously. When an agent uses another agent as a tool, it's essentially asking for help while maintaining responsibility for the final response. When an agent performs a handoff, it's saying, "this other agent is better equipped to handle this entire conversation from here on."
Consider the difference in conversation flow. In tool delegation, the user interacts with the orchestrator throughout: the user asks a question, the orchestrator calls a specialist tool, receives the result, and then provides its own response incorporating that information. The user never directly interacts with the specialist agent.
In the handoff pattern, the conversation flow changes completely. The user starts by talking to one agent, but that agent recognizes that another agent should take over. The first agent transfers not just the task, but the entire conversation context to the specialist. From that point forward, the specialist agent is directly responding to the user, and the original agent is no longer involved. This pattern is particularly powerful when you have agents with very different capabilities or when the nature of a request clearly falls into one agent's domain of expertise.
To implement handoffs, we need to extend our existing Agent class with the ability to transfer control to other agents. This requires adding a new parameter to track available handoff targets and creating a special handoff tool that agents can use to transfer control.
The handoffs parameter accepts an array of other Agent instances to which this agent can transfer control. We store this as an array rather than an object because agents are identified by their name property, and we want to maintain the flexibility to search through available agents dynamically.
The parameter controls how much computational effort the model invests in reasoning through problems. Setting it to provides faster responses for straightforward tasks, while higher values like or enable deeper analysis for complex problems. For handoff decisions and basic task routing, low reasoning effort is typically sufficient.
Next, we need to create a tool schema that allows the agent to request handoffs. This schema will be automatically added to the agent's available tools when handoff targets are provided.
The handoff schema follows OpenAI's function calling format with a type of "function", a descriptive name, and a parameters object defining the expected inputs. The schema includes two required parameters: the name of the target agent and a reason for the handoff. The reason parameter serves both as documentation for debugging and as a way to help the agent think through whether a handoff is truly necessary.
Notice how we dynamically include the list of available agents in the description using JSON.stringify(), helping the model understand which handoff options are available. The additionalProperties: false constraint ensures that only the specified parameters can be passed, preventing unexpected inputs. Now we need to make this handoff schema available to the agent alongside its other tools.
To make handoffs work seamlessly, we need to build a complete list of available tools that includes both regular tool schemas and the handoff schema when appropriate. Unlike some agent frameworks that use a separate method for building request arguments, our implementation constructs the tool list directly within the run method before each API call.
This approach builds the allTools array fresh for each turn by first spreading any regular tool schemas into it, then pushing the handoff schema if handoff targets are configured. The array is passed directly to responses.create() as the tools parameter. This inline construction ensures that the handoff tool is automatically available to any agent that has handoff targets configured, without requiring separate schema management methods.
With the handoff tool now available to agents, we need to implement the logic that actually performs the control transfer when this tool is called.
Now, let's build the callHandoff method. This is the core logic that performs the actual transfer of control from our current agent to another. We need this method to parse the model's request, locate the target agent, and safely execute the handoff.
Here is how we are structuring this logic:
- Extracting Parameters: Since the model sends
functionCall.argumentsas a JSON string, we start by usingJSON.parse()to retrieve theagentNameand thereason. Logging the reason is a great practice here—it helps us debug exactly why our agent decided to pass the baton. - Locating the Target: We use
Array.find()to search through ourhandoffsarray for an agent whose name matches the requested input. If the model hallucinated a name or requested an unavailable agent, we throw an error immediately to enter our catch block.
The main execution loop in the run method needs to detect handoff function calls and handle them differently from regular tools. When a handoff succeeds, it should immediately return the target agent's response rather than continuing the current agent's execution.
The key difference from the previous tool delegation pattern is how we handle function calls once detected. We continue to filter response.output using a type guard to identify function calls, but now we check if to distinguish handoff requests from regular tool calls.
Let's create a complete example that demonstrates how agents make intelligent handoff decisions. We'll set up a general assistant that can hand off mathematical problems to a specialized calculator assistant.
Notice how we create the calculator assistant first without any handoffs, then create the general assistant with the calculator in its handoffs array. This creates a clear hierarchy where the general assistant can transfer control to the specialist, but not vice versa.
Now let's test the system with different types of questions to see how it makes handoff decisions.
Let's test the system with a general knowledge question to see how the agent decides whether to handle the task itself or perform a handoff.
When we run this test, the general assistant recognizes that this is a straightforward factual question that doesn't require mathematical expertise:
The agent handled this question directly without any handoffs or tool calls, demonstrating that it can distinguish between tasks it should handle itself and those requiring specialist expertise. Now let's test with a mathematical problem that should trigger a handoff to see the complete control transfer process in action.
Now let's test with a mathematical problem that should trigger a handoff to demonstrate the complete control transfer process.
This test demonstrates the complete handoff process in action:
The execution trace shows the complete handoff process: the general assistant recognized that this was a mathematical problem requiring specialist expertise, initiated a handoff to the calculator assistant with a clear reason, and then the calculator assistant took complete control of the conversation. The calculator assistant used its mathematical tools to solve the equation step by step and provided the final response directly to the user.
Understanding when to apply each pattern is crucial for building effective agent systems.
Use agents as tools when you need an orchestrating agent to maintain control and synthesize multiple specialist inputs into a unified response. This works well for complex tasks requiring coordination across different domains, like planning a trip that involves flights, hotels, and restaurants.
Use handoffs when a specialist is clearly better equipped to handle the entire conversation from a certain point forward. This is ideal when the task falls entirely within one domain of expertise and the specialist can provide more value through direct interaction than filtered through an orchestrator.
The key question: Does the task require orchestration and synthesis, or does it need deep specialization with direct user interaction? Choose accordingly.
When implementing handoffs, success depends heavily on designing clear decision boundaries and robust error handling. The most effective handoff systems define explicit criteria in agent prompts, helping agents make confident transfer decisions rather than hesitating between options. For example, your general assistant should know precisely when mathematical problems warrant a calculator handoff versus when they can provide basic arithmetic directly.
Key practices for reliable handoffs include:
- Define clear handoff criteria in agent prompts so agents know exactly when to transfer control
- Implement robust error handling for failed handoffs with graceful fallbacks
- Use descriptive handoff reasons for debugging and system transparency
- Design handoff chains with clear direction to avoid circular transfers
The biggest pitfall to avoid is creating circular handoffs where agents pass control back and forth indefinitely. Design your handoff chains with clear directionality and avoid giving agents too many transfer options, which can lead to decision paralysis. Remember that handoffs should feel like natural conversation flows, similar to being transferred to the right department in a well-organized company rather than bouncing between confused representatives.
You've now mastered the handoff pattern, a powerful approach for building agent systems where specialists can take complete control of conversations when their expertise is needed. This pattern differs fundamentally from agent-as-tool delegation because it transfers not just the task, but the entire conversation ownership to the most appropriate agent.
In your upcoming practice exercises, you'll build multi-agent systems with complex handoff chains, where agents can intelligently route conversations through multiple specialists based on the evolving needs of each interaction. This foundation will enable you to create sophisticated agent ecosystems that can handle diverse, complex tasks while maintaining clear specialization and efficient resource utilization.
