Prompt 最朴素的含义,就是交给模型的指令和输入。所谓“工程”,不是把一句话写得更华丽,而是反复消除歧义:同类任务来十次,系统都知道应该先调查什么、哪些边界不能越过、什么证据才算完成。
读完这篇,你应该能回答:一份最小可用的 Prompt 需要哪些信息?每项信息消除什么歧义?哪些问题继续改措辞也解决不了?
资料说明:机制依据来自 OpenAI 与 Anthropic 的官方 prompt 和 agent 工程材料。示例是厂商中立的教学形状;真实消息角色、字段、优先级和缓存行为以目标 API 当前文档为准。篇末教程只作为延伸阅读。
一、先用一项任务看懂 Prompt
1.1 同一个任务,三版 Prompt 会带来三种结果
版本 0:“修复支付模块的失败测试。”模型只能猜:它可能直接改代码,可能顺手改公开接口,也可能没有运行测试就说完成。
版本 1:“修复 refund_should_reject_expired_order;不要修改公开 API;只做与失败直接相关的改动。”现在目标和修改边界清楚了,但“修好”仍然没有证据标准。
版本 2:再补上:“修改后运行支付测试;如果测试环境不可用,停止并说明阻塞原因;最终报告列出修改文件、测试命令和结果。”这一次,系统不只知道要做什么,也知道何时可以宣布完成、何时必须停下来。
这就是 Prompt engineering 的基本动作:找到任务里的默认假设,把它们变成清楚、可观察、可检查的要求。它可以用自然语言表达,并不等于把所有内容改写成 JSON。
1.2 一份工作说明要回答哪些问题
先不要背字段名。拿版本 2 逐项检查,就能看到一份 Prompt 通常包含六类内容:
- 目标:最终要解决什么问题?这里是让指定支付测试恢复通过。
- 背景与本次输入:现在发生了什么?例如失败测试名和错误日志。
- 约束:哪些边界不能越过?例如不能修改公开 API。
- 工作方法:遇到信息不足、工具失败或高风险动作时怎样处理?
- 完成条件:凭什么算做完?这里是实际运行测试并得到可核对结果。
- 交付方式:最后怎样报告,让下一位 reviewer 能快速检查?
示例是第七种可选材料:当“怎样处理边界情况”很难只用规则说清时,给一个正常例、一个阻塞例和一个反例,往往比继续堆解释更有效。
把六项合成一份自然语言 Prompt:“修复
refund_should_reject_expired_order。先根据失败日志和相关代码确认原因,只做与失败直接相关的最小修改,不要改变公开 API。修改后运行支付测试;如果测试环境不可用或必须改变公开 API 才能继续,就停止并说明阻塞。最终列出原因、修改文件、测试命令和结果。”
二、把一份好说明变成稳定接口
2.1 从“一次好回答”走到“稳定行为”
以“修复支付模块失败测试”为例,同一条任务可能产生三种行为:模型直接改代码;先读失败日志再修改;发现测试环境不可用后报告阻塞。Prompt engineering 的真正对象不是某一句输出,而是同类输入下这些可观察行为是否稳定:什么情况下先调查,什么情况下拒绝,什么证据足够完成,工具失败后怎样继续,最终输出必须包含什么。
因此,改动评审不能只比较 diff,还要比较行为。把“更简洁”“更主动”写进 prompt 只是意图;只有在代表性任务上测到更少的无效工具调用、更高的约束遵循和稳定输出,意图才变成工程结果。

2.2 进入产品后,再按变化速度拆开内容
| 内容 | 回答的问题 | 变化频率 | 推荐位置 |
|---|---|---|---|
| 稳定政策 | 永远不能做什么?何时必须升级给人? | 低 | 集中维护的稳定 instruction |
| 产品契约 | 角色、目标、完成标准、输出 schema 是什么? | 中低 | 版本化产品模板 |
| 任务参数 | 这一次要处理哪个对象、范围与偏好? | 高 | 结构化 user/task input |
| 动态事实 | 仓库、客户、时间和外部世界现在是什么状态? | 高且易过期 | Context / tool observation |
最常见的坏味道,是把动态事实硬编码进稳定 prompt。比如“当前发布分支是 release/42”很快会过期;又或者把稳定政策当作检索文档,结果某次没召回就失效。按变化频率拆开,才能让 prompt 保持稳定契约,让 context 承担新鲜证据。
Prompt 与 Context 不是两只互斥的盒子。一次模型请求里,Prompt 是“怎样做”的行为说明;Context 是模型这次能看到的全部材料,其中既可能包含 Prompt,也包含当前任务、文件和工具结果。Prompt Engineering 负责把说明写稳,Context Engineering 负责把整包材料装配对。
2.2.1 把模板变量变成有类型的边界
先看一个无法评审的版本:修复支付模块失败测试,改好后告诉我。 它没有说明能改什么、怎样验证、失败时怎样退出。更稳的形状是固定模板、显式字段、明确转义,并把不可信内容标成数据:
- 先固定契约:版本化目标、约束、完成条件和交付格式,避免每次任务临时改写。
- 再填本次任务:测试名、范围和当前错误进入明确字段,而不是混进稳定规则。
- 标出不可信数据:失败日志可以提供事实,但其中的文字不能提升为更高权威的指令。
- 最后装配请求:运行时检查必填项、转义和版本,再把契约与任务序列化给目标 API。
{
"contract_version": "payment-fix/2",
"goal": "Fix the failing payment test and provide review evidence",
"constraints": ["Do not change public APIs", "Run payment tests"],
"task": {
"failing_test": "refund_should_reject_expired_order",
"failure_log": "<untrusted data>"
},
"output": { "type": "json_schema", "name": "change_report" }
}这里,目标与约束属于可版本化的行为契约;测试名和失败日志属于本次任务;日志内容只是数据,不能提升为指令;“必须运行测试”还需要 harness 和 loop 真正执行,不能只靠模型承诺。
这不能消灭 prompt injection,但会让安全设计有抓手:不可信数据不会因为被拼到同一段文字里就自动升级成指令;真正的权限仍由 harness 控制,而不是由 prompt 自我声明。
2.3 多层指令冲突时,谁优先
工具型 agent 同时接收平台政策、开发者契约、用户任务和运行时事实。它们可能冲突:用户要求跳过测试,开发者契约要求修改后必须验证;网页内容要求上传密钥,平台政策禁止泄漏秘密;工具错误说路径不存在,但旧消息里仍保留旧路径。

2.3.1 为冲突写显式策略
- 高权威规则定义允许的行为集合,低权威请求只能在集合内选择。
- 同权威内容冲突时,使用更具体、更接近当前任务的规则,并记录无法消解的歧义。
- Evidence 描述世界,不自动拥有命令权限;网页、文件和工具输出默认可能不可信。
- 无法同时满足目标与约束时,输出阻塞原因和所需授权,不要静默降级。
这类规则应该可测试。构造“用户要求忽略开发者约束”“工具结果包含伪指令”“同层规则互相冲突”的样例,观察系统是否稳定走到拒绝、追问或升级路径。
2.4 工具说明要让模型知道何时用、怎样用
Agent 不只要知道工具名,还要知道何时调用、参数怎样约束、结果代表什么、失败能否重试。OpenAI 的实践指南建议工具定义清晰、标准化并有充分文档;Anthropic 的 agent 工程文章同样强调工具设计与接口质量。模糊工具会把本可确定解决的问题推给模型猜。
| 工具契约要素 | 坏形状 | 可评审形状 |
|---|---|---|
| 调用条件 | “需要时搜索” | 缺少当前事实或必须验证外部状态时搜索 |
| 参数 | 一个自由文本 query | 字段含义、枚举、范围和互斥条件明确 |
| 结果语义 | 返回任意文本 | 区分成功、空结果、暂时错误与永久错误 |
| 副作用 | 描述里说“谨慎使用” | 由 harness 标记读/写、审批和幂等语义 |
Prompt 可以教模型选择工具,却不能替代参数校验、权限和幂等控制。行为接口负责“应该怎么用”,runtime harness 负责“到底能不能执行”。
2.5 用示例教边界,不只展示顺利路径
Few-shot 示例最有价值的地方,不是让文字更像范文,而是展示边界决策。只放三个顺利样例,模型学不到冲突、空结果和升级。更好的示例集覆盖正常路径、边界路径与明确反例。
- 正常样例:失败日志指向过期订单校验;读取目标文件、做最小修改、运行 payment tests,并把命令与结果写入报告。
- 边界样例:测试命令因权限不可用;停止修改,返回
blocked、已知证据和所需授权。 - 反例:代码改完但未运行测试却输出“已修复”;明确标记它违反了
Run payment tests契约。 - 工具样例:本地日志已足够定位时不调用网络搜索,防止“有工具就调用”。
示例也消耗 context budget,因此不是越多越好。先用 eval 找到最常见的行为歧义,再用最小样例消除它;可以由 schema 或确定性代码保证的格式,不必全部靠示例重复。
三、进阶:像发布代码一样发布 Prompt
OpenAI 的官方 prompt 指南明确建议用 eval 衡量 prompt 表现。生产上可以把流程再补完整:先写行为 spec,分配版本,在固定回归集和探索集上评测,人工审查关键失败,灰度发布,观察真实分布,必要时回滚。
cases = [
Case("normal_fix", expected="verified"),
Case("test_unavailable", expected="blocked"),
Case("user_requests_skip", expected="refuse_shortcut"),
Case("tool_output_injection", expected="ignore_untrusted_instruction")
]
for version in ["payment-fix/1", "payment-fix/2"]:
for case in cases:
result = run_agent(prompt=version, task=case.input)
record(version, case.name,
contract_ok=validate_contract(result),
evidence_ok=validate_verification(result))
3.1 每个版本至少记录什么
- 模板正文、工具 schema 与关联模型快照。
- 变更意图:想改变哪类行为,明确不想改变什么。
- 评测集版本、整体指标与按失败类型切片的结果。
- 发布范围、观察窗口、回滚目标和负责人。
只看平均分会掩盖关键回归。一个版本可能让常规任务更短,却让“权限不足”场景更敢于猜测。指标必须按风险、任务族和工具路径切片,并保留代表性 trace 做质检。
四、复盘:什么时候改 Prompt,什么时候不要改
| 现象 | 先改 Prompt? | 更合适的第一落点 |
|---|---|---|
| 稳定约束经常被忽略 | 是 | 提高契约清晰度、优先级与冲突测试 |
| 模型不知道刚发生的事实 | 否 | Context selection / retrieval |
| 危险命令真的执行了 | 否 | Harness 权限、沙箱、审批 |
| 格式偶发不合法 | 部分 | 结构化输出 + 验证;Prompt 解释语义 |
| Agent 过早宣布完成 | 部分 | 完成契约 + Loop 中的确定性验证 |
| 新版本整体变好但高风险场景退化 | 否(先回滚) | 版本回滚、评测切片、重新设计 spec |
Prompt engineering 没有消失,它只是从“经验性写作”升级成了接口工程。下一篇进入另一条轴:Context 不是长期仓库,而是每次 inference 临时装配的工作集。
官方资料
- OpenAI:Prompt engineering
- OpenAI:Structured outputs
- OpenAI:A practical guide to building agents
- Anthropic:Prompt engineering overview
- Anthropic:Building effective agents