Identifier les intervenants

La diarisation étiquette chaque partie d'une transcription avec le locuteur qui l'a prononcée, dans un fichier enregistré ou une session en direct. Cette page montre comment l'activer dans chaque mode, lire les étiquettes et nommer les locuteurs connus avec des clips de référence.

i
Information

Pour connaître la signification des étiquettes de locuteur et savoir comment afficher les locuteurs qui parlent en même temps, consultez Locuteurs et diarisation.

Référence API — POST /v1/audio/transcriptions
Activer la diarisation

Activer la diarisation

La diarisation fonctionne hors ligne et en temps réel, avec la même option diarize. Seuls l'envoi de cette option et le retour des étiquettes diffèrent.

Hors ligne

Hors ligne

Pour diariser un fichier, définissez diarize sur true et timestamp_granularities sur segment dans la requête de transcription. La diarisation attribue des étiquettes aux segments : la requête échoue donc sans horodatage de segment.

import os

from mistralai.client import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

with open("customer_call.wav", "rb") as audio:
    transcription = client.audio.transcriptions.complete(
        model="voxtral-mini-transcribe-3",
        file={"file_name": "customer_call.wav", "content": audio},
        diarize=True,
        timestamp_granularities=["segment"],
    )

for segment in transcription.segments:
    print(f"[{segment.start:5.1f}s - {segment.end:5.1f}s] {segment.speaker_id}: {segment.text.strip()}")

La réponse contient la transcription sous forme de liste de segments, chacun avec son locuteur :

{
  "segments": [
    {
      "type": "transcription_segment",
      "text": "Thanks for calling Northwind Support.",
      "start": 0.0,
      "end": 1.6,
      "speaker_id": "speaker_1"
    },
    {
      "type": "transcription_segment",
      "text": " This is Daniel.",
      "start": 1.7,
      "end": 2.4,
      "speaker_id": "speaker_1"
    },
    {
      "type": "transcription_segment",
      "text": " How can I help you today?",
      "start": 2.6,
      "end": 3.5,
      "speaker_id": "speaker_1"
    },
    {
      "type": "transcription_segment",
      "text": " Hi Daniel.",
      "start": 4.1,
      "end": 4.8,
      "speaker_id": "speaker_2"
    }
  ]
}

Le script Python affiche une ligne par segment :

[  0.0s -   1.6s] speaker_1: Thanks for calling Northwind Support.
[  1.7s -   2.4s] speaker_1: This is Daniel.
[  2.6s -   3.5s] speaker_1: How can I help you today?
[  4.1s -   4.8s] speaker_2: Hi Daniel.
[  5.1s -  13.2s] speaker_2: I ordered two Quilliera headsets last week, order 4417, and the tracking page still says pending.
[ 13.8s -  14.3s] speaker_1: Let me check.
Temps réel

Temps réel

Pour diariser une session en direct, définissez diarize sur true dans le message session.update, avant d'envoyer le premier fragment audio :

{
  "type": "session.update",
  "session": {
    "audio_format": { "encoding": "pcm_s16le", "sample_rate": 16000 },
    "diarize": true
  }
}

Avec la diarisation activée, la session envoie des événements transcription.segment au lieu des événements transcription.text.delta. Chaque événement contient quelques mots et l'étiquette de leur locuteur :

{"type": "transcription.segment", "text": " today?", "start": null, "end": null, "speaker_id": "speaker_1"}
{"type": "transcription.segment", "text": " Hi,", "start": null, "end": null, "speaker_id": "speaker_2"}
{"type": "transcription.segment", "text": " Daniel.", "start": null, "end": null, "speaker_id": "speaker_2"}

Pour un client temps réel complet, consultez le script Python dans Nommer des locuteurs avec des clips de référence : il envoie la même configuration de session, plus les clips de référence.

Note

Le SDK Agents vocaux n'envoie ni diarize ni speaker_references dans sa configuration stt. Pour une diarisation temps réel avec des références de locuteurs, utilisez une session WebSocket brute. Les exemples temps réel de cette page utilisent directement un package WebSocket : websockets en Python, installé par mistralai[realtime], et ws en TypeScript, installé par @mistralai/mistralai.

Lire les étiquettes de locuteur

Lire les étiquettes de locuteur

Les étiquettes portent les mêmes valeurs dans les deux modes, mais arrivent à un endroit différent :

Hors ligneTemps réel
Emplacement de l'étiquettespeaker_id sur chaque élément de segments dans la réponsespeaker_id sur chaque événement transcription.segment
Contenu d'un élémentUn segment d'une ou plusieurs phrasesQuelques mots, dès qu'ils sont transcrits
Horodatagesstart et end, en secondesstart et end valent null
Transcription complètetext contient toute la transcription, sans étiquettestranscription.done contient tout le texte et les segments étiquetés
Valeurs des étiquettesspeaker_1, speaker_2 et ainsi de suite, ou les noms de vos clips de référenceIdentique au mode hors ligne

Pour afficher la transcription sous forme de tours de parole, commencez un nouveau tour à chaque changement de speaker_id, puis ajoutez-y le texte des éléments suivants. Les deux exemples Python de cette page affichent la transcription de cette façon.

Attention

Les étiquettes ne sont valables qu'au sein d'une même transcription ou d'une même session. speaker_1 dans un fichier et speaker_1 dans le suivant ne désignent pas nécessairement la même personne. Pour conserver le même nom pour une personne d'un enregistrement à l'autre, utilisez des clips de référence.

Segments qui se chevauchent

Segments qui se chevauchent

Quand deux personnes parlent en même temps, la transcription doit placer les deux voix sur la même plage de temps.

Si votre application dessine des segments sur une timeline, ou les affiche sous forme de thread de conversation, ne supposez pas que chaque segment se termine avant le début du suivant :

  • Triez les segments par start et conservez les deux segments quand leurs périodes se chevauchent.
  • Affichez deux locuteurs actifs à la fois, par exemple sur des pistes séparées d'une timeline, plutôt que de tronquer un segment.
  • Fusionnez des segments consécutifs du même locuteur en tours de parole uniquement quand ils ne chevauchent pas ceux d'un autre locuteur.
Nommer des locuteurs avec des clips de référence

Nommer des locuteurs avec des clips de référence

Quand vous savez qui parle, envoyez un court clip de la voix de chaque personne, sous un nom donné. Les segments de cette voix portent alors ce nom dans speaker_id, au lieu d'une étiquette générique. Les clips de référence respectent les règles suivantes :

RègleValeur
Nombre de clipsJusqu'à 8. Le nombre total de locuteurs dans les références et la prédiction ne doit pas dépasser 10.
Durée de chaque clip3 à 15 secondes
Débit de paroleAu moins 85 % du clip doit contenir de la parole. Évitez les longs silences.
Format
NomLa clé de chaque clip. Elle devient le speaker_id des segments de ce locuteur.
DiarisationObligatoire : définissez diarize sur true dans la même requête ou session
ModesHors ligne et temps réel

Plus les conditions acoustiques et vocales du clip de référence sont proches de l'audio à transcrire, meilleure sera la correspondance. Pour plus de conseils sur le choix de bons clips de référence, consultez Nommer des locuteurs avec des clips de référence.

Ajoutez speaker_references au message session.update, à côté de diarize :

{
  "type": "session.update",
  "session": {
    "audio_format": { "encoding": "pcm_s16le", "sample_rate": 24000 },
    "diarize": true,
    "speaker_references": {
      "agent": "<base64 of agent_reference.wav>",
      "customer": "<base64 of customer_reference.wav>"
    }
  }
}

Le script suivant envoie un appel enregistré en streaming, à vitesse réelle, et affiche une nouvelle ligne à chaque changement de locuteur :

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

def b64_file(path):
    with open(path, "rb") as f:
        return base64.b64encode(f.read()).decode()

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},
                "diarize": True,
                "speaker_references": {
                    "agent": b64_file("agent_reference.wav"),
                    "customer": b64_file("customer_reference.wav"),
                },
            },
        }))
        sender = asyncio.create_task(send_audio(ws, AUDIO_FILE))

        speaker = None
        async for message in ws:
            event = json.loads(message)
            if event["type"] == "transcription.segment":
                text = event["text"]
                if event["speaker_id"] != speaker:
                    speaker = event["speaker_id"]
                    print(f"\n{speaker}: ", end="")
                    text = text.lstrip()
                print(text, end="", flush=True)
            elif event["type"] == "transcription.done":
                print()
                break
            elif event["type"] == "error":
                print("error:", event["error"]["message"])
                break
        sender.cancel()

asyncio.run(main())
agent: Thanks for calling Northwind Support. This is Daniel. How can I help you today?
customer: Hi, Daniel. I ordered two Quilera headsets last week. Order four, 417. And the tracking page still says pending.
agent: Let me check. I can see order 4417. It left our Rotterdam warehouse yesterday, but the tracking was not updated.
customer: Okay. Can you send me the new tracking link? And I also need an invoice for my company, Alder Creek Design.
Diarisation et détection des tours de parole

Diarisation et détection des tours de parole

Une session temps réel peut étiqueter les locuteurs ou détecter les tours de parole, mais pas les deux. Si un même message session.update définit diarize sur true et inclut turn_detection, la session envoie un événement d'erreur. La détection des tours de parole n'existe qu'en temps réel, les requêtes hors ligne ne sont donc pas concernées.

Astuce

Pour répondre aux utilisateurs en direct et conserver un enregistrement étiqueté par locuteur, exécutez la session en direct avec la détection des tours de parole, conservez l'audio, puis diarisez l'enregistrement hors ligne.

Pour savoir quelle fonctionnalité convient à votre application, consultez Détection des tours de parole et diarisation. Pour les paramètres de la détection des tours de parole, voir Détection des tours de parole.

Erreurs

Erreurs

Les requêtes hors ligne renvoient une erreur HTTP. Les sessions temps réel envoient un événement de type error, avec le message dans error.message. Ces erreurs sont spécifiques à la diarisation :

Message d'erreurModeCauseSolution
When diarize is set to True and streaming is disabled, the timestamp granularity must be set to ['segment'] (422)Hors lignediarize sans horodatage de segmentDéfinissez timestamp_granularities sur segment.
Speaker references require diarization (422 hors ligne, événement error en temps réel)Les deuxClips de référence sans diarizeDéfinissez diarize sur true.
Diarization is not compatible with turn detection.Temps réeldiarize et turn_detection dans la même sessionSupprimez l'un des deux. Consultez Diarisation et détection des tours de parole.
Les deuxPlus de 8 clips de référence, ou un clip de moins de 3 s ou de plus de 15 s
Hors ligneLe modèle n'accepte pas les clips de référenceUtilisez un modèle qui les prend en charge.

Pour les autres erreurs de transcription, consultez Dépannage.

Aller plus loin

Aller plus loin