跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

构建多模态 RAG 系统的终极指南

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

三个月前,我们新开发的 AI 应用在诸多看似简单的问题上频频“翻车”。问题根源并非 AI 不够智能或数据不足,而是因为答案蕴含在一张图片里,而当时的系统仅能处理文本。

这一时刻迫使我直面一个在构建 RAG 系统时长期回避的核心问题:我们花费数年时间教 AI “阅读”文字,却忽略了人类同样通过图像、表格、公式和流程图来“表达”信息。

在现实世界中,真正的智能需要能够跨越单一模态的边界,通过关联文本、图像、音频乃至视频,进行更具上下文感知和依据充分的推理。因此,我们需要多模态检索增强生成,实现在不同数据格式间的动态检索与生成。

本文将系统性地拆解多模态 RAG 的架构、核心挑战、关键方法论与创新,以及如何评估与实施。


目录:

  • 为何一定要做多模态?
  • 多模态 RAG Pipeline 解剖
  • 多模态 RAG 的“不可能三角”
  • 多模态方法论的突破与创新
    1. 如何为多模态内容做更好的检索?
    1. 多模态 RAG 如何融合信息?
    1. 增强、推理与生成
  • 如何验证一个多模态 RAG 系统真的按我们预期工作?
  • 多模态 RAG 的实用实现路径
  • 路径一:把所有模态都转成文本
  • 步骤 1:环境与安装
  • 步骤 2:加载与初始化模型
  • 步骤 3:文档处理与元数据提取
  • 步骤 4:基于文本的检索
  • 步骤 5:基于图像的检索
  • 步骤 6:完整的多模态 RAG Pipeline
  • 路径二:RAG Anything 框架
  • 多模态 RAG 的开放性难题

为何一定要做多模态?

现实世界的数据天然分布在文本、图像、音频、视频等多种模态中。在多数场景下,一个能够从所有模态中抓取信息的 RAG 系统,其回答质量会显著提升。例如,在情感分析中,图片或表情符号的模式常常能揭示仅凭文字难以捕捉的语气转折。在动态的网页搜索中,正确答案可能依赖于一张地图截图、一份图表或一段播客片段。只有具备跨模态理解与检索能力的系统,才能提供更稳健、上下文更丰富且可溯源的回答。

具体而言,这意味着系统必须能从所有格式中提取上下文与证据:一份放射科报告不应仅引用临床笔记,而应能“阅读并理解”X光影像;回答科研论文的复杂问题时,需要综合文本论述并解读其中的图表或示意图;在客服场景中,系统需能识别用户上传的截图,并将其与知识库中的相关文档关联起来。

多模态 RAG Pipeline 解剖

要理解多模态 RAG 的独特之处,必须剖析其核心流程:当系统需要利用文本、图像、音频甚至视频来回答问题时,背后究竟发生了什么?理解这条端到端的流程,是把握现代多模态 RAG 复杂性的关键。

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

  1. 查询理解
    用户的查询可能是附带图片的文本问题,或是“音频片段 + 文字说明”的组合输入。第一步是将所有输入模态编码到一个共享的语义空间中。这需要专门的编码器,将文本、图像、音频分别转换为可对齐的向量表示。这种多面向的表示确保了后续步骤不仅理解字面意思,还能纳入完整的上下文。

  2. 检索
    接着,系统在一个动态索引的多模态知识库中搜索相关信息。依据相似度打分或学习到的相关性模型,系统会筛选出与用户意图对齐的候选文档、图片、视频等。

  3. 融合
    这是证据综合阶段。来自多个来源的检索结果不仅被“聚合”,还会通过分数融合、交叉注意力机制或投射到共享嵌入空间等方式进行“融合”。目标是使不同模态的信号对齐,便于下游生成器跨模态识别关键线索,例如将图片中的视觉内容与文本描述对应起来。

  4. 增强
    系统基于迭代反馈、用户引导或自我反思,对检索结果进行过滤、补充或重排。采用诸如上下文增强与迭代检索等技术,确保最终仅使用最高质量的上下文。

  5. 生成
    最终,一个先进的多模态大语言模型接收经过融合与增强的上下文,生成有依据、可追溯的回答。输出本身也可以是多模态的,例如带有图片高亮区域的文字说明,或一组带标注的视频帧序列。


多模态 RAG 的“不可能三角”

多模态 RAG 并非“传统 RAG + 图片”那么简单。它在本质上复杂得多,需要同时应对研究者称之为“不可能三角”的三大核心挑战。

挑战一:统一的多模态表示
如何构建一个能同时处理文本、表格、图片、公式的系统,同时保留每种模态的独特性?

挑战二:结构感知的分解
真实文档往往结构严密,空间关系也承载着重要含义。图注不仅描述图片,还提供了理解框架。公式并非孤立存在,而是与周围的定义相互关联。破坏这些连接,就等于摧毁了知识图谱。

挑战三:跨模态检索
最困难的是,答案往往需要跨模态推理:从文本论断出发,引用表格作为证据,再由可视化图表进行总结。传统系统将每种模态视为独立孤岛,无法在这些跨模态连接中游刃有余。


