コンテンツにスキップ

ボイスエージェントの遅延最適化:ステップバイステップガイド

公開日

聴くこの記事を聴く

ボイスエージェントの応答速度は、ユーザーが話し終えてからエージェントが返答を始めるまでの合計遅延で決まります。この遅延は、たいてい1つの遅い要素だけが原因ではありません。複数の独立した段階で少しずつ積み重なり、それぞれが数十〜数百ミリ秒を加算します。遅延を減らすには、各段階でどれだけ時間がかかっているかを把握する必要があります。

ボイスエージェントの遅延最適化とは、その時間がどこで発生しているかを見つけ、段階ごとに短縮していく作業です。

この記事は、遅延の概念的な概要 の補足となります。そちらのページが遅延とは何かを説明しているのに対し、こちらではアーキテクチャや計測方法を解説します。これを読むことで、計測可能な遅延予算と具体的なアクションプランが得られます。

要点まとめ

  • Time-to-first-audio(TTFA)はパイプライン全体の指標であり、単一モデルの推論時間ではありません。
  • LLMのtime-to-first-tokenとエンドポイント判定が、遅延の2大要素です。
  • 各段階を直列ではなく重ねて実行することで、遅延予算の多くを回収できます。
  • ストリーミング、コーデック選択、プレイヤーバッファの調整で、それぞれ数ミリ秒単位の短縮が可能です。
  • 自分の運用環境ごとに計測し、P50とP95をレポートしてください。

ボイスエージェントの遅延予算の定義

遅延予算とは、パイプライン各段階に割り当てる合計time-to-first-audio(TTFA)の目標値です。各段階に許容値を設定し、その合計が目標値を下回るようにします。これを定義するのが最初のステップですが、ここでよく間違いが起きます。エンジニアが似ているが意味の異なる2つの数値を混同してしまうためです。

1つ目はモデル推論遅延です。これはモデルが出力を生成するのにかかる時間です。例えば、Flashモデル では、一般的な短い入力の場合、約75ms(ネットワークやアプリケーションのオーバーヘッドを除く)です。これは内部的な数値で、モデル同士の比較には役立ちますが、ユーザーが体感する遅延ではありません。

ユーザー視点では、time-to-first-audio(TTFA)に注目します。これは、ユーザーが話し終えてからエージェントの返答が聞こえるまでの経過時間です。TTFAは、どのモデルの推論遅延よりも必ず大きくなります。なぜなら、パイプライン全体の合計だからです。

カスケード型ボイスエージェントは、5つの段階で構成されます:

  • キャプチャ(マイク)→ STT → LLM → TTS → 再生

マイクから音声を取得し、テキストに書き起こし、言語モデルに送信し、モデルのテキストを音声に合成し、その音声をバッファして再生します。各段階で遅延が発生し、意外な段階が最も大きなコストになることもあります。

ここでは、ユーザーの近くにサーバーがある英語エージェントの例を紹介します。数値はあくまで参考値です。

What it covers
Capture + endpointing
Mic capture, VAD/turn-detection delay before the turn is considered finished
STT finalization
Last partial to committed transcript after end-of-speech
Network (client to your server to our API)
Round-trips across the pipeline
LLM time-to-first-token
Prompt processing until the first usable token
TTS time-to-first-audio
First TTS request until first audio chunk leaves the model
Player buffering
Client-side buffer before playback begins
End-to-end TTFA
The total latency of the end-to-end pipeline
P50
Capture + endpointing
120 ms
STT finalization
60 ms
Network (client to your server to our API)
60 ms
LLM time-to-first-token
250 ms
TTS time-to-first-audio
110 ms
Player buffering
80 ms
End-to-end TTFA
~680 ms
P95
Capture + endpointing
280 ms
STT finalization
150 ms
Network (client to your server to our API)
160 ms
LLM time-to-first-token
600 ms
TTS time-to-first-audio
220 ms
Player buffering
150 ms
End-to-end TTFA
~1560 ms

通常、最も大きな遅延要素はLLMのtime-to-first-tokenと、チェーンの最初にあるエンドポイント判定です。

表にするとパイプライン全体が可視化できますが、各段階が厳密に直列で動作するように見えてしまいます。実際にはそうではありません。最も効果的な遅延最適化の多くは、段階を重ねて実行することで実現されており、その重なりによって下記の遅延予算の多くが回収されています。

スピーチtoテキスト:書き起こしとエンドポイント遅延の最適化

書き起こしはパイプラインの2段階目ですが、実際のコストは書き起こし自体ではなく、ユーザーが話し終えたと判断するタイミングにあります。このセクションでは、両方の観点からボイスエージェントの遅延最適化方法を解説します。

