Invitez et gagnez

Fonctionnement des récompenses

Partagez votre lien. Lorsqu’un ami s’inscrit avec ce lien et recharge son solde, vous recevez la récompense affichée sur ses recharges ultérieures.

Transcription de réunions dans Gemini 3.5 Transcribe : intervenants, horodatages et vocabulaire en pratique

Un guide pratique pour transformer l'enregistrement d'une réunion de 45 minutes en transcription vérifiée ou en base de sous-titres avec Gemini 3.5 Transcribe : sélection des options de configuration, découpage adéquat des fichiers audio longs, assemblage des horodatages par mot via le SDK Python et exécution du contrôle qualité manuel.

Sommaire
Transcription de réunions dans Gemini 3.5 Transcribe : intervenants, horodatages et vocabulaire en pratique

Transformer l’enregistrement audio d’une réunion professionnelle en un document de travail fiable exige des choix d’architecture délibérés lors de la configuration des paramètres de reconnaissance vocale. Dans Gemini 3.5 Transcribe, il n’existe aucun mode universel « tout-en-un » : la normalisation éditoriale approfondie (smart), la séparation des intervenants (diarization_mode), la grille temporelle mot à mot (timestamp_granularities) et les lexiques propres à un domaine (custom_vocabulary) constituent des fonctionnalités d’API mutuellement isolées.

Tout pipeline de traitement doit commencer par définir son objectif final : avez-vous besoin d’un bloc de texte concis et fluide pour une lecture rapide, d’une base structurée pour une analyse en aval, ou d’un ensemble de repères temporels précis pour le montage vidéo ? Tenter de combiner des paramètres mutuellement incompatibles entraîne des erreurs de validation de schéma avant même le traitement de la requête.

Voici un guide pratique complet de bout en bout : comment préparer un enregistrement de 45 minutes de deux intervenants truffé de terminologie technique, respecter les limites documentées de l’API, traiter les deux parties à l’aide du SDK Python officiel, assembler des structures de données préliminaires, les vérifier et les corriger par rapport à l’enregistrement audio, puis transmettre des copies vérifiées à un rédacteur de compte rendu ou à un monteur de sous-titres.


Contraintes architecturales et conflits de paramètres

Pour illustrer ce pipeline, prenons l’exemple d’une session de travail classique : Alexey (Tech Lead) et Mikhail (Product Manager) passent 45 minutes à discuter de l’architecture de migration et du déploiement de services associés aux noms de projets internes DataPulse et CloudForge. La conversation comporte du jargon technique, des emprunts lexicaux, des interruptions rapides et des hésitations.

Lors de la conception d’une intégration avec Gemini 3.5 Transcribe, trois règles strictes doivent être prises en compte :

  • Incompatibilité entre vocabulaire personnalisé et métadonnées structurelles : Le paramètre custom_vocabulary (qui accepte jusqu’à 1 000 expressions, avec une recommandation pratique allant jusqu’à 100) ne peut pas être transmis conjointement avec diarization_mode ou timestamp_granularities. Vous devez opérer un arbitrage de conception : soit confier au modèle la transcription exacte des noms de marques spécialisés (en renonçant à l’étiquetage automatique des intervenants et aux horodatages), soit demander l’alignement précis des intervenants et du temps tout en validant la terminologie spécialisée lors de la phase de post-traitement.
  • Incompatibilité entre le mode Smart et l’alignement temporel : Le mode smart applique une normalisation de la parole : il supprime les tics de langage, les bégaiements et les faux départs tout en restructurant la syntaxe grammaticale. Les jetons textuels étant supprimés, fusionnés ou réorganisés, le modèle ne peut pas faire correspondre le texte obtenu à l’axe temporel du flux audio sous-jacent. Par conséquent, l’attribution des intervenants et les horodatages au mot près ne sont pas disponibles en mode smart. De plus, ce mode renvoie uniquement du texte continu non annoté (output_text) et n’extrait pas automatiquement de liste de tâches ou de points d’action (Action Items).
  • Limite de 30 minutes pour les métadonnées avancées : Les requêtes de transcription standard sans annotations détaillées acceptent des fichiers audio d’une durée maximale de 60 minutes. En revanche, dès que vous activez diarization_mode ou timestamp_granularities, la durée maximale documentée par fichier descend à 30 minutes. La soumission de fichiers dépassant ce seuil sort des spécifications prises en charge par l’API et ne doit pas être tentée ; tout enregistrement long doit être découpé côté client avant son envoi.

