运营指南

同一句报错在不同上游含义不同:大模型API聚合层如何统一错误码与排查路径

多模型接入之后,上游错误码各说各话,同一个状态码在不同厂商处含义并不一致,排查、告警与重试策略都难以统一。这篇梳理聚合层的三层翻译机制:传输层归一状态码、语义层收拢错误枚举、建议层给出可否重试与切换判断,并保留原始错误与请求 ID,附一套可复用的排查顺序。

接入两三家大模型之后,很多团队才发现真正的麻烦不是模型效果,而是报错。同一个 HTTP 400,在 A 家是参数格式问题,在 B 家可能意味着上下文超长,在 C 家甚至只是模型名称写错。开发者拿着一段日志去搜,搜到的往往是另一家厂商的解释,排查方向从一开始就偏了。

上游错误码的差异是结构性的。有的厂商沿用 OpenAI 的 error.type 与 error.code 双层结构,语义清晰;有的把限流、余额不足、内容审核全部塞进 HTTP 200 的响应体,靠业务码区分;有的只给一句中文提示,连可编程的字段都没有。还有厂商用 429 配合 Retry-After 头表达限流,另一些则用 503 表达同样的意思。

这些差异会沿着链路放大。客户端要维护多套异常分支,监控系统无法用同一套规则聚合告警,重试策略更难统一:对某家上游该退避重试的错误,在另一家可能是重试也没用的硬错误。流式场景下更棘手,错误可能在已经吐出几百个 token 之后才以 SSE 事件的形式出现,此时重试意味着重复计费和重复输出。

聚合层的第一个动作是传输层归一。无论上游返回的是 200 带业务错误码,还是状态码与错误体各说各话,出口统一映射为标准 HTTP 语义:认证失败、参数错误、限流、上游不可用、超时分门别类。这一步解决的是机器能不能识别,让客户端不必为每家上游各写一套判断逻辑。

第二个动作是语义层翻译。把上游的几十种错误归拢成有限枚举,例如认证失败、额度不足、限流、上下文超长、内容被拦截、模型不存在、上游超时、上游 5xx。枚举数量不宜多,多了等于没归;也不宜少,少了会把重试有用和重试无用这两类错误混在一起。

第三步是给出处置建议。归一后的错误对象里通常还要带几个字段:是否可重试、是否已产生计费、是否建议切换通道、原始错误体是什么。尤其是是否可重试这一项,如果没有聚合层兜底,业务侧只能靠状态码猜,猜错的代价是放大上游压力,或者漏掉本可以自动恢复的机会。

原始错误必须保留。翻译不等于抹掉,raw_error 字段把上游原样返回的错误体、状态码、请求 ID 一并带上。排查时先用归一码判断大类,再用原始体确认细节,两者配合才能既快又不失真。请求 ID 的贯穿同样关键,从网关入口到上游返回应当是同一条链路,否则跨系统对账基本无从下手。

重试与降级依赖错误分类。非流式请求且幂等的前提下,限流类错误适合指数退避后重试,上游 5xx 适合直接切换备用通道,参数类错误重试没有意义。流式请求在已经输出内容之后一般不做透明重试,而是把错误事件如实下发,由业务自己决定是否重新发起一次调用。

计费口径也需要跟着错误码走。失败请求是否计费、部分成功如何结算,如果上游和聚合层各有一套说法,对账时很容易出现差额。通行做法是未产生有效 token 的失败请求不计费,并在账单里单独列出错误请求量,方便区分到底是上游抖动,还是自身调用方式有问题。

快米兔在这条链路上走的是统一出口的思路。平台兼容 OpenAI 协议,接入多家国产模型后对上层暴露一套错误结构与统一的请求 ID,换模型不需要改异常处理代码;多模型路由与通道健康检查把限流、超时这类高频错误在网关侧消化掉一部分,业务侧感知到的报错量会明显下降。平台注册送 5 元测试金、按量计费,错误请求的具体计费口径以官方说明为准,整体适合先小流量跑通链路再逐步放量。

回到排查本身,一条可复用的顺序是:先用请求 ID 定位是哪一次调用、走了哪条通道,再看归一错误码判断属于哪一类,接着看原始错误体确认上游的真实说法,最后确认重试与通道切换是否已经发生。顺着这条链路走,大多数报错能在几分钟内定位,而不是在多家文档之间来回翻找。