Introdução
Nosso produto de voz teve duas reclamações que não conseguimos conciliar internamente. Para clientes empresariais que falam inglês, a IA pareceu ágil. Dos clientes que falam mandarim – e cada vez mais dos clientes que falam vietnamita, tagalo e hindi – a IA parecia lenta. A diferença, medida de ponta a ponta, desde o silêncio do chamador até a primeira sílaba da IA, foi de 260ms em inglês e 540ms em mandarim. Ambos os números estavam dentro do orçamento de latência publicado.
Apenas um deles se sentiu aceitável em um telefonema.
O instinto de qualquer equipe de engenharia que vê uma lacuna na latência é trabalhar no modelo. Inferência mais rápida. Modelo menor. Melhor quantização. Fizemos tudo isso. Isso nos rendeu 60ms em geral, o que não fechou nada – a lacuna relativa ainda estava lá e o inglês permaneceu duas vezes mais rápido.
A verdadeira solução veio de uma observação diferente: estávamos tratando a latência como um problema de modelo, quando na verdade era um problema de roteamento.
Para onde vão os milissegundos
Dividir a resposta do mandarim de 540 ms em segmentos foi a primeira coisa que esclareceu o problema. A contabilidade ficou mais ou menos assim:
- Captura de áudio e endpoint – 80ms
- Primeiro token de fala para texto – 140ms
- Pipeline de intenção + recuperação – 90ms
- Primeiro token de modelo grande (geração) – 180ms
- Primeiro áudio de conversão de texto em fala – 50ms
No caminho inglês, os mesmos segmentos chegaram a 80/60/90/80/40. Os dois segmentos que divergiram dramaticamente foram a fala para texto e a geração de modelos grandes. O STT foi mais lento em mandarim porque o modelo acústico foi treinado com uma janela de contexto mais longa – necessária para a desambiguação tonal – que empurrou o primeiro token em 80 ms.
A geração do modelo foi mais lenta porque o tokenizador produziu mais tokens por caractere de significado equivalente em mandarim do que em inglês.
Nem era um defeito do modelo. Ambas foram compensações que foram acumuladas discretamente por equipes independentes que otimizavam suas próprias métricas – STT para precisão, o LLM para qualidade de geração. O custo da latência era real, mas não era lucro de ninguém.
A correção de roteamento que comprou 200ms
Paramos de encaminhar todos os idiomas por meio de um único LLM. Em vez disso, construímos uma fina camada de classificação antes da geração que detecta, em menos de 8 ms, três coisas: o idioma da expressão recebida, a classe de intenção de conversação e se a solicitação é um dos cerca de 40 padrões de alta frequência que identificamos agrupando segmentos de chamada de 2 milhões.
Quando o classificador identifica um padrão de alta frequência em um idioma diferente do inglês, roteamos a geração para um modelo menor e especializado no idioma que lida diretamente com esse padrão. O modelo menor foi destilado em milhões de conclusões do mesmo padrão, portanto sua qualidade nesses caminhos específicos está dentro do nível do modelo grande – mas sua latência do primeiro token é de aproximadamente um terço.
Quando o classificador vê uma intenção final – qualquer coisa que não esteja nos 40 padrões – a solicitação passa para o modelo grande, como antes. A maior parte da queda na latência vem do fato de que os padrões de alta frequência são também os padrões que representam aproximadamente 78% do volume de chamadas. Os casos finais pagam a latência original, mas são raros.
O classificador que ninguém previu chegar
O classificador fino era a parte do projeto para a qual ninguém havia orçado. Construí-lo demorou mais do que construir o próprio sistema de roteamento, porque os modos de falha eram sutis. Um classificador que desvia 1% das solicitações para um modelo pequeno que não as trata produz alucinações, e não apenas respostas degradadas.
Três coisas fizeram o classificador funcionar. Primeiro, ele foi treinado em tráfego de produção real, não em dados sintéticos. Em segundo lugar, ele retornou uma pontuação de confiança que o roteador poderia atingir – qualquer confiança abaixo de 0,91 passa automaticamente para o modelo grande.
Terceiro, nós o implantamos na sombra por seis semanas no pipeline existente, com o modelo grande fornecendo informações básicas, antes que qualquer tráfego fosse realmente direcionado para modelos pequenos.
A implantação sombra encontrou quatro classes de erros que o conjunto de testes offline não percebeu. Dois foram fáceis de corrigir nos dados de treinamento. Duas regras de roteamento deliberadas exigiam: qualquer coisa relacionada à verificação de identidade, pagamentos ou cancelamento de compromisso sempre vai para o grande modelo, independentemente da confiança do classificador, porque o custo de uma alucinação nesses caminhos é maior do que o ganho de latência.
O lado TTS do problema
Enquanto as alterações do LLM e do STT passavam pela revisão, uma equipe paralela trabalhava na saída do discurso. O primeiro áudio TTS de 50ms em inglês foi efetivamente imbatível; a lacuna de 70ms-90ms na maioria dos idiomas diferentes do inglês foi impulsionada por modelos de voz menores, cache menos agressivo e um único pool de GPU compartilhado que priorizava o inglês sob carga.
Dividimos a infraestrutura TTS em pools fixados por idioma. O Mandarin TTS agora é executado em seu próprio pool, com suas próprias regras de escalonamento, em hardware próximo às regiões onde se origina a maior parte do nosso tráfego em Mandarim. A latência caiu de 70-90ms para 45ms duas semanas após a transição.
Nada disso foi inteligente; foi um trabalho de infraestrutura que ninguém se preocupou em fazer porque a melhoria marginal em qualquer idioma único não o justificou, isoladamente.
A lição – e agora estamos anotando-a internamente – é que “não investimos nisso porque nenhuma métrica voltada para o usuário justificava” é exatamente o tipo de decisão que resulta em uma lacuna de 280 ms entre dois idiomas ao longo de quatro anos.
Medindo paridade, não médias
Uma mudança pequena, mas consequente, que fizemos internamente foi na forma como o painel de latência da equipe relata. Costumávamos rastrear o p50 e o p95 da latência ponta a ponta, calculada a média de todos os idiomas, em relação a um único SLO. O painel parecia verde na maior parte do tempo.
O novo painel relata p50 e p95 por idioma contra um paridade SLO — a lacuna entre o idioma suportado mais lento e o mais rápido tem seu próprio orçamento, aplicado separadamente. Definimos o orçamento de paridade em 60ms. Quando a lacuna excede o orçamento, o plantão é avisado.
O painel de paridade faz algo que o painel baseado em média não consegue: torna visíveis as regressões na linguagem mais lenta com a mesma urgência que as regressões na linguagem mais rápida. Quando você calcula a média de 32 idiomas, uma regressão de 200 ms em vietnamita move a média em 6 ms. Quando você mede a paridade, o painel se move em 200 ms.
O que ainda é lento
Após o trabalho de roteamento, os pools de TTS e o painel de paridade, nosso pior idioma (cantonês, principalmente porque o corpus do modelo acústico de suporte é menor) fica em 320ms. Inglês está em 220ms. A lacuna restante de 100 ms é real e sabemos aproximadamente onde ela está – o modelo acústico STT cantonês é mais antigo e ainda não está na nova arquitetura, e iremos treiná-lo novamente no terceiro trimestre.
Mas "o idioma mais lento está 100 ms atrás do mais rápido" é uma conversa diferente de "o idioma mais lento está 280 ms atrás do mais rápido". A primeira é uma regressão conhecida com uma correção trimestral planejada. Esta última foi uma emergência de experiência do cliente que não sabíamos que tínhamos.
O que faríamos de diferente na próxima vez
Três coisas, em ordem de prioridade, se estivéssemos reconstruindo a pilha de latência do zero com o que sabemos agora.
- Construa primeiro o roteador-classificador e depois o modelo. A camada de roteamento acabou sendo a peça de maior aproveitamento. Tratá-lo como um “adicionaremos mais tarde se precisarmos” acabou significando que vivemos com latência de modelo único por 18 meses a mais do que o necessário.
- Defina SLOs de paridade antes dos SLOs de idioma. A tentação é definir metas por idioma e deixar emergir a paridade. A paridade não surge; diverge, lentamente, na direção do idioma principal da equipe.
- Utilize a infraestrutura por idioma como se cada idioma fosse um produto separado. O Mandarin TTS merecia seu próprio pool de GPU desde o primeiro dia, não desde o dia em que medimos a lacuna de latência.
Nada disso é pesquisa nova. Nada disso exigia papel. Foi necessário tratar a paridade de latência como um compromisso de produto e, em seguida, financiar a infra-estrutura nada glamorosa para apoiar o compromisso. Temos a paridade que precisamos agora. Fazia quatro anos que não tínhamos isso.