Tworzenie wieloagentowego planera torów Formuły E za pomocą pakietu ADK i protokołu A2A

1. 1. Przegląd

W tym ćwiczeniu utworzysz system wieloagentowy, który projektuje tor uliczny Formuły E i zatwierdza go zgodnie z uproszczonym zestawem przepisów FIA.

Użyjesz agents-cli do wygenerowania 2 standardowych projektów pakietu Agent Development Kit (ADK), a następnie napiszesz najważniejsze części: umiejętność agenta, 3 agentów i ocenę. Wszystko inne (układ projektu, serwer, zależności, konfiguracja wdrożenia) pochodzi z interfejsu wiersza poleceń.

Nie znasz Formuły E?

Nie musisz znać się na sportach motorowych. Wystarczy tych 5 haseł:

  • Formuła E: mistrzostwa FIA w wyścigach samochodów elektrycznych. Większość wyścigów odbywa się na tymczasowych torach ulicznych w centrach miast.
  • Homologacja: oficjalne zatwierdzenie przez FIA, że tor jest bezpieczny do wyścigów. Bez homologacji nie ma wyścigu.
  • Hamowanie regeneracyjne: gdy samochód hamuje, silniki ładują akumulator. Mocniejsze hamowanie pozwala odzyskać więcej energii, dlatego reguły tego ćwiczenia wymagają co najmniej 1 strefy mocnego hamowania.
  • Warstwy CAD: nazwane warstwy w rysunkach projektowych toru, takie jak linia wyścigowa, aleja serwisowa i bariery. FIA sprawdza, czy są tam te obowiązkowe.
  • Tryb ataku: kierowcy otrzymują tymczasowe zwiększenie mocy, gdy przejeżdżają przez strefę aktywacji umieszczoną poza linią wyścigu.

Agenty odgrywają obie role: jeden projektuje tor, a drugi sprawdza go pod kątem zgodności z wymaganiami FIA.

Co utworzysz

Architektura: w projekcie track-planner, który działa w Cloud Shell, agent track_planner używa narzędzi get_track_topology, fia_assessor i remote_gatekeeper. remote_gatekeeper wywołuje agenta gatekeeper przez A2A. W projekcie strażnika, który działa w Agent Runtime, agent strażnika używa narzędzia verify_infrastructure_checklist.

Zielone pola to agenty, a pola z przerywaną linią to ich narzędzia, tak samo jak w przypadku ADK Playground. Dwa narzędzia opakowują innych agentów: fia_assessor to agent FIA Assessor z umiejętnością zgodności z FIA, a remote_gatekeeper wywołuje agenta Gatekeeper w innym projekcie przez A2A.

Agent

Projekt

Zadanie

thinking_level

Główny architekt

track-planner

Odczytuje miejsce, pyta rzeczoznawcę, rozwiązuje problemy, prosi strażnika o zatwierdzenie

HIGH

Asesor FIA

track-planner

Sprawdza układ za pomocą umiejętności FIA i narzędzia do oceny.

MEDIUM

Digital Twin Gatekeeper

gatekeeper

Uruchamia końcową listę kontrolną homologacji

LOW

Czego się nauczysz

  • jak agents-cli scaffold create układa projekt ADK i które pliki edytujesz;
  • Jak spakować wiedzę o domenie jako umiejętność agenta (SKILL.md, references/, scripts/) i wczytać ją za pomocą SkillToolset ADK.
  • Jak dopasować thinking_level do zadania każdego agenta.
  • Jak oceniać agentów za pomocą agents-cli eval run.
  • Jak wdrożyć agenta w Agent Runtime, czym Agent Identity różni się od konta usługi i jak wywołać wdrożonego agenta za pomocą protokołu A2A.

2. 2. Konfigurowanie środowiska

Zanim zaczniesz

Potrzebujesz projektu Google Cloud z włączonymi płatnościami. Jeśli wykonujesz ten ćwiczenie w ramach wydarzenia, użyj konta i projektu, które zostały Ci udostępnione.

Otwieranie Cloud Shell i ustawianie projektu

Otwórz Cloud Shell i uruchom:

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

Tworzenie danych logowania dla agentów

gcloud jest już zalogowany, ale kod Pythona, który uruchamiają Twoi agenci, używa domyślnego uwierzytelniania aplikacji (ADC), które wymaga osobnego logowania:

gcloud auth application-default login

Otwórz wyświetlony link, zaloguj się na to samo konto, skopiuj kod i wklej go z powrotem w Cloud Shell. Następnie ustaw projekt, w którym rozliczany jest limit:

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

Włączanie interfejsów 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

Do czego służą poszczególne interfejsy API:

Interfejsy API

Używane dla

aiplatform

Modele Gemini i Agent Runtime

cloudresourcemanager, serviceusage, iam

kontrole projektu i uprawnień, które wykonują gcloud i agents-cli deploy;

compute

tworzy domyślne konto usługi Compute Engine, którego używa wdrożenie w kroku 7;

storage, logging, cloudtrace

miejsce na dane, logi i logi czasu zgodnie z zaleceniami w przewodniku po konfiguracji Agent Runtime.

cloudbuild, artifactregistry

kompilacje i obrazy kontenerów, które są uwzględniane jako środek ostrożności na potrzeby wdrożenia w kroku 7;

Wiele projektów ma już włączone niektóre z tych funkcji. Włączenie interfejsu API, który jest już włączony, niczego nie zmienia.

Instalowanie aplikacji uv i rozszerzenia 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

To ćwiczenie zostało przetestowane z narzędziem agents-cli w wersji 1.10.0, więc zainstaluj tę wersję, aby uzyskać takie same wygenerowane pliki.

3. 3. Tworzenie projektów i dodawanie zasobów

Wygeneruj oba projekty

agents-cli scaffold create generuje kompletny projekt ADK, który można wdrożyć. Uruchom go 2 razy, po jednym dla każdego systemu agenta.

Najpierw planer ścieżki:

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

Następnie osoba pracująca w recepcji/sekretariacie:

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

Flaga

Znaczenie

--agent adk

Zacznij od szablonu ADK w Pythonie

--deployment-target agent_runtime

Generowanie serwera i konfiguracji dla środowiska wykonawczego agenta

--prototype

Pomiń CI/CD; zachowaj minimalny rozmiar projektu

--yes

Zaakceptuj wartości domyślne bez wyświetlania prośby

Przeglądanie układu

Wygenerowany projekt wygląda tak. Edytuj tylko pliki oznaczone symbolem ✍️:

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`

Opis każdego wygenerowanego pliku znajdziesz w sekcji Struktura projektu w przewodniku po interfejsie wiersza poleceń agentów.

Testowanie wygenerowanego agenta

Zanim wprowadzisz jakiekolwiek zmiany, uruchom agenta szablonu, aby zobaczyć, co daje szkielet. Zainstaluj zależności i uruchom lokalne środowisko testowe na porcie 8080:

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

Aby go otworzyć, kliknij przycisk Podgląd w przeglądarce na pasku narzędzi Cloud Shell (ikona oka w prawym górnym rogu), a następnie wybierz Podejrzyj na porcie 8080.

W środowisku testowym zadaj agentowi szablonu pytanie, na które jego narzędzia mogą odpowiedzieć, na przykład:

What's the weather in San Francisco?

W panelu bocznym wyświetlane są wszystkie zdarzenia, w tym wywołanie narzędzia get_weather i jego wynik. Później użyjesz tego samego widoku, aby śledzić własnych agentów.

Gdy skończysz, wróć do terminala i naciśnij Ctrl+C, aby zatrzymać środowisko testowe.

Dodaj zasoby Formuły E

Dane o miejscu, zasady FIA, skrypt fizyki, narzędzia i zbiory danych do oceny nie są częścią lekcji, więc skopiuj je z GitHuba, zamiast je wpisywać:

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 to stały tag wersji, więc wszyscy otrzymują te same pliki. Zmienna $ASSETS została ustawiona w kroku 2 i wskazuje folder, który właśnie został sklonowany.

Spowoduje to dodanie tych plików:

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

Instalowanie i testowanie

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

agents-cli install uruchamia uv sync, aby utworzyć środowisko wirtualne dla każdego projektu. Wszystkie testy jednostkowe powinny zakończyć się powodzeniem.

4. 4. Napisz umiejętność zgodności z FIA

Umiejętność agenta zawiera instrukcje, dokumenty referencyjne i skrypty w jednym folderze. SkillToolset ADK wczytuje go na 3 poziomach, dzięki czemu agent odczytuje tylko to, czego potrzebuje:

Poziom

Plik

Kiedy pracownik obsługi klienta widzi tę informację

1

SKILL.md – strony przedtytułowe (name, description)

Zawsze (kilkadziesiąt tokenów)

2

SKILL.md – treść

Gdy dzwoni do load_skill

3

references/, scripts/

Gdy wywołuje funkcję load_skill_resource lub run_skill_script

Umiejętność znajduje się w app/skills/, ponieważ wygenerowany plik Dockerfile kopiuje tylko app/ do wdrożonego obrazu.

Przeglądanie plików na poziomie 3

W kroku 3 skopiowano pliki poziomu 3 umiejętności:

  • references/track_regulations.md zawiera reguły, które sprawdza rzeczoznawca: limity długości i szerokości, bariery wymagane w każdej strefie hamowania, obowiązkowe warstwy CAD (oraz warstwy medyczne w przypadku tras do szpitala), cel regeneracji i reguły trybu ataku.
  • scripts/calculate_regen_zones.py to kalkulator. Na podstawie prędkości wejścia w strefę hamowania, prędkości wyjścia i drogi hamowania oblicza odzyskaną energię i barierę, której wymaga zakręt, dzięki czemu agent nie musi zgadywać fizyki.

Uruchom skrypt dla zakrętu 10 w Londynie, gdzie samochody hamują z 215 do 55 km/h na odcinku 82 metrów:

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

Samochód odzyskuje tu około 508 kW, czyli więcej niż docelowe 250 kW. Zwalnia przy przeciążeniu powyżej 2 G, więc na zakręcie potrzebne są bariery 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"}

Zapisz: SKILL.md

Utwórz plik, który połączy umiejętność:

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

Dodaj te treści:

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

description to tekst, który model odczytuje, aby zdecydować, kiedy użyć umiejętności. Zawiera on informacje o tym, co obejmuje umiejętność i kiedy jej używać.

Zobacz, jak działa umiejętność

Otwórz plik app/tools.py w edytorze Cloud Shell:

cloudshell edit app/tools.py

Znajdź funkcję 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 odczytuje folder umiejętności: SKILL.md, references/ i scripts/.
  • SkillToolset przekształca je w narzędzia, które agent może wywołać: list_skills, load_skill, load_skill_resource i run_skill_script.
  • UnsafeLocalCodeExecutor umożliwia run_skill_script uruchomienie skryptu w Pythonie na tym komputerze. W środowisku laboratoryjnym jest to dopuszczalne, ale w środowisku produkcyjnym należy używać wykonawcy w trybie piaskownicy.

W następnym kroku dołączysz ten zestaw narzędzi do narzędzia do oceny.

5. 5. Pisanie agentów

Modele Gemini 3 kontrolują rozumowanie za pomocą thinking_level (LOW, MEDIUM lub HIGH) zamiast budżetu tokenów. W pakiecie ADK ustawiasz go za pomocą types.ThinkingConfig(thinking_level=types.ThinkingLevel.HIGH). Dopasuj go do zadania każdego agenta: więcej myślenia oznacza lepsze planowanie, ale też większy czas oczekiwania i koszt.

Asystent (LOW)

Gatekeeper wykonuje tylko deterministyczną listę kontrolną, więc nie wymaga dużego rozumowania. Zastąp agenta szablonu:

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

Lokalne uruchamianie Gatekeepera

Otwórz drugą kartę Cloud Shell i uruchom wygenerowany serwer Gatekeepera na porcie 8001:

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

Wróć na pierwszą kartę i spójrz na kartę agenta Gatekeepera w A2A. W ten sposób inne agenty dowiadują się, czym jest Gatekeeper i co potrafi:

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

Powinien wyświetlić się taki obiekt JSON (skrócony):

{
  "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 ..."}
  ]
}
  • Wartości name i description pochodzą bezpośrednio z definicji Agent(...).
  • skills zawiera listę działań, które może wykonać agent. Pakiet ADK automatycznie dodał verify_infrastructure_checklist, używając docstringu narzędzia.
  • supportedInterfaces informuje rozmówców, gdzie wysyłać żądania A2A. Dlatego serwer został uruchomiony z APP_URL=http://localhost:8001: element wywołujący najpierw odczytuje kartę, a następnie wysyła żądania do dowolnego url, który jest reklamowany na karcie.

Nic nie zostało napisane ręcznie: serwer szkieletowy tworzy kartę na podstawie agenta. Planista torów odczyta tę kartę, aby znaleźć i wezwać strażnika.

Asesor (MEDIUM) i główny architekt (HIGH)

Planer ścieżek ma 2 agenty:

  • Asesor bierze pod uwagę kilka reguł, więc otrzymuje MEDIUM. Jest on opakowany w AgentTool, więc główny architekt wywołuje go jak funkcję i otrzymuje wynik.
  • Główny architekt planuje, naprawia i koordynuje, dzięki czemu HIGH. Dociera do strażnika dostępu przez RemoteA2aAgent, utworzony przez create_remote_gatekeeper_agent() w app/auth.py, i jest opakowany w AgentTool.

Dlaczego oba pomocniki są opakowane jako narzędzia? AgentTool zwraca odpowiedź elementowi wywołującemu, dzięki czemu główny architekt zachowuje kontrolę i tworzy ostateczny plan. Gdyby strażnik był sub_agent, główny architekt przekazałby mu rozmowę, a krótki stan strażnika stałby się ostateczną odpowiedzią.

Otwórz plik app/agent.py planera ścieżek:

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

Skopiuj poniższy kod i zastąp nim całą zawartość pliku ~/track-planner/app/agent.py:

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

Uruchamianie całego systemu w środowisku testowym

Upewnij się, że Gatekeeper nadal działa na drugiej karcie. Na pierwszej karcie uruchom środowisko testowe planisty ścieżek:

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

Kliknij Podgląd w przeglądarce > Podejrzyj na porcie 8080, tak jak w kroku 3.

Poproś o tor w miejscu, które spełnia zasady:

Design and homologate a Formula E circuit at Tokyo Bay.

Obserwuj panel zdarzeń podczas pracy agentów. Powinny się wyświetlić kolejno:

  1. get_track_topology: główny architekt wczytuje dane dotyczące Zatoki Tokijskiej.
  2. fia_assessor: główny architekt przekazuje układ rzeczoznawcy, który wywołuje load_skill, load_skill_resource i run_skill_script (raz na strefę hamowania), a następnie evaluate_track_layout.
  3. remote_gatekeeper: główny architekt dzwoni do strażnika przez A2A. Żądanie pojawi się na drugiej karcie.
  4. Ostateczny plan od głównego architekta, zawierający listę wszystkich warstw CAD i stan ZATWIERDZONY od strażnika.

Kliknij dowolne zdarzenie, aby zobaczyć jego dokładne dane wejściowe i wyjściowe.

Teraz rozpocznij nową sesję i wypróbuj miejsce, które narusza zasady:

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

Tym razem rzeczoznawca odrzuca szykanę o szerokości 6,8 m, główny architekt proponuje szerszą, a poprawiony układ wraca do rzeczoznawcy, zanim trafi do strażnika.

Gdy skończysz, naciśnij Ctrl+C na pierwszej karcie, aby zatrzymać środowisko testowe. Pozostaw Gatekeepera uruchomionego na drugiej karcie – będzie Ci potrzebny w następnym kroku.

6. 6. Ocenianie agentów

agents-cli eval run uruchamia agenta na zbiorze danych, rejestruje każdą turę i wywołanie narzędzia oraz ocenia wyniki. To Ty decydujesz, co chcesz mierzyć w tests/eval/eval_config.yaml.

Przeglądanie zbioru danych

Otwórz zbiór danych w edytorze Cloud Shell:

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

Plik zawiera listę: eval_cases. Każdy przypadek ma eval_case_id, prompt (pytanie użytkownika) i reference (jak wygląda dobra odpowiedź). Sędzia LLM porównuje odpowiedź agenta z odpowiedzią wzorcową.

eval_case_id

Co sprawdza

fe_001_tokyo_compliant

Miejsce, które spełnia wszystkie reguły i zostaje zatwierdzone.

fe_002_monaco_narrow

Szykana o szerokości 6,8 m nie spełnia reguły szerokości.

fe_003_berlin_too_long

Obwód o długości 4350 m nie spełnia reguły dotyczącej długości.

fe_004_missing_layers

Układ, w którym brakuje obowiązkowych warstw CAD

fe_005_low_regen

strefę hamowania poniżej docelowej wartości 250 kW,

fe_006_sao_paulo_medical

Dostęp do szpitala wymagający warstw CAD dotyczących medycyny

fe_007_attack_mode

Strefa trybu ataku na linii wyścigowej

fe_008_multiturn_remediation

Wieloetapowa rozmowa: Monaco zostaje odrzucone, użytkownik prosi o poprawkę, a poprawiony układ powinien zostać zatwierdzony.

W ostatnim przypadku użyto agent_data zamiast prompt. Odtwarza wcześniejsze tury rozmowy, dzięki czemu możesz sprawdzić, jak agent radzi sobie z kolejnymi prośbami, a nie tylko z pojedynczym pytaniem.

track_eval_quick.json w tym samym folderze zawiera tylko 2 przypadki: przypadek 1 (pojedyncze pytanie) i przypadek 8 (rozmowa). Uruchomisz to zapytanie, ponieważ jest szybsze i zużywa mniej limitu.

Zapisywanie danych

Zbiór danych określa, o co należy zapytać agenta. eval_config.yaml podaje sposób oceniania odpowiedzi. agents-cli eval run odczytuje go po udzieleniu przez agenta odpowiedzi na wszystkie zgłoszenia i uruchamia każdy rodzaj danych dla każdej odpowiedzi.

Otwórz plik wygenerowany przez szablon:

cloudshell edit tests/eval/eval_config.yaml

Zawiera już ogólny sędziowski model LLM (custom_response_quality) i wskaźnik agent_turn_count, który zlicza tylko tury. To dobry początek dla każdego agenta, ale nie mówi nic o Formule E. Zachowasz sędziego i dodasz wskaźnik, który sprawdza regułę dotyczącą tej domeny.

Dobre oceny zwykle łączą 2 rodzaje danych:

Rodzaj

Sposób oceniania

Przeznaczenie

Słabsze strony

LLM-as-a-judge

Model odczytuje odpowiedź i reference zgłoszenia, a następnie przyznaje ocenę od 1 do 5 wraz z wyjaśnieniem.

Cechy rozmyte: czy odpowiedź jest poprawna, kompletna i jasna?

Kosztuje 1 wywołanie modelu na przypadek; wyniki mogą się nieznacznie różnić między uruchomieniami

Dane dotyczące kodu

Niewielka funkcja Pythona sprawdza odpowiedź.

Dokładne, możliwe do sprawdzenia fakty: czy odpowiedź zawierała coś, co musi się w niej znaleźć?

Sprawdza tylko to, co można wyrazić w kodzie.

Zastąp zawartość pliku tym kodem:

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

Co robi każda część:

  • metrics_to_run zawiera listę danych do uruchomienia według nazwy. Usunięcie nazwy w tym miejscu spowoduje wyłączenie tego wskaźnika bez jego usuwania.
  • custom_response_quality to sędzia LLM. custom_function_file punktów w tests/eval/response_quality.py, które wygenerował szablon. Otwórz go, jeśli chcesz zobaczyć prompta do oceny.
  • fia_cad_layer_compliance to wskaźnik kodu zapisany w tekście pod custom_function. W każdym przypadku otrzymuje instance (prompt, ostateczną odpowiedź agenta response i reference). W przypadkach, gdy oczekiwana odpowiedź jest zatwierdzeniem, sprawdza, czy odpowiedź zawiera nazwy wszystkich 4 wymaganych warstw CAD, i wymienia te, których brakuje. Na przykład plan, który pomija F1_TEC3, otrzymuje wynik 0,75. Inne przypadki otrzymują wynik 1,0, ponieważ odrzucenie lub odpowiedź w trybie ataku nie muszą zawierać wszystkich warstw.

Dlaczego warto sprawdzać warstwy CAD? Zatwierdzony plan, który pomija obowiązkową warstwę, jest nieprawidłowy, nawet jeśli jest czytelny, a sędzia LLM może to przeoczyć. Sprawdzanie kodu stosuje tę samą regułę w ten sam sposób przy każdym uruchomieniu.

Przeprowadź ocenę

Pozostaw lokalną bramkę uruchomioną na drugiej karcie, a następnie uruchom:

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

Ocena zajmuje kilka minut. Każdy przypadek obejmuje cały proces z wieloma agentami, czyli około 10 wywołań modelu, przy czym główny architekt myśli w HIGH, więc Step 1/2: eval generate jest powolną częścią. Nie jest zablokowany.

Po zakończeniu otwórz raport HTML w artifacts/grade_results/ za pomocą edytora Cloud Shell. Niskie wyniki fia_cad_layer_compliance oznaczają, że ostateczna odpowiedź głównego architekta pomija warstwy. Spróbuj doprecyzować instrukcje i ponownie przeprowadzić ocenę.

7. 7. Wdrażanie Gatekeepera w Agent Runtime

Wdrażasz tylko Gatekeepera. Planer ścieżek nadal działa w Cloud Shell i wywołuje tę funkcję.

Otwieranie nowej karty Cloud Shell

Wdrażanie zajmuje od 5 do 8 minut, więc wykonaj je w nowej karcie, podczas gdy ocena z kroku 6 się kończy:

  1. Na pasku narzędzi Cloud Shell kliknij Otwórz nową kartę (+).
  2. Nowa karta nie zachowuje zmiennych z kroku 2, więc ustaw je ponownie:
    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"
    

Pozostaw lokalną usługę Gatekeeper uruchomioną na drugiej karcie: ocena nadal z niej korzysta, a wdrożenie nie wymaga jej zatrzymania.

Wdróż

Na nowej karcie wdróż Gatekeepera. Działa jako domyślne konto usługi Compute Engine projektu:

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

Wdrażanie trwa około 5–8 minut. Nie zamykaj karty Cloud Shell podczas działania skryptu. Jeśli strumień logów zostanie rozłączony, sprawdź postęp za pomocą polecenia agents-cli deploy --status. W międzyczasie wróć do kroku 6 i zapoznaj się z raportem z oceny.

Sprawdzanie tożsamości agenta

agents-cli rejestruje wdrożony zasób w 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

Drugi wiersz danych wyjściowych to tożsamość, z którą działa Gatekeeper:

Wdrożono z użyciem:

Przykładowe dane wyjściowe

Konto usługi (domyślne)

123456789012-compute@developer.gserviceaccount.com

Tożsamość agenta

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

Konto usługi jest współdzielone przez wszystkie elementy w projekcie, które go używają. Tożsamość agenta należy do tego jednego agenta, więc możesz przyznać mu dokładnie te role, których potrzebuje, i osobno wyświetlać jego działania w dziennikach kontrolnych.

8. 8. Łączenie się z wdrożonym Gatekeeperem przez A2A

Agent Runtime obsługuje trasy A2A wygenerowane przez Gatekeepera za uwierzytelnioną ścieżką /api.

Upewnij się, że ocena z kroku 6 została zakończona, a następnie zatrzymaj lokalnego Gatekeepera na drugiej karcie (Ctrl+C). Od teraz narzędzie do planowania ścieżki będzie wywoływać wdrożoną wersję. Na pierwszej karcie skieruj na niego narzędzie do planowania ścieżki:

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

Dlaczego klient odświeża token

Otwórz pokój app/auth.py. Tokeny dostępu Google Cloud wygasają po godzinie, więc klient, który podczas uruchamiania ustawia stały nagłówek Authorization, po godzinie zaczyna zwracać błąd 401 Unauthorized. GoogleAuthRefresh jest podklasą httpx.Auth i sprawdza dane logowania przy każdym żądaniu, odświeżając je w razie potrzeby. create_remote_gatekeeper_agent() dołącza go tylko do adresów URL https://.

Uruchomienie od początku do końca

Ponownie uruchom środowisko testowe, aby uwzględnić nową wartość GATEKEEPER_A2A_URL:

agents-cli playground --port 8080

Kliknij Podgląd w przeglądarce > Podejrzyj na porcie 8080, rozpocznij nową sesję i zapytaj:

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

Zdarzenia wyglądają tak samo jak w kroku 5, ale wywołanie remote_gatekeeper jest teraz kierowane do strażnika w środowisku wykonawczym agentów, a nie do drugiej karty.

Wyświetlanie połączenia przychodzącego w Agent Runtime

Udowodnij, że żądanie rzeczywiście dotarło do wdrożonego strażnika przez A2A:

  1. W konsoli Cloud wyszukaj Agent Platform, a następnie w menu po lewej stronie otwórz Wdrożenia.
  2. Wybierz gatekeeper.
  3. Otwórz kartę Sesje.

Zobaczysz nową sesję, której identyfikator użytkownika zaczyna się od A2A_USER_. Został on utworzony przez serwer A2A Gatekeepera, gdy wywołał go Twój planer ścieżki. Otwórz kartę Ślady na tej samej stronie, aby zobaczyć wywołanie modelu strażnika i wywołanie narzędzia verify_infrastructure_checklist dla tego żądania.

Opcjonalnie: wdróż też planowanie ścieżki

Do tej pory narzędzie do planowania ścieżek działało w Cloud Shell, więc jego wywołania A2A do Gatekeepera używały Twoich danych logowania. Wdróż też planistę. Obaj agenci będą działać w Agent Runtime i wywoływać się nawzajem za pomocą własnych wdrożonych tożsamości.

Naciśnij Ctrl+C, aby zatrzymać Playground, a następnie wdróż planner w taki sam sposób jak 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

Oba agenty działają jako to samo konto usługi Compute Engine. Zachowaj --service-account: bez tego moduł planujący działa jako domyślny agent usługi Agent Runtime i nie może wywołać Gatekeepera (patrz Dlaczego to konto usługi? w kroku 7).

Jak wdrożony planista dociera do osoby pracującej w recepcji/sekretariacie:

  • agents-cli deploy kopiuje wartości z pliku .env do wdrożenia, dzięki czemu planista otrzymuje dodany przez Ciebie parametr GATEKEEPER_A2A_URL.
  • W środowisku wykonawczym agentów GoogleAuthRefresh w app/auth.py przejmuje wdrożoną tożsamość planisty zamiast Twojej.

Wdrażanie trwa około 5–8 minut. Po zakończeniu sprawdź, z jaką tożsamością działa planista:

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

Aby wypróbować wdrożony system:

  1. W konsoli Cloud wróć do Agent Platform > Wdrożenia.
  2. Wybierz track-planner i otwórz kartę Playground.
  3. Wpisz:
    Design and homologate a Formula E circuit at Tokyo Bay.
    
  4. Poczekaj na schemat ze statusem APPROVED (Zatwierdzony) od strażnika.
  5. Wróć do sekcji Wdrożenia, wybierz gatekeeper i otwórz kartę Sesje. Najnowsza sesja A2A_USER_ pochodzi z wdrożonego planera, a nie z Cloud Shell.

Podczas czyszczenia w następnym kroku usuń też planistę:

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

9. 9. Rozwiązywanie problemów i czyszczenie

Rozwiązywanie problemów

Krótki opis problemu

Napraw

UREQ_TOS_NOT_ACCEPTED

Zaakceptuj Warunki korzystania z usługi w konsoli Cloud, a następnie spróbuj ponownie.

Projekt: (unset)

Ponownie uruchom polecenia projektu z kroku 2.

service account info is missing 'email' field

Uruchom gcloud auth application-default login (krok 2), a następnie uruchom ponownie wszystkie działające serwery.

FileNotFoundError za SKILL.md

Wykonaj krok 4 lub skopiuj $ASSETS/solution/track-planner/. do ~/track-planner/.

Od Connection refused do localhost:8001

Uruchom lokalną bramę (krok 5) lub ustaw GATEKEEPER_A2A_URL na wdrożoną bramę (krok 8).

Permission 'resourcemanager.projects.setIamPolicy' denied podczas wdrażania

Na Twoim koncie nie można używać tożsamości agenta. Wykonaj czynności opisane w polu czyszczenia w kroku 7, a następnie użyj wdrożenia konta usługi.

Wdrożony moduł planowania ścieżki zgłasza 403 Forbidden od strażnika

Tożsamość planisty nie może wywoływać strażnika. Wdróż ją ponownie za pomocą --service-account, jak w kroku 7, lub za pomocą --agent-identity, jeśli jesteś właścicielem projektu.

429 RESOURCE_EXHAUSTED

Osiągnięto limit Gemini. Poczekaj chwilę lub dodaj --concurrency 1 --qps 2 do agents-cli eval run.

Address already in use

Zatrzymaj drugi serwer lub wybierz inny --port.

Czyszczenie danych

Usuń wdrożony kontroler Gatekeeper, aby nie działał dalej:

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"

Jeśli projekt został utworzony tylko na potrzeby tego codelab, możesz usunąć cały projekt. Jeśli jesteś na wydarzeniu, zachowaj otrzymany projekt, chyba że organizatorzy powiedzą inaczej.

Czego się nauczysz

  • agents-cli scaffold create udostępnia standardowy projekt ADK, który można wdrożyć. Musisz tylko napisać agentów, umiejętności i oceny.
  • Umiejętności agenta sprawiają, że wiedza o domenie nie jest uwzględniana w promptach, dopóki agent jej nie potrzebuje.
  • thinking_level pozwala na kompromis między głębią rozumowania a szybkością działania poszczególnych agentów.
  • agents-cli eval run przekształca pytanie „Czy to działa?” w wyniki, które możesz śledzić.
  • Tożsamość agenta i A2A umożliwiają bezpieczną współpracę niezależnie wdrożonych agentów.

Więcej informacji