Claude Code 上下文压缩分析

Claude Code 上下文压缩分析

1. 前言

大模型对话系统面临的核心挑战在于有限的上下文窗口与无限增长对话历史之间的矛盾。上下文压缩的重要性体现在三个层面:一是成本控制 ,每次API调用都按token计费,过长的上下文会急剧增加推理成本;二是响应延迟 ,窗口满载时模型处理速度下降,影响用户体验;三是信息保真,若不压缩,早期关键指令、技术决策或用户偏好会在滚动中被截断遗忘,导致模型"迷失在中间"。

然而,有效压缩并非易事,面临多重挑战:

  • 信息选择性损失:压缩本质是有损的,如何判断哪些细节(如错误修复过程、代码片段)必须保留,哪些可以概括,没有通用标准。
  • 结构完整性 :对话中常存在工具调用对(tool_use / tool_result)、思考链(thinking blocks)等结构化依赖,简单截断会破坏消息间的逻辑配对,引发模型调用错误。
  • 场景适配差异:首次压缩需要完整摘要,增量压缩应避免重复,缓存命中场景则要求摘要能独立支撑后续任务------单一压缩策略无法满足所有需求。
  • 性能开销:调用LLM生成摘要本身消耗token和时间,若压缩过于频繁,反而得不偿失;若触发不及时,窗口溢出又会导致请求失败。

我们可以通过学习Claude Code 的上下文压缩机制,使大模型应用系统在速度、成本和保真度之间寻求动态平衡

2. Claude Code 上下文压缩机制

工程实现了多层次的上下文压缩机制。工程中主要有三种上下文压缩方式:

  1. 传统压缩 (compactConversation) - 完整的会话摘要
  2. 会话内存压缩 (trySessionMemoryCompaction) - 实验性压缩
  3. 微压缩 (microcompact) - 细粒度的工具结果清理

2.1. 三种压缩的优先级和关系

时间触发
缓存触发
不触发




API调用前
微压缩
时间微压缩
缓存微压缩
继续
需要压缩?
会话记忆可用?
不压缩
会话记忆压缩
传统压缩
压缩完成

2.2. 执行顺序

  1. 微压缩 - 每次调用前都尝试(轻量级)

    • 时间触发优先
    • 缓存触发次之
  2. 会话记忆压缩 - 在需要压缩时优先尝试

    • 如果会话记忆可用且有效
    • 且压缩后不超过阈值
  3. 传统压缩 - 最后的兜底方案

    • 调用 LLM 生成摘要
    • 成本最高但最可靠

2.3. 触发时机总结

压缩类型 触发时机 触发条件 成本
时间微压缩 每次调用前 间隔 > 60分钟 + 主线程 最低
缓存微压缩 每次调用前 功能启用 + 模型支持 + 主线程
会话记忆压缩 需要压缩时 功能启用 + 记忆文件存在 + 非空
传统压缩 需要压缩时 Token数超过阈值 + 自动压缩启用

3. 压缩提示词分析

主要文件:src/services/compact/prompt.ts

这个文件的核心目的是为对话压缩提供结构化的提示词,确保模型生成高质量、格式统一的摘要,同时避免不必要的工具调用。

3.1. 提供三种使用场景的压缩模式

使用不同的提示词模板(BASE、PARTIAL、UP_TO)处理不同的压缩场景:

  1. BASE: 确保完整性,适合首次压缩
  2. PARTIAL: 避免重复,适合增量压缩
  3. UP_TO: 为后续消息提供上下文,适合缓存命中场景

这三种设计体现了对话压缩的不同阶段和需求,确保在各种场景下都能生成高质量的摘要。

3.1.1. 完整对话压缩(BASE_COMPACT_PROMPT)

  • 用途: 压缩整个对话历史

  • 场景示例

text 复制代码
对话历史: [消息1] [消息2] [消息3] [消息4] [消息5]
                    ↓
                首次压缩
                    ↓
摘要: 完整对话摘要
  • 提示词结构:
text 复制代码
NO_TOOLS_PREAMBLE
├─ DETAILED_ANALYSIS_INSTRUCTION_BASE (分析整个对话)
└─ BASE_COMPACT_PROMPT
   ├─ 9个必需部分
   │  ├─ 1. Primary Request and Intent
   │  ├─ 2. Key Technical Concepts
   │  ├─ 3. Files and Code Sections
   │  ├─ 4. Errors and fixes
   │  ├─ 5. Problem Solving
   │  ├─ 6. All user messages
   │  ├─ 7. Pending Tasks
   │  ├─ 8. Current Work (重点描述最近的工作)
   │  └─ 9. Optional Next Step
   └─ NO_TOOLS_TRAILER

关键特征:

  • 分析范围: 整个对话
  • 使用 DETAILED_ANALYSIS_INSTRUCTION_BASE - 指导按时间顺序分析每个消息
  • 包含完整的用户消息列表
  • 强调"Current Work"部分,描述最近的工作
  • 适用于: 首次压缩或需要完整摘要的场景

3.1.2. 最近消息压缩(PARTIAL_COMPACT_PROMPT)

  • 用途: 仅压缩最近的消息,保留早期上下文
  • 场景示例:
text 复制代码
已保留: [消息1] [消息2] [消息3]
新增:   [消息4] [消息5]
                    ↓
                部分压缩
                    ↓
摘要: 仅 [消息4][消息5] 的摘要
最终: [消息1][消息2][消息3] + 摘要
  • 提示词结构:

    NO_TOOLS_PREAMBLE
    ├─ DETAILED_ANALYSIS_INSTRUCTION_PARTIAL (仅分析最近消息)
    └─ PARTIAL_COMPACT_PROMPT
    ├─ 9个必需部分
    │ ├─ 1. Primary Request and Intent (来自最近消息)
    │ ├─ 2. Key Technical Concepts (最近讨论的)
    │ ├─ 3. Files and Code Sections (最近操作的)
    │ ├─ 4. Errors and fixes (最近遇到的)
    │ ├─ 5. Problem Solving (最近解决的)
    │ ├─ 6. All user messages (仅最近的消息)
    │ ├─ 7. Pending Tasks (最近的任务)
    │ ├─ 8. Current Work (最近的工作)
    │ └─ 9. Optional Next Step
    └─ NO_TOOLS_TRAILER

  • 关键特征 :

    • 分析范围: 仅最近的消息
    • 使用 DETAILED_ANALYSIS_INSTRUCTION_PARTIAL - 指导仅分析最近消息
    • 明确说明: "The earlier messages are being kept intact and do NOT need to be summarized"
    • 适用于: 已经有早期上下文,只需压缩新增内容的场景

3.1.3. 压缩到某点(缓存命中)(PARTIAL_COMPACT_UP_TO_PROMPT)

  • 用途: 压缩到某个点,摘要将放在新会话的开头
  • 场景示例:
text 复制代码
缓存命中: [消息1] [消息2] [消息3] [消息4] [消息5]
                    ↓
                压缩到消息3
                    ↓
摘要: [消息1][消息2][消息3] 的摘要
后续: 摘要 + [消息4] [消息5]
  • 提示词结构:

    NO_TOOLS_PREAMBLE
    ├─ DETAILED_ANALYSIS_INSTRUCTION_BASE (分析整个对话)
    └─ PARTIAL_COMPACT_UP_TO_PROMPT
    ├─ 9个必需部分
    │ ├─ 1. Primary Request and Intent
    │ ├─ 2. Key Technical Concepts
    │ ├─ 3. Files and Code Sections
    │ ├─ 4. Errors and fixes
    │ ├─ 5. Problem Solving
    │ ├─ 6. All user messages
    │ ├─ 7. Pending Tasks
    │ ├─ 8. Work Completed (替换 Current Work)
    │ └─ 9. Context for Continuing Work (替换 Optional Next Step)
    └─ NO_TOOLS_TRAILER

  • 关键特征 :

    • 分析范围: 整个对话
    • 使用 DETAILED_ANALYSIS_INSTRUCTION_BASE - 与BASE相同
    • 关键区别 :
      • 第8部分是 "Work Completed" 而非 "Current Work"
      • 第9部分是 "Context for Continuing Work" 而非 "Optional Next Step"
    • 特殊说明: "This summary will be placed at the start of a continuing session; newer messages that build on this context will follow after your summary"
    • 适用于: 缓存命中场景,摘要将作为新会话的开头上下文

