1. Giới thiệu
Khi các tác nhân AI đảm nhận nhiều trách nhiệm hơn, việc duy trì, mở rộng và phát triển một tác nhân duy nhất làm mọi việc sẽ trở nên khó khăn. Các chức năng khác nhau thường cần các chiến lược triển khai, chu kỳ cập nhật khác nhau, hoặc thậm chí là các nhóm khác nhau sở hữu chúng.
- Giao thức A2A (Agent2Agent) giải quyết vấn đề giao tiếp – chuẩn hoá cách các tác nhân khám phá khả năng của nhau và cộng tác trên nhiều khung và tổ chức.
- Thời gian chạy nền tảng tác nhân Gemini Enterprise giải quyết phía triển khai – một nền tảng không máy chủ, được quản lý hoàn toàn, lưu trữ các tác nhân của bạn với tính năng hỗ trợ A2A tích hợp, khả năng tự động cấp tài nguyên bổ sung, các điểm cuối bảo mật, phiên liên tục và không cần quản lý cơ sở hạ tầng.
Nhờ đó, bạn có thể tạo các tác nhân chuyên biệt, triển khai chúng dưới dạng các dịch vụ A2A có thể phát hiện và kết hợp chúng thành các hệ thống đa tác nhân.
Sản phẩm bạn sẽ tạo ra
Một Tác nhân đặt chỗ quản lý việc đặt bàn tại nhà hàng (tạo, kiểm tra và huỷ) bằng cách sử dụng trạng thái phiên ADK do Gemini Enterprise Agent Platform Sessions quản lý. Bạn triển khai tác nhân này vào Thời gian chạy Nền tảng tác nhân Gemini Enterprise, nơi tác nhân này có thể được phát hiện thông qua thẻ tác nhân của giao thức A2A. Sau đó, bạn nâng cấp tác nhân hỗ trợ nhà hàng Foodie Finds (từ lớp học lập trình tiên quyết, đừng lo lắng nếu bạn chưa truy cập vào lớp học lập trình này – chúng tôi đã chuẩn bị một kho lưu trữ khởi đầu cho bạn) để sử dụng Reservation Agent làm tác nhân phụ A2A từ xa. Kết quả: một hệ thống đa tác nhân, trong đó trình điều phối định tuyến các truy vấn về trình đơn đến MCP Toolbox và các yêu cầu đặt trước đến tác nhân A2A từ xa.

Kiến thức bạn sẽ học được
- Tạo một tác nhân ADK sử dụng dịch vụ phiên được quản lý để quản lý dữ liệu đặt phòng
- Hiển thị một tác nhân ADK dưới dạng máy chủ A2A có thẻ và kỹ năng của tác nhân
- Triển khai tác nhân A2A vào Thời gian chạy tác nhân Gemini Enterprise
- Sử dụng tác nhân A2A từ xa từ một tác nhân ADK khác bằng cách dùng
RemoteA2aAgentvà xử lý yêu cầu đã xác thực - Kiểm thử hệ thống nhiều tác nhân theo từng bước: A2A cục bộ, A2A đã triển khai, tích hợp một phần, triển khai đầy đủ
Điều kiện tiên quyết
- (Nên làm) Hoàn tất các lớp học lập trình sau :
- Xây dựng tác nhân AI bền bỉ bằng ADK và CloudSQL -> thông tin chi tiết khác về phiên và trạng thái ADK
- Agentic RAG bằng ADK, Bộ công cụ MCP và Cloud SQL –> bạn có thể tiếp tục xây dựng tác nhân của mình từ lớp học lập trình này, mã khởi đầu được cung cấp là giống nhau
- Tài khoản Google Cloud có tài khoản thanh toán đang hoạt động
- Hiểu biết cơ bản về Python và các khái niệm ADK
2. Thiết lập môi trường – Tiếp tục từ lớp học lập trình trước
Nội dung mà chúng tôi cung cấp trong lớp học lập trình này thực sự là phần tiếp theo của lớp học lập trình tiên quyết này: Agentic RAG bằng ADK, Bộ công cụ MCP và Cloud SQL . Bạn có thể tiếp tục công việc từ lớp học lập trình trước
Chúng ta có thể bắt đầu tạo trong thư mục làm việc của lớp học lập trình trước ( thư mục làm việc phải là build-agent-adk-toolbox-cloudsql). Để tránh nhầm lẫn, hãy đổi tên thư mục thành tên thư mục mà chúng ta dùng khi bắt đầu từ đầu
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
Xác minh rằng các tệp khoá trong lớp học lập trình trước đã được đặt đúng vị trí:
echo "--- Restaurant Agent ---"
cat restaurant_agent/agent.py | head -5
echo ""
echo "--- Toolbox Config ---"
cat tools.yaml | head -5
Bạn sẽ thấy tệp restaurant_agent/agent.py có chế độ nhập LlmAgent và tệp tools.yaml có cấu hình Toolbox.
Tiếp theo, hãy khởi động lại môi trường Python
rm -rf .venv
uv sync
Ngoài ra, hãy xác minh rằng cơ sở dữ liệu đã được gieo hạt và sẵn sàng:
uv run python scripts/verify_seed.py
Nếu làm theo từng chi tiết kiểm thử trong lớp học lập trình trước, bạn có thể thấy kết quả như sau
Menu Items: 16/15 Embeddings: 16/15 ✗ Database not ready
Không sao cả! Quy trình kiểm tra cơ sở dữ liệu không tính đến dữ liệu bổ sung mà bạn nhập từ quy trình kiểm tra nhập dữ liệu. Miễn là bạn có từ 15 dữ liệu trở lên, mọi thứ đều ổn!
Kích hoạt API bắt buộc
Tiếp theo, bạn cần đảm bảo rằng bạn đã bật API cần thiết để tương tác với Nền tảng tác nhân Gemini Enterprise
gcloud services enable \
cloudresourcemanager.googleapis.com
Bạn đã có các tệp và cơ sở hạ tầng cần thiết để tiếp tục chuyển sang phần tiếp theo: A2A Protocol and Gemini Enterprise Agent Runtime!
3. Thiết lập môi trường – Bắt đầu từ đầu với kho lưu trữ khởi động
Bước này chuẩn bị môi trường Cloud Shell, định cấu hình dự án trên đám mây Google Cloud và sao chép kho lưu trữ ban đầu.
Mở Cloud Shell
Mở Cloud Shell trong trình duyệt. Cloud Shell cung cấp một môi trường được định cấu hình sẵn với tất cả các công cụ bạn cần cho lớp học lập trình này. Nhấp vào Uỷ quyền khi được nhắc
Sau đó, nhấp vào "View" (Xem) -> "Terminal" (Thiết bị đầu cuối) để mở thiết bị đầu cuối.Giao diện của bạn sẽ trông tương tự như thế này

Đây sẽ là giao diện chính của chúng ta, IDE ở trên cùng, thiết bị đầu cuối ở dưới cùng
Thiết lập thư mục làm việc
Sao chép kho lưu trữ khởi đầu. Tất cả mã bạn viết trong lớp học lập trình này sẽ nằm ở đây:
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
Tạo tệp .env từ mẫu được cung cấp:
cp .env.example .env
Để đơn giản hoá việc thiết lập dự án trong thiết bị đầu cuối, hãy tải tập lệnh thiết lập dự án này xuống thư mục làm việc của bạn:
curl -sL https://raw.githubusercontent.com/alphinside/cloud-trial-project-setup/main/setup_verify_trial_project.sh -o setup_verify_trial_project.sh
Chạy tập lệnh. Lệnh này xác minh tài khoản thanh toán dùng thử của bạn, tạo một dự án mới (hoặc xác thực một dự án hiện có), lưu mã dự án vào một tệp .env trong thư mục hiện tại và đặt dự án đang hoạt động trong gcloud.
bash setup_verify_trial_project.sh && source .env
Tập lệnh sẽ:
- Xác minh rằng bạn có một tài khoản thanh toán dùng thử đang hoạt động
- Kiểm tra xem có dự án nào trong
.envhay không (nếu có) - Tạo dự án mới hoặc sử dụng lại dự án hiện có
- Liên kết tài khoản thanh toán dùng thử với dự án của bạn
- Lưu mã dự án vào
.env - Đặt dự án làm dự án
gcloudđang hoạt động
Xác minh rằng dự án được thiết lập đúng cách bằng cách kiểm tra văn bản màu vàng bên cạnh thư mục đang hoạt động trong dấu nhắc của thiết bị đầu cuối Cloud Shell. Mã này sẽ hiển thị mã dự án của bạn.

Kích hoạt API bắt buộc
Tiếp theo, bạn cần đảm bảo rằng bạn đã bật API cần thiết để tương tác với Nền tảng tác nhân Gemini Enterprise
gcloud services enable \
aiplatform.googleapis.com \
cloudresourcemanager.googleapis.com
Thiết lập cơ sở hạ tầng cơ bản
Trước tiên, chúng ta cần cài đặt các phần phụ thuộc Python bằng uv. Đây là một trình quản lý dự án và gói Python nhanh được viết bằng Rust ( tài liệu về uv). Lớp học lập trình này sử dụng uv để duy trì dự án Python một cách nhanh chóng và đơn giản
uv sync
Sau đó, hãy chạy tập lệnh thiết lập đầy đủ để tạo phiên bản Cloud SQL, gieo dữ liệu và triển khai dịch vụ Hộp công cụ. Dịch vụ này sẽ đóng vai trò là trạng thái ban đầu của tác nhân nhà hàng
bash scripts/full_setup.sh > logs/full_setup.log 2>&1 &
4. Khái niệm: Giao thức Agent2Agent (A2A) và thời gian chạy tác nhân Gemini Enterprise
Trước khi bắt đầu xây dựng, hãy dành một chút thời gian để tìm hiểu 2 công nghệ chính được trình bày trong lớp học lập trình này nhằm mở rộng quy mô ứng dụng dựa trên tác nhân của chúng ta.
Giao thức Agent2Agent (A2A)
Giao thức Agent2Agent (A2A) là một tiêu chuẩn mở được thiết kế để cho phép giao tiếp và cộng tác liền mạch giữa các tác nhân AI. Trong đó, MCP (Giao thức bối cảnh mô hình) kết nối các tác nhân với các công cụ và dữ liệu, A2A kết nối các tác nhân với các tác nhân khác – cho phép các tác nhân khám phá khả năng của nhau, uỷ quyền nhiệm vụ và cộng tác trên nhiều khung và tổ chức.

Điểm khác biệt chính giữa việc bao bọc một tác nhân dưới dạng một công cụ (thông qua MCP) so với việc hiển thị tác nhân đó thông qua A2A: các công cụ không có trạng thái và thực hiện các chức năng đơn lẻ, trong khi các tác nhân A2A có thể suy luận, duy trì trạng thái và xử lý các lượt tương tác nhiều lượt như thương lượng hoặc làm rõ. Một tác nhân được hiển thị thông qua A2A vẫn giữ nguyên toàn bộ khả năng của mình thay vì bị giảm xuống thành một lệnh gọi hàm.
A2A xác định 3 khái niệm cốt lõi:
- Thẻ tác nhân – một tài liệu JSON mô tả những việc mà tác nhân thực hiện, các kỹ năng và điểm cuối của tác nhân. Các tác nhân khác tìm nạp thẻ này để khám phá các chức năng.
- Tin nhắn – yêu cầu của người dùng hoặc tác nhân phần mềm được gửi đến một điểm cuối A2A, kích hoạt một tác vụ.
- Tác vụ – một đơn vị công việc có vòng đời (đã gửi → đang hoạt động → đã hoàn thành/thất bại) và cấu phần phần mềm chứa kết quả.

Để tìm hiểu kỹ hơn, hãy xem bài viết A2A là gì?
Thời gian chạy Nền tảng tác nhân Gemini Enterprise
Thời gian chạy tác nhân là một dịch vụ được quản lý hoàn toàn trên Google Cloud để triển khai, mở rộng quy mô và quản lý các tác nhân AI trong quá trình sản xuất bằng các tính năng bảo mật cấp doanh nghiệp (ví dụ: VPC Service Controls, CMEK). Dịch vụ này xử lý cơ sở hạ tầng để bạn có thể tập trung vào logic của tác nhân.

Agent Runtime cung cấp:
- Triển khai được quản lý – triển khai các tác nhân được tạo bằng ADK, LangGraph hoặc bất kỳ khung Python nào bằng một lệnh gọi SDK duy nhất
- Lưu trữ A2A – triển khai các tác nhân dưới dạng điểm cuối tuân thủ A2A với tính năng phân phát thẻ tác nhân tự động và quyền truy cập đã xác thực
- Phiên liên tục –
VertexAiSessionServicelưu trữ nhật ký cuộc trò chuyện và trạng thái trên các yêu cầu - Tự động cấp tài nguyên bổ sung – cấp tài nguyên bổ sung từ 0 để xử lý lưu lượng truy cập mà không cần quản lý cơ sở hạ tầng
- Khả năng ghi nhận – tính năng theo dõi, ghi nhật ký và giám sát tích hợp thông qua ngăn xếp khả năng ghi nhận của Google Cloud
- và nhiều tính năng khác, hãy xem tài liệu này để biết thông tin chi tiết
Trong lớp học lập trình này, bạn sẽ triển khai tác nhân đặt phòng cho Agent Runtime. Quy trình triển khai sẽ chuyển đổi mã tác nhân của bạn thành dữ liệu nối tiếp (pickle) rồi tải mã đó lên. Agent Runtime cung cấp một điểm cuối không máy chủ để phân phát giao thức A2A – các tác nhân (hoặc ứng dụng) khác tương tác với điểm cuối này thông qua các lệnh gọi HTTP tiêu chuẩn, được xác thực bằng thông tin đăng nhập Google Cloud.
5. Tạo tác nhân đặt phòng
Bước này sẽ tạo một tác nhân ADK mới xử lý việc đặt chỗ tại nhà hàng bằng cách sử dụng trạng thái phiên. Tác nhân này hỗ trợ 3 thao tác (tạo, kiểm tra và huỷ) với số điện thoại là khoá tra cứu. Tất cả dữ liệu đặt phòng đều nằm trong trạng thái phiên của ADK
Tạo cấu trúc cơ bản cho nhân viên hỗ trợ
Sử dụng adk create để tạo cấu trúc thư mục tác nhân với cấu hình dự án và mô hình chính xác:
source .env
uv run adk create reservation_agent \
--model gemini-2.5-flash \
--project ${GOOGLE_CLOUD_PROJECT} \
--region ${GOOGLE_CLOUD_LOCATION}
Thao tác này sẽ tạo một thư mục reservation_agent/ có __init__.py, agent.py và .env được định cấu hình sẵn cho mô hình Gemini trên Nền tảng tác nhân.
adk-a2a-agent-runtime-starter/ ├── reservation_agent/ │ ├── __init__.py │ ├── agent.py │ └── .env ├── logs ├── scripts └── ...
Tiếp theo, hãy cập nhật mã tác nhân
Viết mã cho tác nhân
Mở tệp tác nhân đã tạo:
cloudshell edit reservation_agent/agent.py
Sau đó, hãy thay thế nội dung bằng nội dung sau:
# 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. Chuẩn bị cấu hình máy chủ A2A
Xác định thẻ nhân viên hỗ trợ A2A
Thẻ tác nhân là nội dung mô tả có cấu trúc về các chức năng của tác nhân. Các tác nhân và ứng dụng khác sử dụng thẻ này để khám phá những việc mà tác nhân của bạn làm. Tạo cấu hình thẻ:
cloudshell edit reservation_agent/a2a_config.py
Sao chép nội dung sau vào 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],
)
Tạo trình thực thi A2A
Trình thực thi kết nối giao thức A2A và tác nhân ADK. Nó nhận các yêu cầu A2A, chạy các yêu cầu đó thông qua tác nhân ADK và trả về kết quả dưới dạng các tác vụ A2A:
cloudshell edit reservation_agent/executor.py
Sao chép nội dung sau vào 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())
Trình thực thi tự động phát hiện môi trường của nó: khi GOOGLE_CLOUD_AGENT_ENGINE_ID được đặt (Agent Runtime sẽ chèn giá trị này tại thời điểm triển khai), trình thực thi sẽ sử dụng VertexAiSessionService cho các phiên liên tục. Ở cấp cục bộ, giá trị này sẽ quay lại InMemorySessionService.
Thư mục reservation_agent của bạn hiện sẽ chứa:
reservation_agent/ ├── __init__.py ├── agent.py ├── a2a_config.py ├── executor.py └── .env
7. Chuẩn bị A2A Agent bằng Agent Platform SDK và Kiểm thử cục bộ
Bước này bao bọc tác nhân đặt phòng dưới dạng một tác nhân tuân thủ A2A bằng cách sử dụng lớp A2aAgent của Agent Platform SDK ( tên SDK vẫn sử dụng thuật ngữ vertex để đảm bảo khả năng tương thích ngược), sau đó kiểm thử toàn bộ quy trình giao thức A2A cục bộ – truy xuất thẻ tác nhân, gửi thông báo và truy xuất tác vụ. Đây cũng là đối tượng A2aAgent mà bạn triển khai cho Agent Runtime ở bước tiếp theo.
Thêm phần phụ thuộc
Cài đặt Agent Platform SDK có hỗ trợ Agent Runtime và ADK, cùng với A2A SDK:
uv add "google-cloud-aiplatform[agent_engines,adk]==1.149.0" "a2a-sdk==0.3.26"
Tìm hiểu các thành phần A2A
Để bao bọc một tác nhân ADK cho A2A, bạn cần có 3 thành phần:
- Thẻ của trợ lý ảo – "danh thiếp" mô tả các chức năng, kỹ năng và URL điểm cuối của trợ lý ảo. Các tác nhân khác sử dụng thông tin này để khám phá những việc mà tác nhân của bạn làm.
- Agent Executor – cầu nối giữa giao thức A2A và logic của tác nhân ADK. Nó nhận các yêu cầu A2A, chạy các yêu cầu đó thông qua tác nhân ADK và trả về kết quả dưới dạng các tác vụ A2A.
- A2aAgent – lớp Agent Platform SDK kết hợp thẻ và trình thực thi thành một đơn vị có thể triển khai.
Tạo kịch bản kiểm tra
Tạo tập lệnh sau để kiểm thử cục bộ
cloudshell edit scripts/test_a2a_agent_local.py
Sao chép nội dung sau vào 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())
Kịch bản kiểm tra nhập thẻ tác nhân và trình thực thi mà bạn đã tạo ở bước trước – không có nội dung trùng lặp. Thao tác này sẽ tạo một A2aAgent cục bộ, mô phỏng các lệnh gọi giao thức A2A thông qua các yêu cầu HTTP mô phỏng và xác minh cả 3 thao tác đặt phòng.
Vì không có GOOGLE_CLOUD_AGENT_ENGINE_ID nào được đặt cục bộ, nên trình thực thi sẽ dùng InMemorySessionService. Khi được triển khai cho Agent Runtime, cùng một trình thực thi sẽ tự động chuyển sang VertexAiSessionService cho các phiên liên tục.
Chạy kiểm thử
PYTHONPATH=. uv run python scripts/test_a2a_agent_local.py
Kết quả sẽ trải qua 5 giai đoạn:
- Thẻ trợ lý AI – truy xuất các chức năng và kỹ năng của trợ lý AI
- Tạo lượt đặt chỗ – đặt bàn và trả về một tác vụ kèm theo thông tin xác nhận
- Nhận kết quả của tác vụ – truy xuất tác vụ đã hoàn thành cùng với câu trả lời
- Kiểm tra thông tin đặt chỗ – tìm thông tin đặt chỗ theo số điện thoại
- Huỷ yêu cầu đặt chỗ – huỷ yêu cầu đặt chỗ và xác nhận
Ví dụ về kết quả đầu ra như minh hoạ dưới đây
================================================== 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! ==================================================
Đến đây, bạn đã xác minh: thẻ tác nhân phần mềm A2A mô tả các kỹ năng chính xác, cả 3 thao tác đặt trước đều hoạt động thông qua luồng thông báo/tác vụ của giao thức A2A và trạng thái vẫn duy trì trên các thông báo trong cùng một bối cảnh.
8. Triển khai Reservation Agent vào Agent Runtime
Bước này triển khai tác nhân đặt phòng vào Thời gian chạy Nền tảng tác nhân Gemini Enterprise – một nền tảng không máy chủ, được quản lý toàn diện, lưu trữ tác nhân của bạn và hiển thị tác nhân đó dưới dạng một điểm cuối A2A bảo mật. Sau khi triển khai, mọi ứng dụng khách được uỷ quyền đều có thể khám phá và tương tác với tác nhân thông qua các điểm cuối HTTP A2A tiêu chuẩn.
Tạo vùng lưu trữ tạm thời
Tạo một bộ chứa Cloud Storage để dàn xếp Thời gian chạy của tác nhân. Agent Runtime sử dụng vùng chứa này để tải mã và các phần phụ thuộc của tác nhân lên trong quá trình triển khai:
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
Tạo tập lệnh triển khai
Tiếp theo, chúng ta sẽ cần chuẩn bị tập lệnh triển khai
cloudshell edit scripts/deploy_a2a_agent_runtime.py
Sao chép nội dung sau vào 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()
Tập lệnh triển khai nhập cùng agent_card và ReservationAgentExecutor được dùng trong kiểm thử cục bộ – không cần mã trùng lặp. Agent Runtime sẽ chuyển đổi đối tượng A2aAgent cùng với các phần phụ thuộc của đối tượng đó thành chuỗi để triển khai. Ở cuối tập lệnh triển khai, tập lệnh này sẽ ghi giá trị RESERVATION_AGENT_RESOURCE_NAME vào tệp .env
Triển khai cho Thời gian chạy của tác nhân
Chạy tập lệnh triển khai:
PYTHONPATH=. uv run python scripts/deploy_a2a_agent_runtime.py
Quá trình triển khai mất từ 3 đến 5 phút. Tập lệnh này cung cấp một điểm cuối không máy chủ trên Agent Runtime để lưu trữ tác nhân đặt phòng. Sau khi triển khai thành công, bạn sẽ thấy kết quả tương tự như dưới đây
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
Bạn có thể xem tác nhân đã triển khai trong bảng điều khiển đám mây. Tìm Agent Platform trong thanh tìm kiếm của bảng điều khiển

Sau đó, trên thẻ bên trái, hãy di chuột đến Agents rồi chọn Deployments

Bạn sẽ thấy Reservation Agent xuất hiện trong danh sách triển khai như hình dưới đây

Kiểm thử nhân viên hỗ trợ đã triển khai
Bây giờ, chúng ta đã sẵn sàng kiểm thử tác nhân đã triển khai, hãy tạo một kịch bản kiểm tra cho tác nhân đã triển khai:
cloudshell edit scripts/test_a2a_agent_runtime.py
Sao chép nội dung sau vào 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())
Sau đó, hãy chạy kiểm thử
source .env
uv run python scripts/test_a2a_agent_runtime.py
Đầu ra cho thấy thẻ tác nhân có kỹ năng "Đặt chỗ nhà hàng", sau đó là tác vụ hoàn tất với thông tin xác nhận đặt chỗ.
================================================== 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! ==================================================
Giờ đây, tác nhân đặt phòng đang chạy thành công dưới dạng một điểm cuối A2A được quản lý trên Agent Runtime.
9. Tích hợp tác nhân đặt phòng A2A với tác nhân nhà hàng gốc
Bước này nâng cấp tác nhân nhà hàng để sử dụng tác nhân đặt phòng đã triển khai làm tác nhân phụ A2A từ xa. Trình điều phối chạy cục bộ trong khi tác nhân đặt phòng chạy trên Agent Runtime – một quy trình tích hợp một phần giúp xác thực kết nối A2A trước khi triển khai đầy đủ.
Phân giải URL thẻ nhân viên hỗ trợ A2A
RemoteA2aAgent cần URL thẻ của tác nhân đặt phòng đã triển khai để khám phá các chức năng của tác nhân đó. Tạo một tập lệnh tìm nạp URL này từ Thời gian chạy của tác nhân và ghi URL đó vào .env của tác nhân nhà hàng:
cloudshell edit scripts/resolve_agent_card_url.py
Sao chép nội dung sau vào 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())
Chạy tập lệnh để điền URL thẻ đại lý vào tệp .env
uv run python scripts/resolve_agent_card_url.py
source .env
Cập nhật tác nhân nhà hàng
Mở tệp tác nhân nhà hàng:
cloudshell edit restaurant_agent/agent.py
Sau đó, hãy thay thế nội dung bằng phiên bản mới có bao gồm tác nhân đặt trước từ xa dưới dạng tác nhân phụ:
# 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],
)
Sau đây là những thay đổi chính so với phiên bản trước:
GoogleCloudAuth– một trình xử lýhttpx.Authtuỳ chỉnh giúp làm mới mã truy cập Google Cloud trước mỗi yêu cầu. Agent Runtime yêu cầu các lệnh gọi A2A đã xác thực và mã thông báo sẽ hết hạn sau một khoảng thời gian.RemoteA2aAgentđọcRESERVATION_AGENT_CARD_URLtừ.env(do tập lệnh phân giải ghi) và sử dụnghttpx_clientđã xác thực- Được đăng ký làm tác nhân phụ – Trình điều phối của ADK sẽ tự động uỷ quyền các yêu cầu đặt phòng cho tác nhân phụ này
- Đã cập nhật hướng dẫn để đề cập đến tính năng uỷ quyền đặt phòng
Kiểm thử tác nhân tích hợp cục bộ
Nhân viên hỗ trợ khởi đầu cần tích hợp với MCP Toolbox. Tệp bắt buộc phải được cung cấp từ lớp học lập trình trước hoặc từ kho lưu trữ khởi đầu. Chúng ta chỉ cần đảm bảo rằng quy trình hộp công cụ chạy đúng cách.
Nếu TOOLBOX_URL trong .env của bạn đã trỏ đến một dịch vụ Cloud Run (từ lớp học lập trình trước hoặc có thể từ full_setup.sh của kho lưu trữ khởi động), thì bạn có thể bỏ qua bước này – tác nhân sẽ kết nối với Toolbox đã triển khai.
Nếu bạn cần một Toolbox cục bộ, hãy kiểm tra xem có Toolbox nào đang chạy hay chưa trước khi bắt đầu một phiên bản mới:
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
Sau đó, chúng ta có thể thử tương tác với tác nhân nhà hàng thông qua giao diện người dùng dành cho nhà phát triển web ADK
uv run adk web --allow_origins "regex:https://.*\.cloudshell\.dev" --port 8080
Mở giao diện người dùng web ADK bằng Cloud Shell Web Preview (nhấp vào nút Web Preview, thay đổi cổng thành 8080), sau đó chọn restaurant_agent

Thử nghiệm một cuộc trò chuyện kết hợp:
Truy vấn trình đơn
What Italian dishes do you have?
Yêu cầu đặt chỗ
I want to create reservation under name Bob, phone number 123456
Kiểm tra thông tin đặt chỗ
Tạo phiên mới ( bắt đầu cuộc trò chuyện mới):
Check the reservation for 123456



Dừng quy trình adk web bằng cách nhấn tổ hợp phím Ctrl+C hai lần. Tiếp theo, hãy hoàn tất hệ thống bằng cách triển khai đầy đủ tác nhân
10. Triển khai Restaurant Agent mới cập nhật lên Cloud Run
Bước này triển khai lại tác nhân nhà hàng vào Cloud Run bằng cách tích hợp A2A, hoàn tất hệ thống đa tác nhân được triển khai đầy đủ.
Cấp quyền truy cập vào Thời gian chạy của tác nhân
Tài khoản dịch vụ Cloud Run cần có quyền gọi Thời gian chạy của tác nhân. Cấp vai trò roles/aiplatform.user cho tài khoản dịch vụ Compute Engine mặc định:
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"
Triển khai lên Cloud Run
Trong quá trình thiết lập này, chúng tôi giả định rằng dịch vụ tác nhân nhà hàng đã tồn tại từ lớp học lập trình trước hoặc bằng cách chạy scripts/full_setup.sh nếu bạn bắt đầu từ đầu. Thao tác này sẽ triển khai lại bằng mã đã cập nhật (tích hợp RemoteA2aAgent mới) và thêm URL thẻ nhân viên đặt phòng làm một biến môi trường mới – các biến môi trường hiện có (TOOLBOX_URL, GOOGLE_CLOUD_PROJECT, v.v.) sẽ được giữ nguyên:
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
Kiểm thử hệ thống đã triển khai đầy đủ
Lấy URL của dịch vụ đã triển khai:
AGENT_URL=$(gcloud run services describe restaurant-agent --region=$REGION --format='value(status.url)')
echo "Agent URL: $AGENT_URL"
Mở URL trong trình duyệt. Giao diện người dùng web ADK sẽ tải – đây là giao diện tương tự mà bạn đã sử dụng cục bộ, hiện đang chạy trên Cloud Run.
Bạn có thể trò chuyện thoải mái với tác nhân
Truy vấn trình đơn
What spicy dishes do you have?
Yêu cầu đặt chỗ
Book a table for 4 on Friday at 7pm. Name: Eve, Phone: 555-0505
Kiểm tra thông tin đặt chỗ
Tạo phiên mới ( bắt đầu cuộc trò chuyện mới):
Check reservation for 555-0505


