نحوه استقرار یک برنامه gradio frontend که یک عامل ADK باطن را فراخوانی می کند، هر دو در Cloud Run اجرا می شوند

۱. مقدمه

نمای کلی

در این آزمایشگاه کد، شما یک عامل ADK را به عنوان یک سرویس backend در Cloud Run مستقر خواهید کرد و سپس یک رابط کاربری Gradio frontend را برای عامل ADK به عنوان سرویس Cloud Run دوم مستقر خواهید کرد. این آزمایشگاه کد نشان می‌دهد که چگونه احراز هویت را برای سرویس عامل ADK خود الزامی کنید و از سرویس frontend Gradio فراخوانی‌های احراز هویت شده را به آن انجام دهید.

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

  • نحوه استقرار یک عامل ADK در Cloud Run
  • نحوه استقرار یک برنامه گرادیو در Cloud Run
  • نحوه برقراری تماس‌های احراز هویت شده سرویس به سرویس در Cloud Run

۲. فعال کردن APIها

ابتدا، پروژه Google Cloud خود را تنظیم کنید.

gcloud config set project <YOUR_PROJECT_ID>

شما می‌توانید پروژه گوگل کلود خود را با اجرای دستور زیر تأیید کنید:

gcloud config get-value project

این آزمایشگاه کد نیاز به فعال کردن API های زیر دارد:

gcloud services enable run.googleapis.com \
    compute.googleapis.com \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    artifactregistry.googleapis.com \
    aiplatform.googleapis.com

۳. تنظیمات و الزامات

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

ابتدا، متغیرهای محیطی را برای این آزمایشگاه کد تنظیم کنید که در سراسر این آزمایشگاه کد استفاده خواهند شد.

export PROJECT_ID=<YOUR_PROJECT_ID>
export REGION=<YOUR_REGION>

export SERVICE_ACCOUNT_ADK="adk-agent-cr"
export SERVICE_ACCOUNT_ADDRESS_ADK=$SERVICE_ACCOUNT_ADK@$PROJECT_ID.iam.gserviceaccount.com

export SERVICE_ACCOUNT_GRADIO="adk-agent-gradio"
export SERVICE_ACCOUNT_ADDRESS_GRADIO=$SERVICE_ACCOUNT_GRADIO@$PROJECT_ID.iam.gserviceaccount.com

export AGENT_APP_NAME="multi_tool_agent"

در مرحله بعد، حساب کاربری سرویس را برای عامل ADK ایجاد کنید.

gcloud iam service-accounts create $SERVICE_ACCOUNT_ADK \
--display-name="Service account for adk agent on cloud run"

و به حساب سرویس ADK نقش "Vertex AI User" را اعطا کنید.

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

اکنون، حساب کاربری سرویس را برای رابط کاربری Gradio ایجاد کنید.

gcloud iam service-accounts create $SERVICE_ACCOUNT_GRADIO \
  --display-name="Service account for gradio frontend cloud run"

و به رابط کاربری Gradio، نقش فراخوانی Cloud Run را اعطا کنید، که به آن اجازه می‌دهد تا عامل ADK میزبانی شده در Cloud Run را فراخوانی کند.

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$SERVICE_ACCOUNT_ADDRESS_GRADIO" \
  --role="roles/run.invoker"

۴. یک برنامه ADK ایجاد کنید

در مرحله بعد، کد مربوط به برنامه شروع سریع ADK را ایجاد خواهید کرد.

نکته: در پایان تمرین، ساختار فایل شما باید به شکل زیر باشد:

- codelab-gradio-adk  <-- you'll deploy the ADK agent from here
  - gradio-frontend  <-- you'll deploy the gradio app from here
    - app.py
    - requirements.txt
  - multi_tool_agent
    - __init__.py
    - agent.py
    - requirements.txt

ابتدا، یک دایرکتوری برای کل codelab ایجاد کنید.

mkdir codelab-gradio-adk
cd codelab-gradio-adk

اکنون، یک دایرکتوری برای سرویس ADK agent ایجاد کنید.

mkdir multi_tool_agent && cd multi_tool_agent

یک فایل __init__.py با محتوای زیر ایجاد کنید:

from . import agent

یک فایل requirements.txt ایجاد کنید:

google-adk

فایلی به نام agent.py ایجاد کنید.

import datetime
from zoneinfo import ZoneInfo
from google.adk.agents import Agent

def get_weather(city: str) -> dict:
    """Retrieves the current weather report for a specified city.

    Args:
        city (str): The name of the city for which to retrieve the weather report.

    Returns:
        dict: status and result or error msg.
    """
    if city.lower() == "new york":
        return {
            "status": "success",
            "report": (
                "The weather in New York is sunny with a temperature of 25 degrees"
                " Celsius (77 degrees Fahrenheit)."
            ),
        }
    else:
        return {
            "status": "error",
            "error_message": f"Weather information for '{city}' is not available.",
        }


