只追「第一個音訊封包多快」很容易做出一個會搶話、停不下來,甚至講到一半換聲線的語音 Demo。Nari Qwen3-TTS 與 Qwen3-ASR 真正值得測的,不是單一榜單,而是把 ASR → LLM → TTS 接成一條可插話的回路後,繁中能否聽懂、任務有沒有做對、聲音是否維持同一位說話者。
這篇會帶你選 Cloud beta 或 H100 self-host 路線、跑通第一段串流音訊、加入取消與 timeout,再用 12 段繁中/英文案例做回歸驗收。先把邊界說清楚:截至 2026 年 9 月 15 日,Nari Hosted ASR 列有中文,Hosted TTS 的 voice pack 則只有英文與西班牙文;所以 Cloud 路線的繁中測試放在 ASR,TTS 測例要用其支援語言。這是一套可執行的驗收流程,不是替你的硬體與網路預填成績。
如果你還沒接過語音事件,先讀 Voice Agent Eval 的 12 段錄音法;若想先用完全可控的假元件練插話,可搭配 Gander 可插話 Mock 教學。
先說結論:Voice Agent 是五個零件,不是三個 API
先記住本文的核心式:
可用 Voice Agent = 耳朵(ASR)+大腦(LLM)+嘴巴(TTS)+煞車(取消/插話)+儀表板(評測)。
Nari 幫你提供耳朵與嘴巴,但輪替、狀態、播放佇列和安全閘門仍是你的 orchestrator 責任。最小上線標準也不是「有聲音」:ASR completed 才能授權高風險動作、使用者插話後舊音訊不能再漏出、每一輪都要保存相同的 run_id、時間點、音檔、文字、voice、seed 與 profile。
Nari Qwen3-TTS/ASR 是什麼?先看清能力邊界
Nari Cloud 提供 Hosted ASR WebSocket 與 Hosted TTS HTTP API。官方 模型與價格頁在本文截稿時仍列出 Free 型號,也列出 early-access partner 價格:TTS Standard/Fast 分別為每百萬字元 5/10 美元,ASR Standard/Fast 分別為每輸入小時 0.06/0.12 美元。Nari 發表文同時預告該週轉付費 GA;文件仍混用 public beta/alpha 與 partner 字眼,因此部署當天要重查型號、額度與帳單。
官方在 2026 年 9 月 14 日的 Coval 榜單快照報告 ASR p50 TTFS 44 ms、平均逐段 WER 約 3.6%,TTS p50 TTFA 63 ms、平均逐段 WER 約 3.8%;同文也明示榜單約每 30 分鐘更新。Coval 是外部持續量測,但當時樣本以短英文為主,不涵蓋台灣華語或完整 Agent。數字適合當「值得測」的線索,不等於你的繁中、網路、LLM 或播放 buffer 會得到相同結果。
另一條路是開源的 Nari Qwen3-TTS serving engine(本文檢查的 main commit)。它服務 Qwen3-TTS 1.7B CustomVoice,公開 HTTP 與增量文字 WebSocket;但官方容器要求 Linux x86_64、NVIDIA H100、CUDA 13 相容 driver,且 engine 主要只用英文測過。上游模型支援多語言,不代表這個 serving 實作已替你的繁中場景完成驗收。
先選路線:Cloud beta 還是 H100 self-host?

- Cloud:沒有 H100 時先選它。ASR 可收 16 kHz PCM16,TTS 串流輸出 24 kHz PCM16;Hosted TTS 目前以完整文字送 HTTP,官方把增量文字 WebSocket 標成 coming soon。
- H100 self-host:只把 TTS 搬到自己的 H100;ASR 仍可接 Nari Hosted API。這條路才有目前 repo 內的
response.cancel與增量文字協定。 - 不要混成一張成績單:Cloud 與 self-host 的模型 ID、網路、語言、計費與取消語意不同,應分兩個 baseline。
SGLang-Omni 維護者曾用 Nari 發布的映像與同一 Qwen checkpoint,在單張 H100、Ryan/英文、10 RPS 的條件下 獨立重跑出低於 50 ms 的 audible p95 TTFA。這支持「TTS 元件延遲值得深入測」,但該重跑沒有評 WER、speaker similarity 或主觀品質,更不能改寫成「整個 Voice Agent 低於 50 ms」。
把 ASR → LLM → TTS 接成會停的回路

Nari turn detection 文件提供 speech_started、speech_stopped、commit 與 transcript completed 事件。把 speech_started 當煞車觸發點:先清空本機播放佇列,再取消上一輪 LLM/TTS,遞增 turn_epoch;任何晚到、epoch 不符的音訊一律丟棄。對不可逆工具,partial transcript 只做 UI 或預取,等 completed 與必要確認才執行。
on speech_started:
turn_epoch += 1
playback.clear()
llm.abort(previous_turn)
tts.abort_or_cancel(previous_turn)
on transcript_completed(text, epoch):
reply = llm.stream(text, epoch)
for complete_clause in reply:
if epoch != turn_epoch: break
tts.stream(complete_clause, voice, seed, epoch)
on audio_chunk(chunk, epoch):
if epoch == turn_epoch: playback.enqueue(chunk)
Cloud TTS 中斷後不能續傳:關閉連線、丟掉 buffer,新一輪要從頭生成;官方未承諾 client abort 一定停止後端計算。Self-host WebSocket 可送 {"type":"response.cancel"},目前實作會呼叫 engine cancel;HTTP stream 提前關閉也在清理路徑取消 request。兩條路都要量「插話到最後一個舊 sample」,不能只記 API 回覆取消成功。
步驟一:Cloud key、ASR 與第一段串流音訊
先在 Nari 建立 API key,放進環境變數,不寫進程式碼或 Trace。Hosted TTS 的 voice 名稱區分大小寫,應先呼叫 GET /v1/voices?model=...取得當下清單;auto 只沿用 voice 語言,不會替你偵測或翻譯。
export NARI_API_KEY="你的 key"
curl --fail --silent --show-error --no-buffer \
https://api.narilabs.com/v1/audio/speech \
-H "Authorization: Bearer $NARI_API_KEY" \
-H "Content-Type: application/json" \
--data '{"model":"qwen3-tts-fast:free","input":"Hello, this is the first streaming check.","voice":"diana","response_format":"pcm","stream":true}' \
| ffplay -nodisp -autoexit -f s16le -ar 24000 -ac 1 -i -
ASR 依 官方 WebSocket quickstart連到 wss://api.narilabs.com/v1/realtime?intent=transcription,送 16 kHz、mono、PCM16、無 WAV header 的 100 ms chunks。先用 language: zh跑固定繁中集,再另開 en集;要自動辨識才設 null。partial 可能重寫先前內容,completed 才是該 item 的權威結果。
Free 方案的官方限制目前是每個 API region 共用 concurrency 2,每個 TTS model 每日 100 個 accepted requests、每個 ASR model 每日 100 個 accepted connections,00:00 UTC 重置;被接受後即使取消或失敗也不返還免費額度。初次除錯先跑 2 段 smoke test,不要讓無限重試吃完整天配額。
步驟二:在 H100 啟動 Nari Qwen3-TTS
docker run --rm --gpus all --platform linux/amd64 \
-p 8000:8000 \
-e HF_TOKEN \
-e QWEN3_TTS_PROFILE=balanced \
-v nari-qwen3-tts-cache:/home/nari/.cache \
ghcr.io/nari-labs/nari-qwen3-tts@sha256:5de567b3d5cc1b9943d5ae6a65dfd0d399d03035bac93c68e6d19cd5882f47af
curl --fail http://127.0.0.1:8000/health
curl --fail http://127.0.0.1:8000/ready
curl --fail http://127.0.0.1:8000/v1/models
上面固定的是本文日期查到的 linux/amd64 image manifest;換版時仍以 docker image inspect保存實際 RepoDigest。當日 latest 的 image revision 與 GitHub main 並非同一段歷史,雖然 runtime 原始碼相同,也不能只記「latest」。/health 可用只代表服務程序回應;/ready 要等 CUDA Graph capture 與 warm-up TTS 都完成才會通過。
先明確指定 balanced 建 baseline,再比較 ttfa(較小首段 Codec chunks)與 throughput(較大 chunks/batches)。目前 repo 的 raw CLI/container fallback 與 README 對「預設」描述並不完全一致,所以不要省略 profile。官方單 H100 跑分是特定英文負載下的供應商結果,不應直接變成你的 SLO。
第一個本機請求可沿用 OpenAI Audio Speech 形狀,model 固定為 Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice;先用官方已測的英文、固定 voice 與 seed。通過後再把完整句改成 self-host WebSocket 的 request.start → input_text.append → input_text.end,讓 LLM 每產生一個完整語意短句就送出,而不是每個 token 都塞進 TTS。
步驟三:用 12 段案例抓聲線漂移與假低延遲

A 組 01~04 固定同一批繁中/英文音檔,覆蓋人名、金額日期、中英夾雜、噪音與停頓;B 組 05~08 固定任務答案,驗 FAQ、缺欄位追問、timeout 轉文字、插話後零舊音洩漏;C 組 09~12 固定文字、voice、seed 與 profile,驗 8 秒短句、30 秒長句、三次重複、取消後重啟。Cloud 的 C 組只用英文/西班牙文;繁中 TTS 若走 self-host,要獨立標成尚待驗收的語言路線。
聲線漂移不能只靠「聽起來怪」。把長輸出切成前/中/後三段,用同一個 speaker-verification evaluator 算 embedding cosine similarity,再加入不同 voice 的負控制;門檻從「同 voice 重複三次」的分布校準,不搬用網路上的神奇常數。同時請不知道版本的聽者盲判「是否像同一位說話者」,因為單一 embedding 也可能受內容、語言與噪音影響。
一則 Show HN 使用者回報指出 33 秒音檔中途像換了 voice,但沒有公開音檔、prompt、seed、版本與 Trace;這是建立 Case 10 的現場訊號,不是普遍缺陷的證據。另有 vLLM-Omni 的 Qwen3-TTS 長中文串流案例追到 sentence-by-sentence 獨立 request 造成邊界換聲;改為連續 request 後邊界改善,卻仍有長距離音高漂移。兩者提醒我們:固定 seed 只是控制變因,還要分開查「編排切斷」與「模型長程漂移」。
七個指標怎麼量,才不會被首包數字騙?
- TTFS:同時記
speech_end → transcript_completed與commit → completed,不要混用 server VAD 與手動 commit。 - TTFA:
第一個可播放、非靜音 sample − TTS request 送出;WAV header 或第一個網路 byte 不算聲音。 - 英文 WER/中文 CER:英文用
(替換+刪除+插入) ÷ 參考詞數;中文另報字元錯誤率,並單列姓名、數字等關鍵槽位。 - Task Success:正確工具、參數與最終狀態且無額外動作才記 1;回覆說「完成」不算證據。
- Barge-in stop:
最後一個舊 sample − 使用者開始插話,另計取消後舊音訊 bytes,目標是 0。 - Voice Drift:前/中/後相似度、同聲線 baseline、異聲線負控制與盲判一起保存。
- 每分鐘成本:Cloud 為 TTS 字元費+ASR 秒數費+LLM 費;self-host 為 GPU、CPU、網路、儲存與維運總成本除以通過品質 gate 的有效分鐘,取消與重試也要算。
每次 run 輸出一列 JSONL:build_sha、route、profile、case_id、voice、seed、t_speech_end、t_asr_final、t_llm_first_clause、t_first_audio、t_last_old_sample、wer_or_cer、task_ok、audio_path、x_request_id。門檻要在看結果前寫進 manifest;小樣本先看逐案 delta 與 median,不用 12 次結果假裝穩定 P95。要決定 production 尾端 SLO,再擴到數百次觀測、不同 fresh starts,以及符合目標口音、裝置與網路的樣本。
Nari Qwen3-TTS 三種 profile 怎麼選?
選 profile 的順序是 hard gates → 任務成功 → 尾端延遲 → 成本。任何一輪換聲、舊音洩漏、錯工具或播放 underrun,該 build 先 fail;剩下的才比較 TTFA 與每分鐘成本。短客服開場先看 ttfa,大量並發看 throughput,不知道就明確指定 balanced。每個 profile 用同一套 12 案至少重複三次,保留 cold/warm 分組。
Timeout 也要分層:ASR 逾時提示重說,LLM 逾時回固定短句或文字,TTS 逾時直接降級成文字。依 Nari 錯誤處理文件,429 concurrency 先等 in-flight request 釋放,500/503 才以有限次數的 exponential backoff+jitter 重試。不要對每日額度耗盡做無限重試,也不要讓上一輪 retry 在新一輪復活。
最常見的 6 個失敗點
- 把第一個 byte 當 TTFA:你量到的可能是 header 或靜音,不是使用者聽見的聲音。
- partial 直接叫工具:串流文字會改寫;否定詞或數字翻轉時,外部狀態已來不及收回。
- Cloud TTS 塞繁中:Hosted voice pack 目前只列英/西語;ASR 支援中文不代表 TTS 也支援。
- 只停播放器:LLM/TTS 舊 coroutine 還在跑,稍後可能把過期音訊重新塞回 buffer。
- 固定 seed 就宣布不漂:seed 只是控制變因,長句仍要切段、重跑、盲判。
- 抄榜單當驗收:供應商快照、英文資料集與單 H100 壓測,都不能替代自己的語言、網路、任務和併發。
常見問題 FAQ
Q1:Nari Cloud TTS 可以直接說繁體中文嗎?
目前不要這樣規劃。Hosted ASR 列有中文,但 Hosted TTS voice pack 截至本文日期只有英文與西班牙文;上線前再查 voices endpoint。
Q2:沒有 H100 能做這篇流程嗎?
可以。先用 Cloud 串起 Hosted ASR/TTS 與自己的 LLM;只有 self-host Nari TTS engine 才硬性需要 H100。
Q3:/health 通過就能接流量嗎?
不能只看它。H100 路線要等 /ready 通過,因為 CUDA Graph capture 與 warm-up TTS 都完成後才 ready。
Q4:stream:true 就代表 Voice Agent 低延遲嗎?
不代表。它只讓 TTS 邊生成邊回傳;ASR endpointing、LLM 首個完整短句、網路與播放佇列仍會增加等待。
Q5:TTFS 和 TTFA 有什麼差別?
TTFS 看 ASR 第一份 final 語音辨識結果,TTFA 看 TTS 第一段可播放音訊。一定要寫明起點與終點,否則兩個數字都無法比較。
Q6:固定 seed 能消除 voice drift 嗎?
不能保證。它讓重跑更可比;是否漂移仍要看長句切段相似度、負控制、重複 runs 與盲聽。
Q7:可以把 Nari 榜單數字設成自己的 SLO 嗎?
不建議。榜單是特定時間、資料與測試方法的供應商快照;先用自己的 current build 跑出 baseline,再定門檻。
Q8:ttfa、balanced、throughput 該先用哪個?
先明確指定 balanced。它是折衷起點;接著只改 profile,以同一套案例比較首段音訊、underrun、任務成功與成本。
給新手的 7 步執行清單
- 先選 Cloud 或 H100,建立各自獨立 baseline。
- Cloud 查 voices;H100 等
/ready,再播放第一段 PCM。 - 讓 ASR、LLM、TTS、player 共用
run_id、turn_epoch與 monotonic clock。 - 把
speech_started接到清 buffer、取消舊工作與 epoch 失效。 - 建立 12 案 manifest,鎖音檔、文字、voice、seed、profile 與答案鍵。
- 先擋錯工具、舊音洩漏與換聲,再優化 TTFS/TTFA。
- 每個 production incident 都回填案例,留下修正前 fail、修正後 pass、舊案仍 pass 的證據。
想把這套回路接進完整的 Agent 工程,可先從 AI Agent Harness與 最小 Harness 實作補齊狀態與 Trace;更多模型、工具與評測教學在 AlphaLab AI 專區,系統化學習則可查看 AlphaLab 線上課程。
接著閱讀
左右滑動查看更多推薦
結語:先裝煞車,再追第一段音訊
回到開頭的五個零件:耳朵+大腦+嘴巴+煞車+儀表板。今天先跑 Case 08 的插話與 Case 10 的 30 秒長句;只要能證明舊音不再漏出、completed 才授權動作、同一聲線從頭到尾站得住,再去比較 ttfa 與 throughput。這樣做出的 Nari Voice Agent,才是可回歸、可定位、能放心繼續優化的系統。






