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

实时对话

了解如何管理实时语音到语音对话。

通过 WebRTCWebSocket 连接到 Realtime API 后,您就可以调用实时模型(例如 gpt-realtime-2.1)进行语音到语音对话。为此,您需要 发送客户端事件 来发起操作,并 监听服务器事件 ,以响应 Realtime API 执行的操作。

本指南将介绍使用音频和文本生成、图像输入、函数调用等模型能力所需的事件流程,以及如何理解实时会话的状态。

如果您不需要与模型对话,也就是说不期望模型返回任何响应, 可以在转录 模式下使用 Realtime API。

实时语音到语音会话

实时会话是模型与已连接的客户端之间的有状态交互。会话的主要组成部分包括:

  • 会话 对象,用于控制交互参数,例如所用的模型、生成输出时使用的音色以及其他配置。
  • 对话,表示当前会话期间产生的用户输入条目和模型输出条目。
  • 响应,即模型生成并添加到对话中的音频或文本条目。

输入音频缓冲区与 WebSockets

如果您使用 WebRTC,向模型发送音频和接收模型音频所需的大部分媒体处理工作都由 WebRTC API 协助完成。


如果您使用 WebSockets 处理音频,则需要通过包含 base64 编码音频的 JSON 事件向服务器发送音频,手动与 输入音频缓冲区 交互。

这些组件共同构成一个实时会话。您将使用客户端事件更新会话状态,并监听服务器事件,以便对会话中的状态变化作出响应。

实时会话状态示意图

会话生命周期事件

通过 WebRTCWebSockets 启动会话后,服务器会发送 session.created 事件,表示会话已就绪。在客户端,您可以通过 session.update 事件更新当前会话配置。大多数会话属性都可以随时更新,但模型在会话中首次以音频响应后,就不能再更改用于音频输出的 voice。实时会话的最长持续时间为 60 分钟

以下示例展示了如何使用 session.update 客户端事件更新会话。有关通过这些通道发送客户端事件的更多信息,请参阅 WebRTCWebSocket 指南。

更新模型在此会话中使用的系统指令
const event = {
  type: "session.update",
  session: {
    type: "realtime",
    model: "gpt-realtime-2.1",
    // Lock the output to audio (set to ["text"] if you want text without audio)
    output_modalities: ["audio"],
    audio: {
      input: {
        format: {
          type: "audio/pcm",
          rate: 24000,
        },
        turn_detection: {
          type: "semantic_vad",
        },
      },
      output: {
        format: {
          type: "audio/pcm",
        },
        voice: "marin",
      },
    },
    // Use a server-stored prompt by ID. Optionally pin a version and pass variables.
    prompt: {
      id: "pmpt_123", // your stored prompt ID
      version: "89", // optional: pin a specific version
      variables: {
        city: "Paris", // example variable used by your prompt
      },
    },
    // You can still set direct session fields; these override prompt fields if they overlap:
    instructions:
      "Speak clearly and briefly. Confirm understanding before taking actions.",
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

会话更新后,服务器会发出 session.updated 事件,其中包含会话的新状态。

相关客户端事件 相关服务器事件

session.update

session.created

session.updated

文本输入和输出

要使用实时模型生成文本,您可以向当前对话添加文本输入,请求模型生成响应,并监听服务器发送的事件以了解模型响应的生成进度。要生成文本,必须将会话配置为支持 text 模态(默认已启用)。

使用 conversation.item.create 客户端事件创建新的文本对话条目。这类似于在 REST API 中通过 Chat Completions 发送用户消息(提示)

创建包含用户输入的对话条目
const event = {
  type: "conversation.item.create",
  item: {
    type: "message",
    role: "user",
    content: [
      {
        type: "input_text",
        text: "What Prince album sold the most copies?",
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

将用户消息添加到对话后,发送 response.create 事件以触发模型响应。如果当前会话同时启用了音频和文本,模型的响应将同时包含音频和文本内容。如果您只想生成文本,可以在发送 response.create 客户端事件时指定,如下所示。

生成纯文本响应
const event = {
  type: "response.create",
  response: {
    output_modalities: ["text"],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

响应完全生成后,服务器会发出 response.done 事件。此事件将包含模型生成的完整文本,如下所示。

监听 response.done 以查看最终结果
function handleEvent(message) {
  const data = "data" in message ? message.data : message.toString();
  const serverEvent = JSON.parse(data);
  if (serverEvent.type === "response.done") {
    console.log(serverEvent.response.output[0]);
  }
}

// Listen for server messages (WebRTC)
dataChannel.addEventListener("message", handleEvent);

// Listen for server messages (WebSocket)
// ws.on("message", handleEvent);

在模型生成响应的过程中,服务器会发出多个生命周期事件。您可以监听这些事件,例如 response.output_text.delta,在响应生成期间向用户提供实时反馈。服务器发出的完整事件列表见下方的 相关服务器事件。这些事件大致按发出顺序排列,同时列出了与文本生成相关的客户端事件。

相关客户端事件 相关服务器事件

conversation.item.create

response.create

conversation.item.added

conversation.item.done

response.created

response.output_item.added

response.content_part.added

response.output_text.delta

response.output_text.done

response.content_part.done

response.output_item.done

response.done

rate_limits.updated

音频输入和输出

Realtime API 最强大的功能之一是可以直接与模型进行语音交互,无需经过文本转语音或语音转文本的中间步骤。这可以降低语音界面的延迟,同时让模型获得更多语音输入中的语气和语调信息。

音色选项

您可以为实时会话配置多种内置音色之一,用于生成音频输出。在创建会话时(或在 response.create 中),您可以设置 voice 来控制模型的声音。目前可选的音色包括 alloyashballadcoralechosageshimmerversemarincedar。一旦模型在会话中输出了音频,就无法再修改该会话的 voice。为获得最佳质量,我们建议使用 marincedar

使用 WebRTC 处理音频

如果您使用 WebRTC 连接到 Realtime API,Realtime API 就会与您的客户端建立对等连接。模型的音频输出以远程媒体流的形式传送到客户端。输入模型的音频通过音频设备采集(getUserMedia),媒体流则以轨道的形式添加到对等连接中。

WebRTC 连接指南中的示例代码展示了如何使用浏览器 API 对本地和远程音频进行基本配置:

// Create a peer connection
const pc = new RTCPeerConnection();

// Set up to play remote audio from the model
const audioEl = document.createElement("audio");
audioEl.autoplay = true;
pc.ontrack = (e) => (audioEl.srcObject = e.streams[0]);

// Add local audio track for microphone input in the browser
const ms = await navigator.mediaDevices.getUserMedia({
  audio: true,
});
pc.addTrack(ms.getTracks()[0]);

上述代码片段可以实现与 Realtime API 的交互,您还可以在此基础上实现更多功能。如需了解不同类型用户界面的更多示例,请查看 WebRTC 示例代码仓库。您也可以在此查看这些示例的在线演示。

在浏览器中使用媒体捕获和流功能,您可以将麦克风静音或取消静音、选择用于采集输入的设备等。

WebRTC 中的音频客户端和服务器事件

默认情况下,WebRTC 客户端在发送音频输入之前,无需向 Realtime API 发送任何客户端事件。将本地音轨添加到对等连接后,您的用户就可以直接开始说话了!

不过,当音频通过对等连接在客户端与服务器之间双向传输时,WebRTC 客户端仍会收到服务器发送的多个生命周期事件。例如:

通过 WebRTC API 操作媒体流,可能就能满足您所有的控制需求。不过,有时您可能需要使用更底层的接口来处理音频输入和输出。请参阅下方的 WebSockets 部分,了解更多信息,以及精细处理音频输入所需的事件列表。

使用 WebSockets 处理音频

通过 WebSocket 发送和接收音频时,您需要完成更多工作,才能从客户端发送媒体并接收服务器返回的媒体。下表介绍了 WebSocket 会话中通过 WebSocket 收发音频所需的事件流程。

以下事件按生命周期顺序列出,但某些事件(例如 delta 事件)可能会并发发生。

生命周期阶段 客户端事件 服务器事件
会话初始化

session.update

session.created

session.updated

用户音频输入

conversation.item.create


  (发送完整的音频消息)

input_audio_buffer.append


  (分块流式传输音频)

input_audio_buffer.commit


  (禁用 VAD 时使用)

response.create


  (禁用 VAD 时使用)

input_audio_buffer.speech_started

input_audio_buffer.speech_stopped

input_audio_buffer.committed

服务器音频输出

input_audio_buffer.clear


  (禁用 VAD 时使用)

conversation.item.added

conversation.item.done

response.created

response.output_item.added

response.content_part.added

response.output_audio.delta

response.output_audio.done

response.output_audio_transcript.delta

response.output_audio_transcript.done

response.output_text.delta

response.output_text.done

response.content_part.done

response.output_item.done

response.done

rate_limits.updated

向服务器流式传输音频输入

要向服务器流式传输音频输入,您可以使用 input_audio_buffer.append 客户端事件。此事件要求您通过套接字向 Realtime API 分块发送 经过 Base64 编码的音频字节 。每个数据块的大小不得超过 15 MB。

您可以为整个会话统一配置输入数据块的格式,也可以针对每个响应单独配置。

向对话追加音频输入字节
import fs from "fs";
import decodeAudio from "audio-decode";

// Converts Float32Array of audio data to PCM16 ArrayBuffer
function floatTo16BitPCM(float32Array) {
  const buffer = new ArrayBuffer(float32Array.length * 2);
  const view = new DataView(buffer);
  let offset = 0;
  for (let i = 0; i < float32Array.length; i++, offset += 2) {
    let s = Math.max(-1, Math.min(1, float32Array[i]));
    view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
  }
  return buffer;
}

// Converts a Float32Array to base64-encoded PCM16 data
function base64EncodeAudio(float32Array) {
  const arrayBuffer = floatTo16BitPCM(float32Array);
  let binary = "";
  let bytes = new Uint8Array(arrayBuffer);
  const chunkSize = 0x8000; // 32KB chunk size
  for (let i = 0; i < bytes.length; i += chunkSize) {
    let chunk = bytes.subarray(i, i + chunkSize);
    binary += String.fromCharCode(...chunk);
  }
  return btoa(binary);
}

// Fills the audio buffer with the contents of three files,
// then asks the model to generate a response.
const files = [
  "fixtures/sample1.wav",
  "fixtures/sample2.wav",
  "fixtures/sample3.wav",
];

for (const filename of files) {
  const audioFile = fs.readFileSync(filename);
  const audioBuffer = await decodeAudio(audioFile);
  const channelData = audioBuffer.channelData[0];
  const base64Chunk = base64EncodeAudio(channelData);
  ws.send(
    JSON.stringify({
      type: "input_audio_buffer.append",
      audio: base64Chunk,
    })
  );
}

ws.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
ws.send(JSON.stringify({ type: "response.create" }));

发送完整的音频消息

您也可以使用完整的录音创建对话消息。使用 conversation.item.create 客户端事件创建包含 input_audio 内容的消息。

创建包含完整音频输入的对话项
const fullAudio = "<a base64-encoded string of audio bytes>";

const event = {
  type: "conversation.item.create",
  item: {
    type: "message",
    role: "user",
    content: [
      {
        type: "input_audio",
        audio: fullAudio,
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

处理通过 WebSocket 接收的音频输出

要在网页浏览器等客户端上播放输出音频,我们建议使用 WebRTC,而非 WebSockets。在网络状况不稳定时,WebRTC 能更可靠地向客户端设备发送媒体。

但如果您要在服务器间通信的应用中使用 WebSocket 处理音频输出,就需要监听 response.output_audio.delta 事件,其中包含模型生成的、经过 Base64 编码的音频数据块。您需要缓冲这些数据块并将其写入文件,或者立即将其流式传输到其他目标,例如 Twilio 电话通话

请注意,response.output_audio.doneresponse.done 事件实际上不包含音频数据,只包含音频内容的转录文本。要获取实际的字节数据,您需要监听 response.output_audio.delta 事件。

您可以为整个会话统一配置输出数据块的格式,也可以针对每个响应单独配置。

监听 response.output_audio.delta 事件
function handleEvent(message) {
  const serverEvent = JSON.parse(message.toString());
  if (serverEvent.type === "response.output_audio.delta") {
    // Access Base64-encoded audio chunks
    // console.log(serverEvent.delta);
  }
}

// Listen for server messages (WebSocket)
ws.on("message", handleEvent);

图像输入

gpt-realtime-2gpt-realtime 也支持图像输入。您可以将图像作为用户消息的一个内容部分附加到消息中,模型在响应时便可结合图像中的内容。

向对话添加图像
const base64Image = "<a base64-encoded string of image bytes>";

const event = {
  type: "conversation.item.create",
  item: {
    type: "message",
    role: "user",
    content: [
      {
        type: "input_image",
        image_url: `data:image/{format};base64,${base64Image}`,
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

语音活动检测

实时会话默认启用 语音活动检测(VAD) ,这意味着 API 会判断用户何时开始或停止说话,并自动响应。

有关如何配置 VAD 的更多信息,请参阅我们的语音活动检测指南。

禁用 VAD

您可以通过 session.update 客户端事件将 turn_detection 设置为 null,以禁用 VAD。这适用于需要精细控制音频输入的界面,例如按键通话界面。

禁用 VAD 后,客户端必须手动发送一些额外的客户端事件,才能触发音频响应:

保留 VAD,但禁用自动响应

如果您希望保持 VAD 模式启用,同时手动决定何时生成响应,可以通过 session.update 客户端事件将 turn_detection.interrupt_responseturn_detection.create_response 设置为 false。这样会保留 VAD 的所有行为,但不会自动创建新的响应。客户端可以通过 response.create 事件手动触发响应。

这适用于内容审核、输入验证或 RAG 模式等场景,前提是您可以接受稍高的交互延迟,以换取对输入的控制。

在默认对话之外创建响应

默认情况下,会话期间生成的所有响应都会添加到该会话的对话状态(即“默认对话”)中。不过,您可能希望在会话的默认对话上下文之外生成模型响应,或并发生成多个响应。您也可能希望更精细地控制模型生成响应时参考哪些对话条目(例如,仅参考最近 N 轮对话)。

使用 response.create 客户端事件创建响应时,将 response.conversation 字段设置为字符串 none,即可生成不会添加到默认对话状态中的“带外”响应。

创建带外响应时,您可能还需要一种方式来识别服务器发送的哪些事件与此响应有关。您可以为模型响应提供 metadata,以便识别哪个响应是针对该客户端发送的事件生成的。

创建带外模型响应
const prompt = `
Analyze the conversation so far. If it is related to support, output
"support". If it is related to sales, output "sales".
`;

const event = {
  type: "response.create",
  response: {
    // Setting to "none" indicates the response is out of band
    // and will not be added to the default conversation
    conversation: "none",

    // Set metadata to help identify responses sent back from the model
    metadata: { topic: "classification" },

    // Set any other available response fields
    output_modalities: ["text"],
    instructions: prompt,
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

现在,监听 response.done 服务器事件时,您就可以识别带外响应的结果。

创建带外模型响应
function handleEvent(message) {
  const data = "data" in message ? message.data : message.toString();
  const serverEvent = JSON.parse(data);
  if (
    serverEvent.type === "response.done" &&
    serverEvent.response.metadata?.topic === "classification"
  ) {
    // this server event pertained to our OOB model response
    console.log(serverEvent.response.output[0]);
  }
}

// Listen for server messages (WebRTC)
dataChannel.addEventListener("message", handleEvent);

// Listen for server messages (WebSocket)
// ws.on("message", handleEvent);

为响应创建自定义上下文

您还可以在默认或当前对话之外构建自定义上下文,供模型生成响应时使用。这可以通过 response.create 客户端事件中的 input 数组实现。您可以使用新的输入,也可以通过 ID 引用对话中已有的输入项。

监听使用自定义上下文的带外模型响应
const event = {
  type: "response.create",
  response: {
    conversation: "none",
    metadata: { topic: "pizza" },
    output_modalities: ["text"],

    // Create a custom input array for this request with whatever context
    // is appropriate
    input: [
      // potentially include existing conversation items:
      {
        type: "item_reference",
        id: "some_conversation_item_id",
      },
      {
        type: "message",
        role: "user",
        content: [
          {
            type: "input_text",
            text: "Is it okay to put pineapple on pizza?",
          },
        ],
      },
    ],
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

创建无上下文的响应

您也可以将响应插入默认对话,同时忽略所有其他指令和上下文。只需将 input 设为空数组。

将无上下文的模型响应插入默认对话
const prompt = `
Say exactly the following:
I'm a little teapot, short and stout!
This is my handle, this is my spout!
`;

const event = {
  type: "response.create",
  response: {
    // An empty input array removes existing context
    input: [],
    instructions: prompt,
  },
};

// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));

函数调用

Realtime 模型还支持 函数调用,让您能够执行自定义代码来扩展模型的能力。其基本工作流程如下:

  1. 更新会话创建响应时,您可以指定可供模型调用的函数列表。
  2. 如果模型在处理输入时判断需要调用函数,就会向对话中添加表示函数调用参数的条目。
  3. 当客户端检测到包含函数调用参数的对话条目时,就会使用这些参数执行自定义代码。
  4. 自定义代码执行完毕后,客户端会创建包含函数调用输出的新对话条目,并请求模型生成响应。

下面添加一个可调用的函数,为模型用户提供今日星座运势,以此展示实际工作流程。我们将展示需要发送的客户端事件对象的结构,以及服务器相应发送的事件。

配置可调用的函数

首先,我们必须向模型提供一组函数,供其根据用户输入选择调用。可用函数既可以在会话级别配置,也可以为单个响应配置。

以下是 session.update 客户端事件的负载示例,用于配置星座运势生成函数。该函数接受一个参数,即要生成运势的星座:

session.update

{
  "type": "session.update",
  "session": {
    "tools": [
      {
        "type": "function",
        "name": "generate_horoscope",
        "description": "Give today's horoscope for an astrological sign.",
        "parameters": {
          "type": "object",
          "properties": {
            "sign": {
              "type": "string",
              "description": "The sign for the horoscope.",
              "enum": [
                "Aries",
                "Taurus",
                "Gemini",
                "Cancer",
                "Leo",
                "Virgo",
                "Libra",
                "Scorpio",
                "Sagittarius",
                "Capricorn",
                "Aquarius",
                "Pisces"
              ]
            }
          },
          "required": ["sign"]
        }
      }
    ],
    "tool_choice": "auto"
  }
}

函数和参数的 description 字段可帮助模型决定是否调用该函数,以及为每个参数提供什么数据。如果模型收到的输入表明用户想了解自己的星座运势,就会调用此函数并传入 sign 参数。

检测模型何时想要调用函数

模型可能会根据收到的输入,决定调用函数以生成最佳响应。假设我们的应用通过 conversation.item.create 事件添加以下对话条目,然后创建响应:

{
  "type": "conversation.item.create",
  "item": {
    "type": "message",
    "role": "user",
    "content": [
      {
        "type": "input_text",
        "text": "What is my horoscope? I am an aquarius."
      }
    ]
  }
}

接着发送 response.create 客户端事件来生成响应:

{
  "type": "response.create"
}

模型不会立即返回文本或音频响应,而是会生成包含参数的响应,这些参数应传递给开发者应用中的函数。您可以监听 response.function_call_arguments.delta 服务器事件,获取函数调用参数的实时更新;response.done 也会包含调用函数所需的完整数据。

response.done

{
    "type": "response.done",
    "event_id": "event_AeqLA8iR6FK20L4XZs2P6",
    "response": {
        "object": "realtime.response",
        "id": "resp_AeqL8XwMUOri9OhcQJIu9",
        "status": "completed",
        "status_details": null,
        "output": [
            {
                "object": "realtime.item",
                "id": "item_AeqL8gmRWDn9bIsUM2T35",
                "type": "function_call",
                "status": "completed",
                "name": "generate_horoscope",
                "call_id": "call_sHlR7iaFwQ2YQOqm",
                "arguments": "{\"sign\":\"Aquarius\"}"
            }
        ],
        ...
    }
}

通过服务器发送的 JSON,我们可以检测到模型想要调用自定义函数:

属性在函数调用中的作用
response.output[0].type设为 function_call 时,表示此响应包含调用指定名称的函数所需的参数。
response.output[0].name要调用的已配置函数的名称,此处为 generate_horoscope
response.output[0].arguments包含函数参数的 JSON 字符串。在本例中为 "{\"sign\":\"Aquarius\"}"
response.output[0].call_id系统为此次函数调用生成的 ID。 您需要使用此 ID 将函数调用结果传回模型

有了这些信息,我们就可以在应用中执行代码来生成星座运势,然后将结果传回模型,以便模型生成响应。

将函数调用结果提供给模型

收到模型返回的包含函数调用参数的响应后,您的应用就可以执行代码来完成该函数调用。具体操作可以按您的需要实现,例如调用外部 API 或访问数据库。

准备好将自定义代码的结果提供给模型后,您可以通过 conversation.item.create 客户端事件,创建包含该结果的新对话条目。

{
  "type": "conversation.item.create",
  "item": {
    "type": "function_call_output",
    "call_id": "call_sHlR7iaFwQ2YQOqm",
    "output": "{\"horoscope\": \"You will soon meet a new friend.\"}"
  }
}
  • 对话条目的类型为 function_call_output
  • item.call_id 与上文 response.done 事件返回的 ID 相同。
  • item.output 是包含函数调用结果的 JSON 字符串。

添加包含函数调用结果的对话条目后,我们再次从客户端发送 response.create 事件。这会触发模型使用函数调用返回的数据生成响应。

{
  "type": "response.create"
}

错误处理

会话期间,每当服务器遇到错误时,就会发送 error 事件。有时,这些错误可以追溯到您的应用发送的某个客户端事件。

在 HTTP 请求和响应中,响应与客户端请求隐式关联。而在这里,我们需要通过客户端事件的 event_id 属性,确定是哪个事件触发了服务器错误。下面的代码演示了这一方法,其中客户端尝试发送一种不受支持的事件类型。

const event = {
  event_id: "my_awesome_event",
  type: "scooby.dooby.doo",
};

dataChannel.send(JSON.stringify(event));

客户端发送的这个未能成功处理的事件会触发如下错误事件:

{
  "type": "invalid_request_error",
  "code": "invalid_value",
  "message": "Invalid value: 'scooby.dooby.doo' ...",
  "param": "type",
  "event_id": "my_awesome_event"
}

打断与截断

在许多语音应用中,用户可以在模型说话时打断它。启用 VAD 后,Realtime API 会处理这类打断:检测到用户说话时,取消正在进行的响应,并开始新的响应。不过,在这种情况下,您需要让模型知道它在哪里被打断,以便自然地继续对话(例如,用户问“刚才最后说的是什么?”时)。我们将这一操作称为 截断 模型的上一条响应,即从对话中移除模型上一条响应中尚未播放的部分。

在 WebRTC 和 SIP 连接中,服务器管理输出音频缓冲区,因此知道任一时刻音频已播放到哪里。用户打断时,服务器会自动截断尚未播放的音频。

使用 WebSocket 连接时,音频播放由客户端管理,因此客户端必须停止播放并处理截断。具体流程如下:

  1. 客户端监听服务器发送的新 input_audio_buffer.speech_started 事件,该事件表示用户已开始说话。服务器会自动取消正在进行的模型响应,并发送 response.cancelled 事件。
  2. 客户端检测到此事件后,应立即停止播放当前正在播放的模型音频,并记录上一条音频响应在被打断前已播放到哪里。
  3. 客户端应发送 conversation.item.truncate 事件,从对话中移除模型上一条响应尚未播放的部分。

示例如下:

{
    "type": "conversation.item.truncate",
    "item_id": "item_1234", # this is the item ID of the model's last response
    "content_index": 0,
    "audio_end_ms": 1500 # truncate audio after 1.5 seconds
}

转录文本也会一并截断吗?Realtime 模型没有足够的信息来精确对齐转录文本和音频,因此 conversation.item.truncate 会在指定位置截断音频,并移除未播放部分的转录文本。这解决了移除未播放音频的问题,但不会提供截断后的转录文本。

按住说话

Realtime API 默认使用语音活动检测(VAD),这意味着音频输入会触发模型响应。您也可以禁用 VAD,并在应用层控制何时向模型发送音频输入,从而实现按住说话的交互方式。例如,按住空格键采集音频,松开时触发响应。对于某些应用,这种方式的效果出乎意料地好:用户可以掌控交互过程,也能避免 VAD 检测失败,而且由于无需等待 VAD 超时,交互感觉更灵敏。

通过 WebSockets 和 WebRTC 实现按住说话的方式略有不同。在 Realtime API 的 WebSocket 连接中,所有事件都通过同一通道按相同的顺序发送;而 WebRTC 连接则使用独立的通道分别传输音频和控制事件。

WebSockets

要通过 WebSocket 连接实现按住说话,您需要让客户端停止音频播放、处理打断并发起新的响应。详细步骤如下:

  1. session.update 事件中设置 "turn_detection": null,以关闭 VAD。
  2. 按下时,在客户端开始录制音频。
    1. 如果模型有正在进行的响应,请发送 response.cancel 事件将其取消。
    2. 如果模型的输出音频仍在播放,请立即停止播放,并发送 conversation.item.truncate 事件,从对话中移除所有尚未播放的音频。
  3. 松开时,发送包含音频的 input_audio_buffer.append 消息,将新音频放入输入缓冲区。
  4. 发送 input_audio_buffer.commit 事件,提交已写入输入缓冲区的音频,并启动输入转录(如果已启用)。
  5. 然后通过 response.create 事件触发响应。

WebRTC 和 SIP

使用 WebRTC 实现按住说话的方式类似,但必须显式清空输入音频缓冲区。具体步骤如下:

  1. session.update 事件中设置 "turn_detection": null,以关闭 VAD。
  2. 按下按钮时,发送 input_audio_buffer.clear 事件,清空之前的所有音频输入。
    1. 如果模型有正在进行的响应,请发送 response.cancel 事件将其取消。
    2. 如果模型的输出音频仍在播放,请发送 output_audio_buffer.clear 事件,清除尚未播放的音频,同时也会截断对话。
  3. 松开按钮时,发送 input_audio_buffer.commit 事件,提交已写入输入缓冲区的音频,并启动输入音频转录(如果已启用)。
  4. 然后通过 response.create 事件触发响应。