Deuxième article de la série « Production Engineering, Evals & Security avec Claude ».
Une application basée sur Claude peut produire dix réponses convaincantes d’affilée et pourtant ne pas être prête pour la production.
Le problème est simple : « ça a l’air de fonctionner » n’est pas une métrique.
Pour passer d’un prototype à un système que l’on peut réellement maintenir, comparer et faire évoluer, il faut définir ce que signifie « fonctionner correctement », puis le mesurer sur un ensemble de cas reproductibles.
C’est précisément le rôle des evals.
Dans le module Production Engineering, Evals & Security, Anthropic présente l’eval comme le mécanisme permettant de transformer une appréciation subjective en score mesurable sur un ensemble fixe de cas.
Qu’est-ce qu’une eval ?
Une eval est un ensemble de :
inputs
+
expected behaviors
+
grading
permettant de mesurer le comportement d’une fonctionnalité.
Le principe général est :
Dataset
│
↓
Application / Claude
│
↓
Output
│
↓
Grader
│
↓
Score
Chaque cas possède au minimum :
Input
→ ce que reçoit le système
Expected behavior
→ ce qu'il devrait produire
Output
→ ce qu'il produit réellement
Grade
→ dans quelle mesure le résultat satisfait l'attente
On exécute ensuite tous les cas et on agrège leurs scores.
Une version minimale pourrait conceptuellement ressembler à :
def run_test_case(test_case):
output = run_prompt(test_case)
score = grade(
test_case,
output
)
return {
"output": output,
"test_case": test_case,
"score": score
}
def run_eval(dataset):
results = [
run_test_case(case)
for case in dataset
]
average = (
sum(r["score"] for r in results)
/ len(results)
)
return results, average
Ce code n’améliore pas Claude.
Il mesure simplement le comportement du système.
C’est une distinction essentielle.
Une eval ne corrige pas le système
Anthropic utilise dans le document une analogie particulièrement utile : l’eval fonctionne comme un thermomètre.
Un thermomètre ne soigne pas un patient.
Il permet de mesurer son état.
De la même manière :
eval
≠
solution au problème
Une eval permet de détecter :
régression
cas limite
mauvaise instruction
problème de retrieval
problème de contexte
différence entre modèles
Mais la correction peut se trouver ailleurs :
prompt
retrieval
tool
model
architecture
context management
C’est pourquoi un mauvais score est avant tout une information permettant d’orienter l’analyse.
Pourquoi écrire les evals avant l’implémentation ?
C’est l’une des idées importantes du module.
Avant d’écrire le système, il faut définir :
À quoi ressemble un résultat correct ?
Prenons une fonctionnalité de résumé.
Une spécification comme :
« Résumer correctement la conversation »
est beaucoup trop vague.
Comment déterminer automatiquement si le système respecte cette exigence ?
En revanche :
« Produire un résumé de deux phrases mentionnant le problème rencontré et son statut actuel. »
devient vérifiable.
Par exemple :
{
"input": "Long support thread about a delayed refund...",
"expected_behavior":
"A 2-sentence summary naming the issue (delayed refund) and the current status (escalated)."
}
On peut maintenant construire un grader autour de ce comportement attendu.
Le design document vient avant l’eval
Le document source va même un cran plus loin : avant la production, Anthropic recommande de formaliser un design document contenant quatre décisions.
| Décision | Question |
|---|---|
| Success criteria | Que doit produire le système ? |
| Failure handling | Quelles défaillances doit-il supporter ? |
| Cost & latency budget | Quelles limites doit-il respecter ? |
| Trust boundary | Quelles données et actions sont autorisées ? |
Pour les evals, la première partie est fondamentale :
Success criteria
↓
Expected behaviors
↓
Eval cases
↓
Grading
↓
Score
Cela évite un problème classique avec les LLM : modifier les critères après avoir vu les réponses du modèle.
Si le résultat est déjà devant nous, il est très facile de rationaliser :
« Ce n’est pas exactement ce qu’on avait demandé, mais finalement cette réponse est acceptable. »
L’eval définie avant l’implémentation limite ce biais.
Les trois grandes méthodes de grading
Tous les outputs ne doivent pas être évalués de la même manière.
Le document distingue trois méthodes principales :
exact/string matchcode-graded checkLLM-as-judge
Le choix dépend de la forme du résultat attendu.
1. Exact match : lorsqu’il existe une seule réponse correcte
Supposons que Claude doive classifier un message parmi :
billing
technical
sales
Le résultat attendu est :
billing
On peut simplement tester :
def grade(expected, output):
return 10 if output == expected else 0
C’est :
- rapide ;
- déterministe ;
- très peu coûteux ;
- simple à exécuter à grande échelle.
Pour une classification ou une valeur précisément définie, c’est souvent la meilleure solution.
Le piège de l’exact match
Supposons maintenant qu’on demande trois villes sous forme de tableau JSON.
Référence :
[
"Paris",
"Lyon",
"Marseille"
]
Claude retourne :
[
"Marseille",
"Paris",
"Lyon"
]
Si l’ordre n’a aucune importance, la réponse est correcte.
Pourtant un exact match caractère par caractère échoue.
Le grader est donc trop strict.
Le problème n’est pas le modèle.
Le problème est le choix du grader.
2. Code grader : vérifier une propriété plutôt qu’une chaîne exacte
Lorsque le résultat doit respecter des contraintes structurelles, il est souvent préférable d’écrire du code.
Par exemple :
import json
def validate_json(text):
try:
json.loads(text.strip())
return 10
except json.JSONDecodeError:
return 0
Pour du Python :
import ast
def validate_python(text):
try:
ast.parse(text.strip())
return 10
except SyntaxError:
return 0
Le grader peut également contrôler :
- la présence de propriétés obligatoires ;
- un type ;
- une plage numérique ;
- la présence de certaines valeurs ;
- la validité d’une structure ;
- l’absence de champs interdits.
Exemple plus réaliste
Imaginons le contrat suivant :
{
"category": "...",
"confidence": 0.0
}
On peut vérifier :
def validate_output(text):
try:
obj = json.loads(text)
assert "category" in obj
assert "confidence" in obj
assert obj["category"] in {
"billing",
"technical",
"sales"
}
assert 0 <= obj["confidence"] <= 1
return 10
except (ValueError, AssertionError):
return 0
Ici, plusieurs outputs différents peuvent être acceptables.
On vérifie le contrat, pas une chaîne de caractères.
Ce qu’un code grader ne sait pas mesurer
Supposons maintenant qu’on demande :
« Explique pourquoi cette architecture est appropriée pour cette application. »
On pourrait vérifier :
len(output) > 0
Mais cela ne dit absolument pas si l’explication est :
- correcte ;
- pertinente ;
- complète ;
- fidèle au contexte ;
- bien argumentée.
C’est là qu’intervient le troisième type de grader.
3. LLM-as-a-judge : évaluer une réponse ouverte
Le LLM-as-judge consiste à utiliser un second appel à un modèle pour évaluer le résultat du premier.
Architecture :
Task
│
↓
Claude
│
↓
Output
│
↓
┌────────────────┐
│ Judge model │
│ + evaluation │
│ rubric │
└────────────────┘
│
↓
Score
Cette méthode devient utile lorsqu’on veut mesurer :
faithfulness
completeness
instruction following
quality
tone
et que ces propriétés ne peuvent pas être exprimées par une simple règle de code.
Construire le prompt du judge
Le document donne le principe suivant : le judge ne devrait pas uniquement retourner un nombre.
Il est préférable de demander :
{
"strengths": [],
"weaknesses": [],
"reasoning": "...",
"score": 8
}
Conceptuellement :
def grade_by_model(task, solution):
eval_prompt = f"""
You are an expert reviewer.
Evaluate the solution for the task.
Task:
{task}
Solution:
{solution}
Return JSON with:
"strengths": array of 1-3 points
"weaknesses": array of 1-3 points
"reasoning": one to two sentences
"score": number from 1 to 10
"""
result = chat([
{
"role": "user",
"content": eval_prompt
}
])
return json.loads(result)
Le raisonnement demandé au judge permet d’ancrer davantage son score dans des critères explicites.
Un LLM-as-a-judge n’est pas automatiquement fiable
C’est probablement le point le plus important concernant cette technique.
Il serait tentant de penser :
Claude produit une réponse
↓
un autre modèle donne 8.7/10
↓
donc qualité = 8.7
Non.
Un nombre produit par un autre LLM reste un résultat probabiliste.
Le document insiste donc sur la calibration du judge.
Calibrer le judge avec des évaluations humaines
Il faut commencer avec un ensemble de réponses déjà évaluées par des humains.
Par exemple :
| Cas | Score humain |
|---|---|
| A | 9 |
| B | 3 |
| C | 8 |
| D | 5 |
| E | 2 |
On demande ensuite au judge d’évaluer exactement les mêmes réponses :
| Cas | Humain | Judge |
|---|---|---|
| A | 9 | 8 |
| B | 3 | 4 |
| C | 8 | 8 |
| D | 5 | 6 |
| E | 2 | 2 |
On mesure alors le niveau d’accord.
Conceptuellement :
human labels
│
├───────────────┐
↓ ↓
ground truth LLM judge
│
↓
judge labels
│
┌───────────────┘
↓
measure agreement
Si l’accord est mauvais, on ne doit pas simplement accepter le judge.
On améliore sa rubric.
Comment améliorer un judge mal calibré ?
Le document recommande notamment de :
- préciser la signification des scores ;
- fournir des exemples ;
- montrer une bonne réponse ;
- montrer une mauvaise réponse ;
- puis mesurer à nouveau l’accord avec les humains.
Par exemple, une échelle :
1–3 = mauvaise
4–7 = moyenne
8–10 = bonne
reste relativement vague.
Il est préférable d’expliciter ce que signifie chaque plage selon la tâche.
Le judge dispose alors d’une rubric beaucoup plus précise.
Le coût caché du LLM-as-a-judge
Un exact match coûte pratiquement rien.
Un code grader s’exécute localement et coûte également très peu.
Mais un LLM-as-judge implique :
1 cas d'eval
=
1 exécution du système
+
1 appel supplémentaire au judge
Pour :
1000 eval cases
on peut donc avoir :
1000 appels système
+
1000 appels judge
Le document propose donc une stratégie très intéressante :
Chaque commit
↓
exact match
+
code graders
Périodiquement
↓
full eval
+
LLM-as-judge
Autrement dit, le type de grader dépend également de la fréquence à laquelle on veut pouvoir exécuter l’eval.
Tableau de décision
| Type d’output | Grader recommandé |
|---|---|
| Label unique | Exact match |
| Valeur exacte | Exact match |
| JSON valide | Code grader |
| Code syntaxiquement valide | Code grader |
| Valeur dans une plage | Code grader |
| Champs obligatoires | Code grader |
| Résumé fidèle | LLM-as-judge |
| Qualité d’une explication | LLM-as-judge |
| Respect complexe d’instructions | LLM-as-judge |
Raccourci utile pour l’examen :
Une seule forme correcte
→ exact match
Une propriété vérifiable
→ code grader
Une qualité ouverte
→ LLM-as-judge
Coverage : tester davantage de situations
Une eval parfaite sur trois exemples n’est pas nécessairement utile.
Le document insiste sur un autre principe :
Coverage matters more than perfection.
Une vingtaine de cas couvrant différentes situations peut révéler davantage de problèmes que trois cas méticuleusement évalués.
Il faut notamment inclure :
normal cases
edge cases
ambiguous cases
missing information
unexpected formats
long inputs
conflicting information
Claude peut aider à générer des edge cases
Une stratégie proposée dans le module consiste à demander au modèle de rechercher les situations susceptibles de casser l’implémentation actuelle.
Supposons qu’on construise un extracteur de date.
Les tests initiaux contiennent :
"My order was placed on March 3."
Claude peut suggérer des situations comme :
aucune date
deux dates
date relative
date ambiguë
date incorrecte
"next Tuesday"
"March 3 or March 4"
"I ordered March 3
and received it April 12"
Ces cas peuvent ensuite être transformés en véritables exemples d’eval.
Important : le document recommande de spot-checker humainement les cas générés afin de conserver un dataset fiable.
Le cas des deux dates : pourquoi la validation ne suffit pas
Le document présente un exemple particulièrement instructif.
Une fonctionnalité extrait la date de commande depuis un message client.
Pendant le développement, elle fonctionne correctement.
Puis arrive :
“I placed my order on March 3 but did not receive it until April 12.”
Le système extrait :
April 12
comme date de commande.
Le problème est subtil.
La validation vérifie que :
April 12
est bien une date.
Donc :
format valide
✓
champ renseigné
✓
date possible
✓
Pourtant :
valeur correcte
✗
C’est une distinction fondamentale :
La validation peut confirmer que la donnée a la bonne forme sans confirmer qu’il s’agit de la bonne donnée.
Pourquoi le système avait pourtant passé tous les tests ?
Parce que les tests manuels contenaient uniquement :
une phrase
+
une date
Personne n’avait défini le comportement attendu pour :
une phrase
+
deux dates
Le système avait donc réussi tous les tests existants.
Mais les tests existants ne couvraient pas suffisamment le domaine réel.
C’est précisément le problème que doit résoudre une eval correctement construite.
Une régression devient alors impossible à oublier
Une fois le bug découvert, on ajoute :
{
"input":
"I placed my order on March 3 but did not receive it until April 12.",
"expected_behavior":
"Extract March 3 as the order date."
}
Puis on corrige le système.
Ce cas reste ensuite dans l’eval.
Ainsi, une future modification du :
prompt
model
retrieval
tool
qui réintroduit le bug fait immédiatement baisser le score.
C’est un regression test comportemental.
La boucle d’amélioration
Une bonne stratégie d’amélioration n’est pas :
modifier prompt
+
changer modèle
+
ajouter examples
+
changer retrieval
↓
eval
Si le score passe de :
7.1 → 8.4
on ignore ce qui a réellement provoqué l’amélioration.
Le document recommande donc de changer une variable à la fois.
Baseline
│
↓
Eval
│
↓
Analyse des échecs
│
↓
UNE modification
│
↓
Eval
│
↓
Comparer
Puis :
amélioration ?
├── oui → conserver
└── non → revenir en arrière
C’est une démarche expérimentale.
Ne regardez pas uniquement le score moyen
Supposons :
Version A = 8.2/10
Version B = 8.2/10
On pourrait conclure :
aucun changement
Mais en examinant les résultats individuels :
Version B
+ corrige 3 edge cases
- casse 3 cas fréquents
La moyenne masque complètement cette évolution.
Il faut donc suivre :
aggregate score
+
per-case results
Le diagnostic détaillé est souvent aussi important que le score global.
Une mauvaise eval peut conduire à une mauvaise décision
Une eval n’est pas automatiquement bonne simplement parce qu’elle produit un nombre.
On peut avoir :
dataset non représentatif
+
grader mal choisi
+
judge non calibré
+
coverage insuffisante
et obtenir :
9.4 / 10
Ce nombre peut donner une illusion de rigueur sans mesurer ce qui compte réellement.
La qualité d’une eval dépend donc de trois éléments :
CAS
+
EXPECTED BEHAVIOR
+
GRADER
Les trois doivent correspondre au problème réel.
Exemple d’architecture d’eval complète
Une architecture plus réaliste peut être :
EVAL DATASET
│
┌─────────────┼─────────────┐
↓ ↓ ↓
normal edge adversarial
cases cases cases
│ │ │
└─────────────┼─────────────┘
↓
FEATURE
│
↓
OUTPUT
│
┌──────────┼──────────┐
↓ ↓ ↓
exact code judge
match check rubric
│ │ │
└──────────┼──────────┘
↓
RESULTS
│
┌──────────┴──────────┐
↓ ↓
global score per-case results
C’est beaucoup plus informatif qu’une série de tests manuels.
Ce qu’il faut retenir pour la certification
1. Une eval définit ce que signifie « done »
input cases
+
expected behaviors
+
grading
=
eval
Elle doit idéalement être pensée avant l’implémentation.
2. Choisir le grader selon la nature de l’output
one correct form
→ exact match
structural rule
→ code grader
open-ended quality
→ LLM-as-judge
3. Un LLM-as-judge doit être calibré
human-labeled cases
↓
judge
↓
measure agreement
Un score produit par un judge non calibré n’est pas une preuve suffisante de qualité.
4. Coverage > quelques beaux exemples
Inclure notamment les edge cases.
5. Une validation structurelle n’est pas une validation sémantique
valid date
≠
correct date
6. Modifier une variable à la fois
change
→ eval
→ compare
Sinon, on ne sait pas quelle modification a amélioré ou dégradé le système.
7. Examiner les résultats par cas
Une moyenne stable peut cacher plusieurs nouvelles régressions.
Pièges d’examen fréquents
Question : une réponse JSON peut être formulée de plusieurs manières mais doit contenir quatre champs précis. Quel grader privilégier ?
→ Code-graded check, pas LLM-as-judge.
Question : vous devez évaluer la fidélité de résumés rédigés librement.
→ LLM-as-judge.
Question : le judge fournit des scores très précis de 1 à 10. Peut-on les utiliser immédiatement comme métrique de production ?
→ Non. Il faut notamment le calibrer sur des cas évalués humainement et mesurer l’accord.
Question : un extracteur renvoie une date syntaxiquement valide mais correspondant au mauvais événement.
→ Le problème ne peut pas être détecté par une simple validation du format. Il faut un cas d’eval définissant la valeur sémantiquement correcte.
Question : vous modifiez simultanément le modèle, le system prompt et les few-shot examples et le score augmente.
→ Vous ne pouvez pas déterminer quelle modification a provoqué l’amélioration.
À retenir en une phrase
Une eval transforme « cela semble fonctionner » en une mesure reproductible : des cas représentatifs, un comportement attendu et le grader le plus simple capable de mesurer correctement ce comportement.

