Files
AIclinicalresearch/docs/02-通用能力层/03-RAG引擎/05-RAG引擎使用指南.md
HaHafeng 596f2dfc02 docs: Update RAG engine docs - pg_bigm v1.2 installation completed
Summary:
- pg_bigm v1.2 successfully installed in PostgreSQL 15
- GIN index created for ekb_chunk.content
- Keyword search performance improved 10-100x

Documentation updates:
- RAG engine guide v1.0 -> v1.1 (update Q3, add performance data)
- System status guide v4.1 -> v4.2 (mark pg_bigm as installed)
- pg_bigm installation guide v1.0 -> v1.1 (mark as completed)

Status: Production ready with full RAG capabilities
2026-01-24 19:17:24 +08:00

16 KiB
Raw Blame History

RAG 引擎使用指南

文档版本: v1.1
最后更新: 2026-01-24
状态: 生产就绪(完整功能)
目标读者: 业务模块开发者PKB、AIA、ASL 等)

本次更新pg_bigm v1.2 已安装,关键词检索性能提升 10-100倍


📋 快速开始

5 秒上手

import { getVectorSearchService } from '@/common/rag';

const searchService = getVectorSearchService(prisma);

// 单查询检索
const results = await searchService.vectorSearch('银杏叶对老年痴呆的效果', {
  topK: 10,
  filter: { kbId: 'your-kb-id' }
});

// 业务层生成多查询后检索(推荐)
const queries = ['银杏叶副作用', 'Ginkgo side effects'];
const results = await searchService.searchWithQueries(queries, {
  topK: 10,
  filter: { kbId: 'your-kb-id' }
});

🏗️ 架构设计

核心原则:"Brain-Hand" 模型

┌─────────────────────────────────────────────────────────────┐
│   业务层 (The Brain) - 你的代码                              │
│   PKB / AIA / ASL                                            │
│                                                             │
│   职责:思考 "怎么搜?"                                      │
│   • 理解用户意图(上下文、历史)                             │
│   • 调用 DeepSeek V3 生成查询词                              │
│   • 决定检索策略(单语/双语/多查询)                         │
│                                                             │
│   示例:                                                     │
│   const queries = await generateQueries(userInput, context);│
│   // ["K药副作用", "Keytruda AE", "Pembrolizumab"]          │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│   引擎层 (The Hand) - RAG 引擎                               │
│   VectorSearchService                                        │
│                                                             │
│   职责:执行检索(无上下文)                                 │
│   • 向量检索 (text-embedding-v4 1024维)                    │
│   • 关键词检索 (pg_bigm)                                   │
│   • RRF 融合                                               │
│   • Rerank (qwen3-rerank)                                  │
│                                                             │
│   ✅ 不调用 LLM 理解意图                                    │
│   ✅ 只执行检索指令                                         │
└─────────────────────────────────────────────────────────────┘

📦 核心组件

1. EmbeddingService - 文本向量化

import { getEmbeddingService } from '@/common/rag';

const embeddingService = getEmbeddingService();

// 单文本
const { embedding, tokenCount } = await embeddingService.embed(text);

// 批量自动分批每批10条
const { embeddings, totalTokens } = await embeddingService.embedBatch(texts);

配置:

# .env
TEXT_EMBEDDING_MODEL=text-embedding-v4
TEXT_EMBEDDING_DIMENSIONS=1024  # 推荐1024性能平衡

2. ChunkService - 文本分块

import { getChunkService } from '@/common/rag';

const chunkService = getChunkService();

// Markdown 智能分块(保留标题层级)
const { chunks } = chunkService.chunkMarkdown(markdown);

// 普通文本分块
const { chunks } = chunkService.chunk(text);

默认配置:

  • 最大块大小1000 字符
  • 块间重叠200 字符

3. VectorSearchService - 检索服务

import { getVectorSearchService } from '@/common/rag';

const searchService = getVectorSearchService(prisma);

// 方法 1: 单查询检索(简单场景)
const results = await searchService.vectorSearch(query, options);

