Files
HaHafeng bbf98c4d5c fix(backend): Resolve PgBoss infinite loop issue and cleanup unused files
Backend fixes:
- Fix PgBoss task infinite loop on SAE (root cause: missing queue table constraints)
- Add singletonKey to prevent duplicate job enqueueing
- Add idempotency check in reviewWorker (skip completed tasks)
- Add optimistic locking in reviewService (atomic status update)

Frontend fixes:
- Add isSubmitting state to prevent duplicate submissions in RVW Dashboard
- Fix API baseURL in knowledgeBaseApi (relative path)

Cleanup (removed):
- Old frontend/ directory (migrated to frontend-v2)
- python-microservice/ (unused, replaced by extraction_service)
- Root package.json and node_modules (accidentally created)
- redcap-docker-dev/ (external dependency)
- Various temporary files and outdated docs in root

New documentation:
- docs/07-运维文档/01-PgBoss队列监控与维护.md
- docs/07-运维文档/02-故障预防检查清单.md
- docs/07-运维文档/03-数据库迁移注意事项.md

Database fix applied to RDS:
- Added PRIMARY KEY to platform_schema.queue
- Added 3 missing foreign key constraints

Tested: Local build passed, RDS constraints verified
2026-01-27 18:16:22 +08:00

349 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 知识库引擎RAG Engine
> **能力定位:** 通用能力层
> **复用率:** 57% (4个模块依赖)
> **优先级:** P0
> **状态:** 🔄 升级中Dify → PostgreSQL + pgvector
> **核心原则:** 提供基础能力(乐高积木),不做策略选择
> **最后更新:** 2026-01-20
---
## 📋 能力概述
知识库引擎是平台的**核心通用能力**,提供知识库相关的**基础能力(乐高积木)**。
### ⭐ 核心设计原则
```
✅ 提供基础能力(乐高积木)
❌ 不做策略选择(组装方案由业务模块决定)
```
**原因**:不同业务场景需要不同策略
- 小知识库10个文件→ 直接全文塞给 LLM
- 大知识库100+文件)→ RAG 向量检索
- 特殊场景 → 摘要筛选 + Top-K 全文
### 基础能力清单
- 📄 **文档入库** - ⚡️ 异步入库pg-boss返回 taskId 轮询状态
- 📝 **全文获取** - 单文档/批量获取文档全文
- 📋 **摘要获取** - 单文档/批量获取文档摘要(💰 可选生成)
- 🔍 **向量检索** - 基于 pgvector 的语义检索
- 🔤 **关键词检索** - 基于 pg_bigm 的中文精确检索
- 🔀 **混合检索** - 向量 + 关键词 + RRF 融合
- 🎯 **重排序** - 🆕 基于 Qwen-Rerank 的精排序
> ⚠️ **注意**:不提供 `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 | 全平台 |
| 设计目标 | 单模块功能 | 通用能力 |
---
## 📊 依赖模块
**4个模块依赖57%复用率):**
| 模块 | 使用场景 | 优先级 |
|------|----------|--------|
| **PKB** 个人知识库 | 知识库管理、RAG 问答、全文阅读 | P0首个接入 |
| **AIA** AI智能问答 | @知识库 问答、附件理解 | P0 |
| **ASL** AI智能文献 | 文献库检索、智能综述生成 | P1 |
| **RVW** 稿件审查 | 稿件与文献对比、查重检测 | P1 |
---
## 💡 基础能力使用
### 1. 文档入库(⚡️ 异步)
```typescript
import { KnowledgeBaseEngine } from '@/common/rag';
const kbEngine = new KnowledgeBaseEngine(prisma);
// 提交入库任务(立即返回)
const { taskId, documentId } = await kbEngine.submitIngestTask({
kbId: 'kb-123',
userId: 'user-456',
file: pdfBuffer,
filename: 'research.pdf',
options: {
enableSummary: true, // 💰 可选,默认 false
enableClinicalExtraction: true // 💰 可选,默认 false
}
});
// 轮询任务状态
const status = await kbEngine.getIngestStatus(taskId);
// { status: 'processing', progress: 45 }
```
> 详见:[Postgres-Only异步任务处理指南](../Postgres-Only异步任务处理指南.md)
### 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);
// 关键词检索pg_bigm 中文精确匹配)
const keywordResults = await kbEngine.keywordSearch(kbIds, query, 20);
// 混合检索(向量 + 关键词 + RRF
const hybridResults = await kbEngine.hybridSearch(kbIds, query, 10);
// 🆕 重排序
const reranked = await kbEngine.rerank(hybridResults, query, 5);
```
---
## 🎯 业务模块策略示例
### 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;
}
```
---
## 🏗️ 技术架构
### 代码结构
```
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 接口
### KnowledgeBaseEngine
```typescript
class KnowledgeBaseEngine {
// ========== 文档入库(⚡️ 异步) ==========
submitIngestTask(params: IngestParams): Promise<{ taskId: string; documentId: string }>;
getIngestStatus(taskId: string): Promise<{ status, progress, error? }>;
// ========== 内容获取 ==========
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[]>; // pg_bigm
hybridSearch(kbIds: string[], query: string, topK?: number): Promise<SearchResult[]>;
rerank(docs: SearchResult[], 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 网关实现
---
## 📅 开发计划
### 分阶段实施(推荐)
详见:[03-分阶段实施方案.md](./03-分阶段实施方案.md)
| 阶段 | 内容 | 工期 | 状态 |
|------|------|------|------|
| **Phase 1 MVP** | 入库 + 向量检索 + 全文获取 | 3 天 | 🔜 待开始 |
| **Phase 2 增强** | + 关键词检索 + 混合检索 + rerank | 2 天 | 📋 规划中 |
| **Phase 3 完整** | + 异步入库 + 摘要 + PICO | 3 天 | 📋 规划中 |
### 技术实现参考
详见:[02-pgvector替换Dify计划.md](./02-pgvector替换Dify计划.md)
---
## 📂 相关文档
- [知识库引擎架构设计](./01-知识库引擎架构设计.md) - 完整架构目标
- [pgvector 替换 Dify 技术方案](./02-pgvector替换Dify计划.md) - 详细技术实现
- [分阶段实施方案](./03-分阶段实施方案.md) - 🆕 MVP → 增强 → 完整
- [文档处理引擎](../02-文档处理引擎/01-文档处理引擎设计方案.md)
- [Postgres-Only异步任务处理指南](../Postgres-Only异步任务处理指南.md)
- [通用能力层清单](../00-通用能力层清单.md)
---
## 📅 更新日志
### 2026-01-20 v1.2 架构审核优化
- ⚡️ **入库异步化**`submitIngestTask()` + `getIngestStatus()`,基于 pg-boss
- 💰 **成本控制**:摘要/PICO 提取默认关闭,按需开启
- 🔧 **中文检索**`tsvector``pg_bigm`,专为 CJK 文字优化
- 🆕 **新增能力**`rerank()` 重排序Qwen-Rerank API
- 📋 **分阶段实施**:新增 MVP → 增强 → 完整 三阶段方案
### 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 模块使用---**维护人:** 技术架构师