AI agent 工具 Schema 演进与兼容策略:升级参数、版本和解析逻辑时怎么不打断旧 Run

HTMLPAGE 团队
21 分钟阅读

AI agent 工具不会一直不变。本文讲清 Schema 演进、兼容读取、双写双读、版本协商和回滚策略,避免旧 Run 被新工具升级打断。

#AI agent #Schema 演进 #工具兼容 #工程实践

AI agent 工具一旦进入真实业务,就不会停留在第一版 schema。字段会增加,命名会调整,枚举会拆分,某些参数会从可选变成必填。问题不在于升级本身,而在于升级经常发生在系统仍然有旧 Run、旧 prompt、旧回放样本、旧评测集的情况下。

很多团队第一次踩坑,都不是因为升级失败,而是因为升级“部分成功”:新请求能跑,旧回放开始报错,某些长任务在中途继续执行时突然读不懂新结构。看起来像偶发 bug,本质却是 schema 演进策略缺位。

建议先结合 AI Agent 工具注册表治理AI agent 工具结果标准化AI agent 灰度发布与功能开关AI Agent Session Replay 调试指南 一起看。

先给结论:schema 演进要同时照顾 4 类消费者

消费者为什么会受影响
新请求需要理解新字段和新规则
旧 Run 恢复可能还带着旧参数继续执行
回放与评测历史样本需要按旧版本复现
观测与审计日志、告警、报表要读懂新旧结构

如果你只关心“线上新请求能否跑通”,schema 升级一定会在别处留下坑。

一、先分清三种 schema 变化

不是每种变更都同样危险。至少分三类:

变化类型示例风险等级推荐策略
向后兼容新增可选字段直接发布,但保留默认值
条件兼容字段改名、枚举扩展双写双读,灰度迁移
破坏性变更必填新增、结构重组新旧版本并存,逐步切换

最危险的是把破坏性变更伪装成“只是字段改了一下”。

二、工具 schema 不要只存一份“当前版本”

很多系统的工具定义只有当前 schema。这样做的后果是:历史 Run 无法知道自己当时到底用的是哪版结构。

更稳的做法是把版本作为一等公民:

{
  "toolName": "createDraft",
  "schemaVersion": "v2",
  "status": "active",
  "compatibility": {
    "reads": ["v1", "v2"],
    "writes": ["v2"]
  }
}

这里有两个关键点:

  • 工具当前写什么版本
  • 工具还能读什么版本

如果只记录 current=v2,恢复旧任务时系统仍然不知道该如何解释旧 payload。

三、双写双读是过渡期的主策略

当 schema 从 v1 升级到 v2,最稳的过渡不是强切,而是一个短暂的双写双读窗口:

阶段
阶段 1v1v1
阶段 2v1 + v2v1 + v2
阶段 3v1 + v2v2
阶段 4v2v2

这意味着:

  • parser 能同时解释 v1 和 v2
  • 新产生的 artifact 可以暂时同时保存旧结构和新结构
  • 回放和恢复不会立刻断掉

双写双读看起来麻烦,但比半夜修历史 Run 要便宜得多。

还有一个经常被忽略的现实问题:双写窗口如果没有明确结束条件,就会无限期存在,最后把系统拖成“什么都兼容、什么都不敢删”的状态。因此每次进入双写双读前,就要先定义退出门槛,例如:

  • 新版本写入占比连续 7 天超过 95%
  • 历史回放样本全部通过 v2 adapter
  • checkpoint 恢复在新旧版本下都通过演练
  • 审计报表已经切换到新字段口径

只有退出条件提前写清,双写双读才是迁移手段,而不是永久负债。

四、改名和重组时,最好显式提供 compatibility adapter

比如原来的参数是:

{ "invoiceId": "inv_123" }

新版本改成:

{
  "invoice": {
    "id": "inv_123",
    "country": "CN"
  }
}

不要指望模型自己猜旧字段如何映射到新结构,而应该在工具层提供 adapter:

function normalizeCreateInvoiceInput(input: unknown): CreateInvoiceV2Input {
  if (isV2(input)) return input

  if (isV1(input)) {
    return {
      invoice: {
        id: input.invoiceId,
        country: 'CN'
      }
    }
  }

  throw new Error('unsupported schema version')
}

