Criar um planejador de pistas de Fórmula E multiagente com ADK e A2A

1. 1. Visão geral

Neste codelab, você vai criar um sistema multiagente que projeta um circuito de rua da Fórmula E e o aprova de acordo com um conjunto simplificado de regras da FIA.

Você vai usar o agents-cli para gerar dois projetos padrão do Kit de Desenvolvimento de Agente (ADK) e, em seguida, escrever as partes que importam: uma habilidade de agente, três agentes e uma avaliação. Todo o resto (layout do projeto, servidor, dependências, configuração de implantação) vem da CLI.

Não conhece a Fórmula E?

Você não precisa entender de automobilismo. Estes cinco termos são suficientes:

  • Fórmula E: o campeonato de corrida totalmente elétrico da FIA. A maioria das corridas acontece em circuitos de rua temporários nos centros das cidades.
  • Homologação: a aprovação oficial da FIA de que um circuito é seguro para corridas. Sem homologação, sem corrida.
  • Frenagem regenerativa: quando o carro freia, os motores recarregam a bateria. Uma frenagem mais brusca recupera mais energia. Por isso, as regras deste codelab exigem pelo menos uma zona de frenagem brusca.
  • Camadas de CAD: camadas nomeadas nos desenhos de design do circuito, como a linha de corrida, o pit lane e as barreiras. A FIA verifica se os obrigatórios estão presentes.
  • Modo de ataque: os pilotos recebem um aumento temporário de potência ao passar por uma zona de ativação fora da linha de corrida.

Seus agentes jogam dos dois lados: um projeta o circuito e dois verificam se ele está de acordo com a FIA.

O que você criará

Arquitetura: no projeto track-planner, em execução no Cloud Shell, o agente track_planner usa get_track_topology, fia_assessor e remote_gatekeeper. remote_gatekeeper chama o agente gatekeeper por A2A. No projeto gatekeeper, em execução no Agent Runtime, o agente gatekeeper usa verify_infrastructure_checklist.

As caixas verdes são agentes, e as caixas tracejadas são as ferramentas deles, da mesma forma que o ADK Playground as desenha. Duas das ferramentas envolvem outros agentes: fia_assessor é o agente FIA Assessor com a habilidade de conformidade com a FIA, e remote_gatekeeper chama o agente Gatekeeper no outro projeto por A2A.

Agente

Projeto

Job

thinking_level

Lead Architect

track-planner

Lê o local, pergunta ao Assessor, corrige problemas, pede aprovação ao Gatekeeper

HIGH

Avaliador da FIA

track-planner

Verifica o layout usando a habilidade de FIA e uma ferramenta de pontuação

MEDIUM

Gatekeeper do Gêmeo Digital

gatekeeper

Executa a lista de verificação de homologação final

LOW

O que você vai aprender

  • Como o agents-cli scaffold create organiza um projeto do ADK e quais arquivos você edita.
  • Como empacotar o conhecimento de domínio como uma Habilidade do Agente (SKILL.md, references/, scripts/) e carregá-lo com o SkillToolset do ADK.
  • Como associar thinking_level ao job de cada agente.
  • Como avaliar agentes com o agents-cli eval run.
  • Como implantar um agente no Agent Runtime, como a identidade do agente é diferente de uma conta de serviço e como chamar o agente implantado pelo protocolo A2A.

2. 2. Configurar o ambiente

Antes de começar

Você precisa de um projeto na nuvem do Google Cloud com o faturamento ativado. Se você estiver seguindo este codelab em um evento, use a conta e o projeto que recebeu.

Abrir o Cloud Shell e definir seu projeto

Abra o Cloud Shell e execute:

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

Criar credenciais para seus agentes

A gcloud já está conectada, mas o código Python executado pelos agentes usa o Application Default Credentials (ADC), que precisa de um login separado:

gcloud auth application-default login

Abra o link que aparece, faça login com a mesma conta, copie o código e cole-o de volta no Cloud Shell. Em seguida, defina o projeto em que a cota é faturada:

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

Ative as 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 que serve cada API:

APIs

Usado para

aiplatform

Modelos do Gemini e Agent Runtime

cloudresourcemanager, serviceusage, iam

As verificações de projeto e permissão que gcloud e agents-cli deploy executam

compute

Cria a conta de serviço padrão do Compute Engine que a implantação na Etapa 7 usa.

storage, logging, cloudtrace

