1. 引言:传统RAG的“失明”困境与多模态破局之道

你是否遇到过这样的场景:把一份充满图表、产品图片和手写签名的PDF报告丢给传统的RAG系统,它却只能回复“未找到相关信息”?这并非系统“偷懒”,而是传统RAG存在致命的“失明”缺陷——它只能索引和理解纯文本文档,对于图片、表格、公式等视觉元素,它就像一个面对黑暗的人,完全束手无策。

在企业级应用中,这种缺陷尤为致命。一份财务年报可能包含十几张柱状图展示营收变化,一份医疗诊断报告可能关键信息都隐藏在CT影像中,一份产品手册的核心卖点可能全在示意图上。如果RAG系统只能读取文字描述,那么它丢失的信息量往往超过50%。这意味着,你辛辛苦苦构建的RAG系统,在面对这些“重中之重”的视觉信息时,几乎是在盲人摸象。

那么,多模态RAG 正是为打破这种文本边界而生的技术方案。它的核心逻辑很简单:让RAG系统不仅“读”文字,还能“看”图片、“认”图表、“懂”公式。通过将图像、表格等非结构化视觉数据与文本数据统一到一个检索-生成框架中,多模态RAG能够实现真正的“图文并茂”式理解与回答。

具体来说,多模态RAG通过两条核心路径破局:图文向量检索视觉理解+混合排序。前者使用像CLIP这样的双塔模型,将文本和图像映射到同一个高维语义空间,实现“文搜图”、“图搜图”甚至“图文混合搜”;后者则引入OCR(光学字符识别)和VLM(视觉语言模型),让系统能够“读懂”图片中的文字和语义,甚至理解复杂图表背后的逻辑关系。

通过本文,你将系统掌握多模态RAG的完整知识体系:

  • 原理层面:彻底搞懂CLIP双塔模型、RRF融合检索、OCR+VLM协作机制;
  • 实战层面:获得可复现的Python代码,涵盖文搜图、图搜图以及企业级多模态PDF RAG Pipeline;
  • 进阶技巧:掌握RRF参数调优、多模态Embedding领域微调方法,以及避坑指南。

无论你是刚入门RAG的开发者,还是寻求在项目中落地多模态检索的资深工程师,这篇文章都能为你提供清晰的路线图和可直接运行的代码示例。准备好了吗?让我们一起,打破文本的边界。

2. 多模态RAG核心原理:从单模态到图文双通道

2.1 两阶段架构:检索与生成,分工明确

多模态RAG的整体架构可以分为两大阶段:图文混合检索视觉增强生成。这两个阶段并非简单串接,而是相互配合,形成一个完整的闭环。

检索阶段的目标是从海量图文数据中,快速找到与用户查询最相关的候选片段。与纯文本RAG不同,这里的检索对象不仅包括文本段落,还包括图片、图表甚至表格。检索的挑战在于:如何将不同模态的数据(文本、图像)在同一个检索空间内进行比较?

生成阶段的任务则更加复杂:将检索到的多模态片段(文本+图片)作为上下文,结合用户的查询,生成一个自然语言回答。这个回答可能不仅包含文字,还可能引用或描述检索到的图片。例如:“根据图3所示,公司Q3营收增长了15%,这主要得益于新产品线的成功。”

2.2 双塔模型:CLIP如何实现图文语义对齐

实现图文混合检索的核心技术是双塔模型,其代表作就是OpenAI的CLIP。CLIP的工作原理可以用“桥接”来理解:它用两个独立的编码器——一个文本编码器(类似BERT)和一个图像编码器(类似ViT)——分别将文本和图像映射到同一个高维向量空间中。

1
2
3
4
文本编码器(Text Encoder):
"一个红色的汽车" -> [0.12, 0.87, -0.45, ...]
图像编码器(Image Encoder):
图片 -> [0.13, 0.86, -0.44, ...]

通过在海量图文对数据(如4亿个Web图文对)上进行对比学习训练,CLIP学到了一个至关重要的能力:语义相近的文本和图片,它们的向量表示在高维空间中也彼此靠近;语义无关的文本和图片,向量则彼此远离。这就实现了图文语义的“对齐”。

注意:CLIP的对齐是基于语义层面的。它擅长理解图片的“内容”和“主题”,但对于细粒度的物体检测、位置关系、图像中的文字识别(如名片上的电话号码),CLIP表现并不好。这也是为什么在实战中,CLIP常常需要与OCR、VLM配合使用。

假设我们有这样一个知识库:

  • 图片A:一辆红色的跑车在公路上行驶
  • 图片B:一座雪山

用户查询:“红色的汽车”。经过CLIP编码后,“红色的汽车”这个文本向量与图片A的向量余弦相似度会远高于图片B。因此,系统能够准确返回图片A。这就是文搜图多模态RAG实现的底层原理。

2.3 稀疏检索与RRF融合:为什么单纯向量检索不够

尽管向量检索(稠密检索)在多模态检索中表现出色,但它并非万能。特别是当查询中包含精确的实体名、编号、日期时,纯向量检索往往表现不佳。例如,查询“2023年财报P12的表格3”,向量模型可能因为无法理解“P12”这样的位置信息而检索到完全无关的内容。

这时,稀疏检索(如BM25)就派上了用场。BM25基于关键词的词频-逆文档频率进行匹配,对精确匹配非常敏感。但传统的BM25只能处理文本,无法直接处理图片。怎么办呢?

答案是:同时使用向量检索和稀疏检索,然后用RRF(Reciprocal Rank Fusion)融合。具体做法是:

  1. 向量检索:使用CLIP对用户查询和图片编码,计算相似度,得到topN候选结果。
  2. 稀疏检索:对图片使用OCR提取中的文字(或图片的文本描述),然后对这些文本进行BM25检索,得到topN候选结果。
  3. RRF融合:对两个排序结果中的每个候选文档,计算其RRF评分:score = ∑ 1/(rank + k)。其中rank是文档在某个检索结果中的排名,k是一个经验常数(通常设为60)。最后按RRF总分排序。

这种RRF融合检索多模态RAG方案弥补了单一检索方式的不足。向量检索保证“语义相关”,BM25保证“精确匹配”,两者融合后,检索的鲁棒性大大提升。

提示:在多模态RAG中,我们通常只对图片的“文本描述”做BM25,而不是直接对图片做BM25,因为图片本身不包含文本。这个文本描述可以来自OCR识别的文字,或者VLM生成的图片语义标签。

3. 四种典型图文检索方案对比:文搜图、图搜图、混合检索、端到端RAG

理解了原理,我们来看看在实际开发中,多模态RAG图文件检索有哪些具体的实现路线。根据业务需求不同,我们可以选择不同的方案。

3.1 文搜图(Text-to-Image Retrieval):文本查询,返回图片

这是最常用也最基础的场景。用户输入一段文字描述,系统从图片库中返回最匹配的图片。

核心流程

  1. 离线阶段:使用图像编码器(如CLIP的Image Encoder)对所有图片进行编码,将图片向量存入向量数据库(如Faiss、Milvus)。
  2. 在线阶段:用户输入文本查询,使用文本编码器(如CLIP的Text Encoder)将查询编码为向量,在向量数据库中进行ANN(近似最近邻)搜索。
  3. 输出:返回相似度最高的topK图片。

适用场景:产品图库搜索、素材库按主题查找、创意设计参考。

3.2 图搜图(Image-to-Image Retrieval):以图搜图,寻找相似视觉元素

核心流程

  1. 离线阶段:同文搜图,对所有图片进行编码并存库。
  2. 在线阶段:用户提供一张参考图片,使用图像编码器将其编码为向量,然后在向量库中检索与它最相似的图片。
  3. 输出:返回视觉上最接近的图片。

适用场景:以图搜商品(电商)、找相似素材、图像去重、知识产权保护。

3.3 图文混合检索(Hybrid Retrieval):文本+图片特征融合后统一检索

这是更高级的检索方式。用户输入的查询可能同时包含文本和图片。例如,用户上传一张“沙滩”的图片,并输入“加上海边的人”。系统需要理解“沙滩”和“海边的人”这两个条件综合起来的语义。

核心流程:使用一个多模态模型(如CLIP或更强大的多模态Encoder)同时对输入的文本和图片进行编码,得到一个融合的查询向量,然后检索数据库。

适用场景:复杂图像描述、用户提供示例图片+文字说明进行精细搜索。

3.4 多模态RAG端到端:先检索后生成,输出包含图片引用的文字回答

这是最接近智能化应用的方案。系统收到一个图文混合查询后,先进行上述的图文混合检索,然后不是直接返回图片列表,而是将这些视觉信息作为上下文,传给一个VLM(视觉语言模型)或API(如GPT-4o),让模型生成一段描述性文字,并能够在回答中引用检索到的图片。

企业级多模态RAG系统实战:例如,用户问“去年哪个季度的毛利率最高,请出示相关图表”。系统检索到对应的季度财务报表图片(图表),然后调用VLM生成回答:“根据图X所示,第三季度的毛利率为45.3%,是最高的。” 输出中附带了图X。

选型依据

  • 如果你的任务是直接从图片库中取图,文搜图/图搜图就够了,用多模态RAG图文检索方案
  • 如果你的任务是回答一个复杂问题,需要理解图片内容并给出推断,那么多模态RAG端到端是更合适的方案,你需要结合OCR与VLM图片语义识别

4. 实战一:基于CLIP构建文搜图与图搜图检索系统(附代码)

接下来,我们进入实战环节。我们将使用HuggingFace Transformers库中的CLIP模型和开源库Faiss,构建一个完整的文搜图/图搜图检索系统。代码会逐行讲解,确保你能够直接复现。

4.1 环境准备

1
2
3
4
5
# 安装必要的库
pip install torch torchvision
pip install transformers
pip install faiss-cpu # 如果是GPU环境,使用 faiss-gpu
pip install Pillow

4.2 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import torch
from PIL import Image
from transformers import CLIPProcessor, CLIPModel
import numpy as np
import faiss
import os

# ==========================================
# 第一部分:初始化模型和处理器
# ==========================================
# 加载CLIP模型(base版本,约300MB)
model_name = "openai/clip-vit-base-patch32" # 也可以换成 "laion/CLIP-ViT-B-32-laion2B-s34B-b79K"
model = CLIPModel.from_pretrained(model_name)
processor = CLIPProcessor.from_pretrained(model_name)

# 辅助函数:对单张图片进行编码
def encode_image(image_path):
"""
将单张图片转化为向量
:param image_path: 图片文件路径
"""
try:
image = Image.open(image_path).convert("RGB") # 确保是RGB模式
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
image_features = model.get_image_features(**inputs)
# 归一化:这对于余弦相似度计算至关重要
image_features = image_features / image_features.norm(dim=-1, keepdim=True)
return image_features.cpu().numpy().flatten()
except Exception as e:
print(f"处理图片 {image_path} 时出错: {e}")
return None

# 辅助函数:对文本查询进行编码
def encode_query(query_text):
"""
将文本查询转化为向量
"""
inputs = processor(text=[query_text], return_tensors="pt", padding=True)
with torch.no_grad():
text_features = model.get_text_features(**inputs)
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
return text_features.cpu().numpy().flatten()

# ==========================================
# 第二部分:构建图片向量库(使用Faiss)
# ==========================================
# 假设我们有一个图片目录
image_dir = "./my_images" # 替换为你的图片目录
image_paths = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith(('.jpg', '.png', '.jpeg'))]

print(f"发现 {len(image_paths)} 张图片")

# 预计算所有图片的向量,存入Faiss索引
dimension = 512 # CLIP ViT-B/32的向量维度,如果是large版本是768
index = faiss.IndexFlatIP(dimension) # 使用内积索引(与余弦相似度等价,因为已经归一化)

image_vectors = []
valid_image_paths = []
for img_path in image_paths:
vec = encode_image(img_path)
if vec is not None:
image_vectors.append(vec)
valid_image_paths.append(img_path)

# 将向量堆叠成矩阵并添加到索引
if image_vectors:
image_vectors_np = np.array(image_vectors).astype('float32')
index.add(image_vectors_np)
print(f"成功添加 {index.ntotal} 个向量到索引")
else:
print("警告:没有有效的图片向量添加到索引")
exit()

# ==========================================
# 第三部分:执行检索
# ==========================================
def search_by_text(query_text, top_k=5):
"""
文本搜图(文搜图)
:param query_text: 用户输入的文本描述
:param top_k: 返回最匹配的前k张图片
"""
query_vec = encode_query(query_text).astype('float32').reshape(1, -1)
distances, indices = index.search(query_vec, top_k)

results = []
for i, idx in enumerate(indices[0]):
if idx != -1 and idx < len(valid_image_paths):
results.append((valid_image_paths[idx], distances[0][i]))
return results

def search_by_image(query_image_path, top_k=5):
"""
图搜图(以图搜图)
"""
query_vec = encode_image(query_image_path)
if query_vec is None:
return []
query_vec = query_vec.astype('float32').reshape(1, -1)
distances, indices = index.search(query_vec, top_k)

results = []
for i, idx in enumerate(indices[0]):
if idx != -1 and idx < len(valid_image_paths):
results.append((valid_image_paths[idx], distances[0][i]))
return results

# ==========================================
# 第四部分:测试检索效果(示例)
# ==========================================
if __name__ == "__main__":
# 测试文搜图
query = "一片秋天的树林" # 替换为你的查询
print(f"\n文搜图查询:'{query}'")
results = search_by_text(query, top_k=3)
for path, score in results:
print(f" 图片: {path}, 相似度: {score:.4f}")

# 测试图搜图(假设有一张参考图片)
ref_image = "path/to/reference.jpg" # 替换为参考图片路径
if os.path.exists(ref_image):
print(f"\n图搜图查询(参考图片:{ref_image})")
results = search_by_image(ref_image, top_k=3)
for path, score in results:
print(f" 图片: {path}, 相似度: {score:.4f}")

4.3 关键点与优化思路

  • 归一化:第7步中的归一化非常重要。如果不归一化,Faiss的内积索引计算的是点积结果,无法直接用于衡量相似度。归一化后,点积等价于余弦相似度。
  • batch_size:如果在离线阶段处理大量图片,可以设置batch_size参数,避免内存溢出。processor(images=image_list, return_tensors="pt", padding=True) 可以一次处理多张图片。
  • 相似度阈值:在实际应用中,建议设置一个相似度阈值(如0.7),低于该阈值的图片直接过滤。这能有效减少无关结果的干扰。

最佳实践:当你的图片库超过10万张时,Faiss的IndexFlatIP(暴力搜索)速度会变慢。建议更换为更高级的索引结构,如IndexIVFFlat(倒排文件索引)或IndexHNSWFlat(分层可导航小世界图)。这些索引可以显著提升检索速度,但会略微降低召回率。

5. 实战二:OCR + VLM 实现多模态PDF RAG Pipeline(企业级场景)

第四节的CLIP检索系统只能处理纯图片场景。在企业级应用中,我们面对的是包含图文混排的PDF文档。这时,我们需要组合使用OCR(光学字符识别)VLM(视觉语言模型),才能实现真正的多模态RAG。

5.1 整体架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PDF文档
|
+-- 文本提取(使用PyMuPDF等工具)
|
+-- 图片提取(从PDF中解析出图片块)
| |
| +-- OCR提取图片中的文字(PaddleOCR)
| |
| +-- VLM生成图片语义标签(Qwen-VL / InternVL)
|
+-- 构建统一向量数据库
| |
| +-- 文本块 embedding
| +-- (图片OCR文本 + 图片语义标签)组合成“图片文本摘要”后 embedding
|
+-- 检索阶段(RRF融合)
|
+-- 生成阶段:LLM生成包含图片引用的回答

5.2 抽取PDF中的图片块并进行OCR/VLM处理

由于篇幅限制,这里提供核心思路和伪代码片段,实际实现时可参考具体库的文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# 注意:以下代码为伪代码,需要结合具体库实现
import fitz # PyMuPDF,用于PDF操作
from paddleocr import PaddleOCR
from transformers import AutoModelForCausalLM # 以Qwen-VL为例

# 1. 从PDF提取图片
def extract_images_from_pdf(pdf_path):
doc = fitz.open(pdf_path)
images_info = []
for page_num, page in enumerate(doc):
# 获取页面上的所有图片引用
image_list = page.get_images(full=True)
for img_index, img in enumerate(image_list):
xref = img[0]
base_image = doc.extract_image(xref)
image_bytes = base_image["image"]
image_ext = base_image["ext"]
image_path = f"page_{page_num}_img_{img_index}.{image_ext}"
with open(image_path, "wb") as f:
f.write(image_bytes)
images_info.append({
"image_path": image_path,
"page_num": page_num,
"position_on_page": img # 记录图片在页面上的位置(bbox等)
})
return images_info

# 2. 对图片进行OCR和VLM处理
def process_image_with_ocr_vlm(image_path, vlm_model):
"""
使用OCR提取图片中的文字,使用VLM生成图片的语义标签
"""
# 初始化OCR引擎(PaddleOCR)
ocr = PaddleOCR(use_angle_cls=True, lang='ch')
result = ocr.ocr(image_path, cls=True)

# 提取OCR识别的文字
ocr_text = ""
for line in result:
for word_info in line:
ocr_text += word_info[1][0] + " "
# OCR文字可作为图片的“文本摘要”

# 使用VLM生成语义标签
# 假设vlm_model是一个可以接受图片并返回描述的模型
image = Image.open(image_path)
prompt = "用一句话描述这张图片的主要内容"
vlm_description = vlm_model.generate(prompt, image=image)
# vlm_description 如:"一个红色的汽车"

# 组合OCR文本和VLM描述,作为该图片的“最终文本表示”
# 注意:OCR文本和VLM描述都是文本,都可以用于BM25检索
combined_text = f"图片所在页面: {page_num}\n图片内容描述: {vlm_description}\n图片中文字: {ocr_text}"

return {
"ocr_text": ocr_text,
"vlm_description": vlm_description,
"combined_text": combined_text
}

# 3. 构建统一索引
# 文本块 -> embedding
# 图片的combined_text -> embedding
# 使用同一embedding模型编码,存入Faiss
# ...(具体代码参考第4节)

# 4. 检索阶段:RRF融合
# ...(略,参考第2节原理)

# 5. 生成阶段:LLM生成回答
def generate_answer_with_images(query, retrieved_results, llm):
"""
retrieved_results包含文本片段和图片信息
"""
# 构建包含图片引用的上下文
context = ""
for result in retrieved_results:
if result.type == "text":
context += f"从文档第{result.page}页提取的文本: {result.content}\n"
elif result.type == "image":
context += f"从文档第{result.page}页提取的图片,描述为: {result.combined_text}\n"
# 关键:在上下文中标记图片存在,LLM后才能引用
context += f"[图片引用: {result.image_path}]"

# 让LLM生成回答,期望它能够引用图片
prompt = f"""以下是从知识库中检索到的相关上下文。

请根据上下文回答用户的问题。
如果上下文中有图片,请在回答中引用它们,例如“根据图X所示”。

上下文:
{context}

用户问题:{query}

回答:"""
response = llm.generate(prompt)
return response

5.3 关键考量

  • 图片位置记录:在提取图片时必须记录它在文档中的位置(页码、区域)。这样在生成回答时,才知道是哪一页的哪张图片。
  • OCR与VLM分工:OCR擅长提取图片中的精确文字(如表格数字、名牌、公式),VLM擅长语义理解(如“这是一张展示公司Q3营收的柱状图”)。两者组合,互为补充。
  • 企业级多模态RAG系统实战:考虑到性能,建议对图片进行预处理。例如,将大图片缩放到512x512像素以上,否则VLM处理会非常慢。同时,可以缓存VLM的生成结果,避免重复调用。

6. 进阶技巧:RRF融合检索优化与多模态Embedding微调

6.1 RRF中的k值选择

RRF中k值的选择对检索结果影响很大。k值越大,不同排序来源的权重越均衡,融合结果受低频分的影响越小;k值越小,高排名的权重更大,融合结果更倾向于某个单独的排名来源。

经验值推荐k=60。但在多模态RAG场景下,由于向量检索(CLIP)和稀疏检索(BM25 over OCR文本)的得分尺度可能相差很大,建议通过网格搜索调整k值(如尝试k=30, 60, 100),并在验证集上评估召回率。一个“trick”是:如果你的OCR文本非常短(几张图片),BM25的得分区间很窄,可以适当增大k值(如k=100),让向量检索的权重稍微突出一些。

6.2 多模态Embedding的领域微调

通用CLIP模型在特定领域(如医学影像、工业图纸)上的表现可能不尽人意。因为CLIP的训练数据主要集中在自然场景和日常物体上。为了提升专业领域的检索召回率,我们可以对CLIP进行微调,称为多模态Embedding微调

推荐方法:LoRA微调
由于CLIP模型参数量较大(约300M),全参数微调成本高。LoRA是一种高效的微调方法,它冻结原始模型,在模型层中插入少量可训练的低秩矩阵。你可以使用LoRA对CLIP的图像编码器或文本编码器进行微调。

微调数据准备

  • 收集领域内的图文对数据。例如,对于医学领域,收集X光片+诊断报告描述;对于工业领域,收集零件图纸+技术参数说明。
  • 数据量至少需要数百对。

微调步骤(简化)

  1. 加载预训练CLIP模型。
  2. 使用LoRA库(如PEFT)配置LoRA(r=8, alpha=16)。
  3. 使用对比学习损失函数进行微调,优化目标:让正确的图文对相似度高于所有负样本对。
  4. 保存微调后的LoRA权重,在推理时加载。

6.3 引入多模态知识图谱RAG

如果你想进一步强化RAG的推理能力,可以考虑多模态知识图谱RAG。思路如下:

  1. 从多模态文档中提取实体(如“公司A”,“产品B”)和关系(如“生产”)。
  2. 将识别出的实体(可能来自OCR文本或VLM)存入图数据库(如Neo4j)。
  3. 在检索时,先通过知识图谱进行多跳推理,找到深度相关的文档节点。
  4. 最后,将这些节点作为候选结果进行排序。

这种方案能让RAG系统理解复杂的逻辑关系,而不仅仅是语义相似。

7. 踩坑记录:部署多模态RAG常见的10个陷阱

  1. 图片分辨率不足:OCR对低分辨率图片(<150 DPI)的识别准确率会急剧下降。预处理时应放大PDF扫描件或使用超分辨率模型。
  2. 表格识别需额外模型:普通的OCR很难正确处理表格结构,建议使用专门的表格识别模型(如TableMASTER、CascadeTabNet)提取结构化表格数据。
  3. 大图内存溢出:VLM处理高分辨率大图时极易OOM。标准做法是将输入图片预处理为最大边不超过512像素,且保持宽高比。
  4. CLIP对艺术字体理解差:CLIP的文本编码器对非标准字体(如手写体、艺术字)不敏感。重排版/艺术字体建议先用OCR转成标准文本再输入。
  5. VLM推理延迟过高:多模态大模型通常较大,推理速度慢。

建议使用推理框架(如vLLM)或量化模型(如AWQ、GPTQ)。
6. 图文对齐错乱:在多页PDF中,图片可能被分割到两页之间,导致检索到的图片与文本描述的位置不匹配。必须始终保留并记录每一张图片在原始文档中的精确位置(页码、bbox)。
7. OCR文本与图片内容无关:有些图片(如装饰背景图)的OCR文本可能只是一些无意义的字符串。

需要设置过滤规则,如只保留文字行数>3的图片。
8. 向量维度选择不一致:在微调后,千万不要更改CLIP模型的输出向量维度,否则与Faiss索引维度不匹配。
9. 评估问题:传统的Recall@k指标无法衡量生成的含图回答的准确性。要使用EvalScope或多模态问答对,评估生成的回答是否引用了正确的图片,以及文字描述是否与图片相符。

  1. 多语言支持:PaddleOCR支持中文,但CLIP对非英语文本的理解可能较差。如果需要多语言支持,推荐使用多语言CLIP版本或AltCLIP。

8. 总结与拓展:从单文档到多模态Agent的未来

8.1 核心要点回顾

通过本文,我们完整走通了多模态RAG的从原理到实战的全流程。核心可以归纳为一条公式:

多模态RAG = CLIP/多模态Embedding + RRF融合 + VLM生成

  1. 图文混合检索:依赖CLIP双塔模型实现文搜图和图搜图,解决了“语义对齐”的问题。
  2. RRF融合:弥补了向量检索无法精确匹配的缺点,通过融合BM25,提升了对包含精确词汇查询的鲁棒性。
  3. 视觉增强生成:通过OCR提取图片中的文字,VLM生成语义标签,从而让LLM能够理解并引用图片内容。

8.2 未来拓展方向

  1. 多模态Agent:未来的RAG系统将不再是被动响应,而是主动“观察”。模型可以自主决定何时调用图搜图“找相似图片”,何时调用文搜图“找语义相关段落”,甚至在不确定时调用拍照功能“看看现实世界”。这将是多模态RAG与Agent的融合。
  2. 端到端多模态原生模型:GPT-4o、Gemini等模型已经打破了“先检索后生成”的范式,它们可以直接理解多模态输入,甚至进行多模态推理。未来,RAG系统可能不再需要显式的检索阶段,而是让模型直接“回忆”或“推理”知识。
  3. 结合知识图谱的多模态推理:如第6节所述,将多模态知识图谱引入RAG,可以让系统理解复杂的逻辑链条。例如:从“产品A”的图片→识别出“零件B”→从知识图谱中找到“零件B的供应商C”→生成回答“这个零件由供应商C提供”。

8.3 下一步行动

不要停留在理论层面。建议你立刻下手,尝试以下具体任务以巩固学习成果:

  1. 找一份图文混排的PDF年报、PPT或产品手册。
  2. 使用PaddleOCR和Qwen-VL(或GPT-4o)构造图片的文本/语义标签。
  3. 将文本和“图片标签”统一embedding后存入Faiss。
  4. 向系统提问一个需要引用图表才能回答的问题,观察系统是否能正确检索并生成带引用的答案。

当你真正动手实现了第一个多模态RAG Pipeline,你将发现这不仅是技术的精进,更是对“知识”本身维度的一次突破。祝你好运!