
Daripada OCR ke Piksel Tanpa Kehilangan Fallback
GeekBye berhenti membaca screenshot anda dengan OCR dan mula menghantar imej sebenar ke model vision — yang merupakan bahagian mudahnya. Bahagian yang memberi pengajaran ialah pengoptimuman yang melangkau OCR sepenuhnya untuk pengguna vision, secara senyap mematahkan fallback ke model bukan-vision, dan terpaksa ditarik balik ke satu versi yang jaring keselamatannya berjalan selari dan tidak berkos apa-apa.
Ada satu simpang jalan yang sebenar bagi mana-mana aplikasi yang membenarkan anda men-screenshot sesuatu dan bertanya kepada AI mengenainya. Anda boleh menjalankan OCR — mengekstrak teksnya pada peranti, menghantar satu string kecil, dan membuang gambarnya. Atau anda boleh menghantar pikselnya — menghantar imej sebenar dan membiarkan satu model berkeupayaan vision membacanya. OCR murah dan kecil tetapi buta kepada segala yang bukan teks: susun atur, gambar rajah, inden kod, garis merah beralun di bawah ralat. Piksel mengekalkan semua itu tetapi berkos lebih banyak bait dan memerlukan satu model yang boleh melihat. GeekBye bermula di sisi OCR bagi simpang itu dan, sepanjang v1.8.6 dan v1.8.7, menyeberang ke sisi piksel. Peralihan itu ialah bahagian mudah cerita ini. Bahagian yang menarik ialah pengoptimuman yang datang bersamanya, mematahkan fallback, dan terpaksa dibatalkan.
Sebelumnya: baca teksnya, buang gambarnya
Sehingga episod ini, sebuah screenshot menjadi satu jawapan begini: anda menekan pintasan tangkapan, client menjalankan OCR — framework Vision milik Apple melalui satu binari Swift VisionOCR di macOS, satu skrip PowerShell di Windows, kedua-duanya di sebalik satu abstraksi IOcrEngine — dan teks yang diekstrak dibalut ke dalam satu string dan dihantar ke backend sebagai screenshotText. Imej itu sendiri tidak pernah meninggalkan mesin. Itu cekap, dan untuk satu dinding teks biasa ia memadai. Tetapi sebuah screenshot jarang sekali hanya teks. Ia ialah iklan kerja dengan susun atur dua lajur, satu masalah pengaturcaraan dengan gambar rajah, satu ralat dengan stack trace yang indennya ialah petunjuknya. OCR meratakan semua itu menjadi transkripsi baris demi baris dan model menjawab satu soalan yang lebih nipis daripada yang anda tanya.
Peralihannya: hantar pikselnya (v1.8.6)
v1.8.6 menambah laluan yang satu lagi. Sebuah imageOptimizer.ts baharu, dibina atas sharp dan oleh itu merentas-platform, melakukan tepat tiga perkara pada sebuah screenshot mentah:
sharp(pngFilePath).resize({ width: 1920, withoutEnlargement: true }).jpeg({ quality: 80 })
Ubah saiz kepada paling lebar 1920px (jangan sekali-kali membesarkan tangkapan yang lebih kecil), tukar PNG ke JPEG, kualiti 80. Keluarannya ialah base64 mentah dengan jenis media dibawa dalam satu medan berasingan, dan ia meluncur ke backend dalam dua medan permintaan baharu di samping yang lama: screenshotImage dan screenshotImageMediaType, bebas daripada screenshotText. Backend kini boleh menerima satu imej, teks, atau kedua-duanya.
Langkah pengoptimuman itu bukan kosmetik. Sebuah PNG screenshot mentah bersaiz 3–8 MB; JPEG yang diubah saiznya lebih kurang 200–500 KB. Itu satu order magnitud lebih kecil pada muat naik, dan ia juga memangkas bil model — model vision membahagikan sebuah imej kepada jubin mengikut resolusinya dan mengecaj setiap jubin, jadi mengehadkan lebar pada 1920 dan memampat semula secara langsung mengurangkan kedua-dua bait di kabel dan token-imej yang anda bayar, tanpa kehilangan kebolehbacaan yang bererti untuk teks di skrin.
Bagaimana aplikasi tahu sama ada model tersebut boleh melihat pun? Backend mengiklankannya. Respons /api/config memperoleh satu blok ai.vision yang diindeks mengikut pelan, dan client meng-cache satu boolean tunggal — aiSupportsVision — pada setiap pengambilan config. Apabila vision disokong, optimumkan imejnya; apabila tidak, jatuh ke OCR. Jadi keupayaan itu ialah satu fakta backend, di-cache di sisi client, dan keputusan setiap-screenshot untuk menjalankan atau melangkau OCR dibuat di client daripada flag itu. Backend masih memiliki keputusan model mana yang sebenarnya menjawab.
Pengoptimuman yang terlalu bijak
Di sinilah ia menjadi memberi pengajaran. Versi pertama laluan vision menjalankan OCR dan pengoptimuman imej bersama-sama, secara selari. Kemudian satu commit "perf" mengetatkannya menjadi salah-satu-atau: jika pelan menyokong vision, optimumkan imejnya dan langkau OCR sepenuhnya; jika tidak, jalankan OCR dan langkau kerja imej. Di atas kertas ini ialah kemenangan yang jelas — mengapa mengekstrak teks yang model vision tidak perlukan? Changelog malah mengiklankannya: "OCR is skipped when the AI can read the image directly."
Masalahnya ialah satu perkataan itu: fallback. Backend tidak mempunyai satu model; ia mempunyai satu primer dan satu fallback, dan fallback itu mungkin satu model bukan-vision. Bayangkan urutannya: model primer pelan anda menyokong vision, jadi client dengan bangga melangkau OCR dan menghantar hanya imej. Penyedia primer tersekat. Backend jatuh ke satu model teks-sahaja — yang kini diberi satu imej yang ia tidak boleh baca dan tiada teks, kerana client telah mengoptimumkan teksnya sehingga hilang. Laluan pantas itu secara senyap telah memadamkan jaring keselamatan. Anda boleh menggugurkan satu laluan pantas yang berlebihan dengan selamat; anda tidak boleh menggugurkan benda yang laluan pantas itu hanyalah satu jalan pintas untuk mengelaknya.
Penyelesaiannya: bersandar pada fallback, bukan primer
Penyelesaiannya ialah bahagian yang berbaloi dicuri. Ia bukan kembali kepada sentiasa menjalankan OCR — itu akan membuang kemenangan untuk kes biasa. Ia adalah menjadikan pelangkauan itu bersyarat pada satu flag keupayaan kedua: adakah fallback menyokong vision? Config backend memperoleh satu blok fallbackVision; client meng-cache aiFallbackSupportsVision di samping flag pertama. Kini keputusan itu mempunyai empat cabang:
- Primer dan fallback kedua-duanya melihat → optimumkan imej, langkau OCR. Laluan pantas yang biasa.
- Primer melihat tetapi fallback buta → optimumkan imej dan jalankan OCR, sebagai insurans untuk fallback.
- Pengoptimuman imej melontar ralat → OCR sebagai fallback keras.
- Pelan langsung tiada vision → OCR sahaja.
Dan cabang kedua — kes insurans — ialah tempat gerakan terakhir itu penting. Menjalankan OCR selepas pengoptimuman imej akan menambah latensinya semula di atasnya. Jadi kedua-duanya berjalan bersama di bawah satu Promise.all: pengoptimuman imej dan OCR bertindih, dan OCR yang wujud hanya kalau-kalau backend memerlukannya selesai dalam masa yang imej itu memang sedang dioptimumkan. Jaring keselamatan itu percuma dari segi masa-nyata bila-bila ia tidak digunakan. Itulah keseluruhan helahnya dalam satu ayat: kekalkan fallback yang murah, sandarkannya pada sama ada penyedia fallback anda benar-benar memerlukannya, dan selarikannya supaya ia tidak berkos apa-apa sehingga hari ia menyelamatkan anda.
Dua perangkap lebih kecil yang dicetuskan oleh pelangkauan itu
Melangkau kerja yang ditangguhkan secara agresif mempunyai cara untuk menonjolkan setiap tempat yang secara senyap menganggap kerja itu akan berlaku. Dua muncul dalam semakan PR pada minggu yang sama.
Satu kebuntuan baris gilir. Baris gilir screenshot menganggap satu item "sedia" apabila ia mempunyai teks OCR. Screenshot vision kini tidak pernah mendapat teks OCR — memang direka begitu — jadi pemeriksaan kelengkapan hasIncompleteOCR menunggu selama-lamanya dan baris gilir tergantung. Penyelesaiannya mentakrif semula "sedia" sebagai ada satu imej dioptimumkan atau ada teks OCR, yang memang benar-benar dijamin pipeline sekarang.
Satu flag keupayaan basi. aiSupportsVision di-cache, yang bermaksud ia boleh menjadi basi tepat ke arah yang salah: seorang pengguna log keluar, atau satu pengambilan config gagal, dan client tertinggal mempercayai bahawa ia mungkin masih boleh menghantar imej ke satu model yang tidak lagi menyokongnya. Flag itu kini diset semula semasa log keluar dan semasa kegagalan config-fetch — satu keupayaan yang di-cache mesti mempunyai jawapan untuk saat benda yang ia gambarkan lenyap.
Sepatah kata tentang angka-angkanya
Kerana kejujuran ialah tujuan siri ini: mesej-mesej commit membawa angka — pengoptimuman imej "~30ms" berbanding OCR "~100–150ms," payload "~200–500 KB vs ~3–8 MB PNG" — dan ia munasabah, tetapi ia ialah anggaran inline penulis sendiri, bukan keluaran satu benchmark. Tiada latensi hujung-ke-hujung sebelum/selepas yang terukur di mana-mana dalam sejarahnya. Anggap hujah kelajuan sebagai niat reka bentuk yang kukuh, bukan hasil yang terbukti. Pengurangan saiz payload ialah satu-satunya nombor yang anda boleh percaya, kerana ia terhasil terus daripada mengubah saiz dan memampat semula.
Tiga perkara yang diajarkan release ini kepada kami
- Menghantar piksel mengatasi membaca teks — apabila model boleh melihat. OCR membuang segala yang disampaikan sebuah screenshot selain aksara. Satu model vision membaca susun atur, gambar rajah, dan inden, dan satu JPEG dioptimumkan menjadikan itu mampu dibeli. Tetapi ia satu pertaruhan keupayaan, itulah sebabnya ia perlukan satu fallback.
- Anda boleh mengoptimumkan sehingga hilang satu laluan pantas, tidak pernah jaring keselamatan. Melangkau OCR untuk pengguna vision betul untuk primer dan salah untuk fallback. Bug itu bukan pelangkauan; ia adalah melangkau berdasarkan keupayaan primer sedangkan keupayaan fallback yang penting.
- Satu jaring keselamatan percuma ialah yang diselarikan. Sandarkan kerja insurans pada sama ada ia benar-benar diperlukan, kemudian jalankan ia bersebelahan dengan laluan pantas supaya ia bertindih. Dilakukan dengan betul, penurunan anggun tidak berkos apa-apa sehingga hari ia menjadi satu-satunya sebab ciri anda masih berfungsi.
Untuk bab sebelumnya dalam kisah v1, dua mod kegagalan sebuah overlay click-through (v1.8.5); dan untuk keseluruhan lengkungnya, anatomi mengeluarkan perisian hingga ke kesempurnaan.