Hermes Trilium Draw 架构图生成 · Skill+MCP 完全剖析

Hermes Trilium Draw 架构图生成 · Skill+MCP 完全剖析


一、数据流总览:四层管道

图1: 四层管道数据流总览

二、格式链深度解剖 (Format Chain)

2.1 用户意图 → Mermaid → Visual Design

这是最短路径:用户描述架构意图 → Hermes 调用 mcp_drawio_open_drawio_mermaid(mermaid语法) → 浏览器在 draw.io 编辑器中自动打开一个 Mermaid-to-drawio 转换后的草图。

Mermaid 支持的全部 26 种图类型:

  • 流程图graph TD / flowchart LR
  • 时序图sequenceDiagram
  • 类图classDiagram
  • 状态图stateDiagram-v2
  • ER 图erDiagram
  • C4 模型C4Context
  • 架构蓝图architecture-beta
  • ……等

关键认识:Mermaid 只是起点草图——@drawio/mcp 将 Mermaid 转换为 draw.io 可编辑元素后,用户在编辑器中进行视觉精调(拖组件、改颜色、布线、调间距),最终导出。

2.2 导出格式解码:SVG + content 属性的双层结构

draw.io 导出的 SVG(标准 XMLSVG 格式)是一种数据 + 渲染双层格式

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     version="1.1" width="1100px" height="600px"
     viewBox="0 0 1100 600"
     content="&lt;mxfile host=&quot;embed.diagrams.net&quot; ...&gt;
               &lt;diagram name=&quot;Page-1&quot; id=&quot;xxx&quot;&gt;
                 &lt;mxGraphModel&gt;
                   &lt;root&gt;
                     &lt;mxCell id=&quot;0&quot;/&gt;
                     ...
                   &lt;/root&gt;
                 &lt;/mxGraphModel&gt;
               &lt;/diagram&gt;
             &lt;/mxfile&gt;">
  <defs/>
  <g data-cell-id="0">
    <g data-cell-id="1">
      <rect x="30" y="20" width="500" height="600" .../>
      <text ...>服务器面板</text>
    </g>
    <g data-cell-id="2">
      <rect x="50" y="70" width="460" height="50" .../>
      <text ...>Nginx</text>
    </g>
  </g>
</svg>
作用消费者
SVG body(可见层)W3C SVG 1.1 标准矢量图形——矩形、文字、线条、箭头。包含 data-cell-id 属性映射到 mxCell 的 id。CKEditor5 预览、共享页面渲染
content 属性(数据层)HTML 编码的 mxGraph XML(完整 draw.io 文件结构)。包含 <mxfile><diagram><mxGraphModel><root><mxCell> 的完整层级。所有值、样式、几何信息、连线的原始数据。draw.io 编辑器双击打开、编辑后保存

核心设计思想:SVG body 是人类可见的渲染结果,content 属性是机器可读的编辑源数据。两者通过 data-cell-id 属性一一对应。这实现了「预览即渲染图、双击即编辑器」的双重能力。

2.3 格式兼容性

Trilium 使用的格式 = draw.io 标准导出格式(不是 Trilium 私有格式):

  • draw.io 桌面 CLI: drawio -x -f svg --embed-diagram file.drawio
  • draw.io Web UI: 文件 → 导出为 → SVG(保持「嵌入图表数据」打开)
  • draw.io embed API: format: 'xmlsvg'

三种途径的输出可以直接存入 Trilium,无需转换。

2.4 多页图表

一个 SVG 文件中可以包含多个 <diagram> 元素(即多页):

content="&lt;mxfile&gt;
  &lt;diagram name=&quot;当前架构·组件拓扑&quot; id=&quot;current&quot;&gt;...&lt;/diagram&gt;
  &lt;diagram name=&quot;目标架构·解耦后&quot; id=&quot;target&quot;&gt;...&lt;/diagram&gt;
&lt;/mxfile&gt;"

SVG body 只渲染第一个 <diagram>(第 1 页)。Trilium CKEditor5 预览也只显示第 1 页。用户双击进入 draw.io 编辑器后可以看到全部页面并在页面间切换。


三、@drawio/mcp 工具三兄弟

工具入口输入场景
open_drawio_mermaidMermaid 语法流程图/时序图/ER图等文本描述最常用——用户描述意图 → Hermes 写 Mermaid → 浏览器编辑
open_drawio_xmlmxGraph XML完整的 draw.io XML 数据编辑现有 Trilium 图(extract-xml.py 提取)
open_drawio_csvCSV 表格结构化表格数据组织结构图/ER 图从表格生成

所有三个工具都有共同的辅助参数:

  • lightbox:只读模式(禁止编辑)
  • darkMode:深色模式

执行机制:@drawio/mcp 在宿主机(Hermes Docker 容器)上打开一个 Chrome/Chromium 浏览器标签页,导航到 embed.diagrams.net 并传入数据。用户在浏览器中编辑后,通过「导出为 SVG」获得文件。


四、Trilium 原生 draw.io 笔记的三要素

要让一张 svg 文件成为 Trilium 中可双击编辑的 draw.io 原生笔记,必须满足三个条件:

TBD: 需要生成三要素的单图

为什么是这三个条件?

  • type=image:Trilium 将 note 视为图片资源,可以在 CKEditor5 中通过 <img src="api/images/{id}/{title}"> 引用。不允许用旧版 type='file'——那会导致 1×1px 占位符。
  • mime=image/svg+xml:告诉 Trilium 这是一个 SVG 格式的矢量图,而非普通图片。
  • 标题以 .drawio.svg 结尾:触发 Trilium 的 draw.io 插件识别。插件扫描笔记树,找到所有标题以 .drawio.svg 结尾且 type=image 的笔记,在双击时打开 draw.io 编辑器(而非 Trilium 内置图片查看器)。

三要素的协作验证

Trilium 检出标题 → ".drawio.svg" 结尾?
  ├─ 否 → 图片查看器
  └─ 是 → 检查 type=image + mime=image/svg+xml?
        ├─ 否 → 提示格式不兼容
        └─ 是 → 从 content 属性提取 mxGraph XML
              → 调用 draw.io widget API 打开编辑器
              → 编辑器加载完整图表(所有页面、样式、连线)
              → 用户编辑后保存 → Trilium 接收新 SVG 写入笔记

五、模板机制与 Widget 插件

5.1 两层结构

┌─ draw.io 插件笔记 (type=code, mime=application/javascript;env=frontend)
│  ├── #widget 标签 → 声明这是前端插件
│  └── #run=frontendStartup → 在 Trilium 启动时加载
│
├── 模板笔记 (子笔记) ⬅ #widget 的子笔记
│  ├── type=image, mime=image/svg+xml
│  ├── 标题 = 任意.svg
│  ├── #template → 声明这是一个模板
│  └── #originalFileName=drawio.svg → 声明文件类型
│
└── 用户创建的 draw.io 笔记(引用模板)
   ├── template relation → 模板笔记 ID
   └── 继承模板的预览样式和编辑能力

5.2 模板 = 新笔记的蓝本

当 draw.io 笔记设置了 template relation 指向模板笔记时:

  • 双击编辑时,编辑器从 content 属性加载数据
  • 如果 content 属性为空(新建空图),Trilium 会从模板笔记复制初始 SVG 内容作为起点
  • 模板可以包含预设的样式默认值(画布尺寸、颜色主题等)

5.3 Widget 插件的生命周期

① Trilium 启动 → 扫描 #widget 标签
② 加载 JS 插件到前端
③ 插件注册 draw.io 编辑器入口:
   监听笔记双击事件 → 检查三要素 → 匹配时拦截默认行为
   打开 draw.io 编辑器 iframe
④ 保存时:插件从 iframe 取得新 SVG → 通过 Trilium API 写入笔记

六、imageLink 嵌入模式详解

6.1 为什么需要 imageLink?

在 Trilium 中,draw.io 子笔记是一个独立的图片资源。要在母笔记的 HTML 内容中显示这张图,需要两个步骤:

  1. 元数据关联:在母笔记上创建 imageLink relation,指向 draw.io 子笔记。这建立了「母笔记包含此图片」的语义关系。
  2. HTML 引用:在母笔记的 HTML 内容中添加 <img> 标签,通过 Trilium 的内部图片 API 加载。

6.2 完整嵌入步骤

① 创建 draw.io 子笔记
   └── 得到 noteId = "gm4DzEp30gcq"

② 在母笔记上设置 imageLink relation
   mcp_trilium_manage_attributes(
     action="create",
     noteId="母笔记ID",
     type="relation",
     name="imageLink",
     value="gm4DzEp30gcq"
   )

③ 在母笔记 HTML 中插入 img 标签
   <img src="api/images/gm4DzEp30gcq/架构图·系统拓扑.drawio.svg"
        style="max-width: 100%; height: auto;" />

④ 可选:设置 template relation
   mcp_trilium_manage_attributes(
     action="create",
     noteId="gm4DzEp30gcq",
     type="relation",
     name="template",
     value="U8Z4DeGS5V7T"  // 模板笔记 ID
   )

6.3 API 路径语义

api/images/{noteId}/{title} 是 Trilium 内部图片服务端点:

  • {noteId}:draw.io 子笔记的 ID
  • {title}:实际输出时可以是任意字符串(Trilium 主要用 noteId 定位)
  • 返回:SVG 文件的原始内容(Content-Type: image/svg+xml)

6.4 共享页面的渲染

共享页面(#shareAlias)中的 api/images/{id}/{title} 路径会被 Trilium 的 ETAPI 代理正确渲染。因此设置了 #shareAlias 的母笔记,其中的 draw.io SVG 图片在共享页上也会正常显示。


七、三种工作流的深度对比

7.1 创建新图(从零)

用户: "画个系统架构图"
   │
   ▼
① Hermes 调用 open_drawio_mermaid("graph TD; A-->B")
   → 浏览器打开 draw.io 编辑器
   → 用户拖拽布局、调颜色、布线
   → 导出 SVG → 发回聊天
   │
   ▼
② Hermes 用 MCP API 创建笔记:
   mcp_trilium_create_note(
     parentNoteId="...",
     title="系统架构.drawio.svg",     ← 三要素①
     type="image",                    ← 三要素②
     mime="image/svg+xml",            ← 三要素③
     content=svg_content
   )
   │
   ▼
③ Hermes 设置关联:
   - template relation → 模板笔记
   - imageLink relation → 母笔记
   - 母笔记 HTML 插入 <img>
   │
   ▼
④ 用户在 Trilium 中双击 → draw.io 编辑器打开 → 可再次编辑

7.2 编辑已有图

用户: "改一下架构图"
   │
   ▼
① Hermes 读取笔记 SVG 内容
   │
   ▼
② 从 SVG 的 content 属性提取 mxGraph XML
   extract-xml.py: re.search(r'content="([^"]*)"', svg)
                   → unescape HTML entities → mxGraph XML
   │
   ▼
③ Hermes 调用 open_drawio_xml(xml=mxGraph_xml)
   → 浏览器打开编辑器,加载现有图表
   → 用户在 draw.io 中修改
   → 导出 SVG → 发回聊天
   │
   ▼
④ Hermes 用 MCP API 更新笔记
   mcp_trilium_update_note(
     noteId="...",
     content=new_svg_content     ← 新 SVG(保持旧标题/关联不变)
   )

7.3 导入 .drawio 文件

用户: 上传了一个 diagram.drawio 文件
   │
   ▼
① Hermes 处理:
   make-trilium-svg.py diagram.drawio > note.svg
   原理: 读取 .drawio 的 mxGraph XML
         → 创建最小 SVG shell
         → 将 XML 写入 content 属性
   │
   ▼
② 同创建流程的②③④步:
   创建 Trilium 笔记 + 设关联

八、编码链陷阱(最易出错的环节)

8.1 编码层次

从 mxGraph XML 到最终存储在 Trilium 中的 SVG content 属性,经过了 三层编码嵌套

原始 mxGraph XML:
  <mxCell id="1" value="Trilium\nDocker" style="..." />
      │
      ▼  XML-SAX escape + HTML entity encoding
  content 属性(单行 HTML 编码):
  &lt;mxCell id=&quot;1&quot; value=&quot;Trilium\nDocker&quot; style=&quot;...&quot; /&gt;
      │
      ▼  SVG XML 中的属性值
  <svg content="&amp;lt;mxCell id=&amp;quot;1&amp;quot; ...">
      │
      ▼  存储到 Trilium SQLite

每一层 decode 都有可能出错。如果任何一层多 decode 或少 decode,就会出现 "非绘图文件""Unescaped '<' not allowed" 错误。

8.2 \n vs <br> 编码陷阱

这是经过实战验证的最常见问题:

方式style 中value 中编码复杂度结果
❌ html=1 + <br>html=1Trilium&lt;br&gt;Docker三重编码极易在某层解码出错
✅ 无 html=1 + \n不含 html=1Trilium\\nDocker (字面反斜杠+n)零编码问题可靠稳定

原理\n 是两个安全 ASCII 字符(反斜杠 + 小写 n),在 XML 属性值中不需要转义。而 <br> 是 HTML 标签,在 XML 属性中必须被转义为 &lt;br&gt;,经过多层嵌套后编码链极易断裂。

关键事实:即使用 \n 模式创建笔记,用户在 draw.io 编辑器中双击保存一次后,Trilium 会自动将格式转换为 html=1 + <br>。这是因为 draw.io 编辑器自己的序列化引擎偏好 html=1。但这没关系——因为编辑过程在 draw.io 内部完成,编码链由 draw.io 自己负责。只要初始创建时用 \n 避开编码陷阱即可。

8.3 其他格式要求

要求正确值错误代价
host 属性embed.diagrams.net保存失败
User-Agent真实浏览器 UA(如 Mozilla/5.0 ...保存可能失败
content 属性单行(无 &#xA;、无 <?xml?>"非绘图文件" 错误
data-cell-id每个 SVG <g> 必须有对应 mxCell id编辑器显示旧内容
页面数SVG body + content 中 exactly 1 页(或多页但只有第1页渲染)预览≠编辑器
SVG body 有实际图形含 <rect><text><line><polygon> 等空白预览

九、手写生成模式(Python 脚本)

9.1 适用场景

当需要批量生成、程序化生成或一致性高的架构图时,可用 Python 脚本在 /tmp/ 中手写 mxGraph XML 并包装为 Trilium SVG。这种方式在 @drawio/mcp 出现之前是主力方式,现在由 @drawio/mcp + 人工设计替代。但理解此方式有助于深刻理解格式本质。

9.2 关键数据结构

mxCell 层级树:
  id="0"      ← 根节点(root),所有节点的 parent
    id="1"    ← 画布节点(mxCell parent=0, 无几何)
      id="2"  ← 实际图形(parent=1, 有几何+样式+值)
      id="3"
        ...

几何表示:
  mxGeometry x="30" y="20" width="500" height="600"
  as="geometry"(顶点/容器)
  as="geometry" relative="1" + sourcePoint + targetPoint(连线)

连线(edge):
  edge="1"
  mxGeometry relative="1"
    mxPoint as="sourcePoint"(起点)
    mxPoint as="sourcePoint"(中途点,可多个)
    mxPoint as="targetPoint"(终点)

9.3 容器嵌套(视觉 vs 逻辑)

⚠️ 重要区别:mxGraph 的父容器关系有两种:

  • 逻辑父容器mxCell parent="id"):决定图的层级结构和组织方式。所有元素最终父容器为 id="1"(画布)。
  • 视觉父容器:子元素在父元素框内展示,通过 <mxGeometry x="相对偏移" y="..."> 实现。但 mxCell 的 parent 仍指向 id="1"。

容器面板和内部组件在 mxGraph 中不设真正的 parent-child 关系——所有 mxCell 的 parent 都设为 "1",只靠坐标位置(x,y)落在面板区域内部来实现视觉包含。这样做避免了 ID 索引和层级解析的复杂性。

9.4 多页生成架构

每个 <diagram> 元素 = 一页
每个 <diagram> 包含完整的 <mxGraphModel> → <root>
所有 <diagram> 包裹在 <mxfile> 中

结构:
<mxfile host="embed.diagrams.net" ...>
  <diagram name="当前架构" id="page1">
    <mxGraphModel>
      <root>
        <mxCell id="0"/>
        <mxCell id="1" parent="0"/>
        <mxCell id="2" parent="1" .../>
      </root>
    </mxGraphModel>
  </diagram>
  <diagram name="目标架构" id="page2">
    ...(mxCell id 继续自增,不重置)
  </diagram>
</mxfile>

SVG body 只渲染第一个 <diagram>。用户双击进入编辑器后可看到所有页面。


十、与字符架构图的并行关系

Trilium 中有两种架构图绘制风格,各有优劣且互不依赖:

维度Draw.io SVG 架构图字符架构图(Unicode 框线)
渲染方式W3C SVG 矢量图形<pre><code> 中的 Unicode 框线字符
表达能力无限——颜色、渐变、虚线、圆角、箭头样式、图标有限——单色、框线 + 文字、无图形
可编辑性双击进入 draw.io 编辑器直接修改 HTML 源码
跨设备一致性完全一致(SVG 是精确渲染)受字体影响(CJK 对齐需要精准计算宽度)
生成方式@drawio/mcp 浏览器编辑器 + 导出 SVGPython vw()/pad() 计算 + 断言验证
生成复杂度低(用户拖拽)高(需程序化计算每行视觉宽度)
共享页兼容性好(SVG 由浏览器原生渲染)受 shareCss 的 font-mono 配置影响
适合场景系统架构、拓扑、部署图、流程图简单的流程/层级结构、代码随附文档
技能参考trilium-draw-architecture-flowcharttrilium-architecture-diagram-conventions

选择建议

  • 展示给他人(共享页面、报告)→ draw.io SVG,专业美观
  • 随代码快速记录思路 → 字符架构图,无需离开编辑器
  • 需要精确颜色/图标 → draw.io SVG
  • 纯文本可搜索/可嵌入代码注释 → 字符架构图

十一、运维与排障体系

11.1 完整验证清单

创建后验证:
  [✓] 笔记 type=image, mime=image/svg+xml
  [✓] 标题以 .drawio.svg 结尾
  [✓] template relation → 模板笔记
  [✓] imageLink relation → 母笔记
  [✓] 母笔记 HTML 中有 <img src="api/images/{id}/{title}">
  [✓] CKEditor5 预览有实际图形(非空白)
  [✓] 双击子笔记 → 打开 draw.io 编辑器
  [✓] 编辑后保存 → 图片更新
  [✓] 共享页面 → 图片正常渲染

11.2 故障诊断矩阵

现象根因解决
CKEditor5 预览空白SVG body 无实际 <rect>/<text> 等渲染元素用 draw.io 导出替代手写
有矩形框无文字旧版手动生成缺 text 元素重新导出(draw.io 自动生成完整 SVG body)
"非绘图文件" 错误content 属性格式不符:host 不是 embed.diagrams.net、含 xml? 头、多行编码确保 content 单行、host 正确、无 xml 头
双击打开编辑器但内容空/旧data-cell-id 不匹配 / html=1 编码链断裂检查 data-cell-id 与 mxCell id 对应;用 \n 替代 br
共享页图片断裂shareCss 的 font-mono 覆盖 <pre> 字体从 shareCss 删除 font-family: var(--font-mono)
编辑保存后不更新浏览器缓存旧 SVGCtrl+Shift+R 强刷新
母笔记不显示图片缺少 imageLink relation 或 <img src> 路径错误检查 relation + HTML 中的 noteId 正确

十二、系统架构图:三层容器层次模式

12.1 模式定义

针对多服务器、多 Docker 容器的架构展示场景,定义了「服务器 → Docker 容器 → 程序内组件」三层的视觉风格:

视觉样式颜色方案边框
服务器大虚线面板#e8eaf6 浅紫背景, #7986cb 紫边框dashed=1, rx=8
Docker 容器中实线面板#e0f2f1 浅绿背景, #4db6ac 绿边框rx=8
程序内组件小实线框#f5f5f5 灰背景, #9e9e9e 灰边框rx=8

12.2 布局策略

画布: 1100×650px

左栏 (ghost.atibm.com):
  ├── 服务器面板: x=30, y=20, w=500, h=600
  │   ├── Nginx 代理: x=50, y=70, w=460, h=50
  │   ├── Trilium Docker: x=50, y=150, w=460, h=260
  │   │   ├── 组件 A: 190×40px @ x=65
  │   │   └── 组件 B: 190×40px @ x=270
  │   └── ...

右栏 (arm.atibm.com):
  ├── 服务器面板: x=570, y=20, w=500, h=600
  │   ├── Hermes Docker: x=590, y=70, w=460, h=310
  │   └── ...

组件间连线:
  ── 正交折线 (edgeStyle=orthogonalEdgeStyle)
  ── 颜色: 蓝=MCP, 橙=gbrain, 紫=Telegram, 绿=反向代理
  ── 标签在连线中点

十三、工具生态总结

13.1 Hermes 侧的辅助脚本

脚本路径功能
make-trilium-svg.pyskills/.../scripts/.drawio 文件 → Trilium SVG
extract-xml.pyskills/.../scripts/Trilium SVG → mxGraph XML(编辑用)

13.2 涉及的服务

服务角色通信方式
Hermes Agent编排层——加载 skill、调用 MCP、处理 SVG、写入 Trilium本地进程
@drawio/mcpMCP 服务器——打开浏览器编辑器stdin/stdout JSON-RPC
draw.io (浏览器)图形编辑引擎——用户设计 SVG 导出HTTP (embed.diagrams.net)
Trilium (Docker)知识存储——SVG + mxGraph XML → SQLiteHTTP ETAPI / MCP API
trilium-mcp-serverMCP 网关——统一 API 访问层stdin/stdout JSON-RPC

13.3 完整绘制流程总结

描述意图
  └─→ Hermes 写 Mermaid 脚本
       └─→ @drawio/mcp 打开浏览器编辑器
            └─→ 用户在 draw.io 中设计精修
                 └─→ 导出 SVG → 发回聊天
                      └─→ Hermes 用 MCP API 创建 Trilium 笔记
                           ├─ type=image, mime=image/svg+xml, 标题.drawio.svg
                           ├─ template relation → 模板笔记
                           ├─ imageLink relation → 母笔记
                           └─ 母笔记 HTML 添加 <img>
                                └─→ 双击可编辑 → 保存即更新

十四、附录:相关笔记与技能

  • 技能trilium-draw-architecture-flowchart — 通过 @drawio/mcp 创建/编辑 draw.io 笔记
  • 技能trilium-architecture-diagram-conventions — 字符架构图规范(并行选项)
  • 参考container-hierarchy-pattern.md — 三层容器层次架构图模式(服务器→Docker→组件)
  • 参考drawio-encoding-debug-transcript.md — 编码链调试记录(\n vs <br> 陷阱定位过程)
  • 参考drawio-architecture-diagrams-example.md — 手写生成示例(多页、连线模式、MCP API 写入)

本文编写于 2026-06-02。