目录
  1. 1. 1. 什么是”本地模型部署”
    1. 1.1. 1.1 两种执行模式对比
  2. 2. 2. 原版的完整 API 请求链路
    1. 2.1. 2.1 调用栈(从用户输入到网络请求)
    2. 2.2. 2.2 核心文件对应关系
  3. 3. 3. 本地模型接入机制
    1. 3.1. 3.1 唯一注入点:ANTHROPIC_BASE_URL
    2. 3.2. 3.2 官方支持的 4 种提供商
  4. 4. 4. 重要:接入本地模型时的功能降级
    1. 4.1. 4.1 第一方 URL 检测
    2. 4.2. 4.2 被禁用的功能
  5. 5. 5. 重试策略
    1. 5.1. 5.1 重试参数
    2. 5.2. 5.2 重试场景
    3. 5.3. 5.3 流式 → 非流式降级
  6. 6. 6. 接入本地模型的步骤
    1. 6.1. 6.1 必要条件
    2. 6.2. 6.2 推荐方案:LiteLLM Proxy(协议转换)
    3. 6.3. 6.3 端到端验证
    4. 6.4. 6.4 方案二:Ollama 原生 Anthropic 端点(部分支持)
    5. 6.5. 6.5 方案三:vllm(GPU 推理)
  7. 7. 7. 模型选择建议
    1. 7.1. 7.1 工具调用能力强的本地模型
    2. 7.2. 7.2 工具调用常见问题
  8. 8. 8. 配置速查
  9. 9. 9. 完整流程图(LiteLLM 方案)
  10. 10. 10. 与 claw-code Rust 版本的对比
  11. 11. 涉及源文件
【Claude Code源码剖析】18-API 请求链路与本地模型部署

⚠️ 学习声明:本文档基于 Claude Code 2.1.88 源码分析整理,仅供个人学习研究使用,不做任何商业用途。

这是 docs/ 系列中专门讲解 TypeScript 原版如何发起 LLM 请求,以及如何接入本地模型的文档。

先厘清一个概念:本地模型部署不是”把算法部署到本地”,而是”把模型的推理服务跑在本地机器上”。
Claude Code 本身不包含任何模型权重和推理算法,它只是一个调用 AI API 的客户端


1. 什么是”本地模型部署”

1.1 两种执行模式对比

云端模式(默认):
用户输入 → Claude Code (本地) → HTTPS → api.anthropic.com (Anthropic 云服务器)

GPU 集群运行 claude-opus-4 推理算法

流式 token 响应 ← 返回

本地模式:
用户输入 → Claude Code (本地) → HTTP → 本地推理服务 (ollama / vllm / llama.cpp)

本地 GPU/CPU 运行 qwen / llama / deepseek 等模型

流式 token 响应 ← 返回

关键点:Claude Code 没有推理算法,模型的智能(语言理解、代码生成、推理)全部在推理服务(云端或本地)内完成。Claude Code 只负责:

  • 组织提示词(system prompt + 对话历史 + 工具定义)
  • 发送 HTTP 请求
  • 解析 SSE 流式响应
  • 执行模型请求的工具调用(bash/文件读写等)

2. 原版的完整 API 请求链路

2.1 调用栈(从用户输入到网络请求)

用户在 REPL 输入内容


query.ts → queryModel() ← 主查询入口(第 1017 行)

├─ 组装 system prompt
├─ 标准化消息历史(normalizeMessagesForAPI)
├─ 构建工具 schema(toolToAPISchema)
├─ 处理 Prompt Cache 标记


services/api/claude.ts → paramsFromContext() ← 构造完整请求参数

├─ model
├─ messages(完整对话历史)
├─ system(系统提示词块)
├─ tools(工具定义数组)
├─ max_tokens
├─ betas(Beta 功能头)
├─ thinking(扩展思考配置)
├─ metadata(设备 ID 等)


services/api/withRetry.ts → withRetry() ← 重试包装器


services/api/client.ts → getAnthropicClient() ← 创建 HTTP 客户端

├─ Bedrock? → AnthropicBedrock
├─ Foundry? → AnthropicFoundry
├─ Vertex? → AnthropicVertex
└─ 默认 → new Anthropic({ baseURL: ANTHROPIC_BASE_URL })

这里就是本地模型的注入点


anthropic.beta.messages.create({ ..., stream: true }) ← 发出 HTTP 请求


解析 SSE 流:BetaRawMessageStreamEvent → StreamEvent → UI 渲染

2.2 核心文件对应关系

文件 职责
src/query.ts 查询主循环,工具执行,消息管理
src/services/api/claude.ts 请求构造、流式处理、重试逻辑(3420行)
src/services/api/client.ts SDK 客户端创建,支持 4 种提供商
src/services/api/withRetry.ts 指数退避重试、429/529 处理
src/utils/model/providers.ts 判断 Provider 类型,是否一方地址

3. 本地模型接入机制

3.1 唯一注入点:ANTHROPIC_BASE_URL

client.ts 第 315 行(默认路径):

// client.ts
const clientConfig = {
apiKey: ...,
// 当 ANTHROPIC_BASE_URL 设置时,SDK 自动使用此地址替代 api.anthropic.com
// SDK 源码:new Anthropic({ baseURL: process.env.ANTHROPIC_BASE_URL })
...ARGS,
}
return new Anthropic(clientConfig)

@anthropic-ai/sdk 内部会读取 ANTHROPIC_BASE_URL 环境变量,将所有请求从 https://api.anthropic.com/v1/messages 重定向到你设置的地址。

这是唯一的 base URL 控制点。原版没有 OPENAI_BASE_URL 等其他协议的支持。

3.2 官方支持的 4 种提供商

// client.ts 判断逻辑
if (CLAUDE_CODE_USE_BEDROCK) → AnthropicBedrock (AWS)
if (CLAUDE_CODE_USE_FOUNDRY) → AnthropicFoundry (Azure)
if (CLAUDE_CODE_USE_VERTEX) → AnthropicVertex (GCP)
默认 → new Anthropic({ baseURL: ANTHROPIC_BASE_URL })

Bedrock/Foundry/Vertex 是企业云托管,不是”本地”。真正的本地模型只通过默认路径 + ANTHROPIC_BASE_URL 接入。


4. 重要:接入本地模型时的功能降级

这是使用原版接入本地模型必须了解的限制。

4.1 第一方 URL 检测

// src/utils/model/providers.ts
export function isFirstPartyAnthropicBaseUrl(): boolean {
const baseUrl = process.env.ANTHROPIC_BASE_URL
if (!baseUrl) return true // 未设置 → 认为是官方
try {
const host = new URL(baseUrl).host
// 只有这两个域名被认为是"第一方"
const allowedHosts = ['api.anthropic.com']
if (process.env.USER_TYPE === 'ant') {
allowedHosts.push('api-staging.anthropic.com')
}
return allowedHosts.includes(host)
} catch { return false }
}

任何非 api.anthropic.com 的地址(包括 localhost、内网 IP、LiteLLM 代理)都被判定为”第三方”。

4.2 被禁用的功能

Fine-Grained Tool Streaming (FGTS)

// claude.ts ~第 196 行
if (
getAPIProvider() === 'firstParty' &&
isFirstPartyAnthropicBaseUrl() && // 本地模型 = false,直接跳过
(getFeatureValue_CACHED(...) || isEnvTruthy(CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING))
) {
base.eager_input_streaming = true // 仅第一方启用
}

FGTS 关闭时:工具调用的参数 JSON 需要等模型完整生成后才开始执行,大型工具输入(如写长文件)可能导致明显延迟。

Tool Search 乐观模式

// src/utils/toolSearch.ts
if (!isFirstPartyAnthropicBaseUrl()) {
// 自动禁用,需手动设置 ENABLE_TOOL_SEARCH=true 才能强制开启
logForDebugging(`[ToolSearch:optimistic] disabled: ANTHROPIC_BASE_URL is not first-party...`)
return false
}

Client Request ID 注入

// claude.ts ~第 1820 行
clientRequestId =
getAPIProvider() === 'firstParty' && isFirstPartyAnthropicBaseUrl()
? randomUUID()
: undefined // 本地模式不注入

