OpenAI Realtime APIはWebSocketまたはWebRTCでクライアントを接続します。サーバー側の接続なら、インターネットがあればどのマシンからでもOpenAIに直接アクセスできます。クライアント側の接続 — これでまさに作りたいものの大半です — では、ブラウザに安全なoriginが必要で、バックエンドがエフェメラルトークンを発行する必要があります。どちらもトンネル経由で動きます。知っておくべき詳細がいくつかあります。
2つの接続方法、トンネルする2つの理由
Realtime APIはサーバー側接続(バックエンドがOpenAIへWebSocketを開く)とクライアント側接続(バックエンドが発行したエフェメラルトークンを使い、ブラウザがWebRTCピア接続またはWebSocketを開く)をサポートします。サーバー側の経路はトンネル不要。クライアント側はほぼ必ず必要です。
クライアント側にHTTPSが必要な理由:
- ブラウザのマイク権限は安全なoriginを要求します。
- WebRTCの
getUserMediaとICE処理はHTTPSで最もうまく動きます。 - (あれば)Service Workerに必要です。
- エフェメラルトークンを得るためにバックエンドの
/sessionエンドポイントを呼ぶなら、フロントがHTTPSならそのバックエンドもHTTPSでなければなりません。
1つ目はブラウザフラグで回避できます。やめましょう。トンネルを使って現実世界で生きましょう。
開発時のRealtimeアプリの構造
最小限のrealtimeアプリは3つの部分から成ります。
- マイク入力を捕捉しオーディオ出力を再生するフロントエンド。
- 本物のAPIキーでOpenAIの
/v1/realtime/sessionsエンドポイントを叩き、エフェメラルトークンを発行するバックエンドのルート。 - エフェメラルトークンを使った、ブラウザからOpenAIへの直接のWebRTCまたはWebSocket接続。
トンネルはフロントエンド+バックエンド(多くの構成では同一ドメイン)を公開します。実際のrealtimeトラフィックはトンネルを通らずブラウザ-OpenAI間を流れます — その接続はHTTPS/WSSで直接です。
PortPreviewでのクイックセットアップ
- dev サーバー(Next.js、Viteなど)をポート3000または5173で起動します。
npx portpreview 3000を実行します。- ブラウザでHTTPS URLを開きます。マイク権限のプロンプトは通常どおり動きます。
- トークン発行エンドポイントを叩き、エフェメラルトークンを取得し、realtime接続を開きます。
- 話すと、ストリーミングされた応答が返ります。
Viteを使うなら、Vite + トンネルのallowedHostsとHMR設定が必要です。Next.jsでどこかにedge runtimeがあるなら、Node cryptoを必要とするルートのruntime設定に注意してください。
トンネル越しのWebSocketアップグレード
realtime接続はWebSocket(またはほぼトンネルを迂回するWebRTC)です。使うトンネルはHTTPのUpgradeリクエストを正しく転送する必要があります。ほとんどは転送します — PortPreview、Cloudflare quick tunnels、ngrokはどれもWebSocketアップグレードを処理します。転送しないと、接続はブラウザで静かに失敗し、汎用的なWebSocket closed before connectedメッセージだけで有用な詳細はありません。
不安ならwscatでアップグレードをテストしましょう。wss://your-tunnel.portpreview.dev/anythingに接続し、ハンドシェイクを観察します。
役立つ開発のコツ
トークン発行リクエストを調べる
フロントエンドはOpenAIを呼ぶバックエンドのルートを呼びます。ここで最も多いdevバグは、/v1/realtime/sessionsへの設定ミスのリクエストです — モデル名が違う、voiceが違う、modalitiesが違う。トンネルはフロント→バックエンドのリクエストは捕捉しますが、バックエンド→OpenAIのホップは捕捉しません。そのリクエストはサーバー側でログするか、両層をローカルで動かしてトンネル経由でプロキシしましょう。
オーディオ形式の不一致
Realtime APIはデフォルトで設定可能なサンプルレートのPCM16を使います。別の形式のオーディオを流し込むと、モデルは拒否するか幻覚を起こします。ブラウザのMediaRecorderはたいていOpusかWebMを返します — 変換を処理するか、それを代わりにやってくれるWebRTCの経路を使う必要があります。
長いセッション中のトークン失効
エフェメラルトークンは短命です。トークンのTTLより長いセッションをテストするなら、スケジュールに沿って更新します。Realtime SDKにはそのためのヘルパーがあります。自前でやるならsession.expiredイベントを監視し、古いトークンが死ぬ前に新しいものを発行します。
本番ではどう見えるか
開発から本番への唯一の実質的な変更はURLです。トンネルのホスト名を本物のドメインに置き換えます。トークン発行エンドポイントはバックエンドに残ります。ブラウザからOpenAIへのWebRTC/WebSocket接続は同じコードパスです。これを考えすぎてrealtimeトラフィックをバックエンド経由でプロキシしようとするチームを見てきました — やめましょう。レイテンシが増え、OpenAIのSDKが前提とするモデルを壊します。
Anthropicの並行的な提供(ストリーミングWebSocketよりtool useループに近い)については、Anthropicのtool useとWebhookをローカルでを参照。WebSocketアップグレードをデフォルトで処理するトンネルのために、PortPreviewのウェイトリストに登録してください。