BrainBank
AI 课堂/知识Claude Code Deep Dive

上下文压缩管理

2026/7/31 17:27:47 · 更新于 2026/7/31 17:33:24

#claude-code#software-architecture#knowledge#prompt-engineering#context-compression#token-optimization

Claude Code 通过多层递进的分级压缩管线(工具预算裁剪、snip、microcompact、context collapse、autocompact 与 reactive compact 兜底),在保障长程工程任务连续性的同时最大化保留细粒度上下文结构,而非依赖单一的全局摘要机制。

Claude Code 的长上下文能力并非单纯依赖模型窗口大小,而是基于一套精密的多阶段分级压缩管线。在主查询链路中,它串联了预算裁剪、细粒度瘦身、视图折叠、自动摘要与报错兜底等六层机制,并深度结合会话持久化与 Prompt Cache 协同,确保在长期工程任务中实现结构优先、延后信息损失的运行时上下文管理。


为什么 Claude Code 必须做上下文压缩

Claude Code 的任务不是一次性问答,而是持续执行复杂的工程操作:

  • 读取多个文件
  • 搜索代码库
  • 运行 Bash 命令
  • 写文件和补丁
  • 调用子 Agent
  • 与 MCP / LSP 交换结果

这些行为会不断把新消息和工具结果追加进会话历史。如果没有压缩,模型很快就会被旧消息、长工具输出和文件附件塞满。

Anthropic 在系统提示词中甚至直接明确指出了这一机制:

自动摘要承诺 The conversation has unlimited context through automatic summarization. (这段对话会通过自动摘要获得“近似无限”的上下文。)

压缩触发条件 The system will automatically compress prior messages in your conversation as it approaches context limits. (当对话接近上下文限制时,系统会自动压缩更早的消息。对应中文即为:当对话接近上下文限制时,系统会自动压缩更早的消息。)

从产品承诺到运行时实现,Claude Code 都将“自动压缩”视为基础设施,而非事后打补丁的逻辑。


主链路与分级管线架构

真正的压缩主链位于 /Users/xuanyuan/Downloads/claude-code-src/query.ts。从源码顺序可以看出,它并非只做一次 compact(),而是多层串联:

messagesForQuery = await applyToolResultBudget(...)

const snipResult = snipModule!.snipCompactIfNeeded(messagesForQuery)
messagesForQuery = snipResult.messages

const microcompactResult = await deps.microcompact(
  messagesForQuery,
  toolUseContext,
  querySource,
)
messagesForQuery = microcompactResult.messages

const collapseResult = await contextCollapse.applyCollapsesIfNeeded(
  messagesForQuery,
  toolUseContext,
  querySource,
)
messagesForQuery = collapseResult.messages

const { compactionResult } = await deps.autocompact(
  messagesForQuery,
  toolUseContext,
  ...
)

最值得注意的不是函数名本身,而是严格的执行顺序

  1. 先裁掉过大的工具结果
  2. 再做 snip(局部瘦身)
  3. 再做 microcompact(结构化微压缩)
  4. 投影 context collapse(折叠视图)
  5. 最后才尝试 autocompact(全局摘要)

这意味着 Claude Code 并不急着把旧历史粗暴压成一段摘要,而是优先尝试保留更多细节与结构。整个上下文管理本质上是一个多阶段管线,而非单点能力。

image.png image.png


六层分级压缩机制详解

第 1 层:工具结果预算裁剪

最先运行的是 applyToolResultBudget(...)

messagesForQuery = await applyToolResultBudget(
  messagesForQuery,
  toolUseContext.contentReplacementState,
  ...,
  new Set(
    toolUseContext.options.tools
      .filter(t => !Number.isFinite(t.maxResultSizeChars))
      .map(t => t.name),
  ),
)

核心目标:在进入真正的上下文压缩之前,先把明显过大的工具结果做替换或裁剪。这非常关键,因为很多时候最占空间的不是用户消息,而是:

  • BashTool 打出来的大段终端输出
  • 搜索工具返回的长结果
  • 文件读取工具读到的大文件片段

如果这类结果不先处理,后续的压缩管线会被低价值的超长文本拖累。

第 2 层:snip 细粒度裁剪

源码注释明确标注了它的定位:

// Apply snip before microcompact (both may run — they are not mutually exclusive).

这说明两点:

  1. snipmicrocompact 不是互斥关系,可叠加执行。
  2. snip 更靠前,属于更轻量的局部瘦身

对应代码逻辑:

const snipResult = snipModule!.snipCompactIfNeeded(messagesForQuery)
messagesForQuery = snipResult.messages
snipTokensFreed = snipResult.tokensFreed
if (snipResult.boundaryMessage) {
  yield snipResult.boundaryMessage
}

从返回值可以看出其作用:产出新消息数组、记录释放的 token 数、必要时注入边界提示。可以将 snip 理解为:

在不破坏主要会话结构的前提下,先对低价值部分做局部裁剪。

第 3 层:microcompact 微压缩

microcompactsnip 深入一层,但尚未进入“整段历史总结”阶段。源码注释指出:

// Apply microcompact before autocompact
// cached MC operates purely by tool_use_id

这说明它的核心设计目标是围绕工具调用记录(tool_use_id)做细粒度压缩。特别适合以下场景:

  • 工具调用链很长
  • 部分工具结果内容极长
  • 但工具调用的元数据/结构信息仍值得保留

snip 的粗略对比:

机制作用范围核心策略
snip局部轻量裁剪削减低价值文本
microcompact结构化微压缩保留 tool_use_id 链路,压缩冗余 Payload

image.png

这一层体现了 Claude Code 极强的工程判断:只要还能保留结构化上下文,就不要急着把它们全变成一段摘要。

第 4 层:context collapse 折叠视图

这是源码中容易被忽视、但极其巧妙的一层。注释原文精准描述了其存在意义:

// Project the collapsed context view and maybe commit more collapses.
// Runs BEFORE autocompact so that if collapse gets us under the
// autocompact threshold, autocompact is a no-op and we keep granular
// context instead of a single summary.

核心逻辑:在进入自动压缩前,先投影一个折叠后的上下文视图。如果折叠之后已经回到阈值以下,自动压缩将直接跳过(no-op),从而保留更细粒度的原始上下文,而非合成一段大摘要。

调用入口:

const collapseResult = await contextCollapse.applyCollapsesIfNeeded(
  messagesForQuery,
  toolUseContext,
  querySource,
)
messagesForQuery = collapseResult.messages

关键设计思想不是“删除历史”,而是**“重新投影视图”**。底层日志未必被彻底抹除,但当前喂给模型的视图已被折叠。这与后续 sessionStorage.ts 中的 contextCollapseCommitscontextCollapseSnapshot 完美呼应:

const contextCollapseCommits: ContextCollapseCommitEntry[] = []
let contextCollapseSnapshot: ContextCollapseSnapshotEntry | undefined

说明 collapse 的处理不仅是临时内存操作,而是具备提交记录与快照版本的持久化概念。

第 5 层:autocompact 自动摘要压缩

真正对应大众认知的“自动摘要”,在源码中为 deps.autocompact(...)

const { compactionResult, consecutiveFailures } = await deps.autocompact(
  messagesForQuery,
  toolUseContext,
  {
    systemPrompt,
    userContext,
    systemContext,
    toolUseContext,
    forkContextMessages: messagesForQuery,
  },
  querySource,
  tracking,
  snipTokensFreed,
)

若压缩成功,会立即重建消息链:

const postCompactMessages = buildPostCompactMessages(compactionResult)

for (const message of postCompactMessages) {
  yield message
}

messagesForQuery = postCompactMessages

关键点

  • 不只是生成摘要文本,还会重建完整的 post-compact 消息序列
  • 压缩结果会真正回写进当前对话执行链,后续模型请求基于此继续。
  • 它不是旁路日志记录,而是实际改变主循环所能看到的上下文状态。

第 6 层:reactive compact 报错恢复兜底

如果主动压缩管线依然无法释放足够空间,Claude Code 设有一条兜底链路:reactive compact。该逻辑位于 query.ts 流式返回的后半段:

