Pular para o conteúdo

Transcrição de Voz em Tempo Real em menos de 200ms: Guia de arquitetura

Publicado
Última atualização

OuvirOuça este artigo

A transcrição de voz em tempo real (STT) transcreve ativamente o áudio enquanto uma pessoa fala, retornando suas palavras como texto em poucas centenas de milissegundos. Mas manter a baixa latência do STT é tanto um desafio de arquitetura quanto de modelo. Desenvolvedores precisam planejar o transporte, o envio em blocos, a detecção de fim de fala e o caminho de captura, pois cada um adiciona latência à equação. A ineficiência em qualquer um deles pode estourar seu orçamento de 200 ms.

Este guia oferece um sistema prático para criar pipelines de transcrição de voz em tempo real, começando pela camada de transporte. Vamos nos basear no Scribe v2 Realtime, que produz transcrições parciais com cerca de 150 ms de latência do modelo, é compatível com mais de 90 idiomas, aceita áudio PCM (8 kHz–48 kHz) e mu-law, e oferece detecção de atividade de voz, além de controle de commit manual para finalizar segmentos.

Vamos acompanhar como o áudio chega ao servidor, como as hipóteses evoluem para texto confirmado, o custo dos recursos no fluxo e como capturar e encaminhar o áudio corretamente.

Resumo

  • A criação de sistemas de transcrição de voz em tempo real exige ajustes na arquitetura para manter a latência baixa em todo o pipeline.
  • WebSocket é a opção padrão ideal para a maioria dos pipelines, embora o WebRTC ofereça vários benefícios com maior complexidade.
  • A detecção de atividade de voz realiza a segmentação sem uso das mãos, enquanto o commit manual permite que suas aplicações substituam esse comportamento quando sabem que o turno terminou.
  • As transcrições parciais são provisórias, e as finais são confirmadas. Por isso, você deve exibi-las de formas diferentes. 
  • Pequenos blocos de PCM, com cerca de 100 ms, minimizam a latência até a primeira transcrição parcial.

WebSocket vs. WebRTC para transcrição de voz em tempo real

Antes de qualquer transcrição acontecer, o áudio precisa viajar da fonte até o reconhecedor. O canal escolhido determina a latência mínima de tudo que vem depois. Há duas opções viáveis para o áudio chegar à camada de transcrição.

WebSocket é um canal bidirecional, confiável, ordenado e de longa duração sobre TCP. Você abre uma conexão, envia frames binários de áudio e recebe eventos de transcrição. É simples tanto no cliente quanto no servidor, atravessa os proxies corporativos e firewalls que já permitem HTTPS, e é compatível com todos os navegadores e ambientes de servidor. 

A limitação do WebSocket é que ele usa TCP. Se um pacote se perde, o TCP o retransmite e retém os dados posteriores até que a lacuna seja preenchida. Em boas condições de rede, isso não é perceptível. Com perda de pacotes, porém, ocorre o bloqueio de início de fila: uma breve pausa em que o áudio se acumula e depois chega de uma vez.

O WebRTC foi criado para mídia em tempo real. Ele transmite mídia por UDP (via SRTP), portanto, um pacote perdido não interrompe o fluxo: o pipeline continua. Ele inclui um buffer de jitter que absorve variações no tempo de chegada dos pacotes, negocia a travessia de NAT com ICE/STUN/TURN para que pares atrás de roteadores possam se conectar e traz seus próprios recursos de captura e codificação de áudio. 

Em geral, você precisa de servidores TURN para clientes que não conseguem se conectar diretamente, e o lado do servidor precisa encerrar um fluxo de mídia em vez de ler um fluxo de bytes.

Veja rapidamente essa relação de vantagens e desvantagens:

WebSocket
Transport
TCP (reliable, ordered)
Behavior under packet loss
Head-of-line blocking, bursty recovery
Jitter handling
Your responsibility
NAT traversal
Not needed (client-initiated)
Browser support
Universal, trivial
Server complexity
Low
WebRTC
Transport
UDP/SRTP (real-time, loss-tolerant)
Behavior under packet loss
Graceful degradation
Jitter handling
Built-in jitter buffer
NAT traversal
Requires ICE/STUN/TURN
Browser support
Universal, but more API surface
Server complexity
High (media server or SFU)

Para a maioria dos casos de uso, WebSocket é a opção certa. Use-o quando seus clientes tiverem boa conectividade e você controlar o caminho de captura: pipelines entre servidores, apps para desktop, apps de navegador em banda larga e a maioria dos backends de centrais de atendimento, nos quais o áudio já chega ao seu servidor por outro meio. 

Escolha WebRTC quando capturar áudio diretamente de dispositivos de consumidores em redes móveis instáveis, quando já usar uma stack WebRTC para áudio bidirecional — por exemplo, um agente de voz que também responde falando — ou quando o comportamento em tempo real tolerante a perdas importar mais do que a simplicidade de implementação.

O restante deste guia usa o transporte WebSocket para a conexão com o reconhecedor, pois ele deixa os componentes visíveis e é o melhor ponto de partida para a maioria das equipes. Nada disso é específico ao WebSocket, então você pode adicionar depois uma camada de mídia WebRTC antes dele, decodificar o áudio em PCM no servidor e encaminhar os mesmos blocos para o pipeline.

Transcrições parciais e finais: entenda os resultados intermediários

Um reconhecedor em tempo real não espera uma frase completa para mostrar resultados. Em vez disso, ele emite um fluxo contínuo de hipóteses que se tornam mais precisas à medida que mais áudio chega e então as confirma. Entender a diferença entre esses dois estados é o que separa uma transcrição que parece viva de uma que parece falha. 

Uma hipótese parcial (intermediária) é a melhor estimativa do modelo com base no áudio recebido até então. As parciais são instáveis por definição. À medida que mais áudio chega, o modelo revisa palavras anteriores: “Eu quero” pode se tornar “Eu quero dois ingressos” quando o contexto posterior resolve a ambiguidade. Elas chegam rápido — é isso que descreve a latência de aproximadamente 150 ms — e devem ser substituídas.

Uma hipótese final é um segmento confirmado que não será alterado. Quando um segmento é finalizado, o reconhecedor segue adiante, e as hipóteses seguintes descrevem o áudio posterior. Os resultados finais são o que você persiste, envia para um LLM ou armazena como transcrição.

A diferença entre resultados parciais e finais influencia três aspectos que você vai errar se os confundir:

  • Experiência do usuário: Exibir as parciais faz a transcrição parecer ao vivo: o usuário vê as palavras surgirem enquanto fala, confirmando que o microfone funciona e que o sistema está ouvindo.
  • Detecção de fim de fala: As parciais fornecem um sinal contínuo de atividade de fala. Combinadas à VAD, elas permitem decidir quando o falante realmente parou.
  • Tempo das etapas seguintes: Em um pipeline de agente de voz, as etapas são: entrada de áudio, transcrição de voz, LLM, Text to Speech e saída de áudio. Você pode começar um trabalho especulativo com as parciais e confirmá-lo com os resultados finais, reduzindo o tempo de resposta percebido, embora ocasionalmente precise descartar esse trabalho especulativo.

Exiba parciais e finais de formas diferentes. Um padrão simples e eficaz é manter uma única “linha atual” mutável vinculada à parcial mais recente e confirmá-la em uma transcrição apenas para acréscimos quando chegar um resultado final:

type TranscriptState = {
  committed: string[]; // finalized segments, never rewritten
  current: string;     // latest partial, overwritten on each update
};

const onPartial = (s: TranscriptState, text: string): TranscriptState =>
  ({ ...s, current: text });

const onFinal = (s: TranscriptState, text: string): TranscriptState =>
  ({ committed: [...s.committed, text], current: "" });

Visualmente, exiba o texto confirmado de forma estável e o atual com um estilo mais claro ou em itálico, para que o usuário entenda que ele ainda pode mudar.

Detecção de fim de fala e detecção de atividade de voz (VAD)

