1. 为什么要在本地跑 Claude Code
Claude Code 是目前终端里体验最顺手的编码 Agent 之一,但直接连官方服务有两个现实问题:一是网络链路不稳定,二是长会话的 Token 消耗很快。如果你手上正好有一台带 GPU 的机器,或者像 GB10 这类小盒子,把模型放到本地跑,再让 Claude Code 指过去,就能把这两件事一起解决。
核心思路其实就一句话:Claude Code 认的是ANTHROPIC_BASE_URL这个环境变量,只要有一个能说 Anthropic 协议的网关顶在前面,后面接什么模型都行。本地大模型(比如 Qwen3-Coder-30B)通常只暴露 OpenAI 风格的/v1/chat/completions,而 Claude Code 会发一些 OpenAI 风格不支持的字段,所以中间需要一个转换层。LiteLLM 就是干这个的,它能把 Anthropic 请求翻译成 OpenAI 请求,再转发给 TensorRT-LLM 或 vLLM 起的推理服务。
这篇面向的是已经能在本地把模型跑起来、但卡在 Claude Code 接入这一步的开发者。我会给出可复制的settings.json骨架、LiteLLM 的config.yaml、连通性验证命令,以及几个我实际踩过的报错。如果你本地模型还没部署,先把推理服务跑通再回来,后面的配置才有意义。
另外提一句,如果你不想维护本地网关,或者想先用一个统一 Key 把链路跑通再决定要不要本地化,TaoToken 提供了一个兼容 Anthropic 协议的入口,配置方式和本地 LiteLLM 完全一致,只是把ANTHROPIC_BASE_URL换成它的地址即可。下面会分别给出两种写法。
2. TaoToken 前置:统一 Key 与地址准备
在动手改配置之前,先把「Key 从哪来、地址填什么」这件事定下来。Claude Code 需要两个环境变量:ANTHROPIC_BASE_URL和ANTHROPIC_AUTH_TOKEN。前者是网关地址,后者是鉴权令牌。
如果你走 TaoToken 这条线,先去控制台创建一个 API Key。地址是https://taotoken.net/api-keys,登录后在密钥管理页新建一个,复制出来形如sk-开头的字符串。这个 Key 就是后面ANTHROPIC_AUTH_TOKEN的值。对应的ANTHROPIC_BASE_URL填https://taotoken.net/api,注意这里不要带任何查询参数,Claude Code 会自己在后面拼/v1/messages。
如果你走本地 LiteLLM 这条线,ANTHROPIC_AUTH_TOKEN可以随便填一个占位符,比如internal,因为 LiteLLM 默认不校验这个字段(除非你在 config 里开了 master_key)。ANTHROPIC_BASE_URL填http://localhost:4000,端口和你启动 LiteLLM 时指定的保持一致。
两条线的区别只在于地址和 Key 的来源,Claude Code 侧的配置结构完全一样。我建议你先用 TaoToken 把 Claude Code 的配置跑通,确认settings.json写对了,再把地址切到本地 LiteLLM,这样排错时能明确知道问题出在客户端配置还是网关。
有一点要注意:TaoToken 的 Key 和本地 LiteLLM 的占位符不要混用。如果你在settings.json里写了 TaoToken 的 Key,但ANTHROPIC_BASE_URL指向localhost:4000,LiteLLM 会把这个 Key 当成无效凭证透传给后端,报 401。反过来也一样。配置切换时两个变量一起改。
3. 可复制配置:settings.json 与 LiteLLM config.yaml
Claude Code 的配置分两层:一层是 Claude Code 自己的settings.json,决定它往哪个地址发请求;另一层是 LiteLLM 的config.yaml,决定请求怎么翻译、转发给哪个模型。先把 LiteLLM 这层配好。
3.1 LiteLLM config.yaml 骨架
在 Windows 上我习惯把配置放在C:\Users\<你>\.litellm\config.yaml。核心是model_list里把 Claude Code 会请求的模型名映射到本地推理服务的真实模型名。Claude Code 默认会请求claude-sonnet-4-5这类名字,所以你要么在启动时用--model指定,要么在 config 里把这些名字都映射一遍。
model_list: - model_name: qwen3-coder-30b litellm_params: model: openai/Qwen3-Coder-30B-A3B-Instruct api_base: http://192.168.8.20:8100/v1 api_key: internal drop_params: true max_tokens: 66912 - model_name: claude-sonnet-4-5 litellm_params: model: openai/Qwen3-Coder-30B-A3B-Instruct api_base: http://192.168.8.20:8100/v1 api_key: internal drop_params: true max_tokens: 66912 litellm_settings: drop_params: true truncate_prompt_tokens: 66912 suppress_error_logs: true strict_param_validation: false几个参数值得单独说。drop_params: true是关键,Claude Code 会发thinking、metadata这类 OpenAI 不认的字段,不丢弃就会 400。truncate_prompt_tokens设成和推理服务的max_num_tokens一致,避免超长上下文被后端直接拒绝。strict_param_validation: false让 LiteLLM 对未知参数宽容一点,减少调试期的噪音。
api_base指向你本地推理服务的地址。TensorRT-LLM 或 vLLM 起服务时通常会暴露http://<ip>:8100/v1,端口按你实际启动参数改。api_key填internal是因为本地服务一般不校验,但 LiteLLM 要求这个字段非空。
3.2 启动 LiteLLM
Windows 下先激活虚拟环境,再启动。命令如下:
C:\Users\nicex\.litellm\litellm-env\Scripts\Activate.ps1 pip install 'litellm[proxy]' litellm --config C:\Users\nicex\.litellm\config.yaml --port 4000启动后终端会打印一行Uvicorn running on http://0.0.0.0:4000,看到这行说明网关起来了。如果报Address already in use,换个端口,比如--port 4001,同时记得改ANTHROPIC_BASE_URL。
3.3 Claude Code settings.json 骨架
Claude Code 的配置文件在用户目录下的.claude/settings.json。如果你只想临时试,用环境变量也行,但写进settings.json更稳,重启终端不丢。
{ "env": { "ANTHROPIC_BASE_URL": "http://localhost:4000", "ANTHROPIC_AUTH_TOKEN": "internal", "ANTHROPIC_MODEL": "qwen3-coder-30b" } }如果你走 TaoToken,把ANTHROPIC_BASE_URL换成https://taotoken.net/api,ANTHROPIC_AUTH_TOKEN换成你在控制台创建的 Key,ANTHROPIC_MODEL换成你想用的模型名。ANTHROPIC_MODEL这一项是可选的,不写的话启动时用--model指定也行,但写进去省事。
注意:
settings.json里的env字段是 Claude Code 启动时注入的环境变量,优先级高于系统环境变量。如果你之前用$env:ANTHROPIC_BASE_URL设过,记得清掉,否则可能互相覆盖。
4. 验证请求与成功结果
配置写完别急着开 Claude Code,先用 curl 打一下 LiteLLM 的 Anthropic 端点,确认网关能正常翻译。这一步能省掉大量「到底是客户端问题还是网关问题」的纠结。
4.1 验证 LiteLLM 网关
LiteLLM 暴露的 Anthropic 兼容端点是/v1/messages。用 curl 发一个最小请求:
curl -X POST http://localhost:4000/v1/messages \ -H "Content-Type: application/json" \ -H "x-api-key: internal" \ -H "anthropic-version: 2023-06-01" \ -d '{ "model": "qwen3-coder-30b", "max_tokens": 64, "messages": [{"role": "user", "content": "只回复两个字:通了"}] }'如果返回体里有"content": [{"type": "text", "text": "通了"}]这样的结构,说明 LiteLLM 到本地模型的链路是通的。如果返回 400 且提示Unsupported parameter,回去检查drop_params有没有生效。如果返回 500 且提示连接超时,说明api_base填错了,或者本地推理服务没起来。
4.2 验证 Claude Code 侧
网关通了之后,在终端里直接启动 Claude Code:
claude --model qwen3-coder-30b进去之后随便问一句「当前目录有哪些文件」,看它能不能正常调用工具、返回结果。如果 Claude Code 卡在Connecting...不动,多半是ANTHROPIC_BASE_URL没生效,用claude --debug启动能看到它实际请求的地址。
成功的话你会看到 Claude Code 正常输出,并且本地推理服务的日志里能看到请求进来。这时候可以试着让它改一个小文件,验证工具调用链路也是通的。我实测下来,Qwen3-Coder-30B 在 66912 上下文下跑常规编码任务够用,但如果你要它读大文件,上下文还是容易吃紧,truncate_prompt_tokens设小了会截断,设大了后端可能 OOM,这个值要按你显存调。
5. 本篇常见报错排查
下面这几个是我在配 Claude Code + LiteLLM + TensorRT-LLM 时实际撞到的,按出现频率排序。
5.1 400 Unsupported parameter: thinking
Claude Code 会发thinking字段,OpenAI 风格后端不认。解决方式是确保config.yaml里drop_params: true同时出现在litellm_params和litellm_settings两处。只写一处有时候不生效,这是 LiteLLM 的已知行为。
5.2 401 Invalid API key
分两种情况。如果你走本地 LiteLLM,检查ANTHROPIC_AUTH_TOKEN是不是和 config 里的api_key一致,或者干脆都填internal。如果你走 TaoToken,检查 Key 有没有复制完整、有没有多余空格。还有一种情况是ANTHROPIC_BASE_URL和 Key 来源不匹配,比如地址指向 localhost 但 Key 是 TaoToken 的,这种必报 401。
5.3 上下文截断导致回答不完整
现象是 Claude Code 读到一半突然说「文件太长」或者回答明显被切断。这是truncate_prompt_tokens设得比实际需求小。把它调到和推理服务max_num_tokens一致,比如 66912。但要注意,这个值受显存限制,调太大后端会 OOM,需要你在显存和上下文之间找平衡。
5.4 Connection refused
curl http://localhost:4000/v1/messages直接连不上,说明 LiteLLM 没起来或者端口不对。先确认终端里Uvicorn running on那行还在,如果进程挂了看报错日志。Windows 上还有一种情况是防火墙拦了 4000 端口,换端口或者放行即可。
5.5 Claude Code 忽略 settings.json
如果你改了settings.json但 Claude Code 行为没变,先确认文件路径对不对。用户级配置在~/.claude/settings.json,项目级在项目根目录的.claude/settings.json。项目级优先级更高,如果你在项目里也放了一份,会覆盖用户级。用claude --debug能看到它加载了哪个文件。
6. 后续怎么用:从本地到统一 Key
链路跑通之后,日常使用其实就两种模式。一种是纯本地,ANTHROPIC_BASE_URL指向localhost:4000,适合对数据不出内网有要求的场景,缺点是模型能力受本地硬件限制。另一种是切到 TaoToken,把地址换成https://taotoken.net/api,Key 换成控制台创建的,这样 Claude Code 的配置结构不变,但背后可以用到更强的模型,适合本地模型搞不定的复杂任务。
切换的时候只改settings.json里那两个字段就行,LiteLLM 那层可以留着不动,需要本地的时候再切回来。如果你还没决定要不要长期本地化,建议先用 TaoToken 把 Claude Code 的配置和验证流程走一遍,确认客户端没问题,再花时间调本地推理服务。接入文档在https://taotoken.net/doc,里面有各语言的调用示例,配置卡住的时候对着看比猜快。
最后留一个实用习惯:每次改完settings.json,先用claude --debug启动一次,看它打印的 base URL 和 model 是不是你期望的。这个动作花不了十秒,但能省掉很多「明明改了却没生效」的困惑。