2.1 Context Management¶
本节摘要
从State Management角度看,Context Management回答的核心问题是:每次 LLM 调用时Context 由什么组成、怎么保持稳定不变的部分尽量缓存、Context 快满时怎么丢弃、丢弃后怎么恢复关键信息? 本节按「组成 → 稳定性 → 丢弃 → 恢复」的主线展开。
一、Context 由什么组成¶
每次 API 调用时,LLM 收到的Context 由以下部分构成,各自有不同的变化频率:
| 组成部分 | 变化频率 | 说明 |
|---|---|---|
| System Prompt 静态区域 | 不变(跨组织共享缓存) | 身份、安全指令、工具规范、风格等 7 个 Section |
| System Prompt 动态区域 | 会话级(/clear 或 /compact 时重算) |
环境信息、CLAUDE.md、Skill 指导、MCP 指令等 11+ Section |
| 对话历史 | 每 turn 增长 | 进程内 messages[] 数组,用户消息 + 助手回复 + 工具调用/结果 |
| Attachment | 每 turn 重算 | <system-reminder> 包裹的动态 Context(详见 2.6 Attachment 系统) |
| 工具定义 | 会话级(偶尔变化) | 工具 JSON Schema 数组(详见 2.3 Tool Management) |
对话历史是Context 中增长最快的部分——每次工具调用都会产生 tool_use + tool_result 消息对,在 agentic 场景下增速很快。
二、怎么保持稳定:Prompt Cache 策略¶
Context 中不变的部分越多,API 的 Prompt Cache 命中率越高(省成本、降延迟)。Claude Code 围绕这一目标做了三层缓存设计。
System Prompt 的静态/动态分区¶
getSystemPrompt() 用 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 常量将系统提示切成两半:
| 区域 | Section 数量 | 缓存行为 | 包含内容 |
|---|---|---|---|
| 静态 | 7 个 | scope: 'global',跨组织共享 |
身份、安全、工具规范、风格等 |
| 动态 | 11+ 个 | 会话级客户端缓存 | 环境、CLAUDE.md、Skill 指导、MCP 等 |
静态区域约占系统提示的 60-70%。Anthropic API 对这部分做服务端缓存,每次 API 调用都命中。
Section 的两种缓存类型¶
| 类型 | 行为 | 何时使用 |
|---|---|---|
systemPromptSection(name, compute) |
计算一次后缓存,/clear 或 /compact 才重算 |
大多数 Section |
DANGEROUS_uncachedSystemPromptSection(name, compute, reason) |
每轮重算 | MCP 指令等必须实时更新的内容 |
DANGEROUS_ 前缀是故意的命名约定——提醒维护者「这个 Section 会破坏 Prompt Cache」。
环境 Context 的 memoize¶
getSystemContext()(git 状态)和 getUserContext()(CLAUDE.md + 日期)通过 memoize 缓存,单次会话仅执行一次。CLAUDE.md 作为 user context 前缀注入,不放进 system prompt 静态区——保护缓存。
三、怎么丢弃:六层渐进式压缩¶
当Context 接近窗口上限时,Claude Code 通过六层渐进式压缩来丢弃旧状态。核心原则:能不破坏缓存就不破坏,能不丢信息就不丢。
graph LR
A["L1: 清除旧结果\n无损"] -->|"不足"| B["L2: API 缓存编辑\n保持缓存"]
B -->|"不足"| C["L3: API 原生裁剪\n委托 API"]
C -->|"不足"| D["L4: Session Memory\n缓存失效⚠️"]
D -->|"不足"| E["L5: LLM 摘要\n有损"]
E -->|"不足"| F["L6: 紧急折叠\n核弹选项"]
| 层级 | 触发条件 | 做了什么 | 丢弃了什么 | 破坏缓存? |
|---|---|---|---|---|
| L1 Time-Based | 消息 > 60 分钟未引用 | 清除旧 tool_result 文本,保留调用骨架 |
过时的工具输出 | ❌ |
| L2 Cached | 工具调用超阈值 | API cache_edits 清理 |
API 端缓存消息 | ❌ |
| L3 API MC | input > 180K (90%) | 委托 API 原生 context_management |
API 自主裁剪 | ❌ |
| L4 Session Memory | > 167K (83.5%) | 用已提取的 Session Memory 替代完整历史 | 全部旧消息 | ⚠️ 是 |
| L5 Legacy | L4 失败 fallback | LLM 生成 9 章节结构化摘要 | 全部旧消息(摘要替代) | ⚠️ 是 |
| L6 Collapse | ~90% / 95% 阻断 | 紧急压缩(与 autocompact 互斥) | 最大范围丢弃 | ⚠️ 是 |
关键分界线在 L3 和 L4 之间:L1-L3 在保持缓存的前提下尽量释放空间;L4 开始完全重建Context,缓存失效。
精确阈值(200K 窗口)¶
| 阈值 | 值 | 占比 | 设计原因 |
|---|---|---|---|
| 有效窗口 | 180,000 | 90% | 预留 20K 给输出(p99.99 = 17,387) |
| 自动压缩 | 167,000 | 83.5% | 留 ~13K 给当前 turn 完成 |
| 警告 | 147,000 | 73.5% | 给用户反应时间 |
| 阻断 | 177,000 | 88.5% | 防止 API 400 错误 |
熔断器¶
MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3——连续失败 3 次后停止尝试。源码记录:1,279 sessions had 50+ consecutive failures, wasting ~250K API calls/day globally。
四、丢弃后怎么恢复:Rehydration¶
L4/L5 压缩会清空全部旧消息。压缩后系统自动恢复关键状态,防止模型「失忆」到无法继续:
| 恢复项 | 预算 | 来源 |
|---|---|---|
| 最近操作的文件 | 5 个文件 × 5K tokens ≈ 50K | readFileState |
| 活跃 Skill 全文 | 每个 5K tokens,总 ≈ 25K | invoked_skills attachment |
| CLAUDE.md | 全层级重加载 | 磁盘文件 |
| AGENTS.md 关键段落 | Session Startup + Red Lines | postCompactCleanup |
恢复顺序(compact.ts):
缓存清理:压缩后清除 getUserContext 缓存 + resetGetMemoryFilesCache + 执行 processSessionStartHooks('compact')——确保重新读取可能已变化的文件。
关键文件索引¶
| 文件 | 职责 |
|---|---|
src/constants/prompts.ts |
getSystemPrompt() + 静态/动态分区 |
src/constants/systemPromptSections.ts |
Section 两种缓存类型 |
src/context.ts |
getSystemContext() / getUserContext() + memoize |
src/services/compact/autoCompact.ts |
六层压缩调度 + 阈值 + 熔断器 |
src/services/compact/compact.ts |
Rehydration 重注入逻辑 |
src/services/compact/postCompactCleanup.ts |
压缩后缓存清理 |