⏳ This skill is pending AI review.
Scores will appear once the review pipeline completes.
Context Compressor
自动上下文压缩。当session超阈值时,先提取重要内容到日记,再压缩JSONL。支持light/standard/aggressive模式。触发条件:session>8K tokens、用户要求压缩、心跳维护时发现session过大。v5.0新增:防死循环、大输出截断、性能优化。
// RATINGS
// README
name: Context Compressor slug: context-compressor version: 5.0.0 description: 自动上下文压缩。当session超阈值时,先提取重要内容到日记,再压缩JSONL。支持light/standard/aggressive模式。触发条件:session>8K tokens、用户要求压缩、心跳维护时发现session过大。v5.0新增:防死循环、大输出截断、性能优化。 metadata: {"openclaw":{"emoji":"📦","requires":{"bins":[]},"os":["linux","darwin","win32"]}}
Context Compressor 📦
两阶段压缩:先提取,再压缩。不提取就压缩 = 丢信息。
何时压缩
| 条件 | 动作 |
|---|---|
| Session > 8K tokens(~32K字符) | 主动压缩 |
| 用户说"压缩上下文"/"compress context" | 立即压缩 |
| 心跳检查发现session膨胀 | 主动压缩 |
| 长任务开始前,session已很大 | 先压缩再继续 |
不要压缩:任务进行中、session即将结束、刚压缩过且仍低于2倍阈值。
Token估算
| 内容 | 公式 | 说明 |
|---|---|---|
| 主要中文 | 字符数 / 1.5 | 中文token密度高 |
| 主要英文 | 字符数 / 4 | 英文token密度低 |
| 混合 | 字符数 / 3 | 通用估算(脚本默认) |
| JSON/代码多 | 字符数 / 3.5 | 结构化内容更紧凑 |
注意:以上为粗略估算,实际token数因模型而异。建议用模型官方tokenizer精确计算。
快速判断:session文件 > 32K字符 ≈ > 8K tokens(混合内容),该压缩了。
Phase 1: 提取(必须先做)
扫描session中间部分,把重要内容存到外部文件。
| 要提取的 | 存到哪里 | 格式 |
|---|---|---|
| 用户偏好/事实 | SOUL.md → 偏好部分 | 更新或新增bullet |
| 关键决策/结论 | memory/YYYY-MM-DD.md | ## Decision: <topic> |
| 创作作品全文 | memory/YYYY-MM-DD.md | ## Creative: <title> |
| 重要对话摘录 | memory/YYYY-MM-DD.md | ## Conversation: <topic> |
| 技术解决方案 | memory/YYYY-MM-DD.md | ## Technical: <issue> |
| 新话题/类别 | SOUL.md → 事件索引 | 新增条目 |
| 闲聊/日常 | 不存 | — |
日记追加格式:
---
## 压缩提取 HH:mm
### <Category>: <Brief Title>
- **Context**: 为什么重要
- **Content**: 实际信息
- **Session ref**: 大致轮次范围
日记追加,绝不覆盖。
Phase 2: 压缩Session
优先级系统(从低到高删)
| 优先级 | 内容 | 删减顺序 |
|---|---|---|
| P0 | 无法解析的行(安全保留) | 保留 |
| P1 | 大型tool输出(>2000字符) | 1st |
| P2 | 导航/探索调用(ls, pwd, grep未命中) | 2nd |
| P3 | 成功但常规的tool调用 | 3rd |
| P4 | 冗长的assistant解释 | 4th |
| P5 | 日常寒暄("好的""谢谢") | 5th |
| P6 | 有意图的用户消息 | 保留 |
| P7 | 最近对话(tail) | 保留 |
| P8 | System消息 | 保留 |
| P9 | 已有Summary(合并处理) | 合并 |
| P10 | Session header | 保留 |
压缩模式
| 模式 | Token目标 | 操作 | 适用场景 |
|---|---|---|---|
| light | 6K | 只删P1-P2 | 刚过阈值,主要是tool膨胀 |
| standard | 4K | 删P1-P3,摘要P4-P5 | 常规维护(默认) |
| aggressive | 2.5K | 删P1-P5,最大化摘要 | session很长,需要最大压缩 |
大输出截断策略
当tool输出 >2000字符时,不要只做全删/全留二选一。优先截断:
| 策略 | 操作 | 节省 |
|---|---|---|
| 截断到500字符 | 保留前500字符 + ...[truncated, N chars omitted] | ~75% |
| 截断到200字符 | 保留前200字符 + 截断标记 | ~90% |
| 全删 | 完全移除该行 | 100% |
截断优先级:先截断到500 → 仍超标则截断到200 → 仍超标则全删。
截断时保留tool调用的结构信息(tool name、参数摘要),只删输出内容。
操作步骤
1. 读取 session JSONL
路径: ~/.openclaw-autoclaw/agents/<agentId>/sessions/<sessionId>.jsonl
2. 估算token数,低于阈值则退出
3. 检查防死循环标记(见下方),如触发则跳过
4. 备份原文件 → <sessionId>.jsonl.bak
5. 分类每行(P0-P10),缓存解析结果,避免重复JSON解析
6. 识别tail(从末尾往前数60行P6+内容)
7. 截断大输出(P1行先截断到500字符,再截断到200,最后全删)
8. 按优先级从低到高删除(不删tail)
直到token数达标或只剩P6+
每删一行计数+1,超过maxIter则停止
9. 生成 [SESSION SUMMARY](合并已有摘要,不要堆叠)
≤6条bullet,每条≤2行,带文件引用
10. 组装:header + summary(1行) + tail
11. 验证:每行有效JSON、至少1条user+1条assistant、token数达标
12. 写入,验证通过后删.bak,失败则回滚
13. 写入防死循环标记
14. 日志:追加到 memory/YYYY-MM-DD.md
Summary格式
[SESSION SUMMARY]
• [Decision] <决定内容> → memory/<YYYY-MM-DD>.md##Section
• [Technical] <解决方案> → memory/<YYYY-MM-DD>.md##Section
• [Fact] <关键事实>
• [State] <当前任务状态>
• [Warning] <未来必须知道的事>
规则:≤6条、每条≤2行、带文件引用、用标签分类、只写结果不写过程。日期使用当天日期。
处理已有Summary
如果session中已有 [SESSION SUMMARY]:
- 解析旧summary
- 与新内容合并
- 去重:
[State]替换为新[State],已解决的[Warning]删除,同类多bullet保留(如多个[Technical]) - 结果始终只有一条
[SESSION SUMMARY]
绝对不要堆叠多条summary。
防死循环机制 ⚠️
本地模型执行压缩时可能陷入死循环。以下机制防止此问题:
1. 最大迭代限制
| 参数 | 默认值 | 说明 |
|---|---|---|
| maxIter | 100 | 模型内操作时,删除操作的最大迭代次数 |
| maxIter (脚本) | 5000 | 脚本执行时的最大迭代次数 |
超过限制后立即停止,输出当前结果。
2. 压缩冷却期
压缩完成后,在session中写入标记:
{"type":"system","lastCompressed":"<timestamp>","compressedTokens":<N>}
规则:
- 距上次压缩 < 5分钟 → 不压缩
- 上次压缩后token数未增长 > 20% → 不压缩
- 连续3次压缩后仍未达标 → 停止压缩,报告错误
3. 压缩效果检查
压缩后验证:
- 压缩后token数必须 < 压缩前token数 × 0.9(至少减少10%)
- 如果压缩后几乎没减少 → 说明session主要由P6+内容组成,无法进一步压缩
- 此时不要重试,而是报告:"Session主要由高优先级内容组成,无法进一步压缩。建议增大阈值或手动清理。"
4. 模型执行安全
当由本地模型执行压缩时:
- 单次执行:每次只执行一次完整的压缩流程
- 不自动重试:压缩完成后不自动检查是否需要再次压缩
- 明确退出:压缩完成后输出
"Compression complete. <N> tokens remaining."然后停止 - 异常中断:如果执行过程中遇到错误,立即停止并报告,不要尝试恢复
安全与恢复
| 步骤 | 动作 |
|---|---|
| 压缩前 | 复制 .jsonl → .jsonl.bak |
| 验证通过 | 删除 .jsonl.bak |
| 验证失败 | 回滚 .jsonl.bak → .jsonl |
| 手动恢复 | 用户可从 .jsonl.bak 恢复 |
验证清单:每行有效JSON ✅ 至少1条user消息 ✅ 至少1条assistant消息 ✅ token数达标 ✅
脚本(可选)
如需脚本化执行,见 scripts/ 目录:
| 脚本 | 平台 | 用法 |
|---|---|---|
compress-session.ps1 | Windows | .\compress-session.ps1 -SessionFile <path> [-Mode standard] [-KeepTail 60] [-MaxIter 100] [-DryRun] |
compress-session.sh | Linux/macOS | ./compress-session.sh <session-file> [mode] [keep-tail] [dry-run] |
compress-simple.py | 跨平台 | python compress-simple.py <session.jsonl> [mode] [tail_lines] |
compress-all.py | 跨平台(批量) | python compress-all.py |
diary-logger.py | 跨平台 | 见下方日记记录章节 |
模型内操作优先:直接用read/write/exec工具按上述步骤执行,比脚本更灵活。
日记记录
diary-logger.py 用法
# Phase 1: 压缩前提取重要内容到日记
python diary-logger.py extract <session.jsonl> <workspace_dir>
# 压缩后记录压缩事件
python diary-logger.py log-compression <workspace_dir> --mode standard --before 8000 --after 4000 --removed 45
# 验证日记完整性
python diary-logger.py validate <workspace_dir>
# 手动更新 MEMORY.md 索引
python diary-logger.py update-index <workspace_dir> --note "手动添加的备注"
日记记录触发机制
| 触发时机 | 动作 | 命令 |
|---|---|---|
| 压缩前(Phase 1) | 提取重要内容到日记 | diary-logger.py extract |
| 压缩后 | 记录压缩事件 | diary-logger.py log-compression |
| 心跳检查时 | 验证日记完整性 | diary-logger.py validate |
| 手动 | 更新MEMORY.md索引 | diary-logger.py update-index |
日记数据格式
压缩提取追加到 memory/YYYY-MM-DD.md:
---
## 压缩提取 HH:mm
### Decision: <简要标题>
- **Context**: 为什么重要
- **Content**: 实际信息
- **Source**: user/assistant/summary
压缩事件追加到 memory/YYYY-MM-DD.md:
---
## 压缩执行 HH:mm
### Compression: standard mode
- **Before**: 8,000 tokens
- **After**: 4,000 tokens
- **Saved**: 50.0%
- **Removed lines**: 45
- **Timestamp**: 2026-04-21T12:00:00+08:00
错误处理策略
| 错误场景 | 处理方式 |
|---|---|
| 日记文件不存在 | 自动创建,含正确标题 |
| 写入失败 | 从 .bak 回滚,报告错误 |
| MEMORY.md 格式异常 | 前缀匹配日期,兼容 ### 2026-04-21 — 标题 格式 |
| 提取无重要内容 | 输出提示,不创建空条目 |
| .bak 文件残留 | validate 命令检测并报告 |
完整性验证
diary-logger.py validate 检查以下项目:
- ✅ 今日日记文件存在
- ✅ 日记标题格式正确(
# YYYY-MM-DD) - ✅ 日记包含章节内容
- ✅ MEMORY.md 包含今日条目
- ✅ SOUL.md 存在
- ✅ memory/ 目录存在且有文件
- ✅ 无残留 .bak 文件
- ⚠️ 检测空章节
- ⚠️ 检测压缩提取次数过多(>3次)
配置
| 参数 | 默认值 | 说明 |
|---|---|---|
| tokenThreshold | 8000 | 超过此token数触发压缩 |
| charThreshold | 32000 | 备用:超过此字符数触发 |
| keepTail | 60 | 保留最近对话的最小行数 |
| mode | standard | 压缩模式:light/standard/aggressive |
| autoBackup | true | 压缩前自动备份 |
| maxIter | 100 | 模型内操作最大迭代次数 |
| truncateThreshold | 2000 | 超过此字符数的tool输出触发截断 |
| truncateSize | 500 | 截断后保留的字符数 |
| cooldownMinutes | 5 | 两次压缩之间的最小间隔(分钟) |
最佳实践
- 先提取再压缩 — 不提取就压缩 = 永久丢失信息
- 按token不是按行 — 100行短对话 ≠ 100行tool输出
- 先截断再删除 — 截断大输出比全删更安全,保留上下文线索
- 先删tool调用 — 最低价值、最高token,通常单这一步就能省20-40%
- 只有一条summary — 合并不堆叠
- summary里引用外部文件 — 让未来的自己能找到详情
- 不中途压缩 — 等任务边界再压缩
- 遵守冷却期 — 不要连续压缩,至少间隔5分钟
- 日志记录 — 压缩后追加到日记:
"Compressed X→Y tokens (mode)" - 压缩失败就停止 — 不要重试,报告错误让用户决定
// HOW IT'S BUILT
KEY FILES