Cách triển khai một ứng dụng giao diện người dùng gradio gọi một tác nhân ADK phụ trợ, cả hai đều chạy trên Cloud Run

1. Giới thiệu

Tổng quan

Trong lớp học lập trình này, bạn sẽ triển khai một tác nhân ADK đến Cloud Run dưới dạng dịch vụ phụ trợ, sau đó triển khai một giao diện người dùng gradio cho tác nhân ADK dưới dạng dịch vụ Cloud Run thứ hai. Lớp học lập trình này hướng dẫn cách yêu cầu xác thực cho dịch vụ tác nhân ADK và thực hiện các lệnh gọi đã xác thực đến dịch vụ đó từ dịch vụ giao diện người dùng gradio.

Kiến thức bạn sẽ học được

  • Cách triển khai một tác nhân ADK lên Cloud Run
  • Cách triển khai ứng dụng gradio lên Cloud Run
  • Cách thực hiện các cuộc gọi được xác thực giữa các dịch vụ trong Cloud Run

2. Bật API

Trước tiên, hãy thiết lập dự án Google Cloud.

gcloud config set project <YOUR_PROJECT_ID>

Bạn có thể xác nhận dự án Google Cloud bằng cách chạy lệnh sau:

gcloud config get-value project

Lớp học lập trình này yêu cầu bạn bật các API sau:

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

3. Thiết lập và yêu cầu

Trong phần này, bạn sẽ tạo một vài tài khoản dịch vụ và cấp cho các tài khoản đó vai trò IAM thích hợp. Mỗi dịch vụ chạy trên đám mây sẽ có một Tài khoản dịch vụ riêng.

Trước tiên, hãy thiết lập các biến môi trường cho lớp học lập trình này. Các biến này sẽ được dùng trong suốt lớp học lập trình.

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"

Tiếp theo, hãy tạo tài khoản dịch vụ cho tác nhân ADK.

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

Và cấp cho tài khoản dịch vụ ADK vai trò "Người dùng Vertex AI"

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

Bây giờ, hãy tạo tài khoản dịch vụ cho giao diện người dùng Gradio

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

Đồng thời cấp cho giao diện người dùng Gradio vai trò người gọi Cloud Run. Vai trò này sẽ cho phép giao diện người dùng gọi tác nhân ADK được lưu trữ trên Cloud Run.

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

4. Tạo ứng dụng ADK

Trong bước tiếp theo, bạn sẽ tạo mã cho ứng dụng khởi động nhanh ADK.

Lưu ý: khi kết thúc lớp học, cấu trúc tệp của bạn sẽ có dạng như sau:

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

Trước tiên, hãy tạo một thư mục cho lớp học lập trình tổng thể

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

Bây giờ, hãy tạo một thư mục cho dịch vụ tác nhân ADK.

mkdir multi_tool_agent && cd multi_tool_agent

Tạo tệp __init__.py có nội dung sau:

from . import agent

Tạo tệp requirements.txt:

google-adk

Tạo một tệp có tên là 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],
)

5. Triển khai tác nhân ADK

Trong phần này, bạn sẽ triển khai tác nhân ADK cho Cloud Run. Sau đó, bạn sẽ xác minh rằng quá trình triển khai đã hoạt động bằng giao diện người dùng web dành cho nhà phát triển do ADK cung cấp. Cuối cùng, bạn sẽ cần các lệnh gọi đã xác thực đến dịch vụ này.

Chuyển đến thư mục mẹ.

LƯU Ý: Mã tác nhân ADK phải có thư mục multi_tool_agent làm thư mục gốc.

cd ..

Trước tiên, hãy tạo dịch vụ Cloud Run:

LƯU Ý: --with_ui là không bắt buộc để kiểm thử bằng Giao diện người dùng dành cho nhà phát triển, như minh hoạ trong bước sắp tới:

LƯU Ý: lệnh -- cho phép bạn truyền các cờ dòng lệnh đến lệnh gcloud run deploy cơ bản.

LƯU Ý: uvx --from sẽ thực thi một lệnh từ gói google-adk. uvx sẽ tạo một môi trường ảo tạm thời, cài đặt google-adk vào đó, chạy lệnh đã chỉ định, sau đó huỷ môi trường.

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

