
🌈 个人主页:Zfox_
🔥 系列专栏:LangChain-AI 应用开发框架

目录
- 一:🔥消息 (Messages)
-
- [🦋 LLM消息结构](#🦋 LLM消息结构)
- [🦋 LangChain 消息](#🦋 LangChain 消息)
-
- [🎀 BaseMessage抽象消息类](#🎀 BaseMessage抽象消息类)
- [🎀 对话模式](#🎀 对话模式)
- [🦋 缓存历史消息](#🦋 缓存历史消息)
-
- [🎀 多轮对话](#🎀 多轮对话)
- [🎀 内存缓存](#🎀 内存缓存)
- [🦋 管理历史消息](#🦋 管理历史消息)
-
- [🎀 前置概念](#🎀 前置概念)
- [🎀 消息裁剪(trim_messages)](#🎀 消息裁剪(trim_messages))
- [🎀 消息过滤(filter_messages)](#🎀 消息过滤(filter_messages))
- [🎀 消息合并(merge_message_runs)](#🎀 消息合并(merge_message_runs))
- [二:🔥 提示词模板(Prompt Template)](#二:🔥 提示词模板(Prompt Template))
-
- [🦋 概念](#🦋 概念)
- [🦋 用法](#🦋 用法)
-
- [🎀 字符串模板(PromptTemplate)](#🎀 字符串模板(PromptTemplate))
- [🎀 聊天消息模板(ChatPromptTemplate)](#🎀 聊天消息模板(ChatPromptTemplate))
- [🎀 消息占位符(MessagesPlaceholder)](#🎀 消息占位符(MessagesPlaceholder))
- [🦋 使用 LangChain Hub 的提示词模板](#🦋 使用 LangChain Hub 的提示词模板)
- [三:🔥 少样本提示(few-shotting)](#三:🔥 少样本提示(few-shotting))
-
- [🦋 概念](#🦋 概念)
- [🦋 实现少样本提示](#🦋 实现少样本提示)
- [🦋 使用案例](#🦋 使用案例)
-
- [🎀 案例一:推理引导](#🎀 案例一:推理引导)
- [🎀 案例二:使用示例数据增强信息提取能力](#🎀 案例二:使用示例数据增强信息提取能力)
- [四:🔥 示例选择器(Example selectors)](#四:🔥 示例选择器(Example selectors))
-
- [🦋 概念](#🦋 概念)
- [🦋 按长度选择示例](#🦋 按长度选择示例)
- [🦋 按语义相似性选择示例(Similarity)](#🦋 按语义相似性选择示例(Similarity))
-
- [🎀 概念](#🎀 概念)
- [🎀 实现](#🎀 实现)
- [🦋 按最大边际相关性选择示例(MMR)](#🦋 按最大边际相关性选择示例(MMR))
-
- [🎀 概念](#🎀 概念)
- [🎀 实现](#🎀 实现)
- [🦋 重叠选择示例](#🦋 重叠选择示例)
-
- [🎀 概念](#🎀 概念)
- [🎀 实现](#🎀 实现)
- [五:🔥 共勉](#五:🔥 共勉)
一:🔥消息 (Messages)
消息是聊天模型中的通信单位,⽤于表⽰聊天模型的 输⼊ 和 输出 ,以及可能与对话关联的任何其他 上下⽂ 或 元数据 。
🦋 LLM消息结构
每条消息都有⼀个⻆⾊和内容,以及因LLM的不同⽽不同的附加元数据。
- 消息⻆⾊(Role) :⽤来区分对话中不同类型的消息,并帮助聊天模型了解如何响应给定的消息序列。

- 消息内容(Content):表⽰多模态数据(例如,图像、⾳频、视频)的消息⽂本或字典列表的内容。内容的具体格式可能因底层不同的LLM⽽异。⽬前,⼤多数模型都⽀持⽂本作为主要内容类型,对多模态数据的⽀持仍然有限。
- 消息其他元数据(Additionalmetadata)

下⾯展⽰⼀个OpenAI的格式消息列表:
python
[
{
"role": "user",
"content": "Hello, how are you?",
},
{
"role": "assistant",
"content": "I'm doing well, thank you for asking.",
},
{
"role": "user",
"content": "Can you tell me a joke?",
}
]
🦋 LangChain 消息
LangChain提供了⼀种统⼀的消息格式,可以跨聊天模型使⽤,允许⽤⼾使⽤不同的聊天模型,⽽⽆需担⼼每个模型提供商使⽤的消息格式的具体细节。例如:
python
openai_model = init_chat_model("gpt-4o-mini", model_provider="openai")
anthropic_model = init_chat_model("claude-3-5-sonnet-latest",
model_provider="anthropic")
deepseek_model = init_chat_model("deepseek-chat", model_provider="deepseek")
google_genai_model = init_chat_model("gemini-2.5-flash",
model_provider="google_genai")
model = init_chat_model(...)
这些模型提供商不同,但对于其输⼊和输出,统⼀使⽤LangChain的消息格式。LangChain消息格式主要分为五种,分别是:
| 消息类型 | 对应角色 | 描述 |
|---|---|---|
| SystemMessage | system | 启动AI模型的行为并提供额外上下文 |
| HumanMessage | user | 人类与模型交互的输入 |
| AIMessage | assistant | 来自模型的响应,可包括文本或工具调用请求 |
| AIMessageChunk | assistant | 用于流式响应的消息块 |
| ToolMessage | tool | 包含调用工具的结果 |
这⼏个消息类型,我们已经全部⻅过!它们都是LangChain BaseMessage 的⼦类,全部是作为LangChain聊天模型的输⼊和输出!!
🎀 BaseMessage抽象消息类
class langchain_core.messages.base.BaseMessage 是作为LangChain聊天模型的输⼊和输出!!
- content:消息的字符串内容
- additional_kwargs:与消息关联的其他有效负载数据。对于来⾃AI的消息,可能包括模型提供程序编码的⼯具调⽤。
- response_metadata:响应元数据,例如:响应标头、logprobs、令牌计数、模型名称。
- type:消息类型。必须是消息类型唯⼀的字符串。此字段的⽬的是在对消息进⾏反序列化时⽅便地识别消息类型。
- name:消息名称
- id:唯一标识符
内置⽅法:
pretty_print() → None :打印消息的漂亮表⽰。
pretty_repr(html: bool = False) → str :获得消息的漂亮表⽰。
- 请求:是否将消息格式化为HTML。如果为True,则消息将使⽤HTML标记进⾏格式化。默认值为False。
- 响应:这是消息的漂亮表⽰。
text() → str :获取消息的⽂本内容
🎀 对话模式
⼤多数对话都以设置对话上下⽂的系统消息开始。接下来是包含⽤⼾输⼊的⽤⼾消息,然后是包含模型响应的助⼿消息。如下图所⽰:

🦋 缓存历史消息
🎀 多轮对话
在与⼤型语⾔模型交互的过程中,我们常常体验到与智能助⼿进⾏连贯多轮对话的便利性。但⽬前我们的系统还不⽀持此功能,代码如下:
python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
# 定义⼤模型
model = ChatOpenAI(model="gpt-4o-mini")
# 第⼀次对话
result = model.invoke([HumanMessage(content="Hi! I'm Bob")])
result.pretty_print()
# 第⼆次对话
result = model.invoke([HumanMessage(content="What's my name?")])
result.pretty_print()
打印结果:
python
================================== Ai Message
==================================
Hi Bob! How can I assist you today?
================================== Ai Message
==================================
I'm sorry, but I don't have access to personal information about you unless you share it with me. What would you like me to call you?
可以发现,聊天模型并不认识我们,更别说⽀持更多轮的对话了~
稍作修改,让我们将AI回复给我们的响应跟着新的⽤⼾消息⼀起发给聊天模型试试。代码如下:
python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
# 定义⼤模型
model = ChatOpenAI(model="gpt-4o-mini")
# 记录消息
messages = [
HumanMessage(content="Hi! I'm Bob"),
AIMessage(content="Hello Bob! How can I assist you today?"),
HumanMessage(content="What's my name?"),
]
model.invoke(messages).pretty_print()
打印结果:
python
================================== Ai Message
==================================
Your name is Bob! How can I help you today, Bob?
从结果可知,只要将历史消息,重新发送给聊天模型,那么就可以实现多轮对话的功能。
🎀 内存缓存
那么对于历史消息的管理就显得尤为重要。在LangChain⽼版本中,可以使⽤ RunnableWithMessageHistory 消息历史类来包装另⼀个Runnable并为其管理聊天消息历史记录。它将跟踪模型的输⼊和输出,并将其存储在某个数据存储中。未来的交互将加载这些消息,并将其作为输⼊的⼀部分传递给链。
代码如下:
python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.chat_history import BaseChatMessageHistory, InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
# 定义⼤模型
model = ChatOpenAI(model="gpt-4o-mini")
# 接受⼀个 session_id 并返回⼀个消息历史对象。
# 这个 session_id ⽤于区分不同的对话,并应作为配置的⼀部分在调⽤新链时传⼊
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
# InMemoryChatMessageHistory() 将消息存储在内存列表中。
store[session_id] = InMemoryChatMessageHistory()
return store[session_id]
# 包装model,管理聊天消息历史记录
with_message_history = RunnableWithMessageHistory(model, get_session_history)
config = {"configurable": {"session_id": "1"}}
with_message_history.invoke(
[HumanMessage(content="Hi! I'm Bob")],
config=config,
).pretty_print()
with_message_history.invoke(
[HumanMessage(content="What's my name?")],
config=config,
).pretty_print()
class langchain_core.runnables.history.RunnableWithMessageHistory 类初始化参数说明:
- runnable :被包装Runnable实例,这⾥就是我们定义的聊天模型
- get_session_history :返回类型为 BaseChatMessageHistory 的函数,传⼊后作为回调函数。此函数接受⼀个 session_id 字符串类型,并返回相应的聊天消息历史记录实例。
class langchain_core.runnables.history.RunnableWithMessageHistory 类⽅法说明:
- .invoke() ⽅法:此⽅法与其他Runnable实例的 .invoke() ⽅法相同。只不过注意其 config 配置,需要配置成 config={"configurable": {"session_id": ""}} ,让 RunnableWithMessageHistory 可以读取到会话id。
最终打印结果:
python
================================== Ai Message
==================================
Hi Bob! How can I assist you today?
================================== Ai Message
==================================
Your name is Bob! How can I help you today?
记忆功能已经实现!
说明
从LangChain的v0.3版本开始,官⽅建议LangChain⽤⼾不要使⽤ RunnableWithMessageHistory ,⽽是利⽤ LangGraph 持久性 来完成(⻅LangGraph章节)。
原因是它们的功能有限,不太适合现实世界的对话式AI应⽤程序。这些内存抽象缺乏对多⽤⼾、多对话场景的内置⽀持,⽽这对于实际的对话式⼈⼯智能系统⾄关重要。这些实现中的⼤多数已在LangChain0.3.x中被正式弃⽤,取⽽代之的是 LangGraph 持久性 。 LangGraph 持久性 ⾮常灵活,可以⽀持⽐ RunnableWithMessageHistory 接⼝更⼴泛的⽤例。我们会在 LangGraph 篇
章中学习它!
因此, RunnableWithMessageHistory 这部分我们讲解的并不深⼊。在之前,对于⽣产环境,我们还需要使⽤聊天消息历史记录的持久化实现,例如 RedisChatMessageHistory() ,⽽不是 InMemoryChatMessageHistory() ,但现在也已不推荐新应⽤使⽤它们了。
🦋 管理历史消息
🎀 前置概念
上下文窗口:模型一次处理请求时所能查看和处理的最大Token数量。
管理历史消息,⽆⾮就是理解如何"管理","管理"⽆⾮也就是⼀些"CRUD"。那么在了解如何管理消息之前,需要先了解下多轮对话的核⼼概念:上下⽂窗⼝。上下⽂窗⼝可以理解为模型的"短期⼯作记忆区",即LLM在⼀次处理请求时,所能查看和处理的最⼤Token数量,它包含了:
- ⽤⼾的输⼊
- ⼤模型的输出
- 有时还包括系统指令(SystemMessage)和对话历史。
不同⼤模型⽀持的上下⽂窗⼝⼤⼩不同,例如:
- OpenAI下GPT-5模型上下⽂窗⼝为400000(最⼤Token数量)
- GPT-4.1模型上下⽂窗⼝为1047576(最⼤Token数量)
- 其他模型上下⽂窗⼝可参考对应模型官⽹说明,如OpenAI下模型可以参考这里。
Token概念:文本的基本单位,计算机将文本转换为数字的第一步。
在⾃然语⾔处理(NLP)中,Token是⽂本的基本单位。它不是完全等同于⼀个单词或⼀个汉字,⽽是⼀个更细粒度的划分。
为什么⽤Token?计算机⽆法直接理解⽂字,它需要将⽂本转换为数字(向量)。Tokenization(令牌化)就是这个转换过程的第⼀步,将句⼦分解成模型可以理解和处理的碎⽚。
🎀 消息裁剪(trim_messages)
有了上下⽂窗⼝和Token的认知,再来看多轮对话的实现原理,其实就是:
- 输⼊=系统消息+对话历史+最新⽤⼾问题
- 对于模型来说,并不真正"记忆",⽽是每次都将完整的上下⽂重新输⼊。
由于所有模型的上下⽂窗⼝⼤⼩都是有限的,这意味着作为输⼊的Token也是有限的。如果有累积了很⻓的消息历史记录,则需要管理传递给模型的消息的⻓度。
trim_messages 可⽤于将聊天历史记录的⼤⼩减⼩为指定的令牌计数或指定的消息计数。
基于输⼊Token数的修剪
下⾯演⽰⼀个通过 trim_messages 裁剪消息的⽰例(基于输⼊Token数的修剪)。
先来看看不做任何输⼊限制的聊天:
python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage, trim_messages
# 定义⼤模型
model = ChatOpenAI(model="gpt-4o-mini")
# 历史消息记录
messages = [
SystemMessage(content="you're a good assistant"),
HumanMessage(content="hi! I'm bob"),
AIMessage(content="hi!"),
HumanMessage(content="I like vanilla ice cream"),
AIMessage(content="nice"),
HumanMessage(content="whats 2 + 2"),
AIMessage(content="4"),
HumanMessage(content="thanks"),
AIMessage(content="no problem!"),
HumanMessage(content="having fun?"),
AIMessage(content="yes!"),
HumanMessage(content="What's my name?"),
]
print(model.invoke(messages))
打印结果:
python
content='Your name is Bob!'
additional_kwargs={'refusal': None}
response_metadata={
'token_usage': {
'completion_tokens': 6,
'prompt_tokens': 88,
'total_tokens': 94,
'completion_tokens_details': {
'accepted_prediction_tokens': 0,
'audio_tokens': 0,
'reasoning_tokens': 0,
'rejected_prediction_tokens': 0
},
'prompt_tokens_details': {
'audio_tokens': 0,
'cached_tokens': 0
}
},
'model_name': 'gpt-4o-mini',
'system_fingerprint': 'fp_eb37e061ec',
'id': 'chatcmpl-DXLRLci4WW0jr9le2VXYWHGTIOMe9',
'service_tier': 'default',
'finish_reason': 'stop',
'logprobs': None
}
id='run--019db3e4-b965-7562-b11d-968f9ae6fc22-0'
usage_metadata={
'input_tokens': 88,
'output_tokens': 6,
'total_tokens': 94,
'input_token_details': {
'audio': 0,
'cache_read': 0
},
'output_token_details': {
'audio': 0,
'reasoning': 0
}
}
从打印结果看来,LLM还认识我们,且共输⼊了88tokens。接下来让我们对消息进⾏裁剪,我们只希望将来输⼊时,最多输⼊65tokens,超出的需要按照⼀定的"规则"进⾏裁剪,代码如下:
python
# 管理消息列表,裁剪
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, trim_messages
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model_name="gpt-4o-mini")
# 历史消息记录
messages = [
SystemMessage(content="you're a good assistant"),
HumanMessage(content="hi! I'm bob"),
AIMessage(content="hi!"),
HumanMessage(content="I like vanilla ice cream"),
AIMessage(content="nice"),
HumanMessage(content="whats 2 + 2"),
AIMessage(content="4"),
HumanMessage(content="thanks"),
AIMessage(content="no problem!"),
HumanMessage(content="having fun?"),
AIMessage(content="yes!"),
HumanMessage(content="What's my name?"),
]
#使⽤ trim_messages 减少发送给模型的消息数量
trimmer = trim_messages(
max_tokens=65, # 修剪消息的最⼤令牌数,根据你想要的谈话⻓度来调整
strategy="last", # 修剪策略:
# "last"(默认):保留最后的消息。
# "first":保留最早的消息。
token_counter=model, # 传⼊⼀个函数或⼀个语⾔模型(因为语⾔模型有消息令牌计数⽅法)
include_system=True, # 如果想始终保留初始系统消息,可以指定 include_system=True
allow_partial=False, # 是否允许拆分消息的内容 只取出最后65个tokens
start_on="human", # 如果需要确保我们的第⼀条消息(不包括系统消息)始终是特定类型,可以指定 start_on
)
chain = trimmer | model
print(chain.invoke(messages))
打印结果:
python
content = "I don't know your name unless you tell me. What is it?"
additional_kwargs = {'refusal': None}
response_metadata = {
'token_usage': {
'completion_tokens': 15,
'prompt_tokens': 60,
'total_tokens': 75,
'completion_tokens_details': {
'accepted_prediction_tokens': 0,
'audio_tokens': 0,
'reasoning_tokens': 0,
'rejected_prediction_tokens': 0
},
'prompt_tokens_details': {
'audio_tokens': 0,
'cached_tokens': 0
}
},
'model_name': 'gpt-4o-mini',
'system_fingerprint': 'fp_eb37e061ec',
'id': 'chatcmpl-DXLj2j0I3c8dJpDBuYm33Pw6T8ltZ',
'service_tier': 'default',
'finish_reason': 'stop',
'logprobs': None
}
id = 'run--019db3f5-6fd0-7dd3-ab42-d1e172893bc7-0'
usage_metadata = {
'input_tokens': 60,
'output_tokens': 15,
'total_tokens': 75,
'input_token_details': {
'audio': 0,
'cache_read': 0
},
'output_token_details': {
'audio': 0,
'reasoning': 0
}
}
可以看⻅,此时我们的输⼊message已经被修剪了。被修剪了哪些消息呢?来看下:
python
trimmer = trim_messages(
...
)
#打印经过裁剪器裁剪后的结果
print(trimmer.invoke(messages))
结果如下:
python
[SystemMessage(content="you're a good assistant", additional_kwargs={}, response_metadata={}),
HumanMessage(content='whats 2 + 2', additional_kwargs={}, response_metadata={}),
AIMessage(content='4', additional_kwargs={}, response_metadata={}), HumanMessage(content='thanks', additional_kwargs={}, response_metadata={}),
AIMessage(content='no problem!', additional_kwargs={}, response_metadata={}),
HumanMessage(content='having fun?', additional_kwargs={}, response_metadata={}),
AIMessage(content='yes!', additional_kwargs={}, response_metadata={}),
HumanMessage(content="What's my name?", additional_kwargs={}, response_metadata={})]
从结果来看,确实是按照我们给定的裁剪"规则"来完成的。修剪聊天记录后,⽣成的聊天记录(输⼊)应该有效,需遵循对话模式原则:

- 聊天记录以 HumanMessage 或 SystemMessage 开头,后跟 HumanMessage 。这可以通过设置 start_on="human" 来实现。
- 聊天记录以 HumanMessage 或 ToolMessage 结尾。这可以通过设置 ends_on=("human", "tool") 来实现。
- ToolMessage 只能出现在涉及⼯具调⽤的 AIMessage 之后。
- 如果原始聊天历史记录中存在 SystemMessage ,则新聊天历史记录应包括 SystemMessage ,因为 SystemMessage 包含对聊天模型的特殊说明。 SystemMessage 总是历史记录中的第⼀条消息(如果存在)。这可以通过设置 include_system=True 。
基于消息数的修剪
除了基于token的修剪,还可以通过设置 token_counter=len 根据消息数修剪聊天记录。在这种情况下, max_tokens 将控制最⼤消息数。⽰例如下:
python
# 管理消息列表,裁剪
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage, trim_messages
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model_name="gpt-4o-mini")
# 历史消息记录
messages = [
SystemMessage(content="you're a good assistant"),
HumanMessage(content="hi! I'm bob"),
AIMessage(content="hi!"),
HumanMessage(content="I like vanilla ice cream"),
AIMessage(content="nice"),
HumanMessage(content="whats 2 + 2"),
AIMessage(content="4"),
HumanMessage(content="thanks"),
AIMessage(content="no problem!"),
HumanMessage(content="having fun?"),
AIMessage(content="yes!"),
HumanMessage(content="What's my name?"),
]
# 现在一共有88个token,需要裁剪到65个token (根据token数进行裁剪)
#使⽤ trim_messages 减少发送给模型的消息数量
trimmer = trim_messages(
max_tokens=11, # 最大消息数
strategy="last", # 修剪策略:
# "last"(默认):保留最后的消息。
# "first":保留最早的消息。
token_counter=len, # 传⼊⼀个函数或⼀个语⾔模型(因为语⾔模型有消息令牌计数⽅法) token_counter=model = len 就可以根据消息数量裁
include_system=True, # 如果想始终保留初始系统消息,可以指定 include_system=True
allow_partial=False, # 是否允许拆分消息的内容 只取出最后65个tokens
start_on="human", # 如果需要确保我们的第⼀条消息(不包括系统消息)始终是特定类型,可以指定 start_on
)
print(trimmer.invoke(messages)) # 打印裁剪的消息
结果如下:
python
[
SystemMessage(content="you're a good assistant", additional_kwargs={},
response_metadata={}),
HumanMessage(content='I like vanilla ice cream', additional_kwargs={},
response_metadata={}),
AIMessage(content='nice', additional_kwargs={}, response_metadata={}),
HumanMessage(content='whats 2 + 2', additional_kwargs={},
response_metadata={}),
AIMessage(content='4', additional_kwargs={}, response_metadata={}),
HumanMessage(content='thanks', additional_kwargs={}, response_metadata=
{}),
AIMessage(content='no problem!', additional_kwargs={}, response_metadata=
{}),
HumanMessage(content='having fun?', additional_kwargs={},
response_metadata={}),
AIMessage(content='yes!', additional_kwargs={}, response_metadata={}),
HumanMessage(content="What's my name?", additional_kwargs={},
response_metadata={})
]
🎀 消息过滤(filter_messages)
在更复杂的场景下,我们可能会使⽤消息列表来跟踪状态,例如我们可能只想将这个完整消息列表的⼦集传递模型调⽤,⽽不是所有的历史记录。
filter_messages ⽅法则可以轻松地按类型、ID或名称过滤message。
下⾯演⽰相关过滤⽰例,⾸先准备消息列表:
python
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage, filter_messages
# 历史消息记录
messages = [
SystemMessage("你是⼀个聊天助⼿", id="1"),
HumanMessage("⽰例输⼊", id="2"),
AIMessage("⽰例输出", id="3"),
HumanMessage("真实输⼊", id="4"),
AIMessage("真实输出", id="5"),
]
- 按类型进⾏筛选:
python
print(filter_messages(messages, include_types="human"))
# 注意写法等价于:
# print(filter_messages(include_types="human").invoke(messages))
# 结果如下:
# [
# HumanMessage(content='⽰例输⼊', additional_kwargs={}, response_metadata={}, name='example_user', id='2'),
# HumanMessage(content='真实输⼊', additional_kwargs={}, response_metadata={}, name='bob', id='4')
# ]
- 按类型+ID进⾏筛选:
python
print(filter_messages(messages, include_types=[HumanMessage, AIMessage],exclude_ids=["3"]))
# 结果如下:
# [
# HumanMessage(content='⽰例输⼊', additional_kwargs={}, response_metadata={}, name='example_user', id='2'),
# HumanMessage(content='真实输⼊', additional_kwargs={}, response_metadata={}, name='bob', id='4'),
# AIMessage(content='真实输出', additional_kwargs={}, response_metadata={},name='alice', id='5')
# ]
🎀 消息合并(merge_message_runs)
若我们的消息列表存在连续某种类型相同的消息,但实际上某些模型不⽀持传递相同类型的连续消息。因此对于这种情况,我们可以使⽤ merge_message_runs ⽅法轻松合并相同类型的连续消息。
⽰例如下:
python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage, merge_message_runs
# 定义⼤模型
model = ChatOpenAI(model="gpt-4o-mini")
# 历史消息记录
messages = [
SystemMessage("你是⼀个聊天助⼿。"),
SystemMessage("你总是以笑话回应。"),
HumanMessage("为什么要使⽤ LangChain?"),
HumanMessage("为什么要使⽤ LangGraph?"),
AIMessage("因为当你试图让你的代码更有条理时,LangGraph 会让你感到"节点"是个好主意!"),
AIMessage("不过别担⼼,它不会"分散"你的注意⼒!"),
HumanMessage("选择LangChain还是LangGraph?"),
]
merged = merge_message_runs(messages)
# 打印合并后的每个消息
print("\n".join([repr(x) for x in merged]))
合并结果:
python
SystemMessage(content='你是⼀个聊天助⼿。\n你总是以笑话回应。', additional_kwargs={}, response_metadata={})
HumanMessage(content='为什么要使⽤ LangChain?\n为什么要使⽤ LangGraph?', additional_kwargs={}, response_metadata={})
AIMessage(content='因为当你试图让你的代码更有条理时,LangGraph 会让你感到"节点"是个好主意!\n不过别担⼼,它不会"分散"你的注意⼒!', additional_kwargs={},response_metadata={})
HumanMessage(content='选择LangChain还是LangGraph?', additional_kwargs={},response_metadata={})
调⽤⼤模型:
python
## ⽅式⼀:
merged_message = merge_message_runs(messages)
model.invoke(merged_message).pretty_print()
## ⽅式⼆:
merger = merge_message_runs()
chain = merger | model
chain.invoke(messages).pretty_print()
打印结果:
python
================================== Ai Message ==================================
这就像选择汉堡还是热狗!如果你想要⼀个多层次的体验,选择LangChain;如果你想要⼀个清晰的链接,LangGraph就像是你的"⽀架"!让你的代码加个"料"!
二:🔥 提示词模板(Prompt Template)
🦋 概念
提⽰词模板(PromptTemplate)是LangChain的核⼼抽象之⼀,它被⼴泛应⽤于构建⼤语⾔模型(LLM)应⽤的各个环节。
简单来说,只要是需要动态、批量、或有结构地向⼤语⾔模型【发送请求】的地⽅,⼏乎都会⽤到提⽰词模板。
⼀个简单的例⼦,假设我们想根据⼀个城市名询问LLM其历史,按照之前的做法,我们可以定义HumanMessage("请介绍上海的历史") 、HumanMessage("请介绍西安的历史") 消息等等。可以发现每次询问都会描写重复的消息内容: 请介绍xxx的历史 。
在LangChain中,针对这种情况,可以定义⼀个模板:
- 固定⽂本(模板):"请介绍{city}的历史。"
- 输⼊变量:["city"]
定义好后,可以使⽤该模板:
- 当我们需要查询北京时,就将city变量赋值为"北京"。模板引擎会⽣成:"请介绍北京的历史。"
- 当我们需要查询上海时,就将city变量赋值为"上海"。模板引擎会⽣成:"请介绍上海的历史。"

提示词模板是可复用的提示词蓝图,允许动态生成提示词。
⽽不是每次都⼿动编写完整的提⽰词。它类似于编程中的字符串格式化功能。你创建⼀个带有"占位符"的模板,然后在运⾏时,⽤具体的值(变量)填充这些占位符,从⽽⽣成⼀个最终发送给LLM的完整提⽰词。
提⽰词模板解决了以下⼏个核⼼问题:
- 可复⽤性:只需定义⼀个模板,就可以⽤于⽆数个类似的查询。
- 关注点分离:将提⽰词的结构和逻辑(⼯程)与具体的内容和数据分离开。提⽰⼯程师可以专注于优化模板,⽽应⽤程序则负责提供变量值。
- ⼀致性:确保发送给LLM的提⽰词结构统⼀,这有助于获得更稳定、可预测的输出结果。
- 可维护性:如果需要修改提⽰词的⻛格或结构,只需修改⼀个模板⽂件,⽽不⽤在代码的⽆数个地⽅进⾏修改。
🦋 用法
🎀 字符串模板(PromptTemplate)
LangChain提供了 PromptTemplate 类来轻松实现这⼀功能。 PromptTemplate 实现了标准的 Runnable接⼝。⽰例如下:
python
from langchain_core.prompts import PromptTemplate
# 1. 定义模板
prompt_template = PromptTemplate.from_template("Translate the following into {language}")
# 2. 实例化模板
print(prompt_template.invoke({"language": "Chinese"}))
打印结果:
python
text='Translate the following into Chinese'
说明:
class langchain_core.prompts.prompt.PromptTemplate 类,其参数如下:
- template :提⽰模板
- input_variables :需要其值作为提⽰输⼊的变量的名称列表。
内置⽅法:
from_template() :从模板定义提⽰模板。⽅法返回了⼀个 PromptTemplate 实例
因此除了上⾯⽰例中 PromptTemplate.from_template 定义提⽰模板的⽅式外,下⾯这种⽅法也可以直接初始化模板:
python
prompt_template = PromptTemplate(
input_variables=["language"],
template="Translate the following into {language}",
)
🎀 聊天消息模板(ChatPromptTemplate)
python
from langchain_core.prompts import ChatPromptTemplate
prompt_template = ChatPromptTemplate([
("system", "Translate the following into {language}."),
("user", "{text}")
])
messagesValue = prompt_template.invoke({"language": "Chinese", "text": "what is your name?"})
messages = messagesValue.to_messages()
🎀 消息占位符(MessagesPlaceholder)
在上⾯的ChatPromptTemplate中,我们看到了如何格式化两条消息,每条消息都是⼀个字符串。但如果我们希望将消息插⼊特定位置怎么办?使⽤ MessagesPlaceholder 。
MessagesPlaceholder 负责在特定位置添加消息列表。代码如下:
python
# 提示词模版
from langchain_core.messages import HumanMessage, AIMessage
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder
model = ChatOpenAI(model_name="gpt-4o-mini")
# 处理聊天消息的模版
chat_prompt_template = ChatPromptTemplate(
[
("system", "将文本从{language_from}翻译为{language_to}"),
MessagesPlaceholder("msg"), # 消息占位符
("user", "{text}"),
# ("ai", "")
]
)
# 把已经现有的消息插入到模版里
messages_placeholder = [
HumanMessage(content="hi, what is your name?"),
AIMessage(content="你好,你的名字是什么?"),
]
# 执行链,现在是链需要模版参数了
chain = chat_prompt_template | model
chain.invoke(
{
"language_from": "英文",
"language_to": "中文",
"text": "hi, what is your age?",
"msg": messages_placeholder,
}
).pretty_print()
🦋 使用 LangChain Hub 的提示词模板
LangChainHub是⼀个⽤于上传、浏览、拉取和管理提⽰词(prompts)的地⽅。
随着LLM的发展,提⽰变得越来越重要。LangChain正在打造⼀个与像GitHub这样的传统平台,GitHub⻓期以来⼀直是共享和协作代码的⾸选平台。于是推出了LangChainHub平台。
LangChainHub创建⼀个分享和发现Prompt的平台,使得开发者可以更容易地发现新⽤例和精炼提⽰。这⼀举措使提⽰⼯程师更容易合作,重复使⽤现有的提⽰,并对其进⾏微调以实现特定的结果,
从⽽加速对话代理和其他基于语⾔的应⽤程序的开发和部署。早期的时候LangChainHub有 Prompt、Chain、Agent,现在只有Prompt。
LangChainHub官⽹地址:https://smith.langchain.com/hub/。通过登录到Hub来探索所有现有提⽰
⽬前收藏最⾼的提⽰词模板是: hardkothari/prompt-maker 。我们就以它为⽰例,演⽰⼀下如何使⽤LangChainHub上的提⽰。
Prompt Maker 模板是⼀个【提⽰⽣成器】,它可以⾃动化优化提⽰的过程,从⽽提⾼语⾔模型在各种应⽤中的质量和效果。
要想使⽤该能⼒,需要先申请并配置 LangSmith 环境变量: LANGSMITH_API_KEY="你的LangSmith API Key" 。接着,需要从hub拉取相应的提⽰,并使⽤,代码如下:
python
from langchain_openai import ChatOpenAI
from langsmith import Client
# 从 hub 拉取 "hardkothari/prompt-maker" 提⽰词模板。
client = Client()
prompt = client.pull_prompt("hardkothari/prompt-maker", include_model=True)
# 定义模型
model = ChatOpenAI(model="gpt-4o-mini")
# 定义链
chain = prompt | model
while True:
task = input("\n你的任务是什么?(输⼊ quit 退出聊天)\n")
if task == 'quit':
break
lazy_prompt = input("\n你当前的提⽰是什么?(输⼊ quit 退出聊天)\n")
if lazy_prompt == 'quit':
break
print("\n Response:")
chain.invoke({'lazy_prompt': lazy_prompt, 'task': task}).pretty_print()
运⾏代码:
python
你的任务是什么?(输⼊ quit 退出聊天)
写⼀个快排代码
你当前的提⽰是什么?(输⼊ quit 退出聊天)
开发专家,需中⽂回复
Response:
================================== Ai Message ==================================
As a coding expert specialized in algorithms and data structures, please write a detailed implementation of the Quick Sort algorithm in Python.
### Instructions:
Your code should be well-structured, with clear comments explaining each step
of the algorithm. Additionally, include an example of how to use the function
to sort a list of integers and print the sorted result.
### Context:
The implementation should focus on efficiency and clarity, ideally spanning no
more than 20 lines of code. Ensure that the prompt is in Chinese, and
structure your response to be easily understandable for readers with a basic
knowledge of programming.
Example:
```python
def quick_sort(arr):
# 快速排序函数实现
...
return sorted_arr
# 使⽤⽰例
unsorted_list = [34, 7, 23, 32, 5, 62]
print(quick_sort(unsorted_list))
Please provide the complete code and any additional notes that could aid in
understanding the Quick Sort algorithm. Thank you!
你的任务是什么?(输⼊ quit 退出聊天)
通过使⽤这个模板,可以⼤⼤减少⼿动调整提⽰所需的⼯作量,从⽽节省时间和资源。PromptMaker通过分析初始提⽰的结构和内容,然后应⽤⼀组预定规则或算法来优化提⽰,以提⾼响应质量、清晰度和相关性。这在提⽰的质量对模型的输出有很⼤影响的场景中特别有⽤,⽐如客⼾服务机器⼈、对话代理或数据分析任务
三:🔥 少样本提示(few-shotting)
🦋 概念
少样本提⽰是⼀种通过向LLM提供少量具体⽰例或样本,来教会它如何执⾏某项特定任务的技术。提⾼模型性能的最有效⽅法之⼀是给出⼀个【模型⽰例】指导⼤模型你想做什么、怎么做。下⾯⽤⼀个例⼦解释少样本提⽰的作⽤。
- 参考之前学习的内容,我们可以将其想象为"零样本提⽰":直接给模型⼀道考题,不给任何例⼦。模型会凭借已有的知识直接回答。例如我们输⼊ "What is 2 🦜 9?" ,答案可能是:
python
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")
model.invoke("What is 2 🦜 9?").pretty_print()
# 输出:
# ================================== Ai Message ==================================
#
#表达式"2🦜9"不是⼀个标准的数学运算或⽅程。它似乎是数字2和鹦鹉🦜的组合,后⾯跟着数字9。
# 它没有特定的数学意义。
- 少样本提⽰则是在给出考题前,先给它看⼏道类似的、附有正确答案的例题。添加⽰例输⼊和预期输出的技术给到模型提⽰,让模型通过例题来理解任务应该怎么做。例如以下样例,模型通过样例后可以发现 🦜 与 ➕ 含义相似,便可以按照此规则得出 What is 2 🦜 9? 的结果是 11 :
python
examples = [
{"input": "2 🦜 2", "output": "4"},
{"input": "2 🦜 3", "output": "5"},
}
这能解决什么问题?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
计算成功!
🦋 使用案例
🎀 案例一:推理引导
我们希望输⼊:
《教⽗》和《星球⼤战》的导演来⾃同⼀个国家吗?
让聊天模型可以先分析再得出结论,⽽不是直接得出结论。分析过程需要展⽰出来,⽰例如下:
python
# 少样本提示 案例⼀:推理引导
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
model = ChatOpenAI(model_name="gpt-4o-mini")
# 文本提示词模板
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(
example_prompt=example_prompt, # 用于格式化单个实例
examples=examples,
suffix="Question: {input}", # suffix 表示放在示例之后的模版字符串
input_variables=["input"], # 输入变量列表
)
# print(few_shot_prompt.invoke({"input": "《教⽗》和《星球⼤战》的导演来⾃同⼀个国家吗?"}).to_string())
# print(few_shot_prompt.invoke({"input": "《教⽗》和《星球⼤战》的导演来⾃同⼀个国家吗?"}).to_messages())
chain = few_shot_prompt | model
chain.invoke({"input": "《教⽗》和《星球⼤战》的导演来⾃同⼀个国家吗?"}).pretty_print()
结果如下:
python
================================== Ai Message ==================================
是否需要后续问题:是的。
后续问题:《教父》的导演是谁?
中间答案:《教父》的导演是弗朗西斯·福特·科波拉。
后续问题:弗朗西斯·福特·科波拉来⾃哪⾥?
中间答案:他来自美国。
后续问题:《星球大战》的导演是谁?
中间答案:《星球大战》的导演是乔治·卢卡斯。
后续问题:乔治·卢卡斯来⾃哪⾥?
中间答案:他也来自美国。
所以最终答案是:是
通过为模型提供这样的⽰例,我们可以引导模型做出更好的响应。
来看下 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() ⽅法可以将提⽰作为【消息列表】返回。
🎀 案例二:使用示例数据增强信息提取能力
在我们学习【结构化输出】⼩节时,其中有⼀个使⽤场景是使⽤结构化输出进⾏信息提取。当时说明可以结合少样本提⽰来实现。于是在这⾥,我们来实现⼀个基于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],表⽰为PydanticBaseModel的⼯具调⽤列表
- 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-c13da7328e45
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)
结果如下:
python
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_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.utils.function_calling import tool_example_to_messages
from langchain_openai import ChatOpenAI
from typing import Optional, List
from pydantic import BaseModel, Field
from langchain_core.messages import HumanMessage, SystemMessage
# 定义大模型
model = ChatOpenAI(model="gpt-4o-mini")
# 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 让ai学习判断有没有人
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=[]) 准确的参考标准) LangChain 提供的一个工具函数,用于将"少样本示例"(few-shot examples)转换为标准的聊天消息序列,方便插入到提示词模板中。
ai_response=ai_response, # 让 LLM 强制返回ai_response
))
#[
# HumanMessage(content='海洋是广阔的、蓝色的。它有两万多英尺深', additional_kwargs={}, response_metadata={}),
# AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'a2964a25-ba20-426a-978a-16f1b8b2066d', 'type': 'function', 'function': {'name': 'Data', 'arguments': '{"people":[]}'}}]}, response_metadata={}, tool_calls=[{'name': 'Data', 'args': {'people': []}, 'id': 'a2964a25-ba20-426a-978a-16f1b8b2066d', 'type': 'tool_call'}]),
# ToolMessage(content='You have correctly called this tool.', tool_call_id='a2964a25-ba20-426a-978a-16f1b8b2066d'),
# AIMessage(content='未检测到人', additional_kwargs={}, response_metadata={}),
#
# HumanMessage(content='小明在跳舞,1米78的身高看起来很灵活', additional_kwargs={}, response_metadata={}),
# AIMessage(content='', additional_kwargs={'tool_calls': [{'id': '40d9eba9-d8ba-48ee-8c12-a0f3632eab90', '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': '40d9eba9-d8ba-48ee-8c12-a0f3632eab90', 'type': 'tool_call'}]),
# ToolMessage(content='You have correctly called this tool.', tool_call_id='40d9eba9-d8ba-48ee-8c12-a0f3632eab90'),
# AIMessage(content='检测到人', additional_kwargs={}, response_metadata={})
# ]
# print(example_messages)
# 5. 定义的结构化模型
structured_model = model.with_structured_output(schema=Data)
# 6. 定义链(没有强制结构化输出)
chain = prompt_template | structured_model
print(chain.invoke(
{
"example_messages": example_messages,
"new_message": "篮球场上,身高两米的中锋王伟默契地将球传给一米七的后卫挚友李明,完成一记绝杀。这对老友用十年配合弥补了身高的差距。"
}
))
# 虽然结果准确,但我们可以通过示例加强信息提取的准确度
总结:该案例展⽰了LangChain在结构化信息提取中的核⼼能⼒:通过Pydantic 定义输出模式、少样本学习引导模型⾏为、提⽰词模板动态组装上下⽂,以及链式调⽤简化流程。最终实现从⾮结构化⽂本中精准提取结构化数据。
四:🔥 示例选择器(Example selectors)
🦋 概念
⼀旦我们有了⽰例数据集,就需要考虑提⽰中应该有多少个⽰例。关键的权衡是,更多的⽰例通常会提⾼性能,但更⼤的提⽰会增加成本和延迟。超过某个阈值,太多⽰例可能会开始混淆模型。
找到正确数量的⽰例在很⼤程度上取决于模型、任务、⽰例的质量以及成本和延迟限制。有趣的是,模型越好,它需要精准的⽰例就越少。但其实,最佳的⽅法是使⽤不同数量的⽰例进⾏⼀些实验。
若此时我们有【⼤量】的⽰例数据集。对于⼤模型来说,就没必要全部使⽤与参考。我们需要有⼀种⽅法可以根据给定的输⼊,从数据集中选择⽰例。
在LangChain中,⽰例选择器就可以帮我们从⼀组【⽰例的集合】中根据具体策略选择正确的【⽰例⼦集】构建少样本提⽰。

选择策略有:
- Length :根据特定【⻓度】内可以容纳的数量选择⽰例。
- Similarity :使⽤输⼊和⽰例之间的【语义相似性】来决定选择哪些⽰例。
- MMR :使⽤输⼊和⽰例之间的【最⼤边际相关性】来决定要选择哪些⽰例。
- Ngram :使⽤输⼊和⽰例之间的【ngram重叠】来决定要选择哪些⽰例。
这些其实都是⾃然语⾔处理(NLP)⾥的相似性衡量问题。
🦋 按长度选择示例
当我们担⼼构造提⽰时,将超过上下⽂窗⼝⻓度,根据特定⻓度内可以容纳的数量选择⽰例。对于较⻓的输⼊,它将选择更少的⽰例来包含;⽽对于较短的输⼊,它将选择更多⽰例。
实现按⻓度选择⽰例的⽰例选择器是:
class langchain_core.example_selectors.length_based.LengthBasedExampleSelector 类,其参数如下:
- example_prompt :PromptTemplate,⽤于格式化⽰例的提⽰模板。
- examples :模板所需的⽰例列表。
- max_length :提⽰的最⼤⻓度,超过该⻓度将剪切⽰例。
- get_text_length :测量提⽰⻓度的⽅法。默认为字数统计。
内置⽅法:
add_example(example: dict[str, str]) :将新⽰例添加到列表中。
- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
select_examples(input_variables: dict[str, str]) → list[dict] :根据输⼊⻓度选择要使⽤的⽰例。- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
- 输出:要包含在提⽰中的⽰例列表。
接下来,演⽰⼀下如何使⽤⻓度⽰例选择器,让我们:
- 给⼀个⽰例集,输⼊和输出互为反义词
- 定义 PromptTemplate 字符串模板,包含输⼊和输出两个"占位符"
- 定义 LengthBasedExampleSelector ⻓度⽰例选择器,设置初始⽰例集与最⼤⻓度
- 定义⼀个 FewShotPromptTemplate 模板对象,⽤于实例化⽰例,将⽰例转化为聊天消息
- 打印消息结果
代码如下:
python
# 示例选择器 按照长度
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,
# 格式化⽰例的最⼤⻓度。
# ⻓度由下⾯的get_text_length函数测量。
max_length=25,
# ⽤于获取字符串⻓度的函数,⽤于确定包含哪些⽰例。
# 如果没有指定,它是作为默认值提供的。
# 该函数返回⼀个整数,表⽰字符串中由换⾏符或空格分隔的"单词"数量
# get_text_length: Callable[[str], int] = lambda x: len(re.split("\n| ",x))
)
# 少样本模版 转换 Message
# ⽤于实例化⽰例的模板
dynamic_prompt = FewShotPromptTemplate(
# 提供了⼀个ExampleSelector⽽不是examples。
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输⼊的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
print(
dynamic_prompt.invoke({"adjective": "big"}).to_messages()[0].content
)
结果如下:
python
给出每个输⼊的反义词
Input: happy
Output: sad
Input: tall
Output: short
Input: energetic
Output: lethargic
Input: sunny
Output: gloomy
Input: windy
Output: calm
Input: big
Output:
可以看到,⽰例选择器将所有的⽰例都选择了,因为还没有超过设置的⽰例最⼤⻓度,⻓度计算是根据字符串中由换⾏符或空格分隔的"单词"数量得到的(默认)。
接下来让我们加⼊⻓输⼊,验证⻓度选择器筛选⽰例。代码如下:
python
# ⼀个⻓的⽰例输⼊,所有⽰例超出最⼤⻓度
long_string = "⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常⾮常 ⾮常⼤,⽐其他任何东西都要⼤得多"
print(
dynamic_prompt.invoke({"adjective": long_string}).to_messages()[0].content
)
结果如下:
python
给出每个输⼊的反义词
Input: happy
Output: sad
Input: tall
Output: short
Input: ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常 ⾮常⼤,⽐其他任何东西都要⼤得多
Output:
🦋 按语义相似性选择示例(Similarity)
🎀 概念
什么是语义相似?它是衡量⽂本在【含义上】的接近程度。例如下述两段⽂本:
text1="我喜欢猫"
text2="我讨厌狗"
这两段⽂本表⾯相似度低,但语义上都是表达对动物的态度。
再例如:
text1="苹果很甜"
text2="苹果市值创新⾼"
"苹果"可以指⽔果或公司,语义相似可以解决⼀词多义问题,因此这两段⽂本语义上不相似。
LangChain能根据输⼊和⽰例之间的语义相似性来决定选择哪些⽰例,它通过查找与输⼊具有最⼤余弦相似性的嵌⼊⽰例来实现这⼀点。
🎀 实现
实现按语义相似性选择⽰例的⽰例选择器是: class langchain_core.example_selectors.semantic_similarity.SemanticSimilarity
ExampleSelector 类,
内置⽅法:
from_examples() :根据⽰例集⽣成语义相似⽰例选择器
- 输⼊:
- examples :⽰例列表
- embeddings :初始化的嵌⼊API接⼝,如 OpenAIEmbeddings()
- vectorstore_cls :向量存储数据库接⼝类。
- k :最终要选择的⽰例的数量。默认值为4。
- 输出:语义相似性⽰例选择器
add_example(example: dict[str, str]) :将新⽰例添加到列表中。- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
select_examples(input_variables: dict[str, str]) → list[dict] :根据输⼊选择要使⽤的⽰例。- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
- 输出:要包含在提⽰中的⽰例列表。
接下来,演⽰⼀下如何使⽤语义相似⽰例选择器,让我们:
- 给⼀个⽰例集,输⼊和输出互为反义词
- 定义 PromptTemplate 字符串模板,包含输⼊和输出两个"占位符"
- 定义 SemanticSimilarityExampleSelector 语义相似⽰例选择器,设置初始⽰例集与嵌⼊API接⼝
- 定义⼀个 FewShotPromptTemplate 模板对象,⽤于实例化⽰例,将⽰例转化为聊天消息
- 打印消息结果
代码如下:
注意:运⾏前先安装 langchain-chroma 库: pip install -U langchain-chroma
python
# 示例选择器 按照语义相似性
from langchain_chroma import Chroma
from langchain_core.example_selectors import LengthBasedExampleSelector, 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(model="text-embedding-3-large"), # 使用嵌入模型的能力度量语义
Chroma, # 向量数据库,⽤于存储嵌⼊并对其进⾏相似性搜索。
k=1, # ⽣成⽰例的数量。只选择一个相似度最高的
)
# 少样本模版 转换 Message
# ⽤于实例化⽰例的模板
dynamic_prompt = FewShotPromptTemplate(
# 提供了⼀个ExampleSelector⽽不是examples。
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输⼊的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
print(
dynamic_prompt.invoke({"adjective": "worried"}).to_messages()[0].content
)
结果如下:
python
给出每个输⼊的反义词
Input: happy
Output: sad
Input: worried
Output:
我们输⼊的内容和最后选择的⽰例都表⽰⼼情,在语义上最相似!
🦋 按最大边际相关性选择示例(MMR)
🎀 概念
什么是最⼤边际相关性?它是⼀种重新排序算法,它使⽤语义相似性作为基础⼯具,从⼀个候选集中挑选出⼀组既能代表查询主题⼜彼此多样化的结果。
听起来好像和语义相似性类似,⽤⼀个例⼦看下两者的区别:
- 【语义相似性】就像⾯试官衡量每个应聘者与职位要求的匹配度。他会给每个应聘者打⼀个分数。
- 【最⼤边际相关性】就像团队经理(MMR算法)要组建⼀个团队。⽬标是选出⼀组"精华"结果,⽽不是⼀个单⼀结果:
- 每个成员都要满⾜基本职位要求(满⾜相关性)。
- 但经理不希望团队⾥全是只会⼀种技能的程序员。他需要前端、后端、算法、测试等不同专⻓的⼈,以确保团队能⼒全⾯、减少冗余(新颖性/多样性)。
- 经理的策略是:先招⼀个最匹配的技术⼤⽜(第⼀步),然后接下来招的⼈,既要技术达标,⼜要和已招的⼈技能互补(迭代过程)。
了解下使⽤最⼤边际相关性的场景,更能让我们理解其概念:
- 语义相似性使⽤场景:搜索引擎的基础排序、重复检测、聚类、语义搜索。
- MMR使⽤场景:
- 推荐系统:推荐与用户兴趣相关但⼜不同类型的物品,避免"信息茧房"。
- ⽂档摘要:从⻓⽂档中选择能代表主旨⼜包含不同信息的句⼦,避免摘要内容重复。
- RAG(检索增强⽣成):在从知识库检索完⼀堆相关⽂档后,使⽤MMR进⾏去重和多样化筛选,再交给LLM⽣成答案,能有效提升答案质量和减少幻觉。
🎀 实现
了解了相关概念后,LangChain提供了按最⼤边际相关性选择⽰例的能⼒,该⽰例选择器是:
class
langchain_core.example_selectors.semantic_similarity.MaxMarginalRelevanceExampleSelector 类,
内置⽅法:
from_examples() :根据⽰例集⽣成MMR⽰例选择器
- 输⼊:
- examples :⽰例列表
- embeddings :初始化的嵌⼊API接⼝,如 OpenAIEmbeddings()
- vectorstore_cls :向量存储数据库接⼝类。
- k :最终要选择的⽰例的数量。默认值为4。
- 输出:MMR⽰例选择器
add_example(example: dict[str, str]) :将新⽰例添加到列表中。- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
select_examples(input_variables: dict[str, str]) → list[dict] :根据输⼊选择要使⽤的⽰例。
- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
- 输出:要包含在提⽰中的⽰例列表。
接下来,演⽰⼀下如何使⽤MMR⽰例选择器,让我们:
- 给⼀个⽰例集,输⼊和输出互为反义词
- 定义 PromptTemplate 字符串模板,包含输⼊和输出两个"占位符"
- 定义 MaxMarginalRelevanceExampleSelector MMR⽰例选择器,设置初始⽰例集与嵌⼊API接⼝
- 定义⼀个 FewShotPromptTemplate 模板对象,⽤于实例化⽰例,将⽰例转化为聊天消息
- 打印消息结果
代码如下:
注意:运⾏前先安装 langchain-chroma 库: pip install -U langchain-chroma
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}",
)
# 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
)
结果如下:
python
给出每个输⼊的反义词
Input: happy
Output: sad
Input: windy
Output: calm
Input: worried
Output:
🦋 重叠选择示例
🎀 概念
什么是【ngram】?ngram指⼀个⽂本序列中连续的n个词(word)或字符(character)。
什么是【ngram重叠】?通过计算它们之间共同拥有的ngram数量来⼀种衡量两段⽂本相似度的⽅法。
例如下述两段⽂本:
text1="苹果⼿机很好⽤"(分词后: 苹果 ⼿机 很 好⽤ )
text2="这款⼿机很好⽤"(分词后: 这款 ⼿机 很 好⽤ )
这两段⽂本单词重复度很⾼,连续三个词的相同的情况也存在,因此ngram重叠⾼。
再看个例⼦:
text1=苹果⼿机很好⽤"(分词后: 苹果 ⼿机 很 好⽤ )
text2="iPhone⾮常不错"(分词后: iPhone ⾮常 不错 )
这两段⽂本在含义上⾮常相似,但它们的ngram重叠度为0。
因此,传统ngram重叠是⼀种表⾯形式的匹配。它只关⼼词是否完全⼀样,但对于同义词却⽆法处理。
什么是【语义ngram重叠】?不再⽐较词本⾝,⽽是⽐较词背后的语义向量(Embedding)。也就是说,它不是看两个词 [苹果] 和 [iPhone] 的字⾯是否相同,⽽是计算它们在语义空间中的向量是否相似。如果相似度超过某个阈值,就认为它们"重叠"了。还是看这个例⼦:
text1=苹果⼿机很好⽤"(分词后: 苹果 ⼿机 很 好⽤ )
text2="iPhone⾮常不错"(分词后: iPhone ⾮常 不错 )
- 计算 苹果 和 iPhone 的向量相似度→得分0.95(很⾼,视为重叠)
- 计算 ⼿机 和 iPhone 的向量相似度→得分0.88(很⾼,但可能不会同时计分,取决于算法设计,避免重复计算)
- 计算 很 和 ⾮常 的向量相似度→得分0.90(很⾼,视为重叠)
- 计算 好⽤ 和 不错 的向量相似度→得分0.82(很⾼,视为重叠)
- 最终,语义上的unigram重叠度可能为3或4(⾮常相似!)。
那么语义ngram重叠的使⽤场景是什么?语义ngram重叠常⽤于需要更精准语义评估的场景,例如剽窃检测 ,能够发现那些改换了词汇但保留了核⼼思想的"智能"剽窃。
🎀 实现
LangChain实现按语义ngram重叠选择⽰例的⽰例选择器是:
class langchain_community.example_selectors.ngram_overlap.NGramOverlapExampleSelector 类,其参数如下:
- example_prompt :PromptTemplate,⽤于格式化⽰例的提⽰模板。
- examples :模板所需的⽰例列表。
- threshold :算法停⽌的阈值。默认设置为-1.0。
- 对于负阈值:按[重叠分数]对⽰例进⾏排序,但不排除任何⽰例。
- 对于等于0.0的阈值:按[重叠分数]进⾏排序,并排除与输⼊没有ngram重叠的⽰例。
- 对于⼤于1.0的阈值:排除所有⽰例,并返回⼀个空列表。
内置⽅法:
- add_example(example: dict[str, str]) :将新⽰例添加到列表中。
- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
- select_examples(input_variables: dict[str, str]) → list[dict] :返回根据输⼊得到的重叠分数排序的降序⽰例列表。
- 输⼊:⼀个字典,其中键作为输⼊变量,值作为其值。
- 输出:要包含在提⽰中的⽰例列表。
接下来,演⽰⼀下如何使⽤语义相似⽰例选择器,让我们:
- 给⼀个⽰例集,设置相关⽂本
- 定义 PromptTemplate 字符串模板,包含输⼊和输出两个"占位符"
- 定义 NGramOverlapExampleSelector ⽰例选择器,设置初始⽰例集与阈值(-1),表⽰对⽰例进⾏排序,但不排除任何⽰例。
- 定义⼀个 FewShotPromptTemplate 模板对象,⽤于实例化⽰例,将⽰例转化为聊天消息
- 打印消息排序结果
代码如下:
注意:
- 运⾏前先安装 nltk 库: pip install nltk 。它是⾃然语⾔处理⼯具包,在NLP领域中最常使⽤的⼀个Python库。
- 运⾏前先安装 langchain_community 库: pip install -U langchain_community
python
from langchain_community.example_selectors import NGramOverlapExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 翻译⽰例
examples = [
{"input": "See Spot run.", "output": "看⻅Spot跑。"},
{"input": "My dog barks.", "output": "我的狗叫。"},
{"input": "Spot can run.", "output": "Spot可以跑。"},
]
# 字符串模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# NGram ⽰例选择器
example_selector = NGramOverlapExampleSelector(
examples=examples,
example_prompt=example_prompt,
threshold=-1.0,
)
# ⽤于实例化⽰例的模板
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输⼊的中⽂翻译",
suffix="Input: {sentence}\nOutput:",
input_variables=["sentence"],
)
# 按照重叠分数排序
print(
dynamic_prompt.invoke({"sentence": "Spot can run fast."}).to_messages()[0].content
)
结果如下:
python
给出每个输⼊的中⽂翻译
Input: Spot can run.
Output: Spot可以跑。
Input: See Spot run.
Output: 看⻅Spot跑。
Input: My dog barks.
Output: 我的狗叫。
Input: Spot can run fast.
Output:
可以看到结果集是按照ngram重叠分数进⾏了排序。
可以分别设置 threshold=0.0 和 1.0 进⾏测试,打印结果如下:
python
threshold=0.0 (表⽰排除不相关的⽰例)
给出每个输⼊的中⽂翻译
Input: Spot can run.
Output: Spot可以跑。
Input: See Spot run.
Output: 看⻅Spot跑。
Input: Spot can run fast.
Output:
threshold=1.0(排除所有⽰例)
给出每个输⼊的中⽂翻译
Input: Spot can run fast.
Output:
五:🔥 共勉
😋 以上就是我对 【LangChain】核心组件(上) 的理解, 觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~ 😉
