缓存时间:
2026/05/11 18:33
# 使用 gpt-realtime-translate 构建实时翻译应用 来源:https://developers.openai.com/cookbook/examples/voice_solutions/realtime_translation_guide
`gpt-realtime-translate` 是一个用于构建跨广播、流媒体、通话和视频通话的多语言音频体验的实时语音转语音翻译模型。它接收语音输入,自动检测源语言,并返回翻译后的语音和文本转录。开发者只需指定目标输出语言即可。该模型具备两项使其独具优势的新特性:
1. 与通用语音模型不同,`gpt-realtime-translate` **针对口译场景进行了优化**。它接受了数千小时专业口译音频的训练,这有助于它专注于翻译任务,并在生成语音前等待足够的上下文。这对于句法结构差异较大的语言之间尤为重要。
2. 它能够**在处理输入音频的同时,流式返回翻译后的音频**。这使得连续语音交互能够实现真正的低延迟。
我们构建 `gpt-realtime-translate` 是因为实时口译对现有 AI 语音交互有着不同的要求。虽然可以提示通用模型进行翻译,但它们仍可能回答问题或遵循指令,而不是执行翻译。此外,它们依赖于基于回合的交互,要求说话者在模型生成翻译音频时暂停,这不利于流畅的口译。这些一直是阻碍实现自然实时口译所需的高准确性和低延迟的主要因素,而 `gpt-realtime-translate` 正是为了解决这些问题而生。
该模型的独特之处在于,它主要旨在**赋能人类实现多语言交流,而非构建 AI 语音代理**。如果你正在构建语音代理,请使用新的 `gpt-realtime-2` 模型。我们认为 `gpt-realtime-translate` 主要有两种应用场景:第一种是**广播式翻译**:适用于直播、网络研讨会、讲座、财报电话会议、大会主旨演讲等场景,即大量听众需要将单一主要来源的音频翻译为另一种语言。第二种是**对话式翻译**,适用于两名或多名参与者使用不同语言相互交流的场景:如呼叫中心、视频聊天或其他基于电话的工作流。
本 Cookbook 将重点围绕这些模式展开:我们将从基础知识入手,接着构建一个用于**单向实时翻译**的 Web 应用(捕获任意浏览器标签页音频),使用 Twilio 将翻译功能集成到**电话通话**中,并结合 LiveKit 创建一个**多语言群组视频聊天**室。最后,我们将介绍生产环境最佳实践、模型限制以及评估方法。你将掌握三种为现有音频路径添加实时翻译的方法:
1. **浏览器标签页翻译:** 使用 `getDisplayMedia()` 捕获标签页音频,通过 WebRTC 将其发送至 Realtime Translation,并在浏览器中播放翻译后的语音及字幕。
2. **电话通话翻译:** 使用 Twilio Media Streams 通过 WebSocket 接收电话音频,将其桥接至 Realtime Translation,并将翻译后的音频发回给另一位呼叫者。
3. **视频通话翻译:** 订阅远程 LiveKit 麦克风轨道,为监听者实时翻译每位远程发言者,并在本地渲染翻译后的音频和字幕。
完整的演示应用位于配套的文件夹中:
- Browser tab translation (https://github.com/openai/openai-cookbook/tree/main/examples/voice_solutions/realtime_translation_guide/browser-translation-demo)
- Twilio phone translation (https://github.com/openai/openai-cookbook/tree/main/examples/voice_solutions/realtime_translation_guide/twilio-translation-demo)
- LiveKit video translation (https://github.com/openai/openai-cookbook/tree/main/examples/voice_solutions/realtime_translation_guide/livekit-translation-demo)
你需要准备:
- 一个 OpenAI API 密钥。
- 用于浏览器和 Twilio 演示的 Node.js。
- 如果你想运行电话通话演示,需要一个 Twilio 电话号码。
- 如果你想运行视频房间演示,需要一个 LiveKit 项目或自建 LiveKit 服务器。
请将 OpenAI API 密钥保存在服务器端。浏览器示例应使用短期客户端密钥(client secrets),而不是将 API 密钥暴露给客户端代码。有关设置 WebRTC 和 WebSocket 会话、客户端与服务端事件以及配置选项的完整详情,请参阅我们的 Live Translation 文档 (https://developers.openai.com/api/docs/guides/realtime-translation)。
### 主要差异
Realtime Translation 会话的配置围绕目标输出语言展开。使用 `session.audio.output.language` 设置目标语言。该模型目前支持超过 70 种输入语言和 13 种输出语言。该模型目前不支持自定义提示词或语音选择参数。如果你希望在翻译音频的同时获取源语言转录,请使用 `gpt-realtime-whisper` 配置输入转录。
Realtime Translation 采用**动态语音适配**技术。它不选择固定的输出语音,而是让翻译后的语音跟随源说话人的整体音调、音高和说话风格。在多说话人会话中,随着新说话人音频的传入,翻译语音也会随之切换。
### 会话生命周期
会话生命周期也与标准的 Realtime 语音会话不同:
- **专用端点:** 连接至 `/v1/realtime/translations`。
- **连续音频输入:** 使用 `session.input_audio_buffer.append` 流式传输 24 kHz PCM16 音频,包含短语之间的静音。
- **连续翻译输出:** 模型以 200 毫秒 PCM16 数据块的形式输出翻译音频,以及目标语言的转录增量(transcript deltas)。
- **无回合生命周期:** 翻译直接基于传入的音频流启动。无需管理 `response.create`、助手回合、工具调用或会话状态。
### 协议
对于基于浏览器的应用,请使用 WebRTC。浏览器将麦克风、标签页或远程参与者的音频作为媒体轨道发送,并接收远程音频轨道形式的翻译语音。使用 `oai-events` 数据通道获取会话更新、转录增量和错误信息。有关实现示例,请参阅浏览器标签页翻译或 LiveKit 章节。
对于后端媒体管道,请使用 WebSockets。当你的服务器已经接收原始音频(如 Twilio Media Streams、SIP、广播摄入或媒体 worker)时,这是最佳选择。使用 `session.input_audio_buffer.append` 发送 base64 编码的 24 kHz PCM16 音频,包含语音短语之间的静音。有关示例,请参阅使用 Twilio 进行电话通话章节。
让我们从一个用于单向实时翻译的小型浏览器应用开始。该应用捕获浏览器标签页的音频,通过 WebRTC 启动 Realtime Translation 会话,并在模型生成内容时播放带有字幕的翻译语音。当原始体验已在浏览器中进行时(如公开会议、直播、大会演讲、在线课程或没有内置实时配音的视频),这种模式非常有用。应用无需重建播放器或发布独立的语言流,而是直接嵌入页面旁边,为听众提供实时的翻译音频和字幕。
浏览器标签页翻译架构
### 工作原理
1. 你的服务器创建一个短期的翻译客户端密钥(client secret)。
2. 浏览器使用 `getDisplayMedia()` 捕获标签页音频。
3. 浏览器创建 `RTCPeerConnection`,添加捕获的音频轨道,并打开 `oai-events` 数据通道。
4. 浏览器将 SDP 提议(offer)连同客户端密钥一起 POST 到 Realtime Translation 通话端点。
5. 模型在远程 WebRTC 音频轨道上返回翻译音频,并在数据通道上发送转录增量。
### 创建翻译客户端密钥
在你的服务器上创建客户端密钥,以确保标准的 OpenAI API 密钥不会传递到浏览器。模型和输出语言包含在客户端密钥请求中。
```javascript
const TRANSLATION_CLIENT_SECRET_URL = "https://api.openai.com/v1/realtime/translations/client_secrets";
app.post("/session", async (req, res) => {
const language = req.body.targetLanguage ?? "es";
const response = await fetch(TRANSLATION_CLIENT_SECRET_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
session: {
model: "gpt-realtime-translate",
audio: {
input: {
transcription: { model: "gpt-realtime-whisper" },
noise_reduction: { type: "near_field" },
},
output: { language },
},
},
}),
});
res.status(response.status).json(await response.json());
});
```
完整的浏览器演示在发起 OpenAI 请求前会验证目标语言代码,并且仅将短期的客户端密钥返回给浏览器。
### 捕获标签页音频
使用 `getDisplayMedia()` 让用户明确选择源标签页。如果支持,请请求 `suppressLocalAudioPlayback`,以便听众不会同时听到原始音频和翻译音频。
```javascript
async function captureTabAudio() {
const audio = {
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false,
};
if (navigator.mediaDevices.getSupportedConstraints?.().suppressLocalAudioPlayback) {
audio.suppressLocalAudioPlayback = true;
}
const stream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio,
});
if (!stream.getAudioTracks().length) {
stream.getTracks().forEach((track) => track.stop());
throw new Error("Choose a browser tab and enable tab audio.");
}
return stream;
}
```
### 开启 WebRTC 翻译会话
使用短期的客户端密钥,将浏览器的 SDP 提议 POST 到 Realtime Translation 通话端点。音频输出将作为远程轨道到达。翻译和输入转录增量将通过数据通道到达。
```javascript
const sessionResponse = await fetch("/session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ targetLanguage }),
});
const session = await sessionResponse.json();
const pc = new RTCPeerConnection();
const events = pc.createDataChannel("oai-events");
for (const track of sourceStream.getAudioTracks()) {
pc.addTrack(track, sourceStream);
}
pc.ontrack = ({ streams }) => {
translatedAudio.srcObject = streams[0];
};
events.onmessage = ({ data }) => {
const event = JSON.parse(data);
if (event.type === "session.output_transcript.delta") {
translatedSubtitles.textContent += event.delta;
}
if (event.type === "session.input_transcript.delta") {
sourceTranscript.textContent += event.delta;
}
};
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
const sdpResponse = await fetch(
"https://api.openai.com/v1/realtime/translations/calls",
{
method: "POST",
headers: {
Authorization: `Bearer ${session.client_secret}`,
"Content-Type": "application/sdp",
},
body: offer.sdp,
}
);
await pc.setRemoteDescription({ type: "answer", sdp: await sdpResponse.text() });
```
运行完整演示:
```bash
cd examples/voice_solutions/realtime_translation_guide/browser-translation-demo
npm install
npm run dev
```
然后打开本地 URL,选择一个包含音频的标签页,选择目标语言,然后开始翻译。
接下来,让我们将 `gpt-realtime-translate` 集成到 Twilio 通话路径中。当每个参与者拥有独立的音频流时,对话式翻译效果最佳。Twilio Media Streams 为你的后端提供了一个用于电话音频的服务器端 WebSocket,你的服务器负责处理格式转换边界:接收呼叫者音频,将其转换为 Realtime Translation 所需的格式,然后将翻译后的音频转换回 Twilio 媒体消息。
Twilio 电话翻译架构
### 工作原理
1. 呼叫者加入并说出其首选的输出语言。
2. Twilio 将该呼叫者的音频流式传输到你的后端。
3. 你的后端将 Twilio 音频转换为 Realtime Translation 期望的音频格式。
4. 对于每个翻译方向,你的后端使用监听者的目标语言开启一个 Realtime Translation 会话。
5. 当翻译音频从 OpenAI 返回时,你的后端将其转换回 Twilio 媒体消息。
6. Twilio 将翻译后的音频播放给另一方参与者。
对于双人通话,这通常意味着两个翻译会话:A 到 B 和 B 到 A。对于更大的通话,请根据谁需要用哪种语言听哪位源发言者的声音来创建翻译会话,而不是将所有参与者混合到一个共享流中。
> **生产环境注意事项:** `gpt-realtime-translate` 可能不会翻译已经处于监听者所选输出语言的音频。由于此演示未将原始 Twilio 音频透传给另一方参与者,同语言发言可能导致静音。生产环境的电话桥接应添加原始音频透传或混音功能。此演示以一对一的方式配对呼叫者;额外的呼叫者将等待另一位呼叫者并组成单独的配对。
### 配置 Twilio Webhook
为你的服务器创建一个公共端点,然后设置 Twilio 电话号码的 Voice webhook (https://www.twilio.com/docs/usage/webhooks/voice-webhooks),将呼入电话路由到你的应用。对于本地开发,请使用隧道工具暴露服务器,或将其部署在 Twilio 可通过 443 端口访问的位置。同一个主机必须同时服务于 HTTP webhook 路由和 WebSocket Media Stream 路由。
### 询问呼叫者目标语言
当呼叫者拨入时,返回 TwiML (https://www.twilio.com/docs/voice/twiml),询问他们希望翻译为何种语言。
```
你想听什么语言? /choose-language
```
### 启动 Twilio Media Stream
当呼叫者选择支持的语言后,返回一个双向 Media Stream。将所选语言作为自定义参数传递,以便 WebSocket 处理器使用该首选输出语言注册呼叫者。
```
你选择了西班牙语。等待另一位呼叫者,然后开始说话。
```
### 开启服务器端翻译会话
由于此集成运行在你的后端,你不需要浏览器客户端密钥。直接使用你的 OpenAI API 密钥从服务器打开翻译 WebSocket,并在 URL 中选择模型。
```javascript
import WebSocket from "ws";
function openTranslationSession({ targetLanguage }) {
const ws = new WebSocket(
"wss://api.openai.com/v1/realtime/translations?model=gpt-realtime-translate",
{
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
},
}
);
ws.on("open", () => {
ws.send(JSON.stringify({
type: "session.update",
session: {
audio: {
input: {
transcription: { model: "gpt-realtime-whisper" },
noise_reduction: { type: "near_field" },
},
output: { language: targetLanguage },
},
},
}));
});
return ws;
}
```
### 将 Twilio 音频桥接至 Realtime Translation
Twilio 以 8 kHz 发送 base64 编码的 `audio/x-mulaw`。Realtime Translation 期望接收 24 kHz 的小端 PCM16 base64 音频,因此桥接器会解码 u-law,重采样至 24 kHz,并追加音频缓冲区。请保持连续发送音频(包括静音),因为翻译会话不是基于回合的,也不会等待 `response.create`。
```javascript
function sendTwilioAudioToTranslation(realtimeWs, twilioMessage) {
if (twilioMessage.event !== "media") return;
const realtimeAudio = twilioMediaToRealtimeAudio(
twilioMessage.media.payload
);
realtimeWs.send(JSON.stringify({
type: "session.input_audio_buffer.append",
audio: realtimeAudio,
}));
}
```
### 将翻译音频桥接回 Twilio
Realtime Translation 以 base64 24 kHz PCM16 格式在 `session.output_audio.delta` 中发出翻译音频。在将其发送给 Twilio 之前,请将其转换回 8 kHz u-law,并包装在 Twilio 媒体消息中。
```javascript
function sendTranslationToTwilio(twilioWs, streamSid, realtimeEvent) {
if (realtimeEvent.type !== "session.output_audio.delta") return;
const twilioPayload = realtimeAudioToTwilioMedia(realtimeEvent.delta);
twilioWs.send(JSON.stringify({
event: "media",
streamSid,
media: {
payload: twilioPayload,
},
}));
}
```
### 配对呼叫者并为每个方向创建会话
当两名呼叫者都在等待时,将他们配对并开启两个 Realtime Translation 会话。每个会话的输出语言是监听者选择的目标语言,而非说话者的语言。
```javascript
function pairCallers(a, b) {
const aToB =