Back to Index

OpenAI's Agents SDK | Tools and Agents-as-Tools Explained


Chapters

0:0 Agents SDK Tools
0:59 Using Prebuilt Tools
2:30 Custom Tools
9:36 FunctionTool Object
13:43 Agents as Tools

Transcript

In this chapter of the Agents SDK course we are going to be taking a look at tools. Now for tools in Agents SDK there are various ways that we can use them. First we have pre-built tools so there is a small number of these pre-built tools that come with Agents SDK.

They are OpenAI built and they allow us to do a few interesting things. Then there are of course custom tools so we can define our own tools and use those as well. So in this chapter we'll work through those and take a look at how we use tools and how we can best use tools in Agents SDK.

So for this notebook I would recommend actually you can open this one in Colab or if you prefer run it locally. It is completely up to you. We of course will need the OpenAI API key unless you're using another provider. Although with another provider I will note that these pre-built tools will not work.

So yeah you have to be using OpenAI models to use these. So there are a few of these pre-built tools from Agents SDK. Those include the Web Search tool, the File Search tool and Computer Use tool among others. And we're just going to take a quick look at the Web Search tool which we briefly use in the previous example.

So when we're passing tools to our Agents, we define an Agent object, we pass the name instructions as we would usually do, we also pass a model as we would usually do. But then we also provide tools. Okay and this is just a list of these function tool objects and we'll see that in a moment.

Right now it's just a pre-built web search tool. Another thing to note here is that the Nano model is not recommended for tools. So you should use at least Mini ideally. Now we can see which tools an Agent object has access to by just looking at the tools attribute.

And we'll see a list. In this case it's just a web search tool that we just defined. And as we usually would do, we use the runner object to run our Agent and get some results from there. So you can see the final output there. So that is today.

And this all seems relatively recent. Now although the pre-built tools are, you know, they're really nice. It's nice to have those. In most cases I think the majority of tools we would be using are either defined through MCP or they are custom tools. Custom tools that we are building and we are defining within the framework.

Now the way that we do that is we use this function tool object. Now the function tool, it's a decorator, we're using it here. Function tool used in this way converts our tool here which is just fetching the time and it's just returning the time for us. So we might use this in this scenario that we need to be able to tell our LLM or the LLM needs to be able to check time.

Maybe the user says, hey, you know, what time is it right now? Or how many days is it until Christmas, right? In this case the LLM might go and use a fetch time tool. You may also just insert time directly into your system prompt if this is something where you're expecting your agent to be like time aware like a lot of the time or you can just give it all to use.

It depends. So in this case we're just going to define a function tool for our model to fetch the time. Now one thing, if we don't set the name overrider here, this is optional, right? So we can just do, you know, without that, we could just do function tool.

But in this case, the function tool name as provided to our LLM will be fetch time. Alternatively, if you want to give it an actual name or a different name to what the function is called, you can do that. So you just use this name override parameter. I believe also that the name here cannot be, it needs to follow like a lowercase with underscores format.

So let's just try that because I don't quite remember. Okay. You can see if we do that, if we don't format that name correctly, we're going to get this. So invalid tool's name string does not match pattern. Okay. And it tells you here, this is the pattern we're allowed.

So we're allowed to have hyphens. We're allowed, we are actually allowed uppercase. So we're allowed uppercase, lowercase and numbers or underscores. Okay. So that's good to know. So we could, if we wanted to, we could just do something like this. That should work, but I'll revert it to what we had before.

Okay. Now we can just confirm that this is a function tool object. And you see, yes, our fetch time function is in fact a function tool object. That's great. That means, that means our decorator worked as expected. And because it's a function tool object, that means we can, we have these specific attributes that we can pull out.

So we can get the name, which you'll see a moment ago, I modified this. Let's run this again. And we'll see that it's now our underscore name there or a valid name. We can see the description. Hey, that's weird. Where did we, where did we set that? So let's come back up a little bit and we can see that the description is being pulled from the dark string.

