Skip to content
源码分析手册

上下文窗口管理:动态装配、剪裁与压缩重组

在 Claude Code 的设计中,上下文窗口(Context Window)不是一个静态增长的缓冲区,而是一个高度动态的、经过精细剪裁的“工作视图”。它是 Claude 能够理解超大规模项目并保持高效推理的核心。

它解决了什么问题

上下文管理本质上是 Prompt 的动态物化(Materialization)协议

它旨在解决长会话中的三大核心矛盾:项目文件太多放不下(Token 溢出)、无关信息干扰推理(注意力漂移)、以及 Token 消耗带来的成本与延迟。Claude Code 不会将所有文件一股脑塞给模型,而是根据当前任务状态、用户需求和历史交互,实时构建一份最相关的“上下文快照”。

运行时的真相

上下文窗口的生命周期由四层装配与重组逻辑驱动:

  1. 启动装配层(Environment Mirroring): 在 claude-code-opensource/src/context.ts 中,系统会首先构建基础环境镜像。

    • Git 状态:包含当前分支、主分支对比、近期提交摘要和 git status。这些信息是快照式的,在会话中不会实时通过 fs watcher 自动更新,而是依赖重新装配。
    • 规则注入:核心是 CLAUDE.md 和各级 .claude/rules 的递归读取,以及本地记忆文件(Memory Files)的注入。
  2. 按需补载层(Lazy Loading): Claude Code 的上下文是高度懒加载的。

    • Skill 工具集:只有当模型在推理中显式需要某种能力时,相关的 Skill 定义才会被载入。
    • 动态路径扫描:当用户在 Prompt 中提到特定路径或文件时,系统会实时检索并注入相关的子目录规则。
    • MCP 资源:通过 @ 符号引用的外部资源,是在发送 Prompt 的前一刻才被物化为附件的。
  3. 精细剪裁层(Auto Pruning): 在正式发送前,系统会对物料进行预处理以防止溢出。

    • Git Status 截断:如果 git status 输出超过 2000 字符,会被强行截断,并提示用户通过 BashTool 自行查看。
    • 附件化优化:大量的工具输出(如大规模的 grep 结果)会被转为附件(Attachments)而非直接放在正文中,以利用 Claude 3 家族对长上下文附件的处理优势。
  4. 压缩重组层(Context Compaction): 当 Token 接近临界值或用户手动执行 /compact 时,系统会启动 claude-code-opensource/src/services/compact/compact.ts 中的算法。

    • 分叉总结:系统会启动一个“影子 Agent”(通过 runForkedAgent),在隔离会话中总结历史。它会提炼出:事实性发现(如 Bug 原因)、当前任务进度(Plan/Todos)和已修改文件列表。
    • 物料保留:压缩不是简单的截断。算法会强制保留 CLAUDE.md、关键规则和最近读写过的文件附件(File Attachments),防止压缩后出现即时“失忆”。
    • 增量策略:系统支持“层级总结”,保留上一次压缩后的简报,仅对新产生的对话进行递归归纳,从而最大化节省总结本身的 Token 成本。

实战注意事项

  • 非实时性陷阱:Git 状态和项目层级规则在 getUserContext 中是经过 memoize 缓存的。如果你在会话中通过 Bash 切换了分支,Claude 的 System Prompt 可能仍保留着旧分支信息,除非触发了 /compact 或重新启动。
  • 压缩代价:执行 /compact 虽然腾出了 Token 空间,但会导致模型丢失极细微的历史痕迹(如之前被否决的拼写细节)。如果你的任务强依赖于“刚才那个拼写错误”,压缩后将无法回溯。
  • Bare 模式的盲区:在 --bare 模式下,自动化的 CLAUDE.md 发现、CWD 遍历和背景总结都会被彻底关闭,上下文退化为仅包含当前对话和显式追加的目录。
  • 附件优先:模型看到的 Transcript 与你看到的终端输出并不一致。模型视图中充斥着大量的结构化 XML 元数据和隐藏的附件引用。

推荐阅读路径

源码锚点

  • claude-code-opensource/src/context.ts — 上下文装配与缓存的主入口(memoize 逻辑所在)。
📄 src/context.ts — 上下文装配与缓存的主入口(`memoize` 逻辑所在)。L36-65 of 190
typescript
export const getGitStatus = memoize(async (): Promise<string | null> => {
  if (process.env.NODE_ENV === 'test') {
    // Avoid cycles in tests
    return null
  }

  const startTime = Date.now()
  logForDiagnosticsNoPII('info', 'git_status_started')

  const isGitStart = Date.now()
  const isGit = await getIsGit()
  logForDiagnosticsNoPII('info', 'git_is_git_check_completed', {
    duration_ms: Date.now() - isGitStart,
    is_git: isGit,
  })

  if (!isGit) {
    logForDiagnosticsNoPII('info', 'git_status_skipped_not_git', {
      duration_ms: Date.now() - startTime,
    })
    return null
  }

  try {
    const gitCmdsStart = Date.now()
    const [branch, mainBranch, status, log, userName] = await Promise.all([
      getBranch(),
      getDefaultBranch(),
      execFileNoThrow(gitExe(), ['--no-optional-locks', 'status', '--short'], {
        preserveOutputOnError: false,
  • claude-code-opensource/src/services/compact/compact.ts — 核心压缩重组算法与影子总结 Agent 的实现。
📄 src/services/compact/compact.ts — 核心压缩重组算法与影子总结 Agent 的实现。L37-41 of 1706
typescript
  getAgentListingDeltaAttachment,
  getDeferredToolsDeltaAttachment,
  getMcpInstructionsDeltaAttachment,
} from '../../utils/attachments.js'
import { getMemoryPath } from '../../utils/config.js'
  • claude-code-opensource/src/utils/claudemd.ts — 指令文件与路径规则的发现读取逻辑。
📄 src/utils/claudemd.ts — 指令文件与路径规则的发现读取逻辑。L72-75 of 1480
typescript
  type InstructionsLoadReason,
  type InstructionsMemoryType,
} from './hooks.js'
import type { MemoryType } from './memory/types.js'

基于 Claude Code v2.1.88 开源快照的深度分析