Files
AIclinicalresearch/docs/02-通用能力层/03-RAG引擎/知识库引擎数据模型设计审查报告.md
HaHafeng 40c2f8e148 feat(rag): Complete RAG engine implementation with pgvector
Major Features:
- Created ekb_schema (13th schema) with 3 tables: KB/Document/Chunk
- Implemented EmbeddingService (text-embedding-v4, 1024-dim vectors)
- Implemented ChunkService (smart Markdown chunking)
- Implemented VectorSearchService (multi-query + hybrid search)
- Implemented RerankService (qwen3-rerank)
- Integrated DeepSeek V3 QueryRewriter for cross-language search
- Python service: Added pymupdf4llm for PDF-to-Markdown conversion
- PKB: Dual-mode adapter (pgvector/dify/hybrid)

Architecture:
- Brain-Hand Model: Business layer (DeepSeek) + Engine layer (pgvector)
- Cross-language support: Chinese query matches English documents
- Small Embedding (1024) + Strong Reranker strategy

Performance:
- End-to-end latency: 2.5s
- Cost per query: 0.0025 RMB
- Accuracy improvement: +20.5% (cross-language)

Tests:
- test-embedding-service.ts: Vector embedding verified
- test-rag-e2e.ts: Full pipeline tested
- test-rerank.ts: Rerank quality validated
- test-query-rewrite.ts: Cross-language search verified
- test-pdf-ingest.ts: Real PDF document tested (Dongen 2003.pdf)

Documentation:
- Added 05-RAG-Engine-User-Guide.md
- Added 02-Document-Processing-User-Guide.md
- Updated system status documentation

Status: Production ready
2026-01-21 20:24:29 +08:00

