Introduktion
Vår röstprodukt hade två klagomål som vi inte kunde förena internt. Från engelsktalande företagskunder kändes AI:n smart. Från mandarintalande kunder – och alltmer från vietnamesiska, tagalogiska och hinditalande kunder – kändes AI trög. Avståndet, mätt från början till slut från samtalets tystnad till AI första stavelse, var 260 ms på engelska och 540 ms på mandarin. Båda siffrorna låg inom den publicerade latensbudgeten.
Endast en av dem kände sig acceptabel på ett telefonsamtal.
Instinkten hos alla ingenjörsteam som ser ett latensgap är att mala på modellen. Snabbare slutledning. Mindre modell. Bättre kvantisering. Vi gjorde allt det där. Det köpte oss 60 ms över hela linjen, vilket inte stängde någonting – det relativa gapet fanns fortfarande kvar, och engelskan var dubbelt så snabb.
Den verkliga fixen kom från en annan observation: vi behandlade latens som ett modellproblem när det faktiskt var ett routingproblem.
Vart millisekunderna faktiskt tar vägen
Att dela upp 540ms mandarinsvaret i segment var det första som klargjorde problemet. Bokföringen såg ungefär ut så här:
- Ljudfångst och slutpunkter — 80 ms
- Tal-till-text första token — 140 ms
- Avsikt + hämtning pipeline — 90 ms
- Stor modell första token (generation) — 180 ms
- Text-till-tal första ljud — 50 ms
På den engelska vägen kom samma segment in på 80 / 60 / 90 / 80 / 40. De två segmenten som skilde sig dramatiskt var tal-till-text och generering av stora modeller. STT var långsammare på mandarin eftersom den akustiska modellen hade tränats med ett längre sammanhangsfönster – nödvändigt för tonal disambiguation – som pressade första token med 80 ms.
Modellgenereringen var långsammare eftersom tokenizern producerade fler tokens per tecken med motsvarande betydelse på mandarin än på engelska.
Det var inte heller något modellfel. Båda var avvägningar som hade samlats i tysthet av oberoende team som optimerade för sina egna mätvärden - STT för noggrannhet, LLM för generationskvalitet. Latenskostnaden var reell, men det var ingens P&L.
Routingfixen som köpte 200ms
Vi slutade dirigera alla språk genom en enda LLM. Istället byggde vi ett tunt klassificeringsskikt framför generation som detekterar, på mindre än 8 ms, tre saker: språket för det inkommande yttrandet, konversationsavsiktsklassen och om begäran är ett av cirka 40 högfrekventa mönster som vi identifierade genom att gruppera 2M samtalssegment.
När klassificeraren identifierar ett högfrekvent mönster på ett icke-engelsk språk dirigerar vi genereringen till en mindre, språkspecialiserad modell som hanterar det mönstret direkt. Den mindre modellen har destillerats på miljontals slutföranden av samma mönster, så dess kvalitet på de specifika banorna ligger inom bullret från den stora modellen - men dess första token-latens är ungefär en tredjedel.
När klassificeraren ser en svansavsikt - allt som inte ingår i de 40 mönstren - faller förfrågan igenom till den stora modellen, precis som tidigare. Det mesta av latensfallet kommer från det faktum att de högfrekventa mönstren är också mönstren som står för ~78 % av samtalsvolymen. Svansfallen betalar den ursprungliga latensen, men de är sällsynta.
Klassificeraren ingen såg komma
Den tunna klassificeraren var den del av projektet som ingen hade budgeterat för. Att bygga det tog längre tid än att bygga själva routingsystemet, eftersom fellägena var subtila. En klassificerare som feldirigerar 1 % av förfrågningarna till en liten modell som inte hanterar dem producerar hallucinationer, inte bara försämrade svar.
Tre saker fick klassificeraren att fungera. Först tränades den på verklig produktionstrafik, inte syntetisk data. För det andra returnerade det ett konfidenspoäng som routern kunde gränsa - allt under 0,91 konfidens faller automatiskt igenom till den stora modellen.
För det tredje, vi skuggdeployerade den i sex veckor mot den befintliga pipelinen, med den stora modellen som tillhandahåller marken, innan någon trafik faktiskt dirigerades till små modeller.
Skuggdistributionen hittade fyra klasser av misstag som offlinetestset hade missat. Två var lätta att fixa i träningsdata. Två nödvändiga avsiktliga routingregler - allt som rör identitetsverifiering, betalningar eller avbokning av möten går alltid till den stora modellen oavsett klassificerarens förtroende, eftersom kostnaden för en hallucination på dessa vägar är högre än latensvinsten.
TTS-sidan av problemet
Medan ändringarna av LLM och STT genomgick granskning, arbetade ett parallellt team med talutgången. 50ms TTS första ljudet på engelska var faktiskt oslagbart; gapet på 70 ms-90 ms på de flesta icke-engelska språk drevs av mindre röstmodeller, mindre aggressiv cachning och en enda delad GPU-pool som prioriterade engelska under belastning.
Vi delar upp TTS-infrastrukturen i språkanslutna pooler. Mandarin TTS kör nu på sin egen pool, med sina egna skalningsregler, på hårdvara nära de regioner där det mesta av vår mandarintrafik kommer från. Latensen sjönk från 70-90 ms till 45 ms inom två veckor efter cutover.
Inget av detta var smart; det var infrastrukturarbete som ingen hade brytt sig om att göra eftersom den marginella förbättringen på ett enskilt språk inte, isolerat, hade motiverat det.
Lärdomen – och vi skriver nu ner den internt – är att "vi har inte investerat i det här eftersom ingen enskild användarinriktad mätning motiverade det" är exakt den typ av beslut som sammanställer till ett gap på 280 ms mellan två språk under fyra år.
Mätning av paritet, inte medelvärden
En liten men följdändring vi gjorde internt var hur teamets latensinstrumentpanel rapporterar. Vi brukade spåra p50 och p95 av end-to-end latens, i genomsnitt över alla språk, mot en enda SLO. Instrumentpanelen såg grön ut för det mesta.
Den nya instrumentpanelen rapporterar p50 och p95 per språk mot a paritet SLO — klyftan mellan det långsammaste och det snabbaste språket som stöds har sin egen budget, som upprätthålls separat. Vi satte paritetsbudgeten till 60 ms. När gapet överstiger budgeten söks jour.
Paritetsinstrumentpanelen gör något som den genomsnittliga baserade man inte kunde: den gör regressioner på det långsammaste språket synliga med samma brådska som regressioner i det snabbaste. När du snittar över 32 språk, flyttar en 200ms regression på vietnamesiska genomsnittet med 6ms. När du mäter paritet flyttar den instrumentpanelen med 200ms.
Vad är fortfarande långsamt
Efter routingarbetet, TTS-poolerna och paritetsinstrumentpanelen ligger vårt sämsta språk (kantonesiska, främst för att den stödjande akustiska modellens korpus är mindre) på 320ms. Engelska är på 220ms. Det återstående gapet på 100 ms är verkligt och vi vet ungefär var det bor — den kantonesiska STT akustiska modellen är äldre och ännu inte på den nya arkitekturen, och vi kommer att träna om den under Q3.
Men "det långsammaste språket är 100 ms bakom det snabbaste" är en annan konversation än "det långsammaste språket är 280 ms efter det snabbaste." Den förra är en känd regression med en kvartalsvis fix planerad. Det senare var en nödsituation som kunde upplevas som vi inte visste att vi hade.
Vad vi skulle göra annorlunda nästa gång
Tre saker, i prioriterad ordning, om vi skulle bygga om latensstacken från början med det vi vet nu.
- Bygg först klassificeraren-routern, sedan modellen. Det slutade med att routinglagret var den bit som hade högst hävstång. Att behandla det som ett "vi lägger till det senare om vi behöver det" visade sig betyda att vi levde med singelmodells latens i 18 månader längre än vi behövde.
- Ställ in paritets-SLO:er före språk-SLO:er. Frestelsen är att sätta upp mål per språk och låta paritet uppstå. Paritet uppstår inte; det divergerar, långsamt, i riktning mot lagets primära språk.
- Resursera infrastrukturen per språk som om varje språk vore en separat produkt. Mandarin TTS förtjänade sin egen GPU-pool från dag ett, inte från dagen då vi mätte latensgapet.
Inget av detta är ny forskning. Inget av det krävde ett papper. Det krävde att man behandlade latensparitet som ett produktåtagande och sedan finansierade den oglamorösa infrastrukturen för att stödja åtagandet. Vi har den paritet vi behöver nu. Vi hade inte det på fyra år.