Nhóm tác nhân của Google trong thực tế: ADK, A2A, MCP trên Google Cloud

Thực hành về ngăn xếp tác nhân của Google:
ADK, A2A, MCP trên Google Cloud

Thông tin về lớp học lập trình này

subjectLần cập nhật gần đây nhất: thg 7 1, 2025
account_circleTác giả: Christina Lin

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

Chào mừng bạn! Hôm nay, chúng ta sẽ bắt đầu một hành trình khá thú vị. Hãy bắt đầu bằng cách nghĩ về một nền tảng sự kiện xã hội phổ biến InstaVibe. Mặc dù tính năng này rất thành công, nhưng chúng tôi biết rằng đối với một số người dùng, việc lên kế hoạch thực tế cho các hoạt động nhóm có thể giống như một công việc lặt vặt. Hãy tưởng tượng bạn phải tìm hiểu xem bạn bè mình quan tâm đến điều gì, sau đó xem xét vô số lựa chọn về sự kiện hoặc địa điểm, rồi cuối cùng là điều phối mọi thứ. Thật là nhiều! Đây chính là nơi chúng ta có thể giới thiệu AI, cụ thể là các tác nhân thông minh, để tạo ra sự khác biệt thực sự.

Ý tưởng là xây dựng một hệ thống mà các tác nhân này có thể xử lý những việc nặng nhọc, chẳng hạn như "nghe" một cách thông minh để hiểu được lựa chọn ưu tiên của người dùng và bạn bè, sau đó chủ động đề xuất các hoạt động tuyệt vời, phù hợp. Mục tiêu của chúng tôi là biến việc lên kế hoạch cho các hoạt động xã hội trên InstaVibe trở nên liền mạch và thú vị. Để bắt đầu xây dựng những trợ lý thông minh này, chúng ta cần đặt nền tảng vững chắc bằng các công cụ phù hợp.

Sau đây là khái niệm mà bạn sẽ thấy:

Trang tiêu đề

Kiến thức cơ bản về ADK của Google: Nắm vững kiến thức cơ bản về cách tạo trợ lý thông minh đầu tiên bằng Bộ phát triển trợ lý (ADK) của Google. Tìm hiểu các thành phần thiết yếu, vòng đời của tác nhân và cách tận dụng hiệu quả các công cụ tích hợp sẵn của khung.

Mở rộng chức năng của tác nhân bằng Giao thức ngữ cảnh mô hình (MCP): Tìm hiểu cách trang bị cho tác nhân các công cụ và ngữ cảnh tuỳ chỉnh, cho phép tác nhân thực hiện các nhiệm vụ chuyên biệt và truy cập vào thông tin cụ thể. Giới thiệu khái niệm Giao thức ngữ cảnh mô hình (MCP). Bạn sẽ tìm hiểu cách thiết lập máy chủ MCP để cung cấp ngữ cảnh này.

Thiết kế hoạt động tương tác và điều phối của tác nhân: Hãy chuyển sang tìm hiểu hoạt động điều phối của tác nhân thay vì chỉ một tác nhân. Thiết kế các mẫu tương tác, từ quy trình làm việc tuần tự đơn giản đến các tình huống phức tạp liên quan đến vòng lặp, logic có điều kiện và xử lý song song. Giới thiệu khái niệm về tác nhân phụ trong khung ADK để quản lý các tác vụ mô-đun.

Tạo hệ thống nhiều tác nhân cộng tác: Khám phá cách thiết kế hệ thống mà nhiều tác nhân cộng tác để đạt được các mục tiêu phức tạp. Tìm hiểu và triển khai giao thức giao tiếp Tác nhân với tác nhân (A2A), thiết lập cách thức chuẩn hoá để các tác nhân phân tán (có thể chạy trên nhiều máy hoặc dịch vụ) tương tác một cách đáng tin cậy.

Triển khai tác nhân trên Google Cloud: Chuyển đổi các ứng dụng tác nhân của bạn từ môi trường phát triển sang đám mây. Tìm hiểu các phương pháp hay nhất để thiết kế và triển khai các hệ thống đa tác nhân mạnh mẽ, có thể mở rộng trên Google Cloud Platform (GCP). Tìm hiểu thông tin chi tiết về cách tận dụng các dịch vụ của GCP như Cloud Run và khám phá các tính năng của Google Agent Engine mới nhất để lưu trữ và quản lý các trợ lý của bạn.

2. Kiến trúc

Lập kế hoạch mạng xã hội dựa trên AI bằng InstaVibe

Dịch vụ Lắng nghe cộng đồng mạng xã hội là gì?

Nghe trên mạng xã hội là quá trình theo dõi các cuộc trò chuyện trên mạng trên các nền tảng như mạng xã hội, diễn đàn và trang web tin tức để hiểu những gì mọi người đang nói về một chủ đề, thương hiệu hoặc ngành. Dữ liệu này cung cấp thông tin chi tiết có giá trị về tâm lý của công chúng, xu hướng và nhu cầu của người dùng. Trong hội thảo này, chúng ta sẽ tận dụng khái niệm này trong một hệ thống dựa trên tác nhân.

Bạn là thành viên của Nhóm InstaVibe

Hãy tưởng tượng bạn làm việc tại "InstaVibe", một công ty khởi nghiệp thành công với nền tảng sự kiện xã hội phổ biến nhắm đến thanh niên. Mọi thứ đang diễn ra tốt đẹp, nhưng giống như nhiều công ty công nghệ khác, nhóm của bạn phải chịu áp lực từ các nhà đầu tư để đổi mới bằng AI. Trong nội bộ, bạn cũng nhận thấy một phân khúc người dùng không tương tác nhiều như những người dùng khác – có thể họ ít có xu hướng khởi tạo hoạt động nhóm hoặc thấy quá trình lập kế hoạch gặp nhiều khó khăn. Đối với công ty của bạn, điều này có nghĩa là mức độ gắn bó với nền tảng thấp hơn trong nhóm người dùng quan trọng này.

Nghiên cứu của nhóm bạn cho thấy rằng tính năng hỗ trợ dựa trên AI có thể cải thiện đáng kể trải nghiệm cho những người dùng này. Ý tưởng là đơn giản hoá quy trình lập kế hoạch cho các hoạt động xã hội bằng cách chủ động đề xuất các hoạt động phù hợp dựa trên mối quan tâm của người dùng và bạn bè của họ. Câu hỏi mà bạn và các đồng nghiệp của bạn phải đối mặt là: Làm cách nào để các tác nhân AI tự động hoá những nhiệm vụ thường tốn nhiều thời gian như khám phá mối quan tâm, nghiên cứu hoạt động và có thể là điều phối ban đầu?

Giải pháp dựa trên tác nhân (Khái niệm nguyên mẫu)

Bạn đề xuất phát triển một tính năng nguyên mẫu do hệ thống nhiều tác nhân cung cấp. Dưới đây là thông tin chi tiết về khái niệm:

Trường hợp sử dụng

  • Tác nhân phân tích hồ sơ xã hội: Tác nhân này sử dụng các kỹ thuật theo dõi mạng xã hội để phân tích mối quan hệ, lượt tương tác của người dùng và các xu hướng công khai có thể rộng hơn liên quan đến lựa chọn ưu tiên của người dùng. Mục đích của tính năng này là xác định các mối quan tâm chung và đặc điểm hoạt động phù hợp (ví dụ: lựa chọn ưu tiên cho các cuộc tụ họp yên tĩnh hơn, sở thích cụ thể).
  • Trình lập kế hoạch sự kiện: Bằng cách sử dụng thông tin chi tiết từ Trình lập hồ sơ xã hội, trình này sẽ tìm kiếm các tài nguyên trực tuyến về các sự kiện, địa điểm hoặc ý tưởng cụ thể phù hợp với các tiêu chí đã xác định (chẳng hạn như vị trí, mối quan tâm).
  • Trình tác nhân tương tác với nền tảng (sử dụng MCP): Trình tác nhân này lấy kế hoạch đã hoàn tất từ Trình tác nhân lập kế hoạch hoạt động. Chức năng chính của lớp này là tương tác trực tiếp với nền tảng InstaVibe bằng cách sử dụng công cụ MCP (Giao thức ngữ cảnh mô hình) được xác định trước. Công cụ này cung cấp cho nhân viên hỗ trợ khả năng cụ thể để soạn thư đề xuất sự kiện và tạo bài đăng nêu rõ kế hoạch.
  • Trình điều phối tác nhân: Tác nhân này đóng vai trò là trình điều phối trung tâm. Ứng dụng này nhận được yêu cầu ban đầu của người dùng từ nền tảng InstaVibe, hiểu được mục tiêu tổng thể (ví dụ: "lên kế hoạch cho một sự kiện cho tôi và bạn bè"), sau đó uỷ quyền các nhiệm vụ cụ thể cho các tác nhân chuyên biệt phù hợp theo trình tự hợp lý. Lớp này quản lý luồng thông tin giữa các tác nhân và đảm bảo kết quả cuối cùng được gửi lại cho người dùng.

Các thành phần và công nghệ kiến trúc chính

Kiến trúc

Google Cloud Platform (GCP):

  • Vertex AI:
    • Mô hình Gemini: Cung cấp quyền truy cập vào các Mô hình ngôn ngữ lớn (LLM) hiện đại của Google như Gemini, giúp tăng cường khả năng lập luận và ra quyết định của các trợ lý.
    • Công cụ tác nhân Vertex AI: Một dịch vụ được quản lý dùng để triển khai, lưu trữ và mở rộng quy mô tác nhân điều phối của chúng tôi, đơn giản hoá việc sản xuất và tóm tắt các vấn đề phức tạp về cơ sở hạ tầng.
  • Cloud Run: Một nền tảng không máy chủ để triển khai các ứng dụng được đóng gói trong vùng chứa. Chúng tôi sử dụng API này để:
    • Lưu trữ ứng dụng web InstaVibe chính.
    • Triển khai các tác nhân hỗ trợ A2A riêng lẻ (Trình lập kế hoạch, Phân tích xã hội, Tương tác trên nền tảng) dưới dạng dịch vụ vi mô độc lập.
    • Chạy Máy chủ công cụ MCP để cung cấp các API nội bộ của InstaVibe cho các đại lý.
  • Spanner: Một cơ sở dữ liệu quan hệ được quản lý toàn diện, phân phối trên toàn cầu và cực kỳ nhất quán. Trong hội thảo này, chúng ta sẽ tận dụng các chức năng của công cụ này dưới dạng Cơ sở dữ liệu đồ thị bằng cách sử dụng các tính năng truy vấn và GRAPH DDL để:
    • Mô hình hoá và lưu trữ các mối quan hệ xã hội phức tạp (người dùng, mối quan hệ bạn bè, tham dự sự kiện, bài đăng).
    • Cho phép truy vấn hiệu quả các mối quan hệ này cho các tác nhân Phân tích xã hội.
  • Artifact Registry (Cơ sở lưu trữ cấu phần phần mềm): Dịch vụ được quản lý toàn diện để lưu trữ, quản lý và bảo mật hình ảnh vùng chứa.
  • Cloud Build: Dịch vụ giúp chạy bản dựng trên Google Cloud. Chúng tôi sử dụng công cụ này để tự động tạo hình ảnh vùng chứa Docker từ mã nguồn của ứng dụng và tác nhân.
  • Cloud Storage: Được các dịch vụ như Cloud Build sử dụng để lưu trữ cấu phần phần mềm của bản dựng và Agent Engine sử dụng cho các nhu cầu hoạt động của nó.
  • Khung và giao thức của tác nhân chính:
    • Bộ phát triển tác nhân (ADK) của Google: Khung chính để:
      • Xác định logic cốt lõi, hành vi và tập lệnh cho từng tác nhân thông minh.
      • Quản lý vòng đời, trạng thái và bộ nhớ của tác nhân (trạng thái phiên ngắn hạn và kiến thức có thể dài hạn).
      • Tích hợp các công cụ (như Google Tìm kiếm hoặc các công cụ tuỳ chỉnh) mà các nhân viên hỗ trợ có thể sử dụng để tương tác với thế giới.
      • Điều phối quy trình công việc nhiều tác nhân, bao gồm cả việc thực thi tuần tự, lặp lại và song song của các tác nhân phụ.
    • Giao thức giao tiếp giữa các tác nhân (A2A): Một tiêu chuẩn mở cho phép:
      • Giao tiếp và cộng tác trực tiếp, chuẩn hoá giữa các tác nhân AI khác nhau, ngay cả khi chúng đang chạy dưới dạng các dịch vụ riêng biệt hoặc trên các máy khác nhau.
      • Các nhân viên hỗ trợ khám phá khả năng của nhau (thông qua Thẻ nhân viên hỗ trợ) và uỷ quyền công việc. Điều này rất quan trọng để tác nhân Điều phối của chúng tôi tương tác với các tác nhân Lập kế hoạch, Mạng xã hội và Nền tảng chuyên biệt.
    • Thư viện Python A2A (a2a-python): Thư viện cụ thể dùng để tạo các tác nhân ADK nói giao thức A2A. Thư viện này cung cấp các thành phần phía máy chủ cần thiết để:
      • Hiển thị các tác nhân của chúng tôi dưới dạng máy chủ tuân thủ A2A.
      • Tự động xử lý việc phân phát "Thẻ đại lý" để khám phá.
      • Nhận và quản lý các yêu cầu tác vụ đến từ các tác nhân khác (chẳng hạn như Trình điều phối).
    • Giao thức theo bối cảnh mô hình (MCP): Một tiêu chuẩn mở cho phép các tác nhân:
      • Kết nối và sử dụng các công cụ, nguồn dữ liệu và hệ thống bên ngoài theo cách chuẩn hoá.
      • Tác nhân tương tác với nền tảng của chúng tôi sử dụng ứng dụng MCP để giao tiếp với máy chủ MCP. Đổi lại, máy chủ này sẽ hiển thị các công cụ để tương tác với các API hiện có của nền tảng InstaVibe.
  • Công cụ gỡ lỗi:
    • Công cụ kiểm tra A2A: Công cụ kiểm tra A2A là một công cụ gỡ lỗi dựa trên web được sử dụng trong suốt hội thảo này để kết nối, kiểm tra và tương tác với các tác nhân hỗ trợ A2A của chúng tôi. Mặc dù không phải là một phần của cấu trúc sản xuất cuối cùng, nhưng đây là một phần thiết yếu trong quy trình phát triển của chúng tôi. API này cung cấp:
      • Trình xem thẻ nhân viên hỗ trợ: Để tìm nạp và xác thực các chức năng công khai của nhân viên hỗ trợ.
      • Giao diện trò chuyện trực tiếp: Để gửi tin nhắn trực tiếp đến một nhân viên hỗ trợ đã triển khai để kiểm thử ngay lập tức.
      • Bảng điều khiển gỡ lỗi: Để xem các thông báo JSON-RPC thô đang được trao đổi giữa trình kiểm tra và tác nhân.
  • Mô hình ngôn ngữ (LLM): "Bộ não" của hệ thống:
    • Mô hình Gemini của Google: Cụ thể, chúng tôi sử dụng các phiên bản như gemini-2.0-flash. Những mẫu này được chọn cho:
      • Suy luận nâng cao và làm theo hướng dẫn: Khả năng hiểu các câu lệnh phức tạp, làm theo hướng dẫn chi tiết và lý luận về các nhiệm vụ giúp chúng phù hợp với việc hỗ trợ quá trình ra quyết định của tác nhân.
      • Sử dụng công cụ (Gọi hàm): Mô hình Gemini rất giỏi trong việc xác định thời điểm và cách sử dụng các công cụ được cung cấp thông qua ADK, cho phép các tác nhân thu thập thông tin hoặc thực hiện hành động.
      • Hiệu quả (Mô hình Flash): Các biến thể "flash" mang đến sự cân bằng tốt giữa hiệu suất và tính hiệu quả về chi phí, phù hợp với nhiều tác vụ của tác nhân tương tác cần phản hồi nhanh.

Bạn cần khoản tín dụng Google Cloud?

3. Trước khi bắt đầu

👉Nhấp vào Kích hoạt Cloud Shell ở đầu bảng điều khiển Google Cloud (Đây là biểu tượng hình dạng thiết bị đầu cuối ở đầu ngăn Cloud Shell), Cloud Shell

👉Nhấp vào nút "Mở Trình chỉnh sửa" (nút này trông giống như một thư mục đang mở có bút chì). Thao tác này sẽ mở Trình chỉnh sửa mã Cloud Shell trong cửa sổ. Bạn sẽ thấy một trình khám phá tệp ở bên trái. Cloud Shell

👉Nhấp vào nút Đăng nhập bằng mã trên đám mây trong thanh trạng thái dưới cùng như minh hoạ. Uỷ quyền cho trình bổ trợ theo hướng dẫn. Nếu bạn thấy Cloud Code - no project (Mã trên đám mây – không có dự án) trong thanh trạng thái, hãy chọn mục đó, sau đó chọn "Select a Google Cloud Project" (Chọn dự án Google Cloud) trong trình đơn thả xuống, rồi chọn một dự án Google Cloud cụ thể trong danh sách dự án mà bạn đã tạo. Cloud Shell

👉 Tìm Mã dự án trên Google Cloud:

  • Mở Google Cloud Console: https://console.cloud.google.com
  • Chọn dự án bạn muốn sử dụng cho hội thảo này trong trình đơn thả xuống dự án ở đầu trang.
  • Mã dự án của bạn sẽ xuất hiện trong thẻ Thông tin dự án trên Trang tổng quan

Cloud Shell

👉Mở thiết bị đầu cuối trong IDE trên đám mây, Cloud Shell

👉💻 Trong dòng lệnh, hãy xác minh rằng bạn đã được xác thực và dự án được đặt thành mã dự án của bạn bằng lệnh sau:

gcloud auth list

👉💻 Sao chép dự án instavibe-bootstrap từ GitHub:

git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh

Tìm hiểu cấu trúc dự án

Trước khi bắt đầu xây dựng, hãy dành chút thời gian để tìm hiểu bố cục của dự án instavibe-bootstrap mà bạn vừa nhân bản. Việc này sẽ giúp bạn biết nơi tìm và chỉnh sửa tệp trong suốt hội thảo.

instavibe-bootstrap/
├── agents/
  ├── orchestrate/
  ├── planner/
  ├── platform_mcp_client/
  └── social/
├── instavibe/
  ├── static/
  └── templates/
├── tools/
  └── instavibe/
├── utils/
├── init.sh
└── set_env.sh

Dưới đây là thông tin chi tiết về các thư mục chính:

  • agents/: Đây là trung tâm của hệ thống AI của chúng tôi. Mỗi thư mục con (planner/, social/, v.v.) chứa mã nguồn cho một tác nhân thông minh cụ thể.
    • agent.py: Bên trong thư mục của mỗi tác nhân, đây là tệp chính chứa logic của tác nhân.
    • a2a_server.py: Tệp này gói tác nhân ADK bằng máy chủ Tác nhân với tác nhân (A2A).
    • Dockerfile: Xác định cách tạo hình ảnh vùng chứa để triển khai tác nhân cho Cloud Run hoặc Agent Engine.
  • instavibe/: Thư mục này chứa toàn bộ mã nguồn cho ứng dụng web InstaVibe.
  • tools/: Thư mục này dùng để tạo các công cụ bên ngoài mà nhân viên hỗ trợ của chúng tôi có thể sử dụng.
    • instavibe/ chứa Máy chủ Giao thức ngữ cảnh mô hình (MCP).

Cấu trúc mô-đun này tách ứng dụng web khỏi nhiều thành phần AI, giúp toàn bộ hệ thống dễ quản lý, kiểm thử và triển khai hơn.

👉💻 Chạy tập lệnh khởi chạy:

Tập lệnh này sẽ nhắc bạn nhập Mã dự án trên Google Cloud.

Nhập Mã dự án Google Cloud mà bạn tìm thấy ở bước cuối cùng khi tập lệnh init.sh nhắc:

cd ~/instavibe-bootstrap
./init.sh

👉💻 Đặt mã dự án cần thiết:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 Chạy lệnh sau để bật các API Google Cloud cần thiết:

gcloud services enable  run.googleapis.com \
                       
cloudfunctions.googleapis.com \
                       
cloudbuild.googleapis.com \
                       
artifactregistry.googleapis.com \
                       
spanner.googleapis.com \
                       
apikeys.googleapis.com \
                       
iam.googleapis.com \
                       
compute.googleapis.com \
                       
aiplatform.googleapis.com \
                       
cloudresourcemanager.googleapis.com \
                       
maps-backend.googleapis.com

👉💻 Thiết lập tất cả biến môi trường cần thiết:

export PROJECT_ID=$(gcloud config get project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export SERVICE_ACCOUNT_NAME=$(gcloud compute project-info describe --format="value(defaultServiceAccount)")
export SPANNER_INSTANCE_ID="instavibe-graph-instance"
export SPANNER_DATABASE_ID="graphdb"
export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_GENAI_USE_VERTEXAI=TRUE
export GOOGLE_CLOUD_LOCATION="us-central1"

Thiết lập quyền

👉💻 Cấp quyền. Trong dòng lệnh, hãy chạy :

gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/spanner.admin"

# Spanner Database User
gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/spanner.databaseUser"

# Artifact Registry Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/artifactregistry.admin"

# Cloud Build Editor
gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/cloudbuild.builds.editor"

# Cloud Run Admin
gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/run.admin"

# IAM Service Account User
gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/iam.serviceAccountUser"

# Vertex AI User
gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/aiplatform.user"

# Logging Writer (to allow writing logs)
gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/logging.logWriter"


gcloud projects add-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/logging.viewer"


👉 Xác thực kết quả trong Bảng điều khiển IAMCloud Shell

👉💻 Chạy các lệnh sau trong dòng lệnh để tạo kho lưu trữ Registry Artifact (Registry Artifact). Tất cả hình ảnh Docker cho các tác nhân, máy chủ MCP và ứng dụng InstaVibe của chúng tôi đều được lưu trữ tại đây trước khi triển khai lên Cloud Run hoặc Agent Engine.

export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
 
--repository-format=docker \
 
--location=us-central1 \
 
--description="Docker repository for InstaVibe workshop"

Thiết lập nền tảng Maps cho Khoá API

Để sử dụng các dịch vụ của Google Maps trong ứng dụng InstaVibe, bạn cần tạo một khoá API và hạn chế khoá đó một cách thích hợp.

👉 Trong một thẻ mới, hãy chuyển đến API và Dịch vụ > Thông tin xác thực. Trên trang "Thông tin xác thực", hãy nhấp vào nút + TẠO THÔNG TIN XÁC THỰC ở trên cùng. Chọn khoá API trong trình đơn thả xuống. văn bản thay thế

👉 Một hộp thoại sẽ xuất hiện, trong đó có khoá API mới tạo của bạn. Bạn sẽ cần thông tin này sau cho cấu hình ứng dụng.

👉 Nhấp vào CLOSE (ĐÓNG) trên hộp thoại "API key created" (Đã tạo khoá API).

👉 Bạn sẽ thấy khoá API mới của mình trong danh sách (ví dụ: "Khoá API 1"). Nhấp vào biểu tượng bánh hamburger ở bên phải, chọn Chỉnh sửa khoá API để mở trang "Hạn chế và đổi tên khoá API". văn bản thay thế

👉 Trong trường Tên ở trên cùng, hãy thay đổi tên mặc định thành: Khoá API Nền tảng Maps (🚨🚨LƯU Ý QUAN TRỌNG🚨🚨 Vui lòng sử dụng tên này!)

Maps Platform API Key

👉 Trong phần "Hạn chế ứng dụng", hãy đảm bảo bạn chọn Không.

👉 Trong phần "Hạn chế API", hãy chọn nút chọn Hạn chế khoá.

👉 Nhấp vào trình đơn thả xuống Chọn API. Trong hộp tìm kiếm xuất hiện, hãy nhập Maps JavaScript API rồi chọn Maps JavaScript API trong danh sách. văn bản thay thế

👉 Nhấp vào OK.

👉 Nhấp vào nút LƯU ở cuối trang.

Kết quả chính

Bạn đã tạo thành công một khoá API có tên "Khoá API của Nền tảng Maps", chỉ cho phép sử dụng "Maps JavaScript API" và đảm bảo API này được bật cho dự án của bạn.

4. Thiết lập cơ sở dữ liệu đồ thị

Trước khi có thể xây dựng các tác nhân thông minh, chúng ta cần có cách lưu trữ và hiểu rõ các mối kết nối phong phú trong mạng xã hội InstaVibe. Đây là lúc Cơ sở dữ liệu đồ thị xuất hiện. Không giống như cơ sở dữ liệu quan hệ truyền thống lưu trữ dữ liệu trong các bảng gồm các hàng và cột, cơ sở dữ liệu đồ thị được thiết kế riêng để biểu thị và truy vấn dữ liệu theo các nút (như người, sự kiện hoặc bài đăng) và mối quan hệ (cạnh) kết nối các nút đó (như mối quan hệ bạn bè, tham dự sự kiện hoặc đề cập). Cấu trúc này cực kỳ mạnh mẽ đối với các ứng dụng mạng xã hội vì nó phản ánh cách mạng xã hội trong thế giới thực được cấu trúc, giúp bạn dễ dàng khám phá cách các thực thể khác nhau kết nối với nhau.

Chúng tôi đang triển khai cơ sở dữ liệu đồ thị này bằng Google Cloud Spanner. Mặc dù Spanner chủ yếu được biết đến là một cơ sở dữ liệu quan hệ được phân phối trên toàn cầu và có tính nhất quán cao, nhưng nó cũng cho phép chúng ta xác định và truy vấn các cấu trúc biểu đồ ngay trên các bảng quan hệ.

Điều này mang lại cho chúng tôi những lợi ích kết hợp của khả năng mở rộng, tính nhất quán trong giao dịch và giao diện SQL quen thuộc của Spanner, cùng với khả năng biểu đạt của các truy vấn biểu đồ để phân tích các động lực xã hội phức tạp, rất quan trọng đối với các tính năng sử dụng AI của chúng tôi.

👉💻 Trong thiết bị đầu cuối IDE Cloud Shell. Cung cấp cơ sở hạ tầng cần thiết trên Google Cloud. Chúng ta sẽ bắt đầu bằng cách tạo một Thực thể Spanner. Thực thể này đóng vai trò là vùng chứa chuyên dụng cho cơ sở dữ liệu của chúng ta. Sau khi thực thể đã sẵn sàng, chúng ta sẽ tạo Cơ sở dữ liệu Spanner thực tế trong đó. Cơ sở dữ liệu này sẽ lưu trữ tất cả các bảng và dữ liệu biểu đồ của InstaVibe:

. ~/instavibe-bootstrap/set_env.sh

gcloud spanner instances create $SPANNER_INSTANCE_ID \
 
--config=regional-us-central1 \
 
--description="GraphDB Instance InstaVibe" \
 
--processing-units=100 \
 
--edition=ENTERPRISE

gcloud spanner databases create $SPANNER_DATABASE_ID \
 
--instance=$SPANNER_INSTANCE_ID \
 
--database-dialect=GOOGLE_STANDARD_SQL

👉💻 Cấp quyền đọc/ghi Spanner cho tài khoản dịch vụ mặc định

echo "Granting Spanner read/write access to ${SERVICE_ACCOUNT_NAME} for database ${SPANNER_DATABASE_ID}..."

gcloud spanner databases add-iam-policy-binding ${SPANNER_DATABASE_ID} \
 
--instance=${SPANNER_INSTANCE_ID} \
 
--member="serviceAccount:${SERVICE_ACCOUNT_NAME}" \
 
--role="roles/spanner.databaseUser" \
 
--project=${PROJECT_ID}

👉💻 Bây giờ. Chúng ta sẽ thiết lập môi trường ảo Python, cài đặt các gói Python bắt buộc, sau đó thiết lập giản đồ Cơ sở dữ liệu đồ thị trong Spanner và tải giản đồ đó bằng dữ liệu ban đầu rồi chạy tập lệnh setup.py.

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap
python -m venv env
source env/bin/activate
pip install -r requirements.txt
cd instavibe
python setup.py

👉 Trong một thẻ trình duyệt mới, hãy truy cập vào Google Cloud Console, chuyển đến Spanner, bạn sẽ thấy danh sách các phiên bản Spanner. Nhấp vào instavibe-graph-instance. thực thể spanner 👉 Trên trang tổng quan về thực thể, bạn sẽ thấy danh sách cơ sở dữ liệu trong thực thể đó. Nhấp vào graphdbcơ sở dữ liệu spanner

👉 Trong ngăn điều hướng bên trái cho cơ sở dữ liệu, hãy nhấp vào Spanner Studio spanner studio

👉 Trong trình chỉnh sửa truy vấn (thẻ Truy vấn không có tiêu đề), hãy dán truy vấn SQL của Biểu đồ sau. Truy vấn này sẽ tìm tất cả các nút Người và mối quan hệ Bạn bè trực tiếp của các nút Người đó với các nút Người khác. Sau đó, hãy nhấp vào RUN (CHẠY) để xem kết quả.

Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths

biểu đồ spanner

👉 Trong cùng một trình chỉnh sửa truy vấn, hãy thay thế DDL trước đó để tìm những người đã tham dự cùng một sự kiện, điều này ngụ ý mối liên kết gián tiếp thông qua một hoạt động chung.

Graph SocialGraph
MATCH result_paths =  (p1:Person)-[:Attended]->(e:Event)<-[:Attended]-(p2:Person)
WHERE p1.person_id < p2.person_id
RETURN SAFE_TO_JSON(result_paths) AS result_paths

biểu đồ spanner

👉 Truy vấn này khám phá một loại mối liên kết khác, trong đó những người được đề cập trong bài đăng do bạn bè của một người cụ thể viết. Hãy chạy truy vấn sau trong trình chỉnh sửa truy vấn.

Graph SocialGraph
MATCH result_paths =  (user:Person {name: "Alice"})-[:Friendship]-(friend:Person)-[:Wrote]->(post:Post)-[:Mentioned]->(mentioned_person:Person)
WHERE user <> mentioned_person AND friend <> mentioned_person -- Avoid self-mentions or friend mentioning themselves in their own post if not intended
RETURN SAFE_TO_JSON(result_paths) AS result_paths

biểu đồ spanner

Những truy vấn này chỉ cho thấy một phần sức mạnh của việc sử dụng Spanner làm cơ sở dữ liệu đồ thị cho ứng dụng InstaVibe của chúng ta. Bằng cách lập mô hình dữ liệu xã hội dưới dạng một biểu đồ liên kết, chúng tôi có thể phân tích phức tạp các mối quan hệ và hoạt động. Đây sẽ là nền tảng để các tác nhân AI của chúng tôi hiểu được bối cảnh của người dùng, khám phá các mối quan tâm và cuối cùng là hỗ trợ lập kế hoạch xã hội thông minh.

Giờ đây, khi đã có cấu trúc dữ liệu cơ bản và đã kiểm thử, hãy chuyển sự chú ý của chúng ta sang ứng dụng InstaVibe hiện có mà các nhân viên hỗ trợ của chúng ta sẽ tương tác.

5. Trạng thái hiện tại của InstaVibe

Để hiểu được vị trí phù hợp của các tác nhân AI, trước tiên, chúng ta cần triển khai và chạy ứng dụng web InstaVibe hiện có. Ứng dụng này cung cấp giao diện người dùng và các chức năng cơ bản kết nối với cơ sở dữ liệu biểu đồ Spanner mà chúng ta đã thiết lập.

trang chủ

Ứng dụng InstaVibe sử dụng Google Maps để hiển thị vị trí sự kiện một cách trực quan trên trang chi tiết sự kiện. Để bật chức năng này, ứng dụng cần có khoá API mà chúng ta đã tạo trước đó. Tập lệnh sau đây sẽ truy xuất chuỗi khoá thực tế bằng cách sử dụng tên hiển thị mà chúng ta đã chỉ định ("Khoá API của Nền tảng Maps").

trang sự kiện

👉💻 Quay lại IDE Cloud shell. Chạy tập lệnh bên dưới. Sau đó, hãy kiểm tra kỹ kết quả để đảm bảo GOOGLE_MAPS_API_KEY hiển thị khớp với khoá bạn đã tạo và sao chép từ Google Cloud Console trước đó.

. ~/instavibe-bootstrap/set_env.sh
export KEY_DISPLAY_NAME="Maps Platform API Key"

GOOGLE_MAPS_KEY_ID=$(gcloud services api-keys list \
 
--project="${PROJECT_ID}" \
 
--filter="displayName='${KEY_DISPLAY_NAME}'" \
 
--format="value(uid)" \
 
--limit=1)

GOOGLE_MAPS_API_KEY=$(gcloud services api-keys get-key-string "${GOOGLE_MAPS_KEY_ID}" \
   
--project="${PROJECT_ID}" \
   
--format="value(keyString)")

echo "${GOOGLE_MAPS_API_KEY}" > ~/mapkey.txt

echo "Retrieved GOOGLE_MAPS_API_KEY: ${GOOGLE_MAPS_API_KEY}"

kết quả chính

👉💻 Bây giờ, hãy tạo hình ảnh vùng chứa cho ứng dụng web InstaVibe và đẩy hình ảnh đó vào kho lưu trữ Cấu phần phần mềm.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud builds submit . \
 
--tag=${IMAGE_PATH} \
 
--project=${PROJECT_ID}

👉💻 Triển khai hình ảnh ứng dụng web InstaVibe của bản dựng mới lên Cloud Run

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/
export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

gcloud run deploy ${SERVICE_NAME} \
 
--image=${IMAGE_PATH} \
 
--platform=managed \
 
--region=${REGION} \
 
--allow-unauthenticated \
 
--set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
 
--set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
 
--set-env-vars="APP_HOST=0.0.0.0" \
 
--set-env-vars="APP_PORT=8080" \
 
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
 
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
 
--set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
 
--project=${PROJECT_ID} \
 
--min-instances=1

Sau khi triển khai thành công, nhật ký Cloud Run sẽ hiển thị URL công khai cho ứng dụng InstaVibe đang chạy.

URL

Bạn cũng có thể tìm thấy URL này bằng cách chuyển đến phần Cloud Run (Chạy trên đám mây) trong Google Cloud Console rồi chọn dịch vụ instavibe. Danh sáchURL

Hãy mở URL đó trong trình duyệt web ngay để khám phá nền tảng InstaVibe cơ bản. Xem các bài đăng, sự kiện và mối kết nối của người dùng dựa trên cơ sở dữ liệu biểu đồ mà chúng ta đã thiết lập.

Bây giờ, khi ứng dụng mục tiêu của chúng ta đang chạy, hãy bắt đầu xây dựng tác nhân thông minh đầu tiên để nâng cao khả năng của ứng dụng.

6. Tác nhân cơ bản,Trình lập kế hoạch sự kiện bằng ADK

Khung ADK

Giới thiệu về Khung ADK của Google Giờ đây, khi đã thiết lập nền tảng (ứng dụng và cơ sở dữ liệu InstaVibe), chúng ta có thể bắt đầu xây dựng tác nhân thông minh đầu tiên bằng Bộ phát triển tác nhân (ADK) của Google.

Bộ phát triển tác nhân (ADK) là một khung linh hoạt và mô-đun được thiết kế riêng để phát triển và triển khai tác nhân AI. Nguyên tắc thiết kế của công cụ này là giúp việc phát triển tác nhân giống như phát triển phần mềm truyền thống, nhằm giúp nhà phát triển dễ dàng tạo, triển khai và điều phối các cấu trúc tác nhân có thể xử lý mọi thứ, từ các tác vụ đơn giản, có mục đích duy nhất đến quy trình làm việc phức tạp, nhiều tác nhân.

Về cơ bản, ADK xoay quanh khái niệm Agent, bao gồm các hướng dẫn, cấu hình (chẳng hạn như mô hình ngôn ngữ đã chọn, ví dụ: Gemini) và một tập hợp Tools mà ứng dụng có thể sử dụng để thực hiện các hành động hoặc thu thập thông tin.

06-agent.png

Nhân viên hỗ trợ ban đầu của chúng tôi sẽ là "Người lập kế hoạch sự kiện". Mục đích chính của ứng dụng này là tiếp nhận yêu cầu của người dùng về các hoạt động xã hội (chỉ định vị trí, ngày và mối quan tâm) và tạo ra các đề xuất sáng tạo, phù hợp. Để đảm bảo các đề xuất phù hợp và dựa trên thông tin hiện tại (chẳng hạn như các sự kiện cụ thể diễn ra vào cuối tuần đó), chúng ta sẽ tận dụng một trong các công cụ tích hợp sẵn của ADK: Google Tìm kiếm. Điều này cho phép trợ lý dựa vào kết quả trên web theo thời gian thực để tìm nạp thông tin chi tiết mới nhất về địa điểm, sự kiện và hoạt động phù hợp với tiêu chí của người dùng.

👉📝 Quay lại IDE Cloud shell, trong ~/instavibe-bootstrap/agents/planner/agent.py, hãy thêm lời nhắc và hướng dẫn sau để tạo tác nhân

from google.adk.agents import Agent
from google.adk.tools import google_search

root_agent = Agent(
   
name="planner_agent",
   
model="gemini-2.0-flash",
   
description="Agent tasked with generating creative and fun dating plan suggestions",
   
instruction="""

        You are a specialized AI assistant tasked with generating creative and fun plan suggestions.

        Request:
        For the upcoming weekend, specifically from **[START_DATE_YYYY-MM-DD]** to **[END_DATE_YYYY-MM-DD]**, in the location specified as **[TARGET_LOCATION_NAME_OR_CITY_STATE]** (if latitude/longitude are provided, use these: Lat: **[TARGET_LATITUDE]**, Lon: **[TARGET_LONGITUDE]**), please generate a distinct dating plan suggestions.

        Constraints and Guidelines for Suggestions:
        1.  Creativity & Fun: Plans should be engaging, memorable, and offer a good experience for a date.
        2.  Budget: All generated plans should aim for a moderate budget (conceptually "$$"), meaning they should be affordable yet offer good value, without being overly cheap or extravagant. This budget level should be *reflected in the choice of activities and venues*, but **do not** explicitly state "Budget: $$" in the `plan_description`.
        3.  Interest Alignment:
               Consider the following user interests: **[COMMA_SEPARATED_LIST_OF_INTERESTS, e.g., outdoors, arts & culture, foodie, nightlife, unique local events, live music, active/sports]**. Tailor suggestions specifically to these where possible. The plan should *embody* these interests.
               Fallback: If specific events or venues perfectly matching all listed user interests cannot be found for the specified weekend, you should create a creative and fun generic dating plan that is still appealing, suitable for the location, and adheres to the moderate budget. This plan should still sound exciting and fun, even if it's more general.
        4.  Current & Specific: Prioritize finding specific, current events, festivals, pop-ups, or unique local venues operating or happening during the specified weekend dates. If exact current events cannot be found, suggest appealing evergreen options or implement the fallback generic plan.
        5.  Location Details: For each place or event mentioned within a plan, you MUST provide its name, precise latitude, precise longitude, and a brief, helpful description.
        6.  Maximum Activities: The plan must contain a maximum of 3 distinct activities.

        RETURN PLAN in MARKDOWN FORMAT
    """,
   
tools=[google_search]
)

Và đó là tác nhân đầu tiên của chúng ta! Một trong những điểm mạnh của ADK là tính trực quan và các công cụ tiện lợi mà ADK cung cấp. Một trong những công cụ đặc biệt hữu ích là Giao diện người dùng dành cho nhà phát triển ADK. Công cụ này cho phép bạn kiểm thử tác nhân của mình theo cách tương tác và xem phản hồi của tác nhân theo thời gian thực.

👉💻 Hãy bắt đầu. Các lệnh sau sẽ khởi chạy giao diện người dùng ADK DEV:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/planner/.env
adk web

Sau khi chạy các lệnh, bạn sẽ thấy kết quả trong thiết bị đầu cuối cho biết Máy chủ web ADK đã khởi động, tương tự như sau:

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://localhost:8000.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

👉 Tiếp theo, để truy cập vào giao diện người dùng dành cho nhà phát triển ADK từ trình duyệt, hãy làm như sau:

Trong biểu tượng Xem trước trên web (thường trông giống như một con mắt hoặc hình vuông có mũi tên) trên thanh công cụ Cloud Shell (thường ở trên cùng bên phải), hãy chọn Thay đổi cổng. Trong cửa sổ bật lên, hãy đặt cổng thành 8000 rồi nhấp vào "Thay đổi và xem trước". Sau đó, Cloud Shell sẽ mở một thẻ hoặc cửa sổ trình duyệt mới hiển thị Giao diện người dùng dành cho nhà phát triển ADK.

bản xem trước trên web

Sau khi giao diện người dùng dành cho nhà phát triển ADK mở ra trong trình duyệt: Trong trình đơn thả xuống ở trên cùng bên phải của giao diện người dùng, hãy chọn planner làm tác nhân mà bạn muốn tương tác. Bây giờ, trong hộp thoại trò chuyện ở bên phải, hãy thử giao cho trợ lý một việc cần làm. Ví dụ: trò chuyện với nhân viên hỗ trợ:

Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime

Đề xuất ngày (Lựa chọn ưu tiên của bạn)

July 12 2025

Bạn sẽ thấy nhân viên hỗ trợ xử lý yêu cầu của bạn và đưa ra một kế hoạch dựa trên kết quả tìm kiếm trên Google.

giao diện người dùng phát triển adk

Giờ đây, việc tương tác với một tác nhân là một chuyện, nhưng làm cách nào để biết tác nhân đó có hoạt động nhất quán như mong đợi hay không, đặc biệt là khi chúng ta thực hiện các thay đổi?

Các phương pháp kiểm thử phần mềm truyền thống thường không phù hợp với các tác nhân AI do bản chất tạo sinh và không xác định của chúng. Để chuyển từ một bản minh hoạ thú vị sang một đại lý sản xuất đáng tin cậy, bạn cần có một chiến lược đánh giá vững chắc. Không giống như việc chỉ kiểm tra kết quả cuối cùng của một mô hình tạo sinh, việc đánh giá một tác nhân thường liên quan đến việc đánh giá quy trình ra quyết định và khả năng sử dụng công cụ hoặc làm theo hướng dẫn một cách chính xác trong nhiều tình huống. ADK cung cấp các tính năng để giúp bạn giải quyết vấn đề này.

Eval

👉 Trong giao diện người dùng dành cho nhà phát triển ADK, hãy nhấp vào thẻ "Eval" (Đánh giá) trong bảng điều hướng bên trái. Bạn sẽ thấy một tệp kiểm thử được tải trước có tên plan_eval. Tệp này chứa các tiêu chí và dữ liệu đầu vào được xác định trước để kiểm thử tác nhân lập kế hoạch của chúng ta.

👉 Chọn một tình huống, chẳng hạn như "boston", rồi nhấp vào nút Run Evaluation (Chạy quy trình đánh giá). Trong cửa sổ bật lên, hãy giảm điểm trùng khớp xuống 0,3 rồi nhấp vào Bắt đầu.

Điểm đối sánh

Thao tác này sẽ thực thi tác nhân bằng dữ liệu đầu vào kiểm thử và kiểm tra xem kết quả của tác nhân có đáp ứng các kỳ vọng đã xác định hay không. Điều này giúp bạn có cách thức để kiểm thử hiệu suất của tác nhân một cách có hệ thống.

đánh giá giao diện người dùng của nhà phát triển adk

👉 Bây giờ, hãy xem điều gì xảy ra khi đặt ngưỡng nghiêm ngặt hơn. Chọn tình huống "nyc" rồi nhấp lại vào Run Evaluation (Chạy quy trình đánh giá). Lần này, hãy để điểm khớp ở giá trị mặc định (Điểm khớp phản hồi: 0,7) rồi nhấp vào Bắt đầu. Bạn sẽ thấy kết quả là Không đạt. Điều này là dự kiến vì kết quả mẫu quảng cáo của nhân viên hỗ trợ không khớp hoàn toàn với câu trả lời "vàng" được xác định trước.

Không đánh giá được giao diện người dùng của nhà phát triển adk

👉 Để tìm hiểu lý do không thành công, hãy nhấp vào biểu tượng không thành công trong hàng "nyc". Giao diện người dùng hiện hiển thị nội dung so sánh song song giữa Phản hồi thực tế của tác nhân và Phản hồi dự kiến của trường hợp kiểm thử. Chế độ xem này rất cần thiết cho việc gỡ lỗi, cho phép bạn xem chính xác vị trí đầu ra của tác nhân bị phân kỳ và tinh chỉnh hướng dẫn cho phù hợp.

Sau khi bạn khám phá xong giao diện người dùng và quá trình đánh giá, hãy quay lại thiết bị đầu cuối Cloud Shell Editor (Trình chỉnh sửa Cloud Shell) và nhấn Ctrl+C để dừng giao diện người dùng ADK Dev.

Mặc dù đầu ra văn bản dạng tự do là một khởi đầu tốt, nhưng để các ứng dụng như InstaVibe dễ dàng sử dụng các đề xuất của một tác nhân, dữ liệu có cấu trúc (như JSON) sẽ thực tế hơn nhiều. Hãy sửa đổi tác nhân của chúng ta để trả về kế hoạch ở định dạng JSON nhất quán.

👉📝 Trong ~/instavibe-bootstrap/agents/planner/agent.py, hãy tìm dòng hiện có nội dung RETURN PLAN in MARKDOWN FORMAT trong chuỗi hướng dẫn của tác nhân. Thay thế dòng đó bằng cấu trúc JSON chi tiết sau:

Return your response *exclusively* as a single JSON object. This object should contain a top-level key, "fun_plans", which holds a plan objects. Each plan object in the list must strictly adhere to the following structure:

       
--json--
       
{
         
"plan_description": "A summary of the overall plan, consisting of **exactly three sentences**. Craft these sentences in a friendly, enthusiastic, and conversational tone, as if you're suggesting this awesome idea to a close friend. Make it sound exciting and personal, highlighting the positive aspects and appeal of the plan without explicitly mentioning budget or listing interest categories.",
         
"locations_and_activities": [
             
{
             
"name": "Name of the specific place or event",
             
"latitude": 0.000000,  // Replace with actual latitude
             
"longitude": 0.000000, // Replace with actual longitude
             
"description": "A brief description of this place/event, why it's suitable for the date, and any specific details for the weekend (e.g., opening hours, event time)."
             
}
             
// Add more location/activity objects here if the plan involves multiple stops/parts
         
]
       
}

Giờ đây, bạn đã cập nhật hướng dẫn của tác nhân để yêu cầu cụ thể đầu ra JSON, hãy xác minh thay đổi này.

👉💻 Chạy lại giao diện người dùng dành cho nhà phát triển ADK bằng cách sử dụng cùng một lệnh như trước:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
adk web

Làm mới thẻ nếu bạn đã mở thẻ đó. Hoặc làm theo các bước tương tự như trước để mở giao diện người dùng dành cho nhà phát triển ADK trong trình duyệt (thông qua Bản xem trước web của Cloud Shell trên cổng 8000). Sau khi giao diện người dùng được tải, hãy đảm bảo bạn đã chọn tác nhân lập kế hoạch.

👉 Lần này, hãy đưa ra một yêu cầu khác. Trong hộp thoại trò chuyện, hãy nhập:

Plan an event Boston this weekend with art and coffee

Hãy kiểm tra kỹ phản hồi của nhân viên hỗ trợ. Thay vì một tin nhắn trả lời thuần tuý bằng văn bản, giờ đây, bạn sẽ thấy một phản hồi được định dạng nghiêm ngặt dưới dạng đối tượng JSON, khớp với cấu trúc mà chúng ta đã xác định trong hướng dẫn (chứa fun_plans, plan_description, locations_and_activities, v.v.). Điều này xác nhận rằng tác nhân hiện có thể tạo ra đầu ra có cấu trúc phù hợp với việc sử dụng theo phương thức lập trình của ứng dụng InstaVibe.

adk dev ui json

Sau khi xác nhận kết quả JSON, hãy quay lại dòng lệnh Cloud Shell và nhấn Ctrl+C để dừng giao diện người dùng ADK Dev.

Thành phần ADK

Mặc dù giao diện người dùng ADK Dev rất phù hợp cho hoạt động kiểm thử tương tác, nhưng chúng ta thường cần chạy các tác nhân theo phương thức lập trình, có thể là một phần của ứng dụng hoặc dịch vụ phụ trợ lớn hơn. Để hiểu cách hoạt động của tính năng này, hãy xem một số khái niệm cốt lõi của ADK liên quan đến thời gian chạy và quản lý ngữ cảnh.

Để có được các cuộc trò chuyện có ý nghĩa và nhiều lượt, các nhân viên hỗ trợ cần phải hiểu bối cảnh – gợi nhắc những gì đã nói và làm để duy trì tính liên tục. ADK cung cấp các cách có cấu trúc để quản lý ngữ cảnh này thông qua Phiên, Trạng tháiBộ nhớ:

  • Phiên: Khi người dùng bắt đầu tương tác với một nhân viên hỗ trợ, một Phiên sẽ được tạo. Hãy coi đó là vùng chứa cho một chuỗi tin nhắn trò chuyện cụ thể. Nó chứa một mã nhận dạng duy nhất, nhật ký tương tác (Sự kiện), dữ liệu đang hoạt động (Trạng thái) và siêu dữ liệu như thời gian cập nhật gần đây nhất.
  • Trạng thái: Đây là bộ nhớ ngắn hạn, bộ nhớ hoạt động của tác nhân trong một Phiên duy nhất. Đây là một từ điển có thể thay đổi, trong đó tác nhân có thể lưu trữ thông tin tạm thời cần thiết để hoàn thành tác vụ hiện tại (ví dụ: lựa chọn ưu tiên của người dùng đã thu thập được cho đến nay, kết quả trung gian từ các lệnh gọi công cụ).
  • Bộ nhớ: Chỉ số này thể hiện khả năng của trợ lý trong việc gợi nhắc dài hạn trên nhiều phiên hoặc truy cập vào các cơ sở tri thức bên ngoài. Mặc dù Phiên và Trạng thái xử lý cuộc trò chuyện tức thì, nhưng Bộ nhớ (thường do MemoryService quản lý) cho phép một tác nhân truy xuất thông tin từ các lượt tương tác trước đây hoặc các nguồn dữ liệu có cấu trúc, giúp tác nhân có được bối cảnh kiến thức rộng hơn. (Lưu ý: Ứng dụng đơn giản của chúng tôi sử dụng các dịch vụ trong bộ nhớ để đơn giản hoá, nghĩa là bộ nhớ/trạng thái chỉ tồn tại trong khi tập lệnh chạy).
  • Sự kiện: Mọi lượt tương tác trong một Phiên (thông báo của người dùng, phản hồi của trợ lý, yêu cầu sử dụng công cụ, kết quả của công cụ, thay đổi trạng thái, lỗi) đều được ghi lại dưới dạng một Sự kiện không thể thay đổi. Thao tác này sẽ tạo một nhật ký theo trình tự thời gian, về cơ bản là bản chép lời và nhật ký hành động của cuộc trò chuyện.

Vậy các tệp này được quản lý như thế nào khi một tác nhân chạy? Đó là nhiệm vụ của Runner (Trình chạy).

  • Runner (Trình chạy): Runner là công cụ thực thi cốt lõi do ADK cung cấp. Bạn xác định tác nhân và các công cụ mà tác nhân sử dụng, còn Runner sẽ điều phối quy trình thực hiện yêu cầu của người dùng. Lớp này quản lý Phiên, xử lý luồng Sự kiện, cập nhật Trạng thái, gọi mô hình ngôn ngữ cơ bản, điều phối các lệnh gọi công cụ và có thể tương tác với MemoryService. Hãy coi đó là người chỉ huy đảm bảo tất cả các phần hoạt động cùng nhau một cách chính xác.

Chúng ta có thể sử dụng Runner để chạy tác nhân dưới dạng một ứng dụng Python độc lập, hoàn toàn độc lập với Giao diện người dùng dành cho nhà phát triển.

Hãy tạo một tập lệnh ứng dụng đơn giản để gọi tác nhân lập kế hoạch theo phương thức lập trình.

👉📝 Trong tệp ~/instavibe-bootstrap/agents/planner/planner_client.py, hãy thêm mã Python sau đây vào phần nhập hiện có. Trong planner_client.py, trong phần nhập, hãy thêm nội dung sau:

async def async_main():
 
session_service = InMemorySessionService()

 
session = await session_service.create_session(
     
state={}, app_name='planner_app', user_id='user_dc'
 
)

 
query = "Plan Something for me in San Francisco this weekend on wine and fashion "
 
print(f"User Query: '{query}'")
 
content = types.Content(role='user', parts=[types.Part(text=query)])

 
root_agent = agent.root_agent
 
runner = Runner(
       
app_name='planner_app',
       
agent=root_agent,
       
session_service=session_service,
 
)
 
print("Running agent...")
 
events_async =  runner.run_async(
   
session_id=session.id, user_id=session.user_id, new_message=content
 
)

 
async for event in events_async:
   
print(f"Event received: {event}")


if __name__ == '__main__':
 
try:
   
asyncio.run(async_main())
 
except Exception as e:
   
print(f"An error occurred: {e}")

Mã này thiết lập các dịch vụ trong bộ nhớ để quản lý phiên và cấu phần phần mềm (giữ cho mã đơn giản trong ví dụ này), tạo một phiên, xác định truy vấn của người dùng, định cấu hình Runner bằng tác nhân của chúng ta, sau đó chạy tác nhân không đồng bộ, in ra từng sự kiện được tạo trong quá trình thực thi.

👉💻 Bây giờ, hãy thực thi tập lệnh máy khách này từ thiết bị đầu cuối:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
python -m planner.planner_client

👀 Quan sát kết quả. Thay vì chỉ có kế hoạch JSON cuối cùng, bạn sẽ thấy cấu trúc chi tiết của từng đối tượng Sự kiện được tạo trong luồng thực thi của tác nhân. Luồng này bao gồm sự kiện tin nhắn ban đầu của người dùng, các sự kiện tiềm năng liên quan đến lệnh gọi công cụ (như Google Tìm kiếm) và cuối cùng là sự kiện phản hồi của mô hình chứa kế hoạch JSON. Luồng sự kiện chi tiết này rất hữu ích cho việc gỡ lỗi và tìm hiểu quy trình xử lý từng bước diễn ra trong Thời gian chạy ADK.

Running agent...
Event received: content=Content(parts=[Part(video_metadata=None, thought=None, code_execution_result=None, executable_code=None, file_data=None, function_call=None, function_response=None, inline_data=None, text='```json\n{\n "fun_plans": [\n  {\n   "plan_description": "Embark on a stylish adventure through Hayes Valley,
...(turncated)
, offering a variety of fashion styles to browse and enjoy."\n    }\n   ]\n  }\n ]\n}\n```')], role='model') grounding_metadata=GroundingMetadata(grounding_chunks=[GroundingChunk(retrieved_context=None, web=GroundingChunkWeb(domain='islands.com', title='islands.com', uri='http
...(turncated)
QyTpPV7jS6wUt-Ix7GuP2mC9J4eY_8Km6Vv44liF9cb2VSs='))], grounding_supports=[GroundingSupport(confide
...(turncated)
>\n', sdk_blob=None), web_search_queries=['..e']) partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None invocation_id='e-04d97b8b-9021-47a5-ab41-17b5cbb4bf03' author='location_search_agent' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='CInHdkKw' timestamp=1746978846.232674

Nếu tập lệnh chạy liên tục hoặc bị treo, bạn có thể cần phải dừng tập lệnh theo cách thủ công bằng cách nhấn Ctrl+C.

7. Tác nhân tương tác với nền tảng – tương tác với Máy chủ MCP

Mặc dù ADK giúp cấu trúc các tác nhân của chúng tôi, nhưng các tác nhân này thường cần tương tác với các hệ thống hoặc API bên ngoài để thực hiện các hành động trong thế giới thực.

Giao thức ngữ cảnh mô hình (MCP)

Giao thức ngữ cảnh mô hình (MCP) là một tiêu chuẩn mở được thiết kế để chuẩn hoá cách các ứng dụng AI như tác nhân kết nối với các nguồn dữ liệu, công cụ và hệ thống bên ngoài. Mục đích của công cụ này là giải quyết vấn đề cần tích hợp tuỳ chỉnh cho mọi ứng dụng AI và tổ hợp nguồn dữ liệu bằng cách cung cấp một giao diện chung. MCP sử dụng cấu trúc máy khách-máy chủ, trong đó máy khách MCP nằm trong các ứng dụng AI (máy chủ lưu trữ) quản lý các kết nối với máy chủ MCP. Các máy chủ này là các chương trình bên ngoài hiển thị các chức năng cụ thể như truy cập vào dữ liệu cục bộ, tương tác với các dịch vụ từ xa thông qua API hoặc cung cấp lời nhắc được xác định trước, cho phép các mô hình AI truy cập vào thông tin hiện tại và thực hiện các nhiệm vụ ngoài quá trình huấn luyện ban đầu. Cấu trúc này cho phép các mô hình AI khám phá và tương tác với các chức năng bên ngoài theo cách chuẩn hoá, giúp việc tích hợp trở nên đơn giản và dễ mở rộng hơn.

Tạo và triển khai máy chủ MCP InstaVibe

07-mcp-server.png

Cuối cùng, các nhân viên hỗ trợ của chúng tôi sẽ cần tương tác với chính nền tảng InstaVibe.Cụ thể là để tạo bài đăng và đăng ký sự kiện bằng các API hiện có của nền tảng. Ứng dụng InstaVibe đã hiển thị các chức năng này thông qua các điểm cuối HTTP tiêu chuẩn:

Enpoint

URL

Phương thức HTTP

Mô tả

Tạo bài đăng

api/posts

ĐĂNG

Điểm cuối API để thêm bài đăng mới. Dự kiến nội dung JSON:
{"author_name": "...", "text": "...", "sentiment": "..." (optional)}

Tạo sự kiện

api/events

ĐĂNG

Điểm cuối API để thêm một sự kiện mới và những người tham dự sự kiện đó (giản đồ được đơn giản hoá).
Dự kiến nội dung JSON: { "event_name": "...", "description": "...", "event_date": "YYYY-MM-DDTHH:MM:SSZ", "locations": [ {"name": "...", "description": "...", "latitude": 0.0, "longitude": 0.0, "address": "..."} ], "attendee_names": ["...", "..."] }

Để cung cấp các chức năng này cho các tác nhân thông qua MCP, trước tiên, chúng ta cần tạo các hàm Python đơn giản đóng vai trò là trình bao bọc xung quanh các lệnh gọi API này. Các hàm này sẽ xử lý logic yêu cầu HTTP.

👉 Trước tiên, hãy triển khai hàm trình bao bọc để tạo bài đăng. Mở tệp ~/instavibe-bootstrap/tools/instavibe/instavibe.py và thay thế chú thích #REPLACE ME CREATE POST bằng mã Python sau:

def create_post(author_name: str, text: str, sentiment: str, base_url: str = BASE_URL):
    """
    Sends a POST request to the /posts endpoint to create a new post.

    Args:
        author_name (str): The name of the post's author.
        text (str): The content of the post.
        sentiment (str): The sentiment associated with the post (e.g., 'positive', 'negative', 'neutral').
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
   
url = f"{base_url}/posts"
   
headers = {"Content-Type": "application/json"}
   
payload = {
       
"author_name": author_name,
       
"text": text,
       
"sentiment": sentiment
   
}

   
try:
       
response = requests.post(url, headers=headers, json=payload)
       
response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
       
print(f"Successfully created post. Status Code: {response.status_code}")
       
return response.json()
   
except requests.exceptions.RequestException as e:
       
print(f"Error creating post: {e}")
       
# Optionally re-raise the exception if the caller needs to handle it
       
# raise e
       
return None
   
except json.JSONDecodeError:
       
print(f"Error decoding JSON response from {url}. Response text: {response.text}")
       
return None

👉📝 Tiếp theo, chúng ta sẽ tạo hàm trình bao bọc cho API tạo sự kiện. Trong cùng tệp ~/instavibe-bootstrap/tools/instavibe/instavibe.py, hãy thay thế chú thích #REPLACE ME CREATE EVENTS bằng mã sau:

def create_event(event_name: str, description: str, event_date: str, locations: list, attendee_names: list[str], base_url: str = BASE_URL):
    """
    Sends a POST request to the /events endpoint to create a new event registration.

    Args:
        event_name (str): The name of the event.
        description (str): The detailed description of the event.
        event_date (str): The date and time of the event (ISO 8601 format recommended, e.g., "2025-06-10T09:00:00Z").
        locations (list): A list of location dictionaries. Each dictionary should contain:
                          'name' (str), 'description' (str, optional),
                          'latitude' (float), 'longitude' (float),
                          'address' (str, optional).
        attendee_names (list[str]): A list of names of the people attending the event.
        base_url (str, optional): The base URL of the API. Defaults to BASE_URL.

    Returns:
        dict: The JSON response from the API if the request is successful.
              Returns None if an error occurs.

    Raises:
        requests.exceptions.RequestException: If there's an issue with the network request (e.g., connection error, timeout).
    """
   
url = f"{base_url}/events"
   
headers = {"Content-Type": "application/json"}
   
payload = {
       
"event_name": event_name,
       
"description": description,
       
"event_date": event_date,
       
"locations": locations,
       
"attendee_names": attendee_names,
   
}

   
try:
       
response = requests.post(url, headers=headers, json=payload)
       
response.raise_for_status()  # Raise an exception for bad status codes (4xx or 5xx)
       
print(f"Successfully created event registration. Status Code: {response.status_code}")
       
return response.json()
   
except requests.exceptions.RequestException as e:
       
print(f"Error creating event registration: {e}")
       
# Optionally re-raise the exception if the caller needs to handle it
       
# raise e
       
return None
   
except json.JSONDecodeError:
       
print(f"Error decoding JSON response from {url}. Response text: {response.text}")
       
return None

Như bạn có thể thấy, các hàm này là các trình bao bọc đơn giản xung quanh các API InstaVibe hiện có. Mẫu này rất hữu ích, nếu đã có API cho các dịch vụ của mình, bạn có thể dễ dàng hiển thị chức năng của các dịch vụ đó dưới dạng công cụ cho các tác nhân bằng cách tạo các trình bao bọc như vậy.

Triển khai máy chủ MCP

Giờ đây, chúng ta đã có các hàm Python thực hiện các hành động (gọi API InstaVibe), chúng ta cần tạo thành phần Máy chủ MCP. Máy chủ này sẽ hiển thị các hàm này dưới dạng "công cụ" theo tiêu chuẩn MCP, cho phép ứng dụng MCP (chẳng hạn như các tác nhân của chúng tôi) khám phá và gọi các hàm đó.

Máy chủ MCP thường triển khai hai chức năng chính:

  • list_tools: chịu trách nhiệm cho phép ứng dụng khám phá các công cụ có sẵn trên máy chủ, cung cấp siêu dữ liệu như tên, nội dung mô tả và các tham số bắt buộc, thường được xác định bằng JSON Schema
  • call_tool: xử lý việc thực thi một công cụ cụ thể do ứng dụng yêu cầu, nhận tên và đối số của công cụ đó, đồng thời thực hiện hành động tương ứng, chẳng hạn như trong trường hợp của chúng ta là tương tác với một API

Máy chủ MCP được dùng để cung cấp cho các mô hình AI quyền truy cập vào dữ liệu và hành động trong thế giới thực, cho phép thực hiện các tác vụ như gửi email, tạo tác vụ trong hệ thống quản lý dự án, tìm kiếm cơ sở dữ liệu hoặc tương tác với nhiều phần mềm và dịch vụ web. Mặc dù các hoạt động triển khai ban đầu thường tập trung vào các máy chủ cục bộ giao tiếp thông qua đầu vào/đầu ra (stdio) tiêu chuẩn để đơn giản hoá, đặc biệt là trong môi trường phát triển hoặc "studio", nhưng việc chuyển sang máy chủ từ xa sử dụng các giao thức như HTTP với Sự kiện do máy chủ gửi (SSE) sẽ phù hợp hơn với việc sử dụng rộng rãi và các trường hợp sử dụng của doanh nghiệp.

Mặc dù có thêm lớp giao tiếp mạng, nhưng cấu trúc từ xa mang lại những lợi thế đáng kể: cho phép nhiều ứng dụng AI chia sẻ quyền truy cập vào một máy chủ duy nhất, tập trung quản lý và cập nhật các công cụ, tăng cường bảo mật bằng cách lưu giữ dữ liệu nhạy cảm và khoá API ở phía máy chủ thay vì phân phối trên nhiều máy khách, đồng thời tách biệt mô hình AI khỏi thông tin cụ thể về việc tích hợp hệ thống bên ngoài, giúp toàn bộ hệ sinh thái dễ mở rộng quy mô, an toàn và dễ bảo trì hơn so với việc yêu cầu mỗi thực thể AI quản lý các hoạt động tích hợp trực tiếp của riêng mình.

07-mcp-server.png

Chúng ta sẽ triển khai máy chủ MCP bằng HTTP và Sự kiện do máy chủ gửi (SSE) để giao tiếp. Đây là phương thức phù hợp với các trường hợp sử dụng doanh nghiệp và các quá trình thực thi công cụ có thể chạy trong thời gian dài.

👉📝 Trước tiên, hãy triển khai điểm cuối list_tools. Mở tệp ~/instavibe-bootstrap/tools/instavibe/mcp_server.py rồi thay thế chú thích #REPLACE ME - LIST TOOLS bằng mã sau. :

@app.list_tools()
async def list_tools() -> list[mcp_types.Tool]:
  """MCP handler to list available tools."""
 
# Convert the ADK tool's definition to MCP format
 
mcp_tool_schema_event = adk_to_mcp_tool_type(event_tool)
 
mcp_tool_schema_post = adk_to_mcp_tool_type(post_tool)
 
print(f"MCP Server: Received list_tools request. \n MCP Server: Advertising tool: {mcp_tool_schema_event.name} and {mcp_tool_schema_post}")
 
return [mcp_tool_schema_event,mcp_tool_schema_post]

Hàm này xác định các công cụ (create_event, create_post) và cho ứng dụng kết nối biết về các công cụ đó.

👉📝 Tiếp theo, hãy triển khai điểm cuối call_tool để xử lý các yêu cầu thực thi thực tế từ ứng dụng. Trong cùng tệp ~/instavibe-bootstrap/tools/instavibe/mcp_server.py, hãy thay thế chú thích #REPLACE ME - CALL TOOLS bằng mã sau.

@app.call_tool()
async def call_tool(
   
name: str, arguments: dict
) -> list[mcp_types.TextContent | mcp_types.ImageContent | mcp_types.EmbeddedResource]:
  """MCP handler to execute a tool call."""
 
print(f"MCP Server: Received call_tool request for '{name}' with args: {arguments}")

 
# Look up the tool by name in our dictionary
 
tool_to_call = available_tools.get(name)
 
if tool_to_call:
   
try:
     
adk_response = await tool_to_call.run_async(
         
args=arguments,
         
tool_context=None, # No ADK context available here
     
)
     
print(f"MCP Server: ADK tool '{name}' executed successfully.")
     
     
response_text = json.dumps(adk_response, indent=2)
     
return [mcp_types.TextContent(type="text", text=response_text)]

   
except Exception as e:
     
print(f"MCP Server: Error executing ADK tool '{name}': {e}")
     
# Creating a proper MCP error response might be more robust
     
error_text = json.dumps({"error": f"Failed to execute tool '{name}': {str(e)}"})
     
return [mcp_types.TextContent(type="text", text=error_text)]
 
else:
     
# Handle calls to unknown tools
     
print(f"MCP Server: Tool '{name}' not found.")
     
error_text = json.dumps({"error": f"Tool '{name}' not implemented."})
     
return [mcp_types.TextContent(type="text", text=error_text)]

Hàm này nhận tên công cụ và các đối số, tìm hàm bao bọc Python tương ứng mà chúng ta đã xác định trước đó, thực thi hàm đó và trả về kết quả

👉💻 Sau khi xác định logic máy chủ MCP, chúng ta cần đóng gói logic đó dưới dạng một vùng chứa. Trong dòng lệnh, hãy chạy tập lệnh sau để tạo hình ảnh Docker bằng Cloud Build:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud builds submit . \
 
--tag=${IMAGE_PATH} \
 
--project=${PROJECT_ID}

👉💻 Sau đó, triển khai hình ảnh dưới dạng dịch vụ trên Google Cloud Run.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/tools/instavibe

export IMAGE_TAG="latest"
export MCP_IMAGE_NAME="mcp-tool-server"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${MCP_IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="mcp-tool-server"
export INSTAVIBE_BASE_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe)/api

gcloud run deploy ${SERVICE_NAME} \
 
--image=${IMAGE_PATH} \
 
--platform=managed \
 
--region=${REGION} \
 
--allow-unauthenticated \
 
--set-env-vars="INSTAVIBE_BASE_URL=${INSTAVIBE_BASE_URL}" \
 
--set-env-vars="APP_HOST=0.0.0.0" \
 
--set-env-vars="APP_PORT=8080" \
 
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
 
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
 
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
 
--project=${PROJECT_ID} \
 
--min-instances=1

👉💻 Sau khi triển khai thành công, máy chủ MCP sẽ chạy và có thể truy cập được thông qua URL công khai. Chúng ta cần thu thập URL này để tác nhân (đóng vai trò là ứng dụng MCP) biết nơi kết nối.

export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

Giờ đây, bạn cũng có thể thấy dịch vụ mcp-tool-server được liệt kê là "Đang chạy" trong phần Cloud Run của Google Cloud Console.

Cloud Run

Khi đã triển khai máy chủ MCP và thu thập URL của máy chủ này, chúng ta có thể triển khai tác nhân sẽ đóng vai trò là ứng dụng khách MCP và sử dụng các công cụ do máy chủ này hiển thị.

8. Tác nhân tương tác với nền tảng (sử dụng MCP)

Ứng dụng MCP Ứng dụng MCP là một thành phần nằm trong ứng dụng hoặc tác nhân AI, đóng vai trò là giao diện giữa mô hình AI và một hoặc nhiều Máy chủ MCP; trong quá trình triển khai, ứng dụng này sẽ được tích hợp trực tiếp trong tác nhân của chúng tôi. Chức năng chính của ứng dụng này là giao tiếp với Máy chủ MCP để khám phá các công cụ có sẵn thông qua hàm list_tools, sau đó yêu cầu thực thi các công cụ cụ thể bằng cách sử dụng hàm call_tool, truyền các đối số cần thiết do mô hình AI hoặc tác nhân điều phối lệnh gọi cung cấp.

Ứng dụng MCP

Bây giờ, chúng ta sẽ tạo tác nhân đóng vai trò là Ứng dụng MCP. Tác nhân này, chạy trong khung ADK, sẽ chịu trách nhiệm giao tiếp với mcp-tool-server mà chúng ta vừa triển khai.

👉 Trước tiên, chúng ta cần sửa đổi định nghĩa của tác nhân để tìm nạp linh động các công cụ từ máy chủ MCP đang chạy. Trong agents/platform_mcp_client/agent.py, hãy thay thế #REPLACE ME - FETCH TOOLS bằng nội dung sau:

"""Gets tools from the File System MCP Server."""
 
tools =  MCPToolset(
     
connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
 
)

Mã này sử dụng phương thức MCPToolset.from_server để kết nối với MCP_SERVER_URL (mà chúng ta đã đặt làm biến môi trường trước đó) và truy xuất danh sách các công cụ có sẵn.

Tiếp theo, chúng ta cần cho định nghĩa tác nhân ADK biết để thực sự sử dụng các công cụ được tìm nạp động này.

👉 Trong agents/platform_mcp_client/agent.py, hãy thay thế #REPLACE ME - SET TOOLs bằng nội dung sau:

  tools=[tools],

👉💻 Bây giờ, hãy kiểm thử tác nhân này trên máy bằng cách sử dụng Giao diện người dùng dành cho nhà phát triển ADK để xem tác nhân này có thể kết nối chính xác với máy chủ MCP và sử dụng các công cụ để tương tác với ứng dụng InstaVibe đang chạy hay không.

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
sed -i "s|^\(O\?MCP_SERVER_URL\)=.*|MCP_SERVER_URL=${MCP_SERVER_URL}|" ~/instavibe-bootstrap/agents/platform_mcp_client/.env
adk web

Mở lại Giao diện người dùng dành cho nhà phát triển ADK trong trình duyệt (sử dụng tính năng Xem trước web của Cloud Shell trên cổng 8000). Lần này, trong trình đơn thả xuống ở trên cùng bên phải, hãy chọn tác nhân platform_mcp_client.

Hãy kiểm thử công cụ create_post. Trong hộp thoại trò chuyện, hãy nhập yêu cầu sau:

Create a post saying "Y'all I just got the cutest lil void baby 😭✨ Naming him Abyss bc he's deep, mysterious, and lowkey chaotic 🔥🖤 #VoidCat #NewRoomie" I'm Julia

Bài đăng về giao diện người dùng dành cho nhà phát triển ADK

Tác nhân sẽ xử lý việc này, xác định nhu cầu sử dụng công cụ create_post, giao tiếp với máy chủ MCP, sau đó gọi API InstaVibe.

👉 Bước xác minh: Sau khi nhân viên hỗ trợ xác nhận hành động, hãy mở thẻ nơi ứng dụng InstaVibe đang chạy (hoặc làm mới ứng dụng). Bạn sẽ thấy bài đăng mới của "Julia" xuất hiện trên trang chủ!

Bài đăng InstaVibe

👉💻 Chạy tập lệnh này trong một cửa sổ dòng lệnh riêng biệt để lấy đường liên kết Instavibe nếu cần:

gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe

👉📝 Bây giờ, hãy kiểm thử công cụ create_event. Nhập yêu cầu nhiều dòng sau vào hộp thoại trò chuyện:

Hey, can you set up an event for Hannah and George and me, and I'm Julia? Let's call it 'Mexico City Culinary & Art Day'.
here are more info
 
{"event_name": "Mexico City Culinary & Art Day",
 
"description": "A vibrant day in Mexico City for Hannah and George, starting with lunch at one of the city's best taco spots in the hip Condesa neighborhood, followed by an inspiring afternoon exploring the Museo Soumaya's stunning art collection.",
 
"event_date": "2025-10-17T12:00:00-06:00",
 
"locations": [
   
{
     
"name": "El Tizoncito",
     
"description": "Considered one of the original creators of tacos al pastor, El Tizoncito offers a legendary taco experience in the heart of Condesa. Their flavorful meats, house salsas, and casual vibe make it a must-visit for foodies.",
     
"latitude": 19.412179,
     
"longitude": -99.171308,
     
"address": "Av. Tamaulipas 122, Hipódromo, Cuauhtémoc, 06100 Ciudad de México, CDMX, Mexico"
   
},
   
{
     
"name": "Museo Soumaya",
     
"description": "An architectural icon in Mexico City, Museo Soumaya houses over 66,000 works of art, including pieces by Rodin, Dalí, and Rivera. The striking silver structure is a cultural landmark and a visual feast inside and out.",
     
"latitude": 19.440056,
     
"longitude": -99.204281,
     
"address": "Plaza Carso, Blvd. Miguel de Cervantes Saavedra 303, Granada, Miguel Hidalgo, 11529 Ciudad de México, CDMX, Mexico"
   
}
 
],
 
"attendee_names": ["Hannah", "George", Julia],
}

Xin nhắc lại, tác nhân phải sử dụng công cụ thích hợp thông qua máy chủ MCP. Trong thẻ Sự kiện, hãy nhấp vào từng sự kiện riêng lẻ. Bạn sẽ thấy dấu vết chi tiết, từng bước của quá trình thực thi.

Sự kiện giao diện người dùng dành cho nhà phát triển ADK

👉 Bước xác minh: Quay lại ứng dụng InstaVibe đang chạy và chuyển đến phần "Sự kiện" (hoặc tương đương). Bây giờ, bạn sẽ thấy sự kiện "Mexico City Culinary & Art Day" (Ngày nghệ thuật và ẩm thực Mexico City) mới tạo được liệt kê.

Sự kiện InstaVibe

Điều này minh hoạ thành công cách MCP cho phép đại lý của chúng tôi tận dụng các công cụ bên ngoài (trong trường hợp này là API của InstaVibe) theo cách chuẩn hoá.

Sau khi xác minh cả hai thao tác, hãy quay lại thiết bị đầu cuối Cloud Shell và nhấn Ctrl+C để dừng giao diện người dùng ADK Dev.

9. Tác nhân quy trình công việc và nhiều tác nhân trong ADK

Hiện tại, các nhân viên hỗ trợ của chúng tôi có thể lên kế hoạch cho các chuyến đi chơi và tương tác với nền tảng. Tuy nhiên, để lập kế hoạch thực sự phù hợp với từng người dùng, bạn cần hiểu rõ vòng tròn xã hội của người dùng. Đối với những người dùng bận rộn có thể không theo dõi chặt chẽ hoạt động của bạn bè, việc thu thập ngữ cảnh này theo cách thủ công sẽ rất khó khăn. Để giải quyết vấn đề này, chúng tôi sẽ xây dựng một tác nhân Phân tích xã hội tận dụng Cơ sở dữ liệu biểu đồ Spanner để phân tích hoạt động và mối quan tâm của bạn bè, từ đó đưa ra các đề xuất phù hợp hơn.

Tác nhân lập hồ sơ mạng xã hội

Trước tiên, chúng ta cần có các công cụ để tác nhân này truy cập vào dữ liệu biểu đồ.

👉📝 Thêm các hàm Python sau vào cuối tệp ~/instavibe-bootstrap/agents/social/instavibe.py:

def get_person_attended_events(person_id: str)-> list[dict]:
    """
    Fetches events attended by a specific person using Graph Query.
    Args:
       person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
   
if not db_instance: return None

   
graph_sql = """
        Graph SocialGraph
        MATCH (p:Person)-[att:Attended]->(e:Event)
        WHERE p.person_id = @person_id
        RETURN e.event_id, e.name, e.event_date, att.attendance_time
        ORDER BY e.event_date DESC
    """
   
params = {"person_id": person_id}
   
param_types_map = {"person_id": param_types.STRING}
   
fields = ["event_id", "name", "event_date", "attendance_time"]

   
results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

   
if results is None: return None

   
for event in results:
       
if isinstance(event.get('event_date'), datetime):
           
event['event_date'] = event['event_date'].isoformat()
       
if isinstance(event.get('attendance_time'), datetime):
           
event['attendance_time'] = event['attendance_time'].isoformat()
   
return results

def get_person_id_by_name( name: str) -> str:
    """
    Fetches the person_id for a given name using SQL.

    Args:
       name (str): The name of the person to search for.

    Returns:
        str or None: The person_id if found, otherwise None.
                     Returns the ID of the *first* match if names are duplicated.
    """
   
if not db_instance: return None

   
sql = """
        SELECT person_id
        FROM Person
        WHERE name = @name
        LIMIT 1 -- Return only the first match in case of duplicate names
    """
   
params = {"name": name}
   
param_types_map = {"name": param_types.STRING}
   
fields = ["person_id"]

   
# Use the standard SQL query helper
   
results = run_sql_query( sql, params=params, param_types=param_types_map, expected_fields=fields)

   
if results: # Check if the list is not empty
       
return results[0].get('person_id') # Return the ID from the first dictionary
   
else:
       
return None # Name not found


def get_person_posts( person_id: str)-> list[dict]:
    """
    Fetches posts written by a specific person using Graph Query.

    Args:
        person_id (str): The ID of the person whose posts to fetch.


    Returns:
        list[dict] or None: List of post dictionaries with ISO date strings,
                           or None if an error occurs.
    """
   
if not db_instance: return None

   
# Graph Query: Find the specific Person node, follow 'Wrote' edge to Post nodes
   
graph_sql = """
        Graph SocialGraph
        MATCH (author:Person)-[w:Wrote]->(post:Post)
        WHERE author.person_id = @person_id
        RETURN post.post_id, post.author_id, post.text, post.sentiment, post.post_timestamp, author.name AS author_name
        ORDER BY post.post_timestamp DESC
    """
   
# Parameters now include person_id and limit
   
params = {
       
"person_id": person_id
   
}
   
param_types_map = {
       
"person_id": param_types.STRING
   
}
   
# Fields returned remain the same
   
fields = ["post_id", "author_id", "text", "sentiment", "post_timestamp", "author_name"]

   
results = run_graph_query(graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

   
if results is None:
       
return None

   
# Convert datetime objects to ISO format strings
   
for post in results:
       
if isinstance(post.get('post_timestamp'), datetime):
           
post['post_timestamp'] = post['post_timestamp'].isoformat()

   
return results


def get_person_friends( person_id: str)-> list[dict]:
    """
    Fetches friends for a specific person using Graph Query.
    Args:
        person_id (str): The ID of the person whose posts to fetch.
    Returns: list[dict] or None.
    """
   
if not db_instance: return None

   
graph_sql = """
        Graph SocialGraph
        MATCH (p:Person {person_id: @person_id})-[f:Friendship]-(friend:Person)
        RETURN DISTINCT friend.person_id, friend.name
        ORDER BY friend.name
    """
   
params = {"person_id": person_id}
   
param_types_map = {"person_id": param_types.STRING}
   
fields = ["person_id", "name"]

   
results = run_graph_query( graph_sql, params=params, param_types=param_types_map, expected_fields=fields)

   
return results

Bây giờ, hãy thảo luận về cách cấu trúc tác nhân của chúng ta. Để phân tích nhiều hồ sơ của bạn bè rồi tóm tắt kết quả, bạn cần thực hiện một số bước. Đây là trường hợp hoàn hảo để sử dụng các tính năng đa tác nhân của ADK, cụ thể là Tác nhân quy trình công việc.

Trong ADK của Google, Tác nhân quy trình công việc không tự thực hiện các tác vụ mà điều phối các tác nhân khác, được gọi là tác nhân phụ. Điều này cho phép thiết kế mô-đun, chia nhỏ các vấn đề phức tạp thành các thành phần chuyên biệt. ADK cung cấp các loại quy trình làm việc tích hợp sẵn như

  • Tuần tự (từng bước)
  • Song song (thực thi đồng thời)
  • và Vòng lặp (thực thi lặp lại)

Tác nhân lập hồ sơ mạng xã hội

Đối với tác vụ lập hồ sơ xã hội, thiết kế của chúng ta sử dụng một Trình đại diện lặp để tạo quy trình làm việc lặp lại. Mục đích là xử lý từng người một: profile_agent thu thập dữ liệu, summary_agent cập nhật kết quả phân tích và check_agent xác định xem chúng ta có nên lặp lại hay không.

Hãy xác định các tác nhân phụ cần thiết cho quy trình công việc này.

👉📝 Trong ~/instavibe-bootstrap/agents/social/agent.py, hãy thay thế #REPLACE FOR profile_agent bằng nội dung sau:

profile_agent = LlmAgent(
   
name="profile_agent",
   
model="gemini-2.5-flash",
   
description=(
       
"Agent to answer questions about the this person social profile. Provide the person's profile using their name, make sure to fetch the id before getting other data."
   
),
   
instruction=(
       
"You are a helpful agent to answer questions about the this person social profile. You'll be given a list of names, provide the person's profile using their name, make sure to fetch the id before getting other data. Get one person at a time, start with the first one on the list, and skip if already provided. return this person's result"
   
),
   
tools=[get_person_posts,get_person_friends,get_person_id_by_name,get_person_attended_events],
)

Tiếp theo, tác nhân sẽ lấy thông tin hồ sơ đã thu thập (được tích luỹ qua các vòng lặp) và tạo bản tóm tắt cuối cùng, xác định điểm chung nếu nhiều người được phân tích.

👉📝 Trong cùng một ~/instavibe-bootstrap/agents/social/agent.py, hãy thay thế #REPLACE FOR summary_agent bằng đoạn mã sau:

summary_agent = LlmAgent(
   
name="summary_agent",
   
model="gemini-2.5-flash",
   
description=(
       
"Generate a comprehensive social summary as a single, cohesive paragraph. This summary should cover the activities, posts, friend networks, and event participation of one or more individuals. If multiple profiles are analyzed, the paragraph must also identify and integrate any common ground found between them."
   
),
   
instruction=(
        """
        Your primary task is to synthesize social profile information into a single, comprehensive paragraph.

            **Input Scope & Default Behavior:**
            *   If specific individuals are named by the user, focus your analysis on them.
            *   **If no individuals are specified, or if the request is general, assume the user wants an analysis of *all relevant profiles available in the current dataset/context*.**

            **For each profile (whether specified or determined by default), you must analyze:**

            1.  **Post Analysis:**
                *   Systematically review their posts (e.g., content, topics, frequency, engagement).
                *   Identify recurring themes, primary interests, and expressed sentiments.

            2.  **Friendship Relationship Analysis:**
                *   Examine their connections/friends list.
                *   Identify key relationships, mutual friends (especially if comparing multiple profiles), and the general structure of their social network.

            3.  **Event Participation Analysis:**
                *   Investigate their past (and if available, upcoming) event participation.
                *   Note the types of events, frequency of attendance, and any notable roles (e.g., organizer, speaker).

            **Output Generation (Single Paragraph):**

            *   **Your entire output must be a single, cohesive summary paragraph.**
                *   **If analyzing a single profile:** This paragraph will detail their activities, interests, and social connections based on the post, friend, and event analysis.
                *   **If analyzing multiple profiles:** This paragraph will synthesize the key findings regarding posts, friends, and events for each individual. Crucially, it must then seamlessly integrate or conclude with an identification and description of the common ground found between them (e.g., shared interests from posts, overlapping event attendance, mutual friends). The aim is a unified narrative within this single paragraph.

            **Key Considerations:**
            *   Base your summary strictly on the available data.
            *   If data for a specific category (posts, friends, events) is missing or sparse for a profile, you may briefly acknowledge this within the narrative if relevant.
                """
       
),
   
output_key="summary"
)

Chúng ta cần có cách để xác định thời điểm vòng lặp dừng (tức là khi tất cả hồ sơ được yêu cầu đã được tóm tắt)

👉📝 Trong cùng một ~/instavibe-bootstrap/agents/social/agent.py, hãy thay thế #REPLACE FOR check_agent bằng đoạn mã sau:

check_agent = LlmAgent(
   
name="check_agent",
   
model="gemini-2.5-flash",
   
description=(
       
"Check if everyone's social profile are summarized and has been generated. Output 'completed' or 'pending'."
   
),
   
output_key="summary_status"
)

Chúng ta thêm một quy trình kiểm tra đơn giản (CheckCondition) để xem xét rõ ràng summary_status được lưu trữ trong Trạng thái do check_agent trả về và cho Loop Agent biết liệu có tiếp tục (escalate=False) hay dừng (escalate=True).

👉📝 Trong cùng một ~/instavibe-bootstrap/agents/social/agent.py, hãy thay thế #REPLACE FOR CheckCondition nằm ở đầu tệp bằng đoạn mã sau:

class CheckCondition(BaseAgent):
   
async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]:
       
#log.info(f"Checking status: {ctx.session.state.get("summary_status", "fail")}")
       
log.info(f"Summary: {ctx.session.state.get("summary")}")

       
status = ctx.session.state.get("summary_status", "fail").strip()
       
is_done = (status == "completed")

       
yield Event(author=self.name, actions=EventActions(escalate=is_done))

Trạng thái và lệnh gọi lại cho kết quả vòng lặp

Trong ADK của Google, Trạng thái là một khái niệm quan trọng đại diện cho bộ nhớ hoặc dữ liệu đang hoạt động của một tác nhân trong quá trình thực thi. Về cơ bản, đây là một ngữ cảnh ổn định chứa thông tin mà một tác nhân cần duy trì trên nhiều bước, lệnh gọi công cụ hoặc lượt tương tác. Trạng thái này có thể lưu trữ kết quả trung gian, thông tin người dùng, tham số cho các hành động tiếp theo hoặc bất kỳ dữ liệu nào khác mà tác nhân cần ghi nhớ khi thực hiện một tác vụ.

Trong trường hợp của chúng ta, khi Trình đại diện vòng lặp lặp lại, summary_agentcheck_agent sẽ lưu trữ đầu ra của chúng (summary và summary_status) trong Trạng thái của tác nhân. Điều này cho phép thông tin tồn tại trong các lần lặp lại. Tuy nhiên, chính Trình đại diện vòng lặp không tự động trả về bản tóm tắt cuối cùng từ trạng thái khi hoàn tất.

Tác nhân lập hồ sơ mạng xã hội

Lệnh gọi lại trong ADK cho phép chúng ta chèn logic tuỳ chỉnh để thực thi tại các điểm cụ thể trong vòng đời của tác nhân hoặc để phản hồi một số sự kiện nhất định, chẳng hạn như khi hoàn tất lệnh gọi công cụ hoặc trước khi tác nhân hoàn tất quá trình thực thi. Các lớp này cung cấp một cách để tuỳ chỉnh hành vi của tác nhân và xử lý kết quả một cách linh động.

Chúng ta sẽ sử dụng after_agent_callback chạy khi vòng lặp kết thúc (vì CheckCondition đã được chuyển lên cấp trên). Lệnh gọi lại modify_output_after_agent này truy xuất bản tóm tắt cuối cùng từ trạng thái và định dạng bản tóm tắt đó dưới dạng thông báo đầu ra cuối cùng của tác nhân.

Gọi lại

👉📝 Trong cùng một ~/instavibe-bootstrap/agents/social/agent.py, hãy thay thế #REPLACE FOR modify_output_after_agent bằng mã sau:

def modify_output_after_agent(callback_context: CallbackContext) -> Optional[types.Content]:

   
agent_name = callback_context.agent_name
   
invocation_id = callback_context.invocation_id
   
current_state = callback_context.state.to_dict()
   
current_user_content = callback_context.user_content
   
print(f"[Callback] Exiting agent: {agent_name} (Inv: {invocation_id})")
   
print(f"[Callback] Current summary_status: {current_state.get("summary_status")}")
   
print(f"[Callback] Current Content: {current_user_content}")

   
status = current_state.get("summary_status").strip()
   
is_done = (status == "completed")
   
# Retrieve the final summary from the state

   
final_summary = current_state.get("summary")
   
print(f"[Callback] final_summary: {final_summary}")
   
if final_summary and is_done and isinstance(final_summary, str):
       
log.info(f"[Callback] Found final summary, constructing output Content.")
       
# Construct the final output Content object to be sent back
       
return types.Content(role="model", parts=[types.Part(text=final_summary.strip())])
   
else:
       
log.warning("[Callback] No final summary found in state or it's not a string.")
       
# Optionally return a default message or None if no summary was generated
       
return None

Xác định Tác nhân lặp lại gốc

Cuối cùng, chúng ta xác định LoopAgent chính. Lớp này điều phối các tác nhân phụ theo trình tự trong mỗi vòng lặp (profile_agent -> summary_agent -> check_agent -> CheckCondition). Hàm này sẽ lặp lại trình tự này tối đa là max_iterations lần hoặc cho đến khi CheckCondition báo hiệu hoàn tất. Hàm after_agent_callback đảm bảo rằng nội dung tóm tắt cuối cùng được trả về.

👉📝 Trong cùng một ~/instavibe-bootstrap/agents/social/agent.py, hãy thay thế #REPLACE FOR root_agent bằng mã sau:

root_agent = LoopAgent(
   
name="InteractivePipeline",
   
sub_agents=[
       
profile_agent,
       
summary_agent,
       
check_agent,
       
CheckCondition(name="Checker")
   
],
   
description="Find everyone's social profile on events, post and friends",
   
max_iterations=10,
   
after_agent_callback=modify_output_after_agent
)

Hãy kiểm thử quy trình làm việc nhiều tác nhân này bằng giao diện người dùng dành cho nhà phát triển ADK.

👉💻 Chạy máy chủ web ADK:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?GOOGLE_CLOUD_PROJECT\)=.*|GOOGLE_CLOUD_PROJECT=${PROJECT_ID}|" ~/instavibe-bootstrap/agents/social/.env
adk web

Mở giao diện người dùng dành cho nhà phát triển ADK (cổng 8000 thông qua tính năng Xem trước trên web). Trong trình đơn thả xuống về người đại diện (ở trên cùng bên phải), hãy chọn Người đại diện Mạng xã hội.

👉 Bây giờ, hãy giao cho ứng dụng này nhiệm vụ lập hồ sơ cho nhiều người. Trong hộp thoại trò chuyện, hãy nhập:

Tell me about Mike and Bob

Sau khi nhân viên hỗ trợ trả lời (có thể mất thêm chút thời gian do vòng lặp và nhiều lệnh gọi LLM), đừng chỉ xem kết quả trò chuyện cuối cùng. Chuyển đến thẻ Sự kiện trong ngăn bên trái của giao diện người dùng dành cho nhà phát triển ADK.

👉 Bước xác minh: Trong thẻ Sự kiện, bạn sẽ thấy dấu vết chi tiết, từng bước của quá trình thực thi. 09-01-adk-dev-ui.png

Sau khi quan sát cách tác nhân gọi từng tác nhân phụ, bạn dự kiến luồng sẽ đi từ profile_agent -> summary_agent -> check_agent, Trình kiểm tra trong mỗi lần lặp lại. Tuy nhiên, trong thực tế, chúng ta thấy tính năng "tự tối ưu hoá" mạnh mẽ của tác nhân đang hoạt động.

Vì mô hình cơ bản xem toàn bộ yêu cầu (ví dụ: "phân tích Mike và Bob"), thường chọn đường dẫn hiệu quả nhất, thu thập tất cả dữ liệu cần thiết trong một lượt tổng hợp thay vì lặp lại nhiều lần. Bạn có thể xem dữ liệu đầu vào, đầu ra và trạng thái cho từng bước, bao gồm cả các lệnh gọi công cụ do profile_agent thực hiện

09-02-ui-graph.png

và thông tin cập nhật trạng thái từ check_agent và CheckCondition. 09-03-ui-state.png

Dấu vết trực quan này rất có giá trị để hiểu và gỡ lỗi cách quy trình làm việc nhiều tác nhân hoạt động cho đến khi thông tin tóm tắt cuối cùng được tạo và trả về bằng lệnh gọi lại.

Sau khi bạn khám phá phản hồi trò chuyện và dấu vết sự kiện, hãy quay lại thiết bị đầu cuối Cloud Shell và nhấn Ctrl+C để dừng Giao diện người dùng dành cho nhà phát triển ADK.

10. Giao tiếp giữa các nhân viên hỗ trợ (A2A)

Cho đến nay, chúng ta đã xây dựng các tác nhân chuyên biệt, nhưng các tác nhân này hoạt động riêng biệt hoặc trong một quy trình công việc được xác định trước trên cùng một máy. Để xây dựng các hệ thống đa tác nhân thực sự phân tán và cộng tác, chúng ta cần một cách để các tác nhân (có thể chạy dưới dạng các dịch vụ riêng biệt) khám phá lẫn nhau và giao tiếp hiệu quả. Đây là lúc giao thức Agent-to-Agent (A2A) phát huy tác dụng.

Giao thức A2A là một tiêu chuẩn mở được thiết kế riêng cho hoạt động giao tiếp có khả năng tương tác giữa các tác nhân AI. Trong khi MCP tập trung vào hoạt động tương tác giữa nhân viên hỗ trợ với công cụ, thì A2A tập trung vào hoạt động tương tác giữa nhân viên hỗ trợ với nhân viên hỗ trợ. Tính năng này cho phép các đại lý:

  • Khám phá: Tìm các nhân viên hỗ trợ khác và tìm hiểu về năng lực của họ thông qua Thẻ nhân viên hỗ trợ được chuẩn hoá.
  • Liên lạc: Trao đổi tin nhắn và dữ liệu một cách an toàn.
  • Cộng tác: Uỷ quyền công việc và điều phối hành động để đạt được các mục tiêu phức tạp.

Giao thức A2A hỗ trợ hoạt động giao tiếp này thông qua các cơ chế như "Thẻ đại lý". Các đại lý có thể sử dụng thẻ này để quảng cáo các chức năng và thông tin kết nối của họ.

10-05-agent-card

A2A sử dụng các tiêu chuẩn web quen thuộc (HTTP, SSE, JSON-RPC) và thường sử dụng mô hình máy khách-máy chủ, trong đó một tác nhân (máy khách) gửi tác vụ đến một tác nhân khác (tác nhân/máy chủ từ xa). Việc chuẩn hoá này là chìa khoá để xây dựng các hệ thống mô-đun, có thể mở rộng, trong đó các tác nhân được phát triển độc lập có thể hoạt động cùng nhau.

Bật tính năng A2A cho nhân viên hỗ trợ InstaVibe

Để các tác nhân khác có thể truy cập vào các tác nhân Lập kế hoạch, Tương tác trên nền tảng và Xã hội hiện có của chúng ta thông qua A2A, chúng ta cần gói từng tác nhân bằng một thành phần Máy chủ A2A. Máy chủ này sẽ:

  • Hiển thị thẻ tác nhân: Phân phát nội dung mô tả tiêu chuẩn về các chức năng của tác nhân thông qua điểm cuối HTTP.
  • Nghe các tác vụ(Yêu cầu thông báo): Chấp nhận các yêu cầu tác vụ đến từ các tác nhân khác (ứng dụng A2A) theo giao thức A2A.
  • Quản lý việc thực thi tác vụ(yêu cầu thông báo): Chuyển các tác vụ đã nhận cho logic tác nhân ADK cơ bản để xử lý.

Trình tác nhân của Trình lập kế hoạch (Đã bật A2A)

all-agent-planner

Hãy bắt đầu bằng cách thêm lớp máy chủ A2A vào Trình đại diện lập kế hoạch.

Xác định logic khởi động máy chủ A2A. Mã này xác định AgentCard (nội dung mô tả công khai của tác nhân), định cấu hình A2AServer và khởi động A2AServer, liên kết A2AServer với PlatformAgentExecutor.

👉📝 Thêm mã sau vào cuối ~/instavibe-bootstrap/agents/planner/a2a_server.py:

class PlannerAgent:
    """An agent to help user planning a event with its desire location."""
   
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

   
def __init__(self):
       
self._agent = self._build_agent()
       
self.runner = Runner(
           
app_name=self._agent.name,
           
agent=self._agent,
           
artifact_service=InMemoryArtifactService(),
           
session_service=InMemorySessionService(),
           
memory_service=InMemoryMemoryService(),
       
)
       
capabilities = AgentCapabilities(streaming=True)
       
skill = AgentSkill(
           
id="event_planner",
           
name="Event planner",
           
description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
           
tags=["instavibe"],
           
examples=["What about Bostona MA this weekend?"],
       
)
       
self.agent_card = AgentCard(
           
name="Event Planner Agent",
           
description="""
            This agent generates multiple fun plan suggestions tailored to your specified location, dates, and interests,
            all designed for a moderate budget. It delivers detailed itineraries,
            including precise venue information (name, latitude, longitude, and description), in a structured JSON format.
            """,
           
url=f"{PUBLIC_URL}",
           
version="1.0.0",
           
defaultInputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
           
defaultOutputModes=PlannerAgent.SUPPORTED_CONTENT_TYPES,
           
capabilities=capabilities,
           
skills=[skill],
       
)

   
def get_processing_message(self) -> str:
       
return "Processing the planning request..."

   
def _build_agent(self) -> LlmAgent:
        """Builds the LLM agent for the night out planning agent."""
       
return agent.root_agent


if __name__ == '__main__':
   
try:
       
plannerAgent = PlannerAgent()

       
request_handler = DefaultRequestHandler(
           
agent_executor=PlannerAgentExecutor(plannerAgent.runner,plannerAgent.agent_card),
           
task_store=InMemoryTaskStore(),
       
)

       
server = A2AStarletteApplication(
           
agent_card=plannerAgent.agent_card,
           
http_handler=request_handler,
       
)
       
logger.info(f"Attempting to start server with Agent Card: {plannerAgent.agent_card.name}")
       
logger.info(f"Server object created: {server}")

       
uvicorn.run(server.build(), host='0.0.0.0', port=port)
   
except Exception as e:
       
logger.error(f"An error occurred during server startup: {e}")
       
exit(1)

👉💻 Hãy nhanh chóng kiểm thử xem máy chủ A2A có khởi động chính xác trên máy và phân phát Thẻ đại lý hay không. Chạy lệnh sau trong cửa sổ dòng lệnh đầu tiên:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server

👉 Bây giờ, hãy mở một cửa sổ dòng lệnh khác. (Nhấp vào dấu + trong bảng điều khiển của thiết bị đầu cuối) hai đầu nối

👉💻 Sử dụng curl để yêu cầu Thẻ đại lý từ máy chủ chạy cục bộ:

curl http://localhost:10003/.well-known/agent.json | jq

Bạn sẽ thấy nội dung đại diện JSON của AgentCard mà chúng ta đã xác định, xác nhận rằng máy chủ đang chạy và quảng cáo tác nhân Planner.

10-02-planner-a2a.png

Quay lại thiết bị đầu cuối đầu tiên (nơi máy chủ đang chạy) rồi nhấn Ctrl+C để dừng máy chủ.

👉💻 Giờ đây, khi đã thêm logic máy chủ A2A, chúng ta có thể tạo hình ảnh vùng chứa.

Tạo và triển khai Trình tác nhân lập kế hoạch

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"

echo "Building ${AGENT_NAME} agent..."
gcloud builds submit . \
 
--config=cloudbuild-build.yaml \
 
--project=${PROJECT_ID} \
 
--region=${REGION} \
 
--substitutions=_AGENT_NAME=${AGENT_NAME},_IMAGE_PATH=${IMAGE_PATH}

echo "Image built and pushed to: ${IMAGE_PATH}"

👉💻 Và triển khai Trình tác nhân lập kế hoạch trên Cloud Run.

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/agents

# Set variables specific to the PLANNER agent
export IMAGE_TAG="latest"
export AGENT_NAME="planner"
export IMAGE_NAME="planner-agent"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="planner-agent"
export PUBLIC_URL="https://planner-agent-${PROJECT_NUMBER}.${REGION}.run.app"


gcloud run deploy ${SERVICE_NAME} \
 
--image=${IMAGE_PATH} \
 
--platform=managed \
 
--region=${REGION} \
 
--set-env-vars="A2A_HOST=0.0.0.0" \
 
--set-env-vars="A2A_PORT=8080" \
 
--set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=TRUE" \
 
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
 
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
 
--set-env-vars="PUBLIC_URL=${PUBLIC_URL}" \
 
--allow-unauthenticated \
 
--project=${PROJECT_ID} \
 
--min-instances=1

Hãy xác minh rằng dịch vụ đã triển khai đang chạy và phân phát Thẻ tác nhân một cách chính xác từ đám mây bằng Trình kiểm tra A2A.

👉 Trên biểu tượng Xem trước trên web trong thanh công cụ Cloud Shell, hãy chọn Thay đổi cổng. Đặt cổng thành 8081 rồi nhấp vào "Thay đổi và xem trước". Một thẻ trình duyệt mới sẽ mở ra với giao diện Trình kiểm tra A2A.

10-08-web-preview.png

👉💻 Trong dòng lệnh, hãy lấy URL của tác nhân lập kế hoạch đã triển khai:

export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}

👉💻 Sao chép URL đầu ra.

👉 Trong giao diện người dùng của Trình kiểm tra A2A, hãy dán URL vào trường URL của tác nhân rồi nhấp vào Kết nối.

👀 Thông tin thẻ và JSON của nhân viên hỗ trợ sẽ xuất hiện trên thẻ Nhân viên hỗ trợ, xác nhận rằng bạn đã kết nối thành công.

10-03-planner-a2a.png

👉 Nhấp vào thẻ Chat (Trò chuyện) trong Trình kiểm tra A2A. Đây là nơi bạn có thể tương tác trực tiếp với tác nhân đã triển khai. Hãy gửi cho tác nhân một tin nhắn để kiểm tra khả năng lập kế hoạch của tác nhân. Ví dụ:

Plan something for me in Boston MA this weekend, and I enjoy classical music

👀 Để kiểm tra nội dung giao tiếp thô, hãy nhấp vào bong bóng tin nhắn của bạn, rồi nhấp vào bong bóng phản hồi của nhân viên hỗ trợ trong cửa sổ trò chuyện. Khi bạn nhấp vào từng mục, thông báo JSON-RPC 2.0 đầy đủ đã gửi hoặc nhận sẽ hiển thị. Đây là thông tin vô giá để gỡ lỗi.

Hãy giữ thẻ Trình kiểm tra A2A ở chế độ tiện dụng. ĐỪNG đóng cửa sổ này! Chúng ta sẽ sử dụng lại lớp này để kiểm thử hai tác nhân còn lại.

10-06-a2a-inspector.png

Trình tác nhân tương tác với nền tảng (Đã bật A2A)

all-agent-platform

Tiếp theo, chúng ta sẽ lặp lại quy trình này cho Trình tác vụ tương tác với nền tảng (trình tác vụ sử dụng MCP).

👉📝 Xác định chế độ thiết lập máy chủ A2A, bao gồm cả AgentCard riêng biệt của máy chủ đó ở cuối ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py:

class PlatformAgent:
  """An agent that post event and post to instavibe."""

 
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

 
def __init__(self):
   
self._agent = self._build_agent()
   
self.runner = Runner(
       
app_name=self._agent.name,
       
agent=self._agent,
       
artifact_service=InMemoryArtifactService(),
       
session_service=InMemorySessionService(),
       
memory_service=InMemoryMemoryService(),
   
)
   
capabilities = AgentCapabilities(streaming=True)
   
skill = AgentSkill(
           
id="instavibe_posting",
           
name="Post social post and events on instavibe",
           
description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
           
tags=["instavibe"],
           
examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
       
)
   
self.agent_card = AgentCard(
           
name="Instavibe Posting Agent",
           
description="""
            This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
            for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
            to perform these actions on your behalf, ensuring a smooth sharing experience.
            """,
           
url=f"{PUBLIC_URL}",
           
version="1.0.0",
           
defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
           
defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
           
capabilities=capabilities,
           
skills=[skill],
       
)


 
def get_processing_message(self) -> str:
     
return "Processing the social post and event request..."

 
def _build_agent(self) -> LlmAgent:
    """Builds the LLM agent for the Processing the social post and event request."""
   
return agent.root_agent


if __name__ == '__main__':
   
try:
       
platformAgent = PlatformAgent()

       
request_handler = DefaultRequestHandler(
           
agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
           
task_store=InMemoryTaskStore(),
       
)

       
server = A2AStarletteApplication(
           
agent_card=platformAgent.agent_card,
           
http_handler=request_handler,
       
)

       
uvicorn.run(server.build(), host='0.0.0.0', port=port)
   
except Exception as e:
       
logger.error(f"An error occurred during server startup: {e}")
       
exit(1)

Nhân viên hỗ trợ mạng xã hội (Đã bật A2A)

all-agent-social

Cuối cùng, hãy bật A2A cho Tác nhân phân tích hồ sơ xã hội.

👉📝 Xác định chế độ thiết lập máy chủ A2A và AgentCard ở cuối ~/instavibe-bootstrap/agents/social/a2a_server.py:

class SocialAgent:
  """An agent that handles social profile analysis."""

 
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]

 
def __init__(self):
   
self._agent = self._build_agent()
   
self.runner = Runner(
       
app_name=self._agent.name,
       
agent=self._agent,
       
artifact_service=InMemoryArtifactService(),
       
session_service=InMemorySessionService(),
       
memory_service=InMemoryMemoryService(),
   
)
   
capabilities = AgentCapabilities(streaming=True)
   
skill = AgentSkill(
               
id="social_profile_analysis",
               
name="Analyze Instavibe social profile",
               
description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
               
tags=["instavibe"],
               
examples=["Can you tell me about Bob and Alice?"],
   
)
   
self.agent_card = AgentCard(
               
name="Social Profile Agent",
               
description="""
                Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
                It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
                and connections based on profile data.
                """,
               
url=f"{PUBLIC_URL}",
               
version="1.0.0",
               
defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
               
defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
               
capabilities=capabilities,
               
skills=[skill],
   
)

 
def get_processing_message(self) -> str:
     
return "Processing the social profile analysis request..."

 
def _build_agent(self) -> LoopAgent:
    """Builds the LLM agent for the social profile analysis agent."""
   
return agent.root_agent

if __name__ == '__main__':
   
try:
       
socialAgent = SocialAgent()

       
request_handler = DefaultRequestHandler(
           
agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
           
task_store=InMemoryTaskStore(),
       
)

       
server = A2AStarletteApplication(
           
agent_card=socialAgent.agent_card,
           
http_handler=request_handler,
       
)

       
uvicorn.run(server.build(), host='0.0.0.0', port=port)
   
except Exception as e:
       
logger.error(f"An error occurred during server startup: {e}")
       
exit(1)

Xây dựng và triển khai các tác nhân tương tác trên nền tảng và tác nhân xã hội

Các tác nhân này cần có quyền truy cập vào Spanner, vì vậy, hãy đảm bảo rằng các biến môi trường SPANNER_INSTANCE_ID, SPANNER_DATABASE_IDMCP_SERVER_URL được truyền chính xác trong quá trình triển khai.

👉💻 Tạo bản dựng và triển khai lên Cloud Run bằng Cloud Build:

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/agents
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse


gcloud builds submit . \
 
--config=cloudbuild.yaml \
 
--project="${PROJECT_ID}" \
 
--region="${REGION}" \
 
--substitutions=\
_PROJECT_ID="${PROJECT_ID}",\
_PROJECT_NUMBER="${PROJECT_NUMBER}",\
_REGION="${REGION}",\
_REPO_NAME="${REPO_NAME}",\
_SPANNER_INSTANCE_ID="${SPANNER_INSTANCE_ID}",\
_SPANNER_DATABASE_ID="${SPANNER_DATABASE_ID}",\
_MCP_SERVER_URL="${MCP_SERVER_URL}"

👉💻 Trong dòng lệnh, hãy lấy URL của tác nhân nền tảng đã triển khai:

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
echo $PLATFORM_MPC_CLIENT_URL

👉💻 Sao chép URL đầu ra.

👉 Trong giao diện người dùng của Trình kiểm tra A2A, hãy dán URL vào trường URL của tác nhân rồi nhấp vào Kết nối.

👀 Thông tin thẻ và JSON của nhân viên hỗ trợ sẽ xuất hiện trên thẻ Nhân viên hỗ trợ, xác nhận rằng bạn đã kết nối thành công.

10-05-platform-a2a.png

👉 Nhấp vào thẻ Chat (Trò chuyện) trong Trình kiểm tra A2A. Đây là nơi bạn có thể tương tác trực tiếp với nhân viên hỗ trợ đã triển khai. Hãy gửi tin nhắn cho nhân viên hỗ trợ để kiểm tra khả năng tạo bài đăng của nhân viên hỗ trợ:

Create a post for me, the post says 'Paws, purrs, and ocean views 🐾☕🌊. Spent my morning at the Morning Seaside Cat Café, where every sip comes with a side of snuggles and sea breeze.' and make it positive, and I'm Oscar.

👀 Để kiểm tra nội dung giao tiếp thô, hãy nhấp vào bong bóng tin nhắn của bạn, rồi nhấp vào bong bóng phản hồi của nhân viên hỗ trợ trong cửa sổ trò chuyện. Khi bạn nhấp vào từng mục, thông báo JSON-RPC 2.0 đầy đủ đã gửi hoặc nhận sẽ hiển thị. Đây là thông tin vô giá để gỡ lỗi.

👉💻 Trong thiết bị đầu cuối, hãy lấy URL của tác nhân mạng xã hội đã triển khai:

export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL

👉💻 Sao chép URL đầu ra.

👉 Trong giao diện người dùng của Trình kiểm tra A2A, hãy dán URL vào trường URL của tác nhân rồi nhấp vào Kết nối.

👀 Thông tin thẻ và JSON của nhân viên hỗ trợ sẽ xuất hiện trên thẻ Nhân viên hỗ trợ, xác nhận rằng bạn đã kết nối thành công.

10-04-social-a2a.png

👉 Nhấp vào thẻ Chat (Trò chuyện) trong Trình kiểm tra A2A. Đây là nơi bạn có thể tương tác trực tiếp với tác nhân đã triển khai. Hãy gửi cho tác nhân đó một thông báo để phân tích hồ sơ người dùng trong cơ sở dữ liệu của bạn:

Can you tell me about both Ian and Kevin's profile, what are their common interests?

👀 Để kiểm tra nội dung giao tiếp thô, hãy nhấp vào bong bóng tin nhắn của bạn, rồi nhấp vào bong bóng phản hồi của nhân viên hỗ trợ trong cửa sổ trò chuyện. Khi bạn nhấp vào từng mục, thông báo JSON-RPC 2.0 đầy đủ đã gửi hoặc nhận sẽ hiển thị. Đây là thông tin vô giá để gỡ lỗi.

👉 Tuyệt vời! Chúng tôi đã kiểm tra xong tất cả các tác nhân. Giờ bạn có thể đóng thẻ Trình kiểm tra A2A.

11. Tác nhân điều phối (Ứng dụng A2A)

Hiện tại, chúng tôi có 3 tác nhân chuyên biệt (Trình lập kế hoạch, Nền tảng, Mạng xã hội) chạy dưới dạng các dịch vụ độc lập, hỗ trợ A2A trên Cloud Run. Phần cuối cùng là Tác nhân điều phối. Tác nhân này sẽ đóng vai trò là điều phối viên trung tâm hoặc Ứng dụng A2A. Ứng dụng này sẽ nhận yêu cầu của người dùng, xác định(các) tác nhân từ xa cần thiết để thực hiện yêu cầu (có thể theo trình tự), sau đó sử dụng giao thức A2A để uỷ quyền tác vụ cho các tác nhân từ xa đó. Trong hội thảo này, chúng ta sẽ chạy tác nhân Điều phối trên máy bằng giao diện người dùng dành cho nhà phát triển ADK.

all-agent-orchestrator

Trước tiên, hãy nâng cao logic của Trình điều phối để xử lý việc đăng ký các tác nhân từ xa mà Trình điều phối phát hiện được. Lưu trữ thông tin chi tiết về kết nối từ Thẻ nhân viên hỗ trợ được tìm nạp trong quá trình khởi chạy.

👉📝 Trong ~/instavibe-bootstrap/agents/orchestrate/agent.py, hãy thay thế #REPLACE ME REG AGENT CARD bằng:

async with httpx.AsyncClient(timeout=30) as client:
           
for i, address in enumerate(REMOTE_AGENT_ADDRESSES):
               
log.info(f"--- STEP 3.{i}: Attempting connection to: {address} ---")
               
try:
                   
card_resolver = A2ACardResolver(client, address)
                   
card = await card_resolver.get_agent_card()
                   
                   
remote_connection = RemoteAgentConnections(agent_card=card, agent_url=address)
                   
self.remote_agent_connections[card.name] = remote_connection
                   
self.cards[card.name] = card
                   
log.info(f"--- STEP 5.{i}: Successfully stored connection for {card.name} ---")

               
except Exception as e:
                   
log.error(f"--- CRITICAL FAILURE at STEP 4.{i} for address: {address} ---")
                   
log.error(f"--- The hidden exception type is: {type(e).__name__} ---")
                   
log.error(f"--- Full exception details and traceback: ---", exc_info=True)

Tiếp theo, hãy xác định công cụ cho chính tác nhân Điều phối trong ADK.

  • send_message (hàm A2A để uỷ quyền công việc).

👉📝 Thay thế #REPLACE ME CREATE AGENT trong ~/instavibe-bootstrap/agents/orchestrate/agent.py bằng:

def create_agent(self) -> Agent:
        """Synchronously creates the ADK Agent object."""
       
return Agent(
           
model="gemini-2.5-flash",
           
name="orchestrate_agent",
           
instruction=self.root_instruction,
           
before_agent_callback=self.before_agent_callback,
           
description=("Orchestrates tasks for child agents."),
           
tools=[self.send_message],
       
)

Logic cốt lõi của Trình điều phối nằm trong hướng dẫn của trình điều phối, hướng dẫn cách sử dụng A2A.

👉📝 Thay thế #REPLACE ME INSTRUCTIONS trong ~/instavibe-bootstrap/agents/orchestrate/agent.py bằng phương thức tạo hướng dẫn sau:

def root_instruction(self, context: ReadonlyContext) -> str:
       
current_agent = self.check_active_agent(context)
       
return f"""
                You are an expert AI Orchestrator. Your primary responsibility is to intelligently interpret user requests, break them down into a logical plan of discrete actions, and delegate each action to the most appropriate specialized remote agent using the send_message function. You do not perform the tasks yourself but manage their assignment, sequence, and critically, their outcomes.
                    **Core Directives & Decision Making:**

                    *   **Understand User Intent & Complexity:**
                        *   Carefully analyze the user's request to determine the core task(s) they want to achieve. Pay close attention to keywords and the overall goal.
                        *   Identify if the request requires a single agent or a sequence of actions from multiple agents. For example, "Analyze John Doe's profile and then create a positive post about his recent event attendance" would require two agents in sequence.

                    *   **Task Planning & Sequencing (for Multi-Step Requests):**
                        *   Before delegating, outline the clear sequence of agent tasks.
                        *   Identify dependencies. If Task B requires output from Task A, execute them sequentially. If tasks are independent (like creating a post and then creating an event), execute them one after the other as separate delegations.
                        *   Agent Reusability: An agent's completion of one task does not make it unavailable. If a user's plan involves multiple, distinct actions that fall under the same agent's expertise (e.g., create a post, then create an event), you must call that same agent again for the subsequent task.

                    *   **Task Delegation & Management (using `send_message`):**
                        *   **Delegation:** Use `send_message` to assign actionable tasks to the selected remote agent. Your `send_message` call MUST include:
                            *   The `remote_agent_name` you've selected.
                            *   The `user_request` or all necessary parameters extracted from the user's input, formatted in a way the target agent will understand.
                        *   **Contextual Awareness for Remote Agents:** If a remote agent repeatedly requests user confirmation or seems to lack context, assume it lacks access to the full conversation history. In such cases, enrich your `send_message` with all necessary contextual information relevant to that specific agent from the conversation history.
                        *   **Sequential Task Execution:**
                            *   After a preceding task completes (indicated by the agent's response or a success signal), gather any necessary output from it.
                            *   Then, use `send_message` for the next agent in the sequence, providing it with the user's original relevant intent and any necessary data obtained from the previous agent's task.
                        *   **Active Agent Prioritization:** If an active agent is already engaged and the user's request is related to its current task, route subsequent related requests directly to that agent by providing updated context via `send_message`.
                   
                   
                    **Critical Success Verification:**

                    *   You **MUST** wait for the tool_output after every send_message call before taking any further action.
                    *   Your decision to proceed to the next task in a sequence **MUST** be based entirely on a confirmation of success from the tool_output of the previous task.
                    *   If a tool call fails, returns an error, or the tool_output is ambiguous, you MUST STOP the sequence. Your next action is to report the exact failure or ambiguity to the user.
                    *   DO NOT assume a task was successful. Do not invent success messages like "The event has been created." Only state that a task is complete if the tool's response explicitly says so.
                   
                    **Communication with User:**

                    *   **Transparent Communication:** Always present the complete and detailed response from the remote agent to the user. Do not summarize or filter unless explicitly instructed.
                    *   When you delegate a task (or the first task in a sequence), clearly inform the user which remote agent is handling it.
                    *   For multi-step requests, you can optionally inform the user of the planned sequence (e.g., "Okay, first I'll ask the 'Social Profile Agent' to analyze the profile, and then I'll have the 'Instavibe Posting Agent' create the post.").
                    *   If waiting for a task in a sequence to complete, you can inform the user (e.g., "The 'Social Profile Agent' is currently processing. I'll proceed with the post once that's done.").
                    *   **User Confirmation Relay:** If a remote agent asks for confirmation, and the user has not already provided it, just make up something.
                    *   If the user's request is ambiguous, if necessary information is missing for any agent in the sequence, or if you are unsure about the plan, just make up something.

                    **Important Reminders:**

                    *   **Autonomous Agent Engagement:** Never seek user permission before engaging with remote agents. If multiple agents are required to fulfill a request, connect with them directly without requesting user preference or confirmation.
                    *   **Focused Information Sharing:** Provide remote agents with only relevant contextual information. Avoid extraneous details that are not directly pertinent to their task.
                    *   **No Redundant Confirmations:** Do not ask remote agents for confirmation of information or actions they have already processed or committed to.
                    *   **Tool Reliance:** Strictly rely on your available tools, primarily `send_message`, to address user requests. Do not generate responses based on assumptions. If information is insufficient, request clarification from the user.
                    *   **Prioritize Recent Interaction:** Focus primarily on the most recent parts of the conversation when processing requests, while maintaining awareness of the overall goal for multi-step tasks.
                    *   Always prioritize selecting the correct agent(s) based on their documented purpose.
                    *   Ensure all information required by the chosen remote agent is included in the `send_message` call, including outputs from previous agents if it's a sequential task.

                    Agents:
                    {self.agents}

                    Current agent: {current_agent['active_agent']}`
                """

Kiểm thử Trình điều phối và Hệ thống A2A đầy đủ

Bây giờ, hãy kiểm thử toàn bộ hệ thống. Chúng ta sẽ chạy Trình điều phối cục bộ bằng Giao diện người dùng dành cho nhà phát triển ADK. Trình điều phối này sẽ giao tiếp với các tác nhân Lập kế hoạch, Nền tảng và Xã hội chạy từ xa trên Cloud Run.

👉💻 Trước tiên, hãy đảm bảo biến môi trường REMOTE_AGENT_ADDRESSES chứa các URL được phân tách bằng dấu phẩy của các tác nhân hỗ trợ A2A đã triển khai. Sau đó, hãy thiết lập các biến môi trường cần thiết cho tác nhân Điều phối và khởi chạy Giao diện người dùng dành cho nhà phát triển ADK:

. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate

export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}

cd  ~/instavibe-bootstrap/agents
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env
adk web

👉 Mở giao diện người dùng dành cho nhà phát triển ADK (Thay đổi cổng trở lại 8000 thông qua tính năng Xem trước trên web).

10-08-web-preview.png

👉 Trong trình đơn thả xuống về tác nhân, hãy chọn tác nhân orchestrate (điều phối).

👉 Bây giờ, hãy giao cho nó một nhiệm vụ phức tạp đòi hỏi phải điều phối nhiều tác nhân từ xa. Hãy thử ví dụ đầu tiên này, trong đó có liên quan đến Tác nhân xã hội và sau đó là Tác nhân lập kế hoạch:

You are an expert event planner for a user named  Diana.
   
Your task is to design a fun and personalized event.

   
Here are the details for the plan:
   
- Friends to invite: Ian, Nora
   
- Desired date: "2025-10-15"
   
- Location idea or general preference: "Chicago"

   
Your process should be:
   
1. Analyze the provided friend names. If you have access to a tool to get their InstaVibe profiles or summarized interests, please use it.
   
2. Based on their potential interests (or general good taste if profiles are unavailable), create a tailored plan for the outing, check if you have access to any event planner tools.
   
3. Ensure the plan includes the original `planned_date`.

   
The user wants a comprehensive plan that includes:
   
- The list of invited friends.
   
- A catchy and descriptive name for the event.
   
- The exact planned date for the event.
   
- A summary of what the group will do.
   
- Specific recommended spots (e.g., restaurants, bars, activity venues) with their names, (if possible, approximate latitude/longitude for mapping, and address), and a brief description of why it fits the plan.
   
- A short, exciting message that {Diana} can send to {Ian, Nora} to get them excited about the event.

Điều phối

Quan sát hoạt động tương tác trong cửa sổ trò chuyện trên giao diện người dùng ADK Dev. Hãy chú ý đến phản hồi của Trình điều phối – phản hồi này sẽ cho biết tác nhân từ xa nào được uỷ quyền thực hiện tác vụ (ví dụ: "Được rồi, trước tiên tôi sẽ hỏi Nhân viên hỗ trợ hồ sơ mạng xã hội về Ian và Nora...").

Ngoài ra, hãy kiểm tra thẻ Sự kiện trong giao diện người dùng để xem các lệnh gọi công cụ cơ bản (send_message) được thực hiện đến URL của các tác nhân từ xa.

Gửi việc cần làm

👉 Bây giờ, hãy thử ví dụ thứ hai liên quan trực tiếp đến Trình tích hợp nền tảng:

Hey, can you register an event on Instavibe for Laura and Charlie? Let's call it 'Vienna Concert & Castles Day'.
here are more info
"event_name": "Vienna Concert & Castles Day",
 
