Aller au contenu principal

Module 8 — Évaluation : fidélité, pertinence, couverture

Un système RAG mesuré à l'œil s'améliore à l'œil : à chaque changement, on se persuade que « ça semble mieux », alors qu'on a peut-être régressé. Ce module donne l'infrastructure d'évaluation qui permet de dire, chiffre à l'appui, si une modification aide ou nuit. Sans elle, tout le reste du cours est du bricolage — c'est le module qui transforme l'assistant en produit.

Trois questions, trois familles de métriques

Un système RAG peut échouer à trois endroits distincts, et il est capital de les distinguer :

  1. La recherche a-t-elle trouvé le bon passage ? — métrique : rappel.
  2. La réponse s'appuie-t-elle uniquement sur les passages trouvés ? — métrique : fidélité.
  3. La réponse est-elle vraiment utile à la question posée ? — métrique : utilité perçue.

Un système peut avoir 95 % de rappel et 40 % de fidélité (le modèle ignore les passages fournis). Ou 90 % de fidélité et 30 % de rappel (le modèle est fidèle à des passages qui n'étaient pas les bons). Améliorer l'un sans regarder les autres se retourne toujours.

Construire un jeu annoté

L'infrastructure minimale : 50 à 200 questions annotées par un humain qui connaît le corpus. Pour chaque question, on enregistre :

  • La question exacte.
  • Les identifiants des passages qui contiennent effectivement la réponse (souvent 1 à 3, parfois zéro).
  • La réponse attendue en une ou deux phrases.
  • Une catégorie : « factuelle courte », « procédurale », « sans réponse dans le corpus », « contradiction connue ».

Cent questions bien choisies valent mille questions générées au hasard : elles doivent couvrir les cas rares — acronymes, tableaux, sections récentes — qui font échouer le système. La catégorie « sans réponse » représente idéalement 20 à 30 % des questions, pour mesurer le taux d'abstention correcte.

- id: "q001"
question: "Quelle est la duree de la periode d'essai pour un cadre ?"
passages_verite: ["p_reglement_intx_042"]
reponse_attendue: "Quatre mois, renouvelable une fois par ecrit."
categorie: "factuelle_courte"

- id: "q002"
question: "Quel est le montant du telephone de fonction pour un stagiaire ?"
passages_verite: []
reponse_attendue: "abstention"
categorie: "sans_reponse"

Mesurer la recherche

Le rappel à kk est la métrique fondamentale :

Rappel@k=1QqQ1 ⁣[VqRqk]\text{Rappel@}k = \frac{1}{|Q|} \sum_{q \in Q} \mathbb{1}\!\left[V_q \cap R_q^{k} \neq \varnothing\right]

VqV_q est l'ensemble des passages vérité pour la question qq et RqkR_q^{k} les kk passages remontés.

def rappel_a_k(questions, chercher, k=5):
hits = 0
for q in questions:
if q["passages_verite"]:
resultats = {p["id"] for p in chercher(q["question"], k=k)}
if set(q["passages_verite"]) & resultats:
hits += 1
total = sum(1 for q in questions if q["passages_verite"])
return hits / total

Le rappel à 5 est celui qui compte : c'est le nombre de passages qu'on enverra au modèle. Un rappel à 5 en dessous de 0,80 signale que la couche de recherche est le goulot ; on retourne au module 3, 4 ou 5.

Mesurer la fidélité de la réponse

La fidélité — faithfulness dans la littérature — est plus subtile. Elle mesure la proportion d'affirmations de la réponse qui sont attestées par au moins un passage cité.

L'évaluation manuelle est fiable mais lente. Une évaluation par modèle juge — un modèle de langage à qui l'on présente la réponse et les passages cités, et à qui l'on demande de vérifier chaque phrase — s'accorde à 85 % avec un humain, ce qui suffit pour du suivi.

JUGE_FIDELITE = """Voici une reponse produite par un assistant, et les
extraits qu'il pretend citer. Pour chaque phrase de la reponse,
indique si elle est entierement attestee par au moins un extrait.
Reponds par un JSON : {"phrases": [{"texte": "...", "atteste": true/false}, ...]}.

Reponse :
{reponse}

Extraits :
{extraits}
"""

def fidelite(reponses, appeler_modele):
total, attestees = 0, 0
for r in reponses:
verdict = appeler_modele(JUGE_FIDELITE.format(
reponse=r["texte"], extraits=r["extraits"]))
for phrase in json.loads(verdict)["phrases"]:
total += 1
if phrase["atteste"]:
attestees += 1
return attestees / total if total else 0
Un juge modèle n'est pas neutre

Un juge issu de la même famille de modèles que le modèle évalué a tendance à sur-noter les réponses de sa famille — jusqu'à 8 points d'écart. Utiliser un modèle juge d'une autre famille (Anthropic pour évaluer un modèle Meta, ou l'inverse) réduit ce biais. Pour les décisions à fort enjeu, garder de toute façon un échantillon annoté à la main comme calibration.

L'abstention : une métrique à part

Sur les questions catégorisées « sans_reponse », on mesure séparément la précision d'abstention — la proportion de fois où le système a effectivement produit la phrase de repli.

def precision_abstention(reponses, motif="Je ne trouve pas cette information"):
sans_reponse = [r for r in reponses if r["categorie"] == "sans_reponse"]
abstenus = sum(1 for r in sans_reponse if motif in r["texte"])
return abstenus / len(sans_reponse) if sans_reponse else 1.0

Un bon système RAG en production tient au moins 0,90 sur cette métrique. Un système à 0,50 hallucine sur une question hors corpus sur deux — c'est inacceptable pour un intranet à visée réglementaire.

Ce que fait RAGAS

RAGAS est une bibliothèque qui automatise l'évaluation autour de quatre métriques :

  • context_recall : les passages retrouvés contiennent-ils la vérité ?
  • faithfulness : la réponse est-elle attestée par les passages ?
  • answer_relevance : la réponse répond-elle à la question ?
  • context_precision : parmi les passages retrouvés, lesquels sont utiles ?

Le tableau de bord obtenu ressemble à ceci sur le fil rouge après optimisation :

MétriqueCibleFil rouge à ce stade
Rappel @ 5> 0,800,91
Fidélité> 0,850,88
Précision d'abstention> 0,900,93
Utilité perçue> 4/54,2 / 5

On ne cherche pas la perfection : on cherche à détecter les régressions entre deux versions du système. Une baisse de 3 points sur la fidélité après un changement de découpage est plus informative qu'un chiffre absolu.

Rappel contre fidélité : le compromis à connaître

Améliorer le rappel — en montant kk à 20 par exemple — dilue le contexte et fait chuter la fidélité : le modèle a plus de passages à trier, dont certains hors sujet. C'est l'un des sept thèmes explicites de la banque d'examen.

La règle empirique : garder k=5k = 5 envoyé au modèle, faire monter le candidat de reclassement à 20 ou 30 pour compenser. On paie le reclassement, pas la fidélité.

Regarder les échecs, pas les moyennes

Une moyenne de 0,85 sur cent questions cache que le système échoue de façon dramatique sur les vingt questions procédurales et parfaitement sur les quatre-vingts questions factuelles. Segmenter les métriques par catégorie donne un signal cent fois plus utile que la seule moyenne, et pointe presque toujours le module à retravailler.

En résumé

  • Trois échecs, trois métriques : rappel de la recherche, fidélité de la réponse, précision d'abstention. On les mesure séparément sur un jeu annoté de 50 à 200 questions.
  • Un jeu annoté couvre les cas rares (acronymes, tableaux) et inclut 20 à 30 % de questions sans réponse dans le corpus.
  • Un modèle juge d'une autre famille suit la fidélité et l'utilité entre versions ; un échantillon humain sert d'ancre.
  • Rappel et fidélité s'échangent ; on garde k=5k = 5 à la génération et l'on paie la sécurité par un reclassement plus large.

Module suivant : mettre en cache ce qui peut l'être et maîtriser le coût par question en production.