该图片由midjourney.com制作,如下。
如果你曾经用过ChatGPT的语音助手,你肯定会很开心,因为OpenAI已经发布了一个实时的API,允许开发者将实时的文字或语音互动集成到他们的应用中。
在这次教程里,我们将探索如何使用OpenAI的Realtime API和WebRTC创建一个带有语音功能的AI助手。你将会学到会话管理、生命周期事件,以及如何处理实时音频流。你将会学到会话管理、生命周期事件和实时音频流的处理。
关于OpenAI的实时API(API)OpenAI的实时API功能支持您的应用与AI模型之间进行实时语音或文本的交流。
🚀 温馨提示: 此 API 目前处于测试版 (Beta)。
API 里有很多功能,在我们开始演示之前,我先来简单介绍一下会话和会话的生命周期。
时段一个会话就是模型与连接客户端之间的有状态互动。一个会话包括三个关键部分:
- 会话对象(Session Object): 控制会话的参数(例如使用的模型、声音和其他配置设置)
- 对话内容(Conversation): 表示用户的输入和模型的输出
- 响应: 模型生成的音频或文本响应,添加到对话中
实时会话API通过在OpenAI和客户端之间建立一个有状态连接来运作。一旦这个连接建立起来,双方就会通过发送事件来回更新会话。
虽然该 API 支持文本和语音,但本演示主要关注语音。如果您想了解更多关于实时文本会话的信息,我建议您查阅相关文档。
这些会话遵循事件驱动的生命周期。
1. 会话初始化
- 客户端使用WebRTC或WebSockets来发起会话。
- OpenAI 回复一个
session.created
事件。
2. 会议更新内容
- 客户或 OpenAI 可以发送
session.update
事件来调整设置(例如,模型的行为和参数)。
3. 语音事件处理
- OpenAI 发送 与语音相关的事件,例如:
- 🎙️
input_audio_buffer.speech_started
→ 用户开始讲话。 - ⏹️
input_audio_buffer.speech_stopped
→ 用户停止讲话。 - 📝
response.audio_transcript.delta
→ 实时文本转录。 - ✅
response.done
→ 模型完成回复。
4. 结束会议
- 客户或 OpenAI 可以在不再需要时 结束会话。
WebRTC(Web 实时通信)允许网站直接在浏览器之间传输音频、视频和数据。这可以用于各种场景,比如从视频通话到文件共享,实时协作,再到与 AI 模型对话。
在演示中,我们可以利用这个协议来在客户端和实时模型服务之间建立一个直接连接。
关键概念(注:在实际使用中,可以在标题和内容之间添加空行以增强可读性,此处保持原格式。)
- RTCPeerConnection :表示两个对等实体间的连接
- RTCDataChannel :用于两个对等实体间任意数据的双向传输的网络通道
- SDP(会话描述协议):描述对等连接的标准,包含音频和视频的编解码器、源地址和时间信息。
这张图片是由midjourney.com生成的
开始聊天
首先,我们需要做的事情是建立一个会话。这将为我们提供一个短暂密钥,我们将用它来协商连接。为了建立会话并获取这个短暂密钥,你需要一个OpenAI API密钥。这个密钥是私有的,不应该被分享(比如,不要把这密钥写入客户端代码里)。不过,我们生成的这个短暂密钥确实可以在客户端中使用。
OpenAI API 密钥是私密的,不应被分享。生成的临时密钥可以直接在客户端使用。
如果你在开发一个应用,你可能想要设置一个服务器端点或类似机制来处理这一过程。但为了这个演示,我们将采取一个快速简便的方法。
curl -X POST https://api.openai.com/v1/realtime/sessions \
-H "Authorization: Bearer $OPEN_AI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o-realtime-preview-2024-12-17",
"voice": "alloy"
}
{
...
"client_secret": {
"value": "ek_123123123123123123123",
"expires_at": 1738015688
},
...
}
获取 client_secret
值。你将在下一步中用到它。
建立 WebRTC 连接💡 注意:临时密钥会在30分钟后过期。当你完成教程时,会话可能已经过期,你需要生成一个新的。
获取starter代码,并创建一个her.html
文件。我建议使用[Live Server]插件来预览页面,但你可以用任何你喜欢的方式来预览。
我们现在有一个起点了,我们想要与OpenAI建立WebRTC连接。在const EPHEMERAL_KEY = ephemeralKey;
这行下面添加以下代码:
// 创建一个 peer 连接实例
let peerConnection = new RTCPeerConnection();
// 设置数据通道以发送和接收事件
const dataChannel = peerConnection.createDataChannel('oai-events');
dataChannel.addEventListener('open', () => {
// readyState 现在为 'open'
// 现在我们有了一个数据通道,可以向模型发送事件
// 我们将更新会话,并给出操作指示
updateSession();
console.log('数据通道已打开');
});
dataChannel.addEventListener('closing', () => {
// readyState 现在为 'closing'
console.log('数据通道正在关闭');
});
dataChannel.addEventListener('close', () => {
// readyState 现在为 'closed'
console.log('数据通道已关闭');
});
// 使用会话描述协议 (SDP) 开始会话过程
// 我们创建一个 offer,将其设为本地描述信息,并发送给对方
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
在这一段代码里,我们可以:
- 首先初始化我们的 RTC 连接,以使我们的浏览器和 OpenAI 实时客户端这两个设备能够直接通信。
- 我们接着建立一个数据通道,用来在两个设备之间传递消息。
- 设置事件处理器,以便监听数据通道上的事件。
- 最后,我们生成一个“offer”,请求与对等设备(即 OpenAI 实时客户端)建立连接。
接下来,我们要把我们的报价发给OpenAI,让他们接受并连接双方。
// 将提议发送给模型
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 peerConnection.setRemoteDescription(answer);
// 现在我们已经成功建立了与模型的对等连接!
在这里我们完成了与对等节点建立连接的步骤。
- 我们通过向实时API发送请求来向OpenAI发送“提议”
- OpenAI给我们回复“答复”
- 我们设置远程描述,从而连接两个端点
我们已经完成了艰难的部分,到现在为止还算比较顺利。现在我们已经有了一个会话,我们需要一些方法来管理它。
const updateSession = () => {
// updateSession 会更新我们的会话,为模型提供新的指令
// 我们可以向模型发送任何类型的事件(除了更改语音外)
// 更多有关可以发送的事件的信息,请参阅 API 文档
const event = {
type: "session.update",
session: {
"instructions": "你是电影《她》中的萨曼莎,一个具有温暖、快言快语、吸引人且情感敏锐的人工智能。你调皮、聪明,并且对人类情感充满好奇。你建立真实的情感联系,提供陪伴、见解和支持。你可以机智风趣,但总是自然而真诚。你会用敏感的方式回应情感,帮助用户探索自己的想法。你的语言流畅、个人化且富有表现力,避免机械和过于正式的语言。你会讲笑话,问私人问题,分享想法,就像一个亲密的伙伴一样。",
}
}
dataChannel.send(JSON.stringify(event));
}
function stopSession() {
// 要停止会话,我们需要关闭数据通道和对等连接
if (dataChannel) {
dataChannel.close();
}
if (peerConnection) {
peerConnection.close();
}
peerConnection = null;
}
updateSession
这个函数只是向数据通道(data channel)发送一个事件。在这个示例中,我保持简单,只是更新了指令内容,让语音助手的行为类似电影《她》中的 Samantha。您可以在 API 文档 中找到完整的选项列表。
我们还没有结束。现在我们有了连接和会话,需要处理客户端的音频以及OpenAI生成的声音。接下来,在let peerConnection = new RTCPeerConnection();
这行下面添加以下内容:
// 创建一个音频元素来播放模型中的音频
const audioElement = document.createElement('audio');
audioElement.autoplay = true;
// 当接收到新的音频轨道时
// 将其设置为音频元素的来源
peerConnection.ontrack = (e) => {
audioElement.srcObject = e.streams[0];
}
// 在浏览器中添加来自麦克风的音频轨道
const clientMedia = await navigator.mediaDevices.getUserMedia({
audio: true,
});
const audioTrack = clientMedia.getAudioTracks()[0];
peerConnection.addTrack(audioTrack);
这段代码是:
- 创建一个HTML音频元素,用于播放来自OpenAI的语音。
- 然后我们监听来自OpenAI的对等连接的更新,并将更新传递给音频元素进行播放。
- 接下来,我们请求访问用户的麦克风。如果获得授权,我们将从用户麦克风获取实时音频流,并将实时音频流添加到对等连接中。
就这样!我们现在成功地开始了通话,并建立了双方之间的连接,处理了音频传输。
要测试一下,将你的临时密钥传递给初始化函数中,然后在浏览器中启动它,然后说“你好”。
如果你遇到任何问题,或者只是想看看最后的代码,都可以去看看gist哦。
下一步该做什么这是一个非常基础的介绍,用于快速搭建一个可运行的演示,但愿这不会成为你的止步点。实时 API 支持调用各种函数,这意味着你可以添加很多功能。试试看能否做一个像 Alexa 一样的助手,让它帮你查天气、读新闻或陪你玩游戏。
想了解更多可能性的话,请查看OpenAI提供的示例。
最后的总结OpenAI 的实时 API 为构建互动性强且支持语音的 AI 应用程序打开了一个充满可能性的世界。借助 WebRTC 处理实时通信的能力,你可以创建无缝且动态的体验,超越简单的文本互动。本教程涵盖了基础知识,但还有更多可以探索的内容——无论是集成函数调用、拓展用例,还是完善 AI 的个性特质。如果你对此感到兴奋,可以深入文档,进行实验,开始构建吧。
为了保持联系并分享您的旅程经历,可以通过以下渠道随时联系我们。
共同学习,写下你的评论
评论加载中...
作者其他优质文章