免费POC, 零成本试错
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


我要投稿

Struct Array 如何让多向量检索返回完整实体?知识库、电商、视频通用|Milvus Week

发布日期:2025-12-02 18:35:57 浏览次数: 1521
作者:Zilliz

微信搜一搜,关注“Zilliz”

推荐语

Milvus 2.6.4的Struct Array + MAX_SIM功能,彻底解决多向量检索返回完整实体的行业痛点。

核心内容:
1. 多向量检索场景中的常见问题与业务需求
2. Struct Array技术原理与MAX_SIM算法实现
3. 知识库、电商、视频三大场景的实践案例解析

杨芳贤
53AI创始人/腾讯云(TVP)最具价值专家

图片

图片
图片

本文为Milvus Week系列第二篇,该系列旨在分享Zilliz、Milvus在系统性能、索引算法和云原生架构上的创新与实践,以下是DAY2内容划重点:

Struct Array + MAX_SIM ,能够让数据库看懂 “多向量组成一个实体” 的逻辑,进而原生返回业务要的完整结果

图片

用向量数据库的人大概率都碰过这类问题:数据库里存的是被拆成片段的向量(比如一篇文档的段落向量、商品的单张图片向量),但业务要的是完整的实体(整个文档、整个商品)。

举几个真实场景中的案例:

知识库检索:存的是段落向量,然而用户想搜的是最相关的几篇文档,却因为搜到的多个段落匹配到的同一篇文档,导致去重后文档数量不足;

电商搜索:存的是商品图向量,结果召回的结果是同一商品的不同角度图占满检索结果,无法返回足量商品;

视频平台:存的是片段向量,导致最后搜到的都是同一部视频的不同切片;

这些问题本质上都是一回事:视角错位。数据库认为“一个向量 = 一条数据”,但业务看来 “多个向量 = 一个实体”。结果就是应用层需要额外加去重、分组、rerank,既麻烦又容易出 bug。

好在 Milvus 2.6.4 出了 Struct Array + MAX_SIM 功能,能够让数据库看懂 “多向量组成一个实体” 的逻辑,进而原生返回业务要的完整结果。

下面用Wikipedia 文档检索、ColPali 文档图像检索两个真实案例,做详细解读。(在本文场景中:我们用它来存储一个实体的多个向量,但它的能力远不止于此,你还能用它聚合任何类型的结构化数据。)

01

什么是 Struct Array?原理是什么

Struct Array 的本质,就是允许在一个字段里存储多个结构化对象(可以包含标量、向量、字符串等任意类型),然后把它们组织成一个整体。

Struct Array的核心价值在于打破传统数据结构的限制:允许在单个字段中存储多个结构化对象(可包含标量、向量、字符串等任意数据类型),并将这些对象组织为一个逻辑整体。这种结构特别适用于处理 “多向量组合” 场景(如文本分词后的 embedding list)。

而 MAX_SIM(最大相似度求和)算法则是基于 Struct Array 实现语义级检索的核心实现路径 。 它解决了传统检索依赖词形完美匹配的痛点,通过向量语义相似度实现更灵活的匹配逻辑。

接下来我们通过一个案例,来详细拆解 MAX_SIM 的计算逻辑(所有向量均通过相同的 embedding 模型生成,相似度采用余弦相似度计算,取值范围 [0,1])。

假设用户输入的query是“机器学习入门课程”,由4个向量组成,"机器", "学习", "入门", "课程"。数据库中有两篇doc,[1]新手深度神经网络python实战; [2]理论进阶之大模型paper详解; 也分别tokenization后按向量储存。

我们先来计算query和doc_1的相似度。首先,我们计算query中的每个向量和doc内的每个向量之间的cosine相似度,如下表所示。

对于query中的每个向量,我们都会从doc中找到最为匹配的向量。例如query中的“机器学习”将匹配doc_1中的“深度神经网络”,“入门”将匹配“新手”,“课程”将匹配“实战”,最终query和doc_1的相似度为以上最佳匹配的相似度之和,0.9 + 0.8 + 0.7 = 2.4.

同理,我们计算query和doc_2的相似度,“机器学习”将匹配“大模型”,“入门”和“课程”都会匹配“详解”,但是我们注意到,“入门”和最佳匹配“详解”的相似度只有0.6,所以最终相似度得分只有0.9 + 0.6 + 0.8 = 2.3,低于doc_1,这符合我们的预期。

基于上述案例,可总结 MAX_SIM 的三大关键特性:

  1. 语义优先,不依赖词形匹配:核心依赖向量 embedding 的语义相似度,而非关键词的字面重合(如 “机器学习” 与 “深度神经网络” 无相同字符,但语义匹配度高达 0.9),更适合处理同义词、相关概念的检索场景。

  2. 长度与顺序无关:不限制 query 和文档的向量列表长度(如 doc_1 含 4 个向量、doc_2 含 5 个向量,均能正常计算),且无需考虑向量的顺序(如 query 的 “入门” 在前、文档的 “新手” 在后,不影响匹配结果)。

  3. 平等关注每个 query 向量:对 query 中的每个核心向量,均取其最佳匹配值参与求和,避免因部分向量未匹配导致的检索偏差(如 “入门” 向量的匹配质量直接影响最终得分)。

目前,Milvus 作为开源向量数据库,依托其高效的向量检索引擎,已扩展支持基于 Struct Array 的 MAX_SIM 算法:

  • 可直接存储多向量组合的 Struct Array 数据,无需额外拆分字段;

  • 结合向量索引(如 IVF、HNSW)优化最佳匹配的计算效率,避免全量遍历;

  • 适用于长文本检索、多维度语义匹配等场景(如文档摘要匹配、多关键词语义检索),为 AI 应用提供更灵活的检索方案。

02 

Struct Array适用场景

Struct Array的核心能力概括来说,有三点:

  1. 能把不同类型的数据(标量、向量、字符串)凑成一个结构化对象;

  2. 让数据库里的 “一行” 对应业务里的 “一个东西”(文章 / 商品 / 视频);

  3. 配合 MAX_SIM 这类聚合函数,数据库直接返回实体级结果,不用应用层做额外工作。

因此,如果你的数据存在 “整体 - 部分” 结构(如一篇文章包含多个段落、一个商品对应多张图片),业务需要返回完整实体而非碎片化向量(如用户需获取文章列表而非零散段落),且正面临应用层需手动实现复杂去重、分组与重排逻辑,或是向量检索结果中同一实体反复占据 Top 位导致冗余的问题时,Struct Array 正是适配这类需求的解决方案。

在需要多向量检索的AI应用场景中尤其适合:ColBERT 模型将一个文档拆分为 100-500 个 Token 向量,适用于法律文档、学术论文的细粒度检索;ColPali 模型把一个 PDF 页转化为 256-1024 个 Patch 向量,可满足财报、合同、发票等跨模态检索场景的需求。

拿电商商品举例子就懂了:

  • 以前存商品图:是扁平化存储思路,一张图一行数据,同一个商品的正面、侧面图得拆成 3 行,搜的时候还得自己去重;

  • 用 Struct Array:一个商品占一行,所有图片的角度、是否主图、向量信息,都塞在images这个字段里 —— 数据库可以直接认出这是一个商品的所有图。

再看知识库的场景:

  • 以前存维基百科:一篇文章拆成 N 个段落,每个段落一行,搜出来全是零散段落;

  • 用 Struct Array:一篇文章一行,所有段落的文本和向量都包在 “paragraphs” 字段里,数据库返回的直接是整篇文章。

03 

实战

实战Demo 1:Wikipedia文档检索

目标:将段落数据转换为文档数据,实现文档级检索。

核心流程:数据分组 → 创建 Schema → 插入数据 → 创建索引 → 搜索

Data Model

