Aller au contenu principal

Exercice : evaluations de prompts

Objectif

Construire un pipeline d’evaluation complet pour un prompt de resume de CV. Vous allez :

  1. Ecrire un prompt initial
  2. Creer un jeu de donnees de test
  3. Implementer les fonctions d’evaluation
  4. Ajouter un notateur par modele
  5. Mesurer un score de reference
  6. Ameliorer le prompt et comparer les scores

Contexte

Vous developpez un outil qui prend un CV professionnel et produit un resume en 3 points cles. L’outil doit extraire les informations les plus importantes de maniere concise et precise.


Etape 1 : Le prompt initial (naif)

Commencez avec un prompt simple, sans optimisation :

PROMPT_V1 = "Resume ce CV en 3 points."

Etape 2 : Creer le jeu de donnees de test

Creez 3 CV fictifs couvrant des profils differents. Vous pouvez les ecrire vous-meme ou demander a Claude de les generer.

test_cases = [
    {
        "input": """CV - Sophie Martin
Poste actuel : Directrice Marketing chez TechVision (2020-present)
Experience precedente : Responsable Communication chez DataCorp (2015-2020)
Formation : Master Marketing Digital, HEC Paris (2014)
Competences : SEO/SEA, gestion d'equipe (12 personnes), budget 2M EUR/an
Realisations : +45% de leads qualifies en 2 ans, lancement de 3 produits SaaS
Langues : Francais (natif), Anglais (C1), Espagnol (B2)""",

        "expected_focus": "Marketing digital, management, resultats chiffres"
    },
    {
        "input": """CV - Ahmed Benali
Poste actuel : Developpeur Full-Stack Senior chez CloudNine (2021-present)
Experience precedente : Developpeur Backend chez StartupFlow (2018-2021), Stage chez IBM (2017)
Formation : Ingenieur Informatique, INSA Lyon (2018)
Competences : Python, TypeScript, React, AWS, Docker, Kubernetes, PostgreSQL
Realisations : Architecture microservices servant 2M utilisateurs, reduction latence de 60%
Certifications : AWS Solutions Architect, Kubernetes CKA
Langues : Francais (natif), Anglais (C1), Arabe (B1)""",

        "expected_focus": "Full-stack senior, cloud/DevOps, performance a grande echelle"
    },
    {
        "input": """CV - Marie Dupont
Poste actuel : Recherche d'emploi (disponible immediatement)
Derniere experience : Assistante de direction chez Groupe Renard (2019-2024)
Experiences precedentes : Assistante administrative chez PME Martin (2016-2019), Hotesse d'accueil chez EventPro (2014-2016)
Formation : BTS Assistant de Manager, Lycee Condorcet (2014)
Competences : Pack Office avance, gestion d'agenda, organisation d'evenements, redaction de comptes-rendus
Realisations : Organisation de 50+ evenements corporate, gestion simultanee de 3 directeurs
Langues : Francais (natif), Anglais (B1)""",

        "expected_focus": "Assistanat de direction, organisation, polyvalence"
    }
]

Etape 3 : Implementer le pipeline d’evaluation

import anthropic

client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-20250514"

def run_prompt(cv_text: str, prompt_template: str) -> str:
    """Envoie le CV a Claude avec le prompt donne et retourne la reponse."""
    full_prompt = f"{prompt_template}\n\n{cv_text}"
    response = client.messages.create(
        model=MODEL,
        max_tokens=512,
        messages=[{"role": "user", "content": full_prompt}]
    )
    return response.content[0].text

def run_test_case(test_case: dict, prompt_template: str) -> dict:
    """Execute un cas de test et retourne la reponse avec les metadonnees."""
    output = run_prompt(test_case["input"], prompt_template)
    return {
        "input": test_case["input"],
        "expected_focus": test_case["expected_focus"],
        "output": output
    }

Etape 4 : Le notateur par modele

Le notateur evalue chaque resume sur trois criteres : exactitude, concision et couverture des informations cles.

def model_grader(result: dict) -> dict:
    """Note le resume sur 3 criteres via un appel a Claude."""
    grading_prompt = f"""Tu es un evaluateur de resumes de CV. Evalue le resume suivant sur 3 criteres, chacun note de 0 a 10.

CV original :
{result['input']}

Resume produit :
{result['output']}

Informations cles attendues : {result['expected_focus']}

Criteres :
1. **Exactitude** : Les informations du resume sont-elles correctes et fideles au CV ?
2. **Concision** : Le resume est-il court et va-t-il a l'essentiel (3 points, pas de superflu) ?
3. **Couverture** : Les informations les plus importantes sont-elles presentes (poste, competences cles, realisations) ?

Reponds UNIQUEMENT au format JSON :
{{"exactitude": X, "concision": X, "couverture": X}}"""

    response = client.messages.create(
        model=MODEL,
        max_tokens=100,
        messages=[{"role": "user", "content": grading_prompt}]
    )

    import json
    try:
        scores = json.loads(response.content[0].text)
        scores["moyenne"] = round(
            (scores["exactitude"] + scores["concision"] + scores["couverture"]) / 3, 2
        )
        return scores
    except (json.JSONDecodeError, KeyError):
        return {"exactitude": 0, "concision": 0, "couverture": 0, "moyenne": 0}

Etape 5 : La fonction d’evaluation complete

def run_eval(test_cases: list, prompt_template: str) -> dict:
    """Execute l'evaluation complete et retourne les resultats."""
    print(f"\n{'='*60}")
    print(f"EVALUATION DU PROMPT")
    print(f"{'='*60}")
    print(f"Prompt : {prompt_template[:80]}...")
    print(f"Nombre de cas de test : {len(test_cases)}\n")

    all_scores = []

    for i, case in enumerate(test_cases):
        print(f"--- Cas de test {i+1} ---")
        result = run_test_case(case, prompt_template)
        scores = model_grader(result)

        print(f"Resume produit :\n{result['output']}\n")
        print(f"Scores : Exactitude={scores['exactitude']}, "
              f"Concision={scores['concision']}, "
              f"Couverture={scores['couverture']}, "
              f"Moyenne={scores['moyenne']}")
        print()

        all_scores.append(scores)

    # Score global
    avg_exactitude = round(sum(s["exactitude"] for s in all_scores) / len(all_scores), 2)
    avg_concision = round(sum(s["concision"] for s in all_scores) / len(all_scores), 2)
    avg_couverture = round(sum(s["couverture"] for s in all_scores) / len(all_scores), 2)
    avg_global = round(sum(s["moyenne"] for s in all_scores) / len(all_scores), 2)

    print(f"{'='*60}")
    print(f"SCORES GLOBAUX")
    print(f"  Exactitude : {avg_exactitude}/10")
    print(f"  Concision  : {avg_concision}/10")
    print(f"  Couverture : {avg_couverture}/10")
    print(f"  MOYENNE    : {avg_global}/10")
    print(f"{'='*60}")

    return {
        "detail": all_scores,
        "average": avg_global,
        "by_criteria": {
            "exactitude": avg_exactitude,
            "concision": avg_concision,
            "couverture": avg_couverture
        }
    }

Etape 6 : Executer, ameliorer, comparer

Baseline (prompt naif)

PROMPT_V1 = "Resume ce CV en 3 points."
results_v1 = run_eval(test_cases, PROMPT_V1)

Prompt ameliore

PROMPT_V2 = """Tu es un expert en recrutement. Analyse le CV ci-dessous et produis un resume en exactement 3 points cles.

Regles :
- Chaque point commence par un tiret (-)
- Point 1 : Poste actuel et niveau d'experience (junior/confirme/senior)
- Point 2 : Competences techniques ou metier les plus differenciantes
- Point 3 : Realisation chiffree la plus marquante
- Maximum 25 mots par point
- Pas d'introduction ni de conclusion, uniquement les 3 points"""

results_v2 = run_eval(test_cases, PROMPT_V2)

Comparaison des resultats

print(f"\n{'='*60}")
print(f"COMPARAISON")
print(f"{'='*60}")
print(f"Prompt V1 (naif)    : {results_v1['average']}/10")
print(f"Prompt V2 (ameliore): {results_v2['average']}/10")
print(f"Amelioration        : {results_v2['average'] - results_v1['average']:+.2f} points")

Solution complete

Voici le code complet en un seul fichier :

"""
Pipeline d'evaluation complet pour un resume de CV.
Necessite : pip install anthropic
Variable d'environnement : ANTHROPIC_API_KEY
"""

import anthropic
import json

client = anthropic.Anthropic()
MODEL = "claude-sonnet-4-20250514"