Armazenamento, registros e traces, conforme recomendado pelo guia de configuração do Agent Runtime

cloudbuild, artifactregistry

Compilações e imagens de contêiner, incluídas como precaução para a implantação na Etapa 7

Muitos projetos já têm algumas dessas opções ativadas. Ativar uma API que já está ativada não muda nada.

Instalar uv e 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 foi testado com o agents-cli 1.10.0. Instale essa versão para receber os mesmos arquivos gerados.

3. 3. Criar o scaffold dos projetos e adicionar os recursos

Gerar os dois projetos

O agents-cli scaffold create gera um projeto do ADK completo e implantável. Execute-o duas vezes, uma para cada sistema de agente.

Primeiro, o planejador de trilhas:

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

Depois, o recepcionista:

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

Sinalização

Significado

--agent adk

Começar com o modelo do ADK para Python

--deployment-target agent_runtime

Gerar o servidor e a configuração para o Agent Runtime

--prototype

Pular CI/CD; manter o projeto mínimo

--yes

Aceitar os padrões sem solicitar

Analisar o layout

O projeto gerado tem esta aparência. Você só edita os arquivos marcados com ✍️:

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 uma descrição de cada arquivo gerado, consulte Estrutura do projeto no guia da CLI de agentes.

Testar o agente gerado

Antes de mudar qualquer coisa, execute o agente de modelo para ver o que o scaffold oferece. Instale as dependências e inicie o playground local na porta 8080:

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

Para abrir, clique no botão Visualização da Web na barra de ferramentas do Cloud Shell (o ícone de olho no canto superior direito) e selecione Visualizar na porta 8080.

No playground, faça ao agente de modelo uma pergunta que as ferramentas dele possam responder, por exemplo:

What's the weather in San Francisco?

O painel lateral mostra cada evento, incluindo a chamada de ferramenta get_weather e o resultado dela. Você vai usar a mesma visualização mais tarde para acompanhar seus próprios agentes.

Quando terminar, volte ao terminal e pressione Ctrl+C para interromper o playground.

Adicionar os recursos da Fórmula E

Os dados do local, as regras da FIA, o script de física, as ferramentas e os conjuntos de dados de avaliação não são a lição. Por isso, você vai copiá-los do GitHub em vez de digitá-los:

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 é uma tag de lançamento fixa, então todos recebem os mesmos arquivos. $ASSETS foi definido na Etapa 2 e aponta para a pasta que você acabou de clonar.

Isso adiciona os seguintes arquivos:

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

Instalar e testar

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

O agents-cli install executa o uv sync para criar um ambiente virtual para cada projeto. Todos os testes de unidade precisam ser aprovados.

4. 4. Escrever a habilidade de conformidade com a FIA

Uma habilidade de agente agrupa instruções, documentos de referência e scripts em uma pasta. O SkillToolset do ADK o carrega em três níveis, para que o agente leia apenas o que precisa:

Nível

Arquivo

Quando o agente vê

1

SKILL.md front matter (name, description)

Sempre (algumas dezenas de tokens)

2

Corpo SKILL.md

Quando ele chama load_skill

3

references/, scripts/

Quando ele chama load_skill_resource ou run_skill_script

A habilidade está em app/skills/ porque o Dockerfile gerado só copia app/ para a imagem implantada.

Analisar os arquivos do nível 3

Você copiou os arquivos de nível 3 da habilidade na Etapa 3:

  • references/track_regulations.md contém as regras que o Assessor verifica: limites de comprimento e largura, qual barreira cada zona de frenagem precisa, as camadas CAD obrigatórias (além das camadas médicas para rotas hospitalares), a meta de regeneração e as regras do Modo de Ataque.
  • scripts/calculate_regen_zones.py é uma calculadora. Com base na velocidade de entrada, na velocidade de saída e na distância de frenagem de uma zona de frenagem, ele calcula a energia recuperada e a barreira que a curva precisa, para que o agente nunca precise adivinhar a física.

Execute o script para a curva 10 de Londres, em que os carros freiam de 215 para 55 km/h em 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

O carro recupera cerca de 508 kW aqui, acima da meta de 250 kW. Ele desacelera a mais de 2G, então a curva precisa de barreiras 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"}

Gravar SKILL.md

Crie o arquivo que une a habilidade:

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

Adicione este conteúdo:

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

