Preparare i dati per la Retrieval-Augmented Generation (RAG)
La Generazione Aumentata dal Recupero (RAG) combina l'IA generativa con i dati aziendali per migliorare la pertinenza e l'accuratezza delle risposte fornite dai modelli linguistici di grandi dimensioni (LLM). Tuttavia, senza una pipeline di dati RAG adeguata, il sistema RAG potrebbe non trovare i dati più precisi per rispondere alle query degli utenti. La qualità dei dati è di massima importanza nei sistemi RAG, poiché risposte errate o fuorvianti possono minare l'affidabilità e l'efficacia dell'intera applicazione.
Pertanto, la creazione di un'applicazione RAG di successo include una serie di attività di pre-elaborazione dei dati, fondamentali per ottimizzare il processo di recupero. Queste attività comprendono il filtraggio dei contenuti, la normalizzazione del testo, il chunking, il tagging dei metadati e la generazione di embedding. Ogni passaggio gioca un ruolo cruciale nel trasformare i dati grezzi in un formato che sia efficiente e significativo per i sistemi RAG.
Per garantire una corretta preparazione dei dati, i team di sviluppo RAG devono innanzitutto comprendere come RAG renda i dati ricercabili. Successivamente, devono esaminare attentamente le strategie di pre-elaborazione dei dati, in particolare i metodi di chunking, che determinano come i dati vengono suddivisi in unità gestibili. Infine, è essenziale imparare a costruire una pipeline di dati RAG robusta e automatizzata, che copra l'intero ciclo di vita dei dati, dalla selezione iniziale e pulizia all'embedding e all'archiviazione efficiente in un database vettoriale.
Come RAG rende i dati ricercabili
RAG aiuta i servizi di IA generativa ad accedere a dati esterni utilizzando un archivio vettoriale o un database dotato di specifici meccanismi di ricerca. Questo approccio consente agli LLM di attingere a informazioni aggiornate e specifiche dell'azienda, superando i limiti dei dati su cui sono stati inizialmente addestrati.
Per includere dati non strutturati – come ad esempio un documento di testo – in un archivio vettoriale, i team devono prima incorporare i dati tramite un modello di embedding. Un embedding è una rappresentazione numerica del testo. Converte il testo in vettori numerici, ovvero una lista di numeri decimali che rappresentano il testo, come parole o frasi, in un modo comprensibile per i computer. Questi vettori catturano il significato semantico del testo, consentendo ai sistemi di identificare la somiglianza tra diverse porzioni di testo.
Consideriamo, ad esempio, il seguente paragrafo da un documento:
Abbiamo introdotto Copilot la scorsa settimana e l'accettazione è eccellente. Le prime misurazioni mostrano una diminuzione del volume dei ticket del 32 percento e un aumento delle soluzioni self-service del 27 percento entro le prime 48 ore. Il reparto finanziario stima un risparmio annuale di 1,4 milioni di euro se questa tendenza dovesse continuare.
L'embedding di questo paragrafo in vettori che rappresentano il testo si presenta in una forma fortemente semplificata come segue:
[0,81, -0,23, 0,45, 0,12, -0,67, 0,94, 0,30, -0,55, 0,76, 0,10]
Questo è, come detto, un esempio altamente semplificato. Se i team utilizzano un modello di embedding più sofisticato, come il modello OpenAI text-embedding-3-small, questo produce un vettore a 1536 dimensioni, molto più complesso e ricco di informazioni semantiche. Esistono numerosi modelli di embedding disponibili da diversi fornitori, ognuno con i propri punti di forza e debolezze. Per aiutare gli sviluppatori a scegliere il modello più adatto, Hugging Face ha una classifica degli embedding che mostra le performance comparative di vari modelli di embedding sul mercato.
Questi vettori, insieme al testo originale associato, vengono memorizzati in un archivio vettoriale, che può essere una soluzione dedicata come Pinecone o Weaviate. I dati all'interno di un archivio vettoriale sono tipicamente strutturati in modo da facilitare una ricerca rapida basata sulla somiglianza vettoriale, come nel seguente esempio:
{
"id": "unique-id-123",
"values": [0,81, -0,23, ..., 0,789], // il vettore (embedding)
"metadata": {
"text": " Abbiamo introdotto Copilot la scorsa settimana e l'accettazione è eccellente ...",
"source": "Documento A",
"page": 3
}
}
Quando un utente desidera porre una domanda sui dati, invia il prompt tramite un orchestratore. Questo orchestratore – che può essere un framework come LangGraph, LangChain o Semantic Kernel – traduce la domanda dell'utente in un vettore di query e invia una richiesta all'archivio vettoriale. Utilizzando un metodo di ricerca basato sulla somiglianza vettoriale, il sistema trova e restituisce due o più parti di contenuto rilevante. Queste informazioni vengono quindi inoltrate al modello linguistico di grandi dimensioni (LLM), che le utilizza come contesto aggiuntivo per generare una risposta più informata e precisa per l'utente.
Perché il chunking è importante
Il chunking è un metodo standard di pre-elaborazione che consiste nel dividere un set di dati esteso in sezioni più piccole e gestibili, chiamate "chunk". Questa pratica è indispensabile perché gli archivi vettoriali, pur essendo efficienti, non possono memorizzare tutti i dati originali in un'unica unità massiccia, né un singolo embedding può rappresentare efficacemente l'intero contesto di un documento molto lungo. Di conseguenza, i dati devono essere suddivisi in chunk per ottimizzare l'archiviazione e il recupero.
Se, ad esempio, prendiamo il paragrafo su Copilot e lo suddividiamo arbitrariamente in due sezioni:
- Chunk 1: Abbiamo introdotto Copilot la scorsa settimana e l'accettazione è eccellente.
- Chunk 2: Le prime misurazioni mostrano una diminuzione del volume dei ticket del 32 percento e un aumento delle soluzioni self-service del 27 percento entro le prime 48 ore. Il reparto finanziario stima un risparmio annuale di 1,4 milioni di euro se questa tendenza dovesse continuare.
Il problema di questo approccio semplice è che alcune parti possono perdere il loro significato originale o la loro coerenza contestuale. Ad esempio, se un utente nel sistema RAG chiedesse informazioni specifiche sui "risparmi annuali di Copilot", il meccanismo di ricerca potrebbe restituire solo il primo chunk, non il secondo, poiché la correlazione diretta tra "Copilot" e "risparmi" è contenuta nel secondo chunk. Senza il secondo chunk, l'utente non riceverebbe il contesto aggiuntivo e le informazioni complete sull'accettazione e i benefici finanziari di Copilot. Questo esempio evidenzia come un chunking inadeguato possa portare a risposte incomplete o imprecise, rendendo il sistema RAG meno efficace.
È qui che le strategie di chunking mirate diventano fondamentali. Possono aiutare a scomporre i dati in modo che si adattino all'archivio vettoriale, ma soprattutto che mantengano il loro significato originale e la coerenza contestuale all'interno di un motore RAG. La scelta della strategia giusta è cruciale per massimizzare la rilevanza delle informazioni recuperate.
4 strategie di chunking
Senza strategie adeguate, i sistemi RAG possono scomporre i dati in modo errato, compromettendo la qualità delle risposte. Sono disponibili diversi metodi di chunking, ciascuno con i propri vantaggi e scenari di applicazione ideali. Le principali strategie includono il chunking a dimensione fissa, il chunking a dimensione variabile basato sul contenuto, il chunking basato su regole e il chunking a finestra scorrevole (sliding window).
- Chunking a dimensione fissa
Con questo metodo, il testo viene suddiviso in segmenti di dimensioni uguali indipendentemente dal contenuto. Ad esempio, ogni chunk potrebbe contenere esattamente 400 parole o 800 caratteri. Questo approccio è semplice da implementare ma può tagliare frasi o paragrafi a metà, potenzialmente spezzando il contesto e il significato. - Chunking a dimensione variabile basato sul contenuto
Qui il testo viene suddiviso in base a confini naturali, come i segni di fine frase, le interruzioni di riga o gli indizi strutturali. Questi confini possono essere identificati da strumenti di elaborazione del linguaggio naturale (NLP - Natural Language Processing) che analizzano il layout e il significato dei documenti. Questo metodo tende a preservare meglio l'integrità semantica dei chunk. - Chunking basato su regole
Questo approccio si basa sulla struttura intrinseca o sui confini linguistici del documento. Metodi tipici sono il chunking per frasi o paragrafi utilizzando regole predefinite. Ad esempio, si potrebbe decidere che ogni frase costituisce un chunk, o che ogni paragrafo è un chunk, purché non superi una certa lunghezza massima. - Chunking a finestra scorrevole (sliding window)
Questa tecnica genera chunk sovrapposti spostando una finestra di dimensione fissa attraverso il testo. Ad esempio, ogni chunk può contenere 500 parole, con il chunk successivo che inizia 300 parole più avanti. Questo significa che i chunk si sovrappongono di 200 parole. L'overlap aiuta a mantenere il contesto tra i chunk adiacenti, riducendo il rischio di perdere informazioni importanti ai margini del chunk.
Per il paragrafo di Copilot menzionato in precedenza, un approccio a finestra scorrevole o basato su regole sarebbe preferibile rispetto a un chunking arbitrario. Questi metodi garantirebbero che il paragrafo non perda il suo significato, poiché entrambi i contenuti (l'introduzione di Copilot e i suoi benefici misurati) sarebbero disponibili nello stesso chunk o in chunk sovrapposti, preservando la coesione contestuale.
6 passaggi della pipeline di dati RAG
Un altro aspetto cruciale da considerare nei sistemi RAG è l'aggiornamento continuo dei dati. Se, ad esempio, i dati originali sono un documento Word e non vengono regolarmente aggiornati prima di essere incorporati in un archivio vettoriale, i dati potrebbero perdere rapidamente valore, diventando obsoleti e inefficaci per generare risposte accurate. La freschezza dei dati è tanto importante quanto la loro qualità iniziale.
Inoltre, i documenti che non possono essere incorporati direttamente nel loro formato originale, come i file PDF scansionati o le presentazioni PowerPoint, devono essere convertiti in un formato più adatto al testo per essere elaborati e trasferiti nell'archivio vettoriale. Questo processo di conversione richiede spesso strumenti specifici per estrarre il testo mantenendo, per quanto possibile, la struttura e il contesto originali.
È quindi cruciale costruire una pipeline di dati che possa automatizzare il processo di embedding continuo di nuovi dati da diverse fonti e tipi di file nell'archivio vettoriale. Una pipeline robusta e ben progettata garantisce che il sistema RAG sia sempre alimentato con le informazioni più recenti e pertinenti. Una pipeline di dati RAG è generalmente composta dai seguenti sei passaggi:
- Selezione e acquisizione del corpus
In questa fase iniziale, si identificano e si raccolgono le fonti di dati e i contenuti più rilevanti in base al caso d'uso specifico del sistema RAG. È fondamentale scegliere dati che siano pertinenti, affidabili e sufficientemente ampi da coprire le possibili domande degli utenti. - Pre-elaborazione e analisi dei dati
Una volta acquisiti, i dati grezzi devono essere puliti e standardizzati per renderli pronti per l'embedding e il recupero. Questo include la rimozione di rumore, la correzione di errori e la conversione in un formato uniforme. Uno strumento frequentemente utilizzato per questo scopo è MarkItDown, che supporta formati come PDF, PowerPoint e molti altri, convertendo il contenuto in formato Markdown. Questo facilita la strutturazione del testo e la preparazione per il chunking. - Arricchimento
Questo passaggio prevede la rimozione di contenuti di disturbo (come intestazioni o piè di pagina non rilevanti) e l'aggiunta di metadati utili ai dati. Ad esempio, includere il titolo del documento all'inizio di ogni chunk assicura che questa informazione contestuale sia disponibile durante il processo di chunking e recupero, migliorando la rilevanza delle risposte. - Filtraggio
Dopo l'arricchimento, è importante rimuovere tutti i documenti irrilevanti o di scarso valore che non supportano il caso d'uso specifico. Questo passaggio migliora la qualità complessiva del corpus e riduce il "rumore" che potrebbe confondere il sistema RAG. - Chunking
I dati puliti e arricchiti vengono quindi divisi in chunk più piccoli e logicamente coerenti. Questo processo è vitale per migliorare le prestazioni di recupero, poiché chunk di dimensioni appropriate sono più facili da embeddare e recuperare. Framework come LangChain offrono diverse implementazioni di metodi di chunking, come LangChain CharacterTextSplitter, che consentono di dividere il testo in base a vari criteri. - Embedding
L'ultimo passaggio della pipeline consiste nel convertire ogni chunk in un vettore numerico utilizzando un modello di embedding. Questi vettori, che catturano il significato semantico di ciascun chunk, vengono quindi memorizzati in un archivio vettoriale insieme ai metadati associati. È da questo archivio che il sistema RAG recupererà le informazioni pertinenti per rispondere alle query degli utenti.