AI Agent 走进三维世界之后

最近两个月,我在 MinePilot 里密集做了一批三维建筑与地形融合实验。动手之前,我心里有一套默认分工:空间美感和建筑比例大概得靠 Claude Opus 或 Fable,几何计算与复杂代码多半还是 Codex Astra 更稳。没想到一轮峡谷圣所、三角洲温室、盛唐夜景街区和具象雕塑跑下来,Gemini Carbon 的表现意外地全面——从体块推敲、曲面建模、玩家视线调整,到进原生游戏查水流与碰撞回执,它几乎每一环都能接住。

不过,真正值得写下来的并不是一份模型排行榜。这套工作流里很多关键工具和诊断,恰恰是 Fable、Opus、Sol、Argon、Dot 和 Hydrogen 在不同阶段补齐的。真正把我拉回工程现实的,是一座藏在瀑布后面的洞穴。

在 Stratafall Ridge Sanctuary(内部代号 T01)的第一版源码(R1)里,远景镜头很漂亮:两侧崖壁夹着双层主殿,石拱桥横跨深谷,两条瀑布跌进潭水,钟楼正好卡在视觉焦点上。如果这只是一个网页里的三维展示 Demo,截图发出去大概已经可以收工了。

但独立评审 Agent(Argon)没有只看远景。它把相机切到玩家眼高(eye-06),走进那座号称“藏在瀑布后面”的洞穴,向上扫了一遍体素列:头顶整整 69 列直通天空,洞穴根本没封顶;位置也不在主瀑布正后方,为了在特定机位凑出“水帘洞”画面,脚本在半空硬挂了一条悬空假水帘。桥上的问题更隐蔽:为了让远景看清主殿立面,脚本声明了一个视线净空盒(void.reveal-sightline),顺手削掉了高架桥的一截桥面和护栏,留下两盏灯笼孤零零飘在空中。而当时的变更检查居然报告 collateralCells = 0(零误伤),因为那个切坏桥面的空气盒正是作者自己声明的区域。直到后来在冻结源码上完成局部修复(R1 → M0),先后用同机位代理渲染和原生客户端核对,这座水帘洞才真正立住。

图 1:T01 从 R1 到 M0 的同机位修复对照(重点可看底部 eye-06 水帘洞行)。这是冻结模型的代理渲染,用来比较洞顶与视线变化,不是 Minecraft 实机截图;手机端可点右侧链接放大查看细节。 查看大图

图 2:T01《Stratafall Ridge Sanctuary》修复后的瀑布洞穴玩家视角(eye-06,Minecraft Java 26.3 原生实机截图,无光影)。

那一刻我意识到,自己之前给 Agent 搭的基础设施只走完了一半。

编译器能证明合法,证明不了空间

几个月前写《给 Agent 造一个 Domain Harness》时,我总结过一条原则:Agent is the author. Engine is the compiler.

当时为了不让大模型在 Minecraft 里实时乱走卡死,也不让它手算几万个方块的绝对坐标,我把底层体素构建引擎 CraftDAG 拆出来做了开源(MinePilot 则是承载生产目录、预览、评审与交付的私有站点系统)。在常规建筑上,CraftDAG 跑的是一条清晰的语义编译链:

BuildIntent → ComponentPlan → CraftDAG IR → VoxelPlan → Sponge .schem

Agent 用 RoomShell、Door、GableRoof 以及 anchor、wall、offset、overhang 写计划,编译器负责展开依赖、检查越界、落成具体的 BlockState。出错时引擎返回带字段路径和 repairHint 的结构化提示,Agent 像修代码一样做局部修正。对普通住宅、哨塔,甚至罗马斗兽场这类以重复拱廊为主的建筑,这条路都很稳。

问题在于,当建筑开始和峡谷、河道、洞穴、桥梁、夜景光线乃至人物衣褶纠缠在一起时,“能编译”和“能成立”之间裂开了一条大缝。编译器能证明构件没越界、没有悬空孤岛、导出的 .schem 读回内存零差异;但它证明不了洞穴真在水流后面,证明不了玩家站在看台上看到的是一池水还是一堵胸口高的实心栏杆,也证明不了一座下界要塞看上去像城门还是像黑砖火电厂。

就像 TypeScript 编译通过不代表网页不是白屏。走进三维世界之后,Engine 依然是编译器,但 Agent 还需要观察空间、接受批评,并在不毁掉全局的前提下做局部手术。

为什么这比做一个漂亮的 3D 预览难得多

常有人问:现在让模型写一段 Three.js 搭出带光影的漂亮场景并不难,生成 Minecraft 建筑为什么还要折腾这么多轮?

这倒不是 Three.js 缺什么三维能力,关键在于验收契约完全不同。展示型 3D 场景围绕 Mesh、打光和少数预设镜头交付,背面穿模只要镜头不拍到就不算大事;而放进玩家存档的 .schem 蓝图必须同时扛住六层约束:

  1. 三维拓扑与负空间:房间、洞穴、楼梯间都是可进入的空体积,不能只糊一层外皮。洞穴要有连续岩顶,桥和崖壁要咬合,圆拱离散成方块后不能走形。
  2. 离散体素转译:无论前端是粗体块还是网格,最终都要落回整数格 BlockState。楼梯朝向、上下半砖、墙体连接态和水深错一个属性就可能漏水断路。
  3. 玩家尺度可达性:航拍好看远远不够,玩家碰撞盒需要真实落脚点、楼梯净空、防坠护栏,以及跨高程走回起点的环路。
  4. 原生游戏物理:网页 WebGL / Deepslate 预览是静态渲染。放进原生 Minecraft 后,水会不会漫进室内、悬吊灯笼会不会掉成物品、重开存档状态稳不稳,只有进真游戏才知道。
  5. 有界局部修复:十几万方块的作品里,某个机位被栏杆挡住时,Agent 要从像素反查命名区域(pick -> edit -> diff -> re-render),锁死受保护空气层(protected AIR),确保无关区域零改动。
  6. 技术与审美分立验收:TECH、ART、NATIVE_STATE、PLAYABILITY、TRANSFER 是五道独立门槛。状态零误差、静态路径全连通,既不等于好看,也不等于真实游戏里走得通。

几条昂贵的弯路:概念图、镜头隧道与绿灯幻觉

跑通现在的闭环之前,我们在几个方向上实打实栽过跟头。

第一个教训是 2D 概念图当不了 3D 设计源。在做大型下界建筑实验(N01,后来稳定发布为 The Lodestone Plumb)时,Opus 曾端到端生成过一版约 11 万方块的候选作品 Cinder Gate:工程指标零误差,进游戏一看却像废弃的黑砖发电厂(美术盲审 49/80)。我们起初以为是缺视觉参考,便在 N01 上让 Opus 和 Carbon 试过“先画 2D 概念图再转 3D”。结果更糟:单张画没有背面和剖面,悬挑露台一翻成三维就退化成对称台阶金字塔;换成多角度 2D 图组,不同透视之间的几何关系又处处打架。三维建筑的起点只能是三维体块、平面与剖面。

图 3:Opus N01 的二维概念图与实际可编辑三维模型。概念画里的空间关系没有完整落到三维源里;下方三维模型展示的是转译结果,不是原生游戏验收。 查看大图

第二个教训来自极端镜头优先。先锁死几个玩家机位、沿着视锥反向长出建筑行不行?我们在 PF02 和代谢派塔楼实验里试过。Carbon 在对照实验里很快抓到病根:过早锁死相机,Agent 会把建筑做成一条狭窄的“镜头走廊”——正前方堆满细节,两侧砌成死气沉沉的高墙;或者体块还没落稳就先定机位,最后拍出去九成都是空天空。空间分层与主地标必须先立住(Design-First),玩家视角随后进场挑刺。

