Gemini API'de Yönetilen Ajanlar ile Günlük Teknoloji Özeti Ajanı Oluşturma

1. Genel Bakış

Yapay zeka ve teknoloji dünyası, herkesin takip edebileceğinden daha hızlı gelişiyor. Her gün yeni modeller, makaleler ve ürünler yayınlanıyor. Bugünün başlıklarını getiren, net özetler yazan ve her sabah PDF oluşturan bir özet aracısı bu sorunu çözebilir. Ancak böyle bir aracı oluşturmak için bir çerçeve seçmek, Python'da araçlar tanımlamak, bir düzenleme döngüsü yazmak, bir kapsayıcı paketlemek ve Cloud Run'a dağıtmak gerekiyordu. Tüm bunlar, aracı tek bir web isteğinde bulunmadan önce gerçekleşir.

Gemini API'deki yönetilen ajanlar bu denklemi değiştiriyor. İki markdown yapılandırma dosyası ve önceden oluşturulmuş bir oluşturucu komut dosyası yazıp bir API çağrısı yaptığınızda gerçek bir Ubuntu sanal alanı başlatılır, web'de gezinir, özetlerinizi yazar ve PDF oluşturur. Kapsayıcı yok. Dağıtım yok. Orkestrasyon kodu yok.

Bu codelab'de, boş bir işlevden başlayıp günlük özet işlevi oluşturarak tam olarak bu tür bir ajan geliştireceksiniz.

Ne oluşturacaksınız?

  • Gerçek bir Linux sanal ortamında ilk yönetilen aracınızı oluşturma ve çalıştırma
  • Ajanı editoryal tarz, web kaynakları ve PDF becerisiyle özelleştirme
  • Yıkıcı komutları çalıştırılmadan önce engellemek için güvenlik kancası ekleyin
  • Aracının oluşturduğu PDF'yi indirme
  • Web'i yeniden getirmeden çok aşamalı etkileşimde özeti iyileştirme
  • Aracı yapılandırmasını kaydedin ve gelecekteki çalıştırmalarda kimliğe göre çağırın.
  • Özeti Gmail API aracılığıyla gelen kutunuza gönderme
  • Temsilciyi her gün otomatik olarak çalışacak ve gönderecek şekilde planlayın

İhtiyacınız olanlar

  • Python 3.10 veya sonraki sürümler
  • Gemini API anahtarı: aistudio.google.com/api-keys (ücretsiz katman dahildir; kesintisiz çalıştırma için faturalandırma önerilir)

2. Gemini API'de yönetilen ajanlar nedir?

Üç yapay zeka sistemi düzeyi

Koda geçmeden önce, yönetilen aracıların iki alternatife kıyasla nerede yer aldığını inceleyelim:

Seviye

Nedir?

Altyapıyı Kim Yönetir?

Standart LLM

Siz istem girersiniz, o da metinle yanıt verir. Eller yok, hafıza yok, internet yok.

Geçersiz: Kendi başına hiçbir şey yapamaz.

Self-Hosted Agent

ADK/LangChain/AutoGen + Docker + araçlar + bellek bağlantısını oluşturursunuz.

Her şey size aittir (veya Agent Engine gibi yönetilen bir platform)

Yönetilen Ajan

Hedef belirleyin. Google, güvenli bir korumalı alan sağlar. Ajan, kodu yazar, çalıştırır, hataları okur, web'de arama yapar ve hataları bağımsız olarak düzeltir.

Google: tümü

Bu codelab, üçüncü satırla ilgilidir. Bir görev ve yapılandırma dosyaları sağlarsınız. Diğer her şeyi Google halleder.

ADK + Cloud Run ile neler oluşturabilirsiniz?

Web'e göz atan, Python çalıştıran ve PDF oluşturan bir haber özeti aracısı oluşturmak için ADK + Cloud Run ile tüm bunlara ihtiyacınız olur:

# 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

Bu, aracı bir kez çalıştırmadan önce olur. Yine de sanal alan izolasyonu (böylece aracı sunucunuza zarar veremez), paket yükleme, araç çağrıları arasındaki durum yönetimi ve etkinlikleri bir istemciye getirmek için akış altyapısı size aittir.

Yönetilen aracılar, bu aracının yerine hangi aracı kullanır?

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,
            },
        ],
    },
)

ADK + Cloud Run için gerekenler

Yönetilen Ajanlar sizin için hangi işlemleri yapar?

