Agen dalam Skala Besar: Arsitektur Multi-Agen dengan A2A Protocol di Integrasi ADK dan Agent Runtime

1. Pengantar

Seiring dengan makin banyaknya tanggung jawab yang diemban agen AI, satu agen yang melakukan semuanya akan sulit dipertahankan, diskalakan, dan dikembangkan. Kemampuan yang berbeda sering kali memerlukan strategi deployment, siklus update, atau bahkan tim yang berbeda untuk menanganinya.

  • Protokol A2A (Agent2Agent) menyelesaikan sisi komunikasi — menstandardisasi cara agen menemukan kemampuan satu sama lain dan berkolaborasi di berbagai framework dan organisasi.
  • Runtime Platform Agen Gemini Enterprise menyelesaikan sisi deployment — platform serverless yang terkelola sepenuhnya yang menghosting agen Anda dengan dukungan A2A bawaan, penskalaan otomatis, endpoint yang aman, sesi persisten, dan pengelolaan infrastruktur nol.

Bersama-sama, keduanya memungkinkan Anda membangun agen khusus, men-deploy-nya sebagai layanan A2A yang dapat ditemukan, dan menyusunnya ke dalam sistem multi-agen.

Yang akan Anda build

Agen Reservasi yang mengelola pemesanan meja restoran (membuat, memeriksa, dan membatalkan) menggunakan status sesi ADK yang dikelola oleh Sesi Platform Agen Gemini Enterprise. Anda men-deploy agen ini ke Runtime Platform Agen Gemini Enterprise yang membuatnya dapat ditemukan melalui kartu agen protokol A2A. Kemudian, Anda mengupgrade agen pemandu restoran Foodie Finds (dari codelab prasyarat, jangan khawatir jika Anda belum membuka codelab tersebut–kami telah menyiapkan repositori awal untuk Anda) agar dapat menggunakan Agen Reservasi sebagai sub-agen A2A jarak jauh. Hasilnya: sistem multi-agen di mana pengorkestrasi merutekan kueri menu ke MCP Toolbox dan permintaan reservasi ke agen A2A jarak jauh.

143fadef342e67a6.jpeg

Yang akan Anda pelajari

  • Membangun agen ADK yang menggunakan layanan sesi terkelola untuk mengelola data reservasi
  • Mengekspos agen ADK sebagai server A2A dengan kartu dan kemampuan agen
  • Men-deploy agen A2A ke Gemini Enterprise Agent Runtime
  • Menggunakan agen A2A jarak jauh dari agen ADK lain menggunakan RemoteA2aAgent dan menangani permintaan yang diautentikasi
  • Menguji sistem multi-agen secara inkremental: A2A lokal, A2A yang di-deploy, integrasi parsial, deployment penuh

Prasyarat

2. Penyiapan Lingkungan - Melanjutkan dari codelab sebelumnya

Narasi yang kami berikan dalam codelab ini sebenarnya adalah kelanjutan dari codelab prasyarat ini: RAG Agentik dengan ADK, MCP Toolbox, dan Cloud SQL . Anda dapat melanjutkan pekerjaan dari codelab sebelumnya

Kita dapat mulai membangun di direktori kerja codelab sebelumnya ( direktori kerja harus berupa build-agent-adk-toolbox-cloudsql). Untuk menghindari kebingungan, mari ganti nama direktori dengan nama direktori yang sama dengan yang kita gunakan saat memulai dari awal

mv ~/build-agent-adk-toolbox-cloudsql ~/adk-a2a-agent-runtime-starter
cloudshell workspace ~/adk-a2a-agent-runtime-starter && cd ~/adk-a2a-agent-runtime-starter
source .env

Pastikan file kunci dari codelab sebelumnya sudah ada:

echo "--- Restaurant Agent ---"
cat restaurant_agent/agent.py | head -5
echo ""
echo "--- Toolbox Config ---"
cat tools.yaml | head -5

Anda akan melihat file restaurant_agent/agent.py dengan impor LlmAgent, dan tools.yaml dengan konfigurasi Toolbox Anda.

Selanjutnya, mari kita inisialisasi ulang lingkungan python kita

rm -rf .venv
uv sync

Selain itu, pastikan database telah diisi dan siap:

uv run python scripts/verify_seed.py

Jika Anda mengikuti setiap detail pengujian dari codelab sebelumnya, Anda mungkin akan melihat output seperti ini

Menu Items: 16/15
Embeddings: 16/15

✗ Database not ready

Tidak apa-apa. Pemeriksaan database tidak memperhitungkan data tambahan yang Anda masukkan dari pemeriksaan penyerapan data. Selama Anda memiliki >=15 data, semuanya akan baik-baik saja.

Aktifkan API yang Diperlukan

Selanjutnya, kita perlu memastikan bahwa kita mengaktifkan API yang diperlukan untuk berinteraksi dengan Platform Agen Gemini Enterprise

gcloud services enable \
  cloudresourcemanager.googleapis.com

Anda seharusnya sudah memiliki file dan infrastruktur yang diperlukan untuk melanjutkan ke bagian berikutnya: A2A Protocol and Gemini Enterprise Agent Runtime.

3. Penyiapan Lingkungan - Mulai dari awal dengan repo starter

Langkah ini akan menyiapkan lingkungan Cloud Shell, mengonfigurasi project Google Cloud, dan meng-clone repositori awal.

Buka Cloud Shell

Buka Cloud Shell di browser Anda. Cloud Shell menyediakan lingkungan yang telah dikonfigurasi sebelumnya dengan semua alat yang Anda perlukan untuk codelab ini. Klik Authorize saat diminta untuk

Kemudian, klik "View" -> "Terminal" untuk membuka terminal.Antarmuka Anda akan terlihat mirip dengan ini

86307fac5da2f077.png

Ini akan menjadi antarmuka utama kita, IDE di atas, terminal di bawah

Menyiapkan direktori kerja Anda

Clone repositori awal, semua kode yang Anda tulis dalam codelab ini ada di sini:

rm -rf ~/adk-a2a-agent-runtime-starter
git clone https://github.com/alphinside/adk-a2a-agent-runtime-starter.git
cloudshell workspace ~/adk-a2a-agent-runtime-starter && cd ~/adk-a2a-agent-runtime-starter

Buat file .env dari template yang disediakan:

cp .env.example .env

Untuk menyederhanakan penyiapan project di terminal, download skrip penyiapan project ini ke direktori kerja Anda:

curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh

Jalankan skrip. Skrip ini memverifikasi akun penagihan uji coba Anda, membuat project baru (atau memvalidasi project yang ada), menyimpan project ID Anda ke file .env di direktori saat ini, dan menetapkan project aktif di gcloud.

bash setup_verify_trial_project.sh && source .env

Skrip akan:

  1. Pastikan Anda memiliki akun penagihan uji coba yang aktif
  2. Periksa project yang ada di .env (jika ada)
  3. Buat project baru atau gunakan kembali project yang sudah ada
  4. Menautkan akun penagihan uji coba ke project Anda
  5. Simpan project ID ke .env
  6. Menetapkan project sebagai project gcloud yang aktif

Pastikan project disetel dengan benar dengan memeriksa teks berwarna kuning di samping direktori kerja Anda di perintah terminal Cloud Shell. Project ID Anda akan ditampilkan.

5c515e235ee1179f.png

Aktifkan API yang Diperlukan

Selanjutnya, kita perlu memastikan bahwa kita mengaktifkan API yang diperlukan untuk berinteraksi dengan Platform Agen Gemini Enterprise

gcloud services enable \
  aiplatform.googleapis.com \
  cloudresourcemanager.googleapis.com

Penyiapan Infrastruktur Awal

Pertama, kita perlu menginstal dependensi Python menggunakan uv, yang merupakan pengelola paket dan project Python cepat yang ditulis dalam Rust ( dokumentasi uv). Codelab ini menggunakannya untuk kecepatan dan kesederhanaan dalam memelihara project Python

uv sync

Kemudian, jalankan skrip penyiapan lengkap, yang membuat instance Cloud SQL, mengisi data, dan men-deploy layanan Toolbox yang akan bertindak sebagai status awal agen restoran kita

bash scripts/full_setup.sh > logs/full_setup.log 2>&1 &

4. Konsep: Protokol Agent2Agent (A2A) dan Runtime Agen Gemini Enterprise

Sebelum membangun, mari luangkan waktu sejenak untuk memahami dua teknologi utama yang disajikan dalam codelab ini untuk menskalakan aplikasi agentik kita.

Protokol Agent2Agent (A2A)

Protokol Agent2Agent (A2A) adalah standar terbuka yang dirancang untuk memungkinkan komunikasi dan kolaborasi yang lancar antar-agen AI. Jika MCP (Model Context Protocol) menghubungkan agen ke alat dan data, A2A menghubungkan agen ke agen lain — sehingga memungkinkan mereka menemukan kemampuan masing-masing, mendelegasikan tugas, dan berkolaborasi di seluruh framework dan organisasi.

5586b67d0437d79f.png

Perbedaan utama antara membungkus agen sebagai alat (melalui MCP) vs. mengeksposnya melalui A2A: alat tidak memiliki status dan melakukan fungsi tunggal, sedangkan agen A2A dapat melakukan penalaran, mempertahankan status, dan menangani interaksi multi-turn seperti negosiasi atau klarifikasi. Agen yang diekspos melalui A2A mempertahankan kemampuan penuhnya, bukan direduksi menjadi panggilan fungsi.

A2A mendefinisikan tiga konsep inti:

  1. Kartu Agen — dokumen JSON yang menjelaskan fungsi agen, kemampuannya, dan endpoint-nya. Agen lain mengambil kartu ini untuk menemukan kemampuan.
  2. Pesan — permintaan pengguna atau agen yang dikirim ke endpoint A2A, yang memicu tugas.
  3. Tugas — unit kerja dengan siklus proses (dikirimkan → sedang dikerjakan → selesai/gagal) dan artefak yang berisi hasilnya.

e7e3224d05b725f0.jpeg

Untuk mengetahui informasi lebih lanjut, lihat Apa yang dimaksud dengan A2A?

Runtime Platform Agen Gemini Enterprise

Agent Runtime adalah layanan yang terkelola sepenuhnya di Google Cloud untuk men-deploy, menskalakan, dan mengelola agen AI dalam produksi dengan fitur keamanan Enterprise (misalnya, Kontrol Layanan VPC, CMEK). Layanan ini menangani infrastruktur sehingga Anda dapat berfokus pada logika agen.

8ecbfbce8f0b9557.png

Runtime Agen menyediakan:

  • Deployment terkelola — men-deploy agen yang dibuat dengan ADK, LangGraph, atau framework Python apa pun dengan satu panggilan SDK
  • Hosting A2A — men-deploy agen sebagai endpoint yang kompatibel dengan A2A dengan penayangan kartu agen otomatis dan akses yang diautentikasi
  • Sesi persistenVertexAiSessionService menyimpan histori dan status percakapan di seluruh permintaan
  • Penskalaan otomatis — diskalakan dari nol untuk menangani traffic, tanpa pengelolaan infrastruktur
  • Kemampuan observasi — pelacakan, logging, dan pemantauan bawaan melalui stack kemampuan observasi Google Cloud
  • dan banyak fitur lainnya, lihat dokumentasi ini untuk mengetahui detailnya

Dalam codelab ini, Anda akan men-deploy agen reservasi ke Agent Runtime. Proses deployment akan melakukan serialisasi (pickle) kode agen Anda dan menguploadnya. Agent Runtime menyediakan endpoint serverless yang melayani protokol A2A — agen (atau klien) lain berinteraksi dengannya melalui panggilan HTTP standar, yang diautentikasi dengan kredensial Google Cloud.

5. Membangun Agen Pemesanan

Langkah ini membuat agen ADK baru yang menangani reservasi restoran menggunakan status sesi. Agen mendukung tiga operasi — buat, periksa, dan batalkan — dengan nomor telepon sebagai kunci penelusuran. Semua data reservasi ada di status sesi ADK

Membuat kerangka agen

Gunakan adk create untuk membuat struktur direktori agen dengan konfigurasi model dan project yang benar:

source .env
uv run adk create reservation_agent \
    --model gemini-2.5-flash \
    --project ${GOOGLE_CLOUD_PROJECT} \
    --region ${GOOGLE_CLOUD_LOCATION}

Tindakan ini akan membuat direktori reservation_agent/ dengan __init__.py, agent.py, dan .env yang telah dikonfigurasi sebelumnya untuk model Gemini di Agent Platform.

adk-a2a-agent-runtime-starter/
├── reservation_agent/
│   ├── __init__.py
│   ├── agent.py
│   └── .env
├── logs
├── scripts
└── ...

Selanjutnya, perbarui kode agen

Tulis kode agen

Buka file agen yang dihasilkan:

cloudshell edit reservation_agent/agent.py

Kemudian, ganti konten dengan kode berikut:

# reservation_agent/agent.py
from google.adk.agents import LlmAgent
from google.adk.tools import ToolContext

# App-scoped state prefix ensures reservations persist across all sessions.
# See https://adk.dev/sessions/state/ for state scope details.
STATE_PREFIX = "app:reservation:"


def create_reservation(
    phone_number: str,
    name: str,
    party_size: int,
    date: str,
    time: str,
    tool_context: ToolContext,
) -> dict:
    """Create a new restaurant reservation.

    Args:
        phone_number: Customer's phone number, used as the reservation ID.
        name: Name for the reservation.
        party_size: Number of guests.
        date: Reservation date (e.g., '2025-07-15' or 'this Friday').
        time: Reservation time (e.g., '7:00 PM').

    Returns:
        Confirmation of the reservation.
    """
    reservation = {
        "name": name,
        "party_size": party_size,
        "date": date,
        "time": time,
        "status": "confirmed",
    }
    tool_context.state[f"{STATE_PREFIX}{phone_number}"] = reservation
    return {
        "status": "confirmed",
        "message": f"Reservation created for {name}, party of {party_size} on {date} at {time}. Phone: {phone_number}.",
    }


def check_reservation(phone_number: str, tool_context: ToolContext) -> dict:
    """Look up an existing reservation by phone number.

    Args:
        phone_number: The phone number used when the reservation was created.
        tool_context: ADK tool context for state access.

    Returns:
        The reservation details, or a message if not found.
    """
    reservation = tool_context.state.get(f"{STATE_PREFIX}{phone_number}")
    if reservation:
        return {"found": True, "reservation": reservation}
    return {"found": False, "message": f"No reservation found for {phone_number}."}


def cancel_reservation(phone_number: str, tool_context: ToolContext) -> dict:
    """Cancel an existing reservation by phone number.

    Args:
        phone_number: The phone number used when the reservation was created.
        tool_context: ADK tool context for state access.

    Returns:
        Confirmation of cancellation, or a message if not found.
    """
    key = f"{STATE_PREFIX}{phone_number}"
    reservation = tool_context.state.get(key)
    if not reservation:
        return {"success": False, "message": f"No reservation found for {phone_number}."}
    if reservation.get("status") == "cancelled":
        return {"success": False, "message": f"Reservation for {phone_number} is already cancelled."}
    reservation["status"] = "cancelled"
    tool_context.state[key] = reservation
    return {"success": True, "message": f"Reservation for {reservation['name']} ({phone_number}) has been cancelled."}


root_agent = LlmAgent(
    name="reservation_agent",
    model="gemini-2.5-flash",
    instruction="""You are a friendly reservation assistant for "Foodie Finds" restaurant.
You help diners create, check, and cancel table reservations.

When a diner wants to make a reservation, collect these details:
- Name for the reservation
- Phone number (used as the reservation ID)
- Party size (number of guests)
- Date
- Time

Always confirm the details before creating the reservation.
When checking or cancelling, ask for the phone number if not provided.
Be concise and professional.""",
    tools=[create_reservation, check_reservation, cancel_reservation],
)

6. Menyiapkan Konfigurasi Server A2A

Menentukan kartu agen A2A

Kartu agen adalah deskripsi terstruktur tentang kemampuan agen Anda — agen dan klien lain menggunakannya untuk mengetahui fungsi agen Anda. Buat konfigurasi kartu:

cloudshell edit reservation_agent/a2a_config.py

Salin kode berikut ke reservation_agent/a2a_config.py:

# reservation_agent/a2a_config.py
from a2a.types import AgentSkill
from vertexai.preview.reasoning_engines.templates.a2a import create_agent_card

reservation_skill = AgentSkill(
    id="manage_reservations",
    name="Restaurant Reservations",
    description="Create, check, and cancel table reservations at Foodie Finds restaurant",
    tags=["reservations", "restaurant", "booking"],
    examples=[
        "Book a table for 4 on Friday at 7pm",
        "Check reservation for 555-0101",
        "Cancel my reservation, phone number 555-0101",
    ],
    input_modes=["text/plain"],
    output_modes=["text/plain"],
)

agent_card = create_agent_card(
    agent_name="Reservation Agent",
    description="Handles restaurant table reservations — create, check, and cancel bookings for Foodie Finds restaurant.",
    skills=[reservation_skill],
)

Membuat eksekutor A2A

Pelaksana menghubungkan A2A protocol dan agen ADK. Fungsi ini menerima permintaan A2A, menjalankannya melalui agen ADK, dan menampilkan hasil sebagai tugas A2A:

cloudshell edit reservation_agent/executor.py

Salin kode berikut ke reservation_agent/executor.py:

# reservation_agent/executor.py
import os
from typing import NoReturn

import vertexai
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.tasks import TaskUpdater
from a2a.types import TaskState, TextPart, UnsupportedOperationError
from a2a.utils import new_agent_text_message
from a2a.utils.errors import ServerError
from google.adk.artifacts import InMemoryArtifactService
from google.adk.memory.in_memory_memory_service import InMemoryMemoryService
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService, VertexAiSessionService
from google.genai import types

from reservation_agent.agent import root_agent as reservation_agent


