---
name: Wiki Build
slug: wiki-build
category: Writing
description: Wiki Build 构建或重建本地知识库，把源文件整理成分层 Wiki 索引、摘要页、实体页和概念页，并维护交叉链接与日志。它适合初始建库、索引迁移和超长源文件的分段构建。
github: "https://github.com/zhuzhaoyun/Molio/tree/main/apps/daemon/src/tools/skills/wiki-build"
language: TypeScript
stars: 241
forks: 35
install: "npx degit https://github.com/zhuzhaoyun/Molio/tree/main/apps/daemon/src/tools/skills/wiki-build ~/.claude/skills/wiki-build"
installs_to: ~/.claude/skills/wiki-build
source_path: apps/daemon/src/tools/skills/wiki-build/SKILL.md
collection_size: 8
category_size: 1060
collection_url: "https://dirskills.com/collections/zhuzhaoyun/Molio"
added: 2026-09-02T05:22:04.743Z
last_synced: 2026-09-02T05:22:04.743Z
canonical_url: "https://dirskills.com/skills/wiki-build"
---

# Wiki Build

Wiki Build 构建或重建本地知识库，把源文件整理成分层 Wiki 索引、摘要页、实体页和概念页，并维护交叉链接与日志。它适合初始建库、索引迁移和超长源文件的分段构建。

**Install:**

```bash
npx degit https://github.com/zhuzhaoyun/Molio/tree/main/apps/daemon/src/tools/skills/wiki-build ~/.claude/skills/wiki-build
```

## README

# wiki-build: 构建 Wiki

wiki 不是一次性的输出，而是一个持续增长的复利资产 — 每次构建、导入、查询都会让它更丰富。

> **维护注记**：wiki-* 五件套（build/query/ingest/save/lint）同版本号共进——改任一 skill 时，五个 `version:` 一起 bump 到同一下一个版本。

## 核心原则

- **源文件不可变**：源文件（notes/、docs/ 等）是用户的原始资料，只能读取，绝对不能修改或删除。
- **密集交叉链接**：[[wiki 链接]] 是这个知识库的核心价值。每个页面都应大量链接到其他相关页面，形成知识网络。宁可多链接，不要少链接——但**只链接最终页面集合里存在的页面**：没有页面的名字在正文中一律用纯文本（或先建 stub 页再链）。收尾对账（deadcheck/linkpass，见"链接对账"节）机械保证零死链、零漏链。
- **合成而非搬运**：wiki 不是源文件的简单摘要集合，而是要提炼出跨源文件的综合观点、论点和洞察。
- **结构化元数据**：每个页面必须带完整的 frontmatter，这是知识网络可查询、可审计的基础。

## Vault 结构

vault 根目录就是当前工作目录。源文件在子目录中（如 raw/、notes/、docs/）。
wiki 相关内容的目录结构：
- `raw/` — 未处理的原始资料目录
- `raw/wechat/` — 微信通道收到的网页、文件等原始资料统一先放在这里
- `wiki/` — 所有 wiki 页面的根目录
- `wiki/INDEX.md` — 根索引，只列目录级概览（各目录页数 + 覆盖范围）与概述入口页，不逐页罗列
- `wiki/<dir>/INDEX.md` — 每个内容目录（sources/entities/concepts/comparisons/questions）自己的索引，列全该目录页面及一句话摘要
- `wiki/log.md` — 按时间顺序记录的操作日志（最新条目在最上面）
- `wiki/hot.md` — 近期上下文缓存（~500 字，每次操作后刷新）
- `wiki/meta/` — 元数据目录（lint 报告等）
- `wiki/sources/` — 源文件摘要页，由 raw/、notes/、docs/ 等原始资料生成；不要把原始资料直接放入这里
- `wiki/entities/` — 人物、组织、工具等实体页
- `wiki/concepts/` — 概念、模式、框架等
- `wiki/comparisons/` — 对比分析页
- `wiki/questions/` — 归档的问答页

