openai-realtime-voice について
OpenAI Realtimeでライブ音声製品を構築する開発スキルです。ターン交代やツール利用を伴う継続的な音声エージェント向けに、アーキテクチャ、通信方式、セッション設定、中断処理を扱い、リアルタイム対話とファイル音声処理を区別します。
主な機能
- 01音声セッションの構成と通信方式の選択
- 02リアルタイムセッションと音声設定
- 03ターン検出と会話状態の設計
- 04中断・ツール・引き継ぎへの対応
- 05プライバシー・安全性・コストの考慮
活用シーン
- 01ブラウザー向けリアルタイム音声アシスタントの構築
- 02電話型音声サポートエージェントの実装
- 03ユーザーが中断できる音声対話の設計
- 04リアルタイム音声セッションへのツール追加
MCP 対応
リアルタイムセッションでは、アプリが実行する関数ツールや組み込みコネクターに加え、リモートMCPサーバーが公開するツールを利用できます。ツール設定と実行主体の計画を説明します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ calesthio/ generative-media-skills --skill openai-realtime-voiceインストール後、Claude Code で/openai-realtime-voiceを入力して読み込まれたか確認します。
同梱ファイルEVAL.md
詳細
OpenAI Realtime 音声
成果物がリアルタイムの音声対話である場合は、このスキルを使用してください。対象には、ブラウザー音声アシスタント、電話のようなエージェント、低遅延の音声間サポートフロー、ガイド付きインタビュー、個別指導の会話、ライブキオスク、聞き取り、推論、発話、中断、ツール呼び出しを行うリアルタイム音声 UI が含まれます。
バッチ音声テキスト変換、オフライン TTS、ナレーション生成、音声編集、吹き替え、音楽、ファイルベースの音声タスクには、このスキルを使用しないでください。これらは、別の OpenAI 音声/リクエスト API またはほかの音声スキルの対象です。判断基準はシンプルです。永続的なリアルタイムセッションとターンテイクの動作が製品の一部であれば、このスキルを使用してください。「このファイル/テキストをテキスト/音声に変換する」作業であれば、使用しないでください。
以下の OpenAI API に関する情報は、すべて 2026-07-10 に OpenAI の公式ドキュメントで確認済みです。Realtime の仕様は急速に変化するため、本番リリース前にモデル識別子、価格、セッション制限、音声、API オブジェクトのフィールド、データ保持、ポリシー要件を再確認してください。
すべての設計に反映すべき、情報源に基づく事実
文書に記載されている事実:
- OpenAI は、低レイテンシーが必要なライブ音声には Realtime セッションが最適であり、ファイル、範囲が限定されたリクエスト、ライブセッションを必要としない音声生成にはリクエストベースの音声 API が最適だと位置付けています。出典:Realtime と音声の概要。
- 音声エージェントセッションは、モデルがユーザーに応答し、ツールを呼び出し、
/v1/realtime上で会話状態を管理するアプリケーション向けです。翻訳セッションと文字起こしセッションは、別個の Realtime セッションタイプです。出典:Realtime と音声の概要。 - OpenAI の音声エージェントガイドでは、音声から音声へのライブセッションと、連鎖型の STT -> テキスト推論 -> TTS パイプラインを区別しています。割り込み、最初の音声が出力されるまでの低レイテンシー、自然なターン交代、リアルタイムのツール使用には、ライブ音声が出発点となります。出典:音声エージェント。
- ドキュメントで示されているブラウザー向けの方法は WebRTC で、
RealtimeAgentやRealtimeSessionなどの Agents SDK ヘルパーを利用することが推奨されています。サーバー間のインテグレーションでは WebSocket を使用します。出典:音声エージェント、WebRTC ガイド、WebSocket ガイド。 - Realtime の音声から音声へのセッションは、Session オブジェクト、Conversation、Responses で構成されます。Session はモデル、音声、設定を制御し、Conversation にはユーザーとモデルの Items が含まれ、Responses はモデルが生成した音声またはテキストの Items です。出典:Realtime の会話。
- サーバーが
session.createdを送信した後、クライアントはsession.updateを使用できます。ほとんどのセッションプロパティはいつでも更新できますが、セッション内でモデルが一度音声を返した後は、音声を変更できません。ドキュメントに記載されている Realtime セッションの最大継続時間は 60 分です。出典:Realtime の会話。 - WebSocket 音声では、クライアントが入力音声バッファーイベントを通じて、Base64 エンコードされた音声を手動で送信します。WebRTC では、メディア転送の多くが処理されます。出典:Realtime の会話。
- 音声から音声への Realtime セッションでは、VAD がデフォルトで有効になっており、
session.audio.input.turn_detectionで設定できます。ドキュメントに記載されている VAD モードはserver_vadとsemantic_vadです。server VAD では、しきい値、プレフィックスのパディング、無音時間を調整できます。出典:音声アクティビティ検出。 - Realtime セッションでは、アプリケーションが実行する関数ツール、リモート MCP サーバーが公開する MCP ツール、組み込みコネクターを使用できます。ツールはセッション単位、または個々のレスポンス単位で追加できます。出典:ツールを使用した Realtime。
- Realtime 音声エージェントセッションでは、テキスト、音声、画像のモダリティをまたいで入出力トークンが発生します。翻訳と文字起こしのストリーミングセッションは、継続時間に応じて課金されます。後続の会話ターンでは、サーバー側の会話全体が各 Response の入力となるため、コストが高くなります。OpenAI は、音声トークンの計算方法として、ユーザーメッセージの音声は 100 ミリ秒あたり 1 トークン、アシスタントメッセージの音声は 50 ミリ秒あたり 1 トークンとしています。出典:コストの管理。
- 入力音声の文字起こしは、Realtime モデルが本来備えている音声理解とは別の ASR プロセスであり、文字起こしがモデルの解釈と異なる場合があると OpenAI は警告しています。文字起こしは可観測性やアクセシビリティのための成果物として扱い、推論への入力内容そのものとして扱わないでください。出典:Realtime API リファレンス。
- OpenAI は、組織が明示的にオプトインしない限り、API に送信されたデータをモデルのトレーニングや改善に使用しないとしています。出典:OpenAI プラットフォームのデータ管理。
- Realtime 音声エクスペリエンスにも、OpenAI の利用ポリシーと安全性に関するベストプラクティスが引き続き適用されます。これには、有害コンテンツ、なりすまし、プライバシー、重大な影響を及ぼす意思決定、ユーザー保護に関する制約が含まれます。出典:利用ポリシー、安全性に関するベストプラクティス。 このスキルにおける本番環境向けのヒューリスティックは、OpenAI が保証するものではありません。文書化された API の範囲をもとに音声製品を構築するエージェント向けの、実践的な設計指針です。
アーキテクチャの選択
プロンプトやコードを書く前に、アーキテクチャを選択してください。
次のような場合は、音声から音声への Realtime 音声エージェントセッションを使用します。
- 低レイテンシーと自然な会話の流れが製品の要となる場合
- ユーザーがアシスタントの応答を中断できる必要がある場合
- ライブ音声でやり取りしながら、アシスタントがツールを呼び出す必要がある場合
- 音声の振る舞い、タイミング、ターンテイキングが品質の一部である場合
- セッションで会話状態を安全に保持できる場合
次のような場合は、代わりに STT -> テキストエージェント -> TTS のパイプラインを使用します。
- すべてのツール操作をアプリケーションが明示的に順序付ける必要がある場合
- 自然な音声間のタイミングよりも、決定論的な文字起こしが重要な場合
- 既存の製品にすでにテキストエージェントがあり、音声が入出力レイヤーである場合
- 音声合成の前にテキストを保存、確認、または承認することがコンプライアンスで求められる場合
- 実際の用途がバッチ処理、ファイル、長時間の録音、ナレーション、またはボイスオーバーである場合
ストリーミングの文字起こし差分が必要で、モデルが生成する音声応答が不要な場合に限り、Realtime の文字起こしモードを使用してください。製品が通訳者向けの場合に限り、Realtime 翻訳を使用してください。これらを音声エージェントのアーキテクチャに無理に組み込まないでください。
セッションとトランスポートのパターン
すべての実装を、クライアントメディア、信頼できるサーバー、OpenAI Realtime セッションという3つの境界を中心に設計してください。
ブラウザーまたはモバイルの場合:
- ユーザーから明示的な同意を得てから、ローカルでマイク音声をキャプチャします。
- Realtime セッションの開始または承認を、信頼できるサーバーに依頼します。
- 可能な限り、メディアには WebRTC を、イベントにはデータチャネルを使用します。
- 標準の OpenAI API キーはサーバーに保持します。一時キーのフローを使用する場合は、対象セッション専用の短期間有効な認証情報のみを発行します。
- アシスタントの音声を、発話中/待機中の状態が見える形で再生し、分かりやすい停止/ミュート操作を用意します。
サーバー間の場合:
- 音声のキャプチャと再生は、管理下のインフラストラクチャ、テレフォニーブリッジ、またはバックエンドプロセス内で行います。
- 標準の API キーを使い、安全なサーバーからのみ
wss://api.openai.com/v1/realtime?model=...に接続します。 - セッション更新、入力音声バッファーのイベント、ツールの結果を明示的に送信します。
- OpenAI が WebSocket 接続について文書化しているように、アプリケーションで割り当てる場合は、プライバシーを保護する安定した安全識別子のヘッダーを追加します。
- 生のシークレットや不要な音声を記録せずに、イベント ID、アイテム ID、レスポンス ID、ツール呼び出し、ユーザーに表示される結果を記録します。
標準の API キーをブラウザー、モバイルアプリ、デスクトップパッケージ、またはダウンロード可能なクライアントに含めないでください。認証とメディアに関するトレードオフを意図的に受け入れる場合を除き、ブラウザーからの WebSocket 接続は特殊なプロトタイプとして扱ってください。
モデル、音声、セッションの設定
2026-07-10 時点で確認済み:OpenAI の現在の音声エージェントのサンプルでは gpt-realtime-2.1 が使用されています。古い、または別のリアルタイムモデル名がドキュメントやモデルページに記載されている場合があります。実装時には最新のモデルリファレンスからモデルを選択し、正確なモデル名をログと成果物に記録してください。
起動前に記録する最低限のセッション設定:
model:正確な Realtime モデル識別子と確認日。transport:該当する場合は WebRTC、WebSocket、または SIP/電話ブリッジ。session_type:音声エージェント、文字起こし、または翻訳。voice:正確な音声識別子。そのセッションで最初の音声応答が行われた後は音声を変更できないことも記録します。modalities:出力が音声、テキスト、またはその両方か。- 手動で音声を扱う場合は
input_audio_formatとoutput_audio_format。 turn_detection:server_vad、semantic_vad、または無効/手動。input_audio_transcription:有効かどうか、使用する ASR モデル、文字起こしがユーザーに表示されるかどうか。tools:ツールの一覧、スキーマ、実行主体、承認ポリシー、副作用。retention/logging:生音声、文字起こし、イベント、ツールのペイロードを保存するかどうか、保存期間、アクセスできるユーザー。
経験則:接続後ではなく、セッション作成時に音声を設定してください。現在のドキュメントで最初の音声の後も変更可能と明示されていない限り、通話中の音声変更リクエストは「新しい音声で新しいセッションを開始する」として扱います。
ターンテイキングと割り込みの設計
ターンの状態が見えないと、音声 UX はうまく機能しません。状態機械を明示的に実装してください。
- idle:マイクの権限が有効になっていない、または通話が開始されていない状態。
- listening:ユーザーの音声がストリーミングされている状態。
- user_speaking:VAD が発話開始を検出した状態。
- thinking:ユーザーのターンが確定し、応答が生成されている状態。
- assistant_speaking:音声出力がストリーミングされている状態。
- interrupted:ユーザーの割り込み発話、または UI の停止操作によって、アシスタントの出力がキャンセルまたは途中で切り詰められた状態。
- tool_pending:モデルが外部アクションを要求した状態。
- handoff_or_escalation:人間による対応、または別のワークフローが必要な状態。
- ended:セッションが終了し、要約と成果物が確定した状態。
通常の部屋で会話するデフォルトのアシスタントには server_vad を使用してください。次のように調整します。
- ユーザーが素早い応答のやり取りを期待し、割り込みが多い場合は
silence_duration_msを短くします。 - 考えながら間を置くユーザー、アクセシビリティが重要な場面、言語学習者、複雑なサポート事例では
silence_duration_msを長くします。 - 騒がしい部屋では
thresholdを高くします。 - 最初の音節が途切れないよう、
prefix_padding_msを十分に確保します。
無音に基づくチャンク分割で長い間を置くユーザーの発話が途中で切れる場合や、速度よりも文の完結性が重要な場合は、semantic_vad を検討してください。実際のアクセント、背景雑音、専門分野の語彙を使って慎重にテストしてください。
自動 VAD を無効にして手動でターンを確定するのは、UI に意図的なプッシュトゥトーク、またはオペレーターが制御するワークフローがある場合に限ってください。VAD がオフの場合、ターン境界の管理はアプリケーション側が担い、意図しない背景音声をユーザー入力として送信しないようにする必要があります。
割り込みのルール:
- 「発話を停止」または「アシスタントをミュート」する操作を見える形で用意してください。
- 法的または安全上の理由で許可しない場合を除き、長いアシスタント音声の再生中でも、ユーザーの発話による割り込みを可能にしてください。
- 割り込み後は、実際に発話された内容だけを要約してください。ユーザーが残りの内容を聞いたと想定してはいけません。
- ツール呼び出しでは、音声再生の割り込みと、副作用を伴うツール操作のキャンセルを区別してください。副作用については、個別のキャンセル仕様が必要です。
音声エージェント向けのプロンプトと指示の設計
リアルタイム音声のプロンプトでは、テキストプロンプトでは暗黙のままになりがちな動作を制約する必要があります。
必要に応じて、次の指示ブロックを含めてください。
- 役割と目的:エージェントが何を支援するのか、何を扱ってはならないのか。
- 音声のペルソナ:親しみやすさ、フォーマルさ、話す速さ、簡潔さ、つなぎ言葉の方針。
- 発話の長さ:回答の最大文数または秒数。
- 会話の修復:聞き返し方、不明瞭な音声の確認方法、背景ノイズからの回復方法。
- ツールの方針:ツールを呼び出すタイミング、許可を求めるタイミング、待機中の状態の説明方法。
- 安全上の境界:規制対象の助言、緊急事態、未成年者、本人確認、金融・法律・医療に関する免責事項。
- プライバシーに関する動作:必要でない限り機密情報を尋ねないこと。秘密情報を読み上げる際はマスキングするか、読み上げを避けること。
- 言語に関する方針:対応言語、コードスイッチング、発音、ユーザーが言語を切り替えた場合の対応。
- エスカレーションと引き継ぎ:人間の担当者に引き継ぐタイミング、またはセッションを終了するタイミング。
- 出力スタイル:Markdownを使わないこと、口頭で自然な場合を除きリストを使わないこと、「AI言語モデルとして」などの表現を使わないこと、ツールの結果が届く前にツール操作が完了したふりをしないこと。
音声エージェントのヒューリスティクス:
- 短く答えることを優先します。直接的な回答を一つ述べ、続けて質問を一つします。
- 情報を詰め込んだ列挙は避けます。手順が3つを超える場合は、続けて説明するかどうか尋ねてください。
- 耳で聞いて分かりやすい確認を使います。「注文を確認しました。昨日発送されています。」のように伝え、「ツールが返したステータスは発送済みと等しいです。」のような表現は避けます。
- 謝りすぎないようにします。音声で謝罪を繰り返すと、会話のテンポが遅く感じられます。
- 音声認識結果に確信が持てない場合や名前が曖昧な場合は、重要な項目を確認します。「注文番号は3917とおっしゃいましたか?」のように尋ねます。
- 感情的な会話では、まず簡潔に気持ちを受け止めてから、次に役立つ行動へ進みます。
- ツールの応答に時間がかかる場合は、「ただいま確認します。」のように、一度だけ状況を伝えます。沈黙を憶測で埋めないでください。
ツールと関数呼び出し
音声エージェントが最新のデータや副作用を伴う処理を必要とする場合に限り、ツールを接続してください。ツールはプロンプトの指示に代わるものではありません。
次の場合は、アプリケーションで実行する関数ツールを使用します。
- アプリが非公開のビジネスロジック、認証情報、またはポリシーチェックを管理している。
- 人間による承認が必要になる場合がある。
- 処理によって状態が変更される、金銭が支払われる、メッセージが送信される、予約が行われる、ドアのロックが解除される、または記録が変更される。
- ツールの結果を読み上げる前に、機密情報を削除する必要がある。
MCP ツールまたはコネクタツールは、組織がリモートツールサーバー/コネクタを承認しており、認可の境界が明確で、アプリケーションの境界外に送信される可能性のあるデータがログに記録される場合に限り使用してください。
ツール設計のルール:
- スキーマは小さく、具体的に保ちます。ツールが不足している項目を一つ尋ねるだけで済む場合、音声ユーザーは誤りをより素早く訂正できます。
- 必須フィールドを正確に指定し、モデルにIDを推測させないでください。
- モデルがスキーマに一致するJSONを出力する場合でも、サーバー側で引数を検証してください。
- 状態を示す機械可読フィールドとともに、簡潔で読み上げやすい結果を返します。
- アプリケーション側の承認ルールなしに、モデルに取り消し不可能な操作を実行させないでください。
- 支払い、契約、医療の予約、アカウントの変更、法律上または金銭上の判断については、副作用を伴う操作の直前に明示的な確認を求めてください。
- ツールが失敗した場合は、何が起きたかを伝え、次の手段を一つ提示します。スタックトレースを公開しないでください。
ツール承認ポリシーの例:
Read-only tools may run automatically after user intent is clear.
Drafting tools may create but not send.
Side-effecting tools require explicit user confirmation in the same turn as execution.
High-impact tools require human operator approval outside the model.
The assistant must never claim completion until function_call_output confirms success.
遅延、コスト、コンテキストの制御
遅延予算は、単なるエンジニアリング指標ではなく、ユーザー体験の要件です。最低限、次の項目を追跡してください。
- マイクの許可から接続準備完了まで;
- 発話開始から VAD の speech_started まで;
- 発話停止からレスポンス作成まで;
- レスポンス作成から最初のアシスタント音声まで;
- 最初の音声から最後の音声まで;
- ツール呼び出しの発行からツール結果の返却まで;
- 割り込み指示から音声停止まで。
コストの制御:
- システム指示は簡潔かつ完全に保ちます。長い指示はセッションのコンテキストに繰り返し含まれます。
- 長時間のセッションで会話履歴をそのまま残す必要がない場合は、古い会話 Item を要約または切り詰めます。
- 音声以外の入力や背景雑音に対して Responses を作成しないようにします。VAD と UI のミュート機能を利用してください。
- ツール呼び出しでは、必要なフィールドのみを取得します。
- アイドル状態のセッションは速やかに終了します。
- トランスクリプトが必要かどうかを判断します。入力の文字起こしは、Realtime モデルの料金ではなく、ASR モデルの料金に基づいて別途課金されます。
- OpenAI が公開している目安を使って、音声トークンの使用量を見積もります。ユーザー音声は 100 ms あたり 1 トークン、アシスタント音声は 50 ms あたり 1 トークンで、特殊トークンによる変動もあります。
ヒューリスティック:本番環境のダッシュボードでは、セッション、ユーザーのターン、アシスタントの音声時間、ツールの処理時間、放棄・アイドル時間ごとにコストを分けます。音声チームは、アシスタントが長々と話す部分が、最もコストが高く、UX として最も使いにくい部分でもあると気付くことがよくあります。
同意、権利、プライバシー、安全性
音声を収集する前に、次の対応を行います。
- マイクの使用について、必要に応じて録音やトランスクリプトの保存についても、明確な同意を示します。
- 人間が音声やトランスクリプトを確認する可能性があるかどうかを伝えます。
- 適切な場合は、ミュート、停止、削除・セッション終了、人間によるサポートへの連絡手段を用意します。
- 法務・コンプライアンス上の対策が整っていない限り、子どもの個人データを収集しないでください。
- 製品において明示的な同意、法的根拠、および特定の用途に対するプロバイダーのサポートがない限り、声による生体認証を行わないでください。
生成音声について:
- エージェントが実在の人間であるかのように示してはいけません。ただし、それが事実であり、開示されている場合を除きます。
- 個人、有名人、従業員、臨床医、公職者になりすましてはいけません。
- 適用される音声ポリシーおよび現地法に基づく権利と同意が文書で確認できない限り、声を複製または模倣してはいけません。
- エージェントがブランドを代表する場合は、その役割を明確にします。
プライバシーについて:
- 生の音声の保持を最小限に抑えます。QA に十分な場合は、イベントメタデータと、機密情報を除去したトランスクリプトを優先します。
- API キー、一時的なクライアントシークレット、Authorization ヘッダー、コネクタトークン、またはユーザーが口にした未編集のシークレットを、決してログに記録しないでください。
- 音声を保存しない場合でも PII が含まれる可能性があるため、トランスクリプトは機密情報として扱います。
- ツールやコネクタを使用する場合は、ユーザーの音声から得られたどのデータが各外部システムに送信されるのかを文書化します。
- 内部ドキュメントだけでなく、アプリケーション内でデータの保持と削除の動作を提供します。
安全性について:
- 医療、法律、金融、雇用、住宅、教育、緊急事態、自傷行為に関する状況に応じて、分野固有の拒否とエスカレーションの動作を追加します。
- OpenAI が安全性識別子を推奨している場合は、プライバシーを保護する安定したユーザー識別子を使用します。
- 背景の声、第三者が口頭で伝えるプロンプトインジェクション、近くにいる子ども、差別的な言葉、精神的苦痛、ツールの悪用の試みなど、敵対的な音声を使って評価します。
承認、ログ記録、成果物の管理
本番環境で音声エージェントを運用する場合は、監査可能な実行記録を作成してください。最低限、以下を保存します。
- モデル/APIの検証日;
- モデル、音声、トランスポート、セッション種別、セッション設定;
- プロンプト/指示のバージョン;
- VAD設定;
- ツールスキーマ、ツールの承認ポリシー、コネクターの一覧;
- UIに表示したユーザー同意文;
- 生音声、文字起こし、イベント、ツールのペイロード、要約の保持ポリシー;
- テストマトリックスと結果;
- 既知の制限事項とエスカレーション先。
セッションごとに、以下を記録します。
- セッションIDまたはアプリケーションの相関ID;
- ユーザーの安全識別子または内部ユーザーIDのハッシュ。可能な限り、生のPIIは記録しないでください;
- 接続、音声イベント、応答、中断、ツール呼び出し、切断のタイムスタンプ;
- APIイベントから取得したトークン数/利用時間;
- エラーと復旧対応;
- 成果物の管理情報:音声/文字起こし/イベントログの保存場所、保持期限、アクセス区分。
評価用または採点用の成果物は、本番エージェントのコンテキストに決して保存しないでください。製品のプロンプト、テスト用フィクスチャ、採点用資料は分離しておきます。
完全な例:ブラウザー上のカスタマーサポート音声エージェント
インテントの例:ログイン済みのECサイト利用者が、音声で注文状況を尋ね、アシスタントを中断し、担当者への引き継ぎを依頼できます。アシスタントは注文を照会できますが、明示的な確認とサーバー側の承認なしに、キャンセルや返金を行うことはできません。
2026-07-10に検証したAPIの選択肢:
- セッション種別:音声エージェントのRealtimeセッション;
- モデル:ビルド時にモデルのドキュメントで引き続き最新であることを確認できた場合は、
gpt-realtime-2.1; - トランスポート:Agents SDKヘルパーを使用したブラウザーのWebRTC;
- VAD:
server_vad。騒がしい環境でのテスト後に調整; - ツール:アプリケーションが実行する
lookup_order、create_refund_draft、request_human_handoff; - ログ記録:イベントメタデータと、機密情報をマスキングした文字起こしを保持。ユーザーがQAレビューへの参加を選択しない限り、生音声はデフォルトで保存しない。
音声エージェントへの指示:
# Role & objective
You are Acme Retail's voice support assistant. Help authenticated customers understand orders, shipping, returns, and simple account questions.
# Spoken style
Use a warm, calm retail-support voice. Keep answers to 1-2 short sentences, then ask one useful follow-up question. Do not use markdown or numbered lists unless the user asks for steps.
# Audio uncertainty
If a name, order number, address, or amount is uncertain, ask the user to confirm it before using a tool. If background noise may have changed intent, ask a clarifying question.
# Tools
Use lookup_order for order status. It is read-only and may run automatically after intent is clear.
Use create_refund_draft only after explaining the refund reason you heard. It creates a draft, not a refund.
Before any cancellation, refund, message send, or account change, ask for explicit confirmation. Never say an action is complete until the tool result says success.
# Privacy
Do not read full addresses, payment details, tokens, or secrets aloud. Mask sensitive fields. If the user starts sharing a card number or password, stop them and explain that you do not need it.
# Boundaries
For threats, self-harm, legal claims, medical issues, or harassment, move to the approved escalation route. If the user wants a human, call request_human_handoff.
サーバー側での一時セッション作成パターン:
// Example only. Re-check current OpenAI SDK and REST shapes before production.
import express from "express";
import OpenAI from "openai";
const app = express();
app.use(express.json());
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
app.post("/voice/session", requireLoggedInUser, async (req, res) => {
const realtimeSession = await openai.beta.realtime.sessions.create({
model: "gpt-realtime-2.1",
voice: "alloy",
instructions: SUPPORT_VOICE_INSTRUCTIONS,
audio: {
input: {
turn_detection: {
type: "server_vad",
threshold: 0.55,
prefix_padding_ms: 300,
silence_duration_ms: 650
}
}
},
tools: [
{
type: "function",
name: "lookup_order",
description: "Look up an authenticated customer's order status.",
parameters: {
type: "object",
properties: {
order_number: { type: "string" }
},
required: ["order_number"]
}
}
]
});
audit.log("realtime_session_created", {
user_hash: hashUser(req.user.id),
model: "gpt-realtime-2.1",
voice: "alloy",
raw_audio_retention: "off"
});
res.json({ client_secret: realtimeSession.client_secret });
});
ブラウザー接続パターン:
// Example only. Prefer current Agents SDK docs at implementation time.
import { RealtimeAgent, RealtimeSession } from "@openai/agents/realtime";
const agent = new RealtimeAgent({
name: "Acme Support",
instructions: SUPPORT_VOICE_INSTRUCTIONS,
});
const session = new RealtimeSession(agent, {
model: "gpt-realtime-2.1",
});
async function startVoice() {
await showConsentAndGetMicPermission();
const auth = await fetch("/voice/session", { method: "POST" }).then(r => r.json());
await session.connect({ apiKey: auth.client_secret.value });
ui.setState("listening");
}
session.on("transport_event", (event) => {
auditClientEvent(event);
updateVoiceUI(event);
});
この例をこのような構成にした理由:
- ブラウザーに標準APIキーが渡ることはありません。
- アシスタントの役割を限定し、音声での応答を短くしています。
- ツールによる副作用のある操作を、読み取り専用の照会から分離しています。
- 同意とデータ保持を明確にしています。
- VADは設定していますが、実際の音声でのテストを想定しています。
想定される障害モード:
- アシスタントが注文に関する機密情報を声に出して読み上げる。
- VADが、ためらいながら話すユーザーの注文番号を途中で切ってしまう。
- ツールが過剰な量のJSONを返し、アシスタントがそのまま読み上げる。
- フロントエンドが一時的なシークレットをログに記録する。
- 下書きを作成しただけなのに、モデルが返金を実行したと言う。
完全な例:サーバーサイドのインタビュースクリーナー
例の意図:ある調査チームが、製品に関するフィードバックを得るため、同意を得たうえで音声インタビューを実施します。エージェントは用意された質問を投げかけ、自然に追加質問を行い、回答にタグを付け、各セクションの後に save_answer を呼び出します。バックエンドで適格性が確認されない限り、ユーザーが報酬を受け取ると主張してはなりません。
アーキテクチャ:
- 調査プラットフォームが管理されたインフラストラクチャを通じて録音するため、サーバー間 WebSocket を使用します。
- VAD:選択したセッションで現在のドキュメントとモデルが対応している場合は
semantic_vadを使用し、それ以外の場合は調整済みのserver_vadを使用します。 - 出力:音声と、モデレーターダッシュボード用の任意のテキスト要約です。
- ユーザーが同意した場合に限り、音声の生データを 14 日間保持します。調査分析のため、匿名化した文字起こしを保持します。
指示:
# Role & objective
You are a voice research moderator. Run a 12-minute interview about a beta productivity app. Ask one question at a time, listen actively, and save concise answer notes after each topic.
# Interview method
Use the approved topic order: onboarding, first useful moment, confusing moments, trust concerns, pricing reaction, final recommendation.
Ask one follow-up when an answer is vague. Do not argue, persuade, or defend the product.
# Spoken style
Sound curious and neutral. Keep your turns under 12 seconds. Leave space for the participant to think.
# Consent and privacy
If the participant asks about recording, say: "We record only if you opted in on the consent screen. You can stop at any time." Do not request passwords, payment details, or medical information.
# Tools
After each topic, call save_answer with topic, concise_summary, notable_quote_if_any, and sentiment. Do not invent quotes. If you are uncertain, omit the quote.
Call flag_for_human_review if the participant withdraws consent, reports a safety issue, or asks to delete data.
サーバー WebSocket のパターン:
// Example only. Match event names and fields to the current Realtime API reference.
import WebSocket from "ws";
const ws = new WebSocket("wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1", {
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"OpenAI-Safety-Identifier": hashParticipant(participantId),
},
});
ws.on("open", () => {
ws.send(JSON.stringify({
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2.1",
voice: "alloy",
instructions: INTERVIEW_INSTRUCTIONS,
audio: {
input: {
turn_detection: {
type: "server_vad",
threshold: 0.5,
prefix_padding_ms: 400,
silence_duration_ms: 900
}
}
},
tools: [{
type: "function",
name: "save_answer",
description: "Save a concise research note for the current interview topic.",
parameters: {
type: "object",
properties: {
topic: { type: "string" },
concise_summary: { type: "string" },
notable_quote_if_any: { type: "string" },
sentiment: { type: "string", enum: ["positive", "neutral", "negative", "mixed"] }
},
required: ["topic", "concise_summary", "sentiment"]
}
}]
}
}));
});
ws.on("message", async (raw) => {
const event = JSON.parse(raw.toString());
logRealtimeEventMetadata(event);
if (event.type === "response.function_call_arguments.done") {
const result = await executeApprovedTool(event.name, event.arguments);
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: event.call_id,
output: JSON.stringify(result)
}
}));
ws.send(JSON.stringify({ type: "response.create" }));
}
});
この例をこのような構成にした理由:
- プロンプトでは、サポート担当者としての振る舞いではなく、調査手法を徹底させます。
silence_duration_msを長めに設定すると、参加者が考える時間を確保できます。save_answerは追加的で監査可能なものであり、隠れた意思決定エンジンではありません。- 同意の撤回は、主要なツール経路として扱います。
起こりやすい失敗:
- アシスタントが沈黙をすぐに埋めてしまい、インタビューにバイアスがかかります。
- 言い換えた内容を引用として保存します。
- 同意撤回の経路がテストされておらず、同意の撤回を見逃します。
- 体裁の整った要約は作成しますが、根拠に紐づいた調査メモを作成しません。
リリース前の確認事項
以下の項目をすべて満たすまで、リリースをブロックしてください。
- モデル、API フィールド、料金の前提、セッション時間、音声の一覧、トランスポート経路、ポリシー上の制約を、日付を記録したうえで OpenAI のドキュメントに照らして再確認しています。
- アーキテクチャの選択を文書化し、ファイルベースの OpenAI 音声 API と区別しています。
- 標準の API キーはサーバー側に保持し、クライアントの認証情報は一時的、または同等にスコープが限定されたものにしています。
- 音声のキャプチャを開始する前に、マイクの使用と録音・文字起こしへの同意をユーザーに提示しています。
- UI にミュート/停止/終了コントロールと、聞き取り中/発話中を示す表示があります。
- 実際のマイク、アクセント、ノイズ、発話の重なり、間、割り込みを使って VAD をテストしています。
- ツールのスキーマは最小限で、サーバー側で検証され、副作用がある操作には承認ゲートが設けられています。
- 不要な音声の生データやシークレットを保持することなく、デバッグ、不正利用の確認、コスト分析に十分な情報をログに記録しています。
- 敵対的な音声シナリオを使って、安全性、エスカレーション、人間への引き継ぎ動作をテストしています。
- コストダッシュボードに、音声トークン/時間や文字起こしのコストを含め、セッション単位とターン単位の使用量が表示されています。
- セッションの失敗、モデルの利用不可、ツールの失敗、音声の停止、ユーザーによる同意撤回、データ削除リクエストに対応するランブックがあります。
簡単なトラブルシューティング
エージェントがユーザーの発話にかぶせて話す場合:
silence_duration_msを増やし、semantic VAD を検討し、アシスタントの回答を短くして、割り込み時に音声再生がキャンセルされることを確認してください。
最初の音節が途切れる場合:
prefix_padding_msを増やし、クライアントのマイクバッファリングを調べ、声の小さい話者でテストしてください。
エージェントの応答が遅い場合:
- 最初の音声が出るまでのレイテンシーと回答全体の長さを分けて計測し、プロンプトの肥大化を抑え、音声での応答を短くし、不要なツールを減らして、ブラウザー/モバイルでは WebRTC を使用してください。
文字起こしとモデルの動作が食い違う場合:
- 入力の文字起こしは別個の ASR 処理であることを念頭に置いてください。文字起こしは可観測性のために使用し、モデルが音声を正確にどう理解したかの証拠として扱わないでください。
セッション中にコストが増加する場合:
- 会話の増加、アシスタントの長い音声、不要な Responses、背景ノイズ、文字起こしの設定を確認してください。
ツールの操作が安全でない場合:
- 承認をモデルの外で行い、明示的な確認を必須にし、スキーマを絞り込み、音声で安全に伝えられるツールの結果を返してください。
ユーザーが音声を信頼しない場合:
- それが AI 音声エージェントであることを開示し、録音または保存される内容を説明し、人間のように見せかける欺瞞を避け、人間に対応を引き継ぐ手段を用意してください。