Agen Anda memanggil API. API mengembalikan 429. Agen segera mencoba lagi, mendapat 429 lagi, lalu mengulanginya sampai proses mati atau biaya membengkak. Tidak ada yang sengaja menulis loop itu; biasanya ini muncul dari implementasi naif “penanganan error”. Pola ini juga sering muncul di diskusi Anthropic SDK.
Pemulihan error membedakan demo agen yang rapi dari sistem produksi yang dapat diandalkan. Model bukan satu-satunya masalah: kode Anda harus tahu apa yang dilakukan ketika tool call lambat, terkena rate limit, atau gagal total. Artikel ini membahas empat pola inti:
- Retry dengan exponential backoff dan jitter.
- Timeout pada setiap panggilan keluar.
- Circuit breaker untuk dependensi yang sedang gagal.
- Kunci idempoten untuk operasi yang mengubah data.
Di bagian akhir, Anda akan menguji semua jalur tersebut dengan mock. Untuk konteks yang lebih luas, baca mengapa agen AI rusak dalam produksi.
Anda tidak bisa menguji pemulihan terhadap API yang sehat
Dalam development, dependensi biasanya sehat: request berhasil, demo lancar, dan kode recovery tidak pernah dieksekusi. Akibatnya, pertama kali backoff, timeout, atau circuit breaker berjalan adalah saat insiden produksi—ketika pengguna sedang menunggu.
Aturannya sederhana: uji recovery dengan kegagalan yang disengaja.
Buat mock untuk API yang dipanggil agen, lalu program mock tersebut untuk mengembalikan:
429 Too Many Requests500 Internal Server Error- respons yang sangat lambat atau timeout
- body JSON yang tidak valid
- respons sukses setelah beberapa kegagalan
Arahkan tool agen ke mock tersebut dan verifikasi perilakunya. Dengan pendekatan ini, kegagalan menjadi skenario test yang dapat Anda ulang, bukan kejutan pukul 3 pagi. Apidog dapat digunakan untuk membuat mock dan menyusun urutan respons tersebut.
Retry dengan exponential backoff dan jitter
Retry adalah garis pertahanan pertama, tetapi retry langsung adalah pola yang berbahaya:
while True:
try:
return call_api()
except Exception:
continue
Jika layanan sedang terbebani, banyak klien akan gagal dan mencoba lagi secara bersamaan. Hasilnya adalah retry storm: layanan yang sudah kesulitan menerima beban tambahan tepat ketika sedang mencoba pulih.
Gunakan exponential backoff:
percobaan 1: tunggu 1 detik
percobaan 2: tunggu 2 detik
percobaan 3: tunggu 4 detik
percobaan 4: tunggu 8 detik
Lalu tambahkan jitter, yaitu variasi acak pada waktu tunggu. Jitter mencegah ribuan klien melakukan retry pada detik yang sama.
Contoh Python:
import random
import time
MAX_RETRIES = 4
MAX_DELAY_SECONDS = 30
def retry_with_backoff(operation):
for attempt in range(MAX_RETRIES):
try:
return operation()
except Exception:
if attempt == MAX_RETRIES - 1:
raise
base_delay = min(2 ** attempt, MAX_DELAY_SECONDS)
jitter = random.uniform(0, base_delay)
time.sleep(jitter)
Batasi dua hal:
- Jumlah retry, agar kegagalan permanen tidak menjadi loop tanpa akhir.
- Delay maksimum, agar aplikasi tidak menunggu terlalu lama di antara percobaan.
Tiga sampai lima percobaan biasanya cukup untuk error sementara. Lebih dari itu sering berarti Anda mengulang request yang tidak akan berhasil.
Anthropic SDK sudah melakukan retry untuk error koneksi dan status tertentu pada panggilannya sendiri, dengan batas yang dapat diatur melalui max_retries. Namun, mekanisme tersebut tidak otomatis mencakup API lain yang dipanggil tool agen Anda. Untuk endpoint berisiko tinggi seperti pembayaran, pelajari pola retry yang lebih ketat dalam logika percobaan ulang untuk API berisiko tinggi.
Tetapkan timeout pada setiap panggilan
Retry hanya berguna jika request benar-benar gagal. Kasus yang lebih buruk adalah request yang tidak pernah selesai: koneksi diterima, tetapi server tidak mengirim respons.
Tanpa timeout, satu socket yang menggantung dapat memblokir tool call dan membuat seluruh eksekusi agen macet.
Setiap panggilan keluar sebaiknya memiliki:
- Connection timeout: batas waktu untuk membuat koneksi.
- Read timeout: batas waktu untuk menunggu respons.
- Total execution budget: batas waktu total untuk satu run agen.
Contoh dengan httpx:
import httpx
timeout = httpx.Timeout(
connect=3.0,
read=10.0,
write=10.0,
pool=5.0,
)
with httpx.Client(timeout=timeout) as client:
response = client.get("https://api.example.com/data")
response.raise_for_status()
Pilih angka berdasarkan metrik latensi nyata, bukan tebakan. Gunakan p99 dependensi sebagai titik awal, lalu tambahkan ruang cadangan.
Jika API memiliki p99 sekitar 2 detik, timeout baca 3–5 detik mungkin masuk akal. Jika Anda menetapkan timeout terlalu rendah, request yang sebenarnya valid akan dibatalkan. Jika terlalu tinggi, agen akan menunggu dependensi mati terlalu lama.
Untuk respons streaming, gunakan anggaran tersendiri. Respons streaming yang panjang memang dapat berlangsung lebih lama daripada request JSON biasa.
Picu circuit breaker saat dependensi tidak berfungsi
Backoff cocok untuk kegagalan sementara. Namun, jika dependensi benar-benar mati, terus mencoba ulang hanya menambah beban dan memperpanjang waktu tunggu pengguna.
Circuit breaker memiliki tiga status:
| Status | Perilaku |
|---|---|
closed |
Request berjalan normal dan kegagalan dihitung. |
open |
Request langsung gagal tanpa memanggil dependensi. |
half-open |
Satu atau beberapa request probe diizinkan untuk memeriksa pemulihan. |
Alurnya:
- Dependensi mulai gagal.
- Jumlah kegagalan melewati ambang batas.
- Breaker berubah ke
open. - Request berikutnya gagal cepat selama cooldown.
- Setelah cooldown, breaker berubah ke
half-open. - Jika probe berhasil, breaker kembali ke
closed. - Jika probe gagal, breaker kembali ke
open.
Pseudocode sederhana:
if breaker.is_open():
raise DependencyUnavailable("Payment API sedang tidak tersedia")
try:
result = call_payment_api()
breaker.record_success()
return result
except Exception:
breaker.record_failure()
if breaker.failure_count >= 5:
breaker.open_for(seconds=30)
raise
Untuk agen, circuit breaker mengubah puluhan timeout lambat menjadi satu error cepat yang dapat ditangani dengan jelas.
Pasang circuit breaker per dependensi, bukan global. API pencarian yang gagal tidak boleh menghentikan agen menggunakan API penagihan yang sehat.
Amankan retry dengan kunci idempoten
Retry mengasumsikan operasi aman untuk diulang. Untuk operasi yang mengubah state, asumsi ini sering salah.
Contoh masalah:
- Agen mengirim
POST /charge. - Server berhasil memproses pembayaran.
- Respons timeout sebelum diterima agen.
- Agen menganggap request gagal.
- Agen melakukan retry.
- Pelanggan ditagih dua kali.
Solusinya adalah kunci idempoten.
Klien membuat satu ID unik untuk setiap tindakan logis, lalu mengirimkannya dalam header Idempotency-Key:
import uuid
import httpx
idempotency_key = str(uuid.uuid4())
response = httpx.post(
"https://api.example.com/charge",
headers={
"Idempotency-Key": idempotency_key,
},
json={
"customer_id": "cus_123",
"amount": 50000,
},
)
Kunci tersebut harus dibuat sebelum loop retry, bukan di dalamnya:
# Benar: satu kunci untuk semua retry
idempotency_key = str(uuid.uuid4())
for attempt in range(3):
send_charge_request(idempotency_key)
# Salah: setiap retry menjadi tindakan baru
for attempt in range(3):
idempotency_key = str(uuid.uuid4())
send_charge_request(idempotency_key)
Di sisi server, request dengan kunci yang sama harus mengembalikan hasil operasi pertama alih-alih menjalankan aksi lagi.
Gunakan kunci idempoten untuk setiap aksi yang membuat atau mengubah state, misalnya:
- pembayaran
- pesanan
- email terkirim
- pembuatan record
- pembaruan data
- pengiriman pesan
Panggilan read-only seperti GET /users/123 umumnya aman di-retry tanpa kunci idempoten. Panduan kunci idempoten membahas implementasi klien dan server secara lebih lengkap.
Bertahan dari rate limit dan loop RateLimitError
Rate limit memerlukan penanganan khusus karena server biasanya memberi instruksi kapan Anda boleh mencoba lagi.
Respons melebihi batas laju biasanya berupa 429 dengan header berikut:
Retry-After: 30
Artinya: tunggu minimal 30 detik sebelum melakukan retry.
Jangan mengabaikan header itu. Jika server meminta Anda menunggu 30 detik tetapi kode Anda mencoba lagi dalam 2 detik, Anda akan mendapat 429 lagi dan membangun loop RateLimitError.
Implementasi dasar:
import random
import time
def get_retry_delay(response, attempt):
retry_after = response.headers.get("Retry-After")
if retry_after:
try:
return float(retry_after)
except ValueError:
pass
base_delay = min(2 ** attempt, 30)
return random.uniform(0, base_delay)
Urutan prioritasnya:
- Jika ada
Retry-After, hormati nilainya. - Jika header tidak ada atau tidak valid, gunakan exponential backoff dengan jitter.
- Tetap batasi jumlah retry.
- Setelah batas tercapai, kembalikan error yang jelas.
Anthropic SDK sudah menghormati Retry-After untuk request SDK itu sendiri. Namun, Anda tetap perlu menerapkan aturan yang sama untuk API lain yang digunakan oleh tool agen Anda. Masalah serupa juga dibahas di thread Anthropic SDK ini.
Selain recovery, lakukan pencegahan secara proaktif. Jika provider memberi batas request per menit, gunakan rate limiter seperti token bucket agar agen tidak terus-menerus menabrak batas tersebut.
Cara menguji jalur pemulihan
Pola recovery hanya berguna jika sudah terbukti berjalan. Jangan menunggu API produksi gagal untuk mengetahui apakah retry dan timeout Anda benar.
Gunakan alur test berikut.
1. Mock dependensi
Buat mock untuk setiap API yang dipanggil tool agen. Dengan mock, Anda mengontrol:
- status code
- header
- response body
- delay
- urutan respons
Tidak ada pembayaran, email, atau perubahan data nyata yang terjadi selama pengujian.
2. Program urutan respons
Contoh satu skenario recovery penuh:
Request 1 → 429 + Retry-After: 2
Request 2 → 500
Request 3 → 200 + body valid
Satu endpoint dapat mensimulasikan kegagalan sementara, rate limit, lalu pemulihan.
3. Arahkan agen ke URL mock
Konfigurasikan base URL tool agen agar menggunakan mock, bukan layanan produksi:
API_BASE_URL = "https://mock.example.test"
Jalankan skenario agen dari awal hingga akhir.
4. Verifikasi perilaku, bukan hanya hasil akhir
Pastikan test memeriksa hal-hal berikut:
- Agen menunggu minimal 2 detik setelah menerima
429. - Agen menghormati
Retry-After. - Agen melakukan retry setelah
500. - Agen berhasil pada respons ketiga.
- Agen tidak melebihi batas retry.
- Agen mengembalikan error yang jelas jika semua retry gagal.
Tambahkan skenario khusus untuk tiap pola:
| Skenario | Verifikasi |
|---|---|
| Semua request gagal | Agen berhenti pada batas retry. |
| Respons menggantung | Timeout aktif dan agen tidak macet. |
| Kegagalan beruntun | Circuit breaker berubah ke open. |
| Respons aksi tulis hilang | Retry mengirim Idempotency-Key yang sama. |
Untuk test idempoten, buat mock menerima request yang mengubah data, proses request pertama, lalu putuskan respons. Ketika agen melakukan retry, periksa bahwa kedua request membawa header yang sama:
Idempotency-Key: 5a6f7c2e-...
Jika retry menggunakan kunci baru, Anda telah menemukan potensi pengiriman ganda sebelum pelanggan menemukannya. Untuk pendekatan yang lebih menyeluruh, lihat panduan menguji agen yang memanggil API Anda.
Daftar periksa pemulihan error
Sebelum mendorong agen ke produksi, pastikan semua poin ini terpenuhi:
- [ ] Setiap panggilan keluar memiliki connection timeout dan read timeout.
- [ ] Eksekusi agen memiliki anggaran waktu total.
- [ ] Retry menggunakan exponential backoff dengan jitter.
- [ ] Jumlah retry dan delay maksimum dibatasi.
- [ ] Respons
429membaca dan menghormatiRetry-After. - [ ] Circuit breaker dipasang per dependensi.
- [ ] Request yang mengubah state menggunakan kunci idempoten stabil.
- [ ] Jalur gagal mengembalikan error jelas, bukan loop atau penantian tanpa batas.
- [ ] Semua jalur recovery diuji terhadap mock yang sengaja gagal.
Jika semua poin ini tercentang, agen Anda pulih karena desain, bukan karena keberuntungan.
Di mana Apidog sesuai, dan di mana tidak
Peran alat harus jelas. Apidog bukan framework agen, model host, atau runtime orkestrasi. Apidog tidak membangun atau menjalankan agen Anda, dan tidak menilai output model.
Namun, Apidog relevan pada lapisan API yang dipanggil agen Anda—lapisan tempat kegagalan, retry, dan validasi request terjadi.
Gunakan Apidog untuk:
- Mem-mock dependensi yang dipanggil agen.
- Membuat respons gagal seperti
429,500, timeout, atau body salah format. - Menyusun urutan respons untuk menguji recovery.
- Memvalidasi request yang diterima mock.
- Memastikan header seperti
Idempotency-Keyada dan stabil. - Memastikan jumlah request tidak melebihi batas yang Anda tetapkan.
Itulah kecocokan yang tepat: mock kegagalan yang harus ditangani agen, lalu periksa request yang dikirim agen saat mencoba pulih.
Pertanyaan yang sering diajukan
Bukankah Anthropic SDK sudah menangani retry?
Untuk panggilan SDK-nya sendiri, ya. Anthropic SDK melakukan retry untuk error tertentu dengan exponential backoff dan menghormati Retry-After. Anda dapat mengatur batasnya melalui max_retries.
Namun, mekanisme itu tidak mencakup API lain yang dipanggil tool agen Anda. Untuk API pembayaran, CRM, pencarian, email, atau sistem internal, Anda perlu menerapkan pola yang sama sendiri.
Kapan saya memerlukan kunci idempoten?
Gunakan untuk setiap operasi yang membuat atau mengubah state: pembayaran, pesanan, pesan, email, atau record baru. Buat satu kunci per tindakan logis dan gunakan kunci tersebut untuk seluruh retry.
Apakah semua error perlu di-retry?
Tidak. Jangan retry error yang jelas bersifat permanen, seperti validasi request yang gagal atau autentikasi yang tidak valid. Retry terutama cocok untuk error sementara seperti timeout, kegagalan koneksi, 429, dan sebagian 5xx.
Latih satu kegagalan minggu ini
Anda tidak perlu membangun semua pola sekaligus. Mulailah dari risiko terbesar di sistem Anda—biasanya loop rate limit atau retry pada request yang tidak idempoten.
Buat satu mock yang mengembalikan 429, tambahkan Retry-After, lalu lihat apakah agen menunggu dengan benar. Atau buat mock yang memproses request pembayaran tetapi menjatuhkan responsnya, lalu pastikan retry memakai satu Idempotency-Key yang sama.
Ketika Anda melihat agen melakukan backoff yang benar dan mencegah aksi ganda, Anda punya alasan yang lebih kuat untuk mempercayainya daripada sekadar demo yang mulus.

Top comments (0)