EVA Gateway WebSocket API
EVA Gateway 提供基于 WebSocket 的 ASR 双向流式接口,可在持续发送音频的同时接收实时识别结果。该接口兼容 OpenAI Realtime API 的转录事件子集;模型、音频格式、采样率等参数与 HTTP ASR 接口保持一致,具体参数取值和模型限制请参阅 ASR 参数说明。
当前仅支持 ASR,暂不支持 TTS。 仅部分 ASR 模型支持 WebSocket 实时识别,请在模型广场中查看模型信息、接口能力和音频参数。
连接与鉴权
连接地址:
wss://eva-gateway.autoarkai.com/v1/realtime建立 WebSocket 连接时,须在握手请求头中携带 API Key:
Authorization: Bearer <你的 API_KEY>会话流程
- 建立 WebSocket 连接,等待服务端返回
session.created。 - 发送
session.update,配置模型、音频格式和服务端 VAD。 - 收到
session.updated后,通过input_audio_buffer.append持续发送 Base64 编码的音频块。 - 服务端返回实时识别结果,并通过
server_vad自动完成分句。 - 音频发送完毕并收到最终结果后,由客户端关闭连接;若长时间没有语音输入,服务端会在静音超时后主动断开。
当前仅支持 turn_detection: {"type": "server_vad"} 自动分句,不支持客户端通过 input_audio_buffer.commit 或 input_audio_buffer.clear 手动控制分句。
静音超时
若 60 秒内没有识别出任何文本,服务端会发送 error(code: "51005")并主动断开连接。
注意:部分上游服务商在一定时间内未收到音频帧时会主动断开连接,请客户端自行处理此类问题,例如发送静音音频帧、监听连接关闭事件并按需重连等。
时序
sequenceDiagram
participant C as 客户端
participant G as EVA Gateway
C->>G: WebSocket Upgrade + Authorization
G-->>C: session.created
C->>G: session.update
G-->>C: session.updated
loop 持续发送音频
C->>G: input_audio_buffer.append(audio: Base64)
G-->>C: conversation.item.input_audio_transcription.delta
end
G-->>C: input_audio_buffer.committed
G-->>C: conversation.item.input_audio_transcription.completed
C->>G: 关闭连接每条消息均为 JSON 文本帧。event_id 由 Gateway 生成,用于定位事件;客户端不应依赖其具体格式。
客户端事件
session.update
收到 session.created 后,客户端应发送此事件以初始化转录会话。模型通过 session.audio.input.transcription.model 指定。
{ "type": "session.update", "session": { "type": "transcription", "audio": { "input": { "format": { "audio_format": "pcm", "sample_rate": 16000, "channels": 1 }, "transcription": { "model": "<支持实时 ASR 的模型 ID>", "keywords": "奇多多,无界方舟" }, "turn_detection": { "type": "server_vad" } } }, "provider_params": "{\"key\": \"value\"}" }}| 参数 | 类型 | 说明 |
|---|---|---|
session.type | string | 固定为 transcription。当前不支持完整的 realtime 会话。 |
session.audio.input.transcription.model | string | 模型广场中标记为支持实时 WebSocket 的 ASR 模型 ID。不同模型的音频格式和采样率要求可能不同。 |
session.audio.input.transcription.keywords | string,可选 | 热词提示,逗号分隔字符串(与 HTTP 转录接口的 hotwords 参数一致),按模型能力生效。 |
session.audio.input.format.audio_format | string | 音频编码类型。仅支持可按字节精确计时的 PCM 格式(pcm* 及其容器 wav);opus/flac/aac 等压缩格式因无法按转发字节计费而会被拒绝。经 provider_params 重写的格式同样受限。 |
session.audio.input.format.sample_rate | integer | 必填。音频采样率,单位 Hz,必须为正整数并符合所选模型要求。 |
session.audio.input.format.channels | integer | 声道数。示例使用单声道 1。 |
session.audio.input.turn_detection.type | string | 当前固定为 server_vad,由模型服务自动判断分句结束。 |
session.provider_params | string,可选 | 原生参数透传通道,用于传入上表未覆盖的模型专属参数(与 HTTP 转录接口的 provider_params 参数一致),其值为一个 JSON 字符串。当某项参数同时通过上表字段设置时,以 provider_params 中的值为准(model 除外)。 |
input_audio_buffer.append
发送一段音频。audio 必须是原始音频字节经 Base64 编码后得到的字符串。
{ "type": "input_audio_buffer.append", "audio": "<Base64 编码的音频字节>"}建议客户端按照音频实际速率发送时长约为 100-200 ms 的音频块。当前不支持 input_audio_buffer.commit 和 input_audio_buffer.clear,发送后会收到 error 事件。
服务端事件
session.created
连接建立后,Gateway 发送以下事件:
{ "event_id": "evt_abc12345_1", "type": "session.created", "session": { "id": "abc12345-...", "object": "realtime.session" }}session.updated
配置成功后,Gateway 返回规范化后的会话配置:
{ "event_id": "evt_abc12345_2", "type": "session.updated", "session": { "id": "abc12345-...", "object": "realtime.session", "audio": { "input": { "format": { "audio_format": "pcm", "sample_rate": 16000, "channels": 1 }, "transcription": { "model": "<请求的模型 ID>", "keywords": "奇多多,无界方舟" }, "turn_detection": { "type": "server_vad" } } } }}conversation.item.input_audio_transcription.delta
返回实时识别候选文本:
{ "event_id": "evt_abc12345_8", "type": "conversation.item.input_audio_transcription.delta", "item_id": "item_turn_001", "content_index": 0, "delta": "你好,欢迎使用"}delta 的内容取决于具体模型,可能是当前分句的增量片段,也可能是当前分句的完整候选文本,客户端不要无条件拼接所有 delta。
服务端 VAD 确定分句后,事件顺序为 committed → completed:
input_audio_buffer.committed
{ "event_id": "evt_abc12345_9", "type": "input_audio_buffer.committed", "item_id": "item_turn_001", "previous_item_id": null}conversation.item.input_audio_transcription.completed
{ "event_id": "evt_abc12345_10", "type": "conversation.item.input_audio_transcription.completed", "item_id": "item_turn_001", "content_index": 0, "transcript": "你好,欢迎使用 EVA Gateway。", "usage": { "duration_ms": 1840 }, "credit": 123}error
{ "event_id": "evt_abc12345_11", "type": "error", "error": { "code": "40003", "type": "client", "message": "input_audio_buffer.commit is not supported; turns are detected automatically (server_vad)", "param": null }}Python 示例
import asyncioimport base64import json import websockets async def main(): async with websockets.connect( "wss://eva-gateway.autoarkai.com/v1/realtime", additional_headers={"Authorization": "Bearer <你的 API_KEY>"}, ) as ws: await ws.recv() # session.created # 配置会话 await ws.send(json.dumps({ "type": "session.update", "session": { "type": "transcription", "audio": { "input": { "format": {"audio_format": "pcm", "sample_rate": 16000, "channels": 1}, "transcription": {"model": "<支持实时 ASR 的模型 ID>"}, "turn_detection": {"type": "server_vad"}, } } } })) await ws.recv() # session.updated # 发送音频(16kHz 单声道 PCM,按实际速率每 200ms 发送 200ms 音频) CHUNK_BYTES = 6400 # 200ms @ 16kHz/16bit/mono with open("audio.pcm", "rb") as f: while data := f.read(CHUNK_BYTES): await ws.send(json.dumps({ "type": "input_audio_buffer.append", "audio": base64.b64encode(data).decode(), })) await asyncio.sleep(0.2) # 音频发完后追加 2s 静音,让 server_vad 判定句子结束、下发 completed SILENCE = b"\x00" * (16000 * 4) # 2s 静音(16kHz/16bit/单声道) for i in range(0, len(SILENCE), CHUNK_BYTES): await ws.send(json.dumps({ "type": "input_audio_buffer.append", "audio": base64.b64encode(SILENCE[i:i + CHUNK_BYTES]).decode(), })) await asyncio.sleep(0.2) # 接收识别结果 async for raw in ws: event = json.loads(raw) if event["type"] == "conversation.item.input_audio_transcription.delta": print("实时:", event["delta"]) elif event["type"] == "conversation.item.input_audio_transcription.completed": print("最终:", event["transcript"]) break asyncio.run(main())运行前请将 API_KEY 替换为你的密钥,并按所选模型的要求准备音频文件(上述示例对应 16kHz / 单声道 PCM)。