- 0. 论文信息
- 1. 先说结论
- 2. 它在解决什么问题?
- 3. 核心思路一句话
- 4. 方法拆解(按可复现视角写)
- 5. 实验设置
- 6. 关键实验结果(数字尽量逐字保留)
- 7. Ablation / 分析里最值得记的点
- 8. 局限与适用边界
- 9. 复现清单(Checklist)
- 10. 我的判断:值不值得深读?
- 11. 一句话总结
0. 论文信息
- 标题:Test-Driven Agentic Development: Reducing Code Regressions in AI Coding Agents via Graph-Based Impact Analysis
- 中文意译:测试驱动的智能体开发:用图式影响分析减少 AI 编程智能体的代码回归
- 链接:https://arxiv.org/abs/2603.17973
- PDF:https://arxiv.org/pdf/2603.17973
- 时间:2026-03-18(v1)
- 代码:https://github.com/pepealonso95/TDAD
- 备注:本文原文可获取;下面的实验数字优先来自 arXiv 正文与项目 README。若某些实现细节作者未完全展开,我会明确标注。
1. 先说结论
今天我会选这篇。
原因很简单:它不是再做一个“会写代码的 agent”壳子,而是盯住一个更实际的问题——agent 修好了当前 issue,却顺手把原来通过的测试搞挂了。这件事在真实代码审查里比“单点修复率”更接近能不能 merge。
我的判断:
- 作者声称:TDAD 通过代码-测试图谱 + 影响分析,把需要验证的测试提前暴露给 agent,可显著降低回归。
- 实验观察:在 SWE-bench Verified 上,Qwen3-Coder 30B 设定下,test-level regression rate 从 6.08% 降到 1.82%,P2P failures 从 562 降到 155;同时发现 TDD prompt 单独使用反而更差(9.94%)。
- 我的判断:这篇最有价值的点不是“GraphRAG”这个词,而是它给了一个非常务实的经验——对 coding agent,提供结构化上下文(哪些测试最该跑)往往比给一大段流程口号(你要按 TDD 做)更有用。 这点很值得做进真实 agent 系统。
2. 它在解决什么问题?
问题不是 agent 会不会修 bug,而是:
修一个 bug 的 patch,是否会把一堆原本通过的测试搞坏?
作者把这个问题叫 regression。在 SWE-bench 这类 benchmark 里,大家通常盯的是 resolution rate(问题有没有修好),但实际工程里更重要的是:
- 你改动后有没有破坏别的模块;
- 你有没有因为共享接口变化,引发跨文件、跨模块连锁损坏;
- 你跑的测试是不是只覆盖了改动附近,而漏掉了真正受影响的地方。
作者给出的具体动机很强:
- vanilla agent 在 100 个实例里造成 562 个 pass-to-pass 测试失败;
- 平均每个生成补丁会打坏 6.5 个测试;
- 在个别案例里,一个 patch 能把 322 个原本通过的 P2P 测试全打挂。
这不是边角问题,而是默认会发生的问题。
3. 核心思路一句话
先离线把仓库索引成“代码—测试依赖图”,再在 agent 修代码时,把“最可能受影响的测试集合”以一个轻量技能形式喂给它,让它优先验证这些测试,而不是盲跑或只靠 TDD 提示词。
4. 方法拆解(按可复现视角写)
4.1 系统总览
TDAD 分两阶段:
阶段 A:离线建图 / 索引
给一个 Python 仓库,解析出:
- File 节点
- Function 节点
- Class 节点
- Test 节点
以及五类边:
CONTAINS:文件包含函数/类CALLS:函数调用函数IMPORTS:文件导入文件TESTS:测试覆盖/关联代码实体INHERITS:类继承类
阶段 B:在线影响分析
当 agent 改了若干文件后,TDAD 对这些 changed files 做 impact analysis,输出:
- 哪些测试最可能受影响;
- 按置信度打分后的测试清单;
- 一个静态
test_map.txt供 agent 用grep查询。
重点是:运行时不需要图数据库服务,不需要 MCP,不需要额外 API。 agent 只拿到一个 text map 和一份很短的 skill 说明,就能用。
4.2 图谱 schema
原文明确写了 4 类 node、5 类 edge:
Node types
File:Python 源文件,关键属性包括path,content_hashFunction:顶层函数,关键属性包括name,file,lines,signatureClass:类定义,关键属性包括name,file,basesTest:测试函数或测试方法,关键属性包括name,file,is_test
Edge types
CONTAINS:File -> Function/ClassCALLS:Function -> FunctionIMPORTS:File -> FileTESTS:Test -> Function/ClassINHERITS:Class -> Class
如果你要自己复现,一个最小可行版本其实就是:
- 用 AST 建
File/Function/Class/Test; - 先把
IMPORTS+CALLS+ 粗粒度TESTS做出来; - 后面再补更细的 linking heuristics。
4.3 Indexing pipeline
(1) AST parser
作者用 Python 标准库 ast 解析仓库。
抽取内容包括:
- 函数定义:签名、行号范围、docstring
- 类定义:基类、方法
- import 语句
- 函数体内 call target
测试识别规则也很朴素:
- 文件名:
test_*.py或*_test.py - 函数名:
test_* - 类名:
Test*
(2) Graph builder
把解析结果转成图:
- 每个文件一个
Filenode; - 文件里的函数/类建立
CONTAINS; - import 建
IMPORTS; - inheritance 建
INHERITS; - call target 建
CALLS。
(3) Test linker
这是最关键的部分,因为真实 Python 仓库里 test 组织方式很乱。
作者给了 3 个按优先级排列的 linking 策略:
- naming convention:比如
test_foo.py -> foo.py - prefix matching:逐步截断 test 文件 stem,找最像的 source 文件
- directory proximity:当多个候选都像时,用目录距离判定
此外还专门提到:
- 对 Django 这类把测试堆在
tests.py的情况,做了 proximity-based mapping。
这意味着它并不是靠昂贵动态分析,而是靠一组静态但足够工程化的 heuristics。
4.4 Impact analysis
给定 changed files 后,TDAD 并行使用 4 种策略打分,再合并:
- direct TESTS edge
- transitive call chain
- coverage-style linkage
- import-based relation
原文给了一个统一评分形式:
score = (1 - c_w) * w_strategy + c_w * confidence
其中:
c_w = 0.3confidence ∈ [0,1]- 论文列出的典型 confidence:
- direct TESTS:
1.0 - transitive call:
0.56 - coverage:
0.5 - imports:
0.45
- direct TESTS:
测试选择分三级:
- high:
>= 0.8 - medium:
0.5 ~ 0.8 - low:
< 0.5
默认最多返回 50 个测试。
作者还提供三种权重配置:
- conservative:偏 precision
- balanced:默认
- aggressive:偏 recall
4.5 它怎么接入 agent?
这是这篇最聪明也最务实的部分。
TDAD 最终给 agent 的不是一个复杂服务,而是两个静态工件:
test_map.txtSKILL.md
其中 SKILL.md 被作者收敛成一个很短的操作说明,大意只有三步:
- 修 bug
- grep
test_map.txt找相关测试 - 跑这些测试并修回归
作者特别强调,这个 skill 最终只有 20 行。而且在 auto-improvement loop 里,把说明从 107 行压到 20 行,本身就是最大增益来源之一。
我的理解:
- 对小/中型本地模型,长 prompt 经常不是帮助,而是噪声;
- 真正有价值的是“定位信息”而不是“流程说教”;
- 所以 TDAD 更像是一个 context tool,而不是一个复杂 orchestration framework。
4.6 Backend 架构
原型最初使用:
- Neo4j + Docker
后续迭代迁移为:
- NetworkX in-memory backend(默认)
- 持久化:
.tdad/graph.pkl - 安装:
pip install tdad - Neo4j 仍可选,通过
TDAD_BACKEND=neo4j
这点对可复现很关键:
- 你不需要先搭一个数据库才能试;
- 在 agent 系统里也更容易落地。
5. 实验设置
5.1 Benchmark
- 数据集:SWE-bench Verified
- 来源:12 个流行 Python 仓库,共 500 个经人工验证的 issue 实例
- 每个实例包括:
- issue 描述
- 仓库快照
FAIL_TO_PASS测试PASS_TO_PASS测试
论文里分两阶段:
- Phase 1:100 个实例
- Phase 2:25 个实例
5.2 模型与 agent 设定
Phase 1
- 模型:Qwen3-Coder 30B
- 量化:Q4_K_M 4-bit
- 推理:
llama.cpp - context window:32K
- temperature:0
- 每个实例 patch 生成预算:15 分钟
Phase 2
- 模型:Qwen3.5-35B-A3B
- 量化:4-bit
- 推理:MLX on Apple Silicon
- agent:OpenCode v1.2.24
作者解释为什么故意不用最豪华设定:
- 想证明 TDAD 不依赖 frontier 模型;
- 想让实验更便宜、更可复现;
- 想在“上下文特别珍贵”的资源约束条件下测试它。
5.3 评测指标
论文用了四个指标:
- Resolution rate:所有 F2P 测试都通过的比例
- Generation rate:是否生成了非空 patch
- Test-level regression rate:总 P2P failures / 总 P2P tests
- Instance-level regression rate:有至少 1 个 P2P failure 的 patch 比例
作者明确主张:
- test-level regression rate 才是主指标,因为它能区分“只坏 1 个测试”和“炸掉 322 个测试”的严重性差异。
6. 关键实验结果(数字尽量逐字保留)
6.1 Phase 1:100 个实例上的回归控制
作者报告:
Vanilla baseline
- Resolution: 31%
- Test-level regression rate: 6.08%
- Total P2P failures: 562
TDD prompting
- Resolution: 31%
- Test-level regression rate: 9.94%
- Total P2P failures: 799
GraphRAG + TDD(TDAD)
- Resolution: 29%
- Test-level regression rate: 1.82%
- Total P2P failures: 155
直接看结论:
- 相比 vanilla,P2P failures 从 562 -> 155,减少约 72%
- 相比 TDD-only,P2P failures 从 799 -> 155,减少约 81%
- test-level regression rate 从 6.08% -> 1.82%,降低约 70%
灾难性回归案例
论文举了几个特别说明问题的例子:
astropy-13977:vanilla 把 322 个 P2P 测试全打挂;GraphRAG+TDD 只坏 12 个django-13089:TDD prompting 单独使用把失败数从 4 直接放大到 352/352 全挂
这非常说明问题:
- “多提示词 + 更认真做 TDD”不等于更安全;
- 如果没给 agent 结构化定位信息,它可能只是更积极地改更多代码。
6.2 代价:resolution 小幅下降
TDAD 的 resolution 从 31% 降到 29%,下降 2 个百分点。
作者解释是:
- TDAD 会让 agent 在高风险情况下更常 abstain,导致 empty patch 增多;
- 换句话说,它更保守。
论文里的原话方向是:agent “knew what it didn’t know”。
我的判断:
- 这在真实工程里其实未必是坏事;
- 一个不确定就少出手的 agent,往往比“什么都敢改”的 agent 更可部署。
6.3 Phase 2:作为可复用 skill 接入另一个 agent 栈
在另一个模型 + agent 组合上,作者把 TDAD 作为 skill 重新评估:
| Metric | Baseline | TDAD Skill | Delta |
|---|---|---|---|
| Resolved | 6/25 (24%) | 8/25 (32%) | +8pp |
| Generated | 10/25 (40%) | 17/25 (68%) | +28pp |
| Res. of generated | 6/10 (60%) | 8/13 (62%) | +2pp |
| Empty patches | 15 | 8 | -7 |
| Regression Rate | 0% | 0% | 0pp |
这里的机制和 Phase 1 不一样:
- Phase 1 主要体现为 减少回归;
- Phase 2 则更像是 帮助 agent 理解代码结构,从而更愿意、也更能产出 patch。
也就是说,代码-测试图不仅是安全工具,也是导航工具。
7. Ablation / 分析里最值得记的点
7.1 TDD prompting paradox
这是本文最值得记的一条经验。
作者观察到:
- TDD prompting 单独使用,回归率反而从 6.08% 升到 9.94%。
论文给了两个解释:
- verbose prompt 挤占上下文:30B 小模型更需要仓库上下文,而不是一大段程序化说明
- ambition without localization:agent 因为“被要求更认真”而做了更大修改,但并不知道该验证哪些测试,所以 collateral damage 更多
作者还做了相关 ablation:
- 只缩短 prompt,不给 graph context,不能解释 TDAD 改善;
- 只加长 prompt,不给 graph context,也没有带来帮助;
- 真正关键的是:short prompt + graph-derived context。
这条结论我非常认同。
7.2 Auto-improvement loop
作者还做了一个外层自动改进循环,灵感来自 Karpathy 的 autoresearch。
流程大意:
- agent 每轮只改一个点
- unit tests 先 gate
- 小规模 benchmark 评估
- 好了就更新 best snapshot,差了就回滚
15 次迭代里:
- 接受 4 次
- 拒绝 11 次
最佳结果:
- Generation:28% -> 80%
- Resolution:12% -> 60%
- Regression:全程 0%(10-instance eval)
其中最大增益来自:
- 把
SKILL.md从 107 行精简到 20 行,resolution 直接从 12% -> 50%
这是一个很强的实证信号:
- 对 agent 工具设计,简洁而高信息密度 经常比“完整流程说明”更重要。
8. 局限与适用边界
8.1 只覆盖 Python 生态
当前方法明显是围绕 Python 仓库设计的:
- 依赖 Python AST
- 测试命名与目录规则也都是 Python 习惯
如果迁移到 JS/TS、Java、Rust,多半需要重写 parser 和 linker。
8.2 静态分析的上限
它主要依赖:
- AST
- import
- naming convention
- directory proximity
这意味着对动态分发、反射、运行时 monkey patch、复杂 fixture/参数化测试,可能会漏链或误链。
8.3 结果仍带资源设定依赖
论文刻意用资源受限设定做实验,这有优点,但也意味着:
- 在更强模型、更复杂 agent scaffolding 下,收益幅度未必完全一样;
- 尤其 resolution 的 trade-off,可能会变化。
8.4 Phase 2 样本偏小
第二阶段只有 25 个实例,而且 regression rate 两边都是 0%。 这让它更像一个“迁移可行性证明”,还不是强统计结论。
9. 复现清单(Checklist)
9.1 数据
- benchmark:SWE-bench Verified
- 需要拿到每个实例的:仓库快照、issue 描述、F2P、P2P tests
9.2 索引构建
- 解析所有 Python 文件:
ast - 建四类 node:File / Function / Class / Test
- 建五类 edge:CONTAINS / CALLS / IMPORTS / TESTS / INHERITS
- 重点实现 test linker:
- naming convention
- prefix matching
- directory proximity
- 针对 monolithic test modules 的特殊规则
9.3 影响分析
- 输入:changed files
- 四路并行策略:
- direct TESTS
- transitive calls
- coverage-style linkage
- imports
- 合并策略:保留最高分
- 默认 top tests:50
- profile:balanced(论文默认)
9.4 Agent 接入
- 导出静态
test_map.txt - 准备精简
SKILL.md - agent 运行流程:
- 修 bug
- grep test map 找受影响测试
- 运行这些测试
- 若失败则继续修正
9.5 实验配置
Phase 1 近似复现
- Qwen3-Coder 30B
- 4-bit quantization(Q4_K_M)
- llama.cpp
- 32K context
- temperature 0
- 每题 15 分钟预算
Phase 2 近似复现
- Qwen3.5-35B-A3B
- 4-bit
- OpenCode agent
9.6 评测
用 SWE-bench Docker harness:
- checkout base commit
- apply patch
- 跑 F2P
- 跑 P2P
- 记录 resolution / generation / test-level regression / instance-level regression
10. 我的判断:值不值得深读?
值得。 但我会把“值得读”的原因分成两层。
10.1 为什么值得读
第一层:问题选得对
它盯住的是 coding agent 最实际的失败模式之一:
- 不是“能不能写出 patch”
- 而是“能不能不把仓库搞坏”
第二层:方法足够工程化
它没有堆很重的在线系统:
- 不是一个复杂平台;
- 更像一个随时能加进现有 agent stack 的 skill。
第三层:给了一个很可迁移的设计原则
对 agent 来说,好的结构化上下文,常常比详细流程指令更重要。
这个原则不只适用于测试影响分析,也适用于:
- tool selection
- repo navigation
- browser agent 的 action grounding
- multi-agent 的 task routing
10.2 我保留的疑问
- 如果换成更强的 frontier coding agent,TDAD 还能有多大边际收益?
- 静态 test linkage 在超大仓库、复杂 fixture、生成式测试下会不会显著退化?
- 如果把 runtime trace / coverage 真正纳入,会不会进一步提升 precision?
- 真实企业 CI 里,除了 test regressions,还要不要把 lint、type-check、benchmark regression 一起纳入 impact map?
10.3 如果我要把它用起来
我会优先做两件事:
- 短期:把它当成 coding agent 的一个 repo-level verification skill,用在 patch 生成后的本地验证环节;
- 中期:把 test impact map 扩成更一般的“change impact map”,把 type checks、linters、build targets、benchmark suites 一起串进去。
11. 一句话总结
这篇论文真正有价值的,不是又做了个 agent,而是把“coding agent 的回归控制”从口号变成了一个可以落地的、轻量的、可复现的工具设计。