Back to Index

LLM Chains using GPT 3.5 and other LLMs — LangChain #3


Chapters

0:0 What are Langchain chains?
1:1 LLMChain
5:14 Utility chains walkthrough
10:25 All generic chains in langchain
14:7 Langchain hub
15:10 Other chains in langchain

Transcript

Today, we're going to take a look at chains in the Langchain library. As you might have guessed, chains are a pretty big component of Langchain. And naturally, being able to use chains is a essential skill for anyone wanting to use the Langchain library. Now, today is going to be a little bit different.

We have Francisco, who is one of the contributors to the Langchain library. And later on, he's going to dive into some of the details behind chains and actually show you how to use them. So that'll be pretty interesting. But for now, let's just introduce chains. I'll give you a quick overview before handing it over.

Now, we can think of chains as thin wrappers around different components in the Langchain library. And more specifically, primitives within the library. So primitives cover a few different things. They can be the prompts that we use, they can be large-language models, utilities within the library, and even other chains.

Now, the simplest of these chains, you might have seen it before, is called the large-language model chain. And what it is, is actually very simple. So up here, we have our prompt template. Now, to actually build our prompt template, we'd actually use a prompt template object as well. Let's just pretend I've done that here.

Now, what a user is going to input into this large-language model chain is going to be a query, okay? Now, the query we actually have here, right? So the first part of this chain is taking the input from the user and putting it into first primitive of the chain, which is this prompt template here.

And then the next step is this prompt template with the query included inside it will now be passed across to our large-language model, okay? And the large-language model will, of course, output some generation based on our prompt. That is the LLM chain. And yeah, I mean, you've seen it, it's super simple.

And naturally, it's very easy to create. We can actually see the code here. So we'll just run this. We would put in our OpenAI API key here. We'd initialize our large-language model, create our prompt template, as I just described, and then we just use this. So if we run that, we should be able to get an output.

So we're going to run this here, llmchain.run, and we just ask a question. And it's going to return as a bullet point list because in this prompt template that we use here, we asked for a bullet point list. And there we get it. So despite only running this single function, we've actually created a chain of, in this case, two functions.

Now, as I said, this is one of the simplest chains in line chain. They get far more complicated than this. And we can actually see that in the docs here. So if we go over to the left, we have chains, how-to guides, we can see that there's these three different types of chains.

Generic chains, combined document chains, and utility chains. And we can describe each of these chains, starting with the generic chains, which is, as you might have guessed from the name, fairly generic. They are more or less used to build other chains in the library. You wouldn't necessarily use them by themselves because they're not built for any particular purpose.

But nonetheless, you might find yourself using them as I just demonstrated with the Lodged Image Model chain. We use it just to generate some text. But typically, I'm using these as parts of other chains. We have the combined document chains, which is super useful when we are doing anything where we're working with other documents, like we might do in question answering, summarization, or any retrieval augmented use case.

And then, the final one is utility chains. And these are more specific chains that consist of usually a Lodged Image Model chain alongside another specific line chain utility. Now, the combined document chains, they are very use case specific, and I feel need a bit more of a deep dive to understand how to use them properly.

So we're actually just going to focus on two of the types of chains at the moment, which are the generic chains and the utility chains. Now, what I'm going to do is leave you there, and I'm going to let Francisco take you through these chains and take a look at one of the utility chains and how we'd use it, and also have a look at what the generic chains are and give a couple of cool examples on actually using them, which I think rarely gets across what they are useful for.

So without any further ado, let me hand over to Francisco. - Hi there, my name is Francisco. As James said, I am a contributor to Lankchain. I think Lankchain is definitely one of the most powerful libraries out there. And to really leverage Lankchain fully, we need to understand what its fundamental building blocks are so we know how we can use them for our specific use case.

So let's get right into the code for chains. We will be using the OpenAI LLM as the LLM in this notebook. So you will need to set your OpenAI API key right here. I have already set it up so I can initialize my LLM there. And we will be using this count tokens function, which will really let us know how many tokens we are using when we run our LLM.

And this is important because OpenAI charges us by the number of tokens we use. So we really want to keep track of these tokens when we are making many experiments with Lankchain. So we will be diving into one example for the utility chains to really understand what a utility chain is under the hood.

And the example chain we'll be using is the LLM math chain. The LLM math chain, as you can see here, will help us use LLMs to do complex math. So let's say that we want to know what 13 is raised to the 0.3432 power. And if we run this, we will be running the LLM math chain with that query.

And we will get an answer for that. And it will be the correct answer. This is not so easy to do with a vanilla LLM because the LLM doesn't know how to do complex math. So we need to help it to be able to return accurate results. So let's see what's going on here.

The chain received a question and we are changing that question. We are computing that question through the LLM and we are getting some Python code in return. So now we need to ask ourselves, what is going on under the hood here? And we can see that Python code is being generated in an intermediate step.

So why is this Python code being generated and who is generating this Python code? Here is where prompts come in. And prompts are really important for utility chains because utility chains serve a very specific purpose. And the prompt we use for the utility chain will tell the chain how exactly it needs to perform the utility purpose that we have for that chain.

So we will be sending this prompt to the LLM and the LLM will know how to behave because this prompt will give it very precise instructions. And this is a concept that is repeated across all utility chains. So let's take a look. As we can see here, we can always print a chain's prompt by accessing the prompt attribute and then the template attribute from the prompt.

So here we can see that we're telling the LLM that it can do basic math, but it should not try to do complex math. And if anyone asks it a complex question, it should just output Python code. So that's why we're getting Python code as an intermediate step. The LLM is returning Python code because it knows that it cannot do really complex math.

And what if we try to send the LLM just a random complex query and see if it can perform, give us an accurate result on its own. If we do, as we have here, we will send it the same question, but without any context. We will not ask it to generate Python code, just give us an answer.

So if we run this, we will see that the answer is 2.907, which is different and wrong from our right answer, which was 2.41. So if we don't have this prompt and we don't do this intermediate Python code, we wouldn't be getting a right answer. So here the chain is really enabling us to use LLMs to do math.

Whereas if we didn't have this chain, the LLM would be doing the math wrongly. So here is an insight for utility chains. By using prompts intelligently, we can force the LLM to avoid common pitfalls. And this can be done by explicitly programming it to behave in a certain way.

And this is really important because this is what is done again and again in different utility chains. So let's see the LLM math call method to see how this Python code is being used then. Once the Python code is generated, we can see here that if the LLM returned Python code, T here is the output of the LLM, we will be able to run that code and then give that code as an answer right here.

And if not, we will just output that answer directly. So here it's interesting that we can handle both cases. We can receive Python code and run it, or we can just return the straight answer. And this is it for the LLM math chain. As we can see, there are several other chains that do very different things.

There's the SQL chain, which computes SQL commands and can build SQL commands for natural language queries. There's also the API chain, which helps us make correct API calls to a specific API by giving the LLM the documentation for that API. And also there's the bash commands chain, which helps us create bash commands on the fly.

But there's many more, and we really encourage you to check them out on your own and play with the example notebooks, which are in the documentation, which you can check out in this link. And with that documentation and those notebooks, you will be able to understand which utility chains you might need for your specific application.

Awesome. So now we are ready to go deep into generic chains, and generic chains are a bit different than utility chains in that they're not thought of as chains that you will be using on their own, but more as building blocks for building other chains. And there are three types of generic chains, which we will cover, and we will cover them all in the same example.

So we can really understand the power of combining these chains to build custom solutions for applications. So let's start by the first of these chains, which is the transform chain. And let's say we need to clean an input text from extra spaces, because extra spaces are being charged for us as tokens, and we don't want to spend extra with dirty text.

And also it's just not really neat. So we have this function as we can see here, and we will initialize our chain by saying this is the input, and this is the output, and this is a transform function. So basically a transform chain just gets some inputs, applies a function to those inputs, and returns the outputs.

It's very important here to notice that the transform chain does not have an LLM. So not all chains are LLM dependent. So we here, we will be building our clean extra spaces chain, and then we will run it. We don't need the count tokens here function, because we don't have any LLMs.

So as we can see here the random text with a lot of irregular spacing will be cleaned, and we will have our clean text as an output. So let's say now that we want to use this chain to clean some text and then send it to an LLM. And we want to send it to an LLM that will change the style of what we have given as an input to another style.

So say to write it in the style of a poet or a policeman. So to do that, we will need to build our prompt template for our LLM chain. And we will say paraphrase this text in the style of a specific style here. And this is our prompt. And here is our style paraphrase chain with this LLM chain, which is the second generic chain that we will be seeing.

And the LLM chain is just a chain that computes an LLM call with a prompt template. James already covered this in detail. So as we can see here, we will have our style paraphrase chain. And now we want to combine these two. And here comes our third generic chain.

So how can we combine two chains? Well, we can do that with a sequential chain. So the sequential chain just receives a few chains as an input. And we want to tell it which are the input variables we will give in the sequential chain, and now which output variables we are expecting.

So we will initialize that. And now we can give our final chain an input, which will be cleaned from extra spacing, and then will be written in the style of some desired style we want. So let's say we have this definition of chains from the Lang chain documentation, which has, as you can see, lots of extra spacing that we don't really want.

But we also want it to be written in the style of, let's say, a '90s rapper, just because we can try that. And if we just run it here, we will see that here we get our answer. Let's change, let us link up multiple pieces to make one dope app.

Like we can take user input, style it up with a prompt template, then pass it to an LLM. We can get even more creative by combining multiple chains or mixing chains with our components. So that's quite creative and accurate in the style of a '90s rapper. So finally, just a small note on LangChainHub.

LangChainHub is where chains, prompts, and agents are serialized, and we can load them from LangChainHub to use them for our own purposes. And it's going to be the place where these components will ultimately live. So it's really important that we learn how to use LangChainHub to load these components and then use them.

So it's really, really easy to load chains from LangChainHub. You can just import the load chain function, set the path for the chain you want to import. This path, you can find it in the LangChainHub repository. You can find the appropriate path for the chain you want to load.

And here you can overwrite any of the default parameters that you want to change. So for example, if you want to have the chain not be verbose when it's being run, you can just set it to false, and then you can see here that it's not verbose anymore. So it's really easy to change any parameters that you might want.

So that's all for chains. - Okay, so that's it for the code portion. Again, massive thank you to Francisco for taking us through that and sharing some of his insights as one of the contributors of the library. If there's one thing that I'd like to point out, it's that just now we only saw a couple of chains.

This really just acts as an introduction to chains in LangChain. But there are a huge number of these that we haven't even spoken about or even mentioned yet. And okay, we've covered generic chains relatively well. Combined documents chains, we haven't covered whatsoever. And utility chains, we've covered a little bit.

But even within utility chains, we've had a look at LLM math, but we haven't had a look at any of the other ones. So there are a ton of these chains that we can use in the library. And we will make sure to go through at least a few of these in the future.

But the point being that there is a lot more potential for building really cool tools here than what we've just shown you. So for now, we're going to leave it there. I hope this is all useful and interesting. So thank you very much for watching and I will see you again in the next one.

Bye. (soft music) (soft music) (soft music) (soft music) (soft music)