Skip to content

05. Token、Tokenizer 与上下文窗口 ​

版本:v1.2

最后更新:2026-07-09

适用对象:准备把 LLM 应用从“会调用模型”推进到“能做成本、延迟、质量与上下文治理”的产品、研发、平台、算法与运维同学

很多 AI 系统的问题,表面上看像是:

  • Prompt 写得不够好
  • 检索结果不够准
  • 模型太贵
  • 延迟突然变慢
  • 多轮历史越来越失控

但往下深挖,经常会发现根因和下面三个概念直接相关:

  • token
  • tokenizer
  • context window

如果不真正理解它们,你很难稳定优化:

  • 成本
  • 延迟
  • 上下文质量
  • 模型路由
  • 缓存收益
  • 多轮状态治理

所以这一章不是“术语扫盲”,而是很多生产问题的共同底层。


1. 为什么这章在生产里特别重要 ​

在 Demo 阶段,团队常常只关心:

  • 模型能不能回答
  • 结果看起来像不像对

但一旦进入生产,问题会变成:

  • 一次请求到底要花多少钱
  • 为什么同一个场景延迟忽高忽低
  • 为什么多轮历史越积越贵
  • 为什么结构化输出一上工具 schema 就突然爆 token
  • 为什么换了模型,明明字数差不多,token 却变化很大
  • 为什么明明上下文窗口很大,效果还是不稳定

这些问题的共同底层几乎都绕不开 token 和上下文预算。

一句话理解:

  • Token 是模型系统里的流量计、成本表和容量约束。

2. 什么是 Token ​

最实用的理解是:

  • token 是模型处理输入和输出时使用的离散计量单位

它通常不是:

  • 一个汉字
  • 一个英文单词
  • 一个字符

更准确地说,文本、代码、JSON、表格、URL、多模态描述内容进入模型前,会先被 tokenizer 切成 token 序列,再交给模型处理。

对工程系统来说,token 最重要的意义不在定义本身,而在于它直接决定:

  • 输入成本
  • 输出成本
  • 上下文容量
  • 实际延迟
  • 是否会触发截断、裁剪或缓存策略

根据 OpenAI 当前 Token counting 官方文档,精确估算 token 的意义至少包括:

  • 在请求发出前预估成本
  • 判断是否会打满模型上下文窗口
  • 为上下文裁剪和模型路由提供依据

3. 为什么不能按字符、字数或单词数理解 token ​

这是很多新手最容易犯的第一类错误。

同样长度的两段文本,token 数可能差很多,原因通常包括:

  • 常见词会被编码成较少 token
  • 罕见词可能被拆成多个子词
  • 标点、空格、数字、URL、JSON、代码片段的切分方式不同
  • 中英混排和特殊符号会放大差异
  • 不同模型使用的 tokenizer 也可能不同

这也是为什么:

  • 看起来差不多长的中文和英文,真实 token 成本未必接近
  • 一段表格、SQL、JSON、Python 代码通常比自然语言更“烧 token”
  • 工具 schema 看着不长,拼到请求里后可能明显增重

工程上真正该记住的是:

  • 肉眼长度不是成本长度,token 长度才是成本长度。

4. 什么是 Tokenizer ​

Tokenizer 可以理解为:

  • 把原始输入转换成模型可处理 token 序列的组件

它决定的不是“文本显示长什么样”,而是模型实际看到什么切分结果。

从 Hugging Face 当前 tokenizer 官方文档视角看,它不是一个边缘组件,而是模型输入预处理的核心部分。这一点很重要,因为它提醒我们:

  • 文本送进模型前,已经先经过了一次结构变换

对工程系统来说,这意味着:

  • 估算成本时必须关心 tokenizer
  • 做 prompt 压缩时必须关心 tokenizer
  • 做长上下文治理时必须关心 tokenizer
  • 做跨模型迁移时必须重新验证 token 分布

5. 常见分词算法为什么值得知道 ​

大多数团队不需要自己实现 tokenizer,但最好知道常见思路,例如:

  • BPE
  • WordPiece
  • Unigram

知道这些算法的价值不是为了面试,而是为了理解:

  • 为什么专有名词、缩写、代码和混合语言文本会被切得更碎
  • 为什么同一段文本在不同模型上 token 数可能明显不同
  • 为什么“稍微改写一下字符串形态”就可能改变真实成本

如果系统里存在下面内容,这种差异会被进一步放大:

  • 大量代码
  • 长文档
  • 表格
  • JSON
  • URL
  • 中英混排
  • 工单号、订单号、设备号等长串标识

6. Tokenizer pipeline 不只是“切词” ​

很多 tokenizer 并不是“文本进来,直接切 token”这么简单,而是会经历多个阶段,例如:

  • 规范化
  • 预分词
  • 子词切分
  • 后处理

这也是为什么:

  • 空格、大小写、标点、换行、特殊字符都可能影响结果
  • 一样的内容换一种写法,token 数可能就变了
  • 结构化文本和自然语言之间的 token 密度常常明显不同

对生产系统而言,这提醒我们:

  • 不要把“看起来类似”当成“真实计费类似”

7. 什么是上下文窗口 ​

上下文窗口可以理解为:

  • 模型在一次请求中能够处理的总 token 容量

这里的“总容量”不是只算用户输入,而是通常要一起容纳:

  • 系统规则
  • 用户输入
  • 检索证据
  • 工具定义
  • 历史消息
  • 中间状态
  • 模型输出
  • 某些模型还包括 reasoning 相关 token

所以,上下文窗口不是“用户能输入多少字”,而是:

  • 整条请求-响应回路可用的总预算

8. 上下文窗口里到底都装了什么 ​

很多团队直到线上成本爆了才意识到,请求里真正烧 token 的远不止用户提问。

一个真实请求里常见的 token 消耗项包括:

  • instructions 或系统规则
  • few-shot 示例
  • 用户当前输入
  • 历史消息
  • 检索片段
  • 工具描述和 schema
  • 图片 / 文档等多模态输入的编码开销
  • 模型输出
  • reasoning token 或思考预算

在复杂 Agent 或 RAG 系统里,真正最重的部分常常不是用户一句话,而是:

  • 被系统自己拼进去的上下文

这也是为什么很多成本治理最终会回到一句话:

  • 先量系统自己拼了什么,再谈用户说了什么。

9. 为什么上下文窗口不是越大越好 ​

更大的上下文窗口当然有价值,但它不是免费午餐。

9.1 成本误区 ​

上下文窗口越大,不代表每次都会花满,但它很容易诱导团队:

  • 什么都往里塞

一旦这样做,成本通常不是线性“有点变贵”,而是:

  • 每个请求都被历史、证据、schema 和状态对象一起放大

9.2 延迟误区 ​

输入 token 越多,通常意味着:

  • 请求构建更慢
  • 服务端处理更重
  • 首 token 延迟和总延迟都可能上升

9.3 质量误区 ​

长上下文并不自动等于高质量。

如果你把大量低价值、重复、过期或冲突信息一起塞进去,常见结果是:

  • 关键证据被淹没
  • 检索相关性下降
  • 模型更容易引用旧信息
  • 工具选择被上下文噪声干扰

真正成熟的心智模型不是:

  • 窗口越大越强

而是:

  • 窗口越大,越需要预算治理和信息分层。

10. 输入窗口、输出窗口和 reasoning 预算最好分开理解 ​

很多团队说“这个模型有超长上下文”,但工程上真正有用的是把预算拆开看。

至少要分清:

  • 输入 token 预算
  • 输出 token 预算
  • 某些推理模型的 reasoning token 预算

根据 OpenAI 当前 Reasoning best practices 官方文档,reasoning models 的内部思考过程会消耗上下文窗口,而且 reasoning token 计入输出 token 计费。官方当前建议是在复杂任务中为 reasoning 预留足够空间,甚至可按任务预留上万 token。

这意味着:

  • 不是“用户只问一句话,所以输出预算可以随便小”
  • 复杂任务里,如果不给 reasoning 留预算,模型可能在质量和完整性上退化

更实用的理解是:

  • 你管理的不是一个窗口,而是多个争抢同一容量池的预算项。

11. 一个请求里真正会烧 token 的东西有哪些 ​

如果你们开始做生产优化,建议把一次请求拆成下面这些类别分别统计:

  1. 基础规则 token
  2. few-shot token
  3. 用户输入 token
  4. 历史消息 token
  5. 检索证据 token
  6. 工具 schema token
  7. 多模态输入 token
  8. 模型可见输出 token
  9. reasoning token

这样做的价值很大,因为你能回答:

  • 到底是产品需求在烧钱
  • 还是工程拼接方式在烧钱

很多团队以为自己成本高是因为:

  • 用户问题太长

结果拆账后才发现真相是:

  • 历史消息太长
  • 工具定义太宽
  • 检索片段塞太多
  • 思考预算没控住

12. 为什么精确 token counting 是工程刚需 ​

OpenAI 当前 Token counting 指南非常明确地把 token counting 放在请求前治理里,而不是事后统计。

你应该在请求发出前就能回答:

  • 这次预估会花多少 token
  • 是否超出窗口
  • 哪部分最重
  • 是否需要裁剪历史或检索结果
  • 是否应该换一个更便宜或更大窗口的模型

12.1 事前计数比事后记账更重要 ​

事后记账只能告诉你:

  • 钱已经花掉了

事前计数才真正帮助你:

  • 防止超窗
  • 做预算拦截
  • 做模型路由
  • 做上下文压缩

12.2 本地估算和服务端真实计费可能不完全相同 ​

这点非常重要。

即便你用官方 tokenizer 或近似工具,也要知道:

  • 最终计费仍以服务端实际处理为准

偏差可能来自:

  • 模型 tokenizer 差异
  • 请求结构差异
  • 工具定义注入方式
  • 多模态编码细节
  • 推理 token 的额外消耗

所以更稳妥的做法是:

  • 本地做请求前估算
  • 服务端 usage 做最终核账
  • 两者都留痕

13. Prompt caching 为什么已经不是“可选优化” ​

根据 OpenAI 当前 Prompt caching 官方文档,prompt caching 的本质不是“缓存整次回答”,而是:

  • 让相同前缀输入在后续请求里复用已处理成本

这对生产系统是重要杠杆,因为很多请求都共享大段稳定前缀:

  • 系统规则
  • 长工具定义
  • 大型知识模板
  • 固定 few-shot
  • 共享上下文前缀

13.1 OpenAI 当前的核心前提:前缀必须高度一致 ​

OpenAI 当前文档强调的不是“语义差不多就行”,而是:

  • 请求前缀要尽可能稳定、一致

也就是说,下面这些做法会显著影响命中率:

  • 每轮都重排工具列表
  • 每次都改系统提示词顺序
  • few-shot 示例顺序随机
  • 把可复用前缀和高频变化内容混在一起

13.2 Anthropic 也把 prompt caching 放到了显式产品能力层 ​

Anthropic 当前文档同样提供 prompt caching 能力,核心思想一致:

  • 可复用前缀值得被单独治理

这说明 prompt caching 已经不是某一家平台的偶然优化,而是:

  • 长上下文时代的通用工程杠杆

13.3 Gemini 也有上下文缓存思路 ​

Google Gemini 当前官方文档提供 context caching / prompt caching 相关能力,其工程价值同样在于:

  • 把高复用、低变化、长前缀内容从重复计算里剥离出来

今天做多模型平台时,缓存治理已经应该进入默认设计,而不是上线后补救。


14. 哪些内容最适合做缓存 ​

通常最适合缓存的是:

  • 长期稳定系统规则
  • 很少变化的工具 schema
  • 固定 few-shot 示例
  • 长期不变的模板化背景说明
  • 重复使用的大段产品知识前缀

这些内容共同特点是:

  • 长
  • 稳定
  • 多请求共享

它们既容易形成缓存收益,也最适合做版本化治理。


15. 哪些内容通常不适合做缓存 ​

通常不适合直接依赖缓存收益的内容包括:

  • 高频变化的用户输入
  • 每次都不一样的检索片段
  • 时效性强的工单、订单、日志
  • 当前会话临时状态
  • 高度个性化的短期上下文

这些内容的问题不是“绝对不能缓存”,而是:

  • 你不能把成本优化预期建立在它们稳定命中上

16. 怎样提高缓存命中 ​

如果你们已经开始做缓存治理,更实用的方法通常是:

  1. 把稳定前缀和变化内容分层
  2. 保持稳定前缀的顺序稳定
  3. 工具定义尽量版本化,不要每次动态拼不同字段
  4. 固定 few-shot 的排列和表述
  5. 不要把本轮临时状态插到稳定前缀中间

一句话总结:

  • 缓存命中靠的不是“开关”,而是前缀工程。

17. 为什么工具 schema 会成为隐形成本黑洞 ​

很多团队只有在接入大量工具后才意识到:

  • 工具不是免费能力,它们会显著增加请求体积

工具 schema、描述、参数定义、枚举字段、使用说明都会进入模型可见上下文。

这会带来几个现实问题:

  • 工具越多,请求前缀越重
  • schema 越松,说明文字越长
  • 多轮 Agent 每轮都带全量工具时,成本会被持续放大

更好的做法通常是:

  • 只暴露本轮必要工具
  • 缩短描述但保留判断边界
  • 把大工具面拆成按任务动态收缩的能力集

18. 多轮对话里最容易失控的是历史上下文 ​

很多团队一开始的做法很简单:

  • 每轮把全部历史继续追加

短期能跑,长期几乎一定失控。

失控方式通常包括:

  • 成本越来越高
  • 延迟越来越高
  • 旧信息影响新任务
  • 历史里错误摘要被持续复用
  • 关键新证据被挤到窗口后面

18.1 历史越长,不代表模型记得越好 ​

真正的问题不是“历史多不多”,而是:

  • 历史里有多少对当前任务仍然有价值的信息

18.2 摘要不是天然可靠的压缩手段 ​

很多系统会做摘要压缩,但摘要如果不可追溯,就会出现:

  • 错误事实被固化
  • 重要边界条件被压没
  • 原始证据无法回放

18.3 历史治理应该和任务类型绑定 ​

客服问答、代码 copilot、审批 Agent、RAG 工作流,对历史保留策略不应该一样。

真正成熟的做法通常是:

  • 针对不同任务单独定义历史保留、摘要和过期策略

19. RAG 系统里最容易浪费 token 的地方 ​

RAG 的 token 浪费通常不是出在 embedding,而是出在:

  • 检索召回过多
  • chunk 太长
  • 重复证据太多
  • 排序不准
  • 把整段原文机械拼进上下文

19.1 “召回越多越保险”通常是错觉 ​

召回更多片段确实可能提高覆盖,但也常常导致:

  • token 成本快速膨胀
  • 上下文噪声增多
  • 相关片段被稀释

19.2 真正值得优化的是证据密度 ​

相比“多塞几段”,更有价值的往往是:

  • 提高 chunk 质量
  • 做重排
  • 去重
  • 保留高证据密度片段
  • 控制单轮注入总预算

20. 长上下文不等于长记忆 ​

这是最容易被误解的点之一。

上下文窗口大,表示:

  • 一次请求里能容纳更多内容

它不自动表示:

  • 模型对长期状态有真正稳定记忆
  • 历史里所有关键信息都会被稳定利用
  • 你不再需要显式状态管理

更准确地说:

  • 长上下文解决的是“能放多少”
  • 长记忆解决的是“该保留什么、如何稳定调用什么”

所以即便模型支持超长窗口,你仍然需要:

  • 状态对象设计
  • 历史裁剪策略
  • 摘要与回放策略
  • 检索与记忆分层

21. 多模态输入为什么更要谨慎做预算 ​

一旦输入里包含:

  • 图片
  • PDF
  • 视频片段
  • 音频转写

token 预算问题往往会更复杂。

原因不是“多模态一定更贵”,而是:

  • 输入编码方式不同
  • 同一素材可能被转换成大量模型可见表示
  • 你更难通过肉眼直接判断真实成本

工程上更稳妥的做法是:

  • 对多模态单独做预算规则
  • 不要沿用纯文本任务的估算习惯
  • 对图片、文档和转写链路分别统计成本

22. reasoning token 为什么是 2026 年必须单独讲的成本项 ​

在推理模型上,很多团队只盯着:

  • 最终给用户看到的输出 token

这在今天已经不够了。

根据 OpenAI 当前 Reasoning best practices 文档,reasoning token:

  • 占用上下文窗口
  • 按输出 token 计费
  • 对复杂任务质量有直接影响

这带来几个工程结论:

22.1 复杂任务不能只给“用户可见回答”留预算 ​

如果任务本身需要长推理链路,你必须预留:

  • 思考空间

否则常见后果是:

  • 答案变短但质量下降
  • 工具规划不完整
  • 复杂任务中途收缩

22.2 reasoning token 也是优化对象 ​

优化 reasoning token 的常见方向不是“完全不要思考”,而是:

  • 让任务边界更清楚
  • 让证据更高质量
  • 减少无意义循环
  • 减少过度宽泛的工具面

22.3 任务级成本统计必须把 reasoning 纳入 ​

否则你会误以为:

  • 输出看起来不长,所以任务不贵

但真实计费可能已经被 reasoning 拉高。


23. 什么时候必须做 token counting ​

