Welcome back! In the previous lesson, you learned how to make your OpenAI agents more powerful by integrating hosted tools, such as the webSearchTool
, which allow agents to access real-time information from the web. This was a big step forward, as it enabled your agents to answer questions and provide recommendations using up-to-date data, rather than being limited to what the language model already knows.
Today, you will take the next step: learning how to create and register your own custom function-based tools. This is a key skill for building agents that can do more than just search the web — they can now perform calculations, access your own data, or run any logic you define. By the end of this lesson, you will know how to write a TypeScript function with type annotations, turn it into a tool using the tool
helper, register it with an agent, and see it in action alongside other tools. This will prepare you for the hands-on exercises that follow, where you will practice building and using your own custom tools.
A custom function tool is a TypeScript function that you define and then register as a tool for your agent. Unlike hosted tools, which are provided and maintained by OpenAI, custom tools let you add your own logic and capabilities. This means you can make your agent do things that are specific to your needs, such as calculating a travel budget, looking up information in your own database, or even calling an external API.
While hosted tools are great for general tasks like web search, custom tools are essential when you want your agent to perform actions that are unique to your application or business. For example, if you want your travel assistant agent to estimate the cost of a trip based on your own pricing logic, you can write a function for that and register it as a tool. This flexibility is what makes custom tools so powerful.
To create a custom function tool in TypeScript, you use the tool
helper from the @openai/agents
package. This helper takes an object with several properties:
name
: A string identifier for your tooldescription
: A clear description of what the tool doesparameters
: A Zod schema defining the input parametersexecute
: An async function that implements the tool's logic
The parameters
property uses Zod schemas to define the structure and types of your tool's inputs. This provides runtime validation and helps the agent understand what data the tool expects. Each parameter can include a description using the .describe()
method, which helps the agent understand how to use the parameter correctly.
For example, here is a function tool that estimates a travel budget:
In this example, the function takes a destination and a number of days and returns a string representing the estimated budget. The Zod schema and descriptions make it easy for the agent to understand how to use this tool.
After creating your custom function tool, you can add it to your agent's list of tools, just like you did with hosted tools. When you create the agent, include your tool in the tools
array. The agent will then be able to call your tool whenever it decides it is relevant to the user's request.
For example, you can create a travel assistant agent that uses both the webSearchTool
and your new estimateBudget
tool:
Now, when a user asks about the cost of a trip, the agent can decide to call your custom tool to calculate the budget, use the web search tool for up-to-date information, or even combine both. This makes your agent much more flexible and capable. It's important to mention the available tools in your agent's instructions to guide the agent on when to use them, which helps avoid unexpected behavior and reduces the chance of the agent hallucinating information instead of using the appropriate tool.
Let's examine how our agent leverages the custom budget estimation tool in a practical scenario. Here's a simple code example that demonstrates running the agent and displaying its output:
The agent processes this request by identifying that it needs to calculate a travel budget. Since we've equipped it with our custom estimateBudget
tool, it can perform this calculation without needing to search the web. The response might appear as:
This response directly reflects our tool's calculation logic: Norway is classified as a high-cost destination (with a 2.0 multiplier), so the math works out to 1,500. The agent has successfully applied our custom function's logic to provide a precise answer.
To gain deeper insight into how the agent utilized our custom tool, we can inspect the complete interaction sequence:
This reveals the step-by-step process of how the agent handled the request:
This detailed breakdown illustrates the agent's decision-making process:
- It first receives the user's budget inquiry
- It recognizes this as a calculation task and invokes our
estimate_budget
function - It correctly extracts and passes the relevant parameters ("Norway" and 5 days)
- It receives the calculated result ($1500 USD)
- Finally, it formulates a natural language response incorporating this value
By examining this detailed flow, you can verify that your custom tool is being used correctly and understand exactly how the agent is processing and responding to user queries.
When developing custom function tools, it's beneficial to define input schemas separately from the tool definition. This practice enhances maintainability and reusability, especially when multiple tools require similar input structures or when you need to share schemas across different parts of your application.
Here's how you can define a separate schema for trip information and integrate it into your estimateBudget
function:
By defining the TripInfoSchema
separately, you achieve several advantages:
- Reusability: The same schema can be utilized across multiple tools requiring trip information
- Maintainability: Any changes to the trip information structure need to be made in only one place
- Type Safety: You can infer the schema type for use in other parts of your application
- Testing: The schema can be tested independently of the tool's logic
When the agent calls this tool, it will construct and pass an object matching the schema. The arguments in the function call will be formatted like this:
This approach ensures that your tool receives all necessary information in a clear and organized manner, facilitating easier extension of your tool's input schema in the future by simply updating the separate schema definition.
In this lesson, you learned how to create and register your own custom function tools for OpenAI agents. You saw how to use the tool
helper with Zod schemas to define tools with clear parameter types and descriptions, and add them to your agent's list of tools. You also learned how to create maintainable schemas separately from your tool definitions, making your code more reusable and easier to maintain. Finally, you saw a complete example in which a travel assistant agent used both a hosted tool and your custom tool to answer a user's question.
With these skills, you can now extend your agents with any logic you need, making them much more powerful and adaptable. In the next set of exercises, you will get hands-on practice building and using your own custom tools. Congratulations on reaching this milestone — your agents are now ready to do much more!
