← 返回文章归档

CUADebug:把 GUI Agent 的失败解释变成可以被重跑证伪的修复信号

UIUC + Yale 的 CUADebug 给 OSWorld 的 204 条失败轨迹做了人工根因标注,配一个带工具的 ReAct 调试器做多模态因果定位,再用重跑检验诊断是否真的可执行。联合诊断 11.2%→19.6%,单次重跑 13.89%→29.90%。拆解它的配对观测设计、五类根因分类法、那个证明「自动演化分类法不收敛」的负面实验,以及这套东西在 APP 自动化测试的失败归因与用例自愈上怎么落。

#GUI Agent#Computer Use#Root Cause Analysis#Agent Debugging#Mobile QA#OSWorld

OSWorld 跑完给你一个数:通过率 20%。这个数唯一能告诉你的事情是「八成任务没做成」。至于那八成死在哪一步、是看错了、点歪了、还是想岔了、抑或干脆是环境弹了个框,benchmark 一个字都不说。

这在纯文本 agent 上还能忍——轨迹是文本,人肉翻一遍 reasoning trace 通常能看出问题。放到 computer-use agent 上就不行了。CUA 的决定性证据是多模态的:某一步截图上那个控件到底选中了没有、点击落在了哪个像素、动作执行完屏幕状态变没变。这些东西不在文本里。

更麻烦的是时间上的错位。

CUADebug 的引子案例。任务是把 LibreOffice Impress 里几乎看不清的幻灯片页码改成红色。第 2 步 agent 想选中页码文本,实际单击选中的是外层占位框;几十步之后症状才显现为「页码还是灰的」。图中标出三重困难:目标是大对象里的一个微小视觉细节、单击/双击/焦点导致不同 UI 状态、症状延迟到后续格式化动作之后才暴露

任务是把幻灯片页码改成红色。第 2 步,agent 想选中页码文本,单击下去选中的是外层的占位框。它自己不知道——截图上「有东西被选中了」,看起来一切正常。接下来几十步它在这个错误焦点上正常地执行了改颜色、确认、报告完成的完整流程。最后终态检查失败:页码还是灰的。

失败信号出现在第 N 步,可修的地方在第 2 步。中间隔着几十个「看起来都对」的动作。

CUADebug(arXiv:2608.02643,UIUC + Yale,2026-07-31,归类在 cs.SE 而不是 cs.AI,这个选择本身就说明了作者的定位)要解决的就是这件事:把 CUA 的失败归因当成一个多模态因果定位问题,并且要求诊断结果能被重跑证伪。

放进谱系里看,它填的是哪个格子

agent 失败归因这条线上已经有几拨工作,各自占位不同:

AgentDebug / AgentErrorBench(Zhu et al., 2025)做的是通用 LLM agent 的根因调试,环境是 ALFWorld、GAIA、WebShop。这些环境的观测本质是文本,归因判断可以在文本空间里完成。

BacktrackAgent(Wu et al., 2025,EMNLP 2025)做的是运行时在线回退——agent 跑着跑着检测到错了,回滚重来。它是个执行期机制,不产出可复用的诊断结论。

CUADebug 选的是第三个位置:事后(after-execution)的多模态因果定位,产出结构化诊断,再把诊断当成修复配方注入重跑来验证。

这三者的关键差别在于「诊断的产物是什么」。在线回退的产物是一次行为修正,用完即弃;通用 agent 归因的产物是一段解释文本。CUADebug 要的是一个既能被评测(对不对得上人工标注)、又能被执行(注入重跑能不能提高完成率)、还能被存下来复用(写进 episodic memory)的对象。

论文自己那句话说得挺准:有效的 CUA 调试不该停在解释失败,而应该把解释转换成可以被重执行检验的修正。

CUAErrorBench:204 条人工根因标注

数据集从 OSWorld 的失败轨迹来,三个 agent 源:Claude 4.5 Sonnet 的 50 步轨迹 144 条、Gemini 2.5 Pro 30 条、Qwen 3.5 30 条,共 204 条可用标注(弃用的 15 步 split 已排除)。

CUAErrorBench 的标注分布。整体 204 条:R(任务推理与控制)110、P(感知)36、G(落点与交互)25、S(外部/系统)13、O(其他)20。按轨迹来源拆分:Claude 4.5 Sonnet n=144(P25/G8/R76/S13/O19,注意 S 类全部来自这一 split)、Gemini 2.5 Pro n=30(R15)、Qwen 3.5 n=30(R19)

