Créer un planificateur de circuits de Formule E multi-agents avec ADK et A2A

1. 1. Présentation

Dans cet atelier de programmation, vous allez créer un système multi-agent qui conçoit un circuit urbain de Formule E et le fait approuver par rapport à un ensemble simplifié de règles de la FIA.

Vous allez utiliser agents-cli pour générer deux projets Agent Development Kit (ADK) standards, puis écrire les parties importantes : une compétence d'agent, trois agents et une évaluation. Tout le reste (mise en page du projet, serveur, dépendances, configuration du déploiement) provient de la CLI.

Vous découvrez la Formule E ?

Vous n'avez pas besoin de vous y connaître en sport automobile. Ces cinq termes suffisent :

  • Formule E : le championnat de course automobile 100 % électrique de la FIA. La plupart des courses se déroulent sur des circuits urbains temporaires dans les centres-villes.
  • Homologation : approbation officielle de la FIA attestant qu'un circuit est sûr pour la course. Pas d'homologation, pas de course.
  • Freinage régénératif : lorsque la voiture freine, ses moteurs rechargent la batterie. Un freinage plus brusque permet de récupérer plus d'énergie. Les règles de cet atelier de programmation exigent donc au moins une zone de freinage brusque.
  • Calques CAO : calques nommés dans les dessins de conception du circuit, tels que la trajectoire idéale, la voie des stands et les barrières. La FIA vérifie que les éléments obligatoires sont présents.
  • Mode Attaque : les pilotes bénéficient d'un boost de puissance temporaire en traversant une zone d'activation située en dehors de la trajectoire de course.

Vos agents jouent sur les deux tableaux : l'un conçoit le circuit, et deux autres le vérifient pour la FIA.

Objectifs de l'atelier

Architecture : dans le projet track-planner, qui s'exécute dans Cloud Shell, l'agent track_planner utilise get_track_topology, fia_assessor et remote_gatekeeper. remote_gatekeeper appelle l'agent gatekeeper via A2A. Dans le projet gatekeeper, qui s'exécute sur Agent Runtime, l'agent gatekeeper utilise verify_infrastructure_checklist.

Les cases vertes représentent les agents et les cases en pointillés leurs outils, de la même manière que le playground ADK les dessine. Deux des outils encapsulent d'autres agents : fia_assessor est l'agent FIA Assessor doté de la compétence de conformité FIA, et remote_gatekeeper appelle l'agent Gatekeeper dans l'autre projet via A2A.

Agent

Projet

Job

thinking_level

Architecte principal

track-planner

Lit le lieu, demande à l'évaluateur, résout les problèmes, demande l'approbation du contrôleur d'accès

HIGH

Évaluateur FIA

track-planner

Vérifie la mise en page à l'aide de la compétence FIA et d'un outil de notation

MEDIUM

Gatekeeper de jumeau numérique

gatekeeper

Exécute la checklist d'homologation finale

LOW

Objectifs de l'atelier

  • Comment agents-cli scaffold create organise un projet ADK et quels fichiers vous modifiez.
  • Comment empaqueter des connaissances du domaine en tant que compétence d'agent (SKILL.md, references/, scripts/) et la charger avec SkillToolset d'ADK.
  • Comment faire correspondre thinking_level au travail de chaque agent.
  • Évaluer des agents avec agents-cli eval run
  • Comment déployer un agent sur Agent Runtime, en quoi l'identité de l'agent diffère d'un compte de service et comment appeler l'agent déployé via le protocole A2A.

2. 2. Configurer votre environnement

Avant de commencer

Vous devez disposer d'un projet Google Cloud pour lequel la facturation est activée. Si vous suivez cet atelier de programmation lors d'un événement, utilisez le compte et le projet qui vous ont été fournis.

Ouvrir Cloud Shell et définir votre projet

Ouvrez Cloud Shell, puis exécutez la commande suivante :

# Cloud Shell sometimes opens without a project set. If so, this sets it to
# the first project on your account.
if [ -z "$(gcloud config get-value project 2>/dev/null)" ]; then
  gcloud config set project "$(gcloud projects list --format='value(projectId)' --limit=1)"
fi

export GOOGLE_CLOUD_PROJECT="$(gcloud config get-value project)"
export REGION="us-central1"
export ASSETS="$HOME/devrel-demos/codelabs/formula-e-track-planner"
echo "Project: $GOOGLE_CLOUD_PROJECT  Region: $REGION"

Créer des identifiants pour vos agents

gcloud est déjà connecté, mais le code Python exécuté par vos agents utilise les identifiants par défaut de l'application (ADC), qui nécessitent une connexion distincte :

gcloud auth application-default login

Ouvrez le lien qui s'affiche, connectez-vous avec le même compte, copiez le code et collez-le dans Cloud Shell. Ensuite, définissez le projet auquel le quota est facturé :

gcloud auth application-default set-quota-project "$GOOGLE_CLOUD_PROJECT"

Activer les API

gcloud services enable \
    aiplatform.googleapis.com \
    cloudbuild.googleapis.com \
    artifactregistry.googleapis.com \
    storage.googleapis.com \
    cloudresourcemanager.googleapis.com \
    serviceusage.googleapis.com \
    compute.googleapis.com \
    iam.googleapis.com \
    logging.googleapis.com \
    cloudtrace.googleapis.com

À quoi sert chaque API :

API

Utilisation

aiplatform

Modèles Gemini et Agent Runtime

cloudresourcemanager, serviceusage, iam

Vérifications des projets et des autorisations exécutées par gcloud et agents-cli deploy

compute

Crée le compte de service Compute Engine par défaut utilisé par le déploiement à l'étape 7.

storage, logging, cloudtrace

Stockage, journaux et traces, comme recommandé dans le guide de configuration d'Agent Runtime

cloudbuild, artifactregistry

Builds et images de conteneurs, inclus par précaution pour le déploiement à l'étape 7

De nombreux projets ont déjà activé certaines de ces fonctionnalités. L'activation d'une API déjà activée ne change rien.

Installer uv et agents-cli

curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
uv tool install google-agents-cli==1.10.0
agents-cli --version

Cet atelier de programmation a été testé avec agents-cli 1.10.0. Installez donc cette version pour obtenir les mêmes fichiers générés.

3. 3. Créer les projets et ajouter les assets

Générer les deux projets

agents-cli scaffold create génère un projet ADK complet et déployable. Exécutez-la deux fois, une fois pour chaque système d'agent.

Tout d'abord, le planificateur de parcours :

cd ~
agents-cli scaffold create track-planner \
  --agent adk \
  --deployment-target agent_runtime \
  --region "$REGION" \
  --prototype \
  --yes

Ensuite, le gardien :

cd ~
agents-cli scaffold create gatekeeper \
  --agent adk \
  --deployment-target agent_runtime \
  --region "$REGION" \
  --prototype \
  --yes

Option

Signification

--agent adk

Commencer à partir du modèle ADK Python

--deployment-target agent_runtime

Générer le serveur et la configuration pour Agent Runtime

--prototype

Ignorer l'intégration/le déploiement continus ; conserver un projet minimal

--yes

Accepter les valeurs par défaut sans demander confirmation

Explorer la mise en page

Le projet généré se présente comme suit. Vous ne modifiez que les fichiers marqués d'un ✍️ :

track-planner/
├── agents-cli-manifest.yaml   # Records the template, deployment target and root agent name
├── pyproject.toml             # Dependencies (google-adk, a2a-sdk, ...)
├── .env                       # Your project and model location, filled in for you
├── Dockerfile                 # Used by `agents-cli deploy`; copies app/ into the image
├── app/
│   ├── agent.py               # ✍️ Your agents
│   ├── fast_api_app.py        # Generated server: ADK API, A2A routes, Agent Runtime routes
│   └── app_utils/             # Generated helpers (A2A, sessions)
└── tests/
    ├── unit/                  # Fast tests with no model calls
    ├── integration/           # Generated server tests
    └── eval/                  # ✍️ eval_config.yaml and datasets for `agents-cli eval`

Pour obtenir une description de chaque fichier généré, consultez Structure du projet dans le guide de la CLI Agents.

Essayer l'agent généré

Avant de modifier quoi que ce soit, exécutez l'agent du modèle pour voir ce que l'échafaudage vous donne. Installez les dépendances et démarrez l'environnement de test local sur le port 8080 :

cd ~/track-planner
agents-cli install
agents-cli playground --port 8080

Pour l'ouvrir, cliquez sur le bouton Aperçu sur le Web dans la barre d'outils Cloud Shell (l'icône en forme d'œil en haut à droite), puis sélectionnez Prévisualiser sur le port 8080.

Dans le playground, posez à l'agent modèle une question à laquelle ses outils peuvent répondre, par exemple :

What's the weather in San Francisco?

Le panneau latéral affiche chaque événement, y compris l'appel de l'outil get_weather et son résultat. Vous utiliserez la même vue plus tard pour suivre vos propres agents.

Lorsque vous avez terminé, revenez au terminal et appuyez sur Ctrl+C pour arrêter le playground.

Ajouter les assets Formula E

Les données sur le circuit, les règles de la FIA, le script physique, les outils et les ensembles de données d'évaluation ne font pas partie de la leçon. Vous les copiez donc depuis GitHub au lieu de les saisir :

cd ~
git clone --depth 1 --filter=blob:none --sparse \
  --branch formula-e-lab-v1 \
  https://github.com/tdsymonds/devrel-demos.git
git -C devrel-demos sparse-checkout set codelabs/formula-e-track-planner

cp -r "$ASSETS/track-planner/." ~/track-planner/
cp -r "$ASSETS/gatekeeper/." ~/gatekeeper/

formula-e-lab-v1 est un tag de version fixe, de sorte que tout le monde reçoit les mêmes fichiers. $ASSETS a été défini à l'étape 2 et pointe vers le dossier que vous venez de cloner.

Cela ajoute les fichiers suivants :

track-planner/
├── app/
│   ├── tools.py                                  # get_track_topology, evaluate_track_layout
│   ├── auth.py                                   # A2A client with per-request token refresh
│   ├── data/circuits.json                        # Five candidate venues
│   └── skills/fia-compliance-skill/
│       ├── references/track_regulations.md       # The FIA rules
│       └── scripts/calculate_regen_zones.py      # Braking-energy calculator
└── tests/
    ├── unit/test_tools.py
    └── eval/datasets/track_eval.json, track_eval_quick.json
gatekeeper/
├── app/tools.py                                  # verify_infrastructure_checklist
└── tests/unit/test_tools.py

Installer et tester

(cd ~/track-planner && agents-cli install && uv run pytest tests/unit)
(cd ~/gatekeeper && agents-cli install && uv run pytest tests/unit)

agents-cli install exécute uv sync pour créer un environnement virtuel pour chaque projet. Tous les tests unitaires doivent réussir.

4. 4. Rédiger la compétence de conformité FIA

Une compétence agentique regroupe des instructions, des documents de référence et des scripts dans un seul dossier. SkillToolset d'ADK le charge à trois niveaux, de sorte que l'agent ne lit que ce dont il a besoin :

