英語

音声AI & 音声エージェント

図解入門

1. 2025年の会話型音声AI

LLMは会話が得意です。

ChatGPTやClaudeと自由形式の対話を多く行ったことがあるなら、LLMと話すことが非常に自然に感じられ、広範に有用であるという直感を持っているはずです。

LLMは非構造化情報を構造化データに変換することも得意です。[1]

新しい音声AIエージェントは、これら二つのLLMの能力—会話と非構造化データからの構造抽出—を活用し、新しい種類のユーザー体験を作り出しています。

[1] ここでは広義に意味しており、一部のLLMの「構造化出力」機能の狭義の意味に限定するものではありません。

音声AIは今日、さまざまなビジネス文脈で展開されています。例えば:

  • 医療予約前の患者データ収集、
  • インバウンドの営業リードへのフォローアップ、
  • さまざまなコールセンター業務の処理、
  • 企業間のスケジュールや物流の調整、そして
  • ほぼあらゆる種類の小規模事業の電話対応。

消費者向けでも、会話型の音声(およびビデオ)AIはソーシャルアプリケーションやゲームにも浸透し始めています。開発者はGitHubやソーシャルメディアで毎日のように個人的な音声AIプロジェクトや実験を共有しています。

2. 本ガイドについて

本ガイドは音声AIの最先端におけるスナップショットです。[2]

本番稼働に耐えうる音声エージェントを構築するのは難しいです。多くの要素はゼロから実装するのが容易ではありません。音声AIアプリを構築する場合、このガイドで扱う多くの事項についてフレームワークに依存することが多いでしょう。しかし、すべてをゼロから構築するかどうかに関わらず、部品がどのように組み合わさるかを理解することは有益だと考えています。

このガイドはSean DuBoisのオープンソース書籍WebRTC For the Curiousに直接触発されました。その書籍は最初に公開されてから4年間、数多くの開発者がWebRTCを理解するのに役立ってきました。[3]

この文書における音声AIコード例はPipecatオープンソースフレームワークを使用しています。Pipecatはリアルタイム音声AIのためのベンダーに依存しないエージェント層です。[4]本書でPipecatを使用した理由は次のとおりです:

  1. 我々が日々これで構築し、メンテナンスをしていて、よく知っているからです!
  2. Pipecatは現在最も広く使われている音声AIフレームワークであり、NVIDIA、Google、AWS、OpenAI、および数百のスタートアップがPipecatを活用し、また貢献しています。

この文書では一般的なアドバイスを提供するように努めており、商用製品やサービスを推奨することは避けています。特定のベンダーを挙げる場合は、多くの音声AI開発者がそれらを使用しているためです。

それでは始めましょう …

[2] 本ガイドは元々2025年2月のAI Engineering Summit向けに執筆しました。2025年5月中旬に更新しています。

[3] webrtcforthecurious.com — WebRTCは音声AIに関連があり、後述のWebSockets と WebRTCの節で議論します。

[4] Pipecatは60以上のAIモデルとサービスの統合を持ち、ターン検出や割り込み処理などの最先端実装を備えています。Pipecatを使えば、WebSockets、WebRTC、HTTP、および電話を使ってユーザーと通信するコードを書けます。PipecatにはTwilio、Telnyx、LiveKit、Dailyなどを含むさまざまなインフラプラットフォーム向けのトランスポート実装があります。JavaScript、React、iOS、Android、およびC++向けのクライアント側Pipecat SDKも用意されています。

3. 会話型AIの基本的なフロー

音声AIエージェントの基本的な「やるべき仕事」は、人間の発話を聞き、有用な方法で応答し、その順序を繰り返すことです。

こんにちの本番環境の音声エージェントはほぼすべて非常に似たアーキテクチャを持っています。音声エージェントプログラムはクラウドで動作し、音声から音声へのループをオーケストレーションします。エージェントプログラムは複数のAIモデルを使用し、その一部はエージェントにローカルに実行され、他はAPI経由でアクセスされます。エージェントプログラムはまた、LLMのFunction Callingや構造化された出力を使用し、バックエンドシステムと連携します。

  1. 音声はユーザーのデバイスのマイクでキャプチャされ、エンコードされ、ネットワークを通じてクラウドで動作する音声エージェントプログラムに送信されます。
  2. 入力音声は文字起こしされ、LLMへのテキスト入力が作られます。
  3. テキストはコンテキスト—プロンプト—として組み立てられ、LLMによって推論が行われます。推論の出力は多くの場合、エージェントプログラムのロジックによってフィルタリングまたは変換されます。[5]
  4. 出力テキストは音声合成モデルに送られ、オーディオ出力が生成されます。
  5. 生成された音声出力がユーザーに返送されます。

音声エージェントプログラムがクラウドで実行され、音声合成(TTS:text-to-speech)、LLM、および音声認識(STT:speech-to-text)処理がクラウド上で行われていることが分かるでしょう。長期的には、より多くのAIワークロードがデバイス上で実行されるようになると予想されます。しかし今現在、本番環境の音声AIはクラウドが中心です。その理由は2つあります:

  1. 音声AIエージェントは、低遅延で確実に複雑なワークフローを実行するために、利用可能な最良のAIモデルを使用する必要があります。エンドユーザーデバイスはまだ、最高のSTT、LLM、TTSモデルを許容できる遅延で実行するのに十分な処理能力をもったCPUを持っていません。
  2. 今ある商用音声AIエージェントの大半は、電話を介してユーザーとやり取りしています。電話の場合、エンドユーザーデバイスは存在しません — 少なくとも、あなたがコードを実行できるようなデバイスはありません!

このエージェントオーケストレーションの世界に深く入り込み[6]、次のような質問に答えていきましょう:

  1. 音声AIエージェントにはどのLLMが最適か?
  2. 長時間のセッション中に会話コンテキストをどのように管理するか?
  3. 音声AIエージェントを既存のバックエンドシステムにどのように接続するか?[7]
  4. 音声AIエージェントが適切に機能しているかどうかをどう判断するか?

こんにちのほぼすべての本番環境でのアーキテクチャ

[5] 例えば、一般的なLLMエラーや安全性の問題を検出するため。

[6] 詳細に説明しましょう — 編注。

[7] 例えば、CRM、独自のナレッジベース、コールセンターシステムなど。

4. コア技術とベストプラクティス

4.1. レイテンシ(遅延)

音声AIエージェントの構築は、ほとんどの点で他の種類のAIエンジニアリングと類似しています。テキストベースのマルチターンAIエージェントの構築経験があれば、そのドメインで得た多くの経験がボイスにも役立ちます。

大きな違いはレイテンシです。

人間は通常の会話で速い応答を期待します。典型的な応答時間は500msです。長い沈黙は不自然に感じられます。

音声AIエージェントを構築するなら、エンドユーザーの視点からレイテンシを正確に測定する方法を学ぶ価値があります。

多くの場合、AIプラットフォームは実際の「音声対音声」の測定ではないレイテンシを提示しています。悪意があってしているわけではありません。プロバイダ側から見ると、レイテンシを測る簡単な方法は推論時間を測ることです。そのためプロバイダはレイテンシをそう考えがちです。しかし、このサーバー側の視点はオーディオ処理、フレーズのエンドポイント遅延、ネットワーク転送、およびOSのオーバーヘッドを考慮しません。

音声対音声のレイテンシを測るのは手動でも簡単です。

会話を録音し、その録音をオーディオ編集ソフトに読み込み、オーディオ波形を見て、ユーザーの発話の終了からLLMの発話の開始までを測定するだけです。

本番用途の会話型ボイスアプリケーションを構築する場合、時折この方法でレイテンシの数値をさっと確認するとよいです。テスト時にネットワークのパケット損失とジッターをシミュレートするとさらによいです!

音声から音声のレイテンシをプログラム的に測定するのは難しいです。一部のレイテンシはOSの深部で発生します。したがって、多くの可観測性ツールは最初の(オーディオ)バイト到着までの時間を測定します。これは総合的な音声対音声レイテンシの合理的な代理指標ですが、フレーズのエンドポイント変動やネットワークの往復時間など、測定していない要素が追跡手段がなければ問題になる可能性があることに注意してください。

会話型AIアプリケーションを構築しているなら、音声対音声で800msのレイテンシを目標にするのが良いでしょう。以下はユーザーのマイクからクラウドに送り、戻ってくる音声対音声のラウンドトリップの内訳です。これらの数値はかなり一般的で、合計は約1秒です。こんにちのLLMで安定して800msのラウンドトリップ時間を達成するのはチャレンジですが、不可能ではありません!

ステージ 時間(ms)
macOS マイク入力 40
opus エンコード 21
ネットワークスタックと転送 10
パケット処理 2
ジッターバッファ 40
opus デコード 1
文字起こしとエンドポイント検出 300
LLM 初回バイト(ttfb) 350
文の集約 20
TTS 初回バイト(ttfb) 120
opus エンコード 21
パケット処理 2
ネットワークスタックと転送 10
ジッターバッファ 40
opus デコード 1
macOS スピーカー出力 15
合計(ms) 993

音声対音声会話のラウンドトリップ — レイテンシの内訳

我々は、すべてのモデルを同一のGPU対応クラスタ内にホスティングし、スループットではなくレイテンシ最適化を行うことで500msの音声対音声レイテンシを達成するPipecatエージェントを実証しました。このアプローチは現状では広く使われていません。モデルのホスティングは高コストです。また、オープンウェイトのLLMは、GPT-4oやGeminiのような最良の独自モデルに比べて音声AIで使われる頻度が低いです。音声AIエージェント向けのLLMについては次節を参照してください。

ボイスユースケースではレイテンシが非常に重要なため、本ガイド全体でレイテンシが頻繁に取り上げられます。

4.2. ボイスユースケース向けのLLM

2023年3月のGPT-4のリリースは、現在の音声AI時代の幕開けとなりました。GPT-4は、柔軟なマルチターン会話を維持でき、かつ有用な作業を行うために十分に正確な最初のLLMでした。今日では、GPT-4の後継であるGPT-4oが会話型音声AIの支配的なモデルとなっています。

他のモデルは、元のGPT-4よりもいくつかの点において同等かそれ以上の性能を発揮するようになっています:

  • 対話型の音声会話に耐える十分低いレイテンシ。
  • 指示への良い従順性。[8]
  • 信頼できるFunction Calling。[9]
  • ハルシネーションやその他の不適切な応答の発生率が低い。
  • 個性とトーン。
  • コスト。

[8] モデルに特定のことを実行させるプロンプトの出しやすさはどれほどか?

[9] 音声AIエージェントはFunction Callingに大きく依存する。

しかし、現状、GPT-4oはオリジナルのGPT-4よりも優れています!特に指示への従順性、Function Calling、ハルシネーションの発生率低減において優れています。

GPT-4oの主要な競合はGoogleのGemini 2.0 Flashです。Gemini 2.0 Flashは高速で、指示への従順性とFunction CallingにおいてGPT-4oと同等であり、価格も攻勢的です。

音声AIのユースケースは要求が厳しいため、一般的には利用可能な最良のモデルを使うのが妥当です。 いずれ状況は変わり、最先端でないモデルでも音声AIのユースケースで広く採用されるのに十分になる時が来るでしょう。まだその段階ではありません。

4.2.1 LLM レイテンシ

Claude Sonnetは音声AIに非常に良い選択肢となり得ますが、推論レイテンシ(最初のトークンまでの時間)がAnthropicの優先事項ではなかった点が問題です。Claude Sonnetの中央値レイテンシは通常、GPT-4oやGemini Flashの2倍で、P95のばらつきもはるかに大きいです。

モデル 中央値 TTFT (ms) P95 TTFT (ms)
GPT-4o 460 580
GPT-4o mini 290 420
GPT-4.1 450 670
Gemini 2.0 Flash 380 450
Llama 4 Maverick (Groq) 290 360
Claude Sonnet 3.7 1,410 2,140

最初のトークンまでの時間(TTFT)メトリクス:OpenAI、Anthropic、Google API - 2025年5月

大まかな目安として、LLMのTTFTが500ms以下であればほとんどの音声AIユースケースで十分です。GPT-4oのTTFTは通常400〜500msです。Gemini Flashも同様です。

4.2.2 コスト比較

推論コストは頻繁かつ急速に低下しています。したがって、一般にコストはLLMを選ぶ際の重要な要因ではありません。Gemini 2.0 Flashの新たに発表された価格はGPT-4oと比べて10倍のコスト削減を実現しています。これが音声AIの状況にどのような影響を与えるかは注視が必要です。

モデル 3分の会話 10分の会話 30分の会話
GPT-4o $0.009 $0.08 $0.75
Gemini 2.0 Flash $0.0004 $0.004 $0.03

複数の会話が発生するセッションのコストは、継続時間とともに超線形に増加します。30分のセッションは3分のセッションより概ね100倍の費用がかかります。キャッシュ、コンテキスト要約、その他の手法で長時間セッションのコストを削減できます。

セッション長に対してコストは超線形に増加する点に注意してください。セッション中にコンテキストをトリミングまたは要約しない限り、長時間のセッションではコストが問題になります。これはspeech-to-speechへのモデルで特に当てはまります(下の を参照)。

コンテキスト増大という数学的性質により、音声会話の1分あたりコストを特定するのは難しくなります。さらに、APIプロバイダはトークンキャッシュを提供することが増えており、これはコストを相殺(およびレイテンシを低減)できますが、異なるユースケースのコスト見積もりを複雑にします。

OpenAIの OpenAI Realtime API向けの自動トークンキャッシュ は特に便利です。Googleも最近、すべてのバージョン2.5モデル向けに 暗黙のキャッシュ(implicit caching) と呼ばれる類似機能を導入しました。

4.2.3 オープンソース / オープンウェイト

MetaのLlama 3.3および4.0のオープンウェイトモデルは、ベンチマーク上でGPT-4よりも良い性能を示します。とはいえ、商用ユースケースにおいてGPT-4oやGeminiより常に優れているわけではありません。しかし、それらを基に構築したり自社インフラで実行したりできる能力は重要です。[11]

多くのプロバイダがLlamaの推論エンドポイントを提供しており、サーバーレスGPUプラットフォームはLlamaをデプロイするためのさまざまなオプションを提供します。Metaは最近、ファーストパーティの推論APIを発表し、オープンウェイトのLlamaモデルが同社の重要な戦略的焦点であることを強く示しました。

Llamaファミリの中で興味深く有能なモデルの一つがUltravoxです。Ultravoxはオープンソースのネイティブ音声LLMです。Ultravoxの背後にある会社は、商用レベルの音声対話APIも提供しています。Ultravoxは、Llama 3.3を音声領域に拡張し、音声AIユースケース向けにベースモデルの指示従順性とFunction Calling性能を改善するために多数の手法を活用しています。Ultravoxは、オープンソースAIエコシステムの利点とネイティブ音声モデルの有望な可能性の両方を示す例です。

我々は2025年に入ってオープンソース/オープンウェイトモデルの多くの進展を見ています。Llama 4はごく新しく、コミュニティは対話型の会話AIにおける実用的な性能をまだ評価中です。Alibabaの新しいQwen 3モデルは優れた中規模モデルで、初期ベンチマークではLlama 4と互角に競っています。さらに、DeepSeek、Google(Gemma)、Microsoft(Phi)から将来登場するオープンウェイトモデルも音声AIにとって良い選択肢になる可能性が高いと考えられます。

[11] 特定のユースケース向けにLLMをファインチューニングする予定があるなら、Llama 3.3 70Bは非常に良い出発点です。ファインチューニングについては下記を参照してください。

4.2.4 音声→音声モデルはどうか?

音声→音声モデルは興味深く、比較的新しい発展です。音声→音声LLMはテキストではなく音声でプロンプトを与えられ、直接音声を出力できます。これにより、音声エージェントのオーケストレーションループにおける音声→テキストおよびテキスト→音声の部分が排除されます。

音声→音声モデルの潜在的な利点は次のとおりです:

  • レイテンシーの低下。
  • 人間の会話のニュアンスを理解する能力の向上。
  • より自然な音声出力。

OpenAIとGoogleは共に音声→音声のAPIを公開しています。大規模モデルを訓練し音声AIアプリケーションを構築する多くの人々は、音声→音声モデルが音声AIの未来であると考えています。

しかし、現時点の音声→音声モデルとAPIは、ほとんどの本番環境の音声AIにとってまだ十分に良いとは言えません。

今ある音声→音声モデルは、確かに現状のText-to-speechモデルより自然に聞こえます。OpenAIのgpt4o-audio-preview[12]モデルは、音声AIの未来のプレビューのように確かに聞こえます。

ただし、音声→音声モデルはまだテキストモードのLLMほど成熟しておらず信頼性も十分とは言えません。

  • 理論的にはレイテンシーは低減可能ですが、音声はテキストよりも多くのトークンを使用します。トークンコンテキストが大きくなるとLLMが処理するのに時間がかかります。実際には、音声モデルは長い対話型会話において、テキストモデルより遅くなります。[13]
  • これらのモデルは理解力の向上が実際の利点であるように見えます。これは特にGemini 2.0 Flashの音声入力で顕著です。gpt-4o-audio-previewについては、テキストモードのGPT-4oより小さくやや能力が劣るモデルであるため、現時点では状況がやや不明瞭です。
  • より自然な音声出力は今でも明らかに知覚できます。しかしオーディオLLMは音声モードで奇妙な出力パターンを示すことがあり、テキストモードほど頻繁ではないものの、語の反復、時に不気味の谷に入る談話マーカー、文を完結できないことが時折あります。

[13] 音声モデルにおけるこのレイテンシー問題は、キャッシュ、巧妙なAPI設計、モデル自体のアーキテクチャ進化の組み合わせによって明確に解決可能です。

これらの問題の中で最も大きいのは、マルチターン音声に必要な大きなコンテキストサイズです。ネイティブ音声の利点を得つつコンテキストサイズの欠点を回避する一つの方法は、各会話ターンをテキストと音声の混合として処理することです。最新のユーザーメッセージには音声を使用し、残りの会話履歴はテキストを使用します。

OpenAIのベータ版音声→音声提供であるOpenAI Realtime APIは高速で音声品質は驚異的です。しかし、そのAPIの背後にあるモデルはフルのGPT-4oではなく小型のgpt-4o-audio-previewです。したがって指示従順性やファンクションコールは同等ではありません。Realtime APIで会話コンテキストを管理するのもやや扱いにくく、APIにはいくつかの新製品ならではの粗さがあります。[14]

GoogleのMultimodal Live APIも有望であり、進化の初期段階にある音声→音声サービスです。このAPIはGeminiモデルの近未来像を示しています:長いコンテキストウィンドウ、優れたビジョン機能、高速推論、強力な音声理解、コード実行、検索に基づくグラウンディング。OpenAI Realtime API同様、Multimodal Live APIは現時点ではほとんどの本番音声AIアプリケーションにとって適切な選択肢とは言えません。

音声→音声APIは比較的高価である点に注意してください。我々はOpenAI Realtime APIのコスト計算機を作成しており、OpenAIの自動トークンキャッシュ機能を考慮してセッション長に応じたコストのスケーリングを示しています。

我々は2025年に音声→音声分野で多くの進展が見られると予想しています。しかし、本番の音声AIアプリケーションがマルチモデルのアプローチから音声→音声APIへの移行をどれほど速く進めるかはまだ不明です。

[14] Realtime APIに関する詳細ノートを参照してください

OpenAI Realtime API コスト計算機

4.3. Speech-to-text

Speech-to-text(音声→テキスト)は音声AIの「入力」段階です。Speech-to-textは一般に文字起こしまたはASR(自動音声認識)とも呼ばれます。

音声AIでは、非常に低い文字起こしレイテンシーと非常に低い語誤り率が必要です。残念ながら、音声モデルを低レイテンシー向けに最適化すると精度にマイナスの影響があります。

今日、低レイテンシー向けに設計されていない非常に優れた文字起こしモデルがいくつか存在します。Whisperは多くの製品やサービスで使われているオープンソースモデルです。非常に優れていますが、通常のTTFT(ファーストトークン到達時間)が500ms以上であるため、会話型の音声AIにはめったに使われません。

4.3.1 DeepgramとGladia

多くの本番環境で利用される音声AIエージェントはSpeech-to-textにDeepgramまたはGladiaを使用しています。Deepgramは、低レイテンシー、低語誤り率、低コストの優れた組み合わせを長年にわたり提供してきた商用のSpeech-to-textAI研究所であり、APIプラットフォームです。Gladiaは2022年設立の比較的新しい参入企業で、多言語サポートに特に強みを持ちます。

DeepgramのモデルはセルフサーブのAPIとして、または顧客が自社システムで実行できるDockerコンテナとして提供されています。ほとんどの人はまずAPI経由でDeepgramのSpeech-to-textを使用し始めます。米国内のユーザではファーストトークン到達時間は通常約150msです。

スケーラブルなGPUクラスタを管理することは継続的な大きなDevOps作業であるため、APIから自社インフラでモデルをホストすることに移行するのは正当な理由がない限り行うべきではありません。正当な理由には次が含まれます:

  • 音声/文字起こしデータをプライベートに保つこと。DeepgramはBAAや機密データ処理に関する契約を提供していますが、一部の顧客は音声および文字起こしデータを完全に管理したいと考えるでしょう。米国外の顧客は自国または地域内にデータを保持する法的義務がある場合があります。(デフォルトではDeepgramの利用規約はAPI経由で送信したすべてのデータで学習することを許可しています。エンタープライズプランではこれをオプトアウトできます。)
  • レイテンシーの削減。Deepgramは米国外に推論サーバーを持ちません。ヨーロッパからのTTFTは約250ms、インドからは約350msです。

Deepgramはファインチューニングサービスを提供しており、ユースケースに比較的珍しい語彙、話し方、またはアクセントが含まれる場合に誤り率を下げるのに役立ちます。

Gladiaは英語圏外の新しい音声AIプロジェクトで最もよく見られるSpeech-to-textプロバイダです。Gladiaはフランスに本社を置き、米国とヨーロッパに推論サーバーを持ち、100以上の言語をサポートしています。

Gladiaはホスト型APIと自社インフラでモデルを実行するオプションを提供しています。GladiaのAPIはヨーロッパのデータ居住要件があるアプリケーションで利用できます。

4.3.2 プロンプトはLLMを助ける

文字起こしの誤りの大部分は、リアルタイムストリームで文字起こしモデルが利用できるコンテキストが非常に少ないことに起因します。

今日のLLMは文字起こし誤りを回避するのに十分賢いです。LLMが推論を行う際には完全な会話コンテキストにアクセスできます。したがって、入力がユーザー音声の文字起こしであり、その点を考慮して推論すべきであるとLLMに伝えることができます。

You are a helpful, concise, and reliable voice assistant. Your primary goal is to understand the user's spoken requests, even if the speech-to-text transcription contains errors. Your responses will be converted to speech using a text-to-speech system. Therefore, your output must be plain, unformatted text.

When you receive a transcribed user request:
1. Silently correct for likely transcription errors. Focus on the intended meaning, not the literal text. If a word sounds like another word in the given context, infer and correct. For example, if the transcription says "buy milk two tomorrow" interpret this as "buy milk tomorrow".
2. Provide short, direct answers unless the user explicitly asks for a more detailed response. For example, if the user says "what time is it?" you should respond with "It is 2:38 AM". If the user asks "Tell me a joke", you should provide a short joke.
3. Always prioritize clarity and accuracy. Respond in plain text, without any formatting, bullet points, or extra conversational filler.
4. If you are asked a question that is time dependent, use the current date, which is February 3, 2025, to provide the most up to date information.
5. If you do not understand the user request, respond with "I'm sorry, I didn't understand that."

Your output will be directly converted to speech, so your response should be natural-sounding and appropriate for a spoken conversation.

音声AIエージェント向けのプロンプト例の文言。

4.3.3 その他のSpeech-to-textの選択肢

我々は2025年にSpeech-to-text分野で多くの新しい開発が起こると予想しています。2025年4月初旬時点で注目している新展開のいくつか:

  • OpenAIは最近、gpt-4o-transcribeとgpt-4o-mini-transcribeという2つの新しいSpeech-to-textモデルを発表しました。
  • 他の評判の良い音声技術企業であるSpeechmaticsAssemblyAIは、会話型音声により注力し始めており、ストリーミングAPIやより高速なTTFTのモデルを提供し始めています。
  • NVIDIAはベンチマークで非常に良好な性能を示すオープンソース音声モデルを提供しています。
  • 推論を提供する企業であるGroqのホスト版Whisper Large v3 Turboは現在中央値のTTFTが300ms未満であり、会話型音声アプリケーションの選択肢の範囲に入っています。これは我々がこのレイテンシーを達成したのを確認した最初のWhisper APIサービスです。

主要なクラウドサービスはすべてSpeech-to-textのAPIを提供しています。現時点で、低レイテンシの音声AIユースケースにおいては、どれもDeepgramやGladiaほど優れてはいません。

ただし、次の場合は Azure AI Speech、Amazon Transcribe、または Google Speech-to-Text を使用したいことがあるかもしれません:

  • 既にこれらのクラウドプロバイダのいずれかと大きなコミット済み支出やデータ処理の取り決めがある場合。
  • これらのクラウドプロバイダのいずれかに大量のスタートアップクレジットがあり、使い切りたい場合!

4.3.4 Google Gemini を使った文字起こし

Gemini 2.0 Flash を低コストのネイティブ音声モデルとして活用する一つの方法は、会話生成と文字起こしの両方に Gemini 2.0 を使うことです。

これを行うには、2つの並列な推論プロセスを実行する必要があります。

  • 一方の推論プロセスは会話の応答を生成します。
  • もう一方の推論プロセスはユーザーの発話を文字起こしします。
  • 各音声入力は単一のターンのみに使用されます。完全な会話コンテキストは常に最新のユーザー発話の音声と、過去の全ての入力および出力のテキスト文字起こしです。
  • これにより両方の利点が得られます: 現在のユーザー発話に対するネイティブな音声理解と、全体コンテキストのトークン数削減。[15]

[15] 音声をテキストに置き換えるとトークン数は約10倍少なくなります。10分間の会話では、処理される総トークン数、したがって入力トークンのコストは約100倍削減されます。(会話履歴は各ターンで累積するため。)

ここでは、これらの並列推論プロセスを Pipecat パイプラインとして実装するためのコードを示します。


  pipeline = Pipeline( 
    [   
        transport.input(), 
        audio_collector,
        context_aggregator.user(),
        ParallelPipeline( 
            [ # transcribe
                input_transcription_context_filter,
                input_transcription_llm,
                transcription_frames_emitter,
            ],
            [ # conversation inference
                conversation_llm,
            ],
        ),
        tts,
        transport.output(),
        context_aggregator.assistant(),
        context_text_audio_fixup, 
    ] 
  )            
            

論理は次のとおりです。

  1. 会話用LLMは会話履歴をテキストとして受け取り、さらに各新しいターンのユーザー発話をネイティブ音声として受け取り、会話応答を出力します。
  2. 文字起こし用LLMは同じ入力を受け取りますが、最新のユーザー発話の逐語的な文字起こしを出力します。
  3. 各会話の最後に、ユーザーの音声コンテキスト項目はその音声の文字起こしに置き換えられます。

Gemini のトークンあたりコストは非常に低いため、この方法は実際には文字起こしに Deepgram を使うより安価になることがあります。

ここで重要なのは、Gemini 2.0 Flash を音声→音声モデルとして使用しているわけではなく、その音声理解能力を利用しているという点です。モデルをプロンプトして、会話モードと文字起こしモードという2つの異なる「モード」で動作させています。

このようなLLMの使い方は、最先端のLLMアーキテクチャの持つ可能性を示します。このアプローチは新しくまだ実験的ですが、初期のテストでは他のどの手法よりも優れた会話理解とより正確な文字起こしができる可能性が示唆されています。ただし欠点もあります。文字起こしのレイテンシは専用のSpeech-to-textモデルほど良くありません。2つの推論プロセスを実行しコンテキスト要素を入れ替える複雑さは相当なものです。専用の文字起こしモデルでは問題とならないプロンプトインジェクションやコンテキスト追従エラーに対して脆弱になります。

ここに文字起こし用のシステム指示(プロンプト)を示します。


You are an audio transcriber. You are receiving audio from a user. Your job is to transcribe the input audio to text exactly as it was said by the user.

You will receive the full conversation history before the audio input, to help with context. Use the full history only to help improve the accuracy of your transcription.

Rules:
- Respond with an exact transcription of the audio input.
- Do not include any text other than the transcription.
- Do not explain or add to your response.
- Transcribe the audio input simply and precisely.
- If the audio is not clear, emit the special string "".
- No response other than exact transcription, or "", is allowed.
            

4.4. Text-to-speech

Text-to-speechは、音声→音声処理ループの出力段階です。

音声AI開発者は次の基準で音声モデル/サービスを選びます:

  • 音声の自然さ(総合的な品質)[16]
  • レイテンシ[17]
  • コスト
  • 対応言語
  • 単語レベルのタイムスタンプ対応
  • 声、アクセント、発音のカスタマイズ能力

[16] 発音、イントネーション、話速、強勢、リズム、感情的価数。

[17] 最初の音声バイトまでの時間。

音声の選択肢は2024年に大幅に増えました。新しいスタートアップが登場し、最高品質の音声は大幅に向上しました。すべての提供者がレイテンシを改善しました。

Speech-to-textの場合と同様に、主要なクラウドプロバイダはすべてText-to-speech製品を持っています。[18] しかし現在はスタートアップのモデルの方が優れているため、多くの音声AI開発者はそれらを使っていません。

[18] Azure AI Speech、Amazon Polly、Google Cloud Text-to-Speech。

リアルタイム会話型ボイスモデルで最も勢いのあるラボは(アルファベット順):

  • Cartesia – 独自の状態空間モデルアーキテクチャを使用して、高品質と低レイテンシを両立しています。
  • Deepgram – レイテンシと低コストを優先しています。
  • ElevenLabs – 感情的および文脈的なリアリズムを重視しています。
  • Rime – 会話音声のみで訓練されたカスタマイズ可能なTTSモデルを提供しています。

これら4社はいずれも強力なモデル、エンジニアリングチーム、安定かつ高性能なAPIを持っています。Cartesia、Deepgram、Rime のモデルは自社インフラにデプロイできます。

1分あたりのコスト(概算) 中央値 TTFB (ms) P95 TTFB (ms) 平均プレスピーチ ms
Cartesia $0.02 190 260 160
Deepgram $0.008 150 320 260
ElevenLabs Turbo v2 $0.08 300 510 160
ElevenLabs Flash v2 $0.04 170 190 100
Rime $0.024 340 980 160

1分あたりの概算コストおよびTime to first byteまでの時間の指標 – 2025年2月。コストは利用量や使用する機能に依存する点に注意してください。avg pre-speechは、最初の発話フレームの前にオーディオストリーム内で発生する平均的な初期無音間隔です。

Speech-to-textと同様に、非英語言語のサポートには音声モデルによって大きな差があります。非英語のユースケース向けに音声AIを構築する場合は、より広範なテストが必要になる可能性が高いです。様々なサービスや複数の音声をテストして、満足できるソリューションを見つけてください。

