Twilio Voice、OpenAIのRealtime API、Pythonを使用してAI音声アシスタントを構築
Time to read:
Twilio Voice、OpenAIのRealtime API、Pythonを使用してAI音声アシスタントを構築
OpenAIから、革新的なRealtime APIが一般公開されました。GPT Realtimeモデルのマルチモーダル機能を活かしたこのRealtime APIなら、音声から音声への直接的な対話(Speech to Speech、S2S)が可能です。
S2Sモデルは、音声認識(SST)やテキスト読み上げ(TTS)の変換工程を省くことなどで、レイテンシーの改善を可能にします。これにより、人間とのインタラクションに近い、極めて自然なAI対話を実現するアプリケーションの構築が可能となります。OpenAIとのコラボレーションによって実現したこの統合機能を、今回のローンチで皆さまに提供できることを心から嬉しく思っています。
このチュートリアルでは、PythonとFastAPI Webフレームワークを使用し、Twilio VoiceとOpenAI Realtime APIでAI音声アシスタントを構築する方法を解説します。Twilio Media Streamサーバーをセットアップして、通話の音声を受信し、それをOpenAI Realtime APIで処理した後、AIによる音声応答をTwilio経由で発信者に送り返します。構築後は、会話や質問応答からジョークまで、どんな内容にも対応できるようになります。
それでは、構築手順を見ていきます。
前提条件
このチュートリアルを進めるには、以下が必要です。
- Python 3.9以降 (このチュートリアルではバージョン3.9.13を使用)
- Twilioアカウント。お持ちでない場合は、こちらから無料トライアルに登録できます。
- Voice機能を備えたTwilio番号。電話番号の購入手順はこちら。
- OpenAIアカウントとOpenAI APIキー。こちらからサインアップできます。
- OpenAI Realtime APIアクセス。詳細はこちらをご覧ください。
- (オプション)ngrokなどのトンネリングソリューションを使用してローカルサーバーをインターネットに公開し、テストを行います。ngrokはこちらからダウンロードしてください。
先に進む前に、上記の準備が完了していることを確認してください。それでは始めましょう。
Realtime API speech-to-speech Pythonプロジェクトの設定
以下の手順では、プロジェクトの設定、必要な依存関係のインストール、サーバーコードの書き込みを行います。ステップごとに重要なパートを中心に解説していきます。
こちらでTwilioのレポジトリもご覧いただけます。チュートリアルのビデオバージョンも以下にご用意しています。
ステップ1: プロジェクトの初期化
まず、新しいPythonプロジェクトを設定し、開発マシンで混乱が起きないように仮想環境を作成しましょう。コマンドラインに以下を入力します。
ステップ2: 依存関係のインストール
次に、プロジェクトに必要な依存関係のインストールが必要です。以下のコマンドを実行します。これについては後で説明します。
TwilioとOpenAIによるWebSocketの処理にはWebSocketライブラリが必要です。またh、環境変数の読み込にはpython-dotenv、Twilioへの指示の構成にはtwilioが必要です。
fastapiは、このチュートリアルの作成に使用したPythonのWebフレームワークです。Pythonコミュニティでは、他にもFlask、Django、Pyramidなどがよく使われています。
サーバーにはuvicornを使用します。これは非同期アプリケーションに最適な、極めて軽量なサーバーです。実際に試してみれば納得していただけるはずです。
ステップ3: プロジェクトファイルの作成
次にファイルを作成します。メインコードとサーバーロジック用のmain.pyファイル、OpenAI APIキー保存用.envファイルの2つです。(作成方法はPython環境変数に関する記事で詳しく解説しています。)
ステップ3.1: main.pyファイルの作成
次のコマンドを実行します。
ステップ3.2: .envファイルの作成
まず、.envファイルを作成します。
次に、そのファイルをテキストエディタで開き、OpenAI Realtime APIキーを追加します。
(your_openai_api_key_hereの部分はご自身のAPIキーに置き換えてください。)
ステップ4: サーバーコードの構築
これで足場が整いました。
サーバーコードはいくつかのステップに分けて作成していきます。ステップごとに関連するコードを紹介し、その後、コードの難しい部分についてはできる限り簡潔に解説します。
ステップ4.1: 依存関係のインポートと環境変数の読み込み
main.pyファイルの先頭で、必要なモジュールをインポートします。次に.envファイルから環境変数を設定して読み込みます。
以下のコードをmain.pyの先頭に貼り付けてください。
ステップ4.2: 定数の定義とFastAPIの初期化
次に、システムメッセージ、AIの応答音声、ログに記録するイベントの定数を定義します。FastAPIアプリの初期化も行います。
以下のコードをファイルに貼り付けてください。
SYSTEM_MESSAGEでは、AIの振る舞いや性格を設定します。内容は自由に変更できます。
VOICE定数は、AIが応答に使用する音声を指定します。ローンチ時点では、ここで使用したalloyのほか、echo、shimmerも選択できます。
TEMPERATURE定数は、プロンプトに対するAIの応答のランダム性を制御します。値が高いほどランダム性が高くなります。
最後に、LOG_EVENT_TYPESはOpenAI APIのどのイベントをログに記録するかを指定します。詳細については、OpenAIのRealtime APIドキュメントをご覧ください。
FastAPIアプリケーションインスタンスの初期化と、OpenAI APIキーの存在確認も行います。
ステップ4.3: 着信用ルートとルートエンドポイントの定義
次に、2つのルートを定義します。サーバーの稼働確認用の最上位のルート(最終的なデモでは使用しませんが、動作確認に有用。パスは /)と、着信を処理してTwiMLの指示をTwilioに返すルートです。
以下をmain.pyに貼り付けます。
/incoming-callルートは、Twilioからの着信を処理し、TwiMLの指示(通話の処理方法をTwilioに理解させるための特殊なXMLの方言)を返します。ここでは、Twilio Python Helperライブラリを使用してコードを簡素化しています。
このTwiML応答では、発信側に待機するよう指示してから、Twilioに/media-stream WebSocketエンドポイントへの接続を指示します。ぜひ実際に試してみてください。
ステップ4.4: Twilio Media StreamsとOpenAIのWebSocket接続の処理
次のコードでは、Media Streams用のWebSocketルートを設定し、TwilioとOpenAIの両方のWebSocketに接続します。このコードは分量が多いため、後で主要なポイントを解説します。
以下のコードをルート定義の下に貼り付けてください。
/media-stream WebSocketエンドポイントは、通話中にTwilioからの接続を処理します。その後、2つのWebSocket間で音声を中継するための処理を行います。
OpenAI Realtime APIへの接続
OpenAI Realtime APIへのWebSocket接続を確立します。
websockets.connect(...): このコードは、指定されたエンドポイントとヘッダー(OpenAI APIキーを含む)を使用してOpenAI Realtime APIに接続します。詳細はOpenAIのドキュメントをご覧ください。send_session_update(openai_ws): 接続確立後に、このコードでセッション更新の初期設定をOpenAIに送信します。ここでは、前のセクションで定義した定数の一部を使用します。詳細は次のセクションで説明します。
TwilioとOpenAI間の音声中継
receive_from_twilioコルーチンは、Twilioから受信した音声データ処理し、OpenAIに送信します。その対となるsend_to_twilioは、OpenAIから受信したresponse.audio.deltaイベントをTwilioに送り返します(その他のイベントタイプ、すなわちLOG_EVENT_TYPES定数で制御するタイプは、コマンドラインに出力されます)。
ステップ4.5: OpenAIへセッション更新を送信
最後に、OpenAIのWebSocketにセッション更新を送信する関数を定義します。(これが前のセクションで触れた関数です。)
main.pyの最後に以下を貼り付けます。
この関数は、OpenAI Realtime APIセッションの初期設定を送信します。その設定例をいくつか紹介します(詳細はこちらをご覧ください)。具体的には次のようになります。
- ターン検出: サーバー側の音声活動検出(VAD)を有効にします。これにより、AIが応答のタイミングを判断できるようになります。
- オーディオ形式: 入力と出力のオーディオ形式を指定します。Twilioはaudio/pcmuをサポートしています。
- 音声: アプリで設定したAIのVOICE。
- 指示: AIの振る舞いを決める指示文を設定します。定数セクションの
SYSTEM_MESSAGEを変更できます。詳細やベストプラクティスはOpenAIのリアルタイムプロンプトガイドをご覧ください。 - 出力モダリティ: 音声応答機能を有効にします。
ステップ4.6: サーバーの準備
最後にサーバーのエントリーポイントを追加します。これはFastAPIサーバーを起動し、指定のポートで待ち受けるためです。main.pyの最後に以下を貼り付けます。
ステップ5: サーバーの実行
ここまでの手順が終われば、準備完了です。以下のコマンドでサーバーを実行してください。
正しく設定されていれば、以下のようなメッセージが表示されます。
もう少しです。あと数ステップで通話できるようになります。
セットアップの仕上げ
ステップ6: ngrokを使用してローカルサーバーを公開
Twilioには着信の処理方法についての指示が必要です。それには先ほど触れたTwiMLが必要ですが、まずはTwiMLを提供するサーバーにアクセスするための公開URLを用意する必要があります。
ngrokをまだインストールしていない場合は、ダウンロードし、インストールしてから次のコマンドを実行してください。ポート番号を5050以外に変更している場合は、ここでも必ず同じ番号を指定してください。
以下はコマンドを実行した後の画面です。
ステップ7: Twilioの設定
あと少しです。手順を進めましょう。ここからは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と入力します。
入力したら[Save](保存)をクリックします。これで準備完了です。
設定をテスト
ngrokセッションが実行中であり、サーバーが稼働していることを確認してください。次に、携帯電話または固定電話からTwilio番号に電話をかけます。
サーバーは通話を処理し、追加しておいた挨拶メッセージを流してから、OpenAI Realtime APIとTwilio Media Stream WebSocketを接続します。会話を始めると、AIがリアルタイムで応答します。会話を楽しんでください。
よくある問題とトラブルシューティング
サーバーは稼働しているものの、正常に動作しない場合は、まず以下の点を確認してください。
- ngrokは動作していますか? [A Call Comes In](着信)の[Voice Configuration](音声設定)にURLが正しく表示されていることを確認してください。
- Twilioにエラーはありませんか? Twilioのエラーはいくつかの方法でデバッグできます。詳細はこちらの記事をご覧ください。
- サーバーログに何か表示されていませんか? サーバーが問題なく動作していることを確認してください。
- コードはOpenAIを正しく呼び出していますか?
まとめ
これで完了です。Twilio Voice、Media Streams、OpenAI Realtime APIを用いた、PythonによるインタラクティブAI音声アプリケーションが正しく構築されました。いつでも会話できる、低レイテンシーのインタラクティブ音声アシスタントの完成です。ここからは、ビジネスロジックやガードレールの追加、製品化、ソリューションの規模拡大などに取り掛かることができます。今後の展開が楽しみです。
それでは、開発をお楽しみください。
次のステップ:
- より高度な機能については、TwilioのドキュメントとOpenAI APIのドキュメントをご覧ください。
- OpenAIの概念に関するドキュメントをご覧ください。
TwilioブログのチーフテクニカルエディターであるPaul Kampは、OpenAIのRealtime APIとの会話を楽しみながら、このチュートリアルを作成しました。特に、娘たちに話をさせたのが楽しかったと言います。連絡先はpkamp [at] twilio.comです。