使用 vLLM 和 LiteLLM 在本地 LLM 上运行 Claude Code

发布: (2026年2月5日 GMT+8 10:12)
9 min read
原文: Dev.to

Source: Dev.to

(请提供您希望翻译的正文内容,我将为您翻译成简体中文并保持原有的格式。)

在本地运行 Claude Code 与 vLLM + LiteLLM

目标: 将专有源代码保留在本地,同时仍使用 Claude Code 的工作流。
解决方案: 通过 LiteLLM 代理 Claude Code 的 Anthropic Messages API 调用,将其转换为本地 vLLM 推理服务器使用的兼容 OpenAI 的 API。

Claude Code → LiteLLM (port 4000) → vLLM (port 8000) → Local GPU

1. 一行环境变量

export ANTHROPIC_BASE_URL="http://localhost:4000"

Claude Code 现在指向 LiteLLM 代理,而不是 Anthropic 的云端端点。

2. 模型与硬件

  • 模型: Qwen3‑Coder‑30B‑A3B‑Instruct‑AWQ(混合专家,30 B 总参数,前向时激活 3 B)
  • GPU: 双 AMD MI60(ROCm)使用张量并行(size = 2)
  • 量化: AWQ → 能够舒适地装入 GPU 显存

2.1 vLLM Docker 服务(docker‑compose 片段)

services:
  vllm:
    image: nalanzeyu/vllm-gfx906:v0.11.2-rocm6.3
    container_name: vllm
    devices:
      - /dev/kfd:/dev/kfd
      - /dev/dri/card1:/dev/dri/card1
      - /dev/dri/card2:/dev/dri/card2
      - /dev/dri/renderD128:/dev/dri/renderD128
      - /dev/dri/renderD129:/dev/dri/renderD129
    shm_size: 16g
    environment:
      - HIP_VISIBLE_DEVICES=0,1
    command:
      - python
      - -m
      - vllm.entrypoints.openai.api_server
      - --model
      - QuantTrio/Qwen3-Coder-30B-A3B-Instruct-AWQ
      - --tensor-parallel-size
      - "2"
      - --max-model-len
      - "65536"
      - --gpu-memory-utilization
      - "0.9"
      - --enable-auto-tool-choice
      - --tool-call-parser
      - qwen3_coder

为什么要加最后两个标志?

  • --enable-auto-tool-choice – 让模型自行决定何时发出工具调用。
  • --tool-call-parser qwen3_coder – 将 Qwen 的 XML 风格工具调用转换为 LiteLLM(以及最终的 Claude Code)所期望的 OpenAI 工具调用格式。

3. LiteLLM 配置

model_list:
  - model_name: claude-*
    litellm_params:
      model: hosted_vllm/QuantTrio/Qwen3-Coder-30B-A3B-Instruct-AWQ
      api_base: http://vllm:8000/v1
      api_key: "not-needed"
    model_info:
      max_tokens: 65536
      max_input_tokens: 57344
      max_output_tokens: 8192

litellm_settings:
  drop_params: true          # ignore Anthropic‑only params
  request_timeout: 600
  modify_params: true        # adapt params for OpenAI API

general_settings:
  disable_key_check: true    # no API key needed locally

设置说明

SettingEffect
drop_params: true静默丢弃没有 OpenAI 对应字段的 Anthropic‑specific 参数。
modify_params: true允许 LiteLLM 重写参数(例如 max_tokens),以匹配目标 API 的期望。
disable_key_check: true跳过 API‑key 验证——在服务器本地运行且无需身份验证时很有用。

4. 在本地堆栈上运行 Claude Code

export ANTHROPIC_BASE_URL="http://localhost:4000"

cd my-project
claude          # launches Claude Code as usual

Result: 用户体验与托管的 Anthropic API 完全相同,但所有推理都在本地进行。

Performance & Limits

指标观察结果
Throughput~25‑30 tokens / s on dual MI60, ~175 ms time‑to‑first‑token
Context windowCapped at 64 K tokens (Claude Opus can go to 200 K)
Model capabilityQwen3‑Coder excels at coding; Claude has broader general knowledge and instruction following.

Upsides

  • Zero API cost
  • Full data sovereignty (code never leaves your network)
  • Works on air‑gapped environments

5. 端到端测试:构建 Flask 待办事项应用

export ANTHROPIC_BASE_URL="http://localhost:4000"

cd /tmp && mkdir flask-test && cd flask-test
claude --dangerously-skip-permissions -p \
  "Build a Flask todo app with SQLite persistence, \
   modern UI with gradients and animations, \
   mobile responsive design, and full CRUD operations."

生成的项目结构

flask_todo_app/
├── app.py              # Flask routes + SQLite setup
├── requirements.txt    # Dependencies
├── run_app.sh          # Launch script
├── static/
│   ├── css/
│   │   └── style.css   # Gradients, animations, hover effects
│   └── js/
│       └── script.js   # Client‑side interactions
└── templates/
    └── index.html      # Jinja2 template (responsive layout)

示例 app.py

from flask import Flask, render_template, request, redirect, url_for
import sqlite3

app = Flask(__name__)

