Files
AIclinicalresearch/docs/09-架构实施/01-知识库引擎架构设计.md
HaHafeng 40c2f8e148 feat(rag): Complete RAG engine implementation with pgvector
Major Features:
- Created ekb_schema (13th schema) with 3 tables: KB/Document/Chunk
- Implemented EmbeddingService (text-embedding-v4, 1024-dim vectors)
- Implemented ChunkService (smart Markdown chunking)
- Implemented VectorSearchService (multi-query + hybrid search)
- Implemented RerankService (qwen3-rerank)
- Integrated DeepSeek V3 QueryRewriter for cross-language search
- Python service: Added pymupdf4llm for PDF-to-Markdown conversion
- PKB: Dual-mode adapter (pgvector/dify/hybrid)

Architecture:
- Brain-Hand Model: Business layer (DeepSeek) + Engine layer (pgvector)
- Cross-language support: Chinese query matches English documents
- Small Embedding (1024) + Strong Reranker strategy

Performance:
- End-to-end latency: 2.5s
- Cost per query: 0.0025 RMB
- Accuracy improvement: +20.5% (cross-language)

Tests:
- test-embedding-service.ts: Vector embedding verified
- test-rag-e2e.ts: Full pipeline tested
- test-rerank.ts: Rerank quality validated
- test-query-rewrite.ts: Cross-language search verified
- test-pdf-ingest.ts: Real PDF document tested (Dongen 2003.pdf)

Documentation:
- Added 05-RAG-Engine-User-Guide.md
- Added 02-Document-Processing-User-Guide.md
- Updated system status documentation

Status: Production ready
2026-01-21 20:24:29 +08:00

