← 开发日志

检查,写进流程里

第一回之后 · 第 3 篇 · 连载中

一个问题修好以后,这个修正去了哪里?

先讲一个我以前做游戏时候的事。我每天早上的习惯,是把最新的代码和美术资源拉下来,编译,把游戏跑一遍。有一天早上,游戏一跑就崩了,内存不够。原来是美术团队提交了一批贴图,尺寸没有缩到规定的大小,把内存预算撑爆了。这也不是什么大事,我写了封邮件给技术美术。贴图不是他提交的,可是美术资源出了问题都归他管。他很快就修好了,游戏又能跑了。

两个星期以后,又来了一次,这回换了一张贴图。这次我在游戏里加了一道检查,贴图超了尺寸,游戏不会再崩,只会在那张贴图上盖一层”坏贴图”的标记。然后我又请美术团队把那张贴图修好,他们也修了。

结果第三次又来了。游戏没崩,这是我那道检查的功劳。可是那层标记就明晃晃地摆在那里,荧光粉色(我能找到的最烦人的颜色),整个团队的人都看得见。

这时候我才去弄清楚,到底为什么会这样。原来提交这些资源的美术同事,根本不跑游戏。他们有自己的一套工具,要先拉最新的代码,再编译游戏,再进游戏里检查自己的资源,太花时间了,平时的工作里根本排不进去。所以错误总是出在一个谁都看不见的地方,却总是在一个所有人都看得见的地方才被发现。于是我把同一道检查放进了他们的工具链里,贴图超了预算,提交直接失败。从那以后,游戏里再也没出过这种事。我相信那道检查后来还是拦下过不少超标的贴图,只不过它们在到达任何人手里之前,就已经被拦住了。

这三次修正,每一次都没有错。那封邮件修好了一张贴图,游戏里的检查治住了崩溃,可这两次都是治标不治本。只有第三次,修的是犯错的那个地方。

到了十月,我发现自己又回到了第一次的做法,在 MoVox 上,和 Claude 一起,整整做了两回。而这个项目,从头到尾只有我一个人。

本来应该是怎么运转的。

MoVox 画面这一边的计划是这样的:本地模型读小说,给每一张画写提示词,自己检查自己写的东西,然后出图。我负责验收每个人物的肖像,再对结果做抽查。Claude,也就是我和它一起做 MoVox 的那个 AI,负责写代码和测试。

计划是这样,可是第三十七、三十八回实际做下来,完全是另一回事。

每一张关键帧、每一张照片、每一段片段,我都是在审查工具里一张一张亲眼看过的。哪里不对,我就标出来,然后 Claude 去修。修的不是代码,是结果:写一条手动覆盖(override),意思是”这个场景,画这几个人,在做这件事”;或者直接改数据库;或者换一个随机种子重画。每一次,当下看都是最快的选择。标一个问题进来,一个修正出去,画面就对了。

举一个典型的例子。第三十七回里,刘备骑马经过田边,有几个农夫一边锄地一边唱歌,歌的头两句是”苍天如圆盖,陆地似棋局”。模型写提示词的时候,描绘的是歌里的比喻,唱歌的人反倒没有提到,所以出来的画面是一块棋盘,上面罩着一个圆顶的天。我让它重画,出来的还是棋盘。不管重画多少次,棋盘都在,因为重画只是拿同一句提示词再抽一次签,而那句提示词要的就是棋盘。最后的修正是一条手动覆盖:画农夫在锄地、在唱歌。

这个修正没有错,画面现在也是对的。可是它被写进了一个文件,而写提示词的那个模型,从来不读这个文件。

136 处手动修改。

到十月二十四号,这个覆盖文件里已经有了 136 条,光是第三十七、三十八回就占了 62 条。每一条都记着模型原来写的是什么,应该写成什么,以及为什么。这其实就是一条写得很清楚的经验教训,只是没有任何人在学它。

棋盘只是其中一类错误:诗歌的场景,把诗里的比喻画了出来。这样的类别一共有二十多种。只是被人提到的人物,被画成站在屋子里。一个根本不是人的词,被当成人物放进画面,比如”荆榛”,比如”当日”。一个其实是先后两件事的动作,比如骑驴过了桥再下驴,被硬塞进同一张画里。第三十九回会把这些错误原样再犯一遍,因为上游什么都没有变。

所以说实话,问题出在三个地方,没有一个是”模型不够聪明”。

第一,我的审查没有变成数据。审查工具里的一个标记,只写着”照片”,没写为什么。后来我们回头去给检查打分的时候,22 个照片标记里,一个备注都没有。原因当然是有的,可是它们散落在 Claude Code 的聊天记录里、提交说明里、问题清单里,还有 Claude 自己的笔记里。这些东西,本地模型一样都读不到。

第二,从来没有人问过本地模型那些简单的问题。我做的修改,大部分其实跟画面关系不大,都是关于文字的判断,在任何 GPU 工作开始之前就能做:这个人是在场,还是只是被提到?这是一个瞬间,还是先后几个动作?这个词到底是不是一个人?一个 35B 的模型,看着原文那一句就答得出来,可是流水线里没有任何一步去问它。整个循环里唯一的审查员,看的是画好的成品,问的是开放式的问题,它的表现怎么样,我在之前那一篇里已经讲过了。

第三,也是我最想老实交代的一点:我们的角色都跑偏了。Claude 本来应该去写检查,结果它自己变成了那道检查。我本来应该做抽查,结果每一张都在审。因为每一次修改都很快,所以从来不觉得这是在做什么决定,只觉得是在把这一回赶完。

一个修正,应该放在哪里?

