1. Ringkasan
Lanskap AI dan teknologi berkembang lebih cepat daripada yang dapat dilacak oleh siapa pun. Model, makalah, dan produk baru dirilis setiap hari. Agen ringkasan yang mengambil berita utama hari ini, menulis ringkasan yang tajam, dan membuat PDF setiap pagi akan menyelesaikan masalah tersebut, tetapi untuk membuatnya, Anda harus memilih framework, menentukan alat di Python, menulis loop orkestrasi, mengemas container, dan men-deploy ke Cloud Run. Semua itu terjadi sebelum agen membuat satu permintaan web.
Agen terkelola di Gemini API mengubah persamaannya. Anda menulis dua file konfigurasi markdown dan skrip perender bawaan, melakukan satu panggilan API, dan sandbox Ubuntu yang sebenarnya akan melakukan booting, menjelajahi web, menulis ringkasan, dan membuat PDF. Tidak ada penampung. Tidak ada deployment. Tidak ada kode orkestrasi.
Dalam codelab ini, Anda akan membuat agen tersebut: dari fungsi kosong hingga ringkasan harian yang berfungsi, satu konsep dalam satu waktu.
Yang akan Anda buat
- Membuat dan menjalankan agen terkelola pertama Anda di sandbox Linux sungguhan
- Menyesuaikan agen dengan gaya bahasa editorial, sumber web, dan keterampilan PDF
- Menambahkan hook keamanan untuk memblokir perintah merusak sebelum dijalankan
- Mendownload PDF yang dibuat agen
- Memperbaiki ringkasan dalam percakapan multi-turn tanpa mengambil ulang web
- Simpan konfigurasi agen dan panggil berdasarkan ID pada proses mendatang
- Mengirim ringkasan ke kotak masuk Anda melalui Gmail API
- Jadwalkan agen untuk berjalan dan mengirim secara otomatis setiap hari
Yang Anda butuhkan
- Python 3.10+
- Kunci Gemini API: aistudio.google.com/api-keys (paket gratis disertakan; penagihan direkomendasikan untuk menjalankan tanpa gangguan)
2. Apa yang dimaksud dengan Agen terkelola di Gemini API?
Tiga tingkat sistem AI
Sebelum mempelajari kode, berikut adalah posisi Agen Terkelola dibandingkan dengan dua alternatif lainnya:
Level | Apa Ini | Siapa yang Mengelola Infrastruktur? |
LLM Standar | Anda memberikan perintah, AI akan membalas dengan teks. Tidak ada tangan, tidak ada memori, tidak ada internet. | T/A: tidak dapat melakukan apa pun sendiri |
Agen yang Dihosting Sendiri | Anda menghubungkan ADK/LangChain/AutoGen + Docker + alat + memori. | Anda: semuanya (atau platform terkelola seperti Agent Engine) |
Managed Agent | Anda menetapkan sasaran. Google menyediakan sandbox yang aman. Agen menulis kode, menjalankannya, membaca error, menelusuri web, dan memperbaiki bug secara mandiri. | Google: semuanya |
Codelab ini membahas baris ketiga. Anda menyediakan file tugas dan konfigurasi. Google akan menangani sisanya.
Yang akan Anda bangun dengan ADK + Cloud Run
Untuk membuat agen ringkasan berita yang menjelajahi web, menjalankan Python, dan membuat PDF, Anda memerlukan semua hal berikut dengan ADK + Cloud Run:
# 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
Hal ini terjadi sebelum agen dijalankan satu kali. Anda tetap memiliki isolasi sandbox (sehingga agen tidak dapat merusak server Anda), penginstalan paket, pengelolaan status di antara panggilan alat, dan infrastruktur streaming untuk mendapatkan peristiwa ke klien.
Penggantiannya dengan Agen Terkelola
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,
},
],
},
)
Yang diperlukan ADK + Cloud Run | Yang ditangani Agen Terkelola untuk Anda |
Image container + Dockerfile + CI/CD | Sandbox Ubuntu yang dikelola sepenuhnya (Python 3.12, Node 22, 4 CPU / 16 GB RAM) |
Deployment + penskalaan Cloud Run | Disediakan per interaksi, otomatis berakhir setelah 7 hari tidak aktif |
Isolasi sandbox | Terisolasi per interaksi |
Alat PDF kustom + | Agen menginstal paket di dalam sandbox |
Infrastruktur streaming SSE |
|
Definisi alat di Python | Alat bawaan: penjelajahan web, eksekusi kode, sistem file |
Pengelolaan status di antara panggilan alat | Terintegrasi ke dalam reasoning loop agen |
Anda menulis file konfigurasi (AGENTS.md, SKILL.md, skrip bawaan) dan melakukan satu panggilan API. Google akan menangani sisanya.
Cara kerja 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
Sandbox akan tetap ada selama 7 hari tidak aktif. Anda dapat melanjutkannya dengan environment_id untuk menyempurnakan output, menjalankan tugas lanjutan, atau membuat fork ke agen bernama yang disimpan.
3. Siapkan
Opsi A: Cloud Shell (direkomendasikan)
Klik tombol di bawah untuk membuka codelab ini di Google Cloud Shell. Semua dependensi telah diinstal sebelumnya.
Opsi B: Penyiapan lokal
git clone https://github.com/Saoussen-CH/tech-digest-managed-agent.git
cd tech-digest-managed-agent
Instal uv jika diperlukan:
curl -LsSf https://astral.sh/uv/install.sh | sh
Konfigurasi kunci API Anda
cp .env.example .env
cloudshell edit .env
Tetapkan kunci Anda:
GEMINI_API_KEY=your-key-here
Menginstal dependensi
uv sync
4. Melakukan Panggilan Agen Pertama Anda
Membuka file awal
cloudshell edit run_digest.py
run_digest() memiliki satu TODO yang harus diisi sekarang dan tiga lagi untuk langkah berikutnya. Dua pembantu sudah diisi otomatis di atasnya:
load_source(path): membaca file dari.agents/relatif terhadap skrip. Anda akan menggunakannya dalam latihan berikutnya untuk memasang gaya editorial, buku pedoman PDF, dan perender ke dalam sandbox.run_stream(stream): memproses aliran peristiwa dan menampilkan(environment_id, interaction_id). Anda tidak perlu menulis sendiri loop peristiwa.
Yang harus ditambahkan
TODO 1: ganti pass dengan (abaikan TODO 3, 4 untuk saat ini; TODO tersebut untuk langkah berikutnya):
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}")
Fungsi setiap bagian
genai.Client() membaca GEMINI_API_KEY dari lingkungan. Semua hal lainnya akan melalui klien ini.
interactions.create() adalah panggilan inti. Empat parameter yang membuatnya berfungsi:
agent=BASE_AGENT: memilih agen Antigravity (antigravity-preview-05-2026), agen terkelola serbaguna yang didukung oleh Gemini 3.7 Flash secara default. Anda dapat mengonfigurasi model pokok menggunakanagent_config(opsi:gemini-3.7-flash,gemini-3.6-flash,gemini-3.5-flash,gemini-3.5-flash-lite). Model ini dilengkapi dengan tiga alat bawaan yang diaktifkan secara default:code_execution(jalankan Bash, Python, Node.js),google_search, danurl_context(ambil dan baca halaman web). Alat sistem file (read_file,write_file,list_files) diaktifkan secara otomatis saat Anda meneruskan parameterenvironment. Satu panggilan menyediakan lingkungan Ubuntu yang terkelola sepenuhnya dengan Python 3.12, Node.js 22, git, pip, dan curl yang telah diinstal sebelumnya. Tidak ada container yang perlu dibangun, tidak ada deployment yang perlu dijalankan.input: tugas untuk proses ini. Agen menjelajahi Hacker News dan menalar hasilnya.environment="remote": menyediakan sandbox cloud baru untuk interaksi ini.stream=True: menampilkan iterable peristiwa, bukan memblokir. Tanpa itu, panggilan akan menunggu 30-90 detik dan menampilkan semua output sekaligus sebagaiinteraction.output_text. Dengan streaming, Anda dapat melihat alasan dan tindakan agen secara real time. Streaming bukanlah fitur lanjutan di sini: ini adalah default yang tepat, karena kotak hitam 90 detik tidak memberi Anda sinyal tentang apakah agen berfungsi atau macet.
environment_id adalah tuas ke sandbox yang baru saja berjalan. Setelah interaction.completed, sandbox tidak akan ditutup: sandbox akan tetap aktif hingga 7 hari. environment_id adalah cara Anda kembali ke halaman tersebut. Teruskan ke panggilan interactions.create() kedua dan agen akan dilanjutkan di sistem file yang sama, dengan file dan paket yang diinstal yang sama, seolah-olah tidak pernah keluar. Langkah berikutnya menggunakannya untuk mendownload PDF tanpa menjalankan ulang agen, dan langkah setelahnya menggunakannya untuk melanjutkan percakapan.
interaction_id adalah handle untuk giliran percakapan yang baru saja selesai. Teruskan sebagai previous_interaction_id dalam panggilan berikutnya dan agen memiliki memori penuh tentang apa yang dikatakan dan dilakukannya pada giliran ini.
Verifikasi
uv run python run_digest.py
Anda akan melihat output langsung saat agen bekerja:
[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
API menampilkan environment_id yang sebenarnya meskipun dengan environment="remote". Sandbox berjalan. Yang tidak ada adalah konfigurasi: tidak ada suara, tidak ada skill, tidak ada generator PDF. Agen baru saja mencetak cerita sebagai teks dan berhenti. Langkah berikutnya akan menambahkan batasan tersebut.
Setiap baris output memetakan ke peristiwa dari run_stream():
| Penjelasan | Yang dicetak |
| agen mengambil URL |
|
| agen yang menjalankan kode di sandbox |
|
| agen menelusuri web |
|
| alat file dan lainnya |
|
| agen sedang menulis teks | di-streaming langsung ke stdout |
5. Menyesuaikan Agen
Agen tidak memiliki petunjuk: tidak ada suara, tidak ada keterampilan, tidak ada generator PDF. Pada langkah ini, Anda akan memuat file konfigurasi dari .agents/ dan memasangnya ke sandbox.
Yang akan diubah
Buat empat perubahan pada run_digest.py:
TODO 2: Di bawah load_source(), tambahkan tiga konstanta tingkat modul (ini berada di luar run_digest(), di bagian atas file):
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")
Buka setiap file untuk melihat apa yang Anda muat: AGENTS.md menetapkan aturan alur kerja dan gaya bahasa editorial; SKILL.md adalah panduan PDF langkah demi langkah; generate_pdf.py adalah perender bawaan yang akan dijalankan agen.
Sekarang, buat dua perubahan lagi di dalam run_digest():
TODO 3: ubah environment dari "remote" ke dict sumber, dan tetapkan input ke "Generate the digest.":
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: tambahkan baris ini tepat setelah print(f"\nDone. environment_id={environment_id}"):
save_env(ENVIRONMENT_ID=environment_id, INTERACTION_ID=interaction_id)
save_env sudah ditentukan dalam run_digest.py. Langkah ini menulis kedua ID ke .env sehingga langkah berikutnya dapat mendownload PDF tanpa menjalankan ulang agen.
Fungsi setiap sumber
Setiap sumber adalah file yang dipasang ke sistem file sandbox saat startup sebelum agen berjalan. Jalur target cocok dengan tempat harness Antigravity diharapkan menemukannya:
.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
Jalur | Variabel | Fungsi tali pengikat |
|
| Dimuat otomatis sebagai petunjuk persisten: gaya editorial, alur kerja, aturan eksekusi |
|
| Ditemukan dan didaftarkan secara otomatis sebagai skill bernama; agen memanggilnya berdasarkan nama |
|
| Perender PDF bawaan; agen menulis |
Verifikasi
uv run python run_digest.py
Sekarang, prosesnya memerlukan waktu 1-3 menit. Anda akan melihat agen membaca file konfigurasi, menulis ringkasan, dan menyimpan PDF:
[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 kini menjadi nilai sebenarnya: sandbox berjalan dengan file konfigurasi Anda dan agen membuat digest.pdf. Langkah berikutnya menambahkan hook keamanan sebelum mendownload.
6. Menambahkan Hook Keamanan
Hook memungkinkan Anda menjalankan skrip di dalam sandbox sebelum atau setelah setiap panggilan alat. Agen ringkasan menggunakan code_execution untuk menjalankan skrip Python, sehingga hook pre_tool_execution dapat mencegat panggilan tersebut dan memblokir perintah shell yang merusak sebelum dijalankan.
Runtime membaca .agents/hooks.json dari sandbox. Sebelum setiap panggilan alat pencocokan, alat ini akan menyalurkan detail panggilan ke skrip gerbang Anda di stdin. Skrip mencetak {"decision": "allow"} atau {"decision": "deny", "reason": "..."} ke stdout. Penolakan akan membatalkan panggilan alat dan agen akan melihat alasan Anda serta mengoreksi diri sendiri.
Yang harus ditambahkan
TODO 5: di run_digest.py, tambahkan dua konstanta ini di dekat bagian atas, setelah panggilan load_source yang ada:
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: tambahkan dua entri lagi ke daftar sources di dalam interactions.create():
{"type": "inline", "target": ".agents/hooks.json", "content": HOOKS_JSON},
{"type": "inline", "target": ".agents/hooks-scripts/gate.py", "content": GATE_PY},
Cara hook dipicu dalam proses ringkasan
Setiap kali agen memanggil code_execution untuk menjalankan skrip Python atau perintah shell, runtime akan menyalurkan detail panggilan ke gate.py terlebih dahulu. Jika perintah berisi rm -rf, hook akan menampilkan deny dan agen akan menerima alasan penolakan serta mencoba lagi dengan alternatif yang aman. Semua panggilan eksekusi kode lainnya diteruskan tanpa perubahan.
Verifikasi
uv run python run_digest.py
Outputnya sama seperti sebelumnya: gerbang keamanan mengizinkan semua perintah pembuatan PDF normal. Untuk mengonfirmasi bahwa hook diaktifkan, ubah sementara input agen untuk memintanya menjalankan rm -rf /tmp/test — Anda akan melihat agen melaporkan bahwa perintah diblokir dan memilih alternatif.
7. Unduh PDF
Agen menulis digest.pdf ke /workspace/digest.pdf di dalam sandbox. Snapshot lingkungan tersedia sebagai arsip tar melalui Gemini Files API.
Instal requests jika diperlukan:
uv pip install requests
Yang harus diisi
Buka download_pdf.py. Ada dua TODO.
TODO 1: isi panggilan requests.get():
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()
URL ini mengarah ke snapshot sandbox. params={"alt": "media"} menampilkan byte mentah, bukan metadata. GEMINI_API_KEY yang ada juga mengautentikasi Files API.
TODO 2: temukan dan ekstrak PDF dari arsip tar:
member = next(m for m in tar.getmembers() if m.name.endswith("workspace/digest.pdf"))
tar.extract(member, path=tmp, filter="data")
Awalan jalur tar bervariasi di seluruh proses, jadi telusuri berdasarkan akhiran, bukan dengan meng-hardcode jalur persisnya. filter="data" menekan peringatan penghentian penggunaan Python 3.13 tentang ekstraksi tar yang tidak aman.
Verifikasi
uv run python download_pdf.py
Saved digest.pdf (48,231 bytes)
Buka digest.pdf di direktori yang sama. Objek ini berisi ringkasan berformat yang dihasilkan agen dari halaman web aktif.
8. Melanjutkan Percakapan
Anda sudah memiliki digest.pdf. Jika hanya menginginkan file, Anda sudah selesai. Langkah ini membahas hal yang berbeda: meminta agen untuk mengubah ringkasan tanpa mengambil ulang web.
Sandbox masih aktif. Agen masih memiliki /workspace/digest.pdf dan mengingat setiap cerita yang diringkasnya. Panggilan interactions.create() kedua mengirimkan pesan lanjutan ke sandbox yang sama. Di sini, Anda memintanya untuk menambahkan catatan "Mengapa ini penting" di bawah setiap cerita, dan PDF akan diperbarui di tempat, tanpa pengambilan ulang dan tanpa meringkas ulang.
Yang harus diisi
Buka refine_digest.py. Ada tiga TODO.
TODO 1 dan 2: isi dua parameter multi-turn di dalam interactions.create():
environment=environment_id,
previous_interaction_id=interaction_id,
environment=environment_id melanjutkan sandbox yang sama dengan file dan paketnya. previous_interaction_id=interaction_id memberikan histori percakapan agen. Tidak ada perubahan lain dari panggilan pertama.
TODO 3: mempertahankan interaction_id baru kembali ke .env setelah loop peristiwa:
save_env(INTERACTION_ID=interaction_id)
Setiap panggilan interactions.create() menghasilkan interaction_id baru. Menulisnya kembali berarti proses berikutnya akan meneruskan penyempurnaan ini sebagai previous_interaction_id, sehingga rantai belokan dengan benar. ID sandbox tidak pernah berubah sehingga ENVIRONMENT_ID tidak perlu diperbarui.
Dua parameter yang membuat percakapan multi-giliran berfungsi
ID | Yang dipertahankan | Analogi |
| File, paket yang diinstal, status sistem: semuanya ada di sistem file Linux | Tetap menggunakan meja kantor yang sama di antara rapat |
| Histori percakapan: apa yang dikatakan dan dilakukan agen pada giliran sebelumnya | Mengingat apa yang dibahas dalam rapat terakhir |
Anda dapat meneruskan salah satu ID secara terpisah:
environment_idsaja: menggunakan kembali file dan paket, tetapi memulai percakapan baru. Berguna untuk tugas baru di ruang kerja yang sama.previous_interaction_idsaja: melanjutkan konteks percakapan, tetapi di sandbox baru (file hilang).- Keduanya: kontinuitas penuh, yang digunakan oleh langkah ini.
Tanpa environment_id: sandbox kosong, tidak ada PDF. Tanpa previous_interaction_id: tidak ada konteks, agen tidak dapat menyempurnakan bagian tertentu.
Verifikasi
uv run python refine_digest.py
Streaming harus cepat; agen tidak mengambil ulang apa pun. Setelah selesai:
Refinement done.
Saved digest_v2.pdf (52,418 bytes)
Buka digest_v2.pdf dan bandingkan dengan digest.pdf. Setiap cerita kini harus memiliki baris "Mengapa ini penting".
9. Mempertahankan Konfigurasi Agen Terkelola
Setiap panggilan sejauh ini telah meneruskan AGENTS.md, SKILL.md, dan generate_pdf.py secara inline. Cara tersebut berhasil, tetapi kode panggilan Anda membawa seluruh konten file pada setiap eksekusi. agents.create() membuat konfigurasi ke dalam agen bernama yang disimpan di sisi Google. Pemanggilan berikutnya hanya meneruskan ID agen:
Inline calls: send sources on every call
Named agent: bake once → invoke by ID, no sources
Yang harus diisi
Buka save_agent.py. Ada satu TODO (TODO 1).
Perhatikan bahwa konstanta diimpor langsung dari run_digest.py (tidak ada duplikasi):
from run_digest import BASE_AGENT, AGENTS_MD, SKILL_MD, GENERATE_PDF_PY
TODO 1: isi panggilan agents.create():
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,
},
],
},
)
agent_config menetapkan model pokok. gemini-3.7-flash adalah pilihan default dan terbaik untuk alur kerja ini; gemini-3.6-flash, gemini-3.5-flash, dan gemini-3.5-flash-lite tersedia jika Anda menginginkan proses yang lebih ringan atau berbiaya lebih rendah.
base_environment (bukan environment) adalah perbedaan utama dari panggilan inline pada langkah sebelumnya: sumber disimpan di sisi Google dan dipasang secara otomatis pada setiap pemanggilan berikutnya. Eksekusi sekali, bukan pada setiap proses ringkasan.
Verifikasi: simpan agen
uv run python save_agent.py
Saved: my-digest
my-digest: Daily tech digest with editorial voice and PDF generation.
Memanggil agen yang disimpan
Buka invoke_agent.py. Agen yang disimpan dipanggil berdasarkan ID tanpa sumber:
stream = client.interactions.create(
agent="my-digest",
input="Generate the digest.",
stream=True,
environment="remote",
)
Bandingkan dengan panggilan inline: agent=BASE_AGENT digantikan oleh "my-digest", dan blok environment lengkap dengan tiga sumber inline digantikan oleh environment="remote". Konfigurasi sudah diterapkan di sisi Google.
Verifikasi: panggil agen yang disimpan
uv run python invoke_agent.py
Anda akan melihat live stream yang sama seperti saat dijalankan inline, tetapi panggilan tidak membawa file sumber. Setelah dijalankan, ENVIRONMENT_ID dan INTERACTION_ID di .env akan diperbarui sehingga Anda dapat melanjutkan refine_digest.py seperti sebelumnya.
[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. Mengirim melalui Gmail
Agen telah membuat ringkasan dan menyimpannya ke /workspace/digest.pdf. Sejauh ini Anda telah mendownloadnya secara lokal. Langkah ini mengirimkannya langsung ke kotak masuk Anda dengan membuat agen memanggil Gmail REST API dari dalam sandbox.
Pendekatan: Anda mendapatkan token akses OAuth 2.0 secara lokal dan meneruskannya ke agen di perintah input. Agen menggunakan code_execution untuk membuat email MIME dengan PDF terlampir dan mempostingnya ke Gmail API. Tidak ada alat kustom, tidak ada pendaftaran server MCP.
Prasyarat
Aktifkan Gmail API di project GCP Anda dan buat client ID OAuth 2.0:
- Buka console.cloud.google.com/apis/library/gmail.googleapis.com dan aktifkan Gmail API.
- Buka API & Layanan > Kredensial > Buat Kredensial > Client ID OAuth 2.0.
- Jenis aplikasi: Aplikasi desktop. Download JSON dan simpan sebagai
credentials.jsondi root project.
Tambahkan email penerima Anda ke .env:
RECIPIENT_EMAIL=you@gmail.com
Instal library auth jika diperlukan:
uv sync
Yang harus diisi
Buka send_digest.py. Ada dua TODO.
TODO 1: memuat atau memperbarui token akses OAuth 2.0:
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())
Hapus baris raise NotImplementedError setelah menambahkannya. Saat dijalankan pertama kali, browser akan terbuka untuk menampilkan layar izin OAuth. Token di-cache di .gmail_token.json untuk proses mendatang.
TODO 2: ganti input="" dengan petunjuk email. Token sudah dalam cakupan sebagai 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}"
),
Fungsi setiap bagian
Interaksi dilanjutkan di sandbox yang sama tempat agen telah membuat digest.pdf dan summaries.json. previous_interaction_id memberikan histori percakapan agen.
Token akses diteruskan dalam string input. Agen membacanya dari perintah dan menggunakannya di header Authorization: Bearer saat memanggil Gmail API. File tersebut tidak pernah menyentuh komputer atau sistem file lokal Anda.
Agen menggunakan code_execution untuk menulis dan menjalankan skrip Python di dalam sandbox: agen membaca summaries.json, memformatnya sebagai newsletter, membaca digest.pdf, membuat pesan multipart MIME, mengenkodenya dengan base64url, dan melakukan POST ke https://gmail.googleapis.com/gmail/v1/users/me/messages/send.
Verifikasi
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.
Periksa kotak masuk Anda. Email akan masuk dengan isi berformat newsletter dan digest.pdf terlampir.
11. Menjadwalkan Operasi Harian
Setiap langkah sejauh ini telah dipicu secara manual. Pemicu memungkinkan Anda menjadwalkan agen bernama untuk berjalan secara otomatis pada ekspresi cron. Agen akan berjalan pada waktu yang dijadwalkan, menjalankan alur kerja ringkasan lengkap, dan lingkungan akan tetap ada di antara eksekusi sehingga paket yang diinstal pada proses pertama akan tersedia pada setiap proses berikutnya.
Manual: python run_digest.py → runs once, now
Trigger: client.triggers.create() → runs every morning, automatically
Yang harus diisi
Buka create_trigger.py. Ada satu TODO.
TODO 1: isi panggilan triggers.create(). Pemicu menjalankan alur kerja lengkap setiap hari: membuat ringkasan DAN mengirimkannya ke kotak masuk Anda. Karena token akses akan berakhir dalam satu jam, token akses akan menyisipkan token refresh dari .gmail_token.json sebagai sumber inline sehingga agen dapat menukarkannya dengan token baru pada setiap proses.
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 adalah waktu tunggu default. max_consecutive_failures=3 akan otomatis menjeda pemicu setelah 3 kali gagal berturut-turut (default API adalah 5; 3 lebih konservatif untuk workshop).
Daftar sources menyuntikkan .gmail_creds.json ke sandbox di /workspace/.gmail_creds.json. Agen membacanya, menukar token refresh dengan token akses baru, dan memanggil Gmail API. Token refresh tidak akan habis masa berlakunya, sehingga berfungsi pada setiap jadwal yang dijalankan tanpa perlu refresh token secara manual.
Hapus baris raise NotImplementedError setelah menambahkan panggilan.
Verifikasi
uv run python create_trigger.py
Trigger created: trig_abc123
Next run: 2026-07-23T09:00:00Z
create_trigger.py menyimpan ID pemicu ke .env secara otomatis.
Untuk memeriksa histori eksekusi setelah menjalankan:
uv run python check_trigger.py
Untuk mengaktifkan pemicu segera tanpa menunggu waktu terjadwal berikutnya:
uv run python fire_trigger.py
Untuk menjeda atau menghapus pemicu:
uv run python pause_trigger.py
12. Pembersihan
Sandbox akan otomatis berakhir masa berlakunya setelah 7 hari tidak aktif. Tidak ada server yang perlu dihentikan. Tidak ada penampung untuk dihapus.
Jika Anda menyimpan konfigurasi agen, hapus konfigurasi tersebut:
uv run python delete_agent.py
13. Ringkasan
Anda membuat agen terkelola dari awal, satu konsep dalam satu waktu. Berikut adalah materi yang diajarkan dalam setiap latihan:
Latihan | Konsep | Key API |
Melakukan panggilan pertama Anda | Menyediakan sandbox Linux asli dan melakukan streaming langsung peristiwanya |
|
Menyesuaikan agen | Memasang file konfigurasi; mempertahankan ID ke |
|
Tambahkan kait pengaman | Menyadap panggilan alat sebelum dieksekusi; menolak perintah yang merusak |
|
Unduh PDF | Mendownload PDF tanpa menjalankan ulang agen | Gemini Files API |
Lanjutkan percakapan | Melanjutkan percakapan tanpa mengambil ulang web |
|
Mempertahankan konfigurasi agen | Mempertahankan konfigurasi agen; memanggil berdasarkan ID, tidak memerlukan sumber |
|
Mengirim melalui Gmail | Mendapatkan token OAuth secara lokal; meneruskannya ke agen, yang memanggil Gmail REST API melalui | OAuth 2.0, |
Menjadwalkan lari harian | Menjalankan agen secara otomatis sesuai jadwal cron |
|
Pola utama
- Satu panggilan, satu sandbox:
interactions.create()menangani semua infrastruktur (tidak ada container untuk di-deploy, tidak ada paket untuk diinstal secara lokal) - Streaming progresif:
stream=Truemengubah kotak hitam berdurasi 90 detik menjadi feed live dari panggilan alat dan potongan teks - Sumber inline: memasang skrip
AGENTS.md,SKILL.md, dan bawaan ke sandbox tanpa langkah upload atau deployment - Penemuan otomatis harness: file yang ditempatkan di
.agents/akan diambil secara otomatis (tidak memerlukan konfigurasi SDK) - Status dua dimensi:
environment_idmelacak file dan paket;previous_interaction_idmelacak konteks percakapan; keduanya dapat diteruskan secara terpisah - Download snapshot: lingkungan adalah tar sistem file lengkap, yang dapat diakses melalui Gemini Files API
- Agen bernama:
agents.create()memanggang konfigurasi secara permanen; panggilan mendatang hanya meneruskan ID agen danenvironment="remote", tanpa sumber - Hook:
hooks.json+ skrip gerbang mencegat panggilan alat sebelum dieksekusi; responsdenymembatalkan panggilan dan agen mengoreksi diri sendiri - Panggilan API eksternal: teruskan kredensial dalam perintah
input; agen menulis dan menjalankan kode integrasi di dalam sandbox melaluicode_execution - Pemicu: menjadwalkan agen pada ekspresi cron dengan
client.triggers.create(); lingkungan tetap ada di seluruh eksekusi
ADK + Cloud Run vs. Agen Terkelola: sekilas tentang perbedaannya
Kemampuan | ADK + Cloud Run | Agen terkelola di Gemini API |
Menyediakan sandbox |
|
|
Menentukan alat | Fungsi Python yang terdaftar di agen | Bawaan: penjelajahan web, eksekusi kode, sistem file |
Menginstal paket |
| Agen berjalan |
Streaming peristiwa | Infrastruktur SSE kustom |
|
Melanjutkan sesi | Database sesi + injeksi konteks |
|
File konfigurasi | Dikodekan secara permanen di agen atau disuntikkan saat startup | Dipasang melalui |
Infrastruktur yang akan dikelola | Container, Cloud Run, IAM, secret | Tidak ada |