本文介紹如何通過WebSocket串連訪問CosyVoice語音合成服務。
DashScope SDK目前僅支援Java和Python。若想使用其他程式設計語言開發CosyVoice語音合成應用程式,可以通過WebSocket串連與服務進行通訊。
使用者指南:關於模型介紹和選型建議請參見即時語音合成-CosyVoice/Sambert。
WebSocket是一種支援全雙工系統通訊的網路通訊協定。用戶端和伺服器通過一次握手建立持久串連,雙方可以互相主動推送資料,因此在即時性和效率方面具有顯著優勢。
對於常用程式設計語言,有許多現成的WebSocket庫和樣本可供參考,例如:
Go:
gorilla/websocketPHP:
RatchetNode.js:
ws
建議您先瞭解WebSocket的基本原理和技術細節,再參照本文進行開發。
CosyVoice 系列模型僅支援通過 WebSocket 串連調用,不支援 HTTP REST API。如果使用 HTTP 要求(如 POST 方式)調用,將返回 InvalidParameter 或 url error 錯誤。
前提條件
模型與價格
語音合成文本限制與格式規範
文本長度限制
單次通過continue-task指令發送的待合成文本長度不得超過 20000 字元,多次調用continue-task指令累計發送的文本總長度不得超過 20 萬字元。
字元計算規則
漢字(包括簡/繁體漢字、日文漢字和韓文漢字)按2個字元計算,其他所有字元(如標點符號、字母、數字、日韓文假名/諺文等)均按 1個字元計算
計算文本長度時,不包含SSML 標籤內容
樣本:
"你好"→ 2(你)+2(好)=4字元"中A文123"→ 2(中)+1(A)+2(文)+1(1)+1(2)+1(3)=8字元"中文。"→ 2(中)+2(文)+1(。)=5字元"中 文。"→ 2(中)+1(空格)+2(文)+1(。)=6字元"<speak>你好</speak>"→ 2(你)+2(好)=4字元
編碼格式
需採用UTF-8編碼。
數學運算式支援說明
當前數學運算式解析功能僅適用於cosyvoice-v2、cosyvoice-v3-flash和cosyvoice-v3-plus模型,支援識別中小學常見的數學運算式,包括但不限於基礎運算、代數、幾何等內容。
詳情請參見LaTeX 方程式轉語音。
SSML標記語言支援說明
使用 SSML 功能需要同時滿足以下條件:
模型支援:僅cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型支援SSML功能
音色支援: 必須使用支援 SSML 的音色。支援 SSML 的音色包括
所有複刻音色(通過聲音複刻 API 建立的自訂音色)
音色列表中標記為支援SSML的系統音色
說明如果使用不支援 SSML 的系統音色(如部分基礎音色),即使開啟
enable_ssml參數,也會報錯“SSML text is not supported at the moment!”。參數配置: 在run-task指令中將
enable_ssml參數設為true
滿足上述條件後,通過continue-task指令發送包含SSML的文本即可使用 SSML 功能。完整樣本請參見快速開始。
互動流程
用戶端發送給服務端的訊息稱作指令;服務端返回給用戶端的訊息有兩種:JSON格式的事件和二進位音頻流。
按時間順序,用戶端與服務端的互動流程如下:
建立串連:用戶端與服務端建立WebSocket串連。
開啟任務:用戶端發送run-task指令以開啟任務。
等待確認:用戶端收到服務端返回的task-started事件,標誌著任務已成功開啟,可以進行後續步驟。
發送待合成文本:
用戶端按順序向服務端發送一個或多個包含待合成文本的continue-task指令,服務端接收到完整語句後返回result-generated事件和音頻流(文本長度有約束, 詳情參見continue-task指令中
text欄位描述)。說明您可以多次發送continue-task指令,按順序提交文本片段。服務端接收文本片段後自動進行分句:
完整語句立即合成,此時用戶端能夠接收到服務端返回的音頻
不完整語句緩衝至完整後合成,語句不完整時服務端不返迴音頻
當發送finish-task指令時,服務端會強制合成所有緩衝內容。
接收音頻:通過
binary通道接收音頻流通知服務端結束任務:
待文本發送完畢後,用戶端發送finish-task指令通知服務端結束任務,並繼續接收服務端返回的音頻流(注意不要遺漏該步驟,否則可能收不到語音或收不到結尾部分的語音)。
任務結束:
用戶端收到服務端返回的task-finished事件,標誌著任務結束。
關閉串連:用戶端關閉WebSocket串連。
為提高資源使用率,建議複用 WebSocket 串連處理多個任務,而非為每個任務建立新串連。參見關於建連開銷和串連複用。
task_id 必須全程一致:同一次合成任務中,run-task、所有 continue-task、finish-task 必須使用相同的 task_id。
錯誤後果:如使用不同的 task_id,會導致:
服務端無法關聯請求,音頻流返回順序混亂
常值內容被錯誤分配到不同任務,語音內容錯位
任務狀態異常,可能收不到 task-finished 事件
無法正確計費,usage 統計不準確
正確做法:
在 run-task 時產生唯一的 task_id(如使用UUID)
將該 task_id 儲存在變數中
後續所有 continue-task 和 finish-task 都使用該 task_id
任務結束後(收到 task-finished),如需發起新任務,產生新的 task_id
用戶端實現注意事項
在實現 WebSocket 用戶端時,特別是使用 Flutter、Web 或移動端平台時,需要明確服務端與用戶端的職責劃分,以確保語音合成任務的完整性和穩定性。
服務端與用戶端職責
服務端職責
服務端保證按順序返回完整的音頻流。您無需擔心音頻資料的順序性或完整性,服務端會按照文本順序依次產生並推送所有音頻分區。
用戶端職責
用戶端需要負責以下關鍵任務:
讀取並拼接所有音頻分區
服務端返回的音頻是以多個二進位分區(Binary Frame)的形式推送的。用戶端必須完整接收所有分區,並按接收順序拼接成最終的音頻檔案。範例程式碼如下:
# Python 樣本:拼接音頻分區 with open("output.mp3", "ab") as f: # 追加模式寫入 f.write(audio_chunk) # audio_chunk 為每次接收到的二進位音頻資料// JavaScript 樣本:拼接音頻分區 const audioChunks = []; ws.onmessage = (event) => { if (event.data instanceof Blob) { audioChunks.push(event.data); // 收集所有音頻分區 } }; // 任務完成後合并音頻 const audioBlob = new Blob(audioChunks, { type: 'audio/mp3' });保證 WebSocket 生命週期完整
在整個語音合成任務過程中(從發送 run-task指令 到接收 task-finished事件),不要提前斷開 WebSocket 串連。常見錯誤包括:
在所有音頻分區返回前就關閉串連,導致音頻不完整;
忘記發送 finish-task指令,導致服務端緩衝的文本未能合成;
頁面跳轉、應用程式切換到後台等情境下,未妥善處理 WebSocket 的保活機制。
重要移動端應用(如 Flutter、iOS、Android)需要特別注意應用進入後台時的網路連接管理。建議在背景工作或服務中維護 WebSocket 串連,或在恢複前台時檢查任務狀態並重建立立串連。
ASR→LLM→TTS 聯動情境的文本完整性
在語音辨識(ASR)→大語言模型(LLM)→語音合成(TTS)的聯動流程中,確保傳遞給 TTS 的文本是完整的,不被中途截斷。例如:
等待 LLM 產生完整句子或段落後,再發送 continue-task指令,而非逐字推送;
如果需要流式合成(邊產生邊播放),可以按自然語句邊界(如句號、問號)分批發送文本;
在 LLM 輸出完成後,務必發送 finish-task指令,避免遺漏尾部內容。
平台特定提示
Flutter:使用
web_socket_channel包時,注意在dispose方法中正確關閉串連,避免記憶體流失。同時,處理應用生命週期事件(如AppLifecycleState.paused)以應對後台切換情境。Web(瀏覽器):部分瀏覽器對 WebSocket 串連數有限制,建議複用同一串連處理多個任務。另外,使用
beforeunload事件在頁面關閉前主動中斷連線,避免殘留串連。移動端(iOS/Android 原生):在應用進入後台時,作業系統可能會暫停或終止網路連接。建議使用背景工作(Background Task)或前台服務(Foreground Service)保持 WebSocket 活躍,或在恢複前台時重新初始化任務。
URL
WebSocket URL固定如下:
國際
在國際部署模式下,存取點與資料存放區均位於新加坡地區,模型推理計算資源在全球範圍內動態調度(不含中國內地)。
WebSocket URL:wss://dashscope-intl.aliyuncs.com/api-ws/v1/inference
中國內地
在中國內地部署模式下,存取點與資料存放區均位於北京地區,模型推理計算資源僅限於中國內地。
WebSocket URL:wss://dashscope.aliyuncs.com/api-ws/v1/inference
常見 URL 配置錯誤:
錯誤:使用 http:// 或 https:// 開頭的 URL → 正確:必須使用 wss:// 協議
錯誤:將 Authorization 參數放在 URL 查詢字串中(如
?Authorization=bearer <your_api_key>)→ 正確:Authorization 必須在 HTTP 握手的 Headers 中設定(參見Headers)錯誤:URL 末尾添加模型名稱或其他路徑參數 → 正確:URL 固定不變,模型通過run-task指令的
payload.model參數指定
Headers
要求標頭中需添加如下資訊:
參數 | 類型 | 是否必選 | 說明 |
Authorization | string | 是 | 鑒權令牌,格式為 |
user-agent | string | 否 | 用戶端標識,便於服務端追蹤來源。 |
X-DashScope-WorkSpace | string | 否 | 阿里雲百鍊業務空間ID。 |
X-DashScope-DataInspection | string | 否 | 是否啟用資料合規檢測功能。預設不傳或設為 |
鑒權驗證時機與常見錯誤
Authorization 鑒權在 WebSocket 握手階段進行驗證,而非後續發送run-task指令時。如果 Authorization 頭缺失或 API Key 無效,服務端將拒絕握手並返回 HTTP 401/403 錯誤,用戶端庫通常解析為 WebSocketBadStatus 異常。
鑒權失敗排查步驟
若 WebSocket 串連失敗,請按以下步驟排查:
檢查 API Key 格式:確認 Authorization 頭格式為
bearer <your_api_key>,注意 bearer 和 API Key 之間有一個空格。驗證 API Key 有效性:在百鍊控制台確認 API Key 未被刪除或禁用,且具有調用 CosyVoice 模型的許可權。
檢查 Headers 設定:確認 Authorization 頭在 WebSocket 握手時正確設定。不同程式設計語言的 WebSocket 庫設定方式不同:
Python(websockets 庫):
extra_headers={"Authorization": f"bearer {api_key}"}JavaScript:WebSocket 標準 API 不支援自訂 Headers,需使用服務端中轉或其他庫(如 ws)
Go(gorilla/websocket):
header.Add("Authorization", fmt.Sprintf("bearer %s", apiKey))
網路連通性測試:使用 curl 或 Postman 測試 API Key 是否有效(通過其他支援 HTTP 的 DashScope API)。
瀏覽器環境 WebSocket 使用說明
在瀏覽器環境(如 Vue3、React 等前端架構)中使用 WebSocket 時,存在以下限制:瀏覽器 WebSocket API 不支援自訂 Headers。瀏覽器原生的 new WebSocket(url) API 不支援在握手時設定自訂要求標頭(如 Authorization),這是瀏覽器安全性原則的限制。因此,無法直接在前端代碼中使用 API Key 進行鑒權。
解決方案:使用後端代理
在後端服務(Node.js、Java、Python 等)中建立 WebSocket 串連到 CosyVoice 服務,後端可以正確設定 Authorization 頭。
前端通過 WebSocket 串連到自己的後端服務,後端作為代理轉寄訊息到 CosyVoice。
優點:API Key 不暴露在前端,更安全;可以在後端添加額外的商務邏輯(如鑒權、日誌、限流等)。
不要將 API Key 寫入程式碼在前端代碼中或通過瀏覽器直接發送。API Key 泄露會導致帳號被盜用、產生高額費用或資料泄露風險。
範例程式碼:
如需其他程式設計語言實現,您可以參考樣本中的邏輯,使用對應語言實現。或者使用AI工具將樣本轉換為目標語言。
前端(原生 Web)+ 後端(Node.js Express):cosyvoiceNodeJs_en.zip
前端(原生 Web)+ 後端(Python Flask):cosyvoiceFlask_en.zip
指令(用戶端→服務端)
指令是用戶端發送給服務端的訊息,為JSON格式,以Text Frame方式發送,用於控制任務的起止和標識任務邊界。
發送指令需嚴格遵循以下時序,否則可能導致任務失敗:
發送 run-task指令
用於啟動語音合成任務。
返回的
task_id需在後續發送continue-task指令和finish-task指令時使用,必須保持一致。
用於發送待合成文本。
必須在接收到服務端返回的task-started事件後,才能發送此指令。
用於結束語音合成任務。
在所有continue-task指令發送完畢後發送此指令。
1. run-task指令:開啟任務
該指令用於開啟語音合成任務。可在該指令中對音色、採樣率等請求參數進行設定。
發送時機:WebSocket串連建立後。
不要發送待合成文本:在 run-task 指令中發送文本不利於問題排查,應避免在此發送文本。待合成文本應通過continue-task指令發送。
input 欄位必須存在:payload 中必須包含 input 欄位(格式為
{}),不可省略,否則會報錯“task can not be null”。
樣本:
{
"header": {
"action": "run-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx", // 隨機uuid
"streaming": "duplex"
},
"payload": {
"task_group": "audio",
"task": "tts",
"function": "SpeechSynthesizer",
"model": "cosyvoice-v3-flash",
"parameters": {
"text_type": "PlainText",
"voice": "longanyang", // 音色
"format": "mp3", // 音頻格式
"sample_rate": 22050, // 採樣率
"volume": 50, // 音量
"rate": 1, // 語速
"pitch": 1 // 音調
},
"input": {// input不能省去,不然會報錯
}
}
}header參數說明:
參數 | 類型 | 是否必選 | 說明 |
header.action | string | 是 | 指令類型。 當前指令中,固定為"run-task"。 |
header.task_id | string | 是 | 當次任務ID。 為32位通用唯一識別碼(UUID),由32個隨機產生的字母和數字組成。可以帶橫線(如 在後續發送continue-task指令和finish-task指令時,用到的task_id需要和發送run-task指令時使用的task_id保持一致。 |
header.streaming | string | 是 | 固定字串:"duplex" |
payload參數說明:
參數 | 類型 | 是否必選 | 說明 |
payload.task_group | string | 是 | 固定字串:"audio"。 |
payload.task | string | 是 | 固定字串:"tts"。 |
payload.function | string | 是 | 固定字串:"SpeechSynthesizer"。 |
payload.model | string | 是 | 語音合成模型。 不同模型版本需要使用對應版本的音色:
|
payload.input | object | 是 | run-task 指令中必須包含 input 欄位(不可省略),但不應在此發送待合成文本(因此應使用Null 物件
重要 常見錯誤:省略 input 欄位或在 input 中包含非預期欄位(如 mode、content 等)會導致服務端拒絕請求並返回“InvalidParameter: task can not be null”或串連關閉(WebSocket code 1007)錯誤。 |
payload.parameters | |||
text_type | string | 是 | 固定字串:“PlainText”。 |
voice | string | 是 | 語音合成所使用的音色。 支援系統音色和複刻音色:
|
format | string | 否 | 音頻編碼格式。 支援pcm、wav、mp3(預設)和opus。 音頻格式為opus時,支援通過 |
sample_rate | integer | 否 | 音頻採樣率(單位:Hz)。 預設值:22050。 取值範圍:8000, 16000, 22050, 24000, 44100, 48000。 說明 預設採樣率代表當前音色的最佳採樣率,預設條件下預設按照該採樣率輸出,同時支援降採樣或升採樣。 |
volume | integer | 否 | 音量。 預設值:50。 取值範圍:[0, 100]。50代表標準音量。音量大小與該值呈線性關係,0為靜音,100為最大音量。 |
rate | float | 否 | 語速。 預設值:1.0。 取值範圍:[0.5, 2.0]。1.0為標準語速,小於1.0則減慢,大於1.0則加快。 |
pitch | float | 否 | 音高。該值作為音高調節的乘數,但其與聽感上的音高變化並非嚴格的線性或對數關係,建議通過測試選擇合適的值。 預設值:1.0。 取值範圍:[0.5, 2.0]。1.0為音色自然音高。大於1.0則音高變高,小於1.0則音高變低。 |
enable_ssml | boolean | 否 | 是否開啟SSML功能。 該參數設為 |
bit_rate | int | 否 | 音頻碼率(單位kbps)。音頻格式為opus時,支援通過 預設值:32。 取值範圍:[6, 510]。 |
word_timestamp_enabled | boolean | 否 | 是否開啟字層級時間戳記。 預設值:false。
該功能僅適用於cosyvoice-v3-flash、cosyvoice-v3-plus和cosyvoice-v2模型的複刻音色,以及音色列表中標記為支援的系統音色。 更多說明請參見時間戳記資料提取最佳實務。 |
seed | int | 否 | 產生時使用的隨機數種子,使合成的效果產生變化。在模型版本、文本、音色及其他參數均相同的前提下,使用相同的seed可複現相同的合成結果。 預設值0。 取值範圍:[0, 65535]。 |
language_hints | array[string] | 否 | 指定語音合成的目標語言,提升合成效果。 當數字、縮寫、符號等朗讀方式或者小語種合成效果不符合預期時使用,例如:
取值範圍:
注意:此參數為數組,但目前的版本僅處理第一個元素,因此建議只傳入一個值。 重要 此參數用於指定語音合成的目標語言,該設定與聲音複刻時的樣本音訊語種無關。如果您需要設定複刻任務的源語言,請參見CosyVoice聲音複刻API。 |
instruction | string | 否 | 設定指令,用於控制方言、情感或角色等合成效果。該功能僅適用於cosyvoice-v3-flash模型的複刻音色,以及音色列表中標記為支援Instruct的系統音色。 長度限制:100字元 漢字(包括簡/繁體漢字、日文漢字和韓文漢字)按2個字元計算,其他所有字元(如標點符號、字母、數字、日韓文假名/諺文等)均按 1個字元計算 使用要求:
支援的功能: 說明 對於cosyvoice-v3-flash複刻音色,可使用任意自然語言控制語音合成效果。為獲得穩定、可預期的效果,建議僅使用下述已支援的功能點,並盡量按照樣本中的格式編寫 instruction。 對於其他音色,需嚴格按照樣本中的格式編寫 instruction。 |
enable_aigc_tag | boolean | 否 | 是否在產生的音頻中添加AIGC隱性標識。設定為true時,會將隱性標識嵌入到支援格式(wav/mp3/opus)的音頻中。 預設值:false。 僅cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。 |
aigc_propagator | string | 否 | 設定AIGC隱性標識中的 預設值:阿里雲UID。 僅cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。 |
aigc_propagate_id | string | 否 | 設定AIGC隱性標識中的 預設值:本次語音合成請求Request ID。 僅cosyvoice-v3-flash、cosyvoice-v3-plus、cosyvoice-v2支援該功能。 |
2. continue-task指令
該指令專門用來發送待合成文本。
可以在一個continue-task指令中一次性發送待合成文本,也可以將文本分段並按順序在多個continue-task指令中發送。
發送時機:在收到task-started事件後發送。
發送文本片段的間隔不得超過23秒,否則觸發“request timeout after 23 seconds”異常。
若無待發送文本,需及時發送finish-task指令結束任務。
服務端強制設定23秒逾時機制,用戶端無法修改該配置。
樣本:
{
"header": {
"action": "continue-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx", // 隨機uuid
"streaming": "duplex"
},
"payload": {
"input": {
"text": "床前明月光,疑是地上霜"
}
}
}header參數說明:
參數 | 類型 | 是否必選 | 說明 |
header.action | string | 是 | 指令類型。 當前指令中,固定為"continue-task"。 |
header.task_id | string | 是 | 當次任務ID。 需要和發送run-task指令時使用的task_id保持一致。 |
header.streaming | string | 是 | 固定字串:"duplex" |
payload參數說明:
參數 | 類型 | 是否必選 | 說明 |
input.text | string | 是 | 待合成文本。 |
3. finish-task指令:結束任務
該指令用於結束語音合成任務。
請務必確保發送該指令,否則會出現以下問題:
音頻不完整:服務端緩衝的不完整語句不會被強制合成,導致音頻缺失尾部內容。
連線逾時:如果在最後一次continue-task指令後超過 23 秒未發送 finish-task,串連會因逾時而斷開。
計費異常:未正常結束的任務可能無法返回準確的 usage 資訊。
發送時機:finish-task 應在所有continue-task指令發送完畢後立即發送,不要等待音頻返回完畢或延遲發送,否則可能觸發逾時。
樣本:
{
"header": {
"action": "finish-task",
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"streaming": "duplex"
},
"payload": {
"input": {}//input不能省去,否則會報錯
}
}header參數說明:
參數 | 類型 | 是否必選 | 說明 |
header.action | string | 是 | 指令類型。 當前指令中,固定為"finish-task"。 |
header.task_id | string | 是 | 當次任務ID。 需要和發送run-task指令時使用的task_id保持一致。 |
header.streaming | string | 是 | 固定字串:"duplex" |
payload參數說明:
參數 | 類型 | 是否必選 | 說明 |
payload.input | object | 是 | 固定格式:{}。 |
事件(服務端→用戶端)
事件是服務端返回給用戶端的訊息,為JSON格式,代表不同的處理階段。
服務端返回給用戶端的二進位音頻不包含在任何事件中,需單獨接收。
1. task-started事件:任務已開啟
當監聽到服務端返回的task-started事件時,標誌著任務已成功開啟。只有在接收到該事件後,才能向服務端發送continue-task指令或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事件
用戶端發送continue-task指令和finish-task指令的同時,服務端持續返回result-generated事件。
為了讓使用者能夠將音頻資料與對應的常值內容關聯,服務端在返迴音頻資料的同時,通過result-generated事件返回句子的元資訊。服務端會對使用者輸入的文本進行自動分句,每個句子的合成過程包含以下3個子事件:
sentence-begin:標識句子開始,返回待合成的句子常值內容sentence-synthesis:標識音頻資料區塊,每個此事件後立即通過WebSocket binary通道傳輸一個音頻資料幀一個句子的合成過程中會產生多個
sentence-synthesis事件,每個對應一個音頻資料區塊用戶端需要按順序接收這些音頻資料區塊並以追加模式寫入同一檔案
sentence-synthesis事件與其後的音頻資料幀是一一對應的關係,不會出現錯位
sentence-end:標識句子結束,返回句子常值內容和累計的計費字元數
通過payload.output.type欄位區分子事件類型。
樣本:
sentence-begin
{
"header": {
"task_id": "3f2d5c86-0550-45c0-801f-xxxxxxxxxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"sentence": {
"index": 0,
"words": []
},
"type": "sentence-begin",
"original_text": "床前明月光,"
}
}
}sentence-synthesis
{
"header": {
"task_id": "3f2d5c86-0550-45c0-801f-xxxxxxxxxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"sentence": {
"index": 0,
"words": []
},
"type": "sentence-synthesis"
}
}
}sentence-end
{
"header": {
"task_id": "3f2d5c86-0550-45c0-801f-xxxxxxxxxx",
"event": "result-generated",
"attributes": {}
},
"payload": {
"output": {
"sentence": {
"index": 0,
"words": []
},
"type": "sentence-end",
"original_text": "床前明月光,"
},
"usage": {
"characters": 11
}
}
}header參數說明:
參數 | 類型 | 說明 |
header.event | string | 事件類型。 當前事件中,固定為"result-generated"。 |
header.task_id | string | 用戶端產生的task_id。 |
header.attributes | object | 附加屬性,通常為空白對象。 |
payload參數說明:
參數 | 類型 | 說明 |
payload.output.type | string | 子事件類型。 取值範圍:
完整的事件流程 對於每個待合成的句子,服務端按以下順序返回事件:
|
payload.output.sentence.index | integer | 句子的編號,從0開始。 |
payload.output.sentence.words | array | 字層級資訊數組,通常為空白數組。 |
payload.output.original_text | string | 對使用者輸入文本進行分句後的句內容。最後一個句子可能沒有此欄位。 |
payload.usage.characters | integer | 截止當前,本次請求中計費的有效字元數。
在一次任務中, |
3. task-finished事件:任務已結束
當監聽到服務端返回的task-finished事件時,說明任務已結束。
結束任務後可以關閉WebSocket串連結束程式,也可以複用WebSocket串連,重新發送run-task指令開啟下一個任務(參見關於建連開銷和串連複用)。
樣本:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-finished",
"attributes": {
"request_uuid": "0a9dba9e-d3a6-45a4-be6d-xxxxxxxxxxxx"
}
},
"payload": {
"output": {
"sentence": {
"words": []
}
},
"usage": {
"characters": 13
}
}
}header參數說明:
參數 | 類型 | 說明 |
header.event | string | 事件類型。 當前事件中,固定為"task-finished"。 |
header.task_id | string | 用戶端產生的task_id。 |
header.attributes.request_uuid | string | Request ID,可提供給CosyVoice開發人員定位問題。 |
payload參數說明:
參數 | 類型 | 說明 |
payload.usage.characters | integer | 截止當前,本次請求中計費的有效字元數。
在一次任務中, |
payload.output.sentence.index | integer | 句子的編號,從0開始。 本欄位和以下欄位需要通過word_timestamp_enabled開啟字層級時間戳記 |
payload.output.sentence.words[k] | ||
text | string | 字的文本。 |
begin_index | integer | 字在句子中的開始位置索引,從 0 開始。 |
end_index | integer | 字在句子中的結束位置索引,從 1 開始。 |
begin_time | integer | 字對應音訊開始時間戳,單位為毫秒。 |
end_time | integer | 字對應音訊結束時間戳記,單位為毫秒。 |
時間戳記資料提取最佳實務
開啟 word_timestamp_enabled 後,時間戳記資訊會在 task-finished 事件中返回。樣本如下:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-finished",
"attributes": {"request_uuid": "0a9dba9e-d3a6-45a4-be6d-xxxxxxxxxxxx"}
},
"payload": {
"output": {
"sentence": {
"index": 0,
"words": [
{
"text": "今",
"begin_index": 0,
"end_index": 1,
"begin_time": 80,
"end_time": 200
},
{
"text": "天",
"begin_index": 1,
"end_index": 2,
"begin_time": 240,
"end_time": 360
},
{
"text": "天",
"begin_index": 2,
"end_index": 3,
"begin_time": 360,
"end_time": 480
},
{
"text": "氣",
"begin_index": 3,
"end_index": 4,
"begin_time": 480,
"end_time": 680
},
{
"text": "怎",
"begin_index": 4,
"end_index": 5,
"begin_time": 680,
"end_time": 800
},
{
"text": "麼",
"begin_index": 5,
"end_index": 6,
"begin_time": 800,
"end_time": 920
},
{
"text": "樣",
"begin_index": 6,
"end_index": 7,
"begin_time": 920,
"end_time": 1000
},
{
"text": "?",
"begin_index": 7,
"end_index": 8,
"begin_time": 1000,
"end_time": 1320
}
]
}
},
"usage": {"characters": 15}
}
}
正確的提取方式:
僅在 task-finished 事件中提取完整時間戳記:完整的句子時間戳記資料僅在任務結束時(task-finished 事件)返回,包含 payload.output.sentence.words 數組。
result-generated 事件不包含時間戳記:result-generated 事件主要用於標識音頻流的產生進度,不包含字層級時間戳記資訊。
事件過濾樣本(Python):
def on_event(message): event_type = message["header"]["event"] # 僅在 task-finished 事件中提取時間戳記 if event_type == "task-finished": words = message["payload"]["output"]["sentence"]["words"] for word in words: print(f"文字: {word['text']}, 開始: {word['begin_time']}ms, 結束: {word['end_time']}ms") # result-generated 事件用於音頻流處理 elif event_type == "result-generated": # 處理音頻流,不提取時間戳記 pass
如果在多個事件中提取時間戳記資料,會導致重複。請確保僅在 task-finished 事件中提取。
4. task-failed事件:任務失敗
如果接收到task-failed事件,表示任務失敗。此時需要關閉WebSocket串連並處理錯誤。通過分析報錯資訊,如果是由於編程問題導致的任務失敗,您可以調整代碼進行修正。
樣本:
{
"header": {
"task_id": "2bf83b9a-baeb-4fda-8d9a-xxxxxxxxxxxx",
"event": "task-failed",
"error_code": "InvalidParameter",
"error_message": "[tts:]Engine return error code: 418",
"attributes": {}
},
"payload": {}
}header參數說明:
參數 | 類型 | 說明 |
header.event | string | 事件類型。 當前事件中,固定為task-failed。 |
header.task_id | string | 用戶端產生的task_id。 |
header.error_code | string | 報錯類型描述。 |
header.error_message | string | 具體報錯原因。 |
任務中斷方式
在流式合成過程中,如需提前終止當前任務(如使用者取消播放、即時對話中打斷等),可通過以下方式實現:
中斷方式 | 服務端行為 | 適用情境 |
直接關閉串連 |
| 立即中斷:使用者取消播放、切換內容、應用退出等 |
發送 finish-task |
| 優雅結束:停止發送新文本,但需接收已緩衝內容的音頻 |
發起新 run-task |
| 任務切換:即時對話中使用者打斷,立即切換到新內容 |
關於建連開銷和串連複用
WebSocket服務支援串連複用以提升資源的利用效率,避免建立串連開銷。
服務端收到用戶端發送的 run-task指令後,將啟動一個新的任務,用戶端發送finish-task指令後,服務端在任務完成時返回task-finished事件以結束該任務。結束任務後WebSocket串連可以被複用,用戶端重新發送run-task指令即可開啟下一個任務。
在複用串連中的不同任務需要使用不同 task_id。
如果在任務執行過程中發生失敗,服務將依然返回task-failed事件,並關閉該串連。此時這個串連無法繼續複用。
如果在任務結束後60秒沒有新的任務,串連會逾時自動斷開。
效能指標與並發限制
並發限制
具體限制請參見限流。
如需提升並發配額(如支援更多並發串連數),請聯絡客服申請。配額調整可能需要審核,一般在 1~3 個工作日內完成。
最佳實務:為提高資源使用率,建議複用 WebSocket 串連處理多個任務,而非為每個任務建立新串連。參見關於建連開銷和串連複用。
串連效能與延遲
正常串連耗時:
中國內地用戶端:WebSocket 串連建立(從 newWebSocket 到 onOpen)通常耗時 200~1000 毫秒。
跨境串連(如香港、海外地區):可能出現 1~3 秒的串連延遲,偶發情況下可能達到 10~30 秒。
串連耗時過長排查:
如果 WebSocket 串連建立耗時超過 30 秒,可能的原因包括:
網路問題:用戶端與服務端之間的網路延遲較高(如跨境串連、電訊廠商網路品質問題)。
DNS 解析慢:dashscope.aliyuncs.com 的 DNS 解析耗時較長。可嘗試使用公用 DNS(如 8.8.8.8)或配置本地 hosts 檔案。
TLS 握手慢:用戶端 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
CosyVoice WebSocket API 部署在中國內地(北京)地區。如果用戶端位於其他地區(如香港、海外),建議使用就近的代理服務器或 CDN 加速串連。
音頻產生效能
合成速度:
即時率(RTF): CosyVoice 各模型的合成速度通常為 0.1~0.5 倍即時率(即 1 秒音頻約需 0.1~0.5 秒產生),具體速度取決於模型版本、文本長度和服務端負載。
首包延遲:從發送 continue-task 指令到接收到第一個音頻分區,通常在 200~800 毫秒之間。
範例程式碼
範例程式碼僅提供最基礎的服務調通實現,實際業務情境的相關代碼需您自行開發。
在編寫WebSocket用戶端代碼時,為了同時發送和接收訊息,通常採用非同步編程。您可以按照以下步驟來編寫程式:
錯誤碼
如遇報錯問題,請參見錯誤資訊進行排查。
常見問題
功能特性/計量計費/限流
Q:當遇到發音不準的情況時,有什麼解決方案可以嘗試?
通過SSML可以對語音合成效果進行個人化定製。
Q:為什麼使用WebSocket協議而非HTTP/HTTPS協議?為什麼不提供RESTful API?
Voice Messaging Service選擇 WebSocket 而非 HTTP/HTTPS/RESTful,根本在於其依賴全雙工系統通訊能力——WebSocket 允許服務端與用戶端主動雙向傳輸資料(如即時推送語音合成/識別進度),而基於 HTTP 的 RESTful 僅支援用戶端發起的單向要求-回應模式,無法滿足即時互動需求。
Q:語音合成是按文本字元數計費的,要如何查看或擷取每次合成的文本長度?
通過服務端返回的result-generated事件中payload.usage.characters參數擷取字元數,請以收到的最後一個result-generated事件為準。
故障排查
Q:如何擷取Request ID?
通過以下兩種方式可以擷取:
解析result-generated事件中服務端返回的資訊。
解析task-finished事件中服務端返回的資訊。
Q:使用SSML功能失敗是什麼原因?
請按以下步驟排查:
確保限制與約束正確
確保用正確的方式進行調用,詳情請參見SSML標記語言支援說明
確保待合成文本為純文字格式且符合格式要求,詳情請參見SSML標記語言介紹
Q:為什麼音頻無法播放?
請根據以下情境逐一排查:
音頻儲存為完整檔案(如xx.mp3)的情況
音頻格式一致性:確保請求參數中設定的音頻格式與檔案尾碼一致。例如,如果請求參數設定的音頻格式為wav,但檔案尾碼為mp3,可能會導致播放失敗。
播放器相容性:確認使用的播放器是否支援該音頻檔案的格式和採樣率。例如,某些播放器可能不支援高採樣率或特定編碼的音頻檔案。
流式播放音訊情況
將音頻流儲存為完整檔案,嘗試使用播放器播放。如果檔案無法播放,請參考情境 1 的排查方法。
如果檔案可以正常播放,則問題可能出在流式播放的實現上。請確認使用的播放器是否支援流式播放。
常見的支援流式播放的工具和庫包括:ffmpeg、pyaudio (Python)、AudioFormat (Java)、MediaSource (Javascript)等。
Q:為什麼音頻播放卡頓?
請根據以下情境逐一排查:
檢查文本發送速度: 確保發送文本的間隔合理,避免前一句音頻播放完畢後,下一句文本未能及時發送。
檢查回呼函數效能:
檢查回呼函數中是否存在過多商務邏輯,導致阻塞。
回呼函數運行在 WebSocket 線程中,若被阻塞,可能會影響 WebSocket 接收網路資料包,進而導致音頻接收卡頓。
建議將音頻資料寫入一個獨立的音頻緩衝區(audio buffer),然後在其他線程中讀取並處理,避免阻塞 WebSocket 線程。
檢查網路穩定性: 確保網路連接穩定,避免因網路波動導致音頻傳輸中斷或延遲。
Q:語音合成慢(合成時間長)是什麼原因?
請按以下步驟排查:
檢查輸入間隔
如果是流式語音合成,請確認文字發送間隔是否過長(如上段發出後延遲數秒才發送下段),過久間隔會導致合成總時間長度增加。
分析效能指標
首包延遲:正常500ms左右。
RTF(RTF = 合成總耗時/音頻時間長度):正常小於1.0。
Q:合成的語音發音錯誤如何處理?
請使用SSML的<phoneme>標籤指定正確的發音。
Q:為什麼沒有返回語音?為什麼結尾部分的文本沒能成功轉換成語音?(合成語音缺失)
請確認是否忘記發送finish-task指令。在語音合成過程中,服務端會在緩衝足夠文本後才開始合成。如果忘記發送finish-task指令,可能會導致緩衝中的結尾部分文本未能被合成為語音。
Q:為什麼返回的音頻流順序錯亂?導致播放內容混亂
請從以下兩個方面排查:
確保同一個合成任務的 run-task指令、continue-task指令和finish-task指令使用相同的
task_id。檢查非同步作業是否導致音頻檔案寫入順序與接收位元據的順序不一致。
Q:WebSocket 串連錯誤如何處理?
WebSocket 串連關閉(code 1007)如何處理?
WebSocket 串連在發送 run-task 指令後立即關閉,且關閉碼為 1007。
錯誤原因:服務端檢測到協議或資料格式錯誤,主動中斷連線。常見原因包括:
run-task 指令的 payload 中包含非法欄位(如在 payload 中誤加了
"input": {}以外的其他欄位)。JSON 格式錯誤(如缺少逗號、括弧不匹配等)。
必要欄位缺失(如 task_id、action 等)。
解決方案:
檢查 JSON 格式:驗證請求體格式是否正確。
檢查必要欄位:確認 header.action、header.task_id、header.streaming、payload.task_group、payload.task、payload.function、payload.model、payload.input 均已正確設定。
移除無效欄位:run-task 的 payload.input 中僅允許Null 物件
{}或包含 text 欄位,不要添加其他欄位。
WebSocket 串連報錯 WebSocketBadStatus 或 401/403 如何處理?
在建立 WebSocket 串連時報錯 WebSocketBadStatus、401 Unauthorized 或 403 Forbidden。
錯誤原因: 鑒權失敗。服務端在 WebSocket 握手階段驗證 Authorization 頭,如果 API Key 無效或缺失,將拒絕串連。
解決方案:參見鑒權失敗排查步驟。
許可權與認證
Q:我希望我的 API Key 僅用於 CosyVoice 語音合成服務,而不被百鍊其他模型使用(許可權隔離),我該如何做?
可以通過建立業務空間並只授權特定模型來限制API Key的使用範圍。詳情請參見業務空間管理。
更多問題
請參見GitHub QA。