CodeTracer:给AI代码智能体装上"黑匣子",故障追溯一步到位

你有没有遇到过这种情况——一个代码智能体跑了200步,最后提交的patch还是错的,你盯着满屏日志完全不知道它到底在哪一步走偏了?更让人崩溃的是,有时候智能体明明在前面已经读到了正确的线索,却还是做出了错误的决定,然后一路错下去,越陷越深。

这就是当前代码智能体调试的真实困境:我们只有"成功"或"失败"的标签,没有任何过程级的诊断信息。你没法知道智能体哪一步开始出错,也没法知道错误是怎么一步步级联放大、最终把整条执行路径搞崩的。

CodeTracer这篇论文,正是冲着这个问题来的。

核心摘要

CodeTracer提出了一套面向代码智能体的层次化追踪与故障定位框架。它通过演化提取器解析不同框架产生的异构运行产物,把扁平的执行日志重建为层次化的追踪树,再执行故障起始定位——找出最早出错的阶段和步骤。配套构建的CodeTraceBench包含3.3K条步骤级标注轨迹,覆盖4个智能体框架、5个前沿骨干。实验表明CodeTracer的步骤级F1达到48%,远超直接提示基线的19%和轻量级基线的19%,且Token成本更低。更有价值的是,把诊断信号反馈给智能体进行反射回放,能在匹配预算下一致地恢复原本失败的运行。这项工作揭示了一个被忽视的事实:智能体往往已经收集到了正确的证据,只是没能把它转化为正确的行动。

1 问题背景:为什么我们需要追踪智能体状态?

当前代码智能体评估有个根本性的盲区——几乎所有基准都只看端到端指标。SWE-bench看patch是否通过测试,TerminalBench看终端交互是否完成。整个执行轨迹被折叠成一个"通过"或"不通过"的标签。

这就好比去医院体检,医生只告诉你"你不健康",但不告诉你哪里出了问题、什么时候开始出的问题。你拿到了诊断结果,却没法对症下药。

更具体地说,代码智能体的执行有一个很要命的特征:错误级联。一个早期的小失误——比如误读了某个函数的返回类型——可能导致后续所有步骤都在错误假设上展开。智能体可能在第30步才明确"跑偏",但真正的错误起源可能在第5步就已经埋下了。你光看第30步的输出,根本回溯不到根因。

现有的智能体轨迹分析要么只关注简单的对话交互(比如单轮问答的错误定位),要么依赖小规模的人工检查(不可扩展)。代码智能体的运行产物又是异构的——SWE-Agent的日志格式和OpenHands完全不同,甚至同一个框架不同版本之间也会变。这些都让系统化的故障诊断变得极其困难。

2 CodeTracer架构:三阶段流水线

CodeTracer的设计思路很清晰:先把杂乱的原始产物标准化,再把扁平的步骤序列结构化,最后在结构化的追踪树上做诊断。

CodeTracer流水线总览

图1:CodeTracer整体流水线。原始轨迹被标准化为层次化追踪,经过策划构建CodeTraceBench并提供步骤级监督,再通过故障起始定位进行诊断,可选反射回放。

2.1 阶段一:演化提取

代码智能体框架五花八门,日志格式各不相同。SWE-Agent输出一种格式,OpenHands输出另一种,Terminus 2又有自己的格式。更麻烦的是,同一框架升级后格式也可能变。

演化提取器就是这么来的——它扫描运行目录,生成一个"布局规范"来描述哪些产物记录了哪些执行步骤。然后检查现有解析器注册表,如果找不到匹配的解析器,就自动合成一个新的并注册。这样一来,每种独特格式只需要解析一次,后续所有使用同一格式的运行都可以复用。

提取出来的标准化步骤记录带有类型字段:action(智能体主动执行的操作)、observation(环境返回的响应)、diff(代码变更)、verification outcome(验证结果)。这个标准化过程还防止了跨运行的格式漂移,确保后续分析的一致性。

这个设计挺巧妙的。想想看,如果每来一个新框架就要手写解析器,那维护成本直接爆炸。演化提取器让CodeTracer能自动适应新格式,而人工只需要审核自动合成的解析器是否正确就行。

2.2 阶段二:树索引

这一步是整个架构的核心创新。标准的智能体轨迹就是一个扁平的步骤列表:步骤1、步骤2、步骤3……但这个扁平结构丢失了关键信息——步骤之间的状态依赖关系。

CodeTracer把扁平步骤序列转换成层次化的追踪树

  • 探索节点:只检查当前环境、不修改代码库或执行状态的步骤,挂在同一个状态节点下
  • 状态更改节点:修改了代码库或执行环境的步骤,触发一个到子状态的转换