O description é o que o modelo lê para decidir quando usar a habilidade. Por isso, ele informa o que a habilidade abrange e quando usá-la.

Veja como a habilidade está conectada

Abra app/tools.py no editor do Cloud Shell:

cloudshell edit app/tools.py

Encontre a função 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 lê a pasta de habilidades: SKILL.md, references/ e scripts/.
  • O SkillToolset as transforma em ferramentas que o agente pode chamar: list_skills, load_skill, load_skill_resource e run_skill_script.
  • UnsafeLocalCodeExecutor permite que run_skill_script execute o script Python nesta máquina. Isso é aceitável para um laboratório, mas use um executor em sandbox na produção.

Você vai anexar esse conjunto de ferramentas ao Assessor na próxima etapa.

5. 5. Escrever os agentes

Os modelos do Gemini 3 controlam o raciocínio com thinking_level (LOW, MEDIUM ou HIGH) em vez de um limite de tokens. No ADK, você define isso com types.ThinkingConfig(thinking_level=types.ThinkingLevel.HIGH). Combine isso com o trabalho de cada agente: mais reflexão significa um planejamento melhor, mas também mais latência e custo.

O Gatekeeper (LOW)

O Gatekeeper só executa uma lista de verificação determinística, então precisa de pouco raciocínio. Substitua o agente de modelo:

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

Executar o Gatekeeper localmente

Abra uma segunda guia do Cloud Shell e inicie o servidor gerado pelo Gatekeeper na porta 8001:

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

De volta à primeira guia, confira o card de agente A2A do Gatekeeper. É assim que outros agentes descobrem o que é o Gatekeeper e o que ele pode fazer:

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

Você verá um 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 e description vêm diretamente da definição de Agent(...).
  • skills lista o que o agente pode fazer. O ADK adicionou verify_infrastructure_checklist automaticamente, usando a docstring da ferramenta.
  • supportedInterfaces informa aos autores das chamadas para onde enviar solicitações A2A. Por isso, você iniciou o servidor com APP_URL=http://localhost:8001: um autor de chamada lê o cartão primeiro e, em seguida, envia as solicitações para qualquer url que o cartão anunciar.

Nada aqui foi escrito à mão: o servidor estruturado cria o card com base no seu agente. O planejador de trajetos vai ler esse cartão para encontrar e chamar o Gatekeeper.

O avaliador (MEDIUM) e o arquiteto líder (HIGH)

O planejador de trajetos tem dois agentes:

  • O Avaliador considera várias regras, então recebe MEDIUM. Ele é encapsulado em um AgentTool, então o Lead Architect o chama como uma função e recebe o veredito de volta.
  • O arquiteto-chefe planeja, corrige e coordena, então ele recebe HIGH. Ele chega ao Gatekeeper por um RemoteA2aAgent, criado por create_remote_gatekeeper_agent() em app/auth.py, e também o envolve em um AgentTool.

Por que encapsular os dois auxiliares como ferramentas? Um AgentTool retorna a resposta ao autor da chamada, então o arquiteto principal continua no comando e escreve o projeto final. Se o Gatekeeper fosse um sub_agent, o Lead Architect entregaria a conversa para ele, e o status curto do Gatekeeper se tornaria a resposta final.

Abra o arquivo app/agent.py do planejador de trajetos:

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

Copie o código a seguir e substitua todo o conteúdo de ~/track-planner/app/agent.py por ele:

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

Executar todo o sistema no playground

Verifique se o Gatekeeper ainda está em execução na segunda guia. Na primeira guia, inicie o playground do planejador de trajetos:

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

Clique em Visualização na Web > Visualizar na porta 8080, como você fez na Etapa 3.

Peça um circuito em um local que atenda às regras:

Design and homologate a Formula E circuit at Tokyo Bay.

Observe o painel de eventos enquanto os agentes trabalham. Você vai ver, em ordem:

  1. get_track_topology: o arquiteto-chefe carrega os dados da Baía de Tóquio.
  2. fia_assessor: o arquiteto-chefe entrega o layout ao avaliador, que chama load_skill, load_skill_resource e run_skill_script (uma vez por zona de frenagem) e, em seguida, evaluate_track_layout.
  3. remote_gatekeeper: o arquiteto líder chama o Gatekeeper pelo A2A. A solicitação vai aparecer registrada na segunda guia.
  4. Um projeto final do arquiteto-chefe, listando todas as camadas de CAD e o status APROVADO do Gatekeeper.

Clique em qualquer evento para ver as entradas e saídas exatas.

Agora inicie uma nova sessão e tente um local que viole as regras:

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

Desta vez, o avaliador rejeita a chicane de 6,8 m, o arquiteto líder propõe uma mais larga, e o layout corrigido volta para o avaliador antes de chegar ao gatekeeper.

Quando terminar, pressione Ctrl+C na primeira guia para interromper o playground. Deixe o Gatekeeper em execução na segunda guia: você vai precisar dele na próxima etapa.

6. 6. Avaliar os agentes

O agents-cli eval run executa o agente em um conjunto de dados, registra cada turno e chamada de ferramenta e avalia os resultados. Você decide o que medir no tests/eval/eval_config.yaml.

Analisar o conjunto de dados

Abra o conjunto de dados no editor do Cloud Shell:

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

O arquivo contém uma lista de eval_cases. Cada caso tem um eval_case_id, um prompt (o que o usuário pergunta) e um reference (como é uma boa resposta). O juiz LLM compara a resposta do agente com a referência.

eval_case_id

O que é testado

fe_001_tokyo_compliant

Um local que segue todas as regras e é aprovado

fe_002_monaco_narrow

A chicane de 6,8 m não segue a regra de largura

fe_003_berlin_too_long

O circuito de 4.350 m não obedece à regra de comprimento

fe_004_missing_layers

Um layout sem as camadas CAD obrigatórias

fe_005_low_regen

Uma zona de frenagem abaixo da meta de 250 kW

fe_006_sao_paulo_medical

Acesso ao hospital que precisa de camadas de CAD médico

fe_007_attack_mode

Uma zona de Modo de Ataque na linha de corrida

fe_008_multiturn_remediation

Uma conversa com várias interações: o layout de Mônaco é rejeitado, o usuário pede uma correção e o layout corrigido é aprovado.

O último caso usa agent_data em vez de prompt. Ele repete as interações anteriores de uma conversa para que você possa testar como o agente lida com uma solicitação de acompanhamento, não apenas com uma única pergunta.

track_eval_quick.json na mesma pasta contém apenas dois casos: o caso 1 (uma única pergunta) e o caso 8 (uma conversa). Você vai executar esse porque ele é mais rápido e usa menos cota.

Gravar as métricas

O conjunto de dados diz o que perguntar ao agente. eval_config.yaml diz como pontuar as respostas. O agents-cli eval run lê o arquivo depois que o agente responde a todos os casos e executa cada métrica em cada resposta.

Abra o arquivo gerado pelo scaffold:

cloudshell edit tests/eval/eval_config.yaml

Ele já contém um avaliador de LLM genérico (custom_response_quality) e uma métrica agent_turn_count que conta apenas as interações. Esse é um bom começo para qualquer agente, mas não diz nada sobre a Fórmula E. Você vai manter o avaliador e adicionar uma métrica que verifica uma regra específica para esse domínio.

As boas avaliações geralmente combinam dois tipos de métricas:

Tipo

Como é a pontuação

Ideal para

Compensação

LLM-as-a-judge

Um modelo lê a resposta e o reference do caso e atribui uma pontuação de 1 a 5 com uma explicação.

Qualidades imprecisas: a resposta está correta, completa e clara?

Custa uma chamada de modelo por caso; as pontuações podem variar um pouco entre as execuções

Métrica de código

Uma pequena função Python verifica a resposta

Fatos exatos e verificáveis: a resposta incluiu algo que precisa estar lá?

Verifica apenas o que você pode expressar em código

Substitua o conteúdo do arquivo por:

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

O que cada parte faz:

  • metrics_to_run lista as métricas a serem executadas, por nome. Remover um nome aqui desativa a métrica sem excluí-la.
  • custom_response_quality é o LLM Judge. custom_function_file aponta para tests/eval/response_quality.py, que foi gerado pelo scaffold. Abra-o se quiser ver o comando de avaliação.
  • fia_cad_layer_compliance é a métrica do código, escrita inline em custom_function. Para cada caso, ele recebe um instance (o comando, o response final do agente e o reference). Para casos em que a resposta esperada é uma aprovação, ele verifica se a resposta nomeia todas as quatro camadas obrigatórias do CAD e lista as que estão faltando. Um blueprint que deixa de fora F1_TEC3, por exemplo, tem uma pontuação de 0,75. Outros casos recebem uma pontuação de 1,0, porque uma rejeição ou uma resposta do Modo de Ataque não precisa listar todas as camadas.

Por que verificar as camadas de CAD? Um blueprint aprovado que deixa de fora uma camada obrigatória está errado, mesmo que seja bem escrito, e um juiz de LLM pode ignorar isso. Uma verificação de código aplica a mesma regra da mesma maneira em cada execução.

Executar a avaliação

Mantenha o Gatekeeper local em execução na segunda guia e execute:

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

A avaliação leva alguns minutos. Cada caso executa todo o fluxo multiagente, cerca de dez chamadas de modelo com o arquiteto principal no pensamento HIGH, então Step 1/2: eval generate é a parte lenta. Não está travado.

Quando terminar, abra o relatório HTML em artifacts/grade_results/ com o editor do Cloud Shell. Pontuações baixas de fia_cad_layer_compliance indicam que a resposta final do arquiteto principal está deixando de fora camadas. Tente restringir as instruções e executar a avaliação novamente.

7. 7. Implantar o Gatekeeper no Agent Runtime

Você só implanta o Gatekeeper. O planejador de trajetos continua em execução no Cloud Shell e o chama.

Abrir uma nova guia do Cloud Shell.

A implantação leva de 5 a 8 minutos. Por isso, faça isso em uma nova guia enquanto a avaliação da Etapa 6 é concluída:

  1. Clique em Abrir uma nova guia (+) na barra de ferramentas do Cloud Shell.
  2. Uma nova guia não mantém as variáveis da Etapa 2, então defina-as novamente:
    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"
    

Deixe o Gatekeeper local em execução na segunda guia: a avaliação ainda o usa, e a implantação não precisa que ele seja interrompido.

Implantar

Na nova guia, implante o Gatekeeper. Ele é executado como a conta de serviço padrão do Compute Engine do projeto:

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

A implantação leva de 5 a 8 minutos. Não feche a guia do Cloud Shell enquanto ele estiver em execução. Se o fluxo de registros for desconectado, verifique o progresso com agents-cli deploy --status. Enquanto espera, volte para a Etapa 6 e leia o relatório de avaliação.

Inspecionar a identidade do agente

agents-cli registra o recurso implantado em 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

A segunda linha de saída é a identidade em que o Gatekeeper é executado:

Implantado com

Exemplo de saída

A conta de serviço (padrão)

123456789012-compute@developer.gserviceaccount.com

Identidade do agente

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

A conta de serviço é compartilhada por tudo no projeto que a utiliza. Uma identidade de agente pertence a esse agente, então é possível conceder a ela exatamente os papéis necessários e ver as ações dela separadamente nos registros de auditoria.

8. 8. Conectar-se ao Gatekeeper implantado por A2A

O Agent Runtime disponibiliza as rotas A2A geradas pelo Gatekeeper por um caminho /api autenticado.

Verifique se a avaliação da Etapa 6 foi concluída e interrompa o Gatekeeper local na segunda guia (Ctrl+C). A partir de agora, o planejador de trajetos chama o que foi implantado. Na primeira guia, aponte o planejador de trajetos para ele:

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 que o cliente atualiza o token

Abra app/auth.py. Os tokens de acesso do Google Cloud expiram após uma hora. Portanto, um cliente que define um cabeçalho Authorization fixo na inicialização começa a falhar com 401 Unauthorized após uma hora. GoogleAuthRefresh é uma subclasse de httpx.Auth e verifica as credenciais em todas as solicitações, atualizando-as quando necessário. create_remote_gatekeeper_agent() só o anexa para URLs https://.

Executar de ponta a ponta

Inicie o playground novamente para que ele use o novo GATEKEEPER_A2A_URL:

agents-cli playground --port 8080

Clique em Visualização na Web > Visualizar na porta 8080, inicie uma nova sessão e pergunte:

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

Os eventos são os mesmos da Etapa 5, mas a chamada remote_gatekeeper agora vai para o Gatekeeper no Agent Runtime, em vez da segunda guia.

Ver a chamada chegar no Agent Runtime

Prove que a solicitação realmente chegou ao Gatekeeper implantado por A2A:

  1. No console do Cloud, pesquise Plataforma de Agentes e abra Implantações no menu à esquerda.
  2. Selecione gatekeeper.
  3. Abra a guia Sessões.

Você vai encontrar uma nova sessão em que o ID do usuário começa com A2A_USER_. O servidor A2A do Gatekeeper o criou quando foi chamado pelo planejador de trajetos. Abra a guia Traces na mesma página para ver a chamada de modelo do Gatekeeper e a chamada de ferramenta verify_infrastructure_checklist para essa solicitação.

Opcional: implantar o planejador de trajetos também

Até agora, o planejador de trajetos é executado no Cloud Shell, então as chamadas A2A para o Gatekeeper usam as suas credenciais. Implante o planejador também, e os dois agentes serão executados no Agent Runtime, chamando um ao outro com as próprias identidades implantadas.

Pressione Ctrl+C para interromper o playground e implante o planejador da mesma forma que o 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

Os dois agentes são executados como a mesma conta de serviço do Compute Engine. Mantenha --service-account: sem ele, o planejador é executado como o agente de serviço padrão do Agent Runtime e não pode chamar o Gatekeeper (consulte Por que essa conta de serviço? na Etapa 7).

Como o planejador implantado alcança o Gatekeeper:

  • agents-cli deploy copia os valores em .env para a implantação, de modo que o planejador receba o GATEKEEPER_A2A_URL que você adicionou acima.
  • No Agent Runtime, GoogleAuthRefresh em app/auth.py usa a identidade implantada do planejador em vez da sua.

A implantação leva de 5 a 8 minutos. Quando terminar, verifique qual identidade o planejador executa:

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 testar o sistema implantado:

  1. No console do Cloud, volte para Plataforma de Agentes > Implantações.
  2. Selecione track-planner e abra a guia Playground.
  3. Insira o seguinte:
    Design and homologate a Formula E circuit at Tokyo Bay.
    
  4. Aguarde o blueprint com o status APROVADO do Gatekeeper.
  5. Volte para Implantações, selecione gatekeeper e abra a guia Sessões. A sessão mais recente do A2A_USER_ veio do planejador implantado, não do Cloud Shell.

Ao fazer a limpeza na próxima etapa, exclua também o planejador:

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

9. 9. Solução de problemas e limpeza

Solução de problemas

Sintoma

Correção

UREQ_TOS_NOT_ACCEPTED

Aceite os Termos de Serviço no console do Cloud e tente novamente.

O projeto é (unset)

Execute novamente os comandos do projeto na Etapa 2.

service account info is missing 'email' field

Execute gcloud auth application-default login (Etapa 2) e reinicie todos os servidores em execução.

FileNotFoundError para SKILL.md

Conclua a Etapa 4 ou copie $ASSETS/solution/track-planner/. para ~/track-planner/.

Connection refused a localhost:8001

Inicie o Gatekeeper local (Etapa 5) ou defina GATEKEEPER_A2A_URL como o implantado (Etapa 8).

Permission 'resourcemanager.projects.setIamPolicy' denied na implantação

Sua conta não pode usar a identidade do agente. Siga as instruções de limpeza na Etapa 7 e use a implantação da conta de serviço.

O planejador de faixa implantado informa 403 Forbidden do Gatekeeper

A identidade do planejador não tem permissão para chamar o Gatekeeper. Implante-o novamente com --service-account, como na etapa 7, ou com --agent-identity, se você for o proprietário do projeto.

429 RESOURCE_EXHAUSTED

Você atingiu a cota do Gemini. Aguarde um minuto ou adicione --concurrency 1 --qps 2 a agents-cli eval run.

Address already in use

Interrompa o outro servidor ou escolha outro --port.

Limpar

Exclua o Gatekeeper implantado para que ele não continue em execução:

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"

Se você criou um projeto apenas para este codelab, exclua o projeto inteiro. Se você estiver em um evento, mantenha o projeto que recebeu até que os organizadores digam o contrário.

O que você aprendeu

  • O agents-cli scaffold create oferece um projeto do ADK padrão e implantável. Você só precisa escrever agentes, habilidades e avaliações.
  • As habilidades do agente mantêm o conhecimento do domínio fora dos comandos até que o agente precise dele.
  • O thinking_level permite que você troque a profundidade do raciocínio pela velocidade por agente.
  • O agents-cli eval run transforma a pergunta "isso funciona?" em pontuações que você pode acompanhar.
  • A identidade do agente e o A2A permitem que agentes implantados de forma independente trabalhem juntos com segurança.

Saiba mais