Agentic AI guardrail'leri: while(true) döngüsünü token bütçeni yakmasından durdurmak
Vibes-döngüsünü production'da gerçekten çalıştırabileceğin bir şeye çeviren dört guardrail: bütçe zarfı, retry eğrileri, break koşulları ve gerçek bir fallback zinciri.
Geçen yazımda agentic AI'ın çoğunlukla vibes'larla yazılmış bir while(true) döngüsü olduğunu söylemiştim. O yazıdan birkaç "peki fallback ne?" mesajı geldi, dürüst cevap: fallback döngünün gövdesi. Onsuz, sen SSH'le bağlanıp kill -9 çekene kadar daire içinde para yakan bir şey kalır.
Bu yazı teknik devamı. Vibes-döngüsünü production trafiğinde gerçekten çalıştırabileceğin bir şeye çeviren 4 guardrail.
Guard 1 — İlk çağrıdan önce bütçe zarfı
Agent failure'larının çoğu mantıksal değil — finansal. Token maliyeti istek başına $0,0001'ken önemsiz, ama tek bir kullanıcı sorgusu planner "bu arada hava durumuna da bakayım" deyince 47 tool çağrısı doğurunca aniden önemli hale geliyor.
Çözüm: giriş noktasında sert bir zarf. "%80'de uyar" gibi yumuşak bir şey değil — exception fırlatan sert bir tavan.
class BudgetGuard {
private spent = 0;
constructor(private readonly cap: number) {}
charge(inputTokens: number, outputTokens: number) {
this.spent += inputTokens + outputTokens;
if (this.spent > this.cap) {
throw new BudgetExceeded(this.spent, this.cap);
}
}
remaining() {
return Math.max(0, this.cap - this.spent);
}
}
const budget = new BudgetGuard(50_000); // token, dolar değil — faturalama anında çevirHer model çağrısı devam etmeden önce charge çağırır. Production'da ilk çalıştırdığım agent, bir sipariş bakması gerekirken bir servisi refaktör etmeye karar verdi. Ertesi sabahki fatura, 1 cent maliyetli olması gereken bir sorgu için 100 dolardı. Bu guard'dan sonra aynı sorgu hop 8'de BudgetExceeded: 51200 > 50000 ile patlar, kibar bir "yer kalmadı" mesajı döndürür, en kötü durum 10 cent.
Guard 2 — Zorlamayan, iyileştiren retry eğrileri
"Exponential backoff" Google'ladığında bir denemeden diğerine delay'i ikiye katlayan 5 satırlık bir kod görürsün. Backoff'un amacı bu değil. Amaç kibar retry'ları boşluklamak DEĞİL. Amaç downstream servise iyileşme zamanı vermek.
Gerçek eğri:
- Jitter (1000 instance senkron retry yaparsa aynı servisi öldürür)
- Cap (17 dakika beklemek bir API çağrısı için fail etmekten daha kötü)
- Circuit breaker (peş peşe 50 fail olduysa 51. de fail olacak)
async function callWithBackoff<T>(
fn: () => Promise<T>,
{ maxAttempts = 5, baseMs = 200, capMs = 8_000 } = {},
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
if (!isRetryable(err) || attempt === maxAttempts) throw err;
const delay = Math.min(capMs, baseMs * 2 ** (attempt - 1));
const jittered = delay * (0.5 + Math.random() / 2);
await sleep(jittered);
}
}
throw new Error("ulaşılamaz");
}isRetryable eğriden daha önemli. 429 ve 503 retryable. 400 ve 401 değil — onları retry etmek aynı hatayı daha yüksek sesle yapmak demek.
Guard 3 — Döngüden ne zaman çıkılır
while(true) yazmanın en zor yanı while değil. break yazmak. Beş koşul gövdeye girer:
while (true) {
if (budget.remaining() < 1_000) break; // 1. finansal
if (Date.now() - started > 30_000) break; // 2. duvar saati
if (lowConfidenceStreak >= 3) break; // 3. model 3 kez "emin değilim" dedi
if (outputUnchanged >= 2) break; // 4. sadece dönüyor
if (cancellationToken.aborted) break; // 5. kullanıcı vazgeçti
const step = await agent.step({ budget: budget.remaining() });
budget.charge(step.inputTokens, step.outputTokens);
if (step.isDone) break;
lowConfidenceStreak = step.confidence < 0.4 ? lowConfidenceStreak + 1 : 0;
outputUnchanged = sameAsLast(step.output) ? outputUnchanged + 1 : 0;
}"Output unchanged" kontrolü production döngülerinin çoğunu yakalayan koşul. Düşünmekte sıkışmış bir agent çoğunlukla aynı kısmi cevabı 4-5 kez peş peşe üretir, bir sonraki sample'ın doğru olacağını umarak. Olmuyor. Çık, vazgeç, elindekini döndür.
Şu an aklımdaki agent, kimse ona vazgeç demediği için 47 kez retry yaptı. 48. denemesi 47. ile aynıydı. Retry maliyeti, orijinal cevabın GPT-4 fiyatlarındaki maliyetinden yüksekti.
Guard 4 — Fallback message değil fallback zinciri
"Elimden geleni yaptım" fallback değil. Teslim olmak. Gerçek fallback bir zincir:
async function answer(query: string): Promise<string> {
try {
return await runAgent(query, { model: "claude-opus" });
} catch (err) {
if (err instanceof BudgetExceeded) {
// Daha ucuz model, daha sıkı döngü
return await runAgent(query, { model: "claude-haiku", maxSteps: 3 });
}
if (err instanceof TimeExceeded) {
// Agent yok — tool'suz direkt LLM çağrısı
return await directCompletion(query);
}
// Diğer her şey: rule-based
return ruleBased(query) ?? "Emin olduğum bir cevabım yok.";
}
}Zincir önemli çünkü hatalar farklı. Bütçe aşımı "daha ucuz düşünmem lazım" demek. Zaman aşımı "daha basit düşünmem lazım" demek. Logic hatası "düşünmemem lazım" demek. Her dal sebep üzerinden retry etmeli, semptom üzerinden değil.
Kapanış
Guardrail'siz agentic AI, daire içinde autocomplete'tir. En zor kısım while(true) yazmak değil. break yazmak.
Eğer tek bir şey alacaksan: her şeyden önce döngünün etrafına bütçe zarfını koy. Diğer üç guard kalite için. Bütçe hayatta kalma için.
Son not: bu pattern'leri 7 milyon ürünlü bir marketplace katalogu için tool seçen bir sisteme uyguluyorum. API maliyeti aylık $40 — iki iterasyon öncesinde aynı trafik $4.000/ay olabilirdi. Fark daha akıllı agent değil. Daha iyi break'ler.
// madem buradasın
- 9 dk okuma
Agentic AI çoğunlukla vibe'larla yazılmış bir while(true)
Uzun süreli agent döngülerini production'da çalıştırmaktan çıkan dersler, gerçekten işe yarayan fallback pattern'leri ve agent'ın aynı tool'u 47 kere denediği gün.
agentic-aiproductionengineering-lessons - 9 min okuma
Ayda 4.000 dolardan 40 dolara: agent guardrail'lerinin gerçek maliyet eğrisi
Kanı durduran şey daha küçük bir model değildi — planlayıcının fazladan adım iştahını kesmekti.
agentic-aiproductioncost-engineering