Vai al contenuto principale
Retrieval-augmented generation, o RAG, è uno dei pattern più utili per costruire applicazioni AI che devono rispondere a partire dai tuoi documenti. Invece di chiedere a un modello di affidarsi alla sola memoria, recuperi prima il materiale sorgente rilevante, invii quel contesto al modello e gli chiedi di rispondere con citazioni. In questo tutorial, costruiremo un bot RAG privato usando Python, Venice per embedding e chat completions, Qdrant per la vector search e FastEmbed per il re-ranking locale. Alla fine, avrai i pezzi essenziali per un assistente di documenti locale in grado di ingerire i tuoi file, recuperare chunk rilevanti, ri-classificarli e rispondere con citazioni. Il bot RAG in azione Prima di continuare: se vuoi eseguire il codice in questo articolo, ti servirà una Venice API key. Esportala come variabile d’ambiente:
Interessato all’implementazione completa del codice? Dai un’occhiata al repository GitHub.

Come funziona un moderno bot RAG

Una buona pipeline RAG è qualcosa di più di “mettere i documenti in un database vettoriale”. Il flusso di base appare così: Il passo di re-ranking è l’upgrade che rende questo approccio molto più utile di una demo RAG di base. La vector search è veloce e brava a trovare chunk semanticamente simili, ma può comunque restituire passaggi adiacenti all’argomento piuttosto che direttamente utili. Un cross-encoder legge insieme la domanda e ciascun chunk candidato, poi assegna un punteggio a quanto quel chunk risponde realmente alla domanda.

Installazione delle dipendenze

Useremo l’SDK Python di OpenAI perché Venice espone un’API compatibile con OpenAI. Useremo anche il client Python di Qdrant con il supporto FastEmbed:
Se preferisci tenere le dipendenze in un file, crea requirements.txt con gli stessi pacchetti:

Scelta dei modelli

Crea un file chiamato rag_bot.py, poi inizia aggiungendo gli import, le strutture dati, l’URL dell’API e i nomi dei modelli:
Il nome del modello di embedding è intenzionalmente compatibile con OpenAI. Venice mappa i nomi dei modelli di embedding compatibili a modelli di embedding ospitati da Venice, quindi il codice esistente che usa l’SDK OpenAI di solito può essere spostato cambiando il base_url e l’API key. Puoi elencare i modelli Venice disponibili con:
Per i modelli di chat:

Creazione dei client Venice e Qdrant

Crea un singolo client Venice compatibile con OpenAI sia per gli embedding sia per le chat completions:
Per Qdrant, hai tre modalità utili: Per un bot privato locale, inizia con un percorso Qdrant locale su disco:
Ci sono diversi modi per gestire il deployment in produzione. Tuttavia, se usi un deployment Qdrant remoto, ricorda che i tuoi chunk di documenti e metadati verranno archiviati lì. Venice può mantenere privato il layer di inferenza, ma dovresti comunque scegliere il deployment Qdrant adatto ai tuoi dati.

Caricamento e chunking dei documenti

Per questo tutorial, lasceremo che il bot ingerisca file o cartelle locali. Inizia con file .md, .rst e .txt:
Una volta caricati i file, dobbiamo dividere il testo facendo “chunking” — separandolo in pezzi di dati. Una strategia ingenua potrebbe dividere i chunk in modo uniforme. Tuttavia, nella maggior parte dei casi, questo può perdere informazioni in determinati confini semantici, il che può ridurre l’efficacia del tuo sistema RAG. La strategia di chunking che useremo preferisce i confini di paragrafo o di frase in modo che il modello riceva un contesto coerente:
Una dimensione di chunk iniziale di 1000 caratteri con 150 caratteri di overlap è un buon default per documenti misti Markdown e testo. Chunk più piccoli possono migliorare la precisione. Chunk più grandi possono preservare più contesto. L’impostazione giusta dipenderà spesso dai tipi di documenti che stai archiviando.

Embedding dei documenti con Venice

Una volta che abbiamo i chunk, li embeddiamo in batch:
Il batching è importante. Embeddare un chunk alla volta è semplice, ma aggiunge latenza evitabile. Mantieni la dimensione del batch configurabile in modo da poter regolare il throughput in base al tuo workload.

Memorizzazione dei vettori in Qdrant

Prima di inserire i point, crea una collection Qdrant con la dimensione del vettore giusta. Il modo più semplice per conoscere la dimensione del vettore è embeddare il primo batch, poi usare len(embeddings[0]).
Ogni point archivia il vettore più i metadati di payload. Il payload include il testo originale e un percorso di sorgente in modo che la risposta possa citare da dove proviene il contesto:
Usa UUID deterministici derivati da source, chunk_index e contenuto. Questo rende l’ingestion ripetuta idempotente per i chunk non modificati.

Recupero dei chunk candidati

Al momento della domanda, il bot embedda la domanda dell’utente e chiede a Qdrant i top match vettoriali:
Il limit qui è il conteggio dei candidati. Dovrebbe di solito essere più alto del numero di chunk che pianifichi di inviare al modello perché il passo successivo li ri-classificherà. Un buon default è recuperare 8 candidati e inviare i 4 migliori al modello di chat.

Re-ranking con FastEmbed

Ora aggiungiamo la parte che fa sembrare il retrieval molto più intelligente.
L’importante differenza tra la ricerca con embedding e il re-ranking con cross-encoder è come avviene lo scoring. La ricerca con embedding confronta un vettore per la domanda contro un vettore per ciascun chunk. È veloce e scalabile. Un cross-encoder valuta la domanda e il chunk insieme. È più lento, ma può giudicare la rilevanza in modo più diretto. Ecco perché il pattern abituale è:
  1. Recupera un insieme di candidati più ampio con la vector search.
  2. Ri-classifica solo quei candidati localmente.
  3. Invia i primi pochi chunk al language model.
Un buon punto di partenza è candidate_k=8 e top_k=4. Aumenta candidate_k se la sorgente giusta è spesso vicina ma non riesce ad arrivare nel contesto finale.

Rispondere con Venice Chat Completions

Una volta selezionato il contesto, formattalo con numeri di sorgente:
Poi invia il contesto a un modello di chat Venice:
Nota il prompt di sistema: al bot viene detto di rispondere solo dal contesto fornito. È un guardrail semplice ma importante. Un assistente RAG non dovrebbe rispondere con sicurezza dalla conoscenza generale del modello quando i documenti recuperati non supportano la risposta.

Eseguire il bot

Una volta assemblati i pezzi in uno script, salvalo come rag_bot.py. Una prima esecuzione semplice può usare alcuni documenti di esempio integrati in modo da verificare la pipeline prima di ingerire i tuoi file:
Per ingerire i tuoi documenti:
Per mantenere una collection Qdrant locale su disco e avviare una chat interattiva:
Lo script stampa la risposta, poi stampa le sorgenti con sia i punteggi vettoriali sia quelli di re-ranking:
Se vuoi ispezionare il testo effettivo passato al modello, aggiungi:

Opzioni CLI utili

Esponi le principali leve di retrieval come opzioni CLI in modo da poter regolare il bot senza modificare il codice: Per lo sviluppo locale ripetuto, un flusso comune è:
Poi fai domande di follow-up senza ingerire di nuovo:

Note sulla privacy

Per un setup RAG privato, pensa a ciascun layer separatamente: Il default più privato per questo tutorial è Venice per l’inferenza, Qdrant locale su disco e re-ranking FastEmbed locale. Questo ti dà un bot RAG pratico senza inviare i payload del tuo database vettoriale a un vector store di terze parti.

Errori comuni da gestire fin da subito

Se cambi modello di embedding, ricrea la collection Qdrant. Modelli di embedding diversi possono produrre vettori con dimensioni diverse, e le collection Qdrant si aspettano una dimensione vettoriale fissa.

Dove andare dopo

Una volta che hai la baseline funzionante, i miglioramenti di maggior impatto sono solitamente:
  • Aggiungi loader specifici per i documenti per PDF, HTML, ticket o pagine wiki interne.
  • Archivia metadati più ricchi come titoli, intestazioni, date, owner e URL.
  • Regola candidate_k, top_k, dimensione del chunk e overlap su domande reali.
  • Aggiungi domande di valutazione per misurare la qualità del retrieval prima e dopo le modifiche.
  • Effettua lo streaming della chat completion finale di Venice per una migliore esperienza di chat interattiva.
I sistemi RAG sono facili da mostrare e sorprendentemente facili da rendere mediocri. Il pattern vector search più re-ranking è una base solida perché mantiene il retrieval veloce dando al bot una migliore possibilità di inviare al language model il contesto giusto.