JetBrains 将类似 Maven 的构建工具决策逻辑编译为固定权重矩阵,并用它构建真实 Java 库,产物与 Apache Maven 逐字节一致。包含测试的干净构建耗时约为相同 Python 逻辑的 1.49 倍。
借助众多 JetBrains 产品内置的 AI 功能,让你的工具更强大。
我们试了,而且它真能构建。我们把一个 Maven 风格构建工具中负责决策的部分编译成固定的权重矩阵,并用它构建了一个真实的 Java 库。输出结果与 Apache Maven 逐字节一致。开启测试的完整清理构建,耗时是同一套逻辑用 Python 实现时的 1.49 倍(95% 区间为 1.4~1.54 倍,共进行了 30 组配对运行)。
网络的权重表,每根柱子对应一行。柱子的高度表示这一行在一次 ZXing 构建中执行的次数;亮点则逐步回放这次构建中真实发生的执行片段。
每个 Java 开发者都见过 Maven「思考」的样子。它解析依赖、编译、测试、打包。大部分时间花在 javac 和 JUnit 上,但贯穿整个过程的,还有一小段繁琐的逻辑,负责决定接下来做什么。两个模块对同一个库的版本意见不一致时,最终选哪个版本。哪些文件交给编译器,以及使用哪些参数。测试失败时,是否停止构建。
我们想知道,这部分逻辑能不能放进神经网络。我们要的不是一个经过训练、用来猜测 Maven 通常下一步会做什么的模型:这种模型大多数时候是对的,而一个大多数时候正确的构建工具,就是坏掉的构建工具。我们想把决策逻辑本身编译进权重,让网络要么给出完全正确的答案,要么拒绝回答。
本文顶部的视频展示的就是这个网络。26,626 根柱子,每根都是权重表中的一行,使用的是 nmvn-exact-v2 模型,后面降低成本的部分会介绍它。柱子的高度表示这一行在一次 ZXing 构建中执行的次数,亮点则逐步回放这次构建中真实发生的执行片段。决定构建如何进行的一切,都在里面。
这个技巧来自 Handwired:你可以先写一个程序,再设置网络的权重,让这个网络本身就成为那个程序。我们用 Python 的一个受限子集写了一个兼容 Maven 的小型构建控制器,再把它编译成一张包含 29,625 × 7 个数字的表,以及 7,382 个存放常量的单元。这是第一个版本 v1;后文会把这张表缩减到上面看到的 26,626 行。没有训练,没有梯度下降:优化器执行了零步。
运行过程是一个循环。每一步,网络通过两次矩阵乘法查出表中的一行,在一小组寄存器上执行这一行编码的运算,再把结果写回内存。网络不能打开文件,也不能启动 javac。需要这些操作时,它会把请求写入内存中的固定槽位,由一层很薄的桥接层把请求交给外部执行,再把原始结果写回来。桥接层从不做决策。所有选择都来自权重。
测试对象是条形码库 ZXing 3.5.3:构建范围包括它的父项目,以及 core 和 javase 模块,执行 clean package,并开启测试。
能。它生成的 JAR 与 Apache Maven 的输出逐字节一致,而且构建是在沙箱中完成的:其中无法运行 Maven,网络连接已关闭,也无法读取那份 Python 版决策逻辑或 Maven 自己的输出。上游的 549 个测试全部执行并通过,刚构建出来的 JAR 在 27 次二维码编码和解码检查中也全部正确。
有两项检查让我们确信,工作确实发生在权重里。完成一次完整构建时,网络的 CPU 和 GPU 版本按完全相同的顺序发出了完全相同的 1,731 个请求。而当我们删除或破坏权重文件后,构建根本无法进行。
由刚构建出来的 ZXing JAR 写出的二维码,再由这些 JAR 读取回来。
左侧:普通 Python 实现的控制器。右侧:同一个控制器的神经网络版本。两者在同一台 Mac 上先后实际构建 ZXing,并以真实速度回放。
两侧各录制了一次构建,并实时回放。时间线中的灰色部分,表示花在 javac、JUnit、bnd 和 zip 上的时间。
在我们测量的所有场景中,它都更慢。Python 直接执行控制器逻辑;而网络每执行一条微小的指令,都要完成一整轮矩阵运算。一次 ZXing 构建需要执行 4,796,191 条这样的指令。
| 测量内容 | 普通 Python | 神经网络,CPU | 神经网络,GPU(Metal) | Apache Maven 3.9.11 | 神经网络 CPU / Python |
|---|---|---|---|---|---|
| 仅决策,不执行 Java 工作 | 146.54 ms | 4.15 s | 27.97 s | – | 28.3× |
| 从冷启动到首次决策 | 385.94 ms | 4.80 s | 28.25 s | – | 12.4× |
| 开启测试的完整清理构建 | 15.52 s | 23.12 s | 70.11 s | 15.29 s | 1.49× |
| 遇到编译错误后停止的构建 | 1.74 s | 4.41 s | 22.19 s | 2.81 s | 2.53× |
实际耗时的中位数,越低越好。最后一列:神经网络 CPU 耗时除以 Python 耗时。
开启测试的完整清理构建,单位为秒,取中位数。柱形表示中位数;误差线表示 p25 到 p75。
只看决策部分,网络远远落后。但放到一次完整构建中,差距会缩小到大约 1.5 倍,因为大部分时间花在 javac 和 JUnit 上,而两个版本使用的是同一套工具。把网络放到 GPU 上运行,情况反而更糟。一个逐条执行指令的程序,没什么工作可以交给 GPU 并行处理。
所以,这不是一个更快的构建工具。它是一个能以 tensor 形式拿在手里的构建工具。
所以,这不是一个更快的构建工具。它是一个能以 tensor 形式拿在手里的构建工具。
耗时大致等于步数乘以每步成本,于是我们统计了这些步数都花在了哪里。我们在指令级模拟器中回放了一次完整的 ZXing 构建,并统计它执行的每一条指令。在 v1 的 4,796,191 步中,有 652,773 步(14%)只是把一个值从一个寄存器复制到另一个寄存器;另有 553,333 步(12%)用于加载常量,或跳回循环顶部。这些都是编译器生成的衔接指令,不是构建逻辑。
于是,我们没有动网络,而是修改了编译器。现在,结果会直接写入需要它的寄存器,常量嵌入使用它的指令中,条件直接编译为分支,循环则在底部检查条件,不必跳回顶部。执行机器、单步计算图和 GPU kernel 都没有变化。变的只有那张数字表,而且它变小了。
一次完整 ZXing 构建中的每一步,按指令类别统计。指令集模拟器回放录制好的构建;两个模型的操作序列完全一致。
| 测量内容 | 神经网络 CPU,v1 | 神经网络 CPU,v2 | Python(v2 测量时) | v1 / Python | v2 / Python(95% CI) |
|---|---|---|---|---|---|
| 仅决策,不执行 Java 工作 | 4.15 s | 3.40 s | 140.81 ms | 28.3× | 24.2×(23.6×~25.2×) |
| 从冷启动到首次决策 | 4.80 s | 4.14 s | 402.38 ms | 12.4× | 10.3×(10.1×~10.7×) |
| 开启测试的完整清理构建 | 23.12 s | 21.09 s | 14.83 s | 1.49× | 1.42×(1.40×~1.43×) |
| 遇到编译错误后停止的构建 | 4.41 s | 3.70 s | 1.78 s | 2.53× | 2.08×(2.05×~2.11×) |
神经网络 CPU 的 v1 与 v2 对比,取实际耗时的中位数。比值来自同一会话中的配对运行,为神经网络耗时 / Python 耗时;v1 来自 nmvn-bench-v0,v2 来自后续测量方案。
GPU 的收益更大,因为它的成本几乎完全取决于步数:仅决策部分,v1 耗时 27.97 s,v2 耗时 21.61 s,减少了 23%,接近步数的降幅。不过,它仍然是最慢的执行引擎。
我们用新权重重新跑了一遍完整的正确性验证链:所有测试套件、必须生成逐字节一致 JAR 的沙箱构建、负对照,以及 CPU/GPU 回放。削减确实有效,但在真正重要的整体耗时上,收益有限。Java 工具仍然占据完整构建的大部分时间,所以缩减网络这一部分,对端到端耗时的改善没有步数降幅看起来那么大。
为了检查编译器是否暗中针对某一个程序做了调优,我们给它换了一个毫不相关的任务:迷宫求解器,使用广度优先搜索,代码位于 programs/maze/maze_nml.py。它编译后得到 279 行权重。一个小应用会同时启动传统版本和神经网络版本,让它们作为独立进程运行,每当某个程序报告一个格子,就立即把它画出来。
应用窗口的录屏。应用在运行时会自行保存画面帧;神经网络一侧使用 CPU。GPU 版本耗时更长。
在 3 个不同的 171×121 迷宫上,普通 Python、Python 调试垫片、神经网络 CPU 版本和神经网络 GPU 版本生成了完全一致的事件序列:访问的每个格子、访问顺序,以及距离,全都相同。第一个迷宫用了 2,572,288 个网络步骤。神经网络 CPU 耗时是普通 Python 的 161~168 倍,GPU 则是 791~812 倍。这些数据来自各程序自身的计时器,每个迷宫只运行一次,因此只能视为粗略结果。
问得合理。我们也问过自己。如果你想要更快的构建,答案是没用:继续用 Maven。换一块更大的 GPU 也救不了它,因为网络一次只运行一条指令,而构建控制器里没有什么能分摊到数千个核心上执行的工作。
不过,速度从来都不是最有意思的地方。我们最终得到的是一个每个权重都有明确含义的神经网络,而这很少见。下面是我们认为它有价值的方向,按我们最有把握的程度排序。
可解释性研究试图仅凭权重,弄清一个网络在做什么。难点在于怎么给答案打分,因为对于训练出来的模型,没有人知道真实答案。这个网络自带真实答案。除了一个七行的启动桩,26,626 行权重中的每一行,都属于控制器 249 个已知函数中的某一个,而且我们知道每个函数做什么。因此,我们可以直接指出 do_surefire 对应的那些行:这部分负责运行测试,并决定测试失败是否停止构建。如果某个工具声称找到了「依赖解析电路」,你就能核查。手工构造的测试网络通常都是小玩具。这个网络则会在真实代码库上完成实际工作,我们认为这是它最好的用途。
一个有 99% 概率选对库版本的模型,每一百次构建就会错一次。把权重编译出来,就能让一个精确的子程序与学习得到的模型拥有相同的形式:相同的 tensor、相同的 runtime、相同的硬件。说清楚一点,我们还没有把它接到一个训练好的模型上。那是下一个实验,还不是已有成果。即便如此,这仍然是最让我们兴奋的方向。
整套策略就是一个装着数字的文件。把它从 CPU 移到 GPU,它会按相同顺序做出相同的 1,731 次决策;给它一份录制好的构建,它就能精确回放。这也是我们在降低成本那一节中,逐项比较 v1 和 v2 决策的方法。你可以对它做 diff、签名,并证明某次构建来自它。公平地说,普通代码配合 lockfile,已经能做到其中大部分。tensor 版本额外带来的是:无论你把什么程序编译进去,执行策略的始终是同一个由查表和算术组成的固定循环,而 runtime 越小,需要信任的东西就越少。
它还有一个优点:当决策只是重型工具之上的薄薄一层时,额外成本会缩小。在一次完整的 ZXing 构建中,javac 和 JUnit 完成了大部分工作,所以网络的耗时约为 Python 的 1.5 倍,而不是二十多倍。围绕高成本步骤编写的衔接逻辑,也只需付出类似的适度代价。
更快的构建,或任何对毫秒级延迟敏感的场景
给这个问题堆更多 GPU 算力
用一个有标准答案的网络测试可解释性工具
让精确子程序与学习得到的模型共存
可以计算哈希、回放和比较的决策逻辑
这并不是完整的 Maven。它只处理一个真实项目的一种构建,也就是 clean package,并跳过了 Enforcer、Checkstyle、Javadoc 和 source JAR。签名、部署,以及所有只在 verify 阶段运行的内容,都不在范围内。插件也不是神经网络:javac、JUnit、bnd 和 zip 都作为普通程序运行。
这不是机器学习。权重是从程序编译出来的,不是学习出来的。我们没有尝试训练一个版本,而且预计它会在没见过的项目上失败。
权重、runtime 和完整结果已发布到 Hugging Face 的 shirman/neural-maven:包括方法、全部 11 个场景、内存占用、运行环境和证据哈希,以及原始测量数据、两套测量方案、分析代码和校验和。这些内容足以重新计算本文中的每一个数字。
谢谢,我们已经收到!
在测试过的所有模型、Agent 和任务类型中,JetBrains IDE 的智能能力总体上都提高了编程 Agent 的效率,通常提升 20%~30%,最好情况下约为 65%,具体取决于模型、Agent 和任务。
Mellum2.1 在真实环境中通过强化学习训练,专为编程 Agent 和在你自己的硬件上运行的快速子 Agent 打造。
上周,我们分享了对 Agent 开发未来方向的看法,并介绍了 JetBrains 给出的答案:JetBrains Air,一个面向 Agent 开发的开放产品系统。今天,我们启动了 Air 在 JetBrains IDE 中的 Early Access Program。它现在已经以插件形式提供……
第 1 部分:解析、分块与向量化。前段时间,我们着手打造力所能及的最佳语义代码搜索平台:一条 RAG 流水线,为 LLM Agent 提供来自真实仓库、准确且可引用的证据,而不是 grep 碰巧搜出来的内容。最终的解决方案就是 Air Context。……