Alrighty, let me go and tag the solution as this is the solution without link chain expression language.
And it's the simple function based approach.
And let's now go and implement the link chain expression language version of our Rag retrieval.
So to the link chain expression language version let's go and add another import.
So I imported the str output parser that we have got to know in previous sections.
And let's go now and also import something which is called a runnable pass through.
And as it name suggests, it's a runnable that is going to let its input pass through when it's invoked.
So it's not going to change it.
So let me go to its actual implementation.
So right now we are actually going to the source code of the Lang chain ecosystem.
And we can see that this runnable actually behaves almost exactly like the identity function.
So it does not change the input, except that it has the ability to be configured such that we add additional
keys to the output.
If the input is a dictionary.
And this is going to be very helpful for us, and we'll see it later in this video.
And I want also to import item Gator, which is a Python utility function from the operator module that
creates a callable object to fetch items from an object using indexing, we can actually use lambda
functions to do so, but it's more convenient to do it with item gator.
So we're going to also use this later.
So I'm going to create a new function.
And this function is going to be called create retrieval chain with link chain expression language.
Now notice that this function does not receive any arguments.
And the reason for this is that this function is going to eventually return us a length chain chain.
So the chain is going to be a length chain runnable.
So we can go and use the invoke method on what this function will return.
So this is why this function does not receive any input.
The input to the chain is going to be the input to the invoke function, like we know from previous
videos of this course.
And we're going to see this very very soon after we implement this function.
All right.
So here I listed all of the advantages of using the length chain expression language.
So you're going to see that it's going to be much more declarative and composable.
So we can go and use it later on other linked chain components.
And because it uses a runnable interface then we have streaming support, async support, batch processing
support and type safety.
So this is very very cool.
But I think the most important advantage is that we get much better observability with lamb-smith.
Once we have a runnable object, once we bind everything under a LinkedIn chain.
Going to see this when we trace everything with Len Smith.
All right.
Let's go and start implementing this function.
So I'm going to create a variable called a retrieval chain.
And this is what's going to be the return value from this function.
So let's go and return it.
Right now let's start writing this retrieval chain.
And I pasted this three piping of the object.
Now those parts of the chain correspond to the original implementation to steps three, four and five
here.
So I piped the prompt template to the LM and the lm to the str output parser.
So the prompt template is going to be the prompt template of the prompt that we wrote with the user's
question and with the retrieve context, so that we're going to pipe into the LM.
So this means we're going to invoke the LM with this input.
And once we get a response from the LM, we're going to use the str output parser simply to access the
dot content key of the response here.
So what we see so far is stuff that we already did in previous sections of this course.
So now the question is how do we get to populate this prompt template with the context field.
And with the question field, just like we did in the naive implementation in steps one, two and three.
So we want somehow to invoke the retriever then to pipe the result of the retriever with the retrieved
documents to the format documents function in that to pipe to the prompt template.
So eventually the format documents is going to return us a string.
And this is going to be piped to the prompt template.
And once we have that, everything should work here.
Hey there.
Ethan here.
Popping out.
I hope you're enjoying the course.
I just want to give you a quick heads up on the next couple of minutes.
So the next part is a bit tricky.
It's nothing complicated.
It's just a lot of syntactic sugar with the link chain expression language.
So I have to tell you that from my personal experience, it took me a while to understand the next couple
of concepts.
However, once you understand them and I hope you will by the end of this video, you have a much deeper
understanding on LinkedIn and how link chain is actually working under the hood.
So I just want to give you a quick heads up before we continue.
So it's okay if you don't understand everything, right from the first viewing of this video.
So I do recommend watching this video a couple of times from the beginning to the end, until you finally
understand what's happening and what's happening under the hood, However, we have a couple of problems
right now.
So I remind you, the format docs is a function.
It's not a long chain runnable.
It doesn't have the invoke method.
And even if you try for yourself, try to invoke this function, you will get an error.
But I promise you, we're going to run this and this is going to work.
Because when we use regular Python functions in a link chain expression language, chain link chain
automatically converts those regular Python functions into runnable lambdas.
So when we write something like retrieval pipe format docs, pipe to the prompt template under the hood
link chain automatically converts the format docs into a runnable lambda that is going to run the format
doc.
And this runnable lambda is a runnable, and it adheres to the runnable interface.
So we can invoke it.
We can stream with it, we can batch process with it.
So it's really going to fix this issue.
I've discussed now.
Cool.
So now we need to solve another issue.
And I promise you this is the last issue we're going to be solving today.
So the prompt template I remind you it needs to receive two arguments.
It needs to receive the question of the user.
And it needs to also receive the context which is a string.
So right now the output of this part over here is not going to do this for us.
So we need to find some kind of way to take this output and somehow to attribute it to the key of context.
So the result of this, which is going to go into the prompt template, is going to be under the context
key when we invoke the prompt template.
So we are going to take this and we are going to wrap it under a runnable pass through, and we are
going to use the assign method.
So the gist of it is that runnable passthrough dot assign creates a new dictionary that is going to
combine the original input with the new computed field that we're going to explicitly mention.
And here the input field is going to be the question and what is pinecone.
Because this is eventually how we're going to be executing and running this chain.
Because we're using the runnable pass through.
Then the input is not going to change.
So the output of it still is going to have a dictionary of the key question with the value of what is
pinecone.
However, now we want to add to this dictionary new key with the key of context and the value of this
chain right over here.
So this is now a link chain chain which is going to be invoked.
So we have the retrieval and the format documents that we already know.
And we have another edition of the item getter, which we called with the value of question.
So this is equivalent of using a lambda function like this.
And it simply pulls out just the question string.
So that's all that it's doing.
Okay.
So I know this is a lot to digest.
So I want to reiterate on it one more time.
And this is okay.
If you want to watch this video one more time.
For me it took also a while to understand this.
So I want to talk about this step of the pipeline right here.
So the input for it is going to be a dictionary with the key of question.
And it's going to have some value.
So this is going to be the input when we invoke now this a chain.
And this is the first part of the chain.
So we see that the first step is wrapped entirely with a runnable passthrough.
So this means we do not change the input.
So the result of invoking this is still going to have a dictionary with the key of question.
And it's going to have this value inside it.
And because we used the assign method and we gave it a keyword argument of context.
So this means we are adding now to this output dictionary.
Another key.
And the key is going to be context.
And the value of the key is going to be this chain running over here.
I remind you we're still inside a runnable passthrough.
So this means that the input for running this chain this sub chain is going to be the original input.
So when we use the item getter A with question it's simply going to fish out the question value.
And it's going to pipe this string into the retriever.
And then the output to the format documents and so on and so on.
So after running the runnable path through, step over here with this input.
The output of this step is going to be a dictionary with the key of question and the key of context.
And the context now is going to be having the relevant context after we retrieve the documents and format
them nicely.
And this is eventually what we're going to be piping to the prompt template.
So this is how everything is playing nicely with each other.
I know this was a bit overwhelming, so I think an easy way to also understand it is to compare our
naive and simple version to this link chain expression language version here.
Okay, so try to think of which step is existing where.
Hey there Eden here popping out again.
I told you this part was tricky, so if you didn't fully understand it, Please don't freak out.
Trust me.
Try to have a break.
Maybe go drink some coffee, then return.
And then watch this video again.
And this should be much clearer when you're going to be writing the code and running it and seeing it
for yourself.
So let's continue.
Cool.
So let's go now and run this baby and see that everything is working and we're getting a nice result.
So I'm going to write in the main part over here that we are going to run the linked chain expression
language implementation.
It has a bunch of advantages which we are going to see after we run it.
And in order to run everything, we simply need to call the create retrieval chain function.
This is going to return us with a long chain chain which is a runnable.
So it has the invoke method.
So the chain with LCL variable we can actually go and invoke it with the input of the dictionary with
the key of question and the user's query.
Let's go now and print the result.
And let's go now and run everything and let's see what we get.
So let me go and show the output.
All right.
So now it's running in the chain without LCL.
After it finishes it's going to run with the LCL.
All right.
And we can see the answer, which is very similar to the answer of our original implementation here.
So the results are the same.
The implementation is different.
And now I want to show you how better it is as far as observability and tracing.
So I want to go and switch to Laxmi.
And I want to show you the trace of this chain execution.
So I'm going to also link it in the videos resources.
But we can see all the moving parts here of this Rag retrieval pipeline are in one place are under a
link chain runnable sequence because this is eventually a chain.
So everything here is under one trace, which is super, super convenient to debug and to analyze.
Later we can see the original question and we can see the final output of this chain, which is this
raw text.
Over here we can see how long everything took.
We can see what was the bottleneck, what took most of the time.
And this is why it's so convenient to structure everything under a chain here.
All right.
Now let me show you the tricky part I told you about with the runnable a assign.
And here you can see we are invoking it with the assigning of the context.
We can see the input is a dictionary with the key of question, and the output is a dictionary with
the key of context and the relevant context, and it also has the key of question.
The original input.
All right.
Now this entire runnable path through has inside it three steps.
We have the item getter, the retrieval and the format docs.
So let's start with the item getter.
So here it's simply a runnable lambda underneath the hood which takes the input dictionary and return
us the question itself.
So this is what's happening here.
Now this is going to be piped to the retriever.
So let's go check out the retriever.
We can see now the retriever gets the user's query and it returns the relevant documents which will
limit it to three.
And then we go and we pipe this into the format docs.
And the output of this subchain, we simply want to assign under the context key of the output.
So this is what's happening in this overall chain here.
So now we take now this dictionary which has the key of context with the relevant context and the original
question.
And we simply go and pipe it to the chat prompt template.
And then we go and pipe the chat prompt template into the LLM.
And this is what's going to generate the answer here.
So we have all of the augmented prompt with the relevant context.
And finally we parse out the output.
And you can go and get the code for this video.
If you go to the repository in the branch project slash rag gist.
Let's go here to the commits.
And here you can see this commit add HCl based retrieval chain.
Here you can see all the code that we added here in this video.