すべての音声モデルは時折単語を誤発音するし、固有名詞や珍しい語の発音を必ずしも知っているわけではありません。

一部のサービスは発音を調整する機能を提供しています。これは、出力テキストに特定の固有名詞が含まれることが事前に分かっている場合に便利です。もし音声サービスが音素ベースの調整をサポートしていない場合、LLMに特定の単語の「音のような」綴りを出力させるようプロンプトすることもできます。例えば、NVIDIAの代わりに in-vidia と発音させたり。


              Replace "NVIDIA" with "in vidia" and replace 
"GPU" with "gee pee you" in your responses.

LLMのテキスト出力を使って発音を誘導するためのプロンプト例の文言

会話型の音声では、ユーザーが聞いたテキストを追跡できることが、正確な会話コンテキストを維持するうえで重要です。これは、モデルが音声に加えて単語レベルのタイムスタンプメタデータを生成し、タイムスタンプデータが元の入力テキストへ逆変換可能であることを要求します。これは音声モデルにとって比較的新しい機能です。上の表にあるモデルのうちElevenLabs Flashを除くすべてが単語レベルのタイムスタンプをサポートしています。


{
  "type": "timestamps",
  "context_id": "test-01",
  "status_code": 206,
  "done": false,
  "word_timestamps": {
    "words": ["What's", "the", "capital", "of", "France?"],
    "start": [0.02, 0.3, 0.48, 0.6, 0.8],
    "end": [0.3, 0.36, 0.6, 0.8, 1]
  }
}
                    

Cartesia APIからの単語レベルタイムスタンプ。

さらに、リアルタイムストリーミングAPIが利用できればとても便利です。会話型音声アプリケーションでは同時に複数の音声推論を行うことがよくあります。音声エージェントには、進行中の推論を中断でき、各推論リクエストを出力ストリームに紐づけるような実装が必要になります。音声モデルプロバイダのストリーミングAPIは比較的新しく、まだ進化途上です。現時点では、CartesiaとRimeが最も成熟したストリーミングサポートをPipecatで使える形で提供しています。

音声モデルの進歩は2025年も続くと予想されます。

  • 上記に記載された企業のいくつかは、年の前半に新モデルを出すことを示唆しています。
  • OpenAIは最近、新しいテキスト読み上げモデル gpt-4o-mini-tts をリリースしました。このモデルは完全に制御可能であるため、音声モデルに対して「何を言うか」だけでなく「どのように話すか」を指示することが可能になります。gpt-4o-mini-ttsの制御を試すにはopenai.fmで実験できます。
  • Groq PlayAIは最近提携を発表しました。Groqは高速な推論で知られ、PlayAIは30以上の言語をサポートする低レイテンシの音声モデルを提供します。

4.5. オーディオ処理

優れた音声AIプラットフォームやライブラリは、オーディオ取り込みと処理の複雑さをほとんど隠してくれます。しかし、複雑な音声エージェントを構築すると、いずれオーディオ処理のバグやコーナーケースに直面します。[19] したがって、オーディオ入力パイプラインをざっくり理解しておくにこしたことはないです。

[19] …これはソフトウェアのすべての事柄に一般化され、そしておそらく人生のほとんどの事柄にも当てはまります。

4.5.1 マイクと自動利得制御

現代のマイクは、高度なハードウェアと大量の低レイヤーソフトウェアが組み合わさった非常に洗練されたデバイスです。これは素晴らしいことで、モバイル機器、ノートパソコン、Bluetoothイヤーピースなどに内蔵された小型マイクから優れたオーディオを得られます。

しかし時には、この低レイヤーのソフトウェアが望んだ通りに動作しないことがあります。特にBluetoothデバイスは音声入力に数百ミリ秒のレイテンシを追加することがあります。これは音声AI開発者にとって制御外であることが多いですが、特定のユーザーが使用しているOSや入力デバイスによってレイテンシが大きく異なる可能性がある点は把握しておくべきです。

Bluetooth is problematic? Always has been.

ほとんどのオーディオキャプチャパイプラインは入力信号に対して何らかの自動利得制御(AGC)を適用します。これも通常は望ましい挙動で、ユーザーのマイクからの距離などを補正してくれます。一部の自動利得制御は無効にできることがありますが、消費者向けデバイスでは完全に無効にできない場合が多いです。

4.5.2 エコーキャンセレーション

ユーザーが電話を耳に当てていたりヘッドフォンを着用している場合、ローカルマイクとスピーカー間のフィードバックを心配する必要はありません。しかし、スピーカーフォンで会話している場合やヘッドフォンなしのノートパソコンを使っている場合は、優れたエコーキャンセレーションが極めて重要です。

エコーキャンセレーションはレイテンシに非常に敏感なため、エコーキャンセレーションはデバイス上で実行する必要があります。今日では電話システム、ウェブブラウザ、WebRTCを使ったネイティブアプリケーションのSDKに組み込まれています。[20]

したがって、音声AI、WebRTC、または電話向けSDKを使用している場合、ほとんどの実世界シナリオで「そのまま動作する」エコーキャンセレーションが利用できるはずです。自前で音声キャプチャパイプラインを構築している場合は、エコーキャンセレーションロジックを統合する方法を見つける必要があります。例えば、WebSocketベースのReact Nativeアプリを構築している場合、デフォルトではエコーキャンセレーションはありません。[21]

[20] Firefoxのエコーキャンセレーションはあまり優れていない点に注意してください。音声AI開発者はChromeとSafariを主なプラットフォームとして構築し、時間が許せばFirefoxでテストすることを推奨します。

[21] つい最近、ある人のReact Nativeアプリのオーディオ問題のデバッグを手伝いました。根本原因は、音声AIやWebRTC SDKを使っていなかったためエコーキャンセレーションを実装する必要があることに気づいていなかったことでした。

4.5.3 ノイズ抑制、音声、音楽

電話システムやWebRTCのオーディオ取り込みパイプラインは、ほとんど常に「音声モード」をデフォルトにします。音声は音楽よりもはるかに圧縮可能で、狭帯域信号に対するノイズ低減やエコーキャンセレーションアルゴリズムは実装が容易です。

多くの電話プラットフォームは8kHz音声しかサポートしていません。これは現代の基準では明らかに低品質です。この制限があるシステムを経由している場合、対処のしようがありません。ユーザーが品質の低さに気付くかどうかは場合によります — ほとんどの人は電話通話の音質に対して期待値が低いです。

WebRTCは非常に高品質なオーディオをサポートします。[22] WebRTCのデフォルト設定は通常、48kHzサンプルレート、モノラル、32 kbpsのOpusエンコード、および適度なノイズ抑制アルゴリズムです。これらの設定は音声向けに最適化されており、幅広いデバイスや環境で機能するため、音声AIには一般的に適切な選択です。

これらの設定では音楽は良い音にはなりません!

WebRTC接続で音楽を送信する必要がある場合、次のようなことを行うとよいでしょう:

  • エコーキャンセレーションをオフにする(ユーザーはヘッドフォンを着用する必要があります)。
  • ノイズ抑制をオフにする。
  • 必要に応じてステレオを有効にする。
  • Opusのエンコードビットレートを上げる(モノラルなら64 kbps、ステレオなら96 kbpsまたは128 kbpsが良い目安です)。

[22] 高品質オーディオのユースケースの例:

  • LLM教師による音楽レッスン。
  • 背景音や音楽を含むポッドキャストの録音。
  • 対話的に生成されるAI音楽。

4.5.4 エンコーディング

エンコーディングは、音声データをネットワーク接続で送信するためにどのようにフォーマットするかの総称です。[23]

[23](またはファイルに保存する場合。)

リアルタイム通信で一般的なエンコーディングには次のものがあります:

  • 16ビットPCM形式の非圧縮オーディオ。
  • Opus — WebRTCおよび一部の電話システム。
  • G.711 — 幅広いサポートを持つ標準的な電話用コーデック。
コーデック ビットレート 品質 ユースケース
16ビットPCM 384 kbps(モノラル 24 kHz) 非常に高い(ほぼ無損失) 音声録音、組み込みシステム、単純なデコードが重要な環境
Opus 32 kbps 32 kbps 良好(音声に最適化されたサイコアコースティック圧縮) ビデオ通話、低帯域幅ストリーミング、ポッドキャスティング
Opus 96 kbps 96 kbps 非常に良い〜優秀(サイコアコースティック圧縮) ストリーミング、音楽、音声アーカイブ
G.711(8 kHz) 64 kbps 低品質(帯域幅制限、音声中心) レガシーなVoIPシステム、電話、ファクス伝送、音声メッセージング

音声AIで最も頻繁に使用されるオーディオコーデック

これら三つの選択肢の中では、Opusが圧倒的に最良です。Opusはウェブブラウザに組み込まれており、低遅延コーデックとしてゼロから設計されていて非常に効率的です。幅広いビットレートで良好に動作し、音声と高忠実度の両方のユースケースをサポートします。

16ビットPCMは「生のオーディオ」です。サンプリングレートとデータ型が正しく指定されていれば、PCMのオーディオフレームをソフトウェアのサウンドチャネルに直接送信できます。ただし、この非圧縮オーディオは一般的にインターネット接続で送信したいものではないことに注意してください。24 kHzのPCMはビットレートが384 kbpsです。これは多くの実際のエンドユーザーデバイスからの接続ではリアルタイムでバイトを配信するのが難しいほど大きなビットレートです。

4.5.5 サーバーサイドノイズ処理と話者分離

音声認識(Speech-to-text)や音声活動検出モデルは、通常、一般的な環境ノイズ(街の音、犬の鳴き声、マイク近くの大きなファン、キーボードのクリックなど)を無視できます。したがって、多くの人間同士のユースケースで重要な従来の「ノイズ除去」アルゴリズムは、音声AIにとってそこまで重要ではありません。

しかし、音声AIに特に有用な音声処理が一つあります:主要話者の分離です。主要話者の分離は背景の会話を抑制します。これにより文字起こしの精度が大幅に向上する可能性があります。

空港のような環境から音声エージェントに話しかけようとする状況を想像してください。携帯のマイクはゲート案内や通行人の会話など、多くの背景音を拾いやすいです。LLMが見るテキストの文字起こしにそうした背景音が入って欲しくありません!

または、リビングでテレビやラジオが背景で流れているユーザーを想像してください。人間は低音量の背景会話を比較的うまくフィルタリングできるため、カスタマーサポートに電話する前にテレビやラジオを消すことを思いつかない場合があります。

独自の音声AIパイプラインで使用できる最良の話者分離モデルは、Krispが提供しています。ライセンスは主にエンタープライズユーザーを対象としており、安価ではありません。しかし、大規模な商用ユースケースにおいては、コストにみあう音声エージェントの性能向上が見込めます。

OpenAIは最近、Realtime APIの一部として新しいノイズ低減機能を出荷しました。参照ドキュメントはこちらです。


  pipeline = Pipeline(
    [
      transport.input(),
      krisp_filter,
      vad_turn_detector,
      stt,
      context_aggregator.user(), 
      llm, 
      tts, 
      transport.output(), 
      context_aggregator.assistant(),
    ]
  )
            

Krisp処理要素を含むPipecatパイプライン

4.5.6 音声活動検出

音声活動検出(VAD)のステージは、ほとんどすべての音声AIパイプラインの一部です。VADはオーディオセグメントを「話し声」と「無音(話していない)」に分類します。VADについては下のターン検出セクションで詳しく説明します。

4.6. ネットワーク輸送

4.6.1 WebSocketsとWebRTC

WebSocketsとWebRTCはいずれもオーディオストリーミングにAIサービスで使用されます。

WebSocketsはサーバー間ユースケースに最適です。遅延が主要な懸念でないユースケースでも問題なく、プロトタイピングや一般的なハッキングに適しています。

WebSocketsは本番環境におけるクライアント—サーバー間のリアルタイムメディア接続で使用すべきではありません。

ブラウザまたはネイティブモバイルアプリを構築していて、会話遅延(対話的レイテンシ)がアプリケーションにとって重要である場合は、アプリから音声を送受信するためにWebRTC接続を使用するべきです。

エンドユーザーデバイスへのリアルタイムメディア配信におけるWebSocketsの主な問題点は次のとおりです:

  • WebSockets は TCP 上に構築されているため、オーディオストリームはHOLブロッキングの影響を受けます。
  • WebRTC に使用される Opus オーディオコーデックは、WebRTC の帯域推定やパケットペーシング(輻輳制御)ロジックと密に連携しており、WebSocket 接続だとレイテンシを蓄積してしまうようなネットワーク挙動に対しても WebRTC のオーディオストリームは対応できます。
  • Opus オーディオコーデックは非常に優れた前方誤り訂正を備えており、比較的高いパケット損失に対してもオーディオストリームを堅牢にします。(ただし、これはネットワークトランスポートが遅延到着パケットを破棄でき、ヘッドオブラインブロッキングを行わない場合のみ有効です。)
  • WebRTC のオーディオは自動的にタイムスタンプが付与されるため、再生や途切れ検知のロジックは自明になります。
  • WebRTC ではパフォーマンスおよびメディア品質の統計データを取得できます。優れた WebRTC プラットフォームは詳細なダッシュボードと分析を提供します。このレベルの可観測性は、WebSockets に対しては非常に困難か不可能に近い場合があります。
  • WebSocket の再接続ロジsックは堅牢に実装するのがかなり難しいです。ping/ack フレームワークを構築する必要があるか、WebSocket ライブラリが提供するフレームワークを十分にテストして理解する必要があります。TCP のタイムアウトや接続イベントはプラットフォームによって挙動が異なります。
  • 最後に、今日の優れた WebRTC 実装には非常に優れたエコーキャンセル、ノイズ抑制、および自動利得制御が備わっています。

WebRTC は 2 つの方法で使用できます。

  1. クラウド内の WebRTC サーバーを経由するルーティング。
  2. クライアントデバイスと音声AI プロセスの間で直接接続を確立すること。

クラウドサーバーを経由するルーティングは、多くの現実的なユースケースでより良いパフォーマンスを発揮します(下記の network routing を参照)。クラウドインフラストラクチャは、直接接続では容易に、あるいは大規模にサポートしにくい多数の機能(マルチ参加者セッション、電話システムとの統合、録音など)を可能にします。

しかし「サーバーレス」WebRTC は多くの音声AIのユースケースに適しています。Pipecat は SmallWebRTCTransport クラスを通じてサーバーレス WebRTC をサポートしています。また、HuggingFace の FastRTC のようなフレームワークは、このネットワーキングパターンを中心に構築されています。

WebSocket vs WebRTC diagram

4.6.2 HTTP

HTTP は音声AI にとってもなお有用かつ重要です!HTTP はインターネット上のサービス相互接続の共通言語です。REST API は HTTP です。Webhook も HTTP です。

テキスト指向の推論は HTTP を通じて行われるため、音声AI パイプラインは通常、会話ループの LLM 部分のために HTTP API を呼び出します。

音声エージェントは外部サービスや内部 API と統合する際にも HTTP を使用します。便利な手法の一つは、LLM のFunction Callingを HTTP エンドポイントにプロキシすることです。これにより、音声AI エージェントの実装やDevOpsをFunction Callingの実装から切り離せます。

マルチモーダル AI アプリケーションは、HTTP と WebRTC の両方のコードパスを実装したくなることがよくあります。テキストモードとボイスモードの両方をサポートするチャットアプリを想像してください。会話状態はどちらの接続経路からもアクセスできる必要があり、これはクライアント側とサーバー側両方のコード(例えば Kubernetes ポッドや Docker コンテナのアーキテクチャなど)に影響を及ぼします。

