Staja başlarken backend alanında ve AI alanında daha önce çok bir deneyimim yoktu ama staj sonunda RAG’den kendi modelimi eğitmeye kadar uzanan bir proje geliştirmiş olarak buldum kendimi. Peki bu nasıl oldu?

Öncelikle 20 iş gününde end-to-end bir proje için hızlı aksiyon alınması gerektiğini düşünüyorum. Ve bu konuda VakıfBank’taki mentörüm Esra İnce’ye teşekkür ederim. İlk iş günümde önümüzdeki 20 iş günü için neler beklendiğini, nasıl bir projenin ortaya çıkmasının istendiğini, hangi haftalarda nelerin tamamlanması gerektiğini bana açıkladığı bir mail attı ❤️

Bu arada artık proje demek istemiyorum, kendisinin adı Tutorai. Tutorai, yapay zekâ destekli multimodal bir kişisel asistan uygulaması. Peki Tutorai için hangi teknolojilerden yararlandım?

iOS kısmı bana tanıdık olan kısımdı ama benim bir backend’e, veritabanına ve AI tarafına da ihtiyacım vardı.

Projenin client tarafını iOS ile oluşturdum. Bu da mentörümün benim ilgi alanımı göz önüne alarak projede istediği şeylerden biriydi. Benim için diğer kısımlara nazaran safe area diyebiliriz. iOS tarafını geliştirirken UIKit ve SwiftUI’ı birlikte kullandığım hybrid bir yapı tercih ettim. Chat ekranı daha önce uyguladığım bir şey değildi, burası için MessageKit kullandım. Bundan dolayı o kısım UIKit tabanlıydı, diğer ekranların bazı view’larını ise SwiftUI ile yazdım. İki yapıyı bir arada kullanabilmek için UIHostingController’dan yararlandım.

Uygulamayı MVVM mimarisi ile yazdım. Bu şekilde sorumlulukları olabildiğince birbirinden ayırdım. Tutorai multimodal bir uygulama olduğu için chat ekranında yalnızca text mesajlarıyla uğraşmıyordum. Kullanıcının görsel, PDF ve ses gönderebildiği bir yapı yazdım. Bunları backend’e ulaştırabilmek için multipart/form-data kullandım. iOS tarafında benim için yeni olan konulardan biri de streaming’ti. Ama bu konuya sonrasında değineceğim.

Projenin backend tarafına gelecek olursak, .NET kullanarak geliştirdim. Veritabanı olarak PostgreSQL tercih ettim. İkisi arasındaki iletişimi ise Npgsql üzerinden gerçekleştirdim.

Backend kısmı benim için iOS’e göre daha yeni bir alan olduğu için mimariye dikkat etmeden yazmıştım ama proje büyüdükçe Controller – Service – Repository katmanlarına ayırarak ilerledim. Controller’ları request/response tarafında, Service katmanını business logic için, Repository katmanını ise veritabanı işlemleri için konumlandırdım. Katmanlı mimarinin neden kullanıldığını teoride makalelerde okudum ama ihtiyaç duyduğum bir projede uygulamak çok daha öğretici oldu benim için. Backend’imin sorumluluklarından biri de RAG akışını yönetmekti. RAG konusuna yazının devamında daha detaylı değineceğim.

Projenin AI kısmına gelelim ufaktan, benim için en az hâkim olduğum ama açıkçası en keyif aldığım kısım diyebilirim. Tutorai’da Gemini 2.5 Flash kullandım. Gemini’ye erişimi ise Google Cloud Vertex AI üzerinden gerçekleştirdim. Mentörümün projemde istediği şeylerdi bunlar. Ama kendim bu projeyi yapsam da Gemini kullanırdım çünkü multimodal bir proje geliştiriyorum. Gemini’nin yalnızca kendi genel bilgisini kullanarak değil, özellikle o dokümanın içerisindeki bilgilere dayanarak cevap vermesini istiyordum. İşte burada daha önce backend kısmında bahsettiğim RAG devreye girdi.

Tabii projenin AI tarafı yalnızca Gemini’den ibaret değildi. RAG, embedding ve Vector Search gibi yapıların yanında, ilk kez kendi document classifier modelimi train edip deploy etme fırsatım da oldu. Bundan da yazının devamında detaylı olarak bahsedeceğim.

RAG

Benim için asıl heyecanlı olan, daha önce hiç uygulama fırsatı bulamadığım bazı şeyleri bu projede gerçekten deneyebilmekti. Bunlardan biri de RAG’di. RAG’e neden ihtiyaç duydum peki? Tutorai’ya bir PDF yüklediğimde Gemini’nin yalnızca genel bilgisini kullanmasını değil, kullanıcının yüklediği dokümanı gerçekten anlayıp o dokümana göre de cevap verebilmesini istiyordum. İşte burada RAG devreye girdi. Peki bunu nasıl yaptım?

En basit haliyle, yapay zekâya cevap vermeden önce ihtiyaç duyabileceği ilgili bilgileri bulup vermek diyebiliriz. Tutorai’a yüklenen PDF’leri daha küçük parçalara ayırıp bu parçaları anlamlarını temsil eden sayısal verilere, yani embedding’lere dönüştürdüm. Bu chunk’ların her biri için gemini-embedding-001 kullanarak embedding oluşturdum ve bunları Vertex AI Vector Search tarafında indexledim. Kullanıcı dokümanla ilgili bir soru sorduğunda bu kez sorunun da embedding’ini oluşturup Vector Search üzerinde en yakın chunk’ları aradım.

Bulduğum ilgili parçaların gerçek text içeriklerini PostgreSQL’den alıp Gemini’ye context olarak ekledim. Gemini de cevabını oluştururken hem kendi bilgisini hem de dokümandan retrieve ettiğimiz bu bilgileri kullanabiliyordu.

"RAG yapısı nasıl çalışıyor"

Streaming

Benim için yeni olan bir diğer konu ise streaming’di. Kullanıcı olarak sık karşılaştığım bir şey olsa da developer olarak daha önce uyguladığım bir şey değildi. Daha önce iOS tarafında request-response yapısıyla çok kez çalıştım; bir istek gönderip response’un tamamının gelmesini beklemek alışık olduğum bir akıştı açıkçası ama bir chatbot uygulaması için bu uygun olmayacaktı. Reels kaydırmak gibi alışkanlıklarımızın olduğu bir dönemde o cevabın tamamının oluşmasını beklemek tad kaçırıcı olurdu :D

Bu yüzden cevapları stream ederek, Gemini cevabı üretmeye devam ederken oluşan parçaları kullanıcıya gösterdim. Backend’den iOS’a bu chunk akışını Server-Sent Events (SSE) ile sağladım. iOS tarafında ise gelen chunk’ları URLSession üzerinden dinleyip aynı assistant mesajının içeriğine ekleyerek UI’ı güncelledim. Böylece Gemini hâlâ cevabı üretmeye devam ederken kullanıcı da oluşan cevabı eş zamanlı olarak ekranda görebiliyordu.

Burada WebSocket yerine SSE kullanmamın sebebi de ihtiyacımın çift yönlü ve sürekli bir bağlantıdan ziyade, backend’den client’a doğru akan tek yönlü bir response olmasıydı. Kullanıcının mesajı normal bir HTTP request ile backend’e gidiyor, Gemini’nin oluşturduğu cevap ise SSE üzerinden stream ediliyordu.

"Streamig yapısı nasıl çalışıyor"

Kendi Modelimi Eğitmek

Tutorai’daki bir diğer konu da kendi classifier modelimi eğitmemdi. Proje genelinde zaten Gemini entegre durumdaydı ama hem mentörümün bu kısmı da görmemi istemesi hem de benim deneyimlemek istemem nedeniyle app’in küçük bir kısmında kullandığım, PDF’leri türlerine göre classify eden bir model yazdım. Projede beni en heyecanlandıran noktalardan biriydi diyebilirim.

Çünkü bu süreçte aslında model eğitmenin yalnızca birkaç satır kod yazıp “train” demekten ibaret olmadığını gördüm. Dataset’leri hazır buldum, o konuda problemim yoktu ama verinin modele uygun hale getirilmesi, modelin eğitilmesi ve uygulama tarafında kullanılabilir hale getirilip deploy edilmesi kısmı için aynı şeyi söyleyemeyeceğim :D Böyle söylesem de en keyif aldığım süreçlerden biriydi.

Modelimi TensorFlow/Keras kullanarak eğittim, ardından Vertex AI üzerinde deploy ederek backend’in kullanabileceği bir endpoint haline getirdim. “Gemini bunu zaten yapabilirdi, niye ben uğraştım?” Bu sorunun cevabını yukarıda söylemiştim: tamamen deneyimlemek ve öğrenmekti. Bir diğer soru da benim modelim ve Gemini arasında maliyet veya performans açısından nasıl farklılıklar olduğuydu. Gemini’nin 10 saniyede yapacağı şey için sen kendin oturup model yazınca insan merak edip araştırıyor maliyeti, performansı vs. hahaha.

Ben dediğim gibi sadece yüklediğim PDF’in türünü belirlemek için bir model yaptım, yani amacım spesifik bir şeydi. Bu yüzden şu an yazacaklarımı sadece bu case için yazıyorum. Gemini gibi bir LLM tabii ki çok daha esnek. Bir gün doküman türü olarak yeni bir şey eklemek istediğimde kendi modelimi yazmış olmamdan dolayı çok daha fazla efor sarf edeceğim. Ama benim geliştirdiğim model de tek bir probleme odaklandığı için daha kontrollü.

Bu kıyaslamada “bu, bundan daha iyi” diyemem; ikisinin de artıları ve eksileri var. “Kendi modelim kesinlikle daha ucuz” tarzında bir cümle de söyleyemiyorum. Hatta bu kadar küçük bir kullanımda ve trafik azken Gemini ekonomik olarak daha mantıklı bile olabilir. Trafik büyüdüğünde ise custom modelin boyutu, seçilen compute kaynakları ve kullanım yoğunluğu gibi faktörlere göre hesap değişebilir. Bu konu uzar gider gibi :D

Ve projeyi tamamladıktan sonra son günümde müdürüm Zekeriya Yolcu’ya ve sevgili mentörüm Esra İnce’ye sunum yaptım. Böylece VakıfBank’taki staj yolculuğumu tamamlamış oldum. Gözyaşım pıt :D

Tüm süreci kısaca(!) anlattığım bu blog yazısını zaman ayırıp okuduğunuz için sizlere de teşekkür ederim. Daha kısa blog yazılarımda görüşmek üzere. By by 👋

← More articlesBack to top ↑