Skip to content

混合检索

混合检索

介绍

混合检索(BM25 + Dense)→ 企业级 RAG 的标配,准确率较大提升

场景向量检索(Dense)BM25(Sparse)混合效果
查询“如何请病假?”✅ 能匹配“医疗期规定”等语义相近内容❌ 可能漏掉(无关键词)✔️ 互补
查询“订单号 A-9527 的状态?”❌ 向量模型觉得“A-9527”没语义,忽略✅ 精准命中含该编号的文档✔️ 必须用 BM25
查询“感冒怎么办?”✅ 可召回“上呼吸道感染”✅ 可召回“风寒、流感”(若分词覆盖)✔️ 更全面

Milvus混合检索

Milvus 支持在同一个 collection 中定义多个向量字段,并同时搜索它们。

img

两种典型混合场景

类型描述适用模型
稀疏+稠密混合检索结合 BM25(关键词匹配) + Dense Embedding(语义理解)BGE-M3(输出 dense)、传统 BM25
多模态混合检索文本 + 图像 + 音频联合搜索CLIP(图文)、Whisper(语音)等

RRF排序

RRF(Reciprocal Rank Fusion,互反排名融合)是一种用于融合多个排序列表(ranked lists)的算法

  • 无需归一化分数:不同检索系统(如关键词搜索 vs 向量搜索)的打分尺度不同,直接融合分数不可靠,而 RRF 只看排名,规避了这个问题。
  • 简单有效:无需训练,参数少(通常固定 k=60),在很多基准测试中表现优异。
  • 适合多路召回:例如同时使用稠密向量(Dense)、稀疏向量(Sparse)、全文检索(BM25)等,RRF 可有效融合它们的结果。

img

步骤建议值
稠密向量搜索 limit5 ~ 10
稀疏向量搜索 limit5 ~ 10
RRF 融合后输出 limit3

参数k设置

场景推荐 $ k $
默认 / 通用 / limit ≤ 1060 ✅
大召回(limit ≥ 20)且需平滑融合可试 80~100
你的当前设置(limit=5)坚持用 60

参考资料

Milvus混合检索实战

配置

  • 定义 Schema 和 BM25 函数
  • 配置索引
  • 创建 Collections,插入数据后

以上过程,参考文档《BM25检索》

创建多个 AnnSearchRequest 实例

混合搜索是通过在hybrid_search() 函数中创建多个AnnSearchRequest 来实现的,其中每个AnnSearchRequest 代表一个特定向量场的基本 ANN 搜索请求。

python
from pymilvus import (
    AnnSearchRequest,
    Function,
    FunctionType,
    MilvusClient,
    SearchResult,
)
from langchain_ollama import OllamaEmbeddings


# 嵌入模型
embeddings = OllamaEmbeddings(model="bge-m3", base_url="http://127.0.0.1:11434")

query = "危险化学品"
query_dense_vector = embeddings.embed_query(query)
# print(type(query_dense_vector))
# print(query_dense_vector)


request_1 = AnnSearchRequest(
    data=[query_dense_vector],
    anns_field="vector",
    param={"nprobe": 10},  # TODO 可调优
    limit=6,
)

request_2 = AnnSearchRequest(
    data=[query],
    anns_field="sparse_vector",
    param={},
    limit=6,
)

reqs = [request_1, request_2]

重排策略

对 ANN 搜索结果集进行合并和重新排序

  • RRF排序策略
  • 加权排序策略
python
# RRF排序策略
rrf_ranker = Function(
    name="rrf",
    input_field_names=[],
    function_type=FunctionType.RERANK,
    params={"reranker": "rrf", "k": 60},  # TODO 可调优
)

混合搜索

python
client = MilvusClient(uri="http://127.0.0.1:19530")
client.use_database(db_name="default_db")

res = client.hybrid_search(
    collection_name="default_collection", reqs=reqs, ranker=rrf_ranker, limit=3
)
print(type(res))
print(len(res[0]))
for i in res[0]:
    print(i)

输出结果示例

json
<class 'pymilvus.client.search_result.SearchResult'>
3
{'id': 464289779212620541, 'distance': 0.03154495730996132, 'entity': {}}
{'id': 464289779212620286, 'distance': 0.03151364624500275, 'entity': {}}
{'id': 464289779212620353, 'distance': 0.016393441706895828, 'entity': {}}

LangChain Milvus混合检索实战

介绍

密集+稀疏混合检索

img

配置

  • 定义 Schema 和 BM25 函数
  • 配置索引
  • 创建 Collections,插入数据后

以上过程,参考文档《BM25检索》

混合检索

python
from langchain_milvus import Milvus, BM25BuiltInFunction
from langchain_ollama import OllamaEmbeddings


embedding = OllamaEmbeddings(model="bge-m3", base_url="http://127.0.0.1:11434")

uri = "http://127.0.0.1:19530"
vectorstore = Milvus(
    embedding,
    builtin_function=BM25BuiltInFunction(),
    vector_field=["vector", "sparse"],
    connection_args={
        "uri": uri,
        "db_name": "default_db",
    },
    collection_name="default_collection",
    auto_id=True,
    consistency_level="Session",
)

# 执行混合检索
query = "热门专业top20中,前3名有哪些?"
docs_and_scores = vectorstore.similarity_search_with_score(
    query=query, k=3, ranker_type="rrf", ranker_params={"k": 60}
)

# 打印结果
for doc, score in docs_and_scores:
    print(doc.metadata)
    print(score)
    # print(doc.page_content[:50])  # 打印前50个字符

输出结果示例

检索方式分数特征分数范围含义
混合检索 (RRF)0.016 左右通常 < 0.1RRF 融合后的倒数排名分数
纯向量检索0.84/0.810 ~ 1余弦相似度分数
  • 分数非常小(例如 0.01639...),这通常是 RRF (Reciprocal Rank Fusion) 算法的特征。
  • 分数< 0.015时,两种检索都排名靠后,结果可能不相关
bash
{'source': 'uploads\\1\\691bf8886d054d458f7845176dbc1914ba41d566ec36843c40baf1692c25ed6e.md', 'kb_id': '1', 'doc_id': '1', 'original_filename': '热门专业top20.md', 'id': 464429499724323791}
0.016393441706895828
{'source': 'uploads\\1\\691bf8886d054d458f7845176dbc1914ba41d566ec36843c40baf1692c25ed6e.md', 'kb_id': '1', 'doc_id': '1', 'original_filename': '热门专业top20.md', 'id': 464429499724323792}
0.016129031777381897