1. Wprowadzenie

Dowiedz się, jak zbudować wielomodowy silnik z wieloma agentami, który na podstawie krótkiego opisu tematu koordynuje tworzenie angażujących zasobów do mediów społecznościowych. Za pomocą pakietu Agent Development Kit (ADK) od Google i protokołu Agent2Agent (A2A) utworzysz autonomiczne mikroagenty, spakujesz reguły marki jako umiejętność agenta wielokrotnego użytku, wdrożysz usługę agenta w Cloud Run i ocenisz wygenerowane kluczowe elementy wizualne pod kątem zgodności z marką za pomocą funkcji AI BigQuery.
Jakie zadania wykonasz
Z tego modułu dowiesz się, jak:
- Aranżuj wielu agentów ADK za pomocą deterministycznego przepływu pracy w formie wykresu.
- Zapakuj wytyczne dotyczące marki jako umiejętność agenta, która jest wczytywana tylko wtedy, gdy jest potrzebna.
- Wyposaż agenta w narzędzie do generowania obrazów, które zapisuje wyniki jako artefakty.
- Udostępnij agenta jako usługę A2A i wywołaj go z innego agenta jako węzeł zdalny.
- Wdróż Visual Director w Cloud Run i wywołaj przepływ pracy za pomocą A2A.
- Oceniaj wygenerowane kluczowe elementy wizualne pod kątem dopasowania do marki w BigQuery za pomocą funkcji AI.
Czego potrzebujesz
- przeglądarka internetowa (np. Chrome);
- Konto Google Cloud i projekt Google Cloud z włączonymi płatnościami.
2. Zanim zaczniesz
Tworzenie projektu Google Cloud
- W konsoli Google Cloud na stronie wyboru projektu wybierz lub utwórz projekt w chmurze.
- Sprawdź, czy w projekcie Cloud włączone są płatności. Dowiedz się, jak sprawdzić, czy w projekcie są włączone płatności.
Uruchamianie Cloud Shell
Do pobrania kodu, uruchomienia skryptów konfiguracji i wdrożenia aplikacji użyjesz Google Cloud Shell.
- Otwórz Google Cloud Shell w przeglądarce (lub kliknij Aktywuj Cloud Shell u góry konsoli Google Cloud):
Jeśli pojawią się dodatkowe powiadomienia, kliknij Dalej.Poczekaj, aż terminal i edytor wczytają się w całości. Może to potrwać minutę lub dwie.https://shell.cloud.google.com/ - Skonfiguruj identyfikator projektu Google Cloud i region (np.
us-central1) w swoim środowisku, wklejając to w terminalu (zastąpiswoimi wartościami):export PROJECT_ID="<YOUR_PROJECT_ID>" export REGION="<YOUR_REGION>" gcloud config set project $PROJECT_ID
Powinien wyświetlić się komunikat podobny do tego:Your active configuration is: [cloudshell-####] Updated property [core/project] - Włącz wymagane interfejsy Google Cloud API w projekcie:
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
Klonowanie repozytorium
Aby ułatwić Ci rozpoczęcie pracy z tym kursem, przygotowaliśmy szablon kodu. Jest on stosunkowo mały, ale repozytorium GitHub, w którym się znajduje, jest dość duże. Aby przyspieszyć klonowanie i zaoszczędzić miejsce na dysku, zwykle używamy poleceń git clone z opcją --filter=blob:none i git sparse-checkout oraz kilku innych poleceń.
Brzmi to bardzo skomplikowanie, ale Antigravity może to wszystko zrobić za nas.
Najpierw skonfigurujemy interfejs Antigravity CLI, aby mógł od razu rozwiązać nasz problem, a następnie go uruchomimy.
W Cloud Shell uruchom to polecenie:
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)
Polecenie konfiguruje interfejs Antigravity CLI i uruchamia go w tym samym terminalu. Gdy zamkniesz Antigravity CLI, wrócisz do prompta Cloud Shell.

W repozytorium potrzebujemy tylko folderu codelabs/bwg-multi-agent-pitch-generator. Poprośmy Antigravity o pobranie tylko tego folderu i umieszczenie go lokalnie w ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/.
Powierz Antigravity CLI to zadanie:
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 wykona zadanie. Poprosi Cię o uprawnienia do uruchomienia kilku poleceń. Po pobraniu kodu zamknij interfejs wiersza poleceń Antigravity, wykonując polecenie /exit:
/exit

Otwieranie obszaru roboczego repozytorium
Teraz otwórz obszar roboczy kodu w Cloud Shell. Uruchom to polecenie w terminalu Cloud Shell:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/
cloudshell workspace .
Uruchamianie skryptu konfiguracji
Aby ułatwić sobie to zadanie, uruchom skrypt konfiguracji z repozytorium. Konfiguruje zmienne środowiskowe, instaluje agents-cli, tworzy zasobnik Cloud Storage na potrzeby artefaktów, tworzy połączenie z zasobem Cloud BigQuery i przyznaje wymagane uprawnienia IAM kontu usługi z automatyczną ponowną próbą.
Skrypt konfiguracji wywołuje też setenv.sh, czyli mały program pomocniczy, który na podstawie wyeksportowanych powyżej zmiennych PROJECT_ID i REGION wyznacza zmienne potrzebne obu agentom: GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_REGION, GOOGLE_CLOUD_LOCATION i GOOGLE_GENAI_USE_VERTEXAI. Zapisuje też te 2 wartości w pliku .env w katalogu głównym, aby można było je później odzyskać.
- W Cloud Shell ustaw skrypt konfiguracji jako wykonywalny i uruchom go:
chmod +x codelab-setup.sh setenv.sh ./codelab-setup.sh - Potrwa to około minuty. Powinien wyświetlić się komunikat o powodzeniu: „🎉 🦄 Wykonanie skryptu zakończone”.

- Skrypt konfiguracji został uruchomiony w osobnej powłoce, więc jego zmienne nie dotarły do terminala. Wczytaj je do sesji:
Powinny się wyświetlić informacje o projekcie, regionie i lokalizacji.source ./setenv.sh
3. Aranżowanie agentów za pomocą przepływu pracy w formie grafu
W tym zadaniu utworzysz projekt generatora propozycji i zbudujesz przepływ pracy w postaci wykresu, który uruchamia 2 agenty i łączy ich dane wyjściowe.
Prezentacja musi zawierać koncepcję, czyli pomysł i elementy, które go sprzedają. W tym przypadku zaprojektujemy agentów, którzy będą mieli różne obowiązki w zakresie pracy. Pokażemy, jak można rozdzielić złożone zadania i przekazać je różnym agentom o odmiennych obowiązkach, co ułatwi zarządzanie nimi.
Podział tego rodzaju pracy między specjalistów wymaga koordynacji, gdy każdy agent jest uruchamiany. Możesz pozwolić agentowi decydować, kiedy wywołać każdego subagenta, lub zastosować bardziej deterministyczne podejście. W tym ćwiczeniu użyjesz deterministycznego podejścia z przepływem pracy w postaci wykresu.
Utwórz szkielet pierwszego projektu agenta
- W Cloud Shell utwórz projekt, wklejając ten kod w terminalu:
Tworzy to kompletny, gotowy do uruchomienia projekt: agenta,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-approveDockerfile, testy, Terraform do późniejszego wykorzystania i plik.envz jego szablonem.env.example. W tym ćwiczeniu ważne są 2 elementy:pitch_generator/agent.pyto agent i plik, który przepisujesz.pitch_generator/to nazwa pakietu ustawiona przez--agent-directory.
setenv.sh, więc nie musisz niczego konfigurować. - Zainstaluj zależności:
Uwaga: szkielet projektu jest też skonfigurowany do obsługi protokołu A2A, który będzie używany w dalszej części tego modułu. Na razie nie zmieniaj tych plików.cd pitch-generator agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
Odczytywanie wykresu
ADK w wersji 2.x obsługuje przepływy pracy z wykresami. Graf składa się z węzłów, czyli jednostek pracy, oraz krawędzi, które je łączą. Węzeł jest zwykle agentem, ale może też być zwykłą funkcją Pythona. Krawędź oznacza, że gdy jeden węzeł zakończy działanie, następny zostanie uruchomiony.
Krawędzie są zapisywane w postaci listy, a ADK ustala ich kolejność:
edges=[
("START", creative_director), ## the graph begins here
(creative_director, copywriter), ## then this
]
Zagnieżdżenie nadaje krawędzi znaczenie:
Edge | Znaczenie |
| Łańcuch: a, potem b, potem c, jeden po drugim |
| Zwielokrotnienie wyjściowe: a, a następnie b i c w tym samym czasie |
| A fan-in. c czeka na a i b |
W przypadku fan-in używasz JoinNode, który czeka na wszystkich poprzedników i przekazuje ich dane wyjściowe razem.
W tym zadaniu utworzymy ten przepływ pracy:

Tworzenie potoku
- Otwórz plik agenta, wklejając to w terminalu:
W edytorze Cloud Shell zobaczysz szkieletcd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator cloudshell edit pitch_generator/agent.pyagent.py.💡 Wskazówka: w dowolnym momencie możesz wybrać kod i użyć ikony żarówki, aby poprosić Gemini o wyjaśnienie go. Możesz też zadać pytanie w interfejsie wiersza poleceń Antigravity CLI.
- Zastąp cały plik w
pitch_generator/agent.pyedytorze tym kodem: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", ) agents-clizawiera wbudowany interfejs programistyczny do tworzenia aplikacji internetowych o nazwieplayground, który umożliwia inspekcję podobną do ADK. Aby uruchomić środowisko testowe: Pojawi się pytanie, czy chcesz włączyć telemetrię. Wpiszagents-cli playground --port 8080ni naciśnij Enter.- Kliknij Podgląd w przeglądarce (ikonę oka w prawym górnym rogu terminala) i wybierz Podejrzyj na porcie 8080.

- Aby porozmawiać z pracownikiem obsługi klienta, zobaczysz ten interfejs. Wyślij prompta „Latające deskorolki dla kotów”. Zobaczysz odpowiedź jak poniżej. Możesz też sprawdzić pełny wykres przepływu pracy.
Sprawdź, jak powstała ta odpowiedź. W piaskownicy widać każdy krok wykonany przez wykres w kolejności: creative_director,copywriter,assemble,package. Ta sekwencja to wykres, który został przez Ciebie utworzony. W ten sposób możesz szybko potwierdzić, że wykres został utworzony prawidłowo. - Wróć do terminala i zatrzymaj działający proces
agents-cli playground, naciskając dwukrotnie Ctrl+C.
4. Nadawanie agentowi umiejętności
W tym zadaniu utworzysz drugiego agenta i przypiszesz mu zestaw wytycznych dotyczących marki w postaci umiejętności.
Potok zapisuje słowa. Obraz potrzebuje stylu: palety kolorów, sposobu oświetlania, kompozycji, których używasz, i tych, których nigdy nie używasz. Jest to strona reguł, a strona reguł wymaga lepszego mechanizmu zarządzania. Jednym ze sposobów jest spakowanie go jako „umiejętności agenta”.
Umiejętność to folder zawierający plik SKILL.md. Struktura katalogu umiejętności wygląda tak jak w tym przykładzie (z użyciem visual_director, czyli kolejnego agenta, którego utworzysz):
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
...
Punktem wejścia umiejętności agenta jest plik SKILL.md, który zawiera sekcję z metadanymi YAML, taką jak pokazana poniżej:
---
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...
Etykietą jest frontmatter. To wszystko, co agent widzi na początku, dlatego opis musi zawierać informację, kiedy warto wczytać umiejętność. Treść to zawartość, która pozostaje poza kontekstem, dopóki agent nie wywoła funkcji load_skill.
Ten podział jest kluczowy. Prompt pozostaje krótki, agent pobiera reguły tylko wtedy, gdy ich potrzebuje, a zmiana stylu oznacza edytowanie jednego pliku zamiast przeszukiwania ciągu instrukcji.
Zaczniemy tworzyć drugiego agenta, visual-director, wykorzystując umiejętności agenta zgodnie z diagramem poniżej:

Wspieranie dyrektora wizualnego
Utworzysz wyspecjalizowanego agenta do obsługi komponentu wizualnego visual-director. Jest to osobny projekt, ponieważ później staje się odrębną usługą, która pokazuje, jak wielu agentów komunikuje się ze sobą za pomocą protokołu A2A. Obok siebie zobaczysz 2 projekty:
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
- Utwórz projekt i zainstaluj jego zależności, wklejając ten kod w terminalu:
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
Tworzenie wskazówek dotyczących marki
- Utwórz folder umiejętności i otwórz plik:
mkdir -p visual_director/skills/brand-guidelines cloudshell edit visual_director/skills/brand-guidelines/SKILL.md - Wklej do otwartego pliku
SKILL.mdw edytorze tę zawartość i zapisz go:
--- 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.
Zaktualizuj visual_director/agent.py
Zaktualizuj plik agent.py i skonfiguruj go tak, aby zawierał wcześniej zdefiniowaną umiejętność.
- Otwórz plik agenta:
cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py - Zastąp całą zawartość pliku
visual_director/agent.pytym kodem: Agentimport 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-directorjest teraz prawidłowo skonfigurowany z umiejętnością agenta. Zanim przetestujesz tego agenta, wyposażysz go w narzędzie do generowania komponentów z obrazem jako jedną z jego kluczowych funkcji.
5. Wyposażanie agenta w narzędzie do generowania obrazów
W tym zadaniu udostępnisz Reżyserowi wizualnemu narzędzie, które renderuje kierunek artystyczny jako obraz i zapisuje go jako artefakt. Artefakt to nazwany, wersjonowany fragment danych binarnych lub zawartości pliku, który agent AI tworzy, modyfikuje lub wykorzystuje podczas wykonywania zadania.

Wdrażanie i dodawanie narzędzia do agenta
- Otwórz plik agenta:
cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py - Skopiuj to narzędzie
generate_key_visualprzed definicją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, } - Dodaj
generate_key_visualponiżejSkillToolset, aby wyposażyć agenta w narzędzie:tools=[ SkillToolset(skills=[BRAND_SKILL]), ## Supply the `generate_key_visual` tool below this comment generate_key_visual, ], agents-clizawiera wbudowany interfejs programistyczny do tworzenia aplikacji internetowych o nazwieplayground, który umożliwia inspekcję podobną do ADK. Aby uruchomić środowisko testowe:agents-cli playground --port 8080- Kliknij Podgląd w przeglądarce (ikonę oka w prawym górnym rogu terminala) i wybierz Podejrzyj na porcie 8080.

- Aby porozmawiać z pracownikiem obsługi klienta, zobaczysz ten interfejs. Spróbuj wysłać prompt „Nine lives. Zero gravity”.
Z interakcji możesz sprawdzić, jak visual_directoragent wykonujeload_skill("brand-guidelines"), aby odczytać umiejętność wytycznych dotyczących marki, a następnie wykonujegenerate_key_visual, aby rozpocząć generowanie obrazu. Wynikowy obraz jest przechowywany jako artefakt, który jest też wyświetlany w interfejsie i można go łatwo sprawdzić. - Wróć do terminala i zatrzymaj działający proces
agents-cli playground, naciskając dwukrotnie Ctrl+C.
6. Architektura systemu wieloagentowego z protokołem A2A
W tym zadaniu udostępnisz narzędzie Visual Director jako usługę, z której mogą korzystać inne agenty, i skonfigurujesz je tak, aby odpowiadało wygenerowanym obrazem.
Agent2Agent (A2A) to otwarty protokół umożliwiający jednemu agentowi wywoływanie innego w sieci. Centralnym elementem jest karta agenta, czyli dokument JSON, który agent publikuje o sobie pod znanym adresem URL, podając swoją nazwę, opis i możliwości. Dzwoniący pobiera kartę i wie, jak rozmawiać z agentem, który za nią stoi, bez wspólnego kodu źródłowego, zależności ani języka.

agents-cli domyślnie już to skonfigurował. Wygenerowany szkielet visual_director/app_utils/a2a.py, który tworzy kartę na podstawie agenta i montuje trasy, oraz visual_director/fast_api_app.py, który je obsługuje. Wystarczy niewielka modyfikacja, aby umożliwić mu również wysyłanie artefaktów z powrotem do klienta.
Architektura będzie wyglądać tak, jak pokazano poniżej. Pamiętaj, że agent nie jest jeszcze zintegrowany z naszym początkowym przepływem pracy:

szkielet serwera A2A agents-cli,
Podczas inicjowania agenta visual-director za pomocą agents-cli podany niżej kod i moduł są już dostępne. Otwórz te pliki, wklejając w terminalu te polecenia:
cloudshell edit visual_director/fast_api_app.py
cloudshell edit visual_director/app_utils/a2a.py
visual_director/fast_api_app.py uruchamia agenta i montuje trasy A2A w ścieżce utworzonej na podstawie nazwy aplikacji:
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 wykona resztę w 3 ruchach.
Najpierw tworzy kartę agenta na podstawie samego agenta. Nazwa, opis i każde narzędzie stają się wpisami, które rozmówca może odczytać:
agent_card = await AgentCardBuilder(
agent=agent,
rpc_url=f"{resolved_app_url}{rpc_path}",
).build()
Po drugie, umieszcza wykonawcę między protokołem a agentem. Przychodzące żądania A2A są tłumaczone na uruchomienia ADK, a wyniki są tłumaczone z powrotem:
request_handler = DefaultRequestHandler(
agent_executor=A2aAgentExecutor(runner=runner, force_new_version=True),
task_store=task_store,
agent_card=agent_card,
)
Po trzecie, montuje 2 trasy: jedną do wyświetlania karty, a drugą do odbierania połączeń.
Trasa | Cel |
| kartę, Dzwoniący czyta to jako pierwsze, aby dowiedzieć się, co może zrobić agent. |
| Punkt końcowy RPC JSON. Gdzie trafiają rzeczywiste żądania |
Zezwalanie na przekraczanie granicy przez artefakty
Domyślnie odpowiedź A2A zawiera tekst. Obraz jest artefaktem, a artefakt pozostaje w pamięci agenta obsługującego, chyba że skonfigurujesz jego uwzględnianie.
- Otwórz wygenerowany plik A2A:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director cloudshell edit visual_director/app_utils/a2a.py - Znajdź import
AgentCardBuilderi dodaj pod nim importyA2aAgentExecutorConfigiinclude_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, ) - Znajdź wiersz
agent_executor=A2aAgentExecutor(runner=runner),i zastąp go tym wierszem (dodającA2aAgentExecutorConfig):agent_executor=A2aAgentExecutor( runner=runner, force_new_version=True, config=A2aAgentExecutorConfig( execute_interceptors=[include_artifacts_in_a2a_event_interceptor] ), ),
Uruchom usługę i odczytaj opublikowaną kartę A2A
- Uruchom serwer w tle. Musisz zadeklarować
APP_URL, ponieważ jest to adres, który jest reklamowany na karcie jako jej własny. Wklej to w terminalu:APP_URL=http://localhost:8801 nohup uv run uvicorn visual_director.fast_api_app:app --port 8801 > /tmp/visual-director.log 2>&1 & - Zaczekaj kilka sekund, aż zacznie działać, a potem sprawdź, czy działa:
Powinno pojawić się:sleep 10 tail -2 /tmp/visual-director.logINFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8801 (Press CTRL+C to quit) - Odczytaj kartę, którą agent właśnie opublikował:
Karta zawiera nazwę, opis, adres URL i umiejętności agenta, w tymcurl -s http://localhost:8801/a2a/visual_director/.well-known/agent-card.json | python3 -m json.toolgenerate_key_visual. Jest on generowany bezpośrednio z definicji agenta w kodzie. Możesz teraz osadzić agenta{ "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-directorw głównym przepływie pracypitch-generator.
7. Osadź agenta zdalnego A2A w przepływie pracy
W tym zadaniu dodasz agenta zdalnego do wykresu jako trzecią gałąź.
Agent zdalny jest umieszczony w nawiasach RemoteA2aAgent i umieszczony na wykresie jak każdy inny węzeł. Krawędzie zachowują ten sam kształt, a koordynator nie wie, które z jego węzłów są lokalne.
Skieruj koordynatora na usługę i zaktualizuj przepływ pracy wykresu.
- Otwórz plik agenta:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator cloudshell edit pitch_generator/agent.py - Dodaj te instrukcje importu na początku pliku obok istniejących:
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 - Dodaj tę definicję
RemoteA2aAgentnad deklaracją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, ) - Zastąp funkcję Pythona węzła
packagetą definicją: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) - Dodaj bramkę zatwierdzania przez człowieka, zanim nastąpi dalsze generowanie. W sekcji
pitch_generator/agent.pyzdefiniuj węzły zatwierdzania powyżejroot_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") - Na koniec zaktualizuj importy u góry, aby uwzględnić
RequestInputinodezgoogle.adk.eventsigoogle.adk.workflow: i zmodyfikujfrom google.adk.events import Event, RequestInput from google.adk.workflow import JoinNode, Workflow, noderoot_agentprzepływ pracyedges, aby kierować ruch przezuser_approval: Warto zwrócić uwagę na kilka szczegółów:edges=[ ("START", creative_director), (creative_director, user_approval, (copywriter, visual_director)), ((creative_director, copywriter, visual_director), assemble), (assemble, package), ],- Zatwierdzenie w ramach procesu z udziałem człowieka:
approve_conceptdajeRequestInput. Gdy przepływ pracy dotrze do tego węzła, wykonanie zostanie wstrzymane i do rozmówcy zostanie wysłane przerwanie. Gałęzie podrzędne (copywriterivisual_director) nie będą działać, dopóki użytkownik nie potwierdzi zatwierdzenia. visual_directorpojawia się na krawędziach dokładnie tak samo jakcopywriter, chociaż jedno jest wywołaniem funkcji, a drugie jest przesyłane przez sieć.- Zwrócony obraz jest odczytywany ze zdarzeń sesji, a nie z
node_input, ponieważ obecnie tylko tekst jest promowany do danych wyjściowych węzła. Obraz przekroczył granicę za pomocą interfejsu A2A, ale dotarł do zdarzeń agenta zdalnego, więc funkcja pomocnicza_key_visualpobiera go stamtąd.
- Zatwierdzenie w ramach procesu z udziałem człowieka:
Testowanie integracji
Teraz sprawdzisz, czy Reżyser wizualny (działający w tle) i Generator propozycji mogą się komunikować za pomocą protokołu A2A.
- Sprawdź, czy Visual Director nadal działa w tle:
Powinien się wyświetlić wynikcurl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.jsonvisual director: 200.Jeśli pojawi się inny stan, uruchom ponownie usługę za pomocą tego polecenia: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 - Rozpocznij podgląd:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator/pitch_generator agents-cli playground --port 8080 - Kliknij Podgląd w przeglądarce (ikonę oka w prawym górnym rogu terminala) i wybierz Podejrzyj na porcie 8080.

- Zobaczysz interfejs do czatowania z agentem. Spróbuj wysłać prompt „Latające deskorolki dla kotów”.

Zobaczysz pełną interakcję, w której uruchamia się przepływ pracy i działa każdy agent. Gdy creative_directorprzygotuje koncepcję, przepływ pracy zatrzyma się na etapie zatwierdzania i poprosi Cię o potwierdzenie: „Zatwierdź koncepcję kampanii (tak/nie)”. Aby kontynuować, w polu do wprowadzania danych wpisz „yes”.Po zatwierdzeniu wykres się rozszerzy: zobaczysz, kiedy agentvisual-directorzaczyna wczytywać umiejętność, a w trakcie generowania obrazów agentcopywriterkończy generowanie podpisu do mediów społecznościowych – co pokazuje, że oba agenty działają równolegle, nie czekając na siebie. Po zakończeniu obu procesów węzełassemblełączy wyniki z zatwierdzoną koncepcją wygenerowaną przez węzełcreative_director, finalizując dostarczenie treści prezentacji w funkcjipackage.
8. Przechowywanie kluczowych elementów wizualnych w Cloud Storage
Do tej pory każdy kluczowy element wizualny był przechowywany w pamięci. W momencie zatrzymania procesu obraz znika, co jest w porządku w przypadku placu zabaw, ale bezużyteczne w kampanii. W tym zadaniu przypiszesz przepływowi pracy zasobnik, w którym będzie zapisywać dane, i utworzysz zbiór danych BigQuery, którego użyjesz w ostatnim zadaniu.
ADK decyduje, gdzie mają się znajdować artefakty podczas uruchamiania. Wygenerowany kod app_utils/services.py odczytuje jedną zmienną środowiskową:
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()
Jeśli ustawisz LOGS_BUCKET_NAME, każde połączenie save_artifact będzie trafiać do Cloud Storage.

