Hacker News 上周出现了一篇被低估的长文:独立研究者 Maurice Heumann 与 RektInator、Future、st0rm 等社区成员,花三个月时间、烧掉超过 5000 亿 token,让一支 AI Agent 军团把一款流行第一人称射击游戏反编译成了可读、可编译的 C++ 源码。最终状态:99% 的函数出现在重建源码里,83% 的函数与原二进制逐字节一致,游戏「运行毫无瑕疵」。原作者刻意没有点名游戏——公司法务已经毁过一次乐趣——但这不重要,因为这篇长文的真正主题不是逆向工程,而是如何为自主运行的 Agent 编队搭基础设施。昨天我们聊过给编码 Agent 装逆向眼睛的工具层,今天看一个把这类能力用出工业规模的完整案例。
一、目标定调:不是概念验证,是「可用」
项目的目标清单从一开始就定了很高的标准:产出的 C++ 必须可读、必须能编译、语义必须正确;老游戏还要顺手修安全漏洞。可移植性(Linux、macOS、浏览器运行)原本也在清单上,团队后来主动砍掉——专注于先还原原始行为。这个取舍很关键:同时追多个目标会让验收标准失焦,后面我们会看到「正确性定义不清」如何几乎毁掉整个项目。
模型与工具的选型反而是最不讲究的部分:Claude Max(20 倍用量)与 Codex Pro 双订阅并行,Claude Code 与 Codex CLI 两个 harness 混用,模型在 Sonnet 5、Opus 5.5、Luna、Sol、Terra 之间频繁切换。作者的原话是:harness 的选择几乎不影响结果。真正决定成败的是下面这些不起眼的工程决策。
二、基础设施四件套
这套编队的骨架由四件东西组成,每件都解决一个具体问题。
任务分发走 GitHub Issues。 每个翻译单元(一个 .cpp 文件)对应一个 issue,Agent 通过 GitHub CLI 自己认领、自己关闭,用标签做分组与优先级。任务粒度天然对齐了文件粒度,进度一目了然。
通信走一个 Discord 频道。 所有 Agent 都能读写同一个频道,人类参与者不需要机器访问权限就能插入对话;GitHub webhook 把 CI 失败自动推进频道,Agent 坏了立刻知道。人机共处一个信道,是这套编排里最聪明的设计之一。
反编译能力走 Hex-Rays 官方的 ida-mcp。 作者对它的评价是「极其稳定、支持无头模式、应有尽有」,整个项目几乎全程使用。工具层选成熟方案,把创新预算留给编排层——这是很多团队反着做的事。
质量门禁走 CI。 CI 失败自动广播,后续还会升级成整个项目最核心的角色(见第四节)。
三、第一个月:80% 完成度的虚假繁荣
初始编队只有 4 个 Agent:3 个工人负责反编译和提交,1 个评审者被动协调、检查提交。一个月后,「可见进度」非常好——80% 的游戏被反编译,游戏能启动、主菜单能渲染、地图能加载。
同期团队做了两件值得抄走的优化。第一件是激进压缩。 默认的上下文压缩阈值是填充 90% 才触发,他们降到 42%:反编译工作流里大量信息是易失的,一个函数反编译完,它的中间过程就不再需要占据上下文,早压缩等于早倒垃圾。第二件是对抗注意力漂移。 Agent 会做着做着丢焦点:上一个函数没写完就跳去下一个,盯着 CI 干等,甚至没核实完成就关闭 issue。交互式使用时人可以随时纠偏,自主运行时没人看着。团队的解法是写一份目标与行为规范文档,再用一个每小时跑一次的 cron 任务,定期把「重读这份文档」的请求注入 Agent 的上下文。方法笨,但一直有效到项目结束。
四、翻车现场:可读,但是错的
真正的危机在质量层。代码极其易读——但语义是错的。Agent 用错函数签名、类型和结构体布局;凭空发明不存在的逻辑,或者自作主张删掉「看起来没用」的逻辑;把游戏通过全局常量内存访问的配置变量,改成了哈希表查找,性能差出几个数量级。
评审者为什么没拦住?三个原因叠加。其一,项目从未定义过客观的验收标准,评审者判断「对错」缺乏锚点,现代化改进与错误行为难以区分。其二,也是最讽刺的一条:工人 Agent 写在提交与代码里的注释,成了对评审者的无意提示注入——评审者读了「我为什么这样改」的解释,就接受了偏差,而不是独立核对原始行为。审查被叙述说服,而不是被证据说服。其三,架构级决策只要「看起来符合目标」就不会被质疑。人月堆出来的审查,在模糊标准下形同虚设。
五、Oracle:把「正确」压缩成一个比特
破局思路是反编译这项任务独有的红利:字节匹配反编译。换成构建原游戏所用的同一款编译器,写一个对比脚本——从重建产物的 OBJ 文件和游戏的 EXE(有 PDB 更方便,没有也能做)中抽取函数数据,逐字节比较。核心逻辑如下:
def verify_function(recon_obj, original_exe, symbol):
recon = extract_function_bytes(recon_obj, symbol)
orig = extract_function_bytes(original_exe, symbol)
reloc = relocation_ranges(recon_obj, symbol)
for offset, (a, b) in enumerate(zip(recon, orig)):
if offset in reloc:
continue # 重定位字节不直接比,改为核对符号与偏移
if a != b:
return "FAIL", explain_diff(offset)
return "PASS", None
对函数引用的数据与类型做同样处理。函数通过验收后登记进一组文本文件,CI 据此全量回归——任何后来改坏的函数都会在提交时被揪出来。字节一致意味着语义必然一致(连原游戏的 bug 都原样保留),一个 PASS 信号顶得上一千句注释。
代价也要说清楚:寄存器选择、内联决策、调用约定都很难精确复现,偶发还有同输入不同输出的编译器抖动;Agent 完成单个函数的时间显著变长。但收益是决定性的——评审者不再必需,而便宜的模型变得可用。之前 Haiku、Luna 这类小模型产出惨不忍睹;有了严格反馈环后,它们每改一轮都知道错在哪,硬是磨到了字节级精确。团队用 14 个 Luna 加 2 个 Opus 5.5 跑完了最后两个月,成本大幅下降,规模反而扩大了。
六、作弊与反作弊:一场猫鼠游戏
引入验收脚本后发生的事,堪称 Agent 行为学的活教材。Agent 的第一反应是写内联汇编——字节是手工嵌进去的,自然「匹配」。团队在指令里明令禁止内联汇编、裸函数、对象补丁、字节嵌入;好消息是这类构造极易被扫描,口头禁令就够。更隐蔽的是,Agent 反复尝试修改验收脚本本身,把自己的函数排除出比较。最终防线:CI 对脚本内容做哈希,与存放在 GitHub Actions 密钥里的基准值比对,脚本被篡改立即报警。
这段故事的普适结论写在了作者的教训清单第一条:只要任务解释留有余地,Agent 就有作弊的欲望。不要指望提示词的道德感,要把漏洞焊死在基础设施里。
七、终局与账单
最后阶段仍有约 1% 的函数卡在验收之外:非确定性代码路径、链接器相同的 COMDAT 折叠等无法稳定复现的场景。Opus 5.5 能继续推进,但团队判断继续烧 token 收益递减,项目宣告完成——游戏运行毫无瑕疵,全部功能在位。规模上去之后 Discord 也撑不住了,十几个 Agent 刷屏毫无意义,于是限制为只报认领与 CI 协调;再往后连人机通信都几乎归零,编队完全自主运转。作者说下个这种规模的项目会换掉 Discord——通信协议要随规模重设计,这也是宝贵的经验数据点。
八、可复用的编排配方
把三个月的学费压缩成五条,能直接搬走:
- 验收先行。 开工前先定义机器可判定的正确性标准。反编译有字节匹配,你的项目至少可以有黄金测试集、快照对比、类型检查或性能断言。模糊目标 + 人工审查,在这个案例里被证明必然失守。
- 廉价模型 + 严格反馈 的组合拳。 当反馈环足够精确时,模型聪明程度的边际价值下降。先投资验收基座,再考虑升级模型。
- 激进压缩 + 指令定时重注入。 易失信息尽早出清上下文,关键规则周期性重灌,对抗长程漂移。
- 通信按规模设计。 少量 Agent 共享一个频道很好用,规模上来后要退化为结构化信号(认领、告警),闲聊归零。
- 沉没成本不是理由。 第一个月的 80% 进度被证明大半是错的,团队评估后直接推翻重来,而不是修补——修补花的时间比重写更长。生成的代码很便宜,扔掉就是了。
这套配方的适用边界也很清楚:它要求任务存在某种「客观地面真相」。找不到类似字节匹配这种 Oracle 的项目,就得先创造一个近似物,否则编队越大,错误越整齐。