ASR → LLM → TTS を「リアルタイム」にするには? RTF と応答遅延の勘所
はじめに
音声で話しかけて音声で返ってくる —— いわゆる音声対話システムを作るとき、多くの場合 「ASR(音声認識) → LLM → TTS(音声合成)」 というパイプラインを組みます。
ここで必ずぶつかるのが、
これを "リアルタイム" にするには、各段の RTF をどこまで下げればいいのか? 全体では?
という問いです。本記事はこれを整理します。先に結論だけ:
- リアルタイム性には 2つの別々の指標 がある。①つっかえずに出し続けられるか(= RTF / スループット)と、②話し終えてから最初の音が出るまで(= 応答遅延)。この2つは別物。
-
RTF は「足し算して 1 未満か」で考えると間違い。 ストリーミングでは各段が時間的に重なるので、全体の律速は 一番遅い段 で決まる(=
max、和ではない)。 - ASR / TTS は各々 RTF < 1(実務では ≤ 0.5)。LLM はほぼ律速にならない(理由は後述)。
-
達成の可否を本当に決めるのは「エンドツーエンド応答遅延」という別予算。
< 300msで人間の会話に近い、300〜800msで快適、> 1.5sで明らかに遅い。 - 遅延を削る最大のレバーは 「最初の一文が出た瞬間に TTS へ流す」 こと。
RTF とは / 混同しがちな2つの問題
RTF(Real-Time Factor)= 処理時間 ÷ 音声長。 RTF < 1 ならリアルタイムより速い、= 1 でちょうど等速、> 1 で間に合わない。
大事なのは、「リアルタイムにしたい」という願望が実は 2つの別問題 を含んでいることです。
- つっかえずに出し続けられるか(スループット) → これが RTF の担当。
- 話し終えてから最初の音が返るまでの時間(応答遅延 / レイテンシ) → これは RTF とは別物。
「全体の RTF はいくつ必要?」と聞くとき、本当に気にしているのはたいてい②(遅延)なのに、RTF という指標が答えるのは①。ここがズレの元です。
なぜ各段の RTF は「足し算」ではないのか
もし 「録音しきる → ASR で全部書き起こす → LLM で全部生成 → TTS で全部合成 → 再生」 という逐次(バッチ)処理なら、遅延はほぼ各段の合計になります。この場合 だけ RTF の足し算に意味があります。
でも本気でリアルタイムをやるなら、そうはしません。ストリーミング にします。
- ASR は喋っている 最中に 書き起こす
- LLM は文が出たそばから生成する
- TTS は token / chunk を受け取ったそばから合成して再生する
この3段は時間的に 重なります。だから「つっかえないか」は 一番遅い段が追いつけるか で決まり、3段の和ではありません。
▼ 話し終わり (t=6)
時刻: 0 2 4 6 8 10 12 14
【逐次処理】各段が順番に走る → 遅延=合計(t=6 → 最初の音 t=11)
発話 ████████████
ASR ████
LLM ██████
TTS ██████ ← 最初の音 (t=11)
【ストリーミング処理】重なって走る → 最初の音が早い(t=8)
発話 ████████████
ASR ████████████ ← 発話と並行
LLM ██████
TTS ██████ ← 最初の音 (t=8)、LLMと並行
図のポイント: 逐次版は各段が順番に走るので 遅延 ≈ 合計(話し終わり t=6 → 最初の音 t=11)。ストリーミング版は ASR が発話と並行・TTS が LLM 生成と並行 に走るので、最初の音が t=8 まで早まります(数値は相対的な目安)。ストリーミングの ASR バーが長いのは "遅い" からではなく発話中ずっと並行処理しているためで、話し終わり後に増える遅延はごく短い尻尾だけです。だから "各段 RTF を足して 1 未満か" という直感は外れます。
各コンポーネントの RTF 要件(つっかえない条件)
| 段 | RTF の目安 | 補足 |
|---|---|---|
| ASR(ストリーミング) |
< 1(実務 0.2〜0.5) |
RTF が低いほど、発話停止後の "尻尾"(未処理バッファ)が短い。GPU なら 0.1〜0.3 も普通 |
| TTS(ストリーミング) |
< 1(実務 ≤ 0.3〜0.5) |
再生に追い越されないため。余裕 (1 − RTF) がジッター耐性になる |
| LLM | ほぼ律速にならない | 下記参照 |
LLM は(直感に反して)スループットの律速にならない
自然な発話速度は約 150 語/分 ≈ 2.5 語/秒 ≈ 3〜5 token/秒(日本語もオーダーは同程度)。つまり LLM は 持続的に 5 token/秒くらい出せれば TTS を喋らせ続けられます。
一方で最近の LLM は 30〜150+ token/秒。10〜40 倍の余裕 があります。
だから LLM がスループットのボトルネックになることはほぼなく、LLM の効きどころは 最初のトークンまでの時間(TTFT)、つまり遅延側なのです。
「全体の RTF」の答え
-
ストリーミング: 全体 RTF ≈
max(各ストリーミング段の RTF)。これが< 1(実務≤ 0.5)ならつっかえない。足し算ではない。 - 逐次 / バッチ: このときだけ遅延 ≈ 各段の合計になり、総 RTF の足し算に意味が出る。
本当に体感を決めるのは「応答遅延」
話し終えてから最初の音が返るまで —— これが体感の要です。目安:
| 応答遅延 | 体感 |
|---|---|
< 300ms |
人間同士の会話の間合い(約 200ms)に近い。非常に良い |
300〜800ms |
反応が良い |
800ms〜1.5s |
使えるが遅さを感じる |
> 1.5s |
明確にもたつく |
内訳(ストリーミング、発話停止 → 最初の音):
| 要素 | 目安 | メモ |
|---|---|---|
| VAD / エンドポイント検出 | 100〜300ms |
「本当に喋り終えたか」の判定。見落とされがちだが最大級 |
| ASR の締め | 50〜150ms |
ストリーミング & 低 RTF なら尻尾は短い |
| LLM TTFT | 100〜500ms |
モデルサイズと context 長(プレフィルコスト)次第 |
| TTS 最初の chunk | 100〜300ms |
|
| ネットワーク / バッファ | 50〜100ms |
合計 約 400ms〜1.2s。sub-500ms を狙うなら VAD・TTFT・TTS 最初の chunk の3つが主戦場です(ASR の RTF や LLM の生成速度は通常ボトルネックではない)。
RTF を下げる方法
注: RTF は小さいほど速い。ここでは「RTF を下げる = 処理を速くする」を扱います。
共通(ASR / LLM / TTS すべてに効く)
- 量子化(INT8 / FP8 / INT4): 最も効く単発策の一つ。デコードは元々メモリ帯域律速なので、重みを量子化すると直接速くなる。
- 推論エンジンを変える: 素の PyTorch を避ける。LLM は TensorRT-LLM / vLLM / SGLang、Whisper 系は faster-whisper(CTranslate2、概ね 4〜5 倍)、汎用は ONNX Runtime / TensorRT。演算子融合・FlashAttention 等が入っている。
- 小さいモデル / 蒸留 / 枝刈り: 計算量を直接削減(精度とのトレードオフ)。
- 混合精度: FP16 / BF16 が基線。新しい HW(Hopper / Blackwell)では FP8 がさらに速い。
- バッチング: サーバ集約スループット向け。単一リアルタイムストリームの遅延には注意。continuous batching(vLLM の PagedAttention)なら両立できる。
- ハードウェア: 演算性能だけでなく メモリ帯域 が効く(デコードは帯域律速)。
ASR 固有
- ストリーミング / transducer 系(RNN-T、CTC)を使う。オフラインの full-attention モデルを避ける。
- デコードは greedy か小さい beam 幅で。
TTS 固有(ここが RTF の最大レバー)
-
非自己回帰 / 並列合成: 決定的に効く。自己回帰はフレームを1つずつ生成 → RTF 高。非自己回帰(FastSpeech2 + HiFi-GAN、VITS、flow-matching 系)は全フレームを並列生成し、RTF
0.1以下も狙える。 - 軽量 vocoder(HiFi-GAN、Vocos)。
- diffusion TTS なら few-step に蒸留する。
LLM(スループット側)
- 投機的デコード(speculative decoding): draft model / Medusa / EAGLE。1 回の前向きで複数トークンを出す、デコード速度の大技。
- 量子化、MoE、小型化。
応答遅延を下げる方法
RTF を下げれば遅延も多少縮みますが(尻尾が短く、最初の chunk が速く)、以下は "最初の応答" に特化したレバーです。
VAD / エンドポイント検出(隠れた最大要素)
- セマンティック・エンドポイント検出: 固定の無音タイムアウトを待つのではなく、「喋り終えた」をモデルで予測する。体感で一番効くことが多い。
- 無音タイムアウトを短くする(ただしユーザーを遮りやすくなるトレードオフ)。
- 確定前に下流を先行起動する(投機的)。
LLM TTFT
- prefix / KV キャッシュ: 固定 system prompt の KV をキャッシュし、毎回のプレフィルを省く。長い固定 prompt で効果大。
- prompt / context を短くする(プレフィルは入力長に線形)。
- chunked prefill、モデルのウォームアップ(コールドスタート回避)、最初の応答だけ小型モデルを使う。
TTS 最初の chunk
- ストリーミング合成: 最初の chunk が出たら再生。文全体を待たない。
- 非自己回帰なら最初の chunk が元々速い。必要なら最初の chunk サイズを小さく。
アーキテクチャ / パイプライン(構造的に一番効く)
- 最初の一文が出た瞬間に TTS へ: 最大のレバー。LLM が最初の意味的まとまり(最初の読点 / 句点)を出したら即 TTS。遅延を「全文生成」から「最初の一文生成」に落とす。
- 全経路ストリーミング接続: ASR → LLM → TTS を token / chunk 単位で流し、どの段もバッファを溜め込まない。
- co-location / ネットワークホップ削減: ASR・LLM・TTS を同一マシン / リージョンに寄せて往復を削る。接続の keep-alive、モデルの事前ロードも。
- フィラー / 相槌: 先に短い「えっと」「はい」で後続遅延を隠す(体感対策。実遅延は減らないが有効。音声アシスタントの定番)。
- (攻めるなら)投機的 TTS: 高確率な接頭辞を先に合成開始する。
まとめ
- リアルタイム性は スループット(RTF) と 応答遅延 の2軸。混同しない。
-
RTF: ASR / TTS は各
< 1(目標≤ 0.5)、LLM は自然に満たす。全体はmaxであって和ではない。 - 達成の可否は結局「エンドツーエンド応答遅延」という別予算 で決まる。
- 遅延削減で回収が大きい順: ①最初の一文で TTS 起動、②prefix キャッシュで TTFT 短縮、③セマンティック・エンドポイント。つっかえる場合のみ RTF(特に TTS を非自己回帰へ)。
おまけ: この構成を実際に体験できる OSS「Sokuji」
最後に、本記事のパイプラインをそのまま実プロダクトとして低遅延で回している好例を紹介します。
Sokuji(即時) は、Kizuna AI Lab が開発するオープンソースのライブ音声翻訳アプリです。まさに本記事の「ASR →(翻訳 / LLM)→ TTS」を、リアルタイムで動かしています。
特徴(要点):
- クロスプラットフォーム: デスクトップアプリ(Windows / macOS / Linux)とブラウザ拡張(Chrome / Edge)。Google Meet・Zoom・Teams・Discord などに対応。
- ローカル推論モード: 端末上で ASR → 翻訳 → TTS を WASM + WebGPU で完結。API キー不要・完全オフライン・GPU 不要・プライバシー完全保護。
- クラウドモード: OpenAI / Google Gemini / Palabra.ai / Doubao AST 2.0 / OpenAI 互換 API など。自分の API キーで直接接続(中間サーバなし)。
- 仮想マイク: 翻訳された音声を仮想マイク経由で会議アプリに流し込むので、相手側は何もインストール不要。
- 規模感: 数十の ASR モデル(99+ 言語)、多数の翻訳ペア、100+ の TTS ボイス(数十言語)。
そして本記事的に一番おもしろいのが、最近対応した OpenAI の gpt-realtime-translate(音声 → 音声の専用翻訳モデル)。公式リリースノートでは、これを 「文字起こし + chat + TTS を連結する方式より明らかに低遅延」 と説明しています。
これはまさに本記事で見た 「チェーンした各段の遅延が積み上がる vs 専用パイプラインで短縮する」 という話の実例です。遅延を詰めたい人にとって、Sokuji のアーキテクチャや実装(ジッター吸収バッファ、エコーキャンセル、音声ルーティングなど)はそのまま良い教材になります。
※ 機能や対応モデルは活発に更新されているので、最新情報は公式リポジトリを確認してください。