我给自己那个本地知识库打了两年多的分。

9 月 10 日晚上,它拿到 9.3 / 10。理由很充分:23 条死链、10 篇笔记缺 frontmatter、5 个收件箱积压、一个战略主题零覆盖。

三个小时后,同一套方法、同一套权重、同一批命令,再算一遍:9.7 / 10

这三个小时里,我没有往库里加一行新知识。只是把已经烂掉的地方挖出来扔了。

但真正的故事不在 9.3 到 9.7。真正的故事是——第一份写着"9.7"的报告里,有一处举证是不实的,而且是复核的时候才发现的。

这是"本地 AI 知识库实践"系列的第五篇。前面四篇讲的是怎么搭(从零搭建本地 AI 知识库)、怎么更聪明、怎么让数字可信(卡片索引层)、2.0 时代改了哪些东西(知识库 2.0 升级全解)。这一篇讲的是怎么证明它真的好——以及为什么"它说自己好"这句话本身不可信。

本文全部数字来自 2026-09-10 的现场实测命令,可复现。你的库不必有相同的数字,方法相同即可。

一、三个小时:完整时间线

整件事从当天晚上 22:47 开始,到 23:58 结束。

时间动作结果
22:47第一次审计(六维评分模型)9.3 / 10,三项 P1/P2 弱点
23:00–23:18弱点清零执行死链 23→0、缺 frontmatter 10→0、收件箱积压 5→0
23:18–23:30复审(同方法、同权重)9.7 / 10,九项门槛全过
23:31–23:45独立复核(另一个 Agent 重跑全部指标)采信 9.7,但发现 1 项举证不实 + 2 项需修正 + 4 项缺口
23:40–23:58缺口全量执行7 项动作落地,向量块 121,951 → 122,120
收尾稳态复核死链 0 / frontmatter 0 / 漂移 0 / 积压 0 / 回归 21/21

注意中间那一行——复审和独立复核是两个不同的动作,由两个不同的角色做。

  • 复审:执行方自己再算一遍,确认清零生效。
  • 独立复核:换一个角色,不采信对方的任何中间值,用自己的命令把 16 项指标全部重跑一遍。

分数最终没有变。变的是分数的成分

二、为什么"自评 9.7"这句话不能直接信

先说结论:那份报告的核心数据零失真。 16 项举证里,13 项精确复现,2 项数值一致,1 项不实。9.7 这个结论成立。

但发现不实项的过程,才是这一节的重点。

规则只有一条:不复用对方的任何数字,全部用自己的命令现场重跑。

被复核的报告声称,它在"治理与回滚证据"里列出了"交接文件 145 行的时间线"。复核时的实测是:

1
2
3
4
5
wc -l HANDOFF.md
# → 1531

grep -n "2026-09-10" HANDOFF.md
# → 无输出

那份交接文件实际 1,531 行,而且关于当天的工作一条记录都没有。当天有 12 个实质工作块,按项目自己定的规矩,“每次变更必须更新交接文件"是硬性要求。

所以这条证据不该出现在报告里。它被删除了,缺口由复核方当场补上。

这里有一个可复用的规律,我把它当成这次复盘最直接的收获:

能跑命令的指标容易诚实,靠"我做了"的叙述性指标容易失真。

16 项举证里,13 项是可以用一条命令跑出来的——死链数、向量块数、回归通过率、索引文件大小。这些数字全部精确复现,一项都没错。

唯一不实的那一条,恰恰是最难被随手验证的类型:一个关于"另一份文件写了什么"的叙述。

这不是道德问题,是成本结构问题。跑一条命令的成本是 3 秒,编一个数字的成本也是 3 秒——但核对的成本完全不同。所以:复核的力气应该优先压在叙述类举证上,而不是重复重跑那些本来就诚实的数字。

三、和上一版最大的区别:从「建能力」到「治质量」

如果只看功能列表,会以为 9 月这版和 8 月那版没有太大差别——还是那 2.7 万篇笔记、还是那套检索、还是那些卡片。

差别在目标上。

维度2.0(8 月)3.0(9 月)
目标让知识库能回答业务问题让知识库能证明自己是健康的
重心加能力:卡片索引层、多跳检索、看板保质量:审计 → 复核 → 清零 → 跨周期稳定
关键产物2.3 万张标准答案卡 + 回归题库六维评分模型 + 九项门槛 + 五态分离
谁来验自己写、自己跑、自己看生产者与复核者分离:一方清零,另一方独立复跑
判据功能有没有跑通数字能不能被第三方复现
典型失败能回答,但答案不可信可信,且不可信的地方被明确标出来

一句话概括这个转变:

2.0 关心"知识库能不能用”;3.0 关心"知识库说自己能用的时候,这句话可不可信"。

为什么会走到这一步? 因为库的输出开始被用在不能出错的场合——报价、对账、谈判。

