Token 与上下文窗口:理解 AI 模型的「记忆」与限制
一、为什么 Token 是你必须理解的概念
如果你只用 AI 编程工具做简单的代码补全(Tab 补全),那你可能不需要太关心 Token 是什么。但如果你想要:
- 让 AI 理解和修改一整个文件
- 让 AI 跨多个文件做重构
- 编写精确的 Prompt 来控制 AI 输出
- 理解为什么 AI 有时会"忘记"你之前说的话
那你就必须理解 Token 和上下文窗口这两个核心概念。
在我使用 AI 编程的早期,遇到过一个让我困惑的问题:我给 Claude 贴了一个 1500 行的代码文件,然后在对话中讨论了前面几个函数的优化方案。但当我让它修改文件末尾的一个函数时,它好像"不记得"这个函数的内容了——它的修改建议明显是错误的。
后来我才明白,这不是 AI 的 Bug,而是上下文窗口的特性。当你理解了 Token 和上下文窗口的机制后,这种问题就是完全可以预判和避免的。
二、什么是 Token——AI 的"原子单位"
2.1 Token 的定义
Token(词元)是大语言模型理解和生成文本的最小单位。类比一下:
- 英语中的"字母"是最小书写单位,但人类不会一个字母一个字母地读——我们会把字母组合成"词"
- Token 就是 AI 的"词"——AI 不一个一个字符地读,也不一个一个单词地读,而是把文本切分为一定大小的"Token块"
2.2 Tokenization 的工作机制
文本转为 Token 的过程叫做 Tokenization(分词)。目前主流模型(GPT、Claude)使用的是一种叫 BPE(Byte Pair Encoding)的算法。
BPE 的工作方式是:从字符级别开始,统计最常见的"字符对",将它们合并为一个新的 Token。反复执行这个过程,直到达到预设的词汇表大小。
让我们看一个具体的例子:
原始文本: "const userName = 'hello world';" BPE Tokenization 过程(简化示意): Step 1: 先按常见编程符号拆分 ["const", " ", "user", "Name", " ", "=", " ", "'", "hello", " ", "world", "'", ";"] Step 2: 高频组合合并 "user" + "Name" 经常一起出现 → 作为一个 Token "hello" + " " + "world" → 由于"hello world"是高频短语,"hello"和"world"可能各为一个Token 最终 Token 序列(近似): ["const", " ", "userName", " ", "=", " ", "'", "hello", " ", "world", "'", ";"] 约 12 个 Token2.3 英文 vs 中文的 Token 消耗差异
这是一个很多中文开发者没意识到的"隐性成本"。不同语言的 Token 效率差异很大:
英文的 Token 效率:
文本: "The quick brown fox jumps over the lazy dog" Token 数: ~10 比例: 1个单词 ≈ 1.2 个 Token中文的 Token 效率:
文本: "快速的棕色狐狸跳过了懒惰的狗" Token 数: ~15-20 比例: 1个汉字 ≈ 1.5-2 个 Token代码的 Token 效率:
Python: def calculate_sum(a, b): return a + b Token 数: ~12 中文注释: # 这个函数用来计算两个数的和 Token 数: ~15-20(比代码本身还多!)📊各语言 Token 消耗对比表(以表达相同语义为准):
| 语言 | 相对 Token 消耗 | 说明 |
|---|---|---|
| 英文 | 1x(基准) | 拉丁字母与BPE天然契合 |
| 中文 | 1.5-2x | 每个汉字通常占1-2个Token |
| 日文/韩文 | 1.5-2.5x | 类似中文,字符集大 |
| 代码(英文关键词) | 0.8-1.2x | 常见关键字和模式Token化效率高 |
| 代码(含中文注释) | 1.2-1.8x | 中文注释增加额外消耗 |
💡实用建议:如果使用按 Token 计费的 API(如 GPT-4 API、Claude API),用英文 Prompt 性价比更高。中文 Prompt 的效果可能同样好(甚至在某些情境下更好),但 Token 消耗明显更大。
2.4 代码的 Tokenization 示例
让我们看看一段实际的 JavaScript 代码是如何被 Token 化的:
// 源代码functioncalculateDiscount(price,userLevel){if(userLevel==='vip'){returnprice*0.8;}returnprice;}// 大致的 Token 分解(GPT-3.5/4 的 Tokenizer)// "function" → 1 Token(高频关键字)// " " → 1 Token// "calculate" → 1 Token// "Discount" → 1 Token// "(" → 1 Token// "price" → 1 Token// "," → 1 Token// " " → 1 Token// "user" → 1 Token// "Level" → 1 Token// ")" → 1 Token// " " → 1 Token// "{" → 1 Token// "\n " → 1 Token// "if" → 1 Token// 依此类推...// 总计约 30-35 个 Token💡 高频出现的关键字(function、if、return)、常见变量名、常用模式(=>箭头函数)在 Token 表中有自己的"专属位置",通常一个 Token 就够了。
三、上下文窗口——AI 的"工作记忆"
3.1 上下文窗口的概念
上下文窗口(Context Window)是模型在一次推理中能"看到"的 Token 最大数量。它决定了 AI 的"视野范围"。
用一个类比来理解:上下文窗口就像你在一个白板上写字。白板的大小是固定的——当白板写满后,你若要写新内容,就必须擦掉一些旧内容。同样,当 Token 数量超出上下文窗口后,模型就会"忘记"最早的内容。
上下文窗口示意图: [最早的内容] ... [中间的内容] ... [最新的内容] |<-------------- 上下文窗口 --------------->| 超出窗口的内容会被"截断"——模型完全看不到了3.2 上下文窗口的历史演进
上下文窗口的增长速度是 AI 进步最显眼的指标之一:
| 时间 | 模型 | 上下文窗口 | 约等于 |
|---|---|---|---|
| 2018 | GPT-1 | 512 | 一段话 |
| 2019 | GPT-2 | 1,024 | 一页文字 |
| 2020 | GPT-3 | 2,048 | 一篇短文 |
| 2022 | GPT-3.5 | 4,096 | 一篇长文 |
| 2023 | GPT-4 | 8,192 | 一个章节 |
| 2023 | GPT-4 Turbo | 128,000 | 一本小说 |
| 2024 | Claude 3 | 200,000 | 300页的书 |
| 2024 | Gemini 1.5 Pro | 1,000,000 | 一部《战争与和平》 |
从 512 到 1,000,000——增长近 2000 倍,仅用了 6 年时间。这个速度甚至超过了摩尔定律。
3.3 200K Token 意味着什么
Claude 3 的 200K Token 上下文窗口,对编程来说意味着:
≈ 150,000 个英文单词 ≈ 300 页的技术文档 ≈ 50,000-80,000 行代码(取决于语言和注释密度) ≈ 可以一次性放入以下内容: - 一个中型 Next.js 项目的所有源文件 - React 官方文档 + Express.js 文档 + Prisma 文档 - 整个项目的 package.json + tsconfig + 所有组件的完整代码3.4 “近因效应”——不是所有 Token 都同等重要
虽然模型有大的上下文窗口,但研究表明:模型"注意"得最多的是靠近末尾的内容。这被称为"近因效应"(Recency Bias)或"Lost in the Middle"现象。
上下文中各部分的"注意权重"分布(示意): 高权重:开头部分("首因效应") 中低权重:中间部分("Lost in the Middle") 最高权重:结尾部分("近因效应") 建议:把最重要的信息放在开头或结尾,不要让核心指令"沉"在中间💡实用建议:
- 将核心指令和约束放在 Prompt 的最后(最新位置)
- 将背景信息和示例放在 Prompt 的前部
- 在长对话中,定期重申关键要求
- 对于特别重要的约束,在对话的各个环节反复强调
四、上下文窗口对编程的影响
4.1 单文件代码理解
当你让 AI 理解一个代码文件时,首先需要确保整个文件的内容能放进上下文窗口。
一个 1000 行的 JavaScript 文件 ≈ 10,000-15,000 Token → 在 GPT-4(8K)中放不下 → 在 GPT-4 Turbo(128K)和 Claude 3(200K)中完全没问题4.2 多文件代码修改
这是上下文窗口最能体现差异的场景。假设你要做一个涉及 5 个文件的修改:
文件1(页面组件): 200 行 ≈ 2,500 Token 文件2(API路由): 150 行 ≈ 1,800 Token 文件3(数据模型): 100 行 ≈ 1,200 Token 文件4(类型定义): 80 行 ≈ 1,000 Token 文件5(工具函数): 120 行 ≈ 1,500 Token 对话历史 + Prompt + AI响应: ≈ 4,000 Token 总计: ~12,000 Token这个总数在 8K 的窗口(GPT-4)中已经很拥挤了——可能放不下完整的 5 个文件加上充分的对话讨论。但在 128K(GPT-4 Turbo)和 200K(Claude 3)中,还有大量空间可以添加相关文档、API 规范等额外上下文。
4.3 长对话的"记忆丧失"
当你与 AI 进行长时间的多轮对话时,早期交换的信息会逐渐被"推"出上下文窗口。
第 1 轮对话:讨论项目架构(占 5,000 Token) 第 2 轮对话:讨论数据库设计(占 8,000 Token) 第 3 轮对话:开始写代码(累计 13,000 Token) ... 第 10 轮对话:修改 bug(累计 35,000 Token) 此时,第 1 轮讨论的架构细节可能在 GPT-4(8K)中完全消失了, 只在 GPT-4 Turbo 和 Claude 3 中还能被"记住"。⚠️这就是我在文章开头提到的那个问题的原因——当我把 1500 行代码放进会话,经过多轮讨论后,文件末尾的内容虽然一开始在上下文中,但随着新内容的不断加入,AI 对较早位置的"注意力"减弱了。
五、高效管理 Token 的实用策略
5.1 文件拆分策略
不要把一个 3000 行的文件直接丢给 AI。相反,采用"分层递进"策略:
步骤①:给 AI 项目的骨架 "这是我的项目结构:[目录树] 我要修改 [具体功能] 模块,请先帮我理解相关的文件。" 步骤②:给 AI 关键部分 "这是 [具体文件] 的核心部分(前 200 行),请分析..." 步骤③:选择性提供上下文 "这是和当前修改相关的 [类型定义/接口/配置],请参考..." 步骤④:总结代替完整代码 "文件中剩余的 800 行是 [功能描述],如果你需要某部分的具体代码,请告诉我。"5.2 选择性上下文(Selective Context)
给 AI 的上下文不是"越多越好",而是"越相关越好":
❌ 不好的做法:
把整个项目的 50 个文件全部放进 Prompt → 浪费 Token,AI 反而可能被无关信息干扰✅ 好的做法:
只放与当前任务直接相关的文件: - 正在修改的文件 - 被修改文件的依赖(它 import 了什么) - 类型定义和接口 - 相关的配置文件5.3 Prompt 压缩技巧
一些减少 Token 消耗的实用技巧:
去掉不必要的空白和注释(仅在你发送给 AI 的代码中):
// 原始(包含格式化空白和开发注释)functiongetUserById(id){// 查询用户信息constuser=awaitdb.user.findUnique({// Prisma 查询where:{id:id},include:{profile:true}});returnuser;}// 压缩后(去掉注释和多余空白,AI 仍然能理解)functiongetUserById(id){constuser=awaitdb.user.findUnique({where:{id},include:{profile:true}});returnuser;}用简洁的描述替代完整的文件内容:
"项目的 ESLint 配置是标准配置 + React plugin + TypeScript plugin" (而不是粘贴整个 .eslintrc.json 文件)在大文件场景中,用 // … (略) 标记省略部分:
functionfirstFunction(){/* ... */}functionsecondFunction(){/* ... */}// ... (此处省略 500 行业务逻辑函数)// 以下是要修改的核心部分:functionprocessOrder(order){// 这个函数有 150 行// ...}5.4 "分而治之"策略
对于大型任务,不要试图在一个对话中完成所有工作:
# 错误做法:一个长对话处理一切 对话1(Token 0 → 50K):讨论需求 → 设计架构 → 生成所有代码 → 修改 → 测试 → 后期 AI 的"记忆"已经严重过载 # 正确做法:分阶段、分对话 对话1(Token 0 → 15K):需求分析和架构设计 → 输出:设计文档 对话2(Token 0 → 20K):基于设计文档,实现模块 A → 完成并测试 对话3(Token 0 → 20K):基于设计文档,实现模块 B → 完成并测试 对话4(Token 0 → 10K):集成测试和问题修复 → 每个对话都有充足的上下文空间5.5 利用工具自动管理上下文
一些工具已经内置了智能的上下文管理:
- Claude Code:自动选择相关文件放入上下文,而非全部文件
- Cursor:通过 .cursorrules 和项目索引来优选上下文
- GitHub Copilot:分析最近查看和编辑的文件,自动构建上下文
💡经验法则:让工具帮你管理上下文,你只需要关注"当前这个对话中聊什么"。
六、常见 Token 管理错误与避免方法
错误一:把所有文件都放进 Prompt
错误示范: "我有以下文件:[粘贴了10个文件,总计5000行代码]。 帮我给所有文件的函数加错误处理。" 问题:AI 被过多的信息淹没,可能漏掉某些文件或做出不准确的修改。✅ 改进方法:分文件逐个处理,并在需求中明确具体文件。
错误二:忽视 Token 消耗的"隐形大户"
错误示范: 在 Prompt 中粘贴了一个 200 行的 JSON 配置文件(包含了 AI 不需要知道的 所有细节),而这个文件的"精华"信息其实只有 5 行。✅ 改进方法:提取关键信息,而非全量粘贴。对于 JSON 配置,只给出结构或关键字段。
错误三:在长对话中依赖 AI "记住"所有内容
错误示范: 第 1 轮:我告诉 AI 使用 async/await 风格 ...(50 轮对话后) 第 51 轮:我让 AI 写一个新函数,期望它还记得 async/await 的偏好 → AI "忘了",因为它已经被推出上下文窗口✅ 改进方法:在关键指令被"遗忘"前,主动重申。或在工具层面设置(如 CLAUDE.md 中的规范会在每次对话开始时重新加载)。
错误四:中文 Prompt 中不必要的啰嗦
错误示范: "请帮我写一个能够处理用户登录请求的 Express.js 路由处理函数, 这个函数需要接受用户名和密码两个参数,然后..." Token 数:~100(中文)+ 代码 改进版: "写 Express.js 登录路由:接收 username + password,JWT 认证,bcrypt 加密" Token 数:~30(中文+英文混排)+ 代码✅ 改进方法:Prompt 中使用简洁的关键词 + 英文技术术语(API、框架名等),减少 Token 浪费。
七、总结
Token 和上下文窗口是大语言模型最基础的"物理限制",理解它们就像理解计算机的内存和硬盘——你不需要成为硬件专家,但了解基本概念能在你编程时做出更好的决策。
核心要点:
- ✅ Token 是 AI 处理文本的最小单位,中英文的 Token 效率不同
- ✅ 上下文窗口决定了 AI 一次能"看到"多少内容
- ✅ 模型对靠近末尾的内容"注意"得更多(近因效应)
- ✅ 大型上下文窗口(128K+)让理解整个项目成为可能
- ✅ 高效管理上下文的关键是:选择性提供信息 + 分层递进 + 工具辅助
- ✅ 避免常见错误:信息过载、忽视隐形 Token 消耗、依赖 AI 的长时记忆
掌握了 Token 和上下文窗口的知识,你就理解了为什么有时候 AI 的"记忆"好像不稳定——这不是 Bug,而是机制。下一章我们将探讨 AI 编程的三种核心模式。
下一篇:AI 编程的三种模式:代码补全、对话生成与智能体自动编程