WEBVTT

00:00.150 --> 00:00.983
-: Hey, welcome back.

00:00.983 --> 00:02.250
And in this video, we're gonna have a look at

00:02.250 --> 00:04.800
how you can implement human-in-the-loop editing

00:04.800 --> 00:06.033
inside of LangGraph.

00:06.870 --> 00:08.760
Previously, we were looking at persistence

00:08.760 --> 00:10.350
and how we could use the thread ID

00:10.350 --> 00:14.760
to specifically load the same checkpointed memory version

00:14.760 --> 00:16.770
of a LangGraph state.

00:16.770 --> 00:17.970
In this video, we're gonna have a look

00:17.970 --> 00:20.250
at how we can implement human feedback

00:20.250 --> 00:23.700
to update a graph in real time.

00:23.700 --> 00:25.440
We're gonna install a couple of different packages.

00:25.440 --> 00:27.960
We're gonna run your imports

00:27.960 --> 00:30.090
and we're also gonna add in the Tavily key.

00:30.090 --> 00:32.690
So, I'm just gonna go and add my Tavily key back in.

00:33.780 --> 00:35.820
Now, human-in-the-loop is a way

00:35.820 --> 00:40.260
where we can basically at some point in the LangGraph flow,

00:40.260 --> 00:42.930
decide that we want a human to either approve

00:42.930 --> 00:45.210
or go and edit part of the graph

00:45.210 --> 00:47.270
or the steps that have been taken before.

00:47.270 --> 00:51.930
And we use this in LangGraph

00:51.930 --> 00:53.370
by using this concept called

00:53.370 --> 00:54.900
an interrupt_before functionality

00:54.900 --> 00:57.930
to always break before a specific node.

00:57.930 --> 01:01.380
And then the other important point is that

01:01.380 --> 01:04.320
you really need to, once you've then updated

01:04.320 --> 01:05.768
this state of the graph,

01:05.768 --> 01:09.750
make sure that you enter the same node that you came from.

01:09.750 --> 01:12.420
So if you can think of it like maybe

01:12.420 --> 01:15.090
before you're going onto this node here,

01:15.090 --> 01:17.670
you update the state of the graph.

01:17.670 --> 01:19.260
Well, you probably want to make sure

01:19.260 --> 01:20.910
that when you updated the state

01:20.910 --> 01:22.560
and you came back into the graph,

01:22.560 --> 01:24.840
so if we're taking away these bits here

01:24.840 --> 01:28.440
after we've updated the state of the graph,

01:28.440 --> 01:32.610
we would then want to reenter specifically on this node

01:32.610 --> 01:35.100
and not other parts of the state machine, right?

01:35.100 --> 01:37.353
So, there could be various nodes upstream,

01:38.190 --> 01:42.900
and maybe this node here is the human-in-the-loop node.

01:44.040 --> 01:46.680
And the problem that we've got is if we update

01:46.680 --> 01:48.150
the state of the node,

01:48.150 --> 01:50.610
we don't necessarily want to go anywhere

01:50.610 --> 01:51.510
in the state machine.

01:51.510 --> 01:55.020
We want to specifically go to this node

01:55.020 --> 01:56.520
with the human-in-the-loops feedback

01:56.520 --> 01:59.310
and then go onwards into the right node

01:59.310 --> 02:00.990
via a different edge.

02:00.990 --> 02:02.850
And so, that's kind of the important point

02:02.850 --> 02:06.000
around the interrupt_before is in here,

02:06.000 --> 02:07.990
you'd have an interrupt_before.

02:09.030 --> 02:11.760
So, from this node to that node,

02:11.760 --> 02:13.530
you're going to have a interrupt_before.

02:13.530 --> 02:16.140
And then just before you get into this node,

02:16.140 --> 02:20.250
what's gonna happen is the graph is interrupted, yeah?

02:20.250 --> 02:22.470
So it's stopped, right? The graph is stopped.

02:22.470 --> 02:26.310
The second thing that happens is we manually ask

02:26.310 --> 02:30.000
a human to update the graph

02:30.000 --> 02:33.700
and then we do a reentry into the graph

02:35.610 --> 02:38.280
from the node of our choosing, right?

02:38.280 --> 02:39.690
And in this specific case,

02:39.690 --> 02:43.530
we want to reenter the graph from this node here, yeah?

02:43.530 --> 02:44.880
So there's a couple of different points here

02:44.880 --> 02:46.890
that we're gonna talk on and I just thought we should have

02:46.890 --> 02:49.860
a look at that visually before going on.

02:49.860 --> 02:52.860
So, this interrupt_before is gonna be very important

02:52.860 --> 02:54.840
in LangGraph to make sure that we don't flow

02:54.840 --> 02:56.430
down to these other nodes

02:56.430 --> 02:59.040
before asking for feedback from a human.

02:59.040 --> 03:00.300
Cool.

03:00.300 --> 03:01.860
All right, so we've got pretty much the same code

03:01.860 --> 03:04.740
that we had in the previous persistence lesson.

03:04.740 --> 03:06.690
You've got that Tavily search results.

03:06.690 --> 03:07.950
You have the add_messages.

03:07.950 --> 03:11.040
You've got your SQLite check pointer

03:11.040 --> 03:13.193
and you've got your StateGraph and your ToolNode.

