少样本提示
- 前言
- 一、基本概念
- 二、如何实现
-
- (一)FewShotChatMessagePromptTemplate
- (二)具体使用案例
-
- 1、推理引导
- [2、使用示例数据增强 LangChain 信息提取能力](#2、使用示例数据增强 LangChain 信息提取能力)
- 三、示例选择器
-
- [(一)按长度选择示例 (Length)](#(一)按长度选择示例 (Length))
- (二)按语义相似性选择示例
- (三)按最大边际相关性选择示例(MMR)
- [(四)通过 ngram 重叠选择示例(Ngram)](#(四)通过 ngram 重叠选择示例(Ngram))
- 结语
前言
在介绍完提示词模板后, 我们本期将要继续介绍关于Langchain里提示词的少样本提示的内容。
一、基本概念
少样本提示是一·种通过向 LLM 提供少量具体示例或样本,来教会它如何执行某项特定任务的技术。提高模型性能的最有效方法之一是给出一个【模型示例】来指导大模型你想做什么、怎么做。
其相当于在给出考题前,先给它看几道类似的、附有正确答案的例题。添加示例输入和预期输出的技术给到模型提示,让模型通过例题来理解任务应该怎么做。
二、如何实现
(一)FewShotChatMessagePromptTemplate
实现少样本提示的第一步也是最重要的一步是提出一个好的示例数据集。好的示例应该是在运行时相关、清晰、信息丰富,并提供模型尚不知道的信息。
那具体又如何让LLM如何理解以下的代码块,并能这对类似的问题作出正确的回答呢?
python
examples = [
{"input": "2 🦜 2", "output": "4"},
{"input": "2 🦜 3", "output": "5"},
]
因为聊天模型读的是聊天消息。因此,接下来我们需要将示例集实例化成聊天模型可以读懂的聊天消息。此时我们就可以用Langchain中的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())
此时就可得到这样的消息列表:
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={})
现在我们来看一下FewShotChatMessagePromptTemplate 这个类的具体参数。
初始化参数:
| 参数 | 描述 |
|---|---|
| examples | 样本示例 |
| example_prompt | ChatPromptTemplate,用于格式化单个单例 |
方法说明:
| 参数 | 描述 |
|---|---|
| .invoke()方法 | 输入一个字典给,返回完整的提示内容 PromptValue :PromptValue 的 to_string() 方法可以将提提示值作为【字符串】返回。PromptValue 的 to_messages() 方法可以将提示作为【消息列表】返回。 |
此时我们就可以将上面得到的few_shot_prompt消息列表一起发起请求。就可以让LLM得出正确结果。
python
final_prompt = ChatPromptTemplate(
[
("system", "你是⼀个神奇的数学奇才。"),
few_shot_prompt,
("human", "{input}"),
]
)
from langchain_deepseek import ChatDeepSeek
model =ChatDeepSeek(model = "deepseek-flash")
chain = final_prompt | model
chain.invoke({"input": "What is 2 🦜 9?"}).pretty_print()
(二)具体使用案例
1、推理引导
先让LLM理解案例,在案例的引导下,让其能正确地推理出正确结果。
python
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_deepseek import ChatDeepSeek
model = ChatDeepSeek(model = "deepseek-flash")
# 提示词里的逻辑推理
# 文本提示词模板
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": """
是否需要后续问题:是的。
后续问题:《红高粱》的导演是谁?
中间答案:《红高粱》的导演是张艺谋。
后续问题:张艺来自哪里?
中间答案:中国。
后续问题:《霸王别姬》的导演是谁?
中间答案:《霸王别姬》的导演是陈凯歌。
后续问题:陈凯歌来自哪里?
中间答案:中国。
所以最终答案是:是
""",
},
]
# 消息通过提示词模板构建出来
# FewShotPromptTemplate 针对文本、消息
few_shot_prompt = FewShotPromptTemplate(
examples = examples,
example_prompt=example_prompt, # PromptTemplate,用于格式化单个示例
suffix="Question: {input}", # suffix表示放在示例之后的模板字符串
input_variables=["input"], # 输入变量列表
)
chain = few_shot_prompt | model
chain.invoke({"input": "《教父》和《星球大战》的导演是否来自一个国家?"}).pretty_print()
2、使用示例数据增强 LangChain 信息提取能力
在先前关于结构化输出的学习中,我们学会了使用结构化输出进行信息提取。所以在这里我们也可以来实现⼀个基于 LangChain 的结构化信息提取系统,专门从文本中提取人物相关信息。
python
from typing import Optional, List
from langchain_core.messages import SystemMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.utils.function_calling import tool_example_to_messages
from langchain_deepseek import ChatDeepSeek
from pydantic import BaseModel, Field
model = ChatDeepSeek(model = "deepseek-v4-flash")
# 1. 定义结构化输出
class Person(BaseModel):
"""一个人的信息"""
# 注意:
# 1. 每个字段都是 Optional "可选的" ------ 允许 LLM 在不知道答案时输出 None。
# 2. 每个字段都有一个 description "描述" ------ LLM使用这个描述。
# 有一个好的描述可以帮助提高提取结果。
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] = Field(description="人物列表")
# 2. 定义示例(不是 Message)
examples = [
(
"海洋是广阔的、蓝色的。它有两万多英尺深",
Data(people=[])
),
(
"小明在跳舞,1米78的身高看起来很灵活",
Data(people=[
Person(name='小明', hair_color=None, skin_color=None, height_in_meters='1.78'),
]),
)
]
# 3. 定义提示词模板
prompt_template = ChatPromptTemplate(
[
SystemMessage(content="你是一个提取信息的专家,只从文本中提取相关信息。如果您不知道要提取的属性的值,属性值返回null"),
MessagesPlaceholder("example_messages"), # 消息占位符,将示例转换为Message后插入进来
("user", "{new_message}"),
]
)
# 4. 将实例转换为Messages
example_messages = []
for txt,tool_call in examples:
if tool_call.people:
ai_response = "检测到人"
else:
ai_response = "未检测到人"
example_messages.extend(tool_example_to_messages(
txt, # 示例的输入
[tool_call], # (工具 Data(people=[]) 准确的参考标准)
ai_response=ai_response # 让 LLM 强制返回ai_response
))
# print(example_messages)
# # 结果
# [
# HumanMessage(content='海洋是广阔的、蓝色的。它有两万多英尺深', additional_kwargs={}, response_metadata={}),
# AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'e938c190-e7c3-4a5d-ab74-470db300ec50', 'type': 'function', 'function': {'name': 'Data', 'arguments': '{"people":[]}'}}]}, response_metadata={}, tool_calls=[{'name': 'Data', 'args': {'people': []}, 'id': 'e938c190-e7c3-4a5d-ab74-470db300ec50', 'type': 'tool_call'}], invalid_tool_calls=[]),
# ToolMessage(content='You have correctly called this tool.', tool_call_id='e938c190-e7c3-4a5d-ab74-470db300ec50'),
# AIMessage(content='未检测到人', additional_kwargs={}, response_metadata={}, tool_calls=[], invalid_tool_calls=[]),
# HumanMessage(content='小明在跳舞,1米78的身高看起来很灵活', additional_kwargs={}, response_metadata={}),
# AIMessage(content='', additional_kwargs={'tool_calls': [{'id': '17ad246b-f678-4631-8865-c973b2434288', 'type': 'function', 'function': {'name': 'Data', 'arguments': '{"people":[{"name":"小明","hair_color":null,"skin_color":null,"height_in_meters":"1.78"}]}'}}]}, response_metadata={}, tool_calls=[{'name': 'Data', 'args': {'people': [{'name': '小明', 'hair_color': None, 'skin_color': None, 'height_in_meters': '1.78'}]}, 'id': '17ad246b-f678-4631-8865-c973b2434288', 'type': 'tool_call'}], invalid_tool_calls=[]),
# ToolMessage(content='You have correctly called this tool.', tool_call_id='17ad246b-f678-4631-8865-c973b2434288'), AIMessage(content='检测到人', additional_kwargs={}, response_metadata={}, tool_calls=[], invalid_tool_calls=[])
# ]
# 5. 定义的结构化模型
structured_model = model.with_structured_output(schema=Data)
# 6. 定义链(没有强制结构化输出)
chain = prompt_template | model
chain.invoke(
{
"example_messages": example_messages,
"new_message": "篮球场上,身高两米的中锋王伟默契地将球传给一米七的后卫挚友李明,完成一记绝杀。这对老友用十年配合弥补了身高的差距。"
}
).pretty_print()
三、示例选择器
当我们有许多示例时,就需要考虑选择用那些示例放入提示词中给大模型。因为更多的示例通常会提高性能,但更大的提示会增加成本和延迟,如果超过某个阈值,太多提示可能会开始混淆模型。
此时在 LangChain 中,示例选择器就可以帮我们从⼀组【示例的集合】中根据具体策略选择正确的【示例子集】构建少样本提示。其选择策略有:
- Length :根据特定【长度】内可以容纳的数量选择示例。
- Similarity :使用输入和示例之间的【语义相似性】来决定选择哪些示例。
- MMR :使用输入和示例之间的【最⼤边际相关性】来决定要选择哪些示例。
- Ngram :使用输入和示例之间的【ngram 重叠】来决定要选择哪些示例。
(一)按长度选择示例 (Length)
在担心构造提提示时,将超过上下文窗口长度,根据特定长度内可以容纳的数量选择示例。对于较长的输入,它将选择更少的示例来包含;而对于较短的输入,它将选择更多示例。
这时候就要用到LengthBasedExampleSelector 这一类模板了,其还有内置方法:
add_example(example: dictstr, str) :将新示例添加到列表中。
输入:一个字典,其中键作为输入变量,值作为其值。
select_examples(input_variables: dictstr, str) → listdict :根据输入长度选择要使用的示例。
输入:一个字典,其中键作为输入变量,值作为其值。
输出:要包含在提示中的示例列表。
sql
# 反义词示例集合
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
# 示例模板(文本)
example_prompt = PromptTemplate.from_template("Input: {input}\nOutput: {output}")
# 示例选择器(长度)
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=25 # 格式化示例的最大长度
# 用于获取字符串长度的函数,用于确定包含哪些示例。
# 如果没有指定,它是作为默认值提供的。
# 该函数返回一个整数,表示字符串中由换行符或空格分隔的"单词"数量
# get_text_length: Callable[[str], int] = lambda x: len(re.split("\n| ", x))
)
few_shot_prompt = FewShotPromptTemplate(
example_selector=example_selector, # 示例选择器
example_prompt=example_prompt,
prefix="给出每个输入的反义词:",
suffix="Input: {adjective}\nOutput: ",
input_variables=["adjective"],
)
print(few_shot_prompt.invoke({"adjective": "big"}).to_messages()[0].content)
(二)按语义相似性选择示例
语义相似即文本在【含义上】的接近程度。LangChain 能根据输入和示例之间的语义相似性来决定选择哪些示例,它通过查找与输入具有最大余弦相似性的嵌入示例来实现这一点。而具体的实现是靠SemanticSimilarityExampleSelector这个类来实现。
其内置方法有:
from_examples() :根据示例集生成语义相似示例选择器
输入:
| 参数名 | 描述 |
|---|---|
| examples | 示例列表 |
| embeddings | 初始化的嵌入 API 接口,如 OpenAIEmbeddings() |
| vectorstore_cls | 向量存储数据库接口类。 |
| k | 最终要选择的⽰例的数量。默认值为 4。 |
| 输出:语义相似性示例选择器 |
add_example(example: dictstr, str) :将新示例添加到列表中。
输入:一个字典,其中键作为输入变量,值作为其值。
select_examples(input_variables: dictstr, str) → listdict :根据输入选择要使用的示例。
输入:一个字典,其中键作为输入变量,值作为其值。
输出:要包含在提示中的示例列表
python
from langchain_chroma import Chroma
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import OpenAIEmbeddings
# 反义词⽰例集合
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 = SemanticSimilarityExampleSelector.from_examples(
examples,
# 嵌⼊类⽤于⽣成⽤于度量语义相似度的嵌⼊。
OpenAIEmbeddings(),
# VectorStore类,⽤于存储嵌⼊并对其进⾏相似性搜索。
Chroma,
# ⽣成⽰例的数量。
k=1,
)
#⽤于实例化⽰例的模板
similar_prompt = FewShotPromptTemplate(
# 提供了⼀个ExampleSelector⽽不是examples。
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输⼊的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
print(
similar_prompt.invoke({"adjective": "worried"}).to_messages()[0].content
)
(三)按最大边际相关性选择示例(MMR)
按最大边际相关性选择示例是一种重新排序算法,它使用语义相似性作为基础工具,从一个候选集中挑选出一组既能代表查询主题又彼此多样化的结果。其要使用MaxMarginalRelevanceExampleSelector类,内置方法同上。
MMR常被使用在推荐系统、文档摘要、RAG (检索增强⽣成)当中。
python
rom langchain_chroma import Chroma
from langchain_core.example_selectors import
MaxMarginalRelevanceExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import OpenAIEmbeddings
# 反义词⽰例集合
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}",
)
# MMR ⽰例选择器
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
Chroma,
k=2,
)
# ⽤于实例化⽰例的模板
similar_prompt = FewShotPromptTemplate(
# 提供了⼀个ExampleSelector⽽不是examples。
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输⼊的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
print(
similar_prompt.invoke({"adjective": "worried"}).to_messages()[0].content
)
(四)通过 ngram 重叠选择示例(Ngram)
ngram 指一个文本序列中连续的 n 个词(word) 或字符(character)。
ngram 重叠 则是通过计算它们之间共同拥有的 ngram 数量来一种衡量两段文本相似度的方法。而传统 ngram 重叠是一种表面形式的匹配。它只关心词是否完全一样,但对于同义词却无法处理。
语义 ngram 重叠则不再比较词本身而是比较词背后的语义向量(Embedding)
其要使用NGramOverlapExampleSelector 这个类,其参数如下:
| 参数名 | 描述 |
|---|---|
| example_prompt | PromptTemplate,用于格式化示例的提示模板。 |
| examples | 模板所需的示例列表。 |
| threshold | 算法停止的阈值。默认设置为 -1.0。对于负阈值:按 重叠分数 对示例进行排序,但不排除任何示例。对于等于 0.0 的阈值:按 重叠分数 进行排序,并排除与输入没有 ngram 重叠的示例。对于⼤于 1.0 的阈值:排除所有示例,并返回一个空列表。 |
内置方法:
add_example(example: dictstr, str) :将新示例添加到列表中。
输入:一个字典,其中键作为输入变量,值作为其值。
select_examples(input_variables: dictstr, str) → listdict :返回根据输入得到的重叠分数排序的降序示例列表。
输入:一个字典,其中键作为输入变量,值作为其值。
输出:要包含在示例中的示例列表。
结语
以上便是Langchain关于提示词少样本提示与示例选择器的内容了。如果喜欢我的内容,请点赞、收藏加关注,多多支持我,以及欢迎各位在评论区讨论交流,并指出我的不足,谢谢大家。