用过Claude Code的人,大概率都经历过这么一瞬间:对话还热乎着,问它"刚才我们定的那个接口约束是什么来着",它愣了一下,然后给了一个似是而非的答案。不是模型变笨了,而是它的上下文已经被漫长会话里的试错记录、工具输出、日志片段塞满了。真正有价值的信息,被挤到了窗口边缘,甚至已经被压缩掉了。
Claude Code的上下文管理机制,就是解决这个问题的核心。它本质上是一个AI编程助手在长时间任务里如何保持记忆、如何丢弃噪音、如何让自己不"失忆"的设计。这篇文章我会从机制本身讲起,再结合我实际跑任务时的监控数据、压缩操作和踩坑过程,把自动压缩、手动压缩、外部记忆、上下文隔离这几块掰开揉碎。项目再做不大,配置再好看,上下文管不好,一切都是白搭。这篇东西适合正在用或者准备用Claude Code做真实项目的人,也适合所有对AI Agent上下文工程感兴趣的朋友。
1. 上下文到底在管什么
1.1 先搞清楚"上下文窗口"这四个字
任何基于大语言模型的工具,都有一个物理上限,叫上下文窗口。窗口内的内容,就是模型在生成下一次回复之前能"看到"的全部信息。Claude Code作为命令行里的编程Agent,每一次决策、每一次工具调用,本质上都是先读取整个窗口,再决定下一步动作。
这个窗口用Token来计量。Token是模型处理文本的最小单位,一个Token大约相当于0.75个英文单词,或者0.5到0.8个汉字。上下文窗口越大,它能同时处理的信息就越多,但这不是一个可以无限膨胀的东西。很多模型支持100K甚至200K Token的窗口,听起来很大?换算一下,200K Token大约相当于15万到20万个英文单词。一个大型代码仓库的全部源码量,动辄是它的几十倍。更残酷的是,Claude Code的上下文不仅仅装你让它读的文件,还装系统提示词、对话历史、工具返回结果、子代理报告,所有东西挤在同一个窗口里。
所以第一个要建立的认知是:上下文窗口是Agent的"工作内存",不是"硬盘"。硬盘可以无限大,内存不行。内存满了,系统就要换页、就要清缓存,否则整个程序就卡死。Claude Code的上下文管理机制,做的事情就是操作系统里"内存管理"那一套。
1.2 一个会话里,什么在悄悄吃掉上下文
我跑过几次长会话之后,打开上下文明细,发现真正吃Token的大头,往往不是我自己说的话,而是那些自动产生的内容。下面这些项目,每一个都在实时消耗窗口:
- 系统提示词和工具定义:这是固定开销,每轮请求都会重新计算,虽然单次不大,但架不住它是全程在线的。
- 对话历史:包括用户指令、模型回复、工具调用记录、工具返回的完整输出。这是最大的消耗来源。尤其当你让它"读一下这个文件""跑一下这个测试"的时候,工具返回的内容会原封不动地进入上下文。
- 读取的文件内容:通过@引用的文件、被程序员要求分析的大型代码块,会一次性灌进窗口。
- 子代理的总结报告:主Agent派发任务给子代理,子代理返回的结果摘要,同样占据空间。
- MCP工具调用结果:外部数据源返回的查询结果、API响应,也会堆进来。
这里最容易忽视的是工具调用历史。你不是每轮都在和它纯聊天,它的每一次"读文件""查代码""执行命令"都是一条记录,包括完整参数和完整输出。一顿操作下来,可能你只输入了十几句中文,上下文却已经被几十个文件的内容填满了。
1.3 管理上下文,本质是管理Agent的记忆力
为什么说上下文管理机制是整个Claude Code好不好用的分水岭?因为Agent的工作方式和人类程序员不同。人类做一上午项目,大脑会自动遗忘掉中间那些"试一下""报错了""换个方法"的细节,只保留"最终方案";而Claude Code在默认情况下,是把整个过程原原本本记在上下文里的。如果没有任何管理机制,一个复杂的重构任务走到后半程,它会面临三种典型症状:
第一,早期确定的设计决策被"挤走",回答开始含糊其辞;第二,工具调用的响应质量下降,因为它要在更长的历史中寻找线索;第三,输出变啰嗦,开始重复解释已解释过的问题,因为你之前的话已经被压缩或裁剪,它其实"不记得了"。
所以,上下文管理不是锦上添花,而是决定一个AI编程Agent能不能完成长链路任务的关键。谁能把有限窗口内的信息质量保持住,谁就能让Agent真正做到"干一天活不糊涂"。
2. 压缩机制:自动、手动、还有分层
2.1 自动压缩(Auto Compact):阈值触发和摘要边界
Claude Code在处理长会话时,有一个自动压缩机制,我习惯叫它Auto Compact。当上下文使用量达到一定阈值,工具会在下一次请求前,自动把早期的对话历史压缩成一份结构化摘要,用摘要替换掉原始记录,腾出空间继续干活。
这个机制的核心难点在于:摘要要保留什么?如果只保留"用户说了什么",那Agent会丢失执行细节;如果保留所有细节,压缩又失去意义。所以实际压缩时,工具会尽量保留任务目标、已完成的步骤、当前结论、未完成事项,而把过程中的试错、报错日志、重复讨论压缩掉。
这里有个关键认知:自动压缩是有损的。压缩掉的原始文本不会原样恢复,Agent对新任务的判断,依赖的是那份摘要写得够不够好。我实测下来,摘要质量在简单任务上很不错,但在非常复杂、细节纠缠的项目里,偶尔也会丢掉某个关键约束。所以在长任务里,我会把它当作"兜底机制",而不是"唯一依赖"。如果你发现它在压缩后开始跑偏,少责怪模型,多想想怎么用手动方式把关键信息固化下来。
2.2 手动压缩:主动丢掉低价值内容
自动压缩是工具弹出来的,被动等待;手动压缩是你主动发起的,工具主要提供两个操作思路:一是使用内置的压缩命令,二是直接重开会话。先说压缩命令,在对话中输入相关指令,工具会对当前会话做一次摘要压缩,把历史文本替换成精炼版本。
什么场景下我建议主动手动压缩?一个判断标准是:你觉得最近十轮对话里的信息密度明显下降了,大部分内容是在确认、试错、重复废话。这时候不要心疼那些还没解决的问题,你真正需要保留的是"结论",而不是"推导过程"。手动压缩之前,最好先自己心里过一遍:当前任务目标是什么?已经完成了哪些?下一个要做的动作是什么?如果这三个问题你能在一分钟内回答清楚,压缩出来的效果通常就很好;如果连你自己都说不清楚,那再压缩也没用,应该重开一个会话,把背景重新讲一遍。
注意,手动压缩不是万能的。有一次我在一个特别长的会话里手动压缩后,发现它对某个第三方库的版本号记错了,因为版本号藏在早期一次对话里,压缩时没有把它当重点。从那以后,重要信息一定落在文件里,而不是落在会话里。
2.3 从"压缩旧记录"到"少制造记录":会话重开与分层记忆
压缩做得再好,也只是事后补救。更聪明的思路是:从源头减少无效上下文的产生。具体来说,就是要习惯"重开会话"。
我为同一个项目做过很多次会话,每次新开一个会话,本质上是让Agent站在一个干净的窗口前,重新思考问题。这种做法的好处是立即拥有了全新上下文;坏处是它会把之前会话积累的所有信息忘光。所以重开会话的前提是:长期记忆必须转移到外部。这就是CLAUDE.md这类记忆文件存在的意义,我后面会详细讲。
把记忆分成两层来看:长期记忆存在文件里,比如CLAUDE.md、TODO.md、设计文档;短期记忆存在会话里,也就是当前任务的执行过程。长期记忆要写得像一份"给AI的入职手册",让它每次新开会话都能快速进入状态。短期记忆则尽量短小精悍,把任务拆小,在一个会话里聚焦一件事。做完这件事,立刻重开。这套"文件管长期、会话管短期"的分层策略,是我后期用得最顺手的模式。
3. 输入侧控制:让该进来的进来,不该进来的别进来
3.1 @引用与精准投喂:少贴整文件,多指文件路径
很多人在Claude Code里习惯性地把整个文件内容粘贴到对话框里,然后说"帮我改这里"。这是最浪费上下文的做法。一个几千行的文件,可能真正相关的只有几个函数,但你把整份文件塞进去,它就整体进入了上下文,其他任务需要的空间就被挤占了。
正确做法是用@引用语法,把文件路径交给工具,让它按需读取。你可以针对性地说"读取src/utils/format.ts里的formatDate函数,分析它存在的问题"。这样工具可能只读取文件的指定部分,或者读取后返回更精炼的分析,而不是把整个文件从头到尾输出一遍。这就像你让一个同事帮你改代码,你不是把整个仓库打印出来递给他,而是告诉他文件在哪、函数叫什么、问题是什么。
另外,在做代码库分析时,限制扫描范围同样重要。不要一上来就说"全仓库搜一下哪里用了这个接口",而是尽量缩小目录范围,比如"在src/modules/order/目录下搜索"。范围越小,工具返回的文件列表和内容摘要越短,上下文消耗越少,响应也越快。这是成本最低、见效最快的上下文优化手段。
3.2 CLAUDE.md是长期记忆,不是临时便签
CLAUDE.md是Claude Code里的一个约定文件,放在项目根目录,会话启动时会被自动加载。它承担的功能是:把这个项目的关键背景、技术栈、代码风格、常见约束告诉Agent,让每次会话都有共同知识基础。
写CLAUDE.md有一个核心原则:只放高频、稳定、全局的信息,不要放临时、易变、琐碎的信息。什么是高频稳定?项目用的是什么框架、构建命令是什么、测试怎么跑、目录结构怎么组织、有哪些必须遵守的编码约定。什么是临时琐碎?"今天下午在改登录模块""这个函数昨天重构过""等我调研完再定"——这些信息会话里说就行了,写进CLAUDE.md只会浪费每次会话的加载成本。
我在CLAUDE.md里还会放一个"当前项目状态"小节,专门记录类似"订单模块重构进行中,已完成数据层,待做接口层"这样的进度信息。每次完成一个里程碑就更新一下。这样新开会话时,Agent用很短的开销就能接上进度,根本不需要长对话。这个文件是上下文管理最好的朋友,用好它,你会少踩一半的坑。
3.3 子代理与工具调用:上下文隔离的工程化思路
Claude Code支持把任务派发给子代理来执行。子代理有自己独立的上下文环境,执行完任务后,只把结果摘要返回给主代理。这个设计天然就是做上下文隔离的。
怎么理解它的价值?假设你在做一次全库重构,需要先分析A模块与B模块的依赖关系,再修改调用链。如果你在主对话里让Claude Code一点点分析每个模块,所有中间分析过程都会堆在主上下文里。如果派发给子代理去做"遍历src/api下所有接口,返回它们被调用的位置清单",子代理自己会在独立环境里处理,最后只给主代理返回一份精简的清单。
这就像管理一个团队,你不要求下属把每个文件怎么读的都同步给你,你只要他交一份结论。子代理就是"下属",主代理就是"主管"。我遇到大型批量任务时,会主动把它拆分成多个小任务,一个个派发出去,而不是在一个会话里硬扛。这种方法节省的上下文量,有时候比我压缩十次还多。
4. 实操:一个大任务里的上下文监控与调优
4.1 学会看"仪表盘":状态行与 /context 的读法
Claude Code的界面上有一个状态行,会实时显示当前上下文的占用比例。很多朋友从不看它,其实它就是你的"内存仪表盘"。我一般会在三种时刻看它:任务开启时、工具返回大批量结果后、以及任务进行到中段时。
一次真实的会话里,我观察到的状态行是这样的:任务开始,占用14%;读取两个核心文件后,升到31%;让工具跑了一次全仓库关键字搜索,结果返回后直接跳到67%;又让工具读了一个大型配置文件,升到83%。等到我发出"帮我重构这个模块"的指令,它开始含糊其辞时,占用已经接近92%。
除了状态行,还可以用上下文详情命令查看细分数据,看看是哪部分占用了大头。实际输出大致长这样:
上下文占用:92%(180K / 195K Token)
历史消息:61%,文件内容:23%,系统提示:9%,工具结果:7%
这个明细特别有用。看到历史消息占61%,你就知道该压缩或重开了;看到文件内容占大头,你就知道刚才读取的东西太多,应该收着点。它帮你把"凭感觉觉得上下文有点多"变成了"精确知道多在哪里"。
4.2 实战记录:一次重构任务从90%占用到恢复正常
以我在模拟项目X里的真实经历为例。那是一个跨平台系统,代码量中等,但模块依赖很复杂。我的任务是重构订单模块的接口层,同时保留对外API的兼容性。任务进行到第二阶段时,上下文占用已经到了90%,表现为:它开始忘掉"兼容性"这个约束,生成的代码里直接改掉了公开接口签名。
我做了三步处理。第一步,调出上下文明细,确认罪魁祸首是历史消息,占了61%。第二步,手动执行压缩命令,压缩前我先把"兼容性要求"和"已完成的数据层改动"写进了CLAUDE.md,压完以后占用降到37%。第三步,压缩后我没有继续在旧会话里堆任务,而是把下一步任务拆出来,重新开了一个会话,用@引用指定了要修改的文件,并在开头明确交代了约束。
结果非常明显:新会话里的它状态清醒,知道哪些接口不能动,哪些文件是调用方,改完还主动列出了兼容性影响清单。整个过程没有比之前多花多少时间,但上下文占用从90%一路维持在40%上下。这个案例最值得记住的一点是:压缩不是让你继续无限堆叠,而是给你一个"中场休息"的机会,让你把关键信息转移到文件里,然后轻装重新出发。
4.3 沉淀成SOP:上下文管理的日常操作清单
经过多次调优,我给自己定了一套固定的上下文管理流程,现在执行长任务基本不会翻车:
- 每次新开会话,先确认CLAUDE.md里更新了当前项目进度。
- 任务开始前,明确本轮的目标,只读取与目标相关的文件,不要做全局扫描。
- 运行状态行显示占用超过70%时,先停下来评估:还需要多少轮能结束本轮任务?如果还需要很多轮,就手动压缩或重开会话。
- 压缩前,把必须保留的约束、进度、决策全部写进CLAUDE.md或项目文档,不要指望摘要替你记牢。
- 尽量把大任务拆成子任务,能用子代理派发的绝不自己硬扛。
- 一个任务完成就结束会话,新任务永远从全新上下文开始。
这套SOP不依赖任何模型升级,纯粹是使用习惯的调整。我身边不少同事照此操作后,长任务的完成质量提升非常明显。
5. 常见问题与排查技巧实录
5.1 症状对照表:先判断是上下文问题还是别的问题
很多朋友遇到模型表现变差,第一反应是"模型不行了""工具出Bug了",其实在Claude Code的场景下,八成是上下文管理出了问题。我整理了一张典型的症状对照表,方便快速定位:
| 症状 | 可能原因 | 排查动作 |
|---|---|---|
| 回答含糊,重复问相同问题 | 上下文被挤占,重要信息被压缩或裁剪 | 查看状态行和上下文明细,必要时压缩或重开 |
| 改代码时忘了早期定下的约束 | 早期对话被压缩丢失细节 | 把约束写进CLAUDE.md,压缩前先固化 |
| 执行长任务中途开始变慢 | 上下文过大,每轮处理时间变长 | 检查占用比例,超过80%就主动压缩 |
| 工具返回结果巨大,占用飙升 | 读取文件过多或全库搜索范围过大 | 改用@引用指向具体文件,缩小搜索范围 |
| 输出啰嗦,反复解释旧内容 | 上下文被噪音填满,模型在"重新推理" | 压缩或重开会话,清理试错记录 |
排查的顺序也很重要:先看上下文占用,再看明细分布,最后再决定是压缩、重开还是缩小范围。不要一上来就重装工具、清缓存,那样往往解决不了问题。
5.2 信息丢失后的补救:把关键内容"写进文件里"
压缩最让人头疼的问题是"丢内容"。有一次我让它持续开发一个功能,中途自动压缩后,它把我早前确认过的一个字段命名约定丢了,生成了不一致的代码。排查后发现:当时的命名约定只存在于对话历史中,被压缩时当成了"临时讨论"处理,没有进入摘要。
从那以后我有了一个铁律:任何你想让Agent一直记住的东西,都必须以文本形式存在于项目里。具体做法是,在项目的docs或根目录维护一个"当前决策记录"文件,每确认一个重要决策就追加一条。CLAUDE.md里放"全局约定",决策记录里放"本次任务的决定"。这样就算会话压缩、重开、甚至换一个人来继续,信息都不会丢。
压缩后如果发现它真的忘了什么,不要试图在旧会话里"回忆"给它听,那样只会继续占用上下文。正确的做法是:把丢失的信息补进CLAUDE.md或决策记录,然后重开会话,让它重新读取。新会话的上下文环境是干净的,读到的东西才是真正有效的。
5.3 预防上下文爆炸的几条硬经验
最后分享几条预防性经验,每一条都是我用实际代价换来的:
第一,不要在一个会话里同时进行多个无关任务。比如"帮我重构订单模块"和"顺便看看用户模块那个Bug"会在同一个上下文里留下大量互相干扰的信息。一次只做一个事,做完就分道扬镳。
第二,遇到报错日志,不要原样把几百行堆给它。先自己截取关键行,或者让它"只读最后20行错误堆栈"。几百行日志一进去,相当于几万Token直接蒸发。
第三,限制工具输出的返回量。在请求里明确"不要输出完整文件,只要函数签名和关键逻辑"这类约束。你对输出提要求,它返回的内容就会短得多。
第四,养成"阶段性复盘"的习惯。任务做了三分之一时,停一下,把已完成的事、发现的问题、下一步计划写进CLAUDE.md。这个动作等于给Agent做一次"记忆整理",比任何压缩命令都可靠。
第五,永远给重要信息一个"文件落点"。我在实际使用中反复验证,对话里的信息是易失的,文件里的信息是永久的。宁可多维护几个文档,也不要指望一个超长会话能承载整个项目的记忆。
这些经验,说到底是把"上下文管理"从工具机制变成了个人习惯。Claude Code的自动压缩、手动压缩、CLAUDE.md、子代理隔离,每一套机制都在帮同一个忙:让有限窗口里的信息永远保持在高价值状态。工具能做的已经够多,剩下的就看使用者怎么配合了。
我还是那句话,每次开始新任务前,先花半分钟问自己:这个会话最多还能撑多久?最重要的信息放文件里了没有?先把这两个问题解决了,再接大活也不迟。