同一个 429,三种根因:多人共享 GPT 管道的故障排查与设计
十个同事共用一根管道调 GPT,429 打断了你的 Codex 会话。是你太快、是同事太猛、还是 OpenAI 那边炸了?从错误码分类到管道层设计,一篇讲透共享场景下的重试、配额与可观测性。
十个人共用一根管道调 GPT。某个下午,所有人的 Codex 同时 429。 有人说是限速,有人说是额度用完了,有人说是 OpenAI 宕了。 三种猜测对应三种根因,只有一种是对的——但你现在没法分辨是哪种。
前面几篇聊 harness 和模型的时候,「壳比模型值钱」是面向个人开发者的。但公司里十个人共用一根管道时,429/502/503 的形态跟单机完全不同,而且解法也不一样。这篇就把共享场景下的错误码诊断和管道设计一次讲透。
先把三个码分清
很多人把 429、502、503 混为一谈,碰到「报错了」就重试。但它们含义完全不同,重试策略也不一样。
429 Too Many Requests 是「你太快了」。命中了 RPM(每分钟请求数)或 TPM(每分钟 token 数)限额。OpenAI 的 429 还分两种:rate_limit_exceeded 是真的超了你的 tier 配额;slow_down 是你的请求速率涨得太陡,需要降速但不一定超限额。Anthropic 更特殊——它用 529 表示服务端过载,429 专指你的限额。
502 Bad Gateway 是网关层故障,负载均衡或反向代理挂了。通常几秒到几分钟自动恢复。
503 Service Unavailable 是服务端过载或维护中。OpenAI 9 月做了个重要拆分:原来跟 429 混在一起的「推理层饱和」现在单独返回 503 server_is_overloaded,跟你的请求速率无关,是 OpenAI 自己容量不够。但大部分 SDK 和网关还在把 503 当 429 处理——统一 backoff,不区分根因。
还有一个容易忽略的:insufficient_quota。这也是 429,但不是速率限制,是余额或月度消费上限到了。重试多少次都没用,得充钱或提额。
共享管道为什么更痛
个人用户碰到 429,等 60 秒就好。共享管道里,问题变形了。
429 不是「你太快」而是「你们太快」。 十个同事同时跑 Codex 或 Claude Code,每人都觉得自己没发多少请求。但管道出口是同一个 key、同一个 TPM 池。你等了 Retry-After 给的 60 秒,另外九个人没等——窗口一开就被别人抢了。你永远在排队。
额度归属说不清。 共享 key 下 OpenAI 的 x-ratelimit-remaining-* header 是 key 级别的,不是用户级别的。某轮 429 是谁打爆的?没法追溯。公司想做成本归属和限额分配,得自己在管道层加 per-user 计数——但这又引入延迟。
突发抢占是常态。 某个同事跑了个大仓重构,Codex 一轮把 Git diff、文件内容、测试日志塞进去,单轮轻松 20k-50k token,半个 TPM 池就没了。其他人全等。这不是「禁止大任务」能解决的——大任务恰恰是 Codex/Claude Code 的核心价值。
fallback 会级联炸。 个人用户 429 了 fallback 到 Claude,没问题。但十个人同时 fallback 到 Claude,Claude 的 key 也炸了。第二供应商也得有配额管理和并发上限,否则只是把爆炸换了层。
诊断决策树:先分清是哪层的问题
碰到 429/502/503 时,按这个顺序排查:
第一步,curl 测端点。从管道服务器直接发一个最小请求:
curl -sS -o /dev/null -w "HTTP %{http_code} in %{time_total}s\n" \
https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY"
如果返回 401 或 200 且只花几秒,端点是活的,问题在管道配置或用户侧。如果超时或 000,网络层挂了。如果返回 429,管道的 key 确实到了限额。
第二步,看 status page。status.openai.com 和 status.anthropic.com 都有机器可读接口:
curl -s https://status.openai.com/api/v2/status.json
# {"status":{"indicator":"none","description":"All Systems Operational"}}
indicator 从 none → minor → major → critical。如果已经是 major,别猜了,就是服务端问题,等恢复。
第三步,看管道内部监控。这是共享管道最该有但最常缺的东西。你需要知道:当前 TPM 用了多少、谁在用、哪个模型在排队、平均延迟多少。OpenAI 的响应 header 给了原始数据——x-ratelimit-remaining-tokens、x-ratelimit-reset-tokens 等——但得有人去采、去展示。没有这个 dashboard,运维就是瞎猜:「为什么又 429 了」「不知道,可能谁跑了个大任务吧」。
第四步,区分限速和额度。看 429 的 error body:rate_limit_exceeded 是速率限制,等一会儿就好;insufficient_quota 是余额或月度上限到了,等没用,得充钱。这个区分在共享场景下特别重要——十个人等了一个小时还是 429,最后发现是月度额度到了,白等。
第五步,排查代理层。如果 status page 绿、curl 直连也通,但 Codex/Claude Code 还是超时,十有八九是代理层的问题。Clash 规则匹配、DNS 污染、IPv6 回退、SSE 长连接被代理 buffer 截断——浏览器能开但 CLI 超时的经典症状。用 NO_PROXY=api.openai.com 直连测试一下,如果好了就是代理的锅。
管道层该做的四件事
如果你们公司的「管道」只是一个反向代理加一个 key 轮询,那上面的问题基本无解。真正的共享管道需要四层能力。
1. 配额分配:per-user token bucket
管道层给每个用户分独立的 RPM/TPM 配额,而不是大家抢同一个池子。LiteLLM 和 OneAPI 这类网关都支持 per-key 或 per-user 限额。核心思路是 token bucket——每个用户有一个令牌桶,按速率补充,请求消耗令牌,桶空了就 429 给那个用户而不是整条管道。
这样一个人的大任务只会打爆自己的配额,不影响其他人。代价是配置不当反而更慢——如果配额分得太细,正常使用也经常被限。建议按团队规模和任务类型动态调整,而不是一刀切。
2. 优先级队列:CI 优先、交互次之、批量最低
同一个 TPM 池里,CI/CD 的 API 调用应该优先于交互式开发,交互式优先于批量任务。问题是 Codex 和 Claude Code 的请求长得都一样——都是 messages 数组,没法从请求体区分。
实践中只能靠来源标记:给 CI/CD 用单独的 key 或 header 标记,管道层按标记分流。或者更粗一点——给 CI/CD 一个独立的小池子保证可用性,交互式和批量共用大池子。这不完美但够用,追求完美的话可以看请求的 stream 参数和 max_tokens 大小做启发式判断。
3. 可观测性:不是可选的
共享管道没有 dashboard 等于盲飞。至少要采这几个指标:
| 指标 | 来源 | 为什么重要 |
|---|---|---|
| TPM 消耗 | x-ratelimit-remaining-tokens header | 知道池子还剩多少 |
| per-user 请求数 | 管道层计数 | 追溯谁在打爆 |
| 429 率 | 管道层日志 | 判断是常态还是异常 |
| 平均延迟 | 管道层计时 | 发现服务端退化 |
| 模型分布 | 请求体 model 字段 | 优化路由的依据 |
展示层不用复杂,一个 Grafana panel 或者甚至一个内部网页就够。关键是数据要采、要存、要有人看。
4. 模型路由:便宜模型接简单任务
十个人共用一根管道,不可能所有人都用旗舰模型。有人写代码要 GPT-4o 或 Claude Sonnet,有人跑数据清洗用 mini 就够,有人做文档摘要可以走 Batch API 半价。
管道层做任务分类和模型路由——简单任务走便宜模型、复杂任务走旗舰模型——同样 TPM 池能服务更多人。这跟之前聊 harness 时说的「壳比模型值钱」直接呼应:管道就是团队级的壳,路由策略决定了模型资源的利用率。
实际做路由可以很粗:CI 的 lint 检查走 gpt-4o-mini,只有标注了「架构设计」或「跨文件重构」的才走旗舰。或者看 prompt 长度——超过 10k token 的大概率是复杂任务,走旗舰;短的走 mini。
重试策略:别无脑 backoff
最后说重试,因为这是最容易做错的一环。
分类再重试。 只有 429(rate_limit)、500、502、503、504、529 是可重试的。401/403 是认证问题,400 是请求格式错,重试再多次也不会成功。把不可重试的请求烧在重试循环里是浪费配额。
读 Retry-After,别猜。 OpenAI 和 Anthropic 的 429 响应都带 Retry-After header,告诉你精确等待秒数。用这个值比用 2^attempt 猜的准得多——服务端知道什么时候恢复,你不知道。
加 jitter。 如果十个同事同时 429、同时等 60 秒、同时重试——这就是 thundering herd,瞬间又一轮 429。jitter 就是给等待时间加随机偏移,让重试分散开。full jitter(0 到计算值之间的随机数)效果最好。
设上限。 最多重试 3-5 次,总等待时间不超过 60-120 秒。超过就 fail fast,告诉用户「服务端暂时不可用」,比让 agent 挂在那儿等 10 分钟强。
fallback 也要限流。 上面说了,十个人同时 fallback 到 Claude 会炸 Claude。管道层的 fallback 路由要带并发上限——第二供应商也不是无限的。
和专栏前文的呼应
回到模型自己变成壳那篇的论点:Astra 把执行层吃进模型,但约束层更值钱了。共享管道就是团队级约束层的物理形态——配额分配、优先级队列、可观测性、模型路由,这些不是模型能替你做的。
而安全观察系列里说的「可观测性从 CoT 移到行为层」,在管道层面是同一个道理:你不需要看模型的思维链,你需要看管道的 TPM 消耗和 429 率。监控要落在基础设施层,不是模型层。
参考:OpenAI Rate Limits 文档(platform.openai.com/docs/guides/rate-limits)、OpenAI 9 月 429/503 拆分变更(developers.openai.com/api/docs/changelog)、Anthropic Rate Limits 文档(platform.claude.com/docs/en/api/rate-limits)、TheRouter.ai 对 429 拆分的分析(therouter.ai/news/openai-429-slow-down-503-overloaded-retry-routing)