Hệ thống đa tác nhân đã được triển khai đầy đủ. Tác nhân nhà hàng trên Cloud Run điều phối giữa 2 dịch vụ phụ trợ: MCP Toolbox cho các hoạt động liên quan đến thực đơn và tác nhân đặt chỗ A2A trên Agent Runtime.
11. Xin chúc mừng!
Bạn đã xây dựng và triển khai một hệ thống đa tác nhân bằng giao thức A2A trên Google Cloud.
Kiến thức bạn học được
- Xây dựng một tác nhân ADK sử dụng trạng thái phiên (
ToolContext) để quản lý dữ liệu đặt phòng mà không cần cơ sở dữ liệu - Triển khai một tác nhân A2A vào Thời gian chạy tác nhân bằng SDK Nền tảng tác nhân
- Đã sử dụng một tác nhân A2A từ xa từ một tác nhân ADK khác bằng cách dùng
RemoteA2aAgentlàm tác nhân phụ - Kiểm thử hệ thống theo từng bước: A2A cục bộ → A2A đã triển khai → tích hợp một phần → triển khai đầy đủ
Dọn dẹp
Để tránh bị tính phí vào tài khoản Google Cloud của bạn, hãy xoá các tài nguyên đã tạo trong lớp học lập trình này.
Cách 1: Xoá dự án (nên dùng)
gcloud projects delete $GOOGLE_CLOUD_PROJECT
Cách 2: Xoá từng tài nguyên
# 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