242 lines
7.2 KiB
Markdown
242 lines
7.2 KiB
Markdown
|
|
# OpenClaw中文记忆搜索修复
|
|||
|
|
|
|||
|
|
`OpenClaw` `SQLite` `FTS5` `中文搜索` `Ollama` `记忆优化`
|
|||
|
|
|
|||
|
|
<div align="center">
|
|||
|
|
|
|||
|
|
# 🧠 openclaw-memory-cn
|
|||
|
|
|
|||
|
|
**让 OpenClaw 的中文记忆搜索从「废物」变「能用」**
|
|||
|
|
|
|||
|
|
[](LICENSE)
|
|||
|
|
[](https://github.com/openclaw/openclaw)
|
|||
|
|
[](https://ollama.ai)
|
|||
|
|
|
|||
|
|
*55% → 100% 命中率 | 0 代码修改 | 5 分钟部署*
|
|||
|
|
|
|||
|
|
[English](README_EN.md) · [技术分析](#-核心发现fts5-的中文-bug) · [快速开始](#-快速开始) · [效果对比](#-效果对比)
|
|||
|
|
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 😡 你是不是也遇到过这种情况?
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
你: 我老婆怀孕要去哪个医院来着?
|
|||
|
|
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 秒版本(老手)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 应用搜索参数 + 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 分钟版本(新手)
|
|||
|
|
|
|||
|
|
**第一步:诊断**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git clone https://github.com/abczsl520/openclaw-memory-cn.git
|
|||
|
|
cd openclaw-memory-cn
|
|||
|
|
./scripts/diagnose.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
脚本会告诉你:
|
|||
|
|
- ❌ FTS5 是否有中文分词 bug
|
|||
|
|
- ❌ 哪些文件过大需要压缩
|
|||
|
|
- ❌ 哪些项目文件缺少标签
|
|||
|
|
|
|||
|
|
**第二步:优化搜索配置**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
openclaw config patch < config/memory-search.json
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这会设置:向量搜索权重 0.75(因为 FTS5 中文不可靠)、更小的 chunk size(帮助弱模型精确匹配)、MMR 去重、时间衰减等。
|
|||
|
|
|
|||
|
|
**第三步:优化写入格式**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
openclaw config patch < config/memory-flush-prompt.json
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
以后 memoryFlush 写日志时会自动:
|
|||
|
|
- 第一行加 `<!-- tags: 关键词 -->` 标签
|
|||
|
|
- 中文关键词空格分隔(`老婆 怀孕` 而非 `老婆怀孕`)
|
|||
|
|
- 控制在 5KB 以内
|
|||
|
|
|
|||
|
|
**第四步:清理和标记**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 给现有项目文件批量加标签
|
|||
|
|
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**:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- "老婆刚怀孕" 在索引中是 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](docs/fts5-unicode61-bug.md)
|
|||
|
|
|
|||
|
|
## 📐 三层记忆架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
workspace/
|
|||
|
|
├── MEMORY.md (P0) ← 核心事实,每次启动必读(<2KB)
|
|||
|
|
│ ├── 用户关键信息
|
|||
|
|
│ ├── 服务器/凭证(指针)
|
|||
|
|
│ └── 核心工作规则
|
|||
|
|
└── memory/
|
|||
|
|
├── projects/ (P1) ← 项目档案,带 tags 标签
|
|||
|
|
├── lessons/ (P1) ← 经验教训,搜索命中率最高的文件类型
|
|||
|
|
├── YYYY-MM-DD.md (P2) ← 每日日志(自动生成+自动分词)
|
|||
|
|
└── archive/ ← 完整备份(30天自动清理)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
模板文件见 [templates/](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 ⭐ 让更多人看到!
|
|||
|
|
|
|||
|
|
## 📄 许可证
|
|||
|
|
|
|||
|
|
[MIT](LICENSE)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
<div align="center">
|
|||
|
|
|
|||
|
|
*从实际生产环境中总结,所有数据均为真实测试结果。*
|
|||
|
|
|
|||
|
|
**[OpenClaw](https://github.com/openclaw/openclaw)** · **[Ollama](https://ollama.ai)** · **[SQLite FTS5](https://www.sqlite.org/fts5.html)**
|
|||
|
|
|
|||
|
|
</div>
|