AI 摘要

文章系统解析了RAG的索引构建与检索优化两大模块:前者用BGE-small向量化文档并构建FAISS索引,支持持久化与增量更新;后者在其上实现向量+BM25混合检索,用RRF重排序与元数据过滤提升召回与精度,形成“基础向量层—高级优化层”两级架构,兼顾语义与关键词互补,实现毫秒级精准检索。

📚 模块概述

这是一个索引构建模块,负责将文档块向量化并构建 FAISS 向量索引,是 RAG 系统中的存储层核心组件。它封装了嵌入模型和向量数据库的操作。


🎯 核心职责

  1. 向量化:使用嵌入模型将文本转换为向量
  2. 索引构建:使用 FAISS 构建高效的向量索引
  3. 索引持久化:保存和加载索引
  4. 相似度检索:基于向量相似度搜索文档

🏗️ 类结构分析

实例属性

def __init__(self, model_name, index_save_path):
    self.model_name = "BAAI/bge-small-zh-v1.5"  # 嵌入模型名称
    self.index_save_path = "./vector_index"      # 索引保存路径
    self.embeddings = None                       # 嵌入模型实例
    self.vectorstore = None                      # FAISS向量存储实例

设计特点

  • 支持自定义嵌入模型
  • 可配置索引存储位置
  • 懒加载机制(初始化时只创建 embeddings)

🔧 核心方法详解

setup_embeddings() - 嵌入模型初始化

功能:加载和配置 HuggingFace 嵌入模型

self.embeddings = HuggingFaceEmbeddings(
    model_name=self.model_name,           # 模型路径
    model_kwargs={'device': 'cpu'},       # 运行设备
    encode_kwargs={'normalize_embeddings': True}  # 向量归一化
)

关键配置解析

  1. 模型选择BAAI/bge-small-zh-v1.5

    • BGE(BAAI General Embedding)系列
    • small:轻量级版本(适合 CPU 推理)
    • zh:中文优化
    • v1.5:版本号
  2. 设备配置device='cpu'

    • 使用 CPU 推理(通用性强)
    • 可改为 'cuda' 使用 GPU 加速
  3. 向量归一化normalize_embeddings=True

    • 将向量归一化到单位长度
    • 使余弦相似度等价于点积(加速计算)
    • 提高相似度计算稳定性

为什么选择 BGE-small?

模型对比:
- BGE-small-zh: 约 100M,速度快,CPU 友好
- BGE-base-zh:  约 400M,精度更高,需要更多资源
- BGE-large-zh: 约 1.3G,精度最高,GPU 推荐

食谱应用场景:small 模型足够,平衡性能和精度

build_vector_index() - 向量索引构建

功能:将文档块向量化并构建 FAISS 索引

def build_vector_index(self, chunks: List[Document]) -> FAISS:
    self.vectorstore = FAISS.from_documents(
        documents=chunks,      # 待索引的文档列表
        embedding=self.embeddings  # 嵌入模型
    )
    return self.vectorstore

底层流程

1. 提取文档内容
   chunks → [doc1.page_content, doc2.page_content, ...]

2. 批量向量化
   embeddings.embed_documents(texts)
   → [[0.12, -0.45, ...], [0.33, 0.21, ...], ...]

3. 构建 FAISS 索引
   FAISS.IndexFlatL2 (或 IndexFlatIP)
   → 支持高效近邻搜索

4. 存储元数据
   保存每个向量对应的 Document 对象(包含 metadata)

FAISS 索引类型(LangChain 默认使用):

  • IndexFlatL2:精确 L2 距离搜索(如果未归一化)
  • IndexFlatIP:精确内积搜索(如果归一化)
  • 支持百万级向量的毫秒级检索

add_documents() - 增量添加文档

功能:向现有索引追加新文档(无需重建)

def add_documents(self, new_chunks: List[Document]):
    if not self.vectorstore:
        raise ValueError("请先构建向量索引")
    
    self.vectorstore.add_documents(new_chunks)

应用场景

# 场景:动态更新食谱库
index_module.build_vector_index(initial_chunks)  # 初始构建
# ... 用户添加新食谱 ...
index_module.add_documents(new_recipe_chunks)    # 增量更新
index_module.save_index()                        # 保存更新

优势

  • 避免全量重建(节省时间)
  • 支持热更新(无需重启服务)

save_index() & load_index() - 索引持久化

保存索引

def save_index(self):
    Path(self.index_save_path).mkdir(parents=True, exist_ok=True)
    self.vectorstore.save_local(self.index_save_path)

保存内容

vector_index/
├── index.faiss          # FAISS 索引文件(向量数据)
└── index.pkl            # 文档元数据(Document 对象)

加载索引

def load_index(self):
    if not Path(self.index_save_path).exists():
        logger.info("索引路径不存在,将构建新索引")
        return None
    
    self.vectorstore = FAISS.load_local(
        self.index_save_path,
        self.embeddings,
        allow_dangerous_deserialization=True  # 允许反序列化 pkl
    )

安全性考虑

  • allow_dangerous_deserialization=True:允许加载 pickle 文件
  • ⚠️ 风险:pickle 可能包含恶意代码
  • 缓解措施:仅加载可信来源的索引文件

冷启动优化

# 首次启动
if not index_module.load_index():  # 加载失败
    index_module.build_vector_index(chunks)  # 构建新索引
    index_module.save_index()  # 保存以备下次使用

# 后续启动
index_module.load_index()  # 直接加载,秒级启动

similarity_search() - 相似度检索

功能:根据查询文本检索最相似的文档

def similarity_search(self, query: str, k: int = 5) -> List[Document]:
    return self.vectorstore.similarity_search(query, k=k)

底层算法

1. 查询向量化
   query → embeddings.embed_query("红烧肉怎么做")
   → [0.23, -0.12, 0.45, ...]

2. 向量相似度计算
   FAISS.search(query_vector, k=5)
   → 找到最近的 5 个向量

3. 返回对应文档
   根据向量索引返回 Document 对象(含 metadata)

相似度度量(归一化向量):

# 余弦相似度
similarity = dot(query_vec, doc_vec) / (norm(query_vec) * norm(doc_vec))

# 归一化后简化为点积
similarity = dot(query_vec, doc_vec)  # 范围 [-1, 1]

检索示例

# 输入
query = "简单的荤菜做法"
results = index_module.similarity_search(query, k=3)

# 输出(按相似度排序)
[
  Document(page_content="## 红烧肉\n...", metadata={"category": "荤菜", "difficulty": "简单"}),
  Document(page_content="## 糖醋排骨\n...", metadata={"category": "荤菜", "difficulty": "中等"}),
  Document(page_content="## 清蒸鱼\n...", metadata={"category": "水产", "difficulty": "简单"})
]

🔄 完整工作流程

┌─────────────────────────────────────────────────────────┐
│ 1. 初始化阶段                                            │
│    IndexConstructionModule()                            │
│    └─> setup_embeddings()  # 加载嵌入模型               │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 2. 索引构建/加载                                         │
│    if load_index():           # 尝试加载已有索引        │
│        使用现有索引                                      │
│    else:                                                │
│        build_vector_index()   # 从头构建索引            │
│        save_index()           # 保存以备后用            │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 3. 检索阶段                                              │
│    similarity_search("用户查询", k=5)                    │
│    └─> 返回最相似的 5 个文档块                          │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│ 4. 增量更新(可选)                                      │
│    add_documents(new_chunks)  # 添加新文档               │
│    save_index()               # 持久化更新              │
└─────────────────────────────────────────────────────────┘

💡 设计亮点

1. 向量归一化

encode_kwargs={'normalize_embeddings': True}

好处

  • 余弦相似度 = 点积(计算更快)
  • 避免向量长度影响相似度
  • 数值稳定性更好

2. 懒加载机制

# 初始化时只加载模型
self.embeddings = HuggingFaceEmbeddings(...)
self.vectorstore = None  # 不立即构建索引

# 实际使用时再构建/加载
index_module.load_index()  # 或 build_vector_index()

