那天晚上我发现,我的知识编译器把自己的源代码吃了。
被吃的是一篇还没发布的博客草稿,正文 122 行,被清空到只剩 frontmatter。诡异的是元数据整整齐齐:ingested 日期打上了,sha256 戳也打上了,值是 e3b0c442... 开头——熟悉哈希的人一眼就认识它,这是 空字符串的 SHA256。打戳这个步骤执行得完美无缺:算哈希、写字段、做幂等标记,一步没漏。只是哈希的对象没了。编译器给源代码本身盖了个戳,然后把它盖成了空。
正文能救回来纯属侥幸:草稿还没进 git,那 122 行恰好还在当晚的会话上下文里,我逐行重建了它。如果那是三个月前的一篇旧稿呢?
一份蓝图文档
事故之后没多久,我拿到一份内部文档,《Personal Knowledge Base 最佳实践》,26 节。它的核心主张一句话就能说完:
个人知识库不是「存资料的 Wiki」,而是一台 Knowledge Compiler——持续把非结构化世界编译成结构化知识,再为人和 Agent 生产 Context。
Capture → Compile → Retrieve → Use。文档给了十条最终原则,我挑和我的系统对得上号的列在这里:
| 原则 | 文档原意 | 我的系统里的对应物 |
|---|---|---|
| Inbox first | 降低进入成本,分类交给 AI | raw/notes/ 低摩擦入口,103 篇 |
| Raw immutable | 原始资料永远保留,AI 不许覆盖 | 事故现场,下文详述 |
| CPU first, LLM second | 确定性任务不浪费模型 | 检测脚本纯 Python,抽取才上模型 |
| Claim over Chunk | 管理「我知道什么」,不是「我存了什么」 | 468 个概念/实体/对比页 |
| Source always attached | AI 可以总结,但不能让来源消失 | 每页 frontmatter 的 sources 字段 |
| Incremental compilation | 只处理变化的数据 | sha256 幂等戳 + 每晚定时摄入 |
| KB produces Context | 终点不是搜索,是生产 Context | 博客写作时的素材召回 |
说「我的系统」不是攀附。我的 ~/wiki 就是这份蓝图的一个活样本:139 篇原始文件躺在 raw/ 下面,每晚一个定时任务唤起一个小模型(qwen3-8-flash),扫描未摄入的新文件,抽取概念和实体,写进二层页面,最后在源文件里打上 ingested + sha256 戳做幂等标记——下次扫描直接跳过。文档里画的管线,我这边每天夜里都在跑。
所以那份文档里的每一条原则,我读起来都不是建议,是判词。尤其是三行带过的那条。
被三行带过的承重墙
文档的 Principle 2 全文如下:「Raw immutable。 原始资料永远保留。」加上标题三行,翻过去了。
在我的系统里,它是承重墙。墙塌的那晚,三个条件缺一不可,而每一个当时看起来都人畜无害。
条件一:幂等戳写回源文件。 摄入脚本给 LLM 的指令原文是:
Add ingested + sha256 fields INSIDE the source file's
existing frontmatter. Do NOT modify body content.
注意这个设计:编译器的记账(哪些文件处理过了、内容哈希是什么)写在 源文件的 frontmatter 里。对笔记软件这是常规操作——Obsidian 生态人人都这么干,元数据跟着文件走,迁移方便,肉眼可查。「Do NOT modify body content」这行嘱咐也写了,看起来很周全。
条件二:raw 目录里有一条符号链接。 raw/blog 不是普通目录,是指向博客仓库 content/post/ 的软链——为了让编译器能摄入我自己的博文。于是「原始资料目录」和「生产工作目录」在物理上是同一批文件。
条件三:那晚工作目录里躺着一篇未发布的草稿。 没进 git,没有戳,date 还在未来。检测脚本的四层逻辑(无 frontmatter → 新;无 ingested → 新;哈希不匹配 → 重摄;全匹配 → 跳过)看不出「已发布」和「草稿」的区别——没戳的文件,当然是新文件。
三个条件咬合的瞬间,事故就是必然的:LLM 执行「重写 frontmatter」,实现路径是整文件重写,body 在这次重写里被清空,然后新哈希被 正确地 算出来——空字符串的哈希。戳没打错,账没记错,错的是账本长在源代码身上,而记账的人握着整文件重写的笔。
事故链:
未发布草稿 ──(软链)──> 进入编译器视野
│
检测脚本:无戳 = 新文件,摄入!
│
LLM 执行打戳:重写 frontmatter
│ └── 实现 = 整文件重写
↓
body 清空,sha256 = e3b0c442...(空串哈希)
│
草稿未进 git ──> 正文在任何仓库里都不存在
# generated by hugo AI
人类维护笔记的时代,往 frontmatter 写个字段叫 编辑——人手会抖,但人眼会看见。Agent 维护笔记的时代,往 frontmatter 写个字段叫 编译步骤——每晚无人值守地跑,每次重写都携带整文件覆盖的能力,跑错了没有任何人在场。同一个动作,换了执行者,风险等级完全不同。这就是为什么「Raw immutable」在文档里只配三行,在我的系统里配得上一次事故:原则的分量不由篇幅决定,由违反它的代价决定。
记账进账本,签字离正文
教训可以抽成三条,每条都是那份文档里某句话的事故版注释。
一、编译器状态不许住在源代码里。 幂等标记、索引、抽取结果,全是编译器状态。状态应该进编译器自己的账本——sidecar 文件、SQLite、检测脚本自己的库——而不是源文件的 frontmatter。文档说 Knowledge IR 的好处是「模型可以替换、输出可以验证、可以重新编译」,每一条的前提都是:输入没被输出污染过。
二、签字和签字对象必须分离。 我在 评测集是一份没人签字的文件 里论证过 sha256 戳就是签字——没有签字人的评测集不是资产。这次事故是它的镜像案例:签字有了,签在了源代码本身上。签名盖在文件上的那一刻,签名系统就获得了篡改文件的权限。
三、只读不是态度,是挂载方式。 「Raw 永远不要被 AI 覆盖」靠嘱咐是防不住的——指令里明明写了「Do NOT modify body content」,body 还是没了。靠得住的只有结构性约束:编译器对 raw 只有读句柄。检测、打戳、写页面可以自动化到任何程度,写权限的边界必须是文件系统级别的,不能是提示词级别的。
我实际落地的修复有两层。检测脚本加了过滤:博客仓库里 untracked 的文件、date 在未来的文件,一律不进摄入清单。发布流程加了一步:每篇博客发布后立即由发布它的会话打戳提交,下一晚 cron 看到戳就跳过。注意第二层修复的讽刺之处——戳仍然写在源文件 frontmatter 里,改的不是记账位置,是 记账人的身份和时机:从「无人值守的深夜 cron」换成「刚刚亲手发布它、上下文里还有全文的会话」。这能挡住已知事故,但账本长在源代码上的根本结构没变。把账本挪出去,是我还欠这套系统的一笔债。
第二道疤:来源也会丢
文档 Principle 5,「Source always attached:AI 可以总结,但不能让来源消失」,我的系统在这里也交过学费——早期某次摄入产出的页面漏了 source_url 字段,概念页还在,出处没了,一条知识退化成了一句不知该不该信的话。这条后来写进了我的操作清单,和「Raw immutable」并排。
两道疤其实是同一道:编译器每拿走一样东西(正文、出处),系统就少一样能验证自己的东西。 文档第 14 节有个追溯链的图,Claim → Source → Original Document → Original paragraph,一层不许断。断在哪一层,哪一层以上的知识就从「我知道」退化成「我记得好像是」。
终点是 Context,前提是 raw 还是 raw
Principle 10 是全文的收束:「Knowledge Base produces Context——知识库的终点不是搜索,是为人和 Agent 持续生产高质量 Context。」
这句话我完全同意,而且它和我在 日更、10% 和 15 分钟 里写的产品日更是同构的:每晚一次的定时摄入,就是知识层的日更。但日更的前提是增量编译成立,增量编译的前提是哈希可信,哈希可信的前提是——raw 还是 raw。编译器一旦写回源代码,增量判断就被自己的输出污染:你分不清一个文件是「新素材」还是「我上次处理过的残渣」,整条管线从编译退化成自我消化。
所以判断一个个人知识系统成熟不成熟,不用看它抽取多聪明、检索多准、页面多好看。就问一个问题:
编译器出错的那天晚上,原始数据还在不在?
在,是事故;不在,是灾难。我的那次是事故,因为 122 行恰好还活在会话上下文里——这个「恰好」,我不打算再依赖第二次。
那篇被清空的草稿,后来以 Team Bots 最贵的不是 Bot,是那份两年的技能库 的标题发布了。它现在活得好好的。而在我心里,它的 frontmatter 里永远住着一个空字符串的哈希,e3b0c442——编译器付过的学费,值得留个碑。
事实核对自:《Personal Knowledge Base 最佳实践》为一份未公开发布的内部文档(26 节,19856 字节),本文仅引用其框架与原则表述;事故细节(空字符串哈希 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855、raw/blog 符号链接、打戳指令原文、四层检测逻辑)均来自本人 ~/wiki 系统的脚本源码与操作记录;文中系统规模数字(139 篇原始文件、103 篇 notes、468 个二层页面)为 2026-10-03 实测。