Saber o que foi dito é apenas metade do desafio. Um reconhecedor também precisa saber quando uma ideia terminou. Essa decisão determina quando você finaliza um segmento e, em um agente, quando o sistema começa a responder. 

A detecção de fim de fala decide que uma fala terminou. Finalizar cedo demais interrompe usuários no meio da frase. Finalizar tarde demais deixa um agente em silêncio depois de o usuário claramente ter terminado.

O Scribe v2 Realtime oferece dois mecanismos complementares:

  • A detecção de atividade de voz segmenta o áudio com base no silêncio: O reconhecedor detecta quando a fala dá lugar a um silêncio prolongado e usa esse limite para finalizar automaticamente um segmento. A VAD é a opção padrão ideal para interfaces conversacionais, pois se adapta ao ritmo natural da fala sem que você precise acompanhar o tempo manualmente.
  • Controle de commit manual: O controle de commit manual permite que sua aplicação decida quando finalizar o segmento atual, independentemente do silêncio. Você envia um sinal de commit, o reconhecedor fecha o segmento atual e emite um resultado final. É a ferramenta certa quando sua aplicação já sabe que o turno terminou: ao soltar um botão de apertar para falar, em uma ação de “enviar” ou com uma política externa de tomada de turno.

Os dois funcionam bem em conjunto. Um agente de voz típico usa VAD para operação sem uso das mãos e disponibiliza o commit manual como substituição, para que um usuário que faz uma pausa para pensar não seja interrompido, mas um usuário que toca em um botão obtenha uma delimitação imediata.

O limite de silêncio é uma escolha real entre vantagens e desvantagens, sem um valor universalmente correto:

  • Um tempo limite curto após o fim da fala, por exemplo, finalização após cerca de 200 a 400 ms de silêncio, faz o sistema parecer responsivo. Mas também interrompe usuários que fazem pausas naturais entre orações, dividindo uma ideia em vários segmentos e, em um agente, acionando uma resposta prematura.
  • Um tempo limite longo, por exemplo, cerca de 800 a 1.200 ms, tolera pausas naturais e mantém as falas intactas, mas gera um atraso perceptível antes de o sistema reagir.

Não há uma constante global para usar aqui; ajuste o limite à interação:

  • Ditado e anotações toleram pausas mais longas porque os usuários pensam no meio da frase. Prefira tempos limite maiores e conte com a VAD.
  • Agentes de comando e controle e transacionais se beneficiam de tempos limite menores com commit manual, pois os turnos são curtos e objetivos.
  • Falantes multilíngues ou não nativos fazem mais pausas, portanto, reserve mais tempo de silêncio antes de finalizar.

Seguindo essas dicas, você consegue criar um sistema eficaz de detecção de fim de fala e avançar rumo à transcrição de voz em tempo real.

Recursos no fluxo: detecção de idioma e diarização de falantes

O reconhecimento por streaming pode fazer mais do que produzir palavras. No entanto, cada sinal adicional que você solicita afeta a latência e a estabilidade. A regra geral é ativar apenas o que a experiência ao vivo precisa e deixar o restante para um processamento em lote.

O reconhecimento automático de idioma permite que o Scribe v2 Realtime identifique o idioma falado entre os mais de 90 idiomas compatíveis, sem exigir que você o informe antecipadamente. O custo é que o modelo precisa de um breve trecho de áudio para determinar o idioma com segurança; por isso, as primeiras parciais de um fluxo podem ser menos estáveis enquanto o idioma é definido. Se você já conhece o idioma, especificá-lo elimina essa ambiguidade e tende a gerar parciais iniciais mais estáveis.

Diarização de falantes atribui a fala a diferentes falantes, identificando quem disse o quê. Na transcrição em lote, isso é comparativamente simples porque o modelo analisa o arquivo inteiro. No streaming, é mais difícil: o reconhecedor precisa atribuir um rótulo de falante apenas com base no áudio recebido até então, e um rótulo dado ao áudio inicial pode precisar ser revisado quando mais da voz desse falante for ouvida. Trate os rótulos de falantes no streaming como o texto parcial: provisórios até que o segmento seja finalizado.

A marcação de tempo por palavra e o contexto de entidades seguem a mesma lógica. Quanto mais metadados por token você solicitar, mais dados o modelo e a transmissão precisarão transportar. Para a maioria das interfaces em tempo real, basta ter ao vivo o texto e os limites dos segmentos; os metadados detalhados podem ficar para um processamento em lote pós-chamada com o Scribe v2.

Formatos de áudio para streaming: PCM e mu-law

A lógica de transporte e reconhecimento recebe a maior parte da atenção, mas uma parcela surpreendente dos bugs do mundo real surge um nível abaixo: na forma como você codifica e divide o áudio em blocos. Acertar o formato e o tamanho dos blocos é a forma mais econômica de reduzir a latência da transcrição de voz.

PCM (linear, assinado de 16 bits, little-endian) é o formato a usar quando você controla a captura. Taxas de amostragem maiores carregam mais detalhes acústicos: 16 kHz é o padrão mínimo para reconhecimento de fala e geralmente é suficiente; 8 kHz tem qualidade de telefonia e perde conteúdo de alta frequência. Use a taxa que corresponda à sua fonte. Não há benefício em aumentar a amostragem de áudio de telefonia de 8 kHz para 48 kHz, pois a informação não está disponível para ser recuperada.

Mu-law em 8 kHz é o formato de telefonia. Se você recebe chamadas de um provedor como o Twilio, o áudio chega em mu-law de 8 kHz, e você deve encaminhá-lo nesse formato em vez de transcodificá-lo duas vezes. Corresponder ao formato de origem evita artefatos de reamostragem e uma etapa de conversão desnecessária.

O tamanho dos blocos é o fator que molda mais diretamente a latência percebida. Você envia o áudio em blocos, e o reconhecedor produz parciais à medida que eles chegam. Blocos menores geram atualizações mais frequentes e menor latência até a primeira parcial; blocos maiores significam menos mensagens e um pouco mais de contexto por inferência. Uma faixa prática é de 20 a 250 ms de áudio por bloco. Como referência, em PCM mono de 16 bits a 16 kHz, um segundo de áudio tem 32.000 bytes; logo, um bloco de 100 ms tem cerca de 3.200 bytes.

Capturando a entrada do microfone no navegador

No navegador, a ferramenta certa é a Web Audio API com um AudioWorklet. O worklet é executado na thread de renderização de áudio, recebe o áudio em pequenos frames e não está sujeito às travadas da thread principal como o antigo ScriptProcessorNode. Sua função é converter as amostras nativas de ponto flutuante do navegador para PCM de 16 bits e passá-las para a thread principal, que as encaminha pelo WebSocket.

O núcleo do processador do worklet é a conversão de ponto flutuante para PCM:

// pcm-worklet.ts - registered via audioContext.audioWorklet.addModule()
class PCMWorklet extends AudioWorkletProcessor {
  process(inputs: Float32Array[][]) {
    const channel = inputs[0]?.[0]; // mono; Float32, range [-1, 1]
    if (!channel) return true;
    const pcm = new Int16Array(channel.length);
    for (let i = 0; i < channel.length; i++) {
      const s = Math.max(-1, Math.min(1, channel[i]));
      pcm[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
    }
    // Transfer the buffer to the main thread without copying.
    this.port.postMessage(pcm.buffer, [pcm.buffer]);
    return true;
  }
}
registerProcessor("pcm-worklet", PCMWorklet);

O pipeline em código

O pipeline tem três componentes: um cliente no navegador que captura o microfone e transmite PCM para seu servidor; um servidor Node que encaminha o áudio ao Scribe v2 Realtime e devolve as transcrições; e um cliente programável que transmite PCM de um arquivo ou ponte de telefonia.

O servidor encaminha os dados em vez de expor o reconhecedor diretamente ao navegador por um motivo importante: sua chave da API ElevenLabs é secreta e nunca deve aparecer no código do cliente. O servidor mantém a chave. Se você realmente precisar que o navegador se comunique diretamente com o reconhecedor, gere no servidor um token de uso único e curta duração e entregue-o ao cliente em vez da chave da API.

Cliente do navegador

O cliente abre um WebSocket para seu servidor, captura o microfone pelo worklet acima e encaminha cada frame PCM assim que ele é produzido. Os eventos recebidos, já normalizados pelo servidor no formato { type, text }, definem o estado parcial/final descrito anteriormente:

// client.ts - runs in the browser. ws is an open WebSocket to your server.
const audioContext = new AudioContext({ sampleRate: 16000 });
await audioContext.audioWorklet.addModule("pcm-worklet.js");

const mediaStream = await navigator.mediaDevices.getUserMedia({
  audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true },
});

const source = audioContext.createMediaStreamSource(mediaStream);
const worklet = new AudioWorkletNode(audioContext, "pcm-worklet");

// Forward each PCM frame to the server the moment it is produced.
worklet.port.onmessage = (e: MessageEvent<ArrayBuffer>) => {
  if (ws.readyState === WebSocket.OPEN) ws.send(e.data);
};
source.connect(worklet);

// Manual commit: tell the server to finalize the current segment.
const commit = () => ws.send(JSON.stringify({ type: "commit" }));

Encaminhamento pelo servidor

O servidor abre uma conexão com o reconhecedor por cliente, mantém a chave da API no servidor, encaminha diretamente o PCM binário e normaliza os eventos do reconhecedor para o formato estável { type, text } consumido pelo cliente:

// server.ts - Node, using the `ws` library. ELEVENLABS_API_KEY and the
// recognizer URL come from the environment; see the Speech to Text reference
// for the exact path and query parameters.
import { WebSocketServer, WebSocket } from "ws";

new WebSocketServer({ port: 8080 }).on("connection", (client) => {
  // The API key stays on the server, never on the wire to the browser.
  const recognizer = new WebSocket(process.env.RECOGNIZER_WSS_URL!, {
    headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY! },
  });

  // Browser -> recognizer: forward binary PCM, translate control messages.
  client.on("message", (data, isBinary) => {
    if (recognizer.readyState !== WebSocket.OPEN) return;
    if (isBinary) recognizer.send(data); // raw PCM bytes
    else if (JSON.parse(data.toString()).type === "commit")
      recognizer.send(sendCommit());
  });

  // Recognizer -> browser: normalize events into a stable shape.
  recognizer.on("message", (raw) => {
    const event = parseRecognizerEvent(raw.toString());
    if (event && client.readyState === WebSocket.OPEN)
      client.send(JSON.stringify(event));
  });

  // ... open handshake, queueing pre-open audio, and teardown on close/error
});

Tudo que é específico do endpoint fica restrito às duas funções adaptadoras abaixo. Substitua os nomes dos campos pelos nomes exatos da referência de Speech to Text; o restante do pipeline não muda:

// The single place that knows the recognizer's wire format.
const sendCommit = (): string => JSON.stringify({ type: "commit" });

type NormalizedEvent =
  | { type: "partial"; text: string }
  | { type: "final"; text: string }
  | { type: "vad"; speaking: boolean };

function parseRecognizerEvent(raw: string): NormalizedEvent | null {
  const msg = JSON.parse(raw);
  if (msg.is_final === true || msg.type === "final")
    return { type: "final", text: msg.text ?? "" };
  if (msg.type === "vad") return { type: "vad", speaking: !!msg.speaking };
  if (typeof msg.text === "string")
    return { type: "partial", text: msg.text };
  return null;
}

Cliente de backend programável

Para pipelines de backend e para o benchmark abaixo, a mesma conexão com o reconhecedor funciona sem um navegador: leia PCM de qualquer fonte, envie-o na cadência de blocos em tempo real e receba os eventos. A chave da API e a URL vêm do ambiente, assim como no servidor.

// stream-stt.ts - pace ~100ms chunks at real time, then commit the tail.
const SAMPLE_RATE = 16000, CHUNK_MS = 100;
const CHUNK_BYTES = (SAMPLE_RATE * 2 * CHUNK_MS) / 1000; // 3200 bytes
const ws = new WebSocket(process.env.RECOGNIZER_WSS_URL!, {
  headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY! },
});

// Send: walk the PCM buffer in 100ms chunks, sleeping between to mimic a
// live source. For audio that already arrives in real time, drop the sleep.
async function sendAudio(pcm: Buffer) {
  for (let off = 0; off < pcm.length; off += CHUNK_BYTES) {
    ws.send(pcm.subarray(off, off + CHUNK_BYTES));
    await new Promise((r) => setTimeout(r, CHUNK_MS));
  }
  ws.send(JSON.stringify({ type: "commit" })); // finalize the trailing segment
}

// Receive: print partials in place, append finals.
ws.on("message", (raw) => {
  const e = parseRecognizerEvent(raw.toString());
  if (e?.type === "final") console.log(`[final]   ${e.text}`);
  else if (e?.type === "partial") process.stdout.write(`[partial] ${e.text}\r`);
});

Benchmark de latência de transcrição de voz e taxa de erro de palavras

A latência e a taxa de erro de palavras variam conforme o falante, o idioma, as condições acústicas, a duração do áudio, o caminho de rede até a região mais próxima de cada provedor e a carga atual de cada serviço. 

Um resultado medido em um notebook em uma cidade não se aplica necessariamente à sua infraestrutura de produção em outra. Execute o harness em uma infraestrutura semelhante à de produção, com áudio semelhante à sua entrada real, e informe intervalos e distribuições, não números isolados.

Os únicos números de latência e precisão que importam são aqueles medidos no seu próprio áudio, a partir de uma infraestrutura semelhante à de produção. Veja como avaliar a latência da transcrição de voz.

O que medir para a latência da transcrição de voz

Abaixo estão as principais métricas que você deve medir ao avaliar a latência da transcrição de voz em tempo real:

  • Tempo até a primeira parcial: Do envio do primeiro bloco de áudio ao recebimento da primeira parcial não vazia.
  • Atraso da parcial até o resultado final: Do último bloco de áudio de uma fala até a hipótese final.
  • Taxa de erro de palavras (WER): WER da transcrição finalizada em comparação com uma referência humana, calculada de forma idêntica em todos os sistemas.
  • Variação de estabilidade: Quantas parciais são reescritas antes da finalização. Essa medida indica o quanto a interface ao vivo parecerá mudar.

Controles

Para evitar dados pouco confiáveis, implemente vários controles no experimento para manter a consistência.

Estes são os principais controles para o benchmark de latência de transcrição de voz:

  • Áudio idêntico: Use os mesmos arquivos, a mesma taxa de amostragem e a mesma codificação em todos os sistemas.
  • Cadência idêntica: Transmita para todos os sistemas na mesma cadência de blocos em tempo real, por exemplo, blocos de 100 ms.
  • Repita e informe distribuições: Execute cada arquivo várias vezes ao longo do dia; informe a mediana e a cauda da distribuição (p50/p95).
  • Referências e pontuação idênticas:Normalize o texto da mesma forma, incluindo maiúsculas e minúsculas, pontuação e números, antes de calcular a WER.
  • Informe a região e a rede: Informe onde o harness foi executado e o caminho até cada provedor.

Ao manter todos esses elementos iguais, você terá métricas mais precisas.

Estrutura do harness

O núcleo de medição recebe um adaptador do provedor e registra o tempo até a primeira parcial, o atraso de finalização e as alterações nas parciais:

// benchmark.ts - measurement core; one StreamFn adapter per provider.
type StreamFn = (
  audioPath: string,
  onEvent: (kind: "partial" | "final", text: string) => void,
  result: RunResult
) => Promise<void>; // adapter sets result.lastChunkSentAt on the final chunk

interface RunResult {
  firstPartialMs?: number;
  finalLagMs?: number;
  hypothesis: string;
  partialEdits: number;
  lastChunkSentAt: number;
  startedAt: number;
}