書き起こしはLLMに到達する前に行われます。Scribe v2 リアルタイム(scribe_v2_realtime)は、約150msで部分的な書き起こしを返し、音声をチャンクごとにストリーミングします。そのため、ユーザーが話している間に書き起こしが進みます。8kHz〜48kHzのPCMやmu-lawエンコーディングに対応しており、これは後述のコーデック選択にも関係します。150msの部分書き起こしは低コストです。

より大きな遅延コストはエンドポイント判定、つまりシステムがユーザーの発話が本当に終わったと判断する瞬間です。

Voice Activity Detection(VAD)は無音で発話を区切りますが、ここで時間が積み重なります。例えば、700msの無音を待ってターン終了と判断する場合、書き起こし自体に加えて毎回700msの遅延が発生します。この遅延は書き起こし精度のベンチマークでは見えませんが、実際の会話では大きな影響があります。パイプライン全体で最もコントロールしやすい遅延であり、だからこそ最適化の出発点として最適です。

エンドポイント判定は、応答速度と割り込みリスクのトレードオフです。無音の閾値を短くすると素早く返答できますが、自然な間でユーザーの発話を途中で切ってしまうリスクがあります。長くすると安全ですが、反応が遅くなります。実際には、スピーチtoテキストの遅延を最適化するには次の3つの変更が有効です:

  1. 無音閾値の微調整:ユーザーの自然な間を切らない最小値まで無音閾値を短縮し、推測ではなく本番環境で割り込み率を計測してください。
  2. 物理的な制御イベントの埋め込み: アプリケーションが他のシグナル(プッシュトゥトークの解除やUIイベントなど)でターン終了を把握できる場合は、VADタイマーを待たず手動コミット制御を使いましょう。
  3. LLM処理との重なり:部分書き起こしを早めに下流へ流します。安定した部分書き起こしをLLMに渡し、最終書き起こしと異なる場合は修正します。これは推測実行の一種で、エンドポイント遅延をLLMプロンプト処理の裏で隠すことができます。

詳細は、Scribe v2 Realtimeについてスピーチtoテキスト機能 のページや、リアルタイムスピーチtoテキスト のプロダクトページでご覧いただけます。

LLMの遅延要素

言語モデルは通常、TTFAに対する最大の単一要素です。そのため、段階を重ねて実行することで最も大きな効果が得られます。ここでのポイントは、エージェントは返答全体が揃う前に話し始められるということです。

最も遅延を短縮できるパターンは、LLMからトークンをストリーミングし、文や節の区切りごとにTTSへ渡すことです。トークンを文単位でバッファし、その文を合成している間に次の文を生成します:

const SENTENCE_END = /(?<=[.!?])\s+/;

async function* speakLlmStream(tokens: AsyncIterable<string>) {
  let buffer = "";
  for await (const token of tokens) {
    buffer += token;
    const parts = buffer.split(SENTENCE_END);
    buffer = parts.pop() ?? ""; // keep the incomplete fragment
    for (const sentence of parts) {
      if (sentence.trim()) yield* synthesize(sentence.trim());
    }
  }
  if (buffer.trim()) yield* synthesize(buffer.trim());
}

async function* synthesize(text: string) {
  const stream = await elevenlabs.textToSpeech.stream("JBFqnCBsd6RMkjVDRZzb", {
    text,
    modelId: "eleven_flash_v2_5",
    outputFormat: "mp3_44100_128",
  });
  yield* stream;
}

長時間の会話では、TTS WebSocket を使うのがおすすめです。オープンな接続でテキストを逐次受信でき、毎文ごとに接続を張り直す必要がありません。モデルが実際にオーディオを生成している時間だけが同時実行数にカウントされるため、アイドル状態のWebSocketはほぼコストがかかりません。

テキスト読み上げ:ストリーミングとボイス選択

テキスト読み上げは、遅延を最も正確に把握できる段階です。主な調整ポイントは、音声のストリーミング方法とボイスの選択です。

Flash v2.5(eleven_flash_v2_5)はエージェントに最適なモデルです。短い入力で約75msの推論、32言語対応、1リクエストあたり最大40,000文字まで受け付けます。

75msは推論のみの数値です。上記のTTS TTFAは、推論に加えてネットワーク往復やサーバースケジューリングも含むため、より大きくなります。

ここで最も大きな調整ポイントはストリーミングです。全音声をリクエストして待つ場合、ユーザーはクリップ全体の合成が終わるまで何も聞こえません。ストリーミングなら、最初のチャンクが生成され次第ユーザーに届き、残りは再生中に到着します。ストリーミングはモデル自体を速くするわけではありませんが、生成中でもユーザーへの出力を始められます。

ストリーミングのハウツーガイド ではHTTPストリーミングを、リアルタイムWebSocketガイド ではLLMからトークンを受け取る際に使うWebSocketの方法を解説しています。

クライアントは一度初期化し、以降の呼び出しで再利用してください:

import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient({ apiKey: process.env.ELEVENLABS_API_KEY });

次にストリームをセットアップし、受信したものを順次転送します:

const stream = await elevenlabs.textToSpeech.stream("JBFqnCBsd6RMkjVDRZzb", {
  text: "Your call is connected. How can I help today?",
  modelId: "eleven_flash_v2_5",
  outputFormat: "mp3_44100_128",
});

for await (const chunk of stream) {
  // forward each chunk to your audio sink as it arrives
}

もう一つの調整ポイントはボイス選択で、これにも遅延コストがあります。デフォルトボイス、合成ボイス、Instant Voice Clone(IVC)はProfessional Voice Clone(PVC)よりも高速に合成できます。PVCはモデルの複雑さが増し、1回ごとの生成オーバーヘッドが大きくなるためです。厳しい遅延要件のエージェントには、Flash+IVCまたはデフォルトボイスの組み合わせが最も低遅延です。

ストリーミングチャンクサイズの選択

TTSにトークンが流れ、オーディオが返ってくる場合、次に決めるのはチャンクの大きさと、プレイヤーが再生を始めるまでどれだけバッファするかです。

小さいチャンクは早くプレイヤーに届き、最初のバイトまでの遅延が減りますが、メッセージ数やチャンクごとのオーバーヘッドがやや増えます。大きいチャンクは転送効率が良いですが、最初のチャンクを待つ時間が長くなります。インタラクティブなエージェントでは、発話の最初は小さいチャンクを優先しましょう。ユーザーが待っているのは最初のチャンクだからです。後続のチャンクは再生中に届くため、サイズの影響は小さくなります。

プレイヤーも残りの遅延に大きく関わります。多くのオーディオプレイヤーは最初のバイトで再生を始めず、ストリームが一時的に遅くなっても途切れないように少しバッファします。500msのデフォルトバッファが一般的で、これはそのまま体感遅延に加算されます。バッファを減らすと、わずかに途切れリスクが増えますが、TTFAは短縮されます。適切な値はサーバーとクライアント間のネットワークジッターによります:

  • 安定した接続(サーバー側再生や同一拠点クライアント)なら、50〜150msのバッファで十分安全かつTTFAを大きく短縮できます。
  • ジッターの大きいモバイルやリージョンをまたぐ接続では、バッファを大きくして音切れを防ぐ方が、遅延よりも重要です。

ここでの最適な設定は、用途や優先事項によって異なります。

コーデックの選択

音声の送信先によって、リクエストするコーデックを決めましょう。mp3_44100_128、mp3_22050_32、pcm_16000、pcm_24000、ulaw_8000などの形式を返します。転送先のネイティブ形式に合わせることで、トランスコード処理を省略でき、ボイスエージェントの遅延最適化に役立ちます。

Twilioなどの電話用途ではulaw_8000を使いましょう。電話ネットワークは8kHzのmu-lawで統一されているため、直接リクエストすればパイプライン内のトランスコードを省略でき、キャリア側の期待通りになります。高音質で合成しても電話網で即座にダウンサンプリングされるため、遅延が増えるだけで音質上のメリットはありません。

WebRTCやブラウザ再生では、PCM(pcm_24000またはpcm_16000)やMP3形式を使いましょう。PCMは非圧縮なのでクライアント側でデコード不要、チャンクごとの遅延がわずかに減り、Web Audioパイプラインに直接流す場合に便利です。MP3は転送時にコンパクトで、回線が細い場合に有利ですが、クライアント側で軽いデコード処理が必要です。

地理とネットワーク距離

ここまでの最適化は、バイトが短距離を移動する前提です。地理的な距離が遅延予算の下限を決めるため、他の調整前に確認する価値があります。

ElevenLabsは北米、ヨーロッパ、東南アジアのクラスタからリクエストを処理し、最寄りのクラスタへ自動ルーティングします。パブリックインターネット経由のネットワーク往復は、地理的な近さによって通常20〜200msです。インフラの設置場所を変えない限り、これ以上短縮できません。

サンフランシスコのように北米クラスタに近いユーザーには即時応答に感じられても、南アジアのユーザーは通信が海を2往復するため、遅く感じることがあります。

対策としては、アプリケーションサーバーをユーザーの近くに配置することです。ユーザーがヨーロッパにいる場合は、エージェントのバックエンドもヨーロッパで稼働させ、ユーザーからサーバーまでの距離を短くしましょう。その後のサーバーからモデルへの通信は、ElevenLabs側で最寄りクラスタにルーティングされます。

自分でボイスエージェントの遅延を計測する

上記の遅延予算表の数値は、計画用の参考値です。実際に運用する際は、自分の環境で下記のようなスクリプトを使って計測しましょう。

以下の計測例は、TTS段階のTTFA(リクエストから最初のオーディオチャンクまでの時間)を多数回計測し、パーセンタイルをレポートします。サーバーと同じリージョンから実行し、開発マシンからは計測しないでください。先ほどのelevenlabsクライアントを利用します:

const VOICE_ID = "JBFqnCBsd6RMkjVDRZzb";
const TEXT = "Thanks for waiting. I have pulled up your account and I can help with that now.";
const TRIALS = 50;

async function measureTtfa(): Promise<number | null> {
  const start = performance.now();
  const stream = await elevenlabs.textToSpeech.stream(VOICE_ID, {
    text: TEXT,
    modelId: "eleven_flash_v2_5",
    outputFormat: "mp3_44100_128",
  });
  for await (const _chunk of stream) {
    return performance.now() - start; // first chunk -> stop the clock
  }
  return null;
}

function percentile(values: number[], p: number): number {
  const v = [...values].sort((a, b) => a - b);
  const k = (v.length - 1) * (p / 100);
  const lo = Math.floor(k);
  const hi = Math.min(lo + 1, v.length - 1);
  return v[lo] + (v[hi] - v[lo]) * (k - lo);
}

const samples: number[] = [];
for (let i = 0; i < TRIALS; i++) {
  const ttfa = await measureTtfa();
  if (ttfa !== null) samples.push(ttfa);
  await new Promise((r) => setTimeout(r, 300)); // space requests, don't measure your own queueing
}

console.log(`trials: ${samples.length}`);
console.log(`P50:    ${percentile(samples, 50).toFixed(0)} ms`);
console.log(`P95:    ${percentile(samples, 95).toFixed(0)} ms`);

覚えておきたいポイント:

  • P50とP95をレポート:平均値よりもこれらに注目しましょう。平均は「尻尾」を隠してしまい、尻尾こそがエージェントの信頼性を左右します。P95は20回に1回の体験です。
  • 地域ごとの実験: 各リージョンから同じスクリプトを実行し、結果を分けて記録しましょう。
  • 精度向上のための間隔調整:リクエストの間隔を空けてください(上記のsetTimeout)。一度に大量に送ると、自分自身のキューイングを計測してしまいます。同時実行数を超えると、リクエストは優先度順にキューイングされ、通常50ms程度の遅延が加算されます。さらに超過するとHTTP 429が返されます。
  • 遅延チェーン全体を計測:同じタイミングパターンを他の段階にも適用しましょう。STTの最終化、LLMの最初のトークン、プレイヤーの起動もperformance.now()で計測し、独自の予算表を作成できます。どの段階から着手すべきかが見えてきます。

これらのポイントを押さえれば、自分でボイスエージェントの遅延を計測できます。そこから優先順位が明確になり、最適化の道筋が立てられます。

ボイスエージェントの遅延を最も減らす方法は?

すぐに取り組みたい場合は、以下が最も効果的なアクションです。

おおよその効果順に、以下の方法でエージェントの遅延を短縮できます:

  • エンドポイント遅延を隠すため、安定したSTT部分書き起こしでLLM処理を開始する
  • 文の区切りごとにLLMトークンをTTSへストリーミングし、1文目の合成と2文目の生成を重ねる
  • TTSオーディオをプレイヤーへストリーミングし、ネットワークジッターに合わせてプレイヤーバッファを最小化する
  • 最低遅延のTTSにはFlash+デフォルトボイスまたはIVCを使い、コーデックは転送先に合わせる(電話ならulaw_8000、ブラウザ/WebRTCならPCMまたはMP3)
  • サーバーをユーザーの近くに配置し、地域ごとに計測する。ネットワークの距離は現実的かつ均等ではありません。

より詳細なテクニックは、遅延最適化ハウツーデベロッパーガイド をご覧ください。APIクイックスタートやストリーミングハウツーには、すぐ使えるサンプルも掲載しています。

微調整済みエージェントカスケードをすぐ使いたい方へElevenAgents では、重なり最適化済みのパイプラインをすでに実装しています。

ElevenAgentsで低遅延ボイスエージェントを構築

ボイスエージェントの遅延最適化には、各段階を計測し、遅い段階を他の作業の裏で動かす重なりが必要です。上記のパターンを使って何度か手作業で調整することもできますし、すでに最適化済みのパイプラインから始めることもできます。

ElevenAgentsは、ストリーミングSTTからトークン単位のLLM連携、Flash TTSまで、重なりテクニックを組み込んだカスケード全体を実装しています。ゼロから構築するのではなく、自分にとって重要なパフォーマンス指標の閾値を調整するだけで始められます。

ElevenAgentsを使って、エージェントを作成 してみるか、営業にお問い合わせ ください。

ボイスエージェントの遅延最適化に関するよくある質問

関連記事

最高品質のAIオーディオで創造する