22 août 2026 · 6 min de lecture
Tester un LLM : évaluer une sortie qui n'est jamais deux fois identique
assertEquals ne marche pas sur une sortie de LLM. Jeu de cas figé, assertions sur des propriétés, LLM-as-judge calibré, seuil de régression : la méthode concrète pour transformer « ça a l'air de marcher » en un chiffre qu'on surveille.
Dans notre article sur la mise en production d'un LLM, on identifiait les tests comme "le nœud technique du sujet, et le point où la plupart des équipes hésitent". C'était volontairement laissé en suspens là-bas pour ne pas alourdir un article déjà dense. Voici la méthode, en détail, avec ce qu'on met concrètement en place chez nos clients.
Le point de départ tient en une phrase : on ne peut pas écrire assertEquals(sortie, "réponse attendue") sur une sortie de LLM. La même entrée peut produire deux formulations différentes, également correctes. Ce n'est pas une excuse pour ne pas tester, c'est une contrainte qui change ce qu'on teste.
Le jeu de cas figé, la brique qui manque le plus souvent
Tout commence par un ensemble d'entrées réelles, entre trente et cinquante, représentatives de ce qui arrive vraiment en production, cas limites déjà rencontrés compris. Pas des exemples inventés pour l'occasion : des demandes, des documents, des tickets effectivement passés dans le système, anonymisés si nécessaire.
Trois règles font la différence entre un jeu de cas qui protège et un jeu de cas qui rassure sans le mériter :
- Il vit à côté du prompt, versionné avec lui. Un changement de prompt sans jeu de cas rejoué à côté n'est pas un déploiement maîtrisé, c'est un pari.
- Il grossit à chaque incident. Le premier réflexe après un bug en production, avant même de corriger le prompt, c'est d'ajouter le cas qui a échoué au jeu de cas. Sinon, la même régression repasse inaperçue la fois suivante.
- Il est rejoué avant chaque déploiement, jamais improvisé au moment où on en a besoin. Un jeu de cas qu'on ne consulte que quand quelque chose semble déjà cassé a raté son rôle : détecter avant que ça casse, pas confirmer après coup.
Des assertions sur des propriétés, pas sur le texte
Une fois le jeu de cas en place, la question devient : que vérifie-t-on sur chaque sortie ? Pas une égalité de chaîne de caractères, des invariants qu'une sortie correcte doit respecter quelle que soit sa formulation exacte.
Quelques exemples qui couvrent la majorité des cas qu'on rencontre :
| Ce qu'on vérifie | Exemple concret |
|---|---|
| Présence et type d'un champ | Le champ montant existe et c'est un nombre |
| Appartenance à une énumération | La catégorie renvoyée fait partie des valeurs autorisées par le schéma |
| Plage de valeurs plausible | Le montant extrait reste dans une fourchette réaliste pour ce type de document |
| Longueur bornée | Le résumé fait entre 50 et 300 mots, pas 12 et pas 2 000 |
| Motif interdit absent | La sortie ne contient pas de placeholder du type [à compléter] oublié par le modèle |
Ces assertions se codent comme des tests classiques (un jeu d'affirmations rejoué automatiquement), la seule différence est qu'elles portent sur la forme et les bornes de la sortie, pas sur son contenu exact. C'est un test qu'on peut faire tourner en quelques secondes, sans jugement humain ni appel supplémentaire à un modèle.
Le LLM-as-judge, utile seulement calibré
Pour une qualité plus subjective (la pertinence d'un résumé, le ton d'une réponse), les assertions mécaniques ne suffisent plus. On peut alors faire juger la sortie par un autre appel LLM, à qui on donne l'entrée, la sortie, et une grille d'évaluation écrite noir sur blanc plutôt qu'une consigne vague.
Le piège classique : déployer ce juge et lui faire confiance sans jamais vérifier qu'il juge bien. Le juge est lui-même non déterministe, donc son jugement doit être calibré avant d'être utilisé comme référence :
- Prendre un sous-ensemble du jeu de cas (une quinzaine de cas suffit pour démarrer) et le faire évaluer par un humain, avec un score ou un verdict clair.
- Faire évaluer les mêmes cas par le juge LLM, avec la même grille.
- Mesurer le taux d'accord entre les deux. En dessous d'un seuil qu'on juge acceptable pour l'enjeu (souvent 80-85 % pour une évaluation qualitative), le juge n'est pas fiable : la grille est trop vague, ou le modèle choisi comme juge n'est pas assez capable pour la tâche.
- Ne faire confiance au score du juge sur le reste du jeu de cas qu'une fois cette calibration validée, et la revérifier périodiquement, pas une seule fois au lancement.
Sans cette étape, on ne remplace pas une incertitude par une certitude : on remplace une incertitude qu'on connaît par une autre qu'on croit à tort maîtrisée.
Le seuil de régression, ce qui transforme un test en garde-fou
Le jeu de cas figé, les assertions et le juge calibré produisent ensemble un chiffre : le taux d'accord, le pourcentage de cas du jeu qui passent les vérifications (mécaniques et, le cas échéant, celles du juge). Ce chiffre n'a de valeur que s'il devient une référence comparée automatiquement, pas juste un nombre qu'on regarde une fois puis qu'on oublie.
Concrètement, ça veut dire l'intégrer dans le même pipeline que le reste : un changement de prompt ou de modèle qui fait passer le taux d'accord sous un seuil défini à l'avance bloque le déploiement, au même titre qu'un test unitaire rouge bloquerait un merge sur du code applicatif classique. La différence avec un test classique n'est pas qu'on renonce à bloquer, c'est qu'on bloque sur un taux plutôt que sur un booléen.
Ce même mécanisme sert une seconde fois, plus tard, sans rien changer à l'outillage : quand le fournisseur du modèle publie une mise à jour et que le comportement change sans qu'on ait touché une ligne de son propre code, rejouer le jeu de cas dit précisément ce qu'on a gagné ou perdu sur sa propre tâche, plutôt que de le découvrir via une plainte client.
Un test unitaire classique répond "ça marche" ou "ça casse". Un jeu de cas figé sur un LLM répond "on est à 91 %, contre 94 % la semaine dernière". C'est une réponse moins confortable, et c'est la seule qui soit honnête sur ce que le modèle fait réellement.
Checklist avant de considérer un LLM comme testé
- Un jeu de cas figé (30 à 50 entrées réelles) existe, versionné avec le prompt, et grossit à chaque incident rencontré en production.
- Les vérifications automatiques portent sur des propriétés de la sortie (champ, énumération, plage, longueur), pas sur une égalité de texte.
- Si un LLM-as-judge est utilisé, son taux d'accord avec une évaluation humaine a été mesuré sur un échantillon avant d'être considéré comme fiable.
- Un seuil de régression défini à l'avance bloque le déploiement d'un nouveau prompt ou d'un nouveau modèle, au même titre qu'un test unitaire rouge.
- Le jeu de cas est rejoué à chaque mise à jour de modèle annoncée par le fournisseur, pas seulement à chaque changement de prompt.
En résumé
Tester un LLM ne consiste pas à renoncer à la rigueur du test logiciel, mais à déplacer ce qu'on mesure : d'une égalité de texte vers un taux d'accord suivi dans le temps, sur un jeu de cas réel qui grossit avec l'expérience. C'est ce taux d'accord, pas un sentiment que "ça a l'air de marcher", qui doit décider si un changement de prompt ou de modèle part en production. Pour le cadre général (coût, sortie structurée, place de l'humain, observabilité), notre article sur la mise en production d'un LLM reste le point de départ ; si ce LLM tourne dans un workflow n8n, notre guide sur l'intégration d'un LLM dans n8n montre où brancher ces mêmes vérifications autour du nœud IA.