你可能有过这样的经历:
跟 Agent 干了一个多小时的活,前半段它表现还不错——读文件很准、改代码很快、方案也靠谱。但到了后半段,你明显感觉它开始"降智"了:回答开始含糊、之前说好的约定突然不记得了、甚至刚读过的文件内容它也能搞混。你忍不住怀疑:是不是模型偷偷给我降级了?
大概率不是。模型没变,变的是你喂给它的上下文。
我们来好好聊聊怎么样管理好 Agent 的上下文。接下来的内容,不仅仅是讲工具指令的教程,更多的是一套心法,让你能清清楚楚地知道,在什么情况下选择什么样的管理策略,不让模型变蠢。
上下文窗口到底装了什么?
上下文窗口里堆了那么多东西——系统指令、工具定义、环境信息、MCP/Skills 目录、CLAUDE.md 和 Memory、还有每一轮的对话消息……
不同的工具具体实现可能有所差异,但从底层往上看,都可以分为这么几层:

最底层是系统指令——Claude Code 自身的行为规则,告诉它怎么使用工具、怎么跟你交互。这部分你看不到,但它一直在那里。
第二层是工具定义——Read、Write、Bash、Grep 这些工具的参数说明。
第三层是环境信息——当前工作目录、操作系统、shell 类型、是不是 git 仓库、当前分支和最近几条 commit。这部分不大(几百 token),但它解释了一件事:为什么你换个目录开会话,Agent 就"不认识"你的项目了。
第四层是 MCP 工具和 Skills 的目录——你接的那些 MCP server 提供了哪些工具、你装了哪些 skill。注意这里加载的只是名字和一句话描述,不是完整内容。真正的工具参数 schema、skill 的完整正文,都要等 Agent 实际用到的时候才按需读进来。
第五层是 CLAUDE.md 和 Memory——你的项目指令和个人记忆。会话开始时加载一次,中途修改不会生效(要等 /clear 或重启)。
最后是对话消息——你说的话、Agent 的回复、每一次工具调用的输入和输出、读过的每一个文件的内容。这一层是逐轮增长的,而且增长速度远比你想象的快。
前面五层的体量通常相对固定。真正会随着每轮对话持续增长、最终失控的,是最后一层。
每轮对话持续增长导致上下文爆满的问题的本质:膨胀最快、最占地方的是工具调用的输入输出,其中最没价值又赖着不走的是失败日志。比如:你让 Agent 读了一个 1000 行的文件、跑了一次 grep 返回了两屏结果、还执行了一个失败的编译命令吐了一整页错误,但是失败的编译输出一整页错误,对后续任务零价值,但它没有自动清理机制——你让它跑、它跑了、输出就永远钉在上下文里了。而读文件至少读取当时有用,grep 结果可能在后续定位中用得上。错误日志是纯噪声。这和前面五层固定层形成鲜明对比——系统指令、工具定义、环境信息、MCP/Skills 目录、CLAUDE.md 与 Memory,都在会话启动时加载完毕,体量基本不变。特别注意 MCP/Skills 目录加载的只是名字和一句话描述,正文用到才读,所以它也不会跟着涨。
上下文越长,模型越笨:Context Rot
上下文窗口不是越大越好,而是越干净越好。
模型的注意力机制是有限的。当上下文只有几万 token 的时候,它能把注意力集中在你当前的问题上。但当上下文膨胀到几十万 token,大量早期的、无关的、失败的信息堆在那里,注意力就被分散了。模型必须在一堆噪声里找到跟当前任务相关的那几段信息,这件事本身就消耗了它的"智力"。
这个现象叫 Context Rot(上下文腐烂)。根据 Anthropic 工程团队的实测,大约在 300k-400k token 附近,Claude Code 的表现会开始明显下降。但这不是一条硬性的线——任务越复杂、上下文里的噪声越多,劣化来得越早。

