New API 渠道测试失败:四层排查法十分钟定位问题
在网关后台加了一个上游渠道,点测试,红了。报错通常就一句话,比如”请求失败""连接错误""渠道不可用”,看不出任何有用信息。
这时候大多数人的反应是回去把配置从头看一遍,看不出问题就换个模型再试,还不行就重启容器,再不行去搜别人有没有遇到过一样的报错。这个过程可能花掉一下午,而最后发现原因是 key 复制时多带了一个换行符。
问题不在于这些原因有多难,而在于它们横跨四个完全不同的层次:网关那台机器到上游的网络能不能通、地址和路径拼得对不对、凭证有没有被上游认下、请求里的模型名上游认不认识。这四层里任何一层断掉,网关给你的都是那句差不多的红字。乱查就是在四个层次之间来回跳,查完一遍还不敢确定哪层已经排除了。
按层查就快得多。下面这套顺序我用了很久,正常情况下十分钟内能把问题锁到某一层。本文只讲方法,不涉及具体后台的菜单位置和字段名称——那些各版本会变,一律以你手上后台页面的提示和官方文档为准。方法本身跟界面长什么样没关系。
为什么排查顺序比排查技巧更重要
先说清楚这套顺序的设计逻辑,理解了逻辑你自己也能推出来。
四层之间是有依赖关系的:网络不通,鉴权无从谈起;地址不对,key 再正确也没用;地址和鉴权都对了,模型名写错才会暴露出来。所以从底往上查,每查完一层,上面的层次都还在待定状态,但下面的层次已经被永久排除了。这就是”按层”的全部价值——单调地缩小范围,不回头。
反过来从上往下查就不行。你先怀疑模型名,改了个模型名再测,还是失败,你没法判断是新模型名也错了,还是问题根本不在这一层。信息量为零,时间白花。
四层的顺序是:
| 层 | 问什么 | 典型症状 |
|---|---|---|
| 1. 网络与上游本身 | 网关那台机器能不能直接调通上游 | 超时、连接被拒、DNS 解析失败 |
| 2. 地址与路径 | base_url 与实际请求路径拼出来对不对 | 404、返回一段 HTML 而不是 JSON |
| 3. 鉴权 | 上游认不认这个凭证 | 401、403 |
| 4. 模型标识符 | 请求里的模型名上游认不认识 | 模型不存在,有时候长得像权限错误 |
第一层:先绕过网关,在网关那台机器上直接 curl 上游
这一步是整套方法的核心。做完它,问题被一刀切成两半:要么是上游或凭证的问题,要么是网关配置的问题,中间没有灰色地带。
做法是登录到网关所在的那台机器(容器化部署就进到容器里,或者在宿主机上执行,两者可能有区别,后面会说),用最原始的 curl 直接打上游的接口,带上你准备填进网关的那把 key,请求你准备用的那个模型。不经过网关的任何一行代码。
结果只有两种:
- curl 通了。 那么上游服务正常、这台机器出得去、这把 key 有效、这个模型名上游认识。这四件事一次性全部确认。剩下的问题百分之百在网关侧的配置上——你怎么填的、网关怎么拼的。
- curl 不通。 那么问题在网关之外,继续调网关的配置是纯浪费时间。此时按 curl 返回的具体错误往下走第二、三、四层,但排查对象是你的 curl 命令和上游,不是网关。
为什么必须在网关那台机器上执行
这句话是这一步的关键,很多人做了 curl 但做错了地方,在自己笔记本上跑通了就以为上游没问题,然后回头继续折腾网关配置。
你本地和网关服务器是两个完全不同的网络环境,至少有三处可能不一样:
出网策略。 生产服务器通常挂在有出方向限制的网络里——安全组、防火墙规则、云厂商的 NAT 网关配置。很常见的一种情况是这台机器只被放行了少数几个目的地址,你新加的那家上游不在名单里。你本地当然畅通无阻,服务器上就是超时。
DNS。 服务器可能用的是内网 DNS,或者容器里用的是 Docker 自己的 DNS 配置。你本地能解析的域名,容器里未必能解析,报错会是 DNS 相关的失败而不是连接失败——这两者要分清楚,是不同的处置方向。
代理。 你本地可能有一个代理软件在后台开着,你已经忘了它的存在。服务器上没有。或者相反:服务器上配了全局代理环境变量,而代理本身对某些目的地址不通。
所以在自己机器上 curl 通了,只能证明上游服务和 key 是好的,不能证明网关连得上。这个结论也有价值,但它不是这一步要的那个结论。要严谨,就得在网关跑的地方跑。
容器化部署时还有一层:宿主机能出去,不代表容器能出去,容器的网络模式和 DNS 是独立配置的。如果宿主机 curl 通而容器里不通,方向就已经很清楚了,去查容器网络,别再看网关配置。关于容器化部署本身的配置选择,可以参考 New API 部署实操 里关于部署形态的部分。
这一步的产出要记下来
curl 成功时,把那条完整的命令保存到你的运维笔记里,注释上日期。它以后有两个用途:一是上游哪天出问题时,你可以用同一条命令一秒钟确认是不是上游挂了;二是网关升级或迁移之后,它是你的回归测试基线。
第二层:地址与路径,症状通常是 404 而不是 401
curl 通了,问题在网关侧,接着查地址。
这一层最容易出问题的原因是:各家上游的 OpenAI 兼容地址路径规则根本不统一。同样是”OpenAI 兼容接口”,路径能长出好几种形态:
| 形态 | 例子 |
|---|---|
域名 + /openai/v1 | https://api.groq.com/openai/v1 |
域名 + /v1/openai(两段顺序反过来) | https://api.deepinfra.com/v1/openai |
域名 + /openai(没有 /v1) | https://api.novita.ai/openai |
域名 + /inference/v1(多一段) | https://api.fireworks.ai/inference/v1 |
域名 + /v1/(带末尾斜杠) | https://api.tokenfactory.nebius.com/v1/ |
(以上均取自各平台官方文档,路径可能调整,实际以官方文档当次为准。)
看着都差不多,抄错一段就全盘不通。而这一层真正的坑在于:网关侧填的到底是 base 还是完整路径,取决于网关怎么拼。
有的实现是你填 base,它自己在后面接具体的端点路径;有的是你填到哪儿它就用到哪儿。如果网关会自动补 /v1,而你把带 /v1 的完整地址填了进去,拼出来就是重复的 /v1/v1;反过来网关不补而你只填了域名,拼出来就少一段。两种情况都会失败,且报错通常是 404,而不是 401。
这个症状区分很有用:
- 拿到 404,先查地址和路径,别去查 key。404 的语义是”这个地址上没有这个东西”,跟你是谁没关系。
- 拿到的响应不是 JSON 而是一段 HTML,那基本可以确定路径打到了对方的网站或者某个反代的默认页上,同样是地址问题。
判断网关怎么拼的最可靠办法不是猜,是对照第一层那条 curl 命令。你 curl 时用的是完整 URL,把它和网关里填的值放一起比对,看差了哪一段,缺的那段就是网关本该补而没补的、或者你多填了的。至于填法本身,按后台页面上的提示和官方文档来。
更细的路径差异和它们各自的坑,七家推理平台的模型 ID 那篇里有横向对照。协议兼容层面上”OpenAI 兼容”到底兼容到什么程度,可以看 OpenAI 兼容协议。
第三层:鉴权,先分清 401 和 403
地址对了还是不行,查凭证。这一层最先要做的是把两个状态码分开,它们指向完全不同的处置方向。
401 的意思是”我不知道你是谁”。 凭证没被识别——没带、格式不对、被截断、已经失效、或者根本不属于这个平台。处置方向是凭证本身。
403 的意思是”我知道你是谁,但不让你做这件事”。 身份是认下来的,被拒的是这个具体操作——账户没开通这个能力、这把 key 的权限范围里不包含它、组织层面有限制、或者欠费停用。处置方向是账户和权限,不是重新贴一遍 key。
我见过很多人拿到 403 之后一遍遍重新生成 key、重新粘贴,白折腾半天。分清这两个码,能省掉整段错误方向的排查。
最常见的原因是最低级的那个:首尾空白
复制 key 的时候带上了首尾空白——空格、换行、制表符——这是我见过占比最高的单一原因,没有之一。
它为什么这么顽固,因为它看不见。你在配置里盯着那一行看十遍也看不出末尾多了个换行符。而且它有好几个来源:从网页上双击选中时多选了一格;从终端输出里复制时带上了行尾;文本编辑器在保存时自动补了个末尾换行;从聊天软件里粘过来时对方发的就带着空格。
有效的验证办法只有一个:别用眼睛看,用长度看。 把凭证的字符长度打出来,跟你从上游控制台里拿到的原始值对一下。差 1 个字符,就是末尾多了个换行。这招还有个变体是把首尾各几个字符打出来(中间打码),一眼能看出边界上有没有多余的东西。
第一层的 curl 在这里又有用了:curl 命令里的 key 是你手打或粘贴进去的,如果 curl 通了而网关里不通,那两处的 key 值大概率不一样,差别很可能就在空白上。
环境变量名各家不同
如果你的凭证是通过环境变量注入的,还有一个坑:各家上游用的环境变量名不统一,而且不只是前缀不同,后缀也不同——有的用 *_API_KEY 这种形态,有的用 *_TOKEN。你按照肌肉记忆写了个 *_API_KEY,而那家用的是 *_TOKEN,读出来就是空值。
空值造成的 401 特别有迷惑性,因为你会觉得”我明明配了啊”。查法是在启动日志里把生效的凭证做打码输出——不是输出配置文件里写了什么,是输出程序实际读到的那个值。这两者之间隔着环境变量名拼写、进程有没有重启、配置有没有被别的地方覆盖等一大堆可能性。
401 的完整排查清单在 401 报错怎么排查 里写得更细,这里不重复。
第四层:模型标识符,报错可能长得像权限问题
前三层都对了,最后一层是模型名。
这一层单独拎出来,是因为它有一个非常反直觉的特性:模型名写错,报错有时候不长得像”模型不存在”,而长得像权限问题。
原因不难理解。很多上游的模型标识符是两段式或三段式的——前面是发布方或组织,后面才是模型名。你少写了组织前缀,从平台的视角看,你请求的是一个它无法归属到任何公开模型的标识符,返回的信息就可能落到”你没有权限访问”这一类。
后果是排查方向直接跑偏:你会去查 key 的权限、查账户开通了什么、准备去申请白名单,折腾很久,问题其实在那个漏写的前缀上。
所以到了这一层,先把请求里实际发出去的模型名完整打印出来看一眼,再去想权限。这一眼花一秒钟。
网关侧的模型列表与请求里的模型名必须对得上
网关这一层还多了一个独有的坑:渠道上配的”这个渠道支持哪些模型”,和实际请求体里写的模型名,是两个地方,必须严格一致。
一致性至少涉及三件事:
- 字符串完全相同。两段式标识符通常是大小写敏感的,
DeepSeek-V3不是deepseek-v3。有些配置框架会自动把配置值转小写,这个行为要关掉。 - 前缀完整。组织前缀属于标识符的一部分,不是可选的装饰。
- 映射关系明确。如果网关支持给模型起别名,那么你要清楚请求里写的是别名还是真名,以及别名映射到的目标在这个渠道上存不存在。
典型的失败方式是:渠道的模型列表里配了一批模型名,请求时用的是另一个拼法。网关按请求里的名字去找匹配的渠道,找不到,报”没有可用渠道”——而你明明看着后台里那个渠道好好地开着。这时候不要去查渠道的开关状态,去比对两处的字符串。
多节点部署时的额外坑:两个 secret 必须一致
上面四层针对的是单节点。如果你把网关部署成了多个节点跑在负载均衡后面,还有一类问题需要单独说,因为它的表现根本不像配置错误。
New API 的环境变量里,SESSION_SECRET 在多节点部署时必须设置,并且所有节点必须设成同一个值。另外 CRYPTO_SECRET 是缓存键的 HMAC secret,它默认取 SESSION_SECRET 的值——也就是说前者没配好,后者跟着一起不一致。
不一致会怎样?最典型的现象是”时不时被登出”。你在后台点着点着突然回到登录页,重新登进去又好了,过一会儿又被踢。这个现象极难复现,因为它取决于你这次请求被负载均衡分到了哪个节点:分到签发会话的那个节点就正常,分到另一个节点就认不出你的会话。刷新几次可能又好了,你会以为是网络抖动。
而当你在这种状态下调渠道配置,排查就彻底乱了套——你在 A 节点上改的配置,在 B 节点上的缓存行为可能不一样;你测试渠道时的会话是不是有效的,本身就在随机波动。这时候你查到的任何现象都不可信。
所以给一条明确的判断规则:多节点部署下,如果你观察到节点之间行为不一致,或者出现无法解释的登出、缓存行为诡异,先去核对这两个 secret,再回来查渠道。 顺序反了就是在流沙上排查。
这两个变量的具体配置方式以官方仓库 README 当次为准。同理,多节点部署时数据库和缓存也必须是所有节点共享的外部服务,不能各跑各的本地文件。
加渠道的正确顺序:五步,每步只引入一个变量
排查方法讲完了,反过来说一下怎么加渠道才不需要排查。这个顺序的原则只有一条:每一步只引入一个新变量,出问题时候选原因唯一。
第一步:先在网关那台机器上 curl 通上游。 理由前面说透了——这一步把”上游和凭证”这半边彻底确认掉。没通就不要往下走,此时加进网关只会把一个明确的问题变成一个笼统的红字。
第二步:再加进网关,先只填最必要的配置。 别一次性把所有可选项都填满。可选项越多,出问题时候选原因越多。先用最小配置让渠道跑起来,之后再逐项加。
第三步:用一个最小的模型测一次。 挑上游里最便宜、最基础的那个模型,发一条最短的请求。理由有三个:便宜,测试成本可以忽略;快,反馈周期短,你可以多试几次;而且它把”链路通不通”和”某个大模型有没有开通权限”这两件事分开了——很多上游的高端模型需要单独申请,用它做首测,一个链路问题会被伪装成权限问题。
第四步:链路确认通了,再放开模型列表。 这时候每加一个模型,都是在验证”这个具体的模型名在这家上游存不存在、账户开通没有”,而不是在验证链路。这两类问题被彻底分开了,加十个模型出错三个,你能立刻知道是那三个模型名或权限的事,不用怀疑链路。
第五步:最后才接生产流量。 而且不要一次性切过去。先给这个渠道很低的权重或者很小的流量比例,跑一段时间,看有没有偶发的失败——有些问题只在一定量级下才暴露:上游的速率限制、连接池耗尽、长尾超时。这些在单次测试里全都看不见。
不知道上游的限速数值时怎么稳妥起步?把并发压到很低的水平开始,观察一段时间没有异常再逐步往上加;同时把重试和退避准备好,退避要用指数形式并加随机抖动,避免所有失败请求在同一时刻重来一次把上游打得更狠。这个过程本身也会让你摸出自己的实际可用容量,比问到一个数值更靠谱。
自查清单
渠道测不通时,从上往下依次做,做完一条打一个勾:
- 在网关那台机器上(容器化就进容器)用 curl 直接调上游,带上同一把 key、同一个模型名。通了就确认问题在网关侧配置,不通就别再动网关。
- 拿到的是 404 或者一段 HTML?去查地址与路径,把 curl 里的完整 URL 和网关里填的值逐段比对,看差了哪一段。
- 拿到的是 401 还是 403?401 查凭证本身,403 查账户与权限范围,别把 403 当成 key 错了。
- 用字符长度核对凭证有没有带首尾空白,别用眼睛看配置文件。
- 凭证走环境变量注入的,在日志里打出程序实际读到的打码值,确认变量名没写错、进程重启过。
- 把请求里实际发出的模型名完整打印一次,检查组织前缀有没有漏、大小写对不对,再去怀疑权限。
- 比对渠道上配置的模型列表与请求里的模型名,两处字符串必须完全一致。
- 多节点部署的,核对所有节点的
SESSION_SECRET是不是同一个值(CRYPTO_SECRET默认跟随它);出现随机登出或节点行为不一致时,先修这个再查渠道。
所有具体的配置位置、字段填法、环境变量的设置方式,以你手上后台页面的提示和官方仓库 README 当次为准——这类项目迭代快,界面和参数都可能已经和某篇文章里写的不一样了。但上面这套排查顺序不依赖界面,它只依赖”每次只引入一个变量”这个原则,换成任何一个网关都成立。