跳到主要内容

JSON Schema 校验通过了,但下游消费者因语义漂移拒绝了你的输出

· 阅读需 11 分钟
Tian Pan
Software Engineer

JSON Schema 验证的是输出的形状(shape)。它并不验证该形状内数值的含义。在长达 9 个月的时间里,你的 AI 流水线产生的每一条输出都顺利通过了校验,监控显示 Schema 合规率为 100%,你的团队也理所当然地认为符合 Schema 的响应在契约层面就是正确的。接着,一次模型升级发布了,每一条输出依然能通过校验,但你的 Slack 告警频道却在一夜之间从每天 50 条消息飙升到了 800 条。

Schema 没有出问题,出问题的是其内部数值的分布。这就是大多数 AI 团队在生产环境中发现的鸿沟:JSON 契约是一个类型系统(type system),而非行为系统(behavior system),而下游消费者一直依赖于某种契约从未被要求强制执行的数值分布。

故障模式

想象一个分流流水线。模型接收一个入境支持工单,并输出一个如下所示的 JSON 对象:

{
"category": "billing" | "technical" | "account",
"priority": "low" | "medium" | "high",
"summary": string
}

Schema 在集成边界被强制执行。校验层会拒绝任何不匹配的内容。下游服务消费这些对象并应用一条业务规则:任何 priority: high 的工单都会升级到值班(on-call)的 Slack 频道。

九个月来,历史比例大致为 70% 低优先级、25% 中优先级、5% 高优先级。值班频道每天大约会看到 50 次升级,这是一个单人可以处理的负荷。容量规划、告警路由和轮值表都是针对这一比例进行校准的。

模型在周二早上发布了升级。新模型对自己的判断更加自信。同样的提示词(prompt)现在在(低、中、高)分布上产生了 30/35/35 的比例。每一条输出依然符合 Schema 校验。每一个枚举值依然在允许的集合内。校验层报告零拒绝。推理团队的仪表盘显示的依然是维持了九个月的绿色。

周二,Slack 频道收到了 800 条消息。值班人员无法跟上进度。下游服务因“告警骚扰”被立案。故障根因花了一周时间才找到,因为没有人去排查生产者;生产者的契约显示它非常健康。

Bug 不在模型中。Bug 在于那个假设——即认为语法层面的契约足以约束语义层面的依赖。

为什么 Schema 无法捕捉到这一点

JSON Schema 在设计上就是对结构的描述。它说明了存在哪些字段、它们持有哪种类型以及允许哪些枚举值。它不会说明每个枚举值出现的频率、字段的联合分布,或是下游系统所依赖的相关性。

这并不是 JSON Schema 的缺陷。这是类型系统的边界。类型系统回答的是:这个值在允许的集合中吗?它不回答:允许集合中的数值比例是否与你昨天看到的保持一致?

当输出生产者是一段确定性的代码时,这个问题很少出现。确定性代码不会在你毫无察觉的情况下悄悄改变其输出分布。但模型会。模型的输出分布是其权重、提示词、训练数据以及推理时设置的函数。其中任何一项都可能改变。当它们改变时,Schema 无法察觉。

将 Schema 校验视为生产契约的团队,实际上签署了一份生产者可以随意修改而不违约的合同。提供者可以发布一次升级,改变每一个下游决策的含义,而契约不会发出警告,因为升级并没有改变契约的指代物。指代物永远是形状,而非含义。

漂移潜藏的三个地方

在结构有效的输出中,有三个地方会以 Schema 无法捕捉的方式发生可靠的漂移。

枚举比例偏移 (Enum mix shift)。 模型开始产生不同比例的允许枚举值。上述例子就是这种情况。下游系统有一条基于特定数值的业务规则,而该规则的影响范围(blast radius)对该数值出现的频率非常敏感。

字段相关性偏移 (Field correlation shift)。 单个字段孤立看都正常,但联合分布发生了变化。旧模型产生 category: billing 配对 priority: high 的概率是 2%。新模型产生这种配对的概率是 18%。一个按类别进行键分区(key-partition)的下游队列路由规则是针对旧的联合分布设计的,现在 billing 分区过热。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates