构建更好的RAG系统:深入理解混合搜索

在构建检索增强生成(RAG)系统时,我们经常会遇到一个核心挑战:如何高效、准确地从海量知识库中检索出最相关的信息?

传统的向量搜索(语义搜索)虽然强大,但并非万能。今天,我想和大家深入探讨一种更强大的检索策略——混合搜索(Hybrid Search),它正在成为构建生产级RAG系统的关键组件。


为什么单一检索策略不够?

向量搜索的局限

向量搜索通过将文本转换为高维向量,能够捕捉语义相似性。但它在以下场景中表现不佳:

  • 精确关键词匹配:如产品型号“iPhone 15 Pro Max”

  • 罕见专有名词:如人名“埃隆·里夫·马斯克”

  • 特定短语:如法律条文中的精确表述

关键词搜索的局限

传统的BM25等关键词搜索擅长精确匹配,但:

  • 无法理解同义词(如“汽车”和“轿车”)

  • 无法捕捉上下文语义

  • 对拼写错误敏感


混合搜索:两全其美

混合搜索将语义搜索关键词搜索结合起来,通过智能融合机制,提供更全面、准确的检索结果。

核心架构

text
用户查询
    │
    ├───► 语义检索(向量搜索) ───┐
    │                              │
    └───► 关键词检索(BM25) ─────┼───► 结果融合 ──► 重排序 ──► 最终结果
                                   │
                                   └───► 融合算法(RRF/加权平均)

关键技术组件

1. 语义检索(向量检索)

使用嵌入模型(如OpenAI的text-embedding-3-small)将文本转换为向量,通过余弦相似度查找最相似的文档。

python
from sentence_transformers import SentenceTransformer
import numpy as np

model = SentenceTransformer('all-MiniLM-L6-v2')
query_vector = model.encode("什么是混合搜索?")
doc_vectors = model.encode(documents)
similarities = np.dot(query_vector, doc_vectors.T)

2. 关键词检索(BM25)

基于词频-逆文档频率(TF-IDF)的改进算法,考虑文档长度因素。

python
from rank_bm25 import BM25Okapi

tokenized_docs = [doc.split() for doc in documents]
bm25 = BM25Okapi(tokenized_docs)
scores = bm25.get_scores(query.split())

3. 结果融合策略

方法一:加权平均(Weighted Average)

python
# 归一化后加权求和
semantic_score = semantic_scores / max(semantic_scores)
keyword_score = keyword_scores / max(keyword_scores)

alpha = 0.7  # 语义权重
hybrid_score = alpha * semantic_score + (1 - alpha) * keyword_score

方法二:RRF(Reciprocal Rank Fusion)

python
def reciprocal_rank_fusion(ranked_lists, k=60):
    scores = {}
    for ranked_list in ranked_lists:
        for rank, doc_id in enumerate(ranked_list, 1):
            scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
    return sorted(scores.items(), key=lambda x: x[1], reverse=True)

方法三:学习排序(Learning to Rank)
使用机器学习模型学习最佳融合权重,适应特定领域数据。


实战演示:使用LlamaIndex实现混合搜索

python
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.vector_stores.elasticsearch import ElasticsearchVectorStore
from llama_index.core.retrievers import BM25Retriever
from llama_index.core.retrievers import RouterRetriever

# 1. 创建向量存储
vector_store = ElasticsearchVectorStore(
    index_name="hybrid_demo",
    es_url="http://localhost:9200"
)

# 2. 创建文档索引
documents = SimpleDirectoryReader("data").load_data()
vector_index = VectorStoreIndex.from_documents(
    documents, 
    vector_store=vector_store
)

# 3. 配置混合检索器
vector_retriever = vector_index.as_retriever(similarity_top_k=10)
bm25_retriever = BM25Retriever.from_documents(documents)

# 4. 融合检索结果
from llama_index.core.retrievers import FusionRetriever

hybrid_retriever = FusionRetriever(
    retrievers=[vector_retriever, bm25_retriever],
    mode="reciprocal_rerank"  # 使用RRF融合
)

# 5. 执行混合检索
results = hybrid_retriever.retrieve("什么是混合搜索?")

高级优化技巧

1. 分块策略优化

python
from llama_index.core.text_splitter import SentenceSplitter

# 使用语义感知的分块
splitter = SentenceSplitter(
    chunk_size=512,
    chunk_overlap=50,
    separator="。",  # 以句子为边界
)

2. 元数据过滤增强

python
# 在检索时添加元数据过滤
retriever = index.as_retriever(
    similarity_top_k=10,
    filters=MetadataFilters(
        filters=[
            ExactMatchFilter(key="category", value="技术文档"),
            RangeFilter(key="date", min_value="2024-01-01")
        ]
    )
)

3. 重排序(Re-ranking)

python
from llama_index.core.postprocessor import SentenceTransformerRerank

reranker = SentenceTransformerRerank(
    model="cross-encoder/ms-marco-MiniLM-L-6-v2",
    top_n=5
)

# 对混合搜索结果重排序
final_results = reranker.postprocess_nodes(results, query_str)
posted @ 2026-07-06 22:59  若-飞  阅读(15)  评论(0)    收藏  举报