在这些场景里,“数字是多少"和"数字可不可信"是两个问题,而后者更贵。一个能秒回采购金额的系统,如果偶尔捞回一份去年的快照,它带来的损失比"查不到"大得多——因为查不到你会去翻源文件,查到了错数字你不会。

这个转变也不是自发发生的。它是被反复教育出来的——见多 Agent 协同治理那篇里那些跨 Agent 的来回。当库里有五个 AI 在读写、每个都说"我做完了”,“做完"这三个字就必须被定义成可验证的状态,而不是一种说法。

于是才有了 3.0 的核心机制:五态分离

状态含义
source_written文件写进磁盘了
miyo_visible语义层能读到它了
rag_indexed进了向量索引了
codex_acceptance被独立验收了
complete真正完成了

这五个是不同的状态。把它们混成一个"完成”,是绝大多数"我明明做了"事故的源头。

四、具体优化了什么:7 处改动,逐条对上

这一节按"问题 → 改动 → 验证"三段式写,每条都带可复现的验证方式。

1. 评分脚本的口径分裂——同一个脚本给出两个答案

问题:质量记分卡的内部函数被单独调用时,会报告库里有约 3,200 条死链;而权威扫描器同时报告 0 条。同一台机器、同一个库、同一分钟。

根因是:内部那条计算路径没有剥离代码块。文档里写示例时用的 [[链接]],在它眼里就是真链接——而我的库里到处是这种示例。

改动:让内部函数直接取权威扫描器的结果;另设一个 deadlinks_internal 字段显式标注"这是非权威口径";并给扫描结果加一层缓存,避免同一个进程里把全库扫两遍。

验证:直接调用内部函数 →

1
2
3
deadlinks = 0                              # 权威值
deadlinks_internal = 3227                  # 显式标注为非权威
deadlink_metric_source = "authoritative"   # 来源标注

为什么这条最重要:它不是一个显示问题,是一个跨 Agent 的误判源。任何 Agent 直接调这个函数,过去都会得到"库里有三千条死链"的错误结论——然后花几个小时去"修"一个不存在的病。

2. 模板被"转义"消死链,代价是模板不能用了

问题:为了消掉模板文件里的死链,之前的做法是把 [[笔记名]] 改写成 \[\[笔记名\]\]。死链确实没了,但模板的用途就是"复制出来改"——转义之后,用户复制到新笔记里得到的是字面反斜杠

改动:模板恢复原形;在死链扫描器里加一条豁免规则——文件名以 _template 结尾的不参与死链扫描,在扫描、修复、结构扫描三处同步生效。

验证

  • 全库 wikilink 总数 78,647 → 78,653(模板里的链接重新计入总数),而死链仍然是 0
  • 修复模式空跑:修改文件 0 个

3. 一条链接被改成纯文本,静默抹掉了"待建"信号

问题:另一条死链的消法是把它改成纯文本([[X]]「X(笔记待建)」)。死链没了,但"这条笔记还没建"这个信号也一起没了。

处置:这一条我在复核时候改了判断。原来的结论是"应该给它建一篇占位笔记",但先查了一下这个目标到底存不存在——结果发现它早就存在,有一篇 20KB 的完整笔记,只是文件名不完全一致。

所以它根本不是"待建",是错链

改动:把链接指向那篇真实笔记。

验证:3 条新链全部解析,死链 0

4. 备份覆盖面小于实际改动面

问题:报告称"改动前备份 5 个文件,可回滚"。但这一轮实际触达了 11 个以上文件——另外 6 个(补 frontmatter 的、移动归位的)没有备份。

改动:补齐 5 件,并附一份带回滚命令的说明。

验证:备份件数 5 → 10

教训“可回滚"这句话必须用文件计数验证,不能凭感觉。 你在备份的时候脑子里想的是"我改的那几个文件”,而实际改动面永远更大。

5. 报告自己和自己矛盾

问题:报告给"链接与结构完整性"维度打了 满分 10.0;同一份报告的"保留项"章节里,却白纸黑字写着"这两维的满分与综合 10 分均不授予,因为单日修复未跨周期稳定"。

改动:链接维 10.0 → 9.9,治理维 9.6 → 9.5,综合分 9.695 → 9.675——一位小数仍然是 9.7。

这一条我单独拎出来,因为它是评分通胀的典型指纹,下一节展开。

6. 两组都对的数字被并列摆在一起

问题:报告里写"死链 23 → 0",而持久化记分卡同期的真实基线是"死链 3 → 0"。两组数都对——一个含代码块示例和非标准范围(宽口径),一个不含(权威口径)——但并列摆出来,会让读者以为它们是同一条口径链上的首尾。

改动:加基线口径注脚,明确写"两组数都对,口径不同,不可混用或相减"。

7. 单日数据不等于稳定数据

问题:所有指标在今天一天之内归零。但评分模型自己有一条:满分需要跨周期证据

改动:不授予满分,明确标注观察窗口,等下一帧(第二天自动任务 + 周五例行审计各一帧)再评。

另有 P3:手动跑一次向量索引增量同步,让当天新增的内容当日进索引——而不是等到第二天中午的定时任务。结果:121,951 → 122,120 个向量块,回归 21/21

五、提升在哪:6 个数字 + 3 件看不见的事

能看见的

指标审计前收尾
死链(权威口径)230
缺 frontmatter100
标签漂移00
收件箱积压50
未分类的待建实体0(26 项全部分类)
向量块121,951122,120
检索回归21/2121/21
健康状态🟡 RED🟢 green
综合评分9.39.7

看不见的(其实更重要)

1. 少了一个跨 Agent 的误判源。 第 1 条改动之后,任何 Agent 拿到这台机器上的评分脚本,都只会看到真实数字。这一条的收益不在今天的分数里,在未来每一次别人复用这个脚本的时刻。

2. 模板重新可用。 转义改豁免,模板回到了"复制即用"。这是把一次治标换成了治本——同样死链归零,但一个留下了疤,一个没有。

3. 堵住了一条未来的自动回退路径。 见下一节第 5 条。如果没发现它,今天的努力每周五会被自动回滚一次,而且不报错。

六、六个反直觉发现

这一节是整篇里最值得带走的。

1. 能跑命令的指标容易诚实,靠"我做了"的叙述容易失真

16 项举证,13 项精确复现——全部都是能用一条命令跑出来的数字

唯一不实的那一条,恰恰是唯一一条叙述性的、无法用一条命令验证的。

行动含义:复核不要把力气花在重算那些本来就诚实的数字上。把 80% 的复核时间压在那几条"我说我做了 X"上,逐条去找对应的物理文件。

2. 同一份报告自相矛盾,是"评分通胀"的指纹

数字算错,可能是手滑。

但一边给满分、一边在保留项里写"满分不授予"——这不是手滑。这说明评分是先有结论,后凑证据

识别方法:把报告的"评分表"和"保留项/局限说明"两张表并排看。如果评分表里的高分项,在保留项里被自己否掉了,那就是膨胀信号。这个信号比任何单个数字错误都更值得警惕。

3. 修脏数据有三条路,只有一条不欠债

手法做法欠什么
转义[[X]]\[\[X\]\]可用性——模板不能复制了
建占位建一篇空笔记真实内容——库里多了一篇空壳
豁免扫描器跳过这个文件不欠

但选手法之前,有一句话必须先问:

这个目标到底存不存在?

  • 存在 → 是错链,改指向
  • 不存在 → 是待建,建占位或登记为"延后"
  • 是模板/示例豁免

我在同一天里,把这三类中的前两类都判错过一次。判错的代价不是死链没消掉,而是在错误的题面上做优化——比如给一篇早就存在的笔记去建占位。

4. 口径分裂的修复,往往只修了"刚好出事的那半边"

第 1 条改动里,有一段逻辑是特判:只有在死链数等于 0 的时候,才用权威值覆盖内部值。

也就是说:死链是 0 的时候,一切正常;死链一旦大于 0,两个口径又分裂回去了。

为什么会写成这样?因为出事的时候,死链恰好是 0。修复的人(也就是我)把注意力放在了"让当前这个状态显示正确"上。

行动含义:凡看到 if X == 0: 形式的特判修复,都要追问一句——“X 大于 0 的时候怎么办?” 这类修复改一个分支,等于把陷阱推到另一个分支。

顺带一条:改这段代码时还得先数一数,那个被处理的字符串变量有几个消费者。它同时喂给 4 个不同的检测逻辑,全局处理会静默改掉另外两个跟本次无关的指标。最小侵入的做法是用局部副本

5. 改脚本前,必须读关联自动化的原文——不能只看名字

这是本次最值钱的一次发现。

在把"模板豁免"落进脚本之前,我做了一件事:把当天所有在跑的定时任务列出来,逐个读它们的 prompt 正文

然后在另一个任务里找到了真冲突:

有一个"每周五 16:00 全面复盘与质量审计"的任务。在它自己的正文里,写着这么一条动作:发现死链 → 运行修复脚本。

那个修复脚本对某一类死链的动作是什么?往文件里注入一行 <!-- 待建: X --> 注释。

所以:如果我只按照原方案把模板转义、不动脚本本身——每周五晚上这个任务会自动把模板重新污染一遍。

不会报错。不会告警。只会安静地、周期性地把你刚修好的样板改坏。

而它藏在另一个任务的正文里。只看任务名字(“每日死链扫描 + 修复”,听起来人畜无害)永远发现不了。

