LLM 推理是系统设计与 AI 工程交汇之地:随着模型的不断升级,开源模型和商业模型的效率在不断逼近,2026 年开始,LLM 推理将进入新的阶段,开始从模型训练转向模型推理,为什么?数据统计模型的训练算力只占全球的 30%,而推理占 70%,这个比例将会继续增加,说明现在的开源模型已经大部分场景够用,最大的问题是如何将模型普及并让成本更低。
为此从去年的模型训练的一些总结,逐步转向模型推理的工程实践,最近一段时间也看了一些文章和书籍,于是我决定写一本手册,来介绍 LLM 推理的工程实践,预计分为 12 篇整体介绍推理,包括Prefill-and-Decode、KV cache、quantization、PagedAttention、FlashAttention、speculative decoding、parallelism strategies、vLLM、TensorRT-LLM 和 SGLang等。

前言
每次你向 ChatGPT、Claude 或 Gemini 发送消息时,几毫秒之内就会发生一件了不起的事。在某个数据中心里,一个拥有数十亿参数的模型被唤醒,读取你的文字,然后开始生成响应------一次一个 token,其成本在五年前看来简直不可思议。
构建 LLM 需要研究者的智慧来搭建正确的架构:在哪里使用 Attention,在哪里使用 Mamba 层,如果构建混合模型还需考虑其他层的选择。应该创建 MoE(Mixture of Experts)模型,还是让所有权重都处于激活状态?这些都属于研究层面的决策。
而模型构建完成后的 GPU 集群部署与运维------让它能够服务真实用户请求------则属于工程范畴,因此,LLM 推理与其说是研究,不如说是一门工程艺术。
在"推理"这个词的背后,存在着一个完整的工程学科------它决定了你的 AI 产品是快还是慢,是便宜还是昂贵,是可扩展还是脆弱,推理做得好不好,决定了你的产品只是一个令人惊艳的 demo,还是一个真正能存活的产品。
训练模型只发生一次,推理每天发生数十亿次,每一次低效都会累积叠加,每一个浪费的 GPU 周期都在烧钱,每多一毫秒延迟都在流失用户,这就是为什么世界上最顶尖的 AI 团队------Google、Meta、Anthropic、Mistral------都设有完整的工程组织,专门致力于让推理更快、更便宜、更可扩展。
什么是 prefill 和 decode?
当前最先进的模型是基于 decoder 的自回归模型(autoregressive model),这意味着它们基于之前的 token 来预测下一个 token,在预训练阶段,模型学习的就是基于前文 token 预测下一个 token。
Token 是大语言模型(LLM)处理和理解的基本文本或数据单元,模型并不逐个读取字母,而是将文本切分为更小的块,称为 token,一个 token 可以是一个完整的单词、单词的一部分(subword),或者单个标点符号。
当我们向 LLM 发送 prompt 时,模型首先在一次前向传播(forward pass)中处理整个 prompt,这个阶段称为 prefill ,读取完整 prompt 后,模型预测第一个输出 token,从发送请求到接收到第一个生成 token 的时间称为 TTFT (Time to First Token)。
此后,模型进入 decode 阶段,它一次生成一个 token,第一个生成的 token 成为 context 的一部分,然后模型预测第二个 token,第一个和第二个 token 成为 context 的一部分,然后模型预测第三个 token,以此类推,这个过程持续进行,直到模型达到 max_tokens 限制或生成 EOS(End of Sequence,序列结束符)。
只有经过 Prefill 和 Decode 这两个过程之后,我们才能从 LLM 得到最终的响应。
示例
Prompt:
The capital of France
模型首先读取完整 prompt 并预测:
csharp
is
此时 context 变为:
csharp
The capital of France is
模型再次读取完整 context 并预测下一个 token:
Paris
context 变为:
csharp
The capital of France is Paris
然后模型预测下一个 token:
erlang
.
接着预测:
xml
<EOS>
最终输出为:
csharp
The capital of France is Paris.
现在,对于每一个 token,模型中的 attention 层都会计算 K 和 V 向量。
Attention 简明示例:Query、Key 和 Value

