刚把 Base URL 和 Key 换成新的中转站,程序跑起来了不等于接对了:可能返回的是被缓存的老响应,可能字段少了一半,也可能头几次通、量一上来就超时。这篇给一套从最小请求到批量压测的验证顺序,每一步都有明确的通过标准和失败时先看哪里,照着走一遍就知道这条线能不能用。
刚把 Base URL 和 Key 换成新的中转站,程序跑起来了不等于接对了。你看到的可能是一份被缓存的老响应,可能是字段少了一半的残缺结构,也可能是头几次通、量一上来就超时。能用 curl 拿到 200 只说明这段链路活着,对没对人完全是另一回事。
下面这套验证顺序是从最小请求一路加到并发边界的分层检查,每一步都给通过标准和失败时先看哪里。照着走一遍,你就能判断这条线是真通、通得对,还是只是偶尔通。判断标准不是绝对的,如果你的业务只用到聊天补全、不碰结构化输出和工具调用,那后面几节可以适当裁剪,但连通性、正确性、稳定性这三层至少各留一项。
能连上不代表接对了
TCP 通、HTTP 200,只证明链路层活着。它不证明请求里的模型名对方真的认,不证明鉴权头的写法对,也不证明返回的字段和你在旧站解析的那套一致。很多切换翻车都翻在这一步:程序不报错,但拿到的东西是错的。
中转站常见的「通而不对」有三种。第一种是返回结构缺字段,比如 choices 在但 usage 没了,或者嵌了一层别人自造的包装;第二种是模型被静默替换,你点的是 A,实际跑的是更便宜的那档;第三种是流式和非流式行为不一致,非流式正常,改流式就整段返回或者缺结束标记。
- 先明确这次切换的验收范围:是只跑通聊天补全,还是连结构化输出、工具调用、流式一起要;范围不同,检查项完全不一样
- 把验收范围写成一张勾选清单,每项一句「怎么算过」,避免后面凭感觉判断
- 范围外的东西别顺手测,测出问题又不在本次目标里,只会让结论变模糊
第一步:用最小请求建立基线
挑一条固定短提示词、固定参数(温度、最大长度都写死),发一次非流式请求,把完整响应体原样存下来当基线样本。关键在「原样」两个字——别只存解析后的对象,要存原始文本,后面字段比对全靠它。
- 存一份旧站基线切换前就用同一条提示词在旧站打一次,存原始响应。没有旧基线,你就没有对照物,只能凭印象。
- 再打一次新站同样提示词、同样参数打到新站,存原始响应。两条放一起逐字段比。
- 逐字段核对看 id、model、choices、usage 这些顶层字段在不在、类型是否一致、嵌套层级是否一样。
- 确认能解析拿旧站的解析代码直接跑新响应,解析失败就是结构不对,别急着改解析代码去迁就。
模型名不要沿用旧的硬编码值。不同中转站声明的可用名称不一定完全一致,写错了有的直接报错,有的会静默落到默认模型。填之前去 模型库(/models) 核对该中转站声明支持的名称再填,别猜。
| 检查项 | 通过标准 | 失败先看哪 |
|---|---|---|
| 顶层字段齐全 | id / model / choices / usage 都在 | 响应原始文本,看是不是被包了一层 |
| 字段类型一致 | choices 是数组、usage 是对象 | 对比新旧两站原始响应的 JSON 结构 |
| 可被旧代码解析 | 旧解析逻辑不改动能跑通 | 解析报错位置,通常是层级或命名差异 |
| model 字段回显正确 | 回显的是你请求的模型名 | 换成模型库里确认存在的名称再打一次 |
第二步:验证返回的是不是「对的答案」
结构对了不代表内容对。这一步要确认中转站真的把请求路由到了你指定的模型,而不是拿一个弱模型糊弄过去。方法是用有唯一正确答案的短题来测,答错或答得离谱就说明路由有问题。
- 准备 5 到 10 条有唯一正确答案的短题:简单算术、固定格式的字段抽取,答案唯一,对错一目了然
- 把同一组题在旧站和新站各跑一遍,正确率差太多就是模型被换了的信号
- 结构化输出单独测:要求返回固定 JSON 结构,看是标准字段、被额外包了一层,还是干脆返回纯文本
- 工具调用单独测:给一个必然要被调用的工具定义,看返回里有没有工具调用字段,而不是把参数写进正文
- 每一步都要记录「是哪个字段不对」,不要只写一句「报错」
这三类里,结构化输出和工具调用是中转站最容易出问题的地方,因为要实现它们往往需要对方在中间做一层额外处理,处理不好就会变形。如果你的业务依赖这两项,它们的优先级应该排在最前面。
| 测试项 | 不正常的表现 | 初步判断 |
|---|---|---|
| 唯一答案短题 | 简单算术反复答错 | 可能被路由到弱模型 |
| 结构化输出 | 返回纯文本或外层多包一层 | 中间层没做透传或做了改写 |
| 工具调用 | 参数被写进正文而非工具字段 | 工具调用能力未透传或未实现 |
| 同一提示多打几次 | 答案风格差异特别大 | 后端可能轮询到了不同模型 |
第三步:流式、超时与首字延迟
同样一条请求,分别用流式和非流式各打一次。流式这边重点看两件事:是不是真的分块返回,以及结尾有没有正常的结束标记。有的中转站非流式正常,一切到流式就整段一次性返回,或者压根不给结束标记,客户端会一直挂着等。
耗时这块要记录首字延迟和整段耗时两个值。首字延迟决定用户感知的「快不快」,整段耗时决定你要把超时值设多大。超时值一定要留余量,别贴着实测平均值设,具体分档可以对照 接口测速(/tools/speed-test) 量出来的基线来定。
- 流式是否分块:连续多个数据块才算通过,一次性返回整段要标为异常
- 结束标记是否存在:缺了它客户端可能不收尾,长连接容易堆积
- 首字延迟:单独记录,和整段耗时分开看
- 整段耗时波动:同一条请求重复三次,看三个值的离散程度,单次快不算稳
这里的不适用场景也说明一下:如果你的业务是纯离线批处理、根本不关心首字延迟,那流式和耗时这两项可以只做「能用」级别的确认,不必深挖波动。但只要你有实时交互的界面,这两项就绕不开。
第四步:并发与限流边界
前面几步都是单请求,只能证明「在没压力时能用」。真正上线后是多请求并发,限流和超时往往在这时候才暴露。做法是从小并发起步,逐级往上加,观察从哪个量级开始出现限流或超时。
- 定一个起点从很低的并发开始,比如先 1 到 2 路,确认稳定再说。
- 逐级加码每次把并发翻倍,每档稳定跑一小段时间,记录状态码和耗时。
- 找到临界记下开始出现限流或超时的那一档,这个临界值就是你应用侧配置的依据,别靠猜。
- 看拒绝方式命中限流后看对方是直接拒绝还是排队等待,两种行为对应的退避策略完全不同。
不同中转站的额度分档差别很大,实际数字没法一概而论,去 中转站目录(/sites) 和对应站点页面核对当期口径。要注意这些页面的数字是随时会变的,你看到的永远只是当时那一刻的说明,别把它当成长期承诺写进代码。
- 直接拒绝:应用侧应做指数退避重试,别立刻重打
- 排队等待:耗时会被拉长,超时值要相应放宽,否则会把排队误判成失败
- 平稳无限流:也要记下最高稳定并发,作为容量上限
- 临界值要写进配置,不要硬编码在业务代码里
第五步:日志与降级开关
验证的目的是出问题时能定位,所以每一步请求都得留下能复现的记录。缺了日志,前面所有检查做完也只得到一句「当时好像没问题」。
- 每次请求至少记录:时间、模型名、耗时、状态码、响应体摘要
- 响应体摘要别只存状态码,错误信息往往在 body 里
- 准备一个备用源,主源连续失败时自动切换
- 切换动作本身也要写日志,否则你根本不知道什么时候切过
- 把「切换阈值」写进配置,方便随时回滚
# 验证请求建议留下这些字段,示意结构,具体字段名按你日志系统调整
# time / model / latency_ms / status_code / body_digest / route
# 一次失败至少能靠 time + model 复现出来降级开关要能在不改代码、不重启的前提下生效,否则真出故障时你只能干等发布。阈值写成配置,比如「连续失败几次切备用」「多久内失败率到多少告警」,写死一个绝对数字反而不好用,因为不同业务容忍度差很多。
验收清单:什么情况算通过
最后把所有检查收敛成一张清单。功能通过和稳定通过是两件事,别混在一起判断。功能通过看结构对不对,稳定通过看一段时间内有没有异常失败、耗时波动是否在可接受区间。
| 类别 | 通过标准 | 不适用情况 |
|---|---|---|
| 功能 | 最小请求、结构化输出、工具调用、流式四项全部返回预期结构 | 业务只用聊天补全时,后两项可不做 |
| 稳定 | 连续观察一段时间无异常失败,耗时波动在可接受区间 | 纯离线低频调用,观察周期可放宽 |
| 限流 | 并发加到预期峰值仍无限流,或限流行为可预测 | 内部低并发场景,可不做峰值压测 |
| 可观测 | 失败能靠日志复现并定位到字段 | —— |
只有「偶尔通」的,一律按不稳定处理,不要因为演示成功就接进生产。演示往往是最理想的网络和最低的并发,恰恰测不出问题。
还有一点要说清楚:这套流程给的是判断方法和动作,不保证任何一条线长期稳定。中转站的可用模型、额度分档、限流策略都可能变,验收通过只代表「当时这一刻能过」。建议把这份清单留成常备检查,接入新中转站或对方有变动时重跑一遍,比每次凭感觉试要省事得多。
把这条线的底细摸清楚再上线切完别急着铺量,先去站内的中转站目录和对应页面核对当期口径与可用模型,再按上面的顺序跑一遍验收清单。→常见问题
返回 200 但内容是空的,算通吗?
不算。空内容常见于模型名不被识别却仍然返回成功、或上游超时被兜底成空串。先换一个确认可用的模型名重试,还空就看响应体里的错误字段和用量字段是不是也是空的,两边都空基本可以判定这条链路没真正走到模型。
为什么同一条请求第一次通、后面就失败?
多半是并发或额度触发限流,而不是接口坏了。把请求改成串行再打几次,如果串行稳定、并发就挂,问题在限流阈值。这时要看响应头或错误码里的限流提示,据此调低并发或加退避,别急着换站。
怎么判断中转站是不是偷偷换了模型?
用几道有唯一答案的短题,加一条要求特定格式输出的指令,多次调用看答案质量和格式是否一致。如果时好时坏、格式也飘,说明背后路由不固定。更系统的比对可以看 模型可信度排行(/rankings) 的口径说明。
验证要花多少请求量才够?
功能验证十几条就够,稳定性验证才需要量。建议先跑一轮小批量把四项功能过一遍,再按你真实的调用节奏连续观察一段时间。不要在没做功能验证的情况下直接压量,那样只会把问题放大。
验证通过后还需要做什么?
留一份基线响应快照和一份配置备份,写清切换阈值和回滚步骤。之后模型或上游有变动时,用同一套用例重跑一遍就能快速判断是链路问题还是模型问题。用例本身要版本化,别每次临时想。