Vai al contenuto
AI.info

Approfondimenti tecnici

Generazione aumentata tramite recupero: architettura e buone pratiche

Una guida completa alla progettazione e all’ottimizzazione dei sistemi RAG. Tratta le strategie di chunking, la scelta degli embedding, gli algoritmi di recupero, il reranking e la messa in produzione.

Generazione aumentata tramite recupero: architettura e buone pratiche

AI.info Team ·

Perché esiste il RAG e dove fallisce

Un modello con capacità di ragionamento perfette ma senza alcun accesso ai tuoi dati è inutile per la maggior parte delle domande aziendali. La generazione aumentata tramite recupero serve a colmare questa lacuna: invece di riaddestrare o sottoporre a fine-tuning un modello ogni volta che cambia la tua base di conoscenza, recuperi i passaggi pertinenti al momento della richiesta e li fornisci al modello come contesto.

L’idea risale al paper presentato da Lewis et al. a NeurIPS nel 2020, «Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks», che combinava un sistema di recupero di passaggi basato su rappresentazioni dense con un generatore sequence-to-sequence e mostrava risultati migliori dei modelli puramente parametrici nelle domande e risposte su argomenti aperti. Sei anni dopo, l’idea di fondo è rimasta la stessa, ma la progettazione dei sistemi che la circondano è diventata una disciplina a sé, con modalità di errore distinte in ogni fase: acquisizione, chunking, creazione degli embedding, indicizzazione, recupero, reranking e generazione.

La maggior parte dei team che afferma «il nostro sistema RAG non funziona» in realtà non ha diagnosticato nulla: il problema potrebbe trovarsi in una qualsiasi di queste sette fasi, e ciascuna richiede una soluzione diversa. Questo articolo percorre la pipeline fase per fase, illustrando le tecniche e i numeri specifici che distinguono una demo da un sistema in produzione.

Il livello di acquisizione e chunking

Il chunking determina quale unità di testo viene trasformata in embedding e recuperata. Se si sbaglia qui, nessun accorgimento nelle fasi successive può recuperare le informazioni perse.

Il chunking a dimensione fissa (per esempio, 512 token con una sovrapposizione del 10-15%) è l’impostazione predefinita di RecursiveCharacterTextSplitter di LangChain e SentenceSplitter di LlamaIndex. È economico e prevedibile, ma spezza indiscriminatamente frasi, tabelle e sezioni logiche. Se si suddivide un 10-Q finanziario secondo confini fissi, si può facilmente separare una tabella dei ricavi dalle intestazioni delle colonne.

Il chunking semantico raggruppa le frasi in base alla somiglianza dei loro embedding, creando una separazione quando la divergenza tra frasi adiacenti supera una soglia. Rispetta meglio i confini tra argomenti, ma richiede un passaggio di creazione degli embedding solo per decidere dove tagliare e può produrre chunk di dimensioni molto disomogenee.

Il chunking basato sulla struttura usa gli elementi strutturali del documento — intestazioni Markdown, tag HTML, blocchi del layout PDF — per delimitare i chunk. Per i corpora strutturati (siti di documentazione, contratti, documenti depositati presso la SEC) è di solito l’opzione con il maggiore impatto, perché preserva le unità semantiche definite dall’autore.

Il problema più profondo, comune a tutti e tre gli approcci, è che un chunk isolato perde il contesto del documento da cui proviene. Un chunk che dice soltanto «I ricavi sono cresciuti del 12% nel segmento» è inutile se non si sa di quale azienda, trimestre e segmento si tratti. Due tecniche affrontano direttamente questo problema:

Ogni tecnica aggiunta riduce ulteriormente il tasso di recuperi falliti nel benchmark di Anthropic.

Entrambi gli approcci rispondono alla stessa causa di fondo: gli embedding dei singoli chunk sono accurati a livello locale, ma non vedono il contesto complessivo.

Modelli di embedding: scegliere l’encoder

