Aurelio logo
Updated on July 10, 2025

Tools with Agents SDK

AI Engineering

Agents SDK provides various approaches for tool-use, including pre-built tools and all features we need to develop custom tools. In this chapter we'll learn everything there is about tools and tool-use.


Setup

We'll first need to install openai-agents like so:

python
!pip install -qU \
"openai-agents==0.1.0"

To follow along you will need your OpenAI API key.

python
import os
import getpass

os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") or \
getpass.getpass("OpenAI API Key: ")

Pre-Built Tools

The Agents SDK team provides us with a few pre-built tools that can be used to help with common tasks, these include:

  • The WebSearchTool lets an agent search the web.
  • The FileSearchTool allows retrieving information from your OpenAI Vector Stores.
  • The ComputerTool allows automating computer use tasks.

We can start by creating an agent that uses the WebSearchTool to search the web. To do this we need to define an agent with the tools parameter set to a list containing the WebSearchTool object.

python
from agents import Agent, WebSearchTool

agent = Agent(
name="Web Search Agent",
instructions=(
"You are a web search agent that searches the web for information to answer the user's "
"queries with high accuracy and precision."
),
tools=[
WebSearchTool()
],
model='gpt-4.1-mini'
)

We can look at the tools that are currently available to our agent via the tools attribute connected to our agent object.

python
agent.tools
text
[WebSearchTool(user_location=None, search_context_size='medium')]

Now we can use the Runner object to run the agent, asking specifically for the up-to-date information on weather or news related topics that require a web search.

python
from agents import Runner

query = "What are the current headlines in the news?"

result = await Runner.run(
starting_agent=agent,
input=query
)

As we can see, the agent can successfully search the web and return the results, however, if the response can sometimes be out-of-date, due to the LLM thinking that the current date is earlier than it actually is.

python
print(result.final_output)
text
Here are some of the latest news headlines as of April 10, 2025:

**U.S. News**

- **FEMA Hiring Overhaul Raises Concerns**: The Federal Emergency Management Agency's decision to reshape its hiring process for disaster relief personnel has sparked fears about the agency's effectiveness, especially with hurricane season approaching. ([cbsnews.com](https://www.cbsnews.com/?utm_source=openai))

- **Wildfires in the Carolinas Prompt Evacuations**: Wildfires have led to evacuations and emergency declarations in North and South Carolina, marking the second time in less than a month that residents have faced such threats. ([cbsnews.com](https://www.cbsnews.com/?utm_source=openai))

- **Former Federal Prosecutor Found Dead**: Jessica Aber, who served as U.S. Attorney for the Eastern District of Virginia, was found dead in her home. She had resigned from her position in January after approximately three years of service. ([cbsnews.com](https://www.cbsnews.com/?utm_source=openai))

**International News**

- **Pope Francis Leaves Hospital After Five-Week Stay**: Pope Francis, 88, made his first public appearance in five weeks after being hospitalized with a life-threatening bout of pneumonia. ([cbsnews.com](https://www.cbsnews.com/?utm_source=openai))

- **Venezuela Agrees to Accept Deported Migrants from U.S.**: Venezuelan President Nicolás Maduro has reached a deal to accept deported migrants from the United States, following the suspension of flights in March after the U.S. withdrew Chevron's license to export Venezuelan oil. ([cbsnews.com](https://www.cbsnews.com/?utm_source=openai))

**Politics**

- **Former President Jimmy Carter Lies in State**: Former President Jimmy Carter is lying in state at the U.S. Capitol, with Vice President Harris stating that he "left the world better than he found it." ([nbcnews.com](https://www.nbcnews.com/?utm_source=openai))

- **Federal Prosecutors Uncover Additional Conduct by NYC Mayor**: Federal prosecutors have reported uncovering "additional criminal conduct" by New York City Mayor Eric Adams. ([nbcnews.com](https://www.nbcnews.com/?utm_source=openai))

**Business**

- **Hyundai Announces $20 Billion Investment in the U.S.**: Hyundai has announced a $20 billion investment in the United States, signaling a significant commitment to the American market. ([cnn.com](https://www.cnn.com/?utm_source=openai))

- **US Postal Service Head DeJoy Resigns**: Louis DeJoy has resigned from his position as the head of the U.S. Postal Service. ([cnn.com](https://www.cnn.com/?utm_source=openai))

**Entertainment**

- **Denzel Washington and Jake Gyllenhaal Break Broadway Records**: The actors have broken Broadway box office records with their production of "Othello." ([cnn.com](https://www.cnn.com/?utm_source=openai))

- **'The White Lotus' Serves Up New Drama**: The latest episode of HBO's "The White Lotus" delivers a mix of humor and tension as the characters navigate new challenges. ([cnn.com](https://www.cnn.com/?utm_source=openai))

Please note that news developments are continually evolving. For the most current information, refer to reputable news sources.

Custom Tools

Now although the pre-built tools are useful, for specific tasks we need specific tools tailored to the problem we are trying to solve. To do this, we need to create our own custom tools.

We can start by creating a tool that fetches the current time.

First we need to import the function_tool decorator from the agents module.

Then we need to define our function, this needs to be an async function for the agent to work.

Then using the function_tool decorator, we can create a tool from our function.

python
from agents import function_tool
from datetime import datetime

# we can override the name of a tool using the name_override parameter
@function_tool(name_override="fetch_current_time")
async def fetch_time() -> str:
"""Fetch the current time."""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

We can check if the fetch_time object is a FunctionTool object by using the isinstance function.

python
from agents import FunctionTool

if isinstance(fetch_time, FunctionTool):
print("This is a FunctionTool object")
text
This is a FunctionTool object

As this is now a FunctionTool object, we can print out specific properties of the tool.

The name property might be slightly different to the name we defined in the function, this is because the function_tool decorator has a name_override parameter that we can use to override the name of the tool.

The description attribute is the first line of the docstring of the function.

The params_json_schema attribute is the JSON schema for the parameters of the tool.

python
import json

print("Name:", fetch_time.name)
print("Description:", fetch_time.description)
print("Params:", json.dumps(fetch_time.params_json_schema, indent=2))
text
Name: fetch_current_time
Description: Fetch the current time.
Params: {
"properties": {},
"title": "fetch_current_time_args",
"type": "object",
"additionalProperties": false,
"required": []
}

We want to test our agent, but first we need to define an output class that the agent will use to return its response. This way we can ensure the response is correct as well as the approach taken to get the response.

For this, we will use the BaseModel class from pydantic and the Field class to define the response and approach taken.

python
from pydantic import BaseModel, Field

class Output_Class(BaseModel):
response: str = Field(
description="The agent's response to the user's query"
)
approach_taken: str = Field(
description="The method or strategy the agent used to arrive at the response"
)

Now we can define our agent, with the only difference being the output_type parameter set to our Output_Class.

python
agent = Agent(
name="Time Agent",
instructions="You are a time agent that fetches the current time.",
tools=[fetch_time],
model='gpt-4.1-mini',
output_type=Output_Class
)

Now as before we will use the Runner object to run the agent.

python
query = "What is the current time?"

result = await Runner.run(
starting_agent=agent,
input=query
)

This time we have a slightly different output due to our Output_Class definition. Instead of a simple string, we have a dictionary with two outputs, the response and the approach_taken.

If the agent worked as expected, we should see the correct time, and the approach should mention the use of the fetch_time tool.

python
print("Response:", result.final_output.response)
print("Approach Taken:", result.final_output.approach_taken)
text
Response: The current time is 09:42 AM on April 10, 2025.
Approach Taken: I used the fetch_current_time function to get the current date and time.

We have just defined a tool using the function_tool decorator, but we can also define tools using the FunctionTool object.

When defining the tool this way, we need to ensure that the params_json_schema attribute has a additionalProperties key set to False. By default this will not be included in the schema, so we add a Config class to the FunctionTool object with the extra parameter set to "forbid" to force the schema to include it.

python
from typing import Any
from pydantic import BaseModel
from agents import RunContextWrapper, FunctionTool

class FunctionArgs(BaseModel):
x: float = Field(description="The first number to multiply")
y: float = Field(description="The second number to multiply")

class Config:
extra = "forbid" # This adds additionalProperties: False to the schema

async def multiply_numbers(ctx: RunContextWrapper[Any], args: str) -> float:
parsed = FunctionArgs.model_validate_json(args)
return parsed.x * parsed.y

multiply_tool = FunctionTool(
name="multiply_numbers", # name of the tool
description="Multiplies two numbers", # description of the tool
params_json_schema=FunctionArgs.model_json_schema(), # schema of the tool
on_invoke_tool=multiply_numbers, # function to call when the tool is invoked
)

Now we can check the properties of the tool to ensure it is defined correctly.

Note, if you do not include the Config class, the "additionalProperties": false will not be included in the schema, and when the tool is invoked this will cause an error...

python
print("Name:", multiply_tool.name)
print("Description:", multiply_tool.description)
print("Params:", json.dumps(multiply_tool.params_json_schema, indent=2))
text
Name: multiply_numbers
Description: Multiplies two numbers
Params: {
"additionalProperties": false,
"properties": {
"x": {
"description": "The first number to multiply",
"title": "X",
"type": "number"
},
"y": {
"description": "The second number to multiply",
"title": "Y",
"type": "number"
}
},
"required": [
"x",
"y"
],
"title": "FunctionArgs",
"type": "object"
}

Now we are all set to define our agent and use the tool.

This time we will use the same Output_Class as before to ensure the response is correct including the approach taken.

python
agent = Agent(
name="Multiply Agent",
instructions="You are a multiply agent that multiplies two numbers always by using the tool.",
tools=[multiply_tool],
model='gpt-4.1-mini',
output_type=Output_Class
)

As before we can define our query before using the Runner object to run the agent.

python
query = "multiply 3.41 by 7.2"

result = await Runner.run(
starting_agent=agent,
input=query
)

Next we can print our final outputs to ensure the response is correct.

python
print("Response:", result.final_output.response)
print("Approach Taken:", result.final_output.approach_taken)
text
Response: The result of multiplying 3.41 by 7.2 is 24.552.
Approach Taken: I used the multiplication tool to compute the product of the two numbers, 3.41 and 7.2.

Agents As Tools

The last thing we will look at is how to use agents as tools, as the Agents SDK strongly encourages the use of handoffs and agents as tools to build more complex systems.

First we want to define our bottom level agents (in this case the "tools" we will use). For this example we will redefine the time_agent and multiply_agent from before.

python
multiply_agent = Agent(
name="Multiply Agent",
instructions="""You are a multiply agent that multiplies two numbers always by using the tool.
Make sure when returning your response you include the agent that provided the information
along with any additional tool calls used within the agent.""",
tools=[multiply_tool],
model='gpt-4.1-mini',
)

time_agent = Agent(
name="Time Agent",
instructions="""You are a time agent that fetches the current time.
Make sure when returning your response you include the agent that provided the information
along with any additional tool calls used within the agent.""",
tools=[fetch_time],
model='gpt-4.1-mini',
)

Next we can define our top level agent, the orchestrator agent. This agent will use the multiply_agent and time_agent as tools.

We first need to import asyncio to ensure the agent can run asynchronously.

Then when defining the orchestrator agent, we can use the as_tool method to add the multiply_agent and time_agent as tools to the orchestrator agent.

Within the as_tool method, we can set the tool_name and tool_description parameters to the name and description of the tool for additional clarity.

As before, we can set the output_type parameter to our Output_Class to ensure the response is correct.

python
orchestrator_agent = Agent(
name="Orchestrator Agent",
instructions="""
You are an orchestrator agent that uses the tools given to you to complete the user's query.
You have access to two tools, the `multiply_numbers_agent` tool and the `fetch_current_time_agent` tool.
""",
tools=[
multiply_agent.as_tool(
tool_name="multiply_numbers_agent",
tool_description="Multiply two numbers",
),
time_agent.as_tool(
tool_name="fetch_current_time_agent",
tool_description="Fetch the current time",
),
],
model='gpt-4.1-mini',
output_type=Output_Class
)

Next we can define our query before using the Runner object to run the agent.

python
query = "what time is it?"

result = await Runner.run(
starting_agent=orchestrator_agent,
input=query
)

Then finally we can print the response and approach taken by the agent.

python
print("Response:", result.final_output.response)
print("Approach Taken:", result.final_output.approach_taken)
text
Response: The current time is 09:45:34 on April 10, 2025.
Approach Taken: I used the `fetch_current_time_agent` tool to obtain the current time.