Aller au contenu principal

Trace grading : noter les exécutions

Trace grading : noter les exécutions

Le tracing enregistre ce que fait votre agent. Le trace grading va plus loin : il note automatiquement chaque exécution pour identifier les problèmes, mesurer la qualité dans le temps, et alimenter une boucle d’amélioration continue.

Le concept de trace grading

Chaque trace produite par votre agent contient l’intégralité de l’exécution : le message de l’utilisateur, les appels de tools, les réponses intermédiaires, et la réponse finale. Le trace grading analyse ces traces et attribue des scores selon vos critères.

Grading automatique avec des règles

Le niveau le plus simple : des règles programmatiques qui notent chaque trace :

from dataclasses import dataclass

@dataclass
class GradeTrace:
    trace_id: str
    score: float  # 0.0 à 1.0
    details: dict
    alertes: list[str]

def noter_trace(trace_data: dict) -> GradeTrace:
    """Note une trace selon des règles métier."""
    score = 1.0
    alertes = []
    details = {}

    # Règle 1 : Latence
    duree = trace_data.get("duration_ms", 0)
    if duree > 10000:
        score -= 0.2
        alertes.append(f"Latence élevée : {duree}ms")
    details["latence_ms"] = duree

    # Règle 2 : Nombre de tours
    nb_tours = trace_data.get("num_turns", 0)
    if nb_tours > 15:
        score -= 0.3
        alertes.append(f"Trop de tours : {nb_tours}")
    details["nb_tours"] = nb_tours

    # Règle 3 : Erreurs de tools
    tool_errors = trace_data.get("tool_errors", 0)
    if tool_errors > 0:
        score -= 0.1 * tool_errors
        alertes.append(f"{tool_errors} erreur(s) de tool")
    details["erreurs_tools"] = tool_errors

    # Règle 4 : Guardrails déclenchés
    guardrails = trace_data.get("guardrails_triggered", 0)
    if guardrails > 0:
        alertes.append(f"{guardrails} guardrail(s) déclenché(s)")
    details["guardrails"] = guardrails

    # Règle 5 : Réponse vide
    reponse = trace_data.get("final_output", "")
    if not reponse or len(reponse) < 10:
        score -= 0.5
        alertes.append("Réponse vide ou trop courte")
    details["longueur_reponse"] = len(reponse)

    return GradeTrace(
        trace_id=trace_data["trace_id"],
        score=max(0.0, score),
        details=details,
        alertes=alertes,
    )

Grading par LLM

Pour une évaluation plus fine, utilisez un modèle pour noter les traces :

from pydantic import BaseModel
from agents import Agent, Runner

class GradeLLM(BaseModel):
    score_pertinence: int  # 1-5
    score_exactitude: int  # 1-5
    score_ton: int  # 1-5
    problemes: list[str]
    suggestions: list[str]

agent_grader = Agent(
    name="Grader de traces",
    instructions="""Vous notez les exécutions d'un agent commercial.

    Analysez la trace complète (question, tools appelés, réponse) et notez :
    - Pertinence (1-5) : la réponse répond-elle à la question ?
    - Exactitude (1-5) : les informations sont-elles correctes ?
    - Ton (1-5) : le ton est-il professionnel et adapté ?

    Identifiez les problèmes et proposez des améliorations.""",
    model="gpt-5.4",
    output_type=GradeLLM,
)

async def noter_trace_llm(trace: dict) -> GradeLLM:
    prompt = f"""Trace d'exécution à noter :

    Question utilisateur : {trace['user_message']}
    Tools appelés : {trace['tools_called']}
    Réponse finale : {trace['final_output']}
    Durée : {trace['duration_ms']}ms
    Nombre de tours : {trace['num_turns']}"""

    result = await Runner.run(agent_grader, prompt)
    return result.final_output

Pipeline de grading en production

Mettez en place un pipeline qui note automatiquement les traces :

import asyncio
from collections import defaultdict

class PipelineGrading:
    def __init__(self):
        self.scores = defaultdict(list)
        self.alertes_critiques = []

    async def noter_batch(self, traces: list[dict]):
        """Note un batch de traces."""
        for trace in traces:
            # Grading programmatique (rapide)
            grade_regles = noter_trace(trace)

            # Grading LLM pour les cas douteux
            if grade_regles.score < 0.7:
                grade_llm = await noter_trace_llm(trace)
                score_final = (
                    grade_llm.score_pertinence +
                    grade_llm.score_exactitude +
                    grade_llm.score_ton
                ) / 15.0  # Normaliser sur 0-1
            else:
                score_final = grade_regles.score

            self.scores["global"].append(score_final)

            # Alertes
            if score_final < 0.5:
                self.alertes_critiques.append({
                    "trace_id": trace["trace_id"],
                    "score": score_final,
                    "alertes": grade_regles.alertes,
                })

    def rapport(self):
        """Génère un rapport de qualité."""
        scores = self.scores["global"]
        if not scores:
            return "Aucune trace évaluée."

        import statistics
        return {
            "nb_traces": len(scores),
            "score_moyen": statistics.mean(scores),
            "score_median": statistics.median(scores),
            "score_min": min(scores),
            "pct_critiques": len(self.alertes_critiques) / len(scores),
            "alertes_critiques": self.alertes_critiques[:10],
        }

# Utilisation
pipeline = PipelineGrading()
await pipeline.noter_batch(traces_du_jour)
print(pipeline.rapport())

Tableau de bord de qualité

Suivez l’évolution de la qualité dans le temps :

import json
from datetime import datetime

def enregistrer_metriques(rapport: dict, fichier: str = "metriques_agent.jsonl"):
    """Enregistre les métriques quotidiennes."""
    entree = {
        "date": datetime.now().isoformat(),
        **rapport,
    }
    with open(fichier, "a") as f:
        f.write(json.dumps(entree) + "\n")

def detecter_regression(metriques_recentes: list[dict], seuil: float = 0.1):
    """Détecte une baisse significative de qualité."""
    if len(metriques_recentes) < 2:
        return False
    dernier = metriques_recentes[-1]["score_moyen"]
    precedent = metriques_recentes[-2]["score_moyen"]
    baisse = precedent - dernier
    if baisse > seuil:
        print(f"REGRESSION DETECTEE : score passé de {precedent:.2f} à {dernier:.2f}")
        return True
    return False

Points clés à retenir

  • Le trace grading note automatiquement chaque exécution de votre agent
  • Les règles programmatiques (latence, erreurs, longueur) sont rapides et fiables
  • Le grading LLM évalue la pertinence, l’exactitude et le ton
  • Combinez les deux : règles rapides pour le tri, LLM pour les cas douteux
  • Suivez les scores dans le temps pour détecter les régressions
  • Les alertes critiques (score < 0.5) doivent déclencher une investigation