1. Übersicht
In diesem Codelab erstellen Sie AI Creative Studio, ein verteiltes Multi-Agenten-System, das aus einem einzelnen Prompt eine vollständige Instagram-Kampagne generiert.
Geben Sie einen Satz ein. Sie erhalten Zielgruppenanalysen, Untertitel, visuelle Konzepte, qualitätsgeprüfte Texte und einen vollständigen Projektzeitplan – alles generiert von einem Team aus zusammenarbeitenden KI-Agenten.
Die Agents, die Sie erstellen
Agent | Rolle |
Brand Strategist | Sucht im Web nach Zielgruppenstatistiken, Wettbewerbsanalysen und Trends für 2025 |
Copywriter | Instagram-Bildunterschriften mit Hashtags und CTAs schreiben – basierend auf einem ADK-Skill, der Plattformrichtlinien und Formeln für Bildunterschriften bei Bedarf lädt |
Designer | Erstellt visuelle Konzepte und generiert über Gemini echte Bilder, die in GCS gespeichert werden |
Kritiker | Rezensionstext und ‑bilder – gibt |
Projektmanager | Erstellt einen Projektzeitplan und eine Aufschlüsselung der Aufgaben, die optional über MCP mit Notion synchronisiert werden. |
Creative Director | Orchestrierung aller fünf Spezialisten in der richtigen Reihenfolge – Sie geben einen Prompt ein, den Rest koordiniert Gemini. |
Die fünf Agents werden als unabhängige Cloud Run-Mikrodienste bereitgestellt. Sie kommunizieren über das A2A-Protokoll – einen sprachunabhängigen offenen Standard, sodass jeder Agent unabhängig vom Framework jeden anderen Agenten aufrufen kann. Der Creative Director wird in der Agent Runtime ausgeführt und stellt eine Remote-Verbindung zu den einzelnen Spezialisten her.
Architektur

Lerninhalte
- LLM-Agents mit dem Google ADK erstellen –
Agent, Systemanweisungen und integrierte Tools. - Verpacken Sie wiederverwendbares Agentenwissen in modulare Dateien mit ADK-Skills (
SkillToolset). - Echte Bilder generieren, indem Sie einen Text-Agent mit einem Bildmodell über eine
FunctionToolverbinden. - Externe APIs ohne benutzerdefinierten Glue-Code mit dem Model Context Protocol (MCP) einbinden
- Mit dem Agent-to-Agent-Protokoll (A2A) über HTTPS können Sie jeden Agenten in einen netzwerkfähigen Dienst umwandeln.
- Verteilte Agents mit
RemoteA2aAgentundAgentToolorchestrieren. - Verpacken und stellen Sie unabhängige Agents als Cloud Run-Mikrodienste bereit.
- Hosten Sie einen zustandsbehafteten Orchestrator in der Agent Runtime.
- Mit Kontextverdichtung können Sie lange Multi-Agenten-Workflows innerhalb der Kontextgrenzen halten.
- Erstellen Sie einen Qualitätskontrollzyklus: Überprüfen Sie die Ausgabe kritisch → nehmen Sie bei Bedarf automatische Überarbeitungen vor.
Voraussetzungen
- Ein Google Cloud-Projekt mit aktivierter Abrechnung
- IAM-Rolle Inhaber oder Bearbeiter
- Grundlegende Python-Kenntnisse
2. Umgebung einrichten
In diesem Codelab verwenden wir Cloud Shell.
Was ist Cloud Shell?
Cloud Shell ist eine kostenlose browserbasierte Linux-Umgebung, in der alles vorinstalliert ist: gcloud, git, Python, Docker und mehr. Sie müssen nichts lokal installieren.
Klicken Sie zum Öffnen von Cloud Shell in der Symbolleiste rechts oben in der GCP Console auf das Terminalsymbol:

Wenn Sie Cloud Shell zum ersten Mal öffnen, werden Sie aufgefordert, Ihr Konto zu bestätigen. Klicken Sie auf Bestätigen:

Klicken Sie dann auf Autorisieren, um Cloud Shell für Google Cloud API-Aufrufe zu autorisieren:

Cloud Shell ist jetzt bereit. Im Terminal wird eine Willkommensnachricht angezeigt: 
Projekt authentifizieren und konfigurieren
Cloud Shell ist bereits mit Ihrem Google-Konto authentifiziert. Bestätigen Sie Ihr aktives Konto und suchen Sie Ihre Projekt-ID:
gcloud config list
Sie können Ihre Projekt-ID auch im GCP Console-Dashboard in der linken Seitenleiste sehen. Kopieren Sie sie, da Sie sie im nächsten Befehl benötigen:

Legen Sie jetzt Ihr Projekt fest:
export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-central1" # Cloud Run deployment region
echo "Project: $PROJECT_ID"
Erwartete Ausgabe:
Project: my-project-123
Erforderliche APIs aktivieren
gcloud services enable \
aiplatform.googleapis.com \
apphub.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
generativelanguage.googleapis.com \
iam.googleapis.com \
cloudresourcemanager.googleapis.com \
storage.googleapis.com \
secretmanager.googleapis.com
Das dauert etwa zwei Minuten. Wenn der Vorgang abgeschlossen ist, wird Operation finished successfully angezeigt.
Standardanmeldedaten für Anwendungen (Application Default Credentials, ADC) einrichten
Die Agents rufen die Gemini Enterprise Agent Platform über die Google Auth-Bibliothek auf. Dazu sind Standardanmeldedaten für Anwendungen erforderlich, die sich von der gcloud CLI-Authentifizierung unterscheiden.
Führen Sie diesen Befehl einmal aus:
gcloud auth application-default login
Ein Browsertab wird geöffnet, in dem Sie den Vorgang bestätigen müssen. Klicken Sie auf Zulassen. Sie sehen hier Folgendes:
Credentials saved to file: ~/.config/gcloud/application_default_credentials.json
Starter-Repository klonen
In diesem Codelab wird ein Starter-Repository verwendet – ein Skelettprojekt mit der gesamten Infrastruktur (Dockerfiles, pyproject.toml, Bereitstellungsskripts), in dem Sie die Agentenlogik selbst schreiben müssen.
git clone https://github.com/Saoussen-CH/mas-a2a-gcp.git ~/ai-creative-studio
cd ~/ai-creative-studio/workshop/starter
Jede agent.py enthält # TODO-Platzhalter, in die Sie die Agent-Logik schreiben. Die Skripts Dockerfile, pyproject.toml und das Bereitstellungsskript sind bereits fertig.
Umgebungsvariablen konfigurieren
Kopieren Sie das bereitgestellte Beispiel und fügen Sie Ihre Projekt-ID in einem Schritt ein:
cp .env.example .env
sed -i "s|GOOGLE_CLOUD_PROJECT=your-project-id|GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)|" .env
Erstellen Sie dann den GCS-Bucket, in dem der Designer generierte Bilder speichert, und aktualisieren Sie .env mit seinem Namen:
export PROJECT_ID=$(gcloud config get-value project)
export BUCKET_NAME="${PROJECT_ID}-campaign-images"
gcloud storage buckets create gs://${BUCKET_NAME} \
--location=us-central1 \
--project=${PROJECT_ID}
sed -i "s|GCS_IMAGES_BUCKET=your-project-id-campaign-images|GCS_IMAGES_BUCKET=${BUCKET_NAME}|" .env
Richten Sie dann die Unterstützung für signierte Bild-URLs ein. Der Creative Director generiert für jedes Bild in der endgültigen Kampagnenzusammenfassung anklickbare HTTPS-Links. Dazu ist ein Dienstkonto zum Signieren der URLs erforderlich. Führen Sie die folgenden Befehle aus, um es zu konfigurieren:
export PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value project) --format="value(projectNumber)")
export SA_EMAIL="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
export AGENT_RUNTIME_SA="service-${PROJECT_NUMBER}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
# Allow your user account to sign URLs locally (adk web)
gcloud iam service-accounts add-iam-policy-binding ${SA_EMAIL} \
--member="user:$(gcloud config get-value account)" \
--role="roles/iam.serviceAccountTokenCreator"
# Allow Agent Runtime to sign URLs when deployed
gcloud projects add-iam-policy-binding $(gcloud config get-value project) \
--member="serviceAccount:${AGENT_RUNTIME_SA}" \
--role="roles/iam.serviceAccountTokenCreator"
# Save SA email and project number to .env
grep -q "^SIGNING_SERVICE_ACCOUNT" .env \
&& sed -i "s|^SIGNING_SERVICE_ACCOUNT=.*|SIGNING_SERVICE_ACCOUNT=${SA_EMAIL}|" .env \
|| echo "SIGNING_SERVICE_ACCOUNT=${SA_EMAIL}" >> .env
grep -q "^GOOGLE_CLOUD_PROJECT_NUMBER" .env \
&& sed -i "s|^GOOGLE_CLOUD_PROJECT_NUMBER=.*|GOOGLE_CLOUD_PROJECT_NUMBER=${PROJECT_NUMBER}|" .env \
|| echo "GOOGLE_CLOUD_PROJECT_NUMBER=${PROJECT_NUMBER}" >> .env
Öffnen Sie .env im Editor, um alle Einstellungen zu prüfen:
cloudshell edit .env
Dadurch wird .env als Tab im Cloud Shell-Editor geöffnet. Klicken Sie in der Symbolleiste auf die Schaltfläche Editor öffnen, wenn das Editorfeld nicht sichtbar ist:


