Crea un planificador de pistas de Fórmula E multiagente con el ADK y A2A

1. 1. Descripción general

En este codelab, crearás un sistema multiagente que diseña un circuito urbano de Fórmula E y obtiene su aprobación en función de un conjunto simplificado de reglas de la FIA.

Usarás agents-cli para generar dos proyectos estándar del Kit de desarrollo de agentes (ADK) y, luego, escribirás las partes importantes: una habilidad de agente, tres agentes y una evaluación. Todo lo demás (diseño del proyecto, servidor, dependencias y configuración de la implementación) proviene de la CLI.

¿Es la primera vez que ves la Fórmula E?

No necesitas saber de automovilismo. Estos cinco términos son suficientes:

  • Fórmula E: el campeonato de carreras totalmente eléctricas de la FIA. La mayoría de las carreras se realizan en circuitos urbanos temporales en los centros de las ciudades.
  • Homologación: Es la aprobación oficial de la FIA de que un circuito es seguro para competir. Sin homologación, no hay carrera.
  • Frenado regenerativo: Cuando el auto frena, los motores recargan la batería. Un frenado más brusco recupera más energía, por lo que las reglas de este codelab requieren al menos una zona de frenado brusco.
  • Capas de CAD: Son capas con nombre en los dibujos de diseño del circuito, como la línea de carrera, la calle de boxes y las barreras. La FIA verifica que estén presentes los obligatorios.
  • Modo de ataque: Los conductores obtienen un aumento temporal de potencia cuando conducen por una zona de activación ubicada fuera de la línea de carrera.

Tus agentes juegan en ambos lados: uno diseña el circuito y dos lo revisan para la FIA.

Qué compilarás

Arquitectura: En el proyecto track-planner, que se ejecuta en Cloud Shell, el agente track_planner usa get_track_topology, fia_assessor y remote_gatekeeper. remote_gatekeeper llama al agente gatekeeper a través de A2A. En el proyecto gatekeeper, que se ejecuta en Agent Runtime, el agente gatekeeper usa verify_infrastructure_checklist.

Los cuadros verdes son agentes y los cuadros con líneas punteadas son sus herramientas, de la misma manera que los dibuja la zona de pruebas del ADK. Dos de las herramientas unen a otros agentes: fia_assessor es el agente de FIA Assessor con la habilidad de cumplimiento de la FIA, y remote_gatekeeper llama al agente de Gatekeeper en el otro proyecto a través de A2A.

Agente

Proyecto

Trabajo

thinking_level

Arquitecto principal

track-planner

Analiza el entorno, le pregunta al evaluador, soluciona problemas y le pide aprobación al guardián

HIGH

Asesor de FIA

track-planner

Verifica el diseño con la habilidad de FIA y una herramienta de puntuación

MEDIUM

Gatekeeper de gemelos digitales

gatekeeper

Ejecuta la lista de tareas de homologación final.

LOW

Qué aprenderás

  • Cómo agents-cli scaffold create diseña un proyecto de ADK y qué archivos editas
  • Cómo empaquetar el conocimiento del dominio como una habilidad del agente (SKILL.md, references/, scripts/) y cargarlo con SkillToolset del ADK
  • Cómo hacer coincidir thinking_level con el trabajo de cada agente.
  • Cómo evaluar agentes con agents-cli eval run
  • Cómo implementar un agente en Agent Runtime, en qué se diferencia la identidad del agente de una cuenta de servicio y cómo llamar al agente implementado a través del protocolo A2A.

2. 2. Configura tu entorno

Antes de comenzar

Necesitas un proyecto de Google Cloud con la facturación habilitada. Si estás siguiendo este codelab en un evento, usa la cuenta y el proyecto que se te proporcionaron.

Abre Cloud Shell y configura tu proyecto

Abre Cloud Shell y, luego, ejecuta lo siguiente:

# 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"

Crea credenciales para tus agentes

gcloud ya accedió, pero el código de Python que ejecutan tus agentes usa las credenciales predeterminadas de la aplicación (ADC), que necesitan un acceso independiente:

gcloud auth application-default login

Abre el vínculo que se imprime, accede con la misma cuenta, copia el código y pégalo de nuevo en Cloud Shell. Luego, configura el proyecto al que se factura la cuota:

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

Habilita las APIs

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

Para qué sirve cada API:

API

Se usa para

aiplatform

Modelos de Gemini y Agent Runtime

cloudresourcemanager, serviceusage, iam

Las verificaciones de proyectos y permisos que ejecutan gcloud y agents-cli deploy

compute

Crea la cuenta de servicio predeterminada de Compute Engine que usa la implementación en el paso 7.

storage, logging, cloudtrace

Almacenamiento, registros y seguimientos, como se recomienda en la guía de configuración de Agent Runtime

cloudbuild, artifactregistry

Compilaciones e imágenes de contenedores, incluidas como precaución para la implementación en el paso 7

Muchos proyectos ya tienen algunas de estas opciones activadas. Habilitar una API que ya está activada no cambia nada.

Instala uv y 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

Este codelab se probó con agents-cli 1.10.0, por lo que debes instalar esa versión para obtener los mismos archivos generados.

3. 3. Crea la estructura de los proyectos y agrega los recursos

Genera ambos proyectos

agents-cli scaffold create genera un proyecto del ADK completo y que se puede implementar. Ejecútalo dos veces, una para cada sistema de agentes.

Primero, el planificador de pistas:

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

Luego, la persona responsable de filtrar las conversaciones:

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

Marcar

Significado

--agent adk

Comienza con la plantilla de ADK de Python

--deployment-target agent_runtime

Genera el servidor y la configuración para Agent Runtime

--prototype

Omitir CI/CD; mantener el proyecto al mínimo

--yes

Acepta los valores predeterminados sin preguntar

Explora el diseño

El proyecto generado se ve de la siguiente manera. Solo debes editar los archivos marcados con ✍️:

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`

Para obtener una descripción de cada archivo generado, consulta Estructura del proyecto en la guía de la CLI de Agents.

Prueba el agente generado

Antes de cambiar algo, ejecuta el agente de plantilla para ver qué te ofrece el scaffolding. Instala las dependencias y, luego, inicia la zona de pruebas local en el puerto 8080:

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

Para abrirla, haz clic en el botón Vista previa en la Web en la barra de herramientas de Cloud Shell (el ícono de ojo en la parte superior derecha) y, luego, selecciona Vista previa en el puerto 8080.

En la zona de pruebas, hazle al agente de plantilla una pregunta que sus herramientas puedan responder, por ejemplo:

What's the weather in San Francisco?

En el panel lateral, se muestra cada evento, incluida la llamada a la herramienta get_weather y su resultado. Más adelante, usarás la misma vista para seguir a tus propios agentes.

Cuando termines, vuelve a la terminal y presiona Ctrl+C para detener la zona de pruebas.

Agrega los recursos de la Fórmula E

Los datos del lugar, las reglas de la FIA, la secuencia de comandos de física, las herramientas y los conjuntos de datos de evaluación no son la lección, por lo que los copias de GitHub en lugar de escribirlos:

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 es una etiqueta de versión fija, por lo que todos obtienen los mismos archivos. $ASSETS se configuró en el paso 2 y apunta a la carpeta que acabas de clonar.

Esto agrega los siguientes archivos:

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

Instala y prueba

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

agents-cli install ejecuta uv sync para crear un entorno virtual para cada proyecto. Todas las pruebas de unidades deberían aprobarse.

4. 4. Escribe la habilidad de cumplimiento de la FIA

Una habilidad para agentes empaqueta instrucciones, documentos de referencia y secuencias de comandos en una sola carpeta. El SkillToolset del ADK lo carga en tres niveles, por lo que el agente solo lee lo que necesita:

Nivel

Archivo

Cuando el agente lo ve

1

SKILL.md de materia preliminar (name, description)

Siempre (unas pocas decenas de tokens)

2

SKILL.md de cuerpo

Cuando llama a load_skill

3

references/, scripts/

Cuando llama a load_skill_resource o run_skill_script

La habilidad reside en app/skills/ porque el Dockerfile generado solo copia app/ en la imagen implementada.

Observa los archivos de nivel 3

En el paso 3, copiaste los archivos del nivel 3 de la habilidad:

  • references/track_regulations.md contiene las reglas que verifica el evaluador: los límites de longitud y ancho, qué barrera necesita cada zona de frenado, las capas de CAD obligatorias (además de las capas médicas para las rutas de los hospitales), el objetivo de regeneración y las reglas del modo de ataque.
  • scripts/calculate_regen_zones.py es una calculadora. Dadas la velocidad de entrada, la velocidad de salida y la distancia de frenado de una zona de frenado, calcula la energía recuperada y la barrera que necesita la curva, por lo que el agente nunca tiene que adivinar la física.

Ejecuta la secuencia de comandos para la curva 10 de Londres, en la que los autos frenan de 215 a 55 km/h en 82 metros:

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

El auto recupera alrededor de 508 kW aquí, por encima del objetivo de 250 kW. Se desacelera a más de 2G, por lo que la curva necesita barreras 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"}

Escribe SKILL.md

Crea el archivo que une la habilidad:

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

Agrega este contenido:

---
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.

El description es lo que lee el modelo para decidir cuándo usar la habilidad, por lo que indica qué abarca la habilidad y cuándo usarla.

Observa cómo se conecta la habilidad

Abre app/tools.py en el Editor de Cloud Shell:

cloudshell edit app/tools.py

Busca la función 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 lee la carpeta de habilidades: SKILL.md, references/ y scripts/.
  • SkillToolset las convierte en herramientas a las que el agente puede llamar: list_skills, load_skill, load_skill_resource y run_skill_script.
  • UnsafeLocalCodeExecutor permite que run_skill_script ejecute la secuencia de comandos de Python en esta máquina. Está bien para un lab, pero usa un ejecutor de zona de pruebas en producción.

En el siguiente paso, adjuntarás este conjunto de herramientas al evaluador.

5. 5. Escribe los agentes

Los modelos de Gemini 3 controlan el razonamiento con thinking_level (LOW, MEDIUM o HIGH) en lugar de un presupuesto de tokens. En el ADK, lo configuras con types.ThinkingConfig(thinking_level=types.ThinkingLevel.HIGH). Haz que coincida con el trabajo de cada agente: más razonamiento significa una mejor planificación, pero más latencia y costo.

El guardián (LOW)

El Gatekeeper solo ejecuta una lista de tareas determinística, por lo que necesita poco razonamiento. Reemplaza el agente de plantilla:

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",
)

Ejecuta Gatekeeper de forma local

Abre una segunda pestaña de Cloud Shell y, luego, inicia el servidor generado por Gatekeeper en el puerto 8001:

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

Regresa a la primera pestaña y observa la tarjeta de agente de A2A de Gatekeeper. Así es como otros agentes descubren qué es el Gatekeeper y qué puede hacer:

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

Deberías ver un JSON como este (abreviado):

{
  "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 y description provienen directamente de tu definición de Agent(...).
  • skills enumera lo que puede hacer el agente. El ADK agregó verify_infrastructure_checklist automáticamente con la cadena de documentación de la herramienta.
  • supportedInterfaces indica a los emisores dónde enviar las solicitudes de A2A. Por eso, iniciaste el servidor con APP_URL=http://localhost:8001: un emisor lee primero la tarjeta y, luego, envía sus solicitudes a cualquier url que anuncie la tarjeta.

No se escribió nada a mano: el servidor con scaffolding compila la tarjeta a partir de tu agente. El planificador de pistas leerá esta tarjeta para encontrar y llamar al Gatekeeper.

El evaluador (MEDIUM) y el arquitecto principal (HIGH)

El planificador de pistas tiene dos agentes:

  • El evaluador sopesa varias reglas, por lo que obtiene MEDIUM. Está unida en un AgentTool, por lo que el arquitecto principal la llama como una función y obtiene el veredicto.
  • El arquitecto principal planifica, corrige y coordina, por lo que obtiene HIGH. Llega al Gatekeeper a través de un RemoteA2aAgent, creado por create_remote_gatekeeper_agent() en app/auth.py, y también lo une en un AgentTool.

¿Por qué se deben unir ambos ayudantes como herramientas? Un AgentTool devuelve su respuesta al emisor, por lo que el arquitecto principal sigue a cargo y escribe el plano final. Si el Gatekeeper fuera un sub_agent, el Lead Architect le entregaría la conversación y el estado breve del Gatekeeper se convertiría en la respuesta final.

Abre el archivo app/agent.py del planificador de pistas:

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

Copia el siguiente código y reemplaza todo el contenido de ~/track-planner/app/agent.py con él:

"""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",
)

Ejecuta todo el sistema en Playground

Asegúrate de que Gatekeeper siga ejecutándose en la segunda pestaña. En la primera pestaña, inicia el playground del planificador de pistas:

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

Haz clic en Vista previa en la Web > Vista previa en el puerto 8080, como hiciste en el paso 3.

Solicita un circuito en un lugar que cumpla con las siguientes reglas:

Design and homologate a Formula E circuit at Tokyo Bay.

Observa el panel de eventos mientras los agentes trabajan. Deberías ver lo siguiente, en orden:

  1. get_track_topology: El arquitecto principal carga los datos de la bahía de Tokio.
  2. fia_assessor: El arquitecto principal entrega el diseño al evaluador, que llama a load_skill, load_skill_resource y run_skill_script (una vez por zona de frenado) y, luego, a evaluate_track_layout.
  3. remote_gatekeeper: El arquitecto principal llama al guardián a través de A2A. Verás la solicitud registrada en la segunda pestaña.
  4. Un plano final del arquitecto principal, en el que se enumeran todas las capas de CAD y el estado APROBADO del responsable de aprobación.

Haz clic en cualquier evento para ver sus entradas y salidas exactas.

Ahora, inicia una nueva sesión y prueba con un lugar que no cumpla con las reglas:

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

Esta vez, el evaluador rechaza la chicana de 6.8 m, el arquitecto principal propone una más ancha y el diseño corregido vuelve al evaluador antes de llegar al guardián.

Cuando termines, presiona Ctrl+C en la primera pestaña para detener la zona de pruebas. Deja que Gatekeeper se ejecute en la segunda pestaña, ya que lo necesitarás en el siguiente paso.

6. 6. Evalúa los agentes

agents-cli eval run ejecuta tu agente en un conjunto de datos, registra cada turno y llamada a la herramienta, y califica los resultados. Tú decides qué medir en tests/eval/eval_config.yaml.

Observa el conjunto de datos

Abre el conjunto de datos en el editor de Cloud Shell:

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

El archivo contiene una lista de eval_cases. Cada caso tiene un eval_case_id, un prompt (lo que pregunta el usuario) y un reference (cómo se ve una buena respuesta). El juez de LLM compara la respuesta del agente con la de referencia.

eval_case_id

Qué se prueba

fe_001_tokyo_compliant

Un lugar que cumple con todas las reglas y se aprueba

fe_002_monaco_narrow

La chicana de 6.8 m no cumple con la regla de ancho

fe_003_berlin_too_long

El circuito de 4,350 m no cumple con la regla de longitud

fe_004_missing_layers

Un diseño al que le faltan capas de CAD obligatorias

fe_005_low_regen

Una zona de frenado por debajo del objetivo de 250 kW

fe_006_sao_paulo_medical

Acceso al hospital que requiere capas de CAD médicas

fe_007_attack_mode

Una zona de Attack Mode en la línea de carrera

fe_008_multiturn_remediation

Una conversación de varios turnos: Se rechaza Mónaco, el usuario pide una corrección y se debe aprobar el diseño corregido

En el último caso, se usa agent_data en lugar de prompt. Reproduce los turnos anteriores de una conversación, por lo que puedes probar cómo el agente maneja una solicitud de seguimiento, no solo una pregunta.

track_eval_quick.json en la misma carpeta contiene solo dos casos: el caso 1 (una sola pregunta) y el caso 8 (una conversación). Ejecutarás esa porque es más rápida y usa menos cuota.

Escribe las métricas

El conjunto de datos indica qué preguntarle al agente. eval_config.yaml indica cómo calificar las respuestas. agents-cli eval run lo lee después de que el agente haya respondido cada caso y ejecuta cada métrica en cada respuesta.

Abre el archivo que generó el scaffold:

cloudshell edit tests/eval/eval_config.yaml

Ya contiene un juez de LLM genérico (custom_response_quality) y una métrica agent_turn_count que solo cuenta los turnos. Ese es un buen comienzo para cualquier agente, pero no dice nada sobre la Fórmula E. Mantendrás el juez y agregarás una métrica que verifique una regla específica para este dominio.

Las buenas evaluaciones suelen mezclar dos tipos de métricas:

Tipo

Cómo se calcula la puntuación

Ideal para

Desventajas

LLM-as-a-judge

Un modelo lee la respuesta y el reference del caso y, luego, otorga una puntuación del 1 al 5 con una explicación

Cualidades difusas: ¿La respuesta es correcta, completa y clara?

Cuesta una llamada al modelo por caso; las puntuaciones pueden variar ligeramente entre ejecuciones

Métrica de código

Una pequeña función de Python verifica la respuesta.

Datos exactos y verificables: ¿La respuesta incluyó algo que debe estar allí?

Solo verifica lo que puedes expresar en el código.

Reemplaza el contenido del archivo por lo siguiente:

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"),
          }

Qué hace cada parte:

  • metrics_to_run enumera las métricas que se ejecutarán, por nombre. Si quitas un nombre aquí, se desactivará esa métrica sin borrarla.
  • custom_response_quality es el juez de LLM. custom_function_file apunta a tests/eval/response_quality.py, que generó el scaffold. Ábrelo si quieres ver la instrucción de evaluación.
  • fia_cad_layer_compliance es la métrica de tu código, escrita de forma intercalada en custom_function. Para cada caso, recibe un instance (el mensaje, la response final del agente y la reference). En los casos en los que la respuesta esperada es una aprobación, verifica que la respuesta nombre las cuatro capas de CAD obligatorias y enumere las que falten. Por ejemplo, un plano que omite F1_TEC3 obtiene una puntuación de 0.75. En otros casos, se obtiene una puntuación de 1.0, ya que un rechazo o una respuesta en el modo de ataque no necesitan enumerar todas las capas.

¿Por qué verificar las capas de CAD? Un plano aprobado que omite una capa obligatoria es incorrecto, incluso si se lee bien, y un juez de LLM puede pasarlo por alto. Una verificación de código aplica la misma regla de la misma manera en cada ejecución.

Ejecutar la evaluación

Mantén el Gatekeeper local en ejecución en la segunda pestaña y, luego, ejecuta lo siguiente:

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

La evaluación tarda unos minutos. Cada caso ejecuta todo el flujo de varios agentes, alrededor de diez llamadas al modelo con el arquitecto principal en HIGH, por lo que Step 1/2: eval generate es la parte lenta. No está atascado.

Cuando finalice, abre el informe HTML en artifacts/grade_results/ con el editor de Cloud Shell. Las puntuaciones bajas de fia_cad_layer_compliance indican que la respuesta final del arquitecto principal omite capas. Intenta ajustar su instrucción y vuelve a ejecutar la evaluación.

7. 7. Implementa el Gatekeeper en Agent Runtime

Solo implementas el Gatekeeper. El planificador de pistas se sigue ejecutando en Cloud Shell y lo llama.

Abre una pestaña nueva de Cloud Shell

La implementación tarda entre 5 y 8 minutos, por lo que debes hacerlo en una pestaña nueva mientras finaliza la evaluación del paso 6:

  1. Haz clic en Abrir una pestaña nueva (+) en la barra de herramientas de Cloud Shell.
  2. En una pestaña nueva, no se conservan las variables del paso 2, por lo que debes volver a configurarlas:
    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"
    

Deja el Gatekeeper local en ejecución en la segunda pestaña: la evaluación aún lo usa y la implementación no necesita que se detenga.

Implementar

En la pestaña nueva, implementa Gatekeeper. Se ejecuta como la cuenta de servicio predeterminada de Compute Engine del proyecto:

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

La implementación tarda entre 5 y 8 minutos. No cierres la pestaña de Cloud Shell mientras se ejecuta. Si se desconecta la transmisión de registros, verifica el progreso con agents-cli deploy --status. Mientras esperas, vuelve al paso 6 y lee tu informe de evaluación.

Inspecciona la identidad del agente

agents-cli registra el recurso implementado en 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 segunda línea del resultado es la identidad con la que se ejecuta Gatekeeper:

Implementado con

Ejemplo de resultado:

La cuenta de servicio (predeterminada)

123456789012-compute@developer.gserviceaccount.com

Identidad del agente

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

La cuenta de servicio se comparte con todo lo que la use en el proyecto. Una identidad de agente pertenece a este agente, por lo que puedes otorgarle exactamente los roles que necesita y ver sus acciones por separado en los registros de auditoría.

8. 8. Conéctate al Gatekeeper implementado a través de A2A

Agent Runtime entrega las rutas de A2A generadas por Gatekeeper detrás de una ruta de acceso /api autenticada.

Asegúrate de que la evaluación del paso 6 haya finalizado y, luego, detén el Gatekeeper local en la segunda pestaña (Ctrl + C). A partir de ahora, el planificador de pistas llama al que se implementó. En la primera pestaña, apunta el planificador de pistas hacia ella:

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

Por qué el cliente actualiza su token

Abre app/auth.py. Los tokens de acceso de Google Cloud vencen después de una hora, por lo que un cliente que establece un encabezado Authorization fijo en el inicio comienza a fallar con 401 Unauthorized después de una hora. GoogleAuthRefresh hereda de httpx.Auth y verifica las credenciales en cada solicitud, actualizándolas cuando sea necesario. create_remote_gatekeeper_agent() solo lo adjunta para las URLs de https://.

Ejecútalo de extremo a extremo

Vuelve a iniciar la zona de pruebas para que se aplique el nuevo GATEKEEPER_A2A_URL:

agents-cli playground --port 8080

Haz clic en Vista previa en la Web > Vista previa en el puerto 8080, inicia una sesión nueva y pregunta lo siguiente:

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

Los eventos se ven igual que en el paso 5, pero la llamada remote_gatekeeper ahora se dirige al Gatekeeper en Agent Runtime en lugar de a la segunda pestaña.

Observa cómo llega la llamada a Agent Runtime

Demuestra que la solicitud realmente llegó al Gatekeeper implementado a través de A2A:

  1. En la consola de Cloud, busca Agent Platform y, luego, abre Implementaciones en el menú de la izquierda.
  2. Selecciona gatekeeper.
  3. Abre la pestaña Sesiones.

Verás una sesión nueva cuyo ID de usuario comienza con A2A_USER_. El servidor de A2A de Gatekeeper lo creó cuando lo llamó tu planificador de pistas. Abre la pestaña Seguimientos en la misma página para ver la llamada al modelo de Gatekeeper y su llamada a la herramienta verify_infrastructure_checklist para esa solicitud.

Opcional: Implementa también el planificador de recorridos

Hasta ahora, el planificador de pistas se ejecuta en Cloud Shell, por lo que sus llamadas de A2A al Gatekeeper usan tus credenciales. Implementa también el planificador y ambos agentes se ejecutarán en Agent Runtime, llamándose entre sí con sus propias identidades implementadas.

Presiona Ctrl + C para detener la zona de pruebas y, luego, implementa el planificador de la misma manera que el 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

Ambos agentes se ejecutan como la misma cuenta de servicio de Compute Engine. Mantén --service-account: sin él, el planificador se ejecuta como el agente de servicio predeterminado de Agent Runtime y no puede llamar al Gatekeeper (consulta ¿Por qué esta cuenta de servicio? en el paso 7).

Cómo el planificador implementado se comunica con el responsable de filtrar las comunicaciones:

  • agents-cli deploy copia los valores de .env en la implementación, por lo que el planificador obtiene el GATEKEEPER_A2A_URL que agregaste anteriormente.
  • Dentro de Agent Runtime, GoogleAuthRefresh en app/auth.py toma la identidad implementada del planificador en lugar de la tuya.

La implementación tarda entre 5 y 8 minutos. Cuando finalice, verifica con qué identidad se ejecuta el planificador:

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

Para probar el sistema implementado, haz lo siguiente:

  1. En la consola de Cloud, vuelve a Agent Platform > Implementaciones.
  2. Selecciona track-planner y abre la pestaña Playground.
  3. Escribe lo siguiente:
    Design and homologate a Formula E circuit at Tokyo Bay.
    
  4. Espera a que el plano tenga el estado APROBADO de Gatekeeper.
  5. Regresa a Implementaciones, selecciona gatekeeper y abre la pestaña Sesiones. La sesión de A2A_USER_ más reciente provino del planificador implementado, no de Cloud Shell.

Cuando realices la limpieza en el siguiente paso, borra también el planificador:

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

9. 9. Solución de problemas y limpieza

Solución de problemas

Síntoma

Corregir

UREQ_TOS_NOT_ACCEPTED

Acepta las Condiciones del Servicio en la consola de Cloud y, luego, vuelve a intentarlo.

El proyecto es (unset)

Vuelve a ejecutar los comandos del proyecto del paso 2.

service account info is missing 'email' field

Ejecuta gcloud auth application-default login (paso 2) y, luego, reinicia los servidores que estén en ejecución.

FileNotFoundError para SKILL.md

Completa el paso 4 o copia $ASSETS/solution/track-planner/. en ~/track-planner/.

De Connection refused a localhost:8001

Inicia el Gatekeeper local (paso 5) o establece GATEKEEPER_A2A_URL en el implementado (paso 8).

Permission 'resourcemanager.projects.setIamPolicy' denied en la implementación

Tu cuenta no puede usar la identidad del agente. Sigue las instrucciones del cuadro de limpieza del paso 7 y, luego, usa la cuenta de servicio deploy.

El planificador de pistas implementado informa 403 Forbidden desde el Gatekeeper

La identidad del planificador no tiene permiso para llamar al Gatekeeper. Vuelve a implementarla con --service-account, como en el paso 7, o con --agent-identity si eres el propietario del proyecto.

429 RESOURCE_EXHAUSTED

Alcanzaste la cuota de Gemini. Espera un minuto o agrega --concurrency 1 --qps 2 a agents-cli eval run.

Address already in use

Detén el otro servidor o elige otro --port.

Limpia

Borra el Gatekeeper implementado para que no siga ejecutándose:

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 creaste un proyecto solo para este codelab, puedes borrar todo el proyecto. Si estás en un evento, conserva el proyecto que te asignaron hasta que los organizadores indiquen lo contrario.

Qué aprendiste

  • agents-cli scaffold create te brinda un proyecto de ADK estándar y que se puede implementar. Solo debes escribir agentes, habilidades y evaluaciones.
  • Las habilidades de los agentes mantienen el conocimiento del dominio fuera de las instrucciones hasta que el agente lo necesita.
  • thinking_level te permite cambiar la profundidad del razonamiento por la velocidad por agente.
  • agents-cli eval run convierte la pregunta "¿funciona?" en puntuaciones de las que puedes hacer un seguimiento.
  • La identidad del agente y A2A permiten que los agentes implementados de forma independiente trabajen juntos de forma segura.

Más información