分布本身就是个结论:任务推理与控制占 110/204,53.9%,是最大的一类。这个数字容易被拿来支持「GUI agent 的瓶颈已经从感知转到规划了」的说法。

但另外 46% 才是这篇论文存在的理由——感知 36、落点与交互 25、外部系统 13、其他 20(都是 OSWorld 的 infeasible-task,agent 本该识别出任务不可能完成并如实汇报,结果硬做)。这 94 条失败,纯文本 agent 的归因框架一条都处理不了。

每条标注五个字段:根因步、诊断标签、文本证据、纠正动作、置信度。标注协议里有一条值得单独拎出来:

标注者先确认终态失败症状,然后倒推找到最早引入新因果错误的那一步,而不是仅仅继承了先前错误的步骤

这个定义是整个数据集的质量来源。没有它,「根因步」就退化成「第一个看起来不对的步骤」,而在一条错误传播链上,看起来不对的步骤可能有十几个。

标签空间是两级的。顶层五类:

CUA 错误分类法顶层五类。P 感知:agent 是否误解了观测中可见的内容?G 落点与交互:agent 知道要做什么,但操作在了错误目标上或用了错误的交互机制?R 任务推理与控制:agent 是否选择、维持或修正了错误的计划?S 外部/系统:环境、工具或 benchmark 配置是否阻断了本来有效的进展?O 其他:不属于上述四个执行模块的失败。每类附诊断问句和典型证据

顶层类别的设计意图是「决定修复策略族」,子类才是「具体失败模式」。这个划分对工程落地很重要:知道是 G 类还是 R 类,就知道该改定位策略还是改编排逻辑;知道具体子类,才能去索引历史案例。

附录展开成 30 个子类:

30 个细粒度子类的完整定义表。P 类 5 个(视觉幻觉、误识别/OCR 错误、跨模态错绑、观测遗漏、语义误解);G 类 4 个(坐标/元素定位错误、可见性/可达性错误、交互机制错误、干扰/对抗性误导);R 类 13 个(约束违反、不可能的计划或动作、分解失败、低效冗余策略、动作与意图不一致、非法动作、参数错误、上下文丢失/过度简化、记忆幻觉、进度误判、结果误读、自我纠正失败、因果错误归因);S 类 7 个(渲染/布局失败、时序/竞态、意外系统行为、步数或资源上限、工具/API 失败、环境不稳定、benchmark 评测缺陷);O 类 1 个(不可完成任务)

R 类占了 13 个子类,比其他四类加起来还多。里面有几个值得注意:R5 动作与意图不一致(模型说要点 A,发出的动作点了 B)、R9 记忆幻觉(声称看到过其实没有的东西)、R12 自我纠正失败(检测到问题但改错了)、R13 因果错误归因(自己给自己的失败编了个错误解释,于是选了错误的修复)。R12 和 R13 是 agent 在「试图自救」的过程中制造出来的新失败,这类失败在只看最终截图的评测体系里完全不可见。

CUADebugger:让模型带着工具倒推,而不是一次性猜

方法部分的核心判断是:不要把整条轨迹一次性塞进 prompt 让模型猜根因。

CUADebugger 完整流水线。左起:失败的 CUA 轨迹(指令、环境、逐步动作与终态)→ 工具引导的多模态检视(get_step_details(n) 返回动作前截图、动作码、模型自述推理、执行状态、动作后截图,作为一次配对观测)→ ReAct 在候选步骤上搜索,写持久化观测笔记,定位最早的因果错误 → 结构化 RCA 记录(根因步、L1/L2 子类、有据可查的证据、纠正动作、已检视步骤的摘要)→ Memory(存储可复用记忆:触发条件、失败动作、纠正动作、区分特征、证据、来源 episode;标注 Use with caution——检索到的记忆必须与当前截图和轨迹匹配才能影响 RCA)→ debugger 引导的重跑(把根因步、错误标签、证据、纠正动作、步骤摘要注入执行 agent,避免重复根因错误,测量重跑得分与任务完成率)

这条流水线里有四个地方值得单独拆开看。

