博客技术

我们的音乐 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,让模型看得到并自行纠正。我们那句“已应用”,本该是一个错误。

我们怎么测试?

两种办法,一种已经完成,一种还在等。

  • 测试套件里有一项检查:打开一份下方谱表在小节中间换谱号、后面又转调的乐谱,确认每种读法都显示这两处变化,修改会列出写入的谱号和调号,什么都不改的修改会被拒绝且不产生新版本。它和其他快速检查一起,在每个拉取请求上运行。
  • 一项评测会就一首按出事乐谱构造的 12 小节钢琴曲,给 Starling 发六个请求:一次真正的谱号修改,对已经是低音谱号的小节做同样的修改,问转调之后是什么调,一个什么都不改的音高修改,一个踏板修改,以及数小节数。另一个模型列出回复里的每个说法,并对照这一轮之后保存下来的乐谱逐条判断,从不对照助手自己的话。修改前后的对比还没有跑,所以我们还没有幻觉率可以报告。

规则能降低错误说法的比例,但不能降到零。模型单看图片仍可能看错小节,所以规则让它以文本为准,它做的修改也都会标出来、可以撤销。

给用工具搭智能体的人,我们有什么建议?

  • 工具永远不该为“什么都没做”报告成功。如果一次调用改变不了状态,就返回一个模型读得懂的错误。
  • 返回实际写入的内容,用这个领域自己的说法,而不是复述请求。
  • 模型核对工作所需的每个事实都要放在文本里。如果事实只在图片里,模型就会看图判断。
  • 去读对话记录。我们是从一段保存下来的对话里发现这个问题的,不是从某个指标。

参考资料

  1. Model Context Protocol,Specification 2025-11-25: Tools,Error Handling 一节。
  2. ScoreStarling 2026年10月10日保存的对话和乐谱历史,以及之后的修改和测试。

常见问题

工具为什么会对一个什么都没改的修改报告成功?

我们的工具把每个合法请求都当成一个新版本:把谱号设成它本来的谱号是合法的,于是保存了一份一模一样的乐谱,回复“已应用”。从 2026年10月10日起,这类修改会被拒绝,提示“没有变化”,也不会保存新版本。

光靠提示词里的规则,能阻止智能体谎称做过修改吗?

靠不住。规则有用,但模型还得知道真相是什么。最关键的改动在工具里:拒绝空操作,并且列出每次修改实际写了什么,让事实出现在模型读到的结果里。

ChatGPT 和 Claude 也适用吗?

适用。Starling 和 MCP 连接器调用的是同一套工具,所以 ChatGPT、Claude 和其他 MCP 客户端也会收到同样的拒绝和同样的修改清单,服务器的说明里也有同一条规则。

带来任何音乐带走一份乐谱