多模态方法论的突破与创新

1. 如何为多模态内容做更好的检索?

真实世界的查询很少是纯文本。用户可能上传一张图片并附上一句简短的文字。系统需要既能找到“看起来像”参考图片的内容,又要满足文字中指定的细节,例如“同款物体但换个颜色”或“相似场景”。两种输入都至关重要。

传统检索系统在此处会陷入困境:到底该赋予哪种模态更高的权重?是图片更重要,还是文本更重要,抑或两者需兼顾?

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

1. 高效相似度搜索
最大内积搜索是常用方法,它通过比较表示向量的相似度来匹配查询与文档。像 BanditMIPS 和 MUST 等模型都采用这类方法,以兼顾准确性与速度。

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

一些常见框架包括:
* 多流检索:将每种输入分开处理,独立检索后再合并结果。方法简单,但效率低且准确性难以保证,尤其在难以判断模态重要性时,难以从多个大型候选池中“挑选最佳”。
* 联合嵌入:一开始就将所有输入融合成一个向量。但这可能会模糊关键细节,导致嵌入表示混乱,检索能力减弱。
* MUST 框架:采用跨阶段的混合融合策略,既结合了不同模态的信号,又保留了各自的“个性”。
* 多向量表示:为每个模态分配一个高维向量,提供更丰富、更清晰的表达。
* 向量权重学习:引入一个小模型来学习各模态的重要性权重。这些权重改进查询与上下文的联合相似度评分,捕捉的是“模态本身的重要性”,而非具体内容,从而具备更强的泛化能力。学到的权重可用于拼接成单一向量进行搜索,用户也可手动覆盖权重以实现可控检索。
* 融合索引与联合搜索:不同于多流检索建立多个索引,MUST 构建一个融合的邻近图索引,并利用学习到的权重进行平衡融合,避免了耗时的后处理合并,大幅加速了联合搜索。

实验证明其性能显著提升:检索速度提升10倍以上,准确度较基线提升约93%。

2. 以模态为中心的检索
现实问题常常包含图片、视频或复杂文档,系统需要“全模态”应对能力。
* 文本中心模型:如 BM25、MiniLM、ColBERT,擅长在文字内容中找到最佳匹配。
* 视觉中心模型:如 CLIP、BLIP、ALIGN,可以用文本查询检索图片。还有像 EchoSight 与 ImgRet 这类“以图搜图”的模型。

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

例如在 EchoSight 中,面对带图像的视觉问题,检索器会在知识库中找到与参考图最相似的前K张图片对应的维基百科条目。将粒度下放到“章节”后,再利用文本嵌入与“参考图 + 问题”的 Q-Former 查询令牌的最大成对相似度对这些章节进行重排序,排名最高的章节作为 RAG 提示提供给大语言模型生成最终答案。

  • 视频中心系统:如 iRAG、VideoRAG 与 MV-Adapter,面向动态内容,擅长处理序列、覆盖长视频或挑选关键帧。

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

例如,Video-RAG 包含三大阶段:在查询解耦阶段,大视觉语言模型被提示生成用于检索辅助文本的请求;在辅助文本生成与检索阶段,并行从视频中抽取 OCR、ASR、目标检测三类文本信息并检索相关内容作为辅助文本;在整合与生成阶段,将辅助文本与查询及视频一起用于生成回答。

  • 面向文档的检索器:超越了纯文本,如 ColPali、M3DocVQA、mPLUG-DocOwl 等,还能检索表格、图形、版面布局等。

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

例如 M3DOCRAG 包含三阶段:文档嵌入、页面检索与问答。首先用 ColPali 为每页生成视觉嵌入;然后用文本查询在开放域索引中检索前K个高相关页面;最后用多模态语言模型进行视觉问答得到答案。

3. 重排序与选择
找到“相似”的内容只是第一步,挑出“真正有用”的才至关重要。
* 优化示例选择:对视频可使用聚类或关键时刻选择,确保结果既相关又多样。
* 相关性打分:仅靠相似度不够。SSIM、交叉相关、BERTScore 等指标可衡量证据是否真正回答了问题;也可以训练神经排序器进行更强的排序。
* 过滤:并非所有检索结果都有帮助。困难负样本挖掘、共识评分、动态过滤等技术可用于清理噪声与无关项。


2. 多模态 RAG 如何融合信息?

检索只是第一步。要回答复杂问题,AI 必须将来自文本、图像、音频等多种来源的事实“搭建”起来。系统究竟如何把一切“整合”到一起?

2.1 分数融合
一种实用方法是分数融合。每段证据都会获得一个与问题相关的“相关性分数”。系统融合不同模态的分数,确保跨媒体中最有帮助的内容排在前面。

2.2 交叉注意力
有些问题不是简单“堆叠来源”就能解决的。图片中的上下文可能改变一句话的含义,反之亦然。交叉注意力机制允许模型在各个输入中“聚焦”关键部分,例如将词语与图像中的对象对齐,或将声音与描述对齐。这有助于模型跨模态理解并融合信息,防止关键细节在“翻译”过程中丢失。

