本文介绍如何通过WebSocket连接访问实时语音识别服务。
DashScope SDK目前仅支持Java和Python。若想使用其他编程语言开发Paraformer实时语音识别应用程序,可以通过WebSocket连接与服务进行通信。
用户指南:关于模型介绍和选型建议请参见实时语音识别。
在线体验:仅paraformer-realtime-v2、paraformer-realtime-8k-v2和paraformer-realtime-v1支持在线体验。
WebSocket是一种支持全双工通信的网络协议。客户端和服务器通过一次握手建立持久连接,双方可以互相主动推送数据,因此在实时性和效率方面具有显著优势。
对于常用编程语言,有许多现成的WebSocket库和示例可供参考,例如:
Go:
gorilla/websocket
PHP:
Ratchet
Node.js:
ws
建议您先了解WebSocket的基本原理和技术细节,再参照本文进行开发。
前提条件
已开通服务并获取API Key。请配置API Key到环境变量,而非硬编码在代码中,防范因代码泄露导致的安全风险。
当您需要为第三方应用或用户提供临时访问权限,或者希望严格控制敏感数据访问、删除等高风险操作时,建议使用临时鉴权Token。
与长期有效的 API Key 相比,临时鉴权 Token 具备时效性短(60秒)、安全性高的特点,适用于临时调用场景,能有效降低API Key泄露的风险。
使用方式:在代码中,将原本用于鉴权的 API Key 替换为获取到的临时鉴权 Token 即可。
模型列表
paraformer-realtime-v2(推荐) | paraformer-realtime-8k-v2(推荐) | paraformer-realtime-v1 | paraformer-realtime-8k-v1 | |
适用场景 | 直播、会议等场景 | 电话客服、语音信箱等 8kHz 音频的识别场景 | 直播、会议等场景 | 电话客服、语音信箱等 8kHz 音频的识别场景 |
采样率 | 任意 | 8kHz | 16kHz | 8kHz |
语种 | 中文(包含中文普通话和各种方言)、英文、日语、韩语、德语、法语、俄语 支持的中文方言:上海话、吴语、闽南语、东北话、甘肃话、贵州话、河南话、湖北话、湖南话、江西话、宁夏话、山西话、陕西话、山东话、四川话、天津话、云南话、粤语 | 中文 | 中文 | 中文 |
标点符号预测 | ✅ 默认支持,无需配置 | ✅ 默认支持,无需配置 | ✅ 默认支持,无需配置 | ✅ 默认支持,无需配置 |
逆文本正则化(ITN) | ✅ 默认支持,无需配置 | ✅ 默认支持,无需配置 | ✅ 默认支持,无需配置 | ✅ 默认支持,无需配置 |
定制热词 | ✅ 参见定制热词 | ✅ 参见定制热词 | ||
指定待识别语种 | ✅ 通过 | ❌ | ❌ | ❌ |
情感识别 | ❌ | ❌ | ❌ |
交互流程
客户端发送给服务端的消息有两种:JSON格式的指令和二进制音频(须为单声道音频);服务端返回给客户端的消息称作事件。
按时间顺序,客户端与服务端的交互流程如下:
建立连接:客户端与服务端建立WebSocket连接。
开启任务:
客户端发送run-task指令以开启任务。
客户端收到服务端返回的task-started事件,标志着任务已成功开启,可以进行后续步骤。
发送音频流:
客户端开始发送二进制音频,并同时接收服务端持续返回的result-generated事件,该事件包含语音识别结果。
通知服务端结束任务:
客户端发送finish-task指令通知服务端结束任务,并继续接收服务端返回的result-generated事件。
任务结束:
客户端收到服务端返回的task-finished事件,标志着任务结束。
关闭连接:客户端关闭WebSocket连接。
URL
WebSocket URL固定如下:
wss://dashscope.aliyuncs.com/api-ws/v1/inference
Headers
请求头中需添加如下信息:
{
"Authorization": "bearer <your_dashscope_api_key>", // 将<your_dashscope_api_key>替换成您自己的API Key
"user-agent": "your_platform_info", //可选
"X-DashScope-WorkSpace": "workspaceId", // 可选,阿里云百炼业务空间ID。示例值:llm-xxxx。
"X-DashScope-DataInspection": "enable"
}
指令(客户端→服务端)
指令是客户端发送给服务端的消息,为JSON格式,以Text Frame方式发送,用于控制任务的起止和标识任务边界。
客户端发送给服务端的二进制音频(须为单声道音频)不包含在任何指令中,需单独发送。
发送指令需严格遵循以下时序,否则可能导致任务失败:
用于启动语音识别任务。
返回的
task_id
需在后续发送finish-task指令时使用,必须保持一致。
发送二进制音频(单声道)
用于发送待识别音频。
必须在接收到服务端返回的task-started事件后发送音频。
用于结束语音识别任务。
在音频发送完毕后发送此指令。
1. run-task指令:开启任务
该指令用于开启语音识别任务。task_id
在后续发送finish-task指令时也需要使用,必须保持一致。
发送时机:WebSocket连接建立后。
示例:
{
"header": {
"action": "run-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx", // 随机uuid
"streaming": "duplex"
},
"payload": {
"task_group": "audio",
"task": "asr",
"function": "recognition",
"model": "paraformer-realtime-v2",
"parameters": {
"format": "pcm", // 音频格式
"sample_rate": 16000, // 采样率
"vocabulary_id": "vocab-xxx-24ee19fa8cfb4d52902170a0xxxxxxxx", // paraformer-realtime-v2支持的热词ID
"disfluency_removal_enabled": false, // 过滤语气词
"language_hints": [
"en"
] // 指定语言,仅支持paraformer-realtime-v2模型
},
"resources": [ //不使用热词功能时,不要传递resources参数
{
"resource_id": "xxxxxxxxxxxx", // paraformer-realtime-v2支持的热词ID
"resource_type": "asr_phrase"
}
],
"input": {}
}
}
header
参数说明:
参数 | 类型 | 是否必选 | 说明 |
header.action | string | 是 | 指令类型。 当前指令中,固定为"run-task"。 |
header.task_id | string | 是 | 当次任务ID。 为32位通用唯一识别码(UUID),由32个随机生成的字母和数字组成。可以带横线(如
在后续发送finish-task指令时,用到的task_id需要和发送run-task指令时使用的task_id保持一致。 |
header.streaming | string | 是 | 固定字符串:"duplex" |
payload
参数说明:
参数 | 类型 | 是否必选 | 说明 |
payload.task_group | string | 是 | 固定字符串:"audio"。 |
payload.task | string | 是 | 固定字符串:"asr"。 |
payload.function | string | 是 | 固定字符串:"recognition"。 |
payload.model | string | 是 | 模型名称,支持的模型请参见模型列表。 |
payload.input | object | 是 | 固定格式:{}。 |
payload.parameters | |||
format | string | 是 | 设置待识别音频格式。 支持的音频格式:pcm、wav、mp3、opus、speex、aac、amr。 重要 opus/speex:必须使用Ogg封装; wav:必须为PCM编码; amr:仅支持AMR-NB类型。 |
sample_rate | integer | 是 | 设置待识别音频采样率(单位Hz)。 因模型而异:
|
vocabulary_id | string | 否 | 设置热词ID,若未设置则不生效。v2及更高版本模型设置热词ID时使用该字段。 在本次语音识别中,将应用与该热词ID对应的热词信息。具体使用方法请参见定制热词。 |
disfluency_removal_enabled | boolean | 否 | 设置是否过滤语气词:
|
language_hints | array[string] | 否 | 设置待识别语言代码。如果无法提前确定语种,可不设置,模型会自动识别语种。 目前支持的语言代码:
该参数仅对支持多语言的模型生效(参见模型列表)。 |
semantic_punctuation_enabled | boolean | 否 | 设置是否开启语义断句,默认关闭。
语义断句准确性更高,适合会议转写场景;VAD(Voice Activity Detection,语音活动检测)断句延迟较低,适合交互场景。 通过调整 该参数仅在模型为v2及更高版本时生效。 |
max_sentence_silence | integer | 否 | 设置VAD(Voice Activity Detection,语音活动检测)断句的静音时长阈值(单位为ms)。 当一段语音后的静音时长超过该阈值时,系统会判定该句子已结束。 参数范围为200ms至6000ms,默认值为800ms。 该参数仅在 |
multi_threshold_mode_enabled | boolean | 否 | 该开关打开时(true)可以防止VAD断句切割过长。默认关闭。 该参数仅在 |
punctuation_prediction_enabled | boolean | 否 | 设置是否在识别结果中自动添加标点:
该参数仅在模型为v2及更高版本时生效。 |
heartbeat | boolean | 否 | 当需要与服务端保持长连接时,可通过该开关进行控制:
该参数仅在模型为v2及更高版本时生效。 |
inverse_text_normalization_enabled | boolean | 否 | 设置是否开启ITN(Inverse Text Normalization,逆文本正则化)。 默认开启(true)。开启后,中文数字将转换为阿拉伯数字。 该参数仅在模型为v2及更高版本时生效。 |
payload.resources(内容为列表,不使用热词功能时,不要传递该参数) | |||
resource_id | string | 否 | 热词ID,此次语音识别中生效此热词ID对应的热词信息。默认不启用。需和 注: |
resource_type | string | 否 | 固定字符串“ |
2. finish-task指令:结束任务
该指令用于结束语音识别任务。音频发送完毕后,客户端可以发送此指令以结束任务。
发送时机:音频发送完成后。
示例:
{
"header": {
"action": "finish-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"streaming": "duplex"
},
"payload": {
"input": {}
}
}
header
参数说明:
参数 | 类型 | 是否必选 | 说明 |
header.action | string | 是 | 指令类型。 当前指令中,固定为"finish-task"。 |
header.task_id | string | 是 | 当次任务ID。 需要和发送run-task指令时使用的task_id保持一致。 |
header.streaming | string | 是 | 固定字符串:"duplex" |
payload
参数说明:
参数 | 类型 | 是否必选 | 说明 |
payload.input | object | 是 | 固定格式:{}。 |
二进制音频(客户端→服务端)
客户端需在收到task-started事件后,再发送待识别的音频流。
可以发送实时音频流(比如从话筒中实时获取到的)或者录音文件音频流,音频应是单声道。
音频通过WebSocket的二进制通道上传。建议每次发送100ms的音频,并间隔100ms。
事件(服务端→客户端)
事件是服务端返回给客户端的消息,为JSON格式,代表不同的处理阶段。
1. task-started事件:任务已开启
当监听到服务端返回的task-started
事件时,标志着任务已成功开启。只有在接收到该事件后,才能向服务器发送待识别音频或finish-task指令;否则,任务将执行失败。
task-started
事件的payload
没有内容。
示例:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-started",
"attributes": {}
},
"payload": {}
}
header
参数说明:
参数 | 类型 | 说明 |
header.event | string | 事件类型。 当前事件中,固定为"task-started"。 |
header.task_id | string | 客户端生成的task_id |
2. result-generated事件:语音识别结果
客户端发送待识别音频和finish-task指令的同时,服务端持续返回result-generated
事件,该事件包含语音识别的结果。
可以通过result-generated
事件中的payload.sentence.endTime
是否为空来判断该结果是中间结果还是最终结果。
示例:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"sentence": {
"begin_time": 170,
"end_time": null,
"text": "好,我知道了",
"heartbeat": false,
"sentence_end": true,
"emo_tag": "neutral", //只有请求参数model为paraformer-realtime-8k-v2,且请求参数semantic_punctuation_enabled为false,并且result-generated事件的sentence_end为true时才显示该字段
"emo_confidence": 0.914, //只有请求参数model为paraformer-realtime-8k-v2,且请求参数semantic_punctuation_enabled为false,并且result-generated事件的sentence_end为true时才显示该字段
"words": [
{
"begin_time": 170,
"end_time": 295,
"text": "好",
"punctuation": ","
},
{
"begin_time": 295,
"end_time": 503,
"text": "我",
"punctuation": ""
},
{
"begin_time": 503,
"end_time": 711,
"text": "知道",
"punctuation": ""
},
{
"begin_time": 711,
"end_time": 920,
"text": "了",
"punctuation": ""
}
]
}
},
"usage": {
"duration": 3
}
}
}
header
参数说明:
参数 | 类型 | 说明 |
header.event | string | 事件类型。 当前事件中,固定为"result-generated"。 |
header.task_id | string | 客户端生成的task_id。 |
payload
参数说明:
参数 | 类型 | 说明 |
output | object | output.sentence为识别结果,详细内容见下文。 |
usage | object | 当 当 |
payload.output.usage
格式如下:
参数 | 类型 | 说明 |
duration | integer | 任务计费时长(单位为秒)。 |
payload.output.sentence格式如下:
参数 | 类型 | 说明 |
begin_time | integer | 句子开始时间,单位为ms。 |
end_time | integer | null | 句子结束时间,如果为中间识别结果则为null,单位为ms。 |
text | string | 识别文本。 |
words | array | 字时间戳信息。 |
heartbeat | boolean | null | 若该值为true,可跳过识别结果的处理。 |
sentence_end | boolean | 判断给定句子是否已结束。 |
emo_tag | string | 当前句子的情感:
情感识别遵循如下约束:
|
emo_confidence | number | 当前句子识别情感的置信度,取值范围:[0.0,1.0],值越大表示置信度越高。 情感识别遵循如下约束:
|
payload.output.sentence.words
为字时间戳列表,其中每一个word格式如下:
参数 | 类型 | 说明 |
begin_time | integer | 字开始时间,单位为ms。 |
end_time | integer | 字结束时间,单位为ms。 |
text | string | 字。 |
punctuation | string | 标点。 |
3. task-finished事件:任务已结束
当监听到服务端返回的task-finished
事件时,说明任务已结束。此时可以关闭WebSocket连接并结束程序。
示例:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-finished",
"attributes": {}
},
"payload": {
"output": {},
"usage": null
}
}
header
参数说明:
参数 | 类型 | 说明 |
header.event | string | 事件类型。 当前事件中,固定为"task-finished"。 |
header.task_id | string | 客户端生成的task_id。 |
4. task-failed事件:任务失败
如果接收到task-failed
事件,表示任务失败。此时需要关闭WebSocket连接并处理错误。通过分析报错信息,如果是由于编程问题导致的任务失败,您可以调整代码进行修正。
示例:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-failed",
"error_code": "CLIENT_ERROR",
"error_message": "request timeout after 23 seconds.",
"attributes": {}
},
"payload": {}
}
header
参数说明:
参数 | 类型 | 说明 |
header.event | string | 事件类型。 当前事件中,固定为"task-failed"。 |
header.task_id | string | 客户端生成的task_id。 |
header.error_code | string | 报错类型描述。 |
header.error_message | string | 具体报错原因。 |
关于建连开销和连接复用
WebSocket服务支持连接复用以提升资源的利用效率,避免建立连接开销。
服务端收到客户端发送的run-task指令后,将启动一个新的任务,客户端发送finish-task指令后,服务端在任务完成时返回task-finished事件以结束该任务。结束任务后WebSocket连接可以被复用,客户端重新发送run-task指令即可开启下一个任务。
在复用连接中的不同任务需要使用不同 task_id。
如果在任务执行过程中发生失败,服务将依然返回task-failed事件,并关闭该连接。此时这个连接无法继续复用。
如果在任务结束后60秒没有新的任务,连接会超时自动断开。
示例代码
示例代码仅提供最基础的服务调通实现,实际业务场景的相关代码需您自行开发。
在编写WebSocket客户端代码时,为了同时发送和接收消息,通常采用异步编程。您可以按照以下步骤来编写程序:
错误码
如遇报错问题,请参见错误信息进行排查。
若问题仍未解决,请加入开发者群反馈遇到的问题,并提供Request ID,以便进一步排查问题。
常见问题
功能特性
Q:在长时间静默的情况下,如何保持与服务端长连接?
将请求参数heartbeat
设置为true,并持续向服务端发送静音音频。
静音音频指的是在音频文件或数据流中没有声音信号的内容。静音音频可以通过多种方法生成,例如使用音频编辑软件如Audacity或Adobe Audition,或者通过命令行工具如FFmpeg。
Q:如何将音频格式转换为满足要求的格式?
可使用FFmpeg工具,更多用法请参见FFmpeg官网。
# 基础转换命令(万能模板)
# -i,作用:输入文件路径,常用值示例:audio.wav
# -c:a,作用:音频编码器,常用值示例:aac, libmp3lame, pcm_s16le
# -b:a,作用:比特率(音质控制),常用值示例:192k, 320k
# -ar,作用:采样率,常用值示例:44100 (CD), 48000, 16000
# -ac,作用:声道数,常用值示例:1(单声道), 2(立体声)
# -y,作用:覆盖已存在文件(无需值)
ffmpeg -i input_audio.ext -c:a 编码器名 -b:a 比特率 -ar 采样率 -ac 声道数 output.ext
# 例如:WAV → MP3(保持原始质量)
ffmpeg -i input.wav -c:a libmp3lame -q:a 0 output.mp3
# 例如:MP3 → WAV(16bit PCM标准格式)
ffmpeg -i input.mp3 -c:a pcm_s16le -ar 44100 -ac 2 output.wav
# 例如:M4A → AAC(提取/转换苹果音频)
ffmpeg -i input.m4a -c:a copy output.aac # 直接提取不重编码
ffmpeg -i input.m4a -c:a aac -b:a 256k output.aac # 重编码提高质量
# 例如:FLAC无损 → Opus(高压缩)
ffmpeg -i input.flac -c:a libopus -b:a 128k -vbr on output.opus
Q:为什么使用WebSocket协议而非HTTP/HTTPS协议?为什么不提供RESTful API?
语音服务选择 WebSocket 而非 HTTP/HTTPS/RESTful,根本在于其依赖全双工通信能力——WebSocket 允许服务端与客户端主动双向传输数据(如实时推送语音合成/识别进度),而基于 HTTP 的 RESTful 仅支持客户端发起的单向请求-响应模式,无法满足实时交互需求。
Q:如何识别本地文件(录音文件)?
将本地文件转成二进制音频流,通过WebSocket的二进制通道上传二进制音频流进行识别(通常为WebSocket库的send方法)。代码片段如下所示,完整示例请参见示例代码。
故障排查
如遇代码报错问题,请根据错误码中的信息进行排查。
Q:无法识别语音(无识别结果)是什么原因?
请检查请求参数中的音频格式(
format
)和采样率(sampleRate
/sample_rate
)设置是否正确且符合参数约束。以下为常见错误示例:音频文件扩展名为 .wav,但实际为 MP3 格式,而请求参数
format
设置为 mp3(参数设置错误)。音频采样率为 3600Hz,但请求参数
sampleRate
/sample_rate
设置为 48000(参数设置错误)。
可以使用ffprobe工具获取音频的容器、编码、采样率、声道等信息:
ffprobe -v error -show_entries format=format_name -show_entries stream=codec_name,sample_rate,channels -of default=noprint_wrappers=1 input.xxx
使用
paraformer-realtime-v2
模型时,请检查language_hints
设置的语言是否与音频实际语言一致。例如:音频实际为中文,但
language_hints
设置为en
(英文)。若以上检查均无问题,可通过定制热词提升对特定词语的识别效果。