Claude Code session 进行 45 分钟。早些时候你定了「不用 GraphQL、坚持 REST」。突然它建议加一个 GraphQL resolver;或者第四次重读 CLAUDE.md 和 package.json、像没见过一样;或者它现在的 plan 和 10 分钟时的 plan 互相矛盾。
对话长到一定程度,auto-compact(自动压缩)触发了。Claude Code 把前面的对话摘要成一块来塞进上下文窗口,而那些具体约束(不用 GraphQL、用 vitest、在 apps/web/ 改)没能挺过摘要。Agent 现在用的是一份有损的「你说过什么」记忆。
最快的修法(截至 2026 年 6 月):如果压缩刚刚发生,按两下 Esc,选 Restore conversation 回滚到摘要之前,然后立刻用一条消息重申你的约束。如果你已经走得太远,就开一个干净 session、带上 5 行 handoff(见下方 Step 1)。别试图把 agent「劝」回记忆——用显式状态重新锚定。
compaction 到底怎么工作的
搞清机制就知道该用哪种修法。截至 2026 年 6 月,Claude Code 有三层,从轻到重依次触发:
- Microcompact——session 一长,它会在 API 调用前静默运行,清掉旧的 tool 结果(大约保留最近 5 个,其余用占位符替换),且不调用模型。所以你早期
Read的某个文件会「消失」,但你定的决定通常还在。 - Auto-compact——重量级那一趟。上下文逼近窗口上限时(Sonnet 4.6 / Opus 4.7 默认 200K-token 窗口的约 95%),Claude Code 暂停,把整段对话摘要成一块结构化内容,然后从这份摘要继续。就是这一趟把你早先的约束丢了。
- 手动
/compact——同样的重量级流程,但由你按自己的节奏触发,而且你能指定保留什么。
状态栏会显示 Context left until auto-compact: X%。这个数字变小,就说明重量级那趟快来了。
常见原因
按命中率从高到低:
1. 上下文撞满、auto-compact 摘要前面对话
context 满了(约窗口的 95%),整段对话被摘成一块。摘要保留大意、丢具体——「讨论了方案」取代「定了不用 GraphQL、用 vitest、不允许 inline style」。然后 agent 做的决定就和被摘掉的内容不一致。
如何判断:出现了 “Conversation compacted” / “Compacting conversation…” 之类消息——之后就要预期有损记忆。跑 /context 可以确认 conversation 那块缩小了。
2. 长 tool-call 链比预期更快吃光 context
每次 Read、Grep、Bash 都加 context。5 次大文件 Read 可能 20K token——auto-compact 比对话长度暗示的更早触发。
如何判断:session 感觉不长但工具调用密集。跑 /context——高占用来自 Files / tool output 那一类,而不是你的消息。
3. 摘要保留大目标、丢了具体约束
Auto-compact 留「user 想加 billing feature」,丢「user 说:不要 GraphQL、只改 apps/web/、测试 vitest」。任务在做、护栏没了。
如何判断:输出在 task 上但违反了你早先设定的约束——约束被丢了,task 还在。
4. 约束只写在 path-scoped rule 或嵌套 CLAUDE.md 里
这条最隐蔽。按 Anthropic 的 context-window 文档,compaction 并不对所有指令一视同仁:
| 约束写在哪 | auto-compact 之后 |
|---|---|
项目根 CLAUDE.md、无 scope 的 rule | 从磁盘重新注入(存活) |
Auto memory(MEMORY.md) | 从磁盘重新注入(存活) |
带 paths: frontmatter 的 rule | 丢失,直到再次读到匹配文件 |
子目录里嵌套的 CLAUDE.md | 丢失,直到再次读到该目录下文件 |
| 你只在对话里说过的决定 | 丢失(被摘掉了) |
所以写在 .claude/rules/api-conventions.md(scope 到 src/api/**)里的约束会在 compaction 后消失,只有下次 Claude 碰到匹配文件时才回来。
如何判断:丢的那条 rule 是 path-scoped 的、或在嵌套 CLAUDE.md 里,而且 agent 恰好在 compaction 后不再遵守它。修法:把它挪到项目根 CLAUDE.md。
5. Tool 报错占 context 没贡献信号
Bash 命令连失败 5 次,每次错误 trace 都进 context。Microcompact 本该清掉旧 tool 结果,但一墙相同的 permission-denied 仍会把你更快推向重量级那趟。
如何判断:session 里很多重复 tool 错误——只加噪音不加信号。
6. Plan mode 出长 plan,code 还没开始就吃满 context
100 行 plan + 你的修改 + 用来验证 plan 的 tool read = 真干活之前 context 已半满。第一个长代码生成就触发 compaction、把 plan 丢了。
如何判断:session 开局花大量时间做 plan,bug 在开始 codegen 时出现。
最短修复路径
按收益从高到低。Step 0 是新的「撤销」键,Step 1 是干净重启,Step 2-3 是耐久加固。
Step 0:刚压缩完,先 rewind 回去
Claude Code v2 起每条 prompt 都建一个 checkpoint,对话可以回滚:
- 按两下
Esc(或跑/rewind)。 - 选压缩之前的那条消息。
- 选 Restore conversation——这会回滚对话历史,但保留你磁盘上当前的文件。(“Restore code” 或 “Restore both” 会同时回退文件改动,按需选。)
- 下一条消息重申你的约束,让它们在 context 里是新鲜的。
注意:checkpoint 只捕获通过 Claude 文件工具做的改动。Bash 的副作用(git push、npm install、API 调用)不会被回退。checkpoint 约 30 天后清理。
Step 1:停、snapshot、重启
如果你已经远远越过了那次坏压缩,就别硬救——开新 session 带 handoff:
- 写一段 5 行「我们现在在哪」:
## 状态(2026-06-14 15:30)
任务:给 apps/web/ 加 billing feature
决定:
- 不用 GraphQL——REST only
- 测试 vitest,紧挨源
- 结构照 `apps/web/src/features/auth/`
进度:
- 已完成:`apps/web/src/features/billing/index.ts`、`types.ts`
- 下一步:实现 `billing.service.ts`
阻塞:无
- 跑
/clear(清掉对话但留在同一个项目里),或者开新 session。 - 把状态贴成第一条消息,加「continue from here」。
清空后的 session 对这份 snapshot 是全 context、逐字保留——没有衰减记忆。
Step 2:把 per-session 决定升级到 CLAUDE.md
每个被丢的约束都是 CLAUDE.md 空缺。把它放进项目根 CLAUDE.md(不是 path-scoped rule),因为只有它会在 compaction 后被重新注入:
## 架构决定
- API 风格:REST only(不 GraphQL——2025-Q4 试过弃了)
- 测试框架:vitest only(不 jest——见 `apps/web/AGENTS.md`)
- 新 web feature 工作目录:`apps/web/src/features/`
下次 session 启动就读到,而且每次 compaction 都会被重新注入。跑 /memory 确认到底哪些 CLAUDE.md 真的加载了。文件保持在 ~200 行以内——CLAUDE.md 太大只会更快吃光预算。
Step 3:每 30 分钟把进度落盘
长 task 把进度写到文件,跨任何 context reset 存活:
# session 开始时 claude 建这个文件
echo "## 进度日志" > .agent-progress.md
echo "Started: $(date -Iminutes)" >> .agent-progress.md
Prompt:
完成有意义的一步就 append 到 `.agent-progress.md`:
- 时间戳
- 完成了什么
- 改的文件
- 下一步
context 丢了,这个文件就是权威状态。
Step 4:减少 context-吃货 tool 调用
降噪让 compaction 晚点触发:
- 找东西用 Grep 不用 Read——输出小得多
- Read 时用行区间不读整文件
- 同一个文件不要 Read 两次——记住内容
- 大输出写到 /tmp/<name>.txt 再 refer
- Bash 命令一直 fail 就停下来问,不要重试 5 次
Step 5:正交子任务交给 subagent
长 task 多关切——把它们 delegate 给 subagent(Task tool)。每个 subagent 跑在自己独立的 context window 里,文件在那里读,只有最终摘要回到主 session:
DB migration 这步开一个 subagent。
返回:migration 文件路径 + 应用了的 migration 名。
按 Anthropic 文档,一个读了 6,000 token 文件的 subagent 可能只回 400 token 摘要——那些文件读取从不碰你的主窗口。内置的 Explore 和 Plan agent 就是为此设计的。
Step 6:在 auto 那趟之前、按自己的节奏 compact
状态栏会倒数 Context left until auto-compact: X%。别等它归零——Claude 在窗口最后 ~20% 时推理会变差。截至 2026 年 6 月,实操目标是在 60-75% 占用时动手:
/compact focus on the billing feature, keep the no-GraphQL and vitest constraints——带 focus 的手动 compact 保留你点名的内容,而不是 auto 那趟瞎猜的。- 动手前先把进行中的决定存进
CLAUDE.md或.agent-progress.md。 - 接下来是不相关的活,就用
/clear而不是 compact——干净窗口胜过摘要窗口。
在 60% 按自己节奏 compact,远比从撞坏的 95% 摘要里救回来便宜。
如何确认修好了
重新锚定或重启之后:
- 问:「写代码之前,先列出你正在遵守的约束。」健康的 session 会把 REST-only、vitest、
apps/web/scope 复述给你。 - 跑
/context,确认 conversation 那块很小,而你的约束在刚被重新注入的CLAUDE.md里。 - 盯着下一次矛盾。如果又出现,说明约束还只活在对话里——把它升级到项目根
CLAUDE.md(Step 2)。
常见问题
为什么 Claude 老是重读同一批文件? microcompact 清掉旧 tool 结果(保留约最近 5 个)后,或者一次完整 compaction 把它们摘掉后,Claude 的 context 里就没有文件内容了,于是重读。这是正常的。要减少:session 短一点、prompt scope 紧一点让它少读文件、大块读取交给 subagent。
能不能直接把 auto-compact 关掉?
能(设置里 autoCompact: false,或走 /config),但这样一来窗口满了会直接停下 session,而不是从摘要继续——这是拿有损记忆换一堵硬墙。更好的习惯是自己在干净边界 /compact(带 focus)或 /clear。
1M-token 上下文窗口能解决这个吗?
它只是推迟。Sonnet 4.6 和 Opus 4.7 支持 1M-token 上下文窗口(在你的套餐允许时选 [1m] 模型变体),compaction 会晚很多发生。但同样的摘要会在更高的上限上跑,而且一个塞满陈旧读取的大窗口照样会让推理变差。把更大的窗口当余量,不是「永不 compact」的许可证。
为什么写在文件里的 rule 也被忘了?
path-scoped rule(带 paths: frontmatter 的 .claude/rules/*.md)和嵌套 CLAUDE.md 会在 compaction 时被摘掉,只有下次 Claude 读到匹配文件时才重载。项目根 CLAUDE.md 和 auto memory 则每次都从磁盘重新注入。必须存活的约束放项目根。
/compact 和 /clear 有什么区别?
/compact 把对话摘成摘要并保留为 context(想继续同一个 task、又想瘦身时用)。/clear 彻底清掉对话、在同一个项目里从头开始(切到不相关的活时用)。想撤销某个最近改动而不是把全部压缩,用 /rewind。
预防建议
- per-session 决定升级到项目根
CLAUDE.md,跨 compaction 存活;文件保持 ~200 行以内。 - 别把必须存活的约束埋在 path-scoped rule 或嵌套 CLAUDE.md 里——那些会被摘掉。
- 超过 20 分钟的 task 维护一份
.agent-progress.md——context reset 时的 fallback。 - 在干净边界(一步完成后)重启或
/clear,不要硬撑过 compaction。 - 减少 tool 噪音——Grep > Read、行区间 > 整文件、不重复 read。
- 正交子任务给 subagent,每个独立 context window。
- 把
Context left until auto-compact: ~20-30%当收尾信号;归零前就/compact(带 focus)或/clear。
相关阅读
标签: #排查 #Claude Code #排查 #上下文缺失