构建超长源文件时，确定性过程文件统一放 `.molio/wiki-build/`（文件树扫描会跳过 `.molio`，不污染 vault；跨会话可续传）。

页面路径规则：
- 默认使用单文件页面。**文件名 = 实体/概念的规范名本身**：中文内容用中文名直做文件名（如 `wiki/entities/李白.md`），英文内容用 kebab-case（如 `wiki/entities/molio.md`）。`[[wiki 链接]]` 的链接名必须与目标文件名（去掉 `.md`）完全一致——`[[李白]]` 对应 `李白.md`，写成 `libai.md` 会断链
- 只有当某个实体、项目或主题需要拆成多个稳定页面时，才建立同名目录，并用 `index.md` 作为该目录入口
- 同名目录下的子页面必须围绕该入口主题展开，例如 `wiki/entities/molio/index.md`、`wiki/entities/molio/architecture.md`
- 不要把项目命名空间强行放进错误的内容类型目录；目录首先按页面类型归类，再按主题自然生长

## Frontmatter 规范

每个 wiki 页面必须包含以下 YAML frontmatter：

```yaml
---
type: source | entity | concept | comparison | overview | question | session
title: "人类可读的标题"
created: YYYY-MM-DD
updated: YYYY-MM-DD
tags:
  - 领域标签
related:
  - "[[相关页面]]"
sources:
  - "[[源文件名]]"
---
```

字段说明：
- `type`：页面类型，必须是以上值之一
- `title`：人类可读标题
- `created` / `updated`：创建和最后更新日期
- `tags`：领域标签列表（至少一个）
- `related`：相关页面的 [[wiki 链接]] 列表（尽量多填）
- `sources`：信息来源的 [[wiki 链接]] 列表（source 类型页面填原始文件名，其他类型填参考了哪些 source 页面）

## 页面类型说明

根据源文件的内容、规模和领域，自行决定最合适的页面类型。以下是参考：
- **source**（源文件摘要）— 每个源文件一个摘要页，提取关键信息，放在 `wiki/sources/`
- **entity**（实体）— 人物、组织、工具等命名实体，放在 `wiki/entities/`
- **concept**（概念）— 关键概念、想法、模式、框架，放在 `wiki/concepts/`
- **comparison**（对比）— 相关概念或方法之间的对比分析，放在 `wiki/comparisons/`
- **overview**（概述）— 当源文件较多（≥5 篇）且跨多个主题时创建，提炼跨源文件的核心论点

目录结构应从内容中自然生长，不要强行套用固定模板。如果源文件只有几篇且主题集中，扁平结构就够用。

## 建页粒度

哪些实体建页、建什么样的页，按**信息落点判断，不按实体类别**。判据只问三个**结构量**，与语料领域无关——同一套规则适用于小说、史书、技术文档、论文任何语料；不要按"某类东西该建/不该建"的领域直觉分类（人物/地点/器物/概念在各自语料中的重要度完全不同，预置类别会把管线锁死在一种语料上）：

1. **覆盖深度**：它出现在几个独立区段/章节（跨度）。可机械统计（census/segments）。
2. **独立信息量**：去掉它，已有页面是否仍能完整表达同等信息。若它的全部内容可并入一句描述 → 没有独立落点。
3. **引用需求**：其他页面是否把它的名字当作**讨论对象**、而不是行文背景来提及（判法见下）。

分三档：

**完整页**（任一满足）：
- 出现在章节标题/目录中（强信号，必有内容可写）
- 能用 `grep -nF 名字` 取到足够上下文写出有实质内容的独立描述（首次出现 + 身份归属 + 至少一个关键事件/关系），且**跨度≥2 区段**。跨度=1 的内容通常不足以当独立页——除非它承载了不可并入其他页的关键信息。

