《互联网产品的 AI 架构·模型接入与能力边界》附录 B 能力契约与错误码速查

把卷一反复使用的两类产物压缩成可直接抄用的速查:能力契约模板(六面承诺、请求响应形状、预算与降级字段)与错误模型速查(九类错误、可重试判定、HTTP 映射、用户可见文案、记录字段);另附五种能力的契约对照、流式事件与结构化输出失败表,以及预算算式与契约评审检查点。

这份附录解决什么重复劳动

卷一的每一章都在做同一件事:把模糊的模型调用,改写成有边界的约定。

重复出现的产物只有两类:能力契约与错误模型。前者写清“承诺什么”,后者写清“做不到时怎么表达”。附录 B 把它们做成可直接抄用的模板。

模板不是标准答案。照抄能保证不漏项,但不能保证填得对。填不对的地方,恰恰是需要团队自己决策的地方。

一、能力契约模板

一份契约至少包含六面承诺:

能力标识:      chat.reply
版本:          v2
所有者:        消息团队
模型无关性:    业务只依赖本契约,不依赖模型 ID

输入:          会话摘要 + 最近 N 条消息 + 用户语气偏好
输出:          单段回复文本,长度上限 400 字
质量:          人工抽检通过率 >= 92%,安全拦截率 <= 0.5%
延迟:          首字 P95 <= 800ms,完整 P95 <= 4s
成本:          单次 <= 1200 微元,租户月度上限见配额服务
失败:          不可用时降级为“建议回复”候选,不阻塞输入框

六个字段里最容易空着的是质量与失败。延迟和成本通常有人负责,质量往往只写“效果好”,失败往往只写“报错”。

质量要落到可验收的指标上:抽检样本量、判定人、通过标准、复核周期。没有这些,上线后没人能回答“它到底行不行”。

失败要落到用户可见的行为上:入口是否隐藏、是否换成较弱结果、是否明确提示稍后再试。三者对应完全不同的产品体验。

二、请求与响应形状

契约的传输形状建议保持稳定,不随供应商变化:

请求:
  capability      能力标识,必填
  tenant          租户标识,由网关注入,业务不得伪造
  input           结构化输入,字段随能力而定
  constraints     可选,覆盖默认预算
    deadlineMs    本次完整时限
    maxOutputTokens
  idempotencyKey  可选,写操作与计费相关时必填
  trace           可选,请求链路标识

响应:
  output          结构化输出,字段随能力而定
  usage           输入/输出 token、金额微元
  model           实际使用的模型标识
  degraded        是否发生降级,以及降级等级
  requestId       用于对账与排障

三个字段值得单独说明。

tenant 由网关注入:业务请求里带租户只是一种声明,网关按凭证重新确定。否则客户端可以伪装成别的租户,配额与数据边界同时失效。

constraints 是覆盖不是放权:业务可以要求更短的超时,但不能超过平台上限。反过来,平台可以在业务不知情时收紧预算,响应里的 degraded 要如实反映。

usage 必须回传:没有用量的响应无法计量。计量缺失时,成本只能按调用次数估算,误差会随着模型切换迅速放大。

三、五种能力的契约对照

以「回声」的五项能力为例,对照六面承诺的填写差异:

能力输入重点输出约束质量指标失败姿态
chat.reply会话摘要 + 最近消息单段文本、400 字内人工抽检通过率降级为候选建议
search.rerank查询 + 候选列表只重排,不新增条目前 3 位命中率保留原顺序
content.summarize正文、目标长度要点列表,条数固定要点覆盖率返回截断原文
moderation.classify待审文本 + 策略版本类别 + 置信度 + 理由误杀率与漏放率转人工队列
support.assist工单上下文 + 知识片段建议回复 + 引用编号采纳率、引用准确率只展示知识片段

这张表能暴露一个常见错误:把“失败姿态”统一写成“报错”。五项能力的失败代价完全不同。重排失败保留原顺序几乎无感;审核失败转人工只是慢一点;回复生成失败如果直接报错,用户会以为整个输入框坏了。

四、九类错误

错误模型的价值在于分类,而不是码值本身。九类错误覆盖了卷一中出现的全部失败:

类别含义可重试HTTP典型原因
auth凭证无效或过期否401密钥轮换、租户禁用
quota配额耗尽否402月度额度用完、单次超限
rate-limit触发限流是,带退避429突发流量、共享池争用
timeout超过预算未完成是,有剩余预算时504长输入、模型拥堵
unavailable依赖不可用是503供应商故障、区域中断
invalid-request请求本身不合法否400字段缺失、超长、参数冲突
invalid-output模型输出不合约定是,有限次修复502结构漂移、截断
content-blocked内容被策略拦截否422安全策略、合规要求
provider供应商返回未归类错误视情况502私有错误码、协议异常

分类的关键是可重试与用户可解释两列。第一列决定代码路径,第二列决定产品文案。两者不一致时,故障处理一定混乱:例如把配额耗尽标成可重试,重试只会把额度耗得更快。

五、可重试判定

可重试不是“看起来像临时故障”,而是同时满足三个条件:

  1. 错误类别在可重试集合内;
  2. 剩余预算足够完成一次尝试;
  3. 请求是幂等的,或带幂等键且服务端能去重。

三个条件缺一不可。缺第一条会重试无效错误;缺第二条会让重试把延迟拖爆;缺第三条会在写操作上产生重复副作用。

退避策略建议:

场景初始间隔上限抖动
限流200ms2s全抖动
超时100ms1s等抖动
不可用300ms3s全抖动

上限要小于剩余预算。预算 3 秒、退避上限 3 秒,等于只允许一次重试,这个关系应该在代码里显式表达,而不是靠调参碰运气。

六、HTTP 映射与用户文案

