Summary: - Migrate PostgreSQL to pgvector/pgvector:pg15 Docker image - Successfully install and verify pgvector 0.8.1 extension - Create comprehensive Dify-to-pgvector migration plan - Update PKB module documentation with pgvector status - Update system documentation with pgvector integration Key changes: - docker-compose.yml: Switch to pgvector/pgvector:pg15 image - Add EkbDocument and EkbChunk data model design - Design R-C-R-G hybrid retrieval architecture - Add clinical data JSONB fields (pico, studyDesign, regimen, safety, criteria, endpoints) - Create detailed 10-day implementation roadmap Documentation updates: - PKB module status: pgvector RAG infrastructure ready - System status: pgvector 0.8.1 integrated - New: Dify replacement development plan (01-Dify替换为pgvector开发计划.md) - New: Enterprise medical knowledge base solution V2 Tested: PostgreSQL with pgvector verified, frontend and backend functionality confirmed
191 lines
8.2 KiB
Markdown
191 lines
8.2 KiB
Markdown
# **企业级医学知识库 (EKB) 综合技术解决方案**
|
||
|
||
版本: v2.0 (增强版:临床数据全要素结构化)
|
||
核心目标: 构建高精度、可追溯、基于医学证据的企业级知识库
|
||
技术栈: Node.js, Python, PostgreSQL (pgvector), DeepSeek, pymupdf4llm
|
||
设计范式: R-C-R-G (Router \- Concurrent \- Rerank \- Generate)
|
||
|
||
## **1\. 总体架构设计**
|
||
|
||
本方案采用 **"Postgres-Only"** 极简架构,利用 Node.js 进行业务编排,Python 进行文档解析,外部 API 提供 AI 算力。
|
||
|
||
### **1.1 核心组件**
|
||
|
||
1. **解析层 (Python Microservice)**:
|
||
* **核心工具**:pymupdf4llm
|
||
* **作用**:将 PDF(含复杂表格)转换为 **Markdown** 格式,保留结构化语义。
|
||
2. **存储层 (PostgreSQL 15+)**:
|
||
* **三维信息架构**:
|
||
1. **文献属性信息** (Metadata):PMID、IF分、期刊等。
|
||
2. **文献关键内容信息** (Clinical Data):PICO、用药方案、不良反应、入排标准、终点指标等(JSONB存储)。
|
||
3. **向量信息** (Vector):全文切片的语义向量。
|
||
3. **计算层 (Node.js \+ External APIs)**:
|
||
* **Embedding**:调用 text-embedding-v4 或 BGE-M3 API。
|
||
* **Rerank**:调用 qwen-rerank API。
|
||
* **Reasoning**:调用 DeepSeek V3 进行全要素提取和答案生成。
|
||
4. **文件层 (OSS)**:
|
||
* 存储原始 PDF 和生成的 Markdown 文件。
|
||
|
||
## **2\. 数据模型设计 (存储方案)**
|
||
|
||
利用 PostgreSQL 的强大扩展性,我们在单库中实现多种数据类型的融合存储,构建**健壮的临床数据库**。
|
||
|
||
### **2.1 数据库 Schema (Prisma 描述)**
|
||
|
||
// 1\. 文档主表:承载 "属性信息" 与 "关键内容信息"
|
||
model EkbDocument {
|
||
id String @id @default(uuid())
|
||
projectId String // 业务隔离
|
||
|
||
// \--- A. 文档属性信息 (Metadata) \- 用于基础筛选 \---
|
||
title String
|
||
abstract String? @db.Text // 原始摘要
|
||
pmid String? @unique
|
||
doi String?
|
||
journal String?
|
||
authors String\[\]
|
||
publishYear Int // 用于 "2024年最新" 筛选
|
||
ifScore Float? // 用于 "高分文献" 筛选
|
||
docType String // 指南/RCT/综述/病例报告
|
||
|
||
// \--- B. 文档关键内容信息 (Clinical Data) \- AI 提取的结构化金矿 \---
|
||
// 使用 JSONB 存储,支持 GIN 索引,实现 "SQL 级" 的精准查询
|
||
|
||
// 1\. PICO 要素
|
||
// { "P": "晚期NSCLC", "I": "Keytruda", "C": "化疗", "O": "OS, PFS" }
|
||
pico Json?
|
||
|
||
// 2\. 研究设计与样本
|
||
// { "design": "Phase III RCT", "sampleSize": 500, "blinding": "Double-blind" }
|
||
studyDesign Json?
|
||
|
||
// 3\. 药物与剂量 (Regimen)
|
||
// \[ { "drug": "Pembrolizumab", "dose": "200mg", "freq": "Q3W" } \]
|
||
regimen Json?
|
||
|
||
// 4\. 安全性与不良反应 (Safety)
|
||
// { "ae\_all": \["Rash", "Fatigue"\], "ae\_grade34": \["Pneumonitis"\], "dropout\_rate": "5%" }
|
||
safety Json?
|
||
|
||
// 5\. 入排标准 (Criteria)
|
||
// { "inclusion": \["Age \>= 18", "EGFR \-"\], "exclusion": \["Autoimmune disease"\] }
|
||
criteria Json?
|
||
|
||
// 6\. 观察指标 (Endpoints)
|
||
// { "primary": \["PFS"\], "secondary": \["OS", "ORR"\], "results": {"PFS\_HR": 0.5} }
|
||
endpoints Json?
|
||
|
||
// \--- C. 系统信息 \---
|
||
fileKey String // PDF OSS 路径
|
||
mdKey String? // Markdown OSS 路径
|
||
status String // PENDING, PROCESSED, FAILED
|
||
|
||
chunks EkbChunk\[\]
|
||
|
||
// \--- 索引优化 \---
|
||
@@index(\[projectId\])
|
||
@@index(\[publishYear\])
|
||
@@index(\[ifScore\])
|
||
// GIN 索引:让 JSON 字段支持高速查询 (e.g. 搜特定不良反应)
|
||
@@index(\[pico\], type: Gin)
|
||
@@index(\[regimen\], type: Gin)
|
||
@@index(\[safety\], type: Gin)
|
||
}
|
||
|
||
// 2\. 切片表:向量信息 \+ 文本内容
|
||
model EkbChunk {
|
||
id String @id @default(uuid())
|
||
documentId String
|
||
|
||
// \--- D. 切片内容 \---
|
||
content String @db.Text // Markdown 格式的切片文本
|
||
pageNumber Int // 溯源锚点
|
||
sectionType String? // 标记切片来源:Title, Abstract, Methods, Results, Discussion
|
||
|
||
// \--- E. 向量信息 \---
|
||
// 使用 Unsupported 类型支持 pgvector
|
||
embedding Unsupported("vector(1024)")?
|
||
|
||
document EkbDocument @relation(fields: \[documentId\], references: \[id\], onDelete: Cascade)
|
||
}
|
||
|
||
## **3\. 数据处理流水线 (Ingestion Pipeline)**
|
||
|
||
此流程负责将非结构化的 PDF 转化为结构化的数据库记录。
|
||
|
||
### **步骤一:解析与转换 (Python)**
|
||
|
||
* **工具**:pymupdf4llm
|
||
* **输入**:PDF 文件流。
|
||
* **输出**:Markdown 文本(保留表格结构)。
|
||
|
||
### **步骤二:全要素智能提取 (DeepSeek)**
|
||
|
||
这是构建健壮数据库的关键一步。
|
||
|
||
* **操作**:将全文 Markdown(或前 10k token)丢给 DeepSeek V3。
|
||
* **Prompt**:"你是一个医学数据专家。请阅读这篇文献,严格按照以下 JSON 格式提取关键信息。如果文中未提及,字段留空。
|
||
1. **pico**: { P, I, C, O }
|
||
2. **studyDesign**: { design (如RCT), sampleSize (数字), blinding }
|
||
3. **regimen**: 提取主要药物名称、剂量、给药频率。
|
||
4. **safety**: 提取主要不良反应(Adverse Events)及严重不良反应。
|
||
5. **criteria**: 简要概括核心入选和排除标准。
|
||
6. **endpoints**: 主要和次要观察指标。
|
||
|
||
输出必须是纯 JSON。"
|
||
|
||
* **存储**:解析返回的 JSON,分别填入 EkbDocument 的对应字段。
|
||
|
||
### **步骤三:切片与向量化 (Node.js)**
|
||
|
||
* **切片器**:RecursiveCharacterTextSplitter。
|
||
* **增强策略**:在切片 content 前面加上元数据头。例如:\[Design: RCT\] \[Sample: 500\] ...原文...。这能显著增加向量检索的召回率。
|
||
* **存储**:存入 EkbChunk。
|
||
|
||
## **4\. 检索与问答策略 (增强版 R-C-R-G)**
|
||
|
||
有了结构化数据,我们的检索策略从“模糊搜”升级为“精准筛 \+ 模糊搜”。
|
||
|
||
### **4.1 Router (意图路由)**
|
||
|
||
用户提问后,调用 LLM 判断意图并提取**结构化参数**:
|
||
|
||
1. **安全性查询** \-\> 提取不良反应关键词 (e.g., "Rash")。
|
||
2. **方案对比** \-\> 提取药物名称 (e.g., "Keytruda")。
|
||
3. **高质筛选** \-\> 提取样本量阈值 (e.g., "n \> 100")。
|
||
|
||
### **4.2 Concurrent Retrieval (并发混合召回)**
|
||
|
||
Node.js 并行执行以下查询,最后取并集:
|
||
|
||
* 路径 A (SQL 精准筛选 \- 新增杀手锏):
|
||
利用 JSONB 字段进行逻辑过滤。
|
||
* *Query*: SELECT id FROM EkbDocument WHERE safety-\>\>'ae\_all' ILIKE '%Pneumonitis%' (找提到肺炎副作用的)
|
||
* *Query*: SELECT id FROM EkbDocument WHERE (studyDesign-\>\>'sampleSize')::int \> 100 (找大样本研究)
|
||
* 路径 B (向量检索):
|
||
在路径 A 圈定的 ID 范围内,执行 ORDER BY embedding \<=\> query\_vec。
|
||
* 路径 C (关键词检索):
|
||
使用 ts\_rank 捕捉专有名词。
|
||
|
||
### **4.3 Rerank (重排序)**
|
||
|
||
* **工具**:qwen-rerank API。
|
||
* **策略**:输入用户问题 \+ 混合召回的 Top 50 切片。
|
||
|
||
### **4.4 Generate (生成与溯源)**
|
||
|
||
* **模型**:DeepSeek V3/R1。
|
||
* **优势**:此时 LLM 拿到的 Context 不仅有文本切片,还可以注入该文档的结构化信息(如样本量、PICO),使得回答更加立体。
|
||
|
||
## **5\. 实施关键检查点**
|
||
|
||
1. **JSONB 性能**:PostgreSQL 的 JSONB 查询非常快,但务必建立 GIN 索引(如 @@index(\[safety\], type: Gin)),否则全表扫描会很慢。
|
||
2. **提取成本**:全要素提取会消耗更多 Input Token。按 1000 篇文献算,DeepSeek V3 成本约为 20-30 元人民币,完全可接受。
|
||
3. **数据清洗**:AI 提取的 JSON 可能会有格式错误,Node.js 端需要加一层 try-catch 和简单的格式校验(如 JSON.parse 失败重试)。
|
||
|
||
## **6\. 总结**
|
||
|
||
这套升级后的方案,不仅是一个**知识库**,更是一个**轻量级的 RWE (真实世界证据) 数据库**。
|
||
|
||
* **查询能力质变**:从只能问“文章说了什么”,升级为能问“有哪些文章是用 200mg 剂量的”或“有哪些三期临床涉及了亚洲人”。
|
||
* **应用场景扩展**:支持自动生成 **Meta 分析草稿**、**竞品安全性对比表**、**指南用药推荐汇总** 等高级应用。 |