async function measure(streamFn: StreamFn, audioPath: string): Promise<RunResult> {
  const result: RunResult = {
    hypothesis: "", partialEdits: 0, lastChunkSentAt: 0,
    startedAt: performance.now(),
  };
  let prevPartial = "";

  await streamFn(audioPath, (kind, text) => {
    const now = performance.now();
    if (kind === "partial") {
      if (text && result.firstPartialMs === undefined)
        result.firstPartialMs = now - result.startedAt;
      if (text !== prevPartial) { result.partialEdits++; prevPartial = text; }
    } else { // final
      result.hypothesis = result.hypothesis ? `${result.hypothesis} ${text}` : text;
      if (result.lastChunkSentAt)
        result.finalLagMs = now - result.lastChunkSentAt;
    }
  }, result);

  return result;
}

A taxa de erro de palavras é uma distância de Levenshtein padrão no nível de token, aplicada a texto normalizado. Converta para minúsculas e remova a pontuação da mesma forma na referência e na hipótese antes de calculá-la; caso contrário, você estará medindo seu normalizador, e não o modelo. Execute essa medição em um loop que rode cada arquivo cerca de 10 vezes por provedor e informe a mediana do tempo até a primeira parcial e a WER mediana (p50/p95), pois uma única amostra é dominada pela variação da rede.

Para executá-lo, você fornece duas coisas. Primeiro, escreva um adaptador StreamFn por sistema. O cliente programável acima já é um deles; os adaptadores dos outros seguem o mesmo contrato (audioPath, onEvent, result) e definem result.lastChunkSentAt quando o bloco final de áudio é enviado. Segundo, carregue seus arquivos de áudio e referências e chame as medições para todos eles. Execute-o em uma máquina que represente sua implantação, com áudio que represente seus usuários, e você terá uma comparação reproduzível.

Recapitulando como obter transcrição de voz em tempo real 

Abordamos muitas mudanças de arquitetura neste artigo, permitindo que você melhore seu sistema de forma iterativa e avance rumo à transcrição de voz em tempo real.

Um sistema STT em tempo real em produção depende de algumas decisões fundamentais:

  • Transporte: Escolha WebSocket para ter simplicidade e redes controladas; escolha WebRTC quando precisar de tolerância a perdas e estiver capturando áudio de dispositivos de consumidores.
  • Parciais e finais: Trate as parciais como provisórias e os resultados finais como confirmados, exibindo-os de formas diferentes para que os usuários confiem no texto ao vivo.
  • Detecção de fim de fala: Use VAD para segmentação sem uso das mãos, commit manual como substituição e ajuste o limite de silêncio à interação, em vez de usar uma constante.
  • Recursos no fluxo: Ative recursos no fluxo apenas quando a experiência ao vivo precisar deles e deixe o restante para um processamento em lote com o Scribe v2.
  • Formato de áudio: Capture em pequenos frames PCM, envie blocos de aproximadamente 100 ms e use o formato de origem para telefonia.
  • Benchmark: Defina empiricamente os parâmetros de precisão e latência com base no seu próprio áudio e na métrica desejada.
  • Segurança da API: Mantenha sua chave da API no servidor ou gere tokens de uso único para conexões diretas do cliente.

Se quiser saber como otimizar a latência em um agente de voz, também preparamos um guia para você.

Crie sistemas de transcrição de voz em tempo real com o Scribe v2 Realtime

O Scribe v2 Realtime produz resultados parciais com cerca de 150 ms de latência do modelo. Seus usuários perceberão esse número ou algo maior dependendo da arquitetura ao redor, que é a parte que você controla. Ao usar as estratégias deste artigo, você criará uma arquitetura de pipeline melhor que reduz a latência e melhora a experiência do cliente. 

Para se aprofundar, conheça a visão geral dos recursos de Speech to Text, consulte nossa referência de modelos para ver a lista completa de recursos e idiomas e acesse as páginas dos produtos em tempo real: API de Speech to Text em tempo real e Speech to Text em tempo real.

Quando estiver pronto para criar, crie uma conta gratuita na ElevenLabs e transmita sua primeira transcrição hoje.

Perguntas frequentes sobre latência de transcrição de voz em tempo real

Artigos relacionados

Crie com o áudio de IA da mais alta qualidade