原创 与你一起学习的 2025-11-11 17:58 上海
一篇文章搞明白两大类型的多向量混合检索
今天是双十一,唠点做电商搜索常踩的坑:复杂描述下的召回率偏低问题。
可能不少人都遇到过这个情况,用户发了张裙子照片,还特意备注 “北方、冬天、妈妈穿”,检索结果却匹配到一条花色、版型相似但明显为夏季穿着的沙滩裙,与需求完全不符。
医疗、法律领域做检索时,也经常出现类似问题:医生搜索 “2 型糖尿病 儿童患者 病例” ,系统却召回一堆 1 型糖尿病的资料,语义相近,但与需求牛头不对马嘴;法律场景更典型,用户明确查询 “XX 场景下的连带责任” 相关法条和案例,系统却返回大量 “XX 场景补充责任” 的文书,虽然就两字之差,实际法律含义却相去甚远。
问题看似各不相同,但本质其实都一样:单靠一种维度的信息(也就是单向量检索),根本应付不了复杂场景的需求:电商搜索需要的是图片 + 文字描述的多模态检索能力;医疗、法律场景则需要把语义理解和关键词精准匹配 结合起来。
要解决这类问题,就需要引入多向量混合检索。其核心在于整合文本语义、关键词及不同模态信息,在多维度上并行检索后对结果进行 rerank,使检索结果更贴合实际需求。
那么,多向量混合检索到底是什么,该怎么用?以下是技术解读以及手把手应用实践。
01
多向量混合检索的两种核心类型
多向量混合检索本质就是整合不同搜索方法或跨模态的embedding信息,主要分为两类:
稀疏 - 稠密向量搜索:稠密向量擅长捕捉语义关系,稀疏向量适合精准的关键词匹配。两者结合后,既能理解整体概念,又能匹配精确术语,弥补了单一方法的不足,让复杂查询的结果更优。
以下是稀疏 - 稠密向量混合检索的示范:
https://milvus.io/docs/hybrid_search_with_milvus.md
多模态向量搜索:milvus目前支持跨文本、图像、音频等多种数据类型搜索,能把不同形式的信息整合为连贯的搜索体验。比如电商搜索中,用户输入文本查询+照片,系统可以同时匹配产品的文字描述和实物图像,让结果更准、更丰富。
02
Milvus/Zilliz Cloud 的核心支持能力
要落地多向量混合检索,Milvus 和 Zilliz Cloud 提供了全方位的技术支持:
灵活的Schema定义:能在Collection Schema中定义多个向量字段,支持整数主键(INT64)、字符串(VARCHAR)、稠密向量(DENSE_VECTOR)、稀疏向量(SPARSE_FLOAT_VECTOR)等多种数据类型,还能开启全文搜索分析器(enable_analyzer)。
多样化索引与度量:提供自动索引(AUTOINDEX)、稀疏倒排索引(SPARSE_INVERTED_INDEX)等类型,支持内积(IP)、BM25 等度量方式,适配不同类型向量的搜索需求。
内置全文搜索:集成 BM25 算法,能自动从文本字段生成稀疏向量,不用用户手动计算,简化数据准备流程。
完善的重排序机制:支持 RRFRanker 等多种策略,支持对多个ANN搜索结果集进行合并与重新排序,高效输出最优结果。
严谨的搜索流程:明确规定了搜索请求实例创建、集合加载、参数配置等步骤,保障混合搜索的稳定性和准确性。
凭借上述能力,多向量混合检索可以落地到多种实际场景:
社交媒体内容检索:比如搜索同时包含特定文本描述和图像的推文,要求文本和图像都满足语义匹配。
电商产品搜索:用户输入文本查询后,系统同时关联产品的文字描述(语义 + 关键词)和产品图像,实现多维度精准匹配。
复杂信息查询:针对有多维度介绍的对象(比如多字段商品说明、文档摘要),整合多个字段的向量进行全面检索,避免遗漏关键信息。
03
示例
下面以 产品文本 + 图像的检索场景为例,详细拆解多向量混合搜索的实现步骤。
这里默认每个产品都有文字描述和图片,能支持三种搜索方式:
语义文本搜索:根据文本的实际含义找产品。比如搜 适合夏天穿的宽松上衣,能找到夏季透气款 oversizeT 恤这种意思相近的描述,不用严格匹配关键词。这需要把文本转换成稠密向量,常用 BERT、Transformers 这类模型,或者 OpenAI 的服务来生成。
全文搜索:根据关键词精准匹配。比如搜 “纯棉 白色”,能直接定位到描述里包含这两个词的产品。这需要把文本转换成稀疏向量,可以用 BM25 算法,或者 BGE-M3、SPLADE 这类专门生成稀疏向量的模型。
多模态图像搜索:用文字描述找对应的产品图片。比如搜 “红色带花纹的连衣裙”,能直接找到符合这个描述的裙子图片。这需要把图片转换成稠密向量,常用 CLIP 这类模型来生成。
接下来,我们会结合产品的文字描述和图片信息,一步步讲清楚怎么把这三种搜索结合起来,实现多模态混合搜索。核心是先学会怎么存储这些多类型的向量数据,再用合适的方法给搜索结果排序。
(1)创建具有多个向量字段的集合
要实现混合搜索,首先得建一个能容纳多种向量的 collection。建这个collection分三步:定义 Collection Schema、配置索引参数和创建 Collection。
这里先重点说第一步 ——定义Collection Schema。
因为要支持三种搜索,模板里得有对应不同向量的字段,具体包括这 5 个:
id:每个产品的唯一编号(类似身份证号),用整数类型(INT64)存储,作为区分不同产品的主键。
text:用于存储产品的文字描述(比如 “红色纯棉连衣裙,长度及膝”),用字符串类型(VARCHAR),最长能存 1000 字节。enable_analyzer选项设置为True,开启文本分析功能,方便后面做全文搜索时自动提取关键词。
text_dense:用于存储文本的稠密向量(对应语义文本搜索),用浮点向量类型(FLOAT_VECTOR),维度是 768(这个数字由生成向量的模型决定,比如 BERT 模型生成的向量通常是 768 维)。
text_sparse:用于存储文本的稀疏向量”(对应全文搜索),用稀疏浮点向量类型(SPARSE_FLOAT_VECTOR)。
image_dense:存产品图片的 稠密向量(对应多模态图像搜索),用浮点向量类型(FLOAT_VECTOR),维度是 512(比如 CLIP 模型生成的图像向量常为 512 维)。
另外,因为我们要用系统自带的 BM25 算法来自动生成文本的稀疏向量,所以在定义这个Schema时,还需要加上 Milvus 的内置函数支持。
from pymilvus import (MilvusClient, DataType, Function, FunctionType)client = MilvusClient(uri="http://localhost:19530",token="root:Milvus")# Init schema with auto_id disabledschema = client.create_schema(auto_id=False)# Add fields to schemaschema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, description="product id")schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000, enable_analyzer=True, description="raw text of product description")schema.add_field(field_name="text_dense", datatype=DataType.FLOAT_VECTOR, dim=768, description="text dense embedding")schema.add_field(field_name="text_sparse", datatype=DataType.SPARSE_FLOAT_VECTOR, description="text sparse embedding auto-generated by the built-in BM25 function")schema.add_field(field_name="image_dense", datatype=DataType.FLOAT_VECTOR, dim=512, description="image dense embedding")# Add function to schemabm25_function = Function(name="text_bm25_emb",input_field_names=["text"],output_field_names=["text_sparse"],function_type=FunctionType.BM25,)schema.add_function(bm25_function)定义好 Collection Schema 后,接下来要给各个向量字段建索引。
简单说,索引就像给数据建了个快速查找目录,能大幅提升后续搜索的速度和效率。同时,还得明确用什么方法判断两个向量 “像不像”(也就是相似度类型)。
在这个示例里,三个向量字段的索引配置是这样的:
text_dense_index(文本稠密向量的索引):zilliz cloud中可以用 AUTOINDEX。这种索引不用手动调参数,系统会根据数据特点自动优化,适合处理文本的语义向量。判断相似度用 内积(IP)—— 两个向量的内积值越大,说明它们代表的文本语义越接近。开源milvus里则内置了默认参数。
text_sparse_index(文本稀疏向量的索引):用 稀疏倒排索引(SPARSE_INVERTED_INDEX)。这种索引专门为关键词类的稀疏向量设计,能快速定位包含特定关键词的文本。判断相似度用 BM25 算法 —— 这是全文搜索里常用的方法,能根据关键词出现的频率、位置等,算出文本和查询的匹配度。
image_dense_index(图像稠密向量的索引):和文本稠密向量一样,用AUTOINDEX,搭配内积(IP) 判断相似度。因为图像的视觉特征向量和文本的语义向量类似,都是通过稠密向量表达整体特征,所以用相同的索引和相似度计算方式更合适。
# Prepare index parametersindex_params = client.prepare_index_params()# Add indexesindex_params.add_index(field_name="text_dense",index_name="text_dense_index",index_type="AUTOINDEX",metric_type="IP")index_params.add_index(field_name="text_sparse",index_name="text_sparse_index",index_type="SPARSE_INVERTED_INDEX",metric_type="BM25",params={"inverted_index_algo": "DAAT_MAXSCORE"}, # or "DAAT_WAND" or "TAAT_NAIVE")</include><include target="zilliz">index_params.add_index(field_name="text_sparse",index_name="text_sparse_index",index_type="AUTOINDEX",metric_type="BM25")</include>index_params.add_index(field_name="image_dense",index_name="image_dense_index",index_type="AUTOINDEX",metric_type="IP")定义好 Collection Schema 、创建好向量字段索引之后,第三步就是创建 Collection
此处,我们创建一个名为demo的 Collection。
client.create_collection(collection_name="my_collection",schema=schema,index_params=index_params)
(2)插入数据
接下来,我们要把产品的各种信息按照前面定义的结构,存到名为 my_collection的集合里。这里有个关键点:除了系统能自动生成的内容,其他字段都得按规定格式填,不能出错。
具体来说,每个产品要填这些信息:id、text、text_dense、image_dense。
另外有两个需要注意的点:
生成这些稠密向量时,文本和图像可以用同一个模型,也可以用不同的模型。比如这个例子里,text_dense 是 768 维,image_dense 是 512 维,明显是用了不同的模型(比如文本用 BERT,图像用 CLIP)。这就意味着,后面做搜索时,查文本就得用生成 text_dense 的模型来生成查询向量,查图像就得用生成 image_dense 的模型,不然格式对不上,搜不准。
至于 text_sparse(文本的稀疏向量,用来做关键词匹配的),因为我们用了系统自带的 BM25 算法,它会自动从 text 字段里提取关键词生成稀疏向量,所以不用我们手动填。但如果不想用 BM25,就得自己提前算好稀疏向量。
import random# Generate example vectorsdef generate_dense_vector(dim):return [random.random() for _ in range(dim)]data=[{"id": 0,"text": "Red cotton t-shirt with round neck","text_dense": generate_dense_vector(768),"image_dense": generate_dense_vector(512)},{"id": 1,"text": "Wireless noise-cancelling over-ear headphones","text_dense": generate_dense_vector(768),"image_dense": generate_dense_vector(512)},{"id": 2,"text": "Stainless steel water bottle, 500ml","text_dense": generate_dense_vector(768),"image_dense": generate_dense_vector(512)}]res = client.insert(collection_name="my_collection",data=data)
(3)执行混合搜索
步骤 1:创建多个 AnnSearchRequest 实例
混合搜索的实现,需要在 hybrid_search () 函数中创建多个 AnnSearchRequest 实例 来实现—— 每个实例对应一个向量字段的基础 ANN 搜索请求。因此,执行混合搜索前,必须为每个要用到的向量字段单独创建 AnnSearchRequest。
此外,还可通过在 AnnSearchRequest 中配置 expr 参数,为混合搜索设置过滤条件(例如限定价格范围、地区等)。需要注意的是:混合搜索中,每个 AnnSearchRequest 仅支持传入一条查询数据。
为了演示不同向量字段的搜索功能,我们将基于一个示例查询构建三个 AnnSearchRequest,过程中会使用预先计算好的稠密向量。这三个请求分别对应以下向量字段:text_dense用于语义文本搜索、text_sparse用于全文搜索或关键词匹配、image_dense用于多模态文本到图像的搜索。
from pymilvus import AnnSearchRequestquery_text = "white headphones, quiet and comfortable"query_dense_vector = generate_dense_vector(768)query_multimodal_vector = generate_dense_vector(512)# text semantic search (dense)search_param_1 = {"data": [query_dense_vector],"anns_field": "text_dense","param": {"nprobe": 10},"limit": 2}request_1 = AnnSearchRequest(**search_param_1)# full-text search (sparse)search_param_2 = {"data": [query_text],"anns_field": "text_sparse","param": {"drop_ratio_search": 0.2},"limit": 2}request_2 = AnnSearchRequest(**search_param_2)# text-to-image search (multimodal)search_param_3 = {"data": [query_multimodal_vector],"anns_field": "image_dense","param": {"nprobe": 10},"limit": 2}request_3 = AnnSearchRequest(**search_param_3)reqs = [request_1, request_2, request_3]
由于参数 limit 设置为 2,每个 AnnSearchRequest 返回 2 个搜索结果。在这个例子中,创建了 3 个 AnnSearchRequest 实例,总共产生 6 个搜索结果。
步骤 2:配置重排序策略
为了合并和重新排序 ANN 搜索结果集,选择合适的重排序策略至关重要。提供多种重排序策略。
在这个例子中,由于没有特别强调特定的搜索查询,我们将采用 RRFRanker 策略。
ranker = Function(name="rrf",input_field_names=[],function_type=FunctionType.RERANK,params={"reranker": "rrf","k": 100})步骤 3:执行混合搜索
在启动混合搜索之前,请确保已加载 Collection。如果 Collection 中的任何向量字段缺少索引或未加载到内存中,则在执行混合搜索方法时将发生错误。
res = client.hybrid_search(collection_name="my_collection",reqs=reqs,ranker=ranker,limit=2)for hits in res:print("TopK results:")for hit in hits:print(hit)
(4)以下是输出内容:
["['id: 1, distance: 0.006047376897186041, entity: {}', 'id: 2, distance: 0.006422005593776703, entity: {}']"]
在为混合搜索指定了 limit=2 参数的情况下 将对从三次搜索中获得的六个结果进行重新排序。最终,它们将只返回最相似的前两个结果。
阅读推荐
静态知识库过时了!Agent+Milvus:动态记忆与按需检索实战指南
1bit压缩+高召回,RaBitQ如何成为AI infra的embedding 量化最优解
多智能体系统中,如何用向量数据库共享上下文?OpenAgents x Milvus
Embedding相似度不是万金油,电商、 新闻场景如何按时效性做rerank
不再搞Chain 设计的LangChain 1.0,与LangGraph有哪些区别?
放弃ES+Mongo,如何用Milvus一套系统搞定千万用户视频检索*关键词