5. 重试策略

源码:src/services/api/withRetry.ts(823 行)

5.1 重试参数

参数
DEFAULT_MAX_RETRIES 10 次
BASE_DELAY_MS 500ms
MAX_529_RETRIES 3 次(过载后最多重试 3 次)

5.2 重试场景

错误类型 处理方式
429 Too Many Requests 指数退避重试(前台查询)
529 Overloaded 最多 3 次,超出后 fallback 到非流式
APIConnectionError (ECONNRESET) 重置连接后重试
auth_error 刷新 OAuth token 后重试
context_window_exceeded 减少 max_tokens 后重试
APIUserAbortError 立即抛出,不重试

5.3 流式 → 非流式降级

当流式请求持续失败(529)时,自动降级为非流式请求:

anthropic.beta.messages.create({ stream: true })  ← 流式请求失败

▼(连续 529 超过 MAX_529_RETRIES)
anthropic.beta.messages.create({ stream: false }) ← 非流式降级
(最大超时 300s,远程 session 120s)

6. 接入本地模型的步骤

6.1 必要条件

本地推理服务必须实现 Anthropic Messages API(因为原版只用 @anthropic-ai/sdk,该 SDK 只支持 Anthropic 协议):

POST /v1/messages
Content-Type: application/json

{
"model": "...",
"messages": [...],
"system": "...",
"tools": [...],
"max_tokens": 8096,
"stream": true
}

6.2 推荐方案:LiteLLM Proxy(协议转换)

LiteLLM 把任意模型的接口转换为 Anthropic Messages API:

# Step 1:安装 LiteLLM
pip install litellm[proxy]

# Step 2:创建配置文件
cat > litellm_config.yaml << 'EOF'
model_list:
- model_name: "qwen-coder" # 在 Claude Code 里用的模型名
litellm_params:
model: "ollama/qwen2.5-coder:14b"
api_base: "http://localhost:11434"

- model_name: "deepseek-coder"
litellm_params:
model: "ollama/deepseek-coder-v2:16b"
api_base: "http://localhost:11434"

- model_name: "llama3"
litellm_params:
model: "ollama/llama3.1:8b"
api_base: "http://localhost:11434"

general_settings:
master_key: "sk-local-dev" # 自定义 API Key
drop_params: true # 忽略不支持的参数(重要!)
EOF

# Step 3:启动 LiteLLM(它在 4000 端口实现 Anthropic Messages API)
litellm --config litellm_config.yaml --port 4000
# 输出:LiteLLM running on http://0.0.0.0:4000

# Step 4:配置 Claude Code
export ANTHROPIC_API_KEY="sk-local-dev"
export ANTHROPIC_BASE_URL="http://localhost:4000"

# Step 5:启动 Claude Code,指定模型名(必须与 litellm 配置一致)
claude --model qwen-coder
# 或
claude # 然后用 /model qwen-coder 切换

6.3 端到端验证

# 先验证 LiteLLM 是否正常(用 curl 模拟 Anthropic 请求)
curl http://localhost:4000/v1/messages \
-H "x-api-key: sk-local-dev" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "qwen-coder",
"max_tokens": 100,
"messages": [{"role": "user", "content": "Hello"}]
}'

# 响应应包含 {"type": "message", "content": [...], ...}

6.4 方案二:Ollama 原生 Anthropic 端点(部分支持)

Ollama v0.3+ 提供了实验性的 Anthropic 兼容端点:

ollama pull qwen2.5-coder:14b

export ANTHROPIC_API_KEY="ollama"
export ANTHROPIC_BASE_URL="http://localhost:11434"
claude --model qwen2.5-coder:14b

⚠️ 注意:Ollama 的 Anthropic 兼容性不完整。工具调用(Function Calling)的格式与官方规范有细微差异,可能导致工具调用失败。实践中推荐用 LiteLLM 做适配层。

6.5 方案三:vllm(GPU 推理)

