WEBVTT

00:00.360 --> 00:04.920
Let me now create a new file and I'm going to call it schemas dot p y.

00:05.880 --> 00:11.840
And this file is going to hold the pydantic classes that we want to get structured output of.

00:12.080 --> 00:18.680
And by the way when we installed Linkchain we implicitly installed Pydantic because Linkchain uses Pydantic

00:18.680 --> 00:20.640
a lot in our implementation.

00:21.360 --> 00:23.120
So just to give you a quick overview.

00:23.360 --> 00:28.680
Pydantic is a Python library for data validation using Python typings.

00:28.960 --> 00:35.520
So it automatically validates the data, converts types when possible, and provides clear error messages

00:35.520 --> 00:37.880
when the data doesn't match our descriptions.

00:37.960 --> 00:45.280
And it turns out using Pydantic objects to describe structured output for Llms is very, very effective.

00:45.480 --> 00:46.640
And we're going to see that.

00:46.640 --> 00:50.960
And we're going to discuss at the end of this video why this is happening and why this is working so

00:50.960 --> 00:51.760
well together.

00:52.240 --> 00:52.560
All right.

00:52.560 --> 00:54.240
Let me start with the imports.

00:54.280 --> 00:58.960
So let me first import the type annotation of list.

00:59.240 --> 01:01.770
And I also want to import from pedantic.

01:01.810 --> 01:08.970
The base model and field and base model is the core pedantic class that we're going to inherit from

01:09.130 --> 01:12.730
in order to create our own data models and data objects.

01:12.930 --> 01:19.690
And when we create the class that is going to inherit from base model, we get automatic validations,

01:19.690 --> 01:23.930
realization from JSON and ID auto complete.

01:23.930 --> 01:25.290
And this is very useful.

01:25.730 --> 01:33.970
Now field is a function that lets us add extra validation and metadata to our model attributes.

01:34.210 --> 01:41.570
And we can set constraints like minimum maximum maybe descriptions and default values.

01:41.570 --> 01:46.130
And this is very very useful when we want to create those data objects.

01:47.770 --> 01:49.970
All right let's go and see this in action.

01:49.970 --> 01:54.170
So let's create now a new class which will inherit from base model.

01:54.290 --> 01:56.650
And let's call this class source.

01:57.330 --> 02:05.770
Now source is going to have one attribute, which is going to be a URL, and its type is going to be

02:05.770 --> 02:06.330
string.

02:06.730 --> 02:13.210
Now I'm using now the field function to add a description to mention that this is the URL of the source.

02:13.410 --> 02:20.290
Now notice here I also wrote a short description that this is the schema for a source used by the agent.

02:20.450 --> 02:26.410
So you can imagine that we are going to propagate all of this information eventually to an LLM.

02:26.570 --> 02:29.730
So this is what is going to help us get structured output.

02:30.130 --> 02:30.610
All right.

02:30.610 --> 02:36.410
Let's now create our second class which is going to be our agent response class.

02:36.490 --> 02:39.930
So it's also going to inherit from the base model.

02:40.130 --> 02:45.890
And here the description is going to be that this is the schema for the agent response with answer and

02:45.890 --> 02:46.530
sources.

02:46.570 --> 02:47.130
Right.

02:47.170 --> 02:50.610
So we want now the agent to return us two fields.

02:50.610 --> 02:54.410
One is going to be the field answer which is going to be a string.

02:54.490 --> 02:59.060
And here you can see we added the description of this is the agent's answer to the query.

02:59.260 --> 03:05.420
And if we'll take our example from before, where we asked for job postings for AI engineers in Lake

03:05.420 --> 03:06.460
Chain in the Bay area.

03:06.700 --> 03:09.700
So the answer of the agent should be here in this field.

03:09.700 --> 03:14.620
So it's going to be here the list of three job openings that we looked for.

03:15.180 --> 03:19.020
So the second thing is it's going to have an attribute of sources.

03:19.300 --> 03:20.900
Now notice here the type hinting.

03:20.900 --> 03:25.940
This is going to be a list of the source class that we created before.

03:26.300 --> 03:31.420
And here in the description is going to be the list of sources used to generate the answer.

03:32.180 --> 03:39.460
And the default factory list tells pedantic to call the list function to create a new empty list for

03:39.460 --> 03:42.100
each instance where no value is provided.

03:42.100 --> 03:46.500
So this is going to ensure that all of the instances get their own independent lists.

03:48.380 --> 03:48.780
Cool.

03:48.780 --> 03:52.060
So I also wanted to add to this example a nested class.

03:52.060 --> 03:56.700
So notice the agent response holds within it the class of source.

03:56.740 --> 04:01.870
So we have here two pedantic objects, which we compose one on another.

04:02.230 --> 04:10.110
And LM these days can easily handle this kind of composition and to output those kinds of nested objects.

04:10.230 --> 04:13.110
And a fun fact, this wasn't always the case.

04:13.310 --> 04:18.550
So in the past LMS, even top tier LMS struggled with this task.

04:18.590 --> 04:20.070
However, this is not the case today.

04:20.110 --> 04:21.950
We can easily do those kinds of things.

04:22.670 --> 04:25.950
All right so let's go now and format the code.

04:28.910 --> 04:30.190
Let me write here black.

04:31.190 --> 04:33.190
And I sort all right.

04:33.430 --> 04:41.230
So all of this information here on the answers that we expect to do, they need somehow to be plugged

04:41.230 --> 04:44.590
into the prompt that we send the large language model.

04:44.750 --> 04:48.830
So somewhere somebody needs to plug this information in.

04:49.110 --> 04:51.350
So this is what we're going to do now.

04:51.510 --> 04:55.990
So let's go and create a new file and let's call it prompt py.

04:56.230 --> 04:59.310
And here we're going to write our own special prompt.

04:59.710 --> 05:03.470
I'm going to call it the react prompt with format instructions.

05:03.630 --> 05:08.150
And this prompt is going to be the react prompt that we already saw from before.

05:08.350 --> 05:13.310
But we're going to transform it a bit and add here our format instructions.

05:13.310 --> 05:18.390
So the information that we want to give the LLM for us to get a structured response.

05:18.870 --> 05:23.910
So let me go to the prompt in the prompt tab and let me copy it and paste it.

05:27.550 --> 05:27.950
All right.

05:27.950 --> 05:30.510
So this is the exact prompt that we used before.

05:31.030 --> 05:34.670
And now we want to do a bit of prompt engineering.

05:34.790 --> 05:37.870
And here in this final answer clause here.

05:37.870 --> 05:40.310
So I'm going to edit this prompt a bit.

05:40.350 --> 05:47.830
And here I want to tell the LLM to use the format instructions that I'm going to plug it in in the final

05:47.830 --> 05:48.390
answer.

05:48.710 --> 05:54.470
And let me now read this line, the final answer to the original input question formatted according

05:54.510 --> 05:59.240
to the format Instructions and then the placeholder format instructions.

05:59.240 --> 06:04.720
And you can probably guess that we're going to plug in some dynamic value of the objects that we want

06:04.760 --> 06:06.040
our LM to structure.

06:06.680 --> 06:07.280
Alrighty.

06:07.280 --> 06:09.840
So let's go now to the main file.

06:10.080 --> 06:14.200
And we want to use now this prompt in our reasoning chain.

06:16.440 --> 06:19.400
So we want to get rid of this prompt here.

06:19.400 --> 06:23.360
And here we want to put our new and revised prompt that we just wrote here.

06:23.960 --> 06:24.480
Cool.

06:24.480 --> 06:29.600
So let me go now and let me import some classes that we'll be needing here.

06:30.560 --> 06:32.520
So let's go to the top of the file.

06:33.160 --> 06:41.240
And I'm going to paste in those couple of imports here I want to import the pedantic output parser.

06:41.680 --> 06:44.720
And let's have a sneak peek on the implementation here.

06:45.160 --> 06:49.240
And this class is a link chain output parser.

06:49.520 --> 06:53.240
And it's going to take the response that we get from the LM.

06:53.520 --> 06:59.450
And it's going to parse that response into a Pydantic model object.

06:59.570 --> 07:02.890
Now, we're not going to dive into the implementation here.

07:02.970 --> 07:05.090
It's really some ugly parsing here.

07:05.090 --> 07:07.570
And really nobody wants to see this.

07:08.090 --> 07:16.410
And the assumption here is that the response which the LM is going to return is going to be a JSON response,

07:16.490 --> 07:20.890
which is going to be in the Pydantic object format.

07:21.090 --> 07:28.330
So this class shouldn't have any hard time doing it, because we're leaving all of the output and structuring

07:28.330 --> 07:29.730
the data to the LM.

07:29.770 --> 07:32.690
So the LM is going to do all of the heavy lifting.

07:32.810 --> 07:35.770
And this class is simply going to take the response.

07:35.770 --> 07:38.890
And it's going to format it nicely as a Pydantic object.

07:38.930 --> 07:39.610
Alrighty.

07:39.610 --> 07:41.330
So let's go back to the imports.

07:41.330 --> 07:46.090
And we also imported prompt template which we know from previous lessons.

07:46.330 --> 07:50.370
And we imported a class which is called Runnable Lambda.

07:50.570 --> 07:53.450
Now this is a part of the link chain expression language.

07:53.450 --> 07:56.530
And we'll be seeing and learning it very, very soon.

