← Writing
AI & Generative AI

Unpacking Prompt Templates with LangChain

Data Mastery Series — Episode 27: LangChain Website (Part 2)

9 Oct 202413 min readLangChainDashboard
LangChain Series · Part 19 of 19

Unpacking Prompt Templates with LangChain

Data Mastery Series — Episode 27: LangChain Website (Part 2)

Connect with me and follow our journey: Linkedin, Facebook


Hey everyone! Welcome back to the Data Mastery Series! We’re continuing our Generative AI adventure with LangChain. If you’re just joining us, make sure to check out Part 1 here:

Note: As we dive into LangChain, I’ll be sharing insights and key notes from my own study of the LangChain documentation. Let’s jump in and explore some fascinating features in today’s episode!

Source: https://python.langchain.com/v0.1/docs/modules/model%5Fio/prompts/

Today, we’re diving into the heart of AI communication: Prompt. Think of prompts like instructions you give to a helpful robot. The clearer your instructions, the better the robot understands what you want. LangChain provides some handy tools called Prompt Templates to make this whole process easier. Let’s explore!

A. Quick Reference:

1. Your Basic Prompt Recipes

LangChain offers two main types of prompt templates:

  • PromptTemplate: In real-world applications, prompts need to be task-specific. With PromptTemplate, you can create dynamic prompts that include user input without showing the full prompt. For example, if you want the AI to tell a joke, you could create a template like "Tell me a {adjective} joke about {content}." Then, you simply plug in user inputs for the adjectives and content.

Example - PromptTemplate

prompt_template = PromptTemplate.from_template("Tell me a {adjective} joke about {content}.")
result = prompt_template.format(adjective="funny", content="chickens")
result

Output

'''
Tell me a funny joke about chickens.
'''

You can also create a simpler prompt without variables:

Example - Without PromptTemplate

prompt_template = PromptTemplate.from_template("Tell me a joke")
prompt_template.format()

Output

'''
Tell me a joke
'''

  • ChatPromptTemplate: This one’s perfect for chatbots! It helps you create back-and-forth conversations by assigning roles like “system” (the AI’s personality), “human” (the user), and “ai” (the AI’s responses). Think of it like writing a script for a play.

Example - ChatPromptTemplate

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

messages = chat_template.format_messages(name="Bob", user_input="What is your name?")
messages

Output

'''
[SystemMessage(content='You are a helpful AI bot. Your name is Bob.', additional_kwargs={}, response_metadata={}),
HumanMessage(content='Hello, how are you doing?', additional_kwargs={}, response_metadata={}),
AIMessage(content="I'm doing well, thanks!", additional_kwargs={}, response_metadata={}),
HumanMessage(content='What is your name?', additional_kwargs={}, response_metadata={})]
Tell me a funny joke about chickens.
'''

This example shows how you can structure a conversation with multiple messages, each with a specified role, and the ChatPromptTemplate.from_messages function simplifies creating complex, multi-message interactions. For even more customization, you can combine ChatPromptTemplate with other objects like MessagePromptTemplate to use a template for a more specific task.

Example - OpenAI

chat_template = ChatPromptTemplate.from_messages(
[
SystemMessage(
content=(
"You are a helpful assistant that re-writes the user's text to "
"sound more upbeat."
)
),
HumanMessagePromptTemplate.from_template("{text}"),
]
)
messages = chat_template.format_messages(text="I don't like eating tasty things")
print(messages)

Output

'''
[SystemMessage(content="You are a helpful assistant that re-writes the user's text to sound more upbeat.", additional_kwargs={}, response_metadata={}),
HumanMessage(content="I don't like eating tasty things", additional_kwargs={}, response_metadata={})]
'''

2. Message Prompts: Adding Flexibility to the Conversation

Once you’ve grasped the basics of prompts, you can enhance your interaction with Message Templates, which allow you to customize messages for different roles. Let’s dive into two types of templates that add more flexibility to your prompts.

  • ChatMessagePromptTemplate: This lets you create messages with custom roles beyond the standard “system,” “human,” and “AI.” Imagine you want your AI to act like a Jedi. You could set the role as “Jedi” and use a template like “May the {subject} be with you.”

Example - ChatMessagePromptTemplate

prompt = "May the {subject} be with you"

chat_message_prompt = ChatMessagePromptTemplate.from_template(
role="Jedi", template=prompt
)
chat_message_prompt.format(subject="force")

Output

'''
ChatMessage(content='May the force be with you', additional_kwargs={}, response_metadata={}, role='Jedi')
'''

  • MessagesPlaceholder: Imagine inserting a whole chunk of conversation into a prompt. That’s what MessagesPlaceholder lets you do, making prompts dynamic and adaptable.

