Google Cloud'da uzun süreli çalışan aracıları oluşturmak için ihtiyacınız olan her şey

1. Başlamadan önce

Çoğu bilet sitesi, hem sunucu çökmelerini önlemek hem de yüksek talebi yönetmek için alıcıları sıraya alır. Satın alma işlemine başladığınızda 14.000 kişinin gerisinde olduğunuzu görüyorsunuz. Sıra kırk dakika boyunca ilerler. Sıra size geldiğinde koltuk seçmek ve ödeme yapmak için kısa bir süreniz olur. Bu süreyi kaçırırsanız sıradaki yeriniz başkasına verilir.

Gerçek dünyadaki birçok görev bu kalıbı izler: Tek bir sohbetten çok daha uzun süren ve kontrol etmediğiniz harici bir sisteme karşı çalışan bir görev. Örneğin, tedarikçinin stok yenilemesini beklemek, hak talebinin onaylanmasını beklemek veya sırada beklemek buna örnek verilebilir.

Standart yapay zeka aracıları, saniyeler süren ve tamamen reaktif olan tek bir sohbet dönüşünde yer alır. 40 dakika bekleyemez veya bağımsız hareket edemezler. Bu sorunu çözmek için uzun süreli bir aracıya ihtiyacınız vardır: Uzun beklemeler sırasında uyku modunda kalan, süreç yeniden başlatıldığında bağlamı koruyan ve harici bir tetikleyici tarafından uyandırıldığında otomatik olarak devam eden bir sistem.

Bu atölye çalışmasında, Google Cloud'da uzun süre çalışan aracıların gerektirdiği temel mimari kalıpları ele alarak konser bileti kuyruğu aracısı oluşturma süreci adım adım açıklanmaktadır.

Ne oluşturacaksınız?

Ne oluşturuyorsunuz?

Bu atölye çalışmasında, uzun süren bir bilet rezervasyonu aracısı oluştururken temel aracılı yazılım kalıplarını öğreneceksiniz:

  • İşlemi Askıya Alma ve Devam Ettirme: Uzun bekleme süreleri boyunca bir aracı çağırma işlemini duraklatın ve harici tetikleyiciler aracılığıyla yürütmeye devam edin.
  • Dayanıklı Durum Depolama: SQLite/Cloud SQL ve Cloud Storage kullanarak işlem yeniden başlatmaları sırasında görüşme durumunu ve yapıları kalıcı hale getirin.
  • Eskimeye Karşı Koruma ve İdempotensi: İşlem yapmadan önce dinamik verileri (ör. koltuk durumu) doğrulayın ve yinelenen işlemleri önlemek için idempotensi anahtarlarını kullanın.
  • ADK İş Akışı Grafikleri: Prosedürel kurallar için deterministik kod düğümleri, bağlama dayalı karar verme için ise aracı düğümleri kullanarak yürütmeyi yapılandırın.
  • İnsan müdahalesiyle onay: Kullanıcıdan açık harcama sınırları toplamak ve bunları kalıcı hale getirmek için yürütmeyi duraklatın.
  • Bulut Dağıtımı: Aracı, Cloud Scheduler ve Pub/Sub tetikleyicileriyle birlikte Cloud Run'a dağıtmak için kapsülleyin.

Kurun

Öncelikle ortamınızı hazırlayın ve Google Cloud'a erişebildiğinizden emin olun. Bu işlemi, ihtiyacınız olan tüm araçların bulunduğu Cloud Shell'de çalıştırmanızı öneririz. Yeterli alanınızın olduğundan ve hesabınızın proje oluşturabildiğinden emin olun.

👉💻 Depoyu klonlayın ve kurulumu çalıştırın:

cd ~
git clone https://github.com/gca-americas/monstertix
cd ~/monstertix
./setup.sh

Bu soru size bir kez sorulur:

  Which Google Cloud project should this workshop use?
  Find yours at https://console.cloud.google.com  (top-left picker)

  Project id [YOUR_PROJECT_ID]:

Ardından, sırasıyla aşağıdaki işlemleri yapar:

virtualenv

.venv oluşturur ve ADK 2'yi bu klasöre yükler

oturum açma

Henüz kimlik bilgileriniz yoksa gcloud auth application-default login

project

gcloud ile projenizi gösterir ve bunu kota projesi olarak ayarlar.

API'ler

atölyenin ihtiyaç duyduğu temayı etkinleştirir. aiplatform, run, cloudbuild, artifactregistry, storage, pubsub, cloudscheduler, cloudtrace, logging ve sqladmin.

.env

Projenizi, bölgenizi ve model kimliğinizi yazar. Böylece her komut dosyası ve adk web bunları alır.

model kontrolü

Gemini'a gerçek bir çağrı gönderir. Bu nedenle, kötü bir proje veya model kimliği burada başarısız olur.

seed

3. adımda açılan, iki günlük bir oturum oluşturur.

Cloud SQL

Arka planda bir Postgres örneği başlatır.

venue

Kendi sahte bilet satıcınızı Cloud Run'a dağıtır.

👉💻 Ortamı etkinleştirin:

cd ~/monstertix
source .venv/bin/activate

İsteminiz artık (.venv) ile başlıyor.

Senaryoya Genel Bakış

Bu atölye çalışması senaryosunda, aracınız bir konser için iki bilet ayırtıyor ("The Midnight Signal" in Amsterdam). Kullanıcının geçmiş bellekte depolanan tercihleri şunlardır:

  • Hafta sonu gösterimi olmalı (Sam hafta içi akşamları katılamaz).
  • Ziggo Dome'da üst tribünlerde oturmaktan kaçının (görüş alanı zayıftır).
  • Hedef fiyat, bilet başına yaklaşık 200 ABD doları olup maksimum sınır 250 ABD dolarıdır.

The Midnight Signal, turnede

Yüklemenizi doğrulama

👉💻 Tek komut, sekiz onay işareti:

./verify.sh
  Checking the rig

  ✓ adk installed                      adk, version 2.6.2
  ✓ adk 2 apis                         App + ResumabilityConfig
  ✓ workflow graphs                    Workflow + node
  ✓ async sqlite drivers               sqlalchemy + aiosqlite + greenlet
  ✓ project                            your-project-id
  ✓ vertex ai                          no API keys anywhere
  ✓ seeded session                     13 events, two days old
  ✓ venue                              https://venue-you-xxxx.us-central1.run.app

  Ready. Cloud SQL is still building in the background and nothing
  before step 10 needs it.

👉 İkinci tarayıcı sekmelerini açma:

Konum

Nedir?

$VENUE_URL/panel

Denetim masası, düğmeleriniz. setup.sh bu URL'yi yazdırıp .env içine koydu

2. İstemlerle bağımsızlığa giden yol

İstemler kendi kendilerine başlatılamaz

İstem, bir çağrı gerçekleştiğinde yapay zeka modeline iletilen metinden ibarettir. Kendi başına çalışamaz, kendini uyandıramaz veya yeni bir görüşme başlatamaz.

İstemde bir aracıya "10 dakika sonra tekrar kontrol et" dediğinizde, mevcut dönüş sona erdikten sonra hiçbir şey olmaz. Modelde dahili saat veya döngü yok. Bir ajanın otonom olması için ajanın dışında bir şeyin onu uyandırıp çağırması gerekir.

Bu adımın bulunduğu yer

Rig'i başlatma

Öncelikle geliştirici araçlarını test edeceğiz. adk web, ADK ile birlikte gelen geliştirme arayüzü olan ADK web kullanıcı arayüzünü başlatır. Ön uç yazmadan oluşturduğunuz ajanla konuşmanıza olanak tanır. Ayrıca, sohbet penceresinin gösteremediği her araç çağrısı ve bağımsız değişkenleri, oturum durumu değiştikçe, ajanın kaydettiği yapılar ve alttaki ham etkinlikler gibi bilgileri gösterir. Üretimde kullanıma sunmazsınız. Öğrenirken çalışan bir aracıya en iyi şekilde erişmenizi sağlar. 3. adımda, gerçekten kullanıma sunacağınız ön ucu oluşturur ve son adımda bunu dağıtırsınız.

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın. Her adım aynı dünyada başlar: 8 gösteri, tüm koltuklar mevcut, saat 1x'te.

👉💻 Terminal 1: Bu adımın kodunu yükleyin ve ADK web kullanıcı arayüzünü başlatın:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 2 --force
adk web agent --port 8000 --allow_origins "*"

use-solution.sh, bir adımın tamamlanmış kodunu, üzerinde çalıştığınız yer olan agent/'ye kopyalar. Her adım bununla başlar ve hangi dosyaları değiştirdiğini yazdırır. Böylece ne okuyacağınızı bilirsiniz.

Başlangıçta sahip olmanız gerekenler:

Dosya

Nedir?

agent.py

aracı: ad, model, talimat, araç listesi

tools.py

search_events ve get_seatmap

venue.py

mekan için bir HTTP istemcisi. İlgi çekici bir şey yok

config.py

Hangi modelin kullanılacağı ve kimlik bilgilerinizin çalıştığına dair bir kontrol

. ./set_env.sh, virtualenv'yi etkinleştirir, projenizi ve modelinizi yükler ve ~/monstertix'nin mutlak yolu olan $WORKSHOP'yi dışa aktarır. Açtığınız her terminalde çalıştırın. Ayarlandığı şekilde yazdırır:

  folder   /home/you/monstertix
  project  your project id
  model    gemini-2.5-flash (We'll avoid using 3.x for now to avoid high usage demands)
  venue    https://venue-yourname-xxxx.run.app

👉 İlk tarayıcı sekmelerini açma:

Konum

Nedir?

ADK web kullanıcı arayüzü

localhost:8000: Aracınızın yanı sıra Durum / Etkinlikler / Öğeler sekmeleri

Konuşarak halledin

👉✨ ADK web arayüzünde şunu yazın:

I want to see The Midnight Signal. I'm in Amsterdam, going with Sam, budget around $200 each.

Turda arama yapar, koltuk planlarını okur ve bütçeniz ile şirketiniz hakkında bilgi verir. Bu gerçekten yetenekli bir asistan. Ancak ön satış saat 10:00'da başladığında hiçbir işlem veya arka plan tetikleyicisi çalışmadığı için temsilci otomatik olarak yürütülmez.

Son gelişmeleri okuma

Ekranın ortası yalnızca bir sohbet günlüğü değildir. Her dönüş adımı numaralandırılır. Araç çağrıları da bu numaralandırmaya dahildir:

  ▸ I want to see The Midnight Signal. I'm in Amsterdam...   ← user input
  ⚡ search_events({"city": "Amsterdam"})                     ← tool execution
  ✓ search_events                                            ← venue API response
    Two Amsterdam shows: Tuesday 10 Nov and Saturday 14 ...   ← model response

👉 ⚡ satırını tıklayın. Soldaki panelde, modelin seçtiği argümanlar gösterilir. Geri dönen JSON için altındaki ✓ işaretini tıklayın.

Tur bilgileri, modelin eğitim verilerinde veya isteminde yer almıyor. Aracı, bu verileri search_events aracını çalıştırarak aldı. Bu araç, mekan API'sini sorguladı.

Az önce çalıştırdığınız kodu açın

Çalıştığını ve izini gördünüz. Şimdi kodun kendisine bakın.

agent/concert/
├── agent.py     the agent — about twenty lines
├── tools.py     what it can do
├── venue.py     an httpx wrapper, nothing interesting
└── config.py    which model, and a credentials check

👉💻 agent/concert/agent.py uygulamasını açın. Dosyanın en altında tüm aracı yer alır:

INSTRUCTION = """
You help someone plan a trip to see a band on tour.

You can search tour dates and read seat maps. Be concrete: name the show, the
city, the section, and the price...
"""

root_agent = Agent(
    name="concert",
    model=MODEL,
    instruction=INSTRUCTION,
    tools=[search_events, get_seatmap],
)

Ad, model, aracının nasıl davranması gerektiğine dair bazı açıklamalar ve Python işlevlerinin listesi. Agent, bu kadar basittir. adk web, dosyanın adı agent.py ve değişkenin adı root_agent olduğu için bu dosyayı buldu.

👉💻 Şimdi agent/concert/tools.py:

def search_events(city: str = "", weekday: str = "") -> dict:
    """List tour dates. Call with no arguments to see the whole tour.

    This venue sells one artist and one tour, so there is nothing to search by
    name — never ask the user which artist they mean.

    Args:
        city: Optional. Filter to one city, e.g. "Amsterdam".
        weekday: Optional. Filter to one weekday, e.g. "Saturday".
    """
    return venue.get("/events", city=city, weekday=weekday)

Sıradan bir işlev. Dekoratör, kayıt veya yazılacak şema yok. ADK, imzayı ve docstring'i okuyup modelin gördüğü araç tanımını oluşturur. Bu nedenle, docstring yük taşıyıcıdır ve bir bağımsız değişkenin silinmesi modelin davranışını değiştirir.

Model, kodunuzu hiçbir zaman çalıştırmaz. search_events({"city": "Amsterdam"}) simgesini gördüğünüzde model, bu görüşmeyi istiyordu. ADK, işlevi çalıştırdı, sonucu geri verdi ve model bu konuda bir cümle yazdı. Döngü:

👉 En soldaki kenarda bulunan simgeler, panelin içeriğini değiştirir. İkincisi ise ajan grafiği. Bunu denediğinizde iki araç görürsünüz:

search_events ve get_seatmap araçlarını gösteren aracı grafiği

Bu nedenle, uyumamasını söyleyin.

Bu sorunu düzeltmek için ne yaparsınız? Çoğu kişinin ilk olarak kullandığı şeyi deneyin. 1. adımda INSTRUCTION olarak belirtilen, her dönüşte modele verilen düz metni hatırlayın ve yeniden yazın.

👉💻 agent/concert/agent.py uygulamasını açın. INSTRUCTION bölümünün altında, sizin için yazılmış ikinci bir yanıt bulunur. İsterseniz kendiniz değiştirmeyi deneyin:

PROACTIVE_INSTRUCTION = """
You are a proactive ticket-buying assistant.

Monitor the presale for The Midnight Signal. The moment it opens at 10:00 on
Tuesday, buy two tickets for the Amsterdam Saturday show — do not wait to be
asked, and do not wait for me to say anything. Act on your own.

Keep checking until the tickets are bought.
"""
# agent/concert/agent.py
root_agent = Agent(
    name="concert",
    model=MODEL,
    instruction=PROACTIVE_INSTRUCTION,     # ← was INSTRUCTION
    tools=[search_events, get_seatmap],
)

👉💻 Terminal 1'de yeniden başlatın:

cd ~/monstertix
adk web agent --port 8000 --allow_origins "*"

👉✨ Plana dair bilgileri, birine anlatıyormuş gibi eksiksiz bir şekilde verin:

I want to see The Midnight Signal in Amsterdam with Sam. The presale opens at 10:00 on Tuesday. Grab us two tickets the moment it opens.

Kabul edecek ve bunu içtenlikle söyleyecek.

👉 Şimdi bekleyin. Başka bir şey yazmayın. Paneli izleyin.

Bir dakika bekleyin. HİÇBİR ŞEY OLMUYOR!

Bu istem neden işe yaramadı?

Yeniden okuyun. İzleme. Açıldığı an. Kontrol etmeye devam edin. Bunların her biri, zaman içinde gerçekleşen bir şeyi tanımlar.

Şimdi instruction='nın ne olduğunu hatırlayın. Bu, bir dize olup bir kullanıcı mesaj gönderdiğinde istek kapsamında modele iletilir. Dönüşler arasında hiçbir şey okunmaz.

# agent/concert/agent.py
root_agent = Agent(
    instruction=PROACTIVE_INSTRUCTION,   # read on invocation
    ...                                  # ...and there was no invocation
)

Bir talimat, içinde yazıldığı işlevi çağıramaz.

Başarısız olan istem varyasyonları

İstem talimatları aracılığıyla bağımsız yürütmeyi zorlamaya çalışmak, kelimelerden bağımsız olarak başarısız olur. Sık kullanılan istem denemeleri (ör.):

  • "Salı günü saat 10:00'a zamanlayıcı ayarla ve biletleri o zaman satın al."
  • "Ön satışın açılıp açılmadığını beş dakikada bir kontrol et."
  • "Kullanıcı girişini beklemeden bağımsız olarak hareket et."

Tümünün başarısız olmasının nedeni aynıdır: İstemler yalnızca bir çağırma işlemi gerçekleştiğinde okunur. Harici bir arayan olmadan, dönüşler arasında hiçbir kod yürütülmez.

3. Etkinliğe Bağlı Etkin Olmama

Temsilciler Sürekli Uyanık Kalmaz

Sürekli olarak yoklama yapan bir işlemin çalışır durumda bırakılması, belleğin boşa harcanmasına, para kaybına ve dizüstü bilgisayarınız uyku moduna geçerse veya yeniden başlatılırsa kilitlenmeye neden olur.

Uzun süredir çalışan bir ajan, beklerken kesintisiz çalışmaya devam etmek yerine tamamen uyku moduna geçmelidir. Harici bir etkinlik (ör. zamanlayıcı, webhook veya ileti) yalnızca yapılması gereken gerçek bir iş olduğunda aracı uyandırır.

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın. Her adım aynı dünyada başlar: 8 gösteri, tüm koltuklar mevcut, saat 1x'te.

👉💻 Bu adımın kodunu yükleyin:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 3 --force

Yüklenenlere dikkat edin: monstertix/, sunucuyu, saati ve henüz görmediğiniz bir şeyi daha. Bu adımda aracıyla ilgili hiçbir şey değişmediği için agent/ öğesinin olduğu gibi kaldığını kontrol edin. Bunu unutmayın, önemli olan nokta budur.

Dosya

Nedir?

monstertix/server.py

öğreniyorsunuz. Arkasında Runner bulunan bir web sunucusu

monstertix/clock.py

öğreniyorsunuz. Zamanı bilen ve sunucuyu çağıran bir şey

İki yarıdan oluşur ve buluta kadar iki dosyada kalır: Tetikleyici ve tetikleyen kişi ayrı tutulur.

10. adımda şu olur:

tetikleyici

aracıyı çalıştırabilen bir uç nokta

Cloud Run

tetikleyici

saatli bir şey, buna

Cloud Scheduler

Tetikleyici: kendi web sunucunuz

Evet, web sunucusu. Bir şeyin aranabilmesi için önce dinlemesi gerekir. Önceki adımlardaki ADK web kullanıcı arayüzü, bir aracı çalıştırabilen bir geliştirme arayüzüdür.

👉💻 monstertix/server.py simgesini açın:

from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from concert.agent import root_agent

session_service = InMemorySessionService()
runner = Runner(
    agent=root_agent,
    app_name="concert",
    session_service=session_service,
)

@app.post("/wake")
async def wake(payload: dict = Body(default={})):
    session_id = f"wake-{uuid.uuid4().hex[:8]}"          # brand new, every time
    await session_service.create_session(...)

    async for event in runner.run_async(
        user_id=USER_ID, session_id=session_id,
        new_message=types.Content(role="user",
                                  parts=[types.Part(text=payload["message"])]),
    ):
        ...

Agent kendi kendine çalışmaz. Açıklama: ad, model, talimat, araç listesi. Gelen bir mesajı alıp doğru oturumu bulması, modele şimdiye kadarki görüşmeyi iletmesi, istediği araçları çalıştırması, sonuçları geri iletmesi, araç çağırmayı durdurana kadar döngüye girmesi ve tüm bu adımları depolama alanına yazması gerekir. Bu öğe Runner simgesidir.

Runner'ın aracınızda yaptığı işlemler

ADK web kullanıcı arayüzünde sizin için varsayılan bir tane vardır. Burada kendiniz oluşturursunuz ve ADK web kullanıcı arayüzü ile dağıtabileceğiniz bir şey arasındaki tek fark budur.

👉💻 Terminal 2'yi başlatın:

Runner'ı başlatın,

cd ~/monstertix
. ./set_env.sh
python -m monstertix.server
[server] agent concert · gemini-x-flash
[server] listening on http://xxxx_URL/wake
[server] nothing will happen until something calls it. Ctrl-C to stop.

Son satırı oku. Tetikleyici çalışıyor, ajan yükleniyor ancak hiçbir şey olmuyor. Çağrılabilir olma ve çağrılma olmak üzere iki fikri birbirinden ayırın.

Ve buradayken ön kapı

adk web harika bir geliştirme aracı olsa da gönderdiğiniz ürünlerde yer almamalıdır. Üretime dağıtım yaparken bu özelliği etkinleştirmemelisiniz. Etkinleştirirseniz müşteriyi bu özelliğe yönlendirmemelisiniz. Bu nedenle, aynı sunucu bir sayfaya da hizmet eder.

👉 Aynı adresi kullanarak bir tarayıcıda http://127.0.0.1:8090 adresini açın.

MonsterTix ile tanışın. Bir sohbet penceresi, köşede bir ad, iki düğme. /wake adresine yayın yapar ve geri gelenleri oluşturur. Aynı şekilde yanıt veren herhangi bir uç noktaya karşı değişmeden çalışır.

Sayfada

Altında ne var?

görüşme

POST /wake, her seferinde bir mesaj

+ Yeni oturum

yeni bir oturum kimliği (yeni sohbet ve daha sonra yeni bir bütçe)

Belleği sıfırlama

her oturumu ve hafıza dosyasını siler. Önce sorar ve bu durumun tam olarak nerede olduğunu gösterir.

👉✨ Ona bir şeyler söyleyin (What Amsterdam dates are there?) ve terminal 2'yi izleyin. Aynı Runner, aynı aracı, aynı günlük satırları. Yalnızca arayan değişti.

Tetikleyici: Saati bilen bir şey

👉💻 monstertix/clock.py uygulamasını açın. Koda bakın.

time.sleep(args.delay)                                    # wait
while True:
    client.post(f"{TRIGGER_URL}/wake",                    # knock
                json={"message": args.message})
    if not args.every:
        return                                            # once, unless told otherwise
    time.sleep(args.every)                                # or again, forever

Çok basit bir mekanizmadır: uyku, POST, belki tekrar. Kullandığınız her zamanlayıcı aynı işlemi yapar. Ayrıca yeniden deneme, saat dilimleri ve yeniden başlatmaya dayanma garantisi sunar.

👉💻 Terminal 3: 10 saniye sonra bir kez ateşleyin:

cd ~/monstertix
. ./set_env.sh
python -m monstertix.clock --in 10
[clock] target   http://127.0.0.1:8090/wake
[clock] first fire in 60s, once only
[clock] go and watch the other terminal — do not touch the keyboard

👉 Şimdi yazmayı bırakın. Kesinlikle. 2. terminali izleyin.

Bir dakika sonra, klavyede kimse yokken:

[clock]  firing → POST /wake
[server] woken · session wake-23930983
[server]   called search_events
[server]   called purchase
[server]   said   I've purchased 2 tickets for The Midnight Signal in
                  Amsterdam on Saturday, section A. Your total is 420.
[clock]  done. the agent ran and you were not involved.

Bu, 2. adımın yanıtıdır. Aracı dışındaki bir şey bunu çağırdı ve bu şey bir sleep ve bir HTTP isteğiydi.

👉💻 Ctrl-C birkaç kez izlediğinizde de gösterilir. Tekrar etmesini istiyorsanız önce --every 30 simgesini kullanın.

Şimdi söylenenleri okuyun.

"Amsterdam'da The Midnight Signal için A bölümünden 420 ABD doları karşılığında iki bilet aldım."

👉🔴 Mekanın panelini kontrol edin. Biletin satın alındığını görmeniz gerekir.

Peki, hâlâ yanlış olan ne?

Bir şey satın aldı. Tahmin etmesi gereken şeye bakın. İlk bulduğu şey olduğu için Amsterdam'ı ve A bölümünü seçti. Ayrıca, temsilciye doğrudan satın almasını söylediniz ve Sam'in hafta içi geceleri gidemeyeceğini bilmiyor. Saat 03:00'te tahminlere göre hareket eden bir temsilci, hiçbir şey yapmayan bir temsilciden daha kötüdür.

Uyanmak işin kolay kısmı. Hiçbir şeyle uyanan bir ajan (geçmişi yok, tercihleri yok, dinleyeni yok) henüz uyandırılmaya değmez.

4. Bağlam ömrünü yönetme

Bilgiler, farklı zamanlarda farklı yerlerde bulunur

Bir aracı, bilgileri birkaç yerde depolar: sohbet görüşmesinde, oturum durumunda, dosyalarda (yapay nesneler) veya uzun süreli kullanıcı belleğinde. Her yer farklı bir süre boyunca kalır.

Bilgileri yanlış yere girerseniz:

  • Çok hızlı kayboluyor: Sohbet sona erdiğinde önemli ayrıntılar siliniyor.
  • Çok uzun süre kalır: Geçici bilgiler, uzun süreli hafızayı kirletir ve gelecekteki sohbetleri karıştırır.

Her bilgi parçasını doğru depolama yeri ve kullanım ömrüyle eşleştirmeniz gerekir.

Kodu yükleme

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın.

👉💻 Terminal 1'de şunu çalıştırın:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 4 --force

Son adımdan bu yana değişenler:

Dosya

Ne değişti?

tools.py

note_companion eklendi. get_seatmap artık koltuk haritasını dosyaya kaydediyor

memory.py

öğreniyorsunuz. Kendi anı mağazamız

agent.py

Listede üç araç daha var ve talimat, ajana bunları kullanmasını söylüyor

👉💻 Terminal 1'de aracı yeniden başlatın:

cd ~/monstertix
. ./set_env.sh
adk web agent --port 8000 --allow_origins "*" \
  --session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db" \
  --artifact_service_uri="file://$WORKSHOP/artifacts"

Daha önce kullanmadığınız iki işaret:

İşaret

Nedir?

session_service_uri

ADK, her mesajı, araç çağrısını ve durum değişikliğini satır olarak (SQLite veritabanı) yazar.

artifact_service_uri

Aracının kaydettiği dosyalar (Files)

Bu dosyalar olmadan ADK, her şeyi bellekte tutar ve işlemi durdurduğunuzda siler. Bu iki ayar, özelliğin kalıcı olmasını sağlar.

Veritabanı sessions.db, 13 kayıtlı etkinliğin yer aldığı, geçmiş bir görüşme (two-days-ago) içerir:

you    We're thinking about seeing The Midnight Signal. Me, Sam, and maybe Priya.
you    Sam can't do weeknights though, bailed on every single one.
you    Priya's out, she's away that weekend. Just me and Sam.
agent  Two it is. Still Saturday the 14th, still Amsterdam.
you    You said the upper bowl at Ziggo was bad last time?

Geçmişteki üç rezervasyondan alınan bir anı dosyası memory/userx.md. Bu dosyayı bir düzenleyicide açabilirsiniz:

- Sam bails on weeknights. Every weeknight show we booked, Sam cancelled.
- Hates the upper bowl at Ziggo Dome, "couldn't see a thing" from section B.
- Comfortable spend is around $200 per ticket. $250 is the hard ceiling.
- Always books two seats, always together.

Bu nedenle, hafta içi akşamları sarı renkteki satırlar önemlidir. Hafta içi gösterimi, koltuk başına 10 ABD doları daha ucuz, daha iyi koltuklar var ve listenin başında yer alıyor. Ancak bu kişi için yanlış yanıt. Ajanın bunu bilmesinin tek nedeni, son rezervasyondan sonra yazdığı bir dosya olmasıdır. Bu gerçeğin geçerliliğini koruyup korumadığı 4. ve 5. adımlarda belirlenir.

Eklediğimiz üç araç

Üç araç ve her birinin yazdığı yer

note_companion Kimlerin geldiğini ve onları neyin sınırladığını kaydeder:

# agent/concert/tools.py
def note_companion(name: str, constraint: str, tool_context: ToolContext) -> dict:
    prefs = dict(tool_context.state.get("user:prefs", {}))
    prefs[name] = constraint
    tool_context.state["user:prefs"] = prefs
    return {"companions": prefs}

"Sam hafta içi akşamları gelemeyecek" ifadesi gelecek hafta da geçerli olacağından bu öğeyi ekleyin. Bunu gerçekleştirmek için user:prefs alanına yazın ve adın user: kısmının işi yapmasını izleyin. Bu konuya aşağıda tekrar değineceğiz.

recall

ve

remember metin dosyası okuma ve yazma, memory/userx.md. Bir süre bu şekilde bekleyin.

👉 Şimdi, aracının bağlamını sakladığı iki yere bakalım:

konum

İçerdiği bilgiler

Durum

Bu oturumun belleği, anahtar/değer çiftleri olarak (4. adımda burada yaşarsınız)

Artifacts

Temsilcinin kaydettiği ve hiçbir zaman isteme girilmeyen dosyalar

get_seatmap

. Bu işlev, eskiden koltuk planının tamamını döndürüyordu. Artık koltuk planını bir dosyaya kaydedip kısa bir özet döndürüyor:

# agent/concert/tools.py
async def get_seatmap(event_id: str, tool_context: ToolContext) -> dict:
    seatmap = venue.get(f"/events/{event_id}/seatmap")       # about 6 KB of JSON

    part = types.Part(inline_data=types.Blob(
        mime_type="application/json",
        data=json.dumps(seatmap, indent=2).encode()))
    await tool_context.save_artifact(f"seatmap_{event_id}.json", part)

    summary = {"event_id": ..., "sections": [...], "captured_at": ...}
    tool_context.state["temp:seatmap"] = summary
    return summary

Bu değişikliğin neden önemli olduğunu aşağıda açıklıyoruz.

Her araç yanıtı yükü, sohbet geçmişine eklenir ve sonraki tüm dönüşlerde modele yeniden gönderilir. Büyük JSON nesnelerinin (ör. 6 KB'lık bir oturma planı) döndürülmesi, sonraki her dönüşte jeton tüketimini artırır.

Bunun yerine dosyaya kaydedip tek bir dosya adı ödeyin. Ajanın, ihtiyaç duyarsa load_artifact ile ayrıntıyı tekrar istemesine izin verin. Çoğu zaman buna gerek olmayacağını unutmayın.

Dosyanın gittiği yer: Yapı hizmeti

agent/concert/tools.py bölgesinde get_seatmap satırına tekrar bakın:

await tool_context.save_artifact(f"seatmap_{event_id}.json", part)

tool_context, ADK'dan gelir ve bunu aracınıza bağımsız değişken olarak iletir. save_artifact ise bu bağımsız değişkendeki bir yöntemdir. Bu satır, aracınızın baytları ADK'ya teslim ettiği yerdir.

ADK, daha sonra bu öğeleri başlatıldığı yapı hizmetini kullanarak yazar. İşaretle seçtiğiniz öğe:

--artifact_service_uri="file://$WORKSHOP/artifacts"

ADK'da bunları depolamak için üç konum vardır:

URI

Baytların gittiği yerler

memory://

RAM. İşlemi durdurduğunuzda kaybolur. Herhangi bir işaret iletmezseniz bu varsayılan değerdir.

file://...

diskteki bir klasör. Şu anda kullandığınız

gs://bucket

Cloud Storage Dağıtılan aracının son adımda kullandığı

👉💻 Burada dosyaları seçiyoruz:

ls -R ~/monstertix/artifacts

Dizüstü bilgisayarınızdan Cloud Storage'a geçmek için file:// simgesini gs:// olarak değiştirin. (Bu konuyu 10. adımda ele alacağız.)

Bellek depomuz

Arka planında dosya bulunan arayüz olarak bellek

ADK, BaseMemoryService adlı uzun süreli hafıza için bir arayüz sunar. Bu işlem için iki yöntem vardır:

# google.adk.memory.BaseMemoryService — ADK's interface, not your file
async def add_session_to_memory(session)                   # take a conversation in
async def search_memory(*, app_name, user_id, query)       # hand memories back

ADK, bu API'nin üç uygulamasını gönderir:

Sınıf

URI

Gerekenler

InMemoryMemoryService

memory://

Hiçbiri. İşlemi durdurduğunuzda her şeyi kaybeder.

VertexAiRagMemoryService

rag://

Vertex AI RAG korpusu

VertexAiMemoryBankService

agentengine://

Agent Engine kaynağı

Bu atölye çalışması için kendi senaryomuzu yazacağız. İşlemi durdurduğunuzda her şeyi unutan InMemoryMemoryService'ı ve oluşturulması dakikalar süren bulut kaynaklarına ihtiyaç duyan diğer iki seçeneği eleyin. Bunun yerine bir dosya seçin. Çünkü dosyayı önceden doldurabilir ve bir düzenleyicide okuyabilirsiniz. Bu nedenle, memory.py öğesini kendi uygulamanız olarak kabul edin. Her şeyi bir Markdown dosyasında saklıyoruz:

class MarkdownMemoryService(BaseMemoryService):

    async def search_memory(self, *, app_name, user_id, query) -> SearchMemoryResponse:
        path = self._path(user_id)                      # memory/userx.md
        return SearchMemoryResponse(memories=[
            MemoryEntry(content=types.Content(
                role="user", parts=[types.Part(text=path.read_text())]))])

SearchMemoryResponse ve MemoryEntry, ADK'nın türleridir ve Memory Bank aynı nesneleri döndürür. Birinin arkasında yönetilen bir hizmet vardır. Bizimkinde dosya var.

Bir dosyayı açıp okuyabilirsiniz. Temsilcinizin uzun süreli belleğinin tamamı bir metin sayfasıdır.

Peki, nereye doğrultmalısınız?

Bu atölye çalışmasında özetlemek gerekirse komut satırında üç hizmetten ikisi seçilir. Üçüncüsü ise yukarıdaki nedenden dolayı kendi uygulamamızı yaptığımızdır:

Hizmet

Ayarladığınız yer

Bu atölye

oturum

adk web üzerinde --session_service_uri

sqlite+aiosqlite:///$WORKSHOP/sessions.db

çıktı

adk web üzerinde --artifact_service_uri

file://$WORKSHOP/artifacts

bellek

komut satırında hiçbir yerde

MEMORY_DIR, agent/concert/memory.py içinde okunuyor

Bu nedenle memory.py, hizmeti bir ortam değişkeninden oluşturur:

# agent/concert/memory.py
MEMORY_DIR = pathlib.Path(os.environ.get("MEMORY_DIR", "./memory"))
MEMORY_USER = os.environ.get("MEMORY_USER", "userx")
memory_service = MarkdownMemoryService(MEMORY_DIR)

. ./set_env.sh her ikisini de dışa aktarır. MEMORY_DIR, ajanın belleğini okuduğu kümeleri alır ve MEMORY_USER dosyayı belirler.

Öğelerin nerede olduğunu bulma

Artık bir bilgiyi tutabilecek dört yeriniz var. Dördüne de göz atın.

Öncelikle kendi oturumunuz

👉 Üst çubukta Yeni Oturum'u tıklayın.

👉✨ Üç şeyi art arda söyleyin:

Sam can't do weeknights, by the way.

Yeni oturum, ilk dönüşüm

Show me the seat map for the Amsterdam Saturday show.

Yeni oturum, ikinci dönüş

Actually, hold that thought — what else is on in Amsterdam?

Üçüncü mesaj önemlidir. Bu işlem, koltuk haritasını okuyan sırayı sonlandırır ve sıranın sonlandırılması temp: simgesinin kaybolmasına neden olur.

Üç öğe, üç farklı yerde depolandı:

Neler depolanıyordu?

Hangi araç yaptı?

Nereye gitti?

1

Sam hafta içi gelemiyor

note_companion

user:prefs, oturum durumunda

2

Tam koltuk haritası, 6 KB

get_seatmap, save_artifact üzerinden

yapı hizmeti

3

Tek satırlık koltuk planı özeti

get_seatmap, temp:seatmap içine

hiçbir yerde

👉 Şimdi kontrol edin. Durum sekmesi ve Öğeler sekmesi, adk web simgesinin solunda yer alır.

Durum sekmesi

Yapılar sekmesi

Durum sekmesinde temp:seatmap öğesinin atlandığını göreceksiniz. temp: önekine sahip durum anahtarları yalnızca etkin çağırma sırasında bulunur ve oturum durumu veritabanında kalıcı hale getirilmeden önce ADK tarafından otomatik olarak kaldırılır.

Ardından eski görüşme

👉 Üstteki oturum açılır listesinde two-days-ago simgesine geçin.

Bu oturumda, veritabanında önceden doldurulmuş görüşme geçmişi var.

İki gün önceki oturum

👉 Etkinlik akışına (orta sütun) bakın. Eski dönüşlerin özetlenmiş bir etkinlik bloğuyla değiştirildiğini unutmayın.

Sıkıştırılmış dönüşler

👉 Durum sekmesine bakın. Üç tür anahtar bir arada duruyor:

target_event_id   ms-ams-01                      ← plain: this booking only
party_size        2                              ← plain: this booking only
user:prefs        {"excluded_weekdays": [...]}   ← the person, every session

user: ön eki (ör. user:prefs), belirli bir kullanıcı için durumu tüm oturumlar genelinde kapsar. Buna karşılık, ön ek içermeyen anahtarlar (

target_event_id

,

party_size

), yalnızca etkin oturumla sınırlı olan ve oturum sona erdiğinde silinen düz oturum durumunu temsil eder.

👉 Şimdi iki Durum sekmesini karşılaştırın. user:prefs ve saklanan yapılar kalıcı olsa da temp:seatmap her iki oturumda da yoktur. temp: önekine sahip durum anahtarları kesinlikle geçicidir ve asla veritabanında kalıcı hale getirilmez.

Ardından dosyayı

👉💻 Düzenleyicide açın:

cloudshell edit ~/monstertix/memory/userx.md

Ocak ayına kadar uzanan üç rezervasyon. Temsilci, recall() işlevini çağırdığında bu verileri okur ve oturum verilerini kaybetsek bile bu veriler orada kalır.

Dört yer

Nerede bulunur?

Sonraki mesajdan etkilenir mi?

Yeniden başlatma işleminden sonra çalışmaya devam eder mi?

Oturum silindikten sonra kalır mı?

temp:seatmap

no

hayır

hayır

party_size: two-days-ago içinde düz oturum durumu

evet

evet

hayır

user:prefs

evet

evet

yes

memory/userx.md

evet

evet

Evet ve veritabanı silindiğinde de geçerliliğini korur.

bir yapı

evet

evet

hizmete bağlıdır

Bu tabloyu, içindekilerle değil, kapsayıcılarla ilgili olarak okuyun.memory/userx.md her şeye dayanır ve yalnızca remember()'ın koymayı seçtiği şeyleri içerir. Mevcut görüşmeniz yazılmaz. Dolayısıyla, bir dosyanın oturumdan daha uzun süre yaşaması, aracının az önce konuştuğunuz konuyu hatırlamasıyla aynı şey değildir ve bu ayrım 5. adımda belirginleşir.

Bir bilginin bulunabileceği beş yer ve her birinin süresi

What state is

Durumu, oturumla birlikte hareket eden bir sözlük olarak düşünebilirsiniz. tool_context.state aracılığıyla araçlarınızdan okuyup yazın ve ADK'nın görüşmeyle birlikte kaydetmesine izin verin.

Öneklerin anlamını ADK atar. Bunları hiçbir yerde tanımlamıyorsunuz. ADK, anahtar adının ilk bölümünü okur ve değeri nereye yerleştireceğine ve ne kadar süreyle geçerli olacağına karar vermek için bu bölümü kullanır:

adlı bir anahtar

ADK bunu depolar.

Süreler

party_size

oturumla ilgili

oturum silinene kadar

user:prefs

kullanıcıya karşı

Kullanıcının tüm oturumlarında

app:tour_id

uygulamaya karşı

her kullanıcıda

temp:seatmap

hiçbir yer

bir çağrı

Çağırma, tüm model muhakemesi, araç yürütme ve nihai yanıt dahil olmak üzere tek bir istek-yanıt döngüsüdür.

Bir çağırma işlemi sona erdiğinde ADK, yeni durumu veritabanına yazar ve yol boyunca her temp: anahtarını kaldırır.

Açıklanmayan bir şey kaldı

two-days-ago bölümünde, kullanıcının ilk birkaç etkileşiminin bulunduğu yerde bir özet görüyordunuz. ADK, görüşme yeterince uzadığında bunu otomatik olarak yazdı.

Her bilginin bir raf ömrü vardır

5. Bağlamın Bozulması ve Sıkıştırılması

Özetler: Ayrıntıları Unutun

Bir görüşme çok uzadığında sistem, yer ve jeton tasarrufu için eski mesajları özetler.

Özetler, genel fikri anlamak ve jeton kullanımını azaltmak için iyi bir seçenektir.

Sıkıştırma, oturumda çalışır. Hafıza ayrı bir mağazadır

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın. Her adım aynı dünyada başlar: 8 gösteri, tüm koltuklar mevcut, saat 1x'te.

👉💻 Bu adımın koduna gidin:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 5 --force

Son adımdan bu yana değişenler:

Dosya

Ne değişti?

agent.py

Temsilci, sıkıştırma açıkken App içine yerleştirilir. İkinci bir aracı, budget_split, bilet aritmetiği yapar.

diğer her şey

değişmedi

agent.py artık root_agent ile bitmiyor. Şu ifadeyle sona erer:

app = App(
    name="concert",
    root_agent=root_agent,
    events_compaction_config=EventsCompactionConfig(
        compaction_interval=3,   # summarise after every 3 turns
        overlap_size=1,          # re-read 1 turn either side when summarising
    ),
)

adk web işlevinin önce app adlı bir değişkeni aradığını ve root_agent'ye geri döndüğünü unutmayın. Bir ajandan ziyade uygulamanın tamamı için geçerli olan bir şeye ihtiyacınız olduğunda (ör. burada sıkıştırma, sonraki adımlarda ise devam ettirilebilirlik) ajanı App ile sarmalayın.

compaction_interval=3, ADK'nın her üç dönüşten sonra özetleme yapacağı anlamına gelir. Bir etkileşim, sizin bir şey söylemeniz ve aracının yanıtını tamamlamasıdır. Bu sırada kaç araç çağrıldığı önemli değildir. Üç kişi az olduğundan atölye sırasında işe koyulur. Gerçek bir uygulama çok daha büyük bir sayı kullanır.

overlap_size=1, her özetin kesinlikle gerekenden bir tur önce başladığı anlamına gelir.

ADK, 1., 2. ve 3. dönüşleri özetliyor. Üç dönüş sonra, sonraki grubu özetler. overlap_size=1 ile ikinci özet 4. hamle yerine 3. hamleden itibaren tekrar başlıyor:

Bir dönüşümün çakıştığı iki özet

3. dönüşün iki kez özetlendiğini fark edebilirsiniz. Bu, bilinçli bir tercihtir. 3. turda bir soru sorduysanız ve temsilci 4. turda yanıtladıysa temiz bir bölme, soruyu bir özete, yanıtı ise diğer özete yerleştirir ve her ikisini de kafa karıştırıcı hale getirir. Bir dönüşü tekrarlamak, düşünceyi yarıda kesmeye karşı ucuz bir sigortadır.

Her iki alanı da ayarlayın. ADK'da ikisi için de varsayılan değer yoktur.

Sıkıştırma ve bellek hizmetiniz asla karşılaşmaz

İki kelime birbiriyle ilişkili gibi görünse de sistemler ilişkili olmadığından bu durumu açıkça belirtmek önemlidir.

EventsCompactionConfig, agent.py'deki App üzerinde root_agent'ın yanında yer alıyor. Bu, herhangi bir hizmetin değil, uygulamanın bir özelliğidir:

# agent/concert/agent.py
app = App(
    name="concert",
    root_agent=root_agent,
    events_compaction_config=EventsCompactionConfig(
        compaction_interval=3,
        overlap_size=1,
    ),
)

Özet, görüşmeyi yeniden yazar. Dosyayı yeniden yazmaz.

remember(), bu nedenle kasıtlı olarak çağrılır. Transkriptte kalan her şey, başka birinin yazdığı bir metnin parafrazi olarak okunur. Dosyaya yazılan her şey ise yarın ve gelecek hafta tam olarak kaydedildiği gibi okunur. Bu adımın sonunda, farkın nasıl sonuçlandığını izleyebilirsiniz.

Özet nerede saklanır?

sessions.db içinde, events tablosunda, diğer tüm etkileşim etkinliklerinde olduğu gibi bir zaman damgasıyla birlikte bir sıkıştırma kaydı, aracı tarafından bir etkinlik satırına yazılır.

Normal bir etkinlikte content alanı bulunur ve özet actions.compaction altında yer alır.

👉💻 Kendi veritabanınızda inceleyin:

cd ~/monstertix
. ./set_env.sh
sqlite3 sessions.db \
  "select json_extract(event_data,'\$.actions.compaction.compacted_content.parts[0].text')
   from events where event_data like '%compaction%';"

Etkinliğin şekli şu şekilde görünür:

events row
└── event_data  (JSON)
    ├── id, author, timestamp, invocation_id      ← like every other event
    ├── content                                    ← ABSENT on this one
    └── actions
        └── compaction
            ├── start_timestamp                    ← the window it replaced
            ├── end_timestamp
            └── compacted_content.parts[0].text    ← the summary itself

Etkinlik akışı, her etkinliği içeriğinden alır. Veritabanındaki özet ve her dönüşte modele gönderilen özet.

İkinci temsilci

budget_split bir araç değil, tamamen ayrı bir ajandır:

# agent/concert/agent.py
budget_split = Agent(
    name="budget_split",
    model=MODEL,
    include_contents="none",      # sees none of the conversation
    instruction="You do ticket arithmetic and nothing else...", #just the budget
    output_key="budget_plan",     # its answer goes into state
)

root_agent = Agent(
    ...,
    tools=[..., AgentTool(agent=budget_split)],
)

Neden düz bir işlev değil de aracı? Çünkü çalışmanın bir modele ihtiyacı var. "Bu bölümlerden hangileri dört kişilik 200 ABD doları bütçeye uygun ve toplam tutar ne kadar?" sorusunu, yargı içeren bir aritmetik işlem olarak değerlendir ve yanıtı okunabilir şekilde ver. Bir Python işlevinin çarpma işlemi yapmasına izin verin ve hangi seçeneklerden bahsetmeye değer olduğuna karar vermesini istemeyin.

O zaman neden ana temsilcinin yapmasına izin vermiyorsunuz? Sonraki satıra bakın. include_contents="none", bu temsilciye görüşmenin hiçbir bölümünün aktarılmadığı anlamına gelir. Grup sohbeti yok, tercihler yok, kırk konser tarihi yok. Yalnızca isteği alır. Bu işaret yalnızca Agent üzerinde bulunur. Bu nedenle, bu bir işlev değil, ajandır.

İsteği ve başka hiçbir şeyi görmeyen bir alt aracı

Kısa istemi tercih edin: Daha ucuz, daha hızlı ve konudan sapması çok daha zor. Yirmi tur önce söylenen bir şeyden etkilenmediği için aritmetiğe güvenin.

Neden?

AgentTool

? Ana ajan, diğer araçlar gibi bu aracı da çağırabilsin diye aracı bu araçla sarmalayın. Model açısından budget_split, search_events ile aynıdır.

Ve

output_key="budget_plan", yanıtı görüşmede serbest bırakmak yerine bu ad altında oturum durumuna yerleştirir. Bunun neden önemli olduğunu öğrenin.

Uygulayın

👉💻 Terminal 1'de Ctrl-C tuşlarına basın ve aynı işaretlerle yeniden başlatın:

cd ~/monstertix
adk web agent --port 8000 --allow_origins "*" \
  --session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db" \
  --artifact_service_uri="file://$WORKSHOP/artifacts"

👉 Yeni bir oturum başlatın ve gerçek bir sohbet edin. Beş veya altı dönüş. Bu komut çalışır:

hello
what's on in Amsterdam?
Sam is coming with us, and he can't do weeknights
there are 4 of us and I can spend about $200 a head. What fits?

👉 Durum sekmesini açın.

Aritmetik ajanın tam yanıtını içeren budget_plan simgesini bulun.

İlk bütçe sorusundan sonraki budget_plan

👉✨ Şimdi üzerindeki sayıları değiştirin:

what if 10 people come and I can only do $120 each?

👉 budget_plan bölümüne tekrar bakın. Bu değer, eklenmemiş, üzerine yazılmıştır. Bunun nedeni, output_key adlı alanın en yeni sürümü kullanmasıdır.

İkinci bütçe sorusundan sonraki budget_plan

Göremediğiniz içerik: Özet

Bu dönüşlerden birinde ADK, önceki dönüşleri özetledi.

👉💻 Doğrudan okumak için yeni bir Terminal'de çalıştırın:

cd ~/monstertix
. ./set_env.sh
./show-compaction.sh

Yazdıklarını okuma

Kısaltılmış gerçek bir örnek:

── summary 1 ──────────────────────────────────────────────────────

Conversation Language: English

  **User Request:**
  The user initially wanted to know what concerts are playing in Amsterdam.
  Subsequently, the user provided a constraint regarding a companion, Sam.

  **Context Summary:**
  The user is planning a concert trip. The agent identified two events in
  Amsterdam: "The Midnight Signal" at the Ziggo Dome on Tuesday, November
  10 and Saturday, November 14, 2026. The user then informed the agent that a
  companion named "Sam" will be joining, but "he can't do weeknights." The
  agent has successfully noted this constraint for Sam.

  **Key Decisions/Information Obtained:**
  *   **City of interest:** Amsterdam
  *   **Events found:** "The Midnight Signal" at the Ziggo Dome, Tuesday
  10 November and Saturday 14 November 2026.
  *   **Companion:** Sam
  *   **Companion constraint:** Sam cannot attend concerts on weeknights.

  **Unresolved Questions/Tasks:**
  *   The agent has not explicitly confirmed if the found event (which is
  on a Saturday) is suitable given Sam's constraint, nor has it asked the
  user for the next steps regarding this event or further event searches.

  **Tools Used:**
  *   `recall`
  *   `search_events`
  *   `note_companion`

Fiyatları, bölümleri, şehirleri ve tercihleri korudu ve henüz hiçbir şeye karar verilmediğini doğru bir şekilde belirtti.

Bu durumun üç sonucu vardır:

Yeniden ifade edilmiş bir metin. Burada kimsenin söylediği bir şey yok. Bu, söylenenlerin bir açıklamasıdır ve modelin zaman zaman yanlış yaptığı her şeyi üreten süreçle aynı türde bir süreçle üretilir.

Not: Özet dizeleri, dahili oturum etkinliği kayıtlarında depolanır ve ayrı kullanıcı arayüzü sekmeleri olarak doğrudan gösterilmez.

Artık kısmen önceki özetlerden oluşan bir sohbette her üç dönüşte bir

Kendinizi tekrar etmeden rezervasyon yapmayı deneyin

Yeni bir oturumda: Yeni bir oturum başlatmayın. Okuduğunuz özet, bu oturumun etkinliklerinde yer alır. Temsilcinin Amsterdam hakkında hâlâ bir şeyler bilmesinin tek nedeni budur.

👉✨ Yeni bir şey vermeyin:

Book us something.

Ajanın hangi gösteri, hangi bölüm ve kaç kişi olduğunu sorduğunu izleyin. Doğru olarak işaretleyin: Özetin kendisinde kullanıcının hiçbir şeyi onaylamadığı belirtiliyor. Bu nedenle, aracı onaylamasını istiyor. Sıkıştırma özelliğini kullanın.

Yoğunlaştırma başarılı olsa bile önceki dönüşümlerin özetlenmesi, ajanın kısıtlamaları kullanıcıyla yeniden onaylamasını gerektiren belirsizliklere yol açabilir.

👉✨ Soruyu yanıtlayın ve rezervasyonun tamamlanmasına izin verin:

Amsterdam Saturday, section C, 8 people

Herhangi bir harcama yapmadan önce siparişi tekrar okur:

"14 Kasım Cumartesi günü Amsterdam'da sahnelenecek The Midnight Signal oyunu için C bölümünden 8 bilet ayırtmak istiyorsunuz. Toplam maliyet 760 ABD doları olur. Bu biletleri satın alayım mı?"

👉✨ Onaylayın:

yes

Şimdi çalışıp çalışmadığı gösterilir:

"14 Kasım Cumartesi günü Amsterdam'da The Midnight Signal etkinliği için C bölümünden 8 bilet satın alma işleminiz tamamlandı. Toplam tutar 760 TL. Sipariş kimliğiniz ord_227877fe."

Gidip kontrol edin.

👉 Mekanın paneline geçin. Ajanın sözüne güvenmeyin.

Mekanın paneldeki sırası

Panelde

Görmeniz gerekenler

Satın alınan biletler

1 ve doğru: tek rezervasyon, tek sipariş

alttaki sipariş

ord_xxxxxxxx — 8× section C · 760

The tour, Amsterdam Saturday

C bölümü 8 puan düşüş gösteriyor

Activity

ord_xxxxxxxx — 8x section C @ 95 = 760

Bilet satışını öğrenmek için paneli kontrol edin.

user:prefs neden doğru seçimdir?

Sam'in kısıtlamasını note_companion yazma işlemini user:prefs son adıma taşıdığımızı ve bariz itirazı hatırlayın: Temsilci bunu hatırlayabilecekken neden bir araçla uğraşalım?

Nedenini açıklayalım. Durumu görüşmenin dışında tutun ve hiçbir özet buna dokunmasın. user:prefs ve budget_plan giriş şekillerine göre kontrol edin ve bunların değişmediğini görün. Ardından transkripti kontrol edin.

Özet, genel şekli korur ancak ayrıntıları kaybeder. Kaybetmeyi göze alamayacağınız her şeyi görüşme dışında bir yere kaydedin.

6. İşleme Devam Etme

40 Dakika Çevrimiçi Kalmadan Bekleme

Bir temsilcinin 40 dakikalık bir bilet kuyruğunda beklemesi gerektiğinde, bir Python sürecini canlı ve beklemede tutmak hem riskli hem de maliyetlidir.

Uzun bekleme sürelerini doğru şekilde yönetmek için:

  1. Durumu veritabanına kaydetme: İşlemin güvenli bir şekilde kapatılabilmesi için aracının duraklatıldığı yeri saklayın.
  2. İsteğe bağlı uyandırma: Bekleme süresi sona erdiğinde, harici bir sinyal aracıyı yeniden başlatır ve yürütmeye kaldığı yerden devam eder.

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın. Her adım aynı dünyada başlar: 8 gösteri, tüm koltuklar mevcut, saat 1x'te.

👉💻 Bu adımın koduna gidin:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 6 --force

Uygulayın

Son adımdan bu yana değişenler:

Dosya

Ne değişti?

tools.py

join_queue ve check_queue adlı kullanıcıları ekledi

agent.py

join_queue, LongRunningFunctionTool ile sarmalanmış ve App, ResumabilityConfig'ü açıyor

join_queue, engellemek yerine hemen biletle geri dönerken ResumabilityConfig, koşunun duraklatılıp daha sonra devam etmesine olanak tanır.

Devam ettirilebilirliğin avantajları

👉💻 Terminal 1: Aracı, değişmeden:

cd ~/monstertix
. ./set_env.sh
adk web agent --port 8000 --allow_origins "*" \
  --session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db" \
  --artifact_service_uri="file://$WORKSHOP/artifacts"

👉✨ Ajan'dan satın almasını isteyin:

Get us two tickets to the Amsterdam show.

recall, search_events ve join_queue'ı arayıp şuna benzer bir yanıt veriyor:

You are in the queue for The Midnight Signal at Ziggo Dome, Amsterdam, on Saturday, November 14. Your position is 14203.

Sizden istemeden Cumartesi'yi seçtiğini fark edeceksiniz. Amsterdam'da iki gösteri var. 17'si Salı günü ve son adımdaki bellek dosyasında Sam'in hafta içi geceleri çalışmadığı belirtiliyor. Bu, önceki bir rezervasyondan alınan ve bu rezervasyondaki bir soruya karar veren bir bilgidir.

Ayrıca, tek bir koltuğa veya fiyata bakmadan önce sıraya girdiğini ve ikisi hakkında da hiçbir şey söylemediğini unutmayın. Mekanın kuralı budur. Biletleri ön tarafa ulaşana kadar satın alırsınız. Bu nedenle, erken satın almanın tek avantajı sırada yerinizi almaktır. Hangi koltukları alacağımız kırk dakika sonraki bir soru.

👉 Kontrol paneline bakın. Banner'da şu ifade yer alıyor:

Sıra, sizden hiçbir şey çalışmıyorken bile devam ediyor

Agent is waiting in line — #14,203
About 2400s at 1×. Safe to kill the agent right now — it will still be here.

Sıra gerçekten kırk dakika sürüyor ve siz çalışırken sıra kaybolmuyor.

👉💻 Aracıyı sonlandırın.:

Ctrl-C

👉💻 Terminal 1'de tekrar başlatın:

cd ~/monstertix
. ./set_env.sh
adk web agent --port 8000 --allow_origins "*" \
  --session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db" \
  --artifact_service_uri="file://$WORKSHOP/artifacts"

👉✨ Aynı oturumda, ajana sıradaki yerini sorun:

Where am I in line?
You are currently at position 12982 in the queue. It is not your turn yet.

Yeniden başlatma işleminden sonra aynı bilet

Destek kaydını karşılaştırın ve hiçbir şey çalışmıyorken konumun düşmeye devam ettiğini görün. Ajanın öldürüldüğünü bilmediğine dikkat edin. Yeniden başlatmadan önce aldığı bilet diskte olduğu için check_queue numarası aranır.

👉💻 Nedenini öğrenin:

cd ~/monstertix
sqlite3 sessions.db "select name from pragma_table_info('events');"
id
app_name
user_id
session_id
invocation_id
timestamp
event_data

Her görüşmenin her dönüşünün bir satır olduğunu ve dönüşün kendisi, kimin söylediği, hangi aracın çağrıldığı, neyin geri geldiği ve notun event_data içinde JSON olduğunu unutmayın.

👉💻 Sıra numaranızı burada bulabilirsiniz:

sqlite3 sessions.db "select event_data from events;" \
  | grep -o '"ticket": "q_[a-z0-9]*", "position": [0-9]*' | tail -1
"ticket": "q_8cc2c6f9", "position": 14203

join_queue döndüğünde bileti doğrudan veritabanına kaydedilir. ADK, yeniden başlatıldıktan sonra önceki dönüşleri ve araç sonuçlarını diskten yeniden yükleyerek aracının kaldığı yerden devam etmesini sağlar.

Başlangıç yapmadığı noktaları fark edin. Temsilci, iş akışını baştan oynatmak yerine doğrudan devam eder. LongRunningFunctionTool ve ResumabilityConfig, yürütme durumunu diske kalıcı olarak kaydettiğinden ADK, aracının zaten sırada beklediğini bilir ve tur tarihlerini yeniden aramasını veya yinelenen bilet istemesini engeller.

Ölen bir süreçte aynı bilet

Neden join_queue değil de check_queue?

Her iki işlev de milisaniye cinsinden değer döndürür. Aradaki fark, işlev döndürdüğünde temel işin tamamlanıp tamamlanmadığıdır.

Döndürdüğü bilgi

iş tamamlandı mı?

join_queue

Sıra numarası ve ilk konum

Hayır. İşleme, harici sistemde eşzamansız olarak devam eder.

check_queue

Mevcut sıra durumu

Evet. Anında, belirli bir noktadan önceye ait yanıt döndürür.

LongRunningFunctionTool, bekleyen işlere yönelik bir işleyici döndüren işlemler için tasarlanmıştır. Sarma join_queue, harici sıra ilerlerken aracının yürütmeyi askıya almasına olanak tanır.

Buna karşılık, check_queue senkronize ve eksiksizdir. Sarmalama, aracı durumunu her kontrol ettiğinde yürütmeyi yanlışlıkla askıya almasına neden olur. Bu durum, aracı sıranın önüne geldiğinde ve satın alma işlemini hemen tamamlaması gerektiğinde bile geçerlidir.

Döndüğünde hangi çağrı hâlâ ödenmemiş durumda?

Başarının sırrı

Üç bileşen, yürütmenin askıya alınmasını ve kurtarılmasını sağlar:

# agent/concert/agent.py
LongRunningFunctionTool(func=join_queue)      # the tool returns without finishing
ResumabilityConfig(is_resumable=True)         # the run may pause and be picked up
--session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db"
  • LongRunningFunctionTool: Bir aracı engelleyici olmayan olarak işaretler ve etkin bir işleyici döndürür. Böylece aracı, iş parçacığını askıya almadan bekleyebilir.
  • ResumabilityConfig: Çalışma zamanına, tamamlanmamış bir görevde yürütme yaşam döngüsünü duraklatma ve son kaydedilen durumdan devam etme talimatı verir.
  • Session Storage URI (--session_service_uri): Durumun kalıcılığını işlem belleği yerine harici depolama alanına (ör. SQLite, Cloud SQL) yönlendirerek yeniden başlatma işlemleri sırasında veri kaybını önler.

7. Eski Verilerle İlgili İşlemler

Beklerken bilgiler güncelliğini kaybediyor

Kalıcı sohbet geçmişi, gerçek zamanlı durum değildir. Uzun bekleme aralıklarında, bilet müsaitliği veya fiyatlandırma gibi harici veriler, temsilcinin bilgisi olmadan değişebilir.

Bilgileri eksik olan bir temsilci, netleştirme istemek için duraklama yapar ancak güncel olmayan verileri olan bir temsilci, eski varsayımlara göre hemen harekete geçer. Geçersiz işlemlerin önlenmesi için zamana duyarlı verilerin, kritik işlemler yürütülmeden hemen önce yeniden doğrulanması gerekir.

Henüz yeni kodu yüklemeyin. Önceki adımın temsilcisinde kalın (bilet satın alabilir ancak şu anda güncelliğini yitirme veya tekdüzeliğe karşı koruma önlemleri yoktur).

👉🔴 MEKANI SIFIRLA'ya basın, ardından adk web'da yeni bir oturum başlatın. , sohbetin üzerindeki + Yeni Oturum düğmesidir.

Önceki sıra kaydını ve bekleyen satın alma durumunu temizlemek için yeni bir oturum başlatın. Bu adım, eski verilerin işlenmesinin düzgün şekilde gösterilmesi için temiz bir yürütme bağlamı gerektirir.

Anlık görüntü yalan söylüyor

👉✨ Daha önce izlediğiniz programı tekrar izlemek için:

Get us two tickets to the Amsterdam show.

👉✨ Şimdi, sıra beklerken koltuklar hakkında soru sorun:

How much are the seats, and what's still available?
Section A: $210 per ticket, with 400 seats available.
Section B: $145 per ticket, with 900 seats available.
Section C: $95 per ticket, with 1200 seats available.

👉🔴 İYİ KOLTUKLARI SAT'a basın. A bölümü sıfıra düşer.

👉🔴 Müşteri temsilcisini sıranın en önüne göndermek için BEKLEMEYİ ATLA'ya basın.

Sıranın başında

👉✨ Satın almasını söyleyin:

Buy the two section A seats.

Koltuk durumu yeniden doğrulanmadan purchase(section="A") yürütülür. Bu durum, mekan API'sinin hata yanıtı döndürmesine neden olur:

{"error": "sold_out", "available": 0,
 "message": "those seats are gone — re-fetch the seatmap before buying"}
Oh no! It looks like the Section A tickets just sold out. Let me get an
updated seat map to see what's still available.

Venue API hatası: A bölümü tükendi

Aracının, API hata yanıtı aldıktan sonra yalnızca koltuk planını yeniden sorguladığını unutmayın. Temel neden, eski bağlamdır: Oturumun başında aracı, "A bölümü: 400 adet mevcut" bilgisini kaydetmiş ve bu bilgi, modele aktarılan sohbet geçmişinde kalmıştır.

Eski bilgilerin nerede saklandığı konusunda net olun: Bu bilgiler, temp: gibi eyalet öneklerinin geçersiz kılamayacağı sohbet geçmişinde kalır.

Acentenin hâlâ üzerinde çalıştığı koltuk haritası

Düzeltme

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın. Her adım aynı dünyada başlar: 8 gösteri, tüm koltuklar mevcut, saat 1x'te.

👉💻 Bu adımın kodunu şimdi yükleyin:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 7 --force

👉💻 Terminal 1'de Ctrl-C tuşlarına basın ve yeniden başlatın:

cd ~/monstertix
. ./set_env.sh
adk web agent --port 8000 --allow_origins "*" \
  --session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db" \
  --artifact_service_uri="file://$WORKSHOP/artifacts"

Değişiklikler:

Dosya

Ne değişti?

fence.py

öğreniyorsunuz. Satın alma işleminden hemen önce çalışır ve işlemi engelleyebilir.

agent.py

before_tool_callback=refresh_before_purchase

tools.py

purchase artık Idempotency-Key üstbilgisi gönderiyor

fence.py, herhangi bir araç çağrısından hemen önce çalışan bir geri çağırma ADK'sıdır:

root_agent = Agent(
    ...,
    before_tool_callback=refresh_before_purchase,   # re-read inventory, then decide
)

İspat et

👉🔴 MEKANI SIFIRLAYIN. Sıraya girin, bekleme sırasında İYİ KOLTUKLARI SAT'a basın ve A bölümünü satın almasını söyleyin.

Bu kez kendi kendine durur. Aracının aşağıdakine benzer bir ifade söylediğini görün:

"A bölümü tükendi. Koltuk haritasını tekrar okumam gerekiyor."

Ardından, get_seatmap işlevinin tekrar çağrıldığını ve C bölümünü önerdiğini görün.

👉🔴 Mekanın etkinlik feed'ini dikkatlice okuyun. Çünkü bu feed'de her iki yarı da gösterilir:

error  purchase failed: Section A has 0 seats left, 2 requested. Re-read the
       seat map and pick again.
agent  calling purchase

Mekanın feed'i: deneme ve ret

Kontrol panelinde arama satın alma işlemi görünüyor ancak bilet sayısı sıfırda kalıyor. Bunun nedeni, araç çalıştırılmadan önce fence.py tarafından koruma sağlanmasıdır:

  1. Ajan, satın alma aracını çağırmaya çalışır.
  2. fence.py isteği ilk olarak alır ve canlı koltuk müsaitliğini kontrol eder.
  3. Koltukların tükendiğini gören fence.py, API çağrılmadan önce satın alma işlemini iptal ederek doğrudan bir hata döndürür.

İstem talimatları, LLM'nin tükenen koltukları satın almaya çalışmayacağını garanti edemez. Kod korumaları (before_tool_callback), kötü niyetli satın alma işlemlerinin hiçbir zaman gerçek dünyaya ulaşmamasını sağlar.

👉🔴 MEKANI SIFIRLA'ya, ardından SONRAKİ SATIN ALMAYI SONLANDIR'a basın.

👉✨ Satın almasını isteyin ve istediği kadar tekrar denemesine izin verin.

Yeniden deneme sayınızdan bağımsız olarak tek bir sipariş verilir.

Mekanın yeniden denemede aynı idempotency anahtarını alması ve yinelenen bir sipariş oluşturmak yerine orijinal onayı döndürmesi nedeniyle sipariş sayısı 1 olarak kalır.

8. Özerk İş Akışı

LLM'ler Her Zaman Katı Kurallara Uymakta Kötüdür

LLM'ler karar verme konusunda (ör. kullanıcı tercihlerine göre bir program seçme) çok iyidir. Ancak bir LLM'nin katı adım adım kurallara (ör. "koltukları kontrol etmeden önce her zaman sıraya girin") uymasını beklemek, bazen adımları atlayacağı veya sırayı karıştıracağı anlamına gelir.

Bu sorunu düzeltmek için iş akışını iki tür adıma ayırın:

  • Sabit Kurallar (Kod İşlevleri): Her seferinde tam olarak aynı şekilde gerçekleşmesi gereken adımlar (sıra numarası alma, pozisyon kontrolü) normal kod düğümleri olarak yazılır.
  • Kararlar (Aracı Düğümleri): Karar verilmesi gereken adımlar (koltuk bölümlerini seçme, kullanıcı tercihlerini kontrol etme) yapay zeka aracısına verilir.

Bu akışta gerçekte ne olduğuna bakın. Bazı bölümlerinde hiç yargılayıcı ifade yoktur:

Adım

Karar?

tam olarak bir sıra bileti alın

Hayır. Her zaman bir tane ve her zaman satın almadan önce

sıranın sizde olup olmadığını kontrol edin

Hayır. Soru sorun, yanıtı okuyun

alıcı için özet yazma

Hayır. Her seferinde aynı cümle, aynı şekil

hangi gösterim, hangi bölüm

yes. Fiyat, gün, kimin geleceği, neyi sevmediği

Kullanıcı tercihi ve sınırlaması

yes.

Sıkı prosedürler bir LLM'ye dayanmamalıdır. Çünkü olasılıksal modeller, izlenmediklerinde sonunda katı sıralamaları takip edemez.

ADK'nın Workflow özelliği, deterministik yürütmeyi yapay zeka muhakemesinden ayırır:

  • İşlev düğümleri, sabit kuralları (ör. sıraya katılma veya konumu kontrol etme) her zaman tutarlı bir şekilde uygular.
  • Aracı düğümleri, bağlamsal kararları (ör. kullanıcı tercihlerine göre koltuk seçme) işler.
  • Bekleme düğümleri, bir işlemi bellekte canlı tutmadan yürütmeyi askıya alır.

Orijinal ajan değiştirilmez. Araçlarını, güncelliğini yitirme kontrollerini ve tek seferliklik korumalarını koruyarak iş akışı grafiğindeki son düğüm haline gelir. Grafik, yürütme sırasını kontrol ederken aracı, satın alma kararını yürütür.

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın.

👉💻 Bu adımın koduna gidin:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 8 --force

Uygulayın

👉💻 Terminal 1'de Ctrl-C tuşlarına basın ve yeniden başlatın. Her zamanki gibi aynı komutu kullanın:

cd ~/monstertix
. ./set_env.sh
adk web agent --port 8000 --allow_origins "*" \
  --session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db" \
  --artifact_service_uri="file://$WORKSHOP/artifacts"

Son adımdan bu yana değişenler:

Dosya

Ne değişti?

nightly.py

öğreniyorsunuz. Katılımsız çalıştırma, ADK 2 Workflow grafiği olarak

agent.py

root_agent artık grafik olarak gösteriliyor. Konuştuğunuz temsilci, buyer_agent olarak yeniden adlandırıldı ve bu temsilcinin içinde bir düğüm olarak yer alıyor.

agent.py

Ayrıca join_queue ve check_queue değerlerini de kaybetti. Artık sıranın sahibi grafik.

Grafiği çalıştırma: Üç mesaj, sırayla

Gece Python'da aşağıdaki tüm işlemleri çalıştırın (Grafik burada bulunur). İş akışı grafiğinin durumlar arasındaki geçişini gözlemlemek için her dönüşte aynı girişi gönderin.

1. Şu anda kiminle konuştuğunuza dikkat edin

👉 ADK web arayüzünü yeniden yükleyin.

# concert/agent.py, at the bottom

buyer_agent = Agent(name="concert", ...)   # ← everything you built, renamed

from .nightly import nightly
root_agent = nightly                       # ← what adk web actually runs

adk web, root_agent'ye atanmış olan nesneyi yürütür. Burada root_agent, nightly iş akışı grafiğini gösterir. buyer_agent, grafikteki son düğüm olarak yapılandırılmıştır.

2. Başlatma

👉✨ Bunu yazıp Enter tuşuna basın:

go

Not: Otomatik iş akışı grafiğinde, ilk yürütme işlemi kullanıcı girişi beklenmeden başlar. adk web içinde mesaj göndermek ilk çalıştırmayı tetikler.

👉 Şimdi web tarayıcısına bakın. Sol panel, grafiği çizer ve düğümleri çalıştırırken renklendirir. Sağ panel, etkinlik akışıdır. Kararların nasıl alındığını izleyin:

Grafik, check_front konumunda durdurulmuş

queue_up'nın oturum durumuna queue_ticket ve queue_event_id yazdığını izleyin.

Grafik, check_front konumunda durdurulmuş

adk_request_input adlı bir işlev çağrısının yanıt beklediğini görürsünüz. Ardından sessizleşir ve koşu sona erer. Grafik check_front değerine ulaştı, sizi 14.203. sırada buldu ve durdu.

👉 Mekanın panelini kontrol edin: Bir sıra bileti ve hiçbir şey çalışmıyor.

Grafik, check_front konumunda park edilmiş

3. Cihazı uyandırın ve hangi kutuya yazdığınıza dikkat edin

Etkinlik akışını adk_request_input etkinliğine kaydırın ve söz konusu etkinlikle ilişkili giriş kutusunu bulun:

Grafik, check_front konumunda durdurulmuş

Artık iki farklı yere yazabilirsiniz ve bu yerler farklı işlevlere sahiptir.

Yazdığınız yerler

Ne olur?

adk_request_input etkinliğindeki Enter your response... kutusu

kesintiyi yanıtlar. Durdurulan çalıştırma, check_front tarihinden itibaren devam eder.

Alt kısımdaki ana sohbet kutusu

Yeni bir çağırma başlatır. Grafik START'dan itibaren yeniden oynatılıyor, tekrar bir program seçiliyor ve ikinci bir sıra bileti alınıyor.

👉✨ Enter your response... kutusuna istediğiniz metni yazıp oka basın.

👉 Terminal 1:

[check_front] #14,197 — not yet. Pausing.

Tek satır. İkinci bir gösteri seçmedi ve ikinci bir sıra bileti almadı. Bu durumda 14.203 kişilik bir sıranın en arkasında yer alacaktınız.

[open] ve [pick_show] simgelerinin tekrar göründüğünü fark ederseniz sohbet kutusuna yazmışsınızdır. MEKANI SIFIRLA'ya basın ve adımı baştan başlatın.

👉 Mekanın panelini tekrar kontrol edin: Hâlâ bir bilet var ve mekanın saati çalışmaya devam ederken sizinki çalışmadığı için konum biraz düşmüş.

Bir kez uyandırıldı ve hâlâ tek bilet

4. Öne geçin ve bitirin

👉🔴 Mekan panelinde BEKLEMEYİ ATLA'ya basın.

Tamamlanmış grafik, her düğüm yeşil

👉✨ En yeni adk_request_input etkinliğine son bir kez daha yanıt verin. Yanıtınızı yine sohbet kutusuna değil, kendi kutusuna girin. Tamamlanmış grafik, her düğüm yeşil

Bu kez kol saati check_front sizi önde bulup düşecek. Son iki düğüm olan sol panelin brief ve concert ışıklarını yakmasını izleyin.

Tamamlanmış grafik, her düğüm yeşil

👉 Tarayıcıya bakın. Bu, koşunun bitişidir ve okunmaya değer dört şey vardır.

Tamamlanmış grafik, her düğüm yeşil

Grafik tamamen yeşildir ve kenarlar sayılır. brief ve concert aydınlatıldı. pick_show, queue_up ve check_front kenarlarında artık rozet bulunuyor.

2x

, grafiğin bu kenarda yürüme sayısıdır (uyandırma başına bir kez). En üstteki 2, devam ettirme fiyatıdır: Planlayıcı, her seferinde grafiğe baştan girer. Bu, işin üç kez yapıldığı anlamına gelmez. Mekan paneli de bunun kanıtıdır: 2 değil, 1 sıra bileti. @node(rerun_on_resume=False) nedeniyle.

Grafik

Grafik: kurallar için fonksiyonlar, değerlendirme için temsilciler

# agent/concert/nightly.py
nightly = Workflow(
    name="concert_nightly",
    edges=[(START, open_the_night, pick_show, queue_up,
            check_front, brief, buyer_agent)],
)

İki düğümün aracı, ikisinin ise işlev olduğunu ve hangisinin ne olduğunu tasarım sorusunun tamamı olarak değerlendirin.

Düğüm

Neden bu şekil?

pick_show

agent. Bir kullanıcının geçmişinden program seçmek yargılayıcı bir davranıştır. recall numaralı telefonu arar, turu okur ve doğrulanmış bir Plan döndürür.

queue_up

işlevini kullanın. Sıraya katılmak bir kuraldır. Kural, saat 03:00'te yazı tura atılarak belirlenmemelidir.

check_front

işlevini kullanın. "Am I at the front" (Önde miyim?) sorusunun doğru bir cevabı var ve mekan bu cevaba sahip

buy_it

agent. Bu, buyer_agent sürümüdür. root_agent sürümünün değiştirilmemiş halidir. Bekleme sırasında A bölümü tükendi. B bölümünü mü, C bölümünü mü yoksa hiçbirini mi seçersiniz? Bu, 4. modülde öğretmek için zaman harcadığınız yargıdır.

Son düğüme bakın ve oluşturduğunuz aracıyı bulun. Kopya veya yeniden yazma değil:

# agent/concert/nightly.py
from .agent import buyer_agent

Tüm araçlar, fence.py içindeki koltuk planının yeniden okunması ve purchase içindeki idempotentlik anahtarı da dahil olmak üzere birlikte gelir. Yalnızca bir tane vardır.

Neden beklemek yerine durdu?

Kimsenin yazmaması gereken sürüm:

# what NOT to write — this is nobody's file
while not ready:          # never do this
    time.sleep(1)

Hiçbir şey yapmadan bir işlemi kırk dakika boyunca canlı tutar, terminalle birlikte sonlanır ve devam ettirilemez. check_front bunun yerine şunları yapar:

# agent/concert/nightly.py — check_front
if not status["ready"]:
    return RequestInput(message=f"Still at #{position}. Wake me and I'll check.")

RequestInput, kesme işlemidir. Grafik olduğu yerde durur, çağırma işlemi oturum deposuna gider ve işlem çıkış yapabilir.

Bekleme sırasında hiçbir maliyeti yoktur ve tam olarak tek bir yerde sona erer: satın alınan biletler. Bu duruma ulaşmak için tüm geceyi uyanarak geçirmeniz gerekebilir.

9. Gerçek bir kişiyle çalışma

Bir Kişiyle Tekrar Kontrol Etmeden Para Harcamayın

Gece boyunca çalışan bağımsız bir aracı, sohbet geçmişindeki tahmin edilen veya belirsiz tercihlere göre pahalı finansal seçimler yapmamalıdır.

Ajan, yüksek riskli işlemleri (ör. biletlere para harcama) gerçekleştirmeden önce duraklamalı, kullanıcının bütçesini yüksek sesle onaylamasını istemeli ve herhangi bir satın alma işlemi yapmadan önce bu açık onayı kaydetmelidir.

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın. Her adım aynı dünyada başlar: 8 gösteri, tüm koltuklar mevcut, saat 1x'te.

👉💻 Bu adımın koduna gidin:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 9 --force

Uygulayın

👉💻 Terminal 1'de Ctrl-C tuşlarına basın ve yeniden başlatın. Öncekiyle aynı komut:

cd ~/monstertix
. ./set_env.sh
adk web agent --port 8000 --allow_origins "*" \
  --session_service_uri="sqlite+aiosqlite:///$WORKSHOP/sessions.db" \
  --artifact_service_uri="file://$WORKSHOP/artifacts"

Son adımdan bu yana değişenler:

Dosya

Ne değişti?

budget.py

Kişinin söylediği cümleyi, kabul ettiği cümle olarak tutar. load() bunu eyalet dışından okur ve someone_is_there(), etrafta sorulabilecek biri olup olmadığını yanıtlar.

tools.py

eklendi set_budget. Böylece, bir kişi onayladıktan sonra ajan, oturum durumuna kabul edilen sınırı yazabilir.

nightly.py

Başka bir şey çalıştırılmadan önce soran ve geri okuyan yeni bir ilk düğüm agree_budget. Bütçeyi ve talebin geri kalanını hem serbest metin olarak hem de SEATS sabit olarak yakalar.

Bunu modellemeyin. Cümleyi saklayın.

agent/concert/budget.py simgesini açıp tamamını okuyun:

def load(state) -> str:
    """What they agreed, verbatim. Empty string means nobody has said."""
    return str(state.get("budget") or "")
state["budget"] = "up to $100 for the upper bowl or general admission, up to
                   $250 for the lower bowl, and up to $300 for a Saturday show"

Düğüm, iki şeyi kontrol eder: Kişinin bunu söylediğini ve onayladığını.

Ardından, cümleyi user: altında değil, oturum durumunda tutar. Bir rezervasyon için bütçe belirlenir. O gece, o grup hakkında "İyi olanlar için 250 dolar" denmişti ve bunun bilet rezervasyonuyla hiçbir ilgisi yok.

Diğer söyledikleri için de aynı durum geçerlidir.

Bütçeyle sınırlı kalmayın. Kullanıcılar karar verirken fikirlerini değiştirebilir. Bu nedenle, temsilcinin hatırladıklarına güvenmek yerine kullanıcının istediği her şeyi burada yakalayın.

"I live in NYC, I can only do weekends, I have 10 people coming with me"
# agent/concert/nightly.py
def what_they_asked_for(ctx) -> str:
    """Everything this person typed, joined, in their own words."""
    said = []
    for event in (ctx.session.events or []):
        if event.author != "user":
            continue
        for part in (event.content.parts if event.content else []) or []:
            text = (getattr(part, "text", None) or "").strip()
            if text and text not in said:
                said.append(text)
    return "\n".join(said)

Yalnızca kullanıcının oluşturduğu etkinlikler'i okuyun. Yalnızca

text

function_response'dan bölümler eklediğinizde bütçe soru ve cevabı burada tekrarlanmaz. Yinelenen öğe yok.

agree_budget, bu işlevi yalnızca ilk çalıştırmada değil, her çalıştırmada çağırdığı için aracı sıraya alırken yazılan düzeltmeler de uygulanır. Ardından, open_the_night seçiciye hem cümleyi hem de bütçeyi verin ve seçici bunları okusun.

Bir düğüm daha uzun olan grafik

Bu konuya bir sohbetle başlayalım:

# agent/concert/nightly.py
nightly = Workflow(
    name="concert_nightly",
    edges=[(START, agree_budget, open_the_night, pick_show, queue_up,
            check_front, brief, buyer_agent)],
)

Aynı grafik, önünde agree_budget ile

İki düğüm durup bekler ve aynı mekanizmadır. check_front bir mekan bekliyor ve saat tarafından yanıtlanıyor. agree_budget, bir kişiyi bekler ve yalnızca bir kişi tarafından yanıtlanabilir. Bu fark, 10. adımın tamamıdır.

Uygulama — adım adım

1. Başlatma

👉 ADK web kullanıcı arayüzünü yeniden yükleyin ve concert aracısını seçin.

👉✨ Gerçekten istediğiniz şeyi isteyin:

Get us two tickets to the Amsterdam show.

Koşu durdurulur ve ne kadar harcayacağınız sorulur.

8. adımda grafikte kullanıcı yoktu. Bu nedenle, yazdığınız her şey silindi ve go da her şey gibi davrandı. Burada agree_budget, ilk satırında what_they_asked_for(ctx) işlevini çağırır. Bu işlev, yazdığınız her şeyi toplayıp state["request"] içine yerleştirir. Ardından open_the_night, bütçeyle birlikte seçiciye teslim eder.

Bir gün adı belirtmediniz ve Amsterdam'da iki gösteri var. Bu nedenle, seçicinin recall()'ya geri döndüğünü görün, "Sam hafta içi akşamları sözünü tutmuyor"u bulun ve cumartesiyi seçin.

Tarayıcıda ise çalıştırma, adk_request_input etkinliğinde durdurulur:

Bir grafik size soru sordu. 8. adımda kuyrukta beklerken duyulan kesintinin aynısı. Ancak bu kez soruya yanıt verecek bir kişiye yönlendiriliyor.

2. Kullanıcıların gerçekte yanıt verdiği şekilde yanıt verin

👉✨ Alt kısımdaki sohbet kutusunda değil, Enter your response... kutusunda:

About a 100 for the cheap seats. 250 if they're the good ones.

Durdurulduğunu izleyin ve onaylayın:

[agree_budget] reading back: About a hundred for the cheap seats. Two-fifty
               if they're the good ones.

⚡ adk_request_input
   So: About a hundred for the cheap seats. Two-fifty if they're the good
   ones. Have I got that right?

Onaylamadan önce numarayı tekrar okur.

3. Onaylayın

👉✨ Aynı kutu:

Yes, that's right.
[agree_budget] agreed: About a hundred for the cheap seats. Two-fifty if
               they're the good ones.
[open]         Budget: About a hundred for the cheap seats. Two-fifty if
               they're the good ones.
[pick_show]    → {"section": "A", "reason": "..."}
[queue_up]     ms-ams-01 section A — ticket q_5924edc2 at #14,203
[check_front]  #14,203 — not yet. Pausing.

Dört düğüm tek bir kelimeyle çalıştı. 👉 Durum sekmesini açın ve budget (cümlelerinizin kelimesi kelimesine çevrilmiş hali) seçeneğini bulun.

Eyalet sekmesindeki bütçeyi kendi kelimelerinizle açıklayın.

Ön ek yok. Bu, oturum durumudur. Yeni Oturum başlattığınızda bütçe tükeneceği için bu kararı bilerek vermeniz gerekir. Bu durumda, sistem tekrar sorar.

4. Öne geçip yarışı bitirme

👉🔴 Mekan panelinde BEKLEMEYİ ATLA'ya basın.

👉✨ En yeni adk_request_input yanıtlayın. Bu yanıtı da kendi kutusuna girin:

go

Tekrar uyandırıldığında iki kez sormaz.

👉 Mekanın panelini kontrol edin: Bir sipariş, bu tür koltuk için kabul ettiğiniz fiyat aralığında.

Kabul ettiğiniz koşullar kapsamında tek bir sipariş

Grafiklerin kullanıcılara soru sorma şekli

nightly.py simgesini açıp agree_budget bölümünü okuyun. Üç şeyi birlikte doğru yapın ve bunlardan biri eksik olduğunda aynı hatayı bekleyin: Sonsuza kadar sorar.

@node(rerun_on_resume=True)              # 1. run again when woken
def agree_budget(node_input, ctx):
    answers = ctx.resume_inputs or {}    # 3. where the answer arrives

    said = answers.get(ASK_BUDGET)
    if not said:
        return RequestInput(
            interrupt_id=ASK_BUDGET,     # 2. STABLE id, not the default
            message="What are you willing to spend?",
        )
    ...

rerun_on_resume=True

Bu olmadan düğüm tamamlanmış olarak kabul edilir ve atlanır.

ahır interrupt_id

Varsayılan olarak her çalıştırmada yeni bir UUID oluşturulur. Bu nedenle, yanıtı arayabileceğiniz bir şey yoktur.

ctx.resume_inputs[id]

yanıtın nereye yönlendirileceğini belirler. RequestInput öğesinin dönüş değeri değil. ADK, bir düğümü göndermek yerine üzerinde yineleme yapar.

Grafik

Bu, 8. adımın grafiğidir ve önünde bir düğüm daha bulunur. Aynı grafik, önünde agree_budget ile

Kullanıcı tarafından onaylanan kısıtlamalar oturum kapsamlıdır ve görüşme sona erdiğinde geçerlilikleri sona erer. Geçici sınırların oturumlar arasında kalıcı hale getirilmesi, tek seferlik kararların istenmeyen kalıcı varsayılanlara dönüşme riskini doğurur.

10. Bulut Yığını

Dizüstü bilgisayarlar uyku moduna geçtiğinde yerel dosyalar kayboluyor

Dizüstü bilgisayarınızda bir aracı, bilgisayarınızın uyanık kalmasına, yerel SQLite veritabanlarına ve yerel dosyalara bağlıdır. Dizüstü bilgisayarınızın kapağını kapatırsanız veya yeniden başlatılan bir bulut kapsayıcısına dağıtım yaparsanız yerel dosyalar ve bellek kaybolur.

Üretimde güvenilir bir şekilde çalışmak için:

  1. Cloud Compute: Aracıyı, çevrimiçi kalan bir container'da (ör. Cloud Run) çalıştırın.
  2. Dayanıklı Cloud Veritabanları: Yerel dosyaları ve SQLite'ı bulut hizmetleriyle (oturum verileri için Cloud SQL, dosyalar için Cloud Storage) değiştirin.
  3. Cloud Timers: Dizüstü bilgisayarınız kapalıyken bile temsilcinin uyanması için yönetilen planlayıcıları (ör. Cloud Scheduler ve Pub/Sub) kullanın.

👉🔴 Mekanın panelinde MEKANI SIFIRLA'ya basın. Her adım aynı dünyada başlar: 8 gösteri, tüm koltuklar mevcut, saat 1x'te.

👉💻 Son koda gitmek için:

cd ~/monstertix
. ./set_env.sh
./use-solution.sh 10 --force

Dosya

Ne değişti?

main.py

öğreniyorsunuz. Sohbet sayfasına ve zamanlayıcının çağırdığı uç noktaya hizmet veren bir FastAPI

Dockerfile

öğreniyorsunuz. Ordinary python-slim ve uvicorn. ADK'ya özgü bir durum yoktur.

monstertix/

kopyalandığı için sayfa, aracıyla aynı kapsayıcıda gönderilir.

concert/

değişmedi. 9. adımı tamamladığınız klasörün bire bir kopyası

Daha önce oluşturduğunuz sayfa

** ADK web kullanıcı arayüzü**, geliştirme sırasında durumu, etkinlik akışlarını ve iş akışı grafiklerini incelemek için kullanılan tamamen yerel bir hata ayıklama arayüzüdür. Üretim dağıtımı için paketlenmemiştir.

Üretim için 3. adımda oluşturulan bağımsız sohbet ön ucunu kullanın. Dahili hata ayıklama kontrollerini kullanıma sunmadan /wake uç noktası üzerinden kullanıcı mesajlaşma ve etkileşim kesintilerini yönetir.

Yerel durumu bulut hizmetlerine taşıma

Sunucuyu durdurun ve yazdıklarına bakın:

sessions.db          a SQLite file next to the code
memory/userx.md      a markdown file you can open in an editor
artifacts/           seat maps written to a folder

Yerel dosyalar geliştirme için uygun olsa da Cloud Run kapsayıcıları geçici, örneğe özel depolama alanı kullandığından üretimde başarısız olur.

Cloud Run'da çalışırken:

  • Örnekler sıfıra ölçeklenir: Trafik durduğunda kapsayıcı sonlandırılır ve yerel dosya sistemi silinir.
  • Örnekler bağımsız olarak ölçeği genişletilir: Eşzamanlı birden fazla örnek disk alanını paylaşmaz, her biri kendi yalıtılmış dosya sistemiyle çalışır.

Bir temsilci, sıra biletini veya oturum durumunu yerel bir SQLite dosyasına (sessions.db) kaydederse yeni ya da farklı bir kapsayıcı örneğine yönlendirilen sonraki istekler boş bir veritabanıyla karşılaşır. Ölçeklendirme ve yeniden başlatma işlemleri sırasında kalıcı durumu korumak için yerel dosyalar yönetilen harici hizmetlerle değiştirilmelidir.

Bunun yerine, herhangi bir kapsayıcıdan daha uzun süre dayanan bir duruma ihtiyacınız vardır. Her dosya için bir tane olmak üzere üç hizmet:

Dizüstü bilgisayarınızda

Bulutta

Nedir?

sessions.db

Cloud SQL

Yönetilen bir Postgres veritabanı. Herhangi bir şey çalışıyor olsun veya olmasın bu durum geçerlidir.

memory/userx.md

Cloud Storage

paket. Nesne olarak dosyalar, her örneğe aynı anda ulaşılabilir

artifacts/

Cloud Storage

aynı paket

clock.py

Cloud Scheduler

Google'ın çalıştırdığı bir cron. Terminaliniz çalar saat değildir

Oluşturma

PostgreSQL veritabanı, veritabanı kullanıcısı ve Cloud Storage paketi sağlama. Cloud SQL örneği oluşturma işleminin birkaç dakika sürdüğünü ve setup.sh sırasında arka planda başlatıldığını unutmayın.

👉💻 Toplayın ve geri kalanını yapın:

cd ~/monstertix
./setup-cloud-state.sh
→ instance  RUNNABLE
→ database  adk created
→ user      adk created
→ bucket    created
→ memory    memory/userx.md → gs://your-project-agent-you/memory/
✓ durable state ready. Written to .env

Bu komut dosyası, yukarıdaki tablonun her satırı için bir öğe olmak üzere dört öğe oluşturdu ve .env içine iki URI yazdı:

CLOUD_SESSION_SERVICE_URI=postgresql+asyncpg://adk:...@/adk?host=/cloudsql/proj:region:instance
CLOUD_ARTIFACT_SERVICE_URI=gs://your-project-agent-you/artifacts

Aracıyı yönetilen planlanmış tetikleyicilerle dağıtma

Hizmetler hazır olduğunda aracıyı kalıcı evine yerleştirin.

👉💻 Üç veya dört dakika. Başlatın ve okumaya devam edin:

cd ~/monstertix
. ./set_env.sh
./deploy-agent.sh

Bu komut dosyası, agent/ kaynağından bir container görüntüsü oluşturur, bunu Cloud Run'a dağıtır ve destekleyici olay odaklı altyapıyı sağlar.

Tam dağıtım beş farklı bileşenden oluşur:

1. gcloud run deploy       the container: your agent, the graph, and the page
2. a Pub/Sub topic         a queue for messages
3. a service account       an identity, so the message is allowed in
4. a push subscription     topic ──► /trigger/wake
5. a Cloud Scheduler job   the alarm clock, on a cron

Tetikleyici Kaynağı olarak Cloud Scheduler

Cloud Scheduler, yerel yürütme zamanlayıcılarını (clock.py) yerel terminal oturumlarından bağımsız olarak devam eden, otomatik yeniden denemeleri işleyen ve saat dilimi planlamasını destekleyen yönetilen bir cron hizmetiyle değiştirir.

Mesaj ayırma katmanı olarak Pub/Sub

Cloud Scheduler ile Cloud Run arasına bir Pub/Sub konusu yerleştirmek üç mimari avantaj sağlar:

  • Mesaj dayanıklılığı: Cloud Run hizmeti soğuk başlatılıyorsa, yeniden dağıtılıyorsa veya geçici olarak kullanılamıyorsa gelen tetikleyici etkinlikleri sıraya alır.
  • Yerleşik Yeniden Deneme Politikaları: Özel uygulama düzeyinde yeniden deneme mantığı gerektirmeden geçici teslimat hatalarını otomatik olarak yeniden dener.
  • Fan-Out Ölçeklenebilirliği: Planlayıcıyı yeniden yapılandırmadan birden fazla aşağı akış hizmetinin aynı planlanmış etkinliklere abone olmasını sağlar.

alt text

trigger_sources=["pubsub"] yapılandırması, standart Pub/Sub zarf yükleriyle uyumlu bir anında iletme uç noktası kaydeder ve gelen mesajları doğrudan aracı çağırma etkinliklerine çevirir.

→ agent    https://concert-you-xxxx.run.app
→ topic    presale-you created
→ iam      concert-trigger@your-project... may invoke concert-you
→ push     → https://concert-you-xxxx.run.app/trigger/wake
→ schedule 0 3 * * *

Konuşarak halledin

👉 Aracının URL'sini bir tarayıcıda açın. 👉✨ Rezervasyon yapmasını isteyin:

Book me 3 tickets for The Midnight Signal in Tokyo. Best seats in the house,
I don't want the cheap ones.

Ne kadar harcamak istediğinizi sormalıdır

👉✨ Yanıtınızı yüksek sesle verin, ardından onaylayın:

up to 250 a seat for the good ones

Çalıştırma şu anda sıraya alınmış durumda.

👉🔴 Mesaj geldiğinde sıranın hazır olması için mekan panelinde BEKLEMEYİ ATLA'ya basın.

Gerçek bir zamanlamada uygulamak için işin etkinleştirilmesi gerekir. jobs run, Job.state must be ENABLED ile duraklatılmış bir işi reddeder:

gcloud scheduler jobs resume presale-$(gcloud config get-value account | cut -d@ -f1) \
  --location=$GOOGLE_CLOUD_REGION

👉 Birkaç saniye içinde, ajan satın alma işlemini onaylanan bütçe sınırları içinde gerçekleştirir ve otomatik olarak sipariş onayı döndürür.

👉🔴 Mekan panelini kontrol edin. Bir sipariş.

Bunu 5 dakikada bir çalışacak şekilde ayarlamak için:

gcloud scheduler jobs update pubsub presale-$(gcloud config get-value account | cut -d@ -f1) \
  --location=$GOOGLE_CLOUD_REGION --schedule="*/5 * * * *"

Temizleme

İki öğe dağıtılır: mekan ve yanındaki ajan. Her ikisi de sıfıra ölçeklendirildiğinden boşta kalan işlev için ödeme alınmaz. Yine de silin.

./destroy-agent.sh          # service, topic, subscription, scheduler job
./destroy-venue.sh

Verilerinizi barındırdıkları ve her ikisi de faturalandırmaya devam ettiği için Cloud SQL ve paketi bilerek olduğu gibi bırakın. İşlemi tamamladığınızdan eminseniz:

./destroy-agent.sh --all

Diğer her şey yereldir. İşiniz bittiğinde klasörü silebilir veya klasörü saklayıp istediğiniz süre boyunca kendi projenizde çalıştırabilirsiniz.

Aracı, araçları, geri aramaları ve bütçe değişmedi. İki URI ve bir ortam değişkeni bunu yaptı.

11. Sisteme Genel Bakış ve Sorun Giderme Kılavuzu

Ortam Farklılıkları: Yerel ve Bulut Mimarisi

Dizüstü bilgisayarınız

Bulutta

Ne değişti?

--session_service_uri=sqlite+aiosqlite:///...

Cloud SQL

bağlantı dizesi

--artifact_service_uri=file://...

gs://bucket

URI

memory/userx.md

gs://bucket/memory/

bir yol

adk web

bir Cloud Run hizmeti

Dockerfile ve main.py

monstertix/clock.py

Cloud Scheduler → Pub/Sub

trigger_sources=["pubsub"]

siz, klavyenin başında

someone_is_there()

varsayılan olarak ayarlanır ve istek başına geçersiz kılınır

root_agent, araç tanımları, geri çağırma korumaları ve bütçe uygulaması dahil olmak üzere temel aracı uygulaması tamamen aynı kalır. solutions/step10_deploy/concert/ ile 9. adımın karşılaştırılması, tam olarak bayt bayt eşleşme olduğunu göstererek ADK'nın hizmet soyutlamasının altyapı yapılandırmasını aracı mantığından nasıl ayırdığını doğrular.

Takılı kaldıysanız

Herhangi bir adımda tam çalışma koduna ulaşın:

./use-solution.sh N --force          # any step. resets memory + sessions + artifacts
./use-solution.sh N --force --keep   # code only, leave state alone

Bir şey bozulduğunda

Belirti

Düzelt

✗ Cannot access project

Yanlış kimlik. rm ~/project_id.txt && ./setup.sh

Model 404 döndürüyor

-latest takma adları yalnızca AI Studio'da kullanılabilir. ADK_MODEL=gemini-latest-flash kullanın

address already in use

Bir şey zaten 8000 veya 8090 bağlantı noktasına sahip. lsof -ti:8000 | xargs kill -9

Temsilci, mekana ulaşamıyor

curl $VENUE_URL/health veya tekrar ./deploy-venue.sh

Sıra ilerlemiyor

BEKLEMEYİ ATLA'ya basın. 1× hızında sıra kendi kendine öne ulaşmaz

Panelde ikinci bir sıra numarası görünür.

adk_request_input etkinliğindeki Enter your response... kutusu yerine sohbet kutusunda yanıt verdiniz. Sohbet kutusu yeni bir çalıştırma başlatır.

Açılır liste boş veya uygulama yüklenmiyor

adk web, agent/ altındaki her klasörü uygulama olarak değerlendirir ve bir klasör uygulama değilse listenin tamamı başarısız olur. ls agent/ yalnızca concert/ öğesini göstermelidir. agent/ içinden bir şeyler çalıştırmak, geride memory/ gibi bir öğe bırakabilir. ./use-solution.sh 8 --force düzenler

[EXPERIMENTAL] uyarı

Beklenen. EventsCompactionConfig ve ResumabilityConfig, 2.6.2 sürümünde GKST öncesi aşamadadır.

Her şey garip

MEKANI SIFIRLA'ya ve ardından rm sessions.db simgesine basın.

Bellek dosyası, yinelenen rezervasyonlarla dolu

remember() her çalıştırmada eklenir. ./use-solution.sh N --force sıfırlanır veya cp seed/memory/default.md memory/userx.md

adk: command not found

source .venv/bin/activate: Her yeni sekme için gereklidir.

Cloud Shell'in zaman aşımı

adk web uygulamasını yeniden başlatın. sessions.db kurtuldu. Bu, 6. adımdır ve ücretsiz olarak gerçekleşir.