この記事は 食べログアドベントカレンダー2024 の24日目の記事です🎅🎄
食べログ開発本部 技術部のデータサイエンスチームに所属する河村です。
データサイエンスチームは、データとAIを活用してビジネス成長に貢献することをミッションとしており、生成AI技術のサービス活用や業務活用に取り組んでいます。
その1つとして、生成AIを用いた音声対話についても注目しています。
2024年10月にOpenAIがリリースしたRealtime APIを用いて、GPT-4oの音声対話の仕組みを紐解きましたが、調べる前に思っていたことと違っている点もいくつかありました。
ユーザが話す音声をずっとAIが考えながら応答しているわけではないんですね...
気になった方は是非最後まで読んでみてください。
1. はじめに
OpenAIが2024年5月に発表したGPT-4oは、音声、画像、テキストを統合的に処理できるマルチモーダルAIモデルであり、特に音声対話機能の強化(Advanced Voice Mode)が注目されています。
人とAIがとても自然に会話しているデモ動画を見た方も多いのではないでしょうか。
2024年10月にリリースされたRealtime APIは、このAdvanced Voice Modeを用いた音声対話システムを構築するためのAPIです。
OpenAIから、これを試せるコードが提供されています。
このOpenAI Realtime Consoleを実際に自分の環境で動作させながら、イベントログなどを確認することで、音声対話がどのような仕組みで動作しているのかがわかります。
これを用いて解析した結果、3つの特徴があることがわかりました。
- WebSocketによる双方向通信で、人間らしい対話を実現 (2.3章)
- 音声は常に聞いているが、AIは常に処理しているわけではない (3.1章)
- 料金は会話時間ではなく、AIの処理回数で決まる (3.2章)
本記事では、これらの発見を通じて、Realtime APIを用いた音声対話システムの設計・実装における知見を解説します。 特に、「常に聞いているが常に考えていない」という特性は、システム設計に大きな影響を与えます。
なお、本ページの内容はRealtime APIのベータ版を元にした記述となります。今後変更となる可能性もありますのでご注意ください。
2. Realtime APIを解読し、音声対話の仕組みを紐解く
2.1. 生成AIを活用したこれまでの対話システムの課題
音声ではなくテキストベースの対話ということであれば、生成AIを用いたチャット機能の凄さは皆さんご存知だと思います。
音声をテキストに変換する音声認識機能(Speech To Text)と、テキストから音声を作成する音声合成機能(Text To Speech)のそれぞれの個別機能についても十分に実用レベルのサービスがすでにありますので、これらを組み合わせれば、音声対話システムが実現できます。
テキストのチャット機能をベースに、音声認識機能、音声合成機能を組み合わせて音声対話システムを構築すると次のような形になります。
聞きたい内容をマイクに話し、「送信」ボタンを押すと、そこまで話した音声を音声認識で文字に変換し、生成AIに送る形となります。 生成されたテキストデータを音声合成で音声データに変換し、スピーカーで再生する形です。
このシステムでも、音声でAIと対話することが十分に可能ですが、人間らしい自然な対話かと考えるとかなり程遠いです。 大きな違いとしては下記になります。
- 発話終了の検知方法 人同士の会話においては、発話終了判定は聞き手が行いますが、このシステムでは発話側の操作(送信ボタン押下)で行なっています。
- 応答にかかる処理時間 複数の技術を組み合わせており、ユーザが発話を終えてからAIの返答が返るまでに時間がかかっています。 実装方法にもよりますが、工夫しても数秒程度はかかってしまうため、テンポの良いレスポンスとは言い難いです。
- 応答への割り込み対応 人同士の会話においては、会話に割り込んで話し始めることで、発話権を取得できますが、このシステムではできません。発話側の操作で強制停止することで実現しています。
Realtime APIにおいて、これらの課題にどの様に対応しているかをご説明します。
2.2. 音声認識処理・音声合成処理の統合
Realtime APIではテンポの良いレスポンスを作成するために、音声認識部分から音声合成部分まで全てを統合しています。
1つに統合し、応答速度が速くなるようにチューニングされており、 処理を開始してから話し初めるまでにかかる時間が平均0.3秒程度にまで短縮されています。 実際には、ユーザの会話が終了したことを検知するための時間(0.2秒)も必要ですし、通信にかかる時間などもありますので、 全体としては1秒程度の時間はかかっています。 人が会話している時の応答時間は0.2秒〜0.5秒程度と言われており、それよりは長いもののかなり近い値ではないかと思います。
生成AIを用いた自然な会話が、人と変わらない応答速度で実現できるようになった。 といえます。
2.3. WebSocketを用いた完全双方向通信が可能なアーキテクチャの採用
2.1章で書いたシステムにおいては、発話終了の検出や、AIの応答処理の停止処理をクライアント側の操作で行なっていました。
これは、クライアントからサーバにリクエストを送る形のAPIを用いているため、操作の主体がクライアント側にならざるをえないためだと言えます。
また、スピーカーで再生している間、マイクが動いていないことも、人同士が対話している状態と大きく異なっており、これも大きな原因だと言えます。
Realtime APIでは、WebSocketという技術を採用し、常に双方向の声が聞きとれる仕組みを実現しています。
WebSocketとは、クライアントからサーバにリクエストする形のAPIとは異なり、常に双方向でデータ通信ができる仕組みです。
これを用いて、実際の人と人が対話している状況にかなり近い状況を作り出しています。
これにより、発話終了の検出や、応答への割り込み対応についても、人同士の対話の場合と同様に聞き手(サーバ側)で対処しています。
2.3.1. 話者交代の流れ(発話終了の検出)
下の図は、普通に会話している際のユーザとサーバとのやり取りの様子を表しています。
ユーザの発話終了を検知し、AIが話す。という話者交代がどの様に進んでいくのかを説明します。
まず、常に行われることとして、ユーザ側のマイクでキャッチした音声をサーバに送る処理(オレンジ色の矢印)があります。
ユーザの発話1回分が確定してから送るのではなく、ストリーミングで常に送り続ける形となります。
ユーザの音声は、AIが話している間も常に送り続けていることが重要なポイントです。
人同士の会話においては、一方が話している間にもう一方の発話も聞こえますが、WebSocketを用いることでこれを実現しています。
次に、発話開始・終了の検出方法です。
サーバ側でストリーミングで送られる音声を聞きながら、一定以上のボリュームのある音が検知されたら発話が開始されたと判定します。
また、無音状態が一定時間続いたら発話が終了したと判定します。
この様に無音時間を用いて発話終了を検知する方法をタイムアウト方式と呼びますが、
Realtime APIにおいては、何秒無音だった場合に発話終了と判定するかをパラメータで設定できます。
発話終了を検知したところで、発話文(赤枠の文字列)が確定しますので、応答文(Response)を作成します。
生成AIを用いて速やかに応答文章(緑枠の文字列)を作成し、ユーザへの応答を開始します。
図中の青色矢印の部分になりますが、ここもストリーミングで少しずつ送信する形となります。
このデータがユーザに届き次第、スピーカーで出力されます。
青色の矢印とスピーカーの矢印とを比較すると、スピーカーの矢印がかなり長いですが、
これは、音声の再生時間が生成AIの処理時間と比較するとかなり長いことを表しています。
先頭の2~3文字分の再生が終わる頃には、音声データは全て受信完了しています。
これが、ユーザとAIで順番に話者交代を行なっていく、基本的な流れとなります。
2.3.2. 応答への割り込み対応
下の図は、AIの応答途中に割り込みする際の様子を表しています。
割り込み処理は図の黄色の枠で囲まれた部分で説明します。
先ほども述べましたが、AIの応答途中であっても常にユーザの音声はサーバに送られていますので、
AIの会話に割り込んで話した音もサーバに送信されています。
サーバ側では、送られてくる音の大きさに注目して聞いており、
音声を検知したら、応答を途中でキャンセルし、ユーザの発話を聞くモードに変更する。という処理を行なっています。
緑色の点線で囲んだ部分はキャンセル処理に伴うConversationデータの修正処理になります。
Conversationとは、ユーザとサーバの間のやり取りの記録です。
キャンセル処理を行った場合、話そうとした内容("晴れですよ。")ではなく、
実際にスピーカーから出力できた内容("晴れで")を記録する必要がありますが、問題があります。
スピーカー出力はクライアント側で行われる処理なので、キャンセル処理の際にどこまで再生できたのか、サーバ側ではわからないことです。
このため、どこまで再生したのかをクライアントからサーバに通知する処理が必要となり、これが緑色の矢印です。
補足として、図では「晴れで」まで再生したと言葉で通知していますが、正確には何秒まで再生したかの数値を送っています。
公式ドキュメントconversation.item.truncateに記載がありますのでご確認ください。
この通知を受けて、Conversationのデータを修正するため、 次回以降の回答の際に、この回答が途中までしかできなかったことを踏まえて回答できます。
このように、テキストのみ扱っていた生成AIに音声認識処理・音声合成処理を統合したことと、 WebSocketを用いて完全双方向なアーキテクチャを採用したことで、従来より大幅に自然な対話を実現しています。
3. 事前の想像と異なった点
仕組みについて2章で説明しましたが、実際に触ってみたところ、事前に想像していた動きと異なる箇所がありました。 同じ勘違いをしている方もいらっしゃるかもしれませんので、ぜひ読んでみてください。
3.1. 常に聞いているが、常に考えてはいない。
ユーザの会話(オレンジの矢印)は常にサーバに送信していますとご説明しました。しかし オレンジのデータを直接生成AIが処理しているわけではありません。
タイムアウト方式を用いてユーザの発話終了を検知しますが、この判断は音の大きさだけで判断できますので、AIは必要ありません。 音の大きさを用いて発話終了と判断したタイミングで生成AIを起動しています。
ユーザとの一連の会話を管理する役割(青線部分)と、生成AIの処理を行う役割(黒線部分)の2つの役割が分かれており、 生成AIの処理は、応答作成のタイミングでのみ呼び出される。 というイメージで考えるとわかりやすいです。 生成AIが動いているのはそこだけです。常に生成AIが考えて処理しているるわけではないのです。
逆に言えば、大きな音から無音になるというタイミングがないと、AIは起動しないということです。
この仕様は、ユーザとAIの二人での対話の場合であれば概ねうまく動きますが、それ以外の活用法ではうまく動かないケースもありえます。
例えば次の様なケースです。
3人以上の会話は上手くいかない場合がある
AIを含む三人で会話をするケースを考えてみます。もし、AI以外の二人が熱い議論を始めてしまったら...AIが割り込むことはありません。
無音になるタイミングがないからです。
3人以上の会議に参加させ、適切なタイミングで意見を出してもらう。というようなことは難しいです。
Function Callingのみが起動されることはない
ユーザが定義した処理を適切なタイミングで起動してくれるFunction Callingという機能がありますが、
これについてもAIが動くタイミングでしか動きません。
私はRealtime APIを用いて、課題と決定事項のみを記載する議事録を書かせられないかと考えていました。
会議の内容を黙って聞きながら、課題や決定事項が発生するたびにリアルタイムでFunctionを呼び出し、記録する。
という指示をした上で、会議の録音データを聞かせてみたのですが、録音データの再生が終わり、無音状態になった時にようやく1行記録しただけでした。
ユーザが発話している途中にFunction Callingが発動することはありません。
このように、発話の終了判定にタイムアウト方式を用いているため、できそうでできないことが多数あります。 実際に利用したいユースケースがある場合は、うまく動くかどうか考えてみてください。
3.2. 料金は時間課金ではない。
料金についてはOpenAIのページに表が記載されていますが、計算方法を完全に勘違いしていました。
記事執筆時(2024年12月19日時点)の値ですが下記の記載があります。
モデル | 料金 |
---|---|
gpt-4o-audio-preview | テキスト $2.50/1M 入力トークン $10.00/1M 出力トークン 音声 $40.00/1M 入力トークン $80.00/1M 出力トークン |
さらに見ると、音声入力トークンは1分あたり約2.4セント、音声出力トークンは1分あたり約9.6セントに相当することもわかります。
私はこれを見て「ユーザとAIとそれぞれが話した時間に応じた料金がかかるのだな」と思い、 "ユーザが話した時間(分)×2.4セント+AIが話した時間(分)×9.6セント"という計算式で算出すれば良いと思っていたのですが、 全く違いましたのでご注意ください。
料金は図の黄色で囲んだ部分の生成AIを用いた処理に対してかかります。オレンジや青の矢印は直接関係しません。
生成AIを呼び出す際の入出力メッセージに応じて料金がかかります。
つまり、赤枠で囲んだInput Messagesと緑枠で囲んだOutput Messageに含まれるメッセージのトークンに対して、記載された料金がかかるということです。
ですので、料金計算は会話時間よりも何回発話し、何回レスポンスを作成したか が重要となります。
また、OpenAIのAPIを使用したことがある人であればご存知だと思いますが、
生成AIの処理はステートレスのため、それまでの会話全てをリクエストに含めて送る必要があります。
図で言うと、2回目のリクエストのInput Messagesには全てのメッセージが積まれていますが、
この全てのメッセージに料金がかかります。
会話を何度も繰り返すほど、一回あたりの価格が上がるということも注意が必要です。
幸いこの部分については、割引料金が適用されるルールがあり、薄いピンクと薄い緑のメッセージについては、80%引きの割引料金となりますが、
積み重なっていくことには間違いないですので、ご注意ください。
なお、プログラム上で、実際にかかったトークン数を確認することも可能です。
図の青色の矢印は、実際は複数の通信で構成されていますが、その最後に送信される
response.doneというメッセージにトークン数が記載されます。
公式ドキュメントresponse.doneに記載がありますのでご確認ください。
4. まとめ
Realtime APIを用いた音声対話の仕組みについてご理解いただけましたでしょうか。
Realtime APIを活用することでさまざまな音声対話システムの実装が可能になると思います。 ただ、実装する際に、あるいは設計する際に、裏でどのような処理が行われているかを知っていることはとても重要です。 今回のブログが、その助けとなれば幸いです。
5. 食べログの未来を一緒に変えてくれる人募集中!
AIの活用を一緒に推進してくれる人を探しています!
食べログはChatGPTのプラグイン開発を国内最速で行うなど、生成AIの活用に対する意識がかなり高い組織です。
実際、生成AIを活用したさまざまなPoCが進んでいますし、食べログがどんどん変わっていくとても面白いタイミングです。
私自身が行った業務についてもテックブログを公開しています。下にリンクを貼りますので、気になった方はぜひそちらもご覧いただきたいです。
面白そうと思った方はカジュアル面談からでもご連絡ください。一緒に食べログを変えていきましょう!
いよいよ明日はAdvent Calendar最終日ですね。
最後はCTOの @tkyowa さんによる「カカクコム社のテックカンパニー、そしてAIネイティブへの道」で締めくくります。お楽しみに!