首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >#WorkBuddy# 用 WorkBuddy 管住 200 篇本地 Markdown:自动索引 + 断链体检(附零依赖脚本)

#WorkBuddy# 用 WorkBuddy 管住 200 篇本地 Markdown:自动索引 + 断链体检(附零依赖脚本)

原创
作者头像
用户12406184
发布于 2026-10-20 13:25:55
发布于 2026-10-20 13:25:55
1130
举报

一、你会遇到的场景

用 Obsidian 或纯 Markdown 记笔记,前 50 篇很爽,过了 100 篇就开始烂:索引文件永远是上周的版本,新建的笔记没进去;凭印象写了 [[某篇笔记]],其实那篇还没建,跳过去是空的;有些笔记写完之后再也没被任何一篇引用过,等于白写。

手工维护索引这件事,本质上是"每次新增/改名都要记得去改三处"——只要有一处漏了,索引就开始失真。凡是靠"记得"的流程,最后一定会烂。所以正确做法不是勤快一点,而是根本不手工维护。

二、结论先行

让脚本扫一遍目录自动生成索引,顺手把断链和孤儿笔记报出来;再让 WorkBuddy 只读这个索引文件做检索,不要把 200 篇全文都塞进上下文。

适用边界:本地 Markdown 库、文件在 1000 篇以内、以 wikilink 为主要串联方式。超过这个规模或者笔记散落在多个网盘/在线文档里,这套就不够用了,得换成检索服务。

三、完整操作步骤

步骤 1:给库定一个"机器可识别"的最小约定

索引脚本最怕的是每篇笔记格式都不一样。只强制三件事,别的随便写:

  1. 文件必须是 .md,根目录或子目录都行;
  2. frontmatter 里可选填 title / tags / updated,不填也能跑(会退化成取文件名和首个 # 标题);
  3. 串联一律用 [[双链]]。
代码语言:yaml
复制
---
title: 选题池维护
tags: [内容运营, 流程]
updated: 2026-10-18
---

步骤 2:把体检脚本放进知识库

在库根目录建 .tools/kb_index.py(完整脚本见文末附件,约 200 行,零第三方依赖,Python 3.9+)。核心就四件事:

代码语言:python
复制
# 1) 跳过不该扫的目录(隐藏目录一律跳过)
SKIP_DIRS = {".git", ".obsidian", ".trash", ".trash-backup",
             ".workbuddy", ".tools", "node_modules", "__pycache__"}

# 2) 提取 wikilink 之前,先把代码块围栏剔掉,
#    否则教程里写的 [[示例]] 会被当成真链接,凭空多出一堆断链
FENCE_RE = re.compile(r"```.*?```", re.S)
WIKILINK_RE = re.compile(r"\[\[([^\]\|#]+)(?:#[^\]\|]*)?(?:\|[^\]]*)?\]\]")

def extract_wikilinks(body: str) -> list:
    cleaned = FENCE_RE.sub("", body)
    cleaned = re.sub(r"`[^`\n]*`", "", cleaned)
    return [nfc(x.strip()) for x in WIKILINK_RE.findall(cleaned) if x.strip()]

# 3) 建别名表:文件名 / 相对路径 / frontmatter 标题,都算可链接目标
for n in notes:
    alias.setdefault(nfc(n.path.stem), n)          # 文件名
    alias.setdefault(nfc(n.rel[:-3]), n)           # 相对路径
    alias.setdefault(nfc(n.title), n)              # 标题

# 4) 匹配不上的就是断链;入链为 0 的就是孤儿笔记

关键设计:默认只读。 只有显式加 --write 才落盘,别的一律只打印报告。这条救过我——第一次跑脚本时正则写错了,要是默认就写文件,整个索引会被写成一堆垃圾。

步骤 3:先体检,别急着写

代码语言:bash
复制
# macOS / Python 3.13
cd ~/Documents/MyVault            # 换成你自己的库路径
python3 .tools/kb_index.py .

输出长这样:

代码语言:plaintext
复制
====================================================
知识库:/Users/lee/Documents/MyVault
====================================================
笔记总数   : 8
wikilink   : 10 条,解析成功 8 条
断链       : 2 条
   ✗ 02-方法论/自动化选型 → [[失败自愈]]
   ✗ 02-方法论/自动化选型 → [[定时任务排查清单]]
孤儿笔记   : 3 篇
   · 03-周报/2026-10-06周报
   · 03-周报/2026-10-13周报
   · README
耗时       : 0.7 ms
====================================================
(只读模式,未写文件。加 --write 落盘)

步骤 4:确认报告没问题,再落盘

代码语言:bash
复制
python3 .tools/kb_index.py . --write

生成的 00-索引.md 包含三部分:按目录的笔记表(标题/标签/更新日期/入链数)、⚠️ 断链清单、🔍 孤儿笔记清单。表格里用的是 [[路径\|标题]] 写法,在 Obsidian 里点击可直接跳转。

文件头会自动加一行提醒,防止手改:

代码语言:markdown
复制
> 本文件由 `kb_index.py` 自动生成,**请勿手工编辑**,改了会被下次运行覆盖。

步骤 5:挂成定时任务,彻底不用记得跑

macOS 用 launchd(比 crontab 靠谱,crontab 在 macOS 上遇到全盘权限和睡眠唤醒容易漏跑):

代码语言:bash
复制
cat > ~/Library/LaunchAgents/com.user.kbindex.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.user.kbindex</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/python3</string>
    <string>/Users/lee/Documents/MyVault/.tools/kb_index.py</string>
    <string>/Users/lee/Documents/MyVault</string>
    <string>--write</string>
  </array>
  <key>StartCalendarInterval</key>
  <dict><key>Hour</key><integer>8</integer><key>Minute</key><integer>30</integer></dict>
  <key>StandardOutPath</key><string>/tmp/kbindex.log</string>
  <key>StandardErrorPath</key><string>/tmp/kbindex.err</string>
</dict>
</plist>
EOF

launchctl load -w ~/Library/LaunchAgents/com.user.kbindex.plist
launchctl list | grep kbindex        # 确认已注册

步骤 6:让 WorkBuddy 只读索引,别吞全文

这是省积分的关键一步。200 篇全文塞进上下文,光输入 token 就烧掉一大截;而索引文件只有几 KB。

代码语言:plaintext
复制
提示词模板(可直接复制):

读取 ~/Documents/MyVault/00-索引.md 这个文件(只读,不要扫描其他文件)。
基于其中的「按目录」表回答我的问题:{你的问题}
如果索引里的信息不足以回答,告诉我你还需要读哪几篇具体笔记,
把文件名列出来等我确认,不要自己去遍历目录。

配合 WorkBuddy 5.8.0 的独立文件浏览器窗口,可以一边在主窗口对话,一边在独立窗口里翻 Markdown,互不遮挡;5.8.0 起资料库还支持上传压缩包并用于后续检索,大库可以先打包再问。

四、实测数据

以下均为本机(macOS + Apple Silicon,Python 3.13.12)真实执行结果,跑 3 次取 min/avg/max:

库

笔记数

wikilink 数

断链

孤儿笔记

扫描+分析耗时

演示库(虚构示例)

8

10

2

3

1.2 / 1.7 / 2.0 ms

本人真实在用库

196

141

10

155

26.3 / 47.6 / 81.3 ms

三个可直接用的结论:

  1. 196 篇的库,全量扫描 + 建索引只要几十毫秒。这点耗时根本不需要做增量优化,每天定时全量重跑最省心。
  2. 真实库的孤儿率很高(155/196)。大部分是日记、周报、临时碎片,本来就不该被链接——所以"孤儿"不是错误,是提醒你"这批东西要不要归档"。真正要处理的是断链,那才是写错了。
  3. 断链 10 条全部来自"想引用但还没写的笔记"。这其实是有价值的待办:断链清单 = 你的写作 TODO 列表。

脚本另带 --json 输出,方便接别的自动化;带 --fail-on-broken,有断链时退出码为 1,可以直接当 CI 或定时任务的门禁用。

五、踩坑与边界

坑 1:macOS 中文文件名的 NFD/NFC 归一化(真正会静默出错的那个)

macOS 的文件系统保留文件名的原始 Unicode 形式。同一个词,é(预组合)和 e + ◌́(分解)在字节上完全不同。如果文件名是分解形式、你的 wikilink 是预组合形式,朴素字符串匹配会失败,而且不报错,只是默默算成断链。

本机实测(名字里含 é / が / ě 这类带组合字符的文件):

代码语言:plaintext
复制
listdir: 'Café-が-ě 笔记.md' | NFD? True | NFC? False
朴素匹配:     False     ← 被误判成断链
NFC 归一后:   True

解法:所有文件名、链接目标在进入字典前统一 unicodedata.normalize("NFC", s)。

⚠️ 但要说清楚边界:纯汉字文件名实测不会触发这个坑("测试方法"的 NFD 等于 NFC,朴素匹配通过)。只有名字里带拉丁字母的声调符号、日语浊音符号这类组合字符才会踩到。纯中文库可以不管,混排文件名必须处理。

坑 2:代码块里的 [[ ]] 会被误判成链接。 写教程的库尤其容易中招。解法是提取前先用 re.sub(r"```.*?```", "", body, flags=re.S) 剥掉围栏,行内代码 也要剥。

坑 3:--write 会无条件覆写 00-索引.md。 如果你在索引上手写了内容,第一次跑之前先备份。索引文件本身也被排除在扫描之外(否则会自己链自己,入链数乱掉)。

不适用的场景(别硬套):

  • 笔记散在多个云盘/在线文档里 —— 本地 os.walk 扫不到,得换成走 API 的方案;
  • 笔记之间主要靠标签而不是 wikilink 串联 —— 孤儿检测会失效(其实可以改成按 tag 聚合,但那是另一个脚本);
  • 库超过几千篇 —— 每次全量生成索引文件本身就变大了,得改成分目录索引。

六、小结

索引这活儿,人类必输给脚本——不是因为人不够勤快,而是因为"每次都记得改三处"这件事在概率上就不可能长期成立。把规则写进脚本,让它每天 8:30 自己跑一遍,你只负责看断链清单决定"这篇要不要补"。

顺带一个用法:断链清单天然就是你的写作待办列表,比任何 TODO 工具都准——因为它是你在真实写作上下文里亲手写下的"我打算引用它"。


附件

文件

说明

草稿/assets/kb_index.py

完整脚本,零第三方依赖,Python 3.9+

草稿/assets/demo-vault/

8 篇虚构演示笔记(含 2 处故意埋的断链、1 篇孤儿),可照着跑一遍

草稿/assets/demo-vault/00-索引.md

脚本真实生成的索引输出样例

演示库内所有笔记内容均为虚构示例,与任何真实业务无关。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、你会遇到的场景
  • 二、结论先行
  • 三、完整操作步骤
    • 步骤 1:给库定一个"机器可识别"的最小约定
    • 步骤 2:把体检脚本放进知识库
    • 步骤 3:先体检,别急着写
    • 步骤 4:确认报告没问题,再落盘
    • 步骤 5:挂成定时任务,彻底不用记得跑
    • 步骤 6:让 WorkBuddy 只读索引,别吞全文
  • 四、实测数据
  • 五、踩坑与边界
  • 六、小结
  • 附件
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档