Files
pj0235-eai_agentplatform/docs/04_Backend/BE05_Knowledge_Ingest_Module.md
T
eaiadminandClaude Code 16d63de4e1 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>
2026-09-17 21:32:35 +08:00

17 KiB
Raw Blame History

BE05 — 知识入库体系总设计(Knowledge Ingest)

版本: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)。


1. 模块职责

知识入库体系负责把两类知识源,统一经过「审批前置」流入三张消费表,最终支撑「可浏览 / 可考试 / 可 AI 检索」。

知识源类型 载体 审批单元 流入表
非结构化素材 PPT/PDF/Word/视频/图片 逐文件 knowledge_chunk(+ 预览)
结构化知识 知识源 md(docs/knowledge_source/) 逐文档 product / question / knowledge_chunk

核心原则(与 P02/P04 对齐):所有知识只有 approved 才生效;pending / rejected 一律不解析、不进库、前台不可见。


2. 完整目录结构

eai_agentplatform/
├── docs/
│   └── knowledge_source/                 # 知识源文档(权威源,纳入 git 版本管理)
│       ├── README.md                     # 格式契约 + 答案契约 + 审批状态机
│       ├── 01_通用规则.md
│       ├── 02_资本咨询类.md
│       ├── 03_资质认定辅导类.md
│       ├── 04_AI咨询与实施类.md
│       └── 05_企业级AI工具与平台.md
│
├── backend-go/
│   ├── config/ai_config.json             # AI 路由配置(含 embed_gen/llm_fallback)
│   ├── internal/
│   │   ├── api/
│   │   │   ├── 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/
│       ├── eai_agentplatform.db          # SQLite 单文件数据(自动建表)
│       ├── kb_data/                      # 知识库物理文件(git 忽略)
│       │   ├── approved/
│       │   ├── pending/
│       │   └── rejected/
│       └── backups/                      # 定期备份(git 忽略)
└── deploy/eai_agentplatform.env          # 部署环境变量

约定:

  • docs/knowledge_source/ 是知识源 md 的唯一入库口(权威源,git 管理,可 diff 可回滚)。
  • 运行时摄入直接读取该目录,不复制(单一事实源,避免双份漂移)。
  • 素材物理文件存储在 data/kb_data/(git 忽略),分为 approved/pending/rejected 三个子目录。
  • 数据库为 SQLite 单文件(data/eai_agentplatform.db),GORM 首次启动自动建表。

3. 两条流程的状态机

流程 A:非结构化素材

员工上传 ──▶ media_file(pending) ──审批──▶ approved ──▶ 异步转换
管理员上传 ──▶ media_file(approved) ──▶ 立即异步转换          │
                                                            ▼
                                      ┌──────────────────────────┐
                                      │ 文档: LibreOffice→PDF→    │
                                      │      pdftotext 提取→切片     │
                                      │ 视频/图片: 仅预览          │
                                      └──────────┬───────────────┘
                                                 ▼
                                        knowledge_chunk

流程 B:结构化知识源(知识源 md)

知识源 md(docs/knowledge_source/)
      │
      ▼
【扫描】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 表

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 表(知识块,AI 检索最小单元)

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)

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. 模块清单

# 模块 状态 职责 落点
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 端点

知识源管理

端点 方法 职责
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"
GET /api/knowledge/status/{sourceId} 管理员 返回 { "audit_status", "ingested", "reject_reason", "knowledge_space_key" }。

素材管理

端点 方法 职责
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"
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:

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

断点摄入

ingestSource 先检查 src.Ingested:若已为 true,直接返回(跳过重复解析写入)。

写入逻辑

// 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)
    }
}

断点约束

解析失败(front-matter 缺失、section 缺块、字段缺失)静默跳过对应块,不抛错中断。每个 section 的解析独立于其他 section。


8. 格式契约

  • 每个 md:YAML front-matter(category / domain / source_version)+ 三个固定 ## section。
  • 题目格式:
    • 判断题:- 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. 知识检索管线(api/knowledge_pipeline.go)

知识检索采用 四层管线,逐层降级:

用户提问
  │
  ▼ ① 意图分类器(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 代码,已完成)

  • internal/model/knowledge_source.go — 知识源表
  • internal/model/knowledge_chunk.go — 知识块表(来源扩展:media_file_id/knowledge_source_id 二选一)
  • internal/model/media_file.go — 素材表(status + extract_status 字段)
  • api/knowledge.go — scan / audit / status / ingestSource(md 解析 + 摄入)
  • api/media.go — 上传 / 审批 / 提取管线 / 预览
  • api/knowledge_pipeline.go — 意图分类 / FAQ / 向量检索 / LLM 兜底
  • api/knowledge_index.go — 知识索引重建(Go 原生)
  • internal/store/ — DB 初始化 + 自动建表
  • config/ai_config.json — AI 路由配置(embed_gen / llm_fallback)