Claude Code Compact 功能的问题与解决方案

@u1
日语3天前 · 2026年7月04日
717K
1.6K
180
11
3.3K

TL;DR

本文详细介绍了针对 Claude Code 上下文压缩的结构性修复方案,利用自定义技能和钩子在摘要清除之前保存会话状态和决策逻辑。

好的,这是根据您的要求翻译成的简体中文版本。

以后,我创建了一个插件,可以自动包含以下内容,并透明地增强紧凑功能。

https://x.com/u1/status/2073742408555343968

Claude Code 的 Compact 实际工作原理

作为前提,Claude Code 的 compact 通过两条路径运行:

  • 手动 compact (/compact):由用户显式触发。可以在任何时机执行。
  • 自动 compact:当会话上下文使用量接近限制时自动运行(实践中通常在 90-95% 左右)。它不会在用户期望的确切时刻运行,而是在某个轮次边界上,当你突然意识到上下文已经被压缩时发生。

在两条路径中,内容是一样的:将之前的对话历史发送给大语言模型,以创建一个自然语言摘要,然后将摘要、最近的轮次和系统提示词重构为一个新的上下文。摘要变成了一段关于“做了什么”的叙事性描述,因此,“为什么做出那个选择”、“哪些方案被拒绝了”以及“我们当前处于哪个阶段”等决策结构会被削弱。此外,一旦压缩运行,这是一个不可逆的操作;你无法返回到压缩前的原始日志。

从钩子的角度来看,压缩可以通过 PostCompact 事件捕获。但是,PostCompact 钩子无法返回 additionalContext,因此没有路径可以在压缩后立即向 Agent 直接注入指令。要插入指令,必须通过随后的 UserPromptSubmit 事件。这个约束决定了本文中的钩子设计。

标准 Compact 的问题

Claude Code 的 /compact 会总结对话历史,并将结果作为新会话的初始上下文。虽然摘要本身是合理的,但它是一份“过去的工作日志”,而不是“接下来要做什么的指令”。因此,压缩后的 Agent 容易出现以下误解:

  • 混淆了“工作指令”和“工作日志”的界限。如果摘要提到“考虑了方案 A”,Agent 可能会开始实施方案 A,而不是实际采用的方案 B。
  • 混淆了验证阶段和实施阶段。像“部署前在实际机器上验证”这样的前提可能从摘要中消失,Agent 可能会在下一轮次中直接进行破坏性的部署操作。
  • 重新提出已被否决的错误方案。摘要可能记录某个方案被“尝试过”,但没有记录“被拒绝的原因”。
  • 丢失“会话状态”,例如计划模式、工作者委派或任务树。Agent 可能在退出计划模式后继续工作,或者忘记了在 tmux 中运行的工作者,并开始自己动手工作。

实际发生过哪些事故

回顾过去一周的会话,我观察到至少在 compact 后发生了四类模式。它们似乎都源于未能区分“工作指令”和“工作日志”:

  • 先验证后部署的流程被破坏:尽管同意“在放置文件前先在机器上验证行为”,但压缩后 Agent 只看到了摘要中的完成状态,跳过了验证,并直接覆盖了目标位置。
  • 重新执行被否决的捷径:由于摘要将一次失败的捷径尝试记录为“已尝试过的步骤”,Agent 再次执行它,并再次陷入相同的失败。
  • 忘记了设计原则:在会话中确立的原则,如“编辑源代码仓库而不是目标目录”,从摘要中丢失,导致 Agent 进行了一些短期内有效但无法复现的直接编辑。
  • 误解了任务目标:压缩前某个测试的具体目的丢失了,导致 Agent 偏离到外围调查,并开始处理一个完全不同的问题。

这些情况在同一会话中多次发生,每次 compact 后都出现不同类型的误解。我认为这是标准 compact 规范的结构性缺陷,而非孤立的事故。

我构建的方案

(1) compact-prep 技能

一个作为斜杠命令实现的技能,用户在输入 /compact 之前显式执行它。它会提取那些可能无法放入摘要的“决策结构”和“会话状态”,并以特定格式保存到固定路径 (${TMPDIR}/claude-compact-state/<session_id>.md) 的状态文件中。

保存的内容包括那些在摘要中难以捕获的信息:

  • 活跃计划(计划文件路径和当前阶段)
  • 任务列表摘要(进行中的任务和补充说明)
  • 会话决策(已采纳的方案 / 被拒绝的方案 / 拒绝原因)
  • 约束和阻碍项
  • 工作者拓扑(tmux-bridge 面板 / 角色 / 分配)
  • 编辑中的文件(关于未保存或未验证更改的说明)
  • 恢复笔记(给压缩后的自己的一封信)

关键设计点是“机械式强制执行”。它使用硬性门控,在找不到 session_id 时阻止文件创建;使用固定标题作为强制手段来检测遗漏;并限制允许使用的工具以防止状态损坏。

(2) 两阶段钩子:PostCompact + UserPromptSubmit

即使 (1) 写入了状态文件,如果压缩后的 Agent 不读取它,那也毫无意义。由于 PostCompact 无法返回 additionalContext,我使用了一个两阶段配置:

  1. PostCompact 钩子写入一个包含 session_id 的标记文件(不包含指令,只记录压缩已发生)。
  2. 当下一个 UserPromptSubmit 钩子检测到该标记时,它通过 additionalContext 注入指令:“读取计划文件 / 读取状态文件 / 检查任务列表 / 将摘要的后续步骤视为假设 / 如果计划模式被停用,则确认重新进入”,然后删除标记。

由于 Claude Code 的钩子之间缺乏共享状态机制,文件系统上的标记是唯一的通信路径。这使得每个钩子都能保持精简,职责单一。在正常的轮次中,开销仅是一次 test -f 检查。

(3) 60% 通知(用于避免自动 compact)

前面的工具假设用户手动运行 /compact-prep → /compact。然而,自动 compact 会在毫无预警的情况下运行,绕过了这个流程。如果自动 compact 先触发,状态文件不会被保存,原始日志被压缩成摘要,从而丢失了恢复材料。

解决方法是创建一种状态,让用户可以在自动 compact 触发前手动运行预备步骤。这就是 60% 通知的目的。

为什么是 60%?

  • 足够的提前量:自动 compact 在 90-95% 左右触发。60% 提供了 30% 的缓冲,确保用户在下一轮触发自动 compact 之前看到警告。
  • 有时间完成工作:在 60% 时,仍有 30% 的容量来完成一个子任务,然后再保存状态。
  • 机械式触发器:当你过于专注而忽略了上下文消耗时,它能起到强制作用。
  • 100 万上下文假设:对于 20 万上下文,60% 太小了。我使用的是 100 万上下文(Opus 4.7),60% 大约剩下 60 万个 token——有充足的空间来完成工作并运行预备步骤。

实现方式包括:在状态行钩子中写入一个达到阈值时的警告标记,以及一个 UserPromptSubmit 钩子检测到该标记后建议运行 /compact-prep。

结果

现在,即使在进行了大约 10 次压缩后,会话也能保持稳定,没有逻辑崩溃!

  • 由于压缩导致的工作丢失几乎为零。
  • 被否决的想法被重新提出的情况消失了。
  • 计划模式得以维持。
  • 在 tmux 中运行多个工作者时,不再忘记拓扑结构。

参考:实现代码

以下是实际的技能和钩子代码。将它们复制到标准 Claude Code 目录 (~/.claude/) 即可使用。

A: compact-prep 技能主体

放置在 ~/.claude/skills/compact-prep/SKILL.md

markdown
1---
2name: compact-prep
3description: |
4 在 Claude Code 中执行 /compact 之前,将会话的当前状态保存到一个临时状态文件中。
5 强制触发条件:/compact-prep, compact-prep, 压缩准备。
6 不触发条件:压缩后的恢复、常规进度报告、计划创建。
7strict_procedure: true
8argument-hint: "[恢复笔记]"
9allowed-tools: Read Write Bash(~/.claude/scripts/get-session-id.sh *) Bash(mkdir *) Bash(date *) Bash(pwd)
10---
11
12# compact-prep
13
14`/compact` 之前,将会话状态中难以保留在压缩摘要中的信息保存到
15`${TMPDIR}/claude-compact-state/${SESSION_ID}.md`
16
17## 严格流程概要
18
19- 严格程度:严格流程。
20- 硬性门控:如果无法获取 session_id,则停止。
21- 强制手段:保存后检查固定路径和必要的标题。
22- 完成确认:报告文件路径、已保存的项目以及运行 `/compact` 的指引。
23
24## 流程
25
261. 通过 `~/.claude/scripts/get-session-id.sh` 获取 session_id。
272. 设置路径为 `${TMPDIR:-/tmp}/claude-compact-state/${SESSION_ID}.md`
283. 检查任务列表、活跃计划、tmux-bridge 和编辑中的文件。
294. 按顺序保存标题:# Compact Prep State, ## Active Plan, ## Current Phase, ## TaskList Summary, ## Session Decisions, ## Constraints and Blockers, ## Worker Topology, ## Editing Files, ## Recovery Notes。
305. 验证所有标题都存在。
316. 告知用户:“准备工作已完成。请执行 `/compact`。”

B: 恢复钩子(两阶段)

B-1: PostCompact 钩子

~/.claude/hooks/compaction-recovery.sh

B-2: UserPromptSubmit 钩子(指令注入)

~/.claude/hooks/userpromptsubmit-compaction-recovery.sh

C: 60% 通知

C-1: 状态行钩子阈值分支

添加到 ~/.claude/hooks/statusline.sh

C-2: UserPromptSubmit 钩子(提醒)

~/.claude/hooks/userpromptsubmit-compact-prep-reminder.sh

D: settings.json 注册

将钩子添加到 ~/.claude/settings.json

一键保存

使用 YouMind AI 深度阅读爆款文章

保存原文、追问细节、总结观点,并在一个 AI 工作空间里把爆款文章沉淀成可复用笔记。

了解 YouMind
写给创作者

把你的 Markdown 变成干净的 𝕏 文章

图片上传、表格、代码块,往 𝕏 上手动重排太痛苦。YouMind 把整篇 Markdown 一键转成干净、可直接发布的 𝕏 文章草稿。

试试 Markdown 转 𝕏

更多可拆解样本

近期爆款文章

探索更多爆款文章