Нотатки з поля · три дні роботи

Як я повертав до життя мертвий текст

Чиста реалізація найстарішого трюку Punto Switcher — зроблена за три дні, спостерігаючи, що воно робить, і жодного разу не торкаючись його коду; а потім бенчмарки показали, де я помилявся.

Rust · WASM EN ↔ UK знаковий n-грам точність 1.000
[ х
[ks,хліб
одна клавіша. два значення. увесь застосунок — це вибір правильного.

Проблема

Правильні клавіші, але не та розкладка

Ти сідаєш написати привіт, українська розкладка не ввімкнена — і клавіші виходять як ghbdsn. Або хочеш hello, а отримуєш руддщ. Якщо ти пишеш двома мовами, це трапляється сто разів на день, і щоразу треба зупинитися, стерти, перемкнути, набрати заново.

Punto Switcher — багаторічний інструмент Яндекса — зробив собі ім’я саме на тому, що виправляє це за тебе: стежить за клавішами, впізнає абракадабру, тихо перенабирає її в іншій розкладці. Я хотів те саме для української — але без інструмента, який іде в комплекті. Тож зробив Upyr: упир — це той, хто повернувся з мертвих, а це приблизно те, що застосунок робить із твоїм понівеченим текстом. Rust, чиста реалізація, жодного рядка спільного з Punto.

Поміняти літери — це легка частина: QWERTY та ЙЦУКЕН — це фіксовані позиційні карти, пошук на п’ятдесят рядків. Складне — зрозуміти, коли їх міняти. Кожне завершене слово — це «так» чи «ні»: лишити чи переписати? Перепишеш слово, яке людина справді мала на увазі, — і ти щойно спотворив її текст. Саме через це видаляють застосунки. Пропущене виправлення — це знизане плече; хибне виправлення — це зрада. Тому весь дизайн схиляється до одного правила: сумніваєшся — не роби нічого.

Як воно вийшло

Три дні: від першого релізу до підписаної v0.2.0

Уся історія — просто в логу комітів. Кожен вузол нижче — реальний коміт, а гранатові — це моменти, коли щось клацнуло, або коли бенчмарк упіймав мене на помилці.

День 1 · 16 лип.Ключова ідея приходить
  • Initial Upyr releaseзастосунок у треї, зміна розкладки, обв’язка доступності macOS
  • feat: train compact language n-gram modelз’являється статистичний оцінювач
  • fix: recognize mistyped Ukrainian bracket words
    момент, коли дизайн повертає: [ks, — це не пунктуація, це хліб
  • fix: honor Ukrainian physical punctuation keys
    оцінювання переходить у простір фізичних клавіш, незалежний від розкладки
День 2 · 17 лип.Загартовування й винесення ядра
  • fix: handle names and terminal punctuation
    зберегти кому в кінці: Jkmuf,Ольга,
  • test: simulate multilingual typing streamsпокриття послідовностей подій
  • feat: extract core and add wasm delivery foundation
    рушій стає переносним — та сама логіка рішень у браузері
  • release: harden Upyr 0.1.0 public previewперша тегована збірка, універсальний бандл macOS
День 3 · 18 лип.Вимірювання й ланцюг постачання
  • perf + trigger layer + recall benchmark
    висновки впроваджено: шар тригерів для сліпої зони коротких слів, правила політики для колізій, які знайшла відкладена вибірка
  • ci(security): scanners, SBOM, and Cosign signingCycloneDX SBOM, безключові підписи → v0.2.0

Дизайн, відтворений із поведінки

Рішення у два шари — бо один шар не буває водночас і безпечним, і повним

Punto ніде не каже, як воно вирішує, тож я просто дивився, що воно робить — клавіші на вході, виправлення на виході — доки крізь це не проступила форма. Це два шари, і Upyr копіює обидва. Коротка таблиця жорстких правил має перше слово й може скасувати будь-що. Усе інше йде до статистичної моделі, яка радо каже «не знаю» й мовчить. Ту таблицю правил узято прямо з Punto’вого triggers.dat — мій вихідний файл прямо зізнається в цьому в коментарях.

Про етику розбирання Жодних докорів сумління. Я вивчав лише видиму поведінку Punto — послідовності клавіш на вході, виправлення на виході — і жодного разу її код; Upyr — чиста реалізація, що не має з нею нічого спільного. І тут є в’їдлива іронія: Punto Switcher постачає «щоденник» натискань, який записує все набране й вивантажує на сервери Яндекса — хрестоматійне визначення тієї шпигунської проблеми, яку середовище Upyr без жодної мережі й існує, щоб відкинути. Витягти добру ідею з інструмента, що логує твої натискання, відчувалося не свободою, а радше виправленням несправедливості.
готове слово фізичні клавіші трекер клавіш незалежний від розкладки простір KeyboardEvent.code ШАР 1 triggers.dat 12 точних правил · детерм. скасовують модель ШАР 2 знаковий n-грам 173,964 записи · ~2.8 MiB мовчить, коли не певен Виправити → Лишити тригер може змусити в будь-який бік

Два шари роблять різну роботу. Модель обережна й не вгадуватиме. Правила — для тієї жменьки випадків, у яких я вже впевнений, зокрема для коротких частих слів, які модель навмисно пропускає.

Чим було розбирання — а чим ні

Що насправді всередині Punto Switcher

Слушне запитання до всього, що зветься «розбиранням»: із чого Punto зроблено і чим його розкрили? Ось що задокументовано публічно — і чесний опис методу.

Punto Switcher — програма для Windows (punto.exe) від Яндекса. Розкладку вона обирає за словником у кілька мільйонів слів плюс правилом «неможливого поєднання» — російське слово не може починатися з певних літер, тож їхня низка викриває не ту розкладку — і перевіряє це на кожен пробіл, Enter чи Tab. Її стан живе в кількох двійкових, не-текстових файлах .dat:

ФайлРоль
Data/triggers.datправила-тригери перемикання — саме цей файл вихідник проєкту називає своєю моделлю
Data/ps.datосновний мовний словник (той список у кілька мільйонів слів)
Data/translit.datтаблиця транслітерації
User Data/replace.datавтозаміна / розгортання скорочень
User Data/user.dicкористувацький словник винятків
diary.datщоденник натискань

Публічна структура файлів, зі сторонніх оглядів — а не з інспекції коду Punto. Файли даних — зашифрований двійковий формат; навіть список автозаміни не відкриється в текстовому редакторі.

Файл, у який варто вдивитися Функція «Щоденник» (Дневник) Punto записує кожне натискання у diary.dat — чати в месенджерах, пошукові запити, паролі відкритим текстом — а програма тримає власне мережеве з’єднання; саме тому незалежні огляди вважають її кейлогером, а деякі антивіруси позначають збірку як PUA. Не побічний ризик: це поставлена, задокументована функція.

То яким був «інструментарій»? Його не було.

Ті файли .dat — зашифровані двійкові блоби. Upyr їх ніколи не зламував: жодного дизасемблера, жодного розпакованого ps.dat, жодного словника, знятого з Яндекса. Метод був чорноскриньковий: подаєш послідовності клавіш, дивишся на виправлення на виході — і відтворюєш архітектуру, яку ці файли передбачають, а не їхній вміст. У цьому весь сенс «чистої реалізації» — і тому словників Punto ти ніде в цьому проєкті не знайдеш. Відтворено було форму:

Вивчати публічну структуру файлів і видиму поведінку програми — це геть інше, ніж копіювати її код; і саме суворе дотримання першого дає Upyr не мати з Punto нічого спільного, водночас у неї навчаючись. Джерела про внутрішню будову Punto наведені внизу сторінки.

Ідея, що перекроїла дизайн

Моделюй фізичні клавіші, а не символи

Мій перший оцінювач ставив очевидне запитання: «на що ghbdsn більше схоже — на англійське чи на українське?» Це вже неправильне запитання, і чотири символи показали мені чому.

# на українській розкладці ці фізичні клавіші — ЛІТЕРИ, а не пунктуація [ ] ; ' \ , . / х ї ж є ґ б ю . # тож клавіші [ k s , — це не «дужка, дві літери, кома» — [ks, хліб (bread)

Прочитай рядок [ks, символ за символом — і побачиш дужку-літеру-літеру-кому: засмічений пунктуацією мотлох, який ти б нізащо не чіпав. Але це звичайне слово. Тому я перестав оцінювати літери на екрані й почав оцінювати клавіші під ними: Upyr відстежує фізичні позиції (ті самі назви, що браузер дає в KeyboardEvent.code) і питає модель про слово, яке ці клавіші утворили б в іншій розкладці. Саме ця зміна дає змогу [ks,хліб спрацювати, тоді як справжнє Jkmuf,Ольга, зберігає свою кому.

Висновки

Що сказав навмисно суворий бенчмарк

За поведінкою стежать два бенчмарки, і в обох списки слів заморожені через SHA-256 — тож числа відтворювані, а не «на око». Головні цифри:

1.000
точність на межовому корпусі зі 191 випадку
0
хибних виправлень на 20 000 «чистих» слів
173,964
записів знакового n-грама · ~2.8 MiB
12
детермінованих тригерів, стартовий набір

Висновок — розрив у повноті це вибір, а не межа

Точність (precision) лише каже, що я не ламаю добрий текст. Про повноту (recall) — як часто я справді виправляю зламаний — вона мовчить. Тож другий бенчмарк набирає 1 200 українських і 1 000 англійських слів на неправильній розкладці й проганяє реальний застосунок по них. Ось повнота за замовчуванням, розбита за довжиною слова:

10066330 0.0% 93.1% 98.5% ≤ 3 симв.4–6 симв.7+ симв. профіль за замовч. · українське слово на англ. розкладці · повнота, %

Рівні 0% на коротких словах — і це задум. За min_word_length = 4 найчастіші слова мови (не, як, на, що) не чіпаються взагалі. Це найбільша діра в повноті — і вона навмисна: у трьох символах замало сигналу, щоб подолати планку, поставлену досить високо, аби бути безпечною.

Модель сильна; консервативність — це політика

Перемкни на агресивні пороги — без жодної зміни моделі — і повнота підскакує. Тож та чверть українських слів, яку пропускає режим за замовчуванням, — це не стеля, а рішення «точність важливіша за повноту», яке можна викрутити.

10066330 75.1 92.3 94.3 99.4 УК → АНГАНГ → УК повнота, %, виправлення цілого слова · та сама модель, дві політики

92% / 99% лежать напоготові — щойно я вирішу, що ризик того вартий. Слова, які я «пропускав», були не стіною, об яку я вдарився, а налаштуванням, яке я сам обрав. Ручка, яку можна крутнути, щоразу краща за модель, яку довелося б перетреновувати.

І той, що переважив інтуїцію: більше даних зробило гірше

На старті я припускав: «більший корпус → краща модель». Історія каже інакше. Виграш дало налаштування ємності та квантування, а не обсяг.

90898887 89 88 89 90 100K наївний 1M +сила обраний 1M точні межові виправлення з 90 · ревізії моделі, зліва направо

Кинути в неї у 10× більше тексту зробило її гіршою. Я б цього нізащо не впіймав без фіксованого бенчмарку, проти якого можна міняти по одній речі за раз — і саме тому я написав бенчмарк перед «очевидним» апгрейдом, а не після того, як він мене вкусив. Окрема відкладена вибірка з 20 000 слів Вікіпедії (навмисно інший тип тексту, ніж тренувальні дані) тоді повернула 0 хибних виправлень — і виявила чотири справжні українські слова, що зіштовхнулися з англійськими; тепер вони закріплені як регресійні тести, щоб не могли повернутися.

Висновки → впровадження

Кожен висновок змінив конкретний рядок продукту

Бенчмарк, який не змінює код, — це просто гарне число. Кожен із цих таки змінив — ось коміт для кожного.

Що виявив бенчмаркЩо змінилося і де
Символьна модель не читає фізичні клавіші пунктуації як літери ([ks, = хліб) Оцінювання перейшло в простір фізичних клавіш, незалежний від розкладки; оцінювач міряє задуманий кандидат.fix: recognize mistyped Ukrainian bracket words · fix: honor Ukrainian physical punctuation keys
Власні назви з комою в кінці спотворювалися (Jkmuf,) Спершу оцінюється кандидат зі збереженою пунктуацією, потім звичайне перетворення.fix: handle names and terminal punctuation
Повнота за замовч. — рівні 0% на коротких словах, найчастіших у мові Шар детермінованих тригерів дає коротким певним послідовностям шлях в обхід порога впевненості моделі.perf + trigger layer + recall benchmark
Чиста вибірка виявила чотири колізії з українськими словами (зокрема дупу проти фізичного lege) Пом’якшення пунктуації зробили одностороннім; неоднозначні пари тепер вимагають додаткового пів-запасу. Усі чотири закріплені як регресійні випадки.perf + trigger layer + recall benchmark
Наївний корпус у 10× дав гірший результат (88/90) Відбір налаштував ємність + силу квантування, а не обсяг — 1M / 1.75× / 32 дав 90/90 без жодної помилки на чистій вибірці.feat: train compact language n-gram model

Чому ця реалізація краща — і в чому саме

Не більше функцій. Більше того, що можна перевірити.

Скажу чесно: Punto — двадцятирічний продукт, який уміє куди більше за це, і він випустив цей трюк задовго до мене. Я роблю вузьке твердження: для завдання «англійська ↔ українська», за рядками нижче, моя версія сильніша. І кожну клітинку Upyr можна піти й перевірити самому — у цьому вся суть.

ВимірPunto SwitcherUpyr
МовиНасамперед російськаEN ↔ UK повноцінно, зокрема український ряд пунктуації [];'\,./
ПлатформиЛише WindowsmacOS основна; Windows / Linux у превʼю; і той самий рушій у браузері через WASM
Приватність під час роботиЗакрита; постачає «щоденник» натискань, що записує набране й вивантажує на ЯндексЖодної телеметрії, жодного HTTP-клієнта в застосунку — це стереже check_privacy.py у CI
Безпека памʼятіНативна, доба до RustRust наскрізь
Докази коректностіНе опублікованіТочність 1.000 на 191 межовому випадку; 0 / 20 000 на чистій вибірці; корпуси закріплені через SHA-256, відтворюються однією командою
Ланцюг постачанняПропрієтарний бінарникMIT; CycloneDX SBOM, безключові підписи Cosign, SHA256SUMS, атестації походження
МодельНепрозораЗнаковий n-грам на 173,964 записи, ~2.8 MiB, пошук двійковим поділом, повністю задокументований

Чесні межі: це «краще за цими осями для цієї пари мов», а не «кращий продукт». Широта функцій, охоплення мов і два десятиліття загартовування на крайніх випадках усе ще належать Punto.

Що це таке, а що ні

Межі — так, як їх формулює сам бенчмарк

Що я з цього виніс Розбирати варто було не карту клавіатури — вона публічна й ніколи не змінюється. Варто було — судження: жорсткі правила для того, у чому певен, обережна модель для всього іншого, обидва працюють на фізичних клавішах, а не на літерах на екрані, і бенчмарк, суворий настільки, щоб двічі довести мою неправоту. Punto випустило все це двадцять років тому й ніколи не мусило пояснювати. Складаючи це назад руками, найбільше мене здивувало, скільки роботи — це навчити програму, коли тримати руки при собі.