以下场景基本都建议做请求前 token counting:

  1. RAG 注入片段超过几个 chunk
  2. 多轮会话历史开始累积
  3. 使用大量工具或复杂 schema
  4. 使用长系统提示词或大量 few-shot
  5. 接入推理模型
  6. 存在模型路由、成本门禁或预算上限
  7. 使用多模态输入

如果系统已经进入生产,几乎可以认为:

  • token counting 不是可选项,而是标配。

24. 生产里建议单独监控这些 token 指标 ​

如果你们已经在做观测,建议至少把下面这些指标拆开看:

  • 每请求输入 token
  • 每请求输出 token
  • reasoning token
  • 缓存命中量与缓存折扣收益
  • 历史消息 token
  • 检索注入 token
  • 工具 schema token
  • 多模态输入 token
  • 超窗前裁剪次数
  • 因预算不足触发的降级次数

这些指标的意义在于:

  • 让你知道是哪个层在变重,而不是只有一个总账单

25. 一个实用的上下文预算思路 ​

很多系统真正需要的不是“大窗口”,而是:

  • 清晰预算

一个实用做法是把上下文预算按层拆开:

  1. 规则层预算
  2. 用户输入预算
  3. 历史层预算
  4. 检索层预算
  5. 工具层预算
  6. 输出层预算
  7. reasoning 预留预算

25.1 预算最好按“层”和“责任人”来分 ​

这样你们才能回答:

  • 是 Prompt 团队把规则做重了
  • 是 RAG 团队把检索片段塞多了
  • 还是 Agent 团队把工具面做宽了

25.2 长上下文不应该等于“全量回放” ​

上下文大,不等于什么都保留。

更稳妥的做法是:

  • 只保留当前任务仍需要的信息

25.3 上下文预算要配套压缩和过期策略 ​

预算不是“写在文档里就结束”,还要配套:

  • 历史摘要
  • 证据去重
  • 过期淘汰
  • 动态裁剪
  • 路由降级

26. 成本、延迟和质量为什么经常一起被 token 驱动 ​

很多团队喜欢把这三件事分开讨论:

  • 成本优化
  • 延迟优化
  • 质量优化

但在 LLM 系统里,token 往往是三者交汇点。

26.1 成本维度 ​

token 越多,通常成本越高。

26.2 延迟维度 ​

token 越多,请求构建、服务端处理和结果生成通常越慢。

26.3 质量维度 ​

token 并不是越少越好,过度压缩也会导致:

  • 证据不足
  • 输出不完整
  • reasoning 不够

所以真正成熟的优化目标不是:

  • 把 token 压到最低

而是:

  • 在可接受质量下,让 token 分布合理。

27. 模型迁移时为什么一定要重测 token 分布 ​

换模型时,很多团队只看:

  • 单价
  • 排行榜
  • 能力评分

但如果忽略 tokenizer 和上下文差异,实际账单和延迟可能与你预期差很大。

迁移时至少要重测:

  • 同类请求的输入 token 分布
  • 输出 token 分布
  • reasoning token 分布
  • 工具 schema 开销
  • 缓存命中收益变化

这类差异在下面场景尤其明显:

  • 代码任务
  • JSON 重任务
  • 多工具 Agent
  • 长 RAG 问答

28. 常见误区 ​

28.1 只按字符估成本 ​

这是最常见也最容易出大偏差的错误。

28.2 上下文越大越好 ​

大窗口只会放大治理能力,不会替代治理能力。

28.3 只盯用户输入,不盯系统拼接内容 ​

线上最重的部分,常常恰恰是系统自己拼进去的内容。

28.4 觉得缓存会自动解决一切 ​

缓存收益依赖稳定前缀工程,不是“打开开关”就完事。

28.5 只优化输入,不优化输出 ​

如果输出很长,或者 reasoning token 很重,单看输入优化是不够的。

28.6 把历史追加当成长记忆方案 ​

这通常只会让成本和噪声一起膨胀。

28.7 觉得 token counting 只是财务问题 ​

它同时影响:

  • 成本
  • 延迟
  • 路由
  • 上下文质量
  • 降级策略

29. 建议和哪些专题一起看 ​


30. 重点官方资源 ​


31. 一句话总结 ​

今天做 LLM 系统时,token 不只是计费单位,它同时还是:

  • 上下文容量约束、缓存收益边界、延迟驱动因素和多轮治理的核心预算对象