init: 数字员工平台初始代码

包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。
- 工作台画布:节点拖放、连线模式、右键菜单、AI 助手
- 后端:连接器 API、专员种子数据
- 导航:左侧导航、工坊、市场、控制台
This commit is contained in:
eaiadmin
2026-08-18 20:19:58 +08:00
commit 4e8817d768
239 changed files with 48631 additions and 0 deletions
@@ -0,0 +1,226 @@
# BE05 — 知识入库体系总设计(Knowledge Ingest)
> **版本:V1.1 | 定稿**
> **定位**:把「素材入库」与「结构化知识入库」统一为一套可复用的知识入库体系,覆盖上传 → 生成 → 审批 → 入库全链路。
> **配套**:BE03(素材转换管线)、BE04(AI 检索)、`docs/knowledge_source/README.md`(知识源格式契约)。
---
## 1. 模块职责
知识入库体系负责把两类知识源,统一经过「审批前置」流入三张消费表,最终支撑「可浏览 / 可考试 / 可 AI 检索」。
| 知识源类型 | 载体 | 审批单元 | 流入表 |
|-----------|------|---------|--------|
| **非结构化素材** | PPT/PDF/Word/视频/图片 | 逐文件 | knowledge_chunk(+ 预览) |
| **结构化知识** | 知识源 md(docs/knowledge_source/) | 逐文档 | product / question / knowledge_chunk |
**核心原则(与 P02/P04 对齐)**:所有知识只有 `approved` 才生效;`pending` / `rejected` 一律不解析、不进库、前台不可见。
---
## 2. 完整目录结构(定稿)
```
eaisalestrain_app/
├── docs/
│ └── knowledge_source/ # 知识源文档(权威源,纳入 git 版本管理)
│ ├── README.md # 格式契约 + 答案契约 + 审批状态机
│ ├── 01_通用规则.md
│ ├── 02_资本咨询类.md
│ ├── 03_资质认定辅导类.md
│ ├── 04_AI咨询与实施类.md
│ └── 05_企业级AI工具与平台.md
│
├── backend/
│ ├── app/
│ │ ├── api/
│ │ │ ├── media.py # 【已有】素材上传/审批/预览
│ │ │ └── knowledge.py # 【新增】知识源扫描/审批/摄入
│ │ ├── models/
│ │ │ ├── media_file.py # 【已有】素材表
│ │ │ ├── knowledge_chunk.py # 【改造】来源扩展(见 §4)
│ │ │ └── knowledge_source.py # 【新增】知识源文档表
│ │ ├── services/
│ │ │ ├── media_service.py # 【已有】LibreOffice/PyMuPDF 转换管线
│ │ │ └── knowledge_service.py # 【新增】md 解析 + 摄入
│ │ └── scripts/
│ │ ├── init_db.py # 【已有】建表 + 种子
│ │ └── ingest_knowledge.py # 【新增】知识源扫描/摄入脚本
│ └── data/
│ ├── media/ # 【已有】素材物理文件(git 忽略)
│ │ ├── upload/
│ │ └── _preview_cache/
│ └── logs/ # 【已有】special_trace 按天日志
│
└── docker-compose.yml # 【待创建】
```
**约定**:
- `docs/knowledge_source/` 是知识源 md 的唯一入库口(权威源,git 管理,可 diff 可回滚)。
- 运行时摄入**直接读取**该目录,不复制到 backend/data(单一事实源,避免双份漂移)。
- 素材物理文件仍在 `backend/data/media/`(git 忽略)。
---
## 3. 两条流程的状态机(定稿)
### 流程 A:非结构化素材(BE03 已实现)
```
员工上传 ──▶ media_file(pending) ──审批──▶ approved ──▶ 异步转换
管理员上传 ──▶ media_file(approved) ──▶ 立即异步转换 │
▼
┌──────────────────────────┐
│ 文档: LibreOffice→PDF→ │
│ PyMuPDF 提取→切片 │
│ 视频/图片: 仅预览 │
└──────────┬───────────────┘
▼
knowledge_chunk
```
### 流程 B:结构化知识源(本次新增)
```
知识源 md(docs/knowledge_source/)
│
▼
【扫描】ingest_knowledge.py / POST /api/knowledge/scan
│ 解析 front-matter → 为每个 md 建 knowledge_source 记录
▼
knowledge_source(pending) ──审批──▶ approved ──▶ 解析摄入
│ │
│ ├─→ product(status=active)
│ ├─→ question(status=active)
│ └─→ knowledge_chunk(source=knowledge_source)
│
└─驳回──▶ rejected(理由必填,不生效)
```
**对称性**:素材「审批通过→转换提取」,知识源「审批通过→解析摄入」。两条通道最终都汇入 `knowledge_chunk` 供 AI 检索。
---
## 4. 数据模型变更
### 4.1 新增 `knowledge_source` 表
```sql
CREATE TABLE knowledge_source (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(256) NOT NULL COMMENT '文档标题',
file_path VARCHAR(512) NOT NULL UNIQUE COMMENT 'md 相对路径(docs/knowledge_source/ 下)',
category VARCHAR(64) NOT NULL COMMENT '分类:general/capital_consulting/qualification_counseling/ai_consulting/ai_tools_platform',
domain ENUM('company','product','sales') NOT NULL DEFAULT 'product',
source_version VARCHAR(32) NOT NULL COMMENT '源版本,如 V1.0',
audit_status ENUM('pending','approved','rejected') NOT NULL DEFAULT 'pending',
audit_by BIGINT UNSIGNED DEFAULT NULL COMMENT '审批人ID',
audit_at DATETIME(6) DEFAULT NULL COMMENT '审批时间',
reject_reason VARCHAR(512) DEFAULT NULL COMMENT '驳回理由',
ingested TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已摄入',
created_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
INDEX idx_ks_status (audit_status),
INDEX idx_ks_category (category),
CONSTRAINT fk_ks_auditor FOREIGN KEY (audit_by) REFERENCES user(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```
### 4.2 改造 `knowledge_chunk` 表(来源扩展)
现状 `media_file_id BIGINT UNSIGNED NOT NULL` 强绑定素材。改造后:
```sql
media_file_id BIGINT UNSIGNED DEFAULT NULL, -- 由 NOT NULL 改为可空
knowledge_source_id BIGINT UNSIGNED DEFAULT NULL, -- 新增
-- 约束:media_file_id 与 knowledge_source_id 二者必居其一(应用层校验,Fail Fast)
```
对应 ORM `models/knowledge_chunk.py`:`media_file_id` 改为可空,新增 `knowledge_source_id` 外键与 relationship。
---
## 5. 模块清单(8 模块)
| # | 模块 | 状态 | 职责 | 落点 |
|---|------|------|------|------|
| 1 | 上传模块 | ✅ 已有 | 直传 + 分片(>100MB) | `api/media.py` |
| 2 | 转换模块 | ✅ 已有 | LibreOffice → PDF | `services/media_service.py` |
| 3 | 提取切片模块 | ✅ 已有 | PyMuPDF 提取 + 段落切片 | `services/media_service.py` |
| 4 | **摄入模块** | 🆕 新增 | 解析知识源 md → product/question/chunk | `services/knowledge_service.py` |
| 5 | 审批模块 | 🔧 扩展 | 素材审批(已有)+ 知识源审批(新增) | `api/media.py` + `api/knowledge.py` |
| 6 | 入库模块 | 🔧 扩展 | 写 product/question/knowledge_chunk | `services/knowledge_service.py` |
| 7 | 预览模块 | ✅ 已有 | approved 素材预览 | `api/media.py` |
| 8 | 检索模块 | ✅ 已有 | MySQL FULLTEXT 召回 | `services/ai_service.py` |
---
## 6. 新增 API(knowledge.py)
```python
router = APIRouter(prefix="/api/knowledge", tags=["knowledge"])
# ── 扫描(管理员)──
POST /api/knowledge/scan
# 扫描 docs/knowledge_source/*.md
# 为新增/变更的 md 建 knowledge_source 记录(status=pending)
# 已存在且未摄入的记录跳过;返回扫描结果列表
# ── 审批(管理员)──
GET /api/knowledge/audit-list?status=pending
POST /api/knowledge/audit/{source_id}
# { "action": "approve" | "reject", "reject_reason": "..." }
# approve → 触发摄入(同步解析写入 product/question/chunk,标记 ingested=1)
# reject → 必填 reject_reason,不摄入
# ── 状态查询 ──
GET /api/knowledge/status/{source_id}
# { "audit_status", "ingested", "reject_reason" }
```
---
## 7. 摄入脚本(ingest_knowledge.py)
```python
"""知识源摄入脚本:扫描 md → 建记录 → (审批通过后)解析入库
用法:
cd backend && source venv/bin/activate
python -m app.scripts.ingest_knowledge --scan # 仅扫描建 pending 记录
python -m app.scripts.ingest_knowledge --ingest <source_id> # 摄入单个已审批源
"""
# 解析规则(严格对齐 knowledge_source/README.md 格式契约):
# 1. 读 YAML front-matter:category / domain / source_version
# 2. 按 "## " 二级标题切块:
# ## 结构化产品数据 → 解析 "### code name" + "key: value" 列表 → product
# ## AI 检索知识 → 每个 "### 标题" 段落 → knowledge_chunk
# ## 考试题目 → 每个 "### Qn" 的字段列表 → question
# 3. 摄入后 product/question 置 status=active
# 4. knowledge_chunk 写入 knowledge_source_id,media_file_id 留空
```
**Fail Fast 约束(G02)**:解析失败(front-matter 缺失、section 缺块、字段缺失)立即抛错并中断该源摄入,不静默跳过、不写半截数据。
---
## 8. 格式契约(已定,见 knowledge_source/README.md)
- 每个 md:YAML front-matter(`category` / `domain` / `source_version`)+ 三个固定 `## ` section。
- 题目答案契约:`judge=[bool]`、`single=[索引]`、`multiple=[索引列表]`。
- 已生成 5 个知识源:`01_通用规则` + `02/03/04/05` 四大分类,共 28 产品 + 20 题。
---
## 9. 实现清单(代码侧,已完成)
- [x] `models/knowledge_source.py` 新建
- [x] `models/knowledge_chunk.py` 来源扩展(media_file_id 可空 + knowledge_source_id)
- [x] `services/knowledge_service.py`(md 解析 + 摄入)
- [x] `api/knowledge.py`(scan / audit / status)
- [x] `scripts/ingest_knowledge.py`
- [x] `scripts/init_db.py` 追加 knowledge_source 建表 + knowledge_chunk 字段迁移
- [x] `docs/db_schema.md`、`docs/api.md` 同步更新