所以"越用越傻"的真相是:不是模型变笨了,是你的上下文变脏了,比如:
某些失败的尝试
读完就不再需要的文件
每一段冗长的错误日志
这些东西都在稀释模型对当前任务的注意力。
每一轮结束都是一个决策点
现在我们理解了 context rot,解法的方向就清楚了:主动管理你的上下文,而不是放任它无限增长。
Agent 每完成一轮任务之后,你有六个选择。大部分人只用第一个,但后面五个能显著提升效率、降低运行成本。
Continue:继续对话
这是最自然的操作——直接在同一个会话里打下一条消息。上下文全部保留,不做任何清理。
在同一个任务还在继续、前面的信息还有用的场景下,这个做法确实有用。比如你刚让 Agent 读完了五个文件的代码,现在要基于这些代码做修改——这些文件内容还在上下文里,不用再读一遍,直接用就行。
但如果你要开始一个完全不同的任务,继续在同一个会话里做就不太明智了。前面那些文件内容、工具调用、讨论过程全部还在,模型的注意力要被这些无关信息分走一部分。
Rewind:回退到某条消息
这是最被低估的操作。在 Claude Code 里双击 Esc(或者输入 /rewind),可以跳回到之前的某条消息,从那个点重新开始。跳过去之后,后面的对话全部从上下文里删除。
为什么这个操作这么重要?看一个典型场景:
你让 Agent 修一个 bug,它先读了相关文件,然后试了方案 A,失败了。你说"不行,试试方案 B",它又试了方案 B——还是不行。你说"换方案 C",这次终于成功了。
整个过程下来,上下文里有什么?文件读取 + 方案 A 的全部代码和错误 + 你的纠正 + 方案 B 的全部代码和错误 + 你的第二次纠正 + 方案 C 的成功实现。方案 A 和 B 的代码、错误日志、你的纠正消息——这些全是噪声,对后续任务没有任何价值,但它们会一直留在上下文里拖累模型。

如果用 rewind,做法是这样的:Agent 读完文件、试了方案 B 失败后,你不用说"不行,试试 C",而是 rewind 到读完文件那个点,重新说"不要用方案 B,直接走方案 C,因为 B 会遇到 xxx 问题"。
这么做的结果是,上下文里只有:文件读取 + 一条精准的指令 + 方案 C 的成功实现。干净利落,没有任何失败尝试的噪声。
rewind 还有进阶用法。当你选择 rewind 的时候,除了直接恢复代码和对话,还会看到两个总结选项:

Summarize from here——总结从这个点往后的所有对话,也就是你即将丢掉的那部分。Agent 会把后面那些失败尝试里学到的教训提炼成一段交接信息,然后带着这段总结回到回退点重新开始。相当于 Agent 给"回退后的自己"写了一封信:"我试过了方案 A,不行,因为 xxx 模块不暴露那个接口;方案 B 也不行,因为 yyy。直接走 C。"
Summarize up to here——总结从对话开头到这个点的内容。这个更像是 /compact 的定点版——你指定压缩到哪里为止,而不是让 autocompact 自己判断。适合会话前半段已经很长、但你想保留后半段最近的对话细节的情况。
这两个选项的区别容易搞反,但想明白了就很自然:from here 是总结"后面要丢的",up to here 是总结"前面太长的"。前者帮你在回退时保留教训,后者帮你给前半段瘦身,两者各有自己的适用场景。
/compact:压缩当前会话
当会话已经很长、但你还在做同一件事不想换会话的时候,可以用 /compact。它让模型把当前的对话历史总结成一段摘要,然后用这段摘要替换掉所有的历史消息。
/compact 是有损压缩——模型自己决定什么值得保留、什么可以丢掉。你可以给它提示来引导方向,比如 /compact 重点保留认证重构的进展,调试过程可以丢。
好处是省事,你不用自己写任何东西,而且模型在总结时可能比你更周全——它看过所有消息,可能记得一些你已经忘了的细节。
但 /compact 有一个很容易踩的坑。
Bad Compact:为什么压缩有时候会把关键信息丢了
这个场景你可能经历过:
你在一个长会话里调试一个 bug。过程中,你顺便注意到 bar.ts 里有一个 warning,打算等 bug 修完再处理。经过几轮尝试,你找到了 bug 的根因在 foo.ts,修好了。
这时候上下文快满了,autocompact 自动触发。模型总结了这个调试过程:"调查了 X 问题,排除了方案 A 和 B,根因在 foo.ts,已修复。"然后你说:"顺便把 bar.ts 那个 warning 也修了。"
Agent 完全不知道你在说什么。因为那个 warning 只是调试过程中的一个"附带发现",不是主线任务,compact 总结的时候把它丢了。