Example - MessagesPlaceholder

human_prompt = "Summarize our conversation so far in {word_count} words."
human_message_template = HumanMessagePromptTemplate.from_template(human_prompt)

chat_prompt = ChatPromptTemplate.from_messages(
[MessagesPlaceholder(variable_name="conversation"), human_message_template]
)

human_message = HumanMessage(content="What is the best way to learn programming?")
ai_message = AIMessage(
content="""\

  1. Choose a programming language: Decide on a programming language that you want to learn.
  2. Start with the basics: Familiarize yourself with the basic programming concepts such as variables, data types and control structures.
  3. Practice, practice, practice: The best way to learn programming is through hands-on experience\
    """
    )

chat_prompt.format_prompt(
conversation=[human_message, ai_message], word_count="10"
).to_messages()

Output

'''
[HumanMessage(content='What is the best way to learn programming?', additional_kwargs={}, response_metadata={}),
AIMessage(content='1. Choose a programming language: Decide on a programming language that you want to learn.\n\n2. Start with the basics: Familiarize yourself with the basic programming concepts such as variables, data types and control structures.\n\n3. Practice, practice, practice: The best way to learn programming is through hands-on experience', additional_kwargs={}, response_metadata={}),
HumanMessage(content='Summarize our conversation so far in 10 words.', additional_kwargs={}, response_metadata={})]"
'''

3. LCEL: Making Prompts Dynamic and Powerful

LangChain Expression Language (LCEL) adds a layer of customization to prompts, making them not only flexible but also powerful. Here’s a quick example of how LCEL can make prompts dynamic:

Example - LCEL

chat_template = ChatPromptTemplate.from_messages(
[
SystemMessage(
content=(
"You are a helpful assistant that re-writes the user's text to "
"sound more upbeat."
)
),
HumanMessagePromptTemplate.from_template("{text}"),
]
)

chat_val = chat_template.invoke({"text": "i dont like eating tasty things."})
chat_val.to_messages()

Output

'''
[SystemMessage(content="You are a helpful assistant that re-writes the user's text to sound more upbeat.", additional_kwargs={}, response_metadata={}),
HumanMessage(content='i dont like eating tasty things.', additional_kwargs={}, response_metadata={})]
'''

Example - LCEL (continues)

chat_val.to_string()

Output

'''
System: You are a helpful assistant that re-writes the user's text to sound more upbeat.
Human: i dont like eating tasty things.
'''


B. Example Selectors: Controlling Prompt Length

In certain situations, it’s essential to control the length and relevance of the input to a language model. LangChain provides various example selectors to help manage this, each with a unique approach. Today, we’ll explore four of these methods:

  • 1) Length-Based Selector
    The Length-Based Example Selector picks examples based on token count, ensuring that the prompt stays within a specific length.

Example - Length-Based

List of examples for a task, such as creating antonyms:

examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]

Define the template for examples

example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)

Initialize the Length-Based Example Selector with a maximum length

example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=25, # Maximum allowed length for the examples
)

Define the main prompt template with the selector

dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="Give the antonym of every input",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)

Format the prompt with a sample input

formatted_prompt = dynamic_prompt.format(adjective="big")
print("Formatted Prompt:", formatted_prompt)

Output Example

'''
Formatted Prompt: Give the antonym of every input

Input: happy
Output: sad

Input: tall
Output: short

Input: energetic
Output: lethargic

Input: sunny
Output: gloomy

Input: windy
Output: calm

Input: big
Output: small

Input: big
Output:
'''

The Length-Based Example Selector carefully selects examples that fit within the specified length, providing a concise list of antonyms for shorter inputs. As the input length increases, the selector dynamically adjusts by removing examples to keep the prompt within the allowed token count. Let’s explore how it performs with a longer input string.

Example with a longer input string

long_string = "big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else"
long_formatted_prompt = dynamic_prompt.format(adjective=long_string)
print("Formatted Prompt:", long_formatted_prompt)

Output Example

'''
Formatted Prompt: Give the antonym of every input

Input: happy
Output: sad

Input: big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else
Output:
'''

  • 2) Maximal Marginal Relevance (MMR) Selector
    MMR Selector balances similarity and diversity, ensuring that selected examples are relevant but varied.

Example - MaxMarginalRelevanceExampleSelector

example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)

examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]

example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
FAISS,
k=2,
)

mmr_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="Give the antonym of every input",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)

print(mmr_prompt.format(adjective="worried"))

Output Example

'''
Give the antonym of every input

Input: happy
Output: sad

Input: windy
Output: calm

Input: worried
Output:
'''