Prüfen Sie, ob das Projekt richtig festgelegt wurde:
grep GOOGLE_CLOUD_PROJECT .env
Abhängigkeiten installieren
Wir verwenden uv, einen schnellen, modernen Python-Paketmanager, der virtuelle Umgebungen und Installationen in einem einzigen Tool verarbeitet. Es ist etwa 10- bis 100-mal schneller als pip und die empfohlene Methode zum Verwalten von Python-Projekten.
In Cloud Shell ist uv bereits installiert. Alle Agents haben dieselben Kernabhängigkeiten. Sie müssen sie also nur einmal installieren. Sie funktionieren dann für jeden Agent in diesem Codelab:
uv sync
Mit dem Befehl uv sync wird pyproject.toml gelesen und ein .venv/-Verzeichnis mit allen Abhängigkeiten erstellt. Jeder Spezialist hat auch einen eigenen pyproject.toml, der ausschließlich für Docker-Builds verwendet wird. Die oben beschriebene gemeinsame Installation deckt alles ab, was Sie für lokale Tests benötigen.
3. Google ADK
Bevor wir Code schreiben, sehen wir uns das Agent Development Kit (ADK) an. Das ist das Framework, das Sie zum Erstellen aller Agenten in diesem Codelab verwenden.
Was ist das ADK?
Das Agent Development Kit (ADK) ist ein flexibles und modulares Framework zum Entwickeln und Bereitstellen von KI-Agenten. Das ADK ist zwar für Gemini und das Google-Ökosystem optimiert, ist aber modell- und bereitstellungsunabhängig und für die Kompatibilität mit anderen Frameworks konzipiert. Das ADK wurde entwickelt, um die Entwicklung von Agenten eher wie die Softwareentwicklung zu gestalten und Entwicklern die Erstellung, Bereitstellung und Orchestrierung von agentischen Architekturen zu erleichtern, die von einfachen Aufgaben bis hin zu komplexen Workflows reichen.
Das ADK übernimmt die komplexen Teile – Tool-Aufrufe, mehrfache Unterhaltungen, Kontextverwaltung, Streaming –, sodass Sie sich auf die Logik des Agenten konzentrieren können.
Bausteine eines ADK-Agents
Jeder Agent besteht aus vier Bausteinen:
Blockieren | Rolle |
Modell | Das LLM, das Ziele analysiert, einen Plan entwickelt und Antworten generiert |
Tools | Funktionen, die Daten abrufen oder Aktionen ausführen, indem sie APIs oder Dienste aufrufen |
Orchestrierung | Behält Speicher und Status über mehrere Runden hinweg bei, leitet Tool-Aufrufe weiter und gibt Ergebnisse an das Modell zurück |
Laufzeit | Führt das System aus, wenn es aufgerufen wird – lokal über |
Agent-Definition
Jeder der fünf Agents in diesem Codelab wird auf dieselbe Weise definiert:
from google.adk.agents import Agent
from google.adk.tools.google_search_tool import google_search
root_agent = Agent(
name="brand_strategist", # unique identifier
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"), # the LLM powering this agent
instruction=SYSTEM_INSTRUCTION, # the agent's persona, constraints, and output format
description="Brand strategist for market research, trend analysis, and competitive insights",
tools=[google_search], # functions the LLM can call
)
Feld | Zweck |
| Eindeutige ID – wird von Orchestratoren zum Weiterleiten von Anrufen verwendet |
| Das Gemini-Modell, auf dem dieser KI-Agent basiert |
| Systemprompt: Definiert die Rolle, Einschränkungen und das Ausgabeformat des Agenten. |
| Einzeilige Zusammenfassung – der Orchestrator liest diese, um zu entscheiden, welcher Spezialist aufgerufen werden soll |
| Funktionen, die das LLM aufrufen kann (integrierte Funktionen wie |
So führt das ADK einen Agenten aus
User message
│
▼
Agent (LLM) ← reads instruction + conversation history
│
├─► needs more info? → calls a tool → gets result → continues reasoning
│
└─► done reasoning → returns final text response
Das LLM entscheidet autonom, ob ein Tool aufgerufen werden soll, welches Tool und mit welchen Argumenten. Sie schreiben die Anweisung – das ADK erledigt den Rest.
4. Brand Strategist-Agent erstellen und testen
Beginnen wir mit dem ersten Agent: dem Markenstrategen. Dieser Agent dient ausschließlich der Recherche. Er kann mit der Google Suche Zielgruppenstatistiken, Wettbewerbsanalysen und Trendthemen finden.
Öffnen Sie die Skelett-Agent-Datei im Cloud Shell-Editor:
cloudshell edit agents/brand_strategist/agent.py
Es werden zwei # TODO-Abschnitte angezeigt, die Sie ausfüllen müssen.
TODO 1: Systemanweisung schreiben
Zuerst schreiben Sie die Systemanweisung für den Agent. Die Systemanweisung ist ein String, der die Rolle, die Einschränkungen und das Ausgabeformat des Agenten definiert.
SYSTEM_INSTRUCTION = f"""You are a Brand Strategist specializing in market research and trend analysis.
IMPORTANT: Today's date is {datetime.date.today().strftime("%B %d, %Y")}.
When conducting research, focus on current trends from {datetime.date.today().year}.
Use search queries like "[topic] trends {datetime.date.today().year}" for recent insights.
IMPORTANT: Your role is RESEARCH ONLY. You do NOT create campaign content, captions, or designs.
After providing research insights, your work is complete.
Your expertise:
- Identifying target audience insights and behaviors
- Analyzing competitor strategies
- Researching current social media trends
- Understanding platform algorithms and best practices
You have access to:
- google_search: Search the web for competitors, trends, and market insights
When given a campaign brief:
1. Use google_search to research the target audience's current interests
2. Search for and analyze 2-3 competitor brands
3. Identify 3-5 trending topics related to the product category
4. Provide high-level strategic insights - NOT specific campaign content
DO NOT create captions, copy, designs, or any campaign content.
Format your output as:
**Audience Insights:**
[Key behaviors and preferences based on research]
**Competitive Analysis:**
[What 2-3 competitors are doing - strengths and weaknesses]
**Trending Topics:**
[3-5 relevant trends to consider]
**Key Strategic Insights:**
[High-level themes and positioning opportunities]
"""
TODO 2: root_agent erstellen
Ersetzen Sie dann das unvollständige root_agent durch:
root_agent = Agent(
name="brand_strategist",
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
instruction=SYSTEM_INSTRUCTION,
description="Brand strategist for market research, trend analysis, and competitive insights",
tools=[google_search],
)
Lokal mit der ADK-Web-UI testen
Jetzt testen wir den Agenten mit der ADK-Web-UI – einer integrierten Chat-Oberfläche zum Testen von Agenten vor der Bereitstellung in der Cloud.
uv run adk web agents --allow_origins='*'
Sie sehen hier Folgendes:
INFO: Started server process
INFO: Uvicorn running on http://localhost:8000
Der Server wird jetzt in Cloud Shell ausgeführt:
So öffnen Sie die Vorschau in Ihrem Browser:
- Sehen Sie sich oben auf der Seite die Cloud Shell-Symbolleiste an.
- Klicken Sie auf das Symbol für die Webvorschau (ein Kästchen mit einem Pfeil nach oben, oben rechts in der Cloud Shell-Symbolleiste).
- Klicken Sie auf Port ändern, geben Sie
8000ein und klicken Sie auf Ändern und Vorschau.
Ein neuer Browsertab mit der ADK-Web-UI wird geöffnet. Klicken Sie oben links auf das Drop-down-Menü Agent auswählen. Dort werden alle Ihre Agenten aufgeführt:
Wählen Sie brand_strategist aus, um mit dem Testen zu beginnen:
Test-Prompts ausprobieren
Geben Sie im Chatfeld der ADK-Web-UI Folgendes ein:
Research the eco-friendly water bottle market for health-conscious millennialsWhat are the top Instagram trends in the wellness space in 2025?
Der Agent ruft die Google Suche auf und gibt strukturierte Informationen mit den Abschnitten „Zielgruppenstatistiken“, „Wettbewerbsanalyse“ und „Aktuelle Themen“ zurück.
5. Copywriter-ADK-Skills erstellen
Rolle:Wandle Marktforschung in Instagram-Bildunterschriften um. Der Copywriter erstellt drei Bildunterschriften mit unterschiedlichen Tönen (inspirierend, informativ, Community), jeweils mit Hashtags und einem CTA.
Konzept: ADK-Skills
Bei einem einfachen Ansatz würden alle Plattforminformationen – Zeichenlimits, Hashtag-Stufen, Bildunterschrift-Formeln, Beispiele für die Markenstimme – direkt in den Systemprompt eingebettet. Das funktioniert, aber jede Anfrage wird mit Inhalten überladen, die der Agent nur gelegentlich benötigt.
Mit ADK-Skills (SkillToolset, eingeführt in ADK 1.25.0) können Sie dieses Wissen in modulare Dateien mit drei Ladeebenen packen:
- L1 – Frontmatter (
name+descriptioninSKILL.md): immer verfügbar, wird für die Skill-Erkennung verwendet - L2 – Anweisungen (Hauptteil von
SKILL.md): wird geladen, wenn der Agent den Skill auslöst - L3 – Ressourcen (
references/- undassets/-Dateien): werden nur geladen, wenn der Agent sie explizit liest
Die Systemanweisung wird auf eine kurze Rollenbeschreibung plus „Lade den Skill vor dem Schreiben“ reduziert. Plattformdetails werden nur dann in das Kontextfenster aufgenommen, wenn der Agent sie tatsächlich benötigt.
Die Fähigkeit von Copywriter befindet sich in agents/copywriter/skills/instagram-copywriting/:
skills/
instagram-copywriting/
SKILL.md ← L1 frontmatter (discovery) + L2 instructions (loaded on trigger)
references/
platform-guide.md ← L3: character limits, hashtag tiers, algorithm signals
caption-formulas.md ← L3: hook formulas, CTA patterns, full caption structures
assets/
brand-voice-examples.md ← L3: annotated real-world caption examples
Öffnen Sie die Datei direkt im Cloud Shell-Editor:
cloudshell edit agents/copywriter/agent.py
AUFGABE 1: load_skill_from_dir und skill_toolset importieren
Suchen Sie den Kommentar # TODO 1: Import load_skill_from_dir and skill_toolset und fügen Sie die beiden Importe hinzu:
from google.adk.skills import load_skill_from_dir
from google.adk.tools import skill_toolset
TODO 2: Skill laden und SkillToolset erstellen
Suchen Sie die beiden Kommentare unter den Importen:
# TODO 2: Load the instagram-copywriting skill from the skills/ directory
# TODO 2: Create a SkillToolset with the loaded skill
Ersetzen Sie sie durch:
_instagram_skill = load_skill_from_dir(
pathlib.Path(__file__).parent / "skills" / "instagram-copywriting"
)
_copywriting_skills = skill_toolset.SkillToolset(skills=[_instagram_skill])
load_skill_from_dir liest SKILL.md sowie alle Dateien in references/ und assets/. SkillToolset verpackt es in das Format, das ADK-Agents akzeptieren – ein Toolset, nicht eine Roh-Skill.
TODO 3: Toolset beim Agenten registrieren
Suchen Sie nach tools=[], # TODO 3: Add the SkillToolset here und ersetzen Sie es durch:
tools=[_copywriting_skills],
Öffnen Sie die Skill-Datei, um zu sehen, wie sie strukturiert ist:
cloudshell edit agents/copywriter/skills/instagram-copywriting/SKILL.md
Lassen Sie die ADK-Web-UI geöffnet. Verwenden Sie das Agent-Drop-down-Menü, um zu copywriter zu wechseln, ohne den Server neu zu starten.
Wenn sie nicht ausgeführt wird, starten Sie sie neu:
uv run adk web agents --allow_origins='*'
Testen:Stellen Sie das Drop-down-Menü auf copywriter um und senden Sie die Anfrage:
You are writing captions for EcoFlow Smart Water Bottle targeting health-conscious millennials aged 25-35.
Audience insight: they prioritize sustainability, track health metrics, and share lifestyle content.
Competitor insight: Hydro Flask dominates with lifestyle branding; S'well leads on premium aesthetics.
Write 3 Instagram captions - one inspirational, one educational, one community-focused. Include 5 hashtags each and a CTA.
6. Designer erstellen – Multimodale Bildgenerierung
Lassen Sie die ADK-Web-UI geöffnet. Mit dem Agent-Drop-down-Menü können Sie Agents wechseln, ohne den Server neu zu starten.
Rolle:Erstelle visuelle Konzepte für jede Bildunterschrift und generiere die tatsächlichen Bilder mit der nativen Bildgenerierung von Gemini. Der Designer gibt genau ein visuelles Konzept pro Bildunterschrift aus – mit einem detaillierten Prompt, Stil, einer Farbpalette, Stimmung und dem Instagram-Format – und ruft dann sofort das generate_image-Tool auf, um das tatsächliche Bild zu erstellen und in GCS hochzuladen.
Konzept: Text-Agent über ein Tool mit einem Bildmodell verbinden
Der Designer wird mit gemini-3-flash-preview (dem Textmodell, das über GEMINI_MODEL in .env festgelegt wurde) ausgeführt. Für die Bildgenerierung ist jedoch ein spezielles Modell (gemini-3.1-flash-image-preview) erforderlich. Dieses Bildmodell unterstützt keine Funktionsaufrufe und kann daher nicht direkt als ADK-Agent verwendet werden. Stattdessen wird sie in eine einfache Python-Funktion eingebunden und als FunctionTool registriert.
Dies ist das Muster für jedes Modell oder jede API, die das LLM nicht direkt aufrufen kann: Packen Sie es in ein Tool ein, lassen Sie den Agenten orchestrieren, wann es aufgerufen werden soll, und erhalten Sie ein strukturiertes Ergebnis zurück.
Designer agent (text model)
│
│ decides visual concept, writes image prompt
▼
generate_image tool
│
│ calls gemini-3.1-flash-image-preview
│ uploads result to GCS
▼
{"status": "success", "gcs_uri": "gs://..."}
│
│ returned to agent, included in response
▼
Critic (receives gcs_uri, passes to Vertex AI for multimodal review)
Öffnen Sie die Datei direkt im Cloud Shell-Editor:
cloudshell edit agents/designer/image_gen_tool.py
Die Funktionssignatur, die Einrichtung der Umgebung und die Einfügung des Seitenverhältnisses werden bereitgestellt. Arbeiten Sie die drei TODOs in der Reihenfolge durch:
TODO 1: Gemini-Bildmodell aufrufen
Suchen Sie den Kommentar # TODO 1 und ersetzen Sie ihn durch Folgendes:
client = genai.Client(vertexai=True, project=project_id, location=location)
response = client.models.generate_content(
model=image_model,
contents=prompt_with_aspect,
config=types.GenerateContentConfig(
response_modalities=["IMAGE", "TEXT"],
http_options=types.HttpOptions(
retry_options=types.HttpRetryOptions(
attempts=5, exp_base=2, initial_delay=30,
http_status_codes=[429, 500, 503, 504],
),
timeout=180_000,
),
),
)
TODO 2 – Bild-Bytes aus der Antwort extrahieren
Suchen Sie den Kommentar # TODO 2 und ersetzen Sie ihn durch Folgendes:
image_bytes = None
mime_type = "image/png"
for part in response.candidates[0].content.parts:
if part.inline_data is not None:
image_bytes = part.inline_data.data
mime_type = part.inline_data.mime_type or "image/png"
break
if not image_bytes:
return {"status": "error", "error": "Gemini returned no image data"}
TODO 3: In GCS hochladen und URI zurückgeben
Suchen Sie den Kommentar # TODO 3 und ersetzen Sie ihn durch Folgendes:
ext = "jpg" if "jpeg" in mime_type else "png"
from google.cloud import storage
gcs_client = storage.Client(project=project_id)
bucket = gcs_client.bucket(bucket_name)
blob_name = f"campaign-images/{concept_name}-{uuid.uuid4().hex[:8]}.{ext}"
blob = bucket.blob(blob_name)
blob.upload_from_file(io.BytesIO(image_bytes), content_type=mime_type)
gcs_uri = f"gs://{bucket_name}/{blob_name}"
Testen:Stellen Sie das Drop-down-Menü auf designer um und senden Sie die Anfrage:
Create a visual concept and generate the image for an EcoFlow Smart Water Bottle Instagram post targeting health-conscious millennials.
Style: clean, modern, lifestyle-focused. Include a detailed prompt with color palette, mood, and format (1080x1080 or 1080x1350).
7. Critic – strukturierte Ausgabe erstellen
Rolle:Texte und Bilder vor der Übergabe an den Projektmanager auf Qualität prüfen. Der Kritiker bewertet beide Ergebnisse und gibt APPROVED oder NEEDS_REVISION mit konkreten Vorschlägen zurück. Wenn gcs_uri-Werte in der Eingabe vorhanden sind, wird das Tool review_image aufgerufen, um jedes generierte Bild vor der Bewertung visuell zu prüfen.
Konzept: Wann sollte ein Pydantic-Modell für die Gemini-Ausgabe verwendet werden?
Bei der Regel geht es darum, wer die Ausgabe nutzt:
- Python-Code verwendet sie → Verwenden Sie
response_schema+ Pydantic. Code kann nicht mit Mehrdeutigkeiten umgehen. Sie benötigen also eine garantierte Struktur, um Felder zuverlässig zu extrahieren. - Ein LLM verarbeitet sie: Textformat + Systemanweisung reichen aus. LLMs verstehen Formatierungsregeln und tolerieren Abweichungen.
In review_image benötigt Python-Code score, approval_status, what_works, issues und suggestions als typisierte Werte. Durch die Übergabe von response_schema=_GeminiReview wird Gemini auf API-Ebene dazu gezwungen, gültiges JSON zurückzugeben. model_validate_json() parst es in ein typisiertes Objekt, das Ihr Code zuverlässig verwenden kann.
class _GeminiReview(BaseModel):
score: int = Field(ge=1, le=10)
approval_status: Literal["APPROVED", "NEEDS_REVISION"]
what_works: str
issues: str
suggestions: str
Öffnen Sie die Datei direkt im Cloud Shell-Editor:
cloudshell edit agents/critic/image_review_tool.py
Die Pydantic-Modelle und der Prompt werden bereitgestellt. Arbeiten Sie die drei TODOs in der Reihenfolge durch:
TODO 1: Bildteil aus dem GCS-URI erstellen
Suchen Sie den Kommentar # TODO 1 und ersetzen Sie ihn durch Folgendes:
image_part = types.Part.from_uri(file_uri=gcs_uri, mime_type=mime_type)
TODO 2: Gemini mit einem strukturierten Antwortschema aufrufen
Suchen Sie den Kommentar # TODO 2 und ersetzen Sie ihn durch Folgendes:
response = client.models.generate_content(
model=model,
contents=[image_part, prompt],
config=types.GenerateContentConfig(
response_schema=_GeminiReview,
response_mime_type="application/json",
),
)
TODO 3: Antwort parsen und Ergebnis zurückgeben
Suchen Sie den Kommentar # TODO 3 und ersetzen Sie ihn durch Folgendes:
review = _GeminiReview.model_validate_json(response.text)
return ImageReviewResult(status="success", concept_name=concept_name, **review.model_dump())
Testen:Stellen Sie das Drop-down-Menü auf critic um und senden Sie die Anfrage:
Review this Instagram caption for an eco-friendly water bottle brand targeting millennials:
"Hydrate smarter, live greener. 💧 Our EcoFlow bottle tracks your intake, keeps your drink cold for 24h, and never touches single-use plastic. Because what you drink from matters as much as what you drink. #EcoFlow #HydrationGoals #SustainableLiving #ZeroWaste #HealthyHabits - Shop link in bio."
Score it and indicate APPROVED or NEEDS_REVISION with specific feedback.
Prüfen Sie, ob die Antwort **POSTS REVIEW:**, Status: APPROVED (oder NEEDS_REVISION) und **OVERALL ASSESSMENT:** enthält. Wenn diese Abschnitte vorhanden sind, kann der Critic in den Orchestrator eingebunden werden.
Wenn Sie alle drei Agents getestet haben, drücken Sie Ctrl+C, um den Server zu beenden.
8. Project Manager-Agent mit MCP erstellen
Der Projektmanager führt ein neues Konzept ein: MCP (Model Context Protocol).
Öffnen Sie die Datei:
cloudshell edit agents/project_manager/agent.py
Diese Datei ist komplexer. Sie enthält eine create_project_manager_agent()-Funktion mit zwei Zweigen: einen ohne Notion (nur Text-Zeitachsen) und einen mit dem Notion-Toolset für die Media-Clean-Room-Lösung. Sie füllen beide aus.
Das Problem, das durch MCP gelöst wird
Ihr Agent muss einen externen Dienst aufrufen, z. B. eine Seite in Notion erstellen. Sie können Python-Code schreiben, der die Notion REST API direkt aufruft. Aber dann:
- Jeder Entwickler schreibt einen anderen Wrapper
- Sie müssen benutzerdefinierten Integrationscode verwalten.
- Das LLM weiß nicht, dass die API vorhanden ist, wenn Sie nicht jeden Endpunkt manuell beschreiben.
MCP löst dieses Problem, indem es eine Standardmethode für externe Dienste definiert, mit der ihre Funktionen als Tools bereitgestellt werden, die ein LLM automatisch erkennen und aufrufen kann.
Was ist das MCP?
Das MCP (Model Context Protocol) ist ein offener Standard (veröffentlicht von Anthropic) zum Verbinden von KI-Agenten mit externen Tools und Datenquellen. Er funktioniert wie ein Universaladapter.
Ein MCP-Server ist ein kleines Programm, das:
- Umschließt eine externe API (Notion, GitHub, Datenbanken, Dateisysteme usw.).
- Stellt die API als Liste von typisierten, dokumentierten Tools zur Verfügung.
- Kommuniziert über ein einfaches Protokoll (stdio oder HTTP) mit dem Agent.
Der Agent stellt eine Verbindung zum MCP-Server her, erkennt automatisch die verfügbaren Tools und kann sie wie jedes andere Tool aufrufen. Das LLM sieht API-post-page(...) als aufrufbare Funktion.
A2A im Vergleich zu MCP – was ist der Unterschied?
Das ist häufig ein Grund für Verwirrung. Der entscheidende Unterschied:
A2A | MCP | |
Was wird verbunden? | Agent ↔ Agent | Agent ↔ Externes Tool/externer Dienst |
Die andere Seite ist | Ein anderer LLM-Agent | Ein API-Wrapper (kein LLM) |
Beispiel | Creative Director ruft Markenstrategen an | Project Manager ruft die Notion API auf |
Protokoll | JSON-RPC über HTTPS | stdio- oder HTTP-Stream |
Definiert durch | Anthropic |
Sie können sich das so vorstellen:
- A2A – so kommunizieren KI-Agenten miteinander
- MCP – so kommunizieren Agents mit Tools und Diensten
In diesem Projekt werden beide zusammen verwendet:
Creative Director
│
│ (A2A) Brand Strategist ─── (google_search tool built into ADK)
│ (A2A) Copywriter
│ (A2A) Designer
│ (A2A) Critic
│ (A2A) Project Manager
│
│ (MCP) notion-mcp-server ──► Notion REST API
So funktioniert MCP in diesem Projekt
Wenn der Agent ausgeführt wird, startet das ADK notion-mcp-server als untergeordneten Prozess. Bei diesem Prozess werden diese Tools direkt für das LLM verfügbar gemacht:
Tool | Funktion |
| Ruft das Schema ab (Attributnamen, Typen, gültige Werte) |
| Bestehende Seiten abfragen |
| Neue Seite erstellen |
| Aktualisiert eine vorhandene Seite |
Das LLM ruft diese wie jede andere Funktion auf. Es weiß nicht, dass sie im Hintergrund über MCP an die Notion REST API weitergeleitet werden.
Warum stdio? Warum nicht einfach HTTP?
Der MCP-Server wird als untergeordneter Prozess des Agents ausgeführt und kommuniziert über stdin/stdout. Das bedeutet:
- Kein zusätzlicher Netzwerkanschluss erforderlich
- Der Lebenszyklus wird vom Agent verwaltet (auf Anfrage gestartet, beim Beenden beendet).
- Alles wird in einem Docker-Image ausgeliefert – es muss kein separater Dienst bereitgestellt werden.
(Optional) Notion-Integration aktivieren
Sie können diesen gesamten Abschnitt überspringen. Der Projektmanager-Agent erstellt immer einen vollständigen textbasierten Kampagnenzeitplan, unabhängig davon, ob Notion verwendet wird oder nicht. Wenn Sie diese Einrichtung überspringen, wechselt der KI-Agent in den In-Memory-Modus und gibt die Zeitachse als Klartext im Chat aus. Es geht nichts kaputt, aber Aufgaben werden nicht in einer Notion-Datenbank angezeigt. Gehen Sie direkt zu TODO 1, wenn Sie diesen Schritt überspringen möchten.
Wenn Sie ein Notion-Konto haben und die MCP-Integration in Aktion sehen möchten, führen Sie jetzt die Einrichtung unten durch. In den folgenden TODOs wird auf Notion-Datenbank-IDs verwiesen. So erhalten Sie sie.
Schritt 1: Notion-Datenbank aus einer Vorlage erstellen
Wir verwenden die offizielle Vorlage Notion Projects & Tasks als Datenbank. Wir haben diese Vorlage bewusst ausgewählt, um eine komplexe, realistische Situation zu demonstrieren. Sie enthält mehrere Attributtypen (Status, Zeiträume, Beziehungen, Auswahl) mit nicht offensichtlichen Namen. Das ist ein guter Test für die dynamische Schemaerkennung von MCP: Der Agent muss die genauen Eigenschaftsnamen zur Laufzeit ermitteln, anstatt sie fest zu codieren.
Klicken Sie auf den Link unten, um die Vorlage Ihrem Notion-Arbeitsbereich hinzuzufügen:
→ Vorlage „Projekte & Aufgaben“ zu Notion hinzufügen

Nach dem Hinzufügen haben Sie zwei verknüpfte Datenbanken: Projekte und Aufgaben. Die Vorlage enthält Beispiel-Einträge. Löschen Sie alle, bevor Sie fortfahren, damit der Agent mit einem leeren Arbeitsbereich beginnt (alle auswählen → „Löschen“).
Schritt 2: Notion-Integration erstellen
Integration erstellen:
- Rufen Sie notion.so/my-integrations auf.
- Klicken Sie auf New Integration (Neue Integration) → geben Sie ihr den Namen
AI Creative Studio. - Mit Ihrem Arbeitsbereich verknüpfen
- Klicken Sie auf Einstellungen konfigurieren → prüfen Sie, ob die Funktionen Inhalte lesen, Inhalte aktualisieren und Inhalte einfügen alle aktiviert sind.

- Kopieren Sie das Internal Integration Token (
ntn_...) und fügen Sie es in Ihre.env-Datei ein:
NOTION_TOKEN=ntn_your-token-here
Integration mit Ihren Datenbanken verbinden:
- Öffnen Sie die Vorlagenseite, die Sie gerade dupliziert haben, und klicken Sie dann in die Datenbank Projekte.
- Klicken Sie rechts oben auf das
...-Menü → Verbindungen → Verbindung hinzufügen → wählen SieAI Creative Studioaus.


- Wiederholen Sie diesen Schritt für die Datenbank Aufgaben.
Datenbank-IDs abrufen:
- Klicken Sie auf den Datenbanklink Projekte, um die Datenbank zu öffnen. Sie wird auf einer eigenen Seite mit einer URL wie der folgenden geöffnet:
https://www.notion.so/9887b6a94f7f83f68f8581e038d1aaa4?v=2c37b6a94f7f838685f1086e312c7278

Die Datenbank-ID ist die erste UUID in der URL – alles vor dem ?v=:
https://www.notion.so/{DATABASE_ID}?v=...
^^^^^^^^^^^^^^^^
9887b6a94f7f83f68f8581e038d1aaa4 ← this is your DATABASE_ID
- Wiederholen Sie den Vorgang für den Datenbanklink Tasks, um die zugehörige Datenbank-ID zu erhalten.
- Fügen Sie alle drei Werte zu
.envhinzu:
NOTION_TOKEN=ntn_your-token-here
NOTION_PROJECT_DATABASE_ID=9887b6a94f7f83f68f8581e038d1aaa4 # <-- your Projects DB ID
NOTION_TASKS_DATABASE_ID=your-tasks-db-id # <-- your Tasks DB ID
Schritt 3: Notion MCP-Server installieren
Der Projektmanager stellt über das offizielle @notionhq/notion-mcp-server-Node.js-Paket eine Verbindung zu Notion her. Globale Installation:
npm install -g @notionhq/notion-mcp-server@1.9.1
Installation prüfen:
npm list -g @notionhq/notion-mcp-server
Erwartete Ausgabe:
└── @notionhq/notion-mcp-server@1.9.1
notion-mcp-server: command not found
? Prüfen Sie, ob Node.js installiert ist (node --version) und ob sich Ihr globaler npm-Bin-Ordner in Ihrem PATH befindet (export PATH=$PATH:$(npm bin -g)).
Schritt 4: .env-Datei überprüfen
Öffnen Sie .env und prüfen Sie, ob alle drei Notion-Werte festgelegt sind (Sie haben sie in Schritt 2 hinzugefügt):
cloudshell edit .env
NOTION_TOKEN=ntn_... # integration token
NOTION_PROJECT_DATABASE_ID=... # Projects database ID
NOTION_TASKS_DATABASE_ID=... # Tasks database ID
Der Project Manager-Agent erkennt diese Variablen beim Start automatisch und aktiviert das Notion MCP-Toolset.
So funktioniert die Schemaerkennung
Der Projektmanager verwendet dynamische Schemaerkennung. Notion-Attributnamen werden nie fest codiert:
Step 1: Call API-retrieve-a-database to discover exact property names
Step 2: Read the "properties" object in the response
Step 3: Use ONLY discovered property names (case-sensitive) in API calls
Step 4: For select/status fields, use only values from the options array
Das bedeutet, dass sich der Agent automatisch an jede Notion-Datenbankstruktur anpasst. Sie können Ihre Attribute in Französisch, Arabisch oder eine andere Sprache umbenennen und der Agent funktioniert trotzdem.
TODO 1: Systemanweisung schreiben
Der Starter berechnet bereits notion_section – einen leeren String, wenn Notion nicht konfiguriert ist, oder einen Block mit den Datenbank-IDs und einer vollständigen Anleitung für das Tool, wenn es konfiguriert ist. So werden Notion-Anweisungen vollständig aus dem Prompt des Agents ohne Notion herausgehalten. Die LLM sieht niemals Regeln für Tools, die sie nicht hat.
Ihre Aufgabe ist es, den Platzhalter return durch eine echte Systemanweisung zu ersetzen, die {notion_section} verwendet:
return f"""You are a Project Manager specializing in creative campaign execution.
Today's date is {datetime.date.today().strftime("%B %d, %Y")}.
Use this as the starting point for all timelines.
Your goal: create a complete project plan for the campaign.
{notion_section}
**Project Timeline:**
Phase 1: Strategy & Research | [date] → [date] | [key activities]
Phase 2: Content Creation | [date] → [date] | [key activities]
Phase 3: Review & Revision | [date] → [date] | [key activities]
Phase 4: Launch & Monitoring | [date] → [date] | [key activities]
**Task List:**
| Task | Owner | Deadline | Status |
[list each task with realistic deadlines from today; set Owner to TBD]
**Budget Breakdown:**
[by category with approximate allocations]
**Milestones:**
[3-5 key checkpoints with dates]
**Notion Status:**
[What happened - e.g. "Project created (ID: xxx), 8 tasks linked" or "Notion not configured - text timeline only"]
"""
TODO 2: Agent ohne Notion
Ersetzen Sie in create_project_manager_agent() im Zweig if not notion_token den unvollständigen Agenten durch:
return Agent(
name="project_manager",
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
generate_content_config=GENERATE_CONTENT_CONFIG,
instruction=get_system_instruction(),
description="Project manager that creates campaign timelines and task breakdowns",
)
TODO 3: KI-Agent mit Notion-MCP
Hinweis:Die Starterdatei enthält bereits einen vordefinierten handle_notion_error-Callback über create_project_manager_agent(). Es fängt Notion API-Fehler (400/404) ab und ersetzt die rohen Fehler-Payloads durch übersichtliche, umsetzbare Meldungen, damit das LLM sich selbst korrigieren kann. Sie müssen es nur über after_tool_callback anschließen.
Lesen Sie zuerst die beiden Datenbank-IDs oben auf der Seite create_project_manager_agent():
notion_token = os.getenv("NOTION_TOKEN")
notion_project_db_id = os.getenv("NOTION_PROJECT_DATABASE_ID")
notion_tasks_db_id = os.getenv("NOTION_TASKS_DATABASE_ID")
Erstellen Sie dann im Branch else das MCP-Toolset und den Agenten:
from google.adk.tools.mcp_tool import McpToolset, StdioConnectionParams
from mcp import StdioServerParameters
server_params = StdioServerParameters(
command="notion-mcp-server",
env={
"NOTION_TOKEN": notion_token,
"PATH": os.environ.get("PATH", ""),
}
)
notion_toolset = McpToolset(
connection_params=StdioConnectionParams(
server_params=server_params,
timeout=30.0
)
)
return Agent(
name="project_manager",
model=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"),
generate_content_config=GENERATE_CONTENT_CONFIG,
after_tool_callback=handle_notion_error,
instruction=get_system_instruction(
project_database_id=notion_project_db_id,
tasks_database_id=notion_tasks_db_id,
),
description="Project manager with Notion integration for task tracking",
tools=[notion_toolset],
)
Best Practice:Bei optionalen Integrationen sollte es nie zu einem schwerwiegenden Fehler kommen. Die Text-Timeline ist immer das primäre Ergebnis. Notion ist nur eine Ergänzung.
Project Manager lokal mit ADK Web testen
uv run adk web agents --allow_origins='*'
Öffnen Sie die Webvorschau auf Port 8000. Wählen Sie im Agent-Drop-down-Menü die Option project_manager aus und versuchen Sie Folgendes:
Create a project plan for a GreenBrew organic coffee brand Instagram campaign.
Budget: $2,500. Launch in 3 weeks. Target audience: eco-conscious millennials aged 22-30.
Include phases, tasks with deadlines from today, and milestones.
Sie sollten eine strukturierte Text-Zeitachse mit Phasen, einer Aufgabenliste und Meilensteinen sehen. Wenn Notion-Anmeldedaten in .env festgelegt sind, erstellt der Agent auch Einträge in Ihrem Notion-Arbeitsbereich.
9. A2A-Protokoll
Wir verwenden das Agent-to-Agent-Protokoll (A2A), um die verschiedenen Agenten in unserem System zu verbinden. So funktioniert es:
Das Problem, das durch A2A gelöst wird
Angenommen, Sie haben einen Brand Strategist-Agenten, der mit dem ADK erstellt wurde, und einen Copywriter-Agenten, der mit LangGraph erstellt wurde. Wie ruft man den anderen an? Sie sprechen unterschiedliche interne Sprachen. Sie müssten jedes Mal benutzerdefinierten Glue-Code schreiben.
A2A löst dieses Problem, indem eine universelle Sprache definiert wird, die jeder Agent unabhängig vom Framework sprechen kann. Es ist das HTTP der Agent-Welt: ein Standard, auf den sich alle einigen, damit jeder mit jedem kommunizieren kann.
Was ist A2A?
Agent-to-Agent (A2A) ist ein von Google veröffentlichter offener Standard für die Kommunikation von Agenten. Es definiert:
- Wie sich ein Agent selbst beschreibt – Agentenkarte unter
/.well-known/agent.json - So wird der Agent von einem anderen Agenten aufgerufen – JSON-RPC über HTTPS
- So werden Ergebnisse zurückgegeben – Streaming oder einzelne Antwort
Was macht A2A flexibel?
- Sprachunabhängig: Python-Agents können mit TypeScript-Agents kommunizieren.
- Framework-unabhängig: ADK-Agenten können mit LangGraph- oder CrewAI-Agenten kommunizieren.
- Infrastrukturunabhängig: Lokale Agents können mit Cloud-Agents kommunizieren.
Funktionsweise – Schritt für Schritt
Creative Director Brand Strategist
│ │
│ 1. GET /.well-known/agent.json │
│ ────────────────────────────────►│
│ ◄──── agent card (name, url, │
│ skills, capabilities) ───│
│ │
│ 2. POST / │
│ {"method": "tasks/send", │
│ "params": {"message": ...}} │
│ ────────────────────────────────►│
│ │ LLM does
│ │ the work...
│ 3. streaming response chunks │
│ ◄───────────────────────────────│
│ ◄───────────────────────────────│
│ ◄───────────────────────────────│
Schritt 1: Ermittlung: Der Orchestrator ruft die Agentenkarte einmal ab, um den Namen, die URL und die Funktionen des Agenten zu ermitteln.
Schritt 2: Aufruf: Der Orchestrator sendet eine Aufgabe über JSON-RPC POST. Der Textkörper enthält die Nachricht (die Aufforderung für den Spezialisten).
Schritt 3: Antwort: Der Spezialist streamt seine Antwort in Blöcken zurück, genau wie bei einem normalen LLM-Aufruf.
Die Agentenkarte
Jeder Agent veröffentlicht eine Selbstbeschreibung unter /.well-known/agent.json. Das ist wie eine Visitenkarte – sie informiert die Welt darüber, was der Agent kann und wo er zu erreichen ist:
{
"name": "brand_strategist",
"description": "Market research and competitive analysis",
"url": "https://brand-strategist-xyz.run.app",
"capabilities": { "streaming": true },
"skills": [
{
"id": "market_research",
"description": "Research target audiences, competitors, and trends"
}
]
}
Der Orchestrator liest diese Karte, um sein RemoteA2aAgent-Objekt zu erstellen. Es sind keine hartcodierten Kenntnisse der Interna des Spezialisten erforderlich.
Agent über A2A im ADK bereitstellen
to_a2a() umschließt jeden ADK-Agenten in einer A2A-kompatiblen FastAPI-App. Eine Zeile:
from google.adk.a2a.utils.agent_to_a2a import to_a2a
# root_agent = your normal ADK Agent(...)
a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)
Dadurch wird automatisch Folgendes erstellt:
/.well-known/agent.json– die Agentenkarte/: Der JSON-RPC-Endpunkt. Alle A2A-Aufgabenanfragen werden an den Stamm-Pfad gesendet.
10. KI-Agenten als A2A-Dienste verfügbar machen
Wenn Sie Agenten als A2A-Dienste bereitstellen möchten, können Sie die to_a2a()-Dienstfunktion aus dem ADK verwenden.
So funktioniert to_a2a()
from google.adk.a2a.utils.agent_to_a2a import to_a2a
a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)
to_a2a() umschließt Ihren ADK-Agenten mit einer FastAPI-Anwendung, die automatisch Folgendes bereitstellt:
/.well-known/agent.json– die Agentenkarte (Name, Beschreibung, Funktionen)/a2a/{agent_name}– der JSON-RPC-Endpunkt für den Empfang von Aufgaben
Der Skelettcode jedes Agents enthält bereits einen __main__-Block, der den Agent mit to_a2a() in einen A2A-Server einbettet. Sie müssen diesen Code nicht schreiben, er wird bereitgestellt.
Konfiguration mit zwei URLs
Wenn Sie python agent.py ausführen, werden im __main__-Block zwei separate URL-Konfigurationen verwendet:
# Where the server actually listens (network interface):
HOST = "0.0.0.0"
PORT = 8082 # Brand Strategist (others use 8083–8086 locally)
# What gets advertised in the agent card (the address other agents use to reach it):
PUBLIC_HOST = os.getenv("PUBLIC_HOST", "localhost")
PUBLIC_PORT = int(os.getenv("PUBLIC_PORT", str(PORT)))
PROTOCOL = os.getenv("PROTOCOL", "http")
a2a_app = to_a2a(root_agent, host=PUBLIC_HOST, port=PUBLIC_PORT, protocol=PROTOCOL)
uvicorn.run(a2a_app, host=HOST, port=PORT)
Umgebung |
|
|
Lokal |
|
|
Cloud Run |
|
|
Lokal verweisen beide auf denselben Computer. In Cloud Run lauscht der Container intern auf 8080. Auf der Agent-Karte muss jedoch die öffentliche HTTPS-URL angegeben werden, da der Creative Director den Spezialisten sonst nicht von außerhalb des Containers erreichen kann.
Alle fünf spezialisierten A2A-Server starten
Wir lassen alle fünf Spezialisten gleichzeitig als A2A-Server laufen und testen dann den Creative Director lokal.
Öffnen Sie 5 separate Cloud Shell-Terminals (klicken Sie in der Terminal-Tab-Leiste auf das Symbol +) und führen Sie einen Agent pro Terminal aus.
uv run aktiviert automatisch die .venv – es ist keine manuelle source in jedem Terminal erforderlich.
Terminal 1 – Brand Strategist (Port 8082):
cd ~/ai-creative-studio/workshop/starter
PORT=8082 uv run agents/brand_strategist/agent.py
Terminal 2 – Copywriter (Port 8083):
cd ~/ai-creative-studio/workshop/starter
PORT=8083 uv run agents/copywriter/agent.py
Terminal 3 – Designer (Port 8084):
cd ~/ai-creative-studio/workshop/starter
PORT=8084 uv run agents/designer/agent.py
Terminal 4 – Critic (Port 8085):
cd ~/ai-creative-studio/workshop/starter
PORT=8085 uv run agents/critic/agent.py
Terminal 5 – Project Manager (Port 8086):
cd ~/ai-creative-studio/workshop/starter
PORT=8086 uv run agents/project_manager/agent.py
Localhost-URLs in .env festlegen
Aktualisieren Sie in Terminal 6 .env mit den lokalen Agenten-URLs, damit der Creative Director sie finden kann:
cd ~/ai-creative-studio/workshop/starter
sed -i \
-e 's|STRATEGIST_AGENT_URL=.*|STRATEGIST_AGENT_URL=http://localhost:8082|' \
-e 's|COPYWRITER_AGENT_URL=.*|COPYWRITER_AGENT_URL=http://localhost:8083|' \
-e 's|DESIGNER_AGENT_URL=.*|DESIGNER_AGENT_URL=http://localhost:8084|' \
-e 's|CRITIC_AGENT_URL=.*|CRITIC_AGENT_URL=http://localhost:8085|' \
-e 's|PM_AGENT_URL=.*|PM_AGENT_URL=http://localhost:8086|' \
.env
Agenten mit dem A2A Inspector prüfen
Der A2A Inspector ist ein Open-Source-Entwicklertool, das das A2A-Protokoll nativ unterstützt. Sie können sich direkt mit jedem laufenden A2A-Agenten verbinden, seine Agentenkarte lesen und Aufgaben senden – alles ohne Clientcode.
Was Sie sehen:
- Agent-Karte: Die strukturierten Metadaten, die Ihr Agent bewirbt, z. B. Name, Beschreibung, unterstützte Ein-/Ausgabemodi und die Endpunkt-URL. Das ist der Text, den der Creative Director liest, wenn er einen Spezialisten entdeckt.
- Chatoberfläche: Hier können Sie beliebige Nachrichten über A2A an den Agent senden und die Rohantwort sehen. Sie können Prompts einzeln testen, bevor Sie Agents miteinander verbinden.
- Protokollvalidierung: Der Inspector prüft, ob die Agent-Karte der A2A-Spezifikation entspricht, und weist frühzeitig auf fehlende Felder oder fehlerhafte Antworten hin.
Warum ist das wichtig? Wenn Sie die Bereitstellung später in Cloud Run vornehmen, ermittelt der Creative Director jeden Spezialisten, indem er die zugehörige Agent-Karte aus /.well-known/agent.json abruft. Wenn die Karte falsch ist (falsche URL, fehlende Funktionen), schlägt der Orchestrator stillschweigend fehl. Mit dem Inspector können Sie diese Probleme lokal erkennen, bevor Sie die Cloud-Bereitstellung vornehmen.

Auf der Agentenkarte werden die Identität und die Funktionen des Spezialisten genau so angezeigt, wie sie von anderen Agenten gesehen werden.

Inspector installieren und starten
cd ~/ai-creative-studio/workshop
./setup_inspector.sh
Die Aktualisierung .env ist ein einmaliger Befehl. Verwenden Sie Terminal 6, um den Inspector zu starten:
cd ~/a2a-inspector
bash scripts/run.sh
Öffnen Sie die Benutzeroberfläche des Inspektors über Webvorschau → Port ändern → 5001.
Verbindung zum Brand Strategist herstellen
Geben Sie http://localhost:8082 in das URL-Feld des Inspektors ein und klicken Sie auf Verbinden. Der Inspector ruft die Agentenkarte ab und zeigt die Metadaten des Spezialisten an.

Was die Agent-Karte Ihnen mitteilt
Die Agentenkarte ist mehr als nur Metadaten. Sie ist der vollständige Fähigkeitsvertrag, den der Agent im Netzwerk bewirbt. Stellen Sie eine Verbindung zum Project Manager (http://localhost:8086) her, um das beste Beispiel zu sehen:
{
"name": "project_manager",
"description": "Project manager with Notion integration for task tracking",
"protocolVersion": "0.3.0",
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "project_manager",
"name": "model",
"tags": ["llm"],
"description": "... full system instruction including today's date and Notion database IDs ..."
},
{
"id": "project_manager-API-post-page",
"name": "API-post-page",
"tags": ["llm", "tools"],
"description": "Notion | Create a page"
},
{
"id": "project_manager-API-retrieve-a-database",
"name": "API-retrieve-a-database",
"tags": ["llm", "tools"],
"description": "Notion | Retrieve a database"
}
]
}
Drei Dinge stechen hervor:
1. MCP-Tools werden zu A2A-Skills: Jedes Notion-Tool, auf das der Projektmanager Zugriff hat (API-post-page, API-retrieve-a-database usw.), wird als separater Skill auf der Agentenkarte aufgeführt. Jeder andere Agent im Netzwerk kann genau herausfinden, welche Tools dieser Agent verwenden kann, ohne Code lesen zu müssen.
2. Die Systemanweisung ist eingebettet. – Die erste Skill-Anweisung description enthält die vollständige Systemanweisung, einschließlich des aktuellen Datums und der Notion-Datenbank-IDs. So weiß der Creative Director, was er übergeben muss, wenn er den Projektmanager anruft.
3. Die URL ist der Live-Endpunkt. Das Feld url wird genau so von RemoteA2aAgent verwendet, wenn der Creative Director diesen Spezialisten anruft. Wenn die URL auf der Karte falsch ist, kann der Orchestrator den KI-Agenten nicht erreichen.
Deshalb ist der Inspector ein leistungsstarkes Debugging-Tool: Ein Blick auf die Agent-Karte genügt, um zu sehen, ob der Agent ausgeführt wird, welche Tools er hat und ob der Endpunkt korrekt ist.
Testnachricht senden
Sobald eine Verbindung besteht, geben Sie einen Prompt in den Chatbereich ein und senden Sie ihn. Der Prüfer sendet sie als A2A-Aufgabe und streamt die Antwort zurück – so wie der Creative Director diesen Agenten in der Produktion aufrufen wird.

