Aller au contenu principal
Leçon 20 sur 95 · Construire avec l'API Claude

Notation par modèle

Leçon 20 sur 95 Mis à jour le 5 novembre 2026 Développeur
Sur cette page
  1. Les trois types de notation
  2. Le principe
  3. Définir les critères d’évaluation
  4. Implémenter grade_by_model()
  5. Intégrer dans le pipeline
  6. Pipeline complet
  7. Analyser les résultats
  8. Points d’attention
  9. Résumé
  10. À vous de jouer
  11. Points clés à retenir

Leçon mise à jour le 2 novembre 2026. Le cours d’origine extrayait la note par pré-remplissage et séquence d’arrêt, une technique que les modèles actuels refusent (erreur 400). Le notateur utilise désormais les sorties structurées (output_config) de la leçon « Données structurées » ; le try/except reste utile pour les refus ou les réponses coupées par la limite de jetons.

Mise à jour du 2 novembre 2026 — température. Sur Claude Opus 4.7 et tous les modèles suivants, une valeur de temperature, top_p ou top_k différente de la valeur par défaut renvoie une erreur 400, et la version d’API 2026-10-21 refuse ces trois paramètres dans tous les cas. Ils ont donc été retirés du code de cette leçon. Pour rendre une évaluation fiable sans eux, répétez les exécutions et regardez la dispersion des scores plutôt qu’un score isolé.

Les trois types de notation

Le choix du notateur détermine ce que votre évaluation est capable de mesurer, et il vaut mieux le faire dans l’ordre du tableau ci-dessous. Chaque fois qu’un critère peut se vérifier par du code — le JSON est-il valide, la réponse contient-elle le champ attendu, fait-elle moins de 200 mots — écrivez le code : c’est gratuit, instantané et sans ambiguïté. Le notateur par modèle ne se justifie que pour ce qu’aucun test ne sait trancher.

TypeDescriptionQuand l’utiliser
CodeVérification automatique par programmeRéponses factuelles, formats stricts
ModèleUn autre appel AI évalue la réponseRéponses ouvertes, qualité subjective
HumainUn évaluateur humain noteDernière validation, cas critiques

La notation par code est la plus fiable quand elle s’applique (ex: “la réponse est-elle du JSON valide ?”). Mais pour des critères comme “le code est-il bien commenté ?” ou “l’explication est-elle claire ?”, on a besoin d’un notateur par modèle.

Le principe

L’idée surprend au premier abord — demander à un modèle de juger un modèle — mais la tâche n’est pas la même des deux côtés. Produire une bonne réponse est difficile ; reconnaître si une réponse remplit trois critères explicites l’est beaucoup moins. C’est cette asymétrie qui rend la notation automatique fiable, à condition que les critères soient réellement explicites.

  • La requête originale
  • La réponse générée
  • Les critères d’évaluation
  • L’instruction de produire un score et une justification

Définir les critères d’évaluation

Pour notre assistant AWS, on définit trois critères :

CRITERES_EVALUATION = """Évalue la réponse selon ces critères :

1. FORMAT (0-3 points) : La sortie est-elle dans le format demandé (Python/JSON/Regex) ?
   Est-elle bien formatée et lisible ?

2. VALIDITÉ (0-4 points) : Le code/config généré est-il syntaxiquement correct ?
   Fonctionnerait-il tel quel sans modification ?

3. ADÉQUATION (0-3 points) : La réponse correspond-elle précisément à la demande ?
   Tous les éléments demandés sont-ils présents ?

Score total sur 10."""

Implémenter grade_by_model()

import anthropic
import json

SCHEMA_EVALUATION = {
    "type": "object",
    "properties": {
        "forces": {"type": "string"},
        "faiblesses": {"type": "string"},
        "raisonnement": {"type": "string", "description": "Courte justification de la note, en une ou deux phrases"},
        "score": {"type": "integer", "description": "Note de 1 à 10"},
    },
    "required": ["forces", "faiblesses", "raisonnement", "score"],
    "additionalProperties": False,
}

client = anthropic.Anthropic()

def grade_by_model(test_input, test_type, response):
    """
    Utilise Claude pour noter une réponse.
    Retourne un dict avec score, forces, faiblesses et raisonnement.
    """
    grading_prompt = f"""Tu es un évaluateur strict de code AWS.

REQUÊTE ORIGINALE :
{test_input}

TYPE ATTENDU : {test_type}

RÉPONSE À ÉVALUER :
{response}

{CRITERES_EVALUATION}

Analyse la réponse et fournis :
1. Les points forts (forces)
2. Les points faibles (faiblesses)
3. Une courte justification de ta note (raisonnement)
4. Un score final sur 10

Réponds en JSON."""

    grading_response = client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=4096,
        messages=[{"role": "user", "content": grading_prompt}],
        output_config={"format": {"type": "json_schema", "schema": SCHEMA_EVALUATION}},
    )

    text = next((b.text for b in grading_response.content if b.type == "text"), "")
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        # Notation impossible (réponse tronquée, refus) : on ne l'invente pas
        return {
            "forces": "",
            "faiblesses": "Notation impossible",
            "raisonnement": f"stop_reason : {grading_response.stop_reason}",
            "score": None,
        }

