GEMINI LABEN
SIRI — WWDC 2026で刷新版SiriがGoogle Geminiモデルで動くと確定。ただしEUではDMAによりiOS 27時点で提供されませんFLASH3.5 — Gemini 3.5 FlashがGA。エージェント・コーディングで持続的なフロンティア性能を発揮する最上位FlashモデルですIMAGE-GA — Gemini 3.1 Flash Image / 3.1 Pro Imageがネイティブ視覚モデルとしてGA。preview版は6/25に終了予定MANAGED-AGENTS — Gemini APIでManaged Agentsが公開プレビュー。Googleホストの隔離Linuxサンドボックスで自律エージェントを構築できますFILE-SEARCH — File Searchがマルチモーダル対応。gemini-embedding-2で画像のネイティブ埋め込み・検索が可能になりましたDEPRECATION — gemini-3.1-flash-image-preview / gemini-3-pro-image-previewは6/25に停止。GA版への移行をお早めにSIRI — WWDC 2026で刷新版SiriがGoogle Geminiモデルで動くと確定。ただしEUではDMAによりiOS 27時点で提供されませんFLASH3.5 — Gemini 3.5 FlashがGA。エージェント・コーディングで持続的なフロンティア性能を発揮する最上位FlashモデルですIMAGE-GA — Gemini 3.1 Flash Image / 3.1 Pro Imageがネイティブ視覚モデルとしてGA。preview版は6/25に終了予定MANAGED-AGENTS — Gemini APIでManaged Agentsが公開プレビュー。Googleホストの隔離Linuxサンドボックスで自律エージェントを構築できますFILE-SEARCH — File Searchがマルチモーダル対応。gemini-embedding-2で画像のネイティブ埋め込み・検索が可能になりましたDEPRECATION — gemini-3.1-flash-image-preview / gemini-3-pro-image-previewは6/25に停止。GA版への移行をお早めに
記事一覧/API / SDK
API / SDK/2026-03-29上級

Gemini Live API で感情認識音声アプリを構築する

Gemini 3.1 Flash Live の Live API を使い、ユーザーの感情をリアルタイムで分析・応答する音声アプリケーションを構築する方法を、設計パターンから実装まで体系的に解説します。

gemini-api286live-api5voice-appemotion-recognitionreal-time3python131

取り組みの背景 — 感情を理解する音声アプリの時代

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 established

Step 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つのアーキテクチャを参考に、まずはプロトタイプから構築を始めてみてください。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

API / SDK2026-06-01
Gemini 2.5/3 で本文が空なのに finish_reason が MAX_TOKENS になるときの原因と対処
プロンプトはほんの数行なのに、maxOutputTokens を絞った gemini-2.5-flash が空文字を返し finish_reason が MAX_TOKENS になる — 犯人は思考トークンです。原因と3通りの対処を実装コードで整理します。
API / SDK2026-05-30
Gemini 2.5 Pro で thinkingBudget を 0 にすると INVALID_ARGUMENT になる原因と対処
Gemini 2.5 Pro で thinkingBudget を 0 にすると 400 INVALID_ARGUMENT が返る原因を、モデルごとの思考予算レンジの違いから解説します。Pro でレイテンシを抑える正しい書き方と Flash への切り替え判断を Python・JavaScript のコード付きで紹介します。
API / SDK2026-05-28
Gemini API の chat で send_message ごとに温度やトークン上限が変わらない原因
google-genai SDK の chat セッションで send_message ごとに generationConfig や temperature を渡しているのに、応答の挙動が変わらない・前の設定が引きずられる場合の切り分け手順をまとめました。SDK の設計意図を踏まえた回避策と、現場で使っているラッパー実装まで動くコードで整理しています。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →