要約
- 完全ローカルで動作する Discord ボイスチャット AI Bot を作った - 音声認識 → LLM → 音声合成のパイプラインを、外部 API なしで実現
- TTS - Qwen3-TTS は表現力は高いが遅延が大きく、VOICEVOX に切り替えることで応答速度を劇的に改善
- ローカルで動かせる規模の LLM では会話品質に限界があり、実用性はそこまで高くない
はじめに
対象読者
- Discord Bot 開発に興味がある人
- 音声 AI(STT / TTS / LLM)をローカルで動かしてみたい人
- Pycord や Ollama、VOICEVOX を使ったことがある or 興味がある人
作った背景
Discord でミーティングを行っているのですが、そこで司会を AI にやらせたら面白いのではという話になりました。
とはいえいきなり本格的に作るのは大変なので、まずはローカルで TTS して喋れるかどうかを確かめるところから始めてみました。
アーキテクチャ
処理の流れはシンプルな3段階パイプラインです。
ユーザー発話 (Discord VC)
→ Pycord 音声受信 (Opus → PCM 48kHz stereo)
→ リサンプル (48kHz stereo → 16kHz mono float32)
→ faster-whisper で文字起こし (日本語)
→ Ollama でLLM応答生成
→ 句読点ごとに分割して VOICEVOX / Qwen3-TTS で音声合成
→ リサンプル → 48kHz stereo PCM
→ Pycord で Discord に再生
技術スタック
| コンポーネント | 技術 |
|---|---|
| 言語 | Python 3.12 |
| パッケージ管理 | uv |
| Discord ライブラリ | Pycord (py-cord[voice]) |
| STT | faster-whisper (small, int8) |
| LLM | Ollama + Qwen3 |
| TTS (推奨) | VOICEVOX |
| TTS (代替) | Qwen3-TTS 0.6B |
実行環境
| OS | Windows 11 |
| GPU | RTX 5070 Ti |
| VRAM | 16GB |
実装の工夫点
実用的な速度で動かすために、いくつか工夫した点があります。
1. VAD (Voice Activity Detection) によるリアルタイム発話検出
音声チャンクごとに RMS エネルギーを計算し、発話開始 → 1.5秒の無音で自動的に録音停止 → パイプライン実行という流れで動かしています。
最初は固定時間チャンク方式を試していたのですが、沈黙の待ち時間が無駄に長くなってしまうため、VAD による方式に切り替えました。これにより、発話が終わったタイミングで即座に処理が始まるようになりました。
2. 句読点区切りによる TTS 分割再生
LLM の出力が完了した後、句読点(。!?)で区切った文単位で TTS に渡して順次再生します。全文をまとめて TTS するより最初の文の合成時間が短い分、再生開始が早くなるという利点があります。また長い応答も文単位で分割されるため、会話のテンポが崩れにくくなります。
3. LLM 出力のクリーニング
Qwen3 の <think>...</think> タグや絵文字、特殊記号を正規表現で除去し、TTS に渡す前にクリーンなテキストにしています。thinking モードを有効にすると <think> ブロックがそのまま読み上げられてしまうので、これは必須でした。
4. 話者認識(複数人対応)
Pycord はユーザーごとに音声データを分離して記録してくれます。ユーザーのディスプレイ名を取得し、LLM に 名前「発言内容」 の形式で渡すことで、誰が話しているかを Bot に認識させています。
計測結果
VOICEVOX + Qwen3:8b 構成
| ステップ | 所要時間 |
|---|---|
| STT (faster-whisper small) | 0.1〜0.5秒 |
| LLM (Qwen3:8b) | 0.4〜5秒 |
| TTS (VOICEVOX) | 0.06〜0.1秒 |
| 合計 | 約1.7〜6秒 |
ストリーミング + 句読点区切り再生により、最初の文は約1秒で再生開始できます。
Qwen3-TTS + Qwen3:1.7b 構成
| ステップ | 所要時間 |
|---|---|
| STT (faster-whisper small) | 0.1〜0.5秒 |
| LLM (Qwen3:1.7b) | 0.4〜0.8秒 |
| TTS (Qwen3-TTS 0.6B) | 4〜14秒 |
| 合計 | 約5〜15秒 |
Qwen3-TTS は遅延が大きく、会話として成立しないレベルでした。
TTS 比較: Qwen3-TTS vs VOICEVOX
開発中に最も悩んだのが TTS の選定です。
Qwen3-TTS
- 短い文(「こんにちは」)でも 4〜8秒 かかる
- GPU VRAM を 4〜6GB 消費するため、LLM に使えるモデルサイズが制限される
- 16GB VRAM の場合、STT(1.5GB)+ Qwen3-TTS(4〜6GB)で 6〜8GB が TTS/STT に取られる
- LLM は 1.7b〜4b 程度しか載らない → 応答品質が低下してしまう
- 抑揚や表現力は高いが、リアルタイム会話には速度面で課題がある
VOICEVOX
- 同じ文を 0.06〜0.1秒 で生成
- 設定からエンジンモードを CPU / GPU で切り替えられる
- 応答速度の観点では実用的なレベルに達している
結論: リアルタイム会話用途では VOICEVOX が圧倒的に有利です。Qwen3-TTS は抑揚などの表現力は優れますが、低遅延が求められる用途には向きません。
LLM モデルサイズの影響
VOICEVOX を使うと GPU VRAM に余裕が生まれ、より大きな LLM を載せられます。
| モデル | 初回応答時間 | 応答品質 |
|---|---|---|
| Qwen3:1.7b | 0.4〜0.8秒 | 低い |
| Qwen3:4b | 0.5〜1.5秒 | 中程度 |
| Qwen3:8b | 0.4〜1秒 | 中〜高 |
ただし、ローカルで動かせる規模のモデルでは応答の精度・自然さに限界があります。8b 程度では単発の質疑応答はこなせますが、文脈を維持した継続的な会話としての完成度はまだ低いと感じています。
ハマりポイント
VOICEVOX の localhost 問題
localhost を指定すると DNS 解決のオーバーヘッドで約2秒かかることがありました。127.0.0.1 を直接指定することで 0.02秒に改善しました。
VOICEVOX を使う際は localhost ではなく 127.0.0.1 を指定することをおすすめします。
Speech-to-Speech モデルについて
今回は STT → LLM → TTS という3段階のパイプライン構成を採用しましたが、世の中にはこれらを一体化した Speech-to-Speech モデルも存在します。音声の抑揚や感情をそのまま引き継いだ応答が可能で、より自然な会話体験が期待できます。NVIDIA PersonaPlex はその一例ですが、要求 VRAM が大きく、今回の環境では試せませんでした。
まとめと今後の課題
完全ローカルで動作する Discord ボイスチャット AI Bot を作ることができました。VOICEVOX への切り替えにより、最初の文までの遅延を約1秒まで短縮でき、会話として成立するレベルの速度は達成できたと思います。
一方で、まだ改善したい点もあります:
- より大きな LLM の使用: 8b 程度では会話品質に限界がある。32b 以上や API ベースの LLM を使うことで実用性が大きく向上すると考えている
- 複数人対応の改善: 現在は発話を検知するたびに個別に応答するため、複数人の会話では全員に逐一返答してしまう。実際の複数人利用を想定するなら、発言をまとめて受け取り適切なタイミングで一度だけ応答する仕組みが必要
- 割り込み対応: Bot 再生中にユーザーが話したら停止する機能