Il modello di embedding determina che cosa significhi «simile» nel proprio spazio vettoriale, e il settore è progredito così in fretta che un modello scelto nel 2024 probabilmente non è più la scelta migliore nel 2026. Nella classifica MTEB (Massive Text Embedding Benchmark), text-embedding-3-large di OpenAI — il cavallo di battaglia di una quota enorme dei sistemi RAG in produzione dal suo lancio nel gennaio 2024 — è ancora il modello più recente di OpenAI a settembre 2026, mentre text-embedding-ada-002 è contrassegnato come legacy. Nella maggior parte dei benchmark di retrieval è stato superato da modelli più recenti: voyage-3-large di Voyage AI, i modelli Gemini Embedding di Google e modelli a pesi aperti come la serie Qwen3-Embedding di Alibaba e la linea v5 di Jina, le cui varianti omni, da maggio 2026, incorporano testo, immagini, audio, video e PDF in un unico spazio condiviso.

Qwen3-Embedding-8B era in testa alla classifica multilingue MTEB a metà del 2025 con un punteggio di 70,58, ed è distribuito con una licenza aperta: un aspetto importante se i requisiti di residenza dei dati impongono di ospitarlo sui propri server. Le prime posizioni di quella classifica cambiano ogni pochi mesi e oggi comprende più di mille modelli: qualsiasi posizione specifica — compresa quella appena citata — va quindi letta come una fotografia datata, non come una raccomandazione valida nel tempo.

La decisione pratica non è «quale modello è in cima alla classifica»: MTEB aggrega risultati relativi a compiti che potrebbero non assomigliare al vostro carico di lavoro di retrieval. Le domande da porsi sono queste:

  1. La distribuzione dei dati di addestramento del modello assomiglia a quella del vostro ambito (giuridico, codice, biomedico, multilingue)?
  2. Qual è la lunghezza massima dell’input? Supera la dimensione dei vostri chunk con un margine sufficiente?
  3. La dimensionalità dei vettori, e quindi la memoria occupata dall’indice, è accettabile per le dimensioni del vostro corpus? I modelli addestrati con la tecnica Matryoshka, come text-embedding-3-large, consentono di ridurre il numero di dimensioni con un degrado graduale delle prestazioni: una leva concreta per contenere i costi.
  4. Potete sostenere la latenza delle API e il costo per token ai vostri volumi di acquisizione, oppure vi serve un encoder ospitato sui vostri server?

Ricalcolate gli embedding del vostro set di valutazione ogni volta che prendete in considerazione un cambio di modello: i cambiamenti del modello di embedding non sono necessariamente retrocompatibili, e mescolare nello stesso indice vettori prodotti da modelli incompatibili compromette la ricerca dei vicini più prossimi senza dare segnali evidenti.

Archiviare e cercare i vettori

Una volta ottenuti gli embedding, serve un indice che restituisca rapidamente i vicini più prossimi approssimati. Dominano due famiglie di indici:

Quanto alla scelta dell’infrastruttura, pgvector trasforma Postgres in un archivio vettoriale: una soluzione interessante se usate già Postgres e volete riunire ricerca vettoriale, filtri sui metadati e scritture transazionali in un unico sistema, invece di sincronizzare due database. Le versioni recenti di pgvector hanno ridotto notevolmente il divario di prestazioni rispetto ai database vettoriali dedicati: la costruzione degli indici HNSW è diventata molto più rapida con la versione 0.6.0, che ha introdotto la costruzione parallela. FAISS è una libreria, non un servizio: la integrate nel vostro codice di retrieval e gestite voi stessi la persistenza, una soluzione adatta ai team che vogliono il pieno controllo dei parametri di indicizzazione. I database vettoriali gestiti (Pinecone, Weaviate, Qdrant, Milvus) scambiano questo controllo con una maggiore semplicità operativa: si occupano loro di replica, scalabilità orizzontale e filtri.

Un indice FAISS minimale per qualche centinaio di migliaia di chunk si presenta così:

import faiss
import numpy as np

dim = 1024  # matches your embedding model's output dimension
quantizer = faiss.IndexFlatIP(dim)
index = faiss.IndexIVFPQ(quantizer, dim, 1024, 16, 8)  # 1024 clusters, 16-byte codes
index.train(training_vectors)   # a representative sample, e.g. 50-100k vectors
index.add(all_vectors)
index.nprobe = 16  # cells searched at query time; trade recall for speed
distances, ids = index.search(query_vector, k=20)

Ricerca ibrida: non abbandonate la corrispondenza per parole chiave

