Twilio Voice、OpenAIのRealtime API、Node.jsを使用してAI音声アシスタントを構築
Time to read:
Twilio Voice、OpenAIのRealtime API、Node.jsを使用してAI音声アシスタントを構築
OpenAIからRealtime APIがリリースされました。このAPIは、GPT Realtimeマルチモーダルモデルに音声変換(S2S)機能を提供します。音声を直接入力・出力できるため、従来のように音声認識(STT)でテキストに変換してからテキスト読み上げ(TTS)により音声に戻すといった処理は不要です。
その結果、S2Sモデルはレイテンシーを低減し、OpenAIのRealtime APIはまるで人間同士の会話のような自然なやり取りを実現します。実際にお試しいただくと、その違いが分かるはずです。そのため、OpenAIとのコラボレーションによって実現したこの統合機能を、今回のリリースに合わせて皆さまに提供できることを大変うれしく思います。
このチュートリアルでは、Node.jsを使用し、Twilio VoiceとOpenAI Realtime APIでAI音声アシスタントを構築する方法を解説します。ツールの構築後は、人間と同じようにAIアシスタントと会話し、事実関係の質問はもちろん、ジョークをお願いするといったやり取りも行えるようになります。一緒にTwilio Media Streamサーバーを設定しましょう。通話の音声を受信し、それをOpenAI Realtime APIで処理した後、AIの応答をTwilioに返すことで会話を継続できます。
準備が整ったら、さぁ、始めましょう!
前提条件
このチュートリアルを進めるには、以下が必要です。
- Node.js 18+(このチュートリアルではバージョン
18.20.4を使用。ダウンロードはこちら) - Twilioアカウント。お持ちでない場合は、こちらから無料トライアルに登録できます。
- Voice機能を備えたTwilio番号。電話番号の購入手順はこちら。
- OpenAIアカウントとOpenAI APIキー。こちらからサインアップできます。
- OpenAI Realtime APIアクセス。詳細はこちらをご覧ください。
- (オプション)ngrokなどのトンネリングソリューションを使用してローカルサーバーをインターネットに公開し、テストを行います。ngrokはこちらからダウンロードしてください。
- 発信通話が可能な携帯電話または固定電話
これで準備は完了です。構築を始めましょう。
Realtime API speech-to-speech Nodeプロジェクトの設定
以下のステップでは、プロジェクトの設定、依存関係のインストール、TwilioとOpenAI間のWebSocket接続中継に必要なコードの作成について説明します。
こちらでTwilioのレポジトリもご覧いただけます。このチュートリアルのビデオバージョンも以下にご用意しています。
ステップ2: 依存関係のインストール
次に、プロジェクトに必要な依存関係をインストールします。Webフレームワーク「Fastify」を使用し、WebSocketのサポートも必要になります。機密性の高い環境変数は.envファイルに保存します。
ステップ3: プロジェクトファイルの作成
メインコード用にindex.jsという名前のファイルを作成します。環境変数を保存するための.envファイルも用意します。この手法の詳細については、こちらをご覧ください。
この例では、.envにはOpenAIのAPIキーのみが必要です。このキーにRealtime APIへのアクセス権限があることを確認してください。
ステップ3.1: .envファイルの作成
まず、.envファイルを作成します。
次に、任意のテキストエディタで、1行目にOPENAI_API_KEYを追加します。
ステップ3.2: index.jsファイルの作成
次に、プロジェクトディレクトリに新しいファイルindex.jsを作成します。
ステップ4: サーバーコードの構築
ここまで順調に進めてきました。ここからはチュートリアルの内容に入ります。index.jsのコードを複数のステップに分け、それぞれのパートを説明していきます。ここを飛ばして先に進みたい場合はCode Exchangeアプリやリポジトリに切り替えても構いません。
ステップ4.1: 依存関係のインポート、環境変数の読み込み、Fastifyの初期化
ここはほぼ通常通りです。必要なモジュールをインポートしてからパス解決を設定し、.envファイルから環境変数を読み込みます。
以下のコードをindex.jsに貼り付けてください。
ステップ4.2: 定数の定義
次に、システムメッセージ、音声、サーバーポートの定数を定義します。また、コンソールにログ出力するOpenAIのイベントもここで選択します。
以下のコードをファイルに貼り付けてください。
ここで、SYSTEM_MESSAGEはAIの会話のトーンや振る舞いを設定します。この内容が、最終的にinstructionsとしてOpenAIに渡されます。このメッセージをカスタマイズして、AIの性格や対応スタイルなどを指定できます。もう少し先のセクションで、会話に影響を与えるこのメッセージをOpenAIに渡す方法を説明します。プロンプトの詳細については、OpenAIのRealtime Prompting Guideを参照してください。
VOICE定数はAIの音声を指定します。こちらから音声を選択できます。
TEMPERATUREは、LLMの応答のランダム性を制御します(値が高いほどランダム性が高くなります)。
PORT定数は、アプリケーションが開くポートを指定します。これについては、後ほどngrokセクションで詳しく説明します。
最後のLOG_EVENT_TYPESは、OpenAIのイベントのうち、コマンドラインに表示するイベントタイプを指定します。全イベント一覧は、OpenAIのRealtime APIドキュメントで確認できます。
ステップ4.3: 2つのルートの定義
さて、ここからがコードの核心部分です。最上位のルート(主にヘルスチェック用)と、着信を処理するルートを定義します。この/incoming-callルートは、TwiML(Twilio Markup Language)を返し、通話の処理方法をTwilioに指示します。詳細は後ほど説明します。
LOG_EVENT_TYPESの定数リストを設定したら、以下のコードをindex.jsに貼り付けます。
他のTwiMLと同様に、まずXMLバージョンから開始し、<Response>タグを開きます。次に、Twilioに発信者への短いメッセージ(自由に決めてください)を言わせ、2秒間待ってから、発信者に話し始めるよう促します。
動詞<Connect>と名詞<Stream>を組み合わせ、TwilioのMedia Streamsを使用して双方向ストリームを開きます。ここがデモの真骨頂です。次のステップでは、2つのWebSocket間で音声を中継する方法を紹介します。
ステップ4.4: WebSocket接続の処理
ここで、メディアストリーミング用のWebSocketルート(前のセクションでTwilioに対して定義したルート)を設定し、TwilioとOpenAIの両方に対してWebSocket接続を確立する必要があります。少し長いコードですが、コードブロックの後で詳しく説明します。
以下のコードをルート定義の下に貼り付けてください。
上記のとおり、まずWebSocketのルート(/media-stream)を設定し、TwilioとOpenAI間のメディアストリーミングを処理できるようにします。これは、先ほどTwiMLの説明の中で参照したルートです。続く2つの領域については、もう少し詳しい説明が必要です。
OpenAI Realtime APIのセッションと会話の設定
次に、OpenAIとのセッションを設定します。この設定は、接続が開いた後、少し遅れてJSONオブジェクトとしてOpenAIのWebSocketに送信されます。
続いて、AIとのやり取りやAIの応答をsendSessionUpdate()関数で定義します。ここで選んだオプションの詳細は、OpenAIのRealtime APIドキュメントで確認できます。
sendSessionUpdate関数でOpenAIセッションの属性も設定します。
type: ここでrealtimeを使用していることをOpenAIに伝えますmodel: spt-realtimeモデルを使用していますaudio.input.turn_detection: server_vadでサーバー側の音声活動検出(VAD)を有効にします。audio.input.format.type/audio.output.format.type: オーディオ形式を指定します。ここではTwilioの要件に合わせてaudio/pcmuに変更しています。- audio.output.voice: 先ほど設定したVOICEモデル。
instructions:SYSTEM_MESSAGEを使用してAIの対応に影響を与えます。output_modalities: 音声コミュニケーションを有効にします。
TwilioとOpenAIのWebSocket間の中継
以下の行では、Twilio Media StreamとOpenAI Realtime AIのWebSocket接続間で、TwilioがサポートするG.711 u-law形式の音声データを中継します。通話が開始されると、ここで発信者の音声が処理され、AIが生成した音声ストリームが返されます。
OpenAI RealtimeとTwilio間で中継する方法について、詳しい手順を以下に説明します。
startイベント: ストリーム固有のID(streamSid)を取得します。mediaイベント:mediaイベント: 通話中に送られてくる音声データ(ペイロード)を処理し、OpenAIに転送します。response.output_audio.delta: OpenAIからの音声データを処理し、再エンコードしてTwilioに送信します。- Twilio WebSocket
closeイベント: クライアントの切断を処理し、ストリームを閉じます。
ステップ4.5: サーバーの準備
最後に、Fastifyサーバーを起動して仕上げます(渡された、または定義されたポートを使用)。既存のコードの下に以下を貼り付ければ準備完了です!
サーバーの実行
ファイルを閉じて、元の画面に戻ります。
次のコマンドでサーバーを起動できるようになりました。
サーバーが正常に起動すると、ターミナルに[Server is listening on port 5050](サーバーがポート5050で待ち受けています)と表示されます(ポート番号を指定した場合はその番号が表示されます)。
セットアップの仕上げ
では、Twilioに指示を与えて配線を完了させましょう。リバースプロキシのngrokを使用してサーバーを公開する方法を説明します。
ステップ5: ngrokを使用してサーバーをTwilioに公開
ローカルサーバーをインターネット上に公開するには、ngrokのようなサービス(あるいはVPSなど)を使用する必要があります。Twilioがお客様のサーバーにリクエストを送信し、指示を受け取るには、公開URLが必要です。
ngrokのダウンロードとインストールがまだの場合は、完了後に次のコマンドを実行してください。ポート番号を5050以外に変更している場合は、ここでも必ず同じ番号を指定してください。
これで公開用URL(例:https://abc123.ngrok.io)を取得し、テストに使用できるようになります。この画面は次のような表示になりました。
ステップ6: Twilioの設定
ここまでくれば、あと一歩です。
Twilio Consoleにアクセスし、音声対応の番号を選択します。
[Voice & Fax](音声とFAX)セクションで、[A Call Comes In](着信)WebhookをngrokのURLに設定します(Forwardingの行に表示されているURL、このスクリーンショットではhttps://ad745c4093d9.ngrok.appで、その末尾に/incoming-callを付けます)。例えば、私の例ではhttps://ad745c4093d9.ngrok.app/incoming-callと入力します。
変更を保存します。これが最後のステップです!
設定をテストしましょう!
ngrokセッションが実行中であり、サーバーが稼働していることを確認してください。確認できたら、携帯電話または固定電話からTwilio番号に電話をかけます。
サーバーは通話を処理し(TwiMLをTwilioに提供)、OpenAI Realtime APIとTwilioのWebSocketを中継します。話しかけてみてください。AIのシステムメッセージが聞こえ、対話できるはずです。
よくある問題とトラブルシューティング
設定に不備がある(ただしサーバーは稼働している)場合は、まず以下の点を確認してください。
- ngrokは動作していますか? Voice Configuration -> A Call Comes InセクションのURLは正しく設定されていますか?
- Twilio側のエラー(TwiMLの問題など)はありませんか?Twilioのエラーはいくつかの方法でデバッグできます。詳細はこちらの記事をご覧ください。
- コードはOpenAIを正しく呼び出していますか?詳細はOpenAIのドキュメントを参照してください。
まとめ
素晴らしい仕組みではないでしょうか。Twilio VoiceとOpenAI Realtime APIを用いた、AI音声アシスタントの構築に成功しました。これで、ユーザーの入力にほぼリアルタイムで応答できる、動的で低レイテンシーのインタラクティブ音声アプリケーションの完成です。必要なときにいつでも呼び出せる、信頼性の高い音声インターフェースを利用できるようになりました。
皆さんが構築したアシスタントと対話できる日を楽しみにしています。
次のステップ:
- より高度な機能については、TwilioのドキュメントとOpenAIのRealtime APIドキュメントをご覧ください。
- 概念については、OpenAIのドキュメントを参照してください。
Paul KampはTwilioブログのチーフテクニカルエディターです。彼の連絡先はpkamp [at] twilio.comです(彼のAIアシスタントが対応するかもしれません)。
Dominik KundelはOpenAIのDeveloper Experience担当です。