For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
主要導覽

WebSockets

透過 WebSockets 連接由伺服器管理的音訊串流。

選擇應用程式使用的 API。每個 API 都有各自的身分驗證方式、工作階段建立流程和事件規範。

將伺服器連接至 GPT-Live

當伺服器擷取音訊或為用戶端轉送音訊串流時,請使用主要 WebSocket 連線。這條連線會雙向傳輸音訊和 JSON 事件。請將專案 API 金鑰保存在該受信任的伺服器上。若要開發瀏覽器或行動應用程式,請先從 WebRTC 開始。

本指南介紹主要音訊連線。側頻連線可讓伺服器監看並控制現有的 Live 工作階段。Responses WebSocket 則將後端連接至 Responses API,以使用推理和工具。這兩種連線都無法取代主要音訊連線。

進行身分驗證並啟動工作階段

  1. 連接至 wss://api.openai.com/v1/live/sessions,不要附加查詢參數。使用 Authorization: Bearer $OPENAI_API_KEY 進行身分驗證,並加入範例所示的連線標頭。
  2. session.start 作為第一則訊息傳送。將模型、對話指示、音訊格式、語音和委派組態放在 session 物件中。
  3. 等收到 session.started 後,再傳送音訊或應用程式指令。該事件包含解析後的工作階段組態和工作階段 ID。

以下範例使用 Marin、24 kHz 的 PCM16 音訊,以及支援網頁搜尋的 Responses 後端。請保持對話指示簡短。如需設定後端指示、工具和工具權限,請參閱委派與工具

使用 SDK 串流傳輸音訊

若使用 Node.js,請以 npm install openai ws 安裝 openaiws,並將 JavaScript 範例儲存為 client.mjs。若在 macOS 或 Linux 上使用 Python,請安裝 openai[realtime],並將 Python 範例儲存為 client.py。在伺服器環境中設定 OPENAI_API_KEY。這些範例需要支援 Live 的 SDK 版本。範例會從標準輸入讀取 24 kHz 的原始單聲道 PCM16 音訊,並將傳回的音訊以相同格式寫入標準輸出。請將這些串流連接至應用程式的音訊擷取與播放功能。記錄和轉錄事件會寫入標準錯誤輸出,以免破壞音訊串流。

import OpenAI from "openai";
import { LiveWS } from "openai/resources/live/ws";

// stdin and stdout carry raw mono PCM16 audio at 24 kHz, not WAV files.
// Supply microphone bytes continuously and play stdout in the same format.
process.stdin.pause();
const ws = new LiveWS(new OpenAI());
let started = false;
let closing = false;
let finalized = false;
let pendingByte = Buffer.alloc(0);

let closeTimeout;

ws.socket.on("open", () => {
  ws.send({
    type: "session.start",
    event_id: "event_start",
    session: {
      model: "gpt-live-1",
      instructions:
        "Be concise. Delegate requests needing current information to the backend, which can search the web.",
      audio: {
        format: { type: "audio/pcm", rate: 24000 },
        output: { voice: "marin" },
      },
      delegation: {
        type: "responses",
        responses: {
          model: "gpt-5.6-luna",
          tools: [{ type: "web_search" }],
          tool_choice: "auto",
        },
      },
    },
  });
});

process.stdin.on("data", (chunk) => {
  if (!started || closing || ws.socket.readyState !== 1) return;
  const bytes = Buffer.concat([pendingByte, chunk]);
  const completeLength = bytes.length - (bytes.length % 2);
  pendingByte = bytes.subarray(completeLength);
  if (completeLength) {
    ws.send({
      type: "session.input_audio.append",
      audio: bytes.subarray(0, completeLength).toString("base64"),
    });
  }
});

// Register the final-event handler before any close command can be sent.
ws.on("event", (event) => {
  if (event.type === "session.started") {
    started = true;
    console.error("Session ready", event.session.id);
    process.stdin.resume();
  } else if (event.type === "session.output_audio.delta") {
    process.stdout.write(Buffer.from(event.delta, "base64"));
  } else if (event.type === "session.closed") {
    finalized = true;
    clearTimeout(closeTimeout);
    process.stdin.pause();
    console.error("Final session usage", event.usage);
    ws.close();
  } else {
    // Includes transcript deltas and nested response.event usage.
    console.error(JSON.stringify(event));
  }
});

process.on("SIGINT", () => {
  if (closing) return;
  if (!started || ws.socket.readyState !== 1) {
    ws.socket.platformSocket.terminate();
    return;
  }
  closing = true;
  process.stdin.pause();
  ws.send({ type: "session.close" });
  closeTimeout = setTimeout(() => {
    console.error("Incomplete finalization: session.closed was not received");
    process.exitCode = 1;
    ws.socket.platformSocket.terminate();
  }, 15_000);
});
ws.on("error", (error) => {
  console.error(error.message);
  process.exitCode = 1;
});
ws.socket.on("close", () => {
  clearTimeout(closeTimeout);
  process.stdin.pause();
  if (!finalized) {
    console.error("Connection closed without final session usage");
    process.exitCode = 1;
  }
});

連接音訊來源和播放器後,執行 node client.mjspython client.py。出現 Session ready 後,請依錄音取樣率持續提供麥克風串流。透過管線一次傳入整個檔案,無法模擬即時麥克風。音訊來源到達 EOF 並不會結束對話。請向處理程序傳送 SIGINT,要求正常關閉。

此範例會連接音訊串流;應用程式則負責擷取、緩衝、播放,以及必要時的重新取樣。在評估模型行為之前,請先使用自己的裝置和網路測試這些功能。

選擇音訊格式

在啟動時設定 session.audio.format。輸入和輸出共用同一種格式,且在工作階段期間無法變更。

  • {"type":"audio/pcm","rate":24000}:24 kHz、單聲道、帶正負號的 16 位元小端序 PCM;這是預設格式。
  • {"type":"audio/pcm","rate":16000}:16 kHz、單聲道、帶正負號的 16 位元小端序 PCM。
  • {"type":"audio/pcmu","rate":8000}:8 kHz 的 G.711 μ-law,每個樣本占一個位元組。
  • {"type":"audio/pcma","rate":8000}:8 kHz 的 G.711 A-law,每個樣本占一個位元組。

請對不含 WAV 或其他容器標頭的原始位元組進行 Base64 編碼。PCM 區塊必須包含完整的 16 位元樣本,因此位元組長度必須是偶數。範例會將末尾剩餘的一個位元組保留至下一個輸入區塊。除此之外,區塊邊界可以任意劃分,只要串流保持連續且順序正確即可。

當音訊取樣率與設定的取樣率不同時,請重新取樣。變更格式設定不會轉換輸入位元組。若要將範例調整為使用 G.711,請轉送各區塊的編碼位元組,不要套用 PCM 專用的雙位元組對齊邏輯,並將輸出播放器設定為使用相同的編解碼器。格式相符的 G.711 串流可以直接傳遞,無須轉換為 PCM。如需連接電話通話,請參閱電話整合

傳送與接收事件

將每個事件以 JSON 文字訊息傳送。音訊會以 base64 編碼放在這些訊息中傳輸。

  • 傳送音訊: 傳送 session.input_audio.append,並在 audio 中放入經 base64 編碼的原始位元組。附加音訊不會收到確認回覆。
  • 接收音訊: 解碼每個 session.output_audio.delta 事件中的 delta,並依序將音訊加入播放佇列,以設定的格式播放。
  • 接收轉錄文字:session.input_transcript.deltasession.output_transcript.deltadelta 的文字附加至對應的轉錄文字。
  • 接收後端事件: 使用 Responses 委派時,請處理每個 response.event 封套中巢狀的 event
  • 處理錯誤: 根據 error 事件處理遭拒的指令和工作階段錯誤。若事件中包含 error.client_event_id,可用它識別對應的指令。

輸出音訊事件沒有時間資訊欄位,GPT-Live 也不會發出 output-audio-done 事件。請追蹤播放佇列,以掌握已接收的音訊中有哪些已經播放。轉錄文字的時間戳記描述的是工作階段時間軸上的區間,並不表示音訊播放完畢。後端回應完成,也不代表助理已經說完話。

GPT-Live 會在音訊串流傳輸期間管理聆聽和說話的時機。它不使用 Realtime 透過提交輸入緩衝區和 response.create 來進行的語音輪次循環。在 Live 中,response.create 用於啟動或繼續已委派的後端工作。如需瞭解此工作流程,請參閱委派與工具

設定進行中的工作階段

Live 模型、初始對話指示、音訊格式、語音和委派模式都在啟動時固定。請使用 session.update 調整現有委派模式支援的設定;未指定的設定會保留目前的值。更新成功時會傳回 session.updated,其中包含解析後的工作階段組態。

使用 session.instructions.append 新增對話指示,並使用 session.input_audio.mutesession.input_audio.unmute 控制傳入的音訊。將輸入靜音不會取消後端工作,也不會停止已生成的語音。如需瞭解上下文更新、轉錄文字、輸入控制和用量,請參閱管理工作階段

關閉工作階段

對話結束時,傳送 session.close。請先註冊 session.closed 監聽器,持續接收直到該事件到達,再釋放連線。範例最多等待 15 秒;如果始終未收到終止事件,便會回報結束程序未完成。

保留 session.closed 中的最終語音用量,以及已收到的後端用量事件。語音時長更新是累計值的快照,請勿將它們相加。若在收到 session.closed 之前發生傳輸失敗或逾時,最終用量便無法確認。如需瞭解完整生命週期,請參閱管理工作階段