债务不在 linter 检查的地方
传统软件把债务累积在编译器最终会抱怨的地方:重复的逻辑、过时的接口、没有文档的不变量。Agent 系统把债务累积在没有任何东西会抱怨的地方。提示词是一个字符串。工具 schema 是一份 JSON 文档。关于哪个 agent 拥有哪项决策权的约定,存在于一份第二个 sprint 之后就没人再读的设计文档里。
这不是风格问题,而是结构问题。当决定系统行为的产物是无类型、无版本的散文时,每一次改动都是静默的改动。没有任何东西构建失败。系统继续运行。它只是运行得不一样了,而这个差异要在几周之后才会被发现——当下游某个 agent 开始产出看起来合理、却以某种具体且可复现的方式出错的输出时。
在多个 agent 之间重复、且已静默分叉的提示词片段
一种常见模式:诸如「始终引用源文档」这样的共享指令被复制进四个 agent 的提示词。六周后,其中三个被改过。一个现在写的是「在可用时引用源文档」。另一个写的是「如果检索成功则引用源文档」。第四个没动。没有任何测试失败。有条件引用的 agent 和无条件引用的 agent 产生不同的审计轨迹,而这个差异只有在有人试图跨流水线对账输出时才会浮现。
债务不是重复本身。重复很廉价。债务是缺少一个能检测分叉的机制。一个在构建时导入的共享提示词片段会让分叉变成一次 diff。一个复制粘贴的片段让它变得不可见。
不再匹配其所封装服务的工具 schema
工具 schema 是一份契约。当底层服务发生变化——某个字段被重命名、某个可空字段变成必填、某个错误以 200 加一个 status 字段的形式返回——schema 不会自我更新。agent 继续用旧的形状调用这个工具。服务要么拒绝这次调用,此时 agent 会重试并烧掉 token;要么接受它并返回一些 agent 会误读的东西。
第二种情况更糟。一个大声失败的工具是一个会被修好的工具。一个安静失败的工具会产出自信而错误的输出。这是 agent 系统里等价于「返回 null 而不是抛异常」的那种函数。
关于哪个 agent 拥有哪项决策权的无文档约定
多 agent 系统会发展出隐式的归属关系。一个 agent 决定何时升级。另一个决定何时重试。第三个决定何时总结。这些决策很少被写在一个地方。它们从提示词、编排代码以及 agent 被加入的顺序中涌现出来。
当引入一个新 agent 时,它不继承其中任何一项。它做出自己的决策,有时与既有 agent 冲突。这个冲突不是任何一个 agent 的 bug,而是一份缺失的规格说明。系统现在对同一项决策有了两个所有者,而如何解决取决于执行顺序。

这是最难看见的一类债务,因为它不在任何单一产物里。它在产物之间的缝隙里。
让它可见
补救办法不是写出更好的提示词,而是让提示词、schema 和约定接受与代码同等的纪律约束。
把提示词当作有所有者和变更日志的版本化产物
提示词应该住在仓库里,而不是住在通过管理面板编辑的某个数据库行里。它应该有一个所有者——一个团队或一个角色,而不是某个人——以及一份记录了什么被改动、为什么改动的变更日志。变更日志比版本号更重要。一次没有理由说明的提示词 diff,是一次会被下一个看不懂它的人回滚掉的 diff。
版本化还使回滚成为可能。当一次提示词改动降低了输出质量时,能否回退到一个已知良好的版本,就是十五分钟的修复和数天的排查之间的差别。
用真实端点检验工具 schema 的契约测试
工具 schema 应该像测试一个 API 客户端那样被测试:调用真实端点,并断言响应符合 schema。这不是一个带 mock 的单元测试。mock 把 schema 编码成写 mock 时它所被理解的样子,而这恰恰就是那个需要被证伪的假设。
针对工具 schema 的契约测试断言三件事:端点接受 schema 所声明的输入形状;响应包含 schema 所承诺的字段;错误条件以 agent 能够据以行动的方式暴露出来。第三条断言是最常被跳过的,也是最重要的,因为一个无法区分瞬时错误和永久错误的 agent,会一直重试一个永久错误,直到耗尽预算。
一份把每个 agent 映射到其工具、记忆作用域和升级路径的注册表
注册表是归属关系的唯一事实来源。对每个 agent,它记录:它可以调用哪些工具、它可以读写哪些记忆作用域、以及在什么条件下它会升级。注册表不是文档,而是编排层读取的配置,因此一个 agent 无法调用它并不拥有的工具,也无法写入它并不持有的记忆作用域。
这把一项隐式约定转化为一项被强制执行的约定。取舍是真实的:注册表增加了一层间接性和一份维护负担。每新增一个工具都需要一条注册表条目,而每一条注册表条目都是一个潜在的故障点——如果它与代码发生漂移的话。另一种做法——让归属关系保持隐式——在第一次冲突决策出现之前都更便宜,而一旦出现,诊断的成本就会以很大的幅度超过注册表的成本。

重构规则
如果某个行为很重要,它就属于代码或 schema,而不属于提示词里的散文。
这就是全部规则。它说起来容易,做起来难,因为提示词是阻力最小的路径。往提示词里加一句话,比往 schema 里加一个字段更快。那句话立刻生效。而 schema 需要一次迁移、一个测试和一次评审。
这条规则不是对散文的禁止。提示词需要散文来处理语气、框定,以及那种抗拒形式化的引导。这条规则是一条边界:散文用于那些可以变化而不会破坏系统的东西。schema 和代码用于那些不能变化的东西。
一个有用的检验:如果这个行为是你愿意为它写测试的东西,它就不属于提示词。如果你不会为它写测试,它可能属于提示词。测试就是那条分界线。
这要付出什么代价
诚实的取舍是:这套纪律会拖慢第一个版本。一个有所有者、有变更日志的版本化提示词,比一个在仪表盘里编辑的字符串上线更慢。一个契约测试比一个 mock 写起来更慢。一个注册表比一项约定搭建起来更慢。
回报不在第一个版本,而在第五个版本。改动成本低的 agent 系统,是改动清晰可读的系统。清晰可读来自那些可以被 diff、被测试、被拥有的产物。住在仪表盘里的提示词写起来便宜、改起来昂贵。住在仓库里的提示词写起来昂贵、改起来便宜。选择在于付哪一笔成本,以及什么时候付。
对于在生产环境中运行 agent 系统的团队来说,相关的问题不是债务是否存在。它存在。问题是它是否可见。一个已经静默分叉的提示词片段是不可见的债务。一个有版本、有所有者、有变更日志的提示词片段是可见的债务,而这是唯一一种能够被偿还的债务。
总结
Agent 代码库的腐坏方式不同。编译器不检查提示词、工具 schema 或归属约定,所以债务在没有任何信号的情况下累积。让它可见,需要把提示词当作版本化产物、用真实端点测试工具 schema,并维护一份 agent 能力与升级路径的注册表。重构规则很简单:如果某个行为很重要,它就属于代码或 schema,而不属于提示词里的散文。成本先付;回报在之后的每一次改动中兑现。
封面照片:Arturo A / Pexels。
