Communitygithub.com

kevin-burns/transcribe-summarize

Claude Code / Codex 向けのローカルファースト会議文字起こしスキル。お使いのマシン上で Whisper を実行し(macOS、Windows、Linux 対応)、Whisper が無音部分に生成する発話をフィルタリングし、結果を事実に基づく議事録および PDF として出力します。ホスト型 API はオプトイン方式で、送信前に送信内容とコストを開示します。

transcribe-summarize とは?

このスキルは、Claude Code や Codex などの AI エージェント向けに設計された、ローカルファーストの会議文字起こし・要約ツールです。ユーザーのマシン上で Whisper モデル(mlx-whisper または faster-whisper)を実行し、音声ファイルから高精度な文字起こしを生成します。特筆すべき点として、Whisper が無音部分に誤って生成する「発話の幻覚」を検出・フィルタリングする品質ガード機能が組み込まれており、後から監査可能な形で抑制されたセグメントとその理由を JSON サイドカーに記録します。また、オプションとして Groq、OpenAI、ElevenLabs、Gemini などのネットワーク API バックエンドも利用可能で、送信前にプロバイダ、エンドポイント、ファイルサイズ、推定コストを表示し、ユーザーの明示的な確認を得てから送信します。出力は Markdown 形式の文字起こし(タイムスタンプ付き)、SRT 字幕、JSON 形式の詳細セグメントデータ、そして議事録と PDF です。プラットフォームは macOS(Apple Silicon 含む)、Windows、Linux をサポートし、ffmpeg のみ事前インストールが必要です。

対応✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add kevin-burns/transcribe-summarize

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

transcribe-summarize は何をしますか?

音声を入力として、文字起こし、そしてオプションで議事録を出力します。デフォルトのパスはすべてユーザーのマシン上で完結します。

このツールを自前で Whisper を呼び出すのと区別する二つの点があり、どちらも設計ではなく計測の一日から生まれました。

  1. Whisper は無音部分に発話をでっち上げ、モデルを大きくしても改善しません。 実際の40秒の接続無音を含む通話で、large-v3 は18秒間にわたって同一単語を55回繰り返すセグメントを生成しました。本スキルはこれらをフィルタリングして除去し、除去されたセグメントとその理由、指標をサイドカーファイルに保持することで、判断の監査を可能にします。
  2. 音声のクリーニングはモデル選択に勝ります。 ノーマライズと無音トリミングにより、でっち上げセグメントが2つから0に減り、large-v3 と turbo の両方が誤認識していた単語を修正しました。これはデフォルトで有効です。

セットアップ

ffmpeg と ffprobe が PATH 上にある必要があります — macOS では brew install ffmpeg、Windows では winget install Gyan.FFmpeg、Linux では apt install ffmpeg。

その他はすべて標準ライブラリで動作します。バックエンドはそれぞれ独自のライブラリを必要とし、選択された一つだけが使用されるため、uv run --with で選択します。

バックエンドプラットフォーム--with 指定
mlx-whisper — Apple Silicon 上のデフォルトmacOS arm64 のみ'mlx-whisper>=0.4.2'
faster-whisper — その他の環境のデフォルトmac / Windows / Linux'faster-whisper>=1.2'
parakeet — オプトインApple Silicon 確認済み'parakeet-mlx'
groq, openai, elevenlabs, gemini — オプトイン、ネットワーク任意インストール不要

ライブラリがない場合、エラーは正確なコマンドを表示します。推測する必要はありません。

実行方法

すべてのコマンドブロックの先頭でヘルパーを宣言してください — シェルの状態はツール呼び出し間で保持されません。

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

# 既知の専門用語にデコーダを偏らせ、常に誤認識される名前を修正
ts 'mlx-whisper>=0.4.2' meeting.m4a --prompt 'Terragrunt, EKS' --replace 'north wind=Northwind'

絶対パスを使用してください。相対パスは、カレントワーキングディレクトリがたまたまスキルディレクトリである場合にのみ解決されます。

出力構成

<outdir>/
  <stem>.md                    文字起こし、[hh:mm:ss] アンカー付き
  <stem>-artifacts/
      <stem>.srt               字幕
      <stem>.json               すべてのセグメント(抑制されたものを含む)
      <stem>.run.json           バックエンド、モデル、トリムオフセット、ガードカウント

