当对话历史实在太长、会话修剪(Pruning)也不够用时,Compaction 是 OpenClaw 的核心应对手段。它将较早的对话轮次汇总为一个精简摘要条目,保存到会话转录中,近期消息保持完整。完整历史始终保留在磁盘上,压缩只改变模型在下一轮看到的内容。OpenClaw 的 Compaction 默认启用自动触发,采用 safeguard 质量守护模式,支持可插拔压缩提供商,并在压缩前自动刷新记忆以防止信息丢失。
一、为什么需要 Compaction
会话修剪只能裁剪旧工具结果,不改写对话文本。当对话本身很长——几百轮交互、大量来回讨论——修剪工具输出释放的空间杯水车薪,模型上下文窗口仍然不够用。
| 场景 | 修剪是否够用 | 是否需要 Compaction |
|---|---|---|
| 工具结果累积过大 | 够用 | 不需要 |
| 对话轮次过多 | 不够 | 需要 |
| 接近上下文窗口上限 | 视情况 | 通常需要 |
| 模型返回溢出错误 | 不够 | 需要(自动触发) |
| 想保留关键讨论、丢弃冗余 | 不适用 | 需要 |
Compaction 解决的是”对话本身太长”的问题,而非”单条工具结果太大”的问题。两者的分工明确:修剪裁工具输出,压缩摘要对话文本。
二、Compaction 的工作原理
2.1 核心流程
Compaction 的执行分为四步:
- 选择压缩拆分点(split point),确定哪些轮次被摘要、哪些保留;
- 将较早的对话轮次交给模型生成摘要,保存为
compaction类型的转录条目; - 近期消息保持完整,后续轮次看到的是”摘要 + 近期消息”;
- 完整对话历史仍保留在磁盘上的 SQLite 会话存储中。
# Compaction 核心伪代码
def compact(session, keep_recent_tokens=20000):
# 1. 估算各轮次 Token,找到拆分点
split_point = find_split_point(session, keep_recent_tokens)
# 2. 确保工具调用与 toolResult 配对不被打断
split_point = adjust_for_tool_pairs(session, split_point)
# 3. 将拆分点之前的轮次交给模型生成摘要
older_turns = session[:split_point]
summary = model.summarize(older_turns,
preserve_identifiers=True)
# 4. 写入 compaction 条目,保留近期消息
session.insert_compaction_entry(
summary=summary,
firstKeptEntryId=split_point,
tokensBefore=session.total_tokens
)
# 完整历史仍在磁盘上,不删除
2.2 工具调用配对保留
这是 Compaction 最关键的细节。OpenClaw 选择拆分点时,会让助手的工具调用与其对应的 toolResult 条目保持配对。如果拆分点恰好落在工具块内部——比如助手发了工具调用但结果还没返回的位置——OpenClaw 会自动移动边界,让配对保持在一起。
这一设计避免了”模型看到了工具调用但看不到结果”的断裂状态,否则模型可能在下一轮重复发起相同的工具调用,浪费 Token 和时间。
2.3 摘要的存储与可见性
| 层面 | 说明 |
|---|---|
| 摘要存储位置 | 会话转录(SQLite),类型为 compaction |
| 摘要包含字段 | firstKeptEntryId、tokensBefore |
| 磁盘完整历史 | 始终保留,不删除 |
| 模型下一轮看到的内容 | 摘要 + firstKeptEntryId 之后的消息 |
| 历史查看器 | 仍可渲染原始消息条目 |
OpenClaw 不再为新压缩写入单独的 .checkpoint.*.jsonl 副本,现有旧版检查点文件在被引用时仍可使用,并由正常会话清理机制修剪。
三、自动压缩与手动压缩
3.1 自动压缩(Auto-compaction)
自动压缩默认启用,在两种情况下触发:
- 阈值触发:会话接近上下文窗口上限时(如总窗口 20 万、预留 2 万缓冲,Token 用量超过 18 万时);
- 溢出恢复:模型返回上下文溢出错误时,OpenClaw 先压缩历史再重试该轮请求。
{
"agents": {
"defaults": {
"compaction": {
"enabled": true,
"mode": "safeguard"
}
}
}
}
设置 enabled: false 可禁用嵌入式运行时的主动阈值压缩,但 OpenClaw 的预检和溢出恢复压缩路径仍然可用,手动 /compact 也不受影响。
3.2 手动压缩
在任何聊天中输入 /compact 可强制执行压缩,还能附加指令引导摘要重点:
/compact 请特别保留关于项目架构的讨论内容
手动压缩使用 keepRecentTokens(默认 20,000)作为截断点预算,在重建的上下文中保留该近尾部内容。如果未明确设置保留预算,手动压缩的行为类似硬性检查点,只从新摘要继续。
四、safeguard 质量守护模式
新配置默认将 agents.defaults.compaction.mode 设为 "safeguard",提供更严格的守护措施和摘要质量审计。
4.1 质量校验流程
启用 safeguard 后,OpenClaw 在验证前应用最终摘要预算,执行以下检查:
- 必需标题检查:必须保留的标题必须出现在保留的生成正文中;
- 待处理请求检查:待处理的用户请求必须出现在将存储的确切文本中;
- 标识符保留检查:默认
identifierPolicy: "strict",不透明标识符(如文件路径、ID、配置值)必须原样保留。
4.2 校验失败的处理
| 校验结果 | 处理方式 |
|---|---|
| 通过 | 写入 compaction 条目,压缩完成 |
| 不通过 | 给予配置次数的纠正重试 |
| 所有重试均不通过 | 中止压缩,保留原始历史,暴露现有恢复结果 |
这一机制防止低质量摘要污染会话上下文。如果压缩无法通过质量门禁,宁可不做也不做错——保留原始历史比写入一个丢信息的摘要更安全。
五、配置项与高级用法
5.1 核心配置项
| 配置项 | 默认值 | 说明 |
|---|---|---|
enabled |
true |
是否启用自动压缩 |
mode |
"safeguard" |
压缩模式(safeguard / default) |
keepRecentTokens |
20000 |
手动压缩的近尾部保留预算 |
model |
继承会话模型 | 执行摘要的模型 |
identifierPolicy |
"strict" |
标识符保留策略 |
maxActiveTranscriptBytes |
未设置 | 活跃转录字节守护阈值 |
notifyUser |
false |
是否向用户显示压缩状态消息 |
5.2 指定压缩模型
默认情况下压缩使用 Agent 的主模型。可以指定一个更擅长摘要或更便宜的模型来做压缩:
{
"agents": {
"defaults": {
"compaction": {
"model": "ollama/llama3.1:8b"
}
}
}
}
如果摘要因模型级回退 eligible 的提供商错误而失败,OpenClaw 会通过会话现有的模型回退链重试该次压缩。但显式指定的 compaction.model 覆盖是精确的,不继承会话回退链。
5.3 记忆刷新(Memory Flush)
压缩前,OpenClaw 可以运行一轮静默的记忆刷新,将持久笔记保存到磁盘:
{
"agents": {
"defaults": {
"compaction": {
"memoryFlush": {
"model": "ollama/qwen3:8b"
}
}
}
}
}
这防止了”压缩丢信息”的担忧:重要事实在压缩前已落盘到记忆文件,即使摘要遗漏了某些细节,Agent 仍可通过记忆检索找回。
5.4 活跃转录字节守护
maxActiveTranscriptBytes 针对长期运行的会话,在转录历史达到指定大小时触发压缩:
{
agents: {
defaults: {
compaction: {
maxActiveTranscriptBytes: "20mb"
}
}
}
}
这在提供商侧上下文管理可能保持模型上下文健康、但持久化转录历史持续膨胀的场景下尤其有用。它不拆分原始字节,而是请求正常压缩管线创建语义摘要。
六、可插拔压缩提供商
插件可通过 registerCompactionProvider 注册自定义压缩提供商。当提供商注册并配置后,OpenClaw 将摘要工作委托给它,而非使用内置 LLM 管线。
{
"agents": {
"defaults": {
"compaction": {
"provider": "my-provider"
}
}
}
}
设置 provider 会自动强制 mode: "safeguard"。提供商接收与内置路径相同的压缩指令和标识符保留策略,OpenClaw 仍在提供商输出后保留近期轮次和拆分轮次的后缀上下文。如果提供商失败或返回空结果,OpenClaw 回退到内置 LLM 摘要。
七、Compaction 与 Pruning 的对比
| 维度 | Compaction(压缩) | Pruning(修剪) |
|---|---|---|
| 作用对象 | 整段对话历史 | 旧工具结果 |
| 信息处理方式 | 摘要化 | 截断或清除 |
| 是否持久化 | 是(保存到会话转录) | 否(仅内存,按请求执行) |
| 触发时机 | 接近窗口上限或溢出错误 | 缓存 TTL 过期 + 上下文占比 |
| 信息损失程度 | 中(摘要压缩) | 低(只清工具输出) |
| 配置位置 | agents.defaults.compaction |
agents.defaults.contextPruning |
两者相辅相成:修剪在各次压缩周期之间保持工具输出精简,减少压缩频率;压缩在修剪不够用时做更深度的瘦身。如果两者都不够,就该用 /new 开新会话了。
常见问题(FAQ)
Q1:Compaction 会删除原始对话历史吗?
不会。完整历史保留在磁盘上,压缩只改变模型下一轮看到的内容。
Q2:压缩后 Agent 会忘记之前的讨论吗?
摘要保留关键信息,压缩前还会提醒 Agent 保存重要笔记到记忆文件。
Q3:能指定不同的模型做压缩吗?
能。设置 compaction.model 即可委托更擅长摘要的模型执行压缩。