大模型应用接入生产环境后最常见的问题就是 AI 响应太慢。服务端每生成一个 token 都要维护一份 KV Cache传统推理引擎不仅显存碎片严重而且批处理粒度粗GPU 经常在等待最慢的一条请求。vLLM 之所以被大量项目采用核心是引入了 PagedAttention 和连续批处理Continuous Batching两套机制前者让 KV Cache 的显存使用接近零碎片后者把调度单位从“请求级别”细化到“迭代级别”。这篇文章会从这两个原理开始以 Qwen3-8B 为例完成 vLLM 的环境准备、服务部署、Python 客户端调用、性能参数调优和问题排查适合已经把模型跑通、希望解决部署性能的 Python 开发者也适合正在选型推理框架的后端工程师。1. 先理解 vLLM 为什么比原生推理快得多1.1 传统推理慢在哪里显存碎片、静态批处理与长请求阻塞大模型生成文本是一个“自回归”过程模型每生成一个 token都要把之前所有 token 的 KV Cache 保存在显存里用于后续计算。KV Cache 的大小和序列长度、层数、头数、精度直接相关。假设一个 8B 模型使用 BF16 精度序列长度为 4096KV Cache 可能占用数 GB 显存。问题是传统 Hugging Face Transformers 推理在请求到达时通常会为一条请求预留一大块连续显存。显存不够时只能等待显存有碎块时又无法复用。另一个低效点在批处理。早期推理引擎普遍使用“静态批处理”一批请求全部生成完毕才把这一批释放然后接收下一批。如果批内一条请求很短、另一条很长短请求即使已经结束也不会立即释放显存只能占着位置等长请求跑完。这会让 GPU 出现大量空转和浪费。此外模型推理分为 Prefill处理输入和 Decode逐个生成 token两个阶段。传统实现常常把一条长输入的 Prefill 一次性算完期间其他请求只能排队。对于聊天、客服这类在线服务用户体感就是“首字迟迟不出来”也就是首字延迟偏高。1.2 PagedAttention像操作系统管理内存一样管理 KV CachePagedAttention 是 vLLM 最核心的改动。它的思想来源是操作系统中的虚拟内存分页将逻辑上连续的 KV Cache 切成固定大小的块block每个块可以存放在物理显存的不同位置再通过一张 block table 完成逻辑地址到物理地址的映射。这样做带来的好处非常直接不再要求整段连续显存显存碎片问题被大幅缓解。每次请求按需分配新的块序列结束时能立刻释放不再需要的块。多个请求可以共享同一个 KV 块比如并行采样或 Beam Search 场景下共享前缀可以显著减少显存占用。调度器可以像操作系统换页一样在显存不足时对 block 做预占或抢占而不是直接报 OOM。在工程上看PagedAttention 让同一张 GPU 上能同时运行的请求数量明显上升。请求数量上升batch 变大GPU 的吞吐自然跟着提高。这也是为什么同样的模型用 vLLM 部署后的吞吐会比原生 Transformers 高出好几倍。1.3 Continuous Batching把调度粒度从请求级别缩小到迭代级别连续批处理并不是一个需要用户手动“打开”的开关而是 vLLM 默认使用的调度策略。传统静态批处理以“整条请求”为调度单位而连续批处理以“一次前向迭代”为调度单位。每一轮前向计算前调度器都会重新检查有哪些请求处于等待状态有哪些请求已经生成完毕可以释放有哪些请求还在生成过程中下一轮继续参与计算显存和 batch token 限额是否允许插入新请求。这样短请求结束后立刻让出位置新请求可以马上进入 batchGPU 的利用率会被持续填满。对于线上真实负载来说请求长度参差不齐、到达时间随机连续批处理的效果会比静态批处理好很多。对比维度原生 Transformers 批量推理vLLM 连续批处理显存管理为请求预留连续空间碎片多按 block 分配碎片少调度单位整条请求完成每次前向迭代短请求结束等待整批释放立刻释放并插入新请求长输入 Prefill一次性阻塞可配合 Chunked Prefill 切分高并发吞吐通常偏低显著提升适用场景离线小批量在线服务、高并发 API一句话总结PagedAttention 解决的是“显存放不下、放不整齐”的问题连续批处理解决的是“GPU 空转、batch 空等”的问题。两者叠加才是 vLLM 高吞吐的根源。2. 环境准备安装 vLLM 并规划 GPU 部署方式2.1 检查 Python、CUDA、显卡驱动与显存需求部署 vLLM 之前先确认机器的 GPU 环境。常见的 7B~8B 参数模型BF16 权重约占 16GB 显存加上 KV Cache 和运行时开销单张 24GB 显存显卡可以较轻松运行推理速度也比较理想。如果只用 INT4/INT8 量化权重显存要求会降到 8GB 到 12GB 左右但速度可能受量化算子影响。环境检查建议按以下顺序执行python3 --version nvidia-smi python3 -c import torch; print(torch.__version__, torch.cuda.is_available())安装 vLLM 前要看两个匹配关系vLLM 对 Python 版本有要求常见版本支持 Python 3.9 到 3.12推荐使用 3.10 或 3.11。vLLM 依赖 CUDA 算子编译如果使用 pip 安装需要本机 CUDA 版本与预编译 wheel 匹配。最容易踩的坑是 Python 版本太高或太低导致找不到对应 wheel。如果只是学习验证优先使用一台显存足够的单卡 Linux 机器例如具备 24GB 显存的显卡。生产环境还需额外考虑供电、散热、磁盘 IO 和镜像仓库网络。2.2 用 Python 虚拟环境安装 vLLM学习环境建议使用 Python 虚拟环境避免污染系统环境。安装命令如下python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip pip install vllm安装完成后先执行一条简单的版本检查python3 -c import vllm; print(vllm.__version__)如果能够正常输出版本号说明基础安装成功。实际部署时建议查看 vLLM 官方文档中的版本兼容表确认 Python、CUDA、PyTorch 和 vLLM 的版本组合。不要直接在系统 Python 里装最新版否则后面出现算子编译错误时会很难排查。如果你的网络环境不理想可以先把下载源改成国内 PyPI 镜像例如pip install vllm -i https://pypi.tuna.tsinghua.edu.cn/simple。这一步属于常规镜像配置不影响后续命令。2.3 生产环境推荐使用 Docker Compose 部署生产环境建议使用官方 Docker 镜像部署。vLLM 官方镜像通常包含完整的 CUDA、PyTorch 和已编译算子比自己 pip 装更可控。使用 Docker Compose 可以固定镜像版本、管理端口、GPU 资源和模型目录挂载。一个最小可用的docker-compose.yml示例如下services: vllm: image: vllm/vllm-openai:latest container_name: vllm-qwen restart: unless-stopped shm_size: 16gb ports: - 8000:8000 volumes: - /data/models:/models environment: - HF_TOKEN${HF_TOKEN} command: - --model - /models/Qwen3-8B - --served-model-name - qwen3-8b - --host - 0.0.0.0 - --port - 8000 - --gpu-memory-utilization - 0.9 - --max-model-len - 8192 deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu]这里最值得注意的参数是shm_size。vLLM 在容器里会用到/dev/shm共享内存默认 64MB 会导致共享内存不足典型现象是模型加载时报shared memory相关错误或者服务运行一段时间后不稳定。生产环境建议设置为16gb或更大具体大小与并发请求数、模型的张量并行有关。启动命令用docker compose up -d docker compose logs -f vllm日志中出现模型加载成功和监听端口信息后再进入接口验证阶段。3. 用 vLLM 启动 Qwen3-8B 的 OpenAI 兼容服务3.1 准备模型权重目录vLLM 可以直接加载 Hugging Face 格式的模型目录。你需要先把 Qwen3-8B 权重下载到磁盘。下载方式不限可以是模型仓库下载也可以是已有镜像转移。下面是一条常见下载命令示例使用模型仓库工具将模型放到本机目录modelscope download --model Qwen/Qwen3-8B --local_dir /data/models/Qwen3-8B模型目录下至少应该包含config.json、tokenizer.json、权重文件等。如果没有config.jsonvLLM 无法判断模型架构启动时会直接报模型找不到或模型不受支持。这里要注意模型名不要重复嵌套例如/data/models/Qwen3-8B/Qwen3-8B这种路径很容易在配置命令时写错。3.2 启动服务的最小命令与参数说明在 Python 虚拟环境下最小启动命令是vllm serve /data/models/Qwen3-8B \ --served-model-name qwen3-8b \ --host 0.0.0.0 \ --port 8000 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192各个参数的含义如下参数作用调大/调小影响--served-model-nameOpenAI 接口里model字段的值决定客户端请求时填写的模型名必须一致--host监听地址线上建议0.0.0.0本机调试可以127.0.0.1--port服务端口默认 8000注意与 Docker 映射端口一致--gpu-memory-utilization最大可用显存比例调高增加 KV Cache但也更容易 OOM--max-model-len最大上下文长度调高能支持长文本但会减少可并发请求数--max-num-seqs单个 batch 最多序列数调高提高吞吐过高容易显存不足--enforce-eager关闭 CUDA Graph 即时编译调试显存问题时启动较快服务性能会下降--max-model-len是把好刀也很容易伤到自己。它不只是一个“输入长度限制”还会影响 KV Cache 预分配。如果设成 32768那么每一条请求都会按更长的上下文预留显存虽然动态分配让浪费减少但 batch 内可容纳的序列数会下降。启动日志是排查第一现场。正常启动时日志里会打印模型 config、显存分配、GPU 位置等信息。看到Starting vLLM API server on 0.0.0.0:8000类似信息后说明服务已经就绪。3.3 通过 curl 验证模型列表与聊天接口首先验证服务是否在线curl http://localhost:8000/v1/models正常情况下会返回一个 JSON里面包含model字段名称就是--served-model-name指定的值例如qwen3-8b。接着发送一个聊天补全请求curl -X POST http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: qwen3-8b, messages: [ {role: user, content: 请用一句话介绍 vLLM 的 PagedAttention} ], max_tokens: 256, temperature: 0.7 }返回值类似下面这样{ id: chatcmpl-..., object: chat.completion, model: qwen3-8b, choices: [ { index: 0, message: { role: assistant, content: PagedAttention 是一种将 KV Cache 按块管理的注意力机制能够减少显存碎片并提高吞吐。 }, finish_reason: stop } ], usage: { prompt_tokens: 23, completion_tokens: 32, total_tokens: 55 } }到这里OpenAI 兼容接口已经跑通。后续所有支持 OpenAI SDK 的工具都能通过base_url指向 vLLM 服务来接入。4. Python 客户端实战流式输出、并发请求与超时控制4.1 使用 openai SDK 发起最小调用vLLM 的 API 和 OpenAI 兼容所以 Python 端直接使用openai库即可。安装命令pip install openai1.0调用代码from openai import OpenAI client OpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, ) response client.chat.completions.create( modelqwen3-8b, messages[ {role: system, content: 你是一个简洁的助手。}, {role: user, content: 什么是连续批处理}, ], max_tokens256, temperature0.7, ) print(response.choices[0].message.content)vLLM 默认不校验api_key所以可以用EMPTY占位。base_url必须写完整到/v1很多问题都出在少写了这个后缀上。在 LangChain 中也可以把 vLLM 当成标准的 OpenAI 兼容服务接入from langchain_openai import ChatOpenAI llm ChatOpenAI( base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY, modelqwen3-8b, )4.2 流式输出与首字延迟测量流式输出对用户体验很重要用户不需要等到完整结果而是看到模型一个词一个词往外“吐”。vLLM 侧设置stream: true客户端通过openaiSDK 迭代 chunk 即可。import time from openai import OpenAI client OpenAI(base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY) start time.perf_counter() stream client.chat.completions.create( modelqwen3-8b, messages[{role: user, content: 写一首关于清晨的短诗。}], max_tokens200, streamTrue, ) first_token None chunks [] for chunk in stream: if not chunk.choices: continue delta chunk.choices[0].delta.content if delta: if first_token is None: first_token time.perf_counter() print(f首字延迟{first_token - start:.3f}s) chunks.append(delta) print(.join(chunks)) print(f总耗时{time.perf_counter() - start:.3f}s)首字延迟TTFTTime to First Token是评估在线推理服务最重要的指标之一。它由以下部分构成请求进入队列的等待时间Prefill 阶段处理全部输入 token 的时间解码出第一个 token 的时间。所以如果用户输入特别长、服务并发特别高首字延迟一定会上升。流式输出只能改善用户“等待过程中的体验”并不能降低首字延迟。4.3 使用 ThreadPoolExecutor 做一次简单并发压测在生产环境发布前至少要验证服务的并发吞吐。可以用 Python 的ThreadPoolExecutor快速做一次多请求测试记录每个请求的耗时。from concurrent.futures import ThreadPoolExecutor, as_completed import time from openai import OpenAI client OpenAI(base_urlhttp://127.0.0.1:8000/v1, api_keyEMPTY) def one_request(idx: int): start time.perf_counter() response client.chat.completions.create( modelqwen3-8b, messages[{role: user, content: f请重复数字 {idx} 十次。}], max_tokens64, ) return idx, time.perf_counter() - start with ThreadPoolExecutor(max_workers8) as executor: futures [executor.submit(one_request, i) for i in range(8)] for future in as_completed(futures): idx, elapsed future.result() print(f请求 {idx}: {elapsed:.3f}s)这个压测脚本只能反映端到端延迟和客户端的完成情况不能直接当作基准结果。真正压测时要考虑请求的 prompt 长度和 max_tokens并发是否超过了 GPU 的实际处理能力是否观察了 GPU 利用率和显存占用是否记录每次请求的 prompt tokens 和 completion tokens。建议结合nvidia-smi观察显存和 GPU 利用率。如果 GPU 利用率接近 100%说明服务正在全力生成如果 GPU 利用率低但请求排队很多需要检查 batch 参数或模型加载是否异常。5. 让服务更快连续批处理参数、模型量化与指标监控5.1 Continuous Batching 不需要“开启”但要调好边界搜索“vllm continuous batching 怎么设置”时最容易被误导。vLLM 从设计上默认启用连续批处理没有--enable-continuous-batching这样的参数。真正需要关注的是限制调度边界的参数参数含义建议--max-num-seqs每一轮迭代最多同时运行的序列数显存充足时适当调大例如 128 或 256--max-num-batched-tokens每一轮迭代最多处理的 token 数根据 GPU 算力设置太小会限制吞吐--enable-chunked-prefill把长 Prefill 切块避免阻塞 Decode长输入场景建议开启--max-model-len模型最大上下文长度按业务实际需求设置不要无脑加大如果你的场景是长文档问答输入可能达到几千 token建议关注--enable-chunked-prefill。它会把一条长请求的 Prefill 拆成多块穿插执行其他请求的 Decode避免长输入请求把整个 batch 卡住。如果只是普通短对话把--max-model-len设置在 4096 或 8192--max-num-seqs设在 64 到 256 之间通常就能获得不错的吞吐。实际值要结合 GPU 显存和压力测试结果动态调整。5.2 量化模型先离线量化再让 vLLM 加载搜索“vllm怎么量化模型”时要明确一个概念vLLM 本身更擅长加载量化后的模型而不是提供完整的量化训练流水线。常见的流程是先用 AutoAWQ、llm-compressor 等工具把模型权重从 BF16/FP16 转成 INT4、INT8 或 FP8然后在启动 vLLM 时通过量化参数加载。vLLM 启动时常用的量化相关参数# 加载 AWQ 量化模型 vllm serve /models/Qwen3-8B-AWQ \ --quantization awq \ --served-model-name qwen3-8b # 加载 FP8 量化模型 vllm serve /models/Qwen3-8B-FP8 \ --quantization fp8 \ --served-model-name qwen3-8b量化带来的收益是显存占用下降KV Cache 可以分配更多空间从而支持更大的 batch、更高的并发。但量化不是没有代价INT4/INT8 在个别算子上的实现效率可能不如 BF16某些量化格式需要特定 NVIDIA GPU 架构老显卡可能不支持权重质量损失在复杂推理、数学题和代码生成任务上会更明显。所以量化选型先看业务需求。显存不够、需要同时服务很多用户优先考虑 AWQ 或 FP8显存充足、对输出质量要求高直接使用 BF16 反而更省心。5.3 开启 Prometheus 指标让性能问题可观测生产环境不能只在启动时看日志必须把运行指标暴露出来。vLLM 支持 Prometheus 格式的/metrics端点启动时加入参数vllm serve /models/Qwen3-8B \ --served-model-name qwen3-8b \ --enable-metrics \ --metrics-port 8100如果是 Docker Compose需要把 metrics 端口也映射出来并确保 Prometheus 能访问该端口。服务启动后可以先用 curl 验证curl http://localhost:8100/metrics | grep vllm输出中会看到大量以vllm_开头的指标例如请求排队数、正在运行的序列数、已经生成的 token 数、TTFT 统计、E2E 延迟统计等。具体指标名会随版本变化所以排查时可以先抓全量指标再挑选关键项。在生产环境建议把下面的指标接入监控大盘队列中的请求数正在运行的序列数每请求平均 TTFT每请求平均生成延迟生成 token 数和输入 token 数。注意不要只监控 GPU 利用率。GPU 利用率高可能说明模型在努力计算也可能说明服务已经过载。真正需要关注的是“用户等了多久才看到第一个 token”和“每秒钟能生成多少 token”。6. 服务慢、卡顿、首字延迟高的排查链路6.1 首字慢先分清是 Prefill 慢还是排队慢首字慢是最常见的问题现象是请求发出了用户看着“正在输入”等了好几秒。排查顺序应该是确认输入长度。输入越长Prefill 需要计算的总 token 越多首字延迟必然越高。确认服务是否在高并发下运行。如果同时有几十个请求在排队新请求的首字延迟会被拉高。确认--max-num-seqs是否过小。batch 太小会让 GPU 在低利用率下空转请求挤在一起。确认--enable-chunked-prefill是否开启。长文本 Prefill 会阻塞 decode开启后可以改善其他短请求的响应速度。查看/metrics中 TTFT 指标是否持续升高。解决方式可以分两类如果单条输入过长考虑限制max_model_len或在前置服务里做文本截断、摘要、检索只传必要上下文。如果并发高导致排队考虑增加--max-num-seqs、使用量化模型释放显存、增加 GPU 卡数或通过多副本负载均衡扩展。6.2 吞吐低、GPU 利用率不稳定怎么查如果 GPU 利用率不高但吞吐也上不去重点检查以下方向。检查项可能原因建议GPU 显存占用显存被权重占满KV Cache 空间太小使用量化模型或降低--gpu-memory-utilization以外的占用量--max-num-seqs设置得过小batch 无法扩大逐步调大观察 TPS每秒生成 token 数变化--max-num-batched-tokens单轮处理 token 上限太低调大后测试注意显存峰值--enforce-eager关闭了 CUDA Graph算子启动开销大生产环境不要加这个参数模型精度FP8 或 INT4 算子效率低和 BF16 对比测试确认量化收益磁盘 IO模型从磁盘加载慢权重 Page Cache 未预热启动后先发请求预热或把模型放到高速盘排查时不要凭感觉调参。建议每次只改一个参数压测一轮记录 TTFT、吞吐、显存和 GPU 利用率再决定是否继续调整。6.3 常见启动错误和请求错误下面整理几个真实项目中容易遇到的报错尤其是搜索热词里反复出现的“vllm expecting value”、Docker 部署问题和量化加载问题。问题现象常见原因检查方式解决建议日志或响应报Expecting value: line 1 column 1请求体不是合法 JSON用python -m json.tool校验 curl/payload用 Python 的json.dumps构造 body避免手写转义出错Docker 容器启动报 shared memory 错误/dev/shm默认 64MB 不够docker inspect查看 ShmSizeCompose 中设置shm_size: 16gb模型加载报The model is not supported模型路径错误或架构不支持 vLLM检查 config.json 的 architectures 字段确认模型目录完整升级 vLLM 或换支持模型量化参数报 Unknown quantization当前 vLLM 版本不支持该格式vllm serve --help查看支持的量化方式升级版本或改用 AWQ/FP8 官方检查点请求 OOMmax_model_len或并发过大nvidia-smi观察显存调低--max-model-len、--max-num-seqs使用量化接口返回 model not found客户端请求model与服务端不一致查看/v1/models返回名称统一使用--served-model-name指定的名称注意所有参数修改都要有记录。建议把启动命令和 docker-compose 文件纳入版本管理方便回滚和对比。7. 生产环境最佳实践从能跑到跑稳7.1 上线前检查清单下面是一份生产部署 vLLM 的检查清单每一项都能直接落地[ ] 确认 Python、CUDA、vLLM 版本匹配Docker 镜像锁定具体版本不使用latest。[ ] 确认模型权重目录完整并且容量足够。[ ] 确认 GPU 驱动和容器运行时已安装容器能看到 GPU。[ ] Docker 部署时设置shm_size大于 1GB建议 16GB。[ ] 只对需要开放的服务端口做映射metrics 端口不要暴露到公网。[ ] 设置合理的--max-model-len、--gpu-memory-utilization、--max-num-seqs。[ ] 启动后先用/v1/models和一次 chat 请求验证服务可用。[ ] 用流式请求验证首字延迟并记录正常服务的平均 TTFT。[ ] 开启/metrics接入 Prometheus 和告警。[ ] 压测时不只看并发请求数还要看 prompt tokens、completion tokens 和 GPU 利用率。[ ] 为调用方设置超时和重试策略避免下游无限等待。这套清单的核心逻辑是先保证可回滚再保证可观测最后才调整性能。7.2 vLLM 与 SGLang、Ollama 如何选型搜索热词里经常同时出现 vLLM、SGLang 和 Ollama。三者不是完全替代关系。框架定位优势适合场景vLLM高性能推理服务框架PagedAttention、连续批处理、OpenAI 兼容 API、生态成熟生产高并发 API、多用户服务SGLang高性能推理框架偏结构化加速RadixAttention、复杂提示词场景加速长上下文、多轮 Agent、结构化输出Ollama本地轻量模型运行工具安装简单、模型管理方便开发调试、个人笔记本、低并发如果团队已经在用 LangChain 或 OpenAI SDKvLLM 是兼容成本最低的选择。SGLang 在高并发和长上下文中也有竞争力但需要额外适配。Ollama 适合快速体验生产高并发场景不建议作为主力。选型不用追求“最强”要看你更看重什么兼容性、生态、吞吐还是快速上手。7.3 扩展方向多卡并行、自定义调度器与工具调用如果你已经跑通单卡服务下一步可以从这几个方向继续深入。多卡并行方面vLLM 支持--tensor-parallel-size例如两张卡运行时可以设置为 2。新版本对数据并行也有支持具体参数名会随版本变化启动前先执行vllm serve --help确认。多卡部署要重点观察通信开销不是卡数越多越快。自定义调度器方面vLLM 内部有自己的 scheduler 模块它负责请求排队、抢占和 batch 选择。如果你遇到“并发一高就卡死”的问题可以阅读 scheduler 的日志和指标不一定需要自己重写调度器。社区里有少量项目尝试替换调度策略但要保证兼容 vLLM 的前后端逻辑工程成本较高。工具调用方面vLLM 对 OpenAI 工具调用的支持越来越多。如果 Qwen3 这类模型需要 tool call 解析器可以在启动时指定--tool-call-parser具体取值根据 vLLM 版本和模型模板确认。接入 Agent 时先不要直接对源码下手把/v1/models、/v1/chat/completions这些接口的返回值和官方 OpenAI SDK 对齐再去扩展工具调用。回到最开始的问题AI 响应太慢很多时候不是模型本身弱而是推理引擎的调度和显存管理太粗。vLLM 通过 PagedAttention 和连续批处理把这两件事做到极致又把 OpenAI 兼容 API 变成标准入口。你不需要一开始就把所有参数调到最优只需要先跑通服务、量出首字延迟和吞吐再根据业务场景逐步调整模型长度、并发上限、量化精度和监控告警。这样部署出来的服务才不只是“能启动”而是真正可以接住线上请求。