跳到主要内容

拆一篇不是论文的“论文”:追踪数据形状才是幻觉检测器的承重墙

破壁人
破壁人

· 阅读约 10 分钟

这件事的起点不是一篇论文,而是一个开发者把项目复盘说得比大多数论文摘要还诚实。agent-exec-trace,一个给 AI 代理做 OpenTelemetry 风格追踪的可观测性层,作者 Debashish Ghosal 最初的判断是:难点在检测器逻辑——循环、重试风暴、成本激增、幻觉,这些异常要靠规则检测器去抓。这个判断错得有多离谱,看他自己的测试结果就知道:10 万条真实代理追踪喂进去,一个叫 empty_response 的检测器在 100% 的追踪上触发,35 个规则检测器里 28 个从来没触发过。

我第一次读到这个数字的时候也卡了一下。28/35 的检测器全程沉默,这不是检测器写错了,是它们根本“看不见”——真实追踪里响应的字段位置和格式高度不一致,检测器假设的响应形状,和真实语料里实际存在的响应形状,对不上。检测器在找一个它以为在那里的字段,结果那个字段藏在别的地方、换了个名字、或者干脆不在一楼。这一步是全部。没有它,后面所有关于“为什么数据形状才是承重墙”的讨论都没有起点。

这就是我想拆的那堵墙。agent-exec-trace 这篇“复盘”最诚实的地方在于它说了一件事:项目真正的承重墙不是检测器逻辑,是追踪数据的形状。检测器是墙纸,数据形状是地基。你的检测器能不能触发、触发得有没有意义,不取决于你的规则写得有多细,取决于你喂给它的追踪数据——它们从真实代理里出来的时候,长成什么形状。

而且这个“形状”问题,作者最初是看不见的。他最初采用仅元数据的安全默认设置,不捕获原始工具参数和响应。这听起来是个合理的隐私保守设计,但它直接让幻觉检测器瞎了。幻觉检测器要做的事情是什么?是把“代理说了什么”和“工具实际返回了什么”放在一起比,判断代理输出和工具证据是否一致。你不捕获工具响应,就等于把右边那半证据删了。检测器只能看见代理的输出,看不见它声称依赖的证据,那幻觉检测当然没法有效判断。这个 bug 不是写错了某个判断条件,是数据采集的默认设置从根上切断了检测器需要的输入。作者后来允许捕获截断内容之后,幻觉检测器的误报率大幅下降。这再一次说明:问题从来不在检测器公式里,在数据长什么样。

把这个直觉先砸进去,我们再看那组最重要的数字。作者做了四轮数据规范化处理,涉及响应键名、工具命名约定、操作名称、时间戳解析、父子关系不一致。做完之后,真实语料的“结构兼容性”只有 42.4%。这个数字比它看起来更狠——它意味着超过一半的真实追踪,即便经过了四轮规范化,还是不能按检测器假设的形状被解析出来。而且这个数字是作者自己披露的,此前那个“看着还行”的数字(他没有明确写出来是多少)其实是个假象,因为之前的数据没有被认真按检测器需要的形状去校验。42.4%,这是这个项目的真实地面。评论区那条 Juan Gonzalez 给的评论说得最到位:empty_response 100% 触发和 28 个检测器 0% 触发,本质上是同一类测量问题——两者都没有提供关于代理运行本身的任何信息。他还建议把这个 42.4% 写进 README,让所有后来接这个项目的工具都必须正面回应这个数字。这个建议我完全赞同。把 42.4% 藏起来,或者放在复盘里一笔带过,就是在给后来者留一堵墙。

我画一张表,把“检测器假设的形状”和“真实语料的形状”放在一起看清楚:

检测器想要什么真实追踪里有什么
响应在固定的 response 字段里响应可能叫 message、可能在 output 里、可能在嵌套的 dict 第三层
工具调用统一走 tool_name有的是 tool,有的是 name,有的是 func 包着一层
父子关系清晰:一个 span 指向一个 parent真实代理的 span 树有的是孤儿节点,有的 parent_id 指向不存在的 span
时间戳是 ISO 8601 格式有的带毫秒,有的不带,有的被包在 created_at 里,有的直接缺了

这张图要说明的是:真实代理追踪不是一个规整的数据库表,它是一个半有机的、被不同框架以不同习惯吐出来的产物。检测器写代码的时候,天然会假设输入规整——这是人的心智模型,不是现实。而这个假设一旦放在真实语料里,就撞碎了。

那作者后来怎么办的?他换了一个策略:构建合成追踪生成器,生成 100 万条追踪,包含 10 个模拟代理、14 种工具,以及循环、重试、超时、成本激增这些刻意行为模式。这个合成语料把结构兼容性拉到了 99.2%,35 个检测器里终于有 20 个能触发了。这个提升看起来像是一个成功的故事——数据形状修好了,检测器也活了。但这里出现了整篇复盘里最值得停下来读的一段,因为作者自己说,合成数据也暴露出新的问题。

