AI Agent Geliştirmek Kolay, Production'a Çıkarmak Zor
Son birkaç yılda üretken yapay zekâ tarafında yaşanan gelişmelerle birlikte, AI Agent kavramı yazılım dünyasının en çok konuşulan konularından biri haline geldi. Artık sadece bir modele soru sorup cevap almak yerine, karar verebilen, farklı araçları kullanabilen ve gerektiğinde kendi çıktısını değerlendirerek yeni aksiyonlar alabilen sistemler geliştirmeye çalışıyoruz.
Kulağa oldukça etkileyici geliyor ve gerçekten de öyle. Ancak iş production ortamına geldiğinde, birçok geliştiricinin aynı gerçekle karşılaştığını görüyorum: Demo'da kusursuz çalışan agent, gerçek kullanıcılarla buluştuğunda beklenmedik davranışlar sergilemeye başlıyor.
Demo ile Production Arasındaki Büyük Fark
Bir AI Agent'ın temel çalışma mantığı aslında oldukça basittir.
- Kullanıcıdan isteği al.
- Ne yapılacağına karar ver.
- Gerekli tool veya uzman (specialist) servisi çalıştır.
- Sonucu değerlendir.
- Gerekirse yeni bir karar ver.
Beyaz tahtaya çizildiğinde bu akış oldukça temiz görünür. Hatta birkaç saat içerisinde çalışan bir örnek bile hazırlanabiliyor.
Fakat production ortamında kullanıcılar hiçbir zaman demo senaryolarındaki gibi davranmaz.
- İstekler belirsiz olabilir.
- Tool'lar beklenenden uzun sürebilir.
- Dış servisler hata verebilir.
- Model aynı isteğe farklı kararlar verebilir.
- Bir agent sonsuz döngüye girebilir.
İşte tam bu noktada klasik backend geliştirme alışkanlıklarımız yeterli olmamaya başlıyor. Çünkü artık elimizde deterministik çalışan bir servis değil, olasılıklarla çalışan bir karar mekanizması bulunuyor.
AI Agent'ları Klasik Servisler Gibi Düşünmeyin
Geleneksel backend uygulamalarında bir metodun davranışı çoğunlukla nettir. Aynı parametreleri verdiğinizde aynı sonucu almayı beklersiniz. Bir hata oluşursa exception fırlatılır ve onu yönetirsiniz.
AI Agent tarafında ise işler biraz farklıdır.
Aynı kullanıcı isteği iki farklı çalıştırmada iki farklı tool seçebilir. Hatta bazen tamamen farklı bir çözüm yolu izleyebilir. İlginç olan ise her iki sonucun da teknik olarak doğru olmasıdır.
Bu durum ilk başta geliştiricileri rahatsız edebilir. Çünkü yıllardır deterministik sistemler geliştirmeye alışığız. Ancak AI Agent geliştirirken asıl hedefimiz her zaman aynı kararı vermesi değildir.
Asıl hedefimiz, verdiği kararın nedenini anlayabilmek ve gerektiğinde bu karardan güvenli şekilde geri dönebilmektir.
Production'da güvenilir bir AI Agent; hiç hata yapmayan agent değil, hata yaptığında bunu yönetebilen agent'tır.
Asıl Problem Model Değil, Döngünün Kendisi
Çoğu zaman yaşanan problemlerin sebebi LLM değildir. Asıl sorun, agent'ın etrafındaki çalışma döngüsünün yeterince sağlam tasarlanmamış olmasıdır.
Birçok projede aşağıdaki varsayımların farkında olmadan yapıldığını görüyorum:
- Model her zaman doğru tool'u seçecek.
- Her tool başarılı şekilde çalışacak.
- İlk denemede doğru cevap alınacak.
- Timeout yaşanmayacak.
- Retry mekanizması problemi mutlaka çözecek.
Ne yazık ki production ortamı bu varsayımların neredeyse tamamını kısa sürede boşa çıkarıyor.
İşte bu yüzden AI Agent geliştirirken yalnızca prompt tasarlamak veya tool yazmak yeterli olmuyor. Aynı zamanda başarısızlığı da sistemin doğal bir parçası olarak kabul eden bir mimari kurmanız gerekiyor.
Bu Yazıda Neler Bulacaksınız?
Bu yazıda production ortamında en sık karşılaşılan dört temel AI Agent problemini ele almaya çalışacağım. Bunların neden oluştuğunu, sistemi nasıl etkilediğini ve .NET tarafında uygulanabilecek pratik çözüm yaklaşımlarını birlikte inceleyeceğiz.
İnceleyeceğimiz başlıklar şunlar olacak:
- Non-deterministic kararların yönetimi
- Tool ve specialist sonuçlarının doğru modellenmesi
- Sonsuz retry döngülerinin engellenmesi
- Timeout'ların sistem davranışına doğru şekilde dahil edilmesi
Göreceğiniz çözümlerin hiçbiri karmaşık algoritmalar veya yeni AI modelleri gerektirmiyor. Aksine, oldukça basit yazılım prensiplerinin AI Agent mimarisine uygulanmasından ibaret.
Çünkü production'da güvenilir bir agent geliştirmenin sırrı daha büyük modeller kullanmak değil, başarısız olabileceğini en baştan kabul ederek sistemi buna göre tasarlamaktır.
1. 🧭 Non-Deterministic Kararlar: Aynı İstek, Farklı Sonuçlar
AI Agent geliştirmeye yeni başlayanların en çok şaşırdığı konulardan biri şudur:
Aynı isteği iki kez gönderdim ama agent bu sefer farklı bir tool seçti.
İlk tepki genellikle "Bir yerde bug var." oluyor. Oysa çoğu zaman ortada herhangi bir hata bulunmuyor.
Büyük dil modelleri deterministik algoritmalar değildir. Aynı kullanıcı isteğini değerlendirirken farklı bir uzmanı (Specialist), farklı bir tool'u veya tamamen farklı bir çözüm yolunu tercih edebilirler.
Önemli olan nokta şu:
Bu davranış çoğu zaman sistemin bozulduğu anlamına gelmez. Aksine, modele karar verme özgürlüğü tanıdığınızın doğal bir sonucudur.
Neden Böyle Oluyor?
Klasik yazılım geliştirme alışkanlıklarımız bize aynı girdinin her zaman aynı çıktıyı üretmesini beklemeyi öğretir.
Örneğin aşağıdaki kod her zaman aynı sonucu döndürür.
if (amount > 1000)
{
return PremiumApproval();
}
return StandardApproval();
Burada sürpriz yoktur. Karar tamamen yazılımcının belirlediği kurallara göre verilir.
AI Agent tarafında ise durum biraz farklıdır.
Model yalnızca kuralları takip etmez; isteğin bağlamını yorumlar, olasılıkları değerlendirir ve en uygun gördüğü aksiyonu seçmeye çalışır.
Dolayısıyla aşağıdaki iki istek insanlar için aynı anlamı taşısa bile model bunları farklı şekilde yorumlayabilir.
"Son üç aylık satış raporunu hazırla."
"Geçtiğimiz çeyreğin satışlarını analiz et."
İnsan gözüyle bakıldığında bunlar aynı istektir. Ancak model bunlardan birini Reporting Specialist'e, diğerini Analytics Specialist'e yönlendirebilir.
İşte production ortamında geliştiricileri zorlayan ilk konu tam olarak budur.
Yapılan En Büyük Hata
Bu noktada birçok ekip problemi çözmek yerine modeli kısıtlamaya çalışıyor.
- Temperature değerini sıfırlamak
- Prompt'u gereğinden fazla katı hale getirmek
- Tool seçimlerini elle sınırlandırmak
- Her şeyi deterministic hale getirmeye çalışmak
Bunlar bazen daha tutarlı sonuçlar üretse de asıl problemi çözmüyor, aksine daha sınırlıyor.
Çünkü production ortamında önemli olan modelin her zaman aynı kararı vermesi değildir.
Önemli olan, neden o kararı verdiğini anlayabilmektir.
Karar Sürecini Görünür Hale Getirin
Production sistemlerinde benim tercih ettiğim yaklaşım, agent'ın verdiği kararı ve bunun gerekçesini birlikte loglamaktır.
Böylece haftalar sonra gelen bir destek kaydında bile sistemin neden belirli bir specialist'i seçtiğini anlayabilirsiniz.
public sealed record PlannerDecision(
string Specialist,
string Reason);
public sealed class Planner
{
private readonly IEnumerable<ISpecialist> _specialists;
private readonly ILogger<Planner> _logger;
public Planner(
IEnumerable<ISpecialist> specialists,
ILogger<Planner> logger)
{
_specialists = specialists;
_logger = logger;
}
public async Task<string> RunAsync(
string goal,
CancellationToken cancellationToken)
{
var decision = await ChooseSpecialistAsync(goal, cancellationToken);
_logger.LogInformation(
"Planner selected {Specialist}. Reason: {Reason}",
decision.Specialist,
decision.Reason);
var specialist = _specialists.Single(x => x.Name == decision.Specialist);
var result = await specialist.InvokeAsync(goal, cancellationToken);
return result.Summary;
}
}
Buradaki Reason alanı ilk bakışta gereksiz gibi görünebilir.
Ancak production ortamında en değerli bilgilerden biri haline gelir.
Çünkü loglara baktığınızda artık sadece şunu görmezsiniz:
Analytics Specialist selected.
Bunun yerine çok daha anlamlı bir kayıt elde edersiniz.
Analytics Specialist selected.
Reason:
The request contains trend analysis and historical comparison.
Artık "Agent neden bunu seçti?" sorusunun cevabı elinizde olur.
Production'da Bunun Faydası Nedir?
Düşünün ki kullanıcı destek ekibine şu şikayet geliyor:
"Dün aynı raporu oluşturdu, bugün farklı sonuç verdi."
Eğer yalnızca hangi tool'un çalıştığını logluyorsanız, problemi anlamanız oldukça zor olacaktır.
Fakat kararın gerekçesi de kayıt altındaysa aşağıdaki soruların cevabını birkaç dakika içerisinde bulabilirsiniz.
- Model gerçekten farklı mı düşündü?
- Kullanıcının cümlesi değişmiş miydi?
- Prompt güncellendi mi?
- Yeni bir specialist devreye mi girdi?
- Yoksa gerçekten beklenmeyen bir davranış mı oluştu?
Kısacası karar mekanizmasını görünür hale getirmek, AI Agent sistemlerinde debugging sürecini ciddi anlamda kolaylaştırır.
Sonuç olarak;
Non-deterministic davranış, AI Agent'ların doğal bir özelliğidir. Bunu tamamen ortadan kaldırmaya çalışmak yerine, agent'ın neden o kararı verdiğini kayıt altına almak çok daha sürdürülebilir bir yaklaşımdır. Production ortamında güven, her zaman aynı kararı vermekten değil, verilen kararı açıklayabilmekten gelir.
2. 🥲 Başarısızlık Her Zaman Başarısızlık Değildir
Klasik backend uygulamalarında bir işlemin sonucu çoğunlukla iki ihtimalden biridir.
- Başarılı oldu.
- Başarısız oldu.
Yıllardır bu mantıkla uygulama geliştiriyoruz. Bir metod ya true döndürür ya da false. Daha ciddi bir problem varsa exception fırlatır ve üst katman bunu yönetir, loglar / kullanıcıya uygun mesajı gösterir falan.
Ancak AI Agent mimarisinde işler bu kadar siyah ve beyaz değildir.
Çünkü bir specialist bazen doğru cevabı üretemez ama bu onun tamamen başarısız olduğu anlamına da gelmez.
Gerçek Hayattan Bir Senaryo
Bir AI Agent'ın müşteriye ait sipariş bilgisini sorguladığını düşünelim.
Planner, bu isteği Order Specialist'e yönlendirdi ve specialist ilgili servisi çağırdı.
Ancak aşağıdaki durumlardan biri yaşandı:
- Servis cevap verdi fakat eksik veri döndürdü.
- Dış API birkaç saniye içinde timeout oldu.
- LLM sipariş numarasını tam anlayamadı.
- Sonuç geldi ancak model güven seviyesini düşük olarak değerlendirdi.
Bu durumda gerçekten başarısız mı olduk?
Aslında hayır.
Belki aynı isteği bir kez daha çalıştırmak yeterli olacak. Belki başka bir specialist daha doğru sonuç verecek. Belki kullanıcıdan küçük bir ek bilgi istemek problemi çözecek.
Yani burada yaşanan durum Failure değil, daha çok Henüz Sonuca Ulaşamadık durumudur.
Bool Success Neden Yetersiz?
AI Agent projelerinde sık gördüğüm tasarımlardan biri aşağıdaki gibi oluyor.
public sealed class SpecialistResult
{
public bool Success { get; init; }
public string Summary { get; init; } = string.Empty;
}
İlk bakışta oldukça masum görünüyor.
Fakat planner açısından düşünelim.
Success değeri false geldiğinde planner ne yapacak?
- Aynı specialist'i tekrar mı çalıştıracak?
- Başka specialist'e mi geçecek?
- Kullanıcıdan ek bilgi mi isteyecek?
- İşlemi tamamen mi sonlandıracak?
Planner'ın bunlardan hangisini yapacağını bilebilmesi için daha fazla bilgiye ihtiyacı vardır.
Tek başına bool bir değer bunu kesinlikle anlatamaz.
Üçüncü Bir Sonuç Tanımlayın
Ben AI Agent projelerinde sonucu üç farklı duruma ayırmayı tercih ediyorum.
public enum SpecialistOutcome
{
Success,
Inconclusive,
Failure
}
Ve specialist cevabını şu şekilde modelliyorum.
public sealed record SpecialistResult(
SpecialistOutcome Outcome,
string Summary);
Artık planner yalnızca başarılı olup olmadığını değil, yaşanan durumun ne olduğunu da biliyor.
| Durum | Anlamı | Planner Ne Yapmalı? |
|--------------|-------------------------------------------|--------------------------------------------------------|
| Success | İşlem tamamlandı. | Sonucu kullanıcıya döndür. |
| Inconclusive | Henüz karar vermek için yeterli veri yok. | Retry, farklı specialist veya kullanıcıdan bilgi iste. |
| Failure | Bu specialist bu işi yapamaz. | Başka specialist'e geç veya işlemi sonlandır. |
Planner Artık Daha Akıllı Karar Verebilir
Bu model sayesinde planner'ın karar mekanizması oldukça sade hale gelir.
switch (result.Outcome)
{
case SpecialistOutcome.Success:
return result.Summary;
case SpecialistOutcome.Inconclusive:
return await RetryOrEscalateAsync(
specialist,
goal,
cancellationToken);
case SpecialistOutcome.Failure:
return await TryNextSpecialistAsync(
goal,
cancellationToken);
}
Burada dikkat edilmesi gereken nokta şudur.
Planner artık her başarısız görünen durumda aynı davranışı göstermiyor.
Çünkü gerçekten başarısız olmak ile henüz sonuca ulaşamamak tamamen farklı problemlerdir.
Production'da Bunun Karşılığı
Gerçek sistemlerde timeout, rate limit, geçici ağ problemleri veya eksik kullanıcı bilgisi oldukça normaldir.
Eğer bütün bunları tek bir Failure olarak değerlendirirseniz agent gereğinden erken pes eder.
Tam tersine her durumda retry yaparsanız bu kez sonsuz döngülere girmeye başlarsınız.
İşte bu yüzden planner'ın önce yaşanan durumu anlaması gerekir.
Karar mekanizması doğru modellenirse retry, fallback ve escalation gibi süreçleri yönetmek oldukça kolaylaşır.
Bir Adım Daha İleri Gidebiliriz
Production ortamında ben yalnızca sonucu değil, sonucun neden oluştuğunu da döndürmeyi faydalı buluyorum.
public sealed record SpecialistResult(
SpecialistOutcome Outcome,
string Summary,
string? Reason = null);
Örneğin aşağıdaki gibi cevaplar üretilebilir.
Outcome : Inconclusive
Reason : Customer number could not be extracted.
Outcome : Failure
Reason : Inventory Specialist does not support digital products.
Böylece planner yalnızca sonucu değil, yaşanan problemin nedenini de bilir.
Bu bilgiler hem log kayıtlarında hem de observability araçlarında inanılmaz derecede değerli hale gelir.
Sonuç olarak;
AI Agent mimarisinde her başarısız görünen durum aynı anlama gelmez. Success, Inconclusive ve Failure gibi farklı sonuç tipleri tanımlamak, planner'ın daha doğru kararlar vermesini sağlar. Böylece gereksiz retry işlemlerini azaltabilir, uygun fallback senaryolarını çalıştırabilir ve kullanıcı deneyimini önemli ölçüde iyileştirebilirsiniz.
3. 🔁 Sonsuz Retry Döngüleri: "Bir Daha Dene" Her Zaman Doğru Çözüm Değildir
Yazılım geliştirirken hepimizin sıkça kullandığı çözümlerden biri retry mekanizmasıdır.
Bir servis cevap vermediğinde tekrar deneriz. Ağ bağlantısında geçici bir problem oluştuğunda birkaç saniye bekleyip yeniden istek göndeririz. Özellikle mikro servis mimarilerinde bu oldukça yaygın ve çoğu zaman doğru bir yaklaşımdır.
Ancak AI Agent mimarisinde retry kavramı biraz daha dikkatli ele alınmalıdır.
Çünkü agent yalnızca bir API çağrısını tekrar etmez; yeniden düşünür, yeniden plan yapar, tool seçer ve çoğu zaman tekrar modele istek gönderir.
Yani her retry yalnızca birkaç milisaniyelik ek yük değil, aynı zamanda yeni bir LLM çağrısı, yeni token tüketimi ve yeni maliyet anlamına gelir.
Masum Görünen Bir Döngü
İlk geliştirilen agent'larda genellikle aşağıdakine benzer bir kodla karşılaşabiliyoruz.
while (true)
{
var result = await specialist.InvokeAsync(goal);
if (result.Success)
{
return result.Summary;
}
}
Kod ilk bakışta oldukça basit görünüyor.
"Başarana kadar dene."
Fakat production ortamında bu yaklaşım oldukça risklidir.
Production'da Neler Olabilir?
Düşünün ki specialist, kullanıcıdan gelen isteği anlamakta zorlanıyor.
Planner aynı specialist'i tekrar çalıştırıyor.
Sonuç yine başarısız.
Bir kez daha deniyor.
Sonra tekrar...
Aslında sistem hiçbir ilerleme kaydetmiyor. Sadece aynı problemi tekrar tekrar çözmeye çalışıyor.
Bu sırada ise:
- Yeni LLM istekleri gönderiliyor.
- Token tüketimi artıyor.
- Kullanıcı beklemeye devam ediyor.
- API maliyetleri yükseliyor.
- Thread'ler ve kaynaklar gereksiz yere meşgul ediliyor.
Üstelik bu durum bazen dakikalarca fark edilmeden devam edebilir.
Retry ile İlerleme Aynı Şey Değildir
Burada önemli bir ayrım yapmak gerekiyor.
Her retry sistemi hedefe biraz daha yaklaştırmalıdır.
Eğer her deneme tamamen aynı şartlarda gerçekleşiyorsa, aslında yeni bir şey denemiyorsunuz demektir.
AI Agent açısından düşündüğümüzde planner'ın kendisine şu soruyu sorması gerekir:
Bir kez daha denersem gerçekten farklı bir sonuç alma ihtimalim var mı?
Eğer cevap "Hayır." ise retry yalnızca zaman ve maliyet kaybına dönüşür.
Retry Budget Kavramı
Bu nedenle production sistemlerinde her agent çalışmasının belirli bir bütçesi olmalıdır.
Buradaki bütçe yalnızca deneme sayısını ifade etmez.
Aynı zamanda sistemin ne kadar süre, ne kadar token ve ne kadar işlem maliyeti harcayabileceğini de tanımlar.
En basit haliyle deneme sayısını sınırlandırabiliriz.
public async Task<string> RunAsync(
string goal,
CancellationToken cancellationToken)
{
const int maxAttempts = 3;
for (var attempt = 1; attempt <= maxAttempts; attempt++)
{
var result = await TryOnceAsync(goal, cancellationToken);
if (result.Outcome != SpecialistOutcome.Inconclusive)
{
return result.Summary;
}
}
return "Maximum retry count reached.";
}
Bu örnek oldukça basit görünse de production ortamında büyük fark oluşturur.
Artık agent'ın ne zaman vazgeçeceği nettir.
Sistem sonsuza kadar aynı problemi çözmeye çalışmaz.
Vazgeçmek de Bir Karardır
Yazılım geliştirirken çoğu zaman vazgeçmeyi başarısızlık olarak görürüz.
Oysa AI Agent tarafında doğru zamanda durabilmek, çoğu zaman tekrar denemekten daha doğru bir karardır.
Örneğin aşağıdaki mesaj kullanıcı açısından oldukça değerlidir.
İşlem üç kez denendi ancak güvenilir bir sonuca ulaşılamadı.
Lütfen isteğinizi biraz daha detaylandırın.
Bu yaklaşım hem kullanıcıya dürüst davranır hem de sistem kaynaklarının gereksiz tüketilmesini engeller.
Retry Her Zaman Aynı Retry Olmamalı
Retry yapılacaksa her denemede sistemin yeni bir bilgi kazanması gerekir.
Örneğin planner aşağıdaki stratejileri uygulayabilir.
- Farklı bir specialist seçmek.
- Başka bir tool kullanmak.
- Prompt'u daha fazla bağlamla yeniden oluşturmak.
- Kullanıcıdan ek bilgi istemek.
- Daha küçük görevlere bölerek tekrar plan yapmak.
Dikkat ederseniz bunların hiçbiri aynı işlemi tekrar etmek değildir.
Her biri yeni bir çözüm yolu denemektedir.
İnsan Müdahalesini Unutmayın
AI Agent'ların amacı her problemi tek başına çözmek değildir.
Bazen en doğru karar, işlemi bir insana devretmektir.
Örneğin müşteri destek sistemlerinde aşağıdaki akış oldukça başarılı sonuçlar verir.
- Agent problemi çözmeye çalışır.
- Belirlenen retry bütçesi tükenir.
- Conversation geçmişi saklanır.
- Destek ekibine otomatik olarak aktarılır.
Bu sayede kullanıcı aynı bilgileri tekrar tekrar girmek zorunda kalmaz ve destek ekibi agent'ın hangi adımları denediğini görebilir.
Production'da Asıl Maliyet Token'dır
Klasik uygulamalarda sonsuz döngüler daha çok CPU veya bellek tüketimiyle ilişkilendirilir.
AI Agent sistemlerinde ise buna bir maliyet daha ekleniyor: token tüketimi.
Her gereksiz planner çağrısı, her başarısız tool seçimi ve her anlamsız retry doğrudan kullandığınız modelin maliyetini artırır.
Küçük görünen bir döngü, yoğun trafikte binlerce gereksiz model çağrısına dönüşebilir.
Bu nedenle retry bütçesi yalnızca performans optimizasyonu değil, aynı zamanda maliyet yönetiminin de önemli bir parçasıdır.
Sonuç olarak;
Retry mekanizması, AI Agent mimarisinde dikkatli kullanılmalıdır. Aynı işlemi tekrar etmek yerine her denemede sisteme yeni bir bilgi veya farklı bir çözüm yolu kazandırmak gerekir. Ayrıca her agent çalışmasının belirlenmiş bir retry bütçesi olmalı ve bu bütçe tükendiğinde sistem kontrollü bir şekilde durabilmelidir. Çünkü production ortamında en pahalı döngü, hiç ilerleme kaydetmeden sürekli yeniden çalışan döngüdür.
4. ⏱️Timeout'lar Sistemi Durdurmamalı, Planner'a Bilgi Vermeli
AI Agent geliştirmeye başladığınızda ilk zamanlar bütün tool çağrıları oldukça hızlı çalışır.
Veritabanı sorguları milisaniyeler içerisinde tamamlanır, HTTP istekleri beklenenden kısa sürer ve model neredeyse anında cevap üretir.
Ancak production ortamında işler her zaman bu kadar sorunsuz ilerlemez.
Bir servis yavaşlayabilir, ağ bağlantısında gecikmeler oluşabilir veya kullandığınız LLM sağlayıcısı beklediğinizden daha uzun sürede cevap verebilir.
İşte bu noktada en kritik sorulardan biri şudur:
Bir specialist zamanında cevap vermezse tüm agent çalışması mı durmalı?
Bence cevabı kesinlikle hayır.
Timeout ile Failure Aynı Şey Değildir
Birçok uygulamada timeout yaşandığında doğrudan exception oluşur ve işlem sonlandırılır.
try
{
return await specialist.InvokeAsync(goal, cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Specialist failed.");
throw;
}
Bu yaklaşım klasik backend servislerinde kabul edilebilir olabilir.
Ancak AI Agent tarafında biraz düşünelim.
Specialist gerçekten başarısız mı oldu?
Belki de yalnızca cevap vermesi birkaç saniye daha sürecekti.
Belki farklı bir specialist aynı işi çok daha kısa sürede tamamlayabilirdi.
Belki planner başka bir strateji deneyerek kullanıcıyı hiç bekletmeden sonuca ulaşabilecekti.
Timeout yaşanması, çoğu zaman işlemin imkânsız olduğu anlamına gelmez.
Sadece o denemenin belirlediğimiz süre içerisinde tamamlanamadığını gösterir.
Her Specialist'in Bir Süre Limiti Olmalı
Production ortamında çalışan her tool veya specialist için makul bir çalışma süresi belirlemek iyi bir pratiktir.
Bu süre uygulamanın ihtiyacına göre değişebilir.
- Veritabanı sorgusu için 2 saniye
- HTTP servisi için 5 saniye
- Harici AI servisi için 15 saniye
Önemli olan timeout değerinin var olmasıdır.
Çünkü süre sınırı olmayan bir işlem, sistem kaynaklarını ne kadar süre meşgul edeceğini de belirsiz hale getirir.
CancellationToken Doğru Kullanılmalı
.NET tarafında bu problemi çözmek oldukça kolaydır.
Her specialist çağrısı için ayrı bir CancellationTokenSource oluşturabilir ve ana isteğin iptal bilgisini de koruyabilirsiniz.
public async Task<SpecialistResult>
InvokeWithTimeoutAsync(
ISpecialist specialist,
string goal,
TimeSpan timeout,
CancellationToken cancellationToken)
{
using var cts =
CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken);
cts.CancelAfter(timeout);
try
{
return await specialist.InvokeAsync(
goal,
cts.Token);
}
catch (OperationCanceledException)
when (!cancellationToken.IsCancellationRequested)
{
return new SpecialistResult(
SpecialistOutcome.Inconclusive,
"The operation timed out.");
}
}
Buradaki önemli detay, oluşan OperationCanceledException'ı doğrudan yukarı fırlatmamak.
Bunun yerine planner'ın anlayabileceği normal bir sonuç üretmek.
Planner İçin Her Şey Aynı Dile Dönüşüyor
Yazının başında specialist sonuçlarını üç farklı duruma ayırmıştık.
- Success
- Inconclusive
- Failure
Timeout yaşandığında da aynı dili kullanabiliriz.
| Durum | Planner'ın Yorumu |
|--------------|--------------------------------------------|
| Success | İşlem tamamlandı. |
| Failure | Bu specialist bu işi yapamıyor. |
| Inconclusive | Henüz karar vermek için yeterli bilgi yok. |
Dikkat ederseniz planner'ın timeout için ayrı bir kod yazmasına gerek kalmadı.
Timeout artık yalnızca Inconclusive sonucunu üreten nedenlerden biri haline geldi.
Bu da planner'ın karmaşıklığını ciddi şekilde azaltır.
Timeout Bir İş Kuralına Dönüşüyor
Buradaki bakış açısı oldukça önemlidir.
Timeout artık teknik bir exception olmaktan çıkıp sistem davranışının doğal bir parçası haline geliyor.
Planner ister timeout alsın, ister eksik veri dönsün, ister model düşük güvenle cevap versin...
Hepsini aynı karar mekanizması içerisinde değerlendirebiliyor.
Bu yaklaşım yeni specialist'ler eklendiğinde de mimarinin büyümesini kolaylaştırıyor.
Çünkü planner teknik detaylarla değil, yalnızca iş kurallarıyla ilgileniyor.
Timeout Sonrası Ne Yapılabilir?
Timeout almak her zaman işlemi sonlandırmak anlamına gelmez.
Planner aşağıdaki seçeneklerden birini değerlendirebilir.
- Aynı specialist'i tekrar çalıştırmak.
- Başka bir specialist'e yönlendirmek.
- Daha küçük bir görev oluşturarak yeniden plan yapmak.
- Kullanıcıya işlemin sürdüğünü bildirmek.
- İnsan müdahalesine ihtiyaç olduğunu belirtmek.
Görüldüğü gibi timeout yalnızca yeni bir karar verme noktasıdır.
Sistemin tamamen durmasını gerektiren kritik bir hata değildir.
Observability'yi Unutmayın
Timeout yaşanması tek başına problem olmayabilir. Ancak bunların ne sıklıkla yaşandığını takip etmiyorsanız, zamanla kullanıcı deneyimini olumsuz etkileyen bir performans sorununu gözden kaçırabilirsiniz.
Bu nedenle her timeout olayını loglamak, mümkünse süre bilgisiyle birlikte metrik sistemlerine göndermek faydalı olacaktır.
Örneğin aşağıdaki bilgiler production ortamında oldukça değerlidir.
- Hangi specialist timeout oldu?
- Kaç saniye sonra timeout gerçekleşti?
- Hangi model kullanılıyordu?
- İstek kaçıncı denemeydi?
- Sonrasında fallback çalıştı mı?
Bu bilgiler sayesinde yalnızca hataları değil, sistemin zaman içerisindeki davranışını da analiz edebilirsiniz.
Sonuç Olarak;
Timeout, AI Agent mimarisinde beklenmeyen bir istisna değil, planlanması gereken doğal bir durumdur. Specialist çağrılarını belirli sürelerle sınırlandırmak ve timeout'ları planner'ın anlayabileceği standart sonuçlara dönüştürmek, sistemi hem daha dayanıklı hem de daha sade hale getirir. Güvenilir agent'lar, timeout yaşamayan değil; timeout oluştuğunda doğru tepki verebilen agent'lardır.
Production'da Güvenilir AI Agent'lar Nasıl Tasarlanır?
Bu yazı boyunca dört farklı problemden bahsettik.
- Non-deterministic kararlar
- Specialist sonuçlarının doğru modellenmesi
- Runaway retry döngüleri
- Timeout yönetimi
Dikkat ederseniz bu problemlerin hiçbiri kullandığınız LLM modeliyle doğrudan ilgili değildi.
Hiçbir noktada "daha büyük bir model kullanın", "temperature değerini değiştirin" veya "prompt'u yeniden yazın" demedik.
Çünkü production ortamında güvenilir bir AI Agent geliştirmek çoğu zaman model seçiminden çok, sistem tasarımına bağlıdır.
Asıl Güç, Agent'ın Etrafındaki Mimaridedir
AI Agent'ları klasik servislerden ayıran en önemli özellik, her zaman doğru cevap vermemeleridir.
Bu ilk bakışta olumsuz gibi görünse de aslında onların doğasında vardır. Büyük dil modelleri olasılıksal çalışır ve her zaman aynı yolu izlemek zorunda değildir.
Bu nedenle başarılı sistemler, agent'ın hata yapmayacağını varsaymaz. Tam tersine, hata yapacağını kabul ederek tasarlanır.
İyi bir mimari şu sorulara en baştan cevap verebilmelidir.
- Yanlış specialist seçilirse ne olacak?
- Tool cevap vermezse ne olacak?
- Model düşük güvenle sonuç üretirse ne olacak?
- Retry bütçesi tükenirse ne olacak?
- Kullanıcının bekleme süresi uzarsa ne olacak?
Bu soruların cevapları kodun içerisinde açıkça tanımlanmışsa, sistem yalnızca çalışmakla kalmaz; aynı zamanda öngörülebilir hale gelir.
Gözlemleyemediğiniz Sistemi Yönetemezsiniz
Production ortamında karşılaşılan problemlerin önemli bir kısmı aslında sistemin davranışını görememekten kaynaklanır.
Bir kullanıcı "Agent yanlış cevap verdi." dediğinde aşağıdaki soruların cevaplarını birkaç dakika içerisinde bulabiliyor olmanız gerekir.
- Planner hangi specialist'i seçti?
- Bu kararın gerekçesi neydi?
- Kaç kez retry yapıldı?
- Hangi tool timeout oldu?
- Toplam kaç token tüketildi?
- İstek ne kadar sürdü?
Eğer bu bilgileri göremiyorsanız, problemi çözmek yerine tahmin yürütmeye başlarsınız.
Bu nedenle AI Agent projelerinde loglama yalnızca hata kayıtları tutmak değildir. Aynı zamanda agent'ın düşünme sürecini görünür hale getirmektir.
Production İçin Küçük Ama Etkili Öneriler
Son olarak production ortamında çalışan AI Agent'lar geliştirirken faydasını gördüğüm birkaç öneriyi paylaşmak istiyorum.
- Planner'ın verdiği her kararı gerekçesiyle birlikte loglayın.
- Her agent çalışmasına benzersiz bir Correlation ID atayın.
- Retry sayısını değil, toplam çalışma bütçesini yönetin.
- Timeout'ları exception olarak değil, iş akışının doğal bir sonucu olarak ele alın.
- Token tüketimini ve model maliyetlerini düzenli olarak izleyin.
- Prompt değişikliklerini versiyonlayın ve hangi prompt'un hangi sonucu ürettiğini takip edin.
- Gerekli durumlarda işlemi bir insana devretmekten çekinmeyin.
Bu önerilerin hiçbiri karmaşık algoritmalar gerektirmiyor. Ancak hepsi birlikte uygulandığında agent'ın güvenilirliğini önemli ölçüde artırıyor.
Son Söz
AI Agent geliştirmek artık eskisine göre oldukça kolay. Birkaç satır kodla modelleri çağırabilir, tool'lar tanımlayabilir ve çalışan bir demo hazırlayabilirsiniz.
Ancak gerçek dünya senaryolarında başarıyı belirleyen şey, agent'ın doğru cevap verdiği anlar değil; doğru cevap veremediği anlarda nasıl davrandığıdır.
Çünkü production ortamında önemli olan, sistemin hiç hata yapmaması değildir.
Önemli olan; hataları öngörebilmesi, yönetebilmesi ve gerektiğinde güvenli bir şekilde durabilmesidir.
Güvenilir AI Agent'lar, hiç başarısız olmayan sistemler değildir. Başarısızlığı mimarilerinin doğal bir parçası olarak kabul eden ve bu durumları kontrollü şekilde yönetebilen sistemlerdir.
Yapay zekâ tarafındaki modeller hızla gelişmeye devam edecek. Daha güçlü modeller, daha büyük context pencereleri ve daha başarılı tool kullanım yetenekleri göreceğiz.
Ancak sağlam yazılım prensipleri değişmeyecek.
İyi gözlemlenebilen, sınırları belirlenmiş, hata senaryoları düşünülmüş ve sürdürülebilir şekilde tasarlanmış bir mimari; hangi modeli kullanırsanız kullanın sizi production ortamında bir adım öne taşıyacaktır.
Keyifli kodlamalar. 🖖