GPT-Live に接続したら、セッションイベントを使ってコンテキストを追加し、文字起こしを表示し、接続を管理します。GPT-Live は相手の話を聞きながら話すことができます。文字起こしのテキスト、再生済みの音声、バックエンドのタスクの進捗を個別に追跡すると、アシスタントが話している内容と、まだ実行中の処理を画面に表示できます。
このガイドは、接続後に session.started が発行されていることを前提としています。接続の確立と音声ストリーミングについては接続を、バックエンドでの処理については委譲とツールを参照してください。
セッションの設定
セッションの作成時に、モデル、音声、委譲モードを選択します。会話に関する指示をモデルに与え、関連する履歴を含めます。会話が長くなると、GPT-Live がコンテキストを自動的に管理します。
設定項目
| 設定 | 開始時の設定 | セッション中の変更 |
|---|---|---|
| モデル | 必須項目の model を設定します。 |
変更するには、新しいセッションを開始します。 |
| 指示 | 会話での振る舞いを instructions に指定します。上限は 16,384 トークンです。 |
session.instructions.append で指示を追加します。 |
| 履歴 | input に関連する過去のテキストメッセージを指定します。デフォルトは [] です。 |
追加イベントでコンテキストを追加します。 |
| 音声 | audio.output.voice に、サポート対象の音声または使用許可のあるカスタム音声を指定します。デフォルトは marin です。 |
変更するには、新しいセッションを開始します。 |
| 委譲 | delegation.type に client または responses を指定します。委譲の設定を省略するか null を指定すると、クライアントモードになります。 |
現在のモードのまま Responses の設定を更新できます。 |
| 保存 | store を true にすると、セッションをフォークできます。デフォルトは false です。 |
開始時に選択します。 |
音声の選択肢
セッションの作成時に音声を選択します。audio.output.voice には、"quartz" のように API で使う名前を指定します。GPT-Live では、以下の音声も利用できます。
| 音声 | API 名 | 言語 | 話し方の地域的特徴 | 声の印象 | 音声の種類 |
|---|---|---|---|---|---|
| Quartz | quartz |
英語 | オーストラリア | 女性的 | 生成音声 |
| Ripple | ripple |
英語 | オーストラリア | 男性的 | 自然音声 |
| Vesper | vesper |
英語 | 英国 | 男性的 | 自然音声 |
| Willow | willow |
英語 | アイルランド | 女性的 | 自然音声 |
| Stone | stone |
英語 | アイルランド | 男性的 | 自然音声 |
| Gleam | gleam |
英語 | 北米 | 女性的 | 自然音声 |
| Meridian | meridian |
英語 | 北米 | 男性的 | 自然音声 |
| Bossa | bossa |
ポルトガル語 | ブラジル | 女性的 | 自然音声 |
| Tempo | tempo |
ポルトガル語 | ブラジル | 男性的 | 自然音声 |
| Beacon | beacon |
英語 | フィリピン | 男性的 | 生成音声 |
| Delta | delta |
英語 | 米国南部 | 女性的 | 生成音声 |
| Cinder | cinder |
英語 | 米国南部 | 男性的 | 生成音声 |
| Nuri | nuri |
ドイツ語 | — | 女性的 | 自然音声 |
| Juni | juni |
ドイツ語 | — | 女性的 | 自然音声 |
| Flitz | flitz |
ドイツ語 | — | 男性的 | 自然音声 |
| Brise | brise |
フランス語 | — | 女性的 | 自然音声 |
| Sillage | sillage |
フランス語 | — | 男性的 | 自然音声 |
| Nira | nira |
ヒンディー語 | — | 女性的 | 自然音声 |
| Harema | harema |
日本語 | — | 女性的 | 自然音声 |
| Shida | shida |
日本語 | — | 男性的 | 自然音声 |
| Noeul | noeul |
韓国語 | — | 女性的 | 自然音声 |
地域的特徴は、その音声の話し方を表します。アプリケーションで必要な言語と発音で音声をテストしてください。テキストによる説明や承認済みの録音から音声を作成する方法は、カスタム音声を参照してください。
WebSocket では、開始時に audio.format を指定します。セッションの入力音声と出力音声には同じ形式が適用されます。別の形式を使うには、新しいセッションを開始してください。WebRTC は接続の確立時に音声形式をネゴシエートするため、WebRTC リクエストには audio.format を含めません。サポート対象の形式とストリーミングの詳細は、WebSocket の音声形式を参照してください。
実行中のセッションの更新
すでに Responses 委譲を使用しているセッションでは、session.update で session.delegation.responses の設定を変更します。変更する項目だけを送信してください。省略した項目は現在の値を保持します。設定項目と更新手順は、Responses 委譲の設定を参照してください。
委譲モードと、model、instructions、input、audio、store の各フィールドは開始時に指定します。session.update は、上記のサポート対象の Responses 設定にのみ使用してください。それ以外の設定項目は拒否されます。委譲モードを切り替えるには、新しいセッションを作成します。開始時に delegation: null を指定するとクライアント委譲が選択されます。Responses のデフォルト設定には戻りません。
更新に成功すると、更新後のセッション設定を含む session.updated が発行されます。送信時の event_id と client_event_id を照合して対応付けます。拒否されたコマンドも同じイベントループで処理してください。バックエンドの処理と音声出力は、それぞれ専用のイベントで追跡します。
履歴とコンテキストの提供
以前の話題を再開するには、セッション開始時に履歴を渡します。会話が進むにつれて、関連するコンテキストを追加します。信頼できるアプリケーションからの指示は、ユーザーのメッセージや事実を伝える処理結果と区別してください。
過去の会話を使ったセッションの初期化
セッションの作成時に、過去のテキストメッセージを session.input に含めます。たとえば、次の input フィールドをセッション作成時の設定に追加します。
export const session = {
model: "gpt-live-1",
input: [
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "I need help with my recent order.",
},
],
},
{
type: "message",
role: "assistant",
content: [
{
type: "output_text",
text: "What is the order number?",
},
],
},
],
};リストには最大 128 件のメッセージを指定でき、合計の上限は 8,192 トークンです。各メッセージにはテキストパートを 1 つ含め、ロールとして developer、user、assistant のいずれかを指定します。開発者とユーザーのメッセージには input_text を、アシスタントのメッセージには text または output_text を使います。信頼できるアプリケーションからの指示は instructions または開発者メッセージに含めてください。
次のやり取りに必要なテキスト履歴を選び、開始時に渡します。セッション中の更新には、以下のコンテキストイベントを使います。ツールの結果など、バックエンド固有の項目は委譲のワークフローを通じて送信してください。
コンテキストがモデルに届くタイミング
モデルが最初から必要とするコンテキストは input に含めます。このフィールドの内容はすべて、セッション開始時から利用できます。
セッション中は、session.instructions.append、session.thinking.append、session.commentary.append でコンテキストを順次追加できます。セッションのタイムラインが追加したコンテキストの推定終了時刻に達すると、確認応答が届きます。確認応答の start_ms と end_ms は、その更新がタイムライン上のどの区間に当たるかを示す推定値です。
これらの時刻はコンテキストの配信に関するもので、発話や再生の時刻は示しません。モデルが更新内容をすべて使う前に応答することもあります。新しい指示や事実に基づいてアクションを実行する場合は、アプリケーションで実際の動作を確認してください。
セッションのタイムラインが停止すると、確認応答が保留のままになることがあります。送信時の event_id と client_event_id を照合して確認応答を対応付け、待機中もエラー処理を続けてください。セッションを閉じると、保留中の追加リクエストにはエラーが返ります。
会話中のコンテキストの追加
モデルに更新内容をどう使わせたいかに応じて、イベントを選びます。
session.instructions.append:振る舞いや発話に反映させたい、信頼できるアプリケーションからの指示を追加します。session.thinking.append:事実に関するコンテキストを追加します。その場で読み上げるようモデルに求めることはありません。session.commentary.append:モデルが声に出して伝える情報を渡します。モデルが言い換える場合があります。
各イベントには、最大 500 トークンの通常の文字列を content に指定し、必須の delegation_id を含めます。セッション全体に適用するコンテキストには null を使います。たとえば、アプリケーションがユーザーの同意を確認して検索を開始した後に、次のイベントを送信します。
export function sendUpdate(connection) {
connection.send({
type: "session.thinking.append",
event_id: "context_1",
delegation_id: null,
content:
"The user has already accepted the terms. The account lookup is still running.",
});
}この更新を追跡するには、session.thinking.appended のうち client_event_id: "context_1" を持つもの、または対応するエラーを処理します。確認応答のタイミングは、コンテキストがモデルに届くタイミングを参照してください。
どのイベントで渡した情報も、アシスタントが会話中に口にする可能性があります。会話で伝えてよい情報だけを送信し、認証情報やシークレットはバックエンドに保管してください。アプリケーションで定めた振る舞いを指示するには session.instructions.append を使います。ツールが返した事実はコンテキストとして渡し、権限のチェックや必須の確認はアプリケーションコードで実施してください。
ページの移動、項目の選択など、UI の変化については、UI コンテキストの共有を参照してください。ユーザーが何を指しているかを GPT-Live が理解できるよう、変更内容を簡潔に伝える方法を紹介しています。
特定のバックエンドタスクに関する更新には、対応するクライアント委譲の ID を使います。委譲 ID は Live のタスクを識別します。Responses のレスポンス ID とツール呼び出し ID は、それぞれ別のオブジェクトを識別します。手順は、用途に応じた更新の送信を参照してください。
アプリケーションが問題を検出したら、セッションのプライマリ WebSocket またはサイドバンド WebSocket を通じて短い訂正を送信します。チェック、アクションの制御、再生の処理については、会話へのガードレールの適用を参照してください。
発話と文字起こしの管理
文字起こしの差分
ユーザーの発話については session.input_transcript.delta を、アシスタントの発話については session.output_transcript.delta を監視します。各イベントにはテキストの断片と、セッションのタイムライン上でその断片に対応する区間が含まれます。
{
"type": "session.input_transcript.delta",
"event_id": "event_transcript_1",
"delta": "What is",
"start_ms": 1000,
"end_ms": 1200
}
話者ごとに delta の断片を受信したとおりに連結し、空白や繰り返された単語もそのまま残します。各断片の start_ms と end_ms も保持してください。これらの値はセッション開始時からの経過時間をミリ秒で表します。上の例では、1,000 ミリ秒以上 1,200 ミリ秒未満の区間に対応します。各断片のおおよその時刻を示す値で、単語の正確な境界を示すものではありません。文字起こしを整理する際には、パケットの到着時刻の代わりにこれらの値を使います。
文字起こしイベントはテキストがある区間について届きますが、配信間隔は一定とは限りません。断片には文の一部しか含まれないことがあり、配信の途切れはネットワーク遅延による場合もあります。文字起こしの差分には項目 ID がなく、会話のターンの完了を示すイベントもありません。表示用に断片をどうまとめるかはアプリケーションで決めます。
文字起こしの断片の処理は任意です。会話が続いている間に、断片を使って UI を更新したり、チェックを実行したり、早めに処理を開始したりできます。簡易チェックには、gpt-5.6-luna などの小型モデルで推論強度を低く設定することを検討してください。例と接続方法は、文字起こしの断片に応じた処理を参照してください。
文字起こしのガードレールを使うと、発話が続いている間も会話を監視し、必要に応じて介入できます。アプリケーションでアシスタントの発話を再生前にチェックする必要がある場合は、再生前の発話チェックを参照してください。バッファリング、承認、中断、復旧の処理を説明しています。
文字起こしの時刻と音声の再生は別々に扱ってください。WebSocket の session.output_audio.delta イベントには時刻フィールドがなく、出力音声の完了を知らせるイベントもありません。WebRTC はメディアトラックを通じて音声を配信します。音声の扱いについては、接続を参照してください。
字幕の表示
GPT-Live は全二重通信に対応しているため、通話者とアシスタントは同時に話せます。発話が重なっても双方のテキストを追記できるよう、字幕は話者ごとに更新してください。
アプリでチャットの吹き出しを使う場合、「予約を」と「変更したいのですが」という断片を、通話者の 1 つの吹き出しに表示できます。通話者が話し続けている間にアシスタントが「はい」と応じた場合は、その相づちを別の吹き出しに表示し、通話者の吹き出しには引き続きテキストを追記します。後から届いたテキストで適切な吹き出しを更新できるよう、元の断片とタイムスタンプを保持してください。
発話の字幕には session.output_transcript.delta を使い、バックエンドの進捗は別に表示します。ツールを実行するか、処理をキャンセルするかは、表示用にテキストをまとめる処理から切り離し、アプリケーションのタスク処理で判断してください。
マイク入力の制御
セッションを終了せずに入力をミュートするには、session.input_audio.mute を送信します。
export function sendUpdate(connection) {
connection.send({
type: "session.input_audio.mute",
event_id: "mute_1",
});
}session.input_audio.muted が client_event_id: "mute_1" を伴って届いてから、コマンドが受理されたものとして扱います。入力を再開するには、session.input_audio.unmute を送信し、session.input_audio.unmuted を待ちます。どちらのコマンドでもエラーを処理してください。
入力をミュートしてもセッションは動作し続けます。モデルは音声を生成でき、委譲した処理も継続できます。ローカルでの録音や再生も止める必要がある場合は、アプリケーション側でマイクの音声取得やプレーヤーの再生を制御してください。
通話者が話し始める前の挨拶
GPT-Live から会話を始めたい場合は、セッションの作成時に developer メッセージを session.input に含め、挨拶の指示を渡すと効果的です。WebSocket では、session.start イベントにこのメッセージを含めます。これでセッションの開始時から挨拶の指示を利用できます。
言語とアシスタントが話す内容を指定し、すぐに話し始め、挨拶が済んだら話すのをやめて相手の話を聞くよう指示します。通話者が話し始めるまでは、アプリケーションで選んだ挨拶の言語を使います。たとえば、開発者メッセージに次のように記述します。
今すぐ英語で通話者に挨拶してください。サポートアシスタントであることを伝え、どのようなご用件か尋ねてください。その後は話すのをやめ、相手の話を聞いてください。
- メッセージを
session.inputに追加し、role: "developer"と、input_textのコンテンツパートを 1 つ指定します。メッセージの形式は、過去の会話を使ったセッションの初期化を参照してください。 session.startedを待ち、以下のように、同じ挨拶の指示をsession.commentary.appendで 1 回だけ送信します。一意のevent_idとdelegation_id: nullを指定してください。通話者が話し始める前の無音区間も含め、音声入力を継続します。WebSocket ではsession.input_audio.appendの送信を続け、WebRTC では入力音声トラックを有効に保ちます。- 出力音声と文字起こしをチェックし、アシスタントが通話者に挨拶したことを確認します。開始時の指示によって挨拶しやすくなりますが、モデルが必ず話すとは限りません。
{
"type": "session.commentary.append",
"event_id": "greeting_1",
"delegation_id": null,
"content": "Greet the caller now in English. Introduce yourself as the support assistant and ask how you can help. Then pause and listen."
}
session.commentary.appended イベントはリクエストの受理を知らせます。挨拶が発話されたことや再生されたことは確認できません。GPT-Live は挨拶を言い換えたり、言葉を加えたりすることがあり、何も話さない場合もあります。
挨拶を確実に再生したい場合、文言を厳密に指定したい場合、または再生完了のタイミングを把握したい場合は、確認済みの録音またはレンダリング済みの音声クリップをアプリケーションで再生し、その間は GPT-Live の再生を制御してください。挨拶の途中で通話者が話し始める場合も含め、対応する各言語で挨拶をテストしてください。プロンプトの設計は、音声モデル向けのプロンプトを参照してください。
告知事項の読み上げ
告知事項を指定の文言で読み上げるよう求めるには、session.instructions.append を使います。session.commentary.append では文言が言い換えられることがあります。たとえば、session.started の後に次のイベントを送信します。
export function sendUpdate(connection) {
connection.send({
type: "session.instructions.append",
event_id: "disclosure_1",
delegation_id: null,
content:
"Immediately say the following disclosure exactly and in full before responding to the caller: This call may be recorded for quality and training purposes.",
});
}通話者が話し始める前の挨拶と同様に、音声入力を継続してください。会話中に送った指示によって、進行中の発話が中断されることがあります。
生成された告知の内容とその再生を確認してから、告知済みとして記録してください。指示の確認応答でわかるのは受理されたことまでです。文言は実際の音声で確認します。文言を厳密に指定し、再生完了のタイミングを把握するには、確認済みの録音またはレンダリング済みの音声クリップをアプリケーションで再生し、その間は GPT-Live の出力を制御してください。必要に応じた再生制御を参照してください。
長時間の会話の管理
GPT-Live は長時間の会話を自動的に管理し、開始時に指定した指示を保持します。
デフォルトのコンテキストウィンドウの容量は 128,000 トークンです。指示、会話のテキスト、文字起こしには現れない音声トークンもこの容量に含まれます。
GPT-Live は過去の会話履歴をバックグラウンドで要約します。コンテキストの使用率が 90% を超えると、同じセッション内で交代用の音声エンジンを起動します。新しいエンジンには、開始時の指示と最大 8,192 トークンの会話履歴が渡されます。履歴には直近のメッセージと、利用可能な場合はそれ以前のメッセージの要約が含まれます。要約が用意されても、稼働中のエンジンのコンテキストが直ちに変わるわけではありません。
過去の会話の詳細は要約されたり、省略されたりすることがあります。重要な事実、確認済みのアクション、現在のタスクの状態はアプリケーションで保持し、必要に応じて関連するコンテキストを渡してください。
セッションの保存とフォーク
フォークすると、保存済みの音声会話をもとに新しいセッションが始まります。同じ会話を基準に評価を繰り返したり、以前のセッションが終了した後でユーザーが会話を再開できるようにしたりする場合に使います。フォークごとに新しい接続とセッション ID が割り当てられ、元の会話と保存済みの設定を引き継いで開始します。
基準となる会話を使った評価
たとえば、通話者が注文を変更する場面でエージェントの対応をテストするとします。通話者が対象の注文を特定するところまでのやり取りを一度録音します。通話者が変更を依頼する前に、そのセッションを閉じて終了処理を完了させます。これで、各評価で同じ基準セッションをフォークし、通話者の次の発話として「Actually, can you send it to my office instead?」という同じ音声を入力できます。
試行ごとにテスト用の注文とアプリケーションの状態を同じ条件に戻し、通話者の次の入力を渡して、新しい応答とツールが実行したアクションを評価します。同じシナリオを繰り返すことも、サポート対象の Responses バックエンド設定を比較することもできます。フォークの起動時間と応答時間は分けて測定してください。シナリオの選び方と結果の測定方法は、GPT-Live 評価ガイドを参照してください。
フォークは GPT-Live のモデル、音声、開始時の指示を引き継ぎます。別の音声モデルや開始時のプロンプトと比較するには、その設定で新しいセッションを作成してください。フォーク API は録音が完了した元の会話を使うため、評価を開始したい時点で基準セッションを終了します。
セッション終了後の会話の再開
たとえば、通話者が電話を切って後でかけ直す場合や、通話が途切れて再接続する場合があります。以前のセッションの録音と保存が完了していれば、アプリケーションは新しい接続でそのセッションをフォークし、保存された会話の続きから再開できます。
元のセッション ID とともに、アプリケーションのタスクの状態を保存します。再開前に、未完了のバックエンド処理の状況を確認し、その時点の結果を新しいセッションに渡してください。たとえば、注文の更新をすでに送信していた場合は、再度更新を試みる前に前回の結果を確認します。セッションの制御とサイドバンド接続には新しいセッション ID を使い、その後のバックエンドの結果は新しいセッションに送ります。
フォークに向けたセッションの準備
- 元のセッションの作成時に保存を有効にします。 セッション設定に
store: trueを指定します。保存のデフォルトはfalseです。保存するには、プロジェクトで保存が有効になっており、データポリシーで永続化が許可されている必要があります。 - 元のセッション ID を保存します。
session.startedまたは WebRTC の作成レスポンスから読み取り、アプリケーションの会話レコードに関連付けます。 - 元のセッションの処理を終えて閉じます。 必要なバックエンド処理を完了し、使用量と正常終了の手順に従います。
session.closedが届くまで接続を維持し、終了処理で発生したエラーに対処してください。フォークするには録音と保存が完了している必要があります。録音を保存する分、終了処理に時間がかかる場合があります。 - 新しい接続でフォークを開始します。 以下の接続方式別の手順で元のセッション ID を使い、新しいセッション ID を保存します。起動が完了してから会話を続けてください。子セッションの
storeは明示的に指定します。後でその続きからフォークしたい場合はtrue、その試行を保存する必要がなければfalseにします。省略すると元の設定を引き継ぎます。
保存した録音は 30 日間利用できます。ゼロデータ保持(ZDR)を使用している場合、store は false として扱われ、フォークは利用できません。フォークを使って評価するには、ZDR を使用していない組織で、プロジェクトの保存を有効にしてください。
保存が完了した録音がない場合は、保存済みの関連するテキスト履歴で新しいセッションを開始してください。保存の要件は、GPT-Live のデータ制御を参照してください。
たとえば、元のセッションの WebSocket session.start の設定または WebRTC の作成リクエストに、次のフィールドを指定します。
{
"store": true
}
アプリケーションが使用する接続方式でフォークを開始します。
| 接続方式 | フォークの開始方法 |
|---|---|
| WebSocket | wss://api.openai.com/v1/live/sessions/{source_session_id}/fork に接続します。 |
| WebRTC | 新しい SDP オファーを POST /v1/live/sessions/{source_session_id}/fork に送信します。返された transport.sdp をアンサーとして新しいピア接続に適用します。 |
フォークは、元のセッションのモデル、開始時の指示、入力を引き継ぎます。開始時には、上書きがサポートされている次の項目だけを送信してください。
- WebSocket:
store、Responses の委譲設定、新しい接続のaudio.formatを指定できます。session.startイベントにsessionオブジェクトを含めて送信します。設定の継承がサポートされている項目をそのまま引き継ぐには、{}を使います。 - WebRTC:
store、Responses の委譲設定、フロントエンドクライアントの権限を指定できます。
WebSocket でフォークする場合は、新しい接続の audio.format を指定するか、デフォルトの 24 kHz の PCM16 を使います。元の音声形式とフロントエンドのデータチャネルの権限は引き継がれません。WebRTC は接続の確立時に音声形式をネゴシエートするため、audio.format は省略します。WebRTC では、上書きしない限りフロントエンドの権限設定が引き継がれます。
WebSocket では、session.started を待ってから次のコマンドを送信します。WebRTC では HTTP リクエストでセッションが始まります。session.start を改めて送信せず、ネゴシエーション済みの接続で処理を続けてください。
WebSocket でのフォークの開始
OPENAI_API_KEY を設定します。以下の例では、元のセッションの録音を保存した際にアプリケーションが控えておいたセッション ID を使います。起動を確認してからフォークを閉じます。会話を続ける場合は、session.started の後に、WebSocket の接続手順に従って音声を送受信してください。開始時のフィールドとイベントは、フォーク用 WebSocket のリファレンスを参照してください。
import OpenAI from "openai";
import { ForksWS } from "openai/resources/live/forks/ws";
async function forkSession(sourceSessionId) {
const ws = new ForksWS(new OpenAI(), { session_id: sourceSessionId });
let finalized = false;
let closeRequested = false;
try {
for await (const event of ws) {
if (event.type === "open") {
ws.send({ type: "session.start", session: {} });
} else if (event.type === "error") {
throw event.error;
} else if (event.type === "message") {
if (event.message.type === "session.started") {
console.log("Fork ready:", event.message.session.id);
// This startup example closes the fork after confirming it is ready.
ws.send({ type: "session.close" });
closeRequested = true;
} else if (event.message.type === "session.closed") {
console.log("Final usage:", event.message.usage);
if (!closeRequested) {
throw new Error("Fork ended before startup completed");
}
finalized = true;
break;
}
}
}
if (!finalized) throw new Error("Connection closed before session.closed");
} finally {
ws.close();
}
}WebRTC でのフォークの開始
フロントエンドで新しい SDP オファーを作成し、バックエンドに送信します。以下のバックエンドの例では、そのオファーと、アプリケーションが控えておいたフォーク元の保存済みセッションの ID を使います。
import OpenAI from "openai";
async function forkSession(sourceSessionId, offerSdp) {
const client = new OpenAI();
const fork = await client.live.sessions.fork(sourceSessionId, {
transport: { type: "webrtc", sdp: offerSdp },
});
console.log(JSON.stringify(fork));
}レスポンスをフロントエンドに返し、transport.sdp を新しいピア接続のアンサーとして適用して、新しい session.id を保持します。API キーはバックエンドに保管してください。
サイドバンド接続とセッションの制御には、新しいセッション ID を使います。未完了のアクションを再試行する前に、バックエンドで結果を確認し、アプリケーションのタスクを現在の状態に復元してください。保存が完了した録音がない場合は、保存した履歴で新しいセッションを初期化します。
録音のダウンロード
録音の保存処理が完了したら、GET /v1/live/sessions/{session_id}/content で音声をダウンロードします。レスポンスはステレオ WAV のバイナリデータで、左チャネルに入力音声、右チャネルに出力音声が入っています。以下の例では、アプリケーションが控えておいた保存済みセッションの ID を使い、レスポンスを recording.wav にストリーミングで書き込みます。
import OpenAI from "openai";
import { createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";
async function downloadRecording(sessionId) {
const client = new OpenAI();
const response = await client.live.sessions.downloadRecording(sessionId);
if (!response.body) throw new Error("Recording response has no body");
await pipeline(response.body, createWriteStream("recording.wav"));
}アイドル時のセッション終了と会話の再開
やり取りの間隔が長いアプリケーションでは、操作がない間は音声セッションを閉じ、ユーザーが戻ったときに新しいセッションを開始します。ユーザーが同じ話を繰り返さずに済むよう、会話のコンテキストとアプリケーションのタスクの状態を保持してください。たとえば、車載アシスタントでは、ドライバーが音声機能を再び有効にしたときに会話を再開できます。コーディングアシスタントでは、次の音声会話までの間もバックエンドのワーカーを動かし続けられます。
- セッションを閉じるタイミングを決めます。 音声の有無、アシスタントの音声再生、アプリケーションでの操作をもとに、アプリケーション側で無操作時のタイムアウトを設定します。ユーザーが文章を読んだり考えたりする間も考慮してください。再生が終わり、現在の音声セッションを必要とする保留中の処理がなくなってから閉じます。文字起こしイベントがしばらく届かないことだけでは、無音とは判断できません。
- 状態を保存して正常終了します。 元のセッション ID、会話のコンテキスト、現在のタスクの状態を保存します。必要な Responses の処理を完了し、使用量と正常終了の手順に従います。
session.closedのリスナーを登録し、session.closeを送信して、session.closedを待ってから接続を解放します。クライアント委譲では、音声セッションを閉じている間も、アプリケーションが管理するバックエンド処理を独立して続けられます。 - 再開のタイミングを検出します。 会話を再開ボタン、押している間だけ話せるボタン、またはアプリケーションが管理する起動トリガーを用意します。閉じた Live セッションではユーザーの発話を聞き取れません。ローカルの発話検出で自動的に再開する場合は、マイクの音声取得を続け、接続が確立するまで話し始めの音声をバッファに保持してください。新しいセッションの準備ができたらその音声を渡すことで、ユーザーの最初の言葉を取りこぼさずに済みます。
- 新しいセッションでコンテキストを復元します。 元のセッションを
store: trueで作成し、保存が有効かつ許可されていて、録音の保存処理が正常に完了している場合は、保存済みセッションをフォークします。それ以外の場合は、保存したテキスト履歴で新しいセッションを開始します。新しいセッション ID を保存し、未完了のバックエンド処理の状況を確認して、以降の結果を新しいセッションに送ります。再開時に完了済みのアクションを繰り返さないよう、アプリケーションで処理状況を保持してください。
マイクをミュートしてもセッションは有効なままです。アイドル時のタイムアウトを決める際は、短縮できる音声利用時間と、セッションの作成コストおよび再び音声を使えるようになるまでの待ち時間を比較してください。音声セッションのコストと WebRTC の初期化料金を参照してください。
エラー処理とセッションの終了
終了処理が完了するまで、セッションイベントの読み取りを続けます。アプリケーションが状況に応じて復旧できるよう、コマンドの拒否、接続の失敗、セッションの終了完了を区別してください。
拒否されたコマンドの処理
確認応答とあわせて error イベントを読み取ります。error.client_event_id が含まれている場合は、その値で失敗した送信コマンドを識別できます。
{
"type": "error",
"event_id": "event_error",
"error": {
"type": "invalid_request_error",
"code": "immutable_field_update",
"message": "The delegation type cannot change after session startup.",
"param": "session.delegation.type",
"client_event_id": "event_update"
}
}
エラーコードが null の場合や、クライアントイベント ID がない場合にも対応できる汎用のエラーハンドラーを用意してください。変更できないフィールドに関するエラーが発生した場合は、現在の設定を維持するか、目的の設定で新しいセッションを作成します。
モデレーションへの対応
モデレーションがセッションに及ぼす影響には、次の 2 通りがあります。
- 一部のモデレーションイベントはセッションを終了させます。
- それ以外のモデレーションイベントは、アシスタントの現在の発話の残りの音声を打ち切り、セッションを終了せずに
errorイベントを発行します。
音声の再生中も error イベントの処理を続けてください。音声の中断とセッションの終了を個別に追跡し、発話メッセージは再生を確認してから配信済みとして記録します。組み込みのモデレーションと併せて、アプリケーション独自の会話のガードレールを適用してください。
使用量と正常終了の手順
session.usage.updated は、音声の累積利用時間を秒単位で報告します。
{
"type": "session.usage.updated",
"event_id": "event_usage_1",
"usage": { "seconds": 12 },
"context_window": { "usage_ratio": 0.42 }
}
音声の累積利用時間には、最新の usage.seconds を使います。たとえば、12 秒、続いて 15 秒という更新が届いた場合、利用時間は 15 秒です。バックエンドのトークン使用量は、ネストされた Responses の完了イベントから別途追跡してください。使用量の集計については、コストの最適化を参照してください。
正常終了の手順は次のとおりです。
- 保留中の関数実行結果の受け渡しやレスポンスの継続処理も含め、アプリケーションに必要な、Responses に委譲した処理を完了します。
session.closedのリスナーを、session.closeの送信前に登録します。session.closeを送信し、セッションへの新規処理の投入を停止します。保留中のセッションイベントをすべて受信するまで、WebSocket または WebRTC の接続、データチャネル、接続済みのサイドバンド受信側を稼働させておきます。- 最終的な
usage.seconds、reason、セッションのスナップショットをsession.closedから読み取ります。response.eventを通じてすでに受信した委譲処理の使用量も保持してください。 - このイベントを受信した後で、通信に使ったリソースと音声デバイスを解放します。終了処理が失敗した場合や、アプリケーションで設定したタイムアウトを超えた場合は、終了処理が未完了であることを報告してリソースを解放してください。
session.close を送信すると、キューに入っている Responses の処理がキャンセルされ、それ以降のコマンドは拒否されます。実行中のレスポンスは完了できますが、関数の結果を待っているレスポンスは、終了処理の開始後に処理を続けることができません。クライアント委譲を通じてアプリケーションが実行している処理を完了させるかキャンセルするかは、別途判断してください。
session.closed で終了処理の完了を確認し、最終設定のスナップショットを読み取ります。このイベントが届くまで接続を維持してください。ソケットが先に閉じた場合は、終了処理の完了を未確認として記録します。有効な session.closed の後に閉じた場合は、確認済みの結果を保持します。
最終イベントの reason は、セッションが終了した理由を示します。
| 理由 | 意味 |
|---|---|
close_requested |
アプリケーションが session.close を送信したか、通話終了のエンドポイントを呼び出しました。 |
expired |
セッションが制限時間に達しました。 |
content |
安全フィルターによってセッションが終了しました。 |
remote_hangup |
リモート側のプライマリ接続が正常に終了しました。 |
connection_lost |
プライマリ接続または上流の接続が予期せず切断されました。 |
接続の切断や安全フィルターの作動によってセッションが終了した場合でも、session.closed イベントが届けば終了処理の完了を確認できます。このイベントが届かない場合、最終的な使用量は未確認のままです。保存を有効にしたセッションでは録音の保存に時間がかかる場合があるため、アプリケーションのタイムアウトにはその時間を見込んでください。
接続失敗からの復旧
HTTP によるセッション作成でエラーが発生した場合、セッションは session.started に到達していません。開始時のエラーと実行中のセッションのエラーは別々に処理してください。実行中の接続が session.closed の受信前に失敗した場合は、最後に確認できた使用量を保持し、最終的な使用量を未確認として記録します。
保存が完了した録音があれば、元のセッションをフォークして新しいセッションで会話を続けます。なければ、保存しておいた関連する履歴を使い、代わりのセッションを作成します。再開前に、未完了のアクションをバックエンドで確認し、現在のタスクの状態を復元してください。前のセッションから遅れて届いた結果が新しい処理の結果を上書きしないよう、結果の送信先も更新します。