Goa'ya bir gezi planlamak istiyorsunuz. Beş gününüz, 25.000 rupi bütçeniz var ve plajlar ile deniz ürünleri konusunda net bir tercihinize sahipsiniz. Normalde bu; on tane tarayıcı sekmesi açmak, güncelliğini yitirmiş forum yazılarını okumak ve manuel olarak bir gezi programı oluşturmak anlamına gelir. Bunun yerine, tek bir POST isteği gönderip yemek önerileri, aktivite listeleri ve kesin bir bütçe dağılımı içeren yapılandırılmış, gün bazlı bir plan almayı hayal edin. İşte bu proje tam olarak bunu sağlıyor.
Spring Boot ve Azure OpenAI kullanarak bir REST API oluşturacağız. API; bir destinasyon, bütçe, süre ve ilgi alanlarını kabul eder. Bir frontend veya mobil uygulamanın anında işleyebileceği temiz bir JSON döndürür. Kazıma (scraping) yok, sabitlenmiş (hardcoded) gezi programları yok. Sadece bir seyahat planlayıcısı gibi davranması için yönlendirilmiş bir yapay zeka modeli var.
API Neler Döndürür
Yanıt, regex ile parçalamanız gereken bir Markdown metni bloğu değildir. Günlük aktiviteleri, yemek önerilerini ve bütçe dökümünü içeren yapılandırılmış bir JSON nesnesidir. Bir Goa gezisi için, ilk gün için bir plaj kulübesinde kahvaltı için 500 rupi, sabah Palolem'de vakit geçirme ve belirli bir bölgede akşam deniz mahsulleri yemeği içeren bir bölüm alabilirsiniz. Her gün zaman dilimleri, tahmini maliyetler ve "plaj" veya "yemek" gibi etiketler taşır. Bu yapı önemlidir çünkü modern seyahat uygulamaları paragrafları ayrıştırmak (parse etmek) istemez. Onlar, RecyclerView'lara veya React bileşenlerine eşleyebilecekleri nesneler isterler.
Teknoloji Yığını ve Neden Uygun
Proje, Spring AI ile birlikte Spring Boot 3.5 kullanıyor. Spring AI kritik parçadır. Azure OpenAI'a karşı ham HTTP istemcileri yazmak zorunda kalmamanız için birleşik bir ChatModel soyutlaması sağlar. Servis kodunu değil, bağımlılıkları ve özellikleri değiştirirsiniz.
Build dosyanızda dört bağımlılığa ihtiyacınız var:
spring-boot-starter-webREST katmanı için.spring-ai-starter-model-azure-openaiSpring AI arayüzü aracılığıyla LLM'e bağlanmak için.springdoc-openapiotomatik Swagger dokümantasyonu için.Lombokistek ve yanıt POJO'larınızdaki gereksiz kodları (boilerplate) azaltmak için.
Spring AI, iş mantığınız ile LLM sağlayıcısı arasında yer alır. Bu konumlandırma kasıtlıdır. @Service sınıflarınızı temiz ve sağlayıcıdan bağımsız (provider-agnostic) tutar.
PromptTemplates ile Prompt Mühendisliği
Prompt'ları Java dizeleri (strings) içine sabit olarak yazmak (hardcoding), sürdürülemez yazılımlar oluşturmanın hızlı bir yoludur. Eğer ürün ekibi yapay zekanın daha samimi bir dil kullanmasına veya belirli bir eşiğin üzerindeki bütçe tahminlerini reddetmesine karar verirse, servisinizi yeniden derlemek zorunda kalmamalısınız.
Spring AI, PromptTemplate sağlar. Prompt iskeletini bir kaynak dosyasında veya özel bir şablon dizisinde saklayarak {destination}, {budget}, {days} ve {interests} gibi değişkenler için yer tutucular bırakırsınız. Çalışma zamanında (runtime), servis bir Prompt nesnesi oluşturur ve kullanıcının değerlerini içine enjekte eder.
Sistem mesajlarını kullanıcı mesajlarından ayırın. Persona tanımlamak için sistem mesajını kullanın. Örneğin, modele Hindistan destinasyonlarında uzmanlaşmış, bütçe odaklı ve yalnızca markdown işaretleri olmadan JSON döndürme konusunda katı bir seyahat planlayıcısı olduğunu söylersiniz. Belirli gezi ayrıntılarını iletmek için kullanıcı mesajını kullanın. Bu ayrım, daha sonra API sözleşmesini (contract) değiştirmeden personalar üzerinde A/B testi yapmak istediğinizde size yardımcı olur.
Servis Katmanı: Azure OpenAI ile İletişim
@Service sınıfının tek bir görevi vardır: Prompt'u oluşturmak, modeli çağırmak, yanıtı temizlemek ve sonucu ayrıştırmak.
Spring AI'ın ChatClient veya ChatModel bileşenini enjekte edin. Gelen istek değerleriyle PromptTemplate'i render edin, ardından sohbet (chat) metodunu çağırın. Yanıt bir String olarak gelir. Birçok eğitimin durduğu ve gerçek üretim (production) kodunun başladığı yer burasıdır.
LLM'ler bazen nazik giriş cümleleri ekler. "İşte gezi programınız" diye başlayan ve ardından üçlü ters tırnak (triple backticks) içine alınmış bir JSON döken bir yanıt alabilirsiniz. Bunu Jackson ile doğrudan ayrıştırmaya (deserialize) çalışırsanız uygulamanız çöker. Ham diziyi tarayan, ilk açılış parantezini ve son kapanış parantezini bulan ve yalnızca JSON içeriğini çıkaran küçük bir yardımcı metod ekleyin. Ardından çıkarılan bloğu doğrulayın. Nesneyi kontrolcüye (controller) döndürmeden önce gerekli alanların mevcut olduğunu ve sayısal değerlerin mantıklı olduğunu kontrol edin.
Bu savunmacı ayrıştırma (defensive parsing) isteğe bağlı değildir. Bir demo ile güvenilir bir API arasındaki sınır budur.
Hataları Olgun Bir Sistem Gibi Yönetmek
Harici API'lar hata verebilir. Azure OpenAI; hız sınırı (rate limit) hataları, kimlik doğrulama hataları veya geçici 500 hataları döndürecektir. Eğer bunları kullanıcıya stack trace'ler olarak yansıtırsanız, güvenilirliğinizi kaybedersiniz.
İstisnaları küresel olarak yakalamak için @RestControllerAdvice kullanın. Spring AI istisnalarını, HttpClientErrorException ve genel RuntimeExceptionları tutarlı hata yanıtlarına eşleyin. Net bir mesaj, hız sınırları (rate limits) için 429 gibi bir HTTP durumu ve istemcinin sorunu yeniden denemesi veya günlüğe kaydetmesi için yeterli ayrıntı içeren bir JSON gövdesi döndürün. Kullanıcı, Java sınıf isimleriyle dolu bir ekran yerine "Servis geçici olarak meşgul. Lütfen 30 saniye içinde tekrar deneyin" gibi bir mesaj görmelidir.
Gizli Bilgileri Asla Kodun İçine Sabitlemeyin
Azure OpenAI API anahtarınız, Git'e gönderilen application.properties dosyasının içinde yer almamalıdır. Bunu dışsallaştırın. Spring yapılandırmanızda referans verilen ${AZURE_OPENAI_KEY} ve ${AZURE_OPENAI_ENDPOINT} gibi ortam değişkenlerini kullanın. Geliştirme aşaması için yerel bir .env dosyası tutun, bunu .gitignore dosyasına ekleyin ve Spring Boot'un esnek bağlama (relaxed binding) özelliği aracılığıyla yükleyin. Bir anahtar sızarsa, uygulamanızı yeniden oluşturmak yerine tek bir yerden anahtarı yenileyebilirsiniz.
Swagger Üzerinden Test Etme
springdoc-openapi bağımlılığı, çalışma zamanında bir Swagger UI uç noktası sunar. Uygulamanız başladığında, tarayıcıda /swagger-ui.html adresini açın. Goa örneğini doğrudan doldurabilirsiniz: varış noktası "Goa", bütçe 25000, gün sayısı 5, ilgi alanları "beaches, food" şeklinde. "Execute" butonuna basın ve JSON seyahat planının oluşmasını izleyin. Bu, frontend geliştiricileri herhangi bir birim testi (unit test) yazmadan önce istem (prompt) değişikliklerini doğrulamanıza, serileştirmeyi kontrol etmenize ve canlı bir oyun alanını (playground) onlarla paylaşmanıza olanak tanır.
Kodu Yeniden Yazmadan Sağlayıcı Değiştirme
Girişimler sağlayıcı değiştirir. Belki Azure kredileri biter ya da maliyetleri düşürmek için yerel bir Ollama örneğine (instance) karşı çıkarım (inference) yapmak istersiniz. Spring AI, ChatModel arayüzünü soyutladığı için bu değişim mekaniktir. Maven bağımlılığını spring-ai-starter-model-azure-openai yerine başka bir starter ile değiştirin, özellikler (properties) dosyanızı yeni uç nokta ve anahtar ile güncelleyin ve servis sınıfınıza dokunmayın. Mobil uygulamanızın gördüğü API sözleşmesi (contract) tamamen aynı kalır.
Bu taşınabilirlik, bu mimariyi gerçek ürünler için özellikle kullanışlı kılar. Azure ile evlenmiyorsunuz; onu temiz bir Spring boru hattına (pipeline) takılmış tek bir motor olarak kullanıyorsunuz.
Asıl Çıkarılması Gereken Ders
Bir yapay zeka modeli uygulamanız değildir. Tahmin edilemez metinler döndüren harici bir servistir. Ona bir ödeme geçidine veya üçüncü taraf bir hava durumu API'sine göstereceğiniz titizlikle yaklaşın. Kimlik bilgilerinizi dışsallaştırın. Her yanıtı doğrulayın. Ayrıştırmadan (parsing) önce veri yükünü (payload) temizleyin. Kullanıcılarınızın asla bir yığın izi (stack trace) görmemesi için hataları küresel olarak yönetin.
25.000 rupilik bir bütçeyle Goa seyahat planı oluşturmanın yaratıcı işini yapay zekaya bırakın. Siz ise altyapı işlerini halledin. Bu ikisi ayrı kaldığında, gerçekten yayına alınabilir bir sistem elde edersiniz.
Bu makaleye ilham veren orijinal kılavuza buradan ulaşabilirsiniz.
Spring AI ve benzeri projeleri tartışmakla ilgileniyor musunuz? GyaanSetu öğrenme topluluğuna katılın.