Le cours officiel insiste sur un point : demander au notateur ses forces, ses faiblesses et un raisonnement en plus de la note. Sans ce contexte, les modèles ont tendance à donner des notes moyennes, autour de 6. Son notateur extrait le JSON par préremplissage d’un bloc de code ; ici, le schéma des sorties structurées le garantit, sur les modèles actuels qui refusent le préremplissage. Et quand la notation échoue (réponse tronquée, refus), on ne lui attribue pas une note par défaut : un 5 inventé fausserait la moyenne ; on marque le cas None et on le compte à part.

Trois façons de noter une réponse Trois tuiles : le code (syntaxe, longueur, mots), le modèle (qualité, respect de la consigne, complétude, sécurité), l'humain (qualité générale, profondeur, pertinence) ; le modèle rend forces, faiblesses, raisonnement et note. NOTATEURS Trois façons de noter une réponse 01 Code Syntaxe JSON, Python, regex ; longueur ; mots présents ou absents. Déterministe Gratuit 02 Modèle Qualité, respect de la consigne, complétude, utilité, sécurité. Forces Faiblesses Note 03 Humain Qualité générale, profondeur, pertinence ; lent et fastidieux. Échantillon Fort enjeu Sans forces, faiblesses et raisonnement, un juge tend vers des notes moyennes, autour de 6 ; une notation impossible se marque, elle ne s'invente pas.
D'après la leçon « Model based grading » (Claude Academy, Building with the Claude API).

Intégrer dans le pipeline

On remplace le score placeholder de run_test_case() :

def run_test_case(test_case, prompt_template):
    """
    Exécute un cas de test avec notation par modèle.
    """
    # Obtenir la réponse de Claude
    response = run_prompt(test_case, prompt_template)

    # Notation par modèle (remplace le score provisoire de 10)
    evaluation = grade_by_model(
        test_input=test_case["input"],
        test_type=test_case["type"],
        response=response
    )

    return {
        "input": test_case["input"],
        "type": test_case["type"],
        "context": test_case["context"],
        "difficulty": test_case.get("difficulty", "non spécifié"),
        "response": response,
        "score": evaluation.get("score", 0),
        "forces": evaluation.get("forces", ""),
        "faiblesses": evaluation.get("faiblesses", ""),
        "raisonnement": evaluation.get("raisonnement", "")
    }

Pipeline complet

Voici le code complet qui intègre tout :

import anthropic
import json
from collections import defaultdict

client = anthropic.Anthropic()

SCHEMA_EVALUATION = {
    "type": "object",
    "properties": {
        "forces": {"type": "string"},
        "faiblesses": {"type": "string"},
        "raisonnement": {"type": "string", "description": "Courte justification de la note, en une ou deux phrases"},
        "score": {"type": "integer", "description": "Note de 1 à 10"},
    },
    "required": ["forces", "faiblesses", "raisonnement", "score"],
    "additionalProperties": False,
}

CRITERES_EVALUATION = """Évalue la réponse selon ces critères :

1. FORMAT (0-3 points) : La sortie est-elle dans le format demandé ?
2. VALIDITÉ (0-4 points) : Le code est-il syntaxiquement correct ?
3. ADÉQUATION (0-3 points) : La réponse correspond-elle à la demande ?

Score total sur 10."""

PROMPT_TEMPLATE = """Tu es un expert AWS. Génère du code propre et fonctionnel.

Contexte : {context}
Type de sortie attendu : {type}

Requête : {input}"""


def run_prompt(test_case, prompt_template):
    prompt = prompt_template.format(
        context=test_case["context"],
        type=test_case["type"],
        input=test_case["input"]
    )
    response = client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=4096,
        messages=[{"role": "user", "content": prompt}]
    )
    return "".join(block.text for block in response.content if block.type == "text")


def grade_by_model(test_input, test_type, response):
    grading_prompt = f"""Tu es un évaluateur strict de code AWS.

REQUÊTE : {test_input}
TYPE ATTENDU : {test_type}

RÉPONSE À ÉVALUER :
{response}

{CRITERES_EVALUATION}

Réponds avec : forces, faiblesses, une courte justification (raisonnement), score (entier 1-10)."""

    grading_response = client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=4096,
        messages=[{"role": "user", "content": grading_prompt}],
        output_config={"format": {"type": "json_schema", "schema": SCHEMA_EVALUATION}},
    )

    text = next((b.text for b in grading_response.content if b.type == "text"), "")
    try:
        return json.loads(text)
    except json.JSONDecodeError:
        return {"forces": "", "faiblesses": "Notation impossible", "raisonnement": "", "score": None}


