1. Übersicht
Die KI- und Technologielandschaft entwickelt sich so schnell, dass niemand den Überblick behalten kann. Täglich werden neue Modelle, Publikationen und Produkte veröffentlicht. Ein Digest-Agent, der die Schlagzeilen des Tages abruft, prägnante Zusammenfassungen schreibt und jeden Morgen ein PDF generiert, würde das Problem lösen. Bisher musste man dafür jedoch ein Framework auswählen, Tools in Python definieren, eine Orchestrierungsschleife schreiben, einen Container erstellen und in Cloud Run bereitstellen. All das, bevor der Agent eine einzige Webanfrage gestellt hatte.
Verwaltete KI-Agenten in der Gemini API ändern die Situation. Sie schreiben zwei Markdown-Konfigurationsdateien und ein vorgefertigtes Renderer-Skript, führen einen API-Aufruf aus und eine echte Ubuntu-Sandbox wird gestartet, durchsucht das Web, schreibt Ihre Zusammenfassungen und generiert eine PDF-Datei. Keine Container. Keine Bereitstellung. Kein Orchestrierungscode.
In diesem Codelab erstellen Sie genau diesen Agenten: von einer leeren Funktion bis zu einem funktionierenden Tages-Digest, jeweils ein Konzept nach dem anderen.
Umfang
- Ersten verwalteten Agenten in einer echten Linux-Sandbox erstellen und ausführen
- Agent mit redaktionellem Stil, Webquellen und PDF-Skill anpassen
- Sicherheitshook hinzufügen, um schädliche Befehle vor der Ausführung zu blockieren
- Vom Agenten generiertes PDF herunterladen
- Zusammenfassung in einer Multi-Turn-Unterhaltung verfeinern, ohne das Web neu abzurufen
- Agent-Konfiguration speichern und bei zukünftigen Ausführungen per ID aufrufen
- Zusammenfassung über die Gmail API an Ihren Posteingang senden
- Planen, dass der KI-Agent jeden Tag automatisch ausgeführt und gesendet wird
Voraussetzungen
- Python 3.10 und höher
- Ein Gemini API-Schlüssel: aistudio.google.com/api-keys (kostenlose Stufe enthalten; Abrechnung für ununterbrochene Ausführungen empfohlen)
2. Was sind verwaltete KI-Agenten in der Gemini API?
Drei Ebenen von KI-Systemen
Bevor wir uns den Code ansehen, hier eine Übersicht, wie sich Verwaltete KI-Agenten im Vergleich zu den beiden Alternativen einordnen:
Level | Beschreibung | Wer verwaltet die Infrastruktur? |
Standard-LLM | Sie geben einen Prompt ein und das Modell antwortet mit Text. Keine Hände, kein Gedächtnis, kein Internet. | N/A: Kann nichts selbstständig tun |
Selbst gehosteter Agent | Sie verbinden ADK/LangChain/AutoGen + Docker + Tools + Speicher. | Sie: alles (oder eine verwaltete Plattform wie Agent Engine) |
Managed Agent | Sie geben ihm ein Ziel vor. Google stellt eine sichere Sandbox bereit. Der Agent schreibt Code, führt ihn aus, liest Fehler, sucht im Web und behebt Fehler autonom. | Google: alles |
In diesem Codelab geht es um die dritte Zeile. Sie stellen eine Aufgabe und Konfigurationsdateien bereit. Google kümmert sich um alles Weitere.
Was Sie mit ADK + Cloud Run entwickeln können
Wenn Sie einen Nachrichten-Digest-Agenten erstellen möchten, der im Web surft, Python ausführt und ein PDF generiert, benötigen Sie mit ADK + Cloud Run Folgendes:
# agent.py: define tools and wire up the agent
from google.adk.agents import LlmAgent
from google.adk.tools import google_search, built_in_code_execution
agent = LlmAgent(
name="digest-agent",
model=MODEL,
instruction=AGENTS_MD, # your editorial voice and rules
tools=[google_search, built_in_code_execution],
)
# app.py: serve the agent over HTTP
from google.adk.runners import FastApiRunner
runner = FastApiRunner(agent=agent)
app = runner.app
# pdf_tool.py: custom tool, install reportlab, render PDF
# scraper.py: custom tool, fetch each news source
# streaming.py: wire agent events to your SSE endpoint
# Dockerfile: package everything
FROM python:3.12
COPY . /app
RUN pip install google-adk reportlab requests
CMD ["uvicorn", "app:app", "--host", "0.0.0.0"]
# Deploy to Cloud Run
gcloud run deploy digest-agent \
--image gcr.io/your-project/digest-agent \
--set-secrets GEMINI_API_KEY=gemini-key:latest \
--memory 2Gi
Das ist vor der ersten Ausführung des Agenten. Sie sind weiterhin für die Sandbox-Isolation (damit der Agent Ihren Server nicht beschädigen kann), die Paketinstallation, die Statusverwaltung zwischen Tool-Aufrufen und die Streaminginfrastruktur zuständig, um Ereignisse an einen Client zu senden.
Was durch Verwaltete KI-Agenten ersetzt wird
from google import genai
client = genai.Client()
stream = client.interactions.create(
agent="antigravity-preview-05-2026",
input="Generate the digest.",
stream=True,
environment={
"type": "remote",
"sources": [ # your config files, mounted at startup
{
"type": "inline",
"target": ".agents/AGENTS.md",
"content": AGENTS_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/SKILL.md",
"content": SKILL_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/scripts/generate_pdf.py",
"content": GENERATE_PDF_PY,
},
],
},
)
Voraussetzungen für ADK + Cloud Run | Was Managed Agents für Sie erledigen |
Container-Image + Dockerfile + CI/CD | Vollständig verwaltete Ubuntu-Sandbox (Python 3.12, Node 22, 4 CPUs / 16 GB RAM) |
Cloud Run-Bereitstellung + Skalierung | Wird pro Interaktion bereitgestellt, läuft nach 7 Tagen Inaktivität automatisch ab |
Sandbox-Isolation | Isoliert pro Interaktion |
Benutzerdefiniertes PDF-Tool + | Agent installiert Pakete in der Sandbox |
SSE-Streaminginfrastruktur |
|
Tool-Definitionen in Python | Integrierte Tools: Websuche, Codeausführung, Dateisystem |
Statusverwaltung zwischen Tool-Aufrufen | In den Reasoning Loop des Agents integriert |
Sie schreiben Konfigurationsdateien (AGENTS.md, SKILL.md, ein vorgefertigtes Skript) und führen einen API-Aufruf aus. Google kümmert sich um alles Weitere.
So funktioniert die Sandbox
interactions.create() call
│
▼
Google provisions Ubuntu sandbox (Python 3.12, Node 22, 4 CPU / 16 GB RAM)
│
▼
Agent reasoning loop:
plan → fetch URLs → run Python → write files → reason → repeat
│
▼
Events stream back in real time: tool calls, text chunks, completion
│
▼
interaction.completed → environment_id + interaction_id
Die Sandbox bleibt 7 Tage lang inaktiv. Sie können den Vorgang mit environment_id fortsetzen, um die Ausgabe zu optimieren, Folgeaufgaben auszuführen oder den Vorgang in einen gespeicherten benannten Agent zu verzweigen.
3. Erstelle ein
Option A: Cloud Shell (empfohlen)
Klicken Sie auf den Button unten, um dieses Codelab in Google Cloud Shell zu öffnen. Alle Abhängigkeiten sind vorinstalliert.
Option B: Lokale Einrichtung
git clone https://github.com/Saoussen-CH/tech-digest-managed-agent.git
cd tech-digest-managed-agent
Installieren Sie uv, falls erforderlich:
curl -LsSf https://astral.sh/uv/install.sh | sh
API-Schlüssel konfigurieren
cp .env.example .env
cloudshell edit .env
Schlüssel festlegen:
GEMINI_API_KEY=your-key-here
Abhängigkeiten installieren
uv sync
4. Ersten Agentenaufruf ausführen
Startdatei öffnen
cloudshell edit run_digest.py
run_digest() hat jetzt eine Aufgabe zum Ausfüllen und drei weitere für den nächsten Schritt. Zwei Helfer sind bereits oben vorausgefüllt:
load_source(path): Liest eine Datei aus.agents/relativ zum Skript. Sie verwenden sie in der nächsten Übung, um die redaktionelle Stimme, das PDF-Playbook und den Renderer in die Sandbox einzubinden.run_stream(stream): Verarbeitet den Ereignisstream und gibt(environment_id, interaction_id)zurück. Sie müssen die Ereignisschleife nicht selbst schreiben.
Angaben
TODO 1:Ersetzen Sie pass durch (ignorieren Sie TODOs 3 und 4 vorerst; diese sind für den nächsten Schritt):
from google import genai
client = genai.Client()
stream = client.interactions.create(
agent=BASE_AGENT,
agent_config={"type": "antigravity", "model": "gemini-3.7-flash"},
input="Fetch the Hacker News front page and list the top 5 stories.",
stream=True,
environment="remote",
)
environment_id, interaction_id = run_stream(stream)
print(f"\nDone. environment_id={environment_id}")
Funktionsweise der einzelnen Teile
genai.Client() liest GEMINI_API_KEY aus der Umgebung. Alles andere läuft über diesen Client.
interactions.create() ist der Hauptanruf. Dazu sind vier Parameter erforderlich:
agent=BASE_AGENT: Wählt den Antigravity-Agenten (antigravity-preview-05-2026) aus, einen verwalteten Agenten für allgemeine Zwecke, der standardmäßig auf Gemini 3.7 Flash basiert. Sie können das zugrunde liegende Modell mitagent_configkonfigurieren (Optionen:gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite). Es enthält drei integrierte Tools, die standardmäßig aktiviert sind:code_execution(Bash, Python, Node.js ausführen),google_searchundurl_context(Webseiten abrufen und lesen). Dateisystemtools (read_file,write_file,list_files) werden automatisch aktiviert, wenn Sie den Parameterenvironmentübergeben. Mit einem Aufruf wird eine vollständig verwaltete Ubuntu-Umgebung mit vorinstalliertem Python 3.12, Node.js 22, git, pip und curl bereitgestellt. Es gibt keinen Container, der erstellt werden muss, und keine Bereitstellung, die ausgeführt werden muss.input: Die Aufgabe für diesen Lauf. Der Agent durchsucht Hacker News und analysiert die Ergebnisse.environment="remote": Stellt eine neue Cloud-Sandbox für diese Interaktion bereit.stream=True: Gibt ein iterierbares Objekt mit Ereignissen zurück, anstatt zu blockieren. Ohne diesen Parameter wartet der Aufruf 30–90 Sekunden und gibt die gesamte Ausgabe auf einmal alsinteraction.output_textzurück. Beim Streaming sehen Sie den Grund für die Aktion des Agents und die Aktion selbst in Echtzeit. Streaming ist hier keine erweiterte Funktion, sondern die richtige Standardeinstellung, da eine 90 Sekunden lange Blackbox kein Signal dafür liefert, ob der Kundenservicemitarbeiter arbeitet oder feststeckt.
environment_id ist ein Handle für die gerade ausgeführte Sandbox. Nach interaction.completed wird die Sandbox nicht heruntergefahren, sondern bleibt bis zu 7 Tage lang aktiv. Über environment_id gelangen Sie wieder dorthin. Übergeben Sie sie an einen zweiten interactions.create()-Aufruf. Der Agent wird dann im selben Dateisystem mit denselben Dateien und installierten Paketen fortgesetzt, als wäre er nie beendet worden. Im nächsten Schritt wird die Variable verwendet, um die PDF-Datei herunterzuladen, ohne den Agenten noch einmal auszuführen. Im Schritt danach wird sie verwendet, um die Unterhaltung fortzusetzen.
interaction_id ist ein Handle für den gerade abgeschlossenen Unterhaltungsabschnitt. Übergeben Sie sie als previous_interaction_id im nächsten Aufruf. Der KI-Agent hat dann den vollständigen Kontext der Aussagen und Aktionen in diesem Zug.
Bestätigen
uv run python run_digest.py
Sie sollten die Live-Ausgabe sehen, während der Agent arbeitet:
[agent started]
[tool] run_code
Here are the top 5 stories currently on the Hacker News front page, retrieved via the official Hacker News API:
1. **Qwen 3.6 27B is the sweet spot for local development** (471 points)
2. **.self: A new top-level domain designed to support self-hosting** (116 points)
...
Done. environment_id=e3de58774073f75a6ef42924c6ce2e88
Die API gibt auch bei environment="remote" ein echtes environment_id zurück. Die Sandbox wurde ausgeführt. Es fehlt die Konfiguration: keine Stimme, kein Skill, kein PDF-Generator. Der KI-Agent hat nur Geschichten als Text ausgegeben und dann aufgehört. Im nächsten Schritt werden diese hinzugefügt.
Jede Ausgaberow entspricht einem Ereignis aus run_stream():
| Beschreibung | Was |
| Agent ruft eine URL ab |
|
| KI-Agent, der Code in der Sandbox ausführt |
|
| Agent sucht im Web |
|
| Dateitools und andere |
|
| KI-Agent schreibt Text | direkt an stdout gestreamt |
5. Agent anpassen
Der Agent hatte keine Anweisungen: keine Stimme, keine Fähigkeit, kein PDF-Generator. In diesem Schritt laden Sie die Konfigurationsdateien aus .agents/ und stellen sie in der Sandbox bereit.
Was soll geändert werden?
Nehmen Sie vier Änderungen an run_digest.py vor:
TODO 2:Fügen Sie unter load_source() die drei Konstanten auf Modulebene hinzu (diese befinden sich außerhalb von run_digest(), oben in der Datei):
AGENTS_MD = load_source(".agents/AGENTS.md")
SKILL_MD = load_source(".agents/skills/digest-pdf/SKILL.md")
GENERATE_PDF_PY = load_source(".agents/skills/digest-pdf/scripts/generate_pdf.py")
Öffnen Sie jede Datei, um zu sehen, was Sie laden: AGENTS.md legt die redaktionelle Stimme und die Workflowregeln fest, SKILL.md ist das schrittweise PDF-Playbook und generate_pdf.py ist der vorgefertigte Renderer, den der Agent ausführt.
Nehmen Sie nun zwei weitere Änderungen in run_digest() vor:
TODO 3:Ändern Sie environment von "remote" in das Quellen-Dictionary und legen Sie input auf "Generate the digest." fest:
environment={
"type": "remote",
"sources": [
{
"type": "inline",
"target": ".agents/AGENTS.md",
"content": AGENTS_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/SKILL.md",
"content": SKILL_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/scripts/generate_pdf.py",
"content": GENERATE_PDF_PY,
},
],
},
TODO 4:Fügen Sie diese Zeile direkt nach print(f"\nDone. environment_id={environment_id}") ein:
save_env(ENVIRONMENT_ID=environment_id, INTERACTION_ID=interaction_id)
save_env ist bereits in run_digest.py definiert. Beide IDs werden in .env geschrieben, damit das PDF im nächsten Schritt heruntergeladen werden kann, ohne dass der Agent noch einmal ausgeführt werden muss.
Funktionsweise der einzelnen Quellen
Jede Quelle ist eine Datei, die beim Start in das Sandbox-Dateisystem eingebunden wird, bevor der Agent ausgeführt wird. Die target-Pfade stimmen mit den Pfaden überein, unter denen das Antigravity-Harness die Dateien erwartet:
.agents/
├── AGENTS.md ← auto-loaded as global instructions
└── skills/
└── digest-pdf/
├── SKILL.md ← auto-discovered and registered as a skill
└── scripts/
└── generate_pdf.py ← pre-built renderer the agent can run
Pfad zu | Variable | Was das Geschirr damit macht |
|
| Automatisch geladen als dauerhafte Anweisungen: redaktioneller Stil, Workflow, Ausführungsregeln |
|
| Automatisch erkannt und als benannter Skill registriert; der Agent ruft ihn über den Namen auf |
|
| Vordefinierter PDF-Renderer: Der Agent schreibt |
Bestätigen
uv run python run_digest.py
Die Ausführung dauert jetzt 1–3 Minuten. Sie sollten sehen, dass der Agent Konfigurationsdateien liest, Zusammenfassungen schreibt und die PDF-Datei speichert:
[agent started]
[tool] read_file (/.agents/skills/digest-pdf/SKILL.md)
[tool] list_files (/.agents/skills/digest-pdf/scripts)
[tool] read_file (/.agents/skills/digest-pdf/scripts/generate_pdf.py)
[tool] run_code
[tool] write_file (/workspace/summaries.json)
[tool] run_code
[tool] delete_file (/tmp/test_scrape.py)
I have successfully generated today's tech news digest and saved the formatted document to /workspace/digest.pdf.
Done. environment_id=4129ffd75574e308748e9425d7ec828f
environment_id ist jetzt ein echter Wert: Die Sandbox wurde mit Ihren Konfigurationsdateien ausgeführt und der Agent hat digest.pdf erstellt. Im nächsten Schritt wird vor dem Herunterladen ein Sicherheitsmechanismus eingefügt.
6. Sicherheitshinweis hinzufügen
Mit Hooks können Sie ein Skript in der Sandbox vor oder nach jedem Tool-Aufruf ausführen. Der Digest-Agent verwendet code_execution zum Ausführen von Python-Skripts. Ein pre_tool_execution-Hook kann diese Aufrufe abfangen und destruktive Shell-Befehle blockieren, bevor sie ausgeführt werden.
Die Laufzeit liest .agents/hooks.json aus der Sandbox. Vor jedem Aufruf des Abgleichstools werden die Anrufdetails über stdin an Ihr Gate-Skript weitergeleitet. Das Skript gibt {"decision": "allow"} oder {"decision": "deny", "reason": "..."} in stdout aus. Durch eine Ablehnung wird der Tool-Aufruf abgebrochen. Der Agent sieht Ihren Grund und korrigiert sich selbst.
Angaben
Fügen Sie TODO 5 in run_digest.py diese beiden Konstanten oben nach den vorhandenen load_source-Aufrufen hinzu:
import json
HOOKS_JSON = json.dumps({
"safety-gate": {
"pre_tool_execution": [
{
"matcher": "code_execution",
"hooks": [
{
"type": "command",
"command": "python3 /.agents/hooks-scripts/gate.py",
"timeout": 10,
}
],
}
]
}
}, indent=2)
GATE_PY = """\
#!/usr/bin/env python3
import sys, json
data = json.load(sys.stdin)
cmd = str(data.get("tool_call", {}).get("args", {}))
if "rm -rf" in cmd:
print(json.dumps({"decision": "deny", "reason": "Destructive command blocked by safety gate."}))
else:
print(json.dumps({"decision": "allow"}))
"""
TODO 6:Fügen Sie der Liste sources in interactions.create() zwei weitere Einträge hinzu:
{"type": "inline", "target": ".agents/hooks.json", "content": HOOKS_JSON},
{"type": "inline", "target": ".agents/hooks-scripts/gate.py", "content": GATE_PY},
So werden Hooks während des Digest-Laufs ausgelöst
Jedes Mal, wenn der Agent code_execution aufruft, um ein Python-Skript oder einen Shell-Befehl auszuführen, leitet die Laufzeit die Aufrufdetails zuerst an gate.py weiter. Wenn der Befehl rm -rf enthält, gibt der Hook deny zurück. Der Agent erhält den Ablehnungsgrund und versucht es noch einmal mit einer sicheren Alternative. Alle anderen Code-Ausführungsaufrufe werden unverändert weitergeleitet.
Bestätigen
uv run python run_digest.py
Die Ausgabe ist identisch mit der vorherigen: Das Safety Gate lässt alle normalen Befehle zur PDF-Generierung zu. Um zu bestätigen, dass der Hook ausgelöst wird, ändern Sie die Agent-Eingabe vorübergehend so, dass rm -rf /tmp/test ausgeführt werden soll. Der Agent meldet dann, dass der Befehl blockiert wurde, und schlägt eine Alternative vor.
7. PDF herunterladen
Der Agent hat digest.pdf in /workspace/digest.pdf in der Sandbox geschrieben. Der Umgebungs-Snapshot ist als TAR-Archiv über die Gemini Files API verfügbar.
Installieren Sie requests, falls erforderlich:
uv pip install requests
Was muss ausgefüllt werden?
Öffnen Sie download_pdf.py. Es enthält zwei TODOs.
TODO 1:Füllen Sie den requests.get()-Aufruf aus:
r = requests.get(
f"https://generativelanguage.googleapis.com/v1beta/files/environment-{environment_id}:download",
params={"alt": "media"},
headers={"x-goog-api-key": api_key},
allow_redirects=True,
)
r.raise_for_status()
Die URL verweist auf den Sandbox-Snapshot. params={"alt": "media"} gibt Rohbytes anstelle von Metadaten zurück. Mit Ihrem vorhandenen GEMINI_API_KEY wird auch die Files API authentifiziert.
TODO 2:PDF aus dem TAR-Archiv suchen und extrahieren:
member = next(m for m in tar.getmembers() if m.name.endswith("workspace/digest.pdf"))
tar.extract(member, path=tmp, filter="data")
Das Präfix des TAR-Pfads variiert je nach Ausführung. Suchen Sie daher nach dem Suffix, anstatt den genauen Pfad fest zu codieren. filter="data" unterdrückt die Python 3.13-Warnung zur Einstellung der Unterstützung für unsichere tar-Extraktion.
Bestätigen
uv run python download_pdf.py
Saved digest.pdf (48,231 bytes)
Öffnen Sie digest.pdf im selben Verzeichnis. Sie enthält die formatierte Zusammenfassung, die der Agent aus Live-Webseiten generiert hat.
8. Unterhaltung fortsetzen
Sie haben bereits digest.pdf. Wenn Sie nur die Datei haben wollten, sind Sie fertig. In diesem Schritt geht es um etwas anderes: den Agenten zu bitten, den Digest zu ändern, ohne das Web neu abzurufen.
Die Sandbox ist noch aktiv. Der KI-Agent hat weiterhin /workspace/digest.pdf und erinnert sich an jede Geschichte, die er zusammengefasst hat. Mit einem zweiten interactions.create()-Aufruf wird eine Follow-up-Nachricht in dieselbe Sandbox gesendet. Hier wird das Tool aufgefordert, unter jeder Geschichte eine Anmerkung zum Thema „Warum das wichtig ist“ hinzuzufügen. Das PDF wird dann direkt aktualisiert, ohne dass die Inhalte neu abgerufen oder zusammengefasst werden müssen.
Was muss ausgefüllt werden?
Öffnen Sie refine_digest.py. Es gibt drei TODOs.
TODO 1 und 2:Füllen Sie die beiden Mehrfachdialog-Parameter in interactions.create() aus:
environment=environment_id,
previous_interaction_id=interaction_id,
environment=environment_id setzt dieselbe Sandbox mit ihren Dateien und Paketen fort. previous_interaction_id=interaction_id stellt dem Agenten den Unterhaltungsverlauf zur Verfügung. Ansonsten ändert sich nichts im Vergleich zum ersten Anruf.
TODO 3:Speichern Sie den neuen interaction_id nach der Ereignisschleife wieder in .env:
save_env(INTERACTION_ID=interaction_id)
Bei jedem interactions.create()-Aufruf wird eine neue interaction_id erstellt. Wenn Sie die Änderung zurückschreiben, wird diese Verfeinerung beim nächsten Lauf als previous_interaction_id berücksichtigt und die Abbiegevorgänge werden korrekt verkettet. Die Sandbox-ID ändert sich nie. Daher muss ENVIRONMENT_ID nicht aktualisiert werden.
Die beiden Parameter, die den Mehrfachdialog ermöglichen
ID | Was wird beibehalten? | Analogie |
| Dateien, installierte Pakete, Systemstatus: alles im Linux-Dateisystem | Zwischen Besprechungen am selben Schreibtisch bleiben |
| Unterhaltungsverlauf: Was der Agent in früheren Runden gesagt und getan hat | Erinnern Sie sich an das, was im letzten Meeting besprochen wurde. |
Sie können eine der beiden IDs unabhängig voneinander übergeben:
environment_id: Dateien und Pakete wiederverwenden, aber eine neue Unterhaltung starten. Nützlich für eine neue Aufgabe im selben Arbeitsbereich.previous_interaction_id: Der Kontext der Unterhaltung wird fortgesetzt, aber in einer neuen Sandbox (Dateien sind nicht mehr vorhanden).- Beide: volle Kontinuität, die in diesem Schritt verwendet wird.
Ohne environment_id: leere Sandbox, kein PDF. Ohne previous_interaction_id: kein Kontext, der Kundenservicemitarbeiter kann keinen bestimmten Abschnitt optimieren.
Bestätigen
uv run python refine_digest.py
Der Stream sollte schnell sein, da der Kundenservicemitarbeiter nichts neu abruft. Danach:
Refinement done.
Saved digest_v2.pdf (52,418 bytes)
Öffnen Sie digest_v2.pdf und vergleichen Sie sie mit digest.pdf. Jeder Geschichte sollte nun eine Zeile „Warum das wichtig ist“ hinzugefügt werden.
9. Verwaltete Agent-Konfiguration beibehalten
Bisher wurde bei jedem Aufruf AGENTS.md, SKILL.md und generate_pdf.py inline übergeben. Das funktioniert, aber Ihr Aufrufcode überträgt bei jeder Ausführung den gesamten Dateiinhalt. agents.create() speichert die Konfiguration in einem benannten Agent auf Google-Seite. Beim nächsten Aufruf wird nur die Agent-ID übergeben:
Inline calls: send sources on every call
Named agent: bake once → invoke by ID, no sources
Was muss ausgefüllt werden?
Öffnen Sie save_agent.py. Es gibt eine TODO-Anweisung (TODO 1).
Die Konstanten werden direkt aus run_digest.py importiert (keine Duplizierung):
from run_digest import BASE_AGENT, AGENTS_MD, SKILL_MD, GENERATE_PDF_PY
TODO 1:Füllen Sie den agents.create()-Aufruf aus:
agent = client.agents.create(
id="my-digest",
base_agent=BASE_AGENT,
agent_config={
"type": "antigravity",
"model": "gemini-3.7-flash",
},
description="Daily tech digest with editorial voice and PDF generation.",
base_environment={
"type": "remote",
"sources": [
{
"type": "inline",
"target": ".agents/AGENTS.md",
"content": AGENTS_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/SKILL.md",
"content": SKILL_MD,
},
{
"type": "inline",
"target": ".agents/skills/digest-pdf/scripts/generate_pdf.py",
"content": GENERATE_PDF_PY,
},
],
},
)
Mit agent_config wird das zugrunde liegende Modell festgelegt. gemini-3.7-flash ist die Standardeinstellung und die beste Wahl für diesen Workflow. gemini-3.6-flash, gemini-3.5-flash und gemini-3.5-flash-lite sind verfügbar, wenn Sie eine weniger aufwendige oder kostengünstigere Ausführung wünschen.
base_environment (nicht environment) ist der Hauptunterschied zum Inline-Aufruf im vorherigen Schritt: Die Quellen werden auf der Seite von Google gespeichert und bei jedem zukünftigen Aufruf automatisch eingebunden. Führen Sie sie nur einmal aus, nicht bei jeder Zusammenfassung.
Bestätigen: Agent speichern
uv run python save_agent.py
Saved: my-digest
my-digest: Daily tech digest with editorial voice and PDF generation.
Gespeicherten Agenten aufrufen
Öffnen Sie invoke_agent.py. Der gespeicherte Agent wird ohne Quellen anhand der ID aufgerufen:
stream = client.interactions.create(
agent="my-digest",
input="Generate the digest.",
stream=True,
environment="remote",
)
Vergleichen Sie dies mit dem Inline-Aufruf: agent=BASE_AGENT wird durch "my-digest" ersetzt und der vollständige environment-Block mit drei Inline-Quellen wird durch environment="remote" ersetzt. Die Konfiguration ist bereits auf Google-Seite integriert.
Überprüfen: Gespeicherten Agenten aufrufen
uv run python invoke_agent.py
Sie sollten denselben Livestream wie beim Inline-Lauf sehen, aber der Aufruf enthält keine Quelldateien. Nach dem Lauf werden ENVIRONMENT_ID und INTERACTION_ID in .env aktualisiert, sodass Sie wie gewohnt mit refine_digest.py fortfahren können.
[agent started]
[tool] read_file
[tool] write_file
[tool] run_code
I have successfully created today's tech news digest.
Done. environment_id=9a1c3e02-...
10. Über Gmail senden
Der Agent hat das Kurzfassung erstellt und unter /workspace/digest.pdf gespeichert. Bisher haben Sie es lokal heruntergeladen. In diesem Schritt wird die E‑Mail direkt in Ihren Posteingang gesendet, indem der Agent die Gmail REST API aus der Sandbox heraus aufruft.
Sie rufen ein OAuth 2.0-Zugriffstoken lokal ab und übergeben es im Prompt input an den Agent. Der Agent verwendet code_execution, um eine MIME-E-Mail mit der angehängten PDF-Datei zu erstellen und sie an die Gmail API zu senden. Keine benutzerdefinierten Tools, keine MCP-Serverregistrierung.
Vorbereitung
Aktivieren Sie die Gmail API in Ihrem GCP-Projekt und erstellen Sie eine OAuth 2.0-Client-ID:
- Rufen Sie console.cloud.google.com/apis/library/gmail.googleapis.com auf und aktivieren Sie die Gmail API.
- Rufen Sie APIs & Dienste > Anmeldedaten > Anmeldedaten erstellen > OAuth 2.0-Client-ID auf.
- Anwendungstyp: Desktop-App. Laden Sie die JSON-Datei herunter und speichern Sie sie als
credentials.jsonim Stammverzeichnis des Projekts.
Fügen Sie die E‑Mail-Adresse des Empfängers in .env ein:
RECIPIENT_EMAIL=you@gmail.com
Installieren Sie bei Bedarf die Authentifizierungsbibliotheken:
uv sync
Was muss ausgefüllt werden?
Öffnen Sie send_digest.py. Es enthält zwei TODOs.
TODO 1:OAuth 2.0-Zugriffstoken laden oder aktualisieren:
creds = None
if TOKEN_FILE.exists():
creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
TOKEN_FILE.write_text(creds.to_json())
else:
flow = InstalledAppFlow.from_client_secrets_file("credentials.json", SCOPES)
creds = flow.run_local_server(port=8080, open_browser=False)
TOKEN_FILE.write_text(creds.to_json())
Entfernen Sie die Zeile raise NotImplementedError nach dem Hinzufügen. Beim ersten Ausführen wird ein Browser für den OAuth-Zustimmungsbildschirm geöffnet. Das Token wird in .gmail_token.json für zukünftige Ausführungen im Cache gespeichert.
AUFGABE 2:Ersetzen Sie input="" durch die E‑Mail-Anleitung. Das Token ist bereits im Bereich als creds.token:
input=(
"Use the Gmail REST API to send an email:\n"
f" To: {recipient}\n"
" Subject: Tech Digest - <today's date in YYYY-MM-DD format>\n"
" Attachment: /workspace/digest.pdf attached as digest.pdf\n\n"
"For the body, read /workspace/summaries.json and format it as a "
"human-readable newsletter, NOT raw JSON. Use this structure:\n"
" Tech Digest - <date>\n\n"
" === <source name> ===\n"
" 1. <title>\n"
" <summary>\n\n"
"Steps:\n"
"1. Parse /workspace/summaries.json and build the formatted body text above.\n"
"2. Read /workspace/digest.pdf as bytes.\n"
"3. Build a MIME multipart message using Python's email library.\n"
"4. Base64url-encode the raw message.\n"
"5. POST to https://gmail.googleapis.com/gmail/v1/users/me/messages/send "
"with Authorization header using this token: "
f"{creds.token}"
),
Funktionsweise der einzelnen Teile
Die Interaktion wird in derselben Sandbox fortgesetzt, in der der Agent bereits digest.pdf und summaries.json generiert hat. previous_interaction_id stellt dem Agenten den Unterhaltungsverlauf zur Verfügung.
Das Zugriffstoken wird im String input übergeben. Der Agent liest sie aus dem Prompt und verwendet sie im Authorization: Bearer-Header, wenn er die Gmail API aufruft. Es greift nie auf Ihren lokalen Computer oder Ihr Dateisystem zu.
Der Agent verwendet code_execution, um ein Python-Skript in der Sandbox zu schreiben und auszuführen: Er liest summaries.json, formatiert es als Newsletter, liest digest.pdf, erstellt eine MIME-Multipart-Nachricht, base64url-codiert sie und sendet sie per POST an https://gmail.googleapis.com/gmail/v1/users/me/messages/send.
Bestätigen
uv run python send_digest.py
Sending digest...
[agent started]
[tool] read_file (/workspace/summaries.json)
[tool] run_code
[tool] run_code
Email sent successfully.
Email sent. Check your inbox.
Bitte sehen Sie in Ihrem Posteingang nach. Die E‑Mail geht mit dem im Newsletter-Format formatierten Text und dem Anhang digest.pdf ein.
11. Tägliche Läufe planen
Bisher wurde jeder Schritt manuell ausgelöst. Mit Triggern können Sie festlegen, dass der benannte KI-Agent automatisch nach einem Cron-Ausdruck ausgeführt wird. Der Agent wird zur geplanten Zeit ausgelöst, führt den gesamten Zusammenfassungsworkflow aus und die Umgebung bleibt zwischen den Ausführungen bestehen. Daher sind Pakete, die bei der ersten Ausführung installiert wurden, bei jeder nachfolgenden Ausführung verfügbar.
Manual: python run_digest.py → runs once, now
Trigger: client.triggers.create() → runs every morning, automatically
Was muss ausgefüllt werden?
Öffnen Sie create_trigger.py. Es gibt eine TODO-Anmerkung.
TODO 1:Füllen Sie den triggers.create()-Aufruf aus. Der Trigger führt den gesamten Workflow jeden Tag aus: Er generiert die Zusammenfassung UND sendet sie an Ihren Posteingang. Da Zugriffstokens nach einer Stunde ablaufen, wird das Aktualisierungstoken aus .gmail_token.json als Inline-Quelle eingefügt, damit der Agent es bei jedem Ausführen gegen ein neues Token eintauschen kann.
trigger = client.triggers.create(
schedule="0 9 * * *",
time_zone="UTC",
display_name="daily-tech-digest",
max_consecutive_failures=3,
execution_timeout_seconds=600,
interaction={
"agent": "my-digest",
"input": (
f"Generate the daily tech digest following AGENTS.md instructions. "
f"Then send an email to {recipient}:\n"
"- Subject: Tech Digest - <today's date in YYYY-MM-DD format>\n"
"- Body: the content of /workspace/summaries.json formatted as a readable "
"newsletter (NOT raw JSON).\n"
"- Attachment: /workspace/digest.pdf\n\n"
"For Gmail auth: read /workspace/.gmail_creds.json, POST to "
"https://oauth2.googleapis.com/token with grant_type=refresh_token "
"and the client_id, client_secret, refresh_token from the file to get an "
"access_token. Then POST to "
"https://gmail.googleapis.com/gmail/v1/users/me/messages/send "
"with Authorization: Bearer <access_token>."
),
"environment": {
"type": "remote",
"sources": [
{
"type": "inline",
"target": "/workspace/.gmail_creds.json",
"content": gmail_creds,
}
],
},
},
)
execution_timeout_seconds=600 ist das Standardzeitlimit. max_consecutive_failures=3 pausiert den Trigger automatisch nach drei fehlgeschlagenen Ausführungen in Folge. Der API-Standardwert ist fünf. Drei ist für einen Workshop konservativer.
Mit der Liste sources wird .gmail_creds.json in der Sandbox unter /workspace/.gmail_creds.json eingefügt. Der Agent liest es, tauscht das Aktualisierungstoken gegen ein neues Zugriffstoken ein und ruft die Gmail API auf. Aktualisierungstokens laufen nicht ab. Daher funktioniert dies bei jeder geplanten Ausführung ohne manuelle Aktualisierung des Tokens.
Entfernen Sie die Zeile raise NotImplementedError, nachdem Sie den Aufruf hinzugefügt haben.
Bestätigen
uv run python create_trigger.py
Trigger created: trig_abc123
Next run: 2026-07-23T09:00:00Z
Mit create_trigger.py wird die Trigger-ID automatisch in .env gespeichert.
So rufen Sie den Ausführungsverlauf nach einem Lauf auf:
uv run python check_trigger.py
So lösen Sie den Trigger sofort aus, ohne auf die nächste geplante Zeit zu warten:
uv run python fire_trigger.py
So pausieren oder löschen Sie den Trigger:
uv run python pause_trigger.py
12. Bereinigen
Die Sandbox läuft nach 7 Tagen Inaktivität automatisch ab. Es gibt keine Server, die beendet werden können. Es sind keine Container zum Löschen vorhanden.
Wenn Sie eine Agent-Konfiguration gespeichert haben, löschen Sie sie:
uv run python delete_agent.py
13. Zusammenfassung
Sie haben einen verwalteten Agent von Grund auf erstellt, wobei Sie sich jeweils auf ein Konzept konzentriert haben. Das haben Sie in den einzelnen Übungen gelernt:
Training | Konzept | Key API |
Ersten Anruf starten | Eine echte Linux-Sandbox bereitstellen und ihre Ereignisse live streamen |
|
Agent anpassen | Konfigurationsdateien bereitstellen; IDs im selben Lauf in |
|
Sicherheitshaken hinzufügen | Tool-Aufrufe abfangen, bevor sie ausgeführt werden; schädliche Befehle ablehnen |
|
PDF herunterladen | PDF herunterladen, ohne den Agenten noch einmal auszuführen | Gemini Files API |
Unterhaltung fortsetzen | Unterhaltung fortsetzen, ohne das Web neu abzurufen |
|
KI-Agentenkonfiguration beibehalten | Agent-Konfiguration beibehalten; Aufruf über ID, keine Quellen erforderlich |
|
Über Gmail senden | Ein OAuth-Token lokal abrufen und an den Agent übergeben, der die Gmail REST API über | OAuth 2.0, |
Tägliche Ausführungen planen | KI‑Agenten automatisch nach einem Cron-Zeitplan ausführen |
|
Schlüsselmuster
- Ein Aufruf, eine Sandbox:
interactions.create()übernimmt die gesamte Infrastruktur (keine Container zum Bereitstellen, keine Pakete zur lokalen Installation) - Progressives Streaming:
stream=Trueverwandelt eine 90-sekündige Blackbox in einen Live-Feed von Tool-Aufrufen und Textblöcken. - Inline-Quellen:
AGENTS.md,SKILL.mdund vorgefertigte Skripts werden ohne Upload- oder Bereitstellungsschritt in die Sandbox eingebunden. - Automatische Erkennung von Harness: Dateien, die in
.agents/platziert werden, werden automatisch erkannt (keine SDK-Konfiguration erforderlich). - Zweidimensionaler Status:
environment_idverfolgt Dateien und Pakete,previous_interaction_idverfolgt den Unterhaltungskontext. Beide können unabhängig voneinander übergeben werden. - Snapshot-Download: Die Umgebung ist ein vollständiges Dateisystem-Tar, auf das über die Gemini Files API zugegriffen werden kann.
- Benannte Agents:
agents.create()wird dauerhaft eingebunden. Bei zukünftigen Aufrufen werden nur die Agent-ID undenvironment="remote"ohne Quellen übergeben. - Hooks:
hooks.json+ ein Gate-Skript fängt Toolaufrufe vor der Ausführung ab. Einedeny-Antwort bricht den Aufruf ab und der Agent korrigiert sich selbst. - Externe API-Aufrufe: Übergeben Sie Anmeldedaten im
input-Prompt. Der KI-Agent schreibt und führt den Integrationscode in der Sandbox übercode_executionaus. - Trigger: Planen Sie einen Agenten mit einem Cron-Ausdruck mit
client.triggers.create(). Die Umgebung bleibt über Ausführungen hinweg bestehen.
ADK + Cloud Run im Vergleich zu verwalteten KI-Agenten: der Unterschied auf einen Blick
Funktion | ADK + Cloud Run | Verwaltete KI-Agenten in der Gemini API |
Sandbox bereitstellen |
|
|
Tools definieren | Beim Agenten registrierte Python-Funktionen | Integrierte Tools: Websuche, Codeausführung, Dateisystem |
Pakete installieren |
| KI-Agent wird in der Sandbox |
Stream-Ereignisse | Benutzerdefinierte SSE-Infrastruktur |
|
Sitzung fortsetzen | Sitzungsdatenbank + Kontextinjektion |
|
Konfigurationsdateien | Im Agent hartcodiert oder beim Start eingefügt | Eingebunden über |
Zu verwaltende Infrastruktur | Container, Cloud Run, IAM, Secrets | – |