درس 4 — Tester, évaluer et exploiter

Évaluer sur 20 tickets et exploiter

6 دقائق قراءة

هذا المحتوى غير مترجم بعد. تُعرض النسخة الأصلية.

Évaluer sur 20 tickets et exploiter

Deux niveaux de vérification

pytest (sans clé)python evaluer.py (vrai modèle)
Question posée« le câblage ADK fonctionne-t-il ? »« le modèle trie-t-il correctement ? »
Contenutransfert simulé, état, validation, jeu de données, calcul des scores20 tickets, une session chacun
Résultatvert ou rougedes pourcentages et une liste d'erreurs
Quandà chaque modificationà chaque changement de prompt, de modèle ou de description

Le script d'évaluation

Chaque ticket est joué dans une nouvelle session : aucun ticket ne voit le précédent.

files/evaluer.py
def scores(resultats):
    """Fonction pure : exactitude du routage, de la priorité, et confusions."""
    n = len(resultats)
    return {
        "routage": sum(r["attendu"] == r["obtenu"] for r in resultats) / n,
        "priorite": sum(r["priorite_attendue"] == r["priorite"] for r in resultats) / n,
        "confusions": dict(Counter(f"{r['attendu']}->{r['obtenu']}" for r in resultats if r["attendu"] != r["obtenu"])),
    }

La fonction scores est pure : elle est testée sans modèle (test_scores). Les confusions (par exemple technique->facturation: 2) disent quelle frontière entre équipes pose problème, ce qu'un pourcentage seul ne dit pas.

flowchart LR
  J["tickets.json (20, étiquetés)"] --> L["boucle : 1 session par ticket"]
  L --> RU["InMemoryRunner + routeur"]
  RU --> ST[("state['triage']")]
  ST --> RES["resultats.json"]
  RES --> SC["scores() : routage, priorité, confusions"]
cd files
pytest -q            # 5 tests, sans clé
python evaluer.py    # 20 tickets, quelques dizaines d'appels de modèle

evaluer.py réessaie sur 429 et 503 et ferme proprement le runner à la fin (await runner.close()).

Pourquoi créer une nouvelle session pour chaque ticket ?

Résultat obtenu

Tests sans clé, 2026-10-15 :

test_agent.py::test_jeu_de_tickets_equilibre PASSED
test_agent.py::test_scores PASSED
test_agent.py::test_specialistes_ne_renvoient_pas_le_ticket PASSED
test_agent.py::test_transfert_puis_triage_en_etat PASSED
test_agent.py::test_priorite_invalide_refusee PASSED
============================== 5 passed in 1.31s ===============================

Évaluation réelle python evaluer.py (gemini-3.5-flash, 2026-10-15), sortie complète :

OK  T01 attendu=facturation obtenu=facturation prio P1/P1
OK  T02 attendu=facturation obtenu=facturation prio P3/P3
OK  T03 attendu=facturation obtenu=facturation prio P3/P3
OK  T04 attendu=facturation obtenu=facturation prio P2/P2
OK  T05 attendu=facturation obtenu=facturation prio P2/P2
OK  T06 attendu=facturation obtenu=facturation prio P2/P3
OK  T07 attendu=technique   obtenu=technique   prio P1/P1
OK  T08 attendu=technique   obtenu=technique   prio P2/P2
OK  T09 attendu=technique   obtenu=technique   prio P2/P2
OK  T10 attendu=technique   obtenu=technique   prio P3/P3
OK  T11 attendu=technique   obtenu=technique   prio P3/P3
OK  T12 attendu=technique   obtenu=technique   prio P2/P2
OK  T13 attendu=technique   obtenu=technique   prio P1/P1
OK  T14 attendu=compte      obtenu=compte      prio P2/P2
OK  T15 attendu=compte      obtenu=compte      prio P1/P1
OK  T16 attendu=compte      obtenu=compte      prio P3/P3
OK  T17 attendu=compte      obtenu=compte      prio P2/P2
OK  T18 attendu=compte      obtenu=compte      prio P3/P3
OK  T19 attendu=compte      obtenu=compte      prio P3/P3
OK  T20 attendu=facturation obtenu=facturation prio P2/P1

Routage : 100%   Priorité : 90%   Confusions : aucune

Analyser les erreurs

Le routage est parfait sur ce jeu, y compris le cas ambigu T20. Les deux écarts portent sur la priorité :

TicketAttenduObtenuLecture
T06 « facture impayée alors que j'ai fait le virement lundi »P2P3le modèle y a vu une simple attente de rapprochement bancaire ; défendable
T20 « abonnement expiré, plus d'accès à mes données »P2P1« plus d'accès » ressemble à « service inaccessible » de la définition de P1

Dans les deux cas, la définition de la priorité laisse de la place à l'interprétation. Avant de toucher au prompt, demandez-vous si l'étiquette est la bonne : parfois c'est le jeu de test qu'il faut corriger.

Le score de priorité passe de 90 % à 85 % après une modification du prompt. Que concluez-vous sur 20 tickets ?

Exploiter en production

SujetRecommandation
Entréewebhook de l'outil de support ou file Pub/Sub, vers un service (par exemple Cloud Run avec adk deploy cloud_run)
Sortieécrire équipe, priorité et brouillon dans le ticket ; l'humain valide avant envoi
Tickets hors catégoriesajouter un spécialiste « autre » ou une sortie « à trier manuellement » plutôt que forcer un choix
Suivijournaliser equipe, priorite et les corrections faites par les agents humains : ce sont vos futures étiquettes
Régressionrelancer evaluer.py en CI à chaque changement de prompt ou de modèle, seuil minimal de routage
Outils ADKadk eval et les jeux d'évaluation ADK pour versionner des cas de test d'agent

Exercice : un seuil en CI

Écrivez un test test_seuil_routage qui lit resultats.json (produit par evaluer.py) et échoue si le routage est inférieur à 90 % ou si une confusion apparaît plus de deux fois. Pourquoi ne pas le mettre dans pytest -q par défaut ?

Voir la solution
import json, os, pytest
from evaluer import scores

@pytest.mark.skipif(not os.path.exists("resultats.json"), reason="lancer evaluer.py d'abord")
def test_seuil_routage():
    s = scores(json.load(open("resultats.json", encoding="utf-8")))
    assert s["routage"] >= 0.9
    assert all(n <= 2 for n in s["confusions"].values())

Il dépend d'un fichier produit par un run avec clé, qui coûte des appels et varie d'une exécution à l'autre. On le lance dans un job CI dédié (manuel ou nocturne), pas à chaque commit. Le skipif garde pytest -q vert sans clé.

Je sais faire

  • Évaluer un routeur sur un jeu étiqueté, une session par ticket
  • Lire exactitude et confusions, et analyser chaque erreur
  • Remettre en cause une étiquette avant de modifier le prompt
  • Dire pourquoi 20 tickets ne suffisent pas pour décider
  • Brancher le triage dans un flux de production avec validation humaine