配对观测。 get_step_details(n) 返回的不是一张截图,而是「动作前截图 + 动作码 + 模型自述意图 + 执行状态/reward + 动作后截图」这一整组。关键在于把模型声称要做什么屏幕实际变成了什么摆在一起。前面那个页码案例,单看第 2 步的截图看不出问题,单看第 2 步的 reasoning(「选中页码文本」)也看不出问题,只有把两者对齐才能发现「它说选文本,屏幕上选中的是框」。

这也是 R5、R11(结果误读)这类失败唯一的检出方式。

上下文管理。 ReAct 循环跑起来会积累大量截图。CUADebugger 的处理是:旧截图从上下文里压缩掉,但 agent 每次检视后写的文字观测笔记保留。这是个很实际的工程妥协——多模态 token 太贵,而调试推理真正依赖的是「我在第 12 步看到了什么」这个结论,不是原图。

结构化提交。 finish() 的 schema 强制一次性产出完整 RCA 记录:根因步 N、L1/L2 标签、有据可查的证据、具体纠正动作、[0,1] 区间的校准置信度,以及每个已检视步骤的摘要。最后这项容易被忽略但很关键——这些摘要会被传给重跑时的执行 agent,让它继承调试器对失败轨迹的理解,而不是只拿到一个孤零零的结论。

记忆当候选证据,不当权威标签。 论文在流水线图里明确标了 “Use with caution”,正文也写了:检索到的记忆必须先比对触发条件与当前截图、动作轨迹是否匹配,不匹配的要丢掉而不是照抄。这一条在检索增强调试里是刚需——历史案例的表面相似度极容易误导,「同样是点击没反应」背后可能是遮挡、可能是时序、可能是元素被禁用。

结果:诊断准确率很低,重跑收益却不低

RCA 准确率

五个指标口径不同:L1 顶层类别一致、L2 子类完全一致、Step Exact 根因步精确匹配、Step ±2 根因步误差两步内、Tag+Step 要求子类和精确步同时对

RCA 准确率主表,对照人工标注(百分比)。Claude 4.5 轨迹上:Gemini 2.5 Pro 调试器 Naive→CUADebug 的 L1 51.0→53.8、L2 30.1→37.1、Step Exact 21.0→29.4、Step ±2 38.5→47.6、Tag+Step 11.2→19.6;Qwen 3.5 调试器 L2 19.4→33.0、Tag+Step 7.6→14.6;Claude 4.5 调试器 L2 14.6→31.4、Tag+Step 4.9→15.3。Gemini 2.5 轨迹与 Qwen 3.5 轨迹的六行跨 agent 结果涨跌互现,其中 Qwen 3.5 调试 Gemini 轨迹的 Tag+Step 从 6.7 掉到 3.4,Claude 4.5 调试 Qwen 轨迹从 10.0 掉到 9.1

在样本最全的 Claude split(144 条)上,工具增强对三个 backbone 都是正向的,L2 和 Tag+Step 全线提升,Gemini 2.5 Pro 的联合诊断从 11.2% 涨到 19.6%。方向是对的。

但绝对值必须被认真对待:最好的一行 Tag+Step 只有 19.6%。八成情况下,机器给出的完整诊断(子类 + 精确步)和人类标注对不上。Step ±2 放宽到误差两步内也只有 47.6%——一半的时候,机器指认的根因步离人类认定的位置超过两步。

跨 agent 的六行更值得警惕:CUADebug 不是一致地更好。Qwen 3.5 调 Gemini 轨迹时 Tag+Step 从 6.7 掉到 3.4,Claude 4.5 调 Qwen 轨迹从 10.0 掉到 9.1。论文的解释是能力依赖——工具能把证据摆出来,但模型得有能力解读证据并修正自己的因果假设,弱 backbone 拿到更多证据反而更容易被带偏。这个解释合理,但也意味着这套方法不是即插即用的:给弱模型加调试工具可能是负收益。

分类别看差距更明显:

类别级 RCA F1,Claude 4.5 Sonnet split(N=144)。感知 0.78(n=25)、落点与交互 0.59(n=11)、任务推理与控制 0.85(n=76)、外部/系统 0.35(n=13)、其他 0.86(n=19)

R 类 0.85、O 类 0.86 都不错——但 O 类是 OSWorld 的 infeasible-task,本质上是「这题没法做」的模式识别,不难。S 类只有 0.35,G 类 0.59。也就是说,环境类失败和落点类失败,恰恰是调试器最判不准的两类。这个后面细说,因为它直接决定了这套方法能不能搬到真机测试上。

