Agent nasıl tasarlanır?
Yazılım her geçen gün farklı noktalara evriliyor. Her evrimde "yazılım bitti mi?" diye konuşuyoruz ama aslında her dönüşüm yeni sistem ihtiyaçları doğuruyor. Birkaç sene önce yazılım mimarileri çok popülerken bugün o sistemleri geliştirecek agent'ları tasarlamak ya da yönetebilmek önemli bir meziyet haline geldi. Yönetim kısmını ayrı bir yazıda ele alabiliriz ama bu yazının o konuda da fikir vereceğini düşünüyorum: İyi bir agent'ı nasıl tasarlayacağımızı bilirsek onu nasıl yöneteceğimizi de biliriz.
Görsel: İyi bir agent sistemi biraz da orkestraya benzer. Kazuo ota, Unsplash Lisansı
Agent eşittir LLM değil
Her şeyden önce agent'ın ne olduğunu anlamak gerekiyor. Agent deyince aklımıza belirli kurallar çerçevesinde verilen işleri otomatikleştiren sistemler geliyor. Bu sistemlerin altında yatan LLM'ler de elbette ilk aklımıza gelen kavramlardan. Ama kritik bir nokta var: Agent eşittir LLM değil.
Bu önemli, çünkü verimsiz bir agent gördüğümüzde ilk refleksimiz modeli değiştirmek oluyor. Claude Code, Cursor ya da Copilot gibi popüler agent'ları kullanırken de hep daha güçlü modele geçmeye koşuyoruz. Agent ile LLM'i birbirine karıştırdığımız an agent'ın ne olduğundan uzaklaşıyoruz.
Agent'ı her zaman bir insan gibi düşünüp LLM'i de o insanın beyni olarak görürsek aradaki ince çizgiyi yakalayabiliriz. Beyin insan için ne kadar önemliyse LLM de agent için o kadar önemli. Daha güçlü beyin, daha güzel çıktı; sonuçta hangi insan daha zeki olmak istemez ki :)
Ama tek başına zeki olmak yeter mi? Tabii ki yetmez. Nerede ve nasıl bir ortamda olduğunuz da en az zekanız kadar önemli. Müzik zekası yüksek birinden bir matematik teoremini ispatlamasını beklemezsiniz (bunu yapabilecek istisnalar elbette vardır, iki uç örnek veriyorum). Agent için de aynısı geçerli: Ortamını nasıl kurarsanız agent ona göre davranır. O zaman gelin, agent tasarlarken nelere dikkat edebiliriz biraz inceleyelim.
Talimat dosyaları
Sistemler ne kadar gelişirse gelişsin agent için giriş noktası talimat dosyasıdır. İyi tasarlanmamış bir talimat dosyasıyla agent'tan iyi performans alamayız.
Talimat dosyaları agent'a ilk bilgileri verir; açılışta tek seferlik yüklenir ve agent'ın neler yapabileceğini anlatır. Örneğin şunları buraya koyabiliriz:
- Karar alması gereken noktalarda neleri referans alacağı
- Tool'ları açıklamalarıyla eklesek de hangi durumda hangi tool'u kullanacağı
- Yapılacak işlerle ilgili referans projeler
Agent'lar tasarım gereği talimat mesajlarını öncelikli dikkate alır. Burada net çizgilerle belirttiğimiz her şey çıktıya doğrudan etki eder; kötü hazırlanmış bir talimat dosyası bize kötü çıktı olarak geri döner.
Agent hafızası
Hafızayı efektif kullanmak şart. Agent'lar çalışırken, siz arayüzde fark etmeseniz bile hafızadan bir şeyler alıp hareket eder. Bu hafızayı da genelde bizim haberimiz olmadan kendileri oluşturur: Çoğu zaman prompt verme tarzınızdan etkilenerek memory dosyaları yazılır ve neyin hafızaya gireceğine agent kendisi karar verir.
Sorun şu: Agent, çok spesifik bir durumda öğrendiğini genel bir süreçmiş gibi hafızaya atabilir. Bunun net bir örneğini yaşadım. Kendi bilgisayarımda çalışan Elf adında bir asistanım var. Geçtiğimiz günlerde siteye ilk teknik yazısını yazmasını, yayınlamasını ve ardından LinkedIn hesabımdan paylaşmasını istedim. Bu aşamada amacım çok güçlü bir model kullanmadan en efektif sonucu almak olduğu için epey tekrarla çalıştı. Bu kadar efor harcayınca iş sonunda süreci hafızaya kaydetti. Ama sonrasında LinkedIn'de içerik paylaşma akışını genel bir süreçmiş gibi ele alabiliyor; yani her tarayıcı kullanımında benzer adımlar gerekecekmiş gibi davranabiliyor. Bu tarz spesifik durumlar hafızada karışıklığa yol açıyor.
Kendi agent'ınızı da kodlasanız hazır bir harness da kullansanız memory yönetimine dikkat etmek şart. Arada bir agent'tan hafızasını tazelemesini isteyin, gerekirse dosyalara kendiniz müdahale edin.
- Claude Code kullananlar: Claude'dan arada bir memory dosyalarını tazelemesini isteyebilir ya da
~/.claude/altındaki memory dosyalarını kendileri inceleyebilir. - Kendi agent'ını geliştirenler: Son n mesajın dışında cross-session memory ayarlarını iyi yapmalı. Burada gruplama önemli; ChatGPT ve Claude arayüzlerinde gördüğümüz "Projeler" mantığında gruplama yaparak daha net sonuçlar alabiliriz.
Tool, MCP ve skill kullanımı
Yazmayı en sevdiğim kısım genelde burası, çünkü teknik detaylar burada. Heyecanlandım :)
LLM agent'ın beyniyse tool'lar da eli, kolu, bacağı. Agent'ın sizin adınıza bir eylem gerçekleştirebilmesi büyük ölçüde bu kısma bağlı. Buraya da güzel bir "ama" koyuyorum: Agent'ınız saçmalıyorsa muhtemelen sorun da buradadır.
Her tool'u vermek yetenek değil
"Agent'a ne kadar çok tool ya da MCP verirsem o kadar yetenekli olur" düşüncesini bir kenara bırakalım. Şefin eline et kesmesi için elektrikli testere verirseniz o etten hayır gelmez :)
Sırası şu olmalı:
- Agent'ımızın ne yapması gerektiğine karar veririz.
- Nasıl yapması gerektiğine karar veririz.
- Bunu nelerle yapacağına karar veririz.
Genel kullanım için agent'lar elbette olacak. Ama ben agent geliştirmeyi kurumsal bir yapıya benzetiyorum. Bir agent'la başlarız; yapacağı işler büyüdükçe onu alt kollara ayırır ve genel iş agent'ının bunları orkestre etmesini isteriz. Kurumsal bir şirkette kararlar bir noktada alınır, en alta kadar iner ve herkes kendi üstüne düşeni yapar. Herkesin aracı da farklıdır: Bugün herkes işini laptopla çözüyor ama laptopun içinde kullanılan araçlar her kademede farklı.
Yani "bir agent'ım olsun, lazım olan tüm tool'ları vereyim, kullansın" dememeliyiz. Teknik olarak da bir yerden sonra darboğaza girersiniz: Context rot, context overflow gibi sorunlarla karşılaşır ve o agent'tan pek fayda göremezsiniz. Bu yüzden agent tasarlarken subagent dediğimiz alt agent'ları da planlamalıyız. Her agent'a kendi işiyle ilgili tool'ları verip arkamıza yaslanacağız.
MCP'de seçici olmak
MCP'ler genelde çok sayıda tool ile geliyor; örneğin bir Jira MCP'sinde onlarca tool olabiliyor. Bir MCP bağlarken hangi tool'ların kullanılacağını da belirlemek güzel bir seçenek; hepsini bağlama yüklemenin bir anlamı yok.
Tool çağrılarını sandbox'a almak
Tool çağrılarını sandbox tarzı yapılarda çalıştırıp LLM'e yalnızca ilgili çıktıyı getirmek de bence önemli. Burada CodeAct yaklaşımını seviyorum. Basitçe şöyle: LLM bir görev için adımlar çıkarıyor, bu adımlar birden fazla tool içeriyor. Bunu bir Python kodu gibi düşünün; hangi tool'un çıktısını hangi tool'un alacağı da belli. Tüm adımlar LLM'den bağımsız olarak sandbox'ta çalışıyor ve LLM'e sadece sonuç geliyor. Böylece hem context'ten tasarruf ediyoruz hem de LLM'e gereksiz bilgi gitmiyor. Bu yaklaşımı araştırmanızı öneririm.
Skill'ler
Skill kısmı da önemli. Burada da her agent'a kendi yeteneklerini kazandırmak kıymetli. Ben skill'leri biraz da kalıcı hafıza olarak görüyorum: Spesifik konularda, gerektiğinde agent'ın bağlamına yüklenen dosyalar.
Bu bölüm biraz uzun oldu ama en çok yanlış yapılan kısım burası olduğu için aklıma gelenleri yazmak istedim.
Görev dağılımı
Tool kısmında da bahsettiğim gibi tek bir agent'a tüm işleri yaptırmaya kalkmamalıyız. Agent'ları birer insan gibi düşünüp görev dağılımı yapmalı, alt agent'lar oluşturup her birine kendi sorumluluğunu vermeli, ardından da orkestrasyon üzerine çalışmalıyız. Bu konuda multi-agent sistem mimarileri var ama şimdilik onlara girmeyeceğim.
Birden fazla agent'la çalışmak maliyetli görünse de aslında sizi gizli maliyetlerden kurtarır. Bugün sıkça kullandığımız kodlama agent'larının çoğu da benzer mantıkla çalışır: Tek bir agent çalışıyormuş gibi görünse de arka planda agent'lar spawn edip işleri onlara verir ve ana session'daki bağlamı temiz tutar.
Bunun için yüzlerce agent tasarlayıp hepsini A2A protokolüyle haberleştirmek, her birini ayrı sandbox'ta çalıştırmak zorunda değiliz. Örneğin üzerinde çalıştığım Elf'e bir agent spawn tool'u verdim. Alakasız, uzun sürecek, bağlamı kirletecek ya da session'a aşırı bilgi yükleyecek işleri bu tool ile ayrı bir session'a atıyor, sadece sonucu getiriyor ve kaldığımız yerden devam ediyoruz. Böylece kendi agent takımını kendisi kurup iş yapabiliyor; gerekirse arka planda Claude Code'u çalıştırıp ona da iş yaptırabiliyor.
LLM seçimi
LLM işin sadece bir kısmı desek de beyin bizim için çok önemli. Ortada çok fazla model var ve hepsini deneyip verimliliğini ölçemeyiz.
Çok güçlü frontier modeller pahalı. 5-10 dakikalık işlerde 15-20 dolarlık faturalar çıkabiliyor. Böyle deyince az gibi duruyor ama asıl soru şu: Hangi iş 5-10 dakika sürüyor? :) Güçlü model elbette kıymetli ama tüm işi ona yaptırmak gereksiz. Claude Sonnet de tool çağırabiliyor, güzel çıktılar veriyor; ama herkes Opus diyor.
Burada önemli olan plan. Anthropic'in agent tasarlarken mutlaka düşünülmesi gereken bir önerisi var: Advisor stratejisi. Sonnet ya da Haiku işi yürütüyor, çözemediği bir karar noktasına geldiğinde Opus'a danışıyor. GitHub Copilot'ta da benzer bir fikir HydraFusion adıyla geldi; o da işe göre ucuz bir modelle başlayıp gerekirse daha güçlü modele çıkıyor ya da farklı aileden bir modelle taslağı eleştirtiyor. Hatta Copilot'un bu noktadaki çeşitliliği Claude'a göre bir tık daha iyi, çünkü lokal modellerle de çalışabiliyor.
Amaç, çok reasoning yapan, yavaş ama iyi sonuç veren modelleri her yerde kullanmak yerine onlara planı çıkarttırıp işi daha hızlı modellere yaptırmak. Böylece hem maliyetten tasarruf ediyor, hem hız kazanıyor, hem de birden fazla modelin faydasını tatmış oluyoruz.
Bu arada session içinde sık sık model değiştirmeyi pek önermiyorum, çünkü maliyet açısından LLM cache önemli. Yukarıdaki yaklaşımlar bu konuyu genelde kendi içinde çözüyor ve cache kaybına rağmen maliyeti düşürüyor. Ama bunlardan birini kullanmıyorsanız session içinde modeli çok fazla değiştirmeyin derim.
Doğrulama
Agent'ın çıktısı elbette her zaman güvenilir olmayacak. Şu aşamada genellikle çıktıyı kendimiz doğrulamaya çalışıyoruz. Geçenlerde Uncle Bob'un bir tweet'ini okumuştum (tweetlerini tavsiye ederim, özellikle yapay zeka kullanımıyla ilgili güzel tüyolar veriyor). Kabaca, agent'ın çıktısını sürekli insan gözüyle kontrol etmenin agent'ın faydasını kısıtladığından bahsediyordu.
Aslında doğru bir noktaya değiniyor. Agent'ın amacı işimizi hızlandırmak; ama agent çok fazla çıktı verdiğinde bunu insan gözüyle review etmek çok zaman alıyor ve kazandığımız faydayı geri vermiş oluyoruz.
"Agent'ın çıktısını testlerle kontrol ettirelim" yaklaşımı da var ama orada da bir sorun var: Agent kendi yazdığı kodun doğru olduğunu varsayarak test yazıyor, böylece yanlış kod yanlış testte doğru çalışıyor :) Bu bir döngü ve bir yerde müdahale gerekiyor.
GitHub Copilot'un Rubber Duck yaklaşımı bu noktada güzel: Bir modelin yazdığı planı, testi ya da kodu farklı bir aileden bir modele review ettiriyor. Farklı bir gözle bakıldığı için yazılanın doğru olup olmadığını daha net tartabiliyoruz.
İnsan gözüyle review yine önemli. Ama en azından uzun işleri devredip agent'lara rapor ürettirmek ve insan gözünü kritik noktalara ayırmak gerekiyor. Bu da agent tasarlarken dikkat edilmesi gereken noktalardan biri.
Sonuç
Çenem yine düşmüş, şimdi fark ettim :) Epey uzun bir yazı oldu, şimdilik burada bırakayım. Hoşunuza giderse ikinci bir yazı da yazabiliriz; o zaman daha çok kod üzerinden gidip ajanı kodla nasıl tasarlarız ona odaklanırız. Ya da aklıma farklı başlıklar gelirse onları ayrı bir yazıda paylaşırım.
Kritik konu bence şu: Agent'ı bir insan gibi düşünmek. Duygulardan arınmış bir insan tabii :) Bu şekilde düşünürsek güzel bir noktaya varırız.
Keyifli okumalar...