跳到主要内容

提示词债务螺旋:单行补丁如何摧毁生产环境的提示词

· 阅读需 10 分钟
Tian Pan
Software Engineer

进入生产环境六个月后,你面向客户的 LLM 功能的系统 Prompt 已从最初清爽的 11 行增长到超过 400 个 token,充斥着各种条件指令、对冲表述和异常处理。质量明显比发布时更差,但当时的每一次单独修改似乎都是合理的。没人知道哪些条款相互冲突,也没人知道其中一半是否仍然必要。没人敢动它。

这就是 Prompt 债务螺旋——大多数处于生产阶段的团队已经深陷其中。

Prompt 债务是 LLM 特有技术债务中占比最大的一类,在真实世界 AI 项目中,它占所有已识别技术债务问题的 6% 以上,超过了超参数调优和框架集成问题的总和。数据来自最近对数百个 LLM 代码库的一项经验性研究——但你可能不需要研究也能识别出这种模式。真正的问题是,为什么 Prompt 退化得如此有规律,以及结构性的修复方案是什么样的。

为什么补丁会堆积成债务

触发因素总是生产环境中的边缘情况(edge case)。Agent 在处理客户姓名带撇号的退款请求时出错。摘要生成器在下游系统需要散文时返回了项目符号。分类器幻觉出了一个不存在的类别。

工程师的本能是正确的:识别失败,添加一条解决该问题的指令,部署。问题在于这个循环是无限运行的,而且每次迭代的成本并非固定不变,而是不断增加。

每一个新的条件指令都会与现有的所有条款发生相互作用。系统 Prompt 并不是程序;模型不会按照确定的优先级规则从左到右解析它。它会同时关注整个上下文,在存在冲突指令的情况下,它的行为会变得不可预测,而不是遵循规则。“始终用英语回答”和 500 个 token 之外的“根据用户消息的语言回答”将无法得到清晰的解决——模型会根据周围上下文的细微变化,先应用其中一个,然后应用另一个,最后可能两个都不应用。

关于 LLM 在 Prompt 长度增加时的指令遵循行为的研究揭示了一种被称为指令稀释(instruction dilution)的模式:随着系统 Prompt 的增长,模型开始优先处理文本开头和结尾的条款,而忽视中间的所有内容。一项使用嵌入相关信息的资料集研究发现,对于位于上下文中间位置的内容,准确率下降超过 30%——这还是针对检索而言,而不是更难衡量、更容易被忽视的指令遵循能力。

经验性的失败轨迹是这样的:第一个边缘情况补丁效果显著。第二个补丁有些帮助。第十个补丁的效果可能比什么都不做还要差。

三种结构性失败模式

Prompt 债务在不同地方的表现各异,但失败模式往往呈现出可识别的规律。

相互矛盾的指令。 两个间隔数月添加的条款,各自独立看都很合理,但直接相互冲突。这比编译错误更阴险,因为 Prompt 仍然“有效”——模型只是不可预测地随机选择一条指令,直到你检查失败案例时才会发现。一个客服 Prompt 既写着“始终为不满意的客户提供折扣”,又写着“绝不主动提及折扣”,这就是成熟 Prompt 中常见的冲突类型。

超出适用背景的硬编码假设。 早期的 Prompt 通常包含关于用户群、使用场景范围或模型行为的隐式假设,这些假设在发布时是正确的,但现在已不再适用。这些假设还没错到导致直接失败——它们只是悄悄地降低了质量。例如,一个最初为 B2B 用户编写的 Prompt 被重新用于消费者,并在其上叠加了每一个新的边缘情况补丁。

格式债务。 臭名昭著的 #TODO: 将响应转为 JSON 并就这样发布到了生产环境。在初始实现中推迟的结构化输出要求在稍后被强行加入——此时 Prompt 已经包含了几十条关于内容的条件条款,而这些内容现在还必须生成有效的 JSON。格式要求和内容要求相互作用,使得一旦两者同时存在, Prompt 对后续修改就会变得极其脆弱。

为什么移除债务如此困难

LLM 项目中自认的技术债务中位数会持续 553 天——这是研究文献中所有软件类别中移除率最低的。原因并非懒惰,而是移除 Prompt 债务的成本确实很高,这与移除死代码完全不同。

生产环境 Prompt 中的每一个条款都可能是某些真实输入的“承重墙”。你无法通过 grep 查找调用点。你无法编写一个覆盖完整行为层面的单元测试。移除一个你认为多余的条件指令可能会破坏你尚未测试的功能,而且这种失败可能非常微妙,以至于在手动 QA 中无法察觉。

结构性问题在于缺乏契约(contract)。函数有签名。数据库模式有约束。Prompt 是一份非正式的英文文档,没有机制可以验证移除一个句子是否会改变整个生产查询分布下的输出行为。

那些将 Prompt 视为静态配置文件(在 UI 中编辑、临时部署、无版本历史记录)的团队,无法评估重构成本,因为他们没有可以对比的基准。

