难度
进阶
从任务定义、消息分层、few-shot、结构化输出到工具调用与评测回归,建立一套可复用的提示词工程方法。
难度
进阶
阅读时长
约 120 分钟
更新日期
2026/03/24
主题
Prompting / Few-shot / Chain-of-Thought / 结构化输出
读完这篇教程后,你应该能:
如果你已经读过 Chain-of-Thought Prompting,这篇文章会把“提示词能激发什么能力”进一步落到产品和工程实践上。
很多人把提示词工程理解成“临时技巧”,仿佛只要模型足够强,Prompt 就不重要了。现实恰好相反:
所以 Prompt Engineering 真正解决的不是“让模型变聪明”,而是:
把它看成“模型接口设计”,比把它看成“玄学调词”更接近真实工作。
一个好 Prompt 的前提不是文案华丽,而是任务定义足够明确。动手写之前,建议先回答四个问题:
如果这四件事你自己都说不清,那么写出来的提示词通常也会含糊不稳。
例如,“请总结这篇文档”看起来简单,但其实至少要明确:
Prompt 工程的第一步,从来不是写句子,而是建任务规格。
现代 LLM 应用里,常见输入通常会分成几层:
一个很常见的问题是把所有要求都堆进 user message,最后导致:
更好的做法是分层管理:
这会让 Prompt 更容易维护,也更容易做 A/B 测试。
如果把这四层画开,会更容易理解为什么“所有信息都塞进 user message”通常会越写越乱:
<g>
<rect x="84" y="86" width="390" height="60" rx="18" fill="#ede9fe" stroke="#b7a8ea" />
<text x="114" y="122" font-size="20" font-weight="700">System</text>
<text x="212" y="122" font-size="13" fill="#4b5563">角色、原则、拒答边界、输出约束</text>
</g>
<g>
<rect x="84" y="160" width="390" height="60" rx="18" fill="#e8f1ff" stroke="#98b7e1" />
<text x="114" y="196" font-size="20" font-weight="700">User</text>
<text x="212" y="196" font-size="13" fill="#4b5563">当前任务、目标、用户语气与具体需求</text>
</g>
<g>
<rect x="84" y="234" width="390" height="60" rx="18" fill="#eef6e8" stroke="#a8c48e" />
<text x="114" y="270" font-size="20" font-weight="700">Context</text>
<text x="212" y="270" font-size="13" fill="#4b5563">RAG 证据、工具结果、业务状态、历史信息</text>
</g>
<g>
<rect x="84" y="308" width="390" height="60" rx="18" fill="#fff4dc" stroke="#e2c36f" />
<text x="114" y="344" font-size="20" font-weight="700">Examples</text>
<text x="212" y="344" font-size="13" fill="#4b5563">few-shot 示例、风格样板、边界案例</text>
</g>
<g>
<rect x="612" y="122" width="250" height="172" rx="24" fill="#f9fafb" stroke="#d7e2f1" />
<text x="737" y="160" text-anchor="middle" font-size="21" font-weight="700">模型输出</text>
<text x="737" y="192" text-anchor="middle" font-size="14" fill="#4b5563">回答内容</text>
<text x="737" y="214" text-anchor="middle" font-size="14" fill="#4b5563">结构化格式</text>
<text x="737" y="236" text-anchor="middle" font-size="14" fill="#4b5563">是否调工具</text>
<text x="737" y="258" text-anchor="middle" font-size="14" fill="#4b5563">是否追问 / 拒答</text>
</g>
<line x1="474" y1="116" x2="612" y2="158" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-stack-arrow)" />
<line x1="474" y1="190" x2="612" y2="190" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-stack-arrow)" />
<line x1="474" y1="264" x2="612" y2="224" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-stack-arrow)" />
<line x1="474" y1="338" x2="612" y2="258" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-stack-arrow)" />
</g>
一个稳健的起点通常只需要包含四类信息:
例如:
你是一个中文技术助手。
任务:解释给定术语。
约束:
1. 只输出与问题直接相关的内容
2. 不确定时明确说明
3. 不编造论文或实验结果
输出格式:
- 定义
- 工作原理
- 应用场景
- 常见误区
这个版本看起来不复杂,但已经足够清晰。很多时候,模型失败并不是因为少了某个“高级技巧”,而是任务描述本身模糊。
few-shot 最大的价值,不在于给模型更多文本,而在于告诉它:
一个典型 few-shot 模板会包含:
few-shot 设计时要特别避免两种情况:
few-shot 更像是“任务说明书中的示例页”,而不是“往上下文里堆越多越好”。
Chain-of-Thought Prompting 让很多人第一次意识到,提示词可以影响模型的推理展开方式。但在产品实践里,CoT 不是无脑通用增强器。
它更适合:
它不一定适合:
一个更实用的思路是:
提示词工程真正重要的是“任务匹配”,不是把所有流行技巧叠满。
如果你的模型输出最终还要进数据库、前端组件、工作流引擎或工具调用链,那么“能不能稳定结构化输出”通常比“语言多优雅”更重要。
结构化输出 prompt 至少要明确:
一个简单例子:
请根据用户问题生成 JSON,不要输出额外解释。
Schema:
{
"task_type": "faq | retrieval | escalation",
"priority": "low | medium | high",
"requires_human": true | false,
"reason": "string"
}
如果你没有把 schema 说清楚,模型往往会“自作聪明”地多加说明文字,导致后续解析失败。
可以把字段说明和边界进一步写细一点:
{
"system": "你是客服分流助手。请严格输出 JSON。",
"user": "我的订单已经扣款但后台还是显示未支付,怎么办?",
"instructions": [
"只能输出合法 JSON",
"task_type 只能是 faq、retrieval、escalation 之一",
"当涉及人工核实或资金风险时,requires_human 必须为 true",
"reason 用一句中文说明判断依据"
]
}
这类 Prompt 的重点不是“更长”,而是“可执行性更强”。当你希望模型接入后续工作流时,清晰约束远比风格化措辞重要。
进入 RAG 场景后,Prompt 的重点会发生变化。此时你最该强调的是:
这也是为什么 RAG Prompt 和普通聊天 Prompt 很不一样。普通聊天更强调自然性,RAG 更强调 groundedness,也就是“答案是否真正扎根于证据”。
如果你还没有把 RAG 链路本身搭起来,可以先看 RAG 系统搭建实战。
当模型要接数据库、搜索、代码执行或企业系统 API 时,Prompt 又会多一个重点:工具使用规则。
一个好的工具调用提示至少要明确:
例如,不要只写“你可以使用搜索工具”,而要写得更明确:
Prompt Engineering 在这一步已经非常接近“工作流设计”,而不只是文本写作。
Lost in the Middle 给应用开发很重要的一个提醒是:
不是把更多材料塞进上下文,模型就会更会用。
因此在设计长 Prompt 时,建议特别注意:
Prompt 长度管理,本质上也是上下文设计问题。
Prompt 调整最常见的陷阱,是每次改完只问一句“感觉是不是更好了”。这会让团队永远陷在主观争论里。
更稳的方法是建立小型评测集,并给每类问题打标签,例如:
然后观察改 Prompt 后哪些维度变好了、哪些维度变差了。这样你才知道:
想把评测方法再往前推进,可以继续看 LLM 评测方法论。
你可以把提示词优化做成一个非常清晰的闭环:
比如:
这种副作用如果不通过回归集观察,很容易在上线后才暴露出来。
把这件事做成显式闭环,Prompt 迭代才不会退化成“拍脑袋换措辞”:
<g>
<rect x="34" y="114" width="160" height="74" rx="18" fill="#e8f1ff" stroke="#98b7e1" />
<text x="114" y="145" text-anchor="middle" font-size="19" font-weight="700">失败样本</text>
<text x="114" y="168" text-anchor="middle" font-size="13" fill="#4b5563">线上 bad case / 人工标注</text>
</g>
<g>
<rect x="224" y="114" width="160" height="74" rx="18" fill="#eef6e8" stroke="#a8c48e" />
<text x="304" y="145" text-anchor="middle" font-size="19" font-weight="700">失败分类</text>
<text x="304" y="168" text-anchor="middle" font-size="13" fill="#4b5563">格式错 / 幻觉 / 边界错</text>
</g>
<g>
<rect x="414" y="114" width="160" height="74" rx="18" fill="#fff4dc" stroke="#e2c36f" />
<text x="494" y="145" text-anchor="middle" font-size="19" font-weight="700">Prompt 改动</text>
<text x="494" y="168" text-anchor="middle" font-size="13" fill="#4b5563">只修一类问题,避免混改</text>
</g>
<g>
<rect x="604" y="114" width="160" height="74" rx="18" fill="#fce7ef" stroke="#e2a8bd" />
<text x="684" y="145" text-anchor="middle" font-size="19" font-weight="700">固定评测集</text>
<text x="684" y="168" text-anchor="middle" font-size="13" fill="#4b5563">标准样本 + 风险样本</text>
</g>
<g>
<rect x="794" y="114" width="152" height="74" rx="18" fill="#dff4f0" stroke="#8dc7bd" />
<text x="870" y="145" text-anchor="middle" font-size="19" font-weight="700">上线判断</text>
<text x="870" y="168" text-anchor="middle" font-size="13" fill="#4b5563">收益是否大于副作用</text>
</g>
<line x1="194" y1="151" x2="224" y2="151" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-loop-arrow)" />
<line x1="384" y1="151" x2="414" y2="151" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-loop-arrow)" />
<line x1="574" y1="151" x2="604" y2="151" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-loop-arrow)" />
<line x1="764" y1="151" x2="794" y2="151" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-loop-arrow)" />
<path d="M 870 188 C 870 264, 174 264, 114 188" fill="none" stroke="#5b6b7f" stroke-width="3" marker-end="url(#prompt-loop-arrow)" />
<text x="492" y="298" text-anchor="middle" font-size="15" fill="#4b5563">如果出现副作用,就回到失败分类,而不是继续“叠规则碰运气”</text>
</g>
真正有效的提示词工程依赖任务定义、失败样本和评测闭环,而不是随机换几句话碰运气。
规则过多、互相冲突时,模型反而更难稳定遵守。
few-shot 不是越多越好,关键是能不能代表真实任务中的难点。
有些错误来自模型本身不会做,有些错误只是 prompt 没说明白。两者优化路径完全不同。
从相近主题继续深入,建立连续学习链路。