Un pipeline d'éval LLM en 2 jours, pas en 2 semaines
La plupart des équipes livrent leurs features IA sans éval et tirent à pile ou face à chaque PR. Un jeu d'éval bien construit prend deux jours et rapporte indéfiniment — voici la version minimale viable.

Pourquoi la plupart des équipes sautent l'éval
L'éval, c'est la partie dont tout le monde reconnaît l'importance et que presque personne ne fait. Nous avons livré suffisamment de produits IA pour savoir exactement pourquoi.
Ça ressemble à du travail sans démarrage : un CSV vide, un spec vague, aucun gain visible pour l'utilisateur dans l'immédiat. Face à "livrer la prochaine feature", l'éval perd toujours la bataille des priorités. Le temps que quelqu'un remarque une régression, huit semaines de changements se sont accumulées et personne ne peut isoler quel PR a causé la chute.
Cet article présente le pipeline d'éval minimum viable. Deux jours d'ingénierie. 50 à 100 exemples. Un hook CI. Conçu pour être livré avant même que vous ayez livré ce qu'il évalue, et assez léger pour tourner sur chaque PR.
L'objectif n'est pas de construire un framework d'éval parfait. L'objectif est de cesser de piloter à l'aveugle.
La forme d'un jeu d'éval utile
Une erreur classique consiste à sur-concevoir le jeu d'éval dès le premier jour. Vous n'avez pas besoin de 5 000 exemples. Pas besoin d'un pipeline de données synthétiques. Pas besoin d'un scoring de qualité MTEB. Vous avez besoin de :
- 50 à 100 exemples issus du trafic réel (ou, si vous n'avez pas encore lancé, des exemples curatés à la main représentant les questions utilisateurs attendues)
- Des réponses de référence — rédigées par vous, par un expert du domaine, ou les deux
- Une fonction de scoring — parfois à base de règles, parfois un appel LLM-as-judge
- Un script runner qui prend une version modèle + prompt, exécute le jeu, et émet un seul chiffre plus un détail par exemple
C'est tout. Au départ, l'ensemble tient dans un seul fichier.
D'où tirer les exemples
Trois sources, par ordre de préférence :
1. Les logs de production réels. Si vous avez lancé, c'est le standard or. Extrayez 200 transcripts de conversation aléatoires (ou des requêtes RAG, ou quelle que soit votre unité). Éliminez ceux qui ne sont pas représentatifs. Gardez les 50 à 100 qui ressemblent le plus à un usage steady-state. L'avantage : le jeu d'éval ressemble au trafic réel.
2. Les tickets de support client anonymisés. Même logique, légèrement plus bruité. Si des utilisateurs écrivent "l'agent n'a pas compris X", X doit figurer dans votre éval. Anonymisez avant d'aller plus loin (les PII dans le jeu d'éval sont une erreur de confidentialité que vous regretterez).
3. La curation manuelle par un expert du domaine. Quand vous n'avez pas encore lancé. L'expert s'assied avec vous et brainstorme 50 questions sur le parcours utilisateur. La qualité varie — ils sous-couvriront la longue traîne des questions bizarres que posent vraiment les utilisateurs. Prévoyez de rafraîchir ce jeu dans le premier mois de production.
Le piège à éviter : synthétiser le jeu d'éval avec le même LLM que vous testez. Si vous demandez à GPT-4 de générer 100 questions et testez ensuite comment GPT-4 y répond, vous avez construit une éval auto-référentielle. Les chiffres sont excellents. Ils ne généralisent pas. La génération synthétique convient pour augmenter un jeu réel, pas pour le remplacer.
Rédiger les réponses de référence
Pour chaque exemple, vous avez besoin soit d'une réponse, soit d'un moyen de scorer la réponse.
Pour les questions factuelles (RAG sur un corpus connu, Q&A documentaire, outils internes), rédigez la réponse. Une phrase par question suffit généralement. Citez la source si pertinent.
Pour les tâches de classification ou de routage (quel outil appeler, quelle intention correspond), la vérité terrain est un label ou un label + des arguments. C'est le format d'éval le plus simple.
Pour la génération ouverte (aide à la rédaction, résumé, conversation), la vérité terrain est un rubric : les qualités que la réponse doit avoir. ("Inclut une date. Cite le document de politique. Ne promet pas de rappel.") Le modèle juge vérifie le rubric.
Nous utilisons généralement un fichier JSON :
[
{
"id": "001",
"input": "When does my subscription auto-renew?",
"expected": {
"must_include": ["renewal date", "amount"],
"must_not_include": ["promise of refund", "specific time of day"],
"tone": "informative",
"format": "short paragraph"
},
"tags": ["billing", "factual"]
},
{
"id": "002",
"input": "I want to cancel.",
"expected": {
"tool_call": "respond_redirect",
"tool_args": { "category": "cancellation" }
},
"tags": ["routing", "billing"]
}
]
Les tags comptent. Ils permettent de décomposer le score ensuite : "nous avons régressé sur le routage dans ce PR" est plus actionnable que "nous avons régressé quelque part".
LLM-as-judge — les prompts qui ne dérivent pas
Pour les sorties ouvertes, vous utiliserez un autre LLM comme juge. Le piège : les prompts de juge qui dérivent dans leurs propres critères de scoring au fil du temps. Trois principes pour des juges stables :
1. Faites du juge un classifieur, pas un rédacteur. Ne demandez pas "notez cette réponse de 0 à 10". Posez trois à cinq questions binaires ou à faible cardinalité. Agrégez en un chiffre en dehors du LLM :
const judgePrompt = `Given the user's question and the agent's response, answer:
1. Does the response contain factually wrong information? (yes/no)
2. Does the response answer the user's actual question? (fully/partially/no)
3. Does the response include any forbidden phrases? (yes/no)
Forbidden: ${FORBIDDEN_PHRASES.join(', ')}
Return JSON: {"factually_wrong": bool, "answers": "fully"|"partially"|"no", "forbidden": bool}`
Beaucoup plus stable qu'un score. Chaque réponse binaire est plus facile à vérifier et moins sujette à la dérive.
2. Utilisez un autre modèle que celui que vous évaluez. Si vous testez Claude, jugez avec GPT-4o. Si vous testez Gemini, jugez avec Claude. Les biais sont différents, l'éval est plus honnête. Le self-judging est biaisé vers "ça semble correct parce que je l'aurais écrit ainsi".
3. Épinglez le modèle juge. Quand Anthropic sort une nouvelle version de Claude, ne mettez pas à jour le juge automatiquement. Épinglez l'ID exact du modèle. Le jour où vous upgradez le juge, vos scores peuvent varier même si rien dans le système évalué n'a changé.
Scoring à base de règles pour ce qu'on peut mesurer
Tous les contrôles n'ont pas besoin d'un LLM. Les signaux d'éval les moins coûteux sont déterministes :
- String contains pour must-include / must-not-include.
- Forme JSON pour les schémas d'appel d'outil. Le modèle a-t-il appelé le bon outil avec les bons types d'arguments ?
- Bornes de longueur ("réponse entre 50 et 500 caractères").
- Proxies de ton : présence de mots interdits ("garantir", "promettre"), nombre max de points d'exclamation, pas de majuscules totales.
- Validation des sorties structurées : si l'agent doit retourner du JSON, parsez et rejetez en cas d'échec.
Les contrôles à base de règles coûtent 1 ms chacun et ne coûtent rien. Superposez-les au LLM-as-judge pour les choses que les juges sur-compliquent. Nous obtenons généralement 30 à 60 % de notre signal de score via des contrôles déterministes, et le reste via un seul appel juge par exemple.
Intégration CI — le job qui attrape tout
L'intérêt de construire une éval, c'est qu'elle tourne sur chaque PR, bloquante ou non, et que vous voyez le score avant de merger.
Un job GitHub Actions / GitLab CI minimal :
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pnpm install
- run: pnpm tsx scripts/eval.ts --set=v1 --output=eval-results.json
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} # for judge
- uses: actions/upload-artifact@v4
with:
name: eval-results
path: eval-results.json
- run: pnpm tsx scripts/eval-compare.ts --baseline=main --current=eval-results.json
Le script runner fait trois choses : charger les exemples, les faire passer par le système évalué, écrire les résultats par exemple. Le script de comparaison diff le run courant avec la baseline (le dernier main vert) et poste un commentaire sur le PR avec le score, les régressions et les gains.
Deux choix de conception qui portent leurs fruits :
Non-bloquant par défaut. Si l'éval est lente ou instable les premières semaines, le mode bloquant frustrera l'équipe et elle le désactivera. Non-bloquant avec un commentaire PR clair ("eval score : 87 % (-3 % vs main, 4 régressions)") vous donne 90 % de la valeur. Vous pouvez passer en bloquant après deux semaines de stabilité.
Maintenez un coût prévisible. Faites tourner l'éval sur chaque PR, mais plafonnez le coût. 100 exemples × 0,01 $ par appel = 1 $ par run. À 10 PR/jour, c'est 10 $/jour, 300 $/mois. Ne laissez pas le jeu d'éval grossir jusqu'à 5 000 exemples tant que vous n'avez pas le budget pour ça. S'il grandit, échantillonnez 100 exemples aléatoires par PR et le jeu complet sur main uniquement.
Le dashboard de dérive
Un score par PR, c'est bien. Une tendance dans le temps, c'est mieux. Nous loggons chaque run d'éval dans une table time-series simple :
| run_id | timestamp | git_sha | score | model | prompt_version | by_tag |
| ------ | --------- | ------- | ----- | ----- | -------------- | ------ |
| ... | ... | ... | 0.87 | sonnet-4-6 | v3 | { billing: 0.91, routing: 0.82 } |
Tracez score en fonction de timestamp, coloré par prompt_version. Vous pouvez repérer :
- La dérive lente quand le fournisseur de modèle change silencieusement de comportement (rare mais réel).
- Les falaises quand un changement de prompt a effondré un tag.
- Les récupérations pour savoir quel PR a corrigé la régression.
C'est ce dashboard que les ingénieurs seniors consultent quand quelqu'un demande "l'agent est-il meilleur ou moins bon qu'il y a un trimestre ?". Sans lui, vous répondez au feeling.
Le piège du gaming de l'éval
Vous avez construit l'éval. Le score est à 73 %. Vous changez le prompt et il passe à 89 %. Avez-vous vraiment amélioré le système ?
Peut-être. Peut-être avez-vous sur-fitté le prompt au jeu d'éval.
Trois habitudes pour éviter ça :
1. Gardez un split de test. Conservez 20 % de vos exemples dans un jeu test que l'équipe ne voit pas pendant l'itération des prompts. Faites-le tourner mensuellement. Si le score test diverge significativement du score train, vous avez commencé à gamer.
2. Rafraîchissez le jeu d'éval trimestriellement. Extrayez 20 nouveaux exemples du trafic réel, retirez-en 20 anciens. La dérive en production doit correspondre à la dérive dans le jeu d'éval, pas l'inverse.
3. N'optimisez pas le score directement. Optimisez les modes d'échec qu'il fait remonter. Si l'exemple 7 passe maintenant parce que vous avez ajouté une correspondance de chaîne pour "renewal date", mais qu'il ne comprend pas vraiment le concept de date de renouvellement, vous avez déplacé le score sans déplacer le système.
Outils
Vous n'avez pas besoin d'un framework dès le premier jour. Un dossier avec examples.json, un runner.ts, un judge.ts et compare.ts suffit.
Si vous voulez quand même un framework :
- Ragas — spécifique RAG, métriques de fidélité et de pertinence des réponses out of the box. Solide pour le RAG vanilla ; opinionné pour tout le reste.
- DeepEval — portée plus large, s'intègre bien avec pytest et les test runners Python classiques.
- PromptFoo — piloté par config, idéal pour A/B-tester des prompts à moindre coût entre providers.
- Du custom — ce à quoi on aboutit généralement après un mois, parce que les frameworks vous enferment dans leur modèle de score et qu'on finit toujours par vouloir des rubrics différents par agent.
Commencez par la chose la plus simple qui vous donne un chiffre. Remplacez-la quand elle ne suffit plus.
Où investir une fois les bases en place
Après quelques mois sur le pipeline de base, trois ajouts deviennent rentables :
1. Les spot-checks humains. Une fois par semaine, revoyez 10 exemples aléatoires à la main. Le juge est biaisé ; vous ne détecterez les échecs du juge qu'en regardant les sorties occasionnellement.
2. Un jeu de régression curé. Quand un utilisateur réel remonte un bug, ajoutez-le au jeu d'éval. Le système ne peut plus jamais régresser sur ce mode d'échec exact. Sur une année de production, c'est la couche d'éval la plus précieuse — une base de données des "choses qui ont mal tourné, et qu'on s'est assuré de ne plus pouvoir répéter".
3. Des tranches par tenant dans les SaaS multi-tenants. Les requêtes du client A peuvent se dégrader pendant que celles du client B s'améliorent. Le score agrégé masque ça. Découpez par tenant pour les agents qui comptent.
Quand l'éval ne vaut pas l'investissement
Une courte liste de cas où construire une éval est excessif :
- Les démos de proof-of-concept qui n'arriveront pas en production.
- Les tâches de génération à usage unique (une migration de données ponctuelle, un rapport one-shot). Un spot-check manuel suffit.
- Les sorties à très faible enjeu (un agent "cool emoji du jour"). Le coût d'une erreur est trop faible pour justifier le travail.
Pour tout le reste — tout ce que vous allez livrer et dont vous allez dépendre — l'éval est l'assurance la moins chère disponible.
Conclusion
Le changement de mindset ici est de cesser de traiter l'éval comme un exercice académique et de commencer à la traiter comme un test unitaire pour les features IA. On ne teste pas une feature à la main à chaque fois ; on n'évalue pas un agent en lisant les transcripts à la main. Construisez le runner une fois. Faites-le tourner sur chaque PR. Regardez le dashboard.
Si vous avez livré une feature IA sans éval et que vous ne savez pas comment en ajouter une rétroactivement sans trois semaines de travail, parlons-en. Deux jours suffisent généralement pour obtenir un premier score utile.
Travailler avec Ikki
Vous pilotez votre LLM à l'aveugle ?
Nous montons un pipeline d'éval en 5 jours, avec des cas de test extraits de vos vraies conversations et un dashboard à lancer chaque semaine.
Autres articles
La semaine où Anthropic a pris le contrôle de la pile complète
Project Glasswing en bêta publique, acquisition de Stainless, sept releases SDK en quatre jours. La question n'est plus 'quel modèle' — c'est 'quelle plateforme'.
AgentsSix releases en onze jours : ce que le sprint pré-I/O de Google annonce
@google/genai a livré les API Agent et Environment aujourd'hui — à quelques jours de Google I/O. La cadence du SDK vous dit ce qui arrive avant le keynote.