跳到主要内容

AI 流水线的契约测试:组件间 Schema 校验的交接规范

· 阅读需 11 分钟
Tian Pan
Software Engineer

大多数 AI 流水线故障并非模型问题。模型运行正常,输出看起来也是 JSON,但下游阶段却悄然崩溃——原因可能是字段被重命名、类型发生变化,或者嵌套对象新增了一个下游阶段根本不知道如何处理的必填属性。流水线执行完毕并报告成功,而某个数据仓库里的数字已经悄悄出错。

这就是 AI 流水线的契约测试问题,也是生产 AI 系统中最被忽视的可靠性风险之一。根据近期基础设施基准数据,企业 AI 系统平均每月发生近五次流水线故障,每次解决耗时超过十二小时。主要原因并非模型质量差,而是数据质量和 Schema 契约违规:64% 的 AI 风险存在于 Schema 层。

隐性契约问题

当你将 AI 组件串联起来——提取阶段的输出喂给分类阶段,分类阶段的输出再喂给排序阶段——你实际上在每对组件之间隐式地创建了生产者-消费者契约。阶段 A 承诺返回某种结构,阶段 B 依赖该结构,但这份契约除了解析输出的代码之外,根本不存在于任何地方。

在传统微服务领域,这一问题已有充分认识。消费者驱动的契约测试(由 Pact 等框架首创)通过让下游消费者声明其对上游提供者的需求,再验证提供者是否真正满足这些需求来解决问题。契约存储在共享的代理中,双方可独立地针对契约进行测试。

在 AI 流水线中,同样的问题存在,但有一个让它更加危险的变数:提供者是语言模型,其输出格式可能以无人刻意改变的方式发生漂移。升级基础模型、更新系统提示、调整温度参数——JSON 结构可能突然将原来的 userId 换成 user_id,或者当结果只有一条时,items 从数组变成了对象。

与 API 重大变更会抛出 422 并大声报错不同,AI 流水线的 Schema 漂移往往产生能够正常解析的输出。下游阶段在期望字符串的地方得到 None,或在期望标量的地方得到单元素列表。这个 bug 传播了好几跳才导致任何东西崩溃,而此时追溯到模型输出变化的因果链已经断裂。

为何传统契约测试无法直接套用

核心挑战在于 AI 输出具有概率性。你无法编写一个断言"模型输出恰好是这段 JSON"的契约测试并在 CI 中运行。模型会产生变体。温度、上下文长度、提示措辞以及采样固有的随机性都会在有效 Schema 范围内产生合理变化。

这打破了传统契约测试的两个基本假设:

精确相等断言从设计上就是错误的。 检查输出 A 等于预期输出 B 的测试在每次运行时都会失败,除非你检查的是结构和类型——而非值。你测试的是信封的形状,而不是里面的信。

提供者"回放"测试失效。 传统消费者驱动的契约测试通常使用录制的响应回放给消费者。对于 AI 组件,没有可录制的规范响应,你需要针对可能输出的分布进行测试。

所需的调整微妙但重要:AI 流水线契约测试的是结构保证,而不是值保证。有效契约断言:

  • 字段 sentiment 存在且为 ["positive", "negative", "neutral"] 之一
  • 字段 confidence 是 0 到 1 之间的浮点数
  • 字段 entities 是对象数组,每个对象包含 text: stringtype: string

契约不对出现哪个情感值或置信度分数做任何断言。那是模型的工作。契约的工作是确保下游阶段能安全地解析到达的数据。

AI 流水线设计的契约优先方法

最可靠的 AI 流水线会在编写提示或下游消费者之前先定义契约。这颠倒了典型的设计顺序——传统上是先提示模型,观察输出,再编写代码处理返回的内容。

首先在共享位置定义输出 Schema。 使用 JSON Schema、Pydantic(Python)或 Zod(TypeScript)正式规定阶段 A 必须产出什么,才能让阶段 B 正常工作。这个 Schema 就是契约。

使用结构化输出在 token 级别强制执行契约。 现代 LLM 提供商——OpenAI 自 2024 年 8 月起、Anthropic 自 2026 年初起、Google 自 2024 年底起——均支持受限解码,将 JSON Schema 编译成有限状态机并在采样时应用。模型无法产生违反 Schema 的输出。这将"请输出看起来像这样的 JSON"的尽力提示指令变成了硬性保证。实际差异巨大:朴素的 JSON 提示在生产负载下失败率为 15–20%;受限解码将其推近零。

在每个阶段的接收边界处接入校验。 即使你信任上游阶段的结构化输出强制执行,也要在接收处校验。Pydantic 的 model_validate() 和 Zod 的 parse() 等库在违反契约时会抛出类型化异常。这是防范模型升级、提示变更或配置漂移绕过结构化输出强制执行的最后保障。

对 Schema 而非模型进行版本管理。 Schema 版本应该是显式的——存储在注册中心,由生产者和消费者双方引用。当阶段 A 的 Schema 变化时,递增版本号,更新消费者,并在部署任何一方之前运行完整的契约测试套件。

AI 流水线阶段的消费者驱动测试

消费者驱动模式天然适合 AI 流水线。下游阶段(消费者)知道自己需要什么,上游阶段(提供者)应验证其确实在交付。

具体实现方式如下:

消费者声明契约。 阶段 B 定义一个 JSON Schema(或 Pydantic 模型),涵盖其访问的每个字段、每个字段的预期类型、是否必填或可选,以及任何值约束(枚举、范围、模式)。这份声明就是契约。

契约发布到共享注册中心。 无论是 Pact 代理、Git 仓库还是简单的制品存储,形式不重要。契约需要让提供者测试套件可以访问。

提供者测试套件针对契约进行验证。 测试运行器反复调用阶段 A——使用一批有代表性的输入——并针对每个已注册的消费者契约验证每个输出。每当阶段 A 发生变化时在 CI 中运行:模型、提示、结构化输出 Schema 或上下文构建逻辑的任何变化。

失败信息明确无歧义。 如果阶段 A 的输出不再满足阶段 B 的契约,提供者测试套件会在变更发布前捕获它,而不是在三跳之后、三天之后。

"有代表性的输入"这一点值得展开。由于 AI 输出存在变化,你需要足够多的样本以统计学方式发现故障。每个测试用例至少运行五到十次,并验证 Schema 契约在所有运行中均成立。即使 20% 的时间出现 Schema 违规,也是一个等待爆发的生产事故。如果正确使用受限解码,Schema 违规率应为零。任何高于零的违规率都表明存在配置问题——可能是受限解码实际上并未启用。

集成测试框架

除了每阶段的契约测试,端到端流水线测试还验证整个链路是否协同正常。在生产环境中有效的模式:

追踪级别的校验。 针对固定测试语料库运行完整流水线,并在每个中间阶段而不仅仅是最终输出处验证输出 Schema。这能捕获阶段 A 满足其单独契约但下游阶段 C 失败的问题——因为阶段 B 执行了以无文档方式改变 Schema 的转换。

部署时的 Schema 兼容性检查。 在部署任何流水线阶段的新版本之前,运行自动化兼容性检查:新版本的输出 Schema 是否满足所有已注册的下游消费者契约?如果不满足,阻止部署。这是防止契约破坏进入生产环境的 CI 关卡。

Schema 健壮性的突变测试。 刻意引入 Schema 突变——重命名字段、更改类型、删除必填字段——并验证下游校验能立即捕获。这确认你的契约执行确实有效,而不是假设它有效。

版本化快照测试。 录制一批生产输入及其对应的流水线输出(针对当前契约验证通过)。当流水线变化时,再次运行这批输入并对比输出 Schema 的差异。Schema 级别的差异——而非值级别的差异——才是信号。某个字段在 85% 的记录中出现,现在只在 60% 的记录中出现,在实践中是重大变更,即使在理论上不是。

何时使用 Schema 注册中心

对于三个或更多阶段的流水线,或超过五名工程师的团队,非正式的契约管理方法会失效。Schema 注册中心通过以下方式解决这个问题:

  • 所有阶段输出 Schema 的单一事实来源
  • Schema 更新时的兼容性强制执行(向后、向前或完全兼容模式)
  • 可发现性,让构建新消费阶段的工程师能找到现有 Schema
  • 审计历史,记录变更内容和时间

dbt contracts(用于数据流水线阶段)、Confluent Schema Registry(用于事件驱动流水线)以及 Git 中的自定义 JSON Schema 注册中心等工具都能满足这一需求。关键原则是:任何阶段都不得在未先更新注册中心并验证所有下游消费者仍然兼容的情况下更改其输出 Schema。

跳过这一步的代价

使跳过契约测试尤为危险的失败模式是它会悄然恶化。阶段 A 的 Schema 漂移不会立即破坏阶段 B——它会在某类输入上让阶段 B 悄悄得到 None 而非期望的值,以默认值处理,产生微妙错误的输出。这传播到阶段 C,产生略有偏差的排序。最终用户看到的结果质量略有下降。没有错误抛出,没有告警触发,这种退化只能通过需要数周积累足够信号的业务指标才能发现。

当退化出现在 KPI 复盘中时,因果链已经断裂。模型在三周前更新,此后已有四个提示发生变化,还有两次基础设施变更。将退化归因于阶段 A 的 Schema 契约违规——而非模型更新与提示变更的某种组合——在没有契约测试失败信号的情况下几乎不可能,而那个信号本应在第一天就捕获问题。

为现有 AI 流水线添加契约测试的工程成本很低,而在生产环境中调试静默 Schema 漂移的成本很高。这是 AI 工程中最容易证明投资价值的可靠性投入之一。

结论

AI 流水线的契约测试规范从微服务领域借鉴了正确的模式——消费者驱动的契约、Schema 注册中心、CI 中的提供者验证——并通过聚焦结构保证而非值相等,将其适配到概率性系统。现代 LLM 提供商的受限解码使在生成时强制执行这些结构保证变得可行。剩下的是组织纪律:在提示之前定义 Schema,在编写生产者之前发布消费者契约,并在每个涉及阶段模型、提示或上下文构建的 CI 流水线中运行提供者验证。

采纳这一模式的团队报告称,Schema 层故障——此前是生产事故的沉默多数——变得可见,并可在发布前被捕获。这种可见性是将 AI 流水线可靠性与 API 可靠性同等对待的第一步:作为一个有已知解决方案的工程问题,而非概率性谜题。

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