第三个教训最让人哭笑不得:工具亮绿灯,不等于 Agent 真的“看见”了。Fable 搭好第一代粗体块观察与修复工具(architecture.region-blockout)后,Argon 用它做了一座新艺术风格浴场(R1)。跑完 cli.mjs render,终端吐出退出码 0、RENDERED、eyesInSolid: [],Argon 看到绿灯便放心交卷了。结果打开 views.json 和代理渲染图一看,二层俯瞰泳池的关键机位 eye-05 里,实心石栏杆糊住了画面中央 62.49% 的像素,池水可见比例是 0.00%,穹顶还漏着 U 形破洞。

为了确认作者模型靠自检能不能补上这个盲区,我们拿冻结的浴场 R1 做了一组受控实验(R07),让两个隔离的 Argon 会话各做一轮有界修改:自检组(Self-Critique)重读渲染图与 views.json 后只补了穹顶,却把挡住大半画面的实心栏杆当成“正常前景”,池水可见度仍是 0.00%;拿到独立评审缺陷回执的辅助组(Critic-Assisted)则同步把实心环墙改成透空栏柱,把池水可见比例拉回 5.23%(因外立面无窗等整体造型问题仍在,这组实验模型未进原生客户端)。

图 4:R07 浴场的 R1、Self-Critique 与 Critic-Assisted 三列代理渲染对照(重点可看 eye-05 泳池俯瞰行)。原版和自检版的池水可见比例均为 0%,独立批评辅助版改善到 5.23%;这不是原生客户端测试。 查看大图

这组对照推动了两项改动:一是 render 直接在标准输出打印各玩家机位的前三名可见区域占比,并显式标出 visualQuality: NOT_ASSESSED;二是在复杂场景与旗舰地标任务上,按需引入独立视觉盲审(Independent Critic),把作者生成与看图挑刺拆开。

把观察、批评和局部修复接进闭环

踩完这些坑,我们在 CraftDAG 之上按几何特征分出了三条可复现的前置路线:

  • 语义组件路线(Semantic ComponentPlan):适合开间、楼层和屋顶模块清晰的常规建筑;
  • 命名体块与受保护空气路线(Region Blockout + Protected AIR):适合山地、河谷、桥梁与室内外穿插的复杂场景;
  • 雕塑优先路线(Sculpt-First Procedural Mesh):适合由连续曲面与衣褶主导的具象雕塑,先生成闭合可编辑 .obj 网格,再采样编译进 VoxelPlan。

普通模块化建筑直接走确定性编译与基础校验即可;一旦进入复杂地形或旗舰地标,三条路线都会汇入下面这套完整闭环(其中多方案筛选与独立视觉盲审按空间风险触发,并非所有普通 SKU 的固定流水线):

flowchart TB
    Intent["设计意图与真实建筑参考"] --> Coarse["平剖面规划 + 低模可编辑三维源<br/>ComponentPlan / Region Blockout / Sculpt OBJ"]
    Coarse --> Inspect["绑定源码哈希的固定机位与玩家眼高采样<br/>区域像素占比 + Protected AIR + 碰撞盒落点"]
    Inspect --> Critic["独立 Agent 视觉盲审(复杂旗舰按需触发)<br/>区分 TECH 与 ART"]
    Critic -->|"遮挡 / 漏顶 / 体块失衡"| Repair["有界局部修复<br/>pick -> edit -> diff -> re-render"]
    Repair --> Inspect
    Critic -->|"设计放行"| Compile["确定性编译 BlockState 与 Sponge .schem"]
    Compile --> Native["原生 Minecraft Java 26.3 验证<br/>WorldEdit 导入 / 流体 Tick / 存档重载 / Survival 走线"]
    Native -->|"掉落 / 漫水 / 路线阻断"| Repair
    Native -->|"五维门禁留证"| Review["人工终审与公开上架"]

核心原则只有一条:在几百行低模源码阶段尽早暴露空间硬伤,不要先生成十几万方块再靠贴花补救。

五件走完闭环的作品,以及它们各自的边界

闭环跑顺后,Carbon 与其他模型配合作出了一批已公开上线的作品。挑其中五件来看,既能看清跨环节能力是怎么接上的,也能看到每件作品留下的真实边界。

1. 瀑布洞穴与峡谷圣所:Stratafall Ridge Sanctuary(T01)

回到开头那座漏顶的洞穴。拿到 Argon 的 HOLD 报告后,Carbon 直接在原模型上做局部返工:补齐连续深板岩拱顶,把悬空假水帘改成落到河床上的真瀑布水幕,缩回误切桥面的视线盒,封住河口侧漏并把大殿立柱落回实墙。

定稿的 Stratafall Ridge Sanctuary(157,662 方块)导出 .schem 零差异;放进原生 Java 26.3 后,四千多个动态水方块在 20 ticks 内稳定、室内零漫水,客户端在真实 Survival 模式下零传送走完了 550 步立体环线。Argon 终审给出 ART 77/100 PASS(复核了源码编译与绑定哈希的原生回执,未二次拉起客户端),同时保留了已知边界:西侧沟壁偏平、北侧背坡仍有平面感,截图外围的绿山属于测试宿主河谷而非蓝图本身;单看 T01,跨场景迁移仍标为 UNVERIFIED。

2. 低高差湿地与跨模型接力:Reedglass Delta Conservatory(T02)

为了检验方法能不能换地形复用,Reedglass Delta Conservatory 从百米深谷换成了总高差仅 +7 格的辫状河三角洲湿地,主体是架在防洪墩上的铸铁玻璃温室与北侧水闸,并改走跨模型接力:Carbon 设计(D1)→ Dot 门禁 → Argon 施工(S1)→ Dot 美术反馈 → Argon 受限修整(F1)→ Dot 终审。

D1 阶段,Dot 先抓出一个假绿灯:回程机位 eye-06 虽报 eyesInSolid = 0,脚下却是空气,相机悬在水面上;Carbon 用局部补丁修好栈桥落点,并补上了 0.6 × 1.8 碰撞盒落脚点检查。到了 S1,Argon 跑通原生水流与路线后技术全绿,Dot 却把美术卡在 71/100 HOLD(排除了外圈测试宿主围墙):芦苇丛像方正的绿叶积木,室内地面偏空,高空观测廊用了一圈刺眼的纯白铁块(iron_block),压过了玻璃拱顶。

图 5:T02《Reedglass Delta Conservatory》S1 → F1 同机位原生 Minecraft Java 26.3 对照(重点可看 eye-04 高空栈道与湿地岸线行):白铁块栈道换成深板岩边框和云杉木踏面,温室与湿地补齐了植物层次。 查看大图

因为命名区域清晰,Argon 只做了一轮 F1 局部修整,把白铁块栈道换成深色 deepslate_tiles 边框配暖色 spruce_planks 木踏面,补齐室内红树林气生根与外圈垂柳岸线,原有水体和路线一格未动。Dot 盲审对比图后给出 ART 78/100 PASS,将迁移状态记为 VERIFIED_SCOPED——同时如实注明:那 404 步 Survival 走线由工具驱动真实客户端完成,不冒充人工键盘通关;无头环境使用 llvmpipe 软件渲染,不代表帧率认证。

3. 夜景不是挂满灯笼:灯火长安(Lanterns of Chang’an)

前两件考地形与水,Lanterns of Chang’an(灯火长安)(334,073 方块)考的则是古建群与原版光照。规矩只有一条:关闭光影(Shaders OFF)、零后期,只靠原版 Java 26.3 光照做出盛唐上元夜的层次。

早期方案到处塞隐形光源 minecraft:light,直接覆盖吞掉了 75 个建筑构件,高处殿顶却依然漆黑。Carbon 在“上元万灯”方案里改按古建构造布光:用内退的蛙明灯配菌光体做出透光直棂窗(“雕窗映烛”),靠檐角宫灯与柔和屋脊擦光托出琉璃瓦轮廓而不致过曝,街面再用五层灯轮、悬链灯廊与御沟荷灯拉开景深。

