diff --git a/docs/02-通用能力层/03-RAG引擎/01-知识库引擎架构设计.md b/docs/02-通用能力层/03-RAG引擎/01-知识库引擎架构设计.md new file mode 100644 index 00000000..f6618591 --- /dev/null +++ b/docs/02-通用能力层/03-RAG引擎/01-知识库引擎架构设计.md @@ -0,0 +1,676 @@ +# 知识库引擎架构设计 + +> **文档版本:** v1.1 +> **创建日期:** 2026-01-20 +> **最后更新:** 2026-01-20 +> **能力定位:** 通用能力层 +> **状态:** 🔄 升级中(Dify → PostgreSQL + pgvector) +> **核心原则:** 提供基础能力(乐高积木),不做策略选择 + +--- + +## 📋 概述 + +### 能力定位 + +知识库引擎是平台的**核心通用能力**,提供知识库相关的**基础能力(乐高积木)**,供业务模块根据场景自由组合。 + +### ⭐ 核心设计原则 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ │ +│ ✅ 提供基础能力(乐高积木) │ +│ ❌ 不做策略选择(组装方案由业务模块决定) │ +│ │ +│ 原因: │ +│ • 不同业务场景需要不同的知识库使用策略 │ +│ • 小知识库(10个文件)可能直接全文塞给 LLM 更好 │ +│ • 大知识库(1000+文件)必须用 RAG 向量检索 │ +│ • 有的场景需要 摘要筛选 → Top-K 全文 的两阶段策略 │ +│ • 策略选择是业务逻辑,不是基础设施 │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 基础能力清单 + +| 能力分类 | 基础能力 | 说明 | +|----------|----------|------| +| **文档入库** | `ingestDocument()` | 文档解析 → 切片 → 向量化 → 存储 | +| | `ingestBatch()` | 批量入库 | +| **全文获取** | `getDocumentFullText()` | 获取单个文档全文 | +| | `getAllDocumentsText()` | 获取知识库所有文档全文 | +| **摘要获取** | `getDocumentSummary()` | 获取单个文档摘要 | +| | `getAllDocumentsSummaries()` | 获取知识库所有文档摘要 | +| **向量检索** | `vectorSearch()` | 基于向量的语义检索 | +| **关键词检索** | `keywordSearch()` | 基于 PostgreSQL FTS 的关键词检索 | +| **混合检索** | `hybridSearch()` | 向量 + 关键词 + RRF 融合 | +| **管理操作** | `deleteDocument()` | 删除文档 | +| | `clearKnowledgeBase()` | 清空知识库 | + +> ⚠️ **注意**:不提供 `chat()` 方法!问答策略由业务模块根据场景决定。 + +--- + +## 🎯 业务模块策略选择 + +知识库引擎提供基础能力,业务模块根据场景自由组合: + +### 策略示例 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 业务模块层(策略选择) │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ PKB 个人知识库 │ +│ ├─ 小知识库(< 20 文档)→ 全文模式 │ +│ │ getAllDocumentsText() → 直接塞给 LLM │ +│ └─ 大知识库(100+ 文档)→ RAG 模式 │ +│ hybridSearch() → 检索 Top-K → LLM 回答 │ +│ │ +│ AIA AI智能问答 │ +│ └─ 摘要筛选 + Top-K 全文 │ +│ getAllDocumentsSummaries() → LLM 筛选 Top 5 │ +│ → getDocumentFullText() × 5 → LLM 回答 │ +│ │ +│ ASL 智能文献 │ +│ └─ 向量检索 + Rerank │ +│ vectorSearch(topK=50) → Rerank(topK=10) → 返回 │ +│ │ +│ RVW 稿件审查 │ +│ └─ 全文比对查重 │ +│ getAllDocumentsText() → 逐篇相似度计算 │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ 通用能力层(提供积木) │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ 全文获取 │ │ 摘要获取 │ │ 向量检索 │ │ 关键词检索│ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ 文档入库 │ │ 混合检索 │ │ 文档删除 │ │ +│ └──────────┘ └──────────┘ └──────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 代码示例 + +#### 场景 1:PKB 小知识库(10个文件)→ 全文模式 + +```typescript +// PKB 模块:小知识库直接全文 +async function pkbSmallKbChat(kbId: string, query: string) { + const kbEngine = new KnowledgeBaseEngine(prisma); + + // 获取所有文档全文 + const docs = await kbEngine.getAllDocumentsText(kbId); + + // 直接塞给 LLM + const context = docs.map(d => `## ${d.filename}\n${d.extractedText}`).join('\n\n---\n\n'); + + const systemPrompt = `你是医学专家。以下是知识库的完整内容:\n\n${context}`; + return llmChat(systemPrompt, query); +} +``` + +#### 场景 2:AIA 摘要筛选 + Top-K 全文 + +```typescript +// AIA 模块:摘要筛选 + 全文精读 +async function aiaSmartChat(kbIds: string[], query: string) { + const kbEngine = new KnowledgeBaseEngine(prisma); + + // 1. 获取所有文档摘要 + const summaries = await kbEngine.getAllDocumentsSummaries(kbIds); + + // 2. LLM 筛选最相关的 Top 5 文档 + const topDocIds = await llmSelectTopK(summaries, query, 5); + + // 3. 获取 Top 5 文档全文 + const fullTexts = await Promise.all( + topDocIds.map(id => kbEngine.getDocumentFullText(id)) + ); + + // 4. 基于全文回答 + const context = fullTexts.map(d => `## ${d.filename}\n${d.text}`).join('\n\n'); + return llmChat(context, query); +} +``` + +#### 场景 3:ASL 大规模文献检索 + +```typescript +// ASL 模块:向量检索 + Rerank +async function aslLiteratureSearch(kbIds: string[], query: string) { + const kbEngine = new KnowledgeBaseEngine(prisma); + + // 1. 向量检索 Top 50 + const candidates = await kbEngine.vectorSearch(kbIds, query, 50); + + // 2. Rerank 精排 Top 10 + const reranked = await rerankService.rerank(candidates, query, 10); + + // 3. 返回带引用的结果 + return reranked.map(r => ({ + content: r.content, + source: r.documentName, + score: r.score, + })); +} +``` + +#### 场景 4:RVW 稿件查重 + +```typescript +// RVW 模块:全文比对 +async function rvwPlagiarismCheck(manuscriptText: string, kbId: string) { + const kbEngine = new KnowledgeBaseEngine(prisma); + + // 获取所有文献全文 + const docs = await kbEngine.getAllDocumentsText(kbId); + + // 逐篇比对相似度 + const similarities = docs.map(d => ({ + document: d, + similarity: calculateTextSimilarity(manuscriptText, d.extractedText), + })); + + // 返回可疑重复段落 + return similarities + .filter(s => s.similarity > 0.3) + .sort((a, b) => b.similarity - a.similarity); +} +``` + +--- + +## 🏗️ 整体架构 + +### 架构图 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 业务模块层(策略选择) │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ PKB │ │ AIA │ │ ASL │ │ RVW │ │ +│ │ 全文/RAG │ │摘要+全文 │ │向量+Rerank│ │ 全文比对 │ │ +│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ +│ │ │ │ │ │ +│ │ 根据场景自由组合基础能力 │ │ +│ └────────────┴────────────┴────────────┘ │ +│ │ │ +│ ▼ │ +├─────────────────────────────────────────────────────────────────┤ +│ 知识库引擎(通用能力层 - 提供积木) │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ KnowledgeBaseEngine │ │ +│ │ 提供基础能力,不做策略选择 │ │ +│ ├─────────────────────────────────────────────────────────┤ │ +│ │ │ │ +│ │ ┌────────────────────────────────────────────────┐ │ │ +│ │ │ 文档入库能力 │ │ │ +│ │ │ ingestDocument() / ingestBatch() │ │ │ +│ │ └────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌────────────────────────────────────────────────┐ │ │ +│ │ │ 内容获取能力 │ │ │ +│ │ │ getDocumentFullText() / getAllDocumentsText() │ │ │ +│ │ │ getDocumentSummary() / getAllDocumentsSummaries()│ │ │ +│ │ └────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌────────────────────────────────────────────────┐ │ │ +│ │ │ 检索能力 │ │ │ +│ │ │ vectorSearch() / keywordSearch() / hybridSearch()│ │ │ +│ │ └────────────────────────────────────────────────┘ │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ 文档处理引擎(独立通用能力) │ │ +│ │ PDF/Word/Excel/PPT → Markdown │ │ +│ └─────────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────────┤ +│ 数据存储层 │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ PostgreSQL + pgvector (Postgres-Only) │ │ +│ │ ┌───────────────┐ ┌───────────────────────────┐ │ │ +│ │ │ EkbDocument │ │ EkbChunk │ │ │ +│ │ │ (文档 + JSONB)│───>│ (切片 + vector(1024)) │ │ │ +│ │ └───────────────┘ └───────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📦 代码结构 + +### 目录规划 + +``` +backend/src/common/rag/ +├── index.ts # 统一导出 +├── KnowledgeBaseEngine.ts # 统一入口类(基础能力) +│ +├── services/ +│ ├── ChunkService.ts # 文档切片服务 +│ ├── EmbeddingService.ts # 向量化服务(阿里云 DashScope) +│ ├── SummaryService.ts # 摘要生成服务 +│ ├── VectorSearchService.ts # 向量检索服务 +│ ├── KeywordSearchService.ts # 关键词检索服务(PostgreSQL FTS) +│ ├── HybridSearchService.ts # 混合检索服务(RRF 融合) +│ └── ClinicalExtractionService.ts # 临床要素提取(可选) +│ +├── types/ +│ ├── index.ts # 类型定义 +│ ├── chunk.types.ts +│ ├── search.types.ts +│ └── document.types.ts +│ +├── utils/ +│ ├── rrfFusion.ts # RRF 算法 +│ └── jsonParser.ts # JSON 容错解析 +│ +└── __tests__/ # 单元测试 + ├── embedding.test.ts + ├── chunk.test.ts + └── search.test.ts +``` + +### 基础能力 API + +```typescript +// KnowledgeBaseEngine.ts +export class KnowledgeBaseEngine { + constructor(private prisma: PrismaClient) {} + + // ==================== 文档入库 ==================== + + /** + * 入库文档(完整流程:提取 → 切片 → 向量化 → 存储) + */ + async ingestDocument(params: { + kbId: string; + userId: string; + file: Buffer; + filename: string; + options?: { + extractClinicalData?: boolean; + generateSummary?: boolean; + chunkSize?: number; + chunkOverlap?: number; + }; + }): Promise; + + /** + * 批量入库 + */ + async ingestBatch(documents: IngestParams[]): Promise; + + // ==================== 内容获取(全文) ==================== + + /** + * 获取单个文档全文 + */ + async getDocumentFullText(documentId: string): Promise; + + /** + * 获取知识库所有文档全文 + */ + async getAllDocumentsText(kbId: string): Promise; + + /** + * 批量获取多个知识库的所有文档全文 + */ + async getAllDocumentsTextBatch(kbIds: string[]): Promise; + + // ==================== 内容获取(摘要) ==================== + + /** + * 获取单个文档摘要 + */ + async getDocumentSummary(documentId: string): Promise; + + /** + * 获取知识库所有文档摘要 + */ + async getAllDocumentsSummaries(kbId: string): Promise; + + /** + * 批量获取多个知识库的所有文档摘要 + */ + async getAllDocumentsSummariesBatch(kbIds: string[]): Promise; + + // ==================== 检索能力 ==================== + + /** + * 向量检索(语义搜索) + */ + async vectorSearch( + kbIds: string[], + query: string, + topK?: number + ): Promise; + + /** + * 关键词检索(PostgreSQL FTS) + */ + async keywordSearch( + kbIds: string[], + query: string, + topK?: number + ): Promise; + + /** + * 混合检索(向量 + 关键词 + RRF 融合) + */ + async hybridSearch( + kbIds: string[], + query: string, + topK?: number, + options?: { + vectorWeight?: number; + keywordWeight?: number; + filters?: SearchFilters; + } + ): Promise; + + // ==================== 管理操作 ==================== + + /** + * 删除文档(级联删除切片和向量) + */ + async deleteDocument(documentId: string): Promise; + + /** + * 删除知识库所有文档 + */ + async clearKnowledgeBase(kbId: string): Promise; + + /** + * 获取知识库统计信息 + */ + async getKnowledgeBaseStats(kbId: string): Promise<{ + documentCount: number; + chunkCount: number; + totalTokens: number; + }>; + + // ==================== ❌ 不提供 chat() 方法 ==================== + // 策略由业务模块根据场景决定 +} +``` + +--- + +## 🔄 核心流程 + +### 文档入库流程(Ingest) + +``` +用户上传 PDF/Word/... + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Step 1: 文档处理引擎 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ DocumentProcessor.toMarkdown(file) │ │ +│ │ • PDF → pymupdf4llm │ │ +│ │ • Word → mammoth │ │ +│ │ • Excel → pandas │ │ +│ │ 输出:Markdown 文本 │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Step 2: 文本切片 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ChunkService.split(markdown) │ │ +│ │ • 递归字符切分 │ │ +│ │ • chunkSize: 512, overlap: 50 │ │ +│ │ 输出:Chunk[] │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Step 3: 摘要生成(可选) │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ SummaryService.generate(fullText) │ │ +│ │ • LLM 生成 200-500 字摘要 │ │ +│ │ 输出:summary 字段 │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Step 4: 临床要素提取(可选) │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ClinicalExtractionService.extract(fullText) │ │ +│ │ • PICO、用药方案、安全性数据等 │ │ +│ │ • LLM 提取 + JSON 容错解析 │ │ +│ │ 输出:ClinicalData (JSONB) │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Step 5: 向量化 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ EmbeddingService.embedBatch(chunks.map(c => c.text))│ │ +│ │ • 阿里云 text-embedding-v3 │ │ +│ │ • 1024 维向量 │ │ +│ │ 输出:number[][] │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Step 6: 存储 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ PostgreSQL + pgvector │ │ +│ │ • EkbDocument: 文档元数据 + 摘要 + 临床数据 (JSONB) │ │ +│ │ • EkbChunk: 切片文本 + 向量 (vector(1024)) │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🗄️ 数据模型 + +### Prisma Schema + +```prisma +// schema.prisma + +model EkbDocument { + id String @id @default(uuid()) + kbId String // 所属知识库 + userId String // 上传用户 + + // 基础信息 + filename String + fileType String + fileSizeBytes BigInt + fileUrl String // OSS 地址 + extractedText String? @db.Text // 提取的 Markdown 全文 + summary String? @db.Text // 文档摘要(200-500字) + + // 临床数据(JSONB,可选) + pico Json? // { P, I, C, O } + studyDesign Json? // { design, sampleSize, ... } + regimen Json? // [{ drug, dose, ... }] + safety Json? // { ae_all, ae_grade34 } + criteria Json? // { inclusion, exclusion } + endpoints Json? // { primary, secondary } + + // 状态 + status String @default("pending") // pending | processing | completed | failed + errorMessage String? @db.Text + + // 统计信息 + tokenCount Int? // 文档 token 数量 + + chunks EkbChunk[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([kbId]) + @@index([status]) + @@schema("ekb_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("ekb_schema") +} +``` + +### 索引设计 + +```sql +-- HNSW 向量索引(高性能近似最近邻) +CREATE INDEX IF NOT EXISTS ekb_chunk_embedding_idx +ON "ekb_schema"."EkbChunk" +USING hnsw (embedding vector_cosine_ops) +WITH (m = 16, ef_construction = 64); + +-- 全文检索索引 +CREATE INDEX IF NOT EXISTS ekb_chunk_content_idx +ON "ekb_schema"."EkbChunk" +USING gin (to_tsvector('simple', content)); + +-- JSONB GIN 索引(临床数据查询) +CREATE INDEX IF NOT EXISTS ekb_document_pico_idx +ON "ekb_schema"."EkbDocument" USING gin (pico); + +CREATE INDEX IF NOT EXISTS ekb_document_safety_idx +ON "ekb_schema"."EkbDocument" USING gin (safety); +``` + +--- + +## 📊 策略选择指南 + +### 根据知识库规模选择策略 + +| 知识库规模 | 文档数量 | 估算 Tokens | 推荐策略 | 基础能力组合 | +|-----------|---------|-------------|----------|-------------| +| **微型** | 1-10 | < 30K | 全文塞给 LLM | `getAllDocumentsText()` | +| **小型** | 10-30 | 30K-80K | 全文 + 长上下文模型 | `getAllDocumentsText()` + Qwen-Long | +| **中型** | 30-100 | 80K-300K | 摘要筛选 + Top-K 全文 | `getAllDocumentsSummaries()` + `getDocumentFullText()` | +| **大型** | 100+ | > 300K | RAG 向量检索 | `hybridSearch()` | + +### 根据场景选择策略 + +| 场景 | 推荐策略 | 原因 | +|------|----------|------| +| 精准问答 | 全文模式 | 无信息丢失 | +| 快速检索 | 向量检索 | 速度快 | +| 综合分析 | 摘要筛选 + 全文 | 兼顾广度和深度 | +| 文献综述 | 混合检索 + Rerank | 召回率高 | +| 查重对比 | 全文比对 | 精确匹配 | + +--- + +## 🔗 与其他通用能力的关系 + +### 依赖关系图 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 知识库引擎 (本模块) │ +│ │ +│ 依赖: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 文档处理引擎 │ │ +│ │ • 调用 DocumentProcessor.toMarkdown() │ │ +│ │ • 获取 PDF/Word/Excel 的 Markdown 文本 │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ LLM 网关 │ │ +│ │ • 调用 LLMFactory.getAdapter('deepseek-v3') │ │ +│ │ • 用于摘要生成、临床要素提取 │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 存储服务 │ │ +│ │ • 调用 storage.upload() / storage.download() │ │ +│ │ • 存储原始文档到 OSS │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ 外部依赖: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 阿里云 DashScope API │ │ +│ │ • text-embedding-v3 (向量化) │ │ +│ │ • gte-rerank (重排序,可选) │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 📅 开发计划 + +详见:[02-pgvector替换Dify计划.md](./02-pgvector替换Dify计划.md) + +### 里程碑 + +| 阶段 | 内容 | 工期 | 状态 | +|------|------|------|------| +| **M1** | 数据库设计 + 核心服务 | 5 天 | 🔜 待开始 | +| **M2** | PKB 模块接入 + 测试 | 3 天 | 📋 规划中 | +| **M3** | 数据迁移 + 上线 | 2 天 | 📋 规划中 | + +--- + +## 📚 相关文档 + +- [pgvector 替换 Dify 开发计划](./02-pgvector替换Dify计划.md) +- [文档处理引擎设计方案](../02-文档处理引擎/01-文档处理引擎设计方案.md) +- [LLM 网关](../01-LLM大模型网关/README.md) +- [通用能力层总览](../README.md) + +--- + +## 📅 更新日志 + +### v1.1 (2026-01-20) + +**设计原则重大更新:** +- ✅ 明确"提供基础能力,不做策略选择"原则 +- ❌ 移除 `chat()` 方法,策略由业务模块决定 +- 🆕 新增 `getDocumentFullText()` / `getAllDocumentsText()` 全文获取 +- 🆕 新增 `getDocumentSummary()` / `getAllDocumentsSummaries()` 摘要获取 +- 🆕 新增业务模块策略选择示例(PKB/AIA/ASL/RVW) +- 🆕 新增策略选择指南(按规模、按场景) +- 📝 数据模型添加 `summary` 和 `tokenCount` 字段 + +### v1.0 (2026-01-20) + +- 初始版本 + +--- + +**维护人:** 技术架构师 +**最后更新:** 2026-01-20 diff --git a/docs/02-通用能力层/03-RAG引擎/02-pgvector替换Dify计划.md b/docs/02-通用能力层/03-RAG引擎/02-pgvector替换Dify计划.md new file mode 100644 index 00000000..1018b885 --- /dev/null +++ b/docs/02-通用能力层/03-RAG引擎/02-pgvector替换Dify计划.md @@ -0,0 +1,614 @@ +# 通用能力层:知识库引擎 pgvector 实现计划 + +> **文档版本:** v2.0 +> **创建日期:** 2026-01-19 +> **重构日期:** 2026-01-20(从 PKB 模块提升为通用能力层) +> **预计工期:** 2 周(10个工作日) +> **前置条件:** ✅ pgvector 0.8.1 已安装 +> **目标:** 构建通用知识库引擎,用 PostgreSQL + pgvector 原生 RAG 替代 Dify,服务全平台业务模块 + +--- + +## 🎯 项目定位变更 + +### 从业务模块到通用能力层 + +| 维度 | 原方案(PKB 模块) | 新方案(通用能力层) | +|------|-------------------|---------------------| +| **定位** | PKB 模块内部改造 | 平台级通用能力 | +| **代码位置** | `modules/pkb/services/` | `common/rag/` | +| **使用范围** | 仅 PKB 模块 | PKB、AIA、ASL、RVW 等 | +| **扩展性** | 有限 | 高度可扩展 | + +### 受益模块 + +| 模块 | 使用场景 | 优先级 | +|------|----------|--------| +| **PKB** 个人知识库 | 知识库管理、RAG 问答 | P0(首个接入) | +| **AIA** AI智能问答 | @知识库 问答、附件理解 | P0 | +| **ASL** AI智能文献 | 文献库检索、智能综述 | P1 | +| **RVW** 稿件审查 | 稿件与文献对比、查重 | P1 | + +--- + +## 📊 整体难度评估 + +### 总体评估:⭐⭐⭐ 中等难度 + +| 评估维度 | 难度 | 说明 | +|----------|------|------| +| **数据库设计** | ⭐⭐ 低 | Prisma schema 直接写,pgvector 已就绪 | +| **Embedding 服务** | ⭐⭐ 低 | 调用阿里云 API,简单封装 | +| **文档切片** | ⭐⭐ 低 | 成熟方案,RecursiveCharacterTextSplitter | +| **临床要素提取** | ⭐⭐⭐ 中 | 需要调优 Prompt,处理 JSON 异常 | +| **向量检索** | ⭐⭐⭐ 中 | pgvector SQL 语法需要学习 | +| **混合检索(RRF)** | ⭐⭐⭐ 中 | 核心算法,需要调优 | +| **通用 API 设计** | ⭐⭐⭐ 中 | 需要考虑多模块复用 | +| **服务替换** | ⭐⭐⭐⭐ 中高 | 需要保持 API 兼容,测试覆盖 | +| **数据迁移** | ⭐⭐⭐ 中 | 现有文档需重新向量化 | + +**综合评估**:技术上完全可行,主要挑战在于**通用 API 设计**、**服务替换的平滑过渡**和**检索效果调优**。 + +--- + +## 🔥 核心挑战分析 + +### 挑战 1:通用 API 设计 🔴 高优先级 + +**问题描述**: +- 需要设计一套通用 API,满足多模块需求 +- 不同模块可能有不同的检索策略 +- 需要支持多知识库联合检索 + +**应对策略**: +- 设计 `KnowledgeBaseEngine` 统一入口类 +- 提供灵活的配置选项(chunkSize、topK、filters 等) +- 支持 `kbIds: string[]` 多知识库检索 + +**关键代码**: + +```typescript +// common/rag/KnowledgeBaseEngine.ts +export class KnowledgeBaseEngine { + // 文档入库 + async ingestDocument(params: IngestParams): Promise; + + // 混合检索(通用) + async search(params: SearchParams): Promise; + + // RAG 问答(通用) + async chat(params: ChatParams): Promise; +} +``` + +--- + +### 挑战 2:混合检索效果调优 🔴 高风险 + +**问题描述**: +- 替换 Dify 后,检索效果可能下降 +- 需要调优向量检索 + 关键词检索的权重 +- RRF 参数(k 值)需要实验确定 + +**应对策略**: +- 准备测试数据集(100+ 查询) +- 建立效果评估指标(Recall@K, MRR) +- 先用小批量数据验证,再全量迁移 + +**预留时间**:2 天专门用于调优 + +--- + +### 挑战 3:临床要素提取的准确性 🟡 中风险 + +**问题描述**: +- LLM 提取的 JSON 可能格式错误 +- PICO、用药方案等字段提取不完整 +- 不同类型文献(RCT/综述/病例)提取策略不同 + +**应对策略**: +- 三层 JSON 解析容错(直接解析 → 提取代码块 → LLM修复) +- 字段级校验(必填字段、类型校验) +- 分文献类型设计 Prompt + +**预留时间**:1 天用于 Prompt 调优 + +--- + +### 挑战 4:服务替换的兼容性 🟡 中风险 + +**问题描述**: +- PKB 模块需要保持 API 接口不变(前端零修改) +- `searchKnowledgeBase()` 返回格式需兼容 +- 文档上传流程需要无缝切换 + +**应对策略**: +- 定义适配层,转换返回格式 +- 新旧服务并行运行,灰度切换 +- 充分测试所有使用场景 + +**预留时间**:1 天专门用于兼容性测试 + +--- + +## 📅 详细开发计划 + +### 总览时间线 + +``` +Week 1: 通用能力层核心开发 +├── Day 1: 数据库设计 + Prisma 迁移 +├── Day 2: Embedding 服务 + 切片服务 +├── Day 3: 临床要素提取服务(Prompt 调优) +├── Day 4: 向量检索服务(pgvector SQL) +├── Day 5: 混合检索 + RRF 融合 + 统一入口 + +Week 2: PKB 模块接入 + 测试 + 迁移 +├── Day 6: PKB knowledgeBaseService 改造 +├── Day 7: PKB documentService 改造 +├── Day 8: 集成测试 + 效果调优 +├── Day 9: 数据迁移(现有文档向量化) +├── Day 10: 清理 + 文档 + 上线 +``` + +--- + +### Day 1:数据库设计 + Prisma 迁移 + +**目标**:创建向量存储的数据表(通用 schema) + +**任务清单**: +- [ ] 创建 `ekb_schema`(Enterprise Knowledge Base) +- [ ] 设计 `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 // 知识库 ID + userId String // 上传用户 + + // 基础信息 + filename String + fileType String + fileSizeBytes BigInt + fileUrl String // 原始文件 OSS 地址 + extractedText String? @db.Text // 解析后的 Markdown + + // 临床数据(JSONB,可选) + pico Json? + studyDesign Json? + regimen Json? + safety Json? + criteria Json? + endpoints Json? + + // 状态 + status String @default("pending") // pending | processing | completed | failed + errorMessage String? @db.Text + + chunks EkbChunk[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([kbId]) + @@index([status]) + @@schema("ekb_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("ekb_schema") +} +``` + +**手动 SQL(创建索引)**: + +```sql +-- 创建 HNSW 索引 +CREATE INDEX IF NOT EXISTS ekb_chunk_embedding_idx +ON "ekb_schema"."EkbChunk" +USING hnsw (embedding vector_cosine_ops) +WITH (m = 16, ef_construction = 64); + +-- 创建全文检索索引 +CREATE INDEX IF NOT EXISTS ekb_chunk_content_idx +ON "ekb_schema"."EkbChunk" +USING gin (to_tsvector('simple', content)); + +-- 创建 JSONB GIN 索引 +CREATE INDEX IF NOT EXISTS ekb_document_pico_idx +ON "ekb_schema"."EkbDocument" USING gin (pico); +``` + +--- + +### Day 2:Embedding 服务 + 切片服务 + +**目标**:实现通用的文本向量化和文档切片服务 + +**任务清单**: +- [ ] 创建 `common/rag/services/EmbeddingService.ts` +- [ ] 创建 `common/rag/services/ChunkService.ts` +- [ ] 单元测试:Embedding API 调用 +- [ ] 单元测试:切片效果验证 +- [ ] 环境变量配置(DASHSCOPE_API_KEY) + +**交付物**: +- `backend/src/common/rag/services/EmbeddingService.ts` +- `backend/src/common/rag/services/ChunkService.ts` +- 单元测试文件 + +**预计工时**:4-6 小时 + +**关键代码**: + +```typescript +// common/rag/services/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 { ... } + async embedBatch(texts: string[]): Promise { ... } + async embedQuery(query: string): Promise { ... } +} + +// common/rag/services/ChunkService.ts +export class ChunkService { + splitDocument( + text: string, + options: { chunkSize: number; chunkOverlap: number } + ): Chunk[] { ... } + + detectSections(text: string): Section[] { ... } +} +``` + +--- + +### Day 3:临床要素提取服务 + +**目标**:实现 PICO、用药方案等临床数据的 AI 提取(可选功能) + +**任务清单**: +- [ ] 创建 `common/rag/services/ClinicalExtractionService.ts` +- [ ] 设计提取 Prompt +- [ ] 实现三层 JSON 解析容错 +- [ ] 测试不同类型文献 +- [ ] Prompt 调优 + +**交付物**: +- `backend/src/common/rag/services/ClinicalExtractionService.ts` +- 测试用例 + +**预计工时**:6-8 小时(含 Prompt 调优) + +--- + +### Day 4:向量检索服务 + +**目标**:实现基于 pgvector 的向量检索 + +**任务清单**: +- [ ] 创建 `common/rag/services/VectorSearchService.ts` +- [ ] 实现向量检索(余弦相似度) +- [ ] 实现关键词检索(PostgreSQL FTS) +- [ ] 测试检索性能 +- [ ] 优化查询(索引使用验证) + +**交付物**: +- `backend/src/common/rag/services/VectorSearchService.ts` +- 性能测试报告 + +**预计工时**:6 小时 + +**关键 SQL**: + +```sql +-- 向量检索(支持多知识库) +SELECT + c.id, + c.content, + d.filename, + d."kbId", + 1 - (c.embedding <=> $1::vector) as score +FROM "ekb_schema"."EkbChunk" c +JOIN "ekb_schema"."EkbDocument" d ON c."documentId" = d.id +WHERE d."kbId" = ANY($2::text[]) -- 支持多知识库 +ORDER BY c.embedding <=> $1::vector +LIMIT $3; +``` + +--- + +### Day 5:混合检索 + RRF 融合 + 统一入口 + +**目标**:实现完整的混合检索和统一入口类 + +**任务清单**: +- [ ] 实现 RRF(Reciprocal Rank Fusion)算法 +- [ ] 实现并发三路检索 +- [ ] 创建 `KnowledgeBaseEngine` 统一入口类 +- [ ] 导出通用 API +- [ ] 效果评估 + +**交付物**: +- `backend/src/common/rag/KnowledgeBaseEngine.ts` +- `backend/src/common/rag/index.ts` +- 效果评估报告 + +**预计工时**:6-8 小时 + +**RRF 算法**: + +```typescript +// common/rag/utils/rrfFusion.ts +export function rrfFusion( + vectorResults: SearchResult[], + keywordResults: SearchResult[], + k: number = 60 +): SearchResult[] { + const scoreMap = new Map(); + + 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, ...getResultById(id) })); +} +``` + +--- + +### Day 6-7:PKB 模块接入(服务替换) + +**目标**:PKB 模块作为首个业务模块接入通用知识库引擎 + +**Day 6 任务**: +- [ ] 修改 `knowledgeBaseService.ts` - 检索替换 +- [ ] 移除 `difyClient.retrieveKnowledge()` 调用 +- [ ] 使用 `KnowledgeBaseEngine.search()` +- [ ] 保持返回格式兼容 + +**Day 7 任务**: +- [ ] 修改 `documentService.ts` - 上传替换 +- [ ] 移除 `difyClient.uploadDocumentDirectly()` 调用 +- [ ] 使用 `KnowledgeBaseEngine.ingestDocument()` +- [ ] 移除 Dify 状态轮询逻辑 + +**关键修改**: + +```typescript +// modules/pkb/services/knowledgeBaseService.ts + +// 修改前(Dify) +const results = await difyClient.retrieveKnowledge( + knowledgeBase.difyDatasetId, + query, + { retrieval_model: { search_method: 'semantic_search', top_k: topK } } +); + +// 修改后(通用知识库引擎) +import { KnowledgeBaseEngine } from '@/common/rag'; + +const kbEngine = new KnowledgeBaseEngine(prisma); +const results = await kbEngine.search({ + kbIds: [kbId], + query, + topK +}); + +// 格式转换(保持前端兼容) +return { + query: { content: query }, + records: results.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 8:集成测试 + 效果调优 + +**目标**:端到端测试,确保功能正常 + +**任务清单**: +- [ ] 前端测试:创建知识库 +- [ ] 前端测试:上传文档 +- [ ] 前端测试:RAG 检索问答 +- [ ] 效果对比:Dify vs pgvector 检索质量 +- [ ] 性能测试:检索延迟 +- [ ] Bug 修复 + +**交付物**: +- 测试报告 +- Bug 修复记录 + +**预计工时**:8 小时 + +--- + +### Day 9:数据迁移 + +**目标**:将现有知识库文档迁移到新表并向量化 + +**任务清单**: +- [ ] 编写迁移脚本 +- [ ] 批量向量化现有文档 +- [ ] 验证迁移完整性 +- [ ] 验证检索效果 + +**交付物**: +- `scripts/migrate-to-ekb.ts` +- 迁移日志 + +**预计工时**:6 小时 + +--- + +### Day 10:清理 + 文档 + 上线 + +**目标**:清理遗留代码,更新文档,正式上线 + +**任务清单**: +- [ ] 标记 `DifyClient.ts` 为废弃(暂不删除,保留回滚能力) +- [ ] 更新通用能力层文档 +- [ ] 更新 PKB 模块文档 +- [ ] 更新环境变量文档 +- [ ] 代码 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 | + +--- + +## ✅ 验收标准 + +### 功能验收 + +- [ ] 通用 API:`KnowledgeBaseEngine` 正常工作 +- [ ] PKB 接入:创建知识库不依赖 Dify +- [ ] PKB 接入:上传文档本地处理 + 向量化 +- [ ] PKB 接入:RAG 检索效果 ≥ Dify +- [ ] PKB 接入:全文阅读模式正常 +- [ ] PKB 接入:批处理模式正常 + +### 性能验收 + +- [ ] 检索延迟:< 500ms(95 分位) +- [ ] 上传处理:< 60s/文档(平均) +- [ ] 向量化吞吐:> 100 文档/小时 + +### 质量验收 + +- [ ] 检索召回率:≥ 80%(测试集) +- [ ] 通用能力层代码独立,无业务耦合 +- [ ] 文档更新完整 + +--- + +## 📝 附录 + +### A. 相关文档 + +- [知识库引擎架构设计](./01-知识库引擎架构设计.md) +- [文档处理引擎设计方案](../02-文档处理引擎/01-文档处理引擎设计方案.md) +- [通用能力层清单](../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": { + "p-queue": "^8.0.0" // 并发控制 + } +} +``` + +--- + +**文档维护**:技术架构师 +**原文档**:`03-业务模块/PKB-个人知识库/04-开发计划/01-Dify替换为pgvector开发计划.md` +**最后更新**:2026-01-20 + diff --git a/docs/02-通用能力层/03-RAG引擎/README.md b/docs/02-通用能力层/03-RAG引擎/README.md index 544bb666..09eb17ee 100644 --- a/docs/02-通用能力层/03-RAG引擎/README.md +++ b/docs/02-通用能力层/03-RAG引擎/README.md @@ -1,114 +1,325 @@ -# RAG引擎 +# 知识库引擎(RAG Engine) > **能力定位:** 通用能力层 -> **复用率:** 43% (3个模块依赖) -> **优先级:** P1 -> **状态:** ✅ 已实现(基于Dify) +> **复用率:** 57% (4个模块依赖) +> **优先级:** P0 +> **状态:** 🔄 升级中(Dify → PostgreSQL + pgvector) +> **核心原则:** 提供基础能力(乐高积木),不做策略选择 +> **最后更新:** 2026-01-20 --- ## 📋 能力概述 -RAG引擎负责: -- 向量化存储(Embedding) -- 语义检索(Semantic Search) -- 检索增强生成(RAG) -- Rerank重排序 +知识库引擎是平台的**核心通用能力**,提供知识库相关的**基础能力(乐高积木)**。 + +### ⭐ 核心设计原则 + +``` +✅ 提供基础能力(乐高积木) +❌ 不做策略选择(组装方案由业务模块决定) +``` + +**原因**:不同业务场景需要不同策略 +- 小知识库(10个文件)→ 直接全文塞给 LLM +- 大知识库(100+文件)→ RAG 向量检索 +- 特殊场景 → 摘要筛选 + Top-K 全文 + +### 基础能力清单 + +- 📄 **文档入库** - 文档解析 → 切片 → 向量化 → 存储 +- 📝 **全文获取** - 单文档/批量获取文档全文 +- 📋 **摘要获取** - 单文档/批量获取文档摘要 +- 🔍 **向量检索** - 基于 pgvector 的语义检索 +- 🔤 **关键词检索** - 基于 PostgreSQL FTS +- 🔀 **混合检索** - 向量 + 关键词 + RRF 融合 + +> ⚠️ **注意**:不提供 `chat()` 方法!问答策略由业务模块根据场景决定。 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 业务模块层(策略选择) │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ +│ │ PKB │ │ AIA │ │ ASL │ │ RVW │ │ +│ │ 全文/RAG │ │摘要+全文 │ │向量+Rerank│ │ 全文比对 │ │ +│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ +│ │ 根据场景自由组合基础能力 │ │ +│ └────────────┴────────────┴────────────┘ │ +│ │ │ +│ ▼ │ +├─────────────────────────────────────────────────────────────┤ +│ 知识库引擎(提供基础能力 / 乐高积木) │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ KnowledgeBaseEngine │ │ +│ │ ingest() / getFullText() / getSummary() │ │ +│ │ vectorSearch() / keywordSearch() / hybridSearch() │ │ +│ │ ❌ 不提供 chat() - 策略由业务模块决定 │ │ +│ └─────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ PostgreSQL + pgvector │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🔄 重大更新(2026-01-20) + +### 架构升级:Dify → PostgreSQL + pgvector + +| 维度 | 原方案(Dify) | 新方案(pgvector) | +|------|----------------|-------------------| +| **存储** | Qdrant(外部) | PostgreSQL(内部) | +| **数据控制** | 外部 API | 完全自主 | +| **扩展性** | 受限 | 高度灵活 | +| **符合架构** | ❌ | ✅ Postgres-Only | + +### 定位变更:从 PKB 模块到通用能力层 + +| 维度 | 原定位 | 新定位 | +|------|--------|--------| +| 代码位置 | `modules/pkb/` | `common/rag/` | +| 使用范围 | 仅 PKB | 全平台 | +| 设计目标 | 单模块功能 | 通用能力 | --- ## 📊 依赖模块 -**3个模块依赖(43%复用率):** -1. **AIA** - AI智能问答(@知识库问答) -2. **ASL** - AI智能文献(文献内容检索) -3. **PKB** - 个人知识库(RAG问答) +**4个模块依赖(57%复用率):** + +| 模块 | 使用场景 | 优先级 | +|------|----------|--------| +| **PKB** 个人知识库 | 知识库管理、RAG 问答、全文阅读 | P0(首个接入) | +| **AIA** AI智能问答 | @知识库 问答、附件理解 | P0 | +| **ASL** AI智能文献 | 文献库检索、智能综述生成 | P1 | +| **RVW** 稿件审查 | 稿件与文献对比、查重检测 | P1 | --- -## 💡 核心功能 +## 💡 基础能力使用 -### 1. 向量化存储 -- 基于Dify平台 -- Qdrant向量数据库(Dify内置) +### 1. 文档入库 -### 2. 语义检索 -- Top-K检索 -- 相关度评分 -- 多知识库联合检索 - -### 3. RAG问答 -- 检索 + 生成 -- 智能引用系统(100%准确溯源) - ---- - -## 🏗️ 技术架构 - -**基于Dify平台:** ```typescript -// DifyClient封装 -interface RAGEngine { - // 创建知识库 - createDataset(name: string): Promise; - - // 上传文档 - uploadDocument(datasetId: string, file: File): Promise; - - // 语义检索 - search(datasetId: string, query: string, topK?: number): Promise; - - // RAG问答 - chatWithRAG(datasetId: string, query: string): Promise; +import { KnowledgeBaseEngine } from '@/common/rag'; + +const kbEngine = new KnowledgeBaseEngine(prisma); + +await kbEngine.ingestDocument({ + kbId: 'kb-123', + userId: 'user-456', + file: pdfBuffer, + filename: 'research.pdf', + options: { + generateSummary: true, // 生成摘要 + extractClinicalData: true, // 提取 PICO 等临床数据 + } +}); +``` + +### 2. 全文/摘要获取 + +```typescript +// 获取单个文档全文 +const doc = await kbEngine.getDocumentFullText(documentId); + +// 获取知识库所有文档全文(小知识库场景) +const allDocs = await kbEngine.getAllDocumentsText(kbId); + +// 获取知识库所有文档摘要(筛选场景) +const summaries = await kbEngine.getAllDocumentsSummaries(kbId); +``` + +### 3. 检索能力 + +```typescript +// 向量检索 +const vectorResults = await kbEngine.vectorSearch(kbIds, query, 20); + +// 关键词检索 +const keywordResults = await kbEngine.keywordSearch(kbIds, query, 20); + +// 混合检索(向量 + 关键词 + RRF) +const hybridResults = await kbEngine.hybridSearch(kbIds, query, 10); +``` + +--- + +## 🎯 业务模块策略示例 + +### PKB:小知识库全文模式 + +```typescript +// 10 个文档 → 直接全文塞给 LLM +async function pkbSmallKbChat(kbId: string, query: string) { + const docs = await kbEngine.getAllDocumentsText(kbId); + const context = docs.map(d => `## ${d.filename}\n${d.text}`).join('\n\n'); + return llmChat(context, query); +} +``` + +### AIA:摘要筛选 + Top-K 全文 + +```typescript +// 摘要筛选 → LLM 选 Top 5 → 读取全文 +async function aiaSmartChat(kbIds: string[], query: string) { + const summaries = await kbEngine.getAllDocumentsSummaries(kbIds); + const topDocIds = await llmSelectTopK(summaries, query, 5); + const fullTexts = await Promise.all( + topDocIds.map(id => kbEngine.getDocumentFullText(id)) + ); + return llmChat(fullTexts.join('\n\n'), query); +} +``` + +### ASL:向量检索 + Rerank + +```typescript +// 大规模文献检索 +async function aslSearch(kbIds: string[], query: string) { + const candidates = await kbEngine.vectorSearch(kbIds, query, 50); + const reranked = await rerankService.rerank(candidates, query, 10); + return reranked; } ``` --- -## 📈 优化成果 +## 🏗️ 技术架构 -**检索参数优化:** -| 指标 | 优化前 | 优化后 | 提升 | -|------|--------|--------|------| -| 检索数量 | 3 chunks | 15 chunks | 5倍 | -| Chunk大小 | 500 tokens | 1500 tokens | 3倍 | -| 总覆盖 | 1,500 tokens | 22,500 tokens | 15倍 | -| 覆盖率 | ~5% | ~40-50% | 8-10倍 | +### 代码结构 + +``` +backend/src/common/rag/ +├── index.ts # 统一导出 +├── KnowledgeBaseEngine.ts # 统一入口类(基础能力) +├── services/ +│ ├── ChunkService.ts # 文档切片 +│ ├── EmbeddingService.ts # 向量化(阿里云) +│ ├── SummaryService.ts # 摘要生成 ⭐ +│ ├── VectorSearchService.ts # 向量检索 +│ ├── KeywordSearchService.ts # 关键词检索 +│ ├── HybridSearchService.ts # 混合检索 + RRF +│ └── ClinicalExtractionService.ts # 临床要素提取 +├── types/ +│ └── index.ts # 类型定义 +└── utils/ + └── rrfFusion.ts # RRF 算法 +``` + +### 数据模型 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ekb_schema │ +│ ┌───────────────────────┐ ┌───────────────────────┐ │ +│ │ EkbDocument │ │ EkbChunk │ │ +│ │ ───────────────── │ │ ───────────────── │ │ +│ │ id │ │ id │ │ +│ │ kbId │───>│ documentId │ │ +│ │ filename │ │ content │ │ +│ │ extractedText │ │ embedding (vector) │ │ +│ │ summary ⭐ │ │ pageNumber │ │ +│ │ tokenCount │ │ sectionType │ │ +│ │ pico (JSONB) │ └───────────────────────┘ │ +│ │ studyDesign (JSONB) │ │ +│ │ regimen (JSONB) │ │ +│ │ safety (JSONB) │ │ +│ └───────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +> ⭐ `summary` 字段用于支持"摘要筛选 + Top-K 全文"策略 --- -## 🔗 相关文档 +## 📚 API 接口 -- [通用能力层总览](../README.md) -- [Dify集成文档](../../00-系统总体设计/03-数据库架构说明.md) +### KnowledgeBaseEngine + +```typescript +class KnowledgeBaseEngine { + // ========== 文档入库 ========== + ingestDocument(params: IngestParams): Promise; + ingestBatch(documents: IngestParams[]): Promise; + + // ========== 内容获取 ========== + getDocumentFullText(documentId: string): Promise; + getAllDocumentsText(kbId: string): Promise; + getDocumentSummary(documentId: string): Promise; + getAllDocumentsSummaries(kbId: string): Promise; + + // ========== 检索能力 ========== + vectorSearch(kbIds: string[], query: string, topK?: number): Promise; + keywordSearch(kbIds: string[], query: string, topK?: number): Promise; + hybridSearch(kbIds: string[], query: string, topK?: number): Promise; + + // ========== 管理操作 ========== + deleteDocument(documentId: string): Promise; + clearKnowledgeBase(kbId: string): Promise; + getKnowledgeBaseStats(kbId: string): Promise; + + // ❌ 不提供 chat() 方法 - 策略由业务模块根据场景决定 +} +``` + +--- + +## 🔗 与其他通用能力的关系 + +| 依赖能力 | 用途 | +|----------|------| +| **文档处理引擎** | PDF/Word/Excel → Markdown | +| **LLM 网关** | 摘要生成、临床要素提取 | +| **存储服务** | 原始文档存储到 OSS | + +> 注:LLM 问答由业务模块自行调用 LLM 网关实现 + +--- + +## 📅 开发计划 + +详见:[02-pgvector替换Dify计划.md](./02-pgvector替换Dify计划.md) + +| 里程碑 | 内容 | 工期 | 状态 | +|--------|------|------|------| +| **M1** | 数据库设计 + 核心服务 | 5 天 | 🔜 待开始 | +| **M2** | PKB 模块接入 + 测试 | 3 天 | 📋 规划中 | +| **M3** | 数据迁移 + 上线 | 2 天 | 📋 规划中 | + +--- + +## 📂 相关文档 + +- [知识库引擎架构设计](./01-知识库引擎架构设计.md) +- [pgvector 替换 Dify 开发计划](./02-pgvector替换Dify计划.md) +- [文档处理引擎](../02-文档处理引擎/01-文档处理引擎设计方案.md) +- [通用能力层清单](../00-通用能力层清单.md) + +--- + +## 📅 更新日志 + +### 2026-01-20 v1.1 设计原则重大更新 + +- ⭐ **核心原则**:提供基础能力(乐高积木),不做策略选择 +- ❌ 移除 `chat()` 方法,策略由业务模块决定 +- 🆕 新增 `getDocumentFullText()` / `getAllDocumentsText()` 全文获取 +- 🆕 新增 `getDocumentSummary()` / `getAllDocumentsSummaries()` 摘要获取 +- 🆕 新增业务模块策略示例(PKB/AIA/ASL/RVW) + +### 2026-01-20 v1.0 架构升级 + +- 🔄 定位变更:从 PKB 模块提升为通用能力层 +- 🆕 创建架构设计文档 +- 🆕 重构开发计划(通用能力层视角) +- 📦 规划代码结构:`common/rag/` + +### 2025-11-06 初始版本 + +- 基于 Dify 实现 +- 仅 PKB 模块使用 --- -**最后更新:** 2025-11-06 **维护人:** 技术架构师 - - - - - - - - - - - - - - - - - - - - - - - - - -