ساخت یک مولد ارائه عامل‌محور: تنظیم، ارزیابی و تحلیل با Google Cloud

۱. مقدمه

بنر کلاس استاد مامور

یاد بگیرید که چگونه یک موتور چندعاملی چندوجهی بسازید که یک خلاصه موضوع خام را دریافت کرده و ایجاد دارایی‌های اجتماعی بسیار جذاب را هماهنگ می‌کند. با استفاده از کیت توسعه عامل گوگل (ADK) و پروتکل Agent2Agent (A2A)، شما میکروعامل‌های مستقل را ایجاد خواهید کرد، قوانین برند را به عنوان یک مهارت عامل قابل استفاده مجدد بسته‌بندی می‌کنید، یک سرویس عامل را در Cloud Run مستقر می‌کنید و تصاویر کلیدی تولید شده را برای انطباق برند با توابع هوش مصنوعی BigQuery امتیازدهی می‌کنید.

کاری که انجام خواهید داد

در این آزمایشگاه کد، شما یاد می‌گیرید که چگونه کارهای زیر را انجام دهید:

  • چندین عامل ADK را با یک گردش کار گراف قطعی هماهنگ کنید.
  • دستورالعمل‌های برند را به عنوان یک مهارت نماینده که فقط در صورت نیاز بارگذاری می‌شود، بسته‌بندی کنید.
  • یک عامل را به یک ابزار تولید تصویر مجهز کنید که خروجی آن را به عنوان یک مصنوع ذخیره کند.
  • یک عامل را به عنوان یک سرویس A2A در معرض نمایش قرار دهید و آن را از یک عامل دیگر به عنوان یک گره از راه دور فراخوانی کنید.
  • Visual Director را روی Cloud Run مستقر کنید و گردش کار را از طریق A2A فراخوانی کنید.
  • با استفاده از توابع هوش مصنوعی، به تصاویر کلیدی تولید شده برای تناسب با برند در BigQuery امتیاز دهید.

آنچه نیاز دارید

  • یک مرورگر وب (مانند کروم).
  • یک حساب Google Cloud و یک پروژه Google Cloud با قابلیت پرداخت.

۲. قبل از شروع

ایجاد یک پروژه ابری گوگل

  1. در کنسول گوگل کلود ، در صفحه انتخاب پروژه، یک پروژه گوگل کلود را انتخاب یا ایجاد کنید.
  2. مطمئن شوید که صورتحساب برای پروژه ابری شما فعال است. یاد بگیرید که چگونه بررسی کنید که آیا صورتحساب در یک پروژه فعال است یا خیر .

شروع پوسته ابری

شما از Google Cloud Shell برای دانلود کد، اجرای اسکریپت‌های راه‌اندازی و استقرار برنامه استفاده خواهید کرد.

  1. Google Cloud Shell را در مرورگر خود باز کنید (یا روی فعال کردن Cloud Shell در بالای کنسول Google Cloud کلیک کنید):
    https://shell.cloud.google.com/
    
    در صورت مشاهده‌ی اخطارهای بیشتر ، روی ادامه کلیک کنید. صبر کنید تا ترمینال و ویرایشگر کاملاً بارگذاری شوند. این ممکن است یک یا دو دقیقه طول بکشد.
  2. شناسه و منطقه پروژه گوگل کلود خود (برای مثال، us-central1 ) را با قرار دادن این کد در ترمینال، در محیط خود پیکربندی کنید (جایگزین کنید) و با ارزش‌های شما):
    export PROJECT_ID="<YOUR_PROJECT_ID>"
    export REGION="<YOUR_REGION>"
    gcloud config set project $PROJECT_ID
    
    در ترمینال پیست کنید شما باید پیامی مشابه زیر را ببینید:
    Your active configuration is: [cloudshell-####]
    Updated property [core/project]
    
  3. API های مورد نیاز Google Cloud را برای پروژه خود فعال کنید:
    gcloud services enable \
      compute.googleapis.com \
      cloudresourcemanager.googleapis.com \
      aiplatform.googleapis.com \
      storage.googleapis.com \
      storage-component.googleapis.com \
      bigqueryconnection.googleapis.com \
      bigquery.googleapis.com \
      run.googleapis.com \
      secretmanager.googleapis.com \
      cloudbuild.googleapis.com \
      artifactregistry.googleapis.com \
      iap.googleapis.com \
      iam.googleapis.com
    

مخزن را کلون کنید

ما یک الگوی کد برای شروع کار با 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 در دایرکتوری خانگی شما ذخیره می‌کند تا بتوانید بعداً آنها را بازیابی کنید.

  1. در Cloud Shell، اسکریپت راه‌اندازی را قابل اجرا کنید و آن را اجرا کنید:
    chmod +x codelab-setup.sh setenv.sh
    ./codelab-setup.sh
    
  2. اجرای این دستور حدود یک دقیقه طول می‌کشد. باید این پیام را ببینید که نشان دهنده موفقیت است: "🎉 🦄 اجرای اسکریپت کامل شد." موفقیت
  3. اسکریپت راه‌اندازی در یک پوسته جداگانه اجرا شد، بنابراین متغیرهای آن به ترمینال شما نرسیدند. آنها را در جلسه خود بارگذاری کنید:
    source ./setenv.sh
    
    شما باید بازتاب پروژه، منطقه و موقعیت مکانی خود را ببینید.

۳. هماهنگ‌سازی عامل‌ها با یک گردش کار گراف

در این کار، شما پروژه تولیدکننده‌ی گام (pitch generator) را ایجاد می‌کنید و یک گردش کار گراف می‌سازید که دو عامل را اجرا کرده و خروجی آنها را ترکیب می‌کند.

یک ارائه به یک مفهوم نیاز دارد، یعنی ایده و دارایی‌هایی که آن مفهوم را می‌فروشند. در این مورد کاربردی، ما عواملی را طراحی خواهیم کرد که مسئولیت‌های متفاوتی در محدوده کار دارند و نشان می‌دهیم که چگونه می‌توانید وظایف پیچیده را از هم جدا کرده و آنها را به عوامل مختلف با مسئولیت‌های متمایز واگذار کنید و مدیریت آنها را آسان‌تر کنید.

تقسیم این نوع کار بین متخصصان، نیازمند چیزی برای هماهنگی در زمان اجرای هر عامل است. می‌توانید به عامل خود اجازه دهید تصمیم بگیرد چه زمانی هر زیرعامل را فراخوانی کند یا یک رویکرد قطعی‌تر را اعمال کنید. در این آزمایشگاه کد، از یک رویکرد قطعی با یک گردش کار گراف استفاده خواهید کرد.

اولین پروژه عامل را داربست بندی کنید

  1. در Cloud Shell ، با وارد کردن این کد در ترمینال ، پروژه را ایجاد کنید:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator
    agents-cli create pitch-generator \
      --agent adk \
      --agent-directory pitch_generator \
      --deployment-target cloud_run \
      --session-type in_memory \
      --cicd-runner skip \
      --region <YOUR_REGION> \
      --auto-approve
    
    این یک پروژه کامل و قابل اجرا می‌نویسد: یک عامل، یک Dockerfile ، تست‌ها، Terraform برای بعداً، و یک فایل .env با الگوی .env.example آن. دو بخش برای این آزمایشگاه کد مهم است:
    • pitch_generator/agent.py عامل و فایلی است که شما بازنویسی می‌کنید.
    • pitch_generator/ نام بسته است که توسط --agent-directory تنظیم شده است.
    عامل، پروژه و منطقه خود را از محیطی که شما با setenv.sh اکسپورت کرده‌اید، می‌خواند، بنابراین در اینجا چیزی برای پیکربندی وجود ندارد.
  2. نصب وابستگی‌ها:
    cd pitch-generator
    agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
    
    نکته: این داربست همچنین پروژه را برای سرویس‌دهی به A2A، پروتکلی که بعداً در این آزمایشگاه کد استفاده می‌شود، سیم‌کشی می‌کند. فعلاً آن فایل‌ها را به حال خود رها کنید.

نمودار را درک کنید

ADK نسخه ۲.x از گردش‌های کاری گراف پشتیبانی می‌کند. یک گراف از گره‌ها ، که واحدهای کار هستند، و لبه‌ها ، که آنها را به هم متصل می‌کنند، ساخته شده است. یک گره معمولاً یک عامل است، اگرچه یک تابع پایتون ساده نیز کار می‌کند. لبه می‌گوید وقتی یک گره تمام می‌شود، گره بعدی اجرا می‌شود.

یال‌ها به صورت لیست نوشته می‌شوند و ADK ترتیب آنها را محاسبه می‌کند:

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

تودرتو بودن به یک لبه معنی می‌دهد:

لبه

معنی

(a, b, c)

یک زنجیره. الف، سپس ب، سپس ج، یکی پس از دیگری

(a, (b, c))

یک خروجی با خروجی متغیر. ابتدا a، سپس b و سپس c به طور همزمان

((a, b), c)

یک طرفدار. c منتظر هر دو a و b است.

برای یک تابع fan-in، از یک JoinNode استفاده می‌کنید که منتظر هر تابع قبلی می‌ماند و خروجی‌های آنها را با هم ارسال می‌کند.

در این تمرین، گردش کار زیر را خواهیم ساخت:

نمودار گردش کار اولیه

ساخت خط لوله

  1. فایل agent را با پیست کردن این دستور در ترمینال باز کنید:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    cloudshell edit pitch_generator/agent.py
    
    فایل scaffolded agent.py در ویرایشگر Cloud Shell مشاهده خواهید کرد.💡 نکته : در هر مرحله، می‌توانید کد را انتخاب کرده و با استفاده از لامپ از Gemini بخواهید که آن را توضیح دهد. همچنین می‌توانید از Antigravity CLI بپرسید. از دستیار کد Gemini استفاده کنید
  2. کل فایل موجود در pitch_generator/agent.py در ویرایشگر را با کد زیر جایگزین کنید:
    import mimetypes
    import os
    
    from dotenv import load_dotenv
    from google.adk.agents import Agent
    from google.adk.agents.context import Context
    from google.adk.agents.remote_a2a_agent import (
       AGENT_CARD_WELL_KNOWN_PATH,
       RemoteA2aAgent,
    )
    from google.adk.apps import App
    from google.adk.events.event import Event
    from google.adk.models import Gemini
    from google.adk.workflow import JoinNode, Workflow
    from google.genai import types
    
    load_dotenv()
    
    MODEL = "gemini-3.8-flash"
    
    def _model() -> Gemini:
       return Gemini(
          model=MODEL,
          client_kwargs={"location": "global"},
          retry_options=types.HttpRetryOptions(attempts=3),
       )
    
    """ ⬇️ Configure visual-director RemoteA2aAgent agent below this comment """
    
    
    creative_director = Agent(
       name="creative_director",
       model=_model(),
       description="Turns a raw idea into a campaign concept.",
       instruction=(
          "You are the Creative Director. Turn the idea you are given into ONE "
          "punchy campaign concept line with the rationale explanation behind it."
       ),
       output_key="creative_director",
    )
    
    copywriter = Agent(
       name="copywriter",
       model=Gemini(model=MODEL, retry_options=types.HttpRetryOptions(attempts=3)),
       description="Writes social copy for a campaign concept.",
       instruction=(
          "You are the Copywriter. Write ONE short social caption for the "
          "campaign concept you are given. Under 25 words."
       ),
    )
    
    """ Waits for every node wired into it, then hands their outputs on together."""
    assemble = JoinNode(name="assemble")
    
    def package(node_input: dict):
       """Render the joined branches as the finished pitch.
       """
    
       pitch = "\n\n".join(
          [
                f"CONCEPT\n{node_input['creative_director']}",
                f"COPY\n{node_input['copywriter']}",
          ]
       )
       """ The first event is what the user sees. The second is the node's output,
       which any downstream node would receive."""
       yield Event(content=types.Content(role="model", parts=[types.Part(text=pitch)]))
       yield Event(output=pitch)
    
    """ we put creative_director before `assemble`  so that we can
       gather the `Concept` output at assemble"""
    root_agent = Workflow(
       name="pitch_generator",
       edges=[
          ("START", creative_director),
          (creative_director, copywriter),
          ((creative_director, copywriter), assemble),
          (assemble, package),
       ],
    )
    
    app = App(
       root_agent=root_agent,
       name="pitch_generator",
    )
    
  3. agents-cli شامل یک رابط کاربری توسعه وب داخلی به نام playground برای بازرسی است، مشابه ADK. برای شروع playground:
    agents-cli playground --port 8080
    
    از شما پرسیده می‌شود که آیا می‌خواهید تله‌متری را فعال کنید. n را تایپ کنید و Enter را بزنید.
  4. روی پیش‌نمایش وب ، نماد چشم در سمت راست بالای ترمینال، کلیک کنید و پیش‌نمایش روی پورت ۸۰۸۰ را انتخاب کنید. پیش‌نمایش وب کلیک کنید
  5. رابط کاربری زیر را برای چت با اپراتور مشاهده خواهید کرد. سعی کنید عبارت "Flying skateboards for cats" را ارسال کنید. پاسخی مطابق شکل زیر مشاهده خواهید کرد، که در آن می‌توانید نمودار کامل گردش کار را نیز بررسی کنید. گردش کار نمایشی بررسی کنید که آن پاسخ چگونه تولید شده است. زمین بازی هر مرحله‌ای را که نمودار طی کرده است، به ترتیب نشان می‌دهد: creative_director ، سپس copywriter ، سپس assemble و در نهایت package . این توالی، نموداری است که شما رسم کرده‌اید و به این ترتیب می‌توانید به سرعت نموداری را که تازه ساخته‌اید، تأیید کنید.
  6. به ترمینال برگردید و با دو بار فشردن 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
  1. پروژه را ایجاد کنید و وابستگی‌های آن را با قرار دادن این کد در ترمینال نصب کنید:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator
    agents-cli create visual-director \
      --agent adk \
      --agent-directory visual_director \
      --deployment-target cloud_run \
      --session-type in_memory \
      --cicd-runner skip \
      --region <YOUR_REGION> \
      --auto-approve
    cd visual-director
    agents-cli update -y && agents-cli install && uv sync --upgrade-package google-adk
    

دستورالعمل‌های برند را بنویسید

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

فایل visual_director/agent.py را به‌روزرسانی کنید.

فایل agent.py را به‌روزرسانی کنید و آن را طوری پیکربندی کنید که به مهارت تعریف‌شده‌ی قبلی مجهز شود.

  1. فایل عامل را باز کنید:
    cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py
    
  2. کل محتوای فایل visual_director/agent.py را با کد زیر جایگزین کنید :
    import mimetypes
    from pathlib import Path
    
    from google import genai
    from google.adk.agents import Agent
    from google.adk.apps import App
    from google.adk.models import Gemini
    from google.adk.skills import load_skill_from_dir
    from google.adk.tools import ToolContext
    from google.adk.tools.skill_toolset import SkillToolset
    from google.genai import types
    MODEL = "gemini-3.8-flash"
    
    ## Configure the `brand-guidelines` skill for the agent
    BRAND_SKILL = load_skill_from_dir(Path(__file__).parent / "skills" / "brand-guidelines")
    
    ## Add definition of `generate_key_visual` tool below this comment
    
    root_agent = Agent(
       name="visual_director",
       model=Gemini(
          model=MODEL,
          client_kwargs={"location": "global"},
          retry_options=types.HttpRetryOptions(attempts=3),
       ),
       description="Turns a campaign concept into art direction and a key visual.",
       instruction="""You are the Visual Director on a campaign pitch team.
    
       Call `load_skill` for `brand-guidelines` before you write anything. The house
       style is not optional and it is not in this prompt.
    
       You are given a campaign concept. Then, in order:
    
       1. Write art direction for ONE key visual that sells it, obeying the brand
       guidelines: subject, composition, lighting, color, mood. Three or four
       sentences.
       2. Call `generate_key_visual` with exactly that art direction.
    
       Your final reply is the art direction itself and nothing else. No preamble, no
       alternatives, no questions back. Do not mention the skill, the tool, or the
       image file — the image travels on its own.""",
       tools=[
          SkillToolset(skills=[BRAND_SKILL]),
          ## Supply the `generate_key_visual` tool below this comment
       ],
    )
    
    app = App(
       root_agent=root_agent,
       name="visual_director",
    )
    
    عامل visual-director اکنون به درستی با مهارت agent پیکربندی شده است. قبل از آزمایش آن، شما این عامل را به ابزاری برای تولید image assets به عنوان یکی از قابلیت‌های کلیدی آن مجهز خواهید کرد.

۵. یک اپراتور را به یک ابزار تولید تصویر مجهز کنید

در این وظیفه، شما به مدیر بصری ابزاری می‌دهید که جهت هنری را به عنوان یک تصویر رندر کرده و آن را به عنوان یک مصنوع ذخیره می‌کند. مصنوع ، قطعه‌ای نام‌گذاری شده و نسخه‌بندی شده از داده‌های دودویی یا محتوای فایل است که یک عامل هوش مصنوعی در طول یک وظیفه ایجاد، اصلاح یا استفاده می‌کند.

ابزار و مصنوعاتی که می‌نویسد

ابزار را پیاده‌سازی و به عامل اضافه کنید

  1. فایل عامل را باز کنید:
    cloudshell edit ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director/visual_director/agent.py
    
  2. ابزار generate_key_visual زیر را قبل از تعریف root_agent کپی کنید:
    async def generate_key_visual(art_direction: str, tool_context: ToolContext) -> dict:
       """Render the art direction as the campaign's key visual.
    
       Args:
          art_direction: The art direction to render, in full.
    
       Returns:
          The saved artifact's filename, version, size and media type.
       """
       IMAGE_MODEL = "gemini-3.1-flash-lite-image"
    
       response = await genai.Client(location="global").aio.models.generate_content(
          model=IMAGE_MODEL, contents=art_direction
       )
    
       ## The image arrives as inline bytes on one of the response parts.
       image = next(
          (
             part.inline_data
             for part in response.candidates[0].content.parts or []
             if part.inline_data
          ),
          None,
       )
       if image is None:
          raise ValueError(f"{IMAGE_MODEL} returned no image for: {art_direction[:120]}")
    
       filename = "key_visual" + (
          mimetypes.guess_extension(image.mime_type or "") or ".bin"
       )
       version = await tool_context.save_artifact(filename, types.Part(inline_data=image))
    
       ## Return the artifact filename and version
       return {
          "filename": filename,
          "version": version,
          "bytes": len(image.data),
          "mime_type": image.mime_type,
       }
    
  3. برای تجهیز عامل به این ابزار generate_key_visual در زیر SkillToolset اضافه کنید:
       tools=[
          SkillToolset(skills=[BRAND_SKILL]),
          ## Supply the `generate_key_visual` tool below this comment
          generate_key_visual,
       ],
    
  4. agents-cli شامل یک رابط کاربری توسعه وب داخلی به نام playground برای بازرسی است، مشابه ADK. برای شروع playground:
    agents-cli playground --port 8080
    
  5. روی پیش‌نمایش وب ، نماد چشم در سمت راست بالای ترمینال، کلیک کنید و پیش‌نمایش روی پورت ۸۰۸۰ را انتخاب کنید. پیش‌نمایش وب کلیک کنید
  6. برای چت با مامور، رابط کاربری زیر را مشاهده خواهید کرد. سعی کنید عبارت "Nine lives. Zero gravity" را ارسال کنید. گردش کار نمایشی شما می‌توانید از طریق تعاملات، بررسی کنید که چگونه عامل visual_director تابع load_skill("brand-guidelines") را برای خواندن مهارت دستورالعمل‌های برند اجرا می‌کند و سپس generate_key_visual را برای شروع تولید تصویر اجرا می‌کند. تصویر حاصل به عنوان یک مصنوع ذخیره می‌شود که در رابط کاربری نیز نمایش داده می‌شود و به راحتی قابل بررسی است.
  7. به ترمینال برگردید و با دو بار فشردن Ctrl+C، فرآیند در حال اجرای agents-cli playground را متوقف کنید.

۶. معماری چندعاملی با پروتکل A2A

در این وظیفه، شما Visual Director را به عنوان سرویسی که سایر عامل‌ها می‌توانند آن را فراخوانی کنند، نمایش می‌دهید و آن را طوری پیکربندی می‌کنید که با تصویر تولید شده پاسخ دهد.

Agent2Agent (A2A) یک پروتکل باز برای یک عامل است تا با عامل دیگر از طریق شبکه تماس بگیرد. قطعه مرکزی، کارت عامل است، یک سند JSON که عامل از طریق یک URL شناخته شده در مورد خود منتشر می‌کند و نام، توضیحات و قابلیت‌های خود را ارائه می‌دهد. تماس‌گیرنده کارت را دریافت می‌کند و می‌داند چگونه با عامل پشت آن صحبت کند، بدون هیچ کد منبع، وابستگی‌ها یا زبان مشترکی.

پروتکل A2A

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

معماری به شکل زیر خواهد بود. توجه داشته باشید که عامل هنوز در گردش کار اولیه ما ادغام نشده است:

راه اندازی سرور A2A

داربست سرور agents-cli A2A

کد و ماژول زیر هنگام مقداردهی اولیه عامل visual-director با استفاده از agents-cli از قبل ارائه شده‌اند. این فایل‌ها را با وارد کردن دستورات زیر در ترمینال باز کنید:

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

visual_director/fast_api_app.py عامل را شروع می‌کند و مسیرهای A2A را تحت مسیری که از نام برنامه ساخته شده است، سوار می‌کند:

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

visual_director/app_utils/a2a.py بقیه کار را در سه مرحله انجام می‌دهد.

ابتدا، کارت عامل را از خود عامل می‌سازد. نام، توضیحات و هر ابزاری به ورودی‌هایی تبدیل می‌شوند که تماس‌گیرنده می‌تواند آنها را بخواند:

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

دوم، یک مجری بین پروتکل و عامل شما قرار می‌دهد. درخواست‌های ورودی A2A به اجراهای ADK ترجمه می‌شوند و نتایج به صورت زیر ترجمه می‌شوند:

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

سوم، دو مسیر را برقرار می‌کند: یکی برای ارائه کارت و دیگری برای پذیرش تماس.

مسیر

هدف

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

کارت. تماس‌گیرنده ابتدا این را می‌خواند تا بفهمد که نماینده چه کاری می‌تواند انجام دهد.

/a2a/visual_director

نقطه پایانی JSON-RPC. جایی که درخواست‌های واقعی به آنجا می‌روند

بگذارید مصنوعات از مرز عبور کنند

به طور پیش‌فرض، یک پاسخ A2A حاوی متن است. تصویر یک مصنوع است و یک مصنوع در حافظه عامل ارائه دهنده باقی می‌ماند، مگر اینکه آن را طوری پیکربندی کنید که شامل شود.

  1. فایل A2A تولید شده را باز کنید:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director
    cloudshell edit visual_director/app_utils/a2a.py
    
  2. فایل ایمپورت AgentCardBuilder را پیدا کنید و فایل‌های A2aAgentExecutorConfig و include_artifacts_in_a2a_event_interceptor را در زیر آن اضافه کنید:
    ## ...
    ## from google.adk.a2a.utils.agent_card_builder import AgentCardBuilder
    ## Add the following two below `AgentCardBuilder` import
    from google.adk.a2a.executor.config import A2aAgentExecutorConfig
    from google.adk.a2a.executor.interceptors.include_artifacts_in_a2a_event import (
       include_artifacts_in_a2a_event_interceptor,
    )
    
  3. عبارت agent_executor=A2aAgentExecutor(runner=runner), آن را با کد زیر جایگزین کنید (با اضافه کردن A2aAgentExecutorConfig ):
             agent_executor=A2aAgentExecutor(
                runner=runner,
                force_new_version=True,
                config=A2aAgentExecutorConfig(
                   execute_interceptors=[include_artifacts_in_a2a_event_interceptor]
                ),
             ),
    

سرویس را شروع کنید و کارت A2A منتشر شده را بخوانید

  1. سرور را در پس‌زمینه اجرا کنید. باید APP_URL تعریف کنید، زیرا این آدرسی است که کارت به عنوان آدرس خود تبلیغ می‌کند. این را در ترمینال پیست کنید :
    APP_URL=http://localhost:8801 nohup uv run uvicorn visual_director.fast_api_app:app --port 8801 > /tmp/visual-director.log 2>&1 &
    
  2. چند ثانیه صبر کنید تا شروع شود، سپس تأیید کنید که سرو می‌شود:
    sleep 10
    tail -2 /tmp/visual-director.log
    
    شما باید ببینید:
    INFO:     Application startup complete.
    INFO:     Uvicorn running on http://127.0.0.1:8801 (Press CTRL+C to quit)
    
  3. کارتی را که نماینده اکنون منتشر می‌کند بخوانید:
    curl -s http://localhost:8801/a2a/visual_director/.well-known/agent-card.json | python3 -m json.tool
    
    این کارت شامل نام، توضیحات، URL و مهارت‌های عامل، از جمله generate_key_visual ، است. این کارت مستقیماً از تعریف عامل در کد تولید می‌شود.
    {
       "name": "visual_director",
       "description": "Turns a campaign concept into art direction and a key visual.",
       "skills": [
            {
                "id": "visual_director",
                "name": "model",
                "tags": [
                    "llm"
                ]
            },
            {
                "id": "visual_director-generate_key_visual",
                "name": "generate_key_visual",
                "description": "Render the art direction as the campaign's key visual.\n\nArgs:\n   art_direction: The art direction to render, in full.\n\nReturns:\n   The saved artifact's filename, version, size and media type.",
                "tags": [
                    "llm",
                    "tools"
                ]
            }
       ],
       "preferredTransport": "JSONRPC",
       "protocolVersion": "0.3",
       "url": "http://localhost:8801/a2a/visual_director"
    }
    
    شما آماده‌اید تا عامل visual-director را در گردش کار اصلی pitch-generator جاسازی کنید.

۷. عامل راه دور A2A را در گردش کار جاسازی کنید

در این کار، شما عامل راه دور را به عنوان شاخه سوم به گراف اضافه خواهید کرد.

یک عامل راه دور در RemoteA2aAgent قرار می‌گیرد و مانند هر گره دیگری در گراف قرار می‌گیرد. لبه‌ها شکل یکسانی را حفظ می‌کنند و هماهنگ‌کننده نمی‌داند کدام یک از گره‌هایش محلی هستند.

هماهنگ‌کننده را به سرویس مورد نظر ارجاع دهید و نمودار گردش کار را به‌روزرسانی کنید

  1. فایل عامل را باز کنید:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    cloudshell edit pitch_generator/agent.py
    
  2. این ایمپورت‌ها را در بالای فایل، در کنار ایمپورت‌های موجود، اضافه کنید:
    import asyncio
    
    import subprocess
    import google.auth.transport.requests
    import google.oauth2.id_token
    import httpx
    from google.adk.a2a.converters.part_converter import convert_genai_part_to_a2a_part
    
  3. تعریف RemoteA2aAgent زیر را بالای تعریف creative_director اضافه کنید:
    VISUAL_DIRECTOR_URL = os.getenv("VISUAL_DIRECTOR_URL", "http://localhost:8801")
    
    ## For deployment on Cloud Run
    def _cloud_run_client() -> httpx.AsyncClient | None:
       """An httpx client that signs each request with a Cloud Run ID token.
    
       Cloud Run deploys private, so both the agent-card fetch and the A2A calls
       need one.
       """
       if not VISUAL_DIRECTOR_URL.startswith("https://"):
          return None
    
       async def sign(request: httpx.Request) -> None:
          token = None
          try:
             token = await asyncio.to_thread(
                   google.oauth2.id_token.fetch_id_token,
                   google.auth.transport.requests.Request(),
                   VISUAL_DIRECTOR_URL,
             )
          except Exception:
             token = subprocess.check_output(
                ["gcloud", "auth", "print-identity-token", "-q"],
                stderr=subprocess.PIPE
             ).decode().strip()
          if not token:
                raise Exception("Failed to fetch ID token")
          request.headers["Authorization"] = f"Bearer {token}"
    
       return httpx.AsyncClient(event_hooks={"request": [sign]}, timeout=600)
    
    ## Keep the approval's tool traffic out of the A2A request
    def _pitch_parts_only(part: types.Part):
       """Sends the Visual Director text and media only.
    
       RemoteA2aAgent replays the session on every call, and the Visual Director
       runs downstream of the human approval, so the approval's function_response
       would travel alongside the concept text. An ADK server reads a function
       response as "resume the paused invocation" and rejects a message that also
       carries text, so drop the tool parts here.
       """
       if part.function_call or part.function_response:
          return None
       return convert_genai_part_to_a2a_part(part)
    
    visual_director = RemoteA2aAgent(
       name="visual_director",
       description="Turns a campaign concept into art direction for one key visual.",
       agent_card=(
          f"{VISUAL_DIRECTOR_URL}/a2a/visual_director{AGENT_CARD_WELL_KNOWN_PATH}"
       ),
       httpx_client=_cloud_run_client(),
       genai_part_converter=_pitch_parts_only,
    )
    
  4. تابع پایتون 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)
    
  5. قبل از ادامه تولید پایین‌دستی، یک دروازه تأیید انسانی اضافه کنید. در pitch_generator/agent.py ، گره‌های تأیید بالای root_agent را تعریف کنید:
    @node(rerun_on_resume=False)
    async def approve_concept(ctx: Context):
        yield RequestInput(
            message="Please approve the campaign concept (yes/no).",
            response_schema=str
        )
    
    @node(rerun_on_resume=True)
    async def user_approval(ctx: Context):
       user_response = await ctx.run_node(approve_concept)
       if str(user_response).lower() in ("yes", "y"):
          approved_concept = f"## Approved Concept\n\n{ctx.session.state['creative_director']}"
          yield Event(
             content=types.Content(
                role="model",
                parts=[types.Part(text=approved_concept)]
             )
          )
       else:
          raise ValueError("User rejected the concept")
    
  6. در نهایت، importهای بالا را به‌روزرسانی کنید تا RequestInput و node from google.adk.events و google.adk.workflow را شامل شوند:
    from google.adk.events import Event, RequestInput
    from google.adk.workflow import JoinNode, Workflow, node
    
    و edges گردش کار root_agent را طوری تغییر دهید که از طریق user_approval مسیریابی شود:
        edges=[
            ("START", creative_director),
            (creative_director, user_approval, (copywriter, visual_director)),
            ((creative_director, copywriter, visual_director), assemble),
            (assemble, package),
        ],
    
    چندین جزئیات شایان ذکر است:
    • تأیید توسط انسان در حلقه (HITL) : approve_concept یک RequestInput تولید می‌کند. وقتی گردش کار به این گره می‌رسد، اجرا متوقف شده و یک وقفه به فراخوانی‌کننده ارسال می‌کند. شاخه‌های پایین‌دستی ( copywriter و visual_director ) تا زمانی که تأیید کاربر تأیید نشود، اجرا نخواهند شد.
    • visual_director دقیقاً مانند copywriter در لبه‌ها ظاهر می‌شود، هرچند یکی از آنها فراخوانی تابع است و دیگری در سراسر شبکه قرار دارد.
    • تصویر برگشتی به جای node_input از رویدادهای session خوانده می‌شود، زیرا در حال حاضر فقط متن به خروجی گره منتقل می‌شود. تصویر از طریق رهگیر A2A از مرز عبور کرده است، اما به صورت متصل به رویدادهای عامل راه دور رسیده است، بنابراین تابع کمکی _key_visual آن را از آنجا بازیابی می‌کند.

تست ادغام

اکنون آزمایش خواهید کرد که آیا Visual Director (که در پس‌زمینه اجرا می‌شود) و Pitch Generator می‌توانند از طریق پروتکل A2A با هم ارتباط برقرار کنند یا خیر.

  1. تأیید کنید که Visual Director هنوز در پس‌زمینه در حال اجرا است:
    curl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.json
    
    شما باید نتیجه‌ی visual director: 200 را در اینجا دریافت کنید. در صورتی که وضعیت دیگری دریافت کردید ، دستور زیر را برای راه‌اندازی مجدد آن اجرا کنید:
    fuser -k 8801/tcp 2>/dev/null || true
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director
    APP_URL=http://localhost:8801 nohup uv run uvicorn visual_director.fast_api_app:app --port 8801 > /tmp/visual-director.log 2>&1 &
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    sleep 10
    curl -s -o /dev/null -w "visual director: %{http_code}\n" http://localhost:8801/a2a/visual_director/.well-known/agent-card.json
    
  2. شروع پیش‌نمایش:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator/pitch_generator
    agents-cli playground --port 8080
    
  3. روی پیش‌نمایش وب ، نماد چشم در سمت راست بالای ترمینال، کلیک کنید و پیش‌نمایش روی پورت ۸۰۸۰ را انتخاب کنید. پیش‌نمایش وب کلیک کنید
  4. رابط کاربری چت با اپراتور را مشاهده خواهید کرد. سعی کنید عبارت "اسکیت‌بوردهای پرنده برای گربه‌ها" را ارسال کنید. گردش کار نمایشیگردش کار نمایشی شما تعامل کامل را از جایی که گردش کار شروع می‌شود و هر عامل اجرا می‌شود، مشاهده خواهید کرد. پس از اینکه creative_director مفهوم را پیش‌نویس کرد، گردش کار در دروازه تأیید مکث می‌کند و از شما می‌خواهد که تأیید کنید: "لطفاً مفهوم کمپین را تأیید کنید (بله/خیر)". برای ادامه، در فیلد ورودی "بله" را وارد کنید. پس از تأیید، نمودار به صورت منحنی پخش می‌شود: می‌توانید ببینید که عامل visual-director چه زمانی شروع به بارگذاری مهارت می‌کند و در اواسط تولید تصویر، عامل copywriter تولید کپشن رسانه‌های اجتماعی را به پایان می‌رساند - که نشان می‌دهد هر دو عامل به صورت موازی و بدون انتظار برای یکدیگر اجرا می‌شوند. پس از پایان هر دو، گره assemble نتایج را به همراه مفهوم تأیید شده تولید شده توسط creative_director به هم متصل می‌کند و تحویل محتوای ارائه را در تابع package نهایی می‌کند. این دو سرویس، که از طریق A2A به هم متصل شده‌اند

۸. جلوه‌های بصری کلیدی را در فضای ذخیره‌سازی ابری حفظ کنید

تاکنون هر تصویر کلیدی در حافظه وجود داشته است. به محض اینکه فرآیند متوقف شود، تصویر از بین می‌رود، که برای یک زمین بازی خوب و برای یک کمپین بی‌فایده است. در این وظیفه، شما به گردش کار یک سطل برای نوشتن می‌دهید و یک مجموعه داده 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 در فضای ذخیره‌سازی ابری ذخیره می‌شود.

ادغام سرویس مصنوعات با سطل GCS

ایجاد مجموعه داده BigQuery

  1. در Cloud Shell، دستور زیر را برای ایجاد یک مجموعه داده BigQuery اجرا کنید:
    bq mk --dataset --location=<YOUR_REGION> --project_id=<YOUR_PROJECT_ID> bwg
    
  2. مخزن مصنوعات هماهنگ‌کننده را به سمت سطل نشانه بگیرید:
    export LOGS_BUCKET_NAME="<YOUR_PROJECT_ID>-bwg"
    
    GcsArtifactService یک نام ساده برای bucket می‌گیرد، بدون پیشوند gs:// . مانند سایر متغیرها، این متغیر نیز در shell شما قرار دارد و وظیفه deploy آن را به Cloud Run منتقل می‌کند.
  3. محتویات سطل خود را بررسی کنید. در یک برگه جدید مرورگر، 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 به طور پیش‌فرض به صورت خصوصی مستقر می‌شود.

هر دو عامل به عنوان سرویس‌های Cloud Run

مدیر بصری را مستقر کنید

  1. اگر سرویس پس‌زمینه محلی و playground هنوز در حال اجرا هستند، آنها را متوقف کنید:
    fuser -k 8801/tcp 2>/dev/null || true
    fuser -k 8000/tcp 2>/dev/null || true
    
  2. عامل را مستقر کنید. این یک کانتینر می‌سازد و آن را ارسال می‌کند.⏰ این کار چند دقیقه طول می‌کشد.
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/visual-director
    source .env
    agents-cli deploy \
      --service-name visual-director \
      --project <YOUR_PROJECT_ID> \
      --region <YOUR_REGION> \
      --no-confirm-project \
      --labels dev-tutorial=bwg001-devlab-1 \
      --update-env-vars "LOGS_BUCKET_NAME=$LOGS_BUCKET_NAME,GOOGLE_GENAI_USE_VERTEXAI=true,GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT,GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION,GOOGLE_CLOUD_REGION=$GOOGLE_CLOUD_REGION"
    

مولد پیچ ​​(Pitch Generator) را که از Visual Director مستقر در Cloud Run استفاده می‌کند، اجرا کنید.

  1. آدرس اینترنتی 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 با آن ارتباط برقرار می‌کند.

  1. کلاینت را در پروژه خود کپی کرده و آن را اجرا کنید:
    cd ~/devrel-demos/codelabs/bwg-multi-agent-pitch-generator/pitch-generator
    cp ../solution/pitch-generator/call_agent.py .
    PITCH_GENERATOR_URL=http://localhost:8000 uv run python call_agent.py "Flying skateboards for cats" --save-image ./key_visual.png
    
    از آنجا که گردش کار شامل یک دروازه تأیید انسانی است، اسکریپت پس از تولید پیش‌نویس مفهوم متوقف می‌شود:
    === HUMAN APPROVAL REQUIRED ===
    
    [DRAFT CONCEPT]
    Concept Line: "Gravity is a Suggestion."
    ...
    
    Please approve the campaign concept (yes/no):
    
    برای تأیید و ادامه گردش کار، yes را تایپ کرده و Enter را فشار دهید. (همچنین می‌توانید برای تأیید خودکار -y یا --auto-approve را وارد کنید).
  2. call_agent.py را باز کنید و نحوه تعامل آن با عامل را با استفاده از a2a-sdk بررسی کنید:
    • ClientFactory یک 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 را ببینید.
  3. کلید بصری را در سطل خود بارگذاری کنید، تحت یک پیشوند می‌توانید BigQuery را به آدرس زیر هدایت کنید:
    gcloud storage cp ./key_visual.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png
    
  4. یک طرح دوم ایجاد کنید تا چیزی برای مقایسه داشته باشید و آن را نیز آپلود کنید:
    PITCH_GENERATOR_URL=http://localhost:8000 uv run python call_agent.py "A commuter bike built for rainy cities" --save-image ./bike.png
    gcloud storage cp ./bike.png gs://<YOUR_PROJECT_ID>-bwg/key-visuals/bike.png
    

۱۰. به عناصر بصری کلیدی برای تناسب برند در BigQuery امتیاز دهید.

حالا شما تصاویری در یک سطل و دستورالعمل‌های برندی که قرار بود آنها را شکل دهند، دارید. در این وظیفه، شما این دو را کنار هم قرار می‌دهید: تصاویر را از BigQuery ارجاع می‌دهید، سپس از یک مدل Gemini می‌خواهید که هر یک را بر اساس دستورالعمل‌ها ارزیابی کند، بدون اینکه حتی یک بایت از فضای ذخیره‌سازی ابری جابجا شود.

دو ویژگی BigQuery این کار را انجام می‌دهند:

  • ObjectRef مقداری است که به یک شیء Cloud Storage اشاره می‌کند. OBJ.MAKE_REF یکی از این موارد را از یک URI به علاوه‌ی اتصالی که به BigQuery اجازه‌ی دسترسی به سطل داده را می‌دهد، می‌سازد. pitch-connection ایجاد شده توسط اسکریپت راه‌اندازی، همان اتصال است و از قبل Storage Object Viewer و Agent Platform User را در خود جای داده است.
  • AI.SCORE یک اعلان دریافت می‌کند و یک FLOAT64 برمی‌گرداند. این اعلان می‌تواند رشته‌ها را با مقادیر ObjectRef ترکیب کند، بنابراین مدل، روبریک و تصویر شما را با هم می‌بیند.

بیگ‌کوئری سطل را می‌خواند

یک جدول از تصاویر کلیدی بسازید

  1. BigQuery Studio را در یک تب جدید مرورگر باز کنید:
    https://console.cloud.google.com/bigquery?project=<YOUR_PROJECT_ID>
    
  2. روی ایجاد پرس‌وجوی جدید (آیکون + ) کلیک کنید، سپس دستور زیر را جای‌گذاری و اجرا کنید (جایگزین کردن) و با شناسه پروژه و منطقه شما). این کد برای هر تصویر کلیدی یک ردیف ایجاد می‌کند و مفهوم کمپین را با ارجاع به تصویر جفت می‌کند:
    SET @@location = '<YOUR_REGION>';
    
    CREATE OR REPLACE TABLE bwg.key_visuals AS
    SELECT
      campaign,
      concept,
      OBJ.FETCH_METADATA(
        OBJ.MAKE_REF(uri, '<YOUR_REGION>.pitch-connection')
      ) AS key_visual
    FROM UNNEST([
      STRUCT(
        'cats' AS campaign,
        'Flying skateboards for cats' AS concept,
        'gs://<YOUR_PROJECT_ID>-bwg/key-visuals/cats.png' AS uri
      ),
      STRUCT(
        'bike',
        'A commuter bike built for rainy cities',
        'gs://<YOUR_PROJECT_ID>-bwg/key-visuals/bike.png'
      )
    ]);
    
    بی کیو OBJ.MAKE_REF مرجع را از URI و اتصال ایجاد می‌کند. OBJ.FETCH_METADATA سپس با Cloud Storage تماس می‌گیرد و اندازه، نوع محتوا و تولید شیء را پر می‌کند. فراخوانی دوم همچنین سریع‌ترین راه برای اثبات کارکرد اتصال شماست: اگر مجوزها اشتباه باشند، فیلد details به جای فراداده، خطا را نگه می‌دارد.
  3. آنچه را که ذخیره کرده‌اید بررسی کنید. کوئری قبلی را با این کوئری جایگزین کنید و آن را اجرا کنید :
    SELECT campaign, concept, key_visual.uri, key_visual.details
    FROM bwg.key_visuals;
    
    جدول حاوی یک اشاره‌گر و فراداده‌های آن است، نه تصویر. بایت‌ها هرگز از سطل خارج نشدند.

هر تصویر را بر اساس دستورالعمل‌های برند ارزیابی کنید

به مدیر بصری گفته شده بود که از یک پالت رنگ، یک قانون نورپردازی و یک قانون ترکیب‌بندی پیروی کند. اینکه آیا واقعاً این کار را انجام داده یا نه، قضاوت نهایی است و AI.SCORE برای همین کار ساخته شده است.

  1. کوئری زیر را اجرا کنید (جایگزین کردن با منطقه شما). این اعلان یک چندتایی است: عنوان شما، مفهوم کمپین و ObjectRef برای تصویر:
    SELECT
      campaign,
      concept,
      AI.SCORE(
        ('''You are the brand guardian reviewing a campaign key visual.
    Score the attached image from 1 to 10 on how closely it follows these house rules:
    - Palette: deep indigo and slate, with one warm accent of amber or terracotta. Nothing neon.
    - Light: one dominant low, raking source with long shadows. No flat overhead light.
    - Composition: subject off-center with generous empty negative space on the opposite side.
    - Subject: one subject only, photographic realism, shallow depth of field.
    - Never: text, logos, or watermarks anywhere in the frame.
    A 10 obeys every rule. A 1 ignores them. The campaign concept is: ''',
          concept,
          key_visual),
        connection_id => '<YOUR_REGION>.pitch-connection',
        endpoint => 'gemini-3.5-flash'
      ) AS brand_fit
    FROM bwg.key_visuals
    ORDER BY brand_fit DESC;
    
  2. امتیاز را به حکمی تبدیل کنید که تیمتان بتواند بر اساس آن عمل کند:
    CREATE OR REPLACE TABLE bwg.brand_review AS
    SELECT
      campaign,
      concept,
      brand_fit,
      IF(brand_fit >= 7, 'on brand', 'needs another pass') AS verdict
    FROM (
      SELECT
        campaign,
        concept,
        AI.SCORE(
          ('''Score this campaign key visual from 1 to 10 on how closely it follows the house
    brand rules: deep indigo and slate palette with a single warm accent and nothing neon; one
    low raking light source with long shadows; the subject off-center with empty negative space
    opposite; a single photographic subject; and no text, logos, or watermarks in the frame.
    The campaign concept is: ''',
              concept,
              key_visual),
          connection_id => '<YOUR_REGION>.pitch-connection',
          endpoint => 'gemini-3.5-flash'
        ) AS brand_fit
      FROM bwg.key_visuals
    );
    
    SELECT * FROM bwg.brand_review ORDER BY brand_fit DESC;
    
    AI.SCORE مقدار FLOAT64 را برمی‌گرداند و نه چیز دیگری، بنابراین آستانه در SQL ساده قرار دارد که می‌توانید بدون لمس اعلان، آن را تنظیم کنید. امتیازدهی در هر سطر انجام می‌شود، به این معنی که همان معیار برای هر دارایی که یک کمپین تولید می‌کند اعمال می‌شود، نه فقط دو موردی که در اینجا ایجاد کرده‌اید.

۱۱. مراحل بعدی

ایجاد مقادیر

ارزیابی‌های عامل‌محور یا «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 برای ارزیابی آنها در برابر دستورالعمل‌های برند خودتان استفاده کردید.