1. Czego się nauczysz
Witamy! Wyruszamy dziś w dość ciekawą podróż. Zacznijmy od popularnej platformy społecznościowej InstaVibe. Chociaż ta funkcja jest przydatna, wiemy, że dla niektórych użytkowników planowanie aktywności grupowych może być uciążliwe. Wyobraź sobie, że próbujesz dowiedzieć się, co interesuje Twoich znajomych, a potem przeglądasz nieskończone opcje wydarzeń lub miejsc i wreszcie koordynujesz wszystko. To całkiem sporo. Właśnie w tym obszarze możemy wprowadzić AI, a konkretnie inteligentnych agentów, aby dokonać prawdziwej zmiany.
Chcemy stworzyć system, w którym te narzędzia będą wykonywać najtrudniejsze zadania, takie jak inteligentne „słuchanie” w celu zrozumienia preferencji użytkowników i ich znajomych, a następnie proaktywne sugerowanie fantastycznych, dopasowanych do nich aktywności. Naszym celem jest przekształcenie planowania społecznościowego w InstaVibe w coś bezproblemowego i przyjemnego. Aby zacząć tworzyć tych inteligentnych asystentów, musimy stworzyć solidne podstawy przy użyciu odpowiednich narzędzi.
Oto koncepcja, którą zobaczysz:
Podstawy korzystania z pakietu ADK od Google: opanuj podstawy tworzenia pierwszego inteligentnego agenta za pomocą pakietu Agent Development Kit (ADK) od Google. Poznaj najważniejsze komponenty, cykl życia agenta i dowiedz się, jak skutecznie korzystać z wbudowanych narzędzi platformy.
Rozszerzanie możliwości agentów za pomocą protokołu kontekstu modelu (MCP): dowiedz się, jak wyposażyć agentów w niestandardowe narzędzia i kontekst, aby mogli wykonywać specjalistyczne zadania i uzyskiwać dostęp do konkretnych informacji. Przedstaw koncepcję protokołu kontekstu modelu (MCP). Dowiesz się, jak skonfigurować serwer MCP, aby udostępniać ten kontekst.
Projektowanie interakcji i orkiestracji agentów: wyjdź poza pojedynczych agentów, aby zrozumieć orkiestrację agentów. Projektuj wzorce interakcji, od prostych sekwencyjnych przepływów pracy po złożone scenariusze obejmujące pętle, logikę warunkową i przetwarzanie równoległe. Wprowadź koncepcję podagentów w ramach ADK do zarządzania zadaniami modułowymi.
Tworzenie systemów wieloagentowych opartych na współpracy: dowiedz się, jak projektować systemy, w których wielu agentów współpracuje ze sobą, aby osiągać złożone cele. Poznaj i wdroż protokół komunikacji między agentami (A2A), który ustanawia standardowy sposób niezawodnego współdziałania rozproszonych agentów (potencjalnie działających na różnych maszynach lub w różnych usługach).
Wdrażanie agentów w Google Cloud: przenoszenie aplikacji agentów ze środowisk deweloperskich do chmury. Poznaj sprawdzone metody projektowania i wdrażania skalowalnych, niezawodnych systemów wieloagentowych w Google Cloud Platform (GCP). Poznaj zalety korzystania z usług GCP, takich jak Cloud Run, i odkryj możliwości najnowszego silnika agentów Google do hostowania agentów i zarządzania nimi.
2. Architektura
Planowanie treści w mediach społecznościowych oparte na AI dzięki InstaVibe
Co to jest monitorowanie treści w sieciach społecznościowych?
Social listening to proces monitorowania rozmów w internecie na platformach takich jak media społecznościowe, fora i serwisy informacyjne, aby dowiedzieć się, co ludzie mówią na dany temat, o danej marce lub branży. Dostarcza cennych informacji o opiniach publicznych, trendach i potrzebach użytkowników. Na tych warsztatach wykorzystamy tę koncepcję w systemie opartym na agentach.
Jesteś w zespole InstaVibe
Wyobraź sobie, że pracujesz w „InstaVibe”, odnoszącym sukcesy startupie z popularną platformą wydarzeń społecznościowych skierowaną do młodych dorosłych. Wszystko idzie dobrze, ale tak jak w przypadku wielu firm technologicznych Twój zespół odczuwa presję ze strony inwestorów, aby wprowadzać innowacje z wykorzystaniem AI. W swojej organizacji zauważasz też segment użytkowników, którzy nie angażują się tak bardzo jak inni – być może rzadziej inicjują działania grupowe lub uważają proces planowania za trudny. Dla Twojej firmy oznacza to mniejsze przywiązanie do platformy wśród tej ważnej grupy użytkowników.
Z badań Twojego zespołu wynika, że pomoc oparta na AI może znacznie poprawić wrażenia tych użytkowników. Chodzi o uproszczenie procesu planowania spotkań towarzyskich przez proaktywne sugerowanie odpowiednich aktywności na podstawie zainteresowań użytkownika i jego znajomych. Pytanie, które zadajesz sobie Ty i Twoi współpracownicy, brzmi: jak agenty AI mogą zautomatyzować czasochłonne zadania związane z odkrywaniem zainteresowań, badaniem aktywności i potencjalną wstępną koordynacją?
Rozwiązanie oparte na agentach (koncepcja prototypu)
Proponujesz opracowanie prototypu funkcji opartej na systemie z wieloma agentami. Oto schemat koncepcyjny:
- Agent profilowania społecznościowego: ten agent wykorzystuje techniki monitorowania mediów społecznościowych do analizowania połączeń i interakcji użytkownika oraz potencjalnie szerszych trendów publicznych związanych z jego preferencjami. Jego celem jest identyfikowanie wspólnych zainteresowań i odpowiednich cech aktywności (np. preferencji dotyczących cichszych spotkań lub konkretnych hobby).
- Agent planujący wydarzenia: korzystając z informacji uzyskanych od agenta profilowania społecznościowego, wyszukuje w zasobach online konkretne wydarzenia, miejsca lub pomysły, które są zgodne z określonymi kryteriami (np. lokalizacją, zainteresowaniami).
- Agent interakcji z platformą (korzystający z MCP): ten agent pobiera ostateczny plan od agenta planowania aktywności. Jego głównym zadaniem jest bezpośrednia interakcja z platformą InstaVibe przy użyciu wstępnie zdefiniowanego narzędzia MCP (Model Context Protocol). To narzędzie umożliwia agentowi przygotowanie propozycji wydarzenia i utworzenie posta z planem.
- Agent aranżujący: ten agent działa jako centralny koordynator. Otrzymuje ono początkowe żądanie użytkownika z platformy InstaVibe, rozumie ogólny cel (np. „zaplanuj wydarzenie dla mnie i moich znajomych”), a następnie przekazuje konkretne zadania odpowiednim wyspecjalizowanym agentom w logicznej kolejności. Zarządza przepływem informacji między agentami i zapewnia, że ostateczny wynik jest dostarczany z powrotem do użytkownika.
Kluczowe elementy architektury i technologie
Google Cloud Platform (GCP):
- Vertex AI:
- Modele Gemini: zapewniają dostęp do najnowocześniejszych dużych modeli językowych (LLM) od Google, takich jak Gemini, które odpowiadają za możliwości rozumowania i podejmowania decyzji naszych agentów.
- Vertex AI Agent Engine: zarządzana usługa służąca do wdrażania, hostowania i skalowania naszego agenta orkiestratora, która upraszcza wdrażanie w środowisku produkcyjnym i ukrywa złożoność infrastruktury.
- Cloud Run: platforma bezserwerowa do wdrażania aplikacji w kontenerach. Używamy go do:
- hostować główną aplikację internetową InstaVibe;
- Wdrażaj poszczególne agenty z obsługą A2A (Planer, Profilowanie społecznościowe, Interakcja z platformą) jako niezależne mikroserwisy.
- Uruchom serwer narzędzi MCP, udostępniając agentom wewnętrzne interfejsy API InstaVibe.
- Spanner: w pełni zarządzana, globalnie rozpowszechniona i silnie spójna relacyjna baza danych. W tym warsztacie wykorzystamy jego możliwości jako bazy danych wykresów, używając funkcji GRAPH DDL i zapytań do:
- Modelowanie i przechowywanie złożonych relacji społecznościowych (użytkownicy, znajomi, uczestnictwo w wydarzeniach, posty).
- Umożliwia to wydajne wykonywanie zapytań dotyczących tych relacji przez agentów profilowania społecznościowego.
- Artifact Registry: w pełni zarządzana usługa do przechowywania obrazów kontenerów, zarządzania nimi i zabezpieczania ich.
- Cloud Build: usługa, która uruchamia Twoje kompilacje w Google Cloud. Używamy go do automatycznego tworzenia obrazów kontenerów Dockera z kodu źródłowego agenta i aplikacji.
- Cloud Storage: używana przez usługi takie jak Cloud Build do przechowywania artefaktów kompilacji i przez Agent Engine na potrzeby operacyjne.
- Główne platformy i protokoły agentów:
- Pakiet Agent Development Kit (ADK) od Google: podstawowa platforma do:
- Określanie podstawowej logiki, zachowania i zestawów instrukcji dla poszczególnych inteligentnych agentów.
- zarządzanie cyklami życia, stanem i pamięcią agentów (krótkotrwały stan sesji i potencjalnie długotrwała wiedza);
- integracja narzędzi (takich jak wyszukiwarka Google lub narzędzia utworzone na zamówienie), z których agenci mogą korzystać, aby wchodzić w interakcje ze światem;
- Orkiestracja przepływów pracy z wieloma agentami, w tym sekwencyjne, pętlowe i równoległe wykonywanie subagentów.
- Protokół komunikacji między agentami (A2A): otwarty standard umożliwiający:
- Bezpośrednia, standardowa komunikacja i współpraca między różnymi agentami AI, nawet jeśli działają jako oddzielne usługi lub na różnych maszynach.
- Agenci mogą odkrywać swoje możliwości (za pomocą kart agentów) i delegować zadania. Jest to kluczowe, aby agent Orchestrator mógł wchodzić w interakcje ze specjalistycznymi agentami Planner, Social i Platform.
- Biblioteka A2A Python (a2a-python): konkretna biblioteka używana do sprawiania, że agenci ADK mówią protokołem A2A. Udostępnia komponenty po stronie serwera, które są potrzebne do:
- udostępniać naszych agentów jako serwery zgodne z A2A;
- Automatyczne wyświetlanie „Karty agenta” w celu ułatwienia odkrywania.
- Otrzymywanie przychodzących próśb o wykonanie zadań od innych agentów (np. Orchestratora) i zarządzanie nimi.
- Protokół kontekstu modelu (MCP): otwarty standard, który umożliwia agentom:
- Łączenie się z zewnętrznymi narzędziami, źródłami danych i systemami oraz korzystanie z nich w standardowy sposób.
- Nasz agent interakcji z platformą używa klienta MCP do komunikacji z serwerem MCP, który z kolei udostępnia narzędzia do interakcji z dotychczasowymi interfejsami API platformy InstaVibe.
- Pakiet Agent Development Kit (ADK) od Google: podstawowa platforma do:
- Narzędzia do debugowania:
- A2A Inspector: A2A Inspector to internetowe narzędzie do debugowania, które jest używane w trakcie tych warsztatów do łączenia się z agentami obsługującymi A2A, sprawdzania ich i interakcji z nimi. Nie jest to część ostatecznej architektury produkcyjnej, ale jest niezbędna w naszym procesie tworzenia. Zapewnia:
- Przeglądarka kart agenta: służy do pobierania i weryfikowania publicznych funkcji agenta.
- Interfejs czatu na żywo: umożliwia wysyłanie wiadomości bezpośrednio do wdrożonego agenta w celu natychmiastowego przetestowania.
- Konsola debugowania: umożliwia wyświetlanie nieprzetworzonych wiadomości JSON-RPC wymienianych między inspektorem a agentem.
- A2A Inspector: A2A Inspector to internetowe narzędzie do debugowania, które jest używane w trakcie tych warsztatów do łączenia się z agentami obsługującymi A2A, sprawdzania ich i interakcji z nimi. Nie jest to część ostatecznej architektury produkcyjnej, ale jest niezbędna w naszym procesie tworzenia. Zapewnia:
- Modele językowe (LLM): „mózg” systemu:
- Modele Gemini od Google: używamy w szczególności wersji takich jak gemini-2.0-flash. Te modele są wybierane ze względu na:
- Zaawansowane rozumowanie i wykonywanie instrukcji: zdolność do rozumienia złożonych promptów, wykonywania szczegółowych instrukcji i rozumowania w zakresie zadań sprawia, że modele te nadają się do podejmowania decyzji przez agentów.
- Korzystanie z narzędzi (wywoływanie funkcji): modele Gemini doskonale określają, kiedy i jak używać narzędzi udostępnianych za pomocą ADK, co umożliwia agentom zbieranie informacji lub wykonywanie działań.
- Wydajność (modele Flash): warianty „flash” zapewniają dobrą równowagę między wydajnością a opłacalnością, dzięki czemu nadają się do wielu zadań interaktywnych agentów, które wymagają szybkich odpowiedzi.
- Modele Gemini od Google: używamy w szczególności wersji takich jak gemini-2.0-flash. Te modele są wybierane ze względu na:
Potrzebujesz środków w Google Cloud?
3. Zanim zaczniesz
👉 U góry konsoli Google Cloud kliknij Aktywuj Cloud Shell (jest to ikona terminala u góry panelu Cloud Shell) .
👉Kliknij przycisk „Otwórz edytor” (wygląda jak otwarty folder z ołówkiem). W oknie otworzy się edytor kodu Cloud Shell. Po lewej stronie zobaczysz eksplorator plików.
👉 Na pasku stanu u dołu kliknij przycisk Zaloguj się w Cloud Code, jak pokazano na ilustracji. Autoryzuj wtyczkę zgodnie z instrukcjami. Jeśli na pasku stanu widzisz Cloud Code – brak projektu, wybierz tę opcję, a następnie w menu „Wybierz projekt Google Cloud” wybierz konkretny projekt Google Cloud z listy utworzonych projektów.
👉 Znajdź identyfikator projektu Google Cloud:
- Otwórz konsolę Google Cloud: https://console.cloud.google.com
- Wybierz projekt, którego chcesz użyć w tych warsztatach, z menu u góry strony.
- Identyfikator projektu jest wyświetlany na karcie Informacje o projekcie w panelu.
👉Otwórz terminal w IDE w chmurze.
👉💻 W terminalu sprawdź, czy użytkownik jest już uwierzytelniony i czy projekt jest ustawiony na identyfikator projektu, używając tego polecenia:
gcloud auth list
👉💻 Sklonuj instavibe-bootstrap
projekt z GitHuba:
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
Poznawanie struktury projektu
Zanim zaczniemy tworzyć, przyjrzyjmy się strukturze instavibe-bootstrap
projektu, który właśnie sklonowaliśmy. Dzięki temu będziesz wiedzieć, gdzie znaleźć i edytować pliki w trakcie warsztatów.
instavibe-bootstrap/
├── agents/
│ ├── orchestrate/
│ ├── planner/
│ ├── platform_mcp_client/
│ └── social/
├── instavibe/
│ ├── static/
│ └── templates/
├── tools/
│ └── instavibe/
├── utils/
├── init.sh
└── set_env.sh
Oto podział najważniejszych katalogów:
agents/
: To jest kluczowy element naszego systemu AI. Każdy podkatalog (planner/, social/ itp.) zawiera kod źródłowy konkretnego inteligentnego agenta.agent.py
: w folderze każdego agenta jest to główny plik, w którym znajduje się logika agenta.a2a_server.py
: ten plik zawiera agenta ADK z serwerem komunikacji między agentami (A2A).Dockerfile
: określa sposób tworzenia obrazu kontenera na potrzeby wdrożenia agenta w Cloud Run lub Agent Engine.
instavibe/
: ten katalog zawiera cały kod źródłowy aplikacji internetowej InstaVibe.tools/
: ten katalog służy do tworzenia narzędzi zewnętrznych, z których mogą korzystać nasi agenci.instavibe/
zawiera serwer protokołu kontekstu modelu (MCP).
Ta modułowa struktura oddziela aplikację internetową od różnych komponentów AI, co ułatwia zarządzanie całym systemem, jego testowanie i wdrażanie.
👉💻 Uruchom skrypt inicjujący:
Skrypt poprosi Cię o wpisanie identyfikatora projektu Google Cloud.
Gdy skrypt init.sh
wyświetli odpowiedni komunikat, wpisz identyfikator projektu Google Cloud znaleziony w ostatnim kroku:
cd ~/instavibe-bootstrap
./init.sh
👉💻 Ustaw wymagany identyfikator projektu:
gcloud config set project $(cat ~/project_id.txt) --quiet
👉💻 Aby włączyć wymagane interfejsy Google Cloud API, uruchom to polecenie:
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
👉💻 Ustaw wszystkie potrzebne zmienne środowiskowe:
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"
Konfigurowanie uprawnień
👉💻 Przyznaj uprawnienia. W terminalu uruchom :
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"
👉 Sprawdź wynik w konsoli IAM
👉💻 Aby utworzyć repozytorium Artifact Registry, uruchom w terminalu te polecenia. Wszystkie obrazy Dockera dla naszych agentów, serwera MCP i aplikacji InstaVibe są przechowywane w tym miejscu przed wdrożeniem w Cloud Run lub 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"
Konfigurowanie platformy Map do obsługi kluczy interfejsu API
Aby korzystać z usług Map Google w aplikacji InstaVibe, musisz utworzyć klucz interfejsu API i odpowiednio go ograniczyć.
👉 W nowej karcie otwórz Interfejsy API i usługi > Dane logowania. U góry strony „Dane logowania” kliknij przycisk + UTWÓRZ DANE LOGOWANIA. W menu wybierz Klucz interfejsu API.
👉 Pojawi się okno dialogowe z nowo utworzonym kluczem interfejsu API. Będzie on potrzebny później do skonfigurowania aplikacji.
👉 W oknie „Utworzono klucz API” kliknij ZAMKNIJ.
👉 Wyświetli się nowy klucz interfejsu API (np. „Klucz interfejsu API 1”). Kliknij 3 kropki po prawej stronie i wybierz Edytuj klucz API, aby otworzyć stronę „Ogranicz i zmień nazwę klucza API”.
👉 W polu Nazwa u góry zmień nazwę domyślną na: Klucz interfejsu API Google Maps Platform (🚨🚨WAŻNE🚨🚨 Użyj tej nazwy!).
Maps Platform API Key
👉 W sekcji „Ograniczenia aplikacji” sprawdź, czy wybrano Brak.
👉 W sekcji „Ograniczenia interfejsów API” kliknij opcję „Ogranicz klucz”.
👉 Kliknij menu Wybierz interfejsy API. W wyświetlonym polu wyszukiwania wpisz Maps JavaScript API
i wybierz go z listy.
👉 Kliknij OK.
👉 Kliknij przycisk ZAPISZ u dołu strony.
Klucz interfejsu API o nazwie „Klucz interfejsu API Google Maps Platform” został utworzony. Ograniczyliśmy go tak, aby można było go używać tylko w przypadku interfejsu „Maps JavaScript API”, i sprawdziliśmy, czy interfejs API jest włączony w projekcie.
4. Konfigurowanie bazy danych grafów
Zanim będziemy mogli stworzyć inteligentnych agentów, musimy znaleźć sposób na przechowywanie i rozumienie złożonych połączeń w naszej sieci społecznościowej InstaVibe. W takiej sytuacji przydaje się grafowa baza danych. W przeciwieństwie do tradycyjnych relacyjnych baz danych, które przechowują dane w tabelach z wierszami i kolumnami, baza danych grafów jest specjalnie zaprojektowana do reprezentowania danych i wykonywania na nich zapytań w postaci węzłów (np. osób, wydarzeń lub postów) oraz relacji (krawędzi), które je łączą (np. przyjaźni, uczestnictwa w wydarzeniu lub wzmianek). Ta struktura jest niezwykle przydatna w przypadku aplikacji mediów społecznościowych, ponieważ odzwierciedla sposób, w jaki są one zorganizowane w rzeczywistości, co ułatwia odkrywanie powiązań między różnymi podmiotami.
Implementujemy tę bazę danych grafów za pomocą Google Cloud Spanner. Spanner jest znany przede wszystkim jako globalnie rozproszona, silnie spójna relacyjna baza danych, ale umożliwia też definiowanie struktur grafów i wykonywanie na nich zapytań bezpośrednio na podstawie naszych tabel relacyjnych.
Dzięki temu mamy połączone zalety skalowalności, spójności transakcyjnej i znanego interfejsu SQL usługi Spanner z możliwością wyrażania zapytań grafowych do analizowania złożonych dynamik społecznych, które są kluczowe dla naszych funkcji opartych na AI.
👉💻 W terminalu IDE Cloud Shell. wdrożyć niezbędną infrastrukturę w Google Cloud; Zaczniemy od utworzenia instancji Spannera, która będzie działać jako dedykowany kontener dla naszych baz danych. Gdy instancja będzie gotowa, utworzymy w niej rzeczywistą bazę danych Spanner, w której będą przechowywane wszystkie nasze tabele i dane wykresu 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
👉💻 Przyznawanie kontu usługi Spanner uprawnień do odczytu i zapisu
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}
👉💻 Teraz. Skonfigurujemy środowisko wirtualne Pythona, zainstalujemy wymagane pakiety Pythona, a następnie skonfigurujemy schemat bazy danych grafów w Spannerze, wczytamy do niego dane początkowe i uruchomimy skrypt 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
👉 Na nowej karcie przeglądarki otwórz konsolę Google Cloud i kliknij Spanner. Powinna się wyświetlić lista instancji Spanner. Kliknij instavibe-graph-instance
. 👉 Na stronie przeglądu instancji zobaczysz listę baz danych w tej instancji. Kliknij
graphdb
.
👉 W panelu nawigacyjnym po lewej stronie bazy danych kliknij Spanner Studio .
👉 W edytorze zapytań (karta Bez tytułu) wklej to zapytanie Graph SQL: To zapytanie znajdzie wszystkie węzły Person i ich bezpośrednie relacje Friendship z innymi węzłami Person. Następnie kliknij URUCHOM, aby zobaczyć wynik.
Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths
👉 W tym samym edytorze zapytań zastąp poprzedni język DDL, aby znaleźć osoby, które uczestniczyły w tym samym wydarzeniu, co oznacza pośrednie połączenie przez wspólną aktywność.
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
👉 To zapytanie bada inny rodzaj połączenia, w którym osoby wymienione w postach napisanych przez znajomych konkretnej osoby. Uruchom to zapytanie w edytorze zapytań.
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
Te zapytania to tylko niewielki przykład możliwości wykorzystania Spannera jako bazy danych grafów w naszej aplikacji InstaVibe. Modelując nasze dane społecznościowe jako połączony graf, umożliwiamy zaawansowaną analizę relacji i aktywności, która będzie miała fundamentalne znaczenie dla naszych agentów AI w zakresie zrozumienia kontekstu użytkownika, odkrywania jego zainteresowań i ostatecznie zapewniania inteligentnej pomocy w planowaniu działań społecznościowych.
Po utworzeniu i przetestowaniu podstawowej struktury danych możemy przejść do istniejącej aplikacji InstaVibe, z której będą korzystać nasi pracownicy obsługi klienta.
5. Bieżący stan InstaVibe
Aby dowiedzieć się, gdzie będą przydatni nasi agenci AI, musimy najpierw wdrożyć i uruchomić istniejącą aplikację internetową InstaVibe. Ta aplikacja udostępnia interfejs użytkownika i podstawowe funkcje, które łączą się z bazą danych grafu Spanner, którą już skonfigurowaliśmy.
Aplikacja InstaVibe korzysta z Map Google, aby wizualnie wyświetlać lokalizacje wydarzeń na stronach z informacjami o nich. Aby włączyć tę funkcję, aplikacja potrzebuje utworzonego wcześniej klucza interfejsu API. Poniższy skrypt pobierze rzeczywisty ciąg znaków klucza za pomocą przypisanej przez nas wyświetlanej nazwy („Klucz interfejsu Google Maps Platform API”).
👉💻 Wróć do środowiska IDE Cloud Shell. Uruchom poniższy skrypt. Następnie dokładnie sprawdź dane wyjściowe, aby upewnić się, że wyświetlony klucz GOOGLE_MAPS_API_KEY jest zgodny z kluczem utworzonym i skopiowanym wcześniej z konsoli Google Cloud.
. ~/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}"
👉💻 Teraz skompilujmy obraz kontenera dla aplikacji internetowej InstaVibe i prześlijmy go do repozytorium Artifact Registry.
. ~/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}
👉💻 Wdróż nową wersję obrazu aplikacji internetowej InstaVibe w 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
Po pomyślnym wdrożeniu w logach Cloud Run powinien się wyświetlić publiczny adres URL uruchomionej aplikacji InstaVibe.
Ten adres URL możesz też znaleźć w sekcji Cloud Run w konsoli Google Cloud, wybierając usługę instavibe.
Otwórz teraz ten adres URL w przeglądarce, aby zapoznać się z podstawową platformą InstaVibe. Wyświetl posty, wydarzenia i połączenia użytkowników obsługiwane przez skonfigurowaną przez nas bazę danych grafów.
Aplikacja docelowa jest już uruchomiona, więc zacznijmy tworzyć pierwszego inteligentnego agenta, który zwiększy jej możliwości.
6. Podstawowy agent,organizator wydarzeń z ADK
Platforma ADK
Wprowadzenie do platformy ADK od Google Po utworzeniu podstawy (aplikacji InstaVibe i bazy danych) możemy zacząć tworzyć pierwszego inteligentnego agenta za pomocą zestawu Agent Development Kit (ADK) od Google.
Pakiet Agent Development Kit (ADK) to elastyczna i modułowa platforma zaprojektowana specjalnie do tworzenia i wdrażania agentów AI. Jego zasada projektowania polega na tym, aby tworzenie agentów przypominało tradycyjne tworzenie oprogramowania. Ma to znacznie ułatwić programistom tworzenie, wdrażanie i orkiestrowanie architektur opartych na agentach, które mogą obsługiwać wszystko – od prostych zadań jednofunkcyjnych po złożone przepływy pracy z wieloma agentami.
ADK opiera się na koncepcji Agent
, która obejmuje instrukcje, konfigurację (np.wybrany model językowy, np. Gemini) i zestaw Tools
, których może używać do wykonywania działań lub zbierania informacji.
Pierwszym agentem będzie „Planista wydarzeń”. Jego głównym celem jest przyjmowanie próśb użytkowników o spotkania towarzyskie (z określeniem lokalizacji, dat i zainteresowań) oraz generowanie kreatywnych, dostosowanych do potrzeb sugestii. Aby mieć pewność, że sugestie są trafne i oparte na aktualnych informacjach (np. o konkretnych wydarzeniach, które mają miejsce w dany weekend), wykorzystamy jedno z wbudowanych narzędzi ADK: wyszukiwarkę Google. Dzięki temu agent może opierać swoje odpowiedzi na wynikach wyszukiwania w internecie w czasie rzeczywistym, pobierając najnowsze informacje o miejscach, wydarzeniach i aktywnościach pasujących do kryteriów użytkownika.
👉📝 Wróć do środowiska IDE Cloud Shell i w ~/instavibe-bootstrap/agents/planner/agent.py
dodaj ten prompt i instrukcję, aby utworzyć agenta.
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]
)
To nasz pierwszy zdefiniowany agent. Jedną z największych zalet ADK jest intuicyjność i przydatne narzędzia, które udostępnia. Szczególnie przydatny jest interfejs ADK Dev UI, który umożliwia interaktywne testowanie agenta i wyświetlanie jego odpowiedzi w czasie rzeczywistym.
👉💻 Zaczynamy. Te polecenia uruchomią interfejs 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
Po uruchomieniu poleceń w terminalu powinny pojawić się dane wyjściowe wskazujące, że serwer WWW ADK został uruchomiony, np. takie:
+-----------------------------------------------------------------------------+
| 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)
👉 Następnie, aby uzyskać dostęp do interfejsu ADK Dev UI w przeglądarce:
Na pasku narzędzi Cloud Shell (zwykle w prawym górnym rogu) kliknij ikonę Podgląd w przeglądarce (często wygląda jak oko lub kwadrat ze strzałką) i wybierz Zmień port. W wyskakującym okienku ustaw port na 8000 i kliknij „Zmień i wyświetl podgląd”. Cloud Shell otworzy nową kartę lub nowe okno przeglądarki z interfejsem ADK Dev.
Gdy interfejs ADK Dev UI otworzy się w przeglądarce: w menu u góry po prawej stronie interfejsu wybierz planner jako agenta, z którym chcesz się komunikować. Teraz w oknie czatu po prawej stronie spróbuj zlecić agentowi zadanie. Możesz na przykład porozmawiać z pracownikiem obsługi klienta:
Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime
Zaproponuj datę (Twoja preferencja)
July 12 2025
Powinien on przetworzyć Twoją prośbę i przedstawić plan na podstawie wyników wyszukiwania w Google.
Interakcja z agentem to jedno, ale jak możemy mieć pewność, że działa on zgodnie z oczekiwaniami, zwłaszcza gdy wprowadzamy zmiany?
Tradycyjne metody testowania oprogramowania często nie sprawdzają się w przypadku agentów AI ze względu na ich generatywny i niedeterministyczny charakter. Aby przejść od ciekawej wersji demonstracyjnej do niezawodnego agenta produkcyjnego, kluczowa jest solidna strategia oceny. W przeciwieństwie do zwykłego sprawdzania końcowych wyników modelu generatywnego ocena agenta często obejmuje ocenę procesu podejmowania decyzji i zdolności do prawidłowego korzystania z narzędzi lub wykonywania instrukcji w różnych scenariuszach. ADK udostępnia funkcje, które w tym pomagają.
👉 W interfejsie ADK Dev UI kliknij kartę „Eval” w menu po lewej stronie. Powinien wyświetlić się wstępnie załadowany plik testowy o nazwie plan_eval
. Ten plik zawiera wstępnie zdefiniowane dane wejściowe i kryteria testowania naszego agenta planującego.
👉 Wybierz scenariusz, np. „boston”, i kliknij przycisk Run Evaluation (Przeprowadź ocenę). W wyskakującym okienku obniż wynik dopasowania do 0, 3 i kliknij Start.
Spowoduje to wykonanie agenta z danymi wejściowymi testu i sprawdzenie, czy jego dane wyjściowe spełniają określone oczekiwania. Umożliwia to systematyczne testowanie skuteczności agenta.
👉 Sprawdźmy teraz, co się stanie w przypadku bardziej rygorystycznego progu. Wybierz scenariusz „nyc” i ponownie kliknij Uruchom ocenę. Tym razem pozostaw domyślną wartość wyniku dopasowania (Wynik dopasowania odpowiedzi: 0, 7) i kliknij Rozpocznij. Wynik to Fail (Niepowodzenie). Jest to oczekiwane, ponieważ wygenerowana przez agenta kreacja nie pasuje idealnie do zdefiniowanej wcześniej „złotej” odpowiedzi.
👉 Aby dowiedzieć się, dlaczego się nie powiodło, kliknij ikonę błędu w wierszu „nyc”. W interfejsie wyświetlane jest teraz porównanie obok siebie rzeczywistej odpowiedzi agenta i oczekiwanej odpowiedzi z przypadku testowego. Ten widok jest niezbędny do debugowania, ponieważ pozwala dokładnie zobaczyć, gdzie dane wyjściowe agenta odbiegają od oczekiwanych, i odpowiednio dostosować instrukcje.
Gdy skończysz przeglądać interfejs i ocenę, wróć do terminala edytora Cloud Shell i naciśnij Ctrl+C
, aby zatrzymać interfejs ADK Dev.
Dane wyjściowe w postaci tekstu w dowolnym formacie to dobry początek, ale w przypadku aplikacji takich jak InstaVibe, które mają łatwo korzystać z sugestii agenta, znacznie bardziej praktyczne są dane strukturalne (np. JSON). Zmodyfikujmy agenta, aby zwracał swój plan w spójnym formacie JSON.
👉📝 W ~/instavibe-bootstrap/agents/planner/agent.py
znajdź wiersz, który obecnie zawiera RETURN PLAN in MARKDOWN FORMAT
w ciągu instrukcji agenta. Zastąp tę linię poniższą szczegółową strukturą JSON:
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
]
}
Instrukcje agenta zostały zaktualizowane, aby wyraźnie prosić o dane wyjściowe w formacie JSON. Sprawdźmy teraz, czy zmiana została wprowadzona.
👉💻 Uruchom ponownie interfejs deweloperski ADK za pomocą tego samego polecenia co wcześniej:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
adk web
Odśwież kartę, jeśli jest już otwarta. Możesz też wykonać te same czynności co wcześniej, aby otworzyć interfejs ADK Dev w przeglądarce (za pomocą podglądu w przeglądarce Cloud Shell na porcie 8000). Po załadowaniu interfejsu upewnij się, że wybrany jest agent planujący.
👉 Tym razem spróbujmy z innym żądaniem. W oknie czatu wpisz:
Plan an event Boston this weekend with art and coffee
Uważnie przeanalizuj odpowiedź agenta. Zamiast odpowiedzi w formie czystego tekstu zobaczysz teraz odpowiedź sformatowaną ściśle jako obiekt JSON, który pasuje do struktury zdefiniowanej w instrukcjach (zawierającej fun_plans, plan_description, locations_and_activities itp.). Potwierdza to, że agent może teraz generować dane strukturalne odpowiednie do programowego wykorzystania przez naszą aplikację InstaVibe.
Po potwierdzeniu danych wyjściowych JSON wróć do terminala Cloud Shell i naciśnij Ctrl+C
, aby zatrzymać interfejs ADK Dev UI.
Komponenty ADK
Interfejs ADK Dev UI świetnie sprawdza się w przypadku testów interaktywnych, ale często musimy uruchamiać nasze agenty programowo, np. w ramach większej aplikacji lub usługi backendu. Aby zrozumieć, jak to działa, przyjrzyjmy się niektórym podstawowym pojęciom związanym z ADK, które dotyczą zarządzania środowiskiem wykonawczym i kontekstem.
Sensowne rozmowy wieloetapowe wymagają od agentów zrozumienia kontekstu – przypominania sobie tego, co zostało powiedziane i zrobione, aby zachować ciągłość. ADK udostępnia ustrukturyzowane sposoby zarządzania tym kontekstem za pomocą sesji, stanu i pamięci:
- Sesja: gdy użytkownik zaczyna wchodzić w interakcję z agentem, tworzona jest sesja. Pomyśl o nim jako o kontenerze na pojedynczy, konkretny wątek czatu. Zawiera on unikalny identyfikator, historię interakcji (zdarzenia), bieżące dane robocze (stan) i metadane, takie jak czas ostatniej aktualizacji.
- Stan: to krótkotrwała pamięć robocza agenta w ramach jednej sesji. Jest to modyfikowalny słownik, w którym agent może przechowywać tymczasowe informacje potrzebne do wykonania bieżącego zadania (np. zebrane dotychczas preferencje użytkownika, wyniki pośrednie wywołań narzędzi).
- Pamięć: określa potencjał agenta do długotrwałego zapamiętywania informacji w różnych sesjach lub dostęp do zewnętrznych baz wiedzy. Sesja i stan obsługują bieżącą rozmowę, a pamięć (często zarządzana przez usługę pamięci) umożliwia agentowi pobieranie informacji z poprzednich interakcji lub źródeł danych strukturalnych, co zapewnia mu szerszy kontekst wiedzy. (Uwaga: nasz prosty klient korzysta z usług w pamięci, aby uprościć działanie. Oznacza to, że pamięć i stan są zachowywane tylko podczas działania skryptu).
- Zdarzenie: każda interakcja w ramach sesji (wiadomość użytkownika, odpowiedź agenta, prośba o użycie narzędzia, wynik działania narzędzia, zmiana stanu, błąd) jest rejestrowana jako niezmienne zdarzenie. W ten sposób powstaje dziennik chronologiczny, czyli transkrypcja i historia działań w rozmowie.
Jak więc nimi zarządzać, gdy agent jest uruchomiony? To zadanie aplikacji uruchamiającej.
- Runner: Runner to podstawowy mechanizm wykonawczy udostępniany przez ADK. Definiujesz agenta i narzędzia, których używa, a Runner koordynuje proces realizacji żądania użytkownika. Zarządza sesją, obsługuje przepływ zdarzeń, aktualizuje stan, wywołuje bazowy model językowy, koordynuje wywołania narzędzi i może wchodzić w interakcje z usługą MemoryService. Można to porównać do dyrygenta, który dba o to, aby wszystkie części działały prawidłowo.
Możemy użyć narzędzia Runner, aby uruchomić agenta jako samodzielną aplikację w języku Python, całkowicie niezależną od interfejsu programisty.
Utwórzmy prosty skrypt klienta, aby wywołać agenta planującego programowo.
👉📝 W pliku ~/instavibe-bootstrap/agents/planner/planner_client.py
dodaj ten kod Pythona pod istniejącymi instrukcjami importu. W pliku planner_client.py
pod instrukcjami importu dodaj ten kod:
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}")
Ten kod konfiguruje usługi w pamięci do zarządzania sesjami i artefaktami (w tym przykładzie uprościliśmy to), tworzy sesję, definiuje zapytanie użytkownika, konfiguruje Runner z naszym agentem, a następnie uruchamia agenta asynchronicznie, drukując każde zdarzenie wygenerowane podczas wykonywania.
👉💻 Teraz uruchom ten skrypt klienta z terminala:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
python -m planner.planner_client
👀 Sprawdź dane wyjściowe. Zamiast tylko końcowego planu JSON zobaczysz szczegółową strukturę każdego obiektu Event wygenerowanego podczas wykonywania przez agenta. Obejmuje to początkowe zdarzenie wiadomości użytkownika, potencjalne zdarzenia związane z wywołaniami narzędzi (np. wyszukiwarki Google) i wreszcie zdarzenie odpowiedzi modelu zawierające plan w formacie JSON. Ten szczegółowy strumień zdarzeń jest bardzo przydatny do debugowania i poznawania krok po kroku procesu przetwarzania w środowisku wykonawczym 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
Jeśli skrypt działa w sposób ciągły lub zawiesza się, może być konieczne ręczne zatrzymanie go przez naciśnięcie Ctrl+C
.
7. Agent interakcji z platformą – interakcja z serwerem MCP
ADK pomaga w strukturyzacji naszych agentów, ale często muszą oni wchodzić w interakcje z systemami zewnętrznymi lub interfejsami API, aby wykonywać działania w rzeczywistym świecie.
Model Context Protocol (MCP)
Protokół kontekstu modelu (MCP) to otwarty standard, który ma ujednolicić sposób, w jaki aplikacje AI, takie jak agenci, łączą się z zewnętrznymi źródłami danych, narzędziami i systemami. Ma ono rozwiązać problem konieczności tworzenia niestandardowych integracji dla każdej kombinacji aplikacji AI i źródła danych, zapewniając uniwersalny interfejs. MCP korzysta z architektury klient-serwer, w której klienci MCP znajdujący się w aplikacjach AI (hostach) zarządzają połączeniami z serwerami MCP. Są to programy zewnętrzne, które udostępniają określone funkcje, takie jak dostęp do danych lokalnych, interakcja z usługami zdalnymi za pomocą interfejsów API czy dostarczanie gotowych promptów. Dzięki temu modele AI mogą uzyskiwać dostęp do aktualnych informacji i wykonywać zadania wykraczające poza ich pierwotne szkolenie. Ta struktura umożliwia modelom AI odkrywanie zewnętrznych funkcji i interakcję z nimi w standaryzowany sposób, co upraszcza integrację i zwiększa jej skalowalność.
Tworzenie i wdrażanie serwera MCP InstaVibe
Nasi agenci będą musieli wchodzić w interakcje z samą platformą InstaVibe, a konkretnie tworzyć posty i rejestrować zdarzenia za pomocą dostępnych interfejsów API platformy. Aplikacja InstaVibe udostępnia już te funkcje za pomocą standardowych punktów końcowych HTTP:
Enpoint | URL | Metoda HTTP | Opis |
Utwórz posta | api/posts | POST | Punkt końcowy interfejsu API do dodawania nowego posta. Oczekiwana treść w formacie JSON: |
Utwórz zdarzenie | api/events | POST | Punkt końcowy API do dodawania nowego wydarzenia i jego uczestników (uproszczony schemat). |
Aby udostępnić te funkcje naszym agentom za pomocą MCP, musimy najpierw utworzyć proste funkcje w Pythonie, które będą działać jako otoczki wywołań interfejsu API. Funkcje te będą obsługiwać logikę żądania HTTP.
👉 Najpierw zaimplementujmy funkcję otoki do tworzenia posta. Otwórz plik ~/instavibe-bootstrap/tools/instavibe/instavibe.py
i zastąp komentarz #REPLACE ME CREATE POST
tym kodem w Pythonie:
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
👉📝 Następnie utworzymy funkcję otoki dla interfejsu API do tworzenia zdarzeń. W tym samym pliku ~/instavibe-bootstrap/tools/instavibe/instavibe.py
zastąp komentarz #REPLACE ME CREATE EVENTS
tym kodem:
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
Jak widać, te funkcje są prostymi otoczkami istniejących interfejsów InstaVibe API. Ten wzorzec jest przydatny, jeśli masz już interfejsy API do swoich usług. Możesz łatwo udostępnić ich funkcje jako narzędzia dla agentów, tworząc takie otoczki.
Implementacja serwera MCP
Teraz, gdy mamy już funkcje Pythona, które wykonują działania (wywołują interfejsy InstaVibe API), musimy utworzyć komponent serwera MCP. Ten serwer udostępni te funkcje jako „narzędzia” zgodnie ze standardem MCP, co umożliwi klientom MCP (np. naszym agentom) ich wykrywanie i wywoływanie.
Serwer MCP zwykle implementuje 2 kluczowe funkcje:
- list_tools: umożliwia klientowi wykrywanie dostępnych narzędzi na serwerze, dostarczając metadane, takie jak nazwy, opisy i wymagane parametry, często zdefiniowane za pomocą schematu JSON.
- call_tool: obsługuje wykonywanie konkretnego narzędzia, o które prosi klient, otrzymując nazwę i argumenty narzędzia oraz wykonując odpowiednie działanie, np. w naszym przypadku interakcję z interfejsem API.
Serwery MCP służą do zapewniania modelom AI dostępu do rzeczywistych danych i działań, co umożliwia wykonywanie takich zadań jak wysyłanie e-maili, tworzenie zadań w systemach zarządzania projektami, przeszukiwanie baz danych czy interakcje z różnymi usługami internetowymi i oprogramowaniem. Początkowe wdrożenia często koncentrowały się na serwerach lokalnych komunikujących się za pomocą standardowego wejścia/wyjścia (stdio), co upraszczało działanie, szczególnie w środowiskach deweloperskich lub „studyjnych”. Jednak w przypadku szerszego wdrożenia i zastosowań w przedsiębiorstwach bardziej sensowne jest przejście na serwery zdalne korzystające z protokołów takich jak HTTP z wydarzeniami wysyłanymi przez serwer (SSE).
Architektura zdalna, mimo dodatkowej warstwy komunikacji sieciowej, oferuje znaczące zalety: umożliwia wielu klientom AI współdzielenie dostępu do jednego serwera, centralizuje zarządzanie narzędziami i ich aktualizacje, zwiększa bezpieczeństwo, ponieważ dane wrażliwe i klucze API są przechowywane po stronie serwera, a nie rozproszone na wielu potencjalnych maszynach klienckich, oraz oddziela model AI od specyfiki integracji z systemem zewnętrznym, dzięki czemu cały ekosystem jest bardziej skalowalny, bezpieczny i łatwiejszy w utrzymaniu niż w przypadku, gdy każda instancja AI musi zarządzać własnymi bezpośrednimi integracjami.
Nasz serwer MCP wdrożymy przy użyciu protokołu HTTP i zdarzeń wysyłanych przez serwer (SSE) do komunikacji, co dobrze sprawdza się w przypadku potencjalnie długotrwałych wykonań narzędzi i scenariuszy w firmach.
👉📝 Najpierw wdróżmy punkt końcowy list_tools. Otwórz plik ~/instavibe-bootstrap/tools/instavibe/mcp_server.py
i zastąp komentarz #REPLACE ME - LIST TOOLS
tym kodem: :
@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]
Ta funkcja definiuje narzędzia (create_event, create_post) i informuje o nich łączących się klientów.
👉📝 Następnie wdróż punkt końcowy call_tool
, który obsługuje rzeczywiste żądania wykonania od klientów. W tym samym ~/instavibe-bootstrap/tools/instavibe/mcp_server.py
pliku zastąp #REPLACE ME - CALL TOOLS
komentarz tym kodem.
@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)]
Ta funkcja otrzymuje nazwę narzędzia i argumenty, znajduje odpowiednią zdefiniowaną wcześniej funkcję opakowującą w Pythonie, wykonuje ją i zwraca wynik.
👉💻 Po zdefiniowaniu logiki serwera MCP musimy spakować ją jako kontener. W terminalu uruchom ten skrypt, aby utworzyć obraz Dockera za pomocą 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}
👉💻 Wdróż obraz jako usługę w 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
👉💻 Po pomyślnym wdrożeniu serwer MCP będzie działać i będzie dostępny pod publicznym adresem URL. Musimy zarejestrować ten adres URL, aby nasz agent (działający jako klient MCP) wiedział, z czym się połączyć.
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
Usługa mcp-tool-server powinna być teraz widoczna jako „Uruchomiona” w sekcji Cloud Run w konsoli Google Cloud.
Po wdrożeniu serwera MCP i zarejestrowaniu jego adresu URL możemy wdrożyć agenta, który będzie działać jako klient MCP i korzystać z narzędzi udostępnianych przez ten serwer.
8. Agent interakcji z platformą (korzystający z MCP)
Klient MCP to komponent znajdujący się w aplikacji lub agencie AI, który pełni funkcję interfejsu między modelem AI a co najmniej jednym serwerem MCP. W naszym wdrożeniu ten klient będzie zintegrowany bezpośrednio z naszym agentem. Główną funkcją tego klienta jest komunikacja z serwerami MCP w celu wykrywania dostępnych narzędzi za pomocą funkcji list_tools
, a następnie żądania wykonania określonych narzędzi za pomocą funkcji call_tool
, przekazując niezbędne argumenty dostarczone przez model AI lub agenta koordynującego wywołanie.
Teraz utworzymy agenta, który będzie działać jako klient MCP. Ten agent, działający w ramach ADK, będzie odpowiedzialny za komunikację z mcp-tool-server
, które właśnie wdrożyliśmy.
👉 Najpierw musimy zmodyfikować definicję agenta, aby dynamicznie pobierać narzędzia z uruchomionego serwera MCP. W polu agents/platform_mcp_client/agent.py
zamień #REPLACE ME - FETCH TOOLS
na:
"""Gets tools from the File System MCP Server."""
tools = MCPToolset(
connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
)
Ten kod używa metody MCPToolset.from_server do połączenia się z adresem MCP_SERVER_URL (który wcześniej ustawiliśmy jako zmienną środowiskową) i pobrania listy dostępnych narzędzi.
Następnie musimy poinformować definicję agenta ADK, aby faktycznie korzystała z tych narzędzi pobieranych dynamicznie.
👉 W pliku agents/platform_mcp_client/agent.py
zastąp #REPLACE ME - SET TOOLs
tymi elementami:
tools=[tools],
👉💻 Teraz przetestujmy tego agenta lokalnie za pomocą interfejsu ADK Dev UI, aby sprawdzić, czy może on prawidłowo połączyć się z serwerem MCP i używać narzędzi do interakcji z naszą działającą aplikacją InstaVibe.
. ~/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
Ponownie otwórz interfejs programisty ADK w przeglądarce (korzystając z podglądu w przeglądarce Cloud Shell na porcie 8000). Tym razem w menu w prawym górnym rogu wybierz platform_mcp_client
agenta.
Przetestujmy narzędzie create_post. W oknie czatu wpisz to żądanie:
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
Agent powinien przetworzyć tę informację, określić potrzebę użycia narzędzia create_post, komunikować się z serwerem MCP, który z kolei wywołuje interfejs InstaVibe API.
👉 Krok weryfikacji: po potwierdzeniu działania przez agenta otwórz kartę, na której działa aplikacja InstaVibe (lub odśwież ją). W głównym feedzie powinien się pojawić nowy post od „Julii”.
👉💻 W razie potrzeby uruchom ten skrypt w osobnym terminalu, aby uzyskać link do Instavibe:
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe
👉📝 Przetestujmy teraz narzędzie create_event. W oknie czatu wpisz to wielowierszowe żądanie:
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],
}
Agent powinien ponownie użyć odpowiedniego narzędzia na serwerze MCP. Na karcie Zdarzenia możesz kliknąć poszczególne zdarzenia, aby zobaczyć szczegółowe śledzenie wykonania krok po kroku.
👉 Krok weryfikacji: wróć do uruchomionej aplikacji InstaVibe i otwórz sekcję „Wydarzenia” (lub podobną). Powinno być teraz widoczne nowo utworzone wydarzenie „Mexico City Culinary & Art Day”.
Pokazuje to, jak MCP umożliwia agentowi korzystanie z narzędzi zewnętrznych (w tym przypadku interfejsów API InstaVibe) w standardowy sposób.
Po potwierdzeniu obu działań wróć do terminala Cloud Shell i naciśnij Ctrl+C
, aby zatrzymać interfejs ADK Dev.
9. Agenci przepływu pracy i wielu agentów w ADK
Nasi agenci mogą już planować wyjścia i korzystać z platformy. Jednak prawdziwie spersonalizowane planowanie wymaga poznania kręgu znajomych użytkownika. W przypadku zapracowanych użytkowników, którzy nie śledzą uważnie aktywności znajomych, ręczne zbieranie tych informacji jest trudne. Aby rozwiązać ten problem, stworzymy agenta profilowania społecznościowego, który będzie korzystać z naszej bazy danych grafów Spanner do analizowania aktywności i zainteresowań znajomych, co umożliwi wyświetlanie bardziej spersonalizowanych sugestii.
Najpierw potrzebujemy narzędzi, które umożliwią temu agentowi dostęp do danych wykresu.
👉📝 Na końcu pliku ~/instavibe-bootstrap/agents/social/instavibe.py
dodaj te funkcje Pythona:
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
Porozmawiajmy teraz o tym, jak skonstruować agenta. Analiza profili wielu znajomych i podsumowanie wyników obejmuje kilka etapów. To idealny scenariusz do wykorzystania funkcji ADK dotyczących wielu agentów, a zwłaszcza agentów przepływu pracy.
W ADK Google agent przepływu pracy nie wykonuje zadań samodzielnie, ale koordynuje pracę innych agentów, zwanych podagentami. Umożliwia to projektowanie modułowe, czyli dzielenie złożonych problemów na wyspecjalizowane komponenty. ADK udostępnia wbudowane typy przepływów pracy, takie jak:
- Sekwencyjny (krok po kroku)
- Równoległe (wykonywanie równoczesne)
- i Pętla (powtarzane wykonywanie).
W przypadku zadania profilowania społecznościowego nasz projekt wykorzystuje agenta pętli do tworzenia iteracyjnego przepływu pracy. Chcemy przetwarzać dane po jednej osobie: profile_agent
zbieramy dane, summary_agent
aktualizujemy analizę i check_agent
określamy, czy należy ponownie wykonać pętlę.
Zdefiniujmy podagenty wymagane w tym przepływie pracy.
👉📝 W ~/instavibe-bootstrap/agents/social/agent.py
zastąp #REPLACE FOR profile_agent
tymi wartościami:
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],
)
Następnie agent pobiera zebrane informacje o profilu (zgromadzone w trakcie iteracji pętli) i generuje ostateczne podsumowanie, w którym w przypadku analizy wielu osób wskazuje wspólne cechy.
👉📝 W tym samym pliku ~/instavibe-bootstrap/agents/social/agent.py
zastąp #REPLACE FOR summary_agent
tymi elementami:
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"
)
Musimy mieć sposób na określenie, kiedy pętla powinna się zatrzymać (czyli kiedy wszystkie żądane profile zostaną podsumowane).
👉📝 W tym samym pliku ~/instavibe-bootstrap/agents/social/agent.py
zastąp #REPLACE FOR check_agent
tymi elementami:
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"
)
Dodajemy proste sprawdzenie programowe (CheckCondition), które wyraźnie sprawdza summary_status
przechowywane w stanie, zwracane przez check_agent
, i informuje agenta pętli, czy ma kontynuować (escalate=False), czy zatrzymać działanie (escalate=True).
👉📝 W tym samym pliku ~/instavibe-bootstrap/agents/social/agent.py
zastąp #REPLACE FOR CheckCondition
znajdujący się u góry pliku tymi informacjami:
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))
Stan i wywołania zwrotne dla wyników pętli
W ADK Google stan to kluczowe pojęcie, które reprezentuje pamięć lub dane robocze agenta podczas jego wykonywania. Jest to zasadniczo trwały kontekst, który zawiera informacje potrzebne agentowi do zachowania ciągłości w różnych krokach, wywołaniach narzędzi lub interakcjach. Ten stan może przechowywać wyniki pośrednie, informacje o użytkowniku, parametry kolejnych działań lub inne dane, które agent musi zapamiętać w trakcie wykonywania zadania.
W naszym scenariuszu w miarę iteracji agenta pętli zmienne summary_agent
i check_agent
przechowują swoje dane wyjściowe (summary i summary_status) w stanie agenta. Dzięki temu informacje są zachowywane w kolejnych iteracjach. Sam agent Loop nie zwraca jednak automatycznie ostatecznego podsumowania po zakończeniu działania.
Wywołania zwrotne w ADK umożliwiają wstrzykiwanie niestandardowej logiki, która ma być wykonywana w określonych momentach cyklu życia agenta lub w odpowiedzi na określone zdarzenia, takie jak zakończenie wywołania narzędzia lub przed zakończeniem działania agenta. Umożliwiają one dostosowywanie zachowania agenta i dynamiczne przetwarzanie wyników.
Użyjemy after_agent_callback
, które zostanie uruchomione po zakończeniu pętli (ponieważ funkcja CheckCondition została eskalowana). Ten wywołanie zwrotne modify_output_after_agent
pobiera ostateczne podsumowanie ze stanu i formatuje je jako ostateczną wiadomość wyjściową agenta.
👉📝 W tym samym pliku ~/instavibe-bootstrap/agents/social/agent.py
zastąp #REPLACE FOR modify_output_after_agent
tymi wartościami:
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
Definiowanie agenta pętli głównej
Na koniec definiujemy głównego agenta LoopAgent. Koordynuje on działanie podagentów w sekwencji w ramach każdej iteracji pętli (profile_agent –> summary_agent –> check_agent –> CheckCondition). Będzie powtarzać tę sekwencję maksymalnie max_iterations razy lub do momentu, gdy CheckCondition zasygnalizuje zakończenie. Funkcja after_agent_callback zapewnia zwrócenie ostatecznego podsumowania.
👉📝 W tym samym pliku ~/instavibe-bootstrap/agents/social/agent.py
zastąp #REPLACE FOR root_agent
tymi wartościami:
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
)
Przetestujmy ten przepływ pracy z wieloma agentami za pomocą interfejsu ADK Dev UI.
👉💻 Uruchom serwer WWW 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
Otwórz interfejs ADK Dev (port 8000 w podglądzie w przeglądarce). W menu agenta (w prawym górnym rogu) wybierz agenta Social.
👉 Teraz poproś go o stworzenie profili kilku osób. W oknie czatu wpisz:
Tell me about Mike and Bob
Po odpowiedzi agenta (która może potrwać nieco dłużej ze względu na pętle i wielokrotne wywołania modelu LLM) nie patrz tylko na końcowy wynik czatu. W panelu po lewej stronie interfejsu ADK Dev UI otwórz kartę Events (Zdarzenia).
👉 Krok weryfikacji: na karcie Zdarzenia zobaczysz szczegółowe śledzenie wykonania krok po kroku.
Po sprawdzeniu, jak agent wywołuje każdego subagenta, gdzie oczekujesz, że przepływ będzie przebiegać od agenta profilu -> agenta podsumowania -> agenta sprawdzającego, w każdej iteracji sprawdzający. W praktyce jednak widzimy, jak działa potężna „samooptymalizacja” agenta.
Dzieje się tak, ponieważ model bazowy widzi całe żądanie (np. „profile Mike and Bob”), często wybiera najbardziej efektywną ścieżkę, zbierając wszystkie wymagane dane w jednym, skonsolidowanym kroku, zamiast wielokrotnie powtarzać ten proces. Możesz zobaczyć dane wejściowe i wyjściowe oraz stany każdego kroku, w tym wywołania narzędzi wykonane przez profil_agent.
oraz aktualizacje stanu z funkcji check_agent i CheckCondition.
Ten wizualny ślad jest nieoceniony w procesie zrozumienia i debugowania działania przepływu pracy z wieloma agentami aż do momentu wygenerowania ostatecznego podsumowania i jego zwrócenia przez wywołanie zwrotne.
Po zapoznaniu się z odpowiedzią na czacie i śladem zdarzenia wróć do terminala Cloud Shell i naciśnij Ctrl+C
, aby zatrzymać interfejs ADK Dev.
10. Komunikacja między agentami (A2A)
Do tej pory tworzyliśmy wyspecjalizowane agenty, ale działają one w izolacji lub w ramach zdefiniowanego przepływu pracy na tym samym urządzeniu. Aby tworzyć prawdziwie rozproszone i współpracujące systemy wieloagentowe, potrzebujemy sposobu na wykrywanie się agentów, którzy mogą działać jako oddzielne usługi, i skuteczne komunikowanie się ze sobą. W tym miejscu do akcji wkracza protokół Agent-to-Agent (A2A).
Protokół A2A to otwarty standard zaprojektowany specjalnie z myślą o komunikacji między agentami AI. MCP koncentruje się na interakcji agenta z narzędziem, a A2A – na interakcji agenta z agentem. Umożliwia to agentom:
- Odkrywanie: znajdź innych agentów i poznaj ich możliwości dzięki standardowym kartom agentów.
- Komunikacja: bezpieczna wymiana wiadomości i danych.
- Współpraca: deleguj zadania i koordynuj działania, aby osiągać złożone cele.
Protokół A2A ułatwia tę komunikację dzięki mechanizmom takim jak „karty agenta”, których agenci mogą używać do reklamowania swoich możliwości i informacji o połączeniu.
A2A korzysta ze znanych standardów internetowych (HTTP, SSE, JSON-RPC) i często wykorzystuje model klient-serwer, w którym jeden agent (klient) wysyła zadania do innego (zdalnego agenta/serwera). Ta standaryzacja jest kluczowa w przypadku tworzenia modułowych, skalowalnych systemów, w których agenci opracowywani niezależnie mogą ze sobą współpracować.
Włączanie A2A w przypadku agentów InstaVibe
Aby udostępnić nasze obecne agenty Planner, Platform Interaction i Social innym agentom za pomocą A2A, musimy otoczyć każdego z nich komponentem serwera A2A. Ten serwer:
- Udostępnij kartę agenta: wyświetl standardowy opis możliwości agenta za pomocą punktu końcowego HTTP.
- Nasłuchiwanie zadań(wiadomości z prośbą): akceptowanie przychodzących próśb o wykonanie zadań od innych agentów (klientów A2A) zgodnie z protokołem A2A.
- Zarządzanie wykonywaniem zadań(wiadomości z prośbami): przekazywanie otrzymanych zadań do logiki agenta ADK w celu przetworzenia.
Agent planujący (A2A włączony)
Zacznijmy od dodania warstwy serwera A2A do agenta Planera.
Zdefiniuj logikę uruchamiania serwera A2A. Ten kod definiuje element AgentCard (publiczny opis agenta), konfiguruje i uruchamia serwer A2A, łącząc go z elementem PlatformAgentExecutor.
👉📝 Dodaj ten kod na końcu pliku ~/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)
👉💻 Szybko sprawdźmy, czy serwer A2A uruchamia się prawidłowo lokalnie i czy wyświetla kartę agenta. Uruchom w pierwszym terminalu to polecenie:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server
👉 Teraz otwórz kolejne okno terminala. (Kliknij znak + w panelu terminala).
👉💻 Użyj polecenia curl, aby poprosić o kartę agenta z lokalnie działającego serwera:
curl http://localhost:10003/.well-known/agent.json | jq
Powinna się wyświetlić reprezentacja JSON zdefiniowanej przez nas karty AgentCard, co potwierdza, że serwer działa i reklamuje agenta Planera.
Wróć do pierwszego terminala (na którym działa serwer) i kliknij Ctrl+C
, aby go zatrzymać.
👉💻 Po dodaniu logiki serwera A2A możemy teraz utworzyć obraz kontenera.
Tworzenie i wdrażanie agenta Planera
. ~/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}"
👉💻 Wdróż agenta Planera w 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
Sprawdźmy, czy wdrożona usługa działa i prawidłowo wyświetla kartę agenta w chmurze za pomocą A2A Inspector.
👉 Kliknij ikonę Podgląd w przeglądarce na pasku narzędzi Cloud Shell i wybierz Zmień port. Ustaw port na 8081 i kliknij „Zmień i wyświetl podgląd”. Otworzy się nowa karta przeglądarki z interfejsem narzędzia A2A Inspector.
👉💻 W terminalu uzyskaj adres URL wdrożonego agenta planującego:
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}
👉💻 Skopiuj adres URL wyjściowy.
👉 W interfejsie narzędzia A2A Inspector wklej adres URL w polu Agent URL i kliknij Connect (Połącz).
👀 Na karcie agenta powinny pojawić się szczegóły karty agenta i plik JSON, co potwierdza udane połączenie.
👉 W inspektorze A2A kliknij kartę Chat. W tym miejscu możesz bezpośrednio wchodzić w interakcje z wdrożonym agentem. Wyślij mu wiadomość, aby przetestować jego możliwości planowania. Na przykład:
Plan something for me in Boston MA this weekend, and I enjoy classical music
👀 Aby sprawdzić surową komunikację, kliknij dymek z Twoją wiadomością, a potem dymek z odpowiedzią agenta w oknie czatu. Po kliknięciu każdego z nich wyświetli się pełna wiadomość JSON-RPC 2.0, która została wysłana lub odebrana. Jest to bardzo przydatne podczas debugowania.
Miejmy kartę inspektora A2A pod ręką. NIE zamykaj go! Za chwilę użyjemy go ponownie, aby przetestować pozostałe 2 agenty.
Platform Interaction Agent (A2A Enabled)
Następnie powtórz ten proces dla agenta interakcji z platformą (tego, który korzysta z MCP).
👉📝 Na końcu ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py
zdefiniuj konfigurację serwera A2A, w tym jego unikalną kartę AgentCard:
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)
Social Agent (A2A Enabled)
Na koniec włączmy A2A w przypadku naszego agenta profilowania społecznościowego.
👉📝 Na końcu ~/instavibe-bootstrap/agents/social/a2a_server.py
zdefiniuj konfigurację serwera A2A i kartę AgentCard:
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)
Tworzenie i wdrażanie agentów interakcji z platformą i agentów społecznościowych
Te agenty potrzebują dostępu do Spannera, więc podczas wdrażania upewnij się, że zmienne środowiskowe SPANNER_INSTANCE_ID
, SPANNER_DATABASE_ID
i MCP_SERVER_URL
są prawidłowo przekazywane.
👉💻 Tworzenie i wdrażanie w Cloud Run za pomocą 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}"
👉💻 W terminalu uzyskaj adres URL wdrożonego agenta platformy:
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
👉💻 Skopiuj adres URL wyjściowy.
👉 W interfejsie narzędzia A2A Inspector wklej adres URL w polu Agent URL i kliknij Connect (Połącz).
👀 Na karcie agenta powinny pojawić się szczegóły karty agenta i plik JSON, co potwierdza udane połączenie.
👉 W inspektorze A2A kliknij kartę Chat. W tym miejscu możesz bezpośrednio wchodzić w interakcje z wdrożonym agentem, wysyłać mu wiadomości i testować jego zdolność do tworzenia postów:
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.
👀 Aby sprawdzić surową komunikację, kliknij dymek z Twoją wiadomością, a potem dymek z odpowiedzią agenta w oknie czatu. Po kliknięciu każdego z nich wyświetli się pełna wiadomość JSON-RPC 2.0, która została wysłana lub odebrana. Jest to bardzo przydatne podczas debugowania.
👉💻 W terminalu uzyskaj adres URL wdrożonego agenta Social:
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL
👉💻 Skopiuj adres URL wyjściowy.
👉 W interfejsie narzędzia A2A Inspector wklej adres URL w polu Agent URL i kliknij Connect (Połącz).
👀 Na karcie agenta powinny pojawić się szczegóły karty agenta i plik JSON, co potwierdza udane połączenie.
👉 W inspektorze A2A kliknij kartę Chat. W tym miejscu możesz bezpośrednio wchodzić w interakcję z wdrożonym agentem. Wyślij mu wiadomość, aby przeanalizować profile użytkowników z bazy danych:
Can you tell me about both Ian and Kevin's profile, what are their common interests?
👀 Aby sprawdzić surową komunikację, kliknij dymek z Twoją wiadomością, a potem dymek z odpowiedzią agenta w oknie czatu. Po kliknięciu każdego z nich wyświetli się pełna wiadomość JSON-RPC 2.0, która została wysłana lub odebrana. Jest to bardzo przydatne podczas debugowania.
👉 Świetnie, zakończyliśmy sprawdzanie wszystkich naszych agentów. Możesz teraz zamknąć kartę A2A Inspector.
11. Agent Orchestrator (klient A2A)
Obecnie mamy 3 wyspecjalizowane agenty (Planner, Platform, Social) działające jako niezależne usługi z obsługą A2A w Cloud Run. Ostatnim elementem jest agent aranżera. Ten agent będzie działać jako centralny koordynator lub klient A2A. Będzie on otrzymywać żądania użytkowników, określać, którzy zdalni agenci są potrzebni do ich realizacji (potencjalnie w sekwencji), a następnie używać protokołu A2A do delegowania zadań do tych zdalnych agentów. Na potrzeby tych warsztatów uruchomimy agenta Orchestratora lokalnie za pomocą interfejsu ADK Dev.
Najpierw ulepszmy logikę Orchestratora, aby obsługiwał rejestrację wykrywanych agentów zdalnych. Podczas inicjowania zapisuje szczegóły połączenia z pobranych kart agenta.
👉📝 W polu ~/instavibe-bootstrap/agents/orchestrate/agent.py
zastąp tekst #REPLACE ME REG AGENT CARD
tym tekstem:
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)
Następnie w ADK zdefiniuj narzędzie dla samego agenta Orchestrator.
send_message
(funkcja A2A do delegowania zadań).
👉📝 Zastąp #REPLACE ME CREATE AGENT
w ~/instavibe-bootstrap/agents/orchestrate/agent.py
tym tekstem:
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],
)
Podstawowa logika narzędzia Orchestrator opiera się na instrukcjach, które mówią mu, jak korzystać z A2A.
👉📝 Zastąp #REPLACE ME INSTRUCTIONS
w ~/instavibe-bootstrap/agents/orchestrate/agent.py
tą metodą generowania instrukcji:
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']}`
"""
Testowanie narzędzia Orchestrator i pełnego systemu A2A
Teraz przetestujmy cały system. Orchestrator uruchomimy lokalnie za pomocą interfejsu ADK Dev, a będzie on komunikować się z agentami Planner, Platform i Social działającymi zdalnie w Cloud Run.
👉💻 Najpierw upewnij się, że zmienna środowiskowa REMOTE_AGENT_ADDRESSES
zawiera adresy URL wdrożonych agentów z włączoną funkcją A2A, rozdzielone przecinkami. Następnie ustaw niezbędne zmienne środowiskowe dla agenta Orchestratora i uruchom interfejs ADK Dev UI:
. ~/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
👉 Otwórz interfejs ADK Dev (zmień port z powrotem na 8000 za pomocą podglądu w przeglądarce).
👉 W menu agenta wybierz agenta orchestrate.
👉 Teraz przekaż mu złożone zadanie, które wymaga koordynacji wielu zdalnych agentów. Wypróbuj ten pierwszy przykład, który powinien obejmować agenta społecznościowego, a następnie agenta planującego:
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.
Obserwuj interakcję w oknie czatu interfejsu ADK Dev UI. Zwróć szczególną uwagę na odpowiedzi Orchestratora – powinien on podać, do którego agenta zdalnego deleguje zadania (np. „OK, najpierw zapytam agenta profilu społecznościowego o Iana i Norę…”.
Sprawdź też kartę Zdarzenia w interfejsie, aby zobaczyć wywołania narzędzi (send_message) wysyłane do adresów URL agentów zdalnych.
👉 Teraz wypróbuj drugi przykład, który powinien bezpośrednio obejmować agenta integracji platformy:
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
Ponownie monitoruj czat i kartę Zdarzenia. Orchestrator powinien określić potrzebę utworzenia wydarzenia i przekazać zadanie (wraz ze wszystkimi podanymi szczegółami) do „Platform Integration Agent”. Możesz też kliknąć przycisk Ślad, aby wyświetlić ślady i przeanalizować czasy odpowiedzi na zapytania oraz wykonane operacje.
Następnie możesz sprawdzić, czy zdarzenie pojawia się w aplikacji internetowej InstaVibe.
Pokazuje to udane wdrożenie systemu wieloagentowego z użyciem pakietu ADK i protokołu A2A, w którym centralny koordynator deleguje zadania do wyspecjalizowanych agentów zdalnych.
Po zakończeniu testowania zatrzymaj interfejs programisty ADK (Ctrl+C
w terminalu).
12. Agent Engine i połączenia zdalne z InstaVibe
Do tej pory uruchamialiśmy wyspecjalizowanych agentów w Cloud Run i testowaliśmy Orchestratora lokalnie za pomocą interfejsu ADK Dev. W scenariuszu produkcyjnym potrzebujemy solidnego, skalowalnego i zarządzanego środowiska do hostowania naszych agentów. W tym miejscu przydaje się Vertex AI Agent Engine od Google.
Agent Engine to w pełni zarządzana usługa w Vertex AI zaprojektowana specjalnie do wdrażania i skalowania agentów AI. Usługa nie wymaga zarządzania infrastrukturą, zabezpieczeniami ani kosztami operacyjnymi, dzięki czemu programiści (zwłaszcza ci, którzy nie znają złożonych środowisk chmurowych) mogą skupić się na logice i możliwościach agenta, a nie na zarządzaniu serwerami. Zapewnia dedykowane środowisko wykonawcze zoptymalizowane pod kątem zbiorów zadań agenta.
Teraz wdrożymy agenta Orchestrator w Agent Engine. (Uwaga: mechanizm wdrażania pokazany poniżej korzysta z niestandardowego skryptu (agent_engine_app.py) udostępnionego w materiałach warsztatowych, ponieważ oficjalne narzędzia do wdrażania bezpośrednio z ADK do Agent Engine mogą być jeszcze w fazie rozwoju. Ten skrypt obsługuje pakowanie i wdrażanie agenta Orchestratora skonfigurowanego z niezbędnymi adresami agentów zdalnych).
Aby wdrożyć agenta Orchestrator w Agent Engine, wykonaj to polecenie: Sprawdź, czy zmienna środowiskowa REMOTE_AGENT_ADDRESSES (zawierająca adresy URL agentów Planner, Platform i Social w Cloud Run) jest nadal prawidłowo ustawiona zgodnie z poprzednią sekcją.
👉💻 Wdrożymy agenta Orchestrate w Agent Engine (uwaga: to moja własna implementacja wdrożenia. ADK ma interfejs wiersza poleceń, który pomaga w wdrażaniu. Zaktualizuję tę informację po wdrożeniu BYO-SA).
cd ~/instavibe-bootstrap/agents/
. ~/instavibe-bootstrap/set_env.sh
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-aiplatform-re.iam.gserviceaccount.com" \
--role="roles/viewer"
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
Orchestrator jest teraz hostowany na zarządzanej platformie Agent Engine, więc nasza aplikacja internetowa InstaVibe musi się z nim komunikować. Zamiast interakcji za pomocą interfejsu ADK Dev UI aplikacja internetowa będzie wykonywać zdalne wywołania punktu końcowego Agent Engine.
Najpierw musimy zmodyfikować kod aplikacji InstaVibe, aby zainicjować klienta Agent Engine za pomocą unikalnego identyfikatora wdrożonego agenta Orchestrator. Ten identyfikator jest wymagany, aby kierować reklamy na właściwą instancję agenta na platformie.
👉📝 Otwórz ~/instavibe-bootstrap/instavibe/introvertally.py
i zastąp #REPLACE ME initiate agent_engine
tym kodem: Pobiera identyfikator Agent Engine ze zmiennej środowiskowej (którą za chwilę ustawimy) i uzyskuje obiekt klienta:
ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)
Planowany przepływ użytkownika w InstaVibe obejmuje 2 interakcje z agentem: najpierw wygenerowanie rekomendowanego planu, a potem poproszenie użytkownika o potwierdzenie, zanim agent opublikuje wydarzenie na platformie.
Aplikacja internetowa InstaVibe (działająca w Cloud Run) i agent Orchestrator (działający w Agent Engine) są teraz oddzielnymi usługami, więc aplikacja internetowa musi wykonywać zdalne wywołania punktu końcowego Agent Engine, aby wchodzić w interakcje z agentem.
👉📝 Zaktualizujmy kod, który wykonuje pierwsze wywołanie w celu wygenerowania rekomendacji planu. W tym samym pliku introvertally.py
zastąp #REPLACE ME Query remote agent get plan
tym fragmentem kodu, który używa klienta agent_engine do wysyłania żądania użytkownika:
agent_engine.stream_query(
user_id=user_id,
message=prompt_message,
)
👉📝 Następnie zaktualizuj kod, który obsługuje potwierdzenie użytkownika (np. gdy użytkownik kliknie „Potwierdź plan”). Spowoduje to wysłanie wiadomości uzupełniającej w ramach tej samej rozmowy w Agent Engine, która zawiera instrukcję dla Orchestratora, aby opublikował zdarzenie (co przekaże agentowi Platform Integration). Zastąp #REPLACE ME Query remote agent for confirmation
w celu potwierdzenia w introvertally.py
tym tekstem:
agent_engine.stream_query(
user_id=agent_session_user_id,
message=prompt_message,
)
Trasy aplikacji internetowej muszą mieć dostęp do tych funkcji. Sprawdź, czy w pliku tras Flask zaimportowano niezbędne funkcje z pliku introvertally.py.
👉📝 W cd ~/instavibe-bootstrap/instavibe/ally_routes.py
najpierw wskażemy instancję, zastępując # REPLACE ME TO ADD IMPORT
tymi elementami:
from introvertally import call_agent_for_plan, post_plan_event
👉📝 Dodaj funkcję prototypu do InstaVibe. W ~/instavibe-bootstrap/instavibe/templates/base.html
zastąp <!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> tym tekstem:
<li class="nav-item">
<a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
</li>
Zanim ponownie wdrożymy aplikację InstaVibe, potrzebujemy konkretnego Resource ID
agenta Orchestrator, którego wdrożyliśmy w Agent Engine.
Obecnie pobieranie tej wartości programowo za pomocą gcloud
może być ograniczone, dlatego użyjemy pomocniczego skryptu w języku Python (temp-endpoint.py
udostępnionego na warsztatach), aby pobrać identyfikator i zapisać go w zmiennej środowiskowej.
👉💻 Aby uruchomić skrypt, wpisz te polecenia. Skrypt przechwyci identyfikator punktu końcowego Agent Engine i przyzna niezbędne uprawnienia domyślnemu kontu usługi Agent Engine (uwaga: skrypt jest skonfigurowany tak, aby używać domyślnego konta usługi, ponieważ obecnie nie można go modyfikować).
. ~/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}"
Na koniec musimy ponownie wdrożyć aplikację internetową InstaVibe ze zaktualizowanym kodem i nową zmienną środowiskową ORCHESTRATE_AGENT_ID
, aby wiedziała, jak połączyć się z naszym agentem działającym w Agent Engine.
👉💻 Te polecenia ponownie kompilują obraz aplikacji InstaVibe i wdrażają nową wersję w 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
Po zakończeniu wdrażania otwórz adres URL aplikacji InstaVibe w innej karcie przeglądarki.
Testowanie pełnej wersji InstaVibe opartej na AI
Funkcja „InstaVibe Ally” jest już dostępna. Działa w oparciu o nasz system wielu agentów, który jest koordynowany przez Vertex AI Agent Engine i komunikuje się za pomocą A2A.
Kliknij „InstaVibe Ally” i poproś o zaplanowanie wydarzenia.
Obserwuj dziennik aktywności po prawej stronie, gdy agenci pracują (może to potrwać 90–120 sekund). Gdy pojawi się plan, sprawdź go i kliknij „Potwierdź ten plan”, aby przejść do publikowania.
Orchestrator wyda teraz agentowi platformy polecenie utworzenia posta i wydarzenia w InstaVibe.
Nowy post i wydarzenie znajdziesz na stronie głównej InstaVibe.
Na stronie wydarzenia będą widoczne szczegóły wygenerowane przez agenta.
Analizowanie wydajności za pomocą Cloud Trace
Może to trochę potrwać. Vertex AI Agent Engine jest zintegrowany z Cloud Trace, co pozwala nam analizować opóźnienia w naszym systemie wieloagentowym.
W konsoli Google Cloud otwórz Ślady, w sekcji Zakres kliknij agent_run[orchestrate_agent]
. Powinno się wyświetlić kilka zakresów. Kliknij jeden z nich.
W szczegółach logu czasu możesz sprawdzić, które części trwały dłużej. Na przykład wywołania agenta Planera mogą wykazywać większe opóźnienie ze względu na wyszukiwanie podstaw i złożone generowanie.
Podobnie podczas tworzenia posta i zdarzenia możesz zobaczyć czas spędzony przez Orchestratora na przetwarzaniu danych i przygotowywaniu wywołań narzędzi dla agenta platformy.
Analiza tych śladów pomaga zrozumieć i zoptymalizować działanie systemu agenta.
Gratulacje! Udało Ci się utworzyć, wdrożyć i przetestować zaawansowany system AI z wieloma agentami przy użyciu pakietu ADK, A2A, MCP i usług Google Cloud. Zajęliśmy się orkiestracją agentów, korzystaniem z narzędzi, zarządzaniem stanem i wdrażaniem w chmurze, tworząc funkcjonalną funkcję opartą na AI dla InstaVibe. Gratulujemy ukończenia warsztatów!
13. Czyszczenie
Aby uniknąć obciążania konta Google Cloud bieżącymi opłatami, usuń zasoby utworzone podczas tych warsztatów. Poniższe polecenia pomogą Ci usunąć instancję Spanner, usługi Cloud Run, repozytorium Artifact Registry, klucz interfejsu API, Vertex AI Agent Engine i powiązane uprawnienia IAM.
Ważne:
- Upewnij się, że uruchamiasz te polecenia w tym samym projekcie Google Cloud, którego używasz na warsztatach.
- Jeśli zamkniesz terminal Cloud Shell, niektóre zmienne środowiskowe, takie jak $PROJECT_ID, $SPANNER_INSTANCE_ID itp., mogą nie być ustawione. Musisz je ponownie wyeksportować, tak jak podczas konfiguracji warsztatów, lub zastąpić zmienne w poniższych poleceniach ich rzeczywistymi wartościami.
- Te polecenia trwale usuną Twoje zasoby. Jeśli w tym projekcie masz inne ważne dane, przed uruchomieniem tego narzędzia dokładnie sprawdź, czy nie zostaną one usunięte.
👉💻 Aby wyczyścić dane, uruchom te skrypty.
Resetowanie zmiennych środowiskowych
. ~/instavibe-bootstrap/set_env.sh
Usuń silnik agenta:
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."
Usuń usługi 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."
Zatrzymywanie i usuwanie kontenera Dockera A2A Inspector
docker rm --force a2a-inspector
Usuwanie instancji usługi Spanner:
echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."
Usuwanie repozytorium 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."
Usuwanie ról z konta usługi:
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."
Usuwanie lokalnych plików warsztatowych:
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."