在构建 AI 应用时,提示词(Prompt) 的质量直接决定模型输出的效果。而如何高效、安全、可维护地管理提示词,是每个开发者都必须面对的课题。LangChain 提供了强大的提示词模板体系,帮助我们告别字符串拼接的混乱,走向模块化的工程实践。
本文将带你全面了解 LangChain 提示词模板的设计理念、基础用法和高级特性(包括组合模板),让你在项目中得心应手。
什么是提示词模板?
一张图告诉你

一、为什么需要提示词模板?
1. 字符串拼接的痛点
初学者通常直接用 Python 的 f-string 或 + 拼接提示词:
python
name = "小明"
user_input = "今天天气如何?"
prompt = f"你是一个AI助手,叫{name}\n用户问:{user_input}"
这种方式在 Demo 阶段很方便,但面临以下问题:
- 可读性差:变量一多,字符串乱成一锅粥。
- 易出错:缺少变量校验,拼写错误难以察觉。
- 难以复用:每个场景都要重新拼接。
- 不支持复杂结构:多轮对话、少样本示例、RAG 等场景难以优雅表达。
2. 提示词模板的优势
LangChain 的 ChatPromptTemplate 提供了:
- 结构清晰 :通过占位符
{variable}分离模板与数据。 - 自动校验:调用时检查所有变量是否提供,避免遗漏。
- 高度复用:同一模板可应用于不同场景。
- 生态集成:与 LangChain 的模型、链、代理无缝协作。
- 调试友好:可打印完整的消息列表,便于追踪。
开发建议:临时脚本可用拼接,正式项目请务必使用模板。
二、ChatPromptTemplate 基础
1. 从 from_messages() 创建(推荐)
这是最常用、最直观的方式,通过传入消息元组列表构建模板:
python
from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手,你的名字是{name}"),
("human", "你好,最近怎么样?"),
("ai", "我很好,谢谢!"),
("human", "{user_input}")
])
# 调用时传入变量
result = template.invoke({"name": "小智", "user_input": "你是谁?2+2等于几?"})
print(result)
输出:
text
messages=[SystemMessage(content='你是一个AI助手,你的名字是小智'),
HumanMessage(content='你好,最近怎么样?'),
AIMessage(content='我很好,谢谢!'),
HumanMessage(content='你是谁?2+2等于几?')]
💡 支持的角色类型:
system、human、ai(或assistant),也可用user代替human。
2. 直接实例化
你也可以直接创建 ChatPromptTemplate 对象,效果相同:
python
template = ChatPromptTemplate([
("system", "你是一个AI助手,你的名字是{name}"),
("human", "{user_input}")
])
三、模板的调用方式
创建模板后,我们有三种方式"填充"变量并得到结果。
1. invoke() ------ 返回 ChatPromptValue
python
value = template.invoke({"name": "小智", "user_input": "你好"})
print(value) # 完整的消息对象
print(value.messages) # 消息列表
2. format() ------ 返回纯字符串
python
text = template.format(name="小智", user_input="你好")
print(text)
# 输出:
# System: 你是一个AI助手,你的名字是小智
# Human: 你好
print(type(text)) # <class 'str'>
注意 :format() 返回的是纯字符串,适合用于非结构化场景。
3. format_messages() ------ 返回消息列表
python
messages = template.format_messages(name="小智", user_input="你好")
print(messages)
# [SystemMessage(...), HumanMessage(...)]
print(type(messages)) # <class 'list'>
推荐 :在与模型交互时,尽量使用 format_messages() 或 invoke(),因为它们保留了消息角色信息,更精确。
四、与 LLM 结合使用
将模板与模型串联,即可完成一个完整的调用链:
python
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
load_dotenv(override=True)
# 初始化模型(示例使用 OpenRouter 接入模型)
model = init_chat_model(
model="xiaomi/mimo-v2.5",
model_provider="openai",
api_key=os.getenv("OPENROUTER_API_KEY"),
base_url=os.getenv("OPENROUTER_BASE_URL")
)
template = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手,你的名字是{name}"),
("human", "{user_input}")
])
# 生成消息列表
messages = template.format_messages(name="小智", user_input="2+2等于几?")
# 调用模型
response = model.invoke(messages)
print(response.content)
输出示例:
text
你好!我是小智,2+2等于4。有什么我可以帮你的吗?
五、更丰富的初始化方式
from_messages() 支持多种参数类型,灵活适应不同场景。
1. 字符串列表(只有 human 消息)
python
template = ChatPromptTemplate.from_messages([
"你好,我是{name}"
])
# 等同于 ("human", "你好,我是{name}")
2. 元组列表(最常用)
python
template = ChatPromptTemplate.from_messages([
("system", "你是一个助手"),
("human", "我的名字是{name}")
])
3. 字典列表
python
template = ChatPromptTemplate.from_messages([
{"role": "system", "content": "你是一个助手"},
{"role": "human", "content": "我是{name}"}
])
4. 消息对象列表(⚠️ 注意:变量不会被替换)
python
from langchain_core.messages import SystemMessage, HumanMessage
template = ChatPromptTemplate.from_messages([
SystemMessage(content="你是一个助手"),
HumanMessage(content="你好,我是{name}") # 这里的 {name} 不会替换!
])
result = template.invoke({"name": "小明"})
# 输出中 HumanMessage 内容是 "你好,我是{name}" 字面值
因此,如果需要变量替换,不要直接传入消息对象,应使用模板字符串。
5. 使用 SystemMessagePromptTemplate 等子模板
python
from langchain_core.prompts import SystemMessagePromptTemplate, HumanMessagePromptTemplate
sys_template = SystemMessagePromptTemplate.from_template("你是{role}")
human_template = HumanMessagePromptTemplate.from_template("你好,{name}")
template = ChatPromptTemplate.from_messages([sys_template, human_template])
result = template.invoke({"role": "导游", "name": "游客"})
6. 组合已有的 ChatPromptTemplate 实例
python
template1 = ChatPromptTemplate.from_messages([("system", "你是助手")])
template2 = ChatPromptTemplate.from_messages([("human", "我是{name}")])
combined = ChatPromptTemplate.from_messages([template1, template2])
result = combined.invoke({"name": "小明"})
六、高级特性(上):部分变量预填充与消息占位符
1. 部分变量预填充(partial())
有时候,某些变量在多个请求中固定不变,可以预先填充,避免重复传入。
python
template = ChatPromptTemplate.from_messages([
("system", "你是{role},你的目标人群是{audience}"),
("user", "{task}")
])
# 预设 role 和 audience
final_template = template.partial(role="公务员", audience="人民群众")
res1 = final_template.invoke({"task": "为人民服务"})
res2 = final_template.invoke({"task": "为中国人民谋幸福"})
print(res1) # System: 你是公务员,你的目标人群是人民群众
print(res2) # System: 你是公务员,你的目标人群是人民群众
partial() 非常适合构建特定场景的子模板,例如在代码中固定 role 后,后续只需关注 task 变量。
2. 消息占位符(用于多轮对话)
当我们需要在模板中插入不确定数量的历史消息 时,可以使用 MessagesPlaceholder。
方式一:使用 "placeholder" 元组
python
template = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手"),
("placeholder", "{conversation}") # 占位名称为 conversation
])
result = template.invoke({
"conversation": [
("human", "天气怎么样?"),
("ai", "今天是晴天。"),
("human", "明天呢?")
]
})
方式二:使用 MessagesPlaceholder 对象(更明确)
python
from langchain_core.prompts import MessagesPlaceholder
template = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手"),
MessagesPlaceholder(variable_name="history")
])
实际应用:在 RAG 或聊天机器人中,将历史消息动态传入,而当前问题作为单独的变量:
python
template = ChatPromptTemplate.from_messages([
("system", "你是一个AI助手"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}")
])
result = template.invoke({
"history": [
("human", "你好"),
("ai", "你好!有什么可以帮你?")
],
"question": "帮我查一下明天的天气"
})
这样,历史消息和最新问题被清晰地区分开来,模型能够理解完整的对话上下文。
七、高级特性(下):PipelinePromptTemplate 组合模板(模块化利器)
在实际的复杂业务中,一个提示词往往由多个逻辑模块拼接而成,例如:
- 角色设定(你是谁)
- 背景知识(上下文)
- 风格约束(回答语气/格式)
- 任务指令(具体要做什么)
- 少样本示例(Few-shot)
如果把这些全部塞进一个巨大的模板字符串,变量会非常多,可读性和维护性都会急剧下降。此时,PipelinePromptTemplate 就是你的救星。
它允许你将一个"终极提示词"拆分成若干个"子模板",每个子模板负责生成其中一部分内容,最后再统一组装。
1. 基本用法
我们通过一个实际的例子来看:假设我们需要让 AI 扮演一位"职业规划顾问",面向不同人群给出建议,并要求特定的输出格式。
python
from langchain_core.prompts import PipelinePromptTemplate, PromptTemplate
# 1. 定义子模板:每个子模板只关注自己的逻辑
role_prompt = PromptTemplate.from_template("你的角色是:{role}")
audience_prompt = PromptTemplate.from_template("你的咨询对象是:{audience},他们面临的问题是:{pain_point}")
style_prompt = PromptTemplate.from_template("请使用{style}的语气进行回答")
format_prompt = PromptTemplate.from_template("回答格式要求:{format_rule}")
# 2. 定义最终模板:这里用 {sub_template_name} 占位引用子模板的输出
final_prompt = PromptTemplate.from_template(
"【系统设定】\n{role}\n{audience}\n{style}\n\n"
"【具体要求】\n{format}\n\n"
"【任务】\n请根据以上设定,回答用户的问题:{question}"
)
# 3. 组装管道:将子模板和最终模板绑定
pipeline = PipelinePromptTemplate(
final_prompt=final_prompt,
pipeline_prompts=[
("role", role_prompt), # 对应 final_prompt 中的 {role}
("audience", audience_prompt), # 对应 {audience}
("style", style_prompt), # 对应 {style}
("format", format_prompt) # 对应 {format}
]
)
# 4. 一次性传入所有原始变量,管道会自动逐层处理
result = pipeline.invoke({
"role": "资深职业规划师",
"audience": "应届毕业生",
"pain_point": "不知道选大厂还是考公务员,非常焦虑",
"style": "温和而理性,多用数据说话",
"format_rule": "1. 先分析利弊 2. 给出决策建议 3. 结尾附一句鼓励的话",
"question": "我该如何选择?"
})
# 查看生成的完整提示词
print(result.to_string())
输出效果:
text
【系统设定】
你的角色是:资深职业规划师
你的咨询对象是:应届毕业生,他们面临的问题是:不知道选大厂还是考公务员,非常焦虑
请使用温和而理性,多用数据说话的语气进行回答
【具体要求】
回答格式要求:1. 先分析利弊 2. 给出决策建议 3. 结尾附一句鼓励的话
【任务】
请根据以上设定,回答用户的问题:我该如何选择?
2. 为什么推荐这种方式?
| 特性 | 单个大模板 | Pipeline组合模板 |
|---|---|---|
| 可维护性 | 修改一处需全局查看 | 各模块解耦,修改互不影响 |
| 可测试性 | 难以单独测试某部分 | 可单独测试子模板逻辑 |
| 复用性 | 几乎无法复用片段 | 子模板可在不同场景重复使用 |
| 可读性 | 变量混杂,容易漏填 | 变量归属清晰,校验自动完成 |
| 团队协作 | 所有人都改同一个字符串 | 不同成员可各自维护子模板 |
3. 与 ChatPromptTemplate 的配合使用
⚠️ 注意 :
PipelinePromptTemplate原生设计用于PromptTemplate(纯字符串)。如果你需要组合ChatPromptTemplate(带角色消息),依然可以使用之前介绍的 4.6 节 组合方式(ChatPromptTemplate.from_messages([template1, template2]))。两者适用场景不同:
ChatPromptTemplate.from_messages([...]):适合组合不同角色(system/human/ai)的消息块。PipelinePromptTemplate:适合按逻辑功能(角色/任务/格式)切分内容。
在实战中,你可以将两者结合使用:外层用 ChatPromptTemplate 定义消息角色,内层用 PipelinePromptTemplate 精雕细琢某一段内容的生成逻辑。
python
# 假设我们先通过 Pipeline 生成复杂的 system 消息内容
# 注意:这里需要将 pipeline 的输出作为变量传入
system_part_template = PromptTemplate.from_template(
"你是{role}。你的背景:{background}。你的风格:{style}。"
)
# ... 构建 pipeline 省略 ...
# 再将其嵌入 ChatPromptTemplate 中
final_chat_template = ChatPromptTemplate.from_messages([
("system", "{system_content}"), # 这里接收 pipeline 生成的完整字符串
("human", "{question}")
])
# 更优雅的方式:使用 LCEL 链式调用(后续文章会深入)
八、可复用模板库(工程化建议)
在实际项目中,建议将常用模板集中在一个 Python 文件中,例如 prompts.py:
python
# prompts.py
from langchain_core.prompts import ChatPromptTemplate
# 通用聊天模板
CHAT_TEMPLATE = ChatPromptTemplate.from_messages([
("system", "你是{role},请用{style}风格回答"),
("human", "{question}")
])
# RAG 检索增强模板
RAG_TEMPLATE = ChatPromptTemplate.from_messages([
("system", "根据以下上下文回答问题:\n{context}"),
("human", "{question}")
])
# 复杂流水线模板(需结合 PipelinePromptTemplate)
# 此处建议封装成函数,便于参数管理
然后在主程序中导入复用,大大提升代码整洁度。
九、总结与选型建议
| 特性 | 字符串拼接 | 提示词模板 | 组合模板 |
|---|---|---|---|
| 可读性 | ❌ 混乱 | ✅ 清晰 | ✅ 模块化极强 |
| 变量校验 | ❌ 无 | ✅ 自动 | ✅ 自动 |
| 复用性 | ❌ 差 | ✅ 高 | ✅ 极高(子模块复用) |
| 多轮对话 | ❌ 难 | ✅ 占位符支持 | ✅ 可嵌套支持 |
| 调试 | ❌ 困难 | ✅ 可打印结构 | ✅ 可逐层打印 |
| 学习成本 | 低 | 中等 | 较高 |
| 适用场景 | 临时脚本 | 正式 AI 应用 | 超复杂业务系统 |
最终开发建议:
- 临时脚本、快速实验 → 字符串拼接够用。
- 正式项目、单轮简单问答 →
ChatPromptTemplate+from_messages()。 - 多轮对话/RAG/Agent → 引入
MessagesPlaceholder管理历史。 - 大型复杂系统(>5个逻辑模块) → 果断使用
PipelinePromptTemplate进行分层设计。
LangChain 的提示词模板不仅提升了开发体验,更为后续集成链(Chain)、检索增强(RAG)和智能代理(Agent)打下了坚实基础。掌握这些技巧,你就掌握了 LangChain 开发的半壁江山。
延伸阅读:
- LangChain 官方文档:Prompt Templates