Il retrieval basato esclusivamente su vettori densi offre sistematicamente prestazioni inferiori nelle query che richiedono corrispondenze esatte: codici SKU dei prodotti, codici di errore, citazioni giuridiche, nomi propri rari. Un encoder denso addestrato a cogliere la somiglianza semantica non ha un forte incentivo a distinguere «fattura n. 48213» da «fattura n. 48214»: sono quasi identiche sul piano semantico, ma a voi serve proprio quella esatta. Per questo i sistemi RAG in produzione affiancano quasi sempre al retriever denso un retriever lessicale (BM25, o SPLADE, il suo parente basato su rappresentazioni sparse apprese) e fondono le due liste ordinate per rilevanza.

Reciprocal Rank Fusion (RRF) è il metodo di fusione standard proprio perché aggira il problema della normalizzazione dei punteggi: quelli di BM25 non hanno un limite superiore e dipendono dal corpus, mentre le similarità coseno sono comprese tra -1 e 1, e non esiste un modo fondato per combinarli direttamente. RRF ignora l’entità dei punteggi e combina i documenti soltanto in base alla posizione in classifica, usando la formula:

RRF_score(d) = Σ  1 / (k + rank_i(d))

sommata per ciascun retriever i, con k di norma impostato intorno a 60. Nei benchmark pubblicati, il retrieval ibrido con RRF può ottenere risultati inferiori al retrieval denso in termini di NDCG, ed è supportato nativamente da Elasticsearch, OpenSearch, Weaviate e Qdrant.

Reranking: il filtro del secondo passaggio

Il recupero dei documenti e il reranking risolvono problemi diversi. Un bi-encoder (l’architettura alla base della maggior parte dei modelli di embedding) codifica le query e i documenti indipendentemente: è questo che lo rende abbastanza veloce da cercare tra milioni di vettori, ma tale indipendenza fa anche perdere le informazioni sulle interazioni tra query e documento. Un cross-encoder reranker riceve la query e un documento candidato insieme, come un unico input, e produce un punteggio di pertinenza per ciascuna coppia. Riesce così a cogliere interazioni che sfuggono a un bi-encoder, ma è troppo lento per essere applicato a un intero corpus.

Lo schema standard è questo: prima si recupera un ampio insieme di candidati (50-100 porzioni di testo) con il meno costoso passaggio bi-encoder o ibrido, poi si applica il reranking per ridurre la selezione alle 5-10 porzioni che entrano effettivamente nel prompt. All’inizio di ottobre 2026, il modello gestito di punta di Cohere era rerank-v4.0-pro, un modello multilingue ottimizzato per la qualità su documenti in inglese, in altre lingue e in formato JSON semistrutturato, affiancato da rerank-v4.0-fast, pensato per una bassa latenza e un’elevata capacità di elaborazione; rerank-v3.5 e i modelli v3.0 per l’inglese e multilingui sono ancora disponibili. Il modello open source bge-reranker-v2-m3 di BAAI (Apache 2.0, 100+ lingue) assegna ancora un punteggio a una selezione ristretta in 50-100 ms su una GPU. Il 27 luglio 2026 Jina è passata a jina-reranker-v3.5 — 0,6 miliardi di parametri, un contesto di 131K token, assegnazione dei punteggi all’intera lista — mantenendo disponibile accanto a esso il modello v3 pubblicato nell’ottobre 2025.

Nel benchmark di Anthropic sul recupero contestuale, il reranking è stata la fase che ha prodotto il maggiore miglioramento marginale: aggiunto agli embedding contestuali e a BM25, ha portato la riduzione dei recuperi non riusciti dal 49% al 67%.

Reranker Tipo Note
Cohere rerank-v4.0-pro / -fast Cross-encoder gestito Pro per la qualità, fast per la capacità di elaborazione; multilingue, gestisce JSON
BAAI bge-reranker-v2-m3 Open source, Apache 2.0 50-100 ms su GPU, 100+ lingue
Jina jina-reranker-v3.5 Aperto, assegnazione dei punteggi all’intera lista Contesto di 131K token, 0,6B parametri (luglio 2026)

Oltre la RAG ingenua: architetture a grafo e autocorrettive

La RAG standard risponde alle domande quando il fatto pertinente si trova in una o due porzioni di testo recuperabili. Fallisce invece sulle domande che richiedono di sintetizzare informazioni distribuite in un intero corpus: per «quali sono i temi principali di questa raccolta di documenti» non esiste un singolo passaggio che contenga la risposta.