03:14.310 --> 03:15.960
So, all of this code is exactly the same.

03:15.960 --> 03:16.830
I'm not gonna go through it

03:16.830 --> 03:18.930
because it is pretty much exactly the same.

03:18.930 --> 03:21.660
But one thing that we are doing, which is a new thing,

03:21.660 --> 03:24.570
is you can see we're interrupting before tools.

03:24.570 --> 03:27.810
You can also interrupt__after__actions, as well.

03:27.810 --> 03:31.470
So, you could interrupt after a specific tool is called.

03:31.470 --> 03:34.620
And you can see we've got some some user input.

03:34.620 --> 03:35.970
So, "I'm learning LangGraph.

03:35.970 --> 03:37.798
Could you do some research on,"

03:37.798 --> 03:39.325
and we are using the thread_id of 1

03:39.325 --> 03:40.620
for the check pointer.

03:40.620 --> 03:43.590
Then when we stream, we pass in that runnable config

03:43.590 --> 03:45.360
and we're also passing in the users,

03:45.360 --> 03:48.510
and, you know, the user message with that input

03:48.510 --> 03:49.680
and we're just streaming that out.

03:49.680 --> 03:52.440
And so, if I see the hit, so let me just go make sure

03:52.440 --> 03:55.200
that graph is compiled

03:55.200 --> 03:56.407
and then when I run this, it says,

03:56.407 --> 03:58.530
"I'm learning about LangGraph."

03:58.530 --> 04:02.490
Then it decides that it needs to call the Tavily search tool

04:02.490 --> 04:05.160
and it's got this tool call ID with these args.

04:05.160 --> 04:07.200
Now, the interesting thing here is you can see

04:07.200 --> 04:09.330
that the graph has actually stopped, right?

04:09.330 --> 04:12.870
It's not streaming the tool invocations,

04:12.870 --> 04:16.890
and we can go and get the state of the graph by our config.

04:16.890 --> 04:20.340
And remember, this is actually using the check pointer

04:20.340 --> 04:21.510
underneath the hood

04:21.510 --> 04:23.970
for the configurable of thread_id of 1s.

04:23.970 --> 04:25.500
It's going into the check pointer,

04:25.500 --> 04:28.080
finding the state of that graph.

04:28.080 --> 04:31.350
And then, it's basically getting the state of that,

04:31.350 --> 04:33.120
yeah, with this config.

04:33.120 --> 04:35.820
And then we can see that the snapshot.next

04:35.820 --> 04:38.490
is basically saying, okay, great.

04:38.490 --> 04:43.290
So, next on the list of nodes to call is the tools node

04:43.290 --> 04:47.190
and we can actually go and do the snapshot.values.

04:47.190 --> 04:49.860
And then, if we just have a look at the messages

04:49.860 --> 04:52.710
and we go and get the last message,

04:52.710 --> 04:56.670
you can see this is an AIMessage with some tool_calls

04:56.670 --> 04:57.960
and then it's got a list of these.

04:57.960 --> 04:59.550
So, you can see this is the call ID

04:59.550 --> 05:02.430
and the function that wants to call with the arguments

05:02.430 --> 05:04.950
of this JSON here.

05:04.950 --> 05:05.783
Yeah?

05:05.783 --> 05:08.190
And so what we can then do is we can just have

05:08.190 --> 05:10.080
a look at the tool_calls

05:10.080 --> 05:13.050
and you can see this is the tool_call, right?

05:13.050 --> 05:14.670
Now, this query seems reasonable.

05:14.670 --> 05:15.570
There's nothing to fill here.

05:15.570 --> 05:16.950
The simplest thing the human could do

05:16.950 --> 05:20.340
is just let the graph continue executing, right?

05:20.340 --> 05:23.610
Maybe it's just, okay, let the graph continue.

05:23.610 --> 05:25.530
This looks absolutely fine as a query.

05:25.530 --> 05:28.470
So maybe the human is just saying, "Okay, continue." Right?

05:28.470 --> 05:30.900
They're not necessarily updating

05:30.900 --> 05:32.400
the previous state of the graph, right?

05:32.400 --> 05:34.590
So, it's more of an approval step,

05:34.590 --> 05:37.110
but it's important to note that there is a human

05:37.110 --> 05:40.230
who is deciding to approve that this graph should then flow.

05:40.230 --> 05:43.069
And so, we would then not add anything on

05:43.069 --> 05:45.150
to the extra values as the input.

05:45.150 --> 05:48.510
So, we just set none and then we'll use the same config

05:48.510 --> 05:50.790
and then this will then call that tool.

05:50.790 --> 05:52.800
So you can see now we've actually decided to call it

05:52.800 --> 05:54.600
and we're printing the tool message.

05:55.620 --> 05:56.453
Cool.

05:56.453 --> 05:57.990
All right, so that's one thing can do

05:57.990 --> 06:01.200
and we've learned how to use the interrupt

06:01.200 --> 06:05.340
to add human-in-the-loop execution to your chatbot.

06:05.340 --> 06:06.360
Cool.

06:06.360 --> 06:07.650
In the next lesson, we'll look at

06:07.650 --> 06:11.550
how you can update manually the state of your graph

06:11.550 --> 06:14.820
and then re-execute the graph, which is another method

06:14.820 --> 06:16.670
for using human-in-the-loop feedback.