# 启动 vllm 的 Anthropic 兼容服务器
pip install vllm
python -m vllm.entrypoints.anthropic.api_server \
--model Qwen/Qwen2.5-Coder-14B-Instruct \
--served-model-name "qwen-coder-14b" \
--host 0.0.0.0 \
--port 8001 \
--tensor-parallel-size 2 # 2 张 GPU

export ANTHROPIC_API_KEY="vllm"
export ANTHROPIC_BASE_URL="http://localhost:8001"
claude --model qwen-coder-14b

7. 模型选择建议

接入本地模型后,Claude Code 的所有工具调用(bash、文件读写、grep 等)都需要模型支持 Function Calling / Tool Use

7.1 工具调用能力强的本地模型

模型 推荐用途 最小显存(4bit量化)
qwen2.5-coder:14b 代码相关任务 10GB
deepseek-coder-v2:16b 代码 + 中文 12GB
qwen2.5:32b 通用 + 代码 22GB
llama3.1:70b 通用强模型 42GB
codestral:22b 代码专用 15GB

7.2 工具调用常见问题

症状:模型响应了文本但没有调用工具,任务无法推进

原因:本地模型的工具调用指令遵循能力弱,或工具定义格式与训练数据不匹配

解决方案

  1. 换用更大参数量的模型(14B → 32B)
  2. LiteLLM 开启 drop_params: true 避免不支持的参数报错
  3. 减少工具数量(原版工具定义较多,小模型处理复杂 schema 时容易出错)

8. 配置速查

# 最小配置(LiteLLM + Ollama)
export ANTHROPIC_API_KEY="sk-local-any-value"
export ANTHROPIC_BASE_URL="http://localhost:4000" # LiteLLM 地址
claude --model <你在 litellm.yaml 里定义的 model_name>

# 调试模式(查看发出的请求)
export ANTHROPIC_API_KEY="sk-local"
export ANTHROPIC_BASE_URL="http://localhost:4000"
DEBUG=1 claude --model qwen-coder 2>&1 | grep "\[API"

# 强制开启 Tool Search(第三方代理默认禁用)
export ENABLE_TOOL_SEARCH=true
claude --model qwen-coder

# 调整 API 超时(本地 GPU 推理可能较慢)
export API_TIMEOUT_MS=900000 # 15 分钟
claude --model qwen-coder

# 禁用扩展思考(大部分本地模型不支持)
export CLAUDE_CODE_DISABLE_THINKING=1
claude --model qwen-coder

9. 完整流程图(LiteLLM 方案)

┌─────────────────────┐
│ Claude Code 2.1.88 │
│ (TypeScript + Bun) │
└──────────┬──────────┘
│ ANTHROPIC_BASE_URL=http://localhost:4000
│ 使用 Anthropic Messages API 协议

┌─────────────────────┐
│ LiteLLM Proxy │
│ localhost:4000 │
│ │
│ Anthropic API → │
│ OpenAI API 转换 │
└──────────┬──────────┘
│ OpenAI Chat Completions 协议
│ http://localhost:11434/v1

┌─────────────────────┐
│ Ollama 推理服务 │
│ localhost:11434 │
│ │
│ qwen2.5-coder:14b │
│ (模型权重在本地) │
└─────────────────────┘

10. 与 claw-code Rust 版本的对比

维度 原版 2.1.88 claw-code Rust
协议 仅 Anthropic Messages API Anthropic + OpenAI Chat Completions
直连 Ollama ❌ 需 LiteLLM 转换 ✅ 原生支持
直连 vllm ❌ 需配置 Anthropic 模式 OPENAI_BASE_URL 直连
功能降级 接入本地模型时 FGTS/ToolSearch 降级 无降级
配置复杂度 需额外安装配置 LiteLLM 仅设两个环境变量
Node.js 依赖 必需

涉及源文件

  • services/api/claude.ts
  • services/api/client.ts
  • services/api/withRetry.ts
  • src/query.ts
  • src/services/api/claude.ts
  • src/services/api/client.ts
  • src/services/api/withRetry.ts
  • src/utils/model/providers.ts
  • src/utils/toolSearch.ts
打赏
  • 微信
  • 支付宝

评论