跳到主要内容
54 次调用跑完,我记下来的不是排名,是三种不报错的失败

54 次调用跑完,我记下来的不是排名,是三种不报错的失败

abanana
abanana

· 阅读约 5 分钟

前几天翻到一份公开的推理端点对比测试:六个模型、三种提示形态、每个模型跑三次,一共 54 次调用。每次记了首 token 时间、总耗时和单次成本,总花费 0.0185 美元。发出去的地方是从欧洲的笔记本上,晚上十点左右,带着跨大西洋的网络条件。

点进去本来是想看谁快的。排名那一段我扫两眼就跳过去了,真正让我停下来的是三种失败——它们的共同点是,仪表上一切正常。这篇就把这三件事拆开看看,顺便说一句我对「流式变慢」的默认判断是怎么被推翻的。

先把接入那一层交代一句,后面要用。这套端点兼容 OpenAI 客户端,改 base_url 和模型访问密钥就能连,换模型基本只换模型字符串。作者用的是 Gradient 平台下建的模型访问密钥;他也验证过 dop_v1_ 开头的 API token 能过认证,甚至能顺带访问账户接口,结论仍然是建议用权限更窄的那个。这个我认同。不过我更在意的是「兼容」这两个字被读成了什么。

72 个模型,你的账户可能只调得动 6 个

GET /v1/models 返回了 72 个模型,作者账户实际能调通的只有六个。Anthropic 那几个,加上 GPT-4o、o3,一律 403,错误信息写着不在当前订阅层级里。麻烦的地方在于响应里既没有可用性标记,也没有层级字段——你没法提前筛,只能真的发一次请求、把 403 读出来,才知道这个模型对你的账户到底存不存在。

作者预选的列表里塞了 anthropic-claude-haiku-4.5,理由是它出现在目录页和定价页上。首次运行那一列直接红掉。从第一次碰到 API 到撞上这个失败,中间大概隔了六十秒。

坑不在「文档没读全」。在于目录和定价页回答的问题是「这个模型存在」,不是「你能用它」。这两句话在页面上长得一模一样,填进代码里差一个 403,而且这个 403 不会在启动时报出来,只会在你选了它之后报出来。

空文本、照常计费、状态码 200

qwen3.5-397b-a17b 九次运行里有七次没有文本,钱一分没少收。原因很具体:512 的 token 预算里有 487 个花在了推理上,推理内容进了 delta.reasoning_content,而这个字段不在 OpenAI 架构里,兼容客户端从头到尾看不到任何内容。那一次的账是 completion_tokens 512、reasoning_tokens 487、content 0 字符。

放到整体里看更清楚:54 次调用全部返回 200,没有异常、没有非 200、没有超时,其中九次返回空文本并按满额计费。也就是说,只监控延迟和状态码的那套评估方式,对这种失败完全是瞎的——延迟正常,状态码正常,账单正常,就是没有字。

作者后来打了个补丁,让「没有文本但 reasoning token 非零」的列自动解释原因。评论区 Vinh Nguyen 指出这个修复和 bug 本身同形:它只在提供方既计费又报告 reasoning 数时才触发。作者于是把判断条件换成内容字符数和计费的 completion token 对比,并补了回归测试。这个改动是我觉得整个项目里最值得抄的一段:

# 不问"有没有 reasoning 字段",改问:账上花了 token,正文收到了几个字符
if len(text) == 0 and billed_completion_tokens > 0:
    mark_as_hidden_output()

💡 小技巧:交叉验证的时候,别用一个可能同样缺失的字段去给另一个字段作证,找一个一定存在的量当基准。这里就是账单。

六个流排成一队,看起来像模型慢

这条跟模型一点关系都没有,是服务器配置。

为了让六个模型同时流式输出,作者没上服务端多路复用,而是让浏览器给每个模型各建一条 EventSource,Flask 保持同步、不引入异步编排。他本来预判 gunicorn 默认的同步 worker 会把页面挂住,实际现象却是六个并发流被老老实实排成一队:单个同步 worker 下,六个首 token 依次冒出来,总耗时约 10.7 秒,没有超时也没有错误。

换成 gthread、16 线程、120 秒超时之后:

# gunicorn.conf.py
worker_class = "gthread"
threads = 16
timeout = 120

六个首 token 里有四个落进约 1.4 秒的窗口内,总耗时压到 6.4 秒。

这版要是就那么发出去会怎样?流式响应「成功」了,只是被排了队。看到的人大概率会得出「这几个模型挺慢」的结论,然后去翻模型卡,压根想不起来还有个 worker 配置可以看。我之前记过一篇关于 worker 类型的笔记,当时还觉得这种配置属于「一次配好就不用管」的东西——现在看,串行排队和模型延迟慢,在用户眼里长得完全一样,而后者会被写进结论里。

划重点:第一,模型目录和定价页回答的是「存在」,不是「你能用」,403 只会在你真的调它的时候出现,提前筛不掉;第二,只看延迟和状态码会漏掉「200 但正文空、还照常计费」这一类,至少要拿一个一定存在的量去对一遍,这里就是账单;第三,并发流被 worker 排队和模型本身慢,在页面上看不出区别,换 worker 类型之前别急着给模型下结论。

这篇就记到这里。你要是也在跑多模型对比,可以先去 GET /v1/models 拉一遍列表,再拿一次空响应的账单核一下——这两步花不了几分钟,但能省掉后面一堆误判。

abanana
abanana

把自己踩过的坑整理成一篇能复现的笔记,写给三个月前的自己看。

查看主页 →