1. Lerninhalte
Willkommen! Heute begeben wir uns auf eine ziemlich coole Reise. Nehmen wir als Beispiel die beliebte Social-Event-Plattform InstaVibe. Wir wissen, dass die tatsächliche Planung von Gruppenaktivitäten für einige Nutzer mühsam sein kann. Stellen Sie sich vor, Sie versuchen herauszufinden, wofür sich alle Ihre Freunde interessieren, dann durchsuchen Sie unzählige Optionen für Veranstaltungen oder Orte und koordinieren schließlich alles. Das ist viel. Genau hier können wir KI und insbesondere intelligente Agents einsetzen, um einen echten Unterschied zu machen.
Die Idee ist, ein System zu entwickeln, in dem diese KI-Assistenten die Hauptarbeit übernehmen können, z. B. indem sie auf intelligente Weise „zuhören“, um die Vorlieben von Nutzern und Freunden zu verstehen, und dann proaktiv fantastische, maßgeschneiderte Aktivitäten vorschlagen. Unser Ziel ist es, die soziale Planung auf InstaVibe nahtlos und angenehm zu gestalten. Um diese intelligenten Assistenten zu entwickeln, müssen wir mit den richtigen Tools eine solide Grundlage schaffen.
So sieht das Konzept aus:
Grundlagen mit dem ADK von Google:Hier lernen Sie die Grundlagen für die Entwicklung Ihres ersten intelligenten Agenten mit dem Agent Development Kit (ADK) von Google. Sie lernen die wesentlichen Komponenten und den Lebenszyklus von Agents kennen und erfahren, wie Sie die integrierten Tools des Frameworks effektiv nutzen können.
Agentenfunktionen mit dem Model Context Protocol (MCP) erweitern: Hier erfahren Sie, wie Sie Ihre Agenten mit benutzerdefinierten Tools und Kontext ausstatten, damit sie spezielle Aufgaben ausführen und auf bestimmte Informationen zugreifen können. Einführung in das Konzept des Model Context Protocol (MCP). Sie erfahren, wie Sie einen MCP-Server einrichten, um diesen Kontext bereitzustellen.
Agenteninteraktionen und ‑orchestrierung entwerfen:Hier geht es darum, über einzelne Agents hinauszugehen und die Orchestrierung von Agents zu verstehen. Entwerfen Sie Interaktionsmuster, die von einfachen sequenziellen Workflows bis hin zu komplexen Szenarien mit Schleifen, bedingter Logik und paralleler Verarbeitung reichen. Einführung des Konzepts von untergeordneten Agents im ADK-Framework zur Verwaltung modularer Aufgaben.
Collaborative Multi-Agent Systems entwickeln:Hier erfahren Sie, wie Sie Systeme entwickeln, in denen mehrere Agents zusammenarbeiten, um komplexe Ziele zu erreichen. Sie lernen das Agent-to-Agent-Kommunikationsprotokoll (A2A) kennen und implementieren es. Damit wird eine standardisierte Möglichkeit für verteilte Agents (die möglicherweise auf verschiedenen Maschinen oder Diensten ausgeführt werden) geschaffen, zuverlässig zu interagieren.
KI-Agenten in Google Cloud in die Produktion überführen:Übergang Ihrer Agent-Anwendungen von Entwicklungsumgebungen in die Cloud. Best Practices für die Entwicklung und Bereitstellung skalierbarer, robuster Multi-Agent-Systeme auf der Google Cloud Platform (GCP). Sie erhalten Einblicke in die Nutzung von GCP-Diensten wie Cloud Run und erfahren mehr über die Funktionen der neuesten Google Agent Engine zum Hosten und Verwalten Ihrer Agents.
2. Architektur
KI-basierte Planung für soziale Medien mit InstaVibe
Was ist Social Listening?
Beim Social Listening werden digitale Gespräche auf Plattformen wie sozialen Medien, Foren und Nachrichtenseiten beobachtet, um zu verstehen, was die Leute über ein Thema, eine Marke oder eine Branche sagen. Sie erhalten dadurch wertvolle Informationen zu öffentlicher Meinung, Trends und Nutzerbedürfnissen. In diesem Workshop werden wir dieses Konzept in einem agentenbasierten System nutzen.
Du bist im Team von InstaVibe
Stellen Sie sich vor, Sie arbeiten bei „InstaVibe“, einem erfolgreichen Start-up mit einer beliebten Social-Event-Plattform für junge Erwachsene. Es läuft gut, aber wie viele Technologieunternehmen steht Ihr Team unter dem Druck von Investoren, KI für Innovationen zu nutzen. Intern haben Sie auch ein Segment von Nutzern bemerkt, die weniger aktiv sind als andere. Vielleicht sind sie weniger geneigt, Gruppenaktivitäten zu starten, oder finden die Planung schwierig. Für Ihr Unternehmen bedeutet das eine geringere Plattformbindung bei dieser wichtigen Nutzergruppe.
Die Recherche Ihres Teams hat ergeben, dass KI-basierte Unterstützung die Nutzerfreundlichkeit für diese Nutzer deutlich verbessern könnte. Die Idee ist, die Planung von Treffen zu vereinfachen, indem basierend auf den Interessen des Nutzers und seiner Freunde proaktiv relevante Aktivitäten vorgeschlagen werden. Die Frage, die sich Ihnen und Ihren Kollegen stellt, lautet: Wie können KI-Agents die oft zeitaufwendigen Aufgaben der Interessensermittlung, der Aktivitätsrecherche und der potenziellen ersten Koordination automatisieren?
Eine agentenbasierte Lösung (Prototypkonzept)
Sie schlagen vor, einen Prototyp für eine Funktion zu entwickeln, die auf einem Multi-Agent-System basiert. Hier eine konzeptionelle Aufschlüsselung:
- Social Profiling Agent: Dieser Agent nutzt Social Listening-Techniken, um Nutzerverbindungen, Interaktionen und möglicherweise breitere öffentliche Trends im Zusammenhang mit den Vorlieben des Nutzers zu analysieren. Dabei sollen gemeinsame Interessen und geeignete Aktivitätsmerkmale (z.B. Vorlieben für ruhigere Treffen, bestimmte Hobbys) ermittelt werden.
- Event Planning Agent (Agent für die Eventplanung): Dieser Agent nutzt die Informationen aus dem Social Profiling Agent und sucht in Online-Ressourcen nach bestimmten Events, Veranstaltungsorten oder Ideen, die den ermittelten Kriterien (z. B. Ort, Interessen) entsprechen.
- Platform Interaction Agent (mit MCP): Dieser Agent übernimmt den endgültigen Plan vom Activity Planning Agent. Die Hauptfunktion besteht darin, direkt mit der InstaVibe-Plattform zu interagieren, indem ein vordefiniertes MCP-Tool (Model Context Protocol) verwendet wird. Dieses Tool bietet dem Kundenservicemitarbeiter die Möglichkeit, einen Veranstaltungsvorschlag zu entwerfen und einen Beitrag zu erstellen, in dem der Plan beschrieben wird.
- Orchestrator-Agent: Dieser Agent fungiert als zentraler Koordinator. Sie empfängt die ursprüngliche Nutzeranfrage von der InstaVibe-Plattform und versteht das Gesamtziel (z.B. „Plane eine Veranstaltung für mich und meine Freunde“), und delegiert dann bestimmte Aufgaben in einer logischen Reihenfolge an die entsprechenden spezialisierten Agents. Sie verwaltet den Informationsfluss zwischen den Agents und sorgt dafür, dass das Endergebnis an den Nutzer zurückgegeben wird.
Wichtige Architekturelemente und Technologien
Google Cloud Platform (GCP):
- Vertex AI:
- Gemini-Modelle: Bietet Zugriff auf die hochmodernen Large Language Models (LLMs) von Google wie Gemini, die die Denk- und Entscheidungsfähigkeiten unserer Agents unterstützen.
- Vertex AI Agent Engine: Ein verwalteter Dienst zum Bereitstellen, Hosten und Skalieren unseres Orchestrator-Agents, der die Produktion vereinfacht und Infrastrukturkomplexitäten abstrahiert.
- Cloud Run: Eine serverlose Plattform für die Bereitstellung von containerisierten Anwendungen. Wir verwenden sie für Folgendes:
- Hosten der InstaVibe-Hauptwebanwendung
- Stellen Sie einzelne A2A-fähige Agents (Planner, Social Profiling, Platform Interaction) als unabhängige Mikrodienste bereit.
- Führen Sie den MCP-Tool-Server aus, um die internen APIs von InstaVibe für Kundenservicemitarbeiter verfügbar zu machen.
- Spanner: Eine vollständig verwaltete, global verteilte und stark konsistente relationale Datenbank. In diesem Workshop nutzen wir die Funktionen von Cloud Spanner als Graphdatenbank mit der GRAPH-DDL und den Abfragefunktionen, um:
- Komplexe soziale Beziehungen (Nutzer, Freundschaften, Teilnahme an Veranstaltungen, Beiträge) modellieren und speichern.
- Ermöglichen Sie eine effiziente Abfrage dieser Beziehungen für die Social Profiling-Agents.
- Artifact Registry: Ein vollständig verwalteter Dienst zum Speichern, Verwalten und Sichern von Container-Images.
- Cloud Build: Ein Dienst, der Ihre Builds in Google Cloud ausführt. Wir verwenden es, um automatisch Docker-Container-Images aus unserem Agenten- und Anwendungsquellcode zu erstellen.
- Cloud Storage: Wird von Diensten wie Cloud Build zum Speichern von Build-Artefakten und von Agent Engine für den Betrieb verwendet.
- Wichtige Agent-Frameworks und ‑Protokolle:
- Agent Development Kit (ADK) von Google: Das primäre Framework für:
- Die Kernlogik, das Verhalten und die Anweisungssätze für einzelne intelligente Agents definieren.
- Verwaltung von Agent-Lebenszyklen, ‑Status und ‑Speicher (kurzfristiger Sitzungsstatus und potenziell langfristiges Wissen).
- Integration von Tools (z. B. Google Suche oder benutzerdefinierte Tools), die Agenten verwenden können, um mit der Welt zu interagieren.
- Orchestrierung von Multi-Agent-Workflows, einschließlich sequenzieller, Schleifen- und paralleler Ausführung von untergeordneten Agents.
- Agent-to-Agent (A2A) Communication Protocol: Ein offener Standard, der Folgendes ermöglicht:
- Direkte, standardisierte Kommunikation und Zusammenarbeit zwischen verschiedenen KI-Agents, auch wenn sie als separate Dienste oder auf verschiedenen Maschinen ausgeführt werden.
- Agents können die Fähigkeiten anderer Agents (über Agent-Karten) ermitteln und Aufgaben delegieren. Das ist entscheidend, damit unser Orchestrator-Agent mit den spezialisierten Planner-, Social- und Platform-Agents interagieren kann.
- A2A-Python-Bibliothek (a2a-python): Die konkrete Bibliothek, die verwendet wird, damit unsere ADK-Agents das A2A-Protokoll sprechen. Sie bietet die serverseitigen Komponenten, die für Folgendes erforderlich sind:
- Unsere Kundenservicemitarbeiter als A2A-kompatible Server bereitstellen.
- Die „Agent Card“ für die Erkennung wird automatisch bereitgestellt.
- Eingehende Aufgabenanfragen von anderen Agents (z. B. dem Orchestrator) empfangen und verwalten.
- Model Context Protocol (MCP): Ein offener Standard, der es Agenten ermöglicht,
- Verbindungen zu externen Tools, Datenquellen und Systemen auf standardisierte Weise herstellen und diese nutzen.
- Unser Platform Interaction Agent verwendet einen MCP-Client, um mit einem MCP-Server zu kommunizieren. Dieser wiederum stellt Tools zur Verfügung, mit denen mit den vorhandenen APIs der InstaVibe-Plattform interagiert werden kann.
- Agent Development Kit (ADK) von Google: Das primäre Framework für:
- Tools zur Fehlerbehebung:
- A2A Inspector: Der A2A Inspector ist ein webbasiertes Debugging-Tool, das in diesem Workshop verwendet wird, um eine Verbindung zu unseren A2A-fähigen Agents herzustellen, sie zu untersuchen und mit ihnen zu interagieren. Sie ist zwar nicht Teil der endgültigen Produktionsarchitektur, aber ein wesentlicher Bestandteil unseres Entwicklungs-Workflows. Es bietet Folgendes:
- Agent Card Viewer: Zum Abrufen und Validieren der öffentlichen Funktionen eines Agents.
- Livechat-Oberfläche: Hier können Sie Nachrichten direkt an einen bereitgestellten Agent senden, um ihn sofort zu testen.
- Debug-Konsole: Hier können Sie die unverarbeiteten JSON-RPC-Nachrichten ansehen, die zwischen dem Inspector und dem Agent ausgetauscht werden.
- A2A Inspector: Der A2A Inspector ist ein webbasiertes Debugging-Tool, das in diesem Workshop verwendet wird, um eine Verbindung zu unseren A2A-fähigen Agents herzustellen, sie zu untersuchen und mit ihnen zu interagieren. Sie ist zwar nicht Teil der endgültigen Produktionsarchitektur, aber ein wesentlicher Bestandteil unseres Entwicklungs-Workflows. Es bietet Folgendes:
- Sprachmodelle (LLMs): Das „Gehirn“ des Systems:
- Gemini-Modelle von Google: Wir verwenden insbesondere Versionen wie gemini-2.0-flash. Diese Modelle werden für Folgendes ausgewählt:
- Erweiterte Schlussfolgerungen und Befolgung von Anweisungen: Die Fähigkeit, komplexe Prompts zu verstehen, detaillierte Anweisungen zu befolgen und Aufgaben zu analysieren, macht sie für die Entscheidungsfindung von Agenten geeignet.
- Tool-Nutzung (Funktionsaufrufe): Gemini-Modelle sind hervorragend darin, zu bestimmen, wann und wie die über das ADK bereitgestellten Tools verwendet werden sollen, damit Agents Informationen sammeln oder Aktionen ausführen können.
- Effizienz (Flash-Modelle): Die „Flash“-Varianten bieten ein gutes Gleichgewicht zwischen Leistung und Kosteneffizienz und eignen sich für viele interaktive Agent-Aufgaben, die schnelle Antworten erfordern.
- Gemini-Modelle von Google: Wir verwenden insbesondere Versionen wie gemini-2.0-flash. Diese Modelle werden für Folgendes ausgewählt:
Google Cloud-Guthaben erforderlich?
3. Hinweis
👉 Klicken Sie oben in der Google Cloud Console auf Cloud Shell aktivieren (das ist das Terminal-Symbol oben im Cloud Shell-Bereich),
👉 Klicken Sie auf die Schaltfläche Editor öffnen (sie sieht aus wie ein geöffneter Ordner mit einem Stift). Dadurch wird der Cloud Shell-Codeeditor im Fenster geöffnet. Auf der linken Seite sehen Sie einen Datei-Explorer.
👉 Klicken Sie in der unteren Statusleiste auf die Schaltfläche Cloud Code-Anmeldung (siehe Abbildung). Autorisieren Sie das Plug-in wie beschrieben. Wenn in der Statusleiste Cloud Code – kein Projekt angezeigt wird, wählen Sie diese Option im Drop-down-Menü „Google Cloud-Projekt auswählen“ aus und wählen Sie dann das gewünschte Google Cloud-Projekt aus der Liste der Projekte aus, die Sie erstellt haben.
👉 Google Cloud-Projekt-ID finden:
- Öffnen Sie die Google Cloud Console: https://console.cloud.google.com
- Wählen Sie oben auf der Seite im Drop-down-Menü das Projekt aus, das Sie für diesen Workshop verwenden möchten.
- Ihre Projekt-ID wird im Dashboard auf der Karte „Projektinformationen“ angezeigt.
👉 Öffnen Sie das Terminal in der Cloud-IDE .
👉💻 Prüfen Sie im Terminal mit dem folgenden Befehl, ob Sie bereits authentifiziert sind und das Projekt auf Ihre Projekt-ID festgelegt ist:
gcloud auth list
👉💻 Klonen Sie das Projekt instavibe-bootstrap
von GitHub:
git clone -b adk-1.2.1-a2a-0.2.7 https://github.com/weimeilin79/instavibe-bootstrap.git
chmod +x ~/instavibe-bootstrap/init.sh
chmod +x ~/instavibe-bootstrap/set_env.sh
Projektstruktur verstehen
Bevor wir mit der Entwicklung beginnen, sehen wir uns das Layout des instavibe-bootstrap
-Projekts an, das Sie gerade geklont haben. So wissen Sie, wo Sie Dateien im Workshop finden und bearbeiten können.
instavibe-bootstrap/
├── agents/
│ ├── orchestrate/
│ ├── planner/
│ ├── platform_mcp_client/
│ └── social/
├── instavibe/
│ ├── static/
│ └── templates/
├── tools/
│ └── instavibe/
├── utils/
├── init.sh
└── set_env.sh
Hier eine Übersicht der wichtigsten Verzeichnisse:
agents/
: Das ist das Herzstück unseres KI-Systems. Jedes Unterverzeichnis (planner/, social/ usw.) enthält den Quellcode für einen bestimmten intelligenten Agenten.agent.py
: Dies ist die Hauptdatei im Ordner jedes Agents, in der die Logik des Agents enthalten ist.a2a_server.py
: Diese Datei umschließt den ADK-Agent mit einem Agent-to-Agent-Server (A2A).Dockerfile
: Definiert, wie das Container-Image für die Bereitstellung des Agents in Cloud Run oder Agent Engine erstellt wird.
instavibe/
: Dieses Verzeichnis enthält den gesamten Quellcode für die InstaVibe-Webanwendung.tools/
: Dieses Verzeichnis ist zum Erstellen externer Tools vorgesehen, die unsere Agents verwenden können.instavibe/
enthält den MCP-Server (Model Context Protocol).
Durch diese modulare Struktur wird die Webanwendung von den verschiedenen KI-Komponenten getrennt, wodurch das gesamte System einfacher zu verwalten, zu testen und bereitzustellen ist.
👉💻 Führen Sie das Initialisierungsskript aus:
Sie werden aufgefordert, Ihre Google Cloud-Projekt-ID einzugeben.
Geben Sie die Google Cloud-Projekt-ID ein, die Sie im letzten Schritt ermittelt haben, wenn Sie vom init.sh
-Script dazu aufgefordert werden:
cd ~/instavibe-bootstrap
./init.sh
👉💻 Legen Sie die erforderliche Projekt-ID fest:
gcloud config set project $(cat ~/project_id.txt) --quiet
👉💻 Führen Sie den folgenden Befehl aus, um die erforderlichen Google Cloud APIs zu aktivieren:
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
👉💻 Legen Sie alle erforderlichen Umgebungsvariablen fest:
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"
Berechtigung einrichten
👉💻 Berechtigungen erteilen. Führen Sie im Terminal folgenden Befehl aus :
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"
👉 Ergebnis in der IAM-Konsole prüfen
👉💻 Führen Sie die folgenden Befehle im Terminal aus, um ein Artifact Registry-Repository zu erstellen. Alle Docker-Images für unsere Agents, den MCP-Server und die InstaVibe-Anwendung werden hier gespeichert, bevor sie in Cloud Run oder Agent Engine bereitgestellt werden.
export REPO_NAME="introveally-repo"
gcloud artifacts repositories create $REPO_NAME \
--repository-format=docker \
--location=us-central1 \
--description="Docker repository for InstaVibe workshop"
Kartenplattform für API-Schlüssel einrichten
Wenn Sie Google Maps-Dienste in Ihrer InstaVibe-Anwendung verwenden möchten, müssen Sie einen API-Schlüssel erstellen und ihn entsprechend einschränken.
👉 Rufen Sie auf einem neuen Tab APIs & Dienste > Anmeldedaten auf. Klicken Sie auf der Seite „Anmeldedaten“ oben auf die Schaltfläche „+ ANMELDEDATEN ERSTELLEN“. Wählen Sie im Drop-down-Menü „API-Schlüssel“ aus.
👉 Ein Dialogfeld mit dem neu erstellten API-Schlüssel wird angezeigt. Sie benötigen sie später für die Konfiguration Ihrer Anwendung.
👉 Klicken Sie im Dialogfeld „API-Schlüssel erstellt“ auf SCHLIESSEN.
👉 Der neue API-Schlüssel wird in der Liste angezeigt (z.B. „API-Schlüssel 1“). Klicken Sie rechts auf die drei Punkte und wählen Sie API-Schlüssel bearbeiten aus, um die Seite „API-Schlüssel einschränken und umbenennen“ zu öffnen.
👉 Ändern Sie im Feld „Name“ oben den Standardnamen in Maps Platform API Key. 🚨🚨WICHTIG🚨🚨 Bitte verwenden Sie diesen Namen.
Maps Platform API Key
👉 Prüfen Sie, ob im Abschnitt „Anwendungseinschränkungen“ die Option Keine ausgewählt ist.
👉 Wählen Sie im Abschnitt „API-Einschränkungen“ das Optionsfeld „Schlüssel einschränken“ aus.
👉 Klicken Sie auf das Drop-down-Menü „APIs auswählen“. Geben Sie im angezeigten Suchfeld Maps JavaScript API
ein und wählen Sie es aus der Liste aus.
👉 Klicken Sie auf „OK“.
👉 Klicken Sie unten auf der Seite auf die Schaltfläche „SPEICHERN“.
Sie haben jetzt erfolgreich einen API-Schlüssel mit dem Namen „Maps Platform API Key“ erstellt, die Verwendung auf die „Maps JavaScript API“ beschränkt und dafür gesorgt, dass die API für Ihr Projekt aktiviert ist.
4. Graphdatenbank einrichten
Bevor wir unsere intelligenten Agents erstellen können, benötigen wir eine Möglichkeit, die komplexen Verbindungen in unserem sozialen Netzwerk InstaVibe zu speichern und zu verstehen. Hier kommt eine Graphdatenbank ins Spiel. Im Gegensatz zu herkömmlichen relationalen Datenbanken, in denen Daten in Tabellen mit Zeilen und Spalten gespeichert werden, ist eine Graphdatenbank speziell dafür konzipiert, Daten in Form von Knoten (z. B. Personen, Ereignisse oder Beiträge) und den Beziehungen (Kanten), die sie verbinden (z. B. Freundschaften, Teilnahme an Veranstaltungen oder Erwähnungen), darzustellen und abzufragen. Diese Struktur ist für Social-Media-Anwendungen sehr nützlich, da sie die Struktur realer sozialer Netzwerke widerspiegelt. So lässt sich intuitiv nachvollziehen, wie verschiedene Einheiten miteinander verbunden sind.
Wir implementieren diese Graphdatenbank mit Google Cloud Spanner. Spanner ist zwar in erster Linie als global verteilte, strikt konsistente relationale Datenbank bekannt, ermöglicht es uns aber auch, Diagrammstrukturen direkt auf unseren relationalen Tabellen zu definieren und abzufragen.
So profitieren wir von der Skalierbarkeit, der Transaktionskonsistenz und der vertrauten SQL-Schnittstelle von Spanner sowie von der Ausdrucksstärke von Graphabfragen zur Analyse der komplexen sozialen Dynamik, die für unsere KI-basierten Funktionen entscheidend ist.
👉💻 Im Cloud Shell IDE-Terminal. Stellen Sie die erforderliche Infrastruktur in Google Cloud bereit. Zuerst erstellen wir eine Spanner-Instanz, die als dedizierter Container für unsere Datenbanken dient. Sobald die Instanz bereit ist, erstellen wir die eigentliche Spanner-Datenbank darin, in der alle unsere Tabellen und die Grafikdaten für InstaVibe gespeichert werden:
. ~/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
👉💻 Dem Standarddienstkonto Lese-/Schreibzugriff auf Spanner gewähren
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}
👉💻 Jetzt. Wir richten eine virtuelle Python-Umgebung ein, installieren die erforderlichen Python-Pakete, richten dann das Graph Database-Schema in Spanner ein, laden es mit den ersten Daten und führen das setup.py
-Skript aus.
. ~/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
👉 Rufen Sie in einem neuen Browsertab die Google Cloud Console auf und gehen Sie zu Spanner. Dort sollte eine Liste Ihrer Spanner-Instanzen angezeigt werden. Klicken Sie auf das instavibe-graph-instance
. 👉 Auf der Übersichtsseite der Instanz sehen Sie eine Liste der Datenbanken in dieser Instanz. Klicken Sie auf
graphdb
.
👉 Klicken Sie im linken Navigationsbereich für Ihre Datenbank auf Spanner Studio .
👉 Fügen Sie die folgende Graph SQL-Abfrage in den Abfrageeditor (Tab „Unbenannte Abfrage“) ein. Mit dieser Abfrage werden alle „Person“-Knoten und ihre direkten „Friendship“-Beziehungen zu anderen „Person“-Knoten gefunden. Klicken Sie auf AUSFÜHREN, um das Ergebnis zu sehen.
Graph SocialGraph
MATCH result_paths = ((p:Person)-[f:Friendship]-(friend:Person))
RETURN SAFE_TO_JSON(result_paths) AS result_paths
👉 Ersetzen Sie im selben Abfrageeditor die vorherige DDL, um Personen zu finden, die an derselben Veranstaltung teilgenommen haben. Dies impliziert eine indirekte Verbindung über eine gemeinsame Aktivität.
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
👉 Mit dieser Abfrage wird eine andere Art von Verbindung untersucht. Wenn Sie die Namen von Personen in Beiträgen sehen möchten, die von Freunden einer bestimmten Person geschrieben wurden, führen Sie die folgende Abfrage im Abfrageeditor aus.
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
Diese Abfragen geben nur einen kleinen Einblick in die Leistungsfähigkeit von Spanner als Graphdatenbank für unsere InstaVibe-Anwendung. Indem wir unsere sozialen Daten als vernetztes Diagramm modellieren, ermöglichen wir eine ausgefeilte Analyse von Beziehungen und Aktivitäten. Das ist von grundlegender Bedeutung, damit unsere KI-Assistenten den Nutzerkontext verstehen, Interessen erkennen und letztendlich intelligente Unterstützung bei der sozialen Planung bieten können.
Nachdem wir die grundlegende Datenstruktur eingerichtet und getestet haben, wenden wir uns nun der vorhandenen InstaVibe-Anwendung zu, mit der unsere Kundenservicemitarbeiter interagieren werden.
5. Aktueller Status von InstaVibe
Um zu verstehen, wo unsere KI-Agents eingesetzt werden können, müssen wir zuerst die vorhandene InstaVibe-Webanwendung bereitstellen und ausführen. Diese Anwendung bietet die Benutzeroberfläche und grundlegende Funktionen, die mit der bereits eingerichteten Spanner-Graphendatenbank verbunden sind.
In der InstaVibe-App werden Eventorte auf den Eventdetailseiten mithilfe von Google Maps visuell dargestellt. Damit diese Funktion aktiviert werden kann, benötigt die Anwendung den API-Schlüssel, den wir zuvor erstellt haben. Mit dem folgenden Skript wird der tatsächliche Schlüsselstring anhand des von uns zugewiesenen Anzeigenamens („Maps Platform API Key“) abgerufen.
👉💻 Zurück in der Cloud Shell IDE. Führen Sie das folgende Skript aus. Prüfen Sie anschließend sorgfältig die Ausgabe, um sicherzustellen, dass der angezeigte GOOGLE_MAPS_API_KEY mit dem Schlüssel übereinstimmt, den Sie zuvor in der Google Cloud Console erstellt und kopiert haben.
. ~/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}"
👉💻 Erstellen wir nun das Container-Image für die InstaVibe-Webanwendung und übertragen es per Push in unser Artifact Registry-Repository.
. ~/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}
👉💻 Neues Build-Image der InstaVibe-Webanwendung in Cloud Run bereitstellen
. ~/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
Nach erfolgreicher Bereitstellung sollte in den Cloud Run-Logs die öffentliche URL für Ihre ausgeführte InstaVibe-Anwendung angezeigt werden.
Sie finden diese URL auch, indem Sie in der Google Cloud Console zum Bereich Cloud Run gehen und den instavibe-Dienst auswählen.
Öffnen Sie die URL jetzt in Ihrem Webbrowser, um die grundlegende InstaVibe-Plattform kennenzulernen. Sehen Sie sich die Beiträge, Ereignisse und Nutzerverbindungen an, die von der eingerichteten Graphdatenbank unterstützt werden.
Nachdem unsere Zielanwendung ausgeführt wird, erstellen wir den ersten intelligenten Agenten, um ihre Funktionen zu erweitern.
6. Basic Agent,Event Planner mit ADK
ADK-Framework
Einführung in das ADK-Framework von Google Nachdem wir die Grundlagen (die InstaVibe-App und die Datenbank) geschaffen haben, können wir unseren ersten intelligenten Agenten mit dem Agent Development Kit (ADK) von Google erstellen.
Das Agent Development Kit (ADK) ist ein flexibles und modulares Framework, das speziell für die Entwicklung und Bereitstellung von KI-Agenten entwickelt wurde. Das Designprinzip besteht darin, die Entwicklung von KI-Agenten an die traditionelle Softwareentwicklung anzulehnen, um Entwicklern die Erstellung, Bereitstellung und Orchestrierung von KI-Agentenarchitekturen zu erleichtern, die alles von einfachen, zweckgebundenen Aufgaben bis hin zu komplexen Multi-Agent-Workflows bewältigen können.
Im Grunde dreht sich das ADK um das Konzept eines Agent
, das Anweisungen und Konfigurationen (z. B. das ausgewählte Sprachmodell, z. B. Gemini) und eine Reihe von Tools
, die es zum Ausführen von Aktionen oder zum Sammeln von Informationen verwenden kann.
Unser erster Agent ist ein „Event Planner“ (Veranstaltungsplaner). Der Hauptzweck besteht darin, Nutzeranfragen für soziale Aktivitäten (mit Angabe von Ort, Datum und Interessen) entgegenzunehmen und kreative, maßgeschneiderte Vorschläge zu generieren. Damit die Vorschläge relevant sind und auf aktuellen Informationen basieren (z. B. auf bestimmten Ereignissen, die an diesem Wochenende stattfinden), nutzen wir eines der integrierten Tools des ADK: die Google Suche. So kann der Agent seine Antworten auf Echtzeit-Websuchergebnisse stützen und die neuesten Details zu Orten, Veranstaltungen und Aktivitäten abrufen, die den Kriterien des Nutzers entsprechen.
👉📝 Fügen Sie in der Cloud Shell-IDE in ~/instavibe-bootstrap/agents/planner/agent.py
den folgenden Prompt und die folgende Anleitung hinzu, um den Agent zu erstellen.
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]
)
Das ist unser erster definierter Agent. Das Tolle am ADK ist seine intuitive Bedienung und die praktischen Tools, die es bietet. Besonders nützlich ist die ADK Dev UI, mit der Sie Ihren Agenten interaktiv testen und seine Antworten in Echtzeit sehen können.
👉💻 Los gehts. Mit den folgenden Befehlen wird die ADK DEV-Benutzeroberfläche gestartet:
. ~/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
Nachdem Sie die Befehle ausgeführt haben, sollte in Ihrem Terminal eine Ausgabe angezeigt werden, die darauf hinweist, dass der ADK-Webserver gestartet wurde. Sie sollte in etwa so aussehen:
+-----------------------------------------------------------------------------+
| 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)
👉 So greifen Sie über Ihren Browser auf die ADK Dev-UI zu:
Wählen Sie in der Cloud Shell-Symbolleiste (normalerweise oben rechts) über das Symbol Webvorschau (oft als Auge oder Quadrat mit Pfeil dargestellt) die Option Port ändern aus. Legen Sie im Pop-up-Fenster den Port auf 8000 fest und klicken Sie auf „Ändern und Vorschau“. Cloud Shell öffnet dann einen neuen Browsertab oder ein neues Browserfenster mit der ADK Dev-Benutzeroberfläche.
Wenn die ADK Dev-Benutzeroberfläche in Ihrem Browser geöffnet ist, wählen Sie im Drop-down-Menü oben rechts in der Benutzeroberfläche planner als Agent aus, mit dem Sie interagieren möchten. Weisen Sie Ihrem Agenten nun im Chatdialogfeld auf der rechten Seite eine Aufgabe zu. Sie können beispielsweise eine Unterhaltung mit dem Agent führen:
Search and plan something in Seattle for me this weekend
This weekend and I enjoy food and anime
Datum vorschlagen (Ihre bevorzugte Option)
July 12 2025
Sie sollten sehen, wie der Agent Ihre Anfrage verarbeitet und einen Plan auf Grundlage der Google-Suchergebnisse erstellt.
Die Interaktion mit einem Agent ist das eine, aber wie können wir sicher sein, dass er sich immer wie erwartet verhält, insbesondere wenn wir Änderungen vornehmen?
Herkömmliche Softwaretestmethoden sind für KI-Agents aufgrund ihrer generativen und nicht deterministischen Natur oft nicht ausreichend. Um die Lücke zwischen einer coolen Demo und einem zuverlässigen Produktions-Agent zu schließen, ist eine solide Evaluierungsstrategie unerlässlich. Im Gegensatz zur einfachen Überprüfung der endgültigen Ausgabe eines generativen Modells beinhaltet die Bewertung eines Agents oft die Beurteilung seines Entscheidungsprozesses und seiner Fähigkeit, Tools korrekt zu verwenden oder Anweisungen in verschiedenen Szenarien zu befolgen. Das ADK bietet Funktionen, die dabei helfen.
👉 Klicken Sie in der ADK Dev-Benutzeroberfläche im linken Navigationsbereich auf den Tab „Eval“. Sie sollten eine vorab geladene Testdatei mit dem Namen plan_eval
sehen. Diese Datei enthält vordefinierte Eingaben und Kriterien zum Testen unseres Planner-Agents.
👉 Wählen Sie ein Szenario aus, z. B. „boston“, und klicken Sie auf die Schaltfläche Run Evaluation (Auswertung ausführen). Senken Sie im eingeblendeten Pop‑up-Fenster den Abgleichwert auf 0, 3 und klicken Sie auf „Starten“.
Dadurch wird der Agent mit der Testeingabe ausgeführt und geprüft, ob seine Ausgabe den definierten Erwartungen entspricht. So können Sie die Leistung Ihres Agents systematisch testen.
👉 Sehen wir uns nun an, was bei einem strengeren Schwellenwert passiert. Wählen Sie das Szenario „nyc“ aus und klicken Sie noch einmal auf Run Evaluation (Bewertung ausführen). Lassen Sie den Übereinstimmungsgrad dieses Mal auf dem Standardwert (Antwort-Übereinstimmungsgrad: 0, 7) und klicken Sie auf „Start“. Das Ergebnis ist „Nicht bestanden“. Das ist zu erwarten, da die kreative Ausgabe des Agents nicht perfekt mit der vordefinierten „optimalen“ Antwort übereinstimmt.
👉 Wenn Sie wissen möchten, warum der Test fehlgeschlagen ist, klicken Sie in der Zeile „nyc“ auf das Fehlersymbol. In der Benutzeroberfläche wird jetzt ein direkter Vergleich der tatsächlichen Antwort des Agenten und der erwarteten Antwort des Testlaufs angezeigt. Diese Ansicht ist für das Debugging unerlässlich, da Sie genau sehen können, wo die Ausgabe des Agents abgewichen ist. So können Sie die Anweisungen entsprechend anpassen.
Wenn Sie die Benutzeroberfläche und die Auswertung untersucht haben, kehren Sie zum Terminal des Cloud Shell-Editors zurück und drücken Sie Ctrl+C
, um die ADK Dev UI zu beenden.
Die Ausgabe von Freiformtext ist zwar ein guter Anfang, aber für Anwendungen wie InstaVibe, die die Vorschläge eines Agents einfach nutzen sollen, sind strukturierte Daten (z. B. JSON) viel praktischer. Wir ändern unseren Agent so, dass er seinen Plan in einem einheitlichen JSON-Format zurückgibt.
👉📝 Suchen Sie in ~/instavibe-bootstrap/agents/planner/agent.py
nach der Zeile, in der derzeit RETURN PLAN in MARKDOWN FORMAT
im Anweisungsstring des Agents steht. Ersetzen Sie diese Zeile durch die folgende detaillierte JSON-Struktur:
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
]
}
Nachdem Sie die Anweisungen des Agents aktualisiert haben, um eine JSON-Ausgabe anzufordern, sehen wir uns die Änderung an.
👉💻 Starten Sie die ADK Dev UI neu. Verwenden Sie dazu denselben Befehl wie zuvor:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
adk web
Aktualisieren Sie den Tab, wenn er bereits geöffnet ist. Oder folgen Sie derselben Anleitung wie zuvor, um die ADK Dev UI in Ihrem Browser zu öffnen (über die Webvorschau von Cloud Shell auf Port 8000). Achten Sie nach dem Laden der Benutzeroberfläche darauf, dass der Planner-Agent ausgewählt ist.
👉 Dieses Mal geben wir einen anderen Prompt ein. Geben Sie im Chatdialog Folgendes ein:
Plan an event Boston this weekend with art and coffee
Prüfen Sie die Antwort des Kundenservicemitarbeiters sorgfältig. Anstelle einer reinen Textantwort im Konversationsstil sehen Sie jetzt eine Antwort, die streng als JSON-Objekt formatiert ist und der Struktur entspricht, die wir in der Anleitung definiert haben (mit fun_plans, plan_description, locations_and_activities usw.). Das bestätigt, dass der Agent jetzt strukturierte Ausgaben erstellen kann, die für die programmatische Verwendung durch unsere InstaVibe-Anwendung geeignet sind.
Nachdem Sie die JSON-Ausgabe bestätigt haben, kehren Sie zum Cloud Shell-Terminal zurück und drücken Sie Ctrl+C
, um die ADK Dev UI zu beenden.
ADK-Komponenten
Die ADK-Entwickler-UI eignet sich zwar hervorragend für interaktive Tests, aber wir müssen unsere Agents oft programmatisch ausführen, z. B. als Teil einer größeren Anwendung oder eines Backend-Dienstes. Um zu verstehen, wie das funktioniert, sehen wir uns einige wichtige ADK-Konzepte im Zusammenhang mit Laufzeit- und Kontextverwaltung an.
Für sinnvolle, wechselseitige Unterhaltungen müssen Agents den Kontext verstehen. Sie müssen sich daran erinnern, was gesagt und getan wurde, um die Kontinuität aufrechtzuerhalten. Das ADK bietet strukturierte Möglichkeiten, diesen Kontext über Session, State und Memory zu verwalten:
- Sitzung:Wenn ein Nutzer mit einem Agent interagiert, wird eine Sitzung erstellt. Stellen Sie sich das als Container für einen einzelnen, bestimmten Chat vor. Sie enthält eine eindeutige ID, den Verlauf der Interaktionen (Ereignisse), die aktuellen Arbeitsdaten (Status) und Metadaten wie die Zeit der letzten Aktualisierung.
- Status:Dies ist der Kurzzeitspeicher des Agenten innerhalb einer einzelnen Sitzung. Es handelt sich um ein veränderliches Dictionary, in dem der Agent temporäre Informationen speichern kann, die zum Ausführen der aktuellen Aufgabe erforderlich sind, z.B. bisher erfasste Nutzereinstellungen oder Zwischenergebnisse von Tool-Aufrufen.
- Arbeitsspeicher:Dies steht für das Potenzial des Agents, sich langfristig an verschiedene Sitzungen zu erinnern oder auf externe Wissensdatenbanken zuzugreifen. Während Sitzung und Status die unmittelbare Unterhaltung verarbeiten, kann ein Agent mit dem Memory-Service (oft von einem MemoryService verwaltet) Informationen aus vergangenen Interaktionen oder strukturierten Datenquellen abrufen, wodurch er einen breiteren Wissenskontext erhält. Hinweis: Unser einfacher Client verwendet aus Gründen der Einfachheit In-Memory-Dienste. Das bedeutet, dass der Speicher/Status nur während der Ausführung des Skripts erhalten bleibt.
- Ereignis:Jede Interaktion innerhalb einer Sitzung (Nutzernachricht, Antwort des Agents, Anfrage zur Verwendung eines Tools, Tool-Ergebnis, Statusänderung, Fehler) wird als unveränderliches Ereignis aufgezeichnet. Dadurch wird ein chronologisches Log erstellt, das im Wesentlichen das Transkript und den Aktionsverlauf der Unterhaltung enthält.
Wie werden diese verwaltet, wenn ein Agent ausgeführt wird? Das ist die Aufgabe des Runners.
- Runner: Der Runner ist die vom ADK bereitgestellte zentrale Ausführungs-Engine. Sie definieren Ihren Agent und die Tools, die er verwendet. Der Runner orchestriert dann den Prozess der Erfüllung der Nutzeranfrage. Sie verwaltet die Sitzung, verarbeitet den Ablauf von Ereignissen, aktualisiert den Status, ruft das zugrunde liegende Sprachmodell auf, koordiniert Tool-Aufrufe und interagiert möglicherweise mit MemoryService. Stellen Sie sich vor, dass der Dirigent dafür sorgt, dass alle verschiedenen Teile richtig zusammenarbeiten.
Wir können den Runner verwenden, um unseren Agent als eigenständige Python-Anwendung auszuführen, die völlig unabhängig von der Entwickler-UI ist.
Erstellen wir ein einfaches Client-Script, um unseren Planner-Agent programmatisch aufzurufen.
👉📝 Fügen Sie in der Datei ~/instavibe-bootstrap/agents/planner/planner_client.py
den folgenden Python-Code unter den vorhandenen Importen hinzu. Fügen Sie in planner_client.py
unter den Importen Folgendes hinzu:
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}")
Mit diesem Code werden In-Memory-Dienste für die Sitzungs- und Artefaktverwaltung eingerichtet (um das Beispiel einfach zu halten), eine Sitzung erstellt, eine Nutzeranfrage definiert, der Runner mit unserem Agent konfiguriert und der Agent dann asynchron ausgeführt. Dabei wird jedes während der Ausführung generierte Ereignis ausgegeben.
👉💻 Führen Sie dieses Client-Script jetzt über Ihr Terminal aus:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents
python -m planner.planner_client
👀 Sehen Sie sich die Ausgabe an. Statt nur des endgültigen JSON-Plans sehen Sie die detaillierte Struktur jedes Event-Objekts, das während des Ausführungsablaufs des Agenten generiert wurde. Dazu gehören das erste Nutzerereignis, potenzielle Ereignisse im Zusammenhang mit Tool-Aufrufen (z. B. Google Suche) und schließlich das Antwort-Ereignis des Modells mit dem JSON-Plan. Dieser detaillierte Ereignisstream ist sehr nützlich, um die schrittweise Verarbeitung in der ADK-Laufzeitumgebung zu debuggen und nachzuvollziehen.
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
Wenn das Skript kontinuierlich ausgeführt wird oder hängen bleibt, müssen Sie es möglicherweise manuell durch Drücken von Ctrl+C
beenden.
7. Platform Interaction Agent – Interaktion mit dem MCP-Server
Das ADK hilft zwar, unsere Agents zu strukturieren, aber sie müssen oft mit externen Systemen oder APIs interagieren, um reale Aktionen auszuführen.
Model Context Protocol (MCP)
Das Model Context Protocol (MCP) ist ein offener Standard, der die Verbindung von KI-Anwendungen wie Kundenservicemitarbeitern mit externen Datenquellen, Tools und Systemen standardisieren soll. Ziel ist es, das Problem zu lösen, dass für jede Kombination aus KI-Anwendung und Datenquelle benutzerdefinierte Integrationen erforderlich sind, indem eine universelle Schnittstelle bereitgestellt wird. MCP nutzt eine Client-Server-Architektur, bei der MCP-Clients, die sich in KI-Anwendungen (Hosts) befinden, Verbindungen zu MCP-Servern verwalten. Diese Server sind externe Programme, die bestimmte Funktionen wie den Zugriff auf lokale Daten, die Interaktion mit Remote-Diensten über APIs oder die Bereitstellung vordefinierter Prompts ermöglichen. So können KI-Modelle auf aktuelle Informationen zugreifen und Aufgaben ausführen, die über ihr ursprüngliches Training hinausgehen. Diese Struktur ermöglicht es KI-Modellen, externe Funktionen auf standardisierte Weise zu erkennen und mit ihnen zu interagieren. Das macht Integrationen einfacher und skalierbarer.
InstaVibe-MCP-Server erstellen und bereitstellen
Unsere Kundenservicemitarbeiter müssen schließlich mit der InstaVibe-Plattform selbst interagieren, insbesondere um Beiträge zu erstellen und Ereignisse über die vorhandenen APIs der Plattform zu registrieren. Die InstaVibe-Anwendung stellt diese Funktionen bereits über Standard-HTTP-Endpunkte zur Verfügung:
Endpunkt | URL | HTTP-Methode | Beschreibung |
Beitrag erstellen | api/posts | POSTEN | API-Endpunkt zum Hinzufügen eines neuen Beitrags. Erwartet JSON-Text: |
Termin erstellen | api/events | POSTEN | API-Endpunkt zum Hinzufügen eines neuen Ereignisses und seiner Teilnehmer (vereinfachtes Schema). |
Damit unsere Kundenservicemitarbeiter über MCP auf diese Funktionen zugreifen können, müssen wir zuerst einfache Python-Funktionen erstellen, die als Wrapper für diese API-Aufrufe dienen. Diese Funktionen verarbeiten die Logik für HTTP-Anfragen.
👉 Zuerst implementieren wir die Wrapper-Funktion zum Erstellen eines Beitrags. Öffnen Sie die Datei ~/instavibe-bootstrap/tools/instavibe/instavibe.py
und ersetzen Sie den Kommentar #REPLACE ME CREATE POST
durch den folgenden Python-Code:
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
👉📝 Als Nächstes erstellen wir die Wrapper-Funktion für die API zum Erstellen von Ereignissen. Ersetzen Sie in derselben Datei ~/instavibe-bootstrap/tools/instavibe/instavibe.py
den Kommentar #REPLACE ME CREATE EVENTS
durch diesen Code:
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
Wie Sie sehen, sind diese Funktionen einfache Wrapper für die vorhandenen InstaVibe-APIs. Dieses Muster ist nützlich, wenn Sie bereits APIs für Ihre Dienste haben. Sie können ihre Funktionen ganz einfach als Tools für Agents verfügbar machen, indem Sie solche Wrapper erstellen.
MCP-Serverimplementierung
Nachdem wir nun die Python-Funktionen haben, die die Aktionen ausführen (Aufrufen der InstaVibe-APIs), müssen wir die MCP-Serverkomponente erstellen. Dieser Server stellt diese Funktionen gemäß dem MCP-Standard als „Tools“ zur Verfügung, sodass MCP-Clients (z. B. unsere Kundenservicemitarbeiter) sie erkennen und aufrufen können.
Ein MCP-Server implementiert in der Regel zwei wichtige Funktionen:
- list_tools: Ermöglicht dem Client, die auf dem Server verfügbaren Tools zu ermitteln. Dazu werden Metadaten wie Namen, Beschreibungen und erforderliche Parameter bereitgestellt, die häufig mit JSON Schema definiert werden.
- call_tool: Verarbeitet die Ausführung eines bestimmten Tools, das vom Client angefordert wurde. Dazu werden der Name und die Argumente des Tools empfangen und die entsprechende Aktion ausgeführt, z. B. die Interaktion mit einer API.
MCP-Server werden verwendet, um KI-Modellen Zugriff auf reale Daten und Aktionen zu ermöglichen. So können Aufgaben wie das Senden von E‑Mails, das Erstellen von Aufgaben in Projektmanagementsystemen, das Durchsuchen von Datenbanken oder die Interaktion mit verschiedenen Software- und Webservices ausgeführt werden. Während sich die ersten Implementierungen aus Gründen der Einfachheit oft auf lokale Server konzentrierten, die über Standard-Ein-/Ausgabe (stdio) kommunizierten, insbesondere in Entwicklungs- oder „Studio“-Umgebungen, ist der Übergang zu Remote-Servern, die Protokolle wie HTTP mit Server-Sent Events (SSE) verwenden, für eine breitere Akzeptanz und Unternehmensanwendungsfälle sinnvoller.
Die Remote-Architektur bietet trotz der zusätzlichen Netzwerkkommunikationsebene erhebliche Vorteile: Mehrere KI-Clients können auf einen einzelnen Server zugreifen, die Verwaltung und Aktualisierung von Tools wird zentralisiert, die Sicherheit wird verbessert, da vertrauliche Daten und API-Schlüssel auf der Serverseite verbleiben und nicht auf potenziell vielen Clientcomputern verteilt werden, und das KI-Modell wird von den Besonderheiten der externen Systemintegration entkoppelt. Dadurch wird das gesamte Ökosystem skalierbarer, sicherer und wartungsfreundlicher, als wenn jede KI-Instanz ihre eigenen direkten Integrationen verwalten müsste.
Wir implementieren unseren MCP-Server mit HTTP und Server-Sent Events (SSE) für die Kommunikation. Das ist gut geeignet für potenziell lang andauernde Tool-Ausführungen und Unternehmensszenarien.
👉📝 Implementieren wir zuerst den Endpunkt „list_tools“. Öffnen Sie die Datei ~/instavibe-bootstrap/tools/instavibe/mcp_server.py
und ersetzen Sie den Kommentar #REPLACE ME - LIST TOOLS
durch den folgenden Code. :
@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]
Diese Funktion definiert die Tools (create_event, create_post) und informiert verbundene Clients darüber.
👉📝 Implementieren Sie als Nächstes den call_tool
-Endpunkt, der die tatsächlichen Ausführungsanfragen von Clients verarbeitet. Ersetzen Sie in derselben Datei ~/instavibe-bootstrap/tools/instavibe/mcp_server.py
den Kommentar #REPLACE ME - CALL TOOLS
durch diesen Code.
@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)]
Diese Funktion empfängt den Toolnamen und die Argumente, sucht die entsprechende Python-Wrapper-Funktion, die wir zuvor definiert haben, führt sie aus und gibt das Ergebnis zurück.
👉💻 Nachdem die MCP-Serverlogik definiert wurde, muss sie als Container verpackt werden. Führen Sie dazu im Terminal das folgende Skript aus, um das Docker-Image mit Cloud Build zu erstellen:
. ~/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}
👉💻 Stellen Sie das Image als Dienst in Google Cloud Run bereit.
. ~/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
👉💻 Nachdem die Bereitstellung erfolgreich abgeschlossen wurde, wird der MCP-Server ausgeführt und ist über eine öffentliche URL zugänglich. Wir müssen diese URL erfassen, damit unser Agent (der als MCP-Client fungiert) weiß, wohin er sich verbinden muss.
export MCP_SERVER_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep mcp-tool-server)/sse
Der Dienst „mcp-tool-server“ sollte jetzt auch im Abschnitt Cloud Run Ihrer Google Cloud Console als „Wird ausgeführt“ aufgeführt sein.
Nachdem der MCP-Server bereitgestellt und seine URL erfasst wurde, können wir nun den Agent implementieren, der als MCP-Client fungiert und die von diesem Server bereitgestellten Tools nutzt.
8. Platform Interaction Agent (mit MCP)
MCP-Client: Der MCP-Client ist eine Komponente, die sich in einer KI-Anwendung oder einem KI-Agenten befindet und als Schnittstelle zwischen dem KI-Modell und einem oder mehreren MCP-Servern fungiert. In unserer Implementierung wird dieser Client direkt in unseren Agenten integriert. Die Hauptfunktion dieses Clients besteht darin, mit MCP-Servern zu kommunizieren, um verfügbare Tools über die list_tools
-Funktion zu ermitteln und anschließend die Ausführung bestimmter Tools über die call_tool
-Funktion anzufordern. Dabei werden die erforderlichen Argumente übergeben, die vom KI-Modell oder vom Agent bereitgestellt werden, der den Aufruf orchestriert.
Jetzt erstellen wir den Agent, der als MCP-Client fungiert. Dieser Agent, der im ADK-Framework ausgeführt wird, ist für die Kommunikation mit dem mcp-tool-server
verantwortlich, das wir gerade bereitgestellt haben.
👉 Zuerst müssen wir die Agent-Definition so ändern, dass die Tools dynamisch von unserem laufenden MCP-Server abgerufen werden. Ersetzen Sie in agents/platform_mcp_client/agent.py
#REPLACE ME - FETCH TOOLS
durch Folgendes:
"""Gets tools from the File System MCP Server."""
tools = MCPToolset(
connection_params=SseServerParams(url=MCP_SERVER_URL, headers={})
)
In diesem Code wird die Methode „MCPToolset.from_server“ verwendet, um eine Verbindung zum MCP_SERVER_URL (die wir zuvor als Umgebungsvariable festgelegt haben) herzustellen und die Liste der verfügbaren Tools abzurufen.
Als Nächstes müssen wir der ADK-Agentdefinition mitteilen, dass diese dynamisch abgerufenen Tools verwendet werden sollen.
👉 Ersetzen Sie in agents/platform_mcp_client/agent.py
#REPLACE ME - SET TOOLs
durch Folgendes:
tools=[tools],
👉💻 Testen wir diesen Agenten nun lokal mit der ADK Dev UI, um zu sehen, ob er sich korrekt mit dem MCP-Server verbinden und die Tools verwenden kann, um mit unserer laufenden InstaVibe-Anwendung zu interagieren.
. ~/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
Öffnen Sie die ADK Dev-UI noch einmal in Ihrem Browser (über die Cloud Shell-Webvorschau auf Port 8000). Wählen Sie dieses Mal im Drop-down-Menü rechts oben den platform_mcp_client
-Agent aus.
Testen wir das Tool „create_post“. Geben Sie im Chatdialog die folgende Anfrage ein:
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
Der Kundenservicemitarbeiter sollte dies verarbeiten, die Notwendigkeit der Verwendung des Tools „create_post“ erkennen, mit dem MCP-Server kommunizieren, der wiederum die InstaVibe API aufruft.
👉 Bestätigungsschritt: Nachdem der Kundenservicemitarbeiter die Aktion bestätigt hat, öffnen Sie den Tab, auf dem Ihre InstaVibe-Anwendung ausgeführt wird, oder aktualisieren Sie ihn. Der neue Beitrag von „Julia“ sollte im Hauptfeed angezeigt werden.
👉💻 Führen Sie dieses Skript in einem separaten Terminal aus, um bei Bedarf den Instavibe-Link zu erhalten:
gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep instavibe
👉📝 Jetzt testen wir das Tool „create_event“. Geben Sie die folgende mehrzeilige Anfrage in den Chatdialog ein:
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],
}
Auch hier sollte der Agent das entsprechende Tool über den MCP-Server verwenden. Klicken Sie auf dem Tab „Ereignisse“ auf das jeweilige Ereignis, um einen detaillierten, schrittweisen Trace der Ausführung zu sehen.
👉 Verifizierungsschritt: Gehen Sie zurück zu Ihrer laufenden InstaVibe-Anwendung und rufen Sie den Bereich „Events“ (oder einen entsprechenden Bereich) auf. Das neu erstellte Event „Mexico City Culinary & Art Day“ sollte jetzt aufgeführt sein.
Dies zeigt, wie unser Agent mit MCP externe Tools (in diesem Fall die APIs von InstaVibe) auf standardisierte Weise nutzen kann.
Wenn Sie beide Aktionen bestätigt haben, kehren Sie zu Ihrem Cloud Shell-Terminal zurück und drücken Sie Ctrl+C
, um die ADK Dev UI zu beenden.
9. Workflow-Agent und Multi-Agents im ADK
Unsere Agenten können bisher Ausflüge planen und mit der Plattform interagieren. Für eine wirklich personalisierte Planung ist es jedoch erforderlich, den sozialen Kreis des Nutzers zu kennen. Für vielbeschäftigte Nutzer, die die Aktivitäten ihrer Freunde möglicherweise nicht genau verfolgen, ist es schwierig, diesen Kontext manuell zu erfassen. Dazu entwickeln wir einen Social Profiling-Agent, der unsere Spanner Graph Database nutzt, um Aktivitäten und Interessen von Freunden zu analysieren und so maßgeschneiderte Vorschläge zu ermöglichen.
Zuerst benötigen wir Tools, mit denen dieser Agent auf die Diagrammdaten zugreifen kann.
👉📝 Fügen Sie am Ende der Datei ~/instavibe-bootstrap/agents/social/instavibe.py
die folgenden Python-Funktionen hinzu:
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
Sehen wir uns nun an, wie wir unseren Agenten strukturieren. Das Analysieren der Profile mehrerer Freunde und das Zusammenfassen der Ergebnisse umfasst mehrere Schritte. Dies ist ein ideales Szenario für die Verwendung der Multi-Agent-Funktionen des ADK, insbesondere von Workflow-Agents.
Im ADK von Google führt ein Workflow-Agent keine Aufgaben selbst aus, sondern orchestriert andere Agents, sogenannte Sub-Agents. Dies ermöglicht ein modulares Design, bei dem komplexe Probleme in spezialisierte Komponenten unterteilt werden. Das ADK bietet integrierte Workflowtypen wie
- Sequenziell (Schritt für Schritt)
- Parallel (gleichzeitige Ausführung)
- und „Loop“ (wiederholte Ausführung)
Für unsere Aufgabe zur Erstellung von Social-Media-Profilen verwenden wir einen Loop-Agent, um einen iterativen Workflow zu erstellen. Es ist vorgesehen, jeweils nur eine Person zu verarbeiten: profile_agent
erfasst Daten, summary_agent
aktualisiert die Analyse und check_agent
bestimmt, ob wir den Vorgang wiederholen sollten.
Definieren wir die für diesen Workflow erforderlichen untergeordneten Agents.
👉📝 Ersetzen Sie in ~/instavibe-bootstrap/agents/social/agent.py
#REPLACE FOR profile_agent
durch Folgendes:
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],
)
Als Nächstes wird der Agent verwendet, der die gesammelten Profilinformationen (die über mehrere Schleifendurchläufe hinweg zusammengetragen wurden) nimmt und die endgültige Zusammenfassung erstellt. Wenn mehrere Personen analysiert wurden, werden Gemeinsamkeiten ermittelt.
👉📝 Ersetzen Sie in derselben ~/instavibe-bootstrap/agents/social/agent.py
#REPLACE FOR summary_agent
durch Folgendes:
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"
)
Wir benötigen eine Möglichkeit, um festzustellen, wann die Schleife beendet werden soll, d. h. wenn alle angeforderten Profile zusammengefasst wurden.
👉📝 Ersetzen Sie in derselben ~/instavibe-bootstrap/agents/social/agent.py
#REPLACE FOR check_agent
durch Folgendes:
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"
)
Wir fügen eine einfache programmatische Prüfung (CheckCondition) hinzu, die explizit die in State gespeicherten summary_status
untersucht, die von check_agent
zurückgegeben werden, und dem Loop-Agent mitteilt, ob er fortfahren (escalate=False) oder anhalten (escalate=True) soll.
👉📝 Ersetzen Sie in derselben ~/instavibe-bootstrap/agents/social/agent.py
oben in der Datei #REPLACE FOR CheckCondition
durch Folgendes:
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))
Status und Callbacks für Schleifenergebnisse
Im ADK von Google ist State ein wichtiges Konzept, das den Speicher oder die Arbeitsdaten eines Agenten während der Ausführung darstellt. Es handelt sich im Grunde um einen persistenten Kontext, der Informationen enthält, die ein Agent über verschiedene Schritte, Tool-Aufrufe oder Interaktionen hinweg beibehalten muss. In diesem Status können Zwischenergebnisse, Nutzerinformationen, Parameter für nachfolgende Aktionen oder andere Daten gespeichert werden, an die sich der Agent im Laufe einer Aufgabe erinnern muss.
In unserem Szenario werden die Ausgaben (Zusammenfassung und summary_status) von summary_agent
und check_agent
im Status des Agents gespeichert, während der Loop-Agent durchläuft. So bleiben Informationen über Iterationen hinweg erhalten. Der Loop-Agent selbst gibt jedoch nicht automatisch die endgültige Zusammenfassung aus dem Status zurück, wenn er fertig ist.
Mit Callbacks im ADK können wir benutzerdefinierte Logik einfügen, die an bestimmten Punkten im Lebenszyklus eines Agents oder als Reaktion auf bestimmte Ereignisse ausgeführt wird, z. B. nach Abschluss eines Tool-Aufrufs oder bevor der Agent die Ausführung beendet. Sie ermöglichen es, das Verhalten des Agenten anzupassen und Ergebnisse dynamisch zu verarbeiten.
Wir verwenden ein after_agent_callback
, das ausgeführt wird, wenn die Schleife beendet ist (weil CheckCondition eskaliert wurde). Mit diesem Callback modify_output_after_agent
wird die endgültige Zusammenfassung aus dem Status abgerufen und als endgültige Ausgabenachricht des Agenten formatiert.
👉📝 Ersetzen Sie in derselben ~/instavibe-bootstrap/agents/social/agent.py
#REPLACE FOR modify_output_after_agent
durch Folgendes:
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
Root Loop-Agent definieren
Schließlich definieren wir den Haupt-LoopAgent. Er orchestriert die untergeordneten Agents sequenziell in jeder Schleifeniteration (profile_agent –> summary_agent –> check_agent –> CheckCondition). Diese Sequenz wird bis zu max_iterations Mal wiederholt oder bis CheckCondition den Abschluss signalisiert. Der after_agent_callback sorgt dafür, dass die endgültige Zusammenfassung zurückgegeben wird.
👉📝 Ersetzen Sie in derselben ~/instavibe-bootstrap/agents/social/agent.py
#REPLACE FOR root_agent
durch Folgendes:
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
)
Wir testen diesen Multi-Agent-Workflow mit der ADK Dev UI.
👉💻 ADK-Webserver starten:
. ~/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
Öffnen Sie die ADK-Entwickler-UI (Port 8000 über die Webvorschau). Wählen Sie im Drop-down-Menü für den Kundenservicemitarbeiter (oben rechts) den Social-Kundenservicemitarbeiter aus.
👉 Weisen Sie der KI nun die Aufgabe zu, Profile für mehrere Personen zu erstellen. Geben Sie im Chatdialog Folgendes ein:
Tell me about Mike and Bob
Nachdem der Kundenservicemitarbeiter geantwortet hat (was aufgrund der Schleifen und mehrerer LLM-Aufrufe etwas länger dauern kann), sollten Sie sich nicht nur die endgültige Chatausgabe ansehen. Rufen Sie im linken Bereich der ADK Dev UI den Tab „Events“ auf.
👉 Bestätigungsschritt: Auf dem Tab „Events“ (Ereignisse) sehen Sie eine detaillierte, schrittweise Ablaufverfolgung der Ausführung.
Beobachten Sie, wie der Agent die einzelnen untergeordneten Agents aufruft. Der erwartete Ablauf ist „profile_agent“ -> „summary_agent“ -> „check_agent“, wobei in jeder Iteration der Checker ausgeführt wird. In der Praxis sehen wir jedoch die leistungsstarke „Selbstoptimierung“ des Agents in Aktion.
Da das zugrunde liegende Modell die gesamte Anfrage sieht (z.B. „profile Mike and Bob“), wählt es oft den effizientesten Pfad aus und erfasst alle erforderlichen Daten in einem einzigen, konsolidierten Zug, anstatt mehrmals zu iterieren. Sie können die Ein- und Ausgaben sowie die Status für jeden Schritt sehen, einschließlich der von „profile_agent“ ausgeführten Tool-Aufrufe.
und die Statusaktualisierungen von check_agent und CheckCondition.
Diese visuelle Ablaufverfolgung ist von unschätzbarem Wert, um zu verstehen und zu debuggen, wie der Multi-Agent-Workflow funktioniert, bis die endgültige Zusammenfassung generiert und vom Callback zurückgegeben wird.
Nachdem Sie die Chatantwort und den Ereignistrace untersucht haben, kehren Sie zum Cloud Shell-Terminal zurück und drücken Sie Ctrl+C
, um die ADK Dev UI zu beenden.
10. Agent-to-Agent-Kommunikation (A2A)
Bisher haben wir spezialisierte Agents entwickelt, die jedoch isoliert oder innerhalb eines vordefinierten Workflows auf derselben Maschine ausgeführt werden. Um wirklich verteilte und kollaborative Multi-Agent-Systeme zu entwickeln, benötigen wir eine Möglichkeit, mit der Agents, die möglicherweise als separate Dienste ausgeführt werden, sich gegenseitig erkennen und effektiv kommunizieren können. Hier kommt das Agent-to-Agent-Protokoll (A2A) ins Spiel.
Das A2A-Protokoll ist ein offener Standard, der speziell für die interoperable Kommunikation zwischen KI-Agenten entwickelt wurde. Während sich MCP auf die Interaktion zwischen Agent und Tool konzentriert, liegt der Fokus bei A2A auf der Interaktion zwischen Agenten. Damit können Kundenservicemitarbeiter:
- Entdecken: Über standardisierte Agent-Karten können Sie andere Agents finden und ihre Funktionen kennenlernen.
- Kommunizieren: Nachrichten und Daten sicher austauschen.
- Zusammenarbeiten: Aufgaben delegieren und Aktionen koordinieren, um komplexe Ziele zu erreichen.
Das A2A-Protokoll erleichtert diese Kommunikation durch Mechanismen wie „Agent Cards“, mit denen Agenten ihre Funktionen und Verbindungsinformationen bewerben können.
A2A nutzt bekannte Webstandards (HTTP, SSE, JSON-RPC) und verwendet häufig ein Client-Server-Modell, bei dem ein Agent (Client) Aufgaben an einen anderen (Remote-Agent/Server) sendet. Diese Standardisierung ist entscheidend für die Entwicklung modularer, skalierbarer Systeme, in denen unabhängig entwickelte Agents zusammenarbeiten können.
A2A für InstaVibe-Agents aktivieren
Damit unsere vorhandenen Planner-, Platform Interaction- und Social-Agents über A2A auf andere Agents zugreifen können, müssen wir jeden Agenten mit einer A2A-Serverkomponente umschließen. Dieser Server:
- Agent-Karte bereitstellen: Stellen Sie eine Standardbeschreibung der Funktionen des Agents über einen HTTP-Endpunkt bereit.
- Aufgaben(Anfragenachrichten) abhören: Eingehende Aufgabenanfragen von anderen Agents (A2A-Clients) gemäß dem A2A-Protokoll annehmen.
- Manage Task(Request Messages) Execution (Ausführung von Aufgaben (Anfragenachrichten) verwalten): Übergeben Sie empfangene Aufgaben zur Verarbeitung an die zugrunde liegende ADK-Agent-Logik.
Planner Agent (A2A-fähig)
Fügen wir unserem Planner-Agent zuerst die A2A-Serverebene hinzu.
Definieren Sie die A2A-Server-Startlogik. Mit diesem Code wird die AgentCard (die öffentliche Beschreibung des Agents) definiert, der A2AServer konfiguriert und gestartet und mit dem PlatformAgentExecutor verknüpft.
👉📝 Fügen Sie am Ende von ~/instavibe-bootstrap/agents/planner/a2a_server.py
den folgenden Code ein:
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)
👉💻 Wir testen schnell, ob der A2A-Server lokal richtig gestartet wird und seine Agent Card bereitstellt. Führen Sie im ersten Terminal den folgenden Befehl aus:
. ~/instavibe-bootstrap/set_env.sh
source ~/instavibe-bootstrap/env/bin/activate
cd ~/instavibe-bootstrap/agents/
python -m planner.a2a_server
👉 Öffnen Sie nun ein weiteres Terminalfenster. Klicken Sie im Terminalbereich auf das Pluszeichen (+).
👉💻 Verwenden Sie „curl“, um die Agent-Karte vom lokal ausgeführten Server anzufordern:
curl http://localhost:10003/.well-known/agent.json | jq
Sie sollten die JSON-Darstellung der von uns definierten AgentCard sehen. Das bestätigt, dass der Server ausgeführt wird und der Planner-Agent beworben wird.
Kehren Sie zum ersten Terminal zurück (auf dem der Server ausgeführt wird) und drücken Sie Ctrl+C
, um ihn zu beenden.
👉💻 Nachdem die A2A-Serverlogik hinzugefügt wurde, können wir jetzt das Container-Image erstellen.
Planner-Agent erstellen und bereitstellen
. ~/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}"
👉💻 Stellen Sie den Planner-Agent in Cloud Run bereit.
. ~/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
Prüfen wir mit dem A2A Inspector, ob der bereitgestellte Dienst ausgeführt wird und die Agent-Karte korrekt aus der Cloud bereitstellt.
👉 Wählen Sie in der Symbolleiste von Cloud Shell über das Symbol „Webvorschau“ die Option „Port ändern“ aus. Legen Sie den Port auf 8081 fest und klicken Sie auf „Ändern und Vorschau“. Ein neuer Browsertab mit der A2A Inspector-Oberfläche wird geöffnet.
👉💻 Rufen Sie im Terminal die URL Ihres bereitgestellten Planner-Agents ab:
export PLANNER_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep planner-agent)
echo ${PLANNER_AGENT_URL}
👉💻 Kopieren Sie die Ausgabewebadresse.
👉 Fügen Sie die URL in der A2A Inspector-Benutzeroberfläche in das Feld „Agent URL“ ein und klicken Sie auf „Connect“.
👀 Die Kartendetails und der JSON-Code des Agents sollten auf dem Tab „Agent Card“ (Agent-Karte) angezeigt werden. Das bestätigt, dass die Verbindung hergestellt wurde.
👉 Klicken Sie im A2A Inspector auf den Tab „Chat“. Hier können Sie direkt mit Ihrem bereitgestellten Agent interagieren. Senden Sie ihm eine Nachricht, um seine Planungsfunktionen zu testen. Beispiel:
Plan something for me in Boston MA this weekend, and I enjoy classical music
👀 Wenn Sie die Rohkommunikation ansehen möchten, klicken Sie im Chatfenster auf Ihre Nachrichtenblase und dann auf die Antwortblase des Kundenservicemitarbeiters. Wenn Sie auf die einzelnen Elemente klicken, wird die vollständige JSON-RPC 2.0-Nachricht angezeigt, die gesendet oder empfangen wurde. Das ist sehr hilfreich beim Debuggen.
Lassen Sie den Tab „A2A Inspector“ geöffnet. Schließen Sie es NICHT! Wir werden es gleich noch einmal verwenden, um unsere beiden anderen Kundenservicemitarbeiter zu testen.
Platform Interaction Agent (A2A-fähig)
Als Nächstes wiederholen wir den Vorgang für den Platform Interaction Agent (der MCP verwendet).
👉📝 Definieren Sie die A2A-Serverkonfiguration, einschließlich der eindeutigen AgentCard, am Ende von ~/instavibe-bootstrap/agents/platform_mcp_client/a2a_server.py
:
class PlatformAgent:
"""An agent that post event and post to instavibe."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="instavibe_posting",
name="Post social post and events on instavibe",
description="""
This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
to perform these actions on your behalf, ensuring a smooth sharing experience.
""",
tags=["instavibe"],
examples=["Create a post for me, the post is about my cute cat and make it positive, and I'm Alice"],
)
self.agent_card = AgentCard(
name="Instavibe Posting Agent",
description="""
This "Instavibe" agent helps you create posts (identifying author, text, and sentiment – inferred if unspecified) and register
for events (gathering name, date, attendee). It efficiently collects required information and utilizes dedicated tools
to perform these actions on your behalf, ensuring a smooth sharing experience.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=PlatformAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the social post and event request..."
def _build_agent(self) -> LlmAgent:
"""Builds the LLM agent for the Processing the social post and event request."""
return agent.root_agent
if __name__ == '__main__':
try:
platformAgent = PlatformAgent()
request_handler = DefaultRequestHandler(
agent_executor=PlatformAgentExecutor(platformAgent.runner,platformAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=platformAgent.agent_card,
http_handler=request_handler,
)
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
Social Agent (A2A-fähig)
Aktivieren wir nun A2A für unseren Social Profiling-Agent.
👉📝 Definieren Sie die A2A-Serverkonfiguration und die AgentCard am Ende von ~/instavibe-bootstrap/agents/social/a2a_server.py
:
class SocialAgent:
"""An agent that handles social profile analysis."""
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
def __init__(self):
self._agent = self._build_agent()
self.runner = Runner(
app_name=self._agent.name,
agent=self._agent,
artifact_service=InMemoryArtifactService(),
session_service=InMemorySessionService(),
memory_service=InMemoryMemoryService(),
)
capabilities = AgentCapabilities(streaming=True)
skill = AgentSkill(
id="social_profile_analysis",
name="Analyze Instavibe social profile",
description="""
Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
and connections based on profile data.
""",
tags=["instavibe"],
examples=["Can you tell me about Bob and Alice?"],
)
self.agent_card = AgentCard(
name="Social Profile Agent",
description="""
Using a provided list of names, this agent synthesizes Instavibe social profile information by analyzing posts, friends, and events.
It delivers a comprehensive single-paragraph summary for individuals, and for groups, identifies commonalities in their social activities
and connections based on profile data.
""",
url=f"{PUBLIC_URL}",
version="1.0.0",
defaultInputModes=self.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=self.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=[skill],
)
def get_processing_message(self) -> str:
return "Processing the social profile analysis request..."
def _build_agent(self) -> LoopAgent:
"""Builds the LLM agent for the social profile analysis agent."""
return agent.root_agent
if __name__ == '__main__':
try:
socialAgent = SocialAgent()
request_handler = DefaultRequestHandler(
agent_executor=SocialAgentExecutor(socialAgent.runner,socialAgent.agent_card),
task_store=InMemoryTaskStore(),
)
server = A2AStarletteApplication(
agent_card=socialAgent.agent_card,
http_handler=request_handler,
)
uvicorn.run(server.build(), host='0.0.0.0', port=port)
except Exception as e:
logger.error(f"An error occurred during server startup: {e}")
exit(1)
Plattforminteraktions- und Social-Agents erstellen und bereitstellen
Diese Agents benötigen Zugriff auf Spanner. Achten Sie daher darauf, dass die Umgebungsvariablen SPANNER_INSTANCE_ID
, SPANNER_DATABASE_ID
und MCP_SERVER_URL
während der Bereitstellung korrekt übergeben werden.
👉💻 Mit Cloud Build erstellen und in Cloud Run bereitstellen:
. ~/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}"
👉💻 Rufen Sie im Terminal die URL Ihres bereitgestellten Plattform-Agents ab:
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
👉💻 Kopieren Sie die Ausgabewebadresse.
👉 Fügen Sie die URL in der A2A Inspector-Benutzeroberfläche in das Feld „Agent URL“ ein und klicken Sie auf „Connect“.
👀 Die Kartendetails und der JSON-Code des Agents sollten auf dem Tab „Agent Card“ (Agent-Karte) angezeigt werden. Das bestätigt, dass die Verbindung hergestellt wurde.
👉 Klicken Sie im A2A Inspector auf den Tab „Chat“. Hier können Sie direkt mit Ihrem bereitgestellten Agent interagieren. Senden Sie ihm eine Nachricht, um seine Fähigkeit zu testen, Beiträge zu erstellen:
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.
👀 Wenn Sie die Rohkommunikation ansehen möchten, klicken Sie im Chatfenster auf Ihre Nachrichtenblase und dann auf die Antwortblase des Kundenservicemitarbeiters. Wenn Sie auf die einzelnen Elemente klicken, wird die vollständige JSON-RPC 2.0-Nachricht angezeigt, die gesendet oder empfangen wurde. Das ist sehr hilfreich beim Debuggen.
👉💻 Rufen Sie im Terminal die URL Ihres bereitgestellten Social-Media-Agents ab:
export SOCIAL_AGENT_URL=$(gcloud run services list --platform=managed --region=us-central1 --format='value(URL)' | grep social-agent)
echo $SOCIAL_AGENT_URL
👉💻 Kopieren Sie die Ausgabewebadresse.
👉 Fügen Sie die URL in der A2A Inspector-Benutzeroberfläche in das Feld „Agent URL“ ein und klicken Sie auf „Connect“.
👀 Die Kartendetails und der JSON-Code des Agents sollten auf dem Tab „Agent Card“ (Agent-Karte) angezeigt werden. Das bestätigt, dass die Verbindung hergestellt wurde.
👉 Klicken Sie im A2A Inspector auf den Tab „Chat“. Hier können Sie direkt mit Ihrem bereitgestellten Agent interagieren. Senden Sie ihm eine Nachricht, um Nutzerprofile aus Ihrer Datenbank zu analysieren:
Can you tell me about both Ian and Kevin's profile, what are their common interests?
👀 Wenn Sie die Rohkommunikation ansehen möchten, klicken Sie im Chatfenster auf Ihre Nachrichtenblase und dann auf die Antwortblase des Kundenservicemitarbeiters. Wenn Sie auf die einzelnen Elemente klicken, wird die vollständige JSON-RPC 2.0-Nachricht angezeigt, die gesendet oder empfangen wurde. Das ist sehr hilfreich beim Debuggen.
👉 Wir haben alle unsere Kundenservicemitarbeiter überprüft. Sie können den Tab „A2A Inspector“ jetzt schließen.
11. Orchestrator-Agent (A2A-Client)
Wir haben jetzt drei spezialisierte Agents (Planner, Platform, Social), die als unabhängige, A2A-fähige Dienste in Cloud Run ausgeführt werden. Die letzte Komponente ist der Orchestrator-Agent. Dieser Agent fungiert als zentraler Koordinator oder A2A-Client. Es empfängt Nutzeranfragen, ermittelt, welche Remote-Agents zur Erfüllung der Anfrage erforderlich sind (möglicherweise nacheinander), und delegiert dann Aufgaben mithilfe des A2A-Protokolls an diese Remote-Agents. In diesem Workshop führen wir den Orchestrator-Agenten lokal über die ADK Dev UI aus.
Zuerst müssen wir die Logik des Orchestrators erweitern, damit er die Registrierung der erkannten Remote-Agents verarbeiten kann. Speichert die Verbindungsdetails aus den abgerufenen Agent-Karten während der Initialisierung.
👉📝 Ersetzen Sie in ~/instavibe-bootstrap/agents/orchestrate/agent.py
#REPLACE ME REG AGENT CARD
durch:
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)
Definieren Sie als Nächstes das Tool für den Orchestrator-Agenten selbst im ADK.
send_message
(die A2A-Funktion zum Delegieren von Aufgaben).
👉📝 Ersetzen Sie #REPLACE ME CREATE AGENT
in ~/instavibe-bootstrap/agents/orchestrate/agent.py
durch:
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],
)
Die Kernlogik des Orchestrators liegt in seinen Anweisungen, die ihm mitteilen, wie A2A verwendet werden soll.
👉📝 Ersetzen Sie #REPLACE ME INSTRUCTIONS
in ~/instavibe-bootstrap/agents/orchestrate/agent.py
durch diese Methode zum Generieren von Anleitungen:
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']}`
"""
Orchestrator und vollständiges A2A-System testen
Jetzt testen wir das gesamte System. Wir führen den Orchestrator lokal über die ADK Dev UI aus. Er kommuniziert mit den Planner-, Platform- und Social-Agents, die remote in Cloud Run ausgeführt werden.
👉💻 Prüfe zuerst, ob die Umgebungsvariable REMOTE_AGENT_ADDRESSES
die durch Kommas getrennten URLs deiner bereitgestellten A2A-fähigen Agents enthält. Legen Sie dann die erforderlichen Umgebungsvariablen für den Orchestrator-Agent fest und starten Sie die 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
👉 Öffne die ADK Dev-Benutzeroberfläche (ändere den Port über die Webvorschau wieder zu 8000).
👉 Wählen Sie im Drop-down-Menü für Agents den Agent orchestrate aus.
👉 Geben Sie ihm nun eine komplexe Aufgabe, für die mehrere Remote-Agents koordiniert werden müssen. Probieren Sie dieses erste Beispiel aus, bei dem zuerst der Social Agent und dann der Planner Agent zum Einsatz kommen:
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.
Beobachten Sie die Interaktion im Chatfenster der ADK Dev UI. Achten Sie genau auf die Antworten des Orchestrators. Er sollte angeben, an welchen Remote-Agenten er Aufgaben delegiert (z. B. „Okay, ich frage zuerst den Social Profile Agent nach Ian und Nora…“).
Sehen Sie sich außerdem auf dem Tab „Events“ in der Benutzeroberfläche die zugrunde liegenden Tool-Aufrufe (send_message) an, die an die URLs der Remote-Agents gesendet werden.
👉 Probieren Sie nun ein zweites Beispiel aus, bei dem der Platform Integration Agent direkt beteiligt sein sollte:
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
Behalten Sie den Chat und den Tab „Events“ im Blick. Der Orchestrator sollte erkennen, dass ein Ereignis erstellt werden muss, und die Aufgabe (mit allen bereitgestellten Details) an den „Platform Integration Agent“ delegieren. Sie können auch auf die Schaltfläche Trace klicken, um Traces aufzurufen und so die Antwortzeiten von Abfragen und die ausgeführten Vorgänge zu analysieren.
Anschließend können Sie prüfen, ob das Ereignis in der InstaVibe-Webanwendung angezeigt wird.
Dies zeigt die erfolgreiche Implementierung eines Multi-Agent-Systems mit dem ADK und dem A2A-Protokoll, bei dem ein zentraler Orchestrator Aufgaben an spezialisierte Remote-Agents delegiert.
Denken Sie daran, die ADK Dev UI (Ctrl+C
im Terminal) zu beenden, wenn Sie mit dem Testen fertig sind.
12. Agent Engine und Remote Call von InstaVibe
Bisher haben wir unsere spezialisierten Agents in Cloud Run ausgeführt und den Orchestrator lokal mit der ADK Dev UI getestet. Für ein Produktionsszenario benötigen wir eine robuste, skalierbare und verwaltete Umgebung zum Hosten unserer Agents. Hier kommt Google Vertex AI Agent Engine ins Spiel.
Agent Engine ist ein vollständig verwalteter Dienst in Vertex AI, der speziell für die Bereitstellung und Skalierung von KI-Agenten entwickelt wurde. Sie abstrahiert die Infrastrukturverwaltung, Sicherheit und den Betriebsaufwand, sodass sich Entwickler (insbesondere solche, die weniger mit komplexen Cloud-Umgebungen vertraut sind) auf die Logik und die Funktionen des Agents konzentrieren können, anstatt Server zu verwalten. Sie bietet eine dedizierte Laufzeit, die für agentische Arbeitslasten optimiert ist.
Als Nächstes stellen wir unseren Orchestrator-Agent in Agent Engine bereit. Hinweis: Der unten gezeigte Bereitstellungsmechanismus verwendet ein benutzerdefiniertes Skript (agent_engine_app.py), das in den Workshop-Materialien enthalten ist, da sich die offiziellen Tools für die direkte Bereitstellung von ADK zu Agent Engine möglicherweise noch in der Entwicklung befinden. Dieses Skript übernimmt das Verpacken und Bereitstellen des Orchestrator-Agents, der mit den erforderlichen Remote-Agent-Adressen konfiguriert ist.
Führen Sie den folgenden Befehl aus, um den Orchestrator-Agent in Agent Engine bereitzustellen. Achten Sie darauf, dass die Umgebungsvariable REMOTE_AGENT_ADDRESSES (mit den URLs Ihrer Planner-, Platform- und Social-Agents in Cloud Run) aus dem vorherigen Abschnitt weiterhin korrekt festgelegt ist.
👉💻 Wir stellen den Orchestrate-Agent in Agent Engine bereit. Hinweis: Dies ist meine eigene Implementierung der Bereitstellung. Das ADK hat eine CLI, die bei der Bereitstellung hilft. Ich werde dies aktualisieren, nachdem BYO-SA implementiert wurde.
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
Da der Orchestrator jetzt auf der verwalteten Agent Engine-Plattform gehostet wird, muss unsere InstaVibe-Webanwendung mit ihm kommunizieren. Anstatt über die ADK Dev UI zu interagieren, führt die Web-App Remote-Aufrufe an den Agent Engine-Endpunkt aus.
Zuerst müssen wir den InstaVibe-Anwendungscode ändern, um den Agent Engine-Client mit der eindeutigen ID unseres bereitgestellten Orchestrator-Agents zu initialisieren. Diese ID ist erforderlich, um die richtige Agent-Instanz auf der Plattform anzusprechen.
👉📝 Öffnen Sie ~/instavibe-bootstrap/instavibe/introvertally.py
und ersetzen Sie #REPLACE ME initiate agent_engine
durch den folgenden Code. Dadurch wird die Agent Engine-ID aus einer Umgebungsvariable abgerufen (die wir gleich festlegen) und ein Clientobjekt wird abgerufen:
ORCHESTRATE_AGENT_ID = os.environ.get('ORCHESTRATE_AGENT_ID')
agent_engine = agent_engines.get(ORCHESTRATE_AGENT_ID)
Unser geplanter Nutzerfluss in InstaVibe umfasst zwei Interaktionen mit dem Agent: Zuerst wird der empfohlene Plan generiert und dann wird der Nutzer aufgefordert, ihn zu bestätigen, bevor der Agent das Event auf der Plattform postet.
Da die InstaVibe-Webanwendung (die in Cloud Run ausgeführt wird) und der Orchestrator-Agent (der in Agent Engine ausgeführt wird) jetzt separate Dienste sind, muss die Webanwendung Remote-Aufrufe an den Agent Engine-Endpunkt senden, um mit dem Agent zu interagieren.
👉📝 Aktualisieren wir den Code, mit dem der erste Aufruf zum Generieren der Planempfehlung erfolgt. Ersetzen Sie in derselben Datei introvertally.py
den #REPLACE ME Query remote agent get plan
durch das folgende Snippet, in dem der Client „agent_engine“ verwendet wird, um die Anfrage des Nutzers zu senden:
agent_engine.stream_query(
user_id=user_id,
message=prompt_message,
)
👉📝 Aktualisieren Sie als Nächstes den Code, der die Bestätigung des Nutzers verarbeitet (z.B. wenn der Nutzer auf „Abo bestätigen“ klickt). Dadurch wird eine Follow-up-Nachricht an dieselbe Unterhaltung in Agent Engine gesendet, in der der Orchestrator angewiesen wird, mit dem Posten des Ereignisses fortzufahren. Diese Aufgabe wird an den Platform Integration-Agent delegiert. Ersetzen Sie #REPLACE ME Query remote agent for confirmation
zur Bestätigung in introvertally.py
durch:
agent_engine.stream_query(
user_id=agent_session_user_id,
message=prompt_message,
)
Die Routen der Webanwendung benötigen Zugriff auf diese Funktionen. Achten Sie darauf, dass die erforderlichen Funktionen aus „introvertally.py“ in die Datei mit den Flask-Routen importiert werden.
👉📝 In cd ~/instavibe-bootstrap/instavibe/ally_routes.py
verweisen wir zuerst auf den Instanzersatz # REPLACE ME TO ADD IMPORT
mit Folgendem:
from introvertally import call_agent_for_plan, post_plan_event
👉📝 Fügen Sie die Prototypfunktion zu InstaVibe hinzu. Ersetzen Sie in ~/instavibe-bootstrap/instavibe/templates/base.html
<!–REPLACE_ME_LINK_TO_INTROVERT_ALLY–> durch Folgendes:
<li class="nav-item">
<a class="nav-link" href="{{ url_for('ally.introvert_ally_page') }}">Introvert Ally</a>
</li>
Bevor wir die InstaVibe App neu bereitstellen können, benötigen wir die spezifische Resource ID
des Orchestrator-Agents, den wir in Agent Engine bereitgestellt haben.
Derzeit ist das programmgesteuerte Abrufen über gcloud
möglicherweise eingeschränkt. Daher verwenden wir ein Python-Hilfsskript (temp-endpoint.py
, das im Workshop bereitgestellt wird), um die ID abzurufen und in einer Umgebungsvariablen zu speichern.
👉💻 Führen Sie die folgenden Befehle aus, um das Skript auszuführen. Das Skript erfasst die Agent Engine-Endpunkt-ID und gewährt dem Standarddienstkonto der Agent Engine die erforderlichen Berechtigungen. Hinweis: Das Skript ist für die Verwendung des Standarddienstkontos konfiguriert, da es derzeit nicht vom Nutzer geändert werden kann.
. ~/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}"
Schließlich müssen wir die InstaVibe-Webanwendung mit dem aktualisierten Code und der neuen Umgebungsvariable ORCHESTRATE_AGENT_ID
neu bereitstellen, damit sie weiß, wie sie eine Verbindung zu unserem Agent herstellt, der in Agent Engine ausgeführt wird.
👉💻 Mit den folgenden Befehlen wird das InstaVibe-Anwendungs-Image neu erstellt und die neue Version in Cloud Run bereitgestellt:
. ~/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
Nachdem die endgültige Bereitstellung abgeschlossen ist, rufen Sie die URL Ihrer InstaVibe-Anwendung in einem anderen Browsertab auf.
Vollständig KI-basierte InstaVibe-Funktion testen
Die Funktion „InstaVibe Ally“ ist jetzt verfügbar. Sie basiert auf unserem Multi-Agent-System, das über die Vertex AI Agent Engine orchestriert wird und über A2A kommuniziert.
Klicken Sie auf „InstaVibe Ally“ und bitten Sie den Assistenten, ein Event zu planen.
Beobachten Sie das Aktivitätsprotokoll auf der rechten Seite, während die Agents arbeiten. Das kann 90 bis 120 Sekunden dauern. Sobald der Plan angezeigt wird, sieh ihn dir an und klicke auf „Diesen Plan bestätigen“, um mit dem Posten fortzufahren.
Der Orchestrator weist den Plattform-Agent nun an, den Beitrag und das Ereignis in InstaVibe zu erstellen.
Auf der InstaVibe-Startseite findest du den neuen Beitrag und das neue Event.
Auf der Ereignisseite werden die vom Kundenservicemitarbeiter generierten Details angezeigt.
Leistung mit Cloud Trace analysieren
Der Vorgang kann einige Zeit dauern. Vertex AI Agent Engine ist in Cloud Trace integriert, sodass wir die Latenz unseres Multi-Agent-Systems analysieren können.
Rufen Sie in der Google Cloud Console die Traces auf und wählen Sie im Bereich agent_run[orchestrate_agent]
einige Spans aus. Klicken Sie darauf.
In den Trace-Details können Sie sehen, welche Teile länger gedauert haben. Beispielsweise kann es bei Aufrufen des Planner-Agents aufgrund von Suchanpassung und komplexer Generierung zu einer höheren Latenz kommen.
Beim Erstellen des Beitrags und des Ereignisses kann es ebenfalls zu Wartezeiten kommen, während der Orchestrator Daten verarbeitet und Tool-Aufrufe für den Plattform-Agent vorbereitet.
Wenn Sie diese Traces untersuchen, können Sie die Leistung Ihres Agent-Systems besser nachvollziehen und optimieren.
Glückwunsch! Sie haben mithilfe von ADK, A2A, MCP und Google Cloud-Diensten von Google ein komplexes KI-System mit mehreren Agents entwickelt, bereitgestellt und getestet. Sie haben sich mit der Agent-Orchestrierung, der Verwendung von Tools, der Statusverwaltung und der Cloud-Bereitstellung befasst und so eine funktionale KI-basierte Funktion für InstaVibe erstellt. Herzlichen Glückwunsch zum Abschluss des Workshops!
13. Bereinigen
Damit Ihrem Google Cloud-Konto keine laufenden Gebühren in Rechnung gestellt werden, ist es wichtig, die Ressourcen zu löschen, die wir während dieses Workshops erstellt haben. Mit den folgenden Befehlen können Sie die Spanner-Instanz, Cloud Run-Dienste, das Artifact Registry-Repository, den API-Schlüssel, Vertex AI Agent Engine und die zugehörigen IAM-Berechtigungen entfernen.
Wichtig:
- Achten Sie darauf, dass Sie diese Befehle im selben Google Cloud-Projekt ausführen, das für den Workshop verwendet wird.
- Wenn Sie Ihr Cloud Shell-Terminal geschlossen haben, sind einige Umgebungsvariablen wie $PROJECT_ID, $SPANNER_INSTANCE_ID usw. möglicherweise nicht festgelegt. Sie müssen sie entweder wie bei der Einrichtung des Workshops noch einmal exportieren oder die Variablen in den folgenden Befehlen durch die tatsächlichen Werte ersetzen.
- Durch diese Befehle werden Ihre Ressourcen endgültig gelöscht. Prüfen Sie vor dem Ausführen, ob sich in diesem Projekt andere wichtige Daten befinden.
👉💻 Führen Sie die folgenden Skripts aus, um die Bereinigung durchzuführen.
Umgebungsvariablen zurücksetzen
. ~/instavibe-bootstrap/set_env.sh
Agent Engine löschen:
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."
Cloud Run-Dienste löschen:
# 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."
A2A Inspector-Docker-Container beenden und entfernen
docker rm --force a2a-inspector
Spanner-Instanz löschen:
echo "Deleting Spanner instance: ${SPANNER_INSTANCE_ID}..."
gcloud spanner instances delete ${SPANNER_INSTANCE_ID} --project=${PROJECT_ID} --quiet
echo "Spanner instance deletion initiated."
Artifact Registry-Repository löschen:
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."
Rollen aus dem Dienstkonto entfernen:
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."
Lokale Workshop-Dateien löschen:
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."