def run_test_case(test_case, prompt_template):
    response = run_prompt(test_case, prompt_template)
    evaluation = grade_by_model(test_case["input"], test_case["type"], response)

    return {
        "input": test_case["input"],
        "type": test_case["type"],
        "response": response,
        "score": evaluation.get("score", 0),
        "forces": evaluation.get("forces", ""),
        "faiblesses": evaluation.get("faiblesses", "")
    }


def run_eval(dataset, prompt_template):
    results = []

    for i, test_case in enumerate(dataset):
        print(f"[{i+1}/{len(dataset)}] {test_case['input'][:50]}...")
        result = run_test_case(test_case, prompt_template)
        results.append(result)
        print(f"  -> Score : {result['score']}/10")

    notes = [r["score"] for r in results if r["score"] is not None]
    avg_score = sum(notes) / len(notes)

    print(f"\n{'='*50}")
    print(f"Score moyen : {avg_score:.2f}/10 ({len(results) - len(notes)} notation(s) impossible(s))")

    return results, avg_score


# Lancer l'évaluation
with open("dataset.json", "r", encoding="utf-8") as f:
    dataset = json.load(f)

results, avg_score = run_eval(dataset, PROMPT_TEMPLATE)

# Sauvegarder
with open("eval_results.json", "w", encoding="utf-8") as f:
    json.dump(results, f, indent=2, ensure_ascii=False)

Analyser les résultats

# Score moyen par type
scores_par_type = defaultdict(list)
for r in results:
    if r["score"] is not None:
        scores_par_type[r["type"]].append(r["score"])

print("Scores par type :")
for t, scores in sorted(scores_par_type.items()):
    avg = sum(scores) / len(scores)
    print(f"  {t:10s} : {avg:.1f}/10 (n={len(scores)})")

# Cas les plus faibles
print("\nCas les plus faibles :")
worst = sorted((r for r in results if r["score"] is not None), key=lambda r: r["score"])[:3]
for r in worst:
    print(f"  Score {r['score']}/10 : {r['input'][:60]}...")
    print(f"    Faiblesses : {r['faiblesses']}")

Points d’attention

Coût : chaque cas de test nécessite maintenant deux appels API (un pour la réponse, un pour la notation). Sur 100 cas, ça fait 200 appels. Utilisez Haiku pour la notation si le budget est serré :

# Variante économique : Haiku pour la notation
def grade_by_model_haiku(test_input, test_type, response):
    # Même code, avec model="claude-haiku-5-5"
    ...

Biais du notateur : un modèle qui évalue ses propres réponses est structurellement indulgent — il a produit ce texte parce qu’il le jugeait bon, et on lui demande ensuite de le juger. Deux parades fonctionnent : des critères assez précis pour ne laisser aucune marge d’appréciation, et l’exigence d’une justification avant le score, qui oblige à motiver plutôt qu’à valider par réflexe. Pour atténuer ce biais, soyez très précis dans les critères et demandez explicitement de chercher les faiblesses.

Reproductibilité : les modèles actuels n’acceptent plus de température réglable. Pour des résultats stables, répétez chaque notation (trois exécutions au minimum) et retenez la moyenne, en surveillant les cas où les notes divergent fortement.

Résumé

Le pipeline d’évaluation est maintenant complet :

Dataset → run_prompt() → Réponse Claude → grade_by_model() → Score + Analyse
                              ↓                                      ↓
                        Prompt à évaluer                    Critères d'évaluation

Vous pouvez désormais :

  1. Mesurer objectivement la qualité d’un prompt
  2. Comparer deux versions sur les mêmes données
  3. Identifier les faiblesses spécifiques (par type, par difficulté)
  4. Itérer avec confiance vers un prompt optimal

À vous de jouer

Votre notateur. Écrivez la grille de trois critères de votre assistant (leçon 16), branchez grade_by_model sur votre jeu de test et lancez l’évaluation. Lisez les trois cas les plus faibles et leurs faiblesses : les trouvez-vous justes ?

Contrôler le juge. Notez vous-même dix réponses, sans regarder les notes du modèle, puis comparez. Là où vous divergez de plus de deux points, précisez la grille et relancez.

Sources officielles : Model based grading (Claude Academy) · Define success criteria and build evaluations et Structured outputs (documentation Claude)

Points clés à retenir

  • Trois notateurs : code, modèle, humain ; le code partout où il suffit, le modèle pour ce qui demande du jugement.
  • Un notateur par modèle reçoit la requête, la réponse et des critères explicites, et rend forces, faiblesses, raisonnement et note.
  • Les sorties structurées garantissent un verdict exploitable ; une notation impossible se marque, elle ne s’invente pas.
  • Deux appels par cas : Haiku peut noter à moindre coût ; on répète les notations et on vérifie le juge contre un humain.