Préparation et découpage de l’enregistrement de 45 minutes

Pour extraire la diarisation et les horodatages au niveau des mots à partir d’un enregistrement de 45 minutes, le fichier source doit être scindé en segments strictement inférieurs à 30 minutes. Dans le cadre de notre scénario hypothétique, nous divisons l’enregistrement en deux segments :

  • Partie 1 : 00:00–25:00 (considérée conventionnellement comme durant exactement 1500.0 secondes) ;
  • Partie 2 : 25:00–45:00 (les 20 minutes restantes, soit 1200.0 secondes).

La frontière de 1500.0 secondes est choisie ici dans le seul but de simplifier les calculs de cet exemple. Dans un environnement de production, les découpes doivent être positionnées lors de pauses naturelles entre les tours de parole, et la durée physique exacte du premier segment doit impérativement être extraite des métadonnées du fichier multimédia à l’aide d’un outil d’inspection tel que ffprobe.

Le découpage de l’audio introduit deux difficultés majeures en matière de continuité :

  1. Réinitialisation des horodatages dans le second segment : L’API traite le second segment comme un fichier totalement indépendant, réinitialisant ses décalages temporels internes par mot pour démarrer à 0.000s. Pour reconstituer une chronologie continue sur l’ensemble de la réunion, la durée réelle du premier segment doit être ajoutée programmatiquement à chaque horodatage de mot extrait du second segment.
  2. Portée locale des étiquettes d’intervenants : Le modèle attribue les identifiants d’intervenants tels que spk_1 et spk_2 de manière isolée au sein de chaque appel d’API. L’intervenant désigné comme spk_1 dans le premier segment peut ainsi recevoir l’étiquette spk_2 dans le second. Fusionner des étiquettes techniques identiques provenant de requêtes distinctes sans vérification préalable fausserait l’attribution des propos. Chaque segment requiert une table de correspondance indépendante, validée face aux participants réels par une écoute ponctuelle.

Variantes de configuration selon les cas d’usage

Les paramètres de transcription sont configurés dans le champ transcription_config du dictionnaire generation_config. Voici les configurations de référence adaptées aux différents besoins opérationnels.

Pour un compte rendu de réunion fluide et normalisé, sans repères d’intervenants ni horodatages (adapté aux fichiers complets de moins de 60 minutes) :

generation_config = {
    "transcription_config": {
        "mode": "smart"
    }
}

Pour les échanges articulés autour de noms de produits de niche ou de jargon propriétaire où l’orthographe exacte est prioritaire :

generation_config = {
    "transcription_config": {
        "custom_vocabulary": ["DataPulse", "CloudForge", "ClickHouse", "gRPC"]
    }
}

Pour l’établissement de protocoles de réunion et de grilles temporelles de sous-titres (soumis à la limite de 30 minutes d’audio) :

generation_config = {
    "transcription_config": {
        "mode": {
            "type": "verbatim",
            "diarization_mode": "speaker",
            "timestamp_granularities": ["word"],
        }
    }
}

Pipeline complet avec le SDK Python : traitement en deux parties

Ce script est exécuté avant toute écoute de l’audio ou vérification manuelle. Il téléverse les deux segments via la Files API, envoie les requêtes à l’Interactions API, extrait les annotations mot à mot, valide les horodatages et les identifiants d’intervenants, décale la chronologie de la seconde partie de 1500.0 secondes et génère des fichiers de travail préliminaires :

  • meeting_transcript.txt — un premier jet de transcription du dialogue en UTF-8, structuré par tours de parole ;
  • word_timestamps.json — un tableau brut d’horodatages continus au niveau des mots, destiné au découpage des sous-titres.

Les fichiers générés par ce script constituent explicitement des ébauches non vérifiées, et non des documents finaux. L’opérateur doit écouter l’enregistrement, associer les jetons techniques d’intervenants aux voix réelles (speaker mapping), contrôler l’exactitude de la terminologie technique (terms) et vérifier les décalages temporels (time offsets). Après cet audit, il convient de mettre à jour les paramètres de configuration dans le code et de relancer le pipeline, ou d’éditer directement les fichiers texte générés. Les copies finales ne doivent être remises à un rédacteur de compte rendu ou à un monteur de sous-titres qu’à l’issue de ce protocole de vérification.

Le script produit des structures de données intermédiaires pour les analystes et les monteurs vidéo ; il ne génère pas automatiquement de liste de tâches ni de fichiers de sous-titres formatés en .srt. Les noms des intervenants et la limite de 1500.0 secondes sont spécifiés ici à titre de démonstration dans le cadre de ce scénario hypothétique.

import json
from google import genai

client = genai.Client()

audio_part1 = client.files.upload(file="meeting_part1.mp3")
audio_part2 = client.files.upload(file="meeting_part2.mp3")

transcription_mode_config = {
    "transcription_config": {
        "mode": {
            "type": "verbatim",
            "diarization_mode": "speaker",
            "timestamp_granularities": ["word"],
        }
    }
}

interaction_part1 = client.interactions.create(
    model="gemini-3.5-transcribe",
    input=[
        {
            "type": "audio",
            "uri": audio_part1.uri,
            "mime_type": audio_part1.mime_type,
        }
    ],
    generation_config=transcription_mode_config,
)

interaction_part2 = client.interactions.create(
    model="gemini-3.5-transcribe",
    input=[
        {
            "type": "audio",
            "uri": audio_part2.uri,
            "mime_type": audio_part2.mime_type,
        }
    ],
    generation_config=transcription_mode_config,
)

def extract_word_annotations(interaction):
    words = []
    for step in getattr(interaction, "steps", []) or []:
        for content in getattr(step, "content", []) or []:
            for annotation in getattr(content, "annotations", []) or []:
                if getattr(annotation, "type", None) == "word_info":
                    words.append(annotation)
    return words

def parse_offset_seconds(offset_val, word_text):
    if offset_val is None or offset_val == "":
        raise ValueError(f"Отсутствует таймкод для слова '{word_text}'. Требуется проверка аудиозаписи.")
    val_str = str(offset_val)
    if val_str.endswith("s"):
        val_str = val_str[:-1]
    try:
        return float(val_str)
    except ValueError:
        raise ValueError(f"Некорректный формат таймкода '{offset_val}' для слова '{word_text}'. Требуется проверка аудиозаписи.")

words_part1 = extract_word_annotations(interaction_part1)
words_part2 = extract_word_annotations(interaction_part2)

if not words_part1 or not words_part2:
    raise ValueError("Один из аудиосегментов не содержит пословных аннотаций. Пустой результат не может считаться успешным.")

part1_offset_seconds = 0.0
part2_offset_seconds = 1500.0

manual_mapping_part1 = {
    "spk_1": "Алексей",
    "spk_2": "Михаил",
}

manual_mapping_part2 = {
    "spk_1": "Михаил",
    "spk_2": "Алексей",
}

unified_word_stream = []

for word in words_part1:
    text = getattr(word, "text", "")
    raw_speaker = getattr(word, "speaker", None)
    if not raw_speaker or raw_speaker not in manual_mapping_part1:
        raise ValueError(f"Неизвестный спикер '{raw_speaker}' в части 1. Требуется ручная верификация по аудио.")
    resolved_speaker = manual_mapping_part1[raw_speaker]
    word_start = parse_offset_seconds(getattr(word, "start_offset", None), text) + part1_offset_seconds
    word_end = parse_offset_seconds(getattr(word, "end_offset", None), text) + part1_offset_seconds
    unified_word_stream.append({
        "text": text,
        "speaker": resolved_speaker,
        "start_seconds": word_start,
        "end_seconds": word_end,
        "part": 1,
    })

for word in words_part2:
    text = getattr(word, "text", "")
    raw_speaker = getattr(word, "speaker", None)
    if not raw_speaker or raw_speaker not in manual_mapping_part2:
        raise ValueError(f"Неизвестный спикер '{raw_speaker}' в части 2. Требуется ручная верификация по аудио.")
    resolved_speaker = manual_mapping_part2[raw_speaker]
    word_start = parse_offset_seconds(getattr(word, "start_offset", None), text) + part2_offset_seconds
    word_end = parse_offset_seconds(getattr(word, "end_offset", None), text) + part2_offset_seconds
    unified_word_stream.append({
        "text": text,
        "speaker": resolved_speaker,
        "start_seconds": word_start,
        "end_seconds": word_end,
        "part": 2,
    })

dialogue_turns = []
current_turn = None

for item in unified_word_stream:
    if current_turn is None or current_turn["speaker"] != item["speaker"] or current_turn["part"] != item["part"]:
        if current_turn is not None:
            dialogue_turns.append(current_turn)
        current_turn = {
            "part": item["part"],
            "speaker": item["speaker"],
            "start_seconds": item["start_seconds"],
            "end_seconds": item["end_seconds"],
            "words": [item["text"]],
        }
    else:
        current_turn["end_seconds"] = item["end_seconds"]
        current_turn["words"].append(item["text"])

if current_turn is not None:
    dialogue_turns.append(current_turn)

def format_timestamp(seconds):
    minutes = int(seconds // 60)
    remaining_seconds = seconds % 60
    return f"{minutes:02d}:{remaining_seconds:06.3f}"

with open("meeting_transcript.txt", "w", encoding="utf-8") as f_transcript:
    for turn in dialogue_turns:
        start_str = format_timestamp(turn["start_seconds"])
        end_str = format_timestamp(turn["end_seconds"])
        speech_text = " ".join(turn["words"])
        f_transcript.write(f"[{start_str} - {end_str}] {turn['speaker']}: {speech_text}\n")

with open("word_timestamps.json", "w", encoding="utf-8") as f_json:
    json.dump(unified_word_stream, f_json, ensure_ascii=False, indent=2)

Exemple illustratif de structure mot à mot et zones de contrôle

Voici une représentation synthétique du flux de données extrait lors d’une transition entre deux intervenants.

Note : Cet extrait sert exclusivement à illustrer la structure des objets retournés, et non de journal d’appel d’API réel. La qualité effective de la transcription dépend de l’acoustique, des microphones et de la clarté d’élocution.

[Строка 1] [spk_1] (0.100s -> 0.420s) Мы
[Строка 2] [spk_1] (0.450s -> 0.810s) переносим
[Строка 3] [spk_1] (0.830s -> 1.250s) ДатаПульс
[Строка 4] [spk_1] (1.300s -> 1.550s) на
[Строка 5] [spk_1] (1.600s -> 2.100s) CloudForge.
[Строка 6] [spk_1] (2.300s -> 2.600s) Да,
[Строка 7] [spk_2] (2.650s -> 2.900s) согласен,
[Строка 8] [spk_2] (2.950s -> 3.400s) э-э-э,
[Строка 9] [spk_2] (3.420s -> 3.900s) логично.

Points de contrôle critiques pour la révision manuelle :

  1. Association des étiquettes aux participants réels (Lignes 1–5 et 7–9) : Les identifiants spk_1 et spk_2 ne sont que des jetons vocaux positionnels arbitraires. Un réviseur doit écouter les premiers instants de chaque segment pour déterminer les identités véritables : par exemple, vérifier que dans la Partie 1, spk_1 correspond à Alexey et spk_2 à Mikhail.
  2. Passages de relais et brèves interjections (Ligne 6) : Le mot « Да, » est rattaché au tour de parole de spk_1. Dans une conversation dynamique, il peut en réalité s’agir d’une marque d’acquiescement ou d’une rétroaction vocale émise par l’interlocuteur (spk_2). Les frontières d’alternance des intervenants exigent des vérifications sonores ciblées.
  3. Transcription phonétique des marques non reconnues (Ligne 3) : Lorsque custom_vocabulary est désactivé, les noms techniques anglophones peuvent être transcrits phonétiquement en caractères cyrilliques (par exemple ДатаПульс). Le réviseur doit rectifier ces occurrences pour leur redonner leur graphie canonique : DataPulse.
  4. Interdiction des remplacements automatiques globaux à l’aveugle : Les corrections terminologiques doivent systématiquement être appliquées au cas par cas selon le contexte de la phrase. Une commande « rechercher et remplacer » globale sur l’ensemble du document risquerait de dénaturer des mots courants, des expressions ou des citations littérales présentant des similitudes phonétiques.
  5. Hésitations et tics de langage (Ligne 8) : En mode verbatim, les hésitations telles que « э-э-э » sont explicitement enregistrées. Dans un compte rendu rédigé propre, ces éléments sont nettoyés. En revanche, pour des pistes de sous-titres vidéo, leur minutage doit être préservé si le rythme d’affichage doit concorder avec l’articulation visible à l’écran.

Protocole de vérification ponctuelle avant transmission du matériel

Les données brutes générées par concaténation programmatique doivent faire l’objet d’un audit qualité rigoureux portant sur trois points de contrôle :

  1. Vérifier les intervenants et les limites des prises de parole : Écoutez 15 à 20 secondes d’audio sur 2 ou 3 transitions de tours de parole. Assurez-vous que des voix distinctes n’ont pas été fusionnées sous un même intervenant et que des monologues continus n’ont pas été fragmentés sous des étiquettes fantômes. D’après la documentation officielle, la diarisation pour les réunions réunissant trois personnes ou plus reste expérimentale et réclame une vigilance nettement accrue.
  2. Vérifier la terminologie, les valeurs numériques et les entités nommées : Dressez une liste de contrôle ciblée des noms de projets clés (DataPulse, CloudForge, numéros de versions, allocations budgétaires). Recherchez ces éléments dans le texte et confrontez directement les passages douteux à la piste audio, en évitant tout remplacement de masse inconsidéré.
  3. Vérifier la continuité temporelle aux frontières des segments : Comparez la première minute de la Partie 1 avec la jonction vers la Partie 2 (immédiatement après la limite des 25:00 / 1500.0 secondes). Veillez à ce que les horodatages du second segment prolongent harmonieusement la chronologie au lieu d’être réinitialisés à zéro.

Transmission aux étapes suivantes et conditions d’arrêt du processus

Les fichiers meeting_transcript.txt et word_timestamps.json créés par le script sont produits en amont de toute révision auditive et doivent être traités strictement comme des versions de travail préliminaires. Ne les transmettez jamais dans leur état brut. Appliquez d’abord le protocole de vérification manuelle, harmonisez l’identité des intervenants, la terminologie et les décalages temporels avec l’enregistrement sonore, rectifiez les données (en mettant à jour les paramètres du code puis en relançant le script, ou en modifiant directement les fichiers texte), et ne livrez des copies validées aux équipes en aval qu’ensuite :

  • Pour les rédacteurs de compte rendu ou les modèles de résumé (Note Taker / LLM Summarization) : Fournissez une copie vérifiée de la transcription de réunion — un document relu, assorti d’une attribution confirmée des intervenants et d’une terminologie technique normalisée. Ce document sert de socle à la rédaction de synthèses et de relevés de décisions, sans constituer pour autant une liste de points d’action immédiatement exploitable.
  • Pour les monteurs vidéo ou spécialistes du sous-titrage (Caption Editor) : Fournissez une copie vérifiée du tableau des horodatages par mot — une séquence validée de mots associés à des repères temporels continus. Les règles typographiques aval (durée d’exposition à l’écran, longueur maximale de ligne, vitesse de lecture) dépendent de la plateforme vidéo et de la langue cibles, après quoi un outil de conversion dédié génère les fichiers de sous-titres définitifs en .srt ou .vtt.

Conditions d’arrêt du processus (Stop Conditions)

Interrompez le flux de travail et ne transmettez aucun fichier aux équipes en aval si l’une des conditions suivantes est vérifiée :

  1. Un fichier audio d’une durée supérieure à 30 minutes a été soumis pour diarisation ou horodatage par mot sans découpage préalable (ceci dépasse les limites documentées de l’API et ne doit pas être envoyé).
  2. Les fichiers préliminaires bruts (meeting_transcript.txt ou word_timestamps.json) ont été remis aux utilisateurs finaux directement après l’exécution du script, sans écoute ponctuelle ni révision manuelle.
  3. Les étiquettes d’intervenants du second segment ont été mises en correspondance à l’aveugle d’après les numéros du premier segment sans vérification audio, ou des identifiants d’intervenants non résolus subsistent dans le jeu de données.
  4. La durée physique effective du premier segment n’a pas été additionnée à la chronologie du second segment, ou des horodatages de mots manquants sont détectés.
  5. Des règles de remplacement automatique global ont été appliquées à la terminologie sans validation contextuelle, ou des paramètres de configuration et données chiffrées critiques n’ont pas été vérifiés par rapport à l’enregistrement.

Prêt à optimiser votre workflow LLM ?

Connectez vos modèles via une API unique, gérez les clés et maîtrisez vos dépenses d’IA.

Commencer gratuitement