内容审计手册
治理元数据 | Owner:知识库管理员 | 最后评审:2026-10-28 | 下次复核:2027-01-31
这份手册解决一个问题:知识库最容易烂在「结构正常但内容不对」——链接通、构建过、页面在,但某一节讲的是别的意思,或某个数字与源头对不上。自动检查看不出这类问题,人眼逐篇读又不现实。
下面把审计拆成四层,每层给出「机器能做什么 / 人必须做什么」,并配可复跑的命令。
一、先看五道闸门在管什么
pnpm run check 串了五个脚本,覆盖「结构、文本、口径、算术、组件」五个维度。
| 闸门 | 脚本 | 抓什么 | 抓不到什么 |
|---|---|---|---|
| 文本卫生 | check-text-hygiene.mjs | H1 里的 ~/_ 分隔符、代码块多余转义、纯文本引用、标题 emoji | 内容是否正确 |
| 一致性 | check-consistency.mjs | 死链、锚点、表格列数、表格被引用块截断、自指链接、正文指纹重复、Checklist 标题复读、中英结构差异 | 表述是否准确 |
| 指标权威 | check-metrics.mjs | 同一指标在非权威篇重述阈值 | 阈值本身是否合理 |
| 数字复算 | check-numbers.mjs | 公式与结论不自洽(105 条断言) | 参数取值是否现实 |
| 组件默认值 | check-component-defaults.mjs | 工具组件默认值与正文基准不一致 | 工具逻辑是否正确 |
关键认知:这五道闸门只能保证「不自相矛盾」,不能保证「说得对」。后者必须人读。
二、四层审计方法与复跑命令
第 1 层:标题 ↔ 正文
要查什么:标题声称的内容(几个、什么事)在正文里是否落实。
机器能做的:数结构。标题说「六大杠杆」,就数这一节有几个 H3 / 加粗列表项 / 表格数据行。
# 数量承诺抽查(129 处标题含「N 大/项/步/层」)
python3 - <<'PY'
import pathlib,re
CN={'一':1,'二':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9,'十':10}
QTY=r'([一二三四五六七八九十]|\d+)\s*(大|项|步|阶|层|类|种|档|级|条)'
for p in pathlib.Path('docs').rglob('*.md'):
s=str(p)
if '/en/' in s or '/public/' in s or '.vitepress' in s: continue
inf=False
for l in p.read_text(encoding='utf-8').split('\n'):
if l.strip().startswith('```'): inf=not inf; continue
if inf: continue
if not l.startswith('## '): continue
q=re.search(QTY,l)
if q: print(f"{s[5:-3]} {l[3:][:50]} → {q.group(1)}{q.group(2)}")
PY人必须做的:读每一节的「标题 + 首句 + 骨架」,判断是否跑题。
教训:试过三种统计法全部失效——
方法 结果 为什么不能用 中文 bigram 余弦相似度 中位数 0.04 标题短正文长,字面重合天然极低 标题概念在正文的命中率 595 节里 481 节「命中率 0」 分词切出「全球销售模块子文」这类半截词 小节用词归属模块 208 节「不像本模块」 01 总控、02 产品本就跨职能 跑题是语义问题,字面统计测不出来。 这一层只能人读,机器只做辅助筛选。
第 2 层:表格 ↔ 结论
要查什么:表格算出的数是否支撑正文紧接着下的定性判断。
机器能做的:定位「表格 + 带数字的结论句」配对。
# 找「表格后紧跟带数字结论」的 44 处
python3 - <<'PY'
import pathlib,re
pat=re.compile(r'(✅|❌|达标|通过|未达标|仍高|仍低|高于|低于|结论|因此)')
for p in pathlib.Path('docs').rglob('*.md'):
s=str(p)
if '/en/' in s or '/public/' in s or '.vitepress' in s: continue
lines=p.read_text(encoding='utf-8').split('\n')
inf=False; i=0
while i<len(lines):
l=lines[i]
if l.strip().startswith('```'): inf=not inf; i+=1; continue
if inf: i+=1; continue
if l.strip().startswith('|') and i+1<len(lines) and re.match(r'^\s*\|[\s:|-]+\|\s*$',lines[i+1]):
j=i+2
while j<len(lines) and lines[j].strip().startswith('|'): j+=1
for k in range(j, min(j+7,len(lines))):
t=lines[k].strip()
if t and pat.search(t) and re.search(r'\d', t):
print(f"{s[5:-3]}:{k+1} {t[:100]}")
break
i=j; continue
i+=1
PY人必须做的:判断结论与表格是否同一基准。
典型陷阱(本库真实案例):
现象 真相 LTV:CAC 1.45 判「⚠️ 踩线」 相对放行线 1.2 是刚过,相对稳态线 3.0 是远不达标——两个基准混着说 赠品上限「不超过 MAP 限价 5%」+ 举例 19.95 按 MAP 是 16.96,按 MSRP 才是 19.95,同一赠品包两种口径结论相反 利润率 18.5% EBIT 按净营收 367.08 算,但百分比除以 MSRP 399;按净营收除是 20.1%
第 3 层:跨篇数字引用
要查什么:引用他篇的数值时,数值对不对、口径带没带上。
机器能做的:锁定核心数字 → 找出所有引用点。
# 锁定 11 个核心数字,列出它们在哪些篇出现
python3 - <<'PY'
import pathlib,re
CORE={'73.69':'07-finance/07-06-pl-model','88.69':'07-finance/07-06-pl-model',
'789':'07-finance/07-06-pl-model','367.08':'07-finance/07-06-pl-model',
'156.52':'07-finance/07-06-pl-model','108.34':'06-service/06-04-retention',
'34.65':'06-service/06-04-retention','1,908':'03-supply/03-04-order-calculator',
'1,057.8':'03-supply/03-04-order-calculator','2,860':'05-sales/05-02-01-kickstarter-deposit',
'71,500':'05-sales/05-02-01-kickstarter-deposit'}
docs={}
for p in pathlib.Path('docs').rglob('*.md'):
s=str(p)
if '/en/' in s or '/public/' in s or '.vitepress' in s: continue
docs[s[5:-3]]=p.read_text(encoding='utf-8')
for val,src in CORE.items():
hits=[r for r,t in docs.items() if val in t and r!=src]
print(f'{val:9} 权威={src.split("/")[-1]:34} 其他篇 {len(hits)}: {hits[:4]}')
PY人必须做的:逐条核语义(不只是数字相同,还要口径一致)。
为什么不能全自动:试过三种方案都失败——
- 「引用他篇 + 上下文有数字」→ 1002 条,噪声 90%
- 「引用 + 关系词 + 数字」→ 97 条,混大量索引表
- 「引用 + 该句数值 + 他篇比对」→ 163 条,同句常引多篇,归属不清
最后靠「锁定核心数 → 找引用点 → 人工核口径」收敛到 34 处。
第 4 层:表格纵向自洽
要查什么:同一张表里,逐行加减是否等于结算行。
机器能做的:全部(只要有明确公式关系),已写成 105 条断言。
node scripts/check-numbers.mjs # 105 条,覆盖 5 张关键表已覆盖的表:
| 表 | 验证内容 |
|---|---|
| 07.06 P&L 主表 | 5 条链路 × 3 情景 = 15 项 |
| 03.05 PSI 折损链 | 1,060 → 1,007 → 1,002 → 1,000 |
| 03.04 首批资金占用 | 三项加总 + NRE,含上下限 |
| 06.03 RMA 五科目 | 占比/MSRP%/金额三个合计 + 五项单项 |
| 05.02.01 众筹漏斗 | 4 波次 → 买家/金额/加权 ASP |
教训:验结算表必须逐行验。曾只验 EBIT 一行,而毛利行有两处错误(公式关系搞错 + 负号当正号)恰好互相抵消,最后一行「看起来对」,导致误判整表正确。
三、五道闸门的失败处置
| 报错 | 常见原因 | 处置 |
|---|---|---|
H1 含 ~ / _ 分隔符 | 从飞书导出时转义未还原 | 改本地 H1,再 push 飞书同步节点标题 |
表格分隔行列数不一致 | 手写表格漏一列 | 补齐分隔行,列数必须等于表头 |
表格被引用块截断 | 在表格中间插了 > 说明 | 把说明移到表格之后 |
Checklist 条目是标题复读 | 自动生成器产物 | 改写成可执行动作 |
中英结构不一致 | 只改了一边 | 补另一边,或确认是否故意不同 |
指标阈值越权重述 | 非权威篇写了数值+比较符 | 改为「见 00.02 §五」,或登记进 EXEMPT_LINES |
关键数字复算未通过 | 参数变了但结论没跟 | 同步改结论;若参数本身错,改参数 |
正文完全相同 | 批量脚本写错文件 | 从 git 历史恢复,见下 |
三个已踩过的坑
坑 1:HTML 注释会被飞书抹掉
不要用 <!-- metric:exempt --> 做行内豁免。飞书 markdown 导入会丢弃 HTML 注释,一次 sync 后标记就没了,CI 会突然失败。豁免登记在脚本的 EXEMPT_LINES 里(版本可控)。
坑 2:批量脚本写成「跑完了」不等于「写对了」
去 emoji 脚本曾把 03.01 的正文整篇写成 01.07 的内容。页面还在、链接不断、构建通过,但内容已经没了。现在有「正文指纹重复」闸门兜底。
批量脚本必须按「文件 → 期望内容」逐条验证。
坑 3:先推飞书,再跑 sync
顺序反了,飞书旧版会覆盖本地改动。
# 正确顺序
python3 /tmp/push_doc.py docs/<篇>.md <obj_token> # 1. 推飞书
node scripts/sync-feishu.mjs # 2. 回拉校验
git add -A && git commit && git push # 3. 提交部署四、什么时候该停下来
四层都跑完之后,剩下的只有两类问题,都不在审计范围内:
| 类型 | 例子 | 为什么机器做不了 |
|---|---|---|
| 业务判断 | 毛利率红线定 35% 还是 50%、NPS 健康线 50 是否合理 | 没有对错,只有取舍 |
| 外部数据真伪 | 印尼 VAT 实际税率、各国平台费率 | 需要当地网络与官方渠道 |
判断标准:如果一个问题可以问「这个数是从哪来的」,那它属于外部数据;如果必须问「这个数定多少合适」,那它属于业务判断。两者都不该指望自动检查。
五、审计记录
历次审计的详细发现与修复,见 AUDIT-CHANGELOG(仓库根目录)。按轮次编号 3.1 ~ 3.31,每轮记录「发现了什么、怎么修的、加了什么闸门」。