Pourquoi “ça a l’air juste” n’est pas un test pour un prompt
Vous resserrez la formulation d’un prompt qui route des tickets, vous le testez sur les trois exemples ouverts dans votre notebook improvisé, et vous le déployez. Une semaine plus tard, les réclamations de facturation qui finissaient dans la bonne file commencent à finir dans “question”. Pas les trois cas que vous avez vérifiés : les quatre cents autres que vous n’avez pas vérifiés. Rien ne plante. Il n’y a pas de stack trace. Le prompt, lu à voix haute, sonne toujours bien.
La revue de code et quelques vérifications manuelles passent à côté de ça parce que le même prompt sur le même input ne renvoie pas le même texte deux fois. Vous ne pouvez pas comparer deux exécutions octet par octet et qualifier de bug la moindre différence, comme vous le feriez avec une fonction pure. Ce que vous pouvez vérifier, c’est si chaque sortie a les propriétés qui vous intéressent : la bonne catégorie, un résumé sous la limite de longueur, aucun champ que la source n’a jamais mentionné. Un eval exécute ces vérifications sur un ensemble d’inputs réels à chaque changement du prompt ou du modèle, si bien que la régression apparaît dans votre terminal plutôt que dans le ticket d’un client.
Quelles sont les parties d’un LLM eval
Chaque eval, quel que soit l’outil qui l’exécute, a trois parties : un dataset d’inputs représentatifs, une assertion qui vérifie chaque sortie par rapport à ce que cet input devrait produire, et un taux de réussite que vous suivez d’une exécution à l’autre.
Pensez-y comme à la suite de tests d’une fonction qui renvoie une phrase légèrement différente chaque fois. L’assertion ne peut pas vérifier une correspondance exacte, donc elle vérifie une propriété de la phrase : nomme-t-elle la bonne catégorie, le nombre qu’elle contient se parse-t-il correctement. Une fois que vous acceptez que le test vérifie “est-ce que ceci tient” plutôt que “est-ce identique”, en écrire un relève de l’ingénierie ordinaire.
Comment construire un dataset golden pour un LLM eval
Prenez un prompt qui transforme un email de support brut en ticket structuré :
| |
Le dataset golden est un ensemble d’emails réels associés à la réponse que vous savez déjà correcte. Réels, pas inventés pour faire bien paraître le prompt :
| |
Cinq à dix cas suffisent pour démarrer. La taille compte moins que l’origine des cas suivants : des échecs réels en production.
Comment écrire des assertions déterministes pour la sortie d’un LLM
La plupart de ce qui cloche dans ce prompt n’a rien à voir avec le goût. Le modèle renvoie du JSON malformé, ou invente une quatrième catégorie, ou écrit quarante mots quand vous en demandiez douze. Du code ordinaire intercepte tout cela, sans second modèle :
| |
Exécutez-le sur tout le dataset et vous obtenez un taux de réussite, pas un simple oui ou non :
| |
Ces vérifications ne coûtent rien et ne flanchent jamais au hasard : la même mauvaise sortie échoue de la même façon chaque fois. Écrivez-les en premier. Elles interceptent la plupart des régressions réelles, comme un changement de schéma ou un résumé qui a doublé de longueur sans que personne ne le remarque.
Quand utiliser le LLM-as-judge et comment le calibrer
Catégorie et urgence sont objectives, et le dataset contient déjà la bonne réponse. La qualité du résumé, non. “Remboursement nécessaire, double prélèvement” et “Le client a une question de facturation” sont tous les deux du JSON valide, tous les deux sous les 12 mots, et un seul des deux décrit vraiment l’email. Aucune regex n’attrape ça, donc un second modèle l’évalue selon une grille :
| |
Avant de faire confiance à ce verdict sur tout le dataset, exécutez le judge sur 15 à 20 cas déjà évalués à la main par une personne et comptez à quelle fréquence ils sont d’accord. Un judge qui est en désaccord avec les humains une fois sur trois est une assertion faible déguisée en autorité de modèle.
Deux biais méritent d’être surveillés. Les judges ont tendance à mieux noter les réponses plus longues et élaborées, même quand une réponse plus courte est tout aussi correcte. Et un modèle qui évalue ses propres sorties les note plus favorablement qu’un modèle indépendant ne le ferait. Utilisez un modèle différent comme judge quand c’est possible, et gardez le prompt étroit : une seule question oui/non par appel, pas cinq scores dans une même requête.
| Assertion déterministe | LLM-as-judge | |
|---|---|---|
| Coût par cas | Gratuit | Un appel modèle supplémentaire |
| Ce qu’elle attrape | Schéma, format, faits déjà présents dans le dataset | Qualité subjective, pertinence, ton |
| Répétabilité | Même sortie, même verdict | Une certaine dérive même à température 0 |
| Coût de mise en place | Écrire le contrôle, et c’est fait | Nécessite une calibration préalable sur des exemples jugés par des humains |
En termes de suite de tests, les contrôles déterministes sont vos tests unitaires : rapides et exacts, vous les exécutez à chaque changement. Le judge se rapproche d’un contrôle d’acceptation, celui qu’une personne ferait sinon en lisant chaque sortie. Les compromis entre ces niveaux sont les mêmes que ceux traités dans tests unitaires vs tests d’intégration vs tests end-to-end.
Comment exécuter des LLM evals en CI pour attraper les régressions
Un eval qui ne s’exécute que quand quelqu’un y pense n’attrape rien. Placez le script à côté du prompt qu’il teste (evals/triage.py avec prompts/triage.py) et exécutez-le en CI à chaque changement de l’un ou l’autre fichier, avant le merge. Suivez le taux de réussite dans le temps et faites échouer la build quand il tombe sous une base que vous avez fixée. Une chute de 95 % à 88 % est une régression qu’il vaut la peine de bloquer, même si la plupart des cas passent encore.
Gardez un ensemble de régression dans le dataset : chaque cas qui a un jour été un bug signalé, épinglé de façon permanente après correction. Pour un prompt, cela vous donne la boucle décrite dans ce qu’est vraiment le TDD. Changez le prompt, relancez la suite, voyez exactement quelles assertions ont basculé, corrigez, répétez. Alimentez le dataset avec les nouveaux échecs de production au fur et à mesure, pour ne pas tester uniquement les inputs pour lesquels le prompt a été conçu.
Comment évaluer des pipelines de tool calling et de RAG
Les mêmes trois parties s’appliquent ici aussi. Pour une boucle de tool calling, le dataset associe chaque input au bon nom d’outil et aux bons arguments, et l’assertion vérifie que le modèle a choisi cet outil avec les champs requis remplis. La mécanique de la boucle elle-même est traitée dans function calling et tool use dans les LLM.
Une pipeline RAG a besoin de deux scores séparés. Le retrieval a-t-il récupéré le chunk qui contient la réponse ? La génération a-t-elle utilisé ce chunk correctement ? Si vous les notez ensemble, impossible de savoir quelle moitié a cassé. Voyez comment fonctionne le RAG pour la pipeline que vous découpez en deux.
Erreurs courantes avec les LLM evals
Construire le dataset avec des cas qui passent déjà. Cinq exemples choisis parce que le prompt les gère bien passeront toujours et ne vous diront rien sur l’input qui cassera tout le mois prochain. Chaque échec réel qui vous arrive doit entrer dans le dataset le jour même.
Vérifier une correspondance exacte sur une sortie variable. summary == "Remboursement nécessaire pour double prélèvement" échoue dès que le modèle change un seul mot, même quand la nouvelle formulation est tout aussi correcte. Vérifiez la propriété, pas le texte : le nombre de mots, la catégorie, si un fait requis apparaît.
Sauter la calibration du judge. Un judge que vous n’avez jamais comparé, même à une douzaine d’exemples jugés par des humains, vous livre ses propres biais comme s’ils étaient la vérité, y compris celui qui favorise sa propre sortie.
Ne relancer que le cas que vous venez de corriger. C’est ainsi qu’un vieux bug revient sans que personne ne le remarque. L’ensemble de régression existe précisément pour empêcher ça.
Quand une suite de LLM eval ne vaut pas la peine d’être construite
Un prompt que vous n’exécuterez qu’une fois, ou qu’une personne relit à chaque appel, n’a pas besoin d’un harnais. Mettez le contrôle de validité JSON en ligne si c’est peu coûteux et passez à autre chose. La suite se rentabilise une fois qu’un prompt est déployé à répétition auprès d’utilisateurs ou que vous prévoyez de le modifier plus de deux fois. C’est le moment où “ça avait l’air bien sur mes trois exemples” ne suffit plus.
Pour démarrer : cinq inputs réels, une assertion déterministe par propriété qui vous importe, et un judge LLM uniquement pour la dimension que le code ne peut pas vérifier. Exécutez-le avant votre prochaine modification de prompt, pas après la réclamation.