docs(rag-engine): update architecture design with building-blocks principle
- Add core design principle: provide building blocks, no strategy selection - Remove chat() method, strategy determined by business modules - Add new capabilities: getDocumentFullText(), getAllDocumentsText() - Add new capabilities: getDocumentSummary(), getAllDocumentsSummaries() - Add business module strategy examples (PKB/AIA/ASL/RVW) - Add strategy selection guide (by scale, by scenario) - Update data model with summary and tokenCount fields - Add SummaryService to code structure
This commit is contained in:
676
docs/02-通用能力层/03-RAG引擎/01-知识库引擎架构设计.md
Normal file
676
docs/02-通用能力层/03-RAG引擎/01-知识库引擎架构设计.md
Normal file
@@ -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<IngestResult>;
|
||||
|
||||
/**
|
||||
* 批量入库
|
||||
*/
|
||||
async ingestBatch(documents: IngestParams[]): Promise<IngestResult[]>;
|
||||
|
||||
// ==================== 内容获取(全文) ====================
|
||||
|
||||
/**
|
||||
* 获取单个文档全文
|
||||
*/
|
||||
async getDocumentFullText(documentId: string): Promise<DocumentText>;
|
||||
|
||||
/**
|
||||
* 获取知识库所有文档全文
|
||||
*/
|
||||
async getAllDocumentsText(kbId: string): Promise<DocumentText[]>;
|
||||
|
||||
/**
|
||||
* 批量获取多个知识库的所有文档全文
|
||||
*/
|
||||
async getAllDocumentsTextBatch(kbIds: string[]): Promise<DocumentText[]>;
|
||||
|
||||
// ==================== 内容获取(摘要) ====================
|
||||
|
||||
/**
|
||||
* 获取单个文档摘要
|
||||
*/
|
||||
async getDocumentSummary(documentId: string): Promise<DocumentSummary>;
|
||||
|
||||
/**
|
||||
* 获取知识库所有文档摘要
|
||||
*/
|
||||
async getAllDocumentsSummaries(kbId: string): Promise<DocumentSummary[]>;
|
||||
|
||||
/**
|
||||
* 批量获取多个知识库的所有文档摘要
|
||||
*/
|
||||
async getAllDocumentsSummariesBatch(kbIds: string[]): Promise<DocumentSummary[]>;
|
||||
|
||||
// ==================== 检索能力 ====================
|
||||
|
||||
/**
|
||||
* 向量检索(语义搜索)
|
||||
*/
|
||||
async vectorSearch(
|
||||
kbIds: string[],
|
||||
query: string,
|
||||
topK?: number
|
||||
): Promise<SearchResult[]>;
|
||||
|
||||
/**
|
||||
* 关键词检索(PostgreSQL FTS)
|
||||
*/
|
||||
async keywordSearch(
|
||||
kbIds: string[],
|
||||
query: string,
|
||||
topK?: number
|
||||
): Promise<SearchResult[]>;
|
||||
|
||||
/**
|
||||
* 混合检索(向量 + 关键词 + RRF 融合)
|
||||
*/
|
||||
async hybridSearch(
|
||||
kbIds: string[],
|
||||
query: string,
|
||||
topK?: number,
|
||||
options?: {
|
||||
vectorWeight?: number;
|
||||
keywordWeight?: number;
|
||||
filters?: SearchFilters;
|
||||
}
|
||||
): Promise<SearchResult[]>;
|
||||
|
||||
// ==================== 管理操作 ====================
|
||||
|
||||
/**
|
||||
* 删除文档(级联删除切片和向量)
|
||||
*/
|
||||
async deleteDocument(documentId: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* 删除知识库所有文档
|
||||
*/
|
||||
async clearKnowledgeBase(kbId: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* 获取知识库统计信息
|
||||
*/
|
||||
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
|
||||
614
docs/02-通用能力层/03-RAG引擎/02-pgvector替换Dify计划.md
Normal file
614
docs/02-通用能力层/03-RAG引擎/02-pgvector替换Dify计划.md
Normal file
@@ -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<IngestResult>;
|
||||
|
||||
// 混合检索(通用)
|
||||
async search(params: SearchParams): Promise<SearchResult[]>;
|
||||
|
||||
// RAG 问答(通用)
|
||||
async chat(params: ChatParams): Promise<ChatResult>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 挑战 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<number[]> { ... }
|
||||
async embedBatch(texts: string[]): Promise<number[][]> { ... }
|
||||
async embedQuery(query: string): Promise<number[]> { ... }
|
||||
}
|
||||
|
||||
// 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<string, number>();
|
||||
|
||||
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
|
||||
|
||||
@@ -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<string>;
|
||||
import { KnowledgeBaseEngine } from '@/common/rag';
|
||||
|
||||
// 上传文档
|
||||
uploadDocument(datasetId: string, file: File): Promise<string>;
|
||||
const kbEngine = new KnowledgeBaseEngine(prisma);
|
||||
|
||||
// 语义检索
|
||||
search(datasetId: string, query: string, topK?: number): Promise<SearchResult[]>;
|
||||
await kbEngine.ingestDocument({
|
||||
kbId: 'kb-123',
|
||||
userId: 'user-456',
|
||||
file: pdfBuffer,
|
||||
filename: 'research.pdf',
|
||||
options: {
|
||||
generateSummary: true, // 生成摘要
|
||||
extractClinicalData: true, // 提取 PICO 等临床数据
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
// RAG问答
|
||||
chatWithRAG(datasetId: string, query: string): Promise<string>;
|
||||
### 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<IngestResult>;
|
||||
ingestBatch(documents: IngestParams[]): Promise<IngestResult[]>;
|
||||
|
||||
// ========== 内容获取 ==========
|
||||
getDocumentFullText(documentId: string): Promise<DocumentText>;
|
||||
getAllDocumentsText(kbId: string): Promise<DocumentText[]>;
|
||||
getDocumentSummary(documentId: string): Promise<DocumentSummary>;
|
||||
getAllDocumentsSummaries(kbId: string): Promise<DocumentSummary[]>;
|
||||
|
||||
// ========== 检索能力 ==========
|
||||
vectorSearch(kbIds: string[], query: string, topK?: number): Promise<SearchResult[]>;
|
||||
keywordSearch(kbIds: string[], query: string, topK?: number): Promise<SearchResult[]>;
|
||||
hybridSearch(kbIds: string[], query: string, topK?: number): Promise<SearchResult[]>;
|
||||
|
||||
// ========== 管理操作 ==========
|
||||
deleteDocument(documentId: string): Promise<void>;
|
||||
clearKnowledgeBase(kbId: string): Promise<void>;
|
||||
getKnowledgeBaseStats(kbId: string): Promise<KBStats>;
|
||||
|
||||
// ❌ 不提供 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
|
||||
**维护人:** 技术架构师
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user