So this dark string here is actually pretty important. It is in here that you described your LLM how and when it should use a particular tool. Okay. So it's important that you, that you use this and make sure that you describe, right? In this scenario, this is a very simple scenario.

So it doesn't, you know, we just write something simple, but for example, web search in your dark string here, you should be telling your LLM, look, this is a web search tool you should use, or you should provide as much useful context as possible in your search query. This is in natural language.

If it's a, depending on what web search engine you're using, right? So you can give the LLM instructions on how to best use your tool within this dark string. That, that is an important thing. You should always do that. Then we can also check the parameters. So parameters here, we can see, okay, this function, terrible example, it doesn't have any parameters.

So we're going to see, okay, there's no required parameters. There's just nothing in here. So that's fine. We're going to see later a tool with parameters. Okay. So we have our output class here. We don't necessarily need to use this, but this is, this allows us to do structured output.

I'm going to run without this first and I'm going to switch to using it. So do this and let's just run this and see, see what we get. Okay. So something happened. Let's take a look. Okay. So we've got this. The current time is this. Now the, what I want to show you with that output class is that you can also structure your output.

So in this, this is a, you know, we're adding a lot of complexity to a very simple use case here, but it's good for demoing all of this. We want our response. Okay. As we just got, that's just our response here. But I also want the LM to tell me why did it do what it did.

Okay. So the LM is just going to say, okay, I use the fetch current time tool because the user asked about the current time. So I needed to do that. Right. So we should see something like that. So yeah, we define that both of these outputs are strings. You may also, I don't know, let's say we want like a confidence score, for example, that could be a float.

Uh, yeah, you can see, thank you copilot for writing that for me. You can see the confidence here. We have our flow. This is a confidence level in its response from zero to one. So that can actually be a pretty useful thing to, to include as well in general.

So yeah, this is structured output that we are providing. And we provide that via the output type parameter as before. We're going to use that runner object. And then let's see what final output is because now we have multiple outputs. So it's going to be a bit different. Okay.

We can see output class, the response here, and we can see the approach taken. I fetch current time using systems time query function to provide you with the exact and update time. Okay. That's great. And then we see that is the output class that we defined that that object.

So we can also go in there. If we want to access each one of those specific values, we can go in like that. Okay. So we come to here. No, I already printed it here. Nevermind. We can print it again. Okay. And we can also use the function tool object here as well.

So, you know, we can do that with Pydantic, for example. This can be useful if you are wanting to define a particular structure for the input. So you can see here in this example, we have these function args. We're going to be using these function args in a tool, which is just going to multiply two numbers together.

Again, super simple example, but nonetheless, really simple example. We have the description for that. So this is the, you know, where we had the dark string before, but then we also have this params JSON schema, right? Params JSON schema is consuming what we have up here and using that to describe to our LM what the input parameters are.

And this is really useful because by default, we can't do that. So if we, we just define a function and we decorate it with function tool, we can't specify in that function descriptions for each and every parameter, right? So if you have a set of parameters you need to describe, this is really useful for those more complex use cases.

You can describe everything in much more detail using this approach, which is, it's pretty good. And you can also validate things as they're coming in as well. So that's great. We, we do still need to, of course, define our function that is going to be used. So we have this multiply numbers function here, and we would just pass that down here to the on invoke tool.

Okay. So we have that. Now we can come down and just see, as we did before, we can see everything that we have defined. So we get name and description as before. Okay. We define these a different way before, but the outcome is the same. Okay. So this is the, within that function tool object, these are those attributes.

And then we also have this parameter JSON schema. This one is a little bit different because now we have parameters. Okay. So within the properties dictionary here, before this was empty because we had no parameters. Now there are parameters. We have X and Y. And also within those parameters, we have descriptions.

Okay. So this is all getting sent to the LLM. The LLM now knows exactly what each one of these parameters is for. Of course, in this example, this is a very basic example, it's not really needed, but for more complicated tools, this is, this can be very helpful. We also have this required parameter down here.

