API 客户端:与 Anthropic API 的深度集成

深入 Claude Code 的 API 客户端——流式响应处理、模型选择与降级、Beta API、Token 计数与成本追踪、重试逻辑

问题引入

当你在 Claude Code 中输入一个问题,按下回车的那一刻,一连串精密的操作开始执行:系统构建消息数组,选择合适的模型,添加 Beta 头,通过 SSE 流接收响应,实时解析 token 使用量,计算费用,处理可能的 429/529 错误,必要时降级到备选模型。这一切在 1-2 秒内完成,用户只看到文本开始流出。

Claude Code 的 API 客户端不是简单的 HTTP 封装——它是一个包含重试、降级、缓存、成本追踪、多 Provider 适配的复杂系统。本文深入分析这个系统的每一层。


API 客户端层次结构

应用层
query.ts (主循环)
claude.ts (API 编排)
重试层
withRetry.ts
FallbackTriggeredError
CannotRetryError
SDK 层
client.ts (SDK 初始化)
Anthropic SDK
AWS Bedrock
GCP Vertex AI
Azure Foundry
辅助层
cost-tracker.ts
promptCacheBreakDetection.ts
bootstrap.ts

多 Provider 客户端

Claude Code 支持四种 API Provider,每种有不同的认证和配置方式:

src/services/api/client.ts
TypeScript
1// Direct API:
2// ANTHROPIC_API_KEY: Required for direct API access
3//
4// AWS Bedrock:
5// AWS credentials configured via aws-sdk defaults
6// AWS_REGION or AWS_DEFAULT_REGION
7// ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION: Optional override for Haiku
8//
9// Foundry (Azure):
10// ANTHROPIC_FOUNDRY_RESOURCE: Azure resource name
11// ANTHROPIC_FOUNDRY_BASE_URL: Alternative full base URL
12//
13// Vertex AI:
14// Model-specific region variables (VERTEX_REGION_CLAUDE_*)
15// CLOUD_ML_REGION: Default GCP region
16// ANTHROPIC_VERTEX_PROJECT_ID: Required GCP project ID

客户端初始化考虑了调试需求——当标准错误输出为调试模式时,SDK 日志会重定向到 stderr:

src/services/api/client.ts
TypeScript
1function createStderrLogger(): ClientOptions['logger'] {
2 return {
3 error: (msg, ...args) =>
4 console.error('[Anthropic SDK ERROR]', msg, ...args),
5 warn: (msg, ...args) =>
6 console.error('[Anthropic SDK WARN]', msg, ...args),
7 info: (msg, ...args) =>
8 console.error('[Anthropic SDK INFO]', msg, ...args),
9 debug: (msg, ...args) =>
10 console.error('[Anthropic SDK DEBUG]', msg, ...args),
11 }
12}

Beta Headers 管理

Claude Code 使用了大量 Beta API 功能,通过 anthropic-beta header 声明:

src/services/api/claude.ts
TypeScript
1import {
2 AFK_MODE_BETA_HEADER,
3 CONTEXT_1M_BETA_HEADER,
4 CONTEXT_MANAGEMENT_BETA_HEADER,
5 EFFORT_BETA_HEADER,
6 FAST_MODE_BETA_HEADER,
7 PROMPT_CACHING_SCOPE_BETA_HEADER,
8 REDACT_THINKING_BETA_HEADER,
9 STRUCTURED_OUTPUTS_BETA_HEADER,
10 TASK_BUDGETS_BETA_HEADER,
11} from 'src/constants/betas.js'

这些 Beta 功能包括:

Beta Header功能
CONTEXT_1M_BETA_HEADER1M token 上下文窗口
CONTEXT_MANAGEMENT_BETA_HEADERAPI 端上下文管理
FAST_MODE_BETA_HEADER快速模式(降低延迟)
EFFORT_BETA_HEADEREffort 控制(调整推理深度)
PROMPT_CACHING_SCOPE_BETA_HEADERPrompt 缓存作用域
REDACT_THINKING_BETA_HEADERThinking 内容脱敏
STRUCTURED_OUTPUTS_BETA_HEADER结构化输出
TASK_BUDGETS_BETA_HEADER任务预算控制
AFK_MODE_BETA_HEADER离开模式(后台运行优化)

Extra Body 参数

用户可以通过 CLAUDE_CODE_EXTRA_BODY 环境变量注入额外的 API 参数:

