OpenAI API - Realtime 实时

news2025/4/5 0:50:50

文章目录

  • 实时 API(Beta)
    • 使用实时API入门
      • 示例应用
      • 合作伙伴集成
    • 用例
    • 通过 WebRTC 连接
      • 概述
      • 连接详情
      • 创建一个临时token
      • 发送和接收事件
    • 使用 WebSockets 连接
      • 概述
      • 连接详情
  • 实时对话Beta
    • 实时语音到语音会话
    • 会话生命周期事件
    • 文本输入和输出
    • 音频输入和输出
      • 语音选项
      • 使用 WebRTC 处理音频
      • WebRTC 中的客户端和服务器音频事件
      • 使用 WebSockets 处理音频
      • 将音频输入流式传输到服务器
      • 发送完整音频消息
      • 使用WebSocket处理音频输出
    • 语音活动检测
      • 禁用 VAD
      • 保持VAD模式,但禁用自动响应
    • 在默认对话之外创建响应
      • 为响应创建自定义上下文
      • 使用无上下文创建响应
    • 函数调用
      • 配置可调用的函数
      • 检测模型何时想要调用函数
      • 向模型提供函数调用的结果
    • 错误处理
  • 实时转录Beta
    • 实时转录会话
    • 处理转录
    • 语音活动检测
    • 额外配置
      • 噪声降低
      • 使用 logprobs
  • 语音活动检测 (VAD)Beta
    • 概述
    • 服务器VAD
    • 语义语音活动检测(Semantic VAD)


实时 API(Beta)

使用实时 API 构建低延迟、多模态的体验。

https://platform.openai.com/docs/guides/realtime


The OpenAI 实时 API 允许低延迟、多模态交互,包括语音到语音的对话体验和实时转录。
此API与原生的多模态模型(如 GPT-4o 和 GPT-4o mini)兼容,提供实时文本和音频处理、函数调用和语音生成等功能,以及最新的转录模型 GPT-4o Transcribe 和 GPT-4o mini Transcribe。


使用实时API入门

您可以通过两种方式连接到实时API:

  • 使用 WebRTC,它非常适合客户端应用程序(例如,一个网页应用)
  • 使用 WebSockets,这对于服务器到服务器的应用程序非常出色(例如,如果你的后端或你正在为电话构建语音代理)

开始探索下面的示例和合作伙伴集成,或者学习如何使用最适合您用例的方法连接到实时API。


示例应用

查看以下示例应用之一,以查看实时API的实际应用。


实时控制台
要快速开始,下载并配置实时控制台演示。查看事件来回流动,并检查它们的内容。学习如何通过函数调用执行自定义逻辑。
https://github.com/openai/openai-realtime-console


实时太阳系演示
一个使用WebRTC集成的实时API演示,通过功能调用通过语音导航太阳系。
https://github.com/openai/openai-realtime-solar-system


Twilio 集成演示
一个结合Realtime API和Twilio构建AI电话助手的演示。
https://github.com/openai/openai-realtime-twilio-demo


实时 API 代理演示
A 演示实时 API 语音代理之间的交接以及推理模型验证。
https://github.com/openai/openai-realtime-agents


合作伙伴集成

查看这些合作伙伴集成,它们在前端应用程序和电话用例中使用实时API。

LiveKit集成指南

如何使用实时API与LiveKit的WebRTC基础设施。

https://docs.livekit.io/agents/openai/overview/


Twilio集成指南
使用 Twilio 强大的语音 API 构建实时应用程序。
https://www.twilio.com/en-us/blog/twilio-openai-realtime-api-launch-integration


Agora 集成快速入门
如何将 Agora 的实时音频通信功能与 Realtime API 集成。
https://docs.agora.io/en/open-ai-integration/get-started/quickstart


Pipecat集成指南
创建使用 OpenAI 音频模型和 Pipecat 协奏框架的语音代理。
https://docs.pipecat.ai/guides/features/openai-audio-models-and-apis


客户端工具调用
使用 Cloudflare Workers 构建,一个展示客户端工具调用的示例应用程序。也请查看YouTube 上的教程。
https://github.com/craigsdennis/talk-to-javascript-openai-workers


用例

Realtime API 最常见的用例是构建实时、语音到语音、对话式的体验。这对于构建 语音代理 和其他带语音功能的应用程序来说非常出色。

Realtime API 还可以独立用于转录和轮次检测用例。客户端可以流式传输音频,当检测到语音时,Realtime API 会生成流式转录。

这两个用例都受益于内置的 语音活动检测 (VAD),可以自动检测用户何时完成说话。这有助于无缝处理对话轮次,或者逐句分析转录。

在专门的指南中了解更多关于这些用例的信息。

实时语音到语音

学习如何使用 Realtime API 进行流式语音到语音对话。

https://platform.openai.com/docs/guides/realtime-conversations


实时转录

学习如何使用 Realtime API 进行仅转录的用例。

https://platform.openai.com/docs/guides/realtime-transcription


根据您的用例(对话或转录),您应该以不同的方式初始化会话。使用下面的切换器查看每种情况的详细信息。


通过 WebRTC 连接

WebRTC 是一套强大的标准接口,用于构建实时应用。OpenAI 实时 API 支持通过 WebRTC 服务器间连接连接到实时模型。按照以下指南了解如何配置 WebRTC 连接到实时 API。


