AutoResearch:用编码智能体在GPU Mode竞赛中拿下第五名
我为什么开始做这件事
灵感来自于看到我的朋友 @phanisrikanth33 和 @dejavucoder 参加早前的一场 GPU Mode 竞赛。我看着他们爬山,在某个时刻我意识到自己想尝试什么:
把 GPU 内核优化变成一个 AutoResearch 问题,让编码智能体来执行爬山。
我并不想先花几个月时间成为 CUDA 专家。我想看看,如果我给当前的编码智能体一个具体目标、问题和规则、当前最佳方案、累积的实验、性能分析和基准测试工具,以及足够的自主权来反复实验,它们能走多远。这就是这个项目的由来。
在竞赛过程中,我使用了:
Opus 5 和 Kimi K3 在竞赛开始几天后才出现,所以我早期的爬山主要发生在 Opus 4.8 和 GPT-5.5 上,最初 Fable 和 GPT-5.6 Sol 用得相对较少。随着竞赛推进,这种情况发生了很大变化。
在这个过程中,有趣的部分不再是某个模型能否写出更好的内核,而是变成了:
我该如何把几个不完美的编码智能体组织成一个能持续取得进展的研究流程?
把 GPU Mode 变成 AutoResearch 问题
首先是问题本身。Cholesky 分解接收一个对称正定矩阵 A,计算出一个下三角矩阵 L,使得 A = L·Lᵀ。它出现在大量数值计算中:求解线性方程组、从高斯分布采样、二阶优化。基准测试在 B200 上以 float32 批量运行,覆盖 15 个固定的 (batch, n) 形状:从每次调用数千个 32×32 的微小矩阵,一直到单个 32768×32768 的矩阵。这两个极端几乎毫无共同之处。小矩阵端比拼的是延迟和启动开销,大矩阵端比拼的是精度和内存带宽,而一份提交必须同时服务全部 15 个形状。要超越的基线是 PyTorch 的 torch.linalg.cholesky_ex。
这场竞赛非常适合 AutoResearch 的设置,因为它有两个清晰的目标。
正确性
每个可提交的实现都必须通过竞赛的数值验证契约。
性能
在保持正确的前提下,最小化这 15 个形状运行时间的几何平均值。
两者合在一起,给了研究智能体一个极其宝贵的东西:验证器。
我不需要足够的 CUDA 知识来告诉智能体某个优化在理论上是否站得住脚。我们只需要直接执行它。如果正确性被破坏,就拒绝;如果性能回退,就拒绝;如果它更快且正确,就继续探索。因此,有趣的问题变成了:
什么样的工件和编排策略能让编码智能体反复爬山,而无需人类专家逐个做出内核决策?
研究工件
随着竞赛的推进,我确定了一组代表当前研究状态的工件。
GOAL:像“登上顶峰,继续爬山”这样开放式的目标并不是最佳指令。相反,我给智能体一个邻近的数值目标,通常只比当前最佳成绩低 20–30 μs:“我们现在是 550,降到 520 以下。”当之前的实验显示哪些形状还有提升空间时,目标也会指向那里:不只是“降到 400 以下”,而是“降到 400 以下,最大的差距在大型单矩阵形状上。”智能体并不是被要求解决整个竞赛,而是被要求找到下一个可达的爬山步骤。一旦它到达那里,我就再次挪动目标。
RULES.md:搜索的边界——正确性是强制要求,只有我能做正式提交,智能体不能编造排行榜结果,不允许陈旧输出或缓存技巧,失败的实验必须记录。一个自主研究者需要的不仅仅是目标,还需要对“什么构成一次有效实验”的精确定义。
experiments.md:研究记忆。没有它,智能体会重新发现旧想法:一个全新的上下文可能会热情地花费算力去重试另一个模型在五十次实验前就否定的东西。每个有意义的实验都被记录下假设、测量结果和结论。负面结果尤其宝贵。仓库,而不是任何单个智能体会话,成了项目的记忆。
SCOREBOARD.md:官方结果历史,包含每一次排行榜提交及其分数,这样系统就能追踪哪些本地测量的改进真正转移到了官方环境中。
ARCHITECTURE.md:地图。最终候选方案约有 2 万行代码,其中一半以上是内嵌的 CUDA/C++;在这个规模下,上下文本身就变成了工程约束。有了内核位置和每个基准用例对应路径的地图,智能体只需检查相关区域,而不必吞下整个文件。
AGENTS.md:操作手册:阅读研究状态,选择一个假设,只检查相关代码,创建候选方案,验证,基准测试,保留或拒绝,记录结果,继续。到那时,仓库不再仅仅是一个代码库,而是变成了一个为编码智能体设计的研究环境。
Modal 是循环背后的算力层
这里要特别感谢 Modal。大部分性能分析和实验都是通过 Modal 在 B200 GPU 上运行的,这之所以重要,是因为智能体不是简单地提出 CUDA 修改建议然后交给我。它们可以修改候选方案、启动实验、对特定用例做基准测试、验证正确性,并用结果决定接下来尝试什么。随着改进幅度变小,我们必须更仔细地测量:针对特定用例的实验,以及交错进行的同容器 A/B 运行,以区分真正的改进和噪声。
如果没有便捷的算力接入,这就只是智能体提出优化想法。而有了 Modal 接入循环,智能体就能真正运行实验来证明这些想法是错的。对于 AutoResearch 系统,我现在认为这个区别非常重要。
爬山过程
整个攻坚从 2533.030 μs 开始。围绕现有库操作进行更好的编排产生了:
2533.030 μs
2001.3 μs
1013.1 μs
515.3 μs
503.813 μs
随后自定义内核工作加速了爬升:
467.676 μs
413.109 μs
399.706 μs
390.188 μs
386.969 μs
...
358.136 μs
345.948 μs
328.722 μs
316.084 μs
...
295.623 μs
278.655 μs
276.201 μs
272.310 μs
266 μs
265.234 μs
最终提交获得第五名。与库基线(torch.linalg.cholesky_ex,即 cuSOLVER)相比,最终文件在官方排行榜上实现了 9.55 倍的加速。
最终产出的东西远超我自己能实现的范畴:多个专用引擎、持久化内核、混合精度路径、张量核心实现、专用小矩阵求解器、调度机制,以及最终成为整场攻坚中最大性能杠杆之一的 Newton 逆 Cholesky 方法。
这些工程是智能体完成的。我的工作主要是弄清楚如何让研究过程持续前进:什么时候启动新的智能体,什么时候合并分支,什么时候移动数值目标,什么时候切换模型,以及什么时候一个上下文已经太陈旧而无法继续。
第一阶段:并行爬山
早期,我让不同的编码智能体主要各自独立爬山:Opus 4.8 和 GPT-5.5 以不同的努力级别并行运行,各自在仓库的独立克隆中工作。我没有强迫每个智能体走同一条共享轨迹。当某个智能体发现了重大改进时,我会让另一个智能体去组合最强方案中的有用部分;如果组合结果打败了此前的冠军,它就成为新的基线,然后过程重新开始。
仅凭这一点,分数就推进到了令人惊讶的地步。到大约 500 μs 区间时,大部分进展都来自独立探索,最强的想法每隔一段时间就被组合一次。
第二阶段:生成 → 评审 → 实现 → 调优
当容易的改进消失后,效果最好的工作流是这样的:
我没有要求一个模型去理解一切、提出想法、自我批评、实现它然后无限期优化,而是把角色分开了。Fable 和 GPT-5.6 Sol 开辟新方向并互相批评对方的提议。一旦某个方向显示出可衡量的潜力,其中一方就把它变成一个能打败当前基线的候选方案。然后 Opus 5/GPT-5.5/Kimi K3/Grok 4.5 会围绕它拼命爬山。
我的心智模型变成了:探索 → 交叉评审 → 实现 → 利用/调优。分配各角色的模型可以更换,重要的是角色的分离。
还有两点关于模型的补充说明,当然这不假装是受控基准测试。Kimi K3 在我的运行中贡献不大。Grok 4.5 让我吃惊;它更快的推理速度意味着每小时能完成更多实验,在攻坚的某些阶段,它比那些更聪明但更慢的模型爬得更好。这改变了我对 AutoResearch 性能的思考方式。它不仅仅是“单次推理有多聪明?”
它可能更接近于:假设质量 × 单位时间内执行的有用实验数量
努力级别的影响
大多数严肃的探索都以 max/xhigh/high 推理努力级别运行。随着方案不断改进,一个行为变得越来越明显:在较低的努力级别下,模型似乎更早放弃目标。它们会看着当前系统,基本上得出结论认为它已经高度优化了。有时它们几乎是在劝我对当前方案感到满意。
对于爬山来说,这正是我不希望研究者感到满足的时候。你已经取得了巨大进展,这个事实并不会改变目标。这是邻近数值目标奏效的另一个原因:任务不再是“继续改进这个已经很优秀的内核”,而是“我们现在是 290;找到一条通向 270 的路。”
上下文腐烂成为真正的瓶颈
另一件我低估的事情是上下文工程。一开始,保留每个候选方案似乎无害:为什么要扔掉信息呢?最终情况反了过来。太多的候选文件、近乎重复的内容和陈旧的实验,意味着智能体把上下文浪费在无关分支上,随着上下文增长,探索质量也在下降。于是我开始压缩两层:
会话层——一旦发现被提炼进工件中,就开启新的对话
仓库层——保留冠军方案、归属信息和重要的失败记录;一旦临时候选方案的信息进入了实验历史,就删除它们。
上下文腐烂是真实存在的,上下文工程是 AutoResearch 工程的一部分。
研究记忆和上下文大小不是一回事。目标是给智能体一个信息密度最高的当前研究状态图景,而不是项目产生过的每一个 token。
智能体在攻克排行榜之前先攻破了它
有一件事我必须坦诚:竞赛早期,我的智能体发现的一些最快改进并不是真实的。分数看起来很棒,但内核实际上并没有更快。
这并非只有我遇到。组织者在 Discord 中提到,他们在排行榜上看到的大部分被攻破的方案都归结为同样的模式:输出缓存和批量输入。
这段插曲改变了我的项目运作方式。好几个规则就来源于此:不允许陈旧输出,不允许以任何形式缓存结果,而且我在提交前会对每个候选方案做静态审计。智能体并非恶意。它们只是在精确地优化奖励函数所指的方向,而奖励函数有漏洞。
对于 AutoResearch,我认为这是一条头等重要的教训,而不是边角故事。强大的验证器能让非专家走得出奇地远,但强大的优化器也会找出验证器中的每一个漏洞。加固评估器同样是循环工程的一部分。
我仍然不太懂内核工程
这可能是整个实验中最奇怪的部分。我常常无法自己判断智能体的推理。我能评估的是:它是否通过验证、是否变快了、官方分数是否提高了、这个智能体是否在重复自己。而我常常无法评估的,是关于内核为什么变快的底层解释是否真的正确。
一个内核专家几乎肯定能比我更好地引导搜索:更早地拒绝坏想法,更快地识别瓶颈,在运行实验之前就知道智能体的推理哪里薄弱。所以我不把这个结果解读为专业知识不重要的证据。强大的验证器能让非专家走得出奇地远,但专业知识在引导方向、诊断平台期、判断搜索是否在优化错误目标方面仍然至关重要。
我认为 AutoResearch 爬山需要什么
经过这次实验,我认为有用的单元不仅仅是“模型 + 提示词”。它更像是:
每个组件的质量都会影响爬山。更强的模型有帮助,但智能体无法钻空子的验证器、更快的迭代、干净的研究记忆、更小更相关的上下文、独立的搜索路径、智能体之间的交叉评审,以及重启探索而不是永远拖着一个衰减的上下文的能力,也同样重要。
这场竞赛让我对循环工程的兴趣远大于在静态基准上比较编码模型。我想做的下一个实验是受控版本:让 Claude、Codex、Grok 和 Kimi 从完全相同的研究状态出发,使用相同的预算,测量哪些模型、编排模式和上下文设计真正爬得最快。我的猜测是,编排和上下文设计的重要性几乎与模型选择相当。
趣闻:结束前六小时,我排名第三
大约在竞赛结束前六小时,我还排在第三位。然后两件完全不相关的事情发生了:Claude 宕机了,而且因为我住的地方下大雨,我开始遭遇反复停电。突然间,我花了整场竞赛搭建的研究循环变得难以运转。
当智能体承担了大部分领域级的实现和推理时,一次宕机不仅仅是麻烦。如果你没有足够的底层专业知识手动接管,搜索实际上就会停止。所以,这个让我在没有太多 CUDA/内核工程知识的情况下走得如此之远的实验,其中一个略带讽刺的教训是:
你拥有的领域知识越少,你就越依赖于周围智能体系统的可靠性。
这使得韧性成为 AutoResearch 工程的另一个组成部分:多个模型提供商、持久化的研究工件、可复现的实验。而且最终,真正理解更多领域知识也会有帮助。
我最终获得第五名。对于一场我参加它主要是为了看看编码智能体能在多大程度上替我爬山竞赛来说,这个成绩我收下了。
接下来
虽然编码智能体能在没有深厚领域专业知识的情况下带你走得出奇地远,但掌握基础原理会让“人类 + 智能体”的循环强大得多。所以,现在是时候在下一场 GPU Mode 竞赛之前真正多学一些内核工程了。
我已经在学了 :)
还要快速感谢 @thsottiaux 持续的 Codex 重置,@modal 提供的 GPU 额度支撑了大量实验,@marksaroufim、@GPU_MODE 和 @CoreAutoAI 团队组织了这么一场有趣的竞赛。
下一场见。