Valutazioni per la generazione aumentata tramite recupero: TruLens + Milvus
Questo articolo è stato originariamente pubblicato su The New Stack ed è ripubblicato qui con autorizzazione.
La crescente popolarità dei large language models (LLM) ha alimentato l’ascesa delle tecnologie di ricerca vettoriale, inclusi database vettoriali appositamente progettati come Milvus e Zilliz Cloud, librerie di ricerca vettoriale come FAISS e plugin di ricerca vettoriale integrati con database tradizionali.
Sempre più spesso, la ricerca vettoriale è diventata il caso d’uso aziendale essenziale per l’IA generativa sotto forma di applicazioni di retrieval augmented generation, o RAG, di domanda e risposta. Questo stile di costruzione consente agli LLM di avere facile accesso a una base di conoscenza verificata che possono usare come contesto per rispondere alle domande. Milvus è un database vettoriale open source altamente scalabile appositamente progettato per questa applicazione.
Costruire un RAG
Quando si crea un’efficace applicazione LLM in stile RAG, ci sono molte scelte di configurazione tra cui scegliere che possono influire significativamente sulla qualità del retrieval. Alcune di queste scelte includono:
Costruire il Vector DB
- Selezione dei dati
- Modello di embedding
- Tipo di indice
Trovare dati di alta qualità che corrispondano precisamente ai requisiti della tua applicazione è fondamentale. Il processo di retrieval potrebbe fornire risultati irrilevanti se non hai i dati corretti.
Dopo aver selezionato i dati, considera il modello di embedding che usi, poiché influenza significativamente la qualità del retrieval. Anche se la tua base di conoscenza contiene le informazioni corrette, il retriever può produrre risultati errati se il modello di embedding necessita di una comprensione semantica del tuo dominio.
La pertinenza del contesto è una metrica utile per valutare la qualità del retrieval, e queste selezioni la influenzano notevolmente.
Infine, il tipo di indice può avere un impatto significativo sull’efficienza della ricerca semantica. Questo è particolarmente vero per grandi set di dati; questa scelta ti consente di bilanciare tasso di recall, velocità e requisiti di risorse. Milvus supporta vari tipi di indice, come indici flat, indici basati sulla quantizzazione del prodotto e indici basati su grafi. Puoi leggere di più sui diversi tipi di indice.
Retrieval
- Quantità di contesto recuperato (top k)
- Dimensione dei chunk
Quando arriviamo al retrieval, top k è un parametro spesso discusso che controlla il numero di chunk di contesto recuperati. Un top k più alto ci dà una maggiore probabilità di recuperare le informazioni necessarie e aumenta la probabilità che il nostro LLM incorpori informazioni irrilevanti nella sua risposta. Per domande semplici, un top k più basso è spesso il più performante.
La dimensione dei chunk controlla la dimensione di ciascun contesto recuperato. Una dimensione dei chunk maggiore può essere utile per domande più complesse, mentre chunk più piccoli sono sufficienti per domande semplici a cui si può rispondere con solo una quantità minima di informazioni.
Per molte di queste scelte, non esiste una soluzione unica valida per tutti. Le prestazioni possono variare enormemente a seconda della dimensione e del tipo di dati, degli LLM utilizzati, della tua applicazione e altro ancora. Abbiamo bisogno di uno strumento di valutazione per valutare la qualità di questi retrieval per il nostro caso d’uso specifico. È qui che entra in gioco TruLens.
TruLens per il tracciamento e la valutazione degli LLM
TruLens è una libreria open source per valutare e tracciare le prestazioni delle app LLM, come i RAG. Con TruLens, otteniamo anche la possibilità di usare gli stessi LLM per valutare l’output, la qualità del retrieval e altro ancora.
Quando sviluppiamo applicazioni LLM, il problema più importante nella mente di molte persone è l’allucinazione. I RAG contribuiscono molto a garantire informazioni accurate fornendo contesto recuperato all’LLM, ma non possono garantirlo. Le valutazioni sono essenziali in questo caso per verificare l’assenza di allucinazione nella nostra app. TruLens offre tre test per questa esigenza: pertinenza del contesto, groundedness e pertinenza della risposta. Esaminiamo ciascuno di questi per capire in che modo possono esserci utili.
Pertinenza del contesto
Il primo passo di qualsiasi applicazione RAG è il retrieval; per verificare la qualità del nostro retrieval, vogliamo assicurarci che ogni porzione di contesto sia pertinente alla query di input. Questo è fondamentale perché l’LLM utilizzerà questo contesto per formulare una risposta, quindi qualsiasi informazione irrilevante nel contesto potrebbe essere intrecciata in un’allucinazione.
Groundedness
Dopo che il contesto è stato recuperato, viene quindi trasformato in una risposta da un LLM. Gli LLM spesso si discostano dai fatti forniti, esagerando o ampliando fino a produrre una risposta che suona corretta. Per verificare la groundedness della nostra applicazione, dovremmo separare la risposta in affermazioni distinte e cercare indipendentemente prove che supportino ciascuna all’interno del contesto recuperato.
Pertinenza della risposta
Infine, la nostra risposta deve comunque rispondere in modo utile alla domanda originale. Possiamo verificarlo valutando la pertinenza della risposta finale rispetto all’input dell’utente.
RAG privi di allucinazioni
Raggiungendo valutazioni soddisfacenti per questa triade, possiamo fare un’affermazione sfumata sulla correttezza della nostra applicazione; è verificata come priva di allucinazioni entro il limite della sua base di conoscenza. In altre parole, se il database vettoriale contiene solo informazioni accurate, allora anche le risposte fornite dal RAG sono accurate.
Renderlo concreto
Come abbiamo accennato prima, molte delle scelte di configurazione per il nostro RAG possono avere un impatto sostanziale sull’allucinazione. Per illustrarlo, costruiremo un’applicazione RAG di domanda-risposta basata su articoli di Wikipedia relativi a un piccolo insieme di città. LlamaIndex fungerà da framework per questa applicazione.
Segui questo esempio in Google Colab.
Caricare dati da Wikipedia
Per costruire il nostro vector store, dobbiamo prima caricare i dati. Qui useremo un data loader di LlamaIndex per caricare dati direttamente da Wikipedia.
from llama_index import WikipediaReader
cities = [
"Los Angeles", "Houston", "Honolulu", "Tucson", "Mexico City",
"Cincinatti", "Chicago"
]
wiki_docs = []
for city in cities:
try:
doc = WikipediaReader().load_data(pages=[city])
wiki_docs.extend(doc)
except Exception as e:
print(f"Error loading page for city {city}: {e}")
Configurare i valutatori
Successivamente, vogliamo configurare i nostri valutatori. In particolare, useremo la triade menzionata in precedenza: pertinenza del contesto, groundedness e pertinenza della risposta per testare l’allucinazione.
TruLens fornisce un insieme di valutatori o funzioni di feedback con prompt utili per questa valutazione, che utilizzano uno specifico provider di modelli, come OpenAI, Anthropic o HuggingFace.
# Initialize OpenAI-based feedback function collection class:
openai_gpt4 = feedback.OpenAI()
Dopo aver impostato il nostro provider di modelli, scegliamo la pertinenza domanda-affermazione da utilizzare per la nostra prima valutazione. Per ogni valutazione in questo esempio, useremo anche ragioni chain-of-thought per comprendere meglio le valutazioni. Questo è indicato dal suffisso della funzione di feedback 1_with_cot_reason.
Quando lo facciamo, dobbiamo anche selezionare quale testo passare alla nostra funzione di feedback. TruLens serializza l’applicazione, che viene quindi indicizzata da una struttura simile a JSON. Useremo questo indice per la selezione del testo. TruLens fornisce una serie di funzioni di supporto per rendere tutto questo semplice:
on_input()trova automaticamente l’input principale passato alla nostra applicazione LlamaIndex da usare come primo testo passato alla nostra funzione di feedback.TruLlama.select_source_nodes()identifica i nodi sorgente usati in un recupero LlamaIndex.
Infine, dobbiamo aggregare la rilevanza per ogni porzione di contesto in un singolo punteggio. Per questo esempio, useremo il massimo per l’aggregazione per misurare la rilevanza del blocco più rilevante. Potrebbero essere usate anche altre metriche come media o minimo.
# Question/statement relevance between question and each context chunk.
f_context_relevance = Feedback(openai.qs_relevance_with_cot_reason, name = "Context Relevance").on_input().on(
TruLlama.select_source_nodes().node.text
).aggregate(np.max)
La groundedness viene configurata in modo simile, con un’aggregazione leggermente diversa. In questo caso, prenderemo il punteggio massimo di groundedness di ciascuna affermazione, e poi il punteggio medio di groundedness tra tutte le affermazioni.
grounded = Groundedness(groundedness_provider=openai_gpt4)
f_groundedness = Feedback(grounded.groundedness_measure_with_cot_reason, name = "Groundedness").on(
TruLlama.select_source_nodes().node.text # context
).on_output().aggregate(grounded.grounded_statements_aggregator)
La rilevanza della risposta è la funzione di feedback più semplice da configurare, poiché si basa solo su input/output. Possiamo usare una nuova funzione helper di TruLens per questo — .on_input_output().
# Question/answer relevance between overall question and answer.
f_qa_relevance = Feedback(openai.relevance_with_cot_reason,
name = "Answer Relevance").on_input_output()
Definire lo spazio di configurazione
Ora che abbiamo caricato i nostri dati e configurato i nostri valutatori, è il momento di costruire il nostro RAG. In questo processo, costruiremo una serie di RAG con configurazioni diverse, valuteremo ciascuno e selezioneremo la scelta ottimale migliore.
Come accennato in precedenza, limiteremo il nostro spazio di configurazione ad alcune scelte d’impatto per i RAG. Testeremo tipo di indice, modello di embedding, top k e dimensione del chunk in questo esempio; tuttavia, ti incoraggiamo a testare altre configurazioni come diverse metriche di distanza e parametri di ricerca.
Iterare attraverso le nostre selezioni
Dopo aver definito lo spazio di configurazione, useremo itertools per provare ogni combinazione di queste scelte e valutarne ciascuna. Inoltre, Milvus ci offre un bel vantaggio con il parametro overwrite. Questo ci permette di iterare facilmente attraverso configurazioni diverse senza lente procedure di teardown e istanziazione che possono essere richieste con altri database vettoriali.
In ogni iterazione, passeremo la selezione del parametro dell’indice a MilvusVectorStore e alla nostra applicazione usando il contesto di archiviazione. Passeremo il nostro modello di embedding al contesto di servizio e poi creeremo il nostro indice.
vector_store = MilvusVectorStore(index_params={
"index_type": index_param,
"metric_type": "L2"
},
search_params={"nprobe": 20},
overwrite=True)
llm = OpenAI(model="gpt-3.5-turbo")
storage_context = StorageContext.from_defaults(vector_store = vector_store)
service_context = ServiceContext.from_defaults(embed_model = embed_model, llm = llm, chunk_size = chunk_size)
index = VectorStoreIndex.from_documents(wiki_docs,
service_context=service_context,
storage_context=storage_context)
Quindi, possiamo costruire un motore di query usando questo indice — definendo top_k qui:
query_engine = index.as_query_engine(similarity_top_k = top_k)
Dopo la costruzione, useremo TruLens per avvolgere l’applicazione. Qui, le daremo un nome facilmente identificabile, registreremo le configurazioni come metadati dell’app e definiremo le funzioni di feedback per la valutazione.
tru_query_engine = TruLlama(query_engine,
app_id=f"App-{index_param}-{embed_model_name}-{top_k}",
feedbacks=[f_groundedness, f_qa_relevance, f_context_relevance],
metadata={
'index_param':index_param,
'embed_model':embed_model_name,
'top_k':top_k
})
Questo tru_query_engine funzionerà proprio come il motore di query originale.
Infine, useremo un piccolo set di prompt di test per la valutazione, chiamando l'applicazione affinché fornisca una risposta a ciascun prompt. Poiché stiamo chiamando l'API OpenAI in rapida successione, Tenacity è utile da usare qui per aiutarci a evitare problemi di limite di frequenza tramite backoff esponenziale.
@retry(stop=stop_after_attempt(10), wait=wait_exponential(multiplier=1, min=4, max=10))
def call_tru_query_engine(prompt):
return tru_query_engine.query(prompt)
for prompt in test_prompts:
call_tru_query_engine(prompt)
I risultati
Quale configurazione ha ottenuto le migliori prestazioni?
| Tipo di indice | Modello di embedding | Similarity Top k | Dimensione del chunk |
|---|---|---|---|
| IVF Flat | text-embedding-ada-002 | 3 | 200 |
Quale configurazione ha ottenuto le peggiori prestazioni?
| Tipo di indice | Modello di embedding | Similarity Top k | Dimensione del chunk |
|---|---|---|---|
| IVF Flat | Multilingual MiniLM L12 v2 | 1 | 500 |
Quali modalità di errore sono state identificate?
Una modalità di errore che abbiamo osservato è stata il recupero di informazioni sulla città sbagliata. Puoi vedere un esempio di questo con il ragionamento chain-of-thought qui sotto, dove è stato recuperato contesto su Tucson invece che su Houston.
Allo stesso modo, abbiamo riscontrato anche problemi in cui recuperavamo contesto sulla città corretta, ma il contesto era irrilevante rispetto alla domanda in input.
Dato questo contesto irrilevante, il modello di completamento ha poi continuato ad allucinare. È importante notare qui che un'allucinazione non è necessariamente fattualmente errata; si verifica semplicemente quando il modello risponde senza prove a supporto.
Inoltre, abbiamo persino trovato esempi di risposte irrilevanti.
Comprendere le prestazioni
Per tipo di indice
Il tipo di indice non ha avuto un impatto significativo sulle prestazioni in termini di velocità, utilizzo dei token o valutazioni. Ciò è probabilmente dovuto alla piccola dimensione dei dati ingeriti per questo esempio, e il tipo di indice può essere una scelta più importante per corpora più grandi.
Per modello di embedding
Text-embedding-ada-002 ha superato il modello di embedding MiniLM in termini di groundedness (0,72 rispetto a 0,60 in media) e pertinenza della risposta (0,82 rispetto a 0,62 in media). I due modelli di embedding hanno ottenuto risultati ugualmente buoni sulla pertinenza del contesto.
Questi punteggi di valutazione migliorati possono essere attribuiti agli embedding di OpenAI, più adatti alle informazioni di Wikipedia.
Similarity Top K
Aumentare il top k ha comportato una qualità massima di recupero leggermente migliorata (misurata dalla pertinenza del contesto). Recuperando un numero maggiore di chunk, il retriever ha più tentativi per recuperare contesto di alta qualità.
Un top k più alto ha anche migliorato la groundedness (0,71 rispetto a 0,62 in media) e la pertinenza della risposta (0,76 rispetto a 0,68 in media). Recuperando più chunk di contesto, forniamo più prove al modello di completamento per formulare e supportare affermazioni.
Come previsto, questi miglioramenti comportano un costo in termini di utilizzo dei token molto più elevato (una media di 590 token aggiuntivi per chiamata).
Dimensione del chunk
Aumentare la dimensione del chunk ha ridotto la groundedness del nostro retriever, forzando l'inclusione di testo circostante irrilevante rispetto alla domanda in input.
Il lato positivo è che una dimensione del chunk più elevata ha fornito più prove da verificare. Quindi, quando l'LLM formula affermazioni, è più probabile che siano supportate dal contesto recuperato.
Infine, aumentare la dimensione del chunk ha incrementato l'utilizzo medio dei token di 400 token per record.
Costruisci un RAG migliore con TruLens e Milvus
In questo post, abbiamo imparato a costruire un RAG con varie configurazioni e parametri, tra cui tipo di indice, modello di embedding, top k e dimensione dei chunk. L’ampia quantità di configurazioni supportate e il supporto alla sovrascrittura su Milvus hanno reso possibile questa sperimentazione dinamica. Fondamentalmente, abbiamo anche usato TruLens per tracciare e valutare ogni esperimento, identificare e spiegare nuove modalità di errore e trovare rapidamente la combinazione più performante.
Per provarlo tu stesso. Puoi consultare open source TruLens e installare open source Milvus o Zilliz Cloud.
Continua a leggere

Zilliz Cloud Now Available in Azure North Europe: Bringing AI-Powered Vector Search Closer to European Customers
The addition of the Azure North Europe (Ireland) region further expands our global footprint to better serve our European customers.

Our Journey to 35K+ GitHub Stars: The Real Story of Building Milvus from Scratch
Join us in celebrating Milvus, the vector database that hit 35.5K stars on GitHub. Discover our story and how we’re making AI solutions easier for developers.

Vector Databases vs. Object-Relational Databases
Use a vector database for AI-powered similarity search; use an object-relational database for complex data modeling with both relational integrity and object-oriented features.



