‹ 返回笔记 · Back to notes

现场手记

claude code 又抽风了

Claude Code Threw Another Fit

做面板那两天,它每次要调工具干活,工具调用的格式标签就写坏一次,整段塌成乱码、活发不出去,我只能一次次按下中断。我把 16 次崩坏全扒开,挖到根上:它框结构用的标签,和它要传的内容,共用同一串文本、没有转义,内容里只要混进一段长得像标签的字符,系统就拆错位、把调用截断。这毛病有个老名字,叫定界符碰撞。

Opus 4.8Claude Code工具调用定界符碰撞事故复盘根因取证public-safe

AI 工作站 · 根因取证

上一篇讲它怎么"疯"。这一篇讲它另一种坏——不疯,不胡说,就是每次一张嘴干活,话到一半把自己噎住。我把它噎住的那 16 次全扒开了,一直挖到协议那一层。

六月二十四、二十五号那两天,我在让它做一个小面板(我自己看额度、看任务跑到哪的那个)。活不难,它也不糊涂。可那两天,我对它说得最多的三句话是:"继续""出错了,继续""又出现了错误"。

它每跑几下,就卡一下。不是想错了,是话没说出来。我得一次次按下中断键,把它从半截话里拽回来——光那个会话,我就按了 4 次。

它"塌"了

就在做那个面板的那个会话里,它的工具调用塌了 3 次,系统报错 5 次。

这"塌"是怎么回事,得先说清楚它是怎么"调用工具"的。它要让系统帮它跑条命令、改个文件,不是直接说大白话,而是靠一套 XML 风格的定界标签把这件事框出来:最外层用一对 function_calls 标签包住整批调用,里头每一次调用用一对 invoke 标签框起来、并标明调哪个工具(像 <invoke name="Bash">),每个参数再用一对 parameter 标签框住(像 <parameter name="command"> 后面跟上要跑的命令)。系统照着这些尖括号标签去拆,才知道它要调哪个工具、参数填的是什么。

那 3 次"塌",就是这套标签写坏了:本该成对的 invokeparameter 标签,缺了一截,或者在不该收尾的地方提前收了尾。系统照标签去拆,拆出来一堆对不上的碎片,认不出这是一次调用,只好整段当普通文字扔在一边——活,根本没发出去。我屏幕上那个"出错了",底下就是这个。

跟上次一样,我没停在"它出错了"。机器出错,有出错的道理。我让现在这台没出毛病的,把那 16 次崩坏一行一行扒开,看那套标签到底坏在哪。

扒出来一条挺一致的线索:16 次里能看清断口的有 6 次,断口上每回都压着同一种东西——它要传的内容里,本身就混着一段长得跟 parameterinvoke 这些定界标签几乎一样的字符。

根上是怎么回事:标签和内容,共用一套字符,没分家

问题就出在这儿:这套 invokeparameter 定界标签,和它要传的内容,走的是同一串文本、用的是同一套尖括号字符,中间没有一道"转义"——没有任何机制告诉系统:"这一段尖括号是真的定界标签、是结构;那一段尖括号只是内容里捎带的普通文字,别当真。"

于是,当它要传的内容里本身就含有这种尖括号标签,麻烦就来了。比如我让它改一段代码、一段网页,里头正好有成对的尖括号标签;比如我让它读你那些记录,整篇都是 invokeparameter 的调用日志。系统从头往下拆,拆到内容中间,撞见一段长得像 </parameter>(参数收尾)的字符,就以为"这个参数到这儿结束了",当场把后面真正的内容切断、把整个结构拆错位。这一次调用,就这么塌了。

这在工程上是个有名的老问题,叫定界符碰撞(delimiter collision)——用来分隔的符号,和被分隔的内容,撞上了。你大概见过同类的:Excel 单元格里内容自带逗号会冲乱列;写程序时字符串用引号括起来、引号里又有个没处理好的引号,后半句就飞了。解法也都是现成的:给内容里那些尖括号字符加一道转义(让系统看得出"这是文字、不是标签"),或者换一套内容里几乎不会出现的定界符。这是协议层的事,Anthropic 自己能修,我在外头够不着。

为什么偏偏这摊活高发

所以不是这模型笨。是我那两天派给它的活,内容里天生全是这种尖括号标签字符。

那个面板是改前端,满屏都是 HTML 尖括号标签;我让它读的记录,整篇都是 invokeparameter 的调用日志;它给我讲解,还得把调用长什么样原样引出来给我看。内容里这种尖括号字符越多,系统拆的时候撞上的机会就越大。我那两天派的活,正好全踩在这种字符上。

最绝的:我查它的时候,它当着我面又犯了

这段是真事,我边查边乐。

就在我让它帮我查这个病的那个下午,它自己又犯了好几次。最逗的一次:它正给我写解释,想把那种定界标签(比如 </parameter>)原样打出来给我看一眼,刚打到一半——它这条话,当场就塌了。它在给我演示这个病的时候,被这个病咬了一口。

后来它学乖了:凡是要提到那种标签,就拆开写、绕着写,不再整个打出来。这么一改,那个下午剩下的活,再没塌过。等于当我面比了一回:整个打出来,塌;拆开绕着写,不塌。一清二楚。

记几条

  • 这病不在它"脑子"里。它在它跟系统之间那套格式约定里——invokeparameter 这些定界标签和内容共用同一串文本,没有转义。这一层我够不着,得 Anthropic 自己去改那套格式(给内容里的尖括号字符留个转义的口子,或者换一套不容易和内容撞的定界符)。我关机两天、它换没换模型,都不治这个。
  • 但它能少犯。只要派活的时候、它说话的时候,别让那种标签字符赤裸裸混进内容里,撞上的就少一大半。这点它自己能管——打这次起,它改了打法。
  • 它不危险。这种崩,崩的是"还没说出口的那句话"——活压根没发出去,碰不到我的东西。就是烦人,又毁不了啥,顶多让我多按几次中断键。

还有一层,我后来才回过味来

这次它"塌",是结构标签被内容里的字符撞乱、那次调用作废了——崩在"还没发出去"这一侧,所以伤不到人。可同一类问题要是反过来呢?内容里藏着的一段文字,被系统当成了真指令收下、照着执行了——那就不是塌掉,是被人塞了张纸条,它照着念了。

这事也有名字,叫提示注入(prompt injection),是 AI 安全里专门要防的一类。我这次撞见的,是这毛病温和的那一面;它还有不温和的那一面。那是另一篇的事了。

上一篇那个"疯",吓人,但少见。这个"噎",不吓人,可天天能撞上——因为我派的活,十有八九,内容里就带着那种字。

那两天我跟它较的不是劲。是看它每张一回嘴,就被自己嘴里那个字绊一跤,一遍,又一遍。

这是一个系列: ①《和 Fable 5 的 35 小时》· ②《我昨天还在夸的模型,今天没了》· ③《换了个模型,它当场就疯了》· ④《它不是疯了,是没人能让它停下来》(③的根因前传)· ⑤ 你正在看的这篇——又一次抽风,这回扒到了协议那一层。

把这篇记录接到下一步

读完以后,可以继续追问这篇文章,也可以回到策展目录,或通过标签追同一条线索。

追问这篇 回到目录 浏览标签