Создайте генератор презентаций для агентов: оркестрация, оценка и аналитика с помощью Google Cloud.

1. Введение

баннер мастер-класса агента

Узнайте, как создать многомодальный многоагентный движок, который обрабатывает исходное тематическое задание и организует создание привлекательных материалов для социальных сетей. Используя комплект разработки агентов Google (ADK) и протокол Agent2Agent (A2A), вы будете создавать автономных микроагентов, оформлять правила бренда в виде многоразового навыка агента, развертывать сервис агентов в Cloud Run и оценивать сгенерированные ключевые визуальные элементы на соответствие бренду с помощью функций искусственного интеллекта BigQuery.

Что вы будете делать

В этом практическом занятии вы научитесь выполнять следующие задачи:

  • Организуйте работу нескольких агентов ADK с помощью детерминированного графового рабочего процесса.
  • Преобразуйте фирменные рекомендации в навык агента, который загружается только при необходимости.
  • Оснастите агента инструментом для генерации изображений, который сохраняет результаты его работы в виде артефакта.
  • Предоставьте доступ к агенту в качестве сервиса A2A и вызывайте его с другого агента как с удаленного узла.
  • Разверните Visual Director в Cloud Run и вызовите рабочий процесс через A2A.
  • Оцените соответствие сгенерированных ключевых визуальных элементов бренду в BigQuery с помощью функций искусственного интеллекта.

Что вам понадобится

  • Веб-браузер (например, Chrome).
  • Учетная запись Google Cloud и проект Google Cloud с включенной функцией выставления счетов.

2. Прежде чем начать

Создайте проект в Google Cloud.

  1. В консоли Google Cloud на странице выбора проекта выберите или создайте проект Google Cloud.
  2. Убедитесь, что для вашего облачного проекта включена функция выставления счетов. Узнайте, как проверить, включена ли функция выставления счетов для проекта .

Запустить Cloud Shell

Для загрузки кода, запуска скриптов настройки и развертывания приложения вы будете использовать Google Cloud Shell .

  1. Откройте Google Cloud Shell в браузере (или нажмите «Активировать Cloud Shell» в верхней части консоли Google Cloud):
    https://shell.cloud.google.com/
    
    Нажмите «Продолжить», если появятся дополнительные уведомления. Подождите, пока терминал и редактор полностью загрузятся. Это может занять одну-две минуты.
  2. Настройте идентификатор проекта Google Cloud и регион (например, us-central1 ) в вашей среде, вставив это в терминал (замените и с учетом ваших ценностей):
    export PROJECT_ID="<YOUR_PROJECT_ID>"
    export REGION="<YOUR_REGION>"
    gcloud config set project $PROJECT_ID
    
    Вставьте в терминал Вы должны увидеть сообщение, похожее на следующее:
    Your active configuration is: [cloudshell-####]
    Updated property [core/project]
    
  3. Включите необходимые API Google Cloud для вашего проекта:
    gcloud services enable \
      compute.googleapis.com \
      cloudresourcemanager.googleapis.com \
      aiplatform.googleapis.com \
      storage.googleapis.com \
      storage-component.googleapis.com \
      bigqueryconnection.googleapis.com \
      bigquery.googleapis.com \
      run.googleapis.com \
      secretmanager.googleapis.com \
      cloudbuild.googleapis.com \
      artifactregistry.googleapis.com \
      iap.googleapis.com \
      iam.googleapis.com
    

Клонируйте репозиторий

У нас есть шаблон кода, который поможет вам начать работу с этим практическим заданием. Он относительно небольшой, но репозиторий GitHub, где он находится, довольно большой. Чтобы ускорить клонирование и сэкономить место на диске, мы обычно используем git clone с опцией --filter=blob:none , git sparse-checkout и ещё несколько команд.

Это звучит очень сложно, но антигравитация может всё это сделать за нас!

Для начала давайте настроим Antigravity CLI, чтобы он мог сразу приступить к решению нашей задачи, и запустим его.

В оболочке Cloud Shell выполните следующую команду:

source <(curl -fsSL https://github.com/GoogleCloudPlatform/devrel-demos/raw/refs/heads/vladkol/cloud-shell-configure-agy/codelabs/labs-scripts/bwg-multi-agent-pitch-generator/cloud_shell_configure_agy.sh)

Эта команда настраивает Antigravity CLI и запускает его в том же терминале. После выхода из Antigravity CLI вы возвращаетесь к командной строке Cloud Shell.

Антигравитационный CLI

Нам нужна только папка codelabs/bwg-multi-agent-pitch-generator в репозитории. Давайте попросим Antigravity загрузить только эту папку и разместить её локально в ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/ .

Передайте этой задаче интерфейс командной строки Antigravity:

Download files from under `https://github.com/GoogleCloudPlatform/devrel-demos/tree/main/codelabs/bwg-multi-agent-pitch-generator`
to folder `~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/`.
If `devrel-demos` folder already exists, rename it with a `.backup` suffix.

Подсказка командной строки Antigravity

Antigravity выполнит задачу. Он запросит у вас разрешения на выполнение нескольких команд. После загрузки кода выйдите из командной строки Antigravity, выполнив команду /exit :

/exit

Выход из режима антигравитации CLI

Откройте рабочую область репозитория.

Теперь откроем рабочую область кода в Cloud Shell. Выполните следующую команду в терминале Cloud Shell:

cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/
cloudshell workspace .

Запустите скрипт установки.

Для упрощения процесса запустите скрипт установки из репозитория. Он настраивает переменные среды, устанавливает agents-cli , создает сегмент Cloud Storage для артефактов, устанавливает соединение с облачным ресурсом BigQuery и предоставляет необходимые разрешения IAM для учетной записи службы с автоматическим повтором попыток.

Скрипт установки также вызывает setenv.sh , небольшую вспомогательную программу, которая вычисляет необходимые обоим агентам переменные — GOOGLE_CLOUD_PROJECT , GOOGLE_CLOUD_REGION , GOOGLE_CLOUD_LOCATION и GOOGLE_GENAI_USE_VERTEXAI — на основе значений PROJECT_ID и REGION которые вы экспортировали выше. Он также сохраняет эти два значения в файл .env в вашем домашнем каталоге, чтобы вы могли восстановить их позже.

  1. В Cloud Shell сделайте скрипт установки исполняемым и запустите его:
    chmod +x codelab-setup.sh setenv.sh
    ./codelab-setup.sh
    
  2. Выполнение займет около минуты. Вы должны увидеть сообщение об успешном завершении: "🎉 🦄 Выполнение скрипта завершено." успех
  3. Скрипт установки запускался в отдельной оболочке, поэтому его переменные не попадали в ваш терминал. Загрузите их в свою сессию:
    source ./setenv.sh
    
    Вы должны увидеть, что ваш проект, регион и местоположение отображаются в ответном изображении.

3. Организуйте работу агентов с помощью графового рабочего процесса.

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

Для презентации необходима концепция, то есть идея и материалы, которые её продвигают. В этом примере мы разработаем агентов, выполняющих разные задачи в рамках общего объема работы, демонстрируя, как можно разделить сложные задачи и делегировать их различным агентам с разными обязанностями, что упростит управление ими.

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

Создание основы для первого агентского проекта

  1. В Cloud Shell создайте проект, вставив в терминал следующую команду:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator
    agents-cli create pitch-generator \
      --agent adk \
      --agent-directory pitch_generator \
      --deployment-target cloud_run \
      --session-type in_memory \
      --cicd-runner skip \
      --region <YOUR_REGION> \
      --auto-approve
    
    Это создаст полноценный, запускаемый проект: агент, Dockerfile , тесты, Terraform для последующего использования и файл .env с его шаблоном .env.example . Для этого практического занятия важны две части:
    • pitch_generator/agent.py — это агент, и именно этот файл вы перезаписываете.
    • pitch_generator/ — это имя пакета, задаваемое параметром --agent-directory .
    Агент считывает название проекта и регион из среды, которую вы экспортировали с помощью setenv.sh , поэтому здесь ничего настраивать не нужно.
  2. Установите зависимости:
    cd pitch-generator
    agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
    
    Примечание: В этом примере также предусмотрена возможность подключения проекта к протоколу A2A, который будет использоваться далее в этом практическом занятии. Пока оставьте эти файлы без изменений.

Разберитесь в графике.

ADK v2.x поддерживает графовые рабочие процессы. Граф строится из узлов , которые являются единицами работы, и ребер , которые их соединяют. Узел обычно представляет собой агента, хотя подойдет и простая функция Python. Ребро указывает, что когда один узел завершает работу, запускается следующий.

Ребра записываются в виде списка, и ADK определяет порядок их расположения:

edges=[
    ("START", creative_director),        ## the graph begins here
    (creative_director, copywriter),     ## then this
]

Вложенность придает преимуществу его смысл:

Край

Значение

(a, b, c)

Цепочка. а, затем б, затем в, один за другим.

(a, (b, c))

Веерное распространение. а) затем б) и в) одновременно.

((a, b), c)

Вентиляционный вход c ожидает выполнения условий a и b.

Для объединения входных данных используется JoinNode , который ожидает завершения каждого предыдущего узла и передает их выходные данные вместе.

В этом задании мы построим следующий рабочий процесс:

начальный график рабочего процесса

Создайте конвейер

  1. Откройте файл агента, вставив в терминал следующую команду:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    cloudshell edit pitch_generator/agent.py
    
    В редакторе Cloud Shell вы увидите сгенерированный файл agent.py Подсказка : В любой момент вы можете выделить код и использовать значок лампочки, чтобы попросить Gemini объяснить его. Вы также можете обратиться к интерфейсу командной строки Antigravity. Воспользуйтесь Gemini Code Assist
  2. Замените весь файл pitch_generator/agent.py в редакторе следующим содержимым:
    import mimetypes
    import os
    
    from dotenv import load_dotenv
    from google.adk.agents import Agent
    from google.adk.agents.context import Context
    from google.adk.agents.remote_a2a_agent import (
       AGENT_CARD_WELL_KNOWN_PATH,
       RemoteA2aAgent,
    )
    from google.adk.apps import App
    from google.adk.events.event import Event
    from google.adk.models import Gemini
    from google.adk.workflow import JoinNode, Workflow
    from google.genai import types
    
    load_dotenv()
    
    MODEL = "gemini-3.8-flash"
    
    def _model() -> Gemini:
       return Gemini(
          model=MODEL,
          client_kwargs={"location": "global"},
          retry_options=types.HttpRetryOptions(attempts=3),
       )
    
    """ ⬇️ Configure visual-director RemoteA2aAgent agent below this comment """
    
    
    creative_director = Agent(
       name="creative_director",
       model=_model(),
       description="Turns a raw idea into a campaign concept.",
       instruction=(
          "You are the Creative Director. Turn the idea you are given into ONE "
          "punchy campaign concept line with the rationale explanation behind it."
       ),
       output_key="creative_director",
    )
    
    copywriter = Agent(
       name="copywriter",
       model=Gemini(model=MODEL, retry_options=types.HttpRetryOptions(attempts=3)),
       description="Writes social copy for a campaign concept.",
       instruction=(
          "You are the Copywriter. Write ONE short social caption for the "
          "campaign concept you are given. Under 25 words."
       ),
    )
    
    """ Waits for every node wired into it, then hands their outputs on together."""
    assemble = JoinNode(name="assemble")
    
    def package(node_input: dict):
       """Render the joined branches as the finished pitch.
       """
    
       pitch = "\n\n".join(
          [
                f"CONCEPT\n{node_input['creative_director']}",
                f"COPY\n{node_input['copywriter']}",
          ]
       )
       """ The first event is what the user sees. The second is the node's output,
       which any downstream node would receive."""
       yield Event(content=types.Content(role="model", parts=[types.Part(text=pitch)]))
       yield Event(output=pitch)
    
    """ we put creative_director before `assemble`  so that we can
       gather the `Concept` output at assemble"""
    root_agent = Workflow(
       name="pitch_generator",
       edges=[
          ("START", creative_director),
          (creative_director, copywriter),
          ((creative_director, copywriter), assemble),
          (assemble, package),
       ],
    )
    
    app = App(
       root_agent=root_agent,
       name="pitch_generator",
    )
    
  3. agents-cli включает в себя встроенный пользовательский интерфейс для веб-разработки, называемый playground , для проведения инспекций, аналогичный ADK. Чтобы запустить игровую площадку:
    agents-cli playground --port 8080
    
    Вам будет предложено включить телеметрию. Введите n и нажмите Enter.
  4. Нажмите кнопку «Веб-предварительный просмотр» (значок глаза в правом верхнем углу терминала) и выберите «Предварительный просмотр на порту 8080» . нажмите на предварительный просмотр веб-страницы
  5. Вы увидите следующий интерфейс для общения с агентом. Попробуйте отправить запрос "Летающие скейтборды для кошек" . Вы увидите ответ, как показано ниже, где вы также можете просмотреть полную схему рабочего процесса. демонстрационный рабочий процесс Изучите, как был получен этот ответ. В песочнице показан каждый шаг, предпринятый графом, в следующем порядке: creative_director , затем copywriter , затем assemble , затем package . Эта последовательность — это тот граф, который вы создали, и таким образом вы можете быстро подтвердить правильность только что построенного графа.
  6. Вернитесь в терминал и остановите запущенный процесс agents-cli playground дважды нажав Ctrl+C .

4. Наделите агента навыком.

В этом задании вы создадите второго агента и предоставите ему набор фирменных рекомендаций, оформленных в виде навыка.

Конвейер обработки данных пишет слова. Для изображения необходим свой фирменный стиль: цветовая палитра, способ освещения, используемые композиции и те, которые никогда не используются. Это страница правил, а для управления такой страницей правил необходим более совершенный механизм. Один из способов — представить это в виде «навыка агента».

Навык — это папка, содержащая файл SKILL.md . Структура каталога skills выглядит следующим образом (с использованием visual_director , следующего агента, который вы будете создавать):

visual_director/
   ...
   agent.py
   skills/
      brand-guidelines/
         SKILL.md          ## required: frontmatter + instructions
         references/       ## optional: longer supporting documents
         assets/           ## optional: templates, images, data
         scripts/          ## optional: executable helpers
   ...

Точкой входа в навык агента является файл SKILL.md , который содержит раздел YAML в начале файла, подобный показанному ниже:

---
name: brand-guidelines
description: The house brand rules for a key visual. Load this before writing art direction, so the visual looks like it came from this brand.
---

## Brand guidelines

Deep indigo and slate as the ground. One warm accent, amber or terracotta...

Метка (frontmatter) — это заголовок. Это всё, что агент видит сразу, поэтому в описании должно быть указано, когда стоит загружать навык. Тело (body) — это контент, и он остаётся вне контекста до тех пор, пока агент не вызовет load_skill .

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

Мы начнём создавать второго агента, visual-director , используя навыки агента, как показано на диаграмме ниже:

оснащение второго агента навыками

Постройте структуру для визуального директора.

Вам предстоит создать специализированного агента для обработки визуального компонента, visual-director . Это отдельный проект, поскольку в дальнейшем он станет самостоятельным сервисом, демонстрирующим, как несколько агентов взаимодействуют друг с другом, используя протокол A2A. В итоге у вас получится два проекта, работающих параллельно:

bwg-multi-agent-pitch-generator/
├── pitch-generator/              ## built in the last task
│   ├── pitch_generator/
│   │   ├── agent.py              ## the graph you wrote
│   │   ├── app_utils/            ## generated, A2A wiring
│   │   └── fast_api_app.py       ## generated, serves the agent
│   └── pyproject.toml
└── visual-director/              ## created below
    └── ...                       ## the same shape again
  1. Создайте проект и установите его зависимости, вставив следующую команду в терминал :
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator
    agents-cli create visual-director \
      --agent adk \
      --agent-directory visual_director \
      --deployment-target cloud_run \
      --session-type in_memory \
      --cicd-runner skip \
      --region <YOUR_REGION> \
      --auto-approve
    cd visual-director
    agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
    

Разработайте фирменные рекомендации.

  1. Создайте папку с навыками и откройте файл:
    mkdir -p visual_director/skills/brand-guidelines
    cloudshell edit visual_director/skills/brand-guidelines/SKILL.md
    
  2. Вставьте следующее содержимое в открытый файл SKILL.md в редакторе и сохраните его: Навык
    ---
    name: brand-guidelines
    description: The house brand rules for a key visual — palette, light, composition, subject treatment, and what never appears. Load this before writing art direction or rendering an image, so the visual looks like it came from this brand rather than from a generic image model.
    ---
    
    ## Brand guidelines
    
    House style for campaign key visuals. Every rule here beats a preference in the
    brief. If the brief asks for something forbidden below, follow the brief for the
    subject and the guidelines for the treatment.
    
    ## Palette
    
    Deep indigo and slate as the ground. One warm accent — amber or terracotta —
    carrying the eye to the subject. Nothing neon, nothing fluorescent.
    
    At most three colors with any weight. A fourth reads as clutter.
    
    ## Light
    
    One dominant source, low and raking. Long shadows are wanted. Golden hour or a
    single hard studio key, never flat overhead light and never a ring light.
    
    Rim light is allowed on the subject only, and only to separate it from the
    ground.
    
    ## Composition
    
    Subject off-center, roughly a third in. Generous negative space on the opposite
    side — copy lands there later, so leave it genuinely empty.
    
    Eye level or slightly below. No top-down flat lays. No fisheye, no Dutch tilt.
    
    ## Subject
    
    One subject. If the idea implies a crowd, pick the one that carries it.
    
    Photographic realism with a shallow depth of field. Not illustration, not 3D
    render, not clay, not pixel art.
    
    ## Never
    
    - Text, logos, watermarks, or signage anywhere in the frame
    - Collage or split-screen layouts
    - Chrome, lens flare, bokeh sparkle
    - Cool-toned neon cyan or magenta
    
    ## Rendering the prompt
    
    When you turn art direction into an image prompt, state the palette, the light,
    and the composition explicitly. An image model will not infer them from the
    subject alone. Put the "Never" items in as plain description of what the frame
    does contain, not as negatives — image models handle a positive description far
    better than a list of prohibitions.
    

Обновите файл visual_director/agent.py

Обновите файл agent.py и настройте его таким образом, чтобы он был оснащен ранее определенным навыком.

  1. Откройте файл агента:
    cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py
    
  2. Замените всё содержимое файла visual_director/agent.py следующим кодом:
    import mimetypes
    from pathlib import Path
    
    from google import genai
    from google.adk.agents import Agent
    from google.adk.apps import App
    from google.adk.models import Gemini
    from google.adk.skills import load_skill_from_dir
    from google.adk.tools import ToolContext
    from google.adk.tools.skill_toolset import SkillToolset
    from google.genai import types
    MODEL = "gemini-3.8-flash"
    
    ## Configure the `brand-guidelines` skill for the agent
    BRAND_SKILL = load_skill_from_dir(Path(__file__).parent / "skills" / "brand-guidelines")
    
    ## Add definition of `generate_key_visual` tool below this comment
    
    root_agent = Agent(
       name="visual_director",
       model=Gemini(
          model=MODEL,
          client_kwargs={"location": "global"},
          retry_options=types.HttpRetryOptions(attempts=3),
       ),
       description="Turns a campaign concept into art direction and a key visual.",
       instruction="""You are the Visual Director on a campaign pitch team.
    
       Call `load_skill` for `brand-guidelines` before you write anything. The house
       style is not optional and it is not in this prompt.
    
       You are given a campaign concept. Then, in order:
    
       1. Write art direction for ONE key visual that sells it, obeying the brand
       guidelines: subject, composition, lighting, color, mood. Three or four
       sentences.
       2. Call `generate_key_visual` with exactly that art direction.
    
       Your final reply is the art direction itself and nothing else. No preamble, no
       alternatives, no questions back. Do not mention the skill, the tool, or the
       image file — the image travels on its own.""",
       tools=[
          SkillToolset(skills=[BRAND_SKILL]),
          ## Supply the `generate_key_visual` tool below this comment
       ],
    )
    
    app = App(
       root_agent=root_agent,
       name="visual_director",
    )
    
    Теперь агент visual-director корректно настроен и имеет соответствующий навык. Перед тестированием необходимо оснастить этого агента инструментом для генерации графических ресурсов, что станет одной из его ключевых функций.