2.3 对齐方法
对齐是将不同数据类型“翻译”到一个“共同语言”中,便于比较和融合。像 CLIP、BLIP 这样的方法会创建一个联合空间,把文本、图像甚至视频都映射为“数学空间里的点”。当系统看到问题或证据时,一切都投射到这个共享空间,更容易发现真实关联。

2.4 统一框架与投影
与其为每种数据构建独立的处理流水线,不如让所有模态都输入一个“统一大模型”。有些框架采用联合投影空间,让各模态以平等方式被映射与比较,从而保持一致性,并使系统推理更可解释。内容统一大致有三条路径:

2.4.1 统一为文本

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

UniRAG 以实现多模态问答为目标,其思路直接:将一切转换为文本。
* 阶段1:将所有输入转为文本。图片使用 LLaVA 等模型生成描述;表格拉平成线性文本。这样,视觉、表格等内容都变为统一的文本形态。
* 阶段2:进行检索。使用交叉编码器为“问题-文档”对打分,选出前K个最相关的。
* 阶段3:进行生成。大语言模型读入收集到的文本,生成最终回答。
通过将所有内容当作文本处理,UniRAG 使整个流程简洁统一。

2.4.2 统一为图像
(此处内容待补充,根据原文结构,后续应继续阐述“统一为图像”及其他融合方法,但提供的片段在此处结束。)

M3DOCRAG:面向复杂文档的视觉问答

许多真实文档(如报告、论文、表单)包含图表、多栏布局或图文混排,传统的纯文本OCR处理会丢失这些关键的视觉结构信息。M3DOCRAG 通过一种保留页面原始视觉结构的方法来解决这一问题,其核心流程分为三步:

  1. 页面编码:将每一页文档编码为视觉嵌入(例如使用 ColPali 等多模态编码器)。
  2. 相关页面检索:根据用户查询,利用高效的索引(如倒排索引)从大规模文档库中检索出最相关的 Top-K 页面。
  3. 多模态答案生成:将检索到的页面图像与原始文本问题一同输入多模态大模型(如 Qwen2-VL),生成最终答案。

当答案依赖于图表数据、图形元素或页面空间布局,或者所需信息分散在多个页面时,这种以图像为中心的流程优势尤为明显。传统的纯文本 RAG 系统往往无法捕捉这些视觉线索,而 M3DOCRAG 能够完整保留并利用它们。

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

M3DOCRAG 框架总览 [Source]

2.4.3 跨模态统一表示

RAG-Anything 提出了一种更深层次的整合策略。它将不同模态的内容统一表示为“互联的知识实体”,并采用“双图构建策略”,在一个统一的表示空间中同时捕捉跨模态的关联关系和文本语义。

系统引入了“跨模态混合检索机制”,将两种检索方式结合:
* 基于结构知识的导航:利用构建的显式图关系进行多跳推理,沿着实体关系链查找信息。
* 基于语义的稠密检索:通过嵌入向量进行相似度匹配,查找语义相关的内容。

这种一体化设计使 RAG-Anything 能够无缝访问和关联所有模态中的知识碎片,在处理证据分散、篇幅冗长的复杂文档时表现更为出色。

跨越模态边界:构建真正理解图像、表格与文本的多模态RAG系统

RAG-Anything 框架总览 [Source]


3. 增强、推理与生成

仅仅完成信息的“收集与融合”是不够的。要生成真正可靠、有用的答案,系统还需要以下关键能力:

上下文增强与自适应检索
模型有时需要比单次检索结果更丰富的上下文。先进的系统会利用用户反馈或模型自身对中间答案的置信度,在答案薄弱或不完整时,触发迭代检索。这种自适应的检索机制确保了系统能持续挖掘信息,直至足以解决复杂的多段问题。

来源溯源
不仅要给出答案,还要清晰地标注“答案的每一部分来源于哪里”(例如,将生成的一句话链接到源文档的某张图或某个段落)。这极大地提升了系统的可信度,并方便用户进行事实核查。

上下文学习、指令微调与损失函数
现代系统通过多种技术持续优化其推理能力:
* 指令微调:让模型更好地理解和遵循复杂的人类指令。
* 上下文学习:使模型能够从对话中提供的少量示例里临时学习并应用新知识。
* 精心设计的损失函数:在训练中奖励正确的推理步骤,而不仅仅是最终答案的正确性。

这些技术共同作用,让模型变得更擅长“推理与解释”,而不仅仅是“检索与复述”。


如何评估多模态 RAG 系统的性能?

验证一个多模态 RAG 系统是否按预期工作,需要借助专门的基准测试和评估指标。它们如同AI的“试车场”,通过设计视觉推理、新事实检索、跨模态多步问答等难题来全面考验模型。

  • 专用基准:例如,M2RAG 专注于评估“文本+图像”的联合理解能力;MRAG-Bench 和 VisDoMBench 则聚焦于视觉问题求解。动态基准(如 Dyn-VQA、ScienceQA)强调多跳推理,要求模型串联多个线索才能得出正确答案。

