WEBVTT

00:00.080 --> 00:01.240
Hey there Eden here.

00:01.240 --> 00:06.080
And in this video we're going to be implementing the final part of our section.

00:06.240 --> 00:10.120
So we're going to be implementing a version of Adaptive Rack.

00:10.360 --> 00:14.280
And Adaptive Rack is based on this research paper you see right here.

00:14.640 --> 00:23.040
And to be honest, it's a fancy word of simply using a question router to route our question to different

00:23.040 --> 00:23.960
rack flows.

00:24.240 --> 00:27.400
So in this video we're going to be using two rack flows.

00:27.400 --> 00:33.000
So the first one is going to be taking the route to search on the internet and then to continue downstream

00:33.040 --> 00:34.800
on the same logic we have before.

00:35.120 --> 00:40.280
And the second round is going to be to use the retrieval augmentation from our vector store.

00:40.800 --> 00:46.120
So we're first going to take the user's question, and we're going to decide whether the information

00:46.120 --> 00:49.040
is stored in the vector store to answer that question.

00:49.440 --> 00:54.320
And if it's not we're simply going to take the route to web search and answer from there.

00:54.320 --> 01:00.000
And the main thing we're going to do is to implement a question router chain, which will take the question

01:00.000 --> 01:05.040
and decide whether we're going to route it to the web search or to the retrieve node.

01:05.080 --> 01:11.920
We're going to be writing tests to this chain, and we're going to integrate it into our graph by using

01:11.960 --> 01:14.240
the set conditional entry point.

01:15.240 --> 01:16.000
All right.

01:16.000 --> 01:17.280
Let's go to the code.

01:20.760 --> 01:24.360
So let's go and create a new file under chains.

01:24.680 --> 01:27.520
And we want to call this file router.

01:29.000 --> 01:32.600
And this file will hold the implementation of our router chain.

01:33.000 --> 01:35.160
So we'll start from the imports.

01:35.160 --> 01:37.760
And let's import from typing literal.

01:38.120 --> 01:44.400
And if you're not familiar with the literal type like I was a couple of months ago, then it provides

01:44.400 --> 01:50.480
a way to specify that a variable can only take one of predefined set of values.

01:50.600 --> 01:54.320
So this is very useful for validation and type checking.

01:55.000 --> 01:55.640
All right.

01:55.640 --> 02:01.760
We want to import chat prompt template as well because we're going to use structured outputs.

02:01.760 --> 02:06.480
We want to import base model and field from the lecture library.

02:06.920 --> 02:10.520
And also let's import chat OpenAI for our LLM.

02:10.960 --> 02:15.040
Now let's define the class that we want to structure our output.

02:15.040 --> 02:19.280
So we'll call it root query which will inherit from base model.

02:19.760 --> 02:25.520
It will have only one attribute which is called data source and is going to hold only the values of

02:25.520 --> 02:26.400
vector store.

02:26.600 --> 02:31.920
In case we want to search the answer from the vector store, or it's going to hold web search.

02:31.960 --> 02:37.440
If we don't have the answer in the vector store, and we need to perform some external search with Tavileh.

02:38.120 --> 02:44.200
Now when we initialize the field object, if we put here the ellipsis here, then this means that this

02:44.200 --> 02:48.680
field will be required once we instantiate an object of this class.

02:49.160 --> 02:49.880
All right.

02:49.880 --> 02:52.680
And let's also write the description.

02:52.880 --> 02:55.720
And the description is given a user question.

02:55.720 --> 02:59.000
Choose to route it to web search or to vector store.

02:59.280 --> 03:01.320
So we finished with our Pydantic class.

03:01.320 --> 03:07.160
So now it's time to start writing our prompt and to leverage the structured output from LinkedIn.

03:07.560 --> 03:14.000
So we'll create our LLM And we'll create our LLM with structured output with that class.

03:14.160 --> 03:20.400
So basically we are binding now this pedantic class to be called as a function call like we saw earlier

03:20.400 --> 03:21.240
in this course.

03:21.360 --> 03:25.200
And what's going to be interesting here is the system call.

03:26.480 --> 03:32.440
You are an expert at routing a user question to vector store or web search.

03:35.000 --> 03:42.040
The vector store contains documents related to agents, prompt engineering, and adversarial attacks

03:42.040 --> 03:48.160
because that's what we indexed our vector stored with all those articles we ingested in the first video

03:48.160 --> 03:48.960
of this section.

03:49.960 --> 03:53.000
Use the vector store for questions on those topics.

03:53.200 --> 03:56.000
For everything else, use web search.

03:57.520 --> 04:01.160
All right, let's go now and create the prompt itself.

04:01.280 --> 04:04.400
So we'll use chat prompt template from messages.

04:04.760 --> 04:07.320
And we'll first plug in the system message.

04:07.600 --> 04:14.040
And then we're going to plug in the human message which contains the question that we're going to plug

04:14.040 --> 04:15.960
in once we get the user's question.

04:16.280 --> 04:19.320
And lastly, let's go and let's create the chain.

04:19.320 --> 04:22.760
We'll call it question router which will take the route prompt.

04:22.800 --> 04:26.840
We'll pipe it into the router and that's it.

04:26.880 --> 04:28.840
We finished with our chain.

04:30.000 --> 04:35.000
And I hope you're ready because right now we're going to be writing tests to this chain which is super

04:35.000 --> 04:37.280
important for our software hygiene.

04:38.720 --> 04:43.040
And let's go to the file tests.py.

04:44.000 --> 04:48.800
And we want now to import the router question chain.

04:49.000 --> 04:52.480
So let's simply write that string and auto import it.

04:53.560 --> 04:54.600
And let's go up.

04:54.600 --> 04:57.760
And let's just move it after we load the dot env.

04:57.800 --> 04:59.880
So we won't have any issues.

05:00.480 --> 05:01.040
All right.

05:01.040 --> 05:05.800
So now we want to import the um the route query class.

05:06.200 --> 05:07.680
And let's go up.

05:07.680 --> 05:09.480
And we can see we imported it.

05:10.240 --> 05:12.480
And now it's time to write our tests.

05:13.200 --> 05:18.880
So we're simply want to test that we're able to route it to once in the vector store and once to the

05:18.880 --> 05:19.560
web search.

05:19.920 --> 05:27.240
So the first test is going to be dev test raster to vector store, which receives nothing and returns

05:27.240 --> 05:27.640
none.

05:28.360 --> 05:31.880
And here let's put the question to be agent memory.

05:32.000 --> 05:33.680
So it should route to it.

05:34.520 --> 05:43.440
And now we want to invoke the router question chain and will end up with a route query object.

05:43.720 --> 05:48.200
So we'll call it rez which is a route query object.

05:49.920 --> 05:59.200
And it's going to be equal to the question router chain after we invoke it with the question to be agent

05:59.200 --> 05:59.840
memory.

06:02.320 --> 06:03.160
Alrighty.

06:03.520 --> 06:10.840
So let's go now and assert that the data source is going to be equals to vector store, because that's

06:10.840 --> 06:12.120
what we wrote in the prompt.

06:12.160 --> 06:14.360
Remember let's run it.

06:20.520 --> 06:22.880
And we'll wait to get a result.

06:22.880 --> 06:24.720
And we can see this test passed.

06:24.960 --> 06:25.400
Cool.

06:25.600 --> 06:31.880
So now I want to copy this and let's go instead to call it Route to Vector Store.

06:31.920 --> 06:34.120
Let's call it Route to Web Search.

06:34.560 --> 06:38.560
And our question instead of Agent Primary let's write how to make pizza.

06:39.240 --> 06:42.760
And this time this should route us to the web search node.

06:43.040 --> 06:44.080
And let's run it.

06:52.920 --> 06:54.400
And we can see it passed.

06:55.000 --> 06:58.560
And just as a sanity check, let's go and run all of our tests.

06:58.760 --> 07:04.960
And I have to admit, like every developer, seeing all of those green check marks is very satisfying.

07:05.600 --> 07:09.520
So let's wait and and see that everything passes.

07:10.040 --> 07:13.840
By the way, we can make optimizations to all of those tests.

07:13.840 --> 07:17.760
For example, we can run them concurrently because they're not dependent on each other.

07:18.040 --> 07:22.280
But um, we're not going to cover it now, maybe later in this course.

07:22.960 --> 07:23.680
All right.

07:23.680 --> 07:28.680
So all of the tests passed and we are pretty confident with our chain.

07:29.320 --> 07:30.120
All right.

07:30.120 --> 07:35.240
So let's have a look on what we're going to be implementing right now in our graph py file.

07:35.560 --> 07:38.120
Eventually this is how our graph should look like.

07:38.320 --> 07:40.320
We should start from the start node.

07:40.560 --> 07:46.680
Then we should have this conditional branch which would either go to retrieve or either go to web search