这里有一个结构性的问题:compact 发生的时机,恰好是 context rot 最严重的时候——上下文快满了,模型的注意力已经很分散。而且模型没法预判你下一步要干什么,所以它只能根据"主线叙事"来决定保留什么。偏离主线的信息,大概率会被丢掉。
那如何应对呢?应对办法有三个:
第一,不要等 autocompact,主动 /compact。在你觉得会话开始变长、但模型表现还正常的时候,主动压缩一次。这时候模型的"智力"还在线,总结质量会好一些。
第二,给 compact 一个明确的方向。如果你知道接下来要做什么,就在 compact 指令里说清楚:/compact 保留 bar.ts 的 warning 信息,下一步要处理它。模型会按你的指引调整保留策略。
第三,把 autocompact 关掉,改成完全手动。如果你已经养成了主动管理上下文的习惯,autocompact 反而是个干扰——它总在你不希望的时候触发。在 settings.json 里加一行就能关:
JSON
{
"autoCompactEnabled": false
}
也可以用环境变量 DISABLE_AUTO_COMPACT=1,或者直接在 /config 里把 "Auto-compact" 这项关掉。
关掉之后上下文满了不会自动压缩,需要你自己 /compact 或 /clear——所以这个开关建议等你对上下文用量有感觉了(会看 /context 了)再关,否则容易在关键时刻撞墙。
还有一个更稳的做法,值得单独说。
用文件做交接:最可靠的上下文桥接方式
前面两个办法都是在"求 compact 帮我记住"。但只要还依赖模型总结,就永远有丢信息的风险——它没法预判你下一步要干什么。
真正可靠的做法是:别让上下文来承载关键信息,把它写进文件。
具体操作很简单。在 /compact 或 /clear 之前,让 Agent 先把当前状态落到一个文件里:
PLAINTEXT
把当前的进展、待办事项、已知的坑写进 HANDOFF.md,
要具体到我明天打开一个全新会话,只读这个文件就能接着干。
一份好的 HANDOFF.md 大概长这样:
MARKDOWN
# 认证重构 - 交接
## 已完成
- JWT 签发逻辑迁移到 auth/jwt.service.ts
- 登录接口已切到新逻辑,测试通过
## 待办
- refresh token 还没迁移,逻辑在 auth/legacy.ts:120
- bar.ts 有个 unused import 的 warning,顺手修掉
## 已知的坑
- 别动 auth/session.ts 的 Redis key 格式,线上有老数据依赖
- 方案 A(在中间件里验签)试过,不行:中间件拿不到 user context
然后 /clear,新会话第一句话就是"读一下 HANDOFF.md,我们继续"。
这么做比依赖 compact 总结可靠得多,原因有下面几个:
它不会丢。文件是你写的,你能亲眼检查里面有没有漏东西;compact 的总结是模型生成的黑盒,丢了你也不知道丢了什么。
它是在模型还“清醒”的时候写的。你可以在上下文只用了 40% 的时候就写交接文档,而 autocompact 触发时模型已经处在 context rot 最严重的状态。
它记得住"负面知识"。上面例子里"方案 A 试过不行,因为中间件拿不到 user context"这种信息,compact 总结几乎一定会丢——因为它不是主线成果。但恰恰是这种教训,最能防止新会话的 Agent 重蹈覆辙。
这个习惯还有个额外好处:HANDOFF.md 本身就是一份给人看的进度记录。第二天你自己回来,也不用翻聊天记录回忆昨天干到哪了。
/clear:开新会话
最干净的方式——关掉当前会话,开一个全新的。上下文里只有系统指令、CLAUDE.md 和你新写的第一条消息。
这个清理方式最简单粗暴,以前的上下文会被全部清理掉。所以在敲 /clear 之前,先问自己一句:当前会话里有没有什么是我等下还需要的? 如果有,先按上一节的做法写进 HANDOFF.md,再清。
/branch:分叉一个新会话
不过 /clear 的痛点也很明显:当前会话的上下文细节全丢了。但有时候你的需求其实是——当前会话的上下文很好,Agent 已经完全理解了项目背景和你的意图,你只是想在这个基础上开一条新的对话线。
/branch 就是干这个的。它从当前会话 fork 出一个新会话,新会话完整继承当前的所有上下文——对话历史、读过的文件、已经建立的理解,全部带过去。相当于 git 的分支操作:主线不受影响,你在分支上自由探索。
这在实际开发中非常好用。比如你花了半小时跟 Agent 讨论一个功能的设计方案,它对你的业务逻辑、技术约束、历史包袱都已经了然于胸了。这时候你想让它同时做两件事:一边实现方案 A,一边实现方案 B,然后你来对比。如果用 /clear,新会话里的 Agent 对项目一无所知,你得重新交代一遍。而用 /branch,新会话的 Agent 跟你"聊到现在"的那个一模一样,直接说"走方案 B"就行。
另一个常见场景是:你在一个会话里做完了主线功能的开发,接下来要写文档、写测试、做代码清理——这些是独立的任务,但都需要理解主线功能的上下文。与其开三个新会话分别交代背景,不如从当前会话 branch 三次,每个分支各做一件事。
Subagent:派子会话干活
最后一种上下文管理工具是 subagent。你可以让 Agent 派一个子会话去做一块工作,子会话有自己独立的上下文窗口,干完之后只把结论带回来。
判断该不该用 subagent 的标准很简单:你需要的是中间过程还是结论?
比如你想知道另一个代码库的认证是怎么实现的。如果让 Agent 在当前会话里去读,它可能要读 20 个文件、跑 12 次 grep——这些中间过程全部堆在你的上下文里,但你真正需要的只是"它用了 JWT,中间件在 auth.ts 第 40 行"这一句结论。
用 subagent 的话,所有探索过程留在子会话里,主会话只收到那段简洁的结论。中间的噪声被"垃圾回收"了。

