LiveKit連携
LiveKit連携
LiveKit Agentsワーカーを使用して、LiveKitルームをSpeech Engineに接続します。
このガイドでは、ElevenLabs Speech EngineをLiveKitルームの音声レイヤーとして使用する方法を紹介します。LiveKit Agentsワーカーは参加者としてルームに参加し、ユーザーのオーディオトラックを購読してSpeech EngineへのWebSocketを開き、Speech Engineが合成したオーディオを独自のトラックとしてルームに公開します。
アーキテクチャ
Speech Engineは2種類のWebSocket接続を受け付けます。
- ElevenLabs APIが接続するブレインWebSocket。サーバーでSpeech Engine SDK(
engine.serve()/engine.attach())を実行し、応答するための文字起こしを受け取ります。 - クライアントが接続する会話WebSocket。ブラウザはWebRTCトークン経由で接続します。LiveKit Agentsワーカーなどの非ブラウザクライアントは、署名付きURL経由で接続し、未加工のPCMオーディオを双方向にストリーミングします。
LiveKitワーカーは2つ目の接続を使用します。LiveKitルーム内の参加者に代わって、Speech Engineの「クライアント」として動作します。
ブレインサーバーはSpeech Engineクイックスタートから変更不要です。LiveKitワーカーがブラウザの代わりにオーディオソースとなりますが、LLMロジックはそのままです。
このパターンを使う場面
ルーム自体が体験の一部である場合は、LiveKitブリッジを使用します。
- 複数のユーザーが同時にエージェントと会話するマルチ参加者セッション
- トランスポートを切り替えるとクライアントが機能しなくなる、既存のLiveKitデプロイメント
- 画面共有、ビデオ、テキストチャットとルームを共有する音声エージェント
- 通話中にAIエージェントが必要な、SIPからLiveKitへディスパッチされる通話
他の参加者なしでブラウザからSpeech Engineへの音声ループだけが必要な場合は、Speech EngineクイックスタートのWebRTCクライアントのほうがシンプルです。Speech Engineがブラウザと直接WebRTCで通信するため、LiveKitルームは不要です。
前提条件
- LiveKitプロジェクト(LiveKit Cloudまたはセルフホストサーバー)。ワーカーには
LIVEKIT_URL、LIVEKIT_API_KEY、LIVEKIT_API_SECRETが必要です。 - ElevenLabs Speech Engine。Speech Engineクイックスタートに従って作成し、ブレインサーバーを実行してください。
- Python 3.9+またはNode.js 18+。
Nodeブリッジワーカーは
@livekit/rtc-nodeを使用します。これは現在
Developer Previewです。本番環境へのデプロイには、Pythonワーカーを推奨します。
Speech Engineのオーディオ形式を設定する
LiveKitのAudioStreamは、受信するOpusトラックを指定したPCMサンプルレートにリサンプリングします。そのため、Speech Engineの入力に直接合わせられます。Speech Engineを更新し、ASR入力には16 kHz PCMを受け入れ、TTS出力には24 kHz PCMを出力するようにします。
Speech EngineのPCMは、全体を通して符号付き16ビット・リトルエンディアンです。その他の対応レートについては、オーディオ形式リファレンスを参照してください。
ブリッジワーカーを構築する
ワーカーは、LiveKitサーバーに接続してジョブを待機し、割り当てられたルームに参加して、ルームとSpeech Engineの間でオーディオをブリッジする長時間実行プロセスです。
Speech Engineの署名付きURLを生成する
ワーカーはSpeech Engine会話WebSocket用の短期間有効な署名付きURLをリクエストします。署名付きURLにはエンジンIDと1回限りの署名が含まれるため、APIキーを公開せずにWebSocketを開けます。
ワーカーのエントリーポイントを定義する
ワーカーがルームにディスパッチされるたびに、そのエントリーポイントが実行されます。エントリーポイントはルームに接続し、Speech Engine会話WebSocketを開き、2つのオーディオブリッジを開始します。1つはSpeech Engineに送る発信者オーディオ用、もう1つは戻ってくる合成オーディオ用です。
ワーカーは、track_subscribedハンドラーでローカル参加者のIDと比較することで、自身が公開したオーディオを除外します。このチェックがない場合、ワーカーは自身が合成したオーディオをSpeech Engineへ送り返そうとしてしまいます。
正しく動作させるためには、順序に関する2つの重要なポイントがあります。
- リスナーのタイミング:
TrackSubscribedはctx.connect()の前に登録します。LiveKitは接続ハンドシェイク中に既存トラックを自動購読するため、その後に登録したリスナーはイベントを見逃す可能性があります。オーディオポンプはSpeech Engine WebSocketのFuture/Promiseを待機するため、すぐに購読でき、接続が開いた直後からオーディオを転送できます。 - TypeScriptのみ:キャプチャの直列化:
@livekit/rtc-nodeのAudioSource.captureFrameは、同時に呼び出すとInvalidStateをスローします。TypeScriptハンドラーでは、Promiseチェーンを使ってキャプチャを直列化します。Pythonの単一のasync for el_to_roomループは自然に順次実行されるため、これは不要です。
ワーカーをルームにディスパッチする
ワーカーにはagent_nameがあるため、明示的なディスパッチを使用します。バックエンドから指示された場合にのみルームに参加します。最も簡単なパターンは、ブラウザが接続に使用するLiveKitアクセストークンにRoomAgentDispatchを含めることです。
ブラウザがこのトークンを使用してルームを作成または参加すると、LiveKitは同じルームにブリッジワーカーを自動的にディスパッチします。
ブラウザから接続する
ブラウザに必要なのは標準のLiveKitクライアントのみです。Speech Engineとは直接やり取りしません。
ボタンをクリックすると、ブラウザはLiveKitトークンを取得し、マイクを有効にしてルームに参加し、エージェントのオーディオトラックの受信を開始します。ワーカーがディスパッチされてSpeech Engineセッションを開き、双方向にオーディオをブリッジします。
オーディオ形式リファレンス
Speech Engineは以下のオーディオ形式に対応しています。エンジンではasr.user_input_audio_formatとtts.agent_output_audio_formatで設定します。
LiveKitのAudioStreamとAudioSourceはリサンプリングを自動的に処理します。AudioStreamには任意のサンプルレートをリクエストでき、SDKが基盤となる48 kHz Opusトラックから変換します。
本番運用時の考慮事項
- 明示的なディスパッチ:
WorkerOptionsでは必ずagent_name/agentNameを設定してください。自動ディスパッチでは、LiveKitプロジェクトで作成されるすべてのルームに対してワーカーが起動します。これは通常、意図した動作ではありません。 - ブレインサーバーの認証:Speech Engineに共有シークレットを設定し、ブレインサーバーで検証してください。これにより、Speech Engineだけがエンドポイントにアクセスできるようになります:
ブレインサーバーは、WebSocketアップグレードを受け入れる前に
request.headers["x-api-key"]を確認します。 - トークンサーバー:LiveKitとSpeech Engineのトークンはサーバー側で発行してください。
LIVEKIT_API_SECRETやELEVENLABS_API_KEYをブラウザーに公開しないでください。 - イベントループの健全性:CPU負荷の高い処理をワーカーのイベントループで実行しないでください。
AudioSource.capture_frameとAudioStreamのイテレーションは時間に敏感です。長時間の同期呼び出しにより、割り込みイベントが遅延したり失われたりする可能性があります。ブロッキング処理にはasyncio.to_thread()(Python)またはworker_threads(Node)を使用してください。 - シャットダウン:
ctx.add_shutdown_callback/ctx.addShutdownCallbackを登録し、ElevenLabs WebSocketを適切に閉じてください。デフォルトでは、最後の非エージェント参加者が退出すると、ルーム(およびジョブ)は終了します。