终结这种恶性循环

修复方案是架构层面的,而非文本层面的。你无法通过更仔细地重写 Prompt 来摆脱 Prompt 债务。除非 Prompt 周边的基础设施发生变化,否则下一个边缘情况(edge case)出现时,恶性循环就会再次开启。

分层 Prompt 架构。 最有效的结构化变更是将系统 Prompt(system prompt)中的内容与针对单次请求的上下文注入(context injection)分离开来。系统 Prompt 应该只涵盖目标、硬性约束、输出格式和基本行为 —— 别无他物。仅适用于特定请求类型的边缘情况应放在路由层(routing layer),在推理时预置相关上下文,而不是放在一个随着每个新案例不断膨胀的单体文档中。

一个实用的启发式方法:系统 Prompt 的长度应保持在典型上下文窗口(context window)的 5–10% 以下。如果你使用的模型拥有 128k token 的窗口,而你的系统 Prompt 是 2,000 token,那么你消耗的开销已经超出了该模式所能支持的范围。超出这个范围,你就在为每一次推理调用支付指令稀释(instruction dilution)的代价。

像对待代码变更一样对待 Prompt 变更。 版本控制、解释 为什么 添加某条子句的提交信息(commit messages)、同行评审以及分阶段发布。这些并不稀奇 —— 这正是你对应用程序代码所做的,也正是大多数团队在处理 Prompt 时跳过的,因为他们觉得 Prompt “不是代码”。但它们确实是。它们是 LLM 系统中业务逻辑最密集的产物,需要同样的纪律。

在重构之前构建黄金数据集。 没有回归测试集(regression harness)的重构只是瞎猜。一个包含 50–200 个真实生产环境输入/输出对并带有质量标签的黄金数据集(golden dataset),能让你衡量 Prompt 的变更在实际分布中是让结果变好了还是变坏了 —— 而不仅仅是你随机手动测试的那三个例子。这不需要复杂的评估框架。一个包含预期输出的电子表格和一套系统的审查流程就足以开始。

进行废弃子句审计。 大多数成熟的生产环境 Prompt 都包含为了不再发生的边缘情况而添加的指令,或者是为了当前模型版本已不再表现出的模型行为而添加的指令。这些子句仍然会消耗 token,并与现有的指令产生交互。定期根据最近的生产环境故障审查每条子句:如果你无法确定删除某条子句会改变行为的情况,那就删除它,并通过回归测试集进行验证。

将内容指令与格式指令分离。 格式要求 —— 如 JSON schema、响应长度、输出结构 —— 应该声明在 Prompt 的末尾,或者通过结构化输出 API 来处理,而不是与内容指令混杂在一起。模型对两者的处理方式不同,将它们分开可以降低内容变更意外破坏格式合规性的风险。

模型迁移这一“催化剂”

大多数团队是在被迫进行模型迁移时才通过惨痛的教训学到这些的。现在,模型供应商大约每季度就会弃用旧模型,而原本计划为“更换模型 ID”的迁移工作,往往在团队发现 Prompt 中积累了多少隐性行为依赖时,演变成长达数周的项目。

一个在 2023 年为 GPT-4 编写并经过增量补丁维护到 2025 年的 Prompt,是对可能不再成立的模型行为假设的重重叠加。当迁移导致某些功能损坏时,没有结构化的方法来诊断哪些子句依赖于已弃用模型的特性 —— 因为这些依赖关系从未被记录,子句也从未经过独立测试。

那些能够顺利处理模型迁移的团队,是在需要回归测试集之前就已经构建好它的团队。这个测试集主要不是为了迁移 —— 它是将 Prompt 视为生产基础设施的副产品。迁移只是它发挥价值的时刻。

在恶性循环完成前开始

实施 Prompt 版本控制和回归测试集的最佳时机是在你发布生产环境 Prompt 第一个版本的那天,也就是在任何债务产生之前。此时成本很低,工具要求极小,你可以在激励机制对你不利之前建立起纪律。

第二好的时机就是现在,在相关的 Prompt 变得不可维护之前。

这种模式的可预测性足以让你采取行动:一个系统 Prompt 如果在没有相应回归测试的情况下被修补了五次以上,就已经负债累累了。一个 Prompt 自初始版本以来增长了 3 倍以上,且没有经过刻意的架构审查,无论每一次变更单独看起来多么合理,它都存在结构性问题。这些不是警告信号,而是诊断结果。

Prompt 债务是默默累积的。与缓慢的数据库查询或内存泄漏不同,它不会触发警报。LLM 输出的质量下降很难归因于任何特定的变更,很容易被合理化为“模型就是不稳定”,且在传统监控中是不可见的。当团队达成共识认为出了问题时, Prompt 已经过于错综复杂,无法在没有风险的情况下进行重构。

在债务自动堆积之前,先构建好基础设施。

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