Diarisation : Identifier Qui Parle
Sur cette page
Cours mis à jour le 3 novembre 2026 pour Voxtral 3, d’après le guide Migrate from Voxtral 2 de Mistral : les modèles Voxtral 2 (
voxtral-mini-2602,voxtral-mini-transcribe-realtime-2602,voxtral-mini-tts-2603), dépréciés le 31 octobre 2026 et retirés le 30 avril 2027, sont remplacés dans les exemples parvoxtral-mini-transcribe-3,voxtral-mini-transcribe-realtime-3etvoxtral-mini-tts-3-0. À connaître : horodatage par segment uniquement (wordrenvoie une erreur 400), diarisation possible en temps réel, réglages d’une session temps réel figés dès le premier morceau d’audio, entréescontext_biaslibres (phrases avec espaces). Sources : Migrate from Voxtral 2 · Speech to Text. Complément d’après le guide Diarize speakers : les étiquettes commencent àspeaker_1(et nonspeaker_0) et le champ s’appellespeaker_id; les extraits de référence (speaker_references) permettent de nommer les locuteurs connus, et la diarisation fonctionne aussi en temps réel, contrairement à ce qu’affirmait l’ancienne version. Sources : Diarize speakers · Speakers and diarization.
Qu’est-ce que la diarisation
La diarisation (speaker diarization) est la capacité à identifier et séparer les différents locuteurs dans un enregistrement audio. Le modèle attribue automatiquement un identifiant à chaque voix distincte et segmente la transcription en conséquence.
L’écart de valeur est considérable. Une réunion à cinq personnes transcrite sans diarisation produit un bloc de texte où plus personne ne sait qui a pris quel engagement ; la même réunion diarisée devient un compte-rendu attribuable. C’est pourquoi cette fonctionnalité est essentielle pour tous les enregistrements multi-locuteurs : réunions, interviews, appels téléphoniques, débats, tables rondes.
Activer la diarisation
Un seul paramètre suffit : diarize=True.
import os
from mistralai.client import Mistral
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
with open("reunion_equipe.mp3", "rb") as f:
response = client.audio.transcriptions.complete(
model="voxtral-mini-transcribe-3",
file={"content": f, "file_name": "reunion_equipe.mp3"},
diarize=True,
timestamp_granularities=["segment"]
)
La diarisation fonctionne de pair avec les timestamps par segment : c’est le segment qui porte l’information. Chaque segment retourné contient désormais un champ speaker_id identifiant le locuteur ; sans timestamps par segment, la requête échoue.
Exploiter les résultats
L’affichage le plus simple parcourt les segments et préfixe chaque prise de parole de son auteur et de son horodatage.
for seg in response.segments:
temps = f"{seg.start:.1f}s"
print(f"[{temps}] {seg.speaker_id} : {seg.text}")
Le résultat prend cette forme :
[0.0s] speaker_1 : Bonjour à tous, on commence la réunion hebdomadaire.
[3.2s] speaker_1 : Julien, tu veux commencer avec le point technique ?
[5.8s] speaker_2 : Oui, alors cette semaine on a finalisé la migration.
[12.4s] speaker_2 : Tout est déployé en production depuis mercredi.
[15.1s] speaker_3 : Super, et côté performances, on a vu une amélioration ?
[18.3s] speaker_2 : Oui, le temps de réponse a baissé de trente pour cent.
Les locuteurs sont identifiés comme speaker_1, speaker_2, etc. Ces étiquettes ne valent qu’à l’intérieur d’une transcription : speaker_1 dans un fichier et speaker_1 dans le suivant ne sont pas forcément la même personne. Sans référence, le modèle ne connaît pas les noms — c’est à vous de mapper ces identifiants aux personnes réelles, par exemple après une écoute des premières secondes, dans un simple dictionnaire.
# Mapping des identifiants aux noms réels
noms_locuteurs = {
"speaker_1": "Marie (Manager)",
"speaker_2": "Julien (Tech Lead)",
"speaker_3": "Sarah (Product)",
}
for seg in response.segments:
nom = noms_locuteurs.get(seg.speaker_id, seg.speaker_id)
minutes = int(seg.start // 60)
secondes = int(seg.start % 60)
print(f"[{minutes:02d}:{secondes:02d}] {nom}")
print(f" {seg.text}\n")
Une fois le mapping en place, les durées de segment se transforment en indicateurs. Cumuler seg.end - seg.start par locuteur vous donne le temps de parole de chacun — une mesure que les animateurs de réunion et les responsables qualité en centre d’appels utilisent volontiers.
from collections import defaultdict
temps_parole = defaultdict(float)
for seg in response.segments:
duree = seg.end - seg.start
temps_parole[seg.speaker_id] += duree
print("=== Temps de parole ===")
total = sum(temps_parole.values())
for locuteur, duree in sorted(temps_parole.items()):
nom = noms_locuteurs.get(locuteur, locuteur)
pourcentage = (duree / total) * 100
minutes = int(duree // 60)
secondes = int(duree % 60)
print(f"{nom} : {minutes}m{secondes:02d}s ({pourcentage:.1f}%)")
=== Temps de parole ===
Marie (Manager) : 8m23s (28.4%)
Julien (Tech Lead) : 12m45s (43.2%)
Sarah (Product) : 8m22s (28.4%)
Générer un compte-rendu structuré
La diarisation combinée aux segments permet de produire un compte-rendu directement lisible. L’astuce du script suivant tient en une ligne : il n’écrit un nouvel en-tête que lorsque le locuteur change, ce qui regroupe les interventions consécutives d’une même personne au lieu de les émietter.
def generer_compte_rendu(segments, noms):
"""Génère un compte-rendu Markdown à partir des segments diarisés."""
lignes = ["# Compte-rendu de réunion\n"]
locuteur_actuel = None
for seg in segments:
nom = noms.get(seg.speaker_id, seg.speaker_id)
if seg.speaker_id != locuteur_actuel:
locuteur_actuel = seg.speaker_id
minutes = int(seg.start // 60)
secondes = int(seg.start % 60)
lignes.append(f"\n**{nom}** ({minutes:02d}:{secondes:02d}) :")
lignes.append(f" {seg.text}")
return "\n".join(lignes)
compte_rendu = generer_compte_rendu(response.segments, noms_locuteurs)
with open("compte_rendu.md", "w", encoding="utf-8") as f:
f.write(compte_rendu)
print("Compte-rendu généré")
Aller jusqu’à l’analyse complète
Voici le script qui rassemble tout ce qui précède : transcription diarisée, statistiques par intervenant et restitution en JSON, prêt à alimenter un tableau de bord ou un LLM de synthèse.
import os
import json
from collections import defaultdict
from mistralai.client import Mistral
def analyser_reunion(chemin_audio: str, noms: dict = None):
"""Transcrit et analyse une réunion complète."""
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
# Transcrire avec diarisation
with open(chemin_audio, "rb") as f:
response = client.audio.transcriptions.complete(
model="voxtral-mini-transcribe-3",
file={"content": f, "file_name": chemin_audio},
diarize=True,
timestamp_granularities=["segment"]
)
noms = noms or {}
# Statistiques
stats = defaultdict(lambda: {"duree": 0.0, "segments": 0, "mots": 0})
for seg in response.segments:
stats[seg.speaker_id]["duree"] += seg.end - seg.start
stats[seg.speaker_id]["segments"] += 1
stats[seg.speaker_id]["mots"] += len(seg.text.split())
# Résultat
resultat = {
"transcription": response.text,
"nb_locuteurs": len(stats),
"duree_totale": max(s.end for s in response.segments),
"statistiques": {
noms.get(loc, loc): {
"duree_secondes": round(data["duree"], 1),
"nb_segments": data["segments"],
"nb_mots": data["mots"]
}
for loc, data in stats.items()
}
}
return resultat
# Utilisation
resultat = analyser_reunion("reunion.mp3", {
"speaker_1": "Marie",
"speaker_2": "Julien",
"speaker_3": "Sarah"
})
print(json.dumps(resultat["statistiques"], indent=2, ensure_ascii=False))
Ce que la diarisation ne fait pas
Gardez en tête quelques limites avant de promettre un résultat à un client. Sans extraits de référence, le modèle distingue des voix, pas des personnes, et le mapping reste à votre charge. Il détecte en revanche seul le nombre de locuteurs, vous n’avez pas à le spécifier. Deux voix proches peuvent être confondues, ce qui justifie une relecture des passages critiques. La parole simultanée de deux locuteurs n’est pas prise en charge dans la version actuelle, d’après Mistral. Enfin, la diarisation existe aussi en temps réel, avec voxtral-mini-transcribe-realtime-3 (diarize dans session.update, étiquettes portées par les événements transcription.segment), mais une même session ne peut pas combiner diarisation et détection de tours de parole.
Nommer les locuteurs avec des extraits de référence
Quand vous savez à l’avance qui parle — le conseiller et le client d’un appel de support —, vous pouvez envoyer un court extrait de chaque voix sous un nom : les segments de cette voix porteront alors ce nom dans speaker_id, et le même nom désignera la même personne d’une transcription à l’autre.
import base64
def en_base64(chemin):
with open(chemin, "rb") as f:
return base64.b64encode(f.read()).decode()
with open("appel_support.wav", "rb") as f:
response = client.audio.transcriptions.complete(
model="voxtral-mini-transcribe-3",
file={"content": f, "file_name": "appel_support.wav"},
diarize=True,
timestamp_granularities=["segment"],
speaker_references={
"conseiller": en_base64("ref_conseiller.wav"),
"client": en_base64("ref_client.wav"),
},
)
D’après la documentation de Mistral, chaque extrait dure de 3 à 15 secondes et contient au moins 85 % de parole ; vous pouvez en fournir 8 au plus, et le total des locuteurs, référencés ou détectés, ne doit pas dépasser 10. WAV, Opus, MP3 et FLAC sont acceptés, les formats sans perte donnant les meilleurs résultats ; plus les conditions d’enregistrement de l’extrait ressemblent à celles de l’audio, meilleure est la correspondance. Un locuteur sans référence garde une étiquette générique. Les références exigent diarize=True, fonctionnent aussi en temps réel (speaker_references dans session.update), et un modèle qui ne les accepte pas renvoie une erreur.
Points clés à retenir
- Activez la diarisation avec
diarize=True— un seul paramètre timestamp_granularities=["segment"]est obligatoire avec la diarisation- Chaque segment contient un champ
speaker_id(speaker_1,speaker_2, etc.) - Mappez les identifiants aux noms réels, ou nommez les voix connues avec
speaker_references - Exploitez les statistiques : temps de parole, nombre d’interventions, nombre de mots
- La diarisation fonctionne en offline et en temps réel, mais pas avec la détection de tours dans une même session