DGX Spark 部署本地模型

本文记录在 DGX Spark / 128G 级别机器上下载、启动、测试和压测本地 vLLM 模型。

以下内容默认在 DGX 的 Linux shell 中执行,模型统一放在:

1
~/.cache/huggingface/models

1. 结论和模型选择

场景 推荐模型 启动方式 说明
日常代码 Agent、IDE 补全、轻量多人使用 Qwen3-Coder-30B-A3B-Instruct-FP8 NVIDIA vLLM Docker 30G FP8,短上下文响应快,Docker 镜像可直接加载
复杂代码任务、较长上下文、质量优先 Qwen3-Coder-Next-FP8 NVIDIA vLLM Docker 54.1G FP8,可跑 16K/64K 场景,但多人高并发延迟明显变差
当前最值得保留的综合模型 Qwen3.6-35B-A3B-FP8 本机 venv vLLM 短上下文、中等上下文、长上下文都表现较好,但启动后主机内存占用很高
非 FP8 质量对照、8K 内测试 Qwen3.5-35B-A3B 本机 venv vLLM 能跑通,但需要保守参数,不适合作为长上下文默认模型

不放入主流程的内容:

模型 原因
Qwen3-Coder-Next 75G,实测内存不够
GLM-4.7-Flash 启动后机器进入极重加载/换页状态,未进入 benchmark
Docker 方式启动 Qwen3.6-27B-FP8 / Qwen3.6-35B-A3B-FP8 nvcr.io/nvidia/vllm:26.01-py3 内置软件栈不识别 qwen3_5 / qwen3_5_moe 架构,需要换新版 vLLM/Transformers

2. 环境检查

先检查 Hugging Face、代理和离线变量,避免下载或启动时被旧环境变量影响。

1
2
3
echo $HF_ENDPOINT
env | grep -i proxy
env | grep -E 'HF_HUB_OFFLINE|TRANSFORMERS_OFFLINE|HF_ENDPOINT'

如果要下载模型,确保不要处于离线模式:

1
unset HF_HUB_OFFLINE TRANSFORMERS_OFFLINE

如果代理不可用或会干扰访问,可以先清掉代理:

1
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY http_proxy https_proxy all_proxy

国内网络建议使用 Hugging Face 镜像:

1
export HF_ENDPOINT=https://hf-mirror.com

参数说明:

变量 / 命令 含义 影响
HF_ENDPOINT Hugging Face Hub 访问入口 设成 https://hf-mirror.com 后,hf download 会从镜像站下载
HF_HUB_OFFLINE=1 Hugging Face Hub 离线模式 启动或压测本地模型时很有用,避免联网检查;下载模型前必须取消
TRANSFORMERS_OFFLINE=1 Transformers 离线模式 避免 Transformers 联网找配置;本地文件完整时建议用于服务启动和 benchmark
HTTP_PROXY / HTTPS_PROXY / ALL_PROXY 网络代理 代理错误会导致下载失败;确认代理可用后再设置

3. 下载模型

下载前先用 curl 验证镜像站是否能访问模型配置文件:

1
curl -I https://hf-mirror.com/Qwen/Qwen3-Coder-Next-FP8/resolve/main/config.json

下载 Qwen3-Coder-Next-FP8

1
2
3
4
export HF_ENDPOINT=https://hf-mirror.com

hf download Qwen/Qwen3-Coder-Next-FP8 \
--local-dir ~/.cache/huggingface/models/Qwen3-Coder-Next-FP8

下载命令参数说明:

参数 含义 影响
hf download Hugging Face CLI 的下载命令 会把模型仓库文件下载到本地
Qwen/Qwen3-Coder-Next-FP8 Hugging Face 模型仓库名 换模型时改这里
--local-dir 指定本地保存目录 vLLM 启动时直接指向这个目录;建议所有模型统一放到 ~/.cache/huggingface/models

确认模型目录大小:

1
2
du -sh ~/.cache/huggingface/models/Qwen3-Coder-Next-FP8
ls -lh ~/.cache/huggingface/models

删除不需要的模型前先确认目录:

1
2
3
du -sh ~/.cache/huggingface/models/Qwen3-Coder-Next
rm -rf ~/.cache/huggingface/models/Qwen3-Coder-Next
ls -lh ~/.cache/huggingface/models

rm -rf 会直接删除目录,执行前必须确认路径完整且没有变量拼错。

4. 用 Docker 启动 vLLM

Docker 方式适合 Qwen3-Coder-30B-A3B-Instruct-FP8Qwen3-Coder-Next-FP8。原测试使用镜像:

1
nvcr.io/nvidia/vllm:26.01-py3

4.1 启动 Qwen3-Coder-30B-A3B-Instruct-FP8

