5.4 会话裁剪(Session Pruning)

生成模型:Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗:输入 ~250,000 tokens,输出 ~18,000 tokens(本章合计)


随着对话的进行,会话中累积的消息会越来越多。对于大语言模型(LLM)来说,每次调用都需要将完整的对话历史作为输入,而模型的上下文窗口有固定的 Token 上限。OpenClaw 通过上下文裁剪(Context Pruning)和上下文窗口守卫(Context Window Guard)两套机制来管理这个问题。

衍生解释:上下文窗口(Context Window)是 LLM 的一个核心约束。每个模型都有一个 Token 上限(例如 Claude 的 200K tokens、GPT-4o 的 128K tokens),所有的系统提示词、对话历史和工具结果都必须在这个窗口内。Token 可以粗略理解为"子词"(sub-word),英文中大约每 4 个字符为 1 个 token,中文中每个汉字约 1-2 个 token。当对话历史超出窗口大小时,LLM 会拒绝处理,因此必须在调用前裁剪多余的内容。

5.4.1 工具结果裁剪:在 LLM 调用前修剪旧工具结果

在 Agent 的工作过程中,工具调用的结果(如 bash 命令的输出、文件内容、搜索结果等)往往占据上下文的大量空间。OpenClaw 在 src/agents/pi-extensions/context-pruning/ 目录下实现了一套两阶段裁剪策略:软裁剪(Soft Trim)和硬清除(Hard Clear)。

裁剪配置

src/agents/pi-extensions/context-pruning/settings.ts 定义了裁剪的配置结构和默认值:

// src/agents/pi-extensions/context-pruning/settings.ts
export const DEFAULT_CONTEXT_PRUNING_SETTINGS: EffectiveContextPruningSettings = {
  mode: "cache-ttl",
  ttlMs: 5 * 60 * 1000,          // 5 分钟缓存 TTL
  keepLastAssistants: 3,          // 保护最近 3 轮 Assistant 回复
  softTrimRatio: 0.3,             // 上下文使用率 > 30% 时触发软裁剪
  hardClearRatio: 0.5,            // 上下文使用率 > 50% 时触发硬清除
  minPrunableToolChars: 50_000,   // 可裁剪工具结果至少 50K 字符才执行硬清除
  tools: {},                       // 工具白名单/黑名单
  softTrim: {
    maxChars: 4_000,              // 超过 4K 字符的工具结果才触发软裁剪
    headChars: 1_500,             // 保留头部 1500 字符
    tailChars: 1_500,             // 保留尾部 1500 字符
  },
  hardClear: {
    enabled: true,
    placeholder: "[Old tool result content cleared]",
  },
};

mode 目前只支持 "cache-ttl" 模式:裁剪结果会被缓存,在 TTL(默认 5 分钟)过期前不会重复计算。这避免了每次 LLM 调用都重新执行裁剪逻辑。

裁剪触发机制

裁剪以 Pi Agent 的扩展(Extension)形式注入到 Agent 运行时中。每次 LLM 调用前,扩展会检查是否需要裁剪:

衍生解释:这里使用了事件驱动的扩展模式。Pi Agent Core 在每次准备 LLM 调用时会触发 context 事件,扩展可以拦截这个事件并修改即将发送给 LLM 的消息列表。这种设计使得裁剪逻辑与 Agent 核心完全解耦——可以独立配置、启用或禁用。

两阶段裁剪算法

pruneContextMessages() 是裁剪的核心算法,位于 src/agents/pi-extensions/context-pruning/pruner.ts

算法的关键在于两个保护区域:

  1. 尾部保护:最近的 keepLastAssistants(默认 3)轮 Assistant 回复及其关联的工具结果不可裁剪,因为它们是当前对话的直接上下文

  2. 头部保护:第一条 user 消息之前的所有内容不可裁剪,因为这些通常是引导文件(如 SOUL.mdAGENTS.md)的读取结果,对 Agent 的行为至关重要

在两个保护区域之间的工具结果消息,按照以下流程处理:

阶段
触发条件
操作
效果

阶段 1:软裁剪

ratio > softTrimRatio(默认 0.3)

保留头部 1500 + 尾部 1500 字符,中间用 ... 替代

保留工具结果的关键信息

阶段 2:硬清除

ratio > hardClearRatio(默认 0.5)

整个工具结果替换为占位符文本

彻底释放空间

软裁剪的实现:

硬清除更为激进——直接用占位符替换整个工具结果:

工具结果完整性守卫

除了裁剪,OpenClaw 还需要保证会话转录中工具调用和结果的配对完整性。某些 LLM 提供者(如 Anthropic Claude)要求每个工具调用(toolCall)都必须有对应的工具结果(toolResult),否则会拒绝请求。

src/agents/session-tool-result-guard.ts 通过猴子补丁(Monkey-Patch)SessionManager.appendMessage 方法来追踪未完成的工具调用:

衍生解释:猴子补丁(Monkey-Patch)是一种在运行时替换对象方法的技术。这里通过替换 appendMessage 方法,在不修改 Pi Agent Core 源码的情况下加入了工具结果追踪逻辑。虽然这种技术在生产代码中应谨慎使用,但在需要拦截第三方库行为时是一种实用的方案。

5.4.2 上下文窗口守卫(Context Window Guard)

裁剪机制处理的是"如何缩减上下文",而上下文窗口守卫处理的是"上下文窗口本身是否合理"。src/agents/context-window-guard.ts 提供了上下文窗口大小的解析和校验。

上下文窗口大小解析

不同的模型有不同的上下文窗口大小,而用户还可能通过配置文件覆盖这个值。resolveContextWindowInfo() 按优先级确定最终的窗口大小:

安全阈值检查

确定窗口大小后,evaluateContextWindowGuard() 检查是否低于安全阈值:

shouldBlocktrue 时,Gateway 会拒绝启动 Agent,因为在 16K tokens 以下,系统提示词、工具定义和最基本的对话历史都可能放不下,继续运行只会产生低质量的回复。


本节小结

OpenClaw 的会话裁剪体系由三个互补的机制组成:

  1. 两阶段上下文裁剪:软裁剪(保留头尾、截断中间)和硬清除(替换为占位符)的渐进策略,在控制上下文大小的同时尽量保留有价值的信息

  2. 智能保护区域:最近 N 轮回复和引导文件永远不被裁剪,确保 Agent 的当前对话上下文和基础人格不受影响

  3. 工具结果完整性守卫:通过追踪工具调用和结果的配对关系,自动为缺失的结果生成合成回复,满足 LLM 提供者的严格要求

  4. 上下文窗口守卫:在 Agent 启动前校验模型的上下文窗口大小,低于 16K tokens 时拒绝运行,低于 32K tokens 时发出警告

这套机制使得 OpenClaw 能够在长时间的对话中保持稳定运行,不会因为上下文溢出而崩溃或产生低质量回复。

Last updated