Problem
原話(使用者,2026-08-24):
「用同一條本機路徑(bestASR 的 MCP 今晚斷過兩次,不再依賴): 幫我記錄這個斷線的問題」
一個晚上的實際使用中,transcribe 的 async 模式因 MCP 連線中斷而兩次全損 —— 不是轉錄失敗,是 job 連同它的結果一起消失,且無法查詢、無法回收。最後改用本機 whisper-cli 直接跑才完成工作。
Type
bug
重現時序(兩次,同一晚)
| # |
事件 |
結果 |
| 1 |
transcribe(async=true) 1h46m 音檔 → job A 開始 |
{"job_id":"F1FB…","status":"running"} |
| 2 |
數分鐘後 MCP server 斷線 |
harness 通知 7 個 mcp__plugin_bestasr_bestasr__* 工具不再可用 |
| 3 |
使用者 /mcp 重連 |
Reconnected to plugin:bestasr:bestasr. |
| 4 |
transcribe_status(job A) |
unknown job: F1FB… |
| 5 |
重啟 job B,同一音檔 |
{"job_id":"5AC1B8B1…","status":"running"} |
| 6 |
再次斷線 |
同樣 7 個工具消失 |
兩次之間沒有任何人為操作觸發斷線,音檔也不同大小(66 MB / 297 MB)都會發生。
Root cause(架構層,非偶發)
.mcp.json 宣告 "type": "stdio":
{ "bestasr": { "type": "stdio",
"command": "${CLAUDE_PLUGIN_ROOT}/bin/bestasr-mcp-wrapper.sh", … } }
stdio server 的生命週期綁在 client 連線上 —— 連線斷 = 子行程死 = in-memory 的 job table 一併消失。第 4 步的 unknown job 不是查詢邏輯有誤,是那張表已經不存在。重連時 spawn 的是全新行程,對前一個行程建立的 job 一無所知。
同時觀察到本機有 15 個並存的 bestasr-mcp 行程(每個 Claude session 各一)。這佐證了 per-process 隔離:job 不但不跨重啟,也不跨 session。
Impact — async 模式對長音檔實質不可用
async 的設計目的就是長音檔(tool description 原文):
Use for long audio that would trip the client request timeout.
但這產生一個結構性矛盾:
- 音檔越長 → 越需要 async
- 跑得越久 → 期間斷線的機率越高
- 一斷 → 整個 job 連同已完成的運算全部作廢,且無法得知它曾經跑到哪
對 1h46m 的音檔,實測 whisper large-v3-turbo 需要約 200 秒運算。在那段區間內斷線兩次。運算成本已經付出,產物卻取不回來。
Expected
以下任一即可讓 async 模式在斷線後仍可用:
- Job state 持久化到磁碟 ——
~/.bestasr/jobs/<job_id>.json 之類,重連後 transcribe_status 仍查得到。這是最小修法,不改行程模型。
output_path 語意保證 —— 若 job 帶了 output_path,即使 client 斷線也把結果寫到該路徑。目前 async job 斷線後該路徑不會出現任何檔案,即使運算可能已部分完成。
- 明示 async 的失效邊界 —— 若 (1)(2) 都不做,至少在 tool description 講清楚「job 不跨連線存活」,讓呼叫端知道要自備 fallback。
Actual
unknown job,無 partial output,無錯誤說明,無法區分「job 還在跑」與「job 已隨行程消失」。
Workaround(本次採用)
繞過 MCP,直接用 bestASR 自己快取的模型跑本機 whisper-cli:
~/.bestasr/models/whisper-cpp/ggml-large-v3-turbo-q5_0.bin
1h46m 音檔 200 秒完成(32× realtime),26 分鐘音檔約 50 秒。模型與運算都沒問題 —— 問題純粹在 job 的生命週期管理。
這個 workaround 也反過來說明修法成本可能不高:實際跑轉錄的那層是好的,缺的是一層 job state 的持久化。
環境
- plugin
bestasr v0.16.0(PsychQuant/bestASR)
- binary
bestasr-mcp,2026-08-22 build
- macOS,Claude Code
- 兩次皆為
async=true + format=srt + diarize=true + language=zh
附帶觀察(非本 issue 主體)
轉錄品質在嘈雜環境有明顯 hallucination:1h46m 那支的最後 12 分鐘全部輸出 打赏明镜与点栏目(簡體,訓練資料裡的 YouTube 贊助字幕),且 2946 個 cue 中有 79% 是連續重複行。hallucination_filter 預設為 denylist 但未攔下這個 pattern。若需要,可另開 issue —— 與本 issue 的斷線問題無關。
Problem
一個晚上的實際使用中,
transcribe的 async 模式因 MCP 連線中斷而兩次全損 —— 不是轉錄失敗,是 job 連同它的結果一起消失,且無法查詢、無法回收。最後改用本機whisper-cli直接跑才完成工作。Type
bug
重現時序(兩次,同一晚)
transcribe(async=true)1h46m 音檔 → job A 開始{"job_id":"F1FB…","status":"running"}mcp__plugin_bestasr_bestasr__*工具不再可用/mcp重連Reconnected to plugin:bestasr:bestasr.transcribe_status(job A)unknown job: F1FB…{"job_id":"5AC1B8B1…","status":"running"}兩次之間沒有任何人為操作觸發斷線,音檔也不同大小(66 MB / 297 MB)都會發生。
Root cause(架構層,非偶發)
.mcp.json宣告"type": "stdio":stdio server 的生命週期綁在 client 連線上 —— 連線斷 = 子行程死 = in-memory 的 job table 一併消失。第 4 步的
unknown job不是查詢邏輯有誤,是那張表已經不存在。重連時 spawn 的是全新行程,對前一個行程建立的 job 一無所知。同時觀察到本機有 15 個並存的
bestasr-mcp行程(每個 Claude session 各一)。這佐證了 per-process 隔離:job 不但不跨重啟,也不跨 session。Impact — async 模式對長音檔實質不可用
async 的設計目的就是長音檔(tool description 原文):
但這產生一個結構性矛盾:
對 1h46m 的音檔,實測 whisper large-v3-turbo 需要約 200 秒運算。在那段區間內斷線兩次。運算成本已經付出,產物卻取不回來。
Expected
以下任一即可讓 async 模式在斷線後仍可用:
~/.bestasr/jobs/<job_id>.json之類,重連後transcribe_status仍查得到。這是最小修法,不改行程模型。output_path語意保證 —— 若 job 帶了output_path,即使 client 斷線也把結果寫到該路徑。目前 async job 斷線後該路徑不會出現任何檔案,即使運算可能已部分完成。Actual
unknown job,無 partial output,無錯誤說明,無法區分「job 還在跑」與「job 已隨行程消失」。Workaround(本次採用)
繞過 MCP,直接用 bestASR 自己快取的模型跑本機
whisper-cli:1h46m 音檔 200 秒完成(32× realtime),26 分鐘音檔約 50 秒。模型與運算都沒問題 —— 問題純粹在 job 的生命週期管理。
這個 workaround 也反過來說明修法成本可能不高:實際跑轉錄的那層是好的,缺的是一層 job state 的持久化。
環境
bestasrv0.16.0(PsychQuant/bestASR)bestasr-mcp,2026-08-22 buildasync=true+format=srt+diarize=true+language=zh附帶觀察(非本 issue 主體)
轉錄品質在嘈雜環境有明顯 hallucination:1h46m 那支的最後 12 分鐘全部輸出
打赏明镜与点栏目(簡體,訓練資料裡的 YouTube 贊助字幕),且 2946 個 cue 中有 79% 是連續重複行。hallucination_filter預設為denylist但未攔下這個 pattern。若需要,可另開 issue —— 與本 issue 的斷線問題無關。