多模型 API 中转错误码统一封装:让上游五花八门的报错变成一套可判断的信号
接入多家模型后,上游错误结构不统一往往比价格更折磨人:业务层堆满字符串判断,排障要翻好几份文档。本文拆解中转层错误码统一封装的分层思路、重试与计费口径、限流信息透出方式,并给出先定错误字典再改网关的落地顺序,帮助团队只写一套处理逻辑。
把模型接进业务只是第一步。真正让人头疼的,是上游返回的错误信息没法直接用。同一套业务代码,调用不同厂商的模型时可能收到三种完全不同结构的报错:有的用 HTTP 状态码表达,有的在 body 里塞一个 code 字段,还有的只回一段自然语言文本,调用方只能靠字符串匹配去猜刚才发生了什么。
这种混乱会在两个地方被放大。一是业务层被迫堆满 if 判断,换一次模型就要改一遍代码,回归测试的成本跟着涨;二是线上排障时日志里全是各家格式不同的原始报文,定位一次失败请求要翻好几份文档。错误码统一封装看上去是网关层的小活,实际上是决定接入效率和长期维护成本的基建,早做比晚做便宜得多。
常见的两种偷懒做法都不太可取。一种是原样透传上游响应,把兼容成本完整丢给调用方;另一种是无论什么错都归一化成 500,调用方彻底失去判断力,该重试的没重试,不该重试的疯狂重试,最后把配额和账单一起烧掉。两种做法都会让故障从「可定位」退化成「靠猜」。
统一封装要服务四个目标:可判断、可重试、可计费、可审计。可判断是指调用方拿到响应就知道属于哪一类问题;可重试是指能明确区分临时故障与永久失败;可计费是指成功与失败的量口径清晰、不扯皮;可审计是指事后能从日志回溯到具体上游、具体请求与具体 Key。四个目标定下来,后面每个字段该不该加才有判断依据。
比较稳妥的做法是按层归类。传输层负责连接超时、DNS、TLS 之类的网络问题;鉴权与配额层处理 Key 无效、余额不足、权限不匹配;限流层单独表达速率与并发超限;模型服务层对应上游 5xx、模型不存在、上下文超长;内容层则覆盖安全拦截与审核不通过。每一层给出稳定的语义码,上游怎么变,对外的承诺不变,业务代码只需要认识这一套。
编码规范上有几个细节值得提前定死。对外只暴露一层语义码,把上游原始的错误码、请求 ID 与原始 message 挂在 detail 或 upstream 字段里,用于排障但不参与业务逻辑判断;响应结构尽量向 OpenAI 的错误格式对齐,这样已有的客户端、SDK 和中间件基本不用改。字段命名一旦发布就当作接口契约,宁可新增也不要随意改名。
重试语义要单独表达,不能只靠状态码。建议给每个语义码配一个是否可重试的标记,并在限流、过载这类错误上返回建议等待时间,让调用方做指数退避而不是原地死循环。同时要区分幂等与非幂等请求,生成类接口重试之前最好确认上游是否已经计费,避免重复扣量与重复内容,这类问题在流量高峰期特别容易出现。
计费口径是错误封装里最容易踩坑的部分。超时到底算不算失败、流式响应中断后已生成的部分如何结算、上游报错但已经消耗了 token 怎么办,这些都要在网关层给出明确约定并写进文档。对账时错误码还能当筛选维度用,快速判断是某家上游整体异常,还是个别 Key 的权限配置问题,排查效率差别很明显。
限流类错误值得单独说。调用方最需要的不是一句「请求太频繁」,而是剩余额度、重置时间、当前并发占用这些可行动的信息。把这些字段放进错误响应,客户端就能自己算出下一次请求的合适时间点,而不是盲目退避或者直接放弃。配额提示做得细,前端体验和整体吞吐都会跟着改善。
快米兔 API 在这块的思路是保持 OpenAI 兼容的接入方式,把多模型路由和上游差异收敛在中转层,对调用方暴露相对一致的成功与错误结构,Key 管理与配额也放在同一套体系里。它对外的资费说明是注册送 5 元测试金、按量计费,具体错误码字段与限流参数以官方说明为准。对多数团队来说,把适配复杂度交给中转层处理,比自己维护一张会持续变化的映射表更省事。
落地顺序建议是先写错误字典再改代码。把业务里所有可能触发的失败场景列出来,逐一映射到统一语义码,然后改造网关、做一轮注入式压测,最后灰度切换并观察一段时间。上线后按上游维度统计错误码分布,谁家 5xx 偏多、谁家限流频繁一目了然,扩容、切换或谈判也就有了依据。选型时不妨把错误处理当产品能力来看,能把这层做扎实的服务,后续接入新模型时通常也更省心。