HTTP の 2 つの欠点は、レイテンシと長寿命の双方向接続を実装する難しさです。

  • 暗号化された HTTP 接続のセットアップには複数のネットワークリクエスト往復が必要です。メディア接続のセットアップ時間を 30ms より短くするのは相当難しく、最適化されたサーバーでも実際の最初のバイト送信までの時間は 100ms 前後になることが多いです。
  • 長時間維持される双方向の HTTP 接続は管理が難しいため、通常は WebSockets を使用した方が良いです。
  • HTTP は TCP ベースのプロトコルなので、WebSockets に影響するのと同じヘッドオブラインブロッキングの問題が HTTP にも発生します。
  • HTTP 上で生のバイナリデータを送ることは一般的でないため、多くの API はバイナリを base64 エンコードすることを選び、これによりメディアストリームのビットレートが増加します。

ここで QUIC の話になります …

HTTP API diagram

HTTP と WebRTC の両方をネットワーク通信に使用する音声AI エージェント。

4.6.3 QUIC と MoQ

QUIC は最新バージョンの HTTP(HTTP/3)のトランスポート層として、さらに柔軟に他のインターネット規模のユースケースもサポートするよう設計された新しいネットワークプロトコルです。

QUIC は UDP ベースのプロトコルであり、HTTP に関連する上記のすべての問題に対処します。QUIC により接続時間の短縮、双方向ストリーム、ヘッドオブラインブロッキングの解消が得られます。Google や Facebook は QUIC を着実に展開しており、現在では一部の HTTP リクエストがインターネット上を TCP ではなく UDP パケットとして通過することがあります。[24]

[24] 長年インターネット上で構築してきた人にとっては少し 🤯 なことです。HTTP は常に TCP ベースのプロトコルだと考えられてきました!

QUIC はインターネット上のメディアストリーミングの将来において大きな役割を果たすでしょう。リアルタイムメディアストリーミング向けの QUIC ベースプロトコルへの移行には時間がかかります。QUIC ベースの音声エージェントを構築する上での障壁の一つは、Safari がまだ WebSockets の QUIC ベース進化版である WebTransport をサポートしていないことです。

IETF の Media over QUIC ワーキンググループ[25] は「メディアの取り込みと配信のためのシンプルな低遅延メディア配信ソリューション」を開発することを目的としています。すべての標準化活動に共通するように、重要なユースケースの最大の幅を最も単純な構成要素でサポートする方法を詰めるのは容易ではありません。人々はオンデマンドビデオストリーミング、大規模なビデオ放送、ライブビデオストリーミング、多数参加者の低遅延セッション、低遅延の1:1 セッションなどに QUIC を使うことに期待しています。

リアルタイムの音声AI ユースケースは、MoQ 標準の開発に影響を与えるのにちょうど良いタイミングで成長しています。

4.6.4 ネットワークルーティング

どのようなプロトコル基盤であっても長距離のネットワーク接続におけるレイテンシとリアルタイムメディアの信頼性は課題です。

リアルタイムメディア配信では、サーバーをユーザーにできるだけ近づけるべきです。

例えば、英国のユーザーから北カリフォルニアにある AWS us-west-1 にホストされたサーバーへの往復パケット時間は通常約 140 ミリ秒になります。比較として、同じユーザーから AWS eu-west-2 への RTT は一般的に 15 ミリ秒以下です。

Edge routing diagram

英国のユーザーから AWS us-west-1 への RTT は AWS eu-west-2 への RTT より約 100ms 大きい

それは 100 ミリ秒以上の差です — 音声間レイテンシ目標が 1,000 ミリ秒である場合、あなたのレイテンシ「予算」の 10% に相当します。

エッジルーティング

すべてのユーザーの近くにサーバーを展開できるとは限りません。

世界中のユーザーすべてに対して 15ms RTT を達成するには、少なくとも 40 のグローバルデータセンターにデプロイする必要があります。それは大きなデブオプスの仕事です。しかも GPU を必要とするワークロードを実行していたり、そもそもグローバルに展開されていないサービスに依存している場合は、不可能かもしれません。

光速をごまかすことはできません。[26] しかし、経路の変動性や輻輳を避けることはできます。

[26] 古代のネットワークエンジニアの知恵 – 編者注。

重要なのは、パブリックインターネット経路をできるだけ短く保つことです。ユーザーを近くのエッジサーバーに接続し、そこからプライベートルートを使用します。

このエッジルーティングは中央値のパケット RTT を低減します。英国 → 北カリフォルニアのプライベートバックボーン経由ルートの往復時間はおそらく約 100 ミリ秒です。100 ms(長距離プライベートルート) + 15 ms(パブリックインターネット上の最初のホップ) = 115 ms。このプライベートルートの中央値 RTT はパブリックルートの中央値 RTT より 25ms 優れています。

Edge routing diagram

英国から AWS us-west-1 へのエッジルート。パブリックネットワーク上の最初のホップは依然として 15ms の RTT を持ちます。しかし、プライベートネットワーク経由で北カリフォルニアへの長距離ルートは 100ms の RTT を持ちます。合計 RTT の 115ms は、英国から us-west-1 へのパブリックルートより 25ms 速くなっています。また、パケット損失やジッタが少なく、変動もかなり小さくなります。

中央値 RTT の改善よりさらに重要なのは、配信の信頼性向上とジッタの低減です。[27] プライベートルートの P95 RTT はパブリックルートの P95 よりも大幅に低くなります。[28]

これは、長距離のパブリックルート経由のリアルタイムメディア接続が、プライベートルートを使用する接続よりも測定上遅延が大きくなることを意味します。各オーディオパケットをできるだけ早く配信しようとしている一方で、パケットを順序どおり再生する必要があることを思い出してください。1 つの遅延したパケットがあると、ジッタバッファを拡張して他の受信済みパケットを遅延パケットが到着するまで保持する必要が生じます。(あるいは、到着が長すぎると判断して空白を高度な数学で埋めるか、あるいは破綻したオーディオサンプルで埋めることになります。)

[27] ジッタはパケットが経路を通過するのにかかる時間の変動性のことです。

[28] P95 はある指標の 95 パーセンタイル測定値です。P50 は中央値(50 パーセンタイル)です。広義には、P50 を平均的なケース、P95 を「典型的な最悪ケース」に近い感覚を捉えるものと考えます。

Jitter buffer diagram

ジッタバッファ — 大きなジッタバッファは音声および映像における知覚される遅延の増加に直結します。ジッタバッファをできるだけ小さく保つことは、優れたユーザー体験に大きく寄与します。

優れた WebRTC インフラ提供者はエッジルーティングを提供します。どこにサーバークラスターを持っているかを示し、プライベートルートのパフォーマンスを示すメトリクスを提供できるはずです。

4.7. ターン検出

ターン検出 は、ユーザーが話し終えて LLM の応答を期待しているかどうかを判定することを意味します。

学術文献では、この問題のさまざまな側面が フレーズ検出、音声セグメンテーション、エンドポインティング と呼ばれています。(この問題について学術文献が存在するという事実は、これは自明ではない問題であることを示す手がかりです。)

私たち(人間)は誰かと話すときに毎回ターン検出を行います。しかし、いつも正しく行うわけではありません![29]

したがって、ターン検出は難しい問題であり、完全な解決策は存在しません。ここでは一般的に使われているさまざまなアプローチについて説明します。

[29] 特に視覚的手がかりがない音声通話ではそうです。

4.7.1 音声活動検出(VAD)

現在、音声AI エージェントのターン検出で最も一般的な方法は、長いポーズ(無音)がユーザーの発話終了を意味すると仮定することです。

音声AI エージェントのパイプラインは、小さく専門化された音声活動検出モデルを用いてポーズを識別します。VAD モデルは音声セグメントを「発話」または「非発話」に分類するように訓練されています。(これは音量レベルだけに基づいてポーズを識別しようとするよりもはるかに堅牢です。)

VAD は音声AI 接続のクライアント側またはサーバー側のいずれでも実行できます。クライアント側で大幅な音声処理を行う必要がある場合は、VAD をクライアントで実行してそれを支援する必要があるでしょう。たとえば、組み込みデバイスでウェイクワードを識別し、フレーズの先頭でウェイクワードを検出した場合にのみ音声をネットワーク越しに送信するような場合です。Hey, Siri …

一般的には、VAD を音声AI エージェントの処理ループの一部として実行する方が少し簡単です。電話経由で接続するユーザーがいる場合、VAD を実行できるクライアントがないため、サーバーで実行する必要があります。

音声AIで最も頻繁に使用される VAD モデルは Silero VAD です。このオープンソースモデルは CPU 上で効率的に動作し、複数言語をサポートし、8kHz と 16kHz の両方の音声で良好に機能し、ウェブブラウザでの使用向けに wasm パッケージとしても利用可能です。リアルタイムのモノラル音声ストリーム上で Silero を実行しても、通常は典型的な仮想マシン CPU コアの 1/8 未満の負荷で済みます。

ターン検出アルゴリズムにはいくつかの設定パラメータがあります:

  • ターン終了とみなすために必要なポーズの長さ。
  • 発話開始イベントをトリガーするために必要な発話セグメントの長さ。
  • 各音声セグメントを発話と分類するための信頼度レベル。
  • 発話セグメントの最小音量。
VAD processing step

ここでは音声認識の直前に実行されるように設定された音声活動検出処理ステップ


  # Pipecat's names and default values
  # for the four configurable VAD
  # parameters
  VAD_STOP_SECS = 0.8
  VAD_START_SECS = 0.2
  VAD_CONFIDENCE = 0.7
  VAD_MIN_VOLUME = 0.6
  

これらのパラメータを調整することで、特定のユースケースに対するターン検出の挙動を大幅に改善できます。

4.7.2 プッシュトゥトーク

ポーズに基づくターン検出の明白な問題は、人が一時的にポーズを取っていても話し終えていないことがある点です。

個々の話し方のスタイルは異なります。会話の種類によっては人はより多くポーズを取ります。

長いポーズ間隔を設定すると会話がぎこちなくなり、非常に悪いユーザー体験になります。しかし短いポーズ間隔だと、音声エージェントが頻繁にユーザーの発話を遮ってしまい、これもまた悪いユーザー体験になります。

ポーズベースのターン検出の最も一般的な代替はプッシュトゥトークです。プッシュトゥトークとは、ユーザーが話し始めるときにボタンを押すか押し続け、話し終えたら再度ボタンを押すか放すことを要求する方式です。(昔ながらのトランシーバーの動作を思い浮かべてください。)

プッシュトゥトークではターン検出は明確ですが、ユーザー体験は単に話す場合とは異なります。

電話を使った音声AIエージェントではプッシュトゥトークは実現できません。

4.7.3 エンドポイントマーカー

特定の語をターンの終了マーカーとして使用することもできます。(CB 無線でトラッカーが "over" と言うのを思い浮かべてください。)

特定のエンドポイントマーカーを識別する最も簡単な方法は、各文字起こし断片に対して正規表現マッチを実行することです。しかし、小さな言語モデルを使用してエンドポイントの単語やフレーズを検出することもできます。

明示的なエンドポイントマーカーを使用する音声AIアプリケーションはあまり一般的ではありません。ユーザーはこれらのアプリケーションに話しかける方法を学ぶ必要があります。しかし、このアプローチは特化したユースケースでは非常によく機能することがあります。

たとえば、昨年見た素晴らしいデモでは、ある人が副業として自分用に作ったライティングアシスタントがありました。彼らはターンの終端を示したりモードを切り替えたりするために、さまざまなコマンドフレーズを使用していました。

4.7.4 コンテキストに対応したターン検出(セマンティック VAD とスマートターン)

人間がターン検出を行うとき、次のようなさまざまな手がかりを使います:

  • "えっと" のようなフィラー語を、発話が続く可能性が高いものとして識別すること。
  • 文法構造。
  • 電話番号の桁数のように特定のパターンを知っていること。
  • ポーズ前に語を引き延ばすなどの抑揚や発音パターン。

ディープラーニングモデルはパターン識別に非常に優れています。LLM は潜在的な文法知識を豊富に持ち、プロンプトによってフレーズのエンドポイント検出を行わせることができます。より小さく専門化された分類モデルは、言語、抑揚、発音パターンで訓練することができます。

音声AIエージェントが商業的にますます重要になるにつれて、コンテキストに対応した音声AIのターン検出の新しいモデルが登場すると予想されます。

主に二つのアプローチがあります:

  1. リアルタイムで動作できる小さなターン検出モデルを訓練する。このモデルを VAD モデルと組み合わせて、または代替として使用します。ターン検出モデルはテキスト上でパターンマッチするように訓練できます。テキストモードのターン検出モデルは文字起こしの後にパイプライン内でインラインで動作し、効果的にするには特定の文字起こしモデルの出力で訓練する必要があります。あるいは、ターン検出モデルをネイティブに音声上で動作するように訓練することもでき、これにより言語レベルのパターンだけでなく抑揚、発話のペーシング、発音パターンを考慮した分類が可能になります。ネイティブ音声のターン検出モデルは文字起こし情報を必要としないため、文字起こしと並列して実行でき、パフォーマンスを改善できます。
  2. 大規模 LLM と few shot プロンプトを使ってターン検出を行う。LLM は通常インラインで使用するには遅すぎてパイプラインをブロックしてしまいます。この問題を回避するために、パイプラインを分割してターン検出と「グリーディ」な会話推論を並列で行うことができます。
 
    [
      transport.input(),
      vad,
      audio_accumulater,
      ParallelPipeline(
      [
        FunctionFilter(filter=block_user_stopped_speaking),
      ],
      [
        ParallelPipeline(
        [
          classifier_llm,
          completeness_check,
        ],
        [
          tx_llm,
          user_aggregator_buffer,
        ],
        )
      ],
      [
        conversation_audio_context_assembler,
        conversation_llm,
        bot_output_gate,
      ],
      ),
      tts,
      transport.output(),
      context_aggregator.assistant(),
    ],

Pipecat パイプラインの コンテキスト対応ターン検出用コード は Gemini 2.0 Flash ネイティブオーディオ入力を使用しています。ターン検出とグリーディな会話推論は並列で動作します。ターン検出推論がフレーズのエンドポイントを検出するまで出力はゲートされます。

ターン検出における最近の動向:

  • 3 月に OpenAI は Realtime API 向けの新しいコンテキスト対応ターン検出機能をリリースしました。彼らはこの機能を、より単純な サーバー VAD(ポーズベースのターン検出)と対比して セマンティック VAD と呼んでいます。ドキュメントは こちら にあります。
  • Tavus はトランスフォーマーベースのネイティブ音声ターン検出モデルを開発し、現在はリアルタイム会話ビデオ API の一部となっています。Tavus チームは問題領域とモデルの動作に関する非常に良い 技術的概要 を公開しています。
  • Smart Turn オープンソースモデルは、Pipecat コミュニティによって構築・維持されている最先端のネイティブ音声ターン検出モデルです。すべての訓練データ、訓練コード、推論コード、およびモデルウェイトがオープンソースで公開されています。[30]

4.8. 中断処理

中断処理とは、ユーザーが音声AIエージェントを中断できるようにすることを指します。中断は会話の通常の一部であるため、中断を適切に扱うことが重要です。

中断処理を実装するには、パイプラインのすべての部分がキャンセル可能である必要があります。また、クライアント上でオーディオ再生を非常に迅速に停止できる必要があります。

一般に、使用しているフレームワークは中断がトリガーされたときにすべての処理を停止することを処理してくれます。しかし、リアルタイムより速い速度で生のオーディオフレームを送信するAPIを直接使用している場合は、再生を手動で停止し、オーディオバッファをフラッシュする必要があります。

4.8.1 誤検知による中断を避ける

