ハイブリッド構成: Module-LLM Whisper(ja) + Mac bridge Claude + VOICEVOX
初出: 2026-05-25 — Issue #52 / PR #51 の続き
結論
Module-LLM の LLM (qwen 0.5B / TinySwallow 1.5B) は実用不可 (精度低 or 推論 2分超)。 Module-LLM は「日本語 ASR + KWS + VAD」のためだけに使い、LLM/TTS は Mac Studio bridge に任せる のが最適解。
実測 7 秒で日本語発話まで完結。
[ユーザー] HELLO + 日本語で発話
↓ マイク (Module-LLM)
[KWS → VAD → Whisper(ja)] Module-LLM オンデバイス
↓ テキスト
[WSS agent.run] CoreS3 → Mac bridge
↓
[Claude API] Mac bridge
↓ 応答テキスト
[VOICEVOX 満別花丸] Mac bridge → PCM
↓ tts.start / バイナリ chunk / tts.end
[M5.Speaker.playRaw] CoreS3
↓ 🦆🔊
[満別花丸の声で発話]検証ログ (2026-05-25 20:41)
firmware 起動
[main] kws_work_id=kws.1000 OK
[main] vad_work_id=vad.1001 OK
[main] whisper_work_id=whisper.1002 OK (lang=ja)
[main] llm_work_id=llm.1003 (model=tinyswallow-1.5b-ax630c) ← setup は成功するが使わない
[main] try melotts model=... ← 失敗するが使わない
[main] voice pipeline ready, wake=HELLO
[wss] connected to /stackchan/ws発話 → 応答 (1 ターン分)
CoreS3 シリアル:
[wake] keyword detected
[asr] 聞こえてますか?日本語です私の声が聞こえてますかスタッフクチャン.
[wss] agent.done backend=claude
[wss] tts.start sr=24000 bps=16 ch=1 total=300032
[wss] tts.end received=300032 bytes
[tts] playRaw samples=150016 sr=24000 ok=1Mac bridge:
[bridge] connected device=? session=00488dd4-...
[tts] synthesizing (41 chars): はい、聞こえてますよ。ぼくはドナック、緑の帽子の相棒です。今日は何をしましょうか。...
[tts] WAV ready: 300032B pcm @ 24000Hz (6251ms) synth=818ms体感レイテンシ
発話開始から応答発話まで ~7 秒 (ユーザー実測)。
| 区間 | 推定時間 |
|---|---|
| 発話 → ASR 完了 | 1-2 秒 (Whisper-tiny on AX630C) |
| ASR → Claude 応答テキスト | 3-4 秒 (Claude API) |
| Claude → VOICEVOX 合成 | 0.8 秒 (実測 818ms) |
| PCM 送信 + 再生開始 | 1-2 秒 |
| 合計 | ~7 秒 |
なぜこの構成に落ち着いたか
検証で除外された案:
| 案 | 結果 | 理由 |
|---|---|---|
| Module-LLM 単体完結 (TinySwallow + MeloTTS) | ❌ | TinySwallow 推論 2 分超、MeloTTS は同時 RAM 不足 |
| Module-LLM 単体完結 (qwen 0.5B + MeloTTS) | ⚠️ | 日本語精度低、過去ログで「中国語ナンセンス」 |
| 全 Mac (CoreS3 マイク → PCM ストリーム) | △ | firmware 大改修必要 (PCM ストリーミング新規実装) |
| 案 A: ASR=Module-LLM, LLM=Claude, TTS=VOICEVOX | ✅ | 動いた、コード変更最小 |
firmware の主要変更点
firmware/src/main.cpp の ASR ハンドラ:
cpp
// Before (Module-LLM オンデバイス LLM):
String full_response;
module_llm.llm.inferenceAndWaitResult(llm_work_id, asr.c_str(),
[&full_response](String& token) { full_response += token; });
wsRequestTts(full_response); // bridge VOICEVOX 経由
// After (Claude 直送):
wsAskClaude(asr); // 既存の serial CLI `claude:` と同じ経路LLM/MeloTTS の setup 自体は残してあるが使用しない (将来「オフラインで qwen 0.5B 縮退モード」用の予備)。
bridge 側の役割
mac-studio/bridge/src/server.ts:
- 既存の
agent.runハンドラがそのまま使える - Claude 応答後に VOICEVOX で TTS → tts.start/chunk/end を返す経路は PR #51 のまま
tts.synthesize 単独経路も追加実装してあり (firmware で wsRequestTts 関数として残存)、将来オンデバイス LLM が高速化したら活用可能。
Module-LLM の役割 (再定義)
| 機能 | 状態 |
|---|---|
| マイク (PDM) | ✅ メイン入力デバイス |
| KWS ("HELLO") | ✅ 使う |
| VAD | ✅ 使う |
| Whisper(ja) ASR | ✅ 本構成の核 |
| LLM (qwen / TinySwallow) | ⚪ install 済だが今は使わない |
| MeloTTS ja-jp | ⚪ install 済だが今は使わない |
| YOLO / Camera | (将来用、未使用) |
つまり 「優秀な音声入力デバイス + 将来の NPU 推論オフロード予備」 として保持。
未解決事項 / 今後の課題
- ASR の誤認識: 「スタッフクチャン」「日本語です私の声が聞こえてますか」など句読点崩れ。Whisper-tiny の限界。
whisper-base(公式 repo にある) で改善するかは別検証 - wake word の精度: 英語 "HELLO" 固定。ESP-SR で日本語 "Hi, Stack Chan" 化は別 issue
- Module-LLM body の USB-C は ADB と StackFlow UART で干渉: ADB 接続中は CoreS3 ↔ Module-LLM の UART 通信が不安定 (要追加検証)
- system prompt は Mac 側 backends/claude.ts で管理: Donack キャラクター prompt は bridge 側に集中