Transformers 官方文档把 Serve 定位成轻量方案,适合本地自托管、实验评估和中等负载。真到大体量生产部署,官方口径是转向 vLLM 或 SGLang 这类专门的推理引擎。所以别把两者的差别理解成“一个只能推理、另一个才能上线”,差别在定位和适用场景。
从单次验证到多人并发,问题变了
用 Transformers 跑推理很直接:加载模型和分词器,组织输入,调 model.generate() 或 pipeline() 拿结果。调参、换模型、验证任务效果都方便。
但能跑完一次推理,不等于能高效扛住大量请求。
设想一个课程资料问答系统。最初只有工程师自己验证,一次问一句、等一句,Transformers 加 Serve 还能撑。整个班级同时用就不一样了:有人只问一句短问题,有人贴了很长的资料;有人早就答完,有人还在生成,新请求还在不断进来。
如果程序按“处理完一个再处理下一个”跑,后面的人只能干等。就算把多个请求凑成固定批次一起算,也得考虑短请求做完后能不能及时补进新请求,而不是死等整批结束。
这些是请求调度问题,不是“触发模型生成文本”的接口能解决的。
当关注点转向多人并发、批量吞吐、有限显存和算力的利用效率时,就该上专门的推理框架了。业界用得最多的就是 vLLM——一个面向大语言模型的开源推理与服务体系。
它不只提供跑模型的接口,还通过请求调度、缓存管理、计算优化来提升资源利用率,并支持把模型部署成可供其他程序调用的服务。
具体说,vLLM 不是逐个落实请求,而是把多个请求组织到一起算:连续批处理动态安排请求,分页式 KV Cache 管理提升显存利用率,再叠加其他计算优化。目标只有一个——给定硬件,更高效地完成持续到来的推理任务。
能提升多少吞吐、降多少延迟,得结合模型、硬件和请求负载实测,别指望换个框架所有场景都变快。
学 vLLM 不是否定 Transformers,而是把问题从“怎么让模型完成推理”推进到“怎么高效组织推理并对外提供服务”。
请求进入 vLLM 后,先过 API Server
理解 vLLM 不能只看“启动服务”和“拿到回答”两个动作,中间发生了什么才是关键。下面按 vLLM V1 的标准 GPU 文本生成服务架构,沿一次请求走一遍。
学生在网页上问:“这周的实验要提交什么?”
问题先到应用后端。后端查课程知识库,找到相关实验要求,再把资料和学生问题整理成对话消息。提交给模型的内容不一定只有用户刚输入的那句话——系统提示词、检索到的资料、需要保留的历史对话,都由应用按场景组织。
这里有条职责边界:查哪些资料、带哪些上下文,是应用后端的事;按收到的输入组织模型推理,是 vLLM 的事。应用没找到正确的实验要求,后面的模型推理补不出这份私有资料。
后端通过 API 客户端把请求发给 vLLM。请求到达后先进 API 服务层(API Server),不是直接进 GPU 做模型计算。服务端要接收、校验请求,再做输入处理。
以对话 API 为例,应用提交的是带角色信息的消息,比如 system、user、assistant。这些消息要按模型适用的聊天模板(Chat Template)组织,再经分词器转成 Token ID 序列。聊天模板负责整理角色、消息边界和生成起点,分词器负责把内容编码成数字序列。
调度器决定这一轮算谁
输入准备好后,请求进入引擎核心(Engine Core)。此时服务里可能已经堆着其他任务:有的刚到达,有的在处理输入,有的已经在产出回答。请求被服务接收,不等于立刻拿到 GPU 算力。引擎要持续跟踪请求状态并安排后续执行。
编排器(Scheduler)负责决定当前这一轮算哪些请求、各分配多少 token 的计算量。这个决定受调度预算和可用缓存空间等条件约束,不是简单把所有请求一起丢给 GPU。
举个例子:学生甲已经在输出回答,学生乙刚提交一段很长的资料,学生丙已经答完。下一轮计算要考虑:继续推进甲,安排乙做输入处理,让丙不再占后续的计算名额。这些都是推理引擎要协调的事。
调度完成后,vLLM 通过 GPU Worker 执行实际的模型计算。模型权重通常在服务初始化阶段就加载好了,不是每来一个用户请求就重新加载一次。调度器决定“当前哪些请求参与计算”,接下来要看的是:被选中的请求进入模型后,输入怎么一步步变成回答。
Prefill 与 Decode:输入处理和逐 token 生成
对普通的自回归文本生成,模型先处理请求里已有的输入,这个阶段叫 Prefill。课程问答里,回答规则、课程资料、学生问题共同构成输入上下文。模型对这些输入 token 做计算,处理完后根据计算结果选出第一个输出 token。所以 Prefill 是输入计算阶段,不是单纯把输入存起来。
拿到第一个 token 不等于整段回答就完成了。模型后面还要继续生成,每一步都要用到之前的上下文。假设输入里有一段很长的课程资料,后续每产出一个 token,都要把这段资料重算一遍吗?这就引出了缓存:把已经算出、后面还要用的结果存下来,需要时直接复用,压缩重复计算。
KV Cache 就是 Transformer 推理过程中保存的一类计算缓存。处理输入时,模型会把后续生成需要用到的一部分中间结果保留下来。这里不用深究内部数值结构,只要记住:缓存存的是模型计算产生的中间结果,不是聊天文字本身,也不是提前备好的答案。
没有缓存时,模型每产出一个新 token,都要重新执行大量与前文相关的计算;用了缓存后,前面处理过的内容对应的结果直接复用,只处理新增 token。模型不是不再参考前文,而是通过已保存的计算结果延续上下文。
这本质是用空间换时间。存缓存要额外存储空间,常见 GPU 推理配置里一般占显存。部署时不仅要看模型权重放不放得下,还要给运行时的缓存留空间。这也解释了调度器为什么要考虑可用缓存空间:缓存不够,能同时推进的请求就受限。
注意,Prefill 是处理输入的一个阶段,不意味着整个输入必须在一轮计算里完成。长输入 vLLM 可以分块处理;但从单个请求的生成过程看,仍然是先完成必要的输入处理,再据此选出第一个输出 token。
得到第一个输出 token 后进入 Decode 阶段。此时输入已经处理过,模型读取已有的 KV Cache,处理刚生成的 token,继续预测下一个 token。处理这个新增 token 产生的计算结果也加入缓存,供后续步骤使用。
假设输入共 1,000 个 token。Prefill 完成后,模型已保存这些输入对应的缓存并选出第一个输出 token。预测第二个 token 时,模型处理刚选出的第一个 token,复用前面 1,000 个输入 token 的缓存;下一步处理第二个 token,继续用已有结果。循环往复,回答逐渐变长。
Prefill 负责处理已有输入,Decode 负责在已有上下文基础上继续生成,KV Cache 连接这两个阶段。它在输入处理时建立,又在后续生成中不断被读取和更新。对这里讨论的普通逐 token 生成,整段回答不是一次算出来的,而是连续预测逐步形成的。
一次模型调用,内部对应多轮调度
在 vLLM 中,上述生成流程和调度机制是绑在一起的。引擎持续安排模型计算,按执行结果更新请求状态,再决定下一轮推进哪些请求。某一轮可能继续为学生甲产出内容,同时处理学生乙的一部分输入;下一轮参与计算的请求组合还可能变化。
一个请求不必从开始到结束独占 GPU,多个请求能在连续调度中共同推进。
所以应用发出的一次模型调用,在 vLLM 内部通常对应多轮调度和模型计算。应用不需要为拿每个新 token 重新发一次 HTTP 请求,推理引擎会持续推进生成。模型负责按上下文预测后续内容,vLLM 负责组织请求、安排计算、管理缓存,让多个请求更高效地共享算力。
前缀缓存:跨请求复用,省的是 Prefill
前面讲的是一次生成流程内怎么复用计算结果。再往前一步:如果下一次请求也含相同的课程资料、回答规则或对话历史,能不能接着用之前的结果?
当服务支持前缀缓存(Prefix Caching),且新请求有可匹配、仍可复用的前缀时,就能复用这部分内容对应的缓存。它主要省的是 Prefill 阶段的开销,不代表后面的回答不用继续生成。
这种复用也可能直接体现在 API 计费上。比如 DeepSeek 的价格表把输入区分成缓存命中与缓存未命中,命中的部分输入单价更低。要区分清楚:这里计费针对的是服务端认定的缓存命中,不是模型生成过程中用了 KV Cache,所有输入就自动按低价算。
每次调模型都可能带着系统提示词、工具说明、任务要求和已有历史,多轮执行的 Agent 尤其如此。这种组织方式可能构成较长的重复前缀,为缓存复用创造条件。但任务长不等于必定命中缓存,关键仍是前缀能否匹配、对应缓存是否可用。这类应用里,把固定内容和可变内容合理组织,才有机会同时减少重复输入计算和调用成本。
输出怎么回到用户:反分词与两种返回方式
模型生成的新 token 在程序内部一般以 token ID 形式存在,不是直接以网页文字出现。服务要用与模型配套的分词器把这些编号还原成可读文本,这个过程叫反分词(Detokenization)。它处理的是已经生成的结果,不是让模型重新组织一次答案,也不同于持续预测后续 token 的 Decode 阶段。
完成文本转换后,服务还要按 API 格式组织响应,再通过网络返回客户端。模型计算产生的结果不会直接变成网页上的文字:服务负责返回数据,应用负责读取数据并决定怎么展示给用户。
模型服务通常支持非流式返回和流式返回。非流式要等本次生成结束,把结果一次性返回;流式则可以在生成过程中持续发送已产生的输出片段,客户端接收后逐步更新页面。对用户来说,前者是等一段时间看到整段结果,后者是回答不断向后延伸。
两种方式的主要区别在于结果什么时候返回,而不是模型能否逐步生成。即使非流式返回,模型内部仍要经历前面讲的生成过程,只是中间结果没有立即展示。流式返回也不要求应用为每个新 token 重新发请求,而是在同一次请求的响应流程中持续接收数据;一次收到的文本片段,也不一定恰好对应一个 token。
生成流程需要明确的停止条件。常见的有:模型产出结束标记,输出匹配了指定的停止内容,或者达到设置的最大输出 token 数。满足条件后该请求不再继续生成,服务返回相应的结束信息。要特别注意,生成结束不一定说明回答已经完整表达完毕:如果是因为到达长度上限而停止,最后一句话或最后一部分内容可能还没写完。
所以应用除了读回答内容,也要关注返回的结束原因,别只要收到文字就默认任务完整完成。课程问答场景里,应用可以把结果展示给学生、保存问答记录;如果发现输出受长度限制,就提示回答可能不完整,再按业务需要决定怎么处理。
到这里,一次课程问答的链路就串起来了。学生看到的是整理后的实验提交要求,而这段回答经过了资料准备、输入处理、请求编排、模型计算和输出返回等多个环节。三者的职责可以这样区分:应用决定做什么业务、提供什么信息、怎么用结果;模型按输入做计算,逐步预测后续内容;vLLM 负责接入请求、编排执行、管理缓存,并组织结果返回。
工程上容易踩的几个坑
第一个坑是把 Prefill 和 Decode 的负载特征混为一谈。Prefill 是计算密集型,一次要处理成百上千个输入 token;Decode 是访存密集型,每步只处理一个 token 但要反复读 KV Cache。调度器把这两类请求混在一轮里,容易出现算力和带宽互相拖累。生产上调参时,长输入请求和长输出请求最好分开观察。
第二个坑是显存估算只算权重。KV Cache 的占用跟并发请求数、上下文长度、层数、注意力头数都相关,长上下文场景下它可能比权重还大。部署前用 nvidia-smi 看显存占用,再逐步加压观察 gpu_memory_utilization 是否触顶,比拍脑袋设并发数靠谱。
第三个坑是流式返回下客户端不做超时和断连处理。流式连接可能因为网络抖动或客户端取消而中断,服务端要能感知并释放对应的调度槽位和缓存,否则这些资源会一直挂着。压测时故意断开几个流式连接,看服务端显存和请求队列能不能回落,是个有效的验证手段。
执行主线清楚了,优化才有落点
本节以课程资料问答系统为例,走了一遍 vLLM 怎么组织一次模型请求的执行。重点从“怎么让模型生成回答”转向“怎么让多个请求在有限显存和算力下高效完成推理”。vLLM 的价值不只是一个调用 API,而是把请求接入、输入处理、调度执行、缓存管理和结果返回组织成完整的服务过程。
从应用发出请求到用户看到回答,中间经过应用组织请求、服务处理输入、引擎编排、模型计算和输出返回等环节。应用负责准备场景所需的信息,vLLM 负责组织模型推理。应用发出的一次调用,在引擎内部通常对应多轮编排与计算;采用流式返回时,已生成的内容还能在这个过程中持续传回应用,不必等整段回答生成结束。
模型计算内部要重点理解 Prefill、Decode 和 KV Cache 的关系:Prefill 处理已有输入并据此选出第一个输出 token;Decode 利用已有计算状态逐步产出后续 token;KV Cache 保存可复用的中间结果,连通这两个阶段。缓存压缩了重复计算,但也占存储空间,所以计算怎么安排和缓存怎么管理是紧密关联的两个问题。
建立这条执行主线后,再学连续批处理、分页式缓存管理等优化机制,就不只是记技术名词,而是能判断它们作用在哪个环节、减少了哪些重复计算、等待或资源浪费。实际能提升多少吞吐、改善多少延迟,仍然要结合模型、硬件和请求负载实测。
理解执行过程,是进一步理解推理优化、上线配置和性能评估的基础。