# vLLM 部署 前沿大模型 从显存算术到并行拓扑

当模型来到 **2.8 万亿参数**（Kimi-K3.1-Pro，Qwen3.8 亦有 2.4T）、上下文来到 **100 万 token**，部署的难点已经从「装得下」变成「算得清」： 权重、KV Cache、通信、批调度四本账必须同时算平。这份手册以 vLLM 0.33 与官方 Recipes 为准， 把参数的含义、取值依据、以及不同硬件下的完整命令逐条拆开。

> 引擎 vLLM 0.33.0 · 发布 2026-11-02 · Python 3.11 – 3.14 · 覆盖模型 13 个前沿系列

## 00 导读与数据基准

先说清楚这份文档的边界：它讲什么、数据从哪来、哪些结论会随版本失效。

2026 年的开源模型迭代速度已经超过文档更新速度。本文所有参数释义、默认值、启动命令，均取自 **vLLM 官方文档（docs.vllm.ai，页面更新于 2026-10-30）**与 **vLLM Recipes 仓库（recipes.vllm.ai，逐模型 YAML，最后更新 2026-11-05）**；模型规模与架构取自各模型 `config.json` 与官方模型卡实测值，不是估算。

> **为什么以 Recipes 为准**
> 
> vLLM 官方维护了一套逐模型的 YAML 配方（`recipes/models/<org>/<model>.yaml`），里面不仅有验证过的启动命令，还有 *每个精度变体的最小显存*、*已验证的硬件列表*、*最低 vLLM 版本*。这比博客和二手教程可靠得多——本文的模型表格几乎全部来自这里。

### 三个必须先建立的心智模型

#### ① 权重决定「能不能跑」

稀疏 MoE 让总参数和激活参数脱钩。DeepSeek-V4-Pro 是 1.6T 总参 / 49B 激活，权重仍要按 1.6T 算显存，但每 token 的计算量只按 49B 算。所以「参数大」不等于「慢」，但一定等于「贵」。

#### ② 注意力架构决定「能开多长」

MLA 把 KV 压成低秩潜向量；DSA / MSA / CSA 再叠加稀疏选择；GDN / KDA 用线性注意力把状态变成常数。同样是 1M 上下文，不同模型的 KV 增速相差一个数量级。

#### ③ 并行拓扑决定「快不快」

MoE 模型默认不再是 TP，而是 **DP（注意力）+ EP（专家）**。选错拓扑，即便显存够，吞吐也可能只有最优配置的三成。

#### ④ 版本决定「有没有这个开关」

0.33.0 是当前稳定版。若某模型要求 ≥0.34.0（如 Kimi-K3.1-Pro、Mistral-Large-4），意味着必须用 day-0 镜像，稳定版 wheel 里没有对应代码路径。

### 版本与兼容性基线

| 组件 | 基线 | 说明 |
| --- | --- | --- |
| vLLM | 0.33.0 | 2026-11-02 发布，当前最新稳定版 |
| Python | ≥3.11, <3.15 | 官方 wheel 的 `requires_python` |
| CUDA | 12.8 / 13.0 | Blackwell 建议 cu130 镜像 |
| ROCm | 7.0 / 10.0 | MI355X 新特性多在 ROCm 10 nightly |
| 引擎架构 | V1 | V0 已在 0.11 起移除；本文全部按 V1 描述 |

> **时效性声明**
> 
> 模型的支持状态、内核后端自动选择策略、默认值都在快速变化。凡是涉及「默认值」的判断，请在目标机器上执行 `vllm serve --help` 或查看启动日志确认，不要照抄本文数字。

## 01 2026 的部署格局

三个结构性变化，让 2025 年的部署经验基本失效。

### 变化一：稀疏 MoE 成为默认形态

2025 年的主流还是 70B 稠密模型；2026 年的旗舰清一色是细粒度专家混合。激活比例被压到 3%–6%，这意味着**显存瓶颈和算力瓶颈彻底分离**：你需要为 1.6T 参数准备 HBM，但每 token 只激活 49B，单卡算力反而不是主要矛盾，*跨卡通信*才是。

### 变化二：上下文进入百万级

DeepSeek-V4 系列、Kimi-K3、MiniMax-M3、GLM-5.3、Mistral-Large-4 都标称 1,048,576 token；Qwen3.5 原生 262,144，通过 YaRN 可扩到约 101 万。这带来两个直接后果：

- **Prefill 变成主要成本。**一次 1M token 的 prefill 计算量相当于生成数万 token，必须靠 chunked prefill 和 PD 分离来隔离。
- **KV Cache 的绝对值仍然惊人。**即便压缩到 10%，1M 上下文 × 高并发的 KV 需求依然会吃掉整机 HBM。

### 变化三：注意力架构分道扬镳

| 架构族 | 代表模型 | KV 增长 | 部署含义 |
| --- | --- | --- | --- |
| MLA（多头潜注意力） | DeepSeek-V3/V4、GLM-5、Kimi | 极低，按潜向量存 | 长上下文友好，但需要 `fp8_ds_mla` 等专用 KV 量化 |
| 稀疏注意力（DSA / MSA / CSA+HCA） | DeepSeek-V4、GLM-5.3、MiniMax-M3 | 额外索引器状态 | 需要 `indexer_kv_dtype` 独立配置，算子后端有强绑定 |
| 线性注意力混合（GDN / KDA） | Qwen3.5、Kimi-K3、Qwen3.8 | 常数状态 + 周期性全注意力 | 需要 Mamba cache 管理，`--max-num-seqs` 受 cache block 数限制 |
| GQA + 滑窗 | gpt-oss、Llama 系 | 线性、可预测 | 调优逻辑最经典，适合做基线 |

### 2026 前沿模型一览

下表为本文覆盖的 11 个系列。显存列为官方 recipe 给出的**最小可加载 GPU 显存**（含 1.2 倍余量），不是推荐值。

| 模型 | 总参 / 激活 | 上下文 | 注意力 | 精度 | 最小显存 | vLLM |
| --- | --- | --- | --- | --- | --- | --- |
| **DeepSeek-V4-Pro** | 1.6T / 49B | 1,048,576 | CSA + HCA 混合 | FP4+FP8 | 960 GB | ≥0.20 |
| **DeepSeek-V4.1-Flash** | 552B / 8B·16B | 1,048,576 | SWA + CSA，双层索引 | FP8 | 614 GB | ≥0.31 |
| **Qwen3.5-397B-A17B** | 397B / 17B | 262,144 → 1.01M | GDN + 每 4 层全注意力 | BF16/FP8/NVFP4/INT4 | 953 / 477 / 238 | ≥0.17 |
| **Kimi-K3.1-Pro** | 2.8T / 16 of 896 | 1,048,576 | KDA + 稀疏 MLA | MXFP4 | 1,874 GB | ≥0.34 |
| **GLM-5** | 744B / 40B | 202,752 | MLA + DSA | BF16/FP8/NVFP4 | 1786 / 893 / 446 | ≥0.24 |
| **GLM-5.3** | 743B / 39B | 1,048,576 | MLA + DSA，5-token MTP | FP8/NVFP4/INT4 | 893 / 558 / 447 | ≥0.29 |
| **MiniMax-M3** | 427B / 26B | 1,048,576 | MSA 块稀疏，原生多模态 | BF16/MXFP8/NVFP4 | 1025 / 513 / 257 | ≥0.24 |
| **ERNIE-5.5-Flash** | 280B / 14B | 1,048,576 | HCA + MLA-DSA，ECHO MTP | FP8/BF16 | 347 / 688 | ≥0.32* |
| **Mistral-Large-4** | 1T / 49B | 1,048,576 | 稀疏 MLA + DSpark | NVFP4/FP8/MXFP4 | 733 / 1273 / 694 | ≥0.34 |
| **Qwen3.8-Flash-Next** | 176B / 6B | 262,144 | Qwen4 预览，GDN + GQA | NVFP4/FP8/BF16/MXFP4 | 130 / 250 / 423 | ≥0.30 |
| **gpt-oss-120b** | 117B / 5.1B | 131,072 | 滑窗 + 全注意力交替 | MXFP4 | 96 | ≥0.10 |

* ERNIE-5.5-Flash 需额外安装 `vLLM-ERNIE` 插件，且插件锁定 0.32.0。

> **落地前必读**
> 
> **0.34.0 尚未发布。**Kimi-K3.1-Pro、Mistral-Large-4 要求 0.34.0，目前只能通过 day-0 镜像（`vllm/vllm-openai:kimi-k31`、`vllm/vllm-openai:mistral-large-4`）运行，且官方明确「pip 安装不可用」。这类模型的部署计划必须把镜像版本钉死，不能依赖 `pip install -U vllm`。

## 02 显存预算与 KV 计算

部署第一步不是敲命令，是算账。算错一个数量级，你会花两天在 OOM 里打转。

### 显存的四块开销

vLLM 启动后，一张卡的 HBM 被切成四份。理解这四份的比例，才知道该调哪个旋钮。

![图 1. 显存预算。权重是刚性成本，KV Cache 是唯一可以被调度和量化的弹性空间； --gpu-memory-utilization 控制的正是「权重之外还能占多少」。](assets/vllm/figure-01.svg)

图 1. 显存预算。权重是刚性成本，KV Cache 是唯一可以被调度和量化的弹性空间； --gpu-memory-utilization 控制的正是「权重之外还能占多少」。

| 区块 | 内容 | 控制手段 |
| --- | --- | --- |
| **权重** | 模型参数、embedding、LM head | 换精度（FP8/NVFP4/INT4）、换 checkpoint、加 TP/EP |
| **激活与工作区** | 前向中间张量、MoE all-to-all 缓冲、FlashInfer workspace | `--max-num-batched-tokens`、`--max-num-seqs` |
| **KV Cache** | 分页的 K/V 块 + 稀疏索引器状态 + Mamba 状态 | `--kv-cache-dtype`、`--block-size`、`--gpu-memory-utilization` |
| **图与碎片** | CUDA Graph 捕获、显存碎片 | `--max-cudagraph-capture-size`、`--enforce-eager` |

### KV Cache 的两种算法

不同注意力架构的 KV 计算方式完全不同，用错公式会差出十倍。

#### MHA / GQA / MQA：同一公式，不同取值

这三种架构**用的是同一个公式**，差别只在 `n_kv_heads` 取什么值。把「GQA / MHA」并列成一个类别容易让人误以为两者算法不同，实际上是**分组比 g 倍**的关系。

```
# bytes per token, per sequence
KV = 2 × n_layers × n_kv_heads × head_dim × dtype_bytes
#        ↑ K 和 V 各一份   ↑ 唯一的架构差异在这里

# MHA: n_kv_heads = n_heads        → 1×    
# GQA: n_kv_heads = n_heads / g    → 1/g   
# MQA: n_kv_heads = 1              → 1/n_heads

# example: Llama-3.3-70B, bf16 (64 q-heads / 8 kv-heads, g=8)
# 2 × 80 × 8 × 128 × 2 = 327,680 B ≈ 320 KiB / token
# 同一个模型若用 MHA（64 kv-heads）→ 2.56 MiB/token，整整 8 倍
# 8k 上下文单请求就要 2.6 GB —— 这就是 GQA 模型的并发天花板
```

| 架构 | n_kv_heads | 相对 MHA 的 KV 量 | 典型模型 |
| --- | --- | --- | --- |
| MHA | `= n_heads` | 1× | Llama-1/2、GPT-3 |
| GQA | `= n_heads / g` | **1/g** | Llama-3（g=8）、Qwen3（g=8） |
| MQA | `= 1` | 1/n_heads | PaLM、Falcon |

#### MLA 模型（DeepSeek / GLM / Kimi 系）

```
# MLA 只缓存低秩潜向量 + RoPE 分量，不存完整 K/V
KV = n_layers × (kv_lora_rank + qk_rope_head_dim) × dtype_bytes

# example: DeepSeek-V3 / R1, bf16
# 61 × (512 + 64) × 2 = 70,272 B ≈ 68.6 KiB / token

# example: GLM-5, fp8 (1 byte)
# 78 × (512 + 64) × 1 = 44,928 B ≈ 43.9 KiB / token
# 同样 fp8 下，GLM-5 每 token 只有 Llama-70B 的 1/7
```

> **稀疏注意力的额外开销**
> 
> DeepSeek-V4 的 CSA/HCA、GLM-5.3 的 DSA、MiniMax-M3 的 MSA 都会额外维护**索引器状态（indexer K）**，而且这部分通常**不跟随主 KV 一起量化**。Kimi-K3 的 KDA 层还会为每条序列保留常数大小的循环状态，这部分**不在分页 KV 池里**，会额外吃显存。所以：稀疏模型的真实 KV 占用 = 公式值 + 索引器 + 状态池，务必以启动日志为准。

### 算一算：KV Cache 计算器

改下面任意一项，立刻看到每 token 开销与可容纳的上下文条数。

> KV Cache 计算器为 HTML 交互功能，请在 [HTML 版](vllm-deployment.html#s2) 使用；计算公式见本节。

### 读懂启动日志

vLLM 启动完成时会打印两行关键信息，它们是唯一的权威数据：

```
INFO [kv_cache_utils.py] GPU KV cache size: 643,232 tokens
INFO [kv_cache_utils.py] Maximum concurrency for 40,960 tokens per request: 15.70x
```

- **第一行**是实际可容纳的 token 总数——不是字节数，是所有并发请求共享的池子。
- **第二行**按 `max_model_len` 折算并发数。这里的 40960 来自模型配置，不是你设的 `--max-model-len`。
- 两行数字偏小 → 加卡、降精度、开 KV 量化、或缩短 `--max-model-len`。

> **跳过显存测量的技巧**
> 
> 启动日志还会打印一个 `--kv-cache-memory` 值，把这个值原样传回去（亦作 `--kv-cache-memory-bytes`），下次启动就能**跳过 profiling 和 CUDA graph 显存估算**，冷启动快数十秒。但它只在「同 GPU、同初始空闲显存」下有效；换了硬件或同机有其他进程，必须去掉该参数重新测量。

## 03 架构与参数作用点

知道参数作用在哪个进程，才能判断它影响的是延迟、吞吐，还是启动时间。

### V1 多进程架构

vLLM V1 把服务拆成三类进程。每一类都吃 CPU，都可能是瓶颈。

![图 2. V1 进程模型。API Server 负责输入处理（分词、模板、多模态），Engine Core 负责调度与 KV 管理，GPU Worker 只做前向。参数标注在它真正生效的进程上。媒体线程数对应 VLLM_MEDIA_LOADING_THREAD_COUNT ；每步 token 预算对应 --max-num-batched-tokens 。](assets/vllm/figure-02.svg)

图 2. V1 进程模型。API Server 负责输入处理（分词、模板、多模态），Engine Core 负责调度与 KV 管理，GPU Worker 只做前向。参数标注在它真正生效的进程上。媒体线程数对应 VLLM_MEDIA_LOADING_THREAD_COUNT ；每步 token 预算对应 --max-num-batched-tokens 。

> **最常见的性能杀手：CPU 核数不足**
> 
> 官方明确要求：**至少 2 + N 个物理核**（1 个 API Server + 1 个 Engine Core + 每卡 1 个 Worker）。开启 DP 后公式变为 `A + DP + N + (DP>1 ? 1 : 0)`。例如 `DP=4, TP=2` 跑 8 卡，共有 4 + 4 + 8 + 1 = **17 个进程**。Engine Core 是忙循环，CPU 被抢占会直接表现为 GPU 利用率上不去——很多人误以为是 GPU 或模型问题。

### 一次请求的路径，与参数的作用位置

![图 3. 请求生命周期。TTFT 的三大来源是排队、prefill、首 token 采样；ITL 几乎完全由 decode 阶段的前向决定。调优前先确定自己优化的是哪一个。](assets/vllm/figure-03.svg)

图 3. 请求生命周期。TTFT 的三大来源是排队、prefill、首 token 采样；ITL 几乎完全由 decode 阶段的前向决定。调优前先确定自己优化的是哪一个。

## 04 核心参数详解

不是罗列所有参数，而是讲清那些「改了就出事」或「改了就有收益」的。

### 4.1 模型与精度

| 参数 | 默认 | 含义与取舍 |
| --- | --- | --- |
| `--model` | — | HF 仓库名或本地路径。**生产环境务必用本地路径**，避免启动时联网。国内网络设置 `VLLM_USE_MODELSCOPE=True` 走 ModelScope。 |
| `--dtype` | auto | 权重与激活精度。auto 会把 FP32/FP16 模型转成 FP16、BF16 模型保持 BF16。**不要手动指定 bfloat16 去「强制」一个 FP8 checkpoint**——那会绕过量化的 scale 路径。 |
| `--quantization` | 从 config 推断 | 模型自带 `quantization_config` 时无需指定。只有做在线量化（如 `--quantization fp8` 量化 fp16 权重）才需要显式传。 |
| `--kv-cache-dtype` | auto | KV 存储精度。可选 `fp8`/`fp8_e4m3`/`fp8_e5m2`/`nvfp4`/`fp8_ds_mla`/`int4_per_token_head` 等十余种。**FP8 KV 通常是最划算的一档**：显存减半，精度损失可忽略。`fp8_ds_mla` 专为 DeepSeek 系 MLA 设计。 |
| `--max-model-len` | 从 config 推断 | prompt + output 的总长度上限。支持 `1k`/`1K`/`25.6k` 人类可读格式，`-1` 或 `auto` 表示「塞得下多少用多少」。**调小它是解决 OOM 最快的手段**，但对长文场景是硬伤。 |
| `--tokenizer-mode` | auto | 2026 年的关键参数。DeepSeek-V4 用 `deepseek_v4`，Kimi-K3 用 `kimi_k3`，GLM-5.3 用 `glm5_next`。这些模型不再依赖 Jinja chat template，而是用 Python/Rust 渲染器。**不设这个参数，工具调用和推理模式会全错。** |
| `--trust-remote-code` | False | 加载模型自带的 Python 代码。前沿模型几乎都必须开。安全上是把执行权交给模型仓库，只在你信任来源时使用。 |
| `--block-size` | 视后端而定 | KV 块的 token 数。DeepSeek-V4 官方推荐 **256**，GLM 系常用 128，Kimi-K3 用 64。改小 → 碎片少、前缀匹配更细；改大 → 元数据开销低、但浪费尾部空间。 |

> **FP8 KV 值不值得开**
> 
> 值得，而且几乎是必开。MLA 模型 fp8 KV 让每 token 从 68.6 KiB 降到 34.3 KiB，Kimi-K3 的 NVFP4 KV 甚至把每 token 每层从 576 B 降到 352 B（约 1.5 倍容量），而 GSM8K 从 96.2% 只掉到 96.1%。唯一要注意：**滑窗层对 KV 量化更敏感**，可用 `--kv-cache-dtype-skip-layers sliding_window` 把这些层留在原精度。

### 4.2 显存与并发

| 参数 | 默认 | 含义与取舍 |
| --- | --- | --- |
| `--gpu-memory-utilization` | 0.92（部分场景 1.0） | 本实例最多占用多少比例的显存。**不是「用了多少」，是「允许用到多少」**。同卡多实例时各设 0.5 即可共存。调高 → KV 更大、并发更高，但留给激活的空间变小，长 prompt 易 OOM。 |
| `--kv-cache-memory-bytes` | None | 直接指定 KV 字节数，**设置后忽略 gpu-memory-utilization**。支持 `40G`/`37580963840`。适合需要精确控制、或与其它进程共卡的场景。 |
| `--max-num-seqs` | 测试用默认值 | 单次迭代最多处理多少条序列。**它同时决定 CUDA Graph 的捕获规模和常驻缓冲大小**。调小 → 尾延迟更稳；调大 → 吞吐高但 ITL 抖动。 |
| `--max-num-batched-tokens` | 随模型 | 单次迭代的 token 预算。这是**最重要的吞吐/延迟旋钮**：设 2048 时 ITL 最优（prefill 少打扰 decode）；设 >8192 时 TTFT 最优（一次吃完长 prompt）。官方建议大卡小模型用 >8192。 |
| `--enforce-eager` | False | 关闭 torch.compile 与 CUDA Graph，等价于 `-cc.mode=none -cc.cudagraph_mode=none`。**启动最快、稳态最慢**。仅用于开发调试或显存极度紧张。 |
| `--max-cudagraph-capture-size` | 512（Blackwell 数据中心卡 1024） | CUDA Graph 捕获的最大 batch。过大 → 启动慢、显存吃紧；过小 → 大批次回退到 eager，吞吐掉。有投机解码时需按 `S×(K+1)` 重算。 |
| `--cpu-offload-gb` | 0 | 每卡卸载多少 GiB 权重到 CPU，通过 UVA 零拷贝访问。**需要极快的 CPU-GPU 互联**，否则每层前向都变成 PCIe 瓶颈。只在「就差一点装不下」时用。 |

### 4.3 并行

| 参数 | 含义 | 何时用 |
| --- | --- | --- |
| `--tensor-parallel-size` / `-tp` | 层内切分权重，每层后 all-reduce | 单机装不下；或想给 KV 腾显存。**要求 attention head 数能被 TP 整除。** |
| `--pipeline-parallel-size` / `-pp` | 按层切分，跨 stage 传激活 | 跨节点、或卡间无 NVLink（如 L40S）；支持不整除的切分。 |
| `--data-parallel-size` / `-dp` | 复制完整模型，各 rank 处理不同请求 | 显存够、要堆吞吐。MoE 模型几乎必用。 |
| `--enable-expert-parallel` / `-ep` | MoE 专家分散到多卡，代替 TP | MoE 模型的默认选择。**EP 规模 = TP × DP，自动计算。** |
| `--all2all-backend` | EP 的通信实现 | 单机默认 `allgather_reducescatter`；prefill 密集用 `deepep_high_throughput`；decode 密集用 `deepep_low_latency`；ROCm 用 `mori`。 |
| `--decode-context-parallel-size` / `-dcp` | 把 MLA 潜变量与索引器 KV 切成多份 | 长上下文 + 多会话。Kimi-K3 在 DCP8 下 KV 容量约为 TP8 的 7 倍，代价是 decode 慢 7–25%。 |
| `--numa-bind` | 启动前用 numactl 绑定 worker 到 GPU 本地 NUMA | 多路服务器。默认在满足条件时自动绑定；`--numa-bind-nodes` 可显式指定每卡的 NUMA 节点。 |

### 4.4 调度与缓存

**Chunked prefill 在 V1 中默认开启**，且调度器优先满足 decode。这条策略决定了 `--max-num-batched-tokens` 的双向效应：

![图 4. Chunked prefill 的双向效应。这张图解释了为什么「吞吐调优」和「延迟调优」经常给出相反的参数建议。](assets/vllm/figure-04.svg)

图 4. Chunked prefill 的双向效应。这张图解释了为什么「吞吐调优」和「延迟调优」经常给出相反的参数建议。

| 参数 | 默认 | 要点 |
| --- | --- | --- |
| `--enable-prefix-caching` | V1 默认开启 | 基于块哈希复用前缀 KV。**只缓存完整块**，所以 `--block-size` 越小，命中粒度越细。多轮对话、Agent 场景收益最大。 |
| `--prefix-caching-hash-algo` | sha256 | 要跨机器/跨语言复现哈希，用 `sha256_cbor`；追求速度用 `xxhash`（需装 xxhash）。 |
| `--scheduling-policy` | FCFS | 可选 `priority`，请求带整数 priority，KV 不足时抢占低优先级。适合「在线 + 离线混跑」。 |
| `--async-scheduling` | 随版本 | 让 CPU 调度与 GPU 计算重叠，减少 GPU 空转。**大模型、长序列场景收益明显**，通常与 chunked prefill 一起用。 |
| `--long-prefill-token-threshold` | — | 与 `--prefill-schedule-interval` 配合，控制 DP rank 之间长 prefill 的准入与切片。PD 分离场景常用 512 / 1024。 |

### 4.5 服务协议与解析器

2026 年的前沿模型几乎都自带推理模式与工具调用，解析器配错会导致「内容为空」或「工具调用变成纯文本」。

| 模型族 | tokenizer-mode | tool-call-parser | reasoning-parser |
| --- | --- | --- | --- |
| DeepSeek-V4 / V4.1 | `deepseek_v4` | `deepseek_v4` | `deepseek_v4` |
| Qwen3.5 / 3.8 | `auto` | `qwen3_coder` | `qwen3` |
| GLM-5 / 5.3 | `glm5_next` | `glm47` / `glm47_moe` | `glm47` / `glm45` |
| Kimi-K3 | `kimi_k3` | `kimi_k3` | `kimi_k3` |
| MiniMax-M3 | `auto` | `minimax_m3` | `minimax_m3` |
| gpt-oss | `auto` | `openai` | （内置 harmony） |

```
vllm serve zai-org/GLM-5-FP8 \
  --served-model-name glm-5 \
  --tokenizer-mode glm5_next \
  --enable-auto-tool-choice \
  --tool-call-parser glm47 \
  --reasoning-parser glm47 \
  --chat-template-content-format=string
```

### 4.6 投机解码

在低到中等 QPS、显存受限的场景下，投机解码是性价比最高的延迟优化。方法选型见下表。

| 方法 | 低 QPS（延迟） | 高 QPS（吞吐） | 适用前提 |
| --- | --- | --- | --- |
| **MTP** | 高收益 | 中高收益 | 目标模型自带 MTP 头（DeepSeek/GLM/Qwen3.5 都有） |
| **DSpark** | 高收益 | 中高收益 | DeepSeek-V4 / Mistral-Large-4 的融合 checkpoint |
| **EAGLE / EAGLE3** | 高收益 | 中高收益 | 需要匹配的 draft 模型 |
| **Draft Model** | 高收益 | 中收益 | 需单独的小模型，显存换延迟 |
| **N-gram** | 低–中 | 中收益 | 零额外模型，代码/摘要类重复文本收益高 |
| **Suffix** | 低–中 | 中收益 | 动态推测深度，无需 draft 模型 |

```
# MTP：使用 checkpoint 内置的多 token 预测头
--speculative-config '{"method":"mtp","num_speculative_tokens":3}'

# DSpark：DeepSeek-V4-0813 融合 checkpoint，K 为草稿长度
--speculative-config '{"method":"dspark","num_speculative_tokens":6,"draft_sample_method":"probabilistic","rejection_sample_method":"block"}'

# EAGLE3：需要显式指定 draft 模型
--speculative-config '{"method":"eagle3","model":"/models/eagle3-draft","num_speculative_tokens":3}'

# n-gram：无额外模型，适合重复性强的文本
--speculative-config '{"method":"ngram","num_speculative_tokens":5,"prompt_lookup_max":4}'
```

> **投机解码的三个陷阱**
> 
> **①** 高并发下投机 token 会占用 KV，反而降低吞吐——K 值不是越大越好。**②** 改了 K 或 `max-num-seqs`，CUDA Graph 的捕获尺寸要跟着按 `n×(K+1)` 重算，否则会 OOM 或回退 eager。**③** 用 `draft_sample_method=probabilistic` 时，采样参数会显著影响接受率，压测结果不能直接外推到生产。

## 05 并行策略选型

### 本引擎的推理拓扑与 KV 存放

为便于阅读，下图使用小规模设备组解释机制，不代表旗舰模型能装入这些设备。

> MoE 的专家组可跨 TP×DP。DP4/TP1 不意味着四份独立完整模型；共享专家策略依赖模型与后端。MLA 不套用 GQA 的 KV head 分片结论。

![vLLM · A · Attention TP4 + routed TP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。](assets/vllm/figure-05.svg)

vLLM · A · Attention TP4 + routed TP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。

![vLLM · B · Attention TP4 + routed EP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。](assets/vllm/figure-06.svg)

vLLM · B · Attention TP4 + routed EP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。

![vLLM · C · attention-DP4 + routed TP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。](assets/vllm/figure-07.svg)

vLLM · C · attention-DP4 + routed TP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。

![vLLM · D · attention-DP4 + routed EP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。](assets/vllm/figure-08.svg)

vLLM · D · attention-DP4 + routed EP4 · KV 已明确按 GQA 示例标注：TP4 每卡 2 个 KV head；attention-DP4 每卡保存自己请求的 KV。共享专家布局是图示前提，不是该引擎所有后端的默认承诺。

![vLLM · MLA KV：同样四设备，复制与分片对照 · 上下是两种独立配置，每种都将设备0–3横向排列；颜色表示同一请求的历史 token 子集。上图每设备都存 A/B/C/D，下图每设备只存其中一份；空槽表示该设备不保存这份数据。在图示的复制 MLA KV 路径下，可分片部分的物理总量从 4M 降到 M，代价是额外的 query / 局部输出与 LSE 通信。不代表整个服务显存下降四倍，也不将此结论套用到已按 KV head 分片的 GQA。](assets/vllm/figure-09.svg)

vLLM · MLA KV：同样四设备，复制与分片对照 · 上下是两种独立配置，每种都将设备0–3横向排列；颜色表示同一请求的历史 token 子集。上图每设备都存 A/B/C/D，下图每设备只存其中一份；空槽表示该设备不保存这份数据。在图示的复制 MLA KV 路径下，可分片部分的物理总量从 4M 降到 M，代价是额外的 query / 局部输出与 LSE 通信。不代表整个服务显存下降四倍，也不将此结论套用到已按 KV head 分片的 GQA。

官方依据：[vLLM 并行与配置文档](https://docs.vllm.ai/en/latest/serving/data_parallel_deployment/)。图示策略存在不代表某个模型、精度、硬件组合已实测。

MoE 时代，选错拓扑比选错卡更伤。

### 四种基本切法

### 选型决策

![图 6. 并行选型决策。注意第一层的判断对象是 权重 而非参数量——同样 397B，FP8 只要 4 卡 B300，BF16 就要 8 卡 H200。](assets/vllm/figure-10.svg)

图 6. 并行选型决策。注意第一层的判断对象是 权重 而非参数量——同样 397B，FP8 只要 4 卡 B300，BF16 就要 8 卡 H200。

### EP 的两个反直觉细节

1. **EP 规模不是你设的，是算出来的。**`EP_SIZE = TP × DP`。想开 8 路 EP 但只想用 4 张卡，做不到——必须 `-dp 8 -tp 1` 或 `-dp 4 -tp 2`。
2. **TP=1 时注意力层不通信。**开启 EP 后，若 TP=1，注意力权重在每个 DP rank 上完整复制，各 rank 独立处理自己的请求，完全不需要 all-reduce。这正是 DEP（DP + EP）成为 MoE 默认拓扑的原因：把唯一强制的全局通信（注意力 all-reduce）消掉了。

```
# A. 纯 TP8 —— 所有层都切，每层两次 all-reduce
vllm serve deepseek-ai/DeepSeek-V4-Pro \
  --tensor-parallel-size 8

# B. DP8 + EP —— 注意力复制、专家分片，只有 MoE 层 all-to-all
vllm serve deepseek-ai/DeepSeek-V4-Pro \
  --data-parallel-size 8 \
  --enable-expert-parallel \
  --all2all-backend deepep_low_latency
```

> **官方实测结论**
> 
> Kimi-K3 在单台 B300 上，**TP8 + EP**（注意力 TP8、专家走 MegaMoE 的 EP）相比纯 TP8，34 万 token 的单条 prompt 预填充从约 15 秒降到约 10 秒。而 **DCP8**（decode 上下文并行）能把 KV 容量从 260 万 token 提到 1850 万 token，代价是 decode 步骤慢 7–25%——这是「容量换速度」的典型权衡。

## 06 逐模型实战配置

所有命令均来自官方 Recipes 验证过的配置，只做注释与必要的路径替换。

### 6.1 入门基线：gpt-oss-120b

最适合用来验证环境。117B 总参 / 5.1B 激活，原生 MXFP4，单张 80GB 卡即可跑，96 GB 显存起步。

```bash
# 单卡（A100 80G / H100 / H200 / B200 均可）
vllm serve openai/gpt-oss-120b

# TP4（多卡分摊，降低单卡显存压力）
vllm serve openai/gpt-oss-120b --tensor-parallel-size 4

# 启用工具调用（OpenAI harmony 格式）
vllm serve openai/gpt-oss-120b \
  --tool-call-parser openai \
  --enable-auto-tool-choice

# Blackwell 上 FlashInfer TRTLLM MXFP4 MoE 会自动选中；
# 如需显式固定：
vllm serve openai/gpt-oss-120b \
  --moe-backend flashinfer_trtllm \
  --quantization-config.moe.activation mxfp8
```

**验证：**

```python
# Official OpenAI SDK works unchanged against vLLM
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
r = client.chat.completions.create(
    model="openai/gpt-oss-120b",
    messages=[
        {"role": "system", "content": "Reasoning: high"},
        {"role": "user", "content": "Explain why eigenvalues matter."},
    ],
)
print(r.choices[0].message.content)
```

### 6.2 主力中坚：Qwen3.5-397B-A17B

397B 总参 / 17B 激活，GDN + 每 4 层全注意力，原生多模态。官方推荐 FP8 checkpoint。

| 精度 | 权重显存 | 最少卡数 | 说明 |
| --- | --- | --- | --- |
| BF16 | ~800 GB | H200 × 8 / B300 × 4 | H100 80G 需 TP16（2 节点） |
| FP8 | ~400 GB | H200 × 4 / B300 × 2 | 官方推荐，2 卡 B300 即可 |
| NVFP4 | ~238 GB | GB200 × 4 | Blackwell 专用 |
| MXFP4 (AMD) | ~239 GB | MI350X/MI355X × 1 | 单卡 288 GB 可装 |

```bash
# 8×H200：DP8 + EP，跳过视觉编码器，把显存全给 KV
vllm serve Qwen/Qwen3.5-397B-A17B-FP8 \
  -dp 8 \
  --enable-expert-parallel \
  --language-model-only \
  --reasoning-parser qwen3 \
  --enable-prefix-caching
```

```bash
vllm serve Qwen/Qwen3.5-397B-A17B-FP8 \
  -dp 8 \
  --enable-expert-parallel \
  --mm-encoder-tp-mode data \
  --mm-processor-cache-type shm \
  --reasoning-parser qwen3 \
  --enable-prefix-caching

# 加工具调用
# --enable-auto-tool-choice --tool-call-parser qwen3_coder
```

```bash
# 注意：MTP 会占用 KV，高并发下反而降吞吐
vllm serve Qwen/Qwen3.5-397B-A17B-FP8 \
  --tensor-parallel-size 8 \
  --speculative-config '{"method":"mtp","num_speculative_tokens":1}' \
  --reasoning-parser qwen3
```

> **Qwen3.5 的两个已知坑**
> 
> **①** 单卡 MXFP4 场景下，默认 `max_num_seqs=1024` 会超过单卡留下的约 296 个 Mamba cache block，服务直接拒绝启动——必须显式 `--max-num-seqs 64`。**②** CUDA graph 捕获尺寸大于 Mamba cache 时会报 `assert num_cache_lines >= batch`，把 `--max-cudagraph-capture-size` 调小（默认 512）。

### 6.3 旗舰：DeepSeek-V4-Pro

1.6T 总参 / 49B 激活，权重约 960 GB（FP4+FP8 混合）。**单机 8 卡装不下 GB200 一个 tray**，需要 2 个 tray。

| Checkpoint | 草稿模块 | 备注 |
| --- | --- | --- |
| `DeepSeek-V4-Pro-0813`（默认） | DSpark | 官方正式版，Agent 能力显著强于预览版 |
| `DeepSeek-V4-Pro` | MTP | 预览版 FP4+FP8 混合权重 |
| `nvidia/DeepSeek-V4-Pro-NVFP4` | MTP | modelopt 重量化，Blackwell 专用 |
| `DeepSeek-V4-Pro-DSpark` | DSpark | 预览权重 + 融合 DSpark 模块 |

```bash
# H200 上 1M 上下文放不下全部 KV，官方建议封顶 800K
vllm serve deepseek-ai/DeepSeek-V4-Pro-0813 \
  --data-parallel-size 8 \
  --enable-expert-parallel \
  --max-model-len 800000 \
  --kv-cache-dtype fp8 \
  --block-size 256 \
  --tokenizer-mode deepseek_v4 \
  --reasoning-parser deepseek_v4 \
  --tool-call-parser deepseek_v4 \
  --enable-auto-tool-choice \
  --trust-remote-code
```

```bash
# 每个 4-GPU tray 起本地 4 个 DP rank，两 tray 合成 DP8
export VLLM_USE_V2_MODEL_RUNNER=1
export VLLM_DSV4_MEGA_FP8_COMBINE=1

vllm serve /models/DeepSeek-V4-Pro-0813 \
  --data-parallel-size 8 \
  --data-parallel-size-local 4 \
  --data-parallel-start-rank $((4 * NODE_RANK)) \
  --data-parallel-rpc-port 13345 \
  --data-parallel-hybrid-lb \
  --enable-expert-parallel \
  --enable-ep-weight-filter \
  --enable-cumem-allocator \
  --moe-backend deep_gemm_amxf4_mega_moe \
  --numa-bind --numa-bind-nodes 0 0 1 1 \
  --tokenizer-mode deepseek_v4 \
  --trust-remote-code \
  --max-model-len 1048576 \
  --kv-cache-dtype fp8 \
  --block-size 256 \
  --no-disable-hybrid-kv-cache-manager \
  --disable-flashinfer-autotune \
  --attention-config '{"backend":"FLASHINFER_MLA_SPARSE_DSV4","use_prefill_query_quantization":true,"indexer_kv_dtype":"mxfp4"}' \
  --speculative-config '{"method":"dspark","num_speculative_tokens":5,"draft_sample_method":"probabilistic","rejection_sample_method":"block"}'
```

> **为什么 H200 要封顶 800K**
> 
> H200 是 141 GB × 8 = 1128 GB。960 GB 的混合精度权重在 DP 下**每个 rank 都要一份非专家权重**，KV 空间被挤到极限。官方 recipe 明确写了这条上限，并用 `--max-model-len 800000` 保留 KV 余量。H20 更紧，需要 `--max-model-len 200000 --max-num-seqs 16`。

### 6.4 超大 MoE：Kimi-K3 / K3.1-Pro

2.8T 参数，896 个路由专家每 token 激活 **16 个**（外加共享专家）。架构是 **KDA 线性注意力 + 稀疏 MLA**：93 层解码器中大部分是 Kimi Delta Attention（常数循环状态），每 4 层一次全 MLA。

| 版本 | 总参 | 激活 | 上下文 | 精度 | 最小显存 | vLLM |
| --- | --- | --- | --- | --- | --- | --- |
| **Kimi-K3** | 2.8T | 16 / 896 | 1,048,576 | MXFP4 | 多节点 | ≥0.33 |
| **Kimi-K3.1-Pro** | 2.8T | 16 / 896 | 1,048,576 | MXFP4 | 1,874 GB | ≥0.34 |

> **K3.1-Pro 只能走 day-0 镜像**
> 
> 它要求 `vLLM ≥ 0.34.0`，而当前稳定版是 0.33.0。**官方 recipe 明确写了 `install.pip: false`——没有可用的 wheel**，只能用 `vllm/vllm-openai:kimi-k31` 镜像（NVIDIA 侧是 CUDA 13 构建，AMD 侧是 ROCm 10）。

```bash
# 2 个 GB300 tray 或 1 台 B300 节点，TP8 基础布局
docker run --gpus all --ipc=host --shm-size 32g \
  -p 8000:8000 \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  vllm/vllm-openai:kimi-k31 \
  moonshotai/Kimi-K3.1-Pro \
  --tensor-parallel-size 8 \
  --trust-remote-code \
  --max-model-len 1048576 \
  --gpu-memory-utilization 0.85 \
  --enable-prefix-caching \
  --tool-call-parser kimi_k3 \
  --reasoning-parser kimi_k3 \
  --enable-auto-tool-choice
```

```bash
# Decode Context Parallelism：MLA 潜变量与索引器 KV 分片，
# KV 容量约为 TP8 的 7 倍（约 18.5M vs 2.6M token），代价是 decode 慢 7–25%
vllm serve moonshotai/Kimi-K3 \
  --tensor-parallel-size 8 \
  --decode-context-parallel-size 8 \
  --dcp-comm-backend a2a \
  --attention-backend TOKENSPEED_MLA \
  --kv-cache-dtype fp8 \
  --mamba-ssm-cache-dtype bfloat16 \
  --enable-prefix-caching --prefix-match-unit 128 \
  --max-model-len 1048576 \
  --gpu-memory-utilization 0.88 \
  --speculative-config '{"model":"Inferact/Kimi-K3-DSpark","method":"dspark","num_speculative_tokens":6,"attention_backend":"TOKENSPEED_MLA","draft_sample_method":"probabilistic"}' \
  --tool-call-parser kimi_k3 --reasoning-parser kimi_k3 \
  --language-model-only
```

| 布局 | 适用 | 关键参数 | 代价 |
| --- | --- | --- | --- |
| TP8 | 基线，最简单 | `--tensor-parallel-size 8` | KV 容量最小 |
| TP8 + EP | 长 prompt 的 TTFT | MegaMoE 内核 | 34 万 token 预填充 15s → 10s |
| TP8 + DCP8 | 超长上下文、多会话 | `--decode-context-parallel-size 8` | decode 慢 7–25% |
| TP8×PP2 + DCP8 | 4 tray 全量部署 | `--pipeline-parallel-size 2` | 流水气泡 |

> **KDA 状态池不在分页 KV 里**
> 
> 每个 KDA 层为每条序列保留**常数大小**的循环状态。这部分显存独立于 KV 池，会随并发数线性增长。所以 Kimi-K3 的 `--max-num-seqs` 要留余量，`--gpu-memory-utilization` 不宜压到 0.95。

### 6.5 Qwen3.8-Flash-Next

Qwen4 架构预览：**125B 主模型 + 51B N-gram embedding**，每 token 激活 6B。GDN + GQA 交替，原生多模态，262K 上下文。

| 精度 | checkpoint | 最小显存 | 推荐布局 |
| --- | --- | --- | --- |
| NVFP4 | `Inferact/Qwen3.8-Flash-Next-NVFP4` | 130 GB | 单 GB200 / GB300 |
| FP8 | `Qwen/Qwen3.8-Flash-Next-FP8` | 250 GB | TEP4（B300）/ TEP8（H200） |
| MXFP4 | `amd/Qwen3.8-Flash-Next-Quark-MXFP4` | 219 GB | MI355X |
| BF16 | `Qwen/Qwen3.8-Flash-Next` | 423 GB | 8×H200 |

```bash
vllm serve Qwen/Qwen3.8-Flash-Next-FP8 \
  --tensor-parallel-size 4 \
  --enable-expert-parallel \
  --gpu-memory-utilization 0.90 \
  --max-num-seqs 256 \
  --enable-prefix-caching \
  --kv-cache-dtype fp8 \
  --attention-config.indexer_kv_dtype fp8 \
  --speculative-config '{"method":"mtp","num_speculative_tokens":3}' \
  --enable-auto-tool-choice \
  --tool-call-parser qwen3_coder \
  --reasoning-parser qwen3
```

```bash
# Hopper 上必须用 Triton MoE 后端
vllm serve Qwen/Qwen3.8-Flash-Next-FP8 \
  --tensor-parallel-size 8 \
  --enable-expert-parallel \
  --moe-backend triton \
  --gpu-memory-utilization 0.85 \
  --max-num-seqs 256 \
  --enable-prefix-caching \
  --kv-cache-dtype fp8 \
  --attention-config.indexer_kv_dtype fp8 \
  --speculative-config '{"method":"mtp","num_speculative_tokens":3}' \
  --enable-auto-tool-choice \
  --tool-call-parser qwen3_coder \
  --reasoning-parser qwen3
```

> **三个必须记住的约束**
> 
> **① 纯 TP8 与 FP8 checkpoint 不兼容**——FP8 的 128 宽量化块无法被普通 TP 切分，必须开 `--enable-expert-parallel`。**② `--max-num-seqs 256` 不要降**——低于这个值会在启动时触发 Mamba cache 容量错误。**③ 压测 MTP 要用 SPEED-Bench**（`--dataset-name speed_bench`），随机数据集测不出真实接受率。

### 6.6 GLM-5 / 5.3

744B 总参 / 40B 激活，MLA + DSA。FP8 权重 893 GB，8×H200 单机可跑。

```bash
vllm serve zai-org/GLM-5-FP8 \
  --tensor-parallel-size 8 \
  --speculative-config.method mtp \
  --speculative-config.num_speculative_tokens 3 \
  --tool-call-parser glm47 \
  --reasoning-parser glm47 \
  --enable-auto-tool-choice \
  --chat-template-content-format=string \
  --served-model-name glm-5-fp8
```

> **GLM-5.3 的推理控制变了**
> 
> GLM-5.2 及之前用 `chat_template_kwargs: {"enable_thinking": false}` 关闭思考。GLM-5.3 **无法关闭思考**，改为 `reasoning_effort`（`low`/`high`/`max`，默认 max），另有 `clear_thinking` 控制多轮是否保留历史推理。客户端需要相应改造。

### 6.7 其余三个快速配置

| 模型 | 最小显存 | 关键启动参数 |
| --- | --- | --- |
| **MiniMax-M3**<br>427B / 26B，MSA 稀疏，原生多模态 | 1025 / 513 / 257 | `--trust-remote-code --reasoning-parser minimax_m3 --tool-call-parser minimax_m3`<br>NVFP4 变体需 Blackwell；官方镜像 `vllm/vllm-openai:minimax-m3` |
| **DeepSeek-V4.1-Flash**<br>552B，8B/16B 激活，视觉语言 | 614 | `--tokenizer-mode deepseek_v41 --reasoning-parser deepseek_v41 --tool-call-parser deepseek_v41`<br>最省事的旗舰级选择：单机 8×H200 即可，显存仅为 Pro 的 64% |
| **ERNIE-5.5-Flash**<br>280B / 14B，HCA + MLA-DSA | 347 / 688 | `uv pip install vllm==0.32.0` + `uv pip install git+https://github.com/baidu/vLLM-ERNIE.git`<br>插件锁定 0.32.0，不能升版本；仅 NVIDIA |

### 6.8 接入 Claude Code（本地模型跑 Agent）

vLLM 实现了 Anthropic Messages API，可以直接把 Claude Code 指向本地服务。要求模型有强工具调用能力。

```bash
# 1) 起一个带工具调用的服务
vllm serve openai/gpt-oss-120b \
  --served-model-name my-model \
  --enable-auto-tool-choice \
  --tool-call-parser openai

# 2) 指向它启动 Claude Code
ANTHROPIC_BASE_URL=http://localhost:8000 \
ANTHROPIC_API_KEY=dummy \
ANTHROPIC_AUTH_TOKEN=dummy \
ANTHROPIC_DEFAULT_OPUS_MODEL=my-model \
ANTHROPIC_DEFAULT_SONNET_MODEL=my-model \
ANTHROPIC_DEFAULT_HAIKU_MODEL=my-model \
claude
```

> **三个必踩的坑**
> 
> **①** `--served-model-name` 不能含 `/`，否则 Claude Code 无法识别。**②** Claude Code 会在 system prompt 里注入 per-request hash，**直接摧毁前缀缓存**——vLLM >0.17.1 已自动处理，旧版本需设 `CLAUDE_CODE_ATTRIBUTION_HEADER=0`。**③** WebSearch 依赖 Anthropic 服务端工具，vLLM 会返回 400，需改用 MCP 的搜索服务。

## 07 调优方法论

先测量，再调参。没有基线的调优就是赌博。

### 先定义四个指标

| 指标 | 定义 | 主要由什么决定 |
| --- | --- | --- |
| **TTFT** | 请求到首 token 的时间 | 排队时长 + prefill 计算 + 首 token 采样。长 prompt 场景几乎全由 prefill 决定。 |
| **ITL / TPOT** | 相邻 token 间隔 | decode 单步耗时。**几乎只受 batch 内并发数和 KV 带宽影响。** |
| **Throughput** | 每秒总 token（输入+输出） | GPU 利用率 × batch 效率。 |
| **Goodput** | 满足 SLO 的有效吞吐 | 真正的业务指标；前三个都是它的分解。 |

> **官方建议**
> 
> vLLM 官方明确说：`vllm bench serve` 主要用于功能验证和回归测试；**生产压测推荐 GuideLLM**，它在数据集加载、请求格式、负载模式上更灵活，且能自动生成报告。

### 三个旋钮的相互作用

调优不是单参数扫描，而是三维空间里找 Pareto 前沿。下表是方向性结论（不是绝对值）：

| 旋钮 | ↑ 调大 | ↓ 调小 | 副作用 |
| --- | --- | --- | --- |
| `max-num-batched-tokens` | TTFT ↓，吞吐 ↑ | ITL ↓，延迟更稳 | 过大时 decode 被长 prefill 拖住，尾延迟恶化 |
| `max-num-seqs` | 吞吐 ↑，GPU 利用率 ↑ | ITL ↓，单请求更快 | 过大时 KV 紧张，触发抢占重算 |
| `gpu-memory-utilization` | KV 容量 ↑，并发 ↑ | 更安全，留给激活更多 | 过高时长 prompt 直接 OOM |

### 按场景的配方

| 场景 | 关键配置 | 理由 |
| --- | --- | --- |
| **低延迟对话**<br>单用户、短输出 | `--max-num-batched-tokens 2048`<br>`--max-num-seqs 8`<br>`--speculative-config {"method":"mtp","num_speculative_tokens":1}` | 小预算让 decode 不被打断；小 batch 降低 ITL；MTP-1 接受率最高 |
| **高吞吐批处理**<br>离线、延迟不敏感 | `--max-num-batched-tokens 16384`<br>`--max-num-seqs 512`<br>`--gpu-memory-utilization 0.95` | 大预算吃满 prefill；大 batch 摊薄权重读取；不浪费一点显存 |
| **长上下文 / RAG** | `--max-model-len 1000000`<br>`--kv-cache-dtype fp8`<br>`--enable-prefix-caching`<br>`--block-size 64` | KV 减半；前缀复用避免重复 prefill；小块提升命中粒度 |
| **多轮 Agent** | `--enable-prefix-caching`<br>`-dcp 8`（长会话）<br>KV 卸载到 host DRAM | 每轮只新增少量 token，前缀命中率决定成本；DCP 提升会话容量 |
| **在线混合负载** | `--scheduling-policy priority`<br>`--async-scheduling` | 让交互请求优先；异步调度填补 GPU 空转 |

### 启动时间优化

大模型冷启动可能长达 10–20 分钟。三个手段可以叠加：

1. **复用编译缓存。**torch.compile 产物存在 `VLLM_CACHE_ROOT`（默认 `~/.cache/vllm`），可以打包进镜像或跨机复制。设 `VLLM_FORCE_AOT_LOAD=1` 让缓存未命中时直接报错，而不是静默重编。
2. **跳过显存测量。**把上次日志里的 `--kv-cache-memory` 值传回去。
3. **预编译内核。**`vllm download-kernels` 把 Hopper 及更新架构上启动时才编译的内核提前装好，省几分钟。

| 优化等级 | 行为 | 用途 |
| --- | --- | --- |
| `-O0` | 无编译、无 graph、无 fusion | 调试、快速迭代 |
| `-O1` | 简单编译 + 快速 fusion + PIECEWISE graph | 开发环境平衡点 |
| `-O2`（默认） | 额外编译范围 + 更多 fusion + FULL_AND_PIECEWISE | **生产环境** |
| `-O3` | 当前等同 -O2 | 预留 |

### 压测命令

```bash
vllm bench serve \
  --model zai-org/GLM-5-FP8 \
  --dataset-name random \
  --random-input 8000 \
  --random-output 1024 \
  --request-rate 10 \
  --num-prompts 32 \
  --ignore-eos
```

> **关于 MTP 压测的失真**
> 
> 官方提示：纯压测场景下 MTP 的接受率往往偏低，**测出的吞吐会低估真实表现**。因为压测用随机 prompt，而线上是结构化的真实文本。评估投机解码收益时，最好用生产流量回放。

## 08 生产化与排错

把服务跑起来只是开始，能定位问题才算部署完成。

### 8.1 容器化

```bash
docker run --runtime nvidia --gpus all \
  -v ~/.cache/huggingface:/root/.cache/huggingface \
  -e HF_TOKEN=$HF_TOKEN \
  -p 8000:8000 \
  --ipc=host \
  vllm/vllm-openai:latest \
  --model Qwen/Qwen3-0.6B

# 老驱动（数据中心卡）可开启 CUDA 兼容层
-e VLLM_ENABLE_CUDA_COMPATIBILITY=1
```

- `--ipc=host` 或 `--shm-size` 必须给：vLLM 用共享内存做 TP 进程间通信。
- 可选依赖（如 audio）不在镜像里，需要基于官方镜像加一层 `uv pip install --system "vllm[audio]==<同版本>"`。
- 镜像 tag 直接写版本号，不要用 `latest` 上生产。

### 8.2 监控指标

vLLM 在 `/metrics` 暴露 Prometheus 格式指标。以下是值得设告警的几个：

| 指标 | 类型 | 怎么看 |
| --- | --- | --- |
| `vllm:kv_cache_usage_perc` | Gauge | 1.0 表示打满。**长期 >0.95 说明该扩卡或降上下文** |
| `vllm:num_requests_waiting` | Gauge | 持续增长 = 排队堆积，TTFT 会崩 |
| `vllm:num_preemptions` | Counter | **抢占计数是最直接的显存不足信号**，非零就该介入 |
| `vllm:prefix_cache_hits / queries` | Counter | 比值即命中率。多轮场景低于 30% 说明缓存没生效 |
| `vllm:time_to_first_token_seconds` | Histogram | 看 p95/p99，不要只看均值 |
| `vllm:inter_token_latency_seconds` | Histogram | ITL 抖动通常来自 batch 波动或抢占 |
| `vllm:prompt_tokens_cached_by_source` | Counter | 按 device/host/disk/p2p 分层，定位缓存到底命在哪一层 |

```bash
curl -s http://localhost:8000/metrics | grep -E 'kv_cache_usage|num_requests_waiting|num_preemptions|prefix_cache'
```

### 8.3 故障排查对照表

| 现象 | 最可能原因 | 处理 |
| --- | --- | --- |
| 启动 OOM | 权重 + 激活超出 `gpu-memory-utilization` | 降 `--max-model-len` / `--max-num-seqs`；换低精度 checkpoint；开 `--kv-cache-dtype fp8` |
| 运行中频繁 `preempted` 警告 | KV 空间不足，请求被抢占重算 | 提高 `--gpu-memory-utilization`；降 `--max-num-seqs` 或 `--max-num-batched-tokens`；加 TP/PP |
| GPU 利用率上不去 | CPU 核数不足，Engine Core 被抢占 | 按 `2+N` 分配物理核；检查是否有其他进程抢 CPU |
| 首 token 很慢，后续正常 | 长 prefill 由于预算太小被切成很多块 | 提高 `--max-num-batched-tokens`；考虑 PD 分离 |
| 工具调用不生效 | 缺 `--enable-auto-tool-choice` 或 parser 不匹配 | 按 4.5 表格选 parser；确认 `--tokenizer-mode` 正确 |
| 推理内容为空 / 混在 content 里 | reasoning parser 未配置或选错 | 补 `--reasoning-parser`；GLM-5.3 需改用 `reasoning_effort` |
| 生成质量突然变差 | 0.8.0 起默认采样参数改为模型自带的 `generation_config.json` | 试 `--generation-config vllm` 对比；若是则建议模型方修正 config |
| 下载/加载卡住 | 网络或共享文件系统慢 | 先 `hf download` 到本地；用 `--load-format dummy` 跳过权重验证瓶颈位置 |
| 多机启动超时 | 大模型权重加载超过默认 600 秒 | 设 `VLLM_ENGINE_READY_TIMEOUT_S=3600` |

### 8.4 安全要点

- **默认无鉴权。**对外暴露前必须加 `--api-key` 或放在网关后面。
- **多模态 URL 是 SSRF 入口。**用 `--allowed-media-domains` 限制域名，用 `--allowed-local-media-path` 限定可读目录（默认关闭）。
- **`--trust-remote-code` 会执行模型仓库里的 Python。**只对可信来源开启，必要时先审计。
- **多租户场景**用 `--prefix-caching-hash-algo sha256_cbor` 并配合 cache salt，避免跨租户前缀泄露。

## 09 附录：速查与来源

### A. 参数速查

| 参数 | 一句话 |
| --- | --- |
| `--max-model-len` | prompt+output 总长度上限，解决 OOM 的第一手段 |
| `--gpu-memory-utilization` | 本实例允许占用的显存比例，反推 KV 容量 |
| `--kv-cache-memory-bytes` | 直接钉死 KV 字节数，覆盖上一条 |
| `--max-num-batched-tokens` | 单步 token 预算，TTFT 与 ITL 的总开关 |
| `--max-num-seqs` | 单步最大序列数，决定 CUDA Graph 规模 |
| `--kv-cache-dtype` | KV 精度，fp8 通常必开 |
| `--block-size` | KV 块大小，影响前缀命中粒度 |
| `--enable-expert-parallel` | MoE 走 EP，规模 = TP × DP |
| `-dp / -tp / -pp / -dcp` | 数据 / 张量 / 流水 / 解码上下文并行 |
| `--all2all-backend` | EP 通信后端，prefill/decode 各有最优解 |
| `--speculative-config` | 投机解码，JSON 形式 |
| `--tokenizer-mode` | 新模型的渲染器，配错则工具调用全废 |
| `-O0…-O3` | 优化等级，生产用默认 -O2 |

### B. 常用环境变量

| 变量 | 作用 |
| --- | --- |
| `VLLM_USE_MODELSCOPE` | 从 ModelScope 而非 HF 拉模型 |
| `VLLM_CACHE_ROOT` | 编译缓存目录，可打包进镜像 |
| `VLLM_FORCE_AOT_LOAD` | 缓存未命中时直接报错，避免静默重编 |
| `VLLM_ENGINE_READY_TIMEOUT_S` | 大模型加载等待上限，默认 600 秒 |
| `VLLM_ALLOW_LONG_MAX_MODEL_LEN` | 允许超过 config 里的最大长度（需配合 `--max-model-len`） |
| `VLLM_USE_FASTOKENS` | 启用 Rust 分词后端，长前缀场景更快 |
| `VLLM_ROCM_USE_AITER` | AMD 上启用 AITER 内核，几乎必开 |
| `VLLM_WORKER_MULTIPROC_METHOD` | fork / spawn，影响 NUMA 自动绑定是否生效 |
| `VLLM_HOST_IP` | 多机场景下每节点必须不同 |

### C. 来源

| 来源 | 用途 |
| --- | --- |
| docs.vllm.ai/en/latest | 参数释义、优化指南、显存节省、并行部署、监控指标（页面更新 2026-10-30） |
| recipes.vllm.ai | 逐模型 YAML：最小显存、验证硬件、启动命令（更新至 2026-11-05） |
| pypi.org/project/vllm | 版本号与发布日期核验 |
| 各模型官方模型卡 / config.json | 参数规模、层数、注意力超参、上下文长度 |

> **口径说明**
> 
> 本文所有「最小显存」取自 recipes YAML 的 `vram_minimum_gb`，其定义为「checkpoint 体积 × 1.2 余量」，用于判断**能不能加载**，不是推荐生产配置。生产环境应在该值之上留出 KV 与激活的余量。

