提示词工程进阶 (Prompting for Agents)
做普通聊天机器人时,Prompt 写得“顺口”一点通常就够了。但一旦进入 Agent 场景,Prompt 就不只是文案问题了,它更像一份运行说明书。你不是在教模型怎么聊天,而是在约束它怎么判断、怎么调用工具、怎么输出结果。
很多 Agent 不稳定,最后排查下来并不是模型太弱,而是 Prompt 太松。该明确的没明确,该禁止的没禁止,该结构化的还停留在自然语言层面。
[PROMPT_LAB_BANNER]
1. Agent Prompt 和普通 Chat Prompt 不是一回事
普通 Chat Prompt 更关心回答质量,比如解释清不清楚、语气好不好、内容够不够完整。
Agent Prompt 关心的重点不一样,它更在乎:
- 会不会选对工具
- 会不会漏步骤
- 输出能不能被程序稳定解析
- 遇到边界情况时会不会乱猜
所以两者看起来都叫 Prompt,实际上承担的职责差很多。
| 维度 | Chat Prompt | Agent Prompt |
|---|---|---|
| 目标 | 生成好答案 | 驱动正确动作和流程 |
| 容错空间 | 较高 | 较低 |
| 输出要求 | 人能看懂就行 | 人和程序都要能消费 |
| 失败后果 | 回答不够好 | 调错工具、走错流程、污染状态 |
如果是做 Agent,建议你从一开始就把 Prompt 当成“系统接口”的一部分,而不是把它当成补充说明。
2. 先解决一个核心问题:模型到底什么时候该动手
很多 Agent 的不稳定,根源都在这里。
如果 Prompt 太松,模型可能会:
- 还没想清楚就直接调工具
- 明明缺信息,却先编一个答案
- 工具结果出来后,不会根据 observation 调整动作
- 本来该停下确认,却继续擅自往前做
这也是 ReAct 这类模式一直有生命力的原因。它不是因为名字高级,而是因为它强迫模型把“想”和“做”分开。
ReAct 的价值,不是格式,而是节奏
ReAct 里最有价值的部分,不是 Thought、Action 这些单词本身,而是它把执行过程拆成了:
- 先判断当前信息够不够
- 不够就调用工具
- 拿到 observation 再决定下一步
- 最后再给结论
一个简化后的模板可以是这样:
# Role
你是一个能够使用工具的 AI 助手。
# Tools
- google_search: 搜索互联网信息
- calculator: 进行数学计算
# Rules
- 在信息不足时,不要直接回答
- 每次调用工具前,先说明目的
- 工具结果和原始假设冲突时,以工具结果为准
# Output Format
Question:
Thought:
Action:
Action Input:
Observation:
Final Answer:
这个结构最适合那种“需要边查边做”的任务,比如检索、计算、排错、自动化流程执行。
不过也别机械套模板。不是每个 Agent 都需要把所有 Thought 明文打出来。有些场景只需要内部推理,然后对外暴露结构化动作就够了。
3. 结构化输出不是加分项,是稳定性的底线
如果你的系统后面要接程序逻辑、数据库写入、前端渲染、任务编排,那就不要让模型随意输出“好的,结果如下”这种自由文本。
最稳的思路永远是:
- 输入分区明确
- 输出格式固定
- 程序只信结构,不信语气
XML / 标签分区
把不同类型的信息分开,是很实用的一步。特别是当你要把用户输入、外部文档、系统指令同时塞给模型时,如果不做分区,很容易互相污染。
例如:
<documents>
<doc id="1">...</doc>
<doc id="2">...</doc>
</documents>
<instructions>
只根据 <documents> 中的内容回答。
</instructions>
这种写法的好处,不是“看起来高级”,而是边界更清楚。后面排查 Prompt Injection 或上下文串味时,也更容易定位问题。
JSON 输出
如果下游程序需要解析结果,尽量让模型直接输出 JSON,而不是让后端再从一堆自然语言里抠字段。
例如:
interface Response {
reasoning: string;
plan: string[];
command?: string;
}
然后在 Prompt 里明确要求:
- 必须返回合法 JSON
- 不要输出额外解释
- 字段缺失时返回
null或空数组,而不是省略
这些细节听起来琐碎,但在实际系统里非常重要。很多“模型偶发出错”,本质上就是协议没写死。
4. Persona 真正的作用,是划清边界
很多人写 Persona 时,喜欢写“你是一个专业、友好、知识渊博的 AI 助手”。这类描述几乎没有工程价值。
对 Agent 来说,一个好的 Persona 更像权限声明,而不是文学设定。
坏例子:
你是一个有帮助的助手。
好一点的例子:
你是一个 Python 安全审查 Agent。你只负责识别安全风险,不负责重构业务逻辑。如果发现 SQL 注入或命令注入风险,必须标记为高危;如果证据不足,明确写出不确定性,不允许臆测。
这种 Persona 的关键在于它回答了几个工程问题:
- 你负责什么
- 你不负责什么
- 什么情况下必须保守
- 输出应该长什么样
如果这些边界不写清楚,模型会默认自己什么都能做,最后就很容易越权。
5. Prompt 最难的部分,其实是维护
真正上线以后,你会发现 Prompt 工程最痛苦的阶段不是“写出来”,而是“改不坏”。
Agent Prompt 往往会越来越长,因为你会不断往里补:
- 新规则
- 新边界情况
- 新 few-shot 示例
- 新工具约束
写到后面,如果没有结构,很快就会变成一坨没人敢动的长字符串。
所以我更建议这样维护:
模块化
把 Prompt 拆成几个稳定模块,比如:
rolecontexttoolsrulesexamplesoutput_format
这样改动时,你知道自己改的是哪一层,而不是每次都重写整段系统提示词。
版本化
Prompt 应该像代码一样进版本控制。改完以后,最好记录:
- 改了什么规则
- 为什么改
- 想修复什么失败案例
否则过几周以后,你很难知道某一条限制到底是不是还能删。
用失败样本反推 Prompt
这是很实用的一条。不要凭感觉加规则,优先看真实失败案例。
例如:
- 模型老是调用错工具
- 明明检索不到资料,却硬答
- 输出 JSON 时夹带解释文字
这时候应该针对失败模式补规则,而不是泛泛地再加一句“请更严谨一点”。后者通常没什么用。
6. 一些在实战里很管用的小原则
下面这些不是理论名词,但在 Agent 项目里经常救命。
把禁止项写明
不要只告诉模型“该做什么”,还要告诉它“绝对不能做什么”。
比如:
- 不要在证据不足时猜测
- 不要伪造命令执行结果
- 不要在未获得确认时执行破坏性操作
禁止项越清楚,系统越稳。
给模型判停条件
有些 Agent 会无限尝试,不停调工具,直到把 token 用完。你要明确告诉它:
- 什么情况下应该继续
- 什么情况下应该向用户提问
- 什么情况下应该停止并报告失败
这比一味强调“尽力完成任务”更有用。
Few-shot 要贴近真实错误
示例当然重要,但别只放那种过于完美的 happy path。真正有价值的示例,通常是:
- 信息不足时如何拒答
- 工具失败时如何回退
- 结果冲突时如何处理
这些更接近真实线上情况。
小结
Agent Prompt 的核心不是“写得漂亮”,而是让模型在复杂流程里不乱来。
如果只记几个重点,我会选这几条:
- 先把思考、动作、结果分开。
- 让输入输出都尽量结构化。
- 把角色边界和禁止项写死。
- 用真实失败案例持续修 Prompt。
做到这里,Prompt 才更像系统设计的一部分,而不只是聊天技巧的延伸。