
如果你做过 AI 产品,你应该见过这种场景——
你:用户量上来了!
OpenAI:今晚 3 点 ~ 6 点 API 不可用。
用户:你这玩意儿挂了!
你:这不是我的锅啊,是上游……
用户:我管你上游下游,反正你这破玩意儿挂了。
挂在这种事情上的产品我见过太多。
朋友圈三天两头有 AI 创业公司发"由于上游服务波动,我们暂停服务"的公告。
但你注意一件事—— Anthropic 的 API 也会挂。 但 Claude Code 用户基本没感觉。
这一章讲的就是——Claude Code 是怎么把"上游挂了"这件事,藏到用户感觉不到的深度。
重试这件事,没你想得简单
先讲一个数字。
Claude Code 的 API 通信层,一次请求最多重试 10 次。
听起来很多对吧?但更精彩的是——重试不是无脑重试。
源码里有一个常量叫 DEFAULT_MAX_RETRIES = 10,但旁边还有一个常量叫 MAX_529_RETRIES = 3。
什么意思?
全局重试预算 10 次,但 529 错误(服务过载)独立计数——上限 3 次。
为什么 529 要单独算? 因为 529 是"服务端忙不过来"的信号——你越重试,越是火上浇油。
源码注释里有一句话让我看完后背一凉:
"每一次重试都是对过载后端的 3-10 倍放大效应。"
什么意思?
假设上游服务挂了。1 万个客户端在请求。每个客户端遇到 529,立刻重试。
1 万个变 2 万个。
2 万个变 4 万个。
4 万个变 8 万个。
3 次重试之后,原本的 1 万个流量变成了 8 万个流量。 本来只是过载,现在是雪崩。
所以 Claude Code 设计了 4 条规矩:
529 单独计数,最多 3 次——别再火上浇油了 背景任务(标题生成、摘要)遇到 529 直接放弃——只有用户在等的请求才重试 3 次 529 之后,立刻降级模型——从 Opus 切到 Sonnet——避开过载的那条路 指数退避——重试间隔 0.5s、1s、2s、4s……加上 0~25% 抖动——避免所有客户端在同一秒重试
这 4 条规矩,是 Anthropic 工程师用一次次真实雪崩磨出来的。
我那家挂了的初创公司,曾经因为一次重试雪崩,让上游 API 停了 4 小时。 我们当时用的是最朴素的 try { ... } catch { retry() }——就这。 那 4 小时,我们贡献了上游服务被打挂的一半流量。
"慢不等于死"——这一句话价值几百万
第二个让我重新理解 AI 工程的细节。
流式响应这事——AI 模型一边说话你一边看——很常见对吧?
但流式响应有个隐藏问题:它可能挂在中间。
你看到模型说了一半,然后……没了。 不是结束了。是中间断了。
怎么判断流是"挂了"还是"在思考"?
这是个哲学问题。慢和死,外观看起来一样。
Claude Code 给出了一个让我服气的答案——双重看门狗:
| 看门狗 | 阈值 | 行为 | |---|---|---| | Idle 看门狗 | 90 秒没任何动静 | 断流 | | Stall 看门狗 | 两次事件之间间隔超过 30 秒 | 只记日志,不断 |
Idle = 完全死了。90 秒一个字都没出来——这条命没了,断流,启动 fallback。
Stall = 只是慢了。中间停顿了 30 秒,但还在往外蹦字——别动它,让它继续,但记一笔账。
这一组定义就是 4 个字——慢不等于死。
源码注释里写:
"Idle = no events at all (connection dead);
Stall = events are arriving but with gaps (server is just slow).
Slow is not dead."
为啥这件事重要?
因为如果你把"慢"当成"死",你会浪费一大笔钱。
每次断流,你前面流过来的那些 token 都白付钱了。 每次断流,你要重新从 0 开始,再付一次钱。 每次断流,用户看到的是"刚才那段没了",他要重发问题——又一次完整的钱。
我那家初创公司的产品,就是把"慢"当成"死"——超过 5 秒没响应就断流重试。 结果有一阵子 Anthropic API 在国内访问慢,每个请求都要 8~12 秒。 我们 5 秒就掐断,每次都得重新跑一遍。
那个月烧出去的钱是平时的 2.7 倍。
如果当时我们写的是"30 秒只记不断、90 秒才断",可能还在。
缓存感知重试:等 20 秒还是切模型?
第三个细节——这个让我对工程的理解又上了一个台阶。
API 重试的时候,服务器经常在响应里告诉你"等 X 秒再试"——这个东西叫 Retry-After。
正常的客户端是怎么处理的?
等 X 秒,再试。就这。
Claude Code 的处理多了一步:
如果 X < 20 秒——原地等,保留缓存 如果 X ≥ 20 秒——别等了,切模型为什么 20 秒是分水岭? 因为 prompt cache 的 TTL 大约就是几十秒。
什么是 prompt cache? 你每次发请求,前面那一坨几万 token 的系统提示词,Anthropic 会缓存,下次再发同一个就便宜 90%。
但这个缓存有时效——几十秒后过期。
所以 Claude Code 是这么算账的:
等 < 20 秒:缓存还在,原地等划算。等完直接用同一个模型,享受 90% 折扣。 等 ≥ 20 秒:缓存反正要失效了,再等也没意义。直接切到备用模型——重新建立缓存——避开过载的那条路。
这个 20 秒不是拍脑袋的数字。 是一个用钱算出来的精算结果——
等 20 秒能省 X 块 token 钱 切模型成本 Y 块 token 钱 当 X ≈ Y 时——拐点出现
Claude Code 在这个拐点上做了一行代码的决策。

25 种错误分类——每一种都是一段血泪史
第四个让我服气的细节——错误分类。
正常的 API 客户端,错误分类大概是这样的:
- 4xx:客户端错误 - 5xx:服务端错误就这。
Claude Code 的错误分类有 25 种以上:
aborted:用户主动取消 repeated_529:连续 529 过载 prompt_too_long:请求超长 pdf_password_protected:PDF 加密了 token_revoked:API key 被吊销 bedrock_model_access:AWS Bedrock 模型访问权限问题 tool_use_mismatch:工具调用配对错误(暗示上下文管理 bug) ssl_cert_error:SSL 证书问题(检查代理) ……
每一种错误背后都是一次具体的故障。 某个用户传了加密 PDF,PDF 库挂了——pdf_password_protected 诞生。 某次工具调用的 input 和 output 对不上——tool_use_mismatch 诞生。 某个 AWS 用户的 IAM 权限没配对——bedrock_model_access 诞生。
为什么要分这么细?
因为分得越细,修起来越快。
监控告警显示"500 错误率上升"——你不知道是哪儿挂了,得花 2 小时排查。 监控告警显示"tool_use_mismatch 错误率从 0.01% 跳到 5%"——你 5 分钟知道是某次发布把工具调用的配对逻辑搞挂了。
源码注释里有一句话——
"这套系统的每一行代码都映射着一个曾经发生过的故障场景。"
这种分类不是天上掉下来的,是一次次半夜被叫起来磨出来的。
心跳保活——长睡眠切成 30 秒一节
最后一个细节——这个真的让我服气。
Claude Code 有一个"持久重试模式"——开启之后,重试时间最长能等 5 分钟。
正常的客户端是这么写的:
sleep(retryAfterMs) // 一觉睡到要重试的时候就这。
Claude Code 是这么写的:
let remaining = retryAfterMs while (remaining > 0) { yield 一个心跳事件 await sleep(min(remaining, 30秒)) // 最多睡 30 秒 remaining -= 30秒 }长睡眠切成 30 秒一节。
为什么?两个原因:
第一个原因——容器。 很多用户的 Claude Code 跑在容器里——比如 Docker、CCR 这种。容器有个习惯——5 分钟空闲它就把你回收了。 你睡 5 分钟,醒来发现容器已经被杀了,整个会话挂了。 切成 30 秒一节,每 30 秒动一下,容器就觉得你还在——不会被回收。
第二个原因——用户。 用户可能突然想 Ctrl+C 取消。 你睡 5 分钟,用户按 Ctrl+C 你听不到——他得等你睡醒。 切成 30 秒一节,每 30 秒检查一次"用户是不是要取消"——用户最多等 30 秒就能取消。
这个设计叫"心跳保活"。 名字朴素,背后是两次半夜被叫起来的故事—— "为什么我们的会话莫名挂了?"——容器被回收。 "为什么用户取消了还在跑?"——主流程在 sleep。
每一次半夜被叫起来,就多一行代码。
你的 AI 应用为什么不稳,Claude Code 为什么稳
回到开头——
Claude Code 是怎么把"上游挂了"藏起来的?
10 个细节:
重试 10 次,但 529 错误单独 3 次——避免火上浇油 背景任务遇到 529 直接放弃——别给过载的服务再加流量 3 次 529 之后立刻降级模型——切一条没堵车的路 指数退避 + 0~25% 抖动——避免所有客户端在同一秒重试 双重看门狗——慢不等于死,30 秒只记 90 秒才断 缓存感知重试——20 秒以内保留缓存,超过 20 秒切模型 25 种错误分类——每一种都是一次故障的具体修复 心跳保活——长睡眠切成 30 秒一节,容器不杀、用户能停 fallback 链——主模型挂了切备用模型,备用挂了切再次备用 全程遥测——每次调用三个事件(query/success/error),出问题能查
每一项都对应着一个真实发生过的雪崩。
我以前以为 AI 应用的核心竞争力是"调用对的 API + 写好的 prompt"。 读完这一章我知道——核心竞争力是"上游挂了你能不挂"。
模型有三家在做。提示词全网都在抄。
唯独"上游挂了你能不挂"——是钱和时间堆出来的。
夜雨聆风