🎙️

音声会話システムの一般技術仕様(Realtime API × Python)

に公開

目的:リアルタイム音声会話アプリの自然さ安定性を実現するための一般的な技術仕様
想定:PC/組込み(例:SBC)で動作する Python クライアント + Realtime API

はじめに

自然な応答生成が難しい理由:

  1. 文脈の理解

前の会話を覚えて適切に反応する必要がある
「それ」「あれ」などの指示語が何を指すか判断が難しい

  1. 多様な言い回し

同じ意味でも表現方法が無数にある
状況に応じて適切な表現を選ぶ必要がある

  1. 省略への対応

日本語は主語や目的語を省略しがち
「食べた?」→誰が・何を が不明でも理解が必要

  1. 感情・ニュアンス

同じ文でも言い方で意味が変わる
皮肉や冗談の判別が困難

  1. 話題の展開

自然に話題を広げたり切り替える判断
一問一答で終わらない会話の流れ作り

最大の問題: これらを全て同時に処理する必要があること。AIモデルの性能とプロンプト設計が鍵になります。


0. 高齢者向け?

パラメータ デフォルト 推奨 目的
threshold 0.75 0.85 物音で誤検知しない
prefix_padding_ms 300 500 頭切れ防止
silence_duration_ms 900 1500 ゆっくり話者対応
max_response_output_tokens 100 150 文を完結させる
speak_delay_seconds 1.0 1.5 聞き取りやすい間
response_cooldown 3.0 2.0 自然なテンポ

1. スコープとゴール

  • スコープ:ASR(音声認識)、VAD(無音検出)、応答生成、TTS 再生、割り込み制御を含む一連の会話ループ
  • ゴール
    • 会話の頭切れ防止中断耐性
    • 聞き取りやすい話速適切な間
    • 相槌やフィラーに過剰反応しない設計
    • 低スペック環境でも安定に動作

2. アーキテクチャ概要

[Mic] -> [Audio Input (pcm16, 24kHz, mono)]
      -> [Realtime API: VAD + Transcription + Response(text/audio)]
      -> [Client: 制御ロジック(割り込み、フィルタ、間合い)]
      -> [TTS Play (audio out)]
I/O 形式:pcm16 / モノラル / 24kHz(クライアント/サーバで統一)

モダリティ:["audio","text"](テキストも取得してデバッグ可能に)

3. Realtime API セッション設定(推奨デフォルト)

python

session_config = {
  "modalities": ["audio", "text"],
  "instructions": "<会話スタイル指示(短文・ゆっくり・共感重視)>",
  "voice": "<任意のTTSボイス>",
  "input_audio_format": "pcm16",
  "output_audio_format": "pcm16",
  "input_audio_transcription": {
    "model": "whisper-1",
    "language": "ja"
  },
  "turn_detection": {
    "type": "server_vad",
    "threshold": 0.85,
    "prefix_padding_ms": 500,
    "silence_duration_ms": 1500
  },
  "temperature": 0.7,
  "max_response_output_tokens": 150
  // interrupt_response は未設定(デフォルト動作に委譲)
}

パラメータ設計の要点
VAD:

threshold=0.85:環境ノイズに反応しにくい感度

prefix_padding_ms=500:発話の冒頭欠落を防ぐ

silence_duration_ms=1500:ゆっくり話しても切られにくい

生成:

temperature=0.7:自然かつ安定した応答

max_response_output_tokens=150:短すぎず長すぎない完結文を担保

TTS:

聴き取りやすい声質を選定(男女/ニュートラル等は用途に応じ選択)

4. クライアント制御(必須ロジック)

4.1 応答進行フラグと割り込み抑制

フラグ:response_in_progress: bool

目的:AIの発話中に入力開始イベントが来ても誤中断しない

仕様:

response_in_progress=True 中の speech_started は無視

応答完了イベント(例:response.done)で False に戻す

擬似コード:

python

if message_type == "input_audio_buffer.speech_started":
    if response_in_progress:
        log("AI応答中: 入力検知を一時抑制")
        return

4.2 自動キャンセル(ユーザー主導優先)

目的:ユーザーの新しい有意味な発話で進行中の応答を即停止

実装:response.cancel を送信し、出力をミュート/バッファ破棄

擬似コード:

python

if response_in_progress and is_meaningful(user_text):
    send({"type": "response.cancel", "response_id": current_response_id})
    response_in_progress = False

4.3 相槌/フィラー無視

目的:不要な応答生成を抑制し、会話のテンポを保つ

判定:

短すぎる発話(例:≤2 文字)はスキップ

辞書(例:「うーん」「えっと」等)に一致でスキップ

擬似コード:

python

if len(text.strip()) <= 2 or text in FILLER_WORDS:
    log("フィラー検知 → 応答スキップ")
    return

4.4 間合いの調整

発話開始遅延:speak_delay_seconds(推奨 1.5s)

応答後クールダウン:response_cooldown(推奨 2.0s)

効果:急かさず、聞き取りやすいリズムを確保

5. オーディオ入出力要件

入力:16-bit PCM、24kHz、モノラル
(例:PyAudio paInt16 / rate=24000 / channels=1)

出力:サーバ応答の pcm16 をそのまま再生

バッファ管理:

キャンセル時は再生キューを即時クリア

過剰な先読み再生は避け、キャンセルの即時性を担保

6. 推奨プリセット(一般化)

シナリオ threshold prefix_padding_ms silence_duration_ms max_response_output_tokens speak_delay_seconds response_cooldown
標準 0.85 500 1500 150 1.5s 2.0s
生活音が多い 0.90 600 1800 150 2.0s 2.0s
非常にゆっくり話す 0.85 700 2500 120 2.0s 2.0s
テンポ重視 0.80 400 1000 100 1.0s 1.5s

💡 ポイント

  • まずは「標準」設定から始めて環境に合わせて微調整
  • 一度に複数項目を変えず、1項目ずつ検証
  • threshold(感度)と silence_duration_ms(無音判定時間)はセットで調整すると効果的

7. ログ/メトリクス

必須ログ:

speech_started/speech_ended(時刻・間隔)

response_in_progress の状態遷移

response.cancel 発行回数と理由

フィラー判定の命中

推奨メトリクス:

1 セッション当たりの中断回数

頭切れ率(ユーザー主観フィードバックでも可)

応答開始までの体感待機時間

8. トラブルシューティング(一般)

応答が途中で切れる

speech_started の抑制が効いているか

threshold>=0.85 / max_tokens>=150

発話の頭が切れる

prefix_padding_ms>=500

ゆっくり発話が途中で切られる

silence_duration_ms>=1500(必要に応じ 2000–2500)

話速が速く聞こえる

会話指示を強化(短文・ゆっくり・間合い)

speak_delay_seconds=1.5–2.0 / cooldown≈2.0

9. セキュリティ / プライバシー留意点

音声データ:保存要否を明確化し、デフォルトは最小限収集

ログ:個人特定情報や生音声の保存は避け、統計値中心

設定:環境変数や設定ファイルで API キー・モデル等を外出し

10. 実装テンプレ(抜粋)

python

class ConversationController:
    def __init__(self):
        self.response_in_progress = False
        self.current_response_id = None
        self.speak_delay_seconds = 1.5
        self.response_cooldown_until = 0

    def should_ignore_input(self, text: str) -> bool:
        t = text.strip()
        return len(t) <= 2 or t in FILLER_WORDS

    async def on_user_text(self, text: str):
        # クールダウン
        if time.time() < self.response_cooldown_until:
            return
        # フィラー無視
        if self.should_ignore_input(text):
            return
        # 自動キャンセル
        if self.response_in_progress and self.current_response_id:
            await self.ws.send({"type": "response.cancel",
                                "response_id": self.current_response_id})
            self.response_in_progress = False

        # 応答要求…
        self.response_in_progress = True
        # (API呼び出し・再生開始は適宜実装)

まとめ

VAD(感度/無音/前置き)× 応答長 × 間合い × 割り込み制御をセットで設計する。

汎用プリセットから開始し、単一パラメータを少しずつ動かして再現性を保つ。

フィラー無視と自動キャンセルで、主導権は常にユーザーへ。

この仕様は、用途や利用者特性に依存しない一般的な音声会話品質のベースラインとして利用できます。

Discussion