Aller au contenu principal

Notation par modele

Les trois types de notation

Pour evaluer les reponses de Claude, trois approches existent :

TypeDescriptionQuand l’utiliser
CodeVerification automatique par programmeReponses factuelles, formats stricts
ModeleUn autre appel AI evalue la reponseReponses ouvertes, qualite subjective
HumainUn evaluateur humain noteDerniere validation, cas critiques

La notation par code est la plus fiable quand elle s’applique (ex: “la reponse est-elle du JSON valide ?”). Mais pour des criteres comme “le code est-il bien commente ?” ou “l’explication est-elle claire ?”, on a besoin d’un notateur par modele.

Le principe

On utilise Claude lui-meme (ou un autre modele) comme evaluateur. On lui donne :

  • La requete originale
  • La reponse generee
  • Les criteres d’evaluation
  • L’instruction de produire un score et une justification

Definir les criteres d’evaluation

Pour notre assistant AWS, on definit trois criteres :

CRITERES_EVALUATION = """Evalue la reponse selon ces criteres :

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

2. VALIDITE (0-4 points) : Le code/config genere est-il syntaxiquement correct ?
   Fonctionnerait-il tel quel sans modification ?

3. ADEQUATION (0-3 points) : La reponse correspond-elle precisement a la demande ?
   Tous les elements demandes sont-ils presents ?

Score total sur 10."""

Implementer grade_by_model()

import anthropic
import json

client = anthropic.Anthropic()

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

REQUETE ORIGINALE :
{test_input}

TYPE ATTENDU : {test_type}

REPONSE A EVALUER :
{response}

{CRITERES_EVALUATION}

Analyse la reponse et fournis :
1. Les points forts (forces)
2. Les points faibles (faiblesses)
3. Ton raisonnement detaille
4. Un score final sur 10

Reponds en JSON."""

    grading_response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        temperature=0,
        messages=[
            {"role": "user", "content": grading_prompt},
            {"role": "assistant", "content": "```json\n"}
        ],
        stop_sequences=["```"]
    )

    try:
        evaluation = json.loads(grading_response.content[0].text.strip())
        return evaluation
    except json.JSONDecodeError:
        # Fallback si le JSON est invalide
        return {
            "forces": "Erreur d'evaluation",
            "faiblesses": "Le notateur n'a pas produit de JSON valide",
            "raisonnement": grading_response.content[0].text[:200],
            "score": 5  # Score neutre par defaut
        }

Integrer dans le pipeline

On remplace le score placeholder de run_test_case() :

def run_test_case(test_case, prompt_template):
    """
    Execute un cas de test avec notation par modele.
    """
    # Obtenir la reponse de Claude
    response = run_prompt(test_case, prompt_template)

    # Notation par modele (remplace le score=10 placeholder)
    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 specifie"),
        "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 integre tout :

import anthropic
import json
from collections import defaultdict

client = anthropic.Anthropic()

CRITERES_EVALUATION = """Evalue la reponse selon ces criteres :

1. FORMAT (0-3 points) : La sortie est-elle dans le format demande ?
2. VALIDITE (0-4 points) : Le code est-il syntaxiquement correct ?
3. ADEQUATION (0-3 points) : La reponse correspond-elle a la demande ?

Score total sur 10."""

PROMPT_TEMPLATE = """Tu es un expert AWS. Genere du code propre et fonctionnel.

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

Requete : {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-4-20250514",
        max_tokens=1024,
        temperature=0,
        messages=[{"role": "user", "content": prompt}]
    )
    return response.content[0].text


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

REQUETE : {test_input}
TYPE ATTENDU : {test_type}

REPONSE A EVALUER :
{response}

{CRITERES_EVALUATION}

Reponds en JSON avec : forces, faiblesses, raisonnement, score (entier 1-10)."""

    grading_response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1024,
        temperature=0,
        messages=[
            {"role": "user", "content": grading_prompt},
            {"role": "assistant", "content": "```json\n"}
        ],
        stop_sequences=["```"]
    )

    try:
        return json.loads(grading_response.content[0].text.strip())
    except json.JSONDecodeError:
        return {"forces": "", "faiblesses": "Erreur parsing", "raisonnement": "", "score": 5}


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")

    avg_score = sum(r["score"] for r in results) / len(results)

    print(f"\n{'='*50}")
    print(f"Score moyen : {avg_score:.2f}/10")

    return results, avg_score


# Lancer l'evaluation
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 resultats

# Score moyen par type
scores_par_type = defaultdict(list)
for r in results:
    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(results, 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

Cout : chaque cas de test necessite maintenant deux appels API (un pour la reponse, un pour la notation). Sur 100 cas, ca fait 200 appels. Utilisez Haiku pour la notation si le budget est serré :

# Variante economique : Haiku pour la notation
def grade_by_model_haiku(test_input, test_type, response):
    # Meme code mais avec model="claude-haiku-4-20250514"
    ...

Biais du notateur : un modele qui evalue ses propres reponses peut etre trop indulgent. Pour attenuer ce biais, soyez tres precis dans les criteres et demandez explicitement de chercher les faiblesses.

Reproductibilite : utilisez temperature=0 pour les deux appels (reponse et notation) afin d’obtenir des resultats stables.

Resume

Le pipeline d’evaluation est maintenant complet :

Dataset → run_prompt() → Reponse Claude → grade_by_model() → Score + Analyse
                              ↓                                      ↓
                        Prompt a evaluer                    Criteres d'evaluation

Vous pouvez desormais :

  1. Mesurer objectivement la qualite d’un prompt
  2. Comparer deux versions sur les memes donnees
  3. Identifier les faiblesses specifiques (par type, par difficulte)
  4. Iterer avec confiance vers un prompt optimal