兼容逻辑应该放在工具边界,而不是 prompt 里。prompt 只能影响新生成的参数,不能修复历史数据。

如果工具输入不只来自模型,还来自工作流引擎、批量回放脚本、人工修复控制台,那么 adapter 最好输出一份统一的“解析结果”,而不是仅仅返回业务对象:

interface ParsedToolInput<T> {
  schemaVersion: 'v1' | 'v2'
  normalized: T
  migrated: boolean
  warnings: string[]
}

function parseCreateInvoiceInput(input: unknown): ParsedToolInput<CreateInvoiceV2Input> {
  if (isV2(input)) {
    return {
      schemaVersion: 'v2',
      normalized: input,
      migrated: false,
      warnings: []
    }
  }

  if (isV1(input)) {
    return {
      schemaVersion: 'v1',
      normalized: {
        invoice: {
          id: input.invoiceId,
          country: 'CN'
        }
      },
      migrated: true,
      warnings: ['country defaulted to CN for legacy payload']
    }
  }

  throw new Error('unsupported schema version')
}

这样做的价值是:后续日志、审计和告警可以清楚知道本次执行到底是原生 v2,还是从旧结构迁移过来的“兼容成功”。

五、版本协商最好显式化,不要靠隐式猜测

不少系统把“schemaVersion”藏在文档或者注释里,实际运行时靠 parser 猜。短期能跑,长期会让行为越来越不可解释。

更稳的方式是把版本协商写成明确协议:

{
  "toolName": "createDraft",
  "requestedSchemaVersion": "v2",
  "acceptedSchemaVersions": ["v1", "v2"],
  "selectedSchemaVersion": "v1",
  "adapterVersion": "2026-05-07",
  "selectionReason": "checkpoint_resume_from_legacy_run"
}

这个结构至少解决 3 个问题:

  • 新请求为什么没有直接走最新版
  • 恢复任务当前到底按哪版协议执行
  • 故障复盘时该看哪套 adapter 和测试基线

很多团队的问题不是没有版本,而是版本只存在于代码里,不存在于运行事实里。

六、长任务恢复时,schema 版本必须跟着 checkpoint 走

一个常见误区是:任务恢复时重新读取“当前最新 schema”。这会让长任务在中途切换协议。

恢复信息至少要保存:

{
  "runId": "run_123",
  "toolName": "createDraft",
  "schemaVersion": "v1",
  "checkpointId": "cp_03",
  "inputSnapshot": { "invoiceId": "inv_123" }
}

恢复时先按当时的 schemaVersion 解释 payload,再决定是否需要迁移。不要让“继续执行”变成“顺便升级协议”。

七、兼容矩阵要成为发布门禁,不只是文档附件

最容易被低估的是测试范围。schema 演进不是单测改过就算结束,真正需要验证的是“谁写、谁读、何时恢复、如何回放”的交叉组合。

一个最小兼容矩阵通常至少包括下面这些维度:

写入方读取方运行场景预期结果
v1v1 parser老任务正常执行通过
v1v2 parser + adapter历史 Run 恢复通过并产出迁移告警
v2v1 parser回滚后读取新数据明确失败或受控降级
v2v2 parser新请求实时执行通过
v1 checkpointv2 runtime断点恢复不丢上下文,不重复副作用
v2 artifactreplay loader历史回放通过并保持结果可解释

如果发布单里没有这张表,团队就很容易只测“新写新读”,完全漏掉恢复与回放。

更进一步,兼容矩阵最好直接落到 CI 或回归脚本,而不是贴在文档里就算完成:

const compatibilityCases = [
  { writer: 'v1', reader: 'v2', scenario: 'resume' },
  { writer: 'v2', reader: 'v2', scenario: 'replay' },
  { writer: 'v2', reader: 'v1', scenario: 'rollback-read' }
]

for (const testCase of compatibilityCases) {
  expect(runCompatibilityCase(testCase)).toMatchObject({ ok: true })
}

这里的重点不是测试代码写成什么样,而是兼容性要从“人为记得测”变成“系统强制测”。

八、回滚不是只切 flag,还要考虑历史数据可读性

