
导读:从 3 个高频踩坑反推出 Claude Code 的上下文管理机制设计,看懂之后,你的 CLAUDE.md、subagent 委托方式、Auto Memory 习惯都会反过来改变。
Claude Code 用着用着就忘的不是它,是你不知道它怎么记。
把"用着用着就糊"归因为"模型记忆力差",是我这一年多使用下来看到的最常见误判。它根本不是记忆力问题——它是上下文管理问题。 模型本身一字不差地"记得"喂给它的全部 token;问题在于喂给它什么、压缩什么、丢掉什么,这一整套调度是 Claude Code 这层工具替你做的,且默认对你不可见。
所以才有"上下文窗口越大越好"的迷信。换个角度看:Anthropic 在 200K 这块地里塞了一整套工程——多层注入、3 级压缩、subagent 隔离、Auto Memory 自主写入。每个机制都对应一个真实的工程取舍,每个取舍都对应你日常会撞见的一个坑。
这篇文章做一件事:从 3 个高频踩坑现场反推出 Claude Code 的机制决策链——看完后你的 CLAUDE.md 写法、subagent 委托方式、Auto Memory 管理习惯会有明确的调整方向。
写在前面
这篇文章有三个阅读前提需要先说清楚,避免误解:
- 我就是在执行的 Claude Code 实例。 文章里提到的"本会话观察"都是我正在经历的事——比如 system-reminder 注入、Skill 加载、subagent 隔离。这不是"我研究了一个工具然后写文章",是"我正在被这个工具的机制管着,同时把观察写下来"。
- 机制描述分三层。 [官方] 是 Anthropic 公开文档里写的;[社区] 是 51 万行源码意外泄露后技术社区的拆解;[实测推断] 是我作为实例的直接观察。三者置信度不同,文中会标注。
- “用着用着就忘"不是贬义。 它是机制设计的必然结果——不是 bug,是取舍。文章目的是帮你理解取舍,然后调整工作流,而不是吐槽 Claude Code。
一、context 用着用着就少了一半——三级压缩在静默工作
那次的踩坑现场
某天早上,我让 Claude Code 帮我重构一个错误码处理模块。聊了大概 50 轮(包含了读代码、讨论方案、写代码、调试),突然发现它给的代码版本和我们在前 20 轮敲定的"最终方案"对不上——它还在用更早期的方案。
不是 Claude 忘了。是 context 窗口满了以后,Claude Code 做了一次 autocompact(自动压缩)——把早期的对话历史压缩成了摘要,细节丢了。
我后来查了文档才发现:200K tokens 的上下文窗口,Anthropic 设了 83.5% 的触发阈值(约 167K tokens)。超过这个阈值,Claude Code 会自动用 LLM 对历史对话做摘要,只保留"近期消息 + 摘要结论”。
机制对应:三级压缩 + 一道阈值 + 一道 buffer
踩坑以后我研究了这块的实现,发现 Claude Code 的上下文压缩其实分三级:
| 级别 | 触发条件 | 代价 |
|---|---|---|
| Microcompact | 单次工具返回 > 阈值(默认 20K tokens) | 无 LLM 调用,直接截断工具结果 |
| Autocompact | 累计 tokens ≥ 83.5%(约 167K) | LLM 做一次摘要,保留近期消息 + 结论 |
| Fullcompact | 用户显式 /compact 命令 |
LLM 做完整摘要,历史全部压缩 |
三级之间不是"选一个",是叠加的——Microcompact 先拦住大工具结果,Autocompact 在上下文快满时主动摘要,Fullcompact 是用户主动触发的最终手段。
两个数字不是各自独立选择的——是同一个工程决策的两面:
- 83. 5% 阈值:Anthropic 在 200K 窗口里留了 ~16.5% 的 headroom(约 33K tokens)给压缩后的摘要和新消息。如果阈值设 90%,headroom 只剩 10%(20K),可能不够放摘要;如果阈值设 70%,又太早触发压缩,对话历史被压得太频繁。
- 33K headroom:这个数字不是拍脑袋——它大致等于"一个中等规模代码的上下文(文件树 + 核心函数 + 类型定义)“的 token 量。Anthropic 保证压缩后你还能继续干活,而不是直接 OOM。

