Skip to content

async transcribe job 不跨 MCP 連線存活 — stdio server 重啟後 unknown job,長音檔的 async 模式實質不可用 #189

Description

@kiki830621

Problem

原話(使用者,2026-08-24):
「用同一條本機路徑(bestASR 的 MCP 今晚斷過兩次,不再依賴): 幫我記錄這個斷線的問題」

一個晚上的實際使用中,transcribeasync 模式因 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 模式在斷線後仍可用:

  1. Job state 持久化到磁碟 —— ~/.bestasr/jobs/<job_id>.json 之類,重連後 transcribe_status 仍查得到。這是最小修法,不改行程模型。
  2. output_path 語意保證 —— 若 job 帶了 output_path,即使 client 斷線也把結果寫到該路徑。目前 async job 斷線後該路徑不會出現任何檔案,即使運算可能已部分完成。
  3. 明示 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 的斷線問題無關。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions