AI工程系列:Docker + vLLM 本地部署 Qwen 模型,并通过 NewAPI 对接 Claude Code

关键词:Docker,vLLM,Qwen,NewAPI,Claude Code

内容摘要

  • 部署目标、整体架构与各组件分工说明
  • Docker + vLLM 部署 Qwen模型
  • NewAPI 网关部署、通道与模型映射配置
  • Claude Code 安装配置,对接 NewAPI 网关调用本地Qwen模型

部署目标、整体架构与各组件分工说明

本案例基于本地硬件环境部署大模型:NVIDIA 驱动版本535.154.05,CUDA 版本12.2,GPU 显存 11GiB。目标是在本地部署大模型并对外提供 OpenAI 兼容协议接口;借助 NewAPI 网关完成协议转换,最终实现 Claude Code 作为 Agent 客户端调用本地模型。

(base) [jmstest@zx-61 ~]$ nvidia-smi
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.154.05             Driver Version: 535.154.05   CUDA Version: 12.2     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 2080 Ti     Off | 00000000:03:00.0 Off |                  N/A |
| 33%   35C    P0              28W / 250W |      0MiB / 11264MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|  No running processes found                                                           |
+---------------------------------------------------------------------------------------+
  1. vLLM(Docker 容器)
    vLLM负责加载 Qwen 模型,依靠连续批处理(Continuous Batching)搭配 PagedAttention 实现高效调度与 KV 缓存管理,显著提升 GPU 利用率与推理吞吐,适合多请求并发场景,另外vLLM对外暴露 /v1/chat/completions OpenAI 兼容接口,自带接口鉴权(--api-key),OpenAI 接口已经成为大模型推理服务领域的通用行业范式。
    Docker 实现环境隔离,隔离宿主机的 gcc、python 依赖,不会污染宿主机系统,本环境宿主机 gcc 版本过于老旧,如果直接安装 vLLM会报错,宿主机 NVIDIA 显卡驱动通过--gpus传给容器复用,CUDA Toolkit、Python、编译工具链由容器镜像内部独立提供,环境可迁移、一键启停,便于反复调试模型与参数。

  2. NewAPI(网关服务,Docker 部署)
    整套链路的核心转发与转换层:

    • 协议转换:接收 Claude CLI 发来的 Anthropic 协议/v1/messages请求,转为 OpenAI 协议/v1/chat/completions转发给 vLLM;模型返回结果后,再转回 Anthropic 协议格式返回给 Claude CLI。
    • 模型路由 / 别名映射:配置模型重定向,将客户端请求的模型别名翻译成 vLLM 后端真实识别的模型名称。
    • 统一鉴权:对外提供统一 API 密钥给客户端,后端使用独立密钥访问 vLLM,实现前后端鉴权分离。
    • 通道管理、请求日志、限流等基础网关能力。
  3. Claude Code(Agent 客户端)
    Claude Code 会自主组装对话 messages、工具调用参数,发起标准 Anthropic 协议推理请求,并完整执行 Agent 能力,包括工具调用、多轮对话交互等逻辑。原生遵循 Anthropic 协议,固定请求路径为 /v1/messages;该协议与 OpenAI 协议不互通、不兼容,因此原生 OpenAI 格式的推理接口无法直接被 Claude 客户端调用。

三个组件的工作示意图如下,NewAPI 承担桥梁作用,实现异构协议互通


组件工作示意图

Docker + vLLM 部署 Qwen模型

拉去vLLM镜像

docker pull vllm/vllm-openai:v0.6.6.post1

下载Qwen2.5-3B-Instruct-AWQ模型

git clone https://www.modelscope.cn/Qwen/Qwen2.5-3B-Instruct-AWQ.git

docker启动vLLM容器

docker run --rm \
  --name Qwen2.5-3B-Instruct-AWQ \
  --gpus all \
  --ipc=host \
  -p 8000:8000 \
  -v "/home/model_repository/Qwen2.5-3B-Instruct-AWQ:/models:ro" \
  vllm/vllm-openai:v0.6.6.post1 \
  --model /models \
  --served-model-name Qwen2.5-3B-Instruct-AWQ \
  --quantization awq \
  --gpu-memory-utilization 0.75 \
  --max-num-seqs 1

该脚本没有执行任何命令,说明在镜像bulid的时候在Dcoekrfile中已经声明了入口,通过命令查看

docker inspect vllm/vllm-openai:v0.6.6.post1
...
"Entrypoint": [
    "python3",
    "-m",
    "vllm.entrypoints.openai.api_server"
]
...

