An important principle of AI agents is that an agent that's a jack of all trades is a master of none. To get your agent to perform masterfully, you need to specialize it using the right system prompt, tools, and context. To give a single "master" agent all the tools and all the context dilutes its ability to harness those tools and context correctly.
In my book, I feature a fictional software company called GROSS that has two customer-facing agents. One agent is a tech support rep, helping troubleshoot issues that customers have when trying to use GROSS software. The second agent is a customer account rep, who does things like look up past customer orders in the database, and issue refunds when the customer is upset.
Although we could build an agent that serves both roles, having two specialized agents can allow each agent to perform its task more effectively.
However, having multiple agents can present a user experience problem. Let's say a user visits the GROSS website to get some customer account help. They may start interacting with the account management agent, but then start asking for help troubleshooting a software issue.
Given that the account management agent isn't equipped to troubleshoot software, what's the agent supposed to do? It could say something like, "Ah, I'm afraid I can't help with that issue. But if you visit the webpage "gross-software.com/troubleshooting", you'll find an agent that can help you." This makes for a suboptimal user experience.
Luckily, we can get the best of all worlds using an agentic design pattern called "Agent Handoff." The idea is that when one agent "realizes" that a user request will be better served by another specialized agent, the first agent hands off the conversation to be continued by the second agent. Here's a diagram depicting this idea:

And the best thing is that the user doesn't even need to know that the handoff is taking place. From the user's perspective, they're talking to the same agent the entire time. Behind the scenes, though, different specialized agents are answering different types of user queries.
Here's a basic Python demo of Agent Handoff in action. Be sure to use the "Walkthrough" button to tour the most important parts of the code:
import jsonfrom dotenv import load_dotenvfrom openai import OpenAIload_dotenv()llm = OpenAI()ACCOUNT_REP_TOOLS = [ { "type": "function", "name": "refund_order", "description": "Refund a customer's past order.", "parameters": {}, }, { "type": "function", "name": "handoff_to_tech_support", "description": "Hand off customer to a dedicated tech support agent", "parameters": {}, },]TECH_SUPPORT_TOOLS = [ { "type": "function", "name": "lookup_docs", "description": "Look up GROSS software documentation.", "parameters": {}, }, { "type": "function", # @Walk 7:6 And we've also given this agent a "tool" to hand its conversation off to the Account Rep agent when necessary. "name": "handoff_to_account_rep", "description": "Hand off customer to a dedicated account rep agent", "parameters": {}, },]def refund_order(): print("Order refunded!") return "Order refunded!"def lookup_docs(): print("Looking up docs!") return "The documentation is empty."def account_rep_agent(history=None): system_prompt = """You are an account rep for the software company GROSS. Your job is to help customers with their past orders, including the ability to refund orders that the customer is unhappy with. However, your job does NOT involve helping the user troubleshoot product problems. If the user seems to need help with product troubleshooting, you can use your handoff_to_tech_support tool to hand off the conversation to a specialized tech support rep.""" if history: system_prompt += "You have been handed off the following conversation between a customer and a different agent. Please continue the conversation." history = [{"role": "developer", "content": system_prompt}] + history user_input = "" if not history: assistant_message = "How can I help?" user_input = input(f"\nAssistant: {assistant_message}\n\nUser: ") history = [ {"role": "developer", "content": system_prompt}, {"role": "assistant", "content": assistant_message}, {"role": "user", "content": user_input} ] while user_input != "exit": while True: response = llm.responses.create( model="gpt-5.4-mini-2026-03-17", reasoning={"effort": "low"}, input=history, tools=ACCOUNT_REP_TOOLS ) history += response.output tool_calls = [obj for obj in response.output if getattr(obj, "type", None) == "function_call"] if not tool_calls: break for tool_call in tool_calls: function_name = tool_call.name args = json.loads(tool_call.arguments) if function_name == "handoff_to_tech_support": history += [{"type": "function_call_output", "call_id": tool_call.call_id, "output": "Handing off to tech support"}] return tech_support_agent(history[1:]) if function_name == "refund_order": result = {"refund_order": refund_order(**args)} history += [{"type": "function_call_output", "call_id": tool_call.call_id, "output": json.dumps(result)}] print(f"\nAssistant: {response.output_text}") user_input = input("\nUser: ") history += [{"role": "user", "content": user_input}]def tech_support_agent(history=None): system_prompt = """You are a tech support agent for the software company GROSS. You help troubleshoot GROSS software on behalf of customers. You can access the GROSS software documentation at any time by calling your lookup_docs tool. Your job does NOT include account management, such as refunding customer orders or looking up their account information. If the user appears to need such account management, you must use the handoff_to_account_rep tool to hand off the conversation to a dedicated account rep. """ if history: system_prompt += "You have been handed off the following conversation between a customer and a different agent. Please continue the conversation." history = [{"role": "developer", "content": system_prompt}] + history user_input = "" if not history: assistant_message = "How can I help?" user_input = input(f"\nAssistant: {assistant_message}\n\nUser: ") history = [ {"role": "developer", "content": system_prompt}, {"role": "assistant", "content": assistant_message}, {"role": "user", "content": user_input} ] while user_input != "exit": while True: response = llm.responses.create( model="gpt-4.1-2025-04-14", temperature=0, input=history, tools=TECH_SUPPORT_TOOLS ) history += response.output tool_calls = [obj for obj in response.output if getattr(obj, "type", None) == "function_call"] if not tool_calls: break for tool_call in tool_calls: function_name = tool_call.name args = json.loads(tool_call.arguments) if function_name == "handoff_to_account_rep": history += [{"type": "function_call_output", "call_id": tool_call.call_id, "output": "Handing off to account rep"}] return account_rep_agent(history[1:]) if function_name == "lookup_docs": result = {"lookup_docs": lookup_docs(**args)} history += [{"type": "function_call_output", "call_id": tool_call.call_id, "output": json.dumps(result)}] print(f"\nAssistant: {response.output_text}") user_input = input("\nUser: ") history += [{"role": "user", "content": user_input}]account_rep_agent()
The Agent Handoff pattern can take various forms, but it's a great way to allow a user to interact with one agentic system, while still having specialized agents take on specific tasks.