记一次 AI 记忆系统的彻底重构:SummerMemory v2.0 实战
三个月前我给自己搭了个 AI 记忆系统 SummerMemory——PostgreSQL + pgvector + Ollama 跑中文向量模型,配合 BM25 全文检索,纯本地零成本。当时觉得挺完美:博客写下"混合搜索 60% 向量 + 40% BM25,搜索 78ms"。
然后用着用着,问题来了。
问题:记忆明明有,就是搜不到
最典型的一次:我下午刚和 AI 聊完《美丽的新世界》这本书,晚上搜"美丽的新世界"——搜不到。记忆确实在库里,检索就是不出来。
类似的情况反复发生。我忍了两个月,直到有天晚上下定决心:把三个月的搜索日志、历史踩坑记录、真实查询数据全部翻出来,一次性搞清楚到底哪里坏了。
翻旧账:三个月踩过的 9 个坑
把所有历史问题摆出来之后,发现远比想象的严重:
| 序号 | 问题 | 根因 |
|---|---|---|
| 1 | 目标文件进不了候选池 | 召回深度不够 |
| 2 | BM25 检索 30 个文件同分 | 归一化后无法区分 |
| 3 | ts_rank 对同分文件无区分力 | PostgreSQL tsvector 固有限制 |
| 4 | Reranker 截断丢候选 | 同分随机截断 |
| 5 | 关键信息被截断切掉 | 分块策略缺陷 |
| 6 | 空标题块和内容分离 | 分块合并方向错了 |
| 7 | 搜"部署工具"返回"豆包说话风格" | 向量模型排序质量差 |
| 8 | 大文件永远霸榜 | RRF 同文件累加的天然缺陷 |
| 9 | 短查询被挤出前五 | 默认返回数太小 |
归类下来:5 个排序层问题、2 个分块问题、2 个向量引擎问题。
我还统计了 2075 次真实搜索的分布:80% 是 8 字以上的自然语句(比如"怎么部署新工具"),12% 是短词("待办"、"yx"),8% 带日期。这个数据后来直接决定了新架构的设计方向。
最疼的坑:向量模型是个"演员"
逐个排查到第 7 个坑时,我做了组对照实验——用同一批文档测两个向量模型的排序能力:
| 查询 | jina 排第一 | bge-m3 排第一 |
|---|---|---|
| 怎么部署新工具 | 豆包说话风格(错) | 部署规范(对) |
| 排插控制关机 | 豆包说话风格(错) | 排插控制文档(对) |
| 数据库连接失败 | 陪看电影规范(错) | PostgreSQL 排查(对) |
无关文件的相似度分数反而更高——这个模型跑了三个月的"语义理解"基本是失效的。这就是"记忆里有却搜不到"的最大元凶。
更戏剧的是,查的时候发现这个模型已经从 Ollama 官方库下架了,想重装都装不回来。
重构方案:三层漏斗
问题查清后,新架构的思路自然就出来了:
查询进来
|
【缓存】搜过的词直接返回(13ms)
|
【第一层】精确直达:文件名/日期直接匹配(<1ms)
|
【第二层】BM25 关键词:jieba 分词 + 标题加权(5-25ms)
|
【第三层】向量语义:bge-m3 1024 维(约 230ms)
|
融合:向量 0.55 + BM25 0.45,同文件只取最高分块
三个关键决策:
短查询不走向量。 "待办"两个字字面匹配又快又准,走向量纯属浪费。4 字以内纯 BM25,13 毫秒返回。
BM25 升为主力。 旧架构里它只是配角,实测才发现它又快又稳。加了标题命中加权后,80% 的查询场景它一个人扛住了。
同文件只取最高分块。 RRF 的坑:一个 99 个分块的大杂烩文件,靠累加分数永远排第一。改成同文件只取最高分的那块参与排名,大小文件公平竞争。
向量引擎换血:bge-m3
替代品选了 bge-m3(北京智源 BAAI 出品,HuggingFace 下载量 3600 万):
- 1024 维(原来 768 维)
- Q8 量化版 630MB,纯 CPU 推理 230ms(24 核老至强)
- 常驻内存 1.25GB
- 实测排序:4 组对照全对,分差 0.065 到 0.335——拉得开,不含糊
分块重做
两个分块坑一起修:
空标题节原来并到上一块,导致"待办"标题和待办内容分家——改成并到下一块。
每个分块现在自带标题链:
[tool-deploy > 第三步:配置 nginx]
location /工具名/api/ { proxy_pass ... }
向量模型看到的不再是孤立片段,而是"这属于部署文档的 nginx 章节",语义判断准确得多。
实测结果
20 个真实查询的基准测试(含全部历史坑案例):
| 指标 | 旧版 | 新版 |
|---|---|---|
| 第一名命中率 | 17% | 85% |
| 前三命中率 | 无数据 | 95% |
历史坑全部翻身:"美丽的新世界"从搜不到变成第一名直接命中。
性能:
| 场景 | 耗时 |
|---|---|
| 缓存命中 | 13ms |
| 关键词查询 | 13-25ms |
| 语义查询 | 约 300ms |
300ms 里 230ms 是 bge-m3 在 CPU 上把查询句转向量的物理耗时,无 GPU 服务器的极限。配合 Ollama 的模型常驻参数,冷启动也消灭了。
几点心得
实测数据大于理论架构。 RRF 是业界标准,Elasticsearch 和 Azure 都在用——但在我的数据规模(约 2000 个分块)和大文件分布下就是灾难。别人的最佳实践不一定是你的。
先查真实查询分布再设计。 80% 长语句对 12% 短词的分布,让我敢做"短查询跳过向量"的分流。没有这个数据,我不知道该在哪切。
量化模型要看排序质量,不看参数表。 旧模型参数表漂亮:768 维、8192 上下文、116MB。实际排序是错的。一个简单的相关性对照测试(相关对对比无关对)就能提前暴露,选型时就应该测。
坑要记全。 这次重构的依据,就是三个月攒下的 9 个坑的完整记录。没有这些记录,同一个地方我会摔第二次。
开源
GitHub:https://github.com/Likefr/SummerMemory
在线图谱 Demo:https://ai.likefr.com/graph
v1 诞生于 2026-05-30 | v2.0 重构于 2026-08-26 | Likefr × Summer