feat(pkb): Integrate pgvector and create Dify replacement plan
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
This commit is contained in:
@@ -300,3 +300,4 @@ Level 3: 兜底Prompt(缓存也失效)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -486,3 +486,4 @@ const pageSize = Number(query.pageSize) || 20;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -220,3 +220,4 @@ ADMIN-运营管理端/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -319,3 +319,4 @@ INST-机构管理端/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -886,3 +886,4 @@ export interface SlashCommand {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -191,3 +191,4 @@ export type AgentStage = 'topic' | 'design' | 'review' | 'data' | 'writing';
|
||||
|
||||
本次开发遵循了现有 PromptService 的设计模式,与 RVW 模块集成方式保持一致。
|
||||
|
||||
|
||||
|
||||
@@ -1298,5 +1298,6 @@ interface FulltextScreeningResult {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -412,5 +412,6 @@ GET /api/v1/asl/fulltext-screening/tasks/:taskId/export
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -355,5 +355,6 @@ Linter错误:0个
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -514,5 +514,6 @@ Failed to open file '\\tmp\\extraction_service\\temp_10000_test.pdf'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -172,3 +172,4 @@ UNIFUNCS_API_KEY=sk-xxxx
|
||||
- [Postgres-Only 异步任务处理指南](../../02-通用能力层/Postgres-Only异步任务处理指南.md)
|
||||
- [数据库开发规范](../../04-开发规范/09-数据库开发规范.md)
|
||||
|
||||
|
||||
|
||||
@@ -580,5 +580,6 @@ df['creatinine'] = pd.to_numeric(df['creatinine'], errors='coerce')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -418,5 +418,6 @@ npm run dev
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -995,5 +995,6 @@ export const aiController = new AIController();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1329,5 +1329,6 @@ npm install react-markdown
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -237,5 +237,6 @@ FMA___基线 | FMA___1个月 | FMA___2个月
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -395,5 +395,6 @@ formula = "FMA总分(0-100) / 100"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -229,5 +229,6 @@ async handleFillnaMice(request, reply) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -201,5 +201,6 @@ method: 'mean' | 'median' | 'mode' | 'constant' | 'ffill' | 'bfill'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -351,5 +351,6 @@ Changes:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -423,5 +423,6 @@ cd path; command
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -652,5 +652,6 @@ import { logger } from '../../../../common/logging/index.js';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -656,5 +656,6 @@ Content-Length: 45234
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -308,5 +308,6 @@ Response:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -461,5 +461,6 @@ Response:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -455,5 +455,6 @@ import { ChatContainer } from '@/shared/components/Chat';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -365,5 +365,6 @@ const initialMessages = defaultMessages.length > 0 ? defaultMessages : [{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -405,5 +405,6 @@ python main.py
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -653,5 +653,6 @@ http://localhost:5173/data-cleaning/tool-c
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -263,5 +263,6 @@ Day 5 (6-8小时):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -441,5 +441,6 @@ Docs: docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -416,5 +416,6 @@ const mockAssets: Asset[] = [
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -400,5 +400,6 @@ frontend-v2/src/modules/dc/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -360,5 +360,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -314,5 +314,6 @@ ConflictDetectionService // 冲突检测(字段级对比)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -363,5 +363,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -326,5 +326,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -390,5 +390,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -478,5 +478,6 @@ Tool B后端代码**100%复用**了平台通用能力层,无任何重复开发
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -324,5 +324,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -255,5 +255,6 @@ $ node scripts/check-dc-tables.mjs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -488,5 +488,6 @@ ${fields.map((f, i) => `${i + 1}. ${f.name}:${f.desc}`).join('\n')}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -694,4 +694,5 @@ private async processMessageAsync(xmlData: any) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1087,5 +1087,6 @@ async function testIntegration() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -228,5 +228,6 @@ Content-Type: application/json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -649,4 +649,5 @@ REDCap API: exportRecords success { recordCount: 1 }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -654,5 +654,6 @@ backend/src/modules/iit-manager/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -804,5 +804,6 @@ CREATE TABLE iit_schema.wechat_tokens (
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -561,5 +561,6 @@ Day 3 的开发工作虽然遇到了多个技术问题,但最终成功完成
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -328,5 +328,6 @@ AI: "出生日期:2017-01-04
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -272,5 +272,6 @@ Day 4: REDCap EM(Webhook推送)← 作为增强,而非核心
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -687,4 +687,5 @@ const answer = `根据研究方案[1]和CRF表格[2],纳入标准包括:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -445,3 +445,4 @@ export const calculateAvailableQuota = async (tenantId: string) => {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -318,3 +318,4 @@ https://platform.example.com/t/pharma-abc/login
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# PKB个人知识库模块 - 当前状态与开发指南
|
||||
|
||||
> **文档版本:** v2.0
|
||||
> **文档版本:** v2.1
|
||||
> **创建日期:** 2026-01-07
|
||||
> **维护者:** PKB模块开发团队
|
||||
> **最后更新:** 2026-01-07
|
||||
> **重大进展:** 🎉 **PKB模块核心功能全部实现,具备生产可用性!**
|
||||
> **最后更新:** 2026-01-19
|
||||
> **重大进展:** 🎉 **PKB模块核心功能全部实现,pgvector向量数据库已集成!**
|
||||
> **基础设施:** ✅ pgvector 0.8.1 已安装,RAG检索模式基础设施就绪
|
||||
> **文档目的:** 反映模块真实状态,记录开发历程
|
||||
|
||||
---
|
||||
@@ -65,10 +66,11 @@ UI组件: Ant Design v6 + Ant Design X
|
||||
|
||||
```
|
||||
框架: Fastify v4 (Node.js 22)
|
||||
数据库: PostgreSQL 15 + Prisma 6
|
||||
数据库: PostgreSQL 15 + Prisma 6 + pgvector 0.8.1
|
||||
Schema: pkb_schema (独立隔离)
|
||||
向量存储: pgvector (PostgreSQL原生向量扩展) ✅ 2026-01-19 已集成
|
||||
LLM: DeepSeek-V3, Qwen-Max (通过LLMFactory)
|
||||
RAG: Dify知识库集成
|
||||
RAG: Dify知识库集成 → 计划迁移到 pgvector 原生RAG
|
||||
存储: OSS对象存储
|
||||
```
|
||||
|
||||
@@ -210,30 +212,41 @@ frontend-v2/src/modules/pkb/
|
||||
|
||||
**当前状态**:🔧 API执行待调试
|
||||
|
||||
### 4. RAG检索模式(待开发)
|
||||
### 4. RAG检索模式(基础设施就绪)
|
||||
|
||||
**功能说明**:
|
||||
- 基于向量检索
|
||||
- 精准定位相关段落
|
||||
- 适合快速查找
|
||||
|
||||
**当前状态**:❌ 后端待实现
|
||||
**当前状态**:🟡 基础设施已就绪(pgvector 0.8.1 已安装),后端业务逻辑待实现
|
||||
|
||||
**技术基础**(2026-01-19 完成):
|
||||
- ✅ pgvector 扩展已安装(版本 0.8.1)
|
||||
- ✅ 支持 HNSW 和 IVFFlat 索引
|
||||
- ✅ 与阿里云 RDS pgvector 0.8.0 兼容
|
||||
- ⏳ 向量表设计待实现
|
||||
- ⏳ Embedding 服务集成待实现
|
||||
- ⏳ 相似度检索 API 待实现
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 已知问题
|
||||
|
||||
### 1. RAG检索模式未实现 🟡 中优先级
|
||||
### 1. RAG检索模式业务逻辑未实现 🟡 中优先级
|
||||
|
||||
**问题描述**:
|
||||
- RAG检索模式暂未实现
|
||||
- pgvector 基础设施已就绪(2026-01-19)
|
||||
- RAG检索业务逻辑待实现
|
||||
- 当前优先全文阅读和逐篇精读模式
|
||||
|
||||
**影响**:工作模式选择有限
|
||||
|
||||
**解决方案**:
|
||||
- v2.1版本实现RAG检索
|
||||
- 集成Dify知识库检索能力
|
||||
- v2.1版本实现RAG检索(基于pgvector,不再依赖Dify)
|
||||
- 设计向量表结构(pkb_schema.document_embeddings)
|
||||
- 集成 Embedding 服务(OpenAI/智谱)
|
||||
- 实现相似度检索 API
|
||||
|
||||
### 2. 批处理模板有限 🟢 低优先级
|
||||
|
||||
@@ -265,8 +278,11 @@ frontend-v2/src/modules/pkb/
|
||||
|
||||
### v2.1 版本(短期)
|
||||
|
||||
1. **RAG检索模式** 🟡
|
||||
- 实现基于Dify的知识库检索
|
||||
1. **RAG检索模式** 🟡 (基础设施已就绪 ✅)
|
||||
- ✅ pgvector 0.8.1 已安装
|
||||
- 设计向量表结构(pkb_schema.document_embeddings)
|
||||
- 集成 Embedding 服务(文本向量化)
|
||||
- 实现相似度检索 API
|
||||
- 添加工作模式选择器
|
||||
- 测试检索准确度
|
||||
|
||||
@@ -394,7 +410,32 @@ frontend-v2/src/modules/pkb/
|
||||
|
||||
---
|
||||
|
||||
**最后更新:** 2026-01-07
|
||||
---
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
### 2026-01-19 pgvector 向量数据库集成
|
||||
|
||||
**重大变更**:
|
||||
- ✅ **pgvector 0.8.1 安装成功**:Docker 环境已迁移到 `pgvector/pgvector:pg15` 镜像
|
||||
- ✅ **兼容性验证**:与阿里云 RDS pgvector 0.8.0 完全兼容
|
||||
- ✅ **功能验证**:前后端服务重启后功能正常
|
||||
- ✅ **数据完整性**:用户数据、知识库数据、pg-boss 队列函数全部正常
|
||||
|
||||
**技术细节**:
|
||||
- 镜像:`pgvector/pgvector:pg15`
|
||||
- 扩展版本:0.8.1
|
||||
- 支持索引类型:HNSW、IVFFlat
|
||||
- 向量维度:最高支持 16000 维
|
||||
|
||||
**下一步**:
|
||||
- 设计 `pkb_schema.document_embeddings` 表
|
||||
- 集成 Embedding 服务
|
||||
- 实现 RAG 检索 API
|
||||
|
||||
---
|
||||
|
||||
**最后更新:** 2026-01-19
|
||||
**文档维护:** PKB模块开发团队
|
||||
**联系方式:** 项目Issues
|
||||
|
||||
|
||||
191
docs/03-业务模块/PKB-个人知识库/00-系统设计/企业级医学知识库_综合技术解决方案 V2.md
Normal file
191
docs/03-业务模块/PKB-个人知识库/00-系统设计/企业级医学知识库_综合技术解决方案 V2.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# **企业级医学知识库 (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 分析草稿**、**竞品安全性对比表**、**指南用药推荐汇总** 等高级应用。
|
||||
@@ -0,0 +1,602 @@
|
||||
# **医疗科研AI系统架构评估报告:PostgreSQL与pgvector在RAG及知识库中的深度应用分析**
|
||||
|
||||
## **1\. 执行摘要**
|
||||
|
||||
本报告旨在为一家中国的医疗科研创业AI公司提供关于技术选型及其架构决策的深度分析,重点评估利用PostgreSQL数据库的扩展插件pgvector构建检索增强生成(RAG)系统及医疗知识库的可行性、性能表现及行业最佳实践。
|
||||
|
||||
经过对技术架构、医疗领域需求及当前中国云服务生态的详尽调研,本报告得出以下核心结论:
|
||||
|
||||
1. **战略契合度极高(Reliability & Fit)**:对于一家处于创业阶段且数据规模在“数千至数百万”篇文献量级的医疗AI公司,采用PostgreSQL集成pgvector不仅是“靠谱”的方案,更是**最优的架构决策**。它避免了引入专用向量数据库带来的运维复杂性和数据一致性风险,完全契合创业团队对开发效率和系统稳定性的要求。
|
||||
2. **医疗场景的独特优势(Unique Capabilities)**:PostgreSQL最大的竞争壁垒在于其原生支持**混合检索(Hybrid Search)**。医疗科研对查准率要求极高(如区分“5mg”与“50mg”的剂量差异,或精准匹配特定药物名称),单纯的向量语义检索往往难以满足。pgvector与PostgreSQL内置的全文检索(tsvector)结合,能够通过互惠排名融合(RRF)算法,在单一ACID事务中同时实现语义理解与关键词精准匹配,这是大多数独立向量数据库难以企及的能力。
|
||||
3. **容量与扩展性(Capacity & Scalability)**:针对用户关心的“几千还是几万篇文献”的容量问题,pgvector的处理能力远超此量级。在标准硬件配置下,它可以轻松承载**数千万级**的向量数据(对应数十万篇全长医学文献的切片)。只有在数据量突破亿级(100M+ vectors)时,才需考虑分片或迁移至分布式专用向量引擎。
|
||||
4. **国产化与合规(Ecosystem)**:阿里云、腾讯云及华为云的RDS PostgreSQL服务均已全面支持pgvector(包括最新的HNSW索引),这为医疗数据的本地化部署与合规性提供了坚实的基础设施保障。
|
||||
|
||||
本报告将从技术原理、架构对比、医疗场景实战、局限性分析及落地实施指南五个维度,展开长达15,000字的深度论述。
|
||||
|
||||
## ---
|
||||
|
||||
**2\. 技术背景与架构演进**
|
||||
|
||||
### **2.1 医疗科研AI面临的“检索”挑战**
|
||||
|
||||
在构建医疗垂直领域的RAG系统时,核心痛点并非在于大模型(LLM)的生成能力,而在于**检索(Retrieval)的精准度**。医疗数据具有以下显著特征:
|
||||
|
||||
* **术语严谨性**:医学术语(如“心肌梗死”与“心绞痛”)在语义上相近,但在临床路径上截然不同。向量模型有时会因语义泛化而混淆两者。
|
||||
* **多模态信息**:科研文献中包含大量图表、剂量数值、分子式等非结构化文本,这些信息难以通过单一的文本嵌入(Embedding)完美表达。
|
||||
* **数据一致性**:临床指南或药物说明书的更新必须实时反映在系统中。如果数据库与向量库分离,极易出现“幻觉”——即LLM基于旧版本的检索结果生成错误的医疗建议。
|
||||
|
||||
### **2.2 向量数据库的“分久必合”**
|
||||
|
||||
过去三年,AI技术栈经历了从“分离式架构”向“融合式架构”的演进。
|
||||
|
||||
* **第一阶段(2022-2023)**:应用通常采用“PostgreSQL(存储元数据) \+ Pinecone/Milvus(存储向量)”的双库架构。这种架构虽然性能强劲,但带来了双倍的运维成本、网络延迟及数据同步难题(Data Consistency Drift)。
|
||||
* **第二阶段(2024-2026)**:随着pgvector、pg\_embedding等插件的成熟,PostgreSQL具备了原生向量处理能力。对于95%的企业级应用(数据量在1亿向量以下),“全能型数据库”(Converged Database)成为主流选择。它允许开发者在一条SQL语句中同时完成关系型筛选(如“筛选2024年后的论文”)和语义检索(如“查找关于免疫疗法的研究”)1。
|
||||
|
||||
对于初创公司而言,选择pgvector意味着将技术栈复杂度降低了50%,这是在激烈的市场竞争中保持迭代速度的关键优势。
|
||||
|
||||
## ---
|
||||
|
||||
**3\. 深度解析:PostgreSQL \+ pgvector 的核心能力(回应问题 1 & 2)**
|
||||
|
||||
### **3.1 什么是 pgvector?**
|
||||
|
||||
pgvector 是一个基于PostgreSQL的开源扩展插件,它赋予了PostgreSQL存储、索引和查询高维向量数据的能力。它不是一个独立的数据库,而是作为一种新的数据类型(Data Type)和索引方法(Index Access Method)嵌入在PostgreSQL内核中。
|
||||
|
||||
#### **3.1.1 核心数据结构**
|
||||
|
||||
pgvector 并非简单的存储工具,它针对AI场景引入了多种专用数据类型,极大地丰富了应用场景:
|
||||
|
||||
* **vector (Dense Vector)**:
|
||||
* **描述**:标准的稠密向量类型,支持最高16,000维(默认编译配置通常为2,000维,可调整)。
|
||||
* **医疗应用**:完美适配**BioBERT**(768维)、**PubMedBERT**或OpenAI text-embedding-3(1536/3072维)生成的嵌入向量。这是构建医疗文献语义索引的基础 3。
|
||||
* **halfvec (Half-Precision Vector)**:
|
||||
* **描述**:在pgvector 0.7.0版本中引入,使用16位浮点数(FP16)代替标准的32位浮点数(FP32)存储维度数据。
|
||||
* **价值**:这一特性对医疗初创公司至关重要。它将索引占用的内存(RAM)直接**减半**。基准测试显示,在医疗文献检索任务中,FP16相比FP32的检索精度损失(Recall Loss)通常小于1%,但能够让原本需要64GB内存的服务器承载两倍的数据量,极大地降低了云资源成本 5。
|
||||
* **sparsevec (Sparse Vector)**:
|
||||
* **描述**:稀疏向量支持,仅存储非零元素。
|
||||
* **医疗应用**:这是实现**SPLADE**(Sparse Lexical and Expansion)等高级检索模型的关键。在医疗领域,许多文档特征是稀疏的(例如,在数万种药物中,一篇论文可能只提及了“阿司匹林”和“氯吡格雷”)。使用稀疏向量可以比稠密向量更精准地捕捉这些关键词特征,避免语义漂移 6。
|
||||
* **bit (Binary Vector)**:
|
||||
* **描述**:支持二进制向量及海明距离(Hamming Distance)、杰卡德相似系数(Jaccard Distance)。
|
||||
* **医疗应用**:这是**化学信息学**的神器。在药物研发中,分子指纹(Molecular Fingerprints)通常表示为二进制串(如MACCS Keys)。利用pgvector的位运算索引,科研人员可以毫秒级在数百万分子库中检索结构相似的化合物,这是通用文本向量库(如OpenAI Embeddings)无法直接做到的 6。
|
||||
|
||||
### **3.2 索引机制:HNSW 与 IVFFlat 的抉择**
|
||||
|
||||
PostgreSQL 的查询优化器极其强大,而pgvector为向量数据提供了两种核心索引算法。理解它们的差异对于系统调优至关重要。
|
||||
|
||||
#### **3.2.1 HNSW (Hierarchical Navigable Small World)**
|
||||
|
||||
* **原理**:这是一种基于图(Graph-based)的索引算法。它构建了一个分层的“小世界”网络,搜索过程类似于在地图上从洲际公路(高层)逐级导航到街道(底层)。
|
||||
* **性能**:HNSW是目前业界的**性能金标准**(State-of-the-Art)。它提供了极高的查询速度(低延迟)和召回率(Recall)。
|
||||
* **pgvector实战**:自0.5.0版本引入HNSW以来,pgvector的查询性能已能与Milvus、Qdrant等专用库媲美。对于医疗科研系统,**强烈推荐默认使用HNSW索引**。尽管其构建速度较慢且内存占用较高,但它能保证医生在查询“罕见病治疗方案”时,不会因为索引精度问题而漏掉关键文献 9。
|
||||
|
||||
#### **3.2.2 IVFFlat (Inverted File with Flat Compression)**
|
||||
|
||||
* **原理**:基于聚类(Clustering)的倒排索引。它将向量空间划分为多个区域(Lists),查询时先找到最近的区域中心,再遍历该区域内的向量。
|
||||
* **局限**:如果查询向量位于区域边界,极易漏掉相邻区域的最近邻(Recall较低)。且当新增数据导致数据分布改变时,IVFFlat索引的性能会急剧下降,需要定期重建。
|
||||
* **适用场景**:仅在内存极度受限(如在边缘设备或极低配云主机上运行)时考虑。对于追求精准的医疗AI,应尽量避免使用 12。
|
||||
|
||||
### **3.3 距离度量算法**
|
||||
|
||||
pgvector 支持多种度量方式,且可以在同一列上创建不同度量的索引:
|
||||
|
||||
* **余弦相似度(Cosine Similarity \<=\>)**:最适合归一化的文本嵌入(如BioBERT),衡量向量的方向一致性。
|
||||
* **欧氏距离(L2 Distance \<-\>)**:适合未归一化的数据或空间坐标数据。
|
||||
* **内积(Inner Product \<\#\>)**:常用于推荐系统中的矩阵分解场景。
|
||||
|
||||
## ---
|
||||
|
||||
**4\. 核心竞争力:PostgreSQL 独有的“必杀技”(回应问题 3)**
|
||||
|
||||
用户问到:“PostgreSQL的插件pgvector,与PostgreSQL 有哪些特殊的技能,或者能力,是其他向量库所不具备的?”
|
||||
这是一个非常关键的问题。pgvector的真正威力不在于它作为一个向量库有多快,而在于它依然是PostgreSQL。
|
||||
|
||||
### **4.1 真正的“混合检索”(Hybrid Search)能力**
|
||||
|
||||
在医疗RAG系统中,纯向量检索往往存在致命缺陷——**“精确性丢失”**。
|
||||
|
||||
* *案例*:用户查询“\*\*依鲁替尼(Ibrutinib)\*\*的副作用”。
|
||||
* *纯向量检索的问题*:向量模型可能认为“泽布替尼(Zanubrutinib)”在语义上与“依鲁替尼”极度相似(都是BTK抑制剂),因此检索结果可能混杂了大量关于泽布替尼的文献。这在临床上是不可接受的噪音。
|
||||
* PostgreSQL的解法:
|
||||
利用PostgreSQL内置的全文检索(Full-Text Search, FTS)组件tsvector,结合pgvector,可以在一条SQL语句中同时执行:
|
||||
1. **语义检索**:找到关于“BTK抑制剂副作用”的相关文献(高召回)。
|
||||
2. **关键词检索**:强制要求文档中必须包含“Ibrutinib”这个词(高精确)。
|
||||
3. **重排序(Reranking)**:使用\*\*互惠排名融合(Reciprocal Rank Fusion, RRF)\*\*算法,将两者的评分融合。
|
||||
|
||||
**技术实现代码(SQL):**
|
||||
|
||||
SQL
|
||||
|
||||
WITH semantic\_search AS (
|
||||
SELECT id, RANK() OVER (ORDER BY embedding \<=\> query\_embedding) as rank
|
||||
FROM medical\_docs
|
||||
ORDER BY embedding \<=\> query\_embedding LIMIT 50
|
||||
),
|
||||
keyword\_search AS (
|
||||
SELECT id, RANK() OVER (ORDER BY ts\_rank\_cd(to\_tsvector('english', content), plainto\_tsquery('Ibrutinib side effects'))) as rank
|
||||
FROM medical\_docs
|
||||
WHERE to\_tsvector('english', content) @@ plainto\_tsquery('Ibrutinib side effects')
|
||||
LIMIT 50
|
||||
)
|
||||
SELECT
|
||||
COALESCE(s.id, k.id) as doc\_id,
|
||||
COALESCE(s.rank, 0) \+ COALESCE(k.rank, 0) as combined\_score
|
||||
FROM semantic\_search s
|
||||
FULL OUTER JOIN keyword\_search k ON s.id \= k.id
|
||||
ORDER BY combined\_score ASC
|
||||
LIMIT 10;
|
||||
|
||||
这种\*\*“原生SQL级融合”\*\*是Milvus或Pinecone无法做到的。后者通常需要你在应用层(Python代码)分别请求两个系统(Elasticsearch \+ Milvus),然后在内存中极其痛苦地合并结果,不仅增加了延迟,还引入了系统复杂性 14。
|
||||
|
||||
### **4.2 ACID 事务一致性(Transactional Consistency)**
|
||||
|
||||
医疗数据容不得“最终一致性”。
|
||||
|
||||
* *场景*:某篇科研论文被撤稿(Retracted),或者某位患者撤回了隐私授权。
|
||||
* *专用向量库的痛点*:你需要在MySQL中删除记录,然后异步调用API去Pinecone中删除向量。如果中间网络抖动,Pinecone删除失败,系统就会出现“幽灵数据”——AI依然能检索到这篇已被撤稿的文章并据此生成建议,引发严重的医疗伦理事故。
|
||||
* *PostgreSQL的能力*:
|
||||
SQL
|
||||
BEGIN;
|
||||
DELETE FROM medical\_docs WHERE id \= 1001; \-- 向量数据随行记录一同物理删除
|
||||
COMMIT;
|
||||
|
||||
在PostgreSQL中,向量只是表的一列。删除操作是**原子性(Atomic)的。一旦提交,任何查询(无论是SQL还是向量搜索)都绝不会再看到这条数据。这种强一致性**是医疗级系统的刚需 2。
|
||||
|
||||
### **4.3 复杂的元数据过滤(Pre-Filtering)**
|
||||
|
||||
医疗查询往往伴随着极其复杂的筛选条件。
|
||||
|
||||
* *查询*:“查找**2020年以后**发表的、由**北京协和医院**作者撰写的、关于**肺结节**的**随机对照试验(RCT)**。”
|
||||
* PostgreSQL的优势:
|
||||
PostgreSQL拥有几十年历史的查询优化器(Query Planner)。它能智能地分析统计信息:
|
||||
* 如果“2020年以后”的数据很少,它会先利用B-Tree索引筛选出这些记录,然后对剩下的几条数据进行暴力向量计算(速度极快)。
|
||||
* 如果筛选条件很宽泛,它会先走HNSW向量索引,再过滤属性。
|
||||
这种\*\*基于成本的优化(Cost-Based Optimization)\*\*是其核心壁垒。专用向量库在处理复杂的布尔逻辑(AND/OR/NOT嵌套)和关系型Join(如关联作者表、医院表)时,性能往往大幅下降,甚至不支持 17。
|
||||
|
||||
## ---
|
||||
|
||||
**5\. 局限性与不足分析(回应问题 4)**
|
||||
|
||||
尽管pgvector在架构上优势明显,但在某些维度上,它确实不如专用向量数据库。我们需要客观地认清这些差距,以判断它们是否会成为贵公司的瓶颈。
|
||||
|
||||
### **5.1 规模天花板(The Scale Wall)**
|
||||
|
||||
* **现象**:PostgreSQL是单机垂直扩展(Scale-up)架构。
|
||||
* **局限**:当向量数据量达到\*\*1亿(100 Million)\*\*级别以上时,单机内存(RAM)和磁盘I/O将成为瓶颈。虽然可以通过分区表(Partitioning)缓解,但在十亿级规模下,Milvus或Weaviate这种天生分布式、支持水平分片(Sharding)的系统会更有优势。
|
||||
* **医疗场景评估**:PubMed所有文献摘要加起来约为3500万篇。即使切分成块(Chunking),总量可能在2-3亿向量。如果贵公司的目标是索引全人类所有语种的医疗文献,PostgreSQL可能会吃力(需要引入Citus分布式插件或极高配置的服务器)。但如果仅针对特定领域(如肿瘤学、心血管)或中文医疗文献,数据量通常在百万至千万级,pgvector完全在舒适区 19。
|
||||
|
||||
### **5.2 资源争抢(Resource Contention)**
|
||||
|
||||
* **现象**:向量搜索是计算密集型(CPU密集)和内存密集型任务。
|
||||
* **局限**:在同一个PostgreSQL实例中,构建HNSW索引或执行高并发向量查询会占用大量CPU资源。如果数据库同时还承载着用户登录、订单支付等OLTP业务,可能会导致由于CPU飙升而拖慢常规业务的响应速度。
|
||||
* **解决方案**:架构分离。
|
||||
* **主库(Primary)**:负责写入和常规业务。
|
||||
* **只读副本(Read Replica)**:专门用于承载向量搜索流量。阿里云和腾讯云的RDS都支持创建只读实例,这是标准的规避方案。
|
||||
|
||||
### **5.3 缺乏GPU加速**
|
||||
|
||||
* **局限**:目前的pgvector主要依赖CPU指令集(如AVX-512)进行加速。相比之下,Milvus和FAISS对GPU(NVIDIA CUDA)有深度优化,在大批量(Batch)查询或超大规模索引构建时,GPU的速度是CPU的几十倍。
|
||||
* **影响**:对于实时RAG系统(通常是单条查询),CPU的延迟(毫秒级)已经足够低,GPU的吞吐优势体现不明显。但如果需要离线大规模聚类分析,pgvector会慢很多 20。
|
||||
|
||||
## ---
|
||||
|
||||
**6\. 医疗科研领域的RAG应用实战(回应问题 5 & 6)**
|
||||
|
||||
### **6.1 典型应用场景**
|
||||
|
||||
1. **循证医学问答(Evidence-Based QA)**:
|
||||
* 医生提问:“针对EGFR突变的非小细胞肺癌,三代TKI耐药后的后续治疗方案有哪些?”
|
||||
* 系统利用pgvector检索最新的临床指南和Case Report,结合RAG生成包含引用来源的建议。
|
||||
2. **临床试验匹配(Clinical Trial Matching)**:
|
||||
* 将患者的病历(非结构化文本)转化为向量。
|
||||
* 利用向量相似度搜索,在数据库中匹配入排标准(Inclusion/Exclusion Criteria)相似的临床试验项目。
|
||||
3. **药物重定位(Drug Repurposing)**:
|
||||
* 利用二进制向量(bit类型)存储药物分子指纹。
|
||||
* 通过杰卡德距离计算,发现老药与新靶点在分子结构上的潜在关联 6。
|
||||
|
||||
### **6.2 知识库容量评估**
|
||||
|
||||
用户问:“能够承载多大容量?几千篇还是几万篇?”
|
||||
|
||||
* **结论**:**“几千、几万篇”对PostgreSQL来说属于微量数据**。
|
||||
* **数据推算**:
|
||||
* 假设一篇医疗文献平均5,000字,切分为10个Chunk。
|
||||
* **5万篇文献** \= 50万个向量。
|
||||
* **50万个768维向量**(BioBERT)的原始数据大小约为 **1.5 GB**。
|
||||
* 加上HNSW索引,总内存占用可能在 **2-3 GB** 左右。
|
||||
* **性能预期**:在任何一台现代笔记本电脑或入门级云服务器(2核 4G)上,检索50万向量的耗时都在 **5毫秒(ms)** 以内。
|
||||
* **上限**:pgvector 在单机(如阿里云 32GB 内存实例)上,可以流畅处理 **1000万至2000万** 个向量(对应约100万-200万篇全长文献)。因此,贵公司的需求完全在覆盖范围内,且留有百倍的增长空间 10。
|
||||
|
||||
### **6.3 医疗文献处理的最佳范式**
|
||||
|
||||
在医疗领域,直接将整段文本向量化往往效果不佳。推荐采用\*\*分层索引(Hierarchical Indexing)\*\*策略:
|
||||
|
||||
| 处理步骤 | 方法 | PostgreSQL实现 |
|
||||
| :---- | :---- | :---- |
|
||||
| **1\. 图表处理** | 医疗文献包含大量图表(Table 1, Figure 2)。直接丢弃会丢失关键数据。 | 将表格转化为Markdown或JSON格式,利用LLM生成“表格摘要文本”。对**摘要**进行Embedding存入vector列,原表格存入jsonb列。 |
|
||||
| **2\. 切片策略** | 避免机械切分(如每500字一切)。采用语义切分。 | 建立父子文档结构。父文档(Parent)是整章“Result”,子文档(Child)是具体发现。检索时匹配子文档向量,但在Prompt中提供父文档全文上下文。PostgreSQL的JOIN查询天然支持这种操作。 |
|
||||
| **3\. 引用溯源** | 医疗回答必须有据可查。 | 建立citations表。RAG生成答案后,利用SQL关联查询出对应的文献DOI、发表年份和期刊影响因子,作为可信度背书。 |
|
||||
|
||||
## ---
|
||||
|
||||
**7\. 实施指南与生态资源(回应问题 7)**
|
||||
|
||||
### **7.1 开发教程与易用性**
|
||||
|
||||
pgvector 的学习曲线极低,特别是对于熟悉SQL的开发者。
|
||||
|
||||
**极简开发流程:**
|
||||
|
||||
1. **启用插件**:
|
||||
SQL
|
||||
CREATE EXTENSION vector;
|
||||
|
||||
2. **创建表**:
|
||||
SQL
|
||||
CREATE TABLE papers (
|
||||
id bigserial PRIMARY KEY,
|
||||
content text,
|
||||
embedding vector(768) \-- BioBERT dimensions
|
||||
);
|
||||
|
||||
3. **插入数据**:
|
||||
SQL
|
||||
INSERT INTO papers (content, embedding) VALUES ('Aspirin inhibits...', '\[0.1, \-0.2,...\]');
|
||||
|
||||
4. **查询(最近邻搜索)**:
|
||||
SQL
|
||||
SELECT content FROM papers ORDER BY embedding \<=\> '\[0.1, \-0.2,...\]' LIMIT 5;
|
||||
|
||||
**开发框架支持**:
|
||||
|
||||
* **LangChain**:Python库langchain-postgres提供了开箱即用的PGVector类,完全封装了底层SQL 21。
|
||||
* **LlamaIndex**:原生支持Postgres向量存储,并能自动将LlamaIndex的高级过滤器转换为SQL WHERE子句 23。
|
||||
|
||||
### **7.2 中国云服务商支持情况**
|
||||
|
||||
由于贵公司在中国,云服务商的兼容性至关重要:
|
||||
|
||||
* **阿里云(Aliyun RDS)**:
|
||||
* PostgreSQL 14及以上版本全面支持pgvector。
|
||||
* **注意**:需在控制台确认插件版本。建议升级到支持HNSW的0.5.0以上版本。最新的0.7.0版本(支持稀疏向量)可能需要最新的内核小版本(20240530以后),请在购买前查看RDS发布说明 24。
|
||||
* **腾讯云(TencentDB)**:
|
||||
* PostgreSQL 13/14/15 均支持pgvector。腾讯云对内核进行了深度优化,支持向量化指令加速。
|
||||
* 最新版本已支持pgvector 0.7.0,且支持在控制台管理插件升级 26。
|
||||
* **华为云(GaussDB / RDS)**:
|
||||
* 华为云RDS for PostgreSQL 12+ 支持该插件。
|
||||
* GaussDB(分布式版)也有自己的向量检索引擎,但标准RDS PostgreSQL更适合初创期的轻量级部署 28。
|
||||
|
||||
## ---
|
||||
|
||||
**8\. 总结与建议**
|
||||
|
||||
经过全面评估,我们认为**PostgreSQL \+ pgvector 是贵公司构建医疗AI知识库的首选方案**。
|
||||
|
||||
**核心理由总结**:
|
||||
|
||||
1. **架构极简**:无需维护第二套数据库,降低了创业初期的运维与人力成本。
|
||||
2. **能力全面**:独有的混合检索(Hybrid Search)能力完美解决医疗领域对“精准实体匹配”的刚需。
|
||||
3. **完全合规**:数据完全私有化,依托成熟的国产云数据库底座,规避数据出境风险。
|
||||
4. **容量充足**:轻松应对千万级文献,足以支撑公司未来3-5年的业务增长。
|
||||
|
||||
**落地建议**:
|
||||
|
||||
* **模型选择**:务必使用针对生物医学微调的**BioBERT**或**PubMedBERT**作为Embedding模型,而非通用的OpenAI模型,以提升专业术语的理解能力。
|
||||
* **索引策略**:生产环境务必创建**HNSW索引**,不要使用默认的IVFFlat。
|
||||
* **硬件配置**:向量索引常驻内存。建议数据库服务器内存(RAM)配置充足(例如,每100万个768维向量预留约2-3GB内存)。
|
||||
|
||||
此方案不仅是一个技术上的“可行解”,更是一个商业上的“最优解”,它以最低的复杂度提供了最符合医疗严肃场景需求的核心能力。
|
||||
|
||||
---
|
||||
|
||||
*(以下为报告详细正文,涵盖上述章节的深度展开,包含具体SQL代码、性能调优参数及详细的架构设计图解描述。)*
|
||||
|
||||
# **详细研究报告:基于PostgreSQL与pgvector构建医疗科研AI系统的深度评估**
|
||||
|
||||
## **1\. 引言**
|
||||
|
||||
随着大语言模型(LLM)在医疗领域的应用深化,\*\*检索增强生成(RAG)\*\*已成为解决模型幻觉、赋予模型实时专业知识的关键架构。对于一家中国的医疗科研创业公司而言,选择何种技术栈来构建底层的“知识库”与“向量检索引擎”,直接决定了产品的检索质量、响应速度以及后续数年的运维成本。
|
||||
|
||||
本报告针对用户提出的核心疑问,对利用现有的 **PostgreSQL** 数据库集成 **pgvector** 插件这一技术路线进行了全方位的可行性研究。报告将结合医疗行业的特殊需求(如高精度、多模态数据、数据隐私)及中国本土的云基础设施环境,提供客观、详实的技术分析。
|
||||
|
||||
## **2\. 技术路线评估:为什么选择 pgvector?(Reliability & Architectural Fit)**
|
||||
|
||||
### **2.1 从“专用”到“通用”的范式转移**
|
||||
|
||||
在RAG技术出现的早期(2022年左右),由于传统关系型数据库对向量支持的缺失,开发者往往被迫采用“多库架构”:使用MySQL/PostgreSQL存储业务数据,同时引入Milvus、Pinecone或Weaviate存储向量数据。
|
||||
|
||||
然而,对于医疗科研系统,这种分离架构带来了巨大的痛点:
|
||||
|
||||
1. **数据一致性风险(Data Consistency Risk)**:医疗数据要求极高的严谨性。如果一篇临床指南被修订了,PostgreSQL中的文本更新了,但向量库中的索引未能毫秒级同步,AI就可能引用旧版指南给出错误的医疗建议。这种“同步延迟”在医疗领域是不可接受的风险。
|
||||
2. **元数据过滤的复杂性(Metadata Filtering Complexity)**:科研人员常需要进行复杂的组合查询,例如“查找2023年IF\>10的期刊中关于PD-1抑制剂的论文”。在分离架构中,这需要在两个数据库间传输大量ID进行“交集”运算,效率极低。
|
||||
|
||||
pgvector 的出现解决了这一核心矛盾。
|
||||
作为PostgreSQL的一个扩展,pgvector 让向量(Vector)成为数据库中的一种原生数据类型。这意味着:
|
||||
|
||||
* **单一数据源(Single Source of Truth)**:文本、元数据、向量存储在同一张表中。
|
||||
* **ACID 事务保障**:更新文献和更新向量在同一个事务中完成,要么全成功,要么全失败,彻底消除了数据不一致的隐患。
|
||||
* **运维极简**:初创团队无需学习和维护一套全新的分布式向量数据库系统,只需维护现有的PostgreSQL即可。备份、恢复、高可用(HA)方案完全复用。
|
||||
|
||||
### **2.2 中国医疗环境下的合规性优势**
|
||||
|
||||
在中国,医疗数据属于强监管范畴(如《数据安全法》)。使用SaaS类的向量数据库(如Pinecone)通常涉及数据传输到公有云API,这在合规上存在灰色地带。
|
||||
相比之下,PostgreSQL可以完全私有化部署(Self-hosted)。无论是在医院内网的物理机上,还是在阿里云/腾讯云的私有网络(VPC)中,企业都能对数据拥有100%的掌控权。各大国产数据库(如华为GaussDB、腾讯TDSQL)大多基于PostgreSQL生态,这也为未来的信创迁移留出了空间。
|
||||
|
||||
## ---
|
||||
|
||||
**3\. 功能详解:pgvector 能做什么?(Capabilities & Features)**
|
||||
|
||||
pgvector 的功能迭代极快,特别是0.5.0版本之后的HNSW索引支持,使其真正具备了生产级能力。
|
||||
|
||||
### **3.1 向量数据类型与存储**
|
||||
|
||||
在PostgreSQL中,启用插件后,即可使用 vector 类型:
|
||||
|
||||
SQL
|
||||
|
||||
CREATE EXTENSION vector;
|
||||
CREATE TABLE documents (
|
||||
id bigserial PRIMARY KEY,
|
||||
content text,
|
||||
embedding vector(768) \-- 对应BioBERT模型的维度
|
||||
);
|
||||
|
||||
#### **3.1.1 维度支持**
|
||||
|
||||
pgvector 支持高达16,000维的向量。对于医疗领域常用的Embedding模型:
|
||||
|
||||
* **BioBERT / PubMedBERT**:输出768维向量。
|
||||
* **BGE-M3 (BAAI)**:输出1024维向量。
|
||||
* OpenAI text-embedding-3-large:输出3072维向量。
|
||||
以上模型均能被完美支持。
|
||||
|
||||
#### **3.1.2 半精度向量(Halfvec)—— 降本增效利器**
|
||||
|
||||
在pgvector 0.7.0版本中,引入了 halfvec 类型。它将维度的存储精度从32位浮点数降为16位。
|
||||
|
||||
* **原理**:深度学习模型的输出往往不需要极高的浮点精度来维持检索排序。
|
||||
* **收益**:存储空间减少50%,内存占用减少50%。
|
||||
* **实测**:对于千万级的文献库,这意味着原本需要128GB内存的服务器,现在只需64GB即可全内存加载索引,大幅节省了云服务器成本 5。
|
||||
|
||||
#### **3.1.3 稀疏向量(Sparsevec)—— 专治“关键词遗漏”**
|
||||
|
||||
传统的稠密向量(Dense Vector)是将一段文本压缩成一个实数数组,这会导致信息压缩损失。例如,一篇长论文中只出现了一次“TP53基因突变”,在稠密向量中,这个特征可能被淹没。
|
||||
sparsevec 允许存储稀疏向量(如SPLADE模型的输出)。它仅记录非零维度的值,能够精准捕捉稀缺关键词。这对于医疗科研中的精准术语检索价值巨大 7。
|
||||
|
||||
### **3.2 索引算法:性能的核心**
|
||||
|
||||
#### **3.2.1 HNSW(推荐)**
|
||||
|
||||
pgvector 实现了完整的HNSW索引。
|
||||
|
||||
* **性能**:查询时间复杂度为 $O(\\log N)$。在百万级数据下,单次查询通常在2ms-10ms之间。
|
||||
* **参数可调**:
|
||||
* m:节点最大连接数。医疗场景建议设为16-32,以保证高召回。
|
||||
* ef\_construction:构建时的搜索深度。设为64-128可提升索引质量,虽然构建变慢,但查询更准 9。
|
||||
* **构建速度**:支持并行构建(Parallel Build)。利用多核CPU,构建速度比早期版本提升了数倍。
|
||||
|
||||
#### **3.2.2 IVFFlat**
|
||||
|
||||
这是早期的索引算法。除非您的服务器内存极小(例如只有2GB内存却要跑数百万数据),否则不建议使用。它的召回率不稳定,且维护成本高(需要定期Reindex)。
|
||||
|
||||
## ---
|
||||
|
||||
**4\. 独家绝技:PostgreSQL \+ pgvector 的不可替代性(Unique Skills)**
|
||||
|
||||
用户特别关心“有什么是别人做不到的”。除了前文提到的ACID事务,最大的杀手锏是**混合检索(Hybrid Search)**。
|
||||
|
||||
### **4.1 医疗场景下的“语义”与“符号”之争**
|
||||
|
||||
在医疗RAG中,我们经常面临两难:
|
||||
|
||||
* \*\*语义检索(Vector)\*\*擅长理解概念。例如搜“免疫检查点抑制剂”,它能找到“PD-1”、“CTLA-4”相关的文章。
|
||||
* \*\*符号检索(Keyword)\*\*擅长精确匹配。例如搜“NCT04826348”(临床试验号),或者搜“5mg/kg”,这需要字符级的精确匹配。
|
||||
|
||||
如果只用向量库,很难搜到具体的临床试验号,因为向量模型无法精确编码一串随机数字。
|
||||
如果只用全文检索,搜“癌症治疗”可能会漏掉“肿瘤疗法”的文章。
|
||||
|
||||
### **4.2 完美的结合:pgvector \+ tsvector**
|
||||
|
||||
PostgreSQL 是世界上最强大的开源关系型数据库,它内置了企业级的全文检索引擎 tsvector(基于BM25算法)。
|
||||
利用 pgvector,我们可以在数据库内部实现\*\*“语义+关键词”的双路召回\*\*,并使用\*\*RRF(互惠排名融合)\*\*算法合并结果。
|
||||
实战案例:
|
||||
假设我们要找“阿司匹林在心血管疾病中的应用,且必须提及出血风险”。
|
||||
|
||||
SQL
|
||||
|
||||
WITH vector\_search AS (
|
||||
\-- 路径1:语义检索,找前50个语义最相关的
|
||||
SELECT id, RANK() OVER (ORDER BY embedding \<=\> query\_embedding) as rank
|
||||
FROM papers
|
||||
ORDER BY embedding \<=\> query\_embedding LIMIT 50
|
||||
),
|
||||
keyword\_search AS (
|
||||
\-- 路径2:关键词检索,找前50个包含'bleeding'和'risk'的
|
||||
SELECT id, RANK() OVER (ORDER BY ts\_rank\_cd(to\_tsvector('english', content), plainto\_tsquery('bleeding risk'))) as rank
|
||||
FROM papers
|
||||
WHERE to\_tsvector('english', content) @@ plainto\_tsquery('bleeding risk')
|
||||
LIMIT 50
|
||||
)
|
||||
\-- 融合:计算RRF得分
|
||||
SELECT
|
||||
COALESCE(v.id, k.id) as id,
|
||||
(1.0 / (60 \+ COALESCE(v.rank, 0))) \+ (1.0 / (60 \+ COALESCE(k.rank, 0))) as rrf\_score
|
||||
FROM vector\_search v
|
||||
FULL OUTER JOIN keyword\_search k ON v.id \= k.id
|
||||
ORDER BY rrf\_score DESC
|
||||
LIMIT 10;
|
||||
|
||||
这一能力是**PostgreSQL独有**的。它不需要外部的Elasticsearch,不需要复杂的ETL,不需要在应用层写代码合并列表。它极其高效、稳定,是构建高精度医疗RAG的最佳范式 14。
|
||||
|
||||
## ---
|
||||
|
||||
**5\. 局限性与不足:理性看待差距(Limitations)**
|
||||
|
||||
虽然pgvector对于99%的场景都足够好,但在某些极端场景下,它确实不如Milvus或Pinecone。
|
||||
|
||||
### **5.1 亿级以上规模的挑战**
|
||||
|
||||
* **索引构建时间**:当数据量达到1亿(100M)以上时,PostgreSQL构建HNSW索引可能会非常慢(数小时甚至数天),因为它是基于磁盘的数据库,且主要依赖CPU计算。而Milvus支持GPU加速索引构建,速度快10倍以上 20。
|
||||
* **内存瓶颈**:HNSW索引非常吃内存。虽然PostgreSQL支持将索引存放在磁盘上(利用OS Page Cache),但如果内存不足以容纳大部分索引层,查询会出现大量磁盘IO,导致延迟从2ms飙升到200ms。专用向量库通常有更复杂的内存管理机制(如Mmap优化)来缓解这个问题。
|
||||
|
||||
### **5.2 资源隔离问题**
|
||||
|
||||
* PostgreSQL是单进程多线程(或多进程)模型。如果一个巨大的向量查询占满了CPU,可能会影响到数据库中其他简单的SQL查询(如用户登录)。
|
||||
* **对策**:在生产环境中,强烈建议配置**读写分离**。主库处理写入和元数据更新,配置一个只读节点(Read Replica)专门处理向量查询请求。阿里云和腾讯云的RDS都能一键实现这个架构。
|
||||
|
||||
### **5.3 高级量化技术缺失**
|
||||
|
||||
* 目前pgvector支持halfvec(FP16)和简单的二进制量化。但业界领先的向量库(如Qdrant)支持积量化(Product Quantization, PQ),能将向量压缩到原始大小的1/64。如果您的数据量极其庞大且预算极其有限,pgvector的存储成本会显得较高。
|
||||
|
||||
## ---
|
||||
|
||||
**6\. 容量与性能:几千还是几万?(Capacity Analysis)**
|
||||
|
||||
用户问:“能够承载多大容量的知识库?几千篇文献,还是几万篇文献?”
|
||||
|
||||
### **6.1 容量评估:绰绰有余**
|
||||
|
||||
对于pgvector而言,“几万篇文献”属于微型数据集。
|
||||
让我们做一下量化计算:
|
||||
|
||||
* **场景**:假设您有**10万篇**医疗文献。
|
||||
* **切片(Chunking)**:每篇文献平均切分为20个段落(Chunks)。
|
||||
* **总量**:$100,000 \\times 20 \= 2,000,000$ (200万)个向量。
|
||||
* **存储需求**:
|
||||
* BioBERT向量(768维)大小:$2,000,000 \\times 768 \\times 4 \\text{ bytes} \\approx 6 \\text{ GB}$。
|
||||
* HNSW索引大小:约为数据大小的50%-80%,即约 **4 GB**。
|
||||
* **总内存需求**:约 **10 GB**。
|
||||
|
||||
结论:一台标准的 16GB内存 的云服务器(如阿里云ECS g7型)就足以将这10万篇文献的索引全部加载进内存,实现毫秒级查询。
|
||||
PostgreSQL的舒适区在 1000万至5000万 向量之间。因此,贵公司的业务即使增长100倍,这套架构依然撑得住。
|
||||
|
||||
### **6.2 性能实测数据**
|
||||
|
||||
根据由于Google Cloud和AWS进行的基准测试(基于10):
|
||||
|
||||
* **数据集**:100万条 768维向量。
|
||||
* **索引**:HNSW (m=16, ef=64)。
|
||||
* **查询延迟(Latency)**:\< 5ms (在t3.medium这种低配机器上)。
|
||||
* **每秒查询数(QPS)**:单机可达数千QPS。对于医疗科研系统(通常是医生或研究员使用,并发不高),这性能完全溢出。
|
||||
|
||||
## ---
|
||||
|
||||
**7\. 医疗科研领域的特殊应用与最佳范式(Medical Best Practices)**
|
||||
|
||||
### **7.1 嵌入模型的选择**
|
||||
|
||||
不要直接使用OpenAI的通用模型。在医疗领域,专有模型效果更好。
|
||||
|
||||
* **推荐**:
|
||||
* 英文文献:**BioBERT** 或 **PubMedBERT**。
|
||||
* 中文文献:**Text2Vec-Base-Chinese-Paraphrase** 或针对中医/西医微调过的BERT模型。
|
||||
* **注意**:pgvector 只是存储容器,检索质量的70%取决于Embedding模型本身的质量。
|
||||
|
||||
### **7.2 表格与图表的处理**
|
||||
|
||||
科研文献中的精华往往在图表(Table/Figure)里。
|
||||
|
||||
* **错误做法**:直接把表格转成文本字符串切片。这会丢失行列对应关系。
|
||||
* **最佳范式**:
|
||||
1. 使用OCR或PDF解析工具(如Unstructured.io)提取表格。
|
||||
2. 将表格转化为Markdown格式或JSON。
|
||||
3. 利用大模型(如GPT-4o或Qwen-Max)生成一段\*\*“表格摘要”\*\*(例如:“表1显示,实验组的生存期中位数为12个月,显著高于对照组的8个月...”)。
|
||||
4. 对这段**摘要**进行Embedding,存入vector列。
|
||||
5. 将原始表格Markdown存入PostgreSQL的jsonb列。
|
||||
6. 检索时,匹配摘要,但将原始表格Markdown作为上下文喂给LLM 30。
|
||||
|
||||
### **7.3 引文溯源(Citations)**
|
||||
|
||||
医疗RAG必须解决“幻觉”问题。
|
||||
|
||||
* **设计**:在documents表中增加metadata列(JSONB类型),存储DOI、作者、期刊名、发表日期。
|
||||
* **查询**:在RAG生成回答时,利用PostgreSQL查出的元数据,强制LLM在回答中标注引用来源(例如:“根据2023年JAMA的研究...”)。
|
||||
|
||||
## ---
|
||||
|
||||
**8\. 实施指南与云服务支持(Implementation Guide)**
|
||||
|
||||
### **8.1 云平台支持详情**
|
||||
|
||||
鉴于贵公司在中国,以下是三大云厂商的具体支持情况:
|
||||
|
||||
* **阿里云(Aliyun RDS PostgreSQL)**:
|
||||
* **版本**:PostgreSQL 14/15/16 均支持。
|
||||
* **操作**:在RDS控制台“插件管理”页面,点击安装vector插件即可。
|
||||
* **注意**:确保内核小版本是最新的,以获取HNSW索引支持。
|
||||
* **腾讯云(TencentDB for PostgreSQL)**:
|
||||
* **版本**:全面支持。腾讯云在内核层面做了向量指令加速。
|
||||
* **教程**:腾讯云官方文档中有详细的“使用pgvector构建向量检索系统”的教程 26。
|
||||
* **华为云(RDS for PostgreSQL)**:
|
||||
* 支持pgvector。华为云通常用于政企项目,如果贵公司服务于公立医院,华为云的合规属性会有加分 28。
|
||||
|
||||
### **8.2 开发教程资源**
|
||||
|
||||
* **教程丰富度**:pgvector 是目前GitHub上Star增长最快的PostgreSQL插件之一,社区资源极其丰富。
|
||||
* **推荐库**:
|
||||
* **Python**: psycopg3 或 SQLAlchemy(配合pgvector-python库)。
|
||||
* **AI框架**: **LangChain** (langchain\_postgres) 和 **LlamaIndex** 都内置了对pgvector的完美支持。你甚至不需要写SQL,只需要几行Python代码即可完成知识库的构建和检索 21。
|
||||
|
||||
**LangChain 极简示例:**
|
||||
|
||||
Python
|
||||
|
||||
from langchain\_postgres import PGVector
|
||||
from langchain\_huggingface import HuggingFaceEmbeddings
|
||||
|
||||
\# 1\. 定义连接字符串 (阿里云RDS地址)
|
||||
connection \= "postgresql+psycopg://user:pass@rm-xxx.pg.rds.aliyuncs.com:5432/med\_db"
|
||||
|
||||
\# 2\. 初始化向量库 (自动使用pgvector)
|
||||
vector\_store \= PGVector(
|
||||
embeddings=HuggingFaceEmbeddings(model\_name="shibing624/text2vec-base-chinese"),
|
||||
collection\_name="clinical\_guidelines",
|
||||
connection=connection,
|
||||
use\_jsonb=True,
|
||||
)
|
||||
|
||||
\# 3\. 添加文档
|
||||
vector\_store.add\_documents(documents)
|
||||
|
||||
\# 4\. 检索 (RAG)
|
||||
results \= vector\_store.similarity\_search("高血压的首选药物", k=5)
|
||||
|
||||
## **9\. 结论**
|
||||
|
||||
综上所述,对于贵公司——一家专注于医疗科研的AI创业公司而言,**集成PostgreSQL的pgvector插件不仅是“靠谱”的,而且是目前阶段最具性价比、最稳健的技术选型。**
|
||||
|
||||
它利用PostgreSQL强大的生态,解决了医疗领域最棘手的**混合检索**和**数据一致性**问题,同时完全满足从数千篇到数千万篇文献的扩展需求。相比于盲目引入复杂的专用向量数据库,基于PostgreSQL构建“全栈式”医疗知识库,将让贵公司的技术架构更加简洁、高效且合规。
|
||||
|
||||
#### **引用的著作**
|
||||
|
||||
1. Load vector embeddings up to 67x faster with pgvector and Amazon Aurora \- AWS, 访问时间为 一月 14, 2026, [https://aws.amazon.com/blogs/database/load-vector-embeddings-up-to-67x-faster-with-pgvector-and-amazon-aurora/](https://aws.amazon.com/blogs/database/load-vector-embeddings-up-to-67x-faster-with-pgvector-and-amazon-aurora/)
|
||||
2. Ditch the Extra Database: Simplify Your AI Stack with Managed PostgreSQL and pgvector, 访问时间为 一月 14, 2026, [https://render.com/articles/simplify-ai-stack-managed-postgresql-pgvector](https://render.com/articles/simplify-ai-stack-managed-postgresql-pgvector)
|
||||
3. Postgres Vector Search with pgvector: Benchmarks, Costs, and Reality Check \- Medium, 访问时间为 一月 14, 2026, [https://medium.com/@DataCraft-Innovations/postgres-vector-search-with-pgvector-benchmarks-costs-and-reality-check-f839a4d2b66f](https://medium.com/@DataCraft-Innovations/postgres-vector-search-with-pgvector-benchmarks-costs-and-reality-check-f839a4d2b66f)
|
||||
4. Overfitter/biobert\_embedding: Token and Sentence level embeddings from BioBERT model, 访问时间为 一月 14, 2026, [https://github.com/Overfitter/biobert\_embedding](https://github.com/Overfitter/biobert_embedding)
|
||||
5. What's new in pgvector v0.7.0 \- Supabase, 访问时间为 一月 14, 2026, [https://supabase.com/blog/pgvector-0-7-0](https://supabase.com/blog/pgvector-0-7-0)
|
||||
6. vector 0.7.0: Open-source vector similarity search for Postgres / PostgreSQL Extension Network \- PGXN, 访问时间为 一月 14, 2026, [https://pgxn.org/dist/vector/0.7.0/](https://pgxn.org/dist/vector/0.7.0/)
|
||||
7. The pgvector extension \- Neon Docs, 访问时间为 一月 14, 2026, [https://neon.com/docs/extensions/pgvector](https://neon.com/docs/extensions/pgvector)
|
||||
8. pgvector 0.7.0 Released\! \- PostgreSQL, 访问时间为 一月 14, 2026, [https://www.postgresql.org/about/news/pgvector-070-released-2852/](https://www.postgresql.org/about/news/pgvector-070-released-2852/)
|
||||
9. An early look at HNSW performance with pgvector | Jonathan Katz, 访问时间为 一月 14, 2026, [https://jkatz05.com/post/postgres/pgvector-hnsw-performance/](https://jkatz05.com/post/postgres/pgvector-hnsw-performance/)
|
||||
10. Faster similarity search performance with pgvector indexes | Google Cloud Blog, 访问时间为 一月 14, 2026, [https://cloud.google.com/blog/products/databases/faster-similarity-search-performance-with-pgvector-indexes](https://cloud.google.com/blog/products/databases/faster-similarity-search-performance-with-pgvector-indexes)
|
||||
11. How to calculate amount of RAM required for serving X N-dimensional vectors with pgvector (HNSW)? \- Stack Overflow, 访问时间为 一月 14, 2026, [https://stackoverflow.com/questions/77401874/how-to-calculate-amount-of-ram-required-for-serving-x-n-dimensional-vectors-with](https://stackoverflow.com/questions/77401874/how-to-calculate-amount-of-ram-required-for-serving-x-n-dimensional-vectors-with)
|
||||
12. pgvector/pgvector: Open-source vector similarity search for Postgres \- GitHub, 访问时间为 一月 14, 2026, [https://github.com/pgvector/pgvector](https://github.com/pgvector/pgvector)
|
||||
13. Optimize generative AI applications with pgvector indexing: A deep dive into IVFFlat and HNSW techniques | AWS Database Blog, 访问时间为 一月 14, 2026, [https://aws.amazon.com/blogs/database/optimize-generative-ai-applications-with-pgvector-indexing-a-deep-dive-into-ivfflat-and-hnsw-techniques/](https://aws.amazon.com/blogs/database/optimize-generative-ai-applications-with-pgvector-indexing-a-deep-dive-into-ivfflat-and-hnsw-techniques/)
|
||||
14. Hybrid Search in PostgreSQL: The Missing Manual | ParadeDB, 访问时间为 一月 14, 2026, [https://www.paradedb.com/blog/hybrid-search-in-postgresql-the-missing-manual](https://www.paradedb.com/blog/hybrid-search-in-postgresql-the-missing-manual)
|
||||
15. Hybrid search with PostgreSQL and pgvector \- Jonathan Katz, 访问时间为 一月 14, 2026, [https://jkatz05.com/post/postgres/hybrid-search-postgres-pgvector/](https://jkatz05.com/post/postgres/hybrid-search-postgres-pgvector/)
|
||||
16. Vector Databases vs. PostgreSQL with pg\_vector for RAG Setups \- DEV Community, 访问时间为 一月 14, 2026, [https://dev.to/simplr\_sh/vector-databases-vs-postgresql-with-pgvector-for-rag-setups-1lg2](https://dev.to/simplr_sh/vector-databases-vs-postgresql-with-pgvector-for-rag-setups-1lg2)
|
||||
17. The Case Against pgvector | Alex Jacobs, 访问时间为 一月 14, 2026, [https://alex-jacobs.com/posts/the-case-against-pgvector/](https://alex-jacobs.com/posts/the-case-against-pgvector/)
|
||||
18. Supercharging vector search performance and relevance with pgvector 0.8.0 on Amazon Aurora PostgreSQL | AWS Database Blog, 访问时间为 一月 14, 2026, [https://aws.amazon.com/blogs/database/supercharging-vector-search-performance-and-relevance-with-pgvector-0-8-0-on-amazon-aurora-postgresql/](https://aws.amazon.com/blogs/database/supercharging-vector-search-performance-and-relevance-with-pgvector-0-8-0-on-amazon-aurora-postgresql/)
|
||||
19. 访问时间为 一月 14, 2026, [https://www.instaclustr.com/education/vector-database/pgvector-key-features-tutorial-and-pros-and-cons-2026-guide/\#:\~:text=pgvector%20inherits%20PostgreSQL's%20scalability%20limits,strategies%20or%20external%20vector%20systems.](https://www.instaclustr.com/education/vector-database/pgvector-key-features-tutorial-and-pros-and-cons-2026-guide/#:~:text=pgvector%20inherits%20PostgreSQL's%20scalability%20limits,strategies%20or%20external%20vector%20systems.)
|
||||
20. Beyond PGVector: When Your Vector Database Needs a Formula 1 Upgrade \- Zilliz blog, 访问时间为 一月 14, 2026, [https://zilliz.com/blog/beyond-pgvector-when-your-vectordb-need-a-formula-one-upgrade](https://zilliz.com/blog/beyond-pgvector-when-your-vectordb-need-a-formula-one-upgrade)
|
||||
21. PGVector \- Docs by LangChain, 访问时间为 一月 14, 2026, [https://python.langchain.com/docs/integrations/vectorstores/pgvector/](https://python.langchain.com/docs/integrations/vectorstores/pgvector/)
|
||||
22. PGVectorStore \- Docs by LangChain, 访问时间为 一月 14, 2026, [https://docs.langchain.com/oss/javascript/integrations/vectorstores/pgvector](https://docs.langchain.com/oss/javascript/integrations/vectorstores/pgvector)
|
||||
23. Postgres Vector Store | LlamaIndex Python Documentation, 访问时间为 一月 14, 2026, [https://developers.llamaindex.ai/python/examples/vector\_stores/postgres/](https://developers.llamaindex.ai/python/examples/vector_stores/postgres/)
|
||||
24. How do I use the pgvector extension to perform vector similarity searches? \- ApsaraDB RDS \- Alibaba Cloud Documentation Center, 访问时间为 一月 14, 2026, [https://www.alibabacloud.com/help/en/rds/apsaradb-rds-for-postgresql/pgvector-use-guide](https://www.alibabacloud.com/help/en/rds/apsaradb-rds-for-postgresql/pgvector-use-guide)
|
||||
25. ApsaraDB RDS:AliPG minor engine version release notes (PostgreSQL 14–18) \- Alibaba Cloud, 访问时间为 一月 14, 2026, [https://www.alibabacloud.com/help/en/rds/apsaradb-rds-for-postgresql/release-notes-for-alipg](https://www.alibabacloud.com/help/en/rds/apsaradb-rds-for-postgresql/release-notes-for-alipg)
|
||||
26. Overview \- Tencent Cloud, 访问时间为 一月 14, 2026, [https://www.tencentcloud.com/document/product/409/47734](https://www.tencentcloud.com/document/product/409/47734)
|
||||
27. Kernel Version Release Notes \- Tencent Cloud, 访问时间为 一月 14, 2026, [https://www.tencentcloud.com/document/product/409/44365](https://www.tencentcloud.com/document/product/409/44365)
|
||||
28. pgvector \- Cloud Service Help Center \- Huawei, 访问时间为 一月 14, 2026, [https://doc.hcs.huawei.com/usermanual/rds/rds\_09\_0062.html](https://doc.hcs.huawei.com/usermanual/rds/rds_09_0062.html)
|
||||
29. pgvector\_Extension Management\_User Guide\_Relational Database Service\_RDS for PostgreSQL-Huawei Cloud, 访问时间为 一月 14, 2026, [https://support.huaweicloud.com/intl/en-us/usermanual-rds-pg/rds\_09\_0062.html](https://support.huaweicloud.com/intl/en-us/usermanual-rds-pg/rds_09_0062.html)
|
||||
30. Mastering RAG: Precision techniques for table-heavy documents \- Kx Systems, 访问时间为 一月 14, 2026, [https://kx.com/blog/mastering-rag-precision-techniques-for-table-heavy-documents/](https://kx.com/blog/mastering-rag-precision-techniques-for-table-heavy-documents/)
|
||||
31. Knowledge Table — Multi-Document RAG (Extraction & Memory) | by Chia Jeng Yang, 访问时间为 一月 14, 2026, [https://medium.com/enterprise-rag/knowledge-table-multi-document-rag-extraction-memory-ec08450e858f](https://medium.com/enterprise-rag/knowledge-table-multi-document-rag-extraction-memory-ec08450e858f)
|
||||
32. Accelerate HNSW indexing and searching with pgvector on Amazon Aurora PostgreSQL-compatible edition and Amazon RDS for PostgreSQL | AWS Database Blog, 访问时间为 一月 14, 2026, [https://aws.amazon.com/blogs/database/accelerate-hnsw-indexing-and-searching-with-pgvector-on-amazon-aurora-postgresql-compatible-edition-and-amazon-rds-for-postgresql/](https://aws.amazon.com/blogs/database/accelerate-hnsw-indexing-and-searching-with-pgvector-on-amazon-aurora-postgresql-compatible-edition-and-amazon-rds-for-postgresql/)
|
||||
672
docs/03-业务模块/PKB-个人知识库/04-开发计划/01-Dify替换为pgvector开发计划.md
Normal file
672
docs/03-业务模块/PKB-个人知识库/04-开发计划/01-Dify替换为pgvector开发计划.md
Normal file
@@ -0,0 +1,672 @@
|
||||
# PKB 模块:Dify 替换为 pgvector 开发计划
|
||||
|
||||
> **文档版本:** v1.0
|
||||
> **创建日期:** 2026-01-19
|
||||
> **预计工期:** 2 周(10个工作日)
|
||||
> **前置条件:** ✅ pgvector 0.8.1 已安装
|
||||
> **目标:** 用 PostgreSQL + pgvector 原生 RAG 替代 Dify,实现 R-C-R-G 混合检索架构
|
||||
|
||||
---
|
||||
|
||||
## 📊 整体难度评估
|
||||
|
||||
### 总体评估:⭐⭐⭐ 中等难度
|
||||
|
||||
| 评估维度 | 难度 | 说明 |
|
||||
|----------|------|------|
|
||||
| **数据库设计** | ⭐⭐ 低 | Prisma schema 直接写,pgvector 已就绪 |
|
||||
| **Embedding 服务** | ⭐⭐ 低 | 调用阿里云 API,简单封装 |
|
||||
| **文档切片** | ⭐⭐ 低 | 成熟方案,RecursiveCharacterTextSplitter |
|
||||
| **全要素提取** | ⭐⭐⭐ 中 | 需要调优 Prompt,处理 JSON 异常 |
|
||||
| **向量检索** | ⭐⭐⭐ 中 | pgvector SQL 语法需要学习 |
|
||||
| **混合检索(RRF)** | ⭐⭐⭐ 中 | 核心算法,需要调优 |
|
||||
| **服务替换** | ⭐⭐⭐⭐ 中高 | 需要保持 API 兼容,测试覆盖 |
|
||||
| **数据迁移** | ⭐⭐⭐ 中 | 现有文档需重新向量化 |
|
||||
|
||||
**综合评估**:技术上完全可行,主要挑战在于**服务替换的平滑过渡**和**检索效果调优**。
|
||||
|
||||
---
|
||||
|
||||
## 🔥 核心挑战分析
|
||||
|
||||
### 挑战 1:混合检索效果调优 🔴 高风险
|
||||
|
||||
**问题描述**:
|
||||
- 替换 Dify 后,检索效果可能下降
|
||||
- 需要调优向量检索 + 关键词检索的权重
|
||||
- RRF 参数(k 值)需要实验确定
|
||||
|
||||
**应对策略**:
|
||||
- 准备测试数据集(100+ 查询)
|
||||
- 建立效果评估指标(Recall@K, MRR)
|
||||
- 先用小批量数据验证,再全量迁移
|
||||
|
||||
**预留时间**:2 天专门用于调优
|
||||
|
||||
---
|
||||
|
||||
### 挑战 2:全要素提取的准确性 🟡 中风险
|
||||
|
||||
**问题描述**:
|
||||
- LLM 提取的 JSON 可能格式错误
|
||||
- PICO、用药方案等字段提取不完整
|
||||
- 不同类型文献(RCT/综述/病例)提取策略不同
|
||||
|
||||
**应对策略**:
|
||||
- 三层 JSON 解析容错(直接解析 → 提取代码块 → LLM修复)
|
||||
- 字段级校验(必填字段、类型校验)
|
||||
- 分文献类型设计 Prompt
|
||||
|
||||
**预留时间**:1 天用于 Prompt 调优
|
||||
|
||||
---
|
||||
|
||||
### 挑战 3:服务替换的兼容性 🟡 中风险
|
||||
|
||||
**问题描述**:
|
||||
- 需要保持 API 接口不变(前端零修改)
|
||||
- `searchKnowledgeBase()` 返回格式需兼容
|
||||
- 文档上传流程需要无缝切换
|
||||
|
||||
**应对策略**:
|
||||
- 定义适配层,转换返回格式
|
||||
- 新旧服务并行运行,灰度切换
|
||||
- 充分测试所有使用场景
|
||||
|
||||
**预留时间**:1 天专门用于兼容性测试
|
||||
|
||||
---
|
||||
|
||||
### 挑战 4:向量数据的批量处理 🟢 低风险
|
||||
|
||||
**问题描述**:
|
||||
- 批量 Embedding 调用需要控制并发
|
||||
- 阿里云 API 有 QPS 限制
|
||||
- 大文档切片后向量较多
|
||||
|
||||
**应对策略**:
|
||||
- 使用 p-queue 控制并发(固定 3 并发)
|
||||
- 批量 Embedding(每次最多 25 条)
|
||||
- 增量处理,支持断点续传
|
||||
|
||||
---
|
||||
|
||||
## 📅 详细开发计划
|
||||
|
||||
### 总览时间线
|
||||
|
||||
```
|
||||
Week 1: 基础设施 + 核心服务开发
|
||||
├── Day 1: 数据库设计 + Prisma 迁移
|
||||
├── Day 2: Embedding 服务 + 切片服务
|
||||
├── Day 3: 全要素提取服务(Prompt 调优)
|
||||
├── Day 4: 向量检索服务(pgvector SQL)
|
||||
├── Day 5: 混合检索 + RRF 融合
|
||||
|
||||
Week 2: 服务替换 + 测试 + 迁移
|
||||
├── Day 6: 修改 knowledgeBaseService(检索替换)
|
||||
├── Day 7: 修改 documentService(上传替换)
|
||||
├── Day 8: 集成测试 + 效果调优
|
||||
├── Day 9: 数据迁移(现有文档向量化)
|
||||
├── Day 10: 清理 + 文档 + 上线
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 1:数据库设计 + Prisma 迁移
|
||||
|
||||
**目标**:创建向量存储的数据表
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 设计 `EkbDocument` 表(增强文档,含临床数据 JSONB 字段)
|
||||
- [ ] 设计 `EkbChunk` 表(向量切片,含 pgvector 字段)
|
||||
- [ ] 编写 Prisma schema
|
||||
- [ ] 运行 `prisma migrate dev`
|
||||
- [ ] 创建 HNSW 索引(手动 SQL)
|
||||
- [ ] 验证向量插入和查询
|
||||
|
||||
**交付物**:
|
||||
- `prisma/schema.prisma` 更新
|
||||
- `migrations/xxx_add_ekb_tables.sql`
|
||||
- 索引创建脚本
|
||||
|
||||
**预计工时**:4-6 小时
|
||||
|
||||
**关键代码**:
|
||||
|
||||
```prisma
|
||||
// schema.prisma
|
||||
|
||||
model EkbDocument {
|
||||
id String @id @default(uuid())
|
||||
kbId String
|
||||
userId String
|
||||
|
||||
// 基础信息
|
||||
filename String
|
||||
fileType String
|
||||
fileSizeBytes BigInt
|
||||
fileUrl String // 原始 PDF 的 OSS 地址
|
||||
extractedText String? @db.Text // 解析后的 Markdown/文本
|
||||
|
||||
// 临床数据(JSONB)
|
||||
pico Json?
|
||||
studyDesign Json?
|
||||
regimen Json?
|
||||
safety Json?
|
||||
criteria Json?
|
||||
endpoints Json?
|
||||
|
||||
// 状态
|
||||
status String @default("pending")
|
||||
errorMessage String? @db.Text
|
||||
|
||||
chunks EkbChunk[]
|
||||
knowledgeBase KnowledgeBase @relation(fields: [kbId], references: [id], onDelete: Cascade)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([kbId])
|
||||
@@index([status])
|
||||
@@schema("pkb_schema")
|
||||
}
|
||||
|
||||
model EkbChunk {
|
||||
id String @id @default(uuid())
|
||||
documentId String
|
||||
|
||||
content String @db.Text
|
||||
pageNumber Int?
|
||||
sectionType String?
|
||||
|
||||
// pgvector 字段(需要手动创建)
|
||||
embedding Unsupported("vector(1024)")?
|
||||
|
||||
document EkbDocument @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([documentId])
|
||||
@@schema("pkb_schema")
|
||||
}
|
||||
```
|
||||
|
||||
**手动 SQL(创建索引)**:
|
||||
|
||||
```sql
|
||||
-- 创建 HNSW 索引
|
||||
CREATE INDEX IF NOT EXISTS ekb_chunk_embedding_idx
|
||||
ON "pkb_schema"."EkbChunk"
|
||||
USING hnsw (embedding vector_cosine_ops)
|
||||
WITH (m = 16, ef_construction = 64);
|
||||
|
||||
-- 创建全文检索索引
|
||||
CREATE INDEX IF NOT EXISTS ekb_chunk_content_idx
|
||||
ON "pkb_schema"."EkbChunk"
|
||||
USING gin (to_tsvector('simple', content));
|
||||
|
||||
-- 创建 JSONB GIN 索引(用于临床数据查询)
|
||||
CREATE INDEX IF NOT EXISTS ekb_document_pico_idx
|
||||
ON "pkb_schema"."EkbDocument"
|
||||
USING gin (pico);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ekb_document_safety_idx
|
||||
ON "pkb_schema"."EkbDocument"
|
||||
USING gin (safety);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 2:Embedding 服务 + 切片服务
|
||||
|
||||
**目标**:实现文本向量化和文档切片
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 创建 `EmbeddingService.ts`(阿里云 text-embedding-v3)
|
||||
- [ ] 创建 `ChunkService.ts`(RecursiveCharacterTextSplitter)
|
||||
- [ ] 单元测试:Embedding API 调用
|
||||
- [ ] 单元测试:切片效果验证
|
||||
- [ ] 环境变量配置(DASHSCOPE_API_KEY)
|
||||
|
||||
**交付物**:
|
||||
- `backend/src/common/rag/EmbeddingService.ts`
|
||||
- `backend/src/common/rag/ChunkService.ts`
|
||||
- 单元测试文件
|
||||
|
||||
**预计工时**:4-6 小时
|
||||
|
||||
**关键代码**:
|
||||
|
||||
```typescript
|
||||
// EmbeddingService.ts
|
||||
export class EmbeddingService {
|
||||
private apiKey: string;
|
||||
private baseUrl = 'https://dashscope.aliyuncs.com/api/v1/services/embeddings/text-embedding/text-embedding';
|
||||
|
||||
async embed(text: string): Promise<number[]> { ... }
|
||||
async embedBatch(texts: string[]): Promise<number[][]> { ... }
|
||||
async embedQuery(query: string): Promise<number[]> { ... }
|
||||
}
|
||||
|
||||
// ChunkService.ts
|
||||
export class ChunkService {
|
||||
splitDocument(
|
||||
text: string,
|
||||
options: { chunkSize: number; chunkOverlap: number }
|
||||
): Chunk[] { ... }
|
||||
|
||||
detectSections(text: string): Section[] { ... }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 3:全要素提取服务
|
||||
|
||||
**目标**:实现 PICO、用药方案等临床数据的 AI 提取
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 创建 `ClinicalExtractionService.ts`
|
||||
- [ ] 设计提取 Prompt(参考 EKB 方案)
|
||||
- [ ] 实现三层 JSON 解析容错
|
||||
- [ ] 测试不同类型文献(RCT、综述、病例)
|
||||
- [ ] Prompt 调优(提高提取准确率)
|
||||
|
||||
**交付物**:
|
||||
- `backend/src/modules/pkb/services/ClinicalExtractionService.ts`
|
||||
- `backend/prompts/clinical_extraction.txt`
|
||||
- 测试用例
|
||||
|
||||
**预计工时**:6-8 小时(含 Prompt 调优)
|
||||
|
||||
**关键 Prompt**:
|
||||
|
||||
```
|
||||
你是一个医学数据专家。请阅读这篇文献,严格按照以下 JSON 格式提取关键信息。
|
||||
如果文中未提及,字段留空(null)。
|
||||
|
||||
提取字段:
|
||||
1. pico: { "P": "患者人群", "I": "干预措施", "C": "对照", "O": "结局指标" }
|
||||
2. studyDesign: { "design": "研究类型", "sampleSize": 数字, "blinding": "盲法" }
|
||||
3. regimen: [{ "drug": "药物名", "dose": "剂量", "frequency": "频率" }]
|
||||
4. safety: { "ae_all": ["不良反应列表"], "ae_grade34": ["严重不良反应"] }
|
||||
5. criteria: { "inclusion": ["纳入标准"], "exclusion": ["排除标准"] }
|
||||
6. endpoints: { "primary": ["主要终点"], "secondary": ["次要终点"] }
|
||||
|
||||
输出必须是纯 JSON,不要有任何前言或后缀。
|
||||
|
||||
---
|
||||
文献内容:
|
||||
{{fullText}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 4:向量检索服务(pgvector SQL)
|
||||
|
||||
**目标**:实现基于 pgvector 的向量检索
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 创建 `VectorSearchService.ts`
|
||||
- [ ] 实现向量检索(余弦相似度)
|
||||
- [ ] 实现关键词检索(PostgreSQL FTS)
|
||||
- [ ] 测试检索性能(1000+ 向量)
|
||||
- [ ] 优化查询(索引使用验证)
|
||||
|
||||
**交付物**:
|
||||
- `backend/src/common/rag/VectorSearchService.ts`
|
||||
- 性能测试报告
|
||||
|
||||
**预计工时**:6 小时
|
||||
|
||||
**关键 SQL**:
|
||||
|
||||
```sql
|
||||
-- 向量检索
|
||||
SELECT
|
||||
c.id,
|
||||
c.content,
|
||||
d.filename,
|
||||
1 - (c.embedding <=> $1::vector) as score
|
||||
FROM "pkb_schema"."EkbChunk" c
|
||||
JOIN "pkb_schema"."EkbDocument" d ON c."documentId" = d.id
|
||||
WHERE d."kbId" = $2
|
||||
ORDER BY c.embedding <=> $1::vector
|
||||
LIMIT $3;
|
||||
|
||||
-- 关键词检索
|
||||
SELECT
|
||||
c.id,
|
||||
c.content,
|
||||
d.filename,
|
||||
ts_rank_cd(to_tsvector('simple', c.content), plainto_tsquery('simple', $1)) as score
|
||||
FROM "pkb_schema"."EkbChunk" c
|
||||
JOIN "pkb_schema"."EkbDocument" d ON c."documentId" = d.id
|
||||
WHERE d."kbId" = $2
|
||||
AND to_tsvector('simple', c.content) @@ plainto_tsquery('simple', $1)
|
||||
ORDER BY score DESC
|
||||
LIMIT $3;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 5:混合检索 + RRF 融合
|
||||
|
||||
**目标**:实现 R-C-R-G 架构中的混合检索
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 实现 RRF(Reciprocal Rank Fusion)算法
|
||||
- [ ] 实现并发三路检索(向量 + 关键词 + SQL 筛选)
|
||||
- [ ] 集成 Rerank API(可选,qwen-rerank)
|
||||
- [ ] 效果评估(对比 Dify 检索)
|
||||
- [ ] 参数调优(k 值、权重)
|
||||
|
||||
**交付物**:
|
||||
- RRF 融合模块
|
||||
- 效果评估报告
|
||||
|
||||
**预计工时**:6-8 小时
|
||||
|
||||
**RRF 算法**:
|
||||
|
||||
```typescript
|
||||
function rrfFusion(
|
||||
vectorResults: Result[],
|
||||
keywordResults: Result[],
|
||||
k: number = 60 // RRF 常数,通常取 60
|
||||
): Result[] {
|
||||
const scoreMap = new Map<string, number>();
|
||||
|
||||
// 计算 RRF 分数
|
||||
vectorResults.forEach((r, idx) => {
|
||||
const rrfScore = 1 / (k + idx + 1);
|
||||
scoreMap.set(r.id, (scoreMap.get(r.id) || 0) + rrfScore);
|
||||
});
|
||||
|
||||
keywordResults.forEach((r, idx) => {
|
||||
const rrfScore = 1 / (k + idx + 1);
|
||||
scoreMap.set(r.id, (scoreMap.get(r.id) || 0) + rrfScore);
|
||||
});
|
||||
|
||||
// 排序返回
|
||||
return Array.from(scoreMap.entries())
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.map(([id, score]) => ({ id, score }));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 6:修改 knowledgeBaseService(检索替换)
|
||||
|
||||
**目标**:替换 Dify 检索为 pgvector 检索
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 修改 `searchKnowledgeBase()` 函数
|
||||
- [ ] 移除 `difyClient.retrieveKnowledge()` 调用
|
||||
- [ ] 使用 `vectorSearchService.hybridSearch()`
|
||||
- [ ] 保持返回格式兼容(前端零修改)
|
||||
- [ ] 单元测试
|
||||
|
||||
**交付物**:
|
||||
- 更新后的 `knowledgeBaseService.ts`
|
||||
|
||||
**预计工时**:4 小时
|
||||
|
||||
**关键修改**:
|
||||
|
||||
```typescript
|
||||
// 修改前
|
||||
const results = await difyClient.retrieveKnowledge(
|
||||
knowledgeBase.difyDatasetId,
|
||||
query,
|
||||
{ retrieval_model: { search_method: 'semantic_search', top_k: topK } }
|
||||
);
|
||||
|
||||
// 修改后
|
||||
const searchResults = await vectorSearchService.hybridSearch(kbId, query, topK);
|
||||
|
||||
// 格式转换(保持兼容)
|
||||
return {
|
||||
query: { content: query },
|
||||
records: searchResults.map((r, idx) => ({
|
||||
segment_id: r.id,
|
||||
document_id: r.documentId,
|
||||
document_name: r.documentName,
|
||||
position: idx + 1,
|
||||
score: r.score,
|
||||
content: r.content,
|
||||
})),
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 7:修改 documentService(上传替换)
|
||||
|
||||
**目标**:替换 Dify 上传流程为本地向量化流程
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 修改 `uploadDocument()` 函数
|
||||
- [ ] 移除 `difyClient.uploadDocumentDirectly()` 调用
|
||||
- [ ] 实现本地处理流程(提取 → 切片 → 向量化)
|
||||
- [ ] 移除 Dify 状态轮询逻辑
|
||||
- [ ] 实现自己的异步处理和状态更新
|
||||
- [ ] 单元测试
|
||||
|
||||
**交付物**:
|
||||
- 更新后的 `documentService.ts`
|
||||
|
||||
**预计工时**:6 小时
|
||||
|
||||
---
|
||||
|
||||
### Day 8:集成测试 + 效果调优
|
||||
|
||||
**目标**:端到端测试,确保功能正常
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 前端测试:创建知识库
|
||||
- [ ] 前端测试:上传文档
|
||||
- [ ] 前端测试:RAG 检索问答
|
||||
- [ ] 效果对比:Dify vs pgvector 检索质量
|
||||
- [ ] 性能测试:检索延迟
|
||||
- [ ] Bug 修复
|
||||
|
||||
**交付物**:
|
||||
- 测试报告
|
||||
- Bug 修复记录
|
||||
|
||||
**预计工时**:8 小时
|
||||
|
||||
---
|
||||
|
||||
### Day 9:数据迁移(现有文档向量化)
|
||||
|
||||
**目标**:将现有知识库文档迁移到新表并向量化
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 编写迁移脚本(Document → EkbDocument)
|
||||
- [ ] 批量向量化现有文档
|
||||
- [ ] 验证迁移完整性
|
||||
- [ ] 验证检索效果
|
||||
|
||||
**交付物**:
|
||||
- `scripts/migrate-to-ekb.ts`
|
||||
- 迁移日志
|
||||
|
||||
**预计工时**:6 小时
|
||||
|
||||
**迁移脚本**:
|
||||
|
||||
```typescript
|
||||
// scripts/migrate-to-ekb.ts
|
||||
async function migrateDocuments() {
|
||||
// 1. 获取所有现有文档
|
||||
const documents = await prisma.document.findMany({
|
||||
where: { status: 'completed', extractedText: { not: null } },
|
||||
});
|
||||
|
||||
console.log(`Found ${documents.length} documents to migrate`);
|
||||
|
||||
// 2. 逐个迁移
|
||||
for (const doc of documents) {
|
||||
try {
|
||||
// 创建 EkbDocument
|
||||
const ekbDoc = await prisma.ekbDocument.create({
|
||||
data: {
|
||||
kbId: doc.kbId,
|
||||
userId: doc.userId,
|
||||
filename: doc.filename,
|
||||
fileType: doc.fileType,
|
||||
fileSizeBytes: doc.fileSizeBytes,
|
||||
extractedText: doc.extractedText,
|
||||
status: 'embedding',
|
||||
},
|
||||
});
|
||||
|
||||
// 切片
|
||||
const chunks = chunkService.splitDocument(doc.extractedText!);
|
||||
|
||||
// 向量化
|
||||
const embeddings = await embeddingService.embedBatch(
|
||||
chunks.map(c => c.content)
|
||||
);
|
||||
|
||||
// 存入数据库
|
||||
// ...
|
||||
|
||||
console.log(`✅ Migrated: ${doc.filename}`);
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed: ${doc.filename}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Day 10:清理 + 文档 + 上线
|
||||
|
||||
**目标**:清理遗留代码,更新文档,正式上线
|
||||
|
||||
**任务清单**:
|
||||
- [ ] 删除 `DifyClient.ts`
|
||||
- [ ] 删除 `difyDatasetId` 字段(可选,下个版本)
|
||||
- [ ] 删除 `difyDocumentId` 字段(可选,下个版本)
|
||||
- [ ] 更新 `00-模块当前状态与开发指南.md`
|
||||
- [ ] 更新环境变量文档
|
||||
- [ ] 代码 Review
|
||||
- [ ] 合并到主分支
|
||||
|
||||
**交付物**:
|
||||
- 更新后的文档
|
||||
- 清理后的代码
|
||||
|
||||
**预计工时**:4 小时
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 风险评估与应对
|
||||
|
||||
### 风险矩阵
|
||||
|
||||
| 风险 | 概率 | 影响 | 等级 | 应对措施 |
|
||||
|------|------|------|------|----------|
|
||||
| 检索效果下降 | 中 | 高 | 🔴 | 效果评估 + 参数调优 + 回滚方案 |
|
||||
| API 兼容性问题 | 低 | 高 | 🟡 | 格式转换层 + 充分测试 |
|
||||
| Embedding API 限流 | 中 | 中 | 🟡 | 并发控制 + 重试机制 |
|
||||
| 迁移数据丢失 | 低 | 高 | 🟡 | 备份 + 验证 + 回滚 |
|
||||
| 性能下降 | 低 | 中 | 🟢 | 索引优化 + 缓存 |
|
||||
|
||||
### 回滚方案
|
||||
|
||||
如果新方案效果不理想,可以:
|
||||
1. 保留 `difyDatasetId` 字段,随时切回 Dify
|
||||
2. 新旧服务通过 Feature Flag 切换
|
||||
3. 灰度发布:先 10% 用户使用 pgvector
|
||||
|
||||
---
|
||||
|
||||
## 📊 资源需求
|
||||
|
||||
### 人力资源
|
||||
|
||||
| 角色 | 工作量 | 说明 |
|
||||
|------|--------|------|
|
||||
| 后端开发 | 10 人天 | 核心开发 |
|
||||
| 测试 | 2 人天 | 集成测试 + 效果评估 |
|
||||
| **总计** | **12 人天** | 约 2 周 |
|
||||
|
||||
### 技术资源
|
||||
|
||||
| 资源 | 用途 | 成本 |
|
||||
|------|------|------|
|
||||
| 阿里云 DashScope | Embedding API | ~¥50/月 |
|
||||
| 阿里云 DashScope | Rerank API(可选) | ~¥20/月 |
|
||||
| PostgreSQL | 已有 | ¥0 |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验收标准
|
||||
|
||||
### 功能验收
|
||||
|
||||
- [ ] 创建知识库:不依赖 Dify,直接创建
|
||||
- [ ] 上传文档:本地处理 + 向量化
|
||||
- [ ] RAG 检索:混合检索效果 ≥ Dify
|
||||
- [ ] 全文阅读模式:正常工作
|
||||
- [ ] 批处理模式:正常工作
|
||||
|
||||
### 性能验收
|
||||
|
||||
- [ ] 检索延迟:< 500ms(95 分位)
|
||||
- [ ] 上传处理:< 60s/文档(平均)
|
||||
- [ ] 向量化吞吐:> 100 文档/小时
|
||||
|
||||
### 质量验收
|
||||
|
||||
- [ ] 检索召回率:≥ 80%(测试集)
|
||||
- [ ] 无 Dify 相关代码残留
|
||||
- [ ] 文档更新完整
|
||||
|
||||
---
|
||||
|
||||
## 📝 附录
|
||||
|
||||
### A. 相关文档
|
||||
|
||||
- [企业级医学知识库综合技术解决方案 V2](../00-系统设计/企业级医学知识库_综合技术解决方案%20V2.md)
|
||||
- [PostgreSQL与pgvector深度应用分析](../00-系统设计/医疗科研AI系统架构评估报告:PostgreSQL与pgvector在RAG及知识库中的深度应用分析.md)
|
||||
- [PKB模块当前状态](../00-模块当前状态与开发指南.md)
|
||||
|
||||
### B. 环境变量配置
|
||||
|
||||
```bash
|
||||
# .env 新增
|
||||
DASHSCOPE_API_KEY=sk-xxx # 阿里云 DashScope API Key
|
||||
EMBEDDING_MODEL=text-embedding-v3 # Embedding 模型
|
||||
EMBEDDING_DIMENSION=1024 # 向量维度
|
||||
RERANK_MODEL=gte-rerank # Rerank 模型(可选)
|
||||
```
|
||||
|
||||
### C. 依赖更新
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"dependencies": {
|
||||
// 新增
|
||||
"langchain": "^0.1.0", // 可选,用于切片
|
||||
"p-queue": "^8.0.0" // 并发控制
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**文档维护**:PKB 模块开发团队
|
||||
**最后更新**:2026-01-19
|
||||
**下次更新**:开发完成后更新进度
|
||||
|
||||
@@ -368,3 +368,4 @@ const newResults = resultsData.map((docResult: any) => ({
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -241,3 +241,4 @@ const chatApi = axios.create({
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -776,5 +776,6 @@ docker exec redcap-apache php /tmp/create-redcap-password.php
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -158,5 +158,6 @@ AIclinicalresearch/redcap-docker-dev/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user