Lección 3 — Construire l'agent pas à pas

Priorité, réponse proposée et état

5 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

Priorité, réponse proposée et état

Un résultat structuré, pas une phrase

Le triage doit être exploitable par du code : quelle équipe, quelle priorité, quel brouillon. Plutôt que d'analyser le texte final du modèle, on lui fait appeler un outil dont les paramètres sont exactement les champs voulus.

files/agent.py
def enregistrer_triage(priorite: str, resume: str, reponse_proposee: str, tool_context: ToolContext) -> dict:
    """Enregistre le triage du ticket. À appeler UNE fois, en dernier.

    Args:
        priorite: "P1", "P2" ou "P3".
        resume: le problème en une phrase.
        reponse_proposee: brouillon de réponse au client (3 phrases max), relu par un humain avant envoi.
    """
    if priorite not in PRIORITES:
        return {"erreur": f"Priorité invalide : {priorite}. Valeurs : P1, P2, P3."}
    tool_context.state["triage"] = {"equipe": tool_context.agent_name, "priorite": priorite,
                                    "resume": resume, "reponse_proposee": reponse_proposee}
    tool_context.actions.skip_summarization = True  # fin du tour : pas d'appel modèle de plus
    return tool_context.state["triage"]
LigneRôle
if priorite not in PRIORITESrefuse « urgent » ou « haute » ; le modèle lit l'erreur et réessaie avec P1/P2/P3
"equipe": tool_context.agent_namel'équipe est le nom de l'agent qui appelle l'outil : le modèle ne peut pas se tromper ni mentir sur ce champ
tool_context.actions.skip_summarization = TrueADK considère le résultat de l'outil comme réponse finale : pas de nouvel appel au modèle pour « résumer »
flowchart LR
  M["Modèle du spécialiste"] -->|"enregistrer_triage(P2, ...)"| V{"priorité valide ?"}
  V -->|"non"| ER["{'erreur': ...} renvoyé au modèle"]
  ER --> M
  V -->|"oui"| ST[("state['triage'] : equipe = agent_name")]
  ST --> SK["skip_summarization : fin du tour"]

Pourquoi l'équipe est-elle tirée de tool_context.agent_name plutôt que d'un paramètre de l'outil ?

Les outils métier

Un spécialiste qui vérifie avant d'écrire fait de meilleurs brouillons. Deux outils simples, sur des données fictives :

files/agent.py
FACTURES = {"F-2026-0412": {"montant": 49.0, "statut": "payée", "prelevements": 2},
            "F-2026-0398": {"montant": 19.0, "statut": "impayée", "prelevements": 0}}
SERVICES = {"application web": "opérationnel", "api": "dégradé (latence élevée depuis 9 h)",
            "synchronisation mobile": "opérationnel", "export pdf": "en panne"}


def consulter_facture(numero: str) -> dict:
    """Retrouve une facture (montant, statut, nombre de prélèvements).

    Args:
        numero: par exemple "F-2026-0412".
    """
    return FACTURES.get(numero.upper(), {"erreur": "Facture introuvable."})
SpécialisteOutil métierCe qu'il permet de confirmer
facturationconsulter_facturele double prélèvement est réel (prelevements: 2)
techniqueetat_des_servicesl'export PDF est bien en panne : on peut l'écrire au client
compteaucundans la réalité : statut de la double authentification, dernières connexions

Le modèle appelle enregistrer_triage(priorite="urgent", ...). Que se passe-t-il ?

Lire le résultat

Après le run, le code lit simplement l'état de la session :

files/evaluer.py
s = await runner.session_service.get_session(app_name="triage", user_id="support", session_id=s.id)
triage = s.state.get("triage") or {"equipe": "aucune", "priorite": "-", "reponse_proposee": ""}

Si le routeur avait répondu lui-même sans transférer, triage serait absent : l'évaluation le compterait comme une erreur (obtenu = aucune).

Exercice : un champ « délai de réponse »

Ajoutez au triage un champ delai_cible calculé par le code à partir de la priorité (P1 : 1 h, P2 : 8 h, P3 : 48 h). Le modèle doit-il le fournir ?

Voir la solution

Non : c'est une règle déterministe, le code la connaît. On ne l'ajoute pas aux paramètres de l'outil.

DELAIS = {"P1": "1 h", "P2": "8 h", "P3": "48 h"}

def enregistrer_triage(priorite, resume, reponse_proposee, tool_context):
    if priorite not in PRIORITES:
        return {"erreur": ...}
    tool_context.state["triage"] = {"equipe": tool_context.agent_name, "priorite": priorite,
                                    "delai_cible": DELAIS[priorite],
                                    "resume": resume, "reponse_proposee": reponse_proposee}
    ...

Même principe que pour equipe : ne demandez au modèle que ce qu'il est seul à pouvoir juger (la priorité, le résumé, le brouillon).

Résultat obtenu

Trois triages réels issus de l'évaluation (gemini-3.5-flash, 2026-10-15) :

T01 facturation P1  Bonjour, nous avons bien constaté qu'un double prélèvement a eu lieu pour la
                    facture F-2026-0412. Notre équipe financière prend en charge votre dossier afin
                    de procéder à la régularisation. [...]
T06 facturation P3  Bonjour. Les virements bancaires peuvent nécessiter quelques jours ouvrés avant
                    d'être traités [...]. La facture F-2026-0398 figure actuellement comme impayée [...]
T15 compte      P1  Nous avons bien pris en compte votre signalement concernant l'accès non autorisé
                    à votre compte [...] transmis en priorité absolue à notre équipe de sécurité [...]

Dans T01, « nous avons bien constaté » s'appuie sur consulter_facture (prelevements: 2). T06 a été classé P3 alors que l'étiquette attendue est P2 : on y revient dans l'analyse d'erreurs.

Je sais faire

  • Obtenir un résultat structuré en faisant appeler un outil au modèle
  • Valider les valeurs d'un outil et laisser le modèle se corriger
  • Remplir un champ depuis tool_context plutôt que depuis le modèle
  • Terminer un tour avec skip_summarization
Siguiente