267 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# **知识库引擎数据模型设计审查报告**
文档状态: ✅ 已确认 (v2.0 融合版)
审查日期: 2026-01-20
审查对象: \[04-数据模型设计.md\] & 架构师建议方案
适用架构: Postgres-Only (Prisma \+ pgvector \+ pg-boss)
目标读者: 开发团队、架构师
## **1\. 核心决策摘要 (Executive Summary)**
经过对业务需求文献、病历、多模态和团队现状2人开发、运维极简的深度评估我们做出了以下关键技术决策
### **✅ 决策 1采用 "Unified Schema"(统一模型)策略**
* **结论**:所有类型的知识库(个人文献库、系统规则库、病历库)**共用一套数据库表结构**。
* **理由**
* **运维成本**避免维护多套向量索引HNSW减少 2 人团队的数据库维护负担。
* **业务灵活性**:通过 contentType 和 JSONB 字段支持未知的新内容类型,无需频繁迁移数据库 (Migration)。
### **✅ 决策 2引入 "Container Table"(容器表)**
* **结论**:在文档表之上,必须增加 EkbKnowledgeBase 表。
* **理由**
* **权限隔离**:明确区分 "User KB" (个人私有) 和 "System KB" (全员共享)。
* **策略配置**:允许为不同库配置不同的切片大小 (ChunkSize) 或检索策略 (TopK)。
### **✅ 决策 3确立 "四层数据架构"**
* **结论**:采纳上传文档中的 Layer 0-4 分层设计。
* **价值**:确保系统的**高容错性** —— 即使 AI 提取结构化数据失败Layer 4基础的 RAG 检索Layer 0依然可用。
## **2\. 最终融合版架构图 (ER Diagram)**
我们融合了原设计的“四层架构”优势和建议方案的“容器管理”优势,形成了最终的 v2.0 模型。
erDiagram
%% 容器层(新增):管理权限和策略
Ekb\_KnowledgeBase {
string id PK
string type "USER | SYSTEM"
string owner\_id "userId | moduleId"
jsonb config "策略配置(chunkSize等)"
}
%% 文档层(四层架构):管理内容和元数据
Ekb\_Document {
string id PK
string kb\_id FK
string content\_type "LITERATURE | CASE | ..."
string extracted\_text "Layer 0: 全文"
jsonb metadata "Layer 4: 标准属性(DOI/作者)"
jsonb structured\_data "Layer 4: 业务属性(PICO/诊断)"
}
%% 切片层(检索核心):管理向量和索引
Ekb\_Chunk {
string id PK
string document\_id FK
vector embedding "1024维向量"
string content "切片文本"
jsonb metadata "切片级元数据(IsAnswer)"
}
Ekb\_KnowledgeBase ||--|{ Ekb\_Document : "contains"
Ekb\_Document ||--|{ Ekb\_Chunk : "split into"
**潜在风险(需要补充):**
1. **缺失 `EkbKnowledgeBase` 表**:目前的 Schema 中 `EkbDocument``kbId` 字段,但没有定义 `EkbKnowledgeBase` 表。
* *风险*:你之前提到“用户有 3 个库,系统有业务库”。如果没有这个表,你无法管理“谁拥有这个库”、“这是系统库还是用户库”、“这个库的 RAG 策略是什么”。
2. **`Chunk` 还可以更灵活**:目前的 `EkbChunk` 只有 `sectionType`
* *建议*:建议给 Chunk 也加上 `metadata` (JSONB)。比如做职业考试题时,你可能希望直接在 Chunk 层面标记 `{ "isAnswer": true }`,这样检索时可以加权。
场景模拟
场景 1ASL 模块搜文献(只搜文献库,不搜病历)
场景 2PKB 用户搜自己的知识库(只搜自己的,不搜别人的)
场景 3AIA 智能问答(既要查系统医学库,又要查用户上传的病历)
| 维度 | 我的设计 (上一版) | 你的设计 (上传版) | 🎯 建议融合方案 |
| :---- | :---- | :---- | :---- |
| **知识库管理** | **有 EkbKnowledgeBase 表**。 明确区分 USER vs SYSTEM 类型。 | **无**。 仅在 Document 中引用 kbId。 | **采纳我的**。 必须有这张表来管理权限和配置。 |
| **文档元数据** | 单个 metadata JSONB 字段。 简单,但混杂。 | **分离设计**。 metadata (标准) \+ structuredData (业务)。 | **采纳你的**。 分离设计对医学场景更友好,利于 TS 类型定义。 |
| **全文存储** | 隐含在 Chunk 中。 | **显式存储 extractedText**。 存放在 Document 表中。 | **采纳你的**。 保留全文很有必要,万一以后要换切片策略(比如 512 改为 1024不用重新解析文件。 |
| **切片属性** | metadata JSONB。 | sectionType 枚举 \+ pageNumber。 | **融合**。 保留 sectionType但增加 metadata JSONB 以备不时之需。 |
## **3\. 详细数据模型定义 (Prisma Schema)**
**开发者注意**:这是最终落地的 Schema请直接复制到 prisma/schema.prisma 的 ekb\_schema 部分。
### **3.1 知识库容器 (EkbKnowledgeBase)**
**解决痛点**:解决了“怎么区分这是用户的库还是系统的库”的问题。
model EkbKnowledgeBase {
id String @id @default(uuid())
name String
description String?
// 核心隔离字段
// USER: 用户创建的ownerId \= userId受配额限制 (如最多3个)
// SYSTEM: 系统预置的ownerId \= moduleId (如 "AIA", "IIT"),全员可读
type KbType @default(USER)
ownerId String
// 策略配置 (JSONB)
// 允许未来针对特定库进行优化,例如:
// { "chunkSize": 1024, "enableRerank": true, "embeddingModel": "v3" }
config Json? @db.JsonB
documents EkbDocument\[\]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index(\[ownerId\])
@@index(\[type\])
@@schema("ekb\_schema")
}
enum KbType {
USER
SYSTEM
}
### **3.2 通用文档对象 (EkbDocument)**
**解决痛点**:解决了“医学数据类型极其复杂”的问题。采用**双 JSONB 策略**。
model EkbDocument {
id String @id @default(uuid())
// 归属关系
kbId String
knowledgeBase EkbKnowledgeBase @relation(fields: \[kbId\], references: \[id\], onDelete: Cascade)
userId String // 冗余存储上传者,方便快速查询
// \===== Layer 1: 基础文件信息 \=====
filename String
fileType String // pdf, docx, md, txt
fileSizeBytes BigInt
fileHash String // 用于实现“秒传”和去重
fileUrl String // OSS 路径
status DocStatus @default(PENDING) // 状态机PENDING \-\> PROCESSING \-\> COMPLETED
errorMessage String? @db.Text
// \===== Layer 0: RAG 核心层 \=====
// 必须保留全文,以便未来更改切片策略时,无需重新解析原始文件
extractedText String? @db.Text
// \===== Layer 2: 内容增强层 \=====
summary String? @db.Text
tokenCount Int?
pageCount Int?
// \===== Layer 3: 分类标签层 \=====
contentType String? // 枚举字符串literature, case, exam, drug
tags String\[\]
category String?
// \===== Layer 4: 结构化数据层 (核心设计) \=====
// 1\. 通用元数据:所有文献都有的 (DOI, Journal, Year, Author)
metadata Json? @db.JsonB
// 2\. 业务结构化数据:特定类型特有的 (PICO, Diagnosis, Dosage, ExamAnswer)
// 使用 JSONB \+ GIN 索引,实现 NoSQL 般的灵活性
structuredData Json? @db.JsonB
chunks EkbChunk\[\]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index(\[kbId\])
@@index(\[contentType\])
// GIN 索引:支持对 metadata 和 structuredData 内部字段的高速查询
@@index(\[metadata\], type: Gin)
@@index(\[structuredData\], type: Gin)
@@schema("ekb\_schema")
}
enum DocStatus {
PENDING
PROCESSING
COMPLETED
FAILED
}
### **3.3 向量切片 (EkbChunk)**
**解决痛点**:解决了“如何让检索更精准”的问题。
model EkbChunk {
id String @id @default(uuid())
documentId String
document EkbDocument @relation(fields: \[documentId\], references: \[id\], onDelete: Cascade)
// 核心内容
content String @db.Text
chunkIndex Int
// 向量数据 (PostgreSQL pgvector)
// 注意:需要手动执行 SQL 创建 HNSW 索引
embedding Unsupported("vector(1024)")?
// 溯源信息
pageNumber Int?
sectionType String? // abstract, methods, results...
// 扩展元数据 (新增)
// 允许在切片粒度做标记。例如:考试题的答案切片标记 { "isAnswer": true }
// 检索时可降低纯答案切片的权重,优先展示问题
metadata Json? @db.JsonB
@@index(\[documentId\])
@@schema("ekb\_schema")
}
## **4\. 关键设计问答 (Q\&A)**
### **Q1: 为什么要把 metadata 和 structuredData 分开?**
* **清晰度**metadata 存放通用的、标准化的属性如标题、作者、时间这是所有文档共有的structuredData 存放业务特定的深层数据(如 PICO、诊断结果
* **前端适配**:前端组件可以统一渲染 metadata而针对 structuredData 则根据 contentType 动态加载不同的展示组件。
### **Q2: 为什么要保留 extractedText (全文)?只存 Chunk 不行吗?**
* **未来兼容性**:如果我们下个月发现 chunkSize=512 效果不好,想改成 1024。如果有 extractedText我们可以直接在数据库里重新切片 (Re-chunking);如果没有,我们就得去 OSS 重新下载文件并解析,成本巨大。
### **Q3: 为什么 Chunk 也要加 metadata**
* **精准检索**:在“职业考试”场景中,一个题目可能被切成“题干”和“解析”两段。我们希望用户搜问题时,优先匹配“题干”。如果在 Chunk 上标记 { "type": "question" },检索算法就可以加权。
## **5\. 业务场景演练**
### **场景 APKB 个人文献库**
* **用户操作**:上传一篇 PDF。
* **数据流**
1. 创建 EkbDocument (kbId=用户的个人库ID)。
2. status \= PROCESSING。
3. 后台解析 \-\> 填充 extractedText。
4. 后台切片 \-\> 插入 EkbChunk。
5. status \= COMPLETED。
### **场景 BASL 智能文献筛选**
* **系统操作**:批量导入 1000 篇文献,并进行 PICO 提取。
* **数据流**
1. 创建 EkbDocument (kbId=ASL临时库ID, contentType='literature')。
2. 调用 LLM 提取 PICO。
3. 更新 structuredData \= { "pico": { "P": "...", "I": "..." } }。
4. **查询时**SELECT \* FROM EkbDocument WHERE structuredData-\>'pico'-\>\>'I' LIKE '%Aspirin%' (利用 GIN 索引加速)。
## **6\. 下一步行动 (Action Plan)**
1. **数据库迁移**
* 更新 prisma/schema.prisma。
* 运行 npx prisma migrate dev \--name init\_ekb\_v2。
2. **索引创建 (SQL)**
* 手动执行 HNSW 索引创建语句Prisma 暂不支持自动生成 vector 索引)。
* 手动执行 pg\_bigm 索引创建语句。
3. **Service 层更新**
* 更新 KnowledgeBaseEngine在入库时必须要求传入 kbId。
* 在检索时,增加对 structuredData 的 JSON 过滤支持。