۱. مقدمه

یاد بگیرید که چگونه یک موتور چندعاملی چندوجهی بسازید که یک خلاصه موضوع خام را دریافت کرده و ایجاد داراییهای اجتماعی بسیار جذاب را هماهنگ میکند. با استفاده از کیت توسعه عامل گوگل (ADK) و پروتکل Agent2Agent (A2A)، شما میکروعاملهای مستقل را ایجاد خواهید کرد، قوانین برند را به عنوان یک مهارت عامل قابل استفاده مجدد بستهبندی میکنید، یک سرویس عامل را در Cloud Run مستقر میکنید و تصاویر کلیدی تولید شده را برای انطباق برند با توابع هوش مصنوعی BigQuery امتیازدهی میکنید.
کاری که انجام خواهید داد
در این آزمایشگاه کد، شما یاد میگیرید که چگونه کارهای زیر را انجام دهید:
- چندین عامل ADK را با یک گردش کار گراف قطعی هماهنگ کنید.
- دستورالعملهای برند را به عنوان یک مهارت نماینده که فقط در صورت نیاز بارگذاری میشود، بستهبندی کنید.
- یک عامل را به یک ابزار تولید تصویر مجهز کنید که خروجی آن را به عنوان یک مصنوع ذخیره کند.
- یک عامل را به عنوان یک سرویس A2A در معرض نمایش قرار دهید و آن را از یک عامل دیگر به عنوان یک گره از راه دور فراخوانی کنید.
- Visual Director را روی Cloud Run مستقر کنید و گردش کار را از طریق A2A فراخوانی کنید.
- با استفاده از توابع هوش مصنوعی، به تصاویر کلیدی تولید شده برای تناسب با برند در BigQuery امتیاز دهید.
آنچه نیاز دارید
- یک مرورگر وب (مانند کروم).
- یک حساب Google Cloud و یک پروژه Google Cloud با قابلیت پرداخت.
۲. قبل از شروع
ایجاد یک پروژه ابری گوگل
- در کنسول گوگل کلود ، در صفحه انتخاب پروژه، یک پروژه گوگل کلود را انتخاب یا ایجاد کنید.
- مطمئن شوید که صورتحساب برای پروژه ابری شما فعال است. یاد بگیرید که چگونه بررسی کنید که آیا صورتحساب در یک پروژه فعال است یا خیر .
شروع پوسته ابری
شما از Google Cloud Shell برای دانلود کد، اجرای اسکریپتهای راهاندازی و استقرار برنامه استفاده خواهید کرد.
- Google Cloud Shell را در مرورگر خود باز کنید (یا روی فعال کردن Cloud Shell در بالای کنسول Google Cloud کلیک کنید):
در صورت مشاهدهی اخطارهای بیشتر ، روی ادامه کلیک کنید. صبر کنید تا ترمینال و ویرایشگر کاملاً بارگذاری شوند. این ممکن است یک یا دو دقیقه طول بکشد.https://shell.cloud.google.com/ - شناسه و منطقه پروژه گوگل کلود خود (برای مثال،
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] - 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
مخزن را کلون کنید
ما یک الگوی کد برای شروع کار با codelab داریم. این الگو نسبتاً کوچک است، اما مخزن 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 خود باز میگردید.

ما فقط به پوشه codelabs/bwg-multi-agent-pitch-generator در مخزن نیاز داریم. بیایید از Antigravity بخواهیم که فقط آن پوشه را دانلود کند و آن را به صورت محلی در ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/ داشته باشد.
به Antigravity CLI این وظیفه را بدهید:
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 وظیفه را اجرا خواهد کرد. از شما مجوزهایی برای اجرای چند دستور خواهد پرسید. پس از دانلود کد، با اجرای دستور /exit از Antigravity CLI خارج شوید:
/exit

فضای کاری مخزن را باز کنید
حالا، بیایید فضای کاری کد را در Cloud Shell باز کنیم. این دستور را در ترمینال Cloud Shell اجرا کنید:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/
cloudshell workspace .
اسکریپت راهاندازی را اجرا کنید
برای روانتر کردن این تجربه، اسکریپت راهاندازی را از مخزن اجرا کنید. این اسکریپت متغیرهای محیطی را پیکربندی میکند، agents-cli نصب میکند، فضای ذخیرهسازی ابری را برای مصنوعات ایجاد میکند، اتصال BigQuery Cloud Resource را ایجاد میکند و مجوزهای IAM مورد نیاز را با قابلیت تلاش مجدد خودکار به حساب سرویس آن اعطا میکند.
اسکریپت راهاندازی همچنین setenv.sh را فراخوانی میکند، یک کمککننده کوچک که متغیرهای مورد نیاز هر دو عامل - GOOGLE_CLOUD_PROJECT ، GOOGLE_CLOUD_REGION ، GOOGLE_CLOUD_LOCATION و GOOGLE_GENAI_USE_VERTEXAI - را از PROJECT_ID و REGION که در بالا صادر کردید، استخراج میکند. همچنین آن دو مقدار را در یک فایل .env در دایرکتوری خانگی شما ذخیره میکند تا بتوانید بعداً آنها را بازیابی کنید.
- در Cloud Shell، اسکریپت راهاندازی را قابل اجرا کنید و آن را اجرا کنید:
chmod +x codelab-setup.sh setenv.sh ./codelab-setup.sh - اجرای این دستور حدود یک دقیقه طول میکشد. باید این پیام را ببینید که نشان دهنده موفقیت است: "🎉 🦄 اجرای اسکریپت کامل شد."

- اسکریپت راهاندازی در یک پوسته جداگانه اجرا شد، بنابراین متغیرهای آن به ترمینال شما نرسیدند. آنها را در جلسه خود بارگذاری کنید:
شما باید بازتاب پروژه، منطقه و موقعیت مکانی خود را ببینید.source ./setenv.sh
۳. هماهنگسازی عاملها با یک گردش کار گراف
در این کار، شما پروژه تولیدکنندهی گام (pitch generator) را ایجاد میکنید و یک گردش کار گراف میسازید که دو عامل را اجرا کرده و خروجی آنها را ترکیب میکند.
یک ارائه به یک مفهوم نیاز دارد، یعنی ایده و داراییهایی که آن مفهوم را میفروشند. در این مورد کاربردی، ما عواملی را طراحی خواهیم کرد که مسئولیتهای متفاوتی در محدوده کار دارند و نشان میدهیم که چگونه میتوانید وظایف پیچیده را از هم جدا کرده و آنها را به عوامل مختلف با مسئولیتهای متمایز واگذار کنید و مدیریت آنها را آسانتر کنید.
تقسیم این نوع کار بین متخصصان، نیازمند چیزی برای هماهنگی در زمان اجرای هر عامل است. میتوانید به عامل خود اجازه دهید تصمیم بگیرد چه زمانی هر زیرعامل را فراخوانی کند یا یک رویکرد قطعیتر را اعمال کنید. در این آزمایشگاه کد، از یک رویکرد قطعی با یک گردش کار گراف استفاده خواهید کرد.
اولین پروژه عامل را داربست بندی کنید
- در 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-approveDockerfile، تستها، Terraform برای بعداً، و یک فایل.envبا الگوی.env.exampleآن. دو بخش برای این آزمایشگاه کد مهم است:-
pitch_generator/agent.pyعامل و فایلی است که شما بازنویسی میکنید. -
pitch_generator/نام بسته است که توسط--agent-directoryتنظیم شده است.
setenv.shاکسپورت کردهاید، میخواند، بنابراین در اینجا چیزی برای پیکربندی وجود ندارد. -
- نصب وابستگیها:
نکته: این داربست همچنین پروژه را برای سرویسدهی به A2A، پروتکلی که بعداً در این آزمایشگاه کد استفاده میشود، سیمکشی میکند. فعلاً آن فایلها را به حال خود رها کنید.cd pitch-generator agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
نمودار را درک کنید
ADK نسخه ۲.x از گردشهای کاری گراف پشتیبانی میکند. یک گراف از گرهها ، که واحدهای کار هستند، و لبهها ، که آنها را به هم متصل میکنند، ساخته شده است. یک گره معمولاً یک عامل است، اگرچه یک تابع پایتون ساده نیز کار میکند. لبه میگوید وقتی یک گره تمام میشود، گره بعدی اجرا میشود.
یالها به صورت لیست نوشته میشوند و ADK ترتیب آنها را محاسبه میکند:
edges=[
("START", creative_director), ## the graph begins here
(creative_director, copywriter), ## then this
]
تودرتو بودن به یک لبه معنی میدهد:
لبه | معنی |
| یک زنجیره. الف، سپس ب، سپس ج، یکی پس از دیگری |
| یک خروجی با خروجی متغیر. ابتدا a، سپس b و سپس c به طور همزمان |
| یک طرفدار. c منتظر هر دو a و b است. |
برای یک تابع fan-in، از یک JoinNode استفاده میکنید که منتظر هر تابع قبلی میماند و خروجیهای آنها را با هم ارسال میکند.
در این تمرین، گردش کار زیر را خواهیم ساخت:

ساخت خط لوله
- فایل agent را با پیست کردن این دستور در ترمینال باز کنید:
فایل scaffoldedcd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator cloudshell edit pitch_generator/agent.pyagent.pyدر ویرایشگر Cloud Shell مشاهده خواهید کرد.💡 نکته : در هر مرحله، میتوانید کد را انتخاب کرده و با استفاده از لامپ از Gemini بخواهید که آن را توضیح دهد. همچنین میتوانید از Antigravity CLI بپرسید.
- کل فایل موجود در
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", ) -
agents-cliشامل یک رابط کاربری توسعه وب داخلی به نامplaygroundبرای بازرسی است، مشابه ADK. برای شروع playground: از شما پرسیده میشود که آیا میخواهید تلهمتری را فعال کنید.agents-cli playground --port 8080nرا تایپ کنید و Enter را بزنید. - روی پیشنمایش وب ، نماد چشم در سمت راست بالای ترمینال، کلیک کنید و پیشنمایش روی پورت ۸۰۸۰ را انتخاب کنید.

- رابط کاربری زیر را برای چت با اپراتور مشاهده خواهید کرد. سعی کنید عبارت "Flying skateboards for cats" را ارسال کنید. پاسخی مطابق شکل زیر مشاهده خواهید کرد، که در آن میتوانید نمودار کامل گردش کار را نیز بررسی کنید.
بررسی کنید که آن پاسخ چگونه تولید شده است. زمین بازی هر مرحلهای را که نمودار طی کرده است، به ترتیب نشان میدهد: creative_director، سپسcopywriter، سپسassembleو در نهایتpackage. این توالی، نموداری است که شما رسم کردهاید و به این ترتیب میتوانید به سرعت نموداری را که تازه ساختهاید، تأیید کنید. - به ترمینال برگردید و با دو بار فشردن Ctrl+C، فرآیند در حال اجرای
agents-cli playgroundرا متوقف کنید.
۴. به یک نماینده مهارت بدهید
در این وظیفه، شما یک نماینده دوم ایجاد میکنید و مجموعهای از دستورالعملهای برند را به عنوان یک مهارت به آن میدهید.
خط لوله کلمات را مینویسد. یک تصویر به یک سبک خانه نیاز دارد: یک پالت، روشی برای نورپردازی چیزها، ترکیببندیهایی که استفاده میکنید و آنهایی که هرگز استفاده نمیکنید. این یک صفحه از قوانین است و یک صفحه از قوانین به مکانیزم بهتری برای مدیریت نیاز دارد. یک راه برای انجام این کار، بستهبندی آن به عنوان یک "مهارت عامل" است.
یک مهارت ، پوشهای است که حاوی فایل SKILL.md است. ساختار دایرکتوری مهارتها مانند مثال زیر است (با استفاده از 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 است که شامل یک بخش frontmatter با زبان 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 برچسب است. این تمام چیزی است که عامل از قبل میبیند، به همین دلیل است که توضیحات باید بگوید چه زمانی مهارت ارزش بارگیری دارد. بدنه محتوا است و تا زمانی که عامل 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
- پروژه را ایجاد کنید و وابستگیهای آن را با قرار دادن این کد در ترمینال نصب کنید:
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
دستورالعملهای برند را بنویسید
- پوشه مهارت را ایجاد کنید و فایل را باز کنید:
mkdir -p visual_director/skills/brand-guidelines cloudshell edit visual_director/skills/brand-guidelines/SKILL.md - محتوای زیر را در فایل
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 را بهروزرسانی کنید و آن را طوری پیکربندی کنید که به مهارت تعریفشدهی قبلی مجهز شود.
- فایل عامل را باز کنید:
cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py - کل محتوای فایل
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اکنون به درستی با مهارت agent پیکربندی شده است. قبل از آزمایش آن، شما این عامل را به ابزاری برای تولید image assets به عنوان یکی از قابلیتهای کلیدی آن مجهز خواهید کرد.
۵. یک اپراتور را به یک ابزار تولید تصویر مجهز کنید
در این وظیفه، شما به مدیر بصری ابزاری میدهید که جهت هنری را به عنوان یک تصویر رندر کرده و آن را به عنوان یک مصنوع ذخیره میکند. مصنوع ، قطعهای نامگذاری شده و نسخهبندی شده از دادههای دودویی یا محتوای فایل است که یک عامل هوش مصنوعی در طول یک وظیفه ایجاد، اصلاح یا استفاده میکند.

ابزار را پیادهسازی و به عامل اضافه کنید
- فایل عامل را باز کنید:
cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py - ابزار
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, } - برای تجهیز عامل به این ابزار
generate_key_visualدر زیرSkillToolsetاضافه کنید:tools=[ SkillToolset(skills=[BRAND_SKILL]), ## Supply the `generate_key_visual` tool below this comment generate_key_visual, ], -
agents-cliشامل یک رابط کاربری توسعه وب داخلی به نامplaygroundبرای بازرسی است، مشابه ADK. برای شروع playground:agents-cli playground --port 8080 - روی پیشنمایش وب ، نماد چشم در سمت راست بالای ترمینال، کلیک کنید و پیشنمایش روی پورت ۸۰۸۰ را انتخاب کنید.

- برای چت با مامور، رابط کاربری زیر را مشاهده خواهید کرد. سعی کنید عبارت "Nine lives. Zero gravity" را ارسال کنید.
شما میتوانید از طریق تعاملات، بررسی کنید که چگونه عامل visual_directorتابعload_skill("brand-guidelines")را برای خواندن مهارت دستورالعملهای برند اجرا میکند و سپسgenerate_key_visualرا برای شروع تولید تصویر اجرا میکند. تصویر حاصل به عنوان یک مصنوع ذخیره میشود که در رابط کاربری نیز نمایش داده میشود و به راحتی قابل بررسی است. - به ترمینال برگردید و با دو بار فشردن Ctrl+C، فرآیند در حال اجرای
agents-cli playgroundرا متوقف کنید.
۶. معماری چندعاملی با پروتکل A2A
در این وظیفه، شما Visual Director را به عنوان سرویسی که سایر عاملها میتوانند آن را فراخوانی کنند، نمایش میدهید و آن را طوری پیکربندی میکنید که با تصویر تولید شده پاسخ دهد.
Agent2Agent (A2A) یک پروتکل باز برای یک عامل است تا با عامل دیگر از طریق شبکه تماس بگیرد. قطعه مرکزی، کارت عامل است، یک سند JSON که عامل از طریق یک URL شناخته شده در مورد خود منتشر میکند و نام، توضیحات و قابلیتهای خود را ارائه میدهد. تماسگیرنده کارت را دریافت میکند و میداند چگونه با عامل پشت آن صحبت کند، بدون هیچ کد منبع، وابستگیها یا زبان مشترکی.

agents-cli از قبل این را به طور پیشفرض متصل کرده است. scaffold فایل visual_director/app_utils/a2a.py را ایجاد کرد که کارت را از agent شما میسازد و مسیرها را mount میکند، و همچنین visual_director/fast_api_app.py را که به آنها سرویس میدهد، ایجاد کرد. شما فقط به یک تغییر کوچک نیاز دارید تا بتوانید مصنوعات را به کلاینت نیز ارسال کنید.
معماری به شکل زیر خواهد بود. توجه داشته باشید که عامل هنوز در گردش کار اولیه ما ادغام نشده است:

داربست سرور 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,
)
سوم، دو مسیر را برقرار میکند: یکی برای ارائه کارت و دیگری برای پذیرش تماس.
مسیر | هدف |
| کارت. تماسگیرنده ابتدا این را میخواند تا بفهمد که نماینده چه کاری میتواند انجام دهد. |
| نقطه پایانی JSON-RPC. جایی که درخواستهای واقعی به آنجا میروند |
بگذارید مصنوعات از مرز عبور کنند
به طور پیشفرض، یک پاسخ A2A حاوی متن است. تصویر یک مصنوع است و یک مصنوع در حافظه عامل ارائه دهنده باقی میماند، مگر اینکه آن را طوری پیکربندی کنید که شامل شود.
- فایل A2A تولید شده را باز کنید:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director cloudshell edit visual_director/app_utils/a2a.py - فایل ایمپورت
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, ) - عبارت
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 منتشر شده را بخوانید
- سرور را در پسزمینه اجرا کنید. باید
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 & - چند ثانیه صبر کنید تا شروع شود، سپس تأیید کنید که سرو میشود:
شما باید ببینید: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) - کارتی را که نماینده اکنون منتشر میکند بخوانید:
این کارت شامل نام، توضیحات، URL و مهارتهای عامل، از جملهcurl -s http://localhost:8801/a2a/visual_director/.well-known/agent-card.json | python3 -m json.toolgenerate_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جاسازی کنید.
۷. عامل راه دور A2A را در گردش کار جاسازی کنید
در این کار، شما عامل راه دور را به عنوان شاخه سوم به گراف اضافه خواهید کرد.
یک عامل راه دور در RemoteA2aAgent قرار میگیرد و مانند هر گره دیگری در گراف قرار میگیرد. لبهها شکل یکسانی را حفظ میکنند و هماهنگکننده نمیداند کدام یک از گرههایش محلی هستند.
هماهنگکننده را به سرویس مورد نظر ارجاع دهید و نمودار گردش کار را بهروزرسانی کنید
- فایل عامل را باز کنید:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator cloudshell edit pitch_generator/agent.py - این ایمپورتها را در بالای فایل، در کنار ایمپورتهای موجود، اضافه کنید:
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 - تعریف
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, ) - تابع پایتون node
packageرا با تعریف زیر بازنویسی یا جایگزین کنید: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) - قبل از ادامه تولید پاییندستی، یک دروازه تأیید انسانی اضافه کنید. در
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") - در نهایت، importهای بالا را بهروزرسانی کنید تا
RequestInputوnodefromgoogle.adk.eventsوgoogle.adk.workflowرا شامل شوند: وfrom google.adk.events import Event, RequestInput from google.adk.workflow import JoinNode, Workflow, nodeedgesگردش کارroot_agentرا طوری تغییر دهید که از طریقuser_approvalمسیریابی شود: چندین جزئیات شایان ذکر است:edges=[ ("START", creative_director), (creative_director, user_approval, (copywriter, visual_director)), ((creative_director, copywriter, visual_director), assemble), (assemble, package), ],- تأیید توسط انسان در حلقه (HITL) :
approve_conceptیکRequestInputتولید میکند. وقتی گردش کار به این گره میرسد، اجرا متوقف شده و یک وقفه به فراخوانیکننده ارسال میکند. شاخههای پاییندستی (copywriterوvisual_director) تا زمانی که تأیید کاربر تأیید نشود، اجرا نخواهند شد. -
visual_directorدقیقاً مانندcopywriterدر لبهها ظاهر میشود، هرچند یکی از آنها فراخوانی تابع است و دیگری در سراسر شبکه قرار دارد. - تصویر برگشتی به جای
node_inputاز رویدادهای session خوانده میشود، زیرا در حال حاضر فقط متن به خروجی گره منتقل میشود. تصویر از طریق رهگیر A2A از مرز عبور کرده است، اما به صورت متصل به رویدادهای عامل راه دور رسیده است، بنابراین تابع کمکی_key_visualآن را از آنجا بازیابی میکند.
- تأیید توسط انسان در حلقه (HITL) :
تست ادغام
اکنون آزمایش خواهید کرد که آیا Visual Director (که در پسزمینه اجرا میشود) و Pitch Generator میتوانند از طریق پروتکل A2A با هم ارتباط برقرار کنند یا خیر.
- تأیید کنید که Visual Director هنوز در پسزمینه در حال اجرا است:
شما باید نتیجهیcurl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.jsonvisual 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 - شروع پیشنمایش:
cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator/pitch_generator agents-cli playground --port 8080 - روی پیشنمایش وب ، نماد چشم در سمت راست بالای ترمینال، کلیک کنید و پیشنمایش روی پورت ۸۰۸۰ را انتخاب کنید.

- رابط کاربری چت با اپراتور را مشاهده خواهید کرد. سعی کنید عبارت "اسکیتبوردهای پرنده برای گربهها" را ارسال کنید.

شما تعامل کامل را از جایی که گردش کار شروع میشود و هر عامل اجرا میشود، مشاهده خواهید کرد. پس از اینکه creative_directorمفهوم را پیشنویس کرد، گردش کار در دروازه تأیید مکث میکند و از شما میخواهد که تأیید کنید: "لطفاً مفهوم کمپین را تأیید کنید (بله/خیر)". برای ادامه، در فیلد ورودی "بله" را وارد کنید. پس از تأیید، نمودار به صورت منحنی پخش میشود: میتوانید ببینید که عاملvisual-directorچه زمانی شروع به بارگذاری مهارت میکند و در اواسط تولید تصویر، عاملcopywriterتولید کپشن رسانههای اجتماعی را به پایان میرساند - که نشان میدهد هر دو عامل به صورت موازی و بدون انتظار برای یکدیگر اجرا میشوند. پس از پایان هر دو، گرهassembleنتایج را به همراه مفهوم تأیید شده تولید شده توسطcreative_directorبه هم متصل میکند و تحویل محتوای ارائه را در تابعpackageنهایی میکند.
۸. جلوههای بصری کلیدی را در فضای ذخیرهسازی ابری حفظ کنید
تاکنون هر تصویر کلیدی در حافظه وجود داشته است. به محض اینکه فرآیند متوقف شود، تصویر از بین میرود، که برای یک زمین بازی خوب و برای یک کمپین بیفایده است. در این وظیفه، شما به گردش کار یک سطل برای نوشتن میدهید و یک مجموعه داده 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 در فضای ذخیرهسازی ابری ذخیره میشود.

ایجاد مجموعه داده BigQuery
- در Cloud Shell، دستور زیر را برای ایجاد یک مجموعه داده BigQuery اجرا کنید:
bq mk --dataset --location=<YOUR_REGION> --project_id=<YOUR_PROJECT_ID> bwg - مخزن مصنوعات هماهنگکننده را به سمت سطل نشانه بگیرید:
export LOGS_BUCKET_NAME="<YOUR_PROJECT_ID>-bwg"GcsArtifactServiceیک نام ساده برای bucket میگیرد، بدون پیشوندgs://. مانند سایر متغیرها، این متغیر نیز در shell شما قرار دارد و وظیفه deploy آن را به Cloud Run منتقل میکند. - محتویات سطل خود را بررسی کنید. در یک برگه جدید مرورگر، URL زیر را باز کنید:
روی سطل کلیک کنید. بسته به آزمایشهای گذشته شما، ممکن است حاوی تصاویر تولید شده قبلی باشد.https://console.cloud.google.com/storage/browser?project=<YOUR_PROJECT_ID>
۹. عامل Visual Director را به Cloud Run مستقر کنید
همه چیز تا اینجا روی یک دستگاه اجرا میشد و Visual Director در localhost:8801 قابل دسترسی بود. در این کار، شما عامل Visual Director را به عنوان سرویس Cloud Run خود مستقر میکنید و به مولد pitch اجازه میدهید تا با استفاده از پروتکل Agent2Agent (A2A) در سراسر شبکه با آن ارتباط برقرار کند.
مدیر بصری یک آدرس اینترنتی واقعی https:// دریافت میکند، بنابراین _cloud_run_client که قبلاً نوشتید، به جای بازگرداندن None ، شروع به امضای درخواستها میکند. همچنین، Cloud Run به طور پیشفرض به صورت خصوصی مستقر میشود.

مدیر بصری را مستقر کنید
- اگر سرویس پسزمینه محلی و playground هنوز در حال اجرا هستند، آنها را متوقف کنید:
fuser -k 8801/tcp 2>/dev/null || true fuser -k 8000/tcp 2>/dev/null || true - عامل را مستقر کنید. این یک کانتینر میسازد و آن را ارسال میکند.⏰ این کار چند دقیقه طول میکشد.
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"
مولد پیچ (Pitch Generator) را که از Visual Director مستقر در Cloud Run استفاده میکند، اجرا کنید.
- آدرس اینترنتی Visual Director را بخوانید و Pitch Generator که از آن استفاده میکند را اجرا کنید:
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 روی آدرس اینترنتی تولیدکنندهی پیچ که به صورت محلی در حال اجرا است، تنظیم شود. در همین حال، Visual Director در Cloud Run اجرا میشود و تولیدکنندهی پیچ از طریق A2A با آن ارتباط برقرار میکند.
- کلاینت را در پروژه خود کپی کرده و آن را اجرا کنید:
از آنجا که گردش کار شامل یک دروازه تأیید انسانی است، اسکریپت پس از تولید پیشنویس مفهوم متوقف میشود: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را وارد کنید). -
call_agent.pyرا باز کنید و نحوه تعامل آن با عامل را با استفاده ازa2a-sdkبررسی کنید:-
ClientFactoryیکClientرسمی A2A میسازد که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را ببینید. -
- کلید بصری را در سطل خود بارگذاری کنید، تحت یک پیشوند میتوانید BigQuery را به آدرس زیر هدایت کنید:
gcloud storage cp ./key_visual.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png - یک طرح دوم ایجاد کنید تا چیزی برای مقایسه داشته باشید و آن را نیز آپلود کنید:
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
۱۰. به عناصر بصری کلیدی برای تناسب برند در BigQuery امتیاز دهید.
حالا شما تصاویری در یک سطل و دستورالعملهای برندی که قرار بود آنها را شکل دهند، دارید. در این وظیفه، شما این دو را کنار هم قرار میدهید: تصاویر را از BigQuery ارجاع میدهید، سپس از یک مدل Gemini میخواهید که هر یک را بر اساس دستورالعملها ارزیابی کند، بدون اینکه حتی یک بایت از فضای ذخیرهسازی ابری جابجا شود.
دو ویژگی BigQuery این کار را انجام میدهند:
-
ObjectRefمقداری است که به یک شیء Cloud Storage اشاره میکند.OBJ.MAKE_REFیکی از این موارد را از یک URI به علاوهی اتصالی که به BigQuery اجازهی دسترسی به سطل داده را میدهد، میسازد.pitch-connectionایجاد شده توسط اسکریپت راهاندازی، همان اتصال است و از قبل Storage Object Viewer و Agent Platform User را در خود جای داده است. -
AI.SCOREیک اعلان دریافت میکند و یکFLOAT64برمیگرداند. این اعلان میتواند رشتهها را با مقادیرObjectRefترکیب کند، بنابراین مدل، روبریک و تصویر شما را با هم میبیند.

یک جدول از تصاویر کلیدی بسازید
- BigQuery Studio را در یک تب جدید مرورگر باز کنید:
https://console.cloud.google.com/bigquery?project=<YOUR_PROJECT_ID> - روی ایجاد پرسوجوی جدید (آیکون + ) کلیک کنید، سپس دستور زیر را جایگذاری و اجرا کنید (جایگزین کردن)
وبا شناسه پروژه و منطقه شما). این کد برای هر تصویر کلیدی یک ردیف ایجاد میکند و مفهوم کمپین را با ارجاع به تصویر جفت میکند: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به جای فراداده، خطا را نگه میدارد. - آنچه را که ذخیره کردهاید بررسی کنید. کوئری قبلی را با این کوئری جایگزین کنید و آن را اجرا کنید :
جدول حاوی یک اشارهگر و فرادادههای آن است، نه تصویر. بایتها هرگز از سطل خارج نشدند.SELECT campaign, concept, key_visual.uri, key_visual.details FROM bwg.key_visuals;
هر تصویر را بر اساس دستورالعملهای برند ارزیابی کنید
به مدیر بصری گفته شده بود که از یک پالت رنگ، یک قانون نورپردازی و یک قانون ترکیببندی پیروی کند. اینکه آیا واقعاً این کار را انجام داده یا نه، قضاوت نهایی است و AI.SCORE برای همین کار ساخته شده است.
- کوئری زیر را اجرا کنید (جایگزین کردن
با منطقه شما). این اعلان یک چندتایی است: عنوان شما، مفهوم کمپین و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; - امتیاز را به حکمی تبدیل کنید که تیمتان بتواند بر اساس آن عمل کند:
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 ساده قرار دارد که میتوانید بدون لمس اعلان، آن را تنظیم کنید. امتیازدهی در هر سطر انجام میشود، به این معنی که همان معیار برای هر دارایی که یک کمپین تولید میکند اعمال میشود، نه فقط دو موردی که در اینجا ایجاد کردهاید.
۱۱. مراحل بعدی
ایجاد مقادیر
ارزیابیهای عاملمحور یا «evals» آزمونهای خودکاری هستند که برای سنجش میزان توانایی عاملهای هوش مصنوعی مستقل در استدلال، برنامهریزی، استفاده از ابزارها و تکمیل گردش کار چند مرحلهای استفاده میشوند. آنها مشابه آزمایش واحد برای عاملها هستند، اما ماهیت غیرقطعی آنها را در نظر میگیرند.
یکی از عاملهای ما یک 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.
همچنین میتوانید نمونهای از ارزیابیها و دستورالعملهای اجرای آنها را در مخزن کلونشده بیابید.
۱۲. تمیز کردن
برای جلوگیری از تحمیل هزینه به حساب گوگل کلود خود برای منابع استفاده شده در این آزمایشگاه کد، یا پروژه را حذف کنید (توصیه میشود) یا منابع ایجاد شده در طول آزمایشگاه کد را حذف کنید:
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
۱۳. تبریک میگویم!
در این آزمایشگاه کد، شما یک مولد ارائه کمپین چندعاملی با کیت توسعه عامل ساختید. شما سه عامل را به یک گردش کار گراف قطعی متصل کردید، دستورالعملهای برند را به عنوان یک مهارت عامل که در صورت تقاضا بارگیری میشود، بستهبندی کردید و به یک عامل یک ابزار تولید تصویر دادید که خروجی آن را به عنوان یک مصنوع ذخیره میکند. سپس Visual Director را به سرویس A2A خودش تقسیم کردید، آن را به عنوان یک گره از راه دور در گراف جاسازی کردید، آن را در Cloud Run مستقر کردید و کل گردش کار را از طریق A2A از یک هماهنگکننده که به صورت محلی اجرا میشود، فراخوانی کردید. در نهایت، تصاویر کلیدی تولید شده از BigQuery را با ObjectRef ارجاع دادید و AI.SCORE برای ارزیابی آنها در برابر دستورالعملهای برند خودتان استفاده کردید.