Gemini Multimodal Live APIを使ってみる

AI

ステップ1:Google Cloud (Vertex AI) の準備

  1. プロジェクトの作成: Google Cloud Consoleでプロジェクトを作成。
  2. APIの有効化: Vertex AI API を有効にします。
  3. サービスアカウントの発行: 独自APIからGoogleへ接続するための「鍵」を作ります。
    • 役割:Vertex AI User 権限を付与。
    • キー形式:JSON でダウンロードし、独自APIの環境変数に設定します。

ステップ2:バックエンドAPIとGeminiの中継の実装

  1. WebSocketサーバーの構築: Next.jsからの接続を待機します。
  2. Google認証の実装: サービスアカウントを使って OAuth 2.0 のアクセストークンを取得します。
  3. Geminiへの接続: 取得したトークンを使って、GoogleのLive APIエンドポイント(wss://...)へWebSocketを繋ぎます。
  4. 中継:
    • フロントから来た音声バイナリを realtime_input イベントとしてGeminiへ投げる。
    • Geminiから来た server_content(音声・テキスト)をフロントへ返す。

使用するモデル: gemini-2.5-flash-native-audio-latest

"""利用可能な Gemini モデルを一覧し、bidiGenerateContent 対応を確認する"""

import google.auth
import google.auth.transport.requests
import requests as http_requests
from google.oauth2 import service_account

from src.core.config import settings

SCOPES = ["https://www.googleapis.com/auth/generative-language"]


def main():
    if settings.GOOGLE_SERVICE_ACCOUNT_FILE:
        creds = service_account.Credentials.from_service_account_file(
            settings.GOOGLE_SERVICE_ACCOUNT_FILE, scopes=SCOPES
        )
    else:
        creds, _ = google.auth.default(scopes=SCOPES)

    creds.refresh(google.auth.transport.requests.Request())

    resp = http_requests.get(
        "https://generativelanguage.googleapis.com/v1beta/models",
        headers={"Authorization": f"Bearer {creds.token}"},
    )
    models = resp.json().get("models", [])

    print(f"Found {len(models)} models\n")
    for m in models:
        methods = m.get("supportedGenerationMethods", [])
        if "bidiGenerateContent" in methods:
            print(f"  ✓ {m['name']}  ({', '.join(methods)})")


if __name__ == "__main__":
    main()

動作確認スクリプト

"""
Voice WebSocket テスト用スクリプト

使い方:
  Firebase トークン取得:
     const { getAuth } = await import('firebase/auth')
     const token = await getAuth().currentUser.getIdToken()
     console.log(token)

  実行:
    poetry run python scripts/test_voice_ws.py <token>
"""

import asyncio
import json
import sys

import websockets


async def test(token: str):
    uri = f"ws://localhost:8000/api/v1/voice/ws?token={token}"
    print(f"Connecting to {uri[:80]}...")

    async with websockets.connect(uri) as ws:
        print("Connected!")

        # テキストメッセージを送信
        msg = {"type": "text", "data": "こんにちは。自己紹介してください。"}
        await ws.send(json.dumps(msg))
        print(f"Sent: {msg}")

        # 応答を受信(複数メッセージの可能性あり)
        print("Waiting for response...")
        audio_chunks = 0
        try:
            for i in range(30):
                response = await asyncio.wait_for(ws.recv(), timeout=10)
                data = json.loads(response)

                # 音声データが含まれているか確認
                parts = (
                    data.get("serverContent", {})
                    .get("modelTurn", {})
                    .get("parts", [])
                )
                for part in parts:
                    if "inlineData" in part:
                        audio_chunks += 1
                        mime = part["inlineData"].get("mimeType", "")
                        size = len(part["inlineData"].get("data", ""))
                        print(f"  Audio chunk [{audio_chunks}]: {mime}, {size} bytes (base64)")
                    elif "text" in part:
                        print(f"  Text: {part['text']}")

                # ターン完了チェック
                if data.get("serverContent", {}).get("turnComplete"):
                    print(f"\nTurn complete! ({audio_chunks} audio chunks received)")
                    break
        except asyncio.TimeoutError:
            print("\nNo more responses (timeout)")
        except websockets.ConnectionClosed as e:
            print(f"\nConnection closed: {e}")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: poetry run python scripts/test_voice_ws.py <firebase_token>")
        sys.exit(1)

    asyncio.run(test(sys.argv[1]))

ステップ3:Next.js(フロントエンド)の実装

  1. マイク音声の取得: navigator.mediaDevices.getUserMedia を使用。
  2. 音声の加工(重要): * Geminiの要求(16kHz PCM)に合わせて、ブラウザ側でリサンプリングを行います。
  3. WebSocket通信: 独自APIへ接続し、加工した音声を Base64 で送信します。
  4. 音声再生: サーバーから届く音声データを AudioContext を使って順番に再生(ストリーミング再生)します。

結果

ウェブアプリの実装は載せませんが、この方法で、Geminiと会話することができた。

あなたの名前はと聞いたら、私はGeminiですと答えてました。

関連記事

カテゴリー

アーカイブ

Lang »