Prompt 模式与反模式
少数几种 prompt 结构能解决大部分任务,更少的几个错误造成了大部分失败。可复用的形状,以及要避开的形状。
大多数 prompt 问题都不新鲜,无非是那几种反复出现的形状之一。一旦你能叫出形状的名字,解法就可以复用。软件里的设计模式也是这个道理:不是魔法,只是一套给反复有效的结构起的名字。
下面是值得内化的模式,以及那些会悄悄毁掉一个好 prompt 的反模式。它们都建立在基础之上。
可复用的模式
角色与任务设定。 开头先说明模型在做什么、为谁做。「你在审查一个 pull request 的安全问题」在内容到达之前就收窄了输出分布。这不是奉承,也不是在演人设——它设定的上下文让后面的 prompt 更容易被预测。保持事实性,并贴合具体任务。
指令在内容之前。 先说要什么,再给材料。框架放在前面,模型读数据时就会带着这个框架去读。反过来——一大段文本后面埋着一个问题——前面那些 token 在被处理时,模型完全不知道什么重要。
输出契约。 说清你要的确切形状:「返回一个 JSON 对象,键为 summary 和 risk_level。」含糊的要求得到的是飘忽的输出。一份明确的契约是性价比最高的可靠性提升,需要保证的时候再配合真正的结构化输出约束。
拆解。 任务有好几步,就把步骤要出来。要么带着模型一步步走(思维链),要么拆成几次调用,让前一次的输出喂给后一次。一个 prompt 同时做五件事,五件事都做得更差。
示例优于描述。 语气、风格、格式,演示出来比规定出来容易得多。两三个例子胜过一段形容词。文字留给例子表达不了的规则,比如硬性约束。
出口。 告诉模型做不到时该怎么办:「如果文本里没有日期,返回 null。」不给这个出口,拿到一个不可能完成的要求时,模型会编一个像样的答案,而不是拒答。
面向可靠性的模式
这些在 prompt 进了生产环境之后才真正重要,在试验场里无所谓。
分隔不可信输入。 用清楚的标记把用户提供或检索来的文本包起来,并声明模型应把它当数据、不当指令。这是你对抗提示注入的第一道、也是最弱的防线——必要但不充分。
正面指令优于否定指令。「用一个段落回答」比「不要写好几段」更管用。否定只描述了一大片被禁止的区域,却没有钉住你想要的那个点。说要什么,别去说要的东西的无限补集。
最重要的那条指令,在最要命的时候放到最后。 模型对 prompt 末尾的注意力很强。如果有哪条规则必须守住,在开始生成之前再重复一遍,是防止长 prompt 把它稀释掉的便宜保险。
反模式
把客气当指令。「请尽量确保你仔细考虑……」增加的是 token,不是约束。它读起来像在含糊其辞,而含糊的指令产出含糊的输出。想要什么就直说。
大杂烩 prompt。 十条规则、三个例子、两个人设,再硬塞一份格式说明。每加一样,其他几样就被稀释一分。当 prompt 越加越不见好,问题通常是它已经过载了——该删,不是该加。
互相冲突的指令。「简洁但详尽。有创意但严格忠于事实。」模型会在两者之间取一个不可预测的折中。两条指令互相拉扯时,留下重要的那条,丢掉另一条,或者把它们分到不同的调用里。
过度规定推理过程。 对模型本来就做得好的任务,硬规定确切的推理步骤,会把它挤离自然的路径,反而降低质量。拆解只在有用的时候用,不要处处都用。
以为模型记得住。 每次请求都是无状态的。三轮之前给过的规则,除非还在当前上下文里,否则已经没了。在一次全新的调用里写「如我前面所说」,什么都没指向。
没测过的 prompt 漂移。 一个被反复手工修改、从不和上一版对比的 prompt,会悄悄退化。把 prompt 当代码:给它做版本管理,改动都拿例子验一遍。
怎么把一个 prompt 做出来
靠谱的循环不是「一遍写出完美的 prompt」,而是:
- 从最小开始——任务、契约、一个例子。
- 收集它失败的案例。
- 加最小的改动,解决一类失败,而不是一个孤例。
- 回头检查这次改动有没有弄坏之前已经通过的案例。
大部分工作是对着真实失败调试,而不是一开始就构思。从观察到的失败里长出来的 prompt,胜过一口气写成的 prompt,因为它把真正会出的问题编码了进去。
要点回顾
- 少数几个模式覆盖大部分任务:角色设定、指令在前、输出契约、拆解、示例优于描述,以及明确的出口。
- 为了可靠性:分隔不可信输入、优先用正面指令、把承重的那条规则放在最后。
- 反模式有一个共同点:只加 token,不加约束——客气话、大杂烩 prompt、互相冲突的规则。
- prompt 是无状态的,也会悄悄漂移;给它做版本管理,改动都要测。
- 开发 prompt 靠的是修掉观察到的失败类别,而不是一开始就构思出完美的 prompt。
下一篇: 上下文学习:少样本提示为什么有效