Executer l'evaluation
Les 3 fonctions du pipeline
Le pipeline d’evaluation repose sur trois fonctions qui s’enchainent :
run_prompt(): fusionne un cas de test avec le template de prompt et appelle Clauderun_test_case(): execute le prompt puis note le resultatrun_eval(): orchestre l’evaluation sur tous les cas de test
Le prompt a evaluer
On reprend le contexte de la lecon precedente : un assistant qui genere du code AWS.
PROMPT_TEMPLATE = """Tu es un expert AWS. Genere du code propre et fonctionnel.
Contexte : {context}
Type de sortie attendu : {type}
Requete : {input}"""
Les champs entre accolades ({context}, {type}, {input}) seront remplaces par les donnees de chaque cas de test.
Fonction 1 : run_prompt()
Cette fonction prend un cas de test et le template de prompt, fusionne les deux, et envoie a Claude :
import anthropic
import json
client = anthropic.Anthropic()
def run_prompt(test_case, prompt_template):
"""
Fusionne le cas de test avec le template et appelle Claude.
Retourne la reponse brute de Claude.
"""
# Remplir le template avec les donnees du cas de test
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, # Deterministe pour la reproductibilite
messages=[{"role": "user", "content": prompt}]
)
return response.content[0].text
Note : temperature=0 est important pour l’evaluation. On veut des resultats reproductibles, pas de la creativite.
Fonction 2 : run_test_case()
Cette fonction execute le prompt et note le resultat. Pour l’instant, on met un score placeholder (on implementera la notation par modele dans la lecon suivante) :
def run_test_case(test_case, prompt_template):
"""
Execute un cas de test : appelle Claude puis note la reponse.
Retourne un dictionnaire avec l'input, la reponse et le score.
"""
# Obtenir la reponse de Claude
response = run_prompt(test_case, prompt_template)
# Notation placeholder (sera remplacee par notation par modele)
score = 10 # Score temporaire
return {
"input": test_case["input"],
"type": test_case["type"],
"context": test_case["context"],
"difficulty": test_case.get("difficulty", "non specifie"),
"response": response,
"score": score
}
Fonction 3 : run_eval()
La fonction principale qui orchestre l’ensemble :
def run_eval(dataset, prompt_template):
"""
Execute l'evaluation complete sur tout le dataset.
Retourne la liste des resultats et le score moyen.
"""
results = []
for i, test_case in enumerate(dataset):
print(f"Cas {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")
# Calcul du score moyen
avg_score = sum(r["score"] for r in results) / len(results)
print(f"\n{'='*50}")
print(f"Score moyen : {avg_score:.2f}/10")
print(f"Cas evalues : {len(results)}")
return results, avg_score
Charger le dataset et lancer
# Charger le dataset genere precedemment
with open("dataset.json", "r", encoding="utf-8") as f:
dataset = json.load(f)
print(f"Dataset charge : {len(dataset)} cas de test\n")
# Lancer l'evaluation
results, avg_score = run_eval(dataset, PROMPT_TEMPLATE)
Sortie typique :
Dataset charge : 30 cas de test
Cas 1/30 : Ecris un script qui cree un bucket S3 avec un nom ...
Score : 10/10
Cas 2/30 : Ecris une fonction Lambda qui declenche une notifi...
Score : 10/10
...
Cas 30/30 : Genere une regex qui valide un ARN AWS avec region...
Score : 10/10
==================================================
Score moyen : 10.00/10
Cas evalues : 30
Evidemment, avec le score placeholder a 10, tous les resultats sont parfaits. La prochaine lecon introduit la notation par modele pour obtenir des scores reels.
Examiner les resultats en detail
# Sauvegarder les resultats
with open("eval_results.json", "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
# Analyser par categorie
from collections import defaultdict
scores_par_type = defaultdict(list)
for r in results:
scores_par_type[r["type"]].append(r["score"])
print("\nScores par type :")
for type_code, scores in scores_par_type.items():
avg = sum(scores) / len(scores)
print(f" {type_code:10s} : {avg:.2f}/10 ({len(scores)} cas)")
# Analyser par difficulte
scores_par_diff = defaultdict(list)
for r in results:
scores_par_diff[r["difficulty"]].append(r["score"])
print("\nScores par difficulte :")
for diff, scores in scores_par_diff.items():
avg = sum(scores) / len(scores)
print(f" {diff:10s} : {avg:.2f}/10 ({len(scores)} cas)")
Comparer deux prompts
Le but final : evaluer deux versions du prompt sur le meme dataset :
PROMPT_V1 = """Tu es un expert AWS. Genere du code propre et fonctionnel.
Contexte : {context}
Type de sortie attendu : {type}
Requete : {input}"""
PROMPT_V2 = """Tu es un ingenieur cloud AWS senior. Genere du code de qualite production.
Regles :
- Code commente et lisible
- Gestion des erreurs incluse
- Respecte les bonnes pratiques AWS
Contexte : {context}
Type de sortie : {type}
Requete : {input}"""
# Evaluer les deux versions
print("=== Evaluation V1 ===")
results_v1, score_v1 = run_eval(dataset, PROMPT_V1)
print("\n=== Evaluation V2 ===")
results_v2, score_v2 = run_eval(dataset, PROMPT_V2)
print(f"\nComparaison :")
print(f" V1 : {score_v1:.2f}/10")
print(f" V2 : {score_v2:.2f}/10")
print(f" Delta : {score_v2 - score_v1:+.2f}")
Resume
Le pipeline est en place. Les trois fonctions se decomposent clairement :
| Fonction | Role | Entree | Sortie |
|---|---|---|---|
run_prompt() | Appeler Claude | cas + template | texte brut |
run_test_case() | Executer + noter | cas + template | resultat + score |
run_eval() | Orchestrer | dataset + template | resultats + moyenne |
Il ne manque qu’une piece : remplacer le score placeholder par une notation reelle. C’est l’objet de la prochaine lecon.