
Kustutasime 5000 rida helikoodi — ja transkriptid hakkasid ilmuma kaks korda
GeekBye v1.6 rebis välja kaks seadmesisest Swift-transkribeerijat ja asendas need ühe ühtse pipeline'iga — netokustutus üle 5000 rea, tehtud sel ajal, kui inimesed olid rakenduses keset koosolekut. Seejärel hakkasid transkriptid ilmuma kaks korda, sest viga kolis sinna ühte kihti, mis endiselt arvas, et mootoreid on kaks.
Refaktoreerimises on üht kindlat sorti õudust, kui see kood, mida sa muudad, jookseb just praegu, inimeste jaoks, kes on keset koosolekut. Sa ei saa funktsiooni hoolduseks maha võtta — keegi loodab sellele, et transkribeerida vestlus, mida ta korrata ei saa. GeekBye v1.6 tegi täpselt seda rakenduse kõige kriitilisema teega: helipipeline'iga. See on lugu sellest, mida me kustutasime, mis läks katki, ja kahest töökindluse õppetunnist, mis sellest välja tulid.
Kaks mootorit, üks neist Swiftis
Enne v1.6 jooksis GeekBye kõne-tekstiks-teisendus seadmes Swift-binaarides. Neid oli kaks: üks mähkis Apple'i Speech-raamistikku ja suurem (üle 1,500 rea) hõivas duaalset heli — sinu mikrofon pluss süsteemiheli — ScreenCaptureKit'i kaudu ja avas oma enda WebSocketi otse meie backendi poole, et voogedastada Deepgrami. Kaks transkribeerijat, kaks hõivamisteed, kaks viisi võrguga rääkida, kõik kompileeritud keeles, mis tähendas, et iga muudatus nõudis binaari uuesti kompileerimist.
v1.6.0 surus kogu selle üheks pipeline'iks kokku. Üksainus ühendav commit kustutas Apple Speech transkribeerija (~1,145 rida), Deepgrami Swift-hõivaja (~1,523 rida), mõlemad TypeScript-sillad, vana orkestraatori ja duaalse heli IPC-käitlejad — netokustutus üle 5,200 rea. Nende asemel: renderdaja hõivab heli standardsete Web APIs kaudu, saadab selle PCM-ina IPC kaudu ja Electroni põhiprotsess omab ühtainsat WebSocketi teed backendini, kus pakkuja valitakse backendi konfiguratsiooni järgi. Üks hõivamistee, üks socketi omanik, üks koht, kus mõelda.
Viie tuhande rea kustutamine tundub suurepärane. Umbes päeva jagu.
Viga kolis sinna kihti, mis endiselt arvas, et neid on kaks
Seejärel hakkasid transkriptid ilmuma kaks korda. Üksainus välja öeldud lause salvestati kahe identse kirjena — vahel hullemini.
Algpõhjus on kogu selle väljalaske kõige õpetlikum asi, sest see on ühendamise üldine seadus. Me olime pakkujad ühendanud põhiprotsessis — üks socket, üks käitleja. Aga React-kiht polnud teadet saanud: see ühendas endiselt kaks pakkujaspetsiifilist sündmusekonksu korraga, ühe ehitatud Deepgrami jaoks ja teise ElevenLabsi jaoks. Mõlemad kuulasid. Mõlemad salvestasid. Iga transkript sai talletatud selle konksu poolt, mis oli elus — ja mõlemad olid.
Ja selle all oli teine, salakavalam kahekordistumine: ElevenLabs kiirgab iga lõpetatud rea kaks korda — üks kord kui committed sündmus ja uuesti kui committed_with_timestamps. Nii et isegi ühe konksuga võis üks lause saabuda kahe sündmusena.
Parandus (v1.6 liinis) on korralik kaheosaline, mis kaardistub täpselt kahele põhjusele: väravasta iga sündmusekonks enabled lipu taha, mida juhib backendi aktiivse-pakkuja konfiguratsioon, nii et elus on alati vaid üks kuulaja; ja lisa allikapõhine teksti deduplitseerimine (jäta meelde viimane salvestatud rida mikrofoni ja süsteemiheli jaoks), et neelata ElevenLabsi topeltkiirgus.
Õppetund on suurem kui see viga: kui sa ühendad kaks backendi ühe liidese taha, dubleerimine ei kao — see rändab sinna kihti, mis endiselt eeldab, et neid on kaks. Me surusime torustiku põhiprotsessis kokku ja leke tuli välja kaks kihti kõrgemal, liidese sündmuste ühenduses. Pipeline'i ühendamine pole tehtud siis, kui tootja on ühendatud; see on tehtud siis, kui iga tarbija on lakanud uskumast vana kuju.
Esimene taasühendus: "loobu pärast viit katset"
v1.6.0 tarnis ka GeekBye kõige esimese WebSocketi automaatse taasühenduse eksponentsiaalse tagasilangusega — ja seda tasub näidata just seetõttu, et see on selle kuulikindla versiooni primitiivne eellane, mida me täna jooksutame.
Originaal oli piiratud. Viis katset, viivitused 1s, 2s, 4s, 8s, 16s — ja siis see andis alla, kiirates fataalse vea: "Ühendus kadus — palun taaskäivita transkribeerimine." Tol ajal tundus see vastutustundlik: ära proovi lõputult, ütle kasutajale ausalt. Praktikas oli see maskeeritud UX-viga. Kuueteistkümne sekundi pikkune võrgutõrge — Wi-Fi üleminek, VPN-i taasühendus, tunnel — on hetkeline, kuid piiratud kordus kohtleb seda kui lõplikku. Kasutaja ei teinud midagi valesti ja talle öeldi, et taaskäivitagu live-koosolek.
See on täpselt see ebaõnnestumine, mille praegune disain kõrvaldas. Täna taasühendub GeekBye piiramatu tagasilangusega ja isegi tsükleerib pakkujaid ning peatub kunagi vaid tõeliselt fataalsel põhjusel — autentimine, kvoot, arveldus. Kogu teekond alates "loobu pärast viit" kuni "ära kunagi loobu hetkelise vea korral" on jutustatud loos miks su AI-märkmetegija peatub halva Wi-Fi peal. v1.6 on see, kust see tee algas, naiivse versiooniga, mis pidi esimesena olemas olema.
Aegunud ühendused kummitavad sind, kui neil pole nimesid
Veel üks töökindluse õppetund maandus v1.6.3-s, parandades peent viga klassi, mida igaüks, kes teeb taasühendust olekulise backendi peal, lõpuks kohtab.
Kui sa taasühendud — või vahetad transkribeerimiskeele keset sessiooni, mis taasühendub kapoti all — ei sure vana ühendus alati vaikselt. Selle surmakoriseva teated (CONNECTION_LOST, disconnected) võivad saabuda pärast seda, kui uus ühendus on juba terve, ja kiskuda maha täiuslikult hea asenduse. Parandus annab igale ühenduskatsele identiteedi — ühenduspõhise sessiooni ID, mis on WebSocketi URL-ile templdatud — pluss lühikese armuakna pärast taasühendust, mille jooksul eelnenud katse aegunud lahtiühenduse teateid ignoreeritakse. See saadab ka selgesõnalise stop teate enne socketi sulgemist, et backend saaks vana sessiooni puhtalt maha kiskuda.
Põhimõte: igasugune taasühenduse loogika olekulise backendi peal peab märgistama iga katse identiteediga ja kohtlema eelnevate katsete hilinenud teateid mürana — muidu tapetakse edukas taasühendus oma eelkäija viimase hingetõmbe poolt.
Kolm asja, mida v1.6 õpetas
- Ühendamine kolib vigu; see ei kustuta neid. Suru kaks mootorit üheks ja auditeeri iga tarbija, mis endiselt arvab, et neid on kaks. Meie oma oli liides, ühendades kaks sündmusekonksu pärast seda, kui torustik oli juba liitunud.
- Su esimene taasühendus saab olema piiratud, ja piiratud on live-voogude jaoks vale. "Loobu pärast N katset" muudab hetkelise tõrke lõplikuks ebaõnnestumiseks, milles kasutajat süüdistatakse. Erista fataalset (autentimine, kvoot) hetkelisest (kadunud socket) juba kõige esimesest versioonist.
- Taasühendused vajavad identiteeti. Märgista iga katse; ignoreeri surnud katsete hilinenud teateid. Ühenduse, millel pole nime, võib tappa tema enda kummitus.
See on töökindluse loo teine peatükk, millest saab GeekBye v2. Esimese jaoks vaata auto-uuenduse saagat, mis võttis neli päeva ja kuus väljalaset (v1.5.x); selle kohta, kuhu taasühenduse töö lõpuks jõudis, miks su AI-märkmetegija peatub halva Wi-Fi peal; ja kogu kaare jaoks tarkvara täiuseni tarnimise anatoomia.