逐条取证下来,291 页知识页里有 241 页缺 type。补一个键即可——不必为"合规"重构知识库。
okf.py 站在哪一环来源只读 → 引擎编译 → 流水线补齐并体检 → 各消费者读取。这张图真正要说的是最后一层:哪些字段有人读,哪些没有。
窄屏可左右滑动查看完整图形
这张图要说的其实是最后两行。
前三层是机制,末层是账。okf.py 注入的 type,目前全库没有任何程序在读——build-index.py / build-wiki-site.py / indexlib / searchlib / 三个 Dataview 看板,读的都是 entity_type、类别、dimension、decision_type,没有一个读 type。
所以对「要不要补齐」这个问题,诚实的回答是:补它今天产出为零;真正划算的是顺带做成的 okf.py --check(引擎领地此前零校验)与 stale_after 链路——而那两件都不需要 OKF。留着 type 的唯一理由是成本≈0 的互操作期权。已在 dev-log 记了 2026-11-03 复核点:届时仍无消费者就考虑撤回注入,免得它成为一段伪装成特性的死代码。
OKF 是 Google Cloud Knowledge Catalog 提出的中立知识格式:markdown 正文 + YAML frontmatter 表示一条知识,一个目录就是一个 bundle。它刻意做得薄。
| # | 硬性要求 | 本次改造 |
|---|---|---|
| 1 | 每个非保留 .md 有合法 YAML frontmatter | ✅ 管 |
| 2 | 每个 frontmatter 的 type 非空 | ✅ 管 |
| 3 | index.md / log.md 结构符合 §8-9 | ❌ 另议(见第 7 节) |
index.md 与 log.md,它们不得有 frontmatter——本库早就是这么用的entity_type、类别、decision_type 全都可以原样留着,不构成不合规先克隆内容库实测,不是读代码猜。
| 目录 | 页数 | 已有 type | 现状字段 |
|---|---|---|---|
_wiki/concepts/ | 211 | 0 | entity_type: concept|technique|claim |
_wiki/summaries/ | 8 | 0 | source / source_type / compiled_at |
_wiki/outputs/ | 15 | 10 | 部分页已有 |
material/ | 35 | 19 | 中文键 类别 |
reports/ | 21 | 21 | 全有(生成时就写) |
_wiki/entities/ | 0 | — | 引擎尚未产出 |
| 合计 | 291 | 50 | 缺 241 |
index.md / log.md 早就是保留文件、未知键本来就到处都是。若不取证而按直觉"对齐规范",很容易走成改名重构——那会砸掉三个现成消费者(见第 4 节)。
okf.py:体检与注入两个互斥模式:只读体检,与幂等注入。
python3 scripts/okf.py --check # 只读体检,发现不合规页 → 非零退出
python3 scripts/okf.py --fix # 幂等注入缺失字段
python3 scripts/okf.py --check --json
_wiki/material/reports/_wiki/under_review/ 查询原始产物,确认后才提升入库writing/ 写作成稿,不是知识页raw/ browse/ site/ docs/ scripts/index.mdlog.mdtype 的值从目录 + 已有键推出,同样输入永远同样输出。
已有目标键就一个字节都不写。不幂等的话每轮编译都改文件——git 每天一堆空 diff,还会触发引擎的 reconcile churn。
已有 type 的页一律不碰(reports 全部、outputs 十来页)。
type 从哪来:确定性映射目录决定大类,已有键提供更细的值。概念页那一行是关键:entity_type 本来就是 OKF 的 type 语义,搬过来即可。
| 路径 | type | 取值来源 |
|---|---|---|
任意目录下的 CHANGELOG.md / README.md | changelog / readme | 文件名(最先判) |
_wiki/concepts/ | concept / technique / claim | 直接搬 entity_type,缺失才回落 concept |
_wiki/summaries/ | summary | 目录 |
_wiki/entities/ | entity | 目录 |
_wiki/outputs/decisions/ | decision | 目录(比 outputs/ 更具体,先判) |
_wiki/outputs/ | output | 目录 |
material/ | material | 目录 |
reports/ | report | 目录 |
| 其余 | note | 兜底 |
entity_type 有两个现成消费者(建索引、生成浏览站),类别 有 Obsidian 属性面板在读,decision_type 是决策队列状态机的输入。改名等于为了合规砸自家管线。material/README.md 被 material/ 前缀吃掉、拿到 type: material。测试抓到后追到"分支排序"这个根因,而不是给 README 打特例补丁。编译流水线从五步变六步,okf.py 占两个位置。
_wiki/index.md——它读 frontmatter 生成build-index.py 只读 entity_type / concept / sources / source / 标题|title,并不读 okf 注入的 type / stale_after,顺序目前对索引没有影响。保持顺序的理由换成防御性的:一旦索引哪天开始消费 type,顺序反了会读到旧字段,而那种 bug 极难察觉。
assert i_fix < i_idx, "注入应在重建 index 之前(防御性约定)"
assert i_chk > i_idx, "体检应在 lint 记账阶段(index 之后)"
--fix,每轮自动重放。所以别手动补 type,跑 --fix 就行。_wiki/{summaries,concepts,entities} 由编译引擎全权负责、人不要手改。okf.py 不是"手改",它是流水线的一环,在引擎写完之后确定性地补齐字段。stale_after:把保鲜机制接上 OKFOKF 有个 stale_after 键表示"此页何时应视为过期"。本库早就有等价机制(半衰期保鲜),只是没写成 OKF 的形状——现在把两者接上。
| 声明 | 半衰期 | 适用 |
|---|---|---|
volatility: high | 30 天 | 行业信号、新闻流蒸馏、在办事项的行动清单 |
volatility: medium | 90 天 | 厂商/监管对比、随底层页漂移的综合产物、工具实践 |
volatility: low | 365 天 | 变化很慢的判断 |
half_life_days: N | N 天 | 显式覆盖,优先于 volatility |
last_confirmed: 2026-07-16 —— 确认"此页仍代表现状"的日期stale_after: 2026-08-15 —— okf.py --fix 算出0.5 ^ (距今天数 / 半衰期)不追踪的页不塞这个键。没声明 volatility / half_life_days 的页面一律不碰,与既有语义一致:不加字段就不参与追踪。
⚠️ 一条如实的记录
这套保鲜机制代码早就在、lint 里也跑着,但上线后很长时间零采用——全库没有一页声明过这三个字段,所以 stale_after 首次跑出来是 0 页。后来挑了 5 页真会过时的产出页打标(2 页 high、3 页 medium),链路才真正跑起来。
这是本项目反复撞见的那条:约束写在文档里等于没有约束;想让规范被遵守,就为它写可执行检查。
last_confirmed 取写作日期,不是打标当天字段语义是"上次人工确认此页仍代表现状"。没逐条核对过就盖当天,等于伪造一次人工确认,还会把首次复核平白推迟一个半衰期。改造之前,写集校验的作用面里没有引擎领地——引擎写出什么都没人看。
# validate_write_set.py 改造前的作用面
LLM_SCOPE = ("_wiki/outputs/", "material/", "writing/", "reports/")
# ↑ _wiki/summaries、concepts、entities 不在里面 = 零门禁
| 改造前 | 改造后 | |
|---|---|---|
| 引擎领地校验 | 无 | okf.py --check(非零退出) |
| 合规页 / 总页 | 50 / 291 | 291 / 291 |
| 幂等复跑改动 | — | 0 页 |
stale_after 覆盖 | 无此键 | 已声明保鲜的页自动写回 |
体检(改造前) 291 页,241 页待补 退出码=1
注入 291 页扫描,241 页更新
体检(改造后) 291 页,0 页待补 退出码=0
幂等第二遍 291 页扫描,0 页更新
写集校验 0 败
index.md / log.md 的结构。本库这两个文件各有现成生成器和现成格式,强行对齐要动生成器、收益不明,留待另议。这是刻意的取舍,不是遗漏。