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

6.5 KiB
Raw Blame History

知识库引擎架构设计

文档版本: 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"]。
    • 如果是引擎层做翻译:引擎根本不知道这代表“老年人”,可能直译。

所以,只有业务层才有资格做“精准的翻译(即意图理解)”。