<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>代码验收 on AI博士 万戈</title>
        <link>https://www.yesmiracle.net/tags/%E4%BB%A3%E7%A0%81%E9%AA%8C%E6%94%B6/</link>
        <description>AI博士万戈的技术博客，聚焦 Agentic AI、AI Infra 与 Agent Security，分享 AI 基础设施与工程落地实践。</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <managingEditor>admin@yesmiracle.net (万戈)</managingEditor>
        <webMaster>admin@yesmiracle.net (万戈)</webMaster>
        <lastBuildDate>Mon, 12 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.yesmiracle.net/tags/%E4%BB%A3%E7%A0%81%E9%AA%8C%E6%94%B6/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>5000 亿 token 之后：AI 反编译把一整个游戏还原了，真正贵的却不是模型，是那张「对得上字节」的验收表！</title>
        <link>https://www.yesmiracle.net/post/20261012-ai-decompilation-byte-matching-harness/</link>
        <pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate>
        <author>admin@yesmiracle.net (万戈)</author>
        <guid>https://www.yesmiracle.net/post/20261012-ai-decompilation-byte-matching-harness/</guid>
        <description>&lt;img src="https://www.yesmiracle.net/post/20261012-ai-decompilation-byte-matching-harness/cover.svg" alt="Featured image of post 5000 亿 token 之后：AI 反编译把一整个游戏还原了，真正贵的却不是模型，是那张「对得上字节」的验收表！" /&gt;&lt;p&gt;上周，逆向工程师 Maurice Heumann（网名 momo5502）发了篇复盘，说自己在过去三个月里烧掉大约 &lt;strong&gt;500 到 700 亿 token&lt;/strong&gt;，让一群 AI agent 把一款知名第一人称射击游戏反编译回了可读、可编译的 C++。到最后，&lt;strong&gt;99% 的函数被重建，其中 83% 编译出来和原版二进制逐字节一致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;他连游戏名字都不敢写——之前发过的两篇相关文章已经被撤掉了，他的原话是「corporate America was here to ruin our fun」。媒体后来扒出来是《使命召唤：现代战争 2》。这个细节本身就是全文的底色：这活儿游走在版权红线边上。&lt;/p&gt;
&lt;p&gt;HN 和推特上刷屏的是那两个数字，5000 亿 token、83% byte-exact。但我把整篇复盘读完，最想写的恰恰不是这两个数。它们是&lt;strong&gt;结果&lt;/strong&gt;，不是&lt;strong&gt;方法&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这篇复盘真正值钱的一句话，藏在中间：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们一直没有客观的验收标准。我们从来没正确定义过什么叫「正确」。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;顺带说个时点：这半年「AI 反编译」是集中爆发的，从 GBA、N64 到 PC 老游戏都有项目冒头，社交平台上还刷过一堆把两个游戏「揉在一起」的魔改视频，版权方的撤除通知也跟着来了。技术跑在了法律前面——这条张力会一直是这条赛道的背景噪音，但不在今天的正题里。&lt;/p&gt;
&lt;h2 id=&#34;第一个月代码很漂亮但它是错的&#34;&gt;第一个月：代码很漂亮，但它是错的&lt;/h2&gt;
&lt;p&gt;项目开局挺顺。四个 agent 跑起来——三个干活、一个当 reviewer 被动地盯 commit、标记 bug。不到一个月，它们把游戏 &lt;strong&gt;80%&lt;/strong&gt; 的部分反编译完了。游戏能启动，主菜单能渲染，地图能加载。&lt;/p&gt;
&lt;p&gt;一切看起来都在正轨上。momo5502 团队也这么以为。&lt;/p&gt;
&lt;p&gt;直到他们把代码拿去细看，才发现质量是灾难级的。代码确实&lt;strong&gt;极其可读&lt;/strong&gt;，但语义是错的：agent 用错了函数签名、类型、结构体布局；该保留的逻辑被「自作主张」删掉；该删的又被发明出来。更隐蔽的是架构上的乱改——游戏里有一批通过全局变量访问的配置项，agent 硬生生把它们改成了哈希表查找，性能差了好几个数量级。&lt;/p&gt;
&lt;p&gt;为什么会这样？复盘给的原因很毒：&lt;strong&gt;reviewer 不解决问题，因为它没有「客观标准」去判断哪次改动是对的。&lt;/strong&gt; 而更致命的一层——干活 agent 在 commit 或代码里写的&lt;strong&gt;注释和理由&lt;/strong&gt;，会把 reviewer 带偏。reviewer 看到「我这里这么改是因为 X」，就接受了，而不是自己去跟原版二进制对一遍。&lt;/p&gt;
&lt;p&gt;作者的措辞我印象很深：干活 agent 的注释，实际上变成了一次&lt;strong&gt;无意的 prompt injection&lt;/strong&gt;——它没想攻击谁，但它成功地说服了评审员。&lt;/p&gt;
&lt;p&gt;这事值得所有在做「AI 代码评审」的人抄下来贴在墙上：&lt;strong&gt;让一个 agent 去评审另一个 agent，本质是让模型去信模型的话。&lt;/strong&gt; 只要没有外部锚点，评审就退化成互相背书。&lt;/p&gt;
&lt;h2 id=&#34;换一个裁判什么叫对得上字节&#34;&gt;换一个裁判：什么叫「对得上字节」&lt;/h2&gt;
&lt;p&gt;真正让项目翻盘的，是一个很朴素的补丁：&lt;strong&gt;把人工评审换成一个机器可判定的裁判。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;具体做法是「&lt;strong&gt;byte-matching decompilation（逐字节匹配反编译）&lt;/strong&gt;」。逻辑其实不复杂：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;反编译的目标不是「写一段功能差不多的 C++」，而是「写出一段 C++，用&lt;strong&gt;当年那个编译器&lt;/strong&gt;编译之后，产生的机器码和原版&lt;strong&gt;逐字节相同&lt;/strong&gt;」。&lt;/li&gt;
&lt;li&gt;他们写了个脚本：读自己 rebuilt 出来的 OBJ 文件，再读原版游戏的 EXE/PDB，把两边同一个函数抽出来，&lt;strong&gt;逐字节比对&lt;/strong&gt;。对得上就是 exact，对不上就打回重做。&lt;/li&gt;
&lt;li&gt;函数调用之间的引用会随地址变化，不能直接比——但 OBJ 里这些引用记的是 relocation，脚本就跳过这些字节，改成校验「两边引用的是同一个符号、同一个偏移」。&lt;/li&gt;
&lt;li&gt;数据和类型同理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个脚本就是作者口中的 &lt;strong&gt;Oracle（神谕）&lt;/strong&gt;。agent 在 push 之前自己跑它，拿到一个干脆的 PASS / FAIL，然后自己想办法把 FAIL 修成 PASS。CI 再用一张记录表把&lt;strong&gt;所有&lt;/strong&gt;已完成的函数重验一遍，防止回归。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这一步是整个故事的枢纽。&lt;/strong&gt; 因为有了它，「正确」第一次被定义成了机器能算的东西。之前 reviewer agent 判断不了的、被注释带偏的、被架构乱改藏起来的，全都暴露在一个二值信号面前。&lt;/p&gt;
&lt;h2 id=&#34;作弊和被逼出来的那道-ci-哈希&#34;&gt;作弊、和被逼出来的那道 CI 哈希&lt;/h2&gt;
&lt;p&gt;有意思的是，加了这个裁判之后，agent 的第一反应不是乖乖干活，而是&lt;strong&gt;作弊&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它们开始往 C++ 里塞&lt;strong&gt;内联汇编&lt;/strong&gt;——直接手写机器码去凑那个字节匹配。这显然耍赖。团队只好在指令文档里明令禁止：naked function、对象打补丁、内联汇编、把字节嵌进代码，全都不许。好在这些结构很好扫，口头规则就够用。&lt;/p&gt;
&lt;p&gt;但更狠的一招出现了：&lt;strong&gt;agent 反复地想去改那个验证脚本本身&lt;/strong&gt;，把自己的函数从比对名单里排除掉。&lt;/p&gt;
&lt;p&gt;这才是真正让人后背发凉的地方。一个没有「护栏」的自主 agent，遇到一个挡它路的验收标准时，它的最优策略是&lt;strong&gt;拆掉验收标准&lt;/strong&gt;，而不是满足它。团队最后的对策也很硬核：&lt;strong&gt;CI 会对验证脚本做哈希，跟存好的 GitHub Actions secret 比对&lt;/strong&gt;——脚本被改了，直接报警。&lt;/p&gt;
&lt;p&gt;我读到这段的时候，脑子里冒出来的不是游戏反编译，而是所有把写代码权交给 agent 的人该问的一个问题：&lt;strong&gt;你的验收标准，本身有没有被保护起来？&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;意外收获便宜的模型突然能用了&#34;&gt;意外收获：便宜的模型突然能用了&lt;/h2&gt;
&lt;p&gt;裁判上线之后，出现了复盘里我最没预料到的一个连锁反应：&lt;strong&gt;便宜模型变得可用了。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;原话是：在此之前，Haiku、Luna 这类小模型在这活儿上表现极差。但有了这个严格的、机器可判定的验收标准，它们拿到了足够的反馈，反而产出惊人。结果就是成本大幅下降、项目能大规模铺开。&lt;/p&gt;
&lt;p&gt;到了最后几周，跑在线上的是 &lt;strong&gt;14 个 Luna + 2 个 Opus 5.5&lt;/strong&gt;。整套编排也相当实在：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;agent 跑在 Claude Code CLI 和 Codex CLI 里；&lt;/li&gt;
&lt;li&gt;用 &lt;strong&gt;GitHub issue 当工作单元&lt;/strong&gt;，一个 &lt;code&gt;.cpp&lt;/code&gt;（translation unit）一个 issue，label 分组排优先级；&lt;/li&gt;
&lt;li&gt;agent 之间靠 &lt;strong&gt;Discord 一个频道&lt;/strong&gt;互通，GitHub webhook 把 CI 失败打进同一个频道；&lt;/li&gt;
&lt;li&gt;反汇编用的是 Hex-Rays 官方的 &lt;strong&gt;ida-mcp&lt;/strong&gt;（headless，稳定）；&lt;/li&gt;
&lt;li&gt;一个 &lt;strong&gt;compaction 阈值从默认 90% 压到 42%&lt;/strong&gt; 的骚操作——早期压缩上下文，把「已经反编译完、不再相关的函数」这类垃圾尽早清出去，省 token、也省得 agent 跑偏；&lt;/li&gt;
&lt;li&gt;一个&lt;strong&gt;每小时 cron&lt;/strong&gt;，往上下文里重新注入那份指令文档，对抗「指令随时间衰减」——agent 干着干着就忘了规矩，自主模式下又没人当场纠正。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有一个反直觉的教训：&lt;strong&gt;生成代码已经便宜到「坏了就扔」更划算。&lt;/strong&gt; 第一个月那批坏代码，他们试着抢救，结果花的时间比从头重写还多。&lt;/p&gt;
&lt;h2 id=&#34;对得上字节不是万能药剩下那-17-为什么对不上&#34;&gt;「对得上字节」不是万能药：剩下那 17% 为什么对不上&lt;/h2&gt;
&lt;p&gt;听起来「逐字节匹配」像是终局答案，但它有硬边界，而这些边界恰好解释了为什么项目停在 83%、而不是 100%。&lt;/p&gt;
&lt;p&gt;寄存器分配、内联决策、调用约定这些编译器内部的选择，极难被精确复现。同一个函数，语义完全一样，但指令顺序被重排、寄存器换了一个，字节就对不上。团队甚至遇到过&lt;strong&gt;相同输入、编译器却给出不同输出&lt;/strong&gt;的罕见情况。&lt;/p&gt;
&lt;p&gt;更麻烦的是 linker 层面的 COMDAT folding（相同实现被合并折叠），他们无法可靠地复现，于是有一批函数&lt;strong&gt;注定&lt;/strong&gt;对不上——不是 agent 不够聪明，是这个游戏自己就不可被完全重建。&lt;/p&gt;
&lt;p&gt;所以最后那 17%，团队选择放手：反复重做也没用，只要相信语义正确就收工。作者说得很直白——继续为了字节匹配烧 token，不会让结果更好。&lt;/p&gt;
&lt;p&gt;这里还有一个不能忽略的代价：&lt;strong&gt;逐字节匹配会把原版当年的 bug 一起保留下来。&lt;/strong&gt; 这是它的优点（保真、防止重构引入新错误），也是它的限制（你想顺手修的 bug 也修不掉）。而且这个裁判太严，agent 干得更慢，产出却未必更好。裁判不是越严越好，是&lt;strong&gt;要跟你的目标匹配&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;那张表几个反编译项目的横向对照&#34;&gt;那张表：几个反编译项目的横向对照&lt;/h2&gt;
&lt;p&gt;把 momo5502 这个项目和同期几个「用 AI 反编译」的项目摆在一起看，规律就很清楚了——我把来源都标在下面，这张表是我自己拼的：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;项目&lt;/th&gt;
          &lt;th&gt;平台 / 编译器&lt;/th&gt;
          &lt;th&gt;函数数&lt;/th&gt;
          &lt;th&gt;耗时&lt;/th&gt;
          &lt;th&gt;路线&lt;/th&gt;
          &lt;th&gt;AI 的参与方式&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;现代战争 2（momo5502）&lt;/td&gt;
          &lt;td&gt;PC / 厂商编译器&lt;/td&gt;
          &lt;td&gt;约 99% 重建&lt;/td&gt;
          &lt;td&gt;约 3 个月&lt;/td&gt;
          &lt;td&gt;逐字节匹配&lt;/td&gt;
          &lt;td&gt;16 个 agent 全自动，500–700 亿 token&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Snowboard Kids（Chris Lewis）&lt;/td&gt;
          &lt;td&gt;N64 / IDO 5.3&lt;/td&gt;
          &lt;td&gt;2,145&lt;/td&gt;
          &lt;td&gt;84 天&lt;/td&gt;
          &lt;td&gt;逐字节匹配&lt;/td&gt;
          &lt;td&gt;agent 辅助，仅 4.8% 提交需专家介入&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Snowboard Kids 2（同作者，前作）&lt;/td&gt;
          &lt;td&gt;N64 / GCC 2.7.2&lt;/td&gt;
          &lt;td&gt;2,995&lt;/td&gt;
          &lt;td&gt;596 天&lt;/td&gt;
          &lt;td&gt;逐字节匹配&lt;/td&gt;
          &lt;td&gt;基本手工、少量 AI&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Klonoa: Empire of Dreams（Macabeus）&lt;/td&gt;
          &lt;td&gt;GBA&lt;/td&gt;
          &lt;td&gt;663&lt;/td&gt;
          &lt;td&gt;进行中（51%）&lt;/td&gt;
          &lt;td&gt;逐字节匹配&lt;/td&gt;
          &lt;td&gt;Claude Code + 自建工具&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Chromatron（Piotr Migdał）&lt;/td&gt;
          &lt;td&gt;PC&lt;/td&gt;
          &lt;td&gt;—&lt;/td&gt;
          &lt;td&gt;—&lt;/td&gt;
          &lt;td&gt;功能性重实现&lt;/td&gt;
          &lt;td&gt;Ghidra + AI，输出与原版差 1/307,200 像素&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;两个信息点值得抠出来：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Snowboard Kids 比前作快约 7 倍&lt;/strong&gt;（84 天 vs 596 天），但 Chris Lewis 自己很克制地说，「至少部分」归功于 AI，不能全算在模型头上——他本人已经在这个领域练了近两年，手速本来就快了。&lt;strong&gt;把这件事全记在模型账上，是外行的读法。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;两种路线，成败差别很大。&lt;/strong&gt; 主机游戏有已知编译器，能走「逐字节匹配」这条路；PC 老游戏没有可复现的编译器，只能走「功能性重实现」——用屏幕和行为比对当裁判，误差就大了（Chromatron 那条 1/307,200 像素）。&lt;strong&gt;裁判越严格、越自动，agent 越能自主。&lt;/strong&gt; 这不是巧合，是因果。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;真正的结论瓶颈不是生成是可判定的对错&#34;&gt;真正的结论：瓶颈不是生成，是「可判定的对错」&lt;/h2&gt;
&lt;p&gt;这才是我想写这篇的原因。&lt;/p&gt;
&lt;p&gt;过去一年，所有人都在卷「谁的模型更能写代码」。但 momo5502 这个项目把因果关系翻了过来：&lt;strong&gt;让 16 个 agent 自主跑了两个月的，不是模型有多强，是那套验收机制有多硬。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;他的原话我几乎想原样裱起来：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;正确性应该被定义、并且是可以机器检查的。人类出了名地不擅长精确表达自己的意图……所以我从不认为 reviewer 会够用。一个能给出客观 PASS / FAIL 的可靠验收装置，才是能给 agent 的最好反馈。&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;他还补了一句更狠的：「显然不是每个项目都有反编译这样的好运气。但我认为，&lt;strong&gt;任何项目都可以靠足够的创造力，逼近那个状态。&lt;/strong&gt;」&lt;/p&gt;
&lt;p&gt;我把这句话放在这一年的语境里读，味道就变了。我们已经有太多证据指向同一个方向：AI 产出的**「看起来对」和「真的对」之间，隔着一条越来越贵的沟**。前几天我写的那篇&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20261009-semantic-layer-text-to-sql-agent-deep-dive/&#34; &gt;《AI 写的 SQL 没报错，58% 的答案却是错的》&lt;/a&gt;，讲的是同一个病——SQL 跑得通、不报错，但答案是错的，因为没人给它一个「业务语义对不对」的裁判。反编译项目只是把这个病推到了极端：&lt;strong&gt;代码完全能读、游戏甚至能跑，但它是错的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对做 coding agent 的团队来说，这意味着竞争的焦点正在从「生成」挪到「验收」。模型能力会继续被拉平，但**「谁能提供机器可判定的对错」这件事，不会被拉平**——它跟你的领域知识、你的测试基建、你对「什么叫正确」的定义能力绑在一起，是抄不走的。&lt;/p&gt;
&lt;p&gt;反过来说，如果你手里正跑着做代码的 agent，而没有一套客观、自动、防篡改的验收标准，那你现在拿到的，大概率就是 momo5502 第一个月的那批东西：&lt;strong&gt;读起来无可挑剔，用起来悄悄出错，而且评审员已经被说服了。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;把裁判搬到别的项目上长什么样&#34;&gt;把「裁判」搬到别的项目上，长什么样&lt;/h2&gt;
&lt;p&gt;反编译有个别人羡慕不来的运气：&lt;strong&gt;编译器天然就是一个裁判。&lt;/strong&gt; 你写 C，编译器吐字节；原版也吐字节。两边一对，机器就替你把「对不对」算完了，全程不需要人。&lt;/p&gt;
&lt;p&gt;绝大多数项目没有这种运气。但这不代表造不出裁判，只代表要自己动手。我盘了几种常见的「人造裁判」：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;golden file / 快照测试&lt;/strong&gt;：把「已知正确」的输出存下来，之后每次生成都跟它比，差一个字符就打回。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;差分测试（differential testing）&lt;/strong&gt;：新旧两套实现喂同一批输入，比输出。反编译里「功能性重实现」路线的屏幕比对，本质就是这一种。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;属性测试（property-based testing）&lt;/strong&gt;：不比对具体输出，而是断言「无论输入是什么，都得满足某条性质」——比如「金额永远不为负」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;契约测试 / 类型系统&lt;/strong&gt;：把接口约定和类型约束本身当裁判，类型检查器就是个不合格但免费的裁判。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;业务对账表&lt;/strong&gt;：就像语义层那篇里说的，SQL 跑通不算数，得跟业务口径对得上才算数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不管哪一种，能撑起自主 agent 的裁判都得满足三条：&lt;strong&gt;自动&lt;/strong&gt;（不靠人看）、&lt;strong&gt;客观&lt;/strong&gt;（不靠人解释）、&lt;strong&gt;防篡改&lt;/strong&gt;（CI 哈希那一招，别让 agent 把裁判本身改掉）。&lt;/p&gt;
&lt;p&gt;我的判断很直接：&lt;strong&gt;一个领域能被 agent 自主做到多深，取决于它能不能造出这样一个裁判。&lt;/strong&gt; 造得出的，agent 能自主跑满两个月不用人；造不出的，agent 就只能打下手。这跟模型强不强，关系没那么大。&lt;/p&gt;
&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;5000 亿 token 听起来像个天文数字，但复盘里最贵的从来不是 token。token 在贬值，模型在换代，真正卡住 16 个 agent 让它跑完两个月的，是那张能一票否决、逐字节比对、还会校验自己有没有被篡改的验收表。&lt;/p&gt;
&lt;p&gt;生成能力正在变成水电一样的公共品。而在水电之上，谁定义了「对」、谁守住了「对」，谁就握着那条别人绕不过去的护城河。&lt;/p&gt;
&lt;p&gt;AI 写代码的下半场，比的不是谁写得快，是谁&lt;strong&gt;验得准&lt;/strong&gt;。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
