Salta al contenuto

Trascrizione vocale in tempo reale sotto i 200ms: guida all’architettura

Pubblicato

AscoltaAscolta questo articolo

La trascrizione vocale in tempo reale (STT) trascrive attivamente l’audio mentre una persona parla, restituendo le parole come testo in poche centinaia di millisecondi. Ma mantenere bassa la latenza della STT è tanto una questione di architettura quanto di modello. Gli ingegneri devono pianificare ogni fase: trasporto, suddivisione in chunk, end-pointing e percorso di acquisizione, ognuna delle quali aggiunge latenza. L’inefficienza in uno solo di questi punti può far saltare il limite dei 200ms.

Questa guida offre un sistema pratico che puoi usare per costruire pipeline di trascrizione vocale in tempo reale a partire dal livello di trasporto. Ci concentreremo su Scribe v2 Realtime, che produce trascrizioni parziali in circa 150ms di latenza del modello, supporta oltre 90 lingue, accetta audio PCM (8kHz-48kHz) e mu-law, e offre Voice Activity Detection oltre al controllo manuale di commit per la finalizzazione dei segmenti.

Vedremo come l’audio arriva al server, come le ipotesi si trasformano in testo definitivo, quanto costano le funzionalità in-stream e come acquisire e inoltrare correttamente l’audio.

TL;DR

  • Per creare sistemi di trascrizione vocale in tempo reale serve una messa a punto architetturale, così da mantenere la latenza bassa lungo tutta la pipeline.
  • WebSocket è la scelta giusta per la maggior parte delle pipeline, anche se WebRTC offre diversi vantaggi pur essendo più complesso.
  • La Voice Activity Detection gestisce la segmentazione automatica, mentre il commit manuale permette alle tue applicazioni di intervenire quando sanno che il turno è finito.
  • Le trascrizioni parziali sono provvisorie e quelle finali sono definitive: dovresti visualizzarle in modo diverso.
  • Piccoli chunk PCM di circa 100ms riducono al minimo la latenza per la prima trascrizione parziale.

WebSocket vs. WebRTC per la trascrizione vocale in tempo reale

Prima che avvenga qualsiasi trascrizione, l’audio deve viaggiare dalla sorgente al riconoscitore. Il canale che scegli stabilisce la latenza minima per tutto ciò che segue. Ci sono due opzioni valide per far arrivare l’audio al livello di trascrizione.

WebSocket è un canale duraturo, ordinato, affidabile e bidirezionale su TCP. Apri una connessione, invii frame audio binari e ricevi eventi di trascrizione. È semplice sia lato client che server, attraversa proxy e firewall aziendali che già consentono HTTPS, ed è supportato da tutti i browser e runtime server.

Il limite di WebSocket è che si basa su TCP. Se un pacchetto va perso, TCP lo ritrasmette e blocca i dati successivi finché la lacuna non viene colmata. In buone condizioni di rete non si nota, ma in caso di perdita di pacchetti si verifica un blocco temporaneo, con l’audio che si accumula e poi arriva tutto insieme.

WebRTC è pensato per i media in tempo reale. Trasporta i media su UDP (tramite SRTP), quindi la perdita di un pacchetto non blocca lo stream; la pipeline continua. Include un jitter buffer che assorbe le variazioni nei tempi di arrivo dei pacchetti, gestisce la traversata NAT con ICE/STUN/TURN così che i peer dietro ai router possano connettersi, e integra la propria acquisizione ed encoding audio.

Di solito servono server TURN per i client che non possono connettersi direttamente, e lato server bisogna terminare uno stream media invece di leggere uno stream di byte.

Ecco il confronto in breve:

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)

Per la maggior parte dei casi d’uso, WebSocket è la scelta giusta. Usalo quando i tuoi client hanno una buona connessione e controlli il percorso di acquisizione: pipeline server-to-server, app desktop, app browser su banda larga e la maggior parte dei backend dei contact center dove l’audio arriva già al tuo server.

Scegli WebRTC quando acquisisci direttamente da dispositivi consumer su reti mobili instabili, quando già usi uno stack WebRTC per l’audio bidirezionale (ad esempio un voice agent che parla anche), o quando la priorità è la reattività in tempo reale più che la semplicità di implementazione.

Il resto di questa guida usa WebSocket come trasporto per la connessione al riconoscitore, perché mantiene visibili le parti in gioco ed è il punto di partenza ideale per la maggior parte dei team. Nulla di quanto segue è specifico di WebSocket: puoi aggiungere in seguito una tratta media WebRTC davanti, decodificare l’audio in PCM sul server e inoltrare gli stessi chunk nella pipeline.

Trascrizioni parziali e finali: come funzionano i risultati intermedi

Un riconoscitore in tempo reale non aspetta una frase completa prima di restituire il testo. Invece, emette una sequenza continua di ipotesi che si affinano man mano che arriva altro audio, poi le blocca. Capire la differenza tra questi due stati è ciò che distingue una trascrizione che sembra viva da una che sembra rotta.

Un’ipotesi parziale (intermedia) è la miglior stima del modello in base all’audio ricevuto fino a quel momento. Le parziali sono instabili per natura. Con l’arrivo di altro audio, il modello può rivedere le parole precedenti: "Voglio" può diventare "Voglio due biglietti" quando il contesto successivo risolve l’ambiguità. Arrivano rapidamente (è questo il dato dei ~150ms di latenza) e sono pensate per essere sovrascritte.

Un’ipotesi finale è un segmento definitivo che non cambierà più. Una volta finalizzato un segmento, il riconoscitore passa oltre e le ipotesi successive descrivono l’audio successivo. Le finali sono quelle che salvi, invii a un LLM o archivi come trascrizione.

La distinzione tra parziali e finali influenza tre aspetti che rischi di sbagliare se li confondi:

  • Esperienza utente: Mostrare le parziali fa sembrare la trascrizione viva: l’utente vede comparire le parole mentre parla, il che conferma che il microfono funziona e il sistema sta ascoltando.
  • End-pointing: Le parziali ti danno un segnale continuo di attività vocale. Insieme alla VAD, ti permettono di decidere quando l’oratore ha davvero finito.
  • Tempistiche a valle: In una pipeline di voice agent i passaggi sono: audio in ingresso, poi trascrizione vocale, poi un LLM, poi sintesi vocale, poi audio in uscita. Puoi iniziare a lavorare in modo speculativo sulle parziali e confermare sulle finali, riducendo il tempo di risposta percepito a costo di scartare talvolta il lavoro speculativo.

Visualizza parziali e finali in modo diverso. Un pattern semplice ed efficace è mantenere una "riga corrente" modificabile legata all’ultima parziale e aggiungerla alla trascrizione solo quando arriva una finale:

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: "" });

A livello visivo, mostra il testo definitivo come testo normale e quello corrente in stile più chiaro o in corsivo, così l’utente capisce che potrebbe ancora cambiare.

End-pointing e Voice Activity Detection (VAD)

Sapere cosa è stato detto è solo metà del lavoro. Un riconoscitore deve anche capire quando un pensiero è finito. Questa decisione determina quando finalizzare un segmento e, in un agente, quando il sistema inizia a rispondere.

L’end-pointing è la decisione che un enunciato sia terminato. Finalizzare troppo presto interrompe l’utente a metà frase. Finalizzare troppo tardi lascia un agente in silenzio dopo che l’utente ha già finito.

Scribe v2 Realtime ti offre due meccanismi complementari:

  • La Voice Activity Detection segmenta l’audio in base ai silenzi: Il riconoscitore rileva quando la voce lascia spazio a un silenzio prolungato e usa quel confine per finalizzare automaticamente un segmento. La VAD è la scelta giusta per le interfacce conversazionali perché si adatta al ritmo naturale del parlato senza che tu debba gestire i tempi manualmente.
  • Controllo manuale di commit: Il controllo manuale di commit permette alla tua applicazione di decidere quando finalizzare il segmento corrente, indipendentemente dal silenzio. Inviando un segnale di commit, il riconoscitore chiude il segmento attuale ed emette una finale. È lo strumento giusto quando la tua applicazione sa già che il turno è finito: rilascio di un pulsante push-to-talk, azione "invia" o una policy esterna di turn-taking.

I due metodi si integrano bene. Un voice agent tipico usa la VAD per il funzionamento a mani libere e offre il commit manuale come override, così un utente che si ferma a pensare non viene interrotto, ma chi preme un pulsante ottiene subito un confine.

La soglia di silenzio è un vero compromesso senza un valore universalmente corretto:

  • Un timeout breve (ad esempio, finalizzazione dopo ~200-400ms di silenzio) rende il sistema reattivo. Ma rischia di interrompere chi fa pause naturali tra le frasi, spezzando un pensiero in più segmenti e, in un agente, attivando una risposta prematura.
  • Un timeout lungo (ad esempio, ~800-1200ms) tollera le pause naturali e mantiene intatti gli enunciati, ma comporta un ritardo percepibile prima che il sistema reagisca.

Non esiste una costante globale da usare: adatta la soglia al tipo di interazione:

  • Dettatura e appunti tollerano pause più lunghe perché l’utente riflette a metà frase. Meglio timeout più lunghi e affidarsi alla VAD.
  • Agenti di comando e controllo o transazionali beneficiano di timeout più brevi e commit manuale, perché i turni sono brevi e netti.
  • Chi parla più lingue o non è madrelingua fa più pause: prevedi più silenzio prima di finalizzare.

Seguendo questi consigli puoi costruire un sistema di end-pointing efficace e avvicinarti alla trascrizione vocale in tempo reale.

Funzionalità in-stream: rilevamento lingua e diarizzazione speaker

Il riconoscimento in streaming può fare più che produrre parole. Tuttavia, ogni segnale extra che chiedi interagisce con latenza e stabilità. La regola pratica è attivare solo ciò che serve davvero all’esperienza live e rimandare il resto a una fase batch.

Il riconoscimento automatico della lingua permette a Scribe v2 Realtime di identificare la lingua parlata tra le oltre 90 supportate, senza che tu debba dichiararla in anticipo. Il costo è che il modello ha bisogno di un breve tratto di audio per determinare con sicurezza la lingua, quindi le prime parziali di uno stream possono essere meno stabili mentre la lingua si assesta. Se già conosci la lingua, specificarla elimina questa ambiguità e rende più stabili le parziali iniziali.

La diarizzazione speaker attribuisce il parlato ai diversi interlocutori, identificando chi ha detto cosa. Nella trascrizione batch è relativamente semplice perché il modello vede tutto il file. In streaming è più difficile: il riconoscitore deve assegnare un’etichetta speaker solo in base all’audio ricevuto finora, e un’etichetta data all’inizio può dover essere rivista quando si sente meglio la voce di quel parlante. Tratta le etichette speaker in streaming come il testo parziale: provvisorie finché il segmento non è finalizzato.

Tempistiche a livello di parola e contesto delle entità seguono la stessa logica. Più metadati per token chiedi, più il modello e la rete devono gestire. Per la maggior parte delle UI in tempo reale ti servono solo testo e confini dei segmenti live; puoi rimandare i metadati dettagliati a una fase batch post-chiamata con Scribe v2.

Formati audio per lo streaming: PCM e mu-law

Trasporto e logica di riconoscimento attirano la maggior parte dell’attenzione, ma molti bug reali nascono uno strato più sotto, da come codifichi e suddividi l’audio. Scegliere formato e dimensione dei chunk giusti è il modo più semplice per ridurre la latenza della trascrizione vocale.

PCM (lineare, 16 bit signed, little-endian) è il formato da usare quando controlli l’acquisizione. Frequenze di campionamento più alte portano più dettagli acustici: 16kHz è lo standard minimo per il riconoscimento vocale e di solito basta; 8kHz è qualità telefonica e perde le frequenze alte. Usa la frequenza che corrisponde alla sorgente. Non ha senso upsamplare audio telefonico 8kHz a 48kHz: l’informazione non c’è da recuperare.

Mu-law a 8kHz è il formato telefonico. Se ricevi chiamate da un provider come Twilio, l’audio arriva come mu-law 8kHz e dovresti inoltrarlo così, senza ricodificare due volte. Mantenere il formato sorgente evita artefatti di ricampionamento e conversioni inutili.

La dimensione dei chunk è la leva che incide di più sulla latenza percepita. Invi l’audio a chunk e il riconoscitore produce parziali man mano che arrivano. Chunk più piccoli significano aggiornamenti più frequenti e latenza minore per la prima parziale; chunk più grandi significano meno messaggi e un po’ più di contesto per inferenza. Un intervallo pratico è 20-250ms di audio per chunk. Come riferimento: a 16kHz mono 16 bit PCM, un secondo di audio sono 32.000 byte, quindi un chunk da 100ms sono circa 3.200 byte.

Acquisire l’audio del microfono nel browser

Nel browser, lo strumento giusto è la Web Audio API con un AudioWorklet. Il worklet gira sul thread di rendering audio, riceve l’audio in piccoli frame e non subisce i blocchi del main thread come il vecchio ScriptProcessorNode. Il suo compito è convertire i campioni float nativi del browser in PCM 16 bit e passarli al main thread, che li inoltra via WebSocket.

Il cuore del processore worklet è la conversione da float a 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);

La pipeline in codice

La pipeline ha tre componenti: un client browser che acquisisce il microfono e invia PCM al server, un server Node che inoltra l’audio a Scribe v2 Realtime e restituisce le trascrizioni, e un client scriptabile che invia PCM da file o bridge telefonico.

Il server fa da relay invece di esporre direttamente il riconoscitore al browser per un motivo importante: la tua API key di ElevenLabs è un segreto e non deve mai comparire nel codice client. La chiave resta sul server. Se hai bisogno che il browser parli direttamente col riconoscitore, genera lato server un token monouso a breve scadenza e passa quello al client invece della API key.

Client browser

Il client apre un WebSocket verso il tuo server, acquisisce il microfono tramite il worklet sopra e inoltra ogni frame PCM appena prodotto. Gli eventi in ingresso (già normalizzati dal server in { type, text }) gestiscono lo stato parziale/finale visto prima:

// 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" }));

Relay server

Il server apre una connessione al riconoscitore per ogni client, mantiene la API key sul server, inoltra il PCM binario così com’è e normalizza gli eventi del riconoscitore nella forma stabile { type, text } che il client consuma:

// 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
});

Tutto ciò che è specifico dell’endpoint è racchiuso nelle due funzioni adapter qui sotto. Sostituisci i nomi dei campi con quelli esatti del riferimento Speech to Text; il resto della pipeline non cambia:

// 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;
}

Client backend scriptabile

Per pipeline backend e per il benchmark qui sotto, la stessa connessione al riconoscitore funziona senza browser: leggi PCM da qualsiasi sorgente, invialo a cadenza reale e ricevi gli eventi. API key e URL arrivano dall’ambiente, come sul server.

// 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 della latenza e del word error rate nella trascrizione vocale

Latenza e word error rate variano in base a chi parla, lingua, condizioni acustiche, durata dell’audio, percorso di rete verso la regione più vicina di ogni provider e carico attuale di ciascun servizio.

Un risultato misurato da un laptop in una città non si applica alla tua infrastruttura di produzione in un’altra. Esegui il test su un’infrastruttura simile a quella di produzione, su audio simile a quello reale, e riporta intervalli e distribuzioni invece di singoli numeri.

Le uniche misure di latenza e accuratezza che contano sono quelle che ottieni sul tuo audio reale da un’infrastruttura simile a quella di produzione. Ecco una guida per fare benchmark della latenza della trascrizione vocale.

Cosa misurare per la latenza della trascrizione vocale

Qui sotto trovi le metriche principali da misurare quando fai benchmark della latenza della trascrizione vocale in tempo reale:

  • Tempo al primo parziale: Dal momento in cui invii il primo chunk audio a quando ricevi la prima parziale non vuota.
  • Ritardo tra parziale e finale: Dall’ultimo chunk audio di un enunciato all’ipotesi finale.
  • Word error rate (WER): WER sulla trascrizione finale rispetto a una referenza umana, calcolato allo stesso modo su tutti i sistemi.
  • Stabilità (churn): Quante parziali vengono riscritte prima della finalizzazione. Questa misura è un indicatore di quanto la UI live cambierà agli occhi dell’utente.

Controlli

Per evitare dati poco affidabili, dovresti implementare diversi controlli nell’esperimento per mantenere la coerenza.

Ecco i controlli principali da usare nel benchmark della latenza della trascrizione vocale:

  • Audio identico: Usa gli stessi file, stessa frequenza di campionamento e stessa codifica per ogni sistema.
  • Cadenza identica: Invia ogni sistema alla stessa cadenza di chunk real-time (ad esempio chunk da 100ms).
  • Ripeti e riporta le distribuzioni: Esegui ogni file più volte durante la giornata; riporta mediana e coda (p50/p95).
  • Referenze e scoring identici: Normalizza il testo allo stesso modo (maiuscole, punteggiatura, numeri) prima di calcolare il WER.
  • Dichiara regione e rete: Indica dove è stato eseguito il test e il percorso verso ogni provider.

Mantenendo tutti questi elementi uguali, otterrai metriche più precise.

Struttura del test

Il nucleo della misurazione prende un adapter provider e registra tempo al primo parziale, ritardo di finalizzazione e churn delle parziali:

// 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;
}

Il word error rate è una distanza di Levenshtein a livello di token su testo normalizzato. Porta tutto in minuscolo e togli la punteggiatura sia su referenza che ipotesi prima di calcolarlo, altrimenti misurerai il normalizzatore invece del modello. Metti questa misura in un ciclo che esegue ogni file ~10 volte per provider e riporta la mediana di tempo al primo parziale e WER (p50/p95), dato che un singolo campione è dominato dalla variabilità di rete.

Per farlo funzionare, ti servono due cose. Primo, scrivi un adapter StreamFn per ogni sistema. Il client scriptabile sopra è già uno, con adapter per gli altri che seguono lo stesso contratto (audioPath, onEvent, result) e impostano result.lastChunkSentAt quando l’ultimo chunk audio viene inviato. Secondo, carica i tuoi file audio e le referenze e lancia le misure su di essi. Esegui tutto da una macchina simile a quella di produzione, su audio che rappresenta i tuoi utenti, e avrai un confronto ripetibile.

Ricapitolando come ottenere la trascrizione vocale in tempo reale

Abbiamo visto molti cambiamenti architetturali in questo articolo, che ti permettono di migliorare il sistema passo dopo passo e avvicinarti alla trascrizione vocale in tempo reale.

Un sistema STT in tempo reale di produzione si basa su poche decisioni chiave:

  • Trasporto: Scegli WebSocket per semplicità e reti controllate, WebRTC quando serve tolleranza alle perdite e acquisisci da dispositivi consumer.
  • Parziali e finali: Tratta le parziali come provvisorie e le finali come definitive, visualizzandole in modo diverso così che gli utenti si fidino del testo live.
  • End-pointing: Usa la VAD per la segmentazione automatica, il commit manuale come override e adatta la soglia di silenzio all’interazione invece che a una costante.
  • Funzionalità in-stream: Attiva le funzionalità in-stream solo dove servono davvero all’esperienza live, rimanda il resto a una fase batch con Scribe v2.
  • Formato audio: Acquisisci in piccoli frame PCM, invia chunk da ~100ms e mantieni il formato sorgente per la telefonia.
  • Benchmark: Regola empiricamente il bilanciamento tra accuratezza e latenza sul tuo audio e sui tuoi obiettivi.
  • Sicurezza API: Tieni la tua API key sul server, oppure genera token monouso per connessioni dirette dal client.

Se vuoi vedere come ottimizzare la latenza in un voice agent, abbiamo scritto anche una guida dedicata.

Costruisci sistemi di trascrizione vocale in tempo reale con Scribe v2 Realtime

Scribe v2 Realtime produce parziali in circa 150ms di latenza del modello. Se i tuoi utenti sperimentano davvero questa latenza o una più alta dipende dall’architettura che costruisci intorno, che è la parte sotto il tuo controllo. Seguendo le strategie di questo articolo, crei pipeline più efficienti che riducono la latenza e migliorano l’esperienza dei clienti.

Per approfondire, scopri la panoramica delle funzionalità di Speech to Text, consulta il nostro riferimento ai modelli per l’elenco completo di funzionalità e lingue, e visita le pagine prodotto realtime: API Speech to Text realtime e Speech to Text realtime.

Quando sei pronto a costruire, crea un account ElevenLabs gratuito e trasmetti la tua prima trascrizione oggi stesso.

FAQ sulla latenza della trascrizione vocale in tempo reale

Articoli simili

Crea con l'audio IA della massima qualità