
OCR'dan Piksellere, Fallback'i Kaybetmeden
GeekBye screenshot'larını OCR ile okumayı bıraktı ve gerçek görüntüyü vision modellerine göndermeye başladı — ki bu kolay kısım. Öğretici kısım, vision kullanıcıları için OCR'ı tamamen atlayan, vision olmayan modellere fallback'i sessizce kıran, ve güvenlik ağının paralel çalıştığı ve hiçbir maliyeti olmayan bir sürüme geri sarılması gereken optimizasyondur.
Bir şeyi screenshot'layıp bir AI'ya onun hakkında soru sormanı sağlayan her uygulama için gerçek bir yol ayrımı vardır. OCR çalıştırabilirsin — metni cihaz üzerinde çıkar, minik bir string gönder, ve resmi at. Ya da pikselleri gönderebilirsin — gerçek görüntüyü yolla ve vision yetenekli bir modelin onu okumasına izin ver. OCR ucuz ve küçüktür ama metin olmayan her şeye kördür: düzen, diyagram, kodun girintisi, hatanın altındaki kırmızı dalgalı çizgi. Pikseller bunların hepsini korur ama daha çok bayta mal olur ve görebilen bir model gerektirir. GeekBye o yol ayrımının OCR tarafında başladı ve v1.8.6 ile v1.8.7 boyunca piksel tarafına yürüdü. O geçiş bu hikâyenin kolay kısmı. İlginç kısım, onunla birlikte gelen, fallback'i kıran, ve geri alınması gereken optimizasyon.
Öncesi: metni oku, resmi at
Bu döneme kadar bir screenshot şöyle bir cevaba dönüşürdü: yakalama kısayoluna basarsın, client OCR çalıştırır — macOS'ta bir Swift VisionOCR binary'si aracılığıyla Apple'ın Vision framework'ü, Windows'ta bir PowerShell betiği, ikisi de tek bir IOcrEngine soyutlamasının arkasında — ve çıkarılan metin bir string'e sarılıp backend'e screenshotText olarak gönderilir. Görüntünün kendisi makineden hiç ayrılmazdı. Bu verimli, ve bir düz metin duvarı için gayet iyi. Ama bir screenshot nadiren yalnızca metindir. İki sütunlu düzeni olan bir iş ilanı, diyagramı olan bir kodlama problemi, girintisi ipucu olan bir stack trace'li bir hatadır. OCR bütün bunları satır satır bir transkripsiyona düzleştirir ve model, sorduğundan daha ince bir soruya cevap verir.
Geçiş: pikselleri gönder (v1.8.6)
v1.8.6 diğer yolu ekledi. sharp üzerine kurulu ve dolayısıyla çapraz-platform olan yeni bir imageOptimizer.ts, ham bir screenshot'a tam olarak üç şey yapar:
sharp(pngFilePath).resize({ width: 1920, withoutEnlargement: true }).jpeg({ quality: 80 })
En fazla 1920px genişliğe küçült (daha küçük bir yakalamayı asla büyütme), PNG'yi JPEG'e çevir, kalite 80. Çıktı, medya tipi ayrı bir alanda taşınan ham base64'tür, ve eskisinin yanında iki yeni istek alanında backend'e gider: screenshotImage ve screenshotImageMediaType, screenshotText'ten bağımsız. Backend artık bir görüntü, metin, ya da ikisini birden alabilir.
O optimizasyon adımı kozmetik değil. Ham bir screenshot PNG'si 3–8 MB'tır; yeniden boyutlandırılmış JPEG kabaca 200–500 KB'tır. Bu, yüklemede bir büyüklük mertebesi fark eder, ve modelin faturasını da kısar — vision modelleri bir görüntüyü çözünürlüğüne göre karolara böler ve karo başına ücret alır, yani genişliği 1920'de sınırlamak ve yeniden sıkıştırmak hem teldeki baytları hem de ödediğin görüntü token'larını doğrudan azaltır, ekran metni için anlamlı bir okunabilirlik kaybı olmadan.
Uygulama modelin görüp göremediğini bile nereden bilir? Backend bunu ilan eder. /api/config yanıtı plana göre anahtarlanmış bir ai.vision bloğu kazandı, ve client her config getirmesinde tek bir boolean'ı — aiSupportsVision — önbelleğe alır. Vision destekleniyorsa görüntüyü optimize et; desteklenmiyorsa OCR'a geri düş. Yani yetenek bir backend gerçeğidir, client tarafında önbelleklenir, ve OCR'ı çalıştırma ya da atlama screenshot başına kararı o bayraktan client'ta verilir. Hangi modelin gerçekten cevap verdiği hâlâ backend'e aittir.
Fazla kurnaz olan optimizasyon
İşte burada öğretici hale geliyor. Vision yolunun ilk hali OCR ile görüntü optimizasyonunu birlikte, paralel çalıştırıyordu. Sonra bir "perf" commit'i onu bir ya-o-ya-öbürüne sıkıştırdı: plan vision destekliyorsa, görüntüyü optimize et ve OCR'ı tamamen atla; desteklemiyorsa, OCR çalıştır ve görüntü işini atla. Kâğıt üzerinde bu bariz kazanç — vision modelinin ihtiyaç duymadığı metni neden çıkarasın ki? Changelog bunu ilan bile ediyor: "OCR is skipped when the AI can read the image directly."
Sorun şu tek kelime: fallback. Backend'in tek bir modeli yok; bir birincili ve bir fallback'i var, ve fallback vision olmayan bir model olabilir. Sırayı canlandır: planının birincil modeli vision destekler, bu yüzden client gururla OCR'ı atlar ve yalnızca görüntüyü gönderir. Birincil sağlayıcı takılır. Backend yalnızca-metin bir modele geri düşer — ki ona artık okuyamadığı bir görüntü ve hiç metin verilir, çünkü client metni optimize edip yok etti. Hızlı yol güvenlik ağını sessizce silmişti. Gereksiz bir hızlı yolu güvenle düşürebilirsin; hızlı yolun etrafından dolaştığı şeyi düşüremezsin.
Çözüm: birincile değil, fallback'e göre karar ver
Çözüm çalmaya değer kısım. Her zaman OCR çalıştırmaya geri dönmek değildi — bu, yaygın durum için kazancı çöpe atardı. Atlamayı ikinci bir yetenek bayrağına koşullu kılmaktı: fallback vision destekliyor mu? Backend'in config'i bir fallbackVision bloğu kazandı; client aiFallbackSupportsVision'ı ilk bayrağın yanında önbelleğe alır. Artık kararın dört dalı var:
- Birincil ve fallback ikisi de görür → görüntüyü optimize et, OCR'ı atla. Hızlı, yaygın yol.
- Birincil görür ama fallback kör → görüntüyü optimize et ve OCR çalıştır, fallback için sigorta olarak.
- Görüntü optimizasyonu hata fırlatır → sert bir fallback olarak OCR.
- Planın hiç vision'ı yok → yalnızca OCR.
Ve ikinci dal — sigorta durumu — son hamlenin önem kazandığı yer. OCR'ı görüntü optimizasyonundan sonra çalıştırmak, gecikmesini üstüne geri eklerdi. Yani ikisi tek bir Promise.all altında birlikte çalışır: görüntü optimizasyonu ve OCR örtüşür, ve yalnızca backend ihtiyaç duyarsa diye var olan OCR, görüntünün zaten optimize edildiği süre içinde biter. Güvenlik ağı, kullanılmadığı sürece duvar-saati açısından bedavadır. Bütün numara tek cümlede: ucuz fallback'i tut, onu fallback sağlayıcının gerçekten ona ihtiyaç duyup duymadığına bağla, ve paralelleştir ki seni kurtardığı güne dek hiçbir maliyeti olmasın.
Atlamanın tetiklediği iki küçük tuzak
Ertelenen işi agresif biçimde atlamanın, işin olacağını sessizce varsayan her yeri açığa çıkarma huyu vardır. İkisi aynı hafta PR incelemesinde ortaya çıktı.
Bir kuyruk kilidi. Screenshot kuyruğu bir öğeyi OCR metni olduğunda "hazır" sayıyordu. Vision screenshot'ları artık hiç OCR metni almıyordu — tasarım gereği — yani tamlık kontrolü hasIncompleteOCR sonsuza dek bekledi ve kuyruk askıda kaldı. Çözüm "hazır"ı optimize edilmiş bir görüntüsü var ya da OCR metni var olarak yeniden tanımlar, ki pipeline'ın artık gerçekten garanti ettiği şey budur.
Bayat bir yetenek bayrağı. aiSupportsVision önbelleklenir, bu da tam yanlış yönde bayatlayabileceği anlamına gelir: bir kullanıcı oturumu kapatır, ya da bir config getirmesi başarısız olur, ve client artık onları desteklemeyen bir modele hâlâ görüntü gönderebileceğine inanır halde kalır. Bayrak artık oturum kapatmada ve config-getirme başarısızlığında sıfırlanır — önbelleklenmiş bir yeteneğin, tarif ettiği şey ortadan kalktığında ne olacağına dair bir hikâyesi olmalı.
Sayılar üzerine bir söz
Çünkü bu serinin amacı dürüstlük: commit mesajları rakamlar taşır — görüntü optimizasyonu "~30ms" karşısında OCR "~100–150ms," payload'lar "~200–500 KB vs ~3–8 MB PNG" — ve bunlar makul, ama yazarın kendi satır-içi tahminleri, bir benchmark'ın çıktısı değil. Geçmişin hiçbir yerinde ölçülmüş uçtan-uca öncesi/sonrası gecikme yok. Hız argümanını sağlam bir tasarım niyeti olarak ele al, kanıtlanmış bir sonuç olarak değil. Payload-boyutu düşüşü, güvenebileceğin tek sayıdır, çünkü doğrudan yeniden boyutlandırma ve yeniden sıkıştırmadan çıkar.
Bu release'in bize öğrettiği üç şey
- Pikselleri göndermek metni okumayı yener — model görebildiğinde. OCR bir screenshot'ın karakterler dışında ilettiği her şeyi atar. Bir vision modeli düzeni, diyagramı ve girintiyi okur, ve optimize edilmiş bir JPEG bunu karşılanabilir kılar. Ama bu bir yetenek bahsi, işte bu yüzden bir fallback'e ihtiyacı var.
- Bir hızlı yolu optimize edip yok edebilirsin, güvenlik ağını asla. Vision kullanıcıları için OCR'ı atlamak birincil için doğruydu ve fallback için yanlıştı. Bug atlama değildi; önemli olan fallback'in yeteneğiyken birincilin yeteneğine göre atlamaktı.
- Bedava bir güvenlik ağı, paralelleştirilmiş olandır. Sigorta işini gerçekten gerekip gerekmediğine bağla, sonra onu hızlı yolun yanında çalıştır ki örtüşsün. Doğru yapıldığında, zarif düşüş, özelliğinin hâlâ çalışmasının tek nedeni olduğu güne dek hiçbir maliyeti olmaz.
v1 hikâyesinin bir önceki bölümü için, bir click-through overlay'in iki başarısızlık modu (v1.8.5); tüm yay için de yazılımı kusursuzluğa kadar sevk etmenin anatomisi.