Tworzenie zbioru danych BigQuery
- Aby utworzyć zbiór danych BigQuery, uruchom w Cloud Shell to polecenie:
bq mk --dataset --location=<YOUR_REGION> --project_id=<YOUR_PROJECT_ID> bwg - Skieruj magazyn artefaktów koordynatora na zasobnik:
export LOGS_BUCKET_NAME="<YOUR_PROJECT_ID>-bwg"GcsArtifactServiceprzyjmuje samą nazwę zasobnika bez prefiksugs://. Podobnie jak inne zmienne, ta zmienna znajduje się w powłoce, a zadanie wdrażania przekazuje ją do Cloud Run. - sprawdzić zawartość zasobnika, W nowej karcie przeglądarki otwórz ten adres URL:
Kliknij zasobnik. W zależności od poprzednich testów może zawierać wygenerowane wcześniej obrazy.https://console.cloud.google.com/storage/browser?project=<YOUR_PROJECT_ID>
9. Wdrażanie agenta Visual Director w Cloud Run
Wszystkie dotychczasowe działania były wykonywane na jednym komputerze, a Dyrektor wizualny był dostępny pod adresem localhost:8801. W tym zadaniu wdrożysz agenta Visual Director jako osobną usługę Cloud Run i umożliwisz generatorowi prezentacji komunikację z nim w sieci za pomocą protokołu Agent2Agent (A2A).
Reżyser wizualny otrzymuje prawdziwy adres URL https://, więc napisana wcześniej funkcja _cloud_run_client zaczyna podpisywać żądania zamiast zwracać wartość None. Cloud Run domyślnie wdraża usługi prywatne.

