关键词: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 |
+---------------------------------------------------------------------------------------+
vLLM(Docker 容器)
vLLM负责加载 Qwen 模型,依靠连续批处理(Continuous Batching)搭配 PagedAttention 实现高效调度与 KV 缓存管理,显著提升 GPU 利用率与推理吞吐,适合多请求并发场景,另外vLLM对外暴露/v1/chat/completionsOpenAI 兼容接口,自带接口鉴权(--api-key),OpenAI 接口已经成为大模型推理服务领域的通用行业范式。
Docker 实现环境隔离,隔离宿主机的 gcc、python 依赖,不会污染宿主机系统,本环境宿主机 gcc 版本过于老旧,如果直接安装 vLLM会报错,宿主机 NVIDIA 显卡驱动通过--gpus传给容器复用,CUDA Toolkit、Python、编译工具链由容器镜像内部独立提供,环境可迁移、一键启停,便于反复调试模型与参数。-
NewAPI(网关服务,Docker 部署)
整套链路的核心转发与转换层:- 协议转换:接收 Claude CLI 发来的 Anthropic 协议
/v1/messages请求,转为 OpenAI 协议/v1/chat/completions转发给 vLLM;模型返回结果后,再转回 Anthropic 协议格式返回给 Claude CLI。 - 模型路由 / 别名映射:配置模型重定向,将客户端请求的模型别名翻译成 vLLM 后端真实识别的模型名称。
- 统一鉴权:对外提供统一 API 密钥给客户端,后端使用独立密钥访问 vLLM,实现前后端鉴权分离。
- 通道管理、请求日志、限流等基础网关能力。
- 协议转换:接收 Claude CLI 发来的 Anthropic 协议
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/网页如下

先创建密钥,点击"个人资料" -> "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配置vLLM模型服务成功

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

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