def get_current_time(city: str) -> dict:
    """Returns the current time in a specified city.

    Args:
        city (str): The name of the city for which to retrieve the current time.

    Returns:
        dict: status and result or error msg.
    """

    if city.lower() == "new york":
        tz_identifier = "America/New_York"
    else:
        return {
            "status": "error",
            "error_message": (
                f"Sorry, I don't have timezone information for {city}."
            ),
        }

    tz = ZoneInfo(tz_identifier)
    now = datetime.datetime.now(tz)
    report = (
        f'The current time in {city} is {now.strftime("%Y-%m-%d %H:%M:%S %Z%z")}'
    )
    return {"status": "success", "report": report}


root_agent = Agent(
    name="weather_time_agent",
    model="gemini-2.5-flash",
    description=(
        "Agent to answer questions about the time and weather in a city."
    ),
    instruction=(
        "You are a helpful agent who can answer user questions about the time and weather in a city."
    ),
    tools=[get_weather, get_current_time],
)

۵. عامل ADK را مستقر کنید

در این بخش، شما عامل ADK را در Cloud Run مستقر خواهید کرد. سپس با استفاده از رابط کاربری وب توسعه‌دهندگان که توسط ADK ارائه شده است، استقرار را تأیید خواهید کرد. در نهایت، به فراخوانی‌های احراز هویت شده برای این سرویس نیاز خواهید داشت.

به پوشه والد بروید.

نکته: کد عامل ADK باید پوشه multi_tool_agent را به عنوان پوشه ریشه خود داشته باشد.

cd ..

ابتدا، سرویس Cloud Run را ایجاد کنید:

نکته: همانطور که در مرحله بعدی نشان داده شده است، گزینه --with_ui برای تست با رابط کاربری Dev اختیاری است:

نکته: دستور -- به شما امکان می‌دهد تا از طریق پرچم‌های خط فرمان به دستور gcloud run deploy که در زیر آن قرار دارد، دسترسی پیدا کنید.

نکته: دستور uvx --from یک دستور از بسته google-adk را اجرا می‌کند. uvx یک محیط مجازی موقت ایجاد می‌کند، google-adk را در آن نصب می‌کند، دستور مشخص شده را اجرا می‌کند و سپس محیط را از بین می‌برد.

uvx --from google-adk \
adk deploy cloud_run \
    --project=$PROJECT_ID \
    --region=$REGION \
    --service_name=adk-agent-cr \
    --with_ui \
    ./multi_tool_agent \
    -- \
    --service-account=$SERVICE_ACCOUNT_ADDRESS_ADK \
    --allow-unauthenticated

در مرحله بعد، URL را به عنوان یک متغیر env ذخیره کنید که در بخش دوم این آزمایشگاه کد از آن استفاده خواهید کرد.

AGENT_SERVICE_URL=$(gcloud run services describe adk-agent-cr --region $REGION --format 'value(status.url)')

حالا، عامل را امتحان کنید

آدرس اینترنتی سرویس را در مرورگر وب خود باز کنید و بپرسید، « tell me about the weather in new york ». باید پاسخی مشابه «آب و هوای نیویورک آفتابی با دمای ۲۵ درجه سانتیگراد (۷۷ درجه فارنهایت) است» ببینید.

در آخر، عامل را ایمن کنید

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

gcloud run services remove-iam-policy-binding adk-agent-cr \
  --member="allUsers" \
  --role="roles/run.invoker" \
  --region=$REGION

۶. یک رابط کاربری گرادیود (gradio front-end) راه‌اندازی کنید

در این مرحله، یک ظاهر گرادیویی برای عامل ADK خود ایجاد خواهید کرد.

توجه: می‌توانید برنامه‌ی گرادیو را در همان سرویسی که عامل ADK در آن قرار دارد، داشته باشید. این codelab دو سرویس جداگانه ارائه می‌دهد تا نحوه‌ی برقراری تماس‌های احراز هویت شده‌ی سرویس به سرویس در Cloud Run را نشان دهد.

ابتدا، یک برنامه در کنار پوشه multi_tool_agent ایجاد کنید.

mkdir gradio-frontend && cd gradio-frontend

در مرحله بعد، یک فایل requirements.txt ایجاد کنید که شامل موارد زیر باشد:

gradio
requests
google-auth

حالا، یک فایل app.py ایجاد کنید.

import gradio as gr
import requests
import json
import uuid
import os
import google.auth.transport.requests
import google.oauth2.id_token

# https://weather-time-service2-392295011265.us-west4.run.app
BASE_URL = os.environ.get("AGENT_SERVICE_URL")

# multi_tool_agent
APP_NAME = os.environ.get("AGENT_APP_NAME")

# Generate a unique user ID for each session of the Gradio app
USER_ID = f"gradio-user-{uuid.uuid4()}"

# API Endpoints
CREATE_SESSION_URL = f"{BASE_URL}/apps/{APP_NAME}/users/{USER_ID}/sessions"
RUN_SSE_URL = f"{BASE_URL}/run_sse"

def get_id_token():
    """Get an ID token to authenticate with the other Cloud Run service."""
    audience = BASE_URL
    request = google.auth.transport.requests.Request()
    id_token = google.oauth2.id_token.fetch_id_token(request, audience)
    return id_token

def create_session() -> str | None:
    """Creates a new session and returns the session ID."""
    try:
        id_token = get_id_token()
        headers = {"Authorization": f"Bearer {id_token}"}
        response = requests.post(CREATE_SESSION_URL, headers=headers)
        response.raise_for_status()
        return response.json().get("id")
    except Exception as e:
        print(f"Error creating session: {e}")
        return None

def query_agent(prompt: str):
    """Sends a prompt to the agent and returns the streamed response."""
    session_id = create_session()
    if not session_id:
        return "Error: Could not create a session."

    id_token = get_id_token()
    headers = {
        "Content-Type": "application/json",
        "Accept": "text/event-stream",
        "Authorization": f"Bearer {id_token}",
    }
    payload = {
        "app_name": APP_NAME,
        "user_id": USER_ID,
        "session_id": session_id,
        "new_message": {"role": "user", "parts": [{"text": prompt}]},
        "streaming": True
    }

    full_response = ""
    try:
        with requests.post(RUN_SSE_URL, headers=headers, json=payload, stream=True) as response:
            response.raise_for_status()
            for chunk in response.iter_lines():
                if chunk and chunk.decode('utf-8').startswith('data:'):
                    json_data = chunk.decode('utf-8')[len('data:'):].strip()
                    try:
                        data = json.loads(json_data)
                        text = data.get("content", {}).get("parts", [{}])[0].get("text", "")
                        if text:
                            full_response = text
                    except json.JSONDecodeError:
                        pass # Ignore chunks that are not valid JSON
        return full_response
    except requests.exceptions.RequestException as e:
        return f"An error occurred: {e}"

iface = gr.Interface(
    fn=query_agent,
    inputs=gr.Textbox(lines=2, placeholder="e.g., What's the weather in new york?"),
    outputs="text",
    title="Weather and Time Agent",
    description="Ask a question about the weather or time in a specific location.",
)

if __name__ == "__main__":
    iface.launch()

۷. اپلیکیشن گرادیوی خود را مستقر و آزمایش کنید

در این مرحله، برنامه front-end gradio را روی Cloud Run مستقر خواهید کرد.

مطمئن شوید که در دایرکتوری برنامه‌ی gradio هستید.

pwd

شما باید codelab-gradio-adk/gradio-frontend را ببینید.

حالا برنامه گرادیوی خود را مستقر کنید.

توجه: اگرچه این سرویس frontend گرادیو یک وب‌سایت عمومی است، اما سرویس backend نیاز به احراز هویت دارد. برای روشن شدن دلیل نیاز به انجام این کار، می‌توانید احراز هویت کاربر (مثلاً Firebase Auth) را به این سرویس frontend اضافه کنید و سپس فقط به کاربرانی که وارد سیستم شده‌اند اجازه دهید تا سرویس backend را فراخوانی کنند.

gcloud run deploy my-adk-gradio-frontend \
--source . \
--region $REGION \
--allow-unauthenticated \
--set-env-vars AGENT_SERVICE_URL=$AGENT_SERVICE_URL,AGENT_APP_NAME=$AGENT_APP_NAME \
--service-account=$SERVICE_ACCOUNT_ADDRESS_GRADIO

پس از استقرار، بپرسید what's the weather in new york? و باید پاسخی مشابه The weather in New York is sunny with a temperature of 25 degrees Celsius (77 degrees Fahrenheit).

۸. تبریک می‌گویم!

تبریک می‌گویم که آزمایشگاه کد را تمام کردید!

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

آنچه ما پوشش داده‌ایم

  • نحوه استقرار یک عامل ADK در Cloud Run
  • نحوه استقرار یک برنامه گرادیو در Cloud Run
  • نحوه برقراری تماس‌های احراز هویت شده سرویس به سرویس در Cloud Run

۹. تمیز کردن

برای جلوگیری از هزینه‌های ناخواسته، به عنوان مثال، اگر سرویس‌های Cloud Run سهواً بیشتر از تخصیص فراخوانی ماهانه Cloud Run شما در سطح رایگان فراخوانی شوند، می‌توانید سرویس Cloud Run که در مرحله 6 ایجاد کرده‌اید را حذف کنید.

برای حذف سرویس‌های Cloud Run، به کنسول Cloud Run در آدرس https://console.cloud.google.com/run بروید و سرویس‌های my-adk-gradio-frontend و adk-agent-cr را حذف کنید.

برای حذف کل پروژه، به مدیریت منابع بروید، پروژه‌ای را که در مرحله ۲ ایجاد کرده‌اید انتخاب کنید و حذف را انتخاب کنید. اگر پروژه را حذف کنید، باید پروژه‌ها را در Cloud SDK خود تغییر دهید. می‌توانید با اجرای gcloud projects list لیست تمام پروژه‌های موجود را مشاهده کنید.