这跟 AI 没什么关系,就是贴图那个故事又来了一遍。

我们做的每一次手动修改,都停在第一次的做法上:给技术美术发一封邮件。当天是管用的,可是明天还得再发一封。所以每抓到一个问题,都应该问一句我到第三次才想明白的话:这道检查放在哪里,才能不靠我也照样跑?而最好的答案,通常是尽量靠近犯错的那个地方。

所以我们做出来的检查,大部分根本不看画面。那块棋盘,画并没有画错,错在提示词,是一个文字模型在读那首歌的时候犯的错,那时候 GPU 还一张图都没开始画。检查画好的成品,就好比在游戏里检查;检查提示词,才是在美术同事自己的工具里检查。

放到 MoVox 上,最后归成了三个地方,按优先顺序排:

  1. 写成代码里的规则。确定的,有测试的,永远不会累。动作里要是出现”随后""然后”,那就是先后几个动作了。
  2. 问本地模型一个封闭式的问题。不问”这张画好不好”这种开放式的问题,只把原文那一句引出来,让它答”是”或”不是”:这个人是在场,还是只是被提到?
  3. 变成一个测试用例。那 136 条覆盖,每一条都是一个标好答案的例子:场景在这里,模型写的是这个,正确的答案是那个。一道新的检查上线之前,必须先把它这一类里已知的例子都抓出来,而且在我已经标过”没问题”的场景上,不能乱报。

在这三条之上还有一条规则,也是这条规则改变了我们的工作方式:一条经验如果只写在文字里,就不算放进了流程。它要是只在 Claude 的笔记里,没有变成规则、提示词或者测试,那流水线就等于没学会。

标记的方式也改了。现在审查工具里每标一个问题,都会要一个原因,从错误类别的列表里点一下就行,也可以再写几句。这样我的审查就会变成测试数据,不会再凭空消失。Claude 的工作也回到了原来的位置:一个问题被标出来,就去修那条漏掉它的规则、提示词或者检查,然后让流水线把那个场景重新跑一遍。手动覆盖是最后的办法,而且每用一次,都算是流水线的一次失败,不算修好。

这些检查到底抓到了什么。

第一批检查是一个周末做出来的,赶在第三十八回的照片生成之前。每一道检查,我们都拿我手动做过的那些修改去给它打分。结果有好有坏,而我觉得,好坏参半这一点本身才是最有用的。

有些检查就是不管用。问模型”场景的地点,是不是原文里说的那个地方”,16 个已知的例子只抓到 2 个,所以就拿掉了。让视觉模型对着整张照片回答开放式的问题,它报出来的问题里,只有 14% 是真的。比较简单的对比反而做得更好:同一个人,关键帧和照片放在一起比,袍子的颜色对不对得上?最后上线的照片检查,报出来的大约 61% 是真问题,我抓到的那些,它大约能抓到一半。这只能算一道筛子,算不上审查员,所以现在还是要手动打开才会跑。

还有一道检查,让我们对检查本身多懂了一点。“每个人的头饰有没有写得具体”这个问题,在 30 个本来应该没问题的场景里,报了 27 个。看起来像是检查坏了,其实没坏。上游有十七个人物的外貌描述里,带着一个”或”字。刘备的写的是”头戴逍遥巾或裹巾”,所以给他写的每一句提示词里,都带着一个二选一,每一张画也都只能挑一个。检查是对的,错的是数据。所以一道检查要是几乎每次都报警,先别急着改它,往上游看一看,它可能是在源头找到了一个毛病。

它管不到的地方。

上一篇里我说,第三十七回花了几个星期,第三十八回只花了几天。这是真的,那次复盘也是一个很大的原因。可是我不想让你以为,从此检查就全接手了,因为并没有。

复盘以后,覆盖文件里又多了 12 条手动修改,其中 10 条是第三十八回后半段的。第三十八回的很大一部分,还是我在看,Claude 在修。这些检查让审查快了不少,指出来的问题也更常是对的,可是它们没能取代审查。

我自己这一半,也没有完全做到。现在审查工具每标一个问题都会要一个原因,可是我并不是每次都选了。有时候是太赶,有时候是忘了,还有的时候,是我自己也拿不准一个错误该归哪一类,与其乱猜,我就干脆跳过了。第三十八回的照片标记里,有十个没有写原因。说到底,这还是那个美术团队的问题,只不过这回美术团队换成了我自己:一个步骤,如果偏偏在你急着把东西交出去的那一刻要你多花力气,那它就是最容易被跳过的那一步。一个让人选不出来的类别列表,本身就是一种阻力。要修它,大概得换一个更好的列表,或者允许我先不选类别,只留一句备注。

真正的考验,复盘文档里已经写好了:第三十九回从头到尾跑一遍,一处手动修改都不做,然后我来审,我们来数。这一步还没有做,后面的时间先给了配音。所以我能告诉你的是,这套方法在我们已有的例子上是管用的;它能接住一个全新的回目里多少问题,我现在还说不上来。

另一半的活。

授人以鱼,不如授人以渔。那个文件里的每一条手动修改,都是一条鱼,鱼是好鱼,送得也快。可是我们手上这条流水线,本来的意义就是自己去打鱼,而我们一直在往它手里递鱼。

所以,如果要把这篇里的一点东西带回你自己的工作,不管做审查的是你自己、你团队里的骨干,还是一个 AI 智能体,我想说的是:抓到一个错误,活只做了一半。另一半,是把这道检查放到一个不靠你也照样能跑的地方。要是你发现同一个修正已经是第三次在做了,就停下来问一问,到底谁应该是那道检查,因为多半不应该是你。