跳到主要内容

规范先行(Spec-First)智能体:为什么契约必须先于提示词落实

· 阅读需 13 分钟
Tian Pan
Software Engineer

我接手时,我们客服智能体的提示词已经达到了 2,400 个 token,而编写它的工程师已经离职了。其中的每一条指令对于某些生产行为来说都是“承重的”,但没人能告诉我哪些才是关键。一条关于“在回答之前务必先复述用户问题”的条目看起来像是凑数的,直到我们删掉它后,CSAT(客户满意度)在一周内下降了四个百分点。事实证明,提示词就是规范(specification)。它既是实现(implementation),也是测试套件(test suite)——它是隐性的、未记录的,仅存在于那位已经离职的工程师脑中。

这就是“提示词即规范”的终局。提示词既是智能体应该做什么,也是它如何做,一旦提示词规模超过了单个作者的掌握范围,两者就会变得无法区分。你无法重构它,因为你不知道哪些行编码了需求,哪些行仅仅是暗示。你无法评审变更,因为没有可以与之对比的基准产物。你无法让任何人接手并负责它,因为负责意味着“最近阅读过全文并记得每一项条款存在的原因”,这是一项没人愿意批准的、长达六个月的投资。

“规范优先”颠覆了这种顺序。契约(contract)——输入、输出、不变量、错误情况、拒绝语义、升级触发条件——是一个先于提示词并约束每次修订的一等产物(first-class artifact)。对提示词的修改变成了针对规范的补丁(diff),而不是对规范本身的重写。这种转变听起来很官僚,直到你看到它所释放的潜力:评估(evals)源自规范而非反之,评审只需几分钟而非整个下午,最终能让新工程师在没有六个月学徒期的情况下直接接手整个模块。

“提示词即规范”的失败模式

大多数团队所实践的提示词工程(Prompt engineering),是一种迭代式的手艺。你写一个提示词,运行一些例子,发现问题,添加一个条款,再运行更多例子,发现新的失败,再添加一个条款。三个月后,你得到了一个在许多情况下表现良好,但在其余情况下表现诡异的提示词。六个月后,你得到了一个除了原作者之外,任何人都无法在不引起回归(regressions)的情况下进行修改的提示词。

失败的不是这门手艺——迭代优化是大多数工程的运作方式——而是提示词同时承担了三个角色。它规定了预期的行为。它通过措辞、顺序和强调来实现该行为。它还充当了测试套件,因为促使每个条款产生的示例都被固化在了条款中,而不是作为回归测试被捕获。当这三个角色坍缩为一个产物时,每次修改都变得高风险:你无法判断你是在修改需求、实现方式还是测试。

你能在下游看到这些症状。提示词更改被回滚,因为“它在某个我无法重现的边界情况中表现得很奇怪”。两位工程师为一个行为是 Bug 还是预期设计争论一小时,因为没有规范来裁决。提示词增长超过了“两个作者能同时在大脑中理解”的阈值并固化——任何改动的风险都超过了预期收益,因此提示词被冻结,而它周围的产品却在不断迭代。供应商推出了新模型,同样的提示词产生了细微差别的输出;没人能说清新的输出是否违反了契约,因为根本没有契约可供违反。

当你跨入这个区域时的征兆:当有人问“为什么提示词里写了 X?”答案是“我在用户报告了 Y 之后加上的”,但没人能拿出 Y、针对 Y 的回归测试,或者规范中被 Y 违反的部分。

契约实际上包含什么

一个有用的契约既不是设计文档,也不是增加了额外步骤的提示词。它是一个专注的产物,命名了那些提示词不擅长显式表达的事物。至少包括:

  • 输入(Inputs)。 智能体接收内容的形态——用户消息、工具输出、检索到的上下文、对话历史——以及典型案例和边界情况的具体示例。长度限制。什么是保证提供的,什么是可能缺失的。
  • 输出(Outputs)。 智能体生成内容的形态,包括格式(JSON schema、markdown 结构、特定字段)、可接受的内容范围和必填章节。“回答问题”不是输出规范;“返回一个包含 answer(≤200 字)和 confidence(低/中/高)的 JSON 对象”才是。
  • 不变量(Invariants)。 无论输入如何,每个输出都必须满足的条件。智能体永远不泄露系统提示词。它永远不承诺价格。它永远不声明未检索到的事实。如果不使用规范,不变量就是你会添加防护栏(guardrail)的那些地方。
  • 错误情况(Error cases)。 当智能体无法完成正常流程时该怎么做。信息缺失、检索冲突、工具失败、用户意图模糊。每种情况在规范中都有一个具名的分支和示例。
  • 拒绝语义(Refusal semantics)。 智能体拒绝执行什么以及如何拒绝。拒绝是有表现形式的——语气、解释、建议的替代方案、升级流程——这种表现形式是契约的一部分,而不是提示词碰巧产生的一种“氛围”。
  • 升级触发条件(Escalation triggers)。 智能体将任务移交给人工、另一个智能体或另一个工具的具体条件。“用户感到沮丧”不是触发条件;“用户明确要求人工,或在同一个子目标上连续三轮没有进展,或任何被审核工具标记的输入”才是。

这些都不是新鲜事——这些是过去两年在挖掘 LLM 失败案例过程中,形式化契约研究所总结出的类别——但类别本身不是重点。重点是契约是 独立于提示词的,而对提示词的修改是对“规范未变”的声明。如果修改实际上改变了规范中定义的内容,那么规范必须先改变。这一条规则正是让其余工程纪律变得具有“承重”意义的关键。

评测不再是逆向工程的产物

大多数评测套件都是在事后根据生产日志构建的。有人查看上一季度的失败案例,对其进行聚类,然后编写能够捕捉每个聚类的测试用例。套件有机地增长,但总是略微滞后于提示词——它更像是过去 Bug 的考古记录,而不是对预期行为的声明。

“规范优先”(Spec-first)颠倒了这一过程。规范是对预期行为的声明,而评测套件是基于规范生成的。每个不变量都会产生一个或多个用于检查它的测试用例。每个错误分支都会产生一个触发该分支并验证响应的案例。每个升级触发器都会产生一个正向案例(触发器正确触发)和一个负向案例(触发器不会误触发)。输出 Schema 成为应用于每次评测运行的结构化验证。规范告诉你该测试什么;你无需等待生产事故来驱动测试。

关于测试驱动型智能体(Agent)定义的研究表明,从行为规范中生成测试,相比大多数团队使用的基于案例的方法,能捕捉到不同分布的 Bug——具体来说,它能捕捉到那些“在主路径上正常,但在边缘情况中悄无声息出错”的失败,这类失败在生产日志中不会显现,因为用户会直接停止使用而不是报告。当规范要求智能体在字段缺失时请求澄清,评测就能捕捉到那些意外导致智能体进行猜测的提示词改写。当规范要求对某一类请求进行拒绝,评测就能捕捉到那些导致智能体在 3% 的情况下勉强顺从的提示词改写。

次生效应是评测覆盖率变得清晰可读。“我们有 40 个评测”毫无意义;“我们对规范中的每个不变量和每个错误分支都有评测”则非常有意义。规范为你提供了一个分母——正是这个分母使得“我们的评测套件是完整的”成为一个可证伪的断言,而不仅仅是一种感觉(vibe)。

评审变为针对规范的差异比较 (Diff)

在“提示词即规范”的情况下,提示词 PR 看起来是这样的:一个 200 行的文件改动了 3 行,没有说明这些行改变了哪些行为,而评审者要么直接通过(“看起来没问题”),要么将其搁置一周以重构作者的思想模型。这两种结果都不是好事。直接通过会遗漏回归 Bug;搁置则在可能正确的工作上浪费了时间。

在“规范优先”下,提示词 PR 有两种可能的形态。第一种:提示词改变了但规范没变——这是一种声明契约未变的提示词编辑,评审者的工作是验证评测套件(由未变的规范生成)是否仍然通过。评审是机械化的:CI 变绿了吗?如果是,则合并。另一种形态:规范改变了,提示词也随之改变。在这里,规范的变更才是真正的评审核心——添加或删除了哪些新的不变量、错误分支或拒绝策略?变更是否符合预期?相应的评测更新是否正确捕捉了新行为?提示词的变更随规范变更而产生,本身很少需要深入评审。

不再应该出现的形态是过去占据主导地位的那种:提示词改变了,PR 描述或提交信息中规范未变,评审者不得不猜测行为变化是否符合预期。如果提示词编辑未能通过现有评测,规范应该随之改变——但必须是明确的,并且新的评测要随同一个 PR 提交。评审负担从“阅读整个提示词并判断此编辑是否破坏了它”转变为“阅读规范差异并判断此行为变化是否符合预期”。一个没有规范差异且通过了现有评测的三行提示词编辑,其评审是极其简单的。而一个要求更改规范的提示词编辑,则会得到它应得的审慎对待。

从提示词所有者转向契约所有者

组织上的回报是值得投入设置成本的。在“提示词即规范”下,智能体的行为只有编写它的人才能读懂。那个人成为了瓶颈——每一次编辑都要经过他们,每一个问题都需要他们去“考古”,每一次值班轮换都会在智能体表现异常时呼叫他们。团队试图通过轮换角色来解决这个问题,并接受每个新所有者需要三到六个月的时间来吸收提示词,然后才能安全地进行非琐碎的更改。根据我的经验,三到六个月还是乐观的估计;有些提示词从未被第二个所有者吸收,并在第一个所有者离职后变得僵化。

“规范优先”改变了所有权的形态。契约是一个新工程师可以在一个下午读完的人工制品。他们不需要知道提示词中每个条款存在的原因;他们只需要知道智能体应该做什么,这在规范中已经明确说明了。当他们进行更改时,首先对比规范差异,运行从规范生成的评测,然后才去触碰提示词来实现新行为。提示词内部的措辞变成了一个实现细节——只要规范和评测得以保留,未来的重构就可以在无需批准的情况下更改它。

新所有者的上手时间从几个月缩短到大约一周——阅读规范,阅读评测,手持规范阅读提示词以了解每个部分如何实现契约,然后端到端地交付一个小变更以证明流程畅通。这并非猜测;这是真正完成这一转型的团队所报告的情况。角色的名称也随之改变:“提示词所有者”(知道提示词为什么这么写的个人)变成了“契约所有者”(工程师本季度担任的角色,并在下季度移交给他人,而不会产生知识传递危机)。

明天写什么

如果你的 prompt 少于 500 个 token,并且由一个在可预见的未来都会留任的人负责,那么 spec-first 可能是不需要的额外开销。如果你的 prompt 超过 1,500 个 token,在提交历史中有一个以上的作者,或者交付的行为会影响金钱或安全,那么开销其实已经产生了——你只是还没有注意到,因为账单正以评审摩擦、入职成本以及由于没人敢改动而导致的 prompt 逐渐僵化等形式到期。

Spec-first 的最小可行版本不是一个框架或工具。它是一个放在 prompt 旁边的 markdown 文件,用你能写出的最具体的语言说明输入、输出、不变性(invariants)、错误情况、拒绝语义(refusal semantics)和升级触发条件(escalation triggers)——即上述六个类别。提交它。下一个 prompt 的 PR 需要包含规范的差异对比(spec diff),或者声明规范未变。再之后的 PR 则需要根据规范生成的评测(evals)。一个月后,prompt 将成为三个产出物之一,而不再是唯一的一个,agent 的行为也将属于团队,而非属于其作者个人。

Prompt 不再等同于规范。这就是全部的核心转变。

References:Let's stay in touch and Follow me for more thoughts and updates