多模态检索不是一个问题

“多模态 RAG”通常把多个任务混在一起:

  1. 文本查图:“找到带红色急停按钮的产品照片。”
  2. 图片查文:“找到解释这张警告标志照片的手册章节。”
  3. 视觉文档检索:“找到包含季度利润率图表及脚注的页面。”
  4. 精确文本检索:“找到发票 ACME-2026-00417。”
  5. 混合证据检索:“结合接线图、参数表和修订说明解释操作步骤。”

单个索引无法天然解决全部问题。全局图文向量擅长识别大范围视觉语义,却可能漏掉很小的编号;OCR 加 BM25 能找到编号,却可能丢失版面关系;页面图像延迟交互保留更多视觉结构,但索引内存更高,后续仍要设计证据提取。

生产目标不是“把图片放进向量数据库”,而是:

检索有权限、带版本、包含足够图文上下文的证据,并让最终答案可核验。

需要先了解整体流程时,可阅读多模态 RAG 入门与架构指南。本文专注检索设计和评测。

三条互补检索路径

路径 A:图文共享向量空间

CLIP通过对比学习把图片和自然语言描述映射到共享表示空间。类似双编码器可支持:

  • 文本查询图片;
  • 图片查询文字;
  • 通过对齐空间执行以图搜图或 Caption 相似搜索;
  • 每个资产只保存一个全局向量,快速执行 ANN 召回。

优势:

  • 索引和候选生成效率较高;
  • 擅长宽泛语义匹配;
  • 适合照片、商品、场景和插图;
  • 文本查图与图片查文路径对称。

局限:

  • 小字、精确编号、表格和细粒度空间关系可能被压缩进一个向量;
  • 分数会随模型、预处理、语言和语料变化;
  • 视觉相似不等于能够支持用户所问事实。

CLIP 是模型家族与训练方法,并不保证所有衍生 Checkpoint 的许可证、语言支持、输入尺寸和分数校准完全相同。

路径 B:视觉文档延迟交互

ColPali发表于 ICLR 2025。它将文档页面图像编码成多个向量,再让 Query Token Vector 与 Page Vector 做延迟交互。简化相关性函数为:

text
score(query, page) =
  对每个 query vector i 求和 (
    在所有 page vector j 上取 max similarity(query_i, page_j)
  )

与每页单向量检索不同,不同 Query Token 可以匹配页面的不同视觉区域,因此能够保留表格、图形、字体和版式信号。

ColPali 论文还引入 ViDoRe 视觉文档页面检索基准,并在论文指定数据集和协议下报告了很强的结果。但论文不能证明任意 ColPali Checkpoint 都优于所有 OCR 流水线,更不能推出“任何环境索引快 3 倍”或“2026 年最佳模型”。

实际代价:

  • 每页多向量会提高索引内存;
  • 候选评分比单向量余弦检索更昂贵;
  • 页面级召回后仍需区域定位或证据提取;
  • 精确文本与策略过滤仍可能依赖 OCR 或元数据;
  • 索引端与查询端的模型、预处理版本必须一致。

路径 C:文字、版面与元数据检索

OCR 并未过时,它提供视觉向量无法保证的能力:

  • 精确 ID、金额、日期、条款和错误码;
  • Phrase 与 Boolean Search;
  • 确定性语言或文档类型过滤;
  • 文本高亮和无障碍输出;
  • 脱敏与合规复核;
  • 可引用的词法证据。

一条实用文字路径可以包含:

  • 带 Bounding Box 和 Confidence 的 OCR Token;
  • PDF 等文档原生文字;
  • BM25 或其他词法检索;
  • Text Embedding;
  • Layout-aware Chunk;
  • 文档元数据与 ACL 字段。

不要在无记录的情况下静默偏好 OCR 或原生 PDF Text。应保存来源类型与抽取版本,评测才能定位具体失败路径。

生产检索架构

text
摄取
  -> 不可变资产 ID 与版本 ID
  -> 页面渲染与图片 Hash
  -> 原生文本 / OCR 与 Region
  -> 元数据与 ACL 规范化
  -> 图文全局向量
  -> 页面多向量
  -> 词法 / 文本向量