def init_db():
    conn = sqlite3.connect('todos.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS todos
                 (id INTEGER PRIMARY KEY AUTOINCREMENT,
                  task TEXT NOT NULL,
                  completed BOOLEAN DEFAULT FALSE)''')
    conn.commit()
    conn.close()

init_db()

@app.route('/')
def index():
    conn = sqlite3.connect('todos.db')
    c = conn.cursor()
    c.execute('SELECT id, task, completed FROM todos ORDER BY id DESC')
    todos = c.fetchall()
    conn.close()
    return render_template('index.html', todos=todos)

示例 CSS (static/css/style.css)

body {
    font-family: 'Poppins', sans-serif;
    background: linear-gradient(135deg, #667eea, #764ba2);
    min-height: 100vh;
    padding: 20px;
}

.container {
    max-width: 800px;
    margin: 0 auto;
}

.header {
    text-align: center;
    padding: 40px 0;
    color: white;
    text-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

运行应用

cd flask_todo_app
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python app.py   # starts Flask on http://localhost:5000

现在您可以添加、切换和删除任务;SQLite 数据库在重启后仍会保留。

6. TL;DR

  1. ANTHROPIC_BASE_URL 设置为指向 LiteLLM 代理 (http://localhost:4000)
  2. 运行 vLLM(Docker)并使用 Qwen3‑Coder 模型,添加 --enable-auto-tool-choice + --tool-call-parser qwen3_coder 参数。
  3. 配置 LiteLLM,将所有 claude-* 模型名称映射到本地 vLLM 端点。
  4. 像往常一样启动 Claude Code——它现在会调用您本地的 GPU,实现零成本、主权化的推理,同时保留完整的 Claude Code 工作流(包括工具调用)。

享受安全、高性能的编码助手,无需将专有代码发送到外部!

生成概览

  • 整个生成过程大约用了五分钟,经历了多轮 agentic 迭代。
  • 每个文件都是一次独立的工具调用:模型生成代码,Claude Code 执行,返回结果,模型再规划下一步。
  • 91 % 的前缀缓存命中率表明 vLLM 在多轮循环中高效复用上下文。

这验证了 agentic 工作流能够正常运行。模型读取提示,规划文件结构,发出创建目录和写文件的工具调用,并生成可运行的应用。所有推理都在本地的 MI60 上完成——没有代码离开我的网络。

限制与未来工作

  • 规模 – 未在更大的代码库上测试。一个小的 Flask 应用算是一个案例,几千行的重构则是另一回事。
  • 上下文窗口 – 64 K token 的限制最终会成为约束,模型在处理复杂架构决策时可能不如真实的 Claude 那样游刃有余。
  • 当前适用性 – 适合聚焦、范围明确的任务。

Claude Code 兼容性检查表

要求细节
强大的工具使用模型必须可靠地发出结构化的工具调用
代码聚焦Qwen3‑Coder 表现良好;DeepSeek Coder 和 CodeLlama 系列也应可行
足够的上下文我使用了 64 K;更小的窗口可能可用,但未经过测试

观察

  • Qwen3‑Coder‑30B‑A3B 能很好地处理直接的编码任务。
  • 对于复杂的重构或架构决策,真实的 Claude API 仍是更好的选择。

硬件建议

  • 如果没有 64 GB 显存,可以使用 Qwen2.5‑Coder‑7BQwen3‑8B,它们可以在单块 16 GB 或 24 GB 显卡上运行。
  • 这些配置我尚未测试,无法评论它们的上下文限制或在 Claude Code 的 agentic 工作流中的表现。

工作流建议

  • 与其使用宽泛的 “重构此模块” 提示,不如将工作拆分为更紧凑、聚焦的请求。
  • 更细粒度的提示更能发挥小模型的优势。

完整的 Docker‑Compose 配置

services:
  vllm:
    image: nalanzeyu/vllm-gfx906:v0.11.2-rocm6.3
    container_name: vllm
    restart: unless-stopped
    ports:
      - "8000:8000"
    devices:
      - /dev/kfd:/dev/kfd
      - /dev/dri/card1:/dev/dri/card1
      - /dev/dri/card2:/dev/dri/card2
      - /dev/dri/renderD128:/dev/dri/renderD128
      - /dev/dri/renderD129:/dev/dri/renderD129
    group_add:
      - "44"
      - "992"
    shm_size: 16g
    volumes:
      - /mnt/cache/huggingface:/root/.cache/huggingface:rw
    environment:
      - HIP_VISIBLE_DEVICES=0,1
    command:
      - python
      - -m
      - vllm.entrypoints.openai.api_server
      - --model
      - QuantTrio/Qwen3-Coder-30B-A3B-Instruct-AWQ
      - --tensor-parallel-size
      - "2"
      - --max-model-len
      - "65536"
      - --gpu-memory-utilization
      - "0.9"
      - --host
      - "0.0.0.0"
      - --port
      - "8000"
      - --enable-auto-tool-choice
      - --tool-call-parser
      - qwen3_coder

  litellm:
    image: litellm/litellm:v1.80.15-stable
    container_name: litellm
    restart: unless-stopped
    ports:
      - "4000:4000"
    volumes:
      - ./litellm-config.yaml:/app/config.yaml:ro
    command:
      - --config
      - /app/config.yaml
      - --port
      - "4000"
      - --host
      - "0.0.0.0"
    depends_on:
      - vllm

运行堆栈

# 启动(使用 nerdctl 或 Docker)
nerdctl compose -f coder.yaml up -d

在网络中的任意机器上,将 Claude Code 指向 Feynman(GPU 工作站),即可进行本地推理。

# 完成后,关闭并清理
nerdctl compose -f coder.yaml down

最后思考

  • 该方案并不能取代所有人的 Claude API。
  • 如果需要最大化的能力,Anthropic 的托管模型仍是最佳选择。
  • 对于在乎数据主权的用户,本地推理意味着专有代码永远不会离开网络。
  • 每次向 Claude Code 提问时看到自己的 GPU 亮起,也是一件令人满足的事。
Back to Blog

相关文章

阅读更多 »

当 AI 流量破坏你的计费系统

AI traffic 并不像 human traffic 那样行为。它不会缓慢 ramp up,而是突然出现,常常以大规模的 bursts 形式出现,执行时间只有几秒甚至毫秒级,且 d…