if ((isWithheld413 || isWithheldMedia) && reactiveCompact) {
  const compacted = await reactiveCompact.tryReactiveCompact({
    hasAttempted: hasAttemptedReactiveCompact,
    querySource,
    aborted: toolUseContext.abortController.signal.aborted,
    messages: messagesForQuery,
    cacheSafeParams: {
      systemPrompt,
      userContext,
      systemContext,
      toolUseContext,
      forkContextMessages: messagesForQuery,
    },
  })
}

主要用于处理两类 runtime 失败:

  • prompt too long(API 返回 413)
  • 媒体内容超限(大图片 / PDF / 多图输入)

设计本质:当真实 API 调用已经报错时,触发一次恢复性压缩把任务“救回来”。

image.png

这体现了工程的成熟度:不假设“主动压缩一定成功”,而是将失败恢复纳入主循环设计。


压缩的持久化与会话边界管理

压缩不仅发生在内存中,还严格影响会话持久化与恢复逻辑。QueryEngine.ts 专门处理 compact_boundary

if (
  persistSession &&
  message.type === 'system' &&
  message.subtype === 'compact_boundary'
) {
  const tailUuid = message.compactMetadata?.preservedSegment?.tailUuid
  ...
}

在回放时,它同样被视为必须确认的系统消息:

(msg.type === 'system' && msg.subtype === 'compact_boundary')

这说明 compact_boundary 的作用绝非 UI 提示,而是:

  • 标记压缩边界:明确一次摘要在会话链条中的物理/逻辑位置。
  • 告知 Transcript:哪一段历史已被总结,哪些仍需保留。
  • 提供恢复锚点:让下游系统能准确重新拼接 preserved segment

为什么 sessionStorage.ts 如此复杂

如果只做“摘要替换”,会话恢复逻辑会非常简单。但 Claude Code 采用了更精细的策略,因此在 sessionStorage.ts 中出现了大量处理压缩边界和保留段的逻辑:

核心注释 Splice the preserved segment back into the chain after compaction.

以及 applyPreservedSegmentRelinks(...) 中的关键说明:

Only the LAST seg-boundary is relinked — earlier segs were summarized into it.

这揭示了一个重要设计原则:

  1. 压缩后并非所有旧消息都彻底消失。
  2. 关键片段会以 preserved segment 形式保留。
  3. 会话恢复时,必须将这些片段精确重新接回链上。

这也是 Claude Code 的上下文管理不同于普通聊天产品“把前文总结成一段文字”的根本原因。


与 Prompt Cache 的协同设计

constants/prompts.ts 中定义了一个关键边界常量:

export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY =
  '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'

源码注释直言不讳:

Everything BEFORE this marker in the system prompt array can use scope: 'global'. Everything AFTER contains user/session-specific content and should not be cached.

将其与上下文压缩结合来看,能更清晰理解 Claude Code 的总体架构策略:

  • 静态系统提示:尽量缓存(提升推理速度/降低成本)
  • 动态用户上下文:分层压缩管理(保障可持续性)
  • 历史消息管理:通过 boundary 标记与 snapshot 机制控制恢复正确性

因此,它解决的不仅是“如何缩小 token 数”,而是同时统筹:

  • Token 成本控制
  • 长上下文任务可持续性
  • Prompt Cache 命中率
  • /resume 会话恢复的正确性

实际体验与架构总结

这套分级管线最终转化为以下用户可感知的效果:

  1. 容错性强:长任务不会因为读了太多文件/代码立刻崩溃。
  2. 延迟稳定:对话可持续多轮,不会随着历史增长而明显迟钝。
  3. 具备自救能力:在触达硬界限时有主动与被动双重压缩机制。
  4. 恢复保真度高/resume 恢复出的会话能无缝接上前文逻辑。
  5. 结构保留优先:优先折叠视图与局部瘦身,不轻易将历史压成模糊摘要。

总结

Claude Code 的上下文压缩,远非“接近上限时做一次摘要”的简单逻辑。从源码审视,它更像一套分层内存管理系统

  • 前端层:优先轻量裁剪与结构化微压缩。
  • 中端层:通过视图折叠保留关键调用链,阻断不必要的摘要。
  • 后端层:触发全局自动摘要,并在 API 报错时启动恢复性兜底。

若仅将 Claude Code 视为“模型 + 工具”,会严重低估其复杂度。真正支撑它持续完成复杂工程任务的,正是这套运行时级别的上下文架构与资源编排能力。


