音声区間検出(VAD)は、音声ストリームから発話を識別します。音声エージェントでは「誰かが話しているか」を知るために役立ちます。より難しいのは、「話し終わったか」という判断です。
電話の相手が「住所は……」と言い、番地を確認するために間を置いたとします。検出器が正しく無音を検出していても、エージェントが誤って話し始めることがあります。自信満々に話を遮ってほしい人はいません。
会話エージェントを作るなら、発話区間が必要な場所でVADを使い、ターンを交代する方針を明確に決めましょう。各要素の関係、単独の検出器が役立つ場面、エージェントに電話を任せる前に試すべきことを説明します。
音声区間検出の仕組み
VADは短い時間窓の音声を処理し、発話が含まれるかを推定します。実装によって、二値の判定、確率、判定結果からまとめた発話のタイムスタンプを返します。
単純なエネルギーしきい値では、十分に大きい音を発話として扱います。計算は軽いものの、ドアを勢いよく閉めた音も大きな音です。より高度な検出器は、音響特徴やニューラルモデルで発話と他の音を区別します。現在のVADは、音量の測定だけに限りません。
アプリケーションは通常、検出器の前後に制御を加えます。複数の発話フレームを確認してから区間を開始し、検出前の音声を短いバッファに保持し、少しの無音を待ってから区間を閉じる、といった処理です。こうした制御は聞こえる音に影響します。バッファがなければ、検出が正しくても遅すぎて、単語の冒頭が欠けることがあります。
VADは、文字起こし、特定の話者の識別、録音からの雑音除去は行いません。それらは別の処理です。テレビの声を正しく発話と認識しても、通話相手だけを聞くはずのエージェントでは、誤った動作を引き起こす場合があります。
VAD、エンドポイント検出、意味に基づくターン検出
これらは、それぞれ異なる判断を指します。
| 要素 | 判断すること | 考慮すべき制限 |
|---|---|---|
| 音声区間検出 | この音声に発話が含まれるか | 間があるだけでは、考えを言い終えたかはほとんど分かりません。 |
| 無音に基づくエンドポイント検出 | ターンを閉じるほど長く発話が止まったか | 固定の待機時間では、考えるための間と、完結した文を同じように扱います。 |
| 意味に基づくターン検出 | 音声と言語の文脈から、話し終えたと考えられるか | 推定が早すぎたり遅すぎたりするため、アプリケーションに回復処理が必要です。 |
| 割り込み処理 | 発話が始まったら、現在の応答を止めるべきか | 再生バッファや生成中の処理も停止・破棄する必要があります。 |
無音ベースのエンドポイント検出は、出発点として適していることが多い方式です。短い待機時間なら早く応答できますが、まだ考えている人を遮る危険があります。長く待てば相手に余裕を与えられますが、回答が完結した後も返答が遅れます。
意味に基づくターン検出は、追加の文脈でこの違いを見分けます。「メールアドレスは……」と「以上です。ありがとうございます」では、その後の間が同じ長さでも、待ち方を変えるべき場合があります。それでも推定であることに変わりはなく、相手が決して言い直さないと考えてよいわけではありません。
CartesiaのInk 2の発表では、意味に基づくエンドポイント検出を音声認識に組み込んだ理由を説明しています。目指すのは、人が考えを言い終え、適切なタイミングで返答を受け取れる会話です。音声フレームの判定精度だけを最適化しても、その体験を保証できません。
単独のVADを使う場面
次の処理を決める前に発話を見つけたい場合、単独の検出器が役立ちます。録音内の発話区間をマークする、発話中の表示を出す、独自のエージェント処理に発話開始イベントを渡す、といった用途です。ローカル検出なら、その処理だけを端末内で行うこともできます。ただし、それでクラウド接続されたエージェント全体がプライベートやオフラインになるわけではありません。
評価候補には、py-webrtcvad経由のWebRTC VADとSilero VADがあります。
| 実装 | 入力と実行環境の注意点 | アプリケーションで確認すること |
|---|---|---|
| WebRTC VAD | Pythonラッパーは16ビットのモノラルPCM、8・16・32・48kHz、10・20・30msのフレームを受け付けます。検出の厳しさを4段階で設定できます。 | 厳しい設定で小さな声を見落とさないか、音声取得処理が正しいフレームを渡すか。 |
| Silero VAD | PyTorchとONNXに対応するニューラル検出器。プロジェクトの文書では8・16kHzに対応しています。 | 対象端末でのモデル・実行コスト、チャンクの要件、実際の音声での誤検出。 |
形式の要件は実装ごとに異なります。WAVヘッダーはPCMサンプルではなく、MP3パケットをそのままWebRTC VADへ渡すこともできません。必要に応じて検出前にデコード・変換してください。ブラウザーのマイクの既定サンプルレートが使えると思い込まず、現行の実装文書を確認しましょう。
どちらも会話全体を管理するものではありません。エンドポイント検出、割り込み処理、相手が話し続けた場合の回復方法は、別途必要です。
Inkに組み込まれたターン検出を使う
ストリーミング文字起こしとターン境界を一緒に扱いたい場合、CartesiaのRealtime Speech-to-Text (Auto) APIには両方が含まれています。境界検出のための別のVADは不要です。
イベントの流れでは、終了の可能性と、確定した終了を区別できます。
| イベント | アプリケーションで行うこと |
|---|---|
turn.start | 相手が話し始めました。再生中の応答に割り込み方針を適用します。 |
turn.update | 表示・保存する、このターンの文字起こしを更新します。 |
turn.eager_end | ターンが完了した可能性があります。先読みで応答生成を始められます。 |
turn.resume | 相手が話を続けました。暫定的な終了に基づく処理をキャンセル・破棄します。 |
turn.end | 検出器がターンの完了を確定しました。完成した文字起こしで処理を進めます。 |
たとえば「キャンセルしたいです」の後に、暫定的な終了が検出されても、「最初ではなく、2件目の予約を」と続くことがあります。遅延を減らすために返答の準備を始めても、再生は終了が確定するまで待ちます。予約のキャンセルなど、実際の影響を伴う操作には、アプリケーションの検証・確認ルールを適用してください。早期の終了予測は、ユーザーの許可ではありません。
見落としやすい実装上の注意が2つあります。文字起こしを含むイベントは、ターン内の累積テキストを持ちます。現在のターンの文を置き換えてください。毎回追加すると単語が重複します。また、APIには無音を含む連続した音声が必要です。手前のVADで無音チャンクを落とさないでください。音声が来ない状態は「話し終わった証拠」ではなく、「追加入力を待っている状態」として扱われます。
ターン検出のドキュメントは、しきい値、キャンセル、接続終了時のバッファ内イベントの処理を説明しています。既定値から始め、記録した会話の失敗例で、設定を1つずつ変えて試してください。
検出器に加えて、会話全体をテストする
しきい値を選ぶ前に、どんなやり取りにしたいかを決めましょう。住所を聞く受付エージェントなら、相手が調べる時間が必要です。短い「はい・いいえ」のやり取りなら、待ち時間は短くできるでしょう。1つの無音タイムアウトだけですべての要件は表せません。
適切な許可を得た録音を使い、実際に使うマイクや電話回線を含めて試します。切り出した音声だけでなく、やり取り全体を聞いてください。
| 場面 | 確認する失敗 |
|---|---|
| 無音の後、最初の音節が小さい | 録音や文字起こしで単語の冒頭が欠ける。 |
| 住所の途中で間を置く | 相手が言い終える前に応答する。 |
| 短く完結した回答 | 明らかに終わった後も待ち続ける。 |
| エージェントの発話中に「やっぱり木曜日で」と訂正する | 訂正にかぶせて、バッファに残った音声を再生し続ける。 |
| 背景の会話、キーボード音、スピーカーの反響 | 誤った音に反応して、応答を止めたり始めたりする。 |
| 異なるアクセント、話速、ためらい | テストを書いた人には合うが、別の話者を遮る。 |
応答の遅れと併せ、早すぎる終了や割り込みの見逃しを記録します。ターンを閉じるまでの遅延と、LLM、ツール呼び出し、音声生成、再生にかかる時間を分けてください。VADのタイムアウトを短くしても、遅いカレンダー検索は直りません。
割り込み時には、出力経路全体を確認します。テキスト生成を止めても、クライアントの再生待ち音声が消えるとは限りません。「止めて」と言う人にとって大事なのは、どのサーバータスクがキャンセルを受け取ったかではなく、いつ音が止まるかです。
統合されたターン検出は、Inkのブラウザーデモで、文の途中に意図的な間を入れて試せます。その後、Realtime STTのサンプルを実際の音声条件で動かしてください。ユーザーが最後まで話せるか、返答を待っているときに応答できるかを確かめることが、役に立つテストです。