src/services/api/claude.ts
TypeScript
1export function getExtraBodyParams(betaHeaders?: string[]): JsonObject {
2 const extraBodyStr = process.env.CLAUDE_CODE_EXTRA_BODY
3 let result: JsonObject = {}
4
5 if (extraBodyStr) {
6 try {
7 const parsed = safeParseJSON(extraBodyStr)
8 if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
9 // Shallow clone — safeParseJSON is LRU-cached and returns the
10 // same object reference. Mutating result would poison the cache.
11 result = { ...(parsed as JsonObject) }
12 }
13 } catch (error) {
14 logForDebugging(`Error parsing CLAUDE_CODE_EXTRA_BODY: ${errorMessage(error)}`)
15 }
16 }
17
18 // Anti-distillation: send fake_tools opt-in for 1P CLI only
19 if (feature('ANTI_DISTILLATION_CC') ? /* gate check */ : false) {
20 result.anti_distillation = ['fake_tools']
21 }
22
23 return result
24}

注意 shallow clone——safeParseJSON 使用 LRU 缓存,直接修改返回值会污染缓存,导致后续调用看到被修改的值。


Prompt 缓存控制

Prompt 缓存可以按模型粒度控制:

src/services/api/claude.ts
TypeScript
1export function getPromptCachingEnabled(model: string): boolean {
2 if (isEnvTruthy(process.env.DISABLE_PROMPT_CACHING)) return false
3 if (isEnvTruthy(process.env.DISABLE_PROMPT_CACHING_HAIKU)) {
4 if (model === getSmallFastModel()) return false
5 }
6 if (isEnvTruthy(process.env.DISABLE_PROMPT_CACHING_SONNET)) {
7 if (model === getDefaultSonnetModel()) return false
8 }
9 // ...
10}

这种按模型禁用的设计源于实际需求——某些模型的缓存创建成本可能不划算(比如 Haiku 本身就很便宜,缓存的创建费用反而更高)。


重试系统

重试逻辑是 API 客户端最复杂的部分,定义在 withRetry.ts 中。

重试配置

src/services/api/withRetry.ts
TypeScript
1const DEFAULT_MAX_RETRIES = 10
2const FLOOR_OUTPUT_TOKENS = 3000
3const MAX_529_RETRIES = 3
4export const BASE_DELAY_MS = 500

前台 vs 后台查询源

不是所有查询都应该重试。后台查询(摘要、标题生成、分类器)在 529 错误时立即放弃——它们不是用户在等待的结果,重试只会放大容量级联:

src/services/api/withRetry.ts
TypeScript
1const FOREGROUND_529_RETRY_SOURCES = new Set<QuerySource>([
2 'repl_main_thread',
3 'repl_main_thread:outputStyle:custom',
4 'repl_main_thread:outputStyle:Explanatory',
5 'repl_main_thread:outputStyle:Learning',
6 'sdk',
7 'agent:custom',
8 'agent:default',
9 'agent:builtin',
10 'compact',
11 'hook_agent',
12 'hook_prompt',
13 'verification_agent',
14 'side_question',
15 'auto_mode',
16])

重试状态机

...

Fast Mode 降级

Fast Mode 是一种低延迟模式。当遇到限流时,系统需要决定是等待(保持缓存命中)还是降级(切换到标准速度):

src/services/api/withRetry.ts
TypeScript
1if (wasFastModeActive && !isPersistentRetryEnabled() &&
2 error instanceof APIError &&
3 (error.status === 429 || is529Error(error))) {
4 // Overage 限制——永久禁用 fast mode
5 const overageReason = error.headers?.get(
6 'anthropic-ratelimit-unified-overage-disabled-reason',
7 )
8 if (overageReason !== null && overageReason !== undefined) {
9 handleFastModeOverageRejection(overageReason)
10 retryContext.fastMode = false
11 continue
12 }
13
14 const retryAfterMs = getRetryAfterMs(error)
15 if (retryAfterMs !== null && retryAfterMs < SHORT_RETRY_THRESHOLD_MS) {
16 // 短等待——保持 fast mode 以保护 prompt cache
17 await sleep(retryAfterMs, options.signal, { abortError })
18 continue
19 }
20
21 // 长等待或未知——进入冷却期(切换到标准速度)
22 const cooldownMs = Math.max(
23 retryAfterMs ?? DEFAULT_FAST_MODE_FALLBACK_HOLD_MS,
24 MIN_COOLDOWN_MS,
25 )
26 triggerFastModeCooldown(Date.now() + cooldownMs, cooldownReason)
27 retryContext.fastMode = false
28 continue
29}

决策逻辑:

  • retry-after < 阈值 → 短等待,保持 fast mode(保护 prompt cache 不失效)
  • retry-after >= 阈值或未知 → 进入冷却期,切换标准速度
  • Overage 限制 → 永久禁用 fast mode

认证错误恢复

