Ana sayfa → Teknik
Agent İki Kez Çağırdı: Otonomi Değil, Idempotency
Cuma 16:20, test ortamı. Log’da aynı satır iki kez:
cekim_onayla(WD-24817). İkisi de başarılı. Sebep üç satır yukarıdaydı: tool
32 saniyede döndü, bizim zaman aşımımız 30 saniye. Agent
cevabı alamadı, “olmamış” diye düşündü ve tekrar denedi. Oysa olmuştu.
Test ortamında iki kayıt, 1.250 $. Canlıda aynı log, çift ödeme demek.
- Agent eklemek, sisteme at-least-once bir çağırıcı eklemektir. Model belirsizlikte tekrar dener; bu bir hata değil, davranış. Tasarımın buna göre yapılması gerekir.
- Tekrarın üç kaynağı var: zaman aşımı, anlaşılmayan hata, yarıda kalıp yeniden başlayan koşu. Üçü de aynı sonucu verir: aynı iş iki kez.
- Anahtarı model üretmez, kod üretir. Doğal anahtardan hash: kayıt no + işlem türü + iş günü. Model anahtar uydurursa iki çağrıda iki anahtar olur ve koruma çalışmaz.
- İkinci çağrı hata değil, ilk sonucun kendisini döndürür. Hata dönersen model üçüncü kez dener. “Zaten yapıldı, sonucu bu” cevabı döngüyü bitirir.
- Geri alınamaz işler iki aşamalıdır: agent plan üretir, kuyruğa yazılır, onaydan sonra kod çalıştırır. Onay akışta durur; prompt’taki “önce sor” cümlesi güvence değildir.
- Denetim log’u pazarlık konusu değil. Kim, hangi koşuda, hangi anahtarla, hangi argümanlarla. Bu olmadan çift onayı iki gün sonra bulamazsın.
Neden tekrar ediyor?
Mesaj kaç kez gelir yazısındaki mesele burada aynen geçerli: ağ üzerinden yapılan bir çağrının cevabı gelmediğinde, çağrının yapılmadığını bilemezsin. İki ihtimal vardır ve ikisi de aynı görünür: istek gitmedi, ya da istek gitti cevap dönmedi. Agent’ta bu klasik problemin üstüne bir katman daha biniyor: karar veren taraf bir model ve model, emin olmadığında tekrar denemeye meyilli.
| Tekrarın kaynağı | Nasıl görünür | Savunma |
|---|---|---|
| Zaman aşımı | Tool aslında çalıştı, cevap geç geldi | Idempotency anahtarı + sunucuda tekil kayıt |
| Anlaşılmayan hata | “500” gören model aynı çağrıyı tekrarlar | Ne yapacağını söyleyen hata mesajı |
| Modelin kararsızlığı | Aynı tool, aynı argüman, arka arkaya | Tekrar koruması: 2. çağrı önbellekten, 3. çağrı hata |
| Koşunun yeniden başlaması | Tamamlanmış adım yeniden çalışır | Koşu durumu kaydı: adım tamamlandıysa atla |
| Kullanıcı tekrarı | Aynı gece işi iki kez tetiklenir | Koşu anahtarı: tarih + iş adı tekildir |
Sahadan: 32 saniyelik onay
O cumanın dökümü. Agent, gece biriken 6 çekim için “kural setine uyanları onayla” görevini çalıştırıyordu (test ortamı, gerçek para yok).
| Saat | Olay |
|---|---|
| 16:19:41 | cekim_onayla(WD-24817) çağrıldı |
| 16:20:11 | Agent tarafında 30 sn zaman aşımı; çağrı iptal edildi, model “hata” gördü |
| 16:20:13 | Ödeme servisi işlemi tamamladı: onay kaydedildi (32 sn sürmüş) |
| 16:20:14 | Model aynı tool’u aynı argümanla tekrar çağırdı |
| 16:20:16 | İkinci onay kaydedildi. Kayıt iki kez onaylı |
| 16:24 | Koşu “6 çekim onaylandı” diye bitti; aslında 5 çekim, biri iki kez |
Burada modelin yaptığı şey yanlış değil: cevap gelmedi, tekrar denedi. Ben olsam ben de denerdim. Yanlış olan, aynı işin ikinci kez kabul edilmesiydi. Bunu bir modelin “dikkatli olması”na bırakmak, iki thread’li bir yarışı “dikkatli kod”a bırakmak gibi: bir süre işe yarar, sonra yaramaz.
Anahtarı kim üretir?
İlk düzeltme denememiz şuydu: tool şemasına idempotency_key alanı ekledik, model
doldursun. İki koşuda iki farklı anahtar üretti — biri wd-24817-onay, diğeri
WD24817_approve_2. Koruma çalışmadı; sadece veritabanında iki satır oldu.
Doğrusu: anahtar modele hiç gösterilmez. Orchestrator, işin doğal anahtarından üretir ve çağrıya kendisi ekler.
# dogal anahtar: bu isi benzersiz yapan alanlar
def idempotency_key(tool_adi, argumanlar, is_gunu):
dogal = "|".join([
tool_adi, # cekim_onayla
argumanlar["kayit_no"], # WD-24817
is_gunu.isoformat(), # 2026-09-20
])
return sha256(dogal).hexdigest()[:32]
# ayni is -> ayni anahtar; farkli is -> farkli anahtar
# model bu alani ne gorur ne de doldurur
İş günü neden var? Aynı kaydı bir hafta sonra kasten yeniden onaylamak isteyebiliriz; anahtar sonsuza kadar aynı kalırsa meşru ikinci işlem de engellenir. Pencereyi işin doğasına göre seç: bizde bir iş günü doğru cevaptı.
Sunucu tarafı: ikinci çağrıya ne dönmeli?
Anahtar tek başına bir şey yapmaz; onu kabul eden tarafın davranışı önemli. Teslim garantileri yazısındaki kuralın aynısı:
def cekim_onayla(kayit_no, _key):
kayitli = idempotency_tablosu.bul(_key)
if kayitli:
# ONEMLI: hata degil, ilk sonucun kendisi
return {**kayitli.sonuc, "tekrar": True}
with transaction():
# unique index: ayni anda gelen iki istekten biri duser
idempotency_tablosu.ekle(_key, durum="calisiyor")
sonuc = odeme_servisi.onayla(kayit_no)
idempotency_tablosu.tamamla(_key, sonuc)
return sonuc
İki ayrıntı acıyla öğrenildi:
- İkinci çağrıya hata dönme. İlk denememizde
409 Conflictdönüyorduk; model bunu “olmadı” diye okuyup üçüncü kez denedi, sonra da kullanıcıya “onaylayamadım” dedi. Oysa onaylanmıştı. Doğru cevap ilk sonucun kendisidir;"tekrar": truealanı yalnızca log için. - Tekillik veritabanında olmalı. “Önce bak, yoksa ekle” kodu iki eş zamanlı çağrıda ikisini de geçirir. Unique index olmadan bu bir race condition’dır; paralel alt agent’lar varsa çok daha olasıdır.
Geri alınamaz işler: iki aşama
Idempotency, “iki kez yapılmasın”ı çözer. “Hiç yapılmamalıydı”yı çözmez. Para gönderme, mail atma, kayıt silme, dış sisteme çağrı — bunlar için tool’u ikiye böldük:
# 1. asama: agent yalnizca plan uretir (yan etki yok)
cekim_onayla_plan(kayit_no)
# -> {"kayit_no": "WD-24817", "tutar": 1250, "para_birimi": "USD",
# "kural": "limit_alti", "anahtar": "a91f...", "durum": "onay_bekliyor"}
# 2. asama: onay sonrasi kod calistirir; model cagirmaz
# onay kurali akista:
# tutar < 500 USD ve musteri KYC tam -> otomatik
# digerleri -> insan onayi
Plan bir kuyruğa yazılıyor; kuyruğu boşaltan şey agent değil, akış. Bunun üç faydası oldu:
- Agent yanlış karar verirse bedel bir kuyruk satırı; geri almak için “sil” yeter.
- Onay kuralı kodda olduğu için okunabiliyor ve test edilebiliyor. Prompt’taki “500 doların üstünde onay iste” cümlesi test edilemez.
- Prompt injection ile agent’ı kandıran biri bile yalnızca kuyruğa satır yazdırabiliyor; parayı hareket ettiremiyor.
Geri alınamazlar listesi
Bir sayfa tuttuk; adı tam olarak bu. Her yan etkili tool listeye giriyor ve üç soruya cevap veriyor: geri alınabilir mi, kim onaylar, tekrarı ne yapar?
| Tool | Geri alınabilir mi? | Onay | Tekrar edilirse |
|---|---|---|---|
cekim_onayla | Hayır (para çıktı) | Tutar eşiği + insan | Anahtar yakalar, ilk sonuç döner |
musteri_notu_ekle | Evet (not silinir) | Gerekmez | Anahtar yakalar |
kyc_reddet | Kısmen (müşteriye mail gitti) | İnsan | Mail ikinci kez gitmez |
rapor_mail_at | Hayır (mail geri gelmez) | Kod: yalnızca iç alıcılar | Anahtar yakalar |
mutabakat_kapat | Evet | Gerekmez | Anahtar yakalar |
Listenin asıl faydası tool eklerken çıktı: yeni bir yan etkili tool yazan kişi bu üç soruya cevap vermeden PR açamıyor. Soruların cevabı yoksa tool okuma tool’u olarak kalıyor.
Ne izlemeli?
- Idempotency isabet sayısı. Kaç çağrı “zaten yapılmış” diye döndü? Sıfırsa koruma hiç sınanmamıştır; artıyorsa zaman aşımı eşiğine bak.
- Yan etkili çağrı / koşu. Beklenenden fazlaysa model gereksiz yazma yapıyordur.
- Zaman aşımı oranı ve tool süresi p99. Bizdeki olayın kök sebebi buydu: p99 31 saniye, eşik 30.
- Onay kuyruğunda bekleme süresi. Uzuyorsa insan onayı darboğaz olmuştur; eşiği gözden geçir.
- Otomatik onay oranı. Yükseliyorsa eşik gevşemiş olabilir; ayda bir bakılır.
- Denetim log’u eksiksizliği. Her yan etkili çağrıda koşu kimliği, anahtar, argüman ve sonuç var mı?
Kontrol listesi
- Hangi tool’lar yan etkili? Şemada işaretli mi?
- Idempotency anahtarını kim üretiyor: kod mu, model mi?
- Anahtarın doğal bileşenleri ne? Zaman penceresi ne kadar?
- İkinci çağrıya ne dönüyor: hata mı, ilk sonuç mu?
- Tekillik veritabanında mı, kodda mı?
- Tool zaman aşımı, tool’un p99 süresinden büyük mü?
- Geri alınamaz adımlar plan/uygulama olarak ikiye bölündü mü?
- Onay kuralı kodda mı, prompt’ta mı?
- Yarıda kalan koşu yeniden başlarsa tamamlanmış adımları atlıyor mu?
- Denetim log’unda koşu kimliği, anahtar ve argümanlar var mı?
- Çift işlem olursa bunu hangi rapor yakalar? Ne kadar sürede?
Sonuç
O cuma agent bir şeyi yanlış yapmadı: cevap gelmedi, tekrar denedi. Sistem yanlıştı, çünkü aynı işi ikinci kez kabul etti. Bu yazının tamamı aslında eski bir konunun yeni bir kılığı: ağ güvenilmez, çağrılar tekrarlanır, tekillik veritabanında durur. Yeni olan tek şey, tekrar eden tarafın artık bir model olması ve ne zaman tekrar edeceğini önceden bilememen.
Düzelttikten sonraki hâl: anahtarı kod üretiyor, ikinci çağrı ilk sonucu döndürüyor, para hareketi olan adım plan–onay–uygula şeklinde üçe ayrılmış durumda. Agent hâlâ tekrar deniyor — log’da ayda 3–4 kez “tekrar: true” görüyoruz — ama artık bu bir olay değil, bir satır.
Akılda kalacak cümle: agent’ın otonomisi, tool’larının idempotency’si kadardır. İkincisi yoksa birincisi bir özellik değil, açık bir risktir.