GPT-Live 实时语音会话
GPT-Live(gpt-live-1)是全双工实时语音模型:可以边听边说、随时被打断;需要检索或调用工具时,它把工作委托给您的应用或 Responses 模型完成,对话同时继续。图灵平台提供两种传输:浏览器和 App 使用 WebRTC,由您的服务端创建会话、音频直连服务;服务端和设备也可以通过图灵的 WebSocket 收发音频。
本页说明接入流程、会话控制与计费。请求、响应字段见 API 参考 → Create a GPT-Live WebRTC session 与 Stop a running GPT-Live session。
GPT-Live 使用独立的会话协议,接口和事件与 turing/gpt-realtime 系列不通用。GPT Realtime 的用法见 GPT Realtime 使用指南。
接口概览
| 项目 | 说明 |
|---|---|
| 创建会话(WebRTC) | POST /api/v1/live/sessions |
| WebSocket 会话 | wss://live-turing.cn.llm.tcljd.com/ws/v1/live/sessions,见WebSocket 传输 |
| 停止会话 | POST /api/v1/live/sessions/{session_id}/close |
| 模型 | gpt-live-1 |
| 传输方式 | WebRTC:音频走媒体轨,会话事件走名为 oai-events 的 data channel;WebSocket:音频与事件都走同一条连接 |
| 委托 | client(默认)或 responses,见委托 |
| 鉴权 | Authorization: Bearer $TURING_API_KEY,只在服务端使用 |
| 计费 | 语音按会话时长 $0.05 / 分钟、按秒计费;Responses 委托另按所用模型计费 |
| 支持地区 | 中国区 |
选择传输方式
| 传输 | 适用场景 | 说明 |
|---|---|---|
| WebRTC | 浏览器、App | 延迟最低,浏览器内置回声消除与编解码;媒体直连服务端的 3478 端口(UDP / TCP),需要网络放行 |
| WebSocket | 您的服务端、设备、封锁 UDP 的网络 | 走 TCP 443,能访问 HTTPS 即可使用;由调用方采集、编码和播放 24 kHz PCM16 音频 |
两种传输的会话事件、委托、停止接口与计费完全相同。
WebRTC 接入流程
- 浏览器创建
RTCPeerConnection,加入麦克风音轨和名为oai-events的 data channel,生成 SDP offer。 - 浏览器把 offer 发给您的服务端。
- 您的服务端用 Turing API Key 调用创建会话接口,拿到
session.id与 SDP answer。 - 浏览器设置 answer 后,音频在浏览器与 GPT-Live 之间直连,会话事件通过 data channel 收发。
GPT-Live 不提供临时客户端密钥,创建会话必须在持有 API Key 的服务端完成。浏览器只与您的服务端交换 SDP。
浏览器端
const audioElement = document.querySelector("audio");
const pc = new RTCPeerConnection();
pc.ontrack = (event) => {
audioElement.srcObject = event.streams[0];
};
const microphone = await navigator.mediaDevices.getUserMedia({ audio: true });
pc.addTrack(microphone.getAudioTracks()[0], microphone);
const events = pc.createDataChannel("oai-events");
events.addEventListener("message", ({ data }) => {
const event = JSON.parse(data);
console.log(event.type, event);
});
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
// 您自己的服务端接口,由它调用 POST /live/sessions(见下一节)
const { session_id, answer_sdp } = await fetch("/your-server/live-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sdp: offer.sdp }),
}).then((response) => response.json());
await pc.setRemoteDescription({ type: "answer", sdp: answer_sdp });
服务端:创建会话
服务端把浏览器的 SDP offer 连同会话配置发给平台,再把 SDP answer 返回浏览器。请求与响应遵循 GPT-Live WebRTC 协议,平台原样透传。
- cURL
- Python
- Node.js
# OFFER_SDP 为浏览器发给您服务端的 SDP offer
curl "https://live-turing.cn.llm.tcljd.com/api/v1/live/sessions" \
-H "Authorization: Bearer $TURING_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg sdp "$OFFER_SDP" '{
session: {model: "gpt-live-1", instructions: "Be concise.", delegation: {type: "client"}},
transport: {type: "webrtc", sdp: $sdp}
}')"
import os
import requests
def create_live_session(offer_sdp: str) -> dict:
response = requests.post(
"https://live-turing.cn.llm.tcljd.com/api/v1/live/sessions",
headers={"Authorization": f"Bearer {os.environ['TURING_API_KEY']}"},
json={
"session": {
"model": "gpt-live-1",
"instructions": "Be concise.",
"delegation": {"type": "client"},
},
"transport": {"type": "webrtc", "sdp": offer_sdp},
},
timeout=60,
)
response.raise_for_status()
created = response.json()
return {
"session_id": created["session"]["id"],
"answer_sdp": created["transport"]["sdp"],
"trace_id": response.headers.get("x-turing-trace-id"),
}
export async function createLiveSession(offerSdp) {
const response = await fetch("https://live-turing.cn.llm.tcljd.com/api/v1/live/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.TURING_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
session: { model: "gpt-live-1", instructions: "Be concise.", delegation: { type: "client" } },
transport: { type: "webrtc", sdp: offerSdp },
}),
});
if (!response.ok) {
throw new Error(`GPT-Live session failed: ${response.status} ${await response.text()}`);
}
const created = await response.json();
return {
sessionId: created.session.id,
answerSdp: created.transport.sdp,
traceId: response.headers.get("x-turing-trace-id"),
};
}
- 创建成功返回
201,session.id用于停止会话和排查问题。 - 响应头
x-turing-trace-id是这次会话的追踪 ID,结算后可以用它查询计费(见计费与限额)。 session对象由上游严格校验,包含未知字段的请求会被拒绝。可用字段:model(必填,gpt-live-1)、instructions、audio.output.voice、delegation(见委托)。其中model、instructions和音色在会话开始后不可修改。- 可选查询参数
idle_timeout_seconds:会话连续这么多秒没有人说话(没有转写事件)时,平台主动关闭会话,例如POST /api/v1/live/sessions?idle_timeout_seconds=300。不传时使用平台的 1 小时,更大的值也按 1 小时处理。取值必须是正整数,否则返回400(错误码1004)。
WebSocket 传输
连接 wss://live-turing.cn.llm.tcljd.com/ws/v1/live/sessions(也可以用 /api/openai/v1/live/sessions),用请求头 Authorization: Bearer $TURING_API_KEY 鉴权,只在服务端或设备上使用。连接后按 GPT-Live 的 WebSocket 协议收发事件:
- 发送
session.start,session对象与创建 WebRTC 会话时相同;收到session.started后会话开始。 - 用
session.input_audio.append发送 base64 编码的 24 kHz 单声道 PCM16 音频,从session.output_audio.delta取回模型语音。 - 发送
session.close,收到session.closed后连接关闭。
握手响应头 x-turing-trace-id 是这次会话的追踪 ID。连接 URL 同样可以带 idle_timeout_seconds(例如 /ws/v1/live/sessions?idle_timeout_seconds=300),规则与 WebRTC 相同。连接时同样会检查预算、限流、同时进行的会话数和 idle_timeout_seconds,不满足时收到错误消息后连接以关闭码 1008 断开。平台拒绝的命令(例如把委托换成不支持的模型)会收到带 client_event_id 的 error 事件,且不会发给上游,会话继续。
- Python
- Node.js
import asyncio
import base64
import json
import os
import websockets
async def speak_then_close(ws, audio: str) -> None:
await ws.send(json.dumps({"type": "session.input_audio.append", "audio": audio}))
await asyncio.sleep(10) # 留出时间让模型回答
await ws.send(json.dumps({"type": "session.close"}))
async def main() -> None:
async with websockets.connect(
"wss://live-turing.cn.llm.tcljd.com/ws/v1/live/sessions",
extra_headers={"Authorization": f"Bearer {os.environ['TURING_API_KEY']}"},
) as ws:
print("trace id:", ws.response_headers.get("x-turing-trace-id"))
await ws.send(
json.dumps({"type": "session.start", "session": {"model": "gpt-live-1", "instructions": "Be concise."}})
)
# hello_24khz_mono.pcm:无文件头的 24 kHz 单声道 PCM16 音频
audio = base64.b64encode(open("hello_24khz_mono.pcm", "rb").read()).decode()
async for message in ws:
event = json.loads(message)
if event["type"] == "session.started":
asyncio.ensure_future(speak_then_close(ws, audio))
elif event["type"] == "session.output_audio.delta":
pcm16 = base64.b64decode(event["delta"]) # 交给您的播放器
elif event["type"] == "session.closed":
print("usage:", event["usage"])
asyncio.run(main())
import fs from "node:fs";
import WebSocket from "ws";
const ws = new WebSocket("wss://live-turing.cn.llm.tcljd.com/ws/v1/live/sessions", {
headers: { Authorization: `Bearer ${process.env.TURING_API_KEY}` },
});
ws.on("upgrade", (response) => console.log("trace id:", response.headers["x-turing-trace-id"]));
ws.on("open", () => {
ws.send(JSON.stringify({ type: "session.start", session: { model: "gpt-live-1", instructions: "Be concise." } }));
});
ws.on("message", (data) => {
const event = JSON.parse(data.toString());
if (event.type === "session.started") {
// hello_24khz_mono.pcm:无文件头的 24 kHz 单声道 PCM16 音频
const audio = fs.readFileSync("hello_24khz_mono.pcm").toString("base64");
ws.send(JSON.stringify({ type: "session.input_audio.append", audio }));
setTimeout(() => ws.send(JSON.stringify({ type: "session.close" })), 10_000);
} else if (event.type === "session.output_audio.delta") {
const pcm16 = Buffer.from(event.delta, "base64"); // 交给您的播放器
} else if (event.type === "session.closed") {
console.log("usage:", event.usage);
}
});
会话事件
两种传输的常用事件(WebRTC 走 data channel):
| 事件 | 说明 |
|---|---|
session.started | 会话已开始 |
session.input_transcript.delta / session.output_transcript.delta | 用户与模型语音的转写片段,按音频节奏分段,不代表完整轮次 |
session.delegation.created | 模型把一项工作交给您的应用;完成后用 session.thinking.append 或 session.commentary.append 带上 delegation_id 回填结果 |
response.event | Responses 委托的事件信封,按内层 event.type 处理,例如 response.output_item.done、response.completed |
session.usage.updated | 累计语音时长(usage.seconds),约每分钟一次;是累计值,不要相加 |
session.closed | 会话已结束,带最终 usage.seconds 与结束原因 reason |
error | 启动、校验或命令错误 |
完整事件定义见 Azure GPT-Live 事件参考。
委托
对话中需要检索、调用工具或深入推理时,GPT-Live 会把这部分工作委托出去,语音对话同时继续。委托方式在创建会话时通过 session.delegation 指定。
client 委托
默认方式。data channel 收到 session.delegation.created(target 为 client)后,由您的应用完成工作,再用 session.thinking.append(静默上下文)或 session.commentary.append(让模型说出来)带上 delegation_id 回填结果。平台只按语音时长计费。
Responses 委托
由 Responses 模型在服务端完成工作:
{
"delegation": {
"type": "responses",
"responses": {
"model": "gpt-6-sol",
"instructions": "Use tools when current information is required.",
"tools": [{ "type": "web_search" }]
}
}
}
- 可用模型:
gpt-6-sol、gpt-6-luna、gpt-6-astra、gpt-5.6-sol、gpt-5.6-terra、gpt-5.6-luna,也接受带turing/前缀的写法。价格见模型列表。 - 可用工具:
function与web_search,其他工具类型会被拒绝(400,错误码1002)。 - 每次委托运行按所选模型计费,计费方式与调用
/responses相同:包括缓存 token、web_search按次计费;service_tier为priority时按 Priority 价格计费,flex按标准价格计费。 - 委托的事件包在
response.event信封里。遇到function调用时,从内层response.output_item.done取出call_id与参数,由您的应用执行,再用response.item.create提交function_call_output,并发送response.create继续本轮委托。 - 会话中途通过
session.update把委托换成不支持的模型或工具时,平台会立即关闭会话。模型无法识别的委托运行按可用模型中最贵的价格计费。
结束会话
以下两种方式都可以结束会话,平台随后按上游的最终用量结算:
- 浏览器在 data channel 发送
{"type": "session.close"}。上游发出session.closed后会立即关闭 data channel,这条事件不一定能先送到浏览器,所以收到session.closed或 data channel 关闭都表示会话已结束,之后再关闭RTCPeerConnection。 - 服务端调用停止接口。会话创建者或管理员可以调用,平台通常在 5 秒内关闭会话。这时浏览器收不到
session.closed,只会看到 data channel 和连接关闭。
curl -X POST "https://live-turing.cn.llm.tcljd.com/api/v1/live/sessions/$SESSION_ID/close" \
-H "Authorization: Bearer $TURING_API_KEY"
停止接口返回 202,data.status 为 closing(已通知关闭,等待上游确认)或 closed(会话已经结束)。
计费与限额
- 按语音会话时长计费:
$0.05/ 分钟,按秒计。计费时长覆盖整个会话,包括静音和模型处理委托工作的时间。 - 会话结束后异步结算。用创建会话时返回的
x-turing-trace-id调用/trace/{trace_id}/billing,可以查到gpt-live-1的计费记录。 - 会话进行中,上游每次上报累计用量(约每分钟一次)以及每次委托运行结束时,平台会先把这部分费用计入当月花费,会话结束后再结算剩余部分;计费记录仍是整场会话。
- 上游没有返回最终用量时,平台按会话从创建到结束的时长结算。
- Responses 委托的每次运行按所选模型另行计费,与语音记录在同一个 trace 中,可以在
/trace/{trace_id}/billing看到对应模型的计费记录。 - 创建会话前平台会检查预算与限流(包括委托模型);已超出时返回
429(错误码1110/1104/1115),不会创建会话。会话进行中,每次计入花费后平台都按用户当前的额度检查预算,当月预算用完(包括会话期间额度被调低)时主动关闭会话。 - 每个用户最多同时进行 3 个会话。超出时创建会话返回
429(错误码1115),WebSocket 连接则收到同一错误码的错误消息后断开;会话结束后名额自动释放。 - 静音同样计费,因此会话中连续 1 小时没有人说话(没有转写事件)时,平台主动关闭会话;创建会话时可以用
idle_timeout_seconds设置更短的空闲超时。 - 单个会话最长约 3 小时 10 分钟(11,400 秒,约
$9.50);单会话费用上限$10,按语音与委托费用合计。达到任一上限时平台主动关闭会话。单次委托运行在结束后才计入上限,因此最后一次运行可能让会话略超过$10。 - 平台主动关闭会话时,WebSocket 调用方会收到
session.closed,WebRTC 浏览器只会看到 data channel 和连接关闭。关闭原因记录在 trace 的extra_info.gpt_live_terminated_by:max_duration/max_cost(单会话上限)、over_budget(预算用完)、idle(空闲超时)、manual(停止接口)、delegation_policy(委托超出允许范围)。
计费口径与查询方式见 计费与用量。
限制
- WebRTC 媒体直连服务端的 3478 端口(UDP / TCP);网络不放行时 data channel 无法建立,请改用 WebSocket 传输。
- Responses 委托只支持上文列出的模型和
function/web_search工具。 - 上游按订阅限制 GPT-Live 的并发会话数,高峰期创建会话可能返回上游的限流错误。
错误处理
401:缺少或无效的 Turing API Key。400:委托的模型或工具不受支持(错误码1002)、委托配置格式错误(错误码1050),或idle_timeout_seconds不是正整数(错误码1004),不会创建会话。429:超出预算、限流或同时进行的会话数上限,不会创建会话。- 其他
4xx:上游拒绝了请求(例如 SDP 无法解析、会话配置包含未知字段),状态码与错误体原样透传。 - 停止接口:
403表示只能停止自己创建的会话,404表示会话不存在或已结算完成。
错误响应中的 trace_id 可用于请求追踪,错误码含义见错误码参考。