src/services/api/withRetry.ts
TypeScript
1const isStaleConnection = isStaleConnectionError(lastError)
2if (isStaleConnection && getFeatureValue_CACHED_MAY_BE_STALE(...)) {
3 disableKeepAlive() // 禁用连接池,重建连接
4}
5
6if (
7 client === null ||
8 (lastError instanceof APIError && lastError.status === 401) ||
9 isOAuthTokenRevokedError(lastError) ||
10 isBedrockAuthError(lastError) ||
11 isVertexAuthError(lastError) ||
12 isStaleConnection
13) {
14 if ((lastError instanceof APIError && lastError.status === 401) ||
15 isOAuthTokenRevokedError(lastError)) {
16 const failedAccessToken = getClaudeAIOAuthTokens()?.accessToken
17 if (failedAccessToken) {
18 await handleOAuth401Error(failedAccessToken)
19 }
20 }
21 client = await getClient() // 重建客户端
22}

认证恢复涵盖了所有 Provider 的特殊情况:

  • Anthropic 1P — 401 时刷新 OAuth token
  • AWS Bedrock — 403 或 CredentialsProviderError
  • GCP Vertex — 凭证刷新失败
  • 连接重置 — ECONNRESET/EPIPE 时禁用 keep-alive 并重连

529 连续错误与模型降级

src/services/api/withRetry.ts
TypeScript
1if (is529Error(error) &&
2 (process.env.FALLBACK_FOR_ALL_PRIMARY_MODELS ||
3 (!isClaudeAISubscriber() && isNonCustomOpusModel(options.model)))) {
4 consecutive529Errors++
5 if (consecutive529Errors >= MAX_529_RETRIES) {
6 if (options.fallbackModel) {
7 throw new FallbackTriggeredError(
8 options.model,
9 options.fallbackModel,
10 )
11 }
12 }
13}

连续 3 次 529 后触发模型降级(如 Opus → Sonnet)。FallbackTriggeredErrorquery.ts 捕获并处理——清除已有的 assistant 消息,切换模型,重试整个请求。

持久化重试(无人值守模式)

对于自动化场景(CI/CD、cron 任务),系统支持无限重试:

src/services/api/withRetry.ts
TypeScript
1const PERSISTENT_MAX_BACKOFF_MS = 5 * 60 * 1000 // 5 分钟最大退避
2const PERSISTENT_RESET_CAP_MS = 6 * 60 * 60 * 1000 // 6 小时超时
3const HEARTBEAT_INTERVAL_MS = 30_000 // 30 秒心跳
4
5function isPersistentRetryEnabled(): boolean {
6 return feature('UNATTENDED_RETRY')
7 ? isEnvTruthy(process.env.CLAUDE_CODE_UNATTENDED_RETRY)
8 : false
9}

持久化重试通过 SystemAPIErrorMessage 发送心跳,防止宿主环境(如容器编排系统)将会话标记为空闲。


成本追踪

每次 API 响应都会更新成本状态:

src/cost-tracker.ts
TypeScript
1type StoredCostState = {
2 totalCostUSD: number
3 totalAPIDuration: number
4 totalAPIDurationWithoutRetries: number
5 totalToolDuration: number
6 totalLinesAdded: number
7 totalLinesRemoved: number
8 lastDuration: number | undefined
9 modelUsage: { [modelName: string]: ModelUsage } | undefined
10}

成本计算通过 calculateUSDCost 函数基于每个模型的价格表:

src/services/api/claude.ts
TypeScript
1import { addToTotalSessionCost } from 'src/cost-tracker.js'

成本状态不仅用于显示——它在会话切换时保存到项目配置,恢复时读回:

src/cost-tracker.ts
TypeScript
1export function saveCurrentSessionCosts(fpsMetrics?: FpsMetrics): void {
2 saveCurrentProjectConfig(current => ({
3 ...current,
4 lastCost: getTotalCostUSD(),
5 lastAPIDuration: getTotalAPIDuration(),
6 lastAPIDurationWithoutRetries: getTotalAPIDurationWithoutRetries(),
7 lastToolDuration: getTotalToolDuration(),
8 lastDuration: getTotalDuration(),
9 // ...
10 }))
11}

Bootstrap API

启动时,系统通过 Bootstrap API 获取服务端配置:

src/services/api/bootstrap.ts
TypeScript
1async function fetchBootstrapAPI(): Promise<BootstrapResponse | null> {
2 if (isEssentialTrafficOnly()) return null // 隐私模式跳过
3 if (getAPIProvider() !== 'firstParty') return null // 第三方 Provider 跳过
4
5 // OAuth 优先,API Key 回退
6 const hasUsableOAuth =
7 getClaudeAIOAuthTokens()?.accessToken && hasProfileScope()
8 if (!hasUsableOAuth && !apiKey) return null
9
10 const endpoint = `${getOauthConfig().BASE_API_URL}/api/claude_cli/bootstrap`
11
12 return await withOAuth401Retry(async () => {
13 const token = getClaudeAIOAuthTokens()?.accessToken
14 // 每次重读 OAuth token(retry 可能已刷新)
15 let authHeaders: Record<string, string>
16 if (token && hasProfileScope()) {
17 authHeaders = { Authorization: `Bearer ${token}`, ... }
18 } else if (apiKey) {
19 authHeaders = { 'x-api-key': apiKey }
20 } else {
21 return null
22 }
23
24 const response = await axios.get(endpoint, {
25 headers: { ...authHeaders },
26 timeout: 5000,
27 })
28 return bootstrapResponseSchema().safeParse(response.data)
29 })
30}

