۱. 📖 مقدمه

پروتکل Agent2Agent (A2A) برای استانداردسازی ارتباط بین عاملهای هوش مصنوعی، به ویژه برای آنهایی که در سیستمهای خارجی مستقر هستند، طراحی شده است. پیش از این، چنین پروتکلهایی برای ابزارهایی به نام پروتکل زمینه مدل (MCP) ایجاد شده بودند که یک استاندارد نوظهور برای اتصال LLMها با دادهها و منابع است. A2A سعی میکند MCP را تکمیل کند، جایی که A2A بر روی یک مشکل متفاوت متمرکز است، در حالی که MCP بر کاهش پیچیدگی برای اتصال عاملها با ابزارها و دادهها تمرکز دارد، A2A بر چگونگی فعال کردن عاملها برای همکاری در روشهای طبیعی خود تمرکز دارد. این پروتکل به عاملها اجازه میدهد تا به عنوان عامل (یا به عنوان کاربر) به جای ابزار، ارتباط برقرار کنند. به عنوان مثال، وقتی میخواهید چیزی سفارش دهید، ارتباط رفت و برگشتی را فعال کنید.
A2A مکمل MCP است، در اسناد رسمی توصیه شده است که برنامهها از MCP برای ابزارها و از A2A برای عاملها استفاده کنند - که با AgentCard نشان داده میشود (بعداً در مورد این موضوع بحث خواهیم کرد). سپس چارچوبها میتوانند از A2A برای ارتباط با کاربر خود، عاملهای از راه دور و سایر عاملها استفاده کنند.

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

- کلاینت A2A ابتدا تمام کارتهای عامل سرور A2A موجود را بررسی کرده و از اطلاعات آن برای ایجاد یک کلاینت اتصال استفاده میکند.
- در صورت نیاز، کلاینت A2A پیامی را به سرور A2A ارسال میکند، سرور این را به عنوان یک وظیفه (Task) که باید تکمیل شود ارزیابی میکند. اگر URL گیرنده اعلانهای فوری (push notification) روی کلاینت A2A پیکربندی شده باشد و توسط سرور A2A پشتیبانی شود، سرور همچنین قادر خواهد بود وضعیت پیشرفت وظیفه را به نقطه پایانی دریافت کننده روی کلاینت منتشر کند.
- پس از اتمام کار، سرور A2A پاسخ مصنوع را به کلاینت A2A ارسال میکند.
از طریق codelab، شما یک رویکرد گام به گام به شرح زیر را به کار خواهید گرفت:
- آمادهسازی پروژه گوگل کلود
- راهاندازی دایرکتوری کاری برای محیط کدنویسی
- عامل برگر را به Cloud Run مستقر کنید
- عامل پیتزا را به Cloud Run مستقر کنید
- استقرار متصدی خرید در Agent Engine
- از طریق رابط محلی با متصدی خرید تعامل داشته باشید
نمای کلی معماری
شما معماری سرویس زیر را مستقر خواهید کرد

شما دو سرویس را مستقر خواهید کرد که به عنوان سرور A2A عمل میکنند، عامل Burger (با پشتیبانی چارچوب عامل CrewAI) و عامل Pizza (با پشتیبانی چارچوب عامل Langgraph). کاربر فقط مستقیماً با متصدی خرید که با استفاده از چارچوب کیت توسعه عامل (ADK) اجرا میشود، تعامل خواهد داشت که به عنوان کلاینت A2A عمل خواهد کرد.
هر یک از این عاملها محیط و استقرار خاص خود را خواهند داشت.
پیشنیازها
- کار راحت با پایتون
- درک معماری پایه فول استک با استفاده از سرویس HTTP
آنچه یاد خواهید گرفت
- ساختار اصلی سرور A2A
- ساختار اصلی کلاینت A2A
- استقرار سرویس عامل در Cloud Run
- استقرار سرویس عامل به Agent Engine
- نحوه اتصال کلاینت A2A به سرور A2A
- ساختار درخواست و پاسخ در اتصال غیر استریمینگ
آنچه نیاز دارید
- مرورگر وب کروم
- یک حساب جیمیل
- یک پروژه ابری با حساب صورتحساب فعال
این آزمایشگاه کد که برای توسعهدهندگان در تمام سطوح (از جمله مبتدیان) طراحی شده است، در برنامه نمونه خود از پایتون استفاده میکند. با این حال، برای درک مفاهیم ارائه شده، دانش پایتون لازم نیست.
۲. 🚀 آمادهسازی مقدمات توسعه کارگاه
مرحله ۱: انتخاب پروژه فعال در کنسول ابری
در کنسول گوگل کلود ، در صفحه انتخاب پروژه، یک پروژه گوگل کلود را انتخاب یا ایجاد کنید (به بخش بالا سمت چپ کنسول خود مراجعه کنید)

روی آن کلیک کنید، و لیستی از تمام پروژههای خود را مانند این مثال مشاهده خواهید کرد،

مقداری که با کادر قرمز مشخص شده است، شناسه پروژه (PROJECT ID) است و این مقدار در طول آموزش استفاده خواهد شد.
مطمئن شوید که پرداخت صورتحساب برای پروژه ابری شما فعال است. برای بررسی این موضوع، روی نماد همبرگر ☰ در نوار بالا سمت چپ که منوی پیمایش را نشان میدهد کلیک کنید و منوی پرداخت صورتحساب را پیدا کنید.

اگر عبارت «حساب پرداخت آزمایشی پلتفرم ابری گوگل» را مشاهده کردید، پروژه شما آماده استفاده برای این آموزش است. در غیر این صورت، به ابتدای این آموزش برگردید و حساب پرداخت را فعال کنید.

مرحله ۲: آشنایی با Cloud Shell
شما در بیشتر بخشهای آموزش از Cloud Shell استفاده خواهید کرد، روی Activate Cloud Shell در بالای کنسول Google Cloud کلیک کنید. اگر از شما درخواست تأیید کرد، روی Authorize کلیک کنید.


پس از اتصال به Cloud Shell، باید بررسی کنیم که آیا shell (یا ترمینال) از قبل با حساب ما احراز هویت شده است یا خیر.
gcloud auth list
اگر خروجی جیمیل شخصی خود را مانند نمونه زیر مشاهده کردید، همه چیز درست است.
Credentialed Accounts
ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com
To set the active account, run:
$ gcloud config set account `ACCOUNT`
اگر اینطور نیست، مرورگر خود را رفرش کنید و مطمئن شوید که در صورت درخواست، روی «مجوز» کلیک میکنید (ممکن است به دلیل مشکل اتصال، قطع شود).
در مرحله بعد، باید بررسی کنیم که آیا پوسته از قبل با شناسه پروژه صحیحی که دارید پیکربندی شده است یا خیر، اگر مقداری را داخل () قبل از نماد $ در ترمینال مشاهده کردید (در تصویر زیر، مقدار "a2a-agent-engine" است)، این مقدار پروژه پیکربندی شده برای جلسه پوسته فعال شما را نشان میدهد.

اگر مقدار نمایش داده شده از قبل صحیح است، میتوانید از دستور بعدی صرف نظر کنید . اما اگر صحیح نیست یا وجود ندارد، دستور زیر را اجرا کنید
gcloud config set project <YOUR_PROJECT_ID>
سپس، پوشه کاری قالب را برای این codelab از Github کپی کنید، دستور زیر را اجرا کنید. این دستور پوشه کاری را در پوشه purchases-concierge-a2a ایجاد میکند.
git clone https://github.com/alphinside/purchasing-concierge-intro-a2a-codelab-starter.git purchasing-concierge-a2a
مرحله 3: آشنایی با ویرایشگر Cloud Shell و دایرکتوری کاری برنامه نصب
حالا میتوانیم ویرایشگر کد خود را برای انجام برخی کارهای کدنویسی تنظیم کنیم. برای این کار از ویرایشگر Cloud Shell استفاده خواهیم کرد.
روی دکمهی «باز کردن ویرایشگر» کلیک کنید، این کار یک ویرایشگر Cloud Shell را باز میکند. 
پس از آن، به بخش بالای ویرایشگر Cloud Shell بروید و روی File->Open Folder کلیک کنید، پوشه نام کاربری خود را پیدا کنید و پوشه purchase-concierge-a2a را پیدا کنید و سپس روی دکمه OK کلیک کنید. این کار پوشه انتخاب شده را به عنوان پوشه اصلی کار تبدیل میکند. در این مثال، نام کاربری alvinprayuda است، از این رو مسیر پوشه در زیر نشان داده شده است.


حالا، ویرایشگر Cloud Shell شما باید به این شکل باشد

حالا، ترمینال ویرایشگر را باز کنید. میتوانید این کار را با کلیک روی ترمینال -> ترمینال جدید در نوار منو انجام دهید، یا از Ctrl + Shift + C استفاده کنید، این کار یک پنجره ترمینال در قسمت پایین مرورگر باز میکند.

ترمینال فعال فعلی شما باید در دایرکتوری کاری shopping-concierge-a2a باشد. ما در این آزمایشگاه کد از پایتون ۳.۱۲ استفاده خواهیم کرد و از uv python project manager برای سادهسازی نیاز به ایجاد و مدیریت نسخه پایتون و محیط مجازی استفاده خواهیم کرد. این بسته uv از قبل روی Cloud Shell نصب شده است.
این دستور را اجرا کنید تا وابستگیهای مورد نیاز برای محیط مجازی در دایرکتوری .venv نصب شود.
uv sync --frozen
برای مشاهدهی وابستگیهای اعلامشده برای این آموزش که عبارتند از a2a-sdk, google-adk, and gradio ، فایل pyproject.toml را بررسی کنید.
حالا باید APIهای مورد نیاز را از طریق دستور زیر فعال کنیم. این کار ممکن است کمی طول بکشد.
gcloud services enable aiplatform.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
cloudresourcemanager.googleapis.com
در صورت اجرای موفقیتآمیز دستور، باید پیامی مشابه آنچه در زیر نشان داده شده است را مشاهده کنید:
Operation "operations/..." finished successfully.
۳. 🚀 استقرار نمایندگان فروش از راه دور سرور A2A در Cloud Run
در این مرحله، ما این دو عامل فروشنده از راه دور را که با کادر قرمز مشخص شدهاند، مستقر خواهیم کرد. عامل برگر توسط چارچوب عامل CrewAI و عامل پیتزا توسط عامل Langgraph پشتیبانی خواهد شد.

۴. 🚀 استقرار نماینده فروش برگر - سرور A2A
کد منبع burger agent در دایرکتوری remote_seller_agents/burger_agent قرار دارد.
تمام فایلهایی که در دایرکتوری remote_seller_agents/burger_agent وجود دارند، برای استقرار عامل ما در Cloud Run کافی هستند تا بتوان آن را به عنوان یک سرویس در دسترس قرار داد. دستور زیر را برای استقرار آن اجرا کنید.
gcloud run deploy burger-agent \
--source remote_seller_agents/burger_agent \
--port=8080 \
--allow-unauthenticated \
--min 1 \
--region us-central1 \
--update-env-vars GOOGLE_CLOUD_LOCATION=us-central1 \
--update-env-vars GOOGLE_CLOUD_PROJECT={your-project-id}
اگر از شما خواسته شد که یک مخزن کانتینر برای استقرار از منبع ایجاد شود، به Y پاسخ دهید. پس از استقرار موفقیتآمیز، گزارشی مانند این نشان داده خواهد شد.
Service [burger-agent] revision [burger-agent-xxxxx-xxx] has been deployed and is serving 100 percent of traffic. Service URL: https://burger-agent-xxxxxxxxx.us-central1.run.app
بخش xxxx در اینجا یک شناسه منحصر به فرد هنگام استقرار سرویس خواهد بود.
یک تب جدید در مرورگر باز کنید و به آدرس https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json بروید. مسیر سرویسهای عامل برگر مستقر شده از طریق مرورگر. این URL برای دسترسی به کارت عامل سرور A2A مستقر شده است.
در صورت موفقیتآمیز بودن استقرار، هنگام دسترسی به کارت عامل، پاسخی مانند آنچه در زیر نشان داده شده است را در مرورگر خود مشاهده خواهید کرد.