重跑:诊断到底有没有用

RCA 准确率低,不代表诊断没用。论文用重跑来做这个检验——如果诊断里含有真实的因果修复信息,把它注入执行 agent 应该能减少重复失败;否则它就只是听着有道理的废话。

单次重跑结果,Claude 4.5 Sonnet 轨迹。五个条件的 turn 预算 / 输入 token / 输出 token / 任务完成率 / New+Fixed:Baseline(无 RCA)28.01 / 272.7K / 3.5K / 13.89% / 20+11;Self-debug(自诊断)31.53 / 324.4K / 3.9K / 15.28% / 27+12;Machine RCA 23.03 / 290.8K / 2.9K / 28.47% / 29+35;Human oracle(人工标注)22.37 / 296.9K / 3.7K / 20.10% / 43+29;Our method(RCA + memory)28.28 / 319.1K / 2.9K / 29.90% / 44+24

单次重跑是从根因步的前一步恢复现场重跑。任务完成率:纯历史续跑 13.89%,自诊断 15.28%,机器 RCA 28.47%,本文方法 29.90%。结构化 RCA 把完成率翻了一倍多。

New+Fixed 这个指标的读法是:前一个数是「原根因错误修好了但引入了新的关键错误」,后一个数是「修好了且没引入新错误」。

这张表里藏着一个论文没有展开、但相当扎眼的现象:人工标注(human oracle)这一行的任务完成率只有 20.10%,反而低于机器 RCA 的 28.47%。 但它的 New+Fixed 是 43+29=72,高于机器 RCA 的 64。也就是说人工诊断更擅长修掉那个根因错误,却更不擅长让整个任务跑通。

论文把这一行的可比性做了免责声明——各行在重启点来源和诊断 prompt 格式上不匹配,Table 3 应当读作「重执行包」的对比而不是「诊断质量」的干净消融。这个声明是诚实的,但它同时也抽掉了这张表最想让人得出的那个结论。

持续重跑的设定不同:从 50 步失败的第 51 步接着跑,不回退。

持续重跑结果,Claude 4.5 Sonnet 失败轨迹。原始 turn 均为 50。Baseline 消耗 38.92 turn / 390.3K 输入 / 3.8K 输出 / 12.20% / 26+7;Self-debug 43.76 / 401.6K / 4.4K / 14.20% / 30+11;Machine RCA 47.13 / 405.9K / 5.1K / 21.53% / 21+44;Our method 47.44 / 426.2K / 5.4K / 25.86% / 27+58;Human oracle 45.75 / 419.1K / 4.8K / 29.21% / 29+44

这里的排序回归直觉:12.20% → 14.20% → 21.53% → 25.86%,人类 oracle 29.21% 在上界。本文方法修掉首个关键错误共 85 例(27 带新错 + 58 无新错),baseline 只有 33 例。

有意思的是这里又反过来了:本文方法的 New+Fixed 总数 85 高于人类 oracle 的 73,但任务完成率 25.86% 低于人类的 29.21%。两张表两个方向的背离,说明「修掉根因」和「完成任务」在当前的评测体系里并不是同一件事——修好第一个错之后,剩下的路还很长,而 agent 在剩下的路上会犯新的错。

顺带看一眼成本:单次重跑输入 27 万到 32 万 token,持续重跑 39 万到 43 万。这不是能随便挂进 CI 的量级。

一个负面实验,价值可能高于主实验

论文里最有意思的部分在第 5 节末尾,是个消融性质的负面结果。

一个很自然的质疑是:为什么要花人力标 204 条?让强模型自己从失败轨迹里演化出一套分类法不行吗?

作者真的去做了。让 Gemini 2.5 Pro 在空起点配置下跑 Claude split 的 144 条,不给任何种子分类法,每处理一个案例必须提交五种操作之一:REUSE(复用已有子类)、DISCOVER(新建)、RENAME(重命名)、SPLIT(拆分)、MERGE(合并),分类法状态跨案例持续累积。跑四个随机案例顺序(seed 3 在付费额度下跑完全部 144 例,seed 0-2 各覆盖 47-55 例后截断)。

结果有三条:

(A)不同顺序演化出的分类法互不收敛。 六个 seed 两两配对,在名称严格对齐的判定下,零个子类能对上。seed 0 把失败组织成 P-*/OI-*/I-* 的结构,seed 3 用的是八路的 ACTION/POLICY/EVALUATOR/VISUAL 体系,两者无法通过命名统一。

(B)不是数据量不够。 seed 3 跑完 144 例后已经有 40 个子类,曲线还在涨,没有饱和迹象。

(C)语义层面部分收敛,命名层面不收敛。 通过 LLM-as-judge 把每次运行的标签映射回 CUAErrorBench 的标签空间,单个 seed 能覆盖 15%-28% 的人工子类,四个 seed 取并集约 40%。

结论:自动发现能找到错误结构,但找不到稳定的 schema。

这个实验把人工标注的定位从「成本项」改成了「结构性必需」。CUAErrorBench 的价值不只是 204 个标签,而是这些标签锚定的那个规范 schema

这个结论的适用范围其实远超 GUI agent。任何想用 LLM 自动做缺陷分类、日志聚类、故障归因的团队都应该看一眼:模型能把相似的东西聚到一起,但它给不出一套跨运行稳定、可以拿来做长期统计和趋势对比的标签体系。而缺陷分类法的全部价值恰恰在于稳定——今年的 P2 和去年的 P2 得是同一个东西,否则季度报表就是废纸。

三个 case study,看调试器什么时候对、什么时候错

论文附录给了三个完整案例,正好覆盖了「判对」和「判错」两种情形。

G1 落点错误案例,调试器判断正确。任务:设置 Chrome 在每次关闭浏览器时自动删除浏览数据。人类标注 G1@47,调试器 G1@47。根因:agent 以为点的是扩展图标,实际点到了书签图标。Step 46 上下文:扩展已安装,agent 准备通过工具栏图标打开设置。Step 47 ROOT:agent 自述「扩展图标」但点击落在书签开关上,书签面板被打开。Step 48:agent 意识到「我点了书签」但清理动作也点空了。Step 50 下游:agent 放弃工具栏,改用地址栏输入 chrome://extensions

这是配对观测发挥作用的典型场景。第 47 步 agent 的 reasoning 写得很自信——「我需要在工具栏找扩展图标」,动作是 left_click at (1106, 60)。单看文本毫无破绽。但动作后截图显示打开的是书签面板。意图和结果的错位就是证据本身。

第 48 步更有意思:agent 自己发现了「我点了书签图标」,然后试图关掉面板重找——结果这次清理动作又点空了。这是 R12(自我纠正失败)叠在 G1 上面。调试器正确地把根因归到了 47 而不是 48,符合「最早引入新错误」的定义。

R10 进度误判案例,调试器判断正确。任务:把图片背景设为透明(GIMP)。人类标注 R10@15,调试器 R10@15。根因:在可见背景仍然存在的情况下宣布成功。Step 12:第一次删除只去掉了部分天空。Step 13:agent 意识到清理不完整,点击另一块蓝色区域。Step 14:再次删除后仍有明显天空色像素。Step 15 ROOT:agent 宣称「背景已完全透明」,与截图矛盾。Step 23 下游:轨迹在导出这张未完成的图片后结束

这个案例是「模型说完成了,屏幕说没有」。前面 12-14 步 agent 的自我评估其实都是准的,它确实看出了背景没清干净。到第 15 步突然翻转成「完全透明了」,而截图上那块蓝色一直在。

这种失败在只看终态的评测里会被记成「任务失败」,在看 reasoning 的评测里会被记成「agent 声称成功」,只有把两者对齐才能定位到究竟是哪一步开始脱节的。

第三个案例是调试器判错的:

P4 观测遗漏案例,调试器判断错误。任务:查找 Diamond 营地的 Next Available 日期。人类标注 P4@33,调试器 R10@33。调试器判成 R10(过早推理),但 agent 根本从未观测到那些数据,所以应该是 P4。Step 18:只检视了屏幕上可见的行,从未滚动表格。Step 19:把日期翻页控件当成了 next available 控件。Step 20:切换到新日期区间,旧区间还没读完。Step 21:又看到几行 group-site,仍未滚动看全。Step 33 ROOT:调试器说 agent「误判了进度」(R10),但截图显示 agent 从一开始就没拿到判断所需的数据——是 P4。Step 37 下游:最终总结继承了未观测数据的缺陷