07:56.530 --> 07:58.650
So I don't want to talk about it right now.

07:59.290 --> 08:06.610
And let's go and import the prompt that we wrote and the agent response class from our schema.py file.

08:07.130 --> 08:11.050
Let me just see that the imports are referring to the right code.

08:11.610 --> 08:14.410
So let me go here and let me click here.

08:14.770 --> 08:16.970
And yeah so this seems to be right.

08:16.970 --> 08:17.610
Imports.

08:18.250 --> 08:18.850
All right.

08:18.850 --> 08:22.090
So let me go now and paste this line over here.

08:22.170 --> 08:26.970
And this is going to be the output parser that we're going to create.

08:27.010 --> 08:30.210
It's going to be an object of the pedantic output parser.

08:30.450 --> 08:35.330
And it's going to receive as an input the agent response object.

08:35.450 --> 08:40.970
So we want the output parser to parse objects of the type agent response.

08:42.090 --> 08:46.370
And now I want to create the react prompt with format instructions.

08:46.370 --> 08:50.810
So it's going to be an object of the prompt template.

08:52.090 --> 08:55.900
And the template is going to be what we wrote in the prompt.py file.

08:55.900 --> 08:59.980
So it's going to be the value of the react prompt with format instructions.

09:00.260 --> 09:03.460
So this is now the new template we're going to be using.

09:03.740 --> 09:08.340
So it's going to have the same input variables like before.

09:08.860 --> 09:12.060
So it's going to have a tool names.

09:12.100 --> 09:14.940
It's going to have the input the agent scratchpad.

09:15.180 --> 09:21.140
And by the way notice here I forgot actually to write the tools input variable.

09:21.700 --> 09:24.700
And funny enough this is still going to work.

09:24.700 --> 09:26.500
And I've debugged it for a bit.

09:26.500 --> 09:34.620
And it turns out that in the prompt template logic the login object, they go through the prompt and

09:34.660 --> 09:41.180
they extract everything between those curly brackets and they implicitly add them as input variables.

09:41.340 --> 09:47.820
So this is why the missing tools input variable, which is missing here, is still going to be populated

09:47.820 --> 09:48.420
over here.

09:48.780 --> 09:54.270
And I highly suggest you do those kinds of exercises where if something doesn't add up, simply take

09:54.270 --> 09:56.150
a look at the implementation.

09:56.150 --> 09:59.030
You will have all the answers there anyways.

09:59.030 --> 10:01.510
So up until now we did everything that we know.

10:01.510 --> 10:05.630
We populated the prompt template, but there is something else we need to do here.

10:05.630 --> 10:08.670
So we need to provide those format instructions here.

10:08.670 --> 10:15.350
So we're going to do that with the partial method which is going to allow us to partially format a form

10:15.350 --> 10:21.350
template by pre-filling some of its variables to create a new prompt template that only requires the

10:21.350 --> 10:23.790
remaining variables to be provided later.

10:23.790 --> 10:30.870
And this is useful when we have some variables available early, but others later in the workflow.

10:31.310 --> 10:37.990
So I want to partially populate first the format instructions placeholder, and the value I'm going

10:37.990 --> 10:40.990
to give it is I'm going to give it the output parser.

10:41.030 --> 10:45.230
It has a function or a method which is called get format instructions.

10:45.630 --> 10:49.390
And it's going to get us the format instructions where we want to plug in.

10:49.390 --> 10:52.720
And we're reviewing those format instructions A very very soon.

10:53.280 --> 10:59.120
And anyways, last thing I want to do is to switch the prompt when creating the reasoning chain with

10:59.120 --> 11:01.600
the prompt with format instructions.

11:02.000 --> 11:07.080
And let me run this in debug, because I want to show you the get format instructions here.

11:09.720 --> 11:15.360
So once it's going to get to this breakpoints, let me go and let me copy this expression over here.

11:15.640 --> 11:18.480
Let's paste it here in the debug console.

11:18.680 --> 11:22.000
And here we can see those are the formatting instructions.

11:22.800 --> 11:27.720
The output should be formatted as a JSON instance that conforms to the JSON schema below.

11:28.000 --> 11:30.280
And in example blah blah blah blah.

11:30.560 --> 11:36.440
And at the bottom here we're going to have all the metadata of our Pydantic objects that we wrote.

11:36.640 --> 11:40.600
So eventually this is going to be propagated to the LLM.

11:40.800 --> 11:48.480
And the LLM is going to return us a JSON response that is adhering here to those fields and to those

11:48.480 --> 11:49.080
requirements.

11:49.080 --> 11:50.080
We write it over here.

11:50.200 --> 11:52.400
So this is actually very smart.

11:52.400 --> 11:55.160
And LMS now are very good at doing those things.