在深入之前,让我们快速看一个 attention 如何工作的例子,以及 Query(Q)、Key(K)和 Value(V)向量分别是什么。
在模型预测下一个 token 之前,每个 attention 层为每个 token 创建三个向量:
- Query(Q) :这个 token 在寻找什么?
- Key(K) :这个 token 包含什么信息?
- Value(V) :这个 token 应该向前传递什么信息?
让我们用一个很小的 prompt:
css
I love AI
假设模型为每个 token 创建如下简单的 Key 和 Value 向量:
| Token | Key Vector K | Value Vector V |
|---|---|---|
| I | 1, 0 | 1, 0 |
| love | 0, 1 | 0, 2 |
| AI | 1, 1 | 3, 1 |
现在,假设当前 token 是 AI,其 Query 向量为:
ini
Q_AI = [1, 1]
Attention 层使用点积将该 Query 向量与每个 token 的 Key 向量进行比较。
css
Q × K^T,其中 K^T 表示 K 向量的转置
Q_AI · K_I = [1, 1] · [1, 0] = 1
Q_AI · K_love = [1, 1] · [0, 1] = 1
Q_AI · K_AI = [1, 1] · [1, 1] = 2
原始 attention 分数为:
css
I → 1
love → 1
AI → 2
这些分数经过 softmax 函数转换为概率:
css
I → 0.21
love → 0.21
AI → 0.58
这意味着 token AI 在分配 attention:
shell
21% 的 attention 给 "I"
21% 的 attention 给 "love"
58% 的 attention 给 "AI"
现在模型使用这些 attention 权重来混合 Value 向量:
ini
Attention Output =
0.21 × [1, 0] +
0.21 × [0, 2] +
0.58 × [3, 1]
最终输出约为:
css
Attention Output ≈ [1.95, 1.00]
这个最终向量就是 token AI 的 attention 输出。
简单来说,attention 允许模型查看所有相关 token,决定每个 token 的重要性,然后组合它们的信息。
因此 attention 可以理解为:
vbnet
将 Query 与 Key 进行比较 → 获得 attention 权重 → 混合 Value
这些混合后的信息随后在模型的下一层中继续传递,最终帮助预测下一个 token。
Decode 阶段的重复计算问题

Attention 会为 context 中的每个 token 计算 K 和 V 向量。
现在想象在 decode 阶段,如你所知,我们不断将输出 token 添加到 context 中,因此对于每个新的输出 token,我们需要重新计算 context 中所有先前 token 的 K 和 V 向量。
继续用上面的例子,假设我们给模型这个 prompt:
csharp
The capital of France is
假设这个 prompt 有 5 个 token:
css
[The] [capital] [of] [France] [is]
在第一次前向传播(prefill 阶段)中,模型为所有 5 个输入 token 计算 K 和 V 向量:
yaml
Token 1: The → K, V
Token 2: capital → K, V
Token 3: of → K, V
Token 4: France → K, V
Token 5: is → K, V
此后,模型预测第一个输出 token:
Paris
Prefill 在此结束,Decode 现在开始。
context 变为:
csharp
The capital of France is Paris
模型会再次处理完整 context,重新计算所有 token 的 K 和 V 向量:
yaml
Token 1: The → K, V 再次计算
Token 2: capital → K, V 再次计算
Token 3: of → K, V 再次计算
Token 4: France → K, V 再次计算
Token 5: is → K, V 再次计算
Token 6: Paris → K, V 首次计算
然后模型预测下一个 token:
erlang
.
context 变为:
csharp
The capital of France is Paris.
模型会再次为完整 context 重新计算 K 和 V:
yaml
Token 1: The → K, V 再次计算
Token 2: capital → K, V 再次计算
Token 3: of → K, V 再次计算
Token 4: France → K, V 再次计算
Token 5: is → K, V 再次计算
Token 6: Paris → K, V 再次计算
Token 7: . → K, V 首次计算
并预测:
xml
<EOS>
先前 token 的 K 和 V 向量保持不变,因此在每次 decode 步骤后重复计算它们是浪费的工作。
从第一性原理的工程角度思考:这个问题应该怎么解决?
如果某个计算很昂贵,而且结果会被反复使用,我们应该怎么做?
存起来。 下一篇讲 KV cache,如何有效解决重复计算的问题。