因此以下参数都是python3 -m vllm.entrypoints.openai.api_server的参数

  • --model:指定要加载的模型本地路径,这个和-v中挂载到模型的路径保持一致
  • --served-model-name:对外暴露的模型名称,这个名字是 OpenAI 接口层面识别的名称
  • --quantization:指定量化方式,当前模型是AWQ 4bit 量化模型,可以省略不指定,vLLM会自动抓取config.json中的quantization_config
{
  "architectures": [
    "Qwen2ForCausalLM"
  ],
 ...
  "quantization_config": {
    "bits": 4,
    "group_size": 128,
    "modules_to_not_convert": null,
    "quant_method": "awq",
    "version": "gemm",
    "zero_point": true
  },
 ...
}
  • --gpu-memory-utilization:vLLM 启动时会估算显存,预留 75% 的可用 GPU 显存给模型权重 + KV 缓存,剩下 25% 留给驱动、系统开销。数值越大,预留给 KV Cache 的空间越多,能支持更长上下文;太高容易爆显存 OOM;0.75 是比较稳妥的值

除此之外,还有部分docker启动参数如下

  • -p 8000:8000:左侧是宿主机,右侧是容器,宿主机随便改,容器默认的服务端口是8000,如果要修改,同步vLLM的--port参数要改,例如
docker run --rm \
  --name Qwen2.5-3B-Instruct-AWQ \
  --gpus all \
  --ipc=host \
  -p 8002:8001 \  # 右边改成容器内端口
  -v "/home/model_repository/Qwen2.5-3B-Instruct-AWQ:/models:ro" \
  vllm/vllm-openai:v0.6.6.post1 \
  --model /models \
  --served-model-name Qwen2.5-3B-Instruct-AWQ \
  --gpu-memory-utilization 0.65 \
  --max-num-seqs 1 \
  --port 8001   # vllm 监听端口同步修改
  • -v "/home/model_repository/Qwen2.5-3B-Instruct-AWQ:/models:ro":将宿主机的模型文件目录挂载到容器内,ro代表read only,只读不允许修改,原始语法为-v 宿主机路径:容器内路径[:权限]

启动后通过终端测试请求

[root@zx-61 Qwen2.5-3B-Instruct-AWQ]# curl -N http://127.0.0.1:8000/v1/chat/completions \
>   -H 'Content-Type: application/json' \
>   -d '{
>     "model": "Qwen2.5-3B-Instruct-AWQ",
>     "messages": [
>       {
>         "role": "user",
>         "content": "写一句欢迎语。"
>       }
>     ],
>     "stream": false,
>     "max_tokens": 128
>   }'
\
{"id":"chatcmpl-57b3670315444e9183336607ed10e139","object":"chat.completion","created":1790579190,"model":"Qwen2.5-3B-Instruct-AWQ","choices":[{"index":0,"message":{"role":"assistant","content":"欢迎你提问,无论是技术探讨还是日常生活,我都会尽我所能提供帮助和解答。","tool_calls":[]},"logprobs":null,"finish_reason":"stop","stop_reason":null}],"usage":{"prompt_tokens":34,"total_tokens":55,"completion_tokens":21,"prompt_tokens_details":null},"prompt_logprobs":null}

使用Python的OpenAI客户端测试如下

from openai import OpenAI

# 连接本地vLLM服务
client = OpenAI(
    base_url="http://10.2.13.31:8000/v1",
    api_key="1"  # 随便写,vLLM不校验
)

resp = client.chat.completions.create(
    model="Qwen2.5-3B-Instruct-AWQ",
    messages=[
        {"role": "user", "content": "写一句欢迎语。"}
    ],
    stream=False,
    max_tokens=128
)

print(resp.choices[0].message.content)

输出如下

您好,欢迎来访,希望我的帮助能让您的体验更加美好。

注意在docker启动vLLM的脚本中没有设置--api-key,因此在OpenAI客户端中随便填写一个字符串即可


NewAPI 网关部署、通道与模型映射配置

NewAPI是一个网关服务,类似一个中转站,夹在客户端和后台服务之间,通过Docker部署NewAPI网关

docker pull calciumion/new-api

启动容器,拉起NewAPI网关服务

docker run -d \
  --name new-api \
  --restart always \
  -p 4000:3000 \
  -e TZ=Asia/Shanghai \
  -e SESSION_SECRET=admin-secret-20260924 \
  -v /home/newapi-data:/data \
  calciumion/new-api:latest

启动后打开http://10.2.13.31:4000/网页如下


NewAPI页面

先创建密钥,点击"个人资料" -> "API密钥" -> "创建密钥"

创建密钥

下一步在NewAPI上将刚才vLLM部署的模型配置为渠道,先修改vLLM的启动命令,加上鉴权

docker run --rm \
  --name Qwen2.5-3B-Instruct-AWQ \
  --gpus all \
  --ipc=host \
  -p 8000:8000 \
  -v "/home/model_repository/Qwen2.5-3B-Instruct-AWQ:/models:ro" \
  vllm/vllm-openai:v0.6.6.post1 \
  --model /models \
  --served-model-name Qwen2.5-3B-Instruct-AWQ \
  --quantization awq \
  --gpu-memory-utilization 0.75 \
  --max-num-seqs 1 \
  --enable-auto-tool-choice \
  --tool-call-parser hermes \
  --api-key sk-vllm-local-123456 \
  --rope-scaling '{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":32768}'

其中--api-key用于鉴权,NewAPI收到客户端请求后,会基于这个密钥请求vLLM后端,--enable-auto-tool-choice和--tool-call-parser hermes是工具调用参数,具体的:

  • --enable-auto-tool-choice 是是否调用工具的全局总开关,大模型自动判断是否需要调用工具
  • --tool-call-parser hermes 这个是开启自动判断调用工具后,解析工具输出结果的解释器,hermes是一种解释器,适配于hermes系列模型和Qwen2.5,他的工具调用的特殊字符是<tool_call>,</tool_call>,如果大模型推理的内容是类似
<tool_call>{"name":"calculate_sum","parameters":{"numbers":[20,30]}}</tool_call>

这样的内容,则vLLM会依据hermes解析器解析出具体的工具和入参,而不是把输出当作普通文本

举个例子,在开启工具自动选择和工具解析器之后,使用待用工具信息和使用工具要求的提问请求如下

curl -N http://127.0.0.1:8000/v1/chat/completions \
    -H "Authorization: Bearer sk-vllm-local-123456" \
    -H 'Content-Type: application/json' \
    -d '{
      "model": "Qwen2.5-3B-Instruct-AWQ",
      "messages": [
        {
          "role": "user",
          "content": "帮我把100美元换算成人民币"
        }
      ],
      "tools": [
        {
          "type": "function",
          "function": {
            "name": "convert_usd_to_cny",
            "description": "美元转人民币汇率换算,输入美元金额,返回人民币",
            "parameters": {
              "type": "object",
              "properties": {
                "amount": {
                  "type": "number",
                  "description": "美元金额"
                }
              },
              "required": ["amount"]
            }
          }
        }
      ],
      "tool_choice": "auto",
      "stream": false,
      "max_tokens": 128
    }'

在请求中描述了添加了一个工具描述,包含工具描述,入参,工具的函数名,大模型返回如下

{
    "id": "chatcmpl-e728dac6d2884aaca4976e2de19fc539",
    "object": "chat.completion",
    "created": 1791614431,
    "model": "Qwen2.5-3B-Instruct-AWQ",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": null,
                "tool_calls": [
                    {
                        "id": "chatcmpl-tool-79d59b67f622493aae2a07cf73be3d73",
                        "type": "function",
                        "function": {
                            "name": "convert_usd_to_cny",
                            "arguments": "{\"amount\": 100}"
                        }
                    }
                ]
            },
            "logprobs": null,
            "finish_reason": "tool_calls",
            "stop_reason": null
        }
    ],
    "usage": {
        "prompt_tokens": 191,
        "total_tokens": 217,
        "completion_tokens": 26,
        "prompt_tokens_details": null
    },
    "prompt_logprobs": null
}

此时choices[0].message.content变为null,说明vLLM已经解析到工具调用结果,没有把输出当作普通的文本内容放入content,此时"finish_reason": "tool_calls",一般的文本输出"finish_reason":"stop",然后vLLM回答工具输出的结果灌入tool_calls,包含调用工具的名称和入参解析。
如果在vLLM启动时不开启工具自动选择,那么通用请求下返回为

{"object":"error","message":"\"auto\" tool choice requires --enable-auto-tool-choice and --tool-call-parser to be set","type":"BadRequestError","param":null,"code":400}

因为在请求中有"tools": [...]数组,vLLM会先校验是否开启工具调用参数,如果改成"tols": [...],就能骗过vLLM直接当作普通文本推理。

用于客户端开放给Claude CLI,另外设置--rope-scalingRoPE 缩放使得模型能够支持更长的上下文窗口,默认的3B模型是32768,Claude CLI的系统提示词已经超过了这个长度,因此需要延长。
简单测试如下,只需要在请求头加上Authorization: Bearer {密钥}即可

[root@zx-61]# curl -N http://127.0.0.1:8000/v1/chat/completions \
>    -H "Authorization: Bearer sk-vllm-local-123456" \
>    -H 'Content-Type: application/json' \
>    -d '{
>      "model": "Qwen2.5-3B-Instruct-AWQ",
>      "messages": [
>        {
>          "role": "user",
>          "content": "写一句欢迎语。"
>        }
>      ],
>      "stream": false,
>      "max_tokens": 128
>    }'
{"id":"chatcmpl-07a177d8fc744b98834830969b1b0f4e","object":"chat.completion","created":1790582008,"model":"Qwen2.5-3B-Instruct-AWQ","choices":[{"index":0,"message":{"role":"assistant","content":"欢迎您光临,愿我的回答能为您的探索之旅增添一丝亮光。","tool_calls":[]},"logprobs":null,"finish_reason":"stop","stop_reason":null}],"usage":{"prompt_tokens":34,"total_tokens":52,"completion_tokens":18,"prompt_tokens_details":null},"prompt_logprobs":null}

下一步配置NewAPI,点击"个人资料" -> "渠道" -> "创建渠道" -> "OpenAI",输入以下内容

NewAPI添加渠道

回到渠道页面,若现实测试成功,则NewAPI配置vLLM模型服务成功

渠道测试

回到模型广场,进入模型详情

模型广场

在API页面有请求示例

API请求示例

由于创建的OpenAI模型,所以请求是遵循OpenAI协议的/chat/completions,此时请求NewAPI的会变为NewAPI部署所在的ip和端口,密钥为NewAPI配置的用户统一密钥而不是vLLM模型服务的密钥,现在直接请求vLLM和请求NewAPI都可以

# 请求vLLM
curl http://10.2.13.31:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-vllm-local-123456" \
  -d '{
    "model": "Qwen2.5-3B-Instruct-AWQ",
    "messages": [
      {"role": "user", "content": "你好,请简单介绍自己"}
    ],
    "temperature": 0.7,
    "max_tokens": 1024
  }'

# 请求NewAPI
curl http://10.2.13.31:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-8YaXYHuWyYKuUPH0uD0JWInbmhcTjqNxAK1sNnT4739uS5mK" \
  -H "Content-Type: application/json" \
  -d '{
       "model": "Qwen2.5-3B-Instruct-AWQ",
       "messages": [
         {
           "role": "user",
           "content": "你好,请简单介绍自己"
         }
       ],
       "temperature": 0.7
     }'

由于NewAPI可以实现协议自动转换,Claude的协议/messages也可以请求

curl http://10.2.13.31:4000/v1/messages \
  -H "Authorization: Bearer sk-8YaXYHuWyYKuUPH0uD0JWInbmhcTjqNxAK1sNnT4739uS5mK" \
  -H "Content-Type: application/json" \
  -d '{
       "model": "Qwen2.5-3B-Instruct-AWQ",
       "messages": [
         {
           "role": "user",
           "content": "你好,请简单介绍自己"
         }
       ],
       "temperature": 0.7
     }'

至此,NewAPI网关配置vLLM本地模型服务验证完成。


Claude Code 安装配置,对接 NewAPI 网关调用本地Qwen模型

Claude Code 是 Anthropic 官方提供的命令行客户端工具,原生用来调用 Claude 大模型,内置 Agent 工具调用能力。Claude Code在linux mint安装如下

curl -fsSL https://claude.ai/install.sh | bash

下一步配置Claude Code所使用的模型,打开~/.claude/settings.json进行设置如下

{
  "env": {
    "ANTHROPIC_AUTH_TOKEN": "sk-8YaXYHuWyYKuUPH0uD0JWInbmhcTjqNxAK1sNnT4739uS5mK",
    "ANTHROPIC_BASE_URL": "http://10.2.13.31:4000",
    "ANTHROPIC_MODEL": "Qwen2.5-3B-Instruct-AWQ",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "Qwen2.5-3B-Instruct-AWQ",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "Qwen2.5-3B-Instruct-AWQ",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "Qwen2.5-3B-Instruct-AWQ",
    "ANTHROPIC_REASONING_MODEL": "Qwen2.5-3B-Instruct-AWQ",
    "API_TIMEOUT_MS": "3000000",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
    "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
    "CLAUDE_CODE_NO_FLICKER": "1"
  }
}

其中ANTHROPIC_AUTH_TOKEN配置为NewAPI用户的统一密钥,ANTHROPIC_BASE_URL配置为NewAPI的服务地址,模型填写为NewAPI暴露的模型名称即可。
配置完成后,在终端输入claude启动

claude启动

claude成功打开并且能够进行对话,显示模型为Qwen2.5-3B-Instruct-AWQ,至此vLLM+NewAPI+Claude Code的本地大模型部署完成,全文完毕。

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容