3.1.4. 三种场景差异对比表

特征 BASE PARTIAL UP_TO
分析范围 整个对话 仅最近消息 整个对话
分析指令 DETAILED_ANALYSIS_INSTRUCTION_BASE DETAILED_ANALYSIS_INSTRUCTION_PARTIAL DETAILED_ANALYSIS_INSTRUCTION_BASE
用户消息 所有消息 仅最近消息 所有消息
第8部分 Current Work Current Work Work Completed
第9部分 Optional Next Step Optional Next Step Context for Continuing Work
适用场景 首次压缩 增量压缩 缓存命中
上下文说明 "Earlier messages are being kept intact" "Newer messages will follow after your summary"

3.2. 提示词(Prompts)分析

3.2.1. NO_TOOLS_PREAMBLE

  • 用途: 禁止模型调用工具的前导提示
  • 关键内容: 强调只能返回文本,不能调用任何工具,工具调用会被拒绝
  • 位置: 文件开头,用于所有压缩提示词的前缀

3.2.2. DETAILED_ANALYSIS_INSTRUCTION_BASE

  • 用途: 基础详细分析指令(针对整个对话)
  • 关键内容: 指导模型按时间顺序分析每个消息,识别用户请求、技术决策、代码模式、错误修复等
3.2.2.1. DETAILED_ANALYSIS_INSTRUCTION_BASE 详细内容
text 复制代码
// 常量定义:详细分析指令基础模板
const DETAILED_ANALYSIS_INSTRUCTION_BASE = `在提交最终总结前,请先将你的分析内容包裹在 <analysis> 标签内,用于梳理思路并确保覆盖所有必要要点。分析过程需遵循以下要求:

1. 按时间顺序逐条分析对话中的每一条消息与每个环节,针对每个环节完整明确以下内容:
   - 用户的明确需求与核心意图
   - 你响应用户需求的处理思路
   - 关键决策、核心技术概念与代码范式
   - 各类具体细节,例如:
     - 文件名
     - 完整代码片段
     - 函数签名
     - 文件修改记录
   - 遇到的错误及对应的修复方法
   - 重点关注用户给出的具体反馈,尤其是用户要求调整操作方式的内容

2. 二次核查技术内容的准确性与完整性,确保全面覆盖所有要求的要素。`

3.2.3. BASE_COMPACT_PROMPT

  • 用途: 基础压缩提示词(完整对话压缩)
  • 关键内容: 包含9个必需的摘要部分,从"Primary Request"到"Optional Next Step"
text 复制代码
BASE_COMPACT_PROMPT 包含:
1. NO_TOOLS_PREAMBLE - 严格禁止工具调用
2. DETAILED_ANALYSIS_INSTRUCTION - 详细的分析步骤
3. 摘要结构要求:
   - 核心需求与意图(Primary Request and Intent)
   - 关键技术概念(Key Technical Concepts)
   - 文件与代码片段(Files and Code Sections)
   - 错误与修复(Errors and fixes)
   - 问题解决(Problem Solving)
   - 全部用户消息(All user messages)
   - 待办任务(Pending Tasks)
   - 当前工作(Current Work)
   - 可选下一步(Optional Next Step)
4. NO_TOOLS_TRAILER - 最后的工具调用禁止提醒
  • 依赖: 使用 DETAILED_ANALYSIS_INSTRUCTION_BASE
3.2.3.1. BASE_COMPACT_PROMPT 详细内容
text 复制代码
// 常量定义:基础精简版对话总结提示词
const BASE_COMPACT_PROMPT = `你的任务是为当前为止的对话生成一份详细总结,重点关注用户的明确需求和你此前执行的操作。
这份总结需全面记录核心技术细节、代码范式和架构决策,这些信息是持续开展开发工作、不丢失上下文的关键。

${DETAILED_ANALYSIS_INSTRUCTION_BASE}

你的总结需包含以下板块:

1. 核心需求与意图:详细记录用户的所有明确需求和核心意图
2. 关键技术概念:列出对话中涉及的所有重要技术概念、技术栈和框架
3. 文件与代码片段:逐一列明已查看、修改或创建的具体文件和代码片段。重点关注最新消息,适用场景下附上完整代码,并说明查看/修改该文件的原因
4. 错误与修复:记录所有遇到的错误及对应的解决方法。重点关注用户的具体反馈,尤其是用户要求调整操作的内容
5. 问题解决:记录已解决的问题,以及仍在进行的故障排查工作
6. 全部用户消息:列出**所有**非工具调用结果的用户消息,这是理解用户反馈和意图变化的关键依据
7. 待办任务:梳理所有已被明确要求执行的待办工作
8. 当前工作:详细描述发起本次总结前**正在执行**的具体工作,重点关注用户和助手的最新对话。适用场景下标注文件名和代码片段
9. 可选下一步:列出与当前最新工作相关的下一步操作。**重要提示**:确保该步骤**严格贴合**用户最新的明确需求,以及发起总结前正在执行的任务。
若上一项任务已完成,仅当下一步操作完全符合用户需求时才可列出。切勿擅自处理无关需求或已完成的旧需求,需先与用户确认。
若存在下一步操作,需直接引用最新对话原文,精准说明正在执行的任务和中断节点。必须逐字引用,避免任务理解偏差。

以下是输出格式示例:
<example>
<analysis>
[你的思考过程,确保全面、准确覆盖所有要点]
</analysis>

<summary>
1. 核心需求与意图:
   [详细描述]

2. 关键技术概念:
   - [概念1]
   - [概念2]
   - [......]

3. 文件与代码片段:
   - [文件名1]
      - [该文件的重要性说明]
      - [对该文件的修改内容(如有)]
      - [关键代码片段]
   - [文件名2]
      - [关键代码片段]
   - [......]

4. 错误与修复:
    - [错误1详细描述]:
      - [错误修复方法]
      - [用户对该错误的反馈(如有)]
    - [......]

5. 问题解决:
   [已解决问题和 ongoing 故障排查的描述]

6. 全部用户消息: 
    - [详细的非工具调用类用户消息]
    - [......]

7. 待办任务:
   - [任务1]
   - [任务2]
   - [......]

8. 当前工作:
   [当前工作的精准描述]

9. 可选下一步:
   [待执行的可选下一步操作]

</summary>
</example>

请严格遵循该结构,基于当前对话生成总结,确保内容精准、全面。

上下文内可能包含额外的总结指令,若有此类指令,生成总结时请一并遵守。指令示例如下:
<example>
## 精简指令
总结对话时重点关注TypeScript代码修改,同时记录你犯下的错误及修复方式。
</example>

<example>
# 总结指令
使用精简模式总结时,重点关注测试输出和代码修改,**逐字记录**文件读取内容。
</example>

3.2.4. DETAILED_ANALYSIS_INSTRUCTION_PARTIAL

  • 用途: 部分详细分析指令(针对最近消息)
  • 关键内容: 与BASE类似,但仅分析最近的消息

3.2.5. PARTIAL_COMPACT_PROMPT

  • 用途: 部分压缩提示词(仅压缩最近消息)
  • 关键内容: 与BASE类似,但强调只总结最近的消息
  • 依赖: 使用 DETAILED_ANALYSIS_INSTRUCTION_PARTIAL

3.2.6. PARTIAL_COMPACT_UP_TO_PROMPT

  • 用途: 部分压缩到某点的提示词
  • 关键内容: 用于缓存命中场景,摘要将放在新会话的开头
  • 依赖: 使用 DETAILED_ANALYSIS_INSTRUCTION_BASE
  • 特殊: 包含"Context for Continuing Work"部分而非"Current Work"

3.2.7. NO_TOOLS_TRAILER

  • 用途: 禁止工具调用的尾部提醒
  • 关键内容: 再次强调不调用工具,只返回文本

3.3. 函数依赖关系

3.3.1. getPartialCompactPrompt()

  • 输入 : customInstructions?, direction?: PartialCompactDirection
  • 输出: 完整的部分压缩提示词字符串
  • 依赖的常量 :
    • NO_TOOLS_PREAMBLE
    • PARTIAL_COMPACT_PROMPTPARTIAL_COMPACT_UP_TO_PROMPT (基于direction参数)
    • NO_TOOLS_TRAILER
  • 逻辑: 根据direction选择模板,添加自定义指令和尾部提醒

3.3.2. getCompactPrompt()

  • 输入 : customInstructions?
  • 输出: 完整的基础压缩提示词字符串
  • 依赖的常量 :
    • NO_TOOLS_PREAMBLE
    • BASE_COMPACT_PROMPT
    • NO_TOOLS_TRAILER
  • 逻辑: 使用基础模板,添加自定义指令和尾部提醒

3.3.3. formatCompactSummary()

  • 输入 : summary: string (原始摘要)
  • 输出: 格式化后的摘要字符串
  • 逻辑 :
    • 移除 <analysis> 标签及其内容(草稿部分)
    • 提取 <summary> 标签内的内容
    • 将XML标签替换为可读的标题
    • 清理多余的空白字符

3.3.4. getCompactUserSummaryMessage()

  • 输入 :
    • summary: string
    • suppressFollowUpQuestions?: boolean
    • transcriptPath?: string
    • recentMessagesPreserved?: boolean
  • 输出: 用户摘要消息字符串
  • 依赖的函数 :
    • formatCompactSummary() - 格式化摘要
  • 逻辑 :
    • 调用 formatCompactSummary() 格式化输入摘要
    • 构建基础摘要消息
    • 根据参数添加额外信息(转录路径、保留消息说明)
    • 如果 suppressFollowUpQuestions 为 true,添加继续工作的指令
    • 检查 PROACTIVE/KAIROS 功能是否激活,添加自主模式说明

3.4. 提示词定义和函数调用图

text 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    Prompt Constants                             │
│  ┌──────────────────┐  ┌──────────────────┐                     │
│  │ NO_TOOLS_PREAMBLE│  │NO_TOOLS_TRAILER  │                     │
│  └────────┬─────────┘  └──────────────────┘                     │
│           │                                                     │
│  ┌────────▼──────────────────────────────────────────────────┐  │
│  │BASE_COMPACT_PROMPT                                        │  │
│  │DETAILED_ANALYSIS_INSTRUCTION_BASE                         │  │
│  └───────────────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │PARTIAL_COMPACT_PROMPT                                     │  │
│  │DETAILED_ANALYSIS_INSTRUCTION_PART                         │  │
│  │PARTIAL_COMPACT_UP_TO_PROMPT                               │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Export Functions                             │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │ getPartialCompactPrompt(direction, customInstructions)   │   │
│  │  ├─ Uses: NO_TOOLS_PREAMBLE                              │   │
│  │  ├─ Uses: PARTIAL_COMPACT_PROMPT or                      │   │
│  │  │         PARTIAL_COMPACT_UP_TO_PROMPT (based on dir)   │   │
│  │  ├─ Appends: customInstructions (if provided)            │   │
│  │  └─ Appends: NO_TOOLS_TRAILER                            │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │ getCompactPrompt(customInstructions)                     │   │
│  │  ├─ Uses: NO_TOOLS_PREAMBLE                              │   │
│  │  ├─ Uses: BASE_COMPACT_PROMPT                            │   │
│  │  ├─ Appends: customInstructions (if provided)            │   │
│  │  └─ Appends: NO_TOOLS_TRAILER                            │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │ formatCompactSummary(summary)                            │   │
│  │  ├─ Strips: <analysis>...</analysis> section             │   │
│  │  ├─ Extracts: <summary>...</summary> content             │   │
│  │  ├─ Replaces: XML tags with readable headers             │   │
│  │  └─ Cleans: Extra whitespace                             │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │ getCompactUserSummaryMessage(                            │   │
│  │   summary, suppressFollowUpQuestions,                    │   │
│  │   transcriptPath, recentMessagesPreserved)               │   │
│  │  ├─ Calls: formatCompactSummary(summary)                 │   │
│  │  ├─ Builds: Base summary message                         │   │
│  │  ├─ Appends: Transcript path (if provided)               │   │
│  │  ├─ Appends: Recent messages preserved note (if true)    │   │
│  │  └─ Handles: Suppress follow-up questions logic          │   │
│  │      └─ Checks: PROACTIVE/KAIROS features                │   │
│  └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

3.5. 数据流向

复制代码
用户请求
    │
    ▼
getPartialCompactPrompt() / getCompactPrompt()
    │
    ▼
生成提示词 (NO_TOOLS_PREAMBLE + template + customInstructions + NO_TOOLS_TRAILER)
    │
    ▼
发送给模型
    │
    ▼
模型返回摘要 (包含 <analysis> 和 <summary> 标签)
    │
    ▼
formatCompactSummary()
    │
    ▼
移除 <analysis>,格式化 <summary>
    │
    ▼
getCompactUserSummaryMessage()
    │
    ▼
添加上下文信息和继续指令
    │
    ▼
最终摘要消息

3.6. 压缩结果结构

\src\services\compact\compact.ts:299-310

typescript 复制代码
CompactionResult = {
  boundaryMarker: SystemMessage,      // 压缩边界标记
  summaryMessages: UserMessage[],     // 摘要消息
  attachments: AttachmentMessage[],   // 附件
  hookResults: HookResultMessage[],   // 钩子结果
  messagesToKeep?: Message[],         // 保留的消息
  userDisplayMessage?: string,        // 用户显示消息
  preCompactTokenCount?: number,      // 压缩前token数
  postCompactTokenCount?: number,     // 压缩后token数
  truePostCompactTokenCount?: number, // 真实后压缩token数
  compactionUsage?: TokenUsage        // 压缩API使用情况
}

4. 传统压缩流程

传统压缩主要通过 autoCompactIfNeeded() 函数触发,位于 src/services/compact/autoCompact.ts#L160-L291

4.1. 核心触发逻辑

typescript 复制代码
// 在 autoCompactIfNeeded() 中
const shouldCompact = await shouldAutoCompact(
  messages,
  model,
  querySource,
  snipTokensFreed,
)

4.2. 触发条件

src/services/compact/autoCompact.ts#L105-L110

typescript 复制代码
// 自动压缩阈值计算
const autoCompactThreshold = effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS
// AUTOCOMPACT_BUFFER_TOKENS = 13,000
  • 当上下文使用量超过阈值时触发自动压缩
  • 默认保留13K token缓冲区

4.3. 熔断机制

typescript 复制代码
// 最多连续失败 3 次后停止尝试
if (tracking?.consecutiveFailures >= MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES) {
  return { wasCompacted: false }
}
// MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3

4.4. 压缩逻辑

src/services/compact/compact.ts#L363-L750

复制代码
步骤1: 执行PreCompact钩子
   └─ 收集自定义指令和用户显示消息

步骤2: 构建压缩提示词
   └─ getCompactPrompt(customInstructions)
   └─ 包含详细的分析和摘要结构要求

步骤3: 流式压缩摘要
   ├─ 尝试使用缓存共享的分叉代理 (promptCacheSharingEnabled)
   ├─ 或使用常规流式路径
   └─ 处理prompt-too-long错误 (最多重试3次)

步骤4: 清理缓存
   ├─ 保存文件读取状态
   ├─ 清空readFileState
   └─ 清空loadedNestedMemoryPaths

步骤5: 生成附件
   ├─ createPostCompactFileAttachments (最多5个文件)
   ├─ 创建计划附件
   ├─ 创建技能附件
   └─ 重新声明延迟工具

步骤6: 执行PostCompact钩子
   └─ 执行会话开始钩子

步骤7: 创建压缩边界和摘要消息
   └─ 返回CompactionResult

5. 会话内存压缩流程

会话记忆压缩通过 trySessionMemoryCompaction() 函数触发,位于 src/services/compact/sessionMemoryCompact.ts#L536-L630

5.1. 核心触发逻辑

autoCompactIfNeeded() 中优先尝试:

typescript 复制代码
const sessionMemoryResult = await trySessionMemoryCompaction(
  messages,
  toolUseContext.agentId,
  recompactionInfo.autoCompactThreshold,
)
if (sessionMemoryResult) {
  // 使用会话记忆压缩成功
}

5.2. 触发条件

5.2.1. 必要条件(必须全部满足)

  1. 功能已启用

    typescript 复制代码
    shouldUseSessionMemoryCompaction() === true
    • process.env.ENABLE_CLAUDE_CODE_SM_COMPACT 为 true(测试覆盖)
    • 或者:
      • tengu_session_memory feature 为 true
      • tengu_sm_compact feature 为 true
    • process.env.DISABLE_CLAUDE_CODE_SM_COMPACT 不为 true
  2. 会话记忆文件存在且非空

    typescript 复制代码
    const sessionMemory = await getSessionMemoryContent()
    // sessionMemory 必须存在
    // 且 !await isSessionMemoryEmpty(sessionMemory)
  3. 压缩后不超过阈值(仅自动压缩时检查)

    typescript 复制代码
    if (autoCompactThreshold !== undefined && 
        postCompactTokenCount >= autoCompactThreshold) {
      return null // 不使用会话记忆压缩
    }

5.2.2. 配置参数

typescript 复制代码
// 默认配置
DEFAULT_SM_COMPACT_CONFIG = {
  minTokens: 10_000,           // 最少保留 10k tokens
  minTextBlockMessages: 5,     // 最少保留 5 条文本消息
  maxTokens: 40_000,           // 最多保留 40k tokens
}

远程配置覆盖(通过 GrowthBook):

  • tengu_sm_compact_config - 可以覆盖上述配置

5.2.3. 两种使用场景

  1. 正常情况lastSummarizedMessageId 已设置

    • 只保留该 ID 之后的消息
    • 之前的内容由会话记忆覆盖
  2. 恢复会话lastSummarizedMessageId 未设置

    • 保留所有消息
    • 使用会话记忆作为摘要

5.3. 压缩逻辑

src/services/compact/sessionMemoryCompact.ts#L423-L630

复制代码
步骤1: 检查是否启用会话内存压缩
   ├─ 检查 feature: tengu_session_memory
   └─ 检查 feature: tengu_sm_compact

步骤2: 加载远程配置 (仅一次)
   └─ getDynamicConfig_BLOCKS_ON_INIT('tengu_sm_compact_config')

步骤3: 等待会话内存提取完成
   └─ waitForSessionMemoryExtraction()

步骤4: 计算保留消息索引
   ├─ 查找 lastSummarizedMessageId
   ├─ calculateMessagesToKeepIndex()
   │  ├─ 从 lastSummarizedIndex+1 开始
   │  ├─ 扩展以满足 minTokens 和 minTextBlockMessages
   │  └─ 停止于 maxTokens 或 floor (最后一个边界)
   └─ adjustIndexToPreserveAPIInvariants()
      ├─ 确保 tool_use/tool_result 配对不被拆分
      └─ 确保 sharing message.id 的 thinking blocks 被保留

步骤5: 创建压缩结果
   ├─ truncateSessionMemoryForCompact() - 截断 oversized 段
   ├─ getCompactUserSummaryMessage() - 格式化摘要
   └─ createCompactBoundaryMessage() - 创建边界标记

步骤6: 构建 post-compact 消息
   └─ buildPostCompactMessages(result)

6. 微压缩流程

微压缩通过 microcompactMessages() 函数触发,位于 src/services/compact/microCompact.ts#L258-L530

6.1. 核心触发逻辑

微压缩在每次 API 调用前都会尝试:

typescript 复制代码
export async function microcompactMessages(
  messages: Message[],
  toolUseContext?: ToolUseContext,
  querySource?: QuerySource,
): Promise<MicrocompactResult>

6.2. 时间触发微压缩**

/src/services/compact/microCompact.ts#L370-L430

evaluateTimeBasedTrigger:

  • 检查上次 assistant 消息的时间间隔

  • 超过阈值 (config.gapThresholdMinutes) 时触发

  • 清除除最近 N 个外的所有 compactable tool results

  • COMPACTABLE_TOOLS: Read, Bash, Grep, Glob, WebSearch, WebFetch, Edit, Write

  • 时间间隔超过阈值

typescript 复制代码
const gapMinutes = (Date.now() - lastAssistant.timestamp) / 60_000
gapMinutes >= config.gapThresholdMinutes
// 默认:gapMinutes >= 60 分钟
  • 配置参数
typescript 复制代码
TIME_BASED_MC_CONFIG_DEFAULTS = {
  enabled: false,              // 默认关闭
  gapThresholdMinutes: 60,     // 60 分钟阈值
  keepRecent: 5,               // 保留最近 5 个工具结果
}

6.3. 缓存触发微压缩**

/src/services/compact/microCompact.ts#L230-L368

cachedMicrocompactPath:

  1. 注册可压缩工具 (COMPACTABLE_TOOLS)
  2. 按用户消息分组 tool_results
  3. 确定要删除的工具 (getToolResultsToDelete)
  4. 创建 cache_edits block 并排队
  5. 不修改本地消息内容 (在API层添加 cache_reference/cache_edits)
  6. 记录 baseline cache_deleted_input_tokens

7. 总结

Claude Code 通过三层压缩机制管理上下文,按优先级依次尝试:

  • 微压缩(每次调用前触发):时间间隔超60分钟或缓存命中时,清理部分工具结果(如Read、Bash等),成本最低。

  • 会话记忆压缩(需压缩时优先):依赖已有记忆文件,保留最近10k--40k tokens及至少5条消息,保证tool配对完整,成本中等。

  • 传统压缩(兜底方案):调用LLM生成含9个部分的详细摘要(BASE/PARTIAL/UP_TO三种模板适配不同场景),连续失败3次后熔断,成本最高。

三种压缩按"微压缩 → 会话记忆压缩 → 传统压缩"优先级依次尝试,兼顾效率与可靠性。微压缩几乎无成本快速清理;会话记忆压缩利用持久化记忆避免重复摘要;传统压缩作为最后手段确保上下文不丢失。通过精细的提示词设计和消息保留边界控制,在 token 消耗、响应速度和信息完整性之间取得平衡。

相关推荐
毕胜客源码2 小时前
改进yolov8的香蕉成熟度检测系统,改进前后的模型指标对比,有技术文档,支持图像、视频和摄像实时检测
人工智能·python·深度学习·yolo·django
TheRouter2 小时前
AI Agent 开发中的模型调度策略:何时用便宜模型,何时用强模型
前端·人工智能·react.js
骑着蜗牛找妞2 小时前
cmux 使用教程:专为 AI Coding 打造的终端工作流引擎
人工智能
skywalk81632 小时前
Windows下安装编译安装Whisper-CPP:一个语音实现框架集和高性能推理模型
人工智能·windows·whisper
Spliceㅤ2 小时前
Product-classify-bert项目
人工智能·深度学习·bert
Apache IoTDB2 小时前
【应用案例】电价“先知”!IoTDB 结合 AI 能力,实现电价精准预测
人工智能·iotdb
weixin_416660072 小时前
2026 年 AI 对话转 Word 工具分析:Pandoc、Typora、aitoword 怎么选
人工智能·word
前端不太难2 小时前
深度解析:OpenClaw 多智能体系统四大支柱
人工智能·状态模式·openclaw