LiveAvatar 対応 — 社長アバター会話デモ

← デモに戻る

1. LiveAvatar 対応とは

本デモでは、アバターの表現層に HeyGen LiveAvatar LITE モード を導入しました。

従来は 3D モデル(VRM)をブラウザ上で描画し、音声解析による口パクや瞬きを JavaScript で制御していました。LiveAvatar では、この映像生成を HeyGen のサーバーサイド で行い、WebRTC(LiveKit)経由でブラウザに配信します。

VRM 方式(従来)LiveAvatar 方式(今回追加)
描画 ブラウザ内で Three.js + VRM を描画 HeyGen サーバーで映像生成 → WebRTC で配信
リップシンク AudioContext で周波数解析 → 母音5種の口形 HeyGen がサーバー側で自動制御(高精度)
うなずき・瞬き JS で自前実装(タイミング・確率制御) HeyGen がサーバー側で自動生成
音声再生 ローカル AudioContext で MP3 再生 PCM 音声を WebSocket で送信 → LiveKit Audio Track で受信
見た目 3D キャラクター風 実写風のリアルなアバター映像
クライアント負荷 GPU 描画あり(Three.js) 動画再生のみ(軽量)
外部サービス依存 なし(完全自前) HeyGen API(従量課金)
💡 ポイント: 「物量アバター方式」の根幹 — 回答プール・QAエンジン・相槌ロジック — はそのまま維持しています。変わったのはアバターの表現層だけです。
🔄 今後の方針: VRM 方式は廃止ではなく、今後は VRM と LiveAvatar を切り替え可能にする 予定です。コスト不要で手軽に動かしたい場合は VRM、よりリアルな表現が必要な場面では LiveAvatar — シーンに応じて最適な方式を選択できる設計を目指します。

2. 変わったもの・変わらないもの

✅ 変わらないもの

🔄 変わったもの

➕ 追加されたもの

追加要素概要
HeyGen セッション APIサーバー側で HeyGen の token 取得 → セッション開始を仲介
PCM 音声変換 + キャッシュ既存 MP3 を PCM 24kHz 16bit mono に変換。起動時に全468件を一括キャッシュ
LiveKit Client SDKWebRTC による映像・音声の受信(バンドル済み JS を静的配置)
接続 / 切断ボタンHeyGen クレジット保護のため、手動で接続を制御
セッションタイマー接続中の経過時間を表示。課金状況の把握に使用
安全自動切断290秒(約5分)で自動切断し、クレジットの無駄遣いを防止

3. アーキテクチャ比較

VRM 方式(従来)

ブラウザ FastAPI ┌─────────────────────┐ ┌────────────────────┐ │ WebSpeech STT │───POST───▶ │ /api/ask │ │ Chat UI │◀──JSON───── │ Embed + Selector │ │ Three.js + VRM │ │ │ │ AudioContext (再生) │◀─GET────── │ /audio/*.mp3 │ │ AnalyserNode(リップ)│ │ /model/shingo.vrm │ └─────────────────────┘ └────────────────────┘ ブラウザが全てを担う: 音声ファイルをダウンロード → AudioContext で再生 → AnalyserNode で周波数解析 → VRM の口を動かす

LiveAvatar 方式(今回追加)

ブラウザ FastAPI ┌──────────────────────┐ ┌──────────────────────────┐ │ WebSpeech STT │───POST──▶ │ /api/ask │ │ Chat UI │◀──JSON──── │ Embed + Selector │ │ │ │ │ │ WebSocket │◀═══WSS═══▶ │ /api/heygen/session │ │ agent.speak (PCM) │ │ → HeyGen token + start │ │ agent.speak_end │ │ │ │ agent.interrupt │ │ /api/heygen/audio │ │ session.keep_alive │ │ → PCMキャッシュから返却 │ │ │ └──────────────────────────┘ │ LiveKit (WebRTC) │◀═══════════ HeyGen サーバー │ video → <video> │ ↑ │ audio → <audio> │ LiveAvatar LITE └──────────────────────┘ (映像+音声生成+リップシンク) 役割分担: FastAPI → 接続情報の仲介 + PCM 音声の提供 HeyGen → 映像生成・リップシンク・音声再生 ブラウザ → 操作 UI + WebRTC 映像受信のみ(描画負荷なし)

技術スタック(追加分)

レイヤー技術補足
映像配信HeyGen LiveAvatar LITEサーバーサイドレンダリング + WebRTC
WebRTCLiveKit Client SDK v1.8.0UMD ビルドを静的配置
音声変換pydub + ffmpegMP3 → PCM 24kHz 16bit mono
HeyGen API 呼び出しhttpx(非同期)token + start の2段階

既存の技術スタック(WebSpeech API, Gemini Embedding, Gemini Flash-Lite, FastAPI, nginx + Let's Encrypt, AWS EC2)はアーキテクチャページを参照してください。

4. 音声ストリーミングの仕組み

従来方式ではブラウザが MP3 ファイルをダウンロードして AudioContext で直接再生していましたが、LiveAvatar では HeyGen に音声データを送り込み、HeyGen 側で映像と同期させて配信します。

処理フロー

1

回答選択

/api/ask で質問に最適な回答 ID + バリアント(20s/40s)を決定。ここまでは従来と同じ。

2

PCM 取得

/api/heygen/audio に回答 ID を送り、サーバーのメモリキャッシュから PCM チャンク(Base64)を即座に取得。

3

チャンク送信

WebSocket で agent.speak を 100ms 間隔で逐次送信。全チャンク送信後に agent.speak_end を送信。

4

HeyGen 処理

HeyGen サーバーが PCM 音声を受け取り、リップシンク付きの映像をリアルタイム生成。

5

WebRTC 配信

LiveKit 経由で映像(Video Track)と音声(Audio Track)がブラウザに配信され、<video> 要素で再生。

PCM 事前キャッシュ

全468件(234件 × 20s/40s の2バリアント)の MP3 ファイルを、サーバー起動時に PCM 24kHz 16bit mono に一括変換してメモリに保持しています。

割り込み(interrupt)

アバターが発話中に新しい質問が入った場合、agent.interrupt を即座に送信して前の発話を中断し、新しい回答の再生に切り替えます。チャンク送信ループも内部フラグで即座に停止します。

5. セッション管理とクレジット保護

HeyGen LiveAvatar は従量課金(1分単位・端数切り上げ)のため、セッション管理にはクレジットの無駄遣いを防ぐ仕組みを組み込んでいます。

セッションのライフサイクル

ユーザーが「接続」ボタンを押す │ ▼ ┌────────────────────────────────────────┐ │ 1. Token 取得 │ │ FastAPI → HeyGen API (POST) │ │ → access_token を取得 │ ├────────────────────────────────────────┤ │ 2. Session Start │ │ FastAPI → HeyGen API (POST) │ │ → sessionId, wsUrl, livekitUrl, │ │ livekitClientToken を取得 │ ├────────────────────────────────────────┤ │ 3. WebSocket 接続 │ │ ブラウザ → HeyGen wsUrl に直接接続 │ ├────────────────────────────────────────┤ │ 4. LiveKit Room 接続 │ │ ブラウザ → LiveKit 経由で映像受信 │ │ Video Track → <video> 要素 │ │ Audio Track → <audio> 要素 │ ├────────────────────────────────────────┤ │ 5. Keep-Alive (30秒間隔) │ │ WebSocket で session.keep_alive │ │ → セッション維持 │ ├────────────────────────────────────────┤ │ 6. 切断 │ │ 手動ボタン or 安全自動切断(290秒) │ │ → WebSocket close → LiveKit close │ └────────────────────────────────────────┘

クレジット保護の仕組み

機能内容
手動接続ページを開いただけでは接続しない。ユーザーが「接続」ボタンを明示的に押す必要がある
セッションタイマー接続中の経過時間をリアルタイム表示(m:ss 形式)。切断後も最終値を保持
安全自動切断デフォルト ON。290秒(約5分)経過で自動切断。トグルスイッチで ON/OFF 切替可能
手動切断接続中はいつでも「切断」ボタンで即座にセッションを終了可能
💡 なぜ 290秒? HeyGen の課金は1分単位・端数切り上げのため、5分ちょうどで切ると6分扱いになるリスクがあります。4分50秒(290秒)で切断することで、確実に5分以内に収めます。

6. 応答速度の内訳

質問してから回答が返るまでのレイテンシを分解すると、以下のようになります。

接続レイテンシ(初回のみ)

処理所要時間補足
Token 取得 + Session Start数秒HeyGen API の外部処理
WebSocket + LiveKit 接続数秒WebRTC ハンドシェイク
合計約 12秒HeyGen 側の外部要因が支配的

応答レイテンシ(質問ごと)

処理所要時間補足
Embedding 変換~400msGemini Embedding API(従来と同じ)
コサイン類似度検索~1ms234件の全件探索(従来と同じ)
LLM 指揮者による選択~1,300msGemini Flash-Lite(従来と同じ)
PCM チャンク取得~15msメモリキャッシュから即座に返却
WebSocket チャンク送信開始~100ms最初のチャンク送信まで
合計(発話開始まで)約 2〜3秒QAエンジン処理が支配的(LiveAvatar による追加は微小)
💡 ポイント: LiveAvatar 対応による追加レイテンシは PCM キャッシュ取得(~15ms)+ WebSocket 送信開始(~100ms)の合計 約 0.1秒 程度です。応答速度のボトルネックは従来と同じく QA エンジン(Embedding + LLM 指揮者)にあり、アバター方式の変更は速度にほぼ影響しません。

7. 相槌の扱い

本デモの特徴である「自然な相槌」は、現時点ではローカル再生方式を維持しています。

現在の方式(ローカル再生)

現時点での制約

相槌がローカル再生のため、相槌が鳴っても HeyGen 映像のアバターは口を動かしません。音声は聞こえるが映像には反映されない という不一致が生じます。デモとしてはこの状態で十分機能していますが、より自然な体験を目指す余地があります。

🔄 今後の取り組み: 相槌音声を HeyGen の agent.speak 経由で送信し、映像のリップシンクやうなずきと同期させる方式への移行を予定しています。これにより、相槌を打つ際のアバター映像もより自然になります。

8. 今後の展望

LiveAvatar 対応は完了しましたが、さらなる体験向上のための取り組みを計画しています。

項目内容効果
VRM / LiveAvatar 切替 UI 上でアバター方式を選択可能にする。VRM(コスト不要・3D表現)と LiveAvatar(高品質・実写風)を場面に応じて使い分け コストを抑えたい場面ではVRM、リアルさが求められる場面ではLiveAvatar、という柔軟な運用
相槌のアバター同期 相槌音声を HeyGen 経由で送信し、リップシンク・うなずきと同期。現在のローカル再生から移行 相槌時にもアバターが口を動かす、より自然な会話体験
FALLBACK 動的 TTS 回答プールに該当がない場合、Gemini TTS でリアルタイム音声生成 → HeyGen に送信 回答プール外の質問にも音声付きで対応可能に
各種デバイス対応 PROTOデバイス / iOS Safari などでのレイアウト最適化 PROTOデバイスやスマホでのデモ実施