Tiếp theo, hãy lưu URL này dưới dạng một biến môi trường mà bạn sẽ dùng trong phần thứ hai của lớp học lập trình này

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

Bây giờ, hãy thử tác nhân

Mở URL dịch vụ trong trình duyệt web rồi hỏi tell me about the weather in new york. Bạn sẽ thấy một câu trả lời tương tự như "Thời tiết ở New York là trời nắng với nhiệt độ 25 độ C (77 độ F)."

Cuối cùng, hãy bảo mật nhân viên hỗ trợ

Bây giờ, hãy bảo mật quyền truy cập vào tác nhân. Trong phần tiếp theo, bạn sẽ triển khai một dịch vụ Cloud Run thực hiện một lệnh gọi đã xác thực đến dịch vụ phụ trợ này.

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

6. Triển khai giao diện người dùng gradio

Trong bước này, bạn sẽ tạo một giao diện người dùng gradio cho tác nhân ADK

Lưu ý: Bạn có thể có ứng dụng gradio trong cùng một dịch vụ với tác nhân ADK. Lớp học lập trình này cung cấp 2 dịch vụ riêng biệt để cho biết cách thực hiện các lệnh gọi được xác thực từ dịch vụ đến dịch vụ trong Cloud Run.

Trước tiên, hãy tạo một ứng dụng cùng với thư mục multi_tool_agent

mkdir gradio-frontend && cd gradio-frontend

Tiếp theo, hãy tạo một tệp requirements.txt chứa nội dung sau

gradio
requests
google-auth

Bây giờ, hãy tạo một tệp 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()

7. Triển khai và kiểm thử ứng dụng gradio

Trong bước này, bạn sẽ triển khai ứng dụng gradio giao diện người dùng cho Cloud Run.

Đảm bảo bạn đang ở trong thư mục ứng dụng gradio.

pwd

Bạn sẽ thấy biểu tượng codelab-gradio-adk/gradio-frontend

Bây giờ, hãy triển khai ứng dụng gradio của bạn.

Lưu ý: mặc dù dịch vụ giao diện người dùng gradio này là một trang web có sẵn công khai, nhưng dịch vụ phụ trợ yêu cầu xác thực. Để minh hoạ lý do bạn nên làm việc này, bạn có thể thêm tính năng xác thực người dùng (ví dụ: Xác thực Firebase) vào dịch vụ giao diện người dùng này, sau đó chỉ cho phép những người dùng đã đăng nhập thực hiện các lệnh gọi đến dịch vụ phụ trợ.

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

sau khi triển khai, hãy hỏi what's the weather in new york? và bạn sẽ nhận được câu trả lời tương tự như The weather in New York is sunny with a temperature of 25 degrees Celsius (77 degrees Fahrenheit).

8. Xin chúc mừng!

Chúc mừng bạn đã hoàn thành lớp học lập trình này!

Bạn nên tham khảo tài liệu về việc lưu trữ các ứng dụng và tác nhân AI.

Nội dung đã đề cập

  • Cách triển khai một tác nhân ADK lên Cloud Run
  • Cách triển khai ứng dụng gradio lên Cloud Run
  • Cách thực hiện các cuộc gọi được xác thực giữa các dịch vụ trong Cloud Run

9. Dọn dẹp

Để tránh bị tính phí ngoài ý muốn, chẳng hạn như nếu các dịch vụ Cloud Run được gọi nhiều lần hơn hạn mức gọi Cloud Run hằng tháng của bạn trong bậc miễn phí, bạn có thể xoá dịch vụ Cloud Run mà bạn đã tạo ở Bước 6.

Để xoá các dịch vụ Cloud Run, hãy truy cập vào Cloud Run Cloud Console tại https://console.cloud.google.com/run rồi xoá các dịch vụ my-adk-gradio-frontendadk-agent-cr.

Để xoá toàn bộ dự án, hãy chuyển đến phần Quản lý tài nguyên, chọn dự án bạn đã tạo ở Bước 2 rồi chọn Xoá. Nếu xoá dự án, bạn sẽ cần thay đổi dự án trong Cloud SDK. Bạn có thể xem danh sách tất cả các dự án có sẵn bằng cách chạy gcloud projects list.