Aller au contenu

Speech to Text en temps réel sous 200 ms : guide d’architecture

Publié
Dernière mise à jour

ÉcouterÉcouter cet article

La transcription vocale en temps réel (STT) transcrit activement l’audio à mesure qu’une personne parle et renvoie ses paroles sous forme de texte en quelques centaines de millisecondes. Mais maintenir une faible latence STT relève autant de l’architecture que du modèle. Les ingénieurs doivent planifier le transport, le découpage en segments, la détection de fin de parole et le chemin de capture, qui ajoutent tous de la latence. Une inefficacité dans l’un de ces éléments peut dépasser votre budget de 200 ms.

Ce guide propose une méthode pratique pour concevoir des pipelines de transcription vocale en temps réel, à partir de la couche de transport. Il s’appuie sur Scribe v2 Realtime, qui produit des transcriptions partielles avec environ 150 ms de latence de modèle, prend en charge plus de 90 langues, accepte l’audio PCM (8 kHz-48 kHz) et mu-law, et intègre la détection d’activité vocale ainsi qu’un contrôle de validation manuelle pour finaliser les segments.

Nous verrons comment l’audio atteint le serveur, comment les hypothèses deviennent du texte validé, le coût des fonctionnalités en flux et comment capturer et transmettre correctement l’audio.

Résumé

  • La création de systèmes de transcription vocale en temps réel exige un ajustement précis de l’architecture afin de maintenir une faible latence tout au long du pipeline.
  • WebSocket est le choix par défaut pour la plupart des pipelines, bien que WebRTC offre divers avantages en contrepartie d’une plus grande complexité.
  • La détection d’activité vocale gère la segmentation mains libres, tandis que la validation manuelle permet à vos applications de reprendre la main lorsqu’elles savent que le tour de parole est terminé.
  • Les résultats partiels sont provisoires et les résultats finaux sont validés : affichez-les différemment. 
  • De petits segments PCM d’environ 100 ms réduisent la latence avant le premier résultat partiel.

WebSocket ou WebRTC pour la transcription vocale en temps réel

Avant toute transcription, l’audio doit parvenir de sa source au moteur de reconnaissance. Le canal choisi définit le plancher de latence pour tout ce qui suit. Deux options sont viables pour acheminer l’audio jusqu’à la couche de transcription.

WebSocket est un canal TCP persistant, ordonné, fiable et bidirectionnel. Vous ouvrez une connexion, envoyez des trames audio binaires et recevez des événements de transcription. Il est simple côté client comme côté serveur, traverse les proxys et pare-feux d’entreprise qui autorisent déjà HTTPS, et est pris en charge par tous les navigateurs et environnements d’exécution serveur. 

La contrainte de WebSocket est qu’il repose sur TCP. Si un paquet est perdu, TCP le retransmet et retient les données suivantes jusqu’à ce que le manque soit comblé. Dans de bonnes conditions réseau, cela ne se remarque pas. En cas de perte de paquets, cela crée un blocage en tête de ligne : un bref arrêt pendant lequel l’audio s’accumule avant d’arriver par rafale.

WebRTC est conçu pour les médias en temps réel. Il transmet les médias via UDP (avec SRTP) : un paquet perdu ne bloque donc pas le flux et le pipeline continue. Il inclut un tampon de gigue qui absorbe les variations d’arrivée des paquets, négocie la traversée NAT avec ICE/STUN/TURN afin de connecter des pairs derrière des routeurs, et intègre ses propres mécanismes de capture et d’encodage audio. 

Vous avez généralement besoin de serveurs TURN pour les clients qui ne peuvent pas se connecter directement, et le serveur doit terminer un flux multimédia plutôt que lire un flux d’octets.

Voici les principaux compromis :

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)

Pour la plupart des cas d’usage, WebSocket est le bon choix. Utilisez-le lorsque vos clients disposent d’une connexion correcte et que vous contrôlez le chemin de capture : pipelines serveur à serveur, applications de bureau, applications navigateur sur connexion haut débit et la plupart des backends de centres de contact, où l’audio arrive déjà sur votre serveur par un autre moyen. 

Choisissez WebRTC si vous capturez directement l’audio depuis des appareils grand public sur des réseaux mobiles instables, si vous exploitez déjà une pile WebRTC pour l’audio bidirectionnel — par exemple un agent vocal qui répond oralement — ou si le comportement temps réel tolérant aux pertes compte davantage que la simplicité d’implémentation.

La suite de ce guide utilise WebSocket pour la connexion au moteur de reconnaissance, car cela rend les composants visibles et constitue le meilleur point de départ pour la plupart des équipes. Rien n’est propre à WebSocket : vous pourrez ensuite ajouter en amont une branche média WebRTC, décoder l’audio en PCM sur le serveur et transmettre les mêmes segments au pipeline.

Résultats partiels et transcriptions finales : comprendre les résultats intermédiaires

Un moteur de reconnaissance en temps réel n’attend pas une phrase complète avant de produire du texte. Il émet plutôt un flux continu d’hypothèses qui s’affinent à mesure que l’audio arrive, puis les valide. Comprendre la différence entre ces deux états fait la différence entre une transcription vivante et une transcription qui semble défaillante. 

Une hypothèse partielle (intermédiaire) correspond à la meilleure estimation du modèle à partir de l’audio reçu jusque-là. Les résultats partiels sont instables par nature. À mesure que l’audio arrive, le modèle révise les mots précédents : « Je voudrais » peut devenir « Je voudrais deux billets » lorsque le contexte ultérieur lève l’ambiguïté. Ils arrivent rapidement — c’est ce que décrit la latence d’environ 150 ms — et sont destinés à être remplacés.

Une hypothèse finale est un segment validé qui ne changera plus. Une fois le segment finalisé, le moteur de reconnaissance passe à la suite et les hypothèses suivantes décrivent l’audio ultérieur. Ce sont les résultats finaux que vous conservez, envoyez à un LLM ou stockez comme transcription.

La distinction entre résultats partiels et finaux détermine trois éléments que vous gérerez mal si vous les confondez :

  • Expérience utilisateur : L’affichage des résultats partiels donne à la transcription un caractère direct : l’utilisateur voit les mots apparaître en parlant, ce qui confirme que le microphone fonctionne et que le système écoute.
  • Détection de fin de parole : Les résultats partiels fournissent un signal continu d’activité vocale. Associés à la VAD, ils vous permettent de déterminer quand l’intervenant s’est réellement arrêté.
  • Synchronisation en aval : Dans un pipeline d’agent vocal, les étapes sont les suivantes : entrée audio, transcription vocale, LLM, puis synthèse vocale, puis sortie audio. Vous pouvez commencer un travail spéculatif sur les résultats partiels et le confirmer avec les résultats finaux, afin de réduire le temps de réponse perçu, quitte à abandonner occasionnellement ce travail spéculatif.

Affichez différemment les résultats partiels et finaux. Un modèle simple et efficace consiste à conserver une unique « ligne en cours », modifiable et liée au dernier résultat partiel, puis à l’ajouter à une transcription en ajout seul lorsqu’un résultat final arrive :

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

Visuellement, affichez le texte validé dans un style établi et le texte en cours dans un style plus léger ou italique, afin que l’utilisateur comprenne qu’il peut encore changer.

Détection de fin de parole et détection d’activité vocale (VAD)

Savoir ce qui a été dit ne représente que la moitié du travail. Un moteur de reconnaissance doit aussi savoir quand une idée est terminée. Cette décision détermine quand finaliser un segment et, pour un agent, quand le système commence à répondre. 

La détection de fin de parole consiste à déterminer qu’un énoncé est terminé. Finaliser trop tôt interrompt les utilisateurs en pleine phrase. Finaliser trop tard laisse un agent silencieux alors que l’utilisateur a clairement fini.

Scribe v2 Realtime propose deux mécanismes complémentaires :

  • La détection d’activité vocale segmente l’audio selon les silences : Le moteur de reconnaissance détecte quand la parole laisse place à un silence prolongé et utilise cette limite pour finaliser automatiquement un segment. La VAD est le bon choix par défaut pour les interfaces conversationnelles, car elle s’adapte au rythme naturel de la parole sans nécessiter de gérer le temps manuellement.
  • Contrôle de validation manuelle : Le contrôle de validation manuelle permet à votre application de décider quand finaliser le segment en cours, indépendamment du silence. Vous envoyez un signal de validation, le moteur de reconnaissance clôt le segment en cours et émet un résultat final. C’est l’outil adapté lorsque votre application sait déjà que le tour de parole est terminé : relâchement d’un bouton push-to-talk, action « envoyer » ou politique externe de gestion des tours de parole.

Les deux mécanismes se combinent bien. Un agent vocal utilise généralement la VAD en mode mains libres et propose la validation manuelle comme contrôle prioritaire : un utilisateur qui s’arrête pour réfléchir n’est pas interrompu, tandis qu’un utilisateur qui appuie sur un bouton obtient une limite immédiate.

Le seuil de silence représente un véritable compromis, sans valeur universellement correcte :

  • Un délai de fin de parole court — par exemple une finalisation après environ 200 à 400 ms de silence — rend le système réactif. Mais il interrompt aussi les utilisateurs qui marquent naturellement une pause entre deux propositions, divisant une même idée en plusieurs segments et, pour un agent, déclenchant une réponse prématurée.
  • Un délai long — par exemple environ 800 à 1 200 ms — tolère les pauses naturelles et préserve les énoncés, au prix d’un délai perceptible avant la réaction du système.

Il n’existe pas de constante globale : ajustez le seuil à l’interaction :

  • La dictée et la prise de notes tolèrent des pauses plus longues, car les utilisateurs réfléchissent en cours de phrase. Privilégiez des délais plus longs et appuyez-vous sur la VAD.
  • Les agents de commande et de contrôle ainsi que les agents transactionnels bénéficient de délais plus courts associés à la validation manuelle, car les tours de parole sont brefs et précis.
  • Les locuteurs multilingues ou non natifs font davantage de pauses : prévoyez donc plus de silence avant la finalisation.

Ces recommandations vous aideront à concevoir un système efficace de détection de fin de parole et à vous rapprocher d’une transcription vocale en temps réel.

Fonctionnalités en flux : détection de la langue et diarisation des locuteurs

La reconnaissance en flux peut faire plus que produire des mots. Toutefois, chaque signal supplémentaire demandé influe sur la latence et la stabilité. En règle générale, activez uniquement ce dont l’expérience en direct a besoin et reportez le reste à un traitement par lot.

La reconnaissance automatique de la langue permet à Scribe v2 Realtime d’identifier la langue parlée parmi plus de 90 langues prises en charge, sans vous demander de la déclarer à l’avance. En contrepartie, le modèle a besoin d’un court extrait audio pour parvenir à une décision fiable ; les premiers résultats partiels d’un flux peuvent donc être moins stables pendant que la langue est déterminée. Si vous connaissez déjà la langue, l’indiquer élimine cette ambiguïté et tend à stabiliser les premiers résultats partiels.

La diarisation des locuteurs attribue la parole à des intervenants distincts, en identifiant qui a dit quoi. En transcription par lot, c’est relativement simple, car le modèle voit l’intégralité du fichier. En flux, c’est plus difficile : le moteur de reconnaissance doit attribuer une étiquette de locuteur à partir du seul audio reçu, et une étiquette attribuée aux premiers passages peut devoir être révisée après avoir entendu davantage la voix de ce locuteur. Traitez les étiquettes de locuteurs en flux comme le texte partiel : elles sont provisoires jusqu’à la finalisation du segment.

L’horodatage au niveau des mots et le contexte des entités suivent la même logique. Plus vous demandez de métadonnées par jeton, plus le modèle et la transmission doivent en transporter. Pour la plupart des interfaces en temps réel, vous n’avez besoin en direct que du texte et des limites de segments ; reportez les métadonnées détaillées à un traitement par lot après l’appel avec Scribe v2.

Formats audio pour le streaming : PCM et mu-law

Le transport et la logique de reconnaissance reçoivent l’essentiel de l’attention, mais une part étonnante des bugs en conditions réelles provient d’une couche inférieure : l’encodage et le découpage de l’audio. Choisir le bon format et la bonne taille de segment est le moyen le moins coûteux de réduire la latence de transcription vocale.

Le PCM — linéaire, signé sur 16 bits, petit-boutiste — est le format à utiliser lorsque vous contrôlez la capture. Des fréquences d’échantillonnage plus élevées transportent davantage de détails acoustiques : 16 kHz est le minimum standard pour la reconnaissance vocale et suffit généralement ; 8 kHz correspond à la qualité téléphonique et perd le contenu haute fréquence. Utilisez la fréquence correspondant à votre source. Il est inutile de suréchantillonner un audio téléphonique à 8 kHz vers 48 kHz, car l’information perdue ne peut pas être récupérée.

Le mu-law à 8 kHz est le format téléphonique. Si vous ingérez des appels depuis un fournisseur de téléphonie, l’audio arrive en mu-law à 8 kHz : transmettez-le dans ce format plutôt que de le transcoder deux fois. Faire correspondre le format source évite les artefacts de rééchantillonnage et une étape de conversion inutile.

La taille des segments est le paramètre qui façonne le plus directement la latence perçue. Vous envoyez l’audio par segments, et le moteur de reconnaissance produit des résultats partiels à leur arrivée. Des segments plus petits entraînent des mises à jour plus fréquentes et réduisent la latence avant le premier résultat partiel ; des segments plus grands impliquent moins de messages et légèrement plus de contexte par inférence. Une plage pratique va de 20 à 250 ms d’audio par segment. À titre de repère, en PCM mono 16 bits à 16 kHz, une seconde d’audio représente 32 000 octets ; un segment de 100 ms représente donc environ 3 200 octets.

Capturer l’entrée microphone dans le navigateur

Dans le navigateur, l’outil adapté est l’API Web Audio API avec un AudioWorklet. Le worklet s’exécute sur le thread de rendu audio, reçoit l’audio sous forme de petites trames et n’est pas soumis aux ralentissements du thread principal, contrairement à l’ancien ScriptProcessorNode. Il convertit les échantillons flottants natifs du navigateur en PCM 16 bits, puis les transmet au thread principal, qui les envoie via WebSocket.

Le cœur du processeur worklet est la conversion des valeurs flottantes en 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);

Le pipeline en code

Le pipeline comporte trois éléments : un client navigateur qui capture le microphone et transmet du PCM à votre serveur, un serveur Node qui relaie l’audio vers Scribe v2 Realtime et les transcriptions en retour, et un client scriptable qui transmet du PCM depuis un fichier ou une passerelle téléphonique.

Le serveur relaie les données au lieu d’exposer directement le moteur de reconnaissance au navigateur pour une raison essentielle : votre clé API ElevenLabs est secrète et ne doit jamais apparaître dans le code côté client. Le serveur conserve la clé. Si le navigateur doit communiquer directement avec le moteur de reconnaissance, créez côté serveur un jeton à usage unique et de courte durée, et transmettez-le au client à la place de la clé API.

Client navigateur

Le client ouvre un WebSocket vers votre serveur, capture le microphone via le worklet ci-dessus et transmet chaque trame PCM dès sa production. Les événements entrants — déjà normalisés par le serveur au format { type, text } — gèrent l’état partiel/final présenté précédemment :

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

Relais serveur

Le serveur ouvre une connexion au moteur de reconnaissance par client, conserve la clé API côté serveur, transmet directement le PCM binaire et normalise les événements du moteur de reconnaissance dans le format stable { type, text } utilisé par le client :

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

Tout ce qui est spécifique au point de terminaison se limite aux deux fonctions d’adaptation ci-dessous. Remplacez les noms de champs par ceux indiqués dans le Guide de l’API Speech to Text ; le reste du pipeline ne change pas :

// 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 scriptable

Pour les pipelines backend et le benchmark ci-dessous, la même connexion au moteur de reconnaissance fonctionne sans navigateur : lisez le PCM depuis n’importe quelle source, cadencez-le selon le rythme des segments en temps réel, puis lisez les événements retournés. La clé API et l’URL proviennent de l’environnement, comme sur le serveur.

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

Évaluer la latence de transcription vocale et le taux d’erreur par mot

La latence et le taux d’erreur par mot varient selon le locuteur, la langue, les conditions acoustiques, la durée de l’audio, votre chemin réseau vers la région la plus proche de chaque fournisseur et la charge actuelle de chaque service. 

Un résultat mesuré depuis un ordinateur portable dans une ville ne s’applique pas nécessairement à votre environnement de production dans une autre. Exécutez le banc d’essai depuis une infrastructure représentative de la production, sur un audio représentatif de votre entrée réelle, et présentez des plages et des distributions plutôt que des chiffres isolés.

Les seuls chiffres de latence et de précision pertinents sont ceux que vous mesurez sur votre propre audio depuis une infrastructure représentative de la production. Voici comment évaluer la latence de transcription vocale.

Mesures de latence pour la transcription vocale

Voici les principales métriques à mesurer lors de l’évaluation de la latence de transcription vocale en temps réel :

  • Délai jusqu’au premier résultat partiel : Du premier segment audio envoyé jusqu’à la réception du premier résultat partiel non vide.
  • Délai entre résultat partiel et final : Du dernier segment audio d’un énoncé jusqu’à l’hypothèse finale.
  • Taux d’erreur par mot (WER) : WER de la transcription finalisée par rapport à une référence humaine, calculé de façon identique pour tous les systèmes.
  • Variations de stabilité : Nombre de résultats partiels réécrits avant finalisation. Cette mesure indique dans quelle mesure l’interface en direct semblera changer.

Contrôles

Pour éviter des données peu fiables, intégrez plusieurs contrôles à votre expérience afin de garantir la cohérence des conditions.

Voici les principaux contrôles à appliquer à l’évaluation de la latence de transcription vocale :

  • Audio identique : Utilisez les mêmes fichiers, la même fréquence d’échantillonnage et le même encodage pour chaque système.
  • Cadence identique : Transmettez chaque système selon la même cadence de segments en temps réel, par exemple des segments de 100 ms.
  • Répéter et présenter les distributions : Exécutez chaque fichier plusieurs fois au cours de la journée ; indiquez la médiane et la queue de distribution (p50/p95).
  • Références et notation identiques : Normalisez le texte de la même manière — casse, ponctuation, nombres — avant de calculer le WER.
  • Indiquer la région et le réseau : Précisez où le banc d’essai a été exécuté et le chemin vers chaque fournisseur.

En gardant tous ces éléments identiques, vous obtiendrez des métriques plus précises.

Structure du banc d’essai

Le cœur de mesure utilise un adaptateur de fournisseur et enregistre le délai jusqu’au premier résultat partiel, le délai de finalisation et les variations des résultats partiels :

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

Le taux d’erreur par mot est une distance de Levenshtein standard, calculée au niveau des jetons sur un texte normalisé. Mettez en minuscules et supprimez la ponctuation de façon identique dans la référence et l’hypothèse avant de le calculer, sinon vous mesurerez votre normaliseur plutôt que le modèle. Placez cette mesure dans une boucle qui exécute chaque fichier environ 10 fois par fournisseur et présente le délai médian jusqu’au premier résultat partiel ainsi que le WER médian (p50/p95), car un échantillon unique est largement dominé par la variance réseau.

Pour l’exécuter, fournissez deux éléments. D’abord, écrivez un adaptateur StreamFn par système. Le client scriptable ci-dessus en est déjà un ; les adaptateurs des autres systèmes suivent le même contrat (audioPath, onEvent, result) et définissent result.lastChunkSentAt lors de l’envoi du dernier segment audio. Ensuite, chargez vos fichiers audio et vos références, puis appelez les mesures pour chacun d’eux. Exécutez le tout depuis une machine représentative de votre déploiement, sur un audio représentatif de vos utilisateurs, et vous disposerez d’une comparaison reproductible.

Récapitulatif : obtenir une transcription vocale en temps réel 

Cet article a présenté de nombreux ajustements architecturaux qui vous permettent d’améliorer progressivement votre système et de vous rapprocher d’une transcription vocale en temps réel.

Un système STT en temps réel prêt pour la production repose sur quelques décisions clés :

  • Transport : Choisissez WebSocket pour sa simplicité et les réseaux contrôlés, et WebRTC lorsque vous avez besoin de tolérance aux pertes et capturez l’audio depuis des appareils grand public.
  • Résultats partiels et finaux : Traitez les résultats partiels comme provisoires et les résultats finaux comme validés, puis affichez-les différemment pour que les utilisateurs aient confiance dans le texte en direct.
  • Détection de fin de parole : Utilisez la VAD pour la segmentation mains libres, la validation manuelle comme contrôle prioritaire, et ajustez le seuil de silence à l’interaction plutôt qu’à une constante.
  • Fonctionnalités en flux : Activez les fonctionnalités en flux uniquement lorsque l’expérience en direct les requiert, et reportez le reste à un traitement par lot avec Scribe v2.
  • Format audio : Capturez de petites trames PCM, envoyez des segments d’environ 100 ms et utilisez le format source pour la téléphonie.
  • Évaluation : Réglez empiriquement les paramètres de précision et de latence à partir de votre propre audio et de votre métrique cible.
  • Sécurité des API : Conservez votre clé API sur le serveur, ou créez des jetons à usage unique pour les connexions directes des clients.

Si vous souhaitez découvrir comment optimiser la latence d’un agent vocal, nous avons également rédigé un guide à ce sujet.

Concevez des systèmes de transcription vocale en temps réel avec Scribe v2 Realtime

Scribe v2 Realtime produit des résultats partiels avec environ 150 ms de latence de modèle. Que vos utilisateurs perçoivent cette valeur ou une valeur supérieure dépend de l’architecture qui l’entoure, et c’est la partie que vous contrôlez. En appliquant les stratégies présentées dans cet article, vous concevrez une architecture de pipeline plus performante, qui réduit la latence et améliore l’expérience client. 

Pour aller plus loin, découvrez la présentation des fonctionnalités Speech to Text, consultez notre référence des modèles pour accéder à la liste complète des fonctionnalités et langues, et visitez les pages produit en temps réel : API Speech to Text en temps réel et Speech to Text en temps réel.

Lorsque vous êtes prêt à créer, créez un compte ElevenLabs gratuit et diffusez votre première transcription dès aujourd’hui.

FAQ sur la latence de transcription vocale en temps réel

Articles similaires

Créez avec l'audio IA de la plus haute qualité