非确定性服务的 API 契约:随机输出下的版本管理
你的内容审核服务返回 {"severity": "MEDIUM", "confidence": 0.85}。下游计费系统将 severity 解析为枚举值 ["low", "medium", "high"]。一次模型更新后,服务偶尔开始返回首字母大写的 "Medium"。没有任何部署发生,没有 schema 变更。集成在生产环境中悄然崩溃,整整六天无人察觉——因为所有 HTTP 状态码都是 200。
这是 LLM 支撑服务 API 契约的根本问题:表面看起来像 REST API,但底层行为是概率性的。标准契约工具假设确定性。当这个假设被打破时,它是悄无声息地崩溃的。
为什么传统契约测试会失效
消费者驱动契约测试(CDCT)——即 Pact 风格的模型——在确定性系统中运作良好。消费者编写指定预期交互的测试,提供者验证 能否满足这些交互。契约是一个静态工件,要么通过,要么失败。
这个模型在 LLM 支撑的服务中会因三个原因而崩溃:
输出在 token 级别是概率性的。 相同的提示在 temperature 为 0 时大多数情况下会产生相同的输出,但并非总是如此——浮点非确定性、批处理效应以及跨提供商区域的硬件差异,即便在"确定性"配置下也会引入方差。在更高的 temperature 下,输出空间是真正随机的。
行为的改变不伴随 schema 变更。 一次模型更新可以在不改变任何字段名的情况下,改变输出的语气、冗长度、可靠性或事实准确性。语义化版本是为结构性破坏性变更而设计的,它没有词汇来表达"相同的 schema,幻觉率提高了 8%"。
失败模式是沉默的。 当传统 API 违反契约时,你会收到 4xx 或 5xx。当 LLM 支撑的服务违反语义契约时,它返回 200,带着看似合理的 JSON,却在下游悄然失败。在生产环境中,检测到这类问题的中位时间是数天,而不是数秒。
一个内容审核系统在三个提示词版本中分别返回 "MEDIUM"、"medium" 和 "Medium",已经悄悄三次破坏了调用方。这些破坏都没有触发任何告警。
你实际会遇到的四种失败模式
理解契约在实践中的断裂点,比理论框架更有用。以下是在生产系统中反复出现的四种模式:
模型更新导致的 Schema 漂移。 当你从一个模型版本升级到另一个时,输出结构可能会发生微妙变化——增加的冗长度将原本干净的 JSON 包裹在解释性文字中,字段顺序改变,之前始终存在的可选字段开始被省略。这些都不会出现在变更日志里。
提示词漂移的连锁反应。 看似纯粹是表述上的小改动——将"用有效的 JSON 回复"换成"始终返回可解析的 JSON"——会以重要的方式改变输出分布。研究表明,提示词更新是 LLM 系统生产事故的主要驱动因素。变更在你的版本控制系统中,但它对下游消费者的影响是不可见的,直到它显现出来。
静默的质量退化。 你的 API 返回格式正确、符合 schema 的 JSON,但内容是错误的。无论实际信号如何,置信度分数都聚集在 0.9。实体提取悄悄丢弃低置信度的实体而不是标记它们。这类失败不会触发任何 schema 验证;它需要语义监控。
多智能体管道中的组合失败。 智能体 A 和智能体 B 各自满足它们的契约。当 A 的输出进入 B 的提示词时,组合行为违反了一个任何单独测试都没有覆盖的契约。对 LLM 系统中 73+ 种不同契约类型的生产研究表明,组合失败是最难发现的。
提示词驱动 API 的语义化版本管理
传统语义化版本映射到结构变更:主版本用于破坏性 schema 变更,次版本用于新增变更,修订版本用于 bug 修复。当契约是 schema 时,这很有效。
对于 LLM 支撑的服务,契约包含 schema 版本管理无法捕获的行为属性。一个新模型在某 些输入上拒绝率提高 8%,对某些消费者来说是破坏性变更。一个提示词调整将输出语气从中性变为带有倾向性,对于将文字下游传递给用户的调用方来说是破坏性变更。这些都不会出现在 JSON Schema diff 中。
更实用的版本管理模型:
| 触发条件 | 版本升级 |
|---|---|
| 破坏性结构变更(字段删除、类型变更、新增必填字段) | 主版本 |
| 新增可选输出字段、准确性提升、延迟降低 | 次版本 |
| 错别字修复、格式调整、无语义影响 | 修订版本 |
| 相同 schema,影响可靠性或语义的行为变更 | 至少次版本;如需调用方更新行为则升主版本 |
关键的补充是最后一行。即便 JSON Schema 完全相同,导致拒绝率、幻觉频率或输出冗长度变化的模型升级也应该增加次版本号。调用方理应收到这个信号。
由此衍生出两条操作规则:
不可变性。 一旦版本发布,它就不会改变。v1.2.3 端点始终表现得像 v1.2.3——当你升级底层模型时,它不会悄悄改变行为。如果模型变了,版本就变了。这迫使你明确说明改变了什么,并给调用方一个稳定的目标。
版本化完整的执行上下文。 提示词版本不只是一个字符串,它是提示词 + 模型 + temperature + 检索配置的组合。当其中任何一个发生变化时,输出分布就会改变。把它们放在一起版本化,放在一起追踪。