Wdrażanie narzędzia Visual Director
- Zatrzymaj lokalną usługę w tle i plac zabaw, jeśli nadal działają:
fuser -k 8801/tcp 2>/dev/null || true fuser -k 8000/tcp 2>/dev/null || true - Wdróż agenta. Spowoduje to utworzenie kontenera i jego przesłanie.⏰ Potrwa to kilka minut.
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"
Uruchamianie generatora propozycji, który korzysta z usługi Visual Director wdrożonej w Cloud Run
- Odczytaj adres URL reżysera wizualnego i uruchom generator prezentacji, który go używa:
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 &
Wywoływanie wdrożonego przepływu pracy
Oba agenty obsługują protokół A2A. W repozytorium znajduje się mały klient do wywoływania generatora propozycji za pomocą A2A.
Musi mieć ustawioną wartość PITCH_GENERATOR_URL na adres URL generatora propozycji, który jest uruchomiony lokalnie. Dyrektor wizualny działa w Cloud Run, a Generator propozycji komunikuje się z nim za pomocą protokołu A2A.
- Skopiuj klienta do projektu i uruchom go:
Ponieważ przepływ pracy zawiera bramkę zatwierdzania przez człowieka, skrypt wstrzymuje się po wygenerowaniu wersji roboczej: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 Wpisz=== HUMAN APPROVAL REQUIRED === [DRAFT CONCEPT] Concept Line: "Gravity is a Suggestion." ... Please approve the campaign concept (yes/no):yesi naciśnij Enter, aby zatwierdzić i kontynuować proces. (Możesz też przekazać wartość-ylub--auto-approve, aby automatycznie zatwierdzić prośbę). - Otwórz plik
call_agent.pyi sprawdź, jak wchodzi on w interakcje z agentem za pomocąa2a-sdk:ClientFactorytworzy oficjalneClientkierowaniehttps://SERVICE_URL/a2a/pitch_generatorA2A.generate_pitch_asyncwysyła początkowySendMessageRequest. Gdy odpowiedź zostanie zwrócona z wartościąTaskState.TASK_STATE_INPUT_REQUIRED, wykryje ona częśćadk_request_input, wyświetli wersję roboczą, zbierze Twoją zgodę i wyśle z Twoją odpowiedzią prośbę o podsumowanie z powrotem do zadania.- Pętla analizująca odczytuje sekcje
CONCEPT,COPYiART DIRECTIONz artefaktów ukończonego zadania i zapisuje obraz binarny wkey_visual.png.
key_visual.png. - Prześlij obraz kluczowy do zasobnika pod prefiksem, na który możesz wskazać BigQuery:
gcloud storage cp ./key_visual.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png - Wygeneruj drugą propozycję, aby mieć coś do porównania, i też ją prześlij:
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. Ocena kluczowych elementów wizualnych pod kątem dopasowania do marki w BigQuery
Masz już obrazy w zasobniku i wskazówki dotyczące marki, które miały na nie wpłynąć. W tym zadaniu połączysz te 2 elementy: odwołasz się do obrazów z BigQuery, a potem poprosisz model Gemini o ocenę każdego z nich na podstawie wytycznych bez przenoszenia ani jednego bajta z Cloud Storage.
Odpowiadają za to 2 funkcje BigQuery:
ObjectRefto wartość wskazująca obiekt Cloud Storage.OBJ.MAKE_REFtworzy go na podstawie identyfikatora URI i połączenia, które przyznaje BigQuery przekazywany dostęp do zasobnika.pitch-connectionutworzone przez skrypt konfiguracyjny to połączenie, które ma już uprawnienia Przeglądający obiekty w Cloud Storage i Użytkownik platformy Agent Platform.AI.SCOREprzyjmuje prompta i zwracaFLOAT64. Prompt może łączyć ciągi znaków z wartościamiObjectRef, dzięki czemu model widzi rubrykę i obraz razem.

