知识图谱的人类可读化:从机器数据到可读画像※
Hermes 记忆引擎系列 · 第 5 篇
0x00 序※
前四篇记录了记忆引擎的成长:Hermes 记忆引擎实践:当 AI 记忆遇见 Trilium 知识图谱选型了 Holographic 做本地记忆,记忆引擎的自我进化:从正则到 LLM把它改造成了带 LLM 实体提取的 Seraph,记忆引擎的自迭代:从正则审查到增量标记让审查自动化,人机记忆引擎实时通道:从 30 分钟同步到秒级入脑打通了秒级同步。这些解决的是"记忆怎么进、怎么连成图谱、怎么保持干净、怎么实时"。
但今天检查镜像节点时发现一个问题:实体节点的正文是空的。查到最后,真相不是丢了内容——是机器能读和人能看之间差了一层。
原有的实体内容,本质上是三元组关系的描述:实体名、类型、关联事实、关系边。这些是给机器检索和推理用的,不是一段给人读的可视化文本。本篇要做的,就是补上这层"人话"。
0x01 逻辑与架构※
知识图谱的三层数据,各有各的读者:
- facts(事实):一段话,机器检索用
- entities(实体)+ description(画像):实体名 + 自然语言画像
- entity_relations(关系):(来源, 关系, 目标) 三元组,noteMap 画连线靠它
这些对模型来说是完美的——搜索、推理、图谱遍历都能用。但人打开一个实体节点,看到的是:类型、别名、一串关系列表。它回答了"这个节点连接了谁",却没回答"这个东西是什么"。
我要补的,就是这层"人话":让 LLM 把关联事实聚合成人能读懂的画像,再渲染成带结构的 HTML。
0x02 改造过程※
1. 画像三行模板※
让 LLM 按固定格式生成画像,不再是自由发挥的段落:
【类型】服务器 【别名】sad · sad.aketer.me · 38.76.190.84 · 10.126.126.3 · Ciallo 【说明】本机 Arch Linux 服务器,运行 Hermes、Trilium 等核心服务。
类型由实体类型映射成中文,别名让 LLM 从关联事实提炼 IP/域名/用户,说明一句话讲清"它是什么、连着谁"。缺省容忍——LLM 漏行也不崩,同步时按行解析。
2. 新实体自动生成画像※
在 store 层加了个钩子:_link_entities 和 add_relations 里,凡是本次新建的实体,自动调 generate_entity_description 补画像。幂等——已有画像的跳过,没有事实/关系可聚合的跳过。
好处:以后每加一条事实,新实体自动有画像,不用惦记。
存量怎么补?batch_describe.py 加了 --force 参数,把旧的自由文本画像全部重生成成三行模板。sad 494 个实体 / asus 260 个实体,一次跑完,0 失败。
3. Trilium 渲染成 HTML※
同步脚本把三行画像解析成富文本:
<h3>类型</h3><p>服务器</p> <h3>别名</h3><p>sad · sad.aketer.me · ...</p> <h3>说明</h3><p>本机 Arch Linux 服务器,...</p> <h3>关系</h3> <ul> <li>sad 托管 <a class="reference-link" href="#root/...">trilium</a></li> </ul>
关系列表显示完整三元组(来源 → 关系 → 目标),本实体加粗,另一端实体名包成 #root/ 内部链接——点一下直接跳到那个实体的页面。
4. 关键设计决策※
内部链接和属性边,为什么并存不互替:Trilium 里表达关系有两种方式——属性边(~relation,如 sad ~hosts→trilium)和正文里的内部链接。它们服务不同的读者,不冲突:属性边给 noteMap 画连线(机器/图谱层),正文内链给人点(人类阅读层)。而且回流桥会过滤带 #syncKey 的镜像节点,正文里写什么都不会被当成新事实吞回引擎,两层互不干扰。
画像存纯文本,渲染时转 HTML——为什么不直接存 HTML:让 LLM 直接吐 HTML 有风险——格式不稳、可能带危险标签,而且 description 字段一旦存了 HTML,所有消费方(搜索、probe、其他工具)都会看到一堆标签。所以设计是:Seraph 存三行纯文本(权威源),同步脚本渲染时转 HTML(展示层)。数据层保持干净,展示层要什么格式都行。
5. 踩过的坑※
- 半角/全角标记不一致:LLM 输出过 [类型](半角),同步脚本解析【类型】(全角)——最初对不上。修复:提示词统一全角,脚本兼容两种。
- batch_describe 跳过旧画像:它默认只处理空描述实体,177 个旧格式画像没被升级——加 --force 重生成。
- ETAPI content 端点只认 text/plain:公共函数用 application/json 发 PUT 会被拒(500),root 时间戳一直写不进去。修法:content 更新单独走 text/plain 请求。
- 检查"内容没同步"时先分清层级:root 空 ≠ 实体内容空;查实体正文要用 GET /notes/{id}/content,note 对象的 content 字段本来就是 None。
配图:从机器数据到可读画像※
整条链路:机器数据在 Seraph 里,LLM 把它聚合成三行画像,同步脚本渲染成带内链的 HTML,Trilium 呈现给人看。
0xFF 跋※
这次改造做完,记忆引擎的图谱终于不只是"机器能读",人也看得懂了。实体节点从一段关系描述,变成了有类型、有别名、有说明、能点跳转的画像页。
还没做的:画像生成是 LLM 一次性聚合,后续事实变化后画像不会自动刷新(batch_describe --force 可以重生成,但没有增量刷新);画像质量依赖提示词,个别实体可能生成得一般。这些留给后续迭代。
参考资料※
- Hermes 记忆引擎实践:当 AI 记忆遇见 Trilium 知识图谱
- 记忆引擎的自我进化:从正则到 LLM
- 记忆引擎的自迭代:从正则审查到增量标记
- 人机记忆引擎实时通道:从 30 分钟同步到秒级入脑
- Seraph 笔记
- TriliumNext 笔记
- ETAPI 笔记