10 Modi per Migliorare le Prestazioni dei Sistemi di Generazione Potenziata dalla Recupero

10 Modi per Migliorare le Prestazioni dei Sistemi di Generazione Potenziata

Strumenti per passare dal prototipo alla produzione

La guida di avvio rapido non è sufficiente

“Il recupero con generazione aumentata è il processo di integrazione dell’input di un utente in un grande modello linguistico (LLM) come ChatGPT con informazioni aggiuntive che hai (il sistema) recuperato da un’altra fonte. L’LLM può quindi utilizzare tali informazioni per migliorare la risposta che genera.” – Cory Zue

Gli LLM sono una fantastica invenzione, ma presentano un problema chiave. Inventano cose. RAG rende gli LLM molto più utili fornendo loro un contesto fattuale da utilizzare durante la risposta alle query.

Utilizzando la guida di avvio rapido per un framework come LangChain o LlamaIndex, chiunque può costruire un semplice sistema RAG, come un chatbot per la tua documentazione, con circa cinque righe di codice.

Tuttavia, il bot costruito con queste cinque righe di codice non funzionerà molto bene. RAG è facile da prototipare, ma molto difficile da portare in produzione, cioè raggiungere un punto in cui gli utenti ne sarebbero soddisfatti. Un tutorial di base potrebbe far funzionare RAG all’80%. Ma colmare il 20% successivo richiede spesso seri esperimenti. Le migliori pratiche devono ancora essere definite e possono variare a seconda del caso d’uso. Ma scoprire le migliori pratiche vale sicuramente il nostro tempo, perché RAG è probabilmente il modo più efficace per utilizzare gli LLM.

In questo post verranno esaminate le strategie per migliorare la qualità dei sistemi RAG. È rivolto a coloro che costruiscono con RAG e vogliono colmare il divario tra le configurazioni di base e le prestazioni a livello di produzione. Ai fini di questo post, migliorare significa aumentare la proporzione di query per le quali il sistema: 1. Trova il contesto appropriato e 2. Genera una risposta adeguata. Si presume che il lettore abbia già una comprensione di come funziona RAG. In caso contrario, suggerisco di leggere questo articolo di Cory Zue per una buona introduzione. Si presume anche una conoscenza di base dei framework comuni utilizzati per costruire questi strumenti: LangChain e LlamaIndex. Tuttavia, le idee discusse qui sono indipendenti dal framework.

Non entrerò nei dettagli di come implementare ogni strategia che tratterò, ma cercherò invece di dare un’idea di quando e perché potrebbe essere utile. Date le rapide evoluzioni nel settore, è impossibile fornire un elenco esauriente o perfettamente aggiornato di migliori pratiche. Invece, il mio obiettivo è delineare alcune cose che potresti considerare e provare quando cerchi di migliorare la tua applicazione di generazione con recupero aumentato.

10 modi per migliorare le prestazioni della generazione con recupero aumentato

1. Pulisci i tuoi dati.

RAG collega le capacità di un LLM ai tuoi dati. Se i tuoi dati sono confusi, sia nel contenuto che nella struttura, il tuo sistema ne risentirà. Se stai utilizzando dati con informazioni conflittuali o ridondanti, il tuo recupero avrà difficoltà a trovare il contesto corretto. E quando lo fa, la fase di generazione eseguita dal LLM potrebbe non essere ottimale. Supponiamo che tu stia costruendo un chatbot per la documentazione della tua startup e noti che non funziona bene. La prima cosa da esaminare sono i dati che stai inserendo nel sistema. Gli argomenti sono suddivisi in modo logico? Gli argomenti sono trattati in un unico posto o in vari post separati? Se tu, come essere umano, non riesci a capire facilmente quale documento dovresti consultare per rispondere alle domande comuni, nemmeno il tuo sistema di recupero potrà farlo.

Questo processo può essere semplice come combinare manualmente i documenti sullo stesso argomento, ma puoi spingerti oltre. Uno degli approcci più creativi che ho visto è utilizzare il LLM per creare riassunti di tutti i documenti forniti come contesto. La fase di recupero può quindi eseguire una ricerca su questi riassunti e approfondire i dettagli solo quando necessario. Alcuni framework offrono persino questa funzionalità come astrazione integrata.

2. Esplora diversi tipi di indice.

L’indice è il pilastro centrale di LlamaIndex e LangChain. È l’oggetto che contiene il tuo sistema di recupero. L’approccio standard a RAG prevede l’utilizzo di embedding e ricerca di similarità. Scomponi i dati di contesto, incorpora tutto, quando arriva una query, trova pezzi simili nel contesto. Questo funziona molto bene, ma non è il miglior approccio per ogni caso d’uso. Le query riguardano elementi specifici, come prodotti in un negozio di e-commerce? Potresti voler esplorare la ricerca basata su parole chiave. Non deve essere una scelta tra l’una e l’altra, molte applicazioni utilizzano un approccio ibrido. Ad esempio, potresti utilizzare un indice basato su parole chiave per le query relative a un prodotto specifico, ma fare affidamento sugli embedding per il supporto generale al cliente.

3. Sperimenta con il tuo approccio di chunking.

Il chunking dei dati di contesto è una parte fondamentale nella costruzione di un sistema RAG. I framework astraggono il processo di chunking e ti permettono di evitarlo senza pensarci. Ma dovresti pensarci. Le dimensioni dei chunk sono importanti. Dovresti esplorare ciò che funziona meglio per la tua applicazione. In generale, i chunk più piccoli migliorano spesso il recupero ma possono causare problemi nella generazione a causa della mancanza di contesto circostante. Ci sono molti modi in cui puoi affrontare il chunking. La cosa che non funziona è affrontarlo alla cieca. Questo post di PineCone presenta alcune strategie da considerare. Ho un set di domande di prova. Ho affrontato questo eseguendo un esperimento. Ho ciclato attraverso ogni set una volta con dimensioni di chunk piccole, medie e grandi e ho trovato che le dimensioni piccole sono le migliori.

4. Gioca con il tuo prompt di base.

Un esempio di un prompt di base utilizzato in LlamaIndex è:

‘Le informazioni di contesto sono riportate di seguito. Date le informazioni di contesto e senza conoscenze precedenti, rispondi alla query.’

Puoi sovrascriverlo e sperimentare con altre opzioni. Puoi persino modificare il RAG in modo che consenta all’LLM di fare affidamento sulle sue conoscenze se non riesce a trovare una buona risposta nel contesto. Puoi anche regolare il prompt per aiutare a guidare i tipi di query che accetta, ad esempio, istruendolo a rispondere in un certo modo per domande soggettive. Almeno è utile sovrascrivere il prompt in modo che l’LLM abbia contesto su quali compiti sta svolgendo. Ad esempio:

‘Sei un agente di supporto clienti. Sei progettato per essere il più utile possibile fornendo solo informazioni factual. Dovresti essere amichevole, ma non troppo chiacchierone. Le informazioni di contesto sono riportate di seguito. Date le informazioni di contesto e senza conoscenze precedenti, rispondi alla query.’

5. Prova il filtraggio dei metadati.

Una strategia molto efficace per migliorare il recupero è aggiungere metadati ai tuoi chunk e poi usarli per elaborare i risultati. La data è un tag di metadati comune da aggiungere perché ti consente di filtrare per recente. Immagina di costruire un’app che consente agli utenti di interrogare la cronologia delle loro email. È probabile che le email più recenti siano più rilevanti. Ma non sappiamo che saranno le più simili, dal punto di vista dell’embedding, alla query dell’utente. Questo porta a un concetto generale da tenere presente durante la costruzione di RAG: simile ≠ rilevante. Puoi aggiungere la data di ogni email ai suoi metadati e quindi dare priorità al contesto più recente durante il recupero. LlamaIndex ha una classe integrata di Node Post-Processors che aiutano proprio in questo.

6. Usa il routing delle query.

È spesso utile avere più di un indice. In questo modo puoi instradare le query all’indice appropriato quando arrivano. Ad esempio, potresti avere un indice che gestisce le domande di sintesi, un altro che gestisce le domande precise e un altro che funziona bene per le domande sensibili alla data. Se cerchi di ottimizzare un indice per tutti questi comportamenti, finirai per compromettere quanto bene si comporta in ognuno di essi. Invece puoi instradare la query all’indice corretto. Un altro caso d’uso potrebbe essere quello di indirizzare alcune query a un indice basato su parole chiave come discusso nella sezione 2.

Una volta costruiti gli indici, devi solo definire nel testo per cosa dovrebbero essere utilizzati. Quindi, al momento della query, l’LLM sceglierà l’opzione appropriata. Sia LlamaIndex che LangChain hanno strumenti per questo.

7. Approfondisci il riclassificamento.

Il riclassificamento è una soluzione al problema della discrepanza tra similarità e rilevanza. Con il riclassificamento, il tuo sistema di recupero ottiene i nodi principali per il contesto come al solito. Quindi li riordina in base alla rilevanza. Cohere Rereanker è comunemente usato per questo. Questa strategia è una che vedo spesso consigliata dagli esperti. Indipendentemente dal caso d’uso, se stai costruendo con RAG, dovresti sperimentare con il riclassificamento e vedere se migliora il tuo sistema. Sia LangChain che LlamaIndex hanno astrazioni che semplificano la configurazione.

8. Considera le trasformazioni delle query.

Già modifichi la query dell’utente inserendola nel tuo prompt di base. Ha senso modificarla ulteriormente. Ecco alcuni esempi:

Riphrasing: se il tuo sistema non trova un contesto rilevante per la query, puoi far sì che l’LLM riformuli la query e riprovi. Due domande che sembrano uguali agli umani non sembrano sempre così simili nello spazio di embedding.

HyDE: HyDE è una strategia che prende una query, genera una risposta ipotetica e le utilizza entrambe per la ricerca di embedding. I ricercatori hanno scoperto che questo può migliorare drasticamente le prestazioni.

Sotto-queries: LLM tendono a funzionare meglio quando suddividono le query complesse. Puoi integrare questo nel tuo sistema RAG in modo che una query venga decomposta in più domande.

LLamaIndex ha documentazione che copre questi tipi di trasformazioni delle query.

9. Ottimizza il tuo modello di embedding.

La similarità basata su embedding è il meccanismo di recupero standard per RAG. I tuoi dati vengono suddivisi ed incorporati all’interno dell’indice. Quando arriva una query, viene anche incorporata per il confronto con l’embedding presente nell’indice. Ma cosa fa l’embedding? Di solito, un modello pre-addestrato come OpenAI ‘text-embedding-ada-002.

Il problema è che il concetto di similarità nello spazio di embedding del modello pre-addestrato potrebbe non allinearsi molto bene con ciò che è simile nel tuo contesto. Immagina di lavorare con documenti legali. Vorresti che il tuo embedding basasse il suo giudizio di similarità più su termini specifici del tuo dominio come “proprietà intellettuale” o “violazione di contratto” e meno su termini generici come “qui” e “accordo”.

Puoi ottimizzare il tuo modello di embedding per risolvere questo problema. Farlo può aumentare le tue metriche di recupero del 5-10%. Questo richiede un po’ più di sforzo, ma può fare una differenza significativa nelle prestazioni di recupero. Il processo è più semplice di quanto si possa pensare, poiché LlamaIndex può aiutarti a generare un set di addestramento. Per ulteriori informazioni, puoi consultare questo post di Jerry Liu su come LlamaIndex affronta l’ottimizzazione degli embedding, o questo post che illustra il processo di ottimizzazione.

10. Inizia ad utilizzare gli strumenti di sviluppo LLM.

Probabilmente stai già utilizzando LlamaIndex o LangChain per costruire il tuo sistema. Entrambi i framework dispongono di utili strumenti di debug che ti consentono di definire callback, vedere quale contesto viene utilizzato, da quale documento viene effettuato il recupero e altro ancora.

Se scopri che gli strumenti integrati in questi framework sono carenti, c’è un ecosistema in crescita di strumenti che possono aiutarti ad approfondire il funzionamento interno del tuo sistema RAG. Arize AI ha uno strumento in-notebook che ti consente di esplorare come viene recuperato il contesto e perché. Rivet è uno strumento che fornisce un’interfaccia visiva per aiutarti a costruire agenti complessi. È stato appena rilasciato come open source dalla società di tecnologia legale Ironclad. Vengono costantemente rilasciati nuovi strumenti ed è utile sperimentare per vedere quali sono utili nel tuo flusso di lavoro.

Conclusioni

Costruire con RAG può essere frustrante perché è così facile farlo funzionare e così difficile farlo funzionare bene. Spero che le strategie sopra possano fornirti un po’ di ispirazione su come colmare il divario. Nessuna di queste idee funziona sempre e il processo è fatto di sperimentazione, prova ed errore. In questo post non ho affrontato la valutazione, come puoi misurare le prestazioni del tuo sistema, ma è importante creare un sistema che puoi controllare costantemente. Questo è l’unico modo per capire se le modifiche che stai apportando fanno la differenza. Ho scritto in precedenza su come valutare un sistema RAG. Per ulteriori informazioni, puoi esplorare LlamaIndex Evals, LangChain Evals e un framework davvero promettente chiamato RAGAS.