还在手撕Prompt?这份模板工程化指南,让你的AI输出稳得一批(三)
——从手工作坊到流水线生产,一套方法论搞定所有提示词场景!

为啥要聊这个
前两篇我们把模型调通了,消息结构也捋顺了。但有个问题一直憋着没说——每次写Prompt都是字符串拼接,代码里一堆f-string和反斜杠,看得人血压飙升。
你肯定遇到过这种情况:
prompt = f"你是一个{role},请根据{product}的以下信息:{data},生成一段{style}风格的文案,字数控制在{words}字以内,注意{rule1}、{rule2}、{rule3}..."
这玩意儿写三次就想砸键盘。变量一多,引号嵌套就乱套,改个需求得在十几个地方同步修改。
Prompt模板就是来解决这个问题的。
今天重点说三件事:
两大模板工具怎么选 —— 别再傻傻分不清
MessagesPlaceholder的妙用 —— 多轮对话的标准姿势
三条铁律+两个案例 —— 直接抄作业
一、两大模板工具,别选错了
LangChain给了两个工具,很多人上来就懵。
PromptTemplate:轻量,但有限
这个生成的是纯字符串,适合单轮、不需要区分角色的任务。
template = PromptTemplate.from_template(
"把下面这段文字翻译成{target_lang}:{text}"
)
prompt = template.format(target_lang="英文", text="今天天气不错")
# 输出:把下面这段文字翻译成英文:今天天气不错
简单直接,但有个致命缺陷——没有角色概念。你只能把"你是一个翻译官"这种指令和用户内容揉在一段话里,模型分不清哪个是系统设定、哪个是用户输入。
ChatPromptTemplate:生产环境唯一选择
这个生成的是结构化消息列表,支持System/Human/AI三种角色。
from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "你是{role},回答要{style}"),
("human", "{question}")
])
prompt = template.invoke({
"role": "资深营养师",
"style": "通俗易懂",
"question": "每天喝多少水合适?"
})
输出的不是字符串,而是[SystemMessage, HumanMessage]列表。这意味着:
模型能准确区分"系统设定"和"用户问题"
不同角色的消息在API调用时自动映射到对应字段
后期接入记忆管理、多轮对话时天然兼容
一句话结论:新项目无脑上ChatPromptTemplate,它能向下兼容纯文本需求,但反过来不行。
二、MessagesPlaceholder:多轮对话的标准化姿势
这是本篇文章最重要的知识点,没有之一。
先看问题:多轮对话时,历史消息怎么塞进模板?
错误示范——手动拼接:
history_str = "\n".join([f"用户:{h}\nAI:{a}" for h, a in history])
template = f"历史对话:{history_str}\n当前问题:{question}"
太丑了,而且丢失了消息类型信息。模型分不清哪句是用户说的、哪句是AI说的。
正确姿势——MessagesPlaceholder:
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
template = ChatPromptTemplate.from_messages([
("system", "你是友好的问答助手"),
MessagesPlaceholder(variable_name="history", optional=True, n_messages=4),
("human", "{question}")
])
看到没?在模板里开了一个插槽,专门用来塞历史消息列表。
调用的时候:
prompt = template.invoke({
"history": [
("human", "我叫张伟"),
("ai", "你好张伟,有什么可以帮你的?"),
("human", "我问过订单"),
("ai", "您的订单正在派送中"),
],
"question": "那什么时候能到?"
})
三个参数说清楚:
| 参数 | 作用 | 使用建议 |
|---|---|---|
variable_name |
指定历史消息的变量名 | 固定写"history"即可 |
optional |
是否允许为空 | 首轮对话设为True,避免报错 |
n_messages |
只取最近N条 | 强烈建议设置,防止上下文过长 |
最佳实践:n_messages建议4-6条。太长会超Token限额,太短则丢失上下文。如果对话超过20轮,建议配合摘要压缩技术。
三、Prompt编写的三条铁律
不讲虚的,直接给原则。
原则一:角色要具体
❌ 你是一个助手 ✅ 你是拥有10年经验的跨境电商运营专家,擅长用数据驱动方式撰写商品详情页
角色越具体,模型的措辞、语气、专业度越稳定。
原则二:任务要拆解
不要在一个Prompt里塞三件事。
❌ 请分析情感、提取实体、生成摘要 ✅ 拆成三个独立的Prompt,分步调用
模型不是多线程,任务越聚焦输出越精准。
原则三:约束要可验证
"写得好一点"这种话等于废话。可验证的约束长这样:
| 模糊约束 ❌ | 可验证约束 ✅ |
|---|---|
| 写短一点 | 不超过200字 |
| 专业一些 | 包含至少3个行业术语 |
| 有趣一点 | 使用2个以上比喻或反问句 |
| 全面一些 | 覆盖5W1H(谁、什么、何时、何地、为何、如何) |
| 温和一些 | 不使用"你错了""不对"等否定词 |
四、案例一:商品文案生成器(多变量注入)
电商场景标配,商品数据从数据库来,文案自动生成。
from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "你是资深电商文案策划,擅长用短文案激发购买欲"),
("human", """
商品名称:{name}
核心卖点:{selling_point}
目标人群:{audience}
价格:{price}
生成三段不同风格文案:
1. 朋友圈风格(带emoji,口语化)
2. 详情页风格(偏理性,带参数)
3. 直播话术风格(有互动感)
""")
])
prompt = template.invoke({
"name": "便携式果汁杯",
"selling_point": "无线充电、40秒榨汁",
"audience": "25-35岁白领",
"price": "299元"
})
关键点:多个变量可以来自不同数据源(商品库、价格系统、用户画像),模板层只负责组装。
五、案例二:客服回复生成器(多数据源聚合)
这个更接近真实业务流。
template = ChatPromptTemplate.from_messages([
("system", "你是专业客服,回复需:①安抚情绪 ②给方案 ③说后续步骤"),
("human", """
用户问题:{user_question}
订单号:{order_id}
订单状态:{order_status}
物流信息:{logistics}
预计解决时间:{eta}
生成200字以内回复,语气温和
""")
])
prompt = template.invoke({
"user_question": "快递三天没更新了",
"order_id": "ORD-20260721-0042",
"order_status": "已发货",
"logistics": "2026-07-20 15:32 从杭州发出",
"eta": "2026-07-24 18:00前"
})
架构分层:
┌─────────────────────────────────────┐
│ 数据源层 │
│ ┌─────────┐ ┌─────────┐ │
│ │订单系统 │ │物流系统 │ │
│ └─────────┘ └─────────┘ │
└──────────────┬──────────────────────┘
▼
┌─────────────────────────────────────┐
│ 聚合层 │
│ 统一格式、筛选关键字段、计算ETA │
└──────────────┬──────────────────────┘
▼
┌─────────────────────────────────────┐
│ Prompt模板层 │
│ 变量注入 → 生成结构化消息列表 │
└──────────────┬──────────────────────┘
▼
┌─────────────────────────────────────┐
│ 模型调用层 │
└─────────────────────────────────────┘
Prompt层只管生成,不关心数据从哪来——这才是工程化的正确姿势。
六、工程落地小结
今天的内容可以浓缩成为:
| 场景 | 工具选择 |
|---|---|
| 单轮、无角色区分 | PromptTemplate够用,但建议升级 |
| 多轮、有系统设定 | 直接上 ChatPromptTemplate |
| 需要塞历史消息 | 用 MessagesPlaceholder 做插槽 |
| 变量来源复杂 | 模板只组装,数据由上游层提供 |
代码规范建议:
# 1. 所有模板统一放在 templates.py 或配置中心
# 2. 模板变量命名用 snake_case
# 3. 必填变量在 invoke 前做校验
# 4. 用 Pydantic 定义输入结构,不要裸传 dict
from pydantic import BaseModel
class ProductPromptInput(BaseModel):
name: str
selling_point: str
audience: str
price: str
# 校验通过后再 invoke
validated = ProductPromptInput(**raw_data)
prompt = template.invoke(validated.model_dump())
提高质量的三个追问:
模板里的系统角色够具体吗? —— 越具体输出越稳
每条约束能客观验证吗? —— 不能验证的约束都是废话
变量都来自上游经过校验的数据吗? —— 脏数据进去,垃圾文案出来
更多推荐



所有评论(0)