混合检索架构:BM25+向量+重排序的最佳实践

为什么单一检索不够? 在RAG(检索增强生成)系统中,检索质量直接决定了生成质量。许多团队在初次搭建RAG时选择纯向量检索,但在生产环境中很快遇到瓶颈:精确匹配查询失败、专业术语召回率低、长尾知识覆盖率不足。 问题的根源在于:向量检索擅长语义匹配,但不擅长精确匹配;BM25擅长关键词匹配,但不理解语义。 两者各有盲区,而混合检索正是取长补短的解决方案。 三阶段混合检索架构 架构总览 用户查询 │ ├──→ BM25 检索(关键词召回) │ ↓ Top-K1 ├──→ 向量检索(语义召回) │ ↓ Top-K2 │ └──→ 融合排序(RRF/加权融合) ↓ Top-N 重排序模型(Cross-Encoder) ↓ Top-M 最终上下文 第一阶段:双路召回 BM25:关键词召回的基石 from rank_bm25 import BM25Okapi import jieba class BM25Retriever: def __init__(self, documents): self.documents = documents # 中文需要分词 self.tokenized_docs = [list(jieba.cut(doc)) for doc in documents] self.bm25 = BM25Okapi(self.tokenized_docs) def search(self, query, top_k=20): tokenized_query = list(jieba.cut(query)) scores = self.bm25.get_scores(tokenized_query) # 获取Top-K结果 top_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:top_k] return [(idx, scores[idx]) for idx in top_indices] BM25的优势在于:对精确关键词查询、产品型号、代码片段标识符等场景的召回率远高于向量检索。 向量检索:语义召回的主力 from sentence_transformers import SentenceTransformer import numpy as np class VectorRetriever: def __init__(self, model_name="BAAI/bge-large-zh-v1.5"): self.model = SentenceTransformer(model_name) self.document_embeddings = None self.documents = None def index(self, documents): self.documents = documents self.document_embeddings = self.model.encode( documents, normalize_embeddings=True, batch_size=64, show_progress_bar=True ) def search(self, query, top_k=20): query_embedding = self.model.encode([query], normalize_embeddings=True) # 余弦相似度(已归一化,直接点积) scores = np.dot(self.document_embeddings, query_embedding.T).flatten() top_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:top_k] return [(idx, scores[idx]) for idx in top_indices] 第二阶段:分数融合 双路召回后需要将结果融合。业界有两种主流策略: ...

2026-07-29 · 3 min · 475 words · 硅基 AGI 探索者
鲁ICP备2026018361号