Lossless Claw — OpenClaw 无损上下文管理插件(深度分析)
> 一句话版本:替代 OpenClaw 内置的滑动窗口 compaction。用 SQLite + DAG 摘要树保存所有消息,摘要可追溯原文,Agent 永远不丢记忆。4,383 stars,MIT。
| 项目 | 信息 | ||
|---|---|---|---|
| 来源 | [github.com/Martian-Engineering/lossless-claw](https://github.com/Martian-Engineering/lossless-claw) | ||
| Stars | 4,383 | Forks | 363 |
| 语言 | TypeScript | 许可证 | MIT |
| 创建 | 2026-02-18 | 最后推送 | 2026-04-19 |
| 论文 | [LCM Paper](https://papers.voltropy.com/LCM) | ||
| 可视化 | [losslesscontext.ai](https://losslesscontext.ai) | ||
| 作者 | Martian Engineering / Voltropy |
解决什么问题
OpenClaw 在对话超出上下文窗口时,用滑动窗口截断老消息。信息永久丢失。
Lossless Claw 用 LCM(Lossless Context Management) 替代滑动窗口:
- 每条消息存入 SQLite 数据库(原文永久保留)
- 老消息被摘要压缩成 DAG(有向无环图)层级树
- 摘要可追溯原文——Agent 能 drill down 恢复细节
- 没有任何信息丢失
DAG 摘要通俗解释
这里的 DAG 不是 Exa Canon 那种任务编排图,而是摘要层级树。
普通 compaction(滑动窗口):
消息 1-200 → LLM 总结成一段话 → 丢掉原文 → 继续聊
消息 201-300 → 再总结 → 丢掉 → 继续
...
最终只剩最近几轮 + 一段模糊的总结
"之前聊过一些关于部署的事" ← 你问具体细节?没了
Lossless Claw 的 DAG 摘要:
第1天聊天(消息1-50)
├── 原文完整保留(SQLite)
└── Leaf 摘要:"讨论了 oMLX 报告部署流程,遇到 wrangler 缓存问题"
│
▼
第2天聊天(消息51-120)
├── 原文完整保留
└── Leaf 摘要:"研究了 browser-harness,写了从零开始教程"
│
▼
第3天聊天(消息121-200)
├── 原文完整保留
└── Leaf 摘要:"分析了 active-memory 插件,讨论了 token 代价"
│
▼
┌───┴───┐
▼ ▼
Condensed Condensed
节点 A 节点 B
"本周主要 "技术分析类:
研究了3个 oMLX、browser-harness、
开源项目" active-memory"
│
▼
Root 节点
"本周工作总结:研究了 oMLX、browser-harness、active-memory 三个项目,
写了3份报告部署到 temp.jaylab.io"
树结构就是 DAG(有向无环图):
Root
/ \
Cond-A Cond-B
/ | \ | \
L1 L2 L3 L4 L5 ← Leaf 节点(每段对话的摘要)
| | | | |
原文 原文 原文 原文 原文 ← 永远保存在 SQLite 里
Agent 平时只看 Root 节点(省 token),需要细节时用 lcm_expand 逐层展开(不丢信息)。
工作原理
对话增长
↓
超出阈值(75% 上下文窗口)→ Hard Trigger
或未压缩消息累积超过 80K tokens → Soft Trigger
↓
最近的 8 条消息保持原文(freshTailCount)
↓
更早的消息按 chunk 摘要(leafChunkTokens: 80000 tokens/chunk)
↓
摘要累积后进一步压缩成高层节点(condensed nodes)
↓
形成 DAG(有向无环图)
↓
每次对话组装:高层摘要 + 中层摘要 + 最近原文
两种触发方式
1. Hard Trigger(上下文快满了)
当上下文 token 数超过阈值时自动触发:
const threshold = Math.floor(this.config.contextThreshold * tokenBudget);
// contextThreshold 默认 0.75
// 如果模型上下文是 200K tokens,阈值就是 150K
if (currentTokens > threshold) → shouldCompact: true
"紧急压缩"——上下文快爆了,必须立刻压缩。会持续跑 leaf + condensed 直到降到阈值以下。
2. Soft Trigger(增量维护)
即使没满,如果最早的未压缩消息累积超过 leafChunkTokens(默认 80,000 tokens),也会触发:
async evaluateLeafTrigger(conversationId, leafChunkTokens) {
// 最老的未压缩消息累积超过 80K tokens?
// → 触发一次 leaf compaction(只压缩最老的一批)
}
"日常维护"——定期把老消息摘要化,避免一次性压太多。只跑一次 leaf + 最多 1 轮 condensed。
不是"满了才触发"——类似数据库的 checkpoint + WAL,满了硬触发 + 没满但有老消息时软触发。
核心配置参数
| 参数 | 默认值 | 说明 |
|---|---|---|
| `contextThreshold` | 0.75 | 上下文使用率超过 75% 触发 hard |
| `leafChunkTokens` | 80,000 | 未压缩消息累积超过 80K 触发 soft |
| `freshTailCount` | 8 | 最近 8 条消息永远不压缩 |
| `leafMinFanout` | 8 | 至少 8 个 Leaf 才合并成 Condensed |
| `condensedMinFanout` | 4 | 至少 4 个 depth≥1 才合并成更高层 |
| `condensedMinFanoutHard` | 2 | 强制触发时放宽到 2 |
| `incrementalMaxDepth` | 1 | 每次 soft trigger 最多 1 轮 condensed |
| `leafTargetTokens` | 2,400 | Leaf 摘要目标 token 数 |
| `condensedTargetTokens` | 2,000 | Condensed 摘要目标 token 数 |
| `maxExpandTokens` | 4,000 | lcm_expand 最大返回 token 数 |
树的膨胀限制(源码分析)
没有硬性深度上限,代码里没有 MAX_DEPTH 或 maxTreeHeight。但有三道软限制让树对数增长。
1. incrementalMaxDepth(默认 1)
每次 compaction 循环,condensed pass 最多跑 1 轮:
leaf chunk → Leaf 摘要 (depth=0) → 1 轮 condensed → depth=1 → 停
下一次 compaction 才会再往上长一层。可通过 LCM_INCREMENTAL_MAX_DEPTH 环境变量调整。
2. Fanout 门槛(自然减速器)
每高一层,需要的消息量乘以 4-8 倍:
8 条消息 → 1 个 Leaf (depth=0)
64 条消息 → 1 个 Condensed (depth=1)
256 条消息 → 1 个 depth=2
1024 条 → 1 个 depth=3
4096 条 → 1 个 depth=4
3. /new 命令修剪
/new → 保留 depth >= retainDepth 的节点(默认 2)
→ 删除所有原始消息
→ 树被砍到只剩顶部 2 层
实际膨胀估算
| 消息量 | 最大树深度 | 节点数 | SQLite 大小 |
|---|---|---|---|
| 100 | 1 | ~20 | ~200KB |
| 1,000 | 2 | ~140 | ~1.4MB |
| 10,000 | 3 | ~1,300 | ~13MB |
| 100,000 | 4 | ~12,500 | ~125MB |
| 1,000,000 | 5 | ~125,000 | ~1.25GB |
百万条消息才到 depth=5,正常使用几乎不可能触及。
Session 重置对 LCM 的影响
OpenClaw 有多种 session 重置方式,对 LCM 的影响各不同:
/new — 轻量重置
删除所有原始消息(item_type = 'message')
保留 depth >= retainDepth(默认 2)的摘要节点
删除 depth < 2 的摘要
→ 树被砍到只剩顶部 2 层
→ 下次对话从 retained summaries 重建上下文
/reset — 完全重置
归档当前 conversation row
创建新的 active conversation row(同一个 sessionKey)
→ 下次对话获得全新的 LCM 对话
→ 但历史摘要仍保留在 SQLite 里(可以手动恢复)
Idle 过期(配置后生效)
session.reset.idleMinutes(默认关闭)
空闲 N 分钟后,下条消息创建新 session
reason = "idle"
Daily 重置(配置后生效)
session.reset.mode: "daily"(默认关闭)
每天 4:00 AM 后首条消息创建新 session
reason = "daily"
对比
| 触发 | 新 sessionId | LCM 修剪 | LCM 归档 |
|---|---|---|---|
| `/new` | ✅ | ✅ 保留 depth≥2 | ❌ |
| `/reset` | ✅ | ❌ | ✅ 全部归档 |
| `idle` | ✅ | ❌ | ❌ |
| `daily` | ✅ | ❌ | ❌ |
idle/daily 是最温和的重置——新窗口 + 完整历史摘要保留。LCM 的 assembler 会用之前的摘要重建上下文。
Agent 工具:lcm_grep 和 lcm_expand
不会自动调用——只有在 Agent 主动决定要用的时候才会调用。
lcm_grep(搜索历史)
Agent 觉得需要查找之前对话里的内容时:
用户: "之前那个部署命令是什么来着?"
Agent 调用: lcm_grep("deploy wrangler")
→ 搜索所有摘要节点 + 原文
→ 返回匹配的摘要片段
lcm_expand(恢复原文)
找到相关摘要后,需要看原文细节:
lcm_grep("wrangler") → 返回摘要: "讨论了 wrangler 缓存问题"
Agent 调用: lcm_expand(summary_id="abc123")
→ 恢复该摘要对应的所有原始消息
lcm_expand_query 可以搜索+展开一步完成:
lcm_expand_query("wrangler deploy command")
→ 自动搜索 + 恢复最匹配的原文
被动 vs 主动
| 工具 | 谁触发 | 什么时候 |
|---|---|---|
| `lcm_grep` / `lcm_expand` | Agent 自己决定 | Agent 觉得需要搜历史 |
| `memory_search` / `memory_get` | Active Memory 自动 | 每次回复前(如果启用) |
与 QMD 的关系
Lossless Claw 和 QMD 是互补关系,解决不同层面的问题:
| Lossless Claw | QMD | |
|---|---|---|
| **管什么** | 对话历史(session context) | 记忆文件(memory files) |
| **解决什么** | 上下文窗口满了,老消息被截断 | 跨会话搜索记忆文件,找到相关事实 |
| **存储方式** | SQLite(内部) | 本地 API 服务(监听端口) |
| **工作层级** | 替代 OpenClaw 内置 compaction | 替代内置 memory-core 的搜索后端 |
| **搜索工具** | `lcm_grep` / `lcm_expand` | `memory_search` / `memory_get` |
| **互斥?** | 不互斥,可以同时装 | QMD 和 memory-core 互斥 |
OpenClaw 记忆体系全景:
├── 记忆存储层(写文件)
│ └── MEMORY.md / memory/*.md
├── 记忆搜索层(读文件)← QMD 在这里
│ ├── memory-core(默认)
│ └── QMD(更快更准的替代)
└── 对话上下文层(管理聊天历史)← Lossless Claw 在这里
├── 滑动窗口 compaction(默认)
└── Lossless Claw(DAG 替代)
为什么社区说 "QMD + LCM = incredible":
- QMD:你说"记住我喜欢拉面",三个月后在另一个会话问"我喜欢吃什么",QMD 能搜到
- LCM:今天聊了 200 条消息,第 180 条说了部署命令,第 201 条说"刚才那个命令再来一次",LCM 能恢复原文
- 两个都有:短期不丢 + 长期不丢
三层记忆体系:
Layer 1: 最近上下文(OpenClaw 自动管理,LCM 控制压缩方式)
Layer 2: 对话历史搜索(lcm_grep/expand,Agent 手动调用)
Layer 3: 长期记忆搜索(memory_search/get,Active Memory 自动调用)
代价分析
1. 时间延迟
每次回复前可能触发 compaction(soft trigger 检查),但:
- Soft trigger 检查本身很快(查 SQLite token 计数)
- 实际压缩是异步的,不阻塞回复
lcm_expand恢复原文需要 LLM 调用,有延迟
2. SQLite 存储
- 每条消息存原文 + 元数据
- 长期对话会积累,但有 fanout 自然限制
- 百万消息 ≈ 1.25GB,正常使用远低于此
3. 摘要质量
- 用便宜模型做摘要,质量会下降
- 用强模型做摘要,成本上升
- 这是"压缩比 vs 质量"的永恒权衡
4. 与 Active Memory 的交互
如果同时启用 LCM + Active Memory:
- Active Memory 的 sub-agent 搜索记忆时,recent 模式会看到 LCM 压缩后的对话
- LCM 的摘要质量直接影响 Active Memory 的 recall 效果
- 两者都有额外的 token 消耗
社区评价
🔥 OpenClaw 创始人公开推荐——Reddit 帖标题就是 "OpenClaw's creator says use this plugin"。
🔥 "解决了最大痛点"——上下文窗口丢失是 OpenClaw(和所有 Agent)最大的问题。
🔥 QMD + Lossless Claw 组合被称 "incredible"——测试了所有记忆插件后的结论。
🔥 改变 Agent 行为模式——从"20 分钟后就忘"变成"可以过夜运行不丢信息"。
🟡 配置有学习曲线——一行安装,但参数需要根据模型调整。
🟡 摘要质量依赖模型——便宜模型质量差,强模型成本高。
⚠️ 116 个 open issues——用的人多、问题也多。cache-aware compaction 兼容性问题多次修复。
⚠️ 版本更新频繁——快速迭代中,稳定性还在提升。
总结:OpenClaw 生态"必装插件"级别。争议不在"要不要用",而在"怎么调参最优"。
来源:r/openclaw、r/clawdbot、r/OpenClawInstall、r/AISEOInsider(2026年3月)
管理命令
/lcm backup — 备份 SQLite 数据库
/lcm doctor — 诊断配置和健康状态
/lcm rotate — 轮转日志文件
/lcm status — 查看当前状态
/lcm tui — 终端 UI 浏览对话历史
安装
openclaw plugins install @martian-engineering/lossless-claw
安装后自动替代内置 compaction,不需要改任何配置。
分析
优势:
- 🔥 解决真实痛点——上下文丢失是 Agent 最大的用户体验问题
- 📊 DAG 层级摘要——从粗到细,省 token 又不丢信息
- 🔍 lcm_grep/expand——Agent 可以主动搜索和恢复原文
- 🧹 cache-aware compaction——Anthropic prompt cache 热时不压缩
- 🔌 一行安装——零配置即用
- 🏆 创始人推荐——OpenClaw 官方背书
- 📈 4,383 stars——社区认可度高
风险:
- ⚠️ 116 个 open issues——稳定性还在提升
- ⚠️ 摘要质量依赖模型——需要调参
- ⚠️ 额外存储——SQLite 会持续增长(但对数级)
- 🟡 cache-aware compaction 兼容性——多次修复
与 Jay 的关联:
- 🔥 直接可用——一行安装,researcher agent 的 cron lint 上下文溢出问题可能直接解决
- 配合 Active Memory——LCM 保证对话上下文完整,Active Memory 自动注入记忆
- 配合 QMD——短期 + 长期记忆全覆盖
- Polymarket 项目——长时间运行的交易 Agent 需要不丢上下文
评分
| 维度 | 评分 (1-10) | 说明 |
|---|---|---|
| 设计质量 | 9 | DAG 层级摘要 + cache-aware + fanout 自然限制 |
| 实用性 | 9 | 一行安装,解决真实痛点,创始人推荐 |
| 代码质量 | 8 | TypeScript,SQLite,完善的管理命令 |
| 社区 | 8 | 4,383 stars,活跃维护 |
| 稳定性 | 6 | 116 issues,cache-aware 多次修复 |
| 与 Jay 的关联 | 9 | 直接解决 cron 上下文溢出,配合 Active Memory + QMD |
| **总分** | **8.2** | OpenClaw 生态必装插件 |