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