Key takeaways

  • 多阶段管线优先于单点摘要applyToolResultBudgetsnipmicrocompactcontext collapseautocompactreactive compact,按顺序执行,层层递进释放空间。
  • 结构保留 > 粗暴压缩:通过 tool_use_id 追踪与视图投影(Collapse),最大限度保留工具调用上下文,避免核心逻辑被摘要抹除。
  • 边界与快照机制保障持久化compact_boundarypreserved segment 设计确保会话在跨轮次加载时能精确还原逻辑链路。
  • 与缓存策略深度耦合:引入 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 严格分割静态系统提示与动态对话历史,兼顾 Prompt Cache 命中率与会话管理安全性。
  • 工程兜底思维:不依赖单一压缩路径成功,内置 reactive compact 应对 413 等运行时失败,体现高可用架构设计。

image.png

学习地图

🗺️ 学习路线:掌握 Claude Code 上下文压缩机制

阶段一:基础原理认知

  • 理解 LLM Context Window 的底层限制与 Token 成本关系
  • 区分“固定摘要替换”与“分级增量压缩”的设计哲学差异
  • 熟悉主查询链路各压缩阶段的触发阈值与作用范围

阶段二:源码机制与运行时流转

  • 追踪 query.ts 核心调用栈,掌握 6 层压缩管线的执行顺序
  • 分析 Bash/文件/搜索等工具输出对上下文的污染路径及裁剪策略
  • 理解 context collapse 的视图投影思想与 compact_boundary 的持久化边界逻辑

阶段三:工程配置与 Prompt Cache 协同

  • 结合 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 划分静态/动态区,提升缓存命中率
  • 自定义工具时合理设置 maxResultSizeChars 以适配预算裁剪机制
  • 观察 Session Storage 如何重组 Preserved Segment 并完成会话恢复

阶段四:架构扩展与实战避坑

  • 横向对比同类 Agent 工具的上下文管理差异
  • 设计支持 /resume 恢复的结构化工作流,规避长任务片段断裂风险
  • 结合实际场景调优压缩管线参数,平衡信息保留率与推理延迟

动手实践——分步指南

  1. 环境准备:使用 npm create @anthropic-ai/claude-code@latest 初始化本地工程目录,并通过 CLI 启动交互式会话。
  2. 观察工具预算裁剪:创建一个包含大段冗余内容(如日志文件或大型 JSON)的测试文件,要求模型读取并分析,在终端输出中关注超长内容的自动替换与截断提示。
  3. 触发完整压缩管线:进行多轮代码重构任务(连续读写文件、运行命令、调用子 Agent),每轮结束后记录 Token 计数变化及上下文流转状态,观察从 snip 到 autocompact 的渐进过程。
  4. 测试结构化工具配置:在 CLI 参数或工具定义中调整 maxResultSizeChars 阈值,对比不同大小限制下 Claude Code 裁剪粒度的差异。
  5. 验证 Prompt Cache 协同效果:将 System Prompt 拆分为“静态全局指令(边界前)”与“动态会话指令(边界后)”,通过官方诊断工具或代码注释查看缓存命中情况。
  6. 长任务恢复演练:主动触发上下文超限场景,使用 /compact 清理并执行 /resume,验证 Preserved Segment 是否被正确保留并重新拼接进主循环。

三大推荐资源

  1. 1
    Anthropic 官方提示工程指南(上下文窗口管理)

    官方文档详解 Context Window 工作原理、Windowing 策略及长对话优化的核心原则,是理解 Anthropic 压缩机制的基础。

    https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/context-windows-and-windowing

  2. 2
    Claude Code 官方 GitHub 仓库

    提供 Claude Code 的完整源码、架构文档与实践示例,便于直接阅读 `query.ts` 与 `sessionStorage.ts` 等核心上下文管理模块。

    https://github.com/anthropics/claude-code

  3. 3
    Prompt Engineering Guide(开源实战库)

    涵盖 Prompt Cache、长文本压缩策略及大模型工程化交互模式,提供大量可直接复用的架构设计与优化模板。

    https://www.promptingguide.ai/zh/

链接由 AI 推荐——使用前建议快速核实。