
A csend teherhordó volt
A GeekBye v1 utolsó két kiadása ugyanarról a kényelmetlen igazságról szól: a valós idejű átírás egy valódi hálózaton nem veszteségmentes, és az őszinte lépés az, hogy abbahagyod az úgy tenni, mintha az lenne. A v1.8.20 minden audio chunk másolatát a lemezen tartotta, mielőtt egy újracsatlakozás során eldobta volna, és elkezdte hangosan megjelölni a réseket az átiratban. A v1.9.0 abbahagyta a csend küldését, hogy sávszélességet spóroljon — és felfedezte, hogy a csend pontosan az a jel volt, amelyet az átíró használt annak megállapítására, hogy egy mondat véget ért. Két kiadás a dolgok eldobásának költségéről.
A GeekBye v1 utolsó két kiadása — a v1.8.20 és a v1.9.0, azok, amelyek közvetlenül a 2-es verzió mérföldkő előtt vannak — olyan témát osztanak meg, amely csak akkor válik nyilvánvalóvá, ha együtt olvasod őket. Mindkettő olyan dolgokról szól, amelyeket az alkalmazás csendben eldobott, és mindkettő ugyanarra a következtetésre jut: amit eldobsz, hogy hatékony légy, több munkát végzett, mint gondoltad. Az egyik kiadás hangot dobott el a hálózati újracsatlakozások során. A másik csendet dobott el, hogy sávszélességet spóroljon. Mindkét esetben az eldobott dolog teherhordónak bizonyult.
A valós idejű átírás nem veszteségmentes, és a v1.8.20 ezt beismeri
Íme egy tény a WebSocketen keresztüli streaming beszéd-szöveg átírásról, amelyről könnyű elfordítani a tekintetet: amikor a kapcsolat megszakad és újracsatlakozik — vagy amikor az alkalmazás átvált az egyik átírási szolgáltatóról a másikra, Deepgramról ElevenLabsre — a kliens szándékosan eldobja az adott rés alatt befogott audio chunkokat. Muszáj neki. Egy frissen csatlakozott szolgáltató, amely hirtelen egy halomnyi elavult hangot kap, megfullad, vagy ami rosszabb, a rossz dologra rögzíti a hangaktivitás-érzékelőjét. Így a kliens eldobja azokat a chunkokat.
A probléma nem az eldobás. A probléma az, hogy mit jelentett az eldobás: azok a szavak egyszerűen soha nem jelentek meg az átiratban, és semmi sem mondta meg, hogy hiányoznak. A felvétel teljesnek tűnt. Nem volt az.
A v1.8.20 két fronton támadja ezt, és egyik sem az, hogy „tedd az újracsatlakozásokat sohasem hangvesztővé" — mert nem tudod, valós időben nem. Ehelyett a veszteséget helyreállíthatóvá és őszintévé teszi.
A helyreállítható fele egy tartós PCM tee. A fő folyamat befogó handlerjében minden befogott chunk most egy lemezen lévő fájlba íródik mielőtt elérné az újracsatlakozás és a türelmi időszak eldobó kapuit:
every captured chunk is written to
<userData>/sessions/session-<ts>.wavbefore realtime drop gates so reconnect-lost audio is preserved for a future batch-STT reconciliation pass
A mechanizmus kellemesen alacsony technológiájú: a teeChunkToDisk megnyit egy WriteStream-et, előre lefoglal egy 44 bájtos WAV fejlécet, hozzáfűzi a nyers 16 bites mono mintákat, ahogy megérkeznek, és leálláskor a végleges méretekkel javítja a fejlécet (fs.openSync(..., 'r+'), 44 bájt írása a 0 eltolásnál). Az eredmény a teljes hang lejátszható WAV-ja — beleértve azokat a részeket, amelyeket a valós idejű átirat soha nem látott. Amikor a felvétel véget ér, kibocsát egy session-audio-saved eseményt a fájl útvonalával és a bájtszámmal.
Két őszinteségi megjegyzés számít itt, mert könnyű túlállítani ezt. Először: a tee nem tölti ki a rést ezekben a kiadásokban. Megőrzi a nyers hangot egy későbbi összeegyeztető menethez — egy kötegelt újraátírás, amely összeegyezteti a fájlt az élő átirattal — ami egy horog, amelyet ezek a kiadások leraknak, nem pedig egy funkció, amelyet szállítanak. A követő commit még annotálja is a session-audio-saved-et egy TODO-val, amely az összeegyeztetési problémákra mutat, hogy senki se tévessze össze halott kóddal. Másodszor: ez lemezt fogyaszt. A csapat nagyjából 86 MB-ot becsült 30 perces felvételenként, így ugyanaz a kiadás hozzáad egy StartupCleanupService-t, amely kitakarítja a 7 napnál régebbi mentett munkameneteket. Egy határtalanul növekvő biztonsági háló egy szivárgás; a takarítás az, ami a tee-t egyáltalán szállíthatóvá teszi.
Az őszinte fele még egyszerűbb. Az első újracsatlakozási kísérletnél a kliens egy valódi, látható bejegyzést ír az átiratba:
[reconnecting — audio may be missing]
Az az egyetlen sor a kiadás egész filozófiája kicsiben. Az alkalmazás nem tudja garantálni, hogy mindent hallott, ezért abbahagyja annak sugallását, hogy hallotta. Egy rés, amelyet látsz, egy rés, amelyet meg tudsz kerülni; egy rés, amelyet nem látsz, egy hibajelentés, amely arra vár, hogy megtörténjen.
v1.9.0: a csend, amelyet optimalizálással eltüntettél, volt a jel
A v1.9.0 az utolsó v1 kiadás, és ez az, ahol a backend átadott a kliensnek egy sor élő-átirat felelősséget — csend eldobása, minden átirat-sor mikrofonhoz vagy rendszerhanghoz rendelése egy helyi hangaktivitás-idővonalról, az újracsatlakozási hurok birtoklása, felvételi korlátok érvényesítése. Ennek nagy része a 2-es verzió architektúrájának alapozása, és máshol el lett mesélve. De egy szál benne a legjobb kis történet bármelyik kiadásban, mert egy javítás, amelynek vissza kellett vonnia egy elszigetelten teljesen helyes optimalizáció kárát.
Egy nyilvánvalóan jó változással kezdődik: hagyd abba a csend küldését. A kliens elkezdte kiszűrni azokat a chunkokat, amelyek domináns forrása csend volt, mielőtt elküldte volna őket a socketen — kevesebb sávszélesség, kevesebb haszontalan hang a backendnek feldolgozni, és tiszteletben tartott egy explicit backend kliens-szerződést, amely azt mondta, „ne küldj csendet". Minden ok, hogy megtedd, megalapozott volt.
És teljesen elrontotta a megnyilatkozások véglegesítését. A commit, amely javítja, szokatlan őszinteséggel fekteti le az oksági láncot:
After the client began filtering silence chunks … ElevenLabs Scribe's VAD-based commit strategy no longer fires — EL needs ~1s of silence in the audio stream to commit a partial, and that silence never reaches it. Result: partials accumulate forever, UI shows trailing "...", utterances never finalize.
Ez egy szivárgó absztrakció platóni formája, amely visszaharap. Az átíró hangaktivitás-érzékelőjének nincs külön „a beszélő végzett" bemenete — kikövetkezteti egy mondat végét abból, hogy körülbelül egy másodperc csendet hall a hangban. A csend nem zaj volt a vonalon; egy üzenet volt. Azzal, hogy kiszűrte a sávszélesség spórolása érdekében, a kliens véletlenül abbahagyta annak az egyetlen jelnek a küldését, amely megmondta az átírónak, hogy egy mondat véget ért. Így minden részleges átirat egyszerűen tovább nőtt, a farka egy ...-ba halványult el, amely soha nem oldódott fel.
Megjavíthatnád ezt azzal, hogy visszateszed a csendet — de akkor visszavontad az optimalizációt. A tényleges javítás jobb: küldd el a csend jelentését anélkül, hogy elküldenéd a csendet. A kliens most helyben követi, mennyi ideje csendes a hang, és amikor ez átlépi a SILENCE_COMMIT_THRESHOLD_MS = 1000-et — szándékosan a backend saját VAD csendküszöbéhez illesztve — egy explicit commit jelet tüzel sávon kívül: a sendMixedChunk észreveszi a tartós csendet, küld egy realtime-audio:commit-ot IPC-n át, a fő folyamat továbbít egy { type: 'commit' }-ot a socketen, és a backend megmondja az ElevenLabsnek, hogy véglegesítse a részlegeset. Két apró állapotdarab, a silenceStartedAt és a silenceCommitFired, megakadályozza, hogy ismételten tüzeljen. A sávszélesség-győzelem túléli; a véglegesítés újra működik; a végén lévő ... feloldódik.
Van egy kis, őszinte lábjegyzet is ehhez a kiadáshoz: a határtalan újracsatlakozási logika, amelyet a v1.9.0 bevezetett, először a rendererben épült meg egy WsReconnectController-ként, majd ugyanazon a kiadáson belül törölve lett, amint a valódi implementáció leszállt a fő folyamat handlerjébe, ahová tartozott. Maga az újracsatlakozás története — határtalan backoff, szolgáltatók váltogatása minden öt hiba után — saját fejezetében van elmesélve; amit a v1.9.0 hozzáad, az az, hogy itt született meg az az architektúra, és ahol a kliens először vette át a tulajdonjogát.
Három dolog, amit ezek a kiadások megtanítottak
- Amikor nem tudod megelőzni a veszteséget, tedd helyreállíthatóvá és tedd láthatóvá. A v1.8.20 nem akadályozza meg, hogy az újracsatlakozások hangot dobjanak el — ez fizikailag nem lehetséges valós időben. Megőriz egy másolatot a nyers hangról későbbre, és megcímkézi a rést az átiratban. Egyik sem látványos; együtt a csendes adatvesztést egy ismert, korlátozott, helyreállítható állapottá alakítják.
- Egy biztonsági hálónak megvannak a saját korlátai. A tartós tee csendben megtöltené a lemezt, ha magára hagynád — egy javítás, amely lassított felvételű buggá válik. Az ugyanabban a kiadásban szállított 7 napos takarítás nem utólagos gondolat; ez az, ami a tee-t egyáltalán biztonságosan szállíthatóvá teszi. Ha hozzáadsz egy mechanizmust, amely felhalmoz, add hozzá ugyanabban a lélegzetben a mechanizmust, amely kitakarítja.
- Amit eldobsz, hogy hatékony légy, lehet, hogy egy jel. Az egyetlen legélesebb lecke bármelyik kiadásban: a csend kiszűrése minden tengelyen helyes volt, amelyet normálisan ellenőriznél — sávszélesség, terhelés, szerződési megfelelés — és mégis elrontotta a terméket, mert egy lejjebbi rendszer jelentést olvasott a csendbe. Amikor adatokat távolítasz el a hatékonyság érdekében, kérdezd meg, mi következtetett ki valamit azokból az adatokból. Néha explicit módon kell elküldened a jelentést, éppen azért, mert abbahagytad annak a dolognak a küldését, amelyből korábban kikövetkeztették.
És ezzel véget ér a verzió-1 története — az utolsó megerősítés az újraírás előtt. Az előző fejezetért, a három ige, amely életben tartja a Web Audiót (v1.8.16 + v2.0.2); azért, hogy hová tartott mindez, mit igényel valójában egy 2-es verzió: 206 commit (v2.0.0); a teljes ívért pedig a szoftver tökéletesre szállításának anatómiája.