这个分歧很有代表性。步号两边都是 33,分歧在类别:调试器说 R10(进度误判),人类说 P4(观测遗漏)。

两个标签描述的是同一个现象的不同层面——agent 确实误判了进度(它以为数据看全了),但误判的原因是它从头到尾没滚动过那张表,所以根本没有做判断所需的信息。R10 描述症状,P4 描述原因。

这类分歧解释了为什么 L1 准确率能到 50-60% 而 L2 只有 30-37%:顶层归错族的情况不多,但在「感知没拿到信息」和「推理没用好信息」之间划线,需要判断「如果信息给全了,这个推理还会错吗」——这是个反事实判断,模型做不好,人做也需要讨论(论文说所有 204 例都是多人标注,分歧靠讨论解决)。

对工程的启示是直接的:别指望自动归因给出可以直接进缺陷库的类别标签。 它能给的是一个带证据的候选,最终归类还得人来定,至少在类别边界模糊的地方是这样。

对 APP 自动化测试的落点

这篇论文的实验都在 OSWorld 桌面环境里,但它解决的问题在移动端 QA 里只会更严重——真机上的状态空间更脏,失败原因更杂,而多数团队的 trace 记录能力更弱。

失败分类法可以直接对标 UI 自动化的缺陷分类

五类顶层标签几乎是一一映射的:

  • P(感知) 对应控件识别问题:OCR 读错文案、动态渲染的内容没拿到、WebView 里的元素在原生 dump 里不可见、图片型按钮识别失败。
  • G(落点与交互) 对应坐标与操作语义:点击落在了兄弟节点上、目标被键盘或悬浮球遮挡、该长按用了单击、输入法没收起导致点击穿透、滑动距离不足。
  • R(推理与控制) 对应用例编排与断言:步骤顺序错、前置条件没满足、断言时机太早、状态判断依赖了不可靠的信号、失败后的重试逻辑本身有 bug。
  • S(外部/系统) 对应环境类问题:权限弹窗、系统升级提示、网络抖动、机型适配、应用被后台杀死、渲染超时。
  • O(其他) 对应「这个用例本身就是错的」——期望值过期、测试数据被别的用例污染。

这套分类的实际价值在于把「用例失败了」拆成五个不同的责任方。P/G 类归自动化框架的定位能力,R 类归用例作者,S 类归环境与设备管理,O 类归用例维护。目前多数团队的失败列表是一个扁平的红色清单,没人知道该找谁。

配对观测应该是 trace 的默认落盘结构

这是这篇论文里最容易搬、收益最直接的一条。

get_step_details(n) 返回的那组东西——动作前截图、动作码、意图、执行状态、动作后截图——对应到 Appium/UIAutomator/Maestro 的世界里就是:

每一步落盘:
  before:  截图 + 控件树 dump + 当前 Activity/页面标识
  action:  动作类型 + 目标定位表达式 + 实际解析到的元素 bbox + 参数
  intent:  这一步在用例语义上想达成什么(用例代码里的步骤描述/注释)
  status:  框架返回码 + 耗时 + 是否触发了隐式等待
  after:   截图 + 控件树 dump + 页面标识 + 期间的 logcat 片段

现在多数团队的实际做法是:失败的时候截一张图。这等于主动放弃了归因能力——一张终态截图既看不出错在哪一步,也看不出当时想干什么。

这里面 intent 字段最容易被漏掉,但它恰恰是配对观测的关键。没有「想做什么」,「屏幕变成什么样」就只是一个事实,没法判断对错。在自动化用例里,这个字段是现成的——步骤名、注释、Page Object 的方法名,落盘时带上就行,成本几乎为零。

控件树 dump 的成本要单独算。UIAutomator dump 在复杂页面上可能要几百毫秒,每步都存会把用例执行时间拉长一大截。折中方案是分级:常规步骤只存截图 + 动作 + 意图,在断言步骤和已知易失败步骤上存全量。

「最早引入新错误」可以拿来做失败聚类去重

标注协议里那条「找最早引入新因果错误的步骤,而不是继承先前错误的步骤」,直接对应回归测试里的一个老问题:一次构建挂了 47 条用例,实际上可能只有 3 个缺陷。