概述

在您希望从不受信任的客户端(如网页浏览器)通过网络连接到实时模型的情况下,我们建议使用 WebRTC 连接方法。WebRTC 更适合处理可变连接状态,并提供了一系列方便的 API 用于捕获用户音频输入和播放来自模型的远程音频流。

从浏览器连接到实时 API 应该使用一个临时 API 密钥,通过 OpenAI REST API 生成。初始化 WebRTC 连接的过程如下(假设客户端是网页浏览器):

  1. 浏览器向开发者控制的服务器发送请求,以创建一个临时 API 密钥。
  2. 开发者的服务器使用一个 标准 API 密钥 从 OpenAI REST API 请求一个临时密钥,并将这个新密钥返回给浏览器。请注意,临时密钥目前在被颁发后一分钟内过期。
  3. 浏览器使用临时密钥直接通过 WebRTC 互连 验证与 OpenAI 实时 API 的会话。

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9ea9d21944c442618b959bec231bd3de.png =500xx)


虽然从技术上讲,可以使用 标准API密钥 来验证客户端WebRTC会话,但 这种做法是危险且不安全的,因为它会泄露您的密钥。标准API密钥可以访问您的完整OpenAI API账户,并且应该仅在安全的服务器端环境中使用。我们建议在可能的情况下,在客户端应用程序中使用临时密钥。


连接详情

通过 WebRTC 连接需要以下连接信息:

URLhttps://api.openai.com/v1/realtime
查询参数model 要连接的实时 模型 ID,例如 gpt-4o-realtime-preview-2024-12-17
头部Authorization: Bearer EPHEMERAL_KEY 用临时 API 密钥替换 EPHEMERAL_KEY - 有关如何生成一个的详细信息见下文。

以下示例展示了如何初始化一个 WebRTC 会话(包括用于发送和接收实时 API 事件的 数据通道)。它假设您已经获取了一个临时 API 密钥(用于此目的的示例服务器代码可以在 下一节 中找到)。

async function init() {
  // Get an ephemeral key from your server - see server code below
  const tokenResponse = await fetch("/session");
  const data = await tokenResponse.json();
  const EPHEMERAL_KEY = data.client_secret.value;

  // 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]);

  // Set up data channel for sending and receiving events
  const dc = pc.createDataChannel("oai-events");
  dc.addEventListener("message", (e) => {
    // Realtime server events appear here!
    console.log(e);
  });

  // Start the session using the Session Description Protocol (SDP)
  const offer = await pc.createOffer();
  await pc.setLocalDescription(offer);

  const baseUrl = "https://api.openai.com/v1/realtime";
  const model = "gpt-4o-realtime-preview-2024-12-17";
  const sdpResponse = await fetch(`${baseUrl}?model=${model}`, {
    method: "POST",
    body: offer.sdp,
    headers: {
      Authorization: `Bearer ${EPHEMERAL_KEY}`,
      "Content-Type": "application/sdp"
    },
  });

  const answer = {
    type: "answer",
    sdp: await sdpResponse.text(),
  };
  await pc.setRemoteDescription(answer);
}

init();

WebRTC API 提供了丰富的控件来处理媒体流和输入设备。有关在 WebRTC 上构建用户界面的更多指导,请参阅 MDN 上的文档。


创建一个临时token

要创建一个用于客户端的临时token ,您需要构建一个小的服务器端应用程序(或集成现有的一个)来通过 OpenAI REST API 请求一个临时密钥。您将使用一个 标准 API 密钥 在您的后端服务器上对这次请求进行身份验证。

以下是一个简单的 Node.js express 服务器示例,它使用 REST API 签发一个临时 API 密钥:

import express from "express";

const app = express();

// An endpoint which would work with the client code above - it returns
// the contents of a REST API request to this protected endpoint
app.get("/session", async (req, res) => {
  const r = await fetch("https://api.openai.com/v1/realtime/sessions", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "gpt-4o-realtime-preview-2024-12-17",
      voice: "verse",
    }),
  });
  const data = await r.json();

  // Send back the JSON we received from the OpenAI REST API
  res.send(data);
});

app.listen(3000);

您可以在任何可以发送和接收HTTP请求的平台创建一个类似这样的服务器端点。只需确保您只在服务器上使用标准的OpenAI API密钥,而不是在浏览器中。


发送和接收事件

要了解如何通过WebRTC数据通道发送和接收事件,请参阅实时对话指南。


使用 WebSockets 连接

WebSockets 是一个广泛支持的实时数据传输 API,也是连接到 OpenAI 实时 API 在服务器到服务器应用中的绝佳选择。对于浏览器和移动客户端,我们建议通过 WebRTC 进行连接。


概述

在与 Realtime 的服务器到服务器集成中,您的后端系统将通过 WebSocket 直接连接到 Realtime API。您可以使用 标准 API 密钥 来验证此连接,因为token 只能在您的安全后端服务器上可用。


在这里插入图片描述


如果您选择通过客户端设备上的 WebSocket 连接到 Realtime API,则 WebSocket 连接也可以使用临时客户端令牌进行身份验证(如上文 WebRTC 部分所示)。

标准 OpenAI API 令牌应仅用于安全的服务器端环境


连接详情

语音到语音转录

通过WebSocket连接需要以下连接信息:

URLwss://api.openai.com/v1/realtime
查询参数model 要连接的实时 模型ID,例如 gpt-4o-realtime-preview-2024-12-17
头部Authorization: Bearer YOUR_API_KEYYOUR_API_KEY 替换为服务器上的 标准API密钥,或在非安全客户端上的 临时token (注意,对于此用例,建议使用WebRTC)。OpenAI-Beta: realtime=v1 在测试期间,此头部是必需的。

以下是一些使用这些连接详情初始化WebSocket连接到实时API的示例。

ws模块(Node.js)websocket-client(Python)WebSocket(浏览器)

使用ws模块(Node.js)进行连接

import WebSocket from "ws";

const url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-12-17";
const ws = new WebSocket(url, {
  headers: {
    "Authorization": "Bearer " + process.env.OPENAI_API_KEY,
    "OpenAI-Beta": "realtime=v1",
  },
});

ws.on("open", function open() {
  console.log("Connected to server.");
});

ws.on("message", function incoming(message) {
  console.log(JSON.parse(message.toString()));
});

实时对话Beta

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

https://platform.openai.com/docs/guides/realtime-conversations


一旦通过WebRTC或WebSocket连接到实时API,您就可以调用实时模型(例如gpt-4o-realtime-preview)进行语音到语音的对话。这样做需要您发送客户端事件来启动操作,并监听服务器事件以响应实时API采取的操作。
本指南将介绍使用模型功能(如音频和文本生成以及功能调用)所需的事件流程,以及如何考虑实时会话的状态。
如果您不需要与模型进行对话,即您不期望任何响应,您可以使用实时 API 的 转录模式。


实时语音到语音会话

实时会话是模型与连接客户端之间的有状态交互。会话的关键组件包括:

  • 会话对象,它控制交互的参数,如使用的模型、生成输出的声音以及其他配置。
  • 对话,它表示当前会话期间产生的用户输入项和模型输出项。
  • 响应,这是模型生成的音频或文本项,被添加到对话中。

输入音频缓冲区和WebSockets

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

如果您使用WebSockets进行音频传输,您需要手动与输入音频缓冲区进行交互,通过发送音频到服务器,这些音频以JSON事件的形式发送,并使用base64编码。

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


在这里插入图片描述


会话生命周期事件

通过 WebRTC 或 WebSockets 初始化会话后,服务器将发送一个 session.created 事件,表示会话已准备好。在客户端,您可以使用 session.update 事件更新当前会话配置。大多数会话属性可以在任何时间更新,但模型在会话中响应音频一次后,用于音频输出的 voice 无法更新。实时会话的最大时长为 30分钟

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

更新此会话中模型使用的系统指令

event = {
    "type": "session.update",
    "session": {
        "instructions": "Never use the word 'moist' in your responses!"
    }
}
ws.send(json.dumps(event))

当会话被更新后,服务器将发出一个 session.updated(https://platform.openai.com/docs/api-reference/realtime-server-events/session/updated) 事件,包含会话的新状态。

相关客户端事件相关服务器事件
session.updatesession.createdsession.updated

文本输入和输出

要使用实时模型生成文本,您可以向当前对话中添加文本输入,要求模型生成响应,并监听表示模型响应进度的服务器发送事件。为了生成文本,会话必须配置为使用 text 模式(默认情况下是这样)。

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

创建一个包含用户输入的对话项

event = {
    "type": "conversation.item.create",
    "item": {
        "type": "message",
        "role": "user",
        "content": [
            {
                "type": "input_text",
                "text": "What Prince album sold the most copies?",
            }
        ]
    }
}
ws.send(json.dumps(event))

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

event = {
    "type": "response.create",
    "response": {
        "modalities": [ "text" ]
    }
}
ws.send(json.dumps(event))

当响应完全完成后,服务器将发出response.done事件。此事件将包含模型生成的完整文本,如下所示。
监听 response.done 以查看最终结果

def on_message(ws, message):
    server_event = json.loads(message)
    if server_event.type == "response.done":
        print(server_event.response.output[0])

在模型响应生成过程中,服务器将在处理过程中发出一系列生命周期事件。您可以监听这些事件,例如response.text.delta,以在响应生成时向用户提供实时反馈。服务器发出的所有事件列表如下,在相关服务器事件部分。它们按照事件发出的顺序提供,以及与文本生成相关的客户端事件。

相关客户端事件相关服务器事件
conversation.item.create
response.create
conversation.item.created
response.created
response.output_item.added
response.content_part.added
response.text.delta
response.text.done
response.content_part.done
response.output_item.done
response.done
rate_limits.updated

音频输入和输出

Realtime API 最强大的功能之一是与模型进行语音到语音的交互,无需中间的文本到语音或语音到文本步骤。这使语音界面的延迟更低,并给模型提供了更多关于语音输入的语调和语气的数据来处理。


语音选项

实时会话可以配置为在生成音频输出时使用几个内置语音之一。您可以在会话创建时(或在 response.create 上)设置 voice 来控制模型的声音。当前语音选项有 alloyashballadcoralechosageshimmerverse。一旦模型在一个会话中发出音频,该会话的 voice 就不能修改。


使用 WebRTC 处理音频

如果您使用 WebRTC 连接到实时 API,实时 API 正在充当您的客户端的 peer connection。模型输出的音频以 remote media stream 的形式发送到您的客户端。模型输入的音频通过音频设备(getUserMedia)收集,并将媒体流作为轨道添加到 peer connection。

来自 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]);

上面的代码片段可以启用与实时API的简单交互,但还有更多可以做的事情。有关不同类型用户界面的更多示例,请查看WebRTC samples存储库。这些样本的实时演示也可以在这里找到。
使用 媒体捕获和流 浏览器中的功能,您可以实现诸如静音和取消静音麦克风、选择收集输入的设备等功能。


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

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

然而,当音频在客户端和服务器之间通过对等连接来回传输时,WebRTC 客户端仍然会接收到许多由服务器发送的生命周期事件。例如:

  • 当输入通过本地媒体轨道发送时,您将从服务器接收到 input_audio_buffer.speech_started 事件。
  • 当本地音频输入停止时,您将接收到 input_audio_buffer.speech_stopped 事件。
  • 您将接收到 正在进行中的音频转录的 delta 事件。
  • 当模型转录并完成发送响应时,您将接收到 response.done 事件。

操作 WebRTC API 以获取媒体流可能为您提供所需的所有控制。然而,有时可能需要使用更底层的接口来处理音频输入和输出。有关更多信息以及用于细粒度音频输入处理所需的事件列表,请参阅下方的 WebSockets 部分。


使用 WebSockets 处理音频

在通过 WebSocket 发送和接收音频时,您需要做更多的工作来从客户端发送媒体,并从服务器接收媒体。以下是一个表格,描述了在 WebSocket 会话期间的事件流程,这些事件对于通过 WebSocket 发送和接收音频是必要的。

以下事件按生命周期顺序给出,尽管某些事件(如 delta 事件)可能同时发生。

生命周期阶段客户端事件服务器事件
会话初始化session.updatesession.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.created
response.created
response.output_item.created
response.content_part.added
response.audio.delta
response.audio_transcript.delta
response.text.delta
response.audio.done
response.audio_transcript.done
response.text.done
response.content_part.done
response.output_item.done
response.done
rate_limits.updated

将音频输入流式传输到服务器

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

输入块格式可以配置为整个会话或每个响应。

  • 会话:在 session.update 中的 session.input_audio_format
  • 响应:在 response.create 中的 response.input_audio_format

将音频输入字节追加到对话中

import base64
import json
import struct
import soundfile as sf
from websocket import create_connection

# ... create websocket-client named ws ...

def float_to_16bit_pcm(float32_array):
    clipped = [max(-1.0, min(1.0, x)) for x in float32_array]
    pcm16 = b''.join(struct.pack('<h', int(x * 32767)) for x in clipped)
    return pcm16

def base64_encode_audio(float32_array):
    pcm_bytes = float_to_16bit_pcm(float32_array)
    encoded = base64.b64encode(pcm_bytes).decode('ascii')
    return encoded

files = [
    './path/to/sample1.wav',
    './path/to/sample2.wav',
    './path/to/sample3.wav'
]

for filename in files:
    data, samplerate = sf.read(filename, dtype='float32')  
    channel_data = data[:, 0] if data.ndim > 1 else data
    base64_chunk = base64_encode_audio(channel_data)
    
    # Send the client event
    event = {
        "type": "input_audio_buffer.append",
        "audio": base64_chunk
    }
    ws.send(json.dumps(event))

发送完整音频消息

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

创建包含完整音频输入的对话项目

fullAudio = "<a base64-encoded string of audio bytes>"

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

ws.send(json.dumps(event))

使用WebSocket处理音频输出

要在客户端设备(如网页浏览器)上播放输出音频,我们建议使用WebRTC而不是WebSocket。WebRTC在发送媒体到客户端设备时,即使在不确定的网络条件下,也会更加稳健。

但是,要使用WebSocket在服务器到服务器的应用程序中处理音频输出,您需要监听包含从模型中编码的音频数据块的Base64编码的response.audio.delta事件。您可能需要缓冲这些块并将它们写入文件,或者立即将它们流式传输到另一个源,例如与Twilio的电话通话。

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

输出块格式可以配置为整个会话或每个响应。

  • 会话:在session.update中的session.output_audio_format session.update
  • 响应:在response.create中的response.output_audio_format response.create

监听response.audio.delta事件

def on_message(ws, message):
    server_event = json.loads(message)
    if server_event.type == "response.audio.delta":
        # Access Base64-encoded audio chunks:
        # print(server_event.delta)

语音活动检测

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

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


禁用 VAD

可以通过将 turn_detection 设置为 null 并使用 session.update 客户端事件来禁用 VAD。这在需要精细控制音频输入的接口中非常有用,例如 push to talk 接口。

当 VAD 被禁用时,客户端将需要手动发出一些额外的客户端事件来触发音频响应:

  • 手动发送 input_audio_buffer.commit,这将为新对话创建一个新的用户输入项。
  • 手动发送 response.create 以触发模型生成的音频响应。
  • 在开始新的用户输入之前发送 input_audio_buffer.clear