The MaxMarginalRelevanceExampleSelector selects examples by balancing similarity to the input with diversity among the chosen examples. In the example above, when we input “worried,” the MMR selector identifies “happy/sad” as the closest match and “windy/calm” for added diversity. If we want to focus purely on similarity, we can use Semantic Similarity instead, as shown in the code below.

Example - SemanticSimilarityExampleSelector

example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY),
FAISS,
k=2,
)

similar_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="Give the antonym of every input",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
)

print(mmr_prompt.format(adjective="worried"))

Output Example

'''
Give the antonym of every input

Input: happy
Output: sad

Input: sunny
Output: gloomy

Input: worried
Output:
'''

  • 3) N-Gram Overlap Selector:
    The N-Gram Overlap Selector picks examples based on shared word sequences (n-grams) with the input. By adjusting the threshold, you can control which examples are included.

Example - SemanticSimilarityExampleSelector

example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)

examples = [
{"input": "See Spot run.", "output": "Ver correr a Spot."},
{"input": "My dog barks.", "output": "Mi perro ladra."},
{"input": "Spot can run.", "output": "Spot puede correr."},
]

example_selector = NGramOverlapExampleSelector(
examples=examples,
example_prompt=example_prompt,
threshold=-1.0,
)

dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="Give the Spanish translation of every input",
suffix="Input: {sentence}\nOutput:",
input_variables=["sentence"],
)

print(dynamic_prompt.format(sentence="Spot can play fetch.")) #Spot น่าจะเป็นชื่อสุนัข

Output Example

'''
Give the Spanish translation of every input

Input: Spot can run.
Output: Spot puede correr.

Input: See Spot run.
Output: Ver correr a Spot.

Input: My dog barks.
Output: Mi perro ladra.

Input: Spot can play fetch.
Output:
'''

In the example above, with a threshold set to -1.0, no examples are excluded; they are simply ranked by similarity. To observe how changing the threshold impacts the results, we’ll gradually increase it step-by-step.

Example - threshold = 0.0

example_selector.threshold = 0.0
print(dynamic_prompt.format(sentence="Spot can run fast."))

Output Example

'''
Give the Spanish translation of every input

Input: Spot can run.
Output: Spot puede correr.

Input: See Spot run.
Output: Ver correr a Spot.

Input: Spot can play fetch.
Output:
'''
################################################################

Example - threshold = 0.09

example_selector.threshold = 0.09
print(dynamic_prompt.format(sentence="Spot can play fetch."))

Output Example

'''
Give the Spanish translation of every input

Input: Spot can run.
Output: Spot puede correr.

Input: Spot can play fetch.
Output:
'''
################################################################

Example - threshold = 1.0 + 1e-9

example_selector.threshold = 1.0 + 1e-9
print(dynamic_prompt.format(sentence="Spot can play fetch."))

Output Example

'''
Give the Spanish translation of every input

Input: Spot can play fetch.
Output:
'''
################################################################

  • Threshold = 0.0: The example “My dog barks” is removed, as it has no overlap with the input.
  • Threshold = 0.09: Both “My dog barks” and “See Spot run” are excluded.
  • Threshold = 1.0 + 1e-9: All examples, including “My dog barks,” “See Spot run,” and “Spot can run,” are removed.

The N-Gram Overlap Selector orders or excludes examples based on similarity to the input, using n-gram overlap as a measure. By adjusting the threshold, you can control which examples are included or excluded based on their similarity to the input.

  • 4) Similarity-Based Selector
    The Similarity-Based Example Selector focuses on semantic similarity, finding examples closely aligned with the input’s meaning.

Example - SemanticSimilarityExampleSelector

example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)

examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]

example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
Chroma,
k=1,
)

similar_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="Give the antonym of every input",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)

print(similar_prompt.format(adjective="worried"))

Output Example

'''
Give the antonym of every input

Input: happy
Output: sad

Input: worried
Output:
'''

In the example above, when the input is “worried,” the selector identifies “happy/sad” as the most semantically similar match.

With LangChain’s Example Selectors, you can tailor prompts to fit your needs by controlling length, ensuring diversity, and selecting relevant examples. Next, we’ll dive into Few-Shot Prompts and Partial Prompts in our exploration of LangChain. Thanks for reading, and happy prompt crafting!

Thank you for joining me, and happy prompt crafting!


Data Science Explore the world of data science with Donato_Story

Dashboard Discover the power of data visualization with Donato_Story

Donato_Journey Join me on my journey (Thai version)

Course_Review Discover the training courses with Donato_Story (Thai version)

I really value your thoughts and feedback. Please share any comments or questions you have below, or reach out to me on:

Medium: medium.com/donato-story
Facebook: web.facebook.com/DonatoStory
Linkedin: linkedin.com/in/nattapong-thanngam

Originally published on Medium

Related