class ReservationAgentExecutor(AgentExecutor):
    """Bridge between the A2A protocol and the ADK reservation agent.

    Uses InMemorySessionService for local testing, VertexAiSessionService
    when deployed to Agent Runtime (detected via GOOGLE_CLOUD_AGENT_ENGINE_ID).
    """

    def __init__(self) -> None:
        self.agent = None
        self.runner = None

    def _init_agent(self) -> None:
        if self.agent is not None:
            return

        self.agent = reservation_agent
        engine_id = os.environ.get("GOOGLE_CLOUD_AGENT_ENGINE_ID")

        if engine_id:
            project = os.environ.get("GOOGLE_CLOUD_PROJECT")
            location = os.environ.get("GOOGLE_CLOUD_LOCATION", "us-central1")
            vertexai.init(project=project, location=location)
            session_service = VertexAiSessionService(
                project=project, location=location, agent_engine_id=engine_id,
            )
            app_name = engine_id
        else:
            session_service = InMemorySessionService()
            app_name = self.agent.name

        self.runner = Runner(
            app_name=app_name,
            agent=self.agent,
            artifact_service=InMemoryArtifactService(),
            session_service=session_service,
            memory_service=InMemoryMemoryService(),
        )

    async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
        if self.agent is None:
            self._init_agent()

        query = context.get_user_input()
        updater = TaskUpdater(event_queue, context.task_id, context.context_id)
        user_id = context.message.metadata.get("user_id", "a2a-user") if context.message.metadata else "a2a-user"

        if not context.current_task:
            await updater.submit()
        await updater.start_work()

        try:
            session = await self._get_or_create_session(context.context_id, user_id)
            content = types.Content(role="user", parts=[types.Part(text=query)])

            async for event in self.runner.run_async(
                session_id=session.id, user_id=user_id, new_message=content,
            ):
                if event.is_final_response():
                    parts = event.content.parts
                    answer = " ".join(p.text for p in parts if p.text) or "No response."
                    await updater.add_artifact([TextPart(text=answer)], name="answer")
                    await updater.complete()
                    break
        except Exception as e:
            await updater.update_status(
                TaskState.failed, message=new_agent_text_message(f"Error: {e!s}"),
            )
            raise

    async def _get_or_create_session(self, context_id: str, user_id: str):
        app_name = self.runner.app_name
        if context_id:
            session = await self.runner.session_service.get_session(
                app_name=app_name, session_id=context_id, user_id=user_id,
            )
            if session:
                return session
        session = await self.runner.session_service.create_session(
            app_name=app_name, user_id=user_id, session_id=context_id,
        )
        return session

    async def cancel(self, context: RequestContext, event_queue: EventQueue) -> NoReturn:
        raise ServerError(error=UnsupportedOperationError())

Eksekutor otomatis mendeteksi lingkungannya: saat GOOGLE_CLOUD_AGENT_ENGINE_ID ditetapkan (Agent Runtime menyuntikkan ini pada waktu deployment), eksekutor akan menggunakan VertexAiSessionService untuk sesi persisten. Secara lokal, nilai tersebut akan kembali ke InMemorySessionService.

Direktori reservation_agent Anda sekarang akan berisi:

reservation_agent/
├── __init__.py
├── agent.py
├── a2a_config.py
├── executor.py
└── .env

7. Menyiapkan Agen A2A menggunakan Agent Platform SDK dan Menguji Secara Lokal

Langkah ini membungkus agen reservasi sebagai agen yang kompatibel dengan A2A menggunakan class A2aAgent Agent Platform SDK ( nama SDK masih menggunakan istilah vertex untuk kompatibilitas mundur), lalu menguji alur protokol A2A lengkap secara lokal — pengambilan kartu agen, pengiriman pesan, dan pengambilan tugas. Ini adalah objek A2aAgent yang sama dengan yang Anda deploy ke Agent Runtime pada langkah berikutnya.

Menambahkan dependensi

Instal Agent Platform SDK dengan dukungan Agent Runtime dan ADK, serta A2A SDK:

uv add "google-cloud-aiplatform[agent_engines,adk]==1.149.0" "a2a-sdk==0.3.26"

Memahami komponen A2A

Menggabungkan agen ADK untuk A2A memerlukan tiga komponen:

  1. Kartu Agen — "kartu nama" yang mendeskripsikan kapabilitas, keterampilan, dan URL endpoint agen. Agen lain menggunakannya untuk mengetahui fungsi agen Anda.
  2. Agent Executor — jembatan antara protokol A2A dan logika agen ADK Anda. Layanan ini menerima permintaan A2A, menjalankannya melalui agen ADK, dan menampilkan hasil sebagai tugas A2A.
  3. A2aAgent — class Agent Platform SDK yang menggabungkan kartu dan executor menjadi unit yang dapat di-deploy.

Membuat skrip pengujian

Buat skrip berikut untuk menguji secara lokal

cloudshell edit scripts/test_a2a_agent_local.py

Salin kode berikut ke scripts/test_a2a_agent_local.py:

# scripts/test_a2a_agent_local.py
import asyncio
import json
import os
from pprint import pprint

from dotenv import load_dotenv
from starlette.requests import Request
from vertexai.preview.reasoning_engines import A2aAgent

from reservation_agent.a2a_config import agent_card
from reservation_agent.executor import ReservationAgentExecutor

load_dotenv()


# --- Helper functions for building mock requests ---

def receive_wrapper(data: dict):
    async def receive():
        byte_data = json.dumps(data).encode("utf-8")
        return {"type": "http.request", "body": byte_data, "more_body": False}
    return receive

def build_post_request(data: dict = None, path_params: dict = None) -> Request:
    scope = {"type": "http", "http_version": "1.1", "headers": [(b"content-type", b"application/json")], "app": None}
    if path_params:
        scope["path_params"] = path_params
    return Request(scope, receive_wrapper(data))

def build_get_request(path_params: dict) -> Request:
    scope = {"type": "http", "http_version": "1.1", "query_string": b"", "app": None}
    if path_params:
        scope["path_params"] = path_params
    async def receive():
        return {"type": "http.disconnect"}
    return Request(scope, receive)


# --- Helper: poll for task completion ---

async def wait_for_task(a2a_agent, task_id, max_retries=30):
    """Poll on_get_task until the task reaches a terminal state."""
    for _ in range(max_retries):
        request = build_get_request({"id": task_id})
        result = await a2a_agent.on_get_task(request=request, context=None)
        state = result.get("status", {}).get("state", "")
        if state in ["completed", "failed"]:
            return result
        await asyncio.sleep(1)
    return result


def print_task_answer(result):
    """Extract and print the answer from task artifacts."""
    print(f"Status: {result.get('status', {}).get('state')}")
    for artifact in result.get("artifacts", []):
        if artifact.get("parts") and "text" in artifact["parts"][0]:
            print(f"Answer: {artifact['parts'][0]['text']}")


# --- Local test ---

async def main():
    # Create and set up the A2A agent locally
    a2a_agent = A2aAgent(agent_card=agent_card, agent_executor_builder=ReservationAgentExecutor)
    a2a_agent.set_up()

    # 1. Get agent card
    print("=" * 50)
    print("1. Retrieving agent card...")
    print("=" * 50)
    request = build_get_request(None)
    card_response = await a2a_agent.handle_authenticated_agent_card(request=request, context=None)
    print(f"Agent: {card_response.get('name')}")
    print(f"Skills: {[s.get('name') for s in card_response.get('skills', [])]}")

    # 2. Create a reservation
    print("\n" + "=" * 50)
    print("2. Creating a reservation...")
    print("=" * 50)
    message_data = {
        "message": {
            "messageId": f"msg-{os.urandom(4).hex()}",
            "content": [{"text": "Book a table for 2 on Saturday at 6pm. Name: Bob, Phone: 555-0202"}],
            "role": "ROLE_USER",
        },
    }
    request = build_post_request(message_data)
    response = await a2a_agent.on_message_send(request=request, context=None)
    task_id = response["task"]["id"]
    context_id = response["task"].get("contextId")
    print(f"Task ID: {task_id}")

    # 3. Wait for result
    print("\n" + "=" * 50)
    print("3. Waiting for task result...")
    print("=" * 50)
    result = await wait_for_task(a2a_agent, task_id)
    print_task_answer(result)

    # 4. Check the reservation (same context for session continuity)
    print("\n" + "=" * 50)
    print("4. Checking the reservation...")
    print("=" * 50)
    check_data = {
        "message": {
            "messageId": f"msg-{os.urandom(4).hex()}",
            "content": [{"text": "Check the reservation for 555-0202"}],
            "role": "ROLE_USER",
            "contextId": context_id,
        },
    }
    request = build_post_request(check_data)
    check_response = await a2a_agent.on_message_send(request=request, context=None)
    check_result = await wait_for_task(a2a_agent, check_response["task"]["id"])
    print_task_answer(check_result)

    # 5. Cancel the reservation
    print("\n" + "=" * 50)
    print("5. Cancelling the reservation...")
    print("=" * 50)
    cancel_data = {
        "message": {
            "messageId": f"msg-{os.urandom(4).hex()}",
            "content": [{"text": "Cancel the reservation for 555-0202"}],
            "role": "ROLE_USER",
            "contextId": context_id,
        },
    }
    request = build_post_request(cancel_data)
    cancel_response = await a2a_agent.on_message_send(request=request, context=None)
    cancel_result = await wait_for_task(a2a_agent, cancel_response["task"]["id"])
    print_task_answer(cancel_result)

    print("\n" + "=" * 50)
    print("All tests passed!")
    print("=" * 50)


if __name__ == "__main__":
    asyncio.run(main())

Skrip pengujian mengimpor kartu agen dan eksekutor yang Anda buat pada langkah sebelumnya — tidak ada duplikasi. Skrip ini akan membuat A2aAgent lokal, menyimulasikan panggilan protokol A2A melalui permintaan HTTP tiruan, dan memverifikasi ketiga operasi reservasi.

Karena tidak ada GOOGLE_CLOUD_AGENT_ENGINE_ID yang ditetapkan secara lokal, eksekutor menggunakan InMemorySessionService. Saat di-deploy ke Agent Runtime, executor yang sama akan otomatis beralih ke VertexAiSessionService untuk sesi persisten.

Menjalankan pengujian

PYTHONPATH=. uv run python scripts/test_a2a_agent_local.py

Output-nya membahas lima tahap:

  1. Kartu agen — mengambil kapabilitas dan keahlian agen
  2. Buat pemesanan — memesan tempat dan menampilkan tugas dengan konfirmasi
  3. Mendapatkan hasil tugas — mengambil tugas yang telah selesai dengan jawabannya
  4. Periksa reservasi — mencari reservasi berdasarkan nomor telepon
  5. Batalkan reservasi — membatalkan pemesanan dan mengonfirmasi

Contoh output seperti yang ditunjukkan di bawah

==================================================
1. Retrieving agent card...
==================================================
Agent: Reservation Agent
Skills: ['Restaurant Reservations']

==================================================
2. Creating a reservation...
==================================================
Task ID: f7f7004d-cfea-49c2-b57d-5bca9959e193

==================================================
3. Waiting for task result...
==================================================
Status: TASK_STATE_COMPLETED
Answer: Your reservation for Bob, party of 2, on Saturday at 6:00 PM has been confirmed. The phone number associated is 555-0202.

==================================================
4. Checking the reservation...
==================================================
Status: TASK_STATE_COMPLETED
Answer: I found a reservation for Bob, party of 2, on Saturday at 6:00 PM. The reservation status is confirmed.

==================================================
5. Cancelling the reservation...
==================================================
Status: TASK_STATE_COMPLETED
Answer: Your reservation for Bob (555-0202) has been cancelled.

==================================================
All tests passed!
==================================================

Pada tahap ini, Anda telah memverifikasi: kartu agen A2A menjelaskan keterampilan yang benar, ketiga operasi reservasi berfungsi melalui alur pesan/tugas protokol A2A, dan status tetap ada di seluruh pesan dalam konteks yang sama.

8. Men-deploy Agen Pemesanan ke Agent Runtime

Langkah ini men-deploy agen reservasi ke Runtime Platform Agen Gemini Enterprise — platform serverless yang terkelola sepenuhnya yang menghosting agen Anda dan mengeksposnya sebagai endpoint A2A yang aman. Setelah deployment, klien yang diberi otorisasi dapat menemukan dan berinteraksi dengan agen melalui endpoint HTTP A2A standar.

Buat bucket penyiapan

Buat bucket Cloud Storage untuk penyiapan Agent Runtime. Agent Runtime menggunakan bucket ini untuk mengupload kode dan dependensi agen Anda selama deployment:

STAGING_BUCKET="${GOOGLE_CLOUD_PROJECT}-adk-a2a-agent-runtime"
gsutil mb -l $REGION -p $GOOGLE_CLOUD_PROJECT gs://$STAGING_BUCKET 2>/dev/null || echo "Bucket already exists"
echo "STAGING_BUCKET=$STAGING_BUCKET" >> .env
source .env

Buat skrip deployment

Selanjutnya, kita perlu menyiapkan skrip deployment

cloudshell edit scripts/deploy_a2a_agent_runtime.py

Salin kode berikut ke scripts/deploy_a2a_agent_runtime.py:

# scripts/deploy_a2a_agent_runtime.py
import os
from pathlib import Path

import vertexai
from dotenv import load_dotenv
from google.genai import types
from vertexai.preview.reasoning_engines import A2aAgent

from reservation_agent.a2a_config import agent_card
from reservation_agent.executor import ReservationAgentExecutor

load_dotenv()

PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
REGION = os.environ["REGION"]
STAGING_BUCKET = os.environ.get("STAGING_BUCKET", f"{PROJECT_ID}-adk-a2a-agent-runtime")
BUCKET_URI = f"gs://{STAGING_BUCKET}"

