This is a new syntax that makes it much easier, and more transparent to construct, Let's get to it. A lot of the power of Langchain comes from composing And we have a new way of doing this, and this is LCEL and a runnable protocol which define a few things. First, it defines an allowed set of input types, There's then a bunch of standard methods, and we'll go over all of these, There are ways of modifying parameters at runtime, What is this interface that we expect all runnables to expose? First, there are a few common methods there's There's invoke, which calls the runnable on a single input. There's stream, which calls it on a single input in stream's backer response. And then, there's batch which calls it on a list of inputs. For all of these synchronous methods, there's also a corresponding async method. There's common properties that all runnables have, namely an input schema and an output schema. And you can see a list of the input and output types for common components below. Why use LCL? There are a few benefits that this gets you. First, you get async batch and streaming support out of the box. Second, a big part of working with LLMs is that they're a little bit unpredictable sometimes. They don't always respond how you want them to respond. And so, you can easily attach fallbacks not only to LLMs but also to entire chains. Third, parallelism. LLM calls can be time-consuming. They often take a while and so it's really important to run them in parallel if you can. And the LCEL syntax makes it easy to do that. And fourth, logging is built in. As these chains and agents get more and more complex, being able to see the sequence of steps and the inputs and outputs becomes crucial for building an LLM application. And we've added things into the open source library. And then, And LCAL natively logs all of these things, With that, it's time to get to the code and see what this actually looks like. First, let's set up our environment as before. We'll then import a few different components that we're going to compose together. First, we'll import a prompt template. Next, we'll import a language model, and we'll be using OpenAI. And finally, we'll import an output parser, Let's now use these components to create a simple chain of prompt template to language model to output parser. So first, let's create a prompt template. And we'll create one that asks the language model to tell us a short joke. We'll then initialize the language model that we're going to use and finally we'll create the output parser that we're going to use. And we can now call this chain using the invoke method on some inputs. And the inputs here are going to be the inputs to the prompt template. So, And so, if we call this, we can see we get back a joke. Why don't bears ever get caught in traffic? Because they always take the beariest best routes. Now's a good time to pause a little bit, pass in some other arguments to this function, maybe change up the prompt, play around with it, and get a sense for these different components, We'll now create a slightly more complex chain. In the previous short course, we went over how to do retrieval augmented generation. And so, we'll replicate that same process using link chain expression language. First, we need to set up our retriever. We're first going to create a really simple vector store. We're going to do this by initializing it with two texts. First, Harrison worked at Kensho. And next, bears like to eat honey well. Then, If we call it on something different, if we call it on what do bears like to eat, we can see that the first result that we get back is bears like to eat honey. In order to create this pipeline, we're first going to create a prompt that basically asks the language model to answer the question based only on the following context. And it has two variables here. It has context and question. And so, we're going to be passing in two things. And then, we're going to be passing in the context. We want the first and only input to the chain to be the user question. From then, we want to fetch relevant context. And then, we want to pass that into the prompt. To do that, we're going to use a runnable map which we can import this way. We want this runnable map to have two elements. We can write a lambda function that calls retriever.getRelevantDocuments and passes in the question parameter. We also want to pass along the original question parameter, From here, we want to pass it into the prompt. And then, We can then name this as our chain, and we now have a new runnable. And now, if we invoke this with a question, like where did Harrison work, If we want to take a closer look at what exactly is going on under the scenes, we can just look at this runnable map directly. And then, if we just run this on the same thing. And then, This is then getting passed into the prompt which is then creating a prompt One of the other things that we can do with runnables is we can bind parameters to them. And so normally, this would look like a prompt. And then, a model. But where do the functions go in? And so, And what this will do under the hood is that when the model is called it will pass any parameters in bind along to the invocation. So now, if we create a runnable by doing prompt and then pipe model, If you wanted to change that you would just overwrite it. And now, we have these two different functions here. We can just update the model by binding it with new functions. And then, when we call this new runnable which we're using the new model in, One of the really powerful things about language and expression language is you can easily attach fallbacks, not only to individual components, but to entire sequences. In order to create a situation where this is going to fail, we'll actually use an older version of a language model from OpenAI that isn't quite as good. To do that, we're going to import an OpenAI language model from lanechain.llms. This is different than the chat models. The chat models are newer types of models and so they're generally pretty good at things like outputting JSON, but older models which is lanechain.llms, generally aren't. We'll now create a simple model and a simple chain. In this simple model we're going to be setting temperature equals zero. So, Our chain is just going to be calling this language model and then piping the result into JSON.loads. This will break if the result of the language model isn't valid JSON. Let's now create a challenge problem statement for the language model. We're going to ask it to write three poems in a JSON blob where each poem is a JSON blob of a title, author, and the first line. This is a pretty challenging problem and we want it to respond in valid JSON. And so, if we run the simple model on this, But if we take this and we try to decode it with JSON, we can see that we get a JSON decode error because of what that model is outputting isn't actually valid JSON. The new models from OpenAI are pretty good at outputting valid JSON. And then, we're going to pipe that string into JSON.loads. If we use the same challenge with this model, we can see that it correctly outsplits valid JSON. In order to use fallbacks, what we can do is we can create a final chain, and we can start with the simple chain. And then, In with fallbacks we'll pass in a list of other runnables, If an error is raised, then it goes through this list in order. If we now call this chain on the challenge, we can see that it responds in the correct format. Finally, let's talk about the interface that these runnables expose. Let's go back to the chain that we had before. We're asking it to tell us a joke. And let's take a look at a few different elements of this interface. The one that we were using from the get-go is invoke. This is a synchronous method that calls it on one input. There's also batch which is going to call it on a list of inputs. So here, we pass in two different inputs, one with And under the hood this is actually executing them in parallel as much as possible. We can also stream back responses. And finally, all of these methods have corresponding asynchronous methods. That is it for this lesson, but there's a lot of different ways that you can combine all these elements and put together really complicated types of chains and sequences. We sometimes see sequences that get up to 300 or 400 components in a row. And so, before moving on, Can you do two, three, even four language model calls in a row? What does that look like? Lots of fun things to explore. In the next lesson, we're going to combine what we just learned here, language and expression language, with what we covered in the previous lesson, open AI function calls. And we're going to show how to use those two things together.