查询
  -> 身份与权限过滤
  -> 查询分类
  -> 多路径并行召回
  -> 各路径 Top-k 与 Provenance
  -> 排名融合或校准融合
  -> 可选的证据感知重排
  -> 页面 / Region 证据包
  -> 生成答案
  -> 引用与支持度验证

权限必须在召回前生效

Tenant、用户、Legal Hold、地区与文档状态过滤应进入 Candidate Generation,而不是在排序后处理。先召回再过滤会:

  • 通过延迟或结果数量泄露对象存在;
  • 导致返回条数不足;
  • 让无权候选影响 Reranker 或生成模型;
  • 污染缓存和 Trace。

每条结果应携带授权决策或 Query Scope ID,做到可审计但不在日志中泄露正文。

所有表示共享同一证据身份

同一页的不同表示必须关联同一个 ID:

json
{
  "asset_id": "manual-42",
  "version_id": "sha256:...",
  "page": 17,
  "region_id": "p17-r08",
  "source_uri": "s3://approved/manual-42.pdf",
  "render_hash": "sha256:...",
  "native_text_version": "pdf-parser-4.2",
  "ocr_version": "ocr-7.1",
  "image_encoder": "clip-checkpoint@digest",
  "page_encoder": "colpali-checkpoint@digest",
  "acl_scope": "tenant-7:engineering",
  "indexed_at": "2026-10-03T08:00:00Z"
}

如果没有共享身份,同一页面会作为三个无关候选进入上下文。应在分配最终 Context Budget 前,按证据身份去重。

不要直接平均原始分数

BM25、Cosine Similarity 与 Late Interaction MaxSim 的量纲和分布不同。即使都是余弦分数,不同模型也可能使用不同归一化、维度和训练目标。下面公式没有校准证据时是无效的:

text
final = 0.4 * bm25 + 0.3 * clip_cosine + 0.3 * colpali_maxsim

这些 Weight 看起来精确,却没有稳定含义。

Reciprocal Rank Fusion

Reciprocal Rank Fusion使用候选位置而不是原始分数:

text
RRF(document) = 对所有 ranker r 求和 (weight_r / (k + rank_r(document)))

当多个 Retriever 分数不可比时,RRF 是可靠基线。它不要求 Score Calibration,也不会让某个异常大的原始分数独占结果。但常量 k、路径权重、Candidate Depth 与去重规则仍需评测。

Go 实现

go
package main

import (
	"fmt"
	"sort"
)

type RankedItem struct {
	ID string
}

type FusedItem struct {
	ID    string
	Score float64
}

func reciprocalRankFusion(
	lists [][]RankedItem,
	weights []float64,
	k float64,
) []FusedItem {
	scores := make(map[string]float64)
	for listIndex, items := range lists {
		weight := 1.0
		if listIndex < len(weights) {
			weight = weights[listIndex]
		}
		seen := make(map[string]bool)
		for itemIndex, item := range items {
			if item.ID == "" || seen[item.ID] {
				continue
			}
			seen[item.ID] = true
			rank := float64(itemIndex + 1)
			scores[item.ID] += weight / (k + rank)
		}
	}

	fused := make([]FusedItem, 0, len(scores))
	for id, score := range scores {
		fused = append(fused, FusedItem{ID: id, Score: score})
	}
	sort.Slice(fused, func(i, j int) bool {
		if fused[i].Score == fused[j].Score {
			return fused[i].ID < fused[j].ID
		}
		return fused[i].Score > fused[j].Score
	})
	return fused
}

func main() {
	lists := [][]RankedItem{
		{{ID: "page-17"}, {ID: "page-8"}, {ID: "page-31"}},
		{{ID: "page-8"}, {ID: "page-17"}, {ID: "page-22"}},
	}
	for _, item := range reciprocalRankFusion(lists, []float64{1, 1}, 60) {
		fmt.Printf("%s %.6f\n", item.ID, item.Score)
	}
}

代码中的 Duplicate Guard 防止单个 Retriever 重复返回同一文档来提高分数。生产结构还应携带 Path、Rank、Raw Score、Evidence Version 和 ACL Scope。

何时使用校准或学习式融合

只有具备代表性标签时,才使用校准分数或 Learning-to-Rank。特征可以包括:

  • 各路径 Rank 与 Score;
  • OCR Confidence;
  • Query Type;
  • Language 与 Document Class;
  • 图片质量;
  • 多路径一致性;
  • 时效与版本匹配。

训练和测试应按文档或时间分割,避免近重复泄漏。如果同一模板同时进入 Train 和 Test,Fuser 可能看似准确,却在新供应商、新语言或新扫描类型上失败。

避免脆弱的查询路由

Router 可以节省费用,但也创造新的召回失败。先使用可解释策略:

Query 信号 候选路径
精确编号、引号短语、金额 词法 + Text Embedding
视觉属性或场景 图文共享向量
表格、图表、表单、版面关系 页面延迟交互 + OCR
图片查询 Image-to-image / 共享空间
含糊混合请求 所有有权限路径,再融合

记录实际选择路径,并评估 Router Recall:路由是否排除了唯一包含相关证据的路径?如果低成本 Router 系统性漏掉少数语言或扫描文档,它没有节省真实成本。

为生成阶段打包证据

检索结果不能只是一组 Vector,应生成证据包:

json
{
  "evidence_id": "manual-42@sha256:...#page=17&region=p17-r08",
  "retrieval_paths": [
    {"name": "bm25", "rank": 4},
    {"name": "colpali", "rank": 1}
  ],
  "page_image": "signed://...",
  "region": {"x": 0.14, "y": 0.28, "width": 0.62, "height": 0.31},
  "text": "经过审批的 OCR 或原生文本片段",
  "source_title": "Pump Service Manual",
  "revision": "2026-08-14",
  "acl_scope": "tenant-7:engineering"
}

生成模型只能接收有权限证据,并获得足够邻近上下文来理解 Region。最终引用应指向 Evidence ID,不能使用模型临时生成的页码标签。

图表和表格应同时保留 Page Crop 与文字。OCR 可能展平行列关系,只有图片又难以精确引用;两者组合通常更有价值。

检索评测

标签单位必须匹配产品

  • 媒体搜索使用 Asset Level;
  • 视觉文档检索使用 Page Level;
  • UI 需要高亮时使用 Region Level;
  • 必须联合多页回答时使用 Evidence Set Level。

语料存在多个合理结果时,应标注多个 Relevant Item。Hard Negative 应来自同一模板、产品家族、月份或相似版面,而不是只用明显不相关文档。

必须覆盖的 Query 切片

  • 模态:Text-to-image、Image-to-text、Text-to-page、Mixed;
  • 内容:照片、示意图、图表、表格、表单、密集正文;
  • 文字质量:原生文字、清晰 OCR、劣化扫描、手写;
  • 语言与文字系统;
  • 精确编号与语义描述;
  • 单跳与多证据;
  • 现行文档与废弃版本;
  • 高频与稀有模板;
  • 有权与无权证据;
  • 正常内容与 Prompt Injection。

一个全局平均数可能掩盖高风险切片的零召回。

检索指标

指标 回答的问题
Recall@k 候选集合中是否包含相关证据?
MRR 第一个相关结果出现得多早?
nDCG@k 分级相关结果是否排序合理?
Precision@k 上下文预算中有多少内容真正有用?
Duplicate Rate 多种表示是否浪费 Slot?
Unauthorized Candidate Rate 无权证据是否进入排序?
Stale-version Rate 是否选择了已废弃版本?

样本量允许时应报告置信区间或 Bootstrap Range。每份报告都要固定 Corpus Snapshot 以及准确 Encoder/Index 配置。

端到端 RAG 指标

检索质量和答案质量必须分开:

  • Evidence Sufficiency;
  • Citation Precision 与 Recall;
  • Claim-to-evidence Entailment;
  • Unsupported Claim Rate;
  • 证据不足时拒答;
  • 精确字段准确率;
  • 视觉 Region 归因;
  • 每个有证据答案的延迟与成本。

即使 Recall@k 很高,流畅回答也可能没有依据;反过来,召回缺失也不能靠更强生成模型稳定补救。

更完整的框架参见 RAG 评测指南,文本检索调优可参考混合检索与重排序指南。

运行指标

质量不是唯一门禁:

阶段 需要记录的运行证据
摄取 Pages/Minute、失败、OCR Confidence、渲染确定性
索引 Vectors/Page、Bytes/Page、构建时间、Peak Memory
检索 p50/p95/p99、Candidate Depth、Cache Hit
重排 Batch Size、加速器内存、Timeout Rate
生成 Evidence Token、Image Token、Unsupported Claim
时效 Source-to-searchable Delay、Stale-version Rate
成本 每索引页、每查询、每有证据答案

没有 Checkpoint、精度、Batch Size、页面分辨率、向量数、硬件与软件版本,就不应发布固定 VRAM 或延迟需求。

漂移与变更管理

“对齐漂移”应表示可测变化,而不是所有搜索质量下降的通用解释。需要监控:

  • 各切片 Query 分布;
  • 语料中的语言、模板与图片质量;
  • OCR 与 Renderer 版本;
  • Embedding Checkpoint 与 Preprocessing;
  • Index 参数与 Quantization;
  • 固定 Canary Set 的各路径 Recall;
  • Score 与 Rank 分布;
  • 多路径分歧;
  • Unsupported Answer Rate。

用新 Encoder 重建索引,却继续用旧 Encoder 查询,是明确合约错误。应使用版本化索引、Dual-read 评测和原子流量切换。

Model、OCR、Rendering、Chunking、Fusion 或 ACL 任一变化,都应触发新评测。回滚时恢复完整兼容的 Index 与 Query Stack,不能只回滚一个组件。

安全与隐私

多模态证据可能包含:

  • 可见个人信息或业务秘密;
  • 隐藏 PDF Layer;
  • EXIF 与文档元数据;
  • 二维码和嵌入 URL;
  • 文字或像素中的 Prompt Injection;
  • 针对 Parser 的恶意文件;
  • 跨租户近重复内容。

控制措施:

  1. 在隔离摄取环境扫描和规范化文件;
  2. 删除不需要的元数据;
  3. 严格控制原件访问,只索引经过批准的派生物;
  4. Candidate Generation 前执行 ACL;
  5. 把 OCR 和图片中的指令视为不可信证据;
  6. 绝不根据检索内容授予工具权限;
  7. 对日志和评测导出脱敏;
  8. 删除传播到派生物、索引、缓存与备份;
  9. 验证 Signed Evidence URL 具有短有效期并绑定租户。

选型清单

不要先问“哪个模型最好”,先回答:

  • 检索单位是 Asset、Page、Region 还是 Evidence Set?
  • 需要哪些 Query Direction?
  • 哪些语言、版面和扫描质量重要?
  • 精确标识是否属于业务关键路径?
  • Index Memory 与 p95 Latency 上限是什么?
  • 数据能否离开部署边界?
  • 数据更新后多久必须可搜索?
  • 哪些标注切片和失败成本决定验收?

然后至少比较:

  1. Lexical/Native-text Baseline;
  2. Text Embedding Baseline;
  3. Global Image-text Embedding Baseline;
  4. 场景需要时加入 Page-image Late Interaction;
  5. Rank Fusion Baseline;
  6. 标签充分后再引入 Learned Reranker。

Embedding 向量指南解释向量行为,多模态图文流水线指南覆盖上游图像与文字处理。

一手资料

总结

生产级多模态 RAG 需要多份明确合约:每种表示负责召回什么证据,不可比分数如何融合,每个页面或 Region 如何追溯到带版本来源,哪个权限过滤生效,以及哪套标注评测可以证明变更。CLIP 类共享向量、ColPali 类延迟交互与 OCR/词法检索是互补工具。真正合适的是满足实测业务约束的系统,而不是附带最大未经验证提升数字的模型。