# ずんだもん会話（リアルタイム音声チャット）

## 概要

ブラウザの Web Speech API で音声認識 → Claude で「ずんだもん」風の応答を生成 → VOICEVOX で合成音声として返却するリアルタイム会話機能。
呼びかけワード（「ずんだもん」「ずんだ」等）を検出して反応するため、常時マイク入力していてもバックグラウンドでは発話しない。

さらに、**特定の意図（スマートホーム操作・競馬収支報告）** は AI を経由せず、サービスメソッドから直接応答する設計になっている。

## 使い方

- **URL**: `/ai/zundamon-chat`
- ログイン必須（競馬収支報告を使う場合）

### 会話フロー

1. ブラウザで音声認識を開始
2. 発話に「ずんだもん」などの呼びかけワードが含まれる場合のみ送信
3. バックエンドが以下の優先順でハンドラを試す:
   1. **スマートホーム**（テレビ → エアコン → 照明の順に意図マッチ）
   2. **競馬収支**（「競馬 × 収支/成績」パターン）
   3. 上記にマッチしなければ **Claude で自由応答**
4. 応答テキストを VOICEVOX で合成して `audio_url` を返却
5. フロントエンドで自動再生

### 呼びかけワード

`ずんだもん` / `んだもん` / `ずんだ` / `ズンダモン` / `ずん太郎`（音声認識のゆれを吸収）

## エンドポイント

| メソッド | URL | 名前 | スロットル |
|----------|-----|------|-----------|
| GET | `/ai/zundamon-chat` | `zundamon_chat.index` | - |
| POST | `/ai/zundamon-chat/send` | `zundamon_chat.send` | `30/1min` |

## 技術構成

```
Browser (Web Speech API)
    │ text
    ▼
ZundamonChatController@send
    │
    ├─ ZundamonChatService::tryHandleSmartHome()   → Nature Remo 操作
    ├─ ZundamonChatService::tryHandleHorseRacing() → HorseRacingService::getSummary()
    └─ ZundamonChatService::generateResponse()     → Anthropic Claude
             │ text
             ▼
ZundamonChatService::synthesize() → VoicevoxService → /storage/zundamon_chat/*.wav
```

### 主要クラス

| クラス | 役割 |
|--------|------|
| `App\Http\Controllers\AI\ZundamonChatController` | HTTP 入口、呼びかけワード判定、ハンドラ分岐 |
| `App\Services\AI\ZundamonChatService` | スマートホーム・競馬・AI応答・VOICEVOX合成 |
| `App\Services\IoT\VoicevoxService` | VOICEVOX API 呼び出し（speaker_id: 3 = ずんだもん・ノーマル）|
| `App\Services\IoT\NatureRemoService` | Nature Remo Cloud API |
| `App\Services\Entertainment\HorseRacing\HorseRacingService` | 競馬サマリ |

### スマートホーム意図のマッチ

| 家電 | 判定キーワード | 操作 |
|------|---------------|------|
| テレビ | `テレビ`, `tv` | 電源／音量／チャンネル／入力切替 |
| エアコン | `エアコン`, `冷房`, `暖房`, `クーラー`, `ac` | 電源／モード（冷暖除湿）／温度（16〜32℃）|
| 照明 | `照明`, `電気`, `ライト`, `明かり` | オン／オフ／常夜灯／明るく／暗く |

部屋名（`洋室`, `和室`, `リビング`, `寝室`, ...）をテキストから抽出し、Nature Remo のニックネーム部分一致で対象家電を絞り込む。

### AI 応答パラメータ

- モデル: `claude-3-haiku-20240307`
- `max_tokens`: 96（短文テンポ重視）
- システムプロンプト: 「語尾〜のだ／〜なのだ、明るく元気に 1 文 50 文字以内」
- 会話履歴は直近 6 メッセージ（3 ターン）までフロントから渡す

## 入力バリデーション

- `text`: 必須、最大 1000 文字
- `history`: 配列（任意）、各要素は `role = user|assistant`, `content` 必須

## 必要な環境変数

| 変数名 | 説明 |
|--------|------|
| `ANTHROPIC_API_KEY` | Claude 応答生成用 |
| `VOICEVOX_URL` | VOICEVOX エンドポイント（Docker 内 or 外部）|
| `NATURE_REMO_TOKEN` | スマートホーム連携を使う場合 |

## ラズパイ常駐版（Python デーモン）

Web 版と同等の対話体験を Raspberry Pi 上でスタンドアロンに動かす Python 版デーモンを
`python/zundamon-chat/` に同梱している。マイクで常時聞き耳を立て、「ずんだもん〜」と呼びかけられたら
VOICEVOX の音声でスピーカーから応答する。

| 項目 | Web 版 | Python 版 |
|---|---|---|
| 音声認識 | ブラウザの Web Speech API | マイク＋Google Speech Recognition / Vosk |
| 音声出力 | ブラウザの `<audio>` タグ | ラズパイのスピーカー（`sounddevice`） |
| 音声保存 | `storage/app/public/voicevox/` | メモリ再生（保存しない） |
| スマートホーム | 対応 (Nature Remo) | 対応 |
| Claude 応答 | 対応 | 対応 |
| 競馬収支報告 | 対応（Laravel DB参照） | **未対応**（DB依存のため） |
| 常駐化 | Laravel (php-fpm) | systemd (`zundamon-chat.service`) |

セットアップ・起動手順・オフライン音声認識（Vosk）の導入方法は
[`python/zundamon-chat/README.md`](../../python/zundamon-chat/README.md) を参照。

## レビュー時のメモ

- モデルが `claude-3-haiku-20240307` と古い。体感速度優先なら問題ないが、品質を求める場合は Haiku 4.5 (`claude-haiku-4-5-20251001`) に差し替え可能。
- 発話頻度・VOICEVOX 合成回数を抑えるためスロットル `30/min` を設定済み。
- スマートホーム／競馬の意図検出は「優先 AI 不使用」方針で、レイテンシ・コスト・API 利用制限の三方に優しい。
- Python デーモン版は Web 版と意図判定ロジックを共通化していないため、キーワードルールを変更する際は両方の実装を揃える必要がある（`app/Services/AI/ZundamonChatService.php` と `python/zundamon-chat/intent_handlers.py`）。
