【Langchain】 ChatPromptTemplate:从“手动拼字符串“到“专业模板“的进化之路
零基础看懂 ChatPromptTemplate:从"手动拼字符串"到"专业模板"的进化之路
一句话总结:ChatPromptTemplate 是 LangChain 中专门用来组装聊天消息的"模板引擎",它能让你像填空题一样,把变量插进预设的对话结构里,告别手动拼接字符串的混乱时代。
一、为什么需要它?先看一个"翻车现场"
想象你要让 AI 扮演一个"暴躁的代码审查员",审查一段代码。最原始的做法是手动拼字符串:
# ❌ 原始人写法:手动拼接,极易出错
language = "Python"
code = "def add(a, b): return a + b"
prompt = f"""
你是一个暴躁的代码审查员。
请用{language}的视角审查以下代码:
{code}
要求:1. 指出问题 2. 骂得狠一点
"""
问题在哪?
- 角色、指令、代码全混在一起,改一处可能牵一发而动全身
- 如果对话有"历史记录",拼接起来更是灾难
- 无法复用,每次都要重新写一遍格式
二、ChatPromptTemplate 是什么?
它是 LangChain 提供的"聊天消息组装器",核心思想:
把对话拆成"角色 + 内容"的消息块,留出变量占位符,最后统一填充。
from langchain.prompts import ChatPromptTemplate
# ✅ 现代写法:结构清晰,像填空题
template = ChatPromptTemplate.from_messages([
("system", "你是一个{personality}的代码审查员。"), # 系统消息:设定角色
("human", "请用{language}的视角审查这段代码:\n{code}"), # 人类消息:用户输入
])
对比一目了然:
维度 手动拼接字符串 ChatPromptTemplate
结构 一锅粥 分角色、分消息块
复用 复制粘贴 定义一次,多次填充
维护 改一处崩全局 改模板不影响调用逻辑
扩展 地狱难度 轻松加消息、加变量
三、核心概念:三种"消息角色"
聊天模型(如 GPT-4、Claude)只认三种身份牌:
system—— “幕后导演”
设定 AI 的身份、语气、规则。用户看不到,但 AI 全程照做。
from langchain.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "你是一位{style}的厨师,只做{ cuisine }菜。"),
("human", "我想学做{ dish }"),
])
# 填充变量,生成最终消息列表
messages = template.invoke({
"style": "严厉",
"cuisine": "川菜",
"dish": "麻婆豆腐"
})
print(messages)
# 输出:
# [
# SystemMessage(content='你是一位严厉的厨师,只做川菜。'),
# HumanMessage(content='我想学做麻婆豆腐')
# ]
human/user—— “提问的顾客”
就是用户的输入。可以带变量,也可以纯文本。
template = ChatPromptTemplate.from_messages([
("system", "你是翻译官,把用户的话翻译成{target_lang}。"),
("human", "{user_input}"), # 变量占位
])
result = template.invoke({
"target_lang": "日语",
"user_input": "今天天气真好"
})
# HumanMessage(content='今天天气真好')
ai/assistant—— “假装 AI 已经说过的话”
这是精髓! 用来构造"少样本示例(Few-shot)",让 AI 模仿特定格式。
template = ChatPromptTemplate.from_messages([
("system", "你是一个情感分析器,只输出'正面'或'负面'。"),
# 👇 假装这是上一轮对话:用户问,AI 答
("human", "这部电影太棒了!"),
("ai", "正面"),
("human", "浪费了我两个小时。"),
("ai", "负面"),
# 👇 真正的用户输入
("human", "{text}"),
])
result = template.invoke({"text": "主角演技炸裂"})
# 模型看到历史示例后,大概率输出:正面
四、四种创建方式(从简到繁)
方式 1:元组列表(最常用,推荐⭐)
from langchain.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "你是{role},擅长{skill}。"),
("human", "请帮我{task}"),
])
messages = template.invoke({
"role": "前端专家",
"skill": "React",
"task": "优化一个渲染卡顿的组件"
})
方式 2:Message 对象(更灵活,可加额外参数)
from langchain.prompts import ChatPromptTemplate
from langchain.prompts.chat import SystemMessagePromptTemplate, HumanMessagePromptTemplate
system_template = SystemMessagePromptTemplate.from_template(
"你是{role},语气要{tone}。"
)
human_template = HumanMessagePromptTemplate.from_template("帮我{task}")
chat_prompt = ChatPromptTemplate.from_messages([
system_template,
human_template
])
方式 3:字符串直接转(适合单轮简单场景)
from langchain.prompts import ChatPromptTemplate
# 只有一个消息时,默认当作 human 消息
template = ChatPromptTemplate.from_template("把这句话翻译成{lang}:{text}")
messages = template.invoke({"lang": "英语", "text": "你好世界"})
方式 4:混合使用(历史记录 + 新输入)
from langchain_core.messages import AIMessage, HumanMessage
# 从真实的聊天历史构造
history = [
HumanMessage(content="你好"),
AIMessage(content="你好!有什么可以帮你的吗?"),
]
template = ChatPromptTemplate.from_messages([
("system", "你是客服机器人。"),
*history, # 展开历史记录
("human", "{new_question}"), # 最新问题
])
messages = template.invoke({"new_question": "怎么退款?"})
五、实战案例:从简单到复杂
案例 1:带格式的代码审查(基础用法)
from langchain.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", """你是一位{style}的{language}代码审查专家。
审查规则:
1. 检查是否有空指针风险
2. 检查是否有性能隐患
3. 用{style}的语气给出建议"""),
("human", """请审查以下代码:
```{language}
{code}
```"""),
])
# 使用
messages = template.invoke({
"style": "温和但犀利",
"language": "Java",
"code": """
public String getName(User user) {
return user.getProfile().getName();
}
"""
})
# 可以直接传给模型
# response = model.invoke(messages)
案例 2:动态 Few-shot 学习(让 AI 模仿格式)
假设你要让 AI 从非结构化文本中提取"人物-关系"对,并且严格按 JSON 格式输出:
template = ChatPromptTemplate.from_messages([
("system", "你是一个信息抽取助手。从文本中提取人物关系,只输出 JSON 数组,不要解释。"),
# 示例 1
("human", "文本:小明是小红的哥哥,他们一起去了公园。"),
("ai", '[{"person1": "小明", "relation": "哥哥", "person2": "小红"}]'),
# 示例 2
("human", "文本:张三是李四的老板,王五是他们公司的客户。"),
("ai", '[{"person1": "张三", "relation": "老板", "person2": "李四"}, {"person1": "王五", "relation": "客户", "person2": "公司"}]'),
# 真实输入
("human", "文本:{input_text}"),
])
result = template.invoke({
"input_text": "曹操是刘备的敌人,关羽是刘备的结拜兄弟。"
})
# AI 会模仿前面的 JSON 格式输出,而不是胡说八道
案例 3:多轮对话记忆(结合历史记录)
from langchain_core.messages import AIMessage, HumanMessage
# 假设这是从数据库取出的历史记录
chat_history = [
HumanMessage(content="我想订一张去上海的机票"),
AIMessage(content="好的,请问您想哪天出发?"),
HumanMessage(content="明天"),
AIMessage(content="明天上午 9 点有一班,可以吗?"),
]
template = ChatPromptTemplate.from_messages([
("system", "你是航空公司客服,叫小助手。"),
*chat_history,
("human", "{input}"),
])
messages = template.invoke({"input": "可以,帮我订了吧"})
# 模型能根据上下文理解"可以"指的是"上午 9 点那班"
案例 4:条件分支(根据变量动态改变提示词)
template = ChatPromptTemplate.from_messages([
("system", "你是{level}教程作者,用{level}的语言解释概念。"),
("human", "请解释什么是{concept}"),
])
# 同一套模板,不同变量 = 完全不同的风格
beginner = template.invoke({
"level": "零基础",
"concept": "递归"
})
# 系统消息:你是零基础教程作者,用零基础的语言解释概念。
expert = template.invoke({
"level": "资深工程师",
"concept": "递归"
})
# 系统消息:你是资深工程师教程作者,用资深工程师的语言解释概念。
六、高级技巧
技巧 1:部分填充(Partial Variables)
有些变量是固定的,不想每次调用都传:
template = ChatPromptTemplate.from_messages([
("system", "你是{role},使用{language}回答问题。"),
("human", "{question}"),
])
# 先绑定固定变量
partial_template = template.partial(role="Python 专家", language="中文")
# 之后只需要传变化的 question
messages = partial_template.invoke({"question": "怎么写装饰器?"})
技巧 2:管道组合(Pipeline)
可以把模板和模型串成流水线:
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4")
# 模板 + 模型 = 一个完整的链
chain = template | model
# 直接传入变量,自动经过模板格式化再传给模型
response = chain.invoke({
"role": "诗人",
"language": "中文",
"question": "写一首关于春天的诗"
})
技巧 3:MessagePlaceholder(插入任意消息列表)
当你不知道历史记录有多少条时:
from langchain.prompts import MessagesPlaceholder
template = ChatPromptTemplate.from_messages([
("system", "你是客服机器人。"),
MessagesPlaceholder(variable_name="history"), # 这里会插入任意数量的消息
("human", "{input}"),
])
# history 可以传一个消息列表
messages = template.invoke({
"history": [
HumanMessage(content="你好"),
AIMessage(content="您好!有什么可以帮您?"),
HumanMessage(content="查一下订单"),
],
"input": "订单号 12345"
})
七、常见坑与解决方案
坑 现象 解决
变量名写错 KeyError: 'langauge' 仔细检查占位符 {} 里的名字
忘记 invoke 得到的是模板对象,不是消息 必须调用 .invoke(variables)
单双引号混乱 JSON 里的引号和 f-string 冲突 用 from_messages 的元组写法,避免手动转义
历史记录顺序错 AI 回答错乱 确保是 human/ai/human/ai 交替
变量是列表/对象 直接插进去变成 <object> 先用 json.dumps() 转成字符串
八、总结:一张图看懂流程
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ 定义模板 │ → │ 填充变量 │ → │ 发给模型 │
│ │ │ │ │ │
│ system: 你是{role}│ │ role="医生" │ │ SystemMessage│
│ human: 帮我{task} │ │ task="看报告" │ │ HumanMessage │
│ │ │ │ │ │
│ ChatPromptTemplate│ │ .invoke() │ │ model.invoke()│
└─────────────────┘ └──────────────────┘ └─────────────┘
核心口诀:
角色分清楚,变量留占位,调用 invoke 填,直接丢给模型算。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)