背景
- 2025-10 開始,由我負責架構、SvelteKit 前後端、Azure Speech 與 Azure OpenAI 整合、PostgreSQL 資料流程與部署。
- 題型包含短文朗讀、對話朗讀、長文朗讀、問答、看圖敘述、資訊回應與觀點陳述;提供中英文介面、簽名會話、速率限制、輸入清理與稽核記錄。
- 流程:瀏覽器錄音後轉成 16 kHz WAV 上傳,評分工作進入 BullMQ;worker 先以 Azure Speech 評估發音與流暢度,再以 Azure OpenAI 評估內容並產生回饋,最後彙整成績。
非同步評分:撐過限流、重啟與同時開考
- 問題:評分工作原本只嘗試一次,遇到 Azure 限流或逾時就永久失敗;壓力測試中也發現,部署重啟會把待評分工作重複排入佇列。
- 做法:改為最多 4 次、以小時為單位的指數退避,並以作答 ID 作為 job ID 去重,只有管理員手動重試時才取代舊工作;另自建壓力測試工具模擬整場考試。
- 結果:2000 個 session 規模的壓力測試 0 失敗。同一輪壓測也抓到 100 名學生同時開考時約 50% 請求失敗的問題,改用 READ COMMITTED 搭配唯一約束與重試後解決。
錄音可靠性:用稽核資料找出真正原因
- 問題:部分學生的錄音上傳後是空的,但前端只在 console 留下錯誤,無從追查。
- 做法:整場考試共用裝置檢查時取得的麥克風 stream,依瀏覽器協商實際支援的錄音格式,並把每次錄音的格式、大小、音量峰值與錯誤寫入稽核紀錄;裝置檢查也加上即時波形,拒收音量過小的錄音。
- 結果:稽核資料顯示 WebKit 的 webm 錄音有 165/291 筆只有 5 bytes,推翻了原本「冷啟動」的假設;改讓 WebKit 使用 mp4 後解決,並據此找出考試中 47 筆受影響的 iPad 作答。
評分公平:不讓語音辨識錯誤變成違規判定
- 問題:語音辨識把朗讀內容聽錯,觸發 AI 服務的內容過濾;原本流程會連同已算好的發音分數一起丟棄,甚至把整次作答判為違規,上線期間有 11 筆朗讀題因此被誤判。
- 做法:回饋生成失敗時保留語音分數,並把模型自身輸出被過濾的情況與學生違規分開處理;另依教學需求調整語音與內容的權重,附上不需重新呼叫 AI 的回填腳本。
- 結果:語音辨識錯誤不再讓作答被判違規或遺失分數,評分規則調整也能直接套用到既有成績。
