CosyVoice 语音合成 WebSocket 接口协议
CosyVoice 文本转语音的 WebSocket 接口参数与协议。DashScope SDK 仅支持 Java 和 Python,其他语言请使用 WebSocket 接口。
用户指南: 模型概览和音色选择,参见语音合成。
WebSocket 支持全双工通信。客户端与服务器通过一次握手建立持久连接,然后实时相互推送数据。
常用的 WebSocket 库:
获取 API Key。
参见语音合成。
每条续传指令最多发送 20,000 个字符。所有续传指令的累计字符数不得超过 200,000。
使用 UTF-8 编码。
数学表达式解析功能适用于 cosyvoice-v3-flash 和 cosyvoice-v3-plus,涵盖常见的中小学数学内容,包括基本运算、代数和几何。
详见将 LaTeX 公式转为语音(仅中文)。
使用 SSML 需同时满足以下条件:
客户端到服务器的消息为指令,服务器到客户端的消息为 JSON 事件或二进制音频流。
交互序列:
服务端职责
服务端按顺序推送完整的音频流,无需处理音频排序或完整性。
客户端职责
如果 WebSocket 连接失败:
浏览器
示例代码:
其他语言可参考以下示例实现相同逻辑,或使用 AI 工具转换。
指令是以 WebSocket 文本帧发送的 JSON 消息,用于控制任务生命周期。
按以下顺序发送指令:
启动文本转语音任务。在此配置音色、采样率等参数。
示例:
Python 生成 task_id 示例:
后续所有续传指令和结束指令使用同一个 task_id。
payload.parameters 参数:
启用 word_timestamp_enabled 后,时间戳会出现在结果已生成事件中:
cosyvoice-v3-flash 克隆音色的 instruction 示例:
cosyvoice-v3-flash 系统音色的 instruction 必须使用固定格式,参见音色列表。
hot_fix 示例:
发送待合成的文本。可以一次性发送全部文本,也可以按顺序拆分为多条指令发送。
示例:
结束任务。务必发送此指令,否则:
示例:
事件是服务器发送的 JSON 消息,标记任务生命周期的各个阶段。
确认任务已启动。仅在收到此事件后才能发送续传指令或结束指令,否则任务会失败。
发送续传指令和结束指令期间,服务器会持续返回
标记任务结束。
任务结束后,可以关闭 WebSocket 连接或复用它发送新的启动指令(参见连接开销与复用)。
示例:
表示任务失败。关闭 WebSocket 连接并查看错误信息。
示例:
流式合成过程中,可以提前中断当前任务(如用户取消播放),使用以下方式之一:
WebSocket 服务支持连接复用。
发送启动指令启动任务,发送结束指令结束任务。收到任务已完成事件后,复用同一个连接发送新的启动指令。
参见限速。
如需提高并发配额,请联系客服。配额调整需要审核,通常需要 1-3 个工作日。
典型连接时间:
基础连通性示例。请根据实际业务场景实现生产级逻辑。建议使用异步编程同时发送和接收:
- Go:
gorilla/websocket - PHP:
Ratchet - Node.js:
ws
CosyVoice 模型仅支持 WebSocket,不支持 HTTP REST API。 使用 HTTP 请求(POST、GET)会返回 InvalidParameter 或 URL 错误。
前提条件
获取 API Key。
模型与定价
参见语音合成。
文本与格式限制
文本长度限制
每条续传指令最多发送 20,000 个字符。所有续传指令的累计字符数不得超过 200,000。
字符计数规则
- 中文字符(简体、繁体、日文汉字、韩文汉字)按 2 个字符计算。其他字符(标点、字母、数字、假名/谚文)按 1 个字符计算。
- SSML 标签不计入字符数。
- 示例:
"你好"→ 2 + 2 = 4 个字符"中A文123"→ 2 + 1 + 2 + 1 + 1 + 1 = 8 个字符"中文。"→ 2 + 2 + 1 = 5 个字符"中 文。"→ 2 + 1 + 2 + 1 = 6 个字符"<speak>你好</speak>"→ 2 + 2 = 4 个字符
编码格式
使用 UTF-8 编码。
数学表达式支持
数学表达式解析功能适用于 cosyvoice-v3-flash 和 cosyvoice-v3-plus,涵盖常见的中小学数学内容,包括基本运算、代数和几何。
该功能仅支持中文。
SSML 支持
使用 SSML 需同时满足以下条件:
- 模型: 仅 cosyvoice-v3-flash 和 cosyvoice-v3-plus 支持 SSML。
- 音色: 使用支持 SSML 的音色:
- 所有克隆音色(通过音色克隆 API 创建)。
- 音色列表中标记为支持 SSML 的系统音色。
不支持 SSML 的系统音色(如部分基础音色)即使开启了enable_ssml,仍会返回错误 "SSML text is not supported at the moment!"。 - 参数: 在启动指令中将
enable_ssml设置为true。
交互流程
- 建立 WebSocket 连接。
- 发送启动指令开始任务。
- 等待收到任务已启动事件后再继续。
-
发送文本:
按顺序发送一条或多条续传指令。服务器收到完整句子后,返回结果已生成事件和音频流。文本长度约束参见续传指令中的
text字段。 -
通过
binary通道接收音频流。 - 发送完所有文本后,发送结束指令。继续接收音频流。不要跳过此步骤,否则音频末尾可能丢失。
- 从服务器接收任务已完成事件。
- 关闭 WebSocket 连接。
保持 task_id 一致:单个任务中的启动指令、所有续传指令和结束指令必须使用同一个 task_id。task_id 不匹配会导致:
- 音频推送乱序。
- 语音内容错位。
- 任务状态异常,可能无法收到任务已完成事件。
- 计费失败或使用量统计不准确。
- 发送启动指令时生成唯一的 task_id(如 UUID)。
- 将 task_id 存入变量。
- 后续所有续传指令和结束指令使用该 task_id。
- 收到任务已完成事件后,为下一个任务生成新的 task_id。
客户端实现要点
服务端与客户端职责
服务端职责
服务端按顺序推送完整的音频流,无需处理音频排序或完整性。
客户端职责
- 读取并拼接所有音频片段 服务端将音频拆分为多个二进制帧。接收所有帧并拼接:
-
保持完整的 WebSocket 生命周期
从发送启动指令到接收任务已完成事件期间,不要断开连接。常见错误:
- 音频未接收完毕就关闭连接,导致音频不完整。
- 忘记发送结束指令,导致文本缓存未处理。
- 页面跳转或应用后台化时未处理 WebSocket 心跳保活。
移动端(Flutter、iOS、Android)进入后台时需要特殊网络处理。在后台任务或服务中保持 WebSocket 连接,或返回前台时重新初始化。 - ASR → LLM → TTS 流程中的文本完整性 确保传入 TTS 的文本完整:
平台特定提示
- Flutter: 在
dispose方法中关闭连接,防止使用web_socket_channel时内存泄漏。处理应用生命周期事件(如AppLifecycleState.paused)以应对后台切换。 - Web(浏览器): 部分浏览器限制 WebSocket 连接数量。多个任务复用同一个连接。使用
beforeunload在页面关闭前关闭连接。 - 移动端(iOS/Android 原生): 应用进入后台时,操作系统可能暂停或终止网络连接。使用后台任务或前台服务保持 WebSocket 活跃,或返回前台时重新初始化任务。
URL
请求头
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| Authorization | string | 是 | 认证令牌。格式:bearer $DASHSCOPE_API_KEY。 |
| user-agent | string | 否 | 客户端标识,用于来源追踪。 |
| X-DashScope-WorkSpace | string | 否 | 千问云业务空间 ID。 |
| X-DashScope-DataInspection | string | 否 | 数据合规检查。默认 enable。非必需不要设置。 |
认证时机认证发生在 WebSocket 握手阶段,而非发送启动指令时。如果 Authorization 头缺失或无效,服务器会以 HTTP 401 或 403 拒绝握手。客户端库通常将其报告为 WebSocketBadStatus 异常。
排查认证失败
如果 WebSocket 连接失败:
- 检查 API Key 格式: 确认 Authorization 头使用
bearer $DASHSCOPE_API_KEY格式,bearer与 Key 之间有空格。 - 验证 API Key 有效性: 查看 API Key 页面,确认 Key 处于激活状态且已授权 CosyVoice 模型。
- 检查头设置位置: 在 WebSocket 握手时设置 Authorization 头。各语言示例:
- Python (websockets):
extra_headers={"Authorization": f"bearer {api_key}"} - JavaScript:浏览器原生 WebSocket API 不支持自定义头。使用服务端代理或其他库(如 ws)。
- Go (gorilla/websocket):
header.Add("Authorization", fmt.Sprintf("bearer %s", apiKey))
- Python (websockets):
- 测试网络连通性: 使用 curl 或 Postman 调用其他支持 HTTP 的 DashScope API,验证 API Key 是否有效。
浏览器中使用 WebSocket
浏览器 new WebSocket(url) API 在握手时不支持自定义请求头(包括 Authorization)。无法直接从前端的代码认证。
解决方案:使用后端代理
- 从后端(Node.js、Java 或 Python)连接 CosyVoice,后端可以设置 Authorization 头。
- 前端通过 WebSocket 连接到你的后端,后端转发消息到 CosyVoice。
- 这样可以隐藏 API Key,同时可以添加认证、日志或限流等功能。
不要将 API Key 硬编码在前端代码中。Key 泄露可能导致账户被盗用、产生意外费用或数据泄露。
- 前端(原生 Web)+ 后端(Node.js Express):cosyvoiceNodeJs_en.zip
- 前端(原生 Web)+ 后端(Python Flask):cosyvoiceFlask_en.zip
指令 (客户端到服务器)
指令是以 WebSocket 文本帧发送的 JSON 消息,用于控制任务生命周期。
按以下顺序发送指令:
1. 启动指令 (run-task instruction): 启动任务
启动文本转语音任务。在此配置音色、采样率等参数。
- 发送时机: WebSocket 连接建立后发送。
- 不要在此发送文本。 文本通过续传指令发送。
- input 字段必填,但值必须为
{}。省略会导致 InvalidParameter 错误("Missing required parameter 'payload.input'! Please follow the protocol!")。
header 参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| header.action | string | 是 | 固定值:"run-task"。 |
| header.task_id | string | 是 | 32 位 UUID。连字符可选(如 "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx" 或 "2bf83b9abaeb4fda8d9axxxxxxxxxxxx")。多数语言提供内置的 UUID 生成 API。 |
| header.streaming | string | 是 | 固定值:"duplex"。 |
payload 参数:
常见错误: 省略 input 字段或添加意外字段(如 mode、content)会导致 "InvalidParameter: task can not be null" 或连接关闭(WebSocket 码 1007)。
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| text_type | string | 是 | 固定值:"PlainText"。 |
| voice | string | 是 | 用于合成的音色。参见音色列表查看可用的系统音色。 |
| format | string | 否 | 音频格式。支持 pcm、wav、mp3(默认)和 opus。opus 格式可通过 bit_rate 调整码率。 |
| sample_rate | integer | 否 | 采样率(Hz)。默认 22050。可选值:8000、16000、22050、24000、44100、48000。 |
| volume | integer | 否 | 音量。默认 50。范围 [0, 100],线性缩放。0 为静音,100 为最大音量。 |
| rate | float | 否 | 语速。默认 1.0。范围 [0.5, 2.0]。小于 1.0 减慢语速,大于 1.0 加快。 |
| pitch | float | 否 | 音调倍数。默认 1.0。范围 [0.5, 2.0]。与感知音调的关系并非严格线性,建议测试选择合适的值。 |
| enable_ssml | boolean | 否 | 是否启用 SSML。设为 true 时仅允许发送一条续传指令。 |
| bit_rate | int | 否 | 音频码率(Opus 格式),单位 kbps。默认 32。范围 [6, 510]。 |
| word_timestamp_enabled | boolean | 否 | 是否启用词级时间戳。默认 false。仅支持音色列表中标记为支持的系统音色。 |
| seed | int | 否 | 生成的随机种子。相同种子且参数一致时,输出结果可复现。默认 0。范围 [0, 65535]。 |
| language_hints | array[string] | 否 | 合成目标语言。可选值:zh、en、fr、de、ja、ko、ru、pt、th、id、vi。该字段为数组,但仅处理第一个元素。 |
| instruction | string | 否 | 控制合成效果,如方言、情感或说话风格。仅支持音色列表中标记为支持 Instruct 的系统音色。最大长度 100 个字符。 |
| enable_aigc_tag | boolean | 否 | 是否在生成的音频中嵌入不可见的 AIGC 标识符。设为 true 时,标识符会嵌入 WAV、MP3 和 Opus 格式中。默认 false。cosyvoice-v3-flash 和 cosyvoice-v3-plus 支持。 |
| aigc_propagator | string | 否 | 设置 AIGC 标识符中的 ContentPropagator 字段。仅在 enable_aigc_tag 为 true 时生效。默认 UID。cosyvoice-v3-flash 和 cosyvoice-v3-plus 支持。 |
| aigc_propagate_id | string | 否 | 设置 AIGC 标识符中的 PropagateID 字段。仅在 enable_aigc_tag 为 true 时生效。默认为当前请求 ID。cosyvoice-v3-flash 和 cosyvoice-v3-plus 支持。 |
| hot_fix | object | 否 | 文本热补丁配置。合成前自定义发音或替换文本。仅 cosyvoice-v3-flash 支持。 |
| enable_markdown_filter | boolean | 否 | 是否启用 Markdown 过滤。合成前移除输入文本中的 Markdown 符号。默认 false。仅 cosyvoice-v3-flash 支持。 |
2. 续传指令 (continue-task instruction)
发送待合成的文本。可以一次性发送全部文本,也可以按顺序拆分为多条指令发送。
发送时机: 收到任务已启动事件后。
文本片段之间的间隔不能超过 23 秒,否则会返回 "request timeout after 23 seconds" 错误。如果没有更多文本需要发送,请发送结束指令结束任务。23 秒超时由服务端强制执行,无法修改。
header 参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| header.action | string | 是 | 固定值:"continue-task"。 |
| header.task_id | string | 是 | 必须与启动指令中的 task_id 一致。 |
| header.streaming | string | 是 | 固定值:"duplex"。 |
payload 参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| input.text | string | 是 | 待合成的文本。 |
3. 结束指令 (finish-task instruction): 结束任务
结束任务。务必发送此指令,否则:
- 音频不完整: 服务器不会强制合成缓存中的句子,导致音频末尾丢失。
- 连接超时: 最后一条续传指令后等待超过 23 秒会触发超时。
- 计费问题: 使用量信息可能不准确。
发送时机: 所有续传指令发送完成后立即发送。不要等待音频播放完成——这可能导致超时。
header 参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| header.action | string | 是 | 固定值:"finish-task"。 |
| header.task_id | string | 是 | 必须与启动指令中的 task_id 一致。 |
| header.streaming | string | 是 | 固定值:"duplex"。 |
payload 参数:
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| payload.input | object | 是 | 固定值:{}。 |
事件 (服务器到客户端)
事件是服务器发送的 JSON 消息,标记任务生命周期的各个阶段。
二进制音频单独发送,不包含在任何事件中。
1. 任务已启动事件 (task-started event)
确认任务已启动。仅在收到此事件后才能发送续传指令或结束指令,否则任务会失败。
task-started 事件的 payload 为空。
示例:
header 参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| header.event | string | 固定值:"task-started"。 |
| header.task_id | string | 客户端生成的任务 ID。 |
2. 结果已生成事件 (result-generated event)
发送续传指令和结束指令期间,服务器会持续返回 result-generated 事件和二进制音频帧。
每个 result-generated 事件包含当前句子索引。音频数据以二进制帧形式在事件之间到达。一个句子对应多个二进制音频帧。按顺序接收并追加到同一个文件中。
示例:
header 参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| header.event | string | 固定值:"result-generated"。 |
| header.task_id | string | 客户端生成的任务 ID。 |
| header.attributes | object | 附加属性——通常为空。 |
payload 参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| payload.output.type | string | 句子事件类型。值为 sentence-begin(句子开始)、sentence-synthesis(句子合成中)或 sentence-end(句子结束)。 |
| payload.output.sentence.index | integer | 句子编号,从 0 开始。 |
| payload.output.sentence.words | array | 词信息数组。 |
| payload.output.sentence.words.text | string | 词文本。 |
| payload.output.sentence.words.begin_index | integer | 词在句子中的起始位置,从 0 开始计数。 |
| payload.output.sentence.words.end_index | integer | 词在句子中的结束位置,从 1 开始计数。 |
| payload.output.sentence.words.begin_time | integer | 词音频的起始时间戳,单位毫秒。 |
| payload.output.sentence.words.end_time | integer | 词音频的结束时间戳,单位毫秒。 |
| payload.output.original_text | string | 当前句子的原始文本。仅在 type 为 sentence-begin 或 sentence-end 时出现。 |
| payload.usage.characters | integer | 截至目前的累计计费字符数。usage 字段仅在 type 为 sentence-end 的事件中出现,请以最后一次出现的值为准。 |
3. 任务已完成事件 (task-finished event)
标记任务结束。
任务结束后,可以关闭 WebSocket 连接或复用它发送新的启动指令(参见连接开销与复用)。
示例:
header 参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| header.event | string | 固定值:"task-finished"。 |
| header.task_id | string | 客户端生成的任务 ID。 |
4. 任务失败事件 (task-failed event)
表示任务失败。关闭 WebSocket 连接并查看错误信息。
示例:
header 参数:
| 参数 | 类型 | 描述 |
|---|---|---|
| header.event | string | 固定值:"task-failed"。 |
| header.task_id | string | 客户端生成的任务 ID。 |
| header.error_code | string | 错误类型。 |
| header.error_message | string | 详细错误原因。 |
任务中断
流式合成过程中,可以提前中断当前任务(如用户取消播放),使用以下方式之一:
| 中断方式 | 服务端行为 | 使用场景 |
|---|---|---|
| 关闭连接 | 立即停止合成。丢弃未发送的音频。不返回任务已完成事件。连接无法复用。 | 立即停止: 用户取消播放、切换内容或退出应用。 |
| 发送结束指令 | 强制合成缓存中的文本。返回剩余音频和任务已完成事件。连接可复用。 | 优雅结束: 停止发送文本但接收所有缓存音频。 |
连接开销与复用
WebSocket 服务支持连接复用。
发送启动指令启动任务,发送结束指令结束任务。收到任务已完成事件后,复用同一个连接发送新的启动指令。
性能与并发
并发限制
参见限速。
如需提高并发配额,请联系客服。配额调整需要审核,通常需要 1-3 个工作日。
最佳实践: 复用 WebSocket 连接来执行多个任务。参见连接开销与复用。
连接延迟
典型连接时间:
- 跨境连接:1-3 秒。极少数情况下 10-30 秒。
- 网络延迟: 检查跨境连接质量或 ISP 性能。
- DNS 过慢: 尝试使用公共 DNS(8.8.8.8)或为 dashscope.aliyuncs.com 配置本地 hosts 文件。
- TLS 握手: 升级到 TLS 1.2 或更高版本。
- 代理/防火墙: 企业网络可能阻止或限制 WebSocket 连接。
- 使用 Wireshark 或 tcpdump 分析 TCP 握手、TLS 握手和 WebSocket Upgrade 的耗时。
- 使用 curl 测试 HTTP 延迟:
curl -w "@curl-format.txt" -o /dev/null -s https://dashscope.aliyuncs.com
音频生成速度
- 实时率(RTF):0.1-0.5 倍实时(1 秒音频需要 0.1-0.5 秒生成)。实际速度因模型、文本长度和服务器负载而异。
- 首包延迟:发送续传指令到收到首个音频片段需 200-800 毫秒。
示例代码
基础连通性示例。请根据实际业务场景实现生产级逻辑。建议使用异步编程同时发送和接收:
- 连接: 使用 WebSocket 库的 connect 函数,携带请求头和URL建立连接。
-
监听消息: 服务器推送二进制音频和事件:
事件:
- task-started: 任务已启动。仅在此之后发送续传指令或结束指令。
- result-generated: 发送续传指令或结束指令后持续返回。
- task-finished: 任务完成。关闭连接。
- task-failed: 任务失败。关闭连接并检查错误。
- MP3/Opus 流式播放:使用流式播放器(FFmpeg、PyAudio、AudioFormat、MediaSource)。不要逐帧播放。
- 保存完整音频:以追加模式将帧写入同一个文件。
- WAV/MP3:仅首帧包含头部信息,后续帧仅有音频数据。
- 发送指令: 从独立线程向服务器发送指令。
- 关闭连接: 完成、出错或收到任务已完成事件/任务失败事件后关闭。
- Go
- C#
- PHP
- Node.js
- Java
- Python

