想象你住在一座小镇上,镇里的每栋房子都代表一份文档。过去,镇上只有一本花名册,每栋房只能写一行简介,比如“甜品店、攀岩馆、小花园”全挤在一起。你要找“会做甜品又懂攀岩的老板”,只能挨个把整行简介读一遍,既费劲又容易漏。后来,镇政府推出了新办法:给每栋房子的外墙上挂好几块告示牌,分别写上“甜点工作坊”“攀岩教练”“园艺小铺”。你只需要沿着写着“甜点”的牌子找过去,就能精准敲开相应的门。这个“一房多牌”的思路,就是多向量索引(Multi-Vector Index)的核心:一份文档不再只有一个笼统的向量,而是携带多个子向量,分别对应文档里不同的语义片段。检索时,系统拿着查询“探照灯”去照每一块告示牌,得到多个相似度分数,再综合判断整栋房子值不值得进去。这就是向量数据库里提升 RAG(检索增强生成)召回能力的一大利器。
对 AI 从业者来说,多向量索引是 RAG 系统中提升召回精度与长文档检索效果的关键技能。读懂它,你就知道为什么“一个文档只存一个向量”在复杂语义场景下会失效,以及如何用一整套存储、检索、融合方案让大模型真正回答到点子上。
可视化演示
展示单文档多向量存储结构、并行多维度检索与结果融合策略、以及PQ压缩优化。切换模式探索多向量索引核心技术。
本节配有交互动效(进入视口后自动加载,约 3KB)。
单文档多向量存储结构 每篇文档生成标题/摘要/段落/全文四类向量,分别存储独立索引 doc_id关联多向量记录,支持按维度独立检索后融合 doc → {v_title, v_summary, v_para[], v_fulltext} 多向量索引是RAG召回提升的关键
多向量原理
那年夏天,你到一座 “文档小镇” 找人。镇上的花名册非常简陋,每栋房子只允许写一行简介:“兼卖甜品、教攀岩、附带小花园”。你用放大镜很难从一个词里看清全部信息。后来镇政府规定:每栋房子的外墙可以挂多块告示牌,每块牌只写一个主题。这一改,原本“又厨又户”的模糊标签,变成了清晰的多视角标签。多向量索引的出发点正是如此:一个文档并不只有一个全局语义,它有段落、句子、主题等多个“面孔”。
从告示牌回到技术:传统向量检索把整篇文档编码成一个 embedding 向量,等于强迫所有信息挤进一个坐标点。文档越长、主题越杂,这个坐标点就越“四不像”。多向量索引通常会先对文档做语义切分,让每一句或每一段分别经过嵌入模型,得到多个低维向量。这样,文档 D 就被表示为一个向量集合,每个向量对应文档里的某个具体语义片段。
所以,RAG 开发者在设计索引时,不再问“这个文档的向量是什么”,而是问“这个文档有哪些子语义,分别对应什么向量”。正是这多块告示牌,让检索员不必隔着门缝猜整栋楼。
- D 表示文档的向量集合文档在索引中不是一个单独的点,而是一系列向量的“合集”,所以用大括号
{ }包起来。 - v_i 表示第 i 个子向量每个子向量由文档中的某个句子、段落或语义块嵌入得到,相当于挂在墙上的第 i 块告示牌。
- R^d 表示 d 维实数空间子向量有 d 个浮点数,例如 768 维;v_i 就是这个高维空间里的一个坐标点。
存储结构
告示牌不能悬在半空,需要有地方钉、有分类。镇办事处的档案柜里,每一块告示牌都占用一行:房号、第几面墙、告示牌内容、安装日期。放到向量数据库里,这个档案柜就是一张“子向量表”,每一行记录代表一个子向量,同时用 doc_id 注明它属于哪一栋文档之楼。
多向量存储的本质是维护“文档—子向量”的一对多关系。一个文档对应多条子向量记录,这些记录共享同一个 doc_id,但用 seg_id 区分先后顺序。向量列保存嵌入模型输出的浮点数组;元数据列保存文档来源、页码、标签等过滤条件。检索时,如果一条子向量被命中,数据库必须能通过 doc_id 快速把该文档的所有相关子向量找回来,再做后续聚合。
好的存储设计,就像一本编好索引的告示牌档案。查询命中某块牌子时,检索员能立刻翻到整面墙,而不是只带一块孤单的木头回来。
- doc_id 是文档身份所有属于同一份文档的子向量,都带着同一个 doc_id,就像告示牌上都写着同一栋房的门牌号。
- seg_id 是子向量在文档内的序号它区分“第 1 块牌”与“第 2 块牌”,允许同一文档出现多个不同的语义向量。
- v 是子向量本体即文档某个语义片段的嵌入表示,也是检索时真正参与相似度计算的坐标点。
- metadata 是辅助标签保存时间、类型、权限等,用于过滤或提升后续融合的精准度。
多路召回
你举着“会做甜品”的查询卡来到检索中心。系统并不直接判断整栋楼是否匹配,而是走到每栋楼前,把楼上的每块告示牌都取下来,与查询卡对光目测一番,记下多个相似度分数。这个“挨个看告示牌”的过程,就是多路召回:先看所有子向量,再决定哪些房子有资格进入下一轮。
技术实现上,查询向量 q 与文档 D 中第 i 个子向量 v_i 的相似度,通常使用余弦相似度。这个分数越接近 1,说明这块告示牌越贴合查询意图。如果直接把所有子向量分数都堆到文档头上,会让某些“特别能蹭”的子向量制造假阳性;因此,实践中会先对整张子向量表做近似最近邻搜索,取回 N 个高分候选子向量,再按 doc_id 分组,得到每个文档的候选子向量列表。
多路召回的价值在于“广撒网,再聚合”:查询可以同时命中同一栋楼的多个房间,为后续融合提供丰富素材。
- q 是查询向量用户提问经过嵌入模型编码后的坐标点,相当于你手里的查询卡。
- v_i 是第 i 个子向量文档中某块告示牌的坐标点,等待与查询卡比对。
- 点积与模长分子计算两个向量方向上的对齐程度,分母把向量长度归一化,只保留“夹角”信息。
分数融合
告示牌各有各的亮度,但检索员最终还是要回答:这栋楼到底值不值得进去?假设某栋楼挂着三块告示牌,相似度分别是 0.9、0.4、0.1。有人建议只看最亮的那块,取 0.9;有人认为三块牌都该有发言权,平均成 0.47;还有人觉得“甜点”这块牌代表核心卖点,应该给它更高权重。三种思路正好对应三种常见的融合策略。
最大融合(Max)、平均融合(Avg)和加权融合(Weighted)是向量数据库中最常用的三种聚合方式。Max 能捕捉文档中最强相关的局部语义,适合“只要一个方面命中就很相关”的场景;Avg 更稳妥,避免单一噪声子向量主导结果;加权融合则让系统利用元数据或重排模型,对不同子向量表现出的重要性做二次分配。
融合之后,系统通常会做一次重排序,就像你最终走到门口,先听老板讲两句话,再决定要不要进去。融合分数是“初筛的门铃”,重排才是“正式见面”。
- S_max 取所有子向量分数的最大值只要有一块告示牌强烈命中查询,就给整栋楼高分。
- S_avg 取所有子向量分数的平均值把所有牌子的亮度加起来除以数量,防止某块异常亮或异常暗带偏整体。
- S_w 是加权融合α_i 表示第 i 块告示牌的权重,所有权重之和通常为 1;权重可以由规则设定,也可以由重排模型预测。
索引维护
小镇会不断变化:新楼落成、旧楼翻新、危楼拆除。如果告示牌一直不换,检索员就可能被过时的门牌带偏。多向量索引同样需要一套“贴牌—改牌—摘牌”的维护流程。
离线构建阶段,系统对文档集合 S 批量执行切分与向量化,得到子向量集合 V;当新文档增量到来时,只需要对新增的文档子集 ΔS 做编码,再把新向量追加进现有索引,而不必全量重建。删除操作则往往采用“墓碑”标记:先逻辑删除旧向量,等待后台合并压缩时物理清理。更新一条文档,等价于“新增新版本子向量 + 标记旧版本过期”,并通过版本号保证一致性。
有了增量更新和墓碑机制,文档小镇的地图才能长期保持准确。RAG 系统在持续接入新知识时,也才能做到“旧知识不丢、新知识及时上墙”。
- V_old 是原有索引向量集合就像档案柜里已经挂好的全部旧告示牌。
- E(d) 表示对文档 d 做嵌入编码把新增文档变成若干子向量的过程,等价于制作一批新告示牌。
- ΔS 是新增文档集合只有变化的部分会被处理,避免每次更新都让全镇告示牌重新印刷一次。
- 集合并操作新向量与旧向量合并,形成新索引 V_new。
性能优化
当小镇的楼从一百栋涨到十万栋,每栋楼还挂了 20 块告示牌,检索员就吃不消了:告示牌又大又多,查一次要跑遍全镇。性能优化的思路,就是让告示牌更小、挂法更合理、热门牌子随身带。
让告示牌变小的方法叫向量量化。原始浮点向量每个数占 4 字节,量化成 int8 后每个数只占 1 字节,向量维度不变,但存储与内存开销大幅下降。如果压缩率为 r(量化后字节数 ÷ 原始字节数),那么优化后的存储量 M′ 就是:
除了量化,分段索引把子向量按文档 ID 分成不同段,查询时并行扫描;高频访问的热点文档向量还可以放进缓存,像把常看的告示牌折好放进衣兜。优化后的多向量索引,才能在大规模 RAG 场景中既能“看得准”又“跑得快”。
- M 是优化前的存储量所有子向量占用空间的总和,相当于全镇告示牌的木料总量。
- r 是压缩率量化后单向量字节数与原字节数的比值,int8 相对 float32 约为 1/4。
- M′ 是优化后的存储量木料变少,档案柜变小,检索时内存压力也随之减少。
应用实践
多向量索引最终要落到真实业务里。想象你在读一本技术手册,里面既讲部署、又讲监控、中间还夹着“缓存一致性”的一小节。如果用单向量索引检索,整本书可能被揉成一个模糊的坐标点;用多向量索引后,书里每个章节各有一块告示牌,你问缓存问题时,系统能精准命中那一块,而不会被部署章节稀释。
在实际 RAG 流水线中,多向量索引通常作为第一阶段召回:先得到若干候选文档和融合分数,再交给重排模型精排,最后把最相关文档切片送给大模型生成答案。最终排序可以表达为:
多向量索引非常适合长文档多主题检索、FAQ 中一稿多问、商品多重卖点匹配等场景。它让 RAG 不只是“找到一个相关文档”,而是“找到文档里最相关的那几块门牌”。
- Fusion(Sim(q, V_d)) 是子向量融合分数由前文的最大/平均/加权融合给出,代表“告示牌初筛”的结果。
- Rerank(q, d) 是重排模型得分用更精细的模型重新比较查询与整篇文档,相当于进门面谈后的印象分。
- λ 是重排权重控制“初筛分”与“面谈分”谁更影响最终排名。
AI 应用场景
智能客服知识库召回
阶段:检索召回(Retrieval)
解决的问题:企业知识库里一份产品手册往往包含收益说明、风险提示、办理流程等多个板块。传统单向量索引把整本手册压成一个向量,用户问“哪款产品收益高且风险低”时,很难同时匹配两个条件。多向量索引让每个板块拥有独立子向量,查询可以同时命中“收益高”和“风险低”两块告示牌,再融合得分。
具体案例:某银行 RAG 客服系统接入产品手册后,对每本手册按“产品定义—收益—风险—流程”切分为子向量。用户提问“想找风险低但收益稳健的活期理财”,系统通过多向量索引召回“收益”子向量与“风险”子向量,融合后把最匹配的产品文档送给大模型生成回答,答案命中率显著提升。
长文档学术问答
阶段:索引构建与检索融合
解决的问题:学术论文通常包含摘要、方法、实验、结论等章节,单向量索引会让“实验设置”这类具体问题被其他章节稀释。多向量索引按章节建立子向量,检索“你用了什么 batch size”时,能优先命中实验章节的告示牌。
具体案例:一个论文库问答系统将每篇论文的 abstract、method、experiment 分别编码为多个子向量,同一 paper_id 聚合。用户提问关于模型训练细节时,系统融合实验章节的高分,并跳过背景介绍,最终回答所需上下文更精准。
多维商品检索
阶段:查询编码与多路召回
解决的问题:商品详情页包含标题、卖点、参数表、评价摘要,不同用户关注点完全不同。单向量索引难以同时满足“气垫鞋”与“防滑”两个维度。多向量索引把商品的多条子向量独立入库,让复合条件查询可以分别命中不同告示牌。
具体案例:某电商搜索团队对每个商品按“核心卖点—规格参数—用户评价”生成三组向量。用户搜索“耐磨防滑的运动鞋”,系统分别计算查询与三组向量的相似度,用加权融合把三个维度综合起来,召回率与相关性双提升。
Python 演示
import numpy as np class MultiVectorIndex: def __init__(self, dim=4): self.dim = dim self.doc_count = 0 self.doc_vectors = {} self.doc_meta = {} def add_document(self, doc_id, sub_vectors, meta=""): self.doc_vectors[doc_id] = [np.array(v, dtype=float) for v in sub_vectors] self.doc_meta[doc_id] = meta self.doc_count += 1 def _normalize(self, v): norm = np.linalg.norm(v) if norm == 0: return v return v / norm def _similarity(self, q, v): return float(np.dot(self._normalize(q), self._normalize(v))) def search(self, query_vector, mode="max", top_k=2): scores = {} for doc_id, vectors in self.doc_vectors.items(): sims = [self._similarity(query_vector, v) for v in vectors] if mode == "max": scores[doc_id] = max(sims) elif mode == "avg": scores[doc_id] = sum(sims) / len(sims) else: raise ValueError("unsupported mode: use max or avg") ranked = sorted(scores.items(), key=lambda item: item[1], reverse=True) return ranked[:top_k] if __name__ == "__main__": idx = MultiVectorIndex(dim=4) idx.add_document("doc-a", [[0.9, 0.1, 0.1, 0.1], [0.1, 0.8, 0.2, 0.1]], "甜品店") idx.add_document("doc-b", [[0.1, 0.1, 0.9, 0.2], [0.2, 0.2, 0.1, 0.8]], "攀岩馆") query = [0.8, 0.2, 0.1, 0.1] print(idx.search(query, mode="max", top_k=2)) print(idx.search(query, mode="avg", top_k=2))
self.doc_vectors[doc_id] = [np.array(v, dtype=float) for v in sub_vectors]把每个文档的多块“告示牌”转成浮点向量并保存在一起。self._normalize(v)对向量做 L2 归一化,保证余弦相似度只取决于方向。sims = [self._similarity(query_vector, v) for v in vectors]让查询依次与文档内的每个子向量打分。scores[doc_id] = max(sims)用最大融合策略,把最亮告示牌的分数作为文档代表分。scores[doc_id] = sum(sims) / len(sims)用平均融合策略,让所有告示牌都参与意见。sorted(scores.items(), key=lambda item: item[1], reverse=True)按融合分数从高到低排序,得到最终 Top-K 召回结果。
常见误区
误区我理解了,就是把长文档切成好多块,每一块都单独进索引,这样查询就能命中其中一块了。这和多向量索引没什么区别吧?
正解区别很大。切碎后每块是“独立文档”,没有父子关系,检索结果会返回很多碎片,却不知道它们来自同一篇文章。多向量索引让多个子向量共享同一个 doc_id,查询命中任何一块都能聚合回整栋“文档之楼”。这就像告示牌再多,也必须都挂在同一栋房子的墙上,而不是散落在小镇各处。
为何常见:因为很多初学 RAG 的人确实先接触了“文档切 chunk + 向量化”的做法,于是把多向量单纯理解为“切得更碎”,忽略了 doc_id 分组与融合的关键设计。
误区既然多向量索引是要找最相关的那块告示牌,那我直接把所有子向量分数取最大值排序,不就能保证精准了吗?
正解最大融合容易被“偶然命中的噪声子向量”误导。比如一篇文档里有句话碰巧和查询很像,但整篇内容并不相关,max 会把分数拉得很高。平均融合更稳健,但可能被大量低分稀释;加权融合则能结合业务规则或重排模型调整信任度。实际系统通常需要实验对比,而不是迷信某一种策略。
为何常见:max 实现最简单,名字也最好记,很多人没意识到它牺牲了整体语义的“民意”。
误区那我让每个词都生成一个向量,子向量不就越细越多,信息就越全吗?这样提升效果一定最好。
正解子向量过多会带来三方面问题:存储成本上升、检索噪声变大、融合时低分子向量稀释高分。多向量索引的价值在于“语义粒度合适”,比如按句子或段落切分,而不是无限细分。真正的效果提升来自合理的语义边界和融合策略,而不是简单堆数量。
为何常见:受“更多信息 = 更好结果”的心理影响,新手容易忽略高维向量索引的成本与信噪比。
误区多向量索引已经对多个子向量打分了,重排序模型是不是就没用了?直接按融合分数排序输出就好。
正解多向量索引的融合分数属于“双塔式粗排”,它用向量内积/余弦快速圈定候选,但无法精细建模查询与文档深层的交叉特征。重排序模型,如 cross-encoder,可以把查询和文档拼接后深层交互,效果更准。最佳实践是先用多向量召回,再用重排精修,两者互补。
为何常见:因为多向量已经比单向量“聪明”不少,容易让人误以为它可以一步到位。
面试题
Q1. 在多向量索引中,一个文档通常对应几条向量记录?
- A.一条固定向量
- B.一条或多条子向量← 正确答案
- C.N 条独立文档向量
- D.与查询数相同
答案:B。多向量索引的核心就是一份文档可携带多个子向量,且子向量共享同一个 doc_id。A 是单向量模式;C 错在“独立文档”,子向量并不独立;D 与查询数无关。就像一栋房可以挂多块告示牌,但牌子仍然属于这栋房。
Q2. 下面哪项不是多向量索引的直接优势?
- A.支持文档多粒度语义
- B.提升长文档召回效果
- C.完全替代重排序模型← 正确答案
- D.支持多种分数融合
答案:C。多向量能提升多粒度表达与召回效果,也能配合不同融合策略,但无法替代重排序模型。重排负责查询与文档的深层交互,多向量融合只是粗排环节。C 说法过于绝对,因此不是直接优势。
Q3. 多向量存储记录中,用于把同一文档的多条子向量重新聚合的字段是?
- A.seg_id
- B.doc_id← 正确答案
- C.vector
- D.metadata
答案:B。doc_id 是文档身份标识,所有子向量都通过它找回“同一栋楼”。seg_id 只表示子向量顺序;vector 是向量本体;metadata 用于过滤和附加信息。就像告示牌靠房号回到同一栋房子。
Q4. 某文档三个子向量与查询的相似度分别为 0.9、0.4、0.1,最大融合分数是?
- A.0.47
- B.0.90← 正确答案
- C.0.40
- D.1.00
答案:B。最大融合就是取所有子向量相似度中的最大值,因此是 0.90。0.47 是平均值;0.40 是第二大的分数;1.00 不在已知分数中。这与“只看最亮告示牌”的策略一致。
Q5. 哪种融合策略最容易受单个最高异常分影响?
- A.平均融合
- B.最大融合← 正确答案
- C.加权融合
- D.中位融合
答案:B。最大融合只看分数最高的那个子向量,一旦某块告示牌因噪声异常偏亮,整栋楼都会被带高。平均和中位会把异常摊薄,加权则可在一定程度下控制异常影响。
Q6. 增量更新多向量索引时,下列做法最合理的是?
- A.重新嵌入全部历史文档
- B.只编码新增文档并追加← 正确答案
- C.删除旧索引全部重建
- D.同步更新所有旧向量
答案:B。增量更新的核心是只对新增文档集合 ΔS 调用嵌入模型,再把新向量追加到旧索引中。全量重建浪费算力,删除重建同理,更新旧向量没有必要,除非模型版本升级。
Q7. 将 float32 向量量化为 int8 后,单向量存储空间约为原来的?
- A.二分之一
- B.四分之一← 正确答案
- C.八分之一
- D.十六分之一
答案:B。float32 占 4 字节,int8 占 1 字节,因此存储空间约为原来的四分之一。这个比例对应压缩率 r=0.25。量化让告示牌变小,是常见的性能优化手段。
Q8. 多向量索引的性能优化手段包括?
- A.向量量化压缩← 正确答案
- B.分段存储并行← 正确答案
- C.忽略所有过滤条件
- D.高频结果缓存← 正确答案
答案:ABD。量化压缩减少单向量字节数,分段存储支持并行扫描,高频缓存加速热点文档召回。忽略过滤条件反而会降低精度与效率,不是优化手段。
Q9. 索引维护阶段需要处理哪些任务?
- A.新增文档在线编码← 正确答案
- B.删除向量打墓碑标记← 正确答案
- C.丢弃全部旧向量
- D.压缩合并旧段← 正确答案
答案:ABD。新增文档需要编码并追加;删除向量通常先打墓碑标记,避免高成本物理删除;旧段在后台压缩合并,减少碎片。丢弃全部旧向量会把有效历史内容也清空,不合理。
Q10. 关于多向量索引在 RAG 中的应用,正确的说法有?
- A.可提升检索召回精度← 正确答案
- B.检索后仍需精排重排← 正确答案
- C.多向量由 LLM 生成即可
- D.融合分数可作排序依据← 正确答案
答案:ABD。多向量索引通过多路召回与融合提升召回精度,也能为最终排序提供粗排分数;但检索后通常仍需重排。多向量的子向量来自嵌入模型,而不是直接由 LLM 生成文本得到,因此 C 错误。