
这段时间社区里聊大模型推理十有八九绕不开 vLLM。我自己第一次接触它的时候也蒙一堆概念堆在眼前又是什么 PagedAttention又是什么 Continuous Batching装环境还能装到一半卡住网上资料要么是源码级解析要么就是一句直接用就好。这篇就是给像我当初一样、面对 vLLM 不知道从哪下手的同学写的快速入门引导我会把环境准备、最小推理示例、关键参数、常见坑这些一环一环讲清楚保证你照着走能跑通第一个推理请求并且知道每一步在做什么、为什么这么做。这套内容适合下面几类人刚接触大模型、想把开源模型部署成可用 API 的开发者被推理速度慢、显存不够折腾到头秃的算法工程师以及想评估 vLLM 到底值不值得引入项目的技术负责人。我会默认你已经有一台带 N 卡的 Linux 机器Python 基础也具备代码上尽量保持简单不涉及分布式训练那种复杂玩法。1. 先搞明白vLLM 到底解决什么问题1.1 大模型推理慢的根源算力只是其中一半很多新手第一次跑大模型发现生成一句话要等好几秒第一反应是显卡不够好。其实算力只是一个维度另一个被忽视的瓶颈是显存管理。像 GPT、LLaMA 这类模型在生成文字时每个输出的 token 都会不断把历史计算出的中间状态存下来这些状态有个统一的说法叫 KV Cache。它的容量会随着序列长度线性增长而且不同请求的序列长度完全不一样。如果用传统推理框架显存里要预先给 KV Cache 划一大块连续区域。问题是请求长短不一这个预分配空间要么浪费、要么不够用。模型参数占了大部分显存剩下给 KV Cache 的空间本来就很挤再这样一浪费能同时处理的并发请求数量就很少。实际表现就是单个请求还能忍并发一上来轻则 OOM重则排队排到怀疑人生。1.2 vLLM 的三大核心设计PagedAttention、Continuous Batching、张量并行vLLM 之所以能火不是靠疯狂压榨 GPU 算力而是把操作系统的内存管理思想搬到了显存里。最核心的设计叫 PagedAttention它把 KV Cache 切成固定大小的页按需分配不再要求一块连续的存储空间。显存里的小碎片都能被利用起来实际空闲显存利用率能提高不少这在长上下文场景下尤其明显。第二个设计是 Continuous Batching。常规批处理是把一批请求凑齐了再统一跑一个请求提前结束它占用的整排算力也得等这批全部跑完才能释放。Continuous Batching 的做法是每个解码步都动态决定哪些请求继续跑、哪些已完成退出、哪些新请求插入空闲的算力立刻被新请求补上。这样吞吐提升很直接尤其适合线上那种请求到达时间不规律的场景。第三个设计是张量并行。一个 70B 模型单卡塞不下可以把模型切分到多张卡上并行推理。vLLM 对张量并行的封装做得比较到位命令里写个参数就能启用。但入门阶段建议先用单卡跑通多卡并行等对流程熟悉了再玩。1.3 什么场景适合上 vLLM什么场景没必要我要先泼个冷水如果你的需求只是本地偶尔用 CPU 跑一下 demovLLM 并不适合你它主要面向 GPU 场景CPU 支持虽然有但性能优势发挥不出来。如果你只是想把模型跑起来看看效果一次只发一个请求也不想调参数那用最简单的推理脚本就行接入 vLLM 反而多一层学习成本。反过来如果下面这些场景占了任意一条vLLM 就是很值得上的方案并发请求超过 5 个输入输出上下文动辄几千 token需要把模型包成稳定 HTTP 服务给业务侧调用或者显存只够放下模型几乎没有余地给 KV Cache。我自己的经验是一个原本单个请求生成 256 个 token 就要 5、6 秒的 7B 模型换到 vLLM 并把并发拉起来之后整体吞吐肉眼可见地涨了一截而且显存占用反而更可控。2. 安装与运行环境准备这一步卡住了不少人2.1 硬件基线显存到底怎么算动手安装前先确认显存够不够。模型权重占用的显存大约等于参数量乘以精度字节数。以 7B 参数、FP16 精度为例7 乘以 2 个字节约 14 GB这个数字只是一个粗略下限。实际运行时会额外加载 KV Cache、激活值、CUDA 上下文等推荐至少留出 4 到 6 GB 余量。也就是说一块 24GB 显存跑 7B 模型比较舒服13B 模型建议 40GB 以上70B 模型即使量化后也要 40GB 以上才玩得动非量化单卡基本不可能。如果你显存不够不要立刻放弃可以看看后续量化的用法。入门阶段我建议找一块 24GB 的卡用一个 7B 左右的对话模型练手这是最稳的起点。显存更小的机器可以先从 1B 到 3B 的小模型开始。2.2 创建干净的环境再安装安装 vLLM 建议用虚拟环境隔离。我见过太多人直接在系统 Python 里装结果和旧版本包冲突最后连显卡驱动都被搞得乱七八糟。第一步创建一个干净的 conda 环境指定一个兼容的 Python 版本。不同的系统、CUDA 版本对 vLLM 的要求有差异但一个基本共识是用较新的 Python 版本问题少一些比如 3.10 或 3.11。创建好环境后直接执行conda create -n vllm-env python3.11 -y conda activate vllm-env pip install vllm如果这一步顺利你会看到一堆依赖被装好。CUDA 方面vLLM 的预编译包通常已经包含所需的 CUDA runtime不需要你自己额外装 CUDA Toolkit但显卡驱动版本要够新。nvidia-smi里看到的驱动版本对应支持的 CUDA 版本必须满足要求这是最容易忽略的一条。2.3 安装失败的常见原因和处理思路我碰到过两类安装失败概率最高。第一类是 pip 在构建某个依赖时拉取源码编译编译到一半 GCC 报错。这通常是网络源不稳定或者 Python 版本不匹配导致的。先用国内镜像源试试pip install vllm -i https://pypi.tuna.tsinghua.edu.cn/simple第二类是装完以后导入时报缺少共享库的错误比如libcuda.so找不到。这个多半是 CUDA 驱动路径没让 Python 进程找到把相关库路径加进环境变量通常能解决。具体路径每台机器不同可以用find /usr -name libcuda.so* 2/dev/null查找。验证安装是否成功就运行下面这行python -c import vllm; print(vllm.__version__)能打印出版本号说明环境基本没问题。如果这行直接报错建议把完整错误信息拿到检索工具里搜一下按报错关键字逐个排查而不是盲目重装。3. 第一次上手单卡跑通一个对话模型3.1 准备模型权重本地目录还是模型仓库 IDvLLM 支持直接写一个模型在仓库里的 ID也支持本地目录路径。入门阶段我建议先把模型下载到本地再通过本地路径加载。原因是调试阶段频繁修改代码和参数每次都从远端拉取权重既慢又容易被网络波动搞挂。下载模型需要使用模型仓库工具命令大致是把目标模型仓库拉下来因为模型权重动不动几个 GBGit 操作容易失败。如果网络条件一般建议不通过 Git 拉取而是找到模型文件的直链后用下载工具多线程续传。权重准备好后本地路径下会有一堆文件包括模型权重、配置 JSON、词表文件等。加载时把路径传给model参数就行。我这里用一个虚构的路径./my-chat-7b作为示例你换成自己实际的模型目录。3.2 最小的 Python 推理脚本创建一个 Python 文件比如quickstart.py写入以下内容from vllm import LLM, SamplingParams llm LLM( model./my-chat-7b, dtypebfloat16, gpu_memory_utilization0.85, max_model_len4096, ) sampling_params SamplingParams( temperature0.7, top_p0.9, max_tokens512, ) prompts [ 用一句话解释什么是大语言模型。, 写一个 Python 快速排序函数。, ] outputs llm.generate(prompts, sampling_params) for seq, output in zip(prompts, outputs): text output.outputs[0].text print(f用户: {seq}) print(f模型: {text}) print(---)这段脚本做了三件事。第一先初始化LLM对象这一步会加载模型权重并建立推理引擎通常需要几秒到几十秒模型越大越慢不要误以为卡住。第二定义采样参数temperature控制随机性max_tokens控制最多输出多少 token。第三一次性传入多个 prompt生成结果后逐个打印。gpu_memory_utilization我用的是 0.85意思是给显存管理器留了 15% 的余量。如果你后续想提高并发可以往下调如果显存富余可以往上调到 0.9 左右。max_model_len设成 4096表示模型能接受的上下文总长度上限包含你的输入和输出。如果设置过小长输入会被截断过大则 KV Cache 预留空间变大可支持的并发数下降。运行脚本python quickstart.py第一次运行会看到大量日志里面包括模型加载进度、显存分配情况等。如果看到输出了两条结果恭喜你第一个 vLLM 推理任务跑通了。3.3 命令行启动一个 HTTP 服务Python 脚本适合测试和批处理但很多人的真实需求是给业务侧提供一个可调用的 API。vLLM 内置了一个服务端命令启动方式非常简单vllm serve ./my-chat-7b \ --served-model-name my-chat \ --dtype bfloat16 \ --max-model-len 4096 \ --gpu-memory-utilization 0.85启动后日志会显示服务监听在哪个端口默认是8000。然后你可以用curl发一个请求验证curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: my-chat, messages: [{role: user, content: 你好请介绍一下你自己}], max_tokens: 256, temperature: 0.7 }返回的 JSON 里会包含大模型生成的回复内容以及 token 使用量。这里要留意一个细节curl请求里填的model名必须和--served-model-name保持一致否则服务端会报模型不存在的错误。我第一回跑服务的时候就被这个字段坑过一度以为是端口没起来。3.4 并发请求第一次展示威力最直观感受 Continuous Batching 的方法是用多线程或开几个终端同时发请求。甚至更简单你可以在 Python 脚本里把 prompts 列表扩到 20 条再跑一次。你会发现20 条请求的总耗时绝不是单条的 20 倍而是远小于这个值。这就是批处理在起作用vLLM 不断把处于解码阶段的请求拼在一起同时推进GPU 的算力被填得更满。有个观点我想强调一下vLLM 的单请求延迟不一定比其他框架快多少甚至在某些场景下可能差不多。它最亮眼的指标是高并发下的吞吐量。所以评估 vLLM 时不要只测单条请求要几秒要测并发场景下每秒能处理多少请求、每请求的平均延迟是否有明显劣化。4. 参数与用法进阶入门和高手的差距就在这张表里4.1 SamplingParams 里的核心参数SamplingParams是控制模型生成行为的关键对象。很多人只调temperature其实里面门道不少。我用一张表把常用参数列出来后面再逐个补充经验。参数作用推荐值使用经验temperature控制随机性越高越发散0.6-0.9代码生成建议 0.2 以下创意写作可以高一些top_p概率累计采样的阈值0.8-0.95和 temperature 配合使用不要同时调到极大max_tokens单次生成的最大 token 数根据业务设定设太大容易等很久设太小回复截断且没有提示stop遇到标记停止生成按模型定制对话类模型常用 n每个 prompt 返回几条候选默认 1多候选做重度筛选时才有必要显著增加计算量一个常见误区是把max_tokens当成生成长度限制就完了。事实上它同时影响服务端的内存预留和你的等待时间。设置过大会让某些请求在失败时才报错且 OOM 概率上升。我在业务里定位到过好几次请求一直不返回的情况最后都发现是max_tokens设置得离谱模型输出超长服务端还在慢慢生成客户端早就超时断开。4.2 服务端参数并发能力该怎么调除了采样参数vLLM 服务启动时还有一批参数直接影响并发和稳定性。比较关键的是max-num-seqs它限制了同时处理的序列数量上限。默认值不低但如果你显存紧张它可能是 OOM 的原因。建议根据显存和真实需求显式设置而不是完全依赖默认值。另一个是gpu-memory-utilization。这个值控制有多少显存被分配给模型权重和 KV Cache。设得太低可用 KV Cache 少并发吞吐上不去设得太高遇到输入特别长的突发请求容易直接显存溢出。我个人的经验是对 24GB 显卡跑 7B 模型0.85 是个不错的起点如果并发请求高再慢慢往下微调。如果你想让系统自动管理请求排队而不拒绝连接关注一下是否有--limit-max-requests之类的参数。不同版本名字可能不一样我建议启动服务时先用vllm serve --help查看当前版本的完整参数说明养成这个习惯能少踩很多版本差异的坑。4.3 显存不够时的量化方案很多人的显卡跑 7B 模型很紧张跑 13B 直接没戏这时候量化是个实用出路。vLLM 支持加载已经量化好的低比特权重比如 4bit 或 8bit 格式。量化的核心逻辑是把原本 2 字节的 FP16 权重压缩到更少的比特数显著降低显存占用代价是生成质量可能会有轻微下降。量化权重的加载方法和普通模型一致只需要把模型目录里的权重文件替换成量化版本然后用--dtype配合实际格式。要注意的是量化格式有版权和质量差异不同格式对硬件的适配也不一样。入门阶段不要自己尝试训练或转换量化权重直接下载社区里别人已经处理好的量化模型文件先把流程跑通。等熟悉之后再研究量化参数和精度的权衡。另外即便不换量化权重也可以通过适当降低gpu-memory-utilization之外的一些特殊技巧省显存比如使用--enforce-eager关闭算子图优化。这个选项默认不开启它会省去构建优化图的额外显存但代价是推理速度略慢。适合调试阶段快速验证不适合生产环境长期开启。4.4 前缀缓存带来的长文档处理提升vLLM 还有一个容易被低估的功能叫前缀缓存。它会把请求中相同的前缀内容对应的 KV Cache 缓存下来下次再遇到同样前缀时直接复用不用重新计算。这个功能在 RAG 场景下特别明显因为多个请求的开头往往都是那一大段系统提示词或文档内容。启用方式很简单启动服务时加上--enable-prefix-caching参数即可。效果有多大我实测过一个固定 2000 token 前缀的文档问答场景开启这个功能后相同前缀部分的计算被跳过吞吐提升非常可观。如果你还开着批处理那真的是几层 buff 叠在一起的体验。但要提醒一下前缀缓存也有开销。它会占用额外的 CPU 内存和显存来做索引对短请求、前缀变化大的流量反而可能增加管理成本。是否开启要看业务特征不是无脑加的参数。5. 推进生产之前一定要学会处理的这些问题5.1 显存不足的根本排查链路即便做足了估算生产环境依然可能爆显存。我碰到过一种现象启动时明明显存够用运行一会儿后就报 OOM。排查思路要从四个方向依次看。第一看模型权重是否加载完整。如果日志里有 No enough memory 这类字样多半是权重本身就不止那点显存。当场手段是换更小的模型或启用量化。第二看 KV Cache 是否限额。gpu_memory_utilization设得高不代表没有上限可以临时把max-num-seqs调小观察 OOM 是否消失。第三看并发尖刺。有的请求输入长达上万 token一个请求吃掉的 KV Cache 可能抵得上普通请求几十个只看平均并发会漏掉这种极端请求。第四看其他进程是否占用了显存。用nvidia-smi查看显存实际占用经常发现模型已经加载完另一块显卡上还残留着别的进程。解决显存不足优先级我个人排成这样先限制服务并发和输入长度再上量化或降低精度最无奈才换小模型。限制服务参数改动成本最小对业务影响也可控。5.2 模型加载慢不要误判成机器死机有些新手加载一个大模型看到日志停在某一步不动就以为卡死了直接重启。实际上 70B 模型冷启动加载权重耗时几分钟都很正常。加载慢通常有三个原因磁盘读得慢、模型权重格式需要转换、显存碎片导致分配缓存重建。如果你发现模型从本地加载比预期慢很多可以考虑把权重文件放到 SSD 上这个优化见效极快。另外第一次加载一个新格式权重时vLLM 可能会做权重格式转换并写缓存文件后续加载就会快很多这种第一次慢、之后快的现象不是 bug。所以我的建议是模型文件尽量固定在稳定目录不要频繁移动首次启动前预留足够的耐心。5.3 请求超时和回复乱码的排查思路请求超时是接入服务后最频繁的线上问题。先定位是不是客户端超时设置太短max_tokens设置太大模型生成 512 个 token 可能需要十几秒如果客户端只等了 5 秒必然超时。再定位是不是服务端排队当请求量超过并发能力新来的请求会在队列里等待前序完成这段时间也算在延迟里。这时候需要做的是观察时间分布如果大部分延迟都集中在队列等待就该提高并发上限或扩容。回复乱码的情况我在刚接触语言模型时也见过。排查顺序是先看输入的 tokenization 是否正确有些模型需要特定的聊天模板如果你直接裸投文本输出可能前言不搭后语。再看采样参数temperature过高、top_p过小时内容容易破碎。最后看模型本身中文效果差的模型可能输出叠字、重复或很长的结束符序列这不是框架的锅换个合适对话模型往往能解决。5.4 版本锁定和复现性生产环境的隐形安全网vLLM 迭代速度快版本之间的启动参数、默认端口、API 响应字段都可能有改动。我踩过一次严重的坑一个脚本基于旧版本开发某天更新后服务启动时多了一个必选参数线上所有自动化任务一夜之间全部失败。从那以后我养成了固定版本的习惯思路很简单在任何生产环境里都明确记录 vLLM 的版本号、Python 版本、CUDA 版本和模型文件哈希这四个信息一起构成一个可复现的环境快照。具体操作上可以用项目依赖文件把 Python 包版本全部冻结把服务启动命令写成一个脚本而不是裸敲命令每次有变化都走代码评审流程。vLLM 的 GitHub Releases 页面会标明每个版本支持的 Python 和 CUDA 范围升级前仔细比对。不要轻易用最新版这种模糊策略除非你只在测试环境玩。6. 一些额外的心得和小结零零散散写了这么多最后分享一点我自己的使用体会。vLLM 的学习曲线并不陡真正难的是在没搞清楚它为什么快之前就盲目堆参数。其实这框架的核心思想很朴素把显存当成一个有碎片、有分页的存储系统来管理把算力当成一个需要时刻喂饱的流水线。理解了这两点那些参数名就不再是黑盒。入门时不要急着上多卡、上量化、上各种花哨特性先用一个 7B 模型走通 Python 脚本和 HTTP 服务然后再一个个加特性每次只改一个变量观察它对显存和吞吞吐量的影响。还有一个小技巧是善用vllm serve --help。网络上各种博文写的参数可能对应旧版本但当前环境里那个命令输出的参数说明永远是最准确的。遇到网上说的没错但本地就是不认的情况先去翻--help再对照自己的版本号大部分悬案都能破。vLLM 这个框架我还有大量玩法没展开比如用结构化输出约束 JSON 格式、配合函数调用、做更长上下文的扩展。但那些都建立在今天这基础之上。你先跑通一个最小流程后面的一切都好说。