跳到主要内容

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>

会话流程​

  1. 建立 WebSocket 连接,等待服务端返回 session.created。
  2. 发送 session.update,配置模型、音频格式和分句模式。
  3. 收到 session.updated 后,通过 input_audio_buffer.append 持续发送 Base64 编码的音频块。
  4. 服务端返回实时识别结果,并按所选分句模式完成分句:server_vad 模式下自动分句,手动模式下由客户端 input_audio_buffer.commit 定界。
  5. 音频发送完毕并收到最终结果后,由客户端关闭连接;若长时间没有语音输入,服务端会在静音超时后主动断开。

分句模式​

分句模式由 session.audio.input.turn_detection 指定:

  • server_vad(turn_detection 缺省,或显式传入 {"type": "server_vad"}):服务端根据静音自动判定句边界,客户端无需干预。
  • 手动分句(显式传入 turn_detection: null):句边界由客户端 input_audio_buffer.commit 决定;input_audio_buffer.clear 可丢弃当前未提交的音频。

各模型分别支持哪种模式,见模型列表的 turn_detection列。传入该模型不支持的模式会在 session.update 时被拒绝。

静音超时​

若 60 秒内没有识别出任何文本,服务端会发送 error(code: "51005")并主动断开连接。

注意:部分上游服务商在一定时间内未收到音频帧时会主动断开连接,请客户端自行处理此类问题,例如发送静音音频帧、监听连接关闭事件并按需重连等。

时序​

每条消息均为 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.typestring固定为 transcription。当前不支持完整的 realtime 会话。
session.audio.input.transcription.modelstring模型广场中标记为支持实时 WebSocket 的 ASR 模型 ID。不同模型的音频格式和采样率要求可能不同。
session.audio.input.transcription.keywordsstring,可选热词提示,逗号分隔字符串(与 HTTP 转录接口的 hotwords 参数一致),按模型能力生效。
session.audio.input.format.audio_formatstring音频编码类型。仅支持可按字节精确计时的 PCM 格式(pcm* 及其容器 wav);opus/flac/aac 等压缩格式因无法按转发字节计费而会被拒绝。经 provider_params 重写的格式同样受限。
session.audio.input.format.sample_rateinteger必填。音频采样率,单位 Hz,必须为正整数并符合所选模型要求。
session.audio.input.format.channelsinteger声道数。示例使用单声道 1。
session.audio.input.turn_detection.typestring,可选分句模式,见分句模式。传入 {"type": "server_vad"} 或省略该字段表示 server_vad 自动分句;显式传入 null 表示手动分句。
session.provider_paramsstring,可选原生参数透传通道,用于传入上表未覆盖的模型专属参数(与 HTTP 转录接口的 provider_params 参数一致),其值为一个 JSON 字符串。当某项参数同时通过上表字段设置时,以 provider_params 中的值为准(model 除外)。

input_audio_buffer.append​

发送一段音频。audio 必须是原始音频字节经 Base64 编码后得到的字符串。

{
"type": "input_audio_buffer.append",
"audio": "<Base64 编码的音频字节>"
}

建议客户端按照音频实际速率发送时长约为 100-200 ms 的音频块。server_vad 模式下分句由服务端完成,无需调用 commit / clear(调用会收到 error);手动分句模式下必须在发送完毕后调用 commit 才会得到该句的最终结果。

input_audio_buffer.commit​

手动分句模式下用于结束当前句子:Gateway 会把此前 append 的音频作为一句处理,返回该句的最终结果。

{
"type": "input_audio_buffer.commit"
}

该事件仅在手动分句模式下有效

input_audio_buffer.clear​

手动分句模式下用于丢弃当前未提交的音频:Gateway 会丢弃已接收但尚未 commit 的缓冲,不产生识别结果。

{
"type": "input_audio_buffer.clear"
}

该事件仅在手动分句模式下有效,server_vad 模式下发送会收到 error。clear 是静默操作:不返回任何其他事件,此前已经被识别并完成分句的部分不受影响。

服务端事件​

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。

判定出分句(server_vad 模式由服务端判定,手动分句模式由客户端的 commit 触发)后,事件顺序为 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 asyncio
import base64
import 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)。