Détection des tours de parole

La détection de tour de parole indique à votre application, pendant une session de transcription en temps réel, quand l'utilisateur commence à parler et quand il a terminé. Utilisez-la chaque fois que votre application agit à la fin de ce que dit quelqu'un : un agent vocal qui répond, une commande vocale ou un sous-titrage en direct qui ferme sa ligne.

i
Information

Pour comprendre pourquoi la fin d'un tour est difficile à détecter, et comment choisir entre répondre plus tôt et couper la parole aux utilisateurs, consultez Tours de parole et prise de tour.

Activer la détection des tours de parole

Activer la détection des tours de parole

Pour activer la détection de tour, ajoutez un objet turn_detection au message session.update qui configure votre session en temps réel. Un objet vide applique les valeurs par défaut :

{
  "type": "session.update",
  "session": {
    "audio_format": { "encoding": "pcm_s16le", "sample_rate": 24000 },
    "turn_detection": {}
  }
}

Envoyez-le avant le premier fragment audio : une fois l'audio démarré, la session rejette toute modification de ses paramètres avec l'erreur Session fields cannot be changed after audio has started.

Attention

La détection de tour fonctionne uniquement dans les sessions en temps réel, et pas en combinaison avec la diarisation :

  • Une requête de transcription de fichier avec turn_detection renvoie une erreur 400 : turn_detection is only supported on the realtime transcription endpoint.
  • Une session en temps réel qui définit à la fois turn_detection et diarize reçoit l'erreur Diarization is not compatible with turn detection. Pour étiqueter les locuteurs, consultez Identifier les locuteurs.

L'exemple suivant envoie un fichier WAV en streaming à son propre rythme et affiche chaque événement de tour avec sa position dans l'audio, puis la transcription complète.

Note

Le SDK Agents vocaux envoie turn_detection via la configuration stt. Pour les sessions WebSocket brutes, 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:
        await ws.recv()  # session.created
        await ws.send(json.dumps({
            "type": "session.update",
            "session": {
                "audio_format": {"encoding": "pcm_s16le", "sample_rate": sample_rate},
                "turn_detection": {},
            },
        }))
        sender = asyncio.create_task(send_audio(ws, AUDIO_FILE))

        async for message in ws:
            event = json.loads(message)
            if event["type"] == "session.updated":
                print("turn_detection:", event["session"]["turn_detection"])
            elif event["type"].startswith("turn."):
                print(f"{event['time_s']:6.2f}s  {event['type']}  {event.get('end_reason', '')}")
            elif event["type"] == "transcription.done":
                print("text:", event["text"])
                break
            elif event["type"] == "error":
                print("error:", event["error"]["message"])
                break
        sender.cancel()

asyncio.run(main())

La sortie ressemble à ceci :

turn_detection: {'start_threshold': 0.5, 'eager_end_threshold': 0.5, 'end_threshold': 0.33, 'min_silence_ms': 500, 'end_timeout_ms': 3000}
  0.00s  turn.start
  1.36s  turn.eager_end
  1.44s  turn.resume
  2.56s  turn.eager_end
  2.64s  turn.resume
...
 34.56s  turn.eager_end
 35.12s  turn.end  threshold
text: Thanks for calling Northwind Support. This is Daniel. How can I help you today? [...]

Dans cette sortie :

  • session.updated reprend les valeurs appliquées par la session. Avec un objet turn_detection vide, ce sont les valeurs par défaut.
  • Chaque pause entre les phrases produit un turn.eager_end, suivi d'un turn.resume quand le locuteur continue.
  • Le tour ne se termine par un turn.end que lorsque le locuteur s'est arrêté. Son champ end_reason indique ce qui l'a terminé.
Fonctionnement de la fin de tour de parole

Fonctionnement de la fin de tour de parole

Pendant la transcription, le modèle évalue de 0 à 1 la probabilité que l’utilisateur se trouve dans un segment de parole sémantiquement actif. Le serveur compare ce score à vos seuils et fait passer la session entre trois états. Chaque passage envoie un événement de tour.

États du tour de parole

Chaque flèche représente un événement, avec la condition qui l'envoie.

États de la détection des tours de parole : En attente, puis turn.start vers L'utilisateur parle quand le score dépasse start_threshold, turn.eager_end vers Probablement terminé quand il passe sous eager_end_threshold, turn.resume pour revenir à L'utilisateur parle quand il dépasse de nouveau start_threshold, et turn.end pour revenir à En attente quand le score est sous end_threshold après une pause de min_silence_ms, ou quand une pause atteint end_timeout_ms.

Le même ordre s'applique dans toutes les sessions :

  • Un tour commence toujours par un turn.start.
  • Un turn.resume annule la fin anticipée qui le précède, et le tour continue.
  • Après un turn.end, le turn.start suivant ouvre un nouveau tour.

Le silence minimal et le délai de fin bornent la décision dans le temps. Le tour ne peut pas se terminer tant que la pause n'a pas atteint min_silence_ms, même quand le modèle est confiant. Une fois que la pause atteint end_timeout_ms, le tour se termine même si le modèle n’est pas confiant, avec end_reason défini sur timeout.

Événements et synchronisation du texte

Événements et synchronisation du texte

Les événements de tour arrivent dans le même flux que les événements de transcription. Chacun porte time_s, la position dans l'audio en secondes depuis le début du flux, et non l'heure de votre horloge.

ÉvénementChampsEnvoyé quandVotre application peut
turn.starttime_sL'utilisateur commence à parlerMarquer le début du tour, par exemple pour indiquer que l'utilisateur est en train de parler.
turn.eager_endtime_sL'utilisateur a probablement terminéCommencer un travail que vous pourrez abandonner, comme préparer une réponse.
turn.resumetime_sL'utilisateur continue après une fin anticipéeAbandonner le travail commencé à la fin anticipée.
turn.endtime_s, end_reasonLe tour est terminéAgir sur le tour : répondre, exécuter la commande ou fermer la ligne de sous-titre.

end_reason vaut threshold quand c'est le score qui a mis fin au tour, ou timeout quand c'est le délai de fin.

Attention

Attendez turn.end avant toute action irréversible, comme envoyer un message ou appeler un outil qui modifie des données. Les fins anticipées sont fréquentes : la plupart des pauses entre les phrases en envoient une.

Les derniers mots arrivent après l'événement de tour

Les derniers mots arrivent après l'événement de tour

La transcription est en retard sur les événements de tour. Quand turn.end survient, le texte des derniers mots du tour peut encore être en route : il arrive dans les événements transcription.text.delta suivants.

Les événements de texte ne comportent pas de champ de temps ; vous ne pouvez donc pas les associer à un tour par horodatage. Pour construire le texte complet d'un tour :

  1. Continuez d'ajouter les deltas de texte au tour en cours après turn.end.
  2. Finalisez le texte du tour une fois le délai de streaming écoulé après l'événement, ou à l'arrivée du turn.start suivant.
  3. Agissez sur le texte complet, par exemple en l'envoyant au modèle qui rédige la réponse.
Astuce

Lors d'un turn.eager_end, le texte reçu jusqu'ici suffit pour commencer à préparer une réponse. Si vous l'envoyez à turn.end, vérifiez que les mots arrivés entre-temps ne changent pas la demande.

Pour le délai de streaming et son effet sur la précision, consultez Transcription en temps réel et Latence.

Ajuster les seuils

Ajuster les seuils

Cinq paramètres de turn_detection contrôlent le moment où les tours commencent et se terminent. Ne définissez que ceux que vous voulez modifier : les autres conservent leurs valeurs par défaut.

Astuce

Partez de l'une des configurations courantes, puis modifiez un seul paramètre à la fois et testez avec des enregistrements de vos utilisateurs réels.

ParamètreValeur par défautCe qu'il faitÀ modifier quand
start_threshold0.5Seuil à partir duquel on considère que l'utilisateur a commencé ou repris la parole.Augmentez-le si les tours de parole démarrent trop facilement, par exemple à cause du bruit de fond.
eager_end_threshold0.5Seuil à partir duquel on considère que l'utilisateur a peut-être fini de parler, ce qui déclenche une fin anticipée.Diminuez-le si les fins anticipées se déclenchent trop souvent.
end_threshold0.33Seuil à partir duquel on considère que l'utilisateur a fini de parler, ce qui met fin au tour de parole.Diminuez-le pour couper moins souvent la parole aux utilisateurs. La réponse arrive alors plus tard.
min_silence_ms500Pause minimale avant que le tour de parole puisse se terminer.Augmentez-le pour éviter que les tours de parole ne se terminent trop tôt. La réponse arrive alors plus tard.
end_timeout_ms3000Pause maximale au bout de laquelle le tour de parole se termine dans tous les cas.Diminuez-le pour limiter plus strictement l'attente maximale.
Attention

Les seuils doivent conserver leur ordre : start_threshold doit être au moins égal à eager_end_threshold, lui-même supérieur à end_threshold, et end_timeout_ms doit être au moins égal à min_silence_ms. Sinon, la session renvoie une erreur.

Configurations courantes

Configurations courantes

Deux jeux de valeurs couvrent la plupart des applications. Choisissez le plus proche, puis ajustez les paramètres individuels à partir de là.

Configurationstart_thresholdeager_end_thresholdend_thresholdmin_silence_msend_timeout_msMeilleur cas
Par défaut0.50.50.335003000Un bon point de départ pour la plupart des conversations. Vous obtenez ces valeurs avec un objet turn_detection vide.
Agressive0.50.40.333002500La vitesse compte avant tout, comme dans les échanges rapides : le tour se termine plus vite après l'arrêt de l'utilisateur, au prix de coupures de parole plus fréquentes.

Aucun champ ne permet de sélectionner une configuration par son nom : envoyez ses valeurs dans turn_detection. Par exemple, la configuration Agressive :

{
  "type": "session.update",
  "session": {
    "audio_format": { "encoding": "pcm_s16le", "sample_rate": 24000 },
    "turn_detection": {
      "start_threshold": 0.5,
      "eager_end_threshold": 0.4,
      "end_threshold": 0.33,
      "min_silence_ms": 300,
      "end_timeout_ms": 2500
    }
  }
}
Aller plus loin

Aller plus loin