即時轉錄
透過 Muse Voice Transcribe WebSocket 持續傳送 PCM 音訊,並接收即時轉錄事件。
Muse Voice Transcribe 即時 API 透過持久的 WebSocket 接收未封裝的 PCM 音訊,並持續回傳進度、語音輪次和轉錄事件。此 API 只生成文字,不生成語音回覆。
wss://api.agtcloud.cn/v1/asr/realtime身分驗證與連線
WebSocket 建立後,應在 10 秒內傳送第一個 JSON 文字訊框。Bearer API Key 位於 authorization.accessToken,而不是 WebSocket 握手 Header。
| 項目 | 值 |
|---|---|
| 連線位址 | wss://api.agtcloud.cn/v1/asr/realtime |
| 身分驗證位置 | 第一個 JSON 文字訊框的 authorization.accessToken |
| 模型 | muse-voice-transcribe-1.0 |
| 選用查詢參數 | sessionId,只會直接轉送工作階段識別碼,不代表支援斷線恢復或歷史工作階段查詢 |
第一個訊框不包含 type,大小上限為 64 KiB。收到包含 sessionId 的成功回應後,再開始傳送音訊;WebSocket 升級成功不代表第一個訊框的身分驗證已經通過。
工作階段流程
- 建立 WebSocket 連線。
- 傳送包含身分驗證、模型和音訊設定的第一個訊框。
- 等待伺服器端回傳
sessionId。 - 依即時速率傳送未封裝的 PCM 二進位訊框,同時持續接收事件。
- 音訊結束後傳送
{"type":"endStream"}。 - 繼續接收結尾的轉錄和關閉訊息。
speechComplete 只表示一個語音輪次完成,不表示整個工作階段結束。傳送 endStream 後仍應繼續讀取伺服器端事件,直到連線關閉。
第一個訊框的參數
| 欄位 | 類型 | 說明 |
|---|---|---|
authorization.accessToken | string | 必填,格式為 Bearer your-api-key |
model | string | 必填,使用 muse-voice-transcribe-1.0 |
audioEncoding | string | 必填,即時音訊使用 PCM_16KHZ 或 PCM_24KHZ |
mode | string | 選用:PUSH_TO_TALK、ENDPOINTING、DIARIZATION |
partialMode | string | 選用:CUMULATIVE 回傳累計文字,DELTA 回傳新增文字 |
keywords | string[] | 選用,用於提示術語或專有名詞 |
languageBias | string[] | 選用,用於提示預期語言 |
emitAudioProgress | boolean | 選用,指定是否回傳音訊處理進度事件 |
zdrOverride | boolean | 選用的資料處理選項;是否生效取決於目前的服務和帳戶權限 |
音訊輸入
音訊必須是不含 WAV 檔頭的 PCM16 little-endian 單聲道資料,並與第一個訊框宣告的 16 kHz 或 24 kHz 取樣率一致。每個音訊訊框不得超過 1 MiB,位元組數必須是偶數。
建議以 100 ms 為一個音訊區塊,並依即時速率傳送:24 kHz 對應 4800 位元組,16 kHz 對應 3200 位元組。靜音期間如需維持連續時間軸,也應傳送相同格式的 PCM 靜音資料。
# 生成 24 kHz、單聲道、PCM16 的未封裝音訊
ffmpeg -i input.mp3 -ac 1 -ar 24000 -c:a pcm_s16le -f s16le audio.pcmPython 範例
安裝 WebSocket 用戶端並準備好 audio.pcm 後執行範例。傳送和接收分別在不同的執行緒中進行,避免音訊上傳阻塞伺服器端事件處理。
python -m pip install websocket-clientimport json
import sys
import threading
import websocket
url = "wss://api.agtcloud.cn/v1/asr/realtime"
stop = threading.Event()
sender = None
ws = websocket.create_connection(url, timeout=40, enable_multithread=True)
def send_audio():
try:
with open(sys.argv[1], "rb") as audio:
while not stop.is_set():
chunk = audio.read(4800)
if not chunk:
break
if len(chunk) % 2:
raise ValueError("PCM 位元組數必須是偶數")
ws.send_binary(chunk)
if stop.wait(len(chunk) / 48000):
return
if not stop.is_set():
silence = bytes(4800)
for _ in range(10):
ws.send_binary(silence)
if stop.wait(0.1):
return
ws.send(json.dumps({"type": "endStream"}))
except Exception as exc:
print("傳送中斷:", exc)
stop.set()
ws.close()
try:
ws.send(json.dumps({
"authorization": {"accessToken": "Bearer your-api-key"},
"model": "muse-voice-transcribe-1.0",
"audioEncoding": "PCM_24KHZ",
"mode": "ENDPOINTING",
"partialMode": "CUMULATIVE",
"languageBias": ["Mandarin Chinese"],
"emitAudioProgress": True
}))
ack = json.loads(ws.recv())
if ack.get("type") == "error" or not ack.get("sessionId"):
raise RuntimeError("連線初始化失敗:" + json.dumps(ack, ensure_ascii=False))
print("sessionId:", ack["sessionId"])
sender = threading.Thread(target=send_audio, daemon=True)
sender.start()
while not stop.is_set():
message = ws.recv()
if not message:
break
event = json.loads(message)
print(json.dumps(event, ensure_ascii=False))
if event.get("type") == "error":
break
finally:
stop.set()
ws.close()
if sender is not None:
sender.join(timeout=2)事件 Reference
audioProcessedMs 表示累計處理進度,不應將多個事件的值相加。使用 CUMULATIVE 時,新的部分文字會取代上一份累計結果;使用 DELTA 時,請附加增量文字。
audioProgress
回傳目前累計處理的音訊進度。
| 欄位 | 類型 | 必填 | 說明 | 範例值 |
|---|---|---|---|---|
type | 字串 | 是 | 固定為 audioProgress。 | audioProgress |
audioProcessedMs | 整數 | 是 | 累計處理的音訊長度,單位為毫秒。 | 1600 |
{
"type": "audioProgress",
"audioProcessedMs": 1600
}speechComplete.transcript 包含目前語音輪次的完整轉錄結果。工作階段結束時,final: true 的 transcript 事件可能不包含文字;用戶端不應使用空字串覆寫已儲存的累計文字或 speechComplete.transcript。
工作階段限制與錯誤處理
工作階段最長約 60 分鐘。若連續 15 秒未收到用戶端訊息,可能會開始結束輸入;網路或伺服器端狀態也可能提前結束連線。用戶端應設定總長度、讀取逾時和次數有限的重試策略。
| 關閉碼 | 意義 | 建議處理方式 |
|---|---|---|
1008 | 政策或協議錯誤 | 檢查第一個訊框的結構、身分驗證、參數和後續控制訊息 |
1011 | 伺服器端或上游中斷 | 儲存 sessionId、錯誤事件和關閉原因後,再決定是否重試 |
1013 | 服務暫時無法使用或受到速率限制 | 使用退避策略進行次數有限的重試 |
計費
即時轉錄依已處理的音訊長度計費,實際價格請參閱目前網站的模型清單。進度事件中的毫秒值用於表示處理時間軸,不應作為個別帳單逐筆累加。