部落格技術

我們的音樂 AI 說改了譜號,其實一個音都沒動

一次什麼都沒改的修改回了「已套用」,讀譜結果藏住了曲中換的譜號,模型又相信了圖片。下面逐條講原因和修法,以及我們還沒測量的部分。

簡短回答

2026年10月10日,我們的內建助理 Starling 告訴一位使用者,它已把第 4 小節的左手改成低音譜號。可那個小節仍是高音譜號。三件事湊到了一起:編輯工具對一個什麼都沒改的修改回了「已套用」;我們給模型的樂譜文字讀法不顯示曲中的譜號變化;模型又相信了譜面圖片。我們先在工具裡把三處都修好,再加了一條共用規則;修改前後的比較測量還沒有跑。

發生了什麼?

一份鋼琴譜的下方譜表在第 4 小節換成高音譜號(那裡的分解和弦位置很高),之後又換回低音譜號。使用者在樂譜旁邊讓 Starling 把第 4 小節的左手改成低音譜號。Starling 呼叫了編輯工具,拿到成功的回覆,就說改好了。可頁面上第 4 小節仍是高音譜號。使用者說根本沒變時,模型順著看起來更可能的說法附和,而不是回去重讀那個小節。

我們是在同一天,讀了測試環境上儲存的那段對話和這份樂譜的版本歷史才發現的。

助理為什麼以為修改成功了?

因為它手上的每個資訊來源要麼說成功了,要麼什麼都沒說。單獨哪一個原因都還不至於出事。

  1. 要求「第 4 小節,左手,低音譜號」
  2. 修改設定的是這一行開頭的譜號,而它本來就是低音譜號
  3. 結果「已套用」,還附上新的版本號
  4. 讀譜只顯示開頭的譜號,看不出哪裡不對
  5. 回覆「改好了」
錯誤的報告是這樣形成的:一個什麼都沒改的修改照樣傳回成功和新版本,而模型核對時讀到的樂譜文字,看不到第 4 小節裡的譜號變化。
  • 修改什麼都沒變,卻照樣成功。我們的工具把任何合法請求都當成新版本。把一行譜表設成它本來的譜號是合法的,於是伺服器儲存了一份一模一樣的樂譜,回覆「已套用」並給出新的版本號。模型沒有理由懷疑。
  • 讀譜結果藏住了關鍵資訊。樂譜的文字讀法只列出每個聲部開頭的譜號和調號,不列曲中的變化。第 4 小節換的譜號在文字裡根本看不見。更糟的是,小節末尾用來提醒下一小節換譜號的預示譜號,被算成了這個小節自己的譜號。
  • 模型相信了圖片。文字和渲染出來的譜面對不上、或者文字什麼都沒說的時候,模型就看圖判斷,而一行譜中間的小譜號很容易看錯。

我們改了什麼?

一共四處改動,都在 2026年10月10日完成,改在 Starling 和 MCP 聯結器共用的伺服器裡。

  1. 讓樂譜保持原樣的修改會被拒絕。工具傳回一個錯誤:「沒有變化:這項修改不會讓樂譜有任何不同,沒有儲存新版本」,版本號也不變。復原和重做不受影響。
  2. 每次修改都列出實際寫了什麼。結果裡現在會有這樣的行:bar 4: lower staff clef treble -> bass 或 bars 1-4: key C major -> F major,這樣模型報告的是樂譜上寫的,而不是它請求的。
  3. 讀譜會顯示曲中的變化。樂譜讀法會在譜號、調號和拍號變化的小節寫出來,檢查文字會就地標出「從這裡起高音譜號」和轉調,預示譜號也改算到它預告的那個小節。
  4. 所有入口共用一條規則。Starling、MCP 伺服器和我們外掛程式技能裡的說明現在都寫著:只說工具結果裡顯示的;報錯或「沒有變化」就表示沒改,要照實說;譜號、調號和音符要按文字讀譜判斷,不看圖片;使用者有異議,就重讀那幾個小節。

順序很重要。前兩條把事實放進了模型讀到的結果裡,這是提示詞怎麼寫都做不到的。MCP 規範也是這樣劃分的:執行工具時出的錯應放在工具結果裡,標上 isError,讓模型看得到並自行糾正。我們那句「已套用」,本該是一個錯誤。

我們怎麼測試?

兩種辦法,一種已經完成,一種還在等。

  • 測試套件裡有一項檢查:開啟一份下方譜表在小節中間換譜號、後面又轉調的樂譜,確認每種讀法都顯示這兩處變化,修改會列出寫入的譜號和調號,什麼都不改的修改會被拒絕且不產生新版本。它和其他快速檢查一起,在每個 Pull Request 上執行。
  • 一項評測會就一首按出事樂譜構造的 12 小節鋼琴曲,給 Starling 發六個請求:一次真正的譜號修改,對已經是低音譜號的小節做同樣的修改,問轉調之後是什麼調,一個什麼都不改的音高修改,一個踏板修改,以及數小節數。另一個模型列出回覆裡的每個說法,並對照這一輪之後儲存下來的樂譜逐條判斷,從不對照助理自己的話。修改前後的比較還沒有跑,所以我們還沒有幻覺率可以報告。

規則能降低錯誤說法的比例,但不能降到零。模型單看圖片仍可能看錯小節,所以規則讓它以文字為準,它做的修改也都會標出來、可以復原。

給用工具打造 AI 代理的人,我們有什麼建議?

  • 工具永遠不該為「什麼都沒做」報告成功。如果一次呼叫改變不了狀態,就傳回一個模型讀得懂的錯誤。
  • 傳回實際寫入的內容,用這個領域自己的說法,而不是複述請求。
  • 模型核對工作所需的每個事實都要放在文字裡。如果事實只在圖片裡,模型就會看圖判斷。
  • 去讀對話記錄。我們是從一段儲存下來的對話裡發現這個問題的,不是從某個指標。

參考資料

  1. Model Context Protocol,Specification 2025-11-25: Tools,Error Handling 一節。
  2. ScoreStarling 2026年10月10日儲存的對話和樂譜歷史,以及之後的修改和測試。

常見問題

工具為什麼會對一個什麼都沒改的修改報告成功?

我們的工具把每個合法請求都當成一個新版本:把譜號設成它本來的譜號是合法的,於是儲存了一份一模一樣的樂譜,回覆「已套用」。從 2026年10月10日起,這類修改會被拒絕,提示「沒有變化」,也不會儲存新版本。

光靠提示詞裡的規則,能阻止 AI 代理謊稱做過修改嗎?

靠不住。規則有用,但模型還得知道真相是什麼。最關鍵的改動在工具裡:拒絕空操作,並且列出每次修改實際寫了什麼,讓事實出現在模型讀到的結果裡。

ChatGPT 和 Claude 也適用嗎?

適用。Starling 和 MCP 聯結器呼叫的是同一套工具,所以 ChatGPT、Claude 和其他 MCP 用戶端也會收到同樣的拒絕和同樣的修改清單,伺服器的說明裡也有同一條規則。

帶來任何音樂帶走一份樂譜