Container görüntüsü + Dockerfile + CI/CD

Tamamen yönetilen Ubuntu sanal alanı (Python 3.12, Node 22, 4 CPU / 16 GB RAM)

Cloud Run dağıtımı ve ölçeklendirme

Her etkileşim için sağlanır, 7 gün boyunca işlem yapılmadığında otomatik olarak sona erer.

Korumalı alan izolasyonu

Etkileşim başına yalıtılmış

Özel PDF aracı + pip install

Aracı, paketleri korumalı alanın içine yükler.

SSE yayın altyapısı

stream=True, bir etkinlik yineleyicisi döndürür.

Python'da araç tanımları

Yerleşik araçlar: Web'de gezinme, kod yürütme, dosya sistemi

Araç çağrıları arasında durum yönetimi

Ajanın muhakeme döngüsüne yerleştirilmiştir.

Yapılandırma dosyaları (AGENTS.md, SKILL.md, önceden oluşturulmuş bir komut dosyası) yazıp bir API çağrısı yaparsınız. Diğer her şeyi Google halleder.

Sandbox'ın işleyiş şekli

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

Koruma alanı, 7 gün boyunca etkinlik olmaması durumunda kalıcı olur. Çıktıyı iyileştirmek, takip görevleri çalıştırmak veya kaydedilmiş adlandırılmış bir aracıya çatallamak için environment_id ile devam edebilirsiniz.

3. Kur

Bu codelab'i Google Cloud Shell'de açmak için aşağıdaki düğmeyi tıklayın. Tüm bağımlılıklar önceden yüklenir.

Cloud Shell'de Aç

B seçeneği: Yerel kurulum

git clone https://github.com/Saoussen-CH/tech-digest-managed-agent.git
cd tech-digest-managed-agent

Gerekirse uv uygulamasını yükleyin:

curl -LsSf https://astral.sh/uv/install.sh | sh

API anahtarınızı yapılandırma

cp .env.example .env
cloudshell edit .env

Anahtarınızı ayarlayın:

GEMINI_API_KEY=your-key-here

Bağımlıları yükleme

uv sync

4. İlk Temsilci Çağrınızı Yapma

Başlangıç dosyasını açın.

cloudshell edit run_digest.py

run_digest(), şu anda doldurulması gereken bir YAPILACAKLAR öğesine ve sonraki adım için üç YAPILACAKLAR öğesine sahip. Yukarıda iki yardımcı zaten önceden doldurulmuştur:

  • load_source(path): Dosyayı, komut dosyasına göre .agents/ konumundan okur. Bunu, sonraki alıştırmada editoryal tarz, PDF başucu kitabı ve oluşturucuyu korumalı alana eklemek için kullanacaksınız.
  • run_stream(stream): Etkinlik akışını işler ve (environment_id, interaction_id) değerini döndürür. Etkinlik döngüsünü kendiniz yazmanız gerekmez.

Eklenmesi gerekenler

YAPILACAKLAR 1: pass yerine şunu girin (Şimdilik 3. ve 4. YAPILACAKLAR'ı yok sayın. Bunlar sonraki adım içindir):

    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}")

Her bölümün işlevi

genai.Client(), ortamdan GEMINI_API_KEY okur. Diğer her şey bu istemciden geçer.

interactions.create(), ana görüşmedir. Bu işlevin çalışması için dört parametre gerekir:

  • agent=BASE_AGENT: Varsayılan olarak Gemini 3.7 Flash tarafından desteklenen, genel amaçlı bir yönetilen ajan olan Antigravity ajanını (antigravity-preview-05-2026) seçer. agent_config (seçenekler: gemini-3.7-flash, gemini-3.6-flash, gemini-3.5-flash, gemini-3.5-flash-lite) kullanarak temel modeli yapılandırabilirsiniz. Bu modelde varsayılan olarak etkinleştirilmiş üç yerleşik araç bulunur: code_execution (Bash, Python, Node.js'yi çalıştırma), google_search ve url_context (web sayfalarını getirme ve okuma). environment parametresini ilettiğinizde dosya sistemi araçları (read_file, write_file, list_files) otomatik olarak etkinleştirilir. Tek bir çağrı ile Python 3.12, Node.js 22, git, pip ve curl önceden yüklenmiş olarak tam olarak yönetilen bir Ubuntu ortamı sağlanır. Derlenecek kapsayıcı veya çalıştırılacak dağıtım yoktur.
  • input: Bu çalıştırma için görev. Temsilci, Hacker News'a göz atar ve sonuçlar hakkında akıl yürütür.
  • environment="remote": Bu etkileşim için yeni bir bulut korumalı alanı sağlar.
  • stream=True: Engellemek yerine etkinliklerin yinelenebilir bir öğesini döndürür. Bu parametre olmadan, arama 30-90 saniye bekler ve tüm çıkışı tek seferde interaction.output_text olarak döndürür. Akışla birlikte, temsilci nedenini görür ve anında harekete geçersiniz. Burada akış, gelişmiş bir özellik değildir. 90 saniyelik siyah bir kutu, temsilcinin çalışıp çalışmadığı veya takılıp kalmadığı hakkında size bilgi vermediği için doğru varsayılan seçenektir.

environment_id, az önce çalıştırılan korumalı alanın herkese açık kullanıcı adıdır. interaction.completed tarihinden sonra sanal alan kapatılmaz ve 7 güne kadar etkin kalır. environment_id simgesini kullanarak geri dönebilirsiniz. İkinci bir interactions.create() görüşmesine aktarıldığında aracı, hiç ayrılmamış gibi aynı dosya sistemi, aynı dosyalar ve yüklü paketlerle devam eder. Bir sonraki adım, PDF'yi aracıyı yeniden çalıştırmadan indirmek için bu bilgiyi kullanır. Ondan sonraki adım ise bu bilgiyi kullanarak sohbete devam eder.

interaction_id, yeni tamamlanan sohbet dönüşünün tanımlayıcısıdır. Sonraki görüşmede previous_interaction_id olarak iletin. Temsilci, bu turda söylediklerini ve yaptıklarını tamamen hatırlar.

Doğrula

uv run python run_digest.py

Ajan çalışırken canlı çıkış görürsünüz:

[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, environment="remote" ile bile gerçek bir environment_id döndürür. Korumalı alan çalıştırıldı. Eksik olan yapılandırma: ses yok, beceri yok, PDF oluşturucu yok. Ajan, hikayeleri yalnızca metin olarak yazdırıp durdu. Sonraki adımda bunları ekleyeceksiniz.

Çıkışın her satırı, run_stream()'daki bir etkinlikle eşlenir:

step.type

Nedir?

run_stream() adlı kullanıcının yazdırdığı öğeler

"url_context_call"

bir URL'yi getiren aracı

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

"code_execution_call"

Korumalı alanda kod çalıştıran ajan

[tool] run_code

"google_search_call"

Web'de arama yapan temsilci

[tool] google_search

"function_call"

dosya araçları ve diğerleri

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

step.delta / delta.type == "text"

agent writing text

doğrudan stdout'a aktarılır.

5. Ajanı özelleştirme

Temsilcinin talimatı yoktu: ses, beceri veya PDF oluşturucu yoktu. Bu adımda, .agents/ konumundaki yapılandırma dosyalarını yükleyip bunları korumalı alana monte edersiniz.

Neyi değiştirebilirsiniz?

run_digest.py ile ilgili dört değişiklik yapın:

YAPILACAKLAR 2: load_source() öğesinin altına üç modül düzeyinde sabit ekleyin (bunlar run_digest() dışında, dosyanın en üstünde yer alır):

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")

Yüklediğiniz öğeleri görmek için her dosyayı açın: AGENTS.md, editoryal tarz ve iş akışı kurallarını belirler; SKILL.md, adım adım PDF başucu kitabıdır; generate_pdf.py, aracının çalıştıracağı önceden oluşturulmuş oluşturucudur.

Şimdi run_digest() içinde iki değişiklik daha yapın:

YAPILACAKLAR 3: environment değerini "remote" yerine kaynaklar sözlüğü olarak değiştirin ve input değerini "Generate the digest." olarak ayarlayın:

        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,
                },
            ],
        },

YAPILACAKLAR 4: print(f"\nDone. environment_id={environment_id}") işaretinden hemen sonra şu satırı ekleyin:

    save_env(ENVIRONMENT_ID=environment_id, INTERACTION_ID=interaction_id)

save_env, run_digest.py içinde zaten tanımlanmış. Her iki kimliği de .env dosyasına yazar. Böylece sonraki adım, aracı yeniden çalıştırmadan PDF'yi indirebilir.

Her kaynağın işlevi

Her kaynak, aracı çalışmadan önce başlangıçta korumalı alan dosya sistemine yerleştirilmiş bir dosyadır. target yolları, Antigravity koşumunun bunları bulmayı beklediği yerlerle eşleşir:

.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

target yol

Değişken

Koşumun bu verilerle yaptığı işlemler

.agents/AGENTS.md

AGENTS_MD

Kalıcı talimatlar olarak otomatik yüklenir: editoryal tarz, iş akışı, uygulama kuralları

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

SKILL_MD

Otomatik olarak keşfedilir ve adlandırılmış beceri olarak kaydedilir. Aracı, beceriyi adıyla çağırır.

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

GENERATE_PDF_PY

Önceden oluşturulmuş PDF oluşturucu; ajan summaries.json yazıp bu komut dosyasını çalıştırır.

Doğrula

uv run python run_digest.py

Çalıştırma işlemi artık 1-3 dakika sürüyor. Aracının yapılandırma dosyalarını okuduğunu, özet yazdığını ve PDF'yi kaydettiğini görmelisiniz:

[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 artık gerçek bir değerdir: Sandbox, yapılandırma dosyalarınızla çalıştı ve aracı digest.pdf oluşturdu. Bir sonraki adımda, indirmeden önce bir güvenlik kancası eklenir.

6. Güvenlik kancası ekleme

Kancalar, her araç çağrısından önce veya sonra sanal alan içinde bir komut dosyası çalıştırmanıza olanak tanır. Özet aracısı, Python komut dosyalarını çalıştırmak için code_execution kullanır. Bu nedenle, pre_tool_execution kancası bu çağrıları engelleyebilir ve yıkıcı kabuk komutlarını yürütülmeden önce engelleyebilir.

Çalışma zamanı, .agents/hooks.json değerini korumalı alandan okur. Her eşleştirme aracı çağrısından önce, çağrı ayrıntılarını stdin üzerindeki geçit komut dosyanıza yönlendirir. Komut dosyası, {"decision": "allow"} veya {"decision": "deny", "reason": "..."} değerini stdout olarak yazdırır. Reddetme işlemi, araç çağrısını iptal eder ve temsilci, nedeninizi görerek kendini düzeltir.

Eklenmesi gerekenler

YAPILACAKLAR 5: run_digest.py içinde, mevcut load_source çağrılarından sonra üst kısma yakın bir yere şu iki sabiti ekleyin:

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"}))
"""

YAPILACAKLAR 6: interactions.create() içindeki sources listesine iki giriş daha ekleyin:

{"type": "inline", "target": ".agents/hooks.json",            "content": HOOKS_JSON},
{"type": "inline", "target": ".agents/hooks-scripts/gate.py", "content": GATE_PY},

Özet çalıştırmada kancalar nasıl tetiklenir?

Aracı, bir Python komut dosyası veya kabuk komutu çalıştırmak için code_execution işlevini her çağırdığında çalışma zamanı, arama ayrıntılarını önce gate.py işlevine yönlendirir. Komut rm -rf içeriyorsa kanca deny değerini döndürür ve temsilci, reddedilme nedenini alır ve güvenli bir alternatifle yeniden dener. Diğer tüm kod yürütme çağrıları değiştirilmeden iletilir.

Doğrula

uv run python run_digest.py

Çıkış, öncekiyle aynıdır: Güvenlik kapısı, tüm normal PDF oluşturma komutlarına izin verir. Kancanın tetiklendiğini onaylamak için aracı girişini geçici olarak değiştirerek rm -rf /tmp/test komutunu çalıştırmasını isteyin. Aracı, komutun engellendiğini bildirir ve alternatif bir komut seçer.

7. PDF'yi indirin

Temsilci, sanal ortamda digest.pdf ile /workspace/digest.pdf arasında yazdı. Ortam anlık görüntüsü, Gemini Files API aracılığıyla tar arşivi olarak kullanılabilir.

Gerekirse requests uygulamasını yükleyin:

uv pip install requests

Doldurulması gerekenler

download_pdf.py adlı kişiyi aç. İki yapılacak iş var.

YAPILACAKLAR 1: requests.get() görüşmesini doldurun:

    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, korumalı alan anlık görüntüsüne yönlendirir. params={"alt": "media"} meta veriler yerine ham baytlar döndürür. Mevcut GEMINI_API_KEY, Files API'nin de kimliğini doğrular.

YAPILACAKLAR 2: Tar arşivindeki PDF'yi bulup ayıklayın:

            member = next(m for m in tar.getmembers() if m.name.endswith("workspace/digest.pdf"))
            tar.extract(member, path=tmp, filter="data")

Tar yolu öneki, çalıştırmalar arasında değişiklik gösterir. Bu nedenle, tam yolu sabit kodlamak yerine soneke göre arama yapın. filter="data", güvenli olmayan tar çıkarma işlemiyle ilgili Python 3.13 desteğinin sonlandırılması uyarısını bastırır.

Doğrula

uv run python download_pdf.py
Saved digest.pdf (48,231 bytes)

digest.pdf dosyasını aynı dizinde açın. Müşteri temsilcisinin canlı web sayfalarından oluşturduğu biçimlendirilmiş özeti içerir.

8. Görüşmeye Devam Etme

digest.pdf adlı ürüne zaten sahipsiniz. Yalnızca dosyayı istiyorsanız işlem tamamlanmıştır. Bu adımda farklı bir konu ele alınır: Ajanın, web'i yeniden getirmeden özeti değiştirmesi istenir.

Korumalı alan hâlâ etkin. Ajan, /workspace/digest.pdf özelliğine sahip olmaya devam ediyor ve özetlediği her hikayeyi hatırlıyor. İkinci bir interactions.create() çağrısı, aynı test ortamına bir takip mesajı gönderir. Burada, her hikayenin altına "Önemi" notu eklemesini istiyorsunuz. PDF, yeniden getirme ve yeniden özetleme yapılmadan yerinde güncelleniyor.

Doldurulması gerekenler

refine_digest.py adlı kişiyi aç. Üç yapılacaklar listesi öğesi var.

Yapılacaklar 1 ve 2: interactions.create() içindeki iki çok aşamalı etkileşim parametresini doldurun:

    environment=environment_id,
    previous_interaction_id=interaction_id,

environment=environment_id dosyaları ve paketleriyle aynı sanal ortamı devam ettirir. previous_interaction_id=interaction_id, temsilciye sohbet geçmişini verir. İlk görüşmeye kıyasla başka bir değişiklik olmaz.

YAPILACAKLAR 3: Etkinlik döngüsünden sonra yeni interaction_id öğesini .env'ye geri kalıcı hale getirin:

save_env(INTERACTION_ID=interaction_id)

Her interactions.create() çağrısı yeni bir interaction_id oluşturur. Geri yazma işlemi, sonraki çalıştırmanın bu iyileştirmeyi previous_interaction_id olarak geçeceği ve zincirlemenin doğru şekilde yapılacağı anlamına gelir. Sandbox kimliği hiçbir zaman değişmediğinden ENVIRONMENT_ID değerinin güncellenmesi gerekmez.

Çok aşamalı etkileşimi mümkün kılan iki parametre

Kimlik

Koruduğu bilgiler

Analoji

environment=environment_id

Dosyalar, yüklenen paketler, sistem durumu: Linux dosya sistemindeki her şey

Toplantılar arasında aynı ofis masasını kullanma

previous_interaction_id=interaction_id

İleti dizisi geçmişi: Ajanın önceki dönüşlerde söyledikleri ve yaptıkları

Son toplantıda konuşulanları hatırlama

Aşağıdaki kimliklerden birini bağımsız olarak iletebilirsiniz:

  • environment_id yalnızca: Dosyaları ve paketleri yeniden kullanın ancak yeni bir sohbet başlatın. Aynı çalışma alanındaki yeni görevler için kullanışlıdır.
  • previous_interaction_id yalnızca: Sohbet bağlamına devam edin ancak yeni bir korumalı alanda (dosyalar silinir).
  • Her ikisi de: Bu adımda kullanılan tam süreklilik.

environment_id olmadan: boş korumalı alan, PDF yok. previous_interaction_id olmadan: Bağlam yok, temsilci belirli bir bölümü iyileştiremez.

Doğrula

uv run python refine_digest.py

Akış hızlı olmalıdır. Ajan hiçbir şeyi yeniden getirmez. İşlem tamamlandıktan sonra:

Refinement done.
Saved digest_v2.pdf (52,418 bytes)

digest_v2.pdf uygulamasını açın ve digest.pdf ile karşılaştırın. Artık her haberde "Öneminin nedeni" satırı eklenmiş olmalıdır.

9. Yönetilen aracı yapılandırmasını kalıcı hale getirme

Şimdiye kadar yapılan tüm görüşmelerde AGENTS.md, SKILL.md ve generate_pdf.py satır içi olarak kullanıldı. Bu yöntem işe yarar ancak çağırma kodunuz her çalıştırmada dosyanın tüm içeriğini taşır. agents.create(), yapılandırmayı Google tarafında kaydedilmiş adlı bir aracıya yerleştirir. Bir sonraki çağırma işleminde yalnızca aracı kimliği iletilir:

Inline calls:   send sources on every call
Named agent:    bake once → invoke by ID, no sources

Doldurulması gerekenler

save_agent.py adlı kişiyi aç. Bir yapılacak işi (TODO 1) var.

Sabitlerin doğrudan run_digest.py öğesinden içe aktarıldığını (kopyalama yok) unutmayın:

from run_digest import BASE_AGENT, AGENTS_MD, SKILL_MD, GENERATE_PDF_PY

YAPILACAKLAR 1: agents.create() görüşmesini doldurun:

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 temel modeli ayarlar. gemini-3.7-flash varsayılan seçenektir ve bu iş akışı için en iyi tercihtir. Daha hafif veya daha düşük maliyetli bir çalıştırma istiyorsanız gemini-3.6-flash, gemini-3.5-flash ve gemini-3.5-flash-lite seçeneklerini kullanabilirsiniz.

base_environment (environment değil), önceki adımdaki satır içi çağrıdan temel farktır: Kaynaklar Google'ın tarafında depolanır ve gelecekteki her çağrıda otomatik olarak monte edilir. Her özet çalıştırmasında değil, yalnızca bir kez çalıştırın.

Doğrulama: Aracıyı kaydetme

uv run python save_agent.py
Saved: my-digest
my-digest: Daily tech digest with editorial voice and PDF generation.

Kayıtlı temsilciyi çağırma

invoke_agent.py adlı kişiyi aç. Kaynak olmadan, kayıtlı aracıyı kimliğe göre çağırır:

stream = client.interactions.create(
    agent="my-digest",
    input="Generate the digest.",
    stream=True,
    environment="remote",
)

Bunu satır içi çağrıyla karşılaştırdığımızda agent=BASE_AGENT yerine "my-digest", üç satır içi kaynağın bulunduğu environment bloğunun tamamı yerine ise environment="remote" kullanıldığını görüyoruz. Yapılandırma, Google tarafında zaten yerleşiktir.

Doğrulama: Kayıtlı aracı çağırma

uv run python invoke_agent.py

Satır içi çalıştırmayla aynı canlı yayını görürsünüz ancak çağrı kaynak dosyası içermez. Çalıştırma işleminden sonra .env içindeki ENVIRONMENT_ID ve INTERACTION_ID güncellenir. Böylece refine_digest.py'ü eskisi gibi kullanmaya devam edebilirsiniz.

[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. Gmail üzerinden gönderme

Temsilci, özeti oluşturup /workspace/digest.pdf konumuna kaydetti. Şu ana kadar yerel olarak indirdiniz. Bu adımda, aracı, korumalı alanın içinden Gmail REST API'sini çağırarak e-postayı doğrudan gelen kutunuza teslim eder.

Yaklaşım: Yerel olarak bir OAuth 2.0 erişim jetonu alıp input isteminde aracıya iletirsiniz. Ajan, PDF'nin eklendiği bir MIME e-postası oluşturmak ve bunu Gmail API'ye POST etmek için code_execution kullanır. Özel araç yok, MCP sunucusu kaydı yok.

Ön koşullar

GCP projenizde Gmail API'yi etkinleştirin ve bir OAuth 2.0 istemci kimliği oluşturun:

  1. console.cloud.google.com/apis/library/gmail.googleapis.com adresine gidip Gmail API'yi etkinleştirin.
  2. API'ler ve Hizmetler > Kimlik Bilgileri > Kimlik Bilgileri Oluştur > OAuth 2.0 İstemci Kimliği'ne gidin.
  3. Uygulama türü: Masaüstü uygulaması. JSON'u indirip proje kökünde credentials.json olarak kaydedin.

Alıcınızın e-posta adresini .env bölümüne ekleyin:

RECIPIENT_EMAIL=you@gmail.com

Gerekirse kimlik doğrulama kitaplıklarını yükleyin:

uv sync

Doldurulması gerekenler

send_digest.py adlı kişiyi aç. İki yapılacak işi var.

YAPILACAKLAR 1: OAuth 2.0 erişim jetonunu yükleyin veya yenileyin:

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())

raise NotImplementedError satırını ekledikten sonra kaldırın. Bu işlem, ilk çalıştırmada OAuth kullanıcı rızası ekranı için bir tarayıcı açar. Jeton, gelecekteki çalıştırmalar için .gmail_token.json'da önbelleğe alınır.

YAPILACAKLAR 2: input="" yerine e-posta talimatlarını girin. Jeton, creds.token olarak zaten kapsamda:

    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}"
    ),

Her bölümün işlevi

Etkileşim, temsilcinin digest.pdf ve summaries.json öğelerini oluşturduğu aynı korumalı alanda devam eder. previous_interaction_id, temsilciye sohbet geçmişini verir.

Erişim jetonu, input dizesinde iletilir. Temsilci, istemden okuduğu bu bilgiyi Gmail API'yi çağırırken Authorization: Bearer üstbilgisinde kullanır. Yerel makinenize veya dosya sisteminize hiçbir zaman dokunmaz.

Aracı, sanal alan içinde bir Python komut dosyası yazıp çalıştırmak için code_execution kullanır: summaries.json okur, bunu bir bülten olarak biçimlendirir, digest.pdf okur, bir MIME çok parçalı mesaj oluşturur, bunu base64url ile kodlar ve https://gmail.googleapis.com/gmail/v1/users/me/messages/send POST eder.

Doğrula

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.

Gelen kutunuzu kontrol edin. E-posta, bülten biçimli gövde ve digest.pdf ekiyle birlikte gelir.

11. Günlük çalıştırmaları planlama

Şu ana kadar her adım manuel olarak tetiklendi. Tetikleyiciler, adlandırılmış ajanın bir cron ifadesinde otomatik olarak çalışmasını planlamanıza olanak tanır. Ajan, planlanan zamanda çalışır, özet iş akışının tamamını yürütür ve ortam, yürütmeler arasında kalıcı olur. Böylece ilk çalıştırmada yüklenen paketler, sonraki her çalıştırmada kullanılabilir.

Manual:     python run_digest.py     → runs once, now
Trigger:    client.triggers.create() → runs every morning, automatically

Doldurulması gerekenler

create_trigger.py adlı kişiyi aç. Bir yapılacak işi var.

YAPILACAKLAR 1: triggers.create() görüşmesini doldurun. Tetikleyici, her gün iş akışının tamamını çalıştırır: Özeti oluşturur VE gelen kutunuza gönderir. Erişim jetonlarının süresi bir saat içinde dolduğundan, .gmail_token.json adresinden alınan yenileme jetonunu satır içi kaynak olarak ekler. Böylece aracı, her çalıştırmada bu jetonu yeni bir jetonla değiştirebilir.

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 varsayılan zaman aşımıdır. max_consecutive_failures=3, tetikleyiciyi arka arkaya 3 başarısız çalıştırmanın ardından otomatik olarak duraklatır (API varsayılanı 5'tir; 3, atölye için daha muhafazakar bir değerdir).

sources listesi, /workspace/.gmail_creds.json konumundaki sanal ortama .gmail_creds.json öğesini yerleştirir. Aracı, yenileme jetonunu okur, yeni bir erişim jetonuyla değiştirir ve Gmail API'sini çağırır. Yenileme jetonlarının süresi dolmaz. Bu nedenle, manuel jeton yenileme olmadan her planlanmış çalıştırmada çalışır.

Aramayı ekledikten sonra raise NotImplementedError satırını kaldırın.

Doğrula

uv run python create_trigger.py
Trigger created: trig_abc123
Next run:        2026-07-23T09:00:00Z

create_trigger.py, tetikleyici kimliğini otomatik olarak .env'ye kaydeder.

Çalıştırmadan sonra yürütme geçmişini kontrol etmek için:

uv run python check_trigger.py

Bir sonraki planlanmış zamanı beklemeden tetikleyiciyi hemen etkinleştirmek için:

uv run python fire_trigger.py

Tetikleyiciyi duraklatmak veya silmek için:

uv run python pause_trigger.py

12. Temizleme

Sandbox, 7 gün boyunca işlem yapılmadığında otomatik olarak sona erer. Durdurulacak sunucu yok. Silinecek kapsayıcı yok.

Kaydettiğiniz bir aracı yapılandırmasını silin:

uv run python delete_agent.py

13. Özet

Sıfırdan, her seferinde bir kavram olacak şekilde yönetilen bir aracı oluşturdunuz. Her bir alıştırmada öğretilenler:

Egzersiz

Kavram

Key API

İlk görüşmenizi yapın

Gerçek bir Linux korumalı alanı sağlama ve etkinliklerini canlı olarak yayınlama

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

Ajanı özelleştirme

Yapılandırma dosyalarını bağlayın; aynı çalıştırmada kimlikleri .env olarak kalıcı hale getirin

environment.sources, save_env

Güvenlik kancası ekleme

Araç çağrılarını yürütülmeden önce durdurma, yıkıcı komutları reddetme

hooks.json, pre_tool_execution ve gate.py

PDF'yi indirin

PDF'yi temsilciyi yeniden çalıştırmadan indirme

Gemini Files API :download download_pdf.py içinde

Görüşmeyi sürdürün

Web'i yeniden getirmeden sohbete devam etme

environment=environment_id, previous_interaction_id=interaction_id

Aracı yapılandırmasını kalıcı hale getirme

Aracı yapılandırmasını kalıcı hale getirme; kaynak gerekmeden kimliğe göre çağırma

agents.create(), agents.list()

Gmail üzerinden gönderme

Yerel olarak bir OAuth jetonu alın, bunu code_execution aracılığıyla Gmail REST API'yi çağıran aracıya iletin.

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

Günlük çalıştırmaları planlama

Aracı, cron zamanlamasına göre otomatik olarak çalıştırma

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

Temel kalıplar

  1. Tek çağrı, tek korumalı alan: interactions.create() tüm altyapıyı yönetir (dağıtılacak container'lar veya yerel olarak yüklenecek paketler yoktur).
  2. Aşamalı yayın: stream=True, 90 saniyelik siyah kutuyu araç çağrıları ve metin parçaları içeren canlı bir feed'e dönüştürür.
  3. Satır içi kaynaklar: mount AGENTS.md, SKILL.md ve önceden oluşturulmuş komut dosyalarını yükleme veya dağıtım adımı olmadan korumalı alana yerleştirme
  4. Otomatik koşum keşfi: .agents/ içine yerleştirilen dosyalar otomatik olarak alınır (SDK yapılandırması gerekmez).
  5. İki boyutlu durum: environment_id dosyaları ve paketleri izler; previous_interaction_id sohbet bağlamını izler; her ikisi de bağımsız olarak iletilebilir
  6. Anlık görüntü indirme: Ortam, Gemini Files API aracılığıyla erişilebilen tam bir dosya sistemi tar'ıdır.
  7. Adlandırılmış aracı: agents.create(), yapılandırmayı kalıcı olarak yerleştirir. Gelecekteki çağrılar yalnızca aracı kimliğini ve environment="remote"'ı kaynak olmadan iletir.
  8. Kancalar: hooks.json + bir geçit komut dosyası, yürütülmeden önce çağrıları keser; deny yanıtı çağrıyı iptal eder ve temsilci kendini düzeltir.
  9. Harici API çağrıları: input isteminde bir kimlik bilgisi iletin. Aracı, code_execution aracılığıyla sanal alan içinde entegrasyon kodunu yazar ve çalıştırır.
  10. Tetikleyiciler: client.triggers.create() ile bir cron ifadesinde bir ajan planlayın; ortam, yürütmeler arasında kalıcı olur.

ADK + Cloud Run ve yönetilen aracılar: Bir bakışta farklar

Kapasite

ADK + Cloud Run

Gemini API'de yönetilen ajanlar

Korumalı alan sağlama

docker build + gcloud run deploy

interactions.create()

Araçları tanımlama

Temsilciye kaydedilen Python işlevleri

Yerleşik: Web'de gezinme, kod yürütme, dosya sistemi

Paketleri yükleme

Dockerfile'da pip install

Ajan, korumalı alan içinde pip install çalıştırılıyor

Etkinlikleri yayınlama

Özel SSE altyapısı

stream=True

Oturuma devam etme

Oturum veritabanı + bağlam yerleştirme

environment_id + previous_interaction_id

Yapılandırma dosyaları

Temsilciye gömülü kod olarak eklenmiş veya başlangıçta yerleştirilmiş

environment.sources üzerinden bağlandı

Yönetilecek altyapı

Container, Cloud Run, IAM, sır

Yok

Sonraki adımlar