Now this is saying, okay, X and Y are both required parameters for this function. So the LLM knows it must generate both of these to use the tool correctly. What may happen, right? You, you might have optional parameters. So let's just set one of these to optional very quickly.

Let's say, you know, we don't need X for whatever reason. So we'll run this. We'll come down to here and we'll now see if we make the scrollable, we'll see it required is now just Y. We don't need X. It's an optional parameter, but X is still provided within our schema here.

So it's still described. The LLM still knows it can use X if it needs to, but it's an optional parameter. So it doesn't have to, which can be really used. Well, you come and need that in, in many tools. So we have that. Um, we, of course, in this scenario, we are definitely going to keep X as a required parameter.

So rerun those. So let's just go down here and run our new query. So we're saying we want to multiply 3.41 by 7.2. Let's see what we get here. We can, uh, we can, well, we, we're going to get an object out of this. I'll just show you that very quickly.

So final outputs. That's because we, we set our output class again down here and we can see response and approach taken. And we'll just print both of those out a little more nicely here. Okay. So that's pretty cool. Now the very last thing we'll, we'll take a quick look at here.

We will cover this in a little more, a little more detail later. We want to look at agents as tools. So in agents SDK, we can use handoffs and agents as tools as ways to handle multi-agent systems. Now agents as tools is what it sounds like. You define an agent and then you actually just provide that agent as a tool for another agent.

So in this example, kind of a, again, silly example, because you would just use the tools directly, but nonetheless, in this example, we're going to have our multiply agent, which has access to the multiply tool. And then we also have a time agent, which has access to the fetch time tool.

In reality, it's, you would probably use both of these as more like, okay, this is my social media agent. It is the agent that is going to read all of my social media things and generate posts for me or make posts for me. And then maybe we have like a writer agent, which has been prompted with all this, you know, writing goodness and maybe can reference, you know, our past materials to generate a article or social media post or whatever.

Right. And those two agents would work in tandem to, you know, like read, okay, what, what should we talk about? I'm going to generate a post and, you know, post it out and, you know, just generate loads of AI slop for everyone to scroll past as they're on their, on X or LinkedIn or whatever else.

Right. So, you know, you might want to use it or not for, for that. This example is a little silly in that, yeah, we would just use like, you probably won't even use these as tools a lot of the time, but nonetheless, this is how it works. So coming down here, we've created our two tool agents or sub agents, however you want to call them.

And then we're also going to create this orchestra agent. And this is the kind of like the controlling agent. So when you're using agents as tools, you do need like a top level agent. And we actually don't need to provide this information here. We can remove that. So it's just saying, okay, you're an orchestra agent.

You're going to do some stuff. And then we have these two tools, which are agents, right? So we just write the multiply agent or the agent name as tool. Then we provide the tool name. So as you know, how do we call this and the description for it as well, let's go ahead and let's, let's put all this together.

Let's run and we'll say, what time is it? Now, when we're using agents and tools, just remember that, okay, we have our orchestra agent that is going to say, I want to use the time agent. Time agent checks time, writes a response, takes it to the orchestra agent. Then the orchestra agent is going to write, it's going to read that response and then write the response to you.

So it's a token heavy approach. Just bear that in mind. The, the handoffs are a bit lighter and better than we'll look at those pretty soon. So let's see what we got. And yes, you can see. Okay, cool. So we can take a quick look at our tracers to see what that looked like.

So the, the multi-agent thing, you can see the two tools we use here. So go into there. There was two tools were actually the orchestra agent calling the fetch current time agent, and then the fetch current time agent calling the fetch current time tool. Okay. Again, I just want to emphasize that this is not the way that you should use sub agents, like all agents as tools in agents SDK.

This is a very, very silly example. This is just to show you how it works. We'll see pretty soon more realistic examples, but for now, that is actually it. So we've covered tools and you know, how we, how we use tools, how we define tool, both built-in and the custom tools.

And then we've also seen how we can use agents as tools. So that's it for this chapter. I will see you in the next one. Thanks. Bye.