KI-Agent für einen täglichen Tech-Digest mit verwalteten KI-Agenten in der Gemini API erstellen

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 + pip install

Agent installiert Pakete in der Sandbox

SSE-Streaminginfrastruktur

stream=True gibt einen iterierbaren Wert für Ereignisse zurück

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

Klicken Sie auf den Button unten, um dieses Codelab in Google Cloud Shell zu öffnen. Alle Abhängigkeiten sind vorinstalliert.

In Cloud Shell öffnen

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 mit agent_config konfigurieren (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_search und url_context (Webseiten abrufen und lesen). Dateisystemtools (read_file, write_file, list_files) werden automatisch aktiviert, wenn Sie den Parameter environment ü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 als interaction.output_text zurü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():

step.type

Beschreibung

Was run_stream() druckt

"url_context_call"

Agent ruft eine URL ab

[tool] url_context (https://...)

"code_execution_call"

KI-Agent, der Code in der Sandbox ausführt

[tool] run_code

"google_search_call"

Agent sucht im Web

[tool] google_search

"function_call"

Dateitools und andere

[tool] read_file (/workspace/...)

step.delta, wobei delta.type == "text"

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 target

Variable

Was das Geschirr damit macht

.agents/AGENTS.md

AGENTS_MD

Automatisch geladen als dauerhafte Anweisungen: redaktioneller Stil, Workflow, Ausführungsregeln

.agents/skills/digest-pdf/SKILL.md

SKILL_MD

Automatisch erkannt und als benannter Skill registriert; der Agent ruft ihn über den Namen auf

.agents/skills/digest-pdf/scripts/generate_pdf.py

GENERATE_PDF_PY

Vordefinierter PDF-Renderer: Der Agent schreibt summaries.json und führt dann dieses Skript aus.

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

environment=environment_id

Dateien, installierte Pakete, Systemstatus: alles im Linux-Dateisystem

Zwischen Besprechungen am selben Schreibtisch bleiben

previous_interaction_id=interaction_id

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:

  1. Rufen Sie console.cloud.google.com/apis/library/gmail.googleapis.com auf und aktivieren Sie die Gmail API.
  2. Rufen Sie APIs & Dienste > Anmeldedaten > Anmeldedaten erstellen > OAuth 2.0-Client-ID auf.
  3. Anwendungstyp: Desktop-App. Laden Sie die JSON-Datei herunter und speichern Sie sie als credentials.json im 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

interactions.create(agent, input, environment, stream=True), event.event_type

Agent anpassen

Konfigurationsdateien bereitstellen; IDs im selben Lauf in .env beibehalten

environment.sources, save_env

Sicherheitshaken hinzufügen

Tool-Aufrufe abfangen, bevor sie ausgeführt werden; schädliche Befehle ablehnen

hooks.json, pre_tool_execution, gate.py

PDF herunterladen

PDF herunterladen, ohne den Agenten noch einmal auszuführen

Gemini Files API :download in download_pdf.py

Unterhaltung fortsetzen

Unterhaltung fortsetzen, ohne das Web neu abzurufen

environment=environment_id, previous_interaction_id=interaction_id

KI-Agentenkonfiguration beibehalten

Agent-Konfiguration beibehalten; Aufruf über ID, keine Quellen erforderlich

agents.create(), agents.list()

Über Gmail senden

Ein OAuth-Token lokal abrufen und an den Agent übergeben, der die Gmail REST API über code_execution aufruft

OAuth 2.0, client.interactions.create(input=...)

Tägliche Ausführungen planen

KI‑Agenten automatisch nach einem Cron-Zeitplan ausführen

client.triggers.create(schedule, time_zone, interaction)

Schlüsselmuster

  1. Ein Aufruf, eine Sandbox: interactions.create() übernimmt die gesamte Infrastruktur (keine Container zum Bereitstellen, keine Pakete zur lokalen Installation)
  2. Progressives Streaming: stream=True verwandelt eine 90-sekündige Blackbox in einen Live-Feed von Tool-Aufrufen und Textblöcken.
  3. Inline-Quellen: AGENTS.md, SKILL.md und vorgefertigte Skripts werden ohne Upload- oder Bereitstellungsschritt in die Sandbox eingebunden.
  4. Automatische Erkennung von Harness: Dateien, die in .agents/ platziert werden, werden automatisch erkannt (keine SDK-Konfiguration erforderlich).
  5. Zweidimensionaler Status: environment_id verfolgt Dateien und Pakete, previous_interaction_id verfolgt den Unterhaltungskontext. Beide können unabhängig voneinander übergeben werden.
  6. Snapshot-Download: Die Umgebung ist ein vollständiges Dateisystem-Tar, auf das über die Gemini Files API zugegriffen werden kann.
  7. Benannte Agents: agents.create() wird dauerhaft eingebunden. Bei zukünftigen Aufrufen werden nur die Agent-ID und environment="remote" ohne Quellen übergeben.
  8. Hooks: hooks.json + ein Gate-Skript fängt Toolaufrufe vor der Ausführung ab. Eine deny-Antwort bricht den Aufruf ab und der Agent korrigiert sich selbst.
  9. Externe API-Aufrufe: Übergeben Sie Anmeldedaten im input-Prompt. Der KI-Agent schreibt und führt den Integrationscode in der Sandbox über code_execution aus.
  10. 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

docker build + gcloud run deploy

interactions.create()

Tools definieren

Beim Agenten registrierte Python-Funktionen

Integrierte Tools: Websuche, Codeausführung, Dateisystem

Pakete installieren

pip install im Dockerfile

KI-Agent wird in der Sandbox pip install ausgeführt

Stream-Ereignisse

Benutzerdefinierte SSE-Infrastruktur

stream=True

Sitzung fortsetzen

Sitzungsdatenbank + Kontextinjektion

environment_id + previous_interaction_id

Konfigurationsdateien

Im Agent hartcodiert oder beim Start eingefügt

Eingebunden über environment.sources

Zu verwaltende Infrastruktur

Container, Cloud Run, IAM, Secrets

–

Nächste Schritte