メインコンテンツへスキップ
Realtime Speech-to-Text (Auto) API がユーザーターンを検出するため、ボイスエージェントは独自の音声区間検出器(VAD)を必要としません。 従来の VAD ソリューションは、音声のエネルギーだけで発話中かどうかを判断します。Cartesia のモデルは意味的にターンを検出します。会話のコンテキストや、ユーザーの文が言語的に完結しているかどうかも考慮します。ユーザーが迷っているのか、文の途中で考えているのか、本当に話し終えたのかを判別できます。

ターンのライフサイクル

ユーザーターンの合間、セッションはアイドル状態です。ユーザーが話し始めると turn.start が発火し、トランスクリプトが構築されるに従って turn.update イベントが続きます。 API は会話の状態を表すために以下のイベントを発行します。 ライフサイクルにはいくつかの保証があります:
  • すべてのターンの最初のイベントは turn.start です。
  • turn.eager_end の後には必ず turn.end または turn.resume が続きます。
  • turn.resume は直前の turn.eager_end のあとにのみ発火します。
  • turn.end は常にターンを閉じ、次のターンは新しい turn.start で始まります。

トランスクリプトの動作

transcript プロパティはターン内で累積的です。デルタではなく、このユーザーターンでこれまでに文字起こしされた完全なテキストが含まれます。イベント間で部分結果を連結する必要はありません。 発行されるすべてのテキストは確定 (final) です。モデルは既に送信したテキストを修正しません。turn.update の部分トランスクリプトは、変更を心配することなく到着した瞬間に利用できます。

例: 1 ターン

ユーザーが「Hi I need to cancel my subscription please.」と発話します。
最初の turn.eager_end は「cancel」のあとで早めに発火しますが、ユーザーが話し続けるため turn.resume が続きます。2 回目の turn.eager_end は正しく、turn.end がそれを確定します。

ターン検出の設定

ターン検出は単一のシグナルによって駆動されます。各時点で、モデルはユーザーがアクティブなターンにある尤度を推定します。これは 0 から 1 の間の値です。ステートマシンはこの尤度を 3 つのしきい値と比較して、turn.startturn.eager_endturn.end を発火するタイミングを決定します。 ターン検出には、ほとんどのボイスエージェントで十分に機能するデフォルト値が用意されています。レイテンシーと、各ターンの開始と終了をモデルがどれだけ正確に検出するかのバランスを取りながら、ユースケースに合わせてエージェントの会話フローを最適化するよう調整してください。 各パラメーターはレイテンシーと精度のトレードオフです。以下の表は、どちらの方向に動かすと何が起こるかを示しています。 3 つのしきい値は厳密に順序付けられています: start_threshold > eager_end_threshold > end_threshold。上記の範囲に加えて、各値はこの順序を保つために隣接する値によって制約されます。そのため、eager end のしきい値を start のしきい値より高く設定したり、end のしきい値を eager end のしきい値より高く設定したりすることはできません。 これらはクエリパラメーターturn_start_thresholdturn_eager_end_thresholdturn_end_thresholdturn_end_timeout_ms)で接続ごとに設定するか、config コマンドを送信してセッション中に変更できます。

一般的な構成

以下は、有用な出発点となる構成の例です。ユースケースに最も近いものを選び、そこから個々のしきい値を調整してください。
  • バランス型は出発点として適しており、多くのボイスエージェントの会話で十分に機能します。
  • 応答重視型は、速いテンポの会話のやり取りなど、レイテンシーが最優先の場合に最適です。
  • 待機重視型は、ユーザーが考えたり情報を調べたりするためにポーズする場合や、ターンの途中で遮ることが大きな損失になる場合など、精度が最優先の場合に最適です。

サンプルコード

turn.startturn.end を処理すれば、動作するエージェントが得られます。ユーザーが話し始めたら割り込み、話し終えたら応答を生成します。レイテンシーを削減するには、turn.eager_end も処理します。発火した瞬間に応答生成を開始し、turn.resume が到着したらその処理をキャンセルし、turn.end がユーザーの完了を確定した瞬間に再生します。

エッジケース

音声なし vs 無音

Cartesia の API は連続した音声ストリームを想定しています。 音声の送信を停止すると、サーバーはユーザーが無音であると見なすのではなく、追加の音声チャンクが到着するのを待ちます。 これは通常、ネットワークの遅延に対応するために望ましい挙動ですが、クライアントが音声入力をミュートしている間、無音 (すべてゼロ) を送信し続ける必要があることも意味します。

イベントのドレイン

セッションの音声をすべて送信し終えたら、{"type": "close"} を送信して、バッファリングされている音声をフラッシュし、残りのイベントを発行するようモデルに伝えます。モデルが完了すると、サーバーがソケットをクローズします。 サーバーは文字起こしの精度を高めるために音声を一定量バッファします。close コマンドを送信しなかったり、メッセージの読み取りを早期に停止したりすると、バッファされた音声は処理されません。最後の 1 秒の音声を気にしないのであれば、これは問題ありません。

トランスクリプトの結合

transcript フィールドは ターン内で累積的 です。各 turn.updateturn.eager_endturn.end イベントには、これまでのターンの全文がすでに含まれています。 最終的なトランスクリプトだけが必要な場合は、完了したターンごとに 1 つずつ、各 turn.end から transcript プロパティを取得してください。transcript はそのまま連結してください。strip() で空白を取り除いたり、正規化したり、独自の区切り文字を追加してはいけません。
turn.updateturn.eager_end イベントのトランスクリプトを連結するのは、テキストの重複の典型的な原因です。各更新は累積的なので、それらを連結するとトランスクリプトの一部が繰り返されます。 turn.updateturn.eager_end は、トランスクリプトのチャンクではなく、ターン状態の更新として扱ってください。 最終トランスクリプトは turn.end のみから読み取ってください。

次に進む

オンラインで試す

サインアップやコード不要でターン検出を動作確認

API を使う

Realtime STT API で構築を開始

SDK を使う

実際のコードを見てみる