Eden: Hey there, Eden here,
and I want to have a recap on what we did
so far in the course when it comes to agents.
And by now, I really hope that the ReAct algorithm
architecture and implementation
and everything around it is crystal clear.
And I'm hoping that if I'm going to wake you up at night
and ask you, "What's the flow of the ReAct algorithm?"
then you'll be able to sing it.
And I'll have a really quick recap.
We send the query to the ReAct agent,
the large language model then ponders
and thinks and decides which tool to call.
Then we go and execute that tool
and then we go and do that until we get a final answer
until there are no more tools to invoke.
All right, so let's go back to the diagram
that I showed you earlier in the course
about the evolution of ReAct agents.
So it all started with the ReAct paper
and the ReAct prompt,
where we used the LLM as a reasoning engine
and then LangChain implemented some fancy parsing
in order to extract which tools to execute.
Okay, so this was the very beginning.
And this implementation was super impressive,
but it wasn't that reliable
so we couldn't really use this in production
because the models weren't that strong by that time
and it was really hard to parse the output.
Because the models weren't that reliable,
then parsing the output was really hard.
It was really non-deterministic
so we really didn't have control
about which token the LLM is going to generate.
And because of that, it's enough that the LLM
is going to generate one wrong token,
and it can cause and mess up all the output parsing.
And then LLMs became a bit better
and function calling came out,
and function calling normalized this process
of making the LLM behave as a reasoning engine
and suddenly we don't need this ReAct prompt anymore,
we can rely on the vendors
and the model's function calling capabilities,
and the model is going to return which function to call,
and it's going to do a very good job doing it.
And the function to call,
it's going to specify it
in a very special place in the request.
So we do not need this special ReAct prompt anymore
and this weird output parsing that we did,
which was really hard but not that reliable.
So now we shift everything to the vendor,
the vendor now is responsible to do that,
and we get the information
about which function to call in the LLM's response.
Now, we solved an earlier problem of this output parsing,
which was not reliable,
but we introduced now a new problem
because every vendor did what they want,
and the information about which function to call
was placed differently in different places.
So one called it function calling,
the other tool calling,
it was in different parts of the response.
So what LangChain did is create one single interface
that is going to be called the tool calling interface,
and it implemented all of the integrations to the vendor.
So now we have only one interface of tool calling,
and it's going to get all of the functions to call
and the information about it
and it's going to work for every vendor.
So this was very useful.
Now, when LangGraph came out,
it had a completely different architectural approach.
So instead of having a function-based agent loop,
which was really a while loop abstracted
with the agent executor class of LangChain.
So there wasn't much flexibility
and there wasn't much visibility
or any way to control this loop here.
So then LangGraph came out,
and LangGraph modeled agents as graphs.
So they had nodes, they had edges,
and they had between the nodes a shared state.
So the LangGraph ReAct agent had three components.
It had the state, which was a dictionary
that maintained the conversation,
maybe some intermediate results,
and nodes are simple Python functions
that are receiving the state,
perform the computation,
like maybe to call the LLM or to execute the tool,
and then they return an updated state.
And the edges, they define the control flow.
And by the way, the great motivation for LangGraph
is that LangChain really saw that in every paper
in almost every time you describe an agent,
you actually describe a graph with nodes and with edges.
So this architecture change really helped
to unhide the control flow
because now we have an explicit graph structure
that we can even print and show us a picture,
and it's really hard to understand what's happening
and what's being executed.
Now, if we wanted in the old agent executor
to have some kind of state,
it was really hard to do
and we had to do it with keyword arguments
with a config object,
and it was actually very hard to do.
But with LangGraph and the state schema
is actually very easy to do.
We simply defined in the state
what is the field we want to keep track of.
So if we wanted to know what's happening
with our agent execution
in the agent executor original version,
we really didn't have a way to do it.
So we didn't have a way to monitor and to trace
and to keep track of what's happening.
But with LangGraph, we have automatic checkpoints.
So every time before we execute a node,
LangChain is going to persist the state
and we can have access to that
so we can see exactly what happened and when.
And we can even rewind
and we can travel back in time
and it gives us much greater flexibility.
Now, the biggest thing that LangGraph unlocked
is the fact that we can now compose graphs,
which are agents, one inside another.
So we can actually use a LangGraph graph
as a LangGraph node,
and this is very convenient for us.
And it comes out-of-the-box tracing
and all of the benefits that LangGraph gives us.
And this is something which was very hard to do
with the original agent executor.
So this LangGraph agent was built
under the LangGraph library,
under the graph prebuilt agents
and lived there for a while.
We actually implemented a very similar version ourselves
in this section.
And then LangChain and LangGraph reached version 1.0
and it brought a cleaner API service
with the new create_agent function,
and LangChain then deprecated
and replaced the Create ReAct agent
and the LangGraph prebuilt agent
and simply put everything under this create_agent function,
which returns a compiled graph,
which is LangGraph under the hood,
but with a simple interface for people to use it.
And in this interface,
we can simply give it to the models and the tools,
and boom, we get a ReAct agent which is ready to go,
which has all of the goodies that LangGraph can offer us
with observability, with debugging.
And it also has more flexibility
because we can actually go and customize it ourselves.
All righty, so I hope you enjoyed this video,
and I hope you enjoyed the flow of learning
in general of LangChain agents.
And I think it's really, really important
to know how everything started.
And that you now know, of course,
how to use LangChain create_agent,
but you know exactly how it's implemented under the hood.
And for you, it's not magic,
you know exactly how it's implemented.
And in fact, this interface is just an easier way
for you to use it.
And it's really the foundation for modern LLM agents
and deep agents,
which I hope to cover in the course.