{    "wiki_id"int,                  # WIKI ID(主键)     "paragraphs": ARRAY<STRUCT<      # paragraph 数组        text:VARCHAR                 # 每个段落的文本        emb: FLOAT_VECTOR(768)       # 每个段落文本的向量    >>}


实现

1. 数据分组转换

数据集来源: https://huggingface.co/datasets/Cohere/wikipedia-22-12-simple-embeddings

import pandas as pdimport pyarrow as pa# 加载数据并按 wiki_id 分组df = pd.read_parquet("train-*.parquet")grouped = df.groupby('wiki_id')# 为每篇文章构建段落数组wiki_data = []for wiki_id, group in grouped:    wiki_data.append({        'wiki_id': wiki_id,        'paragraphs': [{'text': row['text'], 'emb': row['emb']}                       for _, row in group.iterrows()]    })

2. 创建 Milvus Collection

from pymilvus import MilvusClient, DataTypeclient = MilvusClient(uri="http://localhost:19530")schema = client.create_schema()schema.add_field("wiki_id", DataType.INT64, is_primary=True)# 定义 Struct Arraystruct_schema = client.create_struct_field_schema()struct_schema.add_field("text", DataType.VARCHAR, max_length=65535)struct_schema.add_field("emb", DataType.FLOAT_VECTOR, dim=768)schema.add_field("paragraphs", DataType.ARRAY,                 element_type=DataType.STRUCT,                 struct_schema=struct_schema, max_capacity=200)client.create_collection("wiki_docs", schema=schema)

3. 插入数据并创建索引

# 批量插入client.insert("wiki_docs", wiki_data)# 创建 HNSW 索引index_params = client.prepare_index_params()index_params.add_index(    field_name="paragraphs[emb]",    index_type="HNSW",    metric_type="MAX_SIM_COSINE",    params={"M"16"efConstruction"200})client.create_index("wiki_docs", index_params)client.load_collection("wiki_docs")

4. 搜索文档

# 搜索查询import coherefrom pymilvus.client.embedding_list import EmbeddingList# 数据集的向量是通过 cohere的 embedding 模型multilingual-22-12,query文本也需要使用相同的模型生成co = cohere.Client(f"<<COHERE_API_KEY>>")query = 'Who founded Youtube'response = co.embed(texts=[query], model='multilingual-22-12')query_embedding = response.embeddingsquery_emb_list = EmbeddingList()for vec in query_embedding[0]:    query_emb_list.add(vec)results = client.search(    collection_name="wiki_docs",    data=[query_emb_list],    anns_field="paragraphs[emb]",    search_params={        "metric_type""MAX_SIM_COSINE",        "params": {"ef"200"retrieval_ann_ratio"3}    },    limit=10,    output_fields=["wiki_id"])# 结果:直接返回 10 篇不同的文章!for hit in results[0]:    print(f"文章 {hit['entity']['wiki_id']}: 相似度 {hit['distance']:.4f}")

效果对比

当然,以上Wikipedia 案例展示了基础的段落检索场景。Struct Array 的真正威力在于支持各种多向量场景:

传统检索场景

AI模型场景(重点)

实战Demo 2:ColPali文档图像检索

ColPali 是现在做 PDF 跨模态检索的热门模型,它会把一页 PDF 切成 1024 个 Patch,每个 Patch 一个向量。要是用传统方式存,一页 PDF 得拆成 1024 行,搜的时候根本没法聚合 ——Struct Array 刚好能解决这个问题。

此外,传统 PDF 检索靠 OCR 转文本,会丢图表、布局信息;ColPali 直接从图像切 Patch,保留所有视觉和文本信息,但需要数据库能处理 “一页 = 1024 个向量” 的聚合需求。

Struct Array 在ColPali文档图像检索领域的典型场景是Vision RAG。比如:财报检索(在数千份PDF中找到包含特定图表的页面)、合同审查(从扫描的合同中检索特定条款)、发票处理(检索特定供应商或金额的发票)、 演示文稿(找到包含特定图示的幻灯片)。

Data Model

{    "page_id"int,                     # 页面ID(主键)     "page_number"int,                 # 页面在文档中是第几页     "doc_name": VARCHAR,                # 文档名称    "patches": ARRAY<STRUCT<            # Patch数组        patch_embedding: FLOAT_VECTOR(128)  # 每个patch的向量    >>}

实现

1. 数据准备

https://huggingface.co/vidore/colpali-v1.3

可以参考这个文档获取colpali如何将图片/文本转成多向量

import torchfrom PIL import Imagefrom colpali_engine.models import ColPali, ColPaliProcessormodel_name = "vidore/colpali-v1.3"model = ColPali.from_pretrained(    model_name,    torch_dtype=torch.bfloat16,    device_map="cuda:0",  # or "mps" if on Apple Silicon).eval()processor = ColPaliProcessor.from_pretrained(model_name)# 假设有2个文档,每个文档5页,共10张图片images = [    Image.open("path/to/your/image1.png"),     Image.open("path/to/your/image2.png"),     ....    Image.open("path/to/your/image10.png")]# 将图片转换成多向量batch_images = processor.process_images(images).to(model.device)with torch.no_grad():    image_embeddings = model(**batch_images)

2. 创建Collection:

from pymilvus import MilvusClient, DataTypeclient = MilvusClient(uri="http://localhost:19530")schema = client.create_schema()schema.add_field("page_id", DataType.INT64, is_primary=True)schema.add_field("page_number", DataType.INT64)schema.add_field("doc_name", DataType.VARCHAR, max_length=500)# Struct Array for patchesstruct_schema = client.create_struct_field_schema()struct_schema.add_field("patch_embedding", DataType.FLOAT_VECTOR, dim=128)schema.add_field("patches", DataType.ARRAY,                 element_type=DataType.STRUCT,                 struct_schema=struct_schema, max_capacity=2048)client.create_collection("doc_pages", schema=schema)

3. 插入并索引

# 插入数据page_data=[    {        "page_id": 0,        "page_number": 0,        "doc_name""Q1财报.pdf",        "patches": [            {"patch_embedding": emb} for emb in image_embeddings[0]        ],    },    ...,    {        "page_id": 9,        "page_number": 4,        "doc_name""产品手册.pdf",        "patches": [            {"patch_embedding": emb} for emb in image_embeddings[9]        ],    },]client.insert("doc_pages", page_data)# 创建索引index_params = client.prepare_index_params()index_params.add_index(    field_name="patches[patch_embedding]",    index_type="HNSW",    metric_type="MAX_SIM_IP",    params={"M": 32, "efConstruction": 200})client.create_index("doc_pages", index_params)client.load_collection("doc_pages")

4. 跨模态搜索:文本查询→图像结果

# 搜索from pymilvus.client.embedding_list import EmbeddingListqueries = [    "quarterly revenue growth chart"    ]# 将查询文本转换成多向量batch_queries = processor.process_queries(queries).to(model.device)with torch.no_grad():    query_embeddings = model(**batch_queries)query_emb_list = EmbeddingList()for vec in query_embeddings[0]:    query_emb_list.add(vec)results = client.search(    collection_name="doc_pages",    data=[query_emb_list],    anns_field="patches[patch_embedding]",    search_params={        "metric_type""MAX_SIM_IP",        "params": {"ef"100"retrieval_ann_ratio"3}    },    limit=3,    output_fields=["page_id""doc_name""page_number"])print(f"查询: '{queries[0]}'")for i, hit in enumerate(results, 1):    entity = hit['entity']    print(f"{i}{entity['doc_name']} - 第{entity['page_number']}页")    print(f"   相似度: {hit['distance']:.4f}\n")

输出示例

查询: 'quarterly revenue growth chart'1. Q1财报.pdf - 第2页   相似度: 0.91232. Q1财报.pdf - 第1页   相似度: 0.76543. 产品手册.pdf - 第1页   相似度: 0.5231

这里的输出结果直接是 PDF 页面,我们不用管背后 1024 个 Patch 的细节,数据库已经自动搞定了聚合。

最后的话

传统数据库将数据打散成一行行记录,而 Struct Array 让数据库真正支持结构化聚合:通过灵活组合标量、向量、字符串等多种类型,让一行数据真正对应一个业务实体。

这意味着,复杂的数据聚合直接应用层的工程问题变成了数据库的原生能力,而这也是数据库的长期进化方向。

作者介绍

图片

朱文星

Zilliz Senior Software Engineer in Quality Assurance

图片

田敏

Senior Software Engineer at Zilliz 

阅读推荐
88.9 倍性能飙升!JSON Shredding 让 JSON 查询告别全表扫描| Milvus Week
写在 Milvus4 万 Star 之际:Zilliz这七年如何走来,又要去往何处?
Agent框架deepagents测评:长任务友好,高可控" data-itemshowtype="0" linktype="text" data-linktype="2">LangChain 最新agent框架deepagents测评:长任务友好,高可控
RAG不会过时,但你需要这10个上下文处理技巧|Context Engineering系列一
月活11亿的Reddit ,怎么选向量数据库:Pgvector、Redis、Milvus、Qdrant
MCP?是agent落地最大的误解" data-itemshowtype="0" linktype="text" data-linktype="2">Skills 取代MCP?是agent落地最大的误解
图片
图片

53AI,企业落地大模型首选服务商

产品:场景落地咨询+大模型应用平台+行业解决方案

承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

添加专属顾问

回到顶部

加载中...

扫码咨询