In this chapter, we're going to be taking a look at Lanchain's expression language. We'll be looking at the runnables, the serializable and parallel of those, the runnable pass-through, and essentially how we use LCL in its full capacity. Now, to do that well, what I want to do is actually start by looking at the traditional approach to building chains in Lanchain.
So, to do that, we're going to go over to the LCL chapter and open that CoLab. Okay, so let's come down. We'll do the prerequisites. As before, nothing major in here. The one thing that is new is DocRay. 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.
If you want to use Lansmith, you just need to add in your Lanchain API key. Okay, and then let's, okay, so now let's dive into the traditional approach to chains in Lanchain. So, the Lanchain, I think it's probably one of the first things introduced in Lanchain, if I'm not wrong.
So, let's take it to prompt and feed it into an LLM and that's it. You can also, you can add like output passing to that as well, but that's optional. I don't think we're going to cover it here. So, what that might look like is we have, for example, this prompt template here.
Give me a small report on topic. Okay, so that would be our prompt template. We'd set up as we usually do with the prompt templates, as we've seen before. We'd then define our LLM. We'd need our OpenAI API key for this, which as usual, we would get from platform.openai.com.
Then, we go ahead, I'm just showing you that you can invoke the LLM there. Then, we go ahead, actually define a output parser. So, we do do this, I wasn't sure we did, but we would then define our LLM chain like this, okay? So, LLM chain, we're adding our prompt, adding our LLM, adding our output parser, okay?
This is the traditional approach. So, I would then say, okay, retrieve augmented generation, and what it's going to do is it's going to give me a little report back on RAG. Okay, takes a moment, but you can see that that's what we get here. We can format that nicely, as we usually do, and we get, okay, look, we get a nice little report.
However, the LLM chain is, one, it's quite restrictive, right? We have to have, like, particular parameters that have been predefined as being usable, which is, you know, restrictive, and it's also been deprecated. So, you know, this isn't the standard way of doing this anymore, but we can still use it.
However, the preferred method to building this and building anything else, really, or chains in general in LLM chain is using LSL, right? And it's super simple, right? So, we just actually take the prompt LLM now, put parser that we had before, and then we just chain them together with these pipe operators.
So, the pipe operator here is saying, take what is output from here and input it into here. Take what is output from here and input it into here. That's all it does. It's super simple. So, put those together, and then we invoke it in the same way, and we'll get the same output.
Okay? And that's what we get. There is actually a slight difference on what we're getting out from there. You can see here we got actually a dictionary, but that is pretty much the same. Okay, so we get that, and as before, we can display that in markdown with this.
Okay, so we saw just now that we have this pipe operator here. It's not really standard Python syntax to use this, or at least it's definitely not common. It's an aberration of the intended use of Python, I think. But anyway, it does, it looks cool. And when you understand it, I kind of get why they do it, because it does make things quite simple in comparison to what it could be otherwise.
So, I kind of get it. It's a little bit weird, but it's what they're doing, and I'm teaching it ourselves. That's what we're going to learn. So, what is that pipe operator actually doing? Well, it's, as I mentioned, it's taking the output from this, putting it as input into what is ever on the right.
But how does that actually work? Well, let's actually implement it ourselves without linechain. So, we're going to create this class called runnable. This class, when we initialize it, it's going to take a function. Okay, so this is literally a Python function. It's going to take that, and it's going to essentially turn it into what we would call a runnable in linechain.
And what does that actually mean? What doesn't really mean anything? It just means that when you run the invoke method on it, it's going to call that function in the way that you would have done otherwise. So, using just function, brackets, open, parameters, brackets, close, it's going to do that.
But it's also going to add this method, this all method. Now, this all method in typical Python syntax, now this all method is essentially going to take your runnable function, the one that you initialize with. And then it's also going to take an other function. Okay, this other function is actually going to be a runnable, I believe.
Yes, it's going to be a runnable, just like this. And what it's going to do is it's going to run this runnable based on the output of your current runnable. Okay, that's what this all is going to do. Seems a bit weird, maybe, but I'll explain in a moment.
We'll see why that works. So, I'm going to chain a few functions together using this all method. So, first, we're just going to turn them all into runnables. Okay, so these are normal functions, as you can see, normal Python functions. We then turn them into this runnable using our runnable class.
Then, look what we can do, right? So, we're going to create a chain that is going to be our runnable chained with another runnable, chained with another runnable. Okay, and let's see what happens. So, we're going to invoke that chain of runnables with three. So, what is this going to do?
Okay, we'll start with five. We're going to add five to three. So, we'll get eight. Then, we're going to subtract five from eight to give us three again. And then, we're going to multiply three by five to give us 15. And we can invoke that. And we get 15.
Okay? Pretty cool. So, that is interesting. How does that relate to the pipe operator? Well, that pipe operator in Python is actually a shortcut for the OR method. So, what we've just implemented is the pipe operator. So, we can actually run that now with the pipe operator here. And we'll get the same.
We'll get 15. Right? So, that's what linechain is doing. Like, under the hood, that is what that pipe operator is. It's just chaining together these multiple runnables, as we'd call them, using their own internal OR operator. Okay? Which is cool. I will give them that. It's kind of a cool way of doing this.
It's creative. I wouldn't have thought about it myself. So, yeah. That is a pipe operator. Then we have these runnable things. Okay? So, this is different to the runnable I just defined here. This is... We define this ourselves. It's not a linechain thing. We didn't get this from linechain.
Instead, this runnable lambda object here, that is actually... It's actually the same as what we just defined. All right? So, what we did here, this runnable, this runnable lambda is the same thing, but in linechain. Okay? So, if we use that, okay, we use that to now define three runnables from the functions that we defined earlier.
We can actually pair those together now using the pipe operator. You could also pair them together, if you want, with the OR operator. Right? So, we could do what we did earlier. We can invoke that. Okay? Or, as we were doing originally, we'd choose a pipe operator. Exactly the same.
So, this runnable lambda from linechain is just what we just built with the runnable. Cool. So, we have that. Now, let's try and do something a little more interesting. We're going to generate a report, and we're going to try and edit that report using this functionality. Okay? So, give me a small report about topic.
Okay? We'll zero through here. We're going to get our report on AI. Okay? So, we have this. You can see that AI is mentioned many times in here. Then, we're going to take a very simple function. All right? So, I'm going to do extract fact. This is basically going to take, what is it?
See, taking the first. Okay? So, we're actually trying to remove the introduction here. I'm not sure if this actually will work as expected. So, we're going to replace it. So, we're going to replace an old word with a new word. So, we're going to replace an old word with a new word.
Our old word is going to be AI. Our new word is going to be Skynet. Okay? So, we can wrap both of these functions as runnable lambdas. We can add those as additional steps inside our entire chain. All right? So, we're going to extract, try and remove the introduction.
Although, I think it needs a bit more processing than just splitting here. And then, we're going to replace the word. We need that actually to be AI. Run that or run this. Okay. So, now we get artificial intelligence, Skynet. Refers to the simulation of human intelligence processed by machines.
And then, we have narrow Skynet, weak Skynet, and strong Skynet. Applications of Skynet. Skynet technology is being applied in numerous fields, including all these things. Scary. Despite its potential, Skynet poses several challenges. Systems can perpetrate existing biases. It raises significant privacy concerns. It can be exploited for malicious purposes.
Okay? So, we have all these. You know, it's just a silly little example. We can see also the introduction didn't work here. The reason for that is because our introduction includes multiple new lines here. So, I would actually, if I want to remove the introduction, we should remove it from here, I think.
I would never actually recommend you do that because it's not very flexible. It's not very robust, but just so I show you that that is actually working. So, this extract fact runnable. Right? So, now we're essentially just removing the introduction, right? Why would we want to do that? I don't know, but it's sad.
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. Okay, it is worth noting that the inputs to our functions here were all single arguments. 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.
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. 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.
That's just one way you can do it. Now, we also have these different runnable objects that we can use. So, here we have runnable_parallel and runnable_passthrough. Kind of self-explanatory to some degree. So, let me just go through those. So, runnable_parallel allows you to run multiple runnable instances in parallel.
Runnable_passthrough, maybe less self-explanatory, allows us to pass a variable through to the next runnable without modifying it. Okay, so let's see how they would work. So, we're going to come down here and we're going to set up these two docker arrays. Obviously, it's two sources of information. And we're going to need our lm to pull information from both of these sources of information in parallel, which is going to lie like this.
So, we have these two sources of information, vector store a, vector store b. This is our docker array, a and docker array b. These are both going to be fed in as context into our prompt. And then our lm is going to use all of that to answer the question.
Okay, so to actually implement that, we need an embedding model. So, we're just opening our embeddings. We have our vector store a, vector store b. They're not, you know, real vectors. They're not full-on vector stores here. We're just passing in a very small amount of information to both. So, we're saying, okay, we're going to create an in-memory vector store using these two bits of information.
So, I'm going to say half the information is here. This would be an irrelevant piece of information. Then we have the relevant information, which is DeepSeq v3 was released in December 2024. Okay, then we're going to have some other information in our other vector store. Again, irrelevant piece here and relevant piece here.
Okay, the DeepSeq v3 lm is a mixture of experts model with 671 billion parameters at its largest. Okay, so based on that, we're also going to build this prompt string. So, we're going to pass in both of those contexts into our prompt. Now, I'm going to ask a question.
We don't actually need, we don't need that bit. And actually, we don't even need that bit. What am I doing? So, we just need this. So, we have both the contexts there. And we would run them through our prompt template. Okay, so we have our system from template, which is this.
And then we're just going to have, okay, our questions going to go into here as a user message. Cool. So, we have that. And then, let me make this easier to read. We're going to convert both those vectors to retrievers, which just means we can retrieve stuff from them.
And we're going to use this runnable parallel to run both of these in parallel, right? So, these are both being run in parallel. But then we're also running our question in parallel because this needs to be essentially passed through this component without us modifying anything. So, when we look at this here, it's almost like, okay, this section here would be our runnable parallel.
And these are being run in parallel. But also, our query is being passed through. So, it's almost like there's another line there, which is our runnable pass through. Okay. So, that's what we're doing here. So, that's what we're doing here. These run in parallel. One of them is a pass through.
Oh, I need to run here. I just realized here we're using the deprecated embeddings. Just switch it to this. So, Langchain OpenAI. We run that, run this, run that. And now, this is set up. Okay. So, we then put our initial. So, this is using our runnable parallel and runnable pass through.
That is our initial step. We then have our prompt. Elnable parser, which is being chained together with the usual, you know, the usual type operator. Okay. And now, we're going to invoke a question. What architecture does the model DeepSeq release in December use? Okay. So, for the ELN to answer this question, it's going to need to tell us, well, it needs the information about the DeepSeq model that was released in December, which we have specified in one half here.
And then it also needs to know what architecture that model uses, which is defined in the other half over here. Okay. So, let's run this. Okay. There we go. DeepSeq V3 model released in December 2024 is a mixture of experts model with 671 billion parameters. Okay. So, a mixture of experts and this many parameters.
Pretty cool. So, we've put together our pipeline using ELSL, using the pipe operator, the runnables. Specifically, we've looked at the runnable parallel, runnable pass-through, and also the runnable lambs. So, that's it for this chapter on ELSL, and we'll move on to the next one. The end of the year.
The end of the year.