Un agent qui « a l’air de marcher » sur trois questions ne prouve rien. Changez un mot du prompt, passez à un modèle moins cher, ajoutez un outil : comment savez-vous que vous améliorez plutôt que vous ne cassez ? Sans mesure systématique, chaque modification est un pari. L’évaluation transforme ce pari en décision étayée.
LangFuse, qu’on a branché en initiation pour tracer, fournit aussi tout l’outillage d’évaluation : scores, datasets de référence, LLM-as-a-judge et expériences comparatives.
Trois sources de score
Un score est une mesure attachée à une trace (ou à une observation). Trois façons complémentaires de le produire :
- Humain — un expert annote (pouce haut/bas, note). La référence de qualité, mais lente et coûteuse. Idéale pour constituer un jeu de validation.
- Heuristique — du code vérifie une propriété objective : format JSON valide, présence d’une citation, longueur, latence, coût. Gratuit et déterministe.
- LLM-as-a-judge — un modèle évalue une propriété subjective (fidélité, pertinence, ton). Le seul moyen de mesurer la qualité à l’échelle.
Attacher un score à une trace
L’API de base est create_score. On y rattache un score par l’id de la trace
(qu’on récupère lors de l’exécution instrumentée).
from langfuse import Langfuse
langfuse = Langfuse()
# heuristique : la réponse cite-t-elle au moins une source ?
def score_citation(trace_id: str, reponse: str):
a_cite = "[source" in reponse.lower()
langfuse.create_score(
trace_id=trace_id,
name="cite_source",
value=1 if a_cite else 0,
comment="Vérifie la présence d'une citation",
)
Les scores s’agrègent ensuite dans LangFuse : évolution dans le temps, par version, par tag — c’est ce qui rend une régression visible.
Le LLM-as-a-judge en pratique
Pour le RAG, la métrique reine est la fidélité : la réponse s’appuie-t-elle vraiment sur les documents récupérés, sans inventer ? On la mesure avec un juge.
from pydantic import BaseModel, Field
class Jugement(BaseModel):
fidele: bool = Field(description="La réponse est-elle justifiée par les sources ?")
note: int = Field(description="Qualité globale de 1 à 5")
raison: str
GABARIT = """Tu es un évaluateur rigoureux. Question : {question}
Sources fournies : {sources}
Réponse de l'agent : {reponse}
La réponse est-elle fidèle aux sources et pertinente ? Sois sévère."""
def juger(trace_id, question, sources, reponse):
juge = modele_juge.with_structured_output(Jugement)
v = juge.invoke(GABARIT.format(question=question, sources=sources, reponse=reponse))
langfuse.create_score(trace_id=trace_id, name="fidelite", value=int(v.fidele))
langfuse.create_score(trace_id=trace_id, name="qualite", value=v.note, comment=v.raison)
return v
En ligne vs hors ligne
- Hors ligne — on évalue sur un dataset figé, à chaque changement (avant de déployer). C’est le test de non-régression.
- En ligne — un juge tourne sur un échantillon du trafic de production, en continu, pour détecter les dérives réelles. LangFuse permet de configurer ces évaluations managées directement sur les traces entrantes.
Les datasets : une vérité terrain versionnée
Un dataset est une collection de cas (entrée, sortie attendue). C’est votre
banc d’essai : on y rejoue l’agent à chaque modification.
# créer le dataset une fois
langfuse.create_dataset(name="support-qa")
langfuse.create_dataset_item(
dataset_name="support-qa",
input={"question": "Comment réinitialiser mon mot de passe ?"},
expected_output="Indiquer la page Paramètres > Sécurité > Réinitialiser.",
)
# … des dizaines de cas réels, idéalement issus de vrais tickets …
Une expérience : rejouer l’agent sur le dataset
Une expérience exécute l’agent sur chaque item du dataset, relie chaque trace à son item, et applique les évaluateurs. On compare ensuite les versions.
dataset = langfuse.get_dataset("support-qa")
for item in dataset.items:
# item.run() relie automatiquement la trace au dataset et à cette exécution
with item.run(run_name="prompt-v3") as root_span:
reponse = agent.invoke({"messages": [{"role": "user", "content": item.input["question"]}]})
sortie = reponse["messages"][-1].content
# scores : heuristique + LLM-juge
root_span.score_trace(name="longueur_ok", value=int(len(sortie) < 1200))
juger(root_span.trace_id, item.input["question"], item.metadata, sortie)
Dans LangFuse, les exécutions prompt-v2 et prompt-v3 s’affichent côte à
côte : score de fidélité moyen, coût, latence. La décision « déployer ou non »
devient lecture de tableau, pas intuition.
Non-régression en intégration continue
L’aboutissement : brancher l’expérience dans la CI. À chaque pull request, on rejoue le dataset et on bloque si un score moyen chute sous un seuil.
# eval_ci.py — exécuté par la CI
resultats = lancer_experience("support-qa", run_name=os.environ["GIT_SHA"])
fidelite = moyenne(r.scores["fidelite"] for r in resultats)
assert fidelite >= 0.9, f"Régression de fidélité : {fidelite:.2f} < 0.90"
print(f"✓ Fidélité {fidelite:.2f} — OK pour merge")
Bonnes pratiques & pièges
- Combinez les trois sources : heuristiques (gratuites), LLM-juge (échelle), humain (vérité).
- Constituez le dataset depuis la production, surtout les cas d'échec ; faites-le vivre.
- Calibrez le LLM-juge contre des annotations humaines sur un échantillon.
- Préférez des verdicts binaires et une grille stricte aux notes floues sur 10.
- Branchez la non-régression en CI : bloquez le merge si un score moyen chute.
- Évaluez en ligne sur un échantillon de prod pour capter les dérives réelles.
- Confondre tracer et évaluer : la trace ne dit pas si la réponse était bonne.
- Juger sur 3 exemples à la main : aucune garantie statistique, aucune non-régression.
- Faire confiance au LLM-juge sans calibration : il favorise le verbeux et se laisse flatter.
- Un dataset figé jamais mis à jour : il finit déconnecté du trafic réel.
- Optimiser un seul score (fidélité) en cassant les autres (latence, coût) sans les suivre.
Ce qu’il faut retenir
- Un score attaché aux traces rend la qualité mesurable et son évolution visible.
- Trois sources : humaine (vérité), heuristique (déterministe), LLM-juge (subjectif à l’échelle) — à combiner et à calibrer.
- Un dataset est votre vérité terrain versionnée ; nourrissez-le depuis la production.
- Une expérience rejoue l’agent sur le dataset et compare les versions côte à côte.
- La non-régression en CI est le filet qui autorise à itérer vite et sans peur.
Mesurer la qualité ferme la boucle de l’ingénierie d’agents. Reste un dernier maillon, côté expérience utilisateur : restituer le travail de l’agent en temps réel. C’est l’objet du deep-dive sur le streaming.