-: Hey, welcome back.
And in this video, we're gonna have a look at
how you can implement human-in-the-loop editing
inside of LangGraph.
Previously, we were looking at persistence
and how we could use the thread ID
to specifically load the same checkpointed memory version
of a LangGraph state.
In this video, we're gonna have a look
at how we can implement human feedback
to update a graph in real time.
We're gonna install a couple of different packages.
We're gonna run your imports
and we're also gonna add in the Tavily key.
So, I'm just gonna go and add my Tavily key back in.
Now, human-in-the-loop is a way
where we can basically at some point in the LangGraph flow,
decide that we want a human to either approve
or go and edit part of the graph
or the steps that have been taken before.
And we use this in LangGraph
by using this concept called
an interrupt_before functionality
to always break before a specific node.
And then the other important point is that
you really need to, once you've then updated
this state of the graph,
make sure that you enter the same node that you came from.
So if you can think of it like maybe
before you're going onto this node here,
you update the state of the graph.
Well, you probably want to make sure
that when you updated the state
and you came back into the graph,
so if we're taking away these bits here
after we've updated the state of the graph,
we would then want to reenter specifically on this node
and not other parts of the state machine, right?
So, there could be various nodes upstream,
and maybe this node here is the human-in-the-loop node.
And the problem that we've got is if we update
the state of the node,
we don't necessarily want to go anywhere
in the state machine.
We want to specifically go to this node
with the human-in-the-loops feedback
and then go onwards into the right node
via a different edge.
And so, that's kind of the important point
around the interrupt_before is in here,
you'd have an interrupt_before.
So, from this node to that node,
you're going to have a interrupt_before.
And then just before you get into this node,
what's gonna happen is the graph is interrupted, yeah?
So it's stopped, right? The graph is stopped.
The second thing that happens is we manually ask
a human to update the graph
and then we do a reentry into the graph
from the node of our choosing, right?
And in this specific case,
we want to reenter the graph from this node here, yeah?
So there's a couple of different points here
that we're gonna talk on and I just thought we should have
a look at that visually before going on.
So, this interrupt_before is gonna be very important
in LangGraph to make sure that we don't flow
down to these other nodes
before asking for feedback from a human.
Cool.
All right, so we've got pretty much the same code
that we had in the previous persistence lesson.
You've got that Tavily search results.
You have the add_messages.
You've got your SQLite check pointer
and you've got your StateGraph and your ToolNode.
So, all of this code is exactly the same.
I'm not gonna go through it
because it is pretty much exactly the same.
But one thing that we are doing, which is a new thing,
is you can see we're interrupting before tools.
You can also interrupt__after__actions, as well.
So, you could interrupt after a specific tool is called.
And you can see we've got some some user input.
So, "I'm learning LangGraph.
Could you do some research on,"
and we are using the thread_id of 1
for the check pointer.
Then when we stream, we pass in that runnable config
and we're also passing in the users,
and, you know, the user message with that input
and we're just streaming that out.
And so, if I see the hit, so let me just go make sure
that graph is compiled
and then when I run this, it says,
"I'm learning about LangGraph."
Then it decides that it needs to call the Tavily search tool
and it's got this tool call ID with these args.
Now, the interesting thing here is you can see
that the graph has actually stopped, right?
It's not streaming the tool invocations,
and we can go and get the state of the graph by our config.
And remember, this is actually using the check pointer
underneath the hood
for the configurable of thread_id of 1s.
It's going into the check pointer,
finding the state of that graph.
And then, it's basically getting the state of that,
yeah, with this config.
And then we can see that the snapshot.next
is basically saying, okay, great.
So, next on the list of nodes to call is the tools node
and we can actually go and do the snapshot.values.
And then, if we just have a look at the messages
and we go and get the last message,
you can see this is an AIMessage with some tool_calls
and then it's got a list of these.
So, you can see this is the call ID
and the function that wants to call with the arguments
of this JSON here.
Yeah?
And so what we can then do is we can just have
a look at the tool_calls
and you can see this is the tool_call, right?
Now, this query seems reasonable.
There's nothing to fill here.
The simplest thing the human could do
is just let the graph continue executing, right?
Maybe it's just, okay, let the graph continue.
This looks absolutely fine as a query.
So maybe the human is just saying, "Okay, continue." Right?
They're not necessarily updating
the previous state of the graph, right?
So, it's more of an approval step,
but it's important to note that there is a human
who is deciding to approve that this graph should then flow.
And so, we would then not add anything on
to the extra values as the input.
So, we just set none and then we'll use the same config
and then this will then call that tool.
So you can see now we've actually decided to call it
and we're printing the tool message.
Cool.
All right, so that's one thing can do
and we've learned how to use the interrupt
to add human-in-the-loop execution to your chatbot.
Cool.
In the next lesson, we'll look at
how you can update manually the state of your graph
and then re-execute the graph, which is another method
for using human-in-the-loop feedback.