# --- Jeu de donnees ---
test_cases = [
    {
        "input": """CV - Sophie Martin
Poste actuel : Directrice Marketing chez TechVision (2020-present)
Experience precedente : Responsable Communication chez DataCorp (2015-2020)
Formation : Master Marketing Digital, HEC Paris (2014)
Competences : SEO/SEA, gestion d'equipe (12 personnes), budget 2M EUR/an
Realisations : +45% de leads qualifies en 2 ans, lancement de 3 produits SaaS
Langues : Francais (natif), Anglais (C1), Espagnol (B2)""",
        "expected_focus": "Marketing digital, management, resultats chiffres"
    },
    {
        "input": """CV - Ahmed Benali
Poste actuel : Developpeur Full-Stack Senior chez CloudNine (2021-present)
Experience precedente : Developpeur Backend chez StartupFlow (2018-2021), Stage chez IBM (2017)
Formation : Ingenieur Informatique, INSA Lyon (2018)
Competences : Python, TypeScript, React, AWS, Docker, Kubernetes, PostgreSQL
Realisations : Architecture microservices servant 2M utilisateurs, reduction latence de 60%
Certifications : AWS Solutions Architect, Kubernetes CKA
Langues : Francais (natif), Anglais (C1), Arabe (B1)""",
        "expected_focus": "Full-stack senior, cloud/DevOps, performance a grande echelle"
    },
    {
        "input": """CV - Marie Dupont
Poste actuel : Recherche d'emploi (disponible immediatement)
Derniere experience : Assistante de direction chez Groupe Renard (2019-2024)
Experiences precedentes : Assistante administrative chez PME Martin (2016-2019), Hotesse d'accueil chez EventPro (2014-2016)
Formation : BTS Assistant de Manager, Lycee Condorcet (2014)
Competences : Pack Office avance, gestion d'agenda, organisation d'evenements, redaction de comptes-rendus
Realisations : Organisation de 50+ evenements corporate, gestion simultanee de 3 directeurs
Langues : Francais (natif), Anglais (B1)""",
        "expected_focus": "Assistanat de direction, organisation, polyvalence"
    }
]

# --- Fonctions du pipeline ---
def run_prompt(cv_text: str, prompt_template: str) -> str:
    full_prompt = f"{prompt_template}\n\n{cv_text}"
    response = client.messages.create(
        model=MODEL,
        max_tokens=512,
        messages=[{"role": "user", "content": full_prompt}]
    )
    return response.content[0].text

def run_test_case(test_case: dict, prompt_template: str) -> dict:
    output = run_prompt(test_case["input"], prompt_template)
    return {
        "input": test_case["input"],
        "expected_focus": test_case["expected_focus"],
        "output": output
    }

def model_grader(result: dict) -> dict:
    grading_prompt = f"""Tu es un evaluateur de resumes de CV. Evalue le resume suivant sur 3 criteres, chacun note de 0 a 10.

CV original :
{result['input']}

Resume produit :
{result['output']}

Informations cles attendues : {result['expected_focus']}

Criteres :
1. Exactitude : Les informations sont-elles correctes et fideles au CV ?
2. Concision : Le resume va-t-il a l'essentiel (3 points, pas de superflu) ?
3. Couverture : Les informations les plus importantes sont-elles presentes ?

Reponds UNIQUEMENT au format JSON :
{{"exactitude": X, "concision": X, "couverture": X}}"""

    response = client.messages.create(
        model=MODEL,
        max_tokens=100,
        messages=[{"role": "user", "content": grading_prompt}]
    )
    try:
        scores = json.loads(response.content[0].text)
        scores["moyenne"] = round(
            (scores["exactitude"] + scores["concision"] + scores["couverture"]) / 3, 2
        )
        return scores
    except (json.JSONDecodeError, KeyError):
        return {"exactitude": 0, "concision": 0, "couverture": 0, "moyenne": 0}

def run_eval(test_cases: list, prompt_template: str) -> dict:
    print(f"\n{'='*60}")
    print(f"EVALUATION")
    print(f"{'='*60}\n")

    all_scores = []
    for i, case in enumerate(test_cases):
        result = run_test_case(case, prompt_template)
        scores = model_grader(result)
        print(f"Cas {i+1} — Moyenne: {scores['moyenne']}/10")
        print(f"  Resume: {result['output'][:120]}...\n")
        all_scores.append(scores)

    avg = round(sum(s["moyenne"] for s in all_scores) / len(all_scores), 2)
    print(f"SCORE MOYEN : {avg}/10\n")
    return {"detail": all_scores, "average": avg}

# --- Execution ---
PROMPT_V1 = "Resume ce CV en 3 points."

PROMPT_V2 = """Tu es un expert en recrutement. Analyse le CV ci-dessous et produis un resume en exactement 3 points cles.

Regles :
- Chaque point commence par un tiret (-)
- Point 1 : Poste actuel et niveau d'experience
- Point 2 : Competences les plus differenciantes
- Point 3 : Realisation chiffree la plus marquante
- Maximum 25 mots par point
- Pas d'introduction ni de conclusion"""

print("=== PROMPT V1 (naif) ===")
r1 = run_eval(test_cases, PROMPT_V1)

print("=== PROMPT V2 (ameliore) ===")
r2 = run_eval(test_cases, PROMPT_V2)

print(f"\nCOMPARAISON : V1={r1['average']}/10 → V2={r2['average']}/10 ({r2['average']-r1['average']:+.2f})")

Ce que vous apprenez

  • Structurer un pipeline d’evaluation avec des fonctions reutilisables
  • Definir des criteres de notation adaptes a votre cas d’usage
  • Mesurer l’impact d’un changement de prompt avec des metriques chiffrees
  • Iterer sur un prompt de maniere methodique plutot qu’au feeling