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 audio 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 et prise de parole.

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 Distinguer 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
import asyncio
import base64
import json
import os
import wave

import websockets

MODEL = "voxtral-mini-transcribe-realtime-3-0"
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

Le serveur compare ce score à vos seuils et fait passer la session entre trois états. Chaque passage envoie un événement de tour.

Turn states

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

Turn detection states: Waiting, then turn.start to User speaking when the score rises above start_threshold, turn.eager_end to Probably finished when it falls below eager_end_threshold, turn.resume back to User speaking when it rises above start_threshold again, and turn.end back to Waiting when the score is below end_threshold after a pause of min_silence_ms, or when a pause reaches 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.

É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 un eager end.Diminuez-le si les eager ends 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