GraphRAG, di Microsoft Research (Edge et al., «From Local to Global: A Graph RAG Approach to Query-Focused Summarization», arXiv:2404.16130, aprile 2024), affronta il problema costruendo un grafo della conoscenza sul corpus: estrae entità e relazioni con un LLM, raggruppa il grafo in comunità gerarchiche usando l’algoritmo Leiden e genera in anticipo sintesi delle comunità. Al momento della query, a una richiesta di «ricerca globale» si risponde sintetizzando le informazioni delle comunità pertinenti, anziché cercando tra le porzioni di testo grezze.

Microsoft ha segnalato risposte generate più complete e diversificate rispetto alla RAG ingenua, al prezzo di un processo di indicizzazione molto più costoso e complesso: l’estrazione del grafo dall’intero corpus non è un’operazione da ripetere con leggerezza.

Altre due architetture affrontano direttamente la qualità del recupero, anziché la sintesi a livello di corpus:

Vale la pena adottare entrambe in modo selettivo: aggiungono latenza e complessità e sono più adatte agli ambiti in cui gli errori di recupero hanno un costo elevato (legale, medico, finanziario), anziché a un’applicazione universale.

Valutazione: misurare l’intero processo, non solo la risposta

L’errore più comune nella valutazione della RAG è controllare soltanto se la risposta finale sembri corretta. Questo confonde due possibili cause di errore indipendenti: il recupero ha trovato i passaggi giusti e la generazione li ha usati fedelmente? RAGAS, un framework open source di valutazione in Python, è costruito proprio attorno a questa distinzione e usa un LLM come giudice per assegnare punteggi secondo diversi criteri:

Misurare separatamente queste quattro metriche trasforma un vago «il sistema RAG non sembra funzionare bene» in una diagnosi su cui si può intervenire: una bassa copertura del contesto indica che occorre correggere la suddivisione del testo o il sistema di recupero; un’alta copertura del contesto accompagnata da una bassa fedeltà indica invece che la fase di generazione ignora o contraddice il contesto che ha recuperato, un problema di prompt, non di recupero.

Modalità di errore da mettere in conto

Anche una pipeline ben costruita tende a peggiorare in modi prevedibili:

RAG e contesti lunghi: uno non sostituisce l’altro

Ora che le finestre di contesto sono arrivate a contenere centinaia di migliaia di token, torna spesso una domanda: il RAG serve ancora? Perché recuperare i contenuti, se si può inserire l’intero corpus nel prompt? Ci sono due ragioni per non farlo. La prima è l’effetto delle informazioni perse nel mezzo: una finestra di contesto più ampia non garantisce che il modello usi davvero tutto ciò che contiene. Il recupero è un modo per selezionare ciò a cui prestare attenzione, non soltanto un espediente per aggirare i limiti di una finestra piccola.

La seconda è che costi e latenza crescono con il numero di token elaborati, indipendentemente dal limite di contesto. Recuperare 10 chunk pertinenti da un corpus di un milione di documenti è più economico e veloce che rielaborare l’intero corpus per ogni query, ed è un approccio che funziona anche oltre la quantità di testo che può entrare in qualunque finestra di contesto. In pratica, i due approcci si completano: un contesto lungo permette di suddividere i documenti in modo meno aggressivo e di recuperare meno passaggi, ma non elimina la necessità di trovare prima la parte pertinente di un corpus ampio.

Costruire una pipeline che regga in produzione

I team che riescono a far funzionare il RAG in modo affidabile finiscono per adottare alcune abitudini: valutano separatamente recupero e generazione invece di limitarsi a esaminare le risposte finali, combinano BM25 e recupero denso invece di puntare tutto sugli embedding, riordinano i risultati prima della generazione invece di fidarsi della sola similarità vettoriale e considerano la suddivisione in chunk una scelta progettuale fondamentale, legata alla struttura dei documenti, anziché un’impostazione predefinita a cui pensare per ultima.

Niente di tutto questo è insolito: si tratta di applicare con rigore tecniche ormai ben documentate e sempre più spesso valutate con benchmark. La differenza tra una demo RAG e un sistema RAG che regge un anno di query di utenti reali sta quasi interamente nel rigore con cui ciascuna di queste fasi viene messa alla prova rispetto alle modalità di errore descritte sopra, non nella scoperta di qualche trucco sconosciuto.

Esplora

Altri articoli