Claude Code 自己也会在合适的时候自动启用 subagent,但你可以主动要求它这么做。一些典型的用法如下:
"起一个 subagent 去读那个代码库的认证实现,总结回来"
"派一个 subagent 根据我的 git diff 写这个功能的文档"
"用 subagent 验证一下这次改动有没有破坏现有功能"
六种策略怎么选:一张决策卡片
说了六种操作,实际用的时候怎么选?我整理了一张决策卡片,你可以存下来对照着用:
场景 | 选择 |
|---|---|
同一任务,上下文还有用 | Continue |
Agent 走错了路 | Rewind |
会话很长但还要继续工作 | /compact |
要开始一个新任务 | /clear(清之前先写 |
上下文很好,想多开一条线 | /branch |
下一步会产生大量中间输出 | Subagent |
如果你用的是 Codex,对应关系是这样的:
操作 | Claude Code | Codex |
|---|---|---|
继续对话 | 直接打字 | 直接打字 |
回退 |
| 双击 Esc 编辑上一条消息并 fork |
压缩 |
|
|
清空 |
|
|
分支 |
|
|
子会话 | Subagent(自动或手动) |
|
不管用哪个工具,核心原则是一样的:主动管理上下文,别让噪声把模型拖笨。
三个心法
讲了一堆命令,但命令是会变的——Claude Code 半年后可能又多几个新指令,Codex 的命名也可能调整。真正能带走的是下面三条心法。
心法一:把上下文当预算,而不是当仓库。
大部分人的默认心态是"窗口这么大,能装就装"。但正确的心态是"每装一样东西,都在消耗模型的注意力预算"。所以在让 Agent 读一个大文件、跑一个会输出几百行的命令之前,先问一句:这些内容,我真的需要它完整地留在上下文里吗?还是我只需要其中的一个结论?
如果只要结论,那就是 subagent 的活,你应该直接说"用 subagent 帮我 xxx"。
心法二:在模型还清醒的时候做决策。
上下文管理最反直觉的一点是——你最需要它的时候,恰好是它最不可靠的时候。等到上下文快满了、模型开始犯迷糊了,这时候触发的 autocompact 质量最差;这时候让 Agent 写交接文档,它也已经记不清前面的细节了。
所以所有的清理动作都要提前:会话到 40%-50% 就写交接文档,觉得有点长就主动 /compact,换任务立刻 /clear 或者 /branch。不要等模型提醒你,等它提醒就晚了。
心法三:关键信息要落到文件,不要留在上下文里。
上下文是容易丢的——compact 会丢、/clear 会丢、会话关了开新会话也会丢。但文件是持久的。
