JetBrains 进行的一项实验显示,让 AI 代理直接访问 Rider 的重构引擎,可以从根本上改变 C# 任务的执行方式,而不是让代理修改文本,然后运行编译器来发现改动破坏了什么。在一项包含 15 项任务的测试中,任务中位用时从 157.9 秒降至 26.6 秒,改善幅度达到 83%;工具调用次数也从每项任务 17 次降至 6.2 次。
这一功能包含在名为 refactoring-code 的内置技能中,自 2026.2.1 版本起在 Rider 中提供。JetBrains 表示,用户无需手动启用它;当代理被要求执行 C# 代码重构时,它会自动调用该技能。该引擎依靠 ReSharper 技术和 Rider 的分析架构,理解项目中符号与引用之间的关系。
先修改再构建方式的问题
在提供该技能之前,JetBrains 观察了一个先进模型执行相同任务的过程。在 2,513 次不同工具调用中,由于没有专用工具,代理没有直接执行任何结构化重构操作。相反,它使用交互式命令输入文本 468 次,调用 git 422 次、sed 392 次,并运行 dotnet build 163 次。
这并不意味着代理在回避重构操作,而是它试图通过搜索和文本修改来近似完成重构,然后利用构建结果评估发生了什么。JetBrains 解释说,以重命名符号为例,就需要区分与特定定义相关联的引用、多态方法调用、分部类、显式接口实现以及文档引用。这些关系无法通过简单的正则表达式可靠地保证。
相比之下,Rider 引擎运行在一棵已解析的语法树上,该语法树能够确定每个标识符所关联的定义、每次调用所调用的版本,以及整个解决方案中的引用位置。这样,任务的结构化部分就交给了引擎,而不是让代理通过反复修改、构建和读取错误的循环逐步重新发现这些关系。
JetBrains 测量了什么?
评估重点关注八项结果明确可验证的操作,分别是:
- 重命名符号及其所有引用。
- 将一组语句提取为新函数。
- 从现有类型中提取接口。
- 提取基类并将成员移入其中。
- 更改 API 签名并更新调用位置。
- 将类型移至另一个命名空间并修复 using 语句。
- 重新组织命名空间,使其与文件夹结构匹配。
- 在没有其他部分依赖某个符号时安全地删除该符号。
任务既包括直接情况,也包括更复杂的情况,其中包含更多调用位置或相互交织的依赖关系。双方运行的是同一个模型 gpt-5.5,通过 Codex CLI,每项任务大约运行十次。唯一的区别是是否提供 refactoring-code 技能。比较依据为记录的轨迹和配对 permutation 检验。
实际结果与成本
启用该技能后,dotnet build 操作次数从 163 次降至仅 3 次,评估中的工具调用总数从 2,513 次降至 926 次。文本修改并未停止,sed 仍然是使用最多的工具,但角色分配发生了变化:普通修改仍由文本编辑工具完成,而结构化变更则由引擎负责,其影响可能延伸到代理无法直接看到的部分。
第 95 百分位的用时从 346.4 秒降至 56.9 秒,这一改善尤其与先修改、再构建、再处理错误的循环中卡住的情况消失有关。每项任务的中位成本从 0.33 美元降至 0.12 美元,成功任务的成本从 0.52 美元降至 0.19 美元。每项任务输入的令牌数也从 436,745 降至 208,524,缓存读取量从 2,973,158 降至 1,257,600,输出量从 32,532 降至 15,538。
这对用户意味着什么?
这项实验表明,代理工具的价值不仅取决于模型生成代码的能力,也取决于它能够调用的工具类型。在 15 项任务中的 8 项里,配备该技能的一方更快、成本更低,并且没有使用更多工具;双方都通过了测试。基准模式下执行时间超过两分钟的 6 项任务,改善幅度在 82% 至 94% 之间。
不过,这些结果并非适用于所有情况的全面收益。双方在两项任务中都失败了,基准模式在一项任务中成功,而配备该技能的模式未成功;此外,还有四项任务本来就足够快,以至于调用 Rider 引擎在经济上并不划算。因此,这些数字证明了该技能在特定重构操作集合中的价值,但不能保证每项任务都会以同样幅度改善。
为了体现差异,JetBrains 展示了一个从 ReportExporter 类型中提取基类的任务。未使用该技能时,执行耗时 336.7 秒,进行了 24 次调用,成本为 1.15 美元,并包含多轮文件修改和构建运行,以处理继承、构造函数和访问权限错误。使用该技能后,执行耗时 19.8 秒,进行了 3 次调用,成本为 0.09 美元;代理执行了 extract_base_class 操作,创建了 ExporterBase,更新了 4 个文件,并重写了 11 个引用。
将 Rider 更新至 2026.2.1 版本、打开 C# 解决方案,然后要求代理重命名元素、提取接口或移动类型,即可试用该功能。文章建议在请求中明确指定操作名称,例如要求从 OrderProcessor 中提取接口,而不是使用“清理这个类”这样的笼统表述。