137 lines
6.5 KiB
Markdown
Raw 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.
# **知识库引擎架构设计**
文档版本: v2.0 (架构修正版)
创建日期: 2026-01-20
最后更新: 2026-01-21
核心原则: 引擎负责“执行”,业务负责“思考”。
## **📋 概述**
### **能力定位修正**
**KnowledgeBaseEngine** 是一个**纯粹的检索执行器**。它不负责“理解用户意图”,只负责“执行检索指令”。
**❌ 错误的设计:**
* 引擎内部调用 LLM 分析聊天记录。
* 引擎依赖 Chat History 数据结构。
**✅ 正确的设计:**
* **业务模块 (AIA/ASL)**:调用 DeepSeek 分析意图 \-\> 生成 \[Query1, Query2, Query3\]。
* **引擎模块 (EKB)**:接收 queries\[\] \-\> 执行向量/关键词检索 \-\> RRF 融合 \-\> 返回结果。
## **🏗️ 交互流程图 (The "Brain-Hand" Model)**
sequenceDiagram
participant User
participant Biz as 业务模块 (AIA/ASL)
participant LLM as DeepSeek V3 (Brain)
participant Engine as 知识库引擎 (Hand)
participant DB as PostgreSQL
User-\>\>Biz: "副作用大吗?" (带着上下文)
rect rgb(240, 248, 255\)
Note over Biz, LLM: 🧠 思考阶段 (策略层)
Biz-\>\>LLM: Prompt: "结合历史,生成检索词"
LLM--\>\>Biz: \["NSCLC一线治疗副作用", "Pembrolizumab AE"\]
end
rect rgb(255, 240, 245\)
Note over Biz, Engine: ✋ 执行阶段 (机制层)
Biz-\>\>Engine: search(queries=\[...\])
Engine-\>\>DB: 并行向量检索 \+ 关键词检索
Engine-\>\>Engine: RRF 融合 & Rerank
Engine--\>\>Biz: 返回 Top-K 文档
end
Biz-\>\>LLM: Prompt: "基于这些文档回答用户"
LLM--\>\>User: 最终回答
## **📦 API 设计 (KnowledgeBaseEngine)**
引擎的 API 变得更加干净、通用:
export class KnowledgeBaseEngine {
/\*\*
\* 纯粹的检索接口
\* @param kbIds 知识库 ID 列表
\* @param searchQueries 检索词列表(由业务层生成好的)
\*/
async search(
kbIds: string\[\],
searchQueries: string\[\], // 👈 接收一组词,而不是一个 query
options?: {
topK?: number;
filters?: SearchFilters; // 结构化过滤,如 { year: 2024 }
}
): Promise\<SearchResult\[\]\> {
// 1\. 并行执行所有 query 的检索 (Fan-out)
const allResults \= await Promise.all(
searchQueries.map(q \=\> this.vectorSearchInternal(kbIds, q))
);
// 2\. RRF 融合
const fusedResults \= this.rrfFusion(allResults.flat());
// 3\. Rerank (可选)
// 使用第一个 Query 作为基准进行重排序
return this.rerank(fusedResults, searchQueries\[0\]);
}
}
把“语义理解”和“意图分析”强行塞进底层的 RAG 引擎,确实会让引擎变得臃肿且不通用。
### **为什么你的思路是“架构级”的提升?**
1. **解耦 (Decoupling)**
* **RAG 引擎 (KnowledgeBaseEngine)** 应该是一个\*\*“精准的执行者”\*\*(机制)。它的职责是:“你给我一组词,我给你找出最匹配的文档”。它不应该关心“这组词是来自一个愤怒的医生,还是来自一个做作业的学生”。
* **业务模块 (AIA/ASL)** 才是\*\*“聪明的决策者”\*\*策略。只有业务模块才知道当前的对话上下文Context、用户的角色Role以及当前任务的目标Goal
2. **场景化差异 (Context Matters)**
* **AIA聊天**:上下文是 `Chat History`,策略是“意图补全”。
* **ASL文献筛选**:上下文是 `Inclusion Criteria`入排标准策略是“PICO 拆解”。
* **如果都塞进引擎**:引擎内部就要写一堆 `if (isChat) ... else if (isLiterature) ...`,这是架构的大忌。
---
我根据你的这个关键修正,将 **《01-知识库引擎架构设计》** 和 **《08-技术方案》** 进行了**最终架构对齐**。
### **修改点概览**
1. **RAG 引擎**:回归纯粹。删除 `search(history)` 这种带业务逻辑的接口,只保留 `search(queries[])`
2. **业务层**:新增 **“检索策略层”**。DeepSeek 的思考过程发生在业务层。
也就是说利用DeepSeek做翻译做语义理解都不应该放在RAG引擎里对吗还是说我们只保留翻译还是都不保留在业务模块调用DeepSeek的时候就考虑中英文翻译的事
**结论:是的,应该全部剥离。**
在 KnowledgeBaseEngineRAG 引擎)里,**既不保留语义理解,也不保留翻译**。它应该是一个“没有任何感情的杀手”,只负责执行。
### **1\. 最终架构定调:各司其职**
为了让你跟团队沟通更明确,我把这种模式总结为 **“决策与执行分离”**
| 层级 | 角色 | 职责 (谁做?) | 核心逻辑 |
| :---- | :---- | :---- | :---- |
| **业务层** (AIA/ASL) | **决策者** (The Brain) | **DeepSeek V3** | **“怎么搜?”** 它负责看懂用户的话,看懂历史记录,然后决定:“我要用中文搜一遍,再用英文搜一遍,还要查一下 K 药的学名”。 最终产出:\["K药", "Keytruda", "Pembrolizumab"\] |
| **引擎层** (EKB) | **执行者** (The Hand) | **Postgres \+ Vector** | **“去搜!”** 它根本不知道什么是“K药”它只收到一个指令search(queries: string\[\])。 它负责把这 3 个词扔进数据库,并行跑向量检索和关键词检索,然后把结果捞上来。 |
### **2\. 为什么要连“翻译”都移出去?**
你可能会问:“翻译不是通用的吗?为什么不留在引擎里?”
因为在医学场景下,**“翻译”往往依赖“上下文”**,而引擎是没有上下文的。
* **场景 AAIA 聊天)**
* 用户说:“阳性吗?”(上文在聊 **EGFR 基因突变**)。
* **业务层 DeepSeek**:结合历史,生成检索词 \["EGFR mutation positive", "EGFR 突变 阳性"\]。
* **如果是引擎层做翻译**:引擎只看到“阳性吗?”,翻译成 Is it positive?,去搜可能会搜出“新冠阳性”或“艾滋病阳性”,完全跑偏。
* **场景 BASL 文献筛选)**
* 用户设定:“年龄 \> 60岁”。
* **业务层 DeepSeek**:生成检索词 \["Elderly patients", "Geriatric", "Age \> 60"\]。
* **如果是引擎层做翻译**:引擎根本不知道这代表“老年人”,可能直译。
**所以,只有业务层才有资格做“精准的翻译(即意图理解)”。**