Transcription en temps réel

La transcription en temps réel convertit en texte l’audio en direct pendant que la personne parle encore. Utilisez-la dans les applications agentiques : transcrivez le flux du microphone ou du téléphone d’un utilisateur, puis transmettez la transcription à un modèle qui génère la réponse de l’assistant.

Cette page explique le déroulement d’une session du début à la fin, sa configuration, la lecture de ses événements et le compromis entre vitesse et précision.

i
Information
Comprendre le cycle de vie

Comprendre le cycle de vie

Une session correspond à une connexion WebSocket qui passe par quatre étapes, toujours dans le même ordre. Votre application envoie l’audio, et l’API renvoie le texte pendant que l’audio continue d’arriver.

Cycle de vie de la session

Lisez de haut en bas : chaque flèche représente un message, envoyé par votre application à gauche ou par l’API à droite.

Cycle de vie d'une session en temps réel : connexion, puis session.created ; session.update, puis session.updated ; input_audio.append et transcription.text.delta, répétés tant que l'audio arrive ; puis input_audio.end et transcription.done avec la transcription complète.

À chaque étape, votre application procède comme suit :

ÉtapeVous envoyezVous recevezCe que fait votre application
OuvertureUne connexion avec le modèle et votre clé APIsession.created, avec les paramètres par défautAttendez cet événement avant d’envoyer quoi que ce soit d’autre.
Configurationsession.update, avec vos paramètressession.updated, avec les paramètres appliqués par la sessionVérifiez les valeurs. Elles sont figées dès le premier fragment audio.
Streaminginput_audio.append, un fragment audio à la foistranscription.text.delta, avec chaque nouveau morceau de texteAjoutez chaque morceau de texte à la fin de la transcription affichée.
Fininput_audio.end, quand l’audio s’arrêtetranscription.done, avec la transcription complèteEnregistrez la transcription complète, puis fermez la connexion.

Si l’API rejette un message, elle envoie à la place un événement error à cette étape. Consultez Problèmes courants.

Se connecter à une session

Se connecter à une session

Une session est une connexion WebSocket vers l’endpoint temps réel, avec le modèle dans la chaîne de requête :

wss://api.mistral.ai/v1/audio/transcriptions/realtime?model=voxtral-mini-transcribe-realtime-3

La connexion s’authentifie avec votre clé API Mistral, envoyée sous la forme Authorization: Bearer <votre clé API>. Pour créer une clé, ouvrez Clés API.

Avec le SDK Python ou TypeScript, connect() ouvre la connexion et rend la main une fois la session créée :

import asyncio
import os

from mistralai.client import Mistral

async def main():
    client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
    async with await client.audio.realtime.connect(
        model="voxtral-mini-transcribe-realtime-3",
    ) as connection:
        print(connection.session.model_dump_json(indent=2))

asyncio.run(main())
Configurer la session

Configurer la session

Envoyez vos paramètres dans un message session.update, avant le premier fragment audio. La session répond par session.updated, qui reprend chaque paramètre qu’elle applique :

{
  "type": "session.update",
  "session": {
    "audio_format": { "encoding": "pcm_s16le", "sample_rate": 24000 },
    "target_streaming_delay_ms": 480,
    "language": "en",
    "context_bias": ["Northwind", "Quillara", "Alder Creek Design", "Rotterdam"]
  }
}

Le script suivant envoie ces paramètres, transmet un fichier WAV en streaming par fragments de 100 ms et affiche la transcription au fur et à mesure de son arrivée.

Note

Le SDK Agents vocaux envoie language, context_bias et turn_detection via sa configuration stt. Pour les sessions WebSocket brutes, sans le SDK, envoyez session.update avec websockets en Python et ws en TypeScript.

import asyncio
import base64
import json
import os
import wave

import websockets

MODEL = "voxtral-mini-transcribe-realtime-3"
URL = f"wss://api.mistral.ai/v1/audio/transcriptions/realtime?model={MODEL}"
HEADERS = {"Authorization": f"Bearer {os.environ['MISTRAL_API_KEY']}"}
AUDIO_FILE = "customer_call.wav"  # 16-bit mono PCM WAV

async def send_audio(ws, path):
    with wave.open(path, "rb") as wav:
        while chunk := wav.readframes(wav.getframerate() // 10):
            await ws.send(json.dumps({"type": "input_audio.append", "audio": base64.b64encode(chunk).decode()}))
    await ws.send(json.dumps({"type": "input_audio.end"}))

async def main():
    with wave.open(AUDIO_FILE, "rb") as wav:
        sample_rate = wav.getframerate()

    async with websockets.connect(URL, additional_headers=HEADERS) as ws:
        print(json.loads(await ws.recv())["type"])  # session.created

        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "audio_format": {"encoding": "pcm_s16le", "sample_rate": sample_rate},
                "target_streaming_delay_ms": 480,
                "language": "en",
                "context_bias": ["Northwind", "Quillara", "Alder Creek Design", "Rotterdam"],
            },
        }))
        sender = asyncio.create_task(send_audio(ws, AUDIO_FILE))

        async for message in ws:
            event = json.loads(message)
            if event["type"] == "session.updated":
                print("session.updated:", json.dumps(event["session"]))
            elif event["type"] == "transcription.text.delta":
                print(event["text"], end="", flush=True)
            elif event["type"] == "transcription.done":
                print("\ntranscription.done")
                break
            elif event["type"] == "error":
                print("error:", event["error"]["message"])
                break
        sender.cancel()

asyncio.run(main())
Attention

Les paramètres sont figés une fois l’audio démarré. Un session.update envoyé après le premier input_audio.append renvoie l’erreur Session fields cannot be changed after audio has started. Pour modifier un paramètre, ouvrez une nouvelle session.

Champs de la session

Champs de la session

session.update accepte les champs suivants. N’envoyez que ceux que vous voulez modifier : les autres gardent la valeur par défaut indiquée dans session.created.

ChampTypeValeur par défautUtilisez-le pour
audio_formatObjet : encoding, sample_ratepcm_s16le à 16000 HzDécrire l’audio que vous envoyez : PCM 16 bits signé little-endian, mono, à la fréquence d’échantillonnage de votre source. Si vous ne le définissez pas, le format est déduit automatiquement des données PCM.
target_streaming_delay_msEntier480 ms (point de départ recommandé)Arbitrer entre vitesse et précision : toute valeur de 80 à 1200, ou 2400. Consultez Contrôler la latence.
languageChaîne, un code ISO 639-1 à deux lettres comme "en"Non définiFixer la langue de l’audio. Sans ce champ, le modèle détecte la langue. Consultez Langues.
context_biasTableau de chaînes[]Aider le modèle à orthographier les noms et les termes. Consultez Biais contextuels.
diarizeBooléenfalseÉtiqueter chaque morceau de texte avec son locuteur. Consultez Identifier les locuteurs.
speaker_referencesObjet : un nom pour chaque extrait audio en base64Non définiNommer des locuteurs connus, avec jusqu’à 8 extraits de 3 à 15 secondes. Nécessite diarize. Consultez Identifier les locuteurs.
turn_detectionObjetNon définiSavoir quand l’utilisateur commence et finit de parler. Consultez Détection des tours de parole.
Attention

diarize et turn_detection ne peuvent pas être utilisés dans la même session : la session renvoie l’erreur Diarization is not compatible with turn detection.

Événements : recevoir les mises à jour et terminer

Événements : recevoir les mises à jour et terminer

Tout ce que l’API envoie arrive sous forme de message JSON avec un champ type. Lisez d’abord le type, puis les champs de l’événement correspondant :

ÉvénementChampsEnvoyé quand
session.createdsessionLa connexion s’ouvre, avec les paramètres par défaut.
session.updatedsessionAprès chaque session.update, avec les paramètres appliqués par la session.
transcription.text.deltatextUn nouveau texte est prêt, pendant que l’audio arrive.
transcription.segmenttext, speaker_id, start, endAvec diarize activé, à la place de transcription.text.delta. Consultez Identifier les locuteurs.
turn.start, turn.eager_end, turn.resume, turn.endtime_s, et end_reason sur turn.endAvec turn_detection activé, aux différents moments de chaque tour. Consultez Détection des tours de parole.
transcription.donetext, segments, language, model, usageAprès input_audio.end, une fois tout l’audio transcrit.
errorerror.message, error.codeL’API rejette un message ou un paramètre.
Afficher le texte au fur et à mesure

Afficher le texte au fur et à mesure

Chaque transcription.text.delta ajoute du texte après celui déjà envoyé. Un delta ne corrige jamais le texte précédent : votre application se contente donc d’ajouter, il n’y a rien à remplacer.

  • Affichez chaque delta dès son arrivée. Il est définitif dès que vous le recevez.
  • Ajoutez-le tel quel. Les deltas contiennent leurs propres espaces et leur ponctuation, comme " for" ou "." : n’ajoutez pas de séparateurs.
  • Enregistrez le text de transcription.done comme transcription de référence.
Astuce

Pour afficher d’abord un texte rapide, puis le remplacer par un texte plus précis, il faut deux sessions sur le même audio. Consultez Débit de transcription variable.

Terminer la session

Terminer la session

Quand l’audio s’arrête, envoyez input_audio.end. L’API envoie les derniers deltas, puis transcription.done avec la transcription complète. Fermez la connexion une fois cet événement reçu.

Contrôler la latence

Contrôler la latence

Le modèle utilise un délai configurable pour contrôler le temps qu’il attend entre le moment où un mot est prononcé et celui où il est transcrit :

  • Un délai plus court transcrit la parole plus tôt, mais avec moins de précision.
  • Un délai plus long donne plus de contexte au modèle : le texte est plus précis, mais arrive plus tard.

target_streaming_delay_ms définit ce délai pour la session. Il accepte tout entier de 80 à 1200, ou 2400. Toute autre valeur renvoie une erreur. Commencez par 480 ms, qui équilibre latence et précision. Le SDK Python peut le définir avec update_session(), en même temps que le format audio.

i
Information

Pour situer ce délai dans l’attente que perçoivent vos utilisateurs, consultez Latence. Les événements de tour n’attendent pas ce délai : consultez Détection des tours de parole.

Débit de transcription variable

Débit de transcription variable

Les lecteurs voient les mots tout de suite, et le texte qu’ils conservent est le plus précis. Les sous-titres en direct et la prise de notes en profitent le plus.

Problèmes courants

Problèmes courants

La plupart des problèmes se traduisent par un événement error accompagné d’un message. Voici ceux propres aux sessions en direct :

Erreur ou symptômeCauseSolution
Session fields cannot be changed after audio has started.Un session.update envoyé après le premier fragment audioEnvoyez tous les paramètres avant le premier input_audio.append. Pour en modifier un, ouvrez une nouvelle session.
target_streaming_delay_ms must be one of the trained valuesUn délai hors de la plage de 80 à 1200, autre que 2400Choisissez une valeur dans la plage acceptée. Consultez Contrôler la latence.
Diarization is not compatible with turn detection.diarize et turn_detection dans la même sessionGardez l’un des deux. Consultez Détection des tours de parole.
Speaker references require diarization, set diarize=true.speaker_references sans diarizeDéfinissez diarize sur true.
Une erreur qui mentionne Invalid language alpha2 codeUne valeur de language qui n’est pas un code ISO 639-1 à deux lettresUtilisez un code comme "en" ou "fr".
La latence mesurée sur un fichier ne correspond pas à l’usage en directLe fichier est envoyé plus vite que le temps réelPour mesurer la latence, envoyez chaque fragment au rythme de l’audio, par exemple 100 ms d’audio toutes les 100 ms.

Pour les autres erreurs, consultez Résolution des problèmes de reconnaissance vocale.

Aller plus loin

Aller plus loin