文章目录
少样本提示(few-shorting)
概念
少样本提示是一种通过向LLM提供少量具体示例或样本,来教会它如何执行某项特定任务的技术。提高模型性能的最有效方法之一是给出一个【模型示例】指导大模型你想做什么、怎么做。下面用一个例子解释少样本提示的作用。
LLM 虽然知识渊博,但有时我们需要它以非常特定的格式、风格或逻辑来回答问题。提供正确的示例可以减少模型"胡说八道"或犯低级错误的概率,将其输出约束在你提供的范例范围内。举个例子更能理解:
强制要求模型以特定的格式(如JSON、XML、特定的列表样式)输出结果。样例可以当作格式样板。
有些任务很难用文字指令清晰描述(例如:"请用莎士比亚的风格写作")。提供几个例子比写长篇大论的指令更有效。
对于需要多步推理的复杂任务,示例可以展示出思考链,引导模型遵循类似的推理路径。
实现少样本提示
实现少样本提示的第一步也是最重要的一步是提出一个好的示例数据集。好的示例应该在运行时相关、清晰、信息丰富,并提供模型尚不知道的信息。如我们给出的例子,就能很好的提示大模型与+含义相似:
python
examples = [
{"input": "2 2", "output": "4"},
{"input": "2 3", "output": "5"}
]
如何让大模型看懂这份示例呢?聊天模型读的是聊天消息。因此,接下来我们需要将示例集实例化成聊天模型可以读懂的聊天消息。对于LangChain就需要创建一个
FewShotChatMessagePromptTemplate对象来实例化示例集。
FewShotChatMessagePromptTemplate是一个提示词模板,专门用来将示例集实例化为聊天消息,用法如下
python
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate
example_prompt = ChatPromptTemplate(
[
("human", "{input}"),
("ai", "{output}"),
]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
example_prompt=example_prompt, # ChatPromptTemplate,用于格式化单个示例
examples=examples, # 样本示例
)
print(few_shot_prompt.invoke({}).to_messages())
可以得到示例集转化的聊天消息列表,结果如下:
python
[
HumanMessage(content='2 2', additional_kwargs={}, response_metadata={}),
AIMessage(content='4', additional_kwargs={}, response_metadata={}),
HumanMessage(content='2 3', additional_kwargs={}, response_metadata={}),
AIMessage(content='5', additional_kwargs={}, response_metadata={})
]
先来看看class 'langchain_core.prompts.few_shot.FewShotChatMessagePromptTemplate,它也实现了标准的Runnable接口。
类初始化参数说明:
examples:样本示例。example_prompt:ChatPromptTemplate,用于格式化单个示例
类方法说明:
- invoke() 方法:此方法与其他 Runnable 实例的 invoke() 方法类似。输入一个字典给它,返回完整的提示内容 PromptValue:
- PromptValue 的 to_string() 方法可以将提示值作为【字符串】返回。
- PromptValue 的 to_messages() 方法可以将提示作为【消息列表】返回。
最后,得到示例集消息列表后,就可以带上一起发起请求:
python
final_prompt = ChatPromptTemplate(
[
("system", "你是一个神奇的数学奇才。"),
few_shot_prompt,
("human", "{input}"),
]
)
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")
chain = final_prompt | model
chain.invoke({"input": "What is 2 9?"}).pretty_print()
输出结果为 :
python
= Ai Message
11
通过少样本提示 LLM知道中间的空格是 + 的意思,进行了加法运算
使用案例
案例一:推理引导
我们希望输入:
《教父》和《星球大战》的导演来自同一个国家吗?
让聊天模型可以先分析再得出结论,而不是直接得出结论。分析过程需要展示出来,示例如下:
python
from langchain_core.prompts import PromptTemplate
# 创建字符串模板
example_prompt = PromptTemplate.from_template("Question: {question}\n{answer}")
# 创建示例集
examples = [
{
"question": "李白和杜甫,谁更长寿?",
"answer":
"""是否需要后续问题:是的。
后续问题:李白享年多少岁?
中间答案:李白享年61岁。
后续问题:杜甫享年多少岁?
中间答案:杜甫享年58岁。
所以最终答案是:李白"""
},
{
"question": "腾讯的创始人什么时候出生?",
"answer":
"""是否需要后续问题:是的。
后续问题:腾讯的创始人是谁?
中间答案:腾讯由马化腾创立。
后续问题:马化腾什么时候出生?
中间答案:马化腾出生于1971年10月29日。
所以最终答案是:1971年10月29日"""
},
{
"question": "孙中山的外祖父是谁?",
"answer":
"""是否需要后续问题:是的。
后续问题:孙中山的母亲是谁?
中间答案:孙中山的母亲是杨太夫人。
后续问题:杨太夫人的父亲是谁?
中间答案:杨太夫人的父亲是杨胜辉。
所以最终答案是:杨胜辉"""
},
{
"question": "电影《红高粱》和《霸王别姬》的导演来自同一个国家吗?",
"answer":
"""是否需要后续问题:是的。
后续问题:《红高粱》的导演是谁?
中间答案:《红高粱》的导演是张艺谋。
后续问题:张艺来自哪里?
中间答案:中国。
后续问题:《霸王别姬》的导演是谁?
中间答案:《霸王别姬》的导演是陈凯歌。
后续问题:陈凯歌来自哪里?
中间答案:中国。
所以最终答案是:是"""
}
]
# 测试:实例化第一个示例
print(example_prompt.invoke(examples[0]).to_string())
结果输出:
python
Question: 李白和杜甫,谁更长寿?
是否需要后续问题:是的。
后续问题:李白享年多少岁?
中间答案:李白享年61岁。
后续问题:杜甫享年多少岁?
中间答案:杜甫享年58岁。
所以最终答案是:李白
接下来我们需要格式化完整的样本提示。此时可以创建一个FewShotPromptTemplate对象来初始化少样本提示模板。其接收少量示例和少量示例的格式化程序
python
from langchain_core.prompts import FewShotPromptTemplate
prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt, # PromptTemplate,用于格式化单个示例
suffix="Question: {input}", # 放在示例之后的提示模板字符串。
input_variables=["input"], # 变量的名称列表,这些变量的值需要作为提示词的输入。
)
print(
prompt.invoke({"input": "《教父》和《星球大战》的导演来自同一个国家吗?"}).to_string()
)
通过为模型提供这样的示例,我们可以引导模型做出更好的响应。
来看下class langchain_core.prompts.few_shot.FewShotPromptTemplate,它也实现了标准的Runnable接口。
类初始化参数说明:
examples:样本示例。example_prompt:PromptTemplate,用于格式化单个示例prefix:放在示例前面的提示模板字符串。suffix:放在示例之后的提示模板字符串。input_variables:变量的名称列表,这些变量的值需要作为提示词的输入。
类方法说明:
invoke() 方法:此方法与其他 Runnable 实例的 .invoke() 方法类似。输入一个字典给提示符模板,返回完整的提示内容 PromptValue:
- PromptValue 的 to_string() 方法可以将提示值作为【字符串】返回。
- PromptValue 的 to_messages() 方法可以将提示作为【消息列表】返回。
python
from langchain_core.prompts import PromptTemplate
from langchain_core.prompts import FewShotPromptTemplate
from langchain_openai import ChatOpenAI
# 创建字符串模板
example_prompt = PromptTemplate.from_template("Question: {question}\n{answer}")
# 创建示例集
examples = [
{
"question": "李白和杜甫,谁更长寿?",
"answer":
"""是否需要后续问题:是的。
后续问题:李白享年多少岁?
中间答案:李白享年61岁。
后续问题:杜甫享年多少岁?
中间答案:杜甫享年58岁。
所以最终答案是:李白"""
},
{
"question": "腾讯的创始人什么时候出生?",
"answer":
"""是否需要后续问题:是的。
后续问题:腾讯的创始人是谁?
中间答案:腾讯由马化腾创立。
后续问题:马化腾什么时候出生?
中间答案:马化腾出生于1971年10月29日。
所以最终答案是:1971年10月29日"""
},
{
"question": "孙中山的外祖父是谁?",
"answer":
"""是否需要后续问题:是的。
后续问题:孙中山的母亲是谁?
中间答案:孙中山的母亲是杨太夫人。
后续问题:杨太夫人的父亲是谁?
中间答案:杨太夫人的父亲是杨胜辉。
所以最终答案是:杨胜辉"""
},
{
"question": "电影《红高粱》和《霸王别姬》的导演来自同一个国家吗?",
"answer":
"""是否需要后续问题:是的。
后续问题:《红高粱》的导演是谁?
中间答案:《红高粱》的导演是张艺谋。
后续问题:张艺来自哪里?
中间答案:中国。
后续问题:《霸王别姬》的导演是谁?
中间答案:《霸王别姬》的导演是陈凯歌。
后续问题:陈凯歌来自哪里?
中间答案:中国。
所以最终答案是:是"""
}
]
prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
suffix="Question: {input}",
input_variables=["input"],
)
# 生成[HumanMessage]消息列表
prompt_messages = prompt.invoke({"input": "《教父》和《星球大战》的导演来自同一个国家吗?"}).to_messages()
model = ChatOpenAI(model="gpt-4o-mini")
model.invoke(prompt_messages).pretty_print()
结果输出:
python
========== Ai Message
是否需要后续问题:是的。
后续问题:《教父》的导演是谁?
中间答案:《教父》的导演是弗朗西斯·福特·科波拉。
后续问题:弗朗西斯·福特·科波拉来自哪里?
中间答案:弗朗西斯·福特·科波拉来自美国。
后续问题:《星球大战》的导演是谁?
中间答案:《星球大战》的导演是乔治·卢卡斯。
后续问题:乔治·卢卡斯来自哪里?
中间答案:乔治·卢卡斯也来自美国。
所以最终答案是:是
案例二:使用示例数据增强LangChain信息提取能力
我们来实现一个基于LangChain的结构化信息提取系统,专门从文本中提取人物相关信息。
例如我们希望,对于输入以下文本:
"篮球场上,身高两米的中锋王伟默契地将球传给一米七的后卫挚友李明,完成一记绝杀。这对老友用十年配合弥补了身高的差距。"
代码会提取出结构化数据,如下所示:
python
people=[
Person(name="王伟", height_in_meters="2", skin_color=None, hair_color=None),
Person(name="李明", height_in_meters="1.7", skin_color=None, hair_color=None)
]
第一步:定义结构化返回对象
python
from typing import List, Optional
from pydantic import BaseModel, Field
class Person(BaseModel):
"""一个人的信息。"""
name: Optional[str] = Field(default=None, description="这个人的名字")
hair_color: Optional[str] = Field(default=None, description="如果知道这个人头发的颜色")
skin_color: Optional[str] = Field(default=None, description="如果知道这个人的肤色")
height_in_meters: Optional[str] = Field(default=None, description="以米为单位的高度")
class Data(BaseModel):
"""提取关于人的数据。"""
people: List[Person]
第二步:定义两个关键示例,每个示例中包含**【文本】和【希望输出】**:
-
无人物场景:文本中没有人名,期望返回空列表
-
部分信息场景:只有名字,其他字段缺失
python
# 定义2个示例
examples = [
(
"海洋是广阔而蓝色的。它有两万多英尺深。",
Data(people=[]), # 没有人物信息的情况
),
(
"小强从中国远行到美国。",
Data(people=[
Person(name="小强", height_in_meters=None, skin_color=None, hair_color=None),
]), # 部分信息缺失的情况
),
]
第三步:定义提示词模板
python
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, SystemMessage
# 定义提示词模板
prompt_template = ChatPromptTemplate(
[
SystemMessage(content="你是一个提取信息的专家,只从文本中提取相关信息。"
"如果您不知道要提取的属性值,属性值返回null"),
MessagesPlaceholder("example_messages"),
("user", "{new_message}"),
]
)
第四步:构造请求的消息列表。处理逻辑:
- 遍历每个示例对:文本、期望输出
- 根据是否有人员信息生成自然语言响应: "检测到人" or "未检测到人"
- 使用
tool_example_to_messages转换格式,将每个示例转换为模型可理解的消息格式
python
from langchain_core.utils.function_calling import tool_example_to_messages
# 样例消息列表
example_messages = []
# 遍历示例对,将每个示例构造消息
for txt, tool_call in examples:
# 根据提取结果生成AI响应文本
if tool_call.people:
ai_response = "检测到人"
else:
ai_response = "未检测到人"
# 将示例转换为模型可理解的消息格式
example_messages.extend(
tool_example_to_messages(
txt, [tool_call], ai_response=ai_response
) # 方法返回 list[BaseMessage]
)
tool_example_to_messages 方法说明:
- 此功能处于测试阶段。它正在积极开发中,因此API可能会发生变化。
- 此方法是一个工具方法,它可以将单个示例转换为聊天模型可以识别的消息列表。
- 参数说明:
input: 输入字符串tool_calls: list[BaseModel],表示为 Pydantic BaseModel 的工具调用列表ai_response: 可选的。如果提供,将是最终 AIMessage 的内容。
- 返回值: list[BaseMessage] 消息列表
python
# 实例化提示词
formatted_prompt = prompt_template.invoke({
"example_messages": example_messages,
"new_message": "篮球场上,身高两米的中锋王伟默契地将球传给一米七的后卫挚友李明,完成一记绝杀。"
"这对老友用十年配合弥补了身高的差距。"
})
# 打印
for message in formatted_prompt.to_messages():
message.pretty_print()
结果输出:
python
= System Message
你是一个提取信息的专家,只从文本中提取相关信息。如果您不知道要提取的属性值,属性值返回null
= Human Message
海洋是广阔而蓝色的。它有两万多英尺深。
= Ai Message
Tool Calls:
Data (f81f0c81-108d-4c06-88dc-9e5f5289749e)
Call ID: f81f0c81-108d-4c06-88dc-9e5f5289749e
Args:
people: []
= Tool Message
You have correctly called this tool.
= Ai Message
= Human Message
小强从中国远行到美国。
= Ai Message
Tool Calls:
Data (ace46c82-07e0-455c-aaef-c13da7328e45)
Call ID: ace46c82-07e0-455c-aaef-c13da73328e45
Args:
people: [{'name': '小强', 'hair_color': None, 'skin_color': None, 'height_in_meters': None}]
= Tool Message
You have correctly called this tool.
= Ai Message
检测到人
= Human Message
篮球场上,身高两米的中锋王伟默契地将球传给一米七的后卫挚友李明,完成一记绝杀。这对老友用十年配合弥补了身高的差距。
消息已经被我们构造成功!可以看到,一个示例经过转换构建出了4条消息:HumanMessage、AiMessage(tool)、ToolMessage、AiMessage。
第五步:初始化模型并绑定结构化输出模式,最后构件件链,并调用。
python
from langchain_openai import ChatOpenAI
# 定义结构化输出模型,自动解析为 Pydantic 对象
model = ChatOpenAI(model="gpt-4o-mini")
structured_model = model.with_structured_output(schema=Data)
chain = prompt_template | structured_model
result = chain.invoke({
"example_messages": example_messages,
"new_message": "篮球场上,身高两米的中锋王伟默契地将球传给一米七的后卫挚友李明,完成一记绝杀。"
"这对老友用十年配合弥补了身高的差距。"
})
print("\n")
print(result)
结果如下:
people=[Person(name='王伟', hair_color=None, skin_color=None, height_in_meters='2.0'), Person(name='李明', hair_color=None, skin_color=None, height_in_meters='1.7')]
完整代码:
python
from langchain_openai import ChatOpenAI
from typing import List, Optional
from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.utils.function_calling import tool_example_to_messages
# 1. 定义结构化输出
class Person(BaseModel):
"""一个人的信息。"""
name: Optional[str] = Field(default=None, description="这个人的名字")
hair_color: Optional[str] = Field(default=None, description="如果知道这个人头发的颜色")
skin_color: Optional[str] = Field(default=None, description="如果知道这个人的肤色")
height_in_meters: Optional[str] = Field(default=None, description="以米为单位的高度")
class Data(BaseModel):
"""提取关于人的数据。"""
people: List[Person]
# 2. 定义2个示例
examples = [
(
"海洋是广阔而蓝色的。它有两万多英尺深。",
Data(people=[]), # 没有人物信息的情况
),
(
"小强从中国远行到美国。",
Data(people=[
Person(name="小强", height_in_meters=None, skin_color=None, hair_color=None),
]), # 部分信息缺失的情况
),
]
# 3. 定义提示词模板
prompt_template = ChatPromptTemplate(
[
SystemMessage(content="你是一个提取信息的专家,只从文本中提取相关信息。"
"如果您不知道要提取的属性值,属性值返回null"),
MessagesPlaceholder("example_messages"),
("user", "{new_message}"),
]
)
# 4. 获取样例消息列表
example_messages = []
# 遍历示例对,将每个示例构造成聊天消息
for txt, tool_call in examples:
# 根据提取结果生成AI响应文本
if tool_call.people:
ai_response = "检测到人"
else:
ai_response = "未检测到人"
# 将示例转换为模型可理解的消息格式
example_messages.extend(
tool_example_to_messages(
txt, [tool_call], ai_response=ai_response
) # 方法返回 list[BaseMessage]
)
# 5. 调用
# 定义结构化输出模型,自动解析为 Pydantic 对象
model = ChatOpenAI(model="gpt-4o-mini")
structured_model = model.with_structured_output(schema=Data)
# 定义链
chain = prompt_template | structured_model
result = chain.invoke({
"example_messages": example_messages,
"new_message": "篮球场上,身高两米的中锋王伟默契地将球传给一米七的后卫挚友李明,完成一记绝杀。"
"这对老友用十年配合弥补了身高的差距。"
})
print("\n")
print(result)