**stub 页**（内容撑不起完整页，但有**引用需求**）：
- 格式：完整 frontmatter + `stub: true` 标记 + 一句话身份定性（必须 grep 取证，不靠记忆）+ 关键关系链接，3-8 行即可
- 判据只有一个：**其他页面会主动链接它**（它是文本中反复被"讨论"的话题，即使篇幅小）。判断方法见下一节"讨论对象 vs 背景挂点"。
- 图谱/问答链路需要这些中间节点（别处提及时钟链到它）；缺了就是死链或漏链。

**不建页**（噪音）：
- 通用词、尊称、无名字的泛称（"某某的娘""某某家的"这类）→ 收纳进相关页的一句话或概念页表格行
- **仅作行文背景、不被讨论**的名字（判据见下）→ 正文出现用纯文本，**禁止**建页、**禁止**在正文中以 `[[ ]]` 出现
- 判定启发式：**跨度=1 且内容可并入其他页面的一句话** → 不建页（信息已被承载，单独立页是重复，只会产生一张空洞的 stub 图）

### 讨论对象 vs 背景挂点（领域无关的二元判据）

对正文里每个候选名字的每一处提及，问：**它是被"讨论"还是被"用作背景"？**
- **讨论对象**：句子以它为主语/宾语在陈述它的属性、行为、关系（"**X** 在 N 处做了某事，因其 Y 而被 Z 所知"；读者查它是因为它本身构成话题）。此类提及即使零散，也指向实体。
- **背景挂点**：它只出现在方位/工具/附属语境里，删掉或换成泛指后句子语义不变（"某事发生在 P 处""用 T 完成了……""安置于 Q"——P/T/Q 是描述其他实体的背景，读者查的是句子的主体而不是它）。

同一句话里两者可并存（"发生在 R 的那个事件"——R 是背景；"事件本身"是讨论对象）。逐处判定时要看**这句话的语义中心**，不是按候选名所属的领域类别划分。拿不准时用**删除测试**：把该名字从所有出现处换成纯文本/泛指，若任何页面的可读性都不受损 → 背景挂点，不建页；若某处去掉后话题悬空（读者会问"它到底是谁"）→ 有引用需求，建 stub 都值得。

不同源文件的粒度不同，按各自内容特征独立判断。census 频率是排序信号，不是建页阈值。**建了 stub 不代表必须留着**：收尾时对入度=0 的 stub 做孤儿审计（见步骤 3），确认是背景挂点误建就删除/降级为纯文本。

## 索引分层结构（根 INDEX + 各目录 INDEX）

索引固定分两层：**根 INDEX 只给目录级概览**（与页面总数解耦，始终几十行），**每个内容目录有自己的 INDEX.md 列全该目录页面**。检索层层递进：根 INDEX → 相关目录 INDEX → 页面。根索引若逐页罗列，页面一多就会长到 runtime 一次读不完（Read 上限 2000 行），整个问答链路随之失效——这是分层的原因，不要因为"库还小"而退回单索引。

**根 `wiki/INDEX.md`** 格式：

```markdown
# Wiki 索引

> 一句话全库概述 + 页面总数构成（源摘要 N · 实体 N · 概念 N · 对比 N …）

## 概述
- [[概述页]] — 一句话摘要（入口页，直接内联列出）

## 目录
- [[sources/INDEX|源文件摘要]]（N 页）— 覆盖主题一句话概览 + 2-3 个代表条目名
- [[entities/INDEX|实体]]（N 页）— 人物/组织/工具；代表条目名
- [[concepts/INDEX|概念]]（N 页）— 覆盖领域；代表条目名
- [[comparisons/INDEX|对比分析]]（N 页）— 有哪些对比
- [[questions/INDEX|归档问答]]（N 页）— …（有页面时才列）
```

**各目录 `wiki/<dir>/INDEX.md`**（sources / entities / concepts / comparisons / questions）格式：

```markdown
# <分类名>索引

- [[页面]] — 一句话摘要
```

规则：
- 概述页（通常 1-2 篇、全库入口）直接在根 INDEX 内联，不为 overview/ 单建目录索引
- `meta/` 是元数据目录（lint 报告等），不建索引、不列入根 INDEX
- 目录 INDEX 列全该目录页面（wikilink + 一句话摘要）；条目多时用 `##` 子标题分组（sources 按主题、entities 按人物/组织/工具、concepts 按领域）
- 目录索引统一大写 `INDEX.md`，与多页主题目录的小写 `index.md` 入口页区分
- 单目录索引几百行 Read 一次可读完，不做二次分片

## 索引分层迁移（已有 wiki）

存量库若是旧单索引布局（根 INDEX.md 直接以 `- [[页面]] — 摘要` 逐页罗列、内容目录无 INDEX.md），**不重建页面**、只重构索引：

1. `find wiki/ -name '*.md'` 一次，建「页名 → 所在目录」映射
2. 读旧根 INDEX.md，每条页面条目按映射分流写入对应目录的 INDEX.md（保留原摘要；条目多则 `##` 分组）
3. 根 INDEX.md 按上节分层格式改写（概述条目内联，其余目录各一行：目录链接 + 页数 + 覆盖范围）
4. log.md 记一条 `index | 索引分层迁移`

触发：用户说「重构索引 / 索引分层 / 索引迁移」。过程幂等——目录索引已存在即跳过。wiki-ingest / wiki-save 在旧布局库上首次触发时也会自动执行本流程（见各自 SKILL.md）。

## wiki/log.md 格式

最新条目在最上面：
```markdown
# 构建日志

## YYYY-MM-DD HH:MM | build | 初始构建
- 扫描源文件数：N
- 创建页面数：N（按类型列出）
- 关键发现：一句话概述
```

## 超长源文件处理（prep + curation + 建页 + 对账）

源文件若无法在一次上下文内通读（通读后还要留空间规划+生成+交叉链接），不能按"读全文→语义识别"抽取实体，必须走本节的管线：确定性预处理把机械活（转码、切章、频率普查）做完，agent 只做语义工作（curation 审核、取证、撰写、链接）。

**判断超长**：`wc -c 源文件` > 1.5MB（约 50 万中文字），或 Read 工具一次读不完（默认上限 2000 行）。未超长的源文件直接通读建页，不必走本管线。

### 步骤 0：断点检查 + 构建权（每次进入必做）

```bash
ls .molio/wiki-build/ 2>/dev/null
node "<skill_dir>/scripts/prep.mjs" status <源文件> --vault .
node "<skill_dir>/scripts/build-lock.mjs" acquire "build-<x>-<日期>" --vault .
```

- 若已有 `curation-<x>.tsv` / `batches/`：这是**续传**——不要重跑 prep/curation，直接从断点继续建页。
- 同一 vault 一次只允许一个构建会话。label 用 "build-<源文件主名>-<日期>" 固定，整个构建过程（含断开续传）都传同一 label。异 label 锁 → exit 1，不要抢。
- 构建结束时：`build-lock.mjs release "build-<x>-<日期>" --vault .`

### 步骤 1：预处理（确定性，零 LLM）

```bash
node "<skill_dir>/scripts/prep.mjs" <源文件> --vault . [--profile <name>] [--charset <编码>]
```

- **profile 选择**：prep 自动检测（检测到章节结构会选 `novel` 并在 stderr warnings 注明）；你若通读源文件开头后判断不同（如它其实是访谈实录/史料），用 `--profile default` 覆盖。自定义 profile 可放 `scripts/profiles/*.json`。**注意**：若 prep 回退到 default 且 candidates 为 0，说明 profile 不匹配——必须定制 profile 而非继续。
- **编码**：GBK/Big5 等自动探测转码；若 warnings 报有损解码或 PUA 字符，用 `--charset` 指定后重跑。
- 产物（`.molio/wiki-build/`）：`transcode-<x>.txt`（行号稳定地址）、`segments-<x>.json`（分段+范围）、`census-<x>.json`（频率普查+别名线索）

### 步骤 2：L1 章节 digest（覆盖全文）

每个处理范围（segments.json 的一个 range，≤15 万字）派一个 subagent，只 Read 自己范围的行号区间，输出 `digests/R###.md`（实体+定性+事件+行号+别名线索）。**digest 文件落地 = 该范围完成的唯一信号**。主 agent 不通读全文。

### 步骤 3：curation（信息落点判据，脚本预填 + agent 审核）

这是整条管线的**核心决策环节**——决定建哪些页、什么类型、怎么分批。

```bash
# 3a. 脚本预填：census → 草稿 TSV（机械映射，零 LLM）
node "<skill_dir>/scripts/curate.mjs" draft <x> --vault .

# 3b. agent 审核草稿：按「建页粒度」判据筛选、分组、标注页类
#     产出: curation-<x>.tsv（审核后的最终版）

# 3c. 脚本分批：校验格式 + 按 # cat= 分组 + 单批超 15 条自动拆分
node "<skill_dir>/scripts/curate.mjs" split <x> --vault .
```

**批次 TSV 格式**（制表符分隔，每行 5 列）：
```
# cat=<类别标签>
名字	定性	别名: X/Y/Z	证据行号: N,N,N	页类: entity|concept
```

**agent 审核时做什么**（脚本做不了的语义判断）：
- 按「建页粒度」的信息落点判据筛选：删噪音、删背景挂点、保留有引用需求的
- 按主题分组（`# cat=帝系` / `# cat=概念与制度页` 等）——**批次即类型标签**
- 每行标注 `页类: entity` 或 `页类: concept`
- 补全别名（从 census.aliasHints / digest 中认领）
- 补全证据行号（代表性行号，不是全量）

**脚本预填做什么**（机械部分，agent 不需花 token）：
- 按 count 排序、过滤低频噪音
- 预填唯一匹配的别名
- 预填证据行号（census 有则填，无则留空）

**批次规模**：留白为主（按主题自然分组），脚本兜底（单批超 15 条自动拆分）。

### 步骤 4：建页（每批一个 subagent）

每个批次派一个 subagent，读批次 TSV 该批行 + transcode 取证 → 写 `drafts/<名字>.md`。

- **一个 subagent 只做一批，即读即写**，不要预先规划多个批次（防 32k 输出上限）
- prompt 按 `页类` 给模板：concept 页写综合论述，entity 页写身份+事件+关系
- 建页 agent 只能链接**批次 TSV 里出现的名字**（白名单），清单外用纯文本
- 高频名取证封顶：首现 + 均匀采样，总量 ≤30 条
- **并发 ≤5-6 个 subagent**（防 API 429 限流）；完成度以磁盘落盘为准，不信通知
- 全部批次完成后跑**落盘对账门禁**（硬性，exit 1 即中止）：
  ```bash
  node "<skill_dir>/scripts/verify-drafts.mjs" <x> --vault .
  ```
  - 校验批次 TSV 声明的每个名字在 `drafts/` 有对应文件且非空
  - 治"subagent 输出超限被截断后静默缺失"和"报告写了卫青、文件实际不存在"——超限与漏写最终都表现为"批次声明了但没落盘"，一个门禁统一定位
  - 缺页 → 补建后重跑，直至 exit 0

### 步骤 5：安置（确定性，零 LLM）

```bash
node "<skill_dir>/scripts/place.mjs" <x> --vault .
```

- 按批次 TSV 的 `页类` 列分发：entity → `wiki/entities/`，concept → `wiki/concepts/`
- 生成/更新对应目录的 INDEX.md（摘要自动提取）
- 幂等可重跑；中途打断后续传只需重跑 place

### 步骤 6：对比页策划（主 agent 创作，数量不固定）

实体/概念页安置完成后，主 agent 从已建页面中识别**值得横向对比的主题**，写 comparison 页到 `wiki/comparisons/`（**由主 agent 直接写，不派 subagent**——数量少、需全局视野，subagent 没有这个上下文）：


