of abstraction constructed on prime of basically easy concepts, some agent framework devs appear to imagine complexity is a advantage.
I are inclined to associate with Einstein’s maxim, “All the pieces needs to be made so simple as doable, however not less complicated”. So, let me present you a framework that’s simple to make use of and simple to grasp.
OpenAI takes a refreshingly totally different strategy to different framework builders : they don’t attempt to be intelligent, they attempt to be clear.
On this article, I’ll present how one can construct multi-agent apps utilizing OpenAI’s open-source SDK.
We’ll see find out how to assemble a easy single-agent app after which go on to discover multi-agent configurations. We’ll cowl tool-calling, linear and hierarchical configurations, handoffs from one agent to a different and utilizing brokers as instruments.
Particularly, we are going to see the next examples:
- A easy name to an agent
- A tool-using agent
- Handoffs from one agent to a different
- Handoffs to a number of brokers
- Utilizing brokers as instruments
- Hierarchical agent orchestration utilizing brokers as instruments
The agent SDK
The agent SDK is predicated on a handful of ideas important to agentic and multi-agent techniques and builds a framework round them — it replaces Swarm, an academic framework developed by OpenAI, the place these ideas had been recognized and applied. The Agent SDK builds upon and expands Swarm whereas sustaining its founding rules of being light-weight and easy.
Easy it could be, however you possibly can assemble subtle agent-based techniques with this framework the place brokers use instruments (which will be different brokers), hand off to different brokers, and will be orchestrated in any variety of intelligent methods.
Set up is by way of pip, or your most well-liked package deal administration instrument, and the package deal is known as openai-agents
. I favour UV, so to start out a brand new venture, I might do one thing like the next.
uv init agentTest
cd agentTest
uv add openai-agents
A easy name to an agent
A easy agent name is proven within the diagram beneath.
It is a knowledge circulate diagram that reveals the operating agent as a course of with knowledge flowing out and in. The circulate that begins the method is the person immediate; the agent makes a number of calls to the LLM and receives responses. When it has accomplished its process, it outputs the agent response.
Beneath we see the code for a fundamental program that makes use of the SDK to implement this circulate. It instantiates an agent, offers it a reputation and a few directions; it then runs it and prints the outcome. It’s much like the primary instance from OpenAI’s documentation, however right here we are going to create a Streamlit app.
First, we import the libraries.
import streamlit as st
import asyncio from brokers
import Agent, Runner
We’d like the Streamlit package deal, after all, and asyncio
as a result of we are going to use its performance to attend for the agent to finish earlier than continuing. Subsequent, we import the minimal from the brokers package deal, Agent
(to create an agent) and Runner
(to run the agent).
Beneath, we outline the code to create and run the agent.
agent = Agent(identify="Assistant", directions="You're a useful assistant")
async def run_agent(input_string):
outcome = await Runner.run(agent, input_string)
return outcome.final_output
This code makes use of the default mannequin from OpenAI (and assumes you’ve gotten a legitimate API key set as an setting variable – and you’ll, after all, be charged , however for our functions right here, it received’t be a lot. I’ve solely spent a couple of tens of cents on this).
First, we instantiate an agent referred to as “Assistant” with some easy directions, then we outline an asynchronous perform that can run it with a string (the question) offered by the person.
The run
perform is asynchronous; we have to look ahead to the LLM to finish earlier than we proceed, and so we are going to run the perform utilizing asyncio
.
We outline the person interface with Streamlit features.
st.title("Easy Agent SDK Question")
user_input = st.text_input("Enter a question and press 'Ship':")
st.write("Response:")
response_container = st.container(peak=300, border=True)
if st.button("Ship"):
response = asyncio.run(run_agent(user_input))
with response_container:
st.markdown(response)
That is principally self-explanatory. The person is prompted to enter a question and press the ‘Ship’ button. When the button is pressed run_agent
is run by way of a name to asyncio.run
. The result’s displayed in a scrollable container. Beneath is a screenshot of a pattern run.

