Multi-Agent Systems with the OpenAI Agents SDK, Explained


An LLM agent can do a lot by itself. But as an application grows, the agent may need to handle different kinds of work.

At that point, it can be useful to introduce a multi-agent setup and split the responsibilities.

In this post, we’ll discuss how to build practical multi-agent systems using the OpenAI Agents SDK. We’ll specifically look at two patterns that are the building blocks for the more complex multi-agent architecture: agent-as-a-tool and handoff.

We’ll illustrate both patterns with two case studies.

···

1. It’s All About Delegation

A multi-agent system requires splitting responsibilities across multiple agents.

Once those responsibilities are split, the agents would need a way to work together. This is where delegation comes in.

In practice, there are two widely used patterns for delegation, i.e., agent as a tool, and handoff.

1.1 Agent as a Tool

Suppose one agent receives a request and needs help from a specialist. The main agent can then call that specialist agent.

The specialist agent works on a well-bounded subtask and returns its result to the calling agent once it finishes. The calling agent remains active, and it’s its responsibility to produce the final answer by combining the specialist’s response with possibly other information.

In this scenario, this specialist agent is effectively a tool from the perspective of the calling agent. And we can easily scale to several specialist agents, where the main agent decides which specialists to consult given the task.

This gives us the so-called “agent-as-a-tool” pattern. In the OpenAI Agents SDK, we create this relationship with specialist_agent.as_tool(...). We’ll see the complete setup in the first case study.

1.2 Handoff

Another way to delegate is to transfer control to a specialist agent.

In this scenario, the initial agent receives the request and decides which specialist to call. What’s different here is that the specialist agent would actually take over and become the active agent. It then becomes the specialist agent’s responsibility to produce the final response.

Here, the initial agent primarily serves as a router, routing the request to the appropriate specialist.

In the OpenAI Agents SDK, we can configure the initial agent with a list of possible handoff targets through handoffs=[...].

These two mechanisms form the building blocks, and based on them, we can build a variety of multi-agent systems.

We’ll return to these larger compositions at the end of the post. But first, let’s see some code examples that implement the above two foundational patterns.

···

2. Putting Delegation into Practice

For the case study, let’s build two small assistants around electric cars.

Read Also:  In conversation with AI: building better language models

In the first case study, we consider the following situation: a family needs to decide whether buying an electric car fits their situation. They live in Zurich, Switzerland, and they drive around 15,000 kilometers per year. They have a budget of 45,000 Swiss Francs, but cannot install a private charger. To make a useful recommendation, our multi-agent system needs to look at suitable models, charging options, and ownership costs.

In the second case study, we help someone who already drives an electric car and relies on public charging in Zurich. The user wants to understand the available networks, subscriptions, and prices.

For the above tasks, we’ll implement two multi-agent assistants with the OpenAI Agents SDK.

We start with the purchase adviser.

2.1 Building an EV Purchase Adviser

For the purchase question, it naturally breaks into several independent lines of research: we need to find suitable vehicles, assess charging practicality, and understand ownership costs. These findings are useful only when combined into one recommendation.

This makes the agent-as-a-tool pattern a natural fit.

We can have specialist agents investigating each of those aspects, while we keep a purchase adviser agent in control and task it to synthesize their findings.

We first configure the shared client and model:

import osfrom agents import ModelSettings, OpenAIResponsesModelfrom dotenv import load_dotenvfrom openai import AsyncAzureOpenAIload_dotenv()client = AsyncAzureOpenAI(    api_key=os.environ["OPENAI_API_KEY"],    azure_endpoint=os.environ["OPENAI_API_BASE"],    api_version=os.environ["OPENAI_API_VERSION"],)model = OpenAIResponsesModel(    model="gpt-5.4",    openai_client=client,)model_settings = ModelSettings(    reasoning={"effort": "medium"},)

As we mentioned earlier, we use three specialists:

  • a vehicle researcher;

  • a charging researcher;

  • an ownership-cost researcher.

We give each specialist the web search tool:

from agents import Agent, WebSearchToolvehicle_research_agent = Agent(    name="Vehicle researcher",    instructions=(        "Research current electric vehicles that fit the user's "        "needs and constraints."    ),    model=model,    model_settings=model_settings,    tools=[WebSearchTool(search_context_size="medium")],)charging_research_agent = Agent(    name="Charging researcher",    instructions=(        "Assess electric-vehicle charging feasibility using current "        "infrastructure and pricing information."    ),    model=model,    model_settings=model_settings,    tools=[WebSearchTool(search_context_size="medium")],)cost_research_agent = Agent(    name="Ownership-cost researcher",    instructions=(        "Research current purchase and ownership costs relevant to "        "an electric-vehicle decision."    ),    model=model,    model_settings=model_settings,    tools=[WebSearchTool(search_context_size="medium")],)

Now comes the key step: we expose the above-defined specialists as tools for the purchase adviser:

vehicle_research_tool = vehicle_research_agent.as_tool(    tool_name="research_ev_models",    tool_description="Research current EV models that fit the user's needs.",    max_turns=5,)charging_research_tool = charging_research_agent.as_tool(    tool_name="research_charging",    tool_description="Research charging access and current prices.",    max_turns=5,)cost_research_tool = cost_research_agent.as_tool(    tool_name="research_ownership_costs",    tool_description="Research purchase and ownership costs.",    max_turns=5,)

Note that the tool_name and tool_description tell the purchase adviser what each specialist can do.

Now we define the coordinating agent:

ev_purchase_adviser = Agent(    name="EV purchase adviser",    instructions=(        "Help the user decide whether an electric vehicle suits their "        "needs. Specialist tools are available for vehicle options, "        "charging, and ownership costs."    ),    model=model,    model_settings=model_settings,    tools=[        vehicle_research_tool,        charging_research_tool,        cost_research_tool,    ],)

Everything is set up. We test it with an actual request:

purchase_request = """We are a family of three living in Zurich. We drive about 15,000 kmper year, mostly for commuting and weekend trips, plus several longdrives across Europe. We rent an apartment and cannot install a privatecharger. Our budget is CHF 45,000, and we need room for a child seatand luggage.Would an electric vehicle make sense for us? If so, recommend up tothree current models and explain the trade-offs."""

We run the adviser:

from agents import Runnerpurchase_result = await Runner.run(    ev_purchase_adviser,    purchase_request,    max_turns=10,)print(purchase_result.final_output)

In my run, the adviser agent concluded that an electric car could fit the family’s driving needs, but that the lack of private charging would be the main constraint. It then compared several current models and explained the relevant trade-offs.

Read Also:  The Best Web Scraping APIs for AI Models in 2026

We can also inspect how the answer was produced:

agent_tool_calls = [    getattr(item.raw_item, "name", None)    for item in purchase_result.new_items    if item.type == "tool_call_item"]print("Agent tools called:", agent_tool_calls)print("Final agent:", purchase_result.last_agent.name)

The trace shows that the adviser called the vehicle, charging, and ownership-cost researchers, then remained the final agent.

So, the pattern of the “agent-as-a-tool” is clear: the specialists contributed their findings, while the manager agent retained responsibility for the answer.

2.2 Building an EV Support Agent

The second task considers a different scenario: an existing EV owner wants practical help with public charging.

Since this request belongs primarily to one specialist, a handoff is a natural fit.

To demonstrate this point, here, we define three possible specialists:

charging_support_agent = Agent(    name="Charging specialist",    handoff_description=(        "Handles questions about home and public EV charging."    ),    instructions=(        "Help electric-vehicle owners solve charging questions using "        "current information."    ),    model=model,    model_settings=model_settings,    tools=[WebSearchTool(search_context_size="medium")],)route_planning_agent = Agent(    name="Route planning specialist",    handoff_description=(        "Handles questions about long-distance EV route planning."    ),    instructions=(        "Help electric-vehicle owners plan journeys using current "        "route and charging information."    ),    model=model,    model_settings=model_settings,    tools=[WebSearchTool(search_context_size="medium")],)vehicle_care_agent = Agent(    name="Vehicle care specialist",    handoff_description=(        "Handles questions about EV maintenance and daily operation."    ),    instructions=(        "Help electric-vehicle owners with maintenance and operating "        "questions using current information."    ),    model=model,    model_settings=model_settings,    tools=[WebSearchTool(search_context_size="medium")],)