Richten Sie den Inspector auf einen beliebigen lokalen Port (8082–8086), um jeden Spezialisten einzeln zu testen.
11. Creative Director Orchestrator erstellen
Der Creative Director ist der Master-Orchestrator. Es liest Spezialisten-URLs aus Umgebungsvariablen, umschließt jede mit RemoteA2aAgent und macht sie als AgentTool verfügbar, die das LLM aufrufen kann.
Prüfen Sie, ob die fünf Spezialisten-Agents noch ausgeführt werden (Terminals 1–5 aus Schritt 10).
Beenden Sie den Inspector in Terminal 6 (dem A2A Inspector-Terminal) mit Ctrl+C.
Öffnen Sie die Datei:
cd ~/ai-creative-studio/workshop/starter
cloudshell edit agents/creative_director/agent.py
Diese Datei enthält drei TODOs. Arbeiten Sie sie der Reihe nach durch.
TODO 1: Bereits geschriebene Systemanleitung überprüfen
Die Systemanweisung befindet sich im selben Verzeichnis in prompt.py und wird automatisch importiert:
from .prompt import SYSTEM_INSTRUCTION_TEMPLATE
Öffnen Sie prompt.py, um sie zu lesen, bevor Sie fortfahren:
cloudshell edit agents/creative_director/prompt.py
Es ist wichtig, das zu verstehen, da es das gesamte Orchestrierungsverhalten steuert.
Warum der Orchestrator-Prompt alles steuert
Öffnen Sie prompt.py. Die folgenden Beispiele beziehen sich auf bestimmte Teile dieses Dokuments.
Der Prompt in prompt.py ist nicht nur Dokumentation, sondern die Steuerungsebene des gesamten Systems. Ein schlecht strukturierter Orchestrator-Prompt führt zu folgenden Problemen: Agenten werden in falscher Reihenfolge aufgerufen, Inhalte werden vom Orchestrator anstelle von Spezialisten generiert, Workflows werden nach Fehlern fortgesetzt und der Kontext wird zwischen Agenten stillschweigend gelöscht. Diese neun Elemente verhindern die häufigsten Fehler:
Element 0: Erst planen, dann ausführen
Dies ist das wichtigste Element. Bevor ein Spezialist angerufen wird, wird der Orchestrator angewiesen, einen nummerierten Plan auszugeben:
I'll create your campaign by coordinating the specialist agents in sequence:
1. Brand Strategist - develop positioning and audience insights
2. Copywriter - write captions using those insights
3. Visual Designer - create image prompts aligned with the copy
4. Critic - review and score the full package
5. Project Manager - build the timeline and task breakdown
Ohne diesen Schritt springt das LLM direkt zu Tool-Aufrufen und verliert den Überblick darüber, wo es sich im Workflow befindet – insbesondere nach dem Empfang einer langen Antwort von einem Spezialisten. Durch die Gliederung des Plans wird der Orchestrator verankert: Er weiß, in welchem Schritt er sich befindet, was als Nächstes kommt und wie ein vollständiger Lauf aussieht. Wenn Sie diesen Schritt überspringen, bleibt der Orchestrator mitten im Workflow stehen oder wiederholt Schritte.
Element 1: Explizite Rollendefinition
❌ "You are a helpful creative assistant."
✅ "You orchestrate specialists. You do NOT write captions, designs, or timelines yourself."
Ohne das explizite Verbot ruft das LLM manchmal keine Spezialisten auf und generiert Inhalte direkt, da es schneller ist und es „weiß“, wie es geht. Die Anweisung muss das korrigieren.
Element 2: Syntax für Tool-Aufruf mit falschen Mustern
Es reicht nicht aus, nur die richtige Syntax zu zeigen. Das LLM kann Aufrufe generieren, die plausibel aussehen, aber im Hintergrund fehlschlagen. Im Prompt werden sowohl das richtige Muster als auch die Muster, die niemals verwendet werden dürfen, explizit aufgeführt:
✅ copywriter(request="...") ← correct
❌ print(copywriter(...)) ← breaks silently
❌ default_api.copywriter(...) ← breaks silently
❌ copywriter.run(...) ← breaks silently
❌ agents.copywriter(...) ← breaks silently
Durch die explizite Auflistung der falschen Muster wurden fehlerhafte Tool-Aufrufe in der Produktion um etwa 95% reduziert.
Element 3: Sequenzielle Ausführung Schritt für Schritt
a) Call the tool
b) Wait for tool_output
c) Verify the output is not an error
d) Confirm to the user: "✓ Brand Strategist complete"
e) Then move to the next agent
Ohne die Schritte (b) und (c) ruft das LLM manchmal zwei Agents gleichzeitig auf oder geht von einem Erfolg aus und fährt fort, bevor es die Antwort erhält.
Element 4: Fehleranweisungen: STOP, report, do not proceed
In frühen Versionen erhielt der Orchestrator einen Fehler von einem Spezialisten, erfand eine plausible Ausgabe dafür und fuhr mit dem nächsten Agenten fort. Der Nutzer hat eine Kampagne erhalten, die auf einer halluzinierten Grundlage basiert. Die Korrektur ist eindeutig: STOP sofort. Melden Sie den genauen Fehler. Nie fortfahren.
Element 5: Regeln für die Übergabe von Kontext
Remote-Agents haben keinen Unterhaltungsverlauf. Wenn der Orchestrator den Copywriter über A2A aufruft, sieht der Copywriter nur die Nachricht in dieser einzelnen Anfrage. Er weiß nicht, was der Brand Strategist gesagt hat. Der Orchestrator muss vorherige Ausgaben explizit in jeden nachfolgenden Aufruf einbinden:
copywriter(request="Create 3 posts for EcoFlow water bottle targeting millennials.
Use these insights from the Brand Strategist: [paste full strategist output here].
Create engaging captions with hashtags.")
In der Anleitung wird dies explizit erwähnt: „Remote-Agents haben KEINEN gemeinsamen Speicher – Sie müssen vorherige Ausgaben explizit übergeben.“ Ohne diese Informationen arbeitet jeder Agent blind.
Element 6: Anfrageklassifizierung – einfach oder komplex
Nicht für jede Anfrage sind alle fünf Agents erforderlich. Der Prompt weist den Orchestrator an, die Anfrage vor der Planung zu klassifizieren:
SIMPLE → one agent needed
"Research the eco-friendly water bottle market" → brand_strategist only
"Write 3 Instagram captions" → copywriter only
COMPLEX → all agents sequentially
"Create a complete campaign with timeline" → all 5 agents
Ohne diese Klassifizierung würde der Orchestrator alle fünf Agents für jede Anfrage ausführen, auch für „Gib mir einfach drei Ideen für Beiträge“. Das würde unnötige Latenz und Kosten verursachen.
Element 7: Kommunikationsregeln – vollständige Ausgaben anzeigen, keine Filterung
Im Prompt wird explizit darauf hingewiesen, dass der Orchestrator die Antworten der Spezialisten nicht zusammenfassen oder bearbeiten darf:
- DO NOT summarize unless the output exceeds 2000 words
- DO NOT filter or edit agent responses
- Show the user exactly what each specialist produced
- NEVER say results are ready unless you received them in tool_output
Andernfalls formuliert der Orchestrator die Ausgaben der Spezialisten in eigenen Worten um, wodurch Details verloren gehen, Fehler eingeführt werden und der Zweck der Spezialisten zunichte gemacht wird.
Element 8: Workflow-Abschluss – nie vorzeitig beenden
Ein subtiler, aber kritischer Fehlermodus: Der Orchestrator kündigt einen 5-stufigen Plan an, führt 3 Schritte aus und präsentiert dann Ergebnisse, als ob er fertig wäre. Der Prompt verhindert dies mit einer expliziten Checkliste, die erfüllt sein muss, bevor der Orchestrator abgeschlossen werden kann:
✓ Did I announce a plan with N agents?
✓ Have I called ALL N agents from my plan?
✓ Did each agent respond successfully?
✓ Am I presenting complete results from ALL agents?
If any answer is NO → continue executing the remaining agents.
Dadurch wird verhindert, dass der Orchestrator einen Teillauf als abgeschlossen betrachtet.
Der Qualitätskontrollprozess
Der Überarbeitungs-Workflow ist der komplexeste Teil von prompt.py. Öffnen Sie den Bereich ## REVISION WORKFLOW und folgen Sie der Anleitung.
Funktionsweise
Nachdem der Kritiker geantwortet hat, leitet der Creative Director die Antwort nicht blind an den Projektmanager weiter. Die Ausgabe des Kritikers wird gelesen und verzweigt:
Critic output
│
├── "All Approved: YES"
│ └──► proceed to Project Manager
│
└── "Status: NEEDS_REVISION"
│
├── posts fail → call copywriter again with feedback
├── visuals fail → call designer again with feedback
└── both fail → call copywriter, then designer
│
└──► revised output → Project Manager
(1 revision max per deliverable)
Das Tool basiert auf LLMs, nicht auf Code.
Im oben erwähnten Codelab wird beschrieben, dass der Orchestrator die Antwort des Kritikers „parst“. Es gibt keinen Python-Code, der diese Analyse durchführt – weder reguläre Ausdrücke noch String-Abgleich. Der Creative Director ist ein LLM, das seine eigene Anleitung liest. Dort heißt es:
Look for "Status: NEEDS_REVISION" in the critic's response.
Posts need revision → call copywriter
Visuals need revision → call designer
Das LLM liest diese genauen Strings in der Ausgabe des Kritikers und folgt dem Zweig. Deshalb ist das Format des Kritikers nicht verhandelbar: Wenn der Kritiker „needs some work“ anstelle von NEEDS_REVISION schreibt, findet das LLM keine Übereinstimmung in seiner Anleitung und überspringt den Überarbeitungsschritt stillschweigend.
So wird Kontext in einem Überarbeitungsaufruf weitergeleitet
Der Überarbeitungsaufruf folgt derselben Regel für die Kontextübergabe aus Element 5: Der Orchestrator muss alles explizit angeben, da der Copywriter sich nicht an die erste Version erinnert:
"I need you to revise the Instagram posts based on critic feedback.
ORIGINAL BRIEF:
[the original user request]
YOUR FIRST VERSION:
[the posts the copywriter created]
CRITIC FEEDBACK (Score: 6/10 - NEEDS_REVISION):
[the critic's specific suggestions]
Please revise the posts addressing this feedback while maintaining
the strengths the critic identified."
Ohne den Abschnitt „YOUR FIRST VERSION“ würde Copywriter den Text von Grund auf neu schreiben, anstatt das Ergebnis zu verbessern.
Das Limit von einer Überarbeitung und warum es wichtig ist
Nach einer Überarbeitungsrunde wird der Orchestrator unabhängig von der Punktzahl an den Projektmanager weitergeleitet. Die Anweisung verfolgt dies mental:
After calling copywriter for revision once:
→ mark "copywriter_revised = true" in context
→ even if the critic still suggests changes, proceed to PM
Ohne diese Begrenzung könnte die Schleife unendlich oft durchlaufen werden: Der Kritiker meldet ein Problem → Der Texter überarbeitet den Text → Der Kritiker meldet das Problem noch einmal → Der Texter überarbeitet den Text noch einmal. Jede Runde kostet Tokens und Zeit. Eine Überarbeitung reicht aus, um die Qualität zu verbessern, ohne dass das Risiko eines unkontrollierten Zyklus besteht.
Was an den Projektmanager übergeben wird
Der Projektmanager erhält immer die endgültigen genehmigten Versionen, nicht die Originale. Wenn Änderungen vorgenommen wurden, übergibt der Orchestrator die überarbeitete Kopie und die überarbeiteten Grafiken. Wenn alles beim ersten Durchlauf genehmigt wurde, wird es direkt weitergeleitet. Der PM sieht nie abgelehnte Entwürfe.
TODO 2: Registrieren Sie jeden Spezialisten als RemoteA2aAgent + AgentTool.
Suchen Sie den Kommentar # TODO 2: For each specialist URL... und ersetzen Sie ihn durch Folgendes:
if strategist_url:
available_agents_list.append(
"- **brand_strategist**: Market research, competitor analysis, trend identification"
)
strategist_agent = RemoteA2aAgent(
name="brand_strategist",
description="Researches markets, competitors, and trends using Google Search",
agent_card=f"{strategist_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=strategist_agent))
if copywriter_url:
available_agents_list.append(
"- **copywriter**: Instagram captions, hashtags, and CTAs"
)
copywriter_agent = RemoteA2aAgent(
name="copywriter",
description="Creates Instagram captions with hashtags and CTAs",
agent_card=f"{copywriter_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=copywriter_agent))
if designer_url:
available_agents_list.append(
"- **designer**: Visual concepts and real images generated via Gemini (GCS URIs returned)"
)
designer_agent = RemoteA2aAgent(
name="designer",
description="Creates visual concepts and generates real images via Gemini, stored in GCS",
agent_card=f"{designer_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=designer_agent))
if critic_url:
available_agents_list.append(
"- **critic**: Quality review with APPROVED/NEEDS_REVISION scoring"
)
critic_agent = RemoteA2aAgent(
name="critic",
description="Reviews campaign materials and returns structured quality feedback",
agent_card=f"{critic_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=critic_agent))
if pm_url:
available_agents_list.append(
"- **project_manager**: Project timelines, task breakdowns, Notion integration"
)
pm_agent = RemoteA2aAgent(
name="project_manager",
description="Creates project timelines and task breakdowns, optionally in Notion",
agent_card=f"{pm_url}/.well-known/agent.json",
)
agent_tools.append(AgentTool(agent=pm_agent))
TODO 3: In eine App mit Kontextverdichtung einbinden
Warum ist die Komprimierung notwendig?
Jede Nachricht in einer Unterhaltung – der Prompt des Nutzers, jeder Tool-Aufruf und jede Tool-Antwort – wird an das Kontextfenster angehängt, das das LLM beim nächsten Zug liest. In einem Workflow mit fünf Agenten summiert sich das schnell:
Turn 1: user prompt ~200 tokens
Turn 2: orchestrator plan ~300 tokens
Turn 3: brand_strategist tool_call ~150 tokens
Turn 4: brand_strategist tool_output ~1,500 tokens ← full research report
Turn 5: copywriter tool_call ~300 tokens ← must include strategist output
Turn 6: copywriter tool_output ~2,000 tokens ← 3 captions
Turn 7: designer tool_call ~500 tokens
Turn 8: designer tool_output ~1,500 tokens
...
Bei Agent 4 (Kritiker) enthält das Kontextfenster die vollständige Ausgabe aller drei vorherigen Agents – oft 8.000 bis 12.000 Tokens allein in den Tool-Antworten. Selbst mit dem großen Kontextfenster von Gemini 2.5 Pro nimmt die Qualität der Orchestrator-Schlussfolgerungen ab, da er sich auf einen immer größeren Verlauf beziehen muss. Ohne Verdichtung stoßen lange Workflows bei Agent 4 an praktische Grenzen.
Was passiert bei der Komprimierung?
Anstatt jedes Ereignis vollständig zu speichern, ruft ADK regelmäßig ein LLM auf, um ältere Ereignisse in einer kompakten Darstellung zusammenzufassen. Nur die Zusammenfassung vergangener Ereignisse und die vollständige Ausgabe des letzten Agents werden im Kontext beibehalten.
Without compaction:
[full strategist output] + [full copywriter output] + [full designer output] + → Critic
With compaction (interval=3, overlap=1):
[summary of strategist + copywriter] + [full designer output] + → Critic
In der Zusammenfassung werden die wesentlichen Fakten (wichtige Statistiken, genehmigte Untertitel, visuelle Konzepte) beibehalten, während die ausführliche Formatierung, der wiederholte Kontext, der an jeden Agent weitergegeben wird, und die Zwischenschritte der Argumentation verworfen werden. Der Critic hat weiterhin alles, was er für die Bewertung benötigt. Er liest nur eine Zusammenfassung anstelle von drei vollständigen Berichten.
Der Code
Suchen Sie den Kommentar # TODO 3: Wrap the agent in an App... und ersetzen Sie den Platzhalter App(...) durch:
from google.adk.apps import App
from google.adk.apps.app import EventsCompactionConfig
from google.adk.apps.llm_event_summarizer import LlmEventSummarizer
from google.adk.models import Gemini
compaction_config = EventsCompactionConfig(
summarizer=LlmEventSummarizer(llm=Gemini(model_id=os.getenv("GEMINI_MODEL", "gemini-2.5-flash"))),
compaction_interval=3, # Summarize after every 3 agent completions
overlap_size=1, # Keep the most recent agent's output in full
)
app = App(
name="creative_director",
root_agent=agent,
events_compaction_config=compaction_config,
plugins=[LoggingPlugin()],
)
return agent, app
compaction_interval=3: Die Verdichtung wird nach jeweils drei Agent-Abschlüssen ausgelöst. Bei einer Pipeline mit fünf Agents wird sie einmal ausgelöst (nach den Agents 1–3). Der Critic und der PM sehen dann eine Zusammenfassung der Agents 1–3 sowie die vollständige Ausgabe des vorherigen Agents.
overlap_size=1: Die vollständige Ausgabe des letzten Agenten wird immer wortwörtlich beibehalten und nie zusammengefasst. Das ist wichtig, weil der Kritiker die vollständige Ausgabe des Designers benötigt, einschließlich der gcs_uri-Werte, um die tatsächlichen Bilder zu laden und zu überprüfen. In einer Zusammenfassung würden diese URIs fehlen.
So läuft eine vollständige Kampagne ab:
Agent 1 (Strategist) → full context
Agent 2 (Copywriter) → full context
Agent 3 (Designer) → full context
↓ compaction fires: summarizes agents 1-2, keeps 3 in full
Agent 4 (Critic) → sees [summary of 1-2] + [full output of 3]
Agent 5 (PM) → sees [summary of 1-3] + [full output of 4]
Informationen zu RemoteA2aAgent und AgentTool
RemoteA2aAgent("brand_strategist", agent_card=url)
│
│ wraps the remote service so ADK can call it
▼
AgentTool(agent=strategist_agent)
│
│ exposes it as a callable tool to the LLM
▼
Agent(tools=[...])
│
│ LLM calls tool("brand_strategist", message=...) when needed
▼
brand-strategist-xxxx.run.app ← actual HTTP A2A call happens here
Das LLM entscheidet anhand der Systemanweisung und der Nutzeranfrage, wann die einzelnen Tools aufgerufen werden. Der Orchestrator ruft KI-Agents nie direkt im Code auf. Alles wird durch die Argumentation des LLM gesteuert.
Creative Director lokal testen
uv run adk web agents --allow_origins='*'
Öffnen Sie die Webvorschau auf Port 8000. Wählen Sie im Agent-Drop-down-Menü die Option creative_director aus und versuchen Sie Folgendes:
Research the eco-friendly water bottle market for health-conscious millennials
Der Creative Director leitet die Anfrage nur an den Brand Strategist weiter und Sie erhalten eine Antwort vom Brand Strategist.
Für die gesamte Kampagne können Sie Folgendes versuchen:
Create a complete Instagram campaign for SolarPack portable solar charger targeting
outdoor enthusiasts and digital nomads aged 22-35.
Budget $2,000, launch in 2 weeks.
Sie sehen, wie der Creative Director alle fünf Spezialisten nacheinander koordiniert und die Ausgabe jedes einzelnen in den nächsten Schritt einfließt.

Beende den Creative Director (Ctrl+C), bevor du fortfährst. Der A2A-Inspector verwendet ebenfalls Port 8000.
Beenden Sie die fünf Spezialistenserver (Ctrl+C in jedem Terminal), wenn Sie mit den lokalen Tests fertig sind.
12. Spezialisten-Agents bereitstellen und testen
Wir können unsere Agenten jetzt in Google Cloud bereitstellen. Cloud Run ist ein hervorragender Dienst zum Bereitstellen von Agents. Sie ist serverlos, skalierbar und einfach zu verwenden. Jeder Spezialisten-Agent wird als unabhängiger Cloud Run-Dienst bereitgestellt.
Konfiguration des Deployments
Die Dockerfile für jeden Spezialisten folgt diesem Muster:
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends gcc curl
# Fast dependency install with uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
COPY pyproject.toml .
RUN uv sync --no-install-project --no-dev
COPY . .
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
ENV PYTHONUNBUFFERED=1 PORT=8080 HOST=0.0.0.0
EXPOSE 8080
CMD ["uv", "run", "python", "agent.py"]
Alle 5 Spezialisten nacheinander einsetzen
cd ~/ai-creative-studio/workshop/starter
source .env
uv run deploy/deploy_all_specialists.py
Mit diesem Script werden alle fünf Agents nacheinander bereitgestellt (insgesamt ca. 10–12 Minuten). Bei der sequenziellen Bereitstellung wird das Cloud Build-Polling-Kontingent (60 Anfragen/Minute) nicht überschritten. Nach Abschluss des Vorgangs wird die Cloud Run-URL jedes Agents in .env zurückgeschrieben.
Nachdem der Designer bereitgestellt wurde, gewährt das Skript automatisch das Cloud Run-Dienstkonto roles/storage.objectCreator für Ihren GCS-Bucket, damit generierte Bilder hochgeladen werden können.
Wenn Sie Notion-Anmeldedaten in .env konfiguriert haben, werden sie vom Skript auch sicher in Secret Manager (als notion-token, notion-project-db-id, notion-tasks-db-id) gespeichert und über --set-secrets anstelle von einfachen Umgebungsvariablen in den Project Manager-Dienst eingefügt. Das bedeutet, dass das Token nie auf dem Tab „Umgebung“ von Cloud Run oder im gcloud-Befehlsverlauf angezeigt wird.
Deployments prüfen
Nach Abschluss der Bereitstellung schreibt das Skript die Cloud Run-URLs automatisch zurück in .env und ersetzt die Localhost-URLs aus dem vorherigen Schritt:
source .env
echo "Deployed URLs:"
echo " Brand Strategist: $STRATEGIST_AGENT_URL"
echo " Copywriter: $COPYWRITER_AGENT_URL"
echo " Designer: $DESIGNER_AGENT_URL"
echo " Critic: $CRITIC_AGENT_URL"
echo " Project Manager: $PM_AGENT_URL"
Der Creative Director verwendet diese Cloud Run-URLs automatisch, wenn er im nächsten Schritt in der Agent Runtime bereitgestellt wird.
Agent-Karten bestätigen
Für jeden bereitgestellten Agent wird unter /.well-known/agent.json eine Agent-Karte angezeigt. Rufen Sie sie ab, um zu prüfen, ob alles live ist:
source .env
for agent_url in $STRATEGIST_AGENT_URL $COPYWRITER_AGENT_URL $DESIGNER_AGENT_URL $CRITIC_AGENT_URL $PM_AGENT_URL; do
echo "=== Agent Card: $agent_url ==="
curl -s "${agent_url}/.well-known/agent.json" | python3 -m json.tool | grep -E '"name"|"url"|"description"'
echo ""
done
Erwartete Ausgabe für jeden Agent:
"name": "brand_strategist",
"url": "https://brand-strategist-xxxx.run.app",
"description": "Brand strategist for market research and competitive insights"
Mit dem A2A Inspector testen (Cloud Run)
Der A2A Inspector wurde bereits in Schritt 10 installiert. Starten Sie sie:
cd ~/a2a-inspector
bash scripts/run.sh
Öffnen Sie die Webvorschau → Port ändern → 5001. Geben Sie Ihre Cloud Run-URL in das Verbindungsfeld ein:
https://brand-strategist-xxxx.us-central1.run.app
Klicken Sie auf Verbinden. Da die Dienste mit --allow-unauthenticated bereitgestellt werden, ist kein Autorisierungstoken erforderlich.
Der Inspector stellt eine Verbindung her, validiert die Agent-Karte und ermöglicht Ihnen, interaktiv über A2A zu chatten.
In Cloud Run bereitgestellte Agents prüfen
Nach der Bereitstellung in Cloud Run können Sie den Inspector auf die öffentliche HTTPS-URL richten, um zu prüfen, ob die Cloud-Bereitstellung funktioniert:

Der Workflow ist identisch: Fügen Sie die Cloud Run-URL ein, stellen Sie eine Verbindung her und senden Sie eine Testnachricht. Wenn die Agent-Karte geladen wird und der Chat antwortet, ist der Kundenservicemitarbeiter korrekt eingesetzt und erreichbar.
13. Creative Director in der Laufzeit für KI-Agenten bereitstellen
Der Orchestrator wird in Agent Runtime bereitgestellt, die verwalteten Sitzungsstatus, automatische Skalierung und integriertes Tracing bietet.
Warum Agent Runtime für den Orchestrator?
Die fünf Spezialisten werden in Cloud Run bereitgestellt – sie sind schlank, zustandslos und jeder übernimmt eine Aufgabe. Der Creative Director hat andere Anforderungen:
Anforderung | Warum das wichtig ist |
Sitzungsstatus | Ein mehrstufiger Workflow dauert mindestens 45 Sekunden. Die Agent Runtime behält den Unterhaltungsstatus zwischen den Tool-Aufrufen des Orchestrators bei, sodass in der Mitte der Pipeline nichts verloren geht. |
Variable Last | Manchmal nur eine Kampagne pro Stunde, manchmal viele parallel. Die Agent-Laufzeit wird bei Inaktivität auf null skaliert und automatisch hochskaliert. Sie zahlen also nicht für inaktive Kapazität. |
Beobachtbarkeit | Cloud Logging, Cloud Monitoring und Cloud Trace sind integriert. Sie können jeden A2A-Aufruf, jedes verwendete Token und jede Latenzspitze sehen, ohne dass Sie Instrumentierung hinzufügen müssen. |
Lang andauernde Workflows | Cloud Run hat ein Anfrage-Zeitlimit von 3.600 Sekunden. Agent Runtime ist für Workflows konzipiert, die mehrere Minuten dauern können, mit verwalteten Wiederholungsversuchen und Statuspersistenz. |
Cloud Run ist die richtige Plattform für zustandslose Spezialisten. Agent Runtime ist die richtige Plattform für den zustandsbehafteten Orchestrator.
Orchestrator bereitstellen
cd ~/ai-creative-studio/workshop/starter
source .env
uv run deploy/deploy_orchestrator.py --action deploy
Das dauert etwa 5–10 Minuten. Wenn der Vorgang abgeschlossen ist, werden AGENT_ENGINE_ID und AGENT_ENGINE_RESOURCE_NAME in .env gespeichert.
source .env
echo "Agent Engine ID: $AGENT_ENGINE_ID"
echo "Resource: $AGENT_ENGINE_RESOURCE_NAME"
So funktioniert die Bereitstellung
client.agent_engines.create() verpackt Ihr App-Objekt, lädt es mit seinen Abhängigkeiten hoch und stellt es auf der verwalteten Infrastruktur bereit. Die einzelnen Parameter bewirken Folgendes:
import vertexai
from vertexai import Client, agent_engines
vertexai.init(project=PROJECT_ID, location=LOCATION, staging_bucket=STAGING_BUCKET)
# Wrap the App in an AdkApp adapter - enables tracing in Cloud Trace
adk_app = agent_engines.AdkApp(app=root_app, enable_tracing=True)
# Initialize client and deploy
client = Client(project=PROJECT_ID, location=LOCATION)
agent_engine_resource = client.agent_engines.create(
agent=adk_app,
config={
"staging_bucket": STAGING_BUCKET, # GCS bucket for packaging artifacts
"display_name": "Creative Director",
# Python packages installed in the managed runtime - pin for reproducibility
"requirements": [
"google-cloud-aiplatform[agent_engines]>=1.132.0,<2.0.0",
"google-adk[a2a]==1.31.1",
"google-genai>=1.70.0",
"google-cloud-storage>=2.10.0",
"python-dotenv>=1.0.0",
"pydantic>=2.0.0",
"cloudpickle>=3.0.0",
],
# Specialist URLs passed as env vars - the orchestrator reads these at runtime
"env_vars": {
"COPYWRITER_AGENT_URL": COPYWRITER_URL,
"DESIGNER_AGENT_URL": DESIGNER_URL,
"STRATEGIST_AGENT_URL": STRATEGIST_URL,
"CRITIC_AGENT_URL": CRITIC_URL,
"PM_AGENT_URL": PM_URL,
},
},
)
resource_name = agent_engine_resource.api_resource.name
agent_engine_id = resource_name.split("/")[-1]
Was passiert im Hintergrund?
1. Agent Engine packages your App + requirements into a container
2. Uploads it to the staging bucket in your project
3. Deploys to managed compute (you never see or manage the VM)
4. Returns a resource name: projects/.../locations/.../reasoningEngines/<id>
5. That ID is saved to .env as AGENT_ENGINE_ID
Nach der Bereitstellung stellt der Orchestrator über die URLs in seinen Umgebungsvariablen eine Verbindung zu den fünf Cloud Run-Spezialisten her.
- Diese werden über
.envübergeben, bevor das Bereitstellungsskript ausgeführt wird.
14. End-to-End-Kampagne durchführen
Das gesamte System wird bereitgestellt. Führen Sie eine vollständige Kampagne über die Agent Runtime-Sandbox aus.
Playground für die Laufzeit für KI-Agenten öffnen
- Rufen Sie https://console.cloud.google.com/agent-platform/runtimes auf. Sie können auch über Agent Platform > Agents > Deployments zur Agent Runtime navigieren.
- Wählen Sie die bereitgestellte Agent Runtime aus (
creative-director). - Klicken Sie in der linken Seitenleiste auf Playground.
- Klicken Sie auf Neue Sitzung, um eine neue Unterhaltung zu starten.
Eine vollständige Kampagne durchführen
Fügen Sie diesen Briefing in den Chat ein und senden Sie ihn:
Create a complete Instagram campaign for:
- Product: EcoFlow Smart Water Bottle (tracks hydration, keeps drinks cold 24h)
- Target Audience: Health-conscious millennials, 25-35 years old
- Platform: Instagram
- Goal: Brand awareness + drive website traffic
- Brand Voice: Motivational, clean, science-backed
- Budget: $3,000
- Timeline: Launch in 2 weeks
Der Creative Director führt alle fünf Agents nacheinander aus:
- Markenstratege → Marktforschung, Wettbewerbsanalyse, Zielgruppenstatistiken
- Copywriter → 3 Instagram-Beiträge mit Untertiteln, Hashtags und CTAs
- Designer → Visuelle Konzepte + echte Bilder, die über Gemini generiert werden (GCS-URIs) für jeden Beitrag
- Kritiker → Qualitätsprüfung mit den Ergebnissen APPROVED / NEEDS_REVISION
- (Überarbeitung bei Bedarf) → Texter oder Designer werden mit Feedback erneut kontaktiert
- Projektmanager → 2-Wochen-Zeitplan, Aufgabenaufschlüsselung, Budgetzuweisung

Single-Agent-Routing testen
Senden Sie diese kürzere Anfrage in einer neuen Sitzung:
Research the luxury skincare market - top brands and trends in 2025
Der Creative Director leitet die Anfrage nur an den Brand Strategist weiter. Es werden keine anderen Kundenservicemitarbeiter kontaktiert. Das ist die Logik zur Klassifizierung von Anfragen aus der Systemanweisung, die ordnungsgemäß funktioniert.
Ausführungstraces prüfen
Führen Sie in der Konsole folgende Schritte aus:
- Klicken Sie in der linken Seitenleiste neben „Playground“ auf Traces (Verläufe).
- Wählen Sie unter Trace View (Trace-Ansicht) den Trace für die Sitzung aus, die Sie gerade ausgeführt haben.
- Erweitern Sie den Ablaufbaum, um jeden Agentenaufruf, seine Ein- und Ausgaben, die Latenz und die Tokennutzung zu sehen.
Jeder A2A-Anruf bei einem Spezialisten wird als separater Zeitraum angezeigt. Sie können genau sehen, welchen Kontext der Creative Director an die einzelnen Agents übergeben hat und was er zurückerhalten hat.
Optional: Vom Terminal aus ausführen
Sie können die Kampagne auch programmatisch mit dem run_campaign.py-Skript ausführen, das bereits im Starter enthalten ist.
cd ~/ai-creative-studio/workshop/starter
uv run run_campaign.py
15. Bereinigen
Google Cloud-Ressourcen bereinigen, um laufende Gebühren zu vermeiden
Führen Sie das Teardown-Skript aus. Es liest Ihre .env und löscht alles, was in diesem Codelab erstellt wurde:
bash deploy/teardown_gcp.sh
Das Skript zeigt Ihnen genau, was gelöscht wird, und fordert Sie auf, die Aktion zu bestätigen, bevor etwas gelöscht wird:
Ressource | Was wird gelöscht? |
Cloud Run-Dienste | Markenstratege, Texter, Designer, Kritiker, Projektmanager |
Laufzeit für KI-Agenten | Reasoning Engine für Creative Director + alle Sitzungen |
Artifact Registry |
|
GCS-Buckets |
|
Secret Manager |
|
Prüfen, ob alles entfernt wurde
gcloud run services list --region=us-central1
gcloud storage buckets list --project=$GCP_PROJECT_ID
Erwartete Ausgabe: leere Listen oder nur Ihre eigenen vorhandenen Ressourcen.
16. Zusammenfassung
Glückwunsch! Sie haben ein Multi-Agenten-KI-System in Produktionsqualität in Google Cloud entwickelt und bereitgestellt.
Was Sie erstellt haben
Agent | Funktion | Bereitstellung |
Brand Strategist | Marktforschung über die Google Suche | Cloud Run |
Copywriter | Instagram-Bildunterschriften erstellen | Cloud Run |
Designer | Bildgenerierung über Gemini + GCS-Upload | Cloud Run |
Kritiker | Qualitätsprüfung mit Bewertung | Cloud Run |
Projektmanager | Zeitachse + Notion MCP | Cloud Run |
Creative Director | Vollständige Orchestrierung über A2A | Laufzeit für KI-Agenten |
Wichtige Muster, die Sie gelernt haben
- ADK
Agent: LLM-Agent mit einer Anweisung und optionalen Tools definieren adk web: Lokales Ausführen und Testen eines beliebigen ADK-KI-Agenten mit einer integrierten Chat-BenutzeroberflächeSkillToolset: Wiederverwendbares Wissen in modularen Dateien verpacken, die bei Bedarf geladen werdenFunctionTool: Beliebige Python-Funktionen (oder externe Modelle) als aufrufbare Agent-Tools einbindento_a2a()– Beliebigen ADK-Agent als A2A-kompatiblen HTTPS-Dienst verfügbar machenRemoteA2aAgent+AgentTool: Remote-Agents als aufrufbare Tools orchestrierenMcpToolset: Verbindung zu externen Diensten über MCP-Stdio-Server herstellenEventsCompactionConfig– Tokenlimits in langen Multi-Agenten-Workflows berücksichtigen- Strukturierte Kritiker-Ausgabe – maschinenlesbare Qualitätskontrolle mit automatischer Überarbeitung
- Cloud Run: Containerisierte Agents im großen Maßstab bereitstellen
- Agent Runtime: Orchestratoren mit verwalteten Sitzungen und Tracing hosten
Nächste Schritte
- Dialogbasierte Bildbearbeitung mit der Bearbeitungsfunktion von
gemini-3.1-flash-image-previewin Designer einfügen - IAM-Authentifizierung zu Cloud Run-Diensten hinzufügen (
--allow-unauthenticatedentfernen) - Einen Spezialisten durch einen LangGraph- oder CrewAI-Agenten ersetzen – A2A ist frameworkunabhängig
- Nutzerfeedback als Tool hinzufügen, damit Teilnehmer Ausgaben bewerten und iterieren können
- Agent Runtime-Tracing in der Cloud Console ansehen