优势:节省启动时间,支持按需构建

3. 索引持久化

save_index() / load_index()

价值

  • 避免每次启动都重新向量化(耗时)
  • 10,000 个文档块向量化可能需要几分钟
  • 加载预构建索引只需几秒

4. 增量更新支持

add_documents(new_chunks)

场景:支持动态知识库更新,无需全量重建


📊 性能分析

向量化速度(BGE-small-zh,CPU)

文档数量     向量化时间      索引构建时间
  100         ~2秒            ~0.1秒
  1,000       ~20秒           ~1秒
  10,000      ~3分钟          ~10秒

检索速度(FAISS IndexFlat)

索引规模      Top-5 检索时间
  1,000       <1ms
  10,000      ~2ms
  100,000     ~20ms
  1,000,000   ~200ms

内存占用(768 维向量)

每个向量:768 * 4 bytes = 3KB
10,000 个向量:约 30MB
100,000 个向量:约 300MB

🎯 与数据准备模块的协作

# 数据准备模块
data_prep = DataPreparationModule(data_path="./data/cook")
data_prep.load_documents()  # 加载父文档
chunks = data_prep.chunk_documents()  # 生成子文档块

# 索引构建模块
index_builder = IndexConstructionModule(
    model_name="BAAI/bge-small-zh-v1.5",
    index_save_path="./vector_index"
)

# 为子文档块构建向量索引
index_builder.build_vector_index(chunks)  # 索引子块(小块检索精准)
index_builder.save_index()

# 检索时
child_chunks = index_builder.similarity_search("红烧肉做法", k=5)  # 检索子块
parent_docs = data_prep.get_parent_documents(child_chunks)  # 获取完整父文档

分工明确

  • 数据准备模块:管理文档内容和父子关系
  • 索引构建模块:管理向量和相似度检索

🔧 实际应用示例

场景 1:首次构建索引

# 初始化
index_module = IndexConstructionModule()

# 加载数据
from rag_modules.data_preparation import DataPreparationModule
data_module = DataPreparationModule("./data/C8/cook")
data_module.load_documents()
chunks = data_module.chunk_documents()

# 构建索引
index_module.build_vector_index(chunks)
index_module.save_index()

场景 2:服务启动(使用缓存)

index_module = IndexConstructionModule(index_save_path="./vector_index")

if not index_module.load_index():  # 尝试加载
    # 加载失败,重新构建
    chunks = load_chunks()  # 从某处获取文档块
    index_module.build_vector_index(chunks)
    index_module.save_index()

场景 3:混合检索基础

# 向量检索(语义相似度)
vector_results = index_module.similarity_search("低热量素菜", k=10)

# 后续可结合元数据过滤、重排序等高级技术
# (在其他模块中实现)

🎓 总结

index_construction.py 是 RAG 系统的向量检索引擎

  1. 核心能力

    • 使用 BGE 嵌入模型将文本转换为语义向量
    • 使用 FAISS 构建高效的向量索引
    • 支持相似度检索和增量更新
  2. 设计优势

    • 向量归一化提升计算效率
    • 索引持久化避免重复向量化
    • 懒加载机制优化启动速度
  3. 与其他模块协作

    • 接收数据准备模块的子文档块
    • 提供检索结果给查询理解模块
    • 支持高级检索策略(混合检索、重排序等)

这是一个轻量、高效、易扩展的向量索引模块,为 RAG 系统提供了坚实的检索基础。

我来详细分析 retrieval_optimization.py 检索优化模块,并结合 index_construction.py 讲解它们的协作关系。