评估多模态AI的“理解与相关性”是一项复杂任务,需要针对不同信息类型采用公平的指标:
* 文本:可使用 BLEU、ROUGE、METEOR、精确匹配等指标。
* 图像:描述匹配度可用 CIDEr、SPICE、CLIP Score;生成质量可用 FID、KID;语义一致性可用 BERTScore。
* 音频:可使用 Fréchet Audio Distance。
* 特定领域:还有专属指标,如地图分析中的测地距离、医疗领域的临床相关性评分等。

多模态评估的核心挑战在于同时兼顾文本、图像、音频、视频等多种模态。每种模态的“准确性”标准不同:模型可能答对了文字部分,却遗漏了图片中的关键细节;或者建立了正确的跨模态关联,却在细节上出现偏差。随着模态增加,测试的复杂性呈指数级增长,但这也使得细致评估变得更为重要——否则,那些在真实场景中会被放大的隐性错误与能力缺口将难以被发现。

多模态 RAG 的实用实现路径

下面介绍两种构建多模态 RAG 系统的实践路径。

路径一:将所有模态统一转换为文本进行处理

此路径的核心思想是利用强大的多模态大模型,将非文本信息(如图像)转化为或关联到文本描述,从而在统一的文本语义空间中进行检索和生成。我们将使用以下 Google Cloud 工具链:
* Gemini API:用于理解图像内容并生成文本描述或回答。
* Vertex AI Embeddings:用于生成文本和图像的语义向量表示。
* Vector Search:基于向量相似度进行高效检索。

整体流程分为三步:
1. 文档处理与嵌入生成:从文档中提取文本和图像,并分别生成对应的语义嵌入。
2. 相似度搜索:将用户查询(文本或图像)转换为嵌入,并在向量数据库中检索最相关的多模态内容。
3. 上下文生成:将检索到的文本和图像描述作为上下文,输入给大模型生成最终答案。

步骤 1:环境配置与依赖安装

# 安装必要的包
!pip install --upgrade --quiet google-cloud-aiplatform
!pip install --quiet pillow numpy pandas

# 导入必要的库
import vertexai
from vertexai.preview.generative_models import GenerativeModel, Part
from vertexai.language_models import TextEmbeddingModel
from vertexai.vision_models import MultiModalEmbeddingModel, Image
from google.cloud import storage
import numpy as np
import pandas as pd
from PIL import Image as PILImage
import base64
from io import BytesIO

# 设置您的 Google Cloud 项目 ID 和区域
PROJECT_ID = "your-project-id"  # 请替换为您的实际项目 ID
LOCATION = "us-central1"

# 初始化 Vertex AI
vertexai.init(project=PROJECT_ID, location=LOCATION)

此步骤初始化 Vertex AI SDK,为使用 Gemini 模型和 Embedding API 搭建多模态 RAG 系统做好准备。

步骤 2:加载与初始化模型

# 初始化用于文本生成的 Gemini 模型
gemini_model = GenerativeModel("gemini-2.0-flash")
# 初始化文本嵌入模型
text_embedding_model = TextEmbeddingModel.from_pretrained("text-embedding-005")
# 初始化多模态嵌入模型(支持图像和文本)
multimodal_embedding_model = MultiModalEmbeddingModel.from_pretrained(
    "multimodalembedding"
)

步骤 3:文档处理与元数据提取

(此部分内容需根据具体文档解析逻辑展开,例如使用 PyPDF2、pdf2image 等库提取页面和图像,并调用上述嵌入模型生成向量。)

这一步是“魔法”的开始:我们需要同时抽取文本与图像,并分别生成嵌入向量。

def get_document_metadata(doc_path, gemini_model, text_embedding_model,
                          multimodal_embedding_model):
    """
    从文档中提取文本和图像的元数据。

    参数:
        doc_path: PDF文档路径
        gemini_model: 用于图像描述的Gemini模型
        text_embedding_model: 用于文本嵌入的模型
        multimodal_embedding_model: 用于图像嵌入的多模态模型

    返回:
        dict: 包含文本块、图像及其嵌入向量的元数据字典
    """
    metadata = {
        'text_chunks': [],
        'text_embeddings': [],
        'images': [],
        'image_descriptions': [],
        'image_embeddings': [],
        'page_numbers': []
    }

    # 步骤1:从文档页面提取文本
    # 在实际生产中,可使用PyPDF2或pdfplumber等库
    text_chunks = extract_text_from_pdf(doc_path)

    # 步骤2:生成文本嵌入向量
    for chunk in text_chunks:
        embedding = text_embedding_model.get_embeddings([chunk])[0].values
        metadata['text_chunks'].append(chunk)
        metadata['text_embeddings'].append(embedding)

    # 步骤3:从文档中提取图像
    images = extract_images_from_pdf(doc_path)

    # 步骤4:使用Gemini生成图像描述并计算嵌入向量
    for idx, image in enumerate(images):
        # 使用Gemini详细描述图像
        prompt = """Describe this image in detail. Focus on:
        - What type of visualization is this (chart, table, diagram)?
        - What data or information does it convey?
        - Key insights or patterns visible in the image
        """

        response = gemini_model.generate_content([prompt, image])
        description = response.text

        # 为图像生成多模态嵌入向量
        image_embedding = multimodal_embedding_model.get_embeddings(
            image=image,
            contextual_text=description
        ).image_embedding

        metadata['images'].append(image)
        metadata['image_descriptions'].append(description)
        metadata['image_embeddings'].append(image_embedding)
        metadata['page_numbers'].append(idx)

    return metadata

# 处理文档
doc_metadata = get_document_metadata(
    doc_path="path/to/your/document.pdf",
    gemini_model=gemini_model,
    text_embedding_model=text_embedding_model,
    multimodal_embedding_model=multimodal_embedding_model
)
print(f"Extracted {len(doc_metadata['text_chunks'])} text chunks")
print(f"Extracted {len(doc_metadata['images'])} images")

流程分为两部分:首先抽取并切分文本,然后抽取文档中的图像。对于每张图像,使用Gemini生成详细的描述,这段“上下文文本”有助于多模态嵌入模型更准确地理解图像内容。随后,分别为文本片段和图像生成嵌入向量,并将所有信息记录在结构化的元数据中。

步骤 4:基于文本的检索

获得嵌入向量后,即可通过相似度搜索查找最相关的文本片段:

import numpy as np

def cosine_similarity(vec1, vec2):
    """计算两个向量之间的余弦相似度。"""
    return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))

def search_text(query, metadata, text_embedding_model, top_k=3):
    """
    基于文本查询搜索相关文本块。

    参数:
        query: 搜索查询字符串
        metadata: 包含嵌入向量的文档元数据
        text_embedding_model: 用于生成查询嵌入向量的模型
        top_k: 返回的结果数量

    返回:
        list: 最相关的k个文本块及其相似度分数
    """
    # 为查询生成嵌入向量
    query_embedding = text_embedding_model.get_embeddings([query])[0].values

    # 计算与所有文本块的相似度
    similarities = []
    for idx, text_emb in enumerate(metadata['text_embeddings']):
        similarity = cosine_similarity(query_embedding, text_emb)
        similarities.append({
            'text': metadata['text_chunks'][idx],
            'similarity': similarity,
            'index': idx
        })

    # 按相似度排序并返回前k个结果
    similarities.sort(key=lambda x: x['similarity'], reverse=True)
    return similarities[:top_k]

# 示例:文本搜索
query = "What was the total revenue in 2023?"
results = search_text(query, doc_metadata, text_embedding_model)
print("Top search results:")
for i, result in enumerate(results, 1):
    print(f"n{i}. Similarity: {result['similarity']:.4f}")
    print(f"Text: {result['text'][:200]}...")

示例输出:

Top search results:

1. Similarity: 0.8234
Text: Our total revenue for fiscal year 2023 reached $282.8 billion,
representing a 9% increase year-over-year. This growth was primarily
driven by our cloud services division...

2. Similarity: 0.7891
Text: In Q4 2023, we achieved record quarterly revenue of $86.3 billion...

步骤 5:基于图像的检索

多模态 RAG 的核心优势之一在于其灵活的跨模态检索能力,例如“以文搜图”和“以图搜图”。这极大地简化了在复杂文档中定位视觉信息的过程。

1. 以文搜图
通过文本查询来搜索相关图像。其核心流程是:首先使用多模态嵌入模型将文本查询转换为向量,然后计算该向量与文档中所有图像嵌入向量的余弦相似度,最后返回相似度最高的结果。

def search_images_with_text(query, metadata, multimodal_embedding_model, top_k=3):
    """
    使用文本查询搜索相关图像。

    Args:
        query: 搜索查询字符串
        metadata: 包含图像嵌入向量的文档元数据
        multimodal_embedding_model: 用于生成嵌入向量的模型
        top_k: 返回结果的数量

    Returns:
        list: 最相关的 top_k 个图像及其描述
    """
    # 使用多模态模型生成文本嵌入向量
    text_emb_result = multimodal_embedding_model.get_embeddings(
        contextual_text=query
    )
    query_embedding = text_emb_result.text_embedding

    # 计算与所有图像嵌入向量的相似度
    similarities = []
    for idx, img_emb in enumerate(metadata['image_embeddings']):
        similarity = cosine_similarity(query_embedding, img_emb)
        similarities.append({
            'image': metadata['images'][idx],
            'description': metadata['image_descriptions'][idx],
            'similarity': similarity,
            'page': metadata['page_numbers'][idx]
        })

    # 按相似度排序并返回 top_k
    similarities.sort(key=lambda x: x['similarity'], reverse=True)
    return similarities[:top_k]

2. 以图搜图
直接使用图像作为查询输入来搜索视觉上相似的图像。流程与文本搜索类似,区别在于查询嵌入向量来源于输入图像本身。

def search_images_with_image(query_image_path, metadata,
                              multimodal_embedding_model, top_k=3):
    """
    使用图像查询搜索相似图像。

    Args:
        query_image_path: 查询图像的路径
        metadata: 包含图像嵌入向量的文档元数据
        multimodal_embedding_model: 用于生成嵌入向量的模型
        top_k: 返回结果的数量

    Returns:
        list: 最相似的 top_k 个图像
    """
    # 加载查询图像
    query_image = Image.load_from_file(query_image_path)

    # 为查询图像生成嵌入向量
    img_emb_result = multimodal_embedding_model.get_embeddings(
        image=query_image
    )
    query_embedding = img_emb_result.image_embedding

    # 计算与所有图像嵌入向量的相似度
    similarities = []
    for idx, img_emb in enumerate(metadata['image_embeddings']):
        similarity = cosine_similarity(query_embedding, img_emb)
        similarities.append({
            'image': metadata['images'][idx],
            'description': metadata['image_descriptions'][idx],
            'similarity': similarity,
            'page': metadata['page_numbers'][idx]
        })

    # 按相似度排序并返回 top_k
    similarities.sort(key=lambda x: x['similarity'], reverse=True)
    return similarities[:top_k]

应用示例

# 示例1:使用文本查询搜索图像
text_query = "revenue comparison chart between years"
image_results = search_images_with_text(
    text_query,
    doc_metadata,
    multimodal_embedding_model
)
print("Images matching text query:")
for i, result in enumerate(image_results, 1):
    print(f"n{i}. Page {result['page']} | Similarity: {result['similarity']:.4f}")
    print(f"Description: {result['description'][:150]}...")

# 示例2:使用图像查询搜索相似图像
query_image = "sample_chart.png"
similar_images = search_images_with_image(
    query_image,
    doc_metadata,
    multimodal_embedding_model
)
print("nSimilar images found:")
for i, result in enumerate(similar_images, 1):
    print(f"n{i}. Page {result['page']} | Similarity: {result['similarity']:.4f}")

价值体现
这种能力解决了传统检索中的关键痛点。例如,当你在数百页的文档中需要查找与一张“收入趋势图”类似的图表时,无需费力地用文字描述图表特征,只需将该图作为查询输入,系统即可直接返回视觉和语义上最接近的结果,极大提升了信息检索的效率和准确性。

步骤 6:完整的多模态 RAG Pipeline

将前面的步骤串联起来,我们实现一个端到端的多模态 RAG 流程:

def multimodal_rag(query, metadata, gemini_model, text_embedding_model,
                  multimodal_embedding_model, top_k_text=3, top_k_images=2):
    """
    完整的多模态 RAG 流水线。

    处理流程:
    1. 基于查询检索相关的文本片段
    2. 基于查询检索相关的图像
    3. 组合文本和图像上下文
    4. 使用 Gemini 模型结合完整上下文生成回答

    参数:
        query: 用户问题
        metadata: 文档元数据
        gemini_model: 用于生成的 Gemini 模型
        text_embedding_model: 用于文本检索的嵌入模型
        multimodal_embedding_model: 用于图像检索的多模态嵌入模型
        top_k_text: 检索的文本片段数量
        top_k_images: 检索的图像数量

    返回:
        dict: 包含生成答案及检索来源的字典
    """
    # 步骤 1:检索相关文本片段
    text_results = search_text(
        query,
        metadata,
        text_embedding_model,
        top_k=top_k_text
    )

    # 步骤 2:检索相关图像
    image_results = search_images_with_text(
        query,
        metadata,
        multimodal_embedding_model,
        top_k=top_k_images
    )

    # 步骤 3:从检索结果构建上下文
    text_context = "nn".join([
        f"Text Chunk {i+1}:n{result['text']}"
        for i, result in enumerate(text_results)
    ])

    image_context = "nn".join([
        f"Image {i+1} (Page {result['page']}):n{result['description']}"
        for i, result in enumerate(image_results)
    ])

    # 步骤 4:构建包含多模态上下文的提示词
    prompt = f"""You are an AI assistant analyzing a document.
    Use the following text and image information to answer the user's question accurately.

    RETRIEVED TEXT CONTENT:
    {text_context}

    RETRIEVED IMAGE DESCRIPTIONS:
    {image_context}

    USER QUESTION:
    {query}

    Provide a comprehensive answer based on both the text and image information above.
    If the images contain relevant data (charts, tables, graphs), reference them specifically.
    Be factual and cite which sources you're drawing from.

    ANSWER:
    """

    # 步骤 5:准备内容部分(文本提示词 + 实际图像)
    content_parts = [prompt]

    # 将实际图像添加到提示词中,供 Gemini 模型“查看”
    for result in image_results:
        content_parts.append(result['image'])

    # 步骤 6:使用 Gemini 模型生成回答
    response = gemini_model.generate_content(content_parts)

    return {
        'answer': response.text,
        'text_sources': text_results,
        'image_sources': image_results
    }


# 示例用法
user_query = "How did our Class A shares perform compared to the S&P 500 in 2023?"
result = multimodal_rag(
    query=user_query,
    metadata=doc_metadata,
    gemini_model=gemini_model,
    text_embedding_model=text_embedding_model,
    multimodal_embedding_model=multimodal_embedding_model
)
print("QUESTION:", user_query)
print("nANSWER:", result['answer'])
print("n" + "="*80)
print("SOURCES USED:")
print(f"nText chunks: {len(result['text_sources'])}")
print(f"Images: {len(result['image_sources'])}")

示例输出:

QUESTION: How did our Class A shares perform compared to the S&P 500 in 2023?
ANSWER: Based on the performance chart (Image 1, Page 7) and the accompanying
text analysis, Class A Google shares outperformed the S&P 500 index in 2023.
Specifically, Class A shares showed a 58% gain compared to the S&P 500's 24%
increase over the same period. The chart shows a clear divergence starting in Q2
2023, with Google shares maintaining a steady upward trajectory while the broader
market experienced more volatility. This outperformance was primarily driven by
strong cloud revenue growth and AI product launches mentioned in Text Chunk 2.
================================================================================
SOURCES USED:
Text chunks: 3
Images: 2

路径二:RAG Anything 框架

RAG Anything 框架旨在统一处理流程,不再依赖多个独立的工具。它在一个集成的系统中处理所有内容类型——包括文本、图像、表格和公式,使它们遵循同一套检索与生成流程。

传统 RAG 流水线在处理图像、图表等“非文本元素”时常常存在短板。RAG Anything 从设计之初就支持真正的多模态检索,从而规避了这些局限性。

用户可以在混合了文本、图示、表格、数学公式的文档中进行跨模态搜索。以下是其核心实现方式:

import asyncio
from raganything import RAGAnything, RAGAnythingConfig
from lightrag.llm.openai import openai_complete_if_cache, openai_embed
from lightrag.utils import EmbeddingFunc

async def main():
    # 1. 配置API
    api_key = "your-api-key"
    base_url = "your-base-url" # 可选

    # 2. 创建RAGAnything配置,启用多模态处理
    config = RAGAnythingConfig(
        working_dir="./rag_storage",
        parser="mineru",  # 解析器选择:mineru 或 docling
        parse_method="auto",  # 解析方法:auto, ocr, 或 txt
        enable_image_processing=True,
        enable_table_processing=True,
        enable_equation_processing=True,
    )

    # 3. 定义文本LLM模型函数
    def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
        return openai_complete_if_cache(
            "gpt-4o-mini",
            prompt,
            system_prompt=system_prompt,
            history_messages=history_messages,
            api_key=api_key,
            base_url=base_url,
            **kwargs,
        )

    # 4. 定义视觉模型函数,处理图像与多模态输入
    def vision_model_func(
        prompt, system_prompt=None, history_messages=[], image_data=None, messages=None, **kwargs
    ):
        # 处理多模态VLM增强查询(messages格式)
        if messages:
            return openai_complete_if_cache(
                "gpt-4o",
                "",
                system_prompt=None,
                history_messages=[],
                messages=messages,
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            )
        # 处理传统单图像输入
        elif image_data:
            return openai_complete_if_cache(
                "gpt-4o",
                "",
                system_prompt=None,
                history_messages=[],
                messages=[
                    {"role": "system", "content": system_prompt}
                    if system_prompt
                    else None,
                    {
                        "role": "user",
                        "content": [
                            {"type": "text", "text": prompt},
                            {
                                "type": "image_url",
                                "image_url": {
                                    "url": f"data:image/jpeg;base64,{image_data}"
                                },
                            },
                        ],
                    }
                    if image_data
                    else {"role": "user", "content": prompt},
                ],
                api_key=api_key,
                base_url=base_url,
                **kwargs,
            )
        # 纯文本输入回退到文本LLM
        else:
            return llm_model_func(prompt, system_prompt, history_messages, **kwargs)

    # 5. 定义嵌入函数
    embedding_func = EmbeddingFunc(
        embedding_dim=3072,
        max_token_size=8192,
        func=lambda texts: openai_embed(
            texts,
            model="text-embedding-3-large",
            api_key=api_key,
            base_url=base_url,
        ),
    )

    # 6. 初始化RAGAnything系统
    rag = RAGAnything(
        config=config,
        llm_model_func=llm_model_func,
        vision_model_func=vision_model_func,
        embedding_func=embedding_func,
    )

    # 7. 处理文档(例如PDF)
    await rag.process_document_complete(
        file_path="path/to/your/document.pdf",
        output_dir="./output",
        parse_method="auto"
    )

    # 8. 执行查询
    # 纯文本查询 - 用于基础知识库搜索
    text_result = await rag.aquery(
        "图表和表格中展示的主要发现是什么?",
        mode="hybrid"
    )
    print("文本查询结果:", text_result)

    # 多模态查询 - 结合特定模态内容(如公式)
    multimodal_result = await rag.aquery_with_multimodal(
        "解释这个公式及其与文档内容的相关性",
        multimodal_content=[{
            "type": "equation",
            "latex": "P(d|q) = \frac{P(q|d) \cdot P(d)}{P(q)}",
            "equation_caption": "文档相关性概率"
        }],
        mode="hybrid"
    )
    print("多模态查询结果:", multimodal_result)

if __name__ == "__main__":
    asyncio.run(main())

多模态 RAG 的开放性挑战

尽管进展迅速,多模态 RAG 领域仍存在诸多未解决的难题,尤其是在系统投入实际生产环境时。

跨模态对齐: 将文本、图像、视频、音频“串联”起来以回答问题,远比表面看起来复杂。每种模态都有其独特的结构和语义表达方式。例如,将X光片与文字报告对齐,或将播客片段与新闻报道对齐,不能简单地依赖“相似度”计算。即便是最先进的模型,也可能误解或忽略其他模态中蕴含的关键信息。

相关性判定: 检索到“看似相关”的内容只是第一步。判断其是否为“正确的证据”则困难得多。如何过滤掉干扰和误导性片段,确保只保留最有用、最相关的信息,仍然是当前模型容易出错的关键环节。

可解释性: 用户不仅需要可信的答案,还希望了解“为什么选择这张图或这个引用”。当答案来源于多模态信息的融合时,能否将其追溯到具体的原始来源?目前,多模态 RAG 在可解释性方面仍显不足,提升系统透明度是重要的研究方向。

真实场景性能评估: 基准测试能提供一定的评估标准,但真实世界的数据往往“不干净”:输入嘈杂、模态混杂且常常含义模糊。投入生产的多模态系统需要强大的监控框架,以进行持续的性能监测与评估。

参考文献

[1] Mohammad Mahdi Abootorabi, Amirhosein Zobeiri et al., Ask in Any Modality: A Comprehensive Survey on Multimodal Retrieval-Augmented Generation

[2] Yibin Yan and Weidi Xie. 2024. Echosight: Advancing visual-language models with wiki knowledge. In Findings of the Association for Computational Linguistics.

[3] Yongdong Luo, Xiawu Zheng, Xiao Yang, Guilin Li, Haojia Lin, Jinfa Huang, Jiayi Ji, Fei Chao, Jiebo Luo, and Rongrong Ji. 2024b. Video-RAG: Visually aligned retrieval-augmented long video comprehension.

[4] Qi Zhi Lim, Chin Poo Lee, Kian Ming Lim, and Ahmad Kamsani Samingan. 2024. UniRAG: Unification, retrieval, and generation for multimodal question answering with pre-trained language models.

[5] Jaemin Cho, Debanjan Mahata, Ozan Irsoy, Yujie He, and Mohit Bansal. 2024. M3DOCRAG: Multi-modal retrieval is what you need for multi-page multi-document understanding.

[6] Zirui Guo, Xubin Ren, Lingrui Xu, Jiahao Zhang, Chao Huang. RAG-ANYTHING: All in one RAG Framework.


关注“鲸栖”小程序,掌握最新AI资讯

本文由鲸栖原创发布,未经许可,请勿转载。转载请注明出处:http://www.itsolotime.com/archives/13052

(0)
上一篇 5天前
下一篇 5天前

相关推荐

  • AI Agents工具构建指南:从规范定义到高效使用的核心策略

    AI Agent 是由一系列大语言模型(LLM)调用构成的程序。它们接收用户任务,并通过调用“工具”来高效解决问题。工具本质上是 Agent 可以调用的函数。然而,构建一个高效的 Agent 远不止于简单地将一组函数塞入其上下文。关键在于如何精心定义工具,以及如何向 Agent 清晰地传达这些工具的信息。 本文旨在阐述为 AI Agent 构建工具时应关注的…

    2025年11月24日
    300
  • DeepSeek OCR:颠覆传统,用视觉压缩破解AI扩展的“十亿美元级”文档处理难题

    Part I: 文本的“隐形重量” 我们通常认为文本是“轻”的:易于存储、传输和计算。但在大语言模型时代,文本变得非常“重”。 处理一张发票的PDF扫描件,就可能消耗1,000至5,000个tokens。将这个数量级乘以企业日志、法律合同、监管文件和数字化档案,总token量将变得极其庞大——其中大部分是冗余、昂贵且处理缓慢的。虽然OpenAI的GPT-4-…

    2025年10月31日
    400
  • DeepSeek 本地化部署:打造专属智能助手

    本文详细介绍了如何在本地使用Ollama框架部署DeepSeek模型,涵盖硬件要求、安装步骤、界面搭建及注意事项,帮助用户打造安全私密的个人智能助手。

    2025年10月15日
    10600
  • Python开发者必备:12个能解决大问题的小型库

    小工具,大作用。 Python 工具带:12 个能解决大问题的小型库 发现一打容易被忽视的 Python 库,它们安静地让开发更顺滑、更高效、更聪明——一次优雅的 import 就够。 如果你是有经验的 Python 开发者,你的工具箱里可能已经装满了 requests、pandas、flask 和 numpy 这样的“大腕”。但在这些明星库之下,还隐藏着一…

    2025年12月4日
    400
  • PostgreSQL向量检索实战解析:生产级应用还是技术炒作?

    一家电商初创公司的工程团队正面临一个典型的技术选型难题。他们的推荐系统需要实现语义搜索,以匹配用户查询与海量商品描述。团队的核心争议在于:是选择 Qdrant 或 Pinecone 这类专用向量数据库,还是采用 pgvector 扩展,将所有数据保留在 PostgreSQL 中? 这并非个例。随着 AI 驱动的搜索与 RAG(检索增强生成)系统在各行业普及,…

    2025年12月3日
    400

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注