1. 本章目标 #
上一章讲清了 System / Human / AI / Tool 消息。前几章里,提示词却多是写死在代码字符串里。业务一复杂,常见问题是:
- 同一段人设复制粘贴多处,改一处漏三处
- 多轮历史、少样本示例、用户变量混在一起,难以复用
- 想做 A/B 或按场景切换 prompt,只能改代码再发版
提示词不只是「给模型看的一段话」,更是业务系统里可维护的一层配置。本章目标:
用 Prompt / ChatPrompt 模板把提示词做成可维护的一层:变量填充、少样本、系统提示设计。
学完你应能:
- 使用
PromptTemplate与ChatPromptTemplate - 用
MessagesPlaceholder插入历史消息 - 做 few-shot(字符串版与聊天消息版)
- 用
partial预填固定变量 - 写出更稳的系统提示,并接到模型 / Agent
参考(API 在 langchain_core.prompts):
- Messages(模板最终仍产出消息)
- Agents · system_prompt
- LangSmith Prompt(团队协作时可把 prompt 放到平台)
2. 为什么需要 Prompt 层 #
把用户输入直接拼进长字符串,Demo 很快,业务一长大就疼:角色、历史、示例、变量全搅在一起,又难测又难改。
硬编码:
model.invoke("你是客服……用户说:" + user_text)
模板化:
prompt = ChatPromptTemplate.from_messages([...])
messages = prompt.format_messages(user_text=user_text, ...)
model.invoke(messages)模板层把「提示长什么样」从调用现场抽出来:
| 收益 | 说明 |
|---|---|
| 结构清晰 | system / human / ai 角色分开写 |
| 变量安全 | 缺变量会报错,少拼字符串 bug |
| 可复用 | 同一模板服务多个入口(API、脚本、Agent) |
| 可组合 | 历史占位、少样本、partial 预填 |
和 Agent 的关系要分清两层:
| 入口 | 管什么 | 典型用法 |
|---|---|---|
create_agent(system_prompt=...)(第 2 章) |
驾驭层级人设 / 全局约束 | 助手是谁、何时必须调工具 |
ChatPromptTemplate |
业务场景级提示 | 翻译模板、抽工单模板、带历史问答 |
两者常并存:Agent 有总人设,具体业务链仍用模板组织输入。第 6 章会把模板接到结构化输出上。
3. PromptTemplate:字符串模板 #
PromptTemplate 产出的是一整段文本,不是消息列表。
适合简单补全、非聊天协议,或只想把一段拼好的字符串塞进某个字段。
聊天模型场景下它也能用(字符串会被当成一条用户消息),但角色边界不清。
本章主推下一节的 ChatPromptTemplate;本节先建立「变量填充」的直觉。
3.1. 1.PromptTemplate.py #
# 从 langchain_core.prompts 导入 PromptTemplate,用于创建可填充变量的提示词模板
from langchain_core.prompts import PromptTemplate
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# {name} 是待填变量;缺变量时 format 会报错
# 用字符串模板创建 PromptTemplate,其中 {name} 为运行时填充的变量
prompt = PromptTemplate.from_template("你是简洁助手。用户叫{name},请用一句话打招呼。")
# 将 name 填充为「张三」,得到完整提示词文本
text = prompt.format(name="张三")
# 打印填充后的提示词,便于确认模板结果
print(text)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用填充后的提示词调用模型,并打印返回消息的文本内容
print(model.invoke(text).content)
要点:
from_template自动推断input_variables(这里是name)format(**kwargs)得到 字符串- 缺变量会报错——这是优点,比静默拼出残缺 prompt 更好查
- 聊天场景更推荐下一节的
ChatPromptTemplate(直接产出消息列表)
4. ChatPromptTemplate:对话模板(主推) #
现代聊天模型按「多条带角色的消息」工作。ChatPromptTemplate 正是为此而设计:
- 每条可以是
system/human/ai(也可用消息对象) - 每条文本里都能写
{变量} format_messages(...)得到第 4 章那种消息列表,可直接model.invoke
相对字符串模板,它更贴近真实对话状态,也更容易插入历史和少样本。
4.1. 2.ChatPromptTemplate.py #
下面示例故意在模板里放了一轮「假历史」(human/ai),用来演示:模板不仅可以填变量,还可以预置对话结构。
# 从 langchain_core.prompts 导入 ChatPromptTemplate,用于创建多角色对话提示词模板
from langchain_core.prompts import ChatPromptTemplate
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 用消息列表创建 ChatPromptTemplate,每条消息可含待填充变量
prompt = ChatPromptTemplate.from_messages(
# 消息模板列表:按 system / human / ai / human 顺序组织对话
[
# 系统消息:设定助手人设,{name} 为运行时填充的变量
("system", "你是乐于助人的助手,名字叫{name}。回答要短。"),
# 人类消息:模拟用户的开场问候
("human", "你好!"),
# AI 消息:模拟助手的自我介绍回复,同样使用 {name} 变量
("ai", "你好,我是{name},有什么可以帮你?"),
# 人类消息:用户真正的提问,{user_input} 为运行时填充的变量
("human", "{user_input}"),
]
)
# 填充全部变量 → 消息列表
# 将 name 与 user_input 填入模板,得到完整的消息对象列表
messages = prompt.format_messages(
# 助手名字填充为「小助」
name="小助",
# 用户输入填充为询问名字的问题
user_input="你叫什么名字?",
)
# 遍历填充后的每条消息,便于确认模板结果
for m in messages:
# 打印消息类型名称与消息文本内容
print(type(m).__name__, ":", m.content)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用填充后的消息列表调用模型,并打印返回消息的文本内容
print(model.invoke(messages).content)
调试习惯:先 print 填完后的 messages,确认角色顺序和变量都对,再调模型。
4.2. 管道写法预览(LCEL) #
第 7 章会系统讲 Runnable / LCEL。这里先建立直觉:
prompt | model= 先按变量填模板,再把消息列表交给模型。
调用方只传字典,不必再手写 format_messages + invoke 两步。
4.2.1. 3.prompt_pipe_model.py #
# 从 langchain_core.prompts 导入 ChatPromptTemplate,用于创建多角色对话提示词模板
from langchain_core.prompts import ChatPromptTemplate
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 用消息列表创建 ChatPromptTemplate,每条消息可含待填充变量
prompt = ChatPromptTemplate.from_messages(
# 消息模板列表:按 system / human 顺序组织翻译任务对话
[
# 系统消息:设定翻译目标语言变量 {target_lang},并要求只输出译文
("system", "把用户句子翻译成{target_lang},只输出译文。"),
# 人类消息:待翻译的原文,{text} 为运行时填充的变量
("human", "{text}"),
]
)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用 LCEL 管道把「填模板 → 调模型」串成一条链
chain = prompt | model
# 传入目标语言与原文,调用整条链得到模型回复
ai = chain.invoke({"target_lang": "英文", "text": "生活不止眼前的代码"})
# 打印模型返回消息的文本内容(即译文)
print(ai.content)
数据流可以记成:
{"target_lang","text"} → ChatPromptTemplate → messages → model → AIMessage5. MessagesPlaceholder:插入历史消息 #
普通 {var} 只能填充字符串。多轮对话要插入的是一整段消息对象列表(第 4 章的 HumanMessage / AIMessage 等),这时用 MessagesPlaceholder。
典型结构:
[SystemMessage] ← 人设
[...history...] ← MessagesPlaceholder 插入
[HumanMessage] ← 当前问题这和第 4 章手写 history.append(...) 是同一思路,只是把「历史插在哪」声明进了模板。
5.1. 4.MessagesPlaceholder.py #
# 从 langchain_core.prompts 导入 ChatPromptTemplate 与 MessagesPlaceholder,用于对话模板与历史消息占位
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# 从 langchain.messages 导入 HumanMessage 与 AIMessage,用于构造人类与助手消息对象
from langchain.messages import HumanMessage, AIMessage
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 用消息列表创建 ChatPromptTemplate,支持插入历史与当前问题变量
prompt = ChatPromptTemplate.from_messages(
# 消息模板列表:按 system / 历史占位 / human 顺序组织对话
[
# 系统消息:设定助手为简洁风格,并要求结合历史回答
("system", "你是简洁助手,结合历史回答。"),
# 历史消息占位符:运行时插入名为 history 的消息对象列表
MessagesPlaceholder("history"),
# 人类消息:当前用户问题,{question} 为运行时填充的变量
("human", "{question}"),
# 结束消息模板列表
]
# 结束 ChatPromptTemplate.from_messages 调用
)
# 构造模拟的多轮对话历史,供 MessagesPlaceholder 插入
history = [
# 人类消息:用户自称叫李雷
HumanMessage("我叫李雷。"),
# AI 消息:助手确认用户名字为李雷
AIMessage("好的,李雷。"),
# 结束历史消息列表
]
# 将 history 与 question 填入模板,得到完整的消息对象列表
messages = prompt.format_messages(
# 把预先构造的历史消息列表填入 history 占位符
history=history,
# 当前问题填充为询问自己的名字
question="我叫什么名字?",
# 结束 format_messages 调用
)
# 遍历填充后的每条消息,便于确认模板结果
for m in messages:
# 打印消息类型名称与消息文本内容
print(type(m).__name__, ":", m.content)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用填充后的消息列表调用模型,并打印返回消息的文本内容
print(model.invoke(messages).content)
注意:
history必须是 Message 列表,不能是普通字符串列表- 历史会越积越长,撞上下文窗口;生产级裁剪 / checkpoint 见第 11 章
- 可选:
MessagesPlaceholder("history", optional=True),允许调用时不传历史(首轮对话很方便)
6. 少样本(Few-shot) #
少样本 = 在提示里放若干「输入 → 期望输出」例子,让模型模仿格式与风格。
口头规则(「请只输出标签」)往往不如「看两个例子照着做」稳,尤其是分类、抽取、固定格式输出。
三种写法由易到难:
| 写法 | 适合 | 产出形态 |
|---|---|---|
| 模板里直接写死 human/ai 示例 | 示例少、几乎不变 | 聊天消息 |
FewShotPromptTemplate |
示例多、要统一文本格式 | 一整段字符串 |
FewShotChatMessagePromptTemplate |
聊天模型 + 可维护示例列表 | 聊天消息对 |
原则:示例的输出格式必须和最终想要的格式一模一样(多一个句号、多一句解释,都会被模型学歪)。
6.1. 聊天消息里直接写死示例(最简单) #
适合示例少、几乎不变:直接把几组 ("human", ...) / ("ai", ...) 写进 from_messages。
6.1.1. 5.few_shot_inline.py #
# 从 langchain_core.prompts 导入 ChatPromptTemplate,用于创建多角色对话提示词模板
from langchain_core.prompts import ChatPromptTemplate
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 用消息列表创建 ChatPromptTemplate,内含系统指令、少样本示例与用户输入变量
prompt = ChatPromptTemplate.from_messages(
# 消息模板列表:按 system / human / ai / human 顺序组织少样本对话
[
# 系统消息:规定情感分类标签,并要求模型只输出标签
("system", "将情感分类为:正面 / 负面 / 中性。只输出标签。"),
# 少样本示例 1:正面情感的用户输入
# 示例 1
("human", "今天天气真好,心情不错。"),
# 少样本示例 1:对应的期望输出标签
("ai", "正面"),
# 少样本示例 2:负面情感的用户输入
# 示例 2
("human", "快递又丢了,太糟心了。"),
# 少样本示例 2:对应的期望输出标签
("ai", "负面"),
# 真正待分类的用户输入占位,{text} 为运行时填充的变量
# 真正的用户输入
("human", "{text}"),
# 结束消息模板列表
]
# 结束 ChatPromptTemplate.from_messages 调用
)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用管道将提示词模板与模型串联成可调用链
chain = prompt | model
# 传入待分类文本调用链,并打印模型返回的情感标签
print(chain.invoke({"text": "这部电影一般般,不好不坏。"}).content)
期望输出应接近 中性(而不是一整句解释)。
6.2. FewShotPromptTemplate(字符串少样本) #
当示例变多、又希望统一管理「每条示例长什么样」时,用:
examples 列表
+ example_prompt(单条怎么格式化)
+ prefix / suffix(任务说明与真正输入)
→ 拼成一整段文本它更偏字符串侧,常和 PromptTemplate 一起用。
6.2.1. 6.FewShotPromptTemplate.py #
# 从 langchain_core.prompts 导入 PromptTemplate 与 FewShotPromptTemplate,用于创建字符串模板与少样本提示词模板
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 定义少样本示例列表,每项包含词语及其反义词
examples = [
# 示例 1:词语「开心」对应反义词「难过」
{"word": "开心", "antonym": "难过"},
# 示例 2:词语「高」对应反义词「矮」
{"word": "高", "antonym": "矮"},
# 结束少样本示例列表
]
# 用字符串模板创建单条示例的 PromptTemplate,规定「词语 / 反义词」的展示格式
example_prompt = PromptTemplate.from_template("词语:{word}\n反义词:{antonym}")
# 用示例列表、单例模板与前后缀组装 FewShotPromptTemplate
few_shot = FewShotPromptTemplate(
# 传入少样本示例列表,供模板逐条格式化后拼入提示词
examples=examples,
# 传入单条示例的格式化模板
example_prompt=example_prompt,
# 前缀:说明任务目标,并要求参照示例格式作答
prefix="给出词语的反义词。参照示例格式作答。\n",
# 后缀:放置真正待求反义词的输入占位,并以「反义词:」引导模型续写
suffix="\n词语:{input}\n反义词:",
# 声明运行时需要填充的变量名为 input
input_variables=["input"],
# 结束 FewShotPromptTemplate 构造
)
# 将 input 填充为「冷」,得到完整的少样本提示词文本
text = few_shot.format(input="冷")
# 打印填充后的提示词,便于确认模板结果
print(text)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用填充后的提示词调用模型,并打印返回消息的文本内容
print(model.invoke(text).content)建议先 print(text) 看拼出来的全文:prefix、每条示例、suffix 是否按预期排列。
6.3. FewShotChatMessagePromptTemplate(聊天少样本) #
把示例也做成 human/ai 消息对,更贴合 Chat Model,也更容易和 ChatPromptTemplate 嵌套组合。
结构通常是:
system
few_shot_prompt → 展开成多组 human/ai
human(真正输入)6.3.1. 7.FewShotChatMessagePromptTemplate.py #
# 从 langchain_core.prompts 导入 ChatPromptTemplate 与 FewShotChatMessagePromptTemplate,用于聊天提示词与少样本聊天模板
from langchain_core.prompts import (
# 导入 ChatPromptTemplate,用于创建多角色对话提示词模板
ChatPromptTemplate,
# 导入 FewShotChatMessagePromptTemplate,用于构建聊天消息形式的少样本提示
FewShotChatMessagePromptTemplate,
# 结束 from langchain_core.prompts 的导入
)
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 定义少样本示例列表,每项包含输入算式与期望输出
examples = [
# 示例 1:输入「2+2」对应输出「4」
{"input": "2+2", "output": "4"},
# 示例 2:输入「2+3」对应输出「5」
{"input": "2+3", "output": "5"},
# 结束少样本示例列表
]
# 用消息列表创建单条示例的 ChatPromptTemplate,规定 human/ai 消息对格式
example_prompt = ChatPromptTemplate.from_messages(
# 消息模板列表:按 human / ai 顺序组织单条少样本示例
[
# 人类消息:示例中的用户输入,{input} 为示例变量
("human", "{input}"),
# AI 消息:示例中的期望输出,{output} 为示例变量
("ai", "{output}"),
# 结束消息模板列表
]
# 结束 ChatPromptTemplate.from_messages 调用
)
# 用示例列表与单例模板组装 FewShotChatMessagePromptTemplate
few_shot_prompt = FewShotChatMessagePromptTemplate(
# 传入少样本示例列表,供模板逐条格式化为消息对后拼入提示词
examples=examples,
# 传入单条示例的消息格式化模板
example_prompt=example_prompt,
# 结束 FewShotChatMessagePromptTemplate 构造
)
# 用消息列表创建最终 ChatPromptTemplate,串联系统指令、少样本与用户输入
final_prompt = ChatPromptTemplate.from_messages(
# 消息模板列表:按 system / few-shot / human 顺序组织完整对话
[
# 系统消息:设定助手为算术助手,并要求只输出最终数字
("system", "你是算术助手。只输出最终数字。"),
# 插入少样本聊天消息模板,运行时展开为多组 human/ai 示例
few_shot_prompt,
# 人类消息:真正待计算的用户输入,{input} 为运行时填充的变量
("human", "{input}"),
# 结束消息模板列表
]
# 结束 ChatPromptTemplate.from_messages 调用
)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用管道将最终提示词模板与模型串联成可调用链
chain = final_prompt | model
# 传入待计算算式调用链,并打印模型返回的最终数字
print(chain.invoke({"input": "10+8"}).content)聊天场景优先这种写法;字符串版更多留给非对话场景,或需要导出纯文本 prompt 的场合。
7. partial:预填部分变量 #
一个模板里往往既有「几乎不变」的变量(租户名、产品线、固定角色),也有「每次请求都变」的变量(用户问题)。
partial 的作用:把前者先锁死,得到一个变量更少的新模板;调用时只需传业务变量。
base(role, user_name, question)
│ partial(role=..., user_name=...)
▼
prompt(question) ← 调用方更简单适合多租户、多产品共用同一骨架、只换几个固定字段的场景。
7.1. 8.partial.py #
# 从 langchain_core.prompts 导入 ChatPromptTemplate,用于创建多角色对话提示词模板
from langchain_core.prompts import ChatPromptTemplate
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 用消息列表创建基础 ChatPromptTemplate,含角色、用户名与问题变量
base = ChatPromptTemplate.from_messages(
# 消息模板列表:按 system / human 顺序组织对话
[
# 系统消息:设定助手角色 {role}、面向用户 {user_name},并要求回答简短
("system", "你是{role},面向用户{user_name},回答要短。"),
# 人类消息:用户当前问题,{question} 为运行时填充的变量
("human", "{question}"),
# 结束消息模板列表
]
# 结束 ChatPromptTemplate.from_messages 调用
)
# 预填角色与用户名,只剩 question
prompt = base.partial(role="售后顾问", user_name="王女士")
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 用管道将预填后的提示词模板与模型串联成可调用链
chain = prompt | model
# 传入用户问题调用链,并打印模型返回消息的文本内容
print(chain.invoke({"question": "订单两天了还没发货,怎么办?"}).content)
PromptTemplate.partial(...) 同理。
也可以 partial 出两套链:prompt_a = base.partial(product_name="A")、prompt_b = base.partial(product_name="B"),再对比回答差异。
8. 系统提示设计 #
系统提示写得好,往往比堆很多 few-shot 更划算:成本低、意图清晰、边界明确。
可按下面六段组织(不必每次全写,但要想清楚有没有漏项):
1. 角色:你是谁
2. 目标:要完成什么
3. 能力与边界:能做什么 / 不能做什么
4. 工具策略:何时必须调工具(若有)
5. 输出格式:长短、语言、是否 JSON
6. 失败行为:信息不足时怎么问澄清和 Agent 的关系:
create_agent(system_prompt=...):适合驾驭层级(Agent 级)总约束- 业务链里的
("system", SYSTEM):适合某个场景的专用规则
两者可以叠加,但不要互相打架(例如一边说「尽量简短」,一边又要求长篇分析)。
8.1. 9.system_prompt_design.py #
# 从 langchain_core.prompts 导入 ChatPromptTemplate,用于创建多角色对话提示词模板
from langchain_core.prompts import ChatPromptTemplate
# 从 langchain.chat_models 导入 init_chat_model,用于按名称初始化聊天模型
from langchain.chat_models import init_chat_model
# 从 dotenv 导入 load_dotenv,用于从 .env 文件加载环境变量
from dotenv import load_dotenv
# 加载 .env 中的环境变量;override=True 表示覆盖已存在的同名变量
load_dotenv(override=True)
# 定义系统提示词:设定电商售后助手角色、目标、边界与输出格式,并用 strip 去掉首尾空白
SYSTEM = """
你是电商售后助手。
目标:用最短步骤帮用户解决问题。
边界:
- 不承诺未核实的退款时效
- 不知道物流状态时,请用户提供订单号
输出:
- 先给结论(1 句)
- 再给 1~3 条可执行步骤
- 使用中文
""".strip()
# 用消息列表创建 ChatPromptTemplate,串联系统提示与用户问题
prompt = ChatPromptTemplate.from_messages(
# 消息模板列表:按 system / human 顺序组织对话
[
# 系统消息:使用上方定义的 SYSTEM 提示词
("system", SYSTEM),
# 人类消息:用户当前问题,{question} 为运行时填充的变量
("human", "{question}"),
# 结束消息模板列表
]
# 结束 ChatPromptTemplate.from_messages 调用
)
# 初始化 DeepSeek 聊天模型,temperature=0 使输出更稳定、更少随机性
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 打印模型对用户问题的回复文本内容
print(
# 用管道将提示词模板与模型串联成可调用链
(prompt | model)
# 传入用户问题调用链,询问快递长时间显示运输中的情况
.invoke({"question": "我的快递一直显示运输中,已经五天了。"})
# 取出返回消息的文本内容
.content
)
改系统提示后,先固定几条测试问句做回归,比凭感觉改完就上线更靠谱。
9. 可维护性建议 #
模板层一旦进业务,就要当「配置」来维护,而不是当一次性字符串。
| 做法 | 说明 |
|---|---|
| 模板与业务代码分离 | 模板集中定义,入口只 invoke 变量 |
| 先 format 再调模型 | 出问题先 print(messages),别先怪模型 |
| 少样本要「像真输出」 | 示例格式必须和期望的最终格式一致 |
| 变量命名稳定 | {question} / {context} 全项目统一 |
| 敏感信息不进 prompt | 密钥、身份证等不要拼进模板 |
| 用 LangSmith 对比版本 | 改 prompt 前后对照 trace / 评测集 |
口诀:
结构用模板,格式靠 few-shot,边界写进 system;改完先 print messages。
10. 练习 #
- 翻译模板:用
ChatPromptTemplate做「源语言 / 目标语言 / 文本」三变量翻译链。 - 带历史客服:
MessagesPlaceholder("history")+ 两轮假历史,问第三轮是否记得用户名。 - 情感分类 few-shot:至少 3 条示例,测 5 句新文本,看标签是否稳定。
- partial 多租户:同一模板
partial(product_name=...)出两套「不同产品」的助手,对比回答。
11. 本章小结 #
- Prompt 层把提示词从散落字符串变成可复用组件。
- 聊天场景主用
ChatPromptTemplate;需要整段文本时用PromptTemplate。 MessagesPlaceholder插入消息历史;few-shot 用示例教格式。partial预填固定变量;系统提示写清角色、边界、输出格式。prompt | model是后续 LCEL 的起点。
下一章:结构化输出——用 Pydantic / Structured Output 抽出订单、工单等稳定结构。