The support agent receives these possible handoff targets:

ev_support_agent = Agent(    name="EV support agent",    instructions=(        "Help users with electric-vehicle ownership questions. "        "Transfer the conversation to the relevant specialist "        "when appropriate."    ),    model=model,    model_settings=model_settings,    handoffs=[        charging_support_agent,        route_planning_agent,        vehicle_care_agent,    ],)

Note that we used an explicit handoffs list to inform the support agent which specialist agents can be delegated to. Here, the support agent does not need to combine several research reports. Its role is to identify which specialist should take responsibility for the request.

We test it with a charging question:

support_request = """I recently bought an electric car and rely entirely on public chargingin Zurich. The different networks, subscriptions, and prices areconfusing. Help me work out a practical charging setup."""

We run the support agent:

support_result = await Runner.run(    ev_support_agent,    support_request,    max_turns=10,)print(support_result.final_output)

In my run, the request was handed to the charging specialist, who researched the available networks and produced the final response.

We can inspect the handoff directly:

handoff_path = [    f"{item.source_agent.name} -> {item.target_agent.name}"    for item in support_result.new_items    if item.type == "handoff_output_item"]print("Handoff:", handoff_path)print("Final agent:", support_result.last_agent.name)

Here is the result:

Handoff:EV support agent -> Charging specialistFinal agent:Charging specialist

···

Read Also:  Getting Started with Neo4j: Installation and Setup Guide

3. From Two Patterns to Larger Systems

Once we have these two delegation mechanisms, we can combine them to build different kinds of multi-agent systems.

In my experience, a useful way to think about the multi-agent system along three dimensions:

  1. Delegation: does the delegated agent return its result, or take over the task? This is essentially what we have covered in this post, and we have two concrete patterns, i.e., agent-as-a-tool or handoff.

  2. Topology: how are the agents connected? Practically, there could be many variants, such as connected through a central coordinator, a chain, a hierarchy, or a peer-to-peer network.

  3. Execution: do agents run sequentially, in parallel, or repeatedly?

With these dimensions in mind, many common multi-agent patterns become easier to understand.

3.1 Fan-Out / Fan-In

A coordinator has several independent subtasks, or the same subtask but wants different agents to work on it from different angles. It can send them to multiple specialists in parallel and combine their results afterwards.

This is commonly called a fan-out / fan-in pattern.

Its delegation style is agent-as-a-tool. Its topology is centralized, and its execution is parallel.

3.2 Pipeline

In a pipeline, different agents handle different stages of a task. Here, the topology is a chain, and execution is sequential. The delegation is usually a handoff type.

3.3 Hierarchical Agents

As a system grows, one specialist may itself need help from more specialized agents. This naturally creates a hierarchical structure:

Figure 1. A hierarchical structure for the multi-agent system. (Image by author)

The topology is now a tree.

A common way to implement this is recursively with agents-as-tools. A manager calls a specialist, and that specialist can in turn call its own workers.

3.4 Evaluator-Optimizer

Some tasks benefit from an iterative process.

For example, one agent can produce an answer while another evaluates it. If the evaluator finds problems, the generator tries again using the feedback.

This is often called an evaluator-optimizer pattern.

The delegation can be implemented as the agent-as-a-tool, since the generator and evaluator provide outputs back to a coordinating workflow. The topology is centralized, and the execution happens in a loop.

3.5 Swarm / Peer-to-Peer

At the other end of the spectrum, we can remove the central coordinator. Instead, the currently active agent can decide which specialist should take over next.

This gives us a peer-to-peer, or swarm-style architecture.

Its delegation mechanism is mainly handoff, its topology is peer-to-peer, and its execution is dynamic.

3.6 Putting It Together

Although we sampled several common multi-agent system patterns above, their underlying agent interaction mechanisms are really built from the same foundation:

  • Agent-as-a-tool, where the caller agent retains control while delegating tasks to specialist agents.

  • Handoff, where the caller agent moves control to another agent.

By arranging these relationships with different topologies and execution strategies, we can build much larger multi-agent systems.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top