Évaluations pour la génération augmentée par récupération : TruLens + Milvus
Cet article a été initialement publié dans The New Stack et est republié ici avec autorisation.
La popularité croissante des grands modèles de langage (LLMs) a alimenté l’essor des technologies de recherche vectorielle, notamment les bases de données vectorielles spécialement conçues comme Milvus et Zilliz Cloud, les bibliothèques de recherche vectorielle comme FAISS, et les plugins de recherche vectorielle intégrés aux bases de données traditionnelles.
De plus en plus, la recherche vectorielle est devenue le cas d’utilisation essentiel en entreprise pour l’IA générative sous la forme d’applications de génération augmentée par récupération, ou RAGs, et de questions-réponses. Ce style de construction permet aux LLMs d’avoir un accès facile à une base de connaissances vérifiée qu’ils peuvent utiliser comme contexte pour répondre aux questions. Milvus est une base de données vectorielle open source hautement évolutive spécialement conçue pour cette application.
Construire un RAG
Lors de la création d’une application LLM efficace de type RAG, il existe de nombreux choix de configuration parmi lesquels choisir, qui peuvent affecter de manière significative la qualité de la récupération. Certains de ces choix incluent :
Construire la base de données vectorielle
- Sélection des données
- Modèle d’embedding
- Type d’index
Trouver des données de haute qualité qui correspondent précisément aux exigences de votre application est essentiel. Le processus de récupération peut fournir des résultats non pertinents si vous ne disposez pas des bonnes données.
Après avoir sélectionné vos données, tenez compte du modèle d’embedding que vous utilisez, car il influence considérablement la qualité de la récupération. Même si votre base de connaissances contient les bonnes informations, le récupérateur peut produire des résultats incorrects si le modèle d’embedding nécessite une compréhension sémantique de votre domaine.
La pertinence du contexte est une métrique utile pour évaluer la qualité de la récupération, et ces sélections l’affectent fortement.
Enfin, le type d’index peut avoir un impact significatif sur l’efficacité de la recherche sémantique. C’est particulièrement vrai pour les grands ensembles de données ; ce choix vous permet de faire un compromis entre le taux de rappel, la vitesse et les besoins en ressources. Milvus prend en charge différents types d’index, tels que les index plats, les index basés sur la quantification de produit et les index basés sur des graphes. Vous pouvez en savoir plus sur les différents types d’index.
Récupération
- Quantité de contexte récupéré (top k)
- Taille des blocs
Lorsque nous arrivons à la récupération, top k est un paramètre souvent discuté qui contrôle le nombre de blocs de contexte récupérés. Un top k plus élevé nous donne une plus grande chance de récupérer les informations nécessaires et augmente la probabilité que notre LLM intègre des informations non pertinentes dans sa réponse. Pour les questions simples, un top k plus faible est souvent le plus performant.
La taille des blocs contrôle la taille de chaque contexte récupéré. Une taille de bloc plus grande peut être utile pour des questions plus complexes, tandis que des blocs plus petits suffisent pour des questions simples auxquelles il est possible de répondre avec seulement une très petite quantité d’informations.
Pour beaucoup de ces choix, il n’existe pas de solution universelle. Les performances peuvent varier considérablement en fonction de la taille et du type de données, des LLMs utilisés, de votre application, et plus encore. Nous avons besoin d’un outil d’évaluation pour apprécier la qualité de ces récupérations pour notre cas d’utilisation spécifique. C’est là que TruLens intervient.
TruLens pour le suivi et l’évaluation des LLM
TruLens est une bibliothèque open source destinée à évaluer et suivre les performances des applications LLM, telles que les RAGs. Avec TruLens, nous avons également la possibilité d’utiliser les LLMs eux-mêmes pour évaluer les sorties, la qualité de la récupération, et plus encore.
Lorsque nous développons des applications LLM, le problème le plus important dans l’esprit de nombreuses personnes est l’hallucination. Les RAG contribuent largement à garantir des informations exactes en fournissant au LLM un contexte récupéré, mais ils ne peuvent pas le garantir. Les évaluations sont essentielles ici pour vérifier l’absence d’hallucination dans notre application. TruLens propose trois tests pour ce besoin : pertinence du contexte, ancrage et pertinence de la réponse. Passons chacun d’eux en revue pour comprendre comment ils peuvent nous être utiles.
Pertinence du contexte
La première étape de toute application RAG est la récupération ; pour vérifier la qualité de notre récupération, nous voulons nous assurer que chaque fragment de contexte est pertinent par rapport à la requête d’entrée. C’est essentiel, car le LLM utilisera ce contexte pour formuler une réponse, donc toute information non pertinente dans le contexte pourrait être intégrée à une hallucination.
Ancrage
Une fois le contexte récupéré, il est ensuite transformé en réponse par un LLM. Les LLM s’écartent souvent des faits fournis, en exagérant ou en élargissant jusqu’à produire une réponse qui semble correcte. Pour vérifier l’ancrage de notre application, nous devons séparer la réponse en énoncés distincts et rechercher indépendamment des preuves qui étayent chacun d’eux dans le contexte récupéré.
Pertinence de la réponse
Enfin, notre réponse doit encore répondre utilement à la question d’origine. Nous pouvons le vérifier en évaluant la pertinence de la réponse finale par rapport à l’entrée utilisateur.
RAG sans hallucination
En obtenant des évaluations satisfaisantes pour cette triade, nous pouvons formuler une affirmation nuancée sur l’exactitude de notre application ; elle est vérifiée comme étant sans hallucination jusqu’à la limite de sa base de connaissances. Autrement dit, si la base de données vectorielle contient uniquement des informations exactes, alors les réponses fournies par le RAG sont également exactes.
Rendre cela concret
Comme nous l’avons mentionné précédemment, de nombreux choix de configuration de notre RAG peuvent avoir un impact substantiel sur l’hallucination. Pour illustrer cela, nous allons créer une application RAG de questions-réponses au-dessus d’articles Wikipédia portant sur un petit ensemble de villes. LlamaIndex servira de framework pour cette application.
Suivez cet exemple dans Google Colab.
Charger des données depuis Wikipédia
Pour construire notre magasin vectoriel, nous devons d’abord charger des données. Ici, nous utiliserons un chargeur de données de LlamaIndex pour charger des données directement depuis Wikipédia.
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}")
Configurer les évaluateurs
Ensuite, nous voulons configurer nos évaluateurs. Plus précisément, nous utiliserons la triade mentionnée plus haut : pertinence du contexte, ancrage et pertinence de la réponse pour tester l’hallucination.
TruLens fournit un ensemble d’évaluateurs ou de fonctions de feedback avec des invites utiles pour cette évaluation, qui utilisent un fournisseur de modèle spécifique, comme OpenAI, Anthropic ou HuggingFace.
# Initialize OpenAI-based feedback function collection class:
openai_gpt4 = feedback.OpenAI()
Après avoir défini notre fournisseur de modèle, nous choisissons la pertinence question-énoncé à utiliser pour notre première évaluation. Pour chaque évaluation dans cet exemple, nous utiliserons également des raisons de type chaîne de pensée afin de mieux comprendre les évaluations. Cela est indiqué par le suffixe de fonction de feedback 1_with_cot_reason.
Lorsque nous faisons cela, nous devons également sélectionner quel texte transmettre à notre fonction de feedback. TruLens sérialise l’application, qui est ensuite indexée par une structure de type JSON. Nous utiliserons cet index pour la sélection du texte. TruLens fournit un certain nombre de fonctions d’aide pour faciliter cela :
on_input()trouve automatiquement l’entrée principale transmise à notre application LlamaIndex à utiliser comme premier texte transmis à notre fonction de feedback.TruLlama.select_source_nodes()identifie les nœuds sources utilisés dans une récupération LlamaIndex.
Enfin, nous devons agréger la pertinence de chaque élément de contexte en un seul score. Pour cet exemple, nous utiliserons le maximum pour l’agrégation afin de mesurer la pertinence du fragment le plus pertinent. D’autres métriques comme la moyenne ou le minimum pourraient également être utilisées.
# 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 est configurée de manière similaire, avec une agrégation légèrement différente. Dans ce cas, nous prendrons le score de groundedness maximal de chaque énoncé, puis le score moyen de groundedness sur l’ensemble des énoncés.
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 pertinence de la réponse est la fonction de feedback la plus simple à configurer, puisqu’elle ne repose que sur l’entrée/la sortie. Nous pouvons utiliser une nouvelle fonction d’aide TruLens pour cela — .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()
Définir l’espace de configuration
Maintenant que nous avons chargé nos données et configuré nos évaluateurs, il est temps de construire notre RAG. Dans ce processus, nous construirons une série de RAGs avec différentes configurations, évaluerons chacun d’eux et sélectionnerons le meilleur choix optimal.
Comme nous l’avons évoqué plus tôt, nous limiterons notre espace de configuration à quelques choix déterminants pour les RAGs. Nous testerons le type d’index, le modèle d’embedding, top k et la taille des fragments dans cet exemple ; toutefois, vous êtes encouragé à tester d’autres configurations, telles que différentes métriques de distance et paramètres de recherche.
Itérer à travers nos sélections
Après avoir défini l’espace de configuration, nous utiliserons itertools pour essayer chaque combinaison de ces choix et évaluer chacune d’elles. De plus, Milvus nous offre un avantage appréciable avec le paramètre overwrite. Cela nous permet d’itérer facilement sur différentes configurations sans les procédures lentes de démontage et d’instanciation qui peuvent être nécessaires avec d’autres bases de données vectorielles.
À chaque itération, nous transmettrons la sélection du paramètre d’index à MilvusVectorStore et à notre application à l’aide du contexte de stockage. Nous transmettrons notre modèle d’embedding au contexte de service, puis nous créerons notre index.
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)
Ensuite, nous pouvons construire un moteur de requête à l’aide de cet index — en définissant top_k ici :
query_engine = index.as_query_engine(similarity_top_k = top_k)
Après la construction, nous utiliserons TruLens pour envelopper l’application. Ici, nous lui donnerons un nom facilement identifiable, enregistrerons les configurations comme métadonnées de l’application et définirons les fonctions de feedback pour l’évaluation.
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
})
Ce tru_query_engine fonctionnera exactement comme le moteur de requête d’origine.
Enfin, nous utiliserons un petit ensemble de prompts de test pour l’évaluation, en appelant l’application afin de fournir une réponse à chaque prompt. Comme nous appelons l’API OpenAI en succession rapide, Tenacity est utile ici pour nous aider à éviter les problèmes de limite de débit grâce au backoff exponentiel.
@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)
Les résultats
Quelle configuration a obtenu les meilleures performances ?
| Type d’index | Modèle d’embedding | Similarity Top k | Taille des chunks |
|---|---|---|---|
| IVF Flat | text-embedding-ada-002 | 3 | 200 |
Quelle configuration a obtenu les moins bonnes performances ?
| Type d’index | Modèle d’embedding | Similarity Top k | Taille des chunks |
|---|---|---|---|
| IVF Flat | Multilingual MiniLM L12 v2 | 1 | 500 |
Quels modes de défaillance ont été identifiés ?
Un mode de défaillance que nous avons observé était la récupération d’informations sur la mauvaise ville. Vous pouvez en voir un exemple avec le raisonnement en chaîne de pensée ci-dessous, où du contexte sur Tucson a été récupéré au lieu de Houston.
De même, nous avons également constaté des problèmes où nous récupérions du contexte sur la bonne ville, mais où ce contexte était sans rapport avec la question d’entrée.
Compte tenu de ce contexte non pertinent, le modèle de complétion s’est ensuite mis à halluciner. Il est important de noter ici que l’hallucination n’est pas nécessairement factuellement incorrecte ; c’est simplement lorsque le modèle répond sans preuve à l’appui.
De plus, nous avons même trouvé des exemples de réponses non pertinentes.
Comprendre les performances
Par type d’index
Le type d’index n’a pas eu d’impact significatif sur les performances en termes de vitesse, d’utilisation des tokens ou d’évaluations. Cela est probablement dû à la petite taille des données ingérées pour cet exemple, et le type d’index peut constituer un choix plus important pour des corpus plus volumineux.
Par modèle d’embedding
Text-embedding-ada-002 a surpassé le modèle d’embedding MiniLM en matière d’ancrage (0,72 contre 0,60 en moyenne) et de pertinence des réponses (0,82 contre 0,62 en moyenne). Les deux modèles d’embedding ont obtenu des performances équivalentes en matière de pertinence du contexte.
Ces meilleurs scores d’évaluation peuvent être attribués aux embeddings OpenAI, mieux adaptés aux informations de Wikipédia.
Similarity Top K
L’augmentation de top k a entraîné une légère amélioration de la qualité maximale de récupération (mesurée par la pertinence du contexte). En récupérant un plus grand nombre de chunks, le récupérateur dispose de davantage de tentatives pour récupérer un contexte de haute qualité.
Un top k plus élevé a également amélioré l’ancrage (0,71 contre 0,62 en moyenne) et la pertinence des réponses (0,76 contre 0,68 en moyenne). En récupérant davantage de chunks de contexte, nous fournissons plus de preuves au modèle de complétion pour formuler et étayer des affirmations.
Comme prévu, ces améliorations se font au prix d’une utilisation beaucoup plus élevée des tokens (en moyenne 590 tokens supplémentaires par appel).
Taille des chunks
L’augmentation de la taille des chunks a diminué l’ancrage de notre récupérateur en imposant l’inclusion de texte environnant sans rapport avec la question d’entrée.
Du côté positif, une taille de chunk plus élevée a fourni davantage de preuves à vérifier. Ainsi, lorsque le LLM formule des affirmations, elles sont plus susceptibles d’être étayées par le contexte récupéré.
Enfin, l’augmentation de la taille des chunks a augmenté l’utilisation moyenne des tokens de 400 tokens par enregistrement.
Construire un meilleur RAG avec TruLens et Milvus
Dans cet article, nous avons appris à créer un RAG avec diverses configurations et paramètres, notamment le type d’index, le modèle d’embedding, top k et la taille des chunks. Le grand nombre de configurations prises en charge et la prise en charge de l’écrasement sur Milvus ont permis cette expérimentation dynamique. Surtout, nous avons également utilisé TruLens pour suivre et évaluer chaque expérience, identifier et expliquer de nouveaux modes de défaillance, et trouver rapidement la combinaison la plus performante.
Pour l’essayer vous-même. Vous pouvez consulter le projet open source TruLens et installer la solution open source Milvus ou Zilliz Cloud.
Continuer à lire

Introducing Zilliz Cloud Global Cluster: Region-Level Resilience for Mission-Critical AI
Zilliz Cloud Global Cluster delivers multi-region resilience, automatic failover, and fast global AI search with built-in security and compliance.

Why AI Databases Don't Need SQL
Whether you like it or not, here's the truth: SQL is destined for decline in the era of AI.

DeepSeek Always Busy? Deploy It Locally with Milvus in Just 10 Minutes—No More Waiting!
Learn how to set up DeepSeek-R1 on your local machine using Ollama, AnythingLLM, and Milvus in just 10 minutes. Bypass busy servers and enhance AI responses with custom data.