保持VAD模式,但禁用自动响应

如果您想保持VAD模式开启,但只想保留手动决定何时生成响应的能力,您可以将 turn_detection.interrupt_responseturn_detection.create_response 设置为 false,使用 session.update(会话更新)客户端事件。这将保留VAD的所有行为,但不会自动创建新的响应。客户端可以使用 response.create(创建响应)事件手动触发这些操作。

这在用于内容审核或输入验证或RAG模式时可能很有用,在这些模式中,您更愿意在交互的延迟与对输入的控制之间进行权衡。


在默认对话之外创建响应

默认情况下,在会话期间生成的所有响应都会添加到会话的对话状态(即“默认对话”)中。然而,您可能希望在不属于会话默认对话的上下文中生成模型响应,或者希望同时生成多个响应。您还可能希望对模型在生成响应时考虑的对话项有更精细的控制(例如,仅考虑最后 N 个回合)。

通过在创建响应时将 response.conversation 字段设置为字符串 none,可以使用 response.create 客户端事件生成不属于默认对话状态的“场外”响应。

在创建场外响应时,您可能还希望有一种方式来识别哪些服务器发送的事件与该响应相关。您可以为模型响应提供 metadata,这有助于您识别为该客户端发送的事件生成的哪个响应。

创建一个场外模型响应

prompt = """
Analyze the conversation so far. If it is related to support, output
"support". If it is related to sales, output "sales".
"""

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
        "modalities": [ "text" ],
        "instructions": prompt,
    },
}

ws.send(json.dumps(event))

现在,当您监听 response.done 服务器事件时,您可以识别出您的非带外响应的结果。
创建一个带外模型响应

def on_message(ws, message):
    server_event = json.loads(message)
    topic = ""

    # See if metadata is present
    try:
        topic = server_event.response.metadata.topic
    except AttributeError:
        print("topic not set")
    
    if server_event.type == "response.done" and topic == "classification":
        # this server event pertained to our OOB model response
        print(server_event.response.output[0])

为响应创建自定义上下文

您还可以创建一个自定义上下文,该模型将使用该上下文在默认/当前对话之外生成响应。这可以通过使用 response.create 客户端事件上的 input 数组来完成。查看文档。您可以使用新的输入,或者通过 ID 引用对话中的现有输入项。

监听具有自定义上下文的出站模型响应

event = {
    "type": "response.create",
    "response": {
        "conversation": "none",
        "metadata": { "topic": "pizza" },
        "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"
            },

            # include new content as well
            {
                "type": "message",
                "role": "user",
                "content": [
                    {
                        "type": "input_text",
                        "text": "Is it okay to put pineapple on pizza?",
                    }
                ],
            }
        ],
    },
}

ws.send(json.dumps(event))

使用无上下文创建响应

您还可以将响应插入到默认对话中,忽略所有其他指令和上下文。通过将 input 设置为空数组来实现。

将无上下文模型响应插入到默认对话中

prompt = """
Say exactly the following:
I'm a little teapot, short and stout! 
This is my handle, this is my spout!
"""

event = {
    "type": "response.create",
    "response": {
        # An empty input array removes all prior context
        "input": [],
        "instructions": prompt,
    },
}

ws.send(json.dumps(event))

函数调用

实时模型也支持函数调用,这使您能够执行自定义代码以扩展模型的功能。以下是它在高层次上的工作原理:

  1. 当更新会话或创建响应时,您可以指定模型可以调用的可用函数列表。
  2. 如果在处理输入时,模型确定它应该进行函数调用,它将向对话中添加代表函数调用参数的项目。
  3. 当客户端检测到包含函数调用参数的对话项目时,它将使用这些参数执行自定义代码。
  4. 当自定义代码执行完毕后,客户端将创建新的对话项目,其中包含函数调用的输出,并要求模型进行响应。

让我们通过添加一个可调用的函数来展示这将在实践中如何工作,该函数将为模型的用户提供今天的星座运势。我们将展示需要发送的客户端事件对象的形状,以及服务器将相应地发出什么。


配置可调用的函数

首先,我们必须为模型提供一个基于用户输入它可以调用的函数选择。可用的函数可以在会话级别或单个响应级别进行配置。

  • 会话:session.tools 属性在 session.update
  • 响应:response.tools 属性在 response.create

以下是一个配置占星术生成函数的 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\"}"
      }
    ],
    "usage": {
      "total_tokens": 541,
      "input_tokens": 521,
      "output_tokens": 20,
      "input_token_details": {
        "text_tokens": 292,
        "audio_tokens": 229,
        "cached_tokens": 0,
        "cached_tokens_details": { "text_tokens": 0, "audio_tokens": 0 }
      },
      "output_token_details": {
        "text_tokens": 20,
        "audio_tokens": 0
      }
    },
    "metadata": null
  }
}

在服务器发出的 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 客户端事件创建一个新的对话项来包含该结果。

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 事件。这将触发使用函数调用数据的一个模型响应。

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"
}

实时转录Beta

学习如何使用实时API进行实时音频转录。

https://platform.openai.com/docs/guides/realtime-transcription


您可以使用实时API进行仅转录的使用场景,无论是从麦克风输入还是从文件输入。例如,您可以使用它实时生成字幕或转录。在仅转录模式下,模型将不会生成响应。
如果您想让模型生成响应,您可以使用实时 API 在 语音到语音对话模式 中。


实时转录会话

要使用实时API进行转录,您需要创建一个转录会话,通过 WebSockets 或 WebRTC 连接。

与常规实时API会话进行对话不同,转录会话通常不包含来自模型的响应。

转录会话对象也与常规实时API会话不同:

{
  object: "realtime.transcription_session",
  id: string,
  input_audio_format: string,
  input_audio_transcription: [{
    model: string,
    prompt: string,
    language: string
  }],
  turn_detection: {
    type: "server_vad",
    threshold: float,
    prefix_padding_ms: integer,
    silence_duration_ms: integer,
  } | null,
  input_audio_noise_reduction: {
    type: "near_field" | "far_field"
  },
  include: list[string] | null
}

一些转录会话支持的附加属性包括:

  • input_audio_transcription.model: 要使用的转录模型,目前支持 gpt-4o-transcribegpt-4o-mini-transcribewhisper-1
  • input_audio_transcription.prompt: 用于转录的提示,用于指导模型(例如:“期待与科技相关的词语”)
  • input_audio_transcription.language: 用于转录的语言,理想情况下采用 ISO-639-1 格式(例如 “en”, “fr”…)以提高准确性和延迟
  • input_audio_noise_reduction:用于转录的降噪配置
  • include: 要包含在转录事件中的属性列表

可能的输入音频格式值为:pcm16(默认)、g711_ulawg711_alaw
您可以在API参考中找到有关转录会话对象的更多信息。


处理转录

当使用实时API进行转录时,您可以监听 conversation.item.input_audio_transcription.deltaconversation.item.input_audio_transcription.completed 事件。

对于 whisper-1delta 事件将包含完整的回合转录,与 completed 事件相同。对于 gpt-4o-transcribegpt-4o-mini-transcribedelta 事件将包含增量转录,这些转录将随着从模型中流出来进行。

以下是一个转录 delta 事件的示例:

{
  "event_id": "event_2122",
  "type": "conversation.item.input_audio_transcription.delta",
  "item_id": "item_003",
  "content_index": 0,
  "delta": "Hello,"
}

以下是示例转录完成事件:

{
  "event_id": "event_2122",
  "type": "conversation.item.input_audio_transcription.completed",
  "item_id": "item_003",
  "content_index": 0,
  "transcript": "Hello, how are you?"
}

请注意,来自不同说话轮次的完成事件之间的顺序不保证。您应使用 item_id 将这些事件与 input_audio_buffer.committed 事件匹配,并使用 input_audio_buffer.committed.previous_item_id 来处理顺序。
要将音频数据发送到转录会话,您可以使用 input_audio_buffer.append 事件。
您有 2 个选项:

  • 使用流式麦克风输入
  • 从wav文件中流式传输数据

语音活动检测

实时API支持自动语音活动检测(VAD)。默认启用,VAD将控制何时提交输入音频缓冲区,因此何时开始转录。

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

您还可以通过将turn_detection属性设置为null来禁用VAD,并控制何时在您端提交输入音频。


额外配置


噪声降低

您可以使用 input_audio_noise_reduction 属性来配置如何处理音频流中的噪声降低。

可能的值有:

  • near_field:使用近场噪声降低。
  • far_field:使用远场噪声降低。
  • null:禁用噪声降低。

默认值是 near_field,您可以通过将属性设置为 null 来禁用噪声降低。


使用 logprobs

您可以使用 include 属性来在转录事件中包含 logprobs,使用 item.input_audio_transcription.logprobs

这些 logprobs 可以用来计算转录的置信度得分。

{
  "type": "transcription_session.update",
  "input_audio_format": "pcm16",
  "input_audio_transcription": {
    "model": "gpt-4o-transcribe",
    "prompt": "",
    "language": ""
  },
  "turn_detection": {
    "type": "server_vad",
    "threshold": 0.5,
    "prefix_padding_ms": 300,
    "silence_duration_ms": 500,
  },
  "input_audio_noise_reduction": {
    "type": "near_field"
  },
  "include": [ 
    "item.input_audio_transcription.logprobs",
  ],
}

语音活动检测 (VAD)Beta

了解 Realtime API 中的自动语音活动检测。

https://platform.openai.com/docs/guides/realtime-vad


语音活动检测(VAD)是实时API中的一项功能,允许自动检测用户何时开始或停止说话。在 语音到语音 或 转录 实时会话中默认启用,但它是可选的,可以关闭。


概述

当启用VAD(语音活动检测)时,音频会自动分块,并且实时API会发送事件来指示用户何时开始或停止说话:

  • input_audio_buffer.speech_started:语音回合的开始
  • input_audio_buffer.speech_stopped:语音回合的结束

您可以使用这些事件来处理您的应用程序中的语音回合。例如,您可以使用它们来管理会话状态或分块处理转录本。

您可以使用session.update事件的turn_detection属性来配置每个语音到文本样本中音频的分块方式。

VAD有两种模式:

  • server_vad:根据静默期间自动分块音频。
  • semantic_vad:当模型根据用户说的词语认为他们已完成其语音时,分块音频。

默认值是server_vad

阅读以下内容了解有关不同模式的更多信息。


服务器VAD

服务器VAD是实时会话的默认模式,并使用静默时间段来自动分割音频。

您可以调整以下属性以微调VAD设置:

  • threshold:激活阈值(0到1)。较高的阈值将需要更响亮的音频来激活模型,因此可能在嘈杂环境中表现更好。
  • prefix_padding_ms:在VAD检测到语音之前包含的音频量(以毫秒为单位)。
  • silence_duration_ms:检测语音停止的静默持续时间(以毫秒为单位)。较短的值会使转弯检测更快。

以下是一个VAD配置示例:

{
  "type": "session.update",
  "session": {
    "turn_detection": {
      "type": "server_vad",
      "threshold": 0.5,
      "prefix_padding_ms": 300,
      "silence_duration_ms": 500,
      "create_response": true, // only in conversation mode
      "interrupt_response": true, // only in conversation mode
    }
  }
}

语义语音活动检测(Semantic VAD)

语义语音活动检测是一种新模式,它使用语义分类器来检测用户何时完成说话,基于他们所使用的词语。这个分类器根据用户完成说话的概率对输入音频进行评分。当概率低时,模型将等待超时,而当概率高时,则无需等待。例如,以“嗯嗯…”结尾的用户音频会导致比明确陈述更长的超时时间。

使用此模式,模型在语音到语音对话中打断用户的可能性较小,或者在使用户完成说话之前截取转录。

可以通过将 turn_detection.type 设置为 semantic_vad 在一个 session.update(https://platform.openai.com/docs/api-reference/realtime-client-events/session/update) 事件中激活语义语音活动检测。

它可以这样配置:

{
  "type": "session.update",
  "session": {
    "turn_detection": {
      "type": "semantic_vad",
      "eagerness": "low" | "medium" | "high" | "auto", // optional
      "create_response": true, // only in conversation mode
      "interrupt_response": true, // only in conversation mode
    }
  }
}

可选的 eagerness 属性是一种控制模型如何急切地打断用户的属性,调整最大等待超时时间。在转录模式下,即使模型不回复,它也会影响音频的分割方式。

  • auto 是默认值,等同于 medium
  • low 将让用户有更多时间说话。
  • high 将尽可能快地分割音频。
    如果您希望模型在对话模式下更频繁地回应,或者在转录模式下更快地返回转录事件,可以将 eagerness 设置为 high
    另一方面,如果您想在对话模式下让用户不间断地说话,或者您希望在转录模式下有更大的转录片段,您可以设置 eagernesslow

2025-03-29(六)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2324569.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PE文件(十三)资源表

所谓的资源也就是我们之前学的MFC中的对话框&#xff0c;按钮&#xff0c;编辑框之类的东西。不仅MFC有资源&#xff0c;我们平时熟悉的控制台程序也有资源 当我们平时写一些程序或者木马时&#xff0c;我们通常对其定义一个随机的名称或者路径&#xff0c;然后再向外界进行释…

丝杆升降机行程控制:精准运行的奥秘

丝杆升降机作为机械传动领域的 “得力干将”&#xff0c;在环保设备、工业生产线、建筑施工等众多场景中发挥着关键作用。其能够实现重物的升降、平移等操作&#xff0c;而行程控制对于丝杆升降机而言&#xff0c;就如同给机器设定了行动边界&#xff0c;不仅关乎设备能否精准达…

力扣.旋转矩阵Ⅱ

59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 代码区&#xff1a; class Solution {const int MAX25; public:vector<vector<int>> generateMatrix(int n) {vector<vector<int>> ans;vector<int> hang;int len_nn;int arry[25][25]…

HFSS 使用入门

资源 下载资源&#xff1a; https://download.csdn.net/download/wangjun_huster/90547193 下载破解&#xff1a; https://download.csdn.net/download/wangjun_huster/90547551 安装 https://www.bilibili.com/list/ml3403866295?oid925751664&bvidBV1CT4y1u7LB 入门…

对内核fork进程中写时复制的理解记录

前言 文章写于学习Redis时对aof后台重写中写时复制的疑问 一、感到不理解的歧义 在部分技术文档中&#xff08;以小林的文章为例&#xff09;&#xff0c;对写时复制后的内存权限存在如歧义&#xff1a; ! 二、正确技术表述 根据Linux内核实现&#xff08;5.15版本&#x…

HarmonyOS-ArkUI Navigation (导航组件)-第一部分

导航组件主要实现页面间以及组件内部的界面跳转&#xff0c;支持在不同的组件间进行参数的传递&#xff0c;提供灵活的跳转栈操作&#xff0c;从而便捷的实现对不同页面的访问和复用。 我们之前学习过Tabs组件&#xff0c;这个组件里面也有支持跳转的方式&#xff0c;Navigati…

【磁盘扩容】linux磁盘扩容

一、新磁盘分区 1、新磁盘在接入服务器后&#xff0c;很好辨认 使用fdisk -l命令&#xff0c;查看&#xff1a; 或者使用 lsblk -f 其中sdb,sdc, sda都是挂载硬盘&#xff0c;sr0为DVD光盘&#xff0c;很明显sdc没有进行任何的挂载&#xff0c;确定sdc为新磁盘 2、格式化新…

详解CountDownLatch底层源码

大家好&#xff0c;我是此林。 今天来分享一下CountDownLatch的底层源码。 CountDownLatch 是 Java 并发包 (java.util.concurrent) 中的线程之间同步工具类&#xff0c;主要用于协调多个线程的执行顺序。其核心思想是通过计数器实现线程间的"等待-唤醒"机制&#…

Python基于EdgeTTS库文本转语音

EdgeTTS&#xff0c;支持粤语等各种方言&#xff0c;无需部署无需Key&#xff0c;完全免费&#xff0c;太香了 因为其底层是使用微软 Edge 的在线语音合成服务&#xff0c;所以不需要下载任何模型&#xff0c;甚至连 api_key 都给你省了&#xff0c;简直不要太良心~ 关键是&a…

MFC案例:利用计时器(Timer)动态绘制正弦曲线

这是一个基于对话框的MFC程序&#xff0c;运行效果是在只画出I、IV象限的坐标系中绘制出红、蓝、绿各相差PI/2的三条正弦曲线&#xff0c;计时器运行一个周期曲线在X轴移动一个像素&#xff08;对应1度&#xff09;&#xff0c;Y轴显示正弦值&#xff08;150个像素代表1&#x…

解析 HTML 网站架构规范

2025/3/28 向全栈工程师迈进&#xff01; 一、网页基本的组成部分 网页的外观多种多样&#xff0c;但是除了全屏视频或游戏&#xff0c;或艺术作品页面&#xff0c;或只是结构不当的页面以外&#xff0c;都倾向于使用类似的标准组件。 1.1页眉 通常横跨于整个页面顶部有一…

Kubernetes》》K8S》》Deployment 、Pod、Rs 、部署 nginx

Deployment deployment文档说明 kubectl get rs,deployment,pods 删除pod 、deployment 、service # 如果只删除pod&#xff0c;deployment会自动重建&#xff0c;所以应该先删除deployment。 # 下面演示的是删除所有deployment&#xff0c;可以指定只删除某个 # 删除所有…

链表(C++)

这是本人第二次学习链表&#xff0c;第一次学习链表是在大一上的C语言课上&#xff0c;首次接触&#xff0c;感到有些难&#xff1b;第二次是在大一下学习数据结构时&#xff08;就是这次&#xff09;&#xff0c;使用C再次理解链表。同时&#xff0c;这也是开启数据结构学习写…

nginx 设置隐藏版本号

Nginx默认会在Server头里包含版本信息&#xff0c;比如“nginx/1.18.0”&#xff0c;这可能存在安全隐患&#xff0c;因为攻击者知道了版本号后&#xff0c;可以针对特定版本的漏洞进行攻击。所以&#xff0c;隐藏版本号是一个常见的安全措施。 可通过在http块里加上server_to…

29_项目

目录 http.js 1、先注册账号 register.html 2、再登录 login.html 3、首页 index.html 4 详情 details.html cart.html css index.css register.css details.css 演示 进阶 http.js let baseURL "http://localhost:8888"; let resgiterApi baseURL &…

排序算法1--插入排序

目录 1.常见排序算法 2.排序算法的预定函数 2.1交换函数 2.2测试算法运行时间的函数 3.插入排序 3.1直接插入排序 3.2希尔排序 3.3插入排序的时间复杂度分析 4.总结 1.常见排序算法 我将分别讲解五种排序算法&#xff0c;但是不代表只有五种固定的代码&#xff0c;之后…

业之峰与宏图智能战略携手,开启家装数字化新篇章

3月8日&#xff0c;业之峰装饰集团董事长张钧携高管团队与宏图智能董事长庭治宏及核心团队&#xff0c;在业之峰总部隆重举行了战略合作签约仪式&#xff0c;标志着双方将携手探索业之峰的数字化转型之路&#xff0c;共同推动家装行业的变革与发展。 近年来&#xff0c;家装行业…

matplotlib标题比x,y轴字体大,明明标题字体更大?

原始代码&#xff1a; plt.xlabel(训练轮次&#xff08;Epochs&#xff09;, fontsize14, fontweightbold, fontpropertieschinese_font) # 设置中文字体、加大、加粗 plt.ylabel(R值, fontsize14, fontweightbold, fontpropertieschinese_font) # 设置中文字体、加大、加粗…

【云原生】docker 搭建单机PostgreSQL操作详解

目录 一、前言 二、前置准备 2.1 服务器环境 2.2 docker环境 三、docker安装PostgreSQL过程 3.1 获取PostgreSQL镜像 3.2 启动容器 3.2.1 创建数据卷目录 3.2.2 启动pg容器 3.3 客户端测试连接数据库 四、创建数据库与授权 4.1 进入PG容器 4.2 PG常用操作命令 4.2…

免费使用!OpenAI 全量开放 GPT-4o 图像生成能力!

2025年3月26日&#xff0c;OpenAI正式推出GPT-4o原生图像生成功能&#xff0c;这一更新不仅标志着多模态AI技术的重大突破&#xff0c;更引发了全球AI厂商的激烈竞争。从免费用户到企业开发者&#xff0c;从创意设计到科学可视化&#xff0c;GPT-4o正在重塑图像生成的边界。本文…