知识图谱的人类可读化:从机器数据到可读画像

知识图谱的人类可读化:从机器数据到可读画像

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_entitiesadd_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 呈现给人看。

读写 聚合画像 三行纯文本 ETAPI 写入 编辑事件 提取 查询 ETAPI 查询 Hermes Agent Seraph / fact_store facts · entities · relations LLM 画像生成 【类型】【别名】【说明】 ETAPI 记忆桥 seraph-api + 脚本 画像渲染(同步脚本) 三行 → HTML + #root/ 内链 Trilium 知识图谱 HTML 画像 · ~relation 边

0xFF 跋

这次改造做完,记忆引擎的图谱终于不只是"机器能读",人也看得懂了。实体节点从一段关系描述,变成了有类型、有别名、有说明、能点跳转的画像页。

还没做的:画像生成是 LLM 一次性聚合,后续事实变化后画像不会自动刷新(batch_describe --force 可以重生成,但没有增量刷新);画像质量依赖提示词,个别实体可能生成得一般。这些留给后续迭代。

参考资料

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

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

目录