context-collapse-deep-dive
Claude Code 上下文压缩三Context Collapse 异步折叠这是 Claude Code 源码学习系列的第三篇。Context Collapse 是四级压缩中最具创新性的设计——它将传统的同步全文总结进化为后台异步多段折叠在不阻塞用户的情况下持续维护上下文健康。一、它在哪一层Snip Compact删除整条消息 ↓ Microcompact清除工具结果内容 ↓ Context Collapse异步折叠 ← 本文 ↓ AutoCompactLLM 兜底总结二、设计哲学从「一刀切」到「精细折叠」2.1 Compact 的局限传统 AutoCompact 的模型很简单对话太长 → LLM 总结一切 → 一份摘要替代全部历史。Compact: 整个对话前缀 ──► [一篇大总结] 问题 - 所有细节丢失变成一段面条式总结 - 你无法保留这段很重要那段可以折 - 同步执行用户必须等待 LLM 总结完成2.2 Collapse 的答案Context Collapse 把一次大总结变成了多段小折叠Context Collapse: 对话 ──► [修复 foo.ts: done ✓] ← 一段折叠 [讨论架构: done ✓] ← 又一段折叠 [最新工作: 保留原始消息] ← 不折叠每段被折叠的区间变成一行自然语言总结。未被折叠的消息保持原样。多段折叠可以在整个对话中的任意位置。三、核心架构异步代理 读写分离3.1 ctx-agent — 后台分析引擎Context Collapse 的核心是一个独立运行的ctx-agent上下文代理主线程用户对话: [用户输入] → [压缩管线] → [API 请求] → [模型回复] → [用户看见] │ │ │ 并行运行互不阻塞 │ │ │ ctx-agent后台: [分析对话] → [识别已完成的任务区间] → [LLM 生成总结] → [stage 到队列] ↑ │ └──── 在空闲时重新 spawn持续分析新对话 ───────────────┘关键设计ctx-agent 的 LLM 调用在后台进行用户等待的是下一个毫秒的 API 响应而不是等待 ctx-agent 的总结完成。3.2 两级状态Staged → CommittedStage暂存队列 ├─ ctx-agent 分析完成、生成 summary 的折叠 ├─ 存在 collapseStore.stagedQueue 中 ├─ 持久化到磁盘 snapshotlast-wins └─ 尚未应用到实际对话中 │ applyCollapsesIfNeeded() ← 主线程调用瞬间完成 ▼ Commit已应用 ├─ 折叠正式生效 ├─ projectView() 实际替换消息 └─ 持久化到磁盘 commit 日志append-only// query.ts:440-447 — 核心集成点if(contextCollapse){constcollapseResultawaitcontextCollapse.applyCollapsesIfNeeded(messagesForQuery,toolUseContext,querySource)messagesForQuerycollapseResult.messages// 折叠后的投影}applyCollapsesIfNeeded() 本身不调用 LLM——它只是检查 staged 队列把已分析好的折叠标记为 committed。毫秒级完成。四、读时投影Read-Time Projection4.1 为什么 Summary 不进 REPL┌───────────────────────────────────────────────┐ │ REPL (mutableMessages) — 用户可滚动回看 │ │ [m1][m2][m3][m4][m5][m6][m7][m8][m9][m10] │ │ 完整历史用户看到所有消息 │ └───────────────────────┬───────────────────────┘ │ collapseStore — 只在内存中管理折叠元数据 commit 1: { first: m1.uuid, last: m4.uuid, summary: 完成了auth模块重构: ... } commit 2: { first: m8.uuid, last: m9.uuid, summary: 讨论了缓存策略: ... } │ │ projectView() — 每次 API 请求时动态投影 ▼ ┌───────────────────────────────────────────────┐ │ API 视图 │ │ [summary_1][m5][m6][m7][summary_2][m10] │ │ 被折叠的消息被 summary 文本替代 │ └───────────────────────────────────────────────┘如果 summary 进了 REPL用户滚动历史时会看到一段突兀的总结文字。放在 collapseStore 中用户看到的始终是完整对话只有 API 请求中才是投影后的视图。4.2 跨轮持久// query.ts:432-439// Nothing is yielded — the collapsed view is a read-time projection// over the REPLs full history. Summary messages live in the collapse// store, not the REPL array. This is what makes collapses persist// across turns: projectView() replays the commit log on every entry.每一轮对话的projectView()都从 commit 日志重放所有折叠——所以一旦折叠被 commit它在后续所有轮次中都持续生效。五、持久化设计5.1 两类磁盘条目// types/logs.ts// Commit — append-only 日志typeContextCollapseCommitEntry{type:marble-origami-commitcollapseId:string// 折叠 IDsummaryUuid:string// summary 占位符 UUIDsummaryContent:string// 完整的 XML 折叠标签summary:string// 纯文本总结firstArchivedUuid:string// 折叠起点lastArchivedUuid:string// 折叠终点}// Snapshot — last-winstypeContextCollapseSnapshotEntry{type:marble-origami-snapshotstaged:Array{startUuid,endUuid,summary,risk,stagedAt}armed:boolean// spawn 触发器状态lastSpawnTokens:number// 上次触发时的 token 数}Commit 是 append-only和消息一样按顺序重放。Snapshot 是 last-wins只有最新的重要记录 ctx-agent 的恢复状态。5.2 Resume 恢复// sessionStorage.ts:3694-3697elseif(entry.typemarble-origami-commit){contextCollapseCommits.push(entry)// 逐条收集}elseif(entry.typemarble-origami-snapshot){contextCollapseSnapshotentry// 只保留最后一条}// 加载后restoreFromEntries(contextCollapseCommits,contextCollapseSnapshot)Resume 后的 collapseStore 恢复至崩溃前的状态——已 committed 的折叠还在staged 的待命折叠也在。六、阈值与触发6.1 多级阈值有效上下文窗口 (effectiveWindow) │ ├─ 90% ─── 提交开始 (commit-start) │ applyCollapsesIfNeeded() 开始把 staged fold commit │ ├─ 93% ─── 这是 autocompact 的老位置 │ collapse 启用时 autocompact 在此被禁 │ 否则两者会竞争compact 胜出但损失粒度 │ └─ 95% ─── 阻断 spawn (blocking-spawn) 强制触发 ctx-agent 进行紧急折叠6.2 与 AutoCompact 的互斥// autoCompact.ts:215-222if(feature(CONTEXT_COLLAPSE)){const{isContextCollapseEnabled}require(../contextCollapse/index.js)if(isContextCollapseEnabled()){returnfalse// ← AutoCompact 直接退场}}Collapse 和 Compact 不是叠加关系是替代关系。Collapse 是更精细的替代品。如果 Collapse 启用了Compact 不再主动触发只在 API 413 时作为最后兜底。七、PTL 恢复路径当对话超长导致 API 返回 413prompt too long时Collapse 有自己的恢复链// query.ts:1089-1117if(contextCollapsetransitionReason!collapse_drain_retry){constdrainedcontextCollapse.recoverFromOverflow(messages,querySource)if(drained.committed0){// 成功 drain 了 staged fold → 用折叠后的视图重试state{...state,messages:drained.messages}continue// 回到 query loop 开头}// drained.committed 0 → staged queue 空或过期// fall through → reactiveCompact → 传统 LLM 总结}恢复优先级Drain staged fold → 如果 ctx-agent 已经分析好了Reactive compact → 紧急 LLM 总结用户错误 → “对话过长请手动 /compact”八、UI 反馈// TokenWarning.tsx — CollapseMode 下的实时进度functionCollapseLabel(){const{collapsedSpans,stagedSpans,totalErrors}getStats()consttotalcollapsedSpansstagedSpansif(totalErrors0){returnText colorwarningcollapse errors:{totalErrors}/Text}returnText dimColor{collapsedSpans}/{total}summarized/Text}用户看到的不是 “Compacting…” 的等待而是一个安静的进度指示器。九、小结Context Collapse 是 Claude Code 上下文管理中最具工程创意的设计异步代理— LLM 分析对话在后台进行不与用户交互争时间多段折叠— 不像 Compact 一刀切而是精确折叠已完成的任务区间读时投影— 消息不变API 请求时动态投影用户看到完整历史两级状态— stage暂存→ commit激活延迟应用随时可调整替代而非叠加— 与 AutoCompact 互斥是更精细的替代方案优雅降级— PTL 时 drain staged → reactive compact → 用户手动下一章将介绍第四级兜底级AutoCompact。本文全部来自博主学习 Claude Code 源码时的笔记和与 AI 的问答整理。