[!NOTE] Traduction automatique Cet article a été traduit automatiquement depuis la version originale en anglais.
Évaluation de RAG : Métriques pour chaque étape d’un système RAG en production
Première partie de la série de production RAG
Un système RAG présentant des filtres défectueux peut fonctionner pendant des mois sans déclencher d’alerte opérationnelle. Il continue de fournir des réponses et de respecter ses objectifs de latence, mais ces réponses s’appuient sur des preuves incomplètes. Une analyse par rappel@k à l’aide du jeu de données d’or initial permet de mettre en évidence cette perte de qualité, ce que les tableaux de bord relatifs à la latence et à la disponibilité ne font pas.
L’évaluation ne peut détecter une défaillance que si chaque étape pipeline dispose de sa propre métrique. Cet article établit un lien entre les modes de défaillance courants et ces métriques, allant de la parsing des documents jusqu’à la surveillance en production.
!!! astuce « Voulez-vous sauter directement à l’exécution du code ? »
L’exécutable [`slavadubrov/rag-evals-demo`](https://github.com/slavadubrov/rag-evals-demo) Le repository applique ces métriques à SciFact. `make eval` exécute le jeu d’outils, et `make benchmark` Il compare les méthodes de segmentation en chunks, embedding, ainsi que les configurations LLM. Les notebooks 00 à 09 présentent séparément chacune de ces métriques. La démonstration fait appel à Qdrant intégré, ce qui évite l’utilisation de Docker.
Résumé
- L’évaluation définit le système. Une étape dépourvue de métriques est une étape qui échoue en silence.
- Une pile d’évaluation utile couvre l’ingestion, la récupération, le ancrage génératif, la conformité avec l’ontologie ainsi que les signaux système. RAGAS, TruLens, DeepEval, Arize Phoenix, et le TREC 2024 RAG Piste Nous vous fournissons les outils nécessaires. Ils ne choisissent pas vos métriques à votre place.
- Pour les systèmes basés sur des métadonnées et des ontologies RAG, un étiquetage incorrect ou un prédicat rigide peut réduire le taux de rappel à zéro. Le Recall@k standard permet de détecter cette perte en conservant l’ensemble de référence d’origine. Une métrique de filtre contre les exclusions fausses identifie la cause du problème, tandis que la fiabilité peut encore sembler bonne, car le modèle a fidèlement indiqué « Je ne sais pas ».
Les sections suivent l’ordre défini par pipeline. Commencez par le tableau de décision, puis utilisez les sections ultérieures comme référence pour chaque étape.
Table de décision d’évaluation RAG
Utilisez ce tableau comme point de départ avant de choisir un framework. La métrique appropriée dépend du mode de défaillance que vous cherchez à détecter, et non du nom de l’outil.
| Question | Famille de métriques | Utilisez ceci lorsque | Faites attention à |
|---|---|---|---|
| Complétude de l’extraction, couverture des tableaux et figures | Les PDF, diapositives, scans et pages HTML sont intégrés au corpus. | Un texte au rendu soigné peut tout de même présenter des sous-titres manquants, des notes de bas de page ou une structure de tableau incorrecte. | |
| La phase de récupération a-t-elle trouvé les preuves appropriées ? | Recall@k, nDCG@k, MRR, précision/recall dans le contexte | Vous pouvez étiqueter les fragments ou documents pertinents. | Un filtre métadonnées strict peut éliminer le document pertinent avant même que le classement ne commence. |
| reranking a-t-il amélioré la liste restreinte ? | Reranker amélioration, Précision@1, delta nDCG | Les encodeurs croisés ou les classeurs LLM se situent après l’étape de récupération. | Mesurer la latence et le coût grâce à l’amélioration de la qualité |
| Fidélité, ancrage, prise en charge des citations | La réponse cite des documents ou se base sur des faits tirés du contexte. | La fidélité ne permet pas de diagnostiquer une analyse incorrecte ou un chargement insuffisant des données. | |
| Le système est-il stable en production ? | Dérive, régénération, mécanisme de secours, latence p95, coût par réponse | Changements de trafic après le lancement | La télémétrie de production nécessite une revue humaine échantillonnée afin de rester calibrée |
Pour une comparaison plus concise des outils, consultez Meilleurs outils et métriques d’évaluation pour RAG en 2026.
Partie 1 : Définir le critère de succès avant l’architecture
Rédigez l’ensemble d’évaluation avant le diagramme d’architecture. Il permet à chaque choix de composant ultérieur d’avoir un objectif mesurable.
Vous ne pouvez pas choisir entre BM25 et la récupération dense, le chunking récursif et sémantique, ou Cohere Rerank et BGE avant de savoir ce que vous optimisez. « Meilleures réponses » n’est pas une métrique. Un cahier des charges illustratif pourrait être : « fidélité ≥ 0,85 sur un ensemble d’entraînement de 200 requêtes couvrant nos trois principales intentions, avec une latence p95 < 1,5 s et un taux d’exclusion fausse par filtrage < 2 % ». Ces chiffres sont des placeholders ; l’essentiel est que la qualité, la couverture, la latence et le filtrage disposent de seuils définis de manière explicite.
Définissez d’abord le harness avant d’écrire le code de récupération. Le premier harness sera incorrect, et vous devrez le corriger. Modifier une métrique coûte bien moins cher que de devoir revoir un système qui a déjà été déployé.
Trois couches pipeline et deux modes de fonctionnement
Le RAG moderne est un pipeline, ce qui implique que son évaluation doit s’effectuer selon des pipeline. Aucune valeur numérique unique ne permet de couvrir tous les modes de défaillance.
L’évaluation en environnement de production comporte trois niveaux pipeline. L’évaluation de l’ingestion vise à vérifier si le corpus ainsi que l’index conservent fidèlement la structure du texte source. L’évaluation en temps de requête s’assure que la réécriture, le filtrage, la récupération des données, reranking, ainsi que l’assemblage du contexte permettent de trouver les preuves appropriées. Enfin, l’évaluation de la réponse et de la production évalue si la réponse fait usage de ces preuves et si sa qualité reste satisfaisante face à un trafic réel. En regroupant ces niveaux en un seul score, les anomalies de normalisation peuvent être masquées au sein d’une note de réponse jugée acceptable.
Ces couches décrivent l’endroit où se produit une défaillance. Les modes offline et online indiquent quand et à partir de quels données s’effectue la vérification. L’évaluation offline utilise une dataset fixe disposant de valeurs de référence connues ; elle est reproductible et convient pour la sélection des composants, les comparaisons A/B ainsi que les contrôles intégrés. L’évaluation online évalue le trafic en temps réel et prend en compte la régénération, le temps d’attente, les retours explicites et l’évolution réelle des requêtes. Elle est plus bruyante et plus difficile à instrumenter.
Chaque couche pipeline peut fournir des vérifications en mode hors ligne ainsi qu’en mode en ligne. Un corpus d’ingestion fixe permet de détecter les régressions du analyseur avant la mise en production, tandis que des moniteurs de fraîcheur et d’échecs de parsing surveillent les mises à jour en temps réel. Un ensemble de requêtes fixe évalue les performances de récupération avant la publication, alors que des traces en temps réel échantillonnées révèlent tout écart par rapport aux conditions de production. Les vérifications exclusivement en mode hors ligne manquent les modifications en temps réel ; celles exclusivement en mode en ligne rendent difficile la reproduction des régressions.
Niveau de composant versus bout en bout
Il existe deux erreurs fréquentes. Une évaluation uniquement en boucle bout en bout indique que le système est défaillant, mais sans préciser l’endroit exact du problème. Une évaluation portant uniquement sur les composants peut montrer que chacune de ses parties fonctionne correctement, alors que le système dans son ensemble échoue malgré tout. La solution consiste à utiliser quelques métriques clés en boucle bout en bout pour prendre des décisions d’acceptation ou de rejet, complétées par des métriques par composant afin de diagnostiquer la cause du dysfonctionnement. Les métriques de récupération permettent de détecter les dégradations du module de récupération des données, tandis que les métriques de génération permettent d’identifier les dégradations du module de génération. Enfin, la correction des réponses en boucle bout en bout permet de repérer les échecs d’intégration.
La référence frameworks (itinéraire déterministe)
| Framework | Idéal pour | Là où cela échoue |
|---|---|---|
| RAGAS | Des métriques RAG sans référence (fidélité, pertinence de la réponse, précision/recouvrement du contexte) ; le vocabulaire de facto | LLM – évaluation du coût ; composantes de score opaques lors du débogage ; paramètres par défaut axés sur l’anglais |
| ARES | Le classifieur entraîné évalue les résultats en fonction de pipeline ; il nécessite moins d’annotations que les approches de type RAGAS ; il offre une précision élevée pour les systèmes à proximité. | Configuration plus lourde : il est nécessaire d’entraîner réellement des modèles. |
| TruLens | Fonctions de retour d’information composables offrant une forte capacité d’explicabilité ; traçage OpenTelemetry ; adaptées à l’environnement de production | Les métriques spécifiques à RAG indiquent moins de batteries incluses que celles de RAGAS. |
| DeepEval | Tests unitaires de style Pytest pour les sorties générées par LLM ; G-Eval, métriques personnalisées, intégration native dans les pipelines CI/CD | Une utilisation intensive de LLM-judge entraîne des pics de coûts. |
| Arize Phoenix | Traçage approfondi et visualisation embedding ; détection visuelle de la dérive embedding ; natif d’OTEL | Vous apportez vos propres définitions de métriques |
| Track TREC 2024 RAG | Public benchmark destiné à l’évaluation des « nuggets » (AutoNuggetizer), au soutien à l’évaluation ainsi qu’à la fluidité sur MS MARCO Segment v2.1 | Ce n’est pas un outil runtime, mais plutôt un benchmark servant à effectuer une calibration par rapport à |
Ma stack par défaut comprend RAGAS pour le vocabulaire des métriques, DeepEval pour les contrôles d’intégration continue, Phoenix pour le traçage en production, ainsi que du code personnalisé destiné aux métriques spécifiques à l’ontologie. Vous finirez inévitablement par avoir besoin de plus que ce avec quoi vous commencez. Choisissez le framework qui facilite la création de métriques personnalisées.
Pour benchmarks, utilisez BEIR (Thakur et al., NeurIPS 2021) pour la généralisation de la récupération sans exemple. MTEB pour une qualité générale embedding, MIRACL pour la récupération multilingue, et le TREC 2024 RAG Piste à des fins d’évaluation bout en bout RAG.
Partie 2 : Associer les points d’évaluation aux pipeline
Un système RAG en environnement de production dépasse de loin le simple cadre consistant à « intégrer des documents, récupérer des fragments et appeler un LLM ». Chaque étape, allant de l’acquisition du document à la livraison de la réponse, peut échouer.
Chaque étape du diagramme dispose d’au moins une métrique. Une étape ne possédant aucune métrique peut échouer sans que personne ne s’en aperçoive.
Les trois voies coïncident aux points où les preuves peuvent être perdues. La voie d’ingestion prend en charge le parsing, le nettoyage, le chunking, embedding, ainsi que l’indexation. La voie en temps de requête gère la réécriture, le filtrage, la récupération, reranking, et l’assemblage du contexte. Enfin, la voie relative à la réponse et à la mise en production s’occupe de la fidélité des résultats, de la vérification des citations, des signaux émis par les utilisateurs, de la dérive des performances, de la latence et des coûts associés.
Les erreurs s’accumulent le long de la chaîne de traitement. Une analyse incorrecte limite la segmentation en chunks. Une segmentation défectueuse entrave la récupération des données. Une récupération insuffisante affecte directement reranking. De même, des reranking médiocres restreignent la capacité de génération. La fidélité ne mesure que la réponse finale, et jamais les causes situées en amont du processus.
Partie 3 : Évaluation de l’ingestion
De nombreuses pannes en environnement de production liées à RAG commencent lors de l’ingestion des données. Le système fonctionne correctement avec des documents de test propres, mais échoue dès qu’il est confronté à des PDF réels, des scans, des tableaux ou des pages de corpus peu structurées.
Acquisition et analyse du document
Qu’il convient de mesurer :
-
Complétude de l’extraction de texte :
extracted_chars / expected_charssur un échantillon étiqueté, calculé pour chaque classe de document. Il n’existe pas de package canonique — écrivez un petit harness qui compare la sortie du analyseur à une référence nettoyée manuellement. Faites attention aux notes de bas de page, en-têtes et légendes manquants. -
Précision OCR : taux d’erreur de caractère (CER) et taux d’erreur de mot (WER), les métriques standards pour le traitement du langage parlé/OCR :
où , , représentent respectivement les substitutions, suppressions et insertions au niveau des caractères, et est le comptage des caractères de référence (sous-indice pour la version par mot). Il ne faut pas appliquer une frontière CER à chaque corpus : il convient de l’ajuster en fonction de la classe du document ainsi que de la perte d’erreur dans les réponses générées ; le texte imprimé, l’écriture manuscrite et les documents multilingues présentent en effet des profils d’erreur distincts. Effectuez les calculs avec
jiwer(jiwer.cer(refs, hyps),jiwer.wer(refs, hyps)) ou HuggingFaceevaluate. Pour les corpus d’évaluation, FUNSD et SROIE Ils sont publics benchmarks.from jiwer import cer, wer refs = ["Mars has two moons, Phobos and Deimos."] hyps = ["Mars has two m00ns, Phobos and Deirnos."] print(f"CER = {cer(refs, hyps):.3f}") # CER = 0.105 print(f"WER = {wer(refs, hyps):.3f}") # WER = 0.286 -
Fidélité de l’extraction de tableaux : TEDS (Similarité basée sur la distance d’édition d’arbres) permet d’évaluer dans quelle mesure l’arbre de tableau HTML prédictif est proche de celui de référence, en normalisant le résultat par la taille de l’arbre le plus volumineux. Zhong et al., 2020 (PubTabNet) :
TEDS fait appel à la fois à la structure (lignes, colonnes, espacements) et au contenu des cellules ; TEDS-S élimine le contenu pour ne considérer que la structure. Implémentation de référence : celui de PubTabNet
teds.py(utilise)apteden interne). Pour les corpus d’évaluation, voir PubTabNet. FinTabNet, ainsi que SciTSR. Les analyseurs naïfs échouent fréquemment avec les tables ; il convient de vérifier benchmark avant de leur faire confiance. -
Préservation du layout/structure : ordre des titres, intégrité des listes, ordre de lecture dans les PDF à plusieurs colonnes. Utiliser DocLayNet pour un benchmark étiqueté. Une comparaison prête à l’emploi peut concerner un analyseur d’éléments tel que
unstructured, une bibliothèque PDF telle quepymupdf, et un analyseur VLM tel quedocling.
Comparez différentes familles de parseurs, tels qu’une version de base de Tesseract, un modèle basé sur VLM et OCR, ainsi que la solution proposée par votre fournisseur. Utilisez un échantillon stratifié composé de classes de documents réelles à une résolution DPI fixe, incluant des scans nets, des photos, des tableaux, du texte multilingue, du contenu mathématique et de l’écriture manuscrite. Indiquez le CER ou le WER pour chaque classe, ainsi que le TEDS pour les pages contenant des tableaux.
Nettoyage et normalisation
-
Précision de suppression des éléments génériques : précision/rappel par rapport aux segments d’éléments génériques étiquetés manuellement. Une suppression trop agressive détruit du contenu pertinent ; une suppression trop lente contamine embeddings. Outils de comparaison :
trafilatura,jusText,Resiliparse. Barbaresi (2021) benchmarks ces confrontations directes. -
Normalisation Unicode : pourcentage de documents générant des sorties NFC et NFKC identiques (calculé à l’aide de la bibliothèque standard)
unicodedata.normalize) Il s’agit d’un signal de dérive utile. Ce sont les incohérences qui provoquent la destruction du taux de rappel dans les systèmes de récupération, en raison des caractères de jonction à largeur nulle et des caractères similaires. -
Précision de détection de langue : valeur F1 sur un échantillon multilingue étiqueté. Critique pour les index multilingues. Utiliser
fasttext-langdetect(Facebook’slid.176),lingua-py, oucld3. FLORES-200 Il fournit du texte d’évaluation dans 200 langues, mais c’est le mélange de langues utilisé en production qui doit déterminer l’échantillon de test. -
Efficacité de la déduplication (MinHash / LSH) : précision/rappel de votre détecteur de quasi-dupliques par rapport à un ensemble étiqueté manuellement. Idée de base : estimer la similarité de Jaccard J(A, B) = \frac|A ∩ B|}{|A ∪ B|} entre les ensembles de fichiers de document via hachages de permutation aléatoire (Broder, 1997) et regrouper les quasi-dupliques au moyen de la bandage LSH (Indyk et Motwani, 1998). Effectuez un balayage du comptage des hachages ainsi que du seuil de Jaccard sur votre corpus. Suivez séparément le taux de fusion fausse (qui corrompt les réponses) et le taux de fusion manquée (qui gaspille de l’espace d’indexation).
datasketchil fournit l’implémentation utilisée ci-dessous ; ses paramètres sont à titre indicatif :from datasketch import MinHash, MinHashLSH def shingles(text: str, k: int = 5) -> set[str]: text = text.lower() return {text[i:i + k] for i in range(len(text) - k + 1)} def to_minhash(text: str, num_perm: int = 128) -> MinHash: m = MinHash(num_perm=num_perm) for s in shingles(text): m.update(s.encode("utf-8")) return m docs = { "d1": "Mars has two moons, Phobos and Deimos.", "d2": "Mars has two moons, Phobos and Deimos!", # near-dup "d3": "Curiosity rover landed on Mars in 2012.", } lsh = MinHashLSH(threshold=0.8, num_perm=128) for did, text in docs.items(): lsh.insert(did, to_minhash(text)) print(sorted(lsh.query(to_minhash(docs["d1"])))) # ['d1', 'd2'] -
Nettoyage des PII : précision et rappel, calculés séparément pour chaque type d’entité (adresses e-mail, numéros de sécurité sociale, noms, adresses). Les erreurs de rappel génèrent des risques en matière de conformité ; les erreurs de précision nuisent à la qualité des réponses. Définir le point de fonctionnement en collaboration avec l’équipe juridique. Parmi les outils envisagés figurent Microsoft Presidio,
scrubadub, ou un modèle NER affiné sur un ensemble étiqueté.
Le découpage en chunks influence la qualité de la récupération
Le découpage en chunks peut engendrer un écart de rappel à plusieurs points, même lorsque le modèle embedding reste inchangé. Dans Le fournisseur NVIDIA pour 2024 benchmark, Le découpage au niveau de page a permis d’obtenir la plus haute précision ainsi que la plus faible variance pour les documents paginés. Il convient de considérer ce résultat comme une preuve spécifique au corpus testé, et non comme une solution valable pour tous les cas.
Le chunking sémantique regroupe des phrases adjacentes en fonction de leur similarité embedding et effectue des coupes aux frontières où cette similarité diminue. LangChain’s SemanticChunker et celui de LlamaIndex SemanticSplitterNodeParser Mettez en œuvre cette stratégie. Elle permet d’améliorer le taux de rappel par rapport aux fenêtres fixes lorsque les frontières thématiques jouent un rôle important.
La séparation récursive des caractères teste d’abord les sauts de paragraphe, puis les sauts de phrase, et enfin les sauts de mot, jusqu’à ce que chaque morceau corresponde à la taille cible. LangChain’s RecursiveCharacterTextSplitter Il met en œuvre cette séquence : sélectionnez des valeurs de fenêtre et d’overlap adaptées à la structure de votre document, puis laissez l’ensemble doré déterminer les valeurs finales.
Métriques à suivre :
- Coérence des blocs : , où désignent les phrases embeddings. Les blocs sains présentent une similitude interne élevée et une dissimilitude à leurs frontières. Le calcul s’effectue avec
sentence-transformersde plusscikit-learn’scosine_similarity. - Qualité des frontières : étiquetage manuel indiquant « s’agit-il d’une coupe pertinente ? » pour un échantillon, complété par une vérification structurelle garantissant que les fragments ne divisent pas de tableaux, de listes ou de sections numérotées.
- Taille optimale des fragments : tester différentes tailles de tokens (128, 256, 512, 1024) et tracer le graphique Recall@k en fonction de la taille sur l’ensemble de référence. Identifier le point de transition optimal ; il ne faut pas se fier aveuglément aux recommandations des tutoriels.
- Efficacité de l’overlap : varier plusieurs proportions d’overlap et mesurer le Recall@k correspondant. Cesser d’augmenter l’overlap dès que la courbe de recall locale devient plate ou lorsque le coût lié aux doublons dépasse les avantages obtenus.
- Fidélité de l’attribution des fragments : pourcentage de fragments conservant un pointeur source vérifiable (numéro de page, ancre de section, ID de document). Cela est indispensable pour assurer l’auditable des résultats.
- Chunkage tardif vs. précoce : agrégation tardive des blocs (Günther et al., 2024) incorpore l’ensemble du document avant de le segmenter, ce qui permet de préserver le contexte global (implémentation de référence dans
jina-embeddings-v3). Recherche contextuelle (Anthropic, 2024) ajoute en préfixe à chaque chunk un contexte généré par LLM. Ces deux approches entraînent une augmentation des coûts. Il est donc nécessaire d’effectuer une Benchmark sur votre corpus avant de choisir l’une ou l’autre solution.
Mon avis : le découpage structurel (division selon les titres, les tableaux et les sections — mis en œuvre par des analyseurs tels que unstructured.io ou en parcourant l’AST déjà généré par votre analyseur) est sous-utilisé. Si vos documents présentent une structure, utilisez‑la avant d’ajouter des heuristiques de similarité. La segmentation récursive des caractères constitue la référence de base ; le chunking sémantique justifie les surcoûts principalement pour du texte non structuré.
Extraction et enrichissement des métadonnées
- NER précision/rappel/F1 : par type d’entité, sur un sous-ensemble étiqueté. Selon le format standard CoNLL/MUC. Calculé à l’aide de
seqeval(from seqeval.metrics import f1_score) pour la version prenant en compte les étiquettes BIO/IOB, ou scikit-learn pour les comparaisons de ensembles d’intervalles. CoNLL-2003 et OntoNotes 5.0 constituent les corpus de référence canoniques. - F1 de l’extraction de relations : encore plus crucial pour les systèmes fondés sur des ontologies. Étiquetez manuellement un ensemble stratifié par type de relation et classe de document. TACRED et DocRED sont des bases de données publiques benchmarks ; parmi les implémentations candidates on trouve
opennreetspaCyrelation pipelines. - Précision d’extraction du titre ou de l’intitulé : correspondance exacte associée à une similarité de Levenshtein normalisée () par rapport aux valeurs de vérité absolue —
python-LevenshteinourapidfuzzFournir les deux éléments en une seule requête. - Préservation des métadonnées hiérarchiques : pourcentage de fragments qui conservent correctement leur section parente, leur document parent et leur chemin d’ascendance. C’est ce métrique qui détermine si votre RAG est capable de répondre à des questions du type « Qu’est-ce que le enfant de la politique X dit ? ».
Génération Embedding
- Sélection du modèle benchmarks : MTEB pour les capacités générales (le nDCG@10 reste l’indicateur principal ; le paquet Python MTEB ce qui vous permet de reproduire le classement localement), BEIR pour la généralisation sans exemple. MIRACL Dans le domaine multilingue, les meilleurs modèles de récupération s’agrègent en une bande étroite autour de la valeur nDCG@10, mais les scores MTEB en anglais ne permettent pas de prédire de manière fiable les performances sur les langues à faibles ressources.
- Évaluation spécifique au domaine : il ne faut pas considérer un classement général benchmark comme un résultat propre à ce domaine. Il convient de définir un ensemble d’or du domaine à partir de sa matrice de couverture ainsi que de l’incertitude que votre décision peut supporter. Ensuite, réclasser les modèles candidats sur cet ensemble.
ranxoupytrec_eval. Un ensemble de domaines peut inverser l’ordre d’un classement, il convient donc de publier la tranche dataset, le protocole de récupération ainsi que l’intervalle de confiance accompagnant le résultat. - Embedding détection de dérive : suivre la dérive distributive basée sur le KL ou la dérive fondée sur le modèle entre une fenêtre de référence fixe et des embeddings en production dynamiques ; mesurer également la stabilité par voie de voisins les plus proches pour un ensemble d’échantillons de test fixe.
evidentlyetalibi-detectMettre en œuvre des détecteurs basés sur des modèles et des méthodes statistiques. Evidently’s étude comparative Il s’agit d’une évaluation de fournisseur ; comparez les méthodes en fonction des variations connues au sein de vos propres embeddings. - Multi-vector vs. single-vector : l’interaction tardive permet de conserver les représentations au niveau des tokens, au lieu de réduire chaque document en un seul vecteur ; ColBERT Il s’agit du design canonique, accompagné d’implémentations de référence en RAGatouille et PyLate. Une telle représentation plus riche augmente les coûts d’indexation et de récupération. Avant de l’adopter, comparez sa qualité, ses besoins en stockage ainsi que sa latence par rapport à une référence à vecteur unique sur le même ensemble de domaines.
Construction de l’index
- Recall@k sous approximation : comparer l’index de voisin le plus proche approximatif (ANN) à une référence par force brute exacte pour le même k — en FAISS, cela correspond à
IndexHNSWFlat(ouIndexIVFFlat) contreIndexFlatIP/IndexFlatL2. Définissez la perte de rappel acceptable à partir de votre budget de qualité en aval. Leann-benchmarksLe projet suit les courbes Pareto du taux de rappel et du QPS au sein des différentes bibliothèques. - HNSW de réglage : HNSW (Hierarchical Navigable Small World — un graphe de proximité en couches ; voir Malkov et Yashunin, 2018, implémenté en
hnswlib, FAISS’sIndexHNSWFlat, et la plupart des bases de données vectorielles) exposent trois paramètres réglables :M(graphique de diffusion),efConstruction(largeur candidate en temps de compilation)efSearch(Largeur des candidats en temps de requête). Partez des valeurs par défaut documentées dans la bibliothèque, puis ajustez progressivement les paramètres jusqu’à ce que la courbe rappel-latence satisfasse les exigences de votre ensemble d’évaluation. - Ajustement de l’IVF : IVF (index de fichier inversé — vectorisation des partitions à l’aide de k-means)
nlistles cellules, puis lors de l’exécution de la requête, effectuer un balayage desnprobecellules les plus proches ; voir FAISS’sIndexIVFFlatetIndexIVFPQ). Balayagenlistetnprobeen fonction du taux de rappel et de la latence liés à la recherche exacte. Benchmark les requêtes filtrées sont traitées séparément, car les familles d’index et les bases de données vectorielles mettent en œuvre le parcours des filtres de manière différente. - Retard de mise à jour de la fraîcheur : délai entre l’enregistrement d’un document et sa disponibilité pour la récupération. Suivre les valeurs p50 et p99. Pour les systèmes soumis à des exigences réglementaires, il convient également de suivre le pourcentage de requêtes traitées à l’aide d’index obsolètes.
Partie 4 : Évaluation en temps de requête
La voie de temps de requête contient les métriques permettant de diagnostiquer le parcours de récupération des données. Le taux de rappel @k seul ne permet pas de déterminer si c’est la réécriture, le filtrage, reranking, ou l’assemblage du contexte qui est à l’origine de l’échec.
Compréhension et réécriture des requêtes
- Qualité d’expansion des requêtes : amélioration du Recall@k sur votre ensemble de référence, entre la requête étendue et la requête brute. Prédefinissez avant les tests le gain utile minimal ainsi que son niveau d’incertitude. Si l’expansion ne parvient pas à franchir ce seuil local, elle ne justifie ni sa latence ni son coût. Les références classiques basées sur le PRF (pseudo-relevance feedback) telles que RM3 et Bo1 Ce sont toujours des vérifications de validité utiles ; l’expansion basée sur LLM doit les surpasser en efficacité.
- Évaluation HyDE : HyDE (Gao et al., 2022) génère une réponse hypothétique à l’aide de LLM, l’incorpore dans le résultat et effectue une recherche en se basant sur celle-ci. Cette approche entraîne un retard de génération ainsi que de nouvelles surfaces de défaillance. Il convient de mesurer séparément le taux de rappel@10 pour les données appartenant au domaine, celles provenant d’un domaine externe, et celles présentant un faible niveau de confiance, avant de décider si cette méthode doit être intégrée dans le flux par défaut, utilisée comme solution de secours, ou ni l’un ni l’autre.
- Génération de multiples requêtes : union Recall@k de N réécritures par rapport à une seule requête. Variez la valeur de N et sélectionnez un point sur votre frontière rappel-latence. Implémentations : LangChain’s
MultiQueryRetriever, LlamaIndex’sQueryFusionRetriever. - Précision de classification des intentions : précision/recall/F1 standards par intention (calculer avec
sklearn.metrics.classification_report), mais la métrique clé est la correctitude du routage — le pipeline cible approprié est-il bien invoqué ? - Routage adaptatif : Adaptatif-RAG (Jeong et al., NAACL 2024) démontrent que toutes les requêtes ne méritent pas la même stratégie de récupération. Il convient donc d’aborder la mesure de l’exactitude du routage comme un problème de classification, en se basant sur un ensemble étiqueté comprenant les cas « pas de récupération nécessaire / une seule itération / itératif ».
Métriques de récupération
Il s’agit des métriques de référence. Si vous ne les suivez pas, il vous est impossible de déterminer si la récupération s’améliore.
| Métrique | Ce qu’il mesure | Quand l’utiliser |
|---|---|---|
| Recall@k | fraction des documents pertinents d’une requête restitués parmi les k premiers | À utiliser lorsque la présence de l’un quelconque des éléments du ensemble concerné est critique |
| Précision@k | pourcentage de top-k pertinents | |
| MRR | moyenne de 1/rang du premier document pertinent | lorsque les utilisateurs ne consultent que les résultats du top-1 ou du top-3 |
| nDCG@k | gain pondéré par la perte de position et les notes de pertinence | métrique de récupération standard pour l’adéquation graduée |
| MAP | moyenne de la précision moyenne sur les requêtes | lorsque l’on se soucie de toute la liste classée |
| Taux de réussite@k | si au moins un document pertinent apparaît parmi les k premiers | Calculer la moyenne du résultat binaire sur l’ensemble des requêtes afin d’obtenir rapidement une métrique de vérification. |
| Couverture | pourcentage de documents « golden » jamais récupérés au cours de toutes les requêtes | détecte les lacunes systématiques dans l’index |
Les formules, à titre de référence (pertinence binaire avec l’ensemble pertinent pour la requête , et si le -ème document récupéré appartient à ) :
\text{Recall@k} = \frac{|R_q \cap \{d_1, \dots, d_k\}|}{|R_q|}, \quad \text{Precision@k} = \frac{|R_q \cap \{d_1, \dots, d_k\}|{k}Pour la pertinence graduée, ; le nDCG binaire constitue le cas particulier utilisé dans le code ci‑dessous. MAP représente la moyenne, parmi toutes les requêtes, de . Voir Manning, Raghavan, Schütze, Introduction à la récupération d’informations, Chapitre 8 : Dérivations.
Pour le code en production, utilisez ranx, pytrec_eval, ou ir_measures — Ils prennent en charge l’ensemble de la famille de métriques TREC et gèrent correctement la pertinence graduée. Définissez des objectifs de déploiement en vous basant sur un ensemble de référence réaliste, sur la qualité des réponses produites et sur le coût d’une erreur de classification. Ne copiez pas les seuils proposés dans des tutoriels.
Le test harness pour ceux-ci est court. Vous pouvez le lancer depuis un notebook avant même d’avoir choisi une base de données vectorielle.
from math import log2
from statistics import mean
# synthetic gold set: query_id -> set of relevant doc ids
gold = {
"q1": {"d3"},
"q2": {"d7", "d2"},
"q3": {"d11"},
"q4": {"d5"},
}
# ranked retrieval results: query_id -> ranked list of doc ids (top-10)
runs = {
"q1": ["d8", "d3", "d1", "d4", "d2", "d9", "d6", "d10", "d12", "d13"],
"q2": ["d2", "d6", "d4", "d7", "d1", "d3", "d8", "d11", "d5", "d9"],
"q3": ["d11", "d2", "d3", "d4", "d1", "d6", "d7", "d8", "d10", "d12"],
"q4": ["d1", "d2", "d3", "d6", "d8", "d9", "d10", "d12", "d13", "d14"],
}
def recall_at_k(ranked, gold_set, k):
if not gold_set:
return 0.0
hit = sum(1 for d in ranked[:k] if d in gold_set)
return hit / len(gold_set)
def reciprocal_rank(ranked, gold_set):
# MRR contribution per query: 1/rank of the first relevant doc.
for rank, d in enumerate(ranked, start=1):
if d in gold_set:
return 1.0 / rank
return 0.0
def ndcg_at_k(ranked, gold_set, k):
# binary relevance: rel ∈ {0, 1}
gains = [1.0 if d in gold_set else 0.0 for d in ranked[:k]]
dcg = sum(g / log2(i + 2) for i, g in enumerate(gains))
# ideal DCG: all gold docs ranked first, capped by k
n_gold_in_topk = min(k, len(gold_set))
idcg = sum(1.0 / log2(i + 2) for i in range(n_gold_in_topk))
return dcg / idcg if idcg else 0.0
K = 5
print(f"Recall@{K}: {mean(recall_at_k(runs[q], gold[q], K) for q in gold):.3f}")
print(f"MRR: {mean(reciprocal_rank(runs[q], gold[q]) for q in gold):.3f}")
print(f"nDCG@{K}: {mean(ndcg_at_k(runs[q], gold[q], K) for q in gold):.3f}")
# Recall@5: 0.750
# MRR: 0.625
# nDCG@5: 0.627
Il s’agit de votre porte de contrôle CI pour la récupération des données. Connectez-la à un sous-ensemble rapide basé sur le taux de couverture pour chaque PR, et exécutez l’ensemble complet de référence lors de la porte de déploiement plus lente. Bloquez la fusion lorsque une métrique préenregistrée dépasse son budget de régression.
Le dépôt associé fixe les valeurs numériques précises mentionnées ci-dessus.Recall@5 = 0.750, MRR = 0.625, nDCG@5 = 0.627) en tant que test unitaire dans tests/test_retrieval_metrics.py; Notebook 01 effectue des analyses de Recall@k / MRR / nDCG sur un index réel SciFact, et la version destinée à la production harness est utilisée en evaluation/retrieval.py.
Fusion hybride de la récupération d’informations et du rang réciproque
BM25 il s’agit d’un évaluateur lexical dispersif qui combine la correspondance terme à terme, le pondération des termes et la normalisation de la longueur. Il est disponible dans rank_bm25Elasticsearch, OpenSearch, ainsi que la plupart des moteurs de recherche.
Fusion de rangs réciproques (Cormack, Clarke, et Buettcher, SIGIR 2009) intègre le BM25 aux classements densifs basés sur la position. L’original k=60 Cette configuration constitue une référence utile. Le RRF étant indépendant de la valeur de score, il élimine la normalisation inter-lignes nécessaire en cas d’interpolation linéaire. Lorsque l’ensemble étiqueté est suffisamment volumineux pour permettre d’estimer une différence stable, il convient également d’évaluer une combinaison convexe et d’ajuster la valeur de α.
L’utilisation d’une récupération hybride associée à un encodage croisé reranker améliore fréquemment la qualité des corpus techniques, de type journal de logs ou de code. Les gains peuvent être limités pour les corpus fortement sémantiques. Il est recommandé de comparer les résultats avec ceux obtenus en utilisant uniquement des données denses ou uniquement des données sparses, car une configuration de fusion inefficace peut entraîner de performances inférieures par rapport à l’un ou l’autre type d’entrée.
La mise en œuvre tient sur quelques lignes seulement.
from collections import defaultdict
# two retrieval lanes: dense embeddings and BM25.
dense = ["d3", "d7", "d1", "d4", "d2", "d9", "d10"]
sparse = ["d2", "d3", "d8", "d1", "d11", "d4", "d6"]
def rrf(rankings: list[list[str]], k: int = 60) -> list[tuple[str, float]]:
"""Reciprocal Rank Fusion (Cormack et al., SIGIR 2009).
score(d) = sum over rankings of 1 / (k + rank(d))
Score-agnostic: only rank position matters. k=60 is the canonical default.
"""
scores: dict[str, float] = defaultdict(float)
for ranking in rankings:
for rank, doc in enumerate(ranking, start=1):
scores[doc] += 1.0 / (k + rank)
return sorted(scores.items(), key=lambda kv: kv[1], reverse=True)
fused = rrf([dense, sparse], k=60)
for doc, score in fused[:5]:
print(f"{doc} score={score:.5f}")
# d3 score=0.03252 <- rank 1 dense, rank 2 sparse
# d2 score=0.03178 <- rank 5 dense, rank 1 sparse
# d1 score=0.03150
Remarquez ce que RRF ne fait pas : il ne prend jamais en compte les scores de similarité bruts. Un retriever dense qui affiche une valeur de cosinus de 0,98 et un algorithme BM25 qui donne un score de 17,4 ne sont pas directement comparables. Si vous les normalisez à l’aide de scores z ou d’une échelle min-max, vous risquez de privilégier le résultat présentant la plus grande variance au sein de ce lot.
RRF ne prend en compte que le rang. Si un récupérateur place un document à la position 2, ce vote vaut 1 / (60 + 2), quel que soit le score brut qui l’a généré.
Hybride + RRF sur SciFact : Notebook 02 compare les algorithmes dense, BM25 et RRF en utilisant des deltas par requête. Le fusionneur adapté au environnement de production est présent dans retrieval/hybrid_rrf.py; tests/test_rrf.py fixe la version canonique d3 / d2 / d1 Commander en k=60.
Reranking
- ΔnDCG / ΔMRR : la seule métrique fiable reranker – elle mesure l’amélioration par rapport à un système sans classement, sur votre ensemble de référence, à la profondeur réellement utilisée par votre application. Elle se calcule en évaluant vos métriques de récupération avec et sans le reranker sur des ensembles de candidats identiques.
- Encodage croisé vs. encodage binaire : un encodage binaire incorpore indépendamment la requête et le document (un vecteur pour chaque élément) puis calcule la similarité par produit scalaire ; un encodage croisé concatène la requête et le document avant d’exécuter une seule passe forward qui prend en compte les deux simultanément. Les encodages croisés sacrifient une passe forward par candidat au profit d’interactions requête-document plus riches. Implémentation de référence :
sentence-transformersCrossEncoder. Benchmark la pertinence et la latence sur le matériel spécifique, la taille des lots ainsi que la profondeur de recherche ; il ne faut pas transférer les résultats d’un modèle ou d’un service géré dans un autre environnement. - Par paires vs. globalement : le calcul par paires évalue indépendamment chaque paire (recherche, document) ; le calcul global évalue l’ensemble des candidats en même temps, permettant ainsi au modèle de comparer ces derniers. Évaluer les deux méthodes sur les mêmes ensembles de candidats. Calibrer les seuils de score pour chaque modèle et corpus, plutôt que de considérer un exemple publié comme universellement applicable.
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")
query = "How do I rotate database credentials in production?"
candidates = [
"Production database credentials are rotated via Vault every 30 days.",
"The new logo was unveiled at the all-hands meeting.",
"To rotate prod DB creds, run the `rotate-secrets` GitHub Action.",
]
scores = reranker.predict([(query, c) for c in candidates])
ranked = sorted(zip(candidates, scores), key=lambda x: -x[1])
for doc, score in ranked:
print(f"{score:+.3f} {doc}")
Un reranker constitue un candidat à fort potentiel pour une RAG pipeline de base, mais ce n’est pas une solution garantie. Mesurez sa ΔPrecision@1 ainsi que sa ΔnDCG sur votre ensemble de référence, puis conservez-le uniquement si l’amélioration obtenue est supérieure à ses limites en termes de latence et de coût. Comparez ensuite cette amélioration mesurée avec celles obtenues grâce à des modifications plus modestes du système de récupération avant de choisir la prochaine étape d’optimisation.
ΔnDCG et ΔPrecision@1 obtenus à l’aide d’un cross-encoder sur SciFact : Notebook 03; module : retrieval/reranker.py.
Construction du contexte et problème du « perdu au milieu »
C’est là que surviennent de nombreux échecs de type « bonne récupération des données, mauvaise réponse ».
- Relevance contextuelle : score de pertinence par morceau RAGAS
ContextRelevancyou un encodeur croisé, agrégé sous forme de moyenne ainsi que en pourcentage des blocs se situant en deçà d’un seuil donné. - Utilisation du contexte : parmi les fragments placés dans le contexte, quel est le nombre qui ont réellement été cités ou utilisés dans la réponse. Le calcul s’effectue selon la formule \frac|\text{chunks cités}|}{|\text{chunks récupérés}|Calculer \ sur un échantillon étiqueté. Définir le seuil de fonctionnement en fonction de la qualité de la réponse et du coût par token, plutôt que d’utiliser un pourcentage universel.
- Détection du type « perdu au milieu » : évaluation synthétique dans laquelle le fragment de référence est placé aux positions {premier, milieu, dernier} d’un contexte long, afin de mesurer la justesse de la réponse. La dégradation en forme de U est réelle et a été documentée dans Liu et al. (TACL 2023). Les modèles modernes offrent de meilleures performances que ceux de l’époque 2023, mais le biais persiste. Mesures d’atténuation : effectuer un reranking suivi d’un retraitement de l’ordre des éléments top-k afin que le fragment ayant la note la plus élevée apparaisse en premier ou en dernier (dans LangChain’s
LongContextReorderfait exactement cela), ou comprime de manière agressive les blocs intermédiaires. Mesurez avec une évaluation stratifiée par position, et non seulement avec un score global. Un exemple d’évaluation stratifiée par position fonctionnelle et exécutable se trouve dans Notebook 06 (module :evaluation/lost_in_middle.py). - Compression du contexte : indiquer le taux de compression (tokens d’entrée / tokens de sortie) en même temps que la justesse de la réponse. Parmi les outils disponibles, on trouve LangChain’s
ContextualCompressionRetrieveret LongLLMLingua. Définissez à l’avance la plus grande perte de correction acceptable en fonction du risque associé à l’application ainsi que de son budget de tokens, puis rejetez les configurations qui dépassent cette limite.
Partie 5 : Le taux de faux rejet du filtre
Ce métrique bénéficie d’une section dédiée, car les scores de récupération agrégés ne permettent pas d’attribuer une erreur de recherche au filtre.
Un filtre métadonnées strict comme tenant_id = X AND product = Y AND locale = en-US Il peut réduire le taux de rappel efficace à zéro. Un Recall@k correctement implémenté permet de détecter cette perte, car son dénominateur reste l’ensemble initial des documents pertinents. Il ne permet cependant pas de déterminer si c’est le filtre, le récupérateur ou le classeur qui est à l’origine de cette omission. La fiabilité peut encore sembler bonne, puisque l’évaluation se fait sur le contexte récupéré incomplet ; le modèle a donc fidèlement indiqué « Je ne sais pas ».
La branche rouge dans l’arbre représente l’échec le plus fréquent : le document souhaité existe bien, mais le filtre le supprime avant sa récupération.
La métrique
filter_false_exclusion_rate =
(# queries where all gold docs were excluded by metadata filter) /
(# queries with at least one gold doc)
Cette définition au niveau de la requête prend en compte les exclusions catastrophiques : aucun document pertinent ne survit. Pour les requêtes multi-or, le Recall@k standard entraîne toujours une perte partielle ; il convient d’ajouter un taux d’exclusion par document si ce critère est important. Pour calculer l’un ou l’autre de ces taux, il faut (a) les identifiants des documents réels pour chaque requête d’évaluation et (b) un système d’instrumentation qui enregistre les prédicats de filtrage appliqués, et non seulement les résultats finaux. Déterminez l’objectif en fonction du coût lié à l’exclusion d’une réponse valide ainsi que de l’intervalle de confiance de votre échantillon de production.
Voici une implémentation fonctionnelle. Elle compare le taux de rappel standard correct à un évaluateur invalide qui redéfinit la pertinence après filtrage.
# A small worked example where hard filters remove relevant documents.
docs = [
{"id": "d1", "tenant": "acme", "locale": "en-US"},
{"id": "d2", "tenant": "acme", "locale": "en-GB"},
{"id": "d3", "tenant": "globex", "locale": "en-US"},
{"id": "d4", "tenant": "acme", "locale": "en-US"},
{"id": "d5", "tenant": "acme", "locale": "de-DE"},
]
queries = [
# the gold doc lives in en-GB but the dynamic filter forced en-US
{"qid": "q1", "gold": {"d2"}, "filter": lambda d: d["locale"] == "en-US"},
# the gold doc is correctly within the tenant filter
{"qid": "q2", "gold": {"d4"}, "filter": lambda d: d["tenant"] == "acme"},
# the gold doc is in a different tenant and gets dropped
{"qid": "q3", "gold": {"d3"}, "filter": lambda d: d["tenant"] == "acme"},
# the gold doc passes the filter (de-DE locale match)
{"qid": "q4", "gold": {"d5"}, "filter": lambda d: d["locale"] == "de-DE"},
]
def filter_false_exclusion_rate(queries, docs):
n_with_gold, n_excluded = 0, 0
for q in queries:
if not q["gold"]:
continue
n_with_gold += 1
survivors = {d["id"] for d in docs if q["filter"](d)}
if not (q["gold"] & survivors):
n_excluded += 1
return n_excluded / n_with_gold if n_with_gold else 0.0
rate = filter_false_exclusion_rate(queries, docs)
print(f"filter_false_exclusion_rate = {rate:.2%}")
# filter_false_exclusion_rate = 50.00%
# Correct Recall@k keeps the original gold set as its denominator.
def standard_recall_at_k(queries, docs, k=10):
recalls = []
for q in queries:
survivors = [d for d in docs if q["filter"](d)][:k]
survivor_ids = {d["id"] for d in survivors}
recalls.append(len(q["gold"] & survivor_ids) / len(q["gold"]))
return sum(recalls) / len(recalls) if recalls else 0.0
print(f"standard recall@10 = {standard_recall_at_k(queries, docs):.2%}")
# standard recall@10 = 50.00%
# INVALID: rebuilding the gold set after filtering changes the question.
# It drops queries whose relevant documents did not survive, then scores 100%.
def invalid_recall_over_filtered_gold(queries, docs, k=10):
recalls = []
all_doc_ids = {d["id"] for d in docs}
for q in queries:
all_survivors = {d["id"] for d in docs if q["filter"](d)}
filtered_gold = q["gold"] & all_doc_ids & all_survivors
if not filtered_gold:
continue
top_k_ids = set(list(all_survivors)[:k])
recalls.append(len(filtered_gold & top_k_ids) / len(filtered_gold))
return sum(recalls) / len(recalls) if recalls else 0.0
invalid = invalid_recall_over_filtered_gold(queries, docs)
print(f"INVALID recall (filtered gold) = {invalid:.2%}")
# INVALID recall (filtered gold) = 100.00%
assert rate == 0.5
assert standard_recall_at_k(queries, docs) == 0.5
assert invalid == 1.0
La moitié des requêtes perdent leur document de référence en raison du filtre, ce qui fait chuter le taux de rappel correct @10 à 50 %. Ce score permet de détecter le symptôme, mais il n’est pas en mesure d’en attribuer la cause. Le taux d’exclusion fausse indique que le prédicat a supprimé deux réponses avant même que le récupérateur ne fonctionne. L’évaluateur délibérément invalide affiche 100 % uniquement parce qu’il élimine ces échecs de son ensemble de référence. Aucun modèle ne peut récupérer un document qui a été filtré.
Le taux de 50 % mentionné ci-dessus est reproduit sous forme de test unitaire dans le repo associé : tests/test_filter_exclusion.py::test_50_percent_exclusion_rate. Notebook 04 on l’exécute sur SciFact à l’aide de métadonnées synthétiques afin de pouvoir observer un filtre réel éliminer complètement le taux de rappel ; la métrique runtime (accompagnée de ses indicateurs associés de précision/pourcentage de rappel) se trouve dans evaluation/filter_exclusion.py.
Métrique associée : précision et rappel du prédicat
Lorsque le filtrage est dynamique (par exemple, un LLM extrait les prédicats de filtrage à partir de la requête), considérez l’extracteur de prédicats comme un modèle de classification et évaluez‑le en conséquence. Mesurez la précision et le rappel des prédicats à l’aide d’un ensemble étiqueté de (query, correct predicate) paires. Un taux d’erreur prédicatif ne correspond pas directement à une perte en points équivalente dans les métriques de rappel ; il convient plutôt d’évaluer la fréquence à laquelle ces erreurs excluent un document de référence. Une fois que ce filtre strict a éliminé le document de référence, aucune quantité de reranking ne peut y remédier.
Amélioration douce versus filtre strict
Cette métrique impose une décision de conception. Il convient d’utiliser des filtres stricts lorsque la correction est binaire : juridiction légale, limites des ACL, état publié contre version草案. À l’inverse, des surpondérations douces doivent être appliquées lorsque la pertinence est évaluée sur une échelle : préférences locales, date de publication, version. En l’absence d’une mesure du taux d’exclusion, il devient difficile de détecter le mauvais choix.
La règle de décision, mesurable :
For each filter predicate F:
hard_recall_F = retrieval_recall@k with F as a hard filter
soft_recall_F = retrieval_recall@k with F as a +0.X rerank boost
hard_precision = relevant_in_top_k / k under hard filter
soft_precision = relevant_in_top_k / k under soft boost
exclusion_rate = % of queries where the gold doc was filtered out (hard)
Use hard filter only if exclusion_rate < ε AND hard_precision >> soft_precision.
Otherwise prefer soft boost.
Choisissez ε en fonction du risque lié à une exclusion erronée, des avantages d’une plus grande précision, ainsi que de la taille de l’échantillon d’évaluation. Un article dédié de cette série aborde plus en détail ce compromis.
Partie 6 : Évaluation de la génération
Les métriques de récupération vous indiquent si le système peut répondre correctement. Elles ne vous disent pas s’il l’a fait réellement. Les métriques de génération comblent cette lacune.
Fidélité et ancrage
Fidélité RAGAS décompose la réponse en affirmations atomiques (énoncés factuels courts et autonomes), puis vérifie chacune d’elles à l’aide d’un juge LLM en s’appuyant sur le contexte récupéré :
Le pourcentage de prétentions pris en charge correspond à ce score. Cette structure est plus utile que n’importe quel chiffre isolé, car elle indique quelles prétentions ne sont pas soutenues. Le code de production se trouve dans le ragas package — son utilisation se présente comme suit :
from datasets import Dataset
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision
samples = Dataset.from_dict({
"question": ["How many moons does Mars have?"],
"answer": ["Mars has two moons, Phobos and Deimos."],
"contexts": [["Mars has two moons named Phobos and Deimos."]],
"ground_truth": ["Mars has two moons."],
})
result = evaluate(samples, metrics=[faithfulness, answer_relevancy, context_precision])
print(result)
Ci-dessous se trouve la même boucle déroulée, avec un juge de remplacement déterministe, afin que vous puissiez observer sa structure du début à la fin.
def extract_claims(answer: str) -> list[str]:
# Production: an LLM call that decomposes the answer.
# Demo: split on sentence-final punctuation.
return [c.strip() for c in answer.replace("?", ".").replace("!", ".").split(".") if c.strip()]
def verify_claim(claim: str, context: str) -> bool:
# Production: an NLI (natural-language inference) model or LLM judge.
# Demo: a deterministic stand-in so the example runs offline.
entailed_pairs = {
"Mars has two moons": True,
"Phobos and Deimos orbit Mars": True,
"Mars has a thick atmosphere": False, # unsupported by context
"Curiosity landed in 2012": True,
}
for k, v in entailed_pairs.items():
if k.lower() in claim.lower() or claim.lower() in k.lower():
return v
words = [w.lower() for w in claim.split() if len(w) > 3]
return all(w in context.lower() for w in words) if words else False
context = (
"Mars has two moons, Phobos and Deimos. NASA's Curiosity rover "
"landed on Mars in 2012."
)
answer = (
"Mars has two moons. Phobos and Deimos orbit Mars. "
"Mars has a thick atmosphere. Curiosity landed in 2012."
)
claims = extract_claims(answer)
verdicts = [(c, verify_claim(c, context)) for c in claims]
faithfulness = sum(1 for _, ok in verdicts if ok) / len(verdicts)
for c, ok in verdicts:
print(f" [{'✓' if ok else '✗'}] {c}")
print(f"faithfulness = {faithfulness:.2f}")
# faithfulness = 0.75 (one unsupported claim about the atmosphere)
La structure est cruciale. En environnement de production, verify_claim cela devient un modèle NLI ou une invocation de LLM. Le reste des étapes de harness reste inchangé : extraction, vérification, agrégation.
Extraction et vérification bout en bout des affirmations concernant les réponses SciFact générées : Notebook 05; module : evaluation/faithfulness.py. Le dépôt exécute également un vérificateur inter-familles de type HHEM au sein du même boucle, afin que vous puissiez déterminer quelle famille de juges est en accord avec laquelle.
Une alternative spécifiquement conçue pour remplacer LLM en tant que juge HHEM-2.1-Open (Hughes Hallucination Evaluation Model, Vectara), un classifieur affiné pour la détection des hallucinations. Sa fiche de modèle décrit le checkpoint, la frontière de décision par défaut, ainsi que les résultats obtenus sur AggreFact et RAGTruth. Considérez ces éléments comme des preuves fournies par la fiche de modèle, et non comme une garantie valable pour votre corpus : calibrez le seuil en utilisant des étiquettes locales et comparez-le avec le critère de jugement que vous avez retenu avant déploiement.
Évaluation des faits atomiques
FActScore (Min et al., EMNLP 2023) décompose les générations de longue forme en faits atomiques, récupère les preuves correspondant à chaque fait, et assigne une étiquette à chacun d’eux supported / not-supported, et indique la fraction prise en charge :
Implémentation de référence : shmsw25/FActScore. Il fonctionne très bien pour les biographies, les résumés ainsi que d’autres types de contenus de long format. Attention cependant : des faits triviaux répétitifs peuvent faire augmenter le score, et les attaques de type « MontageLie » (faits véridiques présentés dans un ordre trompeur) peuvent le rendre inefficace. VeriScore traite les revendications en y appliquant les modificateurs nécessaires ; le Cœur Le filtre contribue à empêcher le remplissage artificiel des données factuelles.
Précision de la citation
Suivez la précision des citations (les segments cités prennent réellement en charge l’affirmation) ainsi que le rappel des citations (les affirmations qui devraient être citées le sont bien) :
La piste TREC 2024 RAG définit un protocole d’évaluation sur support reproductible. Upadhyay et al. (SIGIR 2025) Le rapport indique que GPT-4o est en accord avec les juges humains dans 56 % des cas lors d’une évaluation manuelle effectuée à partir de zéro, ce taux montant à 72 % après édition des prédictions générées par LLM. Cela constitue un atout pour amplifier la productivité dans leurs conditions spécifiques, mais ne remplace pas l’évaluation humaine dans des contextes à haut risque. Il s’agit donc d’une approximation automatisée. ALCE (Gao et al., EMNLP 2023) met en œuvre une précision/recall de citation grâce à une vérification basée sur la NLI.
Exactitude de la réponse, exhaustivité, refus
- Exactitude de la réponse vs. vérité de référence : lorsqu’elle est disponible, correspondance exacte ou F1 par token pour les tâches à réponse courte (
evaluate.load("squad")), similarité sémantique pour les questions ouvertesbert-score, embedding cosinus parsentence-transformers, ou RAGASAnswerCorrectness). - Complétude par les fragments d’information : un « fragment » désigne une unité d’information atomique que toute réponse correcte doit obligatoirement contenir (par exemple, pour la question « Quand la société a-t-elle été fondée ? », les fragments pourraient être
{year: 1994, founder: Jane Doe}). Les critères de TREC AutoNuggetizer Il extrait les éléments essentiels d’une réponse correcte à partir d’une référence, puis évalue la proportion du contenu couvert par le système — une forte corrélation avec l’évaluation manuelle observée sur 21 sujets et 45 exécutions lors de TREC 2024. - Comportement de refus : les requêtes pour lesquelles il n’existe pas de réponse dans le corpus doivent entraîner une abstention, et non une hallucination. Il convient de suivre la précision de l’abstention (les refus qui étaient justifiés) ainsi que le rappel de l’abstention (les requêtes hors champ qui ont déclenché un refus). NoMIRACL S’agit-il du benchmark public dans votre propre domaine ? Marquez alors une partie des requêtes hors champ d’application et suivez la précision de l’abstention.
Vérification post-génération
Les gains de fiabilité les plus économiques proviennent le plus souvent de vérifications déterministes post-validation, et non de modèles plus volumineux.
- Vérification du ancrage des entités : chaque entité nommée présente dans la réponse doit apparaître dans le contexte récupéré (ou en être dérivable). Une vérification par expression régulière simple associée à une recherche de correspondance exacte (ou
spaCy’sentscontre une chaîne de contexte normalisée), cela permet de détecter une proportion surprenante d’hallucinations. - Vérification des affirmations : extraire les affirmations, effectuer une NLI par rapport au contexte, et échouer ou signaler tout élément inférieur au seuil. Modèles NLI en tant que mécanisme de fiabilité :
cross-encoder/nli-deberta-v3-large,MoritzLaurer/DeBERTa-v3-large-mnli-fever-anli-ling-wanli. Ajoute une latence. Cela en vaut la peine dans les domaines à haut risque. - Auto-cohérence (Wang et al., ICLR 2023): Échantillonnez plusieurs générations à une température supérieure à 0 ; indiquez le taux d’accord (par exemple, la proportion de générations correspondant à la réponse modale, ou un score BERTScore par paire) ; sélectionnez le nombre d’échantillons en fonction de la courbe stabilité–coût, et marquez les réponses présentant un faible niveau d’accord afin qu’elles soient examinées manuellement.
- Calibration de la confiance : recueillir l’évaluation verbale de la confiance (“À quel point êtes-vous confiant, de 0 à 1 ?”) et la comparer à la justesse réelle sur l’ensemble d’évaluation. Tracer une courbe de calibration et indiquer l’Erreur de calibration attendue : \text{ECE} = \sum_{m=1}^{M} \frac{|B_m|{n} |\text{acc}(B_m) - \text{conf}(B_m)|, où représentent les binaires de confiance. Implémentations :
netcal,torchmetrics.CalibrationError. Un modèle affichant une confiance de 0,9 devrait être correct dans environ 90 % des cas comparables ; il convient donc d’évaluer l’écart réel plutôt que de supposer une calibration parfaite.
Partie 7 : Évaluation basée sur une ontologie RAG
Les métriques standards mentionnées ci-dessus couvrent les corpus ouverts RAG. Les systèmes basés sur des ontologies nécessitent davantage d’indicateurs. Lorsque votre RAG effectue des recherches à partir d’une ontologie structurée, d’une taxonomie ou d’un graphe de connaissances (produits dans un catalogue, conditions dans SNOMED, composants dans une liste de matériel, techniques de sécurité dans MITRE ATT&CK), les métriques standards RAG sont indispensables, mais insuffisantes. Il est également nécessaire d’évaluer la couche ontologique elle-même.
Précision du lien d’entité
La première tâche consiste à associer une mention issue d’une requête à une entité de l’ontologie (« Aspirin » → wikidata:Q18216, « le 737 » → aircraft:Boeing_737).
- Précision/rappel/F1 au niveau des mentions : standard, par rapport aux intervalles de mentions d’or (calculer avec
seqevalou un comparateur de jeu d’intervalles). - Précision de dissémination : parmi les mentions correctement détectées, quelle fraction correspond à l’ID d’entité approprié ? Les références publiques incluent ReFinED, REL, et GENRE; comme benchmarks AIDA-CoNLL et BELB Montrer que les résultats varient en fonction du système et du domaine.
- Gestion de NIL : précision/représentativité pour le cas d’« entité absente de l’ontologie ». Mesurer les liens superflus vers des entités proches mais incorrectes séparément de l’abstention correcte.
Évaluation tenant compte de la hiérarchie
L’exactitude brute traite le cas où la prédiction est « Sedan » alors que la réalité est « Hatchback » de la même manière que le cas où la prédiction est « Sedan » alors que la réalité est « Submarine ». Or, ces erreurs ne sont pas équivalentes.
-
Précision/rappel/F1 hiérarchique (Kosmopoulos et al., 2015): Attribuer des crédits aux ancêtres et aux descendants dans le DAG de l’ontologie. Avec désignant le nœud prédit ainsi que tous ses ancêtres, et le nœud réel ainsi que tous ses ancêtres :
Mettre en œuvre avec
networkxdans le graphe ontologique ; voirhierarchical-classifier-metricsà titre de référence. -
Similitude Wu-Palmer entre l’entité prédite et l’entité de référence dans la taxonomie (Wu et Palmer, 1994):
où LCA désigne l’ancêtre commun le plus bas dans la taxonomie. Disponible directement dans NLTK pour WordNet.
from nltk.corpus import wordnet as wn; wn.synset("car.n.01").wup_similarity(wn.synset("truck.n.01"))); pour des taxonomies personnalisées, calculer le LCA avecnetworkx. -
Taux de confusion frère/sœur/père/mère : suivre séparément les confusions avec les frères et sœurs, les parents et les enfants —
count_sibling / total_errors,count_parent / total_errors,count_descendant / total_errors. Utilisez des exemples éprouvés pour vérifier si les erreurs entre frères et sœurs proviennent de mentions ambiguës, ou bien si les erreurs parentales résultent d’une sur-généralisation.
Taux de faux rejet des filtres (reprise, désormais critique)
Dans les systèmes fondés sur des ontologies, les filtres rigides proviennent souvent de l’ontologie elle-même (« ne récupérer que les documents étiquetés avec la catégorie X »). La métrique du taux d’exclusion (définie dans Partie 5) Il devient ainsi un signal principal de correction. Une prédiction de catégorie erronée peut annuler complètement le taux de rappel ; le taux d’exclusion attribue cette perte au filtre.
Conformité de génération contrainte
Lorsque votre sortie doit respecter une ontologie (chaque nom d’entité dans la réponse doit être un membre valide de l’ontologie ; chaque prédicat doit provenir d’un vocabulaire fermé), mesurez :
- Taux de validité du schéma : pourcentage des sorties qui se prêtent à une analyse et à une validation par rapport au schéma de l’ontologie. Valider avec
jsonschemaoupydantic. JSONSchemaBench Le benchmark public s’applique aux structured output généraux ; pour les schémas spécifiques à une ontologie, il est nécessaire de développer son propre validateur. - Conformité du vocabulaire : pourcentage d’entités nommées dans la sortie qui correspondent à des identifiants d’ontologie valables — une vérification en une seule ligne de appartenance au vocabulaire fermé.
- Conformité sémantique : la validité syntaxique est indispensable mais insuffisante. Une sortie syntaxiquement valide peut sélectionner une entité valide mais incorrecte. Il convient donc d’associer cette conformité à la justesse des réponses produites.
Constrained decoding frameworks (Aperçus, XGrammar, Directives, OpenAI Structured Outputs) Ils sont conçus pour garantir la validité du schéma. JSONSchemaBench Il compare l’efficacité, le taux de couverture et la qualité entre les différentes implémentations. Il réexécute ses cas correspondant à vos schémas ainsi qu’à serving backend, car le taux de couverture et la latence dépendent des deux facteurs.
Auditabilité
Pour les systèmes fondés sur des ontologies où les réponses font l’objet d’une revue :
- Complétude des citations : pourcentage d’affirmations factuelles disposant d’au moins une citation vérifiable.
- Profondeur de la provenance : pourcentage de citations qui remontent jusqu’à un document source doté d’une identifiant stable, et non simplement à un hash de segment.
- Taux de reproductibilité : exécution répétée de la même requête à partir d’un instantané fixe doit produire le même résultat. Il convient de fixer la version du modèle, runtime, la configuration de décodage ainsi que la valeur de seed, puis de définir le taux de répétition requis en fonction des exigences en matière d’auditableté du flux de travail. Une température égale à zéro ne garantit pas à elle seule le déterminisme. Une erreur peut survenir lors de la génération, du serving runtime, ou à n’importe quelle étape antérieure.
Partie 8 : Évaluation au niveau du système
Qualité globale de la réponse
- LLM-en tant que juge (Zheng et al., NeurIPS 2023): Une approche d’évaluation basée sur des modèles et capable de s’adapter à différentes échelles. G-Eval (un protocole de jugement LLM qui oblige le modèle à générer sa propre grille d’évaluation chain-of-thought avant de procéder à la notation) déduit cette grille à partir d’un critère exprimé en langage naturel, puis effectue la notation en utilisant des valeurs de sortie pondérées par des probabilités logarithmiques. La concordance entre les jugements dépend du juge, de la tâche, de prompt ainsi que du jeu de calibration.
- Préférence par paires : présenter au juge la réponse A par rapport à la réponse B ; enregistrer ensuite la préférence choisie. Cette méthode permet d’éviter les problèmes liés à la calibration basée sur des scores absolus. MT-Bench Il a été constaté que l’accord du juge GPT-4 atteint plus de 80 % tant par rapport aux préférences humaines qu’aux accords entre humains, dans le cadre de ses conditions benchmark ; il ne faut pas transférer ce taux vers un autre domaine sans procéder à une calibration préalable.
LLM-en tant que juge présente de réels biais :
- Biais de position : les juges privilégient la première ou la deuxième réponse, indépendamment de sa qualité. Mesures d’atténuation : aléatoriser l’ordre des présentations, ou exécuter les deux ordres et calculer une moyenne.
- Biais de verbosité : les juges peuvent confondre la longueur avec la qualité. Une Étude contrôlée 2026 Un comportement hétérogène des paires d’expansion a été observé : trois juges ont préféré des réponses plus longues, Claude a privilégié des réponses concises, tandis que GPT-4o est resté globalement neutre. Les cinq modèles se sont tous bien comportés lors des tests de contrôle de troncature. Le résultat reste soumis aux contraintes de benchmark, il convient donc d’indiquer à votre juge comment gérer la complétude du texte et les éléments superflus, avant de rapporter les performances en fonction de la longueur selon votre propre grille d’évaluation.
- Biais de préférence pour soi-même : GPT-4 préfère ses propres sorties ; ce biais est corrélé avec la perplexité du texte généré (les juges privilégient les textes qui leur sont familiers). Mesure corrective : utiliser une famille de juges différente de celle du système évalué. Il ne faut pas employer un modèle pour le juger lui-même.
Recette pratique : sélectionnez un juge à partir de données de calibration étiquetées par des humains, aléatoisez l’ordre des réponses, masquez les identités des modèles, et précisez la politique de longueur dans le rubriche. Ne reproduisez les cas que lorsque les échantillons ajoutés réduisent de manière significative l’incertitude. Pour les évaluations à haut enjeu, comparez les juges issus de familles de modèles différentes et analysez les désaccords par rapport aux étiquettes humaines.
Raisonnement guidé par des schémas pour les juges
La sortie de forme libre constitue l’une des sources de variation entre les exécutions effectuées par les juges. Deux exécutions portant sur la même réponse peuvent organiser le critère d’évaluation différemment, ce qui entraîne des notes distinctes. Raisonnement guidé par schéma (SGR) Il convient de formaliser cette grille d’évaluation en définissant les étapes de notation au sein d’un schéma Pydantic, puis d’obtenir une sortie contrainte à l’aide d’Outlines, XGrammar, vLLM structured outputs, ou OpenAI. response_format Ainsi, chaque exécution renvoie les mêmes champs dans le même ordre.
Pour RAG eval, le schéma décompose l’évaluation en champs explicites et auditable, plutôt que de permettre au modèle d’obtenir directement une valeur numérique :
from pydantic import BaseModel, Field
from typing import Literal
class FaithfulnessJudgment(BaseModel):
extracted_claims: list[str] = Field(
description="Atomic factual claims in the answer, one per item."
)
supported_claims: list[str] = Field(
description="Subset of extracted_claims that are entailed by the context."
)
unsupported_claims: list[str] = Field(
description="Subset that is NOT entailed by the context."
)
failure_mode: Literal[
"none", "fabrication", "overgeneralization", "wrong_entity", "stale_fact"
]
score: float = Field(ge=0.0, le=1.0)
rationale: str
Les champs structurés permettent de récupérer la note. len(supported) / len(extracted) Et indiquer précisément quels arguments ont fait l’objet de désaccord entre les deux juges. Le modèle Pydantic permet également de rendre visibles les modifications apportées à la grille d’évaluation sous forme de diff de code. Comme la sortie est contrainte, seules la structure des résultats est garantie et non un jugement objectif ; par conséquent, la randomisation des positions, l’utilisation de juges issus de familles différentes et la calibration humaine restent nécessaires.
Cette approche fonctionne pour tout juge basé sur des critères prédéfinis, et pas uniquement en ce qui concerne la fidélité. Les préférences par paires, le suivi des citations ainsi que la correction des refus bénéficient tous du même traitement.
Un juge G-Eval / par paires / à biais de position / inter-familial harness est implémenté dans Notebook 07; module : evaluation/llm_judge.py. La balayée benchmarkmake benchmark Dans le dépôt), ces composants interconnectent trois modèles de premier plan — gpt-5-mini, claude-haiku-4-5, gemini-2.5-flash — en un test A/B à juges rotatifs par paires, de sorte que chaque modèle évalue les deux autres, ce qui permet d’obtenir une mesure numérique des préférences intrinsèques.
Latence et coût
- p50, p95, p99 à chaque étape pipeline. Il convient de sélectionner le percentile SLO ainsi que le seuil d’alerte en fonction du parcours utilisateur, du volume de trafic et du budget d’erreurs alloué.
- Time-to-first-token par rapport au temps total de génération. Pour une expérience utilisateur en flux continu, les utilisateurs se soucient principalement du TTFT.
- Découpage des étapes : récupération, reranking, génération, post-traitement. Il faut utiliser les traces pour identifier l’étape responsable du problème plutôt que de supposer arbitrairement laquelle ; il est également nécessaire d’enregistrer le type de dispositif reranker ainsi que la taille du lot lors de la comparaison entre différentes exécutions.
- Coût total par requête = embedding + coût de récupération + coût de rérankement + coût de génération + coût d’amortissement du stockage. Il est essentiel de suivre les valeurs p50 et p99, car c’est sur la « longue queue » que se consomme principalement le budget alloué.
- Taux de hits dans le cache au niveau du cache embedding, du cache de récupération et du cache KV. Il convient d’établir des objectifs distincts en fonction du taux de répétition observé, de la politique d’invalidation en vigueur, ainsi que des économies réalisées à chaque niveau.
La valeur p50/p95/p99 par étape, accompagnée d’un détail par étape, est intégrée directement Notebook 08 et le thread d’exécution à evaluation/latency.py; Le rapport benchmark combine la latence et la fidélité au sein d’une même matrice que vous pouvez relancer à volonté make benchmark.
Test A/B
- Unité de randomisation : sélectionnez l’unité parmi l’estimand, le carryover et l’interférence. Préférez une allocation par utilisateur ou par session lorsque des expositions répétées peuvent modifier le comportement ou générer une expérience utilisateur incohérente. L’allocation par requête n’est justifiable que lorsque ces effets sont négligeables et que les modèles d’analyse tiennent compte des observations répétées.
- Métriques principales, de contrôle et exploratoires : enregistrez-les à l’avance. Choisissez la mesure principale en fonction du résultat produit ; les substituts de satisfaction incluent les évaluations positives, les réutilisations et le temps passé sur l’interface. Considérez la latence et le coût comme des métriques de contrôle dès lors qu’elles restreignent l’expérience utilisateur.
- Taille de l’échantillon : effectuez une analyse de puissance avant de lancer le projet, en fonction de l’effet minimal détectable, de la variance de base, de l’unité d’allocation et des règles d’arrêt.
Partie 9 : Construction de l’ensemble de test
Une métrique n’a de valeur que selon l’ensemble de test sur lequel elle est évaluée. Si votre ensemble d’entraînement couvre trois intentions tandis que le trafic en production en comprend douze, la mesure Recall@10 ne prendra en compte que ces trois intentions. Pire encore, un ensemble de test qui surajuste aux questions faciles (« Quelle est la politique de remboursement de l’entreprise ? ») peut valider un système qui échoue face aux cas complexes (« Quelles sont les conditions de remboursement pour une annulation partielle conformément à la loi européenne sur les services numériques de 2023, facturée en EUR et émise depuis l’Irlande ? »). Le score global augmente alors que le système continue de ne pas gérer une partie importante du trafic réel.
Le même problème se pose pour les données de vérité absolue. Si les experts métier annotent les documents évidents mais manquent ceux de la « longue queue » pertinents, le taux de rappel @k sous-estimera la performance d’un système de récupération qui les a réellement trouvés. On optimise donc en fonction des étiquettes, et non en fonction de la vérité elle-même.
Construisez d’abord l’ensemble de test en fonction de la distribution réelle des requêtes ainsi que de leur niveau de difficulté. Ensuite, sélectionnez des métriques adaptées aux modes d’échec ciblés et ajustez le système en conséquence.
Génération de requêtes synthétiques
Utilisez un LLM pour générer des questions à partir de votre corpus :
- Par morceau : « Générer 3 questions que l’utilisateur pourrait poser et auxquelles ce morceau répond. »
- Multi-hop : sélectionner deux morceaux, générer une question nécessitant les informations des deux.
- Adversarial : générer des questions contenant des entités trompeuses, des formulations presque identiques ou des références ambiguës.
RAGAS Il dispose d’une répartition intégrée des types de questions (raisonnement, conditionnel, multi-contexte). DataMorgana Il génère des données synthétiques benchmarks configurables, adaptées aux différentes catégories d’utilisateurs et de questions. Les données synthétiques sont utiles pour les démarrages en l’absence de données historiques ainsi que pour les tests de couverture. Elles ne peuvent pas remplacer les requêtes réelles des utilisateurs.
Construction « Golden dataset »
Les données curées par des humains servent de référence pour l’ensemble d’entraînement de référence.
- Extraire des requêtes réelles d’utilisateurs (ou simulées en phase pré-lancement) stratifiées selon l’intention.
- Faire répondre à chaque question des experts métier afin d’identifier le ou les documents contenant la réponse.
- Déterminer la taille de l’ensemble à partir de la matrice de couverture et de l’intervalle de confiance requis pour prendre des décisions de mise en production ; la couverture est plus importante qu’un simple nombre de requêtes collectées.
- Réaliser une nouvelle sélection lorsque le rythme de mise en production, les signaux de dérive, les risques liés au domaine et la capacité d’annotation l’exigent.
Ensembles de test adversariaux
- Contre-factuels : substitution des entités clés de la requête. Le système récupère-t-il les bons fragments pour la requête modifiée ?
- Distractions : requêtes pour lesquelles le corpus contient une réponse plausible mais erronée qui ne doit pas être obtenue. C’est ce qui RGB (Chen et al., AAAI 2024) effectuent des tests de résistance : robustesse au bruit, rejet négatif, intégration d’informations et robustesse contrefactuelle.
- Négation et quantificateurs : requêtes contenant « not », « except » et « only ». Les récupérateurs densifs ont souvent du mal à gérer ce type de requêtes.
- Hors champ : requêtes pour lesquelles il n’existe pas de réponse dans le corpus. Le système doit indiquer « Je ne sais pas », et non produire de hallucinations. NoMIRACL Il réside ici. Évaluez explicitement le phénomène d’abstention pour vos types de requêtes en production.
Couverture et évaluation continue
- Construire une matrice de couverture : intention de requête × type de document × branche d’ontologie. Il convient d’atteindre au moins 1 requête par case. Les cases vides représentent des régions non surveillées où les dérives peuvent se cacher.
- Exécuter un sous-ensemble de tests de dérive limité et rapide pour chaque PR, ainsi que l’ensemble complet selon un calendrier plus lent.
- Planifier l’évaluation complète sur l’ensemble de référence en fonction du rythme de publication et des coûts associés ; les candidats à la publication constituent une étape naturelle dans ce processus.
- Planifier l’évaluation de la dérive en fonction du volume de trafic, des changements attendus et du niveau de risque. Il faut utiliser un échantillon en production dynamique et stratifier les tests en fonction des retours utilisateurs, plutôt que de modifier silencieusement la distribution cible.
Partie 10 : Surveillance en production
L’ensemble d’évaluation que vous déployez décrit le système au moment du lancement. Le trafic en production évolue par la suite.
Retour implicite et explicite
- Le taux de clic / taux d’ouverture des sources citées (dans la mesure où votre interface les affiche).
- Le temps passé sur la réponse.
- Le taux de régénération : pourcentage de réponses que l’utilisateur demande à être reformulées ou à être refaites par le système. Considérez-le comme un indicateur de mécontentement et ajustez-le en fonction des conversations analysées.
- Les taux de copie / partage / exportation : c’est un signe très positif.
- Les patterns de suivi : des questions telles que « Êtes-vous sûr ? » ou « Mais qu’en est-il de X ? » indiquent une méfiance.
- Les votes thumbs up/down accompagnés éventuellement de catégories de raison (incorrect, incomplet, hors sujet, nocif, lent). Les modifications en ligne, lorsque votre interface les permet, constituent le signal de retour d’information le plus riche qui existe.
Détection du dérive
- Dérive des requêtes : suivre la distribution des requêtes embedding par rapport à une fenêtre de référence en utilisant la divergence de KL, le MMD ou un détecteur basé sur un modèle. Déclencher une alerte en cas de décalage, puis procéder à une segmentation et à un débogage.
- Dérive Embedding : fixer un ensemble d’échantillons de documents ; ré-embedding périodiquement ces derniers et mesurer leur cosinus par rapport au embeddings d’origine. Même une petite dérive entre les versions du modèle du fournisseur peut altérer silencieusement les résultats de recherche. Le stockage versionné de embedding (captures immuables par version) constitue la solution d’atténuation la moins coûteuse.
- Dérive des performances : suivre au fil du temps des métriques équivalentes à celles en environnement de production (taux de régénération par intention). Des sauts brusques indiquent qu’un problème est survenu ; des dérives progressives signifient que les conditions ont changé.
Évaluation en mode ombre et human-in-the-loop
Exécutez le système candidat en parallèle avec la version en production, comparez les résultats hors ligne, et n’affichez pas ces derniers aux utilisateurs. Cette approche permet de détecter d’éventuelles régressions avant le lancement. Elle entraîne des coûts supplémentaires en termes de calcul, mais n’a aucun impact sur les clients.
Pour l’examen de human-in-the-loop (HITL) :
- Insérer les résultats à faible confiance dans une file d’attente pour révision.
- Inclure un échantillon aléatoire du trafic en production pour une révision aveugle ; déterminer son taux en fonction du volume de trafic, du niveau de risque et de la capacité des réviseurs.
- Accorder un poids élevé aux résultats marqués négativement.
- Utiliser les résultats révisés afin d’élargir l’ensemble « golden ».
L’ensemble minimal de garde-fous
Alerte pour ceux-ci, par ordre de priorité :
- Le score de fidélité/HHEM est en deçà du seuil sur un échantillon de production itératif.
- La latence p95 dépasse les objectifs de service définis (SLO).
- Le taux de faux exclusions filtrées est supérieur au seuil (basé sur des échantillons).
- Le taux de régénération se situe en dehors d’une bande de contrôle calibrée localement, tenant compte de la taille de la fenêtre, du trafic, de la saisonnalité et du budget alloué aux alertes fausses.
- Le coût par requête dépasse le budget prévu.
Lorsqu’un alerte est déclenchée sans modification correspondante du code ou du modèle, il s’agit très probablement d’une dérive. Si elle est déclenchée après une modification, c’est généralement le signe d’une régression. Dans les deux cas, vous recevez un signal avant même l’arrivée des demandes de support.
Précautions à prendre
- Les cibles sont locales, et non universelles. Tout chiffre indiqué à titre indicatif dans ce guide correspond à une configuration d’exemple ou à un résultat obtenu, et non à une valeur seuil officielle. Ajustez les seuils en fonction de votre domaine d’application, des enjeux associés, de l’incertitude liée à l’ensemble d’évaluation et des attentes des utilisateurs.
- L’écosystème framework évolue très rapidement. Les versions de HHEM, les noms de métriques RAGAS, les fiches de description des modèles ainsi que l’ordre du classement peuvent changer après publication. Vérifiez à nouveau la source liée et effectuez une nouvelle benchmark avant de valider quoi que ce soit.
- Les scores d’accord LLM obtenus grâce aux juges sont affichés avec des astérisques. La valeur de 80 % pour GPT-4 contre l’être humain provient des conditions de MT-Bench / Chatbot Arena. Sur des domaines spécialisés ou dans des cas adversariaux, cet taux d’accord diminue fortement. Utilisez les juges comme un outil complémentaire, et non comme un substitut aux vérifications manuelles.
- Les améliorations promises par les fournisseurs benchmark ne sont souvent pas reproductibles de manière indépendante. Reproduisez les résultats sur vos propres données avant de croire en un chiffre donné, en particulier pour les systèmes plus récents tels que rerankers et OCR.
- Aucune métrique ne peut remplacer l’examen direct des sorties générées. Planifiez des revues aveugles sur des échantillons aléatoires produits en condition réelle, en fonction du volume de trafic, du niveau de risque et des capacités des reviewers. Les métriques servent à quantifier cette pratique, mais elles ne la remplacent pas.
Ce qui va suivre dans cette série
Voici l’index. Les actions complémentaires que je prévois :
- Soft Boosts vs. Hard Filters : une analyse approfondie du taux d’exclusion fausse due aux filtres, accompagnée de code, d’exemples tirés de la production réelle et d’une décision framework.
- Chunking est la variable cachée : une expérience contrôlée portant sur le chunking récursif, sémantique, tardif et structurel sur trois corpus.
- La sélection de Reranker en 2026 : comparaison entre BGE, Cohere, ZeRank et les modèles de cross-encoder actuels, en fonction des coûts, de la latence et de l’amélioration des performances.
- RAG ancré dans une ontologie : une démarche du début à la fin : construction de l’ensemble des métriques d’évaluation harness pour un système de recherche ancré dans des entités.
- Évaluation LLM en tant que juge, sans tomber dans le piège de la préférence subjective : méthodes pratiques pour une évaluation automatisée impartiale.
- Évaluation en ligne en environnement de production : schémas d’instrumentation, politiques de notification et tableaux de bord permettant de détecter rapidement les dégradations réelles.
Références
Frameworks et benchmarks
- Es et al., Ragas : Évaluation automatisée de la génération augmentée par la récupération d’informations, 2023.
- Documentation RAGAS et GitHub.
- Saad-Falcon et al., ARES : Une évaluation automatisée Framework des systèmes de génération augmentée par la récupération d’informations, NAACL 2024.
- TruLens, DeepEval, Arize Phoenix.
- Thakur et al., BEIR : Un Benchmark hétérogène pour l’évaluation sans entraînement des modèles de recherche d’informations, NeurIPS 2021.
- Classement MTEB.
- TREC 2024 RAG Piste.
- Pradeep et al., Résultats initiaux de l’évaluation des Nuggets pour la track TREC 2024 RAG utilisant AutoNuggetizer Framework, 2024.
Récupération et classement
- Cormack, Clarke, Buettcher, La fusion de rangs réciproques surpasse les méthodes de type Condorcet ainsi que les approches d’apprentissage des rangs individuels., SIGIR 2009.
- Gao et al., Recherche dense zéro-shot précise sans étiquettes de pertinence (HyDE), 2022.
- Jeong et al., Adaptive-RAG : Apprentissage de l’adaptation des grands modèles de langage augmentés par la récupération en fonction de la complexité des questions, NAACL 2024.
- Anthropic, Présentation de la récupération contextuelle, Septembre 2024.
- Günther et al., Chunkage tardif : segmentation contextuelle Embeddings à l’aide de modèles à long contexte Embedding, 2024.
- Sarthi et al., RAPTOR : Traitement abstrait récursif pour la récupération organisée en arbres, 2024.
Génération, fidélité, évaluateurs
- Min et al., FActScore : évaluation atomique fine de la précision factuelle dans la génération de texte de long format, EMNLP 2023.
- Liu et al., Perdu au milieu : comment les modèles de langage exploitent des contextes longs, TACL 2023.
- Chen et al., Évaluation des modèles de langage de grande taille dans la génération augmentée par la recherche (RGB), AAAI 2024.
- Vectara, HHEM-2.1 – Modèle d’évaluation des hallucinations ouvertes.
- Zheng et al., Évaluer LLM en tant que juge à l’aide de MT-Bench et Chatbot Arena, NeurIPS 2023.
- Upadhyay et al., Évaluation du support pour la piste TREC 2024 RAG : comparaison entre les juges humains et les LLM, SIGIR 2025.
- Thakur et al., NoMIRACL : Savoir quand on ne sait pas pour une génération augmentée par la recherche multilingue fiable, 2023.
- Geng et al., JSONSchemaBench : Un outil de validation Benchmark rigoureux pour les modèles de langage Structured Outputs, 2025.
- Kosmopoulos et al., Mesures d’évaluation pour la classification hiérarchique : une vision unifiée et des approches innovantes, 2015.
Dérive et environnement de production
- Il est évident que, Les méthodes de détection du drift comparées Embedding.
Code d’accompagnement
slavadubrov/rag-evals-demo— un exécutable harness pour chaque métrique présentée dans cet article sur le corpus SciFact, ainsi qu’un processus de segmentation × embedding × LLM benchmark. Les notebooks 00 à 09, les tests unitaires associés aux exemples démontrés ci-dessus, et un index Qdrant intégré permettant son fonctionnement sans Docker.