// 方法 2: 多查询检索(推荐,业务层生成查询词)
const queries = ['Query1', 'Query2', 'Query3'];
const results = await searchService.searchWithQueries(queries, options);

// 方法 3: 混合检索(向量 + 关键词)
const results = await searchService.hybridSearch(query, options);

// 方法 4: Rerank 重排序
const reranked = await searchService.rerank(query, results, { topK: 5 });

4. DocumentIngestService - 文档入库

import { getDocumentIngestService } from '@/common/rag';

const ingestService = getDocumentIngestService(prisma);

const result = await ingestService.ingestDocument(
  {
    filename: 'paper.pdf',
    fileBuffer: pdfBuffer,
  },
  {
    kbId: 'your-kb-id',
    contentType: 'LITERATURE',
    tags: ['医学', 'RCT'],
  }
);

5. QueryRewriter - 查询理解(业务层使用)

import { QueryRewriter } from '@/common/rag';

const rewriter = new QueryRewriter();

// 智能翻译 + 扩展
const result = await rewriter.rewrite('K药副作用');
// {
//   original: "K药副作用",
//   rewritten: ["Keytruda adverse events", "Pembrolizumab side effects"],
//   isChinese: true,
//   cost: 0.0001,
//   duration: 1500
// }

🎯 使用场景

场景 1: PKB 个人知识库(中英混合)

// modules/pkb/services/ragService.ts

async function searchKnowledgeBase(userId: string, kbId: string, userQuery: string) {
  // ===== 业务层:查询理解 =====
  const rewriter = new QueryRewriter();
  const rewriteResult = await rewriter.rewrite(userQuery);
  
  // 生成中英双语查询(适配混合知识库)
  const queries = rewriteResult.isChinese 
    ? [userQuery, ...rewriteResult.rewritten]  // 中文 + 英文
    : [userQuery];
  
  logger.info(`PKB 检索策略: ${queries.length}条查询`, { queries });
  
  // ===== 引擎层:执行检索 =====
  const searchService = getVectorSearchService(prisma);
  
  // 多查询向量检索
  const results = await searchService.searchWithQueries(queries, {
    topK: 20,
    minScore: 0.2,
    filter: { kbId },
  });
  
  // Rerank 精排
  const finalResults = await searchService.rerank(userQuery, results, {
    topK: 10,
  });
  
  return finalResults;
}

场景 2: AIA 智能问答(上下文理解)

// modules/aia/services/chatService.ts

async function chat(userId: string, message: string, chatHistory: Message[]) {
  // ===== 业务层:意图理解 =====
  const llm = LLMFactory.getAdapter('deepseek-v3');
  
  // 结合历史生成检索词
  const prompt = `用户说:"${message}"
上下文:${chatHistory.slice(-3).map(m => m.content).join('\n')}

请生成2-3个精准的医学检索词中英文`;
  
  const response = await llm.chat([{ role: 'user', content: prompt }]);
  const queries = JSON.parse(response.content);  // ["EGFR mutation", "表皮生长因子受体突变"]
  
  // ===== 引擎层:执行检索 =====
  const results = await searchService.searchWithQueries(queries, { topK: 5 });
  
  // 基于检索结果生成回答
  return generateAnswer(message, results);
}

场景 3: ASL 文献筛选PICO 拆解)

// modules/asl/services/screeningService.ts

async function screenLiteratures(picoQuery: PICO) {
  // ===== 业务层PICO 拆解 =====
  const queries = [
    `${picoQuery.P} ${picoQuery.I}`,  // 人群 + 干预
    `${picoQuery.I} efficacy`,         // 干预 + 疗效
    `${picoQuery.O} outcomes`,         // 结局指标
  ];
  
  logger.info('ASL PICO 检索', { pico: picoQuery, queries });
  
  // ===== 引擎层:执行检索 =====
  const results = await searchService.searchWithQueries(queries, {
    topK: 50,
    filter: { contentType: 'LITERATURE' },
  });
  
  return results;
}

