本文介绍实时语音识别的 WebSocket 协议,包括鉴权、请求指令、响应事件和交互流程,适用于不使用 SDK、直接开发客户端的场景。
功能介绍
实时语音识别通过 WebSocket 接收音频流并返回转写结果,支持长语音。指令和事件使用 JSON 格式的文本帧(Text Frame),音频使用二进制帧(Binary Frame)。关于帧类型,请参见 Data Frames。
支持的输入格式:PCM、PCM 编码的 WAV、OGG 封装的 OPUS、OGG 封装的 SPEEX、AMR、MP3、AAC。音频需为单声道,PCM 采样位数为 16 bit。
支持的音频采样率:8000 Hz、16000 Hz。音频、请求参数和项目所选模型的采样率需匹配。
支持返回中间识别结果、添加标点、将中文数字转换为阿拉伯数字,以及返回词信息。
语种和方言模型在项目中配置,不能通过本协议的请求参数切换。模型配置方法请参见管理项目。
鉴权
服务端使用临时 Token 鉴权。建立 WebSocket 连接时,在 URL 的 token 参数中传入有效 Token。
获取方法请参见获取 Token。
将以下 URL 中的 <your_token> 替换为实际 Token。
访问类型 | 说明 | URL |
公网访问 | 通过公网连接服务。 |
|
上海 ECS 内网访问 | 适用于华东 2(上海)地域的 VPC 内 ECS 实例。经典网络不支持此访问方式。通过内网访问不产生 ECS 公网流量费用。 |
|
交互流程
指令和音频流按以下顺序发送:
携带 Token 建立 WebSocket 连接。
发送
StartTranscription指令,等待TranscriptionStarted事件。以二进制帧分片发送音频,同时处理
SentenceBegin、TranscriptionResultChanged和SentenceEnd事件。中间结果仅在开启相应参数后返回。音频发送完毕后,发送
StopTranscription指令。收到
TranscriptionCompleted后关闭连接。发生错误时,读取TaskFailed事件中的状态码和错误信息。
指令
指令通过 JSON 文本帧发送,包含 header 和可选的 payload。以下 JSON 为协议结构示例;调用时替换 Appkey,并为任务和消息生成实际 ID。
Header 格式
参数 | 类型 | 必选 | 说明 |
appkey | String | 是 | 项目的 Appkey。获取方法请参见管理项目。 |
message_id | String | 是 | 当前消息的唯一 ID,由客户端生成,包含 32 个十六进制字符。每条指令使用新的 ID。 |
task_id | String | 是 | 当前识别任务的唯一 ID,包含 32 个十六进制字符。同一任务的所有指令保持一致。 |
namespace | String | 是 | 固定为 |
name | String | 是 | 指令名称: |
StartTranscription 指令
payload 参数如下:
参数 | 类型 | 必选 | 说明 |
format | String | 否 | 音频格式,默认 |
sample_rate | Integer | 否 | 音频采样率,单位为 Hz,默认 |
enable_intermediate_result | Boolean | 否 | 是否返回中间识别结果,默认 |
enable_punctuation_prediction | Boolean | 否 | 是否在后处理中添加标点,默认 |
enable_inverse_text_normalization | Boolean | 否 | 是否启用逆文本规范化(ITN),将中文数字转换为阿拉伯数字,默认 |
customization_id | String | 否 | 自学习模型 ID。 |
vocabulary_id | String | 否 | 定制泛热词 ID。 |
max_sentence_silence | Integer | 否 | 断句静音阈值,单位为毫秒。静音时长超过该阈值时判定断句。取值范围为 |
enable_words | Boolean | 否 | 是否返回词信息,默认 |
disfluency | Boolean | 否 | 是否过滤转写文本中的语气词,默认 |
speech_noise_threshold | Float | 否 | 噪音判定阈值,范围为 |
enable_semantic_sentence_detection | Boolean | 否 | 是否启用语义断句,默认 |
{
"header": {
"message_id": "05450bf69c53413f8d88aed1ee600001",
"task_id": "640bc797bb684bd69601856513070001",
"namespace": "SpeechTranscriber",
"name": "StartTranscription",
"appkey": "<your_appkey>"
},
"payload": {
"format": "pcm",
"sample_rate": 16000,
"enable_intermediate_result": true,
"enable_punctuation_prediction": true,
"enable_inverse_text_normalization": true
}
}StopTranscription 指令
通知服务端音频发送完毕并停止转写。此指令无需 payload。
{
"header": {
"message_id": "05450bf69c53413f8d88aed1ee600002",
"task_id": "640bc797bb684bd69601856513070001",
"namespace": "SpeechTranscriber",
"name": "StopTranscription",
"appkey": "<your_appkey>"
}
}事件
服务端通过 JSON 文本帧返回事件。header 标识任务、事件名称和状态,payload 包含该事件的数据。成功响应的 header.status 为 20000000,状态说明位于 header.status_text。以下为省略部分字段的结构示例,文本和时间值仅用于说明字段含义。
TranscriptionStarted 事件
表示服务端已准备好接收音频。收到该事件后再发送二进制音频帧;通过 header.task_id 关联任务,不依赖响应中存在 payload.session_id。
{
"header": {
"namespace": "SpeechTranscriber",
"name": "TranscriptionStarted",
"task_id": "640bc797bb684bd69601856513070001",
"status": 20000000,
"status_text": "Gateway:SUCCESS:Success."
}
}SentenceBegin 事件
表示服务端检测到一句话的开始。
Payload 参数 | 类型 | 说明 |
index | Integer | 句子编号,从 |
time | Integer | 相对于整个音频流起点的句子开始时间,单位为毫秒。 |
{
"header": {"name": "SentenceBegin", "status": 20000000},
"payload": {"index": 1, "time": 0}
}TranscriptionResultChanged 事件
开启 enable_intermediate_result 后,识别结果变化时返回此事件。
Payload 参数 | 类型 | 说明 |
index | Integer | 句子编号,从 |
time | Integer | 当前已处理的音频时长,单位为毫秒。 |
result | String | 当前的中间识别结果。 |
words |
| 词信息数组,开启 |
status | Integer | 句子级状态码(如返回),与 |
Word 结构
参数 | 类型 | 说明 |
text | String | 词文本。 |
startTime | Integer | 词开始时间,单位为毫秒。 |
endTime | Integer | 词结束时间,单位为毫秒。 |
示例
{
"header": {"name": "TranscriptionResultChanged", "status": 20000000},
"payload": {
"index": 1,
"time": 1000,
"result": "今天天气",
"words": [
{"text": "今天", "startTime": 0, "endTime": 500},
{"text": "天气", "startTime": 500, "endTime": 1000}
]
}
}SentenceEnd 事件
表示服务端检测到一句话的结束。
Payload 参数 | 类型 | 说明 |
index | Integer | 句子编号,从 |
time | Integer | 当前已处理的音频时长,单位为毫秒。 |
begin_time | Integer | 对应 |
result | String | 当前句子的最终识别结果。 |
confidence | Double | 结果置信度,范围为 |
words |
| 词信息数组,开启 |
status | Integer | 句子级状态码。正常识别结果可返回 |
stash_result | StashResult | 暂存结果。启用语义断句后可包含下一句尚未断句的中间结果;其中的文本也可能为空。 |
StashResult 结构
参数 | 类型 | 说明 |
sentenceId | Integer | 句子编号,从 |
beginTime | Integer | 句子开始时间。 |
text | String | 暂存的转写内容。 |
currentTime | Integer | 当前处理时间。 |
示例
{
"header": {"name": "SentenceEnd", "status": 20000000},
"payload": {
"index": 1,
"time": 1500,
"begin_time": 0,
"result": "今天天气很好。"
}
}TranscriptionCompleted 事件
发送 StopTranscription 后,服务端完成转写并返回该事件。
{
"header": {
"namespace": "SpeechTranscriber",
"name": "TranscriptionCompleted",
"task_id": "640bc797bb684bd69601856513070001",
"status": 20000000,
"status_text": "Gateway:SUCCESS:Success."
}
}TaskFailed 事件
请求失败时,读取 header.status 和 header.status_text 定位问题。例如,消息 ID 格式无效时返回 40000002。该值是服务端业务错误码,不是 WebSocket Close 帧的状态码。
JavaScript 示例代码
可以参考实时语音识别前端 demo.html了解浏览器录音和音频发送方式。
常见问题
如何发送音频?每次发送多少字节?
音频通过二进制帧发送,不放入指令的 JSON payload。按音频时长分片发送,例如 16000 Hz、16 bit、单声道 PCM 每 100 ms 对应 3200 字节,8000 Hz 时对应 1600 字节。分片大小不限定为这两个值;发送节奏需与音频时长匹配。
为什么返回 Invalid message id?
检查 message_id 是否包含 32 个十六进制字符。仅长度为 32、但包含非十六进制字符的字符串仍会被拒绝。task_id 也需符合格式要求。
发送音频后连接断开,如何排查?
先记录服务端返回的 TaskFailed 事件、task_id、状态码和错误信息,再检查 Token 是否有效、请求字段是否正确、音频格式是否受支持,以及客户端是否持续按协议发送音频。没有收到错误事件时,再检查客户端的错误和连接关闭日志。客户端设置 status 不能改变服务端处理结果。
如何生成 message_id 和 task_id?
由客户端生成 32 个十六进制字符组成的唯一 ID。同一识别任务始终使用同一个 task_id;每次发送指令时生成新的 message_id。
如何持续发送实时音频?
客户端持续采集音频并分片发送,同时接收服务端事件。停止采集并发送完剩余音频后,再发送 StopTranscription。使用本地文件模拟实时音频流的代码,请参见Java SDK。