如果你发布 v2 后发现有问题,回滚时最容易漏掉两件事:

  • 已经写出的 v2 数据,旧版本是否还能读取
  • 旧 prompt 是否会继续生成 v1 风格参数

一个更完整的回滚清单是:

  1. 关闭新 schema 的写入。
  2. 保留对新写入数据的兼容读取。
  3. 暂停相关回放任务,避免读写混乱。
  4. 标记受影响的 Run 和 artifact。
  5. 把失败样本加入回归集。

九、上线后要盯兼容指标,而不是只盯成功率

很多 schema 变更上线后表面成功率没掉,但兼容成本正在悄悄抬高。真正值得盯的不是单一错误率,而是这些迁移相关指标:

指标为什么要看
旧版本 payload 占比判断双写窗口是否可以收口
adapter 命中率判断历史数据依赖面是否仍然很大
adapter 失败率发现未覆盖的旧结构
checkpoint 恢复成功率判断长任务是否被版本切换打断
replay 兼容通过率判断审计和复盘链路是否可用
迁移告警量判断是否出现默认值兜底过多

如果只看“接口 200 比例”,你会错过很多已经在恢复链路里发生的结构性问题。

建议给每次兼容解析打上统一日志:

{
  "event": "tool_schema_parse",
  "toolName": "createDraft",
  "selectedSchemaVersion": "v1",
  "adapterVersion": "2026-05-07",
  "migrated": true,
  "warningCount": 1,
  "runId": "run_123"
}

这类日志不是为了“多记一点”,而是为了在双写窗口结束前,知道系统到底还有多少历史包袱没有处理完。

十、失败案例:字段改名后,回放全挂但线上新请求正常

一个审核 Agent 原来写 reviewerId,后来统一成 reviewer_id。线上新请求没问题,因为 prompt 和新 parser 一起更新了。但历史 replay 还保留旧事件,回放平台直接报解析失败。

更糟的是,团队一开始没意识到这算生产问题,因为“真实流量没报错”。直到事故复盘需要回放上周 Run,才发现关键样本全读不出来。

修复方式不是把回放平台临时兼容一下,而是:

  • 给工具 registry 加 schemaVersion
  • replay loader 支持旧事件 adapter
  • 任何字段改名都必须附带 compatibility test

十一、把迁移节奏写进发布单,避免团队各自理解

一个成熟的 schema 演进流程,通常不是一次发布,而是至少 4 步:

  1. 先发布新 parser 和 adapter,只增加读取能力。
  2. 再灰度新写入,让少量新请求产出 v2 数据。
  3. 观察恢复、回放、报表、审计是否稳定。
  4. 最后关闭 v1 写入,确认收口窗口和删除计划。

这 4 步如果没有明确负责人,现实里往往会演变成:开发以为已经切完,数据团队还在读旧字段,运维以为双写是临时,最后没人敢删。

因此发布单里最好明确写出:

项目负责人退出条件
parser 双读平台工程历史回放通过率 100%
v2 灰度写入业务开发新写入占比 > 95%
审计口径切换数据/风控报表字段完成对齐
删除 v1 写入平台工程连续一周无兼容告警

当 schema 演进开始被当成跨团队发布节奏管理,它才真的从“代码改动”升级成“协议治理”。

十二、Schema 演进 Checklist

  • 是否明确区分向后兼容、条件兼容和破坏性变更
  • 工具 registry 是否记录 schemaVersion
  • 运行时是否显式记录 selectedSchemaVersion 和 adapterVersion
  • parser 是否支持双读或 adapter
  • 是否有写入方 x 读取方 x 场景的兼容矩阵测试
  • 长任务 checkpoint 是否保存版本信息
  • 回滚后是否还能读新旧数据
  • 是否监控旧版本占比、adapter 命中率和恢复成功率
  • 回放和评测样本是否跑过兼容测试
  • 变更是否附带灰度和关闭条件

结语

AI agent 的工具 schema 演进,不是“改个 JSON”这么简单。真正要治理的是版本、恢复、回放、审计和回滚。只有把 schema 升级当成系统协议演进,而不是单次代码改动,工具体系才不会越长越脆。

延伸阅读: