Создание мультиагентного планировщика трассы Формулы E с помощью ADK и A2A

1. 1. Обзор

В этой практической работе вы создадите мультиагентную систему, которая проектирует уличную трассу Формулы E и проверяет ее на соответствие упрощенному набору правил FIA.

Вы будете использовать agents-cli, чтобы создать два стандартных проекта Agent Development Kit (ADK), а затем напишете важные части: навык агента, три агента и оценку. Все остальное (макет проекта, сервер, зависимости, конфигурация развертывания) поступает из CLI.

Впервые смотрите Формулу E?

Вам не нужно разбираться в автоспорте. Достаточно этих пяти терминов:

  • Формула E – чемпионат по автогонкам на электромобилях с открытыми колесами, организованный Международной автомобильной федерацией. Большинство гонок проходят на временных уличных трассах в центрах городов.
  • Омологация – официальное подтверждение FIA, что трасса безопасна для гонок. Нет омологации – нет гонки.
  • Рекуперативное торможение. При торможении двигатели автомобиля заряжают батарею. Чем сильнее торможение, тем больше энергии восстанавливается, поэтому в правилах этой практической работы требуется хотя бы одна зона сильного торможения.
  • Слои САПР. Именованные слои на чертежах трассы, например траектория, пит-лейн и барьеры. FIA проверяет, все ли обязательные элементы на месте.
  • Режим атаки. Пилоты могут временно увеличить мощность болида, проехав через зону активации вне гоночной траектории.

Ваши агенты играют за обе стороны: один проектирует трассу, а два других проверяют ее на соответствие требованиям FIA.

Что вы создадите

Архитектура: в проекте track-planner, запущенном в Cloud Shell, агент track_planner использует get_track_topology, fia_assessor и remote_gatekeeper. remote_gatekeeper вызывает агента gatekeeper через A2A. В проекте gatekeeper, который выполняется в среде выполнения агента, агент gatekeeper использует verify_infrastructure_checklist.

Зеленые прямоугольники – это агенты, а пунктирные – их инструменты. Так же они изображаются в ADK Playground. Два инструмента вызывают других агентов: fia_assessor – это агент FIA Assessor с навыком FIA compliance, а remote_gatekeeper вызывает агента Gatekeeper в другом проекте через A2A.

Агент

Проект

Задание

thinking_level

Ведущий архитектор

track-planner

Читает информацию о месте проведения, спрашивает у Оценщика, устраняет проблемы, запрашивает одобрение у Привратника

HIGH

Оценщик FIA

track-planner

Проверяет макет с помощью навыка FIA и инструмента оценки.

MEDIUM

Контролер доступа к цифровым двойникам

gatekeeper

Выполняет финальную проверку по контрольному списку

LOW

Что вы узнаете

  • Как agents-cli scaffold create размещает проект ADK и какие файлы вы редактируете.
  • Как упаковать знания о предметной области в навык агента (SKILL.md, references/, scripts/) и загрузить его с помощью SkillToolset ADK.
  • Как сопоставить thinking_level с работой каждого агента.
  • Как оценивать агентов с помощью agents-cli eval run.
  • Как развернуть агента в Agent Runtime, чем идентификатор агента отличается от сервисного аккаунта и как вызвать развернутого агента по протоколу A2A.

2. 2. Как настроить среду

Подготовка

Вам нужен проект Google Cloud с включенными платежными функциями. Если вы выполняете эту практическую работу на мероприятии, используйте предоставленные вам аккаунт и проект.

Откройте Cloud Shell и выберите проект

Откройте Cloud Shell и выполните следующую команду:

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

Как создать учетные данные для агентов

gcloud уже вошел в систему, но код Python, который выполняют ваши агенты, использует Application Default Credentials (ADC), для которых требуется отдельный вход:

gcloud auth application-default login

Откройте ссылку, войдите в тот же аккаунт, скопируйте код и вставьте его в Cloud Shell. Затем укажите проект, для которого выставляется счет за квоту:

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

Включение 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

Назначение каждого API:

API

Назначение

aiplatform

Модели Gemini и Agent Runtime

cloudresourcemanager, serviceusage, iam

Проверки проектов и разрешений, выполняемые gcloud и agents-cli deploy.

compute

Создает сервисный аккаунт Compute Engine по умолчанию, который используется для развертывания на шаге 7.

storage, logging, cloudtrace

хранилище, журналы и трассировки, как рекомендуется в руководстве по настройке Agent Runtime;

cloudbuild, artifactregistry

Сборки и образы контейнеров, включенные в качестве меры предосторожности для развертывания на шаге 7.

Во многих проектах некоторые из них уже включены. Если вы включите API, который уже включен, ничего не изменится.

Установить uv и 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

Эта практическая работа была протестирована с agents-cli версии 1.10.0, поэтому установите эту версию, чтобы получить те же сгенерированные файлы.

3. 3. Создайте проекты и добавьте объекты

Сгенерировать оба проекта

agents-cli scaffold create генерирует полный проект ADK, готовый к развертыванию. Запустите его дважды – по одному разу для каждой системы агентов.

Во-первых, планировщик треков:

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

Затем секретарь:

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

Флаг

Значение

--agent adk

Как начать работу с шаблоном ADK на Python

--deployment-target agent_runtime

Создайте сервер и конфигурацию для Agent Runtime

--prototype

Пропустить CI/CD; оставить проект минимальным

--yes

Принимать значения по умолчанию без запроса

Изучите макет

Сгенерированный проект выглядит следующим образом. Вы редактируете только файлы, помеченные значком ✍️:

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`

Описание каждого созданного файла можно найти в разделе Структура проекта руководства по Agents CLI.

Попробуйте сгенерированного агента

Прежде чем что-либо менять, запустите шаблон агента, чтобы посмотреть, что дает каркас. Установите зависимости и запустите локальную тестовую среду на порте 8080:

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

Чтобы открыть его, нажмите кнопку Web Preview (Предварительный просмотр) на панели инструментов Cloud Shell (значок глаза в правом верхнем углу), а затем выберите Preview on port 8080 (Предварительный просмотр на порте 8080).

В тестовой среде задайте агенту вопрос, на который он может ответить с помощью своих инструментов, например:

What's the weather in San Francisco?

На боковой панели показаны все события, включая вызов инструмента get_weather и его результат. Позже вы будете использовать это же представление, чтобы следить за своими агентами.

Закончив, вернитесь в терминал и нажмите Ctrl + C, чтобы остановить работу тестовой среды.

Добавьте объекты Формулы E

Данные о месте проведения, правила FIA, физический скрипт, инструменты и наборы данных для оценки не являются частью урока, поэтому вы копируете их из GitHub, а не вводите:

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 – это фиксированный тег выпуска, поэтому все получают одни и те же файлы. Переменная $ASSETS была задана на шаге 2 и указывает на папку, которую вы только что клонировали.

Будут добавлены следующие файлы:

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

Установка и тестирование

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

agents-cli install запускает uv sync, чтобы создать виртуальную среду для каждого проекта. Все модульные тесты должны быть пройдены.

4. 4. Как написать навык для проверки на соответствие требованиям FIA

Навык агента – это пакет инструкций, справочных документов и скриптов в одной папке. SkillToolset ADK загружает его на трех уровнях, поэтому агент читает только то, что ему нужно:

Уровень

Файл

Когда агент видит его

1

SKILL.md, вводная часть (name, description)

Всегда (несколько десятков токенов)

2

SKILL.md body

Когда вызывается load_skill

3

references/, scripts/

При вызове load_skill_resource или run_skill_script.

Навык находится в app/skills/, потому что сгенерированный Dockerfile копирует в развернутый образ только app/.

Посмотреть файлы уровня 3

Вы скопировали файлы третьего уровня навыка на шаге 3.

  • references/track_regulations.md содержит правила, которые проверяет эксперт: ограничения по длине и ширине, тип барьера для каждой зоны торможения, обязательные слои САПР (плюс медицинские слои для маршрутов к больницам), цель регенерации и правила режима атаки.
  • scripts/calculate_regen_zones.py – это калькулятор. На основе скорости входа в зону торможения, скорости выхода из нее и тормозного пути он рассчитывает рекуперированную энергию и барьер, необходимый для поворота, поэтому агенту никогда не приходится угадывать физику.

Запустите скрипт для 10-го поворота в Лондоне, где болиды тормозят с 215 до 55 км/ч на отрезке в 82 метра:

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

Автомобиль восстанавливает около 508 кВт, что выше целевого значения в 250 кВт. При перегрузке более 2G скорость снижается, поэтому на повороте нужны барьеры 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"}

Записать файл "SKILL.md"

Создайте файл, который связывает навык воедино:

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

Добавьте следующий контент:

---
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 – это то, что модель читает, чтобы решить, когда использовать навык. Поэтому в этом поле указывается и что охватывает навык, и когда его использовать.

Посмотрите, как устроен навык

Откройте app/tools.py в редакторе Cloud Shell:

cloudshell edit app/tools.py

Найдите функцию 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 считывает папку навыка: SKILL.md, references/ и scripts/.
  • SkillToolset преобразует их в инструменты, которые может вызывать агент: list_skills, load_skill, load_skill_resource и run_skill_script.
  • UnsafeLocalCodeExecutor позволяет run_skill_script запускать скрипт Python на этом компьютере. Для лабораторной работы этого достаточно, но в рабочей среде используйте исполнитель в изолированной среде.

На следующем шаге вы прикрепите этот набор инструментов к Assessor.

5. 5. Напишите агентам

Модели Gemini 3 управляют рассуждениями с помощью параметра thinking_level (LOW, MEDIUM или HIGH), а не лимита токенов. В ADK для этого используется types.ThinkingConfig(thinking_level=types.ThinkingLevel.HIGH). Подбирайте его в зависимости от задачи агента: чем больше времени на размышление, тем лучше планирование, но выше задержка и стоимость.

The Gatekeeper (LOW)

Gatekeeper работает только с детерминированным контрольным списком, поэтому ему не нужно много рассуждать. Замените шаблон агента:

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

Как запустить Gatekeeper локально

Откройте вторую вкладку Cloud Shell и запустите сгенерированный сервер Gatekeeper на порту 8001:

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

Вернитесь на первую вкладку и посмотрите на карточку агента Gatekeeper. Вот как другие агенты узнают, что такое Gatekeeper и что он может делать:

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

Вы должны увидеть JSON, похожий на этот (сокращенный):

{
  "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 и description берутся непосредственно из определения Agent(...).
  • skills перечисляет, что может делать агент. ADK автоматически добавил verify_infrastructure_checklist, используя строку документации инструмента.
  • supportedInterfaces сообщает вызывающим абонентам, куда отправлять запросы A2A. Вот почему вы запустили сервер с APP_URL=http://localhost:8001: вызывающий абонент сначала считывает карту, а затем отправляет свои запросы на любой url, который рекламирует карта.

Здесь нет ничего, написанного вручную: сервер, созданный по шаблону, строит карточку на основе данных от агента. Планировщик треков прочитает эту карту, чтобы найти и вызвать Хранителя врат.

Оценщик (MEDIUM) и ведущий архитектор (HIGH)

В планировщике треков есть два агента:

  • Оценщик учитывает несколько правил, поэтому он получает MEDIUM. Она упакована в AgentTool, поэтому ведущий архитектор вызывает ее как функцию и получает вердикт.
  • Главный архитектор планирует, исправляет и координирует, чтобы получить HIGH. Он достигает Gatekeeper через RemoteA2aAgent, созданный create_remote_gatekeeper_agent() в app/auth.py, а также упаковывает его в AgentTool.

Зачем упаковывать оба помощника в инструменты? AgentTool возвращает ответ вызывающей стороне, поэтому главный архитектор по-прежнему отвечает за работу и создает окончательный проект. Если бы Gatekeeper был sub_agent, Lead Architect передал бы ему разговор, и краткий статус Gatekeeper стал бы окончательным ответом.

Откройте файл app/agent.py планировщика треков.

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

Скопируйте следующий код и замените им все содержимое файла ~/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",
)

Запустите всю систему в тестовой среде.

Убедитесь, что Gatekeeper по-прежнему запущен на второй вкладке. На первой вкладке запустите тестовую среду планировщика треков:

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

Нажмите Web Preview (Предварительный просмотр) > Preview on port 8080 (Предварительный просмотр на порте 8080), как на шаге 3.

Попросите составить тренировку, которая соответствует правилам:

Design and homologate a Formula E circuit at Tokyo Bay.

Наблюдайте за панелью событий во время работы агентов. Вы должны увидеть следующее:

  1. get_track_topology: ведущий архитектор загружает данные о Токийском заливе.
  2. fia_assessor: главный архитектор передает макет оценщику, который вызывает load_skill, load_skill_resource и run_skill_script (по одному разу для каждой зоны торможения), а затем evaluate_track_layout.
  3. remote_gatekeeper: ведущий архитектор вызывает привратника по A2A. Вы увидите, что в нем появилась запись о запросе.
  4. Окончательный чертеж от главного архитектора со списком всех слоев САПР и статусом ОДОБРЕНО от привратника.

Нажмите на событие, чтобы посмотреть его входные и выходные данные.

Начните новую игру и выберите танцпол, где правила не действуют.

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

На этот раз эксперт отклоняет шикану длиной 6,8 м, ведущий архитектор предлагает более широкую, а исправленный макет возвращается к эксперту, прежде чем попасть к контролеру.

Закончив работу, нажмите Ctrl + C на первой вкладке, чтобы остановить тестовую среду. Не закрывайте вкладку с Gatekeeper – она понадобится на следующем шаге.

6. 6. Оцените агентов

agents-cli eval run запускает агента на наборе данных, записывает каждый ход и вызов инструмента и оценивает результаты. Вы сами решаете, какие данные отслеживать в tests/eval/eval_config.yaml.

Посмотрите на набор данных

Откройте набор данных в редакторе Cloud Shell:

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

Файл содержит список eval_cases. У каждого случая есть eval_case_id, prompt (что спрашивает пользователь) и reference (как выглядит хороший ответ). Судья LLM сравнивает ответ агента с эталонным.

eval_case_id

Что проверяется

fe_001_tokyo_compliant

Место, которое соответствует всем правилам и одобрено

fe_002_monaco_narrow

Шикана длиной 6,8 м не соответствует правилу ширины.

fe_003_berlin_too_long

Трасса длиной 4350 м не соответствует правилу длины.

fe_004_missing_layers

Макет, в котором отсутствуют обязательные слои САПР.

fe_005_low_regen

Зона торможения ниже целевого значения 250 кВт

fe_006_sao_paulo_medical

Доступ к больнице, требующий слоев медицинской САПР

fe_007_attack_mode

Зона режима атаки на гоночной трассе

fe_008_multiturn_remediation

Многоэтапный разговор: Монако отклоняется, пользователь просит исправить, и исправленный макет должен быть одобрен.

В последнем случае используется agent_data вместо prompt. Он воспроизводит предыдущие реплики разговора, чтобы вы могли проверить, как агент обрабатывает последующий запрос, а не только один вопрос.

track_eval_quick.json в той же папке содержит только два случая: случай 1 (один вопрос) и случай 8 (разговор). Вы запустите его, потому что он быстрее и использует меньше квоты.

Запись показателей

Набор данных определяет, какие вопросы задавать агенту. eval_config.yaml – как оценивать ответы. agents-cli eval run читает его после того, как агент ответит на все вопросы, и рассчитывает каждый показатель для каждого ответа.

Откройте файл, созданный шаблоном:

cloudshell edit tests/eval/eval_config.yaml

В нем уже есть общий судья LLM (custom_response_quality) и показатель agent_turn_count, который учитывает только реплики. Это хорошее начало для любого агента, но здесь ничего не говорится о Формуле E. Вы сохраните судью и добавите показатель, который проверяет правило, относящееся к этому домену.

Хорошие оценки обычно сочетают в себе два вида метрик:

Тип

Как рассчитывается оценка

Преимущества:

Компромисс

LLM-as-a-judge

Модель читает ответ и reference обращения, а затем оценивает ответ по шкале от 1 до 5 и дает объяснение.

Нечеткие качества: является ли ответ правильным, полным и понятным?

Каждый вызов модели оплачивается. Оценки могут немного различаться от запуска к запуску.

Показатель кода

Небольшая функция Python проверяет ответ

Точные, проверяемые факты: содержит ли ответ то, что должно в нем быть?

Проверяет только то, что можно выразить в коде.

Замените содержимое файла следующим кодом:

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

Что делает каждая часть:

  • metrics_to_run – список показателей, которые нужно получить, по названиям. Если удалить название, показатель будет отключен, но не удален.
  • custom_response_quality – это LLM-судья. custom_function_file указывает на tests/eval/response_quality.py, который сгенерирован каркасом. Откройте его, чтобы посмотреть запрос для оценки.
  • fia_cad_layer_compliance – это показатель кода, указанный в строке под custom_function. Для каждого случая она получает instance (запрос, окончательный response агента и reference). Для случаев, когда ожидаемый ответ – одобрение, она проверяет, что в ответе названы все четыре обязательных слоя САПР, и перечисляет все отсутствующие. Например, если в плане не указан F1_TEC3, то оценка будет 0,75. В остальных случаях ответ получает 1,0 балла, поскольку при отказе или в режиме атаки не нужно перечислять все слои.

Зачем проверять слои САПР? Одобренный чертеж, в котором отсутствует обязательный слой, неверен, даже если он хорошо читается, и судья LLM может упустить это из виду. При проверке кода одно и то же правило применяется одинаково при каждом запуске.

Как запустить оценку

Не закрывайте вкладку с локальным Gatekeeper и выполните следующую команду:

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

Оценка занимает несколько минут. Каждый кейс запускает весь поток с несколькими агентами, около десяти вызовов модели с ведущим архитектором на HIGH, поэтому Step 1/2: eval generate – это медленная часть. Оно не застряло.

Когда он завершится, откройте отчет в формате HTML в artifacts/grade_results/ с помощью редактора Cloud Shell. Низкие оценки fia_cad_layer_compliance говорят о том, что в окончательном ответе ведущего архитектора отсутствуют слои. Попробуйте уточнить инструкции и запустить оценку снова.

7. 7. Развертывание Gatekeeper в Agent Runtime

Вы развертываете только Gatekeeper. Планировщик треков продолжает работать в Cloud Shell и вызывает его.

Откройте новую вкладку Cloud Shell.

Развертывание занимает от 5 до 8 минут, поэтому выполните его в новой вкладке, пока завершается оценка из шага 6:

  1. Нажмите Открыть новую вкладку (+) на панели инструментов Cloud Shell.
  2. На новой вкладке переменные из шага 2 не сохраняются, поэтому задайте их снова:
    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"
    

Оставьте локальный Gatekeeper запущенным на второй вкладке: он по-прежнему используется для оценки, и для развертывания его не нужно останавливать.

Развернуть

На новой вкладке разверните Gatekeeper. Он выполняется как сервисный аккаунт Compute Engine по умолчанию для проекта:

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

Развертывание занимает от пяти до восьми минут. Не закрывайте вкладку Cloud Shell, пока выполняется команда. Если поток журналов отключится, проверьте ход выполнения с помощью agents-cli deploy --status. Пока вы ждете, вернитесь к шагу 6 и прочитайте отчет об оценке.

Проверьте идентификационные данные агента

agents-cli записывает развернутый ресурс в 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

Во второй строке выходных данных указан идентификатор, с которым запускается Gatekeeper:

Развернуто с помощью

Пример выходных данных

Сервисный аккаунт (по умолчанию)

123456789012-compute@developer.gserviceaccount.com

Идентификатор агента

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

Сервисный аккаунт используется всеми компонентами проекта, которые к нему обращаются. Идентификационная информация агента принадлежит только ему, поэтому вы можете предоставить ему именно те роли, которые ему нужны, и отдельно просматривать его действия в журналах аудита.

8. 8. Подключение к развернутому Gatekeeper через A2A

Среда выполнения агента обслуживает маршруты A2A, созданные Gatekeeper, по аутентифицированному пути /api.

Убедитесь, что оценка из шага 6 завершена, а затем остановите локальный Gatekeeper на второй вкладке (Ctrl + C). Теперь планировщик маршрутов вызывает развернутую функцию. На первой вкладке укажите планировщику маршрутов на этот файл:

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

Почему клиент обновляет свой токен

Открыть чат "app/auth.py". Срок действия токенов доступа Google Cloud истекает через час, поэтому клиент, который устанавливает фиксированный заголовок Authorization при запуске, через час начинает получать ошибку 401 Unauthorized. GoogleAuthRefresh является подклассом httpx.Auth и проверяет учетные данные при каждом запросе, обновляя их при необходимости. create_remote_gatekeeper_agent() добавляет его только к URL https://.

Запустите кампанию от начала до конца

Снова запустите тестовую среду, чтобы она использовала новый GATEKEEPER_A2A_URL:

agents-cli playground --port 8080

Нажмите Web Preview (Веб-просмотр) > Preview on port 8080 (Просмотр на порте 8080), начните новый сеанс и спросите:

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

События выглядят так же, как на шаге 5, но вызов remote_gatekeeper теперь направляется в Gatekeeper в среде выполнения агента, а не на вторую вкладку.

Как посмотреть звонок в Agent Runtime

Докажите, что запрос действительно достиг развернутого Gatekeeper через A2A:

  1. В консоли Cloud найдите Agent Platform, а затем в меню слева выберите Deployments (Развертывания).
  2. Выберите gatekeeper.
  3. Откройте вкладку Сеансы.

Вы увидите новый сеанс, идентификатор пользователя которого начинается с A2A_USER_. Его создал сервер A2A Gatekeeper, когда его вызвал ваш планировщик маршрутов. Откройте вкладку Traces (Трассировки) на той же странице, чтобы посмотреть вызов модели Gatekeeper и вызов инструмента verify_infrastructure_checklist для этого запроса.

Необязательно: разверните также планировщик треков

Пока планировщик треков работает в Cloud Shell, поэтому его вызовы A2A к Gatekeeper используют ваши учетные данные. Разверните планировщик, и оба агента будут работать в Agent Runtime, вызывая друг друга со своими собственными развернутыми идентификационными данными.

Нажмите Ctrl + C, чтобы остановить тестовую среду, а затем разверните планировщик так же, как и 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

Оба агента работают в одном сервисном аккаунте Compute Engine. Оставьте --service-account. Без него планировщик будет работать как агент сервиса по умолчанию для Agent Runtime и не сможет вызывать Gatekeeper (см. раздел Зачем нужен этот сервисный аккаунт? на шаге 7).

Как планировщик связывается с секретарем:

  • agents-cli deploy копирует значения из .env в развертывание, поэтому планировщик получает GATEKEEPER_A2A_URL, добавленный вами выше.
  • В среде выполнения агента GoogleAuthRefresh в app/auth.py использует развернутую идентификационную информацию планировщика, а не вашу.

Развертывание занимает от пяти до восьми минут. Когда она завершится, проверьте, от имени какого пользователя выполняется планировщик:

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

Чтобы протестировать развернутую систему:

  1. В консоли Cloud вернитесь в раздел Agent Platform (Платформа агентов) > Deployments (Развертывания).
  2. Выберите track-planner и откройте вкладку Playground.
  3. Укажите следующее:
    Design and homologate a Formula E circuit at Tokyo Bay.
    
  4. Дождитесь, пока у схемы появится статус Одобрено.
  5. Вернитесь в раздел Deployments (Развертывания), выберите gatekeeper и откройте вкладку Sessions (Сеансы). Последний сеанс A2A_USER_ был запущен из развернутого планировщика, а не из Cloud Shell.

Когда вы будете удалять ресурсы на следующем шаге, удалите и планировщик:

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

9. 9. Устранение неполадок и удаление объектов

Устранение неполадок

Symptom

Исправить

UREQ_TOS_NOT_ACCEPTED

Примите Условия использования в консоли Cloud и повторите попытку.

Проект: (unset)

Повторно выполните команды проекта, указанные в шаге 2.

service account info is missing 'email' field

Запустите gcloud auth application-default login (шаг 2), а затем перезапустите все работающие серверы.

FileNotFoundError (SKILL.md)

Выполните шаг 4 или скопируйте $ASSETS/solution/track-planner/. в ~/track-planner/.

Connection refused – localhost:8001

Запустите локальный Gatekeeper (шаг 5) или задайте для GATEKEEPER_A2A_URL развернутый (шаг 8).

Permission 'resourcemanager.projects.setIamPolicy' denied при развертывании

В вашем аккаунте нельзя использовать идентификатор агента. Выполните инструкции в поле "Очистка" на шаге 7, а затем разверните сервисный аккаунт.

Развернутый планировщик треков сообщает 403 Forbidden от Gatekeeper

Идентификатор планировщика не может вызывать Gatekeeper. Разверните его повторно с помощью --service-account, как описано на шаге 7, или с помощью --agent-identity, если вы владелец проекта.

429 RESOURCE_EXHAUSTED

Вы исчерпали квоту Gemini. Подождите минуту или добавьте --concurrency 1 --qps 2 в agents-cli eval run.

Address already in use

Остановите другой сервер или выберите другой --port.

Организовать

Удалите развернутый Gatekeeper, чтобы он не продолжал работать:

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"

Если вы создали проект только для этой практической работы, вы можете удалить его целиком. Если вы находитесь на мероприятии, сохраняйте проект, который вам дали, пока организаторы не скажут иначе.

Что вы узнали

  • agents-cli scaffold create предоставляет стандартный развертываемый проект ADK. Вам нужно только написать агентов, навыки и оценки.
  • Благодаря навыкам агента знания о домене не включаются в запросы, пока они не понадобятся агенту.
  • thinking_level позволяет выбирать между глубиной рассуждений и скоростью для каждого агента.
  • agents-cli eval run превращает вопрос "Работает ли это?" в оценки, которые можно отслеживать.
  • Идентификация агентов и A2A позволяют независимо развернутым агентам безопасно работать вместе.

Подробнее…