Niveau

Fichier

Quand l'agent le voit

1

SKILL.md : front matter (name, description)

Toujours (quelques dizaines de jetons)

2

SKILL.md corps

Lorsqu'il appelle load_skill

3

references/, scripts/

Lorsqu'il appelle load_skill_resource ou run_skill_script

La compétence se trouve dans app/skills/, car le Dockerfile généré ne copie que app/ dans l'image déployée.

Consulter les fichiers du niveau 3

Vous avez copié les fichiers de niveau 3 de la compétence à l'étape 3 :

  • references/track_regulations.md contient les règles que l'Assesseur vérifie : les limites de longueur et de largeur, la barrière dont chaque zone de freinage a besoin, les couches CAO obligatoires (ainsi que les couches médicales pour les itinéraires vers les hôpitaux), l'objectif de régénération et les règles du mode Attaque.
  • scripts/calculate_regen_zones.py est une calculatrice. À partir de la vitesse d'entrée, de la vitesse de sortie et de la distance de freinage d'une zone de freinage, il calcule l'énergie récupérée et la barrière nécessaire pour le virage. L'agent n'a donc jamais à deviner les lois de la physique.

Exécutez le script pour le virage 10 de Londres, où les voitures freinent de 215 à 55 km/h sur 82 mètres :

cd ~/track-planner
uv run python app/skills/fia-compliance-skill/scripts/calculate_regen_zones.py \
  --entry-kph 215 --exit-kph 55 --brake-dist-m 82

La voiture récupère environ 508 kW ici, ce qui est supérieur à l'objectif de 250 kW. Il ralentit à plus de 2G, de sorte que le virage nécessite des barrières Tecpro (F1_TEC3) :

{"entry_kph": 215.0, "exit_kph": 55.0, "brake_dist_m": 82.0, "deceleration_g": 2.07, "estimated_regen_kw": 507.7, "meets_250kw_target": true, "required_barrier_layer": "F1_TEC3"}

Écrire SKILL.md

Créez le fichier qui associe la compétence :

cloudshell edit app/skills/fia-compliance-skill/SKILL.md

Ajoutez le contenu suivant :

---
name: fia-compliance-skill
description: >
  FIA Formula E street circuit rules: length and width limits, mandatory CAD
  layers, barrier selection, and Gen3 regenerative braking estimates. Use when
  designing, checking or fixing a Formula E circuit layout.
---

# FIA Formula E Compliance

Follow these steps for every circuit you check.

1. **Read the rules.** Call `load_skill_resource` with
   `file_path="references/track_regulations.md"`.
2. **Estimate regeneration for every heavy braking zone.** Call
   `run_skill_script` with `file_path="scripts/calculate_regen_zones.py"` and
   `args={"entry-kph": <entry>, "exit-kph": <exit>, "brake-dist-m": <distance>}`.
   Never guess kW values. Use the highest `estimated_regen_kw` as the
   circuit's peak regeneration.
3. **Check barriers.** Any zone whose `required_barrier_layer` is `F1_TEC3`
   needs the `F1_TEC3` CAD layer.
4. **Report.** List every rule the layout fails and a concrete fix for each
   one.

Le description est ce que le modèle lit pour décider quand utiliser la compétence. Il indique donc à la fois ce que la compétence couvre et quand l'utiliser.

Découvrez comment la compétence est connectée

Ouvrez app/tools.py dans l'éditeur Cloud Shell :

cloudshell edit app/tools.py

Recherchez la fonction build_fia_skill_toolset :

def build_fia_skill_toolset() -> SkillToolset:
    return SkillToolset(
        skills=[load_skill_from_dir(SKILL_DIR)],
        code_executor=UnsafeLocalCodeExecutor(),
    )
  • load_skill_from_dir lit le dossier de la compétence : SKILL.md, references/ et scripts/.
  • SkillToolset les transforme en outils que l'agent peut appeler : list_skills, load_skill, load_skill_resource et run_skill_script.
  • UnsafeLocalCodeExecutor permet à run_skill_script d'exécuter le script Python sur cette machine. Cela convient pour un atelier, mais utilisez un exécuteur en bac à sable en production.

Vous associerez cet ensemble d'outils à l'évaluateur à l'étape suivante.

5. 5. Écrire les agents

Les modèles Gemini 3 contrôlent le raisonnement avec thinking_level (LOW, MEDIUM ou HIGH) au lieu d'un budget de jetons. Dans ADK, vous le définissez avec types.ThinkingConfig(thinking_level=types.ThinkingLevel.HIGH). Adaptez-le à la tâche de chaque agent : plus la réflexion est poussée, plus la planification est efficace, mais plus la latence et les coûts sont élevés.

Le Gatekeeper (LOW)

Le Gatekeeper n'exécute qu'une checklist déterministe. Il n'a donc pas besoin de beaucoup de raisonnement. Remplacez l'agent de modèle :

cloudshell edit ~/gatekeeper/app/agent.py
"""FIA Digital Twin Gatekeeper agent.

This agent runs in its own project and is deployed to Agent Runtime. The track
planner calls it over the A2A protocol.
"""

from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.models import Gemini
from google.genai import types

from app.tools import verify_infrastructure_checklist

# Tested with Gemini 3.8 Flash. To try Gemini 4 Argon, change this to
# "gemini-4-argon", delete the `temperature` settings below (Argon ignores
# them) and run `uv add "google-genai>=2.30.1"` in this project first.
MODEL = "gemini-3.8-flash"

