提示词系统最可怕的地方在于它会「安静地崩溃」
我在公司推行一套多技能 Agent 系统的时候就踩过这个坑。当时设计了 15 个技能点和 9 个指令,每个环节产出的结构化 JSON 都要传给下一个环节。跑了两个星期一切正常,直到有一天我发现最终评分不对,但翻遍日志根本没报错。原因极其阴险:其中一个技能点悄悄地停止写入某个字段了,后面的环节读到 null 之后竟然没崩,而是直接带着 null 跑完了流程,最后给出的文档读起来依然极其专业,只是分数低了那么几分。
这种「似是而非」的输出能力是 LLM 的强项,但在工程化落地时,这正是最大的痛点。
针对这种不可控性,我花时间写了一个校验器(Checker),总结下来发现 Prompt 系统里只有三类东西是真正可测试的,而且能覆盖 90% 的回归问题:
- 确定性的算术逻辑: 比如我的系统会对六个维度进行加权评分,且有最低分阈值的惩罚机制。虽然维度分是由模型给出的,但最后的总分计算公式是确定的。我写了一个独立的校验函数,只要磁盘上的最终分数和校验函数重新计算的结果不一致,就说明出问题了,哪怕模型表现得再自信也没用。
- 数据的形状(Schema): 所有的技能输出必须符合预期的结构。包括必填字段、枚举值限制、明确的 null 定义,以及条件性要求(比如 A 字段出现时 B 必须出现)。这种 Schema 校验虽然不高级,但它抓住了最多的回归错误。
- 可量化的文本规则: 很多所谓的「质量要求」其实可以用数字量化。比如备忘录必须在字数预算内,必须包含特定的章节,或者必须引用至少 3 个符合 URL 格式的链接。模型在性能衰减时,通常表现为变得更啰嗦、更模糊且引用减少,这些通过数字校验能瞬间发现。
为了保证这个校验机制在团队内部能真正跑起来,我写了大约 750 行 Python 代码。这里有个细节:我坚持只用标准库,不引入任何第三方依赖。
因为这个校验器被挂在每个文件的写入钩子(Hook)上。如果它依赖一个复杂的虚拟环境,很多同事在 clone 代码库后可能根本懒得配置环境,导致测试根本没跑。一个不运行的测试比没有测试更糟糕,因为它给了你一种「被覆盖」的错觉。
具体的校验逻辑我定义在一个 JSON 配置文件里,通过文件名作为 Key,定义了六种指令:required(必填)、enums(枚举)、nullable(可为空)、conditional(条件触发)、nested_required(嵌套必填)和 one_of(多选一)。其中 one_of 很有用,因为有些分析文档在 B2B 和 B2C 场景下的字段定义不同,用这个指令可以避免把整个 Schema 分叉。
最后分享一个被大多数人忽略的细节:如果一个校验器永远不报错,那你无法证明它是否真的在工作。
为了验证校验器本身没写错,我准备了 10 个测试用例(Fixtures),其中 6 个是正确的,4 个是故意写坏的。最关键的是,这 4 个坏用例必须分别触发精确的 23、3、1 和 2 个错误。如果有一天某个用例只触发了 22 个错误,那就说明校验逻辑本身出 Bug 了。
我还对断言进行了「变异测试」:拿一个能通过的用例,故意改坏其中一个点,看校验器能不能抓到。结果我发现一个低级错误——我的断言在校验前把文本转了小写,导致我删除一个大小写敏感的字符串时,校验依然通过了。这次才让我意识到,之前的测试通过竟然是因为断言写得太宽松。
