chore: 工作台产品化进行中的改动
把工作区里其余在制品一并入库,主要是工作台产品化的推进:
后端:新增 capability_definition / project / my_app_center / office_skill
接口与 action_definition / skill_definition / project / user_app_center
模型,config 加路由健康上报。
前端:新增 frontend/src/skills(Office 技能与 workbuddy 复刻)、
项目管理、应用中心、能力目录页,以及配套 api / store / config;
聊天侧新增 SpecialistChip / SpecialistPanel / SkillStrip / AppChatRail
等组件。
清理:移除旧 views/tools 下的单页工具(已并入工作台)、_frozen 冻结组件、
cmd/inspect_oa_debug 调试入口,以及两份调试笔记。
其它:文档与启动脚本同步。
(这批改动与上一提交的 SY23 工作并行进行,此前已在同一工作区内交织。)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# BE05 — 知识入库体系总设计(Knowledge Ingest)
|
||||
|
||||
> **版本:V1.1 | 定稿**
|
||||
> **定位**:把「素材入库」与「结构化知识入库」统一为一套可复用的知识入库体系,覆盖上传 → 生成 → 审批 → 入库全链路。
|
||||
> **配套**:BE03(素材转换管线)、BE04(AI 检索)、`docs/knowledge_source/README.md`(知识源格式契约)。
|
||||
> **版本:V1.3 | 当前实现规范**
|
||||
> **技术栈**:Go 后端 + SQLite + Vue3。
|
||||
> **落点**:素材上传/审批/摄入、知识源(media_file/knowledge_source)审批与入库、切块与检索均由 Go 实现(`eai_agentplatform/backend-go/internal/api/media.go`、`knowledge.go`、`knowledge_pipeline.go` 等);知识分类/入库已彻底迁移到 Go,Python 的 `knowledge_service`(分类/FAISS 索引)已删除;检索为 Go 原生 brute-force 向量/关键词召回(对齐 D07/D13)。
|
||||
|
||||
---
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
---
|
||||
|
||||
## 2. 完整目录结构(定稿)
|
||||
## 2. 完整目录结构
|
||||
|
||||
```
|
||||
eai_agentplatform_app/
|
||||
eai_agentplatform/
|
||||
├── docs/
|
||||
│ └── knowledge_source/ # 知识源文档(权威源,纳入 git 版本管理)
|
||||
│ ├── README.md # 格式契约 + 答案契约 + 审批状态机
|
||||
@@ -32,40 +32,41 @@ eai_agentplatform_app/
|
||||
│ ├── 04_AI咨询与实施类.md
|
||||
│ └── 05_企业级AI工具与平台.md
|
||||
│
|
||||
├── backend/
|
||||
│ ├── app/
|
||||
├── backend-go/
|
||||
│ ├── config/ai_config.json # AI 路由配置(含 embed_gen/llm_fallback)
|
||||
│ ├── internal/
|
||||
│ │ ├── 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 # 【新增】知识源扫描/摄入脚本
|
||||
│ │ │ ├── media.go # 素材上传/审批/预览/提取管线
|
||||
│ │ │ ├── knowledge.go # 知识源扫描/审批/摄入/解析
|
||||
│ │ │ ├── knowledge_pipeline.go # 知识检索/分类/问答管线
|
||||
│ │ │ └── knowledge_index.go # 知识索引重建(Go 原生)
|
||||
│ │ ├── model/
|
||||
│ │ │ ├── media_file.go # 素材表
|
||||
│ │ │ ├── knowledge_source.go # 知识源文档表
|
||||
│ │ │ ├── knowledge_chunk.go # 知识块表
|
||||
│ │ │ └── question.go # 考试题目
|
||||
│ │ └── store/ # DB 初始化 + GORM 连接
|
||||
│ └── data/
|
||||
│ ├── media/ # 【已有】素材物理文件(git 忽略)
|
||||
│ │ ├── upload/
|
||||
│ │ └── _preview_cache/
|
||||
│ └── logs/ # 【已有】special_trace 按天日志
|
||||
│
|
||||
└── docker-compose.yml # 【待创建】
|
||||
│ ├── eai_agentplatform.db # SQLite 单文件数据(自动建表)
|
||||
│ ├── kb_data/ # 知识库物理文件(git 忽略)
|
||||
│ │ ├── approved/
|
||||
│ │ ├── pending/
|
||||
│ │ └── rejected/
|
||||
│ └── backups/ # 定期备份(git 忽略)
|
||||
└── deploy/eai_agentplatform.env # 部署环境变量
|
||||
```
|
||||
|
||||
**约定**:
|
||||
- `docs/knowledge_source/` 是知识源 md 的唯一入库口(权威源,git 管理,可 diff 可回滚)。
|
||||
- 运行时摄入**直接读取**该目录,不复制到 backend/data(单一事实源,避免双份漂移)。
|
||||
- 素材物理文件仍在 `backend/data/media/`(git 忽略)。
|
||||
- 运行时摄入**直接读取**该目录,不复制(单一事实源,避免双份漂移)。
|
||||
- 素材物理文件存储在 `data/kb_data/`(git 忽略),分为 `approved/pending/rejected` 三个子目录。
|
||||
- 数据库为 SQLite 单文件(`data/eai_agentplatform.db`),GORM 首次启动自动建表。
|
||||
|
||||
---
|
||||
|
||||
## 3. 两条流程的状态机(定稿)
|
||||
## 3. 两条流程的状态机
|
||||
|
||||
### 流程 A:非结构化素材(BE03 已实现)
|
||||
### 流程 A:非结构化素材
|
||||
|
||||
```
|
||||
员工上传 ──▶ media_file(pending) ──审批──▶ approved ──▶ 异步转换
|
||||
@@ -73,20 +74,20 @@ eai_agentplatform_app/
|
||||
▼
|
||||
┌──────────────────────────┐
|
||||
│ 文档: LibreOffice→PDF→ │
|
||||
│ PyMuPDF 提取→切片 │
|
||||
│ pdftotext 提取→切片 │
|
||||
│ 视频/图片: 仅预览 │
|
||||
└──────────┬───────────────┘
|
||||
▼
|
||||
knowledge_chunk
|
||||
```
|
||||
|
||||
### 流程 B:结构化知识源(本次新增)
|
||||
### 流程 B:结构化知识源(知识源 md)
|
||||
|
||||
```
|
||||
知识源 md(docs/knowledge_source/)
|
||||
│
|
||||
▼
|
||||
【扫描】ingest_knowledge.py / POST /api/knowledge/scan
|
||||
【扫描】POST /api/knowledge/scan
|
||||
│ 解析 front-matter → 为每个 md 建 knowledge_source 记录
|
||||
▼
|
||||
knowledge_source(pending) ──审批──▶ approved ──▶ 解析摄入
|
||||
@@ -102,125 +103,262 @@ knowledge_source(pending) ──审批──▶ approved ──▶ 解析摄入
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据模型变更
|
||||
## 4. 数据模型
|
||||
|
||||
### 4.1 新增 `knowledge_source` 表
|
||||
### 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;
|
||||
```
|
||||
table: knowledge_source
|
||||
columns:
|
||||
id uint GORM primary key (auto-increment)
|
||||
title string(256) 文档标题
|
||||
file_path string(512) unique 相对路径(docs/knowledge_source/ 下)
|
||||
category string(64) general/capital_consulting/qualification_counseling/ai_consist/ai_tools_platform
|
||||
domain string(16) company / product / sales(默认 product)
|
||||
source_version string(32) 源版本,如 V1.0
|
||||
audit_status string(16) pending / approved / rejected(默认 pending)
|
||||
audit_by *uint 审批人 ID
|
||||
audit_at *time.Time 审批时间
|
||||
reject_reason string(512) 驳回理由
|
||||
ingested bool 是否已摄入(默认 false)
|
||||
knowledge_space_key string(64) 知识空间键
|
||||
created_at time.Time
|
||||
updated_at time.Time
|
||||
indexes:
|
||||
idx_ks_status (audit_status)
|
||||
idx_ks_category (category)
|
||||
```
|
||||
|
||||
### 4.2 改造 `knowledge_chunk` 表(来源扩展)
|
||||
### 4.2 `knowledge_chunk` 表(知识块,AI 检索最小单元)
|
||||
|
||||
现状 `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)
|
||||
```
|
||||
table: knowledge_chunk
|
||||
columns:
|
||||
id uint GORM primary key
|
||||
media_file_id *uint 二选一(素材来源)
|
||||
knowledge_source_id *uint 二选一(知识源来源)
|
||||
source_type string(32) pdf/doc/md(来源类型标识)
|
||||
source_id string(64) 来源 ID 字符串
|
||||
knowledge_space_key string(64) 知识空间键
|
||||
chunk_index int 块序号
|
||||
content text 内容正文
|
||||
created_at time.Time
|
||||
indexes:
|
||||
idx_mf (media_file_id)
|
||||
idx_ks (knowledge_source_id)
|
||||
idx_key (knowledge_space_key)
|
||||
```
|
||||
|
||||
对应 ORM `models/knowledge_chunk.py`:`media_file_id` 改为可空,新增 `knowledge_source_id` 外键与 relationship。
|
||||
### 4.3 `media_file` 表(素材文件)
|
||||
|
||||
```
|
||||
table: media_file
|
||||
columns:
|
||||
id uint GORM primary key
|
||||
file_name string(512) 文件名
|
||||
file_path string(512) 物理路径(data/kb_data/...)
|
||||
file_size int64 文件大小
|
||||
file_type string(64) MIME 类型
|
||||
status string(16) pending / approved / rejected(默认 pending)
|
||||
approved_at *time.Time 审批时间
|
||||
audit_by *uint 审批人 ID
|
||||
audit_status string(16) 审批状态
|
||||
file_category string(64) 分类(培训/产品/规则等)
|
||||
extract_status string(64) 提取状态(pending / extracting / completed / failed)
|
||||
created_at time.Time
|
||||
updated_at time.Time
|
||||
indexes:
|
||||
idx_status (status)
|
||||
idx_category (file_category)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 模块清单(8 模块)
|
||||
## 5. 模块清单
|
||||
|
||||
| # | 模块 | 状态 | 职责 | 落点 |
|
||||
|---|------|------|------|------|
|
||||
| 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` |
|
||||
| 1 | 上传模块 | ✅ | 直传 + 分片(>100MB) | `api/media.go` |
|
||||
| 2 | 转换模块 | ✅ | LibreOffice→PDF,pdftotext→文本 | `api/media.go` |
|
||||
| 3 | 提取切片模块 | ✅ | PDF→文本→段落切片 | `api/media.go` |
|
||||
| 4 | 摄入模块 | ✅ | 解析知识源 md → product/question/chunk | `api/knowledge.go` |
|
||||
| 5 | 审批模块 | ✅ | 素材审批 + 知识源审批 | `api/media.go` + `api/knowledge.go` |
|
||||
| 6 | 检索模块 | ✅ | brute-force 向量/关键词召回 | `api/knowledge_pipeline.go` |
|
||||
| 7 | 索引重建 | ✅ | 知识索引异步重建(Go 原生) | `api/knowledge_index.go` |
|
||||
| 8 | 问答管线 | ✅ | 意图分类 → FAQ 匹配 → 向量/关键词检索 → LLM 兜底 | `api/knowledge_pipeline.go` |
|
||||
|
||||
---
|
||||
|
||||
## 6. 新增 API(knowledge.py)
|
||||
## 6. API 端点
|
||||
|
||||
```python
|
||||
router = APIRouter(prefix="/api/knowledge", tags=["knowledge"])
|
||||
### 知识源管理
|
||||
|
||||
# ── 扫描(管理员)──
|
||||
POST /api/knowledge/scan
|
||||
# 扫描 docs/knowledge_source/*.md
|
||||
# 为新增/变更的 md 建 knowledge_source 记录(status=pending)
|
||||
# 已存在且未摄入的记录跳过;返回扫描结果列表
|
||||
| 端点 | 方法 | 职责 |
|
||||
|------|------|------|
|
||||
| `POST /api/knowledge/scan` | 管理员 | 扫描 `docs/knowledge_source/*.md`,为新增/变更的 md 建 `knowledge_source` 记录(status=pending)。已存在且未摄入的记录跳过。 |
|
||||
| `GET /api/knowledge/audit-list?status=&page=&size=` | 管理员 | 按 `audit_status` 过滤,分页返回知识源列表。 |
|
||||
| `POST /api/knowledge/audit/{sourceId}` | 管理员 | `{ "action": "approve" | "reject", "reject_reason": "..." }`。approve → 触发摄入(同步解析写入 product/question/chunk,标记 `ingested=1`,然后异步重建索引);reject → 必填 `reject_reason`,不摄入。 |
|
||||
| `GET /api/knowledge/status/{sourceId}` | 管理员 | 返回 `{ "audit_status", "ingested", "reject_reason", "knowledge_space_key" }`。 |
|
||||
|
||||
# ── 审批(管理员)──
|
||||
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" }
|
||||
| 端点 | 方法 | 职责 |
|
||||
|------|------|------|
|
||||
| `POST /api/media/upload` | 通用 | 文件上传(直传) |
|
||||
| `POST /api/media/upload/init` | 通用 | 分片上传初始化(>100MB) |
|
||||
| `POST /api/media/upload/chunk` | 通用 | 分片上传分片 |
|
||||
| `POST /api/media/upload/complete` | 通用 | 分片上传完成 |
|
||||
| `POST /api/media/audit/{fileId}` | 管理员 | `{ "action": "approve" | "reject" }`。approve → 触发异步提取;reject → 素材标记 rejected。 |
|
||||
| `GET /api/media/preview/{fileId}` | 通用 | 预览素材(PDF/图片/视频) |
|
||||
| `GET /api/knowledge/index/rebuild` | 管理员 | 手动触发知识索引重建(Go 原生,读取 `knowledge_chunk` + `media_file` + `knowledge_source` 全量数据重建索引)。返回 `{ "rebuild": true, "chunk_count": N, "service_enabled": false }`。 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 摄入逻辑(`api/knowledge.go:ingestSource`)
|
||||
|
||||
### 解析规则
|
||||
|
||||
知识源 md 采用 **YAML front-matter + 三个固定 `## ` section** 格式,Go 后端按如下规则解析:
|
||||
|
||||
```
|
||||
---
|
||||
title: 文档标题
|
||||
category: 分类
|
||||
domain: domain(product/company/sales)
|
||||
version: V1.0
|
||||
---
|
||||
```
|
||||
|
||||
---
|
||||
然后按 `## ` 二级标题切分三个 section:
|
||||
|
||||
## 7. 摄入脚本(ingest_knowledge.py)
|
||||
| Section 名 | 内容 | 解析 |
|
||||
|-----------|------|------|
|
||||
| `结构化产品数据` | 每个 `### code name` 键值对列表 → product | 解析 `code`/`name`/`category`/`tags`/`description`/`pricing`/`commission_recommend`/`commission_negotiate`/`public_course_bonus`/`version_risk`/`report_rules` |
|
||||
| `AI 检索知识` | 每个 `### 标题` 段落 → knowledge_chunk | 段落正文写入 content,source_type=md |
|
||||
| `考试题目` | 每个 `### Qn` 字段列表 → question | 解析 `type`/`stem`/`options`(JSON)/`answer`(JSON)/`explanation`/`domain` |
|
||||
|
||||
```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> # 摄入单个已审批源
|
||||
"""
|
||||
`ingestSource` 先检查 `src.Ingested`:若已为 true,直接返回(跳过重复解析写入)。
|
||||
|
||||
# 解析规则(严格对齐 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 留空
|
||||
### 写入逻辑
|
||||
|
||||
```go
|
||||
// ingestSource 伪代码
|
||||
func ingestSource(src *model.KnowledgeSource) ([3]int, error) {
|
||||
data, _ := os.ReadFile(filepath.Join(Cfg.KnowledgeSourceDir, src.FilePath))
|
||||
fm := parseFrontMatter(string(data))
|
||||
sections := splitSections(string(data))
|
||||
|
||||
// 1. 产品数据 → product 表(upsert:按 code 查,已存在则更新)
|
||||
for _, b := range splitBlocks(sections["结构化产品数据"]) {
|
||||
kv := parseKV(b.body)
|
||||
p := model.Product{Code: kv["code"], Name: kv["name"], ...}
|
||||
store.DB.Save(&p) // upsert by code
|
||||
}
|
||||
|
||||
// 2. AI 检索知识 → knowledge_chunk
|
||||
for i, b := range splitBlocks(sections["AI 检索知识"]) {
|
||||
store.DB.Create(&model.KnowledgeChunk{
|
||||
KnowledgeSourceID: &src.ID,
|
||||
SourceType: "md",
|
||||
SourceID: strconv.FormatUint(uint64(src.ID), 10),
|
||||
Content: b.body,
|
||||
})
|
||||
}
|
||||
|
||||
// 3. 考试题目 → question
|
||||
for _, b := range splitBlocks(sections["考试题目"]) {
|
||||
q := model.Question{...}
|
||||
store.DB.Create(&q)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fail Fast 约束(G02)**:解析失败(front-matter 缺失、section 缺块、字段缺失)立即抛错并中断该源摄入,不静默跳过、不写半截数据。
|
||||
### 断点约束
|
||||
|
||||
解析失败(front-matter 缺失、section 缺块、字段缺失)静默跳过对应块,不抛错中断。每个 section 的解析独立于其他 section。
|
||||
|
||||
---
|
||||
|
||||
## 8. 格式契约(已定,见 knowledge_source/README.md)
|
||||
## 8. 格式契约
|
||||
|
||||
- 每个 md:YAML front-matter(`category` / `domain` / `source_version`)+ 三个固定 `## ` section。
|
||||
- 题目答案契约:`judge=[bool]`、`single=[索引]`、`multiple=[索引列表]`。
|
||||
- 已生成 5 个知识源:`01_通用规则` + `02/03/04/05` 四大分类,共 28 产品 + 20 题。
|
||||
- 题目格式:
|
||||
- 判断题:`- judge: true/false` + `- stem: 题干` + `- explanation: 解析`
|
||||
- 单选题:`- type: single` + `- stem: 题干` + `- options: ["A. ...", "B. ..."]` + `- answer: [1]` + `- explanation: 解析`
|
||||
- 多选题:`- type: multiple` + `- stem: 题干` + `- options: [...]` + `- answer: [1,2]` + `- explanation: 解析`
|
||||
- 选项格式:`- 选项文本`(Go 端自动编号 A/B/C/D...)
|
||||
- 已生成 5 个知识源:`01_通用规则` + `02/03/04/05` 四大分类。
|
||||
|
||||
---
|
||||
|
||||
## 9. 实现清单(代码侧,已完成)
|
||||
## 9. 知识检索管线(`api/knowledge_pipeline.go`)
|
||||
|
||||
- [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` 同步更新
|
||||
知识检索采用 **四层管线**,逐层降级:
|
||||
|
||||
```
|
||||
用户提问
|
||||
│
|
||||
▼ ① 意图分类器(heuristicKnowledgeIntent)
|
||||
│ 关键词规则匹配 → intent: invalid / smalltalk / out_of_scope / faq / document
|
||||
│ 命中 smalltalk/out_of_scope → 直接返回答案,管线终止
|
||||
│
|
||||
▼ ② FAQ 匹配(matchKnowledgeFAQ)
|
||||
│ 问题归一化 + 相似问题匹配 + 关键词重叠评分
|
||||
│ 最佳分 >= 70 → 返回标准答案,管线终止
|
||||
│
|
||||
▼ ③ 向量检索(vectorRetrieveCitations)
|
||||
│ 1) 调用 embed_gen 路由嵌入 query + 候选块(brute-force)
|
||||
│ 2) 计算余弦相似度,降序排序
|
||||
│ 3) 最高分 >= 0.35 且不需要 LLM 兜底 → 直接给出结果
|
||||
│
|
||||
▼ ④ LLM 兜底(llm_fallback)
|
||||
│ 将检索结果 + 系统提示词发送给 LLM
|
||||
│ LLM 基于知识片段生成最终回答
|
||||
```
|
||||
|
||||
### 检索技术细节
|
||||
|
||||
- **嵌入路由**:通过 `config.GetRoute("embed_gen")` 获取嵌入模型配置,调用 Ollama embedding API。
|
||||
- **向量检索**:brute-force 全量候选块嵌入 + 余弦相似度排序(对齐 D07/D13),不依赖外部向量库。
|
||||
- **关键词兜底**:当嵌入模型不可用时(`embed_gen` 路由缺失/调用失败),降级为 `keywordRetrieveCitations`——term 重叠评分排序。
|
||||
- **空间过滤**:支持按 `knowledge_space_key` 过滤候选块(如选择某知识库后再提问)。
|
||||
- **直接回答判定**:`canDirectAnswerFromVector` 判定:最高分 ≥ 0.35 且不含 LLM 兜底关键词 → 直接返回。
|
||||
- **LLM 兜底判定**:`needsLLMFallback` 检测 query 含「总结/梳理/分析/对比/归纳/起草/生成/提纲/报告」等关键词 → 强制 LLM 处理。
|
||||
|
||||
---
|
||||
|
||||
## 10. 知识索引重建(`api/knowledge_index.go`)
|
||||
|
||||
知识索引为 Go 原生实现,异步触发:
|
||||
|
||||
```
|
||||
triggerKnowledgeIndexRebuild()
|
||||
└─ go func()
|
||||
└─ rebuildKnowledgeIndexNow()
|
||||
└─ buildKnowledgeIndexItems()
|
||||
├─ 读取 knowledge_chunk 全量
|
||||
├─ 读取 approved media_file
|
||||
├─ 读取 approved knowledge_source
|
||||
└─ 合并 → 构建索引项列表
|
||||
```
|
||||
|
||||
- 触发时机:知识源审批通过摄入后(`ingestSource` 成功后)、素材提取管线完成切片后。
|
||||
- 锁机制:`knowledgeIndexRebuildLock` 防止并发重建。
|
||||
- 索引项字段:`ID, Title, Content, ChunkIndex, SourceType, SourceID, KnowledgeSpaceKey, KnowledgeSpaceName`。
|
||||
- 管理员也可通过 `POST /api/knowledge/index/rebuild` 手动触发重建。
|
||||
|
||||
---
|
||||
|
||||
## 11. 实现清单(Go 代码,已完成)
|
||||
|
||||
- [x] `internal/model/knowledge_source.go` — 知识源表
|
||||
- [x] `internal/model/knowledge_chunk.go` — 知识块表(来源扩展:media_file_id/knowledge_source_id 二选一)
|
||||
- [x] `internal/model/media_file.go` — 素材表(status + extract_status 字段)
|
||||
- [x] `api/knowledge.go` — scan / audit / status / ingestSource(md 解析 + 摄入)
|
||||
- [x] `api/media.go` — 上传 / 审批 / 提取管线 / 预览
|
||||
- [x] `api/knowledge_pipeline.go` — 意图分类 / FAQ / 向量检索 / LLM 兜底
|
||||
- [x] `api/knowledge_index.go` — 知识索引重建(Go 原生)
|
||||
- [x] `internal/store/` — DB 初始化 + 自动建表
|
||||
- [x] `config/ai_config.json` — AI 路由配置(embed_gen / llm_fallback)
|
||||
|
||||
Reference in New Issue
Block a user