root_agent = Agent(
    # Must match `root_agent_name` in agents-cli-manifest.yaml.
    name="gatekeeper",
    model=Gemini(
        model=MODEL,
        retry_options=types.HttpRetryOptions(attempts=3),
    ),
    description=(
        "FIA Digital Twin Gatekeeper. Runs the final homologation checklist "
        "for a Formula E circuit blueprint."
    ),
    instruction=(
        "You are the FIA Digital Twin Gatekeeper. For every circuit blueprint "
        "you receive, call `verify_infrastructure_checklist` with its circuit "
        "ID, CAD layers, minimum width and peak regeneration. Reply with the "
        "status and any missing CAD layers. Never approve a blueprint the "
        "checklist rejects."
    ),
    # A deterministic checklist needs little reasoning: keep it fast and cheap.
    generate_content_config=types.GenerateContentConfig(
        temperature=0.0,
        thinking_config=types.ThinkingConfig(
            thinking_level=types.ThinkingLevel.LOW,
        ),
    ),
    tools=[verify_infrastructure_checklist],
)

app = App(
    root_agent=root_agent,
    name="app",
)

Exécuter Gatekeeper localement

Ouvrez un deuxième onglet Cloud Shell et démarrez le serveur généré par Gatekeeper sur le port 8001 :

cd ~/gatekeeper
APP_URL=http://localhost:8001 uv run uvicorn app.fast_api_app:app --port 8001

Revenez au premier onglet et examinez la fiche d'agent A2A de Gatekeeper. Voici comment les autres agents découvrent ce qu'est le Gatekeeper et ce qu'il peut faire :

curl -s http://localhost:8001/a2a/app/.well-known/agent-card.json | jq .

Vous devriez voir un fichier JSON semblable à celui-ci (raccourci) :

{
  "name": "gatekeeper",
  "description": "FIA Digital Twin Gatekeeper. Runs the final homologation checklist for a Formula E circuit blueprint.",
  "supportedInterfaces": [
    {"url": "http://localhost:8001/a2a/app", "protocolBinding": "JSONRPC", "protocolVersion": "1.0"}
  ],
  "skills": [
    {"id": "gatekeeper", "name": "model", "description": "FIA Digital Twin Gatekeeper. ..."},
    {"id": "gatekeeper-verify_infrastructure_checklist", "name": "verify_infrastructure_checklist", "description": "Runs the final homologation checklist ..."}
  ]
}
  • name et description proviennent directement de votre définition Agent(...).
  • skills liste ce que l'agent peut faire. ADK a ajouté verify_infrastructure_checklist automatiquement, en utilisant la docstring de l'outil.
  • supportedInterfaces indique aux appelants où envoyer les requêtes A2A. C'est pourquoi vous avez démarré le serveur avec APP_URL=http://localhost:8001 : un appelant lit d'abord la carte, puis envoie ses requêtes à l'url annoncé par la carte.

Rien n'a été écrit à la main : le serveur échafaudé crée la fiche à partir de votre agent. Le planificateur de parcours lira cette carte pour trouver et appeler le Gatekeeper.

L'évaluateur (MEDIUM) et l'architecte principal (HIGH)

Le planificateur de parcours comporte deux agents :

  • L'évaluateur pondère plusieurs règles, il obtient donc MEDIUM. Il est encapsulé dans un AgentTool. L'architecte principal l'appelle donc comme une fonction et obtient le verdict.
  • L'architecte en chef planifie, corrige et coordonne, de sorte qu'il obtient HIGH. Elle atteint Gatekeeper via un RemoteA2aAgent, créé par create_remote_gatekeeper_agent() dans app/auth.py, et l'encapsule également dans un AgentTool.

Pourquoi encapsuler les deux assistants en tant qu'outils ? Un AgentTool renvoie sa réponse à l'appelant. L'architecte principal reste donc aux commandes et rédige le plan final. Si le Gatekeeper était un sub_agent, le Lead Architect lui transmettrait la conversation, et l'état court du Gatekeeper deviendrait la réponse finale.

Ouvrez le fichier app/agent.py du planificateur de parcours :

cloudshell edit ~/track-planner/app/agent.py

Copiez le code suivant et remplacez l'intégralité du contenu de ~/track-planner/app/agent.py par celui-ci :

"""Formula E track planner: Lead Architect, FIA Assessor and remote Gatekeeper.

- The Lead Architect (root agent) designs the circuit and stays in charge.
- The FIA Assessor checks the layout and is called as a tool (`AgentTool`).
- The Gatekeeper runs in a separate project. It is also called as a tool, over
  A2A, so its verdict comes back to the Lead Architect.
"""

from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.models import Gemini
from google.adk.tools.agent_tool import AgentTool
from google.genai import types

from app.auth import create_remote_gatekeeper_agent
from app.tools import build_fia_skill_toolset
from app.tools import evaluate_track_layout
from app.tools import get_track_topology

# Tested with Gemini 3.8 Flash. To try Gemini 4 Argon, change this to
# "gemini-4-argon", delete the `temperature` settings below (Argon ignores
# them) and run `uv add "google-genai>=2.30.1"` in this project first.
MODEL = "gemini-3.8-flash"

fia_skill_toolset = build_fia_skill_toolset()