1
2
3
4
5
6
7
8
9
10
11
docker run -it --gpus all --network host \
--ipc=host --ulimit memlock=-1 --ulimit stack=67108864 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
nvcr.io/nvidia/vllm:26.01-py3 \
vllm serve /root/.cache/huggingface/models/Qwen3-Coder-30B-A3B-Instruct-FP8 \
--host 0.0.0.0 \
--port 8000 \
--served-model-name Qwen3-Coder-30B-A3B-Instruct-FP8 \
--max-model-len 65536 \
--enable-prefix-caching \
--gpu-memory-utilization 0.85

适用判断:

项目 结果
模型大小 约 30G
512 输入 / 256 输出 / 20 请求 / 1 RPS 全部成功,Mean TTFT 约 256ms,Mean TPOT 约 57ms
推荐用途 默认代码 Agent、日常 IDE、轻量多人

启动后使用 watch -n 1 free -h 观察 DGX 主机内存:

Qwen3-Coder-30B-A3B-Instruct-FP8 启动后内存监测

截图显示:总内存约 121Gi,已用约 109Gi,可用约 11Gi,swap 基本没有明显压力。这说明 30B FP8 虽然已经占用大部分主机内存,但仍保留了一定余量,适合作为日常代码 Agent 默认模型。

4.2 启动 Qwen3-Coder-Next-FP8

短上下文或中等上下文启动:

1
2
3
4
5
6
7
8
9
10
docker run -it --gpus all --network host \
--ipc=host --ulimit memlock=-1 --ulimit stack=67108864 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
nvcr.io/nvidia/vllm:26.01-py3 \
vllm serve /root/.cache/huggingface/models/Qwen3-Coder-Next-FP8 \
--host 0.0.0.0 \
--port 8000 \
--served-model-name Qwen3-Coder-Next-FP8 \
--max-model-len 8192 \
--gpu-memory-utilization 0.75

16K 上下文启动:

1
2
3
4
5
6
7
8
9
10
docker run -it --gpus all --network host \
--ipc=host --ulimit memlock=-1 --ulimit stack=67108864 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
nvcr.io/nvidia/vllm:26.01-py3 \
vllm serve /root/.cache/huggingface/models/Qwen3-Coder-Next-FP8 \
--host 0.0.0.0 \
--port 8000 \
--served-model-name Qwen3-Coder-Next-FP8 \
--max-model-len 16384 \
--gpu-memory-utilization 0.75

适用判断:

项目 结果
模型大小 约 54.1G
2048 输入 / 512 输出 / 20 请求 / 1 RPS 全部成功,Mean TTFT 约 4.4s,Mean TPOT 约 124ms
2048 输入 / 512 输出 / 50 请求 / 4 RPS 全部成功,但 Mean TTFT 约 9s,P99 TTFT 超过 20s
14336 输入 / 1024 输出 / 3 请求 / 0.1 RPS 全部成功,16K 级上下文可用
32768 输入 / 8192 输出 / 2 请求 / 0.03 RPS 全部成功,但总耗时约 6 分钟
推荐用途 复杂任务、长上下文单人或低并发
不推荐用途 多人高并发默认模型

启动后使用 watch -n 1 free -h 观察 DGX 主机内存:

Qwen3-Coder-Next-FP8 启动后内存监测

截图显示:总内存约 121Gi,已用约 98Gi,可用约 23Gi,swap 使用约 1.7Gi。相比 30B FP8,Next-FP8 启动时主机可用内存更多,但已经出现 swap 使用;后续做长上下文和并发压测时,需要同时看 free -h 和 vLLM 日志里的 KV cache / Waiting 状态。

4.3 Docker 参数说明

参数 含义 影响
docker run 创建并运行容器 每次执行会启动一个新的 vLLM 服务容器
-it 交互式终端 适合前台观察日志;SSH 断开或终端关闭时服务可能停止
--rm 容器退出后自动删除 适合一次性 benchmark,不适合长期服务
-d 后台运行容器 适合生产或长时间服务,需配合 docker logs 查看日志
--name <name> 指定容器名 方便 docker logs <name>docker stop <name> 管理
--gpus all 把所有 GPU 暴露给容器 vLLM 才能使用 NVIDIA GPU;如果只想用部分 GPU,需要改成指定设备
--network host 使用宿主机网络 容器内监听 8000 等同宿主机 8000;最简单,但端口冲突要自己处理
--ipc=host 使用宿主机 IPC 命名空间 减少共享内存限制导致的问题,常用于大模型容器
--ulimit memlock=-1 允许锁定内存无限制 避免部分 CUDA / NCCL / 推理路径受内存锁定限制影响
--ulimit stack=67108864 设置栈大小为 64MiB 防止深调用或底层库栈空间不足
-e HF_HUB_OFFLINE=1 容器内开启 Hugging Face 离线模式 本地模型完整时建议开启,避免启动时联网
-e TRANSFORMERS_OFFLINE=1 容器内开启 Transformers 离线模式 避免 Transformers 联网拉配置
-v ~/.cache/huggingface:/root/.cache/huggingface 挂载宿主机模型缓存到容器 容器内模型路径要写 /root/.cache/huggingface/...
nvcr.io/nvidia/vllm:26.01-py3 NVIDIA vLLM 容器镜像 决定 vLLM、Transformers、CUDA、Torch 版本;新模型架构不识别时需要换镜像或 venv
bash -lc '<cmd>' 在容器内用 bash 执行命令 benchmark 命令较长时方便传入多行命令

4.4 vLLM serve 参数说明

参数 含义 影响
vllm serve <model_path> 启动 OpenAI 兼容服务,加载指定模型目录 <model_path> 必须包含 config.json、tokenizer 和权重文件
--host 0.0.0.0 监听所有网卡 局域网其他机器可以访问;只本机访问可用 127.0.0.1
--port 8000 服务端口 OpenAI 兼容接口会暴露在 http://<host>:8000/v1;端口冲突会启动失败
--served-model-name 对外暴露的模型名 API 请求里的 "model" 必须和它一致;可以和目录名不同
--max-model-len 单请求最大上下文长度 必须大于等于输入 tokens + 输出 tokens;值越大,KV cache 预留越多,显存/内存压力越高,并发能力可能下降
--enable-prefix-caching 开启前缀缓存 多个请求有相同系统提示词或相同代码上下文前缀时可减少重复 prefill;会占用额外缓存管理资源
--gpu-memory-utilization vLLM 可使用的 GPU 显存比例 值越高,KV cache 越大、可支持更长上下文或更多并发;太高可能 OOM 或影响系统稳定
--tensor-parallel-size 张量并行 GPU 数 单卡机器设 1;多卡模型切分时才调大
--reasoning-parser qwen3 使用 Qwen3 推理内容解析器 适合 Qwen3 系列 reasoning 输出格式;不需要 reasoning 格式时可省略
--language-model-only 只启用语言模型能力 避免多模态等额外逻辑;纯文本模型建议启用
--enable-auto-tool-choice 开启自动工具调用选择 opencode 这类 Agent 会通过 OpenAI tool calling 协议让模型决定是否调用工具;不开时工具调用兼容性会变差
--tool-call-parser qwen3_coder 指定工具调用解析器 Qwen3 / Qwen3-Coder 系列接 opencode 时建议使用 qwen3_coder;实测 hermes 会导致 opencode 没什么输出
--moe-backend triton MoE 专家计算后端 某些非 FP8 MoE 模型默认后端会 JIT 失败,用 triton 可避开部分问题
--max-num-seqs 同时调度的最大序列数 降低它可以减少 cache 和调度压力;太低会限制并发吞吐

关键调参关系:

1
2
3
4
5
max-model-len >= 输入 tokens + 输出 tokens
gpu-memory-utilization 越高,KV cache 越大,但 OOM 风险越高
并发越高,总吞吐可能提高,但单个用户的 TTFT 和 TPOT 通常会变差
长输入主要增加 prefill 时间,所以最明显影响 TTFT
长输出主要增加 decode 时间,所以最明显影响总耗时和 TPOT

5. 用本机 venv 启动新版架构模型

Qwen3.6-27B-FP8Qwen3.6-35B-A3B-FP8 在 Docker 镜像 nvcr.io/nvidia/vllm:26.01-py3 中启动失败,原因是该镜像内置 Transformers/vLLM 不识别 qwen3_5 / qwen3_5_moe 架构。

已经跑通的本机环境:

项目 结果
vLLM 0.22.0
Transformers 5.9.0
Torch 2.11.0+cu130
启动方式 source ~/.venv/bin/activate 后直接 vllm serve

5.1 启动 Qwen3.6-35B-A3B-FP8

前台启动:

1
2
3
4
5
6
7
8
9
10
11
12
source ~/.venv/bin/activate

vllm serve ~/.cache/huggingface/models/Qwen3.6-35B-A3B-FP8 \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--max-model-len 131072 \
--reasoning-parser qwen3 \
--language-model-only \
--enable-auto-tool-choice \
--tool-call-parser qwen3_coder \
--served-model-name Qwen3.6-35B-A3B-FP8

后台启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
source ~/.venv/bin/activate

nohup vllm serve ~/.cache/huggingface/models/Qwen3.6-35B-A3B-FP8 \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--max-model-len 131072 \
--reasoning-parser qwen3 \
--language-model-only \
--enable-auto-tool-choice \
--tool-call-parser qwen3_coder \
--served-model-name Qwen3.6-35B-A3B-FP8 \
> /tmp/codex_vllm_qwen36_35b.log 2>&1 &

echo $! > /tmp/codex_vllm_qwen36_35b.pid

启动后使用 watch -n 1 free -h 观察 DGX 主机内存:

Qwen3.6-35B-A3B-FP8 启动后内存监测

