canvas-updater - Obsidian Canvas 知识图谱自动更新器
概述
canvas-updater 是一个 Claude Code Skill,用于自动创建和更新 Obsidian Canvas 知识图谱。它能够智能搜索相关文档,按主题分类,并生成可视化的知识网络图。
核心功能
1. 自动创建 Canvas
- 根据主题关键词搜索所有相关文档
- 智能分类文档(插件、学习、集成、AI、配置等9大类别)
- 生成结构化知识图谱(中心节点 + 分组 + 文件节点 + 连线)
2. 智能增量更新
- 只添加新发现的文档,完全保留原有内容
- 新内容放置在独立区域,不干扰用户手动调整的布局
- 自动去重,避免重复添加相同文件
触发方式
当用户提出以下请求时,Skill 会自动激活:
"创建 Obsidian 知识管理 Canvas"
"更新 cflow Canvas"
"同步新文档到知识图谱"
"为 AI 主题创建 Canvas"
关键词:create Canvas, update Canvas, sync knowledge graph, 创建知识图谱, 更新Canvas
实现原理
技术架构
canvas-updater/
├── SKILL.md # Skill 元数据和说明
├── workflow.md # 9步工作流程详解
└── classification.md # 文件分类规则
关键技术要点
1. 紧凑 JSON 格式 ⚠️
这是最关键的发现! Obsidian Canvas 要求使用紧凑 JSON 格式(冒号后无空格),否则会导致节点被清空。
# ✅ 正确格式
json.dumps(canvas, ensure_ascii=False, indent='\t', separators=(',', ':'))
# 输出: "id":"xxx","type":"file"
# ❌ 错误格式(Python 默认)
json.dumps(canvas, ensure_ascii=False, indent='\t')
# 输出: "id": "xxx", "type": "file" ← 冒号后有空格会导致失败2. Canvas JSON 结构
{
"edges": [...], // 连线数组(必须在最前)
"nodes": [...], // 节点数组
"metadata": { // 元数据(必须在最后)
"version": "1.0-1.0",
"frontmatter": {}
}
}3. 节点必需字段
每个节点都必须包含:
{
"id": "unique_id", # 唯一标识
"styleAttributes": {}, # 空对象(必需!)
"type": "file|group|text", # 节点类型
"x": 0, # X坐标
"y": 0, # Y坐标
"width": 400, # 宽度
"height": 80 # 高度
}文件节点额外需要:
file: 文件相对路径
分组节点额外需要:
label: 分组标题color: 颜色代码(“1”-“6”)
工作流程
完整 9 步流程
第一步:识别目标 Canvas
# 1. 用户明确主题 → 直接使用
# 2. 否则扫描 90 Resources/Canvas/ 列出所有 .canvas 文件
# 3. 读取目标 Canvas 文件内容第二步:分析现有内容
# 1. 解析 Canvas JSON 结构
# 2. 提取所有已存在的文件节点路径(用于去重)
# 3. 计算已占用的空间区域
# 4. 识别用户创建的自定义节点第三步:搜索新文档
Obsidian 主题(特殊规则):
# 搜索范围:
# 1. 文件名包含 "Obsidian" - **/*Obsidian*.md
# 2. 标题包含 "Obsidian" - Grep 搜索 ^#+ .*Obsidian.*
# 3. 标签包含 "obsidian" - YAML frontmatter
# 4. 强关联路径:
# - 10 Note/300-实用软件/05-笔记工具/02-Obsidian/
# - Obsidian blog/ 下提到 Obsidian 的文档其他主题:
- 从 Canvas 文件名提取关键词
- 使用 Glob/Grep 搜索相关文档
排除路径:
.trash/, node_modules/, .obsidian/, public/, .git/
90 Resources/Obsidian导出/, SimpRead/, Canvas双链数据/
70 每日日报/(日记类 Canvas 除外)
第四步:计算新节点位置
核心原则:新增内容独立成区,完全不干预已有内容
# 1. 找到 Canvas 中的最大 X/Y 坐标
max_x = max(node['x'] + node.get('width', 0) for node in existing_nodes)
# 2. 在完全空白的区域规划新节点
start_x = max(2200, max_x + 500) # 至少间距 500px
start_y = -600
# 3. 所有新增内容放在独立的分组内第五步:创建新增内容独立区
当新增文档 ≤ 20 个:创建单个总分组
group = {
"id": "update20251206",
"styleAttributes": {},
"type": "group",
"x": start_x,
"y": start_y,
"width": group_width,
"height": group_height,
"color": "6",
"label": "📌 新增内容 (2025-12-06)"
}当新增文档 > 20 个:创建多个子分组按类型细分
categories = {
'plugin': ('🔌 插件类', '1'),
'learning': ('📚 学习类', '2'),
'publish': ('📝 发布类', '3'),
'ai': ('🤖 AI类', '4'),
'integration': ('🔗 集成类', '5'),
'config': ('⚙️ 配置类', '2'),
'other': ('📄 其他', '6')
}
# 垂直堆叠子分组
current_y = start_y
for category in categories:
# 创建子分组...
current_y += group_height + 150 # 间距 150px第六步:生成文件节点
网格布局参数:
NODE_WIDTH = 400
NODE_HEIGHT = 80
H_SPACING = 50 # 水平间距
V_SPACING = 30 # 垂直间距
COLS = 2 # 每行列数
for i, filepath in enumerate(files):
row = i // COLS
col = i % COLS
node_x = group_x + 50 + col * (NODE_WIDTH + H_SPACING)
node_y = group_y + 50 + 40 + row * (NODE_HEIGHT + V_SPACING)
node = {
"file": filepath,
"id": f"new20251206_{i+1:03d}",
"styleAttributes": {},
"type": "file",
"x": node_x,
"y": node_y,
"width": NODE_WIDTH,
"height": NODE_HEIGHT
}第七步:智能合并
核心原则:只添加,不修改,不删除
# 1. 保留原有所有内容
original_nodes = canvas["nodes"]
original_edges = canvas["edges"]
# 2. 分组插入到开头(显示在底层)
for group in reversed(new_groups):
canvas["nodes"].insert(0, group)
# 3. 文件节点追加到末尾(显示在顶层)
canvas["nodes"].extend(new_file_nodes)插入顺序:
更新后的 nodes 数组:
[
新增分组1, // 插入到最前面(底层背景)
新增分组2,
...,
原有分组1, // 原有内容保持不变
原有分组2,
...,
原有文件节点1,
...,
新增文件节点1, // 新文件节点放在末尾(顶层)
新增文件节点2,
...
]
第八步:保存 Canvas
关键:使用紧凑 JSON 格式
json_str = json.dumps(
canvas,
ensure_ascii=False,
indent='\t',
separators=(',', ':') # 冒号后无空格!
)
with open(canvas_path, 'w', encoding='utf-8') as f:
f.write(json_str)第九步:报告结果
✅ Canvas 更新完成!
📊 统计信息:
- 原有节点: 36个
- 新增节点: 45个
- 总节点数: 81个
- 新增位置: 右侧独立区 (X=2200 开始)
📦 新增分组:
- 🔌 插件类: 18个
- 📚 学习类: 5个
- 🔗 集成类: 12个
...
💡 提示:
- 新内容在右侧独立区域,完全不干扰原有布局
- 所有新节点在对应的彩色分组内
- 原有 36 个节点和 5 条连线完全保留
文件分类规则
9 大类别
| 类型 | 关键词 | 颜色 | 图标 | 示例 |
|---|---|---|---|---|
| 插件 | 插件/plugin | 红色(1) | 🔌 | Obsidian插件、Atracker sync |
| 配置 | 配置/config/模板/设置 | 橙色(2) | ⚙️ | Obsidian配置、模板文件 |
| 发布 | 发布/博客/publish | 黄色(3) | 📝 | 博客搭建指南 |
| AI | ai/claude/copilot | 绿色(4) | 🤖 | Claude Code、AI助手 |
| 集成 | memos/notion/同步/转 | 青色(5) | 🔗 | memos同步、OneNote转换 |
| 项目 | 项目/project/开发 | 红色(1) | 📦 | 项目开发日志 |
| 学习 | 学习/教程/索引/资源 | 橙色(2) | 📚 | 学习资源、教程索引 |
| 移动端 | 移动端/mobile/快捷指令 | 黄色(3) | 📱 | 移动端同步 |
| 其他 | 默认 | 紫色(6) | 📄 | 未匹配的文档 |
分类优先级
# 1. 路径特征优先
if '插件' in filepath or '/plugin' in filepath.lower():
return 'plugin'
# 2. 项目类
if any(k in filename for k in ['项目', 'project', '开发']):
return 'project'
# 3. 配置类
if any(k in filename for k in ['配置', '模板', 'config', '设置']):
return 'config'
# 4. AI类
if any(k in filepath.lower() for k in ['ai', 'claude', 'copilot']):
return 'ai'
# 5. 默认:其他
return 'other'空间布局策略
新增区域定位
# 右侧独立区
start_x = max(2200, max_x + 500) # 至少距离现有内容 500px
start_y = -600
# 确保与现有内容有足够间距(≥500px)节点尺寸标准
文件节点: 400 x 80 px
分组节点: 动态计算(根据内容)
网格布局: 2列,水平间距 50px,垂直间距 30px
分组间距: ≥150px 垂直堆叠
布局示例
现有内容区域 新增独立区域
┌─────────────┐ ┌──────────────────┐
│ 原有分组1 │ │ 📚 学习类 (5个) │
│ │ │ ┌────┐ ┌────┐ │
│ ○ 文件1 │ │ │文件│ │文件│ │
│ ○ 文件2 │ ← 500px → │ └────┘ └────┘ │
│ │ └──────────────────┘
└─────────────┘ ↓ 150px
max_x ┌──────────────────┐
│ 🔌 插件类 (3个) │
│ ┌────┐ ┌────┐ │
│ │文件│ │文件│ │
│ └────┘ └────┘ │
└──────────────────┘
使用示例
示例 1:创建 Obsidian 知识图谱
用户输入:
创建 Obsidian 知识管理 Canvas
Skill 执行流程:
- 搜索所有包含 “Obsidian” 的文档(文件名、标题、标签)
- 找到 96 个相关文档
- 按类型分类:插件(18)、学习(13)、集成(12)、发布(8)…
- 创建中心节点 “Obsidian 知识体系”
- 环绕中心节点创建 6 个分组
- 在各分组内按网格布局放置文件节点
- 创建连线连接分组到中心节点
- 保存到
90 Resources/Canvas/Obsidian知识主题.canvas
结果:
✅ Canvas 创建完成!
📊 总节点数: 96个文档 + 6个分组 + 1个中心 = 103个节点
🔗 连线数: 6条(各分组连接到中心)
📍 文件位置: 90 Resources/Canvas/Obsidian知识主题.canvas
示例 2:增量更新 Canvas
用户输入:
更新 Obsidian Canvas
Skill 执行流程:
- 读取现有 Canvas(36个节点)
- 搜索新文档,发现 45 个未包含的文档
- 计算最大 X 坐标:max_x = 1839
- 新增区域起点:x = 2200, y = -600
- 创建 5 个彩色分组(插件、学习、集成、发布、AI)
- 在各分组内添加新文件节点
- 保留原有所有内容(36个节点 + 5条连线)
- 保存更新后的 Canvas
结果:
✅ Canvas 更新完成!
📊 原有节点: 36个 → 新增节点: 45个 → 总节点数: 81个
💡 新内容在右侧独立区域(X=2200),完全不干扰原有布局
示例 3:创建 CFlow 主题 Canvas
用户输入:
创建 cflow 知识主题 Canvas
Skill 执行流程:
- 搜索标题包含 “cflow” 或 “memos” 的文档
- 排除日记文件(70 每日日报/)
- 找到 22 个相关文档
- 分类:配置部署(8)、其他(7)、项目开发(3)、集成(2)…
- 创建中心节点 “CFlow/Memos 知识体系”
- 创建 6 个分组并放置文件节点
- 保存到
90 Resources/Canvas/cflow知识主题.canvas
结果:
✅ Canvas 创建完成!
📦 分组统计:
- ⚙️ 配置部署: 8个
- 📄 其他: 7个
- 📦 项目开发: 3个
- 🔗 集成: 2个
- 🔧 问题解决: 1个
- 🔌 插件: 1个
注意事项
⚠️ 关键要点
-
紧凑 JSON 格式:这是最容易被忽略但最关键的要求
- 必须使用
separators=(',', ':') - 冒号后不能有空格
- 否则 Obsidian 会清空所有节点
- 必须使用
-
完全保留用户修改:
- 不删除任何原有节点
- 不移动任何现有内容
- 不修改用户创建的连线
-
智能去重:
- 使用文件相对路径作为唯一标识
- 每次只添加新发现的文件
-
路径验证:
- 所有文件路径必须真实存在
- 使用相对路径(相对于 vault 根目录)
常见问题
Q: 为什么更新后节点被清空了?
A: 检查 JSON 格式!确保使用 separators=(',', ':'),冒号后不能有空格。
Q: 如何避免干扰原有布局?
A: Skill 会自动计算新增区域,放置在 max_x + 500px 的位置,完全独立。
Q: 可以自定义分类规则吗?
A: 可以修改 .claude/skills/canvas-updater/classification.md 文件。
Q: 支持哪些主题?
A: 理论上支持任何主题,只需提供主题关键词即可自动搜索和分类。
技术细节
允许的工具
allowed-tools: Read, Write, Grep, Glob, Bash- Read: 读取现有 Canvas 文件
- Write: 保存更新后的 Canvas
- Grep: 搜索文档标题和内容
- Glob: 按文件名模式搜索
- Bash: 执行路径验证和文件操作
文件结构
.claude/skills/canvas-updater/
├── SKILL.md # Skill 元数据
│ ├── name: canvas-updater
│ ├── description: 自动发现触发条件
│ └── allowed-tools: 工具权限
│
├── workflow.md # 9步工作流程
│ ├── 第一步:识别目标 Canvas
│ ├── 第二步:分析现有内容
│ ├── 第三步:搜索新文档
│ ├── 第四步:计算新节点位置
│ ├── 第五步:创建新增内容独立区
│ ├── 第六步:生成文件节点
│ ├── 第七步:智能合并
│ ├── 第八步:保存 Canvas
│ └── 第九步:报告结果
│
└── classification.md # 文件分类规则
├── 9大类别定义
├── 分类优先级
└── 示例参考
版本历史
v1.0 (2025-12-06)
首次发布:
- ✅ 支持创建和更新 Canvas
- ✅ 智能文件分类(9大类别)
- ✅ 独立区域布局策略
- ✅ 紧凑 JSON 格式支持
- ✅ 完全保留用户修改
- ✅ 自动去重机制
核心发现:
- 🔧 发现 Obsidian Canvas 紧凑 JSON 格式要求
- 📝 建立完整的 9 步工作流程
- 🎨 定义 9 大文件分类体系
- 🗺️ 确立独立区域布局策略
相关资源
官方文档
相关文件
- Agent 版本:
.claude/agents/Canvas知识图谱更新.md - Skill 配置:
.claude/skills/canvas-updater/SKILL.md - 工作流程:
.claude/skills/canvas-updater/workflow.md - 分类规则:
.claude/skills/canvas-updater/classification.md
示例 Canvas
90 Resources/Canvas/Obsidian知识主题.canvas- 96个 Obsidian 相关文档90 Resources/Canvas/cflow知识主题.canvas- 22个 CFlow/Memos 文档90 Resources/Canvas/Claude Code知识主题.canvas- 32个 Claude Code 文档
总结
canvas-updater Skill 是一个强大的知识管理自动化工具,它能够:
- 自动发现:根据主题关键词智能搜索相关文档
- 智能分类:按照 9 大类别自动归类文档
- 可视化呈现:生成结构化的 Canvas 知识图谱
- 增量更新:只添加新内容,完全保留用户修改
- 零干扰:新内容独立布局,不影响原有结构
通过使用紧凑 JSON 格式、独立区域布局、智能去重等技术,确保了 Canvas 更新的稳定性和可靠性。这是一个真正实用的知识管理自动化解决方案。
作者:苏苏
创建时间:2025-12-06
Skill 版本:v1.0