Ajanınız ödeme uç noktasını çağırdı. İstek başarıyla iletildi, ödeme gerçekleşti ve ardından yanıt geri dönerken zaman aşımına uğradı. Ajan hiçbir zaman bir `200` görmedi, bu yüzden başarısızlık durumunda ona yapmasını söylediğiniz şeyi yaptı: yeniden denedi. Şimdi müşteri iki kez ücretlendirildi ve günlüklerinizde hiçbir şey hataya benzemiyor.
Bu, ajanları sıradan API istemcilerinden ayıran hata modudur. Bir insan “Öde” düğmesine bir kez tıkladığında bir dönen çark görür ve bekler. Yeniden deneme döngüsündeki bir ajan ise sessizlik görür ve bazen arka arkaya üç veya dört kez, herhangi bir insandan daha hızlı bir şekilde tekrar dener. Ajana daha fazla güvenilirlik katmak için eklediğiniz her yeniden deneme politikası, yinelenen yazma işlemlerini de daha olası hale getirir. Çözüm, idempotentliktir: tekrarlanan bir isteğin tek bir istekle aynı sonucu üretmesini sağlamak.
Bu kılavuz, HTTP düzeyinde idempotentliğin ne anlama geldiğini, bir ajanın gerçekten yeniden kullanabileceği anahtarların nasıl oluşturulacağını, sunucunun bunları yerine getirmek için ne saklaması gerektiğini ve gerçek bir müşteri iki kez faturalandırılmadan önce tüm sistemi nasıl test edeceğinizi kapsar. Yapay zeka ajanlarının üretimde neden bozulduğuna dair ana makalemizi okumadıysanız, yinelenen yazma işlemleri çoğu “ajan iki kez yaptı” raporunun altında yatan hata modudur.
Apidog, bunun test kısmında devreye giriyor. Idempotentlik, API'nize ve ajanızın araç katmanına dahil ettiğiniz bir şeydir. Sonrasında ihtiyacınız olan şey, aynı isteği iki kez göndermenin ve ikincisinin hiçbir şeyi değiştirmediğini kanıtlamanın bir yoludur, bu da CI'da kaydedip çalıştırabileceğiniz bir testtir.
Ajanlar neden insanlardan daha sık idempotentliği bozuyor?
Ajan trafiğiyle ilgili üç şey, yinelenenleri yaygın hale getirir.
Birincisi, yeniden deneme hacmidir. Ajan çerçeveleri, geçici ağ hataları bozuk bir çalışmanın en yaygın nedeni olduğundan, varsayılan olarak agresif bir şekilde yeniden dener. Ajan hata kurtarma kılavuzumuz, geri çekilmeyi ve devre kesicileri inceler ve içindeki her teknik, belirli bir isteğin sunucunuza ulaşma sayısını artırır.
İkincisi, bir zaman aşımının belirsizliğidir. Bir istek zaman aşımına uğradığında, istemci sunucunun onu işleyip işlemediği hakkında hiçbir şey öğrenmez. Bir proxy'den gelen `504`, yazma işleminin hiç gerçekleşmediği veya gerçekleştiği ve yanıtın kaybolduğu anlamına gelebilir. İnsanlar genellikle yeniden denemeden önce kontrol ederler. Ajanlar genellikle etmez, çünkü “önce kontrol et” modelin yapmaya karar vermesi gereken ek bir araç çağrısıdır.
Üçüncüsü, döngüdür. Bir görevi başarısız olan bir ajan, yalnızca başarısız olan adımı değil, tüm görevi yeniden başlatabilir. Eğer birinci adım bir sipariş oluşturuyor ve dördüncü adım başarısız oluyorsa, basit bir yeniden başlatma ikinci bir sipariş oluşturur. Çok adımlı ajanların bir betikten keskin bir şekilde farklılaştığı nokta burasıdır: yeniden deneme sınırı belirsizdir ve nerede başlayacağına kodunuz değil, model karar verir.
Bunları bir araya getirdiğinizde sorunun şeklini anlarsınız. Ajanların kötü istekler göndermesi değil. Doğru istekleri birden fazla kez gönderiyorlar.
Idempotentlik aslında neyi garanti eder?
Bir işlem, birçok kez gerçekleştirildiğinde tek bir kez gerçekleştirilmesiyle aynı etkiyi yarattığında idempotenttir. `GET`, `PUT` ve `DELETE`, HTTP semantik spesifikasyonu olan RFC 9110'da idempotent olarak tanımlanmıştır. `POST` ise değildir; tehlikeli işlemlerin genellikle `POST` çağrıları olma eğiliminde olmasının nedeni tam da budur: sipariş oluşturma, mesaj gönderme, transfer başlatma.
İki açıklama, birçok kafa karışıklığını önler.
Idempotent, güvenli ile aynı anlama gelmez. Güvenli bir yöntem hiçbir şeyi değiştirmez. `DELETE` idempotenttir ancak yıkıcıdır: beş kez çağırmak, kaynağı bir kez çağırmakla aynı şekilde silinmiş bırakır, ancak kaynak yine de gitmiş olur. Ajanların her iki özelliği de ayrı ayrı ele alması gerekir; bu, ajanlar için en az ayrıcalıklı API anahtarları hakkındaki yazımızın kimlik bilgileri tarafından ileri sürdüğü argümandır.
Idempotent, aynı yanıtla da aynı anlama gelmez. İkinci çağrı, ilkinin depolanmış sonucunu döndürebilir ve farklı bir durum kodu döndürebilir. Değişmemesi gereken şey sunucudaki durumdur. Tek bir ödeme. Tek bir sipariş. Tek bir e-posta.
Idempotency anahtarları: POST'u güvenli kılan desen
Standart çözüm, istekle birlikte gönderilen istemci tarafından oluşturulan bir anahtardır. Sunucu, anahtarı sonuçla birlikte kaydeder ve aynı anahtarı taşıyan sonraki herhangi bir istek, işi tekrar yapmak yerine kaydedilmiş sonucu döndürür.
Stripe, bu başlığı popüler hale getirdi ve Stripe idempotentlik belgeleri hala semantiğin en açık açıklamasıdır. Ayrıca, kendi başlık adınızı icat etmeden önce okunmaya değer olan Idempotency-Key başlık alanı olarak standartlaştırmak için bir IETF çabası da bulunmaktadır.
İstek şöyle görünür:
POST /v1/payments HTTP/1.1
Host: api.yourservice.com
Authorization: Bearer sk_live_...
Idempotency-Key: 9f2b7c14-6d3a-4b18-9d55-1e2a7c0b4f31
Content-Type: application/json
{
"amount": 4900,
"currency": "usd",
"customer_id": "cus_8812",
"description": "Pro plan, August"
}
Anahtar bir UUID'dir. Sunucu için “bu aynı mantıksal işlemdir” ötesinde hiçbir anlamı yoktur. Sunucu, bunu istek gövdesinin bir parmak izi ve ürettiği yanıtla birlikte saklar.
Ajanın yeniden kullanabileceği bir anahtar oluşturma
Çoğu ajan uygulamasının yanlış yaptığı yer burasıdır. Eğer araç sarmalayıcı her çağrıda yeni bir UUID oluşturursa, anahtar her yeniden denemede değişir ve idempotentlik hiçbir işe yaramaz. Anahtar, HTTP denemesine değil, mantıksal işleme bağlı olmalıdır.
Kural: ajan bir eylem gerçekleştirmeye karar verdiğinde anahtarı oluşturun ve bu kararın her yeniden denemesi için saklayın.
import uuid
class PaymentTool:
def __init__(self, client):
self.client = client
self._keys = {}
def charge(self, task_id, step_id, amount, customer_id):
# Her (görev, adım) için bir anahtar. Aynı adımın yeniden denemeleri bunu yeniden kullanır.
op = f"{task_id}:{step_id}"
if op not in self._keys:
self._keys[op] = str(uuid.uuid4())
return self.client.post(
"/v1/payments",
headers={"Idempotency-Key": self._keys[op]},
json={"amount": amount, "customer_id": customer_id},
)
Deterministik bir anahtar da işe yarar ve bellekteki bir sözlüğün aksine işlem yeniden başlatmalarından sağ çıkar:
import hashlib
def idempotency_key(task_id: str, step_id: str, payload: dict) -> str:
raw = f"{task_id}|{step_id}|{sorted(payload.items())}"
return hashlib.sha256(raw.encode()).hexdigest()[:32]
```Anahtarı görev çalışmasından ve adımdan türetin, asla bir zaman damgasından veya her denemede yeniden oluşturulan rastgele bir değerden türetmeyin. Eğer ajan tüm görevi yeniden başlatır ve gerçekten yeni bir ödeme yapmak isterse, görev kimliği değişir ve anahtar da değişir. İstediğiniz davranış budur.
Sunucunun yapması gerekenler
Başlığı doğru bir şekilde ele almak, yalnızca bir arama yapmaktan daha fazlasını gerektirir. Çalışan bir uygulama dört şey yapar:
Gelişte, anahtarı talep etmeye çalışın. Herhangi bir iş yapmadan önce onu benzersiz bir kısıtlama ile bir tabloya ekleyin. Ekleme başarısız olursa, başka bir deneme ona sahiptir.Eğer anahtar mevcutsa ve depolanan istek parmak izi farklıysa, `422` ile reddedin. Farklı bir gövdeye sahip aynı anahtar bir istemci hatası anlamına gelir ve eski sonucu sessizce döndürmek onu gizlerdi.Eğer anahtar mevcutsa ve ilk deneme hala devam ediyorsa, arayanın yarışmak yerine geri çekilmesi için `409` döndürün.İş bittiğinde, durum kodunu ve gövdeyi anahtara karşı depolayın, ardından sonraki her vuruş için onu döndürün.
CREATE TABLE idempotency_records (
key TEXT PRIMARY KEY,
request_hash TEXT NOT NULL,
state TEXT NOT NULL, -- devam ediyor | tamamlandı
response_status INT,
response_body JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL
);
Bir son kullanma süresi belirleyin. Yirmi dört saat, gerçekçi herhangi bir yeniden deneme penceresini kapsar ve anahtarları sonsuza kadar saklamak, tabloyu bir yük haline getirir. Stripe, anahtarları 24 saat sonra sona erdirir, bu kopyalanması makul bir varsayılan değerdir.
İkinci çağrının hiçbir şeyi değiştirmediğini test etmek
Idempotentlik inşa etmek işin yarısıdır. Bunun geçerli olduğunu kanıtlamak ise diğer yarısıdır ve genellikle atlanan kısım budur, çünkü özellik çalışsa da çalışmasa da "mutlu yol" aynı görünür.
Testi açıklamak basittir: isteği gönderin, sonucu yakalayın, aynı isteği tekrar gönderin ve sunucunun işi iki kez yapmadığını doğrulayın. Zor kısım son doğrulama adımıdır, çünkü sadece yanıt size bunu söylemeyecektir. İki başarılı ödeme de `200` döndürür.
Öyleyse yanıta göre değil, duruma göre doğrulama yapın:
İkinci yanıt gövdesi, kaynak Kimliği dahil olmak üzere ilkiyle eşleşmelidir. Yeni bir Kimlik, yeni bir kaynak oluşturulduğu anlamına gelir.Koleksiyonda yapılan sonraki bir `GET` isteği, iki değil, bir kayıt döndürür.Herhangi bir sayaç veya bakiye bir kez hareket etti.
Apidog'da bunu bir test senaryosu olarak düzenleyebilirsiniz: birinci adım sabit bir `Idempotency-Key` ile `POST` gönderir, ikinci adım onu tekrarlar ve üçüncü adım kaynağı listeler ve sayıyı doğrular. Birinci adımdaki yanıt Kimliğini bir değişkene kaydedin ve ikinci adımın aynı değeri döndürdüğünü doğrulayın. Tüm senaryo saklandığı için, ödeme yolundaki her değişiklikte CI'da çalışır, bu da regresyonların aslında ortaya çıktığı yerdir. Aynı teknik, API sözleşme testi kılavuzumuzdaki daha geniş kalıplara da taşınır.

Gerçek hataları yakaladıkları için ele alınmaya değer iki durum daha:
Aynı anahtar, farklı gövde. Sessiz bir başarı değil, `422` bekleyin.Eşzamanlı kopyalar. Her iki isteği aynı anda gönderin ve tam olarak birinin kazandığını onaylayın. Bu, sıralı bir testin asla ortaya çıkaramayacağı eksik benzersiz kısıtlamayı yakalar.
Mocklama burada da yardımcı olur. Ajanı hala inşa ediyorsanız ve ödeme API'si henüz mevcut değilse, ajanın yeniden deneme mantığının erken aşamada uygulanabilmesi için onu idempotentlik bilincine sahip bir yanıtla sahteleyin. Ajanların neden üretim yerine sahte API'leri kullanması gerektiği hakkındaki yazımız, bu alışkanlığın daha geniş bir gerekçesini sunmaktadır.
Bir anahtar ekleyemediğinizde
Bazen API size ait değildir ve idempotentlik desteği yoktur. Yine de tercihinize göre seçenekleriniz vardır.
İşlemi doğal olarak idempotent hale getirin. İstemcinin seçtiği bir kaynak yoluna yapılan `PUT` işlemi yapısı gereği idempotenttir: `PUT /orders/{client_order_id}`. Eğer API tasarımını kontrol ediyorsanız, bunu `POST` artı bir başlığa tercih edin. Ekstra bir tabloya ihtiyaç duymaz.Yazmadan önce kontrol edin. Ajanın, bir kayıt oluşturmadan önce aynı doğal anahtara sahip mevcut bir kaydı sorgulamasını sağlayın. Bu daha zayıftır, çünkü kontrol ve yazma arasındaki bir yarış hala iki kayıt üretebilir, ancak yaygın zaman aşımı durumunu ortadan kaldırır.Aşağı akışta yinelenenleri kaldırın. Eğer yazma bir mesaj veya bir olay ise, yinelenenleri kaldırma işlemini tüketiciye bırakın. Sabit bir mesaj kimliği ekleyin ve tüketicinin tekrarları atmasını sağlayın. Bu, olay odaklı sistemlerde standart bir uygulamadır vegüvenilir webhook kılavuzumuzdakirehberlikle eşleşir.Eylemi kısıtlayın. Geri döndürülemez ve idempotent hale getirilemeyen işlemler için önüne bir insan koyun. Bu,Yapay Zeka Ajan koruma önlemlerihakkındaki yazımızdan bir onay kapısı desenidir ve bir kopyanın maliyeti yeterince yüksek olduğunda doğru yanıttır.
Hangi çalışmanın ne yaptığını bilin
Idempotentlik yinelenenleri durdurur. Kaydı hangi denemenin oluşturduğunu söylemez ve bir olaydan sonra size sorulan soru budur.
Çalışma kimliğini işe bağlı tutun. Ajan kendi hizmetiniz olduğunda, bu, yukarıdaki anahtar türetmesinden gelen görev kimliği ve adım kimliğinin her denemede günlüğe kaydedilmesi anlamına gelir. Ajan, atanmış işi yürüten bir kodlama çalışma zamanı olduğunda, platform genellikle bunu sizin için tutar: Sharkly'de, her çalışma geldiği Göreve eklenir, yürütme durumu ve sonucu yorum dizisinin yanında depolanır, böylece tekrarlanan bir yazma, anonim bir yeniden deneme yerine belirli bir çalışmaya kadar izlenebilir.

Göndermeden önce bir kontrol listesi
Ajanın çağırabileceği her idempotent olmayan araç bir idempotentlik anahtarı gerektirir ve araç sarmalayıcı anahtar olmadan göndermeyi reddeder.Anahtarlar denemeden değil, görevden ve adımdan türetilir.Sunucu, iş yapmadan önce anahtarı talep eder, sonra değil.Farklı bir yük ile aynı anahtar, önbelleğe alınmış yanıt yerine bir hata döndürür.Eşzamanlı kopyalar, uygulama zamanlamasıyla değil, bir veritabanı kısıtlamasıyla ele alınır.Kaydedilmiş bir test, ikinci çağrının hiçbir şeyi değiştirmediğini kanıtlar ve CI'da çalışır.Anahtarlar belirli bir zamanlamayla sona erer ve tablo temizlenir.
Bu listeyi uyguladığınızda, iki kez ücretlendirme hikayesi imkansız hale gelir, bu da yeniden deneme politikanızın daha az agresif olmak yerine daha agresif hale gelebileceği anlamına gelir. Gerçek karşılığı budur: idempotentlik, bir ajanı tehlikeli hale getirmeden dayanıklı kılmanızı sağlayan şeydir.
Sıkça sorulan sorular
Salt okunur araçlar için idempotentlik anahtarlarına ihtiyacım var mı? Hayır. `GET` istekleri zaten idempotent ve güvenlidir, bu nedenle birini yeniden denemek size biraz gecikmeden başka bir şeye mal olmaz. Anahtarları, durum oluşturan, ücretlendiren, gönderen veya başka bir şekilde değiştiren çağrılar için ayırın.Anahtar nerede oluşturulmalı, ajanda mı yoksa araç sarmalayıcısında mı? Araç sarmalayıcısında, ajanın görev ve adım tanımlayıcılarına göre anahtarlanmış olarak. Modelin anahtarı oluşturmasına izin vermek bir hatadır: modeller yeniden denemelerde değerleri yeniden oluşturur ve görevler arasında çakışmalar üretebilir.Tekrarlanan bir istek hangi durum kodunu döndürmelidir? Orijinal çağrıdan depolanan durumu döndürün, böylece ilk `201` döndüren ikinci bir `POST` aynı gövdeyle tekrar `201` döndürür. Bazı API'ler, tekrarlamayı işaretlemek için `Idempotent-Replay: true` gibi bir başlık ekler, bu hata ayıklama için kullanışlıdır ve bunu görmezden gelen istemciler için zararsızdır.Anahtarlar ne kadar süreyle saklanmalıdır? Yirmi dört saat, neredeyse her yeniden deneme penceresini kapsar. Daha uzun saklama nadiren yardımcı olur ve tabloyu sınırsızca büyütür. Eğer bir istemci pencereden sonra yeniden denerse, bunu yeni bir işlem olarak değerlendirin.Bu, işlemleri (transactions) değiştirir mi? Hayır. Idempotentlik anahtarları, yinelenen isteklerin yinelenen etkiler üretmesini durdurur. İşlemler (transactions), tek bir isteği atomik tutar. Her ikisine de ihtiyacınız vardır ve veritabanınız izin verdiği sürece anahtar talebi, iş ile aynı işlemde yazılmalıdır.Bunu gerçek bir ödeme sağlayıcısı olmadan nasıl test edebilirim? Ajanı, yük uyuşmazlığında `422` dahil olmak üzere anahtar semantiğini uygulayan bir sahte API'ye yönlendirin.Yapay zeka ajanlarını sahte API'lere karşı test etmekılavuzumuz kurulumu kapsar ve sahte API ile yeniden deneme testinin aynı projede yaşamasını istiyorsanızApidog'u İndirin.
