Aller au contenu principal
Leçon 7 sur 16 · Voxtral Audio — Transcription et Speech-to-Text

Diarisation : Identifier Qui Parle

Leçon 7 sur 16 Mis à jour le 3 novembre 2026 Développeur
Sur cette page
  1. Qu’est-ce que la diarisation
  2. Activer la diarisation
  3. Exploiter les résultats
  4. Générer un compte-rendu structuré
  5. Aller jusqu’à l’analyse complète
  6. Ce que la diarisation ne fait pas
  7. Nommer les locuteurs avec des extraits de référence
  8. Points clés à retenir

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 par voxtral-mini-transcribe-3, voxtral-mini-transcribe-realtime-3 et voxtral-mini-tts-3-0. À connaître : horodatage par segment uniquement (word renvoie 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ées context_bias libres (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 non speaker_0) et le champ s’appelle speaker_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