Il vero collo di bottiglia di un sistema RAG non è il retrieval: è la qualità dell'ingestion. Prima di cambiare libreria o aggiungere un knowledge graph, analizza il corpus: struttura dei documenti, granularità corretta dei chunk, metadati necessari. Il metodo si adatta ai dati — non il contrario. Ogni tipo di documento richiede una strategia diversa.
La domanda sbagliata
C'è un dibattito che tiene occupata la community AI da mesi. Da un lato chi sostiene che i knowledge graph strutturali siano il futuro del retrieval. Dall'altro chi difende il vector RAG classico. In mezzo, nuove specifiche markdown che promettono di risolvere tutto con file di testo versionati su Git. Ogni settimana una nuova libreria con 30.000 star e un benchmark costruito su un test set di 50 file.
La domanda sottostante — qual è il metodo migliore? — è formulata male. Non esiste un metodo migliore in assoluto. Esiste un metodo adatto a uno specifico tipo di corpus, con una specifica morfologia, usato per rispondere a uno specifico tipo di domanda. Chi cerca la risposta universale è già sulla strada sbagliata.
Ma c'è una questione ancora più a monte, che questo dibattito ignora quasi sempre: prima di scegliere il metodo di retrieval, bisognerebbe chiedersi come i dati vengono preparati. L'ingestion. Il preprocessing. Il chunking. I metadati. Tutto ciò che avviene prima che il primo documento arrivi al vector store.
Il loop del sacro graal
Riconosco questo pattern abbastanza spesso. Un team decide di costruire un sistema RAG. Installa LangChain, o LlamaIndex, o uno dei suoi successori. Segue un tutorial. Ottiene risultati discreti sul documento di esempio del tutorial. Prova con i propri documenti. I risultati peggiorano. Il sistema recupera testo irrilevante, risponde con informazioni che non corrispondono alla domanda, allucinando dettagli che non esistono nel corpus.
A questo punto, la diagnosi quasi universale è: il metodo non è quello giusto. Si guarda cosa sta usando la community. Si scopre che qualcuno su Hacker News suggerisce un approccio ibrido. Si aggiunge il reranking. Non basta. Si prova un grafo. Non è ancora quello. Si torna su GitHub a cercare la libreria con più star.
Il sacro graal non arriva mai. Non perché non esista un sistema RAG che funziona — esistono eccome. Ma perché il problema reale non è nel retrieval. È in quello che è successo prima.
«Il miglior retriever non può restituire la risposta corretta se la risposta è stata distrutta durante il chunking.»
Dove nascono davvero gli errori
Prendete un contratto di fornitura di dieci pagine. Divisione meccanica a 512 token. Al confine del secondo chunk cadono le condizioni di pagamento: metà dell'articolo 4 finisce nel chunk 2, l'altra metà nel chunk 3. Nessun chunk contiene informazioni complete su questo punto. La query "quali sono i termini di pagamento?" recupera entrambi i chunk, ma il modello non riesce a rispondere in modo preciso perché il contesto è frammentato. Il sistema "allucinisce" — non perché il modello sia scarso, ma perché l'informazione non era recuperabile in quella forma.
Cambiate la libreria di retrieval: il problema non si sposta di un millimetro. Il chunk è ancora spezzato nello stesso punto sbagliato. Aggiungete un knowledge graph strutturale: il grafo rappresenterà fedelmente che esiste un articolo 4, ma il contenuto dell'articolo 4 è ancora frammentato nei vettori sottostanti. Il problema è nell'ingestion, e nessun layer di retrieval sopra può compensarlo.
Questo è il principio che governa qualsiasi pipeline RAG: garbage in, garbage out — ma applicato a un livello di granularità che la maggior parte dei tutorial non tocca nemmeno.
I dati non sono tutti uguali
Il chunking a dimensione fissa — "512 token con 50 di overlap" — è un default ragionevole per niente. È un parametro che presuppone che tutti i documenti abbiano la stessa struttura semantica, la stessa densità informativa, lo stesso tipo di risposta attesa. Nessun corpus reale ha queste caratteristiche.
Ogni tipo di documento ha una morfologia propria che determina come dovrebbe essere segmentato, arricchito e indicizzato.
Il chunk testuale a token fissi distrugge la struttura sintattica. Una funzione spezzata a metà perde il suo significato. L'unità minima è la funzione o la classe — non il token. Un AST parser che rispetta i confini sintattici produce chunk semanticamente coerenti. Il contesto dei file importati è parte del significato.
L'articolo è l'unità minima, non il paragrafo. Chunk grandi con overlap generoso (30–40%). I rimandi interni — "come indicato all'articolo 3.2" — vanno risolti: se il chunk 7 fa riferimento al chunk 2, il sistema deve saperlo. I metadati di sezione (numero articolo, titolo) sono parte dell'informazione.
La coppia domanda-risposta è l'unità atomica. Separarle nel chunking è un errore di design che produce chunk inutilizzabili. Chunk piccoli, senza overlap. I metadati di categoria e sotto-categoria sono segnale forte per il retrieval: vanno aggiunti, non ignorati.
Il threading è informazione: una risposta fuori contesto dal thread originale perde metà del suo significato. La deduplicazione è critica — le email aziendali contengono 3-5x di ridondanza (reply chain, firme, disclaimer). La sequenza temporale non è decorazione: è struttura.
Le istruzioni step-by-step non vanno spezzate tra chunk diversi: un passaggio preso fuori sequenza è pericoloso o inutile. I numeri dei passi sono semantica, non formattazione. Le sezioni di avvertenza e prerequisiti appartengono ai chunk degli step che presuppongono, non solo al capitolo introduttivo.
Una tabella spezzata in due chunk produce due frammenti senza intestazioni — entrambi inutili. Le tabelle vanno serializzate come testo strutturato (riga per riga con etichette di colonna) o estratte come unità indipendenti. Il numero di pagina e il titolo del report sono metadati obbligatori, non opzionali.
Questo non è un elenco esaustivo. È un'illustrazione del principio: ogni corpus ha una struttura semantica propria, e il chunking deve rispettarla — non ignorarla per convenienza implementativa.
Conoscere il proprio corpus prima di scegliere qualsiasi tool
Prima di installare qualsiasi libreria, prima di scegliere tra RAG classico e knowledge graph, prima di guardare benchmark su dataset che non assomigliano ai propri, c'è un lavoro di analisi che non è opzionale. Si tratta di capire la morfologia del corpus su cui si sta costruendo.
Un corpus con documenti da 200 parole e uno con documenti da 80 pagine richiedono strategie di chunking radicalmente diverse. La varianza alta è un segnale di eterogeneità — probabilmente ci sono sottocorpora con esigenze diverse.
Documenti piatti (FAQ, email singole) si chunkano diversamente da documenti gerarchici (manuali con capitoli, sezioni e sottosezioni). In un documento gerarchico, la posizione nel documento è informazione semantica che va preservata nei metadati del chunk.
I corpus aziendali reali contengono spesso 30–60% di contenuto ridondante: versioni precedenti degli stessi documenti, email con reply chain complete, policy con revisioni storiche. La deduplicazione prima dell'ingestion non è un'ottimizzazione — è un requisito per un retrieval affidabile.
Un corpus statico (libri, manuali storici) si ingesta una volta. Un corpus dinamico (prezzi, normative, schede prodotto) richiede una pipeline di aggiornamento incrementale con invalidazione dei chunk obsoleti. Un sistema RAG su dati che cambiano senza aggiornamento del vector store è peggio di nessun sistema — risponde con certezza su informazioni vecchie.
Le domande fattuali precise ("qual è il termine di pagamento nel contratto X?") richiedono chunk piccoli e specifici. Le domande concettuali ("come funziona il processo di approvazione?") richiedono chunk più grandi con più contesto. Se il corpus deve rispondere a entrambi i tipi, serve una strategia multi-livello — non un parametro unico.
Il preprocessing che cambia i risultati
Analizzato il corpus, si progetta il preprocessing. Non si tratta di pulizia cosmetica: le operazioni che seguono hanno impatto diretto sulla qualità del retrieval.
- Rimozione del rumore strutturale. Header e footer ripetuti in ogni pagina (numero di pagina, nome del documento, logo come alt text), watermark, firme standardizzate: vengono embeddati e inquinano il semantic space. Vanno rimossi prima, non filtrati dopo.
- Normalizzazione del testo. Spaziatura irregolare, caratteri speciali, encoding inconsistenti tra documenti diversi — particolarmente comuni in corpus provenienti da OCR su documenti scansionati. Un embedding model non distingue un errore di normalizzazione da un termine raro.
- Rispetto dei confini semantici nel chunking. I confini semantici — fine di paragrafo, fine di articolo, fine di funzione — sono più importanti del numero di token. Un chunk che rispetta i confini semantici naturali è più utile di uno a dimensione fissa che finisce a metà frase.
- Arricchimento dei metadati. Titolo del documento, sezione, data, autore, tipo di documento, versione — questi campi non vanno nell'embedding: vanno nei metadati del chunk per abilitare il filtering al momento del retrieval. Un sistema che recupera tutto e poi filtra è meno preciso di uno che filtra per metadati prima di fare il retrieval vettoriale.
- Gestione dei documenti correlati. Se i documenti si riferiscono l'uno all'altro esplicitamente — contratto e allegati, policy e procedure operative, codice e documentazione — quelle relazioni vanno catturate come metadati, non perse nel chunking individuale.
Il metodo si sceglie dopo, non prima
Solo dopo aver fatto questa analisi e progettato il preprocessing ha senso scegliere l'architettura di retrieval. E a quel punto, spesso, la scelta è più semplice di quanto sembri.
Se il corpus è codice sorgente e le query riguardano relazioni tra componenti — un parser AST che rispetta i confini sintattici è la scelta ovvia, non per seguire un trend ma perché il codice ha una struttura sintattica che il chunking testuale ignora. Se il corpus è documentazione interna con convenzioni non scritte che il team conosce ma l'AI non può inferire dal testo — un layer di specifica esplicita e versionata ha senso. Se il corpus è un mix eterogeneo di testo libero — email, ticket, note — il vector RAG su dati ben preprocessati funziona meglio di qualsiasi architettura più elaborata su dati mal trattati.
Non esiste uno stack superiore in assoluto. Esiste lo stack adatto al problema specifico — e il problema specifico emerge dall'analisi del corpus, non dal benchmark di qualcun altro.
«Prima di aggiungere un layer di complessità, chiediti se il layer precedente funziona davvero. La maggior parte dei sistemi RAG che non funzionano non hanno bisogno di un knowledge graph. Hanno bisogno che i documenti vengano letti con attenzione prima di essere buttati nel vector store.»
La checklist prima di scegliere qualsiasi tool
Cinque domande. Se non hai risposto a tutte, il resto è prematuro.
- Hai analizzato la struttura del corpus — lunghezza, gerarchia, eterogeneità — prima di scegliere il chunk size?
- Hai identificato e rimosso la ridondanza (versioni multiple, reply chain, contenuto ripetuto strutturalmente)?
- Hai definito i metadati che ogni chunk deve portare con sé per abilitare il filtering al retrieval?
- La strategia di chunking rispetta i confini semantici naturali del tuo tipo di documento specifico?
- Hai una pipeline di aggiornamento per gestire i documenti che cambiano — o il vector store invecchierà silenziosamente?
Se la risposta a una di queste è no, è lì che si lavora. Non sulla libreria di retrieval, non sul modello di embedding, non sul knowledge graph. Sull'ingestion.
Il sistema RAG migliore non è quello con l'architettura più sofisticata. È quello costruito su una pipeline di ingestion progettata per il corpus specifico — con le stesse attenzioni che si darebbero alla progettazione del database in un sistema transazionale. I dati meritano altrettanta cura prima di diventare vettori quanto ne meritano dopo.
Se stai costruendo o valutando un sistema RAG per documenti aziendali e i risultati non ti convincono, spesso il problema è in questo livello — e si risolve prima di toccare qualsiasi altra variabile dello stack. È meno interessante da raccontare su GitHub, ma è dove si fa la differenza in produzione.