行动含义:任何一次"改脚本",前置动作不是看代码,是把全部定时任务列出来,逐条读 prompt 原文,标出哪些会写回同一个文件

6. 最容易腐烂的不是代码,是文档里的具体路径和阈值

同一天,我把索引架构迁移成了"成对目录 + 符号链接指针"。

同一天,我自己的操作手册里的回滚步骤,还指向旧路径

旧路径停在当天 12:15 的状态,而生产已经跑到 23:49。照着那份手册回滚,会静默退回到几个小时前——而且看起来完全成功。

架构变了,回头扫一遍所有引用它的文档——这一步没有任何工具会提醒你。代码有类型检查、有测试、有报错;文档没有。

行动含义:改架构时,同步的清单里除了代码,必须包含"grep 一遍所有文档里对旧路径/旧阈值的引用"

七、如何一次性达成这样的结果

先说清楚:这一节不是"我这里是怎么做的"的复述。9 点到 10 点,我是踩了坑才做对的。下面是事后整理出来的、可以一次做对的路径

三个前置条件(缺一个都做不成)

1. 一份写下来的评分模型。

维度、权重、阈值、满分条件,白纸黑字。没有它,“9.7"就只是一个感觉,而且是一个会和昨天、和别人的数字混着用的感觉。

2. 五态分离。

写入了 / 可见了 / 进索引了 / 被验收了 / 完成了——五个不同的状态。混成一个"完成”,就是"我明明做了"事故的母体。

3. 生产者与复核者分离。

清零的人和复核的人不能是同一个。这不是信任问题——是因为一个人对自己刚做完的事情,判断力会系统性下降。你会本能地重跑那些本来就会过的检查,然后跳过那些可能不通过的。

四条硬规则

规则具体动作反面
复核必须重跑不复用对方任何中间值,用自己的环境跑全部命令“报告说 21/21,那应该就是 21/21”
力气压在叙述类举证可跑指标快速复现,叙述类逐条找物理文件把复核时间花在重算分数上
修复前读自动化原文列出全部定时任务,逐条读 prompt 正文只看任务名字判断有没有冲突
修完必问副作用消死链时有没有抹掉待建信号?改脚本时有没有别处会回退它?“死链归零就行了”

一次性检查清单

可以直接照抄:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[ ] 1. 评分模型落盘:维度 / 权重 / 阈值 / 满分条件
[ ] 2. 列出全部定时任务,逐条读 prompt 正文,标出会写回本库的
[ ] 3. 先做一次宽口径审计拿基线,再做一次权威口径审计拿终值
[ ] 4. 每个指标都用一条可复现的命令定义,并把命令写进报告
[ ] 5. 改动前备份,然后数一数:备份件数 vs 实际触达文件数
[ ] 6. 每一项修复,先回答"目标存在吗",再选手法(改指向 / 建占位 / 豁免)
[ ] 7. 修复后重跑全部命令逐项对表;口径不同的数字并列时必须标注
[ ] 8. 换一个执行者独立重跑一遍,不采信对方的任何中间值
[ ] 9. 检查报告内部一致性:评分表 vs 保留项,结论 vs 证据
[ ] 10. 跨周期再取一帧,才谈"稳定"

第 8 条是最容易被省掉、也最不该省的一条。如果你只有一个 AI 可用,最低成本的替代方案是:换一个模型、或者开一个不带上下文的新会话,只给它命令清单和报告原文,让它独立跑一遍。 不带上下文这件事本身就是价值——它不会重复你的思路,只会重复你的命令。

八、边界:这套做法适合谁,不适合谁

不适合:

  • 只有几十篇笔记的库。 打开目录看一眼就知道了,上评分模型是过度工程。
  • 没有任何人在用的库。 质量审计的前提是"有人在依赖它"。一个没人问的库,满分没有意义。
  • 不想写文档的人。 这套东西的核心资产不是脚本——脚本随时可以重写——是那份写下来的评分模型,和每一次的报告。判断标准丢了,就再也重建不回来了。

适合:

  • 有几百篇以上笔记、AI 在里面频繁读写、且**已经出现过"我以为改了其实没改"**的库。
  • 有多个 AI 或多人协作、需要"完成"这个词有确定含义的场景。
  • 准备把知识库的判断结果用于真实决策(报价、对账、谈判)的场景——这种时候,“数字可不可信"比"数字是多少"重要。

再回到开头那三个小时。

9.3 到 9.7 是三个小时。但支撑这三个小时的,是前面两个月攒下来的东西:一份写下来的评分模型、一套五态分离的状态定义、一个每天自动跑的记分卡、一条"生产者与复核者分离"的规矩。

那 0.4 分不是那三个小时挣来的。

那三个小时只是——第一次把这套东西,完整地用了一次。


延伸阅读(本地 AI 知识库实践系列):