这是 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 行(默认路径):
const clientConfig = { apiKey: ..., ...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 种提供商
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 检测
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)
if ( getAPIProvider() === 'firstParty' && isFirstPartyAnthropicBaseUrl() && (getFeatureValue_CACHED(...) || isEnvTruthy(CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING)) ) { base.eager_input_streaming = true }
|
FGTS 关闭时:工具调用的参数 JSON 需要等模型完整生成后才开始执行,大型工具输入(如写长文件)可能导致明显延迟。
Tool Search 乐观模式
if (!isFirstPartyAnthropicBaseUrl()) { logForDebugging(`[ToolSearch:optimistic] disabled: ANTHROPIC_BASE_URL is not first-party...`) return false }
|
Client Request ID 注入
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:
pip install litellm[proxy]
cat > litellm_config.yaml << 'EOF' model_list: - model_name: "qwen-coder" 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" drop_params: true EOF
litellm --config litellm_config.yaml --port 4000
export ANTHROPIC_API_KEY="sk-local-dev" export ANTHROPIC_BASE_URL="http://localhost:4000"
claude --model qwen-coder
claude
|
6.3 端到端验证
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"}] }'
|
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 推理)
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
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 工具调用常见问题
症状:模型响应了文本但没有调用工具,任务无法推进
原因:本地模型的工具调用指令遵循能力弱,或工具定义格式与训练数据不匹配
解决方案:
- 换用更大参数量的模型(14B → 32B)
- LiteLLM 开启
drop_params: true 避免不支持的参数报错
- 减少工具数量(原版工具定义较多,小模型处理复杂 schema 时容易出错)
8. 配置速查
export ANTHROPIC_API_KEY="sk-local-any-value" export ANTHROPIC_BASE_URL="http://localhost:4000" 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"
export ENABLE_TOOL_SEARCH=true claude --model qwen-coder
export API_TIMEOUT_MS=900000 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 依赖 |
必需 |
无 |