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:
0
.claude/commands/.gitkeep
Executable file
0
.claude/commands/.gitkeep
Executable file
15
.claude/commands/kb-ask.md
Executable file
15
.claude/commands/kb-ask.md
Executable 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
18
.claude/commands/kb-check.md
Executable 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
24
.claude/commands/kb-ingest.md
Executable 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
27
.claude/commands/kb-link.md
Executable 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
14
.claude/commands/kb-new.md
Executable 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
17
.claude/commands/kb-save.md
Executable 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
16
.claude/commands/kb-status.md
Executable 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
34
.gitignore
vendored
Executable 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
75
AI-CONTEXT.md
Executable 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/wiki(gitignore,P4 才启用)。
|
||||||
|
- **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/ 预留 type(reserved,激活后才用)
|
||||||
|
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.py,exit0=健康,需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
88
CLAUDE.md
Executable 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/(那些 gitignore,P4 才启用)。
|
||||||
|
- **采集三通道**:飞书群/文档、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+review(docs/12),勿随意覆盖。
|
||||||
136
CONVENTIONS.md
Executable file
136
CONVENTIONS.md
Executable 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 type(Day-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
120
README.md
Executable 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 Code(CC)据此做**可溯源问答**——每条结论都挂原始链接,无据即报 Gap,绝不编造。
|
||||||
|
|
||||||
|
MVP 反过度工程:先做「可溯源 markdown + CC 检索」,编译层(P4)、向量库(P5)按信号延后,默认不上。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 怎么用
|
||||||
|
|
||||||
|
### 1. cd 进来问 AI(MVP 默认方式)
|
||||||
|
|
||||||
|
```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/` 已 gitignore,Git 同步走命令行/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
40
company/_index.md
Executable 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
0
concepts/.gitkeep
Executable file
43
docs/01-方案总览.md
Executable file
43
docs/01-方案总览.md
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
# 公司知识库 · 方案文档
|
||||||
|
|
||||||
|
> 公司内部 AI 自动化 · 第一件事
|
||||||
|
> 状态:方案阶段 | 最后更新:2026-06-24
|
||||||
|
|
||||||
|
## 1. 定位
|
||||||
|
|
||||||
|
知识库是公司的**信息基石**,采用分层架构。第一件事只做底下两层——把分散信息可靠汇聚、留存、可溯源;上层的信息处理(问答/摘要/喂智能体)属于第二件事,本阶段只为其预留干净接口,不锁死设计。
|
||||||
|
|
||||||
|
## 2. 分层架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ 4 呈现消费层 飞书为主(也可 CC / 网页 / 机器人) │ ← 以后
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ 3 处理层(多层) 清洗 / 结构化 / 摘要 / 问答 / 路由 │ ← 留接口,暂不做
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ 2 原始汇聚层 统一格式 + 元数据 + 可溯源 = 本体 │ ← 第一件事
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ 1 采集层 飞书/钉钉/企微/微信/NAS/本地文件 │ ← 第一件事
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 团队与约束
|
||||||
|
|
||||||
|
- 5 人微团队,全员用 Claude Code(CC);前期技术主导,后续同事维护,录入全员参与。
|
||||||
|
- 可用云端 API。呈现与沟通尽量以飞书为主。
|
||||||
|
- 微信内容后续切到飞书,再统一汇入。
|
||||||
|
|
||||||
|
## 4. 关键技术决策
|
||||||
|
|
||||||
|
| 决策 | 选择 | 理由 |
|
||||||
|
|------|------|------|
|
||||||
|
| 本体载体 | Git + 结构化 Markdown | CC 原生可读,零成本检索,天然做下游底座 |
|
||||||
|
| 检索方式 | 初期目录 + CC 语义检索 | 文档量小,先不上向量库,量大再加 |
|
||||||
|
| 平台绑定 | 接入多源,不锁死 | 呈现偏飞书,但本体独立可迁移 |
|
||||||
|
| 录入方式 | 混合 | 重要内容人工沉淀,高频流动(聊天)自动拉取 |
|
||||||
|
|
||||||
|
## 5. 起步策略
|
||||||
|
|
||||||
|
单点跑通:首个闭环 = **项目资料**。选一个在跑的项目,把其文档/聊天/决策汇成一处,验证闭环后再复制到其他项目与场景。
|
||||||
|
|
||||||
|
详见 [02-首个闭环-项目资料.md](02-首个闭环-项目资料.md)。
|
||||||
61
docs/02-首个闭环-项目资料.md
Executable file
61
docs/02-首个闭环-项目资料.md
Executable 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 自动同步。
|
||||||
116
docs/03-知识库框架选型调研.md
Executable file
116
docs/03-知识库框架选型调研.md
Executable 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 OKF(Open 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. 卡帕西方法论的两套参考实现
|
||||||
|
|
||||||
|
### 实现 A:llm-wiki SKILL(本机已激活,pkm-suite)
|
||||||
|
- raw/→wiki/ 增量编译,10 步摄入流程,Delta Check(hash 去重)。
|
||||||
|
- Frontmatter Schema:通用字段 type/title/created/updated/tags/status/related/sources + 类型特有字段(source/entity/concept/comparison/question)。
|
||||||
|
- 三级查询(Quick/Standard/Deep)、Hot Cache(hot.md ~500 词)、8 项 Lint 健康检查、矛盾检测。
|
||||||
|
- 融合 claude-obsidian 精华。**这是我们手里现成的卡帕西实现**。
|
||||||
|
|
||||||
|
### 实现 B:Claude Code + Obsidian 社区实现
|
||||||
|
- 用 Claude Code 把原始源编译进 Obsidian vault,sources/entities/concepts/comparisons 分目录。
|
||||||
|
- 与实现 A 同源同构,验证了"CC 做编译器 + Markdown vault 做载体"是社区主流落地路径。
|
||||||
|
|
||||||
|
## 3. Google OKF(Open Knowledge Format)v0.1
|
||||||
|
|
||||||
|
**定位**:把"LLM wiki"这个涌现模式,标准化成厂商中立、可移植的格式。作者 Sam McVeety / Amir Hormati(Google Cloud),451 行规格,"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 11ty(6 字段细节)](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
181
docs/04-架构.md
Executable 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 处理层 Processing(MVP 极薄,按信号生长) │
|
||||||
|
│ 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 路径 / URL),AI 读一遍→提炼精华写成一页知识→页面留 `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: entity(reserved,首次真实需要时建)
|
||||||
|
├── concepts/ # type: concept(reserved)
|
||||||
|
├── questions/ # type: question(reserved,查询回写)
|
||||||
|
│
|
||||||
|
├── 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
122
docs/05-路线图.md
Executable 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
78
docs/06-工具链.md
Executable 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
113
docs/07-AI-native工作流.md
Executable 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)
|
||||||
|
```
|
||||||
119
docs/08-飞书双向前端方案.md
Executable file
119
docs/08-飞书双向前端方案.md
Executable 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/...`(通用 api,CLI 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 有 idempotency,handle 记已处理 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
75
docs/09-同事使用指南.md
Executable 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:我丢错了/丢重复了怎么办?**
|
||||||
|
A:AI 会自动去重(同一份不会进两次);丢错了跟 AI 说一声,删改由 AI 提请、人确认,不会误删。
|
||||||
|
|
||||||
|
**Q:机器人答得不对怎么办?**
|
||||||
|
A:点它给的 `source_link` 看原文。若是知识库里资料本身过时,@机器人 说一声或补一份新的进去。
|
||||||
|
|
||||||
|
**Q:我要改一条已有的知识?**
|
||||||
|
A:知识不在飞书上直接改(飞书是「读屏」)。用 Obsidian 打开仓库改,或跟 AI 说要改哪条。
|
||||||
|
|
||||||
|
**Q:涉及我个人的草稿、没定稿的东西?**
|
||||||
|
A:放你自己的本地目录,别丢进投递区。**定稿了再丢**——进了库就是全员可见的公司知识。
|
||||||
126
docs/10-部署与运维.md
Executable file
126
docs/10-部署与运维.md
Executable 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 → 演进)
|
||||||
|
- MVP:5 人皆可直接 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)不受影响,仅机器人/投影暂停。
|
||||||
80
docs/11-信息全景与非项目结构.md
Executable file
80
docs/11-信息全景与非项目结构.md
Executable 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(一律读契约)。这也是「先少后多」策略的落地:先激活真痛的,其余登记在册、按需激活。
|
||||||
82
docs/12-治理与规则维护.md
Executable file
82
docs/12-治理与规则维护.md
Executable 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
47
docs/obsidian-setup.md
Executable 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
0
entities/.gitkeep
Executable file
36
learning/_index.md
Executable file
36
learning/_index.md
Executable 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
0
learning/ai/.gitkeep
Executable file
31
learning/ai/karpathy-llm-wiki.md
Executable file
31
learning/ai/karpathy-llm-wiki.md
Executable 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。查询时对**已编译知识**推理。
|
||||||
|
- 对比传统 RAG:RAG 每次查询从零检索、不沉淀(「健忘症」);LLM Wiki 知识累积复利。
|
||||||
|
- 规模红线:纯 wiki 模式在 ~100 源/几百页后失效(index 不可导航),届时用向量库做导航层(混合)。
|
||||||
|
|
||||||
|
## 我们的应用
|
||||||
|
|
||||||
|
本知识库整体就是这套方法论的落地——单一 Git 真相源 + OKF frontmatter + 先不上向量库。详见 [[知识库框架选型调研]]。
|
||||||
|
|
||||||
|
## 溯源
|
||||||
|
|
||||||
|
完整调研见 `source_link`(`docs/03`),及其中列出的一手来源。
|
||||||
0
learning/industry/.gitkeep
Executable file
0
learning/industry/.gitkeep
Executable file
0
learning/management/.gitkeep
Executable file
0
learning/management/.gitkeep
Executable file
4
meta/ingest-manifest.json
Executable file
4
meta/ingest-manifest.json
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"_comment": "摄入 hash 台账:去重用。P2+ 自动化后由单一执行者维护。key=内容 sha256, value=目标页相对路径。",
|
||||||
|
"entries": {}
|
||||||
|
}
|
||||||
101
meta/kb-contract.yaml
Executable file
101
meta/kb-contract.yaml
Executable 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 type(Day-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
14
meta/stats.md
Executable 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
36
projects/_example/_index.md
Executable 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 目录)
|
||||||
0
projects/_example/assets/.gitkeep
Executable file
0
projects/_example/assets/.gitkeep
Executable file
31
projects/_example/conversations/conversation-kickoff-20260618.md
Executable file
31
projects/_example/conversations/conversation-kickoff-20260618.md
Executable 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`。
|
||||||
33
projects/_example/decisions/decision-clientA-tiered-pricing.md
Executable file
33
projects/_example/decisions/decision-clientA-tiered-pricing.md
Executable 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`(飞书群消息锚点)。
|
||||||
0
projects/_example/docs/.gitkeep
Executable file
0
projects/_example/docs/.gitkeep
Executable file
29
projects/_example/docs/doc-clientA-phase1-requirements.md
Executable file
29
projects/_example/docs/doc-clientA-phase1-requirements.md
Executable 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%
|
||||||
|
|
||||||
|
## 溯源
|
||||||
|
以飞书原始文档为准,本页为结构化镜像。
|
||||||
43
projects/wanniu-l1/_index.md
Normal file
43
projects/wanniu-l1/_index.md
Normal 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/` :课件、录屏等附件
|
||||||
|
|
||||||
0
projects/wanniu-l1/assets/.gitkeep
Normal file
0
projects/wanniu-l1/assets/.gitkeep
Normal file
45
projects/wanniu-l1/decisions/小班制20人.md
Normal file
45
projects/wanniu-l1/decisions/小班制20人.md
Normal 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个独立教室
|
||||||
|
|
||||||
0
projects/wanniu-l1/docs/.gitkeep
Normal file
0
projects/wanniu-l1/docs/.gitkeep
Normal file
67
projects/wanniu-l1/docs/课程大纲v1.md
Normal file
67
projects/wanniu-l1/docs/课程大纲v1.md
Normal 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)
|
||||||
|
- 实战:构建知识库采集 Workflow(1.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
0
questions/.gitkeep
Executable file
131
tools/feishu-pull.py
Executable file
131
tools/feishu-pull.py
Executable 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 自动补齐 frontmatter:title(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
114
tools/feishu-render.py
Executable 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
576
tools/feishu-sync.py
Executable 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-cli,PATH 找不到就试常见位置。"""
|
||||||
|
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,返回解析后的 JSON(binary_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 = [
|
||||||
|
# 形态1:markdown 图片 ;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>',
|
||||||
|
# 形态3:HTML <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"" if got else None
|
||||||
|
else:
|
||||||
|
rep = f""
|
||||||
|
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)
|
||||||
|
# 形态B:HTML <img ... src/href="stream" ...>(含表格 <td> 里的)
|
||||||
|
new_md = re.sub(
|
||||||
|
r'<img\b[^>]*\b(?:src|href)="https://' + H + r'/[^"]*"[^>]*/?>',
|
||||||
|
NOTE, new_md)
|
||||||
|
# 形态C:markdown 。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
43
tools/kb-bot/README.md
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
# kb-bot —— 飞书群知识库问答机器人
|
||||||
|
|
||||||
|
> 群里 @机器人 提问 → 查知识库 → 带溯源回复。**长连接,无需公网服务器。**
|
||||||
|
> 底座:lark-cli(bot 身份自管凭据,不入库)。详见 `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
120
tools/kb-bot/handle.py
Executable 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
24
tools/kb-bot/send.sh
Executable 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
35
tools/kb-bot/subscribe.sh
Executable 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_v1;Ctrl-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
80
tools/kb-init.sh
Executable 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
178
tools/kb-lint-fm.py
Executable 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
|
||||||
|
# 目录型 glob(docs/**)也匹配 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}: 建议补 title(AI 可自动=H1 或文件名)")
|
||||||
|
warnings += 1
|
||||||
|
|
||||||
|
print(f"\n检查 {checked} 页 | 错误 {errors} | 警告 {warnings}")
|
||||||
|
sys.exit(1 if errors else 0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
353
完整项目现状报告.md
Normal file
353
完整项目现状报告.md
Normal 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个type(4个激活) |
|
||||||
|
|
||||||
|
### 部分实现(骨架/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行) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 核心缺失环节(阻碍完整落地的)
|
||||||
|
|
||||||
|
### 缺失1:Git 真相源未搭建
|
||||||
|
|
||||||
|
**现状**:知识库只是本地文件夹(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` 录入(低效)
|
||||||
|
|
||||||
|
### 缺失3:bot-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/
|
||||||
|
|
||||||
|
### 阶段2:bot-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
419
落地方案.md
Normal 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.py(1-2天)就能打通全流程。建议先走「快速启动3天」验证价值,再决定是否补 P1 自动化。
|
||||||
Reference in New Issue
Block a user