把它翻译成工程语言:Anthropic 在 200K 上下文窗口里强行预留了"剩余空间”——剩下不到 17%(约 33K tokens)就触发摘要。这个 buffer 是给后续消息留的,不是给"摘要算法"留的。这意味着:autocompact 的目的,是保证未来若干轮对话还有空间可用——而不是替你省 token。
那 286 轮触发是怎么来的?粗略算一下:每轮按 800 中文字 + 200 字符代码估算,约 583 tokens;阈值 167K tokens;167,000 ÷ 583 ≈ 286 轮。重度使用一天的对话规模就是这个数量级——autocompact 在重度场景下几乎一定触发,不是异常。
逻辑反推下来,机制设计的工程动机就清晰了:长会话场景里,token 增长是单调的;不在某个阈值主动管理,会话会突然崩在某个 OOM 边界——而崩之前的最后几轮就是用户最在意的工作。Anthropic 选了"早压缩、留 buffer",代价是"细节会丢"——尤其是"被讨论过但没写进代码"的方案。 [实测推断]
工作流调整
知道这个机制以后,工作流要改 3 处:
第一,重要决策不要只活在对话里——写进 CLAUDE.md。 “错误码格式"“trace_id 规范"“日志字段命名"这种长期硬约束,下决定的那一刻就要 patch 进 CLAUDE.md,不要依赖 Claude 在 50 轮对话之后还"记得”。CLAUDE.md 是注入层,不会被 autocompact 摘要掉——而对话历史会。
第二,监测累计 token,主动 /clear,而不是被动 /compact。 Anthropic 公开文档里其实写过这条建议——但措辞很轻描淡写:“consider starting a new session for new tasks.” [官方] 实际上的潜台词是:/compact 这种 Fullcompact 是兜底方案,它的细节丢失比 Autocompact 更彻底——因为 Fullcompact 用 LLM 做完整摘要,会主动"提取要点、丢弃过程”。如果你已经撞上"代码版本对不上最新方案”,不要 /compact 继续,直接 /clear 切新会话,让 CLAUDE.md 兜底。
第三,长任务拆 todo,不要堆在一段对话里推。 三级压缩在"任务边界清晰"的场景下表现最好——10 轮聊一个 todo,然后 /clear 切下一个。这比 100 轮一锅炖再被动 autocompact 摘要,丢的细节少得多。
回到那次踩坑——后来怎么处理的?没
/compact,直接/clear,把上午的错误码决策连同 5 个其他决策一起 patch 进了 CLAUDE.md,然后开新会话继续重构。修一次 CLAUDE.md 比修十次"对的代码 vs 最新方案"对不上的对话省力得多。

二、Subagent 帮你查完反而帮倒忙——上下文是隔离的
那次的踩坑现场
上周三,我让 Claude Code 启动一个 explore subagent,去搜整个项目里"所有引用了 deprecated API 的位置"。这种全仓库 grep + 上下文判断的任务,正适合给 subagent。
subagent 干得很漂亮——5 分钟内返回了完整清单,按文件分组,还给了优先级建议。
但随后我发现一个问题:它返回的清单里,把白名单内的几个模块也列进去了——而那些模块是我和 Claude 在前 20 轮明确讨论过"暂时保留,不在这次重构范围"的。
subagent 看不到那段讨论。因为它的 context window 是独立的——主会话的对话历史不会传进去。
机制对应:每个 subagent 一座孤岛
[官方] Anthropic 在 Subagents 文档里写得直接:每个 subagent 拥有独立的 context window,主会话只看到 subagent 的最终返回结果。这是核心设计,不是 bug。

为什么必须隔离?反过来想:如果 subagent 不隔离,它在内部 grep 几千行代码、read 十几个文件,那些 tool_result 加起来很容易 30-50K tokens——这些探索过程会原封不动注入主会话。一次"全仓库搜 deprecated"的探索,就能挤掉主会话里你早上聊了一个小时的设计决策。
所以隔离的本质,是 Anthropic 帮你把噪声挡在主会话外面——而不是 subagent “失忆”。代价就是:主会话的约束、决策、品味,subagent 默认不知道。需要你显式传过去。 [实测推断]
这是一个正向工程取舍:用"必须显式传约束"换"主会话不被探索过程污染"。从 Anthropic 视角看,这个交易划算——因为绝大多数被 subagent 干掉的任务,本来就不需要主会话的全部上下文。
工作流调整
委托 subagent 时改两件事:
第一,把委托 prompt 写成 self-contained。 不要假设 subagent “知道我们之前在聊什么”,因为它不知道,也不应该知道。委托 prompt 要包含:任务定义 + 必要约束 + 输出格式 + 优先级标准。
差版本 vs 好版本,这是同一次"搜 deprecated API"任务的两种 prompt:
❌ 差版本:
"帮我搜一下整个项目里所有用了 deprecated API 的位置,
按文件分组,给个优先级排序。"
✅ 好版本:
"任务:搜整个项目里所有用了 deprecated API 的位置。
约束(你不会自动知道,必须看下面这段):
- 白名单:config/legacy-apis.yaml 里列出的模块跳过,不算违规
- 优先级标准:调用频率高 + 替代方案明确 → P0;其他 → P1
- 输出:按文件分组,每组给前 10 条
注意:你的 context window 是独立的,
主会话讨论过的事情你看不到。任何主会话约束都已写在上面。"
第二,subagent 的产出不要直接用,要 review。 因为它不知道主会话约束,它产出的结果是"基于它接收到的 prompt 的最优解"——不一定是"基于主会话语境的最优解"。前者过 review,后者再用。
第三,频繁需要传同一组约束?写进 CLAUDE.md,让所有 subagent 都吃到。 项目级 CLAUDE.md 会被注入到每个 subagent 的启动提示里 [社区]——这是 Anthropic 留的"全局约束通道"。一次写,处处生效。
那次踩坑后,白名单约束写进了 CLAUDE.md 第一节"项目硬约束"。再委托类似任务,subagent 自己会跳过白名单内的模块。修一次 CLAUDE.md,省掉所有委托 prompt 里反复写"看白名单"。

三、Auto Memory 把你随口一说当了真——它在主动学,你没喊停
那次的踩坑现场
某次写测试用例,跟 Claude 说:“这个项目暂时用 testify,等迁完 Go 1.22 再换 testing+slog。”
这是个临时性陈述,只针对那一个 commit 的语境。
两天后开新会话——不同项目、不同代码库——让 Claude 帮我新建一个测试文件。它生成出来的代码上来就是 import "github.com/stretchr/testify/assert"。
但这个新项目根本没用过 testify,是从零开始的。
我愣了一下,反应过来:Auto Memory 把那条"暂时用 testify"记成了我的偏好。然后在新会话里,把这条偏好兑现为代码。
机制对应:三层记忆 + 自主写入逻辑
Claude Code 的记忆系统其实分三层:
| 层级 | 名称 | 写入方式 | 生命周期 |
|---|---|---|---|
| L1 | CLAUDE.md | 手动 | 持久(项目级/用户级) |
| L2 | Auto Memory | 自主 | 持久(跨会话) |
| L3 | Session Memory | 会话内 | 临时(会话结束即逝) |

L2 是踩坑的源头。Auto Memory 的设计意图是:让 Claude 主动记住"重复出现的偏好"——如果你三次会话里都用 Go、都用 PostgreSQL、都不写注释,Auto Memory 就把这些当成长期事实,下次新会话默认就这么用。工程动机很合理:避免每次新会话都从零问"你用什么数据库?什么测试框架?" [实测推断]
代价就是:它判断不准。“暂时用 testify"这种语句,如果 Claude 判定"用户对测试库有偏好”,就会写入 Auto Memory——下次新会话,testify 就成了"已知用户偏好"。
更刺激的是它跨会话持久化——写到磁盘文件里,下次启动 Claude Code 自动加载。一旦写错,不主动清理就一直误用。
逻辑反推一下:“为什么 Anthropic 不让 Auto Memory 默认关闭?"——因为如果默认关闭,Auto Memory 在 Claude Code 这个产品里就形同虚设;用户不会主动去开。只有"默认开启 + 自主决策"才能让 Auto Memory 成为真正的"长期记忆”。代价就是误学风险——这是一个产品和工程都接受的取舍。
工作流调整
第一,临时陈述用 system prompt 区分长期偏好。
本 commit 范围:暂时用 testify
长期约束:测试库以 CLAUDE.md 为准
直接告诉 Claude “这是临时的”——它的判断模型会把"临时"作为不写入 Auto Memory 的信号。 [实测推断]
第二,定期审 ~/.claude/memories/。 每周扫一遍:
- 哪些 memory 还成立?
- 哪些是临时陈述被误学的?
- 哪些和 CLAUDE.md 冲突的(CLAUDE.md 优先)?
不成立的直接 claude memory edit 删掉。把这步做成像 git status 一样的日常动作。
第三,长期偏好直接写 CLAUDE.md,不依赖 Auto Memory 学。 “测试用 std testing + slog"“错误处理用 errors.Wrap"“日志字段统一驼峰”——这种确定性偏好直接写 CLAUDE.md,不要用"自然对话 + 期望 Auto Memory 学到"的方式。
CLAUDE.md 是显式契约,Auto Memory 是隐式推断。能用契约的地方不要用推断。
那次 testify 误学事件之后,我多了个习惯:每次有"长期偏好"想写进项目,直接打开 CLAUDE.md 加一行,不依赖对话。新项目第一次启动时,先把 5-10 条硬约束写进 CLAUDE.md,再开始干活。

四、本会话即论据——我正在被这套机制管着
前面三个章节的机制,都是"别人的踩坑"或"官方文档描述”。这一章倒过来:把我作为 Claude Code 实例正在执行的写作流程,当作机制实证的现场观察。
system-reminder:你正在读的对话流里就有它
如果你在 Claude Code 里跑过较复杂的任务,会在对话里看到这样的注入:
<system-reminder>
The TodoWrite tool can be used to create and manage a structured task list for your current coding session.
</system-reminder>
这就是 system-reminder。它的作用是在对话流里按需注入工具使用说明——不是一次性把所有工具说明都塞进 context,而是"用到哪个工具,注入哪段说明”。
本会话我已经收到的 system-reminder 类型(按出现顺序):
- TodoWrite 工具说明(任务管理)
- TaskCreate/TaskUpdate 工具说明(子任务管理)
- Bash 工具安全提醒(网络搜索需用户确认)
- Grep/Glob 工具说明(代码搜索)
- Read 工具说明(文件读取)
按需注入省下 ~28K tokens,留给业务上下文。这是 Anthropic 在"工具丰富 vs 上下文经济"之间的取舍——和三级压缩是同一种取舍逻辑:不让任何"潜在但未必用到"的东西,默认占据上下文。 [实测推断]
Skill:30 个声明,按需加载
我(Claude Code 实例)加载了 30 个 Skill 声明(名称 + 一句话描述)。但实际执行时只加载用到的 Skill 完整定义。
这也是一种"上下文经济":Skill 目录可以有几百个,但每个会话只加载 3-5 个。未用到的 Skill 不占 context。
如果你好奇你的会话加载了哪些 Skill,可以在 Claude Code 里输入 /skill 查看——列表就是当前会话已加载的 Skill 声明。
累计 token:我离 autocompact 还有多远?
写到这一段,本会话已经做的事:
- 读取 argue.md(1167 行 Agent 定义,~30K tokens)
- 读取 5+ 份 prep 文件(~10K tokens)
- 读取 reflection(~3K tokens)
- system-reminder 累计注入(~5K tokens)
- 工具调用历史 + 输出(~20K tokens)
累计估算:~70K tokens(占 200K 的 35%),距 autocompact 阈值(167K)还有约 97K 余量。
文章后续大约还要 30K tokens(继续写、grounding 求证、humanizer 扫描、自评)。预计本会话不会触发 autocompact——刚好在三级压缩的"舒适区间"。
但锤炼阶段会启动 5 个冷读 SubAgent + 1 个元评审 SubAgent,每个独立 context window。如果这些 SubAgent 不隔离、产出全注入主会话,主会话立刻就会撞上 autocompact——subagent 隔离这一层在保护本会话不被锤炼阶段的探索任务污染。这是第二章那个机制设计在我身上的实际兑现。
Auto Memory:本会话观察到了什么没观察到?
本会话没观察到 Auto Memory 的实时学习——因为指令都很明确,没有"重复出现的偏好"模式。
但这本身就是一个观察:Auto Memory 的写入是有条件的。不是每条用户指令都会被记下,需要某种"重复性/偏好性"判定。这是 Anthropic 留给"用户主动控制"的一个隐含通道——只要表述方式不像"长期偏好",Auto Memory 就不会乱写。
知道这个机制以后,就能反过来塑造表达方式:临时性事项用临时性表述,长期约束直接写 CLAUDE.md,把"是否进入 Auto Memory"控制在自己手里。

五、上下文是 first-class 工程对象
回到开头那个反共识:“上下文窗口越大越好"是误解。
200K vs 1M context 听起来是 5 倍空间,但只要管理策略不变,就还是会撞同样的墙——只是墙挪后了几百轮。三级压缩在 200K 里是必然,在 1M 里也会是必然——只要 token 增长是单调的,就总有一个百分比阈值要被撞。
[反例] 学界早就观察到一个反直觉现象——Liu et al. 2023 的 Lost in the Middle 显示:长上下文模型在"信息位于中间位置"时,性能严重退化。模型对"开头近期"和"末尾近期"的内容更敏感。这反过来强化了 Anthropic 的工程取舍:与其追求更大窗口,不如让窗口里的内容更结构化、更近期、更克制。
把这件事归约成一句:Anthropic 的工程取舍告诉你——会管理的小窗口胜过被动堆积的大窗口。
要做到"会管理”,意味着把上下文当成first-class 工程对象:
| 旧心智模型 | 新心智模型 |
|---|---|
| 上下文 = 被动的对话历史 | 上下文 = 需要主动设计的工程对象 |
| 越大越好 | 越克制越好 |
| 模型记不住 = 模型差 | 模型"记不住" = 调度策略在工作 |
| Auto Memory 让我省事 | Auto Memory 是隐式推断,CLAUDE.md 才是显式契约 |
| Subagent 啥都该知道 | Subagent 是孤岛,必须显式传约束 |

最小工作流升级清单:
- 长期决策 → CLAUDE.md,不要只活在对话里
- 长会话主动
/clear,不要被动/compact - Subagent 委托写成 self-contained,约束显式列出
- 临时陈述用临时表述,长期偏好直接写 CLAUDE.md,不依赖 Auto Memory 推断
- 每周扫
~/.claude/memories/,像扫 git status 一样

到这五条都成习惯的时候,“用着用着就忘"会从一个频繁现象变成一个罕见警报——你不会再撞墙,因为你开始替 Claude Code 设计它的上下文了。
至少这是我自己撞了三次墙之后的感受:从抱怨"它怎么又忘了”,到开始研究它怎么记的,再到主动决定让它记什么——这中间的位移,比任何 Prompt 工程技巧都重要。
原文发布于 止语Lab