记忆引擎的自我进化:从正则到 LLM

0x00 序

上一篇文章《Hermes 记忆引擎实践》记录了记忆引擎的选型:Holographic——本地、轻量、SQLite 存储,配 HRR 全息向量检索。当时觉得它够用。

但用起来很快发现一个致命问题:它用正则提取实体,而正则对中文、小写主机名、域名这些我们天天打交道的名词完全失效。写进去的事实,实体关系建立不起来,图谱是一盘散沙。

这篇文章记录的就是后续的完整进化:发现问题 → 调研行业 → 决定自己造 → 数据治理 → 方法论沉淀。

发现缺陷:正则的无力

Holographic 的实体提取用三条正则规则:大写多词短语("John Doe")、引号内术语、AKA 模式。它假设实体都是英文命名习惯。

但我们的知识图谱里全是这样的实体:

  • 中文:百度网盘、天翼云、移动电脑
  • 小写主机名:sad、silk、cynic
  • 域名:tm.aketer.me、poto.aketer.me

正则一条都匹配不上。实测:写一条含这些名词的事实,引擎提取不出任何实体,图谱里的实体全是手动建的。

这不是修修补补能解决的——正则的范式就是错的。

调研:行业怎么解决

先看主流记忆引擎的做法:

引擎实体提取问题
Holographic正则中文/小写/域名全废
Mem0LLM 提取图谱是实体集合,非可视化
OpenVikingLLM 提取文件树非真图谱
Zep/GraphitiLLM + Neo4jNeo4j 依赖重,社区版弃用

为什么说 Neo4j 重?对比实际数据:

项目Neo4j 要求我们的 sad 本机
运行时Java 17/21(JVM + 调优 heap/pagecache)纯二进制,无 JVM
内存官方手册最低 2GB,生产建议 4GB+(heap + page cache)7.7GB 总,已用 2.8GB,同时跑 14 个服务
存储最小 10GB,数据目录含索引/日志,推荐 NVMe37GB 可用,普通磁盘
CPU2 核最低 / 4 核推荐8 核(够,但内存是瓶颈)

核心矛盾在内存:Neo4j 是 Java 应用,官方手册最低 2GB 只是"能跑",生产要 4GB 以上才流畅。而我们的服务器 7.7GB 内存同时跑着 Trilium、Vaultwarden、Kuma、OpenList、Caddy、frps、Easytier 等 14 个服务,剩余不到 5GB——给 Neo4j 分 2-4GB,所有服务都会被挤到 swap。

更关键的是:Neo4j 提供的能力是"图数据库",但我们要的不是又一个数据库,而是把记忆图谱可视化。Trilium 已经承担了这个角色(1900+ 笔记、noteMap 图视图、属性关系),Neo4j 是重复造轮子还更重。

另外,Graphiti(Zep 的图谱层)社区版已标记 deprecated(弃用),自建路径官方不再维护——长期风险大。

结论:没有"图谱 + LLM 提取"都强的轻量自托管方案。要么重(Neo4j),要么图谱能力弱(文件树)。

而我们已经有了 Trilium——真正的可视化知识图谱,1900+ 笔记沉淀。缺的只是"LLM 实体提取"这一环。

自研:Seraph 记忆引擎

既然没有现成的,就自己造。以 Holographic 为基底 fork,加 LLM 提取层,命名 Seraph。

Seraph 在 Holographic 基础上增加了三个 LLM 通道:

  • 实体提取(带类型):LLM 识别实体并分类,返回 [名字, 类型] 对——server/domain/service/project/repo 等 12 类
  • 关系提取:LLM 提取实体三元组(A -resolves_to-> B),存独立的 entity_relations 表
  • 实体描述生成:LLM 根据实体的关联事实生成结构化描述——点开 sad 实体节点,就是完整的服务器画像

关键设计:复用 Hermes 已有的模型配置(config.yaml 的 model.provider + .env 的 API key),不新增任何凭据。聚合类 provider(如 opencode-go)自动回退到可用的 deepseek。

实测效果——同一句话,正则 vs LLM:

方法提取结果
正则(空)——全是中文和小写
Seraph LLMtm.aketer.me(domain)、trilium(project)、sad(server)、百度网盘(service)、rclone(tool)

数据治理:422 到 265

切到 Seraph 后做了存量回填——134 条事实全部跑 LLM 提取。结果喜忧参半:

  • 喜:title 全补齐、567 条关系边自动生成、实体描述批量生成
  • 忧:LLM 提取创建了 422 个实体,其中 351 个是垃圾——DNS 记录类型(MX/TXT/SPF)、泛词(在线/离线/用户)、路径(/etc)、英文短语(multiple_machines)

于是开始实体表治理:

  • 识别噪声:DNS 类型/IP 段/端口/泛词/路径/0 关系实体
  • IP 是有效实体:IP 地址(38.76.190.84、10.126.126.3)是服务器核心属性,标 resource 类型,建 has_ip 关系——不删
  • 类型归一化:kuma→service、R2→resource、Ankia→project 等
  • 优化提示词:在 LLM 提示词里明确排除 DNS 类型、IP、泛词

最终:422 → 265 个实体,0 unknown,12 类分布合理。类型化的好处是 Trilium 里实体按类型分域归位(server→主机目录、service→服务目录),图谱结构清晰。

治理后的知识图谱
治理后的知识图谱(link 模式)——实体按类型分域归位,cloudflare/github 等中心节点汇聚连接

方法论:图谱建模的常识

治理过程中踩的坑,沉淀成几条建模原则:

  • 主动探索:更新图谱时用常识探索部署位置、多机、依赖、外部托管——不被动等提醒
  • 多实体建模:一个事物多个角色 = 多个独立实体 + 关系。poto 是项目、仓库、域名三个实体
  • 禁止汇总实体:不建"15 个域名"这种汇总——每个域名独立建实体、独立连 cloudflare
  • 域名分类型:解析到主机的(tm.aketer.me→sad)vs Cloudflare 托管的(poto.aketer.me)建模不同
  • 中心实体:cloudflare/github/vaultwarden/easytier 作为汇聚节点,新资源先判断属于哪个中心

0xFF 跋

Seraph 解决了实体提取的根本问题,但还有未完成的事:

  • 信任评分未激活:fact_feedback 反馈闭环设计好了,但依赖使用习惯——每次检索后标记 helpful/unhelpful,评分才会分化,高价值事实才会浮上来
  • LLM 提取仍有噪声:提示词大幅减少,但复杂文本偶尔还会抽出概念词,需要增量治理
  • 实体描述 67% 覆盖:次级实体还没生成描述

下一个可能的方向:让实体描述随关系变化自动更新(现在需要手动刷新),或者把反馈闭环做成自动的——检索命中就隐式加分。

记忆引擎不是一次性项目,它会跟着使用持续进化。这篇文章是它的第一个里程碑。

参考资料

“您的支持是我持续分享的动力”

微信收款码
微信
支付宝收款码
支付宝

目录