截图显示:总内存约 121Gi,已用约 118Gi,可用约 3.0Gi,buff/cache 约 3.3Gi。这和后续测试记录一致:Qwen3.6-35B-A3B-FP8 能跑通长上下文和真实 Agent 任务,但主机内存余量非常小。因此这个模型可以保留为高质量默认候选,但不建议继续做 64K/128K 多人并发压测。

后台启动参数说明:

参数 / 写法 含义 影响
source ~/.venv/bin/activate 激活本机 Python 虚拟环境 使用 venv 里的新版 vLLM / Transformers
nohup ... & 后台运行,终端退出后继续服务 适合长期服务;日志不会直接显示在终端
> /tmp/xxx.log 2>&1 stdout 和 stderr 都写入日志 tail -f /tmp/xxx.log 观察加载和运行状态
echo $! > /tmp/xxx.pid 保存后台进程 PID 后续可用 kill $(cat /tmp/xxx.pid) 停止服务

启动成功日志:

1
2
3
4
5
6
Resolved architecture: Qwen3_5MoeForConditionalGeneration
Using max model len 131072
Model loading took 33.38 GiB memory and 269.21 seconds
Available KV cache memory: 74.67 GiB
GPU KV cache size: 3,795,968 tokens
Maximum concurrency for 131,072 tokens per request: 28.96x

注意:这个模型启动后的主机内存占用非常高,测试前后可用内存最低只有约 3.6-6.2GiB,并出现过 swap 使用约 3.3GiB。因此不建议继续做 64K/128K 多人并发压测。

测试结果摘要:

场景 成功 / 失败 Mean TTFT Mean TPOT 输出吞吐 结论
512 输入 / 256 输出 / 20 请求 / 1 RPS 20 / 0 312ms 68ms 146 tok/s 短上下文体验好
2048 输入 / 512 输出 / 20 请求 / 1 RPS 20 / 0 755ms 90ms 165 tok/s 中等代码上下文可用
2048 输入 / 512 输出 / 30 请求 / 3 RPS 30 / 0 1.64s 121ms 221 tok/s 多人中等上下文可稳定跑完
14336 输入 / 1024 输出 / 3 请求 / 0.1 RPS 3 / 0 2.71s 29ms 51 tok/s 16K 可用
28672 输入 / 1024 输出 / 1 请求 / 0.05 RPS 1 / 0 5.64s 21ms 21.6 tok/s 32K 单请求可用
57344 输入 / 2048 输出 / 1 请求 / 0.02 RPS 1 / 0 13.2s 24ms 18.2 tok/s 64K 单请求可用
122880 输入 / 1024 输出 / 1 请求 / 0.01 RPS 1 / 0 37.6s 30ms 6.1 tok/s 近 128K 单请求可用,但首 token 等待很长

5.2 启动 Qwen3.6-27B-FP8

1
2
3
4
5
6
7
8
9
10
source ~/.venv/bin/activate

vllm serve ~/.cache/huggingface/models/Qwen3.6-27B-FP8 \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--max-model-len 131072 \
--reasoning-parser qwen3 \
--language-model-only \
--served-model-name Qwen3.6-27B-FP8

启动成功日志:

1
2
3
4
5
6
Resolved architecture: Qwen3_5ForConditionalGeneration
Using max model len 131072
Model loading took 27.64 GiB memory and 203.50 seconds
Available KV cache memory: 77.69 GiB
GPU KV cache size: 1,244,034 tokens
Maximum concurrency for 131,072 tokens per request: 9.49x

测试结果摘要:

场景 成功 / 失败 Mean TTFT Mean TPOT 输出吞吐 结论
512 输入 / 256 输出 / 20 请求 / 1 RPS 20 / 0 2.40s 165ms 86 tok/s 短上下文交互速度不理想
2048 输入 / 512 输出 / 20 请求 / 1 RPS 20 / 0 3.69s 180ms 95 tok/s 中等上下文可跑,但慢
14336 输入 / 1024 输出 / 3 请求 / 0.1 RPS 3 / 0 7.83s 145ms 17 tok/s 16K 可跑但慢
122880 输入 / 1024 输出 / 1 请求 / 0.01 RPS 1 / 0 100s 160ms 2.8 tok/s 近 128K 可跑,但不适合作为默认模型

5.3 启动 Qwen3.5-35B-A3B

这个模型不是 FP8,权重占用约 64.69GiB,KV cache 只剩约 3.91GiB。默认 MoE backend 容易遇到 FlashInfer JIT 被 kill 或 Mamba cache blocks 不足的问题,需要使用保守参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
source ~/.venv/bin/activate

vllm serve ~/.cache/huggingface/models/Qwen3.5-35B-A3B \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--max-model-len 8192 \
--reasoning-parser qwen3 \
--language-model-only \
--served-model-name Qwen3.5-35B-A3B \
--gpu-memory-utilization 0.60 \
--moe-backend triton \
--max-num-seqs 128

启动成功日志:

1
2
3
4
Model loading took 64.69 GiB memory and 91.50 seconds
Available KV cache memory: 3.91 GiB
GPU KV cache size: 143,732 tokens
Maximum concurrency for 8,192 tokens per request: 17.55x

测试结果摘要:

场景 成功 / 失败 Mean TTFT Mean TPOT 输出吞吐 结论
512 输入 / 256 输出 / 20 请求 / 1 RPS 20 / 0 628ms 131ms 102 tok/s 可用
2048 输入 / 512 输出 / 20 请求 / 1 RPS 20 / 0 1.12s 150ms 110 tok/s 可用,但不快
7168 输入 / 512 输出 / 5 请求 / 0.2 RPS 5 / 0 1.91s 66ms 44 tok/s 8K 内可作为质量对照

6. API 连通性测试

6.1 查看模型列表

1
curl http://localhost:8000/v1/models

如果从局域网其他机器访问,把 localhost 换成 DGX 的 IP,例如:

1
curl http://192.168.50.75:8000/v1/models

6.2 Chat Completions 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen3-Coder-30B-A3B-Instruct-FP8",
"messages": [
{
"role": "user",
"content": "用 Python 写一个快速排序,并解释时间复杂度"
}
],
"max_tokens": 1000,
"temperature": 0.2
}'

请求参数说明:

参数 含义 影响
/v1/chat/completions OpenAI 兼容聊天接口 Cline、Roo Code、Continue、OpenAI SDK 等可以接这个接口
Content-Type: application/json 请求体格式 必须设置,否则服务端可能无法解析 JSON
model 请求使用的模型名 必须等于启动时的 --served-model-name
messages 聊天上下文 包含 system/user/assistant 消息;内容越长,prefill 越慢,TTFT 越高
role 消息角色 system 适合放全局指令,user 放用户问题,assistant 放历史回答
content 消息文本 计入输入 tokens,占用上下文窗口
max_tokens 最大生成 tokens 输出上限;越大,总耗时和显存占用越高
temperature 采样随机性 越低越稳定、越确定;代码生成建议 0.1-0.3

6.3 Python SDK 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from openai import OpenAI

client = OpenAI(
base_url="http://192.168.50.75:8000/v1",
api_key="EMPTY",
)

resp = client.chat.completions.create(
model="Qwen3-Coder-30B-A3B-Instruct-FP8",
messages=[
{
"role": "system",
"content": "You are an expert coding assistant. Return concise and correct code.",
},
{
"role": "user",
"content": "Write a Python quicksort implementation with type hints and assert tests.",
},
],
temperature=0.2,
max_tokens=1024,
)

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

Python 参数说明:

参数 含义 影响
base_url OpenAI 兼容服务地址 本机用 http://localhost:8000/v1,局域网机器用 DGX IP
api_key="EMPTY" 占位 API key vLLM 默认不校验 key,但 OpenAI SDK 要求传入
client.chat.completions.create 调用聊天补全接口 和 curl 的 /v1/chat/completions 等价

6.4 opencode 局域网接入

本地电脑上的 opencode 可以通过局域网连接 DGX 上的 vLLM OpenAI 兼容接口。例如 DGX 地址是 192.168.50.75,服务端口是 8000,则 opencode 侧模型服务地址应指向:

1
http://192.168.50.75:8000/v1

DGX 启动 Qwen3.6-35B-A3B-FP8 时,需要加上工具调用相关参数:

1
2
3
4
5
6
7
8
9
10
11
12
source ~/.venv/bin/activate

vllm serve ~/.cache/huggingface/models/Qwen3.6-35B-A3B-FP8 \
--host 0.0.0.0 \
--port 8000 \
--tensor-parallel-size 1 \
--max-model-len 131072 \
--reasoning-parser qwen3 \
--language-model-only \
--enable-auto-tool-choice \
--tool-call-parser qwen3_coder \
--served-model-name Qwen3.6-35B-A3B-FP8

实测结论:

项目 结论
--enable-auto-tool-choice 必须加。opencode 会使用工具调用协议,不开启自动工具选择会影响 Agent 调用工具的行为
--tool-call-parser qwen3_coder 必须使用这个 parser。它能正确解析 Qwen3 系列工具调用输出
--tool-call-parser hermes 不适合这里。实测 opencode 会没什么输出
--host 0.0.0.0 必须监听所有网卡,否则局域网电脑无法访问
--port 8000 opencode 侧 base URL 要和这个端口一致

一次 opencode 任务日志观察:本地电脑 192.168.50.52 通过局域网请求 DGX,让模型写一个一维有限元算法程序。服务端多次出现:

1
192.168.50.52:<port> - "POST /v1/chat/completions HTTP/1.1" 200 OK

这说明 opencode 到 DGX 的 OpenAI 兼容接口连通正常,HTTP 请求都成功返回。日志里的推理状态大致如下:

日志项 观察结果 解读
Running 多数时间为 0 或 1 这是单个 opencode Agent 任务,没有形成多人并发压力
Waiting 一直为 0 没有排队,服务端当前处理能力够用
GPU KV cache usage 约 0.0%-0.9% KV cache 压力非常低,这次不是上下文容量瓶颈
Prefix cache hit rate 0.0% 本轮请求没有命中前缀缓存;opencode 多轮工具调用的 prompt 可能变化较多
Avg prompt throughput 约 56-3192 tokens/s,后期多次到 2500-3100 tokens/s prefill 能跑起来,且上下文没有压满
Avg generation throughput 约 4-55 tokens/s 波动 工具调用和短输出片段会让瞬时吞吐波动,不能只按单条日志判断模型整体速度

日志中还出现了几类 Triton JIT warning:

1
2
3
4
5
Triton kernel JIT compilation during inference: _zero_kv_blocks_kernel
Triton kernel JIT compilation during inference: _compute_slot_mapping_kernel
Triton kernel JIT compilation during inference: _causal_conv1d_fwd_kernel
Triton kernel JIT compilation during inference: _fused_post_conv_kernel
Triton kernel JIT compilation during inference: fused_moe_kernel

这些 warning 的含义是:某些 Triton kernel 在实际推理过程中才第一次编译,会造成当次请求的延迟尖刺。它不是接口失败,也不是模型输出错误。更稳的做法是服务启动后先做几轮 warmup,覆盖 opencode 常见的 prompt 长度、工具调用形态和输出长度,减少正式使用时的 JIT 编译抖动。

这段日志的整体判断:

1
2
3
4
5
opencode -> DGX -> vLLM 的链路是通的。
Qwen3.6-35B-A3B-FP8 可以处理 opencode 的工具调用工作流。
本次任务没有排队,也没有 KV cache 压力。
主要需要注意首次或新形状请求触发 Triton JIT,可能带来短时延迟尖刺。
tool-call-parser 必须用 qwen3_coder,不能用 hermes。

7. Benchmark 压测

压测前先启动模型服务,然后另开一个 shell 执行 benchmark。

7.1 短上下文基线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run --rm --gpus all --network host \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
nvcr.io/nvidia/vllm:26.01-py3 \
bash -lc 'vllm bench serve \
--backend openai \
--base-url http://localhost:8000 \
--endpoint /v1/completions \
--model Qwen3-Coder-30B-A3B-Instruct-FP8 \
--tokenizer /root/.cache/huggingface/models/Qwen3-Coder-30B-A3B-Instruct-FP8 \
--num-prompts 20 \
--request-rate 1 \
--random-input-len 512 \
--random-output-len 256'

7.2 中等代码上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run --rm --gpus all --network host \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
nvcr.io/nvidia/vllm:26.01-py3 \
bash -lc 'vllm bench serve \
--backend openai \
--base-url http://localhost:8000 \
--endpoint /v1/completions \
--model Qwen3-Coder-30B-A3B-Instruct-FP8 \
--tokenizer /root/.cache/huggingface/models/Qwen3-Coder-30B-A3B-Instruct-FP8 \
--num-prompts 20 \
--request-rate 1 \
--random-input-len 2048 \
--random-output-len 512'

7.3 多人并发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run --rm --gpus all --network host \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
nvcr.io/nvidia/vllm:26.01-py3 \
bash -lc 'vllm bench serve \
--backend openai \
--base-url http://localhost:8000 \
--endpoint /v1/completions \
--model Qwen3-Coder-Next-FP8 \
--tokenizer /root/.cache/huggingface/models/Qwen3-Coder-Next-FP8 \
--num-prompts 50 \
--request-rate 4 \
--random-input-len 2048 \
--random-output-len 512'

7.4 16K 长上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run --rm --gpus all --network host \
-e HF_HUB_OFFLINE=1 \
-e TRANSFORMERS_OFFLINE=1 \
-v ~/.cache/huggingface:/root/.cache/huggingface \
nvcr.io/nvidia/vllm:26.01-py3 \
bash -lc 'vllm bench serve \
--backend openai \
--base-url http://localhost:8000 \
--endpoint /v1/completions \
--model Qwen3-Coder-Next-FP8 \
--tokenizer /root/.cache/huggingface/models/Qwen3-Coder-Next-FP8 \
--num-prompts 3 \
--request-rate 0.1 \
--random-input-len 14336 \
--random-output-len 1024'

7.5 Benchmark 参数说明

参数 含义 影响
vllm bench serve vLLM 的服务端压测工具 对已经运行的 OpenAI 兼容服务发请求
--backend openai 使用 OpenAI 兼容协议 对应 vLLM 的 /v1 接口
--base-url 服务基础地址 本机一般是 http://localhost:8000
--endpoint 压测接口 /v1/completions 是文本补全接口;聊天应用也可用 chat 接口另测
--model 请求模型名 必须等于服务启动时的 --served-model-name
--tokenizer tokenizer 路径 用于生成指定长度的随机输入;应指向同一个模型目录
--num-prompts 总请求数 越大越接近真实负载;也会让压测耗时更长
--request-rate 请求注入速率,单位 RPS 越高越容易形成排队;实际完成速率可能远低于注入速率
--random-input-len 每个请求的输入 token 长度 越大 prefill 越慢,TTFT 越高,KV cache 占用越大
--random-output-len 每个请求的输出 token 长度 越大 decode 时间越长,总耗时越高

--random-input-len + --random-output-len 必须小于或等于服务启动时的 --max-model-len。例如输入 14336、输出 1024,总计 15360,需要 --max-model-len 至少 16384。

7.6 压测指标说明

指标 含义 怎么看
Successful requests 成功请求数 应等于 --num-prompts
Failed requests 失败请求数 应为 0;非 0 要看是否超上下文、OOM、服务断开
Benchmark duration 压测总耗时 用于判断整体完成时间
Request throughput 实际完成请求速率 不一定等于 --request-rate,模型慢时会低很多
Output token throughput 总输出 token 吞吐 代表服务整体生成能力
Peak output token throughput 峰值输出吞吐 看短时间最高生成能力
Peak concurrent requests 峰值并发请求数 反映 vLLM 批处理调度情况
Mean TTFT 平均首 token 延迟 影响用户等待第一段回答的体验
P99 TTFT 最慢 1% 首 token 延迟 看尾延迟;多人并发时尤其重要
Mean TPOT 后续每个 token 平均耗时 越低,生成速度越快
P99 TPOT 最慢 1% 后续 token 耗时 看生成阶段稳定性

服务端日志也要同时观察:

日志字段 含义 判断方式
Running 正在运行的请求数 长时间很高说明服务在持续消化请求
Waiting 排队等待的请求数 持续大于 0 说明注入压力超过服务处理能力
GPU KV cache usage KV cache 使用率 高到接近满时会限制长上下文和并发
Avg generation throughput 平均生成吞吐 看 decode 阶段整体 token/s

7.7 真实 Agent 任务测试:一维 FEM 程序

除了合成 benchmark,还做过一次更接近真实开发场景的测试:本地电脑上的 opencode 通过局域网连接 DGX 上的 Qwen3.6-35B-A3B-FP8,让模型编写和多轮修复一个一维有限元算法程序 fem_1d.py。随后使用 Codex 对程序做审查、构造反例测试、指出不足,再让 opencode 根据这些反馈继续修复。

相关记录文件:

文件 内容
对话记录.md opencode 生成和修复 fem_1d.py 的过程摘要
conversation_history_codex.md Codex 对 fem_1d.py 的审查、测试和问题定位
historyChat.md opencode 根据问题反馈修复非零边界、非法区间、非法单元数等问题的记录
fem_1d.py 多轮修复后的最终一维 FEM 程序

任务特点:

1
2
3
4
5
6
任务类型:代码生成 + 数值算法调试 + 多轮修复
模型服务:DGX 上的 Qwen3.6-35B-A3B-FP8
客户端:本地电脑 opencode,通过局域网访问 /v1/chat/completions
接口能力:需要启用 tool calling,启动参数必须包含 --enable-auto-tool-choice 和 --tool-call-parser qwen3_coder
上下文压力:64K 上下文经过两次上下文压缩后仍然完成任务
最终产物:fem_1d.py,一维线性有限元 Poisson 方程求解器

最终程序包含的主要模块:

函数 / 结构 功能
FEMResult 保存插值解、节点解、网格等结果
element_stiffness(h) 计算线性单元刚度矩阵 (1/h) * [[1, -1], [-1, 1]]
element_load(f, h, a) 使用 2 点 Gauss 积分计算单元载荷,并正确做参考坐标到物理坐标的映射
assemble(mesh, f) 组装全局刚度矩阵和载荷向量
apply_bc(K, F, bcs) 施加 Dirichlet 边界条件,并修正非零边界值对右端项的贡献
evaluate_at_points(...) 将节点解插值到密集采样点
compute_errors(...) 计算 L2Linf 误差
solve_1d_poisson(...) 求解 -u'' = f(x),支持自定义区间、单元数、左右 Dirichlet 边界值和自定义线性求解器

多轮修复中暴露和解决的问题:

问题 表现 修复结果
单元载荷坐标映射错误 网格加密后误差反而增大,收敛率为负 element_load 增加单元左端点 a,正确映射 xg = a + 0.5 * h * (1 + xi)
非零 Dirichlet 边界条件错误 u(0)=1, u(1)=2 这类问题内部节点值错误 apply_bc 在清零边界列前执行 F[j] -= K[j, idx] * value,把已知边界值贡献转移到自由 DOF
domain=(1.0, 1.0) 产生 nan 和除零警告 增加 a < b 校验,非法区间抛 ValueError
n_elems=0 返回退化网格,物理意义不明确 增加正整数校验
n_elems=-1 触发 IndexError 统一抛出明确的 ValueError
n_elems=4.0 类型语义不清 拦截非整数单元数
np.trapz 弃用 后续维护有兼容风险 改为 np.trapezoid

最终复测环境使用本地 Windows 解释器:

1
E:\miniconda\envs\AgentTool\python.exe

语法检查通过:

1
& 'E:\miniconda\envs\AgentTool\python.exe' -m py_compile fem_1d.py

内置 demo 复现线性 FEM 理论二阶收敛:

1
2
3
4
5
6
7
8
    N           h       L2 error   L2 rate     Linf error   Linf rate
---------------------------------------------------------------------
4 2.50e-01 3.91e-02 -- 7.01e-02 --
8 1.25e-01 9.91e-03 1.98 1.88e-02 1.90
16 6.25e-02 2.49e-03 2.00 4.79e-03 1.97
32 3.12e-02 6.22e-04 2.00 1.20e-03 1.99
64 1.56e-02 1.56e-04 2.00 3.01e-04 2.00
128 7.81e-03 3.89e-05 2.00 7.52e-05 2.00

关键反例复测结果:

1
2
3
4
5
6
7
8
zero_load_nonzero_bc: max_err=0.000e+00
constant_load_nonzero_bc: max_err=0.000e+00
shifted_domain_nonzero_bc: max_err=0.000e+00
n_elems=0: ValueError
n_elems=-1: ValueError
n_elems=4.0: ValueError
domain=(1.0, 1.0): ValueError
domain=(2.0, 1.0): ValueError

这次真实任务的模型表现判断:

1
2
3
4
5
Qwen3.6-35B-A3B-FP8 能完成多轮代码生成和修复任务。
64K 级上下文在两次压缩后仍能保持任务主线,最终产物可运行并通过数值反例测试。
模型初始生成并非一次正确,数值算法细节仍需要外部审查和反例驱动修复。
Codex 负责评审和构造测试,opencode 负责执行修复,这种“生成-审查-修复”闭环比单次生成更可靠。
对数值算法类任务,不能只看程序能运行,还必须验证解析解、非零边界、非法输入和收敛率。

8. 常用调参建议

8.1 只做本机 / 单人代码 Agent

1
2
--max-model-len 8192
--gpu-memory-utilization 0.75

适合普通代码问答、短文件编辑、低并发。首 token 延迟更低,显存压力更小。

8.2 中等代码库上下文

1
2
--max-model-len 16384
--gpu-memory-utilization 0.75

适合 2K-8K 输入、512-1024 输出。需要重点看 TTFT,如果首 token 超过几秒,说明 prefill 已经成为主要瓶颈。

8.3 长上下文单人任务

1
2
--max-model-len 65536
--gpu-memory-utilization 0.75

适合大段代码、长日志、长文档总结。不要直接上多人并发,先用 1-3 个请求验证。

8.4 近 128K 上下文

1
--max-model-len 131072

只建议在已经确认模型能稳定启动后做单请求验证。近 128K 输入会显著增加 prefill 时间,首 token 等待可能达到几十秒甚至更久。即使 KV cache 没打满,计算吞吐也可能成为瓶颈。

8.5 多人并发

多人并发不要只看能否跑完,还要看:

1
2
3
4
5
Mean TTFT
P99 TTFT
Mean TPOT
Waiting
GPU KV cache usage

如果 Waiting 持续大于 0,或者 P99 TTFT 超过 10-20 秒,说明当前模型或参数已经不适合作为多人默认服务。

9. 停止服务和查看日志

Docker 前台启动时,直接在终端按:

1
Ctrl+C

Docker 后台启动时:

1
2
3
docker ps
docker logs -f <container_name>
docker stop <container_name>

venv 后台启动时:

1
2
tail -f /tmp/codex_vllm_qwen36_35b.log
kill $(cat /tmp/codex_vllm_qwen36_35b.pid)

如果服务没有保存 PID,可以查进程:

1
ps aux | grep 'vllm serve'

确认端口占用:

1
ss -lntp | grep 8000

10. 推荐落地配置

如果只保留一个默认服务,优先使用:

1
Qwen3.6-35B-A3B-FP8

原因是它在短上下文、中等上下文、16K、64K、近 128K 单请求测试中都能跑通,且比 Qwen3.6-27B-FP8 更快。缺点是启动后主机内存占用很高,不建议继续做 64K/128K 多人并发。

如果希望用 Docker 保持部署简单,优先使用:

1
Qwen3-Coder-30B-A3B-Instruct-FP8

它是更稳的日常代码 Agent 默认模型。Qwen3-Coder-Next-FP8 可以作为复杂任务备用模型,但不建议作为多人高并发默认模型。