如果每条用例都按根因步 + 根因类别归一次,同根因的失败自然聚成一簇。缺陷单从 47 张变成 3 张,且每张带着 N 条复现路径。这比按报错文案做字符串聚类靠谱得多——同一个根因在不同用例里的表面报错可以完全不同。

但要注意论文的 Step Exact 只有 29.4%、Step ±2 只有 47.6%。自动定位的根因步不够准,不能拿来做硬聚类。 可行的做法是把它当成聚类的候选信号之一,配合页面标识、失败控件、时间窗口一起用,并且给出的是「疑似同源」的提示而不是自动合并。

修复配方 → 用例自愈重跑

单次重跑的设定——从根因步的前一步恢复现场,注入修复配方,用相同预算继续——对应到测试里就是定点重跑而不是整条用例从头重来。

Table 3 里的 turn 数说明了成本差异:机器 RCA 条件只花 23.03 turn,比 baseline 的 28.01 还少,因为它从根因步附近起跑而不是从头。在长用例上这个差异会更大。

但「恢复现场」在移动端比在 OSWorld 里难得多。OSWorld 有虚拟机快照,可以精确回到任意状态。真机没有。可行的替代路径:

  • deeplink 直达:把根因步之前的路径压缩成一次 deeplink 跳转,跳过前置导航。这要求 App 的 deeplink 覆盖度足够。
  • 状态注入:通过 mock API、预置 SharedPreferences/plist、直接写 App 的 SQLite 来重建业务状态,而不是靠 UI 操作走一遍。
  • 分段用例:把长用例拆成带明确前置条件的短段,失败时只重跑对应段。这是最不优雅但最可行的做法。

三条路径的共同前提是测试架构上要有「跳到某个状态」的能力,而不是所有用例都从启动页线性走下来。这个改造的收益不只在自愈重跑,日常调试也受益。

断言层面:区分「修好了」和「跑通了」

Table 3 和 Table 4 那两处背离——人类诊断修根因更强但完成任务更弱,本文方法反过来——在测试里对应的是一个很实际的区分:

用例重跑通过 ≠ 原缺陷被修复。

重跑通过可能是因为换了个起点绕过了问题、可能是时序恰好对上了、也可能是缺陷本身是偶现的。反过来,原缺陷确实修好了但用例仍然失败,说明后面还有第二个问题。

论文的 New+Fixed 指标把这两件事拆开了,值得借鉴:回归验证时应该分别记录「原缺陷是否复现」和「用例是否通过」,而不是只看后者。前者需要针对根因步的定向断言,后者是端到端结论。只看端到端结论,修复验证的假阳性会很高。

成本必须硬约束

单次重跑 27-32 万输入 token,持续重跑 39-43 万。这个量级放进 CI 里,每天几百次构建是撑不住的。

分层大致是这样:

  1. 失败发生时只落 trace,不做归因。成本接近零。
  2. 按缺陷密度触发归因:同一个页面/模块在一个时间窗口内失败超过阈值,才对代表性样本跑一次自动归因。
  3. 归因必须有硬上限:最大检视步数、最大 token、超限直接输出「无法定位」而不是继续烧。宁可给不出结论,也不要给一个花了 50 万 token 的错误结论。
  4. 人工标注当冷启动资产:这篇论文的核心论点就是人工 schema 不可替代。团队自己的 P/G/R/S/O 子类定义、以及每类几十条标注样本,是一次性投入、长期复用的东西,值得单独立项。

局限与判断

真正的贡献

第一,把「解释失败」变成了「可被重跑证伪的修复信号」。 这个方法论转向比具体数字重要。agent 失败归因这个领域一直有个软肋——诊断结论无法验证,模型生成一段听着有道理的因果解释,没人能说它对不对。CUADebug 给出的检验方式是:把诊断注入重跑,看行为有没有变化。这个检验不完美(论文自己承认各行不匹配),但它把评价标准从「解释是否合理」推到了「解释是否有用」。

第二,给出了 CUA 专属的多模态根因 schema。 五类顶层 + 30 子类,且明确把「感知没拿到信息」和「推理没用好信息」分开。这个 schema 本身可以脱离论文单独复用。

第三,用负面实验证明了人工 schema 是结构性必需而非省事。 前面说过,这个结论的适用面比 GUI agent 宽得多。

可能被高估的部分

