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 只是意图;只有在代表性任务上测到更少的无效工具调用、更高的约束遵循和稳定输出,意图才变成工程结果。

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 把模板变量变成有类型的边界

先看一个无法评审的版本:修复支付模块失败测试,改好后告诉我。 它没有说明能改什么、怎样验证、失败时怎样退出。更稳的形状是固定模板、显式字段、明确转义,并把不可信内容标成数据:

  1. 先固定契约:版本化目标、约束、完成条件和交付格式,避免每次任务临时改写。
  2. 再填本次任务:测试名、范围和当前错误进入明确字段,而不是混进稳定规则。
  3. 标出不可信数据:失败日志可以提供事实,但其中的文字不能提升为更高权威的指令。
  4. 最后装配请求:运行时检查必填项、转义和版本,再把契约与任务序列化给目标 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" }
}
形状级示例:版本、契约、任务数据和输出协议各自有边界。它们最终如何序列化取决于 API。

这里,目标与约束属于可版本化的行为契约;测试名和失败日志属于本次任务;日志内容只是数据,不能提升为指令;“必须运行测试”还需要 harness 和 loop 真正执行,不能只靠模型承诺。

这不能消灭 prompt injection,但会让安全设计有抓手:不可信数据不会因为被拼到同一段文字里就自动升级成指令;真正的权限仍由 harness 控制,而不是由 prompt 自我声明。

2.3 多层指令冲突时,谁优先

工具型 agent 同时接收平台政策、开发者契约、用户任务和运行时事实。它们可能冲突:用户要求跳过测试,开发者契约要求修改后必须验证;网页内容要求上传密钥,平台政策禁止泄漏秘密;工具错误说路径不存在,但旧消息里仍保留旧路径。

指令优先级栈,从平台政策、开发者契约、用户任务到运行时证据
权威高低和时间新旧是两条不同轴。新的 evidence 可以纠正旧事实,却不能覆盖更高层政策。

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))
最小回归实验:同一批案例同时跑旧版和新版,再按正常、阻塞、高风险与注入切片比较;平均分提高但高风险切片退化时,仍应停止发布。
Prompt 变更发布循环,从规范、版本、评测、审查、发布到观察,并支持回滚
Prompt 不是文案资产,而是会改变系统行为的版本化依赖。

3.1 每个版本至少记录什么

  • 模板正文、工具 schema 与关联模型快照。
  • 变更意图:想改变哪类行为,明确不想改变什么。
  • 评测集版本、整体指标与按失败类型切片的结果。
  • 发布范围、观察窗口、回滚目标和负责人。

只看平均分会掩盖关键回归。一个版本可能让常规任务更短,却让“权限不足”场景更敢于猜测。指标必须按风险、任务族和工具路径切片,并保留代表性 trace 做质检。

四、复盘:什么时候改 Prompt,什么时候不要改

现象先改 Prompt?更合适的第一落点
稳定约束经常被忽略提高契约清晰度、优先级与冲突测试
模型不知道刚发生的事实Context selection / retrieval
危险命令真的执行了Harness 权限、沙箱、审批
格式偶发不合法部分结构化输出 + 验证;Prompt 解释语义
Agent 过早宣布完成部分完成契约 + Loop 中的确定性验证
新版本整体变好但高风险场景退化否(先回滚)版本回滚、评测切片、重新设计 spec

Prompt engineering 没有消失,它只是从“经验性写作”升级成了接口工程。下一篇进入另一条轴:Context 不是长期仓库,而是每次 inference 临时装配的工作集。

官方资料

延伸阅读