CodeTracer: Towards Traceable Agent States
Paper: arXiv:2604.11641 Code: NJU-LINK/CodeTracer Code reference:
main@2d302191(2026-04-14)
1. Motivation (研究动机)
现有 code agent 的问题不是只“能不能解题”,而是失败后很难回答“从哪一步开始错、错误如何沿着后续动作传播”。SWE-Bench / TerminalBench 这类评测通常给出 end-to-end pass rate 或 patch correctness,把几十到几百步的读文件、改代码、装依赖、跑测试压缩成一个成功/失败标签;一旦失败,研究者只能人工翻日志,既慢又不稳定。
论文指出三个具体瓶颈:第一,不同 agent framework 的 run artifacts 结构差异很大,OpenHands、SWE-Agent、MiniSWE-Agent、Terminus 2 的日志、sessions、step 文件和结果文件并不共享统一 schema,硬编码 parser 很脆弱。第二,code agent 的动作有状态含义:grep / cat 只是探索,sed -i / patch / install 会改变代码或运行环境;如果把所有 step 当平铺日志,无法看出 agent 是在同一状态下继续探索,还是已经进入新状态后继续犯错。第三,失败通常是链式的:一个早期错误假设、错误 patch 或错误验证解释,会让 agent 在后面花很多 token 修补错误前提,最终形成 hidden error chain。
CodeTracer 试图解决的目标是 failure onset localization:给定一条已经标准化并按 stage 划分的轨迹 ,预测失败负责阶段 、该阶段内的 error-relevant steps ,以及支撑诊断的 compact evidence set 。这个问题值得研究,因为它把 agent debugging 从“看最终失败”推进到“定位错误起点 + 给 agent 可复用的诊断信号”,从而既能做 process-level benchmark,也能把诊断结果注入 reflective replay 来恢复原本失败的 runs。
2. Idea (核心思想)
核心洞察:code agent 的执行轨迹应该被建模成“状态转换历史”,而不是线性聊天记录。只有区分 exploration step 和 state-changing step,并把修改造成的新状态显式连到树上,failure localization 才能追踪“错误是在什么状态下产生的、后续动作是否只是沿着错误状态继续展开”。
CodeTracer 的创新是把三件事串起来:用 evolving extraction 处理异构 run directory,把 flat steps 标准化;用 tree indexing 构造 hierarchical trace tree,让状态改变成为树上的分支;再让 diagnosis agent 在压缩树和 evidence queries 上定位 failure onset。与 Bare LLM 直接读 raw log 相比,它不是把全部 token 花在长日志扫描上,而是先把轨迹结构化,再在更小的候选状态空间里找错误链。
与传统 agent observability / tracing 的差别在于,CodeTracer 不只记录“发生了什么 API/tool call”,还把动作是否改变执行状态作为诊断结构的一部分;与 outcome-only benchmark 的差别在于,CodeTraceBench 给 stage-level 和 step-level 监督,评估的是 failure evidence 的 Precision/Recall/F1,而不是最终 patch 是否通过。
3. Method (方法)
3.1 Overall framework:从 raw trajectory 到诊断与 replay
Figure 1 解读:左侧是来自 SWE-Bench / TerminalBench 等任务和多个 agent framework 的 raw trajectories;中间 CodeTracer 先把异构 artifacts 标准化成 hierarchical traces,再构造 CodeTraceBench 的 step-level supervision;右侧诊断模块输出 failure onset localization,并可把诊断信号作为 prefix hint 注入 reflective replay。图中的关键是“trace tree + failure evidence”把原本不可读的长日志变成可查询的状态历史。
形式化地,CodeTracer 的输入是标准化轨迹 和按时间排序的 stages;输出是失败负责阶段 、该阶段内预测的错误相关步骤集合 、以及证据集合 。监督集合记为 ,评估时用 step-level metrics: 直觉上,CodeTracer 能工作是因为它先减少了“日志的表示噪声”,再减少了“错误候选空间”。对一个失败 run,agent 往往会在同一个错误假设下重复探索;tree indexing 把这些探索挂在同一个 state node 下,使诊断器可以看到:后续很多行为不是新的独立错误,而是某次 state-changing action 之后的连锁反应。
3.2 Evolving extraction:让异构轨迹可解析、可复用
论文中的 evolving extraction 做三件事:扫描 run directory,找到记录 execution steps 的 artifacts;生成 compact layout spec;如果现有 parser registry 无法解析,就 synthesis/register 一个新 parser,并用固定 query schema 实例化诊断 prompt。released code 中对应 src/codetracer/discovery/explorer.py 和 src/codetracer/query/normalizer.py:前者做 marker scan、skill detection、必要时 LLM-guided directory analysis;后者把 parser 输出写成 steps.json、task.md、stage_ranges.json 等派生产物。
def evolving_extract(run_dir, skill_pool, llm, output_dir):
candidates = marker_scan(run_dir, markers=["steps.json", "step_*.jsonl", "results.json"])
if not candidates and llm is not None:
candidates = llm_directory_analysis(run_dir)
skill = skill_pool.detect(run_dir)
if skill is None:
skill = synthesize_parser_with_llm(run_dir, existing_skills=skill_pool.index())
skill_pool.register(skill)
traj = skill.parse(run_dir) # -> NormalizedTrajectory(steps, task_description, metadata)
write_json(output_dir / "steps.json", [step.model_dump() for step in traj.steps])
write_text(output_dir / "task.md", traj.task_description)
return traj, skill如果没有 evolving extraction,CodeTracer 会退化成“为每个 agent framework 写一次性脚本”。这会在新日志格式、截断 traces、不同 session layout 上失效,也无法把已学到的 parser 经验迁移到下一批 trajectories。
3.3 Tree indexing:把 flat steps 变成状态树
Figure 4 解读:这张图展示 tree indexing 的核心规则:exploration steps 留在当前 state node 下,state-changing steps 触发到 child state 的 transition。这样,读文件、搜索代码、跑测试等探索动作不会虚假制造新状态;而写文件、安装依赖、patch、改变配置等动作会被看作改变后续上下文的关键节点。诊断器随后沿树导航,而不是从第 1 步到最后一步线性扫描。
released code 的 src/codetracer/query/tree_builder.py 里,ClassificationStore 先用 regex / persistent JSONL / optional LLM fallback 把 action 分类为 change 或 explore;_build_parent_pointers 的核心逻辑是:第一个节点 parent 为 -1,change 节点挂到前一步,explore 节点沿用前一个节点的 parent。这样会形成“修改动作开新状态,探索动作仍属于当前状态”的压缩树。
def build_trace_tree(trajectory, classifier):
metas = []
for step in trajectory.steps:
node_type = classifier.classify(step.action) # "change" or "explore"
label = first_line(step.action)[:60] or f"step {step.step_id}"
metas.append({"step_id": step.step_id, "node_type": node_type, "label": label})
parents = []
for i, meta in enumerate(metas):
if i == 0:
parents.append(-1)
elif meta["node_type"] == "change":
parents.append(i - 1)
else:
parents.append(parents[i - 1])
nodes = [TreeNode(step_id=m["step_id"], label=m["label"], node_type=m["node_type"]) for m in metas]
for i, parent in enumerate(parents):
if parent >= 0:
nodes[parent].children.append(nodes[i])
return TraceTree(root_children=[n for n, p in zip(nodes, parents) if p == -1])3.4 Diagnosis:定位 failure responsible stage 与 evidence set
诊断阶段把 tree.md、stage ranges、任务上下文、历史 memory、budget context 组装成 prompt,要求 trace agent 输出结构化结果:failure responsible stage、error-relevant step ids、compact evidence。released code 的 src/codetracer/agents/trace_agent.py 只是高层 wiring,真正的循环在 BaseAgent:模型每轮只能给一个 bash command,Executor 在 trajectory artifacts/workdir 中执行,输出被记录到 message history;同时 CostTracker 控制预算,CompactManager 管理上下文,OnlineMemoryExtractor 可在分析中抽取跨轨迹经验。
Appendix 给出的 candidate-stage scoring features 包括:verification regression(某 stage 的 state-changing step 是否让先前通过的测试失败)、diff magnitude(该 stage 的累计改动行数)、backtrack frequency(后续 stage 回滚或重试该 stage 的频率)、exploration-to-action ratio(探索 vs 状态改变的比例)。这些特征不是最终标签,但帮助 trace agent 排序可疑阶段。
def diagnose_failure(trace_tree, stage_ranges, task_context, llm, executor, memory, budget):
messages = build_trace_messages(
tree_md=trace_tree.to_markdown(),
stage_ranges_json=stage_ranges,
task_context=task_context,
memory_text=memory,
budget_context=budget,
)
trajectory = []
while not budget.exhausted():
response = llm.chat(messages)
command = parse_single_bash_block(response)
output = executor.run(command)
trajectory.append({"response": response, "command": command, "output": output})
messages = append_observation(messages, output)
if output_file_contains_final_json():
break
return read_final_localization_json(), trajectory3.5 CodeTraceBench:把过程监督做成 benchmark
Figure 2 解读:中央 66 个类别被 5 个 backbones 都解决过,另有 65 个类别没有任何模型解决。论文用它说明:不同 backbone 的 aggregate solved count 并不能完全解释失败模式;在 universally hard categories 上,模型会收敛到类似行为,如 fabrication、placeholder outputs 或 early stopping。
CodeTraceBench 来自五个 benchmark:SWE-bench Verified、SWE-bench Pro、MultiSWE-bench、SWE-PolyBench、TerminalBench;四个 agent frameworks:SWE-Agent、MiniSWE-Agent、OpenHands、Terminus 2;五个 backbones:Claude-sonnet-4、GPT-5、Kimi-K2-Instruct、Qwen3-Coder-480B、DeepSeek-V3.2。论文先得到 4,354 条 standardized + step-level annotated trajectories,再构造 full split(3.32K instances)和更高质量的 verified split(1.06K)。每个 instance 记录 framework、backbone、task metadata(236 tasks、26 categories、difficulty labels)、raw artifact pointers、stage boundaries、failure critical stage labels 和 incorrect step annotations。
annotation protocol 对每个 step 标注 stage:environment verification、dependency installation、inspection/debugging、patching、verification。成功 trajectories 额外标注 redundant 与 trial-and-error steps;失败 trajectories 从最终 failing test output 做 chain-based backward tracing,递归找“哪一步导致当前中间错误”,直到找到 error critical step。错误类型 controlled vocabulary 包括 environment/setup issues、dependency resolution failures、mislocalized edits、incorrect hypotheses、verification misinterpretation、unproductive looping。随机 15% 子集做 double annotation,error-critical step label 的 Cohen’s 。
3.6 Reflective replay:把诊断信号注入原失败 run
Figure 6 解读:灰色柱是原失败 runs 的 baseline Pass@1,斜纹柱是在相同预算下注入 CodeTracer diagnostic signals 后的 replay Pass@1。Claude 从 41.6 提升到 48.3,GPT-5 从 32.6 到 38.2,DeepSeek 从 29.3 到 32.6,Qwen 从 20.2 到 23.9,Kimi-2.5 从 21.3 到 26.9。提升说明定位出的 failure-critical stage 和 evidence 不只是可解释性产物,还能作为 agent 纠错提示。
def reflective_replay(original_run, diagnosis, backbone, token_budget):
prefix_hint = render_hint(
failure_stage=diagnosis.failure_stage,
error_steps=diagnosis.error_step_ids,
evidence=diagnosis.compact_evidence,
)
replay_context = inject_context(original_run.task, prefix_hint)
return run_agent_again(
backbone=backbone,
context=replay_context,
budget=token_budget, # matched to original run
)3.7 Released code mapping
Code reference:
main@2d302191(2026-04-14) — pseudocode and mapping based on this commit
| Paper Concept | Source File | Key Class/Function |
|---|---|---|
| Evolving extraction / format discovery | src/codetracer/discovery/explorer.py | discover_trajectory_dirs, detect_or_generate_skill, _marker_scan, _llm_directory_analysis |
| Parser registry and normalization | src/codetracer/query/normalizer.py | Normalizer.detect, Normalizer.normalize, _write_derived_artifacts |
| Trace tree construction | src/codetracer/query/tree_builder.py | TreeBuilder.build, TreeBuilder.build_with_llm, _build_parent_pointers, _build_tree_nodes |
| Change/explore classification memory | src/codetracer/services/classification.py | ClassificationStore.classify, regex defaults, JSONL cache, LLM fallback |
| Diagnosis loop wiring | src/codetracer/agents/trace_agent.py, src/codetracer/agents/base.py | TraceAgent.run_iter, BaseAgent.run_iter |
| Prompt/context assembly | src/codetracer/agents/context.py, src/codetracer/config/default.yaml | ContextAssembler.build_trace_messages, trace.instance_template |
| Cross-trajectory memory | src/codetracer/services/memory.py | OnlineMemoryExtractor, load_memory, update_memory |
| Reflective replay support | src/codetracer/replay/context_inject.py, src/codetracer/replay/engine.py | build_messages, replay checkpoint/engine utilities |
| Benchmark adapters | src/codetracer/benches/seed/swe_bench/, src/codetracer/benches/seed/terminal_bench/ | BENCH.yaml, provider modules |
论文公式与 released code 实现差异:未发现核心公式层面的冲突;需要注意的是,论文实验报告使用 Claude-sonnet-4、GPT-5、DeepSeek-V3.2 等 backbones,而 released src/codetracer/config/default.yaml 的默认 llm.model_name 是 gpt-4.1-mini-20250414,README 的 quick evaluation 示例也用 --model gpt-4o。因此默认配置是可运行示例,不等同于论文表格的复现实验配置。
4. Experimental Setup (实验设置)
4.1 Datasets and scale
- Trajectory corpus:从 7,936 raw trajectories 开始,过滤 timeout(保留 6,511)、incomplete/truncated generation traces(6,109)、Docker/task 文件损坏(5,284)、少于 10 normalized steps 的 trivial correct runs,最终 retained 3,326 trajectories 用于 trajectory analysis。
- CodeTraceBench:从 4,354 standardized + step-level annotated trajectories 中筛选长程、证据充分、非近重复实例,形成
fullsplit 3.32K 和verifiedsplit 1.06K。 - Source benchmarks:SWE-bench Verified、SWE-bench Pro、MultiSWE-bench、SWE-PolyBench、TerminalBench,覆盖 repository-level bug fixing、refactoring、terminal interaction。
- Metadata scale:236 tasks、26 categories,包含 framework、backbone、difficulty、stage boundaries、failure-critical labels、incorrect step annotations。
4.2 Baselines and compared systems
实验分两层。过程分析层比较 MiniSWE-Agent、Terminus 2、SWE-Agent、OpenHands 的 success/cost。failure localization 层在 intersection subset 上比较 Bare LLM(直接读日志/提示)、Mini-CodeTracer(diagnosis loop 的轻量标准化基线)、CodeTracer(加 evolving extraction 与 tree indexing)。定位实验主要报告 Claude-sonnet-4、GPT-5、DeepSeek-V3.2;trajectory/cost 分析也覆盖 Qwen3-Coder-480B 与 Kimi-K2-Instruct。
4.3 Metrics
Precision 衡量预测错误步骤 中有多少落在 gold set ;Recall 衡量 gold failure evidence 被覆盖多少;F1 是二者调和平均。论文使用 macro averaging:每条 trajectory 先算一组指标,再平均,因此长轨迹不会因为 更大而支配结果。Tok.(k) 报告 LLM token consumption in thousands,用于衡量诊断效率。
4.4 Runtime / configuration details
论文未详细说明 GPU/hardware,因为 CodeTracer 不是训练一个新模型,而是离线分析与 replay 系统;主要 budget 是 LLM tokens 和 iteration/diagnosis steps。论文显式 sweep max_iter ∈ {{10, 20, 40, 100, ≥150}},并在 matched budgets 下做 replay。released code 的实际默认配置来自 src/codetracer/config/default.yaml:trace.cost_limit=3.0、trace.timeout=60、trace.step_limit=0、trace.action_regex 要求单个 bash code block、默认 llm.model_name=gpt-4.1-mini-20250414;README 的 batch evaluation 示例是 codetracer-batch --manifest ... --model gpt-4o --parallel 4 --output outputs/。
5. Experimental Results (实验结果)
5.1 Agent framework complexity vs success
| Agent | Succ. (%) | Steps | Avg. Steps | Tok.(k) |
|---|---|---|---|---|
| MiniSWE-Agent | 32.8 | 18,792 | 19.9 | 44.6 |
| Terminus 2 | 35.2 | 25,381 | 24.7 | 51.3 |
| SWE-Agent | 37.5 | 43,216 | 41.2 | 86.7 |
| OpenHands | 38.3 | 46,754 | 43.9 | 91.4 |
结论是 orchestration 越复杂不一定越有效:SWE-Agent / OpenHands 的 token consumption 接近 MiniSWE-Agent 的 2 倍,但 success 只增加 4.7–5.5 pp;Terminus 2 相比 MiniSWE-Agent 多用 6.7k tokens,success 增加 2.4 pp。论文据此认为,在这些 terminal/coding tasks 上,backbone capability 比 framework complexity 更像主导因素。
5.2 Iteration budget has saturation
| max_iter | Claude Res./Tok | GPT-5 Res./Tok | DeepSeek Res./Tok | Qwen Res./Tok | Kimi Res./Tok |
|---|---|---|---|---|---|
| 10 | 12.67 / 116.98 | 38.69 / 106.19 | 12.43 / 86.79 | 9.76 / 99.38 | 10.61 / 78.81 |
| 20 | 30.13 / 266.53 | 45.48 / 163.89 | 27.50 / 212.60 | 18.22 / 255.66 | 23.02 / 192.28 |
| 40 | 38.00 / 493.46 | 47.06 / 214.60 | 38.18 / 400.96 | 28.85 / 543.64 | 29.14 / 402.20 |
| 100 | 41.65 / 767.77 | 47.06 / 249.80 | 39.75 / 479.77 | 31.67 / 970.46 | 32.55 / 715.24 |
| ≥150 | 41.65 / 886.00 | 47.06 / 266.32 | 39.75 / 512.42 | 32.10 / 1182.06 | 33.27 / 805.38 |
主要现象是 40 steps 前 resolved rate 提升很快,之后收益饱和;更长预算主要补足 under-exploration,而不能根本修复早期错误假设。一旦 agent 早期进入错误 state,后续 token 往往消耗在 redundant exploration 或错误方向上的 patching。

