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.
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.
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
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
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.
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
Les étiquettes portent les mêmes valeurs dans les deux modes, mais arrivent à un endroit différent :
| Hors ligne | Temps réel | |
|---|---|---|
| Emplacement de l'étiquette | speaker_id sur chaque élément de segments dans la réponse | speaker_id sur chaque événement transcription.segment |
| Contenu d'un élément | Un segment d'une ou plusieurs phrases | Quelques mots, dès qu'ils sont transcrits |
| Horodatages | start et end, en secondes | start et end valent null |
| Transcription complète | text contient toute la transcription, sans étiquettes | transcription.done contient tout le texte et les segments étiquetés |
| Valeurs des étiquettes | speaker_1, speaker_2 et ainsi de suite, ou les noms de vos clips de référence | Identique 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.
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
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
startet 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
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ègle | Valeur |
|---|---|
| Nombre de clips | Jusqu'à 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 clip | 3 à 15 secondes |
| Débit de parole | Au moins 85 % du clip doit contenir de la parole. Évitez les longs silences. |
| Format | |
| Nom | La clé de chaque clip. Elle devient le speaker_id des segments de ce locuteur. |
| Diarisation | Obligatoire : définissez diarize sur true dans la même requête ou session |
| Modes | Hors 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
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.
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
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'erreur | Mode | Cause | Solution |
|---|---|---|---|
When diarize is set to True and streaming is disabled, the timestamp granularity must be set to ['segment'] (422) | Hors ligne | diarize sans horodatage de segment | Définissez timestamp_granularities sur segment. |
Speaker references require diarization (422 hors ligne, événement error en temps réel) | Les deux | Clips de référence sans diarize | Définissez diarize sur true. |
Diarization is not compatible with turn detection. | Temps réel | diarize et turn_detection dans la même session | Supprimez l'un des deux. Consultez Diarisation et détection des tours de parole. |
| Les deux | Plus de 8 clips de référence, ou un clip de moins de 3 s ou de plus de 15 s | ||
| Hors ligne | Le modèle n'accepte pas les clips de référence | Utilisez un modèle qui les prend en charge. |
Pour les autres erreurs de transcription, consultez Dépannage.