--- name: obsidian-search description: Obsidian vault 全文搜索与链接图谱分析。触发场景:搜索笔记内容、查反链/出链、体检图谱健康度(孤立笔记 orphans、断头笔记 deadends、未解析链接 unresolved)、AI 回答前做语义召回(RAG)。触发词:搜索笔记、找笔记、反链、出链、孤立笔记、图谱体检、知识库体检。不用于 tag 查询(obsidian-meta)或 Bases 结构化查询(obsidian-bases)。 --- # Obsidian Search · 搜索与图谱分析 > 官方 `obsidian` CLI 的搜索与链接分析子集。让 AI agent 能像读取一个知识图谱数据库一样理解你的 vault。 --- ## 何时使用 - 全文搜索笔记内容(关键词、正则、限定文件夹) - 找到一篇笔记的**反链**(谁引用了我)或**出链**(我引用了谁) - **图谱健康度体检**:找出孤立笔记、没有出链的断头、未解析的坏链 - 在 AI agent 做问答/总结前,**先做语义召回**,把最相关的几篇文章喂给模型 ## 不用于 - 查找含某 **tag** 的笔记 → **obsidian-meta** (`tags`, `tag name= verbose`) - 查询 frontmatter **property** → **obsidian-meta** (`properties`, `property:read`) - 按 Bases 视图结构化查询 → **obsidian-bases** --- ## 命令速查 ### 全文搜索 ```bash obsidian search query="云原生" # 基础全文搜索 obsidian search query="云原生" path="10-Projects" # 限定文件夹 obsidian search query="云原生" limit=20 # 限制结果数 obsidian search query="云原生" case # 区分大小写 obsidian search query="云原生" total # 只返回匹配计数 obsidian search query="云原生" format=json # JSON 输出(agent 友好) obsidian search:context query="云原生" # 搜索 + 显示匹配行上下文 obsidian search:context query="云原生" format=json obsidian search:open query="云原生" # 在 Obsidian 中打开搜索面板 ``` ### 出链 / 反链 ```bash obsidian links file="云原生运行时经济学" # 当前笔记出链 obsidian links file= total # 只返回出链数 obsidian backlinks file= # 反链(tsv 默认) obsidian backlinks file= counts # 含每个反链的引用次数 obsidian backlinks file= format=json # JSON 输出 obsidian backlinks file= total # 反链总数 ``` ### 图谱健康度(三件套) ```bash obsidian orphans # 孤立笔记(没有任何反链 = 没人引用我) obsidian orphans total # 仅返回数量 obsidian orphans all # 含非 markdown 文件 obsidian deadends # 断头笔记(没有任何出链 = 我没引用任何人) obsidian deadends total obsidian unresolved # 未解析链接(链接指向不存在的笔记) obsidian unresolved counts # 含每个坏链的引用次数 obsidian unresolved verbose # 含源文件 obsidian unresolved format=json ``` --- ## AI Agent 最佳实践 ### 场景 1 · 做问答前的语义召回 AI agent 回答用户问题时,不要只凭记忆编造,而要先做召回: ```bash # Step 1: 全文搜索,拿到 top-20 候选 obsidian search:context query="<用户问题关键词>" limit=20 format=json # Step 2: 根据匹配摘要挑 3~5 篇最相关的,逐篇 read obsidian read file="<候选 1>" obsidian read file="<候选 2>" # Step 3: 基于检索到的内容回答,并标注来源 ``` 这是 **RAG(Retrieval-Augmented Generation)的 CLI 实现**,无需向量数据库。 ### 场景 2 · 知识库季度体检 每季度跑一次"三件套"健康检查,清理知识库: ```bash # 孤立笔记(可能是废稿,或需要补反链) obsidian orphans format=json > /tmp/orphans.json # 断头笔记(需要补充关联阅读) obsidian deadends > /tmp/deadends.txt # 未解析链接(笔记被删除或重命名导致的坏链) obsidian unresolved verbose format=json > /tmp/unresolved.json ``` **处理建议**(交给 AI agent 分类): | 类型 | 判定 | 处理 | |------|------|------| | 孤立笔记 | 正文 > 200 字 | 读内容,找相关主题补反链 | | 孤立笔记 | 正文 ≤ 200 字 | 可能是废稿,移到 `00-Inbox/` 待清理 | | 断头笔记 | 永久笔记(`50-Zettel/`) | 违反 Zettelkasten 原则,**必须补出链** | | 断头笔记 | 日常笔记 | 一般可接受 | | 未解析链接 | 指向已删除笔记 | 删除或修复链接 | | 未解析链接 | 指向未来笔记(占位) | 保留,或创建占位笔记 | ### 场景 3 · 重命名前的安全检查 ```bash # 准备把 "旧标题" 改名为 "新标题" obsidian backlinks file="旧标题" format=json # 先看谁引用我 # 如果反链数 > 0,且文件是 markdown 链接而非 wikilink,需要手动修复 # wikilink [[旧标题]] 会被 obsidian move 自动更新 # markdown [链接](旧标题.md) 需要手动替换 obsidian move path="50-Zettel/旧标题.md" to="50-Zettel/新标题.md" ``` ### 场景 4 · 为新笔记找"相关阅读" ```bash # 写完一篇新笔记后,自动找出 vault 中相关的旧笔记 TITLE="云原生运行时经济学" KEYWORDS=("云原生" "运行时" "成本" "密度") for kw in "${KEYWORDS[@]}"; do obsidian search query="$kw" limit=5 format=json done # 由 agent 合并去重,挑出 top-3 ~ 5 篇,在新笔记末尾插入 "## 相关阅读" 区块 ``` --- ## Agent 写入模式:把搜索结果转成笔记 ```bash # 搜索 + 写入结果到一篇"检索报告" QUERY="虚拟线程" obsidian create path="99-Log/search-${QUERY}-$(date +%Y%m%d).md" content="# 检索报告: ${QUERY} 生成时间: $(date '+%Y-%m-%d %H:%M') ## 全文匹配 $(obsidian search:context query=\"${QUERY}\" limit=20) ## 反链分析 (针对 top-3 笔记的反链分析) " ``` --- ## 与其他技能的协作 | 前置 → 当前 → 后继 | 典型链路 | |------------------|---------| | **obsidian** → obsidian-search → **obsidian-meta** | 先发现 vault,再搜索定位,再读 frontmatter | | **obsidian-search** → **obsidian-meta** | 搜索后用 tags 做二次筛选 | | **obsidian-search** → **obsidian-workflow-pkm** | 搜索结果作为 Zettelkasten 补链、MOC 构建的输入 | | **obsidian-search** → **obsidian** (`read`) | 搜索召回 → 逐篇精读 | --- ## 常见陷阱 | 现象 | 原因 | 对策 | |------|------|------| | `search` 结果太多 | 未加 `limit` | `limit=20 path=` 双重限定 | | `backlinks` 为空但确实有引用 | 用的是 markdown 链接而非 wikilink | `search query="[文件名]"` 兜底 | | `orphans` 把每日笔记也列进来 | daily note 一般无人引用 | 在 agent 逻辑里过滤 `90-Daily/` 路径 | | `unresolved` 出现"未来占位" | 预先写了 `[[未创建的笔记]]` | 占位是 Zettelkasten 合法模式,不要自动删 |