文章目录
有人问了,为什么一个简单的提示词还需要弄个模板呢?
其实不然,通过不通的模板,不但方便定义,而且有利于大模型准确的理解。
注:这篇笔记说的都是提示词相关的内容,所以有时prompt会省略掉,例如说template就表示promptTemplate。
运行机制:
1、定义模板
2、传入数据并格式化
3、输出标准化(重)
在 LangChain 等框架中,生成的不仅仅是字符串,而是一个PromptValue对象。
作用:这个对象是一个"变形金刚"。
如果传给普通模型(LLM),它调用 .to_string() 变成纯文本。
如果传给聊天模型(ChatModel),它调用 .to_messages() 变成 [HumanMessage(...)] 列表。
常见的提示词模板有哪些?
1、PromptTemplate
2、ChatPromptTemplate
3、FewShotPromptTemplate
如下表:
| 模板类型 | 核心作用 | 适用场景 |
|---|---|---|
| PromptTemplate | 通用字符串格式化 | 单次任务、简单文本生成 |
| ChatPromptTemplate | 结构化对话消息 | 聊天机器人、多轮对话、Agent |
| FewShotPromptTemplate | 注入示例数据 | 需要给模型看例子才能做对的复杂任务 |
PromptTemplate
PromptTemplate示例
python
from langchain.prompts import PromptTemplate
# 注意看模板的结尾:冒号和换行符是关键
# 我们人为制造了一个"填空题"的语境
template_str = """
你是一个专业的翻译助手。
请将以下中文翻译成英文。
中文:{input_text}
英文:""" # <--- 关键点:在这里戛然而止,强迫 LLM 续写英文
prompt = PromptTemplate.from_template(template_str)
# 生成结果:"...英文:" -> LLM 看到后会自然地接下去写 "Hello World"
ChatPromptTemplate
ChatPromptTemplate示例
python
from langchain.prompts import ChatPromptTemplate
# 我们不再拼接长字符串,而是定义"消息块"
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个专业的翻译助手,负责将中文翻译成英文。"), # 设定人设/指令
("human", "{input_text}") # 用户输入
# 不需要写 ("ai", ""),模型会自动在这里开始生成
])
# 生成结果:[SystemMessage(...), HumanMessage("你好")]
# -> ChatModel 收到后知道该输出 AIMessage 了
FewShotPromptTemplate
FewShotPromptTemplate示例
python
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_core.prompts import PromptTemplate
# 1. 定义示例数据 (Examples)
# 这就是"少样本"的核心:我们预先准备好几组标准的"输入-输出"对
examples = [
{
"document": "这部电影的特效简直炸裂,视觉盛宴!但是剧情有点拖沓,结尾让人摸不着头脑。",
"summary_json": "{\n \"主题\": \"电影评论\",\n \"核心观点\": \"特效好但剧情差\",\n \"情感\": \"混合\",\n \"评分\": 6\n}"
},
{
"document": "这款手机的电池续航能力太差了,充满电只能用半天。而且充电速度也很慢,完全不推荐购买。",
"summary_json": "{\n \"主题\": \"电子产品评论\",\n \"核心观点\": \"续航差,充电慢\",\n \"情感\": \"负面\",\n \"评分\": 2\n}"
},
{
"document": "昨天的会议主要讨论了下一季度的市场预算分配问题,大家一致同意增加在数字营销方面的投入。",
"summary_json": "{\n \"主题\": \"会议纪要\",\n \"核心观点\": \"增加数字营销预算\",\n \"情感\": \"中立\",\n \"评分\": null\n}"
}
]
# 2. 定义示例模板 (Example Prompt)
# 告诉 LangChain 如何把上面字典里的数据拼成一个字符串
example_prompt = PromptTemplate(
input_variables=["document", "summary_json"],
template="文档内容:{document}\n标准摘要:{summary_json}"
)
# 3. 构建 FewShotPromptTemplate
# 这是核心类,它会自动把 examples 和 example_prompt 组合起来
prefix = """你是一个专业的数据分析师。请阅读下方的"待处理文档",并严格按照示例中的格式输出 JSON 摘要。
注意:不要输出任何多余的解释文字,只输出 JSON。
以下是几个示例:"""
suffix = """待处理文档:{input_document}
标准摘要:"""
prompt = FewShotPromptTemplate(
examples=examples, # 传入示例列表
example_prompt=example_prompt, # 传入示例的格式化模板
prefix=prefix, # 头部指令
suffix=suffix, # 尾部输入(包含变量 {input_document})
input_variables=["input_document"] # 声明用户需要传入的变量
)
# ==========================================
# 4. 模拟用户输入(批量/多文档场景)
# ==========================================
user_input = """
文档 A:苹果发布了新款 Vision Pro,售价 3500 美元,虽然技术很先进,但价格让很多人望而却步。
文档 B:特斯拉的股价今天上涨了 5%,因为马斯克宣布了新的全自动驾驶测试计划。
"""
# 5. 生成最终提示词
final_prompt_value = prompt.format(input_document=user_input)
# 打印出来看看效果(模拟发送给模型前的样子)
print("------- 发送给模型的最终内容 -------")
print(final_prompt_value)
解读:
当你运行这段代码时,FewShotPromptTemplate 会自动执行以下拼接逻辑:
**1、Prefix(前缀):**先输出通用的指令("你是一个分析师...")。
**2、Examples(示例循环):**它会自动遍历 examples 列表,利用 example_prompt 把每一组示例格式化成文本,拼在指令后面。
文档内容:电影...
标准摘要:{JSON}...
文档内容:手机...
标准摘要:{JSON}...
**3、Suffix(后缀):**最后放上你的 user_input(待处理文档),并以 标准摘要: 结尾。
🚀 为什么这对"批量/多文档"很有用?
格式锁定:通过示例,模型会模仿示例中的 JSON 结构,即使你的 user_input 包含多个文档(文档 A 和 文档 B),模型也会倾向于按照示例的风格,把它们整合成一个结构化的输出。
逻辑示范:示例中展示了如何处理"混合情感"(电影评论),这教会了模型在面对复杂情况时该怎么做,而不仅仅是简单的正面/负面。
动态性:你可以随时更换 examples 列表。比如针对"医疗文档",你可以换成一组医疗相关的示例,而不需要修改核心代码逻辑。
这就是 Few-Shot 的威力:用示例代替复杂的规则说明。
ChatPromptTemplate和ChatPromptTemplate的区别
| 维度 | LLM (续写型) 的 PromptTemplate | ChatModel (交互型) 的 ChatPromptTemplate |
|---|---|---|
| 思维模式 | 剧本编剧模式:我要怎么写开头,演员才会顺着我的剧本演下去? | 对话管理模式:我要给系统什么规则?用户说了什么? |
| 模板结构 | 单一大字符串:包含所有指令和输入。 | 消息列表:由 System, Human, AI 等模块组成。 |
| 关键技巧 | 结尾诱导:模板通常以 Answer: 或 \n 结尾,利用模型的续写本能。 |
角色隔离:利用 system 角色来强行注入指令,模型会无条件服从。 |
| 如果写错 | 如果结尾没写好(例如以句号结尾),模型可能会自己接一句废话,或者直接停止。 | 如果把指令写在 human 里,模型可能会觉得这是用户在自言自语,而不是在下达命令。 |
baseMessage属于promptTemplate吗?
不属于。
baseMessage是数据单元
promptTemplate是模板工具
他们相互协作将消息定义的更加清楚,更加结构化。
PromptValue
提示值。实际就是发给大模型前的最后一站了。
template、model、value之间是什么关系?
如下这个例子讲的太好了:
1、PromptTemplate 是工厂,负责生产内容。
2、Model 是客户,有的客户只收现金(String),有的客户只收刷卡(List[Message])。
3、PromptValue 就是"万能支付卡"。
你拿着这张卡(PromptValue)去付款。
如果客户要现金,它就提现(to_string)。
如果客户要刷卡,它就刷卡(to_messages)。
自定义格式化转换器
场景:
主流的大模型都支持提示词转换,但是假如某个大模型不支持提示词转换怎么办,那么我们完全可以自己做一个提示词转换工具。
例如,就复用PromptValue这个类。
实现两个方法即可:
to_string()
to_messages()
1、自定义提示词模板:
python
from typing import List, Any
from langchain_core.prompt_values import BasePromptValue
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
# 1. 定义自定义的 PromptValue
class MyCustomPromptValue(BasePromptValue):
# 这里定义你的数据源,比如一段文本和一个特殊的指令头
text: str
special_header: str = "[SPECIAL_MODE]"
# 核心方法 A:适配"普通文本模型"
# 当这个 PromptValue 传给一个只认字符串的模型时,会调用这个方法
def to_string(self) -> str:
return f"{self.special_header}\n\n{self.text}"
# 核心方法 B:适配"聊天模型"
# 当这个 PromptValue 传给 ChatModel 时,会调用这个方法
def to_messages(self) -> List[BaseMessage]:
# 这里你可以随意定义消息的结构
# 比如:把特殊头放在 SystemMessage 里,正文放在 HumanMessage 里
return [
SystemMessage(content="系统已激活特殊模式。"),
HumanMessage(content=f"{self.special_header} {self.text}")
]
# (可选) 如果你需要序列化(比如保存链条),可能需要实现这个
def __str__(self):
return self.to_string()
2、调用并测试
python
# 实例化你的自定义 PromptValue
my_value = MyCustomPromptValue(text="你好,世界", special_header="[DEBUG]")
# 测试变身成字符串 (给普通模型看)
print("--- 字符串格式 ---")
print(my_value.to_string())
# 输出: [DEBUG]
# 你好,世界
# 测试变身成消息 (给聊天模型看)
print("\n--- 消息格式 ---")
for msg in my_value.to_messages():
print(f"{msg.type}: {msg.content}")
# 输出:
# system: 系统已激活特殊模式。
# human: [DEBUG] 你好,世界
自动调用和手动调用的区别?
自动调用,不需要显示的格式化:
(1)定义模板
(2)定义调用链
python
chain = prompt | model
model.invoke("你好")
手动调用,需要手动格式化,不需要定义调用链了,直接invoke:
(1)手动格式化
(2)不需要定义调用链
python
prompt_value="手动格式化的内容"
model.invoke("你好")