上下文窗口和消耗的 Token 有什么区别?
复制为 Markdown一个让人困惑的现象
如果你用过 Claude Code、Cursor、Qoder 这类 AI 编程助手,大概率遇到过这样的场景:
你只是让 AI 帮你重构了一个中等规模的模块,修了几个 bug,跑了跑测试。整个过程大概聊了十几轮。然后你一看 token 用量——怎么已经消耗了 500 万 tokens?
这看起来非常不合理。Claude 3.5 Sonnet 的上下文窗口是 200K tokens,Claude Opus 4 的上下文窗口是 200K(1M 版本也已经推出)。就算你全程把上下文窗口塞满,一次请求最多也就 200K。那这 500 万是怎么来的?
这个问题的答案,藏在两个经常被混淆的概念里:上下文窗口(Context Window) 和 累计 Token 消耗(Cumulative Token Consumption)。它们是相关的,但描述的是完全不同的东西。
这篇文章会从底层开始,一步步讲清楚 token 是怎么计算的、上下文窗口到底意味着什么、以及为什么在 agentic 编程工具里,实际消耗总是远超你的直觉预期。
一、Token:大语言模型的最小计算单位
在讨论上下文窗口之前,我们得先搞清楚一个更基础的问题:模型看到的是什么?
人类看到的是文字,但大语言模型(LLM)看到的是 token——一种把文本切分成的最小语义单元。
1.1 Token 是怎么切分的?
LLM 不会按"字"或"词"来处理文本,而是使用一种叫 子词分词(Subword Tokenization) 的技术。主流的分词器有 BPE(Byte Pair Encoding)、WordPiece、SentencePiece 等。
举个例子,英文单词 "unhappiness" 可能会被拆成:
un + happiness → ["un", "happi", "ness"] // 3 个 tokens
而中文的切分方式更加碎片化。一个常用汉字通常就是 1-2 个 token,但生僻词或专业术语可能被拆得更碎:
"上下文窗口" → ["上", "下文", "窗口"] // 大约 3-5 个 tokens
"Tokenization" → ["Token", "ization"] // 2 个 tokens
关键认知: token 不是字符,不是词,而是一种介于两者之间的"语义碎片"。粗略估算的话,英文中 1 个 token 大约对应 0.75 个单词(或者说 100 个 token 约等于 75 个英文单词),中文的压缩率则更低一些。
1.2 一次 API 调用里有哪些 token?
当你向 LLM 发送一次请求时,输入端(prompt)和输出端(completion)都会被计算 token。但 prompt 不只是你打字输入的那句话——它实际上包含了好几个部分:
| 组成部分 | 说明 | 典型大小 |
|---|---|---|
| System Prompt | 模型的行为指令、角色设定、安全策略、工具定义 | 2K-10K tokens |
| 对话历史 | 之前所有的用户消息和模型回复 | 随轮次递增 |
| 检索上下文 | RAG 检索的文档、工具调用返回的结果 | 1K-50K tokens |
| 当前用户消息 | 你刚输入的这句话 | 100-2K tokens |
模型输出(completion)的部分同样消耗 token,包括:
| 组成部分 | 说明 | 典型大小 |
|---|---|---|
| 最终回复 | 模型给你的回答 | 500-8K tokens |
| 推理过程(Thinking) | 部分模型会先做一段内部推理(如 Claude 的 extended thinking) | 1K-30K tokens |
| 工具调用 | Agent 决定调用工具时生成的结构化指令 | 100-2K tokens |
这些加在一起,就是一次 API 调用的完整 token 消耗。
1.3 隐藏的 token:你可能没注意到的消耗
有几个容易被忽视的 token 来源:
- 特殊 token:
<|begin_of_text|>、<|end_of_turn|>、工具调用的边界标记等,虽然你看不到,但都会被计入总数。 - 多模态转换:如果你发送了图片,图片会被转换成一组视觉 token 嵌入到序列中。一张图可能消耗几百到几千个 token。
- Thinking / Reasoning tokens:具备深度推理能力的模型(如 Claude 的 extended thinking、OpenAI 的 reasoning)在生成最终回复前会先做一段内部"思考",这些中间推理步骤也会消耗 token,并且通常按照 output token 计费。
二、上下文窗口:模型的"工作记忆"
理解了 token 之后,我们可以聊聊上下文窗口了。
2.1 定义
上下文窗口(Context Window) 是一个模型在单次请求中能够处理的最大 token 数量。它包括输入(prompt)和输出(completion)的 token 总和。
你可以把它想象成一张桌子的大小——桌面就这么大,你需要把系统提示、对话历史、参考资料、当前问题和模型回复全都摆上去。桌子放不下?那就只能丢掉一些东西。
不同模型的上下文窗口大小差异很大:
| 模型 | 上下文窗口 |
|---|---|
| GPT-4o | 128K tokens |
| Claude 3.5 Sonnet / Opus | 200K tokens |
| Claude Opus 4 (1M 版本) | 1M tokens |
| Gemini 2.5 Pro | 1M tokens |
| Llama 4 Scout | 10M tokens |
2.2 上下文窗口的技术本质
上下文窗口的限制不是人为拍脑袋定的,而是来自 Transformer 架构的底层物理约束。
Transformer 的核心是自注意力机制(Self-Attention)。在自注意力中,序列中的每一个 token 都需要和其他所有 token 计算相关性权重。这意味着计算复杂度是 O(n²·d),其中 n 是序列长度,d 是模型的隐藏层维度。
换句话说:序列长度翻倍,计算量翻四倍。
这不仅仅是理论问题。在实际推理中,模型需要为每个 token 存储 Key 和 Value 向量(即 KV Cache)。上下文越长,KV Cache 占用的显存就越大。一个处理 200K token 上下文的请求,其 KV Cache 可能需要数 GB 的显存空间。

上图展示了单次 API 请求中,上下文窗口作为一个固定容量容器,各个组成部分如何共享这个有限的空间。
2.3 上下文窗口 ≠ 模型的有效处理能力
这里有一个非常重要的认知:上下文窗口是理论上限,不等于模型在每个位置都能同等有效地处理信息。
研究已经证实,LLM 在处理长上下文时存在"中间丢失(Lost in the Middle)"现象——模型对开头和结尾的信息关注度最高,中间位置的信息容易被忽略或处理得不够准确。这就像你读一本很长的文档,开头和结尾的内容记得最清楚,中间部分反而容易走神。
所以即使上下文窗口有 200K,把 200K 全部塞满也不意味着模型能完美地利用所有信息。上下文越大,信息的密度和相关性就越重要。
三、为什么累计消耗远超上下文窗口?
好了,现在我们已经理解了 token 和上下文窗口的基础概念。终于来到核心问题:为什么实际消耗的 token 总是远远超过上下文窗口的大小?
答案其实很简单,但需要一些思维转换。
3.1 关键认知:每次请求都在"重放"全部历史
LLM 是无状态的。它没有"记忆"这个概念——每次你发送一条新消息,API 并不会记得上一轮聊了什么。
所有的"记忆"都是通过把完整的对话历史作为 prompt 的一部分重新发送来实现的。
这意味着:
- 第 1 轮对话:你发送 系统提示 + 消息1 → 消耗 5K tokens
- 第 2 轮对话:你发送 系统提示 + 消息1 + 回复1 + 消息2 → 消耗 15K tokens
- 第 3 轮对话:你发送 系统提示 + 消息1 + 回复1 + 消息2 + 回复2 + 消息3 → 消耗 30K tokens
- ……
每一轮,你都在重新发送之前所有的内容。 对话越长,每次请求的 prompt 就越大。

上图清晰地展示了核心区别:上下文窗口是单次请求的容量上限(一个固定的水杯),而累计消耗是整个会话中所有请求的总和(你一共倒了多少水)。
3.2 在 Agentic 工具中,这个效应被极度放大
像 Claude Code、Cursor、Qoder 这样的 AI 编程助手,和普通聊天有一个本质区别:它们不只是聊天,它们在执行任务。
一个典型的编程任务可能涉及:
- 用户提出需求
- Agent 分析需求,决定要调用哪些工具
- 调用 Bash 执行命令 → 命令输出进入上下文
- 读取文件内容 → 文件内容进入上下文
- 搜索代码库 → 搜索结果进入上下文
- 编辑文件 → 编辑记录进入上下文
- 运行测试 → 测试日志进入上下文
- 测试失败 → Agent 深度推理 → 读取更多文件 → 修复代码
- ……循环往复
每一次工具调用都是一次独立的 API 请求,每次请求都要带上完整的上下文历史。
更关键的是,工具返回的输出往往非常"庞大":
- 一个
find命令的输出:可能几千 tokens - 读取一个中等大小的源代码文件:几千到上万 tokens
- 一段测试运行的日志:可能上万 tokens
- 一次
git diff的结果:视改动量而定,可能几千 tokens
这些输出全部会被追加到对话历史中,在后续的每一轮请求中被反复发送。

上图展示了一个典型的 Claude Code 会话:仅仅 5 轮工具调用,累计消耗就达到了 430K tokens——已经超过了单次上下文窗口的两倍。在真实的编程场景中,一个会话可能有几十甚至上百轮工具调用。
3.3 算一笔真实的账
让我们用一个真实的 Claude Code 会话来算一算。假设你在一个中型项目里让 AI 帮你实现一个新功能:
| 轮次 | 动作 | 本轮 prompt 大小 | 本轮输出 | 本轮总消耗 |
|---|---|---|---|---|
| 1 | 用户描述需求,Agent 分析 | 10K | 2K | 12K |
| 2 | 搜索相关代码 | 14K | 3K | 17K |
| 3 | 读取 3 个文件 | 25K | 1K | 26K |
| 4 | 创建新文件 | 35K | 5K | 40K |
| 5 | 修改现有文件 | 48K | 3K | 51K |
| 6 | 运行测试 | 60K | 2K | 62K |
| 7 | 测试失败,深度推理修复 | 75K | 15K | 90K |
| 8 | 再次运行测试 | 100K | 2K | 102K |
| 9 | 通过,总结报告 | 110K | 3K | 113K |
累计消耗:12+17+26+40+51+62+90+102+113 = 513K tokens
这只是 9 轮对话,而且还是比较顺利的情况。如果遇到更多 bug、需要反复调试、读取更多文件,一个会话轻松达到 2M-10M tokens。
有开发者分享过,他的一个 Claude Code 会话在 1M 上下文窗口的设置下,累计消耗达到了 172M tokens。
3.4 Prompt Caching:省钱的魔法,但不省 token
你可能会问:每次都重新发送完整的历史记录,这也太浪费了吧?
好消息是,模型提供商也意识到了这个问题,并提供了 Prompt Caching(提示缓存) 机制。
原理是这样的:在 Transformer 的自注意力计算中,每个 token 都会生成一对 Key 和 Value 向量(KV Cache)。如果两次请求的 prompt 前缀是一样的,系统就可以直接复用之前计算好的 KV Cache,而不需要重新算。
- Cache Write(缓存写入):首次发送的 token 会被计算并缓存
- Cache Read(缓存读取):后续请求中重复的前缀部分直接从缓存读取
这对成本和延迟有显著帮助——缓存读取通常比全量计算便宜 90%(以 Anthropic 的定价为例,cache read 的价格约为 input token 价格的 1/10)。
但请注意:缓存只是降低了计算成本和费用,token 的"计数"并没有减少。 从计费角度看,cache read tokens 仍然会被计入总消耗量,只是单价更低。
四、关键指标对比
| 维度 | 上下文窗口 (Context Window) | 累计 Token 消耗 (Cumulative Tokens) |
|---|---|---|
| 定义 | 单次请求能处理的最大 token 数 | 整个会话中所有请求的 token 总和 |
| 类比 | 一个水杯的容量 | 你往杯子里倒水的总量 |
| 是否固定 | 是,由模型架构决定 | 否,随会话长度和复杂度递增 |
| 数量级 | 128K - 10M tokens | 可以达到数百万甚至上亿 tokens |
| 影响 | 决定单次能"看到"多少信息 | 决定成本和延迟 |
| 超出后果 | API 报错或截断 | 账单金额增加、会话变慢 |
| 优化方式 | 选用更大窗口的模型 | 精简 prompt、压缩历史、合理分会话 |
参考资料:
- Context Window Overflow: What It Is & How to Fix It — Redis Blog
- How to Track LLM Token Usage (2026) — Braintrust
- Manage Costs Effectively — Claude Code Docs
- Attention Complexity: Quadratic Scaling — Matt Brenddoerfer
- AI Token Management: Why Your Claude Code Session Drains — MindStudio
- What is a Context Window? — IBM
本站文章遵循 Apache 2.0 开源协议,转载请注明出处,商业化出版请联系本站管理员(页面底部)
鄂公网安备42098402000265号