Context Window Auto-Compaction

Clawdbot Contributors· validated-in-production

问题

上下文(Context)溢出是侵蚀Agent可靠性的隐形杀手。当累积的对话历史超出模型的上下文窗口(context window)时,会引发以下问题:

  • API错误:请求因context_length_exceeded或类似错误而失败
  • 人工干预需求:运维人员必须截断对话记录,导致宝贵的Context丢失
  • 重试复杂性:检测溢出并通过压缩方式重试的过程极易出错

Agent需具备自动压缩功能,在令牌(token)限额内保留核心信息,同时结合模型专属校验机制与预留令牌下限,以防止溢出问题立即再次发生。

方案

由上下文溢出错误触发的自动会话压缩机制,具备智能预留令牌和队列感知重试能力。系统会自动检测溢出、压缩会话记录、验证结果并重试请求——所有操作对用户完全透明。

核心概念:

  • 溢出检测:捕获表明上下文长度超出的API错误(如context_length_exceededprompt过长等)。
  • 带压缩的自动重试:发生溢出时,系统会自动压缩会话并重试请求。
  • 预留令牌下限:压缩后确保剩余令牌不低于最小数量(默认20k),避免立即再次出现溢出。
  • 队列感知压缩:采用分层队列(会话→全局)机制,防止压缩过程中出现死锁。
  • 压缩后验证:估算压缩后的令牌数量,确认其小于压缩前的令牌数。
  • 模型专属验证:Anthropic模型要求严格的轮次顺序;Gemini模型则有不同的会话记录格式要求。

实现草图:

async function compactEmbeddedPiSession(params: {
  sessionFile: string;
  config?: Config;
}): Promise<CompactResult> {
  // 1. 加载会话并配置预留令牌
  const sessionManager = SessionManager.open(params.sessionFile);
  const settingsManager = SettingsManager.create(workspaceDir, agentDir);

  // 确保满足最低预留令牌要求(默认20k)
  ensurePiCompactionReserveTokens({
    settingsManager,
    minReserveTokens: resolveCompactionReserveTokensFloor(params.config),
  });

  // 2. 针对模型API清理会话历史
  const prior = sanitizeSessionHistory({
    messages: session.messages,
    modelApi: model.api,
    modelId,
    provider,
    sessionManager,
  });

  // 3. 执行模型专属验证
  const validated = provider === "anthropic"
    ? validateAnthropicTurns(prior)
    : validateGeminiTurns(prior);

  // 4. 压缩会话
  const result = await session.compact(customInstructions);

  // 5. 估算压缩后的令牌数量
  let tokensAfter: number | undefined;
  try {
    tokensAfter = 0;
    for (const message of session.messages) {
      tokensAfter += estimateTokens(message);
    }
    // 合理性检查:压缩后令牌数必须小于压缩前
    if (tokensAfter > result.tokensBefore) {
      tokensAfter = undefined;  // 不采信该估算结果
    }
  } catch {
    tokensAfter = undefined;
  }

  return {
    ok: true,
    compacted: true,
    result: {
      summary: result.summary,
      tokensBefore: result.tokensBefore,
      tokensAfter,
    },
  };
}

预留令牌强制机制:

const DEFAULT_PI_COMPACTION_RESERVE_TOKENS_FLOOR = 20_000;

function ensurePiCompactionReserveTokens(params: {
  settingsManager: SettingsManager;
  minReserveTokens?: number;
}): { didOverride: boolean; reserveTokens: number } {
  const minReserveTokens = params.minReserveTokens ?? DEFAULT_PI_COMPACTION_RESERVE_TOKENS_FLOOR;
  const current = params.settingsManager.getCompactionReserveTokens();

  if (current >= minReserveTokens) {
    return { didOverride: false, reserveTokens: current };
  }

  // 覆盖配置以确保满足最低令牌下限
  params.settingsManager.applyOverrides({
    compaction: { reserveTokens: minReserveTokens },
  });

  return { didOverride: true, reserveTokens: minReserveTokens };
}

基于API的压缩(OpenAI Responses API): 部分服务商提供了专用的压缩端点,效率优于手动摘要:

// OpenAI的/responses/compact端点
const compacted = await responsesAPI.compact({
  messages: currentMessages,
});

// 返回结果包含以下类型的条目:
// - 特殊type=compaction的条目,带有encrypted_content字段
//   用于保留模型对原始对话的潜在理解
// - 精简后的对话条目

currentMessages = compacted.items;

该方案具备以下优势:

  • 保留潜在理解encrypted_content字段会保存模型对原始对话的压缩表示
  • 效率更高:服务端压缩速度快于客户端本地摘要
  • 自动触发压缩:当超出auto_compact_limit阈值时可自动触发压缩

队列感知重试(防止死锁):

// 压缩流程先经过会话队列,再进入全局队列
async function compactEmbeddedPiSession(params: CompactParams): Promise<CompactResult> {
  const sessionLane = resolveSessionLane(params.sessionKey);
  const globalLane = resolveGlobalLane(params.lane);

  return enqueueCommandInLane(sessionLane, () =>
    enqueueCommandInLane(globalLane, () =>
      compactEmbeddedPiSessionDirect(params)  // 核心压缩逻辑
    )
  );
}

如何使用

操作步骤

  1. 配置预留下限:设置compaction.reserveTokensFloor参数,确保上下文压缩后留有足够缓冲空间(默认值为20000)。
  2. 处理溢出错误:捕获API错误,通过匹配错误信息检测令牌溢出,随后触发上下文压缩。
  3. 验证会话记录:在重试前执行模型专属校验(如Anthropic的轮次校验、Gemini的顺序校验)。
  4. 估算压缩后令牌数:在重试前确认上下文压缩确实降低了令牌总量。
  5. 使用通道排队机制:通过分层通道执行上下文压缩,避免与并发操作产生死锁。

需规避的误区

  • 预留下限设置过于激进:预留令牌阈值过高可能导致实际会话内容的可用空间不足。
  • 遗漏模型专属校验:跳过会话记录的模型专属校验,会导致重试时出现API错误。
  • 令牌估算偏差:估算启发式算法可能与实际令牌数存在偏差,仅需将估算结果作为合理性校验依据。
  • 无限压缩循环:若上下文压缩无法减少令牌数量,需避免无限重试循环,最多执行1-2次尝试。

(注:compaction为LLM领域通用术语,此处译为“上下文压缩”,也可保留原英文表述)

权衡

优点:

  • 透明恢复:溢出错误会自动处理,无需用户干预。
  • 保留关键上下文:压缩操作生成摘要,而非进行任意截断。
  • 防止二次溢出:预留令牌下限确保短期内不太可能立即再次发生溢出。
  • 模型感知适配:针对不同供应商采用专属验证规则,保障API兼容性。

缺点/注意事项:

  • 摘要质量:自动生成的摘要可能会丢失人工整理所能保留的细微细节。
  • 延迟损耗:压缩与重试操作会带来额外开销,耗时从几秒到几分钟不等,具体取决于上下文规模。
  • 令牌估算误差:启发式算法可能会误判实际令牌数量,进而导致重试失败。
  • 实现复杂度:通道感知队列与模型专属验证逻辑增加了实现难度。

参考文献

关键词

涵盖Clawdbot项目中负责压缩编排、预留令牌配置、上下文评估的功能实现文件,Pi编码代理的核心压缩逻辑,OpenAI Codex代理循环的API端点方案,以及上下文窗口主动管理、提示词缓存的相关技术模式。

直译

来源摘要

正在获取来源并生成中文摘要…

来源: https://github.com/clawdbot/clawdbot/blob/main/src/agents/pi-embedded-runner/compact.ts

← 返回社区