ブログ一覧

無言なのに AI が誰かと会話している原因は自分の残響だった

更新: Web開発者向け

OpenAI の Realtime API で作っている音声実況 AI を、スピーカーから音を出す状態で無言テストしていました。誰も喋らず、カメラの前にも何もないのに、AI が誰かと会話を始めます。

入力側の転写ログを見ると、こう出ていました。

なよぎら。
そばランチ。

言った覚えのない言葉が「ユーザーの発話」として転写されています。さらに、AI 自身がある単語を言った直後に、同じ単語が入力転写に現れていました。AI は自分の残響に返事をしていました。

実測: マイクの RMS は 0.001〜0.005 なのに拾われる

最初に疑ったのは音量です。ところがマイク入力の RMS は 0.001〜0.005 で、数値上は静寂です。ノイズゲートを入れても、この残留は止まりませんでした。

この現象は 2種類の原因が重なって起きていました。

  • 残留エコー: スピーカーから出た AI の声がマイクに戻る。ブラウザの echoCancellation: true を有効にしているのに、微小な残留がサーバー側の音声区間検出(VAD)に拾われる
  • 転写の幻聴: 無音に近い入力に対して、転写モデルが「ご視聴ありがとうございました」のような定型句を生成する。Whisper 系の転写で知られている挙動で、私の入力転写にもそれが出ていた

VAD が残留エコーを音声区間と判定し、その区間の転写が幻聴を返し、AI がそれに答え、その声がまたマイクに戻る。このループが「誰かとの会話」の正体でした。

機構: 自前再生はブラウザの AEC の基準信号になりにくい

私の構成は、音声を PCM として WebSocket で受け取り、Web Audio API で自前再生するものでした。検証を速く回すには都合がよく、Gemini Live API のときも同じ構成で動かしていました(SDK なしで Gemini Live API を WebSocket で直接叩く)。

ブラウザのエコーキャンセルは「自分が再生している音」を基準信号として、マイクからそれを引き算します。ところが Web Audio API で自前再生した音はこの基準信号として扱われにくく、スピーカーから出た AI の声は「外からの音」としてマイクに残ります。

ノイズゲートも VAD もこの残留を止められません。どちらも「声かどうか」を見ていますが、AI の声も人間の声と同じ「声」だからです。問題が「騒音」ではなく「自分の声」である限り、音量や声らしさで弾くことはできません。

どう直したか: 再生と収音を WebRTC に預ける

解は、再生と収音の両方を WebRTC に預けることでした。Realtime API は WebRTC 接続に対応しているので、音声の受信を WebSocket + 自前 PCM からWebRTC の音声トラックに切り替え、マイクも同じピア接続で送ります。

WebRTC の音声スタックは通話用に作られていて、再生している音を基準信号にしたエコーキャンセルが最初から組み込まれています。切り替えた後、無言テストで AI が独り言を始めることはなくなり、入力転写に AI 自身の台詞が混入することもなくなりました。

WebRTC に移すと再生の細かい制御を手放し、幻聴そのものは残る

  • 自前 PCM + WebSocket の構成は、イヤホンで検証している限りエコーが起きないので開発中は最速の構成で、スピーカーで動かす実機に出した時点で通話スタックが必要になった
  • WebRTC に預けると、再生タイミングや音声バッファをアプリから細かく制御できなくなる。打ち切りの方法も output_audio_buffer.clear に変わるので(response.cancel だけでは再生が止まらない理由)、自前再生を前提に書いた制御は書き直しになる
  • 転写の幻聴そのものは消えていない。残留エコーが VAD に拾われなくなったので、幻聴が起きる区間が発生しなくなっただけ

エコーには基準信号を持つエコーキャンセルを使い、無言テストはスピーカーで行う

  • 「声だけ認識する」対策(ノイズゲート、VAD の閾値)ではエコーを止められない。問題を「騒音」と「自分の声」に分けて、自分の声には基準信号を持つエコーキャンセルを使う
  • Web Audio API の自前再生はブラウザのエコーキャンセルの基準信号になりにくいので、スピーカー運用なら再生と収音を同じ通話スタック(WebRTC)で行う
  • 無言テストは必ずスピーカーで行う。イヤホンではこのループは再現しない

AI が誰かと会話しているように見えたのは、自分の声の残響が転写の幻聴を生み、幻聴に答えた AI の声がまたマイクに戻っていたからでした。音量を下げても声を選別しても止まらない残響は、再生している音を基準信号にできる経路(今回は WebRTC)で再生と収音を行うしかありませんでした。

この記事をシェア