Eleven v4を発表これまでで最も感情豊かなモデル、Eleven v4をご紹介します。

コンテンツへ移動

200ms未満のリアルタイムスピーチtoテキスト:アーキテクチャガイド

公開日
最終更新日

聴くこの記事を聴く

リアルタイムのスピーチtoテキスト(STT)は、話している音声をその場で文字起こしし、数百ミリ秒以内にテキストとして返します。しかし、STTのレイテンシーを低く保つことは、モデルだけでなくアーキテクチャの課題でもあります。エンジニアは、転送、チャンク化、エンドポインティング、キャプチャ経路を計画する必要があり、それぞれがレイテンシーに影響します。どれか1つでも非効率であれば、200msの予算を超える可能性があります。

このガイドでは、転送レイヤーからリアルタイムのスピーチtoテキストパイプラインを構築するための実践的な仕組みを紹介します。中心となるのはScribe v2 Realtimeです。モデルレイテンシー約150msで部分文字起こしを生成し、90以上の言語をサポート。PCM(8kHz〜48kHz)およびmu-law音声を受け付け、音声アクティビティ検出と、セグメントを確定するための手動コミット制御を提供します。

音声がサーバーに届くまでの流れ、仮説が確定テキストへ変化する仕組み、ストリーム内機能のコスト、音声を正しくキャプチャして転送する方法を見ていきます。

概要

  • リアルタイムのスピーチtoテキストシステムを構築するには、パイプライン全体で低レイテンシーを維持するためのアーキテクチャ調整が必要です。
  • WebSocketは大半のパイプラインに適したデフォルトですが、WebRTCにはより複雑である代わりに多くの利点があります。
  • 音声アクティビティ検出はハンズフリーのセグメンテーションを処理し、手動コミットでは、発話ターンが終わったとアプリケーションが判断した際に上書きできます。
  • 部分結果は暫定的なもので、最終結果は確定済みです。そのため、異なる表示にする必要があります。
  • 約100msの小さなPCMチャンクにより、最初の部分結果までのレイテンシーを最小化できます。

リアルタイムのスピーチtoテキストにおけるWebSocketとWebRTCの比較

文字起こしを始める前に、音声はソースから認識エンジンへ届く必要があります。選択するチャネルが、下流全体のレイテンシーの下限を決めます。音声を文字起こしレイヤーへ送るには、実用的な選択肢が2つあります。

WebSocketは、TCP上で動作する、長時間接続・順序保証・信頼性・双方向のチャネルです。接続を開き、バイナリ音声フレームを送信し、文字起こしイベントを受信します。クライアント側とサーバー側の両方で扱いやすく、すでにHTTPSを許可している企業プロキシやファイアウォールを通過でき、あらゆるブラウザとサーバーランタイムでサポートされています。

WebSocketの制約は、TCP上で動作することです。パケットが失われると、TCPは再送し、欠落部分が埋まるまで後続データを保留します。ネットワーク状態が良ければ問題になりませんが、パケット損失が発生すると、ヘッドオブラインブロッキングが起きます。これは、音声が一時的に滞留し、その後まとめて届く現象です。

WebRTCはリアルタイムメディア向けに設計されています。UDP(SRTP経由)でメディアを送るため、パケットが失われてもストリームは停止せず、パイプラインは継続します。パケット到着時刻のばらつきを吸収するジッターバッファー、ルーター配下のピアも接続できるようにするICE/STUN/TURNによるNATトラバーサルのネゴシエーション、独自の音声キャプチャおよびエンコード機能を備えています。

直接接続できないクライアントには通常TURNサーバーが必要で、サーバー側ではバイトストリームを読むのではなく、メディアストリームを終端する必要があります。

主なトレードオフは次のとおりです。

WebSocket
Transport
TCP (reliable, ordered)
Behavior under packet loss
Head-of-line blocking, bursty recovery
Jitter handling
Your responsibility
NAT traversal
Not needed (client-initiated)
Browser support
Universal, trivial
Server complexity
Low
WebRTC
Transport
UDP/SRTP (real-time, loss-tolerant)
Behavior under packet loss
Graceful degradation
Jitter handling
Built-in jitter buffer
NAT traversal
Requires ICE/STUN/TURN
Browser support
Universal, but more API surface
Server complexity
High (media server or SFU)

ほとんどのユースケースでは、WebSocketが適しています。クライアントの接続品質が十分で、キャプチャ経路を制御できる場合に使用します。たとえば、サーバー間パイプライン、デスクトップアプリ、ブロードバンド環境のブラウザアプリ、別経路で音声がすでにサーバーへ届いている大半のコンタクトセンターバックエンドなどです。

不安定なモバイルネットワーク上のコンシューマーデバイスから直接キャプチャする場合、双方向音声用のWebRTCスタックをすでに運用している場合(たとえば応答音声も返すボイスエージェント)、または実装の単純さよりも損失に強いリアルタイム動作を重視する場合は、WebRTCを選択してください。

このガイドでは、認識エンジンへの接続にWebSocket転送を使用します。構成要素を把握しやすく、大半のチームにとって適切な出発点だからです。ここで扱う内容はWebSocket固有ではないため、後から前段にWebRTCメディア経路を追加し、サーバーで音声をPCMにデコードして、同じチャンクをパイプラインに転送できます。

部分文字起こしと最終文字起こし:中間結果の解説

リアルタイム認識エンジンは、完全な文が終わるまで待ちません。代わりに、音声が増えるにつれて精度が上がる推測を継続的に出力し、その後確定します。この2つの状態の違いを理解することが、生きた文字起こしと壊れたように感じる文字起こしを分けるポイントです。

部分(中間)仮説は、それまでに受信した音声に基づくモデルの最良の推測です。部分結果は設計上不安定です。音声がさらに届くと、モデルは以前の単語を修正します。たとえば、「I want to」が、後続の文脈によって曖昧さが解消されると「I want two tickets」になることがあります。部分結果は高速に届き(約150msのレイテンシーとはこれを指します)、上書きされることを前提としています。

最終仮説は、以後変更されない確定済みセグメントです。セグメントが確定すると、認識エンジンは次へ進み、以降の仮説は後続の音声を表します。最終結果は、永続化したり、LLMへ送ったり、文字起こしとして保存したりする対象です。

部分結果と最終結果の区別は、曖昧にすると間違える次の3点に影響します。

  • ユーザー体験: 部分結果を表示すると、文字起こしがライブで動いているように感じられます。話すそばから単語が表示されるため、マイクが機能し、システムが聞いていることを確認できます。
  • エンドポインティング: 部分結果は、音声アクティビティの継続的なシグナルを提供します。VADと組み合わせることで、話者が実際に話し終えたタイミングを判断できます。
  • 下流処理のタイミング: ボイスエージェントのパイプラインでは、音声入力、スピーチtoテキスト、LLM、テキスト読み上げ、音声出力の順に処理されます。部分結果に基づいて推測的な処理を開始し、最終結果で確定すれば、推測処理をときどき破棄することと引き換えに、体感応答時間を短縮できます。

部分結果と最終結果は異なる表示にします。シンプルで効果的なパターンは、最新の部分結果に紐づく変更可能な「現在の行」を1つ保持し、最終結果が届いたときに追記専用の文字起こしへ確定する方法です。

type TranscriptState = {
  committed: string[]; // finalized segments, never rewritten
  current: string;     // latest partial, overwritten on each update
};

const onPartial = (s: TranscriptState, text: string): TranscriptState =>
  ({ ...s, current: text });

const onFinal = (s: TranscriptState, text: string): TranscriptState =>
  ({ committed: [...s.committed, text], current: "" });

視覚的には、確定済みのテキストは通常の表示にし、現在のテキストは薄い色またはイタリックにして、変更される可能性があることをユーザーに伝えます。

エンドポインティングと音声アクティビティ検出(VAD)

何を話したかを知るだけでは不十分です。認識エンジンは、発話がいつ終わったかも把握する必要があります。この判断によって、セグメントを確定するタイミングや、エージェントが応答を開始するタイミングが決まります。

エンドポインティングとは、発話が終了したと判断することです。早すぎる確定は文の途中でユーザーを遮断し、遅すぎる確定は、ユーザーが明らかに話し終えた後もエージェントを無言のままにします。

Scribe v2 Realtimeには、相互補完的な2つの仕組みがあります。

  • 音声アクティビティ検出による無音ベースの音声セグメンテーション: 認識エンジンは、音声が継続的な無音に変わるタイミングを検出し、その境界を使ってセグメントを自動的に確定します。VADは、手作業でタイミングを追跡しなくても自然な発話リズムに適応するため、会話型インターフェースに適したデフォルトです。
  • 手動コミット制御:手動コミット制御では、無音とは関係なく、アプリケーションが現在のセグメントを確定するタイミングを決められます。コミットシグナルを送信すると、認識エンジンは現在のセグメントを閉じ、最終結果を出力します。プッシュツートークボタンを離したとき、「送信」操作時、外部のターンテイキングポリシーなど、アプリケーションが発話ターンの終了をすでに把握している場合に適した機能です。

この2つはうまく組み合わせられます。一般的なボイスエージェントでは、ハンズフリー操作にVADを使用し、上書き用として手動コミットを提供します。これにより、考えるために間を置いたユーザーを途中で遮らず、ボタンをタップしたユーザーには即座に境界を設定できます。

無音しきい値には、普遍的に正しい値がない、実際のトレードオフがあります。

  • 発話終了後のタイムアウトを短くする(たとえば約200〜400msの無音後に確定する)と、システムの応答性が高く感じられます。一方で、節の間に自然な間を置くユーザーを途中で遮り、1つの発話を複数セグメントに分割し、エージェントでは早すぎる応答を引き起こすことがあります。
  • タイムアウトを長くする(たとえば約800〜1200ms)と、自然な間を許容して発話を維持できますが、システムが反応するまでに目立つ遅延が生じます。

ここで使えるグローバル定数はありません。しきい値はインタラクションに合わせて調整してください。

  • ディクテーションやメモ取りでは、ユーザーが文の途中で考えるため、より長い間を許容できます。長めのタイムアウトを優先し、VADを活用してください。
  • コマンド&コントロールやトランザクション型のエージェントでは、発話ターンが短く明確なため、短いタイムアウトと手動コミットの組み合わせが有効です。
  • 多言語話者や非ネイティブ話者は間を置くことが多いため、確定前の無音時間を長めに確保してください。

これらのヒントを活用すると、効果的なエンドポインティングシステムを構築し、リアルタイムのスピーチtoテキストに近づけます。

ストリーム内機能:言語検出と話者ダイアライゼーション

ストリーミング認識は、単語を出力する以上のことができます。ただし、要求する追加シグナルはすべて、レイテンシーと安定性に影響します。原則として、ライブ体験に必要な機能だけを有効にし、それ以外はバッチ処理に回してください。

自動言語認識では、事前に言語を指定しなくても、Scribe v2 Realtimeが90以上の対応言語から話されている言語を識別できます。ただし、モデルが確信を持って判断するには短い音声区間が必要なため、言語が定まるまではストリームの最初の部分結果が不安定になることがあります。言語がすでにわかっている場合は、指定することで曖昧さがなくなり、初期の部分結果がより安定する傾向があります。

話者ダイアライゼーションは、誰が何を話したかを識別し、発話を個別の話者に割り当てます。バッチ文字起こしでは、モデルがファイル全体を確認できるため、比較的容易です。ストリーミングではより困難になります。認識エンジンは、それまでの音声だけから話者ラベルを割り当てる必要があり、初期の音声に付与したラベルは、その話者の音声をさらに聞いた後に修正が必要になる可能性があります。ストリーミングの話者ラベルも、部分テキストと同様に扱ってください。セグメントが確定するまでは暫定的なものです。

単語単位のタイミングとエンティティコンテキストも同じ考え方です。トークンごとのメタデータを多く要求するほど、モデルと通信の両方で扱う量が増えます。ほとんどのリアルタイムUIでは、ライブで必要なのはテキストとセグメント境界だけです。詳細なメタデータは、Scribe v2による通話後のバッチ処理に回せます。

ストリーミングの音声形式:PCMとmu-law

転送と認識ロジックに注目が集まりがちですが、現実のバグの意外に多くは、さらに下のレイヤーである音声のエンコード方法とチャンク化方法に起因します。形式とチャンクサイズを正しく設定することは、もっとも低コストで得られるスピーチtoテキストのレイテンシー改善策です。

PCM(リニア、16ビット符号付き、リトルエンディアン)は、キャプチャを制御できる場合に使う形式です。サンプルレートが高いほど、より多くの音響的な詳細を保持できます。16kHzは音声認識の標準的な下限であり、通常は十分です。8kHzは電話品質で、高周波成分が失われます。ソースに合ったレートを使用してください。情報自体が存在しないため、8kHzの電話音声を48kHzへアップサンプリングしても利点はありません。

8kHzのmu-lawは電話用形式です。Twilioなどのプロバイダーから通話を取り込む場合、音声は8kHz mu-lawで届くため、二重にトランスコードするのではなく、その形式のまま転送してください。ソース形式に合わせることで、リサンプリングアーティファクトと不要な変換ステップを回避できます。

チャンクサイズは、体感レイテンシーにもっとも直接的に影響する要素です。音声はチャンク単位で送信し、認識エンジンはチャンクが届くたびに部分結果を生成します。小さいチャンクでは更新頻度が上がり、最初の部分結果までのレイテンシーが低下します。大きいチャンクではメッセージ数が減り、推論ごとのコンテキストがわずかに増えます。実用的な範囲は、1チャンクあたり20〜250msの音声です。具体例として、16kHzモノラル16ビットPCMでは、1秒の音声は32,000バイトなので、100msのチャンクは約3,200バイトです。

ブラウザでのマイク入力のキャプチャ

ブラウザでは、適切なツールはWeb Audio APIとAudioWorkletです。ワークレットは音声レンダリングスレッドで動作し、小さなフレームで音声を受け取るため、従来のScriptProcessorNodeのようにメインスレッドのジャンクの影響を受けません。ブラウザネイティブの浮動小数点サンプルを16ビットPCMに変換し、WebSocket経由で転送するメインスレッドへ渡す役割を担います。

ワークレットプロセッサーの中核は、浮動小数点からPCMへの変換です。

// pcm-worklet.ts - registered via audioContext.audioWorklet.addModule()
class PCMWorklet extends AudioWorkletProcessor {
  process(inputs: Float32Array[][]) {
    const channel = inputs[0]?.[0]; // mono; Float32, range [-1, 1]
    if (!channel) return true;
    const pcm = new Int16Array(channel.length);
    for (let i = 0; i < channel.length; i++) {
      const s = Math.max(-1, Math.min(1, channel[i]));
      pcm[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
    }
    // Transfer the buffer to the main thread without copying.
    this.port.postMessage(pcm.buffer, [pcm.buffer]);
    return true;
  }
}
registerProcessor("pcm-worklet", PCMWorklet);

コードで見るパイプライン

パイプラインには3つの構成要素があります。マイクをキャプチャしてPCMをサーバーへストリーミングするブラウザクライアント、音声をScribe v2 Realtimeへ中継して文字起こしを返すNodeサーバー、ファイルまたは電話ブリッジからPCMをストリーミングするスクリプト可能なクライアントです。

認識エンジンをブラウザへ直接公開せず、サーバーが中継する重要な理由があります。ElevenLabs APIキーは秘密情報であり、クライアント側のコードに絶対に含めてはなりません。キーはサーバーで保持します。ブラウザから認識エンジンへ直接通信する必要がある場合は、サーバー側で短期間有効な使い捨てトークンを発行し、APIキーの代わりにクライアントへ渡してください。

ブラウザクライアント

クライアントはサーバーへのWebSocketを開き、上記のワークレットを通じてマイクをキャプチャし、生成された各PCMフレームを転送します。受信イベント(サーバーがすでに{ type, text }へ正規化)は、前述の部分結果/最終結果の状態を更新します。

// client.ts - runs in the browser. ws is an open WebSocket to your server.
const audioContext = new AudioContext({ sampleRate: 16000 });
await audioContext.audioWorklet.addModule("pcm-worklet.js");

const mediaStream = await navigator.mediaDevices.getUserMedia({
  audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true },
});

const source = audioContext.createMediaStreamSource(mediaStream);
const worklet = new AudioWorkletNode(audioContext, "pcm-worklet");

// Forward each PCM frame to the server the moment it is produced.
worklet.port.onmessage = (e: MessageEvent<ArrayBuffer>) => {
  if (ws.readyState === WebSocket.OPEN) ws.send(e.data);
};
source.connect(worklet);

// Manual commit: tell the server to finalize the current segment.
const commit = () => ws.send(JSON.stringify({ type: "commit" }));

サーバーリレー

サーバーはクライアントごとに1つの認識エンジン接続を開き、APIキーをサーバー上に保持し、バイナリPCMをそのまま転送します。また、認識イベントを、クライアントが利用する安定した{ type, text }形式に正規化します。

// server.ts - Node, using the `ws` library. ELEVENLABS_API_KEY and the
// recognizer URL come from the environment; see the Speech to Text reference
// for the exact path and query parameters.
import { WebSocketServer, WebSocket } from "ws";

new WebSocketServer({ port: 8080 }).on("connection", (client) => {
  // The API key stays on the server, never on the wire to the browser.
  const recognizer = new WebSocket(process.env.RECOGNIZER_WSS_URL!, {
    headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY! },
  });

  // Browser -> recognizer: forward binary PCM, translate control messages.
  client.on("message", (data, isBinary) => {
    if (recognizer.readyState !== WebSocket.OPEN) return;
    if (isBinary) recognizer.send(data); // raw PCM bytes
    else if (JSON.parse(data.toString()).type === "commit")
      recognizer.send(sendCommit());
  });

  // Recognizer -> browser: normalize events into a stable shape.
  recognizer.on("message", (raw) => {
    const event = parseRecognizerEvent(raw.toString());
    if (event && client.readyState === WebSocket.OPEN)
      client.send(JSON.stringify(event));
  });

  // ... open handshake, queueing pre-open audio, and teardown on close/error
});

エンドポイント固有の処理は、以下の2つのアダプター関数に限定されています。フィールド名をスピーチtoテキストのリファレンスにある正確なものへ置き換えてください。パイプラインの残りの部分は変わりません。

// The single place that knows the recognizer's wire format.
const sendCommit = (): string => JSON.stringify({ type: "commit" });

type NormalizedEvent =
  | { type: "partial"; text: string }
  | { type: "final"; text: string }
  | { type: "vad"; speaking: boolean };

function parseRecognizerEvent(raw: string): NormalizedEvent | null {
  const msg = JSON.parse(raw);
  if (msg.is_final === true || msg.type === "final")
    return { type: "final", text: msg.text ?? "" };
  if (msg.type === "vad") return { type: "vad", speaking: !!msg.speaking };
  if (typeof msg.text === "string")
    return { type: "partial", text: msg.text };
  return null;
}

スクリプト可能なバックエンドクライアント

バックエンドパイプラインと以下のベンチマークでは、同じ認識エンジン接続をブラウザなしで利用できます。任意のソースからPCMを読み取り、リアルタイムのチャンク間隔で送信し、イベントを受信します。APIキーとURLは、サーバーと同様に環境変数から取得します。

// stream-stt.ts - pace ~100ms chunks at real time, then commit the tail.
const SAMPLE_RATE = 16000, CHUNK_MS = 100;
const CHUNK_BYTES = (SAMPLE_RATE * 2 * CHUNK_MS) / 1000; // 3200 bytes
const ws = new WebSocket(process.env.RECOGNIZER_WSS_URL!, {
  headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY! },
});

// Send: walk the PCM buffer in 100ms chunks, sleeping between to mimic a
// live source. For audio that already arrives in real time, drop the sleep.
async function sendAudio(pcm: Buffer) {
  for (let off = 0; off < pcm.length; off += CHUNK_BYTES) {
    ws.send(pcm.subarray(off, off + CHUNK_BYTES));
    await new Promise((r) => setTimeout(r, CHUNK_MS));
  }
  ws.send(JSON.stringify({ type: "commit" })); // finalize the trailing segment
}

// Receive: print partials in place, append finals.
ws.on("message", (raw) => {
  const e = parseRecognizerEvent(raw.toString());
  if (e?.type === "final") console.log(`[final]   ${e.text}`);
  else if (e?.type === "partial") process.stdout.write(`[partial] ${e.text}\r`);
});

スピーチtoテキストのレイテンシーと単語誤り率のベンチマーク

レイテンシーと単語誤り率は、話者、言語、音響条件、音声の長さ、各プロバイダーの最寄りリージョンまでのネットワーク経路、各サービスの現在の負荷によって変動します。

ある都市のラップトップで測定した結果を、別の都市にある本番環境のフリートへ一般化することはできません。本番に近いインフラ上で、実際の入力に近い音声を使ってハーネスを実行し、単一の数値ではなく範囲と分布を報告してください。

重要なのは、本番に近いインフラから自分の音声で測定したレイテンシーと精度の数値だけです。ここでは、スピーチtoテキストのレイテンシーをベンチマークする方法を紹介します。

スピーチtoテキストのレイテンシーで測定すべき項目

リアルタイムのスピーチtoテキストレイテンシーをベンチマークする際に測定すべき主な指標は次のとおりです。

  • 最初の部分結果までの時間: 最初の音声チャンクを送信してから、空でない最初の部分結果を受信するまでの時間です。
  • 部分結果から最終結果までの遅延: 発話の最後の音声チャンクから最終仮説までの時間です。
  • 単語誤り率(WER): 確定した文字起こしを人間による参照テキストと比較したWERで、すべてのシステムで同じ方法により算出します。
  • 安定性の変動: 確定前に部分結果が何回書き換えられるかです。この指標は、ライブUIがどの程度変化して見えるかの代理指標です。

統制条件

信頼性の低いデータを避けるため、一貫性を保つ複数の統制条件を実験に組み込んでください。

以下は、スピーチtoテキストのレイテンシーベンチマークで使用する主な統制条件です。

  • 同一の音声: すべてのシステムに、同じファイル、同じサンプルレート、同じエンコードを入力します。
  • 同一のペーシング: すべてのシステムを、同じリアルタイムのチャンク間隔(たとえば100msチャンク)でストリーミングします。
  • 繰り返し実行し、分布を報告: 各ファイルを1日の中で何度も実行し、中央値とテール値(p50/p95)を報告します。
  • 同一の参照テキストとスコアリング: WERを計算する前に、テキストを同じ方法で正規化します(大文字・小文字、句読点、数字)。
  • リージョンとネットワークを開示: ハーネスを実行した場所と、各プロバイダーへの経路を明記します。

これらすべての要素を揃えることで、より正確な指標を得られます。

ハーネスの骨組み

測定の中核では、プロバイダーアダプターを受け取り、最初の部分結果までの時間、確定遅延、部分結果の変動を記録します。

// benchmark.ts - measurement core; one StreamFn adapter per provider.
type StreamFn = (
  audioPath: string,
  onEvent: (kind: "partial" | "final", text: string) => void,
  result: RunResult
) => Promise<void>; // adapter sets result.lastChunkSentAt on the final chunk

interface RunResult {
  firstPartialMs?: number;
  finalLagMs?: number;
  hypothesis: string;
  partialEdits: number;
  lastChunkSentAt: number;
  startedAt: number;
}

async function measure(streamFn: StreamFn, audioPath: string): Promise<RunResult> {
  const result: RunResult = {
    hypothesis: "", partialEdits: 0, lastChunkSentAt: 0,
    startedAt: performance.now(),
  };
  let prevPartial = "";

  await streamFn(audioPath, (kind, text) => {
    const now = performance.now();
    if (kind === "partial") {
      if (text && result.firstPartialMs === undefined)
        result.firstPartialMs = now - result.startedAt;
      if (text !== prevPartial) { result.partialEdits++; prevPartial = text; }
    } else { // final
      result.hypothesis = result.hypothesis ? `${result.hypothesis} ${text}` : text;
      if (result.lastChunkSentAt)
        result.finalLagMs = now - result.lastChunkSentAt;
    }
  }, result);

  return result;
}

単語誤り率は、正規化済みテキストに対する標準的なトークンレベルのレーベンシュタイン距離です。計算前に、参照テキストと仮説テキストの両方を同じ方法で小文字化し、句読点を除去してください。そうしないと、モデルではなく正規化処理を測定することになります。この測定をループで囲み、プロバイダーごとに各ファイルを約10回実行して、最初の部分結果までの時間の中央値とWERの中央値(p50/p95)を報告します。単一サンプルはネットワークのばらつきに大きく左右されるためです。

実行するには、2つのものを用意します。まず、システムごとにStreamFnアダプターを1つ作成します。上記のスクリプト可能なクライアントはすでにその1つであり、ほかのアダプターも同じ(audioPath, onEvent, result)契約に従い、最後の音声チャンクを送信するときにresult.lastChunkSentAtを設定します。次に、音声ファイルと参照テキストを読み込み、それらに対してmeasureを呼び出します。デプロイ環境を代表するマシンで、ユーザーを代表する音声を使って実行すれば、再現可能な比較が得られます。

リアルタイムのスピーチtoテキストを実現する方法のまとめ

この記事では、多くのアーキテクチャ変更を取り上げ、システムを段階的に改善してリアルタイムのスピーチtoテキストへ近づける方法を説明しました。

本番環境のリアルタイムSTTシステムは、いくつかの重要な判断に集約されます。

  • 転送: シンプルさと制御されたネットワークを重視するならWebSocketを選び、損失耐性が必要でコンシューマーデバイスからキャプチャするならWebRTCを選びます。
  • 部分結果と最終結果: 部分結果は暫定、最終結果は確定として扱い、ユーザーがライブテキストを信頼できるように異なる表示にします。
  • エンドポインティング: ハンズフリーのセグメンテーションにはVADを使用し、手動コミットを上書き手段として用意し、無音しきい値は固定値ではなくインタラクションに合わせて調整します。
  • ストリーム内機能: ライブ体験で必要な場合にのみストリーム内機能を有効にし、残りはScribe v2によるバッチ処理に回します。
  • 音声形式: 小さなPCMフレームでキャプチャし、約100msのチャンクを送信し、電話音声ではソース形式に合わせます。
  • ベンチマーク: 自身の音声と目標指標に対して、精度とレイテンシーの調整項目を実測に基づいて設定します。
  • APIセキュリティ: APIキーはサーバー上で保持するか、直接クライアント接続用に使い捨てトークンを発行してください。

方法について詳しく知りたい場合は、ボイスエージェントのレイテンシーを最適化するためのガイドもご用意しています。

Scribe v2 Realtimeでリアルタイムのスピーチtoテキストシステムを構築

Scribe v2 Realtimeは、モデルレイテンシー約150msで部分結果を生成します。ユーザーがこの数値を体感するか、それより大きな遅延を体感するかは、制御可能な周辺アーキテクチャに依存します。この記事で紹介した戦略を活用すれば、レイテンシーを削減し、顧客体験を向上させるパイプラインアーキテクチャを構築できます。

さらに詳しくは、スピーチtoテキストの機能の概要をご覧ください。全機能と言語の一覧はモデルリファレンスで確認できます。また、リアルタイムのプロダクトページもご覧ください:リアルタイムスピーチtoテキストAPIおよびリアルタイムスピーチtoテキスト。

構築の準備ができたら、無料のElevenLabsアカウントを作成して、今すぐ最初の文字起こしをストリーミングしましょう。

リアルタイムのスピーチtoテキストのレイテンシーに関するFAQ

執筆者

Tadas Petraは、ElevenLabsのプロダクトスイートを使った開発をサポートするデベロッパーリレーションズエンジニアです。また、デベロッパー向け教育プラットフォーム「Hungrimind」の創設者でもあります。以前はAgoraでリードデベロッパーアドボケイト、Zero To Masteryでインストラクターを務めていました。

関連記事

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