-: And in this video, we're gonna have a look at agents.
Agents are a specific type of workflow
where the LLM will run in a continuous loop,
interacting with its environments and receiving feedback
to refine its actions and decisions.
Agents use tools and then they go through an agentic loop.
They receive, after making an action,
they'll get some feedback from the environment.
After a point in time, the agent will decide to stop.
Some of the use cases for agents
include personal research assistants,
automated code reviewing,
customer service agents is a big one.
And also managing social media presence
by analyzing trending topics, generating relevant content
and scheduling posts.
Okay, let's start by importing OpenAI and Pydantic.
We'll also make sure that we have the JSON, OS
and OpenAI packages installed and imported.
And we'll then also make sure that the client
and model have been created.
The first thing we're gonna do
is we're gonna create a search_knowledge_base function.
(instructor coughing)
The first thing we're gonna do,
(instructor clearing throat)
(instructor gulping)
the first thing we're gonna do,
the first thing we're gonna do
is create a search_knowledge_base function.
And this is just gonna be a fake function
that we're gonna take a query, which is a string.
We're going to return a string
and all we're gonna put in here is return summary for,
and then we'll put query
and then we'll put, this is a simulated summary
from the knowledge base.
Okay, cool, now that we have that function,
the next thing we need to determine is tools.
So obviously tools allow the agent to take actions.
Now, you will have to write JSON schema out.
So the first thing is you have to have a type of function.
And then after that, then the next thing
that we need to do is we need to step into that function.
So we do function
and then we specifically have
to start listing out the individual things.
So for example, what is the name of that function?
Well, it's search_knowledge_base.
What is the description?
Query a knowledge base to retrieve relevant info on a topic.
And you can see it's populated the parameters required
and additional properties.
And I'm gonna get rid of this one here.
And there we go.
So we've got our function, which basically is a schema.
This is a JSON schema definition of a tool.
Now we need to generate some messages.
So we'll have a messages list
and we'll have a role of user and content.
I'd like to know more about quantum computing.
Could you give me a quick summary?
Then we create a chat completions request
with client.chat.completions.create,
and we pass in the tools alongside the model
and the messages.
Now, what you'll see here is it has a .tool_calls.
And basically what this is gonna mean
is that ChatGPT wants to make a tool call.
And the idea of that is this,
and this is the function with the arguments
and the name of the function that it wants to call.
So we can tell ChatGPT, these are the tool_calls.
So let's just do that manually to start with.
So we'll say tool_calls = completion_1.choices,
[0].message.tool_calls.
And then we can get those tool_calls out
and just have a look at them.
So pasting in tool_calls here,
you can see that we have a ChatCompletionMessageToolCall,
which provides a tool ID
and also the function with the arguments of the function
and the name of the function.
Now, the first thing we're gonna do is make sure
that the messages also has the most recent message
specifically from the chat completion_1.
So we'll just do completion_1.choices,
[0].message.
And then that just basically means
that this messages will now have this additional one
in here, which is the ChatCompletionMessage.
So now how can we access these tool_calls?
We're gonna do a for loop.
So we'll save for tool_call in tool_calls,
we're gonna get the function name,
which is the tool_call.function.name.
And we're also gonna get the function args,
and then we're gonna have something like this where we say
if the function name is the search_knowledge_base,
we're gonna execute that function with here.
And then we're gonna add the messages
and we just need to add a role of tool,
the tool_call_id and the result.
And if it's not this function, then we're gonna go
and just raise a ValueError.
So I'm just gonna raise a ValueError
and then we're gonna have a look at the messages as well.
And now you can see here we've got the original message.
We've got this ChatCompletionMessage in the middle,
and we also have the tool_call_id that specifically responds
to this one here, the first tool_call.
Now we can create the completion_2 request
and then passing in the original messages and tools,
and looking at the content of that message.
And it says quantum area computing is an area of computing.
So it is basically decided this is the final answer.
So it executed that search on the knowledge base
and then it hasn't come up with any more tool_calls
and therefore, it's given us a final answer here.
You can confirm that is the case
by looking at the completion_2.choices[]0.message.tool_calls
and it is essentially empty.
Now, there is an alternative way to write this
that is a little bit better in my opinion.
So you have a messages list
and we're just gonna write it in a while truth.
So we'll say messages.append
and then we're gonna basically
just have some information, "role": "user".
And then instead of asking about quantum computing,
we're going to ask about ChatGPT.
So we'll say, can you find some information
about ChatGPT and the AI knowledge base?
We're also gonna create a function just above this.
So we'll say def search_knowledge_base.
And we're gonna say some information about ChatGPT
just to mimic this.
So we'll say ChatGTP
is a large language model developed by OpenAI.
And then what we're gonna do
is we're gonna set up our while true loop.
And then this is where you can basically have all
of your agentic code running in a while true loop.
So you start with your first completion
and then what you're doing
is saying client.chat.completions.create.
And then after that, you paste in the model,
we paste in the messages,
and we're also pasting in the tools.
We then always paste back in the message.
if there aren't tool_calls,
we can then break out of the for loop.
If there is a tool_call, then we get the function name
and the arguments.
And then we also get the result of that tool_call.
We pass in the role, the tool_call_id and the content.
And then what you'll see is then basically this will run
in a while true loop and it finishes after 2.7 seconds.
So it actually does finish
because the reason why is it decided
that there weren't any tool calls
and therefore, all of the agentic work was done.
So you can have a look and you can see we've got
that initial first message.
Then we have our ChatCompletionMessage
that did ask to do a tool_call.
We then did that tool_call.
We got some information about the fact
that ChatGPT is a large language model
and then also, it decided to use that information
to then respond.
So the key point here
is that you are basically stuck in a while true loop
and you also have the ability
to break out if there are no more tool_calls to be made.
Now, just to give you a bit of a word of warning,
the problem that you can run into with this
is that it endlessly makes tool_calls,
trying to search or do additional steps.
So a nice quality of life feature
is to add a maximum number of iterations.
So you can say maximum number of iterations,
and you can just put maybe 10.
And then we can say we can adjust the while true loop.
So I'm just gonna use cursor to do this.
So we'll say adjust the while true loop
to work only up to 10 iterations.
And so that's just gonna say every single time
we do an iteration, we're basically just gonna add on one
to the iteration variable.
And then after that, then we make sure that we don't hit
that number of iterations,
and then we can track how many iterations we make
in real time.
So this can be like another good way of figuring out,
so we made two iterations, the zero and then the one.
And this can be a very good way
of avoiding endless while loops.
Something else that I also want you to consider
is you might also not want
to break when there are no tool_calls.
You might want to break on a specific goal.
So imagine you're trying to get a certain number of leads
or a certain number of email addresses scraped.
You could have that as your goal inside of the while loop
and you could endlessly scan new areas of Google
to specifically go and look for that information.
I actually created a demo version
of this in an AI-powered outreach assistant.
So I'll put a link to the public repository
for you to go and have a look at.
But if we scroll down and have a look
at the agentic while loop,
you can see this is where we start to do this.
So we have our while true loop.
We also then decide okay, if we've gone past the time,
we break out of the loop
and if we've hit our objective,
we also break out of the loop.
So those are some easy ways to make sure
that we always break out of the loop.
And also, the other thing you'll want to be aware of
is when you're running agents for a large amount of time,
the amount of chat message histories
that you get will become exponentially more expensive.
So you might want to prune the messages,
but you must keep the tool pairs, the tool_call
and the tool_call result paired messages next to each other
otherwise OpenAI will throw an error at you.
So make sure that if you are going
to prune the message history,
have a look at the helpers.py inside of here
where you can see there is some advanced pruning
so that we can specifically always keep the system message
and have these remainder pairs of the tool_call_id
with the tool_call result chat message history.
Cool, hopefully this is a good intro into agents
and feel free to let me know if you've got any questions.