读取 [](file:///d%3A/files/%E5%AE%9E%E4%B9%A0/%E6%88%91%E7%9A%84%E8%AE%A1%E5%88%92/all-in-rag/code/C8/rag_modules/retrieval_optimization.py)

读取 [](file:///d%3A/files/%E5%AE%9E%E4%B9%A0/%E6%88%91%E7%9A%84%E8%AE%A1%E5%88%92/all-in-rag/code/C8/rag_modules/retrieval_optimization.py#150-150),行 150 到 163

现在让我详细讲解检索优化模块,并结合索引构建模块分析它们的协作关系:

📚 模块概述

retrieval_optimization.py 是 RAG 系统的高级检索层,在基础向量检索之上实现了多种检索优化策略。它与 index_construction.py 形成两层架构

┌────────────────────────────────────────────────────┐
│  retrieval_optimization.py (检索优化层)             │
│  - 混合检索 (Hybrid Search)                        │
│  - RRF 重排序                                      │
│  - 元数据过滤                                       │
└─────────────────┬──────────────────────────────────┘
                  │ 调用
┌─────────────────▼──────────────────────────────────┐
│  index_construction.py (基础检索层)                 │
│  - 向量检索 (Vector Search)                        │
│  - FAISS 索引管理                                  │
└────────────────────────────────────────────────────┘

🎯 核心职责

  1. 混合检索:融合向量检索(语义)+ BM25检索(关键词)
  2. RRF 重排序:合理融合多路检索结果
  3. 元数据过滤:基于结构化信息精确筛选

🏗️ 类结构与依赖关系

初始化分析

def __init__(self, vectorstore: FAISS, chunks: List[Document]):
    self.vectorstore = vectorstore  # 来自 index_construction 模块
    self.chunks = chunks            # 来自 data_preparation 模块
    self.setup_retrievers()

依赖关系图

IndexConstructionModule.build_vector_index(chunks)
            │
            ├─> vectorstore (FAISS 对象)
            │
            ▼
RetrievalOptimizationModule(vectorstore, chunks)
            │
            ├─> vector_retriever  (使用 vectorstore)
            └─> bm25_retriever    (使用 chunks)

为什么需要同时传入 vectorstore 和 chunks?

  • vectorstore:用于语义向量检索
  • chunks:用于构建 BM25 关键词索引

🔧 核心方法详解

setup_retrievers() - 双检索器初始化

def setup_retrievers(self):
    # 1. 向量检索器(基于 index_construction 的 vectorstore)
    self.vector_retriever = self.vectorstore.as_retriever(
        search_type="similarity",     # 相似度搜索
        search_kwargs={"k": 5}        # 返回 Top-5
    )
    
    # 2. BM25检索器(基于原始文档块)
    self.bm25_retriever = BM25Retriever.from_documents(
        self.chunks,
        k=5
    )

检索器 1:向量检索器

原理:语义相似度匹配

# 底层调用链
vector_retriever.get_relevant_documents("红烧肉做法")
    ↓
vectorstore.similarity_search("红烧肉做法", k=5)
    ↓
FAISS 向量检索(余弦相似度)
    ↓
返回语义最相似的 5 个文档

优势

  • 理解语义:能匹配同义词、近义词
  • 示例:查询"简单菜肴" 能检索到"容易制作的菜品"

劣势

  • 关键词丢失:如果查询包含专有名词,可能匹配不准

检索器 2:BM25 检索器

原理:关键词匹配 + TF-IDF 改进

# BM25 算法核心公式
score = Σ IDF(qi) × [f(qi, D) × (k1 + 1)] / [f(qi, D) + k1 × (1 - b + b × |D| / avgdl)]

其中:
- qi: 查询词
- f(qi, D): 词频
- |D|: 文档长度
- avgdl: 平均文档长度
- k1, b: 调参参数

优势

  • 精确匹配:能准确定位关键词
  • 示例:查询"红烧肉" 必定匹配包含"红烧肉"的文档

劣势

  • 无语义理解:查询"简单菜肴"不会匹配"容易制作"

hybrid_search() - 混合检索(核心方法)

def hybrid_search(self, query: str, top_k: int = 3) -> List[Document]:
    # 步骤1: 分别检索
    vector_docs = self.vector_retriever.get_relevant_documents(query)
    bm25_docs = self.bm25_retriever.get_relevant_documents(query)
    
    # 步骤2: RRF 融合重排
    reranked_docs = self._rrf_rerank(vector_docs, bm25_docs)
    
    # 步骤3: 返回 Top-K
    return reranked_docs[:top_k]

工作流程图

用户查询:"简单的红烧肉做法"
        │
        ├─────────────────┬─────────────────┐
        │                 │                 │
   向量检索         BM25检索          (并行执行)
        │                 │
   语义匹配         关键词匹配
        │                 │
   [doc1, doc3]     [doc2, doc1]
   [doc5, doc7]     [doc3, doc4]
   [doc9, ...]      [doc6, ...]
        │                 │
        └─────────┬───────┘
                  │
            RRF 重排序
                  │
        [doc1, doc3, doc2, doc5, ...]
                  │
            取 Top-3
                  │
        [doc1, doc3, doc2]

_rrf_rerank() - RRF 重排序算法

RRF (Reciprocal Rank Fusion) 算法详解

算法公式

RRF_score(d) = Σ 1 / (k + rank_i(d))

其中:
- d: 文档
- rank_i(d): 文档在第 i 个检索结果中的排名
- k: 平滑参数(默认 60)

代码实现

def _rrf_rerank(self, vector_docs, bm25_docs, k=60):
    doc_scores = {}
    doc_objects = {}
    
    # 1. 处理向量检索结果
    for rank, doc in enumerate(vector_docs):
        doc_id = hash(doc.page_content)
        doc_objects[doc_id] = doc
        
        # RRF 分数计算
        rrf_score = 1.0 / (k + rank + 1)
        doc_scores[doc_id] = doc_scores.get(doc_id, 0) + rrf_score
    
    # 2. 处理 BM25 检索结果(累加分数)
    for rank, doc in enumerate(bm25_docs):
        doc_id = hash(doc.page_content)
        doc_objects[doc_id] = doc
        
        rrf_score = 1.0 / (k + rank + 1)
        doc_scores[doc_id] = doc_scores.get(doc_id, 0) + rrf_score
    
    # 3. 按最终分数排序
    sorted_docs = sorted(doc_scores.items(), key=lambda x: x[1], reverse=True)
    
    # 4. 构建结果(附加 RRF 分数到元数据)
    reranked_docs = []
    for doc_id, final_score in sorted_docs:
        doc = doc_objects[doc_id]
        doc.metadata['rrf_score'] = final_score
        reranked_docs.append(doc)
    
    return reranked_docs

RRF 算法优势

示例说明

查询: "简单的红烧肉做法"

向量检索结果 (语义相似):
  Rank 1: 红烧肉菜谱 (doc_A)
  Rank 2: 糖醋排骨菜谱 (doc_B)
  Rank 3: 清蒸鱼菜谱 (doc_C)
  Rank 4: 红烧茄子菜谱 (doc_D)
  Rank 5: 麻婆豆腐菜谱 (doc_E)

BM25 检索结果 (关键词匹配):
  Rank 1: 红烧肉菜谱 (doc_A)     # 含"红烧肉"
  Rank 2: 红烧茄子菜谱 (doc_D)   # 含"红烧"
  Rank 3: 番茄炒蛋菜谱 (doc_F)
  Rank 4: 宫保鸡丁菜谱 (doc_G)
  Rank 5: 鱼香肉丝菜谱 (doc_H)

RRF 分数计算 (k=60):
  doc_A: 1/(60+1) + 1/(60+1) = 0.0328 (两个第1名)
  doc_D: 1/(60+4) + 1/(60+2) = 0.0317 (向量第4,BM25第2)
  doc_B: 1/(60+2) = 0.0161              (仅向量第2)
  doc_C: 1/(60+3) = 0.0159              (仅向量第3)
  doc_F: 1/(60+3) = 0.0159              (仅BM25第3)
  ...

最终排序:
  1. doc_A (红烧肉) - 0.0328  ✓ 两种检索都排名靠前
  2. doc_D (红烧茄子) - 0.0317
  3. doc_B (糖醋排骨) - 0.0161
  ...

RRF 的智能之处

  • 互补性:同时出现在两个列表的文档会获得更高分数
  • 公平性:不依赖各检索器的原始分数(可能量纲不同)
  • 稳健性:对排名变化不敏感(使用倒数)

metadata_filtered_search() - 元数据过滤检索

def metadata_filtered_search(self, query: str, filters: Dict[str, Any], top_k: int = 5):
    # 步骤1: 先混合检索,获取更多候选(3倍)
    docs = self.hybrid_search(query, top_k * 3)
    
    # 步骤2: 应用元数据过滤
    filtered_docs = []
    for doc in docs:
        match = True
        for key, value in filters.items():
            if key in doc.metadata:
                if isinstance(value, list):
                    # 列表:包含判断
                    if doc.metadata[key] not in value:
                        match = False
                        break
                else:
                    # 单值:精确匹配
                    if doc.metadata[key] != value:
                        match = False
                        break
            else:
                match = False
                break
        
        if match:
            filtered_docs.append(doc)
            if len(filtered_docs) >= top_k:
                break
    
    return filtered_docs

应用场景

# 场景1: 查找简单的荤菜
results = module.metadata_filtered_search(
    query="家常菜做法",
    filters={
        'category': '荤菜',
        'difficulty': '简单'
    },
    top_k=3
)

# 场景2: 查找多个分类的菜品
results = module.metadata_filtered_search(
    query="快手菜",
    filters={
        'category': ['素菜', '汤品'],  # 列表:OR 关系
        'difficulty': '非常简单'
    },
    top_k=5
)

为什么先检索 3 倍?

# 问题:直接检索 top_k=5 可能不够
hybrid_search(query, top_k=5)  # 假设返回 [doc1, doc2, doc3, doc4, doc5]
# 经过元数据过滤后,可能只剩 [doc2, doc4],不足 5 个

# 解决:先检索 3 倍,留有过滤空间
hybrid_search(query, top_k=15)  # 返回 15 个候选
# 过滤后仍能保证足够的结果数量

🔄 与 index_construction.py 的协作流程

完整调用链

# ========== 第1步:索引构建(index_construction.py)==========
from rag_modules.index_construction import IndexConstructionModule

# 初始化索引模块
index_module = IndexConstructionModule(
    model_name="BAAI/bge-small-zh-v1.5",
    index_save_path="./vector_index"
)

# 构建向量索引
vectorstore = index_module.build_vector_index(chunks)
# 内部操作:
#   1. chunks → embeddings.embed_documents(texts)
#   2. vectors → FAISS.from_documents()
#   3. 返回 FAISS vectorstore


# ========== 第2步:检索优化(retrieval_optimization.py)==========
from rag_modules.retrieval_optimization import RetrievalOptimizationModule

# 传入 vectorstore 和 chunks
retrieval_module = RetrievalOptimizationModule(
    vectorstore=vectorstore,  # 来自 index_module
    chunks=chunks             # 原始文档块
)

# 内部操作:
#   1. self.vector_retriever = vectorstore.as_retriever()  # 封装向量检索
#   2. self.bm25_retriever = BM25Retriever.from_documents(chunks)  # 构建BM25索引


# ========== 第3步:混合检索 ==========
results = retrieval_module.hybrid_search("红烧肉怎么做", top_k=3)

# 调用链:
# hybrid_search()
#   ├─> vector_retriever.get_relevant_documents()
#   │   └─> vectorstore.similarity_search()  ← 调用 index_construction 的向量检索
#   │       └─> FAISS.search()
#   │
#   ├─> bm25_retriever.get_relevant_documents()
#   │   └─> BM25 关键词匹配
#   │
#   └─> _rrf_rerank()  # 融合两路结果

📊 两个模块的职责对比

维度index_construction.pyretrieval_optimization.py
抽象层级基础检索层高级检索层
核心功能向量化 + 索引构建混合检索 + 重排序
检索策略单一向量检索向量 + BM25 混合
依赖关系独立模块依赖 vectorstore
输出FAISS vectorstore 对象优化后的文档列表
使用场景冷启动、索引管理实际查询处理

💡 混合检索的优势示例

场景对比

查询: "低热量的素菜做法"

仅向量检索

index_module.similarity_search("低热量的素菜做法", k=5)

结果

1. 清炒时蔬 (语义相似:健康、清淡)
2. 凉拌黄瓜 (语义相似:低卡)
3. 蒸南瓜   (语义相似:健康)
4. 红烧茄子 (误匹配:茄子是素菜,但做法偏油腻)
5. 糖醋排骨 (误匹配:仅因"做法"相似)

仅 BM25 检索

bm25_retriever.get_relevant_documents("低热量的素菜做法")

结果

1. 素炒豆芽 (含"素菜")
2. 清蒸鱼   (含"做法",但不是素菜)
3. 凉拌海带 (含"素菜")
4. 低卡沙拉 (含"低热量")
5. 炸茄盒   (含"素菜",但高热量)

混合检索 (RRF)

retrieval_module.hybrid_search("低热量的素菜做法", top_k=5)

结果

1. 清炒时蔬 (向量第1 + BM25第1 → RRF高分) ✓
2. 凉拌黄瓜 (向量第2 + BM25第4 → RRF高分) ✓
3. 素炒豆芽 (向量第6 + BM25第1 → RRF中分) ✓
4. 低卡沙拉 (向量第5 + BM25第4 → RRF中分) ✓
5. 蒸南瓜   (向量第3 → RRF低分)            ✓

优势体现

  • 同时考虑语义(低热量 ≈ 健康、清淡)
  • 同时考虑关键词(必须包含"素菜")
  • 互补性增强,减少误匹配

🎯 实际应用示例

完整代码流程

# 步骤1: 构建索引
from rag_modules.data_preparation import DataPreparationModule
from rag_modules.index_construction import IndexConstructionModule

data_module = DataPreparationModule("./data/C8/cook")
data_module.load_documents()
chunks = data_module.chunk_documents()

index_module = IndexConstructionModule()
vectorstore = index_module.build_vector_index(chunks)
index_module.save_index()


# 步骤2: 初始化检索优化
from rag_modules.retrieval_optimization import RetrievalOptimizationModule

retrieval_module = RetrievalOptimizationModule(
    vectorstore=vectorstore,
    chunks=chunks
)


# 步骤3: 不同检索策略
# 策略1: 混合检索(平衡)
results = retrieval_module.hybrid_search("家常菜", top_k=3)

# 策略2: 带过滤的混合检索(精准)
results = retrieval_module.metadata_filtered_search(
    query="家常菜",
    filters={
        'category': '荤菜',
        'difficulty': ['简单', '非常简单']
    },
    top_k=3
)

# 步骤4: 获取完整父文档(结合 data_preparation)
parent_docs = data_module.get_parent_documents(results)

🎓 总结

模块定位

RAG 检索架构(三层设计):

┌──────────────────────────────────────┐
│ 应用层: main.py / API                │
│ - 用户查询入口                        │
│ - 结果整合与返回                      │
└─────────────┬────────────────────────┘
              │
┌─────────────▼────────────────────────┐
│ 检索优化层: retrieval_optimization   │
│ - 混合检索 (向量 + BM25)             │
│ - RRF 重排序                         │
│ - 元数据过滤                         │
└─────────────┬────────────────────────┘
              │
┌─────────────▼────────────────────────┐
│ 基础检索层: index_construction       │
│ - 向量化 (BGE embeddings)            │
│ - FAISS 索引管理                     │
│ - 基础相似度搜索                     │
└──────────────────────────────────────┘

核心价值

  1. 检索优化模块

    • 融合多种检索策略,提高召回率和准确率
    • RRF 算法智能融合,无需调参
    • 元数据过滤支持精准控制
  2. 与索引构建模块的协作

    • index_construction 提供向量检索能力
    • retrieval_optimization 增强检索效果
    • 解耦设计,易于扩展和维护
  3. 技术亮点

    • 互补检索:向量(语义)+ BM25(关键词)
    • 智能融合:RRF 算法平衡多路结果
    • 精准过滤:结构化元数据筛选

这是一个生产级 RAG 检索系统的标准实现,平衡了召回率、精确率和系统复杂度。