İçindekiler
- Problem
- Tasarım kararları ve alternatifler
- Gerçek bir hata: benzer görünen metni sızdıran maskeleme
- Kullanım
- Sınırlar
Problem
Pek çok uygulama, kullanıcının içinde arama yapmak ya da soru sormak isteyeceği metinler
tutar: notlar, yardım makaleleri, kaydedilmiş mesajlar. Apple bunu cihaz üzerinde yapmak için
gereken parçaların çoğunu zaten veriyor. NaturalLanguage metni kelimelere ayırıp embedding'e
çeviriyor, Accelerate matrisleri hızlı çarpıyor, iOS 26 ve macOS 26'dan itibaren de Foundation
Models cihaz üstü bir dil modeline erişim sağlıyor. Eksik olan, bunları birleştiren katman:
dokümanı al, pasajlara böl, kişisel veriyi çıkar, embedding'e çevir, indeksle, ara, en iyi
pasajları atıf yapması talimatıyla modele ver ve hepsini uygulama açılışları arasında sakla.
SwiftIntelligence 2.0 bu katmandır, yalnızca bu katman. iOS 18 ve macOS 15 için, bağımlılığı
olmayan tek bir library target. Model eğitmez, görüntü ya da ses işlemez, sunucu tarafında
çalışan bir vektör veritabanı da değildir.
Bu dar kapsam bir yeniden yazımın sonucu. Repo Ağustos 2025'te geniş kapsamlı bir "AI/ML
framework" olarak açıldı. 1.2.2 sürümüne gelindiğinde 16 modüle ulaşmıştı ve 2.0.0
CHANGELOG'unun ifadesiyle "yaklaşık 50.000 satırın çoğu simüle edilmiş sonuç döndürüyordu
(rastgele güven değerleri, sabit cümleler, servis sağlayıcı stub'ları)". Haziran 2026'daki bir
sadeleştirme commit'i
(`32a1ca7`) release
script'lerini ve kullanılmayan modülleri kaldırdı, ama aynı zamanda kardeş bir klasöre,
../SwiftAI'ye path dependency ekledi; manifest artık başka hiçbir makinede çözülmüyordu.
14 Ekim 2026'da yeniden yazımın ilk commit'i
(`0e2dcbd`) eski
modülleri sildi, 15 Ekim'de de
PR #9 sonucu v2.0.0 olarak
birleştirdi. 1.x API'sinden hiçbir şey korunmadı; major sürüm olmasının nedeni bu.
Tasarım kararları ve alternatifler
Tek target, sıfır bağımlılık. 1.x'teki çok modüllü yapı, kodun ne kadar azının gerçekten
iş yaptığını gizliyordu. Yaklaşık 2.400 satırlık kaynak için ayrı library ürünleri (core,
search, privacy, generation) bakım yükü getirir, tek bir paket eklemek isteyen kullanıcıya
da bir şey kazandırmazdı. Kod büyürse sonradan bölünebilir.
KnowledgeBase bir actor. Swift 6 dil modunda actor, değişebilir indeksleri data race'ten
korumanın en kısa yolu. Ancak actor'ler reentrant'tır: bir çağrı await noktasında beklerken
başka bir çağrı çalışabilir. Bu yüzden add önce bir partideki bütün embedding'leri hesaplayıp
doğruluyor (bekleyen tek kısım bu), ardından hepsini hiçbir askıya alma noktası olmadan
depoluyor. Kaynakta bu açıkça yazıyor: "No suspension point from here on: concurrent calls
cannot interleave their changes." Böylece bir embedding hatası, KnowledgeBase'i yarım
indekslenmiş değil, hiç değişmemiş halde bırakıyor. search de aynı kurala uyuyor: tek
await'i sorgunun embedding'i. Kilitle korunan bir class'ı derleyici denetleyemezdi,
@MainActor ise aramaları UI thread'inde çalıştırırdı.
Hibrit arama ve reciprocal rank fusion. Her pasaj iki kez indeksleniyor: KeywordIndex
içinde kelimeleriyle (BM25) ve VectorIndex içinde vektör olarak. Vektörler başka
kelimelerle yazılmış pasajları buluyor; BM25 ise küçük bir embedding modelinin kaçırabileceği
"E-4012" gibi tam kodları yakalıyor. İki skor farklı ölçeklerde (cosine similarity sınırlı,
BM25 sınırsız), dolayısıyla ağırlıklı toplamak keyfi bir normalizasyon gerektirirdi.
RankFusion bunun yerine yalnızca sıralara bakıyor: bir öğenin skoru, listeler üzerinden
weight / (k + rank) toplamı; defaultSmoothing = 60.0, kaynaktaki yorumla "Cormack, Clarke
ve Büttcher'ın (2009) olağan smoothing sabiti". Beraberlikte öğelerin listelerde ilk görüldüğü
sıra korunuyor, yani sonuçlar deterministik.
Tam arama, yaklaşık index yok. VectorIndex birim uzunluktaki satırları tek düz bir dizide
saklıyor; cosine similarity böylece bir dot product oluyor ve arama tüm satırlar üzerinde tek
bir vDSP_mmul çağrısı. Hedef, tek bir kullanıcının ya da tek bir uygulamanın dokümanları;
README bunu "on binlerce pasaja kadar" diye tarif ediyor. Bu boyutta tam arama basit ve
recall kaybı yok. HNSW gibi yaklaşık bir index bu ölçekte hiçbir kazanç sağlamadan bellek,
karmaşıklık ve belirsizlik eklerdi. Bedeli, arama süresinin vektör sayısıyla doğrusal artması.
Varsayılan olarak hashing embedder. HashingEmbedder kelimeleri ve üç karakterlik
parçaları 512 boyuta hash'liyor. Model gerektirmiyor, her dilde ve iOS simülatöründe çalışıyor,
testlerde deterministik. Kendi 64-bit FNV-1a fonksiyonunu kullanıyor, çünkü kaynak yorumunun
açıkladığı gibi Swift'in Hasher'ı her process'te farklı seed'leniyor ve kaydedilip sonra
karşılaştırılan vektörler için kullanılamıyor. SentenceEmbedder ve ContextualEmbedder
Apple'ın embedding'lerini sarmalıyor; TextEmbedder protokolüne uyan her tip takılabiliyor.
Embedder'ın kimliği veriyle birlikte kaydediliyor ve farklı bir embedder ile yükleme,
karşılaştırılamaz vektörleri karşılaştırmak yerine KnowledgeBaseError.embedderMismatch
fırlatıyor.
Gerekirse cevap vermeyen, kaynağa dayalı cevaplar. GroundedPrompt pasajları
numaralandırıyor ve modele yalnızca onlardan cevap vermesini, [1] biçiminde atıf yapmasını
söylüyor. Pasaj bütçesi varsayılan olarak 6.000 karakter. Hiçbir şey eşleşmezse answer(_:)
modeli hiç çağırmadan AnswerError.noRelevantPassages fırlatıyor. Cevap kodu
canImport(FoundationModels) ve availability kontrollerinin arkasında; paket iOS 18 ve
macOS 15 için de derlenmeye devam ediyor.
Gerçek bir hata: benzer görünen metni sızdıran maskeleme
PersonalDataRedactor e-posta adreslerini, kart numaralarını, IBAN'ları ve diğer kişisel
verileri [EMAIL_1] gibi yer tutucularla değiştiriyor. İlk sürümü kalıplarını ham metin
üzerinde çalıştırıyordu; testler düzgün biçimlendirilmiş örnekler kullandığı için geçiyordu.
Oysa bir maskeleme aracının asıl işi tam da düzgün biçimlendirilmemiş metin.
`3e96601` commit'i,
sistematik bir denemenin bulduğunu kaydediyor: eski koda karşı 12 örnek değerden 10'u
tamamen sızdı. En dash, nokta ya da satır sonuyla gruplanmış kart numaraları, başka bir rakam
grubundan sonra gelen kart ("Order 12 4111 1111 1111 1111"), full-width ve aksanlı e-posta
adresleri, hemen arkasından bir kod gelen IBAN'lar ve full-width rakamlarla yazılmış IPv4
adresleri. Çakışan eşleşmelerde kısa olan atılıyor, onun çakışmayan kısmı da görünür kalıyordu.
Düzeltme, eşleştirmenin yapıldığı yeri değiştirdi. Kalıplar artık metnin katlanmış bir
kopyasında (FoldedText) çalışıyor: format karakterleri siliniyor, NFKC uyumluluk biçimleri
uygulanıyor, birleşik işaretler kaldırılıyor, her tire "-" ve her boşluk " " oluyor. Değişen
karakterlerin haritası her eşleşmeyi orijinal metne geri taşıyor. Kart adayları rakam
gruplarından oluşan koşular; her koşu içinde Luhn kontrolünden geçen en uzun pencere
kazanıyor. IBAN'lar ülkelerinin uzunluğuna kesilip mod 97 ile doğrulanıyor. Çakışan
eşleşmeler birleştiriliyor. Sonraki commit'ler aynı sınıftan başka varyantları da kapattı;
RedactionBypassTests suite'i bunların hepsini tutuyor, checksum'dan geçmeyen numaralar gibi
görünür kalması gereken benzerleri de dahil.
Aynı haftadan bir düzeltme de yakın bir dersi gösteriyor. VectorMath.normalized bir vektörü
vDSP ile en büyük değerine bölüyordu; Accelerate subnormal sayıları sıfıra çektiği için
[1e-39, 0, 0, 0] gibi sonlu bir vektör [inf, nan, nan, nan] oluyordu. Ekleme ve kaydetme
başarılı oluyor, ama yükleme kayıtlı vektörleri reddediyor ve KnowledgeBase bir daha
açılamıyordu.
`aabec82` commit'i bu
tür vektörleri önce düz Swift'te 2^64 ile ölçekliyor; bir test de embedding'i subnormal olan
bir dokümanı ekleyip kaydediyor, yüklüyor ve arıyor. Ders: bir invariant'ı veri yazılırken
ve okunurken ayrı ayrı kontrol etmek ve gidiş-dönüşü test etmek.
Kullanım
https://github.com/muhittincamdali/SwiftIntelligence.git adresini Swift Package Manager ile
2.0.0 sürümünden itibaren ekleyin, ardından dokümanları ekleyip arayın. Bu örnek README'den
birebir alındı; orada örnek uygulama tarafından derleniyor ve ReadmeSamples.swift üzerinden
paket testlerinde çalıştırılıyor:
swift
1let knowledge = KnowledgeBase()2try await knowledge.add("You can return any item within 30 days of delivery for a full refund.", id: "returns")3try await knowledge.add("Orders ship within two business days; delivery takes three to five days.", id: "shipping")4try await knowledge.add("Every laptop comes with a two-year warranty against defects.", id: "warranty")5 6let results = try await knowledge.search("How do I return an item for a refund?")7print(results.first?.documentID ?? "nothing found") // returns8print(results.first?.text ?? "") // You can return any item within 30 days …Maskeleme geri çevrilebilir; model yer tutucularla çalışabilir, uygulama da değerleri modelin
cevabına geri koyabilir:
swift
1let redactor = PersonalDataRedactor()2let redacted = redactor.redact("Mail jane@example.com or pay with 4111 1111 1111 1111.")3print(redacted.text) // "Mail [EMAIL_1] or pay with [CARD_1]."4 5// After a model has answered from the redacted text, put the values back.6let reply = redacted.restoringOriginals(in: "I wrote to [EMAIL_1].")7print(reply) // "I wrote to jane@example.com."v2.0.0 etiketinde macOS'ta swift test 22 suite içinde 156 test çalıştırıyor; README iPhone
simülatöründe 155 test bildiriyor. Simülatörde eksik NaturalLanguage modellerine ihtiyaç duyan
testler gerekçesiyle birlikte atlanıyor.
Sınırlar
- Ölçek. İndeksler tam ve bellekte. Tek bir kullanıcının ya da uygulamanın dokümanlarına,
on binlerce pasaja kadar uygun. 2.0.0 sürümü yayımlanmış bir benchmark içermiyor.
- Orijinal metin kaydedilmiyor. Kaydedilen KnowledgeBase maskelenmiş pasajları ve
vektörlerini tutuyor. Embedder değiştirmek, dokümanları yeniden eklemek demek.
- Maskeleme bir güvenlik ağı, garanti değil. Yazıyla yazılmış ("jane at example dot com"),
sıradan kelimelerle bölünmüş ya da checksum'ı geçersiz veri bulunmuyor; isim tespiti
istatistiksel.
- Anlamsal arama kalitesi Apple'ın modellerine bağlı. Bunlar küçük, genel modeller ve iOS
simülatöründe yoklar. README, kendi testleriniz aksini göstermedikçe hibrit modda kalmayı
öneriyor.
- Cevaplar iOS 26 ya da macOS 26 gerektiriyor; cihazda Apple Intelligence açık olmalı. Model,
dokümanların içine yazılmış talimatlara uyabilir; SECURITY.md bunu bilinen bir sınır olarak
listeliyor.
- Kaydedilen dosyalar paket tarafından şifrelenmiyor. SECURITY.md kaydederken
.completeFileProtection kullanılmasını öneriyor.
README'deki yol haritasında iki adım var: doküman kaynağı olarak PDF'ler ve görseller (PDFKit
ve Vision metin tanıma) ve Apple'ın yerleşik embedding'lerinden daha güçlü anlamsal arama için
bir Core ML sentence-encoder embedder'ı.
Kaynak kod: github.com/muhittincamdali/SwiftIntelligence

