Initial commit: company-kb framework

- 完整的知识库框架(P0-P5 路线图)
- 12篇治理文档
- 工具脚本(kb-init.sh, kb-lint-fm.py, feishu-sync.py, kb-bot)
- 7个 Claude 命令
- 示例项目和测试项目(wanniu-l1)
- 契约文件(meta/kb-contract.yaml)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
yangqianqian
2026-07-16 18:24:39 +08:00
commit 84f7950589
60 changed files with 4410 additions and 0 deletions

0
.claude/commands/.gitkeep Executable file
View File

15
.claude/commands/kb-ask.md Executable file
View File

@@ -0,0 +1,15 @@
---
description: 查询循环——基于知识库回答,强制溯源,无据报 Gap
argument-hint: <问题>
---
查询循环。问题:`$ARGUMENTS`。基于本知识库 markdown 回答,**每条结论必须挂原始出处**。
执行:
1. 先扫 `projects/` 及相关目录,用语义检索定位相关页(可先看各 `_index.md` 和文件名,再读命中页)。
2. 综合回答,**每个关键结论后标出处**:引用页的 `source_link`,或页面路径 `[[slug]]`
3. **无据即报 Gap**:知识库没有覆盖的部分,明确说"知识库暂无此信息"**绝不用训练数据编造**,可建议用 `/kb-ingest` 补充。
4. 若命中页 `status: seed`(未成熟),提示该结论可信度较低。
5. 回答末尾附"依据页"清单(路径 + source_link
若这个问答有长期价值,提示用户可用 `/kb-save` 回写成 `questions/` 页沉淀。

18
.claude/commands/kb-check.md Executable file
View File

@@ -0,0 +1,18 @@
---
description: frontmatter 校验 + lint 体检
argument-hint: [可选:路径,默认全库]
---
跑知识库健康体检。
1. 执行:`python3 tools/kb-lint-fm.py $ARGUMENTS`(无参数则扫全库)。
2. 解读退出码:
- `0`:全绿,简报"检查 N 页0 错误"。
- `1`:有 frontmatter 错误(缺硬必填 / 未激活 type / enum 非法 / 派生必填缺失)。
- `2`:环境问题(缺 PyYAML → 提示 `pip install pyyaml`;缺契约文件)。
3. 对每条错误:
- **安全项**(补 title、补可推导字段、加 wikilink→ 你直接自动修,修完重跑确认。
- **有损项**(删页、合并、改 type、解决矛盾→ 列出来交用户拍板,**不擅自改**。
4. 简报修了什么、还剩什么需人处理。
校验规则一律以 `meta/kb-contract.yaml` 为准。

24
.claude/commands/kb-ingest.md Executable file
View File

@@ -0,0 +1,24 @@
---
description: 录入循环——把原始资料编译成 OKF 合规知识页
argument-hint: <项目代号> <源:文件路径/URL/粘贴的聊天>
---
录入循环。参数:`$ARGUMENTS`(第一个词是项目代号,其余是源)。人只负责丢资料 + 一句说明 + 对要点点头;其余你自动完成。
执行步骤:
1. **读契约**:先读 `meta/kb-contract.yaml` 拿 type 集、字段必填矩阵、enum。所有 frontmatter 严格据此,禁止硬编码。
2. **取内容**源是文件→Read是 URL→WebFetch是粘贴文本→直接用。
3. **去重**:与 `meta/ingest-manifest.json` 比对内容 hash命中则跳过并告知。
4. **落原文**:原始内容存到 `projects/<代号>/` 对应子目录doc→docs/聊天→conversations/)。
5. **抽要点**:展示 3-5 个关键发现(决策/需求/风险/客户信息),请用户点头或修正。
6. **编译成页**:按要点写成 OKF 合规页(参考 `projects/_example/` 的结构与 frontmatter
- 自动补齐可推导字段:`title`(←H1/文件名)、`created`(←源日期或今天)、`ingested`(=今天)、`source`/`source_link`(←采集来源)、`status: seed`
- 只有判断类字段(如 `author`、信息有效期 `timestamp`)需要时才问人。
- 决策→`decisions/`(type: decision),聊天沉淀→`conversations/`(type: conversation)。
-`related: [[...]]` wikilink 连到项目 _index 和相关页。
7. **矛盾挂起**:若新信息与已有页冲突,用 `> [!contradiction]` callout 标注,**不静默覆盖**,交人裁决。
8. **更新 manifest**:把 hash + 时间 + 影响页写回 `meta/ingest-manifest.json`
9. **校验**:跑 `python3 tools/kb-lint-fm.py projects/<代号>`,必须 exit 0。
10. 简报写了哪些页。
铁律AI 永不擅自有损操作(删/覆盖)、永不编造、溯源字段必挂。

27
.claude/commands/kb-link.md Executable file
View File

@@ -0,0 +1,27 @@
---
description: 编译链接——把工作目录/NAS/URL 的物料"链接"进知识库(提炼精华+留指针,不搬原件)
argument-hint: <文件路径 / NAS路径 / URL / 或直接口述哪个文件> [项目代号]
---
「编译过的链接」录入。人只需**点名一个物料**`$ARGUMENTS`:文件路径 / NAS 路径 / URL或口述"我工作目录的 XX 文件"),其余你自动完成。核心:**不搬原件,提炼精华 + 留 source_link 指针**——原文件原地不动,库保持精简。
执行步骤:
1. **读契约**:先读 `meta/kb-contract.yaml` 拿 type 集 / 字段必填矩阵 / enum。frontmatter 严格据此,禁止硬编码。
2. **定位原物料**
- 口述文件 → 在用户工作目录/NAS 下定位,不确定就让用户确认绝对路径。
- 文件路径/NAS 路径 → Read 直接读。
- URL → WebFetch。
- 记下原始位置作为 `source_link`NAS/本地绝对路径,或 URL
3. **判 source**:工作目录/本机文件→`本地`NAS 投递区或共享目录→`NAS`;飞书→`飞书文档`;网页→按来源。
4. **读一遍,提炼**:抽 3-8 个关键要点(决策/需求/客户信息/数据/风险),展示给用户点头或修正。**不复制原文全文进库**。
5. **写一页"编译链接"知识页**到对应项目目录(默认 `doc`;若本质是决策→`decision`
- frontmatter`type``title`(←文件名/H1)、`created`(←文件时间或今天)、`ingested`=今天、`source``source_link`=原文件指针、`status: seed``related: [[...]]`
- 正文结构:`## 摘要`AI 提炼的精华,可检索)+ `## 原件` (source_link 指针 + 一句"全文见原件")。
- 精华进库 = 抗原件移动/删除;指针留原件 = 库不膨胀。
6. **去重**:对提炼内容或原文 hash 比对 `meta/ingest-manifest.json`,命中则提示已链接过。
7. **快照后门(可选)**:若判断该原件**关键且易变/可能被删**,主动提议"是否复制原件进 `assets/` 兜底?"——**用户点头才复制**,默认只留链接。
8. **挂 wikilink**:连到项目 `_index` 和相关页。
9. **更新 manifest** + 跑 `python3 tools/kb-lint-fm.py <项目目录>`exit 0
10. 简报写了哪页、source_link 指向哪、是否快照。
铁律默认不搬原件库精简AI 永不擅自复制/删改原文件;永不编造,提炼只基于真实读到的内容。

14
.claude/commands/kb-new.md Executable file
View File

@@ -0,0 +1,14 @@
---
description: 建新项目骨架OKF 合规目录 + _index.md
argument-hint: <项目代号>
---
为项目代号 `$ARGUMENTS` 建骨架。
1. 若给了代号,运行:`bash tools/kb-init.sh $ARGUMENTS`
- 未给代号则先问用户要一个英文短代号(如 `power-bid``clientA`)。
2. 脚本会建出 `projects/<代号>/{_index.md,docs/,decisions/,conversations/,assets/}``_index.md``type: project` + `status: seed` 的 OKF frontmatter。
3.`python3 tools/kb-lint-fm.py projects/$ARGUMENTS` 确认新骨架合规exit 0
4. 简报:建了哪些文件,提示用户下一步用 `/kb-ingest $ARGUMENTS <源>` 录入资料。
字段规范一律以 `meta/kb-contract.yaml` 为准,不要硬编码 type 或字段。

17
.claude/commands/kb-save.md Executable file
View File

@@ -0,0 +1,17 @@
---
description: 把当前对话的洞察回写为 questions/ 知识页
argument-hint: [可选:一句话点题]
---
把当前对话中有价值的问答/洞察沉淀成一页知识。`$ARGUMENTS` 是可选点题。
前置:`question` 是预留 type`meta/kb-contract.yaml``active: false`)。若尚未激活:
1. 提示用户"首次回写需激活 question type",征得同意后把契约里 `question` 改成 `active: true``mkdir -p questions/`
执行:
1.`meta/kb-contract.yaml` 确认 question 字段要求。
2. 提炼本轮对话的问题 + 结论,写到 `questions/<slug>.md`
- frontmatter`type: question``title``status`(草稿→seed)、`created`/`ingested`=今天、`related: [[...]]` 连到依据页。
- 正文:问题 / 结论 / 依据(挂 source_link
3.`python3 tools/kb-lint-fm.py questions/` 确认 exit 0。
4. 简报路径。绝不把无据推测写成知识——只沉淀有出处的结论。

16
.claude/commands/kb-status.md Executable file
View File

@@ -0,0 +1,16 @@
---
description: 知识库健康仪表盘——源计数 / type 分布 / 红线观察
---
生成知识库现状概览。
1. **源计数**P5 向量库红线观察点红线≈100 源/几百页):
- 统计 `projects/` 下知识页总数:`find projects -name '*.md' -not -name '_index.md' | wc -l`
- 项目数:`ls -d projects/*/ | grep -v _example | wc -l`
2. **type 分布**:按 frontmatter `type` 分组计数grep 各页 type 字段)。
3. **健康**:跑 `python3 tools/kb-lint-fm.py` 取错误/警告数。
4. **status 分布**seed / developing / mature 各多少。
5.`meta/stats.md` 附上人工记录的观察。
6. 输出一张简表;若知识页数逼近 ~几百页,提示"接近规模红线,考虑启用 P5 向量库导航层(见 docs/05-路线图.md"。
只读操作,不改任何文件。

34
.gitignore vendored Executable file
View File

@@ -0,0 +1,34 @@
# ── Obsidian 本地配置(插件清单共享在 docs/obsidian-setup.md──
.obsidian/
.trash/
# ── 编译产物gitignore本地按需由 CC 重建(消除并发合并冲突)──
# 全库导航非 _index.md 部分P4 起才由单一执行者生成
hot.md
log.md
index.md
wiki/
# ── 飞书凭据 / 环境变量(绝不入库)──
.env
.env.*
!.env.example
# ── 大附件 / 二进制assets 里体积大的按需忽略;小附件可入库)──
*.mp4
*.mov
*.zip
*.pdf.large
# ── Python / 工具运行时 ──
__pycache__/
*.pyc
.venv/
venv/
# ── OS ──
.DS_Store
# kb-bot 运行时产物(长连接事件、已处理台账)
tools/kb-bot/events/
tools/kb-bot/.seen-msg-ids

75
AI-CONTEXT.md Executable file
View File

@@ -0,0 +1,75 @@
# AI-CONTEXT —— 公司知识库全景蒸馏(给 AI 的单一入口)
> 用 distill-knowledge 框架把 docs/01-12 + 契约 + 命令 + 工具蒸馏成一份。
> **读这一份 = 掌握整个系统**,无需翻 17 个文件。人读全文档见对应 `→源`。
> 蒸馏日期 2026-07-06 | 溯源:每节标 `→` 指向权威源文档,冲突以此蒸馏件的裁决态为准。
## 0. 一句话
5 人公司的 AI-native 知识库:**单一 Git 真相源NAS Gitea+ OKF frontmatter + 卡帕西"编译>检索" + 飞书/Obsidian 双前端 + 可生长不重做**。MVP 反过度工程:只做「可溯源 markdown + CC 检索」,编译层(P4)/向量库(P5)按信号延后。 →01,03,04
## 1. 信条与硬约束不可违反→CLAUDE.md,04,07
1. **单一真相源** = 这个 Git 仓库本身,文件即真相;无 raw/wiki 双层,扁平布局。
2. **type/字段/enum 只认 `meta/kb-contract.yaml`**,脚本禁硬编码。
3. **溯源铁律**:查询每条结论必挂 `source_link`;无据报 Gap**绝不用训练数据编造**。
4. **AI 永不擅自有损操作**:删页/解决矛盾/合并/改 type 一律挂起交人;安全项(补可推导字段/建 stub/加 wikilink可自动做。
5. **人工入口最小化**:人只「点名物料 + 一句说明 + 对要点点头」,其余 AI 自动。
6. **中文目录名禁令**:项目代号用英文/拼音CC 把中文编码为 `-` 会碰撞)。
## 2. 分层架构 →04
- **L1 采集**:三通道 = 飞书群/文档、NAS 各人投递区(`NAS/<人>/📥知识库投递/`,只扫这个,私人目录不碰)、`/kb-link` 点名任意文件/URL。
- **L2 汇聚(真相源)**Git 仓库本身,每页带 OKF frontmatter版本/审计/冲突全靠 Git。
- **L3 处理MVP 极薄)**:只有 lint + CC 语义检索 + Delta 去重。**不生成** hot/index/wikigitignoreP4 才启用)。
- **L4 呈现**Obsidian个人主编辑器=唯一写路径)+ 飞书(双向前端)+ CC问答
## 3. 「编译过的链接」采集模型核心→04,kb-link
默认**不搬原件进库**:人点名物料 → AI 读一遍 → 提炼精华写成一页 markdown → 留 `source_link` 指回原件。三重收益:库不膨胀、精华可检索可溯源、原件移动/删除后知识仍在。仅关键且易变的原件AI 提议+人点头才复制进 `assets/` 兜底。
## 4. 目录结构 →04,11
```
projects/<代号>/ 项目_index.md(project) + docs/ decisions/ conversations/ assets/
_example/ OKF 完整示范(照抄即合规,勿当真实项目)
company/ 公司基本信息doc
learning/ 共享学习库按领域ai/ management/ industry/doc
entities/ concepts/ questions/ 预留 typereserved激活后才用
meta/kb-contract.yaml ★唯一契约 meta/stats.md 红线观察 meta/ingest-manifest.json 去重台账
tools/ kb-init.sh kb-lint-fm.py feishu-pull.py feishu-render.py kb-bot/
docs/ 方案文档 01-12治理层改动走 PR+review
```
## 5. Frontmatter 契约(真相在 kb-contract.yaml→CONVENTIONS,meta
- **硬必填**`type`(缺失只报不猜) `status`(seed/developing/mature,默认seed) `created` `ingested`(=今天,AI自动)。
- **派生必填**:外部摄入的 doc/decision/conversation 页 → `source`+`source_link` 升为必填metric 型 → `resource`。project 的 _index 豁免溯源。
- **可选/自动**`title`(AI从H1回填) description tags(kebab) timestamp author related([[wikilink]])。
- **激活 type**`project decision conversation doc`+company/learning 用 doc。**reserved**entity concept question runbook metric用前改契约 active:true
- **免 frontmatter**README/CONVENTIONS/CLAUDE、docs/** meta/** tools/** .claude/** **/assets/** **/.gitkeep。
## 6. 命令面(.claude/commands/→07
| 命令 | 作用 |
|---|---|
| `/kb-new <代号>` | 建项目骨架(=bash tools/kb-init.sh) |
| `/kb-ingest <代号> <源>` | 录入:去重→落原文→抽要点→写合规页→挂链,矛盾挂起不覆盖 |
| `/kb-link <文件/NAS/URL>` | 编译链接:提炼精华+留指针,不搬原件 |
| `/kb-ask <问题>` | 查询:语义检索,每条挂 source_link缺口明说 |
| `/kb-save` | 对话洞察回写 questions/ 页 |
| `/kb-check` | frontmatter 校验(=python3 tools/kb-lint-fm.pyexit0=健康,需pyyaml) |
| `/kb-status` | 源计数/type分布/红线观察 |
## 7. 飞书双向前端 →08
- **群机器人(能问能答,长连接无公网)**`lark-cli event +subscribe`(WebSocket收) → handle.py 抽问题 → `/kb-ask``lark-cli im +messages-reply` 回复。底座 lark-cli 已装(bot身份 cli_a9458d254e3a5bc3)。组件 `tools/kb-bot/`。只读不写库。
- **呈现三层**(均单向 md→飞书飞书是"读屏"不写库):①云文档(叙事,项目概览) ②多维表格Bitable(结构,全库一张表+多视图,可筛选/看板,通用 lark-cli api 写) ③群机器人(搜索框)。Wiki 暂不做。
- 工具 `tools/feishu-render.py --bitable/--docs`(骨架,写入留TODO需真实表/群)。
## 8. AI-native 五循环 + 人机分工 →07
录入(人丢+点头/AI全包) · 查询(CC检索+强制溯源+报Gap) · 维护(/kb-check,安全项自动修/有损项挂起) · 生长(P5红线才启用向量库,预留) · 飞书问答(群@→/kb-ask→回复)。**铁律**:能自动的绝不让人做;永不擅自有损;永不编造;人是拍板者。
## 9. 演进红线 →05,04
- **P4 编译层**(llm-wiki全量互链wiki+hot/index):触发=`_index 首读定位失效 + 摄入悖论`
- **P5 向量库****唯一硬触发**=`~100 源/几百页 + index 定位不准`。到线前"要不要上RAG"默认=**不**。向量库只做导航层,契约/编译层零改动。
## 10. 部署与治理 →10,12
- **拓扑**:仓库家+常驻处理(机器人长连接/定时投影)在**公司NAS**;交互处理(ingest/ask/link)在各人本机读本地clone。remote=`http://192.168.0.101:3002/robert/company-kb.git`(NAS Gitea,需内网/VPN)。
- **两条NAS用途**Gitea仓库(多人协作真相源) vs 同步盘(单向投递采集通道),不冲突。
- **维护**:物料录入全员参与;规则改动(契约/命令/CONVENTIONS/docs)全员可提PR合入需至少一人review**不可自审自合**。改字段必填前先跑lint扫全库加新type走 docs/11§4 登记(契约加条+建目录+飞书加视图)。处理层生长时契约尽量不动。

88
CLAUDE.md Executable file
View File

@@ -0,0 +1,88 @@
# 公司 AI 知识库 · 项目工作记忆 (OKF Knowledge Base)
> CC现在在公司 AI 知识库根目录。这是一个 **OKF frontmatter + 单一 Git 真相源 + AI 主执行** 的本地知识库。
> 信条:**单一 Git 真相源 + OKF frontmatter + AI 主执行 + 可生长不重做**。反过度工程MVP 只做「可溯源 markdown + CC 检索」,编译层(P4)/向量库(P5)按信号延后。
## 这是什么
- **唯一真相源** = 这个 Git 仓库本身(远程家在公司 NAS 的 Gitea。文件即真相无 raw/wiki 双层,扁平布局。
- **前端,同一仓库**Obsidian 是唯一写路径(编辑 markdown → git push飞书是**双向前端**——采集入口(群/kb-link+ 群机器人问答(读,调 /kb-ask+ 呈现(云文档+多维表格)。飞书不直接写 Git。
- **MVP 问答方式**:你直接对 markdown 做目录 + 语义检索回答,**不生成** hot.md/index.md/wiki/(那些 gitignoreP4 才启用)。
- **采集三通道**:飞书群/文档、NAS 各人投递区(`NAS/<人>/📥知识库投递/`,只扫这个,私人目录不碰)、`/kb-link` 点名任意文件/URL。默认**编译链接**——提炼精华+留 source_link 指针,不搬原件(库不膨胀、原件移动也不丢知识)。
## 目录结构
```
projects/<代号>/ 项目工作区_index.md(project) + docs/ decisions/ conversations/ assets/
_example/ OKF 完整示范(照抄即合规,勿当真实项目)
company/ 公司基本信息(档案/团队/对外介绍type: doc
learning/ 共享学习知识库按领域分ai/ management/ industry/type: doc
entities/ concepts/ questions/ 预留 type 目录reserved激活后才用
meta/kb-contract.yaml ★唯一机器可读契约type/字段/enum
meta/stats.md 源计数红线观察点
meta/ingest-manifest.json 摄入去重台账
tools/ kb-init.sh / kb-lint-fm.py / feishu-pull.py / feishu-render.py / kb-bot/
docs/ 方案文档 01-12治理层改动走 PR+review见 docs/12
```
## 硬约束(不可违反)
1. **type 白名单指向契约,不硬编码**:合法 type / 必填字段 / enum 一律以 `meta/kb-contract.yaml` 为准。当前激活 4 个:`project` `decision` `conversation` `doc``entity/concept/question/runbook/metric` 已登记但未激活,用前先改契约 `active: true`
2. **每页必带 frontmatter**(除 README/CONVENTIONS/CLAUDE/docs/**/meta/**/assets/**)。硬必填:`type status created ingested`。外部摄入的 doc/decision/conversation 页 `source`+`source_link` 升为必填。
3. **溯源铁律**:查询回答的**每条结论必挂 source_link**。覆盖不足就明说 Gap**绝不用训练数据编造**。
4. **AI 永不擅自有损操作**:删页/解决矛盾/合并/改 type 一律挂起交人。安全项(补可推导字段/建 stub/加 wikilink可自动做。
5. **中文目录名禁令**:项目代号用英文/拼音CC 会把中文编码为 `-` 导致碰撞)。
6. **人工入口最小化**:人只做「丢资料 + 一句说明 + 对要点点头」。`title/ingested/source/status` 等能推导的字段你自动补齐。
## frontmatter 速查(真相在契约文件)
```yaml
---
type: decision # 硬必填,缺失只报不猜
status: seed # seed/developing/mature新建默认 seed
created: 2026-06-20 # 内容产生时间
ingested: 2026-06-24 # 汇入时间=今天,你自动填
source: 飞书群 # 外部摄入页必填enum 见契约
source_link: https://... # 外部摄入页必填
title: "..." # 建议,你可从 H1/文件名回填
tags: [kebab-case]
timestamp: 2026-06-20 # 可选,信息有效期
author: 张三 # 可选
related: ["[[slug]]"] # 可选 wikilink 强边
---
```
## 命令面 `/kb <verb>`(见 .claude/commands/ 与 docs/07
| 命令 | 作用 |
|---|---|
| `/kb-new <代号>` | 建项目骨架(= `bash tools/kb-init.sh <代号>` |
| `/kb-ingest <代号> <源>` | 录入Delta去重→落原文→抽要点→写合规页→建 wikilink矛盾标注挂起不静默覆盖 |
| `/kb-link <文件/NAS路径/URL>` | 编译链接:点名物料,提炼精华+留 source_link 指针,**不搬原件**;关键易变件才提议快照进 assets/ |
| `/kb-ask <问题>` | 查询:语义检索 markdown每条结论挂 source_link缺口明说 |
| `/kb-save` | 把对话洞察回写 questions/ 页 |
| `/kb-check` | frontmatter 校验(= `python3 tools/kb-lint-fm.py` |
| `/kb-status` | 源计数 / type 分布 / 红线观察 |
## 常用操作
```bash
bash tools/kb-init.sh <代号> # 建项目骨架
python3 tools/kb-lint-fm.py [路径] # frontmatter 体检退出码非0=需处理(需 pyyaml
bash tools/kb-init.sh --stats # 项目数/页数/源数
python3 tools/feishu-pull.py <代号> --doc <token> # 飞书导出干跑
python3 tools/feishu-pull.py <代号> --doc <token> --write # 实际落盘
python3 tools/feishu-render.py --bitable --dry-run # 全库→多维表格呈现(干跑预览)
python3 tools/feishu-render.py --docs <代号> --dry-run # 项目→飞书云文档(干跑)
bash tools/kb-bot/subscribe.sh # 起飞书群机器人长连接(收消息)
python3 tools/kb-bot/handle.py --watch # 消费事件→/kb-ask→回复常驻在 NAS
```
## 演进红线(默认不上,等信号)
- P4 编译层llm-wiki 全量互链 wiki + hot/index触发信号 = `_index` 首读定位失效 + 摄入悖论。
- P5 向量库:**唯一硬触发** = `~100 源/几百页` + index 首读定位不准。到线前「要不要上 RAG」默认答案 = **不**。向量库只做导航层type 契约与编译层零改动。
## 规则维护(见 docs/12
- **物料录入全员参与****规则改动**(改 `kb-contract.yaml`/`.claude/commands/`/CONVENTIONS/docs全员可提 PR合入需**至少一人 review不可自审自合**。
- 改字段必填/enum 前先跑 `kb-lint-fm.py` 扫全库,确认存量页不被误伤;加新 type 走 docs/11 §4 登记流程(契约加条+建目录+飞书加视图)。
- 处理层(L3)按信号生长,**契约/frontmatter 尽量不动**(上层长、地基稳)。
## 部署拓扑(见 docs/10
- 仓库家 + 常驻处理(机器人长连接、定时投影)在**公司 NAS**交互处理ingest/ask/link在各人本机读本地 clone。
- 远程 remote`http://192.168.0.101:3002/robert/company-kb.git`NAS Gitea需内网/VPN
> 治理文档docs/、CONVENTIONS.md、README.md、kb-contract.yaml改动走 PR+reviewdocs/12勿随意覆盖。

136
CONVENTIONS.md Executable file
View File

@@ -0,0 +1,136 @@
# CONVENTIONS · 录入宪法
> 本文是知识库的录入规范。**type 集、字段必填矩阵、enum 取值的唯一机器可读真相源是 `meta/kb-contract.yaml`**——本文只做人读镜像与规约说明,不复述契约细节。任何冲突以契约文件为准。
> 铁律:能让 AI 自动做的绝不让人做丢资料是唯一强人工入口AI 永不擅自有损操作AI 永不编造,无据即报 Gap。
> **规则归属与修改**(谁能改本规范/契约/命令、怎么改)见 `docs/12-治理与规则维护.md`:物料全员录入,规则改动全员可提 PR、合入需 review。
---
## 1. Frontmatter Schema人读镜像
每个知识页顶部是一段 YAML frontmatter。字段的必填性由 `meta/kb-contract.yaml` 的必填矩阵判定,`kb-lint-fm.py` 运行时读契约校验。下表为人读镜像:
| 字段 | 必填 | 类型 | 说明 / AI 自动化 |
|---|---|---|---|
| `type` | ✅ **唯一硬必填** | string | 取值见 `kb-contract.yaml` 已激活 type缺失只报不猜 |
| `status` | ✅(默认 `seed` | enum | `seed`/`developing`/`mature`;新建即 `seed`AI 可自动填默认 |
| `created` | ✅ | date | 内容产生时间AI 从源推导或填今天 |
| `ingested` | ✅ | date | 汇入时间;**AI 自动 = 今天** |
| `source` | 外部摄入页 ✅ | enum | `飞书文档`/`飞书群`/`微信群`/`本地`/`NAS`/`会议`/`人工沉淀`/`AI编译`;采集器自动填 |
| `source_link` | 外部摄入页 ✅ | string | 原始链接/绝对路径;采集器自动填 |
| `title` | 建议 | string | **AI 自动 = H1 或文件名回填**(实际免手写) |
| `description` | 可选 | string | ≤120 字摘要,供导航 |
| `tags` | 可选 | list | kebab-case |
| `timestamp` | 可选 | date | 信息最后有效期(判断过时) |
| `author` | 可选 | string | 内容产生人 |
| `related` | 可选 | list | `[[slug]]` wikilink 强边 |
| `resource` | `metric` 型 ✅ | string | 底层资源稳定链接 |
**派生必填规则**写进契约lint 据此判定):
- `type ∈ {doc, decision, conversation}``source` 为外部渠道 → `source` + `source_link` 升为必填。
- `projects/*/_index.md``type: project`)豁免溯源字段。
- 顶层 `README.md`/`CONVENTIONS.md`/`docs/*`/`assets/*` 免 frontmatter仓库脚手架非知识页
- **人工必填压到最少**:绝大多数场景人只需拍板 `type`(采集器常可推断),其余由 CC 自动补齐。
**注意**:不设 `updated` 字段——最后修改时间查 `git log`
**完整示例**`projects/_example/decisions/` 下一页):
```markdown
---
type: decision
title: "客户 A 报价采用阶梯定价而非一口价"
description: 因客户预算分期到位,改用阶梯定价锁定长期合作,牺牲首单毛利。
tags: [报价, 客户-A, 定价策略]
timestamp: 2026-06-20
source: 飞书群
source_link: https://company.feishu.cn/im/xxxx?msg=99231
author: 张三
created: 2026-06-20
ingested: 2026-06-24
status: mature
related:
- "[[project-clientA]]"
---
# 客户 A 报价采用阶梯定价而非一口价
## 背景 / 决策 / 参与人·时间 / 溯源
原始讨论见 source_link。
```
---
## 2. Type 体系(引用契约)
**唯一真相源 = `meta/kb-contract.yaml`**。核心集封闭;扩展 = 改契约 + 一次 PR轻量登记
核心 4 typeDay-1 激活,首个闭环即用):`project` / `decision` / `conversation` / `doc`
预留 type登记后按需激活`entity` / `concept` / `question` / `runbook` / `metric`
> lint 白名单 = 契约中「已激活 type」的并集激活即放行。定义与目录归属见 `docs/04-架构.md` 与契约文件本身,此处不复述。
**扩展流程**:新增 type = 在 `kb-contract.yaml``types` 数组登记(含 active/reserved 标记 + 必填规则)→ 建对应目录 → 一次 PR。禁止在脚本里硬编码 type。
---
## 3. 命名与路径规约
- **项目代号**英文或拼音kebab-case禁纯中文目录名CC 会把中文编码为 `-` 导致 ID 碰撞)。例:`projects/clientA/``projects/power-bid/`
- **文件名**kebab-case语义化无空格。例`stepped-pricing-decision.md`
- **项目门户**:每个项目目录必有 `_index.md``type: project`)作为导航锚点。
- **附件**:原始二进制/图片放各项目 `assets/`,免 frontmatter。
- **目录布局**必须与 `docs/04-架构.md` 一致,`kb-init.sh` 据此建骨架。
---
## 4. Wikilink 规约
-`[[slug]]` 建立页间强边,写入 frontmatter 的 `related` 字段或正文。
- slug 用目标文件名(不含 `.md`)或项目代号,例 `[[project-clientA]]`
- **强边related**:语义强关联(同一决策链、同一客户、上下游依赖)。
- 正文内引用其他页用行内 `[[slug]]`Obsidian 图谱据此渲染。
- AI 录入时**自动建 wikilink**:识别页面提及的已有实体/项目/决策并挂链。
---
## 5. 摄入规则
1. **Delta Check 去重**:录入前对内容算 hash比对 `meta/ingest-manifest.json` 台账,已存在则跳过或提示合并,不重复落盘。
2. **落原文**:外部文档原文进 `projects/*/docs/`(或 `assets/`frontmatter 记 `source` + `source_link`
3. **抽关键发现**AI 从原文抽要点,写成合规知识页(`decision`/`conversation` 等)。
4. **frontmatter 自动补齐**AI 自动填 `title`H1/文件名回填)、`ingested`(今天)、`status``seed`)、可推导的 `created`/`source`
5. **矛盾不静默覆盖**:新内容与已有页冲突时,**MVP 用简单标注挂起**(正文加矛盾标记),交人拍板;**不做自动 status 降级状态机**。
6. **凭据不入库**`feishu-pull.py` 等采集器的凭据走环境变量。
7. **MVP 一次录入产出结构化 markdown**,不做 8-15 页 wiki 编译P4 才编译)。
---
## 6. Lint 规则
`tools/kb-lint-fm.py`**PyYAML 解析** frontmatter**读 `meta/kb-contract.yaml`** 判定,退出码非 0 供 CC 判定。检查项:
| 检查项 | 级别 | AI 处置 |
|---|---|---|
| `type` 缺失或不在已激活白名单 | 阻断 | 报错,不猜 |
| 派生必填字段缺失(如外部页缺 `source_link` | 阻断 | 报错;可推导则自动补 |
| `status` 不在 enum | 阻断 | 报错;缺失自动填 `seed` |
| `source` 不在 enum | 阻断 | 报错 |
| `title` 缺失 | 提示 | 自动 = H1/文件名回填 |
| 死链(`related` 指向不存在页) | 提示 | 建 stub 或标注 |
| 孤立页(无任何入链/出链) | 提示 | 提议加 wikilink |
| 过时(`timestamp` 超期) | 提示 | 交人/AI 判断 |
**安全项 AI 自动修**:补可推导字段、建 stub、加 wikilink。
**有损项挂起交人**:删页、解决矛盾、合并、改 type。
> 维护保持轻量提示,**不做周度 cron 强制门禁**;靠 Git PR review + CC 提示 + `/kb-check` 按需运行。
---
## 附:写页 checklist人只需第 1 条)
1. **丢资料 + 一句说明 + 对 AI 抽的要点点头**(唯一强人工动作)。
2. ~~填 frontmatter~~ → AI 自动补齐。
3. ~~建 wikilink~~ → AI 自动挂链。
4. ~~跑 lint~~`/kb-check` 或提交前 CC 自动跑。

120
README.md Executable file
View File

@@ -0,0 +1,120 @@
# 公司 AI 知识库
> **单一 Git 真相源 + OKF frontmatter + AI 主执行 + 可生长不重做**。
> 这个仓库本身就是知识库——没有数据库没有服务文件即真相源。Obsidian 编辑、飞书呈现、Claude Code 问答,三个视图共享同一个 Git 仓库。
>
> 🤖 **AI 快速上手**:读 [`AI-CONTEXT.md`](AI-CONTEXT.md)——全架构蒸馏成一份,读它即掌握整个系统,无需翻 docs。
---
## 这是什么
一个给 5 人小团队用的公司知识库。它把项目决策、聊天沉淀、外部文档汇入到**一个 Git 仓库**里,每页带一段 OKF frontmatter 描述元数据Claude CodeCC据此做**可溯源问答**——每条结论都挂原始链接,无据即报 Gap绝不编造。
MVP 反过度工程:先做「可溯源 markdown + CC 检索」编译层P4、向量库P5按信号延后默认不上。
---
## 怎么用
### 1. cd 进来问 AIMVP 默认方式)
```bash
cd ~/奇域/智能体开发/公司AI
# 然后直接问 Claude Code
# /kb-ask 客户 A 为什么不用一口价?
# /kb-ask power-bid 项目现在进展到哪了?
```
CC 直接语义检索 markdown 回答,每条结论挂 `source_link`
### 2. Obsidian 打开当 vault
直接把 `公司AI/` 目录作为 vault 用 Obsidian 打开,即可 wikilink 跳转、图谱浏览。这是**个人主编辑器**,也是**唯一写路径**(编辑 markdown → `git push`)。
- 插件清单见 `docs/obsidian-setup.md`(装 Dataview
- `.obsidian/` 已 gitignoreGit 同步走命令行/CC不用 Obsidian Git 插件自动提交。
### 3. 飞书导入
飞书是**公司呈现面 + 文档来源****不直接写**仓库。飞书文档经 `tools/feishu-pull.py` 半自动单向导出流入 `projects/*/docs/`AI 自动补 frontmatter。凭据走环境变量不入库。
### 4. 录入新资料
```
/kb-new <代号> # 建项目骨架
/kb-ingest <代号> <源> # 丢资料 + 一句说明 + 对 AI 抽的要点点头
/kb-link <文件/NAS路径/URL> # 点名工作目录/NAS/网页的物料AI 提炼精华+留指针(不搬原件)
```
**人只做**:点名物料 + 一句说明 + 点头。frontmatter 补齐、wikilink、去重全由 AI 自动完成。
**三条采集通道**:飞书群/文档、NAS 各人投递区(`NAS/<同事>/📥知识库投递/`AI 只扫这个,私人目录不碰)、`/kb-link` 点名任意文件/链接。默认**不搬原件**——只把 AI 提炼的精华写进库、留 `source_link` 指回原件,库不膨胀、原件移动也不丢知识。
---
## AI-native 怎么跑
四个循环,人是监督者与拍板者:
- **录入**人丢资料AI 去重→落原文→抽要点→写合规页→建 wikilink。矛盾挂起不静默覆盖。
- **查询**CC 检索 markdown强制溯源无据报 Gap绝不编造。
- **维护**`/kb-check` 跑 lint安全项 AI 自动修,有损项(删/合并/改 type挂起交人。
- **生长**:预留接口,到 P5 红线才启用向量库导航层。
**铁律**:能让 AI 自动做的绝不让人做丢资料是唯一强人工入口AI 永不擅自有损操作AI 永不编造。
详见 `docs/07-AI-native工作流.md`
---
## 文档索引
| 文档 | 内容 |
|---|---|
| [docs/01-方案总览.md](docs/01-方案总览.md) | 方案总览 |
| [docs/02-首个闭环-项目资料.md](docs/02-首个闭环-项目资料.md) | 首个闭环:项目资料 |
| [docs/03-知识库框架选型调研.md](docs/03-知识库框架选型调研.md) | 框架选型调研 |
| [docs/04-架构.md](docs/04-架构.md) | 四层架构 + 扁平目录树 + 双前端映射 + 下游接口 + 演进阶梯 |
| [docs/05-路线图.md](docs/05-路线图.md) | P0-P5 分阶段作战图P5 向量库红线 |
| [docs/06-工具链.md](docs/06-工具链.md) | 三档工具清单 + gitignore 策略 |
| [docs/07-AI-native工作流.md](docs/07-AI-native工作流.md) | 五循环 + 人机分工 + 命令面 |
| [docs/08-飞书双向前端方案.md](docs/08-飞书双向前端方案.md) | 飞书双向前端:群机器人(长连接问答)+ 呈现三层架构 |
| [docs/09-同事使用指南.md](docs/09-同事使用指南.md) | **面向全体同事**:丢/问/看三动作,零技术门槛 |
| [docs/10-部署与运维.md](docs/10-部署与运维.md) | 物理拓扑NAS Gitea+常驻处理)+ 部署步骤 + 运维手册 |
| [docs/11-信息全景与非项目结构.md](docs/11-信息全景与非项目结构.md) | 公司信息全景 + company/learning 结构 + 新类别登记流程 |
| [docs/12-治理与规则维护.md](docs/12-治理与规则维护.md) | 规则放哪/谁维护/怎么改:物料全员录入,规则 PR+review |
| [CONVENTIONS.md](CONVENTIONS.md) | 录入宪法frontmatter schema + 命名/wikilink/摄入/lint 规约 |
| [docs/obsidian-setup.md](docs/obsidian-setup.md) | Obsidian 共享插件清单 |
---
## 单一真相源清单(消除漂移根因)
| 契约 | 唯一存放处 |
|---|---|
| type 集 + 字段必填矩阵 + enum | `meta/kb-contract.yaml`机器可读lint 运行时读) |
| 命令列表 | `docs/07-AI-native工作流.md` + `.claude/commands/` |
| 目录布局 | `docs/04-架构.md` |
| 溯源锚点 | 每页 `source` / `source_link` |
任何脚本硬编码 type/字段一律禁止——读契约文件 `meta/kb-contract.yaml`
---
## 目录速览
```
公司AI/
├── README.md # 本文(总索引)
├── CLAUDE.md # CC 项目级工作记忆
├── CONVENTIONS.md # 录入宪法
├── docs/ # 01-11 方案文档 + obsidian-setup
├── meta/ # kb-contract.yaml契约+ stats + ingest-manifest
├── projects/ # 项目知识_example/ 照抄即合规)
├── company/ # 公司基本信息(档案/团队/对外介绍)
├── learning/ # 共享学习知识库按领域分ai/management/industry
├── entities/ concepts/ questions/ # reserved type按需激活
├── tools/ # kb-init.sh / kb-lint-fm.py / feishu-pull.py
└── .claude/commands/ # /kb-* 命令
```

40
company/_index.md Executable file
View File

@@ -0,0 +1,40 @@
---
type: doc
title: "公司基本信息"
description: 公司档案入口——是什么、团队、组织、对外介绍的总览与导航。
status: seed
created: 2026-07-06
ingested: 2026-07-06
source: 人工沉淀
tags: [公司信息, 档案]
related: []
---
# 公司基本信息
> 公司的「自我档案」——新人第一份读物,对外介绍的第一手素材。
> 这是入口页,具体条目按主题拆成同目录下的独立页(用 `[[wikilink]]` 挂在下方)。
## 公司概览
(一句话业务定位 / 成立时间 / 主营。待补。)
## 团队
5 人团队成员、分工。可拆成 `company/team.md`。待补。)
## 组织与分工
(谁负责什么。待补。)
## 对外介绍
(标准话术、一句话介绍、产品/服务简介。可拆成 `company/pitch.md`。待补。)
## 待建条目
- [ ] `company/about.md` — 公司详细介绍
- [ ] `company/team.md` — 团队成员档案
- [ ] `company/pitch.md` — 对外话术/介绍
> 录入方式:`/kb-link` 点名或 `/kb-ingest company <源>`AI 自动补 frontmatter`type: doc`)。

0
concepts/.gitkeep Executable file
View File

43
docs/01-方案总览.md Executable file
View File

@@ -0,0 +1,43 @@
# 公司知识库 · 方案文档
> 公司内部 AI 自动化 · 第一件事
> 状态:方案阶段 | 最后更新2026-06-24
## 1. 定位
知识库是公司的**信息基石**,采用分层架构。第一件事只做底下两层——把分散信息可靠汇聚、留存、可溯源;上层的信息处理(问答/摘要/喂智能体)属于第二件事,本阶段只为其预留干净接口,不锁死设计。
## 2. 分层架构
```
┌─────────────────────────────────────────────┐
│ 4 呈现消费层 飞书为主(也可 CC / 网页 / 机器人) │ ← 以后
├─────────────────────────────────────────────┤
│ 3 处理层(多层) 清洗 / 结构化 / 摘要 / 问答 / 路由 │ ← 留接口,暂不做
├─────────────────────────────────────────────┤
│ 2 原始汇聚层 统一格式 + 元数据 + 可溯源 = 本体 │ ← 第一件事
├─────────────────────────────────────────────┤
│ 1 采集层 飞书/钉钉/企微/微信/NAS/本地文件 │ ← 第一件事
└─────────────────────────────────────────────┘
```
## 3. 团队与约束
- 5 人微团队,全员用 Claude CodeCC前期技术主导后续同事维护录入全员参与。
- 可用云端 API。呈现与沟通尽量以飞书为主。
- 微信内容后续切到飞书,再统一汇入。
## 4. 关键技术决策
| 决策 | 选择 | 理由 |
|------|------|------|
| 本体载体 | Git + 结构化 Markdown | CC 原生可读,零成本检索,天然做下游底座 |
| 检索方式 | 初期目录 + CC 语义检索 | 文档量小,先不上向量库,量大再加 |
| 平台绑定 | 接入多源,不锁死 | 呈现偏飞书,但本体独立可迁移 |
| 录入方式 | 混合 | 重要内容人工沉淀,高频流动(聊天)自动拉取 |
## 5. 起步策略
单点跑通:首个闭环 = **项目资料**。选一个在跑的项目,把其文档/聊天/决策汇成一处,验证闭环后再复制到其他项目与场景。
详见 [02-首个闭环-项目资料.md](02-首个闭环-项目资料.md)。

View File

@@ -0,0 +1,61 @@
# 首个闭环 · 项目资料
> 第一件事的单点验证场景
> 状态:方案阶段 | 最后更新2026-06-24
## 1. 目标
选一个正在跑的项目,把它散落在飞书文档、微信/飞书聊天、本地文件、NAS 里的资料,汇聚成**一处结构化、带元数据、可溯源**的库。跑通一个,再复制到其他项目。
## 2. 知识库本体结构
```
公司AI/
├── README.md # 总说明
├── docs/ # 方案文档(本目录)
├── CONVENTIONS.md # 录入规范(元数据/命名/目录约定)
└── projects/
└── <项目代号>/
├── _index.md # 项目概览、状态、关键决策索引
├── docs/ # 正式文档(从飞书/本地汇入)
├── decisions/ # 决策记录(每条带时间/参与人/背景)
├── conversations/ # 聊天沉淀(微信/飞书摘录)
└── assets/ # 附件、图、原始文件
```
## 3. 元数据约定(可溯源核心)
每个汇入文件头部带 YAML frontmatter
```yaml
---
title: 文档标题
source: 飞书文档 | 微信群 | 本地 | NAS
source_link: 原始链接或路径
author: 产生人
created: 2026-06-24 # 内容产生时间
ingested: 2026-06-24 # 汇入知识库时间
tags: [需求, 决策]
---
```
## 4. 录入方式(混合)
- **人工沉淀**:重要文档、决策——同事按规范手动放入(全员参与)。
- **自动拉取**:飞书文档走飞书开放 API 同步;聊天先人工摘录,后续接飞书机器人自动归集。
## 5. 跑通标准(验收)
选定项目资料归位后,任何人在 CC 里 `cd` 进该项目目录,就能让 AI 基于这堆资料回答:
- 这个项目现在进展到哪了?
- 某个决策为什么这么定?
- 客户/需求方要的是什么?
且**每条回答都能溯源到出处**(靠 frontmatter 的 source/source_link
## 6. 动手顺序
1. 建知识库骨架:`README.md` + `CONVENTIONS.md`(地基,先定死规范)。
2. 用户给定项目代号,建 `projects/<代号>/` 结构。
3. 现有资料先人工汇入,跑通闭环。
4. 验证 CC 检索效果 → 再考虑接飞书 API 自动同步。

View File

@@ -0,0 +1,116 @@
# 知识库框架选型 · 深度调研报告
> 主题:卡帕西 LLM Wiki 方法论≥2 套实现)+ Google OKF + 传统 RAG 知识库对比
> 用途:公司分层知识库选型
> 调研日期2026-06-24 | 方法smart-router → research-suite 后端 + WebSearch/WebFetch 交叉验证
## 摘要TL;DR
1. **卡帕西方法论 = "Stop Retrieving, Start Compiling"**(编译 > 检索。2026-04 提出X 上 1600 万阅读。核心:让 LLM 把原始资料"编译"成结构化、互链的 Markdown wiki一次性之后直接把 wiki 喂进上下文,而非每次查询都对原始文本跑向量检索。
2. **Google OKFOpen Knowledge Format= 卡帕西模式的标准化**。2026-06-12 Google Cloud 发布 v0.1,本质就是"带 YAML frontmatter 的 Markdown 目录",唯一必填字段 `type`。是"格式不是平台",无 SDK、无运行时、无锁定。
3. **三者不是互斥,而是分层互补**:卡帕西原理是"地基"OKF 是"地基的标准格式"RAG/向量库是"规模超过几百页后的导航层"。
4. **对我们 5 人团队的结论**现阶段方案Git+Markdown+frontmatter已天然命中 OKF/卡帕西模式,**方向正确,且应直接采用 OKF 的 frontmatter 约定**以获得未来可移植性。规模红线在"~100 源 / 几百页",远未到,**先不上 RAG**的判断成立。
详见各章。来源列表见末尾。
## 1. 卡帕西 LLM Wiki 方法论(方法论本体)
**核心洞察**:传统 RAG 是"健忘症"——每次查询都从零检索、合成、然后忘掉。知识从不沉淀系统永远长不大。卡帕西的替代LLM 当"编译器",读完所有源 → 抽取概念、压缩含义、建立交叉引用 → 产出结构化 wiki。查询时对**已编译的知识**推理,而非原始文本。
**关键特征**
- 知识"累积"而非"检索",是持久复利资产(与你机器里 `llm-wiki` SKILL 的"编译>检索"理念一字不差)。
- 纯 Markdown围绕 index.md 组织,无需向量库。
- 版本化、类型化、API 可访问,是 agent 推理的底座。
## 2. 卡帕西方法论的两套参考实现
### 实现 Allm-wiki SKILL本机已激活pkm-suite
- raw/→wiki/ 增量编译10 步摄入流程Delta Checkhash 去重)。
- Frontmatter Schema通用字段 type/title/created/updated/tags/status/related/sources + 类型特有字段source/entity/concept/comparison/question
- 三级查询Quick/Standard/Deep、Hot Cachehot.md ~500 词、8 项 Lint 健康检查、矛盾检测。
- 融合 claude-obsidian 精华。**这是我们手里现成的卡帕西实现**。
### 实现 BClaude Code + Obsidian 社区实现
- 用 Claude Code 把原始源编译进 Obsidian vaultsources/entities/concepts/comparisons 分目录。
- 与实现 A 同源同构,验证了"CC 做编译器 + Markdown vault 做载体"是社区主流落地路径。
## 3. Google OKFOpen Knowledge Formatv0.1
**定位**:把"LLM wiki"这个涌现模式,标准化成厂商中立、可移植的格式。作者 Sam McVeety / Amir HormatiGoogle Cloud451 行规格,"a format, not a platform"。GitHub 上附 3 个样例 bundle + 2 个参考实现。
**规格frontmatter 6 字段,仅 type 必填)**
| 字段 | 必填 | 含义 |
|------|------|------|
| `type` | ✅ 唯一必填 | 概念类别Metric/Table/Dataset/API/Runbook… |
| `title` | 选 | 人类可读名 |
| `description` | 选 | 一句话摘要 |
| `resource` | 选 | 指向底层资源的链接 |
| `tags` | 选 | 分组关键词 |
| `timestamp` | 选 | 信息最后有效时间 |
**结构**:一个 bundle = 一个 Markdown 文件目录,一文件一概念,文件路径即标识符。生产者与消费者解耦(谁定义 type、加什么字段由生产者决定。建议放版本控制里改动可审、有日期。
**OKF 刻意不做的事**:不是搜索排名信号、不是 web 发现标准(不同于 sitemap/llms.txt、不替代 schema.org/JSON-LD、v0.1 明确是草稿。
**OKF 与 RAG 的关系**OKF 面向"在公司知识上跑内部 copilot 或 RAG 的团队",把 bundle 加载进检索层,让 agent"先读 OKF 再猜"。即 OKF 是喂给 RAG 的优质结构化输入,二者协作。
## 4. 传统 RAG / 向量库 知识库
**机制**:文档切块 → 向量化 → 查询时相似度检索 top-k chunk → 喂 LLM 合成答案。
**优势**:规模无上限,适合海量、异构、高频更新语料;不需要预先"编译"。
**痛点(卡帕西批判的核心)**
- chunk 不透明、缺上下文,知识从不沉淀,每次查询从零开始("perpetual amnesia")。
- 准确度/速度/可维护性在"一类知识检索问题"上被 wiki 模式碾压(但仅限那一类)。
## 5. 规模红线与失效模式(选型最该警惕的)⚠️
卡帕西纯 wiki 模式在 **"约 100 个源 / 几百个页面"** 后破裂,两个原因:
1. **index.md 不可导航**目录条目太多LLM 首读的单一摘要文件失效,定位不到相关页。
2. **摄入悖论(循环依赖)**:编译新源时,"不先能导航大索引,就无法判断该更新哪些已有页"——要维护索引得先有可用索引。
**业界共识的解法 = 混合wiki + RAG**:保留"编译知识"的思想,但用**向量库替代 index.md 做导航层**,从而支持任意规模。检索负责"找到相关 chunk"wiki 构建负责"结构化它们",结构化 wiki 作为合成的输入。结论是"RAG 没死,是 wiki 和 RAG 互补"。
- 注意残留局限:矛盾检测受限于检索——不相似的文档永不共现,冲突检测不到。
## 6. 三方对比矩阵
| 维度 | 卡帕西 LLM Wiki | Google OKF | 传统 RAG |
|------|----------------|-----------|---------|
| 本质 | 方法论(编译>检索) | 格式标准 | 检索架构 |
| 载体 | Markdown + index | Markdown + frontmatter | 向量库 + chunk |
| 知识沉淀 | ✅ 累积复利 | ✅(格式层面) | ❌ 每次从零 |
| 规模上限 | ~100源/几百页 | 同 wiki它是格式 | 无上限 |
| 可移植/无锁定 | ✅ | ✅✅(标准化) | 取决于实现 |
| 检索准确度 | 高(小规模) | — | 中chunk 噪声) |
| 基建复杂度 | 极低 | 极低 | 高 |
| CC 原生友好 | ✅✅ | ✅✅ | ❌ 需中间层 |
## 7. 对公司分层知识库的选型建议
1. **采集层 + 原始汇聚层(第一件事)= 直接落地卡帕西/OKF 模式**。我们原方案Git+Markdown+frontmatter已命中只需把 frontmatter **对齐 OKF 6 字段**type 必填)+ 保留我们溯源字段source/author/created/ingested获得未来可移植性与 Google 生态背书。
2. **复用 `llm-wiki` SKILL 作为"编译器"**raw/→wiki/ 增量编译、Lint、矛盾检测都现成是处理层的天然第一层不必重造。
3. **先不上 RAG**5 人团队、项目资料量级远低于 ~100 源红线。`先不上向量库` 的原判断被证实正确。
4. **预留混合演进路径**当某个领域语料逼近几百页、index 开始不可导航时,按业界混合方案引入向量库当"导航层"wiki 编译层不变。这给处理层留了清晰的升级接口。
5. **type 体系要早定**OKF 把 type 交给生产者定义。我们应在 CONVENTIONS.md 里先约定一套公司 type如 project/decision/conversation/doc/runbook/metric避免后期不一致。
## 8. 来源
卡帕西方法论:
- [Beyond RAG: Karpathy's LLM Wiki Pattern](https://nayakpplaban.medium.com/beyond-rag-how-andrej-karpathys-llm-wiki-pattern-builds-knowledge-that-actually-compounds-31a08528665e)
- [The Compiler Analogy Explained](https://www.mindstudio.ai/blog/karpathy-llm-knowledge-base-architecture-compiler-analogy)
- [Building an LLM Wiki with Claude Code and Obsidian](https://medium.com/@manavghosh/building-an-llm-wiki-with-claude-code-and-obsidian-eb6c0990e723)
- [Karpathy's LLM Wiki got 16M views](https://implicator.ai/karpathys-llm-wiki-got-16-million-views-the-code-isnt-the-point-2/)
Google OKF
- [Google Cloud 官方博客OKF](https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing)
- [OKF: What It Is, the Spec, How to Use It](https://www.startuphub.ai/ai-news/insights/2026/google-open-knowledge-format-okf-explained-2026)
- [OKF wants to be the lingua franca](https://www.ppc.land/googles-okf-wants-to-be-the-lingua-franca-for-ai-agent-knowledge/)
- [Google Open-Sources OKF (451 lines, 1 required field)](https://www.implicator.ai/google-open-sources-a-knowledge-format-and-wires-it-into-its-catalog/)
- [Publishing an OKF bundle with 11ty6 字段细节)](https://www.simoncox.com/post/2026-06-17-publishing-an-okf-bundle-with-11ty/)
对比与规模失效:
- [The LLM Wiki at Scale: From Personal Research Tool to Production RAG规模红线](https://michalnasternak.medium.com/the-llm-wiki-at-scale-from-personal-research-tool-to-production-rag-247710a1284c)
- [Karpathy's AI Wiki vs Structured Databases](https://www.mindstudio.ai/blog/karpathy-wiki-vs-structured-database-ai-memory)
- [Did Karpathy's LLM Wiki Just Kill RAG? Enterprise Verdict](https://www.epsilla.com/blogs/llm-wiki-kills-rag-karpathy-enterprise-semantic-graph)
- [LLM Wiki vs RAG for Internal Codebase Memory](https://www.mindstudio.ai/blog/llm-wiki-vs-rag-internal-codebase-memory)
> 调研方法说明research-suite 后端本轮 GATHER 未命中quality WARNING, 0 source已切换 WebSearch/WebFetch 兜底并交叉验证。关键结论OKF 发布日期、6 字段、规模红线)均 ≥2 源印证。

181
docs/04-架构.md Executable file
View File

@@ -0,0 +1,181 @@
# 04 · 架构
> 本文是知识库物理形态与数据流的权威描述。目录布局以此为准,`tools/kb-init.sh` 与 `CONVENTIONS.md` 必须与之一致。
> 核心信条:**单一 Git 真相源 + OKF frontmatter + AI 主执行 + 可生长不重做**。
---
## 1. 四层架构
知识从产生到被消费,流经四层。每层职责单一,层间靠**文件 + frontmatter**解耦,不靠数据库或服务。
```
┌──────────────────────────────────────────────────────────────┐
│ L4 呈现层 Presentation │
│ Obsidian个人主编辑器·wikilink·图谱 + 飞书(公司呈现) │
│ CC 问答cd 进目录语义检索MVP 默认查询方式) │
├──────────────────────────────────────────────────────────────┤
│ L3 处理层 ProcessingMVP 极薄,按信号生长) │
│ kb-lint-fm.py 校验 · CC 语义检索 · Delta Check 去重 │
│ [P4 触发] llm-wiki 全量编译raw markdown → 互链 wiki+hot+index│
│ [P5 红线] 向量库仅作导航层,替代 index.md │
├──────────────────────────────────────────────────────────────┤
│ L2 汇聚层 Aggregation ★唯一真相源★ │
│ 公司AI/ Git 仓库 · 扁平 projects/ 布局 · 每页带 OKF frontmatter│
│ 文件本身即真相,无 raw//wiki/ 双层,无中间数据库 │
├──────────────────────────────────────────────────────────────┤
│ L1 采集层 Collection │
│ 人工丢资料Obsidian 直接写) · feishu-pull.py 半自动导出 │
│ 本地/NAS 文件 · 会议记录 · 聊天摘录 │
└──────────────────────────────────────────────────────────────┘
```
**各层要点:**
- **L1 采集层**:知识入口。唯一强人工动作是「点名物料 + 一句说明」。三条采集通道(见下)汇入同一真相源。飞书文档经 `tools/feishu-pull.py` 半自动单向导出流入 `projects/*/docs/`;个人手记直接在 Obsidian 里写。凭据走环境变量,不入库。
**三条采集通道:**
| 通道 | 适合物料 | 方式 | 落成 |
|---|---|---|---|
| 飞书群/文档 | 对话、临时决策、协作文档 | `feishu-pull.py`P2 | `conversation`/`doc` |
| NAS 各人投递区 | 文件类PDF/Word/Excel/扫描件) | 点名或投递区扫描 | `doc`+可选 `assets` |
| 工作目录/URL 点名 | 任意单个文件/链接 | `/kb-link` 编译链接 | `doc`/`decision` |
**「编译过的链接」模型(核心,抗膨胀+抗失联)**:默认**不把原件搬进库**。人点名一个物料(工作目录文件 / NAS 路径 / URLAI 读一遍→提炼精华写成一页知识→页面留 `source_link` 指向原件。好处:①库只存提炼后 markdown不膨胀②精华进库可检索可溯源③原件日后移动/删除提炼的知识仍在纯快捷方式做不到。少数关键且易变的原件AI 提议、用户点头后才复制进 `assets/` 兜底。
**NAS 投递区(每人各自)**`NAS/<同事>/📥知识库投递/`——AI 只扫这个专用子目录,**私人工作目录永不主动触碰**(隐私隔离 + 噪音从源头掐掉)。投递区跟各自 NAS 同步走不新增基建。MVP 按需扫描,不做常驻监听 daemon过度工程投递频繁再说
- **L2 汇聚层(真相源)**:整个体系的地基。就是 `公司AI/` 这个 Git 仓库本身。**文件即真相源**——没有 raw/ 和 wiki/ 双层,没有中间数据库。每个知识页顶部有 OKF frontmatter见第 2 节 schema唯一契约在 `meta/kb-contract.yaml`)。版本、审计、冲突仲裁、回滚全靠 Git。
- **L3 处理层MVP 极薄)**MVP 阶段只有 `kb-lint-fm.py`(校验)+ CC 直接对 markdown 做目录/语义检索 + Delta Check 去重。**不生成** `hot.md`/`log.md`/`wiki/` 等导航产物。llm-wiki 全量编译到 **P4** 才启用,向量库到 **P5 红线**才上。
- **L4 呈现层**三个视图共享同一真相源。Obsidian 是个人主编辑器,飞书是公司**双向前端**(采集入口 + 群问答查询 + 呈现面CC 是问答入口。飞书呈现走**双载体**:云文档(叙事型:项目概览/决策)+ 多维表格 Bitable结构型全库可筛选/看板)。群机器人问答是**读**操作(调 `/kb-ask`),不构成第二条写路径。详见 `docs/08-飞书双向前端方案.md`
---
## 2. 扁平目录树
```
公司AI/ # Git 根 = 唯一真相源
├── README.md # 总索引 + 快速上手
├── CLAUDE.md # CC 项目级工作记忆
├── CONVENTIONS.md # 录入宪法(引用 kb-contract.yaml
├── .gitignore # .obsidian/ .trash/ 生成产物 大 assets
├── docs/ # 方案文档
│ ├── 01-方案总览.md # 【不动】
│ ├── 02-首个闭环-项目资料.md # 【不动】
│ ├── 03-知识库框架选型调研.md # 【不动】
│ ├── 04-架构.md # 本文
│ ├── 05-路线图.md
│ ├── 06-工具链.md
│ ├── 07-AI-native工作流.md
│ └── obsidian-setup.md # 共享插件清单
├── meta/
│ ├── kb-contract.yaml # ★唯一机器可读契约type 集 + 必填矩阵 + enum
│ ├── ingest-manifest.json # 摄入 hash 台账(去重)
│ └── stats.md # 源计数/页数(红线观察点)
├── projects/ # 项目知识(首个闭环即用)
│ ├── _example/ # OKF 完整示例,照抄即合规
│ │ ├── _index.md # type: project
│ │ ├── docs/ # type: doc
│ │ ├── decisions/ # type: decision
│ │ ├── conversations/ # type: conversation
│ │ └── assets/ # 原始附件(唯一免 frontmatter 目录)
│ └── <代号>/ ... # 英文/拼音代号,禁纯中文目录名
├── entities/ # type: entityreserved首次真实需要时建
├── concepts/ # type: conceptreserved
├── questions/ # type: questionreserved查询回写
├── tools/
│ ├── kb-init.sh # 建项目骨架
│ ├── kb-lint-fm.py # frontmatter 校验(读 kb-contract.yaml
│ └── feishu-pull.py # 飞书半自动导出
└── .claude/commands/ # /kb-new /kb-ingest /kb-ask /kb-save /kb-check /kb-status
```
**布局裁决要点:**
- **扁平 `projects/`**,不建 `raw//wiki/` 双层。文件本身即真相源。
- **免 frontmatter 目录**`assets/`(原始附件);仓库脚手架 `README.md`/`CONVENTIONS.md`/`docs/*`
- **禁纯中文目录名**:项目代号用英文/拼音CC 会把中文编码为 `-` 导致 ID 碰撞)。
- **reserved type 目录**`entities/`/`concepts/`/`questions/`)首次真实需要时才建,激活即在 `kb-contract.yaml` 标记 active。
---
## 3. gitignore 与生成产物策略
编译/导航产物**不进 Git**,本地按需由 CC 重建 → 消除多人并发合并冲突。
| 类别 | 是否入 Git | 说明 |
|---|---|---|
| markdown 知识页 + frontmatter | ✅ | 真相源,全部提交 |
| `_index.md`(项目门户,人写/CC 维护) | ✅ | 项目导航锚点 |
| `meta/kb-contract.yaml` / `stats.md` | ✅ | 契约与观察点 |
| `hot.md` / `log.md` / `wiki/`(全库导航产物) | ❌ | gitignore本地按需重建P4 起由**单一执行者**生成并提交 |
| `index.md`(全库索引) | ❌ | 同上 |
| `.obsidian/` | ❌ | gitignore插件清单共享在 `docs/obsidian-setup.md` |
| `.trash/` | ❌ | Obsidian 回收站 |
| 大体积 assets视频/大图/二进制) | ❌ | 按需,超阈值不入库 |
> **杀掉合并冲突磁石**`_index.md` 之外的全库导航(`hot.md`/`log.md`/`wiki/`/`index.md`)不进 Git。MVP 阶段 CC 直接对 markdown 做检索,根本不生成这些文件。
---
## 4. 双前端映射
两个前端物理坐在**同一个 Git 仓库** `公司AI/`,是同一真相源的两种视图。**MVP 只有一条写路径**(避免两个前端同时写 Git 起冲突)。
| 前端 | 角色 | 读 | 写 |
|---|---|---|---|
| **Obsidian** | 个人主编辑器 | 直接把 `公司AI/` 当 vault 打开wikilink/图谱浏览 | **唯一写路径**:直接编辑 markdown → `git push` |
| **飞书** | 公司双向前端 | 全员看项目概览/决策(云文档+Bitable@机器人 问答 | **不直接写库**:文档经 `feishu-pull.py` 单向导入;群问答是读操作(`/kb-ask`),不写库 |
| **CC** | AI 问答与自动化执行 | `cd` 进目录语义检索 | 录入/回写循环产出合规 markdown受 lint 约束) |
- `.obsidian/` **gitignore**,共享插件清单在 `docs/obsidian-setup.md`(装 Dataview。Git 同步交给 CC/命令行,**不用** Obsidian Git 插件自动提交。
- **个人草稿隐私边界**:个人未定稿走各自**本地 vault不入共享仓**;经一次轻量评审晋升后,文件才移入 `projects/`/`entities/``source` 记晋升来源。公司仓无 `personal/` 分区。
- **删除**(对 5 人是重型基建):飞书双向 API、机器人写回 `conversations/`、双前端一致性校验脚本。
---
## 5. 下游消费接口
知识库对下游 agent / 工具的暴露接口,全部基于文件系统,无需服务:
1. **文件系统接口**:任何 agent `cd``公司AI/` 即可 grep/read markdown。这是最低层、最稳定的接口。
2. **frontmatter 过滤接口**:下游按 `type`/`status`/`tags`/`source` 过滤页面(例:只喂 `type: decision && status: mature` 给报价 agent。契约在 `meta/kb-contract.yaml`,生产者与消费者依赖同一契约。
3. **CC 子 agent 接口**:主 CC 派生子 agent 做检索/编译/校验,子 agent 读同一真相源,结论必带 `source_link` 溯源。
4. **溯源锚点**:每页 `source` + `source_link` 是所有下游答案的强制溯源依据——**无据即报 Gap绝不用训练数据编造**。
> 未来出现**非 CC 消费方**时再建 MCP server出现 P5 红线时向量库只做「找到相关页」的导航层type 契约与编译层原样保留。
---
## 6. 演进阶梯(架构如何生长)
架构分阶段生长,**后阶段是生长而非重做**frontmatter/type 契约从 P0 到 P5 稳定不变。
| 阶段 | 架构增量 | 触发信号 |
|---|---|---|
| **P0** | L2 真相源物理形态定死 + `kb-contract.yaml` + 命令雏形 | 照规范建页、`/kb-check` 通过、CC 能读并引用 |
| **P1** | 首个项目全汇入L3 仅 lint + CC 检索 | 该项目问答稳定,产生复制需求 |
| **P2** | L1 加 `feishu-pull.py` 半自动增量导入 | 项目数 ≥3飞书成主要文档源 |
| **P3** | L2 加 `company/`/`domains/` 分区Obsidian 成团队习惯 | 库覆盖主要知识,产生编译/喂 agent 需求 |
| **P4** | L3 启用 llm-wiki 全量编译(互链 wiki + hot/index下游 agent 消费接口 | **`_index` 首读定位失效 + 摄入悖论显现** |
| **P5** | L3 加向量库导航层(仅替代 index.md编译层零改动 | `~100 源/几百页` + index 首读定位不准/摄入悖论 |
> **P5 向量库红线(唯一硬触发点)**:在 `~100 源/几百页` 且出现 `index 首读定位不准`/`摄入悖论` 之前,坚决不上向量库/RAG——任何「要不要上 RAG」的默认答案是「不」。详见 `docs/05-路线图.md`。
---
## 附:架构层面的单一真相源
| 契约 | 唯一存放处 | 谁读它 |
|---|---|---|
| type 集 + 字段必填矩阵 + enum | `meta/kb-contract.yaml` | kb-lint-fm.py 运行时读 |
| 目录布局 | 本文 `docs/04-架构.md` | kb-init.sh / CONVENTIONS 与之一致 |
| 溯源锚点 | 每页 `source`/`source_link` | 所有下游 agent 答案必带 |
任何脚本硬编码 type/字段一律禁止——读契约文件。

122
docs/05-路线图.md Executable file
View File

@@ -0,0 +1,122 @@
# 05 · 路线图
> P0→P5 分阶段作战图。每阶段可独立交付/验收/止步;**后阶段是生长而非重做**。frontmatter/type 契约从 P0 到 P5 稳定不变。
> 反过度工程铁律:编译层、向量库按**信号触发**延后,不预建。
---
## 阶段总览
| 阶段 | 目标(一句) | 进入下阶段信号 |
|---|---|---|
| **P0 骨架+规范** | 定死唯一真相源物理形态 + 录入契约 + 命令雏形 | 任一成员照规范建合规文档、`/kb-check` 通过、CC 能读到并引用 |
| **P1 首个项目闭环** | 一个在跑项目资料全汇入、CC 可溯源问答 | 该项目问答稳定、同事认可价值,产生复制到第 2/3 个项目的需求 |
| **P2 多项目+飞书导入** | 扩到多项目 + `feishu-pull.py` 半自动增量导入 | 项目数 ≥3、飞书成主要文档源、公司级非项目知识无处安放 |
| **P3 全公司领域+Obsidian 成熟** | 加 `company/`/`domains/` 分区 + Obsidian 编辑成团队习惯 | 库覆盖主要知识、录入成习惯,产生「让 AI 主动编译/问答/喂 agent」需求 |
| **P4 处理层/编译+下游 agent** | 启用 llm-wiki 全量编译 + 下游 agent 消费接口 | **信号触发点:`_index` 首读定位失效 + 摄入悖论显现** |
| **P5 规模化(向量库)** | 向量库仅当**导航层**替代 index.md编译层零改动 | 终态 |
---
## P0 · 骨架 + 规范
**目标**:定死唯一真相源物理形态 + 录入契约 + 命令雏形。
**交付物**
- 目录骨架(`projects/` `entities/` `concepts/` `questions/` `meta/` `tools/` `.claude/commands/`+ `.gitignore`
- `meta/kb-contract.yaml`机器可读契约types 数组含 active/reserved 标记 + 字段必填矩阵 + status/source enum
- `CONVENTIONS.md` + `CLAUDE.md`(项目级)+ `docs/04~07`
- `tools/kb-init.sh` + `tools/kb-lint-fm.py`
- `projects/_example/`OKF 完整示例,照抄即合规)。
- `.claude/commands/``/kb-new` `/kb-ingest` `/kb-ask` `/kb-save` `/kb-check` `/kb-status`)。
**验收**:任一成员照规范建一页合规文档 → `/kb-check` 通过 → CC `cd` 进目录能读到并在回答中引用(带 source_link
**止步点**:若只需「可溯源 markdown + CC 检索」,停在 P0/P1 完全合理。
---
## P1 · 首个项目闭环
**目标**把一个在跑项目的资料全部汇入CC 能可溯源回答「进展如何 / 为何这么定 / 需求方要什么」。
**交付物**
- 选一个真实在跑项目 → `/kb-new <代号>` 建骨架。
- 全部相关资料(决策/聊天/文档汇入frontmatter 合规。
- CC 问答稳定,每条结论挂 `source_link`
**验收**:该项目三类问题(进展 / 决策依据 / 需求)问答稳定准确,同事认可价值。
**进入 P2 信号**:产生「复制到第 2/3 个项目」的需求。
---
## P2 · 多项目 + 飞书导入
**目标**:扩到多项目 + `feishu-pull.py` 半自动增量导入。
**交付物**
- 复制 P1 模式到 ≥3 个项目。
- `tools/feishu-pull.py` 上线,半自动单向导出飞书文档到 `projects/*/docs/`AI 自动补 frontmatter。
- `meta/ingest-manifest.json` 台账记录 hash 去重。
- **飞书双向前端**(见 `docs/08`
- 群机器人能问能答(`tools/kb-bot/`,长连接无公网):群 @机器人`/kb-ask` → 带溯源回复。
- 飞书呈现双载体(`tools/feishu-render.py`):云文档(叙事)+ 多维表格 Bitable结构可筛选/看板)。
- NAS 投递区扫描(`tools/nas-pull.py`,编译链接模式)。
**验收**:飞书成为主要文档源;群里 @机器人 问答稳定带溯源;多维表格全库总览可用。
**进入 P3 信号**:项目数 ≥3、飞书成主要文档源、**公司级非项目知识无处安放**。
---
## P3 · 全公司领域 + Obsidian 成熟
**目标**:加 `company/`/`domains/` 分区 + Obsidian 编辑成团队习惯。
**交付物**
- 新增公司级分区(`company/`/`domains/`),承接非项目知识。
- 视需要激活 `entity`/`concept` type 并建目录。
- Obsidian 成为团队日常编辑器,`docs/obsidian-setup.md` 插件清单落地Dataview 等)。
**验收**:库覆盖主要知识、录入成习惯。
**进入 P4 信号**:产生「让 AI 主动编译/问答/喂 agent」的需求。
---
## P4 · 处理层 / 编译 + 下游 agent
**目标**:启用 llm-wiki 全量编译(生成互链 wiki + hot/index+ 下游 agent 消费接口。
**交付物**
- llm-wiki 全量 raw→wiki 编译层启用,产物由**单一执行者**生成并提交(`wiki/`/`hot.md`/`index.md` 仍 gitignore 或按约定提交,避免并发冲突)。
- 下游 agent 消费接口frontmatter 过滤 + CC 子 agent
**验收**:编译产物可用,下游 agent 能按 `type`/`status` 过滤消费。
**★ 信号触发点**`_index` 首读定位失效 + 摄入悖论显现——**在此之前坚决不启用全量编译**。
---
## P5 · 规模化(向量库)
**目标**:向量库仅当**导航层**替代 `index.md`,卡帕西编译层与 type 契约原样保留。
**★ P5 向量库红线(唯一硬触发点)**
> `~100 源 / 几百页` **且** 出现 `index 首读定位不准` / `摄入悖论`。
> 在此之前——**任何「要不要上 RAG」的默认答案是「不」**。
**交付物**
- 向量库只做「找到相关页」的导航层,替代 `index.md` 首读定位。
- 编译层零改动type 契约原样保留。
**验收**:向量库导航准确率优于 index.md 首读定位,且未触碰 frontmatter/type 契约。
---
## 生长机制(预留,不落地)
生长机制降为**预留接口说明**,不落地 `/kb-grow` 命令与监控脚本:
- 仅保留 `meta/stats.md` 人工可读源计数作红线观察点。
- **不预建**密度/熵增监控。到 P5 红线时再启用向量库导航层,接口预留。

78
docs/06-工具链.md Executable file
View File

@@ -0,0 +1,78 @@
# 06 · 工具链
> 三档工具清单:立即复用 / 立即轻量自建 / 未来再建。附各脚本用途与 gitignore 策略。
> 反过度工程MVP 只落地「立即」两档,未来档按信号触发。
---
## 【立即复用】现成能力,零自建
| 工具 | 用途 | MVP 边界 |
|---|---|---|
| **llm-wiki SKILL** (`~/.claude/skill-repository/pkm-suite/llm-wiki/SKILL.md`) | 其 lint 检查项、Delta Check、hot cache 模式作参考 | **MVP 仅借用查询/lint 思路**,全量 raw→wiki 编译 P4 才启用 |
| **Git** | 版本 = 审计 + 冲突仲裁 + 回滚 | 唯一同步与真相源机制 |
| **Obsidian** | 直接打开 `公司AI/` 当 vault零镜像 | wikilink/图谱浏览;不用 Obsidian Git 插件自动提交 |
| **CC 文件系统检索** | MVP 默认问答方式(`cd` 进目录直接语义检索 markdown | 不生成 index/hot/wiki直接对 markdown 检索 |
---
## 【立即轻量自建】P0 落地
### `CLAUDE.md`(项目级)
让 CC 感知库结构 / type / frontmatter / 溯源硬约束。type 白名单**指向契约文件不复制清单**。
### `tools/kb-init.sh`
一键幂等建 `projects/<代号>/` 骨架:
-`_index.md``type: project` + `status: seed`+ `docs/` `decisions/` `conversations/` `assets/` 子目录。
- 幂等:已存在不覆盖。
- 目录布局与 `docs/04-架构.md` 一致。
### `tools/kb-lint-fm.py`
frontmatter 校验器:
- **PyYAML 解析**(正确解析 `related`/`tags` 列表,不用正则)。
- **读 `meta/kb-contract.yaml`** 判定 type 白名单 + 必填矩阵 + status/source enum**不硬编码**。
- 退出码非 0 供 CC 判定;输出分级(阻断 / 提示)。
- 检查项见 `CONVENTIONS.md` §6。
### `tools/feishu-pull.py`
飞书半自动单向导出:
- **凭据走环境变量,不入库**。
- 拉取飞书文档 → 落 `projects/*/docs/`
- AI 自动补 `title`/`ingested`/`source`/`source_link`
- 单向(飞书→仓库),**不写回飞书**。
### `.claude/commands/`
`/kb *` 薄封装(命令表见 `docs/07-AI-native工作流.md`,此处引用不复述):
`/kb-new` `/kb-ingest` `/kb-ask` `/kb-save` `/kb-check` `/kb-status`
---
## 【未来再建】按信号触发,不预建
| 工具 | 触发信号 | 说明 |
|---|---|---|
| **llm-wiki 全量编译层** | P4`_index` 首读失效 + 摄入悖论) | raw→wiki 全量编译,产物单一执行者生成 |
| **向量库导航层** | P5 红线(~100 源/几百页 + index 定位不准) | 仅做「找到相关页」,编译层零改动 |
| **飞书机器人查询入口** | 非 CC 消费方出现 | 飞书内直接问答 |
| **多维表格录入** | 结构化录入需求 | 飞书多维表格 → 仓库 |
| **MCP server** | 出现非 CC 消费方 | 对外暴露知识库接口 |
> **默认答案是「不」**:任何「要不要上 RAG / 编译 / MCP」的默认答案是「不」直到对应信号出现。
---
## gitignore 策略汇总
| 类别 | 入 Git | 说明 |
|---|---|---|
| markdown 知识页 + frontmatter | ✅ | 真相源 |
| `_index.md` | ✅ | 项目门户锚点 |
| `meta/kb-contract.yaml` / `stats.md` / `ingest-manifest.json` | ✅ | 契约 + 台账 |
| `tools/*` | ✅ | 脚本 |
| `hot.md` / `log.md` / `index.md` / `wiki/` | ❌ | 生成产物,本地按需重建;消除并发冲突 |
| `.obsidian/` | ❌ | gitignore插件清单在 `docs/obsidian-setup.md` |
| `.trash/` | ❌ | Obsidian 回收站 |
| 大体积 assets | ❌ | 超阈值不入库 |
| 凭据 / `.env` | ❌ | 走环境变量,绝不入库 |
详见 `docs/04-架构.md` §3。

113
docs/07-AI-native工作流.md Executable file
View File

@@ -0,0 +1,113 @@
# 07 · AI-native 工作流
> 统一命令面 `/kb <verb>` + 四循环 + 人机分工铁律。**本文是命令列表的唯一真相源**,工具链文档引用不复述。
> 核心AI 主执行,人是监督者与拍板者;丢资料是唯一强人工入口。
---
## 1. 统一命令面 `/kb <verb>`
一处列全,工具链与工作流共用。命令实现在 `.claude/commands/`
| 命令 | 作用 |
|---|---|
| `/kb-new <代号>` | 建项目骨架(调 `kb-init.sh` |
| `/kb-ingest <代号> <源>` | 录入循环入口丢资料AI 全包) |
| `/kb-link <文件/NAS路径/URL>` | 编译链接:点名物料,提炼精华+留指针,不搬原件 |
| `/kb-ask <问题>` | 查询循环入口(强制溯源) |
| `/kb-save` | 把对话洞察回写为 `questions/` 页 |
| `/kb-check` | frontmatter 校验 + lint 体检(调 `kb-lint-fm.py` |
| `/kb-status` | 源计数 / 健康仪表盘(读 `meta/stats.md` |
> 删除 `/kb-grow`——生长机制降为预留接口,不落地命令与监控。
---
## 2. 四循环
### 循环 A · 录入Ingest
**人只做**:丢资料 + 一句说明 + 对要点点头。
**AI 自动**
1. **Delta Check 去重** → 比对 `meta/ingest-manifest.json` hash 台账。
2. **落原文** → 外部文档进 `projects/*/docs/``assets/`
3. **抽关键发现** → 从原文抽要点。
4. **写合规页** → frontmatter 自动补齐(`title`/`ingested`/`status`/可推导 `source`)。
5. **建 wikilink** → 识别提及的实体/项目/决策自动挂链。
6. **矛盾不静默覆盖** → MVP 用简单标注挂起(正文加矛盾标记),**不做自动 status 降级状态机**。
> **MVP 一次录入产出结构化 markdown不做 8-15 页 wiki 编译**P4 才编译)。
### 循环 B · 查询Ask
- CC 直接**语义检索 markdown** 回答MVP 不依赖向量库)。
- **每条结论挂 `source_link` 溯源**。
- 覆盖不足**明说缺口****绝不用训练数据编造**——无据即报 Gap。
- 有沉淀价值时**提议 `/kb-save`** 回写。
### 循环 C · 维护Maintain
- `/kb-check` 跑 lint孤立页 / 死链 / 缺字段 / 过时索引)。
- **保持轻量提示,不做周度 cron 强制门禁**;靠 Git PR review + CC 提示。
- **安全项 AI 自动修**:补可推导字段、建 stub、加 wikilink。
- **有损项挂起交人**:删页、解决矛盾、合并、改 type。
### 循环 D · 生长Grow预留
- 一段说明即可——「到 P5 红线时再启用向量库导航层,接口预留」。
- 仅保留 `meta/stats.md` 人工可读源计数作红线观察点。
- **不预建**密度/熵增监控。
### 循环 E · 飞书群问答P2 上线)
同事在飞书群 @机器人 提问,机器人查知识库带溯源回复。**长连接实现,无需公网**(见 `docs/08`)。
- **人只做**:群里 @机器人 问一句。
- **AI 自动**`kb-bot` 收事件 → 抽问题 → 调 `/kb-ask`(复用查询循环 B 的全部铁律)→ `lark-cli` 回复到 thread。
- **只读不写**:机器人绝不写/删知识库;答案带 `source_link`,无据回「知识库暂无」,绝不编造。
- 人机分工同**循环 B 查询**(此处只是把入口从 CC 换成飞书群)。
---
## 3. 人机分工表
| 环节 | 人 | AI |
|---|---|---|
| 采集 | **丢资料 + 一句说明**(唯一强人工入口) | 拉取/去重/落盘 |
| frontmatter | 拍板 `type`(采集器常可推断) | 自动补 `title`/`ingested`/`status`/`source` |
| 结构化 | 对要点点头 | 抽发现、写合规页、建 wikilink |
| 查询 | 提问、判断可用性 | 检索、溯源回答、报 Gap |
| 矛盾 | 拍板解决 | 标注挂起,不擅自覆盖 |
| 维护 | review 有损操作 | 自动修安全项、提示 |
| 删/合并/改 type | **拍板** | 挂起交人 |
---
## 4. 人机分工铁律
1. **能让 AI 自动做的绝不让人做**——丢资料是唯一强人工入口。
2. **AI 永不擅自有损操作**——删页/覆盖/合并/改 type 必挂起交人。
3. **AI 永不编造**——无据即报 Gap绝不用训练数据填坑。
4. **人是监督者与拍板者**——AI 执行,人决策。
---
## 附:典型会话示范
```
用户:把客户 A 昨天飞书群里定报价那段整理进来
→ /kb-ingest clientA 飞书群
→ AI: Delta Check 通过(新内容)→ 落原文 → 抽出「改用阶梯定价」决策
→ 写 projects/clientA/decisions/stepped-pricing.md
→ frontmatter 自动补齐related 挂到 [[project-clientA]]
→ 请确认要点:①阶梯定价 ②牺牲首单毛利 ③锁长期合作
用户:对
→ AI: 已提交。
用户:客户 A 为什么不用一口价?
→ /kb-ask 客户 A 为什么不用一口价
→ AI: 因客户预算分期到位,改用阶梯定价锁长期合作、牺牲首单毛利。
溯源projects/clientA/decisions/stepped-pricing.md
source: 飞书群 https://company.feishu.cn/im/xxxx?msg=99231
```

View File

@@ -0,0 +1,119 @@
# 08 · 飞书双向前端方案(群机器人 + 呈现)
> 把飞书从「单向导出源」升级为「双向前端」:采集入口 + 查询入口 + 呈现面。
> 状态:方案待批 | 底座已验证lark-cli 已装(bot 身份 cli_a9458d254e3a5bc3),鉴权可用。
## 关键技术验证结论(已实测)
| 能力 | 命令 | 状态 |
|---|---|---|
| 发消息 | `lark-cli im +messages-send --chat-id oc_xxx --markdown "..."` | ✅ bot 可用 |
| 回复(thread) | `lark-cli im +messages-reply` | ✅ |
| **收群消息** | `lark-cli event +subscribe --event-types im.message.receive_v1 --compact` | ✅ **WebSocket 长连接,无需公网回调** |
| 找群 chat_id | `lark-cli im +chat-search --query "群名"` | ✅ |
| 多维表格 | `lark-cli api POST /open-apis/bitable/v1/apps/...`(通用 apiCLI 1.0.3 无 bitable 子命令但通用 api 通鉴权) | ✅ |
| 云文档 | `lark-doc-manager` skill已激活/ `lark-cli docs` | ✅ |
> **最大收获**:能问能答走**长连接**而非公网 HTTP 回调——省掉 VPS 上常驻 web 服务5 人团队负担骤降。
---
## 一、群机器人:能问能答
### 架构(长连接,无公网)
```
同事在飞书群 @机器人 "客户A为什么不用一口价"
↓ 飞书事件 (im.message.receive_v1)
lark-cli event +subscribe ── WebSocket 长连接 ──> 本地/VPS 常驻进程
↓ NDJSON 每行一事件,--compact 提取文本
kb-bot 调度器:解析 → 剥离 @ → 得到问题
调 CC 跑 /kb-ask基于知识库检索强制溯源无据报 Gap
lark-cli im +messages-reply ── 回复到原消息 thread
```
### 落地组件
- `tools/kb-bot/subscribe.sh` — 起长连接:`lark-cli event +subscribe --event-types im.message.receive_v1 --compact --output-dir ./events/`
- `tools/kb-bot/handle.py` — 消费事件:过滤 @机器人 的消息 → 抽问题 → 调 CC headless (`claude -p "/kb-ask <问题>"`) → 取答案 → `lark-cli im +messages-reply`
- 防重event 有 idempotencyhandle 记已处理 message_id。
- 铁律继承:答案强制带 source_link无据回「知识库暂无可 /kb-link 补充」,绝不编造。
### 部署MVP
- 跑在**一台常开机器**(你的 Mac mini / LA VPS 均可,长连接不挑落点)。
- MVP 用 shell + 轮询 events 目录起步;稳定后再包成 launchd/systemd 常驻。
- **不建公网服务、不开端口**——这是刻意的反过度工程。
---
## 二、飞书呈现架构:三层投影
**核心原则:飞书是「读屏」不是「数据库」。** 真相源永远是 Git 仓库,飞书上所有内容都是从 Git **单向投影**的镜像;同事在飞书上看/问,不在飞书上改知识(改走 Obsidian/Git。飞书怎么动都不污染真相源。
三层对应同事三种消费姿势:
```
┌─────────────────────────────────────────────────────┐
│ 第3层 群机器人(问) "客户A为什么不用一口价" │ 找答案·秒回
│ @机器人 → /kb-ask → 带溯源回复(实时,读 Git 真相源) │
├─────────────────────────────────────────────────────┤
│ 第2层 多维表格(查) 全库一张表,按项目/状态筛选/看板 │ 扫全局·俯瞰
│ 每页一行type/status/source/项目/source_link │
├─────────────────────────────────────────────────────┤
│ 第1层 云文档(读) 项目概览、决策来龙去脉 │ 读故事·了解
│ projects/*/_index + 关键 decision → 飞书文档 │
└─────────────────────────────────────────────────────┘
↑ 三层都从同一 Git 仓库单向投影,飞书只读
```
**为什么分三层**:同事需求本就分三种——「了解一个项目」读云文档、「俯瞰筛选全局」用多维表格、「要一个具体答案」问机器人。一层满足不了三种姿势。
**更新节奏(反过度工程)**MVP 第 1/2 层**不做实时同步**——录入后手动跑一次 `feishu-render`,或每天定时同步一次即可;第 3 层机器人是实时的(读 Git 真相源本身,不依赖投影)。
---
### 载体 A云文档第 1 层·叙事型呈现)
- 用途:项目概览、决策来龙去脉——适合「读故事」。
- 同步:`projects/<代号>/_index.md` + 关键 decision 页 → 飞书云文档。
- 工具:`tools/feishu-render.py --docs <代号>`,调 lark-doc-manager / `lark-cli docs +create/+update`
- 频率:按需/手动触发MVP 不做实时)。
### 载体 B多维表格 Bitable第 2 层·结构型呈现)
- 用途:全库总览——每条 decision/doc/conversation 一行,带 `type/status/source/created/项目/source_link` 字段,同事可**筛选/分组/看板/仪表盘**。这是「呈现全库」的主力。
- 同步:扫 `projects/**/*.md` frontmatter → 每页一条记录 → 通用 api 写入 Bitable。
- 工具:`tools/feishu-render.py --bitable`,用 `lark-cli api POST /open-apis/bitable/v1/apps/{app}/tables/{table}/records`
- 字段严格对齐 `meta/kb-contract.yaml`type/status enum 一致)。
- 频率:录入后增量 upsert按 source_link 或页路径做唯一键)。
> Wiki 空间**暂不做**(用户判断现阶段不适合)。
---
## 三、对既有架构的影响(需回改的文档)
1. `docs/04-架构.md` L4 呈现层:飞书从「只读呈现」补充为「双向」——采集(群机器人收/kb-link)、查询(群@问答)、呈现(云文档+Bitable)。**写路径仍唯一(Obsidian/Git)**,机器人问答不写库、只读。
2. `docs/04-架构.md` §4 双前端映射表:飞书行的「写」列注明——群机器人问答是**读**操作(调/kb-ask),不构成第二条写路径。
3. `docs/05-路线图.md`:群机器人 + 呈现属 **P2**(原 P2 只有 feishu-pull现扩为完整双向
4. `docs/07-AI-native工作流.md`:新增「循环 E · 飞书问答」——群 @ → /kb-ask → 回复,人机分工同查询循环。
5. 命令面:无新 /kb 命令(机器人内部调 /kb-ask但新增 `tools/kb-bot/``tools/feishu-render.py`
---
## 四、安全与凭据
- lark-cli 凭据由其自身管理(非本仓环境变量),**不入库**。
- 机器人问答**只读知识库**,不执行写/删——继承「AI 永不擅自有损操作」铁律。
- 群机器人回答带 source_link同事可核。
- 长连接进程日志不含敏感 token--quiet 抑制 stderr
---
## 五、分步实施顺序(建议)
1. **P2-a 发消息打通**`kb-bot` 先做「只发」——手动触发把一条测试答案发到群,验证 chat-id/markdown 渲染。
2. **P2-b 收消息打通**:起 `event +subscribe`@机器人能在 events/ 看到事件。
3. **P2-c 闭环**handle.py 串起「收→/kb-ask→回复」群里问答跑通。
4. **P2-d 呈现-Bitable**:建一张多维表,把 _example 项目的页同步成记录。
5. **P2-e 呈现-云文档**:把 _example 的 _index 同步成飞书文档。
每步独立可验收,任一步卡住不影响已完成步骤。

75
docs/09-同事使用指南.md Executable file
View File

@@ -0,0 +1,75 @@
# 09 · 同事使用指南
> 给全体同事。**你不用懂 Git、frontmatter、OKF 这些技术词**——它们 AI 全包了。
> 你只需要记住三个动作:**丢、问、看**。
---
## 一句话理解
公司知识库 = 一个「把散落各处的资料,沉淀成能随时问、随时查的公司大脑」的系统。
你平时怎么发飞书,就怎么用它——**成本几乎为零**。
---
## 三个动作
### 1. 丢 —— 把值得留下的资料存进去
| 场景 | 怎么做 |
|---|---|
| 飞书群里的文档/消息 | @机器人 说「把这个存进 X 项目」 |
| 你电脑/NAS 里的文件 | 放进你的 `NAS/你的名字/📥知识库投递/` 目录,或跟 AI 点名「我工作目录那个合同要进库」 |
| 一个网页链接 | 直接把链接丢给 AI |
**AI 会自动**:读一遍 → 提炼要点 → 补全元数据 → 归到对应项目。你只需**丢 + 一句说明 + 对 AI 抽的要点点个头**。
> 💡 **原件不用搬**AI 只把「精华」存进库、留个链接指回原文件。所以库不会变臃肿,你的原文件也原地不动。
### 2. 问 —— 快速找答案
飞书群 **@机器人** 直接问,例如:
- 「客户 A 为什么不用一口价?」
- 「power-bid 项目现在进展到哪了?」
机器人查知识库,**带着出处**回答你。查不到会直说「知识库暂无」,**绝不瞎编**。不放心就点它给的链接核对原文。
### 3. 看 —— 浏览和俯瞰
| 我想… | 打开 |
|---|---|
| 了解某个项目的来龙去脉 | 飞书里该项目的**云文档**(适合读故事) |
| 俯瞰全局、按项目/状态筛选 | 飞书**多维表格**(适合扫全局、过滤) |
| 深度编辑整理 | 用 **Obsidian** 打开仓库改(进阶) |
---
## 只需记住的三条
1. **要沉淀的,主动丢一下**——这是你唯一需要主动做的事。不丢,它不会自己进来。
2. **私人文件不用管**——AI 只处理你点名或投递的,不会翻你的工作目录。
3. **答案都带出处**——能核对、不瞎编、查不到就说没有。
---
## 一个典型的一天
- 🌅 开完晨会,把飞书会议纪要 @机器人「存进 clientA」→ 自动沉淀成决策页
- ☀️ 下午想不起细节,群里 @机器人「上次定的报价方案是啥」→ 秒回带链接
- 🌆 周会前打开多维表格,筛出所有「进行中」的项目 → 进展一览
---
## 常见疑问
**Q我丢错了/丢重复了怎么办?**
AAI 会自动去重(同一份不会进两次);丢错了跟 AI 说一声,删改由 AI 提请、人确认,不会误删。
**Q机器人答得不对怎么办**
A点它给的 `source_link` 看原文。若是知识库里资料本身过时,@机器人 说一声或补一份新的进去。
**Q我要改一条已有的知识**
A知识不在飞书上直接改飞书是「读屏」。用 Obsidian 打开仓库改,或跟 AI 说要改哪条。
**Q涉及我个人的草稿、没定稿的东西**
A放你自己的本地目录别丢进投递区。**定稿了再丢**——进了库就是全员可见的公司知识。

126
docs/10-部署与运维.md Executable file
View File

@@ -0,0 +1,126 @@
# 10 · 部署与运维
> 物理拓扑 + 部署步骤 + 运维手册。回答「仓库在哪、处理逻辑在哪跑、同事怎么接入」。
> 核心:**仓库家 + 常驻处理都在公司 NAS**;交互处理在各人本机。
---
## 一、物理拓扑
```
┌──────────────────────────────────────────────────────────────┐
│ 公司 NAS数据的家 + 常驻处理节点) │
│ ├─ Gitea 仓库 company-kb.git ★唯一真相源★ │
│ ├─ 常驻kb-bot 飞书群机器人长连接subscribe + handle
│ └─ 定时feishu-render 投影(云文档 + 多维表格) │
└──────────────────────────────────────────────────────────────┘
▲ git clone/push (内网/VPN) │ 投影(只读)
│ ▼
┌─────────────┐ ┌──────────────┐
│ 各人本机 │ │ 飞书三层呈现 │
│ 本地 clone │ │ 文档/表格/机器人│
│ CC 交互处理 │ └──────────────┘
│ /kb-ingest │
│ /kb-ask │ ┌────────────────────────────┐
│ /kb-link │◀─────│ NAS 同步盘:各人工作目录↔投递区│
└─────────────┘ │ (采集通道,单向投递,无冲突)│
└────────────────────────────┘
```
**两条 NAS 用途,各司其职,不冲突:**
| NAS 角色 | 机制 | 用途 | 为何不冲突 |
|---|---|---|---|
| **Gitea 仓库** | Git 协作 | 知识库本体(提炼后真相源) | 多人共享Git 管合并/审计/回滚 |
| **同步盘** | Synology Drive / SMB | 采集通道(投递原始物料) | 单向投递,无多人同改 |
---
## 二、处理逻辑落点
| 处理类型 | 跑在哪 | 触发 | 说明 |
|---|---|---|---|
| **交互处理** `/kb-ingest`/`/kb-ask`/`/kb-link` | **各人本机** | 人触发 | CC 读本地 clone天然分布式无需中心 |
| **常驻处理** 飞书群机器人长连接 | **NAS** | 常驻 | 读 Git 真相源实时回答,无需公网 |
| **定时处理** feishu-render 投影 | **NAS** | 定时/手动 | md→飞书云文档+多维表格 |
> 关键:常驻/定时处理需要一台常开机 = NAS。交互处理分散在各人机器NAS 不是单点瓶颈。
---
## 三、部署步骤
### 3.1 接 Gitea remote需内网/VPN
```bash
cd ~/奇域/智能体开发/公司AI
# 在 Gitea Web (192.168.0.101:3002/robert) 建空 repo: company-kb
git remote add origin http://192.168.0.101:3002/robert/company-kb.git
git push -u origin main
```
> 现不在内网时 Gitea 不可达,本地提交先攒着,回公司网络再 push。
### 3.2 同事接入(每人一次)
```bash
git clone http://192.168.0.101:3002/robert/company-kb.git
cd company-kb
# CC: cd 进来即可用 /kb-ask /kb-ingest读项目级 CLAUDE.md 自动感知)
# Obsidian: Open folder as vault 打开本目录(个人主编辑器)
```
日常同步:干活前 `git pull`,产出后 `git add/commit/push`(或让 CC 代跑)。
### 3.3 NAS 常驻机器人Gitea 通后)
NAS 上 clone 一份 + 装 lark-cli + 起长连接:
```bash
# NAS 上Container/SSH
git clone <gitea-url> && cd company-kb
lark-cli auth status # 确认 bot 身份
nohup bash tools/kb-bot/subscribe.sh > /tmp/kb-sub.log 2>&1 &
nohup python3 tools/kb-bot/handle.py --watch > /tmp/kb-handle.log 2>&1 &
```
稳定后包成 NAS 的开机自启Synology「任务计划」或 systemd。NAS 侧仓库定时 `git pull` 保证机器人读到最新。
### 3.4 定时投影(可选)
NAS 上 crontab 每日同步飞书呈现:
```cron
# 每天 08:07 投影到飞书(错开整点)
7 8 * * * cd /path/company-kb && git pull -q && python3 tools/feishu-render.py --bitable
```
---
## 四、运维手册
### 备份
- Gitea 仓库随 NAS 备份策略走NAS 快照/RAID
- Git 本身即分布式备份:每个同事的本地 clone 都是全量副本。
### 冲突处理
- 多人改同一页 → Git 合并;`_index.md` 等导航文件已设计为低冲突。
- 编译产物hot/index/wiki已 gitignore不进 Git无合并冲突见 docs/04 §3
### 权限MVP → 演进)
- MVP5 人皆可直接 push main靠 Gitea Web review + `/kb-check` 把关。
- 演进Gitea 上给 main 加保护分支 + PR review团队变大或误操作增多时
### 健康检查
- `python3 tools/kb-lint-fm.py` — frontmatter 合规exit 0 为健康)。
- `/kb-status` — 源计数 / type 分布 / 红线观察(见 docs/05 P5
- 机器人存活:看 `/tmp/kb-sub.log``/tmp/kb-handle.log`
### 凭据安全
- lark-cli 凭据由其自身管理,**不入库**.gitignore 已挡 .env
- 飞书 App Secret 等走环境变量或 lark-cli 配置,绝不提交。
### 故障恢复
- 机器人挂 → NAS 上重跑 3.3 的两条 nohup。
- 仓库损坏 → 从任一同事本地 clone `git push` 恢复 Gitea。
- Gitea 不可用 → 交互处理(各人本机 CC不受影响仅机器人/投影暂停。

View File

@@ -0,0 +1,80 @@
# 11 · 信息全景与非项目结构
> 知识库不只是「项目」。本文定义公司信息全景、非项目信息的落地结构,以及**以后加新类别的登记流程**(可生长性核心)。
---
## 一、公司信息全景
按稳定度/归属分5 人公司的信息大致这几类。**契约已预留对应 type**,按痛感分批激活,不一次全上(反过度工程)。
| 类别 | 内容 | type | 状态 |
|---|---|---|---|
| 项目 | 客户/内部项目的文档·决策·聊天 | project/decision/conversation/doc | ✅ 已用 |
| **公司基本信息** | 公司档案·团队·组织·对外介绍 | doc | ✅ 本次纳入 |
| **共享学习知识库** | 外部知识·学习资料·读书笔记 | doc | ✅ 本次纳入 |
| 公司制度/SOP | 报销·请假·发布·入职流程 | runbook | 预留 |
| 实体档案 | 客户/供应商/竞品(跨项目枢纽) | entity | 预留 |
| 术语/黑话 | 缩写·产品名·行业名词 | concept | 预留 |
| 指标口径 | KPI·财务·业务数据定义 | metric | 预留 |
| 问答沉淀 | 高频问题的权威答案 | question | 预留 |
---
## 二、目录结构扩展
非项目信息**提到顶层平级**(跨项目、不随项目结束消失),不塞进 projects/
```
公司AI/
├── projects/ # 项目(有生命周期) ✅
├── company/ # 公司基本信息(本次新增)
│ └── _index.md # 公司档案入口type: doc
├── learning/ # 共享学习知识库(本次新增,按领域分)
│ ├── _index.md # 学习库总览
│ ├── ai/ # 领域子目录AI
│ ├── management/ # 领域:管理
│ └── industry/ # 领域:行业
├── entities/ concepts/ questions/ # 预留 type按需激活
└── metrics/ company-sop 等 # 未来登记后新增
```
- **company/** 与 **learning/** 本次都用现成 `doc` type不急着激活新 type靠**目录 + tags** 区分,够用。
- learning 按**领域分子目录**(用户定),每篇学习笔记归入对应领域;跨领域用 tags 补充。
- 量大到需要专属字段时(如学习资料要「掌握程度」),再升级成独立 type走第四节流程
---
## 三、飞书呈现映射
沿用「多维表格=全库地图 + 云文档=详情」,非项目信息只是**多加视图**
| 信息类别 | 多维表格视图 | 云文档 |
|---|---|---|
| 公司基本信息 | 「公司信息」视图(筛 company/ 路径) | 公司介绍/团队文档 |
| 共享学习库 | 「学习库」视图(按领域 tags 分组) | 重点学习主题长文 |
| 项目 | 按项目分组视图(已有) | 各项目概览 |
群机器人自动全覆盖——问「公司是做什么的」「XX 领域有什么学习资料」都能答(检索整库,不分项目与否)。
---
## 四、以后加新类别的登记流程 ★可生长性核心★
加新一类信息**不用改代码**——`kb-contract.yaml` 是唯一契约,改它即可:
```
想加一类(例:以后要"财务指标库"
① 在 meta/kb-contract.yaml 的 types 加一条:
- name: metric
active: true
dir: metrics/
desc: 一个指标及其口径、来源
(若需专属字段/派生必填,一并在 fields/derived_rules 登记)
② 建对应顶层目录mkdir metrics/
③ 飞书多维表格加一个筛该 type 的视图
④ 一次 PR 合入
→ 完成lint 自动认它、机器人自动能查,无需改任何脚本
```
**原则**:核心 type 集封闭稳定,扩展走这条轻量登记。**结构稳定但能生长**——不因加类别而重构。禁止在脚本里硬编码 type一律读契约。这也是「先少后多」策略的落地先激活真痛的其余登记在册、按需激活。

View File

@@ -0,0 +1,82 @@
# 12 · 治理与规则维护
> 回答「中间处理规则放哪、谁维护、怎么改」。
> 核心区分:**物料录入=全员参与;规则修改=全员可提 PR合入需至少一人 review。**
---
## 一、两类"规则",分开放
「中间处理规则」其实是两种,位置与维护完全不同,混谈会乱:
| | ①数据契约规则 | ②处理程序规则 |
|---|---|---|
| 管什么 | 「长什么样才合格」 | 「怎么把原料变成知识」 |
| 内容 | type 集 / 字段必填 / enum / 派生规则 | 抽要点·去重·判类型·挂链·矛盾处理·检索·lint |
| **真相源** | `meta/kb-contract.yaml`(机器可读,唯一) | `.claude/commands/kb-*.md`(可执行 AI 指令,就是逻辑本身) |
| 人读镜像 | `CONVENTIONS.md`(宪法,只镜像不复述) | `docs/07-AI-native工作流.md`(四循环描述) |
| 谁执行 | `tools/kb-lint-fm.py` 运行时读 | CC 跑 `/kb-ingest`·`/kb-link`·`/kb-ask` 时执行 |
**结论**:中间处理规则**已经有家**,不是没处放——数据契约在 `kb-contract.yaml`,处理逻辑在 `.claude/commands/`,说明在 CONVENTIONS + docs/07。
> 前提处理层L3目前**极薄**(只有 MVP 必需的去重/抽要点/补 frontmatter/lint/检索)。更深的处理(多级摘要、自动分类、喂下游智能体)属「第二件事」附近,**刻意延后不锁死**。
---
## 二、谁维护——归属矩阵
**分两条线,不要混**
| 事项 | 谁做 | 门槛 |
|---|---|---|
| **物料录入**(丢资料/答问/点头) | **全员** | 无门槛,鼓励人人参与 |
| **规则修改**(改契约/命令/lint | **全员可提,合入需 review** | 至少一人 review 后合并 |
- **物料谁都能丢,规则改动要过审**——因为改一条字段必填,全库 lint 行为就变,影响面大。
- 无固定"负责人",但任何规则 PR **不可自审自合**,需另一人 review。
- 前期实际由技术主导 review随团队成长review 者可扩展。
---
## 三、规则怎么改PR 流程)
任何人想改规则,走统一轻量流程:
```
① 起分支改动(改 kb-contract.yaml / .claude/commands/ / CONVENTIONS / docs/07
② 本地跑 python3 tools/kb-lint-fm.py 确认全库仍 exit 0
(改了契约必须验证:既有页不被新规则误伤)
③ 提 PR说明改什么、为什么、影响面
④ 至少一人 review → 合入 main
```
**改契约的额外铁律**
- 改字段必填/enum → 先跑 lint 扫全库,确认存量页不被打成 error若会先补齐存量再改规则
- 加新 type → 走 `docs/11 §4` 登记流程(契约加条 + 建目录 + 飞书加视图)。
- 禁止在脚本里硬编码 type/字段——一律读契约。
**改命令/处理逻辑**`.claude/commands/`
- 命令是 AI 的可执行指令,改它即改处理行为。
- review 关注:是否违背三条铁律(只读不擅改、不编造、有损操作挂起交人)。
---
## 四、处理层L3演进治理
处理层刻意留白,未来按信号生长,每次生长都是一次规则扩展,同样走 PR + review
| 演进 | 触发信号 | 规则变化 | 阶段 |
|---|---|---|---|
| llm-wiki 全量编译 | `_index` 首读定位失效 | 加编译规则 + 编译产物归属(单一执行者) | P4 |
| 向量库导航层 | ~100 源/几百页 + index 失效 | 加检索规则,契约/编译层不变 | P5 |
| 喂下游智能体 | 第二件事成型 | 加 frontmatter 过滤/消费接口规则 | 第二件事 |
> 原则:**处理层怎么长契约type/frontmatter尽量不动**——上层生长、地基稳定。任何 L3 规则新增都登记在册、可 review、可回滚。
---
## 五、一句话总结
- **中间处理规则不是没家**:数据契约→`kb-contract.yaml`,处理逻辑→`.claude/commands/`说明→CONVENTIONS + docs/07。
- **物料全员录入规则全员可提、review 合入**(不可自审自合)。
- **改规则先过 lint改契约先扫全库**;处理层按信号生长,地基(契约)尽量不动。

47
docs/obsidian-setup.md Executable file
View File

@@ -0,0 +1,47 @@
---
type: doc
title: "Obsidian 前端配置(个人主编辑器)"
status: mature
created: 2026-07-06
ingested: 2026-07-06
source: 人工沉淀
---
# Obsidian 配置 · 个人主编辑器
> 把 `公司AI/` 目录直接作为 Obsidian vault 打开即可。Obsidian 是**个人主编辑器 + 唯一写路径**飞书只读呈现、CC 只读问答。
> `.obsidian/` 已 gitignore——每人工作区配置本地私有不互相污染只有下面这份**推荐插件清单**共享。
## 打开方式
1. Obsidian → Open folder as vault → 选 `~/奇域/智能体开发/公司AI`
2. 首次会提示信任作者,选信任(本仓库脚本仅本地运行)。
## 推荐插件(全员统一)
| 插件 | 用途 | 必装 |
|---|---|---|
| **Dataview** | 按 frontmatter 查询/看板(如"列出所有 status: seed 的 decision" | ✅ |
| Templater | 新建页时自动套 OKF frontmatter 模板 | 建议 |
| Git | 图形化提交(可选,命令行/CC 提交也行) | 可选 |
> 不要装 "Obsidian Git 自动提交"——Git 同步统一走命令行或 CC避免每人自动 push 制造冲突。
## Dataview 速用示例
在任意页面写代码块:
```dataview
TABLE status, created, source
FROM "projects"
WHERE type = "decision"
SORT created DESC
```
即可实时列出所有决策页。字段名严格对齐 `meta/kb-contract.yaml`。
## 写路径铁律
- 编辑 markdown → `git add/commit/push`(或让 CC 代跑)。
- 新建页面照抄 `projects/_example/` 的 frontmatter或用 `/kb-new` / `/kb-ingest`。
- 个人未定稿草稿**不要**放进本仓库——走各自本地私有 vault晋升定稿后才进公司 Git见 `docs/04-架构.md` 双前端映射)。

0
entities/.gitkeep Executable file
View File

36
learning/_index.md Executable file
View File

@@ -0,0 +1,36 @@
---
type: doc
title: "共享学习知识库"
description: 团队共同学习的外部知识沉淀,按领域组织,不属于任何单一项目。
status: seed
created: 2026-07-06
ingested: 2026-07-06
source: 人工沉淀
tags: [学习库, 知识沉淀]
related: []
---
# 共享学习知识库
> 团队的「集体大脑」——把大家学到的外部知识、读书笔记、课程要点沉淀下来,人人可查、可复用。
> 按**领域**分子目录;跨领域的用 `tags` 补充。
## 领域
| 领域 | 目录 | 内容 |
|---|---|---|
| AI | `learning/ai/` | 大模型/Agent/提示工程/工具 |
| 管理 | `learning/management/` | 团队/流程/方法论 |
| 行业 | `learning/industry/` | 所处行业的知识/趋势 |
> 需要新领域?直接建 `learning/<领域>/` 目录即可learning 内部分领域是轻量的,无需改契约)。
## 怎么往里加
- 一篇文章/一本书/一个课程要点 → `/kb-link <链接或文件>`,说明「放进 learning 的 X 领域」。
- AI 自动:读原文 → 提炼要点 → 写成 `learning/<领域>/<slug>.md``type: doc` + `source_link` 指回原文)。
- 原文不搬进库(编译链接模式),库保持精简。
## 与项目的区别
项目知识随项目结束沉淀归档;**学习库是长期资产**,跨项目复用。项目里用到某学习成果时,用 `[[wikilink]]` 链过来。

0
learning/ai/.gitkeep Executable file
View File

View File

@@ -0,0 +1,31 @@
---
type: doc
title: "卡帕西 LLM Wiki 方法论(编译>检索)"
description: LLM 当编译器把原始资料编译成互链 wiki查询时对已编译知识推理而非每次重新检索。
status: developing
created: 2026-04-01
ingested: 2026-07-06
source: AI编译
source_link: docs/03-知识库框架选型调研.md
tags: [ai, 知识库, karpathy, 方法论]
related:
- "[[知识库框架选型调研]]"
---
# 卡帕西 LLM Wiki 方法论
> 学习库示例页:展示非项目信息如何用「编译链接」模式沉淀。精华进库、指针回原文。
## 核心要点
- **编译 > 检索**LLM 读完所有源 → 抽概念、压缩、建交叉引用 → 产出结构化互链 wiki。查询时对**已编译知识**推理。
- 对比传统 RAGRAG 每次查询从零检索、不沉淀「健忘症」LLM Wiki 知识累积复利。
- 规模红线:纯 wiki 模式在 ~100 源/几百页后失效index 不可导航),届时用向量库做导航层(混合)。
## 我们的应用
本知识库整体就是这套方法论的落地——单一 Git 真相源 + OKF frontmatter + 先不上向量库。详见 [[知识库框架选型调研]]。
## 溯源
完整调研见 `source_link``docs/03`),及其中列出的一手来源。

0
learning/industry/.gitkeep Executable file
View File

0
learning/management/.gitkeep Executable file
View File

4
meta/ingest-manifest.json Executable file
View File

@@ -0,0 +1,4 @@
{
"_comment": "摄入 hash 台账去重用。P2+ 自动化后由单一执行者维护。key=内容 sha256, value=目标页相对路径。",
"entries": {}
}

101
meta/kb-contract.yaml Executable file
View File

@@ -0,0 +1,101 @@
# 公司 AI 知识库 · 唯一机器可读契约 (Single Source of Truth)
# ─────────────────────────────────────────────────────────────
# 这是 type 集 / 字段必填矩阵 / enum 的唯一真相源。
# kb-lint-fm.py 运行时读取本文件判定,绝不硬编码。
# CONVENTIONS.md / CLAUDE.md 只引用本文件,不复述内容。
# 扩展 type = 修改本文件 (active: true) + 一次 PR。
version: "1.0"
# ── Type 体系 ──
# active=true 的 type 进入 lint 白名单(激活即放行)。
# reserved 的 type 首次真实需要时改 active: true 并建目录。
types:
# 核心 4 typeDay-1 激活)
- name: project
active: true
dir: projects/<代号>/_index.md
desc: 一个在跑项目的门户/概览页
- name: decision
active: true
dir: projects/<代号>/decisions/
desc: 一条明确决策及其背景、依据、参与人
- name: conversation
active: true
dir: projects/<代号>/conversations/
desc: 聊天记录的结构化沉淀(摘录+要点)
- name: doc
active: true
dir: projects/<代号>/docs/
desc: 从外部汇入的正式文档(需求/方案/合同)
# 预留 type已登记未激活lint 暂不放行)
- name: entity
active: false
dir: entities/
desc: 一个实体(人/公司/客户/产品/供应商)
- name: concept
active: false
dir: concepts/
desc: 一个概念/术语/内部黑话
- name: question
active: false
dir: questions/
desc: 查询回写的问答对
- name: runbook
active: false
dir: runbooks/
desc: 可重复执行的操作步骤/流程
- name: metric
active: false
dir: metrics/
desc: 一个指标及其口径、来源
# ── 字段定义 ──
# required: always硬必填| conditional派生规则见 derived_rules| optional
# auto: AI 可自动补齐的字段(人无需手写)
fields:
type: { required: always, type: string, note: "唯一硬必填,缺失只报不猜" }
status: { required: always, type: enum, auto: true, default: seed }
created: { required: always, type: date, auto: true, note: "内容产生时间AI 从源推导或填今天" }
ingested: { required: always, type: date, auto: true, note: "汇入时间AI 自动=今天" }
source: { required: conditional, type: enum, auto: true }
source_link: { required: conditional, type: string, auto: true }
title: { required: optional, type: string, auto: true, note: "AI 自动=H1 或文件名回填" }
description: { required: optional, type: string }
tags: { required: optional, type: list, note: "kebab-case" }
timestamp: { required: optional, type: date, note: "信息最后有效期" }
author: { required: optional, type: string }
related: { required: optional, type: list, note: "[[slug]] wikilink 强边" }
resource: { required: conditional, type: string, note: "metric 型必填:底层资源稳定链接" }
# ── enum 取值 ──
enums:
status: [seed, developing, mature]
source: [飞书文档, 飞书群, 微信群, 本地, NAS, 会议, 人工沉淀, AI编译]
# ── 派生必填规则lint 据此判定,各处不再复述)──
derived_rules:
# 外部渠道摄入的 doc/decision/conversation 页,溯源字段升为必填
- when:
type_in: [doc, decision, conversation]
source_in: [飞书文档, 飞书群, 微信群, NAS, 会议]
require: [source, source_link]
# metric 型必须带底层资源链接
- when:
type_in: [metric]
require: [resource]
# ── 豁免(免 frontmatter仓库脚手架而非知识页──
# lint 跳过这些路径
frontmatter_exempt:
- README.md
- CONVENTIONS.md
- CLAUDE.md
- AI-CONTEXT.md # 全架构蒸馏入口:顶层脚手架,非 OKF 知识页
- docs/**
- meta/**
- tools/** # 脚本与其 README工具脚手架非 OKF 知识页
- .claude/** # CC 命令/配置:工具脚手架,非 OKF 知识页
- "**/assets/**"
- "**/.gitkeep"
# type: project 的 _index.md 豁免溯源字段(非外部摄入)
source_exempt_types: [project]

14
meta/stats.md Executable file
View File

@@ -0,0 +1,14 @@
# 源计数 / 页数 —— 红线观察点(手动或简单脚本更新)
#
# P5 向量库红线:~100 源 / 几百页 + index 首读定位失效 + 摄入悖论。
# 到线前「要不要上 RAG」默认答案=「不」。
#
# 用 `bash tools/kb-init.sh --stats` 或手动更新下表。
| 指标 | 当前值 | 红线 | 说明 |
|------|-------|------|------|
| 项目数 | 0 | — | projects/ 下非 _example 目录数 |
| 知识页数 | 0 | ~几百页 | 全库带 frontmatter 的 .md 数 |
| 外部源数 | 0 | ~100 | 去重后的 source_link 数 |
> 最后更新:手动维护,见 git log。

36
projects/_example/_index.md Executable file
View File

@@ -0,0 +1,36 @@
---
type: project
title: "示例项目Example"
description: 一个照抄即合规的 OKF 对齐示例项目,给同事看 frontmatter 与目录结构长什么样。
status: developing
created: 2026-06-18
ingested: 2026-06-24
tags: [示例, onboarding]
related:
- "[[decision-clientA-tiered-pricing]]"
- "[[conversation-kickoff-20260618]]"
---
# 示例项目Example
> 这是给同事的活样板。新建真实项目请用 `bash tools/kb-init.sh <代号>`,不要复制本目录名 `_example`。
## 概览
虚构的「客户 A 定制化交付」项目,用来演示如何把项目资料结构化沉淀进知识库。
## 进展
- 2026-06-18 项目启动会,确定范围与需求方(见 conversations/
- 2026-06-20 定价策略拍板为阶梯定价(见 decisions/
## 关键决策
- [[decision-clientA-tiered-pricing]] —— 采用阶梯定价而非一口价
## 需求方 / 参与人
- 需求方:客户 A 采购部
- 内部:张三(商务)、李四(交付)
## 资料索引
- `docs/` :外部汇入的正式文档(需求书/合同/方案)
- `conversations/` :聊天记录结构化沉淀
- `decisions/` :决策记录,每条挂溯源
- `assets/` :原始附件(唯一免 frontmatter 目录)

View File

View File

@@ -0,0 +1,31 @@
---
type: conversation
title: "客户 A 项目启动会要点"
description: 启动会摘录:确认交付范围、需求方对接人、首个里程碑时间。
tags: [启动会, 客户-a, 需求]
timestamp: 2026-06-18
source: 会议
source_link: https://company.feishu.cn/minutes/xxxx
author: 李四
created: 2026-06-18
ingested: 2026-06-24
status: developing
related:
- "[[示例项目Example]]"
- "[[decision-clientA-tiered-pricing]]"
---
# 客户 A 项目启动会要点
## 摘录
> 客户 A本年度预算分季度到位希望先小范围试点再扩量。
> 我方李四:可先交付核心模块,二期再接扩展需求。
## 要点
- 交付范围:一期只做核心模块,扩展需求进二期
- 需求方对接人:客户 A 采购部王经理
- 首个里程碑2026-07-15 完成一期验收
- 遗留问题:定价方式待商务确认 → 后续在 [[decision-clientA-tiered-pricing]] 拍板
## 溯源
完整会议纪要见 `source_link`

View File

@@ -0,0 +1,33 @@
---
type: decision
title: "客户 A 报价采用阶梯定价而非一口价"
description: 因客户预算分期到位,改用阶梯定价锁定长期合作,牺牲首单毛利。
tags: [报价, 客户-a, 定价策略]
timestamp: 2026-06-20
source: 飞书群
source_link: https://company.feishu.cn/im/xxxx?msg=99231
author: 张三
created: 2026-06-20
ingested: 2026-06-24
status: mature
related:
- "[[示例项目Example]]"
---
# 客户 A 报价采用阶梯定价而非一口价
## 背景
客户 A 采购部反馈本年度预算分季度到位,一口价方案会卡在首季审批。
## 决策
改用阶梯定价:首单让出约 8 个点毛利换取签下全年框架,后续订单按量回升。
## 依据
- 客户明确表达长期合作意向LTV 显著高于单单毛利
- 竞品报的是一口价,阶梯定价形成差异化
## 参与人 · 时间
- 张三商务、李四交付2026-06-20 飞书群讨论
## 溯源
原始讨论见 `source_link`(飞书群消息锚点)。

View File

View File

@@ -0,0 +1,29 @@
---
type: doc
title: "客户 A 一期需求说明书(汇入)"
description: 从飞书文档汇入的一期需求正式说明,交付范围的权威依据。
tags: [需求, 客户-a, 合同附件]
timestamp: 2026-06-19
source: 飞书文档
source_link: https://company.feishu.cn/docx/xxxxdocxtoken
author: 客户A采购部
created: 2026-06-19
ingested: 2026-06-24
status: mature
related:
- "[[示例项目Example]]"
---
# 客户 A 一期需求说明书(汇入)
> 本页由 `feishu-pull.py` 半自动导出,原文见 `source_link`。外部摄入页 `source`/`source_link` 为必填。
## 一期范围
- 核心模块 A、B
- 不含扩展模块 C进二期
## 验收标准
- 2026-07-15 前完成一期功能验收,缺陷率 < 2%
## 溯源
以飞书原始文档为准,本页为结构化镜像。

View File

@@ -0,0 +1,43 @@
---
type: project
title: "万牛会L1培训"
description: AI数字员工应用实战培训项目8月8-9日线上实训营
status: developing
created: 2026-07-08
ingested: 2026-07-16
tags: [培训, 万牛会, L1, AI]
related:
- "[[课程大纲v1]]"
- "[[小班制20人]]"
---
# 万牛会L1培训
## 概览
**万牛会L1培训**是面向企业员工的 AI 数字员工应用实战培训项目。
- 时间2026年8月8-9日
- 形式:线上实训营
- 目标:帮助学员掌握 Claude Code、Workflow 等工具
## 进展
- ✅ 2026-07-08课程大纲 v1 完成
- ✅ 2026-07-10确定小班制20人/班)
- 🔄 2026-07-16招生筹备中
## 关键决策
- [[小班制20人]] —— 采用小班制20人/班)保证培训质量
## 核心文档
- [[课程大纲v1]] —— 2天课程安排与模块设计
## 参与人
- 项目负责人qianqian
- 讲师团队qianqian主讲+ 技术团队(助教)
- 招生对象:企业员工
## 资料索引
- `docs/` :课程大纲、教学方案
- `decisions/` :培训模式、定价策略等决策
- `conversations/` :团队讨论记录
- `assets/` :课件、录屏等附件

View File

View File

@@ -0,0 +1,45 @@
---
type: decision
title: "万牛会L1培训采用小班制20人/班)"
description: 为保证培训质量和互动效果采用小班制每班不超过20人
tags: [培训, 万牛会, 小班制, L1]
timestamp: 2026-07-10
source: 飞书群
source_link: https://company.feishu.cn/im/chat_placeholder
author: qianqian
status: mature
created: 2026-07-10
ingested: 2026-07-16
related:
- "[[wanniu-l1]]"
---
# 万牛会L1培训采用小班制20人/班)
## 背景
根据之前的培训经验大班制50+人)存在以下问题:
- 学员互动不足
- 讲师无法照顾到每个学员的进度
- 实操环节混乱
## 决策
采用小班制,具体规则:
- 每班不超过20人
- 开多个班次覆盖需求
- 每班配1名讲师 + 1名助教
## 依据
- 参考行业标准AI培训最佳实践建议15-25人/班
- 内部调研:往期学员反馈希望"老师能看到我的操作"
- 成本核算小班制虽然增加人力但完课率提升30%ROI更高
## 参与人
- 决策人qianqian
- 讨论参与:团队全员
- 时间2026-07-10 飞书群讨论
## 影响
- 招生计划从单班50人调整为3班×20人
- 师资需求需额外招2名讲师
- 场地需求需3个独立教室

View File

View File

@@ -0,0 +1,67 @@
---
type: doc
title: "万牛会L1培训 - 课程大纲 v1"
description: AI数字员工应用实战课程大纲包含Claude Code、Workflow编排、实战项目
tags: [课程大纲, 万牛会, L1, AI培训]
timestamp: 2026-07-08
source: 飞书文档
source_link: https://company.feishu.cn/docx/course_outline_placeholder
author: qianqian
status: mature
created: 2026-07-08
ingested: 2026-07-16
related:
- "[[wanniu-l1]]"
- "[[小班制20人]]"
---
# 万牛会L1培训 - 课程大纲 v1
## 课程定位
面向企业员工的 AI 数字员工应用实战培训,帮助学员掌握 Claude Code、Workflow 等工具,构建可复用的自动化技能。
## 课程时长
- 总时长2天8.8-8.9
- 每天9:00-12:00, 14:00-18:00
- 实操占比60%
## 课程模块
### Day 1 上午AI 工具基础
- Claude Code 基本操作1h
- 提示词工程入门1h
- 实战:用 Claude 写第一个脚本1h
### Day 1 下午Workflow 编排
- Workflow 概念与应用场景1h
- 多 Agent 协作模式1.5h
- 实战:构建知识库采集 Workflow1.5h
### Day 2 上午:实战项目
- 项目需求分析0.5h
- 分组实战选择业务场景2h
- 中期答疑0.5h
### Day 2 下午:优化与部署
- Workflow 调优技巧1h
- 部署与监控1h
- 项目展示与点评2h
## 学员要求
- 有基本的文件操作能力
- 会使用命令行更佳(非必须)
- 带笔记本电脑Mac/Windows 均可)
## 师资配置
根据 [[小班制20人]] 决策:
- 每班20人
- 1名讲师 + 1名助教
- 讲师qianqian 主讲
- 助教:技术团队轮值
## 交付物
- 学员项目代码Git 仓库)
- 课程录屏
- 课件 PPT
- 学员反馈问卷

0
questions/.gitkeep Executable file
View File

131
tools/feishu-pull.py Executable file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""feishu-pull.py —— 飞书半自动单向导出(飞书 → docs/
MVP 形态:半自动单向。飞书只作「文档来源」,不直接写 Git。
凭据走环境变量(绝不入库):
FEISHU_APP_ID, FEISHU_APP_SECRET
用法:
# 干跑(默认):只打印将要导出什么,不落盘
python3 tools/feishu-pull.py <代号> --doc <飞书文档token或url>
# 实际写入 projects/<代号>/docs/
python3 tools/feishu-pull.py <代号> --doc <token> --write
AI 自动补齐 frontmattertitle(H1回填) / ingested(今天) / source(飞书文档) / source_link。
去重:写前查 meta/ingest-manifest.json 的内容 hash。
注意:本文件是接口骨架。飞书 SDK 调用留 TODO接入时填 _fetch_doc()。
凭据缺失时优雅报错,不泄露。
"""
import sys
import os
import json
import hashlib
import argparse
import datetime
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
MANIFEST = os.path.join(ROOT, "meta", "ingest-manifest.json")
def load_manifest():
if os.path.isfile(MANIFEST):
with open(MANIFEST, encoding="utf-8") as f:
return json.load(f)
return {"_comment": "", "entries": {}}
def save_manifest(m):
with open(MANIFEST, "w", encoding="utf-8") as f:
json.dump(m, f, ensure_ascii=False, indent=2)
def content_hash(text):
return hashlib.sha256(text.encode("utf-8")).hexdigest()
def _fetch_doc(token):
"""接入飞书时实现:用 tenant_access_token 拉文档,返回 (标题, markdown 正文, 原始链接)。
现为占位,避免误导:明确报未接入。"""
app_id = os.environ.get("FEISHU_APP_ID")
app_secret = os.environ.get("FEISHU_APP_SECRET")
if not app_id or not app_secret:
raise RuntimeError(
"缺凭据:请设置环境变量 FEISHU_APP_ID / FEISHU_APP_SECRET不要写进代码或 .env 入库)"
)
# TODO: 接入 lark-oapi SDK
# 1) POST /open-apis/auth/v3/tenant_access_token/internal
# 2) GET /open-apis/docx/v1/documents/{token}/raw_content
raise NotImplementedError(
"飞书 SDK 调用尚未接入P2 阶段实现)。当前仅提供 frontmatter 自动补齐与去重骨架。"
)
def build_page(title, body, source_link):
today = datetime.date.today().isoformat()
fm = (
"---\n"
"type: doc\n"
f'title: "{title}"\n'
"status: seed\n"
f"created: {today}\n"
f"ingested: {today}\n"
"source: 飞书文档\n"
f"source_link: {source_link}\n"
"tags: []\n"
"related: []\n"
"---\n\n"
f"# {title}\n\n"
f"{body}\n"
)
return fm
def slugify(title):
keep = "".join(c if c.isalnum() or c in " -_" else "" for c in title)
return "-".join(keep.split())[:60] or "untitled"
def main():
ap = argparse.ArgumentParser(description="飞书半自动单向导出")
ap.add_argument("code", help="项目代号(英文/拼音)")
ap.add_argument("--doc", required=True, help="飞书文档 token 或 URL")
ap.add_argument("--write", action="store_true", help="实际写盘(默认干跑)")
args = ap.parse_args()
outdir = os.path.join(ROOT, "projects", args.code, "docs")
try:
title, body, link = _fetch_doc(args.doc)
except (RuntimeError, NotImplementedError) as e:
print(f"[飞书导出未就绪] {e}", file=sys.stderr)
sys.exit(2)
page = build_page(title, body, link)
h = content_hash(body)
manifest = load_manifest()
if h in manifest.get("entries", {}):
print(f"[去重] 内容已存在于 {manifest['entries'][h]},跳过。")
return
fname = slugify(title) + ".md"
dest = os.path.join(outdir, fname)
rel = os.path.relpath(dest, ROOT)
if not args.write:
print(f"[干跑] 将写入: {rel}\n--- frontmatter 预览 ---\n{page[:400]}...")
print("加 --write 实际落盘。")
return
os.makedirs(outdir, exist_ok=True)
with open(dest, "w", encoding="utf-8") as f:
f.write(page)
manifest.setdefault("entries", {})[h] = rel
save_manifest(manifest)
print(f"[已导出] {rel}")
if __name__ == "__main__":
main()

114
tools/feishu-render.py Executable file
View File

@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""feishu-render.py —— 知识库 → 飞书呈现(单向 md→飞书不回写
两种载体:
--bitable 扫 projects 下页的 frontmatter每页一条记录 upsert 到多维表格(结构型呈现,可筛选/看板)
--docs 代号 把某项目的 _index + 决策同步为飞书云文档(叙事型呈现)
飞书只读呈现,写路径仍是 Obsidian/Git防双写冲突。
凭据由 lark-cli 自管,不入库。字段严格对齐 meta/kb-contract.yaml。
用法:
python3 tools/feishu-render.py --bitable [--dry-run]
python3 tools/feishu-render.py --docs <代号> [--dry-run]
注意本文件是接口骨架。bitable app/table 的创建与 record 写入留 TODO
接入时填 _bitable_upsert()docs 同步复用 lark-doc-manager。
"""
import sys
import os
import re
import glob
import argparse
try:
import yaml
except ImportError:
print("ERROR: 需要 PyYAML。请运行: pip install pyyaml", file=sys.stderr)
sys.exit(2)
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
FM_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
# 呈现给飞书多维表格的字段(对齐 kb-contract.yaml
BITABLE_FIELDS = ["type", "title", "status", "source", "created", "source_link"]
def parse_fm(fp):
text = open(fp, encoding="utf-8").read()
m = FM_RE.match(text)
if not m:
return None
try:
data = yaml.safe_load(m.group(1))
return data if isinstance(data, dict) else None
except yaml.YAMLError:
return None
def collect_pages():
"""扫 projects 下所有知识页,返回 (相对路径, frontmatter) 列表。"""
rows = []
for fp in glob.glob(os.path.join(ROOT, "projects", "**", "*.md"), recursive=True):
fm = parse_fm(fp)
if fm and fm.get("type"):
rel = os.path.relpath(fp, ROOT)
project = rel.split(os.sep)[1] if os.sep in rel else ""
rows.append((rel, project, fm))
return rows
def _bitable_upsert(rows, dry_run):
"""接入时实现:通用 api 写多维表格记录。
唯一键 = 页相对路径(防重复插入,做 upsert
命令参考:
lark-cli api POST /open-apis/bitable/v1/apps/{app}/tables/{table}/records \
--data '{"fields": {...}}'
"""
print(f"[bitable] 采集到 {len(rows)} 页知识:")
for rel, project, fm in rows:
fields = {k: fm.get(k, "") for k in BITABLE_FIELDS}
fields["项目"] = project
fields["页路径"] = rel
if dry_run:
print(f" - {project}/{fm.get('type')}: {fm.get('title', rel)}")
else:
# TODO: lark-cli api POST .../records含 app_token/table_id 配置)
pass
if not dry_run:
raise NotImplementedError(
"Bitable 写入尚未接入P2-d。需先创建多维表并配置 app_token/table_id。"
"当前 --dry-run 可预览将同步的记录。"
)
def _docs_sync(code, dry_run):
idx = os.path.join(ROOT, "projects", code, "_index.md")
if not os.path.isfile(idx):
print(f"ERROR: 找不到 projects/{code}/_index.md", file=sys.stderr)
sys.exit(1)
print(f"[docs] 将同步 projects/{code}/ 的 _index + 决策为飞书云文档")
if dry_run:
print(" dry-run实际同步走 lark-doc-manager / lark-cli docs +create/+update")
return
raise NotImplementedError(
"云文档同步走 lark-doc-manager skill已激活。接入时在此调用其创建/更新流程。"
)
def main():
ap = argparse.ArgumentParser(description="知识库 → 飞书呈现")
g = ap.add_mutually_exclusive_group(required=True)
g.add_argument("--bitable", action="store_true", help="同步全库到多维表格")
g.add_argument("--docs", metavar="代号", help="同步某项目为云文档")
ap.add_argument("--dry-run", action="store_true", help="只预览不写飞书")
args = ap.parse_args()
if args.bitable:
_bitable_upsert(collect_pages(), args.dry_run)
else:
_docs_sync(args.docs, args.dry_run)
if __name__ == "__main__":
main()

576
tools/feishu-sync.py Executable file
View File

@@ -0,0 +1,576 @@
#!/usr/bin/env python3
"""feishu-sync.py —— 飞书文件夹递归镜像到本地(飞书 → 本地目录,单向)
把飞书云盘里一个文件夹(含所有子文件夹)完整拉到本地:
- docx 云文档 → markdown 正文 + 图片下载到本地 assets/ + 图链接改本地路径 + 补 frontmatter
- sheet/bitable → 导出 xlsx在线表格
- file 上传件 → 原样下载pdf/xmind/zip/视频…)
- mindnote 在线思维导图 → 跳过(拉它旁边导出的 .pdf/.xmind 即可)
- shortcut 快捷方式 → 跳过(只是指向别处的链接,无实际内容)
凭据由 lark-cli 自管lark-cli auth login本脚本不碰任何密钥。
用法:
# 干跑(默认):只列出将要拉什么,不落盘
python3 tools/feishu-sync.py --folder <飞书文件夹token> --out <本地目录>
# 实际写盘
python3 tools/feishu-sync.py --folder <token> --out <本地目录> --write
去重:每篇内容算 sha256 记进 <out>/.feishu-sync-manifest.json没变过下次跳过。
"""
import argparse
import datetime
import hashlib
import json
import os
import re
import shutil
import subprocess
import sys
# 飞书导出 markdown 里图片用的临时下载域名(会过期,需替换成本地路径)
STREAM_HOST = "internal-api-drive-stream.feishu.cn"
# 垃圾文件名:飞书/Mac 同步产生,不拉
JUNK_NAMES = {".DS_Store", "DS_Store", ".ds_store", "Thumbs.db",
"desktop.ini", ".localized"}
# 大文件扩展名(视频/音频/压缩包/大 PPT--skip-big 时跳过。
# 这类无法转 markdown、体积大飞书列表不返回体积按扩展名判断最稳
# 避免每个文件多发一次探测请求。
BIG_EXTS = {".mp4", ".mov", ".avi", ".mkv", ".wmv", ".flv", ".m4v",
".mp3", ".wav", ".zip", ".rar", ".7z", ".iso",
".pptx", ".ppt"}
# 每篇文档 URL 前缀按类型拼
DOC_URL = {
"docx": "/docx/",
"doc": "/doc/",
"sheet": "/sheets/",
"bitable": "/base/",
"mindnote": "/mindnotes/",
"slides": "/slides/",
}
def find_lark_cli():
"""定位 lark-cliPATH 找不到就试常见位置。"""
p = shutil.which("lark-cli")
if p:
return p
for cand in [
os.path.expanduser("~/.npm-global/bin/lark-cli"),
os.path.expanduser("~/.nvm/versions/node/v22.17.1/bin/lark-cli"),
]:
if os.path.isfile(cand):
return cand
# 最后从 nvm 目录里扫一个
nvm = os.path.expanduser("~/.nvm/versions/node")
if os.path.isdir(nvm):
for v in sorted(os.listdir(nvm), reverse=True):
cand = os.path.join(nvm, v, "bin", "lark-cli")
if os.path.isfile(cand):
return cand
sys.exit("找不到 lark-cli请先安装并 `lark-cli auth login`。")
LARK = find_lark_cli()
def lark(args, binary_out=None, timeout=60, cwd=None):
"""跑 lark-cli返回解析后的 JSONbinary_out 模式返回 CompletedProcess
cwd下载/导出类命令要求 --output 是「相对当前目录」的路径,故传 cwd=目标目录,
配合 --output ./文件名 使用lark-cli 拒绝绝对路径)。
"""
cmd = [LARK] + args
r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, cwd=cwd)
if binary_out is not None:
return r
if not r.stdout.strip():
raise RuntimeError(f"lark-cli 无输出: {' '.join(args)}\n{r.stderr[:300]}")
try:
return json.loads(r.stdout)
except json.JSONDecodeError:
raise RuntimeError(f"lark-cli 输出非 JSON: {' '.join(args)}\n{r.stdout[:300]}")
def list_folder(folder_token):
"""列一个文件夹里的直接子项(自动翻页)。返回 [{name,type,token,created_time,url}...]
folder_token 为空或 "ROOT" 时列云盘根目录(不传 --folder-token飞书返回全部根目录项
"""
is_root = not folder_token or folder_token == "ROOT"
items = []
page_token = None
while True:
args = ["drive", "files", "list", "--page-size", "200", "--as", "user", "--json"]
if not is_root:
args += ["--folder-token", folder_token]
if page_token:
args += ["--page-token", page_token]
res = lark(args)
data = res.get("data", {})
items.extend(data.get("files", []))
if data.get("has_more") and data.get("next_page_token"):
page_token = data["next_page_token"]
else:
break
return items
def list_bitable_tables(app_token):
"""列一个多维表格的所有子表。返回 [{table_id, name}...]。失败返回空列表。"""
tables = []
page_token = None
while True:
params = {"page_size": 100}
if page_token:
params["page_token"] = page_token
try:
res = lark(["api", "GET",
f"/open-apis/bitable/v1/apps/{app_token}/tables",
"--params", json.dumps(params), "--as", "user", "--json"])
except RuntimeError:
return tables
data = res.get("data", {})
for it in data.get("items", []):
tables.append({"table_id": it.get("table_id"), "name": it.get("name", "")})
if data.get("has_more") and data.get("page_token"):
page_token = data["page_token"]
else:
break
return tables
def safe_name(name, token, ext=""):
"""把飞书文档名清成安全文件名;空名用 token。"""
name = (name or "").strip()
if not name:
name = token
name = re.sub(r'[/\\:*?"<>|]', "_", name)
name = name.strip(". ")[:120] or token
if ext and not name.lower().endswith(ext.lower()):
name += ext
return name
def fetch_docx(token):
"""拉一篇 docx返回 (markdown正文, [媒体 dict 按出现顺序])。
媒体 dict: {"token": file_token, "mime": mime}。同时覆盖两种飞书导出形态:
- 图片 → <img src="file_token" mime="image/..">
- 视频/文件嵌入 → <figure><source token="file_token" mime="video/..">
两者在 markdown 里分别渲染成 ![](过期链接) 和 <figure><source href=过期链接>
必须一起处理,否则视频类的过期链接会残留(违背「长久」)。
"""
md_res = lark(["docs", "+fetch", "--doc", token,
"--doc-format", "markdown", "--detail", "simple",
"--as", "user", "--json"], timeout=120)
md = md_res.get("data", {}).get("document", {}).get("content", "")
xml_res = lark(["docs", "+fetch", "--doc", token,
"--doc-format", "xml", "--detail", "full",
"--as", "user", "--json"], timeout=120)
xml = xml_res.get("data", {}).get("document", {}).get("content", "")
# 按出现顺序扫描 <img> 与 <source>figure 内),保持与 md 中链接顺序一致
media = []
for m in re.finditer(r'<(img|source)\b([^>]*)>', xml):
tag, attrs = m.group(1), m.group(2)
# img 的 file_token 在 src=source 的在 token=
if tag == "img":
tm = re.search(r'\bsrc="([^"]+)"', attrs)
else:
tm = re.search(r'\btoken="([^"]+)"', attrs)
if not tm:
continue
mm = re.search(r'\bmime="([^"]+)"', attrs)
mime = mm.group(1) if mm else ("image/png" if tag == "img" else "")
media.append({"token": tm.group(1), "mime": mime})
return md, media
def download_media(file_token, out_dir, basename):
"""下一张文档内图片,返回本地文件名(含扩展名),失败返回 None。
lark-cli 的 --output 只认「相对当前目录」的路径,故 cwd=out_dir + --output ./basename。
"""
os.makedirs(out_dir, exist_ok=True)
r = lark(["docs", "+media-download", "--token", file_token,
"--output", "./" + basename, "--overwrite", "--as", "user", "--json"],
timeout=120, cwd=out_dir)
saved = r.get("data", {}).get("saved_path")
if saved and os.path.isfile(saved):
return os.path.basename(saved)
# 兜底:目录里找 basename.*
for f in os.listdir(out_dir):
if f.startswith(os.path.basename(basename)):
return f
return None
def localize_images(md, media, assets_dir, assets_rel, doc_slug, write):
"""把 md 里会过期的飞书 stream 链接处理掉,保证不残留死链(长久性铁律)。
- 图片 (mime=image/*) → 下载到本地 assets/,链接改本地相对路径
- 视频/其它 (mime=video/* 等) → 不下载,整段换成 `> 📎 [媒体已省略:类型,见原文]`
- 任何形态的过期链接都在最后兜底清除,绝不残留。
media 与 md 中过期链接按出现顺序一一对应fetch_docx 已保证顺序)。
返回 (新md, 下载成功的图片数, 媒体总数)。
"""
img_got = 0
# 处理每个媒体:图片下载、非图片省略。按序消费 media逐个替换 md 中第一个未处理的过期链接。
def consume_next_link(new_text, replacement):
"""把 new_text 中第一个出现的过期 stream 链接(含其外层包装)替换掉。
覆盖飞书导出的三种媒体形态alt 用非贪婪匹配,兼容 alt 里含 ] [ 等字符。
"""
host = re.escape(STREAM_HOST)
pats = [
# 形态1markdown 图片 ![alt](stream_url)alt 非贪婪,兼容 alt 内含 ]
r'!\[.*?\]\(https://' + host + r'/[^)]+\)',
# 形态2<figure><source ... href="stream_url" .../></figure>
r'<figure\b[^>]*>\s*<source\b[^>]*\bhref="https://'
+ host + r'/[^"]+"[^>]*/?>\s*</figure>',
# 形态3HTML <img ... href="stream_url" .../>(表格单元格内常见)
r'<img\b[^>]*\bhref="https://' + host + r'/[^"]+"[^>]*/?>',
]
best = None
for p in pats:
m = re.search(p, new_text)
if m and (best is None or m.start() < best.start()):
best = m
if best is None:
return new_text, False
return new_text[:best.start()] + replacement + new_text[best.end():], True
new_md = md
for i, item in enumerate(media):
mime = (item.get("mime") or "").lower()
if mime.startswith("image/"):
ext = "." + mime.split("/", 1)[1].split("+")[0] if "/" in mime else ".png"
fname = f"{doc_slug}-img{i+1}"
if write:
got = download_media(item["token"], assets_dir, fname)
rep = f"![]({assets_rel}/{got})" if got else None
else:
rep = f"![]({assets_rel}/{fname}{ext})"
if rep:
new_md, ok = consume_next_link(new_md, rep)
if ok and write:
img_got += 1
# 下载失败则不消费,留给兜底清理
else:
kind = mime.split("/", 1)[0] if "/" in mime else "媒体"
note = f"> 📎 [已省略{kind}媒体,原文见 source_link]"
new_md, _ = consume_next_link(new_md, note)
# 兜底:清除任何仍残留的过期 stream 链接(各种包装形态),绝不留死链。
# 顺序从「包装最完整」到「最裸」,逐层收网;最后一条裸 URL catch-all 确保零残留。
NOTE = "> 📎 [已省略媒体,原文见 source_link]"
H = re.escape(STREAM_HOST)
# 形态A<figure><source href="stream"...></figure>
new_md = re.sub(
r'<figure\b[^>]*>\s*<source\b[^>]*\bhref="https://' + H
+ r'/[^"]+"[^>]*/?>\s*</figure>', NOTE, new_md)
# 形态BHTML <img ... src/href="stream" ...>(含表格 <td> 里的)
new_md = re.sub(
r'<img\b[^>]*\b(?:src|href)="https://' + H + r'/[^"]*"[^>]*/?>',
NOTE, new_md)
# 形态Cmarkdown ![alt](stream)。alt 用 [\s\S]*? 非贪婪,允许 alt 内含 ] 等特殊字符
new_md = re.sub(
r'!\[[\s\S]*?\]\(https://' + H + r'/[^)]+\)', NOTE, new_md)
# 形态D兜底 catch-all——任何裸露的 stream URL连同可能的 markdown 链接括号)
new_md = re.sub(r'\(https://' + H + r'/[^)]+\)', "(见原文)", new_md)
new_md = re.sub(r'https://' + H + r'/\S+', "〔媒体链接已省略〕", new_md)
return new_md, img_got, len(media)
def build_frontmatter(title, source_link, created_date):
today = datetime.date.today().isoformat()
title_esc = title.replace('"', "'")
return (
"---\n"
"type: doc\n"
f'title: "{title_esc}"\n'
"source: 飞书文档\n"
f"source_link: {source_link}\n"
f"created: {created_date}\n"
f"ingested: {today}\n"
"status: seed\n"
"tags: []\n"
"related: []\n"
"---\n\n"
)
def ts_to_date(ts):
try:
return datetime.datetime.fromtimestamp(int(ts)).date().isoformat()
except (ValueError, TypeError):
return datetime.date.today().isoformat()
def load_manifest(path):
if os.path.isfile(path):
try:
return json.load(open(path, encoding="utf-8"))
except (json.JSONDecodeError, OSError):
pass
return {"_comment": "feishu-sync 去重台账key=内容sha256, value=本地相对路径", "entries": {}}
def save_manifest(path, m):
json.dump(m, open(path, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
# ── 统计 ──
STATS = {"docx": 0, "file": 0, "export": 0, "csv": 0, "skip": 0, "skipbig": 0, "dedup": 0, "img": 0, "fail": 0}
class ProgressList(list):
"""记录每条处理结果的列表LIVE=True 时 append 即实时打印并 flush
让同事在长时间同步中能看到逐条动态,而不是一片死寂。"""
LIVE = False
def append(self, line):
super().append(line)
if ProgressList.LIVE:
print(line, flush=True)
PLAN = ProgressList() # 干跑记录待办;写盘时实时打印进度
def handle_item(item, rel_path, out_root, host, manifest, write, skip_big=False):
typ = item.get("type")
name = item.get("name", "")
token = item.get("token")
created = ts_to_date(item.get("created_time"))
local_dir = os.path.join(out_root, rel_path) if rel_path else out_root
# 垃圾文件直接跳过(.DS_Store 等)
if name.strip() in JUNK_NAMES:
STATS["skip"] += 1
return
if typ == "shortcut":
STATS["skip"] += 1
PLAN.append(f" ⏭️ 跳过快捷方式: {rel_path}/{name}")
return
if typ == "mindnote":
STATS["skip"] += 1
PLAN.append(f" ⏭️ 跳过在线思维导图(拉旁边导出件): {rel_path}/{name}")
return
if typ == "docx":
slug = safe_name(name, token).rsplit(".", 1)[0]
fname = slug + ".md"
dest = os.path.join(local_dir, fname)
rel_dest = os.path.relpath(dest, out_root)
src_link = f"https://{host}{DOC_URL.get('docx','/docx/')}{token}"
if not write:
PLAN.append(f" 📄 docx → {rel_dest} (+图片到 assets/)")
STATS["docx"] += 1
return
try:
md, media = fetch_docx(token)
h = hashlib.sha256(md.encode("utf-8")).hexdigest()
if h in manifest["entries"]:
STATS["dedup"] += 1
PLAN.append(f" ♻️ 未变跳过: {rel_dest}")
return
assets_dir = os.path.join(local_dir, "assets")
new_md, got, total = localize_images(
md, media, assets_dir, "assets", slug, write=True)
STATS["img"] += got
title = name or slug
# H1 回填 title若正文首行是 # 标题,用它
m = re.match(r"^#\s+(.+)", new_md.strip())
if m:
title = m.group(1).strip()
os.makedirs(local_dir, exist_ok=True)
page = build_frontmatter(title, src_link, created) + new_md.strip() + "\n"
open(dest, "w", encoding="utf-8").write(page)
manifest["entries"][h] = rel_dest
STATS["docx"] += 1
PLAN.append(f"{rel_dest} (图 {got}/{total})")
except Exception as e:
STATS["fail"] += 1
PLAN.append(f" ❌ docx 失败 {rel_dest}: {str(e)[:120]}")
return
if typ == "sheet":
base_name = safe_name(name, token)
fname = base_name + ".xlsx"
dest = os.path.join(local_dir, fname)
rel_dest = os.path.relpath(dest, out_root)
if not write:
PLAN.append(f" 📊 sheet → {rel_dest} (导出 xlsx)")
STATS["export"] += 1
return
try:
os.makedirs(local_dir, exist_ok=True)
# lark-cli 的 --output-dir 也只认 cwd 内的相对路径,故 cwd=目标目录 + 相对 "."
lark(["drive", "+export", "--token", token, "--doc-type", "sheet",
"--file-extension", "xlsx", "--output-dir", ".",
"--file-name", base_name,
"--overwrite", "--as", "user", "--json"],
timeout=180, cwd=local_dir)
STATS["export"] += 1
PLAN.append(f"{rel_dest}")
except Exception as e:
STATS["fail"] += 1
PLAN.append(f" ❌ 导出失败 {rel_dest}: {str(e)[:120]}")
return
if typ == "bitable":
# 双轨:.base 原生存档(保真、可导回)+ 每个子表一份 csv可读、可检索
base_name = safe_name(name, token)
rel_base = os.path.relpath(os.path.join(local_dir, base_name + ".base"), out_root)
if not write:
PLAN.append(f" 🗂️ bitable → {rel_base} (.base 存档 + 各子表 csv)")
STATS["export"] += 1
return
try:
os.makedirs(local_dir, exist_ok=True)
# ① .base 原生格式存档
lark(["drive", "+export", "--token", token, "--doc-type", "bitable",
"--file-extension", "base", "--output-dir", ".",
"--file-name", base_name,
"--overwrite", "--as", "user", "--json"],
timeout=180, cwd=local_dir)
STATS["export"] += 1
PLAN.append(f"{rel_base}")
# ② 每个子表导一份 csv可读检索
tables = list_bitable_tables(token)
for t in tables:
sub_id = t.get("table_id")
sub_name = safe_name(t.get("name") or sub_id, sub_id)
csv_fname = f"{base_name}-{sub_name}"
rel_csv = os.path.relpath(
os.path.join(local_dir, csv_fname + ".csv"), out_root)
try:
lark(["drive", "+export", "--token", token,
"--doc-type", "bitable", "--file-extension", "csv",
"--sub-id", sub_id, "--output-dir", ".",
"--file-name", csv_fname,
"--overwrite", "--as", "user", "--json"],
timeout=180, cwd=local_dir)
STATS["csv"] += 1
PLAN.append(f" ↳ csv {rel_csv}")
except Exception as e:
STATS["fail"] += 1
PLAN.append(f" ↳ ❌ csv 失败 {rel_csv}: {str(e)[:80]}")
except Exception as e:
STATS["fail"] += 1
PLAN.append(f" ❌ bitable 失败 {rel_base}: {str(e)[:120]}")
return
if typ == "file":
fname = safe_name(name, token)
dest = os.path.join(local_dir, fname)
rel_dest = os.path.relpath(dest, out_root)
# 按扩展名跳过大文件(视频等);飞书列表不返回体积,靠扩展名判断,避免逐个多发请求
if skip_big and os.path.splitext(fname)[1].lower() in BIG_EXTS:
STATS["skipbig"] += 1
PLAN.append(f" ⏭️ 跳过大文件(--skip-big): {rel_dest}")
return
if not write:
PLAN.append(f" 📎 file → {rel_dest} (原样下载)")
STATS["file"] += 1
return
try:
os.makedirs(local_dir, exist_ok=True)
# lark-cli 的 --output 只认「cwd 内的相对路径」,所以 cwd=目标目录 + 相对文件名
lark(["drive", "+download", "--file-token", token,
"--output", "./" + fname, "--overwrite", "--as", "user", "--json"],
timeout=300, cwd=local_dir)
STATS["file"] += 1
PLAN.append(f"{rel_dest}")
except Exception as e:
STATS["fail"] += 1
PLAN.append(f" ❌ 下载失败 {rel_dest}: {str(e)[:120]}")
return
# 其它未知类型
STATS["skip"] += 1
PLAN.append(f" ⏭️ 未知类型 {typ}: {rel_path}/{name}")
def walk(folder_token, rel_path, out_root, host, manifest, write, skip_big=False, depth=0):
if depth > 20:
PLAN.append(f" ⚠️ 目录过深(>20层),停在 {rel_path}")
return
items = list_folder(folder_token)
for it in items:
if it.get("type") == "folder":
sub = os.path.join(rel_path, safe_name(it.get("name"), it.get("token"))) if rel_path \
else safe_name(it.get("name"), it.get("token"))
PLAN.append(f"📁 {sub}/")
walk(it["token"], sub, out_root, host, manifest, write, skip_big, depth + 1)
else:
handle_item(it, rel_path, out_root, host, manifest, write, skip_big)
def main():
ap = argparse.ArgumentParser(description="飞书文件夹递归镜像到本地")
ap.add_argument("--folder", required=True, help="飞书文件夹 token")
ap.add_argument("--out", required=True, help="本地输出目录")
ap.add_argument("--host", default="ncnc1z9jg8c4.feishu.cn",
help="飞书租户域名(拼 source_link 用)")
ap.add_argument("--write", action="store_true", help="实际写盘(默认干跑)")
ap.add_argument("--skip-big", action="store_true",
help="跳过大文件(视频/压缩包/PPT 等,见 BIG_EXTS),只拉可转 markdown 的文档")
args = ap.parse_args()
out_root = os.path.abspath(args.out)
manifest_path = os.path.join(out_root, ".feishu-sync-manifest.json")
manifest = load_manifest(manifest_path)
mode = "写盘" if args.write else "干跑(不落盘)"
print(f"=== feishu-sync [{mode}] ===")
print(f"飞书文件夹: {args.folder}")
print(f"本地输出: {out_root}\n")
if args.write:
os.makedirs(out_root, exist_ok=True)
ProgressList.LIVE = True # 写盘时逐条实时打印进度,避免长时间无输出
print("正在扫描飞书并逐个同步,下面会实时显示进度(文件多时需十几分钟,请耐心等待):\n", flush=True)
try:
walk(args.folder, "", out_root, args.host, manifest, args.write, args.skip_big)
except Exception as e:
# 顶层兜底:飞书未登录/登录过期/网络异常时,给人话而非一屏 Traceback
msg = str(e)
if any(k in msg for k in ("access_token", "unauthor", "登录", "auth",
"not configured", "identity", "401", "403")):
print("\n⚠️ 飞书未登录或登录已过期。请先运行 `lark-cli auth login "
"--domain drive,docs --recommend` 扫码登录,再重试。", file=sys.stderr)
else:
print(f"\n⚠️ 同步中断:{msg[:200]}\n"
" 若反复出现,请把本提示截图求助。", file=sys.stderr)
sys.exit(2)
# 干跑:最后一次性打印全部计划;写盘:已在处理时实时打印过,不再重复
if not args.write:
print("\n".join(PLAN))
print("\n=== 汇总 ===")
print(f"云文档 docx: {STATS['docx']} 在线表格/bitable导出: {STATS['export']} "
f"bitable子表csv: {STATS['csv']} 上传文件: {STATS['file']}")
print(f"跳过(思维导图/快捷方式): {STATS['skip']} 跳过大文件: {STATS['skipbig']} "
f"去重跳过: {STATS['dedup']} 图片下载: {STATS['img']} 失败: {STATS['fail']}")
if args.write:
save_manifest(manifest_path, manifest)
print(f"\n台账已更新: {manifest_path}")
else:
print("\n以上为干跑预览。加 --write 实际落盘。")
if __name__ == "__main__":
main()

43
tools/kb-bot/README.md Executable file
View File

@@ -0,0 +1,43 @@
# kb-bot —— 飞书群知识库问答机器人
> 群里 @机器人 提问 → 查知识库 → 带溯源回复。**长连接,无需公网服务器。**
> 底座lark-clibot 身份自管凭据,不入库)。详见 `docs/08-飞书双向前端方案.md`。
## 组件
| 文件 | 作用 |
|---|---|
| `subscribe.sh` | 起 WebSocket 长连接,把 @机器人 群消息事件落到 `events/` |
| `handle.py` | 轮询 `events/`,抽问题→调 `/kb-ask``lark-cli` 回复 |
| `send.sh` | 手动发消息(验证发消息通路用) |
## 分步验证
```bash
# P2-a 发消息:查群 id 后手动发一条
lark-cli im +chat-search --query "你的群名"
bash tools/kb-bot/send.sh oc_xxxxx "**kb-bot 上线测试**"
# P2-b 收消息:起长连接,去群里 @机器人 说句话,看 events/ 是否落文件
bash tools/kb-bot/subscribe.sh # 前台观察
# P2-c 闭环:另开一个终端跑消费者
python3 tools/kb-bot/handle.py --watch
```
## 常驻部署(验证通过后)
跑在任一常开机器Mac mini / VPS 皆可,长连接不挑落点):
```bash
nohup bash tools/kb-bot/subscribe.sh > /tmp/kb-bot-sub.log 2>&1 &
nohup python3 tools/kb-bot/handle.py --watch > /tmp/kb-bot-handle.log 2>&1 &
```
稳定后再包成 launchd(macOS)/systemd(Linux) 常驻服务。
## 铁律(继承知识库)
- 机器人**只读**知识库,绝不写/删。
- 答案强制带 `source_link` 溯源;无据回「知识库暂无」,**绝不编造**(由 `/kb-ask` 保证)。
- `events/``.seen-msg-ids` 是运行时产物,已 gitignore。

120
tools/kb-bot/handle.py Executable file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""handle.py —— 消费飞书群消息事件,调 /kb-ask 回答,回复到群。
轮询 events/ 目录subscribe.sh 落的事件文件):
过滤 @机器人 的消息 → 抽问题 → CC headless 跑 /kb-ask → lark-cli 回复。
用法:
python3 tools/kb-bot/handle.py # 轮询一轮已有事件后退出
python3 tools/kb-bot/handle.py --watch # 常驻轮询
铁律:只读知识库、答案带溯源、无据报 Gap、绝不编造由 /kb-ask 保证)。
凭据由 lark-cli 自管,不在本脚本。
"""
import sys
import os
import re
import json
import time
import subprocess
BOT_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.abspath(os.path.join(BOT_DIR, "..", ".."))
EVENTS_DIR = os.path.join(BOT_DIR, "events")
DONE_DIR = os.path.join(BOT_DIR, "events", ".done")
SEEN_FILE = os.path.join(BOT_DIR, ".seen-msg-ids")
os.environ["PATH"] = os.path.expanduser("~/.npm-global/bin") + ":" + os.environ.get("PATH", "")
def load_seen():
if os.path.isfile(SEEN_FILE):
return set(open(SEEN_FILE, encoding="utf-8").read().split())
return set()
def mark_seen(mid):
with open(SEEN_FILE, "a", encoding="utf-8") as f:
f.write(mid + "\n")
def extract(event):
"""从事件里取 (message_id, chat_id, 纯文本问题);非群 @ 消息返回 None。"""
# --compact 扁平结构;兼容原始嵌套结构
mid = event.get("message_id") or event.get("event", {}).get("message", {}).get("message_id")
chat_id = event.get("chat_id") or event.get("event", {}).get("message", {}).get("chat_id")
text = event.get("text") or ""
if not text:
content = event.get("content") or event.get("event", {}).get("message", {}).get("content", "")
if isinstance(content, str) and content:
try:
text = json.loads(content).get("text", "")
except json.JSONDecodeError:
text = content
if not (mid and chat_id and text):
return None
# 剥离 @_user_1 等 @ 标记
q = re.sub(r"@_?\w+", "", text).strip()
if not q:
return None
return mid, chat_id, q
def ask_kb(question):
"""调 CC headless 跑 /kb-ask返回答案文本。"""
try:
r = subprocess.run(
["claude", "-p", f"/kb-ask {question}"],
cwd=ROOT, capture_output=True, text=True, timeout=180,
)
return (r.stdout or "").strip() or "(知识库查询无输出,请稍后重试)"
except FileNotFoundError:
return "CC 未安装,无法查询)"
except subprocess.TimeoutExpired:
return "(查询超时,请缩小问题范围)"
def reply(chat_id, mid, answer):
subprocess.run(
["lark-cli", "im", "+messages-reply",
"--message-id", mid, "--markdown", answer, "--as", "bot"],
capture_output=True, text=True,
)
def process_once(seen):
if not os.path.isdir(EVENTS_DIR):
return
os.makedirs(DONE_DIR, exist_ok=True)
for fn in sorted(os.listdir(EVENTS_DIR)):
fp = os.path.join(EVENTS_DIR, fn)
if not fn.endswith(".json") or not os.path.isfile(fp):
continue
try:
event = json.load(open(fp, encoding="utf-8"))
except (json.JSONDecodeError, OSError):
continue
parsed = extract(event)
if parsed:
mid, chat_id, q = parsed
if mid not in seen:
answer = ask_kb(q)
reply(chat_id, mid, answer)
mark_seen(mid)
seen.add(mid)
print(f"[kb-bot] answered {mid}: {q[:40]}")
os.replace(fp, os.path.join(DONE_DIR, fn))
def main():
seen = load_seen()
watch = "--watch" in sys.argv[1:]
while True:
process_once(seen)
if not watch:
break
time.sleep(3)
if __name__ == "__main__":
main()

24
tools/kb-bot/send.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# send.sh —— 手动发一条 markdown 消息到飞书群P2-a 验证发消息通路)
#
# 用法:
# bash tools/kb-bot/send.sh <chat-id> "markdown 内容"
# # chat-id 用 lark-cli im +chat-search --query "群名" 查
#
# 加 --dry-run 只打印不发。
set -euo pipefail
export PATH="$HOME/.npm-global/bin:$PATH"
CHAT_ID="${1:-}"
CONTENT="${2:-}"
if [ -z "$CHAT_ID" ] || [ -z "$CONTENT" ]; then
echo "用法: bash tools/kb-bot/send.sh <chat-id> \"markdown 内容\"" >&2
echo "提示: 先用 lark-cli im +chat-search --query \"群名\" 查 chat-id" >&2
exit 1
fi
lark-cli im +messages-send \
--chat-id "$CHAT_ID" \
--markdown "$CONTENT" \
--as bot

35
tools/kb-bot/subscribe.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# subscribe.sh —— 飞书群消息长连接订阅WebSocket无需公网回调
#
# 起一个常驻长连接,把 @机器人 的群消息事件逐条落到 events/ 目录,
# 由 handle.py 消费。单实例锁由 lark-cli 保证(勿加 --force
#
# 用法:
# bash tools/kb-bot/subscribe.sh # 前台跑
# nohup bash tools/kb-bot/subscribe.sh & # 后台常驻
#
# 依赖: lark-cli 已装且 bot 身份可用lark-cli auth status
set -euo pipefail
BOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EVENTS_DIR="$BOT_DIR/events"
mkdir -p "$EVENTS_DIR"
export PATH="$HOME/.npm-global/bin:$PATH"
if ! command -v lark-cli >/dev/null 2>&1; then
echo "ERROR: lark-cli 不在 PATH。请先安装并 lark-cli auth login。" >&2
exit 2
fi
echo "[kb-bot] 订阅飞书群消息事件 → $EVENTS_DIR"
echo "[kb-bot] 只收 im.message.receive_v1Ctrl-C 停止。"
# --output-dir: 每个事件写成独立 JSON 文件handle.py 轮询消费
# --compact: 扁平化,提取 text去噪
# --quiet: 抑制 stderr 状态噪音(避免日志泄露)
exec lark-cli event +subscribe \
--event-types im.message.receive_v1 \
--compact \
--quiet \
--output-dir "$EVENTS_DIR"

80
tools/kb-init.sh Executable file
View File

@@ -0,0 +1,80 @@
#!/usr/bin/env bash
# kb-init.sh —— 幂等建项目骨架
# 用法:
# bash tools/kb-init.sh <代号> # 建 projects/<代号>/ 骨架 + _index.md(type: project, status: seed)
# bash tools/kb-init.sh --stats # 打印源计数(更新 meta/stats.md 参考值)
# 约束: <代号> 用英文/拼音禁纯中文目录名Claude Code 会把中文编码为 - 导致碰撞)。
set -euo pipefail
# 定位库根(本脚本在 tools/ 下)
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
usage() { grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }
# ── --stats: 红线观察点 ──
if [[ "${1:-}" == "--stats" ]]; then
proj=$(find "$ROOT/projects" -mindepth 1 -maxdepth 1 -type d ! -name '_example' 2>/dev/null | wc -l | tr -d ' ')
pages=$(grep -rl '^type:' "$ROOT/projects" "$ROOT/entities" "$ROOT/concepts" "$ROOT/questions" 2>/dev/null | wc -l | tr -d ' ')
srcs=$(grep -rh '^source_link:' "$ROOT/projects" 2>/dev/null | sort -u | wc -l | tr -d ' ')
echo "项目数: $proj | 知识页数: $pages | 外部源数(去重): $srcs"
exit 0
fi
CODE="${1:-}"
[[ -z "$CODE" || "$CODE" == "-h" || "$CODE" == "--help" ]] && usage 0
# 中文目录名保护
if echo "$CODE" | LC_ALL=C grep -q '[^ -~]'; then
echo "错误: 代号 '$CODE' 含非 ASCII 字符。请用英文/拼音(禁纯中文目录名)。" >&2
exit 1
fi
PDIR="$ROOT/projects/$CODE"
if [[ -d "$PDIR" ]]; then
echo "项目已存在: projects/$CODE (幂等:不覆盖,仅补齐缺失子目录)"
fi
mkdir -p "$PDIR"/{docs,decisions,conversations,assets}
for sub in docs assets; do [[ -f "$PDIR/$sub/.gitkeep" ]] || touch "$PDIR/$sub/.gitkeep"; done
INDEX="$PDIR/_index.md"
if [[ -f "$INDEX" ]]; then
echo "保留已有 _index.md幂等: projects/$CODE/_index.md"
else
TODAY="$(date +%F)"
cat > "$INDEX" <<EOF
---
type: project
title: "$CODE"
description:
status: seed
created: $TODAY
ingested: $TODAY
tags: []
related: []
---
# $CODE
## 概览
一句话说明本项目是什么、当前在做什么。
## 进展
-
## 关键决策
(见 decisions/,用 [[slug]] 链接)
## 需求方 / 参与人
-
## 资料索引
- docs/ :外部汇入文档
- conversations/ :聊天沉淀
- decisions/ :决策记录
EOF
echo "已创建: projects/$CODE/_index.md (type: project, status: seed)"
fi
echo "骨架就绪: projects/$CODE/{docs,decisions,conversations,assets}"
echo "下一步: 丢资料 + 一句说明,或运行 kb-lint-fm.py 校验。"

178
tools/kb-lint-fm.py Executable file
View File

@@ -0,0 +1,178 @@
#!/usr/bin/env python3
"""kb-lint-fm.py —— frontmatter 校验器
单一真相源:读 meta/kb-contract.yaml 判定 type 白名单 / 必填矩阵 / enum
绝不硬编码 type 或字段清单。
用法:
python3 tools/kb-lint-fm.py [路径...] # 默认扫全库
python3 tools/kb-lint-fm.py projects/foo # 只扫某目录/文件
退出码:
0 = 全部通过
1 = 有错误(缺硬必填 / 未激活 type / enum 非法 / 派生必填缺失)
2 = 运行环境问题(缺 PyYAML / 缺契约文件)
供 CC 判定:非 0 即需处理。
"""
import sys
import os
import re
import fnmatch
try:
import yaml
except ImportError:
print("ERROR: 需要 PyYAML。请运行: pip install pyyaml", file=sys.stderr)
sys.exit(2)
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
CONTRACT_PATH = os.path.join(ROOT, "meta", "kb-contract.yaml")
FM_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
def load_contract():
if not os.path.isfile(CONTRACT_PATH):
print(f"ERROR: 缺契约文件 {CONTRACT_PATH}", file=sys.stderr)
sys.exit(2)
with open(CONTRACT_PATH, encoding="utf-8") as f:
return yaml.safe_load(f)
def is_exempt(rel, patterns):
for pat in patterns:
if fnmatch.fnmatch(rel, pat) or fnmatch.fnmatch(rel, pat.rstrip("/") + "/*"):
return True
# 目录型 globdocs/**)也匹配 docs/ 下任意层
for pat in patterns:
if pat.endswith("/**") and rel.startswith(pat[:-2]):
return True
return False
def parse_frontmatter(text):
m = FM_RE.match(text)
if not m:
return None, "缺 frontmatter 块(--- 开头)"
try:
data = yaml.safe_load(m.group(1))
except yaml.YAMLError as e:
return None, f"frontmatter YAML 解析失败: {e}"
if not isinstance(data, dict):
return None, "frontmatter 不是键值映射"
return data, None
def collect_md(paths):
files = []
for p in paths:
ap = os.path.abspath(p)
if os.path.isfile(ap) and ap.endswith(".md"):
files.append(ap)
elif os.path.isdir(ap):
for dp, _, fns in os.walk(ap):
for fn in fns:
if fn.endswith(".md"):
files.append(os.path.join(dp, fn))
return sorted(set(files))
def check_derived(data, rules):
"""返回派生必填字段名列表"""
req = []
t = data.get("type")
src = data.get("source")
for rule in rules or []:
w = rule.get("when", {})
ok = True
if "type_in" in w and t not in w["type_in"]:
ok = False
if "source_in" in w and src not in w["source_in"]:
ok = False
if ok:
req.extend(rule.get("require", []))
return req
def main():
contract = load_contract()
types = {t["name"]: t for t in contract.get("types", [])}
active = {n for n, t in types.items() if t.get("active")}
fields = contract.get("fields", {})
enums = contract.get("enums", {})
exempt = contract.get("frontmatter_exempt", [])
source_exempt_types = set(contract.get("source_exempt_types", []))
derived = contract.get("derived_rules", [])
always_required = [f for f, spec in fields.items()
if spec.get("required") == "always"]
targets = sys.argv[1:] or [ROOT]
files = collect_md(targets)
errors = 0
warnings = 0
checked = 0
for fp in files:
rel = os.path.relpath(fp, ROOT)
if is_exempt(rel, exempt):
continue
checked += 1
with open(fp, encoding="utf-8") as f:
text = f.read()
data, err = parse_frontmatter(text)
if err:
print(f"[ERROR] {rel}: {err}")
errors += 1
continue
# type 硬必填 + 白名单
t = data.get("type")
if not t:
print(f"[ERROR] {rel}: 缺硬必填字段 type")
errors += 1
continue
if t not in types:
print(f"[ERROR] {rel}: 未知 type '{t}'(不在契约中,扩展需改 kb-contract.yaml")
errors += 1
elif t not in active:
print(f"[ERROR] {rel}: type '{t}' 已登记但未激活(改契约 active: true 再用)")
errors += 1
# 其余硬必填
for f_ in always_required:
if f_ == "type":
continue
if data.get(f_) in (None, ""):
print(f"[ERROR] {rel}: 缺硬必填字段 {f_}")
errors += 1
# 派生必填project 型豁免溯源)
req = check_derived(data, derived)
if t in source_exempt_types:
req = [r for r in req if r not in ("source", "source_link")]
for f_ in req:
if data.get(f_) in (None, ""):
print(f"[ERROR] {rel}: 派生必填缺失 {f_}type={t} + source 条件触发)")
errors += 1
# enum 校验
for f_, allowed in enums.items():
v = data.get(f_)
if v is not None and v not in allowed:
print(f"[ERROR] {rel}: 字段 {f_}='{v}' 不在 enum {allowed}")
errors += 1
# 软提示:建议但非必填的 title
if not data.get("title"):
print(f"[WARN] {rel}: 建议补 titleAI 可自动=H1 或文件名)")
warnings += 1
print(f"\n检查 {checked} 页 | 错误 {errors} | 警告 {warnings}")
sys.exit(1 if errors else 0)
if __name__ == "__main__":
main()

353
完整项目现状报告.md Normal file
View File

@@ -0,0 +1,353 @@
# 公司知识库company-kb完整项目现状与落地方案
> 基于完整代码审查和实际状态验证的准确报告
> 更新时间2026-07-16
> 验证方式:逐文件读取 + 目录扫描 + 工具检查
---
## 📊 项目真实现状(不是猜测)
### 一、项目定位与设计理念
**这是什么**
- 一个 AI-native 的公司知识库系统
- **不是**只有飞书集成,而是一个**完整的知识管理框架**
- 核心理念:单一 Git 真相源 + OKF frontmatter + AI 主执行 + 可生长不重做
**关键设计原则**(从 AI-CONTEXT.md 提取):
1. **单一真相源** = Git 仓库本身,文件即真相,无数据库
2. **OKF frontmatter** = 每页顶部的结构化元数据type/title/source/tags等
3. **溯源铁律** = AI 回答必须挂原始链接,无据报 Gap绝不编造
4. **反过度工程** = 编译层P4、向量库P5按信号触发延后默认不上
### 二、完整目录结构(实际扫描结果)
```
company-kb-v0.1-mvp-20260707/
├── README.md ← 总索引
├── AI-CONTEXT.md ← AI 的单一入口(全景蒸馏)
├── CLAUDE.md ← Claude 项目级工作记忆
├── CONVENTIONS.md ← 录入宪法
├── .gitignore ← Git 忽略规则
├── docs/ ← 治理文档12篇
│ ├── 01-方案总览.md
│ ├── 02-首个闭环-项目资料.md
│ ├── 03-知识库框架选型调研.md
│ ├── 04-架构.md
│ ├── 05-路线图.md
│ ├── 06-工具链.md
│ ├── 07-AI-native工作流.md
│ ├── 08-飞书双向前端方案.md
│ ├── 09-同事使用指南.md
│ ├── 10-部署与运维.md
│ ├── 11-信息全景与非项目结构.md
│ ├── 12-治理与规则维护.md
│ └── obsidian-setup.md
├── meta/ ← 契约与统计
│ ├── kb-contract.yaml ← 唯一契约type/字段/enum规则
│ ├── stats.md ← 源计数观察点
│ └── ingest-manifest.json ← 去重台账
├── projects/ ← 项目知识
│ ├── _example/ ← 示例项目(模板)
│ │ ├── _index.md ← 项目门户
│ │ ├── docs/ ← 外部文档
│ │ ├── decisions/ ← 决策记录
│ │ ├── conversations/ ← 聊天沉淀
│ │ └── assets/ ← 附件
│ └── wanniu-l1/ ← 真实项目(我帮你创建的测试项目)
│ ├── _index.md
│ ├── docs/课程大纲v1.md
│ ├── decisions/小班制20人.md
│ └── ...
├── company/ ← 公司基本信息
│ └── _index.md ← (骨架已建,内容待填充)
├── learning/ ← 共享学习库
│ ├── _index.md
│ ├── ai/ ← AI 相关学习
│ ├── management/ ← 管理相关
│ └── industry/ ← 行业知识
├── entities/ ← 实体知识(预留,未激活)
├── concepts/ ← 概念知识(预留,未激活)
├── questions/ ← 问题知识(预留,未激活)
├── tools/ ← 工具脚本
│ ├── kb-init.sh ← ✅ 完整实现:项目骨架生成
│ ├── kb-lint-fm.py ← ✅ 完整实现frontmatter校验
│ ├── feishu-sync.py ← ✅ 完整实现:飞书采集(你已有的)
│ ├── feishu-pull.py ← ⚠️ 骨架TODO标记
│ ├── feishu-render.py ← ⚠️ 骨架TODO标记
│ ├── kb-bridge.py ← ❌ 不存在(需创建)
│ └── kb-bot/ ← ✅ 完整实现:飞书群机器人
│ ├── subscribe.sh ← WebSocket长连接收消息
│ ├── handle.py ← 处理消息+调/kb-ask+回复
│ ├── send.sh ← 手动发送测试
│ └── README.md ← 使用说明
└── .claude/commands/ ← Claude Code 命令
├── kb-ask.md ← 查询知识库
├── kb-check.md ← 健康检查
├── kb-ingest.md ← 录入资料
├── kb-link.md ← 建立关联
├── kb-new.md ← 新建项目
├── kb-save.md ← 保存知识
└── kb-status.md ← 查看统计
```
### 三、实际数据meta/stats.md
| 指标 | 当前值 | 说明 |
|------|-------|------|
| **项目数** | 1个wanniu-l1测试项目 | _example 不计 |
| **知识页数** | 7页 | 包含 _index、docs、decisions |
| **外部源数** | ~3个 | 测试数据 |
| **状态** | **P0 阶段完成P1 测试阶段** | 骨架已建,首个项目已有示例内容 |
### 四、分阶段实施路线图docs/05-路线图.md
| 阶段 | 目标 | 当前状态 | 进入下阶段信号 |
|------|------|---------|---------------|
| **P0 骨架+规范** | 定死物理形态+契约+命令雏形 | ✅ **已完成** | kb-init/kb-lint能跑、CC能读引用 |
| **P1 首个项目闭环** | 一个项目资料全汇入、可溯源问答 | 🔄 **进行中** | wanniu-l1已建待补充更多真实内容 |
| **P2 多项目+飞书导入** | 扩到多项目+feishu-pull半自动导入 | ⏳ 未开始 | 项目数≥3、飞书成主要文档源 |
| **P3 全公司领域** | 加company/domains分区+Obsidian成熟 | ⏳ 未开始 | 非项目知识需求强烈 |
| **P4 编译层** | 生成互链wiki+hot/index | ⏳ 未开始 | _index首读定位失效+摄入悖论 |
| **P5 向量库** | 向量库导航层 | ⏳ 未开始 | ~100源/几百页+index定位不准 |
**当前阶段判定****P0完成P1进行中**(有测试项目但缺真实内容)
---
## 🔧 工具与功能实现状态
### 已完整实现(可直接使用)
| 工具 | 完成度 | 功能 | 验证结果 |
|------|--------|------|---------|
| **kb-init.sh** | ✅ 100% | 创建项目骨架_index+4个子目录 | 已验证创建了wanniu-l1 |
| **kb-lint-fm.py** | ✅ 100% | 读契约校验frontmatter | 已验证能跑依赖PyYAML |
| **feishu-sync.py** | ✅ 100% | 飞书采集+转markdown+下载图片+去重 | 你已有的(飞书同步工具包) |
| **kb-bot/** | ✅ 100% | 飞书群机器人WebSocket+调/kb-ask | 代码完整,依赖齐全,未实测 |
| **7个/kb-*命令** | ✅ 100% | Claude Code快捷命令 | .claude/commands/全部存在 |
| **meta/kb-contract.yaml** | ✅ 100% | type/字段/enum契约 | 定义了9个type4个激活 |
### 部分实现(骨架/TODO
| 工具 | 状态 | 缺失内容 |
|------|------|---------|
| **feishu-pull.py** | ⚠️ 骨架 | TODO标记飞书SDK调用未实现 |
| **feishu-render.py** | ⚠️ 骨架 | TODO标记多维表格写入未实现 |
### 未实现(需创建)
| 工具 | 状态 | 作用 |
|------|------|------|
| **kb-bridge.py** | ❌ 不存在 | 飞书markdown → 知识库知识页的桥接 |
| **auto-sync.sh** | ❌ 不存在 | 整合feishu-sync+kb-bridge的定时脚本 |
| **Git仓库部署** | ❌ 未建 | NAS Gitea真相源 |
---
## 🤖 机器人现状梳理
### bot-v2阿里云ECS已上线
**位置**`47.110.48.113:/opt/bot-v2`
**技术栈**Node.js + lark-cli + SQLite
**功能**9大能力
1. 快速问答Claude API
2. 整理聊天历史(拉取+分析)
3. 存知识库(推送到飞书多维表格)
4. 生图Gemini API
5. RAG 检索
6. 会议纪要自动生成
7. 任务队列异步处理
8. 上下文记忆SQLite
9. 多群白名单配置
**与知识库关系**
- ❌ 目前**未连接**知识库(不能查 projects/
- ✅ 有自己的知识存储SQLite + 飞书多维表格)
- 🔄 **可扩展**:可以加一个"查知识库"功能
### kb-bot知识库内置未部署
**位置**`company-kb/tools/kb-bot/`
**技术栈**Python + lark-cli + Claude Code CLI
**功能**1个能力
- 查知识库问答(调 `/kb-ask` → 读 projects/ → 溯源回答)
**与知识库关系**
-**深度整合**(直接读 projects/,遵守溯源铁律)
-**未部署**(代码完整但没实际运行验证)
- 📋 **定位**:轻量专注版,只做知识库问答
### 两者定位对比
| 维度 | bot-v2 | kb-bot |
|------|--------|--------|
| **定位** | 全能助手(生产主力) | 知识库专属问答 |
| **功能范围** | 9大功能 | 1个功能 |
| **知识源** | SQLite + 飞书多维表格 | Git仓库 projects/ |
| **溯源能力** | 一般 | 强frontmatter强制 |
| **部署状态** | ✅ 已上线 | ❌ 未部署 |
| **维护成本** | 高(代码多) | 低200行 |
---
## 🎯 核心缺失环节(阻碍完整落地的)
### 缺失1Git 真相源未搭建
**现状**知识库只是本地文件夹SynologyDrive 同步盘)
**缺失**
- ❌ NAS Gitea 仓库未建http://192.168.0.101:3002/robert/company-kb
- ❌ 本地工作副本未 clone~/work/company-kb/
- ❌ 无版本控制、协作机制
**影响**
- 无法多人协作
- 无法回滚版本
- 无法审计变更
- 机器人无法通过 Git 同步知识库
### 缺失2飞书采集链路未打通
**现状**:有 feishu-sync.py能拉飞书但没有 kb-bridge.py转知识页
**缺失**
- ❌ kb-bridge.py 不存在飞书markdown → 完整frontmatter → projects/
- ❌ auto-sync.sh 不存在(整合采集+桥接的定时脚本)
- ❌ 定时任务未配置
**影响**
- 飞书文档不能自动进入知识库
- 需要手动 `/kb-ingest` 录入(低效)
### 缺失3bot-v2 和知识库未打通
**现状**bot-v2 在 ECS 运行,但不能查 projects/
**缺失**
- ❌ bot-v2 没有"查知识库"功能
- ❌ ECS 上没有 Git clone 知识库
- ❌ 没有定期 Git pull 同步机制
**影响**
- 群里不能问"万牛会L1怎么安排的"(查不到 projects/
- 知识库和机器人是两套系统
---
## 📋 完整落地的正确路径(不是猜测)
### 阶段0搭建Git真相源必做1小时
**目标**:知识库变成"活的",有版本控制
**步骤**
1. NAS上建Gitea仓库`git init --bare company-kb.git`
2. 本地clone`git clone ssh://...@192.168.0.101/.../company-kb.git ~/work/company-kb`
3. 推送现有内容:`cp -r 现有知识库/* ~/work/company-kb/ && git push`
**验证**:能 `git pull/push`NAS上能看到提交记录
### 阶段1打通飞书采集链路P2核心1-2天
**目标**:飞书文档自动拉取 → 转知识页 → Git
**步骤**
1. 创建 kb-bridge.py我帮你生成调用Claude API分析+生成frontmatter
2. 测试feishu-sync → kb-bridge → lint → git push
3. 创建 auto-sync.sh整合两个脚本
4. 配置定时任务NAS cron每天10:00
**验证**:飞书新文档第二天自动出现在 projects/
### 阶段2bot-v2整合知识库查询1天
**目标**:群里能问知识库问题
**方案A**推荐bot-v2加"查知识库"功能
- ECS上 `git clone` 知识库
- 装 Claude Code CLI
- router.js 加判断:如果问题含"项目/知识库" → 调 `claude -p "/kb-ask xxx"`
**方案B**部署独立的kb-bot
- 部署到NAS或ECS
- 用另一个机器人账号
- 专门做知识库问答
**验证**:群里@机器人问"万牛会L1怎么安排的"能回答并带source_link
### 阶段3补充真实内容持续
**目标**从P1测试阶段到P1完成
**步骤**
1. 录入2-3个真实项目用 kb-init.sh 创建)
2. 每个项目录入5-10条知识决策/文档/聊天)
3.`/kb-ask` 验证查询效果
4. 同事试用,收集反馈
**完成标准**同事认可价值产生复制需求进入P2信号
---
## 🚨 关键澄清(我之前的错误)
### 错误1说kb-bot是"空壳"
**纠正**kb-bot代码100%完整,逻辑清晰,依赖齐全。我当时没读内容就下判断,错了,认错。
### 错误2瞎猜你的需求和现状
**纠正**没搞清楚你有bot-v2、知识库设计理念、P0-P5路线图就给建议导致绕圈子。这次系统读完所有文档确认了真实现状。
### 错误3把company-kb当成"飞书项目"
**纠正**company-kb是**完整的知识管理框架**飞书只是采集源之一还有NAS投递区、手动录入。它有完整的架构设计、治理文档、工具链、命令系统。
---
## ✅ 现在给你的是什么
**一份准确的现状报告**
1. ✅ 完整目录结构(实际扫描)
2. ✅ 每个工具的真实状态(逐个验证)
3. ✅ 分阶段路线图(从文档提取)
4. ✅ 核心缺失环节阻碍落地的3个点
5. ✅ bot-v2 和 kb-bot 的真实关系(不是猜测)
6. ✅ 正确的落地路径(基于设计文档,不是我瞎想的)
**不再包含**
- ❌ 猜测和假设
- ❌ 没验证的建议
- ❌ 让你重复回答已经说过的问题
---
## 📌 下一步(等你指示)
我现在**不主动建议做什么**,等你说:
**选项A**:更新知识库里的「落地方案.md」把这份准确报告写进去
**选项B**开始实施阶段0搭Git给你逐条命令
**选项C**继续深挖某个模块比如bot-v2和知识库怎么打通
**选项D**:其他你想做的
你说。

419
落地方案.md Normal file
View File

@@ -0,0 +1,419 @@
# 公司知识库 · 完整落地方案
> 从架构到实施的一站式方案:整体框架 + 飞书对接 + 分阶段落地
> 更新时间2026-07-16
---
# 第一部分:整体架构
## 1.1 这是什么系统?
一个 **AI 原生的公司知识库**
- **文件柜Git 仓库)** = 存所有知识,无数据库
- **标签纸frontmatter** = 每篇文档的元数据,让 AI 快速识别
- **AI 管理员Claude** = 常驻,自动采集、生成、问答
**核心理念4 条铁律)**
1. **单一真相源** = Git 仓库本身,文件即真相
2. **编译过的链接** = 不搬原件,提炼精华 + 留 source_link 指回原文
3. **溯源铁律** = 每条结论必挂原始链接,无据报 Gap绝不编造
4. **AI 权限边界** = 安全项自动做(补字段/建链),有损项挂起交人(删除/合并)
## 1.2 四层架构
```
┌────────────────────────────────────────────────┐
│ L4 呈现层 Obsidian编辑 + 飞书展示 + Claude问答 │
├────────────────────────────────────────────────┤
│ L3 处理层 lint校验 + AI语义检索 + 去重 │
├────────────────────────────────────────────────┤
│ L2 汇聚层 Git仓库 + OKF frontmatter ★真相源★ │
├────────────────────────────────────────────────┤
│ L1 采集层 飞书 + NAS投递区 + 手动点名 │
└────────────────────────────────────────────────┘
```
## 1.3 目录结构
```
company-kb/ # Git 根 = 唯一真相源
├── projects/ # 项目知识
│ ├── <代号>/ # 每个项目一个档案盒
│ │ ├── _index.md # 项目门户 (type: project)
│ │ ├── docs/ # 外部文档 (type: doc)
│ │ ├── decisions/ # 决策记录 (type: decision)
│ │ ├── conversations/ # 聊天沉淀 (type: conversation)
│ │ └── assets/ # 原始附件(免 frontmatter
│ └── _example/ # 示例项目(照抄即合规)
├── company/ # 公司基本信息P3 启用)
├── learning/ # 共享学习库ai/management/industry
├── entities/ concepts/ questions/ # 预留 type未激活
├── meta/
│ ├── kb-contract.yaml # ★唯一契约type/字段/enum 规则
│ ├── ingest-manifest.json # 去重台账
│ └── stats.md # 统计观察点
├── tools/ # 工具脚本
└── .claude/commands/ # Claude 命令面板 (/kb-*)
```
## 1.4 两个核心概念
### frontmatter = 文件的"标签纸"
每个 .md 文件顶部用 `---` 包起来的元数据:
```yaml
---
type: decision # 类型(唯一硬必填)
title: "客户A阶梯定价" # 标题
description: 因预算分期改用阶梯定价 # 一句话描述
tags: [报价, 客户-a] # 标签(筛选用)
timestamp: 2026-06-20 # 发生时间
source: 飞书群 # 来源渠道
source_link: https://... # 原始链接(溯源)
status: mature # 成熟度 seed/developing/mature
created: 2026-06-20 # 创建时间
ingested: 2026-06-24 # 录入时间
related: ["[[项目门户]]"] # 关联页面
---
```
**作用**:让 AI 先看标签筛选(快、准),再读内容;回答时能挂原文链接(可溯源)。
### projects/<代号>/ = 项目档案盒
每个项目一个统一结构的文件夹所有资料按类型放进对应抽屉docs/decisions/conversations/assets。好处结构统一、AI 友好、照抄即合规。
**创建方式**`bash tools/kb-init.sh <项目代号>`(自动生成骨架 + _index.md
---
# 第二部分:现状盘点
## 2.1 已完成70%
| 组件 | 完成度 | 功能 | 位置 |
|------|--------|------|------|
| **feishu-sync.py** | ✅ 100% | 飞书采集+去重+图片下载+简单frontmatter | 飞书同步工具包/ |
| **run-sync.sh** | ✅ 100% | 定时任务包装 | 飞书同步工具包/ |
| **安装.command** | ✅ 100% | 一键装环境+配定时任务 | 飞书同步工具包/ |
| kb-lint-fm.py | ✅ 100% | frontmatter 校验器 | tools/ |
| kb-init.sh | ✅ 100% | 项目骨架生成 | tools/ |
| kb-contract.yaml | ✅ 100% | type/字段/enum 契约 | meta/ |
| Claude 命令面板 | ✅ 100% | 7个 /kb-* 命令 | .claude/commands/ |
| 示例项目 | ✅ 100% | _example/ | projects/ |
**关键**:你的 `feishu-sync.py` 已经把「飞书采集」这一最难的环节做完了——递归拉取、docx转markdown、下载图片、去重、定时任务全有。
## 2.2 待补全30%
| 组件 | 状态 | 工作量 | 优先级 | 功能 |
|------|------|--------|--------|------|
| **kb-bridge.py** | ❌ 不存在 | 1-2天 | **P0** | 飞书markdown → 知识库知识页 |
| **Git 仓库部署** | ❌ 未建 | 半天 | **P0** | NAS Gitea 真相源 |
| **auto-sync.sh** | ❌ 不存在 | 半天 | **P0** | 整合采集+桥接的定时脚本 |
| nas-watch.py | ❌ 不存在 | 1天 | P1 | 监控投递区自动采集 |
| feishu-render.py | ❌ 骨架 | 2天 | P1 | 推送到飞书多维表格 |
| pre-commit hook | ❌ 不存在 | 半天 | P1 | Git提交前自动校验 |
---
# 第三部分:完整流程图
## 3.1 采集与处理流程
```mermaid
graph TB
A1[飞书文档] --> B{采集方式}
A2[飞书群聊] --> B
A3[NAS投递区] --> B
B -->|手动| C1["/kb-ingest命令"]
B -->|定时自动| C2["feishu-sync.py<br/>每天10:00"]
B -->|实时监控| C3["nas-watch.py"]
C1 --> D[kb-bridge.py<br/>桥接脚本]
C2 --> D
C3 --> D
D --> E[AI分析<br/>Claude API]
E --> F{判断type}
F -->|决策| G1[projects/xxx/decisions/]
F -->|文档| G2[projects/xxx/docs/]
F -->|聊天| G3[projects/xxx/conversations/]
G1 --> H[Git commit + push]
G2 --> H
G3 --> H
H --> I[NAS Git仓库<br/>真相源]
style C2 fill:#90EE90
style D fill:#FFB6C1
style E fill:#87CEEB
style I fill:#FFD700
```
## 3.2 数据存储拓扑
```mermaid
graph LR
subgraph NAS
A[Gitea Git仓库<br/>真相源]
B[投递区]
C[定时任务]
end
subgraph Mac
D[工作副本]
E[Obsidian]
F[Claude Code]
end
subgraph 飞书
G[飞书文档]
H[多维表格展示]
end
G -->|feishu-sync.py| I[临时目录]
I -->|kb-bridge.py| A
B -->|nas-watch.py| A
A <-->|git pull/push| D
D --> E
D --> F
A -->|feishu-render.py| H
style A fill:#FFD700
style I fill:#FFB6C1
```
## 3.3 日常工作流时序
```mermaid
sequenceDiagram
participant 飞书
participant 定时任务
participant feishu_sync
participant kb_bridge
participant Claude
participant Git仓库
Note over 飞书,定时任务: 每天10:00
定时任务->>feishu_sync: 触发
feishu_sync->>飞书: 扫描新文档
飞书-->>feishu_sync: 返回docx
feishu_sync->>feishu_sync: 转markdown+下图
feishu_sync->>kb_bridge: 传markdown
kb_bridge->>Claude: 分析+判断type
Claude-->>kb_bridge: type/tags/要点
kb_bridge->>kb_bridge: 生成完整frontmatter
kb_bridge->>Git仓库: commit+push
```
## 3.4 三个关键位置
```
位置1真相源NAS Git仓库
http://192.168.0.101:3002/robert/company-kb
作用:唯一权威版本,所有人 pull/push
位置2工作区你的Mac
~/work/company-kb/
作用Obsidian编辑、Claude问答、Git提交
位置3投递区NAS共享文件夹
SynologyDrive-zhishiku/知识库投递区/
作用:同事丢文件,监控脚本自动采集
```
---
# 第四部分:分阶段实施
## 阶段 0环境准备半天
### 0.1 NAS 建 Git 仓库
```bash
# NAS 上SSH/DSM终端
cd /volume1/git/repos/robert/
git init --bare company-kb.git
# 本地 clone
cd ~/work/
git clone ssh://用户名@192.168.0.101/volume1/git/repos/robert/company-kb.git
cd company-kb/
# 推送现有知识库
cp -r "现有知识库路径"/* .
git add . && git commit -m "Initial commit" && git push
```
### 0.2 整合飞书工具
```bash
cd ~/work/company-kb/
cp "飞书同步工具包/feishu-sync.py" tools/
cp "飞书同步工具包/run-sync.sh" tools/
git add tools/ && git commit -m "Add feishu-sync" && git push
```
## 阶段 1核心对接1-2天P0
### 1.1 创建 kb-bridge.py
**作用**:把 feishu-sync.py 拉下来的 markdown用 AI 分析后转成合规知识页。
**核心逻辑**
```python
# 伪代码
for md_file in feishu输出目录:
content = 读取(md_file)
分析 = Claude_API分析(content) # 判断type、提取tags、写description
frontmatter = 生成完整frontmatter(分析, 契约)
保存到 projects/<代号>/{分析.type对应目录}/
git commit + push
```
**安装**
```bash
pip3 install anthropic pyyaml
export ANTHROPIC_API_KEY="你的Key"
cp kb-bridge.py tools/
```
### 1.2 测试完整流程
```bash
# 步骤1拉飞书到临时目录
python3 tools/feishu-sync.py --folder ROOT --out /tmp/test --skip-big
# 步骤2桥接处理到知识库
python3 tools/kb-bridge.py --input /tmp/test --project wanniu-l1
# 步骤3校验
python3 tools/kb-lint-fm.py projects/wanniu-l1/
# 期望:错误 0 | 警告 0
# 步骤4推送
git add . && git commit -m "Test ingest" && git push
```
### 1.3 创建统一定时脚本 auto-sync.sh
```bash
#!/bin/bash
# 1. 拉飞书 → 临时目录
python3 tools/feishu-sync.py --folder ROOT --out /tmp/sync-$(date +%s) --skip-big
# 2. 桥接 → 知识库
python3 tools/kb-bridge.py --input /tmp/sync-* --project auto
# 3. 清理临时目录
rm -rf /tmp/sync-*
```
### 1.4 配置定时任务
```bash
# NAS cron 或 Mac crontab
0 10 * * * cd ~/work/company-kb && git pull && bash tools/auto-sync.sh
```
## 阶段 2验证1天P0
- [ ] 手动跑 `bash tools/auto-sync.sh`,看日志
- [ ] 检查 projects/ 下是否有新知识页
- [ ] `python3 tools/kb-lint-fm.py` 全部通过
- [ ] Claude Code 里 `/kb-ask` 测试问答
## 阶段 3锦上添花P1可选
- **nas-watch.py**监控投递区文件一丢就处理1天
- **feishu-render.py**推送知识库到飞书多维表格2天
- **pre-commit hook**Git提交前自动校验半天
---
# 第五部分:日常使用
## 常用命令
```bash
# 创建新项目
bash tools/kb-init.sh <项目代号>
# 手动录入
/kb-ingest <项目代号> <飞书链接/文件>
# 查询知识
/kb-ask <问题>
# 校验知识库
python3 tools/kb-lint-fm.py # 或 /kb-check
# 同步代码
git pull # 拉最新
git push # 推更新
# 查看统计
/kb-status
```
## 五种使用场景
| 场景 | 操作 |
|------|------|
| **查信息** | `/kb-ask 万牛会L1怎么安排的` → AI答案+溯源 |
| **手动录入** | `/kb-ingest wanniu-l1 <飞书链接>` |
| **看自动同步** | `tail -100 sync.log` |
| **编辑知识** | Obsidian打开 → 改 → git push |
| **健康检查** | `/kb-check` → 报告缺字段/死链 |
---
# 第六部分关键决策与FAQ
## 改造前 vs 改造后
**改造前(纯手工)**打开飞书→复制→建md→手写12字段frontmatter→粘贴→commit每篇5-10分钟累且易错。
**改造后(自动化)**每天10:00全自动采集+生成+提交,你只需 `git pull``/kb-ask`每天0分钟。
## FAQ
**Q: API 会不会很贵?**
Sonnet约$3/百万token每天10篇×2000字≈$0.12/天,月约$3.6,可接受。
**Q: 要不要上向量库/RAG**
现阶段不用。文档明确 P5~100源/几百页且定位不准才考虑Claude语义检索够用。
**Q: 定时任务失败怎么办?**
`sync.log`。常见:飞书登录过期(重新 `lark-cli auth login`、API Key失效、Git冲突手动pull
**Q: NAS和本地怎么同步**
用 Git pull/push可靠不要用群晖Drive自动同步易冲突
---
# 快速启动清单3天跑通
## Day 1环境搭建
- [ ] NAS 建 Git 仓库
- [ ] 本地 clone 到 ~/work/company-kb/
- [ ] 推送知识库内容
- [ ] 整合 feishu-sync.py 到 tools/
## Day 2核心对接
- [ ] 安装 kb-bridge.py + 配 API Key
- [ ] 测试feishu-sync → kb-bridge → lint → push
- [ ] 创建 auto-sync.sh
## Day 3定时与验证
- [ ] 手动跑 auto-sync.sh
- [ ] 配置定时任务
- [ ] /kb-ask 测试问答
- [ ] 记录问题准备优化
---
**核心结论**你已完成最难的70%(飞书采集),只需补 kb-bridge.py1-2天就能打通全流程。建议先走「快速启动3天」验证价值再决定是否补 P1 自动化。