- **数量由知识网络结构决定，不设固定数**：有多少值得对比的主题就写多少——有天然对比关系（同类的并置、竞争、沿革、对立）才写，宁缺毋滥；没有就不写。不同语料的对比主题多寡差异很大，以实际建出的页面网络为准，不预设数量。
- **判断标准**：两个及以上实体/概念之间存在可比维度（成败、风格、制度沿革、思想对立），且对比本身能产出超越单页的新洞察——单纯罗列不是对比，不写。
- 每页带完整 frontmatter（`type: comparison`）+ 交叉链接到被对比的页面，并更新 `wiki/comparisons/INDEX.md`。
- 这一环节是 agent 的收尾创作，不是从 census 候选筛出来的，所以不在批次 TSV 里。

### 步骤 7：链接对账（硬性门禁，exit 0 才算通过）

```bash
node "<skill_dir>/scripts/linkpass.mjs" --vault . --batches .molio/wiki-build/batches
node "<skill_dir>/scripts/deadcheck.mjs" --vault .
```

- `linkpass --batches`：从批次 TSV 的别名列读取别名映射，把每个页面名/别名在其他页面正文中的首次出现包成 `[[ ]]`。**庙号/别名死链在这一步自动消解**（如"项王"→`[[项羽|项王]]`）
- `deadcheck`：exit 0 才算通过。有死链 → 补页或改写链接，重跑直至 exit 0

### 步骤 8：引文核验（确定性，零 LLM）

```bash
node "<skill_dir>/scripts/sweep.mjs" <x> --vault .
```

- 全量 verify + **missing 分类**：`wikilink`（链接误报）/ `blockquote`（整行提取噪音）/ `pua`（转码差异）/ `real`（真错）
- **只修 `real` 类**：写进 `rules.json` 的 `repair.repl`，跑 `repair.mjs` 双份修复（wiki + drafts）
- 其他三类是机械噪音，不需要修

### 收尾

- 创建/更新根 INDEX.md + log.md + hot.md
- `build-lock.mjs release`
- 汇报（页面数必须用 `find wiki -name '*.md' | wc -l` 实时统计，禁止凭记忆报数）

多源文件场景：每个源文件各一套过程文件，逐个走完整管线；链接对账是全库级的，所有源文件安置完后跑一次即可。

## 操作步骤

1. **扫描**：读取 vault 中所有源文件（跳过隐藏文件、wiki/ 和 .molio/ 本身），了解全貌，用 `wc -c` 标出超长文件
2. **断点检查 + 构建权 + 预处理**：超长文件先走"超长源文件处理"步骤 0→0.5（build-lock acquire）→1；普通文件通读
3. **规划**：列出打算创建的页面清单和类型，确定目录结构
4. **检查已有 wiki**：如果 wiki/ 已存在，先读根 INDEX.md + 相关目录的 INDEX.md 了解已有内容，只新增/更新，不重复创建；若发现是旧单索引布局（根 INDEX 逐页罗列、无目录索引），先按「索引分层迁移」重构索引再继续
5. **生成页面**：普通文件逐个建页；超长文件按 L1 digest → L2 主表+建页的分层流程执行（merge-master 合并、batcher 分片、G1/G2 门禁）
6. **完成自检**：`prep.mjs status` 确认目标层级达标 + **孤儿 stub 审计**，未达标继续补建
7. **引用抽查**：`sweep.mjs` 全量 verify → `repair.mjs`（rules.json 修复表）修正失准引文，重跑直至归零
8. **链接对账**：`linkpass.mjs` 补漏链 → `deadcheck.mjs` 门禁，**必须 exit 0**；有死链先补 stub 或改写再重跑
9. **创建/更新根 INDEX.md + 各目录 INDEX.md**（分层结构，目录行页数在此统一重算，stub 页也要列入）、**log.md**、**hot.md**
10. **收尾**：`build-lock.mjs release`（释放构建权）
11. **汇报**：页面数量、核心论点摘要、剩余知识缺口（从不被链接的 L3 长尾；高频实体应已在自检中补全）；汇报必须包含"死链 0（deadcheck 通过）"

页面内容要全面但简洁，优先保证准确性。
