在构建高效的检索增强生成(RAG)系统时,如何精确地从海量文档中提取相关信息是核心挑战。传统的RAG方法通常依赖于单一的向量数据库进行语义相似度检索,而关键词搜索则侧重于精确匹配。然而,这两种方法都有其局限性,混合搜索(Hybrid Search)应运而生,旨在融合两者的优势。
以一个包含Python错误代码及其定义和用例的文档集为例,当用户查询“什么是ERR_404_AUTH?”时:
- 经典RAG(仅向量检索): 会从向量数据库中检索所有与认证和错误相关的上下文文档,可能包含大量不直接相关的通用信息。
- 词法搜索(Lexical Search): 会精确搜索包含“What”、“is”、“ERR_404_AUTH”等词语的文档,但可能忽略语义上相关但词汇不完全匹配的内容。
- 混合搜索: 将同时搜索关键词“ERR_404_AUTH”,并通过语义相似度检索相关文档,从而实现更精准的匹配。
利用BM25进行关键词检索
BM25是关键词搜索的一种扩展,可以视为TF-IDF的增强版。在LangChain中,BM25Retriever的实现非常直接。以下是结合LangChain进行BM25检索的步骤和原理:
# pip install rank_bm25
from langchain_community.retrievers import BM25Retriever
from langchain_core.documents import Document
# 假设这是通过文本分割器生成的文档块
chunks = [
Document(page_content="The AX-705 engine uses a 4-stroke cycle."),
Document(page_content="Maintenance for AX-705 requires synthetic oil."),
Document(page_content="Four-stroke engines are common in modern cars.")
]
# 步骤:构建BM25索引(倒排索引)
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 2 # 检索Top 2结果构建混合“集成”检索器
为了充分利用关键词的精确性和语义的泛化性,可以将向量检索器与BM25检索器合并,创建一个混合“集成”检索器(Ensemble Retriever)。
from langchain.retrievers import EnsembleRetriever
# 假设'chroma_retriever'是已经创建好的向量检索器
hybrid_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, chroma_retriever],
weights=[0.3, 0.7] # 为关键词赋予30%的重要性,语义赋予70%的重要性
)BM25的内部工作机制
当调用 `hybrid_retriever.invoke("AX-705 engine")` 时,BM25部分会执行以下四个步骤:
- 分词(Tokenization): 查询“AX-705 engine”被拆分为词元,例如 ["ax-705", "engine"]。
- 查找(Lookup): 检索器在其“倒排索引”(一个字典结构)中查找哪些文档块包含这些精确的字符串。
- 评分(Scoring - f(q, d)): 使用BM25公式计算每个匹配项的得分:
- 稀有性: “AX-705”在数据库中较稀有,因此比“engine”的权重更高。
- 饱和度: 即使文档中多次出现“engine”,其得分也不会线性飙升,这可以防止“关键词堆砌”过度影响排名。
- 长度惩罚: 如果一个短小的文档块(例如10个词)同时匹配了查询中的两个词,它会比一个冗长的文档块(例如1000个词)获得更高的排名。
- 排序(Ranking): 根据计算出的得分,返回排序后的文档块列表。