为什么需要多模态 RAG

传统 RAG 只处理文本,但现实世界的信息以多种模态存在:技术文档包含图表、医疗记录附带影像、电商页面图文并茂。2026 年的多模态 RAG 已经成为企业级 RAG 系统的标配。

核心架构

多模态 RAG 的关键挑战是:如何让文本 Query 检索到相关图片,以及如何让图片 Query 检索到相关文本。

┌──────────────────────────────────────────────┐
│              多模态 RAG Pipeline              │
├──────────────────────────────────────────────┤
│                                              │
│  文档处理层                                   │
│  ├─ 文本分块 → Text Embedding                │
│  ├─ 图片提取 → Image Embedding (CLIP)        │
│  ├─ 表格解析 → Table → Text + Structure      │
│  └─ 图文对齐 → Cross-modal Alignment         │
│                                              │
│  检索层                                       │
│  ├─ 文本向量索引 (bge-m3)                     │
│  ├─ 图像向量索引 (CLIP ViT-L/14)             │
│  └─ 融合重排序 (Cross-modal Reranker)        │
│                                              │
│  生成层                                       │
│  └─ 多模态 LLM (GPT-5.5 / Claude 4)          │
│                                              │
└──────────────────────────────────────────────┘

工程实现

1. 文档解析与多模态提取

from unstructured import partition_pdf
from PIL import Image
import torch
from transformers import CLIPModel, CLIPProcessor

class MultimodalDocProcessor:
    def __init__(self):
        self.clip_model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
        self.clip_processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")
        self.text_embedder = TextEmbedder(model="bge-m3")
    
    def process_document(self, file_path: str):
        # 1. 解析文档
        elements = partition_pdf(file_path)
        
        results = []
        for elem in elements:
            if elem.category == "Text":
                results.append({
                    "type": "text",
                    "content": elem.text,
                    "embedding": self.text_embedder.encode(elem.text),
                    "metadata": {"page": elem.metadata.page_number}
                })
            elif elem.category == "Image":
                # 提取图片并生成 embedding
                img = elem.image
                img_embedding = self._encode_image(img)
                
                # 同时用 VLM 生成图片描述文本
                img_description = vlm.describe(img)
                text_embedding = self.text_embedder.encode(img_description)
                
                results.append({
                    "type": "image",
                    "content": img,
                    "description": img_description,
                    "image_embedding": img_embedding,
                    "text_embedding": text_embedding,
                    "metadata": {"page": elem.metadata.page_number}
                })
            elif elem.category == "Table":
                # 表格转为结构化文本
                table_text = self._table_to_text(elem)
                results.append({
                    "type": "table",
                    "content": table_text,
                    "embedding": self.text_embedder.encode(table_text),
                    "metadata": {"page": elem.metadata.page_number}
                })
        
        return results
    
    def _encode_image(self, image):
        inputs = self.clip_processor(images=image, return_tensors="pt")
        with torch.no_grad():
            features = self.clip_model.get_image_features(**inputs)
        return features[0].cpu().numpy()

2. 双索引构建

class MultimodalIndex:
    def __init__(self):
        self.text_index = MilvusIndex(
            dim=1024,  # bge-m3
            metric="IP"
        )
        self.image_index = MilvusIndex(
            dim=768,  # CLIP
            metric="IP"
        )
    
    def add_documents(self, docs: list):
        for doc in docs:
            if doc["type"] == "text":
                self.text_index.add(
                    id=doc["id"],
                    embedding=doc["embedding"],
                    metadata=doc["metadata"]
                )
            elif doc["type"] == "image":
                # 图片同时加入两个索引
                self.image_index.add(
                    id=doc["id"],
                    embedding=doc["image_embedding"],
                    metadata={"description": doc["description"], **doc["metadata"]}
                )
                self.text_index.add(
                    id=f"{doc['id']}_text",
                    embedding=doc["text_embedding"],
                    metadata={"ref_image_id": doc["id"], **doc["metadata"]}
                )

3. 混合检索与跨模态重排序

class MultimodalRetriever:
    def __init__(self, index: MultimodalIndex, reranker):
        self.index = index
        self.reranker = reranker
    
    def retrieve(self, query: str, top_k: int = 10):
        # 1. 文本检索
        text_hits = self.index.text_index.search(
            embedding=self.text_embedder.encode(query),
            top_k=top_k * 2
        )
        
        # 2. 用 CLIP 做跨模态检索(文本→图像)
        clip_query_emb = self.clip_encode_text(query)
        image_hits = self.index.image_index.search(
            embedding=clip_query_emb,
            top_k=top_k * 2
        )
        
        # 3. 合并候选
        candidates = self._merge(text_hits, image_hits)
        
        # 4. 跨模态重排序
        reranked = self.reranker.rerank(
            query=query,
            candidates=candidates,
            top_k=top_k
        )
        
        return reranked

图文混合检索效果对比

方法文本 Recall@5图像 Recall@5MRR
纯文本 RAG0.820.000.71
纯图像 RAG0.000.680.55
简单双路合并0.800.650.73
双索引+重排序0.880.790.82
双索引+VLM描述增强0.910.850.88

实际案例:技术文档智能问答

以一份 200 页的技术白皮书为例,包含大量架构图和流程图:

# 构建索引
processor = MultimodalDocProcessor()
docs = processor.process_document("whitepaper.pdf")

index = MultimodalIndex()
index.add_documents(docs)

# 查询
retriever = MultimodalRetriever(index, CrossModalReranker())

# 用户问:"系统架构中数据流向是怎样的?"
results = retriever.retrieve("系统架构中数据流向是怎样的?", top_k=5)

# 返回结果包含文本描述 + 架构图 + 相关表格
for r in results:
    print(f"[{r['type']}] Score: {r['score']:.3f}")
    if r['type'] == 'image':
        print(f"  图片描述: {r['metadata']['description']}")
        display(r['content'])  # 显示图片
    else:
        print(f"  内容: {r['content'][:200]}")

踩坑经验

坑1:图片描述质量决定检索效果

用弱模型生成的图片描述会严重拖低检索质量。建议用 GPT-5.5 或 Claude 4 级别的 VLM 生成详细描述。

坑2:表格不能当纯文本处理

表格需要保留结构信息,推荐转为 Markdown 或 HTML 格式,同时生成自然语言摘要。

坑3:CLIP 在中文场景的效果

原版 CLIP 对中文支持较弱,建议使用 Chinese-CLIP 或 CN-CLIP,在中文图文检索上 Recall 提升约 15%。

总结

多模态 RAG 在 2026 年已经成熟,核心技术栈包括:多模态文档解析、双索引架构、跨模态重排序。关键原则是:图片不只靠图像向量检索,还要靠 VLM 生成的文本描述做文本检索,双路并行才能覆盖各种 Query 模式

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。