大模型 Token 科普:什么是 Token?为什么输出 Token 更贵?

很多人第一次看大模型 API 账单,会有两个困惑:

  • 同样是 Token,为什么“模型生成的输出”(completion)经常更贵?
  • 明明提示词写得很“稳定”,为什么有时成本和延迟会突然飙升?

这篇文章用科普视角把 Token、分词器切分、以及“为什么输出 token 更贵”的计算原因讲清楚。

说明一下术语:很多厂商把“输入”叫 prompt(提示词/请求内容),把“模型生成的输出”叫 completion(对输入的续写/补全)。

说明:本文内容整理自笔记材料。


摘要(先看结论)

  • Token 是大模型处理文本的最小计费/计算单位,本质是“分词器把字符串切成片段后映射到词表 id”的结果。
  • 预填充(prefill,处理输入)可以在序列维度高度并行;解码(decode,生成输出)必须自回归逐 token 生成,GPU 利用率更难拉满,所以输出通常更贵。
    • 预填充可以理解成“先把题目读完”:模型一次性处理整段输入的所有 token,并为每个位置做计算;因为输入 token 都已知,很多计算可以并行铺开做。
    • 解码可以理解成“开始写答案”:模型每次只生成 1 个新 token,下一步要基于“刚生成的 token + 之前所有内容”继续生成,所以步骤之间有严格先后顺序,天然更串行。
    • 结果就是:同样 1000 个 token,输入侧更像一块并行计算;输出侧更像 1000 次连续的小步计算,吞吐更难做高,价格往往更高。
  • 一些平台会做“前缀缓存”(prompt prefix cache):当输入前缀完全一致时,可复用部分计算结果,主要影响延迟与成本结构的波动。

快速导航(按问题定位)

你看到的现象 优先看哪节 一句话解释
输出 token 比输入 token 贵 为什么输出更贵、Token 计费怎么来的 并行度不同导致成本结构不同
同样模板,偶尔突然变慢/变贵 前缀缓存命中机制 前缀变了就要重算,等同“冷启动”
语义看起来一样,却没命中缓存 Tokenizer 怎么切、前缀缓存命中机制 缓存看的是字节级一致,不看语义
账单主要被输出占据 为什么输出更贵、Token 计费怎么来的 长回答场景,成本大头常在输出段

Token 是什么:别把它当“字数”

大模型只能处理数字序列,不懂“文字”。所以需要一个翻译官:Tokenizer(分词器)。

一条典型流水线是:

文本 → Tokenizer 编码 → Token id 序列(数字) → 模型计算 → Token id → Tokenizer 解码 → 文本

用一张 ASCII 图把“谁在干什么”画清楚:

         (字符串)                         (数字)                       (字符串)
┌───────────────┐   encode    ┌────────────────────────┐   decode   ┌───────────────┐
│ 输入文本(prompt)│ ─────────► │  token id 序列 [..,..] │ ─────────► │   输出文本 text │
└───────────────┘            └────────────────────────┘            └───────────────┘
           │                               ▲   │                               ▲
           │                               │   │                               │
           ▼                               │   ▼                               │
   ┌─────────────┐                         │ ┌─────────────┐                   │
   │ Tokenizer    │                         │ │   大模型     │                   │
   └─────────────┘                         │ └─────────────┘                   │
                                           │
                                           └──(模型只“认识”数字序列)

这里有两个关键点:

  • Token 不等于“字”也不等于“词”,可能是一个字、一个词、甚至一个子词片段(例如 debug 可能被切成 de + bug 这样的子词)。
  • 你在 API 里看到的输入/输出 token 计数,取决于“分词器如何切分”,不是你肉眼看到的字符数。

可以用一个很直觉的类比理解“子词 token”:有些单独拿出来你不认识,但把它放进一个常见词语里,你马上就认识了。

对应到 Token:

  • 单个 token 可能只是“词的一部分”,单看不一定有意义
  • 但多个 token 拼在一起,就组成了你熟悉的词语/句子
类比(直觉示意,不代表真实分词结果):

单独看:  [醐]  [俎]  [阖]  [缪]  (单看不一定认识/不会读)
放进词语: 醍醐灌顶  越俎代庖  纵横捭阖  未雨绸缪  (放进词语里更容易“认出来”)

对应到 token:
token1 + token2  →  一个你“认识”的词

Tokenizer 怎么切:不懂语义,只做统计切分

分词器通常基于大规模语料训练,目标很朴素:

  • 高频共现的片段更容易被合并成一个 token
  • 低频、混杂符号、罕见组合更容易被拆成多个 token

理解上需要记住两件事:

  1. 对“整段输入字符串”,切分结果是稳定的:同一个模型/同一个 tokenizer、同一段字符串输入,切分结果一致。
  2. 但你在讨论“某个词/短语”时,它在不同上下文里左右邻接字符可能不同(空格、标点、换行、全角半角、Unicode 归一化等),导致它在整段文本中的切分边界看起来不一样。

举个中文例子(示意切分,用来帮助理解“边界来自上下文”,不代表任何具体 tokenizer 的真实输出):

句子 A:我喜欢人工智能
可能的 token:["我", "喜欢", "人工智能"]

句子 B:我喜欢 人工智能
可能的 token:["我", "喜欢", "␠人工", "智能"]

说明:
- 这里的 ␠ 表示“空格字符”。很多 tokenizer 会把“前导空格”一起并进 token 里。
- 所以你肉眼看到的“人工智能”看起来没变,但在不同上下文里,它周围的字符(有没有空格)变了,
  token 的边界就可能变,token 数也可能变。

这也直接影响后面要讲的缓存命中:缓存通常按“前缀是否完全一致”判断,而不是按语义相似判断。


为什么输出更贵:并行 vs 串行

把推理过程粗略拆成两段会更好理解:

输入阶段:预填充(Prefill)

输入 token 在 Transformer 的注意力计算中,可以在序列长度维度高度并行(尽管仍然消耗显存与算力)。因此在同等 token 数下,吞吐更好,单位 token 成本更低。

输出阶段:解码(Decode)

输出 token 的生成是自回归的:

  • 生成第 101 个 token 之前,必须先得到前 100 个 token 的结果
  • 每一步都要做一次前向计算(并更新/读取缓存)

这导致:

  • 计算链路更难并行,GPU 更容易“吃不满”
  • 同样数量的 token,输出段更贵、更慢

用一张图直观看“并行”和“串行”的差别:

时间轴  ─────────────────────────────────────────────────────────────►

Prefill(处理输入:更容易并行)
输入 tokens:  t1  t2  t3  ...  tn
计算方式:   [██████████████████████████████████████████████████████]   (一大块并行算完)

Decode(生成输出:必须串行)
输出 tokens:  y1  y2  y3  ...  ym
计算方式:    [██]→[██]→[██]→ ... →[██]   (每一步都依赖上一步的结果)

Token 计费怎么来的:输入/输出分开计价

很多厂商按“输入 token + 输出 token”分别计费。以笔记中举的 DeepSeek 例子(仅作机制示意):

计费类型 定价逻辑 技术原因 典型价格(DeepSeek 示例)
输入 Token 价格较低 可并行处理,GPU 资源利用率高 2 元 / 百万 token
输出 Token 价格较高 串行生成,推理资源消耗更大 3 元 / 百万 token

你可以用一个最简单的成本公式在心里做估算:

成本 ≈ 输入token数 × 输入单价 + 输出token数 × 输出单价

从这个计费结构可以得到两个直觉结论:

  • “提示词很长但输出很短”的任务,成本更接近输入段
  • “输出很长(写文章/长代码/长推理)”的任务,账单主要由输出段决定

前缀缓存命中机制:前缀一样就省钱,不一样就重算

这里的“缓存”,讨论的是很多厂商在服务端做的“前缀缓存命中”(也常被称为 prompt cache / prefix cache)。

先用一张图把“前缀缓存到底缓存哪一段”说清楚:

一次请求的输入(prompt)通常可以拆成两段:

┌─────────────────────────── 固定前缀(应尽量稳定) ───────────────────────────┐┌──────── 动态后缀(每次变化) ────────┐
│ system prompt / 角色设定 / 输出格式约束 / 工具描述 / 固定示例 / 长模板 ...     ││ user question / 实时数据 / 变量 ...  │
└──────────────────────────────────────────────────────────────────────────────┘└───────────────────────────────────────┘

缓存命中:固定前缀“字节级完全一致” → 复用这段的中间计算结果 → 省时/省钱(视平台而定)
缓存不命中:固定前缀有任何差异(空格/换行/标点) → 这段需要重新计算 → 变慢/变贵

命中是什么意思?

当你的输入文本存在一段“稳定不变的前缀”(典型就是系统提示词、固定指令模板、固定工具描述等),并且这段前缀与历史请求完全一致时:

  • 服务端可以复用这段前缀对应的中间计算结果
  • 减少重复计算,降低延迟
  • 部分平台会对“已缓存 token”给出折扣计费或更低的计费档位

什么情况下会不命中?

只要前缀发生变化(哪怕只是一处标点/空格/换行不同),就需要重新计算整段前缀:

  • 延迟上升
  • 成本上升
  • 在“固定前缀很长”的场景下,变化一次就可能带来明显的波动

笔记中的提醒很直白:缓存未命中可能导致价格“直接飙升”。把它理解成软件系统里的“冷启动”就好。


Token 量级怎么估:一个实用类比

笔记里给了一个很好用的类比:

  • 100 万 token ≈ 3 本《三体》的文本量

你可以把它当作“容量单位”来做粗估:

  • 如果一次对话要塞入很多长文档,先问自己:是不是已经接近“几本书”的量级?
  • 如果每次都在系统提示词里塞一大段规范,缓存命中与否会对成本影响非常明显

常见误区(很容易踩)

  • 误区 1:以为 Token 就是字数/字符数
    实际由 tokenizer 决定,不同语言/符号密度差异很大。

  • 误区 2:以为“语义一样就能缓存命中”
    多数前缀缓存要求字节级一致;差一个空格也可能不命中。

  • 误区 3:只盯着输入段
    写长答案时输出段常是成本大头。


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