33  Dağıtım Topolojileri ve Güvenilirlik

Bölüm durumu: çevrildi. Kaynak metin: İngilizce 33. bölüm. Bölümde yaklaşık bir dakikalık hesaplama gerektiren tek bir küçük ölçüm var: bir ajan adımını yeniden denemenin o adımı aynen tekrarlayıp tekrarlamadığını görmek için bir modelden aynı araç çağrısını defalarca üretmesini isteyen 72 kısa çağrı (üç görev × üç örnekleme koşulu × sekiz yeniden deneme). Geri kalan her şey, belirlenimci olmayan bir çağırana uygulanmış sıradan dağıtık sistem pratiği; bunlar yeniden türetilmek yerine kaynak gösterilerek aktarılıyor. Yazılım: ollama 0.24.0, llama3.2:3b servis ediyor; Python 3.14.5, Apple Silicon.

Öğrenme hedefleri. Bu bölümün sonunda:

33.1 Ajan gerçekte nerede koşuyor

İlk karar sıkıcı bir karar ve takımlar onu çoğu zaman alışkanlıkla veriyor. Üç biçim var; doğru seçimin yolu, hangi kısıtın bağlayıcı olduğunu açıkça söylemek. Yönetilen çalışma zamanına örnek olarak bulut sağlayıcılarının ajan barındırma hizmetleri verilebilir: yazım anında Amazon Bedrock AgentCore Runtime ya da Google Cloud’un Agent Runtime’ı (eski adıyla Vertex AI Agent Engine).

Üç biçim ve birini seçmesi gereken kısıt
ne elde edersiniz neyden vazgeçersiniz ne zaman seçilir
Yönetilen ajan çalışma zamanı oturum durumu, yeniden denemeler, ölçeklenme ve izler (trace) — hiçbirini kendiniz yazmadan döngü üzerindeki denetim, kendi ağınızda koşma imkânı ve taşınabilirlik yayına alma hızı denetimden daha önemliyse ve hiçbir data residency (verinin tutulduğu ve işlendiği ülke ya da bölge) kuralı engellemiyorsa
Kendi konteynerleriniz döngünün, ağın ve veri yolunun tam denetimi oturum depolamasını, iptali, yeniden deneme semantiğini ve gözlemlenebilirliği kendiniz kurmak data residency ya da mevcut bir platform gerekli kılıyorsa — daha ciddi hissettirdiği için değil
Sunucusuz işlevler sıfıra ölçeklenme ve boşta maliyet olmaması uzun koşular, çünkü yürütme süresi sınırlıdır trafik ani sıçramalar yapıyorsa ve her görev saniyeler içinde bitiyorsa

Takımları en çok şaşırtan üçüncüsü. Bir ajan döngüsü bir istek işleyicisi değildir: dakikalar sürebilir ve yürütmeyi birkaç saniyeyle sınırlayan bir platform onu, araçlar zaten çalıştıktan sonra, yarı yolda öldürür. Çalışma zamanınızın katı bir yürütme tavanı varsa döngünün bir kuyruğun arkasına taşınması gerekir. Bu, Bölüm 33.3’ün konusu ve dağıtımda değil, üründe yapılan bir değişikliktir.

33.2 Konuşma nerede yaşıyor

Ajan, mesajlar üzerinde dönen bir döngü olduğuna göre mesajların bir yerde durması gerekir. Sağladıkları olanaklar giderek artacak biçimde sıralanmış üç yer var:

Süreç içinde. Basit ve hızlı; ama bir oturumun sonsuza dek tek bir örneğe (instance) ait olması demek. Her yeniden başlatma, dağıtım ya da scale-in (çalışan örnek sayısının azaltılması) konuşmaları kaybettiriyor. Yük dengeleme de bir kullanıcıyı tek bir örneğe sabitlemek, yani tüm isteklerini aynı örneğe yönlendirmek (sticky session, oturum yapışkanlığı) zorunda kalıyor — sonunda değişikliği zorlayan kısıt bu.

Bir oturum deposunda. Döngü her konuşma turunun (turn) başında geçmişi okuyor ve sonunda geri yazıyor. Herhangi bir örnek herhangi bir tura hizmet edebiliyor, yeniden başlatmalar atlatılabiliyor ve deponun gecikmesi artık her tura ekleniyor. Neredeyse her duruma uyan varsayılan seçenek bu.

İstemcide. Çağıran her turda tüm geçmişi gönderiyor. Sunucu gerçekten durumsuz ve zahmetsizce ölçeklenebilir hâle geliyor. Bedeli, geçmişi taşıyan isteğin ağ üzerinden her turda büyümesi ve istemcinin geçmişi düzenleyebilmesi; Bölüm 36’nın bu konuda söyleyecekleri var. Token faturası ise üç seçenekte de aynı (yandaki not).

Bir geçmişi taşımanın token maliyetini hafife almak kolay ve bu, geçmişin nerede durduğuna bağlı değil. Yirmi turluk bir konuşma son istekte on dokuz turu yeniden gönderiyor; dolayısıyla son prompt (istem) doğrusal, oturumun toplam prompt token’ı ise kabaca karesel büyüyor. Geçmişi ister istemci göndersin ister sunucu tarafındaki bir oturum deposu, aritmetik aynı, çünkü parasını ödediğiniz şey modele ulaşan şey. İstemcide tutulan durumun değiştirdiği şey, maliyet değil, geçmişi kimin düzenleyebileceği — ve Bölüm 31’in ölçümleri, bu bir fatura sorunu olmadan çok önce prefill’in gecikmeye hâkim olduğunu gösteriyor. Prompt önbellekleme tam da burada yardımcı oluyor; yeter ki geçmişe yalnızca sondan ekleme yapılsın ve başta değişken hiçbir şey durmasın.

33.3 Bir isteğin ömrünü aşan ajanlar

Bir görev dakikalar sürmeye başladığında bunun için bir HTTP bağlantısını açık tutmak makul olmaktan çıkıyor: istemciler zaman aşımına uğruyor, yük dengeleyiciler boştaki bağlantıları kesiyor ve bir dağıtım o an sürmekte olan her şeyi öldürüyor.

İşe yarayan biçim, uzun süren işler yürüten her sistemin eninde sonunda vardığı biçim. İsteği kabul edin, bir iş kaydı oluşturun, bir kimlik döndürün. Bir işçi (worker) işi alsın. İstemci ilerlemeyi yoklasın (polling) ya da ilerlemeye abone olsun. Atlanması kolay, sonradan eklenmesi acı verici üç şey:

  • Bir anlamı olan ilerleme. “Bilinmeyen sayıdaki adımdan 4’üncüsü” ilerleme değildir. Adımı, az önce çağrılan aracı ve şimdiye kadar harcanan token’ları bildirin — bunların izlere dönüştüğü yer Bölüm 34’tür.
  • Gerçekten işi durduran iptal. Döngünün hiç bakmadığı bir iptal bayrağı süstür. Döngü onu adımlar arasında okumalı ve araçlar iptal edilebilir ya da en azından idempotent olmalı, çünkü araç çağrısının ortasında gelen bir iptal, aracın zaten başlattığı yan etkiyi geri alamaz.
  • Bir dead-letter queue (tekrar denemelere rağmen işlenemeyen mesajların incelemeye ayrıldığı kuyruk). Tekrar tekrar başarısız olan işler sonsuza dek yeniden denenmek yerine bir insanın göreceği bir yere düşmeli — bu, Bölüm 33.5’teki problemin başka bir biçimi.

33.4 Dayanıklı yürütme ve tekrar oynatmanın kodunuzdan istedikleri

Bir işçi on iki adımlık bir ajan koşusunun yarısında çökerse ne olur? Naif cevap — baştan başla — zaten bir şey göndermiş olanlar dâhil her araç çağrısını yeniden yürütür.

Dayanıklı yürütme motorları buna, tamamlanan her adımı kaydedip çökme sonrasında geçmişi tekrar oynatarak (replay) cevap veriyor; böylece tamamlanan iş tekrarlanmıyor. Temporal ve Restate bilinen örnekler; fikrin kendisi ajanlardan çok daha eskiye dayanıyor.

Bunun size gerçekte ne kazandırdığını tek bir çekince belirliyor. Bir adım ancak sonucu kaydedildiğinde tamamlanmış sayılır; dolayısıyla işçi öldüğü anda koşmakta olan adım yeniden koşar. Araç etkisini gerçekleştirdiyse ve işçi, motor sonucu kaydetmeden önce çöktüyse etki iki kez olur. Temporal’ın belgeleri bunu açıkça söylüyor: bir etkinlik (activity) bir kez tamamlanmış olarak kaydedilir, ama birden fazla kez yürütülebilir. Dayanıklı yürütme, bitmiş adımların yeniden koşmasını ortadan kaldırır; idempotent araçlara olan ihtiyacı kaldırmaz (Bölüm 33.6).

Böyle bir motoru benimserken önemli olan motorun kendisi değil, kodunuza dayattığı sözleşme:

Tekrar oynatma, iş akışı işlevinizin baştan yeniden koşması ve tamamlanmış her adımın yürütülmek yerine kaydedilmiş sonucunu döndürmesi demektir. Bunun doğru çalışması için işlevin belirlenimci olması gerekiyor: aynı geçmiş, aynı karar dizisi. Saati okumak, rastgele bir kimlik üretmek ya da iş akışı kodundan doğrudan bir API çağırmak bunu bozar, çünkü tekrar oynatma bu durumda ilk koşudan farklı bir yol izler.

Bu, ajanlarla pek iyi bağdaşmıyor ve bunu açıkça söylemek gerekiyor. Bir LLM çağrısı, dışsal ve belirlenimci olmayabilecek bir işlem gibi ele alınmalıdır — sıcaklık 0’da bile, çünkü batchleme (istekleri yığın hâlinde birlikte işleme), donanım ve sessizce yapılan bir model güncellemesi çıktıyı değiştirebilir. Çözüm zekice değil, yapısal: model çağrısı, iş akışı kodunun içinde verilen bir karar değil, sonucu kaydedilip tekrar oynatılan bir adımdır — Temporal’ın sözlüğünde bir etkinlik. İş akışı yalnızca düzenler; belirlenimsizlik, onun kaydettiği adımların içinde kalır.

Temporal’ın Python SDK’sına yakın bir sözde kodla bu ayrım şöyle görünüyor:

@workflow.defn
class ArizaBildirimi:
    @workflow.run
    async def run(self, gorev: str) -> str:
        # LLM çağrısı bir etkinlik: sonucu kaydedilir, tekrar oynatmada yeniden koşmaz
        karar = await workflow.execute_activity(
            llm_call, gorev, start_to_close_timeout=timedelta(minutes=2))
        # time.time(), uuid.uuid4() ya da doğrudan bir HTTP çağrısı burada yasak:
        # tekrar oynatmada farklı değer döndürüp iş akışını farklı bir yola sokarlar.
        # Saat gerekiyorsa workflow.now() kullanılır; motor onu geçmişten okur.
        return await workflow.execute_activity(
            send_email, karar, start_to_close_timeout=timedelta(seconds=30))

llm_call ve send_email, iş akışının dışında @activity.defn ile tanımlanmış sıradan işlevlerdir. Etkinlikler yeniden denenebildiği için send_email yine de bir idempotency anahtarı taşımalıdır.

Bu, araç tasarımına (Bölüm 24) gerçek bir kısıt getiriyor; dayanıklı bir motoru sonradan benimsemek de genellikle döngüyü sarmalamak değil, yeniden yazmak demek.

33.5 Bir adımı yeniden denemek, bir isteği yeniden denemek değildir

Bölüm 32 bir sağlayıcıya karşı yeniden denemeleri ele aldı: oradaki arıza, yeniden denemelerin yükü katlaması (amplification); çaresi de backoff, jitter, devre kesiciler ve bir yeniden deneme bütçesi. Bu alt bölüm ise bir ajan adımını yeniden denemekle ilgili; buradaki arıza farklı ve daha kötü.

Bir sağlayıcıya yapılan yeniden deneme iki kez başarılı olursa para kaybedersiniz. Bir ajan adımının yeniden denemesi iki kez başarılı olursa e-posta iki kez gider.

İşin altındaki ilginç soru, genellikle ölçülmeyip varsayılan bir soru: adımı yeniden denediğinizde aynı araç çağrısını mı alıyorsunuz? Ölçüm şöyle: bir modelden, üç görevin her biri için ve üç örnekleme koşulunun her birinde, tek bir send_email çağrısını sekiz kez üretmesi istendi. Üç görev modele giderek daha çok karar bırakıyor: clear alıcıyı adıyla veriyor, vague gevşek ifade edilmiş, choice ise (aşağıda alıntılanıyor) alıcıyı modele bırakıyor. seed sütununda 0 sabit bir seed (rastgele sayı üretecinin başlangıç değeri), None ise seed verilmediği anlamına geliyor. identical, araç çağrısı payload’unun (aracın adı ve bütün argümanları) sekiz yeniden denemenin tamamında bayt bayt aynı olup olmadığını gösteriyor; distinct to ve distinct subj ise yalnızca alıcı ve konu argümanlarının kaç farklı değer aldığını sayıyor.

    task  temp   seed  identical  distinct to  distinct subj
   clear   0.0      0       True            1              1
   clear   0.0   None       True            1              1
   clear   0.7   None      False            1              1
   vague   0.0      0       True            1              1
   vague   0.0   None       True            1              1
   vague   0.7   None      False            1              6
  choice   0.0      0       True            1              1
  choice   0.0   None       True            1              1
  choice   0.7   None      False            5              2

Son satıra bakın. Görev şuydu: “pompa arızalandı, bilmesi gerekene haber ver”. 0,7 sıcaklıkta sekiz yeniden deneme boyunca model beş farklı alıcı seçti:

  3x  maintenance@factory.com
  2x  maintenance@company.com
  1x  pump_repair_team@example.com
  1x  maintenance-team@company.com
  1x  engineering@company.com

Yeniden deneme eylemi tekrarlamadı. Farklı bir eylem gerçekleştirdi. Adreslerin hepsi uydurma; idempotency koruması olmayan bir yeniden deneme döngüsü bunların birkaçına e-posta göndermiş olurdu.

