Hang Zhengyang
English ↗

LLM 推理流程(Inference Chain)

LLM 处理一个 request 的推理流程,可以理解成:用户文本进入系统后,被切成 token,经过模型一层层计算,最后逐 token 生成答案。核心不是“一次性写完整段回答”,而是“每次预测下一个 token”。

用户发来一句话,比如:

"Explain how LLM inference works."

系统首先会把它和系统提示词、开发者提示词、历史对话、工具返回内容等拼成一个完整上下文。模型真正看到的不是单独这一句话,而是一个完整 prompt,例如:

系统规则 + 对话历史 + 用户当前问题

然后进入 tokenizer。tokenizer 会把字符串切成 token,并映射成整数 ID。例如 “inference” 可能是一个 token,也可能被切成几个子词。模型本身不直接理解字符,它只处理 token ID 序列。

接着进入 embedding 层。每个 token ID 会被查表变成一个向量,比如维度是 4096、8192 或更大。此时一句话就从整数序列变成了矩阵:

序列长度 × hidden dimension

例如 1000 个 token,每个 token 4096 维,就是一个 1000 × 4096 的矩阵。

然后加入位置信息。因为 Transformer 本身不知道 token 的顺序,所以要用 positional encoding。现代 LLM 常用 RoPE,也就是 rotary positional embedding。它不是简单加一个位置向量,而是在 attention 里的 query/key 上做旋转变换,让模型知道 token 之间的相对位置。

之后进入 Transformer blocks。一个大模型可能有几十层甚至上百层。每一层大致包括:

  • attention
  • MLP / feed-forward network
  • residual connection
  • normalization

最重要的是 attention。它的作用是让当前 token 从上下文中“取信息”。每个 token 会生成三个向量:Query、Key、Value。

  • Query 可以理解成:“我现在需要找什么信息?”
  • Key 可以理解成:“我这里有什么信息可以被别人匹配?”
  • Value 可以理解成:“如果别人关注我,我提供什么内容?”

模型会计算 Query 和所有 Key 的相似度,得到 attention score。相似度越高,说明当前 token 越应该关注那个位置。然后对 score 做 softmax,变成权重,再用这些权重加权 Value,得到新的 token 表示。

举个例子,句子是:

"The capital of France is"

当模型预测下一个 token 时,当前位置可能会重点关注 “France”,因为它和“capital”一起决定答案更可能是 “Paris”。

Attention 不是单头,而是 multi-head attention。不同 head 可以学习不同关系:有的关注语法关系,有的关注实体关系,有的关注长距离依赖,有的关注代码括号匹配。所有 head 的输出拼接后再经过线性变换。

在推理时还有一个关键限制:causal mask。LLM 是自回归模型,预测第 t 个 token 时只能看前面的 token,不能偷看未来 token。所以 attention 矩阵会被 mask 掉未来位置。这也是为什么它能用于“下一个 token 预测”。

Attention 后面是 MLP,也叫 feed-forward network。它通常比 attention 占更多参数。Attention 更像是在上下文中搬运和组合信息,MLP 更像是对每个 token 的内部表示做非线性变换,存储大量模式、事实和抽象特征。现代结构常用 SwiGLU / GeGLU 这类激活函数。

每层还有 residual connection。也就是输出不是完全替换原向量,而是:

新表示 = 原表示 + 子层输出

这样深层模型训练和推理更稳定,也能保留早期信息。

Normalization,比如 RMSNorm 或 LayerNorm,用来稳定数值尺度,避免一层层计算后向量爆炸或退化。

经过所有 Transformer 层后,最后一个 token 的 hidden state 会进入 lm head。lm head 通常是一个线性层,把 hidden dimension 映射到词表大小。

例如 hidden dimension 是 4096,词表大小是 100k,那么 lm head 会输出 100k 个数字,每个数字对应一个 token 的 logit。logit 还不是概率,只是原始分数。

接着对 logits 做 sampling 处理。最简单是 greedy decoding:选分数最高的 token。但实际聊天模型通常不会每次都选最高,因为那样回答会很死板。常见参数包括:

  • temperature:控制随机性。越高越随机,越低越确定。
  • top-k:只从分数最高的 k 个 token 里选。
  • top-p:只从累计概率达到 p 的候选 token 里选。
  • repetition penalty:降低重复 token 的概率。
  • stop sequence:遇到特定 token 或模式就停止。

假设模型选出了下一个 token:“Paris”。这个 token 会被追加到上下文后面。然后模型继续预测下一个 token。流程变成:

  • 输入:The capital of France is Paris
  • 输出:下一个 token