Your outcome might differ (LLMs are famend for not giving the identical reply twice).
To outline an agent, give it a reputation and a few directions. Operating can also be simple; cross within the agent and a question. Operating the agent begins a loop that completes when a closing reply is reached. This instance is easy and doesn’t must run via the loop greater than as soon as, however an agent that calls instruments may must undergo a number of iterations earlier than a solution is finalised.
The result’s simply displayed. As we will see, it’s the final_output
attribute of the worth that’s returned from the Runner
.
This program makes use of default values for a number of parameters that might be set manually, such because the mannequin identify and the temperature setting for the LLM. The Agent SDK additionally makes use of the Responses API by default. That’s an OpenAI-only API (thus far, a minimum of), so if it’s worthwhile to use the SDK with one other LLM, it’s important to swap to the extra broadly supported Chat Completions API.
from brokers import set_default_openai_api
set_default_openai_api("chat_completions")
Initially, and for simplicity, we’ll use the default Response API.
A tool-using agent
Brokers can use instruments, and the agent, along with the LLM, decides which instruments, if any, it wants to make use of.
Here’s a knowledge circulate diagram that reveals a tool-using agent.

It’s much like the easy agent, however we will see a further course of, the instrument, that the agent utilises. When the agent makes a name to the LLM, the response will point out whether or not or not a instrument must be used. If it does, then the agent will make that decision and submit the outcome again to the LLM. Once more, the response from the LLM will point out whether or not one other instrument name is critical. The agent will proceed this loop till the LLM now not requires the enter from a instrument. At this level, the agent can reply to the person.
Beneath is the code for a single agent utilizing a single instrument.
This system consists of 4 components:
- The imports from the Brokers library and
wikipedia
(which might be used as a instrument). - The definition of a instrument — that is merely a perform with the
@function_tool
decorator. - The definition of the agent that makes use of the instrument.
- Operating the agent and printing the lead to a Streamlit app, as earlier than.
import streamlit as st
import asyncio
from brokers import Agent, Runner, function_tool
import wikipedia
@function_tool
def wikipedia_lookup(q: str) -> str:
"""Lookup a question in Wikipedia and return the outcome"""
return wikipedia.web page(q).abstract
research_agent = Agent(
identify="Analysis agent",
directions="""You analysis subjects utilizing Wikipedia and report on
the outcomes. """,
mannequin="o4-mini",
instruments=[wikipedia_lookup],
)
async def run_agent(input_string):
outcome = await Runner.run(research_agent, input_string)
return outcome.final_output
# Streamlit UI
st.title("Easy Device-using Agent")
st.write("This agent makes use of Wikipedia to search for info.")
user_input = st.text_input("Enter a question and press 'Ship':")
st.write("Response:")
response_container = st.container(peak=300, border=True)
if st.button("Ship"):
response = asyncio.run(run_agent(user_input))
with response_container:
st.markdown(response)
The instrument appears up a Wikipedia web page and returns a abstract by way of a typical name to a library perform. Be aware that we’ve used sort hints and a docstring to explain the perform so the agent can work out find out how to use it.
Subsequent is the definition of the agent, and right here we see that there are extra parameters than earlier: we specify the mannequin that we wish to use and a listing of instruments (there’s just one on this checklist).
Operating and printing the result’s as earlier than, and it dutifully returns a solution (the peak of the Eiffel Tower).

That could be a easy check of the tool-using agent, which solely requires a single lookup. A extra advanced question might use a instrument greater than as soon as to gather the data.
For instance, I requested, “Discover the identify of the well-known tower in Paris, discover its peak after which discover the date of delivery of its creator“. This required two instrument calls, one to get details about the Eiffel Tower and the second to search out when Gustav Eiffel was born.
This course of shouldn’t be mirrored within the closing output, however we will see the levels that the agent went via by viewing the uncooked messages within the agent’s outcome. I printed outcome.raw_messages
for the question above, and the result’s proven beneath.
[
0:"ModelResponse(output=[ResponseReasoningItem(id='rs_6849968a438081a2b2fda44aa5bc775e073e3026529570c1', summary=[], sort='reasoning', standing=None),
ResponseFunctionToolCall(arguments='{"q":"Eiffel Tower"}', call_id='call_w1iL6fHcVqbPFE1kAuCGPFok', identify='wikipedia_lookup', sort='function_call', id='fc_6849968c0c4481a29a1b6c0ad80fba54073e3026529570c1', standing='accomplished')], utilization=Utilization(requests=1, input_tokens=111, output_tokens=214, total_tokens=325),
response_id='resp_68499689c60881a2af6411d137c13d82073e3026529570c1')"
1:"ModelResponse(output=[ResponseReasoningItem(id='rs_6849968e00ec81a280bf53dcd30842b1073e3026529570c1', summary=[], sort='reasoning', standing=None),
ResponseFunctionToolCall(arguments='{"q":"Gustave Eiffel"}', call_id='call_DfYTuEjjBMulsRNeCZaqvV8w', identify='wikipedia_lookup', sort='function_call', id='fc_6849968e74ac81a298dc17d8be4012a7073e3026529570c1', standing='accomplished')], utilization=Utilization(requests=1, input_tokens=940, output_tokens=23, total_tokens=963),
response_id='resp_6849968d7c3081a2acd7b837cfee5672073e3026529570c1')"
2:"ModelResponse(output=[ResponseReasoningItem(id='rs_68499690e33c81a2b0bda68a99380840073e3026529570c1', summary=[], sort='reasoning', standing=None),
ResponseOutputMessage(id='msg_6849969221a081a28ede4c52ea34aa54073e3026529570c1', content material=[ResponseOutputText(annotations=[], textual content='The well-known tower in Paris is the Eiffel Tower. n• Top: 330 metres (1,083 ft) tall n• Creator: Alexandre Gustave Eiffel, born 15 December 1832', sort='output_text')], function='assistant', standing='accomplished', sort='message')], utilization=Utilization(requests=1, input_tokens=1190, output_tokens=178, total_tokens=1368),
response_id='resp_6849968ff15481a292939a6eed683216073e3026529570c1')"
]
You possibly can see that there are three responses: the primary two are the results of the 2 instrument calls, and the final is the ultimate output, which is generated from the data derived from the instrument calls.
We’ll see instruments once more shortly after we use brokers as instruments, however now we’re going to take into account how we will use a number of brokers that cooperate.
A number of brokers
Many agent functions solely require a single agent, and these are already a protracted step past easy chat completions that you just discover within the LLM chat interfaces, similar to ChatGPT. Agents run in loops and might use instruments, making even a single agent fairly highly effective. Nonetheless, a number of brokers working collectively can obtain much more advanced behaviours.
In step with its easy philosophy, OpenAI doesn’t try to include agent orchestration abstractions like another frameworks. However regardless of its easy design, it helps the development of each easy and sophisticated configurations.
First, we’ll take a look at handoffs the place one agent passes management to a different. After that, we’ll see how brokers will be mixed hierarchically.
Handoffs
When an agent decides that it has accomplished its process and passes info to a different agent for additional work, that’s termed a handoff.
There are two basic methods of attaining a handoff: with an agentic handoff, your complete message historical past is handed from one agent to a different. It’s a bit like whenever you name the financial institution however the particular person you first converse to doesn’t know your specific circumstances, and so passes you on to somebody who does. The distinction is that, within the case of the AI agent, the brand new agent has a report of all that was mentioned to the earlier one.
The second methodology is a programmatic handoff. That is the place solely the required info offered by one agent is handed to a different (by way of typical programming strategies).
Let’s take a look at programmatic handoffs first.
Programmatic handoffs
Generally the brand new agent doesn’t must know your complete historical past of a transaction; maybe solely the ultimate result’s required. On this case, as a substitute of a full handoff, you possibly can prepare a programmatic handoff the place solely the related knowledge is handed to the second agent.

The diagram reveals a generic programmatic handoff between two brokers.
Beneath is an instance of this performance, the place one agent finds details about a subject and one other takes that info and writes an article that’s appropriate for teenagers.
To maintain issues easy, we received’t use our Wikipedia instrument on this instance; as a substitute, we depend on the LLM’s information.
import streamlit as st
import asyncio
from brokers import Agent, Runner
writer_agent = Agent(
identify="Author agent",
directions=f"""Re-write the article in order that it's appropriate for teenagers
aged round 8. Be enthusiastic in regards to the subject -
every part is an journey!""",
mannequin="o4-mini",
)
researcher_agent = Agent(
identify="Analysis agent",
directions=f"""You analysis subjects and report on the outcomes.""",
mannequin="o4-mini",
)
async def run_agent(input_string):
outcome = await Runner.run(researcher_agent, input_string)
result2 = await Runner.run(writer_agent, outcome.final_output)
return result2
# Streamlit UI
st.title("Author Agent")
st.write("Write stuff for teenagers.")
user_input = st.text_input("Enter a question and press 'Ship':")
st.write("Response:")
response_container = st.container(peak=300, border=True)
if st.button("Ship"):
response = asyncio.run(run_agent(user_input))
with response_container:
st.markdown(response.final_output)
st.write(response)
st.json(response.raw_responses)
Within the code above, we outline two brokers: one researches a subject and the opposite produces textual content appropriate for teenagers.
This system doesn’t depend on any particular SDK features; it merely runs one agent, will get the output in outcome
and makes use of it because the enter for the following agent (output in result2
). It’s identical to utilizing the output of 1 perform because the enter for the following in typical programming. Certainly, that’s exactly what it’s.
Agentic handoffs
Nonetheless, typically an agent must know the historical past of what occurred beforehand. That’s the place the OpenAI Brokers Handoffs are available in.
Beneath is the info circulate diagram that represents the Agentic Handoff. You will notice that it is extremely much like the Programmatic Handoff; the distinction is the info being transferred to the second agent, and in addition, there’s a doable output from the primary agent when the handoff shouldn’t be required.

The code can also be much like the earlier instance. I’ve tweaked the directions barely, however the primary distinction is the handoffs
checklist in researcher_agent
. This isn’t dissimilar to the way in which we declare instruments.
The Analysis Agent has been allowed handy off to the Child’s Author Agent when it has accomplished its work. The impact of that is that the Child’s Author Agent not solely takes over management of the processing but additionally has information of what the Analysis Agent did, in addition to the unique immediate.
Nonetheless, there’s one other main distinction. It’s as much as the agent to find out whether or not the handoff takes place or not. Within the instance run beneath, I’ve instructed the agent to put in writing one thing appropriate for teenagers, and so it arms off to the Youngsters’ Author Agent. If I had not instructed it to try this, it could have merely returned the unique textual content.
import streamlit as st
import asyncio
from brokers import Agent, Runner
kids_writer_agent = Agent(
identify="Youngsters Author Agent",
directions=f"""Re-write the article in order that it's appropriate for teenagers aged round 8.
Be enthusiastic in regards to the subject - every part is an journey!""",
mannequin="o4-mini",
)
researcher_agent = Agent(
identify="Analysis agent",
directions=f"""Reply the question and report the outcomes.""",
mannequin="o4-mini",
handoffs = [kids_writer_agent]
)
async def run_agent(input_string):
outcome = await Runner.run(researcher_agent, input_string)
return outcome
# Streamlit UI
st.title("Author Agent2")
st.write("Write stuff for teenagers.")
user_input = st.text_input("Enter a question and press 'Ship':")
st.write("Response:")
response_container = st.container(peak=300, border=True)
if st.button("Ship"):
response = asyncio.run(run_agent(user_input))
with response_container:
st.markdown(response.final_output)
st.write(response)
st.json(response.raw_responses)
It’s not within the screenshot, however I’ve added code to output the response
and the raw_responses
as a way to see the handoff in operation in the event you run the code your self.
Beneath is a screenshot of this agent.

An agent can have a listing of handoffs at its disposal, and it’ll intelligently select the right agent (or none) handy off to. You possibly can see how this could be helpful in a customer support scenario the place a tough buyer question could be escalated via a collection of extra skilled brokers, every of whom wants to pay attention to the question historical past.
We’ll now take a look at how we will use handoffs that contain a number of brokers.
Handoffs to a number of brokers
We are going to now see a brand new model of the earlier program the place the Analysis Agent chooses handy off to totally different brokers relying on the reader’s age.
The agent’s job is to provide textual content for 3 audiences: adults, youngsters and youngsters. The Analysis Agent will collect info after which hand it off to certainly one of three different brokers. Right here is the info circulate (word that I’ve excluded the hyperlinks to an LLM for readability – every agent communicates with an LLM, however we will take into account that as an inside perform of the agent).

And right here is the code.
import streamlit as st
import asyncio
from brokers import Agent, Runner, handoff
adult_writer_agent = Agent(
identify="Grownup Author Agent",
directions=f"""Write the article based mostly on the data provided that it's appropriate for adults fascinated with tradition.
""",
mannequin="o4-mini",
)
teen_writer_agent = Agent(
identify="Teen Author Agent",
directions=f"""Write the article based mostly on the data provided that it's appropriate for youngsters who wish to have a cool time.
""",
mannequin="o4-mini",
)
kid_writer_agent = Agent(
identify="Child Author Agent",
directions=f"""Write the article based mostly on the data provided that it's appropriate for teenagers of round 8 years outdated.
Be enthusiastic!
""",
mannequin="o4-mini",
)
researcher_agent = Agent(
identify="Analysis agent",
directions=f"""Discover info on the subject(s) given.""",
mannequin="o4-mini",
handoffs = [kid_writer_agent, teen_writer_agent, adult_writer_agent]
)
async def run_agent(input_string):
outcome = await Runner.run(researcher_agent, input_string)
return outcome
# Streamlit UI
st.title("Author Agent3")
st.write("Write stuff for adults, youngsters or children.")
user_input = st.text_input("Enter a question and press 'Ship':")
st.write("Response:")
response_container = st.container(peak=300, border=True)
if st.button("Ship"):
response = asyncio.run(run_agent(user_input))
with response_container:
st.markdown(response.final_output)
st.write(response)
st.json(response.raw_responses)
This system’s construction is comparable, however now we now have a set of brokers handy off to and a listing of them within the Analysis Agent. The directions within the numerous brokers are self-explanatory, and this system will accurately reply to a immediate similar to “Write an essay about Paris, France for teenagers” or “…for youngsters” or “…for adults”. The Analysis Agent will accurately select the suitable Author Agent for the duty.
The screenshot beneath reveals an instance of writing for youngsters.

The prompts offered on this instance are easy. Extra subtle prompts would probably yield a greater and extra constant outcome, however the purpose right here is to indicate the methods relatively than to construct a intelligent app.
That’s one sort of collaboration; one other is to make use of different brokers as instruments. This isn’t too dissimilar to the programmatic handoff we noticed earlier.
Brokers as instruments
Operating an agent is asking a perform in the identical means as calling a instrument. So why not use brokers as clever instruments?
As a substitute of giving management over to a brand new agent, we use it as a perform that we cross info to and get info again from.
Beneath is a knowledge circulate diagram that illustrates the concept. Not like a handoff, the primary agent doesn’t cross general management to a different agent; as a substitute, it intelligently chooses to name an agent as if it had been a instrument. The referred to as agent does its job after which passes management again to the calling agent. Once more, the info flows to an LLM have been omitted for readability.

Beneath is a screenshot of a modified model of the earlier program. We modified the character of the app a bit. The principle agent is now a journey agent; it expects the person to provide it a vacation spot and the age group for which it ought to write. The UI is modified in order that the age group is chosen by way of a radio button. The textual content enter area needs to be a vacation spot.