意図しない中断の原因となるいくつかの要素は注意に値します。

  1. 音声として分類される一時的なノイズ。優れたVADモデルは「ノイズ」と音声をうまく分離します。しかし、発話の始めに現れる種々の短く鋭い音は、発話の初期段階で中程度の音声確信度が付与されることがあります。咳やキーボードのクリックはこのカテゴリに入ります。VAD の開始セグメント長と確信度レベルを調整することで、この種の中断を最小限に抑えることができます。トレードオフは、開始セグメント長を長くし、確度の閾値を上げると、本来検出したい非常に短いフレーズを完全な発話として検出できなくなる可能性があることです。[31]
  2. エコーキャンセレーションの失敗。エコーキャンセレーションアルゴリズムは完全ではありません。無音から音声再生への移行は特に難しい問題です。多くの音声エージェントのテストを行っていると、ボットが話し始めた直後に自分自身を中断するのを聞いたことがあるでしょう。原因は、エコーキャンセレーションが初期の音声をわずかにマイクにフィードバックさせてしまうことです。最小VAD開始セグメント長はこの問題を回避するのに役立ちます。また、音量の急激な遷移を避けるために音量レベルに対して指数平滑化[32]を適用することも有効です。
  3. バックグラウンドスピーチ。VADモデルはユーザーの発話と背景の発話を区別しません。背景の発話が音量閾値より大きいと、背景の発話が中断を引き起こします。スピーカー分離のためのオーディオ処理ステップは、背景スピーチによる誤検知中断を減らすことができます。上記のサーバー側ノイズ処理とスピーカー分離の節の議論を参照してください。

4.8.2 中断後に正確なコンテキストを維持する

LLMはリアルタイムより速く出力を生成するため、中断が発生したときにはユーザーに送信するためにLLMの出力がキューに入っていることがよくあります。

通常、会話のコンテキストはユーザーが実際に聞いた内容と一致させたい(パイプラインがリアルタイムより速く生成した内容ではなく)ことが多いです。

おそらく会話のコンテキストをテキストとして保存しているでしょう。[33]

したがって、ユーザーが実際に聞いたテキストが何であるかを特定する方法が必要です!

最上級のSpeech-to-textサービスは単語レベルのタイムスタンプデータを出力します。これらの単語レベルのタイムスタンプを使用して、ユーザーが聞いたオーディオに一致するアシスタントメッセージのテキストをバッファして組み立てます。単語レベルのタイムスタンプに関する議論は上記のテキスト読み上げの節を参照してください。Pipecatはこれを自動的に処理します。

[31] Pipecatの標準パイプライン構成は、VADと文字起こしイベントを組み合わせて、誤検知中断と見逃しの両方を回避しようとします。

[33] 標準のコンテキスト構造は、OpenAIによって開発されたユーザー/アシスタントのメッセージリスト形式です。

4.9. 会話コンテキストの管理

LLMはステートレスです。つまり、対話型の会話では、各応答を生成するたびに、これまでのすべてのユーザーとエージェントのメッセージやその他の構成要素をLLMに再度渡す必要があります。

Turn 1:
  User: What's the capital of France?
  LLM: The capital of France is Paris.

Turn 2:
  User: What's the capital of France?
  LLM: The capital of France is Paris.
  User: Is the Eiffel Tower there?
  LLM: Yes, the Eiffel Tower is in Paris.
  
Turn 3:
  User: What's the capital of France?
  LLM: The capital of France is Paris.
  User: Is the Eiffel Tower there?
  LLM: Yes, the Eiffel Tower is in Paris.
  User: How tall is it?
  LLM: The Eiffel Tower is about 330 meters tall.

各ターンで会話履歴全体をLLMに送信すること。

各推論操作(各会話ターン)について、LLMに送信できるものは次のとおりです:

  • システム指示
  • 会話メッセージ
  • LLMが使用するためのツール(Function Calling)
  • 構成パラメータ(例:temperature)

4.9.1 LLM APIによる違い

この一般的な設計は、今日の主要なLLMすべてに共通しています。

しかし、各プロバイダーのAPIには違いがあります。OpenAI、Google、Anthropicはそれぞれメッセージ形式、ツール/関数定義の構造、システム指示の指定方法に違いがあります。

サードパーティのAPIゲートウェイやソフトウェアライブラリがAPI呼び出しをOpenAIの形式に変換することがあります。これは有用です。なぜなら異なるLLM間を切り替えられることは便利だからです。しかし、これらのサービスは常に違いを適切に抽象化できるわけではありません。新機能や各API固有の機能は常にサポートされるとは限りません。(そして時には変換レイヤーにバグがある場合もあります。)

抽象化するか否かは、AIエンジニアリングのこの比較的初期の時代には依然として答えがありません。[34]

例えばPipecatは、コンテキストメッセージとツール定義の両方についてOpenAI形式へのメッセージの変換を行います。しかしこれを行うかどうか、またどのように行うかは大いにコミュニティで議論された主題でした![35]

[34] 自分へのメモ: Claudeに良いハムレットのジョークを考えてもらうよう頼むこと – 編集者より。

[35] こうしたトピックに興味があるなら、ぜひPipecat Discordに参加して、そこでの議論に参加してください。

4.9.2 ターン間でのコンテキスト修正

対話型のコンテキストを管理する必要があることは、音声AIエージェントの開発の複雑さを増します。一方で、コンテキストを遡及的に修正することは有用です。各会話ターンごとに、LLMに送信する内容を正確に決定できます。

LLMは必ずしも完全な会話コンテキストを必要としません。コンテキストを短縮または要約することで、レイテンシーやコストを削減し、音声AIエージェントの信頼性を高めることができます。このトピックの詳細は下のスクリプティングと指示の遵守の節を参照してください。

4.10. Function Calling

本番環境の音声AIエージェントはLLMのFunction Callingに大きく依存しています。

Function Callingは以下の用途で使用されます:

  • 情報取得を用いた検索強化生成(RAG)のための情報取得。
  • 既存のバックエンドシステムやAPIとのやり取り。
  • 電話技術スタックとの統合 — 転送、待ち行列、DTMFトーンの送信。
  • スクリプトの実行遵守 – ワークフローの状態遷移を実装するFunction Calling。

4.10.1 音声AIにおける Function Callingの信頼性

音声AIエージェントがますます複雑なユースケースに展開されるにつれて、Function Callingの信頼性はますます重要になっています。

最先端のLLMはFunction Callingが着実に向上していますが、音声AIのユースケースはLLMのFunction Calling能力を限界まで引き伸ばす傾向があります。

音声AIエージェントは以下の傾向があります:

  • 対話型の会話で関数を使用する。対話型の会話では、各ターンでユーザーとアシスタントのメッセージが追加されるにつれてプロンプトがますます複雑になります。このプロンプトの複雑化はLLMのFunction Calling能力を劣化させます。
  • 複数の関数を定義する。音声AIワークフローでは、関数が5つ以上必要になることが一般的です。
  • セッション中に関数を複数回呼び出します。

私たちは主要なAIモデルのリリースを厳密にテストし、これらのモデルを訓練している人々と頻繁に話をしています。上記のすべての属性は、現世代のLLMを訓練するために使用されたデータに対していくらか範囲外であることは明らかです。

これは、現世代のLLMは一般的なFunction Callingベンチマークで良好な結果を出していても、音声AIのユースケースでは苦戦することを意味します。モデルごと、同一モデルの異なる更新版ごとに、Function Callingの得意不得意が異なり、状況に応じて異なる種類のFunction Callingに対する性能も異なります。

音声AIエージェントを構築している場合、アプリのFunction Calling性能をテストするために独自の評価(eval)を開発することが重要です。下のVoice AI Evalsセクションを参照してください。

4.10.2 Function Callingのレイテンシ

Function Callingは4つの理由でレイテンシを追加します — 場合によっては大きなレイテンシになります:

  1. LLMがFunction Callingが必要だと判断すると、Function Calling要求メッセージを出力します。あなたのコードはその特定の要求された関数に対して必要な処理を行い、その後、同じコンテキストにFunction Callingの結果メッセージを追加して再度推論を呼び出します。したがって、関数が呼び出されるたびに、推論呼び出しが1回ではなく2回必要になります。
  2. Function Calling要求はストリーミングできません。Function Callingを実行する前に、完全なFunction Calling要求メッセージが必要です。
  3. プロンプトに関数定義を追加するとレイテンシが増加する可能性があります。これはやや曖昧な点で、プロンプトに関数定義を追加したことによる追加レイテンシを測定するためのレイテンシ指向の評価(eval)を開発するのが良いでしょう。しかし、少なくとも一部のAPIでは、関数(ツール)使用が有効な場合、関数が実際に呼び出されているかどうかにかかわらず中央値のTTFT(初回トークン到着時間)が高くなることは明らかです。
  4. あなたの関数自体が遅い可能性があります!レガシーなバックエンドシステムと連携している場合、関数が返るのに時間がかかることがあります。

ユーザーが話し終えたときには比較的迅速な音声フィードバックを提供する必要があります。Function Callingが返るのに時間がかかる可能性があると分かっている場合は、何が起きているかをユーザーに伝えて待つよう促す音声を出力することを検討してください。

TTFT for inference that includes a function call

Function Callingを含む推論のTTFT。LLMのTTFTは450msでスループットは毎秒100トークンとする。Function Calling要求のチャンクが100トークンであれば、Function Calling要求を出力するのに1秒かかります。次に関数を実行し再度推論を行います。今回は出力をストリームできるため、450ms後に使用可能な最初のトークンが得られます。完全な推論のTTFTは1450msです(関数自体の実行時間は含みません)。

次のいずれかを行うことができます:

  • Function Callingを実行する前に必ずメッセージを出力する。「Xを実行しますので少々お待ちください…」
  • ウォッチドッグタイマーを設定し、Function Callingループがタイマー発火前に完了していない場合のみメッセージを出力する。「まだ処理中です。もう少しだけお待ちください…」

もちろん両方行うこともできます。長時間実行されるFunction Calling中にバックグラウンドミュージックを再生することも可能です。[36]

[36] ただし、Jeopardyのテーマ曲はやめてください。

4.10.3 中断の処理

LLMはFunction Calling要求メッセージとFunction Calling応答メッセージが対になって現れることを期待して訓練されています。

これは次のことを意味します:

  1. すべてのFunction Callingが完了するまで、speech-to-speechへの推論ループを停止する必要があります。非同期Function Callingに関する注意事項は下を参照してください。
  2. Function Callingが中断されて二度と完了しない場合、何らかのことを示すFunction Calling応答メッセージをコンテキストに入れる必要があります。

ここでのルールは、LLMが関数を呼び出した場合、リクエスト/レスポンスのメッセージペアをコンテキストに入れる必要があるということです。

  • 未解決のFunction Calling要求メッセージをコンテキストに入れたままマルチターン会話を続けると、LLMが訓練された方法から逸脱したコンテキストを作成することになります。(一部のAPIではこれを許可しません。)
  • リクエスト/レスポンスのペアをまったくコンテキストに入れない場合、あなたはLLMに(in‑context learningを通じて)関数を呼び出さないよう教えていることになります。[37] これも結果は予測不能で、おそらく望ましくないでしょう。

[37] 論文「Language Models are Few-Shot Learners」を参照してください。

Pipecatは、Function Callingが開始されるたびにリクエスト/レスポンスのメッセージペアをコンテキストに挿入することで、これらのコンテキスト管理ルールの遵守を支援します。(もちろん、この挙動を上書きしてFunction Callingコンテキストメッセージを直接管理することもできます。)

実行完了(run-to-completion)と中断可能(interruptible)の2つの異なる方法で構成されたFunction Callingについて、パターンは次のようになります。

User:  Please look up the price of 1000 widgets.
LLM: Please wait while I look up the price for 1000 widgets. 
function call request: { name: "price_lookup", args: { item: "widget", quantity: 1000 } }
function call response: { status: IN_PROGRESS }

初期コンテキストメッセージ。Function Calling要求メッセージとFunction Calling応答のプレースホルダー。

User:  Please look up the price of 1000 widgets.
function call request: { name: "price_lookup", args: { item: "widget", quantity: 1000 } }
function call response: { result: { price: 12.35 } }

Function Callingが完了したときのコンテキスト。

User:  Please look up the price of 1000 widgets.
LLM: Please wait while I look up the price for 1000 widgets. 
function call request: { name: "price_lookup", args: { item: "widget", quantity: 1000 } }
function call response: { status: IN_PROGRESS }

User: Please lookup the price of 1000 pre-assembled modules.
LLM: Please wait while I also look up the price for 1000 pre-assembled modules. 
function call request: { name: "price_lookup", args: { item: "pre_assembled_module", quantity: 1000 } }
function call response: { status: IN_PROGRESS }

プレースホルダーにより、Function Callingが実行されている間に会話を続けることができ、LLMを「混乱」させることを避けられます。

User:  "Please look up the price of 1000 widgets."
LLM: "Please wait while I look up the price for 1000 widgets." 
function call request: { name: "price_lookup", args: { item: "widget", quantity: 1000 } }
function call response: { status: CANCELLED }

User: Please lookup the price of 1000 pre-assembled modules.
LLM: Please wait while I look up the price for 1000 pre-assembled modules.
function call request: { name: "price_lookup", args: { item: "pre_assembled_module", quantity: 1000 } }
function call response: { status: IN_PROGRESS }

Function Callingが中断可能として構成されている場合、Function Calling中にユーザーが発話するとキャンセルされます。

4.10.4 ストリーミングモードとFunction Callingのコンテンツチャンク

音声AIエージェントの実装では、ほとんどの場合、会話の推論呼び出しをストリーミングモードで実行します。これは初期の数個のコンテンツチャンクをできるだけ早く取得するためで、speech-to-speechへの応答レイテンシにとって重要です。

ただし、ストリーミングモードとFunction Callingの組み合わせは扱いにくいです。ストリーミングはFunction Callingチャンクには役立ちません。LLMの完全なFunction Calling要求メッセージを組み立てるまで関数を呼び出すことはできません。[38]

[38] AIフレームワークを使用している場合、フレームワークがこの複雑さを隠してくれている可能性があります。

推論プロバイダがAPIを進化させる際のフィードバックとして:Function Callingチャンクを原子性を持って提供し、ストリームされたコンテンツチャンクから分離するモードを提供してください。これにより、LLMプロバイダAPIを使用するコードの複雑さが大幅に軽減されます。

4.10.5 Function Callingをどのように、どこで実行するか

LLMがFunction Calling要求を出力したとき、あなたは何をしますか?ここに一般的に使われるパターンを示します:

  • 要求された関数と同じ名前の関数を自分のコード内で直接実行する。これはほとんどすべてのLLM Function Callingドキュメントの例で見られる方法です。
  • 引数とコンテキストに基づいて要求を操作(オペレーション)にマップする。これは、LLMに対して汎用的なFunction Callingを行い、それをあなたのコード内で曖昧性を解消するように考えてください。このパターンの利点は、選択可能な関数の数を少なくすると一般にLLMがFunction Callingをより上手く行うことが多い点です。[39]
  • Function Callingをクライアントにプロキシする。このパターンは(電話ではなく)アプリケーション文脈で利用可能です。例えば get_location() 関数を想像してください。ユーザーのデバイスの現在位置が必要であれば、そのデバイス上のジオロケーションAPIにフックする必要があります。
  • Function Callingをネットワークエンドポイントにプロキシする。これはエンタープライズ用途で特に有用なパターンです。内部APIと連携する一連の関数を定義し、これらのFunction CallingをHTTPリクエストとして実行する抽象化をコード内に作成します。

[39] ここではFunction Callingを広義のカテゴリとして考えてください — 口語的ではなく形式的な意味での関数です。ルックアップテーブルから値を返すこともできます。SQLクエリを実行することもできます。

Function Callingパターン

4.10.6 非同期Function Calling

場合によっては、Function Callingからすぐに戻りたくないことがあります。関数が完了するまでの時間が予測できない場合や、まったく完了しないかもしれない場合があります。あるいは、時間とともに情報を追加できる長時間実行プロセスを起動したい場合もあります。

例えば、ユーザーがツアー中に見かけるかもしれない事柄への関心を表現できるウォーキングツアーアプリを想像してください。「有名な作家が住んでいた場所があれば、特にそれについて聞きたいです。」この場合の良いアーキテクチャの一つは、ユーザーが特定の関心を表明するたびにLLMが関数を呼び出すことです。その関数はバックグラウンドプロセスを開始し、関心に関連するものが見つかるたびに情報をコンテキストに注入します。

現在のところ、LLMのFunction Callingを使ってこれを直接行うことはできません。Function Callingの要求/応答メッセージはコンテキスト内で一緒に現れなければなりません。

したがって、次の形の関数を定義する代わりに:

  • register_interest_generator(interest: string) -> Iterator[Message]

次のようにする必要があります:

  • create_interest_task_and_return_success_immediately
      (interest: string, context_queue_callback: Callable[Message]) -> 
        Literal["in_progress", "canceled", "success", "failure"]

このトピックの詳細な議論については、以下の Performing async inference tasks セクションを参照してください。

LLM や API がマルチモーダルな会話ユースケースをよりよくサポートするように進化するにつれて、非同期関数やジェネレータとして機能する長時間実行される関数に関するアイデアを LLM 研究者が検討することを期待しています。

4.10.7 並列および複合Function Calling

並列Function Calling は、LLM が単一の推論レスポンス内で複数のFunction Callingを要求できることを意味します。複合Function Calling は、LLM が複数の関数を柔軟に順番に呼び出し、複雑な操作を実行するために関数を連鎖させることを意味します。

これらはエキサイティングな機能です!

しかし、それらは音声エージェントの動作の変動性を増加させます。つまり、並列および複合Function Callingが実際の会話で期待どおりに機能しているかをテストする評価とモニタリングを開発する必要があります。

並列Function Callingの処理はエージェントの実装をより複雑にします。特定の用途がない限り、多くの場合、並列Function Callingを無効にすることを推奨します。

複合Function Callingはうまく機能すると魔法のように感じられます。複合Function Callingの早期の興味深い例の一つは、Claude Sonnet 3.5 がファイル名とタイムスタンプに基づいてファイルからリソースをロードするために関数を連鎖させたのを見たことでした。

User: Claude, load the most recent picture I have of the Eiffel Tower.
function call request: <list_files()>
function call response: <['eiffel_tower_1735838843.jpg', 'empire_state_building_1736374013.jpg', 'eiffel_tower_1737814100.jpg', 'eiffel_tower_1737609270.jpg',
'burj_khalifa_1737348929.jpg']
function call request: <load_resource('eiffel_tower_1737814100.jpg')>
function call response: <{ 'success': 'Image loaded successfully', 'image': … }>
LLM: I have loaded an image of the Eiffel Tower. The image shows the Eiffel
Tower on a cloudy day.

LLM は2つの関数 – list_files()load_resource() – をどのように連鎖させて特定の指示に応答するかを自ら見つけ出します。これら二つの関数はツールリストに記述されています。しかし、この連鎖動作はプロンプトされているわけではありません。

複合Function Callingは最先端の LLM の比較的新しい機能です。性能は「でこぼこ」で、驚くほど良い一方で、苛立たしいほど一貫性に欠けます。

4.11. マルチモダリティ

LLM は現在、テキストに加えて音声、画像、動画を消費・生成します。

前述した speech-to-speech models について話しました。これらは音声を入力として受け取り、音声を出力として生成できるモデルです。

最先端モデルのマルチモーダル能力は急速に進化しています。

GPT-4o、Gemini Flash、Claude Sonnet はいずれも非常に優れた視覚能力を持ち、画像を入力として受け取ります。これらのモデルにおける視覚サポートは、画像の内容を説明したり、画像に表示されるテキストを書き起こししたりすることに重点を置いて始まりました。リリースごとに能力は拡張されます。物体のカウント、バウンディングボックスの特定、画像内のオブジェクト間の関係のより良い理解などは、より新しいリリースで利用可能な有用な能力です。

Gemini Flash は動画入力での推論も可能で、映像トラックと音声トラックの両方を理解できます。[40]

興味深い新しいクラスの音声対応アプリケーションは、画面を「見る」ことができ、ローカルマシンやウェブブラウザ上でタスクを実行するのを助けるアシスタントです。多くの人が音声駆動のウェブ閲覧のための足場を構築しています。

私たちが知っているいくつかのプログラマは、最近では入力する頻度と同じくらい話すことが多いです。音声入力を接続して Cursor や Windsurf を操作するのは比較的簡単です。[41] また、画面キャプチャを接続して AI プログラミングアシスタントが編集エディタ内のコード、構築中のウェブアプリの UI 状態、端末の Python スタックトレースなど、あなたが見ているものを正確に見るようにすることも可能です。この種の完全にマルチモーダルな AI プログラミングアシスタントは、この文書全体で述べてきた未来の断片のもう一つの姿のように感じられます。[42]

[40] GPT-4o と Claude の両方で、動画から個々のフレームを抽出し、そのフレームを画像としてコンテキストに埋め込むことで動画を処理できます。このアプローチには制約がありますが、いくつかの「動画」ユースケースではうまく機能します。

[41] 深い AI 統合とツーリングを備えた人気の新しいプログラミングエディタが二つあります。

[42] OpenAI Dev Day 2024 Singapore での swyx の講演、 "Engineering AI Agents" を参照してください。

現在、最先端モデルはさまざまな組み合わせでマルチモダリティをサポートしています。

  • GPT-4o (gpt-4o-2024-08-06) はテキストと画像の入力を持ち、テキスト出力を行います。
  • gpt-4o-audio-preview はテキストと音声の入力を持ち、テキストと音声の出力を行います。(画像入力はなし)
  • Gemini Flash はテキスト、音声、画像、動画の入力を持ちますが、出力はテキストのみです。
  • OpenAI の新しい speech-to-text と text-to-speech モデルは完全に操作可能で gpt-4o の基盤上に構築されていますが、テキストと音声の間の変換に特化しています:gpt-4o-transcribe、gpt-4o-mini-transcribe、gpt-4o-mini-tts。

マルチモーダルサポートは急速に進化しており、上記のリストはすぐに古くなると予想されます!

音声AIにとって、マルチモダリティの最大の課題は、音声や画像が多くのトークンを使用することであり、トークンが増えるほどレイテンシが高くなることです。

メディアの例 おおよそのトークン数
音声をテキスト化した場合の1分間の音声 150
音声としての1分間の音声 2,000
画像1枚 250
動画1分 15,000

一部のアプリケーションでは、多数の画像を扱いながら会話的なレイテンシを達成することが大きなエンジニアリング上の課題です。会話的レイテンシを達成するには、コンテキストを小さく保つか、ベンダー固有のキャッシュ API に依存する必要があります。画像はコンテキストに多くのトークンを追加します。

常時稼働し、作業ループの一部としてあなたの画面を監視するパーソナルアシスタントエージェントを想像してみてください。例えば、「1時間前に読もうとしていたツイートがあったけど電話がかかってきて忘れてタブを閉じてしまった。そのツイートは何だった?」と尋ねたいかもしれません。

1時間前はほぼ100万トークンに相当します。 たとえモデルがコンテキストに100万トークンを収容できたとしても[43]、そのような多数のトークンで毎ターン対話型の会話を行うコストとレイテンシは現実的ではありません。

[43] Hello, Gemini!

ビデオをテキストとして要約し、要約だけをコンテキストに保持することができます。埋め込みを計算して RAG のような検索を行うこともできます。LLM は特徴要約やFunction Callingを使用して複雑な RAG クエリをトリガーすることに非常に長けています。しかし、これらのアプローチはいずれもエンジニアリング的に複雑です。

最終的に最も効果のある手段はコンテキストキャッシングです。すべての最先端 API プロバイダはキャッシングのサポートを提供しています。現時点のキャッシング機能はいずれも音声AIユースケースにとって完全ではありません。今年はマルチモーダルでマルチターンの会話ユースケースが最先端モデルの訓練者の注目を集めるにつれて、キャッシュ API は改善されると予想しています。

5. 複数の AI モデルの使用

今日の実運用の音声AIエージェントは複数のディープラーニングモデルを組み合わせて使用しています。[44]

前述のとおり、典型的な音声AIの処理ループは、ユーザーの音声を speech-to-text モデルで文字起こしし、文字起こししたテキストを LLM に渡して応答を生成し、最後に text-to-speech ステップを実行してエージェントの音声出力を生成します。

さらに、多くの実運用の音声AIエージェントは今日、複雑で多様な方法で複数のモデルを使用しています。

[44] OpenAI と Google のベータ版音声-to-音声 API でさえ、ターン検出を実装するために専用の VAD とノイズ抑圧モデルを使用しています。

5.1. 複数のファインチューニング済みモデルの使用

ほとんどの音声AI エージェントは、OpenAI や Google(時には Anthropic や Meta)による SOTA[45] モデルを使用します。最新で最も性能の高いモデルを使用することは重要です。なぜなら音声AI のワークフローは一般にモデル能力の jagged frontier[46] の端にあり、ぎりぎりの領域で動作しているからです。音声エージェントは複雑な指示に従い、人間と自然な形で自由度の高い会話に参加し、関数やツールを確実に使える必要があります。

[45] SOTA — state of the art — は AI エンジニアリングで広く使われる用語で、おおまかに「主要なAI研究所からの最新の大型モデル」を意味します。

しかし、特定のユースケースでは、会話の異なる状態ごとにモデルをファインチューニングするのが合理的な場合があります。ファインチューニングされたモデルは、特定のタスクに対しては同等以上の性能を保ちながら、より小さく、より高速で、より安価に動作させることができます。

非常に大きな産業用部品カタログから部品注文を支援するエージェントを想像してください。このタスクでは、プラスチック材料、金属材料、締結具、配管、電気、保護具など、各カテゴリに焦点を当てた複数の異なるモデルを訓練するかもしれません。

[46] ウォートン教授の Ethan Mollick は、SOTA モデル能力の複雑な境界領域を表すために「jagged frontier」という用語を造りました — 場合によっては驚くほど優れていることもあれば、苛立たしいほど劣ることもあります。

ファインチューニングされたモデルは一般に2つの重要なカテゴリで「学習」できます:

  1. 組み込み知識 — モデルは事実を学習できます。
  2. 応答パターン — モデルはデータを変換する手法を学習できます。これには会話のパターンやフローの学習も含まれます。

仮に産業用サプライ会社が膨大な生データを持ってるとします:

  • 各部品のデータシート、メーカー推奨、価格、内部データを含む非常に大きなナレッジベース。
  • テキストチャットログ、電子メールのやり取り、サポート担当者との電話会話の文字起こし。
Using fine-tuned models

特定の会話トピックに対してファインチューニング済みモデルを使用する例。さまざまなアーキテクチャ上のアプローチが考えられます。この例では、各会話ターンの開始時に、ルータとなるLLMが全文脈を分類します。

この生データをファインチューニング用データセットに変換する作業は大きな仕事ですが、実行可能です。必要なデータクリーニング、データセット作成、モデル訓練、モデル評価はいずれもよく理解された問題です。

重要なポイント: いきなりファインチューニングに飛びつかないで — まずはプロンプトエンジニアリングから始めてください。

プロンプティングはほとんどの場合、ファインチューニングと同じタスク結果を達成できます。ファインチューニングの利点は、より小さなモデルを使えることにあり、それは推論が速くコストが低くなることに繋がります。[47]

プロンプティングを使えば、ファインチューニングよりもはるかに簡単に始められ、はるかに速く反復できます。[48]

会話の異なる状態に異なるモデルを使う方法を初期探索する際、プロンプトを小さな「モデル」と考えてください。大きく文脈に特化したプロンプトを作ることで LLM に何をすべきか教えています。

  1. 組み込み知識については、ナレッジベースから情報を引き出し、検索結果を効果的なプロンプトに組み立てる検索機能を実装してください。これについては、下の RAG とメモリ セクションを参照してください。
  2. 応答パターンについては、モデルに異なる質問への応答の例を埋め込みます。場合によっては数例で十分なこともあります。場合によっては、多数 — 100 を超える例が必要になることもあります。

[47] プロンプティングとファインチューニングの詳細を深く掘り下げたい場合は、以下の2つの古典的な論文を参照してください: Language Models Are Few-shot Learners と A Comprehensive Survey of Few-shot Learning.

[48] 古典的なエンジニアリングアドバイスに従ってください: まず動くようにし、次に速くし、最後に安くする。プロンプトエンジニアリングからファインチューニングに移行するのは、プロセスの 速く する段階の中盤あたりまで考えないでください。(そこに至ることがあるなら)

5.2. 非同期推論タスクの実行

時には LLM を実行に比較的長い時間がかかるタスクに使いたいことがあります。コアの会話ループでは応答時間を約1秒(またはそれ以下)にすることを目指していることを忘れないでください。タスクが数秒以上かかる場合、選択肢は2つあります:

  1. ユーザーに何が起きているかを伝え、待つように促す。「お調べしますので少々お待ちください…」
  2. より長いタスクを非同期で実行し、その間会話をバックグラウンドで続けられるようにする。「調べておきますね。それをしている間に他に何か質問はありますか?」

非同期で推論タスクを実行する場合、その特定タスクに別の LLM を使うことを選ぶかもしれません(コアの会話ループから切り離されているため)。音声応答に許容される速度より遅い LLM を使ったり、特定タスク向けにファインチューニングした LLM を使うことがあります。

非同期推論タスクのいくつかの例:

  • コンテンツの「ガードレール」を実装する。(コンテンツガードレール セクション参照。)
  • 画像を作成すること。
  • サンドボックスで実行するコードを生成すること。

推論モデルの最近の驚異的な進歩[49] により、LLM に頼めることの範囲が広がりました。ただし、これらのモデルは有用な出力を出す前に思考トークンの生成にかなりの時間を費やすことが多いため、音声AI 会話ループには直接使えません。とはいえ、マルチモデルの音声AI アーキテクチャの非同期パートとして推論モデルを使うのは有効に機能することがあります。

[49] 推論モデルの例としては DeepSeek R1、Gemini Flash 2.0 Thinking、OpenAI o3-mini などがあります。

非同期推論は通常 LLM のFunction Callingでトリガーされます。単純なアプローチは2つの関数を定義することです。

  • perform_async_inference() — これは長時間実行される推論タスクを実行すべきと LLM が判断したときに LLM によって呼び出されます。これを複数定義することもできます。注意点は、非同期タスクを開始してから直ちに基本的な タスク開始に成功した 応答を返す必要があることです。これはFunction Callingのリクエストとレスポンスのメッセージがコンテキスト内で正しい順序になるようにするためです。[50]
  • queue_async_context_insertion() — これは非同期推論が終了したときにオーケストレーション層によって呼び出されます。ここでの難点は、結果をコンテキストにどのように挿入するかがやろうとしていることと、使用している LLM/API が許可するものに依存することです。一つの方法は、進行中の会話ターンの終わり(すべてのFunction Callingの完了を含む)まで待ち、非同期推論結果を特別に作成したユーザーメッセージに入れ、それからもう一度会話ターンを実行することです。

[50] 非同期Function Calling を参照してください。

5.3. コンテンツガードレール

音声AIエージェントにはいくつかの脆弱性があり、特定のユースケースで重大な問題を引き起こします。

  • プロンプトインジェクション
  • 幻覚(ハルシネーション)
  • 知識の陳腐化
  • 不適切または危険なコンテンツの生成

コンテンツガードレール はこれらを検出しようとするコードの総称であり、LLM を偶発的または悪意のあるプロンプトインジェクションから保護し、ユーザーに送信される前に不適切な LLM 出力を検出します。

ガードレールに特定のモデル(またはモデル群)を使うことにはいくつかの利点があります:

  • 小型モデルはガードレールや安全性監視に適していることが多いです。問題のあるコンテンツを識別することは比較的専門的なタスクであり得ます。(実際、プロンプトインジェクションの回避については、必ずしも普通のプロンプトで応答できるモデルが望ましいわけではありません。)
  • ガードレール作業に別のモデルを使う利点は、それがメインモデルとまったく同じ弱点を持たない可能性があることです。少なくとも理論上は。

いくつかのオープンソースのエージェントフレームワークにはガードレールコンポーネントがあります。

  • llama-guard は Meta の llama-stack の一部です
  • NeMO Guardrails は、LLMベースの会話アプリケーションにプログラム可能なガードレールを追加するためのオープンソースツールキットです
NeMo Guardrails framework

NVIDIAのNeMo Guardrailsフレームワークがサポートする5種類のガードレール。図はNeMo Guardrailsのドキュメントより。

これら両方のフレームワークはテキストチャットを念頭に設計されており、音声AI向けではありません。しかし、どちらも有用なアイデアと抽象化を持っており、ガードレール、安全性、コンテンツモデレーションを考えるなら参考に値します。

重要なのは、LLMはここ1年でこれらの問題を回避する能力が非常に向上している点です。

最新の大手AI研究所のモデルでは、ハルシネーションはもはや重大な問題ではありません。現在、定期的に見られるハルシネーションは主に2種類です。

  • LLMが関数を呼び出すふりをするが、実際には呼び出していないケース。これはプロンプトで修正可能です。プロンプトでこの現象が起きないことを確認するには、適切な評価(evals)が必要です。評価でFunction Callingのハルシネーションが見られたら、再発しなくなるまでプロンプトを反復してください。(対話型の会話はLLMのFunction Calling能力に大きな負荷をかけるため、評価は実際の会話を模倣する必要があります。)
  • ウェブ検索を期待しているときにLLMがハルシネートするケース。組み込みの検索グラウンディングはLLM APIの比較的新しい機能です。LLMが検索を実行するかどうかはまだやや予測しにくいです。検索を行わない場合、重みの中に埋め込まれた(古い)知識やハルシネーションで応答することがあります。Function Callingのハルシネーションとは異なり、プロンプトだけで簡単に修正できるものではありません。しかし、検索が実際に行われたかどうかは容易に確認できます。したがって、アプリケーションのUIにその情報を表示したり、音声会話に注入したりできます。アプリがウェブ検索に依存する場合、これを行うのは良い考えです。問題をユーザーに理解・対処させる形に移すことになりますが、ユーザーから「検索した」「検索しなかった」の区別を隠すよりは良いです。肯定的に言えば、検索が機能すると、古くなった知識の問題を大幅に解消できます。

主要なラボの全てのAPIには非常に優れたコンテンツ安全フィルターが備わっています。

プロンプトインジェクションの回避も1年前よりかなり改善されていますが、LLMが新たな機能を獲得するにつれて潜在的なプロンプトインジェクション攻撃の対象領域は拡大します。例えば、画像内テキストからのプロンプトインジェクションが今問題になっています。

非常に一般的な指針として:今日の音声AIユースケースでは、通常のユーザー行動によって偶発的にプロンプトインジェクションが発生することは稀です。しかし、ユーザー入力だけでシステム命令を覆すようにLLMの挙動を操ることは確実に可能です。これを念頭にエージェントをテストすることが重要です。特に、バックエンドシステムにアクセスする任意の関数へ渡すLLM生成の入力は必ずサニタイズし、クロスチェックすることが非常に重要です。

5.4. 単一推論アクションの実行

AIエンジニアにとって、LLMを活用する方法を学ぶのは継続的なプロセスです。そのプロセスの一部は、これらの新しいツールに対する思考の転換です。LLMを最初に使い始めたとき、多くの人は「言語モデルは何を独自にできるのか?」という視点で考えていました。しかし、LLMは汎用ツールです。非常に広範囲の情報処理タスクに優れています。

音声エージェントの文脈では、常にLLM推論を実行するコードパスを用意しています。LLMをコアの会話ループにのみ使う必要はありません。

たとえば:

  • 正規表現に頼ろうとするたびに、おそらくプロンプトを書けば代替できることが多いです。
  • LLM出力の後処理はしばしば有用です。たとえば、UI表示用のテキストと会話用の音声という2つのフォーマットで出力を生成したい場合があります。会話用LLMに整形されたマークダウンを生成させ、次に音声生成向けに短く再フォーマットするように再度LLMにプロンプトする、ということができます。[51]
  • 再帰は強力です。[52] LLMにリストを生成させ、それぞれの要素に対してLLMを再度呼び出して操作を行う、といったことが可能です。
  • マルチターン会話を要約したくなることがよくあります。LLMは素晴らしく、操作可能な要約器です。これについては以下のスクリプティングと命令追従のセクションで詳述します。

[51] LLM出力の後処理に関しては、上記のコンテンツガードレールセクションも参照してください。

[52] 我々はプログラマーなので、当然… — 編集部より。

これらの新たに出現する実装パターンの多くは、言語モデルが自身または別の言語モデルをツールとして使うように見えます。

これは非常に強力なアイデアであり、2025年には多くの人がこれに取り組むことが予想されます。エージェントフレームワークはライブラリレベルのAPIにこれを組み込むサポートを構築できます。モデルはFunction Callingやコード実行を行うように訓練されるのと大まかに類似した方法で、再帰的に推論を行うよう訓練され得ます。

5.5. 自己改善システムに向けて

SOTAの「モデル」にAPI経由でアクセスする際、我々がアクセスしているのは単一のアーティファクトではありません。APIの背後にあるシステムは、ルーティング、マルチステージ処理、分散システム技術を駆使して、高速に、柔軟に、信頼性高く、そして非常に大規模に推論を行います。これらのシステムは常に調整されます。重みが更新されます。低レベルの推論実装は常により効率的になっています。システムアーキテクチャも進化します。

大手ラボは、ユーザーがAPIをどのように使うかと、推論や他の機能をどのように実装するかの間のフィードバックループを継続的に短縮しています。

これらのますます速くなるフィードバックループは、近年の驚くべきマクロレベルのAI進展の大きな要因です。

これに触発されて、我々のエージェントレベルのコードにおけるマイクロレベルのフィードバックループはどのように見えるでしょうか?会話中にエージェントの性能を改善する特定の足場(スキャフォールド)を組み込めるでしょうか?

  • エージェントがユーザーの発話が終わる前にどれくらい割り込むかを監視し、VADのタイムアウトなどのパラメータを動的に調整する。
  • ユーザーがエージェントに割り込む頻度を監視し、LLMの応答長を動的に調整する。
  • ユーザーが会話を理解するのに苦労していることを示すパターンを探し出す — 例えばユーザーがその言語を母国語としないスピーカーである可能性がある。会話スタイルを調整するか、言語切替を提案する。

他に思いつくアイデアはありますか?

User: How has MNI performed recently?
Agent: The Miami Dolphins won their game yesterday 21
to 3 and now lead the AFC East with two games remain-
 ing in the regular season.
User: No, I meant the stock MNI.
Agent: Ah, my apologies! You're asking about the
stock performance of MNI, which is the ticker symbol
 for McClatchy Company …
 From this point on, the model will bias towards
interpreting phonemes or transcribed text as "MNI"
 rather than "Miami".
            

マルチターンセッション中のユーザーフィードバックに基づいて挙動を調整するLLMの例(コンテキスト内学習)

6. スクリプトと命令追従

1年前、自然な人間の待ち時間でのオープンエンドな会話が可能な音声エージェントを構築できるだけでも興奮するものでした。

現在では、音声AIエージェントを実世界の複雑なタスクに展開しています。今日のユースケースでは、セッション中にLLMに特定の目標に集中させる指示が必要です。しばしば、LLMに特定の順序でサブタスクを実行させる必要があります。

たとえば、医療の患者受付ワークフローでは、エージェントに次のことを行ってほしいです:

  • 何よりも先に患者の本人確認を行うことを確認する。
  • 患者が現在服用している薬を必ず尋ねること。
  • 患者が薬Xを服用していると言った場合、特定のフォローアップ質問をすること。
  • などなど…

我々は、ステップごとのワークフローを作成することを スクリプティング と呼びます。過去1年の音声AI開発で得た教訓の1つは、プロンプトエンジニアリングだけでスクリプトの信頼性を達成するのは難しい、ということです。

単一のプロンプトに詰め込める詳細には限界があります。関連して、マルチターン会話でコンテキストが大きくなると、LLMが追跡すべき情報が増え、命令追従の精度は低下します。

多くの音声AI開発者は、複雑なワークフロー構築に状態機械アプローチへ移行しています。LLMを導くために長く詳細なシステム命令を書く代わりに、一連の状態を定義できます。各状態は:

  • システム命令とツールのリスト。
  • 会話コンテキスト。
  • 現在の状態から別の状態への一つ以上の移動手段。

各状態遷移は以下を行う機会です:

  • システム命令とツールのリストを更新する。
  • コンテキストを要約または修正する。[53]

[53] 通常は、コンテキスト要約を実行するためにLLM推論呼び出しを行います。:-)

状態機械アプローチは、より短く焦点を絞ったシステム指示、ツール一覧、およびコンテキストがLLMの指示遵守を大幅に改善するため、うまく機能します。

課題は、一方でLLMの自由で自然な会話能力を活用することと、他方でLLMがやるべき重要な部分を確実に実行するようにすることの間で、適切なバランスを見つけることです。

Pipecat Flows は、開発者がワークフローステートマシンを作成するのを支援する、Pipecatの上に構築されたライブラリです。

ステート図はJSONとして表現され、Pipecatプロセスにロードできます。これらのJSONステート図を作成するためのグラフィカルエディタがあります。

Pipecat Flows graphical editor

Pipecat Flows グラフィカルエディタ

Pipecat Flows とステートマシンは現在多くの開発者に採用されています。しかし、複雑なワークフローの抽象化を構築するための他の興味深い考え方もあります。

AI研究開発の活発な分野の一つはマルチエージェントシステムです。ワークフローを通過すべき一連の状態として考えるのではなく、マルチエージェントシステムとして考えることもできます。

Pipecatのコアとなるアーキテクチャ要素の一つは並列パイプラインです。並列パイプラインを使うと、処理グラフを通るデータを分割して二度(あるいはそれ以上)処理できます。データをブロックしたりフィルタリングしたりできます。多数の並列パイプラインを定義できます。ワークフローをゲート付きで調整された並列パイプラインの集合として考えることもできます。

音声AIツールの急速な進化は刺激的であり、こうした新しい種類のプログラムを構築するための最良の方法を見つける段階がまだ初期であることを強調しています。

7. 音声AIの評価(Evals)

非常に重要なツールの一種が eval、すなわち評価です。

Eval は、システムの能力を評価し品質を判断するツールやプロセスを指す機械学習用語です。

7.1. 音声AIの評価はソフトウェアのユニットテストとは異なる

従来のソフトウェアエンジニアリング出身であれば、テストを(主に)決定論的な作業として考える習慣があるでしょう。

音声AIは従来のソフトウェア工学とは異なるテストを必要とします。音声AIの出力は非決定論的です。音声AIのテスト入力は複雑で分岐するマルチターンの会話です。

特定の入力が特定の出力を生成することをテストする代わりに (f(x) = y)、確率的な評価を実行する必要があります — ある種のイベントがどのくらいの頻度で発生するかを見るために多数のテストランを行います[54]。あるテストではケースのクラスを10回中8回正しく処理できれば受容可能で、他のテストでは精度が9.99/10である必要があります。

[54] ユーザー要求が満たされた、エージェントがユーザーの発話を遮った、エージェントが話題から逸れた、など

単一の入力だけがあるのではなく、多数の入力、すなわちすべてのユーザー応答があることになります。これにより、ユーザー行動をシミュレートしようとしない限り、音声AIアプリケーションのテストが非常に困難になります。

最後に、音声AIのテストは二値的な結果を持たず、伝統的なユニットテストのように明確な✅や❌を返すことは稀です。代わりに、結果をレビューしてトレードオフを判断する必要があります。

7.2. 失敗モード

音声AIアプリには特有の形態や失敗モードがあり、評価の設計と実行に影響を与えます。レイテンシは重要です(テキストモードでは許容されるレイテンシが音声システムでは失敗になります)。マルチモデル構成です。(例えば、性能の低下はLLMの挙動ではなくTTSの不安定さが原因である可能性があります)。

今日しばしば課題となる分野には次のようなものがあります:

  • 最初の発話までの時間やエージェント応答までのレイテンシ
  • 文字起こしの誤り
  • 住所、メール、名前、電話番号の理解と発話
  • 割り込み(途中遮断)

7.3. 評価戦略の策定

基本的な評価プロセスは、プロンプトとテストケースを記載したスプレッドシートほど簡単なものでも構いません。

典型的なアプローチの一つは、新しいモデルをテストしたりシステムの主要部分を変更したりするたびに各プロンプトを実行し、LLMを使って応答が期待されるパラメータの定義内に収まるかを判定することです。

基本的な評価を持つことは何も持たないよりずっと良いですが、評価に投資し、非常に良い評価を持つことは、規模を運用し始めると重要になります。

音声AIユースケース向けの高度なツールを提供する評価プラットフォームは出始めたばかりです。音声(オーディオ)評価の特定ワークフローとツールに早期投資しているプラットフォームとしては、CovalFreePlay、および Weights & Biases Weave の三つがあります。いずれもPipecatとの統合が良好です。

A screenshot from the Coval evals platform UI

Coval evals プラットフォームUIのスクリーンショット

これらのプラットフォームは次の点で役立ちます:

  • プロンプトの反復改善(プロンプトイテレーション)。
  • オーディオ、ワークフロー、Function Calling、会話の意味的評価に関する既製の指標。
  • 問題領域のヒルクライミング(例えば、エージェントを割り込み対応でより良くする)。
  • 回帰テスト(ある問題領域を修正したときに、以前に解決された他の領域での回帰を導入していないことを確認する)。
  • 開発者による変更やユーザーコホート間での、時間経過に伴う性能変化の追跡。

8. 電話インフラストラクチャとの統合

今日最も急成長している音声AIサービスの多くは電話通話に関係しています。現在、大量のAI音声エージェントが電話に応答し、発信を行っています。

この一部は従来のコールセンターで起きています。コールセンターは主に音声AIを「ディフレクション率(自動化で処理できる通話の割合)」を改善する技術と見なしています。これにより音声AI導入のROIが明確になります。LLMの1分あたりコストが人間エージェントの1分あたりコストより安ければ、導入判断は容易です[55]

[55] もちろん、AIエージェントの性能が良好であることが前提です。今日の多様なカスタマーサポートユースケースの多くでは、その条件が満たされています。

ただし、単純なROI計算を超えて採用を加速するいくつかの興味深い動きが起きています。

音声AIエージェントは人間のスタッフとは異なる方法でスケーラブルです。一度音声AIを導入すると、ピーク時の待ち時間が短くなります。(その結果として顧客満足度スコアが上がります。)

また、LLMにより良いツールを与えることで、LLMが人間エージェントより良い仕事をすることがあります。多くのカスタマーサポート状況では、人間のエージェントは複数のレガシーなバックエンドシステムを扱わなければなりません。適時に情報を見つけることが課題になります。同じ状況に音声AIを導入する際には、これらのレガシーシステムへのAPIレベルのアクセスを構築する必要があります。新しいLLM+APIレイヤーが音声AIへの技術移行を可能にしています。

生成AIが今後数年でコールセンターの景観を完全に再構築することは明らかです。

コールセンター以外でも、音声AIは小規模ビジネスが電話応対を行う方法や、電話を情報探索と調整の手段として使う方法を変えています。我々は毎日、あらゆる業界向けの専門的なAI電話ソリューションを構築するスタートアップと話をしています。

この分野の人々はよく冗談めかして、やがて人間は電話をかけたり受けたりしなくなるだろうと言います。電話はすべてAI対AIになるだろうと。私たちが見るトレンドラインからすると、その言葉には一定の真実があります!

音声AIのための電話技術に関心があるなら、いくつかの頭字語や共通の概念を知っておくとよいです。

  • PSTNはpublic, switched, telephone network(公衆交換電話網)です。実際の電話番号を持つ実際の電話とやり取りする必要がある場合、PSTNプラットフォームと連携する必要があります。Twilioはほとんどの開発者が聞いたことのあるPSTNプラットフォームです。
  • SIP は IP 電話に使用される特定のプロトコルですが、一般的な意味ではシステム間の電話インターコネクトを指すために SIP と呼ばれます。たとえばコールセンターの技術スタックと連携する場合は SIP を使用する必要があります。SIP プロバイダと契約するか、独自の SIP サーバーをホストすることができます。
  • DTMF トーンは電話メニューを操作するためのキー押下音です。音声エージェントは実世界の電話システムと対話するために DTMF トーンを送信できる必要があります。LLM は電話ツリーの処理がかなり得意です。少しプロンプトエンジニアリングを行い、DTMF トーンを送信する関数を定義すれば対応できます。
  • 音声エージェントはしばしば通話転送を実行する必要があります。単純な転送では、音声AIが関数を呼び出して通話転送をトリガーすることでセッションを終了します。[56] ウォームトランスファー は、エージェント同士が通話者を第二のエージェントに接続する前に互いに会話する形での引き継ぎです。音声AIエージェントは人間と同様にウォームトランスファーを行うことができます。音声エージェントは最初に人間の通話者と話し、次に通話者を保留にして新しい人間エージェントと会話を行い、その後通話者をその人間エージェントにつなぎます。

[56] 実際の転送操作は、テレフォニー プラットフォームへの API 呼び出しか、SIP REFER アクションである可能性があります。

9. RAG とメモリ

音声AIエージェントはしばしば外部システムから情報にアクセスします。たとえば、次のようなことが必要になるかもしれません:

  • ユーザーに関する情報を LLM のシステム指示に組み込む。
  • 過去の会話履歴を取得する。
  • ナレッジベースで情報を参照する。
  • ウェブ検索を行う。
  • リアルタイムの在庫や注文状況の確認を行う。

これらはすべて RAG(Retrieval Augmented Generation:検索強化生成)のカテゴリに入ります。RAG は情報検索と LLM プロンプトを組み合わせることを指す一般的な AI エンジニアリング用語です。

音声エージェントにおける「最も単純な RAG」は、会話開始前にユーザー情報を参照し、その情報を LLM のシステム指示に統合することです。

user_info = fetch_user_info(user_id)

system_prompt_base = "You are a voice AI assistant..."

system_prompt = (
  system_prompt_base
  + f"""
The name of the patient is {user_info["name"]}.
The patient is {user_info["age"]} years old.
The patient has the following medical history: {user_info["summarized_history"]}.
"""
)

単純な RAG – セッション開始時にルックアップを実行する

RAG は奥が深く、急速に変化している分野です。[57] 手法は、上記のような基本的なルックアップと文字列挿入を行う比較的単純なアプローチから、埋め込みとベクトルデータベースを使って非常に大量の半構造化データを整理するシステムまで多岐に渡ります。

[57] うーん。最近の生成 AI の他の分野と同じように聞こえますね。

多くの場合、80/20 のアプローチで大きな効果が得られます。既存のナレッジベースがあるなら、すでにある API を使いましょう。検索した結果を会話コンテキストに注入するいくつかのフォーマットをテストするための簡単な評価を作成します。本番にデプロイして、実際のユーザーでどの程度うまく機能するかを監視します。

async def query_order_system(function_name, tool_call_id, args, llm, context, result_callback):
  "First push a speech frame. This is handy when the LLM response might take a while."
  await llm.push_frame(TTSSpeakFrame("Please hold on while I look that order up for you."))

  query_result = order_system.get(args["query"])
  await result_callback({
    "info": json.dumps({
     "lookup_success": True,
     "order_status": query_result["order_status"],
     "delivery_date": query_result["delivery_date"],
    })
 })

llm.register_function("query_order_system", query_order_system)

セッション中の RAG。情報参照が必要なときに LLM が呼び出せる関数を定義します。この例では、システムが応答に数秒かかることをユーザーに伝えるために、あらかじめ設定した発話フレーズも出力しています。

いつものように、音声AIではレイテンシが非音声システムより大きな課題になります。LLM がFunction Callingリクエストを行うと追加の推論呼び出しがレイテンシに加わります。外部システムでの情報参照も遅くなる可能性があります。作業中であることを知らせるために、RAG ルックアップを実行する前に簡単な音声出力をトリガーするのが有用なことが多いです。

より広く見れば、セッション間のメモリは有用な機能です。あなたが話したことをすべて覚えておく必要がある音声AI個人アシスタントを想像してください。一般的には次のような二つのアプローチがあります:

  1. 各会話を永続ストレージに保存する。会話をコンテキストに読み込むためのいくつかのアプローチをテストする。たとえば、パーソナルアシスタントのユースケースにうまく機能する戦略は次のようなものです:エージェント起動時に最新の会話を常に完全に読み込み、最新 N 件の会話の要約を読み込み、必要に応じて LLM が古い会話を動的に読み込めるようにルックアップ関数を定義する。
  2. 会話履歴の各メッセージをメッセージグラフに関するメタデータとともにデータベースに個別に保存する。すべてのメッセージをインデックス化する(おそらくセマンティック埋め込みを使用)。これにより、分岐する会話履歴を動的に構築できます。アプリが画像入力(LLM Vison)を多用する場合はこれを検討したいかもしれません。画像はコンテキスト空間を多く消費します![58] このアプローチは分岐型 UI を構築することも可能にし、AIアプリ設計者がこれから探求し始めている方向の一つです。

[58] 「マルチモーダリティ」を参照してください。

10. ホスティングとスケーリング

音声AIアプリケーションには従来型のアプリコンポーネント(ウェブアプリのフロントエンド、API エンドポイント、その他のバックエンド要素)が含まれることが多いです。しかしエージェントプロセス自体は従来のアプリコンポーネントとは十分に異なるため、音声AIのデプロイとスケーリングには固有の課題があります。

10.1 アーキテクチャ

  • 音声AIエージェントの会話ループは通常長時間実行されるプロセスです(単一の応答生成が終了したら終了するリクエスト/レスポンス関数ではありません)。
  • 音声エージェントはリアルタイムでオーディオをストリームします。ストリーミングが滞るとオーディオのグリッチが生じます。(共有仮想マシンでの CPU スパイク、オーディオスレッドの実行を 10ms でもブロックするようなプログラムフローなど)
  • 音声エージェントは通常 WebSocket または WebRTC のいずれかの接続を必要とします。クラウドサービスのネットワークゲートウェイやルーティング製品は HTTP をサポートするほど WebSocket をうまくサポートしていないことが多いです。UDP をまったくサポートしていないこともあります。(WebRTC には UDP が必要です。)

これらすべての理由から、音声AIにサーバーレスフレームワーク(AWS Lambda や Google Cloud Run など)を使用するのは一般的に不可能です。

現在のベストプラクティスは次のとおりです:

  • プロトタイピング段階を突破したら、エンジニアリング時間を投資してエージェントをデプロイするための軽量ツールを作成し、Docker(または類似)コンテナをビルドするようにします。
  • コンテナを選択したコンピュートプラットフォームにプッシュします。シンプルなデプロイでは固定数の仮想マシンを稼働させ続けるだけで十分です。しかしある時点で、自動スケールや新しいバージョンの優雅なデプロイ、サービスディスカバリとフェイルオーバーの実装、その他大規模運用のためのDevOps要件を満たすためにプラットフォームのツールに接続したくなるでしょう。
  • Kubernetes は現在、コンテナ、デプロイ、およびスケーリングを管理するスタンダードです。Kubernetes は習得コストが高いですが、主要なクラウドプラットフォームのすべてでサポートされています。Kubernetes の周りには非常に大きなエコシステムがあります。
  • ソフトウェア更新をデプロイする際には、既存の接続がセッション終了まで維持されるように長いドレイン時間を設定したいでしょう。これは Kubernetes ではそれほど難しくありませんが、詳細は k8s エンジンとバージョンによって異なります。
  • コールドスタートは音声AIエージェントにとって問題です。接続時間が重要だからです。アイドル状態のエージェントプールを維持することが長いコールドスタートを回避する最も簡単な方法です。ワークロードがローカルで大規模モデルを実行することを要求しない場合、比較的少ない労力で高速なコンテナコールドスタートを設計できます。[59]

仮想マシンスペックとコンテナの詰め込み(packing)は、本番デプロイで人々をつまずかせることがよくあります。エージェントに必要なスペックは、使用するライブラリやエージェントプロセス内でどれだけ CPU 集約的な作業を行うかによって異なります。経験則としては、まず開発マシンでエージェントプロセスが消費する最大メモリの 2 倍の RAM を確保し、仮想マシンの CPU ごとにエージェントを 1 つ実行することから始めると良いでしょう。[60]

[59] 大規模モデルをローカルで実行している場合、コールドスタートに関するアドバイスは本ガイドの範囲外です。GPU やコンテナの最適化に精通していない場合、必要なツールを自分で開発する学習曲線をたどるよりも専門家を見つけることを検討したほうが良いでしょう(少なくとも十分な規模で運用してコストを平準化できるまで)。

[60] コンテナランタイムがアイドルの CPU 上で新しいエージェントプロセスを起動していることを確認してください。これは常に k8s のデフォルトではありません。

10.2 分あたりコストの計算

音声AIのコストは、使用するモデル、API、およびホスティングインフラストラクチャによって大きく異なります。コストはユースケースにも依存します。たとえば上の コスト比較 で述べたように、セッションが長くなるほど分あたりコストは一般的に高くなります。またテレフォニーは WebRTC トランスポートより高価です。

コストは、OpenAIRealtime API のような音声対音声 API を使用する場合は分あたり $0.20 以上、ホスト型のバッテリーインクルード型エージェントプラットフォームで分あたり $0.10、かなり規模の大きいコスト最適化されたデプロイでは分あたり $0.02 まで幅があります。

時々見られる誤りの一つは、音声と LLM API のコストを計算する前にエージェントホスティング自体をコスト最適化してしまうことです。一般に、エージェントプロセス自体のクラウドランタイムコストは総分あたりコストの 1% 未満です。 vCPU あたりのエージェント同時実行数を最適化するためにエンジニアリング作業を費やす価値はほとんどありません。

こちらはインタラクティブなコスト計算機で、Gustavo Garcia によって開発されました。

An interactive cost calculator

インタラクティブなコスト計算機

あるいは、スプレッドシートが好みなら、ここにあるスプレッドシート をコピーして分あたりコスト計算の出発点として使うことができます。

A spreadsheet to calculate per-minute costs for voice AI agents

音声AIエージェントの分あたりコストを計算するためのスプレッドシート

スプレッドシートのスクリーンショットの数字は、Deepgram、GPT-4o、Cartesia を使用するセルフホスト型エージェントのものです。10 分セッションの場合、分あたりコストは約 2.5 セントです。文字起こしと LLM 推論がそれぞれコストの約 1/4、音声生成が約半分を占めます。ホスティングはコストの 1% 未満です。

もちろん、これはセルフホスティングの実際のコストを現実的に表したものではありません。自分ですべてのホスティングインフラを構築・維持する場合、エージェント自体に加えて多数のシステムや機能をセットアップ、スケール、保守する必要があります。

  • サービスディスカバリ
  • ロードバランシング
  • ロギング
  • モニタリング
  • 帯域幅
  • 複数リージョン
  • セキュリティ
  • コンプライアンスおよび規制機能(たとえばデータ居住性)
  • 分析
  • カスタマーサポート

11. 2025年に来るもの

AIの成長に関連して、音声AIは2024年に大きく成長し、2025年もこの傾向が続くと予想されます。

この音声AIに対する関心と採用の拡大は、以下の重要な領域での継続的な進歩を生み出します:

  • すべてのモデル作成者とサービスプロバイダによるさらなるレイテンシ最適化。長い間、多くのサービス実装者やほとんどの公開ベンチマークはスループットに焦点を当ててきましたが、音声AIではトークン毎秒よりも最初のトークン到達時間(time to first token)をはるかに重視します。
  • すべての非テキストモダリティがモデルとAPIに完全統合する方向への進展。
  • テストおよび評価ツールにおける、より多くの音声特化機能。
  • リアルタイムのマルチモーダルユースケースのニーズをサポートするコンテキストキャッシングAPI。
  • 複数プロバイダによる新しい音声AIエージェントプラットフォーム。
  • 複数プロバイダによるスSpeech-to-speechモデルAPI。
  • 文字起こし精度と音声生成品質を向上させるためにコンテキストを取り込めるコンテクスチュアルスピーチモデル。

音声AI分野の4人の専門家による2025年の見解を聞きたい場合は、1月のサンフランシスコVoice AI Meetupのパネル記録の54:05に飛んでください。Karan Goel、Niamh Gavin、Shrestha Basu-Mallick、Swyxが来年に見られると予想する内容として、ユニバーサルメモリ、ハリウッドでのAI、モデルの模倣から理解への移行、そしてロボティクスに関する対立的な見解をそれぞれ述べています。

楽しい一年になりそうです。

寄稿者

責任著者

Kwindla Hultman Kramer

評価セクションの支援をしてくれたBrooke Hopkins、Llamaの性能とUltravoxに関する洞察を提供してくれたZach Koch、コンテクスチュアルスピーチモデルへの移行の重要性に関する注釈をくれたBrendan Iribeに感謝します。

寄稿著者[61]

aconchillo, markbackman, filipi87, Moishe, kwindla, kompfner, Vaibhav159, chadbailey59, jptaylor, vipyne, Allenmylath, TomTom101, adriancowham, imsakg, DominicStewart, marcus-daily, LewisWolfgang, mattieruth, golbin, adithyaxx, jamsea, vr000m, joachimchauvet, sahilsuman933, adnansiddiquei, sharvil, deshraj, balalofernandez, MaCaki, TheCodingLand, milo157, RJSkorski, nicougou, AngeloGiacco, kylegani, kunal-cai, lazeratops, EyrisCrafts, roey-priel, aashsach, jcbjoe, Dev-Khant, wg-daniel, cbrianhill, ankykong, nulyang, flixoflax, DANIIL0579, Antonyesk601, rahultayal22, lucasrothman, CarlKho-Minerva, 0xPatryk, pvilchez, pedro-a-n-moreira, RonakAgarwalVani, xtreme-sameer-vohra, shaiyon, soof-golan, yashn35, zboyles, balaji-atoa, eddieoz, mercuryyy, rahulunair, porcelaincode, weedge, wtlow003, zzz-heygen, adidoit, ArmanJR, Bnowako, chhao01, Regaddi, cyrilS-dev, DamienDeepgram, danthegoodman1, dleybz, ecdeng, gregschwartz, KevGTL, louisjoecodes, M1ngXU, mattmatters, MoofSoup, natestraub

デザイン

Sascha Mombartz

Akhil K G

日本語訳

Tomoaki Imai