--json はこれらのパスを JSON として stdout に出力します。ユーザーには絶対パスを報告してください。ユーザーが探さなければならないファイルは成果物とは言えません。

タイムスタンプは元の録音のクロックに基づいています。 トリミングはデコード中に音声を短縮しますが、オフセットは何かを書き込む前にマッピングし直されるため、.srt はユーザーのファイルと同期します。

ネットワークルール — 明示し、それに従う

ネットワークバックエンドは、その呼び出しでユーザーが明示的に要求した場合にのみ使用可能です。フォールバックとして、ローカルバックエンドが失敗した後、または高速だからという理由で使用されることは決してありません。--backend auto はネットワークバックエンドに到達できません。そのようなコードパスは存在しません。

何かが送信される前に、ツールはプロバイダ、エンドポイント、ファイルサイズ、期間、推定コストを表示し、待機します。--dry-run を使用して、コミット前にそのブロックをユーザーに表示します。非インタラクティブセッションでは、未確認のアップロードは拒否されます。

APIキーは環境変数から取得され、バックエンドごとに1つの変数で、それ以外の場所から取得されることはありません。フラグとして受け入れ、エコーし、ファイルに書き込むことは決してしないでください。

バックエンドキーモデル$/時間話者分離
groqGROQ_API_KEYwhisper-large-v3-turbo0.04なし
groqGROQ_API_KEYwhisper-large-v3(デフォルト)0.111なし
elevenlabsELEVENLABS_API_KEYscribe_v20.22最大32
geminiGEMINI_API_KEYgemini-3.5-transcribe0.306最大3
openaiOPENAI_API_KEYwhisper-10.36なし

--backend gemini — 実行前に伝えるべき二つのこと

ts '' /path/to/audio.m4a --backend gemini --dry-run     # ブロックを表示、何も送信しない
ts '' /path/to/audio.m4a --backend gemini               # 実行

インストールは不要です — 標準ライブラリ http.client を使用するため、--with 引数は空です。実行前にユーザーに伝えるべき二つの事実があります。どちらもコマンドラインからは見えないためです。

  • 音声は二重にアップロードされます。 Gemini には単一送信エンドポイントがありません。ファイルはまず Google の Files API に送られ、文字起こしリクエストはその応答で返ってきた URI のみを保持します。Google はそのアップロードを48時間保持します。 開示ブロックにはこれが表示されます。読み飛ばさないでください。
  • 上限は30分であり、Google 自身のページで先頭に表示される時間ではありません。1時間の制限は、単語タイムスタンプや話者分離がない場合にのみ適用され、このバックエンドは常に両方を要求します — 単語オフセットがなければ、ユーザーのファイルにマッピングし直すためのクロックが存在しないからです。より長い録音は、アップロードが開始される前に拒否されます。

Gemini の smart モードを提供しないでください。意図的に配線されていません。 ユーザーがそれを要求した場合 — それはモデルの代表的な機能であり、フィラーワードを削除し、話者の言い直しを書き換えます — その答えは、Google の API がそれと同時に単語タイムスタンプと話者ラベルを拒否するため、.srt や話者が生成されず、単語が変更されるということです。フィラー除去は代わりに議事ノートのステップで行われ、そこで要約としてラベル付けされ、チェックされます。references/backends.md を参照してください。

品質ガード

デフォルトで有効です。抑制されたセグメントは理由と指標とともに .json に残りますが、.md と .srt からは削除されます。--guard mark はそれらをインラインでフラグ付きで保持し、--guard off は無効にします。

すべてのバックエンドに適用されるわけではありません。 強力なルールは Whisper デコーダの指標を読み取ります。faster-whisper、Groq、OpenAI は3つすべてを返すため、ガードは同じコードを使用します。Parakeet、ElevenLabs、Gemini はどれも返さないため、バックエンドに依存しないルールのみにフォールバックします — ツールは実行時にその旨を表示します。references/backends.md を参照してください。

文字起こしの一部が欠落している理由をユーザーが尋ねた場合は、.json を読んでください。その原因となった数値とともに答えがそこにあります。

既存の文字起こしから始める

ほとんどの会議はすでにプラットフォームによって文字起こしされており、音声が全くないこともよくあります。 Teams や Zoom は .vtt(場合によっては .srt)を提供しますが、散文としては読みにくいものです。数秒ごとにキューがあり、文がキューをまたがって分割され、話者の名前がすべての行で繰り返され、タイムコードが至る所にあります。

これはサポートされているエントリポイントです。再文字起こしは行われません — 音声パスは完全にスキップされます。

"$HOME/.claude/skills/transcribe-summarize/scripts/normalise_transcript.py" meeting.vtt
# -> meeting.md、キューは段落に結合され、話者が帰属され、段落ごとに1つのアンカー

その後、ここでデコードされた場合とまったく同じように進めます。それを読み、会議の事実を尋ね、references/notes-register.md の下に議事ノートを書きます。

--no-speakers は、ソースラベルが間違っているか不要な場合に話者帰属を削除します。

これはフォーマット変換であり、それ以上ではありません。 単語は変更されず、フィラーは削除されず、文法は修正されません。何が言われたかを判断することは要約ステップであり、それ自体のルールがあります。ユーザーが乱雑な文字起こしを渡して「整理して」と依頼した場合、ここでフォーマットを正規化し、判断は議事ノートで行ってください — 記録することを目的とした文書内で、誰かの言葉を静かに書き換えないでください。

話者は文字起こしには保持され、議事ノートでは禁止されます。これは矛盾ではありません。文字起こしは作業用の成果物であり、来歴情報を保持しても構いませんが、議事ノートは記録であり、そうではありません。notes_check.py はそれでも議事ノート内の生の Speaker 2 ラベルを拒否します。

もし誰かが .vtt を transcribe.py に渡した場合、それはデコードしようとせずに停止し、ここを指し示します。

動画ファイル

.mp4 は Teams や Zoom のダウンロードで実際に取得される形式であり、直接動作します — 事前に音声を抽出する必要はありません。動作確認済み: .mp4、.mov、.mkv、.webm、およびすべての音声コンテナ(.m4a、.mp3、.wav、.flac、.opus)。ffmpeg が音声トラックを抽出します。動画は無視され、アップロードされることはありません。

音声トラックがないファイル(ミュートされた画面録画)は、ネットワークバックエンドに提供される前に、最初の段階で拒否されます。このチェックは、出力ゲートの前に特に実行され、無音の動画がアップロードされて課金されることがないようにします。

レビューは工程であり、オプションの追加ではない

文字起こしの出力はドラフトです。そのように伝え、ユーザーに確認させてください。

機械による文字起こしは、どのしきい値でも捉えきれない方法で自信満々に誤ることがあります。このプロジェクトで実際の音声で測定したところ、large-v3 は話者の数字の訂正を黙って落とし、すべてのバックエンドが「board pack」を「board packs up」と聞き取っていました。どちらも完全に流暢に読めます。品質ガードは無音上の発話の捏造を捉えますが、もっともらしい聞き間違いを捉えることはできません。そして、あなたも出力を読むだけではそれを捉えることはできません。

したがって、ワークフローは 文字起こし → ユーザーが確認 → その後議事ノート です。

  1. 文字起こし .md は 「確認すべき箇所」 セクションで終わります — 聞き直しが最も効果的と思われるタイムスタンプ(最も信頼性の低いセグメント、数字を含むものなど)をランク付けします。ユーザーに明示的にそれを指し示してください。
  2. 名前、数字、日付を検証すべき項目です。 これらは最初に劣化しやすく、記録で間違えると最も大きな代償を払うことになります。
  3. 修正は .md に手動で、または再実行時に --replace を使用して加えます。その後、そこから生成されたものをすべて再生成します。

文字起こしや議事ノートのセットを検証済みとして提示しないでください。あなたは音声を聞いていません。ユーザーが聞いたのです。デコードされた内容、最も不確実な箇所を伝え、ユーザーに確認させてください。

編集と再生成

.md がソースです。それを編集し、PDF を再構築してください — 再文字起こしは不要です。

"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notes.md
"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py"  notes.md

編集後は毎回チェッカーを実行してください。手動編集はまさに、来歴情報の漏洩や推測による「次のステップ」の行が混入する場面です。

議事ノートの作成

ユーザーが文字起こしではなく議事ノートを希望する場合、あなたがそれを作成します — 文字起こしを読み、自分で文書を作成してください。scripts/summarize.py フォールバックがあり、ネットワークモデルを呼び出しますが、これは Claude Code セッションの外部でこのパイプラインを実行するためのものです。セッション内部では、それは無意味なアップロードです。

最初に尋ねる — 音声からはわからない三つのこと

ここでのバックエンドのほとんどは話者を識別しません。 すべての Whisper エンジンと Parakeet は話者フィールドをまったく返しません。二つの例外があります。--backend elevenlabs(Scribe)は最大32話者を分離し、--backend gemini は最大3話者を分離します。どちらも名前ではなく位置ラベルを返します — speaker_0 と spk:0 であり、これらは2026-09-04と2026-09-06のライブ応答から測定されたフォーマットです。そして、レジスタは生のラベルを議事ノート文書内で禁止しており、どちらのフォーマットでも同様です。したがって、彼らは何人話したか、どの発言がまとまりかを教えてくれます。それでも誰であるかは教えてくれません。話者の帰属はどちらにせよユーザーから得られます。話者分離は、記憶ではなく文字起こしからマッピングを可能にするだけです。

議事ノートを作成する前に、以下を尋ねてください。

  1. 誰が出席していたか。 名前がなければ、まったく帰属なしで議事ノートを作成してください — 非人称で、「~と述べられた…」。名前、役割、位置ラベルを決して発明しないでください。文字起こしから誰が話したか明らかである場合(誰かが名前で呼ばれているなど)、帰属しても構いません。曖昧な場合は名前なしとします。
  2. 会議の日付。 今日の日付はデフォルトであり、事実ではありません。音声は会話から数日経って書き起こされることがよくあり、事実記録に誤った日付が記載されることは、日付がないことよりも悪いことです。確認してください。
  3. 会議は何だったか — 参加者自身の言葉によるタイトル。

3つすべてを1回のメッセージで尋ねてください。1つずつではなく、今日の日付をデフォルトとして提供し、ユーザーが修正するだけで済むようにしてください。ユーザーが出席者の提供を拒否した場合は、帰属なしで進め、そのようにしたことを伝えてください。

一行も書き始める前に、references/notes-register.md を読んでください。 それは短く、仕様のすべてです。スタイル上のアドバイスではありません。そこに含まれるルールは、実際の文書からセクションが手動で削除されることで学ばれたものであり、その理由は美的ではなく法的なものです。

そこで有効になる二つのルール:

  • 事実に基づく要約のみ。 言われたこと。分析、含意、「次のステップ」、「カバーされなかったこと」、ギャップを埋めることはなし。
  • その場に居合わせた人物による議事録として読めること。 録音、文字起こし、音声、モデル、またはあらゆるデコーダーの成果物に言及しない — タイムコードなし、Speaker 2 ラベルなし。

その後、毎回チェックしてください — レジスタは自己強制ではなく、要約を依頼されたモデルは促されなくても分析を追加します。

"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notes.md

終了コード0は正常、終了コード1は各違反について file:line をリストします。文書を引き渡す前に修正してください。PDF に対しても実行してください — レンダリングされたファイルにのみ存在する漏洩もカウントされます。

ガイダンスは歓迎しますが、議事ノートの後で、かつ文書の外部で行ってください。 あなたの解釈は会話の中で提供するか、別のファイルに書き込んでください。決して議事ノートに追加しないでください。

PDF

"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py" notes.md

ヘッドレス Chrome、Chromium、または Edge が自動的に検出されます。どれもインストールされていない場合は、HTML を書き出し、その旨を伝えて終了コード0で終了します — HTML も成果物です。

来歴情報

ローカルデコードは mlx-whisper(MLX)または faster-whisper(CTranslate2)です。音声準備は ffmpeg です。ネットワークバックエンドは、標準ライブラリ urllib を介して直接 Groq と OpenAI の文字起こしエンドポイントを呼び出します — ベンダー SDK は使用しません。

議事ノート文書から来歴情報を排除することは、文書が何を言うかを変えるだけで、何が起こったかを変えるわけではありません。同意なしに通話を録音することは、多くの地域で刑事犯罪です...

関連スキル