RAG: Retrieval-Augmented Generation Spiegato
I Large Language Model (LLM) rappresentano una delle innovazioni tecnologiche più dirompenti degli ultimi anni: GPT-4, Claude, Gemini e Llama possono scrivere codice, riassumere documenti, rispondere a domande complesse e persino ragionare su problemi astratti. Eppure, soffrono di un difetto strutturale che ne limita drasticamente l'applicabilita in contesti professionali: le allucinazioni.
Quando un LLM non conosce la risposta, non dice "non lo so". Invece, genera testo plausibile ma completamente falso, con la stessa sicurezza con cui presenta fatti verificabili. In ambito aziendale, legale o medico, questo comportamento è inaccettabile. La soluzione più efficace e matura a questo problema si chiama Retrieval-Augmented Generation (RAG).
In questo primo articolo della serie AI Engineering e RAG Avanzato, partiremo dalle basi: cos'è RAG, come funziona, perchè risolve il problema delle allucinazioni e come costruire un sistema RAG funzionante da zero. Al termine, avrai una comprensione solida dell'intera architettura e sarai pronto ad approfondire ogni singolo componente negli articoli successivi.
Panoramica della Serie
| # | Articolo | Focus |
|---|---|---|
| 1 | Sei qui - RAG Spiegato | Fondamenti e architettura completa |
| 2 | Embeddings e Ricerca Semantica | Come i testi diventano vettori |
| 3 | Vector Database in Profondità | Storage, indexing, similarity search |
| 4 | RAG con LangChain e Python | Implementazione pratica end-to-end |
| 5 | Hybrid Retrieval e Reranking | Ricerca ibrida keyword + semantica |
| 6 | Context Window e Prompt Engineering | Ottimizzare il contesto per l'LLM |
| 7 | RAG in Produzione | Scaling, monitoring, evaluation |
| 8 | Knowledge Graphs e RAG | Grafi di conoscenza + retrieval |
| 9 | Multi-Agent Systems | Agenti AI collaborativi |
| 10 | Il Futuro del RAG | Trend, ricerca e next steps |
Cosa Imparerai
- Cos'è RAG e quale problema fondamentale risolve per gli LLM
- L'architettura completa: dalla preparazione dei documenti alla generazione della risposta
- Come funzionano embeddings, vector store e similarity search
- Le strategie di chunking e i loro trade-off
- Come costruire un sistema RAG minimo funzionante con Python
- Le differenze tra Naive RAG e Advanced RAG
- Quando usare RAG vs fine-tuning vs prompt engineering
1. Il Problema delle Allucinazioni nei LLM
Per comprendere perchè RAG è necessario, dobbiamo prima capire il problema che risolve. I Large Language Model sono essenzialmente modelli probabilistici di linguaggio: dato un testo di input, predicono la sequenza di token più probabile come continuazione. Questa architettura, basata sul meccanismo di attention dei Transformer, produce risultati straordinari ma ha un limite intrinseco: il modello non "sa" nulla nel senso umano del termine. Genera testo statisticamente plausibile basandosi sui pattern appresi durante l'addestramento.
I Tre Problemi Strutturali degli LLM
| Problema | Descrizione | Impatto Pratico |
|---|---|---|
| Knowledge Cutoff | La conoscenza è congelata alla data di addestramento | Nessuna informazione su eventi recenti o dati proprietari |
| Allucinazioni | Genera risposte plausibili ma completamente inventate | Informazioni false presentate con alta confidenza |
| No Citazioni | Non può indicare la fonte delle proprie affermazioni | Impossibile verificare la correttezza delle risposte |
| No Dati Privati | Non conosce la documentazione interna dell'azienda | Inutile per casi d'uso specifici senza contesto |
Le allucinazioni non sono un bug occasionale: sono una conseguenza diretta dell'architettura. Quando il modello non ha informazioni sufficienti per rispondere, non restituisce un errore. Invece, genera la continuazione più probabile del testo, che può essere completamente inventata. Il modello è stato addestrato per produrre testo fluente e coerente, non per essere fattualmente accurato.
Esempi Concreti di Allucinazione
Per capire la gravita del problema, consideriamo scenari reali dove le allucinazioni hanno conseguenze significative:
- Ambito legale: Un LLM potrebbe citare sentenze inesistenti o leggi mai promulgate con numeri di articolo credibili ma inventati
- Ambito medico: Potrebbe suggerire dosaggi di farmaci errati o interazioni farmacologiche non documentate
- Documentazione tecnica: Potrebbe descrivere API con parametri che non esistono o funzionalità mai implementate
- Supporto clienti: Potrebbe inventare policy aziendali, procedure di reso o garanzie inesistenti
Le statistiche confermano la portata del problema: i tassi di allucinazione variano significativamente per dominio, con ambiti specialistici come medicina e diritto che registrano tassi del 10-20% anche con i modelli più avanzati. RAG rappresenta la tecnica più efficace per mitigare questo problema, con riduzioni documentate fino al 71%.
2. Cos'è RAG: Definizione e Origini
Retrieval-Augmented Generation (RAG) è un paradigma architetturale che combina un sistema di information retrieval (recupero documenti) con un modello generativo (LLM) per produrre risposte fondate su dati reali. Il concetto è stato formalizzato nel 2020 da Patrick Lewis e colleghi di Meta AI nel paper seminale "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks".
L'intuizione fondamentale è semplice: invece di chiedere al modello di "ricordare" tutte le informazioni dal suo addestramento, gli forniamo i documenti rilevanti al momento della generazione. Questo processo si chiama grounding: ancorare la generazione a fonti concrete e verificabili.
LLM TRADIZIONALE:
Domanda utente ──────────────────> [LLM] ──> Risposta
|
Basata SOLO su training data
Rischio allucinazione: ALTO
RAG (Retrieval-Augmented Generation):
Domanda utente ──> [RETRIEVAL] ──> Documenti rilevanti
| |
| v
└───────> [LLM + Contesto] ──> Risposta con citazioni
|
Basata su DATI REALI recuperati
Rischio allucinazione: BASSO
Analogia: L'Esame Open-Book
Immagina un esame universitario. L'LLM tradizionale è come uno studente che deve rispondere a memoria: conosce molto, ma può confondersi o inventare dettagli. RAG è come un esame open-book: lo studente può consultare i propri appunti e libri prima di rispondere. La risposta sarà più precisa e verificabile perchè fondata su fonti concrete.
Il concetto di grounding è cruciale: ogni affermazione nella risposta generata può essere ricondotta a un documento specifico nella knowledge base. Questo significa che:
- Le risposte sono verificabili: puoi controllare il documento sorgente
- Le risposte sono aggiornabili: aggiornando i documenti, cambiano automaticamente le risposte
- Le risposte sono controllabili: decidi tu quali documenti il sistema può consultare
- Le risposte sono tracciabili: puoi includere citazioni con riferimento alla fonte
3. Come Funziona RAG: La Pipeline Completa
Un sistema RAG si compone di due macro-fasi: la pipeline di indicizzazione, che prepara i documenti una tantum (o periodicamente), e la pipeline di query, che gestisce le domande degli utenti in tempo reale.
3.1 Pipeline di Indicizzazione (Fase Offline)
La fase di indicizzazione trasforma i documenti grezzi in una struttura ottimizzata per la ricerca semantica. Si compone di quattro step sequenziali:
[Documenti Sorgente]
| PDF, HTML, Markdown, CSV, database, API, email...
v
[1. DOCUMENT LOADING]
| Estrazione del testo grezzo dai formati sorgente
| Preservazione dei metadati (autore, data, titolo)
v
[2. CHUNKING (Text Splitting)]
| Divisione in frammenti di dimensione gestibile
| Strategie: fixed-size, semantic, recursive
| Overlap tra chunk per preservare contesto
v
[3. EMBEDDING]
| Trasformazione di ogni chunk in un vettore numerico
| Modelli: OpenAI, Sentence Transformers, Cohere
| Dimensioni tipiche: 384, 768, 1536, 3072
v
[4. VECTOR STORE]
| Salvataggio dei vettori in un database specializzato
| Indexing per ricerca veloce (HNSW, IVF)
| ChromaDB, Pinecone, Weaviate, Milvus, Qdrant
v
[Knowledge Base Pronta per le Query]
3.2 Pipeline di Query (Fase Online)
Quando un utente pone una domanda, la pipeline di query entra in azione per trovare i documenti più rilevanti e generare una risposta fondata:
[Domanda dell'Utente]
| "Come configuro l'autenticazione OAuth nella nostra app?"
v
[1. QUERY EMBEDDING]
| La domanda viene trasformata in vettore
| Stesso modello usato nell'indicizzazione!
v
[2. SIMILARITY SEARCH]
| Ricerca dei chunk più simili nel vector store
| Metriche: cosine similarity, L2, dot product
| Restituisce i top-k risultati (tipicamente k=3..10)
v
[3. CONTEXT ASSEMBLY]
| I chunk recuperati vengono assemblati in un contesto
| Ordinamento per rilevanza, deduplicazione
v
[4. PROMPT CONSTRUCTION]
| Costruzione del prompt con contesto + domanda
| Template: "Basandoti sui seguenti documenti, rispondi..."
v
[5. LLM GENERATION]
| Il modello genera la risposta basandosi sul contesto
| Può includere citazioni ai documenti sorgente
v
[Risposta Fondata + Citazioni]
Regola Fondamentale: Coerenza degli Embeddings
E' obbligatorio usare lo stesso modello di embedding sia nella fase di indicizzazione che nella fase di query. Modelli diversi producono spazi vettoriali incompatibili: i vettori generati da un modello non sono confrontabili con quelli di un altro. Cambiare modello di embedding richiede la re-indicizzazione completa di tutti i documenti.
4. Document Processing: Chunking e Preparazione
Il chunking è una delle fasi più critiche dell'intera pipeline RAG. La qualità dei risultati dipende in larga misura da come i documenti vengono suddivisi. Chunk troppo grandi diluiscono il segnale semantico e sprecano spazio nella context window dell'LLM. Chunk troppo piccoli perdono il contesto necessario per essere comprensibili.
4.1 Fixed-Size Chunking
La strategia più semplice: divide il testo in blocchi di dimensione fissa (es. 500 token) con un overlap opzionale tra chunk consecutivi.
Parametri del Fixed-Size Chunking
| Parametro | Descrizione | Valore Tipico |
|---|---|---|
| chunk_size | Dimensione massima di ogni chunk in token o caratteri | 300-500 token |
| chunk_overlap | Sovrapposizione tra chunk consecutivi | 10-20% del chunk_size |
| separator | Carattere/stringa usato per dividere | "\n\n", "\n", " " |
Documento originale (1500 token):
"Lorem ipsum dolor sit amet... [1500 token di testo]"
Con chunk_size=500 e overlap=50:
Chunk 1: token 1-500 ────────────┐
Chunk 2: token 451-950 ──┐ overlap │
Chunk 3: token 901-1400 ──┘ │
Chunk 4: token 1351-1500 │
────────────┘
L'overlap garantisce che il contesto ai bordi non venga perso.
4.2 Recursive Character Splitting
Strategia più sofisticata che prova a dividere rispettando la struttura del documento. Usa una gerarchia di separatori: prima prova a dividere per paragrafi ("\n\n"), poi per righe ("\n"), poi per frasi (". "), infine per parole (" "). Questo preserva meglio il contesto semantico rispetto al fixed-size.
4.3 Semantic Chunking
La strategia più avanzata: usa gli embeddings stessi per determinare dove dividere. Calcola la similarità tra frasi consecutive e crea un nuovo chunk quando la similarità scende sotto una soglia, indicando un cambio di argomento. Produce chunk di dimensione variabile ma semanticamente coerenti.
Confronto Strategie di Chunking
| Strategia | qualità | Complessità | Quando Usarla |
|---|---|---|---|
| Fixed-Size | Base | Minima | Prototipazione, documenti omogenei |
| Recursive | Buona | Bassa | Uso generale, documenti strutturati |
| Semantic | Ottima | Alta | Alta qualità richiesta, documenti eterogenei |
L'Importanza dei Metadati
Ogni chunk dovrebbe portare con se dei metadati: titolo del documento, autore, data, sezione, pagina. Questi metadati sono fondamentali per il filtraggio durante la ricerca e per generare citazioni accurate nella risposta. Un chunk senza metadati è come un paragrafo senza contesto.
5. Embeddings: Trasformare il Testo in Vettori
Gli embeddings sono il cuore matematico di RAG. Un embedding è una rappresentazione numerica (un vettore di numeri decimali) che cattura il significato semantico di un testo. Due frasi con significato simile avranno vettori "vicini" nello spazio multidimensionale, indipendentemente dalle parole usate.
INPUT: Frase di testo
OUTPUT: Vettore di numeri decimali (es. 1536 dimensioni)
Esempio:
"Il gatto dorme sul divano" --> [0.23, -0.45, 0.67, 0.12, -0.89, ...]
"Il felino riposa sul sofa" --> [0.22, -0.44, 0.68, 0.11, -0.88, ...]
^^ Vettori molto SIMILI (stesso significato)
"Il prezzo dell'oro sale" --> [-0.56, 0.78, -0.12, 0.91, 0.34, ...]
^^ Vettore molto DIVERSO (significato diverso)
Il modello di embedding è una rete neurale addestrata su enormi quantità di testo per apprendere le relazioni semantiche tra parole, frasi e concetti. Non produce semplicemente una rappresentazione sintattica (come il bag-of-words o TF-IDF), ma cattura il significato profondo del testo.
Modelli di Embedding Principali (2025-2026)
| Modello | Dimensioni | Provider | Costo Indicativo |
|---|---|---|---|
text-embedding-3-small |
1536 | OpenAI | ~$0.02 / 1M token |
text-embedding-3-large |
3072 | OpenAI | ~$0.13 / 1M token |
voyage-3-large |
1024 | Voyage AI | ~$0.06 / 1M token |
all-MiniLM-L6-v2 |
384 | HuggingFace | Gratuito (self-hosted) |
nomic-embed-text |
768 | Ollama (locale) | Gratuito (locale) |
embed-v4 |
1024 | Cohere | ~$0.10 / 1M token |
La scelta del modello di embedding dipende dal caso d'uso: i modelli più grandi (3072 dimensioni) offrono una rappresentazione più ricca ma costano di più in termini di storage e computazione. Per molti casi d'uso, modelli a 768-1536 dimensioni offrono un ottimo compromesso tra qualità e costo.
6. Vector Store: Il Database per gli Embeddings
Un vector store (o vector database) è un database specializzato per memorizzare, indicizzare e cercare vettori ad alta dimensionalità. A differenza dei database tradizionali che cercano corrispondenze esatte (SQL WHERE), un vector store cerca i vettori più simili a quello della query.
6.1 Metriche di Similarità
La ricerca nel vector store si basa su metriche di distanza/similarità tra vettori. Le tre metriche più comuni sono:
Metriche di Similarità a Confronto
| Metrica | Range | Descrizione | Quando Usarla |
|---|---|---|---|
| Cosine Similarity | [-1, 1] | Misura l'angolo tra due vettori, ignora la magnitudine | Default per la maggior parte dei casi (scelta consigliata) |
| Distanza Euclidea (L2) | [0, +inf) | Distanza geometrica nello spazio, sensibile alla magnitudine | Quando la magnitudine dei vettori è significativa |
| Dot Product | (-inf, +inf) | Prodotto scalare, combina direzione e magnitudine | Vettori già normalizzati, Maximum Inner Product Search |
6.2 Panoramica dei Vector Database
Il mercato dei vector database è esploso con l'adozione di RAG. Ecco una panoramica dei principali strumenti disponibili:
Vector Database a Confronto
| Database | Tipo | Linguaggio | Ideale Per |
|---|---|---|---|
| ChromaDB | Embedded / Server | Python | Prototipazione, sviluppo locale, piccoli dataset |
| Pinecone | Cloud managed | Multi-lang | Produzione, scalabilità automatica, zero-ops |
| Weaviate | Self-hosted / Cloud | Go | Hybrid search, multi-tenancy, GraphQL |
| Milvus | Self-hosted / Cloud | Go / C++ | Grandi volumi, alte performance, enterprise |
| Qdrant | Self-hosted / Cloud | Rust | Performance, filtering avanzato, API REST |
| pgvector | Estensione PostgreSQL | C | Stack esistente PostgreSQL, dati relazionali + vettoriali |
| FAISS | Libreria in-memory | C++ / Python | Ricerca, benchmark, ottimizzazione massima |
Per iniziare, ChromaDB è la scelta più semplice: si installa con pip, funziona in-memory o persistente su disco, e si integra nativamente con LangChain. Per la produzione, Pinecone (managed) e Qdrant (self-hosted) sono tra le opzioni più popolari.
7. Retrieval: La Ricerca dei Documenti Rilevanti
La fase di retrieval è il momento in cui la domanda dell'utente viene trasformata in un vettore e confrontata con tutti i vettori nel vector store per trovare i chunk più rilevanti. Questo processo avviene in millisecondi anche con milioni di documenti indicizzati, grazie agli algoritmi di indicizzazione approssimata come HNSW (Hierarchical Navigable Small World).
Query: "Come configuro OAuth nella nostra app?"
|
v
[1] Embedding della query ──> [0.34, -0.21, 0.56, ...]
|
v
[2] Similarity Search nel Vector Store
| Confronta il vettore della query con tutti i vettori salvati
| Usa cosine similarity come metrica
|
v
[3] Top-K Results (es. k=5)
|
| Score: 0.92 - "Configurazione OAuth 2.0 per applicazioni web..."
| Score: 0.87 - "Guida ai flussi di autenticazione OAuth..."
| Score: 0.83 - "Impostazione dei redirect URI per OAuth..."
| Score: 0.76 - "Confronto tra OAuth e SAML per SSO..."
| Score: 0.71 - "Sicurezza delle API con token JWT..."
|
v
[4] Filtering e Reranking (opzionale)
| Filtra per metadati (data, categoria, fonte)
| Reranking con modello cross-encoder
|
v
[Chunk Rilevanti Pronti per il Prompt]
7.1 Il Parametro Top-K
Il parametro top-k determina quanti chunk vengono recuperati. La scelta è un trade-off:
- K troppo basso (1-2): Rischio di perdere informazioni rilevanti
- K troppo alto (20+): Troppo rumore nel contesto, spreco di token, rischio di confondere il modello
- K ottimale (3-7): Buon bilanciamento tra copertura e precisione
7.2 Relevance Scoring
Ogni risultato ha un relevance score (punteggio di rilevanza). I chunk con score basso dovrebbero essere filtrati perchè aggiungono rumore senza valore. Una soglia comune è 0.7 per la cosine similarity: tutto sotto viene scartato. In pratica, il threshold ottimale dipende dal dominio e va calibrato sperimentalmente.
8. Generation: Costruire la Risposta con l'LLM
La fase finale della pipeline RAG: i chunk recuperati vengono assemblati in un contesto strutturato e inseriti nel prompt dell'LLM insieme alla domanda dell'utente. Il modello genera la risposta basandosi esclusivamente (o prevalentemente) sul contesto fornito.
8.1 Il Prompt Template
Un buon prompt template per RAG deve istruire il modello a:
- Usare solo le informazioni dal contesto fornito
- Ammettere quando non trova la risposta nei documenti
- Citare le fonti quando possibile
- Non inventare informazioni non presenti nel contesto
Sei un assistente tecnico. Rispondi alle domande SOLO basandoti
sul contesto fornito. Se la risposta non è presente nel contesto,
rispondi "Non ho trovato informazioni sufficienti nei documenti
disponibili."
CONTESTO:
---
{context}
---
DOMANDA: {question}
ISTRUZIONI:
1. Rispondi in modo chiaro e conciso
2. Cita il documento sorgente tra parentesi quadre [Fonte: nome_doc]
3. Se il contesto non contiene la risposta, dillo esplicitamente
4. Non inventare informazioni non presenti nel contesto
RISPOSTA:
8.2 Citation Tracking
Una delle funzionalità più preziose di RAG è la possibilità di tracciare le citazioni. Ogni chunk nel contesto può essere etichettato con un identificatore (es. [DOC-1], [DOC-2]) e il modello viene istruito a riferirsi a questi identificatori nella risposta. Questo permette all'utente di verificare ogni affermazione consultando il documento originale.
Context Window e Limiti
La quantità di contesto che puoi inserire è limitata dalla context window del modello LLM. Se i chunk recuperati superano il limite, dovrai selezionare solo i più rilevanti o riassumerli. Modelli moderni come GPT-4o (128K token) e Claude 3.5 (200K token) hanno context window molto ampie, ma anche cosi, inserire troppo contesto irrilevante degrada la qualità delle risposte (il cosiddetto "lost in the middle" problem).
9. Architettura RAG Completa: Visione d'Insieme
Ora che abbiamo esaminato ogni componente individualmente, assembliamo l'architettura completa di un sistema RAG end-to-end:
FASE OFFLINE (Indicizzazione)
┌──────────────────────────────────────────────────────┐
│ │
│ [Documenti] ──> [Loader] ──> [Chunker] │
│ PDF, HTML Estrazione Divisione │
│ MD, CSV testo in frammenti │
│ │ │
│ v │
│ [Embedding Model] │
│ Testo ──> Vettore │
│ │ │
│ v │
│ [Vector Store] │
│ ChromaDB, Pinecone │
│ Weaviate, Qdrant │
│ │
└──────────────────────────────────────────────────────┘
FASE ONLINE (Query)
┌──────────────────────────────────────────────────────┐
│ │
│ [Domanda Utente] │
│ │ │
│ v │
│ [Query Embedding] ──> [Similarity Search] │
│ Stesso modello Top-K chunk │
│ dell'indicizzazione │ │
│ v │
│ [Context Assembly] │
│ Chunk + Metadati │
│ │ │
│ v │
│ [Prompt Template + Contesto + Domanda] │
│ │ │
│ v │
│ [LLM] │
│ GPT-4, Claude │
│ Llama, Gemini │
│ │ │
│ v │
│ [Risposta con Citazioni] │
│ │
└──────────────────────────────────────────────────────┘
Componenti e Responsabilità
| Componente | Responsabilità | Strumenti Tipici |
|---|---|---|
| Document Loader | Caricare documenti da diverse sorgenti | LangChain loaders, Unstructured, LlamaIndex |
| Text Splitter | Dividere documenti in chunk ottimali | RecursiveCharacterTextSplitter, SemanticChunker |
| Embedding Model | Trasformare testo in vettori semantici | OpenAI Embeddings, Sentence Transformers, Cohere |
| Vector Store | Memorizzare e indicizzare vettori | ChromaDB, Pinecone, Qdrant, pgvector |
| Retriever | Cercare i chunk più rilevanti | Similarity search, MMR, hybrid retrieval |
| LLM | Generare la risposta finale dal contesto | GPT-4o, Claude 3.5, Llama 3, Gemini Pro |
10. Esempio Pratico: RAG Minimo con Python
Costruiamo un sistema RAG funzionante con il minimo codice necessario. Useremo LangChain come framework di orchestrazione, OpenAI per embeddings e generazione, e ChromaDB come vector store locale.
10.1 Setup del Progetto
# Crea un ambiente virtuale
python -m venv rag-env
source rag-env/bin/activate # Linux/Mac
# Installa le dipendenze
pip install langchain langchain-openai langchain-community
pip install chromadb
pip install pypdf # Per caricare PDF
10.2 Pipeline di Indicizzazione
import os
from langchain_community.document_loaders import (
PyPDFLoader,
TextLoader,
DirectoryLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# Configura la API key
os.environ["OPENAI_API_KEY"] = "sk-..."
# 1. DOCUMENT LOADING
# Carica tutti i PDF da una cartella
loader = DirectoryLoader(
"./documenti/",
glob="**/*.pdf",
loader_cls=PyPDFLoader
)
documents = loader.load()
print(f"Caricati {len(documents)} documenti")
# 2. CHUNKING
# Divisione ricorsiva con overlap
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 500 caratteri per chunk
chunk_overlap=50, # 50 caratteri di overlap
length_function=len,
separators=["\n\n", "\n", ". ", " ", ""]
)
chunks = text_splitter.split_documents(documents)
print(f"Creati {len(chunks)} chunk")
# 3. EMBEDDING + 4. VECTOR STORE
# Crea gli embeddings e salvali in ChromaDB
embedding_model = OpenAIEmbeddings(
model="text-embedding-3-small"
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model,
persist_directory="./chroma_db",
collection_name="documenti_aziendali"
)
print("Vector store creato e salvato su disco")
10.3 Pipeline di Query
import os
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
os.environ["OPENAI_API_KEY"] = "sk-..."
# Carica il vector store esistente
embedding_model = OpenAIEmbeddings(
model="text-embedding-3-small"
)
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embedding_model,
collection_name="documenti_aziendali"
)
# Configura il retriever
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 5} # Recupera i 5 chunk più simili
)
# Definisci il prompt template
prompt_template = PromptTemplate(
template="""Sei un assistente tecnico esperto.
Rispondi alla domanda basandoti SOLO sul contesto fornito.
Se non trovi la risposta nel contesto, dillo esplicitamente.
CONTESTO:
{context}
DOMANDA: {question}
RISPOSTA (con citazioni ai documenti fonte):""",
input_variables=["context", "question"]
)
# Crea la chain RAG
llm = ChatOpenAI(
model="gpt-4o",
temperature=0 # Deterministic per risposte fattuali
)
rag_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # Inserisci tutti i chunk nel prompt
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={
"prompt": prompt_template
}
)
# Esegui una query
result = rag_chain.invoke(
{"query": "Come configuro l'autenticazione OAuth?"}
)
# Stampa la risposta
print("RISPOSTA:")
print(result["result"])
print("\nFONTI:")
for doc in result["source_documents"]:
print(f" - {doc.metadata.get('source', 'N/A')}"
f" (pagina {doc.metadata.get('page', 'N/A')})")
Output Atteso
RISPOSTA:
Per configurare l'autenticazione OAuth nella nostra applicazione,
segui questi passaggi:
1. Registra l'app nel provider OAuth (Google, GitHub, etc.)
2. Configura i redirect URI nel file config.yaml [Fonte: guida-oauth.pdf]
3. Implementa il flusso Authorization Code [Fonte: architettura-auth.pdf]
...
FONTI:
- documenti/guida-oauth.pdf (pagina 12)
- documenti/architettura-auth.pdf (pagina 5)
- documenti/faq-sicurezza.pdf (pagina 3)
10.4 Struttura del Progetto
rag-project/
├── documenti/ # I tuoi documenti sorgente
│ ├── guida-oauth.pdf
│ ├── architettura.pdf
│ └── faq.pdf
├── chroma_db/ # Vector store persistente (generato)
├── indexing.py # Script di indicizzazione
├── query.py # Script di query
├── requirements.txt # Dipendenze
└── .env # API keys (mai committare!)
11. Naive RAG vs Advanced RAG
Il sistema che abbiamo costruito finora è quello che la comunita chiama Naive RAG: un'implementazione lineare e semplice che funziona sorprendentemente bene per molti casi d'uso. Tuttavia, esistono limitazioni che le tecniche di Advanced RAG cercano di superare.
11.1 Limitazioni del Naive RAG
- Query-Document Mismatch: La domanda dell'utente potrebbe usare termini diversi dai documenti. "Come resettare la password?" vs documento "Procedura di ripristino credenziali"
- Chunk Boundary Issues: L'informazione rilevante potrebbe essere spezzata tra due chunk consecutivi
- Lost in the Middle: L'LLM tende a dare più peso ai chunk all'inizio e alla fine del contesto, trascurando quelli centrali
- Single-Hop Limitation: Non può rispondere a domande che richiedono sintesi da più documenti
11.2 Tecniche di Advanced RAG
Evoluzione da Naive a Advanced RAG
| Tecnica | Problema Risolto | Come Funziona |
|---|---|---|
| Query Rewriting | Query ambigue o mal formulate | L'LLM riscrive la query in una forma più adatta al retrieval |
| HyDE | Query-Document mismatch | Genera un documento ipotetico dalla query, poi cerca documenti simili |
| Reranking | Risultati non ottimalmente ordinati | Un modello cross-encoder ri-ordina i risultati per rilevanza |
| Multi-Query | Una sola prospettiva di ricerca | Genera varianti della query per ampliare la copertura |
| Self-RAG | Quando il retrieval non è necessario | Il modello decide autonomamente se e quando fare retrieval |
| Multi-Hop RAG | Domande complesse multi-step | Catena di retrieval iterativi per costruire ragionamenti composti |
| Hybrid Search | Limiti della sola ricerca semantica | Combina ricerca semantica (vettoriale) con keyword (BM25) |
| Graph RAG | Relazioni complesse tra entità | Usa knowledge graph per navigare relazioni e contesti strutturati |
NAIVE RAG:
Query ──> Embedding ──> Search ──> Top-K ──> LLM ──> Risposta
ADVANCED RAG:
Query ──> [Query Analysis]
│
├──> Query Rewriting ──> Embedding ──> Search ─┐
├──> HyDE Generation ──> Embedding ──> Search ─┤
└──> Multi-Query ──────> Embedding ──> Search ─┘
│
[Merge + Deduplica]
│
[Reranker Model]
│
[Context Compression]
│
[LLM + Citazioni]
│
[Risposta Verificata]
Approfondiremo ciascuna di queste tecniche negli articoli successivi della serie, in particolare nell'articolo su Hybrid Retrieval e Reranking.
12. Quando Usare RAG: Decision Framework
RAG non è la risposta a tutti i problemi. Esistono tre approcci principali per personalizzare il comportamento di un LLM, ciascuno con vantaggi e limiti distinti. La scelta dipende dal caso d'uso specifico.
RAG vs Fine-Tuning vs Prompt Engineering
| Criterio | Prompt Engineering | RAG | Fine-Tuning |
|---|---|---|---|
| Costo iniziale | Minimo | Medio | Alto |
| Dati necessari | Nessuno | Knowledge base | Dataset di training |
| Aggiornamento | Immediato (cambio prompt) | Veloce (aggiorna documenti) | Lento (re-training) |
| Latenza | Bassa | Media (retrieval + generation) | Bassa |
| Accuratezza fattuale | Dipende dal modello | Alta (fondata su documenti) | Media (dipende dal training) |
| Citabilità | Nessuna | Alta (traccia le fonti) | Nessuna |
| Complessità | Bassa | Media | Alta |
12.1 Decision Tree Pratico
Hai bisogno di personalizzare un LLM?
│
├── I dati cambiano frequentemente? (documenti, policy, catalogo)
│ └── SI ──> RAG
│ (I dati si aggiornano senza re-training)
│
├── Serve citare le fonti?
│ └── SI ──> RAG
│ (Tracciabilita delle risposte)
│
├── Il dominio è stabile e ben definito?
│ ├── Hai un grande dataset di training?
│ │ └── SI ──> Fine-Tuning
│ │ (Personalizzazione profonda del comportamento)
│ └── NO ──> RAG oppure Prompt Engineering
│
├── Serve solo cambiare tono/formato/stile?
│ └── SI ──> Prompt Engineering
│ (Nessuna infrastruttura aggiuntiva)
│
└── Serve ragionamento complesso su dati proprietari?
└── SI ──> RAG + Fine-Tuning (Approccio Ibrido)
(Il meglio di entrambi i mondi)
La Regola d'Oro
L'approccio consigliato dalla comunita AI engineering segue una scala di complessità crescente: inizia con il prompt engineering, poi passa a RAG se servono dati esterni o citazioni, e considera il fine-tuning solo quando i primi due approcci risultano insufficienti. Questa progressione minimizza il costo e la complessità, massimizzando il ROI.
12.2 Casi d'Uso Ideali per RAG
- Chatbot aziendali: Rispondono sulla base della documentazione interna
- Ricerca semantica: Trovare documenti rilevanti per significato, non solo keyword
- Q&A su knowledge base: FAQ dinamiche che si aggiornano con i documenti
- Assistenti legali: Risposte fondate su normative e giurisprudenza
- Supporto tecnico: Risoluzioni basate su ticket precedenti e manuali
- Analisi documentale: Estrarre insight da report, contratti, paper scientifici
12.3 Quando NON Usare RAG
- Task creativi: Scrittura creativa, brainstorming, generazione di idee (il modello deve essere libero)
- Conversazione generica: Chatbot sociali dove non servono dati specifici
- Task con pochi dati: Se hai solo pochi documenti, il prompt engineering potrebbe bastare
- Risposte in tempo reale stretto: Se la latenza del retrieval e inaccettabile (sotto i 50ms)
13. Il Mercato RAG: Numeri e Tendenze
RAG non e più un concetto accademico: e diventato un'architettura di produzione adottata su larga scala. I numeri del mercato confermano la sua importanza strategica.
RAG in Numeri (2024-2030)
| Metrica | Dato |
|---|---|
| Mercato globale RAG (2024) | ~1.2 miliardi USD |
| Proiezione mercato (2030) | ~11 miliardi USD |
| Tasso di crescita annuo (CAGR) | 49.1% (2025-2030) |
| Adozione enterprise | 30-60% dei casi d'uso LLM utilizza RAG |
| Riduzione allucinazioni | Fino al 71% con RAG ben implementato |
| Framework dominanti | 80.5% usa FAISS o Elasticsearch |
I settori che guidano l'adozione sono legale, medico, supporto clienti e servizi finanziari, cioe tutti quei domini dove l'accuratezza fattuale e la citabilità delle fonti sono requisiti non negoziabili. La tendenza nel 2025-2026 è il passaggio dalla sperimentazione alla produzione su larga scala, con un'enfasi crescente su compliance, monitoraggio e qualità dei dati.
14. Conclusioni e Prossimi Passi
In questo articolo abbiamo costruito una comprensione completa di RAG: dal problema che risolve (le allucinazioni degli LLM) all'architettura end-to-end, passando per ogni singolo componente della pipeline. Abbiamo visto un'implementazione pratica funzionante e abbiamo confrontato RAG con le alternative (fine-tuning, prompt engineering).
Riepilogo dei Concetti Chiave
- RAG combina retrieval e generation per produrre risposte fondate su dati reali
- La pipeline di indicizzazione trasforma documenti in vettori: Loading, Chunking, Embedding, Storage
- La pipeline di query trova e usa i documenti rilevanti: Embedding, Search, Assembly, Generation
- Il chunking è una delle decisioni più critiche: influenza direttamente la qualità del retrieval
- Gli embeddings catturano il significato semantico del testo in vettori numerici
- I vector store rendono possibile la ricerca per similarità su milioni di documenti
- Advanced RAG introduce tecniche come reranking, HyDE e multi-hop per superare i limiti del Naive RAG
- RAG è ideale quando servono risposte aggiornate, verificabili e citabili
Prossimo Articolo: Embeddings e Ricerca Semantica
Nel prossimo articolo della serie approfondiremo il componente più affascinante di RAG: gli embeddings. Vedremo come funzionano internamente, come scegliere il modello giusto, come valutarne la qualità e come ottimizzare la ricerca semantica. Esploreremo anche le metriche di valutazione e le tecniche di benchmarking per misurare le performance del tuo sistema di retrieval.
Serie AI Engineering e RAG Avanzato
| Articolo | Argomento |
|---|---|
| 01 - Sei qui | RAG: Retrieval-Augmented Generation Spiegato |
| 02 - Prossimo | Embeddings e Ricerca Semantica in Profondità |
| 03 | Vector Database: Architettura e Best Practices |
| 04 | Costruire un Sistema RAG con LangChain e Python |
| 05 | Hybrid Retrieval: Keyword + Semantica + Reranking |
| 06 | Context Window e Prompt Engineering per RAG |
| 07 | RAG in Produzione: Monitoring, Evaluation, Scaling |
| 08 | Knowledge Graphs e Retrieval Strutturato |
| 09 | Multi-Agent Systems e RAG Orchestrato |
| 10 | Il Futuro del RAG: Tendenze e Ricerca |