Quite a few modifications have been made to the logic of the app. The UI modifications the way in which the data is enter, and that is mirrored in the way in which that the immediate is constructed – we use an f-string to include the 2 items of knowledge into the immediate.
Moreover, we now have an additional agent that codecs the textual content. The opposite brokers are comparable (however word that the prompts have been refined), and we additionally use a structured output to make sure that the textual content that we output is exactly what we count on.
Essentially, although, we see that the author brokers and the formatting agent are specified as instruments within the researcher agent.
import streamlit as st
import asyncio
from brokers import Agent, Runner, function_tool
from pydantic import BaseModel
class PRArticle(BaseModel):
article_text: str
commentary: str
adult_writer_agent = Agent(
identify="Grownup Author Agent",
directions="""Write the article based mostly on the data provided that it's appropriate for adults fascinated with tradition.
Be mature.""",
mannequin="gpt-4o",
)
teen_writer_agent = Agent(
identify="Teen Author Agent",
directions="""Write the article based mostly on the data provided that it's appropriate for youngsters who wish to have a superb time.
Be cool!""",
mannequin="gpt-4o",
)
kid_writer_agent = Agent(
identify="Child Author Agent",
directions="""Write the article based mostly on the data provided that it's appropriate for teenagers of round 8 years outdated.
Be enthusiastic!""",
mannequin="gpt-4o",
)
format_agent = Agent(
identify="Format Agent",
directions=f"""Edit the article so as to add a title and subtitles and make sure the textual content is formatted as Markdown. Return solely the textual content of article.""",
mannequin="gpt-4o",
)
researcher_agent = Agent(
identify="Analysis agent",
directions="""You're a Journey Agent who will discover helpful info to your clients of all ages.
Discover info on the vacation spot(s) given.
When you've gotten a outcome ship it to the suitable author agent to provide a brief PR textual content.
When you've gotten the outcome ship it to the Format agent for closing processing.
""",
mannequin="gpt-4o",
instruments = [kid_writer_agent.as_tool(
tool_name="kids_article_writer",
tool_description="Write an essay for kids",),
teen_writer_agent.as_tool(
tool_name="teen_article_writer",
tool_description="Write an essay for teens",),
adult_writer_agent.as_tool(
tool_name="adult_article_writer",
tool_description="Write an essay for adults",),
format_agent.as_tool(
tool_name="format_article",
tool_description="Add titles and subtitles and format as Markdown",
),],
output_type = PRArticle
)
async def run_agent(input_string):
outcome = await Runner.run(researcher_agent, input_string)
return outcome
# Streamlit UI
st.title("Journey Agent")
st.write("The journey agent will write about locations for various audiences.")
vacation spot = st.text_input("Enter a vacation spot, choose the age group and press 'Ship':")
age_group = st.radio(
"What age group is the reader?",
["Adult", "Teenager", "Child"],
horizontal=True,
)
st.write("Response:")
response_container = st.container(peak=500, border=True)
if st.button("Ship"):
response = asyncio.run(run_agent(f"The vacation spot is {vacation spot} and reader the age group is {age_group}"))
with response_container:
st.markdown(response.final_output.article_text)
st.write(response)
st.json(response.raw_responses)
The instruments checklist is a bit totally different to the one we noticed earlier:
- The instrument identify is the agent identify plus
.agent_as_tool()
, a way that makes the agent suitable with different instruments . - The instrument wants a few parameters — a reputation and an outline.
One different addition, which may be very helpful, is the usage of structured outputs, as talked about above. This separates the textual content that we would like from another commentary that the LLM may wish to insert. Should you run the code, you possibly can see within the raw_responses
the extra info that the LLM generates.
Utilizing structured outputs helps to provide constant outcomes and solves an issue that may be a specific bugbear of mine.
I’ve requested the output to be run via a formatter agent that can construction the outcome as Markdown. It is dependent upon the LLM, it is dependent upon the immediate, and who is aware of, perhaps it is dependent upon the time of day or the climate, however every time I feel I’ve acquired it proper, an LLM will immediately insert Markdown fencing. So as a substitute of a clear:
## It is a header
That is some textual content
I as a substitute get:
Right here is your textual content formatted as Markdown:
''' Markdown
# It is a header
That is some textual content
'''
Infuriating!
Anyway, the reply appears to be to make use of structured outputs. Should you requested it to format the response because the textual content of what you need, plus a second area referred to as ‘commentary’ or some such factor, it seems to do the proper factor. Any extraneous stuff the LLM decides to spout goes within the second area, and the unadulterated Markdown goes within the textual content area.
OK, Shakespeare, it isn’t: adjusting the directions in order that they’re extra detailed may give higher outcomes (the present prompts are quite simple). However it works nicely sufficient as an instance the strategy.
Conclusion
That’s scratched the floor of OpenAI’s Brokers SDK. Thanks for studying, and I hope you discovered it helpful. We’ve got seen find out how to create brokers and find out how to mix them in several methods, and we took a really fast take a look at structured outputs.
The examples are, after all, easy, however I hope they illustrate the simple means that brokers will be orchestrated merely with out resorting to advanced abstractions and unwieldy frameworks.
The code right here makes use of the Response API as a result of that’s the default. Nonetheless, it ought to run the identical means with the Completions API, as nicely. Which suggests that you’re not restricted to ChatGPT and, with a little bit of jiggery-pokery, this SDK can be utilized with any LLM that helps the OpenAI Completions API.
There’s lots extra to search out out in OpenAI’s documentation.
- Pictures are by the writer except in any other case acknowledged.