5. Оснастить агента инструментом для генерации изображений.

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

инструмент и создаваемый им артефакт

Внедрить и добавить инструмент в агента.

  1. Откройте файл агента:
    cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py
    
  2. Скопируйте следующий инструмент generate_key_visual перед определением root_agent :
    async def generate_key_visual(art_direction: str, tool_context: ToolContext) -> dict:
       """Render the art direction as the campaign's key visual.
    
       Args:
          art_direction: The art direction to render, in full.
    
       Returns:
          The saved artifact's filename, version, size and media type.
       """
       IMAGE_MODEL = "gemini-3.1-flash-lite-image"
    
       response = await genai.Client(location="global").aio.models.generate_content(
          model=IMAGE_MODEL, contents=art_direction
       )
    
       ## The image arrives as inline bytes on one of the response parts.
       image = next(
          (
             part.inline_data
             for part in response.candidates[0].content.parts or []
             if part.inline_data
          ),
          None,
       )
       if image is None:
          raise ValueError(f"{IMAGE_MODEL} returned no image for: {art_direction[:120]}")
    
       filename = "key_visual" + (
          mimetypes.guess_extension(image.mime_type or "") or ".bin"
       )
       version = await tool_context.save_artifact(filename, types.Part(inline_data=image))
    
       ## Return the artifact filename and version
       return {
          "filename": filename,
          "version": version,
          "bytes": len(image.data),
          "mime_type": image.mime_type,
       }
    
  3. Добавьте generate_key_visual ниже SkillToolset , чтобы оснастить агента этим инструментом:
       tools=[
          SkillToolset(skills=[BRAND_SKILL]),
          ## Supply the `generate_key_visual` tool below this comment
          generate_key_visual,
       ],
    
  4. agents-cli включает в себя встроенный пользовательский интерфейс для веб-разработки, называемый playground , для проведения инспекций, аналогичный ADK. Чтобы запустить игровую площадку:
    agents-cli playground --port 8080
    
  5. Нажмите кнопку «Веб-предварительный просмотр» (значок глаза в правом верхнем углу терминала) и выберите «Предварительный просмотр на порту 8080» . нажмите на предварительный просмотр веб-страницы
  6. Вы увидите следующий интерфейс для общения с оператором. Попробуйте отправить сообщение "Девять жизней. Невесомость". демонстрационный рабочий процесс В ходе взаимодействия можно увидеть, как агент visual_director выполняет load_skill("brand-guidelines") для чтения навыка, описывающего фирменный стиль, а затем выполняет generate_key_visual для начала генерации изображения. Полученное изображение сохраняется в виде артефакта, который также отображается в пользовательском интерфейсе и может быть легко просмотрен.
  7. Вернитесь в терминал и остановите запущенный процесс agents-cli playground дважды нажав Ctrl+C .

6. Многоагентная архитектура с протоколом A2A

В этом задании вам нужно будет предоставить доступ к Visual Director в качестве сервиса, к которому смогут обращаться другие агенты, и настроить его таким образом, чтобы он отвечал сгенерированным изображением.

Agent2Agent (A2A) — это открытый протокол, позволяющий одному агенту вызывать другого по сети. Центральным элементом является карточка агента — JSON-документ, который агент публикует о себе через известный URL-адрес, указывая свое имя, описание и возможности. Вызывающая сторона получает карточку и знает, как взаимодействовать с агентом, стоящим за ней, без общего исходного кода, зависимостей или языка программирования.

Протокол A2A

agents-cli уже настроил это по умолчанию. В результате генерации шаблона были созданы файлы visual_director/app_utils/a2a.py , который собирает карточку из вашего агента и монтирует маршруты, и visual_director/fast_api_app.py , который их обслуживает. Вам потребуется лишь небольшая модификация, чтобы разрешить отправку артефактов обратно клиенту.

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

настройка сервера A2A

Структура сервера agents-cli A2A

При инициализации агента visual-director с помощью agents-cli уже предоставляются следующие код и модуль. Откройте эти файлы, вставив в терминал следующие команды:

cloudshell edit visual_director/fast_api_app.py
cloudshell edit visual_director/app_utils/a2a.py

visual_director/fast_api_app.py запускает агент и монтирует маршруты A2A по пути, сформированному из имени приложения:

await attach_a2a_routes(
    app,
    agent=root_agent,
    runner=runner,
    rpc_path=f"/a2a/{adk_app.name}",   ## becomes /a2a/visual_director
)

visual_director/app_utils/a2a.py выполняет остальную работу в три этапа.

Во-первых, система формирует карточку агента на основе самого агента . Имя, описание и каждый инструмент становятся элементами, которые может прочитать звонящий:

agent_card = await AgentCardBuilder(
    agent=agent,
    rpc_url=f"{resolved_app_url}{rpc_path}",
).build()

Во-вторых, это размещает исполнителя между протоколом и вашим агентом. Входящие запросы A2A преобразуются в запуски ADK, а результаты преобразуются обратно:

request_handler = DefaultRequestHandler(
    agent_executor=A2aAgentExecutor(runner=runner, force_new_version=True),
    task_store=task_store,
    agent_card=agent_card,
)

Во-третьих, система использует два маршрута: один для обслуживания карты, другой для приема звонков.

Маршрут

Цель

/a2a/visual_director/.well-known/agent-card.json

Карточка. Звонящий сначала читает её, чтобы узнать, что может сделать оператор.

/a2a/visual_director

Конечная точка JSON-RPC. Куда поступают фактические запросы.

Пусть артефакты пересекают границу.

По умолчанию ответ A2A содержит текст. Изображение является артефактом, а артефакты остаются в хранилище обслуживающего агента, если вы не настроите их на включение.

  1. Откройте сгенерированный файл A2A:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director
    cloudshell edit visual_director/app_utils/a2a.py
    
  2. Найдите импорт AgentCardBuilder и добавьте ниже него импорты A2aAgentExecutorConfig и include_artifacts_in_a2a_event_interceptor :
    ## ...
    ## from google.adk.a2a.utils.agent_card_builder import AgentCardBuilder
    ## Add the following two below `AgentCardBuilder` import
    from google.adk.a2a.executor.config import A2aAgentExecutorConfig
    from google.adk.a2a.executor.interceptors.include_artifacts_in_a2a_event import (
       include_artifacts_in_a2a_event_interceptor,
    )
    
  3. Найдите строку agent_executor=A2aAgentExecutor(runner=runner), и замените её следующей (добавив A2aAgentExecutorConfig ):
             agent_executor=A2aAgentExecutor(
                runner=runner,
                force_new_version=True,
                config=A2aAgentExecutorConfig(
                   execute_interceptors=[include_artifacts_in_a2a_event_interceptor]
                ),
             ),
    

Запустите сервис и ознакомьтесь с опубликованной карточкой A2A.

  1. Запустите сервер в фоновом режиме. Вам необходимо объявить APP_URL , поскольку это адрес, который карта рекламирует как свой собственный. Вставьте это в терминал :
    APP_URL=http://localhost:8801 nohup uv run uvicorn visual_director.fast_api_app:app --port 8801 > /tmp/visual-director.log 2>&1 &
    
  2. Подождите несколько секунд, пока запустится приложение, затем подтвердите, что оно запущено:
    sleep 10
    tail -2 /tmp/visual-director.log
    
    Вам следует увидеть:
    INFO:     Application startup complete.
    INFO:     Uvicorn running on http://127.0.0.1:8801 (Press CTRL+C to quit)
    
  3. Ознакомьтесь с визитной карточкой, которую теперь публикует агент:
    curl -s http://localhost:8801/a2a/visual_director/.well-known/agent-card.json | python3 -m json.tool
    
    На карточке указаны имя агента, описание, URL-адрес и навыки, включая generate_key_visual . Она генерируется непосредственно из определения агента в коде.
    {
       "name": "visual_director",
       "description": "Turns a campaign concept into art direction and a key visual.",
       "skills": [
            {
                "id": "visual_director",
                "name": "model",
                "tags": [
                    "llm"
                ]
            },
            {
                "id": "visual_director-generate_key_visual",
                "name": "generate_key_visual",
                "description": "Render the art direction as the campaign's key visual.\n\nArgs:\n   art_direction: The art direction to render, in full.\n\nReturns:\n   The saved artifact's filename, version, size and media type.",
                "tags": [
                    "llm",
                    "tools"
                ]
            }
       ],
       "preferredTransport": "JSONRPC",
       "protocolVersion": "0.3",
       "url": "http://localhost:8801/a2a/visual_director"
    }
    
    Вы готовы интегрировать агент visual-director в основной рабочий процесс pitch-generator .

7. Встройте удаленного агента A2A в рабочий процесс.

В этом задании вам нужно будет добавить удалённого агента в граф в качестве третьей ветви.

Удаленный агент заключен в RemoteA2aAgent и размещается в графе как любой другой узел. Ребра сохраняют свою форму, и координатор не знает, какие из его узлов являются локальными.

Укажите координатору на сервис и обновите рабочий процесс графа.

  1. Откройте файл агента:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    cloudshell edit pitch_generator/agent.py
    
  2. Добавьте следующие импорты в начало файла, рядом с уже существующими:
    import asyncio
    
    import subprocess
    import google.auth.transport.requests
    import google.oauth2.id_token
    import httpx
    from google.adk.a2a.converters.part_converter import convert_genai_part_to_a2a_part
    
  3. Добавьте следующее определение RemoteA2aAgent перед объявлением creative_director :
    VISUAL_DIRECTOR_URL = os.getenv("VISUAL_DIRECTOR_URL", "http://localhost:8801")
    
    ## For deployment on Cloud Run
    def _cloud_run_client() -> httpx.AsyncClient | None:
       """An httpx client that signs each request with a Cloud Run ID token.
    
       Cloud Run deploys private, so both the agent-card fetch and the A2A calls
       need one.
       """
       if not VISUAL_DIRECTOR_URL.startswith("https://"):
          return None
    
       async def sign(request: httpx.Request) -> None:
          token = None
          try:
             token = await asyncio.to_thread(
                   google.oauth2.id_token.fetch_id_token,
                   google.auth.transport.requests.Request(),
                   VISUAL_DIRECTOR_URL,
             )
          except Exception:
             token = subprocess.check_output(
                ["gcloud", "auth", "print-identity-token", "-q"],
                stderr=subprocess.PIPE
             ).decode().strip()
          if not token:
                raise Exception("Failed to fetch ID token")
          request.headers["Authorization"] = f"Bearer {token}"
    
       return httpx.AsyncClient(event_hooks={"request": [sign]}, timeout=600)
    
    ## Keep the approval's tool traffic out of the A2A request
    def _pitch_parts_only(part: types.Part):
       """Sends the Visual Director text and media only.
    
       RemoteA2aAgent replays the session on every call, and the Visual Director
       runs downstream of the human approval, so the approval's function_response
       would travel alongside the concept text. An ADK server reads a function
       response as "resume the paused invocation" and rejects a message that also
       carries text, so drop the tool parts here.
       """
       if part.function_call or part.function_response:
          return None
       return convert_genai_part_to_a2a_part(part)
    
    visual_director = RemoteA2aAgent(
       name="visual_director",
       description="Turns a campaign concept into art direction for one key visual.",
       agent_card=(
          f"{VISUAL_DIRECTOR_URL}/a2a/visual_director{AGENT_CARD_WELL_KNOWN_PATH}"
       ),
       httpx_client=_cloud_run_client(),
       genai_part_converter=_pitch_parts_only,
    )
    
  4. Замените или переопределите функцию Python package node следующим определением:
    async def package(ctx: Context, node_input: dict):
       """Render the joined branches as the finished pitch."""
       empty = [name for name, value in node_input.items() if not value]
       if empty:
          raise ValueError(f"nothing reached the join from: {', '.join(empty)}")
    
       def _key_visual(ctx: Context) -> types.Blob | None:
          """The image the Visual Director sent back, or None if it sent none."""
          for event in reversed(ctx.session.events):
             if event.author != "visual_director" or not event.content:
                   continue
             for part in event.content.parts or []:
                   if part.inline_data and part.inline_data.data:
                      return part.inline_data
          return None
    
       image = _key_visual(ctx)
       if image is None:
          raise ValueError("the Visual Director returned art direction but no image")
    
       suffix = mimetypes.guess_extension(image.mime_type or "") or ".bin"
       filename = f"key_visual{suffix}"
       await ctx.save_artifact(filename, types.Part(inline_data=image))
    
       pitch = "\n\n".join(
          [
             f"CONCEPT\n{node_input['creative_director']}",
             f"COPY\n{node_input['copywriter']}",
             f"ART DIRECTION\n{node_input['visual_director']}",
             f"KEY VISUAL\n{filename}, {len(image.data)} bytes, {image.mime_type}",
          ]
       )
       yield Event(
          content=types.Content(
             role="model",
             parts=[types.Part(text=pitch), types.Part(inline_data=image)],
          )
       )
       yield Event(output=pitch)
    
  5. Добавьте этап подтверждения пользователем перед началом генерации последующих данных. В pitch_generator/agent.py определите узлы подтверждения выше root_agent :
    @node(rerun_on_resume=False)
    async def approve_concept(ctx: Context):
        yield RequestInput(
            message="Please approve the campaign concept (yes/no).",
            response_schema=str
        )
    
    @node(rerun_on_resume=True)
    async def user_approval(ctx: Context):
       user_response = await ctx.run_node(approve_concept)
       if str(user_response).lower() in ("yes", "y"):
          approved_concept = f"## Approved Concept\n\n{ctx.session.state['creative_director']}"
          yield Event(
             content=types.Content(
                role="model",
                parts=[types.Part(text=approved_concept)]
             )
          )
       else:
          raise ValueError("User rejected the concept")
    
  6. Наконец, обновите импорт вверху, добавив RequestInput и node из google.adk.events и google.adk.workflow :
    from google.adk.events import Event, RequestInput
    from google.adk.workflow import JoinNode, Workflow, node
    
    и изменить edges в рабочем процессе root_agent таким образом, чтобы маршрутизация осуществлялась через user_approval :
        edges=[
            ("START", creative_director),
            (creative_director, user_approval, (copywriter, visual_director)),
            ((creative_director, copywriter, visual_director), assemble),
            (assemble, package),
        ],
    
    Следует отметить несколько деталей:
    • Подтверждение с участием человека (Human-in-the-Loop, HITL) : approve_concept возвращает RequestInput . Когда рабочий процесс достигает этого узла, выполнение приостанавливается и отправляется прерывание вызывающей стороне. Ветви нижестоящего процесса ( copywriter и visual_director ) не будут выполняться до подтверждения одобрения пользователя.
    • visual_director отображается на периферии сети точно так же, как copywriter , хотя один доступен через вызов функции, а другой — через сеть.
    • Возвращаемое изображение считывается из событий сессии, а не из node_input , поскольку в настоящее время на выход узла передается только текст. Изображение пересекло границу через перехватчик A2A, но прибыло прикрепленным к событиям удаленного агента, поэтому вспомогательная функция _key_visual извлекает его оттуда.

Проверьте интеграцию

Теперь вам предстоит проверить, могут ли Visual Director (работающий в фоновом режиме) и Pitch Generator обмениваться данными по протоколу A2A.

  1. Убедитесь, что Visual Director по-прежнему работает в фоновом режиме:
    curl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.json
    
    В этом случае вы должны получить код ошибки visual director: 200 Если вы получите другой статус , выполните следующую команду для перезапуска:
    fuser -k 8801/tcp 2>/dev/null || true
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director
    APP_URL=http://localhost:8801 nohup uv run uvicorn visual_director.fast_api_app:app --port 8801 > /tmp/visual-director.log 2>&1 &
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    sleep 10
    curl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.json
    
  2. Начать предварительный просмотр:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator/pitch_generator
    agents-cli playground --port 8080
    
  3. Нажмите кнопку «Веб-предварительный просмотр» (значок глаза в правом верхнем углу терминала) и выберите «Предварительный просмотр на порту 8080» . нажмите на предварительный просмотр веб-страницы
  4. Вы увидите интерфейс для общения с агентом. Попробуйте отправить сообщение "Летающие скейтборды для кошек" . демонстрационный рабочий процессдемонстрационный рабочий процесс Вы увидите полное взаимодействие, где запускается рабочий процесс и выполняется каждый агент. После того, как creative_director разрабатывает концепцию, рабочий процесс приостанавливается на этапе утверждения и запрашивает подтверждение: «Пожалуйста, утвердите концепцию кампании (да/нет)». Введите «да» в поле ввода, чтобы продолжить. После утверждения график расширяется: вы можете увидеть, когда агент visual-director начинает загрузку навыка, и в середине генерации изображения агент copywriter завершает генерацию подписи для социальных сетей — это показывает, что оба агента работают параллельно, не дожидаясь друг друга. После завершения работы обоих узлов сборки, узел assemble объединяет результаты с утвержденной концепцией, сгенерированной creative_director , завершая доставку контента для презентации в функции package . две службы, соединенные по протоколу A2A

8. Сохраните ключевые визуальные элементы в облачном хранилище.

До сих пор все ключевые визуальные элементы хранились в памяти. Как только процесс останавливается, изображение исчезает, что хорошо для тестовой среды, но бесполезно для рекламной кампании. В этом задании вы указываете для рабочего процесса хранилище данных и создаете набор данных BigQuery, который будете использовать в последнем задании.

ADK определяет, куда будут помещены артефакты при запуске. Сгенерированный файл app_utils/services.py считывает одну переменную окружения:

def get_artifact_service():
    """Process-wide artifact service: GCS when a bucket is set, else in-memory."""
    if bucket := os.environ.get("LOGS_BUCKET_NAME"):
        return GcsArtifactService(bucket_name=bucket)
    return InMemoryArtifactService()

Если вы зададите LOGS_BUCKET_NAME , каждый вызов save_artifact будет попадать в Cloud Storage.

Интеграция сервиса артефактов с хранилищем GCS.

Создайте набор данных BigQuery.

  1. В Cloud Shell выполните следующую команду для создания набора данных BigQuery:
    bq mk --dataset --location=<YOUR_REGION> --project_id=<YOUR_PROJECT_ID> bwg
    
  2. Направьте хранилище артефактов координатора на ведро:
    export LOGS_BUCKET_NAME="<YOUR_PROJECT_ID>-bwg"
    
    Переменная GcsArtifactService принимает имя хранилища без префикса gs:// . Как и другие переменные, эта переменная находится в вашей оболочке, и задача развертывания передает ее в Cloud Run.
  3. Проверьте содержимое вашего хранилища. В новой вкладке браузера откройте следующий URL-адрес:
    https://console.cloud.google.com/storage/browser?project=<YOUR_PROJECT_ID>
    
    Нажмите на значок корзины. В зависимости от ваших предыдущих тестов, он может содержать ранее сгенерированные изображения.

9. Разверните агент Visual Director в Cloud Run.

До сих пор все работало на одной машине, при этом Visual Director был доступен по адресу localhost:8801 . В этой задаче вы развертываете агент Visual Director как отдельную службу Cloud Run и позволяете генератору презентаций взаимодействовать с ним по сети, используя протокол Agent2Agent (A2A).

Визуальный директор получает реальный URL-адрес https:// , поэтому написанный вами ранее _cloud_run_client начинает подписывать запросы вместо того, чтобы возвращать None . Кроме того, Cloud Run по умолчанию развертывает приватный режим.

оба агента как сервисы Cloud Run

Разверните Visual Director.

  1. Если локальная фоновая служба и детская площадка все еще работают, остановите их:
    fuser -k 8801/tcp 2>/dev/null || true
    fuser -k 8000/tcp 2>/dev/null || true
    
  2. Разверните агента. Это создаст контейнер и загрузит его.⏰ Это займет несколько минут.
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director
    source .env
    agents-cli deploy \
      --service-name visual-director \
      --project <YOUR_PROJECT_ID> \
      --region <YOUR_REGION> \
      --no-confirm-project \
      --labels dev-tutorial=bwg001-devlab-1 \
      --update-env-vars "LOGS_BUCKET_NAME=$LOGS_BUCKET_NAME,GOOGLE_GENAI_USE_VERTEXAI=true,GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT,GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION,GOOGLE_CLOUD_REGION=$GOOGLE_CLOUD_REGION"
    

Запустите генератор презентаций, использующий Visual Director, развернутый в Cloud Run.

  1. Прочитайте URL-адрес Visual Director и запустите генератор презентаций, который его использует:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    source .env
    export VISUAL_DIRECTOR_URL=$(gcloud run services list \
      --project <YOUR_PROJECT_ID> \
      --region <YOUR_REGION> \
      --flatten='spec.template.spec.containers[0].env' \
      --filter='metadata.name=visual-director AND spec.template.spec.containers[0].env.name=APP_URL' \
      --format='value(spec.template.spec.containers[0].env.value)')
    echo "Visual Director is at $VISUAL_DIRECTOR_URL"
    APP_URL=http://localhost:8000 nohup uv run uvicorn pitch_generator.fast_api_app:app --port 8000 > /tmp/visual-pitch_generator.log 2>&1 &
    

Вызовите развернутый рабочий процесс

Оба агента поддерживают протокол A2A. В репозитории содержится небольшой клиент для вызова Pitch Generator через A2A.

Необходимо установить PITCH_GENERATOR_URL равным URL-адресу генератора презентаций, работающего локально. При этом Visual Director работает в Cloud Run, а генератор презентаций взаимодействует с ним по протоколу A2A.

  1. Скопируйте клиент в свой проект и запустите его:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    cp ../solution/pitch-generator/call_agent.py .
    PITCH_GENERATOR_URL=http://localhost:8000 uv run python call_agent.py "Flying skateboards for cats" --save-image ./key_visual.png
    
    Поскольку рабочий процесс включает в себя этап утверждения человеком , выполнение скрипта приостанавливается после генерации чернового варианта концепции:
    === HUMAN APPROVAL REQUIRED ===
    
    [DRAFT CONCEPT]
    Concept Line: "Gravity is a Suggestion."
    ...
    
    Please approve the campaign concept (yes/no):
    
    Введите yes и нажмите Enter , чтобы подтвердить и продолжить рабочий процесс. (Вы также можете указать -y или --auto-approve для автоматического подтверждения).
  2. Откройте call_agent.py и изучите, как он взаимодействует с агентом, используя a2a-sdk :
    • ClientFactory создает официальный A2A- Client ориентированный на https://SERVICE_URL/a2a/pitch_generator .
    • generate_pitch_async отправляет первоначальный SendMessageRequest . Когда в ответ возвращается TaskState.TASK_STATE_INPUT_REQUIRED , она обнаруживает часть adk_request_input , отображает черновой вариант концепции, запрашивает ваше одобрение и отправляет запрос на возобновление, содержащий ваш ответ, обратно в задачу.
    • Цикл анализа считывает разделы CONCEPT , COPY и ART DIRECTION из артефактов выполненной задачи и сохраняет двоичное изображение в key_visual.png .
    Вы должны увидеть три текстовых фрагмента, а изображение будет сохранено в формате key_visual.png .
  3. Загрузите ключевой визуальный элемент в свой репозиторий, указав префикс, на который BigQuery сможет указать:
    gcloud storage cp ./key_visual.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png
    
  4. Подготовьте вторую презентацию, чтобы было с чем сравнить, и загрузите её тоже:
    PITCH_GENERATOR_URL=http://localhost:8000 uv run python call_agent.py "A commuter bike built for rainy cities" --save-image ./bike.png
    gcloud storage cp ./bike.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/bike.png
    

10. Оцените ключевые визуальные элементы на соответствие бренду в BigQuery.

Теперь у вас есть изображения в хранилище и фирменные рекомендации, которые должны были их формировать. В этой задаче вы объединяете эти два элемента: получаете изображения из BigQuery, а затем просите модель Gemini оценить каждое из них в соответствии с рекомендациями, не перемещая ни единого байта из Cloud Storage.

Для этого используются две функции BigQuery:

  • ObjectRef — это значение, указывающее на объект Cloud Storage. OBJ.MAKE_REF создает его из URI и соединения, предоставляющего BigQuery делегированный доступ к хранилищу. Созданное скриптом настройки pitch-connection является именно таким соединением, и оно уже содержит Storage Object Viewer и Agent Platform User.
  • Функция AI.SCORE принимает запрос и возвращает значение типа FLOAT64 . Запрос может содержать как строки, так и значения ObjectRef , поэтому модель видит вашу рубрику и изображение вместе.

BigQuery считывает данные из хранилища.

Создайте таблицу ключевых визуальных элементов.

  1. Откройте BigQuery Studio в новой вкладке браузера:
    https://console.cloud.google.com/bigquery?project=<YOUR_PROJECT_ID>
    
  2. Нажмите «Создать новый запрос» (значок « +» ), затем вставьте и выполните следующий запрос (заменив...). и (с указанием идентификатора вашего проекта и региона). Создается отдельная строка для каждого ключевого визуального элемента, сопоставляющая концепцию кампании со ссылкой на изображение:
    SET @@location = '<YOUR_REGION>';
    
    CREATE OR REPLACE TABLE bwg.key_visuals AS
    SELECT
      campaign,
      concept,
      OBJ.FETCH_METADATA(
        OBJ.MAKE_REF(uri, '<YOUR_REGION>.pitch-connection')
      ) AS key_visual
    FROM UNNEST([
      STRUCT(
        'cats' AS campaign,
        'Flying skateboards for cats' AS concept,
        'gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png' AS uri
      ),
      STRUCT(
        'bike',
        'A commuter bike built for rainy cities',
        'gs://<YOUR_PROJECT_ID>-bwg/key-visuals/bike.png'
      )
    ]);
    
    бк OBJ.MAKE_REF формирует ссылку на основе URI и подключения. Затем OBJ.FETCH_METADATA связывается с Cloud Storage и заполняет данные о размере объекта, типе содержимого и поколении. Этот второй вызов также является самым быстрым способом проверить работоспособность подключения: если права доступа указаны неверно, в поле details возвращается сообщение об ошибке вместо метаданных.
  3. Проверьте сохраненные данные. Замените предыдущий запрос этим и выполните его:
    SELECT campaign, concept, key_visual.uri, key_visual.details
    FROM bwg.key_visuals;
    
    В таблице хранится указатель и его метаданные, а не само изображение. Байты так и не покинули хранилище.

Оцените каждое изображение в соответствии с фирменными рекомендациями.

Визуальному директору было поручено соблюдать цветовую палитру, правила освещения и правила композиции. Выполнил ли он их на практике — это вопрос личного мнения, и именно для этого существует AI.SCORE .

  1. Выполните следующий запрос (заменив (с указанием вашего региона). Задание представляет собой кортеж: ваша рубрика, концепция кампании и ObjectRef для изображения:
    SELECT
      campaign,
      concept,
      AI.SCORE(
        ('''You are the brand guardian reviewing a campaign key visual.
    Score the attached image from 1 to 10 on how closely it follows these house rules:
    - Palette: deep indigo and slate, with one warm accent of amber or terracotta. Nothing neon.
    - Light: one dominant low, raking source with long shadows. No flat overhead light.
    - Composition: subject off-center with generous empty negative space on the opposite side.
    - Subject: one subject only, photographic realism, shallow depth of field.
    - Never: text, logos, or watermarks anywhere in the frame.
    A 10 obeys every rule. A 1 ignores them. The campaign concept is: ''',
          concept,
          key_visual),
        connection_id => '<YOUR_REGION>.pitch-connection',
        endpoint => 'gemini-3.5-flash'
      ) AS brand_fit
    FROM bwg.key_visuals
    ORDER BY brand_fit DESC;
    
  2. Превратите результат в вердикт, на основании которого ваша команда сможет принять меры:
    CREATE OR REPLACE TABLE bwg.brand_review AS
    SELECT
      campaign,
      concept,
      brand_fit,
      IF(brand_fit >= 7, 'on brand', 'needs another pass') AS verdict
    FROM (
      SELECT
        campaign,
        concept,
        AI.SCORE(
          ('''Score this campaign key visual from 1 to 10 on how closely it follows the house
    brand rules: deep indigo and slate palette with a single warm accent and nothing neon; one
    low raking light source with long shadows; the subject off-center with empty negative space
    opposite; a single photographic subject; and no text, logos, or watermarks in the frame.
    The campaign concept is: ''',
              concept,
              key_visual),
          connection_id => '<YOUR_REGION>.pitch-connection',
          endpoint => 'gemini-3.5-flash'
        ) AS brand_fit
      FROM bwg.key_visuals
    );
    
    SELECT * FROM bwg.brand_review ORDER BY brand_fit DESC;
    
    Функция AI.SCORE возвращает только значение типа FLOAT64 , поэтому пороговое значение хранится в обычном SQL-запросе, который можно настроить без изменения параметров запроса. Оценка производится для каждой строки, а это значит, что одна и та же система критериев применяется ко всем ресурсам, созданным в рамках кампании, а не только к двум, сгенерированным вами здесь.

11. Дальнейшие шаги

Создать оценки

Оценки работы агентов, или «тесты», — это автоматизированные тесты, используемые для измерения того, насколько хорошо автономные агенты ИИ рассуждают, планируют, используют инструменты и выполняют многоэтапные рабочие процессы. Они похожи на модульное тестирование агентов, но учитывают их недетерминированную природу.

Один из наших агентов включает Human in the loop , что означает, что он ожидает ввода. Вам потребуется разрешить Antigravity внести изменения в текущий код, чтобы это поведение не мешало автоматическим вычислениям.

Один из лучших способов начать — обратиться в Antigravity CLI.

Для запуска интерфейса командной строки (CLI) введите в Cloud Shell следующее:

cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/
agy

Вот отправная точка:

Explore this repository (`pitch-generator` and `visual-director`), including their agent instructions, tools, and `SKILL.md` files. Design and implement a creative `agents-cli eval` evaluation suite under `tests/eval/` that stress-tests the agent's behavior.

Примеры оценок и инструкции по их выполнению также можно найти в клонированном репозитории .

12. Уборка

Чтобы избежать списания средств с вашего аккаунта Google Cloud за ресурсы, использованные в этом практическом занятии, либо удалите проект (рекомендуется), либо удалите отдельные ресурсы, созданные во время выполнения практического занятия:

gcloud run services delete visual-director --region=<YOUR_REGION> --project=<YOUR_PROJECT_ID> --quiet
bq rm -r -f -d <YOUR_PROJECT_ID>:bwg
bq rm --connection --location=<YOUR_REGION> --project_id=<YOUR_PROJECT_ID> pitch-connection
gcloud storage rm -r gs://<YOUR_PROJECT_ID>-bwg

13. Поздравляем!

В этом практическом задании вы создали генератор презентаций для многоагентных кампаний с помощью Agent Development Kit. Вы подключили трех агентов к детерминированному графовому рабочему процессу, упаковали фирменные руководства в виде навыка агента, загружаемого по запросу, и предоставили агенту инструмент генерации изображений, который сохраняет результат в виде артефакта. Затем вы разделили Visual Director на отдельный сервис A2A, встроили его обратно в граф в качестве удаленного узла, развернули в Cloud Run и вызвали весь рабочий процесс через A2A с локально работающего координатора. Наконец, вы сослались на сгенерированные ключевые визуализации из BigQuery с помощью ObjectRef и использовали AI.SCORE для оценки их соответствия вашим собственным фирменным руководствам.