07:46.800 --> 07:49.960
depending on our router that we just wrote.

07:50.360 --> 07:56.200
And for this we're going to be introducing something new in graph, which is called a conditional entry

07:56.240 --> 08:03.040
point, which basically is a conditional edge with the first node of the entry point.

08:03.320 --> 08:06.040
So this is basically going to be our routing.

08:06.160 --> 08:10.760
And from them we've already implemented all of the rest of the graph.

08:13.000 --> 08:17.080
All right so let's go back to the graph.py file.

08:17.400 --> 08:24.200
And now we want to import the router and the router query chain from the router.py file.

08:24.360 --> 08:26.880
So this is what happens in line seven.

08:27.640 --> 08:28.160
Cool.

08:28.200 --> 08:35.160
So let's now implement the root question function, which is going to receive the state and return us

08:35.200 --> 08:38.600
a string which is going to be the next node to execute.

08:38.600 --> 08:41.080
And you can see the illustration on the right.

08:42.040 --> 08:42.640
All right.

08:42.640 --> 08:47.160
Let's add a print that we are in the root question.

08:47.680 --> 08:51.280
And let's fish out the question from our graph state.

08:51.560 --> 08:56.240
Now we'll simply run the question router chain with the invoke method.

08:56.240 --> 09:00.960
Plugging in the user's question which we fished out from the state.

09:01.520 --> 09:07.840
And the result we're going to save into a source variable, which is going to be of the type root query.

09:07.920 --> 09:14.720
Because I remind you that the question router chain returns this kind of object because we use the structured

09:14.720 --> 09:15.560
output chain.

09:16.840 --> 09:17.360
All right.

09:17.360 --> 09:19.640
So now we have the result.

09:19.680 --> 09:25.800
Now if the data source is web search we want to route it into the web search node.

09:25.800 --> 09:28.320
So we'll simply return web search.

09:28.600 --> 09:35.720
And if the answer is vector store, then we simply want to route it into the retrieve node to perform

09:35.720 --> 09:36.320
the retrieval.

09:36.320 --> 09:37.160
Augmentation.

09:39.520 --> 09:40.120
All right.

09:40.120 --> 09:42.680
So let's add the conditional entry point.

09:42.880 --> 09:47.840
And for that we want to use the function set conditional entry point.

09:49.600 --> 09:55.280
And now we want to add our route function that we just implemented.

09:56.160 --> 09:58.880
And let's add the path map.

09:58.880 --> 10:01.600
So web search is going to be mapped to web search.

10:01.600 --> 10:04.080
And retrieve is going to be mapped to retrieve.

10:04.360 --> 10:05.080
All right.

10:05.080 --> 10:07.320
So that's it for the implementation.

10:07.920 --> 10:08.600
Let's go.

10:08.600 --> 10:10.240
And let's now run the code.

10:10.240 --> 10:11.960
So I'm going to go to the main file.

10:12.120 --> 10:14.920
And let's run it with what is agent memory.

10:14.920 --> 10:19.160
So our original prompt from the earlier in this section.

10:20.200 --> 10:25.720
So we can see that we are now starting to first route the question.

10:26.600 --> 10:32.520
And now we decide to route it into the vector store and to perform the retrieval, augmentation.

10:32.720 --> 10:36.400
And from then the flow remains, as we know already.

10:37.480 --> 10:40.440
And let's wait for the final answer.

10:40.600 --> 10:43.080
And we indeed get the answer that we want.

10:43.120 --> 10:50.080
We can go to Blanc-smith, and we can check out all of the traces and all of the code that's running

10:50.680 --> 10:51.120
now.

10:51.160 --> 10:53.800
Note that we didn't add any other node here.

10:53.800 --> 10:58.400
We simply added a conditional edge from our entry point.

10:59.240 --> 11:00.640
And let's go to the code.

11:00.640 --> 11:04.600
And let's change the query to how to make pizza.

11:06.360 --> 11:07.680
And let's run it again.

11:07.720 --> 11:14.160
So now we're supposed to root into web search and to start running traveling and to continue from there.

11:18.080 --> 11:20.560
So you can see that we routed into web search.

11:20.560 --> 11:21.080
Indeed.

11:21.320 --> 11:24.360
And yeah, that's pretty much it.

11:24.400 --> 11:26.720
And we got here the answer.

11:27.880 --> 11:28.840
Alrighty.

11:28.840 --> 11:33.280
So we are pretty much done with our advanced drag flow.
