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?

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 |
| |
oturum açma | Henüz kimlik bilgileriniz yoksa | |
project |
| |
API'ler | atölyenin ihtiyaç duyduğu temayı etkinleştirir. | |
| Projenizi, bölgenizi ve model kimliğinizi yazar. Böylece her komut dosyası ve | |
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.

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? |
| Denetim masası, düğmeleriniz. |
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.

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? |
| aracı: ad, model, talimat, araç listesi |
|
|
| mekan için bir HTTP istemcisi. İlgi çekici bir şey yok |
| 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ü |
|
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:

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? |
| öğreniyorsunuz. Arkasında Runner bulunan bir web sunucusu |
| öğreniyorsunuz. Zamanı bilen ve sunucuyu çağıran bir şey |
İki yarıdan oluşur ve buluta kadar iki dosyada kalır: 
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.

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 |
|
+ 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? |
|
|
| öğreniyorsunuz. Kendi anı mağazamız |
| 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? |
| ADK, her mesajı, araç çağrısını ve durum değişikliğini satır olarak (SQLite veritabanı) yazar. |
| 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ç

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 |
| RAM. İşlemi durdurduğunuzda kaybolur. Herhangi bir işaret iletmezseniz bu varsayılan değerdir. |
| diskteki bir klasör. Şu anda kullandığınız |
| 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

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 |
|
| Hiçbiri. İşlemi durdurduğunuzda her şeyi kaybeder. |
|
| Vertex AI RAG korpusu |
|
| 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 |
|
|
çıktı |
|
|
bellek | komut satırında hiçbir yerde |
|
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.

Show me the seat map for the Amsterdam Saturday show.

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 |
|
|
2 | Tam koltuk haritası, 6 KB |
| yapı hizmeti |
3 | Tek satırlık koltuk planı özeti |
| hiçbir yerde |
👉 Şimdi kontrol edin. Durum sekmesi ve Öğeler sekmesi, adk web simgesinin solunda yer alır.


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.

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

👉 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ı? |
| no | hayır | hayır |
| evet | evet | hayır |
| evet | evet | yes |
| 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.

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 |
| oturumla ilgili | oturum silinene kadar |
| kullanıcıya karşı | Kullanıcının tüm oturumlarında |
| uygulamaya karşı | her kullanıcıda |
| 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.

👉🔴 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? |
| Temsilci, sıkıştırma açıkken |
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:

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.

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.

👉✨ Ş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.

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.

Panelde | Görmeniz gerekenler |
Satın alınan biletler |
|
alttaki sipariş |
|
The tour, Amsterdam Saturday | C bölümü 8 puan düşüş gösteriyor |
Activity |
|
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:
- Durumu veritabanına kaydetme: İşlemin güvenli bir şekilde kapatılabilmesi için aracının duraklatıldığı yeri saklayın.
- İ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? |
|
|
|
|
join_queue, engellemek yerine hemen biletle geri dönerken ResumabilityConfig, koşunun duraklatılıp daha sonra devam etmesine olanak tanır.

👉💻 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:

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.

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.

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ı? | |
| Sıra numarası ve ilk konum | Hayır. İşleme, harici sistemde eşzamansız olarak devam eder. |
| 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.

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.

👉✨ 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.

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.

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? |
| öğreniyorsunuz. Satın alma işleminden hemen önce çalışır ve işlemi engelleyebilir. |
|
|
|
|
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

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:
- Ajan, satın alma aracını çağırmaya çalışır.
fence.pyisteği ilk olarak alır ve canlı koltuk müsaitliğini kontrol eder.- 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.

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? |
| öğreniyorsunuz. Katılımsız çalıştırma, ADK 2 |
|
|
| Ayrıca |
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:

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

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.

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:

Artık iki farklı yere yazabilirsiniz ve bu yerler farklı işlevlere sahiptir.
Yazdığınız yerler | Ne olur? |
| kesintiyi yanıtlar. Durdurulan çalıştırma, |
Alt kısımdaki ana sohbet kutusu | Yeni bir çağırma başlatır. Grafik |
👉✨ 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üş.

4. Öne geçin ve bitirin
👉🔴 Mekan panelinde BEKLEMEYİ ATLA'ya basın.

👉✨ 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. 
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.

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

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

# 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? |
| agent. Bir kullanıcının geçmişinden program seçmek yargılayıcı bir davranıştır. |
| işlevini kullanın. Sıraya katılmak bir kuraldır. Kural, saat 03:00'te yazı tura atılarak belirlenmemelidir. |
| işlevini kullanın. "Am I at the front" (Önde miyim?) sorusunun doğru bir cevabı var ve mekan bu cevaba sahip |
| agent. Bu, |
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? |
| Kişinin söylediği cümleyi, kabul ettiği cümle olarak tutar. |
| eklendi |
| Başka bir şey çalıştırılmadan önce soran ve geri okuyan yeni bir ilk düğüm |
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)],
)

İ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.

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?

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.

Ö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

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

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?",
)
...
| Bu olmadan düğüm tamamlanmış olarak kabul edilir ve atlanır. | |
ahır | Varsayılan olarak her çalıştırmada yeni bir UUID oluşturulur. Bu nedenle, yanıtı arayabileceğiniz bir şey yoktur. | |
| yanıtın nereye yönlendirileceğini belirler. | |
Grafik
Bu, 8. adımın grafiğidir ve önünde bir düğüm daha bulunur. 
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:
- Cloud Compute: Aracıyı, çevrimiçi kalan bir container'da (ör. Cloud Run) çalıştırın.
- Dayanıklı Cloud Veritabanları: Yerel dosyaları ve SQLite'ı bulut hizmetleriyle (oturum verileri için Cloud SQL, dosyalar için Cloud Storage) değiştirin.
- 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? |
| öğreniyorsunuz. Sohbet sayfasına ve zamanlayıcının çağırdığı uç noktaya hizmet veren bir FastAPI |
| öğreniyorsunuz. Ordinary python-slim ve uvicorn. ADK'ya özgü bir durum yoktur. |
| kopyalandığı için sayfa, aracıyla aynı kapsayıcıda gönderilir. |
| 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? |
| Cloud SQL | Yönetilen bir Postgres veritabanı. Herhangi bir şey çalışıyor olsun veya olmasın bu durum geçerlidir. |
| Cloud Storage | paket. Nesne olarak dosyalar, her örneğe aynı anda ulaşılabilir |
| Cloud Storage | aynı paket |
| 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.

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? |
| Cloud SQL | bağlantı dizesi |
|
| URI |
|
| bir yol |
| bir Cloud Run hizmeti |
|
| Cloud Scheduler → Pub/Sub |
|
siz, klavyenin başında |
| 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 |
| Yanlış kimlik. |
Model 404 döndürüyor |
|
| Bir şey zaten 8000 veya 8090 bağlantı noktasına sahip. |
Temsilci, mekana ulaşamıyor |
|
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. |
|
Açılır liste boş veya uygulama yüklenmiyor |
|
| Beklenen. |
Her şey garip | MEKANI SIFIRLA'ya ve ardından |
Bellek dosyası, yinelenen rezervasyonlarla dolu |
|
|
|
Cloud Shell'in zaman aşımı |
|