a2a_agent = A2aAgent(
    agent_card=agent_card,
    agent_executor_builder=ReservationAgentExecutor,
)


def main():
    vertexai.init(project=PROJECT_ID, location=REGION, staging_bucket=BUCKET_URI)
    client = vertexai.Client(
        project=PROJECT_ID,
        location=REGION,
        http_options=types.HttpOptions(api_version="v1beta1"),
    )

    print("Deploying Reservation Agent to Agent Runtime...")
    print("This may take 3-5 minutes.")

    remote_agent = client.agent_engines.create(
        agent=a2a_agent,
        config={
            "display_name": agent_card.name,
            "description": agent_card.description,
            "requirements": [
                "google-cloud-aiplatform[agent_engines,adk]==1.149.0",
                "a2a-sdk==0.3.26",
                "google-adk==1.29.0",
                "cloudpickle",
                "pydantic"
            ],
            "extra_packages": [
                "./reservation_agent",
            ],
            "http_options": {
                "api_version": "v1beta1",
            },
            "staging_bucket": BUCKET_URI,
        },
    )

    resource_name = remote_agent.api_resource.name
    print(f"\nDeployment complete!")
    print(f"Resource name: {resource_name}")

    env_path = Path(".env")
    lines = env_path.read_text().splitlines() if env_path.exists() else []
    lines = [l for l in lines if not l.startswith("RESERVATION_AGENT_RESOURCE_NAME=")]
    lines.append(f"RESERVATION_AGENT_RESOURCE_NAME={resource_name}")
    env_path.write_text("\n".join(lines) + "\n")
    print("Written RESERVATION_AGENT_RESOURCE_NAME to .env")


if __name__ == "__main__":
    main()

Skrip deployment mengimpor agent_card dan ReservationAgentExecutor yang sama yang digunakan dalam pengujian lokal — tidak ada duplikasi kode. Agent Runtime melakukan serialisasi (pickles) objek A2aAgent beserta dependensinya untuk deployment. Di akhir skrip deployment, skrip akan menulis nilai RESERVATION_AGENT_RESOURCE_NAME ke file .env

Men-deploy ke Agent Runtime

Jalankan skrip deployment:

PYTHONPATH=. uv run python scripts/deploy_a2a_agent_runtime.py

Deployment memerlukan waktu 3-5 menit. Skrip ini menyediakan endpoint serverless di Agent Runtime yang menghosting agen reservasi. Setelah deployment berhasil, Anda akan melihat output yang mirip seperti yang ditunjukkan di bawah

Deploying Reservation Agent to Agent Runtime...
This may take 3-5 minutes.

Deployment complete!
Resource name: projects/your-project-number/locations/us-central1/reasoningEngines/your-agent-deployment-unique-id
Written RESERVATION_AGENT_RESOURCE_NAME to .env

Anda dapat melihat agen yang di-deploy di konsol cloud. Telusuri Agent Platform di kotak penelusuran konsol

af3751f461e4708c.png

Kemudian, di tab kiri, arahkan kursor ke Agents dan pilih Deployments

8a9c7fd127e60aca.png

Anda akan melihat Reservation Agent tercantum di daftar deployment seperti yang ditunjukkan di bawah

a38b46bcb6c8e4db.png

Menguji agen yang di-deploy

Sekarang, kita siap menguji agen yang di-deploy, membuat skrip pengujian untuk agen yang di-deploy:

cloudshell edit scripts/test_a2a_agent_runtime.py

Salin kode berikut ke scripts/test_a2a_agent_runtime.py:

# scripts/test_a2a_agent_runtime.py
import asyncio
import os
import time

import vertexai
from a2a.types import TaskState
from dotenv import load_dotenv
from google.genai import types

load_dotenv()

PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
REGION = os.environ["REGION"]
RESOURCE_NAME = os.environ["RESERVATION_AGENT_RESOURCE_NAME"]


async def main():
    vertexai.init(project=PROJECT_ID, location=REGION)
    client = vertexai.Client(
        project=PROJECT_ID, location=REGION,
        http_options=types.HttpOptions(api_version="v1beta1"),
    )

    agent = client.agent_engines.get(name=RESOURCE_NAME)

    # 1. Get agent card
    print("=" * 50)
    print("1. Retrieving agent card...")
    print("=" * 50)
    card = await agent.handle_authenticated_agent_card()
    print(f"Agent: {card.name}")
    print(f"URL: {card.url}")
    print(f"Skills: {[s.name for s in card.skills]}")

    # 2. Send a reservation request
    print("\n" + "=" * 50)
    print("2. Sending reservation request...")
    print("=" * 50)
    message_data = {
        "messageId": "msg-remote-001",
        "role": "user",
        "parts": [{"kind": "text", "text": "Book a table for 3 on Sunday at noon. Name: Carol, Phone: 555-0303"}],
    }
    response = await agent.on_message_send(**message_data)

    task_object = None
    for chunk in response:
        if isinstance(chunk, tuple) and len(chunk) > 0 and hasattr(chunk[0], "id"):
            task_object = chunk[0]
            break

    task_id = task_object.id
    print(f"Task ID: {task_id}")
    print(f"Status: {task_object.status.state}")

    # 3. Poll for result
    print("\n" + "=" * 50)
    print("3. Waiting for result...")
    print("=" * 50)
    result = None
    for _ in range(30):
        try:
            result = await agent.on_get_task(id=task_id)
            if result.status.state in [TaskState.completed, TaskState.failed]:
                break
        except Exception:
            pass
        time.sleep(1)

    print(f"Final status: {result.status.state}")
    if result.artifacts:
        for artifact in result.artifacts:
            if artifact.parts and hasattr(artifact.parts[0], "root") and hasattr(artifact.parts[0].root, "text"):
                print(f"Answer: {artifact.parts[0].root.text}")

    print("\n" + "=" * 50)
    print("Remote agent test passed!")
    print("=" * 50)


if __name__ == "__main__":
    asyncio.run(main())

Kemudian, jalankan pengujian

source .env
uv run python scripts/test_a2a_agent_runtime.py

Output menampilkan kartu agen dengan skill "Reservasi Restoran", diikuti dengan penyelesaian tugas dengan konfirmasi reservasi.

==================================================
1. Retrieving agent card...
==================================================
Agent: Reservation Agent
URL: https://us-central1-aiplatform.googleapis.com/v1beta1/projects/your-project-id/locations/us-central1/reasoningEngines/your-agent-unique-id/a2a
Skills: ['Restaurant Reservations']

==================================================
2. Sending reservation request...
==================================================
Task ID: b34585d0-5f03-4cb0-85a3-40710a0d224d
Status: TaskState.completed

==================================================
3. Waiting for result...
==================================================
Final status: TaskState.completed
Answer: Your reservation for Carol, party of 3 on Sunday at noon with phone number 555-0303 is confirmed.

==================================================
Remote agent test passed!
==================================================

Agen reservasi kini berhasil berjalan sebagai endpoint A2A terkelola di Agent Runtime.

9. Mengintegrasikan Agen Reservasi A2A dengan Agen Restoran Root

Langkah ini mengupgrade agen restoran untuk menggunakan agen reservasi yang di-deploy sebagai sub-agen A2A jarak jauh. Orchestrator berjalan secara lokal, sedangkan agen reservasi berjalan di Agent Runtime — integrasi parsial yang memvalidasi koneksi A2A sebelum deployment penuh.

Menyelesaikan URL kartu agen A2A

RemoteA2aAgent memerlukan URL kartu agen reservasi yang di-deploy untuk menemukan kemampuannya. Buat skrip yang mengambil URL ini dari Agent Runtime dan menuliskannya ke .env agen restoran:

cloudshell edit scripts/resolve_agent_card_url.py

Salin kode berikut ke scripts/resolve_agent_card_url.py:

# scripts/resolve_agent_card_url.py
import asyncio
import os
from pathlib import Path

import vertexai
from dotenv import load_dotenv
from google.genai import types

load_dotenv()

PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
REGION = os.environ["REGION"]
RESOURCE_NAME = os.environ["RESERVATION_AGENT_RESOURCE_NAME"]


async def main():
    vertexai.init(project=PROJECT_ID, location=REGION)
    client = vertexai.Client(
        project=PROJECT_ID, location=REGION,
        http_options=types.HttpOptions(api_version="v1beta1"),
    )

    agent = client.agent_engines.get(name=RESOURCE_NAME)
    card = await agent.handle_authenticated_agent_card()
    card_url = f"{card.url}/v1/card"

    print(f"Agent: {card.name}")
    print(f"Card URL: {card_url}")

    # Write to restaurant_agent/.env
    # Write to both restaurant_agent/.env (for adk web) and root .env (for Cloud Run deploy)
    for env_path in [Path("restaurant_agent/.env"), Path(".env")]:
        lines = env_path.read_text().splitlines() if env_path.exists() else []
        lines = [l for l in lines if not l.startswith("RESERVATION_AGENT_CARD_URL=")]
        lines.append(f"RESERVATION_AGENT_CARD_URL={card_url}")
        env_path.write_text("\n".join(lines) + "\n")
        print(f"Written RESERVATION_AGENT_CARD_URL to {env_path}")


if __name__ == "__main__":
    asyncio.run(main())

Jalankan skrip untuk mengisi file .env dengan URL kartu agen

uv run python scripts/resolve_agent_card_url.py
source .env

Memperbarui agen restoran

Buka file agen restoran:

cloudshell edit restaurant_agent/agent.py

Kemudian, ganti isinya dengan versi yang telah diupdate yang menyertakan agen reservasi jarak jauh sebagai sub-agen:

# restaurant_agent/agent.py
import os

import httpx
from google.adk.agents import LlmAgent
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.auth import default
from google.auth.transport.requests import Request as AuthRequest
from toolbox_adk import ToolboxToolset

TOOLBOX_URL = os.environ.get("TOOLBOX_URL", "http://127.0.0.1:5000")
RESERVATION_AGENT_CARD_URL = os.environ.get("RESERVATION_AGENT_CARD_URL", "")

toolbox = ToolboxToolset(TOOLBOX_URL)


class GoogleCloudAuth(httpx.Auth):
    """Auto-refreshing Google Cloud authentication for httpx.

    Refreshes the access token before each request if expired,
    so long-running agents never hit 401 errors.
    """

    def __init__(self):
        self.credentials, _ = default(
            scopes=["https://www.googleapis.com/auth/cloud-platform"]
        )

    def auth_flow(self, request):
        # Refresh the token if it is expired or missing
        if not self.credentials.valid:
            self.credentials.refresh(AuthRequest())
            
        request.headers["Authorization"] = f"Bearer {self.credentials.token}"
        yield request


reservation_remote_agent = RemoteA2aAgent(
    name="reservation_agent",
    description="Handles restaurant table reservations — create, check, and cancel bookings. Delegate to this agent when the user wants to book a table, check a reservation, or cancel a reservation.",
    agent_card=RESERVATION_AGENT_CARD_URL,
    httpx_client=httpx.AsyncClient(auth=GoogleCloudAuth(), timeout=60),
)

root_agent = LlmAgent(
    name="restaurant_agent",
    model="gemini-2.5-flash",
    instruction="""You are a friendly and knowledgeable concierge at "Foodie Finds," a restaurant. Your job:
- Help diners browse the menu by category or cuisine type.
- Provide full details about specific dishes, including ingredients, price, and dietary information.
- Recommend dishes based on natural language descriptions of what the diner is craving.
- Add new menu items when asked.
- For reservation requests (booking, checking, or cancelling tables), delegate to the reservation_agent.

When a diner asks about a specific dish by name or cuisine, use the get-item-details tool.
When a diner asks for a specific category or cuisine type, use the search-menu tool.
When a diner describes what kind of food they want — by flavor, texture, dietary needs, or cravings — use the search-menu-by-description tool for semantic search.

When in doubt between search-menu and search-menu-by-description, prefer search-menu-by-description — it searches dish descriptions and finds more relevant matches.
If a dish is not available (available is false), let the diner know and suggest similar alternatives from the search results.
Be conversational, knowledgeable, and concise.""",
    tools=[toolbox],
    sub_agents=[reservation_remote_agent],
)

Perubahan utama dari versi sebelumnya:

  • GoogleCloudAuth — handler httpx.Auth kustom yang memperbarui token akses Google Cloud sebelum setiap permintaan. Runtime Agen memerlukan panggilan A2A yang diautentikasi, dan token akan berakhir setelah jangka waktu tertentu.
  • RemoteA2aAgent membaca RESERVATION_AGENT_CARD_URL dari .env (ditulis oleh skrip penyelesaian) dan menggunakan httpx_client yang diautentikasi
  • Terdaftar sebagai sub-agen — Pengelola ADK secara otomatis mendelegasikan permintaan reservasi kepadanya
  • Memperbarui petunjuk untuk menyebutkan pendelegasian pemesanan

