Le format d'évaluation d'ADK et la commande adk eval
Le format EvalSet
Un fichier d'évaluation ADK est du JSON validé par les modèles Pydantic de google.adk.evaluation (EvalSet, EvalCase, Invocation). Les clés acceptent le snake_case ou le camelCase.
flowchart TB ES["EvalSet : eval_set_id, eval_cases"] --> EC["EvalCase : eval_id"] EC --> CV["conversation : liste d'Invocation"] EC --> SI["session_input : app_name, user_id, state"] CV --> UC["user_content : message du client"] CV --> FR["final_response : réponse de référence"] CV --> ID["intermediate_data.tool_uses : trajectoire attendue"]
{
"eval_set_id": "banque_atelier_support",
"eval_cases": [{
"eval_id": "solde",
"conversation": [{
"invocation_id": "inv-solde",
"user_content": {"role": "user", "parts": [{"text": "Bonjour, quel est le solde de mon compte ?"}]},
"final_response": {"role": "model", "parts": [{"text": "Le solde de votre compte est de 1 250,40 €."}]},
"intermediate_data": {"tool_uses": [{"name": "my_balance", "args": {}}]}
}],
"session_input": {"app_name": "files", "user_id": "u", "state": {"customer_id": "C-1001"}}
}]
}session_input.state injecte l'identité du client avant le premier tour : c'est indispensable pour un agent dont les outils lisent tool_context.state.
Les critères : test_config.json
{
"criteria": {
"tool_trajectory_avg_score": {"threshold": 1.0, "match_type": "IN_ORDER"},
"response_match_score": 0.3
}
}| Critère | Ce qu'il mesure | Appel au modèle ? |
|---|---|---|
tool_trajectory_avg_score | trajectoire réelle contre tool_uses (modes EXACT, IN_ORDER, ANY_ORDER) | non |
response_match_score | recouvrement de mots (ROUGE-1) avec final_response | non |
final_response_match_v2, critères à grille (rubric) | jugement par un LLM | oui |
Lancer adk eval
adk eval attend un dossier d'agent contenant un __init__.py qui expose root_agent. Le dossier files/ du lab en contient un minimal :
# Point d'entrée de `adk eval .` : ADK importe ce fichier et y cherche `root_agent`.
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from bank_agent import root_agent # noqa: E402,F401cd files
adk eval . evalset.json:solde,carte_volee --config_file_path test_config.jsonLa syntaxe fichier:cas1,cas2 ne lance que les cas cités.
Résultat obtenu
Exécution réelle le 2026-10-15 (4 appels au modèle) :
Using evaluation criteria: criteria={'tool_trajectory_avg_score': BaseCriterion(threshold=1.0, include_intermediate_responses_in_final=False, match_type='IN_ORDER'), 'response_match_score': 0.3} …
*********************************************************************
Eval Run Summary
banque_atelier_support:
Tests passed: 2
Tests failed: 0Le fichier de résultats écrit dans files/.adk/eval_history/ donnait, par cas :
carte_volee tool_trajectory_avg_score=1.0 response_match_score=0.545 inference_call_count_v1=2 token_usage_v1=1247
solde tool_trajectory_avg_score=1.0 response_match_score=0.952 inference_call_count_v1=2 token_usage_v1=1254La limite des arguments
Pourquoi n'avoir lancé que solde et carte_volee ? Dans ADK 2.10, TrajectoryEvaluator compare les arguments par égalité stricte du dictionnaire complet (actual.args == expected.args), sauf si l'on passe ignore_args: true. Pour virement_loyer, l'attendu {"amount": 650} ne sera jamais égal au réel {"to_iban": "FR76…", "amount": 650}.
| Besoin | adk eval (2.10) | Harnais du lab |
|---|---|---|
| Arguments sans importance | ignore_args: true | ignore_args=True |
| Tous les arguments exacts | oui (par défaut) | oui, en les listant tous |
| Seulement certains arguments | non (critère personnalisé à écrire) | oui (_same ne compare que les clés attendues) |
| Outils interdits | non natif | forbidden_called |
Avec adk eval 2.10, l'attendu est transfer_funds avec args {"amount": 650} et l'agent appelle transfer_funds(to_iban="FR76…", amount=650). Résultat ?
Le code de TrajectoryEvaluator compare actual.args == expected.args. Il faut soit lister tous les arguments (et leur forme exacte), soit ignore_args, soit un critère personnalisé, comme le fait notre harnais.
Lequel de ces critères consomme des appels au modèle en plus de l'agent évalué ?
Les deux premiers sont calculés en Python (comparaison de trajectoire, ROUGE). final_response_match_v2 utilise un LLM comme juge, donc des appels et un coût supplémentaires.
Exercice : valider sans clé
Écrivez un test pytest qui échoue si evalset.json ou test_config.json ne respectent plus le format d'ADK.
Voir la solution
import json
from google.adk.evaluation.eval_config import EvalConfig
from harness import load_evalset
def test_fichiers_adk_valides():
es = load_evalset() # EvalSet.model_validate_json
assert all(c.session_input.state.get("customer_id") for c in es.eval_cases)
EvalConfig.model_validate(json.load(open("test_config.json")))Le lab contient déjà test_evalset_au_format_adk. Comme EvalCase refuse les champs inconnus (extra="forbid"), une faute de frappe dans une clé fait échouer le test immédiatement, sans appel au modèle.
Je sais faire
- Écrire un
EvalSetavec trajectoire attendue et état de session - Configurer des critères dans
test_config.json - Lancer
adk evalsur une sélection de cas - Expliquer la limite de comparaison des arguments et la contourner