层次化追踪树

图4:层次化追踪树。探索步骤保留在当前状态节点下,而状态更改步骤诱导到子状态的转换。CodeTracer在树索引期间构建此结构,提供执行轨迹的压缩导航。

打个比方,你在调试一段代码。你先读了一遍源码(探索),再读了一遍测试文件(探索),然后修改了一行代码(状态更改),接着运行测试看结果(探索),发现测试没过,又改了一处(状态更改)。在扁平序列里就是5个步骤,但追踪树告诉你:第一次修改把状态从A带到了B,在B里你做了探索和第二次修改把状态从B带到了C。每个状态转换都是一个分水岭,后续的操作都是在修改后的上下文里执行的。

这个结构让故障定位变得直观得多——如果最终失败了,你只需要沿着树往上回溯,看哪个状态转换引入了问题。

2.3 阶段三:诊断

诊断阶段遍历追踪树,输出三样东西:

  1. 故障负责阶段 \(\hat{s}\):最早引入错误的阶段
  2. 错误相关步骤集 \(P\):该阶段内真正导致问题的步骤
  3. 紧凑证据集 \(E\):支撑诊断结论的最小证据

诊断用的评分特征也很有讲究,不是随便选的:

  • 验证回归:某阶段的状态更改是否导致先前通过的测试失败了
  • diff幅度:阶段内状态更改步骤的累计修改行数
  • 回溯频率:后续阶段中撤销或重试此阶段工作的次数
  • 探索-行动比:阶段中状态更改与探索步骤的比例

这四个特征从不同角度刻画了"这个阶段可能有问题"——你改了东西导致测试挂了、你改了一大堆、你后面反复在回退你的修改、你在探索但迟迟不行动。每个特征都指向一种典型的故障模式。

3 CodeTraceBench:怎么评估故障定位?

有了方法还不够,还得有数据来评估。CodeTracer构建了CodeTraceBench这个基准。

3.1 数据收集与过滤

从5个基准(SWE-bench Verified、SWE-bench Pro、MultiSWE-bench、SWE-PolyBench、TerminalBench)出发,用4个智能体框架(SWE-Agent、MiniSWE-Agent、OpenHands、Terminus 2)和5个骨干模型(Claude-sonnet-4、GPT-5、DeepSeek-V3.2、Qwen3-Coder-480B、Kimi-K2-Instruct)生成轨迹。

从7,936条原始轨迹开始,经过四轮严格过滤——移除超时的、日志不完整的、环境配置错误的、步骤过少且成功的——最终保留3,326条。

3.2 标注体系

标注是这篇工作另一个值得关注的点。每条轨迹都有阶段标签(环境验证、依赖安装、检查/调试、修补、验证),失败轨迹还有基于链的向后追踪标注。

向后追踪的逻辑很直接:从失败的测试输出开始,递归向上游追踪,直到前述步骤不再包含错误或失败原因与早期决策无关。终止点就是错误关键步骤——触发下游级联的最早决策。

错误类型也被分类为受控词汇:环境/设置问题、依赖解析失败、错位编辑、不正确假设、验证误判、非生产性循环。

标注者间一致性在15%子集上达到Cohen's \(\kappa\) = 0.73,算是不错的水平。

CodeTraceBench最终有3.32K实例的完整分割和1.06K实例的验证分割(更高质量),每个实例记录了框架、骨干、任务元数据、阶段边界、故障关键阶段标签和不正确步骤标注。

4 轨迹分析发现:智能体的行为模式

在进入实验结果之前,论文从轨迹语料库中提取了一些非常有价值的发现,值得单独说说。

发现1:模型在解决不了任务时的"伪装"行为

各骨干解决的任务类别

图2:各骨干解决的任务类别数。中心66个类别被所有五个模型解决;65个类别没有任何模型能解决。

不同骨干在不同领域各有千秋——GPT-5在图密集和化学任务上相对强,Claude-sonnet-4在贝叶斯推理上占优,Kimi-K2-Instruct在图形和光线追踪上表现好。这本身不意外。

但真正有意思的是:当所有模型都无法真正解决任务时,它们的行为惊人地相似——不是坦诚地承认失败,而是通过伪造证据、输出占位符假装是真实结果、或者陷入非生产性循环后过早停止来绕过瓶颈。这让我想到,评估代码智能体时,"看起来解决了"和"真正解决了"之间的差距可能比我们想象的大得多。

发现2:过度工程化——更多编排不等于更好结果

智能体 成功率(%) 平均步数 Token(k)
MiniSWE-Agent 32.8 19.9 44.6
Terminus 2 35.2 24.7 51.3
SWE-Agent 37.5 41.2 86.7
OpenHands 38.3 43.9 91.4

从MiniSWE-Agent到OpenHands,步骤数翻了一倍多,Token消耗翻了一倍,成功率只提高了不到6个百分点。额外编排增加成本但未带来成比例的收益,骨干模型能力才是主要杠杆。这个发现对实际部署很有参考价值——与其堆框架复杂度,不如选更好的模型。

发现3:错误类型随工作流阶段可预测变化

错误关键步骤在各阶段的分布

图3:错误关键步骤在各阶段的分布,对比解决和未解决的运行。

环境和依赖错误集中在早期阶段,错位编辑和不正确假设集中在后期的修补和验证阶段。这种可预测性意味着你可以做阶段感知的防护栏——在不同阶段检查不同类型的错误,在故障级联之前就把问题拦截住。

发现4:迭代预算——40步之后的边际收益断崖式下降

max_iter Claude Res.% GPT-5 Res.% DeepSeek Res.%
10 12.67 38.69 12.43
20 30.13 45.48 27.50
40 38.00 47.06 38.18
100 41.65 47.06 39.75
≥150 41.65 47.06 39.75

GPT-5在40步和150步之间成功率完全没变(都是47.06%),Claude-sonnet-4从40步到150步只提高了3.65个百分点,但Token消耗从493k飙升到886k。多出来的迭代全花在了非生产性循环上。这对工程实践是个重要信号:设40步的上限就够了,再多就是浪费算力。

5 实验结果:CodeTracer到底行不行?

5.1 主要定位结果

配置 骨干 Overall F1 Overall Tok(k)
Bare LLM Claude-sonnet-4 16.22 105.1
Bare LLM GPT-5 18.78 58.5
Bare LLM DeepSeek-V3.2 16.33 83.4
Mini-CodeTracer Claude-sonnet-4 19.17 82.4
Mini-CodeTracer GPT-5 19.33 44.8
Mini-CodeTracer DeepSeek-V3.2 19.24 63.8
CodeTracer Claude-sonnet-4 46.57 56.8
CodeTracer GPT-5 48.02 31.1
CodeTracer DeepSeek-V3.2 46.14 44.6

几个关键观察:

CodeTracer在F1上碾压两个基线。Bare LLM(直接把原始日志扔给LLM让它找错误)只有16-19%的F1,Mini-CodeTracer(只加了诊断循环)也差不多。完整的CodeTracer直接跳到46-48%,这是接近3倍的提升。

CodeTracer的Token成本反而更低。Bare LLM因为要把整条日志塞进上下文,消耗了大量Token(58-105k)。CodeTracer通过层次化追踪树压缩了信息,GPT-5甚至只需要31k Token就达到了48%的F1。

三个骨干达到相似F1但策略不同。GPT-5倾向于早停,Precision更高(45.02%)但Recall低一些(51.46%);Claude-sonnet-4扫描更深,Recall最高(54.87%)但Precision低(40.47%);DeepSeek-V3.2在P/R之间更均衡。这跟日常使用这些模型的体验一致——Claude确实更"啰嗦"但覆盖面广,GPT-5更"果断"但可能漏掉一些东西。

5.2 组件消融:树索引才是关键

配置 Claude F1 GPT-5 F1 DeepSeek F1
Bare LLM 16.22 18.78 16.33
Mini-CodeTracer 19.17 19.33 19.24
+ 演化提取 28.12 29.45 28.38
+ 树索引 46.57 48.02 46.14

树索引贡献了最大的单次增益——加上树索引后F1从约29%跳到约47%,一口气提升了18个百分点。演化提取贡献了约9.4个百分点的提升,也不容忽视。

这个结果在情理之中。扁平的步骤列表很难让模型理解步骤之间的因果依赖——你告诉模型"步骤5可能有问题",它很难判断步骤5的修改对后续步骤8、15、23造成了什么影响。但追踪树把这些依赖关系显式化了,模型只需要沿着树结构做推理就行。

5.3 反射回放:诊断信号能救回失败的运行

反射回放结果

图6:反射回放。在匹配预算下注入CodeTracer诊断信号前后,原本失败运行的Pass@1。

把CodeTracer诊断出的故障关键阶段和证据作为前缀提示注入给智能体,让它在相同Token预算下重新尝试。结果是在原本失败的运行上,所有骨干的Pass@1都一致提升。

而且诊断通道本身的Token消耗很低——Claude-sonnet-4平均8.4k、GPT-5平均5.2k、DeepSeek-V3.2平均7.1k。这些Token还是在回放预算之外额外计算的,不影响公平比较。

5.4 证据到行动的差距

步骤预算分解

图5:每个骨干(已解决vs未解决)的步骤预算分解。每条柱状图显示了正确状态更改、有用探索或无效步骤占总步骤的比例。

这个发现我觉得是整篇论文最深刻的洞察之一。对比已解决和未解决的轨迹:

  • 无效步骤占比从已解决的22%几乎翻倍到未解决的40%
  • 正确状态更改从30%下降到21%
  • 探索的有用性只轻微下降

这意味着什么?智能体在失败的运行中并没有停止探索——它还是找到了有用的信息,但就是没法把这些信息转化为正确的行动。这就是论文所说的证据到行动差距(evidence-to-action gap)。

这个发现对智能体的改进方向有重要启示:与其让智能体探索更多信息,不如提高它把已有证据转化为正确操作的能力。CodeTracer的反射回放之所以有效,可能正是因为它帮智能体越过了这个"证据到行动"的鸿沟。

6 工业智能体分析:学术vs生产的差距

论文还把CodeTracer应用到了Claude Code(Anthropic的生产级代码智能体)上,做了一些定性对比。这个分析虽然不是定量的,但揭示了一些很有意思的差异:

工具投资差距巨大:Claude Code有40+专门工具分8个类别,学术智能体只有5-10个通用工具。生产级智能体在专门的错误恢复基础设施上投入巨大。

上下文管理更复杂:Claude Code实现了压缩、Token预算跟踪、特征门控等复杂的上下文管理策略,学术框架基本不操心这些。

探索-更改比更低:Claude Code每个探索步骤伴随更多的行动步骤,和更高的任务成功率相关。这也佐证了前面"证据到行动差距"的发现——真正高效的不是探索更多,而是更果断地行动。

并行执行引入新问题:工业智能体的并行工具调用虽然减少了挂钟时间,但引入了顺序学术框架中不存在的排序敏感性问题。

7 局限性与思考

CodeTracer当然不是万能的。论文也坦诚地列出了几个局限:

  1. 覆盖面有限:CodeTraceBench不覆盖所有软件工程智能体或真实世界仓库的完整设计空间
  2. 标注主观性:虽然Cohen's \(\kappa\) = 0.73还行,但阶段和步骤级标注仍然依赖标注者的解释
  3. 离线评估:追踪和回放结果在匹配的离线预算下评估,不能完全捕捉交互式人类监督的场景
  4. 回放不保证通用:反射回放实验不能保证在所有任务类别和模型系列上都能鲁棒改进

我自己的一个想法:CodeTracer目前是事后诊断的框架——等智能体跑完了再分析哪里出了问题。如果能做到在线追踪,在智能体执行过程中实时检测故障信号并介入干预,价值会大得多。当然,这会引入延迟和成本的问题,但至少是一个值得探索的方向。

另外,CodeTraceBench的标注成本不低——3.3K条轨迹,每条都要做阶段标注和错误关键步骤标注。如果要扩展到更多框架和任务,标注的可扩展性是一个挑战。论文提到的演化提取器解决了"解析可扩展性"的问题,但"标注可扩展性"还没有完全解决。

8 总结

CodeTracer做了一件代码智能体领域早就该做但一直没人系统做好的事——给智能体的执行过程装上"黑匣子",让你能追溯每一步状态变化、定位故障的起始点。

它的三个核心贡献各有价值:演化提取器解决了异构日志的标准化问题,树索引把扁平序列变成了可导航的层次结构,诊断阶段在结构化追踪上精确定位故障源头。CodeTraceBench则为这个方向提供了第一个系统化的评估基准。

但我觉得这篇工作最大的价值不在于技术本身,而在于它揭示的那个洞察:智能体的问题往往不是信息不足,而是行动不力。它们收集到了正确的证据,却在把证据转化为行动时掉了链子。CodeTracer的反射回放之所以能救回失败的运行,正是因为它帮智能体把已经看到但没用好的信息,转化成了切实的纠正操作。

对于做代码智能体研发的同学来说,这篇论文至少有两个直接收获:一是40步迭代预算基本够用了,再多就是浪费;二是与其堆框架复杂度,不如关注智能体把已有证据转化为正确操作的能力。


论文信息:CodeTracer: Towards Traceable Agent States. Han Li, Yifan Yao, Letian Zhu 等16位作者. 南京大学、快手科技、中科院自动化所、伦敦大学学院、中国人民大学、阿里巴巴集团. arXiv:2604.11641v3, 2026年4月.

代码与数据:https://github.com/NJU-LINK/CodeTracer


觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我