从输入到输出:大模型推理过程与缓存计费详解

🔔 核心误解速查表(来自真实提问)

❌ 常见误解 ✅ 正确理解
模型会把中文翻译成英文后再分token 模型处理的是原始UTF-8字节或直接含中文的词表,不会自动翻译
“指令”是独立于提示词的一个步骤 指令只是提示词的一部分,模型不区分对待
Token化就是传统分词 Token化可能切出子词(如”学习”分成”学””习”),比分词更细
Key/Value向量就是嵌入向量 嵌入向量是原料,K/V是从中加工出来的专用向量
预填充结果可能是随机的 预填充纯矩阵运算,完全确定
缓存命中允许微小变化(如空格) 必须从第一个Token开始完全一致,任何差异都会导致失效

一、大模型推理的正确流程(总览)

许多人对流程的理解存在偏差。下图是标准自回归模型(如GPT)的完整步骤:

1
输入提示词 → 分词 → 嵌入 → 预填充(构建KV缓存) → 自回归生成(逐Token采样) → 输出
  • 分词与嵌入:将原始文本拆分为Token,并映射为稠密向量。
  • 预填充:一次性计算所有输入Token的Key、Value向量并缓存。此阶段计算密集,但只做一次。
  • 自回归生成:逐个产生新Token,每步仅计算新Token的K/V并复用缓存。根据采样策略,输出可能确定或随机。

以下各章将按此顺序逐一详解。

二、分词与嵌入:从文字到向量

2.1 什么是Token?

  • 模型不直接认识文字,需要先将输入文本拆分为最小处理单元——Token。
  • 对于中文,一个Token可以是字、词或子词。例如”机器学习”可能分成 ["机器", "学", "习"]

❗ 常见误解:Token化就是传统的中文分词(如jieba)。
✅ 正确理解:Token化会切得更细,常常把完整词拆成常见的片段(如”学习”拆为”学”和”习”),目的是控制词汇表大小并覆盖所有字符。

2.2 模型会把中文翻译成英文再处理吗?

❗ 常见误解:听说某些模型不支持中文,会把中文先翻译成英文再分token。
✅ 正确理解:绝对不会。模型只处理你输入的原始文本的UTF-8字节或直接包含中文词表。如果词表没有中文,每个汉字会变成3个字节Token(如”中” → <0xE4> <0xB8> <0xAD>),这是字节级编码,不是翻译。

2.3 嵌入向量:赋予Token数学含义

  • 每个Token通过查一个可学习的嵌入表,得到一个固定维度的稠密向量(如768维),称为嵌入向量。
  • 相同Token永远映射到相同嵌入向量(不考虑位置编码时),因此是静态的。
  • 嵌入向量是模型的原始输入特征,携带该Token的基础语义信息。

三、预填充阶段:构建KV缓存

当整个输入序列的嵌入向量准备好后,模型进入预填充(Prefill)阶段。这一步会一次性计算所有Token的Key、Value向量并缓存,供后续生成复用。

3.1 从嵌入向量到Key/Value

  • 每个Token的嵌入向量 X 分别乘以三个可训练的权重矩阵:
    1
    Q = X · W_Q,   K = X · W_K,   V = X · W_V
  • 其中 W_K, W_V 将嵌入”加工”为专门用于注意力机制的Key向量和Value向量。

❗ 常见误解:Key/Value向量就是嵌入向量,只是换个名字。
✅ 正确理解:嵌入向量是原料,Key/Value是从原料加工出来的专用工具。嵌入用于表示Token本身,Key用于匹配注意力权重,Value用于提供加权内容。三者完全不同。

  • Key用于匹配相关度(与其他Token的Query计算注意力权重)。
  • Value用于提供内容(根据权重加权求和,得到输出)。

通俗类比:

向量类型 类比
嵌入向量 员工原始简历
Key 简历中提取的”技能标签”(用来匹配岗位需求)
Value 简历中提取的”实际项目经验”(被选中后贡献的内容)

3.2 KV缓存:为什么能省计算?

  • 在自注意力中,每个Token都需要所有Token的Key和Value。如果每次生成新Token都重新计算整个序列的K/V,成本极高。
  • 预填充阶段一次性算好所有Token的K/V,并存入KV缓存。之后每生成一个新Token,只需计算该Token自己的K/V,追加到缓存中即可。
  • 相同输入的前缀,其缓存可直接复用——这就是缓存命中的基础。

3.3 确定性说明

❗ 常见误解:因为模型有随机性,所以预填充的结果也可能每次不同。
✅ 正确理解:在推理模式下(关闭Dropout等随机层),相同的输入序列 → 相同的嵌入 → 相同的K/V → 预填充结果完全确定。模型的随机性只出现在后续的生成采样阶段。

四、自回归生成阶段:逐Token采样与输出

预填充完成后,模型进入自回归生成阶段,逐个产生新的Token。

4.1 每一步做什么?

  1. 利用已有的KV缓存(包含输入序列及之前已生成的所有Token),计算下一个Token的概率分布。
  2. 根据采样策略(如温度采样、Top-K、Top-P)随机或确定地选出一个Token。
  3. 将该Token追加到序列末尾,计算其K/V并加入缓存。
  4. 重复直到遇到结束符或达到最大长度。

4.2 随机性的来源

  • 如果使用贪心搜索(每次选概率最高的Token),输出完全固定。
  • 如果使用随机采样(多数API默认),则相同输入可能产生不同输出——这正是常见”模型不听话”的真正原因。

❗ 常见误解:相同输入得到不同输出,是因为模型内部有”随机种子”在预填充阶段就起作用。
✅ 正确理解:随机性只发生在生成阶段的采样。预填充阶段没有任何随机操作。你可以通过设置 temperature=0 或使用贪婪解码来获得完全确定性的输出。

4.3 输出

  • 将生成的Token序列通过解码器转换回文本,返回给用户。

4.4 思考型模型:思考过程在哪里?

❓ 常见问题:像 DeepSeek-R1、OpenAI o1 等模型,它们展示的”思考过程”属于流程中的哪个阶段?
✅ 简明答案:思考过程完全属于自回归生成阶段,不是独立阶段。

  • 模型在生成最终答案之前,先通过逐Token采样生成一连串表示”思考”的文本(例如”首先,我们需要…”、”因为…所以…”)。
  • 这些思考Token和普通输出一样,是利用KV缓存逐步生成的,每一步只计算新Token的K/V。
  • 思考过程也具有随机性(如果使用采样)或确定性(如果 temperature=0)。

换句话说:思考过程 = 自回归生成早期产生的一批特殊Token,它既不是预填充,也不是嵌入,而是生成的一部分。

五、缓存命中:省钱的秘密

5.1 什么是KV缓存命中?

  • 当你发送新请求时,API服务会检查你的提示词前缀是否与之前某个请求的前缀Token序列完全一致。若是,则直接复用已缓存的K/V,无需重新计算预填充。
  • 计费上:缓存命中的输入Token价格通常仅为未命中的10%~20%。

5.2 命中的硬性条件

  • 前缀必须从第一个Token开始连续完全相同。哪怕多一个空格、一个换行,或改变顺序,都会导致缓存失效。

❗ 常见误解:只要提示词大致相同,比如改了一个字或加了一个空格,应该还能命中大部分缓存吧?
✅ 正确理解:绝对不能。缓存匹配是严格的字节/Token级别相等。一个空格、一个标点、甚至繁体与简体的差异,都会导致从差异位置开始后续全部无法命中。

示例:

  • 请求A:System: 你是个助手 User: 北京天气
  • 请求B:System: 你是个助手 User: 上海天气
  • 命中 System: 你是个助手 User: 这一连续前缀(前提是长度足够触发折扣)。

5.3 缓存为什么会失效?

  • 时间过期(TTL):每个缓存块有存活时间,通常5~60分钟。每次命中间隔不超过TTL则持续有效,否则被清除。
  • 空间淘汰(LRU):缓存容量有限,长期未命中的旧缓存会被新请求挤掉。
  • 内容变更:任何前缀修改(哪怕是一个字、一个标点)都会使原缓存不可用。

5.4 最佳实践:静态在前,动态在后

  • 错误的模式:[动态][静态][动态] → 第一个Token不同,后续静态完全无法命中。
  • 正确的模式:将所有静态内容连续放在最开头,所有动态内容放在末尾。

❗ 常见误解:可以静态和动态交替放置,只要后面有连续静态就能命中那一段。
✅ 正确理解:不能。因为缓存匹配必须从第一个Token连续一致。一旦第一个位置是动态,整个前缀就断裂了,后面的任何静态都无法被命中。只有把所有静态放在最前面形成连续前缀才能缓存。

示例:

  • ❌ 错误:[用户问题] [系统提示词] [随机ID]
  • ✅ 正确:[系统提示词] [历史对话] [当前用户问题]

黄金法则:不变的靠前放,多变的放末尾。

六、确定性与随机性总结

阶段 是否确定 原因
预填充 确定 无随机操作,纯矩阵运算
生成(贪心) 确定 每次选最高概率Token
生成(采样) 随机 按概率分布随机抽取

若需要完全可复现的结果,可以设置随机种子或使用贪心解码。

结语

理解大模型内部的推理流程,不仅能帮你写出更高效的代码,还能在日常API调用中大幅降低成本。记住两条核心要点:

  1. 静态内容永远放在最前面,利用缓存命中节省80%+输入费用。
  2. 生成随机性源自采样,并非模型”出错”。