# 1. FIA Assessor: MEDIUM thinking to weigh several rules without much latency.
assessor_agent = Agent(
    name="fia_assessor",
    model=Gemini(
        model=MODEL,
        retry_options=types.HttpRetryOptions(attempts=3),
    ),
    description=(
        "FIA safety and energy assessor. Audits a proposed circuit against "
        "the geometry, regeneration and CAD layer rules."
    ),
    instruction=(
        "You are the FIA Assessor. Load the `fia-compliance-skill` and follow "
        "it. Estimate regeneration for each braking zone with "
        "`calculate_regen_zones.py`, then call `evaluate_track_layout` with "
        "the highest value. Reply with the verdict, the score and every "
        "remediation note."
    ),
    generate_content_config=types.GenerateContentConfig(
        temperature=0.1,
        thinking_config=types.ThinkingConfig(
            thinking_level=types.ThinkingLevel.MEDIUM,
        ),
    ),
    tools=[fia_skill_toolset, evaluate_track_layout],
)

# 2. Remote Gatekeeper, reached over A2A (see app/auth.py).
remote_gatekeeper = create_remote_gatekeeper_agent()

# 3. Lead Architect: HIGH thinking for multi-step planning and trade-offs.
root_agent = Agent(
    # Must match `root_agent_name` in agents-cli-manifest.yaml.
    name="track_planner",
    model=Gemini(
        model=MODEL,
        retry_options=types.HttpRetryOptions(attempts=3),
    ),
    description=(
        "Lead Formula E circuit architect. Designs FIA-compliant street "
        "circuits and gets them homologated."
    ),
    instruction=(
        "You are the Lead Formula E Circuit Architect. For every request:\n"
        "1. Call `get_track_topology` for the venue.\n"
        "2. Call the `fia_assessor` tool with the full topology.\n"
        "3. If the Assessor rejects the layout, propose concrete fixes (for "
        "example widen a sector to 10.0m or add missing CAD layers) and "
        "re-assess the fixed layout.\n"
        "4. Once the Assessor passes it, call the `remote_gatekeeper` tool "
        "with the circuit ID, CAD layers, minimum width and peak "
        "regeneration for final approval.\n"
        "5. Finish with a short blueprint: circuit ID, length, minimum width, "
        "peak regeneration, every CAD layer by name, and the Gatekeeper's "
        "status."
    ),
    generate_content_config=types.GenerateContentConfig(
        temperature=0.2,
        thinking_config=types.ThinkingConfig(
            thinking_level=types.ThinkingLevel.HIGH,
        ),
    ),
    # Both helpers are tools, so their answers come back to the Lead Architect
    # and it writes the final blueprint.
    tools=[
        get_track_topology,
        AgentTool(agent=assessor_agent),
        AgentTool(agent=remote_gatekeeper),
    ],
)

app = App(
    root_agent=root_agent,
    name="app",
)

Exécuter l'ensemble du système dans le playground

Assurez-vous que Gatekeeper est toujours en cours d'exécution dans votre deuxième onglet. Dans le premier onglet, démarrez le playground du planificateur de parcours :

cd ~/track-planner
agents-cli playground --port 8080

Cliquez sur Aperçu sur le Web > Prévisualiser sur le port 8080, comme vous l'avez fait à l'étape 3.

Demandez un circuit dans un lieu qui respecte les règles :

Design and homologate a Formula E circuit at Tokyo Bay.

Observez le panneau des événements pendant que les agents travaillent. Vous devriez voir, dans l'ordre :

  1. get_track_topology : l'architecte principal charge les données de la baie de Tokyo.
  2. fia_assessor : l'architecte principal transmet la mise en page à l'évaluateur, qui appelle load_skill, load_skill_resource et run_skill_script (une fois par zone de freinage), puis evaluate_track_layout.
  3. remote_gatekeeper : l'architecte principal appelle le Gatekeeper via A2A. La requête est enregistrée dans le deuxième onglet.
  4. Un plan final de l'architecte principal, listant chaque calque CAO et l'état APPROUVÉ du Gatekeeper.

Cliquez sur un événement pour afficher ses entrées et sorties exactes.

Démarrez maintenant une nouvelle session et essayez un lieu qui ne respecte pas les règles :

Can the Monaco Harbour layout be homologated? If not, fix it and try again.

Cette fois, l'évaluateur rejette la chicane de 6,8 m, l'architecte principal en propose une plus large, et la disposition fixe est renvoyée à l'évaluateur avant d'atteindre le responsable de la validation.

Lorsque vous avez terminé, appuyez sur Ctrl+C dans le premier onglet pour arrêter le playground. Laissez Gatekeeper s'exécuter dans le deuxième onglet : vous en aurez besoin à l'étape suivante.

6. 6. Évaluer les agents

agents-cli eval run exécute votre agent sur un ensemble de données, enregistre chaque tour et chaque appel d'outil, et note les résultats. Vous décidez ce que vous souhaitez mesurer dans tests/eval/eval_config.yaml.

Examiner l'ensemble de données

Ouvrez l'ensemble de données dans l'éditeur Cloud Shell :

cd ~/track-planner
cloudshell edit tests/eval/datasets/track_eval.json