Bootstrap 返回的数据包括:

  • client_data — 客户端配置
  • additional_model_options — 额外可用模型列表

5 秒超时确保启动不会因为网络问题而卡住。


流式响应处理

query.ts 中的主循环通过 for await...of 消费流式响应。关键的处理逻辑包括:

Fallback 处理

当流式传输过程中触发模型降级时,已收到的部分消息需要被丢弃:

src/query.ts
TypeScript
1if (streamingFallbackOccured) {
2 // 为已发出的消息生成 tombstone
3 for (const msg of assistantMessages) {
4 yield { type: 'tombstone' as const, message: msg }
5 }
6
7 assistantMessages.length = 0
8 toolResults.length = 0
9 toolUseBlocks.length = 0
10 needsFollowUp = false
11
12 // 丢弃流式工具执行器的待处理结果
13 if (streamingToolExecutor) {
14 streamingToolExecutor.discard()
15 streamingToolExecutor = new StreamingToolExecutor(
16 toolUseContext.options.tools,
17 canUseTool,
18 toolUseContext,
19 )
20 }
21}

Tombstone 消息告诉 UI 和 transcript 移除这些部分消息——特别重要的是移除不完整的 thinking blocks,因为它们带有模型特定的签名,在降级到不同模型后会导致 API 错误。

错误抑制与恢复

某些 API 错误是可恢复的——系统在流式循环中抑制这些错误,在流结束后尝试恢复:

src/query.ts
TypeScript
1let withheld = false
2if (feature('CONTEXT_COLLAPSE')) {
3 if (contextCollapse?.isWithheldPromptTooLong(message, ...)) {
4 withheld = true
5 }
6}
7if (reactiveCompact?.isWithheldPromptTooLong(message)) {
8 withheld = true
9}
10if (mediaRecoveryEnabled && reactiveCompact?.isWithheldMediaSizeError(message)) {
11 withheld = true
12}
13if (isWithheldMaxOutputTokens(message)) {
14 withheld = true
15}
16if (!withheld) {
17 yield yieldMessage
18}

被抑制的消息仍然加入 assistantMessages 数组——恢复逻辑需要检查它们。但不会发送给 SDK 消费者,因为这些消费者(如桌面应用)可能在看到错误后终止会话。


请求构建细节

Tool Schema 转换

每个工具的定义需要转换为 API 兼容的格式,包含 deferred tools 的处理:

TypeScript
1// 引用: src/services/api/claude.ts
2import {
3 formatDeferredToolLine,
4 isDeferredTool,
5 TOOL_SEARCH_TOOL_NAME,
6} from '../../tools/ToolSearchTool/prompt.js'

Advisor 模式

当启用 Advisor 时,额外的模型(如 Opus 作为 Sonnet 的顾问)会参与决策:

src/services/api/claude.ts
TypeScript
1import {
2 ADVISOR_TOOL_INSTRUCTIONS,
3 getExperimentAdvisorModels,
4 isAdvisorEnabled,
5 isValidAdvisorModel,
6 modelSupportsAdvisor,
7} from 'src/utils/advisor.js'

Session Activity 追踪

API 请求期间会标记 session 为活跃状态,用于远程环境的资源管理:

src/services/api/claude.ts
TypeScript
1import {
2 startSessionActivity,
3 stopSessionActivity,
4} from '../../utils/sessionActivity.js'

小结

Claude Code 的 API 客户端是一个多层防御系统:

  • 多 Provider 抽象 — Anthropic/Bedrock/Vertex/Foundry 统一接口,环境变量配置
  • 分层重试 — 按错误类型(认证/限流/过载/连接重置)采取不同策略
  • 智能降级 — Fast Mode → 标准速度 → 备选模型,每一步都有合理的决策逻辑
  • 流式错误抑制 — 可恢复错误不立即暴露给消费者,给系统恢复的机会
  • 成本全链路追踪 — 从 API 响应到项目配置持久化,支持会话恢复
  • 运维旋钮 — Prompt 缓存、Fast Mode、重试策略等都可通过环境变量和 Feature Flag 控制

这个系统的复杂性不是偶然的——它反映了生产环境 AI 应用面临的现实:网络不可靠、服务会过载、认证会过期、用户需要不间断的体验。每一层防护都对应着一个真实的故障模式。