name: kb-sync tags: [knowledge-base, incremental-ingest, rollback, llm-wiki] creator: 传鹏 description: |- 知识库增量同步引擎。扫描原材料目录,识别新增/变更/删除/移动,把新料整理成 md 页面,把被删源派生的页面回滚归档。是整个做课系统新鲜度的保证。
差异由脚本算,内容由你写,状态由脚本记。 你唯一不能做的事:凭记忆判断"这个文件是不是新的"。
幂等性保证:同一次同步跑两遍,第二遍应该是 新增 0 · 变更 0 · 删除 0。如果第二遍还有变化,说明上一次 mark 漏了。
python 90-工具/kb.py status
记下 待处理新增 / 待处理删除 / 待重建 三个数字。这次同步要消掉它们。
python 90-工具/kb.py scan --json
返回五类。处理顺序必须是:MOVED → DELETED → NEW → CHANGED。 理由:先清账再记账。删除不处理就加新料,会导致回滚时误伤。
源文件改名或挪目录,内容 hash 没变。
python 90-工具/kb.py move "<旧路径>" "<新路径>"
脚本会改 manifest 并把所有页面 frontmatter 里的 sources: 引用换掉。不需要重读文件,不需要改写页面内容。
可选:如果页面 title / id 里带了旧文件名,顺手改一下 title。
对每一个 deleted 源:
python 90-工具/kb.py retract "<源相对路径>"
脚本自动做三件事,对应三种情形:
| 情形 | 脚本行为 | 你要做的 |
|---|---|---|
| 独占页(该页只有这一个源) | 页面移入 _archive/,原位置删除 |
无。归档可回溯 |
| 共享页(该页有多个源) | 从 sources 移除该源,标 stale: true,进 rebuild_queue |
用剩余源重新合成该页 |
| 级联(别的页面引用了被归档页) | 这些页被标 stale: true |
判断:改写引用 / 解除引用 / 之后补源 |
_meta/rebuild_queue.jsonl,对每个页面:
stalestatus: draft,正文写明"原依据 X 已移除,以下结论待补充依据"[[引用]]:
lint 会报 DEAD_LINK)_archive/ 里的东西。真要清,等用户明确说。⚠️ 用户删源文件的真实意图常常是"这份资料作废了"。如果归档后发现这门课的产出(20-课程产出/)里引用了作废内容,必须主动提醒用户,不要默默改完就完事。
按文件数量决定策略:
| 新文件数 | 策略 |
|---|---|
| 1–3 | 全部处理,一次汇报 |
| 4–20 | 全部处理,但每 5 个汇报一次进度 |
| > 20 | 先挑 3 个最有代表性的做完给用户看格式,确认后再批量(避免格式返工 20 次) |
按重要性排序再处理:02-客户与需求 > 03-课程素材 > 01-行业资料 > 04-竞品参考。
| 扩展名 | 读法 |
|---|---|
.md .txt .csv .json |
直接读 |
.pdf |
用 workbuddy-pdf 技能(文本层优先,扫描件看页面图) |
.docx .doc |
用 workbuddy-office-routing |
.xlsx .xls |
用 workbuddy-sheetagent |
.pptx |
用 workbuddy-pptx |
.html .htm |
直接读,先剥标签 |
.png .jpg .webp |
ViewImage,提取图中文字与要点 |
.mp3 .mp4 .wav .m4a |
AudioTranscribe 转文字,再整理 |
| 其他二进制 | 尝试 python 提取纯文本;失败则只登记不解析,并在 source 页写明"文件类型不支持自动解析,需人工补充" |
不是每个文件都值得建页。先问三个问题:
三个都是否 → 只建一个最小 source 页,写明"内容与业务相关性低,仅登记备查",不做派生。不要为了凑数硬编派生页。
路径:10-知识库/sources/<去扩展名的文件名>.md
照 _meta/约定.md 的 frontmatter 规范写,sources: 填原材料的相对路径(精确到一字不差)。
正文骨架:
# <标题>
## 这是什么
(一句话定位:什么机构、什么时间、什么性质的文件)
## 核心内容
(3–7 条要点,带数字的优先)
## 可引用原句
> 原话照抄(做课时的弹药,别改写)
## 对做课的价值
(能用在哪个模块、哪个环节;没有就写"暂未发现")
完成后立刻登记:
python 90-工具/kb.py mark "<源相对路径>" --pages "sources/<页面>.md"
读完一个源,问:它能补上哪一块? 三种产出:
A. 并入已有 topic 页(最常见,也最省)
先 kb-query 查有没有相关主题页。有 → 把新事实补进该页,并把该源加进它的 sources: 列表。不要为同一件事开第二个 topic 页。
B. 新建 topic 页
条件:这个主题在库里还不存在,且预计未来还会不断有新料进来。
路径 10-知识库/topics/<主题名>.md。
C. 新建 entity / module 页
派生后必须补登记(把新页面加进 mark 的 --pages):
python 90-工具/kb.py mark "<源相对路径>" --pages "sources/x.md,topics/y.md,modules/z.md"
新手最容易犯的错:新源进来只建了一个 source 页,然后就走了。
Karpathy 的实测经验:一个源入库,平均会触及 10–15 个页面。 你只建了 1 页,说明漏了回声检查。
入库完,回头问四个问题:
| 问 | 动作 |
|---|---|
| 哪些已有 topic 页被这个源补充了? | 把新事实补进去,把源加进它的 sources 列表 |
| 哪些 entity 页的属性变了? | 更新。客户换了决策人、政策出了新版,都是 entity 页的事 |
| 有没有和已有结论打架? | 登记到 _meta/矛盾登记.md,不许静默采用新的 |
| 有没有让某个 module 的素材变强或变弱? | 更新 module 页。这条直接影响做课质量 |
怎么找"被触及"的页:
grep -rln "<新源里的关键名词>" 10-知识库/topics/ 10-知识库/entities/ 10-知识库/modules/
自查标准:如果一个源入库后只新建了 1 页、没改动任何已有页,有两种可能——要么这份料确实孤立(少见),要么你没做回声检查(常见)。
同一路径、内容 hash 不同。判断改动幅度:
| 幅度 | 判据 | 处理 |
|---|---|---|
| 微调 | 改了几个字/格式 | 更新 source 页对应表述,updated 改今天 |
| 中改 | 增删了一个小节 | 重写 source 页正文,检查派生页有没有依赖被删的内容 |
| 重写 | 结构全变 | 当作 NEW 处理:重建 source 页,逐条检查所有引用它的派生页 |
派生页检查方法:
grep -rl "<源相对路径>" 10-知识库/*/ # 找所有引用该源的页面
改完刷新指纹:
python 90-工具/kb.py mark "<源相对路径>" --pages "<受影响的页面列表>"
若 source 页 frontmatter 里有
source_sha字段,lint会自动比对 manifest 抓出漂移。建议加上。
python 90-工具/kb.py graph # 重建关系图和所有 _index.md
python 90-工具/kb.py lint # 健康检查
graph 之前,用户看到的索引全是旧的。graph 之后,索引才是真的。
lint 有问题就当场修,别攒着:
DEAD_LINK → 改引用或删引用DANGLING_SOURCE → 跑 retract 或补 markMISSING_FIELD → 补 frontmatterORPHAN → 找地方挂链接,或确认它是独立页SOURCE_DRIFT → 回到 Step 6最后更新总索引:如果新增了 topic 页,在 10-知识库/index.md 的「业务地图」里挂一行。
同步完成
增量:新增 3 · 变更 1 · 删除 1 · 移动 1
派生:source 3 页 / topic 2 页 / module 1 页
回滚:归档 2 页,1 页待重建(已处理)
健康:92 分,无遗留问题
⚠️ 提醒:删掉的《2024旧版大纲》在 `20-课程产出/AI内训-银行版/大纲.md` 里被引用过,那份课程可能需要复核。
下一步:`topics/客户画像` 目前只有 1 个源,建议补 2 份客户访谈记录再合成。
必须包含:做了什么、库变成什么样、有什么风险、下一步建议。不要只说"已完成"。