AI Daddy › Retrieval Systems
Multi-Modal RAG · Retrieval Systems
Multi-modal RAG extends retrieval-augmented generation beyond plain text to handle images, tables, charts, audio, and mixed-layout documents. Production…
Multi-Modal RAG
Multi-modal RAG extends retrieval-augmented generation beyond plain text to handle images, tables, charts, audio, and mixed-layout documents. Production systems now routinely ingest PDFs with diagrams, slide decks, scanned invoices, and research papers where the visual layout is the meaning. Three architectures dominate: caption-and-index, unified vision-text embeddings (Cohere Embed v4, Voyage-Multimodal-3.5, Gemini Embedding 001), and page-as-image with late interaction (ColPali, ColQwen2.5, ColNomic).
Table of Contents
Why Text-Only RAG Fails
Traditional RAG pipelines parse documents into text chunks, embed them, and retrieve against a text query. This breaks on real-world documents:
| Document Element | Text-Only RAG Behavior | Actual Information Lost |
|---|
| Bar Chart | Extracts axis labels only | Trends, comparisons, magnitudes |
| Architecture Diagram | Misses entirely | Component relationships, data flow |
| Table | Flattened rows lose structure | Row-column associations, headers |
| Infographic | Captures scattered text fragments | Visual hierarchy, spatial groupings |
| Photo with Caption | Gets caption, loses image | Visual evidence, spatial context |
Reality: Enterprise documents are 40-60% non-textual content. A financial report's value is in its charts. A medical paper's key finding is in its figures. Ignoring visual content means ignoring most of the knowledge.
Architecture Patterns
There are three dominant patterns for multi-modal RAG, each with distinct trade-offs:
Pattern 1: Unified Embedding Space
Shared Vector Space
+-------------------+
Text --> Encoder | [0.2, 0.8, ...] |
Image --> Encoder | [0.3, 0.7, ...] | --> Single Index --> Retrieve
Table --> Encoder | [0.1, 0.9, ...] |
+-------------------+
Query "show revenue trends" --> encode --> nearest neighbors across ALL modalities
- How: Use a model like CLIP or SigLIP to project text and images into the same vector space.
- Pros: Single index, single query, simple retrieval logic.
- Cons: Embedding quality varies across modalities; tables need serialization.
Pattern 2: Modality-Specific Retrieval with Fusion
Query --> +----> Text Index --> Top-K text chunks
|
+----> Image Index --> Top-K images
|
+----> Table Index --> Top-K tables
|
v
Fusion / Reranking Layer --> Combined Top-K --> VLM Generator
- How: Separate embeddings and indices per modality. A reranker or reciprocal rank fusion (RRF) merges results.
- Pros: Best-in-class embeddings per modality; can tune each retriever independently.
- Cons: More infra complexity; fusion logic is non-trivial.
Pattern 3: Vision-First (Page-as-Image)
Document Page --> Screenshot/Render --> Vision Encoder --> Multi-vector Index
|
Query ---------> Text Encoder --------------+---> Late Interaction Score
--> Retrieve top pages
- How: Treat every document page as an image. Use a vision-language model (e.g., ColPali) to create patch-level embeddings. Score via late interaction (MaxSim).
- Pros: No OCR, no layout parsing, no table extraction pipeline. End-to-end trainable.
- Cons: Higher compute at indexing; loses fine-grained text search.
Recommendation: Pattern 3 (vision-first) is gaining ground fast for document-heavy use cases. Pattern 2 remains the production workhorse when you need precise text search alongside visual retrieval.
Multi-Modal Embedding Strategies
CLIP (Contrastive Language-Image Pretraining)
The original dual-encoder that maps text and images to a shared 512/768-dim space.
- Strengths: Huge ecosystem, well-understood, many fine-tuned variants.
- Weaknesses: Weaker on document-style images (charts, tables) vs. natural photos. Contrastive loss requires large batch sizes.
SigLIP / SigLIP 2
Replaces CLIP's softmax cross-entropy with a sigmoid loss, allowing each image-text pair to be evaluated independently.
- SigLIP 2 (2025): Adds captioning decoders, self-distillation, and masked prediction. Trained on 10B+ images across 109 languages.
- Key Win: Outperforms CLIP at small batch sizes (4-8k) and provides denser, more robust features.
- Production Use: National Library of Norway, e-commerce visual search, AI art curation.
Comparison for RAG
| Model | Best For | Embedding Dim | Document Quality | Natural Image Quality |
|---|
| CLIP ViT-L/14 | General purpose | 768 | Medium | High |
| SigLIP 2 So400m | Multi-lingual docs | 1152 | High | High |
| Nomic Embed Vision | Text-heavy docs | 768 | High | Medium |
| Voyage Multimodal 3 | Mixed documents | 1024 | High | High |
Embedding Strategy Decision
Is your content mostly natural images (photos, products)?
YES --> CLIP or SigLIP fine-tuned on your domain
NO
|
v
Is your content document pages (PDFs, slides, reports)?
YES --> ColPali / ColQwen (vision-first, no OCR needed)
NO
|
v
Is it a mix of text, images, and structured data?
YES --> Modality-specific encoders + fusion (Pattern 2)
Vision-Language Models for Document Understanding
VLMs serve two roles in multi-modal RAG: (1) as the generator that synthesizes answers from retrieved multi-modal context, and (2) as the indexing engine that extracts structured information at ingestion time.
VLM Capabilities Comparison
| Capability | Claude Opus 4.7 / Sonnet 4.6 | GPT-5.5 | Gemini 3.1 Pro |
|---|
| Chart Reading | Excellent | Excellent | Excellent |
| Table Extraction | Excellent | Good | Excellent |
| Diagram Understanding | Excellent | Good | Excellent |
| Handwriting OCR | Good | Good | Good |
| Multi-page Reasoning | Excellent (1M ctx on Sonnet 4.6) | Excellent (1M ctx) | Excellent (1M ctx) |
| Structured Output | Native JSON mode | Native JSON mode | Native JSON mode |
VLM-Augmented Ingestion Pipeline
Raw PDF
|
v
Page Renderer (pdf2image, 300 DPI)
|
v
VLM Extraction Pass:
+-- "Extract all tables as markdown"
+-- "Describe this chart: axes, trends, key data points"
+-- "Summarize the diagram: components and relationships"
|
v
Structured Output (JSON)
|
+---> Text chunks --> Text embedding index
+---> Table markdown --> Text embedding index (with metadata: "type=table")
+---> Chart summaries --> Text embedding index (with metadata: "type=chart")
+---> Page images --> Image embedding index (CLIP/SigLIP)
This "describe-then-embed" approach converts visual content into searchable text while preserving the original image for the generation step.
ColPali and Vision-Based Retrieval
ColPali represents a paradigm shift: instead of building complex OCR + layout + table extraction pipelines, treat each document page as a single image and let a vision-language model handle everything.
How ColPali Works
Document Page Image
|
v
SigLIP Vision Encoder (So400m)
|
Splits image into patches (e.g., 32x32 grid = 1024 patches)
|
v
Gemma 2B Language Model (contextualizes patch embeddings)
|
v
Linear Projection --> 128-dim patch embeddings
|
Result: 1024 vectors of dim 128 per page
|
v
Stored in Multi-Vector Index
At query time:
Query --> Tokenize --> Embed --> 128-dim token embeddings
|
v
Late Interaction (MaxSim):
Score = Sum over query tokens of Max similarity to any patch
ColPali vs. Traditional Pipeline
| Aspect | Traditional Pipeline | ColPali |
|---|
| OCR | Required (Tesseract, Azure OCR) | Not needed |
| Layout Detection | Required (Detectron2, LayoutLM) | Not needed |
| Table Parser | Required (Camelot, Tabula) | Not needed |
| Chart Extractor | Required (ChartOCR) | Not needed |
| Indexing Speed | Slow (multi-stage) | Fast (single forward pass) |
| Retrieval Quality | High on text, poor on visuals | High across all modalities |
| Storage | Text index (~small) | Multi-vector index (~larger) |
ColPali Family
- ColPali (v1): PaliGemma-3B backbone. The original.
- ColQwen 2.5: Qwen2-VL backbone. Better multilingual support, improved on Asian-language documents.
- ColSmol: Smaller variant for edge deployment. ~1B parameters.
ViDoRe Benchmark Results
ColPali excels on visually complex benchmarks like InfographicVQA, ArxivQA, and TabFQuAD, which test infographics, figures, and tables respectively. It outperforms traditional text-based pipelines even on text-centric documents.
Table Extraction and Structured Data Retrieval
Tables are the hardest modality for traditional RAG. Flattening a table row-by-row destroys the column-header relationships that give each cell meaning.
# Pseudocode: Extract tables using a VLM
def extract_tables_from_page(page_image: bytes) -> list[dict]:
prompt = """
Extract ALL tables from this document page.
For each table, return:
{
"title": "table title or caption",
"headers": ["col1", "col2", ...],
"rows": [["val1", "val2", ...], ...],
"markdown": "| col1 | col2 |\\n|---|---|\\n| val1 | val2 |"
}
Return JSON array. If no tables, return [].
"""
response = vlm.generate(image=page_image, prompt=prompt)
return json.loads(response)
Strategy 2: Specialized Table Parsers
- Tabula / Camelot: Rule-based PDF table extraction. Fast but brittle on complex layouts.
- Table Transformer (DETR-based): Detects table boundaries and cell structure from images.
- Unstructured.io: Combines heuristics with ML models for layout-aware parsing.
Strategy 3: Table-Aware Chunking
Original Table (20 rows x 8 columns)
|
v
Chunk as complete unit (do NOT split tables across chunks)
|
v
Embed the full markdown table as a single chunk
|
v
Add metadata: {"type": "table", "page": 14, "caption": "Q3 Revenue by Region"}
|
v
At generation time: pass the FULL table to the LLM, not a fragment
Key Principle: Tables must be atomic retrieval units. Never split a table across chunk boundaries.
Chart and Diagram Understanding
Chart Types and Extraction Approaches
| Chart Type | What to Extract | Best Approach |
|---|
| Bar/Line/Pie | Data values, trends, comparisons | VLM description + data table extraction |
| Flow Diagram | Steps, decisions, connections | VLM structured extraction (nodes + edges) |
| Architecture Diagram | Components, relationships, data flow | VLM description + entity extraction |
| Scatter Plot | Correlations, outliers, clusters | VLM trend description + raw data if available |
| Gantt Chart | Timeline, dependencies, milestones | VLM structured extraction |
Dual-Representation Strategy
For each chart or diagram, store TWO representations:
Chart Image
|
+---> (1) Text Description (for text-based retrieval)
| "This bar chart shows Q3 revenue by region.
| North America: $4.2M, Europe: $3.1M, APAC: $2.8M.
| NA grew 15% QoQ while APAC declined 3%."
|
+---> (2) Original Image (for visual retrieval + generation context)
Stored with CLIP/SigLIP embedding for image-based queries
This ensures the chart is retrievable by both text queries ("what was APAC revenue?") and visual queries ("show me the revenue chart").
Production Architecture
Full Multi-Modal RAG Pipeline
INGESTION:
Raw Docs --> Doc Classifier --+--> Text-Heavy --> chunking + text embeddings
+--> Visual-Heavy --> page render + ColPali
+--> Mixed --> VLM extraction + hybrid
|
v
[Text Index] [Image Index] [Table Index]
RETRIEVAL:
Query --> Query Analyzer --+--> Text: BM25 + dense search
+--> Image: CLIP/ColPali search
+--> Table: metadata-filtered dense
|
v
Cross-Modal Reranker --> Context Assembly --> VLM --> Response
Scaling Considerations
| Concern | Solution |
|---|
| Index Size | ColPali stores ~1024 vectors/page. For 1M pages = ~1B vectors. Use quantization (binary, PQ). |
| Ingestion Latency | VLM extraction is slow (~2-5s/page). Use async workers with GPU acceleration. |
| Query Latency | Multi-index fan-out adds latency. Use parallel retrieval + aggressive top-k pruning. |
| Cost | VLM calls at ingestion are one-time. Amortize over query volume. Budget $0.01-0.05/page for extraction. |
| Storage | Store page images in object storage (S3). Store embeddings in vector DB. Store text in search index. |
Implementation Example
End-to-End Multi-Modal RAG with ColPali + VLM
# Pseudocode: Production multi-modal RAG pipeline
from colpali_engine import ColPali, ColPaliProcessor
from qdrant_client import QdrantClient
import anthropic
# --- INDEXING ---
def index_document(pdf_path: str, collection: str):
"""Index a PDF document using ColPali for visual retrieval
and VLM extraction for text-based retrieval."""
pages = render_pdf_to_images(pdf_path, dpi=300)
colpali_model = ColPali.from_pretrained("vidore/colpali-v1.3")
processor = ColPaliProcessor.from_pretrained("vidore/colpali-v1.3")
vlm_client = anthropic.Anthropic()
for page_num, page_image in enumerate(pages):
# 1. Generate ColPali multi-vector embeddings
inputs = processor(images=[page_image])
patch_embeddings = colpali_model(**inputs) # shape: [1, 1024, 128]
# 2. Extract structured content via VLM
extraction = vlm_client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=4096,
messages=[{
"role": "user",
"content": [
{"type": "image", "source": encode_image(page_image)},
{"type": "text", "text": """Extract from this page:
1. All text content (preserve structure)
2. Tables as markdown
3. Chart descriptions with data points
Return as JSON with keys: text, tables, charts"""}
]
}]
)
structured = json.loads(extraction.content[0].text)
# 3. Store in vector DB
qdrant.upsert(collection, points=[
# ColPali multi-vector for visual retrieval
PointStruct(
id=f"{pdf_path}:page:{page_num}:colpali",
vector={"colpali": patch_embeddings[0].tolist()},
payload={
"source": pdf_path,
"page": page_num,
"type": "page_image",
"text_preview": structured["text"][:500]
}
),
# Text embeddings for each extracted element
*create_text_chunks(structured, pdf_path, page_num)
])
# --- RETRIEVAL ---
def retrieve(query: str, collection: str, top_k: int = 5):
"""Hybrid retrieval: ColPali visual + text semantic search."""
# Visual retrieval via ColPali
query_inputs = processor(text=[query])
query_embeddings = colpali_model(**query_inputs)
visual_results = qdrant.query(
collection,
query_vector=("colpali", query_embeddings[0].tolist()),
limit=top_k,
query_filter=Filter(must=[FieldCondition(key="type", match="page_image")])
)
# Text retrieval via dense embeddings
text_embedding = text_encoder.encode(query)
text_results = qdrant.search(
collection,
query_vector=("text", text_embedding.tolist()),
limit=top_k
)
# Fuse results using reciprocal rank fusion
fused = reciprocal_rank_fusion(visual_results, text_results, k=60)
return fused[:top_k]
# --- GENERATION ---
def generate_answer(query: str, retrieved_context: list) -> str:
"""Generate answer using VLM with multi-modal context."""
content_blocks = [{"type": "text", "text": f"Question: {query}\n\nContext:"}]
for ctx in retrieved_context:
if ctx.payload["type"] == "page_image":
# Include the actual page image
content_blocks.append({
"type": "image",
"source": load_page_image(ctx.payload["source"], ctx.payload["page"])
})
else:
# Include text/table content
content_blocks.append({
"type": "text",
"text": f"[{ctx.payload['type']}] {ctx.payload['content']}"
})
content_blocks.append({
"type": "text",
"text": "Answer the question using ONLY the provided context. Cite sources."
})
response = vlm_client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=2048,
messages=[{"role": "user", "content": content_blocks}]
)
return response.content[0].text
System Design Interview Angle
Q: Design a RAG system for a financial research platform that needs to answer questions about earnings reports containing text, tables, and charts.
Strong answer:
The core challenge is that 60%+ of the information in earnings reports lives in tables and charts, not prose. A text-only RAG pipeline would miss revenue breakdowns, trend lines, and comparative data.
Architecture: I would use a hybrid approach (Pattern 2 + elements of Pattern 3):
-
Ingestion: Render each PDF page at 300 DPI. Run a VLM extraction pass to convert tables to markdown and charts to structured descriptions. Simultaneously generate ColPali multi-vector embeddings for each page image.
-
Storage: Three indices -- (a) text chunks with dense embeddings (financial text), (b) table markdown with dense embeddings plus metadata filters for table type, (c) ColPali multi-vector index for page-level visual retrieval.
-
Retrieval: Query analyzer classifies the query type. "What was Q3 revenue?" triggers text + table search. "Show me the revenue trend" triggers visual (ColPali) search. Results are fused via RRF and reranked by a cross-encoder.
-
Generation: A VLM (Claude or Gemini) receives the fused context -- text chunks, table markdown, and relevant page images. It generates a grounded answer with citations to specific pages and tables.
Key trade-offs: ColPali gives excellent recall on visual content but stores ~1024 vectors per page, so for 100k documents (500k pages), that is ~500M vectors. I would use binary quantization to reduce storage by 32x, accepting a small recall hit. For the text path, BM25 + dense hybrid search handles financial terminology well.
Q: How would you handle a query that requires information from BOTH a chart and a table on different pages?
Strong answer:
This is the cross-modal, cross-page retrieval problem. The solution has three parts:
-
Retrieval diversity: Ensure the retriever returns results from multiple modalities. Set minimum quotas -- at least 2 text results, 2 table results, and 1 visual result in every retrieval set, regardless of which modality scores highest.
-
Context assembly: When assembling the VLM prompt, include all retrieved content with explicit provenance: "[Table from page 14: Q3 Revenue by Region]" and "[Chart from page 22: Revenue Trend 2024-2026]". The VLM can then reason across both.
-
Agentic fallback: If the initial retrieval does not surface enough cross-modal context, an agentic layer can issue follow-up retrievals: "The table shows revenue numbers but the user asked about trends -- let me also search for charts related to revenue."
The key insight is that cross-modal questions are inherently multi-hop. The system needs to retrieve from one modality, recognize the gap, and retrieve from another.
References
- Faysse et al. "ColPali: Efficient Document Retrieval with Vision Language Models" (ICLR 2025)
- Google. "SigLIP 2: Multilingual Vision-Language Encoders" (2025)
- NVIDIA. "An Easy Introduction to Multimodal Retrieval-Augmented Generation" (2025)
- HKUDS. "RAG-Anything: All-in-One Multimodal RAG Framework" (2025)
- Vespa Blog. "PDF Retrieval with Vision Language Models" (2024)
Previous: Advanced Retrieval Patterns | Next: RAG Evaluation Patterns