11:55.160 --> 12:00.880
So the output parser later is going to have a very easy time taking that JSON and turning that JSON

12:00.880 --> 12:02.640
into a Pydantic object.

12:03.120 --> 12:03.520
All right.

12:03.520 --> 12:08.080
So now let me run everything before we even go and parse it.

12:08.080 --> 12:12.600
And I want to show you the response that we eventually get from the agent here.

12:12.960 --> 12:15.120
So let's go and click run.

12:19.160 --> 12:21.200
And let me fast forward everything here.

12:26.600 --> 12:27.000
All right.

12:27.000 --> 12:28.480
So we got here a response.

12:28.720 --> 12:35.800
Now notice here that in the output field here we can see that we actually have here a JSON which has

12:35.800 --> 12:36.800
the answer key.

12:36.960 --> 12:39.800
And it also has the sources key with the URLs.

12:40.200 --> 12:43.240
So the LLM did what it was supposed to do.

12:43.720 --> 12:44.680
So let me go.

12:44.680 --> 12:50.810
And now put a breakpoint here and run it in debug and let me go and fast forward it again.

12:54.370 --> 12:55.970
So we got the result.

12:55.970 --> 12:57.850
And here is the output key.

12:58.130 --> 13:00.530
And this is here a JSON string.

13:01.050 --> 13:04.330
So let me go and even prove it to you.

13:04.330 --> 13:06.850
And let's go to the debug console.

13:07.010 --> 13:08.690
And let's go and check the types.

13:09.410 --> 13:14.170
So the type of a result is going to be a dictionary.

13:16.090 --> 13:20.970
And the type of result in the key of output is going to be a string.

13:21.090 --> 13:22.610
And this is the JSON string.

13:22.930 --> 13:26.890
And it has all of the properties of the Pydantic object that we want.

13:27.290 --> 13:32.570
So now let me show you how simple it is to create this Pydantic object.

13:32.690 --> 13:35.810
So I'm going to use the output parser of Linkchain.

13:35.970 --> 13:38.810
And it has a method which is called parse.

13:38.970 --> 13:42.050
And I'm going to give it this result here in the output key.

13:42.050 --> 13:47.050
And this is going to parse it into the answer agent object that we want to.

13:54.300 --> 14:02.460
And we can see now the result is of type agent response where we have the answer as the first attribute.

14:02.620 --> 14:09.460
And we also have the sources attribute which is going to be a list of sources.

14:09.500 --> 14:11.380
So this is a very cool.

14:11.580 --> 14:16.380
And now we actually have an object that we can work with programmatically.

14:16.500 --> 14:22.980
So if we want we can downstream it into an application maybe to a front end or expose it via API to

14:23.020 --> 14:23.740
serialize it.

14:23.740 --> 14:26.860
So we have lots of flexibility of what we can do here.

14:26.860 --> 14:32.900
And we can build really robust software leveraging Llms with these kinds of functionality.

14:33.020 --> 14:36.100
So this is a very important part of Linkchain.

14:36.100 --> 14:37.460
And let me show you what I mean.

14:37.460 --> 14:44.980
So let's go and take the parsed object and let's go for example in access the answer here.

14:44.980 --> 14:46.460
So this is the string answer.

14:46.700 --> 14:49.820
Let's go now and check out the sources.

14:50.260 --> 14:54.300
And this is here a list of sources where each source has a URL attribute.

14:54.300 --> 14:56.460
So we can check it for example like this.

14:56.780 --> 15:01.980
This is the URL of one source and we can continue to iterate through the URLs.

15:02.020 --> 15:07.820
Let me just open it and we can see that it opens a valid LinkedIn page.

15:09.180 --> 15:09.700
Hey there.

15:09.740 --> 15:10.980
Ethan here popping out.

15:10.980 --> 15:13.540
And yeah, in case you're wondering, those are new glasses.

15:13.580 --> 15:16.820
Turns out I need glasses in order to review the screen.

15:16.940 --> 15:18.980
You can see much better now, I can tell you that.

15:19.420 --> 15:24.500
And anyways, by now, I hope you have a better understanding of what is output parsing.

15:24.500 --> 15:25.580
Why do we need it?

15:25.660 --> 15:26.740
How does it work?

15:26.780 --> 15:28.260
It all boils down to a prompt.

15:28.260 --> 15:29.260
We send the LM.

15:29.300 --> 15:34.900
The LM structures the output according to our instructions, and then Linkchain simply takes it and

15:34.900 --> 15:38.100
simply formats it nicely to our Pydantic object.

15:38.140 --> 15:38.660
Right?

15:38.700 --> 15:46.380
So in the next video, I want to use the chain expression language to take the output of our agent and

15:46.380 --> 15:47.780
to format it nicely.
