官方文档中定义的Model I/O:Prompts、Chat Models、LLMs和Output Parsers。这4个基本组件,可以处理基本的用户输入并通过大模型处理后按要求输出。这一章主要介绍第一个组件:Prompts,即用户输入的提示语。
Prompt Templates 提示模板
提示模板有助于将用户输入和参数转换为语言模型的说明。这可用于指导模型的响应,帮助它理解上下文并生成相关且连贯的基于语言的输出。Prompt Templates 将字典作为输入,其中每个键代表 Prompt 模板中要填写的变量。
提示模板输出 PromptValue。此 PromptValue 可以传递给 LLM 或 ChatModel,也可以转换为字符串或消息列表。此 PromptValue 存在的原因是为了便于在字符串和消息之间切换。
在实际应用中,LLM应用并不直接处理用户的输入,这是因为用户的输入大多数情况并不全面,或者并不规范。所以在实际开发中会使用提示语模板(Prompt Templates),将用户的输入作为其中的一部分填入模板产生最终的提示语后再输入给大模型进行处理。
有几种不同类型的提示模板:
StringPromptTemplates
这些提示模板用于格式化单个字符串,通常用于更简单的输入。例如,构造和使用 PromptTemplate 的常用方法如下所示:
ini
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template("Tell me a joke about {topic}")
result = prompt_template.invoke({"topic": "cats"})
print(result)
输出结果:
ini
text='Tell me a joke about cats'
ChatPromptTemplates
这些提示模板用于设置消息列表的格式。这些 "模板" 由模板本身的列表组成。例如,构造和使用 ChatPromptTemplate 的常用方法如下:
ini
from langchain_core.prompts import ChatPromptTemplate
chat_template = ChatPromptTemplate([
("system", "你是世界级的技术专家,请根据以下问题,回答最详细的答案。"),
("user", "告诉我一些关于{topic}的知识")])
result = chat_template.invoke({"topic": "Android开发"})
print(result)
输出内容:
ini
messages=[SystemMessage(content='你是世界级的技术专家,请根据以下问题,回答最详细的答案。', additional_kwargs={}, response_metadata={}), HumanMessage(content='告诉我一些关于Android开发的知识', additional_kwargs={}, response_metadata={})]
也有另外一种使用方式:
ini
system_msg = SystemMessage(content="你是世界级的技术专家,请根据以下问题,回答最详细的答案。")
human_msg = HumanMessagePromptTemplate.from_template("{question}")
# 组合成完整模板
chat_template = ChatPromptTemplate.from_messages([system_msg, human_msg])
messages = chat_template.format_messages(question="如何使用langchain?")
print(messages)
MessagesPlaceholder
此提示模板负责在特定位置添加消息列表。在上面的 ChatPromptTemplate 中,我们看到了如何格式化两条消息,每条消息都是一个字符串。但是,如果我们希望用户传入一个消息列表,我们将将其放入特定位置,该怎么办?这时应该使用 MessagesPlaceholder 的方式。
css
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.messages import HumanMessageprompt_template = ChatPromptTemplate([ ("system", "You are a helpful assistant"), MessagesPlaceholder("msgs")])prompt_template.invoke({"msgs": [HumanMessage(content="hi!"), HumanMessage(content="how are you?")]})
输出内容:
messages=[SystemMessage(content='You are a helpful assistant', additional_kwargs={}, response_metadata={}), HumanMessage(content='hi!', additional_kwargs={}, response_metadata={}), HumanMessage(content='how are you?', additional_kwargs={}, response_metadata={})]
给Prompt提供样例
为了生成更加精准的结果,在输入的时候可以提供样例(examples),提示大模型按照样例形式给出相应内容。FewShotPromptTemplate用于进行带样例提示语的封装,主要参数有:
examples: 提供样例。
example_prompt:样例提示语的模板,该模板中的input_variables定义了template中使用的变量。
prefix:在样例提示语之前的自定义内容。
suffix:在样例提示语后的自定义内容。 i
nput_variables:该带样例的提示语中用到的变量。
示例代码:
ini
from langchain_ollama import OllamaLLM
from langchain_core.prompts.few_shot import FewShotPromptTemplate
from langchain_core.prompts.prompt import PromptTemplate
# template参数提供样例模板, input_variables指定模板中期望的变量名称列表
example_prompt = PromptTemplate(
input_variables=["question", "answer"], template="Question: {question}\n{answer}"
)
# 提供的样例列表
examples = [
{
"question": "Who lived longer, Muhammad Ali or Alan Turing?",
"answer": """
Are follow up questions needed here: Yes.
Follow up: How old was Muhammad Ali when he died?
Intermediate answer: Muhammad Ali was 74 years old when he died.
Follow up: How old was Alan Turing when he died?
Intermediate answer: Alan Turing was 41 years old when he died.
So the final answer is: Muhammad Ali
""",
},
{
"question": "When was the founder of craigslist born?",
"answer": """
Are follow up questions needed here: Yes.
Follow up: Who was the founder of craigslist?
Intermediate answer: Craigslist was founded by Craig Newmark.
Follow up: When was Craig Newmark born?
Intermediate answer: Craig Newmark was born on December 6, 1952.
So the final answer is: December 6, 1952
""",
},
{
"question": "Who was the maternal grandfather of George Washington?",
"answer": """
Are follow up questions needed here: Yes.
Follow up: Who was the mother of George Washington?
Intermediate answer: The mother of George Washington was Mary Ball Washington.
Follow up: Who was the father of Mary Ball Washington?
Intermediate answer: The father of Mary Ball Washington was Joseph Ball.
So the final answer is: Joseph Ball
""",
},
{
"question": "Are both the directors of Jaws and Casino Royale from the same country?",
"answer": """
Are follow up questions needed here: Yes.
Follow up: Who is the director of Jaws?
Intermediate Answer: The director of Jaws is Steven Spielberg.
Follow up: Where is Steven Spielberg from?
Intermediate Answer: The United States.
Follow up: Who is the director of Casino Royale?
Intermediate Answer: The director of Casino Royale is Martin Campbell.
Follow up: Where is Martin Campbell from?
Intermediate Answer: New Zealand.
So the final answer is: No
""",
},
]
# 定义带少样例(few-shot)的提示语模板
prompt = FewShotPromptTemplate(
examples=examples, # 提供样例
example_prompt=example_prompt, # 提示语模板
prefix="Answer questions like examples below:", # 提示语之前的自定义内容
suffix="Question: {input}", # 提示语最后的输入内容模板
input_variables=["input"],
)
# 初始化打模型
model = OllamaLLM(model="deepseek-r1:14b", temperature=0)
chain = prompt | model
# 传入input生成最终的提示语并调用chain
print(chain.invoke({"input": "Who was the father of Mary Ball Washington?"}))
打印结果:
挑选需要的样例
由于各种原因,我们可能需要从提供的样例中挑选合适的部分内容填入提示语。LangChain提供了样例选择器(Example Selector)用来选择合乎要求的内容,BaseExampleSelector主要定义了两个抽象方法:
- add_example:添加字典类型的候选样例。
- select_examples:通过定义的规则选择合适的样例。
LangChain提供了一些常用的选择类:
-
LengthBasedExampleSelector:根据样例的长度选择样例,确保所选样例的总长度不超过指定的最大长度限制。
-
SemanticSimilarityExampleSelector:根据样例与输入的语义相似度选择最相关的样例。它使用向量存储(如Chroma)来存储样例的嵌入向量,并基于余弦相似度进行匹配。
-
MaxMarginalRelevanceExampleSelector:根据样例与输入的语义相似度选择最相关的样例,同时还考虑了样例之间的多样性。与SemanticSimilarityExampleSelector不同的地方是MaxMarginalRelevanceExampleSelector通过最大边际相关性(MMR)算法惩罚与已选择样例过于相似的样例来达到所选样例多样性的目的。
-
NGramOverlapExampleSelector:根据样例与输入的n-gram重叠分数(0-1之间的相似度分数)选择和排序样例。
以长度挑选样例的LengthBasedExampleSelector举例来说,代码如下:
ini
from langchain_core.example_selectors import LengthBasedExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 提供输入的样例
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
example_selector = LengthBasedExampleSelector(
# 传入样例
examples=examples,
# 传入样例模板
example_prompt=example_prompt,
# 样例的最大长度,由LengthBasedExampleSelector的get_text_length计算长度。不超过max_length长度情况下,用例被选入。
max_length=20,
)
# FewShotPromptTemplate中传入example_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"],
)
print(dynamic_prompt.format(adjective="big"))
输出结果:
vbnet
Give the antonym of every input
Input: happy
Output: sad
Input: tall
Output: short
Input: energetic
Output: lethargic
Input: sunny
Output: gloomy
Input: big
Output: