0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Whisperベースの文字起こしツールに話者分離を追加して、パイプラインを再設計した話

0
Last updated at Posted at 2026-03-20

はじめに

本記事は、個人の技術検証として行った内容をまとめたものです。
業務とは切り離した形で、音声認識と話者分離の実装を検証しています。

普段はシステム設計に関わることが多いのですが、
今回は「議事録生成を自分でどこまで作れるか」をテーマに取り組みました。

これまでの取り組み

今回の内容は、以下の検証の続きになります。

今回はそこから一歩進めて、
「話者分離を含めたパイプライン化」に取り組みました。

きっかけ

オンライン会議を録画していたにも関わらず、
会議後に議事録生成が動いていないことに気づきました。

原因は単純な設定ミスです。

ただ、このときに思いました。

それなら自分で作ってしまった方が早いのでは?

まずは文字起こしから

最初はシンプルに、音声の文字起こしから始めました。

OpenAI APIやWhisper系のツールを使えば、

  • 音声 → テキスト

は比較的簡単に実現できます。

実際、ここはかなり早く形になりました。

文字起こしのPython実装は私にとって未経験でしたが、
まず Amazon Kiro に相談して仕様を作成してもらい、
その内容を精査しながら疑問点を解消しました。

その後は、普段の進め方と同じように Claude Code で実装し、
Code でテストを行い、最後にユーザテストまで実施しました。

ここまで、たしか1時間もかかっていなかったと思います。

つまり、少なくとも文字起こしまでなら、今のAI支援開発ではかなり短時間で実用レベルまで持っていける、という手応えがありました。

しかし「誰が話したか」が分からない

実際に使ってみると、すぐに問題に気づきました。

  • 誰が話しているのか分からない
  • 会話の流れが追いづらい
  • 実用的な議事録としては弱い

もちろん、「誰が話したか分からない」という問題もありますが、
それ以上に感じたのは、議事録としての精度の限界でした。

会議で決まったことや背景を正確に整理するには、

  • 発言の文脈
  • 誰の意見なのか
  • 話の流れ

これらが揃って初めて意味を持ちます。

つまり、

精度の高い議事録を作るためには、話者分離は必須である

というのが、最初の大きな気づきでした。

話者分離を入れた瞬間に難易度が上がる

文字起こしだけであれば、1本の処理で完結します。

しかし話者分離を入れた瞬間に、構成が大きく変わりました。

音声 → 話者分離 → 文字起こし → 統合 → 出力

実際には、

  • 話者分離(誰が話したか)
  • 音声認識(何を話したか)

この2つを別々のAIで並行処理し、
その結果を時間軸で統合する必要があります。


ここで感じたのは、

「難しい」というより、求められる精度と設計の質が変わったということでした。

AI駆動開発である以上、
実装そのものはClaude Codeなどを使えばある程度進められます。

ただ、これまでのように

「一気に仕上げる」

という進め方では成立しないと感じました。


特に大きかったのは、

  • 話者分離と音声認識で使うAIが異なること
  • それぞれの結果を整合させる必要があること

です。

この「整合させる」という部分は、
単なる実装ではなく設計の問題になります。

実際、このあたりはAmazon Kiroに相談しながら進めることになり、
処理の分割や統合の考え方を整理しながら進めていきました。

アーキテクチャ

全体の処理フローは以下のようになっています。

Audio/Video Input
       ↓
Audio Extraction (ffmpeg)
       ↓
   WAV Audio
       ↓
   ┌───────────────┬───────────────┐
   ↓                               ↓
Diarization                  ASR
(pyannote.audio)             (faster-whisper / whisper)
   ↓                               ↓
Speaker Turns                ASR Segments
   └───────────────┬───────────────┘
                   ↓
                Alignment
                   ↓
         JSON / Markdown Output

各処理の役割

Audio Extraction(ffmpeg)
動画・音声ファイルから解析用の音声(WAV)を抽出

Diarization(pyannote.audio)
「誰がいつ話したか」を推定(Speaker Turnsを生成)

ASR(faster-whisper / whisper)
「何を話したか」をテキスト化(ASR Segmentsを生成)

Alignment
話者情報と発話内容を時間軸で統合

Output
JSON(構造化データ)とMarkdown(議事録形式)を生成


この中で特に重要なのが Alignment(統合処理) です。

話者分離と音声認識はそれぞれ独立して動くため、
その結果をどのように結びつけるかが設計のポイントになります。

設計のポイント

今回の実装で重要だったのは、モデル選定よりも設計でした。

ただし、ここでいう設計は「自分でゼロから考える」というよりも、
Amazon Kiroと相談しながら、最終的な構成を決めていくプロセスでした。

特に意識したポイントは以下です。

  • 話者分離と音声認識の結果をどう統合するか
  • 精度と処理コストのバランスをどう取るか
  • 出力をどの形式で持つか

出力設計(JSON + Markdown)

最終的に、

  • JSON(構造化データ)
  • Markdown(人間が読む議事録)

の2種類を出力する構成にしました。

これは、

  • JSON → 後処理やAI連携に使える
  • Markdown → そのまま議事録として使える

という役割分担を意識したものです。


このあたりは、Amazon Kiroと対話しながら
「最終的にどう使うのか」というゴールを整理しつつ決めていきました。

結果として、

単に動く処理ではなく、
「後工程に繋がるデータ構造」を持つ設計になったと感じています。

気づいたら1600行になっていた

気づいたらコードが約1600行になっていました。

今回の開発はAI駆動で進めていたこともあり、
実装自体はスムーズに進んでいました。

そのため、正直なところ少し油断していました。


違和感に気づいたのは、
開発中やテスト時に「なんとなく遅い」と感じ始めたタイミングです。

そこでソースコードを確認したところ、
1ファイルに1600行以上詰め込まれている状態になっていました。


普段の業務であれば、

  • 300行を超えないように分割する
  • タイミングを見てリファクタリングする

といったルールを設けて進めています。

(Claude Codeに対しても、CLAUDE.mdでその前提を指示しています)


ただ今回は個人の検証ということもあり、
夢中になって進めているうちに、そのあたりの管理が後回しになっていました。

いわゆる「自己研究あるある」です。

リファクタリング

このままでは保守できないと判断し、構造を分割しました。

models.py      # データクラス定義
cli.py         # CLI Parser
device.py      # デバイス判定
diarization.py # 話者分離
asr.py         # 音声認識
alignment.py   # 統合処理
output.py      # 出力生成
pipeline.py    # 全体制御

結果として、

  • 責務ごとに分離できた
  • テストしやすくなった
  • 処理の流れが追いやすくなった

など、設計としても改善できたと感じています。

環境の違い

以下は、個人の検証環境で測定した結果です。

Device Platform Total
CPU macOS ~330s
MPS macOS ~140s
CUDA WSL2/Windows 24.5s

検証環境

  • Windows(WSL2)
    • CPU: Intel Core i7
    • GPU: NVIDIA GeForce RTX 5070 (12GB)
    • RAM: 32GB

もともとはRTX 3060環境で検証していましたが、
昨年RTX 5070にGPUを交換したため、今回の測定はその構成で行っています。

  • macOS
    • MacBook Pro (M4 Pro)
    • メモリ: 48GB

ハマりどころ

今回の実装で一番ヒヤッとしたのは、環境周りのトラブルでした。

仮想環境を作り忘れていた

これは完全に自分の不注意ですが、
Windows環境で仮想環境を作らずに、グローバルにPythonライブラリをインストールしていました。

途中で気づいたときは、正直かなりゾッとしました。

一番まずかったのはCTranslate2

特に問題だったのが CTranslate2 です。

今回の環境では、

  • RTX 5070(CUDA 12系)
  • CTranslate2 の pipパッケージ

の組み合わせがうまく動かず、
そのままでは実行できませんでした。

ソースビルドを決断

最終的に、

CTranslate2をCUDA環境に合わせてソースビルドする

という判断をしました。

これに約1時間ほどかかりましたが、
結果的には問題なく動作するようになりました。

そして気づく「グローバル環境問題」

ここでさらに問題が発覚します。

せっかくビルドしたCTranslate2が、
グローバル環境に入っている状態でした。

つまり、

仮想環境を作り直したら、またやり直し?

という状況です。

解決:グローバルから仮想環境へコピー

ここはAmazon Kiroに相談して解決しました。

グローバルにインストールしたパッケージを
仮想環境にコピーする方法を教えてもらい、

ビルドをやり直すことなく移行できました。

ハマったことによる学び

今回の件での学びはシンプルです。

  • 仮想環境は最初に作る
  • GPU系ライブラリはバージョン依存が強い
  • pipで動かない場合はソースビルドも選択肢
  • 環境を壊しても、リカバリ方法はある

特に、GPU + ライブラリの組み合わせ問題は想像以上にシビアだと感じました。

学び(話者分離プロジェクトを通して)

今回のプロジェクトを通して感じたことは、いくつかあります。

まず一番大きかったのは、

問題はモデルではなく設計にある

ということでした。

文字起こしのように単体で完結する処理は、
AIを使えば比較的短時間で形になります。

しかし話者分離のように複数の処理が関わると、
途端に「どう組み合わせるか」という設計の問題になります。


次に感じたのは、

スクリプトは作れるが、パイプラインは設計しないと作れない

ということです。

今回の構成では、

  • 話者分離
  • 音声認識
  • アライメント
  • 出力

それぞれが独立した処理として存在し、
それらを繋ぐことで初めて意味を持ちます。

これは単なるコードではなく、
明確な設計が必要な領域でした。


そしてもう一つ、

「動くもの」と「使えるもの」は別物である

ということです。

最初は動けばよいと思っていましたが、

  • 出力形式(JSON / Markdown)
  • 再利用性
  • 後工程との連携

まで考えると、
単なるツールではなく「仕組み」として設計する必要がありました。

今後

今回の実装は、あくまで「話者分離付き文字起こし」までです。

ここからさらに発展させる予定です。

  • 議事録生成(要約・整理)
  • ローカルLLMによる処理
  • OpenAI APIとの比較

特に、Qiitaでも触れている通り、
ローカルLLMで議事録生成まで完結させることを目標にしています。


リポジトリ

実装したコードはこちらで公開しています。

おわりに

今回の取り組みは、
「議事録を自動で作れなかった」という小さなトラブルから始まりました。

しかし実際に作ってみると、

単なる文字起こしツールではなく、
複数のAIを組み合わせたパイプライン設計の問題になっていました。


AIを使えば、確かに「すぐ動くもの」は作れます。

ただし、

「使える状態」にするためには設計が必要になる

というのが、今回の一番大きな学びです。

もし同じように、

  • AIを業務に取り入れたい
  • 自分でツールを作ってみたい

と考えている方がいれば、

まずは小さく作ってみること、
そしてそこから設計を見直していくことをおすすめします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?