Tüm Yazılar
KategoriiOS
Okuma Süresi
12 dk
Yayın Tarihi
...
Kelime Sayısı
1.480kelime

Kahveni hazırla - bu içerikli bir makale!

WWDC25'te tanıtılan Foundation Models framework'ü @Generable guided generation, Tool protocol ve streaming ile production'a taşıdık — 4096 token context limiti, guardrail false-positive'leri ve gerçek gizlilik kazanımlarıyla birlikte.

Apple Foundation Models Framework: Cihaz Üstü LLM'i Production'a Taşımak

# Apple Foundation Models Framework: Cihaz Üstü LLM'i Production'a Taşımak

WWDC25'te Apple, FoundationModels framework'üyle Apple Intelligence'ı besleyen ~3 milyar parametreli on-device dil modeline doğrudan Swift API'siyle erişim açtı. Bir yıl sonra WWDC26'da bu API'yi LanguageModel protokolüyle soyutlayıp üçüncü parti sağlayıcılara (Anthropic, Google) kapı araladılar. Bu yazıda framework'ü gerçek bir üründe kullanırken karşılaştığımız üç konuya odaklanıyorum: guided generation ile tip-güvenli çıktı, Tool protocol ile gerçek veriye erişim, ve 4096 token'lık context penceresinin production'da yarattığı sürprizler.

Bu framework'ü Core ML tabanlı özel model pipeline'larıyla karıştırmayın — orada kendi eğittiğiniz modeli çalıştırıyorsunuz, burada Apple'ın hazır ve sürekli güncellenen sistem modelini prompt'luyorsunuz. İki yaklaşımın ne zaman hangisini gerektirdiğini "Core ML on-device AI pipeline" yazımda ayrıca ele almıştım.

Mimari: SystemLanguageModel ve Session Yaşam Döngüsü

Framework'ün merkezinde SystemLanguageModel var — cihazdaki sistem modeline referans. Her etkileşim bir LanguageModelSession üzerinden yürüyor ve session, konuşma geçmişini kendi içinde tutuyor:

swift
1import FoundationModels
2 
3let session = LanguageModelSession(
4 instructions: "Sen kısa, teknik ve doğrudan cevaplar veren bir asistansın."
5)
6 
7let response = try await session.respond(to: "SwiftData ile CloudKit senkronizasyonunun temel riski ne?")
8print(response.content) // String

Production kodunda ilk yapmanız gereken şey respond çağırmak değil, kullanılabilirliği kontrol etmek. Model üç sebepten "yok" olabilir: cihaz uygun değil, Apple Intelligence kapalı, ya da model henüz indirilmedi.

swift
1switch SystemLanguageModel.default.availability {
2case .available:
3 // devam et
4case .unavailable(.deviceNotEligible):
5 showFallbackUI(reason: .unsupportedDevice)
6case .unavailable(.appleIntelligenceNotEnabled):
7 showFallbackUI(reason: .settingsRequired)
8case .unavailable(.modelNotReady):
9 showFallbackUI(reason: .downloading)
10case .unavailable:
11 showFallbackUI(reason: .unknown)
12}

Pro Tip: Bu switch'i uygulamanın en tepesinde, feature flag mantığıyla birlikte çalıştırın. "Model var ama indirilmemiş" durumu simülatörde hiç görünmez, gerçek cihazda Apple Intelligence ilk açıldığında sık karşılaşılan bir senaryodur — QA checklist'inize mutlaka ekleyin.

Guided Generation: @Generable ve @Guide

Framework'ün en pratik özelliği, modelin serbest metin yerine doğrudan Swift struct'ı üretmesini sağlayan guided generation. @Generable makrosu derleme zamanında bir şema üretiyor, model bu şemaya constrained decoding ile uyuyor — yani JSON parse hatası, eksik alan, halüsinasyonla uydurulmuş enum değeri gibi klasik LLM entegrasyon sorunları yapısal olarak ortadan kalkıyor.

swift
1@Generable
2struct MeetingSummary {
3 @Guide(description: "Toplantının tek cümlelik özeti")
4 let headline: String
5 
6 @Guide(description: "Aksiyon maddeleri, her biri kısa ve emir kipinde")
7 let actionItems: [String]
8 
9 @Guide(description: "Toplantının önem derecesi, 1 düşük 5 kritik", .range(1...5))
10 let priority: Int
11}
12 
13let session = LanguageModelSession()
14let result = try await session.respond(
15 to: "Bu toplantı notlarını özetle: \(transcript)",
16 generating: MeetingSummary.self
17)
18 
19let summary = result.content // MeetingSummary, String değil

.range, regex tabanlı .pattern gibi @Guide kısıtları sadece dokümantasyon değil — modelin decode sırasında uyması gereken gerçek kısıtlar. Pratikte bu, backend'e giden bir API şemasını LLM'e prompt olarak açıklamaktan çok daha güvenilir.

Pro Tip: @Generable struct'ları asla doğrudan domain modeliniz yapmayın. Aradaki bir mapping katmanı (MeetingSummaryMeeting) hem şema değişikliklerini izole eder hem de modelin ürettiği alanları backend'e giden alanlardan ayırır — model şemasını genişletmek istediğinizde domain modelinizi kırmazsınız.

Streaming: Partial Generation ile Algılanan Gecikme

3B'lik model hızlı olsa da, uzun bir yanıtı tek seferde beklemek UX'i öldürür. streamResponse her adımda T.PartiallyGenerated tipinde kısmi bir sonuç veriyor — derleyici bunu @Generable struct'ınızdan otomatik türetiyor, yani stream sırasında bile alanlara tip-güvenli erişiyorsunuz:

swift
1let stream = session.streamResponse(
2 to: "5 tane yaratıcı fitness uygulaması ismi öner",
3 generating: AppNameSuggestions.self
4)
5 
6for try await partial in stream {
7 // partial.names bir [String]?, dolarken UI'ı güncelleyin
8 await MainActor.run {
9 viewModel.names = partial.names ?? []
10 }
11}

SwiftUI tarafında bu, ChatGPT tarzı "yazarken görünme" efektini ekstra state machine kurmadan verir — for try await döngüsü zaten @Observable view model'inizi besleyebilir.

Tool Calling: Modeli Gerçek Veriyle Besleme

3B parametreli bir model, eğitim verisinin dondurulduğu andan sonrasını veya sizin özel veritabanınızı bilemez. Tool protocol bu boşluğu kapatıyor: modele hangi fonksiyonları çağırabileceğini söylüyorsunuz, o da hangi durumda hangi tool'u, hangi argümanlarla çağıracağına kendisi karar veriyor — framework paralel ve seri tool çağrı grafiğini otomatik yönetiyor.

swift
1struct FindSavedArticlesTool: Tool {
2 let name = "findSavedArticles"
3 let description = "Kullanıcının kaydettiği makaleler arasında arama yapar."
4 let index: ArticleIndex
5 
6 @Generable
7 struct Arguments {
8 @Guide(description: "Kullanıcının sorusundan kısa bir arama ifadesi")
9 var query: String
10 
11 @Guide(description: "Döndürülecek maksimum makale sayısı", .range(1...5))
12 var limit: Int
13 }
14 
15 func call(arguments: Arguments) async throws -> ToolOutput {
16 let results = await index.search(arguments.query, limit: arguments.limit)
17 return ToolOutput(results.map(\.title).joined(separator: "\n"))
18 }
19}
20 
21let session = LanguageModelSession(tools: [FindSavedArticlesTool(index: articleIndex)])
22let response = try await session.respond(to: "Geçen ay kaydettiğim SwiftUI makalelerini bul")

Burada kritik nokta: tool çağrısı cihazda gerçekleşiyor, sizin call(arguments:) implementasyonunuz network'e çıkabilir ama modelin kendisi hiçbir zaman ham veriyi Apple sunucularına göndermiyor — sadece kendi ürettiği argümanları ve sizin döndürdüğünüz sonucu görüyor.

Birden fazla tool tanımladığınızda framework, gerekirse bunları paralel çağırıp sonuçları tek bir nihai yanıtta birleştiriyor — siz orkestra mantığını elle yazmıyorsunuz, sadece her tool'un name ve description'ını yeterince açık yazmakla sorumlusunuz. Pratikte en sık yapılan hata, iki tool'un açıklamasının birbirine çok benzemesi: model hangisini çağıracağına kararsız kalıp yanlış tool'u tetikliyor. Tool sayısı beşi geçtiğinde açıklamaları gözden geçirip çakışan kelimeleri ayıklamak, prompt mühendisliğinden daha çok kazandırıyor.

Context Window: 4096 Token Duvarı

Burada gerçek üründe en çok canımızı yakan konuya geliyoruz. SystemLanguageModel.default'ün context penceresi 4096 token — GPT-4 sınıfı bulut modellerine alışkın bir ekip için şaşırtıcı derecede küçük. Pencere aşıldığında framework sessizce kısaltmaz, .exceededContextWindowSize hatası fırlatır ve o session artık kullanılamaz hale gelir.

Apple Developer Technote TN3193, tam da bu sorunu çözmek için context yönetimi rehberi yayınladı; iOS 26.4 ile SystemLanguageModel üzerine contextSize property'si ve tokenCount(for:) metodu eklendi — artık prompt'u göndermeden önce kaç token tükettiğini ölçebiliyorsunuz.

swift
1let model = SystemLanguageModel.default
2let promptTokens = model.tokenCount(for: longTranscript)
3 
4if promptTokens > model.contextSize - reservedForResponse {
5 longTranscript = summarizeOrTruncate(longTranscript)
6}
Senaryo
Riskli mi?
Öneri
Tek seferlik kısa prompt (özet, sınıflandırma)
Düşük
Doğrudan gönder, ölçüm gereksiz
Çok turlu sohbet (session state birikir)
Yüksek
Her turda `tokenCount` kontrolü + eski turları özetleyip session'ı sıfırlama
Uzun transcript/log analizi
Çok yüksek
Chunk'lara böl, her chunk'ı ayrı session'da işle
Tool sonuçları büyük JSON döndürüyor
Orta-yüksek
Tool çıktısını modele göndermeden önce özetle

Pro Tip: Çok turlu bir sohbet arayüzü kuruyorsanız, session'ı asla sınırsız büyütmeyin. N turdan sonra eski geçmişi kendi respond(to:generating:) çağrınızla özetleyip yeni bir session'a "context özeti" olarak enjekte edin — kullanıcı akışı kesintisiz görür, siz .exceededContextWindowSize crash'inden kurtulursunuz.

Gizlilik: Somut Kazanım Nerede?

Bulut LLM entegrasyonlarının en büyük operasyonel yükü KVKK/GDPR veri işleme sözleşmeleri, veri saklama süreleri ve "hangi veri hangi sunucuya gidiyor" denetimidir. Foundation Models'te on-device model çalıştığında:

  • Prompt, tool argümanları ve tool sonuçları cihazdan hiç çıkmıyor.
  • Apple Intelligence kapalıyken framework zaten .unavailable dönüyor — veri sızıntısı riski framework seviyesinde yapısal olarak yok.
  • Server tarafı gereken daha büyük görevler için Private Cloud Compute devreye giriyor; Apple'ın iddiası bu isteklerin de doğrulanabilir şekilde loglanmadığı ve sadece isteği işlemek için kullanıldığı yönünde — ama bu ayrı bir güven modeli, on-device ile aynı garanti seviyesinde değil.

Bu, "kullanıcı verisi asla üçüncü tarafa gitmesin" gereksinimi olan sağlık, finans veya kurumsal içerik uygulamaları için mimari kararı kolaylaştırıyor: hassas metni önce on-device modelle işleyip, gerekirse sadece anonimleştirilmiş özet bulut servisine gidiyor.

WWDC26: LanguageModel Protokolü ve Üçüncü Parti Sağlayıcılar

WWDC26'da framework'e gelen en büyük mimari değişiklik, model soyutlama katmanının açılması oldu. Yeni LanguageModel protokolü sayesinde hem SystemLanguageModel hem de sunucu tabanlı modeller aynı LanguageModelSession'ı besleyebiliyor; Anthropic ve Google kendi Swift paketlerini yayınlayarak frontier modellerine bu API üzerinden erişim açtı. Pratik sonucu: aynı @Generable/Tool kod tabanınızı koruyup, ihtiyaç anında on-device model yerine bulut modeline geçiş yapabiliyorsunuz — guided generation ve tool calling sözleşmesi değişmiyor.

Bu, ekip için gerçek bir mimari kazanç: özellik geliştirme sırasında on-device modelle hızlıca prototip çıkarıp, kullanıcı testlerinde 4096 token limitine veya akıl yürütme sınırına takılan senaryoları tespit edip yalnızca o akışları bulut sağlayıcısına yönlendirebiliyorsunuz. Tool protokolü ve @Generable şemanız değişmediği için bu geçiş, ayrı bir entegrasyon projesi değil, session başlatma satırında tek bir parametre değişikliği oluyor.

Ne Zaman Kullanmalı, Ne Zaman Kullanmamalı

Kullanım senaryosu
Foundation Models
Neden
Uygulama içi metin özetleme, sınıflandırma
✅ Uygun
Kısa prompt, düşük gecikme, offline çalışır
Yapılandırılmış form/veri çıkarımı (fiş, form OCR sonrası)
✅ Uygun
Guided generation tip güvenliği veriyor
Basit asistan + kullanıcının kendi verisine erişim (tool calling)
✅ Uygun
Veri cihazdan çıkmadan zenginleştirilmiş yanıt
Uzun doküman/kod tabanı analizi
⚠️ Dikkatli
4096 token limiti — chunking şart
Genel bilgi soru-cevap, güncel olaylar
❌ Uygun değil
3B model, bulut modelleri kadar geniş bilgiye sahip değil
Çok dilli karmaşık akıl yürütme
⚠️ Dikkatli
Guardrail false-positive oranı hâlâ bulut modellerinden yüksek

Sonuç

Foundation Models framework, "her AI özelliği için bulut API'sine bağlan" varsayımını kırıyor — özellikle kısa, yapılandırılmış, gizlilik-hassas görevlerde on-device model production'da gerçekten yeterli. Ama 4096 token context penceresi ve 3B parametrenin akıl yürütme sınırları, bunu "her LLM ihtiyacının çözümü" değil, doğru yerde kullanılan bir araç yapıyor. Bizim kuralımız basit: kullanıcı verisi cihazdan çıkmamalıysa ve görev kısa/yapılandırılmışsa önce Foundation Models'i deneyin, context veya akıl yürütme sınırına çarptığınız anda WWDC26'nın açtığı LanguageModel protokolüyle bulut tarafına sorunsuzca geçin.

Etiketler

#Swift#Foundation Models#Apple Intelligence#On-Device AI
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

12+ yıllık deneyime sahip Lead Mobile Engineer. Swift, SwiftUI, Kotlin ve Flutter ile iOS, Android ve cross-platform mimarilerde uzman. Performanslı ve kullanıcı dostu mobil uygulamalar geliştiriyorum.

iOS Geliştirme Haberleri

Haftalık Swift tips, SwiftUI tricks ve iOS best practices. Spam yok, sadece değerli içerik.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.

Paylaş

İlgili İçerik