上下文管理
本文为「AI 辅助编程实战指南」第7篇,完整系列持续更新中。
前言
你可能遇到过这些情况:同一段代码,在新会话里让 AI 修改,质量明显比旧会话好;或者明明给了 AI 所有信息,它输出的代码却”忘了”你前面提过的规范;又或者对话超过 30 轮后,AI 开始重复之前已经试过的方案。
这些问题的根源都是同一个:上下文管理不到位。
AI 的输出质量直接取决于你喂给它的上下文质量。给太多噪音,AI 会被干扰;给太少信息,AI 会猜错;上下文窗口快满了,AI 会”失忆”。上下文管理不是 Prompt 工程的附属品,它是 AI 编程场景下最重要的单一技能——比 Prompt 写法更重要,比工具选择更重要。
本篇学完你将掌握:
- 上下文窗口的运作机制和容量边界
- 项目规则文件的正确写法和各工具配置差异
- 精准上下文投喂的 @ 引用体系和最佳实践
- 会话管理的黄金法则:何时压缩、何时重启
- 上下文劣化的信号识别和应对策略
- 环境工程化消除环境差异的完整方案
一、上下文窗口:理解 AI 的”工作台”
在讲管理策略之前,先搞清楚上下文窗口是什么、有多大、什么时候会出问题。
1.1 什么是上下文窗口
上下文窗口是 AI 在每次对话中能”看到”的全部信息——包括系统提示词、项目规则文件、你的提问、AI 的回答、引用的代码、对话历史等等。所有这些信息共享同一个有限的空间。
打个比方:上下文窗口就像 AI 的”工作台”。台面大小有限,你摆上去的文件越多,AI 能看到每一份文件的注意力就越分散。如果台面被塞满了,最早放上去的文件就会被挤出去。
1.2 容量边界与消耗速度
| 工具 / 模型 | 上下文窗口 | 大致容量 | 消耗示例 |
|---|---|---|---|
| Claude Code (Opus/Sonnet) | 200K tokens | ~100 万 tokens 工作上下文 | 500 行 TypeScript ≈ 4,000 tokens |
| Cursor (Claude 4 Sonnet) | 200K tokens | 取决于模型 | AI 一次详细回复 ≈ 1,500-3,000 tokens |
| GitHub Copilot | 128K tokens | 取决于模型 | 项目规则文件 ≈ 200-500 tokens |
| ChatGPT (GPT-4.1) | 1M tokens | 100 万 tokens | 完整对话历史 + 文件引用 |
💡 提示:token 不等于字符数。粗略估算:1 个中文字 ≈ 1.5-2 个 token,1 个英文单词 ≈ 1-1.5 个 token。一行典型代码 ≈ 10-15 个 token。
1.3 上下文窗口的消耗结构
每次对话开始时,上下文窗口已经被”预占”了一部分:
1 | ┌───────────────────────────────────────────┐ |
⚠️ 注意:MCP 工具是上下文消耗的大户。有用户记录到仅连接 4 个 MCP 服务器就消耗了 67,000 tokens——在你还没输入任何 Prompt 之前,这些空间就已经被占用了。如果你很少用某些 MCP 工具,建议禁用它们以释放上下文空间。
1.4 上下文满了会怎样
当上下文窗口接近饱和时,各工具有不同的应对机制:
| 工具 | 饱和行为 | 影响 |
|---|---|---|
| Claude Code | 自动压缩(auto-compact),在约 83.5% 容量时触发 | 早期对话细节被压缩摘要,可能丢失关键信息 |
| Cursor | 对话历史截断,早期消息被丢弃 | 上下文断裂,AI “忘记”之前的讨论 |
| ChatGPT | 自动压缩或提示”对话过长” | 需要手动开启新对话 |
自动压缩虽然能延长对话寿命,但压缩过程本身会丢失信息——尤其是具体的代码片段、错误信息和决策细节。这些恰恰是 AI 后续工作最需要的上下文。
二、项目规则文件:每次对话自动加载的”宪法”
项目规则文件是解决上下文问题的第一道防线。它在每次新会话开始时自动加载到上下文中,确保 AI 始终知道你的项目规范和编码约定——不用每次手动重复说明。
2.1 各工具的规则文件对比
| 工具 | 规则文件 | 加载方式 | 共享方式 |
|---|---|---|---|
| Cursor | .cursor/rules/*.md(新版)或 .cursorrules(旧版) |
每次对话自动加载 | 随项目 Git 提交,团队共享 |
| Claude Code | CLAUDE.md(项目根目录 + 子目录) |
每次对话自动加载,子目录按需加载 | 随项目 Git 提交 |
| Qoder | AGENTS.md(项目根目录) |
每次对话自动加载 | 随项目 Git 提交 |
| Copilot | .github/copilot-instructions.md |
每次对话自动加载 | 随项目 Git 提交 |
| OpenAI Codex | AGENTS.md(项目根目录) |
每次对话自动加载 | 随项目 Git 提交 |
2.2 规则文件该写什么
规则文件的核心是回答三个问题:项目是什么、代码怎么写、怎么验证。用一个简洁的框架:
1 | # 项目:ShopFront — 电商平台后台 |
💡 提示:以上示例只有约 30 行——这就够了。社区实测数据显示:具体的规则(”用 2 空格缩进”)遵守率约 89%,而模糊的指令(”写干净的代码”)遵守率只有 35%。更关键的是,规则文件越长,所有规则的遵守率都会下降——因为 AI 的注意力预算是有限的。
2.3 规则文件的长度控制
| 文件位置 | 建议长度 | 内容定位 |
|---|---|---|
全局规则(~/.claude/CLAUDE.md) |
< 50 行 | 个人偏好:缩进风格、回复语言、注释语言 |
项目根规则(./CLAUDE.md) |
< 300 行 | 项目规范:技术栈、编码规范、安全红线 |
子目录规则(.claude/rules/*.md) |
按需 | 模块特定规范,只在相关文件操作时加载 |
| 所有规则文件总计 | < 2,000 tokens | 超过这个量级,规则遵守率显著下降 |
2.4 渐进式展开:不要把所有规则塞进一个文件
当规则内容很多时,使用渐进式展开策略——把详细规则拆到独立文件中,在主规则文件中只放简短描述和引用路径:
1 | # CLAUDE.md(主文件,保持精简) |
AI 在执行相关任务时会按需读取这些文件,而不是在会话开始时就全部加载。这就是第4篇提到的渐进式展开(Progressive Disclosure)在上下文管理中的应用。
2.5 规则文件的常见错误
| 错误 | 原因 | 正确做法 |
|---|---|---|
| 用否定句:”不要使用分号” | 否定句反而激活了”分号”这个概念 | 用肯定句:”使用 no-semicolons ESLint 规则” |
| 全文加粗:”IMPORTANT” | 10 条规则都标重要 = 没有重要的 | 只给真正关键的 1-2 条加粗 |
@引用大文件:@docs/full-api.md |
整个文件被嵌入上下文,浪费数千 tokens | 只写文件路径描述,让 AI 按需读取 |
| 规则太长:500+ 行 | 超出 AI 的注意力预算,所有规则的遵守率都下降 | 控制在 300 行以内,详细内容拆到子目录 |
💡 建议:规则文件应该随着项目演进持续更新。每次发现 AI 犯了一个错误,就修一下规则文件让它不再重犯。几周后,你的规则文件就会变成项目经验的精华浓缩。
三、精准上下文投喂:只给 AI 它需要的
规则文件解决了”持久化规范”的问题,但在具体任务中,你还需要给 AI 提供与当前任务直接相关的上下文。核心原则是:只给相关的,不给全部的。
3.1 Cursor 的 @ 引用体系
Cursor 提供了最丰富的显式上下文引用机制:
| 引用类型 | 语法 | 什么时候用 | 示例 |
|---|---|---|---|
| 文件 | @Files |
需要引用某个具体文件的完整内容 | @src/services/auth.py 参考这个文件的错误处理模式 |
| 目录 | @Folders |
需要引用整个目录的结构 | @src/api/v1 检查这个目录有没有缺少认证的接口 |
| 代码符号 | @Code |
只需要引用某个函数/类 | @useUserData 把这个 hook 从 useState 改为 React Query |
| 文档 | @Docs |
引用外部库的官方文档 | @React Router Docs 用文档中的方式实现路由守卫 |
| Web | @Web |
搜索最新信息 | @Web FastAPI 0.110 有什么新特性 |
| Git | @Git |
引用提交历史或 diff | @Commit 帮我生成 commit message |
| Linter | @Linter Errors |
让 AI 修复 lint 错误 | 修复所有 @Linter Errors 中的问题 |
最佳实践:
1 | # 好的做法:精准引用 |
💡 提示:
@Code(代码符号引用)是最精准的引用方式——它只引入你指定的函数或类,不会把整个文件都塞进去。当你只需要参考某个函数的写法时,优先用@Code而不是@Files。
3.2 Claude Code 的上下文引用
Claude Code 主要通过文件路径引用和对话内引用来管理上下文:
1 | # 引用文件路径(AI 按需读取) |
Claude Code 的 Agent 模式会自动分析代码库,识别相关文件和模式。但在复杂项目中,显式指定文件路径比让 AI 自己搜索更可靠。
3.3 通用策略:片段引用 vs 全文件引用
| 策略 | 适用场景 | 上下文消耗 |
|---|---|---|
| 粘贴代码片段 | 只需要参考某几行代码 | 最低(仅引用片段) |
| @符号引用函数/类 | 需要参考某个完整的函数或类 | 低(仅引用符号) |
| @文件引用 | 需要理解整个文件的结构和逻辑 | 中等(整个文件) |
| @目录引用 | 需要理解目录结构和文件关系 | 较高(目录下所有文件) |
实操建议:
1 | # 最佳:只粘贴相关的代码片段 |
3.4 上下文投喂的反面案例
| 反面做法 | 问题 | 改进方案 |
|---|---|---|
| “帮我优化整个项目的性能” | 范围太大,AI 会随意发挥 | “优化 /api/products 接口的查询性能,当前响应时间 500ms” |
| 把整个 2000 行的文件粘贴到对话中 | 浪费上下文空间,AI 注意力分散 | 只粘贴需要修改的函数 + 相关上下文 |
| 让 AI “参考项目里的写法” | AI 可能参考了错误的文件 | 显式指定要参考的文件路径 |
| 在对话第 25 轮时还在修改同一个功能 | 上下文已严重污染 | 开新会话,带上关键上下文重新提问 |
四、会话管理:何时续命,何时重启
会话管理是上下文管理中最容易被忽视的环节。很多开发者习惯在同一个会话里做所有事情——结果对话越来越长,AI 的输出质量越来越差。
4.1 会话的生命周期
1 | ┌──────────────────────────────────────────────┐ |
4.2 会话边界原则:一个会话 = 一个任务范围
最核心的会话管理原则:一个会话应该有且只有一个明确的任务范围。
1 | # 好的做法:一个会话 = 一个明确任务 |
💡 提示:即使上下文预算还有剩余,切换到完全不同的任务时也应该开新会话。新鲜的上下文是免费的,被污染的上下文是昂贵的。
4.3 两个关键命令
Claude Code 的上下文管理命令:
| 命令 | 作用 | 什么时候用 |
|---|---|---|
/compact |
压缩对话历史,保留关键信息 | 当前任务还没做完,但对话太长 |
/compact "保留认证模块的修改和测试结果" |
带焦点的压缩 | 明确告诉 AI 哪些信息必须保留 |
/clear |
完全清除上下文 | 切换到完全不同的任务 |
使用时机判断:
1 | 当前任务完成了吗? |
Cursor 的会话管理:
Cursor 没有直接的 /compact 命令,但可以通过以下方式管理上下文:
- 关闭当前 Chat 窗口,开启新的 Chat
- 在 Agent 模式下,完成一个功能后提交 commit,然后开新对话
- 使用
@Past Chats引用历史对话中的关键信息
4.4 主动压缩 vs 被动压缩
社区的最佳实践是在上下文用到 70-75% 时就主动压缩,而不是等到 83.5% 的自动触发阈值:
1 | // Claude Code 设置:自定义自动压缩阈值 |
为什么主动压缩更好?因为自动压缩是在上下文已经很满时触发的,此时 AI 可能已经开始出现”上下文劣化”的症状(见下一节)。主动压缩让你在 AI 输出质量还很高的时候就释放空间。
4.5 跨会话上下文传递
当任务需要跨越多个会话时,用以下方式传递关键上下文:
| 方法 | 适用场景 | 具体做法 |
|---|---|---|
| PROGRESS.md | 中长期功能开发 | 维护进度文件,新会话让 AI 读取(详见第6篇) |
| CHANGE_SPEC.md | 有明确规格文档的任务 | 新会话引用规格文件继续工作 |
| git log | 快速了解已完成的工作 | 让 AI 阅读最近的 commit 历史 |
| 代码注释 | 标记待完成的工作 | 在代码中留 // TODO: 下一步实现 XX |
1 | # 新会话的标准开场 Prompt |
五、上下文劣化:信号识别与应对
上下文窗口满了会触发自动压缩,但在满之前,上下文质量其实已经在悄悄劣化。识别这些早期信号,才能在问题恶化之前采取行动。
5.1 四大劣化信号
| 信号 | 表现 | 原因 |
|---|---|---|
| 重复 | AI 建议你已经试过的方案 | 对话历史太长,早期的尝试被压缩但没被完全丢弃 |
| 漂移 | AI 的代码风格变了,不再遵守你的规范 | 规则文件的优先级被后续的对话内容淹没 |
| 幻觉增加 | AI 编造不存在的函数名或 API | 上下文噪音太多,AI 的”搜索”能力被干扰 |
| 遗忘 | AI 忘了前面定义的变量、接口或约束 | 早期信息被挤出上下文窗口 |
5.2 快速诊断方法
如果你怀疑上下文已经劣化,用以下方式快速诊断:
1 | # 诊断方法1:让 AI 复述项目规范 |
5.3 应对策略
1 | 发现劣化信号 |
⚠️ 注意:不要试图”坚持用下去”。当你发现 AI 的输出质量明显下降时,硬撑只会让情况更糟——你会花更多时间修复 AI 的错误输出,而不是开一个新会话重新开始。
六、环境工程化:消除”在 AI 那边能跑,在我这跑不了”
上下文管理不只关乎对话内容,还包括开发环境的一致性。如果 AI 不知道你的 Python 版本、操作系统、依赖版本,它生成的代码可能在你这里跑不起来。
6.1 为什么环境差异会影响 AI 输出
| 差异类型 | 翻车案例 |
|---|---|
| Python 版本 | AI 用 match/case(Python 3.10+),你用的是 3.9 |
| 包版本 | AI 用 Pydantic v1 的写法,你的项目用 v2 |
| 操作系统 | AI 生成 os.path.join(),你的项目要求用 pathlib |
| 数据库 | AI 生成 MySQL 方言 SQL,你用的是 PostgreSQL |
6.2 在规则文件中声明环境
最简单的环境工程化方式是在规则文件中明确声明运行环境:
1 | ## 运行环境 |
6.3 使用 DevContainer 锁定环境
更彻底的方案是用 VS Code DevContainer 或 Docker 锁定整个开发环境:
1 | // .devcontainer/devcontainer.json |
在规则文件中引用环境配置:
1 | ## 开发环境 |
6.4 环境工程化的完整方案
1 | devcontainer.json(锁定 OS + 语言版本 + 系统依赖) |
💡 建议:对于团队协作项目,DevContainer 的 ROI 非常高。一次配置好环境,所有团队成员(和 AI)都在同一个环境中工作,”在我这能跑”的问题基本消失。
七、踩坑记录:对话 40 轮后 AI 开始”鬼打墙”
现象:
在一个会话中连续开发商品管理模块。前 20 轮一切正常,但到第 30 轮左右,AI 开始反复建议已经试过的方案。比如之前已经因为性能问题放弃了嵌套子查询,但 AI 在第 35 轮又提出了同样的嵌套子查询方案。更诡异的是,AI 在第 38 轮生成的代码引用了一个不存在的函数 validate_product_data()——这个函数从来没有在项目中定义过。
原因:
- 对话历史过长(40 轮 ≈ 60,000+ tokens 的对话内容)
- 自动压缩在第 32 轮时触发,早期讨论细节被摘要化
- AI 丢失了”已经试过嵌套子查询但放弃”的关键决策信息
- 上下文噪音过多,AI 开始”幻觉”——编造不存在的函数名
解决方案:
- 立即
/clear,开新会话 - 在新会话中,用
PROGRESS.md+git log恢复上下文 - 在
PROGRESS.md中明确记录”已尝试但放弃的方案”,避免 AI 重复提出
避坑建议:
- 对话超过 20 轮时,主动执行
/compact(不要等自动触发) - 重要的决策信息(如”为什么放弃了方案 A”)要记录在文件中,而不是只在对话中讨论
- 如果 AI 开始重复建议或出现幻觉,不要试图纠正——直接开新会话,比纠正更快更可靠
- 每个会话开始前,花 30 秒想一下”这个会话的目标是什么”,完成后立即结束会话
总结与回顾
| 核心要点 | 关键结论 |
|---|---|
| 上下文窗口 | 共享的有限资源,系统提示 + 规则文件 + MCP + 对话历史共享 |
| 规则文件 | 控制在 300 行以内,具体规则遵守率 89%,模糊指令只有 35% |
| 精准投喂 | 用 @ 引用精准定位,优先引用函数而非整个文件 |
| 会话管理 | 一个会话 = 一个任务范围,完成后 /clear 开新会话 |
| 主动压缩 | 70-75% 时主动 /compact,不等 83.5% 的自动触发 |
| 劣化信号 | 重复、漂移、幻觉、遗忘——出现任一信号就该处理 |
| 环境工程化 | DevContainer + lock 文件 + 规则声明 = 消除环境差异 |
| 核心原则 | 上下文质量决定输出质量,管理上下文比写好 Prompt 更重要 |
延伸阅读
- 上篇:第6篇:任务拆解与多轮迭代
- 下篇:第8篇:记忆系统高效使用
本文为「AI 辅助编程实战指南」第7篇,完整系列持续更新中。