取り組みの背景 — 感情を理解する音声アプリの時代
Gemini 3.1 Flash Live の登場により、「ユーザーの感情を理解して応答を変える」音声アプリケーションの構築が現実的になりましました。従来の音声AIは発話内容のテキスト解析が中心でしたが、Flash Live は声の高さ・速度・抑揚といった音響特徴から感情を推定する能力を備えています。
前提条件と環境構築
必要な環境
- Python 3.11 以上
- Google AI Studio のAPIキー(Gemini Live API プレビューへのアクセス権が必要)
pyaudioまたはsounddevice(マイク入力用)- WebSocket 対応ライブラリ
セットアップ
# 必要なライブラリのインストール
pip install google-generativeai pyaudio websockets numpy
# 環境変数の設定
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"Live API のアーキテクチャ理解
通信モデル
Gemini Live API はWebSocketベースの双方向リアルタイム通信を採用しています。HTTPのリクエスト/レスポンスモデルとは異なり、音声データを連続的にストリーミングしながら、同時に応答を受け取ることができます。
[クライアント] ←WebSocket→ [Gemini Live API]
│ │
├─ 音声チャンク送信 ──────→ │
│ ├─ リアルタイム解析
├─ 音声チャンク送信 ──────→ │
│ ├─ 感情推定 + テキスト化
│ ←───── 応答(音声+テキスト)┤
│ │
セッションライフサイクル
Live API のセッションは以下のライフサイクルで動作します。
- 接続(Connect): WebSocket接続を確立し、モデルとセッション設定を送信
- 設定(Setup): 音声形式、応答モダリティ、声のプリセットを設定
- ストリーミング(Stream): 音声チャンクの送受信を継続
- 切断(Disconnect): セッションを終了しリソースを解放
基本実装: リアルタイム音声会話
Step 1: セッションの確立
import google.generativeai as genai
import os
genai.configure(api_key=os.environ["GEMINI_API_KEY"])
model = genai.GenerativeModel("gemini-3.1-flash-live")
# セッション設定
session_config = {
"response_modalities": ["AUDIO", "TEXT"],
"speech_config": {
"voice_config": {
"prebuilt_voice_config": {
"voice_name": "Aoede"
}
}
},
"system_instruction": """
あなたは感情に配慮した対話アシスタントです。
ユーザーの声のトーンや話し方から感情を推定し、
それに応じた口調と内容で応答してください。
焦っている場合は簡潔に、落ち込んでいる場合は
優しく励ますように応答します。
"""
}
session = model.start_live_session(config=session_config)
print("Session established")
# 出力: Session establishedStep 2: 音声入出力のハンドリング
import pyaudio
import numpy as np
import threading
# オーディオ設定
RATE = 16000 # サンプリングレート(16kHz)
CHUNK = 1024 # チャンクサイズ
FORMAT = pyaudio.paInt16
CHANNELS = 1
audio = pyaudio.PyAudio()
# マイク入力ストリーム
input_stream = audio.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK
)
# スピーカー出力ストリーム
output_stream = audio.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
output=True,
frames_per_buffer=CHUNK
)
def send_audio(session):
"""マイクからの音声をLive APIに送信"""
while True:
data = input_stream.read(CHUNK, exception_on_overflow=False)
audio_array = np.frombuffer(data, dtype=np.int16)
# 無音検出(閾値以下はスキップ)
if np.abs(audio_array).mean() < 100:
continue
session.send_audio(data)
def receive_response(session):
"""Live APIからの応答を受信・再生"""
for response in session.receive():
if response.audio:
output_stream.write(response.audio)
if response.text:
print(f"[Gemini] {response.text}")
# 送受信を別スレッドで並行実行
send_thread = threading.Thread(target=send_audio, args=(session,))
recv_thread = threading.Thread(target=receive_response, args=(session,))
send_thread.start()
recv_thread.start()感情認識レイヤーの設計パターン
Flash Live の感情認識能力を活用するには、システムプロンプトの設計と応答制御の2つのレイヤーで工夫が必要です。
パターン1: システムプロンプト駆動型
最もシンプルなアプローチです。システムプロンプトに感情対応のルールを記述し、モデルの内部判断に委ねます。
EMOTION_AWARE_PROMPT = """
あなたは共感力の高い対話アシスタントです。
以下のルールに従って応答してください:
1. ユーザーが早口で緊張している場合:
- 落ち着いたトーンで応答する
- 要点を簡潔に伝える
- 「大丈夫ですよ」のような安心感を与える表現を使う
2. ユーザーが落ち込んでいる場合:
- 共感を示してから情報を提供する
- 「つらいですよね」のような受容的な表現を使う
- 解決策は押し付けず、選択肢として提示する
3. ユーザーが楽しそうな場合:
- テンションを合わせて明るく応答する
- ユーモアを交えた表現を使う
4. ユーザーが怒っている場合:
- まず謝罪と共感を示す
- 問題解決に集中した簡潔な応答をする
- 弁解ではなく具体的な解決策を提示する
"""パターン2: メタデータ抽出型
モデルに感情のメタデータを構造化して返させ、アプリケーション側で応答ロジックを制御するパターンです。より細かい制御が可能です。
METADATA_EXTRACTION_PROMPT = """
ユーザーの音声に対して、以下の2つを返してください:
1. JSON形式の感情メタデータ(テキスト応答の先頭に配置):
{
"emotion": "happy|sad|angry|anxious|neutral",
"confidence": 0.0-1.0,
"energy_level": "high|medium|low"
}
2. その後に通常の応答テキスト
例:
{"emotion": "anxious", "confidence": 0.8, "energy_level": "high"}
お急ぎのようですね。ご質問にすぐお答えします。
"""
import json
def process_response_with_emotion(response_text):
"""応答テキストから感情メタデータを抽出"""
lines = response_text.strip().split("\n", 1)
try:
emotion_data = json.loads(lines[0])
reply_text = lines[1] if len(lines) > 1 else ""
return emotion_data, reply_text
except json.JSONDecodeError:
return {"emotion": "neutral", "confidence": 0.5}, response_text
# 使用例
emotion, reply = process_response_with_emotion(response.text)
print(f"Detected emotion: {emotion['emotion']} ({emotion['confidence']:.0%})")
print(f"Reply: {reply}")
# 出力例:
# Detected emotion: anxious (80%)
# Reply: お急ぎのようですね。ご質問にすぐお答えします。パターン3: マルチターン感情トラッキング
会話全体を通じて感情の推移を追跡し、長期的なパターンに基づいて応答を調整するパターンです。
from collections import deque
from datetime import datetime
class EmotionTracker:
"""会話中の感情推移を追跡するクラス"""
def __init__(self, window_size=10):
self.history = deque(maxlen=window_size)
def add(self, emotion_data):
self.history.append({
"timestamp": datetime.now().isoformat(),
**emotion_data
})
def get_trend(self):
"""感情のトレンドを分析"""
if len(self.history) < 3:
return "insufficient_data"
recent = list(self.history)[-3:]
emotions = [e["emotion"] for e in recent]
# 一貫してネガティブなら、特別な対応が必要
if all(e in ("sad", "angry", "anxious") for e in emotions):
return "persistent_negative"
# 改善傾向
if emotions[-1] in ("happy", "neutral") and emotions[0] in ("sad", "angry"):
return "improving"
return "stable"
def should_escalate(self):
"""エスカレーション判定"""
trend = self.get_trend()
return trend == "persistent_negative"
# 使用例
tracker = EmotionTracker()
tracker.add({"emotion": "anxious", "confidence": 0.8, "energy_level": "high"})
tracker.add({"emotion": "angry", "confidence": 0.7, "energy_level": "high"})
tracker.add({"emotion": "angry", "confidence": 0.9, "energy_level": "high"})
if tracker.should_escalate():
print("Escalation recommended: persistent negative emotion detected")
# 出力: Escalation recommended: persistent negative emotion detectedユースケース別アーキテクチャ
ユースケース1: カスタマーサポート
[顧客の音声] → [Flash Live API]
│
├── 感情分析 → [EmotionTracker]
│ │
│ escalation判定
│ │
│ ├─ 通常 → AIが応答
│ └─ 要エスカレ → 人間オペレータに転送
│
└── テキスト化 → [ナレッジベース検索]
│
[回答生成・音声合成]
ユースケース2: 教育アプリ
[学習者の音声] → [Flash Live API]
│
├── 理解度推定(声のトーンから)
│ │
│ ├─ 理解している → 次のトピックへ
│ ├─ 困惑している → 別の説明アプローチ
│ └─ 退屈している → インタラクティブな演習へ
│
└── 質問内容の分析 → [教材データベース]
ユースケース3: ヘルスケア(ウェルネスチェック)
[利用者の音声] → [Flash Live API]
│
├── 感情トレンド分析
│ │
│ ├─ 安定 → 通常の対話継続
│ ├─ 改善傾向 → ポジティブフィードバック
│ └─ 持続的ネガティブ → 専門家への相談を提案
│
└── 日次レポート生成 → [ダッシュボード]
パフォーマンス最適化
レイテンシの最小化
Live API で自然な会話体験を実現するには、エンドツーエンドのレイテンシを200ms以下に抑えることが理想です。
- 音声チャンクサイズ: 小さいチャンク(512〜1024サンプル)で送信し、認識の応答性を確保する
- バッファリング戦略: 入力バッファを最小限にし、ネットワーク遅延に対しては出力バッファで吸収する
- リージョン選択: APIエンドポイントは地理的に近いリージョンを選択する
音声品質の確保
# ノイズゲート: 環境ノイズを除去
def apply_noise_gate(audio_chunk, threshold=200):
"""閾値以下の音声を無音に置換"""
audio_array = np.frombuffer(audio_chunk, dtype=np.int16)
mask = np.abs(audio_array) < threshold
audio_array[mask] = 0
return audio_array.tobytes()
# 音量正規化: 入力レベルを一定に保つ
def normalize_volume(audio_chunk, target_rms=3000):
"""RMS値を基準に音量を正規化"""
audio_array = np.frombuffer(audio_chunk, dtype=np.int16).astype(np.float32)
current_rms = np.sqrt(np.mean(audio_array ** 2))
if current_rms > 0:
gain = target_rms / current_rms
audio_array = np.clip(audio_array * gain, -32768, 32767)
return audio_array.astype(np.int16).tobytes()セキュリティとプライバシーの考慮事項
音声データは個人情報の中でも特にセンシティブなカテゴリに属します。実装時には以下の点に注意してください。
- 音声データの保存: Live API は基本的にストリーミング処理であり、サーバー側に音声データを永続保存しない設計にする
- 感情データの取り扱い: 感情分析結果は匿名化して保存し、個人と紐付けない
- ユーザーへの開示: 感情分析を行っていることをユーザーに明示し、同意を得る
- データの暗号化: WebSocket通信はTLS(wss://)で暗号化する
まとめ
Gemini 3.1 Flash Live の Live API は、音声AIアプリケーションの構築に新しい次元を加えます。音響的な感情理解、128,000トークンのコンテキスト維持、リアルタイムストリーミングの3つの特徴を組み合わせることで、カスタマーサポート・教育・ヘルスケアなど多様な分野で感情対応型の音声アプリを実現できます。
本記事で紹介した3つの設計パターン(プロンプト駆動型・メタデータ抽出型・マルチターントラッキング)と3つのアーキテクチャを参考に、まずはプロトタイプから構築を始めてみてください。