Que fait transcription-résumé ?
Audio en entrée, transcription en sortie, et éventuellement un compte-rendu de réunion. Le chemin par défaut s'exécute entièrement sur la machine de l'utilisateur.
Deux choses rendent cette compétence différente d'un simple appel à Whisper, et toutes deux sont issues d'une journée de mesures plutôt que de conception :
- Whisper invente des paroles sur le silence, et un modèle plus grand n'y change rien. Sur un appel réel avec 40 secondes de silence d'attente, le modèle
large-v3a produit un segment de 18 secondes composé d'un mot répété 55 fois. Cette compétence filtre ces segments et les conserve, avec les métriques qui ont justifié leur rejet, dans un fichier annexe pour que la décision soit vérifiable. - Nettoyer l'audio est plus efficace que de choisir un modèle. La normalisation et la suppression du silence ont réduit de 2 à 0 le nombre de segments inventés et ont corrigé un mot que
large-v3etturboavaient tous deux mal transcrit. Cette fonctionnalité est activée par défaut.
Configuration
ffmpeg et ffprobe doivent être disponibles dans le PATH — macOS brew install ffmpeg, Windows winget install Gyan.FFmpeg, Linux apt install ffmpeg.
Tout le reste provient de la bibliothèque standard. Le backend apporte sa propre bibliothèque, et une seule à la fois, donc choisissez-la avec uv run --with :
| backend | plateforme | spécification --with |
|---|---|---|
mlx-whisper — par défaut sur Apple Silicon | macOS arm64 uniquement | 'mlx-whisper>=0.4.2' |
faster-whisper — par défaut ailleurs | mac / Windows / Linux | 'faster-whisper>=1.2' |
parakeet — optionnel | Apple Silicon vérifié | 'parakeet-mlx' |
groq, openai, elevenlabs, gemini — optionnel, réseau | toute plateforme | rien à installer |
Si la bibliothèque est manquante, l'erreur affiche la commande exacte. Ne devinez pas.
Exécution
Déclarez l'assistant en haut de chaque bloc de commandes — l'état du shell ne persiste pas entre les appels d'outil :
ts() {
UV="$(command -v uv || ls "$HOME/.local/bin/uv" "$HOME/.cargo/bin/uv" \
/opt/homebrew/bin/uv /usr/local/bin/uv 2>/dev/null | head -1)"
"$UV" run --with "$1" --script "$HOME/.claude/skills/transcribe-summarize/scripts/transcribe.py" "${@:2}"
}
# Apple Silicon
ts 'mlx-whisper>=0.4.2' meeting.m4a
# Windows / Linux
ts 'faster-whisper>=1.2' meeting.m4a --backend faster-whisper
# Orienter le décodeur vers un jargon connu et corriger un nom qu'il se trompe toujours
ts 'mlx-whisper>=0.4.2' meeting.m4a --prompt 'Terragrunt, EKS' --replace 'north wind=Northwind'
Utilisez le chemin absolu. Un chemin relatif ne fonctionne que si le répertoire de travail se trouve être celui de la compétence.
Contrat de sortie
<dossier_sortie>/
<nom_base>.md la transcription, avec des ancres [hh:mm:ss]
<nom_base>-artefacts/
<nom_base>.srt sous-titres
<nom_base>.json chaque segment, y compris les supprimés
<nom_base>.run.json backend, modèle, décalages de coupe, compteur du filtre
--json affiche ces chemins au format JSON sur la sortie standard. Signalez les chemins absolus à l'utilisateur ; un fichier qu'il doit chercher n'est pas un résultat.
Les horodatages sont basés sur l'horloge de l'enregistrement original. La coupe raccourcit l'audio pendant le décodage, et les décalages sont reportés avant que quoi que ce soit ne soit écrit, de sorte que le fichier .srt corresponde au fichier original de l'utilisateur.
La règle réseau — énoncez-la, puis respectez-la
Un backend réseau est accessible uniquement lorsque l'utilisateur le demande explicitement dans cette invocation. Jamais en solution de repli, jamais après l'échec d'un backend local, jamais parce que ce serait plus rapide. --backend auto ne peut pas en atteindre un ; il n'y a aucun chemin de code.
Avant que quoi que ce soit ne soit envoyé, l'outil affiche le fournisseur, le point d'accès, la taille du fichier, la durée et le coût estimé, puis attend. Utilisez --dry-run pour montrer à l'utilisateur ce bloc avant de vous engager. Dans une session non interactive, un téléchargement non confirmé est refusé.
Les clés proviennent de l'environnement, une variable par backend, et nulle part ailleurs. N'acceptez jamais une clé comme option, ne l'affichez jamais, ne l'écrivez jamais dans un fichier.
| backend | clé | modèle | $/heure | diarisation |
|---|---|---|---|---|
groq | GROQ_API_KEY | whisper-large-v3-turbo | 0.04 | non |
groq | GROQ_API_KEY | whisper-large-v3 (par défaut) | 0.111 | non |
elevenlabs | ELEVENLABS_API_KEY | scribe_v2 | 0.22 | jusqu'à 32 |
gemini | GEMINI_API_KEY | gemini-3.5-transcribe | 0.306 | jusqu'à 3 |
openai | OPENAI_API_KEY | whisper-1 | 0.36 | non |
--backend gemini — les deux choses à dire avant de l'exécuter
ts '' /chemin/vers/audio.m4a --backend gemini --dry-run # affiche le bloc, n'envoie rien
ts '' /chemin/vers/audio.m4a --backend gemini # puis exécutez-le
Rien à installer — c'est http.client de la bibliothèque standard, donc l'argument --with est vide. Deux faits doivent figurer dans la phrase que vous dites à l'utilisateur avant de l'exécuter, car aucun n'est visible depuis la ligne de commande :
- L'audio est téléchargé deux fois. Gemini n'a pas de point d'accès unique : le fichier va d'abord vers l'API Files de Google, et la demande de transcription ne contient ensuite que l'URI renvoyé. Google conserve ce téléchargement pendant 48 heures. Le bloc de divulgation affiche ceci ; ne l'ignorez pas.
- 30 minutes est le plafond, pas l'heure que la page de Google annonce en premier. L'heure ne s'applique que sans horodatage des mots ni diarisation, et ce backend demande toujours les deux — sans décalages de mots, il n'y a pas d'horloge à reporter sur le fichier de l'utilisateur. Un enregistrement plus long est refusé avant le début du téléchargement.
N'offrez pas le mode smart de Gemini. Il n'est pas connecté, et c'est fait exprès. Si l'utilisateur le demande — c'est la fonction phare du modèle, et elle supprime les mots de remplissage et réécrit les autocorrections parlées — la réponse est que l'API de Google refuse les horodatages des mots et les étiquettes des locuteurs en même temps, donc cela ne produirait ni fichier .srt ni locuteurs, et cela modifierait les mots. La suppression des mots de remplissage se fait à l'étape des notes à la place, où elle est étiquetée comme un résumé et vérifiée. Voir references/backends.md.
Le filtre qualité
Activé par défaut. Les segments supprimés restent dans le fichier .json avec la raison et les métriques ; ils sont retirés des fichiers .md et .srt. --guard mark les conserve en ligne avec un indicateur, --guard off le désactive.
Il ne s'applique pas à tous les backends. Les règles strictes lisent les métriques du décodeur Whisper. faster-whisper, Groq et OpenAI renvoient tous les trois, donc le filtre est le même code. Parakeet, ElevenLabs et Gemini n'en renvoient aucun et se rabattent uniquement sur les règles indépendantes du backend — l'outil le dit au moment de l'exécution. Voir references/backends.md.
Si l'utilisateur demande pourquoi une partie de la transcription est manquante, lisez le fichier .json : la réponse s'y trouve avec les chiffres qui l'ont causée.
À partir d'une transcription que vous possédez déjà
La plupart des réunions sont déjà transcrites par la plateforme, et il n'y a souvent pas d'audio du tout. Teams et Zoom fournissent un fichier .vtt (parfois .srt), et il est illisible en tant que prose : un repère toutes les quelques secondes, des phrases coupées entre les repères, le nom du locuteur répété sur chaque ligne, des codes temporels partout.
C'est un point d'entrée pris en charge. Rien n'est retranscrit — le chemin audio est complètement ignoré :
"$HOME/.claude/skills/transcribe-summarize/scripts/normalise_transcript.py" meeting.vtt
# -> meeting.md, repères regroupés en paragraphes, locuteurs attribués, une ancre par paragraphe
Puis continuez exactement comme si la transcription avait été décodée ici : lisez-la, demandez les faits de la réunion et écrivez les notes dans references/notes-register.md.
--no-speakers supprime l'attribution si les étiquettes source sont erronées ou indésirables.
C'est une conversion de format et rien de plus. Aucun mot n'est modifié, aucun mot de remplissage supprimé, aucune grammaire corrigée. Décider ce qui a été dit est l'étape de résumé et elle a ses propres règles. Si un utilisateur vous remet une transcription désordonnée et vous demande de « l'arranger », normalisez le format ici et faites le jugement dans les notes — ne réécrivez pas discrètement les mots de quelqu'un dans un document qui est censé les enregistrer.
Les locuteurs sont conservés dans la transcription et interdits dans les notes, ce qui n'est pas une contradiction : une transcription est un artefact de travail et peut porter une provenance ; les notes sont un enregistrement et ne le peuvent pas. notes_check.py rejettera toujours une étiquette Speaker 2 brute dans les notes.
Si quelqu'un passe un fichier .vtt à transcribe.py, le script s'arrête et redirige ici plutôt que d'essayer de le décoder.
Fichiers vidéo
.mp4 est ce qu'un téléchargement Teams ou Zoom est réellement, et cela fonctionne directement — pas besoin d'extraire d'abord l'audio. Testé et fonctionnel : .mp4, .mov, .mkv, .webm, plus tous les conteneurs audio (.m4a, .mp3, .wav, .flac, .opus). ffmpeg extrait la piste audio ; la vidéo est ignorée et jamais téléchargée.
Un fichier sans piste audio — un enregistrement d'écran muet — est refusé dès le départ, avant qu'un backend réseau ne lui soit proposé. Cette vérification a lieu avant la porte de sortie précisément pour qu'une vidéo silencieuse ne soit jamais téléchargée et facturée.
La relecture est une étape, pas un supplément optionnel
La sortie de la transcription est un brouillon. Dites-le, et faites-la vérifier par l'utilisateur.
La transcription automatique est confiante à tort d'une manière qu'aucun seuil ne détecte. Mesuré sur de l'audio réel dans ce projet : le modèle large-v3 a silencieusement supprimé la correction parlée d'un chiffre par un locuteur, et tous les backends ont entendu « board pack » comme « board packs up ». Les deux lectures sont parfaitement fluides. Le filtre qualité détecte les inventions sur le silence ; il ne peut pas détecter une méprise plausible, et vous non plus en lisant la sortie.
Donc le flux de travail est transcrire → l'utilisateur vérifie → puis les notes :
- Le fichier
.mdde la transcription se termine par une section « À vérifier » — horodatages classés où une réécoute est la plus susceptible de rapporter (segments les moins fiables, tout ce qui contient des chiffres). Indiquez-la explicitement à l'utilisateur. - Les noms, chiffres et dates sont ce qu'il faut vérifier. Ce sont les premières choses à se dégrader et les plus coûteuses à se tromper dans un document officiel.
- Les corrections vont dans le fichier
.md, à la main ou avec--replacelors d'une nouvelle exécution. Ensuite, régénérez tout ce qui en est issu.
Ne présentez jamais une transcription ou un ensemble de notes comme vérifiés. Vous n'avez pas entendu l'audio ; l'utilisateur l'a fait. Dites ce qui a été décodé, dites où c'est le moins certain, et laissez-le confirmer.
Édition et régénération
Le fichier .md est la source. Modifiez-le, puis reconstruisez le PDF — sans retranscription :
"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notes.md
"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py" notes.md
Relancez le vérificateur après chaque modification. L'édition manuelle est exactement le moment où une fuite de provenance ou une ligne « prochaines étapes » inférée est introduite.
Rédaction des notes
Lorsque l'utilisateur souhaite un compte-rendu de réunion plutôt qu'une transcription, c'est vous qui le rédigez — lisez la transcription et produisez le document vous-même. Il existe un script scripts/summarize.py de secours qui appelle un modèle réseau, mais il est destiné à exécuter ce pipeline en dehors d'une session Claude Code ; à l'intérieur d'une session, c'est un téléchargement inutile.
Demandez d'abord — trois choses que l'audio ne peut pas vous dire
Presque aucun backend ici n'identifie les locuteurs. Chaque moteur Whisper et Parakeet ne renvoie aucun champ de locuteur. Deux exceptions : --backend elevenlabs (Scribe) diarise jusqu'à 32 locuteurs, et --backend gemini jusqu'à 3. Les deux renvoient des étiquettes positionnelles plutôt que des noms — speaker_0 et spk:0 respectivement, les formats mesurés à partir de réponses en direct les 2026-09-04 et 2026-09-06 — et le registre interdit une étiquette brute dans un document de notes, dans l'un ou l'autre format. Donc ils vous disent combien de personnes ont parlé et quelles lignes vont ensemble ; ils ne peuvent toujours pas vous dire qui. L'attribution vient de l'utilisateur dans tous les cas ; la diarisation rend simplement la cartographie possible à partir de la transcription plutôt que de la mémoire.
Avant d'écrire les notes, demandez :
- Qui était présent. Sans noms, rédigez les notes sans aucune attribution — impersonnel, « il a été déclaré que… ». N'inventez jamais un nom, un rôle ou une étiquette positionnelle. Si la transcription rend évident qui a parlé (quelqu'un est appelé par son nom), vous pouvez l'attribuer ; l'ambiguïté signifie pas de nom.
- La date de la réunion. La date d'aujourd'hui est une valeur par défaut, pas un fait. L'audio est souvent retranscrit des jours après la conversation, et une date erronée sur un document officiel est pire que pas de date. Confirmez-la.
- Ce qu'était la réunion — un titre dans les propres termes des participants.
Demandez les trois choses en un seul message, pas un par un, et proposez la date d'aujourd'hui comme valeur par défaut afin que l'utilisateur n'ait qu'à la corriger. Si l'utilisateur refuse de fournir les participants, procédez sans attribution et dites que c'est ce que vous avez fait.
Avant d'écrire une seule ligne, lisez references/notes-register.md. Il est court et constitue l'ensemble des spécifications. Ce ne sont pas des conseils de style ; les règles qu'il contient ont été apprises en voyant des sections supprimées à la main d'un document réel, et les raisons sont juridiques plutôt qu'esthétiques.
Les deux règles qu'il active :
- Résumé factuel uniquement. Ce qui a été dit. Pas d'analyse, pas d'implications, pas de « prochaines étapes », pas de « ce qui n'a pas été couvert », pas de comblement de lacune.
- Il se lit comme un compte-rendu de protocole rédigé par quelqu'un qui était présent. Il ne mentionne jamais un enregistrement, une transcription, un audio, un modèle ou un artefact du décodeur — pas de codes temporels, pas d'étiquettes
Speaker 2.
Ensuite, vérifiez-le, à chaque fois — le registre ne s'applique pas tout seul et un modèle invité à résumer ajoutera une analyse de son propre chef :
"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notes.md
Code de sortie 0 signifie propre ; code de sortie 1 liste fichier:ligne pour chaque violation. Corrigez-les avant de remettre le document. Exécutez-le également sur le PDF — une fuite qui n'existe que dans le fichier rendu compte quand même.
Les conseils sont les bienvenus, mais après les notes et en dehors du document. Proposez votre lecture en conversation, ou écrivez-la dans un fichier séparé. Ne l'annexez jamais.
Le PDF
"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py" notes.md
Headless Chrome, Chromium ou Edge, détecté automatiquement. Si aucun n'est installé, le script écrit le HTML, le dit et quitte avec le code 0 — le HTML reste un résultat livrable.
Provenance
Le décodage local utilise mlx-whisper (MLX) ou faster-whisper (CTranslate2) ; la préparation audio est ffmpeg. Les backends réseau appellent les points d'accès de transcription de Groq et OpenAI directement via la bibliothèque standard urllib — pas de SDK propriétaire.
Garder la provenance hors d'un document de notes change ce que le document dit, pas ce qui s'est passé. Enregistrer un appel sans consentement est une infraction pénale.
…
[tronqué pour la traduction]