Obsidian人物关系白板:从Markdown笔记自动生成Canvas关系图谱 📅 发布时间:2026/9/2 19:44:37 👁 浏览次数: 写小说经常遇到一个痛点人物一多就乱谁和谁是青梅竹马、谁和谁反目成仇、哪个角色是中期才登场的笔记里写了一大堆真到要用的时候翻半天。这次我们来看一个 Obsidian 效率插件场景从 Markdown 笔记里自动解析人物关系一键生成可拖拽的关系白板。小说设定党、剧本写作者、长篇知识管理用户都能直接从这套流程里拿到好处。这个方案的核心价值不是“画图”而是让笔记本身成为关系图谱的唯一数据源。你只需要在对应标题下写清楚人物名和关系描述然后用插件或脚本扫描 Markdown就能得到一张基于 Obsidian Canvas 的互动白板。最值得关注的能力有三个一是无需改变现有写作习惯依然在 Markdown 里记录二是输出是可编辑的 Canvas 文件节点可以继续拖拽调整不会只生成一张死图三是支持重复执行笔记更新后重新生成即可适合设定反复修改的场景。本文会带你走一遍完整流程先讲核心能力与适用边界再给环境准备和安装部署步骤然后设计一组功能测试来验证解析效果最后补充批量任务、接口扩展、性能观察、常见问题和最佳实践。如果你正在维护一套人物复杂的长篇设定库这篇文章值得收藏备用。1. Obsidian 人物关系白板插件核心能力速览能力项说明项目类型Obsidian 插件或辅助脚本基于 Markdown 笔记解析并输出 Canvas 关系白板主要功能从 Markdown 标题、列表、标签中提取人物名和关系描述生成可视化关系图输出格式Obsidian Canvas 文件.canvasJSON 结构启动方式Obsidian 插件命令面板运行或独立脚本执行后导入 Obsidian支持平台Windows / macOS / Linux 均可只要 Obsidian 能运行是否支持 API插件模式通常走 Obsidian 内部命令脚本模式可扩展为本地 HTTP 服务是否支持批量任务支持可扫描整个 vault 目录批量解析多篇笔记显存 / 性能要求普通 CPU 即可运行解析速度取决于笔记体量适合场景小说设定管理、剧本人物关系梳理、系列文章人物图谱、知识库关系可视化这里需要提前说明Obsidian 社区插件市场中并没有一个固定名称的“官方人物关系解析插件”。实际操作中你可以选择社区里具备 Canvas 生成能力的图谱类插件也可以用本文提供的解析脚本来实现同等效果。后者原理清晰、可控性更强适合理解核心逻辑后再决定是否接入现成插件。本文后续给出的命令、脚本和 JSON 结构都是通用模板实际应用时需按项目路径和文件名替换。2. 适用场景与使用边界这类工具适合谁首先是小说设定党。一本长篇里角色动辄几十个每个角色有自己的身份、阵营、和主角的关系单纯靠文字记录很难一眼看出整体结构。用关系白板后人物之间是盟友、敌对、师生还是恋人直接以节点和连线呈现构思剧情时扫一眼就能定位。其次是剧本创作者和系列文章作者。剧本里的角色关系往往随着剧情推进发生变化Markdown 笔记天然适合记录这种历史版本脚本重新生成 Canvas等于给每个阶段性关系做了一次快照。知识库用户也可以利用类似思路把技术概念、项目模块、人员职责整理成关系图并不局限于小说人物。不适合的场景同样明确。第一如果你需要的是精确的图谱分析比如计算网络密度、识别关键节点Canvas 白板不是图数据库不适合做复杂关系计算。第二如果你追求专业级的排版美化Canvas 的手动布局能力有限发布用的正式关系图建议用专业绘图工具。第三如果笔记里根本没有结构化的人物描述解析脚本也只能靠标题和列表推断准确率会明显下降。还要提醒一点合规边界。如果笔记内容涉及真实人物的姓名、隐私信息、企业内部人员关系生成关系图后不要把文件随意公开。小说角色、自创人物不受影响但任何涉及真实个人数据的场景都要先确认授权并控制访问范围。批量扫描时也建议只处理自己的笔记目录避免读取他人文件。3. 本地环境准备与前置条件先整理一下运行这套方案需要准备什么。3.1 操作系统与 Obsidian 版本Obsidian 桌面版需要安装好Windows、macOS、Linux 均可。Canvas 功能在较新版本中已经是内置能力打开设置确认一下核心插件列表里能看到 Canvas 即可。更稳妥的做法是对照自己正在用的 Obsidian 版本在官方更新记录中确认 Canvas 是否为内置功能如果没有就升级到最新稳定版。3.2 插件与脚本运行环境如果走插件方式需要确保 Obsidian 允许加载第三方社区插件也就是要在“设置 → 第三方插件”中关闭安全模式并确认社区市场入口可用。国内网络环境下访问社区市场可能不稳定备选方案是手动下载 release 压缩包放入插件目录。如果走脚本方式需要准备一个能运行 Python 3 的环境。Python 不是 Obsidian 必须的但解析脚本、批量扫描目录、启动本地 API 服务都要用到。Windows 用户可以直接用命令行macOS 和 Linux 用户用系统自带终端即可。3.3 Obsidian Vault 与插件目录结构理解插件目录结构很重要。Obsidian 的每个 vault 是一个独立文件夹插件放在.obsidian/plugins/下。手动安装时新插件应该有一个独立子目录目录下包含main.js、manifest.json和styles.css三个文件具体文件名以插件发布包为准。脚本方式则不需要往 vault 里塞文件把脚本放在随便哪个工作目录都行但建议和笔记库保持分离避免脚本被 Obsidian 索引造成不必要的同步负载。3.4 笔记结构约定解析脚本能正常工作依赖你对 Markdown 笔记做一点最小约定。我建议在人物笔记中使用固定标题层级例如# 角色设定 ## 林晚 - 身份天枢阁少阁主 - 与苏禾青梅竹马 - 与陈妄师兄弟后反目 ## 苏禾 - 身份南疆药宗传人 - 与林晚青梅竹马 - 与顾沉舟医患关系这样脚本就能根据##标题提取人物名根据与某某关系描述提取关系边。如果你习惯用###或者人物名后面加冒号也可以调整正则规则适配核心是让笔记有规律可循。4. 安装部署与启动方式4.1 插件方式从社区市场安装如果要在 Obsidian 中使用现成插件步骤如下打开 Obsidian进入“设置 → 第三方插件”。关闭安全模式点击“浏览”进入社区插件市场。搜索关键词canvas、relationship或graph查看插件简介是否支持从 Markdown 解析生成 Canvas。点击安装安装后回到已安装插件列表启用。在命令面板中输入插件名称查看是否出现“生成人物关系白板”等命令。注意具体插件名和命令名以你实际安装的版本为准。如果社区市场无法访问走手动安装。4.2 手动安装插件包手动安装是更通用的兜底方案。假设你下载到了名为relationship-canvas.zip的插件包# 进入 vault 的插件目录 cd /path/to/your/vault/.obsidian/plugins/ # 解压插件包得到独立目录 unzip relationship-canvas.zip -d relationship-canvas解压后目录结构大致如下.vobsidian/plugins/relationship-canvas/ ├── manifest.json ├── main.js └── styles.css然后回到 Obsidian在“已安装插件”列表中启用它。如果列表里没出现重启 Obsidian 后再看。插件版本更新时下载新压缩包覆盖旧目录即可注意先备份自己的配置文件。4.3 脚本方式Python 解析脚本作为核心引擎插件的实质是解析 Markdown 并生成 Canvas 文件。你可以先用一个简单脚本验证整个流程能不能跑通。下面是一套最小可运行模板原理是读取指定.md文件提取人物和关系输出.canvasJSON 文件# -*- coding: utf-8 -*- # generate_relationship_canvas.py import json import re import sys from pathlib import Path def parse_markdown(md_text: str): characters [] edges [] current_char None lines md_text.splitlines() for line in lines: # 匹配 ## 人物名二级标题 m re.match(r^##\s(.)$, line.strip()) if m: current_char m.group(1).strip() characters.append({name: current_char, desc: }) continue # 匹配 - 与某某关系描述 if current_char is not None: rel re.match(r^-\s*与(.?)(.)$, line.strip()) if rel: target rel.group(1).strip() desc rel.group(2).strip() edges.append({ source: current_char, target: target, desc: desc }) return characters, edges def build_canvas(path: Path): md_text path.read_text(encodingutf-8) characters, edges parse_markdown(md_text) nodes [] node_ids {} x 100 y 100 for idx, ch in enumerate(characters): node_id fchar-{idx} node_ids[ch[name]] node_id nodes.append({ id: node_id, type: text, x: x, y: y, width: 160, height: 60, text: ch[name], color: 1 }) x 220 if x 1000: x 100 y 120 edges_out [] for idx, edge in enumerate(edges): if edge[source] in node_ids and edge[target] in node_ids: edges_out.append({ id: fedge-{idx}, fromNode: node_ids[edge[source]], fromSide: right, toNode: node_ids[edge[target]], toSide: left, label: edge[desc] }) return { nodes: nodes, edges: edges_out } if __name__ __main__: input_md Path(sys.argv[1] if len(sys.argv) 1 else ./test.md) output_canvas input_md.with_suffix(.canvas) data build_canvas(input_md) output_canvas.write_text(json.dumps(data, ensure_asciiFalse, indent2), encodingutf-8) print(f生成完成{output_canvas})运行方式python generate_relationship_canvas.py ./notes/characters.md脚本运行后会在同目录生成characters.canvas在 Obsidian 中打开这个文件就能看到白板。上面的脚本只处理单个文件批量扫描能力请看第 6 节。4.4 启动服务先把命令跑通不管走哪种方式第一次成功运行的标准是Obsidian 中能看到生成的 Canvas 文件打开后节点和连线出现在白板里。如果文件生成了但白板空白优先检查 Markdown 是否符合解析规则比如人物名是否用了二级标题、关系行是否使用了- 与某某描述的格式。5. 功能测试与效果验证5.1 基础解析测试先准备一份最小测试笔记test.md内容如下# 角色设定 ## 沈砚 - 身份北境世子 - 与姜黎婚约关系 ## 姜黎 - 身份镇国公府嫡女 - 与沈砚婚约关系 - 与陆离敌对关系 ## 陆离 - 身份江湖暗探 - 与姜黎敌对关系用脚本解析后会得到 3 个节点和 3 条连线。测试结果正确时Obsidian Canvas 中沈砚和姜黎之间有一条双向连线姜黎和陆离之间也有一条连线连线标签分别显示相应关系。判断是否成功的关键标准是关系描述是否完整落到连线上而不是把描述写进了节点文本。常见失败原因人物名重复比如两个## 林晚会导致节点 ID 覆盖关系行写成了- 沈砚和姜黎婚约但脚本只识别与某某开头标题用了###而脚本默认匹配##。遇到这些问题优先统一笔记格式再考虑改正则。5.2 多人物复杂关系测试写完两组人物后试着加入第三个、第四个人物并在一个人物条目下写多个关系。这个测试是为了验证节点坐标布局会不会重叠、连线会不会混乱。脚本里的坐标递增逻辑比较简单超过节点数量后会自动换行但如果你用的是现成插件布局算法不一定可控。实际测试时重点看两点一是所有人物是否都出现在画布中二是互相引用的人物之间连线是否成对出现。5.3 Markdown 标题和列表格式兼容性测试Obsidian 里很多用户的笔记结构并不统一。有人用##表示人物有人用###还有人直接在段落里写“xxx和yyy是师徒关系”。建议准备一份覆盖不同写法的测试文件分别验证二级标题 列表项关系描述二级标题 段落文字中的关系句带有标签、时间戳、代码块的笔记内容如果脚本只支持列表项那么段落形式的描述会被忽略这不算程序错误而是规则限制。测试的意义在于让你清楚知道自己该用哪种笔记格式而不是反过来迁就解析器。5.4 生成 Canvas 后重新布局测试Obsidian Canvas 生成后是 JSON 数据文件节点坐标可以直接改。用脚本生成的白板节点间距通常比较机械这时你可以在 Canvas 中手动拖拽整理。更理想的做法是把坐标抽成配置项比如固定列数、行间距脚本重新生成时自动套用。6. 批量任务与接口 API 扩展单文件解析只是第一步。真实场景里小说设定库通常散落在多个 Markdown 文件里或者一个大型characters.md里包含几十个人物。批量任务和接口能力决定了这套方案能不能和自己现有的工作流接上。6.1 批量扫描目录批量任务的核心是遍历 vault 目录下所有.md文件然后对每个文件执行解析并生成对应的.canvas文件。可以用下面这个通用脚本扩展# batch_generate.py import json from pathlib import Path from generate_relationship_canvas import build_canvas vault_path Path(/path/to/your/vault) notes_dir vault_path / notes output_dir vault_path / 白板输出 output_dir.mkdir(exist_okTrue) for md_file in notes_dir.glob(*.md): canvas_data build_canvas(md_file) out_file output_dir / (md_file.stem .canvas) out_file.write_text( json.dumps(canvas_data, ensure_asciiFalse, indent2), encodingutf-8 ) print(f已生成{out_file})批量任务需要注意三点。第一只扫描你明确需要解析的目录不要对整个 vault 无差别读取否则输出文件会爆炸。第二输出目录和输入目录最好分开避免二次解析.canvas文件。第三添加日志记录哪个文件成功、哪个失败、为什么失败排查速度会快很多。批量任务不是多线程跑就一定快。解析脚本是轻量操作瓶颈基本在磁盘 IO 和 JSON 序列化上。对几百个文件量级来说单线程完全够用不需要引入额外队列。6.2 本地 HTTP 接口调用如果想把解析能力分享给团队或接入自动化流程可以把脚本包装成本地 HTTP 服务。用 Python 标准库http.server就能实现不需要引入大型框架。下面是一个最小示例# api_server.py import json import sys from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.parse import urlparse from pathlib import Path from generate_relationship_canvas import build_canvas class Handler(BaseHTTPRequestHandler): def do_POST(self): if urlparse(self.path).path ! /api/generate: self.send_response(404) self.end_headers() return length int(self.headers.get(Content-Length, 0)) body json.loads(self.rfile.read(length).decode(utf-8)) md_path Path(body.get(path, )) if not md_path.exists(): self.send_response(404) self.end_headers() self.wfile.write(b{\error\: \file not found\}) return canvas_data build_canvas(md_path) output_path md_path.with_suffix(.canvas) output_path.write_text( json.dumps(canvas_data, ensure_asciiFalse, indent2), encodingutf-8 ) self.send_response(200) self.send_header(Content-Type, application/json; charsetutf-8) self.end_headers() self.wfile.write(json.dumps({ canvas_path: str(output_path), node_count: len(canvas_data[nodes]), edge_count: len(canvas_data[edges]) }, ensure_asciiFalse).encode(utf-8)) if __name__ __main__: server HTTPServer((127.0.0.1, 8765), Handler) print(API 服务已启动http://127.0.0.1:8765) server.serve_forever()启动python api_server.py调用示例curl -X POST http://127.0.0.1:8765/api/generate \ -H Content-Type: application/json \ -d {path: ./notes/characters.md}这个接口只绑定本机 127.0.0.1不暴露到局域网安全性相对可控。如果要在团队内使用建议增加访问令牌校验并且明确限制可访问的目录范围避免任意文件被读取和写入。若不需要外部调用单机使用完全可以不开 HTTP 服务直接跑脚本反而更稳定。7. 资源占用与性能观察这类解析脚本不是重负载任务但放在 Obsidian 里跑时还是有几个资源位置值得观察。7.1 观察方法启动 Obsidian 后打开系统任务管理器观察 Obsidian 进程的 CPU 和内存占用。解析脚本运行时命令行窗口会短暂出现 CPU 上涨如果解析几十个文件总耗时通常也就是几秒到十几秒。生成大型 Canvas 文件后Obsidian 打开它时的内存占用会上升因为要加载所有节点和连线数据。用脚本模式CPU 消耗集中在正则匹配和 JSON 序列化用插件模式性能取决于插件实现方式。没有具体插件源码前不要假设它一定优化得很好。最好的办法是拿自己的真实笔记库做一次压测观察启动时间、生成时间、Canvas 打开时间三项指标。7.2 笔记体量对性能的影响Markdown 文件本身很小但有几个因素会让解析变慢单个文件人物数量超过 100 人Canvas 节点和坐标计算量线性增长。关系描述非常长连线标签渲染压力增大。文件编码不一致脚本按 UTF-8 读取时遇到 GBK 编码文件可能直接报错。降低性能压力的手段也很直接。批量任务改为分目录处理每次只解析最近修改的文件Canvas 里不要塞过多无关节点只保留人物名和关系具体设定文字仍放在原笔记里超大型设定库拆成多本笔记按卷、章节或阵营拆分。7.3 进程残留与端口冲突脚本模式跑完就退出基本不会残留进程。但 API 服务方式要特别注意关闭终端窗口时Python 服务进程有可能还被系统保留占着 8765 端口下次启动时报Address already in use。这时候先用命令查端口占用再结束进程# Windows PowerShell netstat -ano | findstr :8765 taskkill /PID pid /F # macOS / Linux lsof -i :8765 kill -9 pid这个排查方法同样适用于其他本地服务属于通用技巧。8. 常见问题与排查方法问题现象可能原因排查方式解决方案脚本生成后 Obsidian 找不到 Canvas 文件输出目录不对或文件后缀不是 .canvas检查脚本输出路径和文件后缀将输出目录设置在 vault 内任何文件夹Obsidian 会自动索引Canvas 打开后只有节点没有连线人物名匹配失败或关系行格式不对打开生成的 .canvas 文件观察 edges 数组是否为空统一关系行格式为- 与某某描述或者修改正则Markdown 中中文显示乱码文件编码不是 UTF-8检查文件编码把笔记另存为 UTF-8 编码批量任务卡在某一个文件不结束该文件包含异常字符或超大文本查看脚本日志定位到具体文件单独处理该文件修复内容格式插件市场搜不到相关插件社区市场网络不稳定或关键词不匹配换关键词搜索或改用手动安装使用 release 压缩包手动安装打开大型 Canvas 卡顿节点和连线太多在 Obsidian 中观察内存占用拆分笔记减少单张白板节点数API 服务启动端口被占用上一次进程未退出用 netstat/lsof 查端口结束旧进程或换一个端口生成的关系图和笔记内容不一致笔记更新后忘记重新生成确认运行时间和文件修改时间把重新生成加入笔记保存后的自动化流程排查思路有个原则先确认输入再检查脚本最后看输出。输入文件格式是否符合约定这是最容易被忽略的一环脚本是否有异常信息命令行窗口的报错能直接定位问题输出 Canvas 的 JSON 结构是否正确决定了 Obsidian 端能否正常渲染。三步走下来大部分问题都能快速收敛。9. 最佳实践与使用建议9.1 规范笔记结构从源头减少解析错误解析效果的上限由笔记结构决定。建议在人物设定库中固定一套格式人物用##标题人物属性用普通列表关系描述统一写成- 与某某关系描述。规范的笔记不止对生成白板有利对后续全文检索、标签管理、多文件关联都有好处属于一次规范、长期受益的工作。9.2 保留最小可运行配置不管是插件还是脚本把一次成功运行的配置文件、示例笔记、生成结果单独存一份。后续改脚本或参数时先用这份最小配置回归测试确认基础功能没坏再处理自己的真实笔记。这个习惯能显著降低踩坑成本。9.3 输入输出目录分离批量任务强制要求输入目录和输出目录分开。原因很简单生成的.canvas文件同样是 JSON 格式如果它被放在输入目录下而脚本又扫描了该目录就会尝试把 Canvas 文件当作 Markdown 解析产生不可预期的结果。目录分离是从根上消除这类问题。9.4 批量任务要加日志和失败重试批量处理场景里加日志不是可选项而是必选项。每处理一个文件就打印一条状态记录失败时记录具体行号和原因重试机制优先处理失败文件列表而不是把全部文件重新跑一遍。对于几百个文件的笔记库这个设计能节省大量时间。9.5 接口服务要限制访问范围如果开了本地 HTTP 服务必须只监听 127.0.0.1不要用0.0.0.0暴露到局域网或公网。路径参数需要做合法性校验防止通过../这类相对路径访问目录外文件。至少在服务端做一份允许访问的根目录白名单一切超出范围的请求直接返回 403。9.6 涉及真实个人信息时确认授权人物关系图如果涉及真实姓名、企业组织、他人隐私不要在未授权的情况下生成并传播。小说角色、虚构设定、公开技术文档不受此限制但涉及真实数据和内部信息时控制访问范围是第一原则。10. 总结与下一步这个方案最值得尝试的点是把 Obsidian 的 Markdown 笔记和 Canvas 白板打通让“文字设定”和“可视化关系图”不再割裂。最先应该验证的是基础解析流程拿一份包含 3 到 5 个人物、带关系描述的笔记跑通脚本确认 Canvas 能正确显示节点和连线。最容易踩的坑是笔记格式不统一。解决思路不是每次都去改正则而是先规范自己的写作格式让解析器只处理一类明确的结构。接下来可以扩展的方向不少。比如在脚本中增加时间戳每次重新生成时保留历史版本把##标题里的标签解析成节点颜色比如阵营不同颜色不同再比如利用 Obsidian 的 Templater 插件在新建人物笔记时自动生成带关系模板的 Markdown 骨架。把这些能力一点点加进自己的工作流人物关系白板就能从实验功能变成真正日常使用的笔记基础设施。