概要
ずんだもんと会話ができるCLIツール "zun-talk" を作ったときのまとめです。
構成
全体の流れです。
マイク入力
↓ PortAudio(recorder)
WAVデータ生成
↓ whisper.cpp(stt)
テキストに変換
↓ Claude API(claude)
返答テキスト生成
↓ VOICEVOX API(voicevox)
WAVデータ生成
↓ oto v3(player)
スピーカー出力
それぞれの役割は以下の通り
| コンポーネント | 役割 | 手段 |
|---|---|---|
| Claude API | AI応答生成 | Anthropic Go SDK |
| VOICEVOX | テキスト→音声合成 | ローカルHTTP API |
| whisper.cpp | 音声→テキスト変換 | サブプロセス呼び出し |
| PortAudio | マイク録音 | gordonklaus/portaudio |
| oto v3 | 音声再生 | ebitengine/oto |
ディレクトリ構成
zun-talk/
├── main.go # エントリポイント・入力モード切替ループ
├── config/
│ └── config.go # 環境変数ロード・定数管理
├── claude/
│ └── client.go # Claude APIクライアント・会話履歴管理
├── voicevox/
│ └── client.go # VOICEVOX APIクライアント
├── player/
│ └── player.go # WAV再生(oto v3)
├── recorder/
│ └── recorder.go # マイク録音(PortAudio)
└── stt/
└── whisper.go # 音声文字起こし(whisper.cpp)
入力モード
実行中に :text / :ptt / :vad と入力するだけでモードが切り替わります。
| モード | コマンド | 動作 |
|---|---|---|
| テキスト入力 | :text |
キーボードで入力(デフォルト) |
| Push-to-talk | :ptt |
Enterを押すと録音開始 |
| 音声自動検出 | :vad |
話し終わりを無音検出して自動送信 |
実装のポイント
ストリーミング×並列合成で低遅延を実現
最初はClaudeの応答が全文返ってくるのを待ってから音声合成を行っており、ずんだもんの返答までの遅延が目立ちました。
そこで今回は文単位でストリーミングしながら並列処理する方法を採用しています。
。!?、\n などの句読点を検出するたびに音声合成をキックするため、Claudeが返答を生成しながらずんだもんが話し始めます。
会話履歴はClaudeクライアントが内部で保持しています。
次やりたいこと
荒削りなのでこれからちょっとずつ改善していきたい。
できるようにしたいこと
- ずんだもん以外のキャラを選択できるようにする
- ずんだもん口調の精度を上げる
まとめ
科学の力(claude code)ってすげー