本文档说明 Bio-RAG 项目的系统性实验设计,用于测试各种 RAG 技术组合在生物医学问答任务上的表现。
- 总量:4,220 个关于蛋白质相互作用的生物医学问答对
- 金标准:500 个专家标注的高质量问答对
- 银标准:3,720 个自动生成的问答对
- 领域:生物医学文献(药物发现、蛋白质相互作用网络)
数据来源:RAGPPI 数据集
每个样本包含:
id:唯一标识符question:用户问题context:参考上下文(蛋白质相互作用相关文献)answer:参考答案
统一分块流程(所有检索方法共享):
原始文档 → 语义分块 → 子文档(256 tokens)
↓
合并相邻子文档 → 父文档(1024 tokens)
核心组件:
| 组件 | 模型/实现 | 说明 |
|---|---|---|
| 语义分块器 | SemanticChunker + BAAI/bge-m3 |
LangChain 官方实现,使用 BGE-M3 计算语义相似度 |
| 嵌入包装器 | LangChainEmbeddingWrapper |
将 BGE-M3 包装为 LangChain 兼容接口,复用全局模型实例 |
| 父文档合并器 | 自定义实现 | 将相邻子文档合并成 ~1024 tokens 的父文档 |
核心参数:
- 子文档大小:256 tokens(语义分块基准)
- 父文档大小:1024 tokens(合并 3-4 个相邻子文档)
- 重叠:64 tokens
- 语义阈值:percentile 0.6(用于确定语义断点)
- 嵌入模型:
BAAI/bge-m3(CUDA,全局单例复用)
实现细节:
- 使用
SemanticChunker对原始文档进行语义分块,基于语义相似度自动确定断点- 内部使用
BAAI/bge-m3嵌入模型计算句子相似度 - 通过
LangChainEmbeddingWrapper复用全局 BGE-M3 实例,避免重复加载
- 内部使用
- 将相邻的子文档合并成父文档,确保每个父文档包含完整语义
- 子文档用于构建向量索引(精确检索)
- 父文档用于提供给 LLM(完整上下文)
嵌入模型复用机制:
# 全局共享的 BGE-M3 嵌入模型(只加载一次)
LocalEmbeddingModel (BAAI/bge-m3, CUDA)
↓
LangChainEmbeddingWrapper (包装器)
↓
SemanticChunker (用于语义分块)
DenseRetriever (用于向量检索)最终得到 33606 个子文档。
嵌入模型:BAAI/bge-m3
- 设备:
cuda(GPU 加速) - 批处理大小:32
- 向量归一化:启用
- 向量维度:1024
- 全局单例:复用同一模型实例,避免重复加载
检索索引:
- FAISS 密集向量索引:
IndexFlatIP(内积检索) - BM25 稀疏索引:词频-逆文档频率
缓存机制:
- 嵌入向量缓存到
./data/embeddings/目录 - 分块结果缓存到
./data/chunks/目录 - 避免重复计算,提升实验效率
两阶段检索策略:
Query → 早期召回 (20个子文档) → 重排序 (可选) → 映射回父文档 → 返回 5 个父文档
参数配置:
- 早期召回数量:20 个子文档(
EARLY_RECALL_K = 20) - 最终返回数量:5 个父文档(
top_k = 5) - 检索倍数:4(
retrieval_multiplier = 4)
检索方法:
| 方法 | 描述 |
|---|---|
FAISS_ONLY |
仅使用密集向量检索 |
BM25_ONLY |
仅使用稀疏向量检索(BM25) |
FAISS_BM25 |
混合检索(密集 + 稀疏) |
可选组件:
- 查询变换:Multi-Query(3个查询)、Query Decomposition、HyDE
- 结果融合:RAG Fusion(RRF 算法)
- 重排序:BGE ReRanker(
BAAI/bge-reranker-v2-m3,CUDA 加速)
LLM 模型:glm-4-flash(智谱 AI)
- 基础 URL:
https://open.bigmodel.cn/api/paas/v4/ - Temperature:0.3(较低温度保证稳定性)
- 最大 Tokens:2048
- 超时时间:300 秒
生成流程:
- 检索获取 5 个父文档作为上下文
- 构造提示词(包含问题、上下文)
- 调用 LLM 生成答案
- 返回生成的答案和检索的上下文
工业界 RAG 评估标准(分离检索和生成评估):
| 指标类型 | 指标 | 评估对象 | 评估粒度 | 说明 |
|---|---|---|---|---|
| 检索评估 | Context Precision | 检索到的子文档 | 子文档 (256 tokens) | 检索到的文档中有多少是相关的 |
| 检索评估 | Context Recall | 检索覆盖度 | 子文档 (256 tokens) | 相关子文档有多少被检索到 |
| 生成评估 | Faithfulness | 答案忠实度 | 父文档 (1024 tokens) | 生成答案是否基于检索到的上下文(无幻觉) |
| 生成评估 | Answer Relevancy | 答案相关性 | - | 答案与问题的相关程度 |
| 综合 | RAGAS Score | 综合分数 | - | 上述四个指标的平均值 |
评估设计原则:
- 检索评估:以真实召回的最小单元(子 Chunk,256 tokens)为评估主体
- 生成评估:以 LLM 实际输入上下文(合并父文档,1024 tokens)为评估主体
- 评估 GT:与检索切片粒度对齐,参考文档切为同粒度子 Chunk 作为 Recall 真值
评估 LLM:glm-4-flash(温度 0.0,确定性输出)
本实验采用因子化设计(Factorial Design),可以隔离每种 RAG 技术的独立贡献:
- 基线 (Baseline):建立性能参考点
- 查询变换 (Query Transform):测试查询变换技术
- 融合 (Fusion):测试 RAG Fusion
- 重排序 (Reranker):测试 BGE 重排序器
- 组合 (Combined):测试完整的现代 RAG 技术栈
| 实验ID | 名称 | 检索方法 | 说明 |
|---|---|---|---|
| B1 | FAISS基线 | FAISS_ONLY |
使用密集向量检索的基线参考 |
| B2 | BM25基线 | BM25_ONLY |
使用稀疏向量检索(BM25)的基线 |
| B3 | FAISS+BM25基线 | FAISS_BM25 |
混合检索基线,结合密集和稀疏检索 |
用途:建立基线性能。通过比较 B1/B2/B3,可以评估不同检索方法的差异。
| 实验ID | 名称 | 组件 | 说明 |
|---|---|---|---|
| Q1 | 多查询扩展 | B3 + Multi-Query | 生成 3 个查询变体以扩大检索范围 |
| Q2 | 查询分解 | B3 + Query Decomposition | 将复杂问题分解为 2-4 个子问题 |
| Q3 | HyDE | B3 + HyDE | 生成假设性答案用于检索 |
用途:隔离每种查询变换技术的效果。比较 Q1/Q2/Q3 与 B3,评估各技术的独立贡献。
| 实验ID | 名称 | 组件 | 说明 |
|---|---|---|---|
| F1 | RAG Fusion | B3 + RAG Fusion | 使用 RRF(倒数排名融合)算法合并密集/稀疏检索结果 |
用途:测量 RAG Fusion 相对于朴素混合检索的贡献。比较 F1 与 B3。
| 实验ID | 名称 | 组件 | 说明 |
|---|---|---|---|
| R1 | BGE重排序 | B3 + BGE Reranker | 使用 BGE-M3 模型对检索结果重排序 |
用途:测量重排序器的贡献。比较 R1 与 B3。
| 实验ID | 名称 | 组件 | 说明 |
|---|---|---|---|
| C1 | Fusion+Reranker | B3 + Fusion + Reranker | 测试融合与重排序的协同效果 |
| C2 | 完整技术栈+多查询 | C1 + Multi-Query | 完整现代 RAG 技术栈 + 多查询扩展 |
| C3 | 完整技术栈+查询分解 | C1 + Query Decomposition | 完整现代 RAG 技术栈 + 查询分解 |
| C4 | 完整技术栈+HyDE | C1 + HyDE | 完整现代 RAG 技术栈 + HyDE |
用途:测试完整的现代 RAG 管道。比较 C2/C3/C4 与 C1,评估查询变换在完整技术栈上的影响。
要测量每种技术的贡献,可以进行以下对比:
| 技术 | 对比方式 | 说明 |
|---|---|---|
| 检索方法对比 | B2 vs B1 vs B3 | BM25 vs FAISS vs 混合检索的差异 |
| 多查询扩展 | Q1 vs B3(隔离) C2 vs C1(完整栈) |
在基线和完整栈上的效果 |
| 查询分解 | Q2 vs B3(隔离) C3 vs C1(完整栈) |
在基线和完整栈上的效果 |
| HyDE | Q3 vs B3(隔离) C4 vs C1(完整栈) |
在基线和完整栈上的效果 |
| RAG Fusion | F1 vs B3(隔离) C1 vs R1(含重排序) |
在基线和含重排序时的效果 |
| BGE 重排序 | R1 vs B3(隔离) C1 vs F1(含融合) |
在基线和含融合时的效果 |
一般而言,预期:
- B3 ≈ 最佳基线:混合检索应优于单一检索方法
- C2/C3/C4 > C1:查询变换应在完整栈上有帮助
- C1 > B3:融合 + 重排序应显著优于基线
- C2/C3/C4 ≈ 最佳:完整现代 RAG 技术栈应达到最佳性能
python run.py run --sample-size 5python run.py run --experiments B1 B2 B3python run.py run --experiments Q1 Q2 Q3python run.py run --experiments C1 C2 C3 C4python run.py listpython run.py visualize --results-dir results/exp_xxx所有配置集中在 config.yaml 和 experiments.yaml 文件中:
config.yaml:
# 嵌入模型配置
embedding:
model_name: "BAAI/bge-m3"
device: "cuda"
batch_size: 32
normalize: true
# 检索配置
retrieval:
top_k: 5 # 最终返回给LLM的文档数量
retrieval_multiplier: 4 # 早期召回倍数 (5 * 4 = 20 个候选)
parent_chunk_size: 1024 # 父文档大小
child_chunk_size: 256 # 子文档大小
chunk_overlap: 64 # 分块重叠
# 重排序配置
reranker:
model_name: "BAAI/bge-reranker-v2-m3"
device: "cuda"
batch_size: 32
top_k: 5experiments.yaml:
- id: B1
name: "FAISS基线"
config:
retrieval: "faiss_only"
query_transform: null
fusion: null
reranker: null
- id: B2
name: "BM25基线"
config:
retrieval: "bm25_only"
query_transform: null
fusion: null
reranker: null
# ... 更多实验配置实验结果保存在 results/exp_YYYYMMDD_HHMMSS/ 目录下:
results/exp_20250331_120000/
├── samples.json # 采样的样本数据
├── summary.json # 实验汇总
├── comparison.csv # 对比表格
├── B1_result.json # 各实验的详细结果
├── B2_result.json
├── ...
├── metric_comparison.png # 指标对比图
└── ranking.png # 排名图
结果文件格式:
{
"experiment_id": "B1",
"config_name": "FAISS基线",
"metrics": {
"faithfulness_mean": 0.85,
"answer_relevancy_mean": 0.78,
"context_precision_mean": 0.65,
"context_recall_mean": 0.70,
"ragas_score_mean": 0.75
},
"detailed_results": [
{
"sample_id": "xxx",
"question": "...",
"generated_answer": "...",
"reference_answer": "...",
"retrieved_context": "...", # 父文档(LLM实际看到的)
"faithfulness": 0.85,
"answer_relevancy": 0.78,
"context_precision": 0.65,
"context_recall": 0.70
}
]
}# 运行 2 个样本的测试
python run.py run --experiments B1 B2 B3 --sample-size 2# 运行所有实验,采样 10 个
python run.py run --sample-size 10 --visualize# 比较不同的查询变换技术
python run.py run --experiments Q1 Q2 Q3 --sample-size 10# 查看结果列表
python run.py list
# 可视化指定实验的结果
python run.py visualize --results-dir results/exp_20250331_120000
# 比较两个实验
python run.py compare --exp1 results/exp_xxx/B1_result.json --exp2 results/exp_yyy/B2_result.jsonbio-RAG/
├── config.yaml # 统一配置文件
├── experiments.yaml # 实验定义(YAML配置驱动)
├── run.py # 主运行脚本
├── src/
│ ├── chunking/
│ │ └── semantic_chunker.py # 语义分块 + 父子文档(使用 BGE-M3)
│ ├── config.py # 配置加载
│ ├── constants.py # 常量定义
│ ├── data/
│ │ └── ragppi_loader.py # 数据加载
│ ├── utils/
│ │ └── embeddings.py # 嵌入模型(BGE-M3,全局单例)
│ ├── evaluation/
│ │ ├── evaluator.py # 评估协调器(分离检索和生成评估)
│ │ ├── faithfulness_metric.py
│ │ ├── answer_relevancy_metric.py
│ │ ├── context_precision_metric.py
│ │ └── context_recall_metric.py
│ ├── rag_pipelines/
│ │ └── unified_pipeline.py # 统一 RAG 管道
│ ├── retrieval/
│ │ ├── dense_retriever.py # FAISS 密集检索(使用 BGE-M3)
│ │ ├── sparse_retriever.py # BM25 稀疏检索
│ │ ├── hybrid_retriever.py # 混合检索
│ │ ├── rag_fusion.py # RAG Fusion
│ │ └── reranker/
│ │ └── bge_reranker.py # BGE 重排序器(CUDA)
│ └── utils/
│ └── llm/
│ └── zhipuai_client.py # 智谱 AI 客户端
└── data/
├── raw/ragppi/ # 原始数据
├── embeddings/ # 嵌入缓存
├── chunks/ # 分块缓存
└── results/ # 实验结果
| 组件 | 模型 | 设备 | 用途 |
|---|---|---|---|
| 嵌入模型 | BAAI/bge-m3 |
CUDA | 语义分块、向量检索 |
| 重排序器 | BAAI/bge-reranker-v2-m3 |
CUDA | 检索结果重排序 |
| 生成 LLM | glm-4-flash |
API | RAG 答案生成 |
| 评估 LLM | glm-4-flash |
API | Faithfulness、Answer Relevancy 等指标评估 |
- RAGPPI 数据集:Hugging Face - Youngseung/RAGPPI
- RAG Fusion:arXiv:2309.01718
- BGE 嵌入模型:BAAI/bge-m3
- BGE 重排序器:BAAI/bge-reranker-v2-m3
- 语义分块:LangChain SemanticChunker
- RAGAS 评估标准:RAGAS Documentation
- 智谱 AI:Zhipu AI GLM-4