Le fichier contient une liste de eval_cases. Chaque cas comporte un eval_case_id, un prompt (ce que l'utilisateur demande) et un reference (ce à quoi ressemble une bonne réponse). Le juge LLM compare la réponse de l'agent à la référence.

eval_case_id

Ce qu'il teste

fe_001_tokyo_compliant

Un établissement qui respecte toutes les règles et qui est approuvé

fe_002_monaco_narrow

La chicane de 6,8 m ne respecte pas la règle de largeur

fe_003_berlin_too_long

Le circuit de 4 350 m ne respecte pas la règle de longueur

fe_004_missing_layers

Une mise en page sans les calques CAO obligatoires

fe_005_low_regen

Une zone de freinage en dessous de l'objectif de 250 kW

fe_006_sao_paulo_medical

Accès à l'hôpital nécessitant des calques de CAO médicale

fe_007_attack_mode

Une zone Attack Mode sur la trajectoire de course

fe_008_multiturn_remediation

Une conversation multitour : Monaco est rejeté, l'utilisateur demande une correction et la mise en page corrigée doit être approuvée.

Le dernier cas utilise agent_data au lieu de prompt. Il rejoue les tours précédents d'une conversation, ce qui vous permet de tester la façon dont l'agent gère une demande de suivi, et pas seulement une seule question.

track_eval_quick.json dans le même dossier ne contient que deux cas : le cas 1 (une seule question) et le cas 8 (une conversation). Vous allez exécuter celui-ci, car il est plus rapide et utilise moins de quota.

Écrire les métriques

L'ensemble de données indique quoi demander à l'agent. eval_config.yaml indique comment noter les réponses. agents-cli eval run le lit une fois que l'agent a répondu à chaque cas, puis exécute chaque métrique sur chaque réponse.

Ouvrez le fichier généré par le scaffold :

cloudshell edit tests/eval/eval_config.yaml

Il contient déjà un juge LLM générique (custom_response_quality) et une métrique agent_turn_count qui ne compte que les tours. C'est un bon début pour n'importe quel agent, mais cela ne dit rien sur la Formule E. Vous allez conserver le juge et ajouter une métrique qui vérifie une règle spécifique à ce domaine.

Les bonnes évaluations combinent généralement deux types de métriques :

Type

Comment il est évalué

Utilisation

Compromis

LLM-as-a-judge

Un modèle lit la réponse et le reference du cas, puis attribue une note de 1 à 5 avec une explication.

Qualités floues : la réponse est-elle correcte, complète et claire ?

Coûte un appel de modèle par cas ; les scores peuvent varier légèrement d'une exécution à l'autre

Métrique de code

Une petite fonction Python vérifie la réponse.

Faits exacts et vérifiables : la réponse inclut-elle un élément qui doit s'y trouver ?

Ne vérifie que ce que vous pouvez exprimer dans le code

Remplacez le contenu du fichier par ce qui suit :

metrics_to_run:
  - custom_response_quality
  - fia_cad_layer_compliance

custom_metrics:
  # LLM-as-a-judge. response_quality.py is generated by `agents-cli scaffold
  # create`; it grades the final answer against the case's `reference`.
  - name: custom_response_quality
    custom_function_file: response_quality.py

  # Deterministic code metric: an approved blueprint must name every
  # mandatory FIA CAD layer.
  - name: fia_cad_layer_compliance
    custom_function: |
      def evaluate(instance):
          response = str(instance.get("response") or "")
          reference = str(instance.get("reference") or "")
          # Only approvals must list every layer. Rejections and other
          # questions (such as Attack Mode) don't, so skip them.
          if "APPROVED" not in reference:
              return {"score": 1.0, "explanation": "Not an approval case."}
          required = [
              "F1_TRACK_CENTRELINE",
              "F1_PIT_ENTRY_SPEED_LIMIT_LANE",
              "F1_WALL_TEMP",
              "F1_TEC3",
          ]
          missing = [layer for layer in required if layer not in response]
          return {
              "score": round(1 - len(missing) / len(required), 2),
              "explanation": "Missing: " + (", ".join(missing) or "none"),
          }

Fonction de chaque partie :

  • metrics_to_run liste les métriques à exécuter, par nom. Si vous supprimez un nom ici, la métrique correspondante sera désactivée, mais pas supprimée.
  • custom_response_quality est le juge LLM. custom_function_file pointe vers tests/eval/response_quality.py, qui a été généré par l'échafaudage. Ouvrez-le si vous souhaitez voir la requête d'évaluation.
  • fia_cad_layer_compliance est votre métrique de code, écrite de manière intégrée sous custom_function. Pour chaque cas, il reçoit un instance (la requête, la response finale de l'agent et la reference). Pour les cas où la réponse attendue est une approbation, il vérifie que la réponse nomme les quatre couches de CAO obligatoires et liste celles qui sont manquantes. Un plan qui omet F1_TEC3, par exemple, obtient un score de 0,75. Les autres cas obtiennent un score de 1,0, car un refus ou une réponse en mode Attaque n'a pas besoin de lister chaque couche.

Pourquoi vérifier les calques CAO ? Un plan approuvé qui omet une couche obligatoire est incorrect, même s'il est bien rédigé, et un juge LLM peut passer à côté de ce problème. Une vérification de code applique la même règle de la même manière à chaque exécution.

Exécuter l'évaluation

Laissez le Gatekeeper local s'exécuter dans votre deuxième onglet, puis exécutez la commande suivante :

agents-cli eval run \
  --dataset tests/eval/datasets/track_eval_quick.json \
  --config tests/eval/eval_config.yaml \
  --concurrency 1
ls artifacts/grade_results/

L'évaluation prend quelques minutes. Chaque cas exécute l'intégralité du flux multi-agent, soit environ dix appels de modèle avec l'architecte principal sur la réflexion HIGH. Step 1/2: eval generate est donc la partie lente. Il n'est pas bloqué.

Une fois l'opération terminée, ouvrez le rapport HTML dans artifacts/grade_results/ avec l'éditeur Cloud Shell. Des scores fia_cad_layer_compliance faibles indiquent que la réponse finale de l'architecte principal omet des couches. Essayez de resserrer ses instructions et de relancer l'évaluation.

7. 7. Déployer le Gatekeeper sur Agent Runtime

Vous ne déployez que Gatekeeper. Le planificateur de pistes continue de s'exécuter dans Cloud Shell et l'appelle.

Ouvrir un nouvel onglet Cloud Shell

Le déploiement prend cinq à huit minutes. Effectuez-le donc dans un nouvel onglet pendant que l'évaluation de l'étape 6 se termine :

  1. Cliquez sur Ouvrir un nouvel onglet (+) dans la barre d'outils Cloud Shell.
  2. Un nouvel onglet ne conserve pas les variables de l'étape 2. Définissez-les à nouveau :
    export GOOGLE_CLOUD_PROJECT="$(gcloud config get-value project)"
    export REGION="us-central1"
    export ASSETS="$HOME/devrel-demos/codelabs/formula-e-track-planner"
    export PATH="$HOME/.local/bin:$PATH"
    echo "Project: $GOOGLE_CLOUD_PROJECT  Region: $REGION"
    

Laissez le Gatekeeper local s'exécuter dans votre deuxième onglet : l'évaluation l'utilise toujours, et le déploiement n'a pas besoin qu'il soit arrêté.

Déployer

Dans le nouvel onglet, déployez Gatekeeper. Il s'exécute en tant que compte de service Compute Engine par défaut du projet :

cd ~/gatekeeper
PROJECT_NUMBER=$(gcloud projects describe "$GOOGLE_CLOUD_PROJECT" --format="value(projectNumber)")
agents-cli deploy \
  --no-agent-identity \
  --service-account "${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --project "$GOOGLE_CLOUD_PROJECT" \
  --region "$REGION" \
  --no-confirm-project

Le déploiement prend environ cinq à huit minutes. Ne fermez pas l'onglet Cloud Shell pendant l'exécution. Si le flux de journaux se déconnecte, vérifiez la progression avec agents-cli deploy --status. En attendant, revenez à l'étape 6 et lisez votre rapport d'évaluation.

Inspecter l'identité de l'agent

agents-cli enregistre la ressource déployée dans deployment_metadata.json :

export RUNTIME_ID=$(jq -r .remote_agent_runtime_id ~/gatekeeper/deployment_metadata.json)
echo "$RUNTIME_ID"

curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${REGION}-aiplatform.googleapis.com/v1beta1/${RUNTIME_ID}" \
  | jq -r .spec.effectiveIdentity

La deuxième ligne de sortie correspond à l'identité sous laquelle Gatekeeper s'exécute :

Déploiement réalisé avec

Exemple de résultat :

Le compte de service (par défaut)

123456789012-compute@developer.gserviceaccount.com

Identité de l'agent

agents.global.org-.system.id.goog/resources/aiplatform/projects//locations/us-central1/reasoningEngines/

Le compte de service est partagé par tous les éléments du projet qui l'utilisent. Une identité d'agent appartient à cet agent. Vous pouvez donc lui attribuer exactement les rôles dont il a besoin et voir ses actions séparément dans les journaux d'audit.

8. 8. Se connecter au Gatekeeper déployé via A2A

Agent Runtime diffuse les routes A2A générées par Gatekeeper derrière un chemin d'accès /api authentifié.

Assurez-vous que l'évaluation de l'étape 6 est terminée, puis arrêtez le Gatekeeper local dans votre deuxième onglet (Ctrl+C). Désormais, le planificateur de parcours appelle le modèle déployé. Dans votre premier onglet, pointez le planificateur de parcours vers celui-ci :

export RUNTIME_ID=$(jq -r .remote_agent_runtime_id ~/gatekeeper/deployment_metadata.json)
: "${RUNTIME_ID:?No deployed Gatekeeper found. Complete Step 7 first.}"
cd ~/track-planner
echo "GATEKEEPER_A2A_URL=https://${REGION}-aiplatform.googleapis.com/reasoningEngines/v1/${RUNTIME_ID}/api/a2a/app" >> .env

Pourquoi le client actualise son jeton

Ouvrez app/auth.py. Les jetons d'accès Google Cloud expirent au bout d'une heure. Par conséquent, un client qui définit un en-tête Authorization fixe au démarrage commence à échouer avec l'erreur 401 Unauthorized au bout d'une heure. GoogleAuthRefresh est une sous-classe de httpx.Auth. Elle vérifie les identifiants à chaque requête et les actualise si nécessaire. create_remote_gatekeeper_agent() ne l'associe qu'aux URL https://.

Exécuter le pipeline de bout en bout

Redémarrez le playground pour qu'il prenne en compte le nouveau GATEKEEPER_A2A_URL :

agents-cli playground --port 8080

Cliquez sur Aperçu sur le Web > Prévisualiser sur le port 8080, démarrez une nouvelle session et posez la question suivante :

Assess the Monaco Harbour layout, widen the chicane to 11m, and get Gatekeeper approval.

Les événements sont identiques à ceux de l'étape 5, mais l'appel remote_gatekeeper est désormais envoyé au Gatekeeper sur Agent Runtime au lieu de votre deuxième onglet.

Voir l'appel arriver sur Agent Runtime

Prouvez que la requête a bien atteint le Gatekeeper déployé via A2A :

  1. Dans la console Cloud, recherchez Agent Platform, puis ouvrez Déploiements dans le menu de gauche.
  2. Sélectionnez gatekeeper.
  3. Ouvrez l'onglet Sessions.

Une nouvelle session s'affiche, dont l'ID utilisateur commence par A2A_USER_. Le serveur A2A de Gatekeeper l'a créé lorsque votre planificateur de parcours l'a appelé. Ouvrez l'onglet Traces sur la même page pour afficher l'appel de modèle de Gatekeeper et son appel d'outil verify_infrastructure_checklist pour cette requête.

Facultatif : déployez également le planificateur de parcours

Pour l'instant, le planificateur de parcours s'exécute dans Cloud Shell. Ses appels A2A au Gatekeeper utilisent donc vos identifiants. Déployez également le planificateur. Les deux agents s'exécutent sur Agent Runtime et s'appellent mutuellement avec leurs propres identités déployées.

Appuyez sur Ctrl+C pour arrêter le playground, puis déployez le planificateur de la même manière que le Gatekeeper :

cd ~/track-planner
PROJECT_NUMBER=$(gcloud projects describe "$GOOGLE_CLOUD_PROJECT" --format="value(projectNumber)")
agents-cli deploy \
  --no-agent-identity \
  --service-account "${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --project "$GOOGLE_CLOUD_PROJECT" \
  --region "$REGION" \
  --no-confirm-project

Les deux agents s'exécutent en tant que même compte de service Compute Engine. Conservez --service-account : sans cela, le planificateur s'exécute en tant qu'agent de service par défaut d'Agent Runtime et ne peut pas appeler le Gatekeeper (voir Pourquoi ce compte de service ? à l'étape 7).

Comment le planificateur déployé atteint le Gatekeeper :

  • agents-cli deploy copie les valeurs de .env dans le déploiement, de sorte que le planificateur obtienne le GATEKEEPER_A2A_URL que vous avez ajouté ci-dessus.
  • Dans Agent Runtime, GoogleAuthRefresh dans app/auth.py récupère l'identité déployée du planificateur au lieu de la vôtre.

Le déploiement prend environ cinq à huit minutes. Une fois l'opération terminée, vérifiez l'identité sous laquelle le planificateur s'exécute :

export PLANNER_ID=$(jq -r .remote_agent_runtime_id deployment_metadata.json)
curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${REGION}-aiplatform.googleapis.com/v1beta1/${PLANNER_ID}" \
  | jq -r .spec.effectiveIdentity

Pour tester le système déployé :

  1. Dans la console Cloud, revenez à Agent Platform > Déploiements.
  2. Sélectionnez track-planner et ouvrez l'onglet Playground.
  3. Saisissez cette commande :
    Design and homologate a Formula E circuit at Tokyo Bay.
    
  4. Attendez que le plan passe à l'état APPROUVÉ par le validateur.
  5. Revenez à Déploiements, sélectionnez gatekeeper, puis ouvrez l'onglet Sessions. La session A2A_USER_ la plus récente provient du planificateur déployé, et non de Cloud Shell.

Lorsque vous effectuerez le nettoyage à l'étape suivante, supprimez également le planificateur :

curl -X DELETE -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${REGION}-aiplatform.googleapis.com/v1beta1/${PLANNER_ID}?force=true"

9. 9. Dépannage et nettoyage

Dépannage

Problème constaté

Solution

UREQ_TOS_NOT_ACCEPTED

Acceptez les conditions d'utilisation dans la console Cloud, puis réessayez.

Le projet est (unset)

Réexécutez les commandes du projet de l'étape 2.

service account info is missing 'email' field

Exécutez gcloud auth application-default login (étape 2), puis redémarrez tous les serveurs en cours d'exécution.

FileNotFoundError pour SKILL.md

Effectuez l'étape 4 ou copiez $ASSETS/solution/track-planner/. dans ~/track-planner/.

De Connection refused à localhost:8001

Démarrez le Gatekeeper local (étape 5) ou définissez GATEKEEPER_A2A_URL sur celui déployé (étape 8).

Permission 'resourcemanager.projects.setIamPolicy' denied au moment du déploiement

Votre compte ne peut pas utiliser l'identité de l'agent. Suivez les instructions de l'encadré "Nettoyage" à l'étape 7, puis utilisez le compte de service "deploy".

Le planificateur de suivi déployé signale 403 Forbidden de Gatekeeper

L'identité du planificateur n'est pas autorisée à appeler le Gatekeeper. Redéployez-le avec --service-account comme à l'étape 7, ou avec --agent-identity si vous êtes propriétaire du projet.

429 RESOURCE_EXHAUSTED

Vous avez atteint le quota Gemini. Attendez une minute ou ajoutez --concurrency 1 --qps 2 à agents-cli eval run.

Address already in use

Arrêtez l'autre serveur ou choisissez un autre --port.

Effectuer un nettoyage

Supprimez le Gatekeeper déployé pour qu'il ne continue pas à s'exécuter :

export RUNTIME_ID=$(jq -r .remote_agent_runtime_id ~/gatekeeper/deployment_metadata.json)
: "${RUNTIME_ID:?No deployed Gatekeeper found.}"
curl -X DELETE -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  "https://${REGION}-aiplatform.googleapis.com/v1beta1/${RUNTIME_ID}?force=true"

Si vous avez créé un projet spécifiquement pour cet atelier de programmation, vous pouvez le supprimer entièrement. Si vous participez à un événement, conservez le projet qui vous a été attribué jusqu'à ce que les organisateurs vous indiquent le contraire.

Ce que vous avez appris

  • agents-cli scaffold create vous fournit un projet ADK standard et déployable. Vous n'avez qu'à écrire des agents, des compétences et des évaluations.
  • Les compétences d'agent permettent de ne pas inclure les connaissances du domaine dans les prompts tant que l'agent n'en a pas besoin.
  • thinking_level vous permet de privilégier la vitesse plutôt que la profondeur de raisonnement pour chaque agent.
  • agents-cli eval run transforme la question "Est-ce que ça fonctionne ?" en scores que vous pouvez suivre.
  • L'identité d'agent et l'A2A permettent aux agents déployés indépendamment de travailler ensemble de manière sécurisée.

En savoir plus