
🧠 SummerMemory — 我自己搭的 AI 记忆系统
💡 零成本 · 全中文 · 毫秒级 · 干翻商业方案
SummerMemory 记忆系统采用PostgreSQL+pgvector做向量数据库
Ollama jina-embeddings-v2-base-zh + BM25 + RRF 排名融合
jieba中文分词
纯本地全免费。不比商业的记忆系统差,搜索记忆毫秒级,比网络延迟还小
🎬 我为什么要做这个?
我玩 AI 有一段时间了,日常用它改代码、写文档、总结日报、控制家里的智能设备。
用着用着,我发现一个问题:它老是忘事。
我前天告诉它"我看了一本书",今天问它"我最近看了什么书",它说不知道。
我让它搜"我最近使用的前端技术",它搜不到,但明明近期有让它优化 Vue 的代码。
我算是发现了,它的记忆系统有三个问题:中文搜不准、不懂变通、搜不到。
所以我就自己做了一个,我称它为 SummerMemory。
🎯 这个项目是干什么的?
简单说:让 AI 记住事情,而且能准确找到。
❌ OpenClaw 记忆系统的问题
第一个:中文搜不准
比如存了"我买了苹果和香蕉",但日后搜"水果"搜不到。
因为字面上没有"水果"这两个字。
第二个:不懂变通
存了"我开车去上班",搜"通勤"搜不到。
因为它不知道"开车去上班"就是"通勤"。
✅ SummerMemory 怎么解决?
我用了这些技术:
PostgreSQL+pgvector 做向量数据库,Ollama jina-embeddings-v2-base-zh 向量召回 + BM25 精确召回 + RRF 排名融合,jieba 中文分词,纯本地全免费。搜索平均 78ms,比网络延迟还小。
核心思路:把文字变成数字,用数学方法算相似度。
🔬 技术拆解
第一步:什么是"向量"?
向量就是一组数字。电脑不认识字,但认识数字。
比如:
"开心" → [0.25, -0.34, 0.56, ...]
"愉快" → [0.23, -0.33, 0.55, ...]
"快乐" → [0.26, -0.35, 0.57, ...]
"悲伤" → [-0.80, 0.45, -0.23, ...]
你看,"开心"、"愉快"、"快乐"的数字很接近,因为意思差不多。
"悲伤"的数字差很远,因为意思不一样。
这就是向量搜索:意思相近的文字,数字也相近。
第二步:什么是"分词"?
中文没有空格,得靠工具拆分。
jieba 能把一句话拆成最小的单位。
比如"我昨天去超市买了苹果和香蕉",拆成:
["我", "昨天", "去", "超市", "买", "了", "苹果", "和", "香蕉"]
中文词语讲究成双成对,"苹果"是一个词,"超市"是一个词。但"还买"不是一个词,jieba 会把它拆开。
第三步:什么是"BM25"?
BM25 是一种搜索算法,看一个词出现多少次。
你搜"苹果",文章里"苹果"出现 5 次的,比出现 1 次的分数高。
还有一个聪明的地方:如果一个词在所有文章里都出现(比如"的"、"是"),重要性就低。如果一个词只在少数文章里出现(比如"苹果"),重要性就高。
第四步:什么是"混合搜索"?
向量搜索能理解语义,但不够精确。
BM25 搜索能精确匹配,但不懂语义。
所以 SummerMemory 把两种结合起来:
- 同时用两种方式搜索
- 合并结果
- 向量分占 60%,BM25 分占 40%
- 按总分排序
这样既能理解语义,又能精确匹配。
第五步:什么是"RRF 排名融合"?(实战迭代)
最初博客里写的是"LLM 重排(计划做的)"。后来真的去做了,这条路踩满了坑:
- LLM 重排(qwen3:0.6b):CPU 推理 12-30 秒/条,thinking 模式吃掉所有 token,输出不稳定——不可用
- BGE-Reranker(Cross-Encoder):61ms/pair 可用,但扩大召回后发现真正的问题是 BM25 同分灾难——28 个文件分数完全一样,重排模型再准也救不了候选池随机截断
最后从 Azure AI Search 官方文档找到了业界标准方案 RRF(Reciprocal Rank Fusion):
RRF_score = Σ 1/(rank + 60)不看原始分数,只看排名位置。28 个 BM25 同分文件按排名天然区分开,向量高分也不会被同分淹没。Elasticsearch、Azure、Vespa 全在用。
最终架构(纯 RRF,无重排):
向量 top 30 ──→ RRF: 1/(rank+60) ──┐
├─ 同文件求和 ──→ 去重 top N ──→ 返回
BM25 top 50 ──→ RRF: 1/(rank+60) ──┘平均 78ms,P99 105ms,CPU 2.1%,内存 158MB。完整的踩坑过程见《搜索优化实战:从混合搜索到 RRF》。
📝 举个完整的例子
你跟 AI 说:
"我昨天去超市买了苹果和香蕉,还买了一盒牛奶,花了 50 块钱"
SummerMemory 处理流程:
第一步:切分
切成三块:
- "我昨天去超市买了苹果和香蕉"
- "还买了一盒牛奶"
- "花了 50 块钱"
第二步:转向量
把每块文字变成数字:
- "苹果" → [0.12, -0.34, 0.56, ...]
- "香蕉" → [0.11, -0.33, 0.55, ...]
- "牛奶" → [0.23, -0.45, 0.67, ...]
"苹果"和"香蕉"的数字很接近,因为都是水果。"牛奶"的数字差远一点,因为是饮料。
第三步:分词
jieba 这个框架常用于电商的关键字匹配搜索,分词可以更好地匹配用户的搜索意图。
["我", "昨天", "去", "超市", "买", "了", "苹果", "和", "香蕉"]
🦙 jina-embeddings-v2-base-zh 是什么?
这是 Ollama 里用的一个模型,专门用来把中文变成向量。
它的名字可以拆开看:
- Jina AI:开发这个模型的公司
- embeddings:把文字变成向量的模型类型
- v2:第二代模型
- base:基础版本,平衡精度和性能
- zh:专门针对中文优化
为什么要用这个模型?
- 它是专门针对中文优化的,中文效果比英文模型好
- 8192 token context,支持长文本不截断
- 768 维向量,精度更高
- Q5_K_M 量化后仅 116MB,CPU 就能跑,不需要 GPU
- 它是开源免费的,不用花钱
🔍 搜索流程演示
过几天,你问 AI:"我之前买了什么水果?"
第一步:查询转向量
"水果" → [0.15, -0.38, 0.52, ...]
第二步:向量搜索
"苹果"的向量 [0.12, -0.34, 0.56, ...] 和"水果"很接近。得分:0.85
第三步:BM25 搜索
查询分词:["我", "之前", "买", "了", "什么", "水果"]
原文里有"我"、"买"、"了",但没有"水果"。得分:0.60
第四步:混合排序
最终得分 = 0.85 × 0.6 + 0.60 × 0.4 = 0.75
第五步:返回结果
AI 回答:"你之前买了苹果和香蕉,都是水果哦!"
📊 性能数据
| 指标 | 数值 | 说明 |
|---|---|---|
| 📊 总文件数 | 80 个 | 记忆文件 |
| 📦 总 chunks | 768 个 | 分块后的记忆片段 |
| ⚡ 搜索速度 | 平均 78ms(P99 105ms) | 纯 RRF 架构实测 |
| 🧬 向量维度 | 768 维 | jina-embeddings-v2-base-zh |
| 💾 数据库大小 | ~120MB | 768 条记录 |
| 🔄 索引速度 | ~2s/文件 | 含 embedding 生成 |
| 💰 总成本 | ¥0 | 纯本地,零费用 |
🆚 SummerMemory vs OpenClaw 内置 vs 商业方案
| 对比维度 | 🧠 SummerMemory | 🔧 OpenClaw 内置 | 💰 商业方案 |
|---|---|---|---|
| 💰 成本 | ✅ 完全免费 | ✅ 免费 | ❌ $70-500/月 |
| 🏠 部署 | ✅ 本地服务器部署 | ✅ 本地 | ❌ 云端托管 |
| 🔒 隐私 | ✅ 零泄露风险 | ✅ 本地 | ❌ 数据上传第三方 |
| 📖 全文搜索 | ✅ BM25 + jieba | ❌ 不支持 | ✅ 有 |
| ✂️ 中文分词 | ✅ jieba 深度优化 | ❌ 无 | ⚠️ 通用分词 |
| 🧬 向量维度 | ✅ 768 维 | 384 维 | 768-1536 维 |
| 🔗 混合搜索 | ✅ 向量+BM25 | ❌ 仅向量 | ✅ 有(但要钱) |
| ⚡ 搜索速度 | ✅ 12-425ms | 100-500ms | 50-200ms + 网络延迟 |
| 📊 可视化 | ✅ D3.js 力导向图 | ❌ 无 | ⚠️ 需额外付费 |
| 🔧 自定义 | ✅ 完全可控 | ❌ 受限 | ⚠️ API 受限 |
🏗️ 服务架构
三层服务,全部本地服务器运行
- 🐘 PostgreSQL 18.1 + pgvector — Docker 容器,端口 5432,存储向量+全文数据
- 🦙 Ollama — Docker 容器,端口 11434,提供 Embedding 推理
- 🧠 SummerMemory Server — Python HTTP 服务,端口 11435,systemd 管理,开机自启,故障自愈
数据规模: 80 文件 → 768 chunks → ~120MB
🗺️ 图谱可视化
我还做了一个图谱可视化界面,把所有记忆用一张图画出来。
节点代表文件,连线代表关系。节点越大,内容越多;连线越粗,关系越紧密。
支持搜索、拖拽、点击查看详情。用 Vue 3 + force-graph 做的,支持中英文切换。
浏览器打开 https://ai.likefr.com/graph 就能看。
💬 一句话总结
🧠 SummerMemory = PostgreSQL + pgvector + Ollama + jieba + BM25
纯本地 · 全免费 · 中文优化 · 毫秒级搜索 · 不比商业方案差!
别人花 $500/月 买的记忆系统,我用 Docker 搞定了 🚀
🗄️ 对话归档(2026-08 新增)
记忆搜索管"记住知识",对话归档管"记住对话"。新增 conversations 表把 OpenClaw 的聊天记录原样落库——纯归档、不进搜索索引,与 memories 表完全隔离,容量再大不影响搜索性能。

双管道捕获:
- 实时:internal hook 监听 message:received / message:sent,fire-and-forget 写入,不影响聊天
- 兜底:sessions_watcher 2 秒轮询会话 jsonl 增量,负责 webchat AI 回复(源码级坑:webchat 走 chat.send 内部通道不发 message:sent 事件)+ 历史导入
双路写入用 5 秒窗口去重。CLI 三个命令:
python3 memory_system.py conv list # 列会话(带预览)
python3 memory_system.py conv get <key> # 完整对话
python3 memory_system.py conv stats # 统计顺序保证:ORDER BY timestamp + 自增 id,会话内消息顺序和当时聊天顺序一致。88000+ 条历史消息一夜导入完成。
🔗 开源地址
SummerMemory 已经开源,欢迎 Star!
📅 诞生于 2026-05-30 | 🔄 08-16 RRF 搜索 + 对话归档 | 🔧 持续优化中 | 💪 Likefr × Summer
1 条评论
感谢分享,谢谢站长!!已收藏