ADR-013 · 评审提交文档的受控 KB ingestion · 只进知识层, 不碰判断产出
| 字段 | 值 |
|---|---|
| 状态 | accepted · 项目主理 2026-07-07 认可 + 零影响严格评审通过 (CTO 会签待补) |
| 日期 | 2026-07-07 |
| 决策者 | CTO + 项目主理 |
| 承接 | ADR-006 (脱敏分层) · ADR-011 (敏感分级) · ADR-012 (多场景评审) |
| 相关 | config.yaml (sources / ignore) · scripts/reviewed_docs_filter.py (骨架) · 现有材料过滤层范式 (§6.5) · scripts/redact_check.py (脱敏闸) · CLAUDE.md §2.1 / §3.3 / §6 / §9 |
1. Context
1.1 · 起源
飞书评审机器人已进入日常运营, 每天都有业务文档被提交评审。这些文档承载真实企业知识 (业务规划 / 组织 / 经营数据), 项目主理希望把它们沉淀进企业知识库 (_wiki/), 丰富 entities / people / concepts。
诉求本身合理: 每天流过的知识不应蒸发。但直接实现会撞上项目最核心的架构边界, 且必须满足第 0 红线: 对现有多场景评审 + 飞书机器人运营绝对零影响。
1.2 · 现状: 两类产物都在 sage-wiki 的 ignore 里
| 产物 | 落盘 | sage-wiki 态度 | 原因 |
|---|---|---|---|
| 评审提交文档 (原始 PDF/docx) | cases/.review-inbox/ (无清理 cron, 已持久) | ignore + gitignore | 队列邻接, 未去重 / 未分级 / 未脱敏 |
| 评审报告 (评委产出) | reports/ | ignore | 版本不可变 / 评委独立 / 归因轨迹 |
CLAUDE.md §2.1 明确: 这条 ignore 列表是 D7 边界判断的核心实现, 是设计原则不是性能优化。
1.3 · 为什么不能"图方便"直接 ingest
- 报告 (
reports/): ingest 会破坏三样东西 —— ①versions/v{n}.md快照不可变性 (归因锚点), ②reviews/<judge>.md评委独立性 (评委间 delta 是数据), ③同一对象多次判断的差异轨迹。§3.3 已把"ingest report"列为明确禁止项。 - 提交文档: 是第三方业务文档, 含真实机构名 + 经营数字 (§9 confidential/tian_only)。Wiki 知识层要求"先脱敏再进"(§6.1)。原始件直接进 = 未脱敏机密铺进知识库, 既污染又违纪; 且评审队列是临时、去重前、与
reports/邻接的目录, 不能作为 watch 源。
2. Decision
分两半, 边界清晰:
2.1 · 报告: 永不 ingest, 维持既有单向集成
reports/ 保持 ignore。报告与 Wiki 的连接已存在且正确 (§3.1 / §3.2): 报告"Background from Wiki"段单向链接到 entities; Wiki entities 自动挂"Related Judgements"回链 (只读 metadata)。不新增任何反向 ingest。 这条是不变承诺。
2.2 · 提交文档: 走一条受控 ingestion 管线 (纯只读取源, 零钩子)
新增一条受控管线, 范式照搬已验证的材料过滤层 (§6.5: 三通道分级 + 阈值 + 敏感度分级, 见 §2.4):
关键 (2026-07-07 修订, 见 §2.4): 内部 KB 保留原文; 敏感度分级在 VM 上做, 跨机的是原文 markdown (走私有分支跟踪, 非公开); 原始 PDF/docx 二进制不跨机。
┌─ VM (生产机) ──────────────────────────────────────────────────────┐
│ 飞书 bot → cases/.review-inbox/ (原始机密 PDF, 仅 VM 本地, gitignored)│
│ │ (SR-1 只读, SR-5 只取对应 job 已 done/ 的) │
│ ▼ scripts/reviewed_docs_filter.py │
│ │ (VM 上独立 cron/unit · SR-3 资源限额+开关 · SR-4 夜间错峰 │
│ │ · 不进消息事件循环 · 不改任何在跑代码) │
│ │ ├─ 去重 (content-hash) │
│ │ ├─ 价值分级 (三通道打分, 是否值得沉淀) │
│ │ ├─ 敏感度分级 ★ redact 当探测器 (机构/财务 → 升 tian_only, 原样) │
│ │ └─ 阈值 ≥0.7 auto / 0.3–0.7 人工 / <0.3 不进 (纯 score) │
│ ▼ raw/reviewed-docs/*.md (原文 markdown + 分级 frontmatter) │
└────┼───────────────────────────────────────────────────────────────┘
│ 传输 (§2.3, 已定 2026-07-07): 脱敏 .md 走 vm-data-backup 分支 (复用现有备份链)
▼
┌─ 开发机 (sage-wiki 所在) ──────────────────────────────────────────┐
│ raw/reviewed-docs/ ← pull / rsync │
│ ▼ sage-wiki compile (watch:false 错峰) │
│ ▼ _wiki/entities / people / concepts ← 只进知识层 │
└────────────────────────────────────────────────────────────────────┘要点:
- 只进知识层 (
entities/people/concepts), 不进reports/、不进summaries/的判断部分。 - 纯只读取源, 零钩子: 过滤器从
cases/.review-inbox/只读取源, 且只取对应 job 已在队列done/状态的文档; 不改 review_worker / feishu_events 一行 (见 §7 SR-2)。 - 进的是原文 (2026-07-07 修订, 见 §2.4): 内部 KB 保留原文, 靠私有存储 + 敏感度分级保护, 不删内容; redact 命中 → 升 tian_only, 不拦。
- 每个进
raw/reviewed-docs/的文件带 provenance: 只读链接回原评审 case (cases/<id>), 供审计追溯 —— 单向, 不反向 ingest report。 - 保留策略 (项目主理 2026-07-07 拍板): 原文副本永久保留, data owner = 项目主理。
- 知识分类 (M2, category): 每份带
categoryfrontmatter (business企业业务知识 /tooling关于 boss 系统自身 / 可扩展)。都入库, 不因类别丢弃 —— 元文档单独归一类, KB 内分类查询, 与业务知识分开。
2.3 · 数据流与跨机传输 (2026-07-07 严格评审补) ★
严格评审暴露一个必须显式解决的缺口: 提交文档只落在 VM (.review-inbox, gitignored + 备份 cron 排除 PDF), 而 sage-wiki 在开发机 —— 现状数据流不通。
定案的物理拓扑:
- 过滤器 (分级) 跑在 VM —— 文档在哪处理就在哪, 原始 PDF/docx 二进制 永不离开 VM。因此 SR-3/SR-4 (独立进程 + 资源限额 + 夜间错峰) 是真约束 (下修正 §7.3)。
- 跨机传输的是原文 markdown (
raw/reviewed-docs/*.md, 见 §2.4: 内部 KB 保留原文)。传输方式已定 (项目主理 2026-07-07): 复用vm-data-backup分支机制 —— 把raw/reviewed-docs/加进vm_backup_cron.sh的DATA_DIRS, 原文 .md 随现有备份链推到私有 backup 分支; 开发机 pull 该分支 → sage-wiki 编译。复用已验证通道, 仅动一个 cron 的目录清单 (非评审运行时)。带外 rsync 作为备选未采纳。 - sage-wiki 编译在开发机 → 富集落
_wiki/。回到 VM 的评审仍走 grep-fallback (不含新源, SR-6), 故富集惠及 sage-wiki 支撑的评审, 不改 VM 评审行为 (§3.4)。
⚠️ 原文 .md 在
vm-data-backup私有备份分支上被跟踪 (私有 repo 内, 非 main, 非公开层), 与该分支已承载的 cases/reports 文本同级。绝不进 www/handbook / 外网 —— 内部原文的安全靠私有存储 + 敏感度分级, 不靠删内容 (§2.4)。
2.4 · 敏感度模型 (2026-07-07 二次修订 · 项目主理定) ★
修订起因: 一版把 redact_check 当入库拦截闸 (命中机构/财务 → 降级人工)。但项目主理指出: 企业 KB 要的正是内部高敏战略/财务/组织文档, 而 redact_check 的设计用途是公网出站闸 (CLAUDE.md §9.2 "输出到外部网络必经"), 用它拦内部入库, 恰好把最有价值的内容全拦了。
定案 (前提: 本 repo 为 private, 内部 KB _wiki/ 不对外发布):
| 环节 | 错的做法 (旧) | 定案 |
|---|---|---|
| redact 命中机构/财务 | 拦截 → 降级人工 | 不拦, 升敏感度分级 (confidential → tian_only), 内容原样进 |
| verdict | 敏感就进不去 | 纯 score (是否值得沉淀); 敏感战略文档该自动进 |
| 保护手段 | 删/脱敏内容 | 敏感度分级 (谁能看) + 私有 repo / 私有分支 存储 |
| 真正的脱敏闸 | 错放在入库 | 留在公网出口 (www/handbook 的 check_public_safe + redact_check, 已存在) |
一句话: 内部 KB 存原文 (分级管控), 只有到公开层 (handbook) 才脱敏。redact_check 是出口闸不是入口闸。
3. Consequences
3.1 · 优势
- 每天流过的企业知识被结构化沉淀进 KB, entities/people/concepts 随运营自然丰富。
- 复用成熟范式 (现有材料过滤层的三通道 + 阈值 + 敏感度分级), 不发明新机制。
- 与判断产出边界零冲突 —— reports 一行不碰; 与运营零冲突 (§7)。
3.2 · 接受的 tradeoff
- 管线成本: 多一条 filter + 一个 watched 目录 + 一份 rules 配置要维护。
- 人工环节: 0.3–0.7 灰区文档进
.review/需人工裁决 (脱敏效果、是否该进)。这是刻意的 —— 机密文档不做无人值守全自动。 - 永久保留的存储与合规: 提交文档从"评完即弃"变为"永久保留原文副本 (私有存储)", data owner = 项目主理, 需承担第三方业务文档长期留存的合规责任。
- 富集只落 sage-wiki 侧 (见 §3.4): 为守绝对零影响, 不改 VM grep-fallback 的目录清单 → 富集只惠及 sage-wiki 支撑的评审, 不自动惠及 VM grep-fallback 路径。
3.3 · 不变的承诺
reports//cases/永远在 ignore 内; 报告永不 ingest。- Wiki→reports 的唯一合法触碰仍只是 §3.2 的 metadata 回链。
- 任何出站/入库仍过
redact_checkfail-close。 - 不改任何在跑的服务代码 (§7 SR-2)。
3.4 · Invariant impact ★
| 受影响的既有 invariant | 变化 | 新增 invariant |
|---|---|---|
| "sage-wiki watch 源是固定 4+N 类 raw" | 新增 1 类 watched 源 raw/reviewed-docs/ (watch:false) | 该源内每个文件带敏感度分级 frontmatter (confidential/tian_only); 内容为原文 (§2.4) |
"评审提交文档评完即弃 (.review-inbox ephemeral)" | 失效 — 现永久保留原文副本 | 保留的是原文 + 分级 + provenance link; owner = 项目主理; 仅存私有 repo/私有分支 |
| "§2.1 ignore = reports/cases 不进 Wiki" | 不变 | 明确: reviewed-docs ≠ reports; 管线禁止从 reports/ 或 cases/*/ 直接取判断产出 |
| "Wiki 知识层内容已脱敏 (§6.1 指公开层)" | 重定位 (§2.4) | 内部 KB (私有) 存原文; 脱敏只在公网出口 (www/handbook 双闸)。redact 命中 → 升 tian_only, 不删 |
| ★ 新红线: KB 原文私有边界 | 新增 | raw/reviewed-docs/ 与其派生 _wiki/ 实体, 只存私有 repo / 私有分支, 禁进 www/handbook/ 或外网; 边界由 check_public_safe + redact_check (只扫 www/handbook) 把守 |
"评审 Phase 1 context 来源" (VM 走 RAW_DIRS_FOR_WIKI_FALLBACK grep) | 不变 | 禁止把 raw/reviewed-docs/ 加进 RAW_DIRS_FOR_WIKI_FALLBACK —— 保 VM 评审上下文零变化 (见 §7) |
本 ADR 有两条核心红线: ①管线物理隔离 review 产出与提交文档 (filter 只读"提交文档持久副本", 绝不扫
reports//reviews//case.json, 白/黑名单钉死); ②内部 KB 原文只在私有边界内 (§2.4 新红线), 脱敏只发生在公网出口。
4. Alternatives Considered
| 方案 | 结论 | 原因 |
|---|---|---|
A. 把 watcher 直接指到 cases/.review-inbox/ | ❌ 拒 | 未脱敏 / 未去重 / 与 reports 邻接, 违 §2.1 D7 边界 |
| B. 连报告一起 ingest | ❌ 拒 | 毁版本不可变性 / 评委独立性 / 归因轨迹 (§3.3 明令禁止) |
| C. review-worker 收尾钩子推送文档 | ❌ 拒 | 碰生产热路径, 违第 0 红线; 且无必要 (.review-inbox 已持久, 独立 cron 只读即可) |
| D. 人工逐篇挑进 raw/clippings | ⚠️ 部分 | 不 scalable; 但可作 M2 dry-run 阶段的人工兜底 |
| E. 受控 filter 管线 · 独立 cron 只读取源 (本 ADR) | ✅ 采纳 | 复用材料过滤层范式, 零钩子 + 敏感度分级 + 灰区人工, 边界与运营皆不破 |
5. Migration Plan
每步之前, 现有多场景评审 + 飞书机器人运营零影响 (§7 SR 保证)。
| 里程碑 | 内容 | 门槛 |
|---|---|---|
| M0 | 本 ADR 双签 (CTO 会签) + §7 前置项确认 (sage-wiki 部署位置) | 通过才进 M1 |
| M1 | reviewed_docs_filter.py 实现 + config/reviewed_docs_filter_rules.yaml + 单测 (纯离线, mock LLM); 只读 .review-inbox (done/ 门控), 只写自留地 | 全绿 + SR 自检 |
| M2 | 在历史提交文档持久副本上 dry-run, 人工抽检脱敏效果与分级准确率 | 抽检达标 |
| M3 | raw/reviewed-docs/ 加进 vm_backup_cron.sh DATA_DIRS (传输); 开发机 config.yaml 加 source (watch:false) + 首批灰度 (单场景) | 观察一周无污染 |
| M4 | 独立 cron/unit 定时跑 (夜间错峰) + 资源限额 + 扩展全场景 | — |
原 M4「review-worker 收尾钩子」已删除 (评审严格评审结论):
.review-inbox已持久且被只读共享, 独立 cron 扫done/即可取源, 无需任何生产代码钩子。骨架 (
scripts/reviewed_docs_filter.py) 随本 ADR 提交但未启用 ——config.yaml的 ignore/sources 不动, 骨架不被任何运行路径 import。M0 前它是惰性设计稿。
6. 关键反共识立场
"评审文档应原样全量进 KB 才不丢信息" —— 这是错的。
KB 的价值不在原始 PDF 的堆积, 而在脱敏 + 结构化后的 entities / people / concepts。原样全量堆进去反而: 污染知识层 (未脱敏机密、重复文档、评审噪声)、埋合规雷 (第三方经营机密持久化且被索引)、稀释信噪比。丰富 KB ≠ 灌满 KB。
7. 零影响实施约束 (SR · 第 0 红线) ★
严格评审 (2026-07-07) 结论: 绝对零影响可达成, 且是架构级保证而非纪律级, 基于三条既有护栏 + 五条硬约束。
7.1 · 三条既有架构护栏 (代码事实, 非承诺)
- 评审运行时不依赖 sage-wiki:
run_pipeline_local明写"不依赖 sage-wiki", 有wiki_query_fallback(grep 退化)。改 sage-wiki 侧配置无法让评审崩溃, 最坏是它自己降级。 - 提交文档已持久、只读共享:
cases/.review-inbox/无清理 cron, 已被 worker + dashboard 只读共享。再加一个只读消费者零冲突。 - grep-fallback 扫固定目录清单:
RAW_DIRS_FOR_WIKI_FALLBACK硬编码 3 目录, 不含 reviewed-docs → 加新源后 VM 评审上下文一个字节不变。
7.2 · 五条硬约束 (实施必须满足)
| SR | 约束 |
|---|---|
| SR-1 只读上游 / 只写自留地 | 对 .review-inbox / 队列 / cases/ 一律只读; 只写 raw/reviewed-docs/ (原文 markdown + 分级) + 去重台账。经 vm-data-backup 私有分支跟踪跨机 (§2.3), 不进 www/handbook / 外网 |
| SR-2 零钩子 | 不改 review_worker.py / feishu_events.py / feishu_ws_client.py / boss_server.py 一行; 取源靠独立 cron 扫 done/ |
| SR-3 独立进程 + 限额 + 开关 | 独立 cron/unit, CPUQuota+MemoryMax, REVIEWED_DOCS_INGEST_ENABLED 默认关, systemctl stop 秒级回滚 |
| SR-4 错峰 / 非实时 | 源 watch:false (定时编译); 过滤器夜间跑; channel-3 LLM 限速, 不与运营时段/网关争限流 |
| SR-5 只取 done | 只处理 job 已 done/ 的文档, 避免读到下载中/评审中的半成品或失败件 |
| SR-6 不动 fallback 清单 | 禁止把 raw/reviewed-docs/ 加进 RAW_DIRS_FOR_WIKI_FALLBACK (保 VM 评审上下文零变化); 富集只经 sage-wiki 落 _wiki/ |
7.3 · 前置确认项 (进 M1 前)
- sage-wiki compile 部署位置 — ✅ 已确认 (2026-07-07): 生产 VM 上无 sage-wiki 进程 (
systemctl/pgrep核实), 评审走 grep-fallback → sage-wiki 编译在开发机, 加源不影响 VM。 - 修正 (2026-07-07 · 数据流评审): 但过滤器 (分级) 必须跑在 VM (文档只在 VM, PDF/docx 二进制不应跨机)。故 SR-3/SR-4 是 VM 上的真约束, 不是"自律" —— 过滤器作为 VM 上独立 cron, 靠资源限额 + 夜间错峰 + 默认关开关与评审运营隔离。零影响仍成立 (只读 + 零钩子 + 独立进程 + 限额), 但靠的是 SR-1~5 的隔离, 而非"物理不同机"。跨机的是原文 markdown (私有分支, §2.3/§2.4)。
- 传输方式 — ✅ 已定 (项目主理 2026-07-07): 复用 vm-data-backup 分支 (§2.3)。
- CTO 会签: 本 ADR 触 §2.1 D7 边界, 项目主理已认可, 待 CTO 会签补齐 (§10.2)。