如此循环,直到生成结束符、达到最大长度、遇到停止条件,或者系统中断。

KV cache 与两阶段推理

这里有一个非常重要的工程优化:KV cache。

如果没有 KV cache,每生成一个新 token,模型都要重新计算整个上下文的 attention。比如上下文 5000 token,生成第 1 个 token 算一次 5000,生成第 2 个又重新算 5001,成本非常高。

KV cache 的做法是:在第一次处理 prompt 时,把每一层每个 token 的 Key 和 Value 缓存下来。之后每生成一个新 token,只需要计算新 token 的 Query、Key、Value,然后让新 token 的 Query 去关注历史缓存里的 Key/Value。

所以推理分成两个阶段:

  • 第一阶段:prefill
  • 第二阶段:decode

Prefill 是处理用户输入的整段 prompt。比如 prompt 有 3000 token,模型可以并行处理这 3000 个 token,计算所有层的 hidden states,并把 KV cache 建好。Prefill 的特点是计算量大,但并行度高。

Decode 是逐 token 生成回答。每次只生成一个 token,依赖上一步结果,所以不能完全并行。Decode 的特点是延迟敏感,KV cache 读写压力大,显存带宽很关键。

这也是为什么 LLM 服务性能常看两个指标:

  • TTFT:time to first token,也就是用户等多久看到第一个字。它主要受 prefill 影响。
  • TPOT:time per output token,也就是每生成一个 token 要多久。它主要受 decode 影响。

在真实服务中,一个 request 不会单独霸占 GPU。推理引擎会做 batching,把多个用户请求合并到一起跑。难点是每个请求长度不同、生成速度不同、结束时间不同,所以需要 continuous batching。也就是动态地把新请求插入 batch,把完成的请求移除,尽量让 GPU 不空转。

还有一个关键瓶颈是 KV cache 显存。每个 request 的历史 token 都要在每层存 Key/Value。上下文越长、并发越高,KV cache 越大。对于长上下文模型,KV cache 往往比参数本身更难管理。因此推理系统会做 paged attention,把 KV cache 像操作系统内存分页一样管理,避免显存碎片,提高并发能力。

如果模型太大,一张 GPU 放不下,还要做模型并行。常见有:

  • tensor parallel:把一个矩阵乘法切到多张 GPU 上。
  • pipeline parallel:把不同层放在不同 GPU 上。
  • expert parallel:用于 MoE 模型,把不同 expert 分到不同设备上。

对于 MoE 模型,推理时不是所有参数都激活。每个 token 会经过 router,选择少数几个 expert。比如总共有 64 个 expert,但每个 token 只用 top-2 expert。这样模型总参数很大,但单 token 实际计算量相对可控。代价是路由、负载均衡和跨卡通信更复杂。

从用户角度看,模型像是在“思考后回答”。但从计算角度看,它每一步只是在做:

当前上下文 → logits → 采样一个 token → 追加 token → 重复

模型没有一次性规划完整答案的显式数据结构,除非外部系统让它先生成计划。它的“连贯性”来自训练中学到的分布,以及当前上下文不断累积的约束。

工具调用也是类似流程。模型不是直接执行工具,而是生成一个特殊格式的输出,表示它想调用某个工具。系统检测到这个输出后,暂停文本生成,执行工具,把工具结果重新塞回上下文,然后模型继续基于新上下文生成答案。

RAG 也是类似。系统先用用户问题去检索文档,把相关片段加入 prompt。模型本身并没有访问数据库的能力,它只是看到“上下文里多了几段资料”,然后基于这些资料回答。

完整流程总结

所以完整 request 流程可以总结成:

  1. 用户输入进入服务端
  2. 系统组装 prompt
  3. tokenizer 转成 token ID
  4. embedding 转成向量
  5. prefill 阶段处理完整 prompt,并建立 KV cache
  6. 每层 Transformer 计算 attention 和 MLP
  7. 最后 lm head 输出词表 logits
  8. 通过 temperature、top-p 等策略采样下一个 token
  9. 把新 token 追加到上下文和 KV cache
  10. decode 阶段重复逐 token 生成
  11. 遇到停止条件后 detokenize 成文本返回用户

最核心的一句话是:LLM 推理不是搜索数据库,也不是执行程序,而是在 Transformer 网络中反复计算“下一个 token 的概率分布”,并通过 KV cache、batching、并行和采样策略,把这个过程做得足够快、足够稳定、足够像人在回答。