-: In the previous lesson, we looked at tool calling.
So how to define a tool,
how to use a tool within a large language model.
In this video we're gonna have a look
at how you can specifically create custom agents
using these tools,
putting them into a loop,
and then breaking that loop accordingly.
The first thing I want you to do is open the notebook
in the openai_features_and_functionality folder
called learning_agents_from_scratch.
So what we're gonna do is import openai,
numpy, matplotlib, and pydantic.
We're gonna have a look at is using a agentic loop
that we'll build from scratch,
and you can either copy
or you can follow along and code it from scratch.
The thing we're gonna be coded from scratch
is a simple agent that's in a while true loop.
So firstly you need to import your OpenAI package,
and the model that we'll be using is gpt-4.1-mini.
Now, we've already set up the get_weather function
and the weather tool for you.
You're going to need to replace your OpenAI key here,
so I'll just let you do that.
Okay, so we have the get_weather function,
we have a weather_tool dictionary here,
and we also have our tools Python list that we've set up
with the information for a single weather tool.
Now, if you scroll down, here's where it gets interesting.
So we're gonna set our messages up
and what we're gonna do for our messages
is we're gonna start with one message.
So I'm gonna put role as equal to user,
and the content is, "What's the weather like
in Paris today?"
Let's get rid of this section here.
So I'm just gonna do just the weather in Paris.
And then what we need to do
is firstly we create our initial response,
given this message,
and we have the model is equal to our gpt-4.1 model.
The input is equal to the messages
and our tools is equal to the tools that we defined earlier.
Then what we're gonna do is process all function calls
in the response to output.
So if there's a response to output,
what we're then gonna do is we're gonna loop through
all of the output items.
So you'll see here, we're going
for output_item in response to output.
If we have a .type
and it's equal to a function call,
what we're then gonna do is append the function call
to the messages.
After that, we're gonna get the tool call
so that would be tool call = output_item
and then we're gonna load in the args,
json.loads(tool_call.arguments).
After that, we're going to execute the function.
So you've got this get_weather
and we're passing in the args of latitude and longitude
and we're printing out the fact
that we've executed that tool call.
And then what we're gonna do
is now that we've executed that tool call,
we need to then append the messages back into the LLM.
So that'll be type function_call_output.
The call_id is equal to the tool_call.call_id,
and the output is the string of this result.
Now, this is similar to what you have with function calling.
The only difference is,
what we're now gonna check for
is if there's any output text.
So we're gonna say if there's any output text,
and we'll do this out here,
if there's any output text,
then we have a final output and we have a break.
And if there isn't a response to output,
we are then just gonna break
just to be on the safe side of things.
So what's gonna happen is,
we have this client.responses.create,
we have a query that's gonna trigger a tool call,
if there's an output,
we're gonna loop through every output
and if it's a function call,
then we're gonna add that tool call to the message.
We're then gonna get that tool call,
get the various bits of information,
we're gonna get the weather and invoke that.
We're then gonna tell the messages
that this is the output of that tool call.
And then this is the clever stuff here
where you can basically say, if we have an output text,
then that basically means we're finished at this point
and we'll break out of the endless while loop.
Now, if we didn't have an output before,
just for simplicity,
then we would also add a break here.
So if you run this now,
what you'll see is if you scroll up here,
it's gonna execute that tool call
and it returns the final output.
Now, we can take this a step further
and just wrap all the code that you just wrote
into a python function called an agent_loop,
which just takes in messages and tools.
And then we can try and run that on a query
that has two different types of intents.
So you'll see here we've got a role of developer,
that's basically the same as the system message,
which is useful for instructional pieces of content.
Then we have a content key,
and under that it says,
"What's the weather like in Paris today?
Before applying, I want you to also
get the weather for Berlin."
Now, when we run our agent_loop function,
what you'll see is it actually decides
to execute two tool calls before responding with the output.
So again, the important point is we put the agent
into a while true loop
and we loop through
and anytime we have tool calls, we're continuing,
but if we have that .output_text property,
then we're breaking out of the loop.
You can also do it slightly differently
with a objective function.
And basically what that means is you have
some kind of custom logic
where rather than just endlessly being
in a while true loop, you might decide,
okay, the agent has a maximum of five times that it can run
or a certain amount of time that it can run.
In our case, we have an objective function
with this objective met in terms of the search count,
is it greater than the maximum number of searches?
We can then do something like this
where the role of developer, you know,
you have the content,
"Your goal is to gather weather
for at least five different cities.
Once you've done that, respond with task complete."
And then what you can see is we have this search,
the weather in Berlin.
Now, we can still use a while true,
but what we can do then is we can then basically do
the same kind of logic,
however we increment the search count.
So then what we do is check if the objective is met
based on that search count,
and if necessary, then we will break out of the loop.
So you can see here, when we run this,
what will happen is we'll be getting weather.
So it's decided to do New York
and it's getting some weather
and it's then it's searched in six cities
and after it's searched over a certain number,
it has exited the while true loop
because of this objective_met function.
So basically all you're doing here is creating
a custom function so that it doesn't endlessly go around
in a while true loop.
It can exit before, by using a custom objective function.
The other common ways to do this
is have, like, a MAXIMUM_NUMBER_OF_ITERATIONS
and then you can then say,
when you're doing these kind of iterations,
you will then have something like this
where if you are over the number of iterations,
we also have to store the current iterations,
CURRENT_NUMBER_oF_ITERATIONS,
if the current number of iterations
is greater than or equal to
the maximum number of iterations,
then we're breaking out of the loop.
And you can see here,
every time we go around,
we're incrementing the current number of iterations.
So this is another way that you could also break
out that while loop
so that your agent doesn't endlessly go on
if it doesn't meet a custom objective function.
Okay, just to recap,
we looked at how we could use the while true loop
to basically create an agentive while loop,
and then we looked at the fact
that we're doing function calling here
and we also have a custom step
which will break us out of the whiled true loop.
This is how a lot of the agents under the hood are designed
and it's well worthwhile, you getting familiar
with this type of pattern.
Secondly, we looked at the fact
that agents can do parallel tool calling.
This mixed intent query
is searching for two types of weather
and we can see that it executed two tool calls
before responding to the user.
Thirdly, we looked at making custom objective functions
and using an objective function
or the maximum number of iterations
to break out of a while loop
to avoid the fact that these agents could get stuck
for larger periods of time.
Cool, I'll see you in the next one.