1. 本章目标 #
前几章里,提示词多是写死在代码字符串里。业务一复杂就会出现:
- 同一段人设复制粘贴多处,改一处漏三处
- 多轮历史、少样本示例、用户变量混在一起,难以复用
- 想做 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 层 #
硬编码:
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=...) 管 harness 级人设;业务里更复杂的「翻译模板 / 抽工单模板 / 带历史的问答」仍常用 ChatPromptTemplate。
3. PromptTemplate:字符串模板 #
适合最终是一段文本的场景(简单补全、非聊天协议、或拼进某个字段)。
3.1. 1.PromptTemplate.py #
from langchain_core.prompts import PromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
# {name} 是待填变量;缺变量 format 会报错
prompt = PromptTemplate.from_template(
"你是简洁助手。用户叫{name},请用一句话打招呼。"
)
text = prompt.format(name="张三")
print(text)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(text).content)要点:
from_template自动推断input_variablesformat(**kwargs)得到 字符串- 聊天场景更推荐下一节的
ChatPromptTemplate(直接产出消息列表)
4. ChatPromptTemplate:对话模板(主推) #
按「多条带角色的消息」组织提示,每条都可含 {变量}。
4.1. 2.ChatPromptTemplate.py #
from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是乐于助人的助手,名字叫{name}。回答要短。"),
("human", "你好!"),
("ai", "你好,我是{name},有什么可以帮你?"),
("human", "{user_input}"),
]
)
# 填充全部变量 → 消息列表
messages = prompt.format_messages(
name="小助",
user_input="你叫什么名字?",
)
for m in messages:
print(type(m).__name__, ":", m.content)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(messages).content)也可用 invoke 得到 ChatPromptValue,再 .to_messages():
value = prompt.invoke({"name": "小助", "user_input": "你叫什么名字?"})
print(value.to_messages())4.2. 管道写法预览(LCEL) #
第 7 章会系统讲 Runnable。这里先建立直觉:prompt | model 把「填模板 → 调模型」串起来。
4.2.1. 3.prompt_pipe_model.py #
from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
prompt = ChatPromptTemplate.from_messages(
[
("system", "把用户句子翻译成{target_lang},只输出译文。"),
("human", "{text}"),
]
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
chain = prompt | model
ai = chain.invoke({"target_lang": "英文", "text": "生活不止眼前的代码"})
print(ai.content)5. MessagesPlaceholder:插入历史消息 #
普通 {var} 填充的是字符串;多轮历史需要插入一整段消息对象列表,用 MessagesPlaceholder。
5.1. 4.MessagesPlaceholder.py #
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.messages import HumanMessage, AIMessage
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是简洁助手,结合历史回答。"),
MessagesPlaceholder("history"),
("human", "{question}"),
]
)
history = [
HumanMessage("我叫李雷。"),
AIMessage("好的,李雷。"),
]
messages = prompt.format_messages(
history=history,
question="我叫什么名字?",
)
for m in messages:
print(type(m).__name__, ":", m.content)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(messages).content)注意:history 必须是 Message 列表,不能是普通字符串列表。第 11 章 Memory 会把「自动维护 history」接上。
可选:MessagesPlaceholder("history", optional=True),允许调用时不传历史。
6. 少样本(Few-shot) #
少样本 = 在提示里放若干「输入 → 期望输出」例子,让模型模仿格式与风格。常比单纯口头描述规则更稳。
6.1. 聊天消息里直接写死示例(最简单) #
适合示例少、固定不变:
6.1.1. 5.few_shot_inline.py #
from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
prompt = ChatPromptTemplate.from_messages(
[
("system", "将情感分类为:正面 / 负面 / 中性。只输出标签。"),
# 示例 1
("human", "今天天气真好,心情不错。"),
("ai", "正面"),
# 示例 2
("human", "快递又丢了,太糟心了。"),
("ai", "负面"),
# 真正的用户输入
("human", "{text}"),
]
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
chain = prompt | model
print(chain.invoke({"text": "这部电影一般般,不好不坏。"}).content)6.2. FewShotPromptTemplate(字符串少样本) #
示例多、要统一格式时,用「示例列表 + 单例模板 + 前后缀」拼成一段文本:
6.2.1. 6.FewShotPromptTemplate.py #
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
examples = [
{"word": "开心", "antonym": "难过"},
{"word": "高", "antonym": "矮"},
]
example_prompt = PromptTemplate.from_template(
"词语:{word}\n反义词:{antonym}"
)
few_shot = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="给出词语的反义词。参照示例格式作答。\n",
suffix="\n词语:{input}\n反义词:",
input_variables=["input"],
)
text = few_shot.format(input="冷")
print(text)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(text).content)6.3. FewShotChatMessagePromptTemplate(聊天少样本) #
把示例也做成 human/ai 消息对,更贴 Chat Model:
6.3.1. 7.FewShotChatMessagePromptTemplate.py #
from langchain_core.prompts import (
ChatPromptTemplate,
FewShotChatMessagePromptTemplate,
)
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
examples = [
{"input": "2+2", "output": "4"},
{"input": "2+3", "output": "5"},
]
example_prompt = ChatPromptTemplate.from_messages(
[
("human", "{input}"),
("ai", "{output}"),
]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
examples=examples,
example_prompt=example_prompt,
)
final_prompt = ChatPromptTemplate.from_messages(
[
("system", "你是算术助手。只输出最终数字。"),
few_shot_prompt,
("human", "{input}"),
]
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
chain = final_prompt | model
print(chain.invoke({"input": "10+8"}).content)示例很多、需要按长度或相似度动态挑选时,可接 example_selector(如 LengthBasedExampleSelector)。本课先掌握静态 few-shot;动态选择属于进阶优化。
7. partial:预填部分变量 #
把模板里很少变的变量先锁死,调用时只传业务变量。
7.1. 8.partial.py #
from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
base = ChatPromptTemplate.from_messages(
[
("system", "你是{role},面向用户{user_name},回答要短。"),
("human", "{question}"),
]
)
# 预填角色与用户名,只剩 question
prompt = base.partial(role="售后顾问", user_name="王女士")
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
chain = prompt | model
print(chain.invoke({"question": "订单两天了还没发货,怎么办?"}).content)PromptTemplate.partial(...) 同理。适合:租户名、产品名、固定人设等多处复用同一骨架。
8. 系统提示设计(实战清单) #
系统提示写得好,比堆 few-shot 更划算。可用下面结构:
1. 角色:你是谁
2. 目标:要完成什么
3. 能力与边界:能做什么 / 不能做什么
4. 工具策略:何时必须调工具(若有)
5. 输出格式:长短、语言、是否 JSON
6. 失败行为:信息不足时怎么问澄清8.1. 9.system_prompt_design.py #
from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
load_dotenv(override=True)
SYSTEM = """
你是电商售后助手。
目标:用最短步骤帮用户解决问题。
边界:
- 不承诺未核实的退款时效
- 不知道物流状态时,请用户提供订单号
输出:
- 先给结论(1 句)
- 再给 1~3 条可执行步骤
- 使用中文
""".strip()
prompt = ChatPromptTemplate.from_messages(
[
("system", SYSTEM),
("human", "{question}"),
]
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print((prompt | model).invoke(
{"question": "我的快递一直显示运输中,已经五天了。"}
).content)接到 Agent 时,把同一段文字放进 system_prompt 即可:
from langchain.agents import create_agent
agent = create_agent(
model="deepseek:deepseek-v4-flash",
tools=[], # 实战中挂查询订单等工具
system_prompt=SYSTEM,
)9. 可维护性建议 #
| 做法 | 说明 |
|---|---|
| 模板与业务代码分离 | 模板集中定义,入口只 invoke 变量 |
| 先 format 再调模型 | 出问题先 print(messages),别先怪模型 |
| 少样本要「像真输出」 | 示例格式必须和你期望的最终格式一致 |
| 变量命名稳定 | {question} / {context} 全项目统一 |
| 敏感信息不进 prompt | 密钥、身份证等不要拼进模板 |
| 用 LangSmith 对比版本 | 改 prompt 前后看 trace / 评测集 |
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 抽出订单、工单等稳定结构。