Autocompact(自动压缩)机制分析

源码:src/services/compact/autoCompact.ts(352 行) 导出函数:autoCompactIfNeeded,作为 deps.autocompactquery.ts 调用


1. 概述

Autocompact 是 Claude Code 上下文管理的最后一道防线 。当 microcompact 和 context collapse 都无法将上下文压缩到有效窗口内时,autocompact 触发全量消息压缩------调用 LLM 将历史消息批量总结为摘要。

执行位置(query.ts)

scss 复制代码
microcompact (line 414)    →  只清理 tool result 内容
context collapse (line 441) → 消息区间折叠为 LLM 摘要
autocompact (line 454)     →  全量消息压缩为单个摘要 ← 这里

2. 关键常量(第 28-70 行)

MAX_OUTPUT_TOKENS_FOR_SUMMARY(第 30 行)

typescript 复制代码
const MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20_000

压缩摘要的最大 token 数。基于 p99.99 的统计------压缩摘要的输出极少超过 17,387 tokens,取整为 20,000。

Buffer Tokens(第 62-65 行)

typescript 复制代码
export const AUTOCOMPACT_BUFFER_TOKENS = 13_000
export const WARNING_THRESHOLD_BUFFER_TOKENS = 20_000
export const ERROR_THRESHOLD_BUFFER_TOKENS = 20_000
export const MANUAL_COMPACT_BUFFER_TOKENS = 3_000
常量 用途
AUTOCOMPACT_BUFFER_TOKENS 13,000 autocompact 触发的预留缓冲(给 model 回复留空间)
WARNING_THRESHOLD_BUFFER_TOKENS 20,000 token 警告阈值
ERROR_THRESHOLD_BUFFER_TOKENS 20,000 token 错误阈值
MANUAL_COMPACT_BUFFER_TOKENS 3,000 blocking limit 的预留缓冲(/compact 手动触发时更激进)

MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES(第 68-70 行)

typescript 复制代码
const MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3

熔断器阈值。注释说明(第 68-69 行):

BQ 2026-03-10: 1,279 sessions had 50+ consecutive failures (up to 3,272) in a single session, wasting ~250K API calls/day globally.

在引入熔断器之前,某些会话会无意义地重试 autocompact 多达 3000+ 次,每天浪费约 25 万次 API 调用。熔断器在 3 次连续失败后停止重试。


3. 有效上下文窗口计算(第 33-49 行)

typescript 复制代码
export function getEffectiveContextWindowSize(model: string): number {
  const reservedTokensForSummary = Math.min(
    getMaxOutputTokensForModel(model),
    MAX_OUTPUT_TOKENS_FOR_SUMMARY,
  )
  let contextWindow = getContextWindowForModel(model, getSdkBetas())

  const autoCompactWindow = process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW
  if (autoCompactWindow) {
    const parsed = parseInt(autoCompactWindow, 10)
    if (!isNaN(parsed) && parsed > 0) {
      contextWindow = Math.min(contextWindow, parsed)
    }
  }

  return contextWindow - reservedTokensForSummary
}

公式:有效窗口 = min(模型上下文窗口, 环境变量覆盖) - min(模型最大输出, 20,000)

  • 从完整上下文窗口中扣除压缩摘要的输出空间
  • 环境变量 CLAUDE_CODE_AUTO_COMPACT_WINDOW 允许用户限制用于压缩的窗口大小
  • getAutoCompactThreshold(第 72 行)调用,减去 AUTOCOMPACT_BUFFER_TOKENS(13k) 后得到实际触发阈值

调用链

scss 复制代码
getEffectiveContextWindowSize(model)
  └─ getAutoCompactThreshold(model)                  (autoCompact.ts:72)
       └─ 减掉 AUTOCOMPACT_BUFFER_TOKENS (13k)       → autocompact 触发阈值
  └─ 直接调用                                         → blocking limit 计算

4. Autocompact 阈值计算(第 72-91 行)

typescript 复制代码
export function getAutoCompactThreshold(model: string): number {
  const effectiveContextWindow = getEffectiveContextWindowSize(model)
  const autocompactThreshold = effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS
  // ...
  return autocompactThreshold
}

触发阈值 = 有效窗口 - 13,000 tokens

这 13,000 tokens 的缓冲是为了让 model 在压缩后还有空间生成回复。

测试覆盖(第 78-88 行)

typescript 复制代码
const envPercent = process.env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE
if (envPercent) {
  const parsed = parseFloat(envPercent)
  if (!isNaN(parsed) && parsed > 0 && parsed <= 100) {
    const percentageThreshold = Math.floor(effectiveContextWindow * (parsed / 100))
    return Math.min(percentageThreshold, autocompactThreshold)
  }
}

环境变量 CLAUDE_AUTOCOMPACT_PCT_OVERRIDE 允许设置阈值为有效窗口的百分比,用于测试场景。例如设为 90 则阈值为有效窗口的 90%。


5. Token 警告状态计算(第 93-145 行)

typescript 复制代码
export function calculateTokenWarningState(tokenUsage: number, model: string): {
  percentLeft: number
  isAboveWarningThreshold: boolean
  isAboveErrorThreshold: boolean
  isAboveAutoCompactThreshold: boolean
  isAtBlockingLimit: boolean
}

多级阈值

lua 复制代码
有效窗口: |←·································································→|
触发阈值:           |←--- 13k buffer ---→|
                                          ↓ autocompact 触发点
警告阈值:           |←------- 20k -------→|
                                          ↓ 黄色警告
错误阈值:           |←------- 20k -------→|
                                          ↓ 红色警告
Blocking Limit:     |←-- 3k buffer --→|
                                      ↓ 阻塞用户输入

关键逻辑(第 103-106 行)

typescript 复制代码
const autoCompactThreshold = getAutoCompactThreshold(model)
const threshold = isAutoCompactEnabled()
  ? autoCompactThreshold
  : getEffectiveContextWindowSize(model)

const percentLeft = Math.max(0, Math.round(((threshold - tokenUsage) / threshold) * 100))

如果 autocompact 被禁用,warning/error 阈值回退到有效窗口大小而非触发阈值------用户仍然能看到警告,但 autocompact 不会自动触发。

百分比计算(第 108-110 行)

percentLeft 的计算分母是 threshold 而非完整上下文窗口。这意味着告警百分比反映的是相对可用空间的占比,而非相对模型最大能力的占比。


6. Autocompact 启用检查(第 147-158 行)

typescript 复制代码
export function isAutoCompactEnabled(): boolean {
  if (isEnvTruthy(process.env.DISABLE_COMPACT)) return false       // 完全禁用
  if (isEnvTruthy(process.env.DISABLE_AUTO_COMPACT)) return false  // 仅禁用自动
  const userConfig = getGlobalConfig()
  return userConfig.autoCompactEnabled                              // 用户设置
}

两级禁用:

  • DISABLE_COMPACT:同时禁用 autocompact 和手动 /compact
  • DISABLE_AUTO_COMPACT:只禁用自动触发,保留 /compact 命令

7. 是否应该压缩的判断(第 160-239 行)

typescript 复制代码
export async function shouldAutoCompact(
  messages: Message[],
  model: string,
  querySource?: QuerySource,
  snipTokensFreed = 0,
): Promise<boolean>

7.1 递归守卫(第 170-183 行)

typescript 复制代码
if (querySource === 'session_memory' || querySource === 'compact') {
  return false
}
if (feature('CONTEXT_COLLAPSE')) {
  if (querySource === 'marble_origami') {
    return false
  }
}

三个禁止触发 autocompact 的来源:

querySource 原因
session_memory 这是 forked agent(子进程),触发 autocompact 会导致死锁
compact /compact 命令本身就在做压缩,不应该再递归触发
marble_origami 这是 context collapse 的 spawn agent,如果它触发 autocompact,runPostCompactCleanupresetContextCollapse()销毁主线程的 committed log(模块级状态在所有 fork 间共享)

7.2 功能开关检查(第 185-223 行)

三层门控:

  1. isAutoCompactEnabled() --- 用户设置 + 环境变量
  2. Reactive-only 模式REACTIVE_COMPACT feature flag):当 GrowthBook flag tengu_cobalt_raccoon 为 true 时,抑制主动 autocompact,改为依赖 API 返回 413 后的被动压缩(reactive compact)
  3. Context collapse 模式 :当 context collapse 激活时,抑制 autocompact,因为 collapse 的 90%/95% 阈值系统拥有上下文管理权

7.3 为什么 collapse 要抑制 autocompact(第 201-223 行注释)

erlang 复制代码
Context-collapse mode: same suppression. Collapse IS the context
management system when it's on --- the 90% commit / 95% blocking-spawn
flow owns the headroom problem. Autocompact firing at effective-13k
(~93% of effective) sits right between collapse's commit-start (90%)
and blocking (95%), so it would race collapse and usually win, nuking
granular context that collapse was about to save.

三个阈值的位置(相对有效上下文窗口):

erlang 复制代码
collapse 提交开始:   90%
autocompact 触发:    ~93%  ← 正好在中间
collapse blocking:  95%

如果没有抑制,autocompact 会在 ~93% 时抢先触发,把 collapse 准备折叠的上下文一次性压缩成单个摘要,破坏了 collapse 的粒度保持策略。

设计选择 :抑制放在 shouldAutoCompact 而非 isAutoCompactEnabled 中,是为了保留 reactive compact(413 后备,内部调用 shouldAutoCompact)和手动 /compact 以及 session memory 压缩的能力。

7.4 最终 token 检查(第 225-238 行)

typescript 复制代码
const tokenCount = tokenCountWithEstimation(messages) - snipTokensFreed
const threshold = getAutoCompactThreshold(model)
const { isAboveAutoCompactThreshold } = calculateTokenWarningState(tokenCount, model)
return isAboveAutoCompactThreshold
  • snipTokensFreed 是 snip 操作(删除已归档消息的引用)释放的 token 数,从总计数中减掉
  • 最终判断:当前 token 数是否超过 autocompact 触发阈值

8. 核心函数:autoCompactIfNeeded(第 241-351 行)

typescript 复制代码
export async function autoCompactIfNeeded(
  messages: Message[],
  toolUseContext: ToolUseContext,
  cacheSafeParams: CacheSafeParams,
  querySource?: QuerySource,
  tracking?: AutoCompactTrackingState,    // 跟踪状态(跨 turn)
  snipTokensFreed?: number,
): Promise<{ wasCompacted: boolean; compactionResult?: CompactionResult; consecutiveFailures?: number }>

8.1 前置检查

全局禁用(第 253-255 行)

typescript 复制代码
if (isEnvTruthy(process.env.DISABLE_COMPACT)) {
  return { wasCompacted: false }
}

首先检查,不占用熔断器计数。

熔断器(第 257-265 行)

typescript 复制代码
if (
  tracking?.consecutiveFailures !== undefined &&
  tracking.consecutiveFailures >= MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES
) {
  return { wasCompacted: false }
}

连续失败 ≥ 3 次 → 跳过本次尝试。熔断状态通过 AutoCompactTrackingState.consecutiveFailures 跨 turn 传递。

shouldAutoCompact(第 267-277 行)

typescript 复制代码
const model = toolUseContext.options.mainLoopModel
const shouldCompact = await shouldAutoCompact(messages, model, querySource, snipTokensFreed)
if (!shouldCompact) return { wasCompacted: false }

8.2 重压缩信息(第 279-285 行)

typescript 复制代码
const recompactionInfo: RecompactionInfo = {
  isRecompactionInChain: tracking?.compacted === true,  // 是否链式重压缩
  turnsSincePreviousCompact: tracking?.turnCounter ?? -1, // 距上次压缩的轮数
  previousCompactTurnId: tracking?.turnId,                // 上次压缩的 turn ID
  autoCompactThreshold: getAutoCompactThreshold(model),   // 触发阈值
  querySource,
}

这些信息传递给 compactConversation,帮助 LLM 理解它正在对已压缩过的上下文做二次压缩。

8.3 路径 A:Session Memory 压缩优先(第 287-310 行)

typescript 复制代码
const sessionMemoryResult = await trySessionMemoryCompaction(
  messages, toolUseContext.agentId, recompactionInfo.autoCompactThreshold,
)
if (sessionMemoryResult) {
  setLastSummarizedMessageId(undefined)
  runPostCompactCleanup(querySource)
  if (feature('PROMPT_CACHE_BREAK_DETECTION')) {
    notifyCompaction(querySource ?? 'compact', toolUseContext.agentId)
  }
  markPostCompaction()
  return { wasCompacted: true, compactionResult: sessionMemoryResult }
}

先尝试 Session Memory 压缩(一种更智能的压缩方式,不是直接给 LLM 全部总结,而是利用之前抽取的结构化记忆)。如果成功:

  1. 重置最后总结的消息 ID(因为消息列表已变)
  2. 执行压缩后清理(重置 collapse 状态、清除 microcompact 缓存等)
  3. 通知缓存断裂检测器(避免将 post-compact 的缓存 miss 误报为断裂)
  4. 标记已压缩

Session Memory Compaction(会话记忆压缩)机制分析

8.4 路径 B:传统压缩(第 312-333 行)

typescript 复制代码
const compactionResult = await compactConversation(
  messages, toolUseContext, cacheSafeParams,
  true,   // suppressUserQuestions
  undefined, // customInstructions
  true,   // isAutoCompact
  recompactionInfo,
)

setLastSummarizedMessageId(undefined)
runPostCompactCleanup(querySource)

return {
  wasCompacted: true,
  compactionResult,
  consecutiveFailures: 0,  // 重置失败计数
}

调用 compactConversation(来自 compact.ts)执行实际的 LLM 压缩。参数说明:

  • suppressUserQuestions = true:自动触发,不询问用户
  • isAutoCompact = true:标记为自动压缩(区别于手动 /compact

成功后 consecutiveFailures 重置为 0。

Claude Code 传统对话压缩

8.5 错误处理与熔断器(第 334-350 行)

typescript 复制代码
catch (error) {
  if (!hasExactErrorMessage(error, ERROR_MESSAGE_USER_ABORT)) {
    logError(error)
  }
  const prevFailures = tracking?.consecutiveFailures ?? 0
  const nextFailures = prevFailures + 1
  if (nextFailures >= MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES) {
    logForDebugging(
      `autocompact: circuit breaker tripped after ${nextFailures} consecutive failures`,
      { level: 'warn' },
    )
  }
  return { wasCompacted: false, consecutiveFailures: nextFailures }
}
  • 用户中止(ERROR_MESSAGE_USER_ABORT)不记入错误日志(不 logError),但仍然会计入熔断器计数
  • 每次失败递增计数,通过返回值传递给 caller,caller 存入 tracking 状态以保持跨 turn 持久性

9. Tracking State(第 51-60 行)

typescript 复制代码
export type AutoCompactTrackingState = {
  compacted: boolean        // 本轮是否已压缩
  turnCounter: number        // turn 计数器
  turnId: string             // 当前 turn 的 UUID
  consecutiveFailures?: number  // 连续失败次数(熔断器)
}

这个状态在 query.ts 的 query loop 中维护,每轮更新,传递给下一次 autoCompactIfNeeded 调用。


10. 完整执行流程图

kotlin 复制代码
autoCompactIfNeeded()
  │
  ├─ DISABLE_COMPACT? → return
  │
  ├─ 熔断器 ≥ 3 次连续失败? → return
  │
  ├─ shouldAutoCompact()? → false → return
  │    │
  │    ├─ querySource 递归守卫 → false
  │    ├─ isAutoCompactEnabled → false
  │    ├─ reactive-only 模式 → false
  │    ├─ context collapse 激活 → false
  │    └─ token 未超阈值 → false
  │
  ├─ trySessionMemoryCompaction()  ← 路径 A
  │    └─ 成功? → postCompactCleanup → return
  │
  └─ compactConversation()  ← 路径 B
       ├─ 成功 → postCompactCleanup → 重置熔断器 → return
       └─ 失败 → 递增熔断器 → return

11. 与 microcompact 和 context collapse 的关系

机制 对消息的影响 缓存影响 频率
microcompact 清空 tool result 内容 不影响(Cached MC)或缓存 miss(Time-based) 每次 API 调用前
context collapse 消息区间替换为 <collapsed> 摘要 不影响(只替换占位符) 每次 API 调用前
autocompact 所有消息替换为单个 summary message 缓存 miss(prompt 完全改变) 极少触发(前两者都无效后)

autocompact 是最重量级的压缩方式------一旦触发,它使用 LLM 总结整个对话历史,替换为单条 summary message,后续对话将在压缩后的上下文中继续。


相关推荐
鹿青5 小时前
给设计稿做体检:我搓了个 Skill,专治 Figma 转代码出垃圾
前端·claude·视觉设计
小二·10 小时前
Claude API 完整实战
ai·api·claude
清欢渡hb11 小时前
一人 AI 软件公司 · Claude Code 插件架构设计
人工智能·ai编程·claude·一人公司
解决问题12 小时前
Microcompact(微压缩)机制分析
claude
沉默王二12 小时前
刚上线就斩获 2.3K 星标!AnySearch 搜索能力拉满!
agent·ai编程·claude
Better Bench12 小时前
Ubuntu 22.04系统中解决运行CC-Switch-v3.16.1-Linux-x86_64.AppImage中文乱码
linux·ubuntu·claude·claude code·cc-switch
黏刚13 小时前
2025 最新 Claude Code 教程:从安装部署到 SpringBoot 项目实战(附完整 Java 示例)
java·ai编程·claude
jiayong231 天前
Claude Code 快速参考卡片
大数据·elasticsearch·搜索引擎·ai·claude·claude code
ZzT1 天前
Harness 怎么拿捏 agent:权限与 effort
openai·ai编程·claude