پشته عامل Google در عمل: ADK، A2A، MCP در Google Cloud

1. آنچه یاد خواهید گرفت

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

ایده این است که سیستمی بسازیم که در آن این عوامل بتوانند کارهای سنگین را انجام دهند، مانند "گوش دادن" هوشمندانه برای درک ترجیحات کاربر و دوست، و سپس پیشنهاد فعالانه فعالیت های خارق العاده و متناسب. هدف ما تبدیل برنامه ریزی اجتماعی در InstaVibe به چیزی یکپارچه و لذت بخش است. برای شروع ساخت این دستیارهای هوشمند، باید با ابزارهای مناسب، زمینه ای قوی ایجاد کنیم.

در اینجا مفهومی است که خواهید دید:

صفحه عنوان

مبانی با ADK Google: بر اصول ساخت اولین نماینده هوشمند خود با استفاده از کیت توسعه نماینده Google (ADK) مسلط شوید. اجزای اساسی، چرخه حیات عامل و نحوه استفاده موثر از ابزارهای داخلی چارچوب را درک کنید.

گسترش قابلیت‌های عامل با پروتکل بافت مدل (MCP): یاد بگیرید که نمایندگان خود را با ابزارها و زمینه‌های سفارشی تجهیز کنید و آنها را قادر به انجام وظایف تخصصی و دسترسی به اطلاعات خاص کنید. مفهوم پروتکل بافت مدل (MCP) را معرفی کنید. شما یاد خواهید گرفت که چگونه یک سرور MCP برای ارائه این زمینه راه اندازی کنید.

طراحی تعاملات و ارکستراسیون عامل: برای درک ارکستراسیون عامل، فراتر از عوامل منفرد حرکت کنید. طراحی الگوهای تعاملی از گردش کار متوالی ساده تا سناریوهای پیچیده شامل حلقه‌ها، منطق شرطی و پردازش موازی. مفهوم عوامل فرعی را در چارچوب ADK برای مدیریت وظایف مدولار معرفی کنید.

ساختن سیستم‌های چند عاملی مشترک: نحوه طراحی سیستم‌هایی را که در آن چندین عامل برای دستیابی به اهداف پیچیده با یکدیگر همکاری می‌کنند، کشف کنید. پروتکل ارتباطی A2A (Agent-to-Agent) را بیاموزید و پیاده سازی کنید، و یک راه استاندارد برای عوامل توزیع شده (که به طور بالقوه در ماشین ها یا سرویس های مختلف اجرا می شوند) برای تعامل قابل اعتماد ایجاد کنید.

عوامل تولید در Google Cloud: برنامه های عامل خود را از محیط های توسعه به ابر منتقل کنید. بهترین روش‌ها را برای معماری و استقرار سیستم‌های چند عاملی مقیاس‌پذیر و قوی در Google Cloud Platform (GCP) بیاموزید. در مورد استفاده از سرویس‌های GCP مانند Cloud Run اطلاعاتی به دست آورید و قابلیت‌های آخرین Google Agent Engine برای میزبانی و مدیریت نمایندگان خود را بررسی کنید.

2. معماری

برنامه ریزی اجتماعی مبتنی بر هوش مصنوعی با InstaVibe

گوش دادن اجتماعی چیست؟

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

شما در تیم InstaVibe هستید

تصور کنید در «InstaVibe» کار می‌کنید، یک استارت‌آپ موفق با یک پلتفرم رویداد اجتماعی محبوب با هدف بزرگسالان جوان. همه چیز به خوبی پیش می رود، اما مانند بسیاری از شرکت های فناوری، تیم شما با فشار سرمایه گذاران برای نوآوری با استفاده از هوش مصنوعی مواجه است. در داخل، شما همچنین متوجه بخشی از کاربران شده‌اید که به اندازه سایرین درگیر نیستند – شاید آنها تمایل کمتری به شروع فعالیت‌های گروهی داشته باشند یا فرآیند برنامه‌ریزی را چالش برانگیز بدانند. برای شرکت شما، این به معنای چسبندگی کمتر پلت فرم در میان این گروه کاربری مهم است.

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

یک راه حل مبتنی بر عامل (مفهوم نمونه اولیه)

شما توسعه یک ویژگی نمونه اولیه را پیشنهاد می کنید که توسط یک سیستم چند عاملی طراحی شده است. در اینجا یک تفکیک مفهومی وجود دارد:

مورد استفاده

  • عامل پروفایل اجتماعی : این عامل از تکنیک های گوش دادن اجتماعی برای تجزیه و تحلیل ارتباطات کاربر، تعاملات و روندهای عمومی بالقوه گسترده تر مربوط به ترجیحات کاربر استفاده می کند. هدف آن شناسایی علایق مشترک و ویژگی های فعالیت مناسب (مثلاً ترجیحات برای گردهمایی های آرام تر، سرگرمی های خاص) است.
  • عامل برنامه‌ریزی رویداد : با استفاده از بینش‌های «نماینده پروفایل اجتماعی»، این نماینده منابع آنلاین را برای رویدادها، مکان‌ها یا ایده‌هایی که با معیارهای شناسایی‌شده (مانند مکان، علایق) مطابقت دارند، جستجو می‌کند.
  • عامل تعامل پلتفرم (با استفاده از MCP) : این عامل طرح نهایی شده را از Activity Planning Agent می گیرد. عملکرد کلیدی آن تعامل مستقیم با پلتفرم InstaVibe با استفاده از یک ابزار از پیش تعریف شده MCP (مدل متن پروتکل) است. این ابزار قابلیت خاصی را در اختیار نماینده قرار می‌دهد تا یک پیشنهاد رویداد را پیش‌نویس کند و یک پست با تشریح طرح ایجاد کند.
  • عامل ارکستراتور : این عامل به عنوان هماهنگ کننده مرکزی عمل می کند. درخواست اولیه کاربر را از پلتفرم InstaVibe دریافت می کند، هدف کلی را درک می کند (به عنوان مثال، "برنامه ریزی یک رویداد برای من و دوستانم")، و سپس وظایف خاصی را به عوامل تخصصی مناسب در یک دنباله منطقی محول می کند. جریان اطلاعات بین عوامل را مدیریت می کند و اطمینان حاصل می کند که نتیجه نهایی به کاربر تحویل داده می شود.

عناصر و فن آوری های کلیدی معماری

معماری

Google Cloud Platform (GCP):

  • Vertex AI :
    • مدل‌های Gemini: دسترسی به پیشرفته‌ترین مدل‌های زبان بزرگ Google (LLM) مانند Gemini را فراهم می‌کند که توانایی‌های استدلال و تصمیم‌گیری نمایندگان ما را تقویت می‌کند.
    • Vertex AI Agent Engine: یک سرویس مدیریت‌شده که برای استقرار، میزبانی و مقیاس‌بندی عامل ارکستر ما، ساده‌سازی تولید و انتزاع پیچیدگی‌های زیرساخت استفاده می‌شود.
  • Cloud Run : یک پلت فرم بدون سرور برای استقرار برنامه های کاربردی کانتینری. ما از آن استفاده می کنیم تا:
    • میزبانی برنامه اصلی وب InstaVibe.
    • عامل‌های فعال A2A (برنامه‌ریز، پروفایل اجتماعی، تعامل پلتفرم) را به عنوان ریزسرویس‌های مستقل مستقر کنید.
    • سرور MCP Tool را اجرا کنید و APIهای داخلی InstaVibe را در دسترس نمایندگان قرار دهید.
  • Spanner : یک پایگاه داده رابطه ای کاملاً مدیریت شده، توزیع شده در سطح جهانی و کاملاً سازگار. در این کارگاه، ما از قابلیت‌های آن به‌عنوان یک پایگاه داده گراف با استفاده از ویژگی‌های GRAPH DDL و پرس‌وجو برای:
    • روابط اجتماعی پیچیده (کاربران، دوستی‌ها، حضور در رویداد، پست‌ها) را مدل‌سازی و ذخیره کنید.
    • پرس و جوی کارآمد از این روابط را برای عوامل اجتماعی پروفایل فعال کنید.
  • Artifact Registry : یک سرویس کاملاً مدیریت شده برای ذخیره، مدیریت و ایمن سازی تصاویر کانتینر.
  • Cloud Build : سرویسی که ساخت‌های شما را در Google Cloud اجرا می‌کند. ما از آن برای ساخت خودکار تصاویر ظرف Docker از کد منبع و عامل خود استفاده می کنیم.
  • Cloud Storage : توسط سرویس هایی مانند Cloud Build برای ذخیره سازی مصنوعات ساخت و توسط Agent Engine برای نیازهای عملیاتی آن استفاده می شود.
  • چارچوب ها و پروتکل های عامل اصلی :
    • کیت توسعه نماینده Google (ADK) : چارچوب اولیه برای:
      • تعریف منطق اصلی، رفتار و مجموعه دستورات برای عوامل هوشمند فردی.
      • مدیریت چرخه عمر عامل، حالت و حافظه (وضعیت جلسه کوتاه مدت و دانش بالقوه بلند مدت).
      • ادغام ابزارهایی (مانند جستجوی گوگل یا ابزارهای سفارشی سازی شده) که نمایندگان می توانند از آنها برای تعامل با جهان استفاده کنند.
      • تنظیم جریان های کاری چند عاملی، از جمله اجرای متوالی، حلقه ای و موازی عوامل فرعی.
    • پروتکل ارتباطی عامل به عامل (A2A) : یک استاندارد باز که امکان:
      • ارتباط و همکاری مستقیم و استاندارد شده بین عوامل هوش مصنوعی مختلف، حتی اگر آنها به عنوان سرویس های جداگانه یا در ماشین های مختلف در حال اجرا باشند.
      • عوامل برای کشف توانایی های یکدیگر (از طریق کارت های عامل) و واگذاری وظایف. این برای عامل ارکستراتور ما برای تعامل با عوامل تخصصی Planner، Social و Platform بسیار مهم است.
    • کتابخانه A2A Python (a2a-python) : کتابخانه بتن مورد استفاده برای ساختن عوامل ADK ما با پروتکل A2A صحبت می کنند. این مؤلفه های سمت سرور مورد نیاز برای:
      • عوامل ما را به عنوان سرورهای سازگار با A2A معرفی کنید.
      • به طور خودکار سرویس "کارت نماینده" را برای کشف مدیریت کنید.
      • درخواست‌های وظیفه دریافتی از سایر عوامل (مانند ارکستراتور) را دریافت و مدیریت کنید.
    • پروتکل زمینه مدل (MCP) : یک استاندارد باز که به عوامل اجازه می دهد:
      • با ابزارهای خارجی، منابع داده و سیستم ها به روشی استاندارد ارتباط برقرار کرده و از آنها استفاده کنید.
      • عامل تعامل پلتفرم ما از یک کلاینت MCP برای برقراری ارتباط با یک سرور MCP استفاده می کند، که به نوبه خود ابزارهایی را برای تعامل با API های موجود پلت فرم InstaVibe در معرض دید قرار می دهد.
  • ابزارهای رفع اشکال :
    • A2A Inspector : A2A Inspector یک ابزار اشکال زدایی مبتنی بر وب است که در سراسر این کارگاه برای اتصال، بازرسی و تعامل با عوامل دارای A2A ما استفاده می شود. در حالی که بخشی از معماری نهایی تولید نیست، بخش مهمی از گردش کار توسعه ما است. فراهم می کند:
      • Agent Card Viewer: برای واکشی و اعتبارسنجی قابلیت‌های عمومی یک نماینده.
      • رابط چت زنده: برای ارسال پیام ها به طور مستقیم به یک عامل مستقر برای آزمایش فوری.
      • Debug Console: برای مشاهده پیام های خام JSON-RPC که بین بازرس و عامل رد و بدل می شود.
  • مدل های زبان (LLM) : "مغزهای" سیستم:
    • مدل های جمینی گوگل: به طور خاص، ما از نسخه هایی مانند gemini-2.0-flash استفاده می کنیم. این مدل ها برای موارد زیر انتخاب می شوند:
      • دنبال کردن استدلال و دستورالعمل پیشرفته: توانایی آنها در درک اعلان های پیچیده، پیروی از دستورالعمل های دقیق و استدلال در مورد وظایف، آنها را برای قدرت بخشیدن به تصمیم گیری عامل مناسب می کند.
      • استفاده از ابزار (تعداد فراخوانی تابع): مدل‌های Gemini در تعیین زمان و نحوه استفاده از ابزارهای ارائه شده از طریق ADK برتری دارند و عوامل را قادر می‌سازد اطلاعات را جمع‌آوری کنند یا اقداماتی را انجام دهند.
      • کارایی (مدل های فلش): انواع "فلش" تعادل خوبی از عملکرد و مقرون به صرفه بودن را ارائه می دهند که برای بسیاری از وظایف عامل تعاملی که نیاز به پاسخ های سریع دارند مناسب است.

3. قبل از شروع

👉روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید (این نماد شکل ترمینال در بالای صفحه Cloud Shell است)، پوسته ابری

👉روی دکمه "Open Editor " کلیک کنید (به نظر می رسد یک پوشه باز با یک مداد است). با این کار ویرایشگر کد Cloud Shell در پنجره باز می شود. در سمت چپ یک فایل کاوشگر خواهید دید. پوسته ابری

همانطور که نشان داده شده است، روی دکمه ورود به سیستم Cloud Code در نوار وضعیت پایین کلیک کنید. پلاگین را طبق دستورالعمل مجاز کنید. اگر Cloud Code - بدون پروژه را در نوار وضعیت می‌بینید، آن را در منوی کشویی «انتخاب یک پروژه Google Cloud» انتخاب کنید و سپس پروژه Google Cloud خاص را از لیست پروژه‌هایی که ایجاد کرده‌اید انتخاب کنید. پوسته ابری

👉 شناسه پروژه Google Cloud خود را پیدا کنید:

  • Google Cloud Console را باز کنید: https://console.cloud.google.com
  • پروژه ای را که می خواهید برای این کارگاه استفاده کنید از منوی کشویی پروژه در بالای صفحه انتخاب کنید.
  • شناسه پروژه شما در کارت اطلاعات پروژه در داشبورد نمایش داده می شود

پوسته ابری

👉ترمینال را در IDE ابری باز کنید، پوسته ابری

👉💻 در ترمینال، با استفاده از دستور زیر بررسی کنید که قبلا احراز هویت شده اید و پروژه به ID پروژه شما تنظیم شده است:

gcloud auth list

👉💻 پروژه instavibe-bootstrap را از GitHub کلون کنید:

git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh

شناخت ساختار پروژه

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

instavibe-bootstrap/
├── agents/
   ├── orchestrate/
   ├── planner/
   ├── platform_mcp_client/
   └── social/
├── instavibe/
   ├── static/
   └── templates/
├── tools/
   └── instavibe/
├── utils/
├── init.sh
└── set_env.sh

در اینجا فهرستی از دایرکتوری های کلیدی آورده شده است:

  • agents/ : این قلب سیستم هوش مصنوعی ما است. هر زیر شاخه (برنامه ریز/، اجتماعی/ و غیره) حاوی کد منبع برای یک عامل هوشمند خاص است.
    • agent.py : در داخل پوشه هر عامل، این فایل اصلی است که منطق عامل در آن قرار دارد.
    • a2a_server.py : این فایل عامل ADK را با یک سرور Agent-to-Agent (A2A) می پیچد.
    • Dockerfile : نحوه ساخت تصویر کانتینر را برای استقرار عامل در Cloud Run یا Agent Engine تعریف می کند.
  • instavibe/ : این فهرست شامل کل کد منبع برنامه وب InstaVibe است.
  • tools/ : این دایرکتوری برای ساخت ابزارهای خارجی است که نمایندگان ما می توانند از آنها استفاده کنند.
    • instavibe/ شامل سرور پروتکل زمینه مدل (MCP) است.

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

👉💻 اسکریپت اولیه سازی را اجرا کنید:

این اسکریپت از شما می خواهد که شناسه پروژه Google Cloud خود را وارد کنید.

شناسه پروژه Google Cloud را که از آخرین مرحله پیدا کردید، با درخواست اسکریپت init.sh وارد کنید:

cd ~/instavibe-bootstrap
./init.sh

👉💻 شناسه پروژه مورد نیاز را تنظیم کنید:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 دستور زیر را برای فعال کردن API های Google Cloud لازم اجرا کنید:

gcloud services enable  run.googleapis.com \
                        cloudfunctions.googleapis.com \
                        cloudbuild.googleapis.com \
                        artifactregistry.googleapis.com \
                        spanner.googleapis.com \
                        apikeys.googleapis.com \
                        iam.googleapis.com \
                        compute.googleapis.com \
                        aiplatform.googleapis.com \
                        cloudresourcemanager.googleapis.com \
                        maps-backend.googleapis.com

👉💻 تمام متغیر محیطی مورد نیاز را تنظیم کنید:

export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
export SPANNER_INSTANCE_ID="instavibe-graph-instance"
export SPANNER_DATABASE_ID="graphdb"
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_LOCATION="us-central1"

راه اندازی مجوز

👉💻 مجوزها را اعطا کنید. در ترمینال، اجرا کنید:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

# Spanner Database User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

# Artifact Registry Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

# Cloud Build Editor
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

# Cloud Run Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

# IAM Service Account User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

# Vertex AI User
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

# Logging Writer (to allow writing logs)
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


👉 نتیجه را در کنسول IAM خود تأیید کنید پوسته ابری

👉💻 دستورات زیر را در ترمینال اجرا کنید تا یک مخزن آرتیفکت رجیستری ایجاد کنید. همه تصاویر Docker برای نمایندگان ما، سرور MCP و برنامه InstaVibe قبل از استقرار در Cloud Run یا Agent Engine در اینجا ذخیره می‌شوند.

export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository for InstaVibe workshop"

راه اندازی پلت فرم نقشه برای کلیدهای API

برای استفاده از خدمات Google Maps در برنامه InstaVibe خود، باید یک کلید API ایجاد کنید و آن را به طور مناسب محدود کنید.

👉 در یک برگه جدید، به APIs & Services > Credentials بروید. در صفحه "Credentials"، روی دکمه + CREATE CREDENTIALS در بالا کلیک کنید. کلید API را از منوی کشویی انتخاب کنید. متن جایگزین

👉 یک کادر محاوره ای ظاهر می شود که کلید API تازه ایجاد شده شما را نشان می دهد. بعداً برای پیکربندی برنامه خود به آن نیاز خواهید داشت.

👉 روی CLOSE در گفتگوی "کلید API ایجاد شد" کلیک کنید.

👉 کلید API جدید خود را در لیست خواهید دید (به عنوان مثال، "کلید API 1"). روی نماد همبرگر در سمت راست کلیک کنید، کلید ویرایش API را انتخاب کنید تا صفحه "محدود کردن و تغییر نام کلید API" را باز کنید. متن جایگزین

👉 در قسمت نام در بالا، نام پیش‌فرض را به: Maps Platform Key API تغییر دهید (🚨🚨مهم🚨🚨 لطفا از این نام استفاده کنید!)

Maps Platform API Key

👉 در بخش "محدودیت های برنامه" مطمئن شوید که هیچ کدام انتخاب نشده است.

👉 در بخش "محدودیت های API"، دکمه رادیویی Restrict key را انتخاب کنید.

👉 روی منوی کشویی Select APIs کلیک کنید. در کادر جستجویی که ظاهر می‌شود، Maps JavaScript API تایپ کرده و آن را از لیست انتخاب کنید. متن جایگزین

👉 روی OK کلیک کنید.

👉 روی دکمه SAVE در پایین صفحه کلیک کنید.

نتیجه کلیدی

شما اکنون با موفقیت یک کلید API به نام "Maps Platform API Key" ایجاد کرده اید، آن را محدود کرده اید تا فقط اجازه استفاده از "Maps JavaScript API" را بدهد و اطمینان حاصل کنید که API برای پروژه شما فعال است.

4. پایگاه داده گراف را راه اندازی کنید

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

ما در حال پیاده سازی این پایگاه داده نمودار با استفاده از Google Cloud Spanner هستیم. در حالی که Spanner در درجه اول به عنوان یک پایگاه داده رابطه ای با توزیع جهانی شناخته می شود، اما همچنین به ما اجازه می دهد تا ساختارهای گراف را مستقیماً در بالای جداول رابطه ای خود تعریف و جستجو کنیم.

این به ما مزایای ترکیبی مقیاس پذیری، ثبات تراکنش، و رابط آشنای SQL را به همراه قدرت بیانی پرس و جوهای گراف برای تجزیه و تحلیل پویایی های اجتماعی پیچیده که برای ویژگی های مبتنی بر هوش مصنوعی ما ضروری است، می دهد.

👉💻 در ترمینال Cloud Shell IDE. زیرساخت های لازم را در Google Cloud فراهم کنید. ما با ایجاد یک نمونه Spanner شروع می کنیم که به عنوان یک ظرف اختصاصی برای پایگاه داده ما عمل می کند. هنگامی که نمونه آماده شد، ما پایگاه داده واقعی Spanner را در آن ایجاد می کنیم که تمام جداول ما و داده های نمودار را برای InstaVibe در خود جای می دهد:

. ~/instavibe-bootstrap/set_env.sh

gcloud spanner instances create $SPANNER_INSTANCE_ID \
  --config=regional-us-central1 \
  --description="GraphDB Instance InstaVibe" \
  --processing-units=100 \
  --edition=ENTERPRISE

gcloud spanner databases create $SPANNER_DATABASE_ID \
  --instance=$SPANNER_INSTANCE_ID \
  --database-dialect=GOOGLE_STANDARD_SQL

👉💻 به آچار دسترسی خواندن/نوشتن به حساب سرویس پیش‌فرض اعطا کنید

echo "Granting Spanner read/write access to ${SERVICE_ACCOUNT_NAME} for database ${SPANNER_DATABASE_ID}..."

gcloud spanner databases add-iam-policy-binding ${SPANNER_DATABASE_ID} \
  --instance=${SPANNER_INSTANCE_ID} \
  --member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
  --role="roles/spanner.databaseUser" \
  --project=${PROJECT_ID}

👉💻 اکنون ما یک محیط مجازی پایتون راه اندازی می کنیم، بسته های پایتون مورد نیاز را نصب می کنیم و سپس طرح واره پایگاه داده Graph را در Spanner راه اندازی می کنیم و آن را با داده های اولیه بارگذاری می کنیم و اسکریپت setup.py اجرا می کنیم.

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap
python -m venv env
source env/bin/activate
pip install -r requirements.txt
cd instavibe
python setup.py

👉 در یک برگه مرورگر جدید، به Google Cloud Console بروید، به Spanner بروید، باید فهرستی از نمونه‌های Spanner خود را ببینید. روی instavibe-graph-instance کلیک کنید. نمونه آچار 👉 در صفحه نمای کلی نمونه، فهرستی از پایگاه های داده را در آن نمونه خواهید دید. روی graphdb کلیک کنید آچار db

👉 در صفحه ناوبری سمت چپ پایگاه داده خود، روی Spanner Studio کلیک کنید استودیو آچار

👉 در ویرایشگر پرس و جو (برگه پرس و جو بدون عنوان)، پرس و جو Graph SQL زیر را جایگذاری کنید. این پرس و جو همه گره های شخص و روابط دوستی مستقیم آنها را با سایر گره های شخص پیدا می کند. و برای مشاهده نتیجه روی RUN کلیک کنید.

Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths

نمودار آچار

👉 در همان ویرایشگر پرس و جو، DDL قبلی را جایگزین کنید تا افرادی را پیدا کنید که در همان رویداد شرکت کرده اند، که به معنای ارتباط غیر مستقیم از طریق یک فعالیت مشترک است.

Graph SocialGraph
MATCH result_paths =  (p1:Person)-[:Attended]->(e:Event)<-[:Attended]-(p2:Person)
WHERE p1.person_id < p2.person_id
RETURN SAFE_TO_JSON(result_paths) AS result_paths

نمودار آچار

👉 این پرس و جو نوع متفاوتی از اتصال را بررسی می کند، جایی که افرادی که در پست های نوشته شده توسط دوستان یک شخص خاص ذکر شده اند، عبارت زیر را در ویرایشگر پرس و جو اجرا می کنند.

Graph SocialGraph
MATCH result_paths =  (user:Person {name: "Alice"})-[:Friendship]-(friend:Person)-[:Wrote]->(post:Post)-[:Mentioned]->(mentioned_person:Person)
WHERE user <> mentioned_person AND friend <> mentioned_person -- Avoid self-mentions or friend mentioning themselves in their own post if not intended
RETURN SAFE_TO_JSON(result_paths) AS result_paths

نمودار آچار

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

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

5. وضعیت فعلی InstaVibe

برای درک اینکه عوامل هوش مصنوعی ما در کجا قرار می گیرند، ابتدا باید برنامه وب InstaVibe موجود را مستقر و اجرا کنیم. این برنامه رابط کاربری و عملکردهای اساسی را فراهم می کند که به پایگاه داده Spanner graph که قبلاً تنظیم کرده ایم متصل می شود.

صفحه اصلی

برنامه InstaVibe از Google Maps برای نمایش بصری مکان رویدادها در صفحات جزئیات رویداد خود استفاده می کند. برای فعال کردن این قابلیت، برنامه به کلید API نیاز دارد که قبلا ایجاد کردیم. اسکریپت زیر رشته کلید واقعی را با استفاده از نام نمایشی که اختصاص داده ایم ("Maps Platform API Key") بازیابی می کند.

صفحه رویداد

👉💻 بازگشت به IDE پوسته Cloud . اسکریپت زیر را اجرا کنید. پس از آن، خروجی را به دقت بررسی کنید تا مطمئن شوید GOOGLE_MAPS_API_KEY نشان داده شده با کلیدی که قبلاً از Google Cloud Console ایجاد و کپی کرده‌اید مطابقت دارد.

. ~/instavibe-bootstrap/set_env.sh
export KEY_DISPLAY_NAME="Maps Platform API Key"

GOOGLE_MAPS_KEY_ID=$(gcloud services api-keys list \
  --project="${PROJECT_ID}" \
  --filter="displayName='${KEY_DISPLAY_NAME}'" \
  --format="value(uid)" \
  --limit=1)

GOOGLE_MAPS_API_KEY=$(gcloud services api-keys get-key-string "${GOOGLE_MAPS_KEY_ID}" \
    --project="${PROJECT_ID}" \
    --format="value(keyString)")

echo "${GOOGLE_MAPS_API_KEY}" > ~/mapkey.txt

echo "Retrieved GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}"

نتیجه کلیدی

👉💻 اکنون، بیایید تصویر کانتینر را برای برنامه وب InstaVibe بسازیم و آن را به مخزن Artifact Registry خود فشار دهیم.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 بیلد جدید تصویر برنامه وب InstaVibe را در Cloud Run اجرا کنید

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --project=${PROJECT_ID} \
  --min-instances=1

با تکمیل موفقیت آمیز استقرار، گزارش‌های Cloud Run باید URL عمومی برنامه InstaVibe در حال اجرا شما را نمایش دهند.

URL

همچنین می توانید این URL را با رفتن به بخش Cloud Run در Google Cloud Console و انتخاب سرویس instavibe پیدا کنید. فهرست کنیدURL

پیش بروید و اکنون آن URL را در مرورگر وب خود باز کنید تا پلتفرم اصلی InstaVibe را بررسی کنید. پست‌ها، رویدادها و اتصالات کاربر را که توسط پایگاه داده نموداری که ما راه‌اندازی کرده‌ایم، مشاهده کنید.

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

6. عامل اصلی، برنامه ریز رویداد با ADK

چارچوب ADK

معرفی چارچوب ADK Google اکنون که پایه ما (برنامه و پایگاه داده InstaVibe) تنظیم شده است، می‌توانیم ساخت اولین عامل هوشمند خود را با استفاده از کیت توسعه نماینده Google (ADK) شروع کنیم.

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

در هسته خود، ADK حول مفهوم Agent می چرخد، که دستورالعمل ها، پیکربندی (مانند مدل زبان انتخاب شده، به عنوان مثال، جمینی)، و مجموعه ای از Tools که می تواند برای انجام اقدامات یا جمع آوری اطلاعات استفاده کند، در بر می گیرد.

06-agent.png

نماینده اولیه ما یک "برنامه ریز رویداد" خواهد بود. هدف اصلی آن دریافت درخواست‌های کاربران برای گردش‌های اجتماعی (مشخص کردن مکان، تاریخ و علایق) و ایجاد پیشنهادهای خلاقانه و متناسب است. برای اطمینان از مرتبط بودن پیشنهادها و بر اساس اطلاعات فعلی (مانند رویدادهای خاصی که در آخر هفته اتفاق می‌افتد)، از یکی از ابزارهای داخلی ADK استفاده می‌کنیم: جستجوی Google . این به عامل اجازه می‌دهد تا پاسخ‌های خود را در نتایج وب بلادرنگ پایه‌گذاری کند، و آخرین جزئیات را در مورد مکان‌ها، رویدادها و فعالیت‌هایی که با معیارهای کاربر مطابقت دارند، دریافت کند.

👉📝 بازگشت به IDE پوسته Cloud ، در ~/instavibe-bootstrap/agents/planner/agent.py دستور و دستورالعمل زیر را برای ایجاد عامل اضافه کنید

from google.adk.agents import Agent
from google.adk.tools import google_search

root_agent = Agent(
    name="planner_agent",
    model="gemini-2.0-flash",
    description="Agent tasked with generating creative and fun dating plan suggestions",
    instruction="""

        You are a specialized AI assistant tasked with generating creative and fun plan suggestions.

        Request:
        For the upcoming weekend, specifically from **[START_DATE_YYYY-MM-DD]** to **[END_DATE_YYYY-MM-DD]**, in the location specified as **[TARGET_LOCATION_NAME_OR_CITY_STATE]** (if latitude/longitude are provided, use these: Lat: **[TARGET_LATITUDE]**, Lon: **[TARGET_LONGITUDE]**), please generate a distinct dating plan suggestions.

        Constraints and Guidelines for Suggestions:
        1.  Creativity & Fun: Plans should be engaging, memorable, and offer a good experience for a date.
        2.  Budget: All generated plans should aim for a moderate budget (conceptually "$$"), meaning they should be affordable yet offer good value, without being overly cheap or extravagant. This budget level should be *reflected in the choice of activities and venues*, but **do not** explicitly state "Budget: $$" in the `plan_description`.
        3.  Interest Alignment:
               Consider the following user interests: **[COMMA_SEPARATED_LIST_OF_INTERESTS, e.g., outdoors, arts & culture, foodie, nightlife, unique local events, live music, active/sports]**. Tailor suggestions specifically to these where possible. The plan should *embody* these interests.
               Fallback: If specific events or venues perfectly matching all listed user interests cannot be found for the specified weekend, you should create a creative and fun generic dating plan that is still appealing, suitable for the location, and adheres to the moderate budget. This plan should still sound exciting and fun, even if it's more general.
        4.  Current & Specific: Prioritize finding specific, current events, festivals, pop-ups, or unique local venues operating or happening during the specified weekend dates. If exact current events cannot be found, suggest appealing evergreen options or implement the fallback generic plan.
        5.  Location Details: For each place or event mentioned within a plan, you MUST provide its name, precise latitude, precise longitude, and a brief, helpful description.
        6.  Maximum Activities: The plan must contain a maximum of 3 distinct activities.

        RETURN PLAN in MARKDOWN FORMAT 
    """,
    tools=[google_search]
)

و این اولین عامل تعریف شده ماست! یکی از چیزهای مهم در مورد ADK ماهیت بصری آن و ابزارهای مفیدی است که ارائه می دهد. یکی از موارد بسیار مفید، ADK Dev UI است که به شما امکان می دهد به صورت تعاملی عامل خود را آزمایش کنید و پاسخ های آن را در زمان واقعی مشاهده کنید.

👉💻 بیایید شروع کنیم. دستورات زیر ADK DEV UI را راه اندازی می کند:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/planner/.env
adk web

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

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://localhost:8000.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

👉 بعد، برای دسترسی به ADK Dev UI از مرورگر خود:

از نماد پیش‌نمایش وب (اغلب با یک فلش مانند یک چشم یا مربع به نظر می‌رسد) در نوار ابزار Cloud Shell (معمولاً سمت راست بالا)، Change port را انتخاب کنید. در پنجره پاپ آپ، پورت را روی 8000 تنظیم کنید و روی "Change and Preview" کلیک کنید. سپس Cloud Shell یک برگه یا پنجره جدید مرورگر را باز می کند که ADK Dev UI را نشان می دهد.

پیش نمایش وب

هنگامی که ADK Dev UI در مرورگر شما باز شد: در منوی کشویی بالا سمت راست رابط کاربری، برنامه نویس را به عنوان عاملی که می خواهید با آن تعامل داشته باشید انتخاب کنید. اکنون، در گفتگوی چت در سمت راست، سعی کنید به نماینده خود یک وظیفه بدهید. به عنوان مثال، با نماینده صحبت کنید:

Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime

تاریخ را پیشنهاد دهید (به دلخواه شما)

July 12 2025

باید ببینید که نماینده درخواست شما را پردازش می کند و طرحی را بر اساس نتایج جستجوی Google ارائه می دهد.

adk dev UI

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

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

ارزشیابی

👉 در ADK Dev UI، روی تب "Eval" در ناوبری سمت چپ کلیک کنید. شما باید یک فایل آزمایشی از پیش بارگذاری شده با نام plan_eval را ببینید. این فایل حاوی ورودی ها و معیارهای از پیش تعریف شده برای آزمایش عامل برنامه ریز ما است.

👉 یک سناریو مانند "boston" را انتخاب کنید و روی دکمه Run Evaluation کلیک کنید. در پنجره پاپ آپی که ظاهر می شود، امتیاز مسابقه را به 0.3 کاهش دهید و روی Start کلیک کنید.

امتیاز مسابقه

این عامل را با ورودی تست اجرا می کند و بررسی می کند که آیا خروجی آن انتظارات تعریف شده را برآورده می کند یا خیر. این به شما راهی می دهد تا به طور سیستماتیک عملکرد نماینده خود را آزمایش کنید.

ارزیابی adk dev UI

👉 حالا، بیایید ببینیم با یک آستانه سخت‌تر چه اتفاقی می‌افتد. سناریوی "nyc" را انتخاب کنید و دوباره روی Run Evaluation کلیک کنید. این بار امتیاز مسابقه را روی مقدار پیش فرض خود بگذارید (Response match score: 0.7) و روی Start کلیک کنید. متوجه خواهید شد که نتیجه شکست است. این مورد انتظار است، زیرا خروجی خلاق عامل کاملاً با پاسخ "طلایی" از پیش تعریف شده مطابقت ندارد.

adk dev ارزیابی رابط کاربری ناموفق

👉 برای درک اینکه چرا شکست خورد، روی نماد شکست در ردیف "nyc" کلیک کنید. رابط کاربری اکنون مقایسه ای کنار هم از پاسخ واقعی از عامل و پاسخ مورد انتظار از مورد آزمایشی را نشان می دهد. این نمای برای اشکال‌زدایی ضروری است، به شما این امکان را می‌دهد که ببینید دقیقاً کجا خروجی عامل واگرا شده است و دستورالعمل‌های آن را مطابق با آن اصلاح کنید.

پس از اتمام کاوش در رابط کاربری و ارزیابی، به ترمینال Cloud Shell Editor خود برگردید و Ctrl+C را فشار دهید تا ADK Dev UI متوقف شود.

در حالی که خروجی متن آزاد شروع خوبی است، برای برنامه هایی مانند InstaVibe که به راحتی از پیشنهادات یک نماینده استفاده کنند، داده های ساختاریافته (مانند JSON) بسیار کاربردی تر است. بیایید نماینده خود را تغییر دهیم تا طرح خود را در قالب JSON ثابت برگرداند.

👉📝 در ~/instavibe-bootstrap/agents/planner/agent.py ، خطی را که در حال حاضر می‌گوید RETURN PLAN in MARKDOWN FORMAT در رشته دستورالعمل عامل می‌گوید، پیدا کنید. آن خط را با ساختار JSON دقیق زیر جایگزین کنید:

Return your response *exclusively* as a single JSON object. This object should contain a top-level key, "fun_plans", which holds a plan objects. Each plan object in the list must strictly adhere to the following structure:

        --json--
        {
          "plan_description": "A summary of the overall plan, consisting of **exactly three sentences**. Craft these sentences in a friendly, enthusiastic, and conversational tone, as if you're suggesting this awesome idea to a close friend. Make it sound exciting and personal, highlighting the positive aspects and appeal of the plan without explicitly mentioning budget or listing interest categories.",
          "locations_and_activities": [
              {
              "name": "Name of the specific place or event",
              "latitude": 0.000000,  // Replace with actual latitude
              "longitude": 0.000000, // Replace with actual longitude
              "description": "A brief description of this place/event, why it's suitable for the date, and any specific details for the weekend (e.g., opening hours, event time)."
              }
              // Add more location/activity objects here if the plan involves multiple stops/parts
          ]
        }

اکنون که دستورالعمل‌های عامل را برای درخواست خروجی JSON به‌روزرسانی کرده‌اید، اجازه دهید تغییر را تأیید کنیم.

👉💻 با استفاده از دستور قبلی، ADK Dev UI را دوباره راه اندازی کنید :

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
adk web

اگر برگه را از قبل باز کرده اید، آن را تازه کنید . یا همان مراحل قبلی را دنبال کنید تا ADK Dev UI را در مرورگر خود باز کنید (از طریق پیش نمایش وب Cloud Shell در پورت 8000). پس از بارگیری رابط کاربری، مطمئن شوید که عامل برنامه ریز انتخاب شده است.

👉 این بار بیایید یک درخواست متفاوت به آن بدهیم. در گفتگوی چت، وارد کنید:

Plan an event Boston this weekend with art and coffee

پاسخ نماینده را به دقت بررسی کنید. به جای یک پاسخ متنی صرفاً محاوره‌ای، اکنون باید پاسخی را ببینید که کاملاً به عنوان یک شی JSON قالب‌بندی شده است و با ساختاری که در دستورالعمل‌ها تعریف کرده‌ایم مطابقت دارد (شامل fun_plans، plan_description، locations_and_activities و غیره). این تأیید می کند که عامل اکنون می تواند خروجی ساختاریافته مناسب برای استفاده برنامه ریزی شده توسط برنامه InstaVibe ما تولید کند.

adk dev UI json

پس از تایید خروجی JSON، به ترمینال Cloud Shell خود برگردید و Ctrl+C را فشار دهید تا ADK Dev UI متوقف شود.

اجزای ADK

در حالی که ADK Dev UI برای تست های تعاملی عالی است، ما اغلب نیاز داریم که عوامل خود را به صورت برنامه نویسی اجرا کنیم، شاید به عنوان بخشی از یک برنامه کاربردی بزرگتر یا سرویس باطن. برای درک اینکه چگونه این کار می کند، اجازه دهید به برخی از مفاهیم اصلی ADK مربوط به زمان اجرا و مدیریت زمینه نگاه کنیم.

مکالمات معنی دار و چند نوبتی به عوامل نیاز دارد که زمینه را درک کنند - یادآوری آنچه گفته شده و انجام شده است تا تداوم داشته باشد. ADK راه های ساختاریافته ای را برای مدیریت این زمینه از طریق Session ، State و Memory ارائه می دهد:

  • Session: هنگامی که کاربر شروع به تعامل با یک عامل می کند، یک Session ایجاد می شود. آن را به عنوان محفظه ای برای یک موضوع گفتگوی خاص در نظر بگیرید. این دارای یک شناسه منحصر به فرد، تاریخچه تعاملات (رویدادها)، داده های کاری فعلی (State) و ابرداده مانند آخرین زمان به روز رسانی است.
  • حالت: این حافظه کوتاه مدت و کاری عامل در یک جلسه واحد است. این یک فرهنگ لغت قابل تغییر است که در آن عامل می تواند اطلاعات موقت مورد نیاز برای تکمیل کار فعلی را ذخیره کند (به عنوان مثال، تنظیمات برگزیده کاربر تا کنون جمع آوری شده، نتایج میانی از فراخوانی ابزار).
  • حافظه: این نشان دهنده پتانسیل عامل برای یادآوری طولانی مدت در جلسات مختلف یا دسترسی به پایگاه های دانش خارجی است. در حالی که Session و State مکالمه فوری را مدیریت می‌کنند، Memory (اغلب توسط MemoryService مدیریت می‌شود) به یک عامل اجازه می‌دهد اطلاعات را از تعاملات گذشته یا منابع داده ساختاریافته بازیابی کند و زمینه دانش گسترده‌تری به آن بدهد. (توجه: کلاینت ساده ما در سرویس های حافظه برای سادگی استفاده می کند، به این معنی که حافظه/وضعیت فقط زمانی که اسکریپت اجرا می شود باقی می ماند).
  • رویداد: هر تعامل در یک جلسه (پیام کاربر، پاسخ عامل، درخواست استفاده از ابزار، نتیجه ابزار، تغییر حالت، خطا) به عنوان یک رویداد تغییرناپذیر ثبت می شود. این یک گزارش زمانی ایجاد می‌کند، اساساً رونوشت و تاریخچه عمل مکالمه.

بنابراین، وقتی یک عامل اجرا می شود، اینها چگونه مدیریت می شوند؟ این کار دونده است.

  • Runner : Runner موتور اجرای هسته ای است که توسط ADK ارائه شده است. شما عامل خود و ابزارهایی را که استفاده می کند تعریف می کنید، و Runner فرآیند برآورده کردن درخواست کاربر را هماهنگ می کند. این Session را مدیریت می‌کند، جریان رویدادها را مدیریت می‌کند، وضعیت را به‌روزرسانی می‌کند، مدل زبان اصلی را فراخوانی می‌کند، تماس‌های ابزار را هماهنگ می‌کند و احتمالاً با MemoryService تعامل دارد. به آن به عنوان هادی فکر کنید که مطمئن می شود همه قسمت های مختلف به درستی با هم کار می کنند.

ما می توانیم از Runner برای اجرای عامل خود به عنوان یک برنامه پایتون مستقل، کاملا مستقل از Dev UI استفاده کنیم.

بیایید یک اسکریپت کلاینت ساده ایجاد کنیم تا عامل برنامه ریز خود را به صورت برنامه نویسی فراخوانی کنیم.

👉📝 در فایل ~/instavibe-bootstrap/agents/planner/planner_client.py ، کد پایتون زیر را در قسمت واردات موجود اضافه کنید. در planner_client.py ، در قسمت import، موارد زیر را اضافه کنید:

async def async_main():
  session_service = InMemorySessionService()

  session = await session_service.create_session(
      state={}, app_name='planner_app', user_id='user_dc'
  )

  query = "Plan Something for me in San Francisco this weekend on wine and fashion "
  print(f"User Query: '{query}'")
  content = types.Content(role='user', parts=[types.Part(text=query)])

  root_agent = agent.root_agent
  runner = Runner(
        app_name='planner_app',
        agent=root_agent,
        session_service=session_service,
  )
  print("Running agent...")
  events_async =  runner.run_async(
    session_id=session.id, user_id=session.user_id, new_message=content
  )

  async for event in events_async:
    print(f"Event received: {event}")


if __name__ == '__main__':
  try:
    asyncio.run(async_main())
  except Exception as e:
    print(f"An error occurred: {e}")

این کد سرویس‌های درون حافظه را برای مدیریت نشست و مصنوع تنظیم می‌کند (برای مثال ساده نگه می‌دارد)، یک جلسه ایجاد می‌کند، یک درخواست کاربر تعریف می‌کند، Runner را با عامل ما پیکربندی می‌کند، و سپس عامل را به صورت ناهمزمان اجرا می‌کند، و هر رویداد تولید شده در طول اجرا را چاپ می‌کند.

👉💻 اکنون این اسکریپت کلاینت را از ترمینال خود اجرا کنید:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
python -m planner.planner_client

👀 خروجی را رعایت کنید. به جای طرح نهایی JSON، ساختار دقیق هر شیء رویداد تولید شده در جریان اجرای عامل را خواهید دید. این شامل رویداد پیام کاربر اولیه، رویدادهای احتمالی مربوط به تماس‌های ابزار (مانند جستجوی Google)، و در نهایت، رویداد پاسخ مدل حاوی طرح JSON است. این جریان رویداد دقیق برای اشکال زدایی و درک پردازش گام به گام که در ADK Runtime اتفاق می افتد بسیار مفید است.

Running agent...
Event received: content=Content(parts=[Part(video_metadata=None, thought=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, inline_data=None, text='```json\n{\n "fun_plans": [\n  {\n   "plan_description": "Embark on a stylish adventure through Hayes Valley, 
...(turncated)
, offering a variety of fashion styles to browse and enjoy."\n    }\n   ]\n  }\n ]\n}\n```')], role='model') grounding_metadata=GroundingMetadata(grounding_chunks=[GroundingChunk(retrieved_context=None, web=GroundingChunkWeb(domain='islands.com', title='islands.com', uri='http
...(turncated)
QyTpPV7jS6wUt-Ix7GuP2mC9J4eY_8Km6Vv44liF9cb2VSs='))], grounding_supports=[GroundingSupport(confide
...(turncated)
>\n', sdk_blob=None), web_search_queries=['..e']) partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None invocation_id='e-04d97b8b-9021-47a5-ab41-17b5cbb4bf03' author='location_search_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='CInHdkKw' timestamp=1746978846.232674

اگر اسکریپت به‌طور مداوم اجرا می‌شود یا هنگ می‌کند، ممکن است لازم باشد با فشار دادن Ctrl+C آن را به‌صورت دستی متوقف کنید.

7. عامل تعامل پلتفرم - تعامل با سرور MCP

در حالی که ADK به ساختار عوامل ما کمک می کند، آنها اغلب نیاز به تعامل با سیستم های خارجی یا API ها برای انجام اقدامات در دنیای واقعی دارند.

پروتکل بافت مدل (MCP)

پروتکل زمینه Model (MCP) یک استاندارد باز است که به منظور استاندارد کردن برنامه های کاربردی AI مانند نمایندگان ، ارتباط با منابع داده های خارجی ، ابزارها و سیستم ها طراحی شده است. این هدف برای حل مسئله نیاز به ادغام های سفارشی برای هر برنامه AI و ترکیب منبع داده با ارائه یک رابط جهانی است. MCP از معماری مشتری-سرور استفاده می کند که در آن مشتریان MCP ، که در برنامه های AI (میزبان) ساکن هستند ، ارتباطات مربوط به سرورهای MCP را مدیریت می کنند. این سرورها برنامه های خارجی هستند که عملکردهای خاصی مانند دسترسی به داده های محلی ، تعامل با خدمات از راه دور از طریق API ها یا ارائه اعلان های از پیش تعریف شده را نشان می دهند و به مدل های هوش مصنوعی اجازه می دهند تا به اطلاعات فعلی دسترسی پیدا کنند و از آموزش اولیه خود وظایف خود را انجام دهند. این ساختار مدل های هوش مصنوعی را قادر می سازد تا با قابلیت های خارجی به روشی استاندارد ، با قابلیت های خارجی کشف و تعامل داشته باشند و ادغام ها را ساده تر و مقیاس پذیر تر می کنند.

سرور MCP Instavibe را بسازید و مستقر کنید

07-MCP-server.png

مأمورین ما در نهایت باید با خود پلتفرم Instavibe تعامل داشته باشند. به طور خاص ، برای ایجاد پست و ثبت رویدادها با استفاده از API های موجود پلتفرم. برنامه instavibe قبلاً این ویژگی ها را از طریق نقاط پایانی استاندارد HTTP در معرض دید قرار می دهد:

نقطه

URL

روش HTTP

توضیحات

ایجاد پست

API/پست ها

ارسال کنید

نقطه پایانی API برای اضافه کردن یک پست جدید. انتظار بدن JSON:
{"author_name": "...", "text": "...", "sentiment": "..." (optional)}

ایجاد رویداد

API/رویدادها

ارسال کنید

نقطه پایانی API برای اضافه کردن یک رویداد جدید و شرکت کنندگان در آن (طرح ساده).
انتظار JSON Body: { "event_name": "...", "description": "...", "event_date": "YYYY-MM-DDTHH:MM:SSZ", "locations": [ {"name": "...", "description": "...", "latitude": 0.0, "longitude": 0.0, "address": "..."} ], "attendee_names": ["...", "..."] } "شرکت کننده_ نام": ["..." ، "..."]}

برای اینکه این قابلیت ها را از طریق MCP در دسترس نمایندگان خود قرار دهیم ، ابتدا باید توابع ساده پایتون را ایجاد کنیم که به عنوان بسته بندی در اطراف این تماس های API عمل می کنند. این توابع منطق درخواست HTTP را اداره می کنند.

👉 ابتدا ، بیایید عملکرد بسته بندی را برای ایجاد یک پست پیاده سازی کنیم. پرونده ~/instavibe-bootstrap/tools/instavibe/instavibe.py را باز کنید و #REPLACE ME CREATE POST :

def create_post(author_name: str, text: str, sentiment: str, base_url: str = BASE_URL):
    """
    Sends a POST request to the /posts endpoint to create a new post.

    Args:
        author_name (str): The name of the post's author.
        text (str): The content of the post.
        sentiment (str): The sentiment associated with the post (e.g., 'positive', 'negative', 'neutral').
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/posts"
    headers = {"Content-Type": "application/json"}
    payload = {
        "author_name": author_name,
        "text": text,
        "sentiment": sentiment
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created post. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating post: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

👉📝 در مرحله بعد ، ما عملکرد بسته بندی را برای API ایجاد رویداد ایجاد خواهیم کرد. در همان پرونده ~/instavibe-bootstrap/tools/instavibe/instavibe.py ، جایگزین #REPLACE ME CREATE EVENTS نظر با این کد:

def create_event(event_name: str, description: str, event_date: str, locations: list, attendee_names: list[str], base_url: str = BASE_URL):
    """
    Sends a POST request to the /events endpoint to create a new event registration.

    Args:
        event_name (str): The name of the event.
        description (str): The detailed description of the event.
        event_date (str): The date and time of the event (ISO 8601 format recommended, e.g., "2025-06-10T09:00:00Z").
        locations (list): A list of location dictionaries. Each dictionary should contain:
                          'name' (str), 'description' (str, optional),
                          'latitude' (float), 'longitude' (float),
                          'address' (str, optional).
        attendee_names (list[str]): A list of names of the people attending the event.
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
    url = f"{base_url}/events"
    headers = {"Content-Type": "application/json"}
    payload = {
        "event_name": event_name,
        "description": description,
        "event_date": event_date,
        "locations": locations,
        "attendee_names": attendee_names,
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
        print(f"Successfully created event registration. Status Code: {response.status_code}")
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error creating event registration: {e}")
        # Optionally re-raise the exception if the caller needs to handle it
        # raise e
        return None
    except json.JSONDecodeError:
        print(f"Error decoding JSON response from {url}. Response text: {response.text}")
        return None

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

اجرای سرور MCP

اکنون که عملکردهای پایتون را انجام می دهیم که اقدامات را انجام می دهند (فراخوانی API instavibe) ، ما باید مؤلفه سرور MCP را بسازیم. این سرور طبق استاندارد MCP این توابع را به عنوان "ابزار" در معرض نمایش قرار می دهد و به مشتریان MCP (مانند نمایندگان ما) اجازه می دهد تا آنها را کشف و فراخوانی کنند.

یک سرور MCP به طور معمول دو ویژگی اصلی را پیاده سازی می کند:

  • list_tools : مسئول اجازه دادن به مشتری برای کشف ابزارهای موجود در سرور ، ارائه ابرداده مانند نام ، توضیحات و پارامترهای مورد نیاز ، که اغلب با استفاده از طرح JSON تعریف شده است
  • call_tool : اجرای یک ابزار خاص که توسط مشتری درخواست شده است ، دریافت نام و استدلال های ابزار و انجام اقدامات مربوطه ، مانند مورد ما در تعامل با یک API را انجام می دهد.

از سرورهای MCP برای ارائه مدل های هوش مصنوعی با دسترسی به داده ها و اقدامات در دنیای واقعی استفاده می شود ، وظایفی مانند ارسال ایمیل ، ایجاد وظایف در سیستم های مدیریت پروژه ، جستجوی بانکهای اطلاعاتی یا تعامل با نرم افزارهای مختلف و خدمات وب را امکان پذیر می کند. در حالی که پیاده سازی های اولیه غالباً بر روی سرورهای محلی که از طریق ورودی/خروجی استاندارد (STDIO) برای سادگی ، به ویژه در محیط های توسعه یا "استودیو" ارتباط برقرار می کنند ، متمرکز شده است ، حرکت به سمت سرورهای از راه دور با استفاده از پروتکل هایی مانند HTTP با رویدادهای سرور (SSE) برای پذیرش گسترده تر و استفاده از شرکت ها باعث می شود.

معماری از راه دور ، با وجود لایه ارتباطی شبکه اضافه شده ، مزایای قابل توجهی را ارائه می دهد: به چندین مشتری هوش مصنوعی اجازه می دهد تا دسترسی به یک سرور واحد را به اشتراک بگذارند ، مدیریت و به روزرسانی ابزارها را متمرکز کنند ، با نگه داشتن داده های حساس و کلیدهای API در سمت سرور به جای توزیع در بسیاری از دستگاه های مشتری به طور بالقوه ، امنیت را تقویت می کنند ، و مدل AI را از ویژگی های Scarable Scaration Seachite Enternational Enternation Seachy System Enternation Seachy System Enternation استفاده می کنند. نیاز به هر نمونه هوش مصنوعی برای مدیریت ادغام مستقیم خود.

07-MCP-server.png

ما سرور MCP خود را با استفاده از رویدادهای HTTP و Server-SENT (SSE) برای ارتباطات پیاده سازی خواهیم کرد ، که برای اجرای ابزار بالقوه طولانی مدت و سناریوهای سازمانی مناسب است.

👉📝 ابتدا ، اجازه دهید نقطه انتهایی List_Tools را پیاده سازی کنیم. پرونده ~/instavibe-bootstrap/tools/instavibe/mcp_server.py را باز کنید و #REPLACE ME - LIST TOOLS با کد زیر لیست کنید. :

@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
  """MCP handler to list available tools."""
  # Convert the ADK tool's definition to MCP format
  mcp_tool_schema_event = adk_to_mcp_tool_type(event_tool)
  mcp_tool_schema_post = adk_to_mcp_tool_type(post_tool)
  print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {mcp_tool_schema_event.name} and {mcp_tool_schema_post}")
  return [mcp_tool_schema_event,mcp_tool_schema_post]

این عملکرد ابزارها را تعریف می کند (create_event ، create_post) و به مشتری های اتصال در مورد آنها می گوید.

👉📝 بعد ، نقطه پایانی call_tool را اجرا کنید ، که درخواست های اجرای واقعی مشتری را انجام می دهد. در همان ~/instavibe-bootstrap/tools/instavibe/mcp_server.py پرونده ، #REPLACE ME - CALL TOOLS .

@app.call_tool()
async def call_tool(
    name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
  """MCP handler to execute a tool call."""
  print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")

  # Look up the tool by name in our dictionary
  tool_to_call = available_tools.get(name)
  if tool_to_call:
    try:
      adk_response = await tool_to_call.run_async(
          args=arguments,
          tool_context=None, # No ADK context available here
      )
      print(f"MCP Server: ADK tool '{name}' executed successfully.")
      
      response_text = json.dumps(adk_response, indent=2)
      return [mcp_types.TextContent(type="text", text=response_text)]

    except Exception as e:
      print(f"MCP Server: Error executing ADK tool '{name}': {e}")
      # Creating a proper MCP error response might be more robust
      error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
      return [mcp_types.TextContent(type="text", text=error_text)]
  else:
      # Handle calls to unknown tools
      print(f"MCP Server: Tool '{name}' not found.")
      error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
      return [mcp_types.TextContent(type="text", text=error_text)]

این عملکرد نام و آرگومان های ابزار را دریافت می کند ، عملکرد بسته بندی پایتون مربوطه را که قبلاً تعریف کرده بودیم ، می یابد ، آن را اجرا می کند و نتیجه را برمی گرداند

👉💻 با تعریف منطق سرور MCP ، اکنون باید آن را به عنوان یک ظرف بسته بندی کنیم ، در ترمینال اسکریپت زیر را اجرا کنیم تا تصویر Docker را با استفاده از Cloud Build بسازیم:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

👉💻 و تصویر را به عنوان یک سرویس در Google Cloud Run مستقر کنید.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="INSTAVIBE_BASE_URL=${INSTAVIBE_BASE_URL}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1

👉💻 پس از اتمام استقرار با موفقیت ، سرور MCP از طریق URL عمومی در حال اجرا و در دسترس خواهد بود. ما باید این URL را ضبط کنیم تا نماینده ما (به عنوان مشتری MCP عمل کند) می داند که در آن ارتباط برقرار کند.

export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

همچنین اکنون باید بتوانید سرویس سرور MCP-Tool-Server را به عنوان "در حال اجرا" در بخش Cloud Run کنسول Google Cloud خود مشاهده کنید.

دویدن

با استفاده از سرور MCP و URL آن ، اکنون می توانیم نماینده ای را اجرا کنیم که به عنوان مشتری MCP عمل کند و از ابزارهای در معرض این سرور استفاده کند.

8. عامل تعامل پلت فرم (با استفاده از MCP)

مشتری MCP مشتری MCP مؤلفه ای است که در یک برنامه یا عامل AI ساکن است و به عنوان رابط بین مدل AI و یک یا چند سرور MCP فعالیت می کند. در اجرای ما ، این مشتری مستقیماً در نماینده ما ادغام می شود. عملکرد اصلی این مشتری برقراری ارتباط با سرورهای MCP برای کشف ابزارهای موجود از طریق عملکرد list_tools است و متعاقباً درخواست اجرای ابزارهای خاص را با استفاده از عملکرد call_tool ، عبور از آرگومان های لازم ارائه شده توسط مدل AI یا عامل ارکستر کردن تماس است.

مشتری MCP

اکنون ما نماینده ای را ایجاد می کنیم که به عنوان مشتری MCP عمل می کند. این نماینده ، که در چارچوب ADK اجرا می شود ، مسئولیت برقراری ارتباط با mcp-tool-server که ما فقط مستقر کرده ایم ، خواهد داشت.

👉 ابتدا ، ما باید تعریف عامل را تغییر دهیم تا ابزارهای پویا از سرور MCP در حال اجرا خود را بدست آوریم. در agents/platform_mcp_client/agent.py ، #REPLACE ME - FETCH TOOLS با موارد زیر:

"""Gets tools from the File System MCP Server."""
  tools =  MCPToolset(
      connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
  )

این کد از روش mcptoolset.from_server برای اتصال به MCP_SERVER_URL (که ما به عنوان یک متغیر محیطی در اوایل تنظیم کرده ایم) استفاده می کند و لیست ابزارهای موجود را بازیابی می کند.

در مرحله بعد ، ما باید به تعریف عامل ADK بگوییم تا در واقع از این ابزارهای پویا شده استفاده شود.

👉 در agents/platform_mcp_client/agent.py ، #REPLACE ME - SET TOOLs :

  tools=[tools],

👉💻 اکنون ، بیایید این عامل را به صورت محلی با استفاده از ADK Dev UI آزمایش کنیم تا ببینیم آیا می تواند به درستی به سرور MCP متصل شود و از ابزارهایی برای تعامل با برنامه Instavibe در حال اجرا استفاده کنیم.

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
sed -i "s|^\(O\?MCP_SERVER_URL\)=.*|MCP_SERVER_URL=${MCP_SERVER_URL}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
adk web

مجدداً ADK DEV UI را در مرورگر خود باز کنید (با استفاده از پیش نمایش وب Cloud Shell در پورت 8000). این بار ، در کشویی بالا سمت راست ، عامل platform_mcp_client را انتخاب کنید.

بیایید ابزار Create_Post را آزمایش کنیم. در گفتگوی چت ، درخواست زیر را وارد کنید:

Create a post saying "Y'all I just got the cutest lil void baby 😭✨ Naming him Abyss bc he's deep, mysterious, and lowkey chaotic 🔥🖤 #VoidCat #NewRoomie" I'm Julia

پست adk dev ui

عامل باید این کار را پردازش کند ، نیاز به استفاده از ابزار Create_Post ، ارتباط با سرور MCP را مشخص کند ، که به نوبه خود API Instavibe را فراخوانی می کند.

👉 مرحله تأیید: پس از تأیید عامل ، عمل را در جایی که برنامه instavibe شما در حال اجرا است باز کنید (یا آن را تازه کنید). شما باید ببینید پست جدید از "جولیا" در فید اصلی ظاهر می شود!

پست instavibe

👉💻 این اسکریپت را در یک ترمینال جداگانه اجرا کنید تا در صورت لزوم پیوند instavibe دریافت کنید:

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe

👉📝 اکنون ، بیایید ابزار Create_Event را آزمایش کنیم. درخواست چند خط زیر را در گفتگوی چت وارد کنید:

Hey, can you set up an event for Hannah and George and me, and I'm Julia? Let's call it 'Mexico City Culinary & Art Day'.
here are more info
  {"event_name": "Mexico City Culinary & Art Day",
  "description": "A vibrant day in Mexico City for Hannah and George, starting with lunch at one of the city's best taco spots in the hip Condesa neighborhood, followed by an inspiring afternoon exploring the Museo Soumaya's stunning art collection.",
  "event_date": "2025-10-17T12:00:00-06:00",
  "locations": [
    {
      "name": "El Tizoncito",
      "description": "Considered one of the original creators of tacos al pastor, El Tizoncito offers a legendary taco experience in the heart of Condesa. Their flavorful meats, house salsas, and casual vibe make it a must-visit for foodies.",
      "latitude": 19.412179,
      "longitude": -99.171308,
      "address": "Av. Tamaulipas 122, Hipódromo, Cuauhtémoc, 06100 Ciudad de México, CDMX, Mexico"
    },
    {
      "name": "Museo Soumaya",
      "description": "An architectural icon in Mexico City, Museo Soumaya houses over 66,000 works of art, including pieces by Rodin, Dalí, and Rivera. The striking silver structure is a cultural landmark and a visual feast inside and out.",
      "latitude": 19.440056,
      "longitude": -99.204281,
      "address": "Plaza Carso, Blvd. Miguel de Cervantes Saavedra 303, Granada, Miguel Hidalgo, 11529 Ciudad de México, CDMX, Mexico"
    }
  ],
  "attendee_names": ["Hannah", "George", Julia],
}

مجدداً عامل باید از طریق سرور MCP از ابزار مناسب استفاده کند. در برگه Events ، با کلیک بر روی رویداد Indiviual ، اثری از گام به گام اجرای آن را مشاهده خواهید کرد.

رویداد adk dev ui

step مرحله تأیید: به برنامه instavibe در حال اجرا خود برگردید و به بخش "رویدادها" (یا معادل آن) بروید. اکنون باید رویداد تازه ایجاد شده "روز آشپزی و هنر مکزیک سیتی" را ذکر کنید.

رویداد instavibe

این با موفقیت نشان می دهد که چگونه MCP به نماینده ما اجازه می دهد تا از ابزارهای خارجی (در این حالت ، API های Instavibe) به روشی استاندارد استفاده کند.

پس از تأیید هر دو عمل ، به ترمینال Cloud Shell خود برگردید و Ctrl+C را فشار دهید تا ADK Dev UI متوقف شود.

9. نماینده گردش کار و چند سازمانی در ADK

مأمورین ما تاکنون می توانند برنامه ریزی ها را برنامه ریزی کرده و با سیستم عامل تعامل داشته باشند. با این حال ، برنامه ریزی واقعاً شخصی نیاز به درک حلقه اجتماعی کاربر دارد. برای کاربران شلوغ که ممکن است از نزدیک فعالیت های دوستان خود را دنبال نکنند ، جمع آوری این زمینه به صورت دستی دشوار است. برای پرداختن به این موضوع ، ما یک عامل پروفایل اجتماعی ایجاد خواهیم کرد که از پایگاه داده نمودار Spanner ما برای تجزیه و تحلیل فعالیت ها و علایق دوستانه استفاده می کند و پیشنهادات متناسب تری را امکان پذیر می کند.

نماینده پروفایل اجتماعی

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

tlate توابع پایتون زیر را به انتهای پرونده ~/instavibe-bootstrap/agents/social/instavibe.py اضافه کنید:

def get_person_attended_events(person_id: str)-> list[dict]:
    """
    Fetches events attended by a specific person using Graph Query.
    Args:
       person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person)-[att:Attended]->(e:Event)
        WHERE p.person_id = @person_id
        RETURN e.event_id, e.name, e.event_date, att.attendance_time
        ORDER BY e.event_date DESC
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["event_id", "name", "event_date", "attendance_time"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None: return None

    for event in results:
        if isinstance(event.get('event_date'), datetime):
            event['event_date'] = event['event_date'].isoformat()
        if isinstance(event.get('attendance_time'), datetime):
            event['attendance_time'] = event['attendance_time'].isoformat()
    return results

def get_person_id_by_name( name: str) -> str:
    """
    Fetches the person_id for a given name using SQL.

    Args:
       name (str): The name of the person to search for.

    Returns:
        str or None: The person_id if found, otherwise None.
                     Returns the ID of the *first* match if names are duplicated.
    """
    if not db_instance: return None

    sql = """
        SELECT person_id
        FROM Person
        WHERE name = @name
        LIMIT 1 -- Return only the first match in case of duplicate names
    """
    params = {"name": name}
    param_types_map = {"name": param_types.STRING}
    fields = ["person_id"]

    # Use the standard SQL query helper
    results = run_sql_query( sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results: # Check if the list is not empty
        return results[0].get('person_id') # Return the ID from the first dictionary
    else:
        return None # Name not found


def get_person_posts( person_id: str)-> list[dict]:
    """
    Fetches posts written by a specific person using Graph Query.

    Args:
        person_id (str): The ID of the person whose posts to fetch.


    Returns:
        list[dict] or None: List of post dictionaries with ISO date strings,
                           or None if an error occurs.
    """
    if not db_instance: return None

    # Graph Query: Find the specific Person node, follow 'Wrote' edge to Post nodes
    graph_sql = """
        Graph SocialGraph
        MATCH (author:Person)-[w:Wrote]->(post:Post)
        WHERE author.person_id = @person_id
        RETURN post.post_id, post.author_id, post.text, post.sentiment, post.post_timestamp, author.name AS author_name
        ORDER BY post.post_timestamp DESC
    """
    # Parameters now include person_id and limit
    params = {
        "person_id": person_id
    }
    param_types_map = {
        "person_id": param_types.STRING
    }
    # Fields returned remain the same
    fields = ["post_id", "author_id", "text", "sentiment", "post_timestamp", "author_name"]

    results = run_graph_query(graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    if results is None:
        return None

    # Convert datetime objects to ISO format strings
    for post in results:
        if isinstance(post.get('post_timestamp'), datetime):
            post['post_timestamp'] = post['post_timestamp'].isoformat()

    return results


def get_person_friends( person_id: str)-> list[dict]:
    """
    Fetches friends for a specific person using Graph Query.
    Args:
        person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
    if not db_instance: return None

    graph_sql = """
        Graph SocialGraph
        MATCH (p:Person {person_id: @person_id})-[f:Friendship]-(friend:Person)
        RETURN DISTINCT friend.person_id, friend.name
        ORDER BY friend.name
    """
    params = {"person_id": person_id}
    param_types_map = {"person_id": param_types.STRING}
    fields = ["person_id", "name"]

    results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

    return results

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

در ADK Google ، یک عامل گردش کار خود وظایف را انجام نمی دهد بلکه عوامل دیگری به نام های فرعی را ارکستر می کند. این امر امکان طراحی مدولار را فراهم می کند و مشکلات پیچیده را به اجزای تخصصی تقسیم می کند. ADK انواع گردش کار داخلی مانند

  • متوالی (گام به گام)
  • موازی (اجرای همزمان)
  • و حلقه (اجرای مکرر)

نماینده پروفایل اجتماعی

برای کار پروفایل اجتماعی ما ، طراحی ما از یک عامل حلقه برای ایجاد یک گردش کار تکراری استفاده می کند. هدف این است که یک نفر را به طور همزمان پردازش کنید: profile_agent داده ها را جمع می کند ، summary_agent تجزیه و تحلیل را به روز می کند و check_agent تعیین می کند که آیا باید دوباره حلقه کنیم.

بیایید فرعی های مورد نیاز برای این گردش کار را تعریف کنیم.

👉📝 in ~/instavibe-bootstrap/agents/social/agent.py ، #REPLACE FOR profile_agent با موارد زیر جایگزین کنید:

profile_agent = LlmAgent(
    name="profile_agent",
    model="gemini-2.5-flash",
    description=(
        "Agent to answer questions about the this person social profile. Provide the person's profile using their name, make sure to fetch the id before getting other data."
    ),
    instruction=(
        "You are a helpful agent to answer questions about the this person social profile. You'll be given a list of names, provide the person's profile using their name, make sure to fetch the id before getting other data. Get one person at a time, start with the first one on the list, and skip if already provided. return this person's result"
    ),
    tools=[get_person_posts,get_person_friends,get_person_id_by_name,get_person_attended_events],
)

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

👉📝 در همان ~/instavibe-bootstrap/agents/social/agent.py ، #REPLACE FOR summary_agent با موارد زیر جایگزین کنید:

summary_agent = LlmAgent(
    name="summary_agent",
    model="gemini-2.5-flash",
    description=(
        "Generate a comprehensive social summary as a single, cohesive paragraph. This summary should cover the activities, posts, friend networks, and event participation of one or more individuals. If multiple profiles are analyzed, the paragraph must also identify and integrate any common ground found between them."
    ),
    instruction=(
        """
        Your primary task is to synthesize social profile information into a single, comprehensive paragraph.

            **Input Scope & Default Behavior:**
            *   If specific individuals are named by the user, focus your analysis on them.
            *   **If no individuals are specified, or if the request is general, assume the user wants an analysis of *all relevant profiles available in the current dataset/context*.**

            **For each profile (whether specified or determined by default), you must analyze:**

            1.  **Post Analysis:**
                *   Systematically review their posts (e.g., content, topics, frequency, engagement).
                *   Identify recurring themes, primary interests, and expressed sentiments.

            2.  **Friendship Relationship Analysis:**
                *   Examine their connections/friends list.
                *   Identify key relationships, mutual friends (especially if comparing multiple profiles), and the general structure of their social network.

            3.  **Event Participation Analysis:**
                *   Investigate their past (and if available, upcoming) event participation.
                *   Note the types of events, frequency of attendance, and any notable roles (e.g., organizer, speaker).

            **Output Generation (Single Paragraph):**

            *   **Your entire output must be a single, cohesive summary paragraph.**
                *   **If analyzing a single profile:** This paragraph will detail their activities, interests, and social connections based on the post, friend, and event analysis.
                *   **If analyzing multiple profiles:** This paragraph will synthesize the key findings regarding posts, friends, and events for each individual. Crucially, it must then seamlessly integrate or conclude with an identification and description of the common ground found between them (e.g., shared interests from posts, overlapping event attendance, mutual friends). The aim is a unified narrative within this single paragraph.

            **Key Considerations:**
            *   Base your summary strictly on the available data.
            *   If data for a specific category (posts, friends, events) is missing or sparse for a profile, you may briefly acknowledge this within the narrative if relevant.
                """
        ),
    output_key="summary"
)

ما به روشی نیاز داریم تا مشخص کنیم چه زمانی حلقه باید متوقف شود (یعنی همه پروفایل های درخواست شده خلاصه شده اند)

👉📝 در همان ~/instavibe-bootstrap/agents/social/agent.py ، #REPLACE FOR check_agent جایگزین کنید:

check_agent = LlmAgent(
    name="check_agent",
    model="gemini-2.5-flash",
    description=(
        "Check if everyone's social profile are summarized and has been generated. Output 'completed' or 'pending'."
    ),
    output_key="summary_status"
)

ما یک چک برنامه نویسی ساده (CheckCondition) اضافه می کنیم که صریحاً به summary_status ذخیره شده در حالت نگاه می کند ، که توسط check_agent بازگردانده می شوند و به عامل حلقه می گویند که آیا ادامه (Escalate = False) یا توقف (Escalate = True).

👉📝 در همان ~/instavibe-bootstrap/agents/social/agent.py ، #REPLACE FOR CheckCondition واقع در بالای پرونده با موارد زیر جایگزین کنید:

class CheckCondition(BaseAgent):
    async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
        #log.info(f"Checking status: {ctx.session.state.get("summary_status", "fail")}")
        log.info(f"Summary: {ctx.session.state.get("summary")}")

        status = ctx.session.state.get("summary_status", "fail").strip()
        is_done = (status == "completed")

        yield Event(author=self.name, actions=EventActions(escalate=is_done))

حالت و تماس تلفنی برای نتایج حلقه

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

در سناریوی ما ، همانطور که عامل حلقه تکرار می شود ، summary_agent و check_agent خروجی های آنها (خلاصه و خلاصه_ستاتوس) را در حالت نماینده ذخیره می کند. این اجازه می دهد تا اطلاعات در طول تکرار ادامه یابد. با این حال ، خود عامل حلقه به طور خودکار خلاصه نهایی را از حالت خارج نمی کند.

نماینده پروفایل اجتماعی

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

ما after_agent_callback استفاده می کنیم که بعد از اتمام حلقه اجرا می شود (زیرا بررسی CheckCondition افزایش یافته است). این پاسخ به تماس modify_output_after_agent خلاصه نهایی را از حالت بازیابی می کند و آن را به عنوان پیام خروجی نهایی نماینده قالب می کند.

تماس بگیرید

👉📝 در همان ~/instavibe-bootstrap/agents/social/agent.py ، #REPLACE FOR modify_output_after_agent جایگزین کنید:

def modify_output_after_agent(callback_context: CallbackContext) -> Optional[types.Content]:

    agent_name = callback_context.agent_name
    invocation_id = callback_context.invocation_id
    current_state = callback_context.state.to_dict()
    current_user_content = callback_context.user_content
    print(f"[Callback] Exiting agent: {agent_name} (Inv: {invocation_id})")
    print(f"[Callback] Current summary_status: {current_state.get("summary_status")}")
    print(f"[Callback] Current Content: {current_user_content}")

    status = current_state.get("summary_status").strip()
    is_done = (status == "completed")
    # Retrieve the final summary from the state

    final_summary = current_state.get("summary")
    print(f"[Callback] final_summary: {final_summary}")
    if final_summary and is_done and isinstance(final_summary, str):
        log.info(f"[Callback] Found final summary, constructing output Content.")
        # Construct the final output Content object to be sent back
        return types.Content(role="model", parts=[types.Part(text=final_summary.strip())])
    else:
        log.warning("[Callback] No final summary found in state or it's not a string.")
        # Optionally return a default message or None if no summary was generated
        return None

تعریف عامل حلقه ریشه

سرانجام ، ما حلقه اصلی را تعریف می کنیم. این برنامه ها را به ترتیب در هر تکرار حلقه (profile_agent -> summary_agent -> check_agent -> بررسی) ، ارکستر می کند. این دنباله را تا زمان MAX_ITERATIONS یا تا زمان تکمیل سیگنال های CheckCondition تکرار می کند. After_Agent_Callback تضمین می کند که خلاصه نهایی برگردانده شود.

👉📝 در همان ~/instavibe-bootstrap/agents/social/agent.py ، #REPLACE FOR root_agent جایگزین کنید:

root_agent = LoopAgent(
    name="InteractivePipeline",
    sub_agents=[
        profile_agent,
        summary_agent,
        check_agent,
        CheckCondition(name="Checker")
    ],
    description="Find everyone's social profile on events, post and friends",
    max_iterations=10,
    after_agent_callback=modify_output_after_agent
)

بیایید این گردش کار چند عامل را با استفاده از ADK Dev UI آزمایش کنیم.

👉💻 سرور وب ADK را راه اندازی کنید:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/social/.env
adk web

ADK DEV UI (پورت 8000 را از طریق پیش نمایش وب) باز کنید. در منوی Dropdown Agent (بالا سمت راست) ، نماینده اجتماعی را انتخاب کنید.

👉 اکنون ، این وظیفه را به شما ارائه دهید تا چندین نفر را مشخص کنید. در گفتگوی چت ، وارد کنید:

Tell me about Mike and Bob

بعد از پاسخ عامل (که ممکن است به دلیل حلقه های حلقه ای و چند LLM کمی بیشتر طول بکشد) ، فقط به خروجی چت نهایی نگاه نکنید. به برگه Events در صفحه سمت چپ ADK Dev UI بروید.

مرحله تأیید: در برگه رویدادها ، اثری دقیق و گام به گام اجرای آن را مشاهده خواهید کرد. 09-01-adk-dev-ui.png

پس از مشاهده چگونگی فراخوانی عامل از هر نماینده فرعی ، جایی که انتظار دارید جریان از profile_agent -> summary_agent -> check_agent ، checker در هر تکرار ، عبور کند. اما در عمل ، با این حال ، ما "بهینه سازی" قدرتمند عامل را در عمل می بینیم.

از آنجا که مدل زیرین کل درخواست را می بیند (به عنوان مثال ، "پروفایل مایک و باب") ، اغلب کارآمدترین مسیر را انتخاب می کند ، و تمام داده های مورد نیاز را در یک چرخش واحد و تلفیقی جمع می کند تا اینکه چندین بار تکرار شود. برای هر مرحله می توانید ورودی ها و خروجی ها و حالت ها را مشاهده کنید ، از جمله تماس های ابزار ساخته شده توسط profile_agent

09-02-ui-graph.png

و به روزرسانی های وضعیت از check_agent و چک. 09-03-ui-state.png

این اثری بصری برای درک و اشکال زدایی در نحوه عملکرد گردش کار چند عامل تا زمان خلاصه نهایی و بازگشت توسط پاسخ به تماس ، بسیار ارزشمند است.

پس از بررسی پاسخ چت و ردیابی رویداد ، به ترمینال Cloud Shell برگردید و Ctrl+C را فشار دهید تا ADK Dev UI متوقف شود.

10. ارتباط عامل به عامل (A2A)

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

پروتکل A2A یک استاندارد باز است که به طور خاص برای برقراری ارتباط قابل تعامل بین عوامل AI طراحی شده است. در حالی که MCP بر تعامل عامل به ابزار تمرکز دارد ، A2A بر تعامل عامل به عامل متمرکز است. این امکان را به نمایندگان می دهد:

  • کشف : سایر عوامل را پیدا کنید و توانایی های آنها را از طریق کارتهای عامل استاندارد یاد بگیرید.
  • ارتباط برقرار کنید : پیام ها و داده ها را با اطمینان تبادل کنید.
  • همکاری : وظایف نماینده و اقدامات هماهنگی برای دستیابی به اهداف پیچیده.

پروتکل A2A این ارتباطات را از طریق مکانیسم هایی مانند "کارتهای عامل" تسهیل می کند که نمایندگان می توانند از آنها برای تبلیغ توانایی ها و اطلاعات اتصال خود استفاده کنند.

10-05-AGENT-AGENT

A2A از استانداردهای وب آشنا (HTTP ، SSE ، JSON-RPC) استفاده می کند و اغلب از یک مدل سرور مشتری استفاده می کند که در آن یک عامل (مشتری) وظایف را به دیگری (عامل/سرور از راه دور) ارسال می کند. این استاندارد سازی برای ساختن سیستم های ماژولار و مقیاس پذیر مهم است که عوامل توسعه یافته به طور مستقل می توانند با هم کار کنند.

فعال کردن A2A برای عوامل instavibe

برای اینکه برنامه ریز موجود ، تعامل پلتفرم و عوامل اجتماعی در دسترس از طریق A2A در دسترس سایر عوامل قرار بگیریم ، باید هرکدام را با یک مؤلفه سرور A2A ببندیم. این سرور:

  • یک کارت عامل را در معرض دید قرار دهید : توضیحات استاندارد از قابلیت های عامل را از طریق نقطه پایانی HTTP ارائه دهید.
  • گوش دادن به وظایف (پیام های درخواست) : درخواست های کار ورودی را از سایر عوامل (مشتری A2A) مطابق پروتکل A2A بپذیرید.
  • مدیریت وظیفه (پیام های درخواست) اجرای : وظایف دریافت شده را به منطق عامل اصلی ADK برای پردازش تحویل دهید.

نماینده برنامه ریز (A2A فعال شده)

همه جانبه

بیایید با اضافه کردن لایه سرور A2A به نماینده برنامه ریز ما شروع کنیم.

منطق راه اندازی سرور A2A را تعریف کنید. این کد AgentCard (توضیحات عمومی نماینده) را تعریف می کند ، A2ASERVER را پیکربندی می کند و آن را شروع می کند و آن را به PlatformAgentExpertor پیوند می دهد.

👉📝 کد زیر را به انتهای ~/instavibe-bootstrap/agents/planner/a2a_server.py اضافه کنید:

class PlannerAgent:
    """An agent to help user planning a event with its desire location."""
    SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

    def __init__(self):
        self._agent = self._build_agent()
        self.runner = Runner(
            app_name=self._agent.name,
            agent=self._agent,
            artifact_service=InMemoryArtifactService(),
            session_service=InMemorySessionService(),
            memory_service=InMemoryMemoryService(),
        )
        capabilities = AgentCapabilities(streaming=True)
        skill = AgentSkill(
            id="event_planner",
            name="Event planner",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            tags=["instavibe"],
            examples=["What about Bostona MA this weekend?"],
        )
        self.agent_card = AgentCard(
            name="Event Planner Agent",
            description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )

    def get_processing_message(self) -> str:
        return "Processing the planning request..."

    def _build_agent(self) -> LlmAgent:
        """Builds the LLM agent for the night out planning agent."""
        return agent.root_agent


if __name__ == '__main__':
    try:
        plannerAgent = PlannerAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlannerAgentExecutor(plannerAgent.runner,plannerAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=plannerAgent.agent_card,
            http_handler=request_handler,
        )
        logger.info(f"Attempting to start server with Agent Card: {plannerAgent.agent_card.name}")
        logger.info(f"Server object created: {server}")

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

👉💻 بیایید سریع آزمایش کنیم که آیا سرور A2A به درستی محلی شروع می شود و کارت عامل خود را سرو می کند. دستور زیر را در اولین ترمینال خود اجرا کنید:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server

👉 اکنون ، یک پنجره ترمینال دیگر را باز کنید. (روی صفحه + در پنل ترمینال کلیک کنید) دو پایانه

👉💻 از CURL برای درخواست کارت عامل از سرور محلی در حال اجرا استفاده کنید:

curl http://localhost:10003/.well-known/agent.json | jq

شما باید نمایندگی JSON از AgentCard را که تعریف کرده ایم ، ببینید ، تأیید می کند که سرور در حال اجرا است و تبلیغات برنامه ریز را تبلیغ می کند.

10-02-planner-a2a.png

به اولین ترمینال (جایی که سرور در حال اجرا است) برگردید و Ctrl+C را فشار دهید تا جلوی آن را بگیرید.

👉💻 با اضافه شدن منطق سرور A2A ، اکنون می توانیم تصویر کانتینر را بسازیم.

نماینده برنامه ریز را بسازید و مستقر کنید

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"

echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
  --config=cloudbuild-build.yaml \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}

echo "Image built and pushed to: ${IMAGE_PATH}"

👉💻 و نماینده برنامه ریز ما را در Cloud Run مستقر کنید.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"


gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --set-env-vars="A2A_HOST=0.0.0.0" \
  --set-env-vars="A2A_PORT=8080" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
  --allow-unauthenticated \
  --project=${PROJECT_ID} \
  --min-instances=1

بیایید تأیید کنیم که سرویس مستقر در حال اجرا و در خدمت کارت عامل خود به درستی از ابر با استفاده از بازرس A2A است.

👉 از نماد پیش نمایش وب در نوار ابزار Cloud Shell ، Port Change را انتخاب کنید. پورت را روی 8081 تنظیم کرده و روی "تغییر و پیش نمایش" کلیک کنید. یک برگه مرورگر جدید با رابط بازرس A2A باز خواهد شد.

10-08-web-preview.png

👉💻 در ترمینال ، URL نماینده برنامه ریز مستقر خود را دریافت کنید:

export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}

url خروجی را کپی کنید.

👉 در بازرس A2A UI ، URL را در قسمت URL عامل قرار دهید و روی Connect کلیک کنید.

👀 جزئیات کارت عامل و JSON باید در برگه Agent Card ظاهر شود و یک اتصال موفق را تأیید کند.

10-03-planner-a2a.png

on بر روی برگه چت در بازرس A2A کلیک کنید. این جایی است که می توانید مستقیماً با Agen مستقر خود در تعامل باشید ، برای آزمایش قابلیت برنامه ریزی خود برای آن پیام ارسال کنید. به عنوان مثال:

Plan something for me in Boston MA this weekend, and I enjoy classical music

👀 برای بازرسی از ارتباطات خام ، روی حباب پیام خود و سپس بر روی حباب پاسخ عامل در پنجره گپ کلیک کنید. با کلیک بر روی هر یک ، پیام کامل JSON-RPC 2.0 ارسال شده یا دریافت شده را نمایش می دهد ، که برای اشکال زدایی بسیار ارزشمند است.

بیایید برگه بازرس A2A را مفید نگه داریم. آن را ببندید! ما در یک لحظه دوباره از آن استفاده خواهیم کرد تا دو عامل دیگر خود را آزمایش کنیم.

10-06-A2A-inspector.png

عامل تعامل سکو (A2A فعال شده)

همه جانباز

در مرحله بعد ، ما فرایند عامل تعامل پلتفرم (موردی که از MCP استفاده می کند) را تکرار خواهیم کرد.

set تنظیم سرور A2A ، از جمله AgentCard منحصر به فرد آن ، در پایان ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py : تعریف کنید:

class PlatformAgent:
  """An agent that post event and post to instavibe."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
            id="instavibe_posting",
            name="Post social post and events on instavibe",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            tags=["instavibe"],
            examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
        )
    self.agent_card = AgentCard(
            name="Instavibe Posting Agent",
            description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
            url=f"{PUBLIC_URL}",
            version="1.0.0",
            defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
            capabilities=capabilities,
            skills=[skill],
        )


  def get_processing_message(self) -> str:
      return "Processing the social post and event request..."

  def _build_agent(self) -> LlmAgent:
    """Builds the LLM agent for the Processing the social post and event request."""
    return agent.root_agent


if __name__ == '__main__':
    try:
        platformAgent = PlatformAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=platformAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

نماینده اجتماعی (A2A فعال شده)

همه جانبه اجتماعی

سرانجام ، بیایید A2A را برای نماینده پروفایل اجتماعی خود فعال کنیم.

set تنظیم سرور A2A و ​​AgentCard را در پایان ~/instavibe-bootstrap/agents/social/a2a_server.py تعریف کنید:

class SocialAgent:
  """An agent that handles social profile analysis."""

  SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

  def __init__(self):
    self._agent = self._build_agent()
    self.runner = Runner(
        app_name=self._agent.name,
        agent=self._agent,
        artifact_service=InMemoryArtifactService(),
        session_service=InMemorySessionService(),
        memory_service=InMemoryMemoryService(),
    )
    capabilities = AgentCapabilities(streaming=True)
    skill = AgentSkill(
                id="social_profile_analysis",
                name="Analyze Instavibe social profile",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                tags=["instavibe"],
                examples=["Can you tell me about Bob and Alice?"],
    )
    self.agent_card = AgentCard(
                name="Social Profile Agent",
                description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
                url=f"{PUBLIC_URL}",
                version="1.0.0",
                defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
                defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
                capabilities=capabilities,
                skills=[skill],
    )

  def get_processing_message(self) -> str:
      return "Processing the social profile analysis request..."

  def _build_agent(self) -> LoopAgent:
    """Builds the LLM agent for the social profile analysis agent."""
    return agent.root_agent

if __name__ == '__main__':
    try:
        socialAgent = SocialAgent()

        request_handler = DefaultRequestHandler(
            agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
            task_store=InMemoryTaskStore(),
        )

        server = A2AStarletteApplication(
            agent_card=socialAgent.agent_card,
            http_handler=request_handler,
        )

        uvicorn.run(server.build(), host='0.0.0.0', port=port)
    except Exception as e:
        logger.error(f"An error occurred during server startup: {e}")
        exit(1)

تعامل پلت فرم و عوامل اجتماعی را بسازید و مستقر کنید

این عوامل نیاز به دسترسی به Spanner دارند ، بنابراین از متغیرهای محیط زیست SPANNER_INSTANCE_ID ، SPANNER_DATABASE_ID و MCP_SERVER_URL اطمینان حاصل کنید که در طول استقرار به درستی منتقل می شوند.

👉💻 ساخت و استقرار به ابر اجرا با ابر ساخت :

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse


gcloud builds submit . \
  --config=cloudbuild.yaml \
  --project="${PROJECT_ID}" \
  --region="${REGION}" \
  --substitutions=\
_PROJECT_ID="${PROJECT_ID}",\
_PROJECT_NUMBER="${PROJECT_NUMBER}",\
_REGION="${REGION}",\
_REPO_NAME="${REPO_NAME}",\
_SPANNER_INSTANCE_ID="${SPANNER_INSTANCE_ID}",\
_SPANNER_DATABASE_ID="${SPANNER_DATABASE_ID}",\
_MCP_SERVER_URL="${MCP_SERVER_URL}"

👉💻 در ترمینال ، URL نماینده سکوی مستقر خود را دریافت کنید:

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
echo $PLATFORM_MPC_CLIENT_URL

url خروجی را کپی کنید.

👉 در بازرس A2A UI ، URL را در قسمت URL عامل قرار دهید و روی Connect کلیک کنید.

👀 جزئیات کارت عامل و JSON باید در برگه Agent Card ظاهر شود و یک اتصال موفق را تأیید کند.

10-05-platform-a2a.png

on بر روی برگه چت در بازرس A2A کلیک کنید. اینجاست که می توانید مستقیماً با Agen مستقر در تعامل خود ارتباط برقرار کنید ، برای ایجاد پیام عامل در ایجاد پست ها به آن ارسال کنید:

Create a post for me, the post says 'Paws, purrs, and ocean views 🐾☕🌊. Spent my morning at the Morning Seaside Cat Café, where every sip comes with a side of snuggles and sea breeze.' and make it positive, and I'm Oscar.

👀 برای بازرسی از ارتباطات خام ، روی حباب پیام خود و سپس بر روی حباب پاسخ عامل در پنجره گپ کلیک کنید. با کلیک بر روی هر یک ، پیام کامل JSON-RPC 2.0 ارسال شده یا دریافت شده را نمایش می دهد ، که برای اشکال زدایی بسیار ارزشمند است.

👉💻 در ترمینال ، URL نماینده اجتماعی مستقر خود را دریافت کنید:

export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL

url خروجی را کپی کنید.

👉 در بازرس A2A UI ، URL را در قسمت URL عامل قرار دهید و روی Connect کلیک کنید.

👀 جزئیات کارت عامل و JSON باید در برگه Agent Card ظاهر شود و یک اتصال موفق را تأیید کند.

10-04-social-a2a.png

on بر روی برگه چت در بازرس A2A کلیک کنید. اینجاست که می توانید مستقیماً با Agen مستقر در تعامل خود ارتباط برقرار کنید ، برای تجزیه و تحلیل پروفایل های کاربر از پایگاه داده خود برای آن پیام ارسال کنید:

Can you tell me about both Ian and Kevin's profile, what are their common interests?

👀 برای بازرسی از ارتباطات خام ، روی حباب پیام خود و سپس بر روی حباب پاسخ عامل در پنجره گپ کلیک کنید. با کلیک بر روی هر یک ، پیام کامل JSON-RPC 2.0 ارسال شده یا دریافت شده را نمایش می دهد ، که برای اشکال زدایی بسیار ارزشمند است.

👉 عالی ، ما بازرسی از همه نمایندگان خود را به پایان رسانده ایم. اکنون می توانید برگه بازرس A2A را ببندید.

11. نماینده ارکستر (مشتری A2A)

اکنون ما سه عامل تخصصی (برنامه ریز ، پلتفرم ، اجتماعی) داریم که به عنوان خدمات مستقل و فعال A2A در Cloud Run اجرا می شوند. قطعه آخر عامل ارکستر است. این نماینده به عنوان هماهنگ کننده مرکزی یا مشتری A2A عمل خواهد کرد. این درخواست های کاربر را دریافت می کند ، می فهمد کدام عامل از راه دور (ها) برای انجام درخواست (به طور بالقوه به ترتیب) مورد نیاز هستند و سپس از پروتکل A2A برای واگذاری وظایف به آن عوامل از راه دور استفاده می کنند. برای این کارگاه ، ما با استفاده از ADK Dev UI ، عامل Orchestrator را بصورت محلی اجرا خواهیم کرد.

اظهارات همه نماینده

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

👉📝 in ~/instavibe-bootstrap/agents/orchestrate/agent.py ، #REPLACE ME REG AGENT CARD با:

async with httpx.AsyncClient(timeout=30) as client:
            for i, address in enumerate(REMOTE_AGENT_ADDRESSES):
                log.info(f"--- STEP 3.{i}: Attempting connection to: {address} ---")
                try:
                    card_resolver = A2ACardResolver(client, address)
                    card = await card_resolver.get_agent_card()
                    
                    remote_connection = RemoteAgentConnections(agent_card=card, agent_url=address)
                    self.remote_agent_connections[card.name] = remote_connection
                    self.cards[card.name] = card
                    log.info(f"--- STEP 5.{i}: Successfully stored connection for {card.name} ---")

                except Exception as e:
                    log.error(f"--- CRITICAL FAILURE at STEP 4.{i} for address: {address} ---")
                    log.error(f"--- The hidden exception type is: {type(e).__name__} ---")
                    log.error(f"--- Full exception details and traceback: ---", exc_info=True)

در مرحله بعد ، ابزار خود را برای عامل ارکستور در ADK تعریف کنید.

  • send_message (عملکرد A2A برای واگذاری کار).

👉📝 جایگزین #REPLACE ME CREATE AGENT در ~/instavibe-bootstrap/agents/orchestrate/agent.py با:

def create_agent(self) -> Agent:
        """Synchronously creates the ADK Agent object."""
        return Agent(
            model="gemini-2.5-flash",
            name="orchestrate_agent",
            instruction=self.root_instruction,
            before_agent_callback=self.before_agent_callback,
            description=("Orchestrates tasks for child agents."),
            tools=[self.send_message], 
        )

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

👉📝 #REPLACE ME INSTRUCTIONS در ~/instavibe-bootstrap/agents/orchestrate/agent.py با این روش تولید دستورالعمل جایگزین کنید:

def root_instruction(self, context: ReadonlyContext) -> str:
        current_agent = self.check_active_agent(context)
        return f"""
                You are an expert AI Orchestrator. Your primary responsibility is to intelligently interpret user requests, break them down into a logical plan of discrete actions, and delegate each action to the most appropriate specialized remote agent using the send_message function. You do not perform the tasks yourself but manage their assignment, sequence, and critically, their outcomes.
                    **Core Directives & Decision Making:**

                    *   **Understand User Intent & Complexity:**
                        *   Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.
                        *   Identify if the request requires a single agent or a sequence of actions from multiple agents. For example, "Analyze John Doe's profile and then create a positive post about his recent event attendance" would require two agents in sequence.

                    *   **Task Planning & Sequencing (for Multi-Step Requests):**
                        *   Before delegating, outline the clear sequence of agent tasks.
                        *   Identify dependencies. If Task B requires output from Task A, execute them sequentially. If tasks are independent (like creating a post and then creating an event), execute them one after the other as separate delegations.
                        *   Agent Reusability: An agent's completion of one task does not make it unavailable. If a user's plan involves multiple, distinct actions that fall under the same agent's expertise (e.g., create a post, then create an event), you must call that same agent again for the subsequent task.

                    *   **Task Delegation & Management (using `send_message`):**
                        *   **Delegation:** Use `send_message` to assign actionable tasks to the selected remote agent. Your `send_message` call MUST include:
                            *   The `remote_agent_name` you've selected.
                            *   The `user_request` or all necessary parameters extracted from the user's input, formatted in a way the target agent will understand.
                        *   **Contextual Awareness for Remote Agents:** If a remote agent repeatedly requests user confirmation or seems to lack context, assume it lacks access to the full conversation history. In such cases, enrich your `send_message` with all necessary contextual information relevant to that specific agent from the conversation history.
                        *   **Sequential Task Execution:**
                            *   After a preceding task completes (indicated by the agent's response or a success signal), gather any necessary output from it.
                            *   Then, use `send_message` for the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.
                        *   **Active Agent Prioritization:** If an active agent is already engaged and the user's request is related to its current task, route subsequent related requests directly to that agent by providing updated context via `send_message`.
                    
                    
                    **Critical Success Verification:**

                    *   You **MUST** wait for the tool_output after every send_message call before taking any further action.
                    *   Your decision to proceed to the next task in a sequence **MUST** be based entirely on a confirmation of success from the tool_output of the previous task.
                    *   If a tool call fails, returns an error, or the tool_output is ambiguous, you MUST STOP the sequence. Your next action is to report the exact failure or ambiguity to the user.
                    *   DO NOT assume a task was successful. Do not invent success messages like "The event has been created." Only state that a task is complete if the tool's response explicitly says so.
                    
                    **Communication with User:**

                    *   **Transparent Communication:** Always present the complete and detailed response from the remote agent to the user. Do not summarize or filter unless explicitly instructed.
                    *   When you delegate a task (or the first task in a sequence), clearly inform the user which remote agent is handling it.
                    *   For multi-step requests, you can optionally inform the user of the planned sequence (e.g., "Okay, first I'll ask the 'Social Profile Agent' to analyze the profile, and then I'll have the 'Instavibe Posting Agent' create the post.").
                    *   If waiting for a task in a sequence to complete, you can inform the user (e.g., "The 'Social Profile Agent' is currently processing. I'll proceed with the post once that's done.").
                    *   **User Confirmation Relay:** If a remote agent asks for confirmation, and the user has not already provided it, just make up something.
                    *   If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, just make up something.

                    **Important Reminders:**

                    *   **Autonomous Agent Engagement:** Never seek user permission before engaging with remote agents. If multiple agents are required to fulfill a request, connect with them directly without requesting user preference or confirmation.
                    *   **Focused Information Sharing:** Provide remote agents with only relevant contextual information. Avoid extraneous details that are not directly pertinent to their task.
                    *   **No Redundant Confirmations:** Do not ask remote agents for confirmation of information or actions they have already processed or committed to.
                    *   **Tool Reliance:** Strictly rely on your available tools, primarily `send_message`, to address user requests. Do not generate responses based on assumptions. If information is insufficient, request clarification from the user.
                    *   **Prioritize Recent Interaction:** Focus primarily on the most recent parts of the conversation when processing requests, while maintaining awareness of the overall goal for multi-step tasks.
                    *   Always prioritize selecting the correct agent(s) based on their documented purpose.
                    *   Ensure all information required by the chosen remote agent is included in the `send_message` call, including outputs from previous agents if it's a sequential task.

                    Agents:
                    {self.agents}

                    Current agent: {current_agent['active_agent']}`
                """

آزمایش ارکستر و سیستم کامل A2A

حال ، بیایید کل سیستم را آزمایش کنیم. ما ارکستراتور را به صورت محلی با استفاده از ADK Dev UI اجرا خواهیم کرد و با برنامه ریز ، پلتفرم و عوامل اجتماعی که از راه دور در Cloud Run اجرا می شوند ، ارتباط برقرار خواهد کرد.

👉💻 ابتدا ، اطمینان حاصل کنید که متغیر محیط REMOTE_AGENT_ADDRESSES شامل URL های جدا شده کاما از عوامل فعال شده A2A شما است. سپس متغیرهای محیط لازم را برای عامل ارکستر تنظیم کرده و ADK Dev UI را راه اندازی کنید:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk web

👉 adk dev ui را باز کنید (از طریق پیش نمایش وب پورت را به 8000 تغییر دهید).

10-08-web-preview.png

👉 در Dropdown Agent ، عامل Orchestrate را انتخاب کنید.

👉 اکنون ، به آن یک کار پیچیده ارائه دهید که نیاز به هماهنگی چندین عامل از راه دور دارد. این مثال اول را امتحان کنید ، که باید عامل اجتماعی و سپس نماینده برنامه ریز را درگیر کند:

You are an expert event planner for a user named  Diana.
    Your task is to design a fun and personalized event.

    Here are the details for the plan:
    - Friends to invite: Ian, Nora
    - Desired date: "2025-10-15"
    - Location idea or general preference: "Chicago"

    Your process should be:
    1. Analyze the provided friend names. If you have access to a tool to get their InstaVibe profiles or summarized interests, please use it.
    2. Based on their potential interests (or general good taste if profiles are unavailable), create a tailored plan for the outing, check if you have access to any event planner tools.
    3. Ensure the plan includes the original `planned_date`.

    The user wants a comprehensive plan that includes:
    - The list of invited friends.
    - A catchy and descriptive name for the event.
    - The exact planned date for the event.
    - A summary of what the group will do.
    - Specific recommended spots (e.g., restaurants, bars, activity venues) with their names, (if possible, approximate latitude/longitude for mapping, and address), and a brief description of why it fits the plan.
    - A short, exciting message that {Diana} can send to {Ian, Nora} to get them excited about the event.

ارکستر کردن

تعامل را در پنجره چت ADK Dev UI مشاهده کنید. توجه زیادی به پاسخ های ارکستر کنید - باید بیان کند که کدام عامل از راه دور وظایف خود را به شما واگذار می کند (به عنوان مثال ، "خوب ، من ابتدا از نماینده پروفایل اجتماعی در مورد یان و نورا می پرسم ...").

همچنین ، برگه Events را در UI بررسی کنید تا تماس های ابزار اصلی (SEND_MESSAGE) را که در URL های عوامل از راه دور ساخته شده است ، ببینید.

ارسال کار

👉 اکنون ، مثال دوم را امتحان کنید که باید مستقیماً عامل ادغام پلتفرم را درگیر کند:

Hey, can you register an event on Instavibe for Laura and Charlie? Let's call it 'Vienna Concert & Castles Day'.
here are more info
"event_name": "Vienna Concert & Castles Day",
  "description": "A refined and unforgettable day in Vienna with Laura and Charlie. The day begins with a guided tour of the magnificent Schönbrunn Palace, showcasing imperial architecture and history. In the evening, enjoy a classical music concert in one of Vienna's most iconic concert halls.",
  "event_date": "2025-10-14T10:00:00+02:00",
  "locations": [
    {
      "name": "Schönbrunn Palace",
      "description": "A UNESCO World Heritage Site and former imperial summer residence, Schönbrunn Palace offers opulent rooms, beautiful baroque gardens, and a glimpse into the life of the Habsburg monarchy. Visitors can stroll the grounds or take a guided historical tour.",
      "latitude": 48.184516,
      "longitude": 16.312222,
      "address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria"
    },
    {
      "name": "Musikverein Vienna",
      "description": "Home to the world-renowned Vienna Philharmonic, the Musikverein is one of the finest concert halls in the world. Its 'Golden Hall' is famous for its acoustics and ornate design. Attendees can enjoy a powerful classical concert in an unforgettable setting.",
      "latitude": 48.200132,
      "longitude": 16.373777,
      "address": "Musikvereinsplatz 1, 1010 Wien, Austria"
    }
  ],
  "attendee_names": ["Laura", "Charlie", "Oscar"] And I am Oscar

باز هم ، برگه چت و رویدادها را کنترل کنید. ارکستر باید نیاز به ایجاد یک رویداد را شناسایی کند و وظیفه (با تمام جزئیات ارائه شده) را به "عامل ادغام پلتفرم" واگذار کند. همچنین می توانید روی دکمه Trace کلیک کنید تا اثری را برای تجزیه و تحلیل زمان پاسخ به پرس و جو و عملیات اجرا شده مشاهده کنید. ارسال رویداد

سپس می توانید تأیید کنید که رویداد در برنامه instavibe ظاهر می شود. رویداد instavibe

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

به یاد داشته باشید پس از اتمام آزمایش ، adk dev ui ( Ctrl+C در ترمینال) متوقف کنید.

12. موتور عامل و تماس از راه دور از instavibe

تاکنون ، ما عوامل تخصصی خود را در Cloud Run اجرا کرده ایم و ارکستور را به صورت محلی با استفاده از ADK Dev UI آزمایش کرده ایم. برای یک سناریوی تولید ، ما به یک محیط قوی ، مقیاس پذیر و مدیریت شده برای میزبانی نمایندگان خود نیاز داریم. اینجاست که موتور عامل AI Google Vertex AI وارد می شود.

Agent Engine یک سرویس کاملاً مدیریت شده در Vertex AI است که به طور خاص برای استقرار و مقیاس گذاری عوامل AI طراحی شده است. این کار مدیریت زیرساخت ها ، امنیت و سربار عملیاتی را از بین می برد و به توسعه دهندگان (به ویژه آنهایی که کمتر با محیط های ابر پیچیده آشنا هستند) به جای مدیریت سرورها ، بر منطق و توانایی های عامل تمرکز می کنند. این یک زمان اجرا اختصاصی بهینه شده برای بارهای کار عامل را فراهم می کند.

اکنون عامل ارکستر خود را به Agent Engine مستقر خواهیم کرد. (توجه: مکانیسم استقرار نشان داده شده در زیر از یک اسکریپت سفارشی (Agent_engine_app.py) ارائه شده در مواد کارگاه استفاده می کند ، زیرا ابزارهای استقرار مستقیم با موتور ADK به آژانس مستقیم ممکن است در حال تحول باشد.

دستور زیر را برای استقرار عامل ارکستر در موتور عامل اجرا کنید. اطمینان حاصل کنید که متغیر محیط Remote_Agent_addresses (حاوی URL های برنامه ریز ، پلتفرم و عوامل اجتماعی در اجرای ابر) هنوز به درستی از بخش قبلی تنظیم شده است.

👉💻 ما نماینده ارکسترات را به Agent Engine مستقر خواهیم کرد (توجه: این اجرای من از استقرار است ، ADK دارای CLI برای کمک به استقرار است ، من این کار را پس از اجرای BYO-SA به روز می کنم.)

cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env

adk deploy agent_engine \
--display_name "orchestrate-agent" \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_LOCATION \
--staging_bucket gs://$GOOGLE_CLOUD_PROJECT-agent-engine \
--trace_to_cloud \
--requirements_file orchestrate/requirements.txt \
orchestrate

اکنون که ارکستر در پلت فرم موتور عامل مدیریت شده میزبان است ، برنامه وب Instavibe ما نیاز به برقراری ارتباط با آن دارد. به جای تعامل از طریق ADK Dev UI ، برنامه وب تماس های از راه دور را به نقطه پایانی موتور عامل تبدیل می کند.

10-Agent-remote.png

ابتدا باید با استفاده از شناسه منحصر به فرد عامل Orchestrator مستقر ما ، کد برنامه Instavibe را اصلاح کنیم. این شناسه برای هدف قرار دادن نمونه صحیح عامل در سیستم عامل لازم است.

👉📝 ~/instavibe-bootstrap/instavibe/introvertally.py را باز کنید و #REPLACE ME initiate agent_engine با کد زیر جایگزین کنید. این شناسه موتور عامل را از یک متغیر محیط (که ما به زودی تنظیم خواهیم کرد) بازیابی می کند و یک شیء مشتری دریافت می کند:

ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)

جریان کاربر برنامه ریزی شده ما در instavibe شامل دو تعامل با نماینده است: اول ، تولید برنامه توصیه شده و دوم ، از کاربر می خواهیم قبل از اینکه نماینده در واقع رویداد را به سیستم عامل ارسال کند ، تأیید کند.

از آنجا که برنامه وب Instavibe (در حال اجرا در Cloud Run) و عامل Orchestrator (در حال اجرا بر روی موتور عامل) اکنون خدمات جداگانه ای هستند ، برنامه وب باید برای تعامل با عامل ، تماس های از راه دور را به نقطه پایانی عامل انجام دهد.

👉📝 بیایید کدی را که تماس اولیه را برای تولید توصیه طرح ایجاد می کند ، به روز کنیم. در همان پرونده introvertally.py ، #REPLACE ME Query remote agent get plan با قطعه زیر جایگزین کنید ، که از مشتری Agent_engine برای ارسال درخواست کاربر استفاده می کند:

agent_engine.stream_query(
                user_id=user_id,
                message=prompt_message,
            )

👉📝 بعد ، کدی را که تأیید کاربر را بر عهده دارد به روز کنید (به عنوان مثال ، وقتی کاربر "برنامه را تأیید می کند" را کلیک می کند). این یک پیام پیگیری را به همان مکالمه در Agent Engine ارسال می کند و به ارکستر دستور می دهد تا با ارسال این رویداد (که آن را به نماینده ادغام پلتفرم واگذار می کند) ادامه دهد. جایگزین #REPLACE ME Query remote agent for confirmation برای تأیید در introvertally.py با:

agent_engine.stream_query(
            user_id=agent_session_user_id,
            message=prompt_message,
        )

مسیرهای برنامه وب نیاز به دسترسی به این توابع دارد. اطمینان حاصل کنید که توابع لازم از درونگرا به صورت درونی در پرونده Flask Routes وارد می شوند.

👉📝 در cd ~/instavibe-bootstrap/instavibe/ally_routes.py ، ما ابتدا به نمونه اشاره خواهیم کرد که # REPLACE ME TO ADD IMPORT :

from introvertally import call_agent_for_plan, post_plan_event

👉📝 ویژگی نمونه اولیه را به instavibe اضافه کنید ، در ~/instavibe-bootstrap/instavibe/templates/base.html ، جایگزین <!-replace_me_link_to_introvert_ally-> را با زیر:

            <li class="nav-item">
              <a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
            </li>

قبل از اینکه بتوانیم برنامه instavibe را دوباره مستقر کنیم ، Resource ID خاص عامل ارکستراتور که به Agent Engine مستقر شده ایم ، نیاز داریم.

در حال حاضر ، بازیابی این برنامه از طریق gcloud ممکن است محدود باشد ، بنابراین ما از یک اسکریپت Helper Python ( temp-endpoint.py ارائه شده در کارگاه) استفاده می کنیم تا شناسه را بدست آوریم و آن را در یک متغیر محیط ذخیره کنیم.

the دستورات زیر را برای اجرای اسکریپت اجرا کنید. این اسکریپت شناسه نقطه انتهایی موتور عامل را ضبط می کند و مجوزهای لازم را به حساب خدمات پیش فرض موتور عامل اعطا می کند (توجه: اسکریپت برای استفاده از حساب خدمات پیش فرض پیکربندی شده است زیرا در حال حاضر قابل تنظیم کاربر نیست).

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
source ~/instavibe-bootstrap/env/bin/activate
python temp-endpoint.py
export ORCHESTRATE_AGENT_ID=$(cat temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"


gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
    --role="roles/viewer"

شناسه نقطه پایانی موتور عامل

سرانجام ، ما باید برنامه Web Instavibe را با کد به روز شده و متغیر محیط جدید ORCHESTRATE_AGENT_ID مجدداً مجدداً مستقر کنیم ، بنابراین می داند چگونه می تواند به عامل ما در حال اجرا بر روی Agent Engine باشد.

command دستورات زیر تصویر برنامه Instavibe را بازسازی کرده و نسخه جدید را به Cloud Run اعزام کنید:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/

export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

echo "Building ${APP_FOLDER_NAME} webapp image..."
gcloud builds submit . \
  --tag=${IMAGE_PATH} \
  --project=${PROJECT_ID}

echo "Deploying ${SERVICE_NAME} to Cloud Run..."

gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
  --set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
  --set-env-vars="APP_HOST=0.0.0.0" \
  --set-env-vars="APP_PORT=8080" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
  --set-env-vars="ORCHESTRATE_AGENT_ID=${ORCHESTRATE_AGENT_ID}" \
  --project=${PROJECT_ID} \
  --min-instances=1 \
  --cpu=2 \
  --memory=2Gi

با تکمیل استقرار نهایی ، به URL Application Instavibe خود در یک برگه مرورگر متفاوت بروید.

آزمایش تجربه کامل instavibe با هوش مصنوعی

ویژگی "Ally Instavibe" اکنون به صورت زنده ، توسط سیستم چند عامل ما که از طریق موتور عامل Vertex AI ارکستر شده و از طریق A2A ارتباط برقرار می کند ، زنده است.

12-02-new.png

روی "Ally Instavibe" کلیک کنید و از آن بخواهید که یک رویداد را برنامه ریزی کند.

12-03-introvertally.png

در حالی که عوامل کار می کنند ، ورود به سیستم را در سمت راست مشاهده کنید (ممکن است 90-120 ثانیه طول بکشد). Once the plan appears, review it and click "Confirm This Plan" to proceed with posting.

12-04-confirm.png

The orchestrator will now instruct the Platform agent to create the post and event within InstaVibe. 12-05-posting.png

Check the InstaVibe home page for the new post and event. 12-06-instavibe.png

The event page will reflect the details generated by the agent.

12-07-event.png

Analyzing Performance with Cloud Trace

You might notice the process takes some time. Vertex AI Agent Engine integrates with Cloud Trace, allowing us to analyze the latency of our multi-agent system.

Go to the Traces in the google cloud console, select agent_run[orchestrate_agent] in the Span, you should see a couple of Spans, click into it

12-08-trace.png

Within the trace details, you can identify which parts took longer. For example, calls to the Planner agent might show higher latency due to search grounding and complex generation. 12-09-plan.png

Similarly, when creating the post and event, you might see time spent by the Orchestrator processing data and preparing tool calls for the Platform agent. 12-10-post.png

Exploring these traces helps understand and optimize the performance of your agent system.

celebrate.png

تبریک می گویم! You've successfully built, deployed, and tested a sophisticated multi-agent AI system using Google's ADK, A2A, MCP, and Google Cloud services. You've tackled agent orchestration, tool usage, state management, and cloud deployment, creating a functional AI-powered feature for InstaVibe. Well done on completing the workshop!

13. Clean Up

To avoid ongoing charges to your Google Cloud account, it's important to delete the resources we created during this workshop. The following commands will help you remove the Spanner instance, Cloud Run services, Artifact Registry repository, API Key, Vertex AI Agent Engine, and associated IAM permissions.

مهم:

  • Ensure you are running these commands in the same Google Cloud project used for the workshop.
  • If you've closed your Cloud Shell terminal, some environment variables like $PROJECT_ID, $SPANNER_INSTANCE_ID, etc., might not be set. You'll need to either re-export them as you did during the workshop setup or replace the variables in the commands below with their actual values.
  • These commands will permanently delete your resources. Double-check before running if you have other important data in this project.

👉💻 Run the following scripts to clean up.

Reset environment variables

. ~/instavibe-bootstrap/set_env.sh

Delete Agent Engine:

cd ~/instavibe-bootstrap/utils
source ~/instavibe-bootstrap/env/bin/activate
export ORCHESTRATE_AGENT_ID=$(cat ~/instavibe-bootstrap/instavibe/temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
python remote_delete.py
deactivate
echo "Vertex AI Agent Engine deletion initiated."

Delete Cloud Run Services:

# InstaVibe Web Application
gcloud run services delete instavibe --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# MCP Tool Server
gcloud run services delete mcp-tool-server --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Planner Agent (A2A Server)
gcloud run services delete planner-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Platform MCP Client Agent (A2A Server)
gcloud run services delete platform-mcp-client --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Social Agent (A2A Server)
gcloud run services delete social-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

echo "Cloud Run services deletion initiated."

Stop and Remove the A2A Inspector Docker Container

docker rm --force a2a-inspector

Delete Spanner Instance:

echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."

Delete Artifact Registry Repository:

echo "Deleting Artifact Registry repository: ${REPO_NAME}..."
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --project=${PROJECT_ID} --quiet
echo "Artifact Registry repository deletion initiated."

Remove Roles from Service Account:

echo "Removing roles from service account: $SERVICE_ACCOUNT_NAME in project $PROJECT_ID"

# Remove Project-level roles for default service account
gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/spanner.databaseUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/artifactregistry.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/cloudbuild.builds.editor"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/run.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/iam.serviceAccountUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/aiplatform.user"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.logWriter"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
  --role="roles/logging.viewer"


echo "All specified roles have been removed."

Delete Local Workshop Files:

echo "Removing local workshop directory ~/instavibe-bootstrap..."
rm -rf ~/instavibe-bootstrap
rm -rf ~/a2a-inspector
rm -f ~/mapkey.txt
rm -f ~/project_id.txt
echo "Local directory removed."

پاک کردن