幻觉检测器在合成数据上以 98% 的触发率运行。98% 是什么意思?它几乎在每一条合成追踪上都在报警。这不是因为检测器强,是因为合成输出和合成工具证据之间存在人为设计出来的过度关联,检测器只需要做一个简单的匹配就能判断,结果把“判断”这回事儿的门槛降到了地板。合成环境里的证据是刻意生成的,所以“代理说了什么”和“工具返回了什么”之间的对应关系太干净、太容易判断,真实环境里那种证据模糊、部分相关、甚至相互冲突的情况根本没有模拟到。至于成本激增检测器,它几乎不触发,因为合成数据的成本大多是美分级别——一美元都跨不过去,还激增什么?这两个失败放在一起,指向一个更根本的问题:合成数据解决了“字段可见性”的问题,但没有解决“校准”的问题。检测器在合成数据上能触发了,但触发得对不对、触发得有没有现实意义,完全不知道。这一步是这个项目真正的思想转折,也是我觉得最有价值的地方——作者没有因为“从 0 触发到 20 个触发”就宣布胜利,而是自己拆穿了自己的合成数据。

把这三件事串起来,就自然得到了那个三层验证方法,这也是这篇文章里最值得带走的心智模型。分三步看。

第一步,单元测试。验证单个检测器的逻辑:一个空响应进来,它该不该触发;一个超时进来,它该不该报警。这一步告诉你“代码写得对不对”。

第二步,合成追踪。验证检测器能否看见它需要的字段:给我一条结构兼容的合成追踪,检测器能不能从中取出那个 response 字段、解析出那个时间戳、找到那个父 span。这一步告诉你“检测器能不能拿到原料”。

第三步,真实追踪。验证检测器是否对现实有校准意义:10 万条真实追踪进来,有多少条能触发,触发之后你人肉去看,是真异常还是假报警。这一步告诉你“检测器在真实世界里到底有没有用”。

这三层里有任何一层缺失,都会得到一个骗人的结论。三层之间最容易被砍掉的是第三层,因为真实追踪很难搞——你要构造 10 万条真实语料,还要接受里面那一堆不规整的烂数据。但正是这一层决定了前面两层是否有意义。我见过太多项目卡在第二层就停了,合成跑通了就宣判结束,因为那看起来像一个“可以发布的数字”。作者把这个坑自己踩了一遍,然后说得特别清楚:下次要尽早做现场测试,追踪语料要当成设计工件,而不是单纯的测试输入。

还有一个几乎可以当独立教材的经历,是关于绿色门禁的。作者曾把一个 OTLP 导出里程碑标记为完成,后来发现这个端到端导出路径从来没有被真正验证——Docker Compose 配置没暴露 gRPC 端口,SDK 路径被配置成导出到本地追踪而非 OTLP,两个缺陷互相掩盖,让 CI 的绿灯一直亮着。两个错误各自都足够让“导出”失败,但它们凑在一起,反而让“导出”这个动作看起来是成功的,因为系统把追踪导出到了本地,而本地恰好也在工作。绿色门禁亮着,不代表门禁背后那条路是通的。这两重缺陷互相掩盖的结果,比单一缺陷更危险,因为它让你失去验证的动机——绿着呢,查什么?

作者对这件事的总结是:只有当真实代理发出追踪、Jaeger 接收、分析系统摄取、API 提供结果时,门禁才算完整。我把这句话再推进一点:凡是“结构上看起来完整但没有真实流量跑过”的验证,本质上都是毛玻璃,只是有些玻璃后面还画了一层假合格。这个判断不止适用于这个项目——任何可观测性系统,如果你的导出路径没有用真实代理的流量端到端跑通过,你就不知道你的“绿色门禁”是在验证啥。这一步我承认自己在这类问题上也犯过懒,CI 绿灯亮着就倾向于不查。但这份复盘让我重新想起一个老原则:绿灯只能说明代码没把测试冲掉,不能说明系统真的在工作。

把这篇复盘拉回地面。对一线开发者来说,这件事的工程价值不在 agent-exec-trace 这个项目本身——它还是新的,作者自己都说问题尚未完全解决,28 个检测器没在真实语料上有意义触发、LLM 检测器在生产负载上的表现、span 树物化这些都没被验证——真正的价值在于它验证了一个普通但总被忽略的判断:在异常检测系统里,数据形状是承重墙,检测逻辑是承重墙上的墙纸。你的规则检测器大概率没你想象的那么脆弱,真正的断裂点在数据采集阶段就埋下了。

Brian Jin 在评论区还有一个更进一步的建议:证据存在不等于证据充分,应该在追踪模型里显式表示代理依赖的“主张—证据”关系。作者回应说这个方向适合作为一等 span 注解。这个建议是往“校准”这一层继续深入的思路——不是“有没有证据”,而是“证据够不够、证据和主张之间是什么关系”。但这是一个尚未被实现的方向,需要把它当成一个开放问题,不是既定结论。这里更新一下我的判断:agent-exec-trace 的真实核心 idea 不是“检测代理异常”,而是“把追踪数据当成要被设计的东西,而不是被消费的东西”。如果这个项目未来真的有资格被拆成一堵完整的墙,那承重墙一定在“追踪数据的形状规范与兼容性验证”这个方向上,不在那 35 个规则检测器的代码里。

这堵墙我替你拆了,路得你自己走。下一次你往生产系统里也部署了一套基于规则的检测逻辑、然后看着 28 个检测器沉默的时候,至少先问一句:它是不是根本看不见数据里那个字段——而不是第一反应去改检测规则。

破壁人
破壁人

把高冷论文拆成中文开发者能懂的:核心 idea、公式推导、示意图、工程联系。

查看主页 →