拆解 claude-obsidian——一个 Claude Code Skill 是如何工作的?
拆解 claude-obsidian
——一个 Claude Code Skill 是如何工作的?
作者:一只阿木木 我相信:在 AI 时代,每个普通人都该拥有一个自动生长的知识系统。
先说你为什么要读这篇文章
如果你:
用了 claude-obsidian 觉得很神奇,但不知道"它到底在哪里" 想给它写一个自定义 skill,但不知道从哪里下手 对"Claude Code Skill 是什么东西"有点模糊 想造自己的 AI Agent 工具,正在找参考架构
那这篇文章就是为你写的。
我们要做的事情只有一件:把 claude-obsidian 的源码目录彻底拆开,逐层搞清楚每个文件是干什么的,它们又是如何协同工作的。
不会有废话,全是结构。
第一层:它到底是什么?重新理解"Claude Code Skill"
在拆代码之前,先把这个概念说清楚——因为很多人对"Skill"的定义是模糊的。
10 大多数 Obsidian AI 插件是聊天界面,它们回答你关于现有笔记的问题。claude-obsidian 是一个知识引擎,它自主地创建、组织、维护和演化你的笔记。
这个区别背后,是架构上的根本不同:
传统 Obsidian 插件是这样工作的:
text
用户操作 → Obsidian JS API → 修改 vault 文件
Claude Code Skill是这样工作的:
text
用户自然语言 → Claude Code Agent → 读取 SKILL.md 指令 → 调用工具(读写文件)→ 修改 vault 文件
关键差异:执行者从 JavaScript 代码变成了 LLM。SKILL.md 不是代码,是给 LLM 看的"操作手册"。LLM 读了这份手册,知道该怎么做,然后自己去做。
12 Skills 可以在 SKILL.md 旁边包含支持文件。Plugins 可以提供专门的子 agent,用于特定任务,Claude 可以在适当时自动调用它们。
这是整个系统的第一层认知——Skill 是 LLM 的说明书,不是传统意义上的程序代码。
第二层:完整目录结构,逐行注释
1 项目的完整目录结构如下:
text
claude-obsidian/
├── .claude-plugin/
│ ├── plugin.json # 插件清单(元数据 + 配置)
│ └── marketplace.json # 分发配置(用于 plugin marketplace)
│
├── skills/ # 15 个 Claude Code skills(v1.9.2)
│ ├── wiki/ # 主编排器 + 7 个参考文件
│ ├── wiki-ingest/ # INGEST 操作(核心写入)
│ ├── wiki-query/ # QUERY 操作(知识检索)
│ ├── wiki-lint/ # LINT 操作(健康检查)
│ ├── wiki-cli/ # Obsidian CLI 传输层(v1.7+)
│ ├── wiki-retrieve/ # 混合检索(v1.7+,可选)
│ ├── wiki-mode/ # 方法论模式路由(v1.8+)
│ ├── wiki-fold/ # 日志折叠(DragonScale 可选)
│ ├── save/ # /save:会话归档
│ ├── autoresearch/ # 自主研究循环
│ │ └── references/
│ │ └── program.md # 可配置的研究目标
│ ├── canvas/ # 可视化层(图片/PDF/笔记)
│ │ └── references/
│ │ └── canvas-spec.md # Obsidian canvas JSON 规范
│ ├── defuddle/ # Web 内容提取封装
│ ├── obsidian-bases/ # Bases schema 参考
│ ├── obsidian-markdown/ # OFM 语法参考
│ └── think/ # 10 原则思维框架(v1.9+)
│
├── agents/
│ ├── verifier.md # 提交前审计 agent(v1.7.1+)
│ ├── wiki-ingest.md # 并行批量摄入 agent
│ └── wiki-lint.md # 健康检查 agent
│
├── commands/
│ ├── wiki.md # /wiki 引导命令
│ ├── save.md # /save 命令
│ ├── autoresearch.md # /autoresearch 命令
│ └── canvas.md # /canvas 可视化命令
│
├── hooks/
│ └── hooks.json # SessionStart + Stop 热缓存钩子
│
├── _templates/ # Obsidian Templater 模板
├── wiki/ # 预填充的示例 vault
├── bin/
│ └── setup-vault.sh # 一键配置脚本
└── scripts/
├── allocate-address.sh # 原子性页面地址分配器
└── wiki-lock.sh # 文件级并发锁
目录很大,但有清晰的层次。下面我们分层拆解最关键的几个部分。
第三层:.claude-plugin/ ——插件的身份证
12 .claude-plugin/plugin.json 文件定义了插件的元数据和配置。清单文件是可选的——如果省略,Claude Code 会自动发现默认位置的组件,并从目录名推导插件名称。当你需要提供元数据或自定义组件路径时才需要使用清单。
plugin.json 的基本结构是这样的:
JSON
{
"name": "claude-obsidian",
"version": "1.9.2",
"description": "Self-organizing AI second brain for Obsidian",
"skills": ["skills/wiki", "skills/wiki-ingest", ...],
"agents": ["agents/wiki-ingest.md", "agents/wiki-lint.md"],
"hooks": "hooks/hooks.json"
}
marketplace.json 是用于 claude plugin marketplace 系统的分发清单。3.claude-plugin/marketplace.json 清单使这个仓库与 Claude Code 的 marketplace 系统兼容。
第四层:skills/ ——系统的大脑
这是整个项目最重要的目录。每个 skill 的结构是:
text
skills/
└── skill-name/
├── SKILL.md # 核心:给 LLM 的操作说明书
└── references/ # 补充参考文档(可选)
└── *.md
12 对 skill 的 SKILL.md 所做的更改会在当前会话中立即生效。这意味着你修改 SKILL.md 之后不需要重启,Claude 下一次调用时就会读到新内容——这是非常重要的开发特性。
skills/wiki/——主编排器
这是整个系统的入口。当你输入 /wiki 时,Claude Code 读取的就是这个 skill 的 SKILL.md。
它的职责是:
初始化 vault(第一次运行时) 检查 vault 健康状态(后续运行时) 1 后续运行时, /wiki从上次停止的地方继续——检查 vault 健康状态,显示过期声明,以及显示来自 hot.md 的最近活动。
skills/wiki-ingest/——最核心的 skill
这是负责把原始资料"编译"成 wiki 的 skill,也是整个系统逻辑最复杂的地方。
触发词:ingest、process this source、add this to the wiki、read and file this、batch ingest、ingest this url
它在 SKILL.md 里定义了完整的 ingest 流程:
第一步:去重检查
在摄入任何文件之前,检查 .raw/.manifest.json 以避免重新处理未更改的来源。manifest 的结构包含每个来源文件的哈希值、摄入时间、创建的页面和更新的页面。
这个机制避免了重复 ingest 同一个文件,同时也作为 ingest 历史的审计日志。
第二步:地址分配
./scripts/allocate-address.sh 原子性地保留并返回下一个地址。--peek 参数在不保留的情况下打印下一个值(安全,只读)。--rebuild 从现有 frontmatter 中观察到的最高 c-NNNNNN 重新计算计数器。
在写入任何新的非元数据页面之前,调用 ./scripts/allocate-address.sh 并捕获输出。在页面的 frontmatter 中包含 address: c-XXXXXX。
第三步:单写锁
Phase 2 是单写模式。不要从多个 Claude 会话或分配地址的子 agent 并行运行 ingest。flock 在 helper 中防止计数器损坏,但不会序列化页面写入本身。
第四步:写入 wiki 页面
SKILL.md 规定了页面写入的完整 Markdown 规范:
语法标准:使用正确的 Obsidian Flavored Markdown 编写所有 Obsidian Markdown。Wikilinks 格式为 [[Note Name]],callouts 格式为 > [!type] Title,embeds 格式为 ![[file]],属性使用 YAML frontmatter。
一个典型的 wiki 页面的 YAML frontmatter 长这样:
YAML
---
address: c-000042
type: entity # entity | concept | source | synthesis
title: "Redis aeEventLoop"
created: 2026-05-01
updated: 2026-05-15
confidence: high # high | medium | low
sources:
- .raw/redis/ae-analysis.md
tags: [redis, event-loop, io-multiplexing]
---
skills/wiki-query/——检索 skill
你提问。Claude 读取热缓存(最近上下文),扫描索引,深入相关页面,综合出答案。它引用的是具体的 wiki 页面,不是训练数据。
检索的三层优先级(在 SKILL.md 里明确定义):
text
第一层:wiki/hot.md(约 500 tokens)——最近上下文
第二层:wiki/index.md(约 1000 tokens)——总目录
第三层:相关的具体 wiki 页面(约 3000 tokens)——详细内容
skills/wiki-retrieve/——可选的混合检索层(v1.7+)
这是 v1.7 新增的高级检索 skill,基于 Anthropic 2024 年 9 月的 contextual retrieval 研究。
wiki-retrieve skill 提供了一个基于 Anthropic 2024 年 9 月上下文检索研究的三层检索管道。BM25 是始终开启的稀疏层。上下文前缀层需要同意(--allow-egress),用于希望将页面内容发送到 Anthropic API 以生成前缀的用户。余弦重排序默认使用本地 ollama 模型。v1.7 的 50 个查询基准测试测量到 top-1 准确率提升了 32 个百分点,错误减少了 41%。
注意:这个 skill 是可选的,默认不启用。开启命令:
Bash
bash bin/setup-retrieve.sh
第五层:agents/ ——并发执行的子 Agent
Plugins 可以提供专门的子 agent,用于 Claude 可以在适当时自动调用的特定任务。Agent 的结构包含 frontmatter:name、description、model、effort、maxTurns、disallowedTools,以及详细的系统提示。
claude-obsidian 有三个 agent:
agents/wiki-ingest.md——并行 ingest agent
当你批量 ingest 多个来源时,这个 agent 会被主 Claude 调用,并行处理多个文件。
并行 ingest 子 agent 可能同时操作同一个 wiki 页面。scripts/wiki-lock.sh 提供文件级的建议锁:一个写入者获取锁,另一个等待并在下一轮重试。PostToolUse 自动提交钩子在暂存前检查锁列表,在写入进行时推迟提交。
它的 frontmatter 大概是这样的:
Markdown
---
name: wiki-ingest
description: 并行处理多个来源的 ingest,当用户批量摄入时调用
model: sonnet
effort: high
maxTurns: 50
disallowedTools: []
---
(详细的 ingest 操作系统提示)
agents/verifier.md——提交前审计 agent(v1.7.1+)
在写入 wiki 文件触发 PostToolUse 钩子之前,verifier agent 负责审计本次写入的质量:
检查 frontmatter 是否完整 检查 wikilinks 是否有效 检查是否引用了 .raw/里的来源
Plugin agents 支持 name、description、model、effort、maxTurns、tools、disallowedTools、skills、memory、background 和 isolation frontmatter 字段。唯一有效的 isolation 值是 "worktree"。
第六层:hooks/hooks.json——Hot Cache 的运行机制
这是整个系统"跨会话记忆"的技术实现,也是最精妙的设计之一。
wiki/hot.md 存储最近会话的滚动摘要。它通过钩子在 SessionStart 时自动注入,并在 Stop 时通过 scripts/update-hot-cache.sh 调用 claude -p 单次模式在后台重新生成。新会话从最近的上下文开始,无需任何回顾。
hooks.json 的结构:
JSON
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-hot-cache.sh"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/update-hot-cache.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/auto-commit.sh"
}
]
}
]
}
}
三个钩子的职责:
| SessionStart | wiki/hot.md,注入上下文 | |
| Stop | wiki/hot.md | |
| PostToolUse | git add + git commit |
这些钩子在 .claude/settings.json 里定义,并使用 $CLAUDE_PROJECT_DIR,所以在任何 clone 里无需编辑就能工作。
第七层:安全边界设计
claude-obsidian 的安全设计有几个值得学习的地方:
.raw/ 的只读约定
这不是代码层面的限制,而是 SKILL.md 里明确写给 LLM 的规则:不得修改 .raw/ 目录下的任何文件。 LLM 会遵守这个规则,因为它被写进了 skill 的操作说明里。
Web 出口隔离
上下文前缀层是同意门控的(--allow-egress),用于希望将页面内容发送到 Anthropic API 进行前缀生成的用户。
没有这个标志,所有处理都在本地进行。
URL 内容安全
在 skills/autoresearch/SKILL.md 中的 Web egress 卫生策略(v1.8.2+)规定:拒绝 file:// / javascript: / RFC1918 主机,剥离 <script> 标签和 wikilink 注入尝试,将 fetch 内容体限制在 50KB 以内。
第八层:数据流全貌
把所有层次合在一起,看一次完整的 ingest 的数据流:
text
用户输入:"ingest redis/ae-analysis.md"
↓
Claude Code 接收输入
↓
读取 skills/wiki-ingest/SKILL.md(操作说明)
↓
检查 .raw/.manifest.json(去重)
↓
调用 scripts/allocate-address.sh(获取页面地址)
↓
读取 .raw/redis/ae-analysis.md(原始来源)
↓
提取实体、概念、关系、矛盾
↓
写入 wiki/entities/*.md(新实体页)
写入 wiki/concepts/*.md(新概念页)
更新 wiki/sources/*.md(来源摘要页)
更新 wiki/index.md(总目录)
↓
PostToolUse 钩子触发 → git commit
↓
Stop 钩子触发 → 更新 wiki/hot.md
↓
Obsidian 自动刷新(检测到文件变更)
第九层:commands/ vs skills/ 的区别
很多人看到 commands/ 目录会疑惑:skill 和 command 有什么区别?
| skills/ | commands/ | |
|---|---|---|
| 触发方式 | /wiki) | |
| 内容形式 | ||
| 典型作用 |
举例:
skills/wiki-ingest/定义了 ingest 的完整操作逻辑commands/wiki.md定义了用户输入/wiki时的引导流程(检查 Obsidian、初始化 vault、问用户问题等)
用一张图理解整个架构
text
┌─────────────────────────────────────────┐
│ 用户自然语言输入 │
└─────────────────┬───────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Claude Code Agent │
│ 读取 SKILL.md → 理解操作规则 → 执行 │
└──────┬────────────────────┬────────────┘
↓ ↓
┌─────────────┐ ┌──────────────────┐
│ skills/ │ │ agents/ │
│ wiki-ingest │ │ wiki-ingest.md │
│ wiki-query │ │ wiki-lint.md │
│ wiki-lint │ │ verifier.md │
│ autoresearch│ └──────────────────┘
│ ... │
└──────┬──────┘
↓
┌─────────────────────────────────────────┐
│ hooks/ 钩子层 │
│ SessionStart → 读 hot.md │
│ Stop → 写 hot.md │
│ PostToolUse → git commit │
└──────┬──────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ vault 文件系统 │
│ .raw/ ← 只读输入区 │
│ wiki/ ← LLM 写入区 │
│ wiki/hot.md ← 会话记忆 │
└─────────────────────────────────────────┘
从拆解到实践:你现在可以做什么
理解了这个架构,你有三件事可以立刻做:
Level 1:自定义 program.md 编辑 skills/autoresearch/references/program.md,配置你的研究偏好(优先学术来源、技术文档、或特定领域)。这是最低成本的定制。
Level 2:修改 SKILL.md 比如在 skills/wiki-ingest/SKILL.md 里添加规则:"当摄入技术文档时,额外生成一个 api-reference/ 分类的页面"。修改后立刻生效。
Level 3:写一个全新的 skill 这就是下一篇文章要做的事情——从零写一个"从 GitHub Trending 自动 ingest"的 skill,完整代码。
👇 如果你想继续跟着做:
关注「一只阿木木」,我们在 AI 时代一起构建自己的知识系统。
扫码加入行动营👇获取更多Obsidian + AI数字大脑实践
关注【一只阿木木】。
我相信:在 AI 时代,每个普通人都该拥有一个自动生长的知识系统
去做,才是真的学。🌊