LangChain提示词模板进化史-通用模板-FewShot-ChatPromptTemplate
LangChain 提示词模板进化史:从字符串拼接到优雅的模板引擎
模型调用学会了,接下来要解决一个更头疼的问题——提示词管理。你有没有经历过:把一大段提示词硬编码在字符串里,改一个词就要翻遍整个代码库,稍不留神就拼错变量名。LangChain 的提示词模板系统,就是来终结这种痛苦的。它把提示词从"手写字符串"升级为"可复用的模板引擎",让提示词管理变得优雅、安全、可维护。
一、提示词为什么需要"模板化"?
先看看没有模板的日子长什么样:
# 原始写法:字符串手拼,灾难现场
role = "Python 高级工程师"
skill = "代码审查"
task = "找出下面代码的性能瓶颈"
prompt = f"你是一位{role},擅长{skill}。请帮我{task}。"
看着还行?等项目大了你就知道痛:
| 痛点 | 说明 |
|---|---|
| 散落各处 | 提示词逻辑和业务代码混在一起,改一个词要翻几十个文件 |
| 变量不安全 | 用户输入里有大括号 {} 直接炸,还得手动转义 |
| 无法复用 | 同一个提示词结构,换个场景就得复制粘贴再改 |
| 难以迭代 | 想对比两版提示词的效果?只能改代码、跑一遍、再改回来 |
| 多轮对话噩梦 | system / human / ai 角色混在一起,字符串拼接像在拆炸弹 |
模板化的本质,是把"提示词的骨架"和"填入的数据"彻底分开。 骨架是模板,数据是变量,两者各管各的,互不干扰。
二、PromptTemplate:一切模板的起点
PromptTemplate 是 LangChain 里最基础的模板类。它用 {变量名} 做占位符,支持动态填充:
2.1 基础用法
from langchain.prompts import PromptTemplate
# 定义模板骨架
template = PromptTemplate.from_template(
"你是一位{role},请用{style}的风格回答以下问题:{question}"
)
# 填入数据,生成最终提示词
prompt = template.format(
role="资深律师",
style="严谨专业",
question="签订劳动合同需要注意哪些法律风险?"
)
print(prompt)
# 你是一位资深律师,请用严谨专业的风格回答以下问题:签订劳动合同需要注意哪些法律风险?
2.2 两种创建方式
from langchain.prompts import PromptTemplate
# 方式一:from_template(推荐,日常够用)
template = PromptTemplate.from_template("请用{language}写一段{task}的代码")
# 方式二:显式声明 input_variables(更严谨,支持参数校验)
template = PromptTemplate(
template="请用{language}写一段{task}的代码",
input_variables=["language", "task"] # 明确声明需要哪些变量
)
日常用
from_template就够了。 但如果你的模板变量很多,显式声明input_variables可以在传错变量时立刻报错,而不是默默生成奇怪的提示词。
2.3 部分填充:先填一半,剩下的后面再说
有些变量是全局固定的(比如公司名、产品名),每次都传一遍太啰嗦。partial 允许你先填一部分,生成一个"半成品模板":
from langchain.prompts import PromptTemplate
template = PromptTemplate.from_template(
"你是{company}的{role},请回答:{question}"
)
# 先把 company 固定为"阿里巴巴"
partial_template = template.partial(company="阿里巴巴")
# 后续只需要填剩下的变量
print(partial_template.format(role="客服专员", question="如何退货?"))
# 你是阿里巴巴的客服专员,请回答:如何退货?
实战技巧: 把 system prompt 里的公司名、术语规范、输出格式要求等固定内容用
partial预填充,后续调用只需传核心变量,代码清爽很多。
三、ChatPromptTemplate:多轮对话的"剧本编辑器"
聊到多轮对话,PromptTemplate 就扛不住了——它只能处理一段纯文本,而聊天场景需要区分 system / human / ai 三种角色。ChatPromptTemplate 就是为这个而生的。
3.1 基础用法:给每个角色写"台词模板"
from langchain.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "你是一位{role},擅长{skill}。请用中文回答。"),
("human", "请帮我{task}"),
])
# 填入变量
messages = template.format_messages(
role="数据分析师",
skill="SQL 优化",
task="分析这条 SQL 为什么慢:SELECT * FROM orders WHERE year=2024"
)
print(messages)
# [
# SystemMessage(content="你是一位数据分析师,擅长SQL 优化。请用中文回答。"),
# HumanMessage(content="请帮我分析这条 SQL 为什么慢:SELECT * FROM orders WHERE year=2024")
# ]
3.2 四种消息构造方式
ChatPromptTemplate 支持多种写法,你可以选最顺手的一种:
from langchain.prompts import ChatPromptTemplate
from langchain.schema import SystemMessage, HumanMessage
# 方式一:元组简写(推荐,最简洁)
template = ChatPromptTemplate.from_messages([
("system", "你是{role}"),
("human", "{question}"),
])
# 方式二:Message 对象(字段名更直观)
template = ChatPromptTemplate.from_messages([
SystemMessage(content="你是{role}"),
HumanMessage(content="{question}"),
])
# 方式三:带 role 的字典(JS 开发者可能更习惯)
template = ChatPromptTemplate.from_messages([
{"role": "system", "content": "你是{role}"},
{"role": "user", "content": "{question}"},
])
3.3 MessagesPlaceholder:给历史消息留个"插槽"
多轮对话的核心需求:把之前的对话历史塞进模板。MessagesPlaceholder 就是干这个的——它在模板里留一个"插槽",专门用来插入一组消息:
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
template = ChatPromptTemplate.from_messages([
("system", "你是一个有用的助手。"),
MessagesPlaceholder(variable_name="history"), # ← 历史消息插槽
("human", "{question}"),
])
# 模拟历史对话
history = [
("human", "我叫张三"),
("ai", "你好张三!有什么可以帮你的?"),
("human", "我今年 30 岁"),
("ai", "了解,30 岁正是好年纪!"),
]
# 填入历史 + 新问题
messages = template.format_messages(
history=history,
question="我叫什么名字?今年多大?"
)
print(messages)
# 模型会看到完整的对话历史,因此能正确回答"你叫张三,今年30岁"
这就是 Memory 组件的底层。
MessagesPlaceholder是"插槽",Memory 组件负责"往插槽里填历史消息"。后面讲 Memory 时你会发现它本质上就是一个自动管理插槽内容的工具。
四、FewShotPromptTemplate:教模型"照猫画虎"
有时候光靠 system prompt 说不清楚,给几个例子效果立竿见影。FewShotPromptTemplate 让你把示例也做成模板的一部分:
4.1 基础用法
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
# 准备示例
examples = [
{"input": "今天天气真好", "output": "正面"},
{"input": "服务太差了,气死我了", "output": "负面"},
{"input": "还行吧,不好不坏", "output": "中性"},
]
# 定义示例的格式模板
example_template = PromptTemplate.from_template(
"输入:{input}\n输出:{output}"
)
# 组装 FewShot 模板
fewshot_template = FewShotPromptTemplate(
examples=examples,
example_prompt=example_template,
prefix="请对以下文本做情感分析,输出'正面'、'负面'或'中性'。\n\n示例:",
suffix="\n输入:{text}\n输出:",
input_variables=["text"],
)
print(fewshot_template.format(text="这个产品质量不错,推荐购买"))
输出效果:
请对以下文本做情感分析,输出'正面'、'负面'或'中性'。
示例:
输入:今天天气真好
输出:正面
输入:服务太差了,气死我了
输出:负面
输入:还行吧,不好不坏
输出:中性
输入:这个产品质量不错,推荐购买
输出:
4.2 动态选择示例:不要让示例"自说自话"
固定示例的问题是:不管用户问什么,都给同样的示例。更好的做法是根据用户输入,动态选择最相关的示例:
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# 准备示例库
examples = [
{"input": "这段代码怎么优化性能", "output": "你可以考虑:1. 减少循环嵌套 2. 使用缓存..."},
{"input": "这个 SQL 查询太慢了", "output": "建议:1. 添加索引 2. 避免 SELECT * 3. 使用 EXPLAIN 分析..."},
{"input": "如何部署到服务器", "output": "步骤:1. 打包应用 2. 配置环境变量 3. 使用 Docker..."},
{"input": "Python 里的装饰器怎么用", "output": "装饰器是一种高阶函数,基本语法是..."},
]
# 用向量相似度动态选择最相关的示例
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples=examples,
embeddings=OpenAIEmbeddings(),
vectorstore_cls=Chroma,
k=2, # 选最相关的 2 个示例
)
fewshot_template = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=PromptTemplate.from_template("问题:{input}\n解答:{output}"),
prefix="你是一个编程助手。以下是一些参考示例:",
suffix="问题:{question}\n解答:",
input_variables=["question"],
)
# 用户问数据库相关,自动匹配到 SQL 示例
print(fewshot_template.format(question="数据库查询优化怎么做?"))
动态示例选择的本质:把示例库向量化,用户问题来了之后,用语义相似度检索最相关的几个示例。这比固定示例效果好得多,也是 RAG(检索增强生成)思想的雏形。
五、模板 + 模型 + 管道:一条龙服务
模板本身只是"造提示词"的工具,真正让它发光的是和模型串成链。用 LCEL 的管道符 |,一行代码把模板、模型、输出解析器串起来:
from langchain.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# 模板
template = ChatPromptTemplate.from_messages([
("system", "你是一个{role},擅长{skill}。"),
("human", "{question}"),
])
# 模型
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
# 串成链
chain = template | llm | StrOutputParser()
# 一行调用
result = chain.invoke({
"role": "脱口秀演员",
"skill": "程序员段子",
"question": "讲一个关于 Git 的段子",
})
print(result)
管道符的两种调用方式
# 方式一:invoke(一次性调用)
result = chain.invoke({"role": "翻译官", "skill": "中英互译", "question": "你好世界"})
# 方式二:stream(流式输出)
for chunk in chain.stream({"role": "诗人", "skill": "写现代诗", "question": "写一首关于代码的诗"}):
print(chunk, end="", flush=True)
模板 + 管道符 = 可复用的提示词流水线。 同一个模板,换不同的变量就能产出不同的提示词,换不同的模型就能得到不同风格的回复——模板和模型各自独立,互不耦合。
六、实战:三个模板,三种场景
6.1 场景一:代码审查助手
from langchain.prompts import ChatPromptTemplate
review_template = ChatPromptTemplate.from_messages([
("system", """你是一位资深代码审查员,审查标准如下:
- 命名是否规范
- 是否有潜在的性能问题
- 是否有安全隐患
- 是否有更好的写法
请逐条给出审查意见。"""),
("human", "请审查以下{language}代码:\n```{language}\n{code}\n```"),
])
messages = review_template.format_messages(
language="python",
code="""
def f(x):
return x*2
"""
)
6.2 场景二:多语言翻译器(带 FewShot)
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
examples = [
{"source": "今天天气真好", "target": "The weather is really nice today"},
{"source": "我昨天加班到凌晨", "target": "I worked overtime until midnight yesterday"},
]
example_template = PromptTemplate.from_template("中文:{source}\n英文:{target}")
translate_template = FewShotPromptTemplate(
examples=examples,
example_prompt=example_template,
prefix="请将以下中文翻译成地道的美式英语:",
suffix="中文:{text}\n英文:",
input_variables=["text"],
)
6.3 场景三:带历史记忆的客服机器人
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
service_template = ChatPromptTemplate.from_messages([
("system", """你是{company}的客服专员。
回复要求:
- 语气亲切、专业
- 不能承诺任何你无法确定的事情
- 遇到投诉先道歉再解决"""),
MessagesPlaceholder(variable_name="history"),
("human", "{question}"),
])
# 模拟历史
history = [
("human", "我的订单什么时候到?"),
("ai", "您的订单 #12345 预计明天下午送达,请注意查收短信通知。"),
]
messages = service_template.format_messages(
company="京东",
history=history,
question="能帮我催一下吗?"
)
七、总结
| 模板类型 | 适用场景 | 核心能力 |
|---|---|---|
PromptTemplate |
纯文本提示词 | 变量填充、部分填充 |
ChatPromptTemplate |
多轮对话 | 角色分离、MessagesPlaceholder 插槽 |
FewShotPromptTemplate |
需要示例引导 | 固定示例 + 动态示例选择 |
MessagesPlaceholder |
插入历史消息 | 为 Memory 组件预留插槽 |
提示词模板的本质,是把"写提示词"从体力活变成工程活。 模板让你可以像管理代码一样管理提示词——版本控制、A/B 测试、组件复用——这些在字符串手拼时代想都不敢想的事情,现在都是标配。
参考资料
更多推荐


所有评论(0)