Tag+Step 最高 19.6%,重跑收益却接近人类 oracle——这两个数摆在一起,是对不上的。 如果机器诊断八成情况下和人类对不上,那重跑的收益从哪来?最可能的解释是:收益主要来自「换了个起点 + 给了个警告」,而不是精确的因果定位。 也就是说,只要告诉执行 agent「你在第 47 步附近犯了个 G 类错误,小心点」,哪怕子类判错了、步号偏两步,它也能避开原来那条死路。

如果这个解释成立,那么这套系统真正起作用的部分可能比论文声称的简单得多。论文缺一个关键消融:只给根因步不给标签、只给标签不给步、给随机偏移的步,三组对照跑一遍。作者在 Limitations 里承认了这一点,说未来工作要「通过匹配的重跑消融来收紧子类预测、精确步定位、证据质量与行为修复之间的联系」。这个坑留得很大。

样本规模和覆盖面。 204 条,只在 OSWorld。跨 agent 的行只有 30 条,且结论在这些行上翻转。任何基于跨 agent 结果的推论都站不住。

S 类 F1 只有 0.35,而这恰恰是真机测试占比最高的一类。 桌面 OSWorld 里外部/系统失败只有 13 条(6.4%),且全部来自 Claude split。真机上呢?权限弹窗、系统升级、网络抖动、机型差异、后台被杀——这些在真实设备农场里能占到失败的三到五成。这套方法在它最不擅长的类别上,恰好要面对最大的量。原因也不难理解:环境类失败的证据往往不在截图里,而在 logcat、系统事件、网络层。只看屏幕的调试器天然判不了这类。

修复本身在制造新缺陷。 单次重跑里本文方法的 New+Fixed 是 44+24——修好了原错误的 68 例中,有 44 例(65%)引入了新的关键错误。持续重跑里这个比例好一些(27/85,32%)。这说明「按诊断去修」这个动作本身有相当高的副作用率。放到测试自愈的场景里,这个比例是不能接受的——自愈改了定位策略,结果用例在别的地方挂了,比原来直接失败更难排查。

Others 类的 F1 0.86 有点虚。 这一类全是 OSWorld 的 infeasible-task,是个高度模式化的类别(「这题做不了」),拉高了整体均值。真实场景里的「其他」通常是杂项,不会这么好判。

可复现、可落地的建议

先落 trace 结构和分类法,再谈自动归因。 配对观测的落盘和 P/G/R/S/O 的分类定义,这两件事不依赖任何模型能力,收益立刻可见,而且是自动归因的前提。倒过来做——先上一个 LLM 归因服务,再发现 trace 里没有它需要的信息——是常见的顺序错误。

线上指标用 Step ±2,不用 Step Exact。 29.4% 对 47.6%,差了一倍多。定位到根因步附近对人工排查已经足够有用,要求精确匹配是在给自己设一个达不到的标准。

把人工标注当冷启动资产,不当一次性成本。 论文的负面实验已经说明白了,指望模型自己长出一套稳定分类法是不现实的。团队自己的标注 schema + 种子样本,价值随时间累积。

归因结论要带置信度,且低置信度的要能说「不知道」。 CUADebugger 的 schema 里有个 [0,1] 的校准置信度字段,这个设计值得抄。一个诚实的「我定位不了」比一个自信的错误归因有用得多——后者会把排查方向带偏,浪费的时间远超省下的。

小结

CUADebug 推进的不是榜单,而是一个新的评价方式:agent 的失败诊断该用「注入后行为有没有改善」来检验,而不是用「解释听起来合不合理」。

它的数字并不漂亮——联合诊断准确率不到两成,环境类失败的 F1 只有 0.35,单次重跑里修好了原错误的案例有六成又引入了新的关键错误。但这些难看的数字本身是有价值的,它们把「自动失败归因」这件事的当前水位标了出来:能给出有用的方向性提示,给不出可以直接采信的结论。

对做 APP 自动化测试的团队来说,最可搬的三件事都不在模型侧:配对观测的 trace 结构、五类根因的缺陷分类法、以及「先定位再定点重跑」这套自愈做法。这三件事不需要等模型变强,现在就能做,而且做完之后,等到自动归因真正可用的那天,你的数据是现成的。

那个负面实验值得单独记一笔:模型能发现结构,但找不到稳定的 schema。 任何想把分类工作整体外包给 LLM 的计划,都该先看看这个实验。

参考