Figure 3 解读:图中对比 solved 与 unsolved runs 的 error-critical steps 在 stages 中的分布。它支撑论文的 stage-dependent errors 观点:失败不是均匀分布在长轨迹中,而是经常集中在特定阶段;因此把轨迹切成 stage 并追踪 earliest critical stage 比直接读最终错误更有效。
5.3 Main localization results on CodeTraceBench
| Method / Backbone | Overall P | Overall R | Overall F1 | Tok.(k) |
|---|---|---|---|---|
| Bare LLM / Claude | 16.64 | 15.82 | 16.22 | 105.1 |
| Bare LLM / GPT-5 | 16.69 | 21.46 | 18.78 | 58.5 |
| Bare LLM / DeepSeek | 13.11 | 21.66 | 16.33 | 83.4 |
| Mini-CodeTracer / Claude | 20.50 | 26.24 | 19.17 | 82.4 |
| Mini-CodeTracer / GPT-5 | 26.03 | 21.39 | 19.33 | 44.8 |
| Mini-CodeTracer / DeepSeek | 24.08 | 20.98 | 19.24 | 63.8 |
| CodeTracer / Claude | 40.47 | 54.87 | 46.57 | 56.8 |
| CodeTracer / GPT-5 | 45.02 | 51.46 | 48.02 | 31.1 |
| CodeTracer / DeepSeek | 43.17 | 49.58 | 46.14 | 44.6 |
CodeTracer 在三种 backbone 上都把 Overall F1 从 Bare LLM 的 16–19% 区间提升到 46–48% 区间,同时 token cost 下降。GPT-5 的 Overall F1 最高(48.02)且 token 最低(31.1k),表现为更早终止搜索、更 compact;Claude Recall 最高(54.87),说明它覆盖更多 failure-relevant evidence,但 token 较高(56.8k);DeepSeek 的 P/R gap 更均衡。
5.4 Ablation: evolving extraction and tree indexing both matter
| Configuration | Claude F1 / Tok | GPT-5 F1 / Tok | DeepSeek F1 / Tok |
|---|---|---|---|
| Bare LLM | 16.22 / 105.1 | 18.78 / 58.5 | 16.33 / 83.4 |
| Mini-CodeTracer | 19.17 / 82.4 | 19.33 / 44.8 | 19.24 / 63.8 |
| + Evolving Extraction | 28.12 / 71.0 | 29.45 / 37.9 | 28.38 / 55.3 |
| + Tree Indexing (= CodeTracer) | 46.57 / 56.8 | 48.02 / 31.1 | 46.14 / 44.6 |
Ablation 显示 two-stage gain:evolving extraction 通过 layout discovery + parser adaptation 让输入格式稳定,F1 从约 19 提到 28–29;tree indexing 贡献最大,把 F1 推到 46–48,并继续降低 tokens。这说明树结构不是展示层,而是诊断效率和准确率的核心。
Figure 5 解读:每个柱子把总 steps 分成 correct state changes、useful exploration、ineffective 三类,并用 hatched bars 标出 unsolved trajectories。图的意义是把“失败”拆成过程组成:失败 run 中无效动作比例更高,且不同 backbone/framework 的预算浪费模式不同;这为 CodeTracer 的 evidence retrieval 提供了行为层面的解释。
5.5 Effective action ratio and replay
Figure 7 解读:Claude、GPT-5、DeepSeek、Qwen、Kimi 的 effective action ratio 分布从左到右整体下降,individual panels 中 mean 分别约为 0.73、0.71、0.69、0.67、0.65。这个指标补充说明:强 backbone 不只是最终 pass rate 更高,它们的执行步骤中有效动作比例也更高;较弱模型更容易在低效探索或错误状态里循环。
Reflective replay 的 Pass@1 全部提升:Claude 41.6→48.3,GPT-5 32.6→38.2,DeepSeek 29.3→32.6,Qwen 20.2→23.9,Kimi-2.5 21.3→26.9。它证明 CodeTracer 的诊断不是 post-hoc narrative:当 failure-critical stage 与 evidence 被反馈给同一 backbone,agent 在 matched budget 下能恢复一部分原本失败的 runs。
5.6 Limitations
作者明确给出四类限制:第一,CodeTraceBench 虽覆盖多个 framework、backbone、workload,但仍不能覆盖全部 software engineering agents 或真实仓库分布,failure patterns / replay gains 可能不完全迁移。第二,stage 与 step-level labels 仍含 annotator interpretation,尤其是长轨迹里 incorrect、unuseful、error critical 的边界。第三,tracing 与 replay 在 offline matched-budget setting 中评估,不能完全模拟在线人工监督、环境变化或重复干预。第四,reflective replay 只说明局部证据能帮助恢复失败,不等于建立了通用训练信号,也不保证所有 task categories 和 model families 都稳健提升。