Post

LangChain Series: A Deep Dive into Chat Prompts

LangChain Series: A Deep Dive into Chat Prompts

Introduction

In our LangChain series, we’ve explored the high-level concepts of chains and agents. Now, let’s zoom in on one of the most fundamental components you’ll use in every application: Prompts.

Modern chat models (like GPT-4 or Claude 3) don’t just take a single string of text as input. They expect a structured list of messages, each with a specific role (e.g., system, user, assistant). This structure allows you to set the AI’s persona, provide few-shot examples, and maintain a coherent conversation history.

LangChain’s primary tool for managing this complexity is the ChatPromptTemplate. This post will show you how to use it to create flexible, reusable, and powerful prompts for any chat model.

The Anatomy of a ChatPromptTemplate

A ChatPromptTemplate is built from a list of message templates. The most common way to define it is with a list of tuples, where each tuple represents a message and follows a ("role", "content") structure.

Let’s break down the roles:

  • system: This message sets the overall behavior, persona, and instructions for the AI. It’s the first message in the list and tells the model how it should act throughout the conversation (e.g., “You are a helpful AI bot that speaks like a pirate.”).
  • human or user: This represents a message from the end-user.
  • ai or assistant: This represents a previous response from the AI. It’s essential for providing few-shot examples or building a conversation history.

The content of each message can be a static string or a template string with {placeholders} for dynamic values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

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}"),
    ]
)

# Use .invoke() with a dictionary to fill in the placeholders
prompt_value = template.invoke(
    {
        "name": "Bob",
        "user_input": "What is your name?",
    }
)

print(prompt_value.to_messages())

When you run this, the output isn’t just a string; it’s a ChatPromptValue object containing a list of structured Message objects, ready to be sent to a chat model:

1
2
3
4
5
6
[
    SystemMessage(content='You are a helpful AI bot. Your name is Bob.'),
    HumanMessage(content='Hello, how are you doing?'),
    AIMessage(content="I'm doing well, thanks!"),
    HumanMessage(content='What is your name?')
]

Building Conversational Memory with MessagesPlaceholder

In a real chatbot, you can’t hardcode the conversation history. You need a way to dynamically insert a list of past messages into your prompt. This is exactly what MessagesPlaceholder is for.

It creates a slot in your template where you can inject a list of messages.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a helpful AI bot."),
        # The placeholder will be replaced by a list of messages
        MessagesPlaceholder(variable_name="conversation"),
        ("human", "{user_input}"),
    ]
)

# Now, we can pass a list of messages to the "conversation" variable
prompt_value = template.invoke(
    {
        "conversation": [
            ("human", "Hi!"),
            ("ai", "How can I assist you today?"),
        ],
        "user_input": "Can you make me an ice cream sundae?"
    }
)

print(prompt_value.to_messages())

This is the standard pattern for building conversational applications. After each turn, you append the user’s message and the AI’s response to your history list and pass it into the MessagesPlaceholder for the next turn.

A Convenient Shortcut for Single-Input Prompts

LangChain provides a handy quality-of-life feature for simple prompts. If your template contains exactly one input variable, you can call .invoke() with just a string value instead of a dictionary. LangChain will automatically assign that string to the single variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from langchain_core.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a helpful AI bot. Your name is Carl."),
        ("human", "{user_input}"),
    ]
)

# This is a shortcut...
prompt_value = template.invoke("Hello, there!")

# ...and is equivalent to this:
# prompt_value = template.invoke({"user_input": "Hello, there!"})

print(prompt_value.to_messages())

Output:

1
2
3
4
[
    SystemMessage(content='You are a helpful AI bot. Your name is Carl.'),
    HumanMessage(content='Hello, there!'),
]

Putting It All Together in a Chain

The true power of prompt templates is realized when you chain them with a model and an output parser using the LangChain Expression Language (LCEL).

This complete example shows the end-to-end flow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# 1. Create the prompt template
template = ChatPromptTemplate.from_messages([
    ("system", "You are a poetic assistant, skilled in explaining complex programming concepts with creative flair."),
    ("human", "Compose a short poem about {topic}.")
])

# 2. Initialize the model and output parser
model = ChatOpenAI(model="gpt-3.5-turbo")
output_parser = StrOutputParser()

# 3. Build the chain using LCEL
chain = template | model | output_parser

# 4. Invoke the chain
response = chain.invoke({"topic": "Recursion"})

print(response)

Conclusion

The ChatPromptTemplate is an essential tool for working with modern chat models. It provides a powerful and flexible way to structure conversations, inject dynamic data, and manage conversation history. By mastering chat prompts, you are taking a critical step toward building sophisticated, reliable, and creative conversational AI applications with LangChain.

This post is licensed under CC BY 4.0 by the author.