图 6:《Lanterns of Chang’an / 灯火长安》在原版 Minecraft Java 26.3(关闭光影)下的上元夜景实机效果。

真正的惊险出在 Argon 生产精修阶段。把蓝图贴进原生 Java 26.3 触发邻格更新后,日志突然抓到:203 盏悬吊宫灯当场掉成了满地物品实体。 它们分别挂在杜鹃树叶和上半砖(slab[type=top])底面——静态网页预览完全正常,但原版底面承重规则挂不住它们。Argon 随后把挂点全部换成红树林木横枝与实心木梁,移走挡视线的河岸灯树,并把 canSupportLantern 写进静态质检,复测实现全部 1,633 盏灯笼零掉落。

4. 当建筑原语遇到衣褶与手臂:自由女神像与救世基督像

把语义组件推向曲面地标时,盒子原语很快见底。Codex Astra 在泰姬陵上迭代四轮,最终只能作为 taj-mahal-study(ART 72/100)做有界改良;到了具象人物雕塑更难硬拼。自由女神像早期版本不仅把火炬镜像举在了左手上,冻结的早期雕塑源还没能保留冠冕下方的 7 扇窗洞(0/7);当时无头客户端甚至连拍出纯蓝天截图,多亏 Sol 查出区块几何缓存未及时失效的渲染 Bug 并修复,才打通原生取证。随后 Fable 给出雕塑解剖诊断并做了头部实验,确认单靠修补粗体素掩码很难兼顾五官体块与细小开孔。

Carbon 干脆切出一条 Sculpt-First(雕塑优先) 路线:先用 Python/C 脚本生成多角色闭合 .obj 雕塑网格,在素模八向视图里把五官放样与古典衣褶比例改对;再经子体素采样、语义角色覆盖优先级与受保护 AIR(配合回归测试锁死冠冕窗洞不被头发或袍子吞没)编译进 VoxelPlan 与 .schem,先过 Deepslate / WebGL 预览,再进原生客户端验收。

图 7:《Statue of Liberty》的可编辑 .obj 雕塑源、体素化结果与 Deepslate/WebGL 预览对照。这张板展示建模与转译过程,不是整板原生 Minecraft 截图。 查看大图

这条路拿下了两座雕塑地标:Statue of Liberty(自由女神像)(总高 208 格、124,954 方块,七芒冠冕与额前凹窗清晰分离,盲审 90/100)与 Christ the Redeemer(里约救世基督像)(100,099 方块立于科科瓦多山峰顶,经 v1 -> v2 微调补齐悬垂袖摆与皂石凹槽阴影后盲审 92/100)。

图 8:《Christ the Redeemer》西北侧全景,展示雕像与科科瓦多山基座(Minecraft Java 26.3 原生截图),不是山顶观景台上的玩家机位。

两座雕塑同样把可玩边界说在明处:自由女神像只有底层基座大厅做了真实键盘 Survival 走线,救世基督像的动线止于登山阶梯与山顶 360° 观景台,雕像内部均不开虚构房间。哪怕评分过了 90,figurative-sculpt-first 在注册表里仍留在 L1 Experimental——两座雕像走通,远不代表任意有机雕塑都能自动泛化。

Carbon 被拦下的那座冰川,以及换方法之后

如果故事停在这里,很容易让人觉得只要工作流搭对就能一路平推。事实并非如此。就在自由女神像和灯火长安收尾的同一天,Carbon 主导的第三个地形场景 T03 冰川裂隙天文台在历经初版 R1 叫停、M1-A 机位诊断与断裂带重塑、再到 M1-B 源码级重做之后,依然被独立评审拦下,该版候选定格在 D1_DESIGN_HOLD_FINAL。

单看 M1-B 的静态工程账本挑不出毛病:261,462 个占用方块编译导出零差异,受保护空气层、承重链与 380 步静态路线全部通过,黄铜穹顶和冰裂隙下方的冰拱水潭(E06)也做出了真实三维负空间。但 Dot 盲审素模对照板与八个玩家机位后,只给 M1-B 打出 74/100 的 D1 设计潜力评分,果断叫停在 S1 与原生实测之前:地上冰川体块在素模里依然像几道带重复阶梯纹路的楔形挡土墙;主露台(E05)近景发黑、远处只剩空天空;室内 E07 的厚重望远镜基座反过来堵死了房间体验。

图 9:T03 冰川从 R1、M1-A 到 M1-B 的设计阶段素模渲染(clay renders),比较冰川体量与轮廓。M1-B 的 74/100 是 D1 设计潜力评分,不是正式 Minecraft ART 分数;该候选未进入原生 Minecraft 或 Survival 验证。 查看大图

我反而很看重这次 HOLD。放在两个月前,面对一个“26 万方块、零编译错误、路线全通”的大工程,人很容易因为沉没成本硬着头皮刷冰砖贴花;把 TECH 和 ART 拆开以后,宏观体块没立住就干净地把 M1-B 封存在低模阶段,反而逼着我们去改几何生成方法。在随后的 M2-A → M2-B 新一轮方法研究里,Carbon 查清了挡土墙感的病根:旧脚本用近 80° 的凸多面体半空间切割冰体,在北侧留出了八十多格不断开的直墙与平直等高线。改用“侵蚀优先的自然内凹曲面包络 + 冰下洞室与冰拱三维协同设计”后,北侧连续高墙被高山隘口切开,东北冰峰做出了陡峭拔蚀壁与缓坡冰肩的走向反差,E05 露台与偏置赤道仪的 E07 穹顶也重新对准了近景裂隙、中景冰塔与远景主峰。写下这篇记录时,M2-B 新源码刚交由 Dot 做独立盲审,尚未经过正式 ART 放行与原生 Survival 验证——但“体块不对就换几何母体,绝不靠贴花硬冲生产”,恰恰是我最想留在系统里的节奏。

回头看最初那个让我意外的现象:为什么在这批三维实验里,Gemini Carbon 会显得格外顺手?我只有一个尚未证实的个人假说。在我们跑过的题目上,不同模型的长板差异很鲜明:Claude Opus 和 Fable 在符号抽象、工具设计(region-blockout)与结构诊断上极强,而 Opus 独立端到端生成整座地标时,符号结构有时会跑在视觉感知前面;Carbon 则在曲面几何计算、离散体素契约与玩家眼高截图挑刺之间显得更均衡。我猜这也许和 Google 从 Gemini 1.0 技术报告起就坚持从头做联合多模态预训练有关——当早期偏笨重的推理与工程能力补齐后,跨模态底座开始在空间闭环上显出复利。当然,单凭我们这批工程样本,没法把预训练底座、Domain Harness 约束、工具遥测与推理时长的影响严格剥离开;况且 T03 在 M1-B 上的受挫也说明,没有任何模型能自动跳过几何方法本身的设计关。

之前在《置身 Agent 时代》和《Jev 的边界》里,我一直在想确定性程序、模型能力与人类判断的分界。MinePilot 这轮实践让我看到,当 Domain Harness 从“能编译、能报错”往前再走一步,把空间视角观察、按需触发的独立批评、有界局部修复和原生物理检验都接进来之后,Agent 能走完的工程深度比我半年前预想的深得多;而人的注意力则进一步收敛到定审美标准、守诚实边界,以及在冰川体块不对劲时拍板换方法。

这也留给我一个更大的问题。过去几年,Coding Benchmark 测的是模型能不能让编译器和测试满意,图像与视频生成测的是模型能不能在单帧画面上讨好眼睛。而像三维世界构建这样,要求 Agent 构想空间、编译进离散物理规则、以第一人称走进去观察、做零误伤的局部修复,同时接受独立审美与真实运行时的双重检验——它会不会成为接下来衡量通用 Agent 空间理解、创作判断、工具使用与可验证执行能力的一块试金石?


相关探索与公开作品: