详解 Claude Code 源码
赛博浮世绘 · 晓风乾 · 24 站拆解
回首页
详解 Claude Code 核心引擎 2.3 · API 层与看门狗
篇章二 · 2.3

API 层与看门狗

循环的第 2 步会走进 services/api/claude.ts——整个系统里离 Anthropic API 最近的地方。这里藏着两个反直觉的决定。

不用官方封装,自己处理裸流

Anthropic SDK 明明提供了 BetaMessageStream(官方流式封装),Claude Code 偏偏不用,选择手动处理 raw stream。源码注释给出了理由:

“Use raw stream instead of BetaMessageStream to avoid O(n²) partial JSON parsing. BetaMessageStream calls partialParse() on every input_json_delta, which we don’t need since we handle tool input accumulation ourselves.”

—— 源码注释,services/api/claude.ts

翻译:官方封装每收到一个增量就对整块 JSON 做部分解析,数据量越大越慢(平方级);Claude Code 自己攒增量,只在块结束时解析一次——线性。

同一段代码,四种云
后端开关(环境变量)客户端
Anthropic 直连默认new Anthropic(…)
AWS BedrockCLAUDE_CODE_USE_BEDROCKAnthropicBedrock
Google VertexCLAUDE_CODE_USE_VERTEXAnthropicVertex
Azure FoundryCLAUDE_CODE_USE_FOUNDRYAnthropicFoundry

企业用什么云,一个环境变量切换——这决定了它能在各大厂内部落地。

手动消费 6 种 SSE 事件
message_start会话开始:初始化 usage,记录首字节时间(TTFB)
content_block_start开一个内容块:text / tool_use / thinking
content_block_delta增量追加(最频繁——性能敏感就在这)
content_block_stop块结束:组装成 AssistantMessage,yield 给上层
message_delta更新 usage 与 stop_reason
message_stop消息结束:记录成本
SSE 组装器 · 消息是怎么被攒出来的 自动播放或逐步:左进事件,右出消息
事件流(左:API 吐出的)
组装区(右:攒给 queryLoop 的)
等 message_start…
idle
90 秒看门狗 演示版缩短为 10 秒——狂点喂狗,停手看它咬人
streaming…(每个 chunk 重置计时)
真实参数:STREAM_IDLE_TIMEOUT_MS = 90,000(无 chunk 超时 abort);45 秒处(一半)打 warning 日志。超时后置 streamIdleAborted 标记并释放流资源——不是崩溃,是体面退出。
tombstone · 流式失败时的「讣告」

流式中途失败会回退到非流式重发(超时 300s / 远程 120s)。但上层已经收到半截消息了——直接重发会导致 API 报错 thinking blocks cannot be modified。解法:重发前先 yield 一条墓碑消息:

{ type: 'tombstone', message }  ← 上层见到墓碑,删掉此前那条半截消息

一句话:流式世界里「部分结果」是个脏状态,tombstone 负责把它撤销干净再重来。

本站要点:性能(拒绝 O(n²))、兼容(四云一码)、韧性(看门狗 + tombstone)——API 层三个关键词。下一站:请求失败了怎么办——退避公式、五种错误码对策、Fallback 降级链。