Skip to content

版本、契约测试与平台差异 ​

“只加字段”也可能破坏消费者 ​

若旧 schema 设 additionalProperties=false,增加字段会使旧消费者拒绝;若新增必填字段,旧生产者又无法提供。因此兼容性是生产者与消费者的关系,不是把版本号加一。版本信息应在 envelope 中明确,业务字段定义与工具返回值分别管理。

Python 3.12 保存 migration.py,运行观察一个有损迁移为何必须拒绝:

python
def upgrade_v1(v):
    if set(v)!={'quantity'} or type(v['quantity']) is not int:
        raise ValueError('invalid v1')
    return {'version':2,'quantity':v['quantity'],'unit':'book'}
def downgrade_v2(v):
    if v['unit']!='book':raise ValueError('cannot represent v2 unit in v1')
    return {'quantity':v['quantity']}
assert downgrade_v2(upgrade_v1({'quantity':2}))=={'quantity':2}
try:downgrade_v2({'version':2,'quantity':2,'unit':'box'})
except ValueError:pass
else:raise AssertionError('lossy conversion accepted')
print('compatibility passed')

v1 的 quantity 隐含单位为本;v2 增加 box 时,直接删除 unit 会改变业务含义。迁移前先记录隐含默认值,再明确哪些新值无法降级。维护旧消费者不需要先改写所有调用,但必须对不能表达的输入返回可识别错误。

平台原生形状要保留 ​

Responses 的工具意图是 function_call,回传 function_call_output 与 call_id;Claude 用 assistant tool_use 和 user tool_result;Gemini 用 function_call/function_response parts,并可能携带额外签名信息。统一内部动作时保留 provider metadata,回传通过原平台适配器构造,不把三个 JSON 形状混用。

结构化文本与函数参数约束并非所有模型都可同用。能力矩阵记录具体模型与 SDK 版本、支持的 schema 子集、是否允许并行工具、拒绝形状及截断信号。版本更新先跑同一契约样本:缺键、额外键、nullable、非法枚举、超范围、拒绝和部分结果。

真实平台练习按 OpenAI、Claude、Gemini 的完整入口执行;本章的版本转换实验只证明本地契约,不声称云端跨平台都通过。

评估执行链而非只评文本 ​

分别统计工具选择正确率、参数通过率、授权拒绝正确率、副作用次数和最终答案依据。一个回答文字正确但重复扣库存的链路必须失败。契约测试使用确定输入,线上评估再测模型的不确定选择;不要用全是合法参数的样本证明校验器有效。

审计事件将模型 call_id、业务幂等键和版本串起来。灰度期间新旧 schema 并行解码,写入只走选定版本;回滚保留旧解码器直到旧消息消费完。异步队列内的旧消息也是维护范围,不只考虑此刻的 HTTP 请求。

练习 ​

假设 v2 支持 unit=box,并规定一箱 10 本。能否直接在 downgrade 中乘 10?指出需要的业务前提,并给出只接受 book 的保守测试。

先记录自己的输出与解释,再核对本章答案。

用心记录每一步成长