
Чому ШІ-транскрипція перекручує технічні терміни (і як ми це виправили)
Жива сесія почула «what is the pointer in C++» як «what is the point in life». Ось слід розслідування від того транскрипту до GeekBye v2.0.11 — keyterm biasing, гонка, що обривала з'єднання, і день, коли наш власний фікс обернувся проти нас.
2 липня ми запустили тестову сесію і поставили GeekBye уголос просте запитання: «What is the pointer in C++?» (що таке вказівник у C++).
Живий транскрипт відповів поезією:
[23:16:37] You: Tell me, what is the point in life? [23:16:52] You: Handy Plus. [23:17:02] You: What the pointer in Plus Plus? [23:17:09] You: C.
Замість «pointer in C++» (вказівник у C++) модель почула «point in life» (сенс життя). Решту в тій самій сесії розповіли метрики здоров'я: 3 обриви з'єднання транскрипції за 163 секунди та 51-секундна діра в транскрипті. І ще один слід, який виявився найважливішим: наш механізм відновлення після сесії — він повторно транскрибує локально збережене аудіо, щоб заповнити прогалини, — відтворив речення майже правильно: «a pointer in plus, plus? What the pointer in plus, plus C++.»
З аудіо все було гаразд. Просто жива модель не мала жодної причини очікувати C++.
Це історія релізу GeekBye v2.0.11, розказана за реальними транскриптами та продакшен-логами.
Чому мовленнєві моделі перекручують вашу лексику
Розпізнавання мовлення — це задача передбачення. Отримавши неоднозначне аудіо, модель обирає найімовірніші слова — а для моделі загального призначення фраза «point in life» значно ймовірніша за «pointer in C++». Кожен інженер, який бачив, як транскрипт зустрічі перетворює Kubernetes на «cube and eddies», знайомий із цим провалом особисто.
Ліки — це не кращий мікрофон, а keyterm biasing: повідомити моделі перед початком сесії, які малоймовірні слова ймовірні саме у вас. Наш мовленнєвий провайдер підтримує до 50 термінів-підказок на сесію. А тепер соромна частина: уся обв'язка для цих термінів існувала в нашому стеку від краю до краю — клієнт, бекенд, провайдер — і ніхто її жодного разу не заповнював. Кожна сесія працювала без будь-якої доменної допомоги.
Фікс 1: ваш профіль стає словником моделі
GeekBye вже знає ваш домен — він записаний у вашому активному профілі. v2.0.11 виводить keyterms для зміщення з назви та опису профілю: терміни із символами (C++, Node.js), акроніми (SQL, AWS), назви в camel case (TypeScript, PostgreSQL) і власні назви. Профіль, у якому згадано ваш стек, тепер робить цей стек очікуваним, а не екзотичним.
День, коли фікс зробив усе гірше
Наша перша версія вважала власною назвою кожне слово з великої літери. На внутрішній тестовій збірці (до клієнтів це не дійшло) профіль, написаний звичайною прозою, надіслав моделі такий список підказок:
Senior, Writing, Direct, For, Includes, Write, Role, Intent…
Зміщувати мовленнєву модель у бік слова «For» гірше, ніж не зміщувати її взагалі. У наступній же тестовій сесії слово «speak» — вимовлене чітко, кілька разів — повернулося як «Clicky», «Hey, Vicky» і «Peter Paderty». Урок коштував нам один вечір: зміщуйте лише характерними термінами. Слова з великої літери тепер враховуються, лише коли стоять усередині речення (справжня ознака власної назви); заголовки markdown, де з великої літери починається кожне слово, не дають внеску ніколи. Той самий профіль тепер виводить рівно LinkedIn, AI, CEO, MCP — а валідаційна сесія коректно транскрибувала багатомовне аудіо зі швидкими перемиканнями 199 секунд поспіль: 189 сегментів транскрипту, нуль помилок.
Фікс 2: гонка, що обривала з'єднання
Keyterms пояснили перекручені слова. Три обриви з'єднання вони не пояснювали.
Цей слід вів до чогось тоншого. Наш провайдер комітить (фіналізує) транскрипцію за власним детектором голосової активності — приблизно за секунду тиші. Наш клієнт теж надсилає страхувальний commit через 250 мілісекунд тиші, щоб дотиснути завислу незакінчену фразу. Підтвердження провайдера про те, що він уже закомітив, повертається за одну-три секунди. Складіть ці три числа: щоразу, коли провайдер комітив першим, наш страхувальний commit бив по майже порожньому буферу — і відповіддю провайдера була не ввічлива відмова. Він обривав з'єднання. Кожна пауза в мовленні була підкиданням монети.
v2.0.11 ставить проти цього два шари захисту:
- У застосунку: коли надходить закомічений транскрипт, клієнт тепер знає, що буфер провайдера щойно було скинуто, і пропускає зайвий страхувальний commit.
- На нашому бекенді, того самого дня: проксі між застосунком і провайдером точно віддзеркалює облік аудіо у провайдера — воно бачить кожен аудіофрейм і кожне підтвердження коміту з нульовою затримкою — і просто відмовляється пересилати будь-який commit, який провайдер відхилив би. Цей шар захищає одразу всі версії клієнта, включно з користувачами, які ще не оновилися.
Ми побачили, як це працює в продакшені, вже протягом години. Захист перехопив приречені коміти зі 178 мс та 256 мс буферизованого аудіо — кожен із них до того дня означав гарантований обрив з'єднання і діру в чиїхось нотатках із зустрічі. 60-хвилинна безперервна сесія того ж дня по обіді зафіксувала п'ять перехоплень і нуль обривів. До фіксу, того самого ранку, реальний користувач п'ять разів за шість хвилин перезапускав запис, воюючи саме з цим багом.
Два менші фікси в тому ж релізі
ШІ-інсайти тепер чекають на зміст. Ті спотворені ранні уривки раніше потрапляли в живі чипи-підказки GeekBye, які з перекрученого запитання про C++ впевнено видавали теми на кшталт «Defining Life's Ultimate Purpose». Тепер підказки чекають, доки в сесії набереться справжня розмовна маса.
Відновлений текст отримує правильного мовця. Механізм відновлення, який коректно транскрибував наше запитання про C++, приписав його «Them» (співрозмовнику). Локально збережена аудіохронологія тепер фіксує, хто говорив, тож відновлені сегменти коректно відносяться до вас або співрозмовника.
Табло результатів
| Метрика (виміряно, а не оцінено) | До | Після v2.0.11 + захист на бекенді |
|---|---|---|
| Обриви з'єднання в тестовій сесії | 3 за 163 с | 0 |
| Найдовша діра в транскрипті | 51 с | ~6 с — найгірша прогалина на валідації |
| «pointer in C++» | «point in life» | правильно, зі зміщеним словником |
| Приречені коміти, що дійшли до провайдера | усі | 0 (перехоплено на бекенді) |
Якщо ви будуєте на realtime-API розпізнавання мовлення
Три переносні уроки з цього релізу:
- Годуйте механізм зміщення. Якщо ваш STT-провайдер підтримує keyterms/підказки фраз, заповнити їх невеликим, характерним словником — найдешевший доступний виграш у точності; заповнити їх звичайними словами — втрата точності.
- Ніколи не змагайтеся зі скінченним автоматом провайдера з неправильного боку мережевого round-trip. Наш клієнт не міг виграти інформаційну гонку 250 мс проти 3 с. Захисту місце там, де сходяться обидва сигнали, — у нас це проксі на бекенді.
- Перевіряйте на живій збірці перед публікацією. Регресію з keyterms упіймали тому, що кожен реліз GeekBye перед виходом тестується як підписана, нотаризована збірка на продакшені. Погана версія прожила кілька годин на одній внутрішній машині, а не на вашому Mac.
GeekBye v2.0.11 уже доступний — якщо ви на v2, він уже прилетів через автооновлення. Про фундамент надійності, на якому побудовано цей реліз, читайте у статтях чому ваш ШІ-нотатник зупиняється на поганому Wi-Fi і що змінилося в GeekBye v2. А про те, як жива транскрипція працює день у день, почніть із транскрипції в реальному часі в GeekBye.