"description": "A refined and unforgettable day in Vienna with Laura and Charlie. The day begins with a guided tour of the magnificent Schönbrunn Palace, showcasing imperial architecture and history. In the evening, enjoy a classical music concert in one of Vienna's most iconic concert halls.",
 
"event_date": "2025-10-14T10:00:00+02:00",
 
"locations": [
   
{
     
"name": "Schönbrunn Palace",
     
"description": "A UNESCO World Heritage Site and former imperial summer residence, Schönbrunn Palace offers opulent rooms, beautiful baroque gardens, and a glimpse into the life of the Habsburg monarchy. Visitors can stroll the grounds or take a guided historical tour.",
     
"latitude": 48.184516,
     
"longitude": 16.312222,
     
"address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria"
   
},
   
{
     
"name": "Musikverein Vienna",
     
"description": "Home to the world-renowned Vienna Philharmonic, the Musikverein is one of the finest concert halls in the world. Its 'Golden Hall' is famous for its acoustics and ornate design. Attendees can enjoy a powerful classical concert in an unforgettable setting.",
     
"latitude": 48.200132,
     
"longitude": 16.373777,
     
"address": "Musikvereinsplatz 1, 1010 Wien, Austria"
   
}
 
],
 
"attendee_names": ["Laura", "Charlie", "Oscar"] And I am Oscar

Xin nhắc lại, hãy theo dõi cuộc trò chuyện và thẻ Sự kiện. Trình điều phối phải xác định nhu cầu tạo một sự kiện và uỷ quyền tác vụ (cùng với tất cả thông tin chi tiết được cung cấp) cho "Trình tích hợp nền tảng". Bạn cũng có thể nhấp vào nút Trace (Theo dõi) để xem dấu vết nhằm phân tích thời gian phản hồi truy vấn và các thao tác đã thực thi. Gửi sự kiện

Sau đó, bạn có thể xác minh sự kiện xuất hiện trong ứng dụng web InstaVibe. Sự kiện InstaVibe

Điều này minh hoạ việc triển khai thành công hệ thống nhiều tác nhân bằng ADK và giao thức A2A, trong đó một trình điều phối trung tâm uỷ quyền các tác vụ cho các tác nhân chuyên biệt, từ xa.

Hãy nhớ dừng Giao diện người dùng phát triển ADK (Ctrl+C trong thiết bị đầu cuối) khi bạn hoàn tất quá trình kiểm thử.

12. Công cụ tác nhân và tính năng Gọi từ xa của InstaVibe

Cho đến nay, chúng ta đã chạy các tác nhân chuyên biệt trên Cloud Run và kiểm thử Trình điều phối cục bộ bằng Giao diện người dùng dành cho nhà phát triển ADK. Đối với trường hợp sử dụng chính thức, chúng ta cần một môi trường mạnh mẽ, có thể mở rộng và được quản lý để lưu trữ các tác nhân của mình. Đó là lý do Google Vertex AI Agent Engine ra đời.

Agent Engine là một dịch vụ được quản lý toàn diện trên Vertex AI, được thiết kế riêng để triển khai và mở rộng quy mô các tác nhân AI. Công cụ này tóm tắt việc quản lý cơ sở hạ tầng, bảo mật và chi phí hoạt động, cho phép nhà phát triển (đặc biệt là những nhà phát triển ít quen thuộc với môi trường đám mây phức tạp) tập trung vào logic và chức năng của tác nhân thay vì quản lý máy chủ. Công cụ này cung cấp một thời gian chạy chuyên dụng được tối ưu hoá cho khối lượng công việc của tác nhân.

Bây giờ, chúng ta sẽ triển khai tác nhân Điều phối cho Agent Engine. (Lưu ý: Cơ chế triển khai hiển thị bên dưới sử dụng một tập lệnh tuỳ chỉnh (agent_engine_app.py) được cung cấp trong tài liệu hội thảo, vì các công cụ triển khai ADK-to-Agent-Engine trực tiếp chính thức có thể vẫn đang phát triển. Tập lệnh này xử lý việc đóng gói và triển khai tác nhân Điều phối, được định cấu hình bằng các địa chỉ tác nhân từ xa cần thiết.)

Thực thi lệnh sau để triển khai tác nhân Điều phối cho Công cụ tác nhân. Đảm bảo rằng biến môi trường REMOTE_AGENT_ADDRESSES (chứa URL của các tác nhân Planner, Platform và Social trên Cloud Run) vẫn được đặt chính xác từ phần trước.

👉💻 Chúng ta sẽ triển khai tác nhân Orchestrate cho Agent Engine (Lưu ý: đây là cách triển khai của riêng tôi, ADK có một CLI để giúp triển khai, tôi sẽ cập nhật thông tin này sau khi triển khai BYO-SA).

cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
export PLATFORM_MPC_CLIENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep platform-mcp-client)
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)

export REMOTE_AGENT_ADDRESSES=${PLANNER_AGENT_URL},${PLATFORM_MPC_CLIENT_URL},${SOCIAL_AGENT_URL}
sed -i "s|^\(O\?REMOTE_AGENT_ADDRESSES\)=.*|REMOTE_AGENT_ADDRESSES=${REMOTE_AGENT_ADDRESSES}|" ~/instavibe-bootstrap/agents/orchestrate/.env

adk deploy agent_engine \
--display_name "orchestrate-agent" \
--project $GOOGLE_CLOUD_PROJECT \
--region $GOOGLE_CLOUD_LOCATION \
--staging_bucket gs://$GOOGLE_CLOUD_PROJECT-agent-engine \
--trace_to_cloud \
--requirements_file orchestrate/requirements.txt \
orchestrate

Giờ đây, khi Trình điều phối được lưu trữ trên nền tảng Agent Engine được quản lý, ứng dụng web InstaVibe của chúng ta cần giao tiếp với Trình điều phối đó. Thay vì tương tác thông qua giao diện người dùng ADK Dev, ứng dụng web sẽ thực hiện các lệnh gọi từ xa đến điểm cuối của Agent Engine.

10-agent-remote.png

Trước tiên, chúng ta cần sửa đổi mã ứng dụng InstaVibe để khởi chạy ứng dụng Agent Engine bằng mã nhận dạng duy nhất của tác nhân Orchestrator đã triển khai. Bạn cần có mã nhận dạng này để nhắm đến đúng thực thể của nhân viên hỗ trợ dự án trên nền tảng.

👉📝 Mở ~/instavibe-bootstrap/instavibe/introvertally.py và thay thế #REPLACE ME initiate agent_engine bằng mã sau. Thao tác này sẽ truy xuất mã nhận dạng Công cụ tác nhân từ một biến môi trường (chúng ta sẽ thiết lập biến này trong thời gian ngắn) và lấy một đối tượng ứng dụng:

ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)

Quy trình người dùng dự kiến của chúng tôi trong InstaVibe bao gồm hai lượt tương tác với nhân viên hỗ trợ: trước tiên, tạo kế hoạch được đề xuất và thứ hai, yêu cầu người dùng xác nhận trước khi nhân viên hỗ trợ thực sự đăng sự kiện lên nền tảng.

Vì ứng dụng web InstaVibe (chạy trên Cloud Run) và tác nhân Điều phối (chạy trên Agent Engine) hiện là các dịch vụ riêng biệt, nên ứng dụng web cần thực hiện các lệnh gọi từ xa đến điểm cuối Agent Engine để tương tác với tác nhân.

👉📝 Hãy cập nhật mã thực hiện lệnh gọi ban đầu để tạo đề xuất về kế hoạch. Trong cùng một tệp introvertally.py, hãy thay thế #REPLACE ME Query remote agent get plan bằng đoạn mã sau đây. Đoạn mã này sử dụng ứng dụng agent_engine để gửi yêu cầu của người dùng:

agent_engine.stream_query(
               
user_id=user_id,
               
message=prompt_message,
           
)

👉📝 Tiếp theo, hãy cập nhật mã xử lý xác nhận của người dùng (ví dụ: khi người dùng nhấp vào "Xác nhận kế hoạch"). Thao tác này sẽ gửi một thông báo tiếp theo đến cùng một cuộc trò chuyện trên Agent Engine, hướng dẫn Trình điều phối tiếp tục đăng sự kiện (mà Trình điều phối sẽ uỷ quyền cho tác nhân Tích hợp nền tảng). Thay thế #REPLACE ME Query remote agent for confirmation để xác nhận trong introvertally.py bằng:

agent_engine.stream_query(
           
user_id=agent_session_user_id,
           
message=prompt_message,
       
)

Các tuyến của ứng dụng web cần có quyền truy cập vào các hàm này. Đảm bảo các hàm cần thiết từ introvertally.py được nhập trong tệp tuyến đường Flask.

👉📝 Trong cd ~/instavibe-bootstrap/instavibe/ally_routes.py, trước tiên, chúng ta sẽ trỏ đến thực thể thay thế # REPLACE ME TO ADD IMPORT bằng nội dung sau:

from introvertally import call_agent_for_plan, post_plan_event

👉📝 Thêm tính năng nguyên mẫu vào InstaVibe, trong ~/instavibe-bootstrap/instavibe/templates/base.html, hãy thay thế <!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> bằng nội dung sau:

            <li class="nav-item">
             
<a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
           
</li>

Trước khi có thể triển khai lại ứng dụng InstaVibe, chúng ta cần có Resource ID cụ thể của tác nhân Điều phối mà chúng ta đã triển khai cho Agent Engine.

Hiện tại, việc truy xuất mã này theo phương thức lập trình thông qua gcloud có thể bị hạn chế, vì vậy, chúng ta sẽ sử dụng tập lệnh Python trợ giúp (temp-endpoint.py được cung cấp trong hội thảo) để tìm nạp mã nhận dạng và lưu trữ mã nhận dạng đó trong một biến môi trường.

👉💻 Chạy các lệnh sau để thực thi tập lệnh. Tập lệnh sẽ ghi lại Mã thiết bị đầu cuối của công cụ tác nhân và cấp các quyền cần thiết cho tài khoản dịch vụ mặc định của công cụ tác nhân (Lưu ý: Tập lệnh được định cấu hình để sử dụng tài khoản dịch vụ mặc định vì người dùng hiện không thể sửa đổi tài khoản này).

. ~/instavibe-bootstrap/set_env.sh
cd ~/instavibe-bootstrap/instavibe/
source ~/instavibe-bootstrap/env/bin/activate
python temp-endpoint.py
export ORCHESTRATE_AGENT_ID=$(cat temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"


gcloud projects add-iam-policy-binding $PROJECT_ID \
   
--member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
   
--role="roles/viewer"

Mã nhận dạng điểm cuối của công cụ nhân viên hỗ trợ

Cuối cùng, chúng ta cần triển khai lại ứng dụng web InstaVibe bằng mã đã cập nhật và biến môi trường ORCHESTRATE_AGENT_ID mới để ứng dụng biết cách kết nối với tác nhân chạy trên Agent Engine.

👉💻 Các lệnh sau đây sẽ tạo lại hình ảnh ứng dụng InstaVibe và triển khai phiên bản mới cho Cloud Run:

. ~/instavibe-bootstrap/set_env.sh

cd ~/instavibe-bootstrap/instavibe/

export IMAGE_TAG="latest"
export APP_FOLDER_NAME="instavibe"
export IMAGE_NAME="instavibe-webapp"
export IMAGE_PATH="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/${IMAGE_NAME}:${IMAGE_TAG}"
export SERVICE_NAME="instavibe"

echo "Building ${APP_FOLDER_NAME} webapp image..."
gcloud builds submit . \
 
--tag=${IMAGE_PATH} \
 
--project=${PROJECT_ID}

echo "Deploying ${SERVICE_NAME} to Cloud Run..."

gcloud run deploy ${SERVICE_NAME} \
 
--image=${IMAGE_PATH} \
 
--platform=managed \
 
--region=${REGION} \
 
--allow-unauthenticated \
 
--set-env-vars="SPANNER_INSTANCE_ID=${SPANNER_INSTANCE_ID}" \
 
--set-env-vars="SPANNER_DATABASE_ID=${SPANNER_DATABASE_ID}" \
 
--set-env-vars="APP_HOST=0.0.0.0" \
 
--set-env-vars="APP_PORT=8080" \
 
--set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
 
--set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
 
--set-env-vars="GOOGLE_MAPS_API_KEY=${GOOGLE_MAPS_API_KEY}" \
 
--set-env-vars="ORCHESTRATE_AGENT_ID=${ORCHESTRATE_AGENT_ID}" \
 
--project=${PROJECT_ID} \
 
--min-instances=1 \
 
--cpu=2 \
 
--memory=2Gi

Khi quá trình triển khai cuối cùng hoàn tất, hãy chuyển đến URL ứng dụng InstaVibe trong một thẻ trình duyệt khác.

Thử nghiệm trải nghiệm InstaVibe đầy đủ dựa trên AI

Tính năng "Trợ lý InstaVibe" hiện đã hoạt động, được cung cấp bởi hệ thống nhiều tác nhân do công cụ tác nhân Vertex AI điều phối và giao tiếp thông qua A2A.

12-02-new.png

Nhấp vào "InstaVibe Ally" rồi yêu cầu trợ lý này lên kế hoạch cho một sự kiện.

12-03-introvertally.png

Quan sát nhật ký hoạt động ở bên phải trong khi các tác nhân hoạt động (có thể mất 90 đến 120 giây). Khi kế hoạch xuất hiện, hãy xem lại kế hoạch đó rồi nhấp vào "Xác nhận kế hoạch này" để tiếp tục đăng.

12-04-confirm.png

Giờ đây, trình điều phối sẽ hướng dẫn Tác nhân nền tảng tạo bài đăng và sự kiện trong InstaVibe. 12-05-posting.png

Kiểm tra trang chủ InstaVibe để xem bài đăng và sự kiện mới. 12-06-instavibe.png

Trang sự kiện sẽ phản ánh thông tin chi tiết do nhân viên hỗ trợ tạo.

12-07-event.png

Phân tích hiệu suất bằng tính năng Theo dõi trên đám mây

Bạn có thể nhận thấy quá trình này mất một chút thời gian. Công cụ tác nhân Vertex AI tích hợp với Cloud Trace, cho phép chúng tôi phân tích độ trễ của hệ thống nhiều tác nhân.

Chuyển đến phần Dấu vết trong bảng điều khiển Google Cloud, chọn agent_run[orchestrate_agent] trong Span, bạn sẽ thấy một vài Span, hãy nhấp vào đó

12-08-trace.png

Trong thông tin chi tiết về dấu vết, bạn có thể xác định những phần nào mất nhiều thời gian hơn. Ví dụ: các lệnh gọi đến tác nhân của Trình lập kế hoạch có thể hiển thị độ trễ cao hơn do việc tìm kiếm cơ sở và tạo phức tạp. 12-09-plan.png

Tương tự, khi tạo bài đăng và sự kiện, bạn có thể thấy thời gian mà Trình điều phối xử lý dữ liệu và chuẩn bị lệnh gọi công cụ cho Tác nhân nền tảng. 12-10-post.png

Việc khám phá các dấu vết này giúp bạn hiểu rõ và tối ưu hoá hiệu suất của hệ thống tác nhân.

celebrate.png

Xin chúc mừng! Bạn đã xây dựng, triển khai và kiểm thử thành công một hệ thống AI đa tác nhân phức tạp bằng cách sử dụng ADK, A2A, MCP và các dịch vụ của Google Cloud. Bạn đã giải quyết việc điều phối tác nhân, sử dụng công cụ, quản lý trạng thái và triển khai trên đám mây, tạo ra một tính năng hoạt động dựa trên AI cho InstaVibe. Chúc mừng bạn đã hoàn thành hội thảo!

13. Dọn dẹp

Để tránh bị tính phí liên tục cho tài khoản Google Cloud, bạn cần xoá các tài nguyên mà chúng ta đã tạo trong lớp học này. Các lệnh sau đây sẽ giúp bạn xoá thực thể Spanner, dịch vụ Cloud Run, kho lưu trữ Artifact Registry, Khoá API, Công cụ tác nhân Vertex AI và các quyền IAM liên quan.

Quan trọng:

  • Đảm bảo bạn đang chạy các lệnh này trong cùng một dự án Google Cloud dùng cho hội thảo.
  • Nếu bạn đã đóng thiết bị đầu cuối Cloud Shell, một số biến môi trường như $PROJECT_ID, $SPANNER_INSTANCE_ID, v.v. có thể chưa được đặt. Bạn sẽ cần xuất lại các tệp này như đã làm trong quá trình thiết lập hội thảo hoặc thay thế các biến trong các lệnh bên dưới bằng giá trị thực tế của các biến đó.
  • Các lệnh này sẽ xoá vĩnh viễn tài nguyên của bạn. Kiểm tra kỹ trước khi chạy nếu bạn có dữ liệu quan trọng khác trong dự án này.

👉💻 Chạy các tập lệnh sau để dọn dẹp.

Đặt lại biến môi trường

. ~/instavibe-bootstrap/set_env.sh

Xoá công cụ nhân viên hỗ trợ:

cd ~/instavibe-bootstrap/utils
source ~/instavibe-bootstrap/env/bin/activate
export ORCHESTRATE_AGENT_ID=$(cat ~/instavibe-bootstrap/instavibe/temp_endpoint.txt)
echo "ORCHESTRATE_AGENT_ID set to: ${ORCHESTRATE_AGENT_ID}"
python remote_delete.py
deactivate
echo "Vertex AI Agent Engine deletion initiated."

Xoá Dịch vụ Cloud Run:

# InstaVibe Web Application
gcloud run services delete instavibe --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# MCP Tool Server
gcloud run services delete mcp-tool-server --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Planner Agent (A2A Server)
gcloud run services delete planner-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Platform MCP Client Agent (A2A Server)
gcloud run services delete platform-mcp-client --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

# Social Agent (A2A Server)
gcloud run services delete social-agent --platform=managed --region=${REGION} --project=${PROJECT_ID} --quiet

echo "Cloud Run services deletion initiated."

Dừng và xoá Vùng chứa Docker của A2A Inspector

docker rm --force a2a-inspector

Xoá thực thể Spanner:

echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."

Xoá kho lưu trữ Artifact Registry:

echo "Deleting Artifact Registry repository: ${REPO_NAME}..."
gcloud artifacts repositories delete ${REPO_NAME} --location=${REGION} --project=${PROJECT_ID} --quiet
echo "Artifact Registry repository deletion initiated."

Xoá vai trò khỏi Tài khoản dịch vụ:

echo "Removing roles from service account: $SERVICE_ACCOUNT_NAME in project $PROJECT_ID"

# Remove Project-level roles for default service account
gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/spanner.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/spanner.databaseUser"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/artifactregistry.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/cloudbuild.builds.editor"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/run.admin"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/iam.serviceAccountUser"

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

gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/logging.logWriter"

gcloud projects remove-iam-policy-binding $PROJECT_ID \
 
--member="serviceAccount:$SERVICE_ACCOUNT_NAME" \
 
--role="roles/logging.viewer"


echo "All specified roles have been removed."

Xoá tệp hội thảo trên máy:

echo "Removing local workshop directory ~/instavibe-bootstrap..."
rm -rf ~/instavibe-bootstrap
rm -rf ~/a2a-inspector
rm -f ~/mapkey.txt
rm -f ~/project_id.txt
echo "Local directory removed."

Dọn dẹp