音声エージェントはかつて、音声認識(speech-to-text)、言語モデル、音声合成(text-to-speech)という3つの可動部分を必要としていました。それぞれのステップで遅延が増し、音調が失われていました。OpenAIのRealtime APIは、これらを1つの音声対音声モデルに統合し、gpt-realtime-2.1-miniはそのファミリーのより安価で高速なティアです。これは音声を聴き取り、考え、単一のストリーミング接続を介して応答します。
このガイドでは、使用するモデルID、WebSocketとWebRTC経由での接続方法、セッションの形成方法、そしてアプリに組み込む前にApidogを使って全体をテストする方法まで、エンドツーエンドで呼び出す方法を説明します。ここにあるすべての内容は、公式のOpenAI Realtimeガイドに対応しています。
まず、正しいモデル名を知る
名前のせいで混乱する人がいるので、コードを書く前に整理しておきましょう。同じミニモデルには2つの識別子があります。
gpt-realtime-2.1-mini: バージョン指定ID。これはOpenAIの料金ページに表示され、2.1世代に固定されます。gpt-realtime-mini: ファミリーエイリアス。常に最新のスナップショット(現在はgpt-realtime-mini-2025-12-15)を指します。
スナップショットを使用すると、本番環境での動作を固定できます。
| 識別子 | 指し示すもの |
|---|---|
gpt-realtime-mini |
最新のミニスナップショット(自動更新) |
gpt-realtime-2.1-mini |
2.1世代のミニモデル |
gpt-realtime-mini-2025-12-15 |
固定されたスナップショット(現在のもの) |
gpt-realtime-mini-2025-10-06 |
固定されたスナップショット(以前のもの) |
構築中はエイリアスを使用し、リリースする前に日付付きのスナップショットを固定してください。そうすることで、モデルの更新がエージェントの動作を一夜にして変更することがなくなります。

gpt-realtime-2.1-miniができること
これは音声対音声モデルです。音声をストリーミング入力すると、別途テキスト化や音声合成のステップなしに、自然なイントネーションで音声をストリーミングして返します。テキストも処理できるため、同じセッションでタイプ入力と音声出力を混在させることができます。
こちらはモデルページからのスペックシートです。
| プロパティ | 値 |
|---|---|
| 入力モダリティ | テキスト、画像、音声 |
| 出力モダリティ | テキスト、音声 |
| コンテキストウィンドウ | 32,000トークン |
| 最大出力 | 4,096トークン |
| 接続 | WebRTC、WebSocket、SIP |
| 音声 | alloy, ash, ballad, coral, echo, sage, shimmer, verse, marin, cedar |
marinとcedarは最新の音声であり、Realtime API専用です。OpenAIは最も自然な出力のためにこれらを推奨しています。特定の音色が必要な場合は、古い音声も引き続き使用できます。
「ミニ」ティアは、推論の深さをわずかに犠牲にする代わりに、低レイテンシーと大幅なコスト削減を実現します。ほとんどのサポートボット、受注フロー、および音声フロントエンドにとって、これは適切なデフォルトです。より高度な推論が必要な会話の場合にのみ、フルバージョンのgpt-realtime-2.1を使用してください。
費用
ミニモデルはフルモデルのおよそ3分の1の価格です。料金ページからのトークンレート:
| モデル | テキスト入力 | キャッシュ入力 | 音声入力 | 音声出力 |
|---|---|---|---|---|
gpt-realtime-2.1-mini |
$0.60 / 100万 | $0.30 / 100万 | $10 / 100万 | $20 / 100万 |
gpt-realtime-2.1(フル) |
$4.00 / 100万 | $0.40 / 100万 | $32 / 100万 | $64 / 100万 |
音声が料金の大半を占め、最大のコスト要因はエージェントがどれだけ話すかです。1分あたり35秒話すエージェントは、1分あたり15秒話すエージェントの約2倍のコストがかかります。ミニモデルの実際の1分あたりのコストは、冗長性に応じて約$0.06から$0.15の範囲になるため、指示でモデルに簡潔にするよう伝えれば、直接費用を削減できます。料金は変更される可能性があるため、予測を立てる前に最新の料金ページで確認してください。
前提条件
必要なものは以下の3つです。
- Realtimeアクセス権を持つOpenAI APIキー。
OPENAI_API_KEYとして設定します。 - サーバーの例にはNode.js 18+(生のWebSocket用には
wsパッケージ、または公式のopenaiSDK)。 - ブラウザでの音声には、
getUserMediaが動作するようにHTTPSまたはlocalhost経由で提供されるページ。
ブラウザに触れる前に1つのルール:実際のAPIキーをクライアントに決して送信しないでください。ブラウザやモバイルアプリは代わりに短命な一時トークンを使用します。これについては後述します。
接続方法を選択
ミニモデルは3つのトランスポートに対応しています。音声がどこにあるかに応じて選択してください。
| トランスポート | 使用する状況 | 認証 |
|---|---|---|
| WebRTC | ブラウザやモバイルアプリで音声がキャプチャまたは再生される場合 | 一時的なクライアントシークレット |
| WebSocket | サーバーがメディアパイプラインからの生の音声をすでに処理している場合 | APIキー(サーバーサイド) |
| SIP | 電話または電話システムに接続する場合 | APIキー |
ほとんどの人は、サーバーサイドのプロトタイプ作成にはWebSocketから始め、実際のクライアントにはWebRTCに移行します。両方を試してみましょう。
クイックスタート1:サーバーからのWebSocket
WebSocketは、モデルの応答を確認する最も速い方法です。エンドポイントは、クエリ文字列にモデルを含む単一のURLです。
wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1-mini
これはGA(一般公開)インターフェースであるため、Authorization: Bearerヘッダーで認証し、古いOpenAI-Betaヘッダーは不要になりました。マイクなしでテストできるように、テキスト入力・テキスト出力の「ハローワールド」を以下に示します。
import WebSocket from "ws";
const url = "wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1-mini";
const ws = new WebSocket(url, {
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
});
ws.on("open", () => {
// 1. Configure the session
ws.send(JSON.stringify({
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2.1-mini",
output_modalities: ["text"],
instructions: "You are a concise API support agent. Keep answers short.",
},
}));
// 2. Add a user message
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [{ type: "input_text", text: "What is an idempotent request?" }],
},
}));
// 3. Ask for a response
ws.send(JSON.stringify({ type: "response.create" }));
});
ws.on("message", (raw) => {
const event = JSON.parse(raw.toString());
if (event.type === "response.output_text.delta") process.stdout.write(event.delta);
if (event.type === "response.done") ws.close();
});
ワークフローは常に同じです: 設定し、入力を追加し、応答をリクエストし、差分をリッスンします。サーバーイベントはJSONとしてストリーミングで返されます。特に重要なのは以下のものです。
session.created/session.updated: 設定が受理されたresponse.output_text.delta: テキストのチャンクresponse.output_audio.delta: Base64形式の音声チャンクresponse.output_audio_transcript.delta: モデルが話している内容の文字起こしresponse.done: ターンが終了した
テキストから音声に移行するには、output_modalitiesを["audio"]に切り替え、音声設定を追加します(次のセクション)。音声はresponse.output_audio.deltaイベントでBase64形式のPCMチャンクとして届き、それをデコードして再生します。
クイックスタート2:ブラウザでのWebRTC
実際の音声アプリでは、ブラウザがマイクをキャプチャし、応答を直接再生することでレイテンシーを低く保ちます。問題は認証です。APIキーを公開できないため、まずサーバーが短命なトークンを生成します。
ステップ1:サーバーで一時トークンを生成します。実際のキーを使用してクライアントシークレットエンドポイントを呼び出します。
// server side
const r = await fetch("https://api.openai.com/v1/realtime/client_secrets", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
session: { type: "realtime", model: "gpt-realtime-2.1-mini" },
}),
});
const { value } = await r.json(); // ephemeral key, starts with "ek_"
valueをブラウザに送信します。これはすぐに期限切れになるため、漏洩のリスクは低いです。
ステップ2:WebRTCを使用してブラウザから接続します。マイクをキャプチャし、イベント用のデータチャネルを開き、/v1/realtime/callsエンドポイントとSDPを交換します。
// browser side: `EPHEMERAL_KEY` came from your server
const pc = new RTCPeerConnection();
// play the model's audio
pc.ontrack = (e) => (document.getElementById("audio").srcObject = e.streams[0]);
// send the mic
const mic = await navigator.mediaDevices.getUserMedia({ audio: true });
pc.addTrack(mic.getTracks()[0]);
// events flow over a data channel
const channel = pc.createDataChannel("oai-events");
channel.onmessage = (e) => console.log(JSON.parse(e.data));
// SDP handshake
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const sdpResp = await fetch(
"https://api.openai.com/v1/realtime/calls?model=gpt-realtime-2.1-mini",
{
method: "POST",
body: offer.sdp,
headers: {
Authorization: `Bearer ${EPHEMERAL_KEY}`,
"Content-Type": "application/sdp",
},
}
);
await pc.setRemoteDescription({ type: "answer", sdp: await sdpResp.text() });
接続が確立されると、モデルはマイクのトラックをリッスンし、pc.ontrackを通じて話します。WebSocketの例と同じJSONイベントを使用して、同じoai-eventsデータチャネル経由で設定とテキストを送信します。
セッションの形成
sessionオブジェクトは動作を制御する場所です。これは、上記で見たもののフルオーディオバージョンです。
{
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2.1-mini",
output_modalities: ["audio"],
instructions: "You are a friendly booking assistant. Confirm details before acting.",
audio: {
input: {
format: { type: "audio/pcm", rate: 24000 },
turn_detection: { type: "semantic_vad" },
},
output: {
format: { type: "audio/pcm", rate: 24000 },
voice: "marin",
},
},
},
}
重要なフィールドは次のとおりです。
instructions: システムプロンプト。ペルソナ、ガードレールを設定し、コストを気にする場合は「簡潔に」と指示します。output_modalities: 発話するエージェントには["audio"]、文字起こしのみのボットには["text"]。audio.output.voice: 10種類の音声から選択します。marinまたはcedarが最も自然な音に聞こえます。audio.input.turn_detection: モデルが話すのをやめたと判断する方法。semantic_vadは意味の自然な中断を待ち、server_vadは沈黙でトリガーされます。セマンティック検出は中断が少なく、会話がよりスムーズに感じられます。
呼び出し中に任意のフィールドを変更するには、別のsession.updateを送信します。再接続する必要はありません。
エージェントが行動できるようにツールを追加する
チャットしかできない音声エージェントはデモです。テーブルを予約したり、注文を確認したりするには、モデルにツールが必要です。Realtimeはプラットフォームの他の部分と同じ関数呼び出し契約を使用します。セッションで関数を宣言し、モデルが呼び出しを発行し、それを実行し、結果をフィードバックします。以前にチャットAPIにツールを組み込んだことがある場合、これは同じメンタルモデルです。OpenAIの関数呼び出しのチュートリアルではスキーマを詳細に説明しており、構造化出力は、引数を厳密な形式に合わせる必要がある場合に役立ちます。
セッション内でツールを宣言し、response.function_call_arguments.doneイベントを処理し、コードを実行し、次のresponse.createの前に結果を含むconversation.item.createを投稿します。いくつかの関数よりも複雑なものについては、OpenAIのAgentKitが多段階の音声エージェントをオーケストレーションするためのより高レベルな方法を提供します。
構築前にApidogでエンドポイントをテストする
半構築されたアプリでコンソールログを読んでRESTコールとWebSocketハンドシェイクをデバッグしたくはないでしょう。まず各部分を単独でテストしてください。これがApidogがリアルタイムワークフローでその価値を発揮する場所です。
クライアントコードを記述する前に検証すべき点が2つあります。
- トークンエンドポイント。
POST https://api.openai.com/v1/realtime/client_secretsは通常のRESTコールです。Apidogでリクエストを作成し、Authorization: Bearerヘッダーを追加し、モデルIDを含むJSONボディを挿入して送信します。すぐにek_トークンとその有効期限が表示されるため、WebRTCが考慮される前にキーとアカウントアクセスが正常であることを確認できます。これは、Responses APIのようなOpenAIのRESTインターフェースをスモークテストする際に使用するのと同じアプローチです。 - WebSocketメッセージフロー。ApidogにはWebSocketクライアントがあるため、
wss://api.openai.com/v1/realtime?model=gpt-realtime-2.1-miniに接続を開き、認証ヘッダーを追加し、session.update、conversation.item.create、response.createメッセージを1つずつ手動で送信できます。読みやすいパネルでサーバーイベントが返ってくるのを見ることで、イベントシーケンスが明らかになり、メッセージをチームの例として保存できます。堅固なAPIテスト戦略をすでに利用している場合、これはぴったりです。
トランスポート層を単独でテストすることは、アプリで何か問題が発生したときに、それがAPI契約の問題ではないことをすでに知っていることを意味します。このガイドに沿って進めたい場合は、Apidogをダウンロードしてください。
費用を管理する
音声出力は高価な部分なので、いくつかの習慣が役立ちます。
- モデルに簡潔にするよう指示する。指示で「回答を1~2文に抑える」とすることで、音声出力トークンを直接削減できます。
- 本番環境でスナップショットを固定する。
gpt-realtime-mini-2025-12-15は変動しませんが、gpt-realtime-miniエイリアスは変動する可能性があります。 semantic_vadを使用する。誤った中断が少ないということは、支払い対象となる無駄な半端な応答が減ることを意味します。- システムプロンプトをキャッシュする。キャッシュされた入力は100万トークンあたり$0.30に対し、新しいテキスト入力は$0.60なので、安定した指示ブロックはどのターンでも安価です。
- アイドル状態のセッションを閉じる。非アクティブなユーザーとの開かれた接続は、依然として課金される可能性のあるセッションです。
よくあるエラーと修正
- 401 Unauthorized: キーが間違っているか、期限切れの一時トークンを送信しました。一時キーは設計上短命です。セッションごとに新しいキーを生成してください。
- Model not found: 正確なIDを確認してください。
gpt-realtime-mini-2.1ではなくgpt-realtime-2.1-miniです。 - ブラウザで音声が出ない:
pc.ontrackでリモートストリームをアタッチしていないか、ページがHTTPS/localhost上にないためマイクが開かなかった可能性があります。 - モデルがユーザーの発言を遮って話し続ける:
turn_detectionをsemantic_vadに切り替え、マイクのトラックが接続に到達していることを確認してください。 - ベータヘッダーを送信している: GAエンドポイントは
OpenAI-Beta: realtime=v1を必要としません。削除してください。
よくある質問
gpt-realtime-2.1-miniはgpt-realtime-miniと同じですか?事実上同じです。gpt-realtime-2.1-miniはバージョン指定IDで、gpt-realtime-miniは最新のスナップショット(gpt-realtime-mini-2025-12-15)を指すエイリアスです。構築にはエイリアスを使用し、リリース時にはスナップショットを固定してください。
音声エージェントの代わりに、単純な文字起こしとして使用できますか?Realtime APIはインタラクティブな音声対音声用に構築されています。一度限りの文字起こしには、OpenAIの専用文字起こしモデルの方が適しています。低レイテンシーで双方向の会話が必要な場合に、ミニリアルタイムモデルを使用してください。
WebRTCが必要ですか、それともWebSocketで十分ですか?WebSocketはサーバーサイドのパイプラインやクイックプロトタイプには十分です。WebRTCは、ブラウザやモバイルアプリが音声を直接キャプチャして再生する場合に使用します。なぜなら、メディアストリームとジッターを自動的に処理してくれるからです。
どの音声を選ぶべきですか?marinとcedarは最新で最も自然な音声であり、Realtime API専用です。特定の音が必要な場合は、他の8つの音声(alloy, ash, ballad, coral, echo, sage, shimmer, verse)も引き続き機能します。
どのように課金されますか?モダリティごとに分割されたトークン単位です。ミニモデルの場合:テキスト入力100万トークンあたり$0.60、音声入力100万トークンあたり$10、音声出力100万トークンあたり$20です。音声出力が主なコストであるため、冗長性が主なコスト削減のポイントです。
チャットモデルのように関数を呼び出すことはできますか?はい。Realtimeは同じ関数呼び出し契約を使用するため、音声エージェントは会話中に注文を検索したり、在庫を確認したり、アクションをトリガーしたりできます。
次にすること
これで、適切なモデルID、WebSocketプロトタイプ、WebRTCブラウザクライアント、セッション設定、ツール、そして本番環境に投入する前にApidogで各部分をテストする方法を含む完全なループが揃いました。アクセスを確認するためにテキストのみのWebSocketの例から始め、output_modalitiesを音声に切り替え、実際のマイクを使用する準備ができたらWebRTCに移行してください。スナップショットを固定し、モデルに簡潔にするよう指示すれば、請求書で驚くことのない低レイテンシーの音声エージェントを手に入れることができます。
