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

图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 阶段三:诊断
诊断阶段遍历追踪树,输出三样东西:
- 故障负责阶段 \(\hat{s}\):最早引入错误的阶段
- 错误相关步骤集 \(P\):该阶段内真正导致问题的步骤
- 紧凑证据集 \(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当然不是万能的。论文也坦诚地列出了几个局限:
- 覆盖面有限:CodeTraceBench不覆盖所有软件工程智能体或真实世界仓库的完整设计空间
- 标注主观性:虽然Cohen's \(\kappa\) = 0.73还行,但阶段和步骤级标注仍然依赖标注者的解释
- 离线评估:追踪和回放结果在匹配的离线预算下评估,不能完全捕捉交互式人类监督的场景
- 回放不保证通用:反射回放实验不能保证在所有任务类别和模型系列上都能鲁棒改进
我自己的一个想法: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前沿,关注我