⚠️ 注意事项

1. 查询理解必须在业务层

错误:在引擎层调用 LLM

// VectorSearchService.ts (引擎层)
async vectorSearch(query) {
  const translated = await deepseek.translate(query);  // ❌ 引擎不应该做这个
  // ...
}

正确:在业务层调用 LLM

// PKB ragService.ts (业务层)
async searchKnowledgeBase(query) {
  const queries = await deepseek.generateQueries(query, context);  // ✅
  const results = await engine.searchWithQueries(queries);
  // ...
}

原因:

  • 引擎没有上下文Chat History, PICO
  • 引擎不知道知识库语言
  • 引擎不理解业务场景

2. 中英双语查询策略

// 检测到中文查询 + 可能有英文文档
if (containsChinese(userQuery)) {
  const rewriter = new QueryRewriter();
  const result = await rewriter.rewrite(userQuery);
  
  // 生成中英双语查询词
  const queries = [
    userQuery,  // 保留中文(匹配中文文档)
    ...result.rewritten,  // 添加英文(匹配英文文档)
  ];
  
  // 中文库和英文库都能匹配!
  return await engine.searchWithQueries(queries);
}

3. pg_bigm 关键词检索需要翻译

// 关键词检索必须用英文(如果文档是英文)
const keywordQuery = rewriteResult.rewritten[0];  // 使用翻译后的英文
const keywordResults = await searchService.keywordSearch(keywordQuery);

// 为什么?
// pg_bigm 是字符匹配,"肺癌" 匹配不到 "Lung Cancer"
// 只有 "Lung" 能匹配到 "Lung Cancer"

4. 性能优化

// 批量向量化自动分批10条/批)
const embeddings = await embeddingService.embedBatch(chunks);  // 自动处理

// 多查询并行检索
const results = await searchService.searchWithQueries([q1, q2, q3]);  // 并行执行

// Rerank 只对候选集
const candidates = await searchService.vectorSearch(query, { topK: 20 });
const final = await searchService.rerank(query, candidates, { topK: 5 });

🔧 环境变量配置

# .env

# ===== 阿里云 API Key必填=====
DASHSCOPE_API_KEY=sk-xxx

# ===== 文本向量模型 =====
TEXT_EMBEDDING_MODEL=text-embedding-v4
TEXT_EMBEDDING_DIMENSIONS=1024  # 推荐 1024平衡

# ===== Rerank 模型 =====
RERANK_MODEL=qwen3-rerank

# ===== DeepSeek V3查询理解=====
DEEPSEEK_API_KEY=sk-xxx  # 业务层使用

# ===== Python 微服务 =====
EXTRACTION_SERVICE_URL=http://localhost:8000

# ===== PKB RAG 后端 =====
PKB_RAG_BACKEND=pgvector  # pgvector | dify | hybrid

📊 性能指标

操作 耗时 成本
PDF → Markdown (Python) 3-5秒 ¥0
文本分块 (72块) <10ms ¥0
批量向量化 (72块) 5秒 ¥0.009
单次向量检索 50ms ¥0
DeepSeek V3 查询重写 1-2秒 ¥0.0001
qwen3-rerank (10候选) 150ms ¥0.002
完整链路 2.5秒 ¥0.0025

🎯 检索效果

跨语言检索(中文查英文)

方案 Top 1 准确率 相似度
纯向量v4 1024维 0.56
+ DeepSeek V3 查询重写 1.00
+ 混合检索 + Rerank 最高 0.77

同语言检索(中文查中文)

方案 Top 1 准确率 相似度
纯向量 0.70+
+ Rerank 最高 0.85+

💡 最佳实践

1. 查询理解 Prompt 模板

// 推荐:在 capability_schema.prompt_templates 中定义

const QUERY_REWRITE_PROMPT = `你是医学检索专家。

任务:
1. 如果是中文查询,翻译为英文医学术语
2. 生成 1-2 个同义扩展查询
3. 标准化俗称(如 "K药" → "Keytruda/Pembrolizumab"

输入:{query}

输出JSON数组格式
["Query1", "Query2", ...]

示例:
输入:"K药副作用"
输出:["Keytruda adverse events", "Pembrolizumab side effects"]`;

2. 完整检索链路(推荐)

async function intelligentSearch(userQuery: string, kbId: string, context?: any) {
  // Step 1: 查询理解(业务层 DeepSeek V3
  const queries = await generateSearchQueries(userQuery, context);
  
  // Step 2: 混合检索(引擎层)
  const candidates = await Promise.all([
    searchService.searchWithQueries(queries, { topK: 20, filter: { kbId } }),
    searchService.keywordSearch(queries[queries.length - 1], { topK: 20, filter: { kbId } }),
  ]);
  
  // Step 3: RRF 融合
  const merged = rrfFusion(candidates.flat(), 10);
  
  // Step 4: Rerank 精排
  const final = await searchService.rerank(userQuery, merged, { topK: 5 });
  
  return final;
}

3. 知识库语言感知

// 建议:在知识库创建时检测语言
async function createKnowledgeBase(name: string) {
  const kb = await prisma.ekbKnowledgeBase.create({
    data: {
      name,
      config: {
        primaryLanguage: 'mixed',  // 'zh' | 'en' | 'mixed'
      }
    }
  });
  
  return kb;
}

// 检索时根据语言优化
if (kb.config.primaryLanguage === 'zh') {
  // 纯中文库:不翻译
  queries = [userQuery];
} else if (kb.config.primaryLanguage === 'en') {
  // 纯英文库:翻译
  queries = [...rewritten];
} else {
  // 混合库:双语查询
  queries = [userQuery, ...rewritten];
}

🐛 常见问题

Q1: 中文查询返回 0 结果?

原因

  • 文档是英文,查询是中文
  • minScore 阈值太高(跨语言相似度通常 0.2-0.35

解决

// 方案 1: 降低阈值
minScore: 0.2  // 跨语言场景

// 方案 2: 使用 DeepSeek V3 查询重写(推荐)
const queries = await rewriter.rewrite(userQuery);

Q2: 关键词检索返回 0 结果?

原因

  • 中文查询匹配不到英文文档
  • pg_bigm 是字符匹配,不是语义匹配

解决

// 必须用翻译后的查询
const keywordQuery = rewritten[0];  // 英文
await searchService.keywordSearch(keywordQuery);

Q3: pg_bigm 性能如何?

当前状态

  • pg_bigm v1.2 已安装2026-01-24
  • GIN 索引已创建(idx_ekb_chunk_content_bigm
  • 关键词检索已启用

性能对比

// 之前ILIKE 全表扫描):
// - 10万条记录500ms - 5s
// - 无索引,线性扫描

// 现在pg_bigm GIN 索引):
// - 10万条记录5ms - 50ms ⚡
// - 10-100倍性能提升

使用方法(无需修改代码):

// VectorSearchService 会自动使用 pg_bigm
const results = await searchService.keywordSearch('银杏叶副作用', {
  topK: 10,
  filter: { kbId: 'your-kb-id' }
});
// 自动使用 GIN 索引加速 ✅

📚 相关文档


🚀 快速测试

cd backend

# 测试 1: 向量化服务
npx tsx src/tests/test-embedding-service.ts

# 测试 2: 端到端(文档入库+检索)
npx tsx src/tests/test-rag-e2e.ts

# 测试 3: Rerank 效果
npx tsx src/tests/test-rerank.ts

# 测试 4: 查询重写(需要 DEEPSEEK_API_KEY
npx tsx src/tests/test-query-rewrite.ts

# 测试 5: PDF 入库
npx tsx src/tests/test-pdf-ingest.ts <pdf文件路径>

📅 版本历史

版本 日期 变更内容
v1.1 2026-01-24 pg_bigm v1.2 安装完成,关键词检索性能大幅提升
v1.0 2026-01-21 初版:基于 "Brain-Hand" 架构重构完成