LLM Wiki 机制学习指南
本指南只讲一件事:LLM Wiki 这套系统本身是怎么运转的——它的输入是什么、处理过程是怎样的、输出是什么。不涉及 raw 里的具体业务数据内容。
LLM Wiki 是一个 「知识编译器」:它把杂乱的原始资料(raw)作为源代码,用一个受严格提示词约束的 LLM Agent 作为编译器,产出结构化、相互链接、可持续增量更新的知识库产物(docs),最后发布到 iWiki 供人阅读。
为什么要"编译"知识?(Karpathy 范式)
知识应当被提前编译成结构化、互链的持久产物并不断累积复利,而不是每次查询时让模型从原始文档重新理解。用一句业界流传的类比:RAG 是解释型语言(每次现场解释源码),LLM Wiki 是编译型语言(提前编译成高效产物,运行时直接用)。
| 传统 RAG / 每次现读 | LLM Wiki(提前编译) | |
|---|---|---|
| 知识处理时机 | 查询时——每次提问都重来 | 摄取时——每个源只处理一次 |
| 知识积累 | 不积累,每次从零"重新发现" | 随每个源和查询复利增长 |
| 算力/Token | 每次都加载大量原始文档 | 读索引即可导航,Token 大幅下降 |
| 基础设施 | 需要向量库、embedding 管道 | 零基础设施,纯 Markdown + Git |
| 矛盾/缺口 | 难以显式追踪 | 元数据显式记录 contradictions / open_questions |
| 维护者 | 系统(黑盒) | LLM(透明、可编辑、可 diff) |
注:上表中"Token 大幅下降 / 零基础设施 / 复利增长"等结论来自 Karpathy 原始范式及社区实践的公开讨论,本节末的起源章节给出出处。
Karpathy 范式起源(外部背景补充)
本仓库的系统提示词开篇就引用了「Karpathy 的 LLM Wiki 范式」。这一节基于公开互联网资料,补充这个范式的来龙去脉,帮助你理解本仓库设计背后的原始思想源头。本节内容来自外部公开资料,非本仓库代码。
起源事件
| 项目 | 内容 |
|---|---|
| 提出者 | Andrej Karpathy(OpenAI 联合创始人、前特斯拉 AI 负责人、"vibe coding" 一词提出者) |
| 时间 | 2026 年 4 月初,一条推文 + 一个 GitHub gist("LLM Wiki" idea file) |
| 热度 | 约 1500 万浏览、4.8 万转发、8.8 万收藏,引爆 AI 社区 |
| Karpathy 原话 | "using LLMs to build personal knowledge bases for various topics of research interest"(用 LLM 为各研究主题构建个人知识库) |
| 思想渊源 | 可追溯到 Vannevar Bush 1945 年的 Memex 构想(《As We May Think》)与 Luhmann 的 Zettelkasten 卡片盒笔记法——它们共同的瓶颈是"谁来维护",而 LLM 恰好补上了这个"不知疲倦的知识管理员"角色。 |
Karpathy 的经典类比与人机分工
Karpathy 原始三层架构
| 层 | Karpathy 原始定义 | 目录名 |
|---|---|---|
| 第一层 | Raw Sources 原始来源,不可变、LLM 只读,是"事实来源" | raw/ |
| 第二层 | Wiki LLM 生成的 markdown(摘要/实体/概念/对比/综述),LLM 完全拥有 | wiki/ |
| 第三层 | Schema 配置文件,"最关键的部分",把聊天机器人变成守纪律的维护者,充当跨会话持久记忆 | CLAUDE.md / AGENTS.md |
INGEST(摄取,最重要,单次波及 10–15 页)、QUERY(查询,好答案可归档回 wiki 形成复利)、LINT(健康检查:矛盾/陈旧/孤儿页/缺口)。部分实现还加入 MERGE(合并高度重叠页面)。
本仓库 vs Karpathy 原始范式(关键改造)
本 llm-wiki-overseas 仓库继承了范式的骨架,但为"企业级 + 多源自动拉取 + 自动发布"做了显著工程化改造。理解这些差异,是理解本仓库设计取舍的关键:
| 维度 | Karpathy 原始范式 | 本仓库(overseas) |
|---|---|---|
| 知识库目录名 | wiki/ | docs/(语义相同) |
| Schema 载体 | CLAUDE.md 等配置文件 | 独立的系统提示词(Knot Agent 的 system prompt) |
| 源的录入 | 人工把文件拖进 raw/ | fetch_raw.py 自动拉取 iWiki/Gongfeng,带版本账本 _fetch_state.json |
| 交叉引用语法 | [[wiki-link]](Obsidian 双链) | 标准 markdown 链接 [标题](path.md)——因为要发 iWiki,双链不渲染 |
| 浏览方式 | Obsidian 图谱视图 + Dataview | 发布到 iWiki(sync 两遍推送 + 反向引用体系) |
| 状态管理 | 基本靠 log.md | 两份账本(fetch + compile)+ 双提交回填 compiled_commit |
| 增量判定 | 较隐式,靠 LLM 判断 | 显式 join 两份账本,按 version/blob SHA 精确判 stale |
| 页面类型 | concept/entity/source-summary/comparison | + synthesis,共 6 类,且每类有严格 frontmatter schema |
| 调用方式 | 自然语言 prompt | 结构化 mode × operation(rebuild/incremental × ingest/query/lint) |
| 矛盾追踪 | Flagged for review | 引用块显式标注 + frontmatter contradictions 双记录 |
Karpathy 原始范式是面向个人、手动、Obsidian 本地浏览的轻量方案;本仓库把它升级为面向团队、自动拉取多源、结构化状态管理、自动发布 iWiki的工程化生产系统。核心思想(三层架构 / 编译而非检索 / ingest-query-lint / 复利积累)一脉相承。
适用边界(来自社区实践)
| 适合 LLM Wiki | 仍需 RAG / 混合架构 |
|---|---|
| 精选、稳定、高价值的知识(约百量级源) | 超大规模、超出上下文窗口的语料 |
| 个人研究库、团队核心知识、Agent 长期记忆 | 高度动态实时数据(行情、新闻流) |
| 零基础设施、可 Git 版本化的场景 | 需要严格权限/并发/审计的企业场景 |
出处:Karpathy LLM Wiki gist(gist.github.com/karpathy/442a6bf…);社区解析文章(agentpedia.codes、掘金 coderMax《从知识检索到知识编译的范式革命》等,均为 2026-04 公开发布)。数据如"Token 减少 ~95%、比 RAG 高效 ~70 倍"为社区实践估算,非本仓库实测。
编译器类比(贯穿全文的主线)
系统提示词开篇即自我定义为「raw→LLM Wiki 编译器」。用传统编译器的概念来理解这套系统,一切都会变得清晰:
| 传统编译器概念 | LLM Wiki 对应 | 本指南章节 |
|---|---|---|
| 源代码 .c/.java | raw/ 原始资料(iWiki 文档、Gongfeng 代码) | 输入① |
| 源文件时间戳/依赖 | _fetch_state.json(源版本、SHA、拉取状态) | 输入② |
| 语言规范 + 编译选项 | 系统提示词(Schema 层,定义全部规则) | 输入③ |
| 编译命令 make / gcc -O2 | 调用指令(mode + operation + input) | 输入④ |
| 编译器本体 gcc/clang | Knot Agent(受提示词约束的 LLM) | 处理器 |
| 增量编译(只编改动的) | incremental 模式 + join 两份账本判定 stale | 增量判定 |
| 全量重编 make clean && make | rebuild 模式(枚举全部源重编) | ingest |
| 目标产物 .o / a.out | docs/ 结构化知识页面(6 种类型) | 输出① |
| 构建缓存 .d / timestamps | _compile_state.json(已编译版本/commit) | 输出② |
| 构建日志 build.log | docs/log.md(append-only 活动记录) | 输出③ |
| 链接 + 部署 | sync_to_iwiki.py 两遍推送到 iWiki | 输出④ |
输入 / 处理 / 输出 全景图
接下来分三大段展开:输入 讲清楚"喂给编译器什么",处理 是核心——讲清楚"编译器内部怎么工作",输出 讲清楚"产出了什么、长什么样"。最后用一个端到端实例把三段串起来。
INPUT输入总览:四类输入
LLM Wiki 编译器接收四类输入。前两类是"要编译什么",第三类是"按什么规则编译",第四类是"这次编译做什么"。
make 的目标和选项。① raw/ 原始资料 —— 输入契约(只讲格式,不讲业务内容)
raw/ 是编译器的源代码目录。作为学习机制,你只需理解它的输入契约:有哪几类源、以什么格式落盘、如何被 Agent 读取。
| 源类型 | source_id 主键格式 | 落盘形式 | 元信息来源 |
|---|---|---|---|
| iWiki 文档 | iwiki:{docid} | 带 YAML frontmatter 的 .md 文件 | frontmatter 内(title/version/url…) |
| Gongfeng 单文件 | gongfeng:{repo}:{ref}:{path} | 原始文件内容(不加工) | 从 _fetch_state.json 读 |
| Gongfeng 目录 | gongfeng_dir:{repo}:{ref}:{path} | 整个目录镜像 | 从 _fetch_state.json 读 |
raw/ 由拉取脚本 fetch_raw.py 独占写入,Agent 永远只读、绝不修改 raw。这保证了:任何时候都能追溯"知识来自哪句原文",编译结果可复现。学习机制时,你不需要关心 raw 里具体写了什么业务(日志、归因……),只需知道它是"输入语料"。
iWiki 源的 frontmatter 契约(拉取时自动生成)
---
source_id: iwiki:4017234012 # 全局唯一主键
url: https://iwiki.woa.com/p/4017234012
title: "(源文档标题)"
version: 9 # 版本号 → 增量判定的关键信号
content_changetime: 2026-02-10 10:24:28
fetched_at: 2026-07-15
status: raw # 标记这是未编译的原始层
---
(下面是原文正文,Agent 阅读的对象)
② _fetch_state.json —— 拉取账本(编译的触发信号)
这是编译器的"源文件时间戳表"。Agent 只读它来判断哪些源发生了变化。关键字段:
{
"schema_version": 3,
"docs": [{
"source_id": "iwiki:4017234012",
"source": {
"type": "iwiki",
"version": 9, // iWiki 用整数版本号比较
"content_hash": "sha256:04f3..." // 正文规范化后的哈希(防"版本变但内容没变")
},
"fetch": {
"state": "fresh", // pending|fresh|changed|fetching|error|removed
"fetched_at": "2026-07-15"
}
}]
}
版本信号不是整数,而是 Git blob SHA(内容哈希)。Agent 用字符串等值比较:SHA 不等即视为源变化。目录源则对全部文件清单算一个稳定哈希作为整体版本。
③ 系统提示词 —— Schema 层(最关键的输入)
同样一个 LLM,喂不喂这份提示词,行为天差地别。它就是编译器的"语言规范 + 编译选项",规定了:三层架构、两份账本主键、六种页面类型、每种页面的 frontmatter schema、ingest/query/lint 工作流、双提交流程、以及所有硬约束红线。复现 LLM Wiki,这份提示词是最不可替代的核心资产。
Agent 的自我定位(提示词原文)
「你是『raw→LLM Wiki 编译器』,一个有纪律的 wiki 维护者,不是普通聊天机器人。你完全拥有并维护 docs/ 知识库层;人类只负责选材、提问、把方向,繁琐的摘要、交叉引用、归档、记账全部由你完成。」
提示词定义的三层架构(编译器视角)
| 层 | 内容 | 可变性 | 谁拥有 |
|---|---|---|---|
| Layer 1 | raw/ 原始资料(源代码) | 不可变,事实来源 | fetch_raw.py(Agent 只读) |
| Layer 2 | docs/ 知识库(编译产物) | Agent 完全拥有,禁止人工编辑 | Knot Agent |
| Layer 3 | 系统提示词(Schema/规则) | 定义结构与工作流 | 就是这份提示词本身 |
④ 调用指令 —— 告诉编译器这次做什么
每次调用必须显式指定 mode,与 operation 正交组合:
# 格式:mode(rebuild|incremental)+ operation(ingest|query|lint)+ 输入
mode=rebuild operation=ingest # 全量重建(初始化/规范大改时)
mode=incremental operation=ingest source_id=iwiki:4028672179 # 增量摄入(日常默认)
mode=incremental operation=query "某某字段怎么查?" # 查询,可选 --archive 归档
mode=incremental operation=lint # 健康检查,可选 --fix
| ingest 摄入 | query 查询 | lint 健康检查 | |
|---|---|---|---|
| rebuild 全量 | 枚举全部源逐个编译,末尾 Lint 收敛,重写 index/overview | — | rebuild 末尾的一致性收敛 |
| incremental 增量(默认) | 只编译候选(stale)源,只动受影响页面 | 读库作答,有价值则归档 | 扫矛盾/孤儿页/断链,报告 + 可选修复 |
PROCESSING处理器:Knot Agent
处理器 = Knot 平台上的 LLM Agent + 系统提示词。它是整个系统里唯一的"编译器本体"。下面按照它内部的处理流程逐步拆解。
处理第一步:增量候选判定(join 两份账本)
增量编译的核心问题是:"哪些源需要重新编译?" Agent 通过同时读两份账本来判定,这正是编译器"增量编译"的本质——对比源文件时间戳和产物时间戳。
源的当前版本
已编译到的版本
按 source_id
运行时派生,不落盘
满足任一即为候选:
- _fetch_state.json 里
fetch.state ∈ {pending, changed, error} - _compile_state.json 里
compile.state ∈ {never_compiled, stale, compile_error},或该 source_id 在编译账本中缺失
stale 怎么算:iWiki 用 source.version 整数比较(compiled_version < 当前 version → stale);Gongfeng 用 blob SHA 字符串等值比较(不等即 stale)。stale 是运行时派生态,不单独存储——这样 fetch 脚本无需懂编译逻辑,Agent 无需懂拉取逻辑,彻底解耦。
处理第二步:ingest 编译流程(核心)
确定候选源后,Agent 对每个源执行 ingest。这是把"源代码"翻译成"知识产物"的核心翻译过程:
- 读取源与状态:从 _fetch_state.json 找到候选 source_id 对应的 raw 文件路径,读取原文。
- 通读原文提炼:完整读原文,提炼 3–5 条关键要点(key_claims)。不臆造,原文没有的不写。
- 产出/更新 source 摘要页:每个源对应一篇
docs/sources/summary-{slug}.md,一一对应,记录 source_id/version/key_claims/confidence。 - 抽取实体与概念:识别原文中的实体(服务/平台/接口/库表…)和概念(机制/方法/术语…),创建或更新
entities/*.md与concepts/*.md。 - 建立交叉引用:新页面至少引用 2 个现有页面(标准 markdown 链接),形成知识网络。
- 标注矛盾:若新源与已有页面冲突,用引用块显式标注
> 矛盾标注(contradiction):…,来源 sources/summary-x.md,并写入 frontmatter 的 contradictions,绝不悄悄覆盖旧结论。 - 归类 cluster:给新页面打主题分组标签;若出现新分组,登记到 index.md 与 overview.md。
- 更新系统页:更新 index.md(主目录,按主题分组)、sources/index.md(源清单)、log.md(追加操作记录)。
- 双提交闭环:先提交 docs 产物(Commit A)拿到 hash,再把 hash 回填进 _compile_state.json 单独提交(Commit B),详见 双提交工作流。
提示词规定:单次 ingest 通常触及 5–15 个 wiki 页面;每个源提炼 3–5 条关键要点。失败情况用 !failure 标签写入 log.md,避免下次重复踩坑。
query 处理流程(读取 + 可选归档)
query 是"用已编译的知识库回答问题",并可把有价值的新分析反哺回知识库:
- 定位:读 index.md 找到相关页面入口。
- 下钻:读相关概念/实体页,必要时进一步下钻到 sources/ 摘要页核对证据。
- 带引用作答:回答时标注信息来自哪些页面。
- 可选归档(--archive):若问答中产生了有价值的横向对比或纵深分析,归档为
comparisons/*.md或syntheses/*.md,标记filed_from_query: true。
lint 处理流程(一致性检查)
lint 是编译器的"静态检查/警告",扫描知识库的结构健康度:
| 检查项 | 标准 | --fix autofix |
|---|---|---|
| 断链 | 所有 .md 相对链接目标必须存在 | 仅报告 |
| 孤儿页 | 每页 ≥2 条入链(系统页除外) | 仅报告 |
| H1 缺失 | 正文首行必须是一级标题 | 仅报告 |
| cluster 缺失/不一致 | frontmatter cluster 与 index.md 分组一致 | 自动修复(只改 frontmatter) |
| 回链缺失 | synthesis/comparison 引用的概念页需有回链 | 自动补回链 |
| 过时声明 | fetch.version 与 compiled.version 不一致 → 标记 stale | 报告为 ingest 待办 |
| 矛盾未标注 | 不同源冲突必须有 contradictions | 不自动选边 |
编译质量约束(红线,保证产物可信)
| 红线 | 规定 |
|---|---|
| 不改输入 | 永不修改 raw/ 与 _fetch_state.json(那是输入,编译器无权改) |
| 只写编译账本的 compile 段 | 只允许更新 _compile_state.json 的 compile 字段 |
| 忠实原文 | 绝不臆造;raw 没有的不写;不确定设 confidence: low |
| 不删页面 | 永不删除页面,改为 frontmatter 标记 deprecated |
| 矛盾显式化 | 用引用块 + frontmatter 双重记录,不悄悄覆盖 |
| 强制交叉引用 | 新页面至少引用 2 个现有页面 |
| 三件套同步 | 每次改 docs 都更新 index.md、sources/index.md、log.md |
| 无 emoji / 中文为主 | 全文不用 emoji;专有名词/接口名/类名保留英文 |
OUTPUT输出总览:四类产物
① docs/ 知识库结构
六种页面类型(产物的"类型系统")
| type | 目录 | 产出什么 | 命名 | 由哪种操作产出 |
|---|---|---|---|---|
| source | sources/ | 每篇已摄入 raw 的摘要(1:1 对应) | summary-{slug}.md | ingest |
| entity | entities/ | 人/团队/平台/服务/接口/工具/库表/组件 | {name}.md | ingest |
| concept | concepts/ | 业务知识/数据链路/方法/术语/机制 | {name}.md | ingest |
| comparison | comparisons/ | 若干对象的横向对比 | {slug}.md | query 归档 |
| synthesis | syntheses/ | 围绕一个主题的纵深综述 | {slug}.md | query 归档 |
| 系统页(非内容页):index.md / overview.md / sources/index.md / log.md —— 随每次操作更新 | ||||
frontmatter:产物的机器可读元数据
每个产物页面头部都有 YAML frontmatter,这是知识图谱的"符号表"。以 concept 页为例:
---
type: concept
title: "概念名"
aliases: [别名, 缩写] # 供 query 命中
sources: [sources/summary-x.md] # 溯源:这页知识来自哪些源摘要
related: [entities/entity1.md] # 交叉引用(≥2)
created: 2026-07-15
updated: 2026-08-03
confidence: high | medium | low # 置信度
cluster: "{主题分组}" # 与 index.md 分组一致
contradictions: [] # 与其它源的冲突
open_questions: [] # 未解决的缺口
---
source 页:额外有 source_id / source_type / source_version / key_claims。entity 页:额外有 entity_type(person/team/platform/service/api/tool/table/component)。comparison / synthesis 页:有 filed_from_query: true,related 必须回链概念页。
互链网络:产物的核心价值
- 只用标准 markdown 链接
[标题](相对路径.md),绝不用 Obsidian 双链[[..]]——iWiki 不渲染双链会退化成纯文本;标准链接才会进入 iWiki「本文被引用」反向引用体系。 - 每个新页面 ≥2 条交叉引用——避免孤儿页,让知识形成网络而非孤岛。
- 正文第一行必须是 H1
# 标题——sync 用首个 H1 作为 iWiki 文档标题,缺失会退化成文件名。
② _compile_state.json —— 编译状态输出
这是编译器写回的"构建缓存",记录每个源已编译到什么程度,供下次增量判定:
{
"schema_version": 3,
"docs": [{
"source_id": "iwiki:4017234012",
"compile": {
"state": "compiled", // never_compiled|stale|compiling|compiled|compile_error|ignored
"compiled_version": 9, // 已编译到的源版本(与 fetch.version 比较判 stale)
"compiled_commit": "63394f7", // 产出这批 docs 的 Commit A hash(双提交回填)
"compiled_at": "2026-07-15",
"error": null
}
}]
}
③ docs/log.md —— 操作审计输出
每次操作追加一条结构化、可 grep 的记录,只追加不删除:
## [2026-08-03] ingest | 摄入 {源标题}(iwiki:xxxx) 及 N 个 stale 源
Sources: iwiki:xxxx (never_compiled→compiled, v3), ...
Pages created: docs/sources/summary-xxx.md, docs/concepts/xxx.md
Pages updated: docs/index.md, docs/overview.md, docs/sources/index.md, docs/log.md
Key changes:
- 简述本次新增/变更的知识点
Contradictions flagged: 无
log.md 是编译器的"构建日志/工作台账",是给维护者和 Agent 自己看的过程记录,不是知识本身,所以留在本地仓库,不推送到 iWiki。
④ iWiki 发布 —— 最终交付(sync_to_iwiki.py)
docs/ 编译完成后,由发布脚本推送到 iWiki。核心难点是页面互链的 docid 解析,用"两遍推送"解决:
docs 里 A 页链接到 B 页用的是相对路径 ../entities/b.md,但推到 iWiki 需要换成 https://iwiki.woa.com/p/{B的docid}。而 B 的 docid 只有推送后才知道。
ensure_docid
createDocument 建档拿号
push_resolved
解析成 iWiki URL 后 saveDocument
由 sync 脚本独写,记录 docs 文件路径 → iWiki docid 的映射,以及目录 FOLDER 结构。frontmatter 会被 iWiki 自动剥离(正文干净),所以照常写 frontmatter 不会污染发布内容。
状态机与增量闭环
整个系统靠两份账本的状态流转驱动增量。理解这两组状态,就理解了"编译器如何知道该编译什么"。
fetch.state(输入侧状态,Agent 只读)
| 状态 | 含义 | 对编译的意义 |
|---|---|---|
pending | 待拉取 | → 候选(可能有新内容) |
fresh | 已拉取、与上次一致 | 不一定候选,看 compile 侧 |
changed | 源端已变更待重拉 | → 候选 |
fetching | 拉取中 | 暂不处理 |
error | 拉取失败 | → 候选(可用现有快照编译) |
removed | 已移除 | 对应页面标 deprecated,不删 |
compile.state(输出侧状态,Agent 独写)
| 状态 | 含义 | 对编译的意义 |
|---|---|---|
never_compiled | 从未编译 | → 候选 |
stale | 源版本已超过已编译版本 | → 候选 |
compiling | 编译中 | 处理中 |
compiled | 已编译到最新 | 跳过 |
compile_error | 上次编译失败 | → 候选(重试) |
ignored | 人为忽略 | 永不编译 |
双提交工作流(产物落地的闭环)
_compile_state.json 需要记录"产出这批 docs 的那次提交 hash",但提交前无法知道自己的 hash。所以:先提交 docs 产物拿到 hash(Commit A),再把 hash 回填进编译账本单独提交(Commit B)。这与 sync 的两遍推送同构——都是"先落地拿标识,再回填引用"。
- 基础检查:新增/改动页有 H1;相对链接目标存在;必要时 py_compile 校验脚本。
- Commit A(wiki 产物):
git add docs/提交本次编译的所有页面(sources/concepts/entities/index/log…),读取其 commit hash。 - Commit B(状态回填):把 Commit A 的 hash 写入 _compile_state.json 对应 source_id 的 compiled_commit(同时写 state=compiled、compiled_version、compiled_at),
git add raw/_compile_state.json单独提交。 - 一次性 push:A、B 都提交后再一次性推送,不停在未提交/未推送状态。
lint 仅报告、query 仅回答、incremental 无候选时 → 直接说明"无需更新"并停止,不产生空提交。
端到端实例:一个源的完整生命周期
把输入/处理/输出串起来,跟随一个 iWiki 源从被添加到最终发布的全过程:
- 输入 添加源:运维执行
fetch_raw.py --add 4028672179。脚本拉取正文、加 frontmatter、算 content_hash,落盘为raw/xxx-4028672179.md,并在 _fetch_state.json 写入fetch.state=fresh, version=3。 - 输入 触发编译:调用
mode=incremental operation=ingest source_id=iwiki:4028672179。 - 处理 候选判定:Agent join 两份账本,发现该 source_id 在 _compile_state.json 中缺失(never_compiled)→ 判定为候选。
- 处理 编译:通读原文 → 提炼 3–5 条要点 → 新建
sources/summary-xxx.md、若干concepts/*.md与entities/*.md→ 建立 ≥2 交叉引用 → 标注矛盾(若有)。 - 输出 写产物:更新 index.md(注册新页 + 主题分组)、overview.md、sources/index.md、log.md(追加 ingest 记录)。
- 输出 Commit A:
git add docs/ && git commit -m "docs: ingest xxx",得到 hashabc123。 - 输出 Commit B:把
abc123回填进 _compile_state.json(state=compiled, compiled_version=3, compiled_commit=abc123),单独提交后 push。 - 发布 sync:CI 触发
sync_to_iwiki.py,第一遍给新页建档拿 docid、第二遍解析链接推送,iWiki 上出现可搜索、可互跳的新文档。 - 再处理 下次源更新:某天该 iWiki 文档被人改动,fetch_raw 拉到 version=4。下次 ingest 时 join 账本发现 compiled_version(3) < version(4) → stale → 只重编这一个源的相关页面(增量),其余页面纹丝不动。
从零复现 LLM Wiki 的最小清单
抛开具体业务,复现这套"知识编译器"只需搭好三类要件:输入管道 + 处理器(提示词)+ 输出管道。
- 输入 搭输入管道:准备
fetch_raw.py(或任意拉取脚本),能把外部源拉成"带 source_id 的标准化 raw 文件 + _fetch_state.json 版本账本"。初始{"schema_version":3,"docs":[]}。 - 输入 准备空产物骨架:建 docs/(index.md、overview.md、log.md + sources/entities/concepts/comparisons/syntheses 五目录)与初始
_compile_state.json、iwiki-mapping.json。 - 处理 装配处理器(最关键):在 Knot 平台建 Agent,绑定仓库读写权限,把系统提示词全文设为 system prompt。没有它,LLM 只是聊天机器人。
- 处理 首次全量编译:调用
mode=rebuild operation=ingest,让 Agent 枚举全部源编译成 docs,末尾自动 lint 收敛。 - 输出 验证产物:检查双提交闭环(Commit A 产物 + Commit B 回填)、log.md 记录、lint 报告(断链/孤儿页为 0)。
- 发布 接发布管道:配置
iwiki-mapping.json的 spaceid/parentid,CI 里跑sync_to_iwiki.py(首次 --all)。 - 处理 建立增量循环:定时 fetch 检测源更新 → 有 stale 就
incremental ingest→ 定期lint保健康。系统开始自我累积复利。
复现避坑(机制层面)
| 陷阱 | 正确做法 |
|---|---|
| 让 Agent 直接改 raw 或 _fetch_state | 禁止。输入不可变,编译器无权改输入。 |
| 不给系统提示词就让 LLM 编译 | 产物会杂乱无章。提示词是编译器的"语言规范",必不可少。 |
| 用 Obsidian 双链 [[..]] | iWiki 不渲染。只用标准 markdown 链接。 |
| 页面无 H1 首行 | iWiki 标题会退化成文件名。首行必须 # 标题。 |
| 新页面不建交叉引用 | 会变孤儿页。每页 ≥2 入链。 |
| 只提交 docs 不回填 compile_commit | 下次增量判定会错乱。必须双提交。 |
| sync 只跑一遍 | 页面互链全失效。必须两遍(建档 → 解析链接)。 |
| 矛盾时直接覆盖旧结论 | 用引用块 + contradictions 显式标注,不选边。 |