# OpenClaw 对话和记忆优化初步方案 > 研究日期:2026-02-17 > 研究者:Ami + 阿米狗 > 状态:已实施并上线 --- ## 背景与目标 OpenClaw 默认配置下存在两个核心痛点: 1. **对话 context 超限**会直接中断会话,体验割裂 2. **记忆搜索被禁用**(需要 OpenAI/Gemini/Voyage API key,成本高) 本方案目标:在不依赖任何外部 API key 的前提下,实现: - 对话 context 的主动管理(不被动等爆) - 本地向量语义搜索(完全离线) - 跨会话记忆自动恢复 --- ## 核心约束 > 我们假设暂时解决不了对话 context 上限这一概念和顶层限制(200k token 硬上限由模型决定,无法改变)。 > 因此,所有优化都是在这个硬上限内做最大化利用。 --- ## 三步方案 ### Step 1 — Token 优化 + 自动压缩 **问题**:context 默认无上限地增长,直到 API 报错才触发压缩,体验差。 **解决**: | 配置项 | 值 | 作用 | |--------|-----|------| | `contextTokens` | `120000` | 软上限,到达后触发剪枝(低于模型 200k 硬上限,留出安全边际) | | `contextPruning.mode` | `cache-ttl` | 在 Anthropic 缓存 TTL 到期前删除老 tool output,减少 cacheWrite 费用 | | `contextPruning.keepLastAssistants` | `3` | 保留最近 3 条 assistant 消息,删除更早的 | | `compaction.mode` | `safeguard` | 确保 Pi runtime 的 compaction 有足够预留空间 | | `compaction.reserveTokensFloor` | `20000` | compaction 预留底线 20k token(防止压缩时空间不足) | **原理**:把原来的"被动爆仓"改成"主动管理",在 context 超限前就开始清理。 --- ### Step 2 — 新会话自动恢复历史(QMD 本地向量搜索) **问题**:`memory_search` 工具默认需要 OpenAI/Gemini/Voyage embedding API,没有 key 则完全禁用。 **解决**:安装并启用 **QMD**(Quick Markdown / 本地向量搜索引擎) #### QMD 是什么 - GitHub: [tobi/qmd](https://github.com/tobi/qmd) - OpenClaw 官方支持的实验性本地搜索后端 - 技术栈:BM25 + 向量(node-llama-cpp)+ reranking - **完全本地运行**,GGUF 模型首次自动下载,无 API key 需求 #### 安装步骤 ```bash # 1. 安装 Bun(QMD 运行时) curl -fsSL https://bun.sh/install | bash # 2. 安装 QMD bun install -g https://github.com/tobi/qmd # 3. 解锁 postinstall(bun 默认阻止) cd ~/.bun/install/global && bun pm trust --all # 4. 补充缺失的 @types/node 并 build cd ~/.bun/install/global/node_modules/@tobilu/qmd bun add -d @types/node && bun run build # 5. 验证 qmd --version # → qmd 1.0.6 ``` **注意**:`bun install -g` 安装的 qmd 包没有预编译 dist,需要手动 build TypeScript 源码。这是一个安装坑,已记录。 #### OpenClaw 配置 ```json { "memory": { "backend": "qmd", "citations": "auto", "qmd": { "command": "/Users/mini/.bun/bin/qmd", "includeDefaultMemory": true, "sessions": { "enabled": true, "retentionDays": 30 }, "update": { "interval": "5m", "debounceMs": 15000 }, "limits": { "maxResults": 6, "timeoutMs": 4000 }, "scope": { "default": "deny", "rules": [{ "action": "allow", "match": { "chatType": "direct" } }] } } } } ``` **关键配置说明**: - `backend: "qmd"` — 切换为本地引擎(替代内置 SQLite 索引) - `sessions.enabled: true` — 自动导出 session 对话记录到 QMD 索引(实现跨会话搜索) - `retentionDays: 30` — 保留 30 天历史 - `scope.rules` — 仅限直接私聊可搜索(不在群组里暴露私人记忆) - `citations: "auto"` — 搜索结果自动附带来源文件路径 **效果**:新会话开始时,`memory_search` 可以检索过去 30 天的对话历史摘要和记忆文件,自动恢复上下文。 --- ### Step 3 — Context 快满时自动提醒 **问题**:用户不知道 context 什么时候快满,只能被动等待中断。 **解决**:在压缩前的"软阈值"触发主动通知 #### 工作流程 ``` context 使用量 ↓ [120k 软上限] → contextPruning 开始清理老消息 ↓ [还剩 ~5k token] → memoryFlush 触发(比 compaction 更早) ↓ ├── 1. 把重要上下文写入 memory/YYYY-MM-DD.md └── 2. 发 Telegram 通知:⚠️ 对话快满了,建议发 /new ↓ [用户发 /new 开启新会话] ↓ [新会话] → memory_search 自动检索历史 → 无缝续接 ``` #### 配置 ```json { "agents": { "defaults": { "compaction": { "memoryFlush": { "enabled": true, "softThresholdTokens": 5000, "prompt": "Context is nearing the compaction limit. Do TWO things:\n1. Write important context/decisions from this conversation to memory/YYYY-MM-DD.md (today's date).\n2. Use the message tool to send a Telegram notification to the user (target: 5588544200, channel: telegram) saying: '⚠️ 对话快满了,建议发 /new 开启新会话,我会自动从历史记忆中恢复上下文。'\nReply with NO_REPLY after completing both steps.", "systemPrompt": "Pre-compaction housekeeping: persist memories and notify user. Silent turn." } } } } } ``` **原理**: - `softThresholdTokens: 5000` — 在 Pi runtime 触发 compaction **之前** 5000 token 就介入 - `prompt` 中的双任务:写记忆 + 发通知 - `NO_REPLY` — 对话中不显示任何内容(静默操作),但 tools 仍然执行(Telegram 消息照常发) --- ## 整体架构图 ``` 用户对话 │ ▼ [OpenClaw Gateway] │ ├── contextTokens: 120k → 软上限保护 ├── contextPruning: cache-ttl → 清理老 tool output │ ├── [context 剩 5k] → memoryFlush 触发 │ ├── 写 memory/YYYY-MM-DD.md │ └── 发 Telegram 通知 → 用户 │ ├── compaction: safeguard → 自动压缩摘要(最后防线) │ └── [用户 /new 开新会话] │ ▼ [新会话启动 — AGENTS.md 规则] ├── 自动读取 MEMORY.md(系统注入) ├── 自动读取 memory/今日+昨日.md(系统注入) └── 自动调用 memory_search("最近对话 上下文") │ ▼ QMD 本地向量索引(上限 5k tokens) ├── MEMORY.md(长期记忆) ├── memory/*.md(日志) └── sessions/*.jsonl(30天对话历史) │ ▼ 自动恢复上下文,无缝续接 ``` --- ## Step 4 — 新会话自动触发历史检索(AGENTS.md 规则) **问题**:QMD 搜索是"按需"的,新会话开始时需要主动触发,否则历史上下文不会自动加载。 **解决**:在 `AGENTS.md` 的"每次会话"规则中加入自动搜索指令。 #### 触发机制说明 | 层级 | 内容 | 加载方式 | |------|------|----------| | `MEMORY.md` | 长期记忆(核心事实、偏好) | 系统自动注入 context | | `memory/YYYY-MM-DD.md` | 今日 + 昨日日志 | 系统自动注入 context | | QMD session 历史 | 近 30 天对话记录 | **需调用 `memory_search` 触发** | #### AGENTS.md 新增规则 ```markdown ## Every Session Before doing anything else: 1. Read SOUL.md — this is who you are 2. Read USER.md — this is who you're helping 3. Read memory/YYYY-MM-DD.md (today + yesterday) for recent context 4. **If in MAIN SESSION**: Also read MEMORY.md 5. **If in MAIN SESSION**: Run memory_search("最近对话 上下文") to retrieve relevant history from QMD — restore any ongoing topics or tasks automatically ``` **Token 消耗评估**: | 场景 | 消耗 | |------|------| | QMD 本地搜索计算 | 0(纯本地) | | 搜索结果注入 context | 1k–5k tokens | | 上限(`maxInjectedChars: 20000`) | ~5000 tokens 硬上限 | **`maxInjectedChars` 的作用**:无论搜到多少内容,最终注入 context 的字符数不超过 20000(约 5000 tokens),防止历史检索本身消耗过多 context 空间。 #### QMD limits 完整配置 ```json { "memory": { "qmd": { "limits": { "maxResults": 6, "maxSnippetChars": 700, "maxInjectedChars": 20000, "timeoutMs": 4000 } } } } ``` **效果**:开新会话后无需任何提示,我会自动检索近期历史并将相关上下文带入当前会话,实现无缝续接。 --- ## 当前局限性 1. **QMD 首次搜索较慢**:需要自动下载 GGUF 模型(reranker + query expansion),约几百 MB 2. **session 索引延迟**:对话结束后才导出,不是实时的 3. **根本上限无法突破**:200k token 硬上限由 Anthropic 决定,所有优化只是更好地利用这 200k 4. **QMD build 依赖**:安装时需要手动 build TypeScript(bun 全局安装的包缺少 dist 目录,已知坑) --- ## 文件位置 | 文件 | 说明 | |------|------| | `~/.openclaw/openclaw.json` | 所有配置的实际存储位置 | | `/Users/mini/clawd/MEMORY.md` | 长期记忆(每次主会话加载) | | `/Users/mini/clawd/memory/YYYY-MM-DD.md` | 每日日志 | | `~/.openclaw/agents/main/qmd/` | QMD 索引数据库 + 缓存 | | `~/.openclaw/agents/main/sessions/*.jsonl` | 对话历史(被 QMD 索引)| --- ## 后续可探索方向 - **3层记忆架构**(@Ktaohzk 方案):Fact/Belief 分层 + 衰减评分,进一步减少 token 占用 - **session memory search 实验性功能**:`memorySearch.experimental.sessionMemory: true`(内置 SQLite 版的 session 索引,与 QMD sessions 二选一) - **混合搜索调优**:调整 `vectorWeight` / `textWeight` 比例优化检索精度 - **embedding 缓存**:`memorySearch.cache.enabled: true` 避免重复 embedding --- *本文档由 Ami + 阿米狗共同研究整理,2026-02-17,v1.1 更新于同日*