7.2 KiB
OpenClaw中文记忆搜索修复
OpenClaw SQLite FTS5 中文搜索 Ollama 记忆优化
🧠 openclaw-memory-cn
让 OpenClaw 的中文记忆搜索从「废物」变「能用」
55% → 100% 命中率 | 0 代码修改 | 5 分钟部署
😡 你是不是也遇到过这种情况?
你: 我老婆怀孕要去哪个医院来着?
AI: 抱歉,我在记忆中没有找到相关信息。
你: ???我明明上周跟你说过啊!!
这不是你的问题,是 SQLite FTS5 的 bug。
OpenClaw 用 FTS5 做全文搜索,但它的 unicode61 分词器会把 老婆刚怀孕选定广东省妇幼保健院 当成一整个 token。你搜 怀孕?不好意思,匹配不上。
本项目用纯配置方案(0 代码修改)解决这个问题。
📊 效果对比
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 中文搜索命中率 | 55% | 100% | +82% |
| 零召回查询 | 10/20 | 0/20 | 🎉 |
| 最高检索分数 | 0.67 | 0.80 | +19% |
| 记忆体积 | ~780KB | ~460KB | -41% |
测试环境:OpenClaw 2026.3.2 + qwen3-embedding:0.6b + 80个记忆文件
🚀 快速开始
30 秒版本(老手)
# 应用搜索参数 + memoryFlush prompt
openclaw config patch < config/memory-search.json
openclaw config patch < config/memory-flush-prompt.json
# 给项目文件加标签 + 重建索引
python3 scripts/add-tags.py ~/path/to/workspace/memory/projects/
openclaw memory index --force
5 分钟版本(新手)
第一步:诊断
git clone https://github.com/abczsl520/openclaw-memory-cn.git
cd openclaw-memory-cn
./scripts/diagnose.sh
脚本会告诉你:
- ❌ FTS5 是否有中文分词 bug
- ❌ 哪些文件过大需要压缩
- ❌ 哪些项目文件缺少标签
第二步:优化搜索配置
openclaw config patch < config/memory-search.json
这会设置:向量搜索权重 0.75(因为 FTS5 中文不可靠)、更小的 chunk size(帮助弱模型精确匹配)、MMR 去重、时间衰减等。
第三步:优化写入格式
openclaw config patch < config/memory-flush-prompt.json
以后 memoryFlush 写日志时会自动:
- 第一行加
<!-- tags: 关键词 -->标签 - 中文关键词空格分隔(
老婆 怀孕而非老婆怀孕) - 控制在 5KB 以内
第四步:清理和标记
# 给现有项目文件批量加标签
python3 scripts/add-tags.py ~/workspace/memory/projects/
# 压缩膨胀的日志(>8KB 的压缩到 <5KB)
python3 scripts/compress-logs.py ~/workspace/memory/ --max-kb 5
# 清理垃圾文件
./scripts/cleanup.sh ~/workspace/memory/
# 重建索引
openclaw memory index --force
第五步:自动维护(可选但推荐)
设置每周 cron 自动压缩日志 + 清理 archive + 补标签 + 重建索引:
复制 templates/cron-job.json 的内容,通过 OpenClaw 的 cron 功能添加。
🔬 核心发现:FTS5 的中文 Bug
问题
SQLite FTS5 默认的 unicode61 分词器把连续中文字符视为一个 token:
-- "老婆刚怀孕" 在索引中是 1 个 token
-- 搜索 "怀孕" → 0 results ❌
SELECT * FROM chunks_fts WHERE chunks_fts MATCH '怀孕'; -- → 0 ❌
证据
从 FTS5 词汇表中查到的实际 token:
| 原文 | FTS5 token | 字符数 |
|---|---|---|
| 元宝老婆刚怀孕 | 元宝老婆刚怀孕 |
7 |
| 选定广东省妇幼保健院天河院区 | 选定广东省妇幼保健院天河院区 |
13 |
| 也叫刘二虎 | 也叫刘二虎 |
5 |
解决方案
在关键词之间加空格,让 FTS5 把它们索引为独立 token:
❌ "老婆刚怀孕,选定广东省妇幼保健院" → 1个token
✅ "老婆 刚怀孕,选定 广东省 妇幼保健院" → 4个token,搜索可命中
详细技术分析见 docs/fts5-unicode61-bug.md
📐 三层记忆架构
workspace/
├── MEMORY.md (P0) ← 核心事实,每次启动必读(<2KB)
│ ├── 用户关键信息
│ ├── 服务器/凭证(指针)
│ └── 核心工作规则
└── memory/
├── projects/ (P1) ← 项目档案,带 tags 标签
├── lessons/ (P1) ← 经验教训,搜索命中率最高的文件类型
├── YYYY-MM-DD.md (P2) ← 每日日志(自动生成+自动分词)
└── archive/ ← 完整备份(30天自动清理)
模板文件见 templates/
⚙️ 推荐参数
| 参数 | 值 | 原因 |
|---|---|---|
vectorWeight |
0.75 | FTS5 中文不可靠,向量主导 |
textWeight |
0.25 | FTS5 只对英文和分词后的中文有效 |
chunking.tokens |
250 | 小 chunk 帮助弱模型精确匹配 |
chunking.overlap |
60 | 保证上下文连贯 |
minScore |
0.15 | 小模型分数偏低,阈值要低 |
halfLifeDays |
90 | 项目配置长期有效 |
MMR lambda |
0.7 | 适度去重 |
🤔 适用范围
| ✅ 适用 | ❌ 不适用 |
|---|---|
| OpenClaw 2026.3+ | 其他 AI 框架 |
| Ollama 本地小模型 | OpenAI/Voyage 云端大模型* |
| 中文为主的使用场景 | 纯英文场景(FTS5英文正常) |
| qwen3-embedding:0.6b | text-embedding-3-large |
| nomic-embed-text | voyage-3 |
| mxbai-embed-large |
*云端大模型用户:文件结构和 memoryFlush prompt 仍然适用,但搜索参数需要调整(大模型不需要这么极端的 vectorWeight)。
📈 为什么这些优化有效
写入时优化(memoryFlush prompt)
↓
tags 标签 → FTS5 精确命中 ✅
中文空格 → FTS5 正确分词 ✅
大小控制 → chunk 更精确 ✅
↓
搜索时优化(config 参数)
↓
vectorWeight 0.75 → 绕过 FTS5 中文缺陷 ✅
小 chunk → 弱模型更容易匹配 ✅
低 minScore → 不漏掉低分但相关的结果 ✅
MMR → 不重复返回相似内容 ✅
↓
维护时优化(cron job)
↓
压缩旧日志 → 减少噪音 ✅
补 tags → 新文件也有标签 ✅
重建索引 → 保持索引新鲜 ✅
🤝 贡献
欢迎提交 Issue 和 PR!特别欢迎:
- 📊 其他小模型(nomic-embed-text、mxbai 等)的测试数据
- 🇯🇵🇰🇷 日文/韩文的 FTS5 分词问题验证(理论上同样受影响)
- 🔧 更好的中文分词解决方案
- 📝 其他语言的翻译
⭐ Star 历史
如果这个项目帮到了你,请给个 Star ⭐ 让更多人看到!
📄 许可证
从实际生产环境中总结,所有数据均为真实测试结果。