Intro to AI Engineering with Python (3/6) – LangChain 101

Hi and welcome to part 3 of this course! In this part, we’ll be looking at LangChain, which is a framework designed to make it easier to build applications that use large language models (LLMs). Think of it as a set of tools that helps bridge the gap between LLMs and the applications you might want to build with them.

LangChain helps us:

  • Provide a unified interface: Any code you write can be used with different LLMs with little modification, and you can use the same code to write prompts or tools for different LLMs. This means you could write code and use the GPT-4o model by OpenAI that we have used so far, and then decide you actually want to work with Google Gemini instead, and you could do so with minimal changes.
  • Prebuilt tools for common tasks: Langchain includes tools for common tasks you might want to do with LLMs, such as building chatbots, summarizing documents, or analyzing code. Besides just building our own tools and functions, we can also import community pre-built tools.
  • Memory and Context: Langchain makes it easy to incorporate memory and context into our LLM applications. This means our application can remember past interactions and use that information to inform future responses, like having a real conversation.

We’ll be looking at the very basics in this introductory lesson, but if you like what you see we have a whole lot more for you on this topic, and we’ll point you there at the end of this lesson.

Installing LangChain

Ok, time to get started with LangChain! We’ll start with some installs. Run the following command in your terminal to install LangChain:

pip install langchain

This will install LangChain and a bunch of other stuff you need with it. I’m using LangChain version 0.3.1 at the time of writing this, so you can also run pip install langchain==0.3.1 to install this specific version if you want to make sure you have the same experience as me.

The next thing we need to install is the langchain-openai library. This is a library that acts as a bridge between LangChain and the OpenAI API. Run the following command in your terminal to install it:

pip install langchain-openai

I’m using version 0.2.1 at the time of writing, so you can once again also run pip install langchain-openai==0.2.1 to install this specific version if you want.

You also need the openai library, but we already installed that in part one of the series.

Getting started

Now create a new file named langchain_basics.py in the root folder of your project:

πŸ“ Intro_to_AI_engineering
    πŸ“ output
    πŸ“„ .env
    πŸ“„ chat_gpt_request.py
    πŸ“„ generate_image.py
    πŸ“„ generate_speech.py
    πŸ“„ langchain_basics.py     ✨ New file
    πŸ“„ test_audio.mp3
    πŸ“„ text_to_summarize.txt
    πŸ“„ transcribe_audio.py

Inside this new langchain_basics.py file, let’s get started with the following imports:

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()

So let’s take a look at all these wacky imports here:

  • StrOutputParser is a class that will help us parse the output from the LLMs into a string format. Normally when you get the return from ChatGPT, we have to index into the response.choices[0].message.content to get the response. Just think of this as a convenience class that will help us with this. (It has more functionality than that but that’s the basic idea).
  • ChatPromptTemplate is a class that will help us create a template for our chat prompts. This will make it easier to create prompts for the LLMs (any LLM you want to work with).
  • ChatOpenAI is a class that will basically just allow us to create an instance of OpenAI and use it with LangChain.

Like last time we import and use load_env to read the environment variable from our .env file.

So as stated, the big value here of these output parsers and prompt templates is that they are a unified interface that we can use in the same manner without changes even if we change the LLM we are using halfway through our project or in the future, or want to use multiple at the same time.

Prompt templates

Now let’s continue our code by creating a prompt template. You’ll see how this works as we go along:

french_german_prompt = ChatPromptTemplate.from_template(
    "Please tell me the french and german words for {word} with an example sentence for each."
)

For this simple example prompt, I’ll create an example that asks for the French and German words for a given word, along with an example sentence for each. This is just a simple example to show the parts of LangChain and how they work together.

The {word} part is the template variable that we can replace with any word we want to ask about. We then create a ChatPromptTemplate using the from_template method and pass in our prompt string. The ChatPromptTemplate class will help us create prompts for the LLMs in a more convenient way and basically deals with formatting message history like this:

## Example of a ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful AI bot. Your name is {name}."),
    ("human", "Hello, how are you doing?"),
    ("ai", "I'm doing well, thanks!"),
    ("human", "{user_input}"),
])

We need only a single message here though, which is why we use the from_template method. In this case, LangChain will assume this to be a human message so this will result in something like this:

## Example of a ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
    ("human", "Please tell me the french and german words for {word} with an example sentence for each.")
])

Creating a chain

Now that we have a prompt template to create our prompts, let’s continue:

llm = ChatOpenAI(model="gpt-4o-mini")
output_parser = StrOutputParser()

french_german_chain = french_german_prompt | llm | output_parser

First, we define our LLM instance using the ChatOpenAI class and pass in the model we want to use. I’ll be using gpt-4o-mini as it is more than enough for the simple test we’re doing here, but you can also use gpt-4o if you want to use the larger model. Think of this llm object we just defined kind of like the CLIENT object we used in the previous parts of this series.

You may notice we’re not passing in an API key here, even though we did so in the previous parts. This is because load_dotenv() has read the .env file and set the OPENAI_API_KEY environment variable for us, and LangChain is set up to read this value from the environment automatically, so we don’t need to manually pass it in.

We then create an instance of the StrOutputParser class by calling it using the () brackets, saving the new instance in a variable named output_parser. This will help us to parse the output from the LLMs into a string response as discussed earlier.

Now that we have three building blocks, it is time for one of LangChain’s important concepts, “chains”. We can simply use the | operator to chain these building blocks together. This operator is taken from the pipe operator in Unix, which is used to chain commands together.

In this case, we take the french_german_prompt as the entry point of our chain, and we pipe the resulting prompt into our llm, making an LLM call. We then pipe the output into our output_parser to get the string response. Notice how easy and readable the chain is, like a simple step 1, step 2, step 3.

In LangChain We use chains to build stuff with large language models, hence the name LangChain. This piping style of syntax above is referred to as LCEL or LangChain Expression Language.

Running the chain

You can use an if __name__ == "__main__": block here if you want, and ask for user input, inserting that, but for simplicity’s sake, I’ll just hardcode words in here for now as I want to focus on LangChain, and you already know how to get user input.

So let’s actually run this chain. To do this we can simply use the invoke method on our chain:

result = french_german_chain.invoke({"word": "blueberries"})
print(result)

We can technically also just pass in the string "blueberries" as we only have a single variable, but it’s better practice to use a dictionary like this as you may have multiple variables in your prompt. So go ahead and run this Python file and you should get something like the following: