大模型的推理引擎如何运转:从一个 Token 到 Agent 缓存

一次前向计算只产生一个 Token。围绕这个物理事实,读懂 Prefill、Decode、KV Cache、连续批处理、分页显存,以及 Agent 为何高度依赖前缀缓存。

在绝大多数开发者的日常直觉里,调用大语言模型是一次简单的黑盒网络请求:我们将一段 Prompt 发送出去,云端某张 GPU 运行模型,随后一行行文字流式返回。但如果把视角切入机架内部,真实的物理世界完全是另一副模样:一块昂贵的 GPU 显存随时面临被挤爆的危险,算力核心也可能因为输入不足而空转,底层系统必须持续协调 CPU 与 GPU 的任务接力。

Together AI 的工程师 Zain Hasan 在 Open Source AI Summit 上带来了一场关于推理引擎底层机制的技术拆解。整场演示剥离了所有黑盒封装,完整还原了大模型推理的物理本质:推理引擎不是单纯的数学运算器,而是一台为了榨干三万美元 GPU 算力而设计的连续饱和机器。

朴素假象与物理真相:模型每次只吐一个整数

原稿第 3 页勾勒了人们对大模型调用的朴素认知:用户提出问题,请求发送给某处的 GPU 或模型,随后直接返回回答。

原稿第 3 页展示的朴素黑盒图:用户提问进入某处的 GPU 或模型并直接输出回答

这张图故意省略了分词、调度、KV 显存管理以及逐 Token 自回归循环,为后文拆解真实黑盒做好了铺垫。这种交互极易让人产生误解,以为大模型像编译器处理代码一样,经过整段语法分析后一口气输出了完整文章。

但 GPU 从未见过任何英文字母或汉字。在请求接触 GPU 之前,运行在 CPU 上的分词器(Tokenizer)必须先把文本切碎,映射为计算机能够识别的整数 ID。

文本被 CPU 分词器切分为离散的整数 Token 序列

原稿的示例词表包含 10 万个以上的条目,并给出一个粗略换算:一个 Token 约等于四分之三个英文单词。输入给模型的并不是句子,而是一串纯粹的整数张量。

进入 GPU 后,大模型的生成规律可以用一句话概括:一次前向传播,只预测一个 Token。

自回归生成每走一步前向计算只产出一个新 Token

模型根据当前输入序列计算出下一个最可能的整数,将其追加到序列末尾,然后将整串新序列作为下一次计算的输入,再次运行前向传播。这个单步循环会一直重复,直到模型吐出结束符(Stop Token)或者达到最大长度限制。写出一篇 500 单词的英文回答,模型必须在底层硬件上连续跑约 700 次完整的前向计算。


推理的双重人格:Prefill 与 Decode 的根本分歧

驱动这 700 次循环的虽然是同一个模型权重,但整个推理生命周期被物理特征完全相反的两个阶段割裂开来。

Prefill 与 Decode 阶段在计算负载与内存传输上的根本差异

第一个阶段是预填充(Prefill)。此时 GPU 一次性吃进用户传入的整段 Prompt,所有输入位置的注意力张量可以完全并行计算。矩阵乘法规模庞大,能轻易把 GPU 的数万个计算核心全部喂饱。这个阶段是纯粹的计算受限(Compute-bound)任务,它耗费的时间直接决定了用户等待第一个字跳出来的延迟(Time To First Token,TTFT)。

第二个阶段是自回归解码(Decode)。一旦进入生成,模型就必须退化为单步串行:每前向传播一次,仅产出一个 Token。在每一步中,硬件都要读取截至当前的上下文缓存,再计算这一个新 Token。单步的并行计算很少,内存读取却很大。此时瓶颈转为显存带宽受限(Memory-bound),它直接框定了每秒能生成多少个 Token(Tokens Per Second,TPS)。

这一计算特性是理解后续所有推理引擎调度设计的底层基石。


显存杀手 KV Cache:用空间置换二次方重算

在 Decode 阶段,自注意力机制(Self-Attention)需要让新 Token 与所有历史 Token 计算相关性。如果没有任何缓存,每向前生成一步,硬件就必须从第 1 个 Token 开始把整个序列的所有 Key 和 Value 矩阵从头重算一遍。计算量将随文本长度呈现不可接受的二次方爆炸增长。

为了避免这种巨大的无用功,推理引擎引入了 KV Cache:将所有历史 Token 的 Key 和 Value 张量计算完毕后保存在 GPU 显存中,后续每一步只需计算当前新增 Token 的一列新向量,历史列直接从显存调取。

KV Cache 将历史列保留在显存中避免无休止的二次方重算

根据原稿图示,KV Cache 把每一步的重复 K/V 计算变成只计算新 Token 的一列,旧列则从缓存读取。读取量仍会随上下文变长,而且这种以空间换时间的设计会让 KV Cache 随并发用户和上下文一起膨胀。因此,演示把显存容量,而非只看计算量,视为推理批次的常见上限。


推理引擎的三层流水线架构

原稿第 8 页指出了真实推理服务必须同时面对的四个核心约束:

原稿第 8 页展示的推理硬件与并发请求的四大物理约束

  • 硬件成本高昂:一张 GPU 成本约 3 万美元,并且不应该处于空转状态;
  • 并发状态各异:系统同时服务数百名用户,每人处于请求的不同阶段;
  • 单步产出极低:每个用户在一次前向计算(Pass)里仅生成 1 个 Token;
  • 显存瓶颈先行:常常先耗尽的是显存容量,而不是计算能力。

这四项物理约束是理解后续系统工程的因果枢纽:为了让昂贵的 GPU 尽量不空转,并在显存先于算力耗尽的现实下协调处于不同阶段的用户请求,推理引擎需要 CPU 调度器、连续批处理(Continuous Batching)与分页 KV 显存(PagedAttention)。

因此,现代推理引擎的本质是一个围绕 GPU 打造的连续供料流水线。整个系统被明确划分为三层,模型权重仅存在于最后一层:

推理引擎三层进程流水线与 CPU-GPU 重叠执行时序

API 服务端(运行于 CPU):负责处理 HTTP 输入输出、将 Prompt 切分成整数 Token 序列,并在输出端将 GPU 算出的整数 ID 翻译回可读字符,再流式返回给客户端。

调度器(运行于 CPU):这是整个引擎的大脑。在每一个执行步(Pass)开始之前,它都要决定当前批次放入哪些请求、每个请求处理多少 Token,以及使用哪些 KV 显存块。

执行工作节点(运行于 GPU):每张 GPU 对应一个独立 Worker,专注于高强度张量并行计算。它从调度器接收打包好的批次,执行一次前向传播,并在词表空间完成采样,将选中的 Token ID 返回给 CPU。

三者在时间轴上重叠推进:当 GPU 正在全速执行第 N 步的前向计算时,CPU 上的 API 进程已在同步对第 N-1 步产生的 Token 执行反分词并推向网络。

在 GPU 吐出最终结果的瞬间,底层经历了一次从数十万维度到单个整数的概率坍缩。

从 10 万维度 Logits 到单 Token 采样的流式处理流程

原演示给出了具体的采样案例:GPU 的最终线性层针对 10 万个以上的词表条目分别输出一个原始得分(Logit)。通过温度系数(Temperature)、Top-p、Top-k 以及重复惩罚等规则重塑概率分布后,GPU 在当前候选集(例如词表中得分分别为 0.52 的“mat”、0.21 的“rug”、0.14 的“floor”等)中完成随机采样,选定离散 ID 4352,再交由 CPU 翻译为文字并输出。


榨干硬件的三大经典调度支柱

在理想状态下,调度器希望同时并发处理几百个用户的请求。但现实中,每个用户的输入长度不同,说话速度各异。如果采用传统的批处理思维,硬件利用率会迅速崩溃。为了让 GPU 保持饱和运转,现代推理引擎确立了三大核心机制。

连续批处理(Continuous Batching)

在早期的静态批处理模式下,系统将几个请求打包为一个 Batch 扔进 GPU。由于不同请求生成的长度差异极大,短请求可能在第 10 步就已输出完毕,而长请求需要走 500 步。在剩余的 490 步里,那些已经结束的算力插槽只能处于空置等待状态,直到整批中最慢的请求结束才能整体换批。

静态批处理产生的空闲气泡与连续批处理的动态补位对比

Continuous Batching 改变了调度的粒度,在每一个生成步重新审视并重组当前批次。一旦某个请求吐出终止符退出了显存插槽,调度器立刻在下一个前向传播步中把新请求塞入空位。所有槽位始终维持在满载状态,彻底消灭了被动等待带来的算力空泡。

分页注意力(PagedAttention)

如果说 Continuous Batching 减少了算力槽位的浪费,PagedAttention 则用按需分块来减少显存预留和碎片带来的浪费。

在过去,系统必须按照请求可能达到的最大长度(例如 4K 或 8K)为每个用户预先划定一段连续的物理显存空间。原演示指出,在这种静态预留机制下,3 个长请求就能把整张卡的显存空间彻底锁死,即便它们当前只生成了几个字、大部分预留显存空空如也,第 4 个请求也只能在队列外排队挂起。

预留最大显存与按需分页共享池的显存利用对比

PagedAttention 引入了现代操作系统虚拟内存分页的设计思想:将 KV Cache 切分成固定大小的物理块(Block),按需申请、动态挂载。各个物理块在显存中完全不需要物理连续,而是通过一张虚拟映射表进行逻辑寻址。通过共享的动态物理块池,原本只能容纳 3 个请求的显存空间,现在可以跑起 4 个请求,同时依然有一半的显存池处于空闲可用状态。正如原稿总结的,KV 缓存容量通常比计算能力更先限制批处理规模。

分块预填充(Chunked Prefill)

当系统里同时存在“正在生成文字的短请求”和“刚刚发送进来的超长 Prompt”时,另一个冲突出现了。

如果调度器允许一个超长 Prompt 一口气霸占整个前向传播步,其他处于解码阶段的活跃用户在该步就无法获得新的解码 Token。

将 8K 提示词按 2K 预算切分四批并与 Decode 混合调度的时序

分块预填充(Chunked Prefill)设定了一个单步计算预算(原稿示例为每步 2K Token)。面对一个 8K 的长 Prompt,引擎并不一次性算完,而是将其切分成 4 个连续的 2K 切片(0-2047、2048-4095、4096-6143、6144-8191),分散到 4 次前向传播中逐步处理。在执行每个 2K 切片的同时,调度器都会把现有活跃用户的单个 Decode Token 混合打包进去。这样既让超长 Prompt 平稳推进预填充,又保证了活跃用户在每一步都能继续获得解码 Token。


Agent 时代的洪峰:膨胀三角形与 Prefix Caching

当大模型的使用场景从普通对话迈向自主 Agent,底层的流量模式发生了剧烈倾斜。

在典型的 Agent 执行循环中,人类给出一个目标,模型首先思考并输出一段工具调用命令;外部系统(搜索接口、Python 解释器、内部 API)执行该工具,并将长篇的运行结果追加到对话记录中;随后模型再次读取全部历史,决定下一步动作。这个过程会循环往复数十次。

对于推理引擎而言,Agent 的每一次迭代都不是增量更新,而是一次重新发送全部历史记录的全新请求。

Agent 每一轮将全部历史作为新请求重新发送形成的膨胀三角形

引擎在底层面对的是一个形态奇特的“膨胀三角形”:输入提示词每一轮都在剧烈膨胀(系统指令 + 几十个工具描述 + 越来越长的历史轨迹),而模型每一次吐出的内容却极短(往往仅有几十个 Token 的工具调用 JSON)。

如果按照传统的无缓存逻辑,每一轮 Agent 循环都要把前几十轮产生的所有文字当成全新的 Prompt 从头跑一遍完整的 Prefill 计算。

为了量化这种开销,原稿第 17 页给出了前缀缓存效果的示意计算模型:

原稿第 17 页示意模型:图中 10 轮下无缓存(55 个单位)与前缀缓存(10 个单位)的 Prefill 计算量对比

需要主动提醒读者的是,该页幻灯片标题虽然写着“across a 100-turn loop”,但图中的横坐标终点明确标为 TURN 10。图中的 55 与 10,实际对应的是图中 10 轮、假设每轮新增尾部等长为 1 个示意单位的模型:无缓存时 10 轮累加为 1 + 2 + … + 10 = 55 个计算单位,缓存时则是 10 轮各处理 1 份新增尾部,共 10 个单位。因此,55 与 10 只是按图中 10 轮、每轮新增尾部等长推演的示意单位,而非 100 轮的实测或总量。在无前缀缓存时,每一轮都在重复处理上一轮的全部历史。

交互 · 示意模型

拖动轮数,看重复 Prefill 如何堆积

假设每轮 Agent 只追加等长的 1 个新尾部单位。无缓存时,每轮都重算整段历史;命中前缀缓存时,只处理新增尾部。

无前缀缓存

55 个 Prefill 单位

1 + 2 + … + 10 = 55

命中前缀缓存

10 个 Prefill 单位

10 × 1 = 10

在这个示意模型中,10 轮时无缓存多处理 45 个单位。

口径说明:原稿该页标题写“100-turn”,图轴和 55/10 的数学关系却对应 10 轮。本交互按图中 10 轮的示意公式展示,不代表实测速度、费用或缓存命中率。

破解这一死循环的武器是前缀缓存(Prefix Caching)。

Prefix Caching 通过哈希比对复用共享物理显存块

由于底层显存已经通过 PagedAttention 实现了分页存储,引擎可以对每个已经算过的物理块内容计算哈希值。无论是同一用户的多轮 Agent 迭代,还是不同用户共享的相同系统提示词和工具定义,只要前缀文本内容一致,新请求直接通过映射表指向已经存在于显存中的物理块 0 到 3。原稿明确强调,这项能力在底层完全透明,使用者无需在调用接口时做任何特殊标记。

引入 Prefix Caching 之后,每一轮循环只需针对本轮新追加的极短尾部执行 Prefill,历史长文全部命中缓存。按照图中 10 轮、每轮新增尾部等长的示意单位,10 轮的预填充总计算量是 10 个单位,相比无缓存时的 55 个单位大幅减少,而且原稿指出每一轮的首个 Token 也会更快到来。


流量特征与系统治理的底层映射

为了让读者完整看清 Agent 应用层行为与推理引擎工程手段之间的对应逻辑,原稿在第 18 页将常见的 Agent 流量特征与系统应对机制整理成了清晰的映射体系:

Agent 典型流量特征与推理引擎底层治理技术的映射矩阵

跨轮次与跨用户的高度共享前缀:Agent 繁重的系统人设、数十个工具的原型描述以及早期对话记录在多次调用中完全一致,引擎依靠 Prefix Caching 实现块级复用,免去重复计算。

并发工具调用与子 Agent 突发脉冲:当 Agent 并发派生多个子任务或同时发起多个搜索请求时,瞬时并发量剧增,引擎依靠 Continuous Batching 动态填补空出的批次位置。

极长的输入上下文与极短的生成输出:针对输入数万 Token 却只吐出几十 Token 工具调用的极度不对称负载,引擎通过 Chunked Prefill 打碎长输入,并在物理集群层面将专门负责大算力吞吐的 Prefill 节点与专门负责高显存带宽流式读取的 Decode 节点进行解耦拆分。

工具执行期间的长时间静默:Agent 调用代码执行或外部检索时,可能需要等待数秒甚至数分钟,期间对应的对话通道完全没有任何生成行为。如果任由其 KV Cache 锁死在昂贵的 GPU 显存中,系统容量会被迅速吞噬。引擎通过 KV Cache 换出机制(Offloading),在等待静默期将这部分数据暂存至相对廉价的海量 CPU 内存中,待工具返回并触发下一轮交互时再迅速恢复换入。


总结:回归系统常识的四条底层认知

从单卡硬件到分布式推理集群,复杂的优化技术最终收敛于四个朴素的物理事实:

第一,一次前向计算只吐出一个 Token。模型接收的计算表示是 Token ID,而不是原始文字;物理底层执行的是词表空间中的概率计算与单步采样。

第二,Prefill 与 Decode 是性质完全不同的两种计算工况。前者受算力核心瓶颈限制,决定首字等待时长;后者受显存带宽瓶颈限制,决定每秒吐字速率。一切吞吐优化的首要前提是看清当前所处的物理瓶颈。

第三,推理引擎是一台极致的硬件饱和机器。Continuous Batching 用动态补位填满了每个计算步的空泡,PagedAttention 用虚拟内存机制撕碎了显存预留的浪费,系统工程的一切目标就是让昂贵的核心永不停转。

第四,Agent 的运行效率深度依赖缓存策略。在极长输入与多次重放的多轮循环中,保持 Prompt 前缀结构的确定性与稳定性,是应用层开发者能够给予底层推理引擎最大的工程善意。

来源
How Inference Engines Actually WorkZain Hasan · Together AI·查看主材料
本站说明
Open Source AI Summit 2026 演示文稿