Aller au contenu principal

Diarisation : Identifier Qui Parle

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.

C’est une fonctionnalité essentielle pour 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 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-latest",
        file={"content": f, "file_name": "reunion_equipe.mp3"},
        diarize=True,
        timestamp_granularities=["segment"]
    )

La diarisation fonctionne de pair avec les timestamps par segment. Chaque segment retourné contient désormais un champ speaker identifiant le locuteur.

Exploiter les résultats

Affichage simple

for seg in response.segments:
    temps = f"{seg.start:.1f}s"
    print(f"[{temps}] {seg.speaker} : {seg.text}")

Résultat typique :

[0.0s] speaker_0 : Bonjour à tous, on commence la réunion hebdomadaire.
[3.2s] speaker_0 : Julien, tu veux commencer avec le point technique ?
[5.8s] speaker_1 : Oui, alors cette semaine on a finalisé la migration.
[12.4s] speaker_1 : Tout est déployé en production depuis mercredi.
[15.1s] speaker_2 : Super, et côté performances, on a vu une amélioration ?
[18.3s] speaker_1 : Oui, le temps de réponse a baissé de trente pour cent.

Les locuteurs sont identifiés comme speaker_0, speaker_1, speaker_2, etc. Le modèle ne connaît pas les noms — c’est à vous de mapper ces identifiants aux personnes réelles.

Mapper les locuteurs à des noms

# Mapping des identifiants aux noms réels
noms_locuteurs = {
    "speaker_0": "Marie (Manager)",
    "speaker_1": "Julien (Tech Lead)",
    "speaker_2": "Sarah (Product)",
}

for seg in response.segments:
    nom = noms_locuteurs.get(seg.speaker, seg.speaker)
    minutes = int(seg.start // 60)
    secondes = int(seg.start % 60)
    print(f"[{minutes:02d}:{secondes:02d}] {nom}")
    print(f"  {seg.text}\n")

Compter le temps de parole par locuteur

from collections import defaultdict

temps_parole = defaultdict(float)

for seg in response.segments:
    duree = seg.end - seg.start
    temps_parole[seg.speaker] += 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}%)")

Résultat :

=== 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 générer un compte-rendu exploitable :

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, seg.speaker)

        if seg.speaker != locuteur_actuel:
            locuteur_actuel = seg.speaker
            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é")

Cas d’usage avancé : réunion complète

Voici un script complet pour transcrire et analyser une réunion :

import os
import json
from collections import defaultdict
from mistralai 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-latest",
            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]["duree"] += seg.end - seg.start
        stats[seg.speaker]["segments"] += 1
        stats[seg.speaker]["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_0": "Marie",
    "speaker_1": "Julien",
    "speaker_2": "Sarah"
})

print(json.dumps(resultat["statistiques"], indent=2, ensure_ascii=False))

Limites de la diarisation

  • Pas de reconnaissance d’identité — Le modèle distingue les voix mais ne sait pas qui est qui. Le mapping est à votre charge.
  • Nombre de locuteurs — Le modèle détecte automatiquement le nombre. Pas besoin de le spécifier.
  • Voix similaires — Deux voix très proches (par exemple, deux personnes du même sexe et du même âge) peuvent parfois être confondues.
  • Pas disponible en temps réel — La diarisation est uniquement supportée en mode offline (Voxtral Mini Transcribe V2), pas avec Voxtral Realtime.

Points clés à retenir

  • Activez la diarisation avec diarize=True — un seul paramètre
  • Combinez avec timestamp_granularities=["segment"] pour des résultats structurés
  • Chaque segment contient un champ speaker (speaker_0, speaker_1, etc.)
  • Mappez les identifiants aux noms réels dans votre code
  • Exploitez les statistiques : temps de parole, nombre d’interventions, nombre de mots
  • La diarisation n’est disponible qu’en mode offline