back to indexLangChain Expression Language (LCEL)

Chapters
0:0 LangChain Expression Language
0:58 Traditional Chains in LangChain
3:2 LangChain LCEL
3:55 LCEL Pipe Operator
9:9 LangChain RunnableLambda
12:41 LCEL Runnable Parallel and Passthrough
00:00:00.000 |
In this chapter, we're going to be taking a look at Lanchain's expression language. 00:00:03.820 |
We'll be looking at the runnables, the serializable and parallel of those, the runnable pass-through, 00:00:10.340 |
and essentially how we use LCL in its full capacity. 00:00:16.660 |
Now, to do that well, what I want to do is actually start by looking at the traditional approach to building chains in Lanchain. 00:00:27.680 |
So, to do that, we're going to go over to the LCL chapter and open that CoLab. 00:00:42.940 |
That's because later on, as you'll see, we're going to be using this as an example of the parallel capabilities in LCL. 00:00:52.280 |
If you want to use Lansmith, you just need to add in your Lanchain API key. 00:00:56.860 |
Okay, and then let's, okay, so now let's dive into the traditional approach to chains in Lanchain. 00:01:04.680 |
So, the Lanchain, I think it's probably one of the first things introduced in Lanchain, if I'm not wrong. 00:01:12.540 |
So, let's take it to prompt and feed it into an LLM and that's it. 00:01:17.520 |
You can also, you can add like output passing to that as well, but that's optional. 00:01:26.580 |
So, what that might look like is we have, for example, this prompt template here. 00:01:37.540 |
We'd set up as we usually do with the prompt templates, as we've seen before. 00:01:46.440 |
We'd need our OpenAI API key for this, which as usual, we would get from platform.openai.com. 00:01:54.960 |
Then, we go ahead, I'm just showing you that you can invoke the LLM there. 00:02:00.140 |
Then, we go ahead, actually define a output parser. 00:02:04.760 |
So, we do do this, I wasn't sure we did, but we would then define our LLM chain like this, okay? 00:02:10.240 |
So, LLM chain, we're adding our prompt, adding our LLM, adding our output parser, okay? 00:02:21.500 |
So, I would then say, okay, retrieve augmented generation, and what it's going to do is 00:02:25.720 |
it's going to give me a little report back on RAG. 00:02:28.900 |
Okay, takes a moment, but you can see that that's what we get here. 00:02:33.120 |
We can format that nicely, as we usually do, and we get, okay, look, we get a nice little report. 00:02:40.480 |
However, the LLM chain is, one, it's quite restrictive, right? 00:02:45.720 |
We have to have, like, particular parameters that have been predefined as being usable, 00:02:50.680 |
which is, you know, restrictive, and it's also been deprecated. 00:02:54.720 |
So, you know, this isn't the standard way of doing this anymore, but we can still use it. 00:03:01.560 |
However, the preferred method to building this and building anything else, really, 00:03:06.260 |
or chains in general in LLM chain is using LSL, right? 00:03:11.800 |
So, we just actually take the prompt LLM now, put parser that we had before, 00:03:15.460 |
and then we just chain them together with these pipe operators. 00:03:18.600 |
So, the pipe operator here is saying, take what is output from here and input it into here. 00:03:24.080 |
Take what is output from here and input it into here. 00:03:29.500 |
So, put those together, and then we invoke it in the same way, and we'll get the same output. 00:03:38.800 |
There is actually a slight difference on what we're getting out from there. 00:03:43.600 |
You can see here we got actually a dictionary, but that is pretty much the same. 00:03:49.240 |
Okay, so we get that, and as before, we can display that in markdown with this. 00:03:55.580 |
Okay, so we saw just now that we have this pipe operator here. 00:03:59.760 |
It's not really standard Python syntax to use this, or at least it's definitely not common. 00:04:10.220 |
It's an aberration of the intended use of Python, I think. 00:04:19.060 |
And when you understand it, I kind of get why they do it, because it does make things quite simple 00:04:28.720 |
It's a little bit weird, but it's what they're doing, and I'm teaching it ourselves. 00:04:35.180 |
So, what is that pipe operator actually doing? 00:04:40.500 |
Well, it's, as I mentioned, it's taking the output from this, putting it as input into what 00:04:52.160 |
Well, let's actually implement it ourselves without linechain. 00:04:57.120 |
So, we're going to create this class called runnable. 00:04:59.320 |
This class, when we initialize it, it's going to take a function. 00:05:03.160 |
Okay, so this is literally a Python function. 00:05:05.260 |
It's going to take that, and it's going to essentially turn it into what we would call 00:05:16.700 |
It just means that when you run the invoke method on it, it's going to call that function 00:05:23.900 |
in the way that you would have done otherwise. 00:05:26.440 |
So, using just function, brackets, open, parameters, brackets, close, it's going to do that. 00:05:33.300 |
But it's also going to add this method, this all method. 00:05:36.180 |
Now, this all method in typical Python syntax, now this all method is essentially going to 00:05:44.880 |
take your runnable function, the one that you initialize with. 00:05:48.820 |
And then it's also going to take an other function. 00:05:52.400 |
Okay, this other function is actually going to be a runnable, I believe. 00:05:56.900 |
Yes, it's going to be a runnable, just like this. 00:05:59.800 |
And what it's going to do is it's going to run this runnable based on the output of your 00:06:11.740 |
Seems a bit weird, maybe, but I'll explain in a moment. 00:06:17.160 |
So, I'm going to chain a few functions together using this all method. 00:06:23.660 |
So, first, we're just going to turn them all into runnables. 00:06:27.000 |
Okay, so these are normal functions, as you can see, normal Python functions. 00:06:31.200 |
We then turn them into this runnable using our runnable class. 00:06:38.560 |
So, we're going to create a chain that is going to be our runnable chained with another runnable, 00:06:50.660 |
So, we're going to invoke that chain of runnables with three. 00:07:03.220 |
Then, we're going to subtract five from eight to give us three again. 00:07:08.340 |
And then, we're going to multiply three by five to give us 15. 00:07:26.940 |
Well, that pipe operator in Python is actually a shortcut for the OR method. 00:07:33.180 |
So, what we've just implemented is the pipe operator. 00:07:36.640 |
So, we can actually run that now with the pipe operator here. 00:07:45.440 |
Like, under the hood, that is what that pipe operator is. 00:07:49.480 |
It's just chaining together these multiple runnables, as we'd call them, using their own internal OR operator. 00:08:11.600 |
So, this is different to the runnable I just defined here. 00:08:21.280 |
Instead, this runnable lambda object here, that is actually... 00:08:28.420 |
It's actually the same as what we just defined. 00:08:31.120 |
So, what we did here, this runnable, this runnable lambda is the same thing, but in linechain. 00:08:40.300 |
So, if we use that, okay, we use that to now define three runnables from the functions that we defined earlier. 00:08:48.000 |
We can actually pair those together now using the pipe operator. 00:08:52.240 |
You could also pair them together, if you want, with the OR operator. 00:09:03.680 |
Or, as we were doing originally, we'd choose a pipe operator. 00:09:09.860 |
So, this runnable lambda from linechain is just what we just built with the runnable. 00:09:16.800 |
Now, let's try and do something a little more interesting. 00:09:20.140 |
We're going to generate a report, and we're going to try and edit that report using this functionality. 00:09:37.240 |
You can see that AI is mentioned many times in here. 00:09:42.740 |
Then, we're going to take a very simple function. 00:09:56.420 |
So, we're actually trying to remove the introduction here. 00:09:59.160 |
I'm not sure if this actually will work as expected. 00:10:03.360 |
So, we're going to replace an old word with a new word. 00:10:13.440 |
So, we're going to replace an old word with a new word. 00:10:16.900 |
So, we can wrap both of these functions as runnable lambdas. 00:10:22.120 |
We can add those as additional steps inside our entire chain. 00:10:28.060 |
So, we're going to extract, try and remove the introduction. 00:10:30.700 |
Although, I think it needs a bit more processing than just splitting here. 00:10:44.040 |
So, now we get artificial intelligence, Skynet. 00:10:47.500 |
Refers to the simulation of human intelligence processed by machines. 00:10:50.420 |
And then, we have narrow Skynet, weak Skynet, and strong Skynet. 00:10:57.540 |
Skynet technology is being applied in numerous fields, including all these things. 00:11:01.640 |
Despite its potential, Skynet poses several challenges. 00:11:21.540 |
We can see also the introduction didn't work here. 00:11:23.660 |
The reason for that is because our introduction includes multiple new lines here. 00:11:28.940 |
So, I would actually, if I want to remove the introduction, we should remove it from here, I think. 00:11:35.660 |
I would never actually recommend you do that because it's not very flexible. 00:11:41.900 |
It's not very robust, but just so I show you that that is actually working. 00:11:56.240 |
So, now we're essentially just removing the introduction, right? 00:12:00.000 |
Just so you can see that we can have multiple of these runnable operations running, and they can be whatever you want them to be. 00:12:07.300 |
Okay, it is worth noting that the inputs to our functions here were all single arguments. 00:12:14.420 |
Okay, if you have a function that is accepting multiple arguments, you can do that in the way that I would probably do it, or you can do it in multiple ways. 00:12:23.700 |
One of the ways that you can do that is actually write your function to accept multiple arguments, but actually do them through a single argument. 00:12:32.340 |
So, just like a single x, which would be like a dictionary or something, and then just unpack them within the function and use them as needed. 00:12:41.140 |
Now, we also have these different runnable objects that we can use. 00:12:46.340 |
So, here we have runnable_parallel and runnable_passthrough. 00:12:55.060 |
So, runnable_parallel allows you to run multiple runnable instances in parallel. 00:13:02.500 |
Runnable_passthrough, maybe less self-explanatory, allows us to pass a variable through to the next runnable without modifying it. 00:13:13.540 |
So, we're going to come down here and we're going to set up these two docker arrays. 00:13:20.660 |
And we're going to need our lm to pull information from both of these sources of information in parallel, 00:13:30.180 |
So, we have these two sources of information, vector store a, vector store b. 00:13:35.060 |
This is our docker array, a and docker array b. 00:13:38.100 |
These are both going to be fed in as context into our prompt. 00:13:43.220 |
And then our lm is going to use all of that to answer the question. 00:13:48.340 |
Okay, so to actually implement that, we need an embedding model. 00:14:02.900 |
We're just passing in a very small amount of information to both. 00:14:06.900 |
So, we're saying, okay, we're going to create an in-memory vector store using these two bits of 00:14:13.620 |
So, I'm going to say half the information is here. 00:14:15.380 |
This would be an irrelevant piece of information. 00:14:17.620 |
Then we have the relevant information, which is DeepSeq v3 was released in December 2024. 00:14:23.060 |
Okay, then we're going to have some other information in our other vector store. 00:14:28.660 |
Again, irrelevant piece here and relevant piece here. 00:14:32.100 |
Okay, the DeepSeq v3 lm is a mixture of experts model with 671 billion parameters at its largest. 00:14:40.420 |
Okay, so based on that, we're also going to build this prompt string. 00:14:45.620 |
So, we're going to pass in both of those contexts into our prompt. 00:14:50.900 |
We don't actually need, we don't need that bit. 00:15:01.140 |
And we would run them through our prompt template. 00:15:04.420 |
Okay, so we have our system from template, which is this. 00:15:08.020 |
And then we're just going to have, okay, our questions going to go into here as a user message. 00:15:19.460 |
We're going to convert both those vectors to retrievers, which just means we can retrieve stuff from them. 00:15:25.940 |
And we're going to use this runnable parallel to run both of these in parallel, right? 00:15:37.140 |
But then we're also running our question in parallel because this needs to be essentially passed through this component 00:15:45.860 |
So, when we look at this here, it's almost like, okay, this section here would be our runnable parallel. 00:15:59.940 |
So, it's almost like there's another line there, which is our runnable pass through. 00:16:13.460 |
I just realized here we're using the deprecated embeddings. 00:16:36.340 |
So, this is using our runnable parallel and runnable pass through. 00:16:43.380 |
Elnable parser, which is being chained together with the usual, you know, the usual type operator. 00:16:53.700 |
What architecture does the model DeepSeq release in December use? 00:16:57.940 |
So, for the ELN to answer this question, it's going to need to tell us, 00:17:02.340 |
well, it needs the information about the DeepSeq model that was released in December, 00:17:10.500 |
And then it also needs to know what architecture that model uses, 00:17:14.100 |
which is defined in the other half over here. 00:17:23.060 |
DeepSeq V3 model released in December 2024 is a mixture of experts model with 671 billion parameters. 00:17:31.620 |
So, a mixture of experts and this many parameters. 00:17:35.540 |
So, we've put together our pipeline using ELSL, using the pipe operator, 00:17:43.540 |
Specifically, we've looked at the runnable parallel, 00:17:46.500 |
runnable pass-through, and also the runnable lambs. 00:17:49.380 |
So, that's it for this chapter on ELSL, and we'll move on to the next one.