要約
OpenAIは、異なるユースケースに対応するため、2つのWebSocket APIモードを提供しています。Responses API WebSocketモードは、頻繁なツール呼び出しを伴うエージェントワークフロー向け(20回以上のツール呼び出しで最大40%高速化)、Realtime APIは、低遅延の音声およびオーディオアプリケーション向けです。どちらのモードも、ステートレスなHTTPリクエストの代わりに永続的なWebSocket接続を使用することで、繰り返しの接続オーバーヘッドを排除し、イベント駆動型のステートフルな対話を可能にすることで、レイテンシを削減します。
はじめに
OpenAIのAPIは、単純なリクエスト・レスポンスのパターンを超えて進化しました。迅速なツール呼び出しやリアルタイムオーディオストリーミングを必要とするアプリケーションでは、従来のHTTPモデルは不要なオーバーヘッドを生み出します。新しいリクエストごとに接続の確立、認証、状態の送信が必要となり、同じ会話を続けている場合でも同様です。
OpenAIのWebSocket APIは、永続的な双方向接続を維持することでこの問題を解決します。20回以上の連続したツール呼び出しを伴うエージェントワークフローの場合、これによりエンドツーエンドの実行速度が約40%高速化されます。音声アプリケーションの場合、500ミリ秒未満のレイテンシで、自然で中断可能な会話を実現します。
このガイドでは、OpenAIのWebSocketモードの両方、すなわちツールを多用するエージェントワークフロー向けのResponses APIと、オーディオストリーミング向けのRealtime APIについて説明します。それぞれの用途、実装方法、および効果的なテスト方法を学びます。
OpenAI WebSocket APIとは?
OpenAI WebSocket APIは、OpenAIの言語モデルと対話するためのHTTPに代わるトランスポートメカニズムを提供します。API呼び出しごとに新しい接続を作成する代わりに、WebSocketはセッション期間中開いたままになる単一の長寿命接続を確立します。
主な特徴
永続的な接続: 一度確立されると、WebSocket接続は明示的に閉じられるまで開いたままであり、リクエストごとの接続オーバーヘッドが排除されます。
双方向通信: クライアントとサーバーの両方がいつでもメッセージを送信でき、真のイベント駆動型アーキテクチャを可能にします。
ステートフルなセッション: サーバーは現在の接続のコンテキストを維持し、完全な会話履歴を再送信することなく、以前の応答を参照できるようにします。
イベント駆動型モデル: 通信は、リクエスト・レスポンスのペアではなく、離散的なイベント(JSONメッセージ)を通じて行われます。
WebSocketプロトコルの基本
WebSocket接続は、HTTPアップグレードリクエストから始まり、その後WebSocketプロトコルに切り替わります。OpenAIの場合、以下のようなエンドポイントに接続します。
- Responses API:
wss://api.openai.com/v1/responses - Realtime API:
wss://api.openai.com/v1/realtime?model=gpt-realtime
wss://スキームは、セキュアなWebSocket接続(HTTPにおけるHTTPSに相当)を示します。
2つのWebSocketモードを解説
OpenAIは、それぞれ異なるユースケースに最適化された2つの異なるWebSocketモードを提供しています。
Responses API WebSocketモード
Responses APIは、多数の連続したツール呼び出しを行う必要があるエージェントワークフローのためにWebSocket接続をサポートしています。このモードは、コーディングアシスタント、オーケストレーションシステム、および複雑なタスクを達成するためにツールを繰り返し呼び出す自律エージェント向けに設計されています。
仕組み:
アクティブなWebSocket接続では、サービスは接続ローカルのインメモリキャッシュに1つの以前の応答状態(最新の応答)を保持します。ターンを継続する場合、送信するのは次のものだけです。
previous_response_id(最後の応答への参照)- 新しい入力項目(ユーザーメッセージ、ツール結果など)
サーバーは、会話履歴全体を再処理する代わりに、キャッシュされた状態を再利用します。
パフォーマンス上の利点:
20回以上のツール呼び出しを伴うワークフローの場合、OpenAIはHTTPと比較して最大40%高速なエンドツーエンド実行を報告しています。これは以下の理由によるものです。
- リクエストごとの接続設定が不要
- 繰り返しの認証オーバーヘッドがない
- キャッシュされた状態により処理時間が短縮される
- 小さな継続メッセージのネットワーク遅延が少ない
互換性:
WebSocketモードは、Zero Data Retention (ZDR)とstore=falseオプションの両方で機能するため、プライバシーに配慮したアプリケーションに適しています。
Realtime API WebSocketモード
Realtime APIは、音声駆動型アプリケーション向けに低遅延のストリーミングオーディオ機能を提供します。これにより、モデルがオーディオ入力にオーディオ出力で応答し、中断を自然に処理できる音声対音声の対話が可能になります。
仕組み:
Realtime APIは、WebSocketを使用してステートフルなイベント駆動型セッションを作成します。オーディオチャンクをAPIにストリーミングすると、APIは文字起こしと生成されたオーディオ応答の両方をストリーミングで返します。接続は以下をサポートします。
- オーディオ入力ストリーミング(キャプチャされたオーディオチャンクを送信)
- オーディオ出力ストリーミング(生成されたオーディオをリアルタイムで受信)
- テキスト入出力(ハイブリッドなテキスト+音声対話用)
- 自動中断処理(ユーザーが話すと生成を停止)
主な機能:
音声活動検出 (VAD): このAPIには、ユーザーが一時停止しただけなのか、話し終えたのかを理解するセマンティックVADが含まれています。これにより、より自然な会話の流れが生まれます。
マルチモーダル機能: GPT-4oのネイティブマルチモーダル機能に直接アクセスし、オーディオとテキストの両方を統合されたモデルで処理します。
低遅延: 音声対話用に500ミリ秒未満のレイテンシで設計されており、リアルタイム会話に適しています。
WebSocket vs HTTP: パフォーマンス比較
WebSocketとHTTPのどちらを選択するかは、アプリケーションの特性に依存します。各プロトコルが優れているケースを以下に示します。

WebSocketがHTTPを上回る場合
大量のツール呼び出し:
ワークフローで10回以上の連続したツール呼び出しを行う場合、WebSocketの永続的な接続により、繰り返しのセットアップオーバーヘッドがなくなります。各HTTPリクエストには以下が必要です。
- DNSルックアップ(キャッシュされていない場合)
- TCPハンドシェイク(3ウェイ)
- TLSハンドシェイク(TLS 1.3では2回のラウンドトリップ)
- HTTPリクエスト/レスポンスヘッダー
WebSocketはこれを一度だけ行い、その後接続を再利用します。
レイテンシに敏感なアプリケーション:
1ミリ秒が重要となるリアルタイム音声またはチャットアプリケーションの場合、WebSocketの永続的な接続とストリーミング機能は、知覚されるレイテンシを大幅に削減します。
サーバーによる更新開始:
WebSocketは、ポーリングなしでサーバーがクライアントにデータをプッシュすることを可能にします。長時間実行されるエージェントタスクの場合、サーバーはイベント発生時に進捗状況の更新を送信できます。
HTTPで十分な場合
単純なリクエスト・レスポンス:
1回限りのAPI呼び出しや、1~2回のツール呼び出しを伴うワークフローの場合、HTTPは実装とデバッグがより簡単です。ほとんどの開発者はHTTPクライアントに精通しており、インフラストラクチャ(ロードバランサー、プロキシ)はHTTPをうまく処理します。
ステートレスな操作:
リクエスト間でセッション状態を維持する必要がない場合、HTTPのステートレス性はむしろ利点となります。接続管理は不要です。
インフラストラクチャの制約:
一部のデプロイ環境(サーバーレス関数、特定のプロキシ)は、長寿命のWebSocket接続をサポートしていません。HTTPは普遍的に機能します。
パフォーマンス指標
OpenAIのドキュメントとコミュニティテストに基づいています。
| 指標 | HTTP | WebSocket (Responses API) | WebSocket (Realtime API) |
|---|---|---|---|
| 接続設定 | リクエストごと(約100~300ミリ秒) | 1回のみ(約100~300ミリ秒) | 1回のみ(約100~300ミリ秒) |
| 20回以上のツール呼び出しワークフロー | 基準 | 約40%高速 | 該当なし |
| 音声往復レイテンシ | 該当なし(この目的には設計されていない) | 該当なし | 500ミリ秒未満 |
| メモリオーバーヘッド | 低い(ステートレス) | 中程度(キャッシュされた状態) | 中程度~高い(セッション状態) |
| 実装の複雑さ | 低い | 中程度 | 中程度~高い |
Responses API WebSocketモードの使い方
エージェントワークフローのためにResponses APIへのWebSocket接続を実装しましょう。
前提条件
- Responses APIへのアクセス権を持つOpenAI APIキー
- WebSocketクライアントライブラリ(Node.js用には
ws、Python用にはwebsocket-client) - OpenAI APIでのツール呼び出しに関する理解
接続設定
Node.jsの例:
const WebSocket = require('ws');
// Connect to Responses API WebSocket endpoint
const ws = new WebSocket('wss://api.openai.com/v1/responses', {
headers: {
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
'OpenAI-Beta': 'responses-api=v1'
}
});
ws.on('open', () => {
console.log('Connected to OpenAI Responses API');
// Send initial request
const initialMessage = {
model: 'gpt-4o',
messages: [
{ role: 'user', content: 'Help me analyze this codebase and suggest improvements.' }
],
tools: [
{
type: 'function',
function: {
name: 'read_file',
description: 'Read contents of a file',
parameters: {
type: 'object',
properties: {
path: { type: 'string', description: 'File path to read' }
},
required: ['path']
}
}
},
{
type: 'function',
function: {
name: 'search_code',
description: 'Search for code patterns',
parameters: {
type: 'object',
properties: {
pattern: { type: 'string', description: 'Regex pattern to search' }
},
required: ['pattern']
}
}
}
]
};
ws.send(JSON.stringify(initialMessage));
});
ws.on('message', (data) => {
const response = JSON.parse(data);
console.log('Received:', response);
// Check if model wants to call tools
if (response.choices[0].finish_reason === 'tool_calls') {
const toolCalls = response.choices[0].message.tool_calls;
// Execute tools (simplified)
const toolResults = toolCalls.map(call => ({
tool_call_id: call.id,
output: executeToolLocally(call.function.name, call.function.arguments)
}));
// Continue the conversation with tool results
const continuation = {
previous_response_id: response.id, // Reference previous response
input: toolResults
};
ws.send(JSON.stringify(continuation));
}
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
ws.on('close', () => {
console.log('Connection closed');
});
function executeToolLocally(name, args) {
// Your tool execution logic
if (name === 'read_file') {
const { path } = JSON.parse(args);
return fs.readFileSync(path, 'utf-8');
}
// ... other tools
}
Pythonの例:
import websocket
import json
import os
def on_message(ws, message):
response = json.loads(message)
print(f"Received: {response}")
# Handle tool calls
if response['choices'][0]['finish_reason'] == 'tool_calls':
tool_calls = response['choices'][0]['message']['tool_calls']
# Execute tools
tool_results = []
for call in tool_calls:
result = execute_tool(call['function']['name'],
json.loads(call['function']['arguments']))
tool_results.append({
'tool_call_id': call['id'],
'output': result
})
# Send continuation with only new input + previous_response_id
continuation = {
'previous_response_id': response['id'],
'input': tool_results
}
ws.send(json.dumps(continuation))
def on_error(ws, error):
print(f"Error: {error}")
def on_close(ws, close_status_code, close_msg):
print("Connection closed")
def on_open(ws):
print("Connected to OpenAI Responses API")
# Send initial request
initial_message = {
'model': 'gpt-4o',
'messages': [
{'role': 'user', 'content': 'Analyze this codebase and suggest improvements.'}
],
'tools': [
{
'type': 'function',
'function': {
'name': 'read_file',
'description': 'Read file contents',
'parameters': {
'type': 'object',
'properties': {
'path': {'type': 'string'}
},
'required': ['path']
}
}
}
]
}
ws.send(json.dumps(initial_message))
def execute_tool(name, args):
if name == 'read_file':
with open(args['path'], 'r') as f:
return f.read()
# ... other tools
# Create WebSocket connection
ws = websocket.WebSocketApp(
"wss://api.openai.com/v1/responses",
header={
"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",
"OpenAI-Beta": "responses-api=v1"
},
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever()
主要な実装詳細
状態管理:
HTTPとの決定的な違いは、継続処理でprevious_response_idを使用することです。これは、APIに最後の応答からのキャッシュされた状態を再利用するよう指示します。
入力のみの継続:
ターンを継続する場合、送信するのは次のものだけです。
previous_response_id: キャッシュされた応答を参照input: 新しいデータ(ツール結果、ユーザーメッセージなど)
完全なmessages配列を再送信しないでください。サーバーはすでにそのコンテキストを持っています。
Zero Data Retention:
WebSocketモードでZDRを使用するには、最初のリクエストにstore: falseを含めます。
Realtime API WebSocketモードの使い方
Realtime APIは低遅延の音声対話を可能にします。その実装方法を以下に示します。
前提条件
- Realtime APIアクセス権を持つOpenAI APIキー
- オーディオキャプチャ/再生機能
- WebSocketクライアントライブラリ
- オーディオエンコーディング(最高のパフォーマンスを得るには24kHz、16ビット、モノラルPCM)
接続設定
JavaScript(ブラウザ)の例:
// Connect to Realtime API
const ws = new WebSocket(
`wss://api.openai.com/v1/realtime?model=gpt-realtime`,
['realtime', 'openai-insecure-api-key.' + process.env.OPENAI_API_KEY]
);
ws.addEventListener('open', () => {
console.log('Connected to Realtime API');
// Configure session
ws.send(JSON.stringify({
type: 'session.update',
session: {
modalities: ['text', 'audio'],
voice: 'alloy',
input_audio_format: 'pcm16',
output_audio_format: 'pcm16',
turn_detection: {
type: 'server_vad', // or 'semantic_vad' for smarter detection
threshold: 0.5,
prefix_padding_ms: 300,
silence_duration_ms: 500
}
}
}));
});
ws.addEventListener('message', (event) => {
const message = JSON.parse(event.data);
switch (message.type) {
case 'session.created':
console.log('Session created:', message.session);
break;
case 'conversation.item.created':
console.log('New item:', message.item);
break;
case 'response.audio.delta':
// Received audio chunk - play it
const audioChunk = base64ToArrayBuffer(message.delta);
playAudioChunk(audioChunk);
break;
case 'response.audio_transcript.delta':
// Received transcript chunk
console.log('Transcript:', message.delta);
break;
case 'input_audio_buffer.speech_started':
console.log('User started speaking');
break;
case 'input_audio_buffer.speech_stopped':
console.log('User stopped speaking');
break;
case 'error':
console.error('API error:', message.error);
break;
}
});
// Send audio from microphone
async function streamMicrophoneToAPI() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const audioContext = new AudioContext({ sampleRate: 24000 });
const source = audioContext.createMediaStreamSource(stream);
const processor = audioContext.createScriptProcessor(4096, 1, 1);
processor.onaudioprocess = (e) => {
const inputData = e.inputBuffer.getChannelData(0);
// Convert Float32 to Int16 PCM
const pcmData = new Int16Array(inputData.length);
for (let i = 0; i < inputData.length; i++) {
pcmData[i] = Math.max(-32768, Math.min(32767, inputData[i] * 32768));
}
// Send to API
ws.send(JSON.stringify({
type: 'input_audio_buffer.append',
audio: arrayBufferToBase64(pcmData.buffer)
}));
};
source.connect(processor);
processor.connect(audioContext.destination);
}
// Send text input
function sendTextMessage(text) {
ws.send(JSON.stringify({
type: 'conversation.item.create',
item: {
type: 'message',
role: 'user',
content: [
{ type: 'input_text', text: text }
]
}
}));
// Request response generation
ws.send(JSON.stringify({
type: 'response.create'
}));
}
function playAudioChunk(arrayBuffer) {
const audioContext = new AudioContext({ sampleRate: 24000 });
audioContext.decodeAudioData(arrayBuffer, (buffer) => {
const source = audioContext.createBufferSource();
source.buffer = buffer;
source.connect(audioContext.destination);
source.start();
});
}
Pythonの例:
import websocket
import json
import base64
import pyaudio
# Audio configuration
RATE = 24000
CHUNK = 4096
FORMAT = pyaudio.paInt16
CHANNELS = 1
audio = pyaudio.PyAudio()
def on_open(ws):
print("Connected to Realtime API")
# Configure session
ws.send(json.dumps({
'type': 'session.update',
'session': {
'modalities': ['text', 'audio'],
'voice': 'alloy',
'input_audio_format': 'pcm16',
'output_audio_format': 'pcm16',
'turn_detection': {
'type': 'server_vad',
'threshold': 0.5,
'silence_duration_ms': 500
}
}
}))
# Start streaming microphone
stream_microphone(ws)
def on_message(ws, message):
data = json.loads(message)
if data['type'] == 'response.audio.delta':
# Decode and play audio
audio_chunk = base64.b64decode(data['delta'])
play_audio(audio_chunk)
elif data['type'] == 'response.audio_transcript.delta':
print(f"Transcript: {data['delta']}", end='', flush=True)
elif data['type'] == 'input_audio_buffer.speech_started':
print("\n[User speaking...]")
elif data['type'] == 'error':
print(f"Error: {data['error']}")
def stream_microphone(ws):
stream = audio.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK
)
def audio_thread():
while True:
audio_data = stream.read(CHUNK)
ws.send(json.dumps({
'type': 'input_audio_buffer.append',
'audio': base64.b64encode(audio_data).decode('utf-8')
}))
import threading
threading.Thread(target=audio_thread, daemon=True).start()
def play_audio(audio_chunk):
stream = audio.open(
format=FORMAT,
channels=CHANNELS,
rate=RATE,
output=True
)
stream.write(audio_chunk)
stream.stop_stream()
stream.close()
# Create WebSocket connection
ws = websocket.WebSocketApp(
f"wss://api.openai.com/v1/realtime?model=gpt-realtime",
header={
"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}"
},
on_open=on_open,
on_message=on_message
)
ws.run_forever()
主要な実装詳細
イベントタイプ:
Realtime APIはイベント駆動型通信を使用します。一般的なイベントは以下の通りです。
クライアント → サーバー:
session.update- セッションパラメータの設定input_audio_buffer.append- オーディオチャンクの送信conversation.item.create- テキストメッセージの追加response.create- AI応答のリクエスト
サーバー → クライアント:
session.created- セッション設定の確認response.audio.delta- AIからのオーディオチャンクresponse.audio_transcript.delta- AIオーディオの文字起こしinput_audio_buffer.speech_started/stopped- VADイベントerror- エラー通知
音声活動検出:
2つのVADモードから選択します。
server_vad: 音量と無音時間に基づく基本的な音声活動検出。
semantic_vad: 自然な一時停止とターンの完了を理解する、よりスマートな検出。ユーザーが思考中に一時停止する可能性のある、より自然な会話に使用します。
ApidogでWebSocket接続をテストする
WebSocket APIのテストはHTTPテストとは異なり、接続を維持し、イベントを送信し、双方向のメッセージフローを監視する必要があります。Apidogは、特殊なWebSocketテスト機能を提供します。

ApidogでのWebSocketテストの設定
ステップ1: WebSocketリクエストを作成
Apidogで新しいリクエストを作成し、プロトコルとして「WebSocket」を選択します。接続URLを入力します。

wss://api.openai.com/v1/responses
ステップ2: ヘッダーを設定
認証ヘッダーを追加します。
Authorization: Bearer YOUR_OPENAI_API_KEY
OpenAI-Beta: responses-api=v1
Realtime APIの場合、URLベースの認証も使用できます。
wss://api.openai.com/v1/realtime?model=gpt-realtime
サブプロトコルヘッダーにAPIキーを含めます。
ステップ3: 接続を確立
「Connect」をクリックしてWebSocket接続を確立します。Apidogには以下が表示されます。
- 接続ステータス(接続済み/切断済み)
- レイテンシ指標
- 接続時間
ステップ4: イベントを送信
Apidogのメッセージコンポーザーを使用してJSONイベントを送信します。Responses APIの場合:
{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": "What's the weather in San Francisco?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather",
"parameters": {
"type": "object",
"properties": {
"location": { "type": "string" }
}
}
}
}
]
}
ステップ5: 応答を監視
Apidogには以下が表示されます。
- すべての受信メッセージを時系列順に表示
- メッセージのタイムスタンプとサイズ
- JSONフォーマットとシンタックスハイライト
- デバッグのためのコピー/エクスポート機能
継続のテスト
previous_response_idを使用した継続パターンをテストするには:
- 最初のメッセージを送信し、応答内の
response.idをメモします。 - 新しい入力のみを含む継続を送信します。
{
"previous_response_id": "resp_abc123",
"input": [
{
"tool_call_id": "call_xyz789",
"output": "{\"temperature\": 72, \"conditions\": \"sunny\"}"
}
]
}
- 完全なコンテキストを再送信する場合と比較して、レイテンシが減少していることを確認します。
Realtime APIのテスト
Realtime APIの場合、Apidogでは以下のことが可能です。
- base64エンコードされたオーディオチャンクの送信
- session.updateイベントの監視
- VADイベント(発話開始/停止)の追跡
- 文字起こしデルタのリアルタイム表示
これは、音声アシスタントがユーザーの発話を途中で遮ったり、音声を適切に検出しない理由をデバッグする際に特に役立ちます。
環境変数
Apidogの環境変数を使用してAPIキーを安全に保存します。
{{OPENAI_API_KEY}}
これにより、リクエストを編集することなく、開発キーと本番キーを切り替えることができます。
実際のユースケース
OpenAIのWebSocketモードが優れている実際のシナリオを見てみましょう。
ユースケース1: 自律コーディングエージェント
シナリオ: コードベースを分析し、問題を特定し、自律的に改善を行うコーディングアシスタント。
Responses API WebSocketを使用する理由:
- 典型的なワークフロー: ファイルの読み込み → 分析 → 類似パターンの検索 → その他のファイルの読み込み → 変更の提案
- これにより、タスクごとに15~30回のツール呼び出しが発生します。
- WebSocketモードにより、総実行時間が約40%削減されます。
- 永続的な接続により、すべてのツール呼び出しでコンテキストが維持されます。
実装パターン:
// Initial task
ws.send({ messages: [{ role: 'user', content: 'Audit security vulnerabilities' }], tools: [...] })
// First response: model calls read_file
ws.on('message', (resp1) => {
ws.send({ previous_response_id: resp1.id, input: [tool_result_1] })
})
// Second response: model calls search_code
ws.on('message', (resp2) => {
ws.send({ previous_response_id: resp2.id, input: [tool_result_2] })
})
// Continue for 20+ iterations...
ユースケース2: 音声カスタマーサービスボット
シナリオ: 自然な会話の流れで顧客の問い合わせに対応する電話サポートボット。
Realtime API WebSocketを使用する理由:
- 低レイテンシが不可欠(自然な会話には500ミリ秒未満)
- 割り込み処理が必要(顧客がボットに話し重ねる場合)
- 個別の文字起こしなしに音声入力を直接処理
- リアルタイムで応答をストリーミング(完全な文章を待たない)
実装パターン:
// Stream phone audio to API
phoneSystem.on('audio', (chunk) => {
ws.send({
type: 'input_audio_buffer.append',
audio: base64Encode(chunk)
})
})
// Play AI responses immediately
ws.on('message', (event) => {
if (event.type === 'response.audio.delta') {
phoneSystem.playAudio(base64Decode(event.delta))
}
})
よくある問題のトラブルシューティング
接続の確立に失敗する
症状: WebSocket接続が開かず、すぐに切断イベントが発生する。
一般的な原因:
- 無効なAPIキー -
Authorizationヘッダーを再確認してください。 - ベータヘッダーの欠落 - Responses APIには
OpenAI-Beta: responses-api=v1が必要です。 - ネットワーク制限 - 一部の企業ネットワークではWebSocketがブロックされることがあります。
- 不正なURL -
wss://(ws://ではない)とエンドポイントパスを確認してください。
解決策:
Apidogを使用して、詳細なエラーメッセージで接続をテストします。リクエストインスペクターは送信されたヘッダーを正確に表示するため、APIキーの欠落や間違いを簡単に見つけることができます。
デバッグコード:
ws.on('error', (error) => {
console.error('Connection error:', error);
});
ws.on('close', (code, reason) => {
console.log(`Closed with code ${code}: ${reason}`);
// 一般的なコード:
// 1006: 異常終了(認証の問題が多い)
// 1008: ポリシー違反(無効なヘッダー)
});
WebSocketを使用しているにもかかわらず高遅延
症状: WebSocket接続は機能するが、HTTPより高速ではない。
一般的な原因:
previous_response_idを使用していない - 完全なコンテキストを再送信している- コールドスタート - 新しい接続での最初のリクエストは遅い
- ネットワークレイテンシ - APIサーバーまでの地理的な距離
- 大きなペイロード - 継続時に不要なデータを送信している
解決策:
継続時に新しい入力のみを送信していることを確認してください。
// 誤り - 毎回完全なコンテキストを送信している
ws.send({
messages: [...allPreviousMessages, newMessage],
tools: [...]
})
// 正しい - キャッシュされた状態を参照している
ws.send({
previous_response_id: lastResponse.id,
input: [newMessage]
})
長期間実行される接続でのメモリリーク
症状: 永続的な接続により、アプリケーションのメモリが時間とともに増加する。
一般的な原因:
- イベントリスナーが削除されていない - 再接続時にリスナーが蓄積されている
- オーディオバッファが解放されていない - 再生されたオーディオへの参照を保持している
- メッセージ履歴が増大している - 受信したすべてのメッセージを保存している
解決策:
// 再接続時にイベントリスナーをクリーンアップ
function cleanupAndReconnect(ws) {
ws.removeAllListeners();
ws.close();
const newWs = createConnection();
return newWs;
}
// 再生後にオーディオバッファを解放
function playAndRelease(audioBuffer) {
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start();
source.onended = () => {
source.disconnect();
// バッファはガベージコレクションによって解放されます
};
}
// メッセージ履歴の制限
const messageHistory = [];
const MAX_HISTORY = 100;
ws.on('message', (data) => {
messageHistory.push(data);
if (messageHistory.length > MAX_HISTORY) {
messageHistory.shift(); // 最も古いものを削除
}
});
結論
OpenAIのWebSocket APIモードは、AIアプリケーションに新たな可能性を切り開きます。Responses API WebSocketモードは、頻繁なツール呼び出しを伴うエージェントワークフローで最大40%高速な実行を実現し、自律コーディングアシスタントやオーケストレーションシステムに最適です。Realtime APIは、音声アプリケーション向けに500ミリ秒未満のレイテンシを提供し、自然で中断可能な会話を可能にします。
適切なモードの選択は、ユースケースによって異なります。
- Responses API WebSocket: ツールを多用するエージェント、コーディングアシスタント、研究ツール(10回以上のツール呼び出し)
- Realtime API WebSocket: 音声アシスタント、電話ボット、語学チューター(オーディオストリーミング)
- HTTP: シンプルなリクエスト、サーバーレス環境、1~5回のAPI呼び出し
WebSocket接続の永続的でイベント駆動型の性質は、HTTPとは異なるテストアプローチを必要とします。ApidogのリアルタイムWebSocketクライアントを使用してOpenAIのWebSocket APIをテストしてください — APIキーをインポートし、接続を確立し、イベントを送信し、詳細なロギングで応答を監視します。無料でお試しいただき、本番デプロイ前に統合を検証してください。
