[控场AI]
· 6 分钟阅读· 3,060 字

别把提示词当散文写:用四段式契约终结LLM漂移

别把提示词当散文写:用四段式契约终结LLM漂移

把LLM提示词从散文改写为契约,通过明确角色、任务、约束和输出schema来消除输出漂移。

文章提出,生产环境中LLM输出不稳定(漂移)的根本原因是提示词留有歧义,模型会用统计最可能的内容填补空白。解决方案是将提示词视为"契约",必须明确定义四个要素:角色、任务、约束和输出契约。其中输出契约需包含失败逃生出口(如`{

大多数所谓的"提示词工程"建议都在纠结措辞,但真正的问题在于:很多人把提示词当成写邮件那样的散文来写,然后对模型在不同运行间产生的"漂移"(drift)感到惊讶。

生产级的提示词不是散文,而是契约。一份契约必须明确定义四件事,否则模型就会用统计上最可能的内容去填补空白——而这正是你看到的漂移根源。

4-part contract that kills LLM drift

契约的四个组成部分

任何需要重复表现一致的提示词,都应该回答以下四个问题:

  1. 角色(Role) —— 谁在说话,他们优化的目标是什么?
  2. 任务(Task) —— 唯一的交付物是什么?(不是一个清单,而是一件事。)
  3. 约束(Constraints) —— 什么事情绝对不能发生?
  4. 输出契约(Output contract) —— 一个有效答案的确切形态,以及当输入无法满足时该怎么办。

只要缺了任何一项,模型就会去猜。而猜测就是敌人。这个框架的价值不在于技巧,而在于把每一个本该由你决定的选择,提前固化下来,不让模型每次运行各做各的。

对比:散文式提示词

先看一个典型的散文写法:

You are a helpful assistant. Summarize the following customer review and tell me
if it's positive or negative and maybe what the main complaint is. Be concise.

这个提示词会根据输入的不同,在至少五个方向上翻车,而且你无法预测它会在哪里出问题。"be concise""maybe"这类模糊措辞,等于把判断权完全交给了模型。

改造后:契约式提示词

ROLE
You extract structured feedback from customer reviews. You optimize for accuracy,
not for making the review sound better.

TASK
Given one review, produce a single JSON object classifying sentiment and listing
complaints.

CONSTRAINTS
- Never invent a complaint that is not present in the text.
- Never output anything except the JSON object. No preamble, no markdown fence.
- If the review is not about a product, set "relevant": false.

OUTPUT CONTRACT (strict)
{
  "relevant": boolean,
  "sentiment": "positive" | "neutral" | "negative",
  "complaints": string[],
  "confidence": number
}
If you cannot comply, output {"error": "<short reason>"}.

第二个提示词更长,而这恰恰是重点。每多一行,都是你一次性做出的决定,而不是放任模型每次运行时做出不同的决定。长度换来的是确定性。

输出契约:所有人都会跳过的那一部分

一个没有"逃生出口"的 schema 会导致一种非常具体的故障:当输入无法契合时,模型会为了满足 schema 而编造内容。它宁可返回一个形态正确但内容错误的对象,也不愿承认失败。

解决办法是显式定义失败形态(比如 {"error": ...}),并在代码层做校验:

import json

REQUIRED = {"relevant", "sentiment", "complaints", "confidence"}

def validate(raw: str) -> dict:
    obj = json.loads(raw)
    if "error" in obj:
        raise ValueError(obj["error"])
    missing = REQUIRED - obj.keys()
    if missing:
        raise ValueError(f"missing keys: {missing}")
    if obj["sentiment"] not in {"positive", "neutral", "negative"}:
        raise ValueError("bad sentiment")
    return obj

经过这样处理,"漂移"就从一个在下游三个系统之后才悄悄爆发的错误答案,变成了一个大声报错、可被捕获的异常。这是从"希望它正确"到"验证它正确"的根本转变。

真正改变行为的约束

约束这一行才是你赢下这场战役的地方。弱约束(比如"be accurate")毫无作用;强约束则能精准移除特定的故障模式:

  • "永远不要编造文本中不存在的投诉" → 阻止幻觉细节。
  • "不要前言,不要 markdown 代码块" → 阻止模型把 JSON 包进代码围栏。
  • "只有一个交付物" → 阻止模型回答你没问的三个问题。
  • "如果 X,就做 Y" → 给模型一个合法出口,从而减少编造。

关键观察:这些约束要么是否定式,要么是条件式。"Be helpful"是不可证伪的;而"never invent"是可测试的。可测试性,决定了一个约束到底有没有用。

几个容易踩的坑

测试契约,而不是感觉。 保留 20–50 组输入/预期输出对,每次改动提示词都跑一遍。一个"感觉更好"的提示词改动,可能悄悄破坏了 10% 的用例。

不要过度约束推理过程。 如果任务需要思考,就给模型空间,只是别让思考过程泄漏到输出里。可以用两次调用的模式:一次负责推理,一次负责格式化。

像管理代码一样管理提示词版本。 提示词是一个依赖项。如果它不在 git 里、没有变更日志,你就无法回滚引入的回归。

同一份契约,适配所有提供商。 如果你在不同模型之间做故障转移,输出契约正是保证它们的响应可以互换的东西。

为什么这件事重要

从一个演示级提示词到一个生产级提示词,差的不是聪明,而是显式性。一个你能够校验的契约,才是一个你真正能发布、能监控、能调试的提示词。其余的一切,都只是在碰运气。

这套方法论的核心启示是:把提示词当作软件工程的一部分来对待——有明确接口、有校验、有版本控制、有测试集。当你开始用"契约"而非"散文"的思维去写提示词,LLM 的输出稳定性会发生质的变化。

#提示词工程#LLM漂移#提示词契约#输出契约#Prompt Engineering#结构化输出#JSON schema#AI应用开发
分享:

相关推荐