对外 API 的 HTTP 状态码与内部错误类别是两层。映射保持稳定,但文案按场景调整:

内部类别HTTP面向开发者的文案面向终端用户的文案
auth401凭证无效,请检查密钥登录状态异常,请重新登录
quota402配额已用尽本月额度已用完
rate-limit429请求过于频繁,请稍后重试当前使用人数较多,请稍后再试
timeout504生成超时生成时间有点久,请重试
unavailable503依赖服务不可用该功能暂时不可用
invalid-request400请求参数不合法输入内容有误,请检查
invalid-output502输出不符合约定生成结果异常,请重试
content-blocked422内容被策略拦截内容不符合规范,请修改后重试
provider502供应商标识错误该功能暂时不可用

两列文案都要写。只写开发者文案,前端会临时想词;只写用户文案,排障时看不出真实原因。

七、响应中的降级字段

降级不是布尔值,而是分级:

等级含义用户感知记录字段
none正常无degraded=false
weaker换了较弱模型或更短输出结果质量下降degraded=true, level=weaker
cached返回缓存结果可能略旧degraded=true, level=cached
fallback走非模型兜底功能变简degraded=true, level=fallback
blocked明确不可用看到提示degraded=true, level=blocked

分级的意义在于复盘。只记录“降级过”,无法区分是换了模型还是返回了缓存,成本与质量的变化也就无法归因。

八、流式事件速查

流式响应建议只暴露五类事件:

事件时机必带字段客户端动作
start首帧requestId, model建立占位
delta每个增量text追加渲染
usage结束前输入/输出 token、金额记录用量
done正常结束finishReason收尾、解除禁用
error任意时刻失败kind, retryable按类别提示或重试

两条硬规则:

  • 首帧之后不自动重试。已经渲染的文本无法撤回,重试会产生重复内容。正确做法是把错误交给客户端,由用户决定是否重新生成。
  • 取消要向上游传播。客户端断开只是断开连接,上游可能仍在生成并计费。取消事件必须让网关通知供应商停止。

如果供应商的流式协议与这五类事件不同,在网关做转换,不要把供应商的私有事件名透给客户端。透传一次,以后换供应商就要改所有客户端。

九、结构化输出失败表

结构化输出的失败不是一种,处理方式也不同:

失败形态例子处理
非法 JSON缺少引号、尾逗号尝试修复一次,失败则重新生成
结构不符缺必填字段、类型错误带校验错误重新生成,最多一次
枚举越界类别不在允许集合内映射到 unknown,不重试
截断输出达到上限缩短输入或提高上限后重试一次
语义冲突字段之间自相矛盾放弃自动修复,转兜底
空结果结构合法但内容为空按能力定义处理,通常视作失败

修复预算要与总预算共享。常见错误是给修复单独设三次重试,结果一次请求变成四次调用,成本和延迟都翻倍。

十、预算计算速查

三个预算的常用算式:

输入 token 上限 = 模型上下文上限 - 输出上限 - 预留余量
单次成本上限 = 输入 token × 输入单价 + 输出 token × 输出单价
端到端预算   = 排队 + 首字 + 生成 + 后处理 + 网络余量

金额统一用整数微元,避免浮点误差。单价按百万 token 计,换算时先乘后除,不要先除后乘。

预算分配建议给后处理留固定余量,而不是按比例。后处理耗时与输出长度弱相关,按比例留会在短输出时不够用。

十一、契约评审的五个检查点

拿到一份契约,按顺序检查:

  1. 六面是否齐全:缺质量或失败的那一面,基本等于没写。
  2. 是否混入模型标识:契约里出现具体模型名,说明还没抽象干净。
  3. 错误是否分类:只有“失败”一个类别,无法决定重试与文案。
  4. 预算是否可拆:只有一个总超时,排障时无法定位卡在哪一段。
  5. 降级是否有等级:只有布尔值,复盘时无法归因。

五个检查点都能过,契约就可以进入实现;过不了的,先补齐再写代码。

十二、契约落地时的三个常见冲突

模板填完不等于能落地。评审中最常出现的冲突有三类:

冲突表现处理方式
质量与成本业务要更高质量,平台要压成本按能力分级:核心路径用标准档,边缘路径用经济档
延迟与安全审核要求先审后发,产品要求首字即出拆成两段:先出草稿,审核结果回来后再允许发送
契约与供应商供应商新特性无法通过现有契约表达先加可选字段并保持旧字段语义,验证后再升主版本

三类冲突的共同点:都不是技术问题,而是取舍问题。契约只能记录取舍结果,不能替团队做取舍。

落笔时把取舍写进字段,比写在会议纪要里可靠。例如“核心路径用标准档”应体现为能力标识或租户配置里的档位字段,而不是一句口头约定。

还有一类冲突容易被忽略:契约与现有客户端的冲突。新增可选字段通常安全,但改变默认值会悄悄影响老客户端。默认值一旦发布,就应当视为契约的一部分,修改它等同于破坏性变更。

因此版本演进的原则是:加字段不升主版本,改语义必须升主版本,删字段要经过弃用期。三条规则写进契约首页,比写在发布规范里更容易被看到。

小结

契约与错误模型是同一件事的两面:前者定义成功,后者定义失败。只写成功路径的契约,上线后会在第一次故障时暴露全部缺口。

模板可以直接抄,但六个承诺的取值必须自己填。填空的过程就是架构决策的过程:质量谁来验收、失败时用户看到什么、预算超了谁负责。这些问题的答案,比模板本身重要得多。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《互联网产品的 AI 架构·平台化与治理》附录 C 平台化评审清单
  2. 《互联网产品的 AI 架构·平台化与治理》附录 B 能力目录与配额字段速查
  3. 《互联网产品的 AI 架构·平台化与治理》附录 A 治理角色与职责速查