Introduction & Lesson Overview

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 Python function with type annotations, turn it into a tool using the @function_tool decorator, 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.

Understanding Custom Function Tools

A custom function tool is a Python 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.

Creating a Custom Function Tool

To create a custom function tool, you start by writing a regular Python function. It is important to use type annotations for all parameters and the return value, as this helps the agent understand what kind of data the tool expects and produces. You should also write a clear and descriptive docstring, including explanations for each parameter and the return value. This docstring is used by the agent to understand how and when to use the tool.

Once your function is ready, you simply add the @function_tool decorator from the agents module above your function. This decorator transforms your function into a tool that the agent can recognize and call. The decorator automatically uses your function’s name, docstring, and type annotations to generate a schema that the agent can use to decide when and how to call your tool.

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 float representing the estimated budget. The type annotations and docstring make it easy for the agent to understand how to use this tool.

Integrating a Custom 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 function (now decorated with @function_tool) in the tools list. 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 estimate_budget 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.

Running and Displaying Custom Tool Results

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 estimate_budget 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 150(basecost)×5(days)×2.0(multiplier)=150 (base cost) × 5 (days) × 2.0 (multiplier) = 1,500. The agent has successfully applied our custom function's logic to provide a precise answer.

Examining the Tool Execution Process

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:

  1. It first receives the user's budget inquiry
  2. It recognizes this as a calculation task and invokes our estimate_budget function
  3. It correctly extracts and passes the relevant parameters ("Norway" and 5 days)
  4. It receives the calculated result (1500.0)
  5. Finally, it formulates a natural language response incorporating this numerical 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.

Structuring Tool Inputs with TypedDict

Sometimes, your custom function tool may require more complex or structured input than just a few simple parameters. In these cases, you can use Python’s TypedDict from the typing_extensions module to define a structured input schema. This is especially useful when your tool needs a group of related parameters, or when you want to make your tool’s interface clearer and more maintainable.

Let’s see how you can refactor the estimate_budget function to accept a single trip_info parameter, which is a TypedDict containing both the destination and the number of days:

By using a TypedDict, you group related parameters into a single structured input. This makes your tool’s interface more explicit and easier to extend in the future (for example, by adding more fields like num_travelers or season). The agent will automatically understand the expected structure of the input thanks to the type annotation and docstring.

When the agent calls this tool, it will construct and pass a dictionary matching the TripInfo schema. The arguments in the function call will be formatted as a nested dictionary, like this:

Notice how the arguments field now contains a trip_info object with both the destination and days fields grouped together, matching the TypedDict structure you defined. This ensures that your tool receives all the necessary information in a clear and organized way, and makes it easier to extend your tool’s input schema in the future.

Summary And Next Steps

In this lesson, you learned how to create and register your own custom function tools for OpenAI agents. You saw how to write a Python function with type annotations and a descriptive docstring, decorate it with @function_tool, and add it to your agent’s list of tools. You also 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!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal