一个不可见字符让我们的护栏失效了三周,而症状看起来完全像是模型不稳定

Reddit r/AI_Agents 新闻

摘要

一位开发者讲述了一个持续三周的生产环境 bug:一个包含字面退格字符的正则表达式静默地禁用了语言检测护栏,让 LLM 看起来不稳定。这篇文章强调了需要对确定性护栏进行埋点,以将其与模型的非确定性区分开来。

我们在生产环境运行着一个语音和聊天代理,用于处理真实预订。它必须根据客户使用斯洛伐克语或英语回答。大约三周时间里,我们遇到了一个我认为值得描述的 bug,因为症状直接指向模型,而原因完全在我们自己。报告是:“当客户用英语书写时,代理有时会滑回斯洛伐克语。”典型的 LLM 非确定性,至少看起来是这样。我们在服务端运行着一个语言检测函数,当检测到英语时,它会向系统提示中注入两次硬性指令,一次作为最终的覆盖指令,另一次作为紧接在问题之前的独立系统消息。双保险。但它仍然泄漏出斯洛伐克语。于是我们花了大量时间在模型侧。重新排序指令、加强措辞、将其移到更靠后的位置、给它单独的轮次。都是些微小的改动,没有真正修复问题。 真正的原因:检测器内部的正则表达式包含一个字面退格字符(0x08),位于本应是词边界的位置。引入该检测器的补丁是由 Python 脚本应用的。正则表达式位于 Python 字符串内部,Python 将反斜杠-b 解释为其退格转义,并将原始控制字节写入了 JavaScript 文件。该文件在编辑器和代码审查中看起来完全正常,因为那个字节根本不会显示出来。正则表达式编译时没有任何报错。它只是永远不会匹配,因此英语分支从未执行,指令也从未被注入。每一次“滑回斯洛伐克语”时,它从未被告诉过不要这样做。 我得到了两点教训。第一,当一个护栏“大部分时间”有效时,在调整提示词之前,先验证它是否真的在执行。我们对最终输出有日志,但没有记录检测分支是否触发。一行“指令已注入:true/false”的日志,就能在第一天发现这个问题,而不是第三周。这就是陷阱:一个表现非确定性的模型和一个从未触发的确定性检查,从外部看是无法区分的。两者看起来都是“通常没问题,偶尔出错”。 第二,不要让一种语言的转义规则去编写另一种语言的源代码。如果你用脚本修补或生成代码,请验证生成的字节,而不是它们在界面上的显示效果。对修改过的行运行 od -c 就能立刻发现问题。我们重写了那个检测器,完全避免了转义序列,现在它只是一个简单的停用词集合,按非字母字符分割,部分是为了可读性,部分是因为它不会再以同样的方式被静默破坏。 更广泛的观点,也是我为什么在这个论坛里反复提及的原因:很多被归咎于模型非确定性的问题,实际上是确定性代码悄悄没有运行。提示词级别的护栏和服务端级别的护栏以完全不同的方式失效。提示词规则会明显且随机地失败。服务端规则会悄然且彻底地失败,除非你对决策本身进行埋点,否则你不会注意到。我很好奇是否有其他人曾被一个实际上从未运行的护栏坑过,以及你们是如何记录它的。我们现在记录每一次注入决策,这很有效但很快就会变得噪音很大,我还没有找到一个好的折中方案。
查看原文

相似文章

整数无故发生神秘变化,而本应无代码生成影响

The Old New Thing (Raymond Chen)

一位开发者发现,交换两个等效宏竟导致无关函数中出现意外的整数变化,这篇博客文章深入探究了这一谜团,并对某个大语言模型(LLM)关于控制流保护的解释提出了质疑。