Tworzenie tabeli z najważniejszymi elementami wizualnymi
- Otwórz BigQuery Studio w nowej karcie przeglądarki:
https://console.cloud.google.com/bigquery?project=<YOUR_PROJECT_ID> - Kliknij Utwórz nowe zapytanie (ikonę +), a następnie wklej i uruchom poniższe zapytanie (zastępując symbole
iidentyfikatorem projektu i regionem). Tworzy ona 1 wiersz na każdy kluczowy element wizualny, łącząc koncepcję kampanii z odniesieniem do obrazu: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_REFtworzy odniesienie na podstawie identyfikatora URI i połączenia.OBJ.FETCH_METADATAnastępnie kontaktuje się z Cloud Storage i uzupełnia rozmiar, typ zawartości i generację obiektu. Drugie wywołanie jest też najszybszym sposobem na sprawdzenie, czy połączenie działa: jeśli uprawnienia są nieprawidłowe, poledetailszwraca błąd zamiast metadanych. - Sprawdź, co zostało zapisane. Zastąp poprzednie zapytanie tym i uruchom je:
Tabela zawiera wskaźnik i jego metadane, a nie obraz. Bajty nigdy nie opuściły zasobnika.SELECT campaign, concept, key_visual.uri, key_visual.details FROM bwg.key_visuals;
Ocena każdego obrazu pod kątem zgodności ze wskazówkami dotyczącymi marki
Dyrektor wizualny otrzymał polecenie, aby przestrzegać palety kolorów, reguły oświetlenia i reguły kompozycji. To, czy tak się stało, zależy od oceny, i właśnie do tego służy AI.SCORE.
- Uruchom to zapytanie (zastępując
nazwą swojego regionu). Podpowiedź to krotka: kryteria oceny, koncepcja kampanii iObjectRefdla obrazu: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; - Przekształć wynik w werdykt, na podstawie którego Twój zespół może podjąć działania:
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.SCOREzwracaFLOAT64i nic więcej, więc próg znajduje się w zwykłym SQL-u, w którym możesz go dostosować bez zmiany promptu. Ocena jest przeprowadzana wiersz po wierszu, co oznacza, że te same kryteria oceny są stosowane do każdego zasobu wygenerowanego przez kampanię, a nie tylko do tych dwóch, które zostały wygenerowane tutaj.
11. Dalsze kroki
Tworzenie ocen
Oceny agentów to zautomatyzowane testy służące do pomiaru tego, jak dobrze autonomiczne agenty AI rozumują, planują, korzystają z narzędzi i wykonują wieloetapowe przepływy pracy. Są one podobne do testów jednostkowych agentów, ale uwzględniają ich niedeterministyczny charakter.
Jeden z naszych agentów zawiera Human in the loop, co oznacza, że czeka na dane wejściowe. Musisz zezwolić firmie Antigravity na wprowadzenie zmian w obecnym kodzie, aby to zachowanie nie przeszkadzało w automatycznych ocenach.
Jednym z najlepszych sposobów na rozpoczęcie jest zadanie pytania interfejsowi Antigravity CLI.
Aby uruchomić interfejs CLI, wpisz w Cloud Shell to polecenie:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/
agy
Oto prompt na początek:
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.
Przykłady ocen i instrukcje ich przeprowadzania znajdziesz też w sklonowanym repozytorium.
12. Czyszczenie danych
Aby uniknąć obciążenia konta Google Cloud opłatami za zasoby użyte w tym laboratorium, usuń projekt (zalecane) lub poszczególne zasoby utworzone w ramach laboratorium:
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. Gratulacje!
W tym ćwiczeniu utworzyliśmy generator propozycji kampanii z wieloma agentami za pomocą pakietu Agent Development Kit. Połączono 3 agenty z deterministycznym przepływem pracy w formie wykresu, spakowano wytyczne dotyczące marki jako umiejętność agenta, która jest wczytywana na żądanie, i udostępniono agentowi narzędzie do generowania obrazów, które zapisuje wyniki w postaci artefaktu. Następnie podzielono usługę Visual Director na osobną usługę A2A, osadzono ją z powrotem w grafie jako węzeł zdalny, wdrożono w Cloud Run i wywołano cały przepływ pracy za pomocą A2A z koordynatora działającego lokalnie. Na koniec odwołałeś się do wygenerowanych w BigQuery kluczowych elementów wizualnych za pomocą funkcji ObjectRef i użyłeś funkcji AI.SCORE, aby ocenić je pod kątem własnych wytycznych dotyczących marki.