Menguji agen terintegrasi secara lokal

Agen awal memerlukan integrasi dengan MCP Toolbox, file yang diperlukan seharusnya sudah disediakan dari codelab sebelumnya atau dari repo awal. Kita hanya perlu memastikan bahwa proses toolbox berjalan dengan benar.

Jika TOOLBOX_URL di .env Anda sudah mengarah ke layanan Cloud Run (dari codelab sebelumnya atau mungkin dari full_setup.sh repo starter), Anda dapat melewati langkah ini — agen akan terhubung ke Toolbox yang di-deploy.

Jika Anda memerlukan Toolbox lokal, periksa apakah Toolbox sudah berjalan sebelum memulai instance baru:

if curl -s http://127.0.0.1:5000/api/toolsets > /dev/null 2>&1; then
  echo "Toolbox already running on port 5000"
else
  set -a; source .env; set +a
  ./toolbox --config=tools.yaml > logs/toolbox.log 2>&1 &
  echo "Toolbox started"
fi

Kemudian, kita dapat mencoba berinteraksi dengan agen restoran melalui UI pengembangan web ADK

uv run adk web --allow_origins "regex:https://.*\.cloudshell\.dev" --port 8080

Buka UI web ADK menggunakan Pratinjau Web Cloud Shell (klik tombol Pratinjau Web, ubah port ke 8080), lalu pilih restaurant_agent

65a055b70ab52aa8.png

Menguji percakapan campuran:

Kueri Menu

What Italian dishes do you have?

Permintaan reservasi

I want to create reservation under name Bob, phone number 123456

Periksa reservasi

Buat sesi baru ( mulai percakapan baru):

Check the reservation for 123456

92cef3bc7671129a.png

16bfd60f202dcaa7.png

c5326bbf6fa778e2.png

Hentikan proses adk web dengan Ctrl+C dua kali. Selanjutnya, selesaikan sistem dengan men-deploy agen sepenuhnya

10. Men-deploy Agen Restoran yang Diperbarui ke Cloud Run

Langkah ini men-deploy ulang agen restoran ke Cloud Run dengan integrasi A2A, sehingga menyelesaikan sistem multi-agen yang di-deploy sepenuhnya.

Memberikan izin untuk mengakses Runtime Agen

Akun layanan Cloud Run memerlukan izin untuk memanggil Agent Runtime. Berikan peran roles/aiplatform.user ke akun layanan Compute Engine default:

PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format='value(projectNumber)')
gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
  --member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --role="roles/aiplatform.user"

Deploy ke Cloud Run

Dalam penyiapan ini, kita mengasumsikan bahwa layanan agen restoran sudah ada dari codelab sebelumnya atau dengan menjalankan scripts/full_setup.sh jika Anda memulai dari awal. Tindakan ini akan men-deploy ulang dengan kode yang diperbarui (integrasi RemoteA2aAgent baru) dan menambahkan URL kartu agen reservasi sebagai variabel lingkungan baru — variabel lingkungan yang ada (TOOLBOX_URL, GOOGLE_CLOUD_PROJECT, dll.) akan dipertahankan:

gcloud run deploy restaurant-agent \
  --source . \
  --region=$REGION \
  --allow-unauthenticated \
  --update-env-vars="RESERVATION_AGENT_CARD_URL=$RESERVATION_AGENT_CARD_URL" \
  --min-instances=0 \
  --max-instances=1 \
  --memory=1Gi \
  --port=8080

Menguji sistem yang di-deploy sepenuhnya

Dapatkan URL layanan yang di-deploy:

AGENT_URL=$(gcloud run services describe restaurant-agent --region=$REGION --format='value(status.url)')
echo "Agent URL: $AGENT_URL"

Buka URL di browser Anda. UI web ADK dimuat — ini adalah antarmuka yang sama yang Anda gunakan secara lokal, yang kini berjalan di Cloud Run.

Jangan ragu untuk mengobrol dengan agen

Kueri Menu

What spicy dishes do you have?

Permintaan reservasi

Book a table for 4 on Friday at 7pm. Name: Eve, Phone: 555-0505

Periksa reservasi

Buat sesi baru ( mulai percakapan baru):

Check reservation for 555-0505

69ae9a7c35255fc.png

55145841338ec9b3.png

Sistem multi-agen telah di-deploy sepenuhnya. Agen restoran di Cloud Run mengatur antara dua layanan backend: MCP Toolbox untuk operasi menu dan agen reservasi A2A di Agent Runtime.

11. Selamat!

Anda telah membangun dan men-deploy sistem multi-agen menggunakan protokol A2A di Google Cloud.

Yang telah Anda pelajari

  • Membangun agen ADK yang menggunakan status sesi (ToolContext) untuk mengelola data reservasi tanpa database
  • Men-deploy agen A2A ke Agent Runtime menggunakan Agent Platform SDK
  • Menggunakan agen A2A jarak jauh dari agen ADK lain menggunakan RemoteA2aAgent sebagai sub-agen
  • Menguji sistem secara bertahap: A2A lokal → A2A yang di-deploy → integrasi parsial → deployment penuh

Pembersihan

Agar tidak menimbulkan biaya pada akun Google Cloud Anda, hapus resource yang dibuat dalam codelab ini.

gcloud projects delete $GOOGLE_CLOUD_PROJECT

Opsi 2: Menghapus resource satu per satu

# Delete the Agent Runtime deployment
uv run python -c "
import vertexai
from google.genai import types
vertexai.init(project='$GOOGLE_CLOUD_PROJECT', location='$REGION')
client = vertexai.Client(
    project='$GOOGLE_CLOUD_PROJECT', location='$REGION',
    http_options=types.HttpOptions(api_version='v1beta1'),
)
agent = client.agent_engines.get(name='$RESERVATION_AGENT_RESOURCE_NAME')
agent.delete(force=True)
print('Agent Runtime deployment deleted.')
"

# Delete Cloud Run services
gcloud run services delete restaurant-agent --region=$REGION --quiet
gcloud run services delete toolbox-service --region=$REGION --quiet

# Delete Cloud SQL instance
gcloud sql instances delete $DB_INSTANCE --quiet

# Delete GCS staging bucket
gsutil rm -r gs://$STAGING_BUCKET