Beş sayısını bir kestirim değil, bir alt sınır olarak okuyun. Sekiz çekilişle, modelin kaç farklı adres uydurabileceğine bir üst sınır koyamazsınız. Ekologlar tür sayarken aynı sorunla karşılaşır; onların Chao1 kestiricisi, hiç görülmemiş değerlerin sayısını tam bir kez (\(f_1\)) ve tam iki kez (\(f_2\)) görülen değerlerden kestirir. Bu dağılımla — bir adres üç kez, biri iki kez, üçü birer kez; yani \(f_1 = 3\), \(f_2 = 1\) — klasik biçim \(5 + f_1^2/(2f_2)\) 9,5 veriyor, küçük örneklem için yanlılığı düzeltilmiş biçim \(5 + \frac{n-1}{n}\cdot\frac{f_1(f_1-1)}{2(f_2+1)}\) ise \(n = 8\) çekilişle yaklaşık 6,3. Sekiz çekiliş ikisi arasında seçim yapmaya hiç yetmez, ama ikisi de beşin üstünde; çünkü sekiz çekilişte yalnızca bir kez görülen üç değer, henüz örneklemediğiniz uzun bir kuyruğun işaretidir. Öbür yönde, sıcaklık 0’daki sekiz özdeş çıktı, tekrar oranı için yaklaşık [%68, %100] aralığında bir %95 Wilson güven aralığı veriyor (tanımı için Bölüm 5.6); yani “sekiz kez bayt bayt aynı” sonucu, gerçek tekrar oranının üçte ikiye yakın olmasıyla da bağdaşıyor. Bu iki düzeltmenin hiçbiri tasarım sonucunu değiştirmiyor; ikisi de neyin iddia edilebileceğini değiştiriyor: bu ölçüm, kusurun var olduğunu ve kolayca yeniden üretildiğini gösteriyor; sizin uç noktanızda (endpoint) ne sıklıkla ortaya çıktığını değil.

Bu ölçümün söylemediği üç şey var ve üçü de önemli.

Sıcaklık 0 çare değil, farklı bir risktir. Sıcaklık 0’da sekiz yeniden denemenin tamamı her görevde bayt bayt aynıydı; buna choice da dâhil — model orada her seferinde aynı maintenance@company.com adresini uydurdu. Yeniden üretilebilir, ama yine de yanlış. Belirlenimcilik yeniden denemeyi tekrarlanabilir kılar; altta yatan kararı doğru kılmaz.

Tabloda olmayan bir koşul, 0,7 sıcaklıkta sabit bir seed, ilgili bir soruyu yanıtlıyor. Bölüm 17 aynı kontrolü daha büyük ölçekte koştu ve bu koşulun da otuz belgenin hepsinde, on tekrar boyunca özdeş çıktı ürettiğini buldu. Yani buradaki eksen aslında sıcaklık değil; örnekleyicinin sabitlenip sabitlenmediği. Sıcaklık 0’ı bir güvenilirlik ayarı olarak kullanmadan önce bunu bilin: sıcaklık 0, isteyebileceğiniz çeşitliliği elinizden alıyor ve karşılığında bir seed’in daha ucuza sağladığı şeyi veriyor — seed parametresini destekleyen ve isteği aynı batch koşullarında işleyen bir sunucuda. Birçok barındırılan API seed sunmuyor ya da sunduğu seed yalnızca en iyi çaba düzeyinde bir belirlenimcilik veriyor; bunun nedeni bir sonraki paragrafta.

Üstelik bu belirlenimcilik, tek akışlı yerel bir sunucunun bir özelliği; bir garanti değil. Kayan noktalı sayılarda toplama sırası sonucu değiştirir: \((a+b)+c\) ile \(a+(b+c)\) son basamakta farklı çıkabilir. Matris çarpımı (matmul), normalleştirme ve attention (dikkat mekanizması) hesapları, çok sayıda değeri tek bir toplama indiren indirgeme (reduction) adımları içerir ve bunları yapan GPU çekirdekleri (kernel) toplamaları batch boyutuna göre farklı sırayla yapar. Belirleyici koşul batch değişmezliği (batch invariance), yani sonucun batch boyutundan bağımsız olmasıdır ve çoğu çıkarım sunucusu bu anlamda batch değişmezi değildir. Dolayısıyla aynı prompt, tek başına işlendiğinde ve on yedi istekli bir batch içinde işlendiğinde sıcaklık 0’da bile farklı çıktı üretebiliyor (He ve Thinking Machines Lab 2025). Barındırılan API’ler isteğinizi başka kullanıcıların istekleriyle aynı batch’e koyuyor. Sıcaklık 0’ı varyansı azaltan bir önlem olarak görün; asla bir idempotency mekanizması olarak değil.

Tablodaki örüntü tek tek sayılardan daha değerli ve “sorun sıcaklıkta” demekten daha incelikli. 0,7’de hiçbir görev bayt bayt aynı değildi — alıcıyı adıyla veren ama e-posta gövdesi yine de değişen clear dâhil. Görevdeki belirsizliğin değiştirdiği şey, hangi alanın oynadığı: clear alıcıyı ve konuyu korudu, vague altı farklı konu satırı üretti, choice beş farklı alıcı üretti. Sıcaklık her şeyi biraz sarsar; bu sarsıntının yan etkisi olan bir alana denk gelip gelmeyeceğine görev karar verir. Payload’un herhangi bir parçasından türetilen bir anahtarın — yalnızca alıcıdan türetilenin değil — güvensiz olmasının nedeni de bu: hangi alanın oynayacağını görev belirler.

33.6 Idempotency anahtarları ve anahtarın nereden geldiği

Yinelenen bir yan etki için standart çare bir idempotency anahtarıdır: çağıran bir anahtar sağlar, alıcı onu kaydeder ve daha önce gördüğü bir anahtarı taşıyan ikinci bir istek, işlemi yeniden yapmak yerine ilk sonucu döndürür.

Yukarıdaki ölçüm, genellikle üstünkörü geçilen tasarım sorusunu yanıtlıyor:

Anahtar modelin çıktısından türetilemez. Araç argümanlarının hash değerini (özet değeri) almak doğal görünüyor ve tam da ihtiyaç duyduğunuzda başarısız oluyor: choice yeniden denemeleri beş farklı argüman kümesi, dolayısıyla beş farklı hash, dolayısıyla beş e-posta üretti. Belirlenimci olmayan bir değerden hesaplanmış bir anahtar, anahtar değildir.

Öyleyse onu yukarı akıştaki, kararlı bir şeyden türetin:

Dört aday anahtar ve işe yarayan ikisi
anahtar neyden türetildi bir yeniden denemeyi tekilleştirir mi? notlar
Araç argümanlarının hash değeri hayır model fikrini değiştirdiğinde değişir — bu bölümün ölçtüğü arıza
Deneme başına rastgele kimlik hayır her yeniden denemede yeni bir anahtar; bu, hiç anahtar olmamasıyla aynı şey. İşlem başına bir kez üretilip ilk denemeden önce saklanan rastgele bir kimlik ise sorunsuzdur — Stripe’ın önerdiği de bu; hata, onu her denemede yeniden üretmektir
(oturum kimliği, adım indisi) evet aynı adımın yeniden denemeleri arasında kararlı — tek bir adım birkaç yan etkili çağrı yapıyorsa bir alt çağrı indisi ekleyin, yoksa çakışırlar
Yukarı akış istek kimliği evet bir isteğin uçtan uca en fazla bir yan etki üretmesi gerektiğinde doğru seçim

Adım indisi ancak çökmeden sağ çıkan bir geçmişten — bir oturum deposundan ya da dayanıklı bir motorun kaydından — geliyorsa kararlıdır. Yeniden başlatma ajanı sıfırdan koşturursa model aynı çağrıyı başka bir adımda yapabilir ve anahtar da onunla birlikte değişir.

Anahtara kiracı (tenant) kimliğini de katın; böylece iki müşteri aynı mantıksal işlem kimliğini kullandığında anahtarlar çakışmaz.

Bunun pratikte işe yarayıp yaramayacağını üç uygulama ayrıntısı belirliyor.

Aynı anahtar farklı argümanlarla geldiğinde ne olacağına karar verin. Cazip davranış — saklanmış sonucu döndürmek — yanlıştır: farklı bir isteğe sessizce eski bir cevap verir. Stripe’ın API’si isteğin bir parmak izini anahtarla birlikte saklar ve parametreleri eşleşmeyen bir yeniden kullanımı reddeder; kopyalanacak davranış budur. Argümanları kayıtla birlikte saklayın, tekrar oynatmada karşılaştırın ve uyuşmazlık varsa açıkça hata verin.

Kayıt ile etki birlikte commit edilmeli (aynı işlemde kalıcılaştırılmalı). Etki sizin veri tabanınızdaki bir satırsa bu tek bir veri tabanı işlemidir (transaction) ve problem çözülmüştür. Etki uzak bir çağrıysa — ki bu bölümün sürekli kullandığı send_email dâhil ilginç her araç için durum budur — çözülmemiştir, çünkü bir HTTP isteği sizin işleminize katılamaz. Bu, çift yazma (dual-write) problemidir ve standart cevabı transactional outbox (gönderilecek çağrıyı yerel değişiklikle aynı işlemde kaydetme): tek bir işlemde hem idempotency kaydını hem de amaçlanan çağrıyı tarif eden bir satırı yazın. Ayrı bir yayıcı (publisher) outbox’ı okur ve çağrıyı en az bir kez (at-least-once) yapar: bir çökmeden sonra çağrıyı tekrarlayabilir ama asla atlamaz. Çağrı anahtarı da taşır; böylece alıcı yinelenen isteği tanıyıp yok sayar. Yinelemeyi ortadan kaldırmadınız. Onu, bir anahtarın soğurabileceği bir yere taşıdınız. Bu kalıbı adıyla bilin, çünkü mülakatlarda sık sorulur: dağıtık bir sistemde tam olarak bir kez (exactly-once) teslimat garanti edilemez; elde edilen, “en az bir kez teslimat + idempotency = etkin olarak bir kez” (effectively-once) işlemedir.

Bu son adım, alıcının idempotency anahtarlarını dikkate aldığını varsayıyor. Stripe’ınki gibi ödeme API’leri dikkate alır; düz SMTP almaz, pek çok e-posta ve mesajlaşma API’si de almaz. Böyle bir alıcıya karşı “en az bir kez”, “arada bir iki kez” demektir. Geriye kalan, çağrı ile kaydı arasındaki yineleme penceresini küçültmek — çağrıdan önce “gönderiliyor” diye kaydetmek ve bir çökmeden sonra yeniden göndermeden önce sağlayıcının kendi kaydına bakmak — ve bu pencerenin var olduğunu bilmektir.

Saklama penceresi en uzun yeniden denemenizden uzun olmalı. Pencereyi beş dakikaya ayarlayıp sonradan altı saat gerektiğini keşfetmek kolaydır: kuyruk birikmiştir ve iş ertesi sabah yeniden teslim edilir.

33.7 Telafi: gönderilmiş e-posta geri alınamaz

Idempotency bir eylemin iki kez olmasını engeller. Bir kez bile olmaması gereken bir eylem hakkında hiçbir şey yapmaz.

Veri tabanları size işlemler (transaction) verir. Bir ajanın çağırdığı araçlar çoğunlukla vermez; bu yüzden kullanılan örüntü telafi eylemidir (compensating action): her yan etki için onu geri alan işlem. Disiplin de telafi sütununu olaydan sonra değil, aracı yayına almadan önce yazmaktır. Adımları sırayla yürütüp bir adım başarısız olduğunda öncekileri telafi eylemleriyle ters sırada geri alan bu yapı, dağıtık sistemlerde saga örüntüsü olarak bilinir.

Telafiler ve gerçek bir geri alması olmayan iki satır
araç telafi gerçek bir geri alma mı?
Kayıt oluştur sil çoğunlukla — satır gider, ama kaydın oluşturulmasıyla çoktan tetiklenmiş olanlar (bir webhook, aşağı akışta bir olay, gönderilmiş bir bildirim) geri alınamaz
Karttan ödeme al iade çoğunlukla — işlem ücreti ve ekstredeki satır kalır
Yer ayır iptal et genellikle — ama yer tutuluyken geri çevrilen biri geri çevrilmiş olarak kalır
Bir kanala gönder mesajı sil hayır — insanlar okudu
E-posta gönder hiçbir şey hayır

Alttaki iki satır tasarım kısıtıdır. Bir aracın telafisi olmadığında denetimin daha erkene taşınması gerekir — sonradan bir geri alma yerine eylem öncesinde bir onay kapısı; bu da Bölüm 37’nin konusu. Bu, mühendislik kılığına girmiş bir ürün kararıdır ve birini bu kararı bilinçli olarak vermeye zorlayan şey de telafi tablosudur.

Araç yazarken yararlı bir alışkanlık (Bölüm 24): yan etkili her araca bir kuru çalıştırma (dry-run) kipi ekleyin. Bu, telafi tablosunu sınanabilir kılar ve ajana, planını uygulamaya geçirmeden önce denetleme olanağı verir.

33.8 Her ajanın ihtiyaç duyduğu üç tavan

Bölüm 23 bunlara neden ihtiyaç olduğunu ölçmüştü: protokollerden birinde en kolay görevlerin yüzde altmışında ajan kendiliğinden hiç durmadı ve adım bütçesi (bu bölümdeki adıyla adım tavanı) dolana kadar koştu — oysa bu koşuların her biri cevabı daha 0. adımda bulmuştu. Yani bir akıl yürütme sonucu gibi görünen doğruluk sütunu aslında bir sonlanma sonucuydu. Üç sınır var ve her biri farklı bir arızayı yakalıyor:

Üç tavan, üç farklı arıza
tavan neyi yakalar sınırda ne yapılır
Adım araç çağırmayı sürdüren döngüyü durdur, kısmi işi döndür, tamamlanmamış olarak işaretle
Token adım başına ucuz, bütünde pahalı kalan koşuyu durdur ve harcamayı raporla — Bölüm 32 ilgili bir bütçeyi sanal anahtar başına zorunlu kılar
Wall-clock süresi diğer iki sınırın görmediği, takılan aracı iptal et ve iptalin araca ulaştığından emin ol

Üçü de gerekli, çünkü her biri diğerlerinin arızasına kör. Bir adım tavanı tek bir aracın bir saat takılmasını durdurmaz; bir wall-clock zaman aşımı altmış hızlı adımın bir bütçeyi tüketmesini durdurmaz; bir token tavanı aynı küçük dosyayı sonsuza dek okuyan bir döngüyü durdurmaz.

Bir tavana çarpmak da sessizce yutulacak bir hata değil, bir sonuçtur. Cevapta, izde ve değerlendirmenizde ayrı bir sonuç olarak görünmelidir — Bölüm 30’un sonlanma oranı bu sayıdır ve koşuların üçte birinde tavana çarparken %85 doğru görünen bir ajan, %85 doğru bir ajan değildir.

33.9 Bir değişikliği yayına almak: sürümler, kanaryalar ve geri alma

Buraya kadar anlatılanların hepsi, bir koşu sürerken gelen arızayı atlatmakla ilgiliydi. Bir ajanın üretimde bozulmasının öteki yaygın yolu ise yeni bir dağıtımdır: yeni bir prompt, yeni bir model sürümü, yeniden yazılmış bir araç açıklaması. Dağıtımları sorunsuz atlatmanızı sağlayan dört alışkanlık var.

Yalnızca kodu değil, davranışın tamamını sürümleyin. Bir ajanın davranışını birkaç şey birlikte belirler: kodu, prompt’ları, sabitlenmiş model sürümü (Bölüm 32.6), araç tanımları ve sıcaklık ile üç tavan gibi ayarlar. Bunları tek bir sürüm kimliği altında birlikte yayına alın ve o kimliği her ize kaydedin (Bölüm 34). Yeni bir sürüm kimliği verilmeden bir panelden düzenlenen prompt, kimsenin kaynağını bulamayacağı ve geri alamayacağı bir dağıtımdır. Bölüm 35’in teşhis tablosundaki “bir sürümle hizalanan ani düşüş” de ancak sürüm kaydedildiyse görünür.

Önce bir dilime açın. Bir kanarya (canary) dağıtımı, canlı trafiğin küçük bir payını — diyelim %5’ini — yeni sürüme gönderir ve yeni sürümü aynı trafikte eski sürümle karşılaştırır: hata oranı, gecikme, koşu başına maliyet, tavana çarpan koşuların payı ve elinizdeki kalite sinyali neyse o. (Adını, madencilerin zehirli gazı erken fark etmek için ocağa indirdiği kanaryadan alır: sorunu önce küçük bir dilim görür.) Pay aşama aşama büyür (%5 → %25 → %100), ama yalnızca kanarya eski sürümden kötü değilse; bir eşik aşılırsa trafik kendiliğinden eski sürüme döner. Bir gölge dağıtım (shadow deployment) ise yeni sürümü canlı girdilerin bir kopyası üzerinde koşturur, ama çıktısını kimseye göstermez. Bu, bir sohbet botu için güvenli, bir ajan için tehlikelidir: gerçek araçları olan bir gölge ajan gerçek e-postalar gönderir. Gölge ajanın yan etkili araçları kuru çalıştırma kipinde çalışmalıdır (Bölüm 33.7). Bu yöntemlerin her birinin neyi yakalayıp neyi kaçırdığı, Bölüm 35’teki değerlendirme merdiveninin konusu.

Geri alma tetikleyicisini dağıtımdan önce kararlaştırın. Bir hizmet düzeyi hedefi (service level objective, SLO), belirli bir zaman penceresi için konan ve bir hizmet düzeyi göstergesiyle (service level indicator, SLI) ölçülen hedeftir — örneğin “30 günlük pencerede ajan koşularının %99,5’i hatasız ve tavana çarpmadan biter”. Başarısız olmasına izin verilen %0,5’lik pay hata bütçesidir (error budget): ayda 200.000 koşu yapan bir sistemde bu, 1.000 koşu eder. Bu bütçeyi eski sürümden çok daha hızlı tüketen bir kanarya, bir olay sırasında tartışılarak değil, önceden konmuş bir kuralla geri alınır. Göstergeyi dikkatle seçin: HTTP 200 başarılı bir koşu demek değildir, çünkü bir koşu 200 döndürüp yine de tavana çarpmış ya da yanlış cevap vermiş olabilir (Bölüm 34). Dilimin büyüklüğüne de dikkat edin. Ayda 200.000 koşuluk bir sistemde %5’lik kanarya günde yaklaşık 330 koşu görür; başarısızlığın taban oranı %0,5 ise günde ikiden az başarısızlık bekler (330 × 0,005 ≈ 1,7). Bu, ciddi biçimde bozuk bir sürümü yakalamaya yeter; biraz daha kötü olanı fark etmeye ise hiç yetmez. Aynı %5’lik payla koşulan bir A/B testi de aynı istatistiksel güç sorununa takılır: küçük farkları görmek için daha büyük bir pay, daha uzun bir süre ve örneklem büyüklüğü önceden hesaplanmış bir A/B testi gerekir.

Geri alma kodu geri alır, etkileri değil. Geri alma, eski davranışı yalnızca yeni koşular için geri getirir. Kötü sürümün gönderdiği e-postaları geri çekmez — o, telafi tablosunun işidir — ve hâlâ sürmekte olan koşulara ne olacağını da belirlemez. Yeni kodla başlamış ve eski kodla tekrar oynatılan (ya da tersi) dayanıklı bir iş akışı farklı bir yol izleyebilir; bu da yine Bölüm 33.4’teki belirlenimcilik kuralıdır. Dayanıklı motorlar bunun için iş akışı sürümlemesi sunar (Temporal’ın Python SDK’sında workflow.patched()); basit kural, sürmekte olan her koşunun başladığı sürümle bitmesine izin vermektir.

33.10 Sezgi kontrolleri

Her birini açmadan önce cevaplayın. İkisi bu bölümdeki yeniden deneme ölçümüne dayanıyor — sıcaklık 0’ın ne kazandırıp ne kazandırmadığı ve idempotency anahtarının neden modelden gelemeyeceği. Geri kalanlar ise çalışan bir prototipin dağıtımdan sağ çıkıp çıkmayacağını belirleyen sorular.

Bir döngüyü bir istek işleyicisinin üzerine dağıttınız.

Sunucusuz platformlar yürütme süresini sınırlıyor, birçok yük dengeleyici fazla uzun boşta kalan bir bağlantıyı kesiyor ve bir ajan döngüsü bir istek değil: dakikalar sürebiliyor ve sınır koşunun ortasında — bazı araçlar zaten yürütüldükten sonra — devreye giriyor. Belirti: gerçek yan etkiler bırakmış ama sonuç döndürmemiş, yarıda kesilmiş bir koşu.

İş, denetiminizdeki bir sınırın içinde güvenilir biçimde bitiyorsa zaman aşımını ona göre yükseltmek meşru ve çok daha ucuz bir çözümdür. Onun ötesinde çözüm yapısal: isteği kabul edin, bir iş kuyruğa alın, bir kimlik döndürün ve döngüyü bir işçi koşsun. Bu değişiklik, ilerleme bildirimini, iptali ve dead-letter queue’yu yerleştireceğiniz yeri de size veriyor.

Hayır; bu soru iki farklı özelliği birbirine karıştırıyor.

Burada ölçüldüğü hâliyle sıcaklık 0, her görev için sekiz yeniden denemeyi bayt bayt aynı yaptı — modelin her seferinde aynı alıcı adresini uydurduğu görev dâhil. Yeniden üretilebilir ve yanlış. Belirlenimcilik yeniden denemeyi tekrarlanabilir kılar; kararı doğru kılmaz ve etkinin zaten gerçekleşmiş olması hakkında hiçbir şey yapmaz.

Ayrıca bir garanti de değil. Barındırılan API’ler sıcaklık 0’da bit bit aynı çıktı vaat etmiyor, çünkü batchleme ve indirgeme sırası o an işlenen diğer isteklere göre değişiyor. Idempotency örnekleme ayarlarından değil, bir anahtardan gelmeli.

Çünkü argümanlar modelin çıktısı ve modelin çıktısı tam da yeniden denemeler arasında değişen şey.

Bu ölçüldü: “pompa arızalandı, bilmesi gerekene haber ver” görevi için 0,7 sıcaklıkta sekiz yeniden deneme beş farklı alıcı adresi üretti. Beş argüman kümesi, beş hash, beş e-posta — anahtar, ihtiyaç duyulduğu anda hiçbir şeyi tekilleştirmemiş olurdu.

Anahtarı yukarı akıştaki, kararlı bir şeyden türetin: (oturum kimliği, adım indisi) ya da bir isteğin en fazla bir etki üretmesi gerektiğinde istek kimliği.

Genellikle bir insanla konuşan.

Oluşturulan bir kayıt silinebilir; karttan alınan bir ödeme, ücret ve ekstre satırı hesaba katılarak iade edilebilir; bir rezervasyon iptal edilebilir. Ama gönderilmiş bir mesajı silmek, insanların onu çoktan okumuş olmasını geri almaz ve gönderilmiş bir e-posta hiç geri alınamaz.

Telafi tablosunun bir olaydan sonra değil, yayına almadan önce yazılmasının nedeni bu. Telafi sütununun “yok” dediği yerde denetim daha erkene taşınır — eylemden önce bir onay kapısı (Bölüm 37) — ve mühendisliğin dışından birinin buna razı olması gerekir.

İş akışı kodunun içinde belirlenimci olmayan bir şey koşuyor.

Tekrar oynatma iş akışı işlevini baştan yeniden koşuyor ve tamamlanmış adımlar için kaydedilmiş sonuçları döndürüyor. Bu yalnızca işlev her seferinde aynı yolu izlerse doğru çalışır; dolayısıyla saati okumak, rastgele bir kimlik üretmek ya da iş akışı kodundan doğrudan bir API çağırmak bunu bozar.

Ajanlara özgü tuzak, bir kez söylenince apaçık: LLM çağrısı elinizdeki en az belirlenimci şey, dolayısıyla iş akışı kodunda verilen bir karar değil, kaydedilmiş bir adım olmalı. İş akışı düzenler; belirlenimsizlik kaydettiği adımlarda yaşar.

Evet, çünkü her biri ötekinin yakaladığı arızayı görmez.

Bir adım tavanı tek bir aracın bir saat takılmasını durdurmaz — takılan araç hiç yeni adım üretmez. Bir wall-clock zaman aşımı altmış hızlı adımın bir bütçeyi tüketmesini durdurmaz. Bir token tavanı aynı küçük dosyayı sonsuza dek okuyan bir döngüyü durdurmaz.

Üçünü de koyun ve herhangi birine çarpmayı cevapta, izde ve değerlendirmede görünen ayrı bir sonuç olarak ele alın. Bölüm 30’un sonlanma oranı o sayıdır ve koşuların üçte birinde tavana çarparken %85 doğru diye raporlanan bir ajan doğru ölçülmemiştir.

33.11 Mülakat soruları

Kaynak konusunda — Bölüm 2’deki çekincenin aynısı. Bu sorular, kitabın kullandığı iki güvenilir açık kaynakta tekrar tekrar karşımıza çıkıyor; bunlara bu bölümün kendi ölçümünün cevaplanabilir kıldığı sorular da ekleniyor.

33.11.1 Arızayı anlayıp anlamadığınızı sınayanlar

S: Ajanınızın işçisi koşunun ortasında çöküyor. Arkasında ne bıraktı?

Tamamlanmış araç çağrılarının zaten yaptığı her şeyi, tamamlandıklarına dair hiçbir kayıt olmadan. Naif bir yeniden başlatma onları yeniden yürütür. İki çözümü sayın: her adımı kaydeden ve tamamlanmış iş tekrarlanmasın diye geçmişi tekrar oynatan dayanıklı yürütme; ya da yeniden yürütmeyi zararsız kılan, yan etkili araçlardaki idempotency anahtarları. Çoğu sistemin ikincisine her hâlükârda ihtiyacı var, çünkü dayanıklı motorlar her adımı en az bir kez koşar: araç etkisini gerçekleştirdikten sonra ama motor sonucu kaydetmeden önce gelen bir çökme o adımı yeniden koşturur.

S: Bir LLM adımını yeniden denemek idempotent midir?

Varsayılan olarak değil; üstelik nedeni, insanların beklediği neden değil. Etkinin zaten gerçekleşmiş olmasının ötesinde, yeniden deneme aynı eylemi tekrarlamayabilir bile — burada ölçüldüğü hâliyle belirsiz bir görev, sekiz yeniden deneme boyunca beş farklı alıcı adresi üretti. Sıcaklık 0 yerel bir sunucuda yeniden denemeleri bayt bayt aynı yaptı, ama bu bir varyans azaltmadır, bir garanti değil ve barındırılan API’ler bunu vaat etmiyor.

S: Bir idempotency anahtarı nereden gelir?

Yukarı akıştaki, kararlı bir yerden — (oturum kimliği, adım indisi) ya da istek kimliği. Açıkça araç argümanlarının hash değeri değil, çünkü onlar modelin çıktısı ve yeniden denemeler arasında değişiyorlar. Sonra işe yarayıp yaramayacağını belirleyen iki ayrıntı: anahtar etkiyle atomik biçimde commit edilmeli — uzak bir çağrı için bu bir outbox demek, çünkü bir HTTP isteği işleminize katılamaz — ve saklama penceresi en uzun yeniden denemenizden uzun olmalı.

33.11.2 Bir tane yayına alıp almadığınızı sınayanlar

S: Görevleri yirmi dakika süren bir ajan için dağıtım tasarlayın.

Kuyruk destekli, çünkü yirmi dakikalık bir HTTP isteği bir dağıtımdan ya da bir yük dengeleyiciden sağ çıkmaz. Kabul edin, kuyruğa alın, bir kimlik döndürün; döngüyü bir işçi koşsun; istemci yoklasın ya da abone olsun. Ardından, sonradan eklenmesi acı verici üç şey: anlamlı ilerleme, döngünün adımlar arasında gerçekten kontrol ettiği iptal ve arızaların sonsuza dek yeniden denenmek yerine bir insana ulaşması için bir dead-letter queue. Üç tavanı ekleyin ve birine çarpmayı raporlanan bir sonuç yapın.

S: Konuşma nerede yaşıyor ve bu neye mal oluyor?

Süreç içinde tutmak en basiti ama bir kullanıcıyı tek bir örneğe sabitliyor; dolayısıyla yeniden başlatmalarda oturumlar kayboluyor. Oturum deposu varsayılan: her tura herhangi bir örnek hizmet ediyor, karşılığında deponun gecikmesi her turun üzerinde. İstemcide tutulan geçmiş sunucuyu durumsuz yapıyor ama istemcinin geçmişi düzenlemesine izin veriyor; bu da bir güvenlik sorusu (Bölüm 36). Üçünden hiçbiri token faturasını değiştirmiyor: geçmiş nerede saklanırsa saklansın model her turda konuşmanın tamamını alıyor. Dolayısıyla prompt token’ları oturum boyunca büyüyor — Bölüm 31’in maliyet aritmetiği.

S: Bir ajan bir müşteri kanalına yanlış bir mesaj gönderdi. İncelemeniz ne hakkında?

Model hakkında değil. O aracın bir telafisi olup olmadığı, telafinin gerçek olup olmadığı — insanların zaten okuduğu bir mesajı silmek bir geri alma değildir — ve değilse eylemin neden bir onay kapısının arkasında olmadığı hakkında. Sonra bunun modelin bir kararı değil, bir yineleme olup olmadığını sorun (idempotency sorusu); son olarak da koşunun bir tavana çarpıp çarpmadığını ve başarı diye raporlanıp raporlanmadığını.

S: E-posta gönderen bir ajan için yeni bir prompt’u nasıl yayına alırsınız?

Bir sürüm olarak: prompt, sabitlenmiş model, araç tanımları ve ayarlar, her ize kaydedilen tek bir kimlik altında. Sonra küçük bir dilimde bir kanarya; eski sürümle hata oranı, tavan çarpmaları, maliyet ve bir kalite sinyali üzerinden karşılaştırılır ve geri alma tetikleyicisi SLO’nun hata bütçesinden alınıp önceden kararlaştırılır. Düz bir gölge dağıtım değil, çünkü gerçek bir send_email’i olan gölge ajan gerçek e-posta gönderir; gölge dağıtım ancak araçlar kuru çalıştırma kipindeyken yapılabilir. Geri almanın neyi yapamayacağını da söyleyin: kanaryanın zaten gönderdiklerini geri çekmez.

33.12 2026 alet çantası

Bu bölümde neredeyse hiçbir şey bir kütüphane gerektirmedi — ölçüm yerel bir modele 72 çağrıydı ve geri kalan her şey kendi döngünüzün bir özelliği. Asıl önemli olan ikinci tablo; o da ikiye net biçimde ayrılıyor: yukarıda anlatılan belirlenimcilik sözleşmesini dayatan dayanıklı yürütme motorları ve dayatmayan düz görev kuyrukları.

Bu sayıların geldiği yığın. Python 3.14.5, Apple Silicon
kütüphane kullanılan sürüm burada ne işe yarıyor
ollama 0.24.0 yeniden deneme belirlenimciliği ölçümü için llama3.2:3b servis ediyor

Bilmeye değer, yukarıda kullanılmadı:

Daha geniş ekosistem, yazım anındaki sürümler
proje sürüm ne zaman başvurursunuz
temporalio 1.32.0 tekrar oynatmalı dayanıklı yürütme; olgun seçenek ve bu bölümde anlatılan belirlenimcilik sözleşmesi onunki
restate-sdk 1.0.4 daha hafif bir operasyonel ayak iziyle dayanıklı yürütme
dbos 2.31.0 zaten bir Postgres işletiyorsanız, Postgres destekli dayanıklı iş akışları
prefect 3.8.4 yeniden denemeler ve gözlemlenebilirlikle iş akışı düzenleme; ajanlardan çok veri hatlarına yakın
celery 5.6.3 kuyruk destekli işçilere ihtiyacınız varsa ve fazlasına yoksa, köklü Python görev kuyruğu
rq 2.12.0 Celery işin gerektirdiğinden fazlaysa, Redis üzerinde daha küçük bir kuyruk
ray 2.58.0 ajan makineler arasına yayıldığında dağıtık yürütme

33.13 Lab: bir adımı güvenle yeniden denenebilir kılın

En az bir yan etkili aracı olan bir ajan alın. Yoksa bir dosyaya ekleme yapan bir send_email yeter — amaç yinelemeyi görmek.

33.13.1 Adım 1 — yinelemeyi yeniden üretin

Ajanı çalıştırın, işçiyi araç yürütüldükten sonra ama sonuç kaydedilmeden önce öldürün ve yeniden başlatın. Etkileri sayın. İki tane almalısınız. Bunu bilerek gerçekleştiremiyorsanız, kazara gerçekleşmeyeceğini iddia edemezsiniz.

33.13.2 Adım 2 — kendi yeniden deneme belirlenimciliğinizi ölçün

Modelinizden üretim sıcaklığınızda aynı araç çağrısını on kez isteyin. Farklı argüman kümelerini sayın. Sonra bilerek eksik belirtilmiş bir görevle tekrarlayın. İki sayı arasındaki fark, prompt’unuzun sizi ne kadar koruduğunu gösterir.

33.13.3 Adım 3 — doğru yerden bir idempotency anahtarı ekleyin

Argümanlar üzerinden değil, (oturum kimliği, adım indisi) üzerinden anahtarlayın. Adım 1’i tekrarlayın. İkinci yürütme, eylemi yeniden yapmadan ilk sonucu döndürmeli.

33.13.4 Adım 4 — anahtarı ve etkiyi tek bir işleme koyun

Bir dosyaya ekleme bir veri tabanı işlemine katılamaz; bu yüzden bu adım için sahte send_email’in, idempotency kayıtlarını tutan aynı veri tabanına (SQLite yeter) bir satır eklemesini sağlayın ve ikisini tek bir işlemde yazın. Gerçek bir uzak çağrı için bunun yerine yukarıda anlatılan outbox’ı kullanın. Sonra adım 1’i, işçiyi etki ile kayıt arasında öldürerek yeniden koşun. Yineleme geri geliyorsa yazma atomik değildi — bu adımın amacı da o hatayı açığa çıkarmak.

33.13.5 Adım 5 — telafi tablosunu yazın

Yan etkili her araç, telafi eylemi ve telafinin gerçek bir geri alma olup olmadığına dair bir not. Tabloyu ekibinizle paylaşın. Telafisi olmayan satırlar onay kapısı gerektirenlerdir; bu da mühendislik dışından biriyle konuşulması gereken bir konu.

33.13.6 Adım 6 — üç tavanı koyun ve her birine çarpın

Adım, token, wall-clock süresi. Her tavana ayrı ayrı çarpan bir görev yazın — bir döngü, pahalı bir koşu, takılan bir araç — ve ajanın durduğunu, hangi tavana çarptığını raporladığını ve sonucun başarıdan ayırt edilebilir olduğunu doğrulayın.

33.13.7 Teslim edilecekler

Anahtardan önceki ve sonraki yineleme sayısı; açık ve belirsiz bir görev için farklı argüman sayılarınız; “gerçek bir geri alma mı?” sütunuyla telafi tablosu; ve üç tavanın her birinin ateşlendiğine ve raporlandığına dair kanıt.

33.14 Alıştırmalar

  1. Yinelemeyi zorlayın. Bir işçiyi, bir araç yürütülmesiyle sonucunun kaydedilmesi arasında öldürün. Etkileri sayın. Sonra bir idempotency anahtarı ekleyin ve sayının bire döndüğünü gösterin.

  2. Belirlenimcilik sayınız. Üretim modelinizden üretim sıcaklığınızda aynı araç çağrısını on kez isteyin ve farklı argüman kümelerini sayın. Bilerek belirsiz bir talimatla tekrarlayın. İkisini de raporlayın.

  3. Telafi tablosu. Üzerinde çalıştığınız bir sistemdeki yan etkili her araç için bir satır: araç, telafisi ve telafinin gerçek bir geri alma olup olmadığı. Bunun yerine onay kapısına ihtiyaç duyan satırları belirleyin.

  4. Atomiklik. Idempotency kaydınızın, koruduğu etkiyle birlikte commit edilip edilmediğini öğrenin. Etki uzak bir çağrıysa edilemez; o zaman outbox’ı uygulayın ve yinelemenin artık sizin işleminizle engellenmek yerine alıcının anahtarıyla soğurulduğunu gösterin. Her hâlükârda yinelemeye yol açan işlem sıralamasını (interleaving) kurgulayın ve bunun sizin trafiğinizde ne kadar olası olduğunu söyleyin.

  5. Üç tavanın üçüne de çarpın. Sırasıyla adımı, token’ı ve wall-clock süresini tüketen üç görev yazın. Her birinin ajanı durdurduğunu ve üçünün log (kütük) kayıtlarınızda ayırt edilebilir olduğunu doğrulayın.

  6. Durum kararı, fiyatlandırılmış. Yirmi turluk bir konuşma için toplam prompt token’larını hesaplayın ve Bölüm 31’in aritmetiğiyle gecikmeye ve maliyete çevirin. Bu sayı istemcide tutulan geçmişte de oturum deposunda da aynı çıkar. O yüzden farklı olanı fiyatlandırın: deponun her turdaki gecikmesi, yeni bir dağıtımda kaybolanlar ve geçmişini düzenleyebilen bir istemcinin neler yapabileceği. Sonra hangisini seçeceğinizi ve nedenini söyleyin.

  7. İptali ölçün. Koşan bir ajana bir iptal gönderin ve işin gerçekten durmasının ne kadar sürdüğünü ve o sırada sürmekte olan araç çağrısının tamamlanıp tamamlanmadığını ölçün. Adımlar arasında bayrağı kontrol eden hiçbir şey yoksa iptaliniz yok demektir.

  8. Kanaryayı boyutlandırın. Tanıdığınız bir ajan için, SLI’ı kesin biçimde belirtilmiş bir SLO yazın ve aylık hata bütçesini hesaplayın. Sonra %5’lik bir kanaryanın günde kaç koşu gördüğünü ve başarısızlık taban oranınızla kaç başarısızlık beklemesi gerektiğini hesaplayın. Bir sürüm ne kadar daha kötü olmalı ki bir günlük kanarya trafiği farkı görebilsin?

33.15 Daha derine

Okumadan önce: sisteminizde, etkisinin iki kez gerçekleşmesini en son isteyeceğiniz aracı seçin ve bugün bunu engelleyen bir şey olup olmadığını öğrenin. O cevap, bunlardan hangisine önce ihtiyacınız olduğuna karar veriyor.

Bu bölüm çoğunlukla operasyon (sistem işletimi) pratiğine dayanıyor. İlk tablo operasyon literatürü ve onu uygulayan motorlar; içindekilerin hiçbiri bir araştırma sonucu değil. İkincisi, bölümün atıf verdiği tek araştırma kaynağı.

Bu bölümün ötesi
kaynak ne olduğu
Making retries safe with idempotent APIs bu bölümün savunduğu anahtarın, kaydın ve işlem sınırının en açık yazımı
Stripe’s idempotent requests aynı tasarım, yayına alınmış açık bir API olarak — çoğu insanın fazla kısa ayarladığı saklama penceresi dâhil
Handling Overload istemci tarafı yeniden deneme bütçesi; başarısız işlerin yeniden denemelerinin bir kesintiye dönüşmesini engelleyen şey
Service Level Objectives Google SRE kitabının SLI ve SLO bölümü; oradaki hata bütçesi, Bölüm 33.9’daki geri alma tetikleyicisidir
Temporal’s workflow determinism rules tekrar oynatma sözleşmesi kesin biçimde ifade edilmiş; herhangi bir dayanıklı motoru benimsedikten sonra değil, önce okuyun
temporalio/sdk-python iş akışı/etkinlik ayrımı kodda; LLM çağrısının hangi tarafa düşmesi gerektiğini gösteren ayrım bu
restate / dbos-transact-py dayanıklılığa iki daha hafif yaklaşım; Temporal’ın sözleşmesinin hangi parçalarının zorunlu olduğunu görmek için yararlı
The saga pattern adlandırılmış bir örüntü olarak telafi; yukarıdaki telafi tablosunun yalnızca işaret ettiği arıza kipleriyle
celery / ray adımlar zaten idempotentken, dayanıklılık olmadan kuyruk destekli biçim
Birincil kaynak
konu kaynak
sıcaklık 0’ın batchlenmiş çıkarımda neden belirlenimci olmadığı He ve Thinking Machines Lab (2025)

33.16 Akılda kalması gerekenler

  • Bir ajan döngüsü bir istek işleyicisi değildir. Dakikalar sürer; dolayısıyla yürütme tavanı olan bir platform onu, bazı araçlar zaten çalıştıktan sonra, koşunun ortasında öldürür. Görevler bir isteğin ömrünü aşmaya başladığında biçim kuyruk desteklidir: kabul et, kuyruğa al, bir kimlik döndür ve ilerlemeyi, iptali ve bir dead-letter queue’yu hâlâ eklemesi ucuzken ekle.

  • Konuşmanın nerede yaşadığı token maliyeti değil, operasyon ve güven kararıdır. Süreç içinde tutmak bir kullanıcıyı tek bir örneğe sabitliyor ve dağıtımda oturumları kaybettiriyor. Oturum deposu varsayılan; gecikmesi her turun üzerinde. İstemcide tutulan geçmiş sunucuyu durumsuz yapıyor ve istemcinin onu düzenlemesine izin veriyor. Geçmiş nerede yaşarsa yaşasın model her turda tamamını alıyor; dolayısıyla prompt token’ları her turda büyüyor — Bölüm 31’in aritmetiği. Üstelik prefill, faturaya hâkim olmadan çok önce gecikmeye hâkim oluyor.

  • Dayanıklı yürütme, iş akışı kodunuza belirlenimcilik dayatır. Tekrar oynatma işlevi yeniden koşuyor ve tamamlanmış adımlar için kaydedilmiş sonuçları döndürüyor; dolayısıyla iş akışı kodunun içindeki saatler, rastgele kimlikler ve doğrudan API çağrıları bunu bozuyor. Elinizdeki en az belirlenimci şey LLM çağrısı, dolayısıyla yeri iş akışı kodu değil, kaydedilmiş bir adım. Bir motoru sonradan benimsemek genellikle döngüyü yeniden yazmak demek. Ve çökme anında koşmakta olan adım yeniden koşar; dolayısıyla araçların yine de idempotent olması gerekir.

  • Bir adımı yeniden denemek, bir isteği yeniden denemek değildir. Bölüm 32’nin yeniden deneme problemi yükün katlanmasıydı; buradaki ise yineleme. Ve yeniden deneme aynı eylemi tekrarlamayabilir bile: bilmesi gerekene haber ver dendiğinde bir model 0,7 sıcaklıkta sekiz yeniden deneme boyunca beş farklı alıcı adresi üretti.

  • Sıcaklık 0 varyans azaltmadır, idempotency değil. Bu ölçümde sıcaklık 0’da sekiz yeniden denemenin tamamı bayt bayt aynıydı — belirlenimci biçimde bir adres uyduran koşu dâhil. Yeniden üretilebilir, doğru demek değildir; üstelik barındırılan API’ler sıcaklık 0’da bit bit aynı çıktıyı zaten vaat etmiyor, çünkü batchleme ve indirgeme sırası o an işlenen diğer isteklere göre değişiyor.

  • Idempotency anahtarını yukarı akıştan türetin, asla modelin çıktısından değil. Araç argümanlarının hash değeri tam da ihtiyaç duyulduğunda başarısız oluyor — beş argüman kümesi, beş hash, beş e-posta demek. (oturum kimliği, adım indisi) ya da istek kimliğini kullanın, kaydı etkiyle birlikte commit edin — etki uzak bir çağrıysa bir outbox üzerinden, çünkü bir HTTP isteği işleminize katılamaz — ve ona en uzun yeniden denemenizden uzun bir saklama penceresi verin.

  • Telafi tablosunu aracı yayına almadan önce yazın. Bir kayıt silinebilir, bir ödeme iade edilebilir, bir rezervasyon iptal edilebilir. Gönderilmiş bir mesajı silmek, okunmuş olmasını geri almaz ve gönderilmiş bir e-posta hiç geri alınamaz. Telafi sütununun “yok” dediği yerde denetim daha erkene taşınır — bir geri alma yerine bir onay kapısı — ve bu, mühendisliğin dışından biri için bir karardır.

  • Üç tavanı da koyun, çünkü her biri diğerlerine kör. Bir adım tavanı takılan bir aracı durdurmaz; bir zaman aşımı bir bütçeyi tüketen altmış ucuz adımı durdurmaz; bir token tavanı aynı dosyayı sonsuza dek okuyan bir döngüyü durdurmaz. Birine çarpmak bir sonuçtur: yeri cevap, iz ve değerlendirmedir ve Bölüm 30’un sonlanma oranı, tavana çarparken doğru görünen bir ajanın yakalandığı yerdir.

  • Bir değişikliği tek bir sürümlenmiş yayın olarak ve önce bir dilime açarak yayına alın. Kod, prompt’lar, sabitlenmiş model, araç tanımları ve ayarlar her izde tek bir sürüm kimliğini paylaşır. Geri alma tetikleyicisi SLO’nun hata bütçesinden alınan bir kanarya, kötü bozulan bir sürümü yakalar; gölge bir ajanın araçları kuru çalıştırma kipinde olmalıdır; geri alma ise kodu geri getirir, kötü sürümün çoktan yarattığı etkileri değil.