RAG multimodale: guida pratica per imparare dai documenti
Impara a costruire una pipeline RAG multimodale per un sistema di Q&A visivo sulle istruzioni di montaggio IKEA usando l'API OpenAI e il database vettoriale ChromaDB.
Immagina di montare un mobile IKEA. Guardi il passaggio 14 delle istruzioni, ma non capisci quale vite va dove. Il diagramma mostra qualcosa, ma non ti è chiaro.
Se chiedessi: "A cosa serve il pezzetto di metallo nel passaggio 14?", un sistema RAG standard basato solo su testo qui non può aiutarti. La risposta è nel diagramma stesso, non nel testo sulla pagina. Le istruzioni IKEA sono famosamente senza parole.
Qui entra in gioco il RAG multimodale.
In questo tutorial ti mostrerò come costruire un sistema RAG multimodale che sappia rispondere a domande su documenti con immagini, diagrammi e contenuti misti. Useremo le istruzioni di montaggio IKEA come caso di test perché sono una buona prova di stress: perlopiù visuali, testo minimo e il classico esempio in cui serve davvero aiuto.
Useremo i modelli di OpenAI: GPT-4o per comprendere le immagini e generare risposte, e text-embedding-3-small per creare vettori ricercabili. Se vuoi imparare a costruire pipeline RAG con un framework diverso, ti consiglio il nostro corso RAG con LangChain.
Alla fine avrai un sistema funzionante che puoi eseguire in locale e adattare ai tuoi documenti.
Cos'è il RAG multimodale?
Il RAG multimodale estende il pattern standard RAG per gestire più del semplice testo. L'idea di base resta la stessa, ma il "contesto" ora include immagini, tabelle, diagrammi e altri contenuti non testuali.
Perché è importante? Perché i documenti reali non sono solo testo.
Pensa a ciò con cui lavori davvero:
Manuali tecnici con schemi elettrici
Articoli scientifici con grafici sperimentali
Report finanziari con grafici dei ricavi
Specifiche di prodotto con immagini dei componenti
Un sistema che elabora solo testo lavora con informazioni incomplete. È come provare a capire una ricetta leggendo solo la lista degli ingredienti e ignorando le foto che mostrano cosa significa "mescolare delicatamente".
Il RAG tradizionale tratta un PDF come un flusso di caratteri. Estrae il testo, lo suddivide, ne crea gli embedding e finisce lì. Le immagini vengono saltate del tutto o magari convertite in segnaposto [IMAGE]. Le tabelle diventano righe di testo sconnesse che perdono la loro struttura.
Il RAG multimodale adotta un approccio diverso: le immagini vengono descritte o incorporate direttamente, e le tabelle vengono convertite in formati strutturati. Il sistema di retrieval può collegare una query come "diagramma di topologia di rete" sia alle descrizioni testuali dell'architettura di rete sia ai diagrammi visivi effettivi. Il modello di generazione può esaminare un'immagine e ragionare su ciò che rappresenta.
Se "multimodale" ti sembra ancora un termine ombrello un po' vago, questo blog su IA multimodale è una spiegazione sintetica con esempi concreti.
Come funziona il RAG multimodale?
La pipeline ha più componenti rispetto al RAG solo testo, ma la struttura è simile. I dati vengono elaborati e incorporati, finiscono in uno store vettoriale e vengono recuperati quando qualcuno fa una domanda.
Ecco il flusso:
Ingestione e parsing dei documenti
I documenti grezzi (PDF, immagini, slide) vengono scomposti nelle loro parti. Estrai il testo, identifichi le immagini, analizzi le tabelle e capisci come tutto è correlato nello spazio. Un diagramma a pagina 5 potrebbe avere una didascalia sotto e un riferimento nel testo a pagina 4. Devi catturare queste relazioni.
Preprocessing dei contenuti
Tipi diversi di contenuto richiedono trattamenti diversi. Per le immagini, hai due opzioni principali: generare descrizioni testuali usando un modello di visione (come GPT-4o), oppure creare embedding delle immagini direttamente con un encoder multimodale (come CLIP). L'approccio delle descrizioni è più semplice e funziona con embedding testuali standard. Le tabelle vengono convertite in formati strutturati o riassunte in linguaggio naturale.
Dopo questo passaggio, tutto viene convertito in vettori. Se usi l'approccio delle descrizioni, ti basta incorporare tutto il testo (testo originale più descrizioni generate delle immagini) con un modello di embedding testuale standard. La chiave è che ora le query su contenuti visivi possono corrispondere a quelle descrizioni.
Indicizzazione e storage
I vettori vengono archiviati in un database con metadati, inclusi documento sorgente, numero di pagina, tipo di contenuto e, per le immagini, il percorso al file originale. Ti servirà quel percorso più avanti quando vorrai mostrarlo al modello di generazione.
Retrieval
Quando arriva una query, creane l'embedding con lo stesso modello, cerca i vicini più prossimi e restituisci i migliori risultati. I chunk recuperati possono fare riferimento a testo, immagini o entrambi.
Generazione
Passa la query, insieme al contesto recuperato, a un modello visione-linguaggio. Se i chunk recuperati fanno riferimento a immagini, carica quelle immagini e includile nel prompt. Il modello vede sia il contesto testuale sia le immagini reali e poi genera una risposta.
La fase di generazione è dove GPT-4o brilla davvero. Può guardare un diagramma IKEA che mostra la posizione delle viti e spiegare in linguaggio semplice cosa succede. Può leggere un grafico e riassumere l'andamento. Può notare dettagli in disegni tecnici che una descrizione testuale potrebbe tralasciare.
Implementazione del RAG multimodale
Costruiamolo. Creeremo un sistema che sappia rispondere a domande sulle istruzioni di montaggio IKEA, passo dopo passo dall'impostazione a un'interfaccia di query funzionante.
Step 0: Scelta di framework e strumenti
Per questo tutorial, restiamo sul semplice:
Componente
Scelta
Perché
Embeddings
text-embedding-3-small
Economico, buona qualità, stessa API
Vector store
ChromaDB
Gira in locale, nessun setup, persistente
Modello di visione
GPT-4o
Il migliore per la comprensione delle immagini
Generazione
GPT-4o
Stesso modello, gestisce entrambi i compiti
Puoi sostituire con alternative. Pinecone o Weaviate per lo store vettoriale se ti serve scalabilità. Claude o Gemini sono buone alternative per la generazione. L'architettura generale resta la stessa.
Perché non usare CLIP o ColPali per gli embedding? Puoi farlo. Permettono di incorporare direttamente le immagini senza generare prima descrizioni testuali. Ma aggiungono complessità e spesso richiedono una GPU. L'approccio delle descrizioni funziona bene per contenuti in stile documentale, dove vuoi catturare dettagli semantici, non solo somiglianza visiva.
Step 1: Configurazione dell'ambiente
Iniziamo con dipendenze e variabili d'ambiente per impostare il progetto.
Installazione delle dipendenze
Per prima cosa, ti serve poppler installato per gestire l'elaborazione dei PDF.
Poi installa i pacchetti Python. Oltre a ChromaDB, ci servono pdf2image per convertire i PDF in immagini, pillow per l'elaborazione delle immagini e requests per scaricare i file PDF.
pip install openai chromadb pdf2image pillow requestsOfferto da
Impostazione della chiave API e dei file
Per usare i modelli, imposta la tua chiave API di OpenAI come variabile d'ambiente.
Unix/Linux/macOS:
export OPENAI_API_KEY="your-api-key-here"Offerto da
Ora configuriamo la struttura del progetto e scarichiamo i dati di esempio. Lo script include già gli URL di download, ma se vuoi dare un'occhiata ai PDF prima, ecco i link diretti sia per la scrivania MALM sia per la struttura letto MALM.
Crea un file chiamato step1_setup.py:
"""
Step 1: Setup and Download Sample Data
"""import os
import requests
# Create directoriesfor d in["data","data/images","data/cache"]:
os.makedirs(d, exist_ok=True)print("✓ Created directories")# Download IKEA assembly instructions
SAMPLE_PDFS ={"malm_desk.pdf":"https://www.ikea.com/us/en/assembly_instructions/malm-desk-white__AA-516949-7-2.pdf","malm_bed.pdf":"https://www.ikea.com/us/en/assembly_instructions/malm-bed-frame-low__AA-75286-15_pub.pdf",}for filename, url in SAMPLE_PDFS.items():
filepath =f"data/{filename}"ifnot os.path.exists(filepath):print(f"Downloading {filename}...")
response = requests.get(url, timeout=30)withopen(filepath,"wb")as f:
f.write(response.content)print(f" ✓ Saved ({len(response.content)/1024:.1f} KB)")else:print(f"✓ {filename} already exists")Offerto da
Per completare l'impostazione iniziale, esegui lo script:
Dovresti vedere i PDF IKEA scaricati nella cartella data/.
Step 2: Preprocessing dei documenti
Le istruzioni IKEA sono insidiose. Sono perlopiù diagrammi con testo minimo. Dobbiamo convertire le pagine PDF in immagini, poi usare GPT-4o per descrivere cosa mostra ogni pagina.
Crea step2_preprocess.py:
"""
Step 2: Preprocess Documents
Convert PDFs to images and generate descriptions with GPT-4o
"""import os
import json
import base64
from pdf2image import convert_from_path
from openai import OpenAI
client = OpenAI()
DATA_DIR ="data"
IMAGES_DIR ="data/images"
CACHE_FILE ="data/cache/descriptions.json"defconvert_pdf_to_images(pdf_path):"""Convert a PDF to page images."""
pdf_name = os.path.splitext(os.path.basename(pdf_path))[0]
images = convert_from_path(pdf_path, dpi=150)
image_paths =[]for i, image inenumerate(images):
image_path =f"{IMAGES_DIR}/{pdf_name}_page_{i+1:03d}.png"
image.save(image_path,"PNG")
image_paths.append(image_path)return image_paths
defdescribe_page(image_path):"""Use GPT-4o to describe an instruction page."""withopen(image_path,"rb")as f:
base64_image = base64.b64encode(f.read()).decode("utf-8")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role":"system","content":"""Describe this IKEA assembly instruction page in detail.
Include: step numbers, parts shown, tools needed, actions demonstrated,
quantities (like "2x"), warnings, and part numbers if visible.
This description will help people find this page when they have questions."""},{"role":"user","content":[{"type":"image_url","image_url":{"url":f"data:image/png;base64,{base64_image}"}},{"type":"text","text":"Describe this instruction page."}]}],
max_tokens=500)return response.choices[0].message.content
defmain():# Load cache if exists
cache ={}if os.path.exists(CACHE_FILE):withopen(CACHE_FILE,"r")as f:
cache = json.load(f)
all_pages =[]# Process each PDFfor pdf_file in os.listdir(DATA_DIR):ifnot pdf_file.endswith(".pdf"):continueprint(f"\nProcessing {pdf_file}...")
pdf_path =f"{DATA_DIR}/{pdf_file}"# Convert to imagesprint(" Converting to images...")
image_paths = convert_pdf_to_images(pdf_path)print(f" ✓ {len(image_paths)} pages")# Generate descriptionsprint(" Generating descriptions with GPT-4o...")for i, image_path inenumerate(image_paths):if image_path in cache:
description = cache[image_path]
status ="cached"else:
description = describe_page(image_path)
cache[image_path]= description
# Save cache after each pagewithopen(CACHE_FILE,"w")as f:
json.dump(cache, f, indent=2)
status ="generated"
all_pages.append({"source_pdf": pdf_file,"page_number": i +1,"image_path": image_path,"description": description
})print(f" Page {i+1}/{len(image_paths)} [{status}]")# Save all page datawithopen("data/cache/all_pages.json","w")as f:
json.dump(all_pages, f, indent=2)print(f"\n✓ Processed {len(all_pages)} pages total")if __name__ =="__main__":
main()Offerto da
Ecco cosa fanno le funzioni:
convert_pdf_to_images(pdf_path): rende ogni pagina del PDF come immagine PNG a 150 DPI usando pdf2image. Questo estrae i diagrammi privi di testo selezionabile, salvandoli con nomi coerenti come malm_desk_page_001.png.
describe_page(image_path): codifica l'immagine in base64 e la invia a GPT-4o con un prompt di sistema mirato. Il modello restituisce una descrizione testuale dettagliata che cattura passaggi, parti e quantità per la ricerca semantica.
main(): orchestra il tutto: carica descrizioni in cache per evitare rielaborazioni, converte i PDF in immagini, genera nuove descrizioni quando serve, salva la cache incrementale e produce all_pages.json con metadati per l'indicizzazione.
Questo passaggio richiede alcuni minuti. GPT-4o analizza ogni pagina e genera una descrizione dettagliata. Le descrizioni vengono messe in cache, quindi se lo riesegui, salta le pagine già elaborate.
Cosa sta succedendo qui? Convertiamo il contenuto visivo in testo ricercabile. Quando qualcuno chiede, "Come attacco le gambe?", il retriever cercherà in queste descrizioni e troverà le pagine che menzionano il fissaggio delle gambe, anche se la pagina originale non ha alcun testo.
Una volta costruita la pipeline di base, i prossimi miglioramenti vengono da tecniche di retrieval, come ricerca ibrida, reranking o riscrittura della query. Per familiarizzare, leggi questo blog sulle Tecniche RAG avanzate.
Step 3: Archiviazione e indicizzazione dei dati multimodali
Ora creiamo gli embedding delle descrizioni e li memorizziamo in ChromaDB.
Crea step3_index.py:
"""
Step 3: Create Embeddings and Index
"""import os
import json
from openai import OpenAI
import chromadb
client = OpenAI()
PAGES_FILE ="data/cache/all_pages.json"
CHROMA_DIR ="data/chroma_db"
COLLECTION_NAME ="ikea_instructions"defget_embedding(text):"""Get embedding from OpenAI."""
response = client.embeddings.create(
model="text-embedding-3-small",input=text
)return response.data[0].embedding
defmain():# Load page descriptionswithopen(PAGES_FILE,"r")as f:
pages = json.load(f)print(f"Loaded {len(pages)} pages")# Initialize ChromaDB with persistent storage
chroma_client = chromadb.PersistentClient(path=CHROMA_DIR)# Delete existing collection if presenttry:
chroma_client.delete_collection(name=COLLECTION_NAME)except:pass
collection = chroma_client.create_collection(
name=COLLECTION_NAME,
metadata={"hnsw:space":"cosine"})print(f"Created collection: {COLLECTION_NAME}")# Index each pageprint("\nCreating embeddings...")for i, page inenumerate(pages):# Combine source info with description
text_to_embed =f"Source: {page['source_pdf']}, Page {page['page_number']}\n\n{page['description']}"
embedding = get_embedding(text_to_embed)
collection.add(
ids=[f"page_{i}"],
embeddings=[embedding],
documents=[page["description"]],
metadatas=[{"source_pdf": page["source_pdf"],"page_number": page["page_number"],"image_path": page["image_path"]}])if(i +1)%5==0or i ==len(pages)-1:print(f" Indexed {i +1}/{len(pages)} pages")print(f"\n✓ Index created with {collection.count()} documents")# Quick retrieval testprint("\nTesting retrieval...")
test_query ="How do I attach the legs?"
query_embedding = get_embedding(test_query)
results = collection.query(query_embeddings=[query_embedding], n_results=2)print(f"Query: '{test_query}'")for meta in results["metadatas"][0]:print(f" → {meta['source_pdf']}, Page {meta['page_number']}")if __name__ =="__main__":
main()Offerto da
Qui, la funzione get_embedding(text) converte il testo della descrizione e le info sulla fonte in un vettore denso usando il modello di OpenAI text-embedding-3-small. Con 1536 dimensioni, questi vettori riescono a catturare il significato semantico per la ricerca per similarità.
main() carica le pagine elaborate da all_pages.json dallo step precedente, inizializza ChromaDB con storage persistente, elimina/ricrea la collezione, genera embedding per ogni pagina, li memorizza con metadati ricchi (sorgente, numero di pagina, percorso immagine) e esegue un rapido test di retrieval per verificare che l'indicizzazione funzioni.
Qui sono importanti i metadati. Memorizziamo image_path per poter recuperare l'immagine reale in seguito e mostrarla a GPT-4o durante la generazione. La descrizione è ciò che viene cercato, ma l'immagine è ciò che viene mostrato.
Step 4: Costruzione della pipeline RAG
Ecco la pipeline RAG multimodale completa. Una query viene incorporata, le pagine rilevanti vengono recuperate, le immagini caricate e GPT-4o genera una risposta basata su descrizioni testuali e contenuti visivi.
Crea step4_query.py:
"""
Step 4: Query the Multimodal RAG System
"""import os
import sys
import base64
from openai import OpenAI
import chromadb
client = OpenAI()
CHROMA_DIR ="data/chroma_db"
COLLECTION_NAME ="ikea_instructions"
TOP_K =3# Initialize ChromaDB
chroma_client = chromadb.PersistentClient(path=CHROMA_DIR)defget_embedding(text):"""Get embedding from OpenAI."""
response = client.embeddings.create(
model="text-embedding-3-small",input=text
)return response.data[0].embedding
defretrieve(query, top_k=TOP_K):"""Find relevant instruction pages."""
collection = chroma_client.get_collection(name=COLLECTION_NAME)
query_embedding = get_embedding(query)
results = collection.query(
query_embeddings=[query_embedding],
n_results=top_k,
include=["documents","metadatas"])return results
defgenerate_answer(query, retrieved_results):"""Generate answer using GPT-4o with retrieved images."""ifnot retrieved_results["ids"][0]:return"No relevant pages found.",[]# Build image content for GPT-4o
image_content =[]
sources =[]for doc, metadata inzip(retrieved_results["documents"][0], retrieved_results["metadatas"][0]):
image_path = metadata["image_path"]ifnot os.path.exists(image_path):continuewithopen(image_path,"rb")as f:
base64_image = base64.b64encode(f.read()).decode("utf-8")
image_content.append({"type":"image_url","image_url":{"url":f"data:image/png;base64,{base64_image}"}})
image_content.append({"type":"text","text":f"[Page {metadata['page_number']} from {metadata['source_pdf']}]"})
sources.append(f"{metadata['source_pdf']}, Page {metadata['page_number']}")# Generate with GPT-4o
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role":"system","content":"""You answer questions about IKEA assembly based on instruction pages.
Be specific, reference page numbers when helpful, and say if something isn't clear from the images."""},{"role":"user","content":[{"type":"text","text":f"Question: {query}\n\nRelevant instruction pages:"},*image_content,{"type":"text","text":"Answer based on these pages."}]}],
max_tokens=600)return response.choices[0].message.content, sources
defanswer_question(query):"""Full RAG pipeline."""print(f"\nQuery: {query}")print("-"*50)print("Retrieving relevant pages...")
results = retrieve(query)print(f" Found {len(results['ids'][0])} pages")print("Generating answer...")
answer, sources = generate_answer(query, results)return answer, sources
defmain():# Check index existstry:
collection = chroma_client.get_collection(name=COLLECTION_NAME)print(f"Connected to index ({collection.count()} documents)")except:print("ERROR: Index not found. Run step2_index.py first.")return# Single query from command lineiflen(sys.argv)>1:
query =" ".join(sys.argv[1:])
answer, sources = answer_question(query)print(f"\nAnswer:\n{answer}")print(f"\nSources: {', '.join(sources)}")return# Interactive modeprint("\nAsk questions about IKEA assembly. Type 'quit' to exit.\n")whileTrue:try:
query =input("Question: ").strip()except KeyboardInterrupt:breakifnot query or query.lower()in["quit","exit"]:break
answer, sources = answer_question(query)print(f"\nAnswer:\n{answer}")print(f"\nSources: {', '.join(sources)}\n")if __name__ =="__main__":
main()Offerto da
Funzioni chiave spiegate:
get_embedding(text): genera un embedding vettoriale per la query usando text-embedding-3-small, abilitando la ricerca per similarità semantica in ChromaDB.
retrieve(query, top_k=TOP_K): crea l'embedding della query, interroga la collezione per i k vicini più prossimi e restituisce documenti e metadati (ad esempio, percorsi delle immagini) tramite include=["documents", "metadatas"].
generate_answer(query, retrieved_results): carica le immagini principali in base64, costruisce un prompt multimodale GPT-4o alternando etichette di pagina e immagini e genera una risposta dettagliata con istruzioni specifiche IKEA.
answer_question(query): orchestra retrieval e generazione, con log di avanzamento.
main(): verifica che l'indice esista, supporta query singole da CLI o modalità chat interattiva (“quit”, “exit” o Ctrl+C per uscire).
Step 5: Esecuzione della pipeline RAG
Puoi eseguirla e digitare la tua domanda, oppure lanciarla direttamente con una domanda specifica, così:
python step4_query.py "How do I attach the legs to the desk?"Offerto da
Retrieving relevant pages...
Found 3 pages
Generating answer...
Answer:
To attach the legs to the MALM desk, follow these steps shown on pages 5-6:
1. First, insert the wooden dowels into the pre-drilled holes on the leg
pieces (you'll see "4x" indicating four dowels are needed)
2. Apply the cam locks to the desk top using the Phillips screwdriver
3. Align each leg with the corresponding holes on the underside of the desk
4. Tighten the cam locks by rotating them clockwise until the legs are
firmly attached
The instructions show this should be done with the desk top face-down on
a soft surface to avoid scratching.
Sources: malm_desk.pdf, Page 5, malm_desk.pdf, Page 6, malm_desk.pdf, Page 7Offerto da
Sembra buono! Il sistema recupera le pagine in base alle descrizioni, carica le immagini reali e GPT-4o genera una risposta guardando direttamente i diagrammi.
Valutazione del RAG multimodale
Costruire un sistema RAG multimodale è una cosa. Capire se funziona bene è un'altra. E farlo girare in produzione in modo affidabile aggiunge un ulteriore livello di sfide.
Le metriche standard del RAG valgono ancora, ma vanno estese al contenuto visivo.
Qualità del retrieval
La Precision@k misura quante delle pagine recuperate erano effettivamente rilevanti. Penalizza le pagine irrilevanti nella top-k, ma per la generazione avere l'immagine chiave conta più che ottenere una top-k perfetta.
La Recall@k misura quanto spesso le pagine corrette compaiono nei primi k risultati. È più pertinente per il nostro caso IKEA perché vogliamo che il sistema trovi almeno una pagina rilevante nella top-k (ad es., k=3). Così GPT-4o ottiene il contesto visivo per rispondere correttamente, anche se altri risultati sono rumore.
Per il nostro sistema IKEA, costruiresti un set di test con magari 50 domande e assegneresti le pagine "corrette" per ciascuna. Poi potresti valutarle usando la seguente funzione:
defevaluate_retrieval(test_cases, top_k=3):"""
test_cases: [{"query": str, "relevant_pages": ["image_path1", ...]}]
"""
hits =0for test in test_cases:
results = retrieve(test["query"], top_k=top_k)
retrieved =[m["image_path"]for m in results["metadatas"][0]]ifany(p in retrieved for p in test["relevant_pages"]):
hits +=1return hits /len(test_cases)Offerto da
Qualità della generazione
Qualcosa di più difficile da misurare automaticamente è la qualità della generazione.
Puoi far valutare alle persone accuratezza e utilità delle risposte, oppure usare un LLM come giudice per verificare se le risposte sono corrette dato il contenuto delle immagini.
Per i diagrammi con passaggi numerati, verifica che il modello assegni i numeri corretti.
Verifiche specifiche multimodali
Ulteriori valutazioni potrebbero rispondere alle seguenti domande:
Il sistema trova risposte basate su immagini quando la risposta è visiva?
Quando il modello fa riferimento a qualcosa in un'immagine, quel riferimento è accurato?
Queste richiedono una revisione manuale, almeno su un campione di query.
Best practice per il RAG multimodale
Se vuoi costruire una pipeline RAG multimodale pronta per la produzione, tieni a mente alcune cose:
I costi di preprocessing si sommano: Le chiamate di visione per la descrizione delle immagini sono la parte costosa. Per un grande corpus di documenti, elabora in batch nelle ore di minor carico e metti in cache in modo aggressivo. Valuta un modello più economico (nel nostro caso, ad es., GPT-4o-mini) per generare descrizioni se la qualità è sufficiente per il tuo caso d'uso.
La latenza della query conta: GPT-4o con immagini richiede 5-10 secondi per query. Lo streaming aiuta gli utenti a vedere risultati parziali in fretta e la cache delle query comuni aiuta per le domande ripetute. Recuperare 2 pagine invece di 5 può fare differenza in termini di token se è già abbastanza contesto.
Monitora le cose giuste: Traccia latenza del retrieval (sotto i 100 ms per collezioni piccole), latenza della generazione (aspettati 5-10 secondi con immagini), costi API per query e feedback degli utenti se lo raccogli.
Il robusto handling degli errori ti salva: Le cose si rompono. Costruisci fallback, come nello snippet seguente.
defanswer_question_safe(query):"""Wrapper with error handling."""try:return answer_question(query)except Exception as e:print(f"Error: {e}")# Fall back to text-only using descriptions
results = retrieve(query)if results["documents"][0]:
context = results["documents"][0][0][:500]returnf"Based on the instructions: {context}...",[]return"Sorry, couldn't process that question.",[]Offerto da
Quando la tua pipeline deve fare più passi (cerca → verifica → ricerca di nuovo → rispondi), i pattern di agentic RAG ti aiutano a strutturarla in modo pulito.
Conclusione
Il RAG multimodale estende la generazione aumentata dal retrieval per gestire l'intera gamma di contenuti nei documenti reali, incluse immagini, diagrammi, tabelle e grafici.
Lo abbiamo usato con successo per interrogare le istruzioni IKEA, che sono notoriamente visive, dove un approccio solo testo non avrebbe funzionato. L'intera pipeline gira in locale tranne che per le chiamate API.
L'intuizione chiave è che non servono embedding multimodali complessi per farlo funzionare. Generando buone descrizioni testuali del contenuto visivo, puoi usare il retrieval testuale standard per trovare immagini pertinenti e poi lasciare che un modello visione-linguaggio faccia il grosso del lavoro in fase di generazione.
Se vuoi proseguire da quanto visto qui, inizia oggi il tuo percorso con il percorso AI Engineer!
FAQ su RAG multimodale
Quali tipi di dati può gestire il RAG multimodale?⌄
Immagini, PDF con grafici, tabelle, diagrammi, audio (tramite trascrizioni) e keyframe video. In pratica, qualsiasi contenuto che vorresti che un'IA consultasse per rispondere alle domande.
Ho bisogno di database vettoriali separati per diverse modalità?⌄
Non necessariamente. La maggior parte dei sistemi in produzione usa un singolo vector store con uno spazio di embedding unificato. Puoi incorporare tutto nello stesso spazio dimensionale, così il retrieval funziona tra tipi di contenuto diversi.
Posso usare OpenAI per l'intera pipeline?⌄
Sì. GPT-4o gestisce sia la comprensione delle immagini sia la generazione delle risposte, mentre text-embedding-3-small gestisce il lato vettoriale. Puoi costruire un sistema RAG multimodale completo senza uscire dall'ecosistema OpenAI.
Qual è la sfida più grande nel RAG multimodale?⌄
Allineamento. Far convivere testo, immagini e dati strutturati nello stesso spazio di embedding in modo che il retrieval funzioni davvero tra modalità. Subito dopo viene la strategia di chunking.
Quanto costa eseguirlo?⌄
Per il tutorial IKEA qui, aspetta circa 2 $ in totale. La parte costosa sono le chiamate GPT-4o per descrivere le immagini durante il preprocessing. Una volta indicizzato, le query costano poco.
Josep è un Data Scientist freelance specializzato in progetti europei, con competenze in archiviazione e processamento dei dati, analisi avanzate e data storytelling efficace.
Come docente, insegna Big Data nel corso di laurea magistrale dell’Università di Navarra e condivide le sue idee tramite articoli su piattaforme come Medium, KDNuggets e DataCamp. Josep scrive anche di Data e Tech nella sua newsletter Databites (databites.tech).
Ha conseguito una laurea in Ingegneria Fisica presso la Università Politecnica della Catalogna e un master in Intelligent Interactive Systems presso la Università Pompeu Fabra.
Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Scopri come integrare l'intelligenza artificiale nelle applicazioni software utilizzando API e librerie open-source. Inizia oggi il tuo percorso per diventare un ingegnere AI!
Immagina di montare un mobile IKEA. Guardi il passaggio 14 delle istruzioni, ma non capisci quale vite va dove. Il diagramma mostra qualcosa, ma non ti è chiaro.
Se chiedessi: "A cosa serve il pezzetto di metallo nel passaggio 14?", un sistema RAG standard basato solo su testo qui non può aiutarti. La risposta è nel diagramma stesso, non nel testo sulla pagina. Le istruzioni IKEA sono famosamente senza parole.
Qui entra in gioco il RAG multimodale.
In questo tutorial ti mostrerò come costruire un sistema RAG multimodale che sappia rispondere a domande su documenti con immagini, diagrammi e contenuti misti. Useremo le istruzioni di montaggio IKEA come caso di test perché sono una buona prova di stress: perlopiù visuali, testo minimo e il classico esempio in cui serve davvero aiuto.
Useremo i modelli di OpenAI: GPT-4o per comprendere le immagini e generare risposte, e
text-embedding-3-smallper creare vettori ricercabili. Se vuoi imparare a costruire pipeline RAG con un framework diverso, ti consiglio il nostro corso RAG con LangChain.Alla fine avrai un sistema funzionante che puoi eseguire in locale e adattare ai tuoi documenti.
Cos'è il RAG multimodale?
Il RAG multimodale estende il pattern standard RAG per gestire più del semplice testo. L'idea di base resta la stessa, ma il "contesto" ora include immagini, tabelle, diagrammi e altri contenuti non testuali.
Perché è importante? Perché i documenti reali non sono solo testo.
Pensa a ciò con cui lavori davvero:
Un sistema che elabora solo testo lavora con informazioni incomplete. È come provare a capire una ricetta leggendo solo la lista degli ingredienti e ignorando le foto che mostrano cosa significa "mescolare delicatamente".
Il RAG tradizionale tratta un PDF come un flusso di caratteri. Estrae il testo, lo suddivide, ne crea gli embedding e finisce lì. Le immagini vengono saltate del tutto o magari convertite in segnaposto
[IMAGE]. Le tabelle diventano righe di testo sconnesse che perdono la loro struttura.Il RAG multimodale adotta un approccio diverso: le immagini vengono descritte o incorporate direttamente, e le tabelle vengono convertite in formati strutturati. Il sistema di retrieval può collegare una query come "diagramma di topologia di rete" sia alle descrizioni testuali dell'architettura di rete sia ai diagrammi visivi effettivi. Il modello di generazione può esaminare un'immagine e ragionare su ciò che rappresenta.
Aspetto
RAG solo testo
RAG multimodale
Tipi di input
Testo semplice, magari markdown
Testo, immagini, tabelle, diagrammi, documenti misti
Cosa viene incorporato
Chunk di testo
Chunk di testo più descrizioni delle immagini o embedding di immagini
Modello di generazione
Qualsiasi LLM
Modello visione-linguaggio (GPT-4o, Claude, Gemini)
Ideale per
Documenti ricchi di testo
Manuali, report, slide, qualsiasi contenuto visivo
Se "multimodale" ti sembra ancora un termine ombrello un po' vago, questo blog su IA multimodale è una spiegazione sintetica con esempi concreti.
Come funziona il RAG multimodale?
La pipeline ha più componenti rispetto al RAG solo testo, ma la struttura è simile. I dati vengono elaborati e incorporati, finiscono in uno store vettoriale e vengono recuperati quando qualcuno fa una domanda.
Ecco il flusso:
Ingestione e parsing dei documenti
I documenti grezzi (PDF, immagini, slide) vengono scomposti nelle loro parti. Estrai il testo, identifichi le immagini, analizzi le tabelle e capisci come tutto è correlato nello spazio. Un diagramma a pagina 5 potrebbe avere una didascalia sotto e un riferimento nel testo a pagina 4. Devi catturare queste relazioni.
Preprocessing dei contenuti
Tipi diversi di contenuto richiedono trattamenti diversi. Per le immagini, hai due opzioni principali: generare descrizioni testuali usando un modello di visione (come GPT-4o), oppure creare embedding delle immagini direttamente con un encoder multimodale (come CLIP). L'approccio delle descrizioni è più semplice e funziona con embedding testuali standard. Le tabelle vengono convertite in formati strutturati o riassunte in linguaggio naturale.
Dopo questo passaggio, tutto viene convertito in vettori. Se usi l'approccio delle descrizioni, ti basta incorporare tutto il testo (testo originale più descrizioni generate delle immagini) con un modello di embedding testuale standard. La chiave è che ora le query su contenuti visivi possono corrispondere a quelle descrizioni.
Indicizzazione e storage
I vettori vengono archiviati in un database con metadati, inclusi documento sorgente, numero di pagina, tipo di contenuto e, per le immagini, il percorso al file originale. Ti servirà quel percorso più avanti quando vorrai mostrarlo al modello di generazione.
Retrieval
Quando arriva una query, creane l'embedding con lo stesso modello, cerca i vicini più prossimi e restituisci i migliori risultati. I chunk recuperati possono fare riferimento a testo, immagini o entrambi.
Generazione
Passa la query, insieme al contesto recuperato, a un modello visione-linguaggio. Se i chunk recuperati fanno riferimento a immagini, carica quelle immagini e includile nel prompt. Il modello vede sia il contesto testuale sia le immagini reali e poi genera una risposta.
La fase di generazione è dove GPT-4o brilla davvero. Può guardare un diagramma IKEA che mostra la posizione delle viti e spiegare in linguaggio semplice cosa succede. Può leggere un grafico e riassumere l'andamento. Può notare dettagli in disegni tecnici che una descrizione testuale potrebbe tralasciare.
Implementazione del RAG multimodale
Costruiamolo. Creeremo un sistema che sappia rispondere a domande sulle istruzioni di montaggio IKEA, passo dopo passo dall'impostazione a un'interfaccia di query funzionante.
Step 0: Scelta di framework e strumenti
Per questo tutorial, restiamo sul semplice:
Componente
Scelta
Perché
Embeddings
text-embedding-3-small
Economico, buona qualità, stessa API
Vector store
ChromaDB
Gira in locale, nessun setup, persistente
Modello di visione
GPT-4o
Il migliore per la comprensione delle immagini
Generazione
GPT-4o
Stesso modello, gestisce entrambi i compiti
Puoi sostituire con alternative. Pinecone o Weaviate per lo store vettoriale se ti serve scalabilità. Claude o Gemini sono buone alternative per la generazione. L'architettura generale resta la stessa.
Perché non usare CLIP o ColPali per gli embedding? Puoi farlo. Permettono di incorporare direttamente le immagini senza generare prima descrizioni testuali. Ma aggiungono complessità e spesso richiedono una GPU. L'approccio delle descrizioni funziona bene per contenuti in stile documentale, dove vuoi catturare dettagli semantici, non solo somiglianza visiva.
Step 1: Configurazione dell'ambiente
Iniziamo con dipendenze e variabili d'ambiente per impostare il progetto.
Installazione delle dipendenze
Per prima cosa, ti serve
popplerinstallato per gestire l'elaborazione dei PDF.Ubuntu/Debian:
sudo apt-get install poppler-utilsmacOS (con Homebrew):
brew install popplerWindows: Scarica dalla pagina dei rilasci e aggiungi al PATH
Poi installa i pacchetti Python. Oltre a ChromaDB, ci servono pdf2image per convertire i PDF in immagini, pillow per l'elaborazione delle immagini e requests per scaricare i file PDF.
Impostazione della chiave API e dei file
Per usare i modelli, imposta la tua chiave API di OpenAI come variabile d'ambiente.
Unix/Linux/macOS:
Windows:
Ora configuriamo la struttura del progetto e scarichiamo i dati di esempio. Lo script include già gli URL di download, ma se vuoi dare un'occhiata ai PDF prima, ecco i link diretti sia per la scrivania MALM sia per la struttura letto MALM.
Crea un file chiamato
step1_setup.py:Per completare l'impostazione iniziale, esegui lo script:
Dovresti vedere i PDF IKEA scaricati nella cartella
data/.Step 2: Preprocessing dei documenti
Le istruzioni IKEA sono insidiose. Sono perlopiù diagrammi con testo minimo. Dobbiamo convertire le pagine PDF in immagini, poi usare GPT-4o per descrivere cosa mostra ogni pagina.
Crea
step2_preprocess.py:Ecco cosa fanno le funzioni:
convert_pdf_to_images(pdf_path): rende ogni pagina del PDF come immagine PNG a 150 DPI usandopdf2image. Questo estrae i diagrammi privi di testo selezionabile, salvandoli con nomi coerenti comemalm_desk_page_001.png.describe_page(image_path): codifica l'immagine inbase64e la invia a GPT-4o con un prompt di sistema mirato. Il modello restituisce una descrizione testuale dettagliata che cattura passaggi, parti e quantità per la ricerca semantica.main(): orchestra il tutto: carica descrizioni in cache per evitare rielaborazioni, converte i PDF in immagini, genera nuove descrizioni quando serve, salva la cache incrementale e produceall_pages.jsoncon metadati per l'indicizzazione.Esegui lo script così:
Questo passaggio richiede alcuni minuti. GPT-4o analizza ogni pagina e genera una descrizione dettagliata. Le descrizioni vengono messe in cache, quindi se lo riesegui, salta le pagine già elaborate.
Cosa sta succedendo qui? Convertiamo il contenuto visivo in testo ricercabile. Quando qualcuno chiede, "Come attacco le gambe?", il retriever cercherà in queste descrizioni e troverà le pagine che menzionano il fissaggio delle gambe, anche se la pagina originale non ha alcun testo.
Una volta costruita la pipeline di base, i prossimi miglioramenti vengono da tecniche di retrieval, come ricerca ibrida, reranking o riscrittura della query. Per familiarizzare, leggi questo blog sulle Tecniche RAG avanzate.
Step 3: Archiviazione e indicizzazione dei dati multimodali
Ora creiamo gli embedding delle descrizioni e li memorizziamo in ChromaDB.
Crea
step3_index.py:Qui, la funzione
get_embedding(text)converte il testo della descrizione e le info sulla fonte in un vettore denso usando il modello di OpenAItext-embedding-3-small. Con 1536 dimensioni, questi vettori riescono a catturare il significato semantico per la ricerca per similarità.main()carica le pagine elaborate daall_pages.jsondallo step precedente, inizializza ChromaDB con storage persistente, elimina/ricrea la collezione, genera embedding per ogni pagina, li memorizza con metadati ricchi (sorgente, numero di pagina, percorso immagine) e esegue un rapido test di retrieval per verificare che l'indicizzazione funzioni.Eseguilo:
Qui sono importanti i metadati. Memorizziamo
image_pathper poter recuperare l'immagine reale in seguito e mostrarla a GPT-4o durante la generazione. La descrizione è ciò che viene cercato, ma l'immagine è ciò che viene mostrato.Step 4: Costruzione della pipeline RAG
Ecco la pipeline RAG multimodale completa. Una query viene incorporata, le pagine rilevanti vengono recuperate, le immagini caricate e GPT-4o genera una risposta basata su descrizioni testuali e contenuti visivi.
Crea
step4_query.py:Funzioni chiave spiegate:
get_embedding(text): genera un embedding vettoriale per la query usandotext-embedding-3-small, abilitando la ricerca per similarità semantica in ChromaDB.retrieve(query, top_k=TOP_K): crea l'embedding della query, interroga la collezione per i k vicini più prossimi e restituisce documenti e metadati (ad esempio, percorsi delle immagini) tramiteinclude=["documents", "metadatas"].generate_answer(query, retrieved_results): carica le immagini principali inbase64, costruisce un prompt multimodale GPT-4o alternando etichette di pagina e immagini e genera una risposta dettagliata con istruzioni specifiche IKEA.answer_question(query): orchestra retrieval e generazione, con log di avanzamento.main(): verifica che l'indice esista, supporta query singole da CLI o modalità chat interattiva (“quit”, “exit” o Ctrl+C per uscire).Step 5: Esecuzione della pipeline RAG
Puoi eseguirla e digitare la tua domanda, oppure lanciarla direttamente con una domanda specifica, così:
Sembra buono! Il sistema recupera le pagine in base alle descrizioni, carica le immagini reali e GPT-4o genera una risposta guardando direttamente i diagrammi.
Valutazione del RAG multimodale
Costruire un sistema RAG multimodale è una cosa. Capire se funziona bene è un'altra. E farlo girare in produzione in modo affidabile aggiunge un ulteriore livello di sfide.
Le metriche standard del RAG valgono ancora, ma vanno estese al contenuto visivo.
Qualità del retrieval
La Precision@k misura quante delle pagine recuperate erano effettivamente rilevanti. Penalizza le pagine irrilevanti nella top-k, ma per la generazione avere l'immagine chiave conta più che ottenere una top-k perfetta.
La Recall@k misura quanto spesso le pagine corrette compaiono nei primi k risultati. È più pertinente per il nostro caso IKEA perché vogliamo che il sistema trovi almeno una pagina rilevante nella top-k (ad es., k=3). Così GPT-4o ottiene il contesto visivo per rispondere correttamente, anche se altri risultati sono rumore.
Per il nostro sistema IKEA, costruiresti un set di test con magari 50 domande e assegneresti le pagine "corrette" per ciascuna. Poi potresti valutarle usando la seguente funzione:
Qualità della generazione
Qualcosa di più difficile da misurare automaticamente è la qualità della generazione.
Puoi far valutare alle persone accuratezza e utilità delle risposte, oppure usare un LLM come giudice per verificare se le risposte sono corrette dato il contenuto delle immagini.
Per i diagrammi con passaggi numerati, verifica che il modello assegni i numeri corretti.
Verifiche specifiche multimodali
Ulteriori valutazioni potrebbero rispondere alle seguenti domande:
Il sistema trova risposte basate su immagini quando la risposta è visiva?
Quando il modello fa riferimento a qualcosa in un'immagine, quel riferimento è accurato?
Queste richiedono una revisione manuale, almeno su un campione di query.
Best practice per il RAG multimodale
Se vuoi costruire una pipeline RAG multimodale pronta per la produzione, tieni a mente alcune cose:
I costi di preprocessing si sommano: Le chiamate di visione per la descrizione delle immagini sono la parte costosa. Per un grande corpus di documenti, elabora in batch nelle ore di minor carico e metti in cache in modo aggressivo. Valuta un modello più economico (nel nostro caso, ad es., GPT-4o-mini) per generare descrizioni se la qualità è sufficiente per il tuo caso d'uso.
La latenza della query conta: GPT-4o con immagini richiede 5-10 secondi per query. Lo streaming aiuta gli utenti a vedere risultati parziali in fretta e la cache delle query comuni aiuta per le domande ripetute. Recuperare 2 pagine invece di 5 può fare differenza in termini di token se è già abbastanza contesto.
Monitora le cose giuste: Traccia latenza del retrieval (sotto i 100 ms per collezioni piccole), latenza della generazione (aspettati 5-10 secondi con immagini), costi API per query e feedback degli utenti se lo raccogli.
Il robusto handling degli errori ti salva: Le cose si rompono. Costruisci fallback, come nello snippet seguente.
Quando la tua pipeline deve fare più passi (cerca → verifica → ricerca di nuovo → rispondi), i pattern di agentic RAG ti aiutano a strutturarla in modo pulito.
Conclusione
Il RAG multimodale estende la generazione aumentata dal retrieval per gestire l'intera gamma di contenuti nei documenti reali, incluse immagini, diagrammi, tabelle e grafici.
Lo abbiamo usato con successo per interrogare le istruzioni IKEA, che sono notoriamente visive, dove un approccio solo testo non avrebbe funzionato. L'intera pipeline gira in locale tranne che per le chiamate API.
L'intuizione chiave è che non servono embedding multimodali complessi per farlo funzionare. Generando buone descrizioni testuali del contenuto visivo, puoi usare il retrieval testuale standard per trovare immagini pertinenti e poi lasciare che un modello visione-linguaggio faccia il grosso del lavoro in fase di generazione.
Se vuoi proseguire da quanto visto qui, inizia oggi il tuo percorso con il percorso AI Engineer!