این اطلاعات کارت برگر ایجنت است که باید برای اهداف اکتشافی قابل دسترسی باشد.
توجه داشته باشید که مقدار url هنوز در اینجا روی http://0.0.0.0:8080/ تنظیم شده است. این مقدار url باید اطلاعات اصلی برای کلاینت A2A برای ارسال پیام از دنیای خارج باشد و به درستی پیکربندی نشده است.
ما باید با اضافه کردن یک متغیر محیطی اضافی HOST_OVERRIDE این مقدار را به URL سرویس عامل برگر خود بهروزرسانی کنیم.
بهروزرسانی مقدار URL مربوط به Burger Agent در کارت Agent از طریق متغیر محیطی
برای اضافه کردن HOST_OVERRIDE به سرویس burger agent، مراحل زیر را انجام دهید.
- جستجوی ابری را در نوار جستجو در بالای کنسول ابری خود اجرا کنید

- روی سرویس اجرای ابری burger-agent که قبلاً مستقر شده است کلیک کنید

- آدرس اینترنتی سرویس برگر را کپی کنید، سپس روی ویرایش کلیک کنید و نسخه جدید را مستقر کنید

- سپس، روی بخش متغیرها و اسرار کلیک کنید

- پس از آن، روی افزودن متغیر کلیک کنید و مقدار
HOST_OVERRIDEرا برابر با آدرس اینترنتی سرویس (همان که الگویhttps://burger-agent-xxxxxxxxx.us-central1.run.appدارد) قرار دهید.

- در نهایت، برای استقرار مجدد سرویس خود، روی دکمهی deploy کلیک کنید.

وقتی دوباره در مرورگر به آدرس https://burger-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json دسترسی پیدا کنید، مقدار url از قبل به درستی پیکربندی شده است.

۵. 🚀 استقرار نماینده فروش پیتزا - سرور A2A
به طور مشابه، کد منبع pizza agent در دایرکتوری remote_seller_agents/pizza_agent قرار دارد.
مشابه مرحله قبلی استقرار burger-agent، تمام فایلهایی که در دایرکتوری remote_seller_agents/pizza_agent وجود دارند، برای استقرار عامل ما در Cloud Run کافی هستند تا بتوان آن را به عنوان یک سرویس در دسترس قرار داد. دستور زیر را برای استقرار آن اجرا کنید.
gcloud run deploy pizza-agent \
--source remote_seller_agents/pizza_agent \
--port=8080 \
--allow-unauthenticated \
--min 1 \
--region us-central1 \
--update-env-vars GOOGLE_CLOUD_LOCATION=us-central1 \
--update-env-vars GOOGLE_CLOUD_PROJECT={your-project-id}
پس از استقرار موفقیتآمیز، گزارشی مانند این نشان داده خواهد شد.
Service [pizza-agent] revision [pizza-agent-xxxxx-xxx] has been deployed and is serving 100 percent of traffic. Service URL: https://pizza-agent-xxxxxxxxx.us-central1.run.app
بخش xxxx در اینجا یک شناسه منحصر به فرد هنگام استقرار سرویس خواهد بود.
همین مورد در مورد عامل برگر نیز صادق است، وقتی سعی میکنید از طریق مرورگر به مسیر https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json مربوط به سرویسهای عامل پیتزای مستقر شده بروید تا به کارت عامل سرور A2A دسترسی پیدا کنید، مقدار url عامل پیتزا روی کارت عامل آن هنوز به درستی پیکربندی نشده است. همچنین باید HOST_OVERRIDE به متغیر محیطی آن اضافه کنیم.
بهروزرسانی مقدار URL نماینده پیتزا در کارت نماینده از طریق متغیر محیطی
برای اضافه کردن HOST_OVERRIDE به سرویس pizza agent، مراحل زیر را انجام دهید.
- جستجوی ابری را در نوار جستجو در بالای کنسول ابری خود اجرا کنید

- روی سرویس اجرای ابری pizza-agent که قبلاً مستقر شده است کلیک کنید

- روی ویرایش کلیک کنید و نسخه جدید را مستقر کنید

- آدرس اینترنتی سرویس پیتزا را کپی کنید، سپس روی بخش متغیرها و اسرار کلیک کنید

- پس از آن، روی افزودن متغیر کلیک کنید و مقدار
HOST_OVERRIDEرا برابر با آدرس اینترنتی سرویس (همان که الگویhttps://pizza-agent-xxxxxxxxx.us-central1.run.appدارد) قرار دهید.

- در نهایت، برای استقرار مجدد سرویس خود، روی دکمهی deploy کلیک کنید.

حالا، وقتی دوباره در مرورگر https://pizza-agent-xxxxxxxxx.us-central1.run.app/.well-known/agent.json به کارت agent مربوط به pizza-agent دسترسی پیدا کنید، مقدار url از قبل به درستی پیکربندی شده است.

در این مرحله، ما با موفقیت هر دو سرویس برگر و پیتزا را در Cloud Run مستقر کردهایم.
۶. 🚀 استقرار متصدی خرید - موتور تبدیل کلاینت به نماینده A2A
در این مرحله، ما نمایندهی مسئول خرید را مستقر خواهیم کرد. این نماینده کسی است که با او تعامل خواهیم داشت.

کد منبع عامل خرید ما در دایرکتوری purchase_concierge قرار دارد. مقداردهی اولیه عامل را میتوان در اسکریپت purchase_concierge/purchasing_agent.py بررسی کرد.
برای استقرار آن، این مراحل را دنبال کنید:
- ابتدا، باید فضای ذخیرهسازی مرحلهای خود را در فضای ذخیرهسازی ابری ایجاد کنیم.
gcloud storage buckets create gs://purchasing-concierge-{your-project-id} --location=us-central1
- حالا، ابتدا باید متغیر .env را آماده کنیم، بیایید .env.example را در فایل .env کپی کنیم.
cp .env.example .env
- حالا فایل .env را باز کنید. محتوای زیر را مشاهده خواهید کرد.
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL={your-pizza-agent-url}
BURGER_SELLER_AGENT_URL={your-burger-agent-url}
AGENT_ENGINE_RESOURCE_NAME={your-agent-engine-resource-name}
این عامل با عامل برگر و پیتزا ارتباط برقرار خواهد کرد، بنابراین باید اعتبارنامههای مناسب را برای هر دوی آنها ارائه دهیم. باید PIZZA_SELLER_AGENT_URL و BURGER_SELLER_AGENT_URL را با آدرس اینترنتی Cloud Run از مراحل قبلی بهروزرسانی کنیم.
اگر این را فراموش کردید، به کنسول Cloud Run مراجعه کنید. در نوار جستجوی بالای کنسول خود عبارت "Cloud Run" را تایپ کنید و روی آیکون Cloud Run کلیک راست کنید تا در یک تب جدید باز شود.

شما باید خدمات قبلی نماینده فروش از راه دور مستقر شده ما را مانند شکل زیر مشاهده کنید.

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

متغیر محیطی نهایی باید شبیه به این باشد
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL=https://pizza-agent-xxxxx.us-central1.run.app
BURGER_SELLER_AGENT_URL=https://burger-agent-xxxxx.us-central1.run.app
AGENT_ENGINE_RESOURCE_NAME={your-agent-engine-resource-name}
- اکنون، ما آمادهایم تا عامل خرید خود را مستقر کنیم. ما آن را به موتور عامل مستقر خواهیم کرد و کد استقرار در داخل اسکریپت
deploy_to_agent_engine.pyقرار دارد.
ما میتوانیم آن را با اجرای اسکریپت مستقر کنیم:
uv run deploy_to_agent_engine.py
پس از استقرار موفقیتآمیز، گزارشی مانند این نمایش داده میشود. نام منبع Agent Engine به صورت "projects/xxxx/locations/us-central1/reasoningEngines/yyyy" نمایش داده میشود.
AgentEngine created. Resource name: projects/xxxx/locations/us-central1/reasoningEngines/yyyy
To use this AgentEngine in another session:
agent_engine = vertexai.agent_engines.get('projects/xxxx/locations/us-central1/reasoningEngines/yyyy)
Deployed remote app resource: projects/xxxx/locations/us-central1/reasoningEngines/xxxx
و وقتی آن را در داشبورد موتور عامل بررسی میکنیم (در نوار جستجو عبارت "موتور عامل" را جستجو کنید) استقرار قبلی ما را نشان میدهد.

همچنین میتوانید بررسی کنید که نام منبع Agent Engine در آنجا نمایش داده شده است. سپس، میتوانیم از این نام منبع برای آزمایش آن استفاده کنیم.
پس از آن، مقدار AGENT_ENGINE_RESOURCE_NAME را در فایل .env با این مقدار بهروزرسانی کنید . مطمئن شوید که نام منبع موتور عامل (agent engine resource name) صحیح را وارد میکنید. فایل .env شما باید به شکل زیر باشد:
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT={your-project-id}
GOOGLE_CLOUD_LOCATION=us-central1
STAGING_BUCKET=gs://purchasing-concierge-{your-project-id}
PIZZA_SELLER_AGENT_URL=https://pizza-agent-xxxxx.us-central1.run.app
BURGER_SELLER_AGENT_URL=https://burger-agent-xxxxx.us-central1.run.app
AGENT_ENGINE_RESOURCE_NAME=projects/xxxx/locations/us-central1/reasoningEngines/yyyy
آزمایش عامل مستقر شده روی موتور عامل
تعامل با موتور عامل میتواند از طریق دستور curl و SDK انجام شود. برای مثال، دستور زیر را اجرا کنید تا تعامل با عامل مستقر شده را امتحان کنید.
میتوانید این کوئری را ارسال کنید تا بررسی کنید که آیا عامل با موفقیت مستقر شده است یا خیر. اسکریپت test_agent_engine.sh زیر را اجرا کنید.
bash test_agent_engine.sh
میتوانید اسکریپت را بررسی کنید و ببینید که ما سعی میکنیم از نماینده بپرسیم «لطفاً منوی برگر موجود را فهرست کنید»
در صورت موفقیت، چندین رویداد پاسخ مانند این در کنسول شما پخش خواهد شد.
{
"content": {
"parts": [
{
"text": "Here is our burger menu:\n- Classic Cheeseburger: IDR 85K\n- Double Cheeseburger: IDR 110K\n- Spicy Chicken Burger: IDR 80K\n- Spicy Cajun Burger: IDR 85K"
}
],
"role": "model"
},
"usage_metadata": {
"candidates_token_count": 51,
"candidates_tokens_details": [
{
"modality": "TEXT",
"token_count": 51
}
],
"prompt_token_count": 907,
"prompt_tokens_details": [
{
"modality": "TEXT",
"token_count": 907
}
],
"total_token_count": 958,
"traffic_type": "ON_DEMAND"
},
"invocation_id": "e-14679918-af68-45f1-b942-cf014368a733",
"author": "purchasing_agent",
"actions": {
"state_delta": {},
"artifact_delta": {},
"requested_auth_configs": {}
},
"id": "dbe7fc43-b82a-4f3e-82aa-dd97afa8f15b",
"timestamp": 1754287348.941454
}
ما در مرحله بعدی از رابط کاربری استفاده خواهیم کرد، با این حال، ابتدا اجازه دهید در مورد اجزای اصلی و جریان معمول کلاینتهای A2A بحث کنیم.
۷. 🚀 تست یکپارچهسازی و بازرسی بار مفید
حالا بیایید با استفاده از یک رابط کاربری وب، رابط خرید خود را با تعامل با اپراتور از راه دور بررسی کنیم. دستور زیر را برای استقرار یک برنامه Gradio اجرا کنید. اجرای این برنامه مستلزم آن است که فایل .env را به درستی پر کرده باشید.
uv run purchasing_concierge_ui.py
در صورت موفقیت، خروجی زیر نمایش داده خواهد شد
* Running on local URL: http://0.0.0.0:8080 * To create a public link, set `share=True` in `launch()`.
سپس، در ترمینال، Ctrl را نگه دارید و روی آدرس http://0.0.0.0:8080 کلیک کنید یا روی دکمه پیشنمایش وب کلیک کنید تا رابط کاربری وب باز شود.

سعی کنید مکالمهای شبیه به این داشته باشید:
- منوی برگر و پیتزا را به من نشان بده
- من میخواهم ۱ پیتزای مرغ باربیکیو و ۱ برگر کاجون تند سفارش دهم.
و مکالمه را تا زمانی که سفارش را تمام کنید ادامه دهید. بررسی کنید که تعامل چگونه پیش میرود و فراخوانی و پاسخ ابزار چیست؟ تصویر زیر نمونهای از نتیجه تعامل است.




میتوانیم ببینیم که ارتباط با دو عامل مختلف، دو رفتار متفاوت را به همراه دارد و A2A میتواند این را به خوبی مدیریت کند. عامل فروشنده پیتزا مستقیماً درخواست عامل خرید ما را میپذیرد، در حالی که عامل برگر قبل از ادامه درخواست ما به تأیید ما نیاز دارد و پس از تأیید ما، عامل میتواند تأیید را به عامل برگر واگذار کند.
اکنون، مفاهیم اولیه A2A را به پایان رساندهایم و میبینیم که چگونه به عنوان معماری کلاینت و سرور پیادهسازی شده است.
۸. 💡 [توضیح کد] مفهوم و پیادهسازی سرور A2A
مقداردهی اولیهی عامل فروشندهی راه دور را میتوان در اسکریپت remote_seller_agents/*/agent.py بررسی کرد. در اینجا قطعه کد عاملهای فروشنده آمده است.
برگر ایجنت
from crewai import Agent, Crew, LLM, Task, Process
from crewai.tools import tool
...
model = LLM(
model="vertex_ai/gemini-2.5-flash-lite", # Use base model name without provider prefix
)
burger_agent = Agent(
role="Burger Seller Agent",
goal=(
"Help user to understand what is available on burger menu and price also handle order creation."
),
backstory=("You are an expert and helpful burger seller agent."),
verbose=False,
allow_delegation=False,
tools=[create_burger_order],
llm=model,
)
agent_task = Task(
description=self.TaskInstruction,
agent=burger_agent,
expected_output="Response to the user in friendly and helpful manner",
)
crew = Crew(
tasks=[agent_task],
agents=[burger_agent],
verbose=False,
process=Process.sequential,
)
inputs = {"user_prompt": query, "session_id": sessionId}
response = crew.kickoff(inputs)
return response
...
پیتزا فروشی
from langchain_google_vertexai import ChatVertexAI
from langgraph.prebuilt import create_react_agent
...
self.model = ChatVertexAI(
model="gemini-2.5-flash-lite",
location=os.getenv("GOOGLE_CLOUD_LOCATION"),
project=os.getenv("GOOGLE_CLOUD_PROJECT"),
)
self.tools = [create_pizza_order]
self.graph = create_react_agent(
self.model,
tools=self.tools,
checkpointer=memory,
prompt=self.SYSTEM_INSTRUCTION,
)
...
همانطور که میبینید، این دو عامل با چارچوبهای کاملاً متفاوتی (CrewAI و Langgraph) در مقایسه با عامل کلاینت (ADK) ساخته شدهاند. با A2A این مشکل وجود ندارد، ما نیازی نداریم که آنها کد داخلی خود را برای برقراری ارتباط با یکدیگر به اشتراک بگذارند، فرقی نمیکند از چه چارچوبهایی استفاده میشود، از چه زبانی استفاده میشود یا کجا مستقر میشوند.
اجزای اصلی سرور A2A
حالا بیایید در مورد مفهوم اصلی و اجزای سرور A2A بحث کنیم.
کارت نماینده
هر یک از سرورهای A2A باید یک کارت عامل داشته باشند که از طریق منبع /.well-known/agent.json قابل دسترسی باشد. این برای پشتیبانی از مرحله کشف در کلاینت A2A است که باید اطلاعات و زمینههای کاملی در مورد نحوه دسترسی به عامل و شناخت تمام قابلیتهای آن ارائه دهد. این امر تا حدودی با مستندات API مستند شده با استفاده از Swagger یا Postman مشابه است.
این محتوای کارت مامور برگر مستقر ما است
{
"capabilities": {
"streaming": true
},
"defaultInputModes": [
"text",
"text/plain"
],
"defaultOutputModes": [
"text",
"text/plain"
],
"description": "Helps with creating burger orders",
"name": "burger_seller_agent",
"protocolVersion": "0.2.6",
"skills": [
{
"description": "Helps with creating burger orders",
"examples": [
"I want to order 2 classic cheeseburgers"
],
"id": "create_burger_order",
"name": "Burger Order Creation Tool",
"tags": [
"burger order creation"
]
}
],
"url": "https://burger-agent-109790610330.us-central1.run.app",
"version": "1.0.0"
}
این کارتهای عامل، بسیاری از مؤلفههای مهم، مانند مهارتهای عامل، قابلیتهای پخش جریانی، روشهای پشتیبانیشده، نسخه پروتکل و موارد دیگر را برجسته میکنند.
تمام این اطلاعات را میتوان برای توسعه یک مکانیسم ارتباطی مناسب مورد استفاده قرار داد تا کلاینت A2A بتواند به درستی ارتباط برقرار کند. روش پشتیبانی شده و مکانیسم احراز هویت، تضمین میکند که ارتباط به درستی برقرار شود و اطلاعات skills عامل میتواند در سیستم کلاینت A2A تعبیه شود تا زمینهای در مورد قابلیتها و مهارتهای عامل از راه دور که باید فراخوانی شوند، به عامل کلاینت ارائه دهد. فیلدهای دقیقتر برای این کارت عامل را میتوانید در این مستندات بیابید.
در کد ما، پیادهسازی agent card با استفاده از sdk پایتون A2A برقرار شده است، برای پیادهسازی، قطعه کد remote_seller_agents/burger_agent/main.py زیر را بررسی کنید.
...
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="create_burger_order",
name="Burger Order Creation Tool",
description="Helps with creating burger orders",
tags=["burger order creation"],
examples=["I want to order 2 classic cheeseburgers"],
)
agent_host_url = (
os.getenv("HOST_OVERRIDE")
if os.getenv("HOST_OVERRIDE")
else f"http://{host}:{port}/"
)
agent_card = AgentCard(
name="burger_seller_agent",
description="Helps with creating burger orders",
url=agent_host_url,
version="1.0.0",
defaultInputModes=BurgerSellerAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=BurgerSellerAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
...
میتوانیم چندین فیلد مانند موارد زیر را در آنجا ببینیم:
-
AgentCapabilities: اعلان توابع اختیاری اضافی که توسط سرویس عامل پشتیبانی میشوند، مانند قابلیت پخش جریانی و/یا پشتیبانی از اعلانهای فوری -
AgentSkill: ابزارها یا عملکردهایی که توسط عامل پشتیبانی میشوند -
Input/OutputModes: نوع ورودی/خروجی که پشتیبانی میشود -
Url: آدرسی برای ارتباط با نماینده
در این پیکربندی، ما یک آدرس اینترنتی میزبان پویا برای عامل ایجاد میکنیم تا جابجایی بین آزمایش محلی و استقرار ابری آسانتر شود، به همین دلیل است که باید متغیر HOST_OVERRIDE را در مرحله قبل اضافه کنیم.
صف وظایف و مجری عامل
سرور A2A ممکن است درخواستهای مربوط به عاملها یا کاربران مختلف را مدیریت کند و بتواند هر وظیفه را به طور کامل از هم جدا کند. برای تجسم بهتر زمینههای این موارد، میتوانید تصویر زیر را بررسی کنید.

بنابراین، هر سرور A2A باید بتواند وظایف ورودی را ردیابی کرده و اطلاعات مناسبی در مورد آن ذخیره کند. SDK A2A ماژولهایی را برای رسیدگی به این چالش در سرور A2A ارائه میدهد. ابتدا، میتوانیم منطق نحوه مدیریت درخواست ورودی را نمونهسازی کنیم. با ارثبری از کلاس انتزاعی AgentExecutor میتوانیم نحوه مدیریت اجرای وظیفه و لغو آن را کنترل کنیم. این پیادهسازی نمونه را میتوان در ماژول remote_seller_agents/burger_agent/agent_executor.py بررسی کرد (مسیر مشابه برای مورد فروشنده پیتزا).
...
class BurgerSellerAgentExecutor(AgentExecutor):
"""Burger Seller AgentExecutor."""
def __init__(self):
self.agent = BurgerSellerAgent()
async def execute(
self,
context: RequestContext,
event_queue: EventQueue,
) -> None:
query = context.get_user_input()
try:
result = self.agent.invoke(query, context.context_id)
print(f"Final Result ===> {result}")
parts = [Part(root=TextPart(text=str(result)))]
await event_queue.enqueue_event(
completed_task(
context.task_id,
context.context_id,
[new_artifact(parts, f"burger_{context.task_id}")],
[context.message],
)
)
except Exception as e:
print("Error invoking agent: %s", e)
raise ServerError(error=ValueError(f"Error invoking agent: {e}")) from e
async def cancel(
self, request: RequestContext, event_queue: EventQueue
) -> Task | None:
raise ServerError(error=UnsupportedOperationError())
...
در کد بالا، ما یک طرح پردازش اولیه پیادهسازی کردهایم که در آن، عامل هنگام دریافت درخواست مستقیماً فراخوانی میشود و رویدادهای وظیفه تکمیلشده را پس از اتمام فراخوانی ارسال میکند. با این حال، ما روش لغو را در اینجا پیادهسازی نکردیم زیرا به عنوان یک عملیات کوتاهمدت در نظر گرفته میشود.
پس از ساخت اجراکننده، میتوانیم مستقیماً از DefaultRequestHandler، InMemoryTaskStore و A2AStarletteApplication داخلی برای راهاندازی سرور HTTP استفاده کنیم. این پیادهسازی را میتوان در remote_seller_agents/burger_agent/__main__.py بررسی کرد.
...
request_handler = DefaultRequestHandler(
agent_executor=BurgerSellerAgentExecutor(),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=agent_card, http_handler=request_handler
)
uvicorn.run(server.build(), host=host, port=port)
...
این ماژول پیادهسازی مسیر /.well-known/agent.json را برای دسترسی به کارت عامل و همچنین نقطه پایانی POST را برای پشتیبانی از پروتکل A2A در اختیار شما قرار میدهد.
خلاصه
به طور خلاصه، تاکنون سرور A2A ما با استفاده از SDK پایتون مستقر شده است که میتواند از دو قابلیت زیر پشتیبانی کند:
- انتشار کارت عامل در مسیر
/.well-known/agent.json - مدیریت درخواستهای JSON-RPC با صفبندی وظایف در حافظه
نقطه ورود به هنگام شروع این قابلیتها را میتوان در اسکریپت __main__.py (در remote_seller_agents/burger_agent یا remote_seller_agents/pizza_agent ) بررسی کرد.
۹. 💡 [توضیح کد] استقرار موتور عامل
قطعه کد مربوط به مسئول خرید در فایل purchase_concierge/purchasing_agent.py به صورت زیر است:
from google.adk import Agent
...
def create_agent(self) -> Agent:
return Agent(
model="gemini-2.5-flash-lite",
name="purchasing_agent",
instruction=self.root_instruction,
before_model_callback=self.before_model_callback,
before_agent_callback=self.before_agent_callback,
description=(
"This purchasing agent orchestrates the decomposition of the user purchase request into"
" tasks that can be performed by the seller agents."
),
tools=[
self.send_task,
],
)
...
این عامل با استفاده از ADK ساخته شده و روی موتور عامل مستقر شده است.
موتور عامل هوش مصنوعی ورتکس (Vertex AI Agent Engine) مجموعهای از سرویسها است که توسعهدهندگان را قادر میسازد تا عاملهای هوش مصنوعی را در محیط تولید مستقر، مدیریت و مقیاسبندی کنند. این موتور، زیرساخت را برای مقیاسبندی عاملها در محیط تولید مدیریت میکند تا بتوانیم بر ایجاد برنامهها تمرکز کنیم. میتوانید اطلاعات بیشتر در این مورد را در این سند بخوانید. اگر قبلاً نیاز داشتیم فایلهای مورد نیاز برای استقرار سرویس عامل خود (مانند اسکریپت سرور اصلی و Dockerfile) را آماده کنیم، در این حالت میتوانیم عامل خود را مستقیماً از اسکریپت پایتون و بدون نیاز به توسعه سرویس backend خود با استفاده از ترکیبی از ADK و موتور عامل مستقر کنیم.
در این آموزش ما با استفاده از اسکریپت deploy_to_agent_engine.py که محتوای آن در زیر نشان داده شده است، عملیات استقرار را انجام میدهیم.
import vertexai
from vertexai.preview import reasoning_engines
from vertexai import agent_engines
from dotenv import load_dotenv
import os
from purchasing_concierge.agent import root_agent
load_dotenv()
PROJECT_ID = os.getenv("GOOGLE_CLOUD_PROJECT")
LOCATION = os.getenv("GOOGLE_CLOUD_LOCATION")
STAGING_BUCKET = os.getenv("STAGING_BUCKET")
vertexai.init(
project=PROJECT_ID,
location=LOCATION,
staging_bucket=STAGING_BUCKET,
)
adk_app = reasoning_engines.AdkApp(
agent=root_agent,
)
remote_app = agent_engines.create(
agent_engine=adk_app,
display_name="purchasing-concierge",
requirements=[
"google-cloud-aiplatform[adk,agent_engines]",
"a2a-sdk==0.2.16",
],
extra_packages=[
"./purchasing_concierge",
],
env_vars={
"GOOGLE_GENAI_USE_VERTEXAI": os.environ["GOOGLE_GENAI_USE_VERTEXAI"],
"PIZZA_SELLER_AGENT_URL": os.environ["PIZZA_SELLER_AGENT_URL"],
"BURGER_SELLER_AGENT_URL": os.environ["BURGER_SELLER_AGENT_URL"],
},
)
print(f"Deployed remote app resource: {remote_app.resource_name}")
این مراحل مورد نیاز برای استقرار عامل ADK ما در موتور عامل است. ابتدا باید یک شیء AdkApp از ADK root_agent خود ایجاد کنیم. سپس میتوانیم با ارائه شیء adk_app ، مشخص کردن الزامات در فیلد requirements ، مشخص کردن مسیر دایرکتوری عامل در extra_packages (همچنین میتوانید در صورت نیاز دایرکتوریها و فایلهای دیگری را در اینجا ارائه دهید) و ارائه متغیرهای env لازم، متد agent_engines.create را اجرا کنیم.
۱۰. 💡 [توضیح کد] مفهوم و پیادهسازی کلاینت A2A

تصویر بالا جریان معمول تعاملات A2A را نشان میدهد:
- کلاینت سعی خواهد کرد هر کارت عامل منتشر شده را در URL عامل راه دور ارائه شده در مسیر
/.well-known/agent.jsonپیدا کند. - سپس، در صورت لزوم، پیامی را به آن عامل ارسال میکند که حاوی پیام و پارامترهای فراداده لازم (مثلاً شناسه جلسه، پیشینه تاریخی و غیره) است. سرور این پیام را به عنوان یک وظیفه (task) که باید تکمیل شود، در نظر میگیرد.
- سرور A2A درخواست را پردازش میکند، اگر سرور از اعلانهای فوری پشتیبانی کند، قادر به انتشار برخی اعلانها در طول پردازش وظیفه نیز خواهد بود (این قابلیت خارج از محدوده این آزمایشگاه کد است)
- پس از اتمام، سرور A2A پاسخ مصنوع را به کلاینت ارسال میکند.
برخی از اشیاء اصلی برای تعاملات فوق این موارد هستند (جزئیات بیشتر را میتوانید اینجا بخوانید ):
- پیام: یک نوبت ارتباطی بین مشتری و یک نماینده از راه دور
- وظیفه : واحد کاری اساسی که توسط A2A مدیریت میشود و با یک شناسه منحصر به فرد مشخص میشود.
- مصنوع: خروجی (مثلاً یک سند، تصویر، دادههای ساختاریافته) تولید شده توسط عامل در نتیجه یک کار، متشکل از بخشهایی
- بخش (Part): کوچکترین واحد محتوا در یک پیام یا مصنوع. بخش میتواند متن، تصویر، ویدیو، فایل و غیره باشد.
کشف کارت
وقتی سرویس کلاینت A2A راهاندازی میشود، فرآیند معمول این است که سعی شود اطلاعات کارت اپراتور دریافت و ذخیره شود تا در صورت نیاز به راحتی به آن دسترسی پیدا کرد. در این آزمایشگاه کد، ما آن را روی before_agent_callback پیادهسازی میکنیم، میتوانید پیادهسازی را در purchasing_concierge/purchasing_agent.py مشاهده کنید. قطعه کد زیر را ببینید.
...
async def before_agent_callback(self, callback_context: CallbackContext):
if not self.a2a_client_init_status:
httpx_client = httpx.AsyncClient(timeout=httpx.Timeout(timeout=30))
for address in self.remote_agent_addresses:
card_resolver = A2ACardResolver(
base_url=address, httpx_client=httpx_client
)
try:
card = await card_resolver.get_agent_card()
remote_connection = RemoteAgentConnections(
agent_card=card, agent_url=card.url
)
self.remote_agent_connections[card.name] = remote_connection
self.cards[card.name] = card
except httpx.ConnectError:
print(f"ERROR: Failed to get agent card from : {address}")
agent_info = []
for ra in self.list_remote_agents():
agent_info.append(json.dumps(ra))
self.agents = "\n".join(agent_info)
...
در اینجا، ما سعی میکنیم با استفاده از ماژول داخلی A2ACardResolver کلاینت A2A به تمام کارتهای عامل موجود دسترسی پیدا کنیم، سپس اتصال مورد نیاز برای ارسال پیام به عامل را جمعآوری میکنیم، پس از آن نیز باید تمام عاملهای موجود و مشخصات آنها را در اعلان لیست کنیم تا عامل ما بداند که میتواند با این عاملها ارتباط برقرار کند.
ابزار اعلان و ارسال وظیفه
این ابزار و اعلانی است که ما در اینجا به نماینده ADK خود ارائه میدهیم.
...
def root_instruction(self, context: ReadonlyContext) -> str:
current_agent = self.check_active_agent(context)
return f"""You are an expert purchasing delegator that can delegate the user product inquiry and purchase request to the
appropriate seller remote agents.
Execution:
- For actionable tasks, you can use `send_task` to assign tasks to remote agents to perform.
- When the remote agent is repeatedly asking for user confirmation, assume that the remote agent doesn't have access to user's conversation context.
So improve the task description to include all the necessary information related to that agent
- Never ask user permission when you want to connect with remote agents. If you need to make connection with multiple remote agents, directly
connect with them without asking user permission or asking user preference
- Always show the detailed response information from the seller agent and propagate it properly to the user.
- If the remote seller is asking for confirmation, rely the confirmation question to the user if the user haven't do so.
- If the user already confirmed the related order in the past conversation history, you can confirm on behalf of the user
- Do not give irrelevant context to remote seller agent. For example, ordered pizza item is not relevant for the burger seller agent
- Never ask order confirmation to the remote seller agent
Please rely on tools to address the request, and don't make up the response. If you are not sure, please ask the user for more details.
Focus on the most recent parts of the conversation primarily.
If there is an active agent, send the request to that agent with the update task tool.
Agents:
{self.agents}
Current active seller agent: {current_agent["active_agent"]}
"""
...
async def send_task(self, agent_name: str, task: str, tool_context: ToolContext):
"""Sends a task to remote seller agent
This will send a message to the remote agent named agent_name.
Args:
agent_name: The name of the agent to send the task to.
task: The comprehensive conversation context summary
and goal to be achieved regarding user inquiry and purchase request.
tool_context: The tool context this method runs in.
Yields:
A dictionary of JSON data.
"""
if agent_name not in self.remote_agent_connections:
raise ValueError(f"Agent {agent_name} not found")
state = tool_context.state
state["active_agent"] = agent_name
client = self.remote_agent_connections[agent_name]
if not client:
raise ValueError(f"Client not available for {agent_name}")
session_id = state["session_id"]
task: Task
message_id = ""
metadata = {}
if "input_message_metadata" in state:
metadata.update(**state["input_message_metadata"])
if "message_id" in state["input_message_metadata"]:
message_id = state["input_message_metadata"]["message_id"]
if not message_id:
message_id = str(uuid.uuid4())
payload = {
"message": {
"role": "user",
"parts": [
{"type": "text", "text": task}
], # Use the 'task' argument here
"messageId": message_id,
"contextId": session_id,
},
}
message_request = SendMessageRequest(
id=message_id, params=MessageSendParams.model_validate(payload)
)
send_response: SendMessageResponse = await client.send_message(
message_request=message_request
)
print(
"send_response",
send_response.model_dump_json(exclude_none=True, indent=2),
)
if not isinstance(send_response.root, SendMessageSuccessResponse):
print("received non-success response. Aborting get task ")
return None
if not isinstance(send_response.root.result, Task):
print("received non-task response. Aborting get task ")
return None
return send_response.root.result
...
در اعلان، نام و توضیحات مربوط به تمام عوامل راه دور موجود را به مسئول خرید خود میدهیم و در ابزار self.send_task مکانیزمی را برای بازیابی کلاینت مناسب جهت اتصال به عامل و ارسال فرادادههای مورد نیاز با استفاده از شیء SendMessageRequest فراهم میکنیم.
پروتکلهای ارتباطی
تعریف وظیفه دامنهای است که متعلق به سرور A2A است. با این حال، از دیدگاه کلاینت A2A، آنها آن را به عنوان پیامی میبینند که به سرور ارسال میشود. این به سرور بستگی دارد که چگونه پیامهای دریافتی از کلاینت را به عنوان کدام وظیفه تعریف کند و آیا تکمیل وظیفه نیاز به تعامل از کلاینت دارد یا خیر. میتوانید جزئیات بیشتر در مورد چرخه حیات وظیفه را در این مستندات بخوانید. مفهوم سطح بالاتر این را میتوان در زیر مشاهده کرد:


این تبادل پیام -> وظیفه با استفاده از قالب بار داده (payload) بر روی استاندارد JSON-RPC پیادهسازی میشود، همانطور که در مثال زیر از پروتکل message/send نشان داده شده است:
{
# identifier for this request
"id": "abc123",
# version of JSON-RPC protocol
"jsonrpc": "2.0",
# method name
"method": "message/send",
# parameters/arguments of the method
"params": {
"message": "hi, what can you help me with?"
}
}
روشهای مختلفی وجود دارد، به عنوان مثال برای پشتیبانی از انواع مختلف ارتباط (مثلاً همگامسازی، پخش جریانی، ناهمگام) یا برای پیکربندی اعلانها برای وضعیت وظیفه. سرور A2A را میتوان به صورت انعطافپذیری پیکربندی کرد تا این استانداردهای تعریف وظیفه را مدیریت کند. جزئیات این روشها را میتوانید در این سند مطالعه کنید.
۱۱. چالش🎯
حالا، آیا میتوانید فایل لازم را آماده کنید و برنامه Gradio را در فضای ابری که خودتان اداره میکنید، مستقر کنید؟ وقت آن است که چالش را بپذیرید!
۱۲. 🧹 تمیز کردن
برای جلوگیری از تحمیل هزینه به حساب Google Cloud خود برای منابع استفاده شده در این codelab، این مراحل را دنبال کنید:
- در کنسول گوگل کلود، به صفحه مدیریت منابع بروید.
- در لیست پروژهها، پروژهای را که میخواهید حذف کنید انتخاب کنید و سپس روی «حذف» کلیک کنید.
- در کادر محاورهای، شناسه پروژه را تایپ کنید و سپس برای حذف پروژه، روی خاموش کردن کلیک کنید.
- همچنین میتوانید به Cloud Run و Agent Engine در کنسول بروید، سرویسی را که اخیراً مستقر کردهاید انتخاب کرده و حذف کنید.