Bir üretim ERP’sinde, tedarik zinciri entegrasyonlarını AI ajanlarla otomatize etmeye çalıştığımda, ilk başta her şey kusursuz görünüyordu. Ajan, gelen siparişleri analiz edecek, stok durumunu kontrol edecek, gerekirse yeni hammadde siparişi için tedarikçi API’sini çağıracak ve üretim planını güncelleyecekti. Kağıt üzerinde bu akış, manuel müdahaleyi belirgin ölçüde azaltacaktı.
Ancak, işin tool-use kısmına geldiğimizde, yani ajanın dış dünyayla etkileşime girmesi gerektiğinde, beklediğim “sihirli” otomasyonun yerini karmaşık mimari seçimler ve beraberindeki operasyonel maliyetler aldı. Bir tool’u ajana sunmanın, sadece bir fonksiyon imzasını vermekten çok daha fazlası olduğunu, hem performans hem de güvenlik açısından ciddi sonuçları olabileceğini tecrübe ettim. Bu yazıda, bu mimari seçimlerin getirdiği maliyetleri ve benim bu süreçte öğrendiklerimi paylaşacağım.
AI Ajanlarda Tool-Use’un Temel Mantığı ve Beklentiler
AI ajanların tool-use yeteneği, büyük dil modellerinin (LLM) sadece metin üretmekle kalmayıp, belirli eylemleri gerçekleştirmek için dış araçları kullanabilmesini ifade eder. Bu, bir ajanın bir veritabanını sorgulaması, bir API’ye istek atması veya hatta belirli bir kodu çalıştırması anlamına gelebilir. Benim deneyimimde, bu yetenek, bir üretim planlama ajanın stok durumunu kontrol etmek için PostgreSQL’den veri çekmesi ya da tedarikçi portalına sipariş göndermesi gibi kritik iş akışlarını otomatikleştirmek için vazgeçilmezdi.
Genellikle bu yetenekten beklentimiz, ajanın bir insan gibi düşünebilmesi ve doğru zamanda doğru aracı kullanarak problemi çözebilmesi yönündedir. Ancak gerçekte, ajana sunulan tool’ların kalitesi, tanımları ve ajanın bu tool’ları nasıl yorumladığı, başarının temelini oluşturur. Eğer tool tanımı eksikse veya ajanın bağlamı doğru anlaması için yeterli bilgi yoksa, ajan “halüsinasyon” görüp yanlış tool’u yanlış parametrelerle çağırabilir. Birbirine benzer iki tool’un (örneğin döviz kuru getiren iki ayrı fonksiyon) açıklaması yeterince ayırt edici değilse, ajan kolaylıkla yanlış olanı seçip hatalı sonuç üretebilir.
[
{
"name": "get_stock_level",
"description": "Belirtilen urun icin mevcut stok seviyesini veritabanindan cek. 'product_id' parametresi zorunludur.",
"parameters": {
"type": "object",
"properties": {
"product_id": {
"type": "string",
"description": "Sorgulanacak urunun benzersiz ID'si."
}
},
"required": ["product_id"]
}
},
{
"name": "place_supplier_order",
"description": "Belirtilen urun icin tedarikciye siparis ver. 'product_id', 'quantity', 'supplier_id' parametreleri zorunludur. Siparis oncesi stok kontrolu yapilmalidir.",
"parameters": {
"type": "object",
"properties": {
"product_id": { "type": "string" },
"quantity": { "type": "integer" },
"supplier_id": { "type": "string" }
},
"required": ["product_id", "quantity", "supplier_id"]
}
}
]
Yukarıdaki gibi net tool tanımları, ajanın doğru aracı seçmesini kolaylaştırır. Aksi takdirde, ajan “sipariş ver” komutunu algıladığında, stok kontrolü yapmadan doğrudan sipariş vermeye kalkabilir, ki bu bir üretim ortamında ciddi hatalara yol açar. Bu yüzden tool’ların sadece teknik olarak değil, iş akışı olarak da doğru tanımlanması ve her edge case’in düşünülmesi gerekiyor.
Mimari Seçim 1: Monolitik vs. Mikroservis Tool Mimarileri
AI ajanlara sunulan tool’ları tasarlarken karşılaştığım ilk büyük mimari karar, bu tool’ları nasıl organize edeceğimle ilgiliydi: Monolitik bir yapı altında mı toplayacağım, yoksa her tool’u kendi mikroservisi olarak mı çalıştıracağım? Bu karar, özellikle büyük ve karmaşık kurumsal sistemlerde operasyonel maliyetleri doğrudan etkiliyor.
İlk başta, bir üretim ERP’sinde, tüm dahili API’leri ve veritabanı işlemlerini tek bir RESTful endpoint altında toplayarak ajana “tek bir büyük tool” sunmayı düşündüm. Bu, geliştirme hızını artırır gibi görünüyordu çünkü tüm tool’lar aynı codebase içinde tanımlanıp yönetiliyordu. Ancak kısa sürede bunun ölçeklenebilirlik ve bakım açısından bir darboğaz olduğunu gördüm. Örneğin, stok sorgulama tool’u gün boyu yoğun şekilde çağrılırken, nadiren kullanılan “yeni tedarikçi ekle” tool’u da aynı monolitik servis içinde yaşıyordu. Stok sorgulama servisinde yaşanan bir yavaşlama (örneğin bir N+1 sorgu hatası yüzünden), tüm tool-use yeteneklerini etkiliyordu.
Mikroservis yaklaşımında ise her tool veya ilgili tool grupları kendi bağımsız servisinde çalışır. Bu, her servisin kendi kaynaklarına sahip olmasını, bağımsız olarak ölçeklenmesini ve hataların izole edilmesini sağlar. Örneğin, bir StokYonetimServisi ve bir TedarikciSiparisServisi ayrı ayrı çalışır. Bu ayrım, bir servisin çökmesinin veya yavaşlamasının diğerlerini etkilemesini engeller. Benim kendi yan ürünümün backend’inde, kullanıcı verilerini yöneten tool’lar ile finansal hesaplama tool’ları ayrı servislerde çalışıyor. Bu sayede, finansal hesaplamalarda yoğun bir yük olduğunda, temel kullanıcı işlemleri etkilenmiyor. Ancak bu yaklaşım, operasyonel olarak daha fazla yönetim ve izleme yükü getiriyor; dağıtık sistemlerin getirdiği karmaşıklıkla uğraşmak gerekiyor. Her servisin kendi CI/CD pipeline’ı, kendi loglama ve metrik sistemi oluyor.
Mimari Seçim 2: Tool Orchestration ve State Yönetimi
AI ajanların tool’ları hangi sırayla çağıracağı ve aradaki state’i nasıl yöneteceği, ajanın karmaşık görevleri başarıyla tamamlaması için kritik bir mimari karardır. Bu karar, hata yönetimi, debug maliyetleri ve ajanın genel “akıl yürütme” yeteneği üzerinde doğrudan etkiye sahiptir. İki temel yaklaşım var: LLM’in kendisinin orkestrasyonu yapması veya harici bir orkestratör kullanmak.
LLM’in orkestrasyonu yapması, ajanın “düşünme” yeteneğini doğrudan tool çağrılarına yansıtması anlamına gelir. Prompt Engineering ile ajana bir “planlama” yeteneği verilir ve ajan, hangi tool’u ne zaman çağıracağına kendisi karar verir. Örneğin, bir üretim planı oluştururken, önce get_stock_level, sonra get_supplier_prices, ardından calculate_production_plan tool’larını sırasıyla çağırması beklenir. Bu yaklaşım, esneklik sağlar ancak ajanın “unutkanlık” veya “halüsinasyon” riskini de beraberinde getirir. Çok adımlı bir akışta ajan, aradaki bir tool’u atlayıp doğrudan sonuç üretmeye kalkabilir; çünkü önceki adımlardan gelen bağlamı kaybetmiş olabilir. Bu durum, LLM’in token limitleri veya prompt’taki karmaşıklık nedeniyle ortaya çıkabilir.
Harici bir orkestratör kullanmak ise, ajanın sadece “hangi tool’u çağıracağını ve hangi parametrelerle çağıracağını” önermesini beklemek, actual çağrı sırasını ve state yönetimini ayrı bir servis veya workflow engine’e bırakmaktır. Örneğin, bir ProductionWorkflowService, ajandan gelen önerileri alır, kendi iş kurallarına göre sıralar, tool’ları çağırır ve sonuçları tekrar ajana iletir. Bu yaklaşım, daha robust ve hata toleranslı sistemler kurmamıza olanak tanır. transaction outbox pattern’ini kullanarak, tool çağrılarının idempotent olmasını sağlayabiliriz; yani aynı tool çağrısı birden fazla kez yapılsa bile sistem durumu değişmez.
# Basit bir harici orkestratör pseudo-kodu
def orchestrate_production_plan(agent_suggestion):
if agent_suggestion.tool_name == "get_stock_level":
stock_data = call_tool(agent_suggestion.tool_name, agent_suggestion.parameters)
# State'i bir veritabanına kaydet veya bir sonraki adıma ilet
return {"status": "success", "data": stock_data, "next_step_hint": "get_supplier_prices"}
elif agent_suggestion.tool_name == "get_supplier_prices":
# Önceki adımdan gelen state'i kullan
supplier_data = call_tool(agent_suggestion.tool_name, agent_suggestion.parameters)
return {"status": "success", "data": supplier_data, "next_step_hint": "calculate_production_plan"}
# ... diğer tool'lar
else:
return {"status": "error", "message": "Unknown tool or invalid state"}
Bu harici orkestratör yaklaşımı, karmaşık iş akışlarında ajanın hata yapma olasılığını azaltırken, orkestrasyon mantığının kendisinin de bakımı ve testi gereken ayrı bir kod tabanı haline gelmesi gibi bir maliyet getiriyor. Ancak benim deneyimimde, özellikle finansal işlemler gibi kritik akışlarda, bu ek maliyet, ajanın öngörülemez davranışlarının potansiyel maliyetinden çok daha düşüktü.
Güvenlik ve İzolasyon: Tool-Use’un Karanlık Yüzü
AI ajanların tool-use yetenekleri, şüphesiz büyük bir güç sunuyor ancak bu güç, beraberinde ciddi güvenlik riskleri de getiriyor. Ajanın dış sistemlerle etkileşime girmesi, potansiyel olarak kötü niyetli veya hatalı tool çağrılarına kapı aralayabilir. Bu nedenle, ajanların tool çağrılarını güvenli bir şekilde yönetmek, production ortamlarında hayati önem taşır.
En basit senaryoda bile, bir ajan yanlışlıkla veya bir prompt enjeksiyonu sonucu, yetkisiz bir API’ye erişmeye çalışabilir. Bir bankanın iç platformunda, bir analitik ajanının, sadece okuma yetkisi olması gerekirken, yanlışlıkla bir update_user_data tool’unu çağırma girişiminde bulunduğunu gördüm. Bu tür durumları engellemek için, her tool çağrısında yetkilendirme (OAuth2/JWT) mekanizmalarını sıkıca uygulamak zorundayız. Ajanın her tool’u çağırmadan önce, o tool için gerekli yetkiye sahip olup olmadığını kontrol etmek, temel bir güvenlik katmanıdır.
Ayrıca, DDoS veya kaynak tüketimi riskleri de var. Bir ajan, bir API’ye yanlış parametrelerle veya aşırı sayıda istek atarak servisi çökertebilir; hatalı bir döngüye giren bir ajan, kısa sürede bir iç servisi devre dışı bırakacak kadar istek üretebilir. Bu tür durumlara karşı fail2ban benzeri sistemler etkili olur; belirli bir IP’den veya ajan kimliğinden gelen art arda başarısız denemelerden sonra geçici engellenme kuralı uygulamak, servisi korurken ajanın hata yapma maliyetini de düşürür.
Kernel module blacklist gibi daha derin güvenlik önlemleri de düşünülmeli. Örneğin, ajanın kod çalıştırma yeteneği varsa, algif_aead gibi bazı modüllerin kara listeye alınması, potansiyel CVE’lere karşı ek bir koruma sağlayabilir. Ayrıca, ajanın çalıştığı ortamın izolasyonu için Docker container’ları içinde cgroup limit’leri belirlemek, ajanın kaynak tüketimini sınırlayarak diğer sistem bileşenlerini korur. Bir container’ın memory.high yumuşak limit’ini makul bir değere ayarladığımda, ajanın hatalı bir işlemde belleği aşırı tüketmeye çalışmasını engelleyerek OOM-killed olmasını sağladım, bu da ana uygulamanın stabil kalmasına yardımcı oldu.
Maliyet Optimizasyonu: LLM Provider Seçimi ve Fallback Stratejileri
AI ajanların tool çağrılarını yorumlaması ve karar vermesi için kullanılan LLM’lerin seçimi, hem performans hem de maliyet açısından kritik öneme sahiptir. Piyasada Gemini Flash, Groq, Cerebras ve OpenRouter gibi birçok farklı LLM provider bulunuyor ve her birinin kendine özgü avantajları ve dezavantajları var. Benim deneyimimde, tek bir provider’a bağlı kalmak yerine, bu provider’ları stratejik olarak bir araya getirmek, hem maliyetleri düşürdü hem de sistemin dayanıklılığını artırdı.
Örneğin, bir AI destekli finansal hesaplayıcıda, kullanıcıdan gelen doğal dil sorgularını (örneğin “100.000 TL’nin 5 yıl vadeli enflasyonlu getirisini hesapla”) tool çağrılarına çevirmek için LLM’leri kullanıyorum. Bu senaryoda latency (gecikme süresi) kritikti, çünkü kullanıcı anında yanıt bekliyordu. Groq’un yüksek işlem hızları ve düşük maliyetleri bu tür senaryolar için idealdi; çoğunlukla göz açıp kapayıncaya kadar yanıt alıyordum. Ancak, daha karmaşık ve daha az sıklıkta yapılan üretim planlama optimizasyonları için, daha pahalı ama daha kapsamlı bir model olan Gemini Pro’yu tercih ettim, çünkü burada doğruluk ve karmaşık akıl yürütme daha öncelikliydi, latency ise ikincildi.
Çoklu provider stratejisinin bir diğer önemli avantajı da dayanıklılık. Bir provider’ın hizmet dışı kalması durumunda, otomatik olarak başka bir provider’a geçiş yapabilen bir fallback mekanizması kurdum. OpenRouter gibi platformlar, bu tür bir orkestrasyonu kolaylaştırıyor. Benim kendi sistemimde, birincil provider’dan belirlediğim süre içinde yanıt alınamazsa veya bir API hatası dönerse, otomatik olarak ikincil provider’a geçiş yapacak bir mantık geliştirdim. Bu sayede, finansal hesaplayıcımın yüksek uptime hedefini tutturabildim. Birincil sağlayıcıda yaşanan kısa bir kesinti, bu mekanizma sayesinde kullanıcıya yalnızca yanıt sürelerinde hafif bir artış olarak yansır; akış kesintiye uğramaz.
Bu mimari seçim, başlangıçta biraz daha fazla entegrasyon eforu gerektirse de, uzun vadede hem operasyonel maliyetleri (kayda değer bir token maliyeti düşüşü gördüm) hem de sistemin genel güvenilirliğini önemli ölçüde artırdı.
Operasyonel Zorluklar ve Gözlemlenebilirlik (Observability)
AI ajanları production ortamında çalıştırmak, geleneksel yazılım sistemlerinden farklı operasyonel zorlukları beraberinde getiriyor. Özellikle tool-use yetenekleri olan ajanların davranışları, karmaşık ve öngörülemez olabildiğinden, gözlemlenebilirlik (observability) bu süreçte hayati bir rol oynuyor. Ajanın ne düşündüğünü, hangi tool’u ne zaman ve hangi parametrelerle çağırdığını, bu çağrıların başarılı olup olmadığını anlamak, debug ve hata ayıklama süreçlerini kolaylaştırıyor.
Benim üretim ERP’sinde kullandığım ajanlar, birden fazla tool’u ardışık olarak çağırarak karmaşık görevleri yerine getiriyordu. Bir üretim emri oluşturma akışında, get_raw_material_stock, check_machine_availability, calculate_production_time gibi çok sayıda tool çağrısı zincirleniyordu. Bu çağrılardan herhangi birinde yaşanan bir hata veya gecikme, tüm iş akışını durdurabiliyor veya yanlış bir sonuç üretilmesine neden olabiliyordu. Örneğin zincirin ortasındaki bir tool beklenenden geç yanıt verip timeout’a yol açtığında, ajan görevi tamamlayamadan kalabiliyordu.
Bu tür sorunları tespit etmek için kapsamlı bir observability altyapısı kurmak zorunda kaldım. Loglama, metrik toplama ve trace’leme (OpenTelemetry ile) bu altyapının temel taşlarıydı. Her tool çağrısı için ayrıntılı loglar tuttum: ajanın niyeti, çağrılan tool’un adı, parametreleri, API yanıtı, geçen süre ve hata kodları. Bu loglar journald üzerinden toplanıp merkezi bir loglama sistemine aktarıldı. journald’nin rate limit’leri sayesinde, ajanın hatalı log üretimi sistemimi çökertmedi.
Metrik tarafında ise, her tool çağrısının latency, success rate ve error rate’ini Prometheus ile izledim. Bir Grafana panosunda, her tool için bu metrikleri anlık olarak görebiliyordum.
Aşağıdaki gibi bir PromQL sorgusuyla belirli bir tool’un ortalama gecikme süresini izledim:
rate(tool_call_duration_seconds_sum{tool_name="get_stock_level"}[5m]) / rate(tool_call_duration_seconds_count{tool_name="get_stock_level"}[5m])
Bu sayede, bir tool’un gecikmesinde ani bir sıçrama olduğunda anında fark edip nedenini (örneğin PostgreSQL tarafında sorgu planını bozan bir değişiklik) araştırabiliyordum. Bu tür gözlemler, hızlı müdahale etmemizi ve servis kesintilerini önlememizi sağladı.
Trace’leme ile de ajanın karar verme sürecini ve birden fazla tool çağrısının nasıl bir zincir oluşturduğunu görselleştirdim. Bir OpenTelemetry trace’i, ajanın LLM’e yaptığı çağrıdan, LLM’in tool seçimine, tool’un API’ye yaptığı HTTP isteğine ve API’nin yanıtına kadar tüm süreci tek bir akışta gösteriyordu. Bu, özellikle karmaşık ve çok adımlı görevlerde, hatanın tam olarak nerede ve ne zaman meydana geldiğini tespit etmek için paha biçilmezdi.
Sonuç: Nereye Gidiyoruz?
AI ajanların tool-use yetenekleri, iş süreçlerimizi dönüştürme potansiyeli taşıyor, bu açık. Ancak benim 20 yıllık saha tecrübemde gördüğüm gibi, bu teknolojiyi production ortamına taşımak, sadece kod yazmaktan ibaret değil. Mimari seçimlerin, özellikle de tool’ları nasıl tasarladığımızın, yönettiğimizin ve güvence altına aldığımızın, operasyonel maliyetler ve sistemin genel dayanıklılığı üzerinde doğrudan bir etkisi var.
Monolitik veya mikroservis tool mimarileri arasında seçim yapmak, orkestrasyonu LLM’e bırakmak veya harici bir servis kullanmak, farklı LLM provider’larını bir araya getirmek ve sağlam güvenlik önlemleri almak; bunların hepsi “olur o kadar” deyip geçemeyeceğimiz kritik kararlar. Yanlış bir seçim, kısa vadede hız kazandırsa da, uzun vadede yüksek bakım maliyetleri, güvenlik açıkları ve sistem kesintileri olarak karşımıza çıkabiliyor.
Bu süreçte öğrendiğim en büyük ders, AI ajanları bir kara kutu olarak görmemek gerektiği. Onların da sınırlılıkları var, onların da hataları oluyor. Bu hataları öngörmek, minimize etmek ve hızla gidermek için sağlam bir mühendislik altyapısı kurmak şart. Önümüzdeki dönemde, agent pattern’lerinin daha da olgunlaşacağını ve özellikle RAG (Retrieval-Augmented Generation) ile birleşerek daha güvenilir ve doğru sonuçlar üreteceğini bekliyorum.
Bu alandaki deneyimlerim, kariyerimde bana çok farklı kapılar açtı. Özellikle karmaşık kurumsal yazılım mimarileri ve distributed sistemlerde observability konularındaki birikimimi AI ajan mimarilerine aktarmak, problemleri daha bütünsel bir bakış açısıyla ele almamı sağladı. Gelecekte AI ajanların daha da otonom hale gelmesiyle, bu mimari kararların önemi katlanarak artacak.