我们那条短视频生产线每天要产出一批成片。里面有两类东西:一类必须好看,一类必须对。
好看的是场面、光线、材质、镜头的运动。对的是机型名、注册号、日期、航线、价格、坐标、图表上的数字。
把这两类交给同一个系统去生成,是这条产线上我犯过最贵的错。
便宜的错和贵的错
生成模型出错,有两种长相。
一种一眼假:飞机倒着飞、起落架长在机翼上、云层在两帧之间整个换了一遍。这种错不贵——它在质检里活不过三秒,而且看见的人只会觉得片子糙,不会被误导。
另一种是:画面里那架飞机的注册号,模型给你生成了一串看起来完全合理的字符。字体对、位置对、透视对、磨损感都对。它唯一的问题是,那架飞机不存在。
第二种才是贵的。它不会被质检拦住,因为它没有任何「看起来不对」的特征。它会被当成信息发出去,被人截图,被引用。一个看起来权威并且是错的东西,比一个明显糙的东西贵一个数量级。
而生成模型在没有结构性阻止的情况下,会非常乐意地产出第二种。这不是它的缺陷,是它的工作方式——它在拟合分布,而「合理」正是分布的中心。
所以我们把这条线画在了系统里
生成负责质感,程序负责事实。
模型生成场景、材质和受控的运动。标题、日期、机型、航线、价格、箭头、引线、字幕,全部由代码合成到一个锁定的前景层上。
可读的文字从不交给视频模型。
这条规则的实现方式很朴素:前景层是程序渲染的,数据来自数据库,合成发生在生成之后。模型交出来的那一帧里本来就没有字——它没有机会写错,因为它根本没有被要求写。
写下来像是一句审美偏好。它不是。它是把「必须精确对的那部分」从生成模型的解空间里整个拿走——错误不是被检查出来的,是被结构性地变成不可能的。
这和不让模型写 SQL 是同一个动作
我在另一篇里写过我们的数据 Agent:它不产出 SQL,只产出一组带类型的参数,下游一个零模型的函数把它编译成查询。模型从头到尾没见过表名,所以编不出不存在的列。
介质完全不同,判断是同一个:
| 必须精确对的部分 | 怎么拿走 | |
|---|---|---|
| 数据查询 | 表名、列名、JOIN 键 | 模型只填参数,SQL 由函数拼 |
| 视频生产 | 机型、日期、价格、注册号 | 模型只生成画面,文字由代码合成 |
两边都不是「让模型更小心」,也不是「加一道检查去抓」。是重新划分谁被允许碰什么。
检查是兜底,边界才是解法。一个只靠检查守住的错误,意味着它仍然会被产出,只是希望每次都能被抓到;而一个被边界排除的错误,是不会发生的。
边界:它只在两类东西能被分开时成立
这条规则有个前提——质感和事实在你的产物里是可分离的。
在视频里它们天然可分:画面是画面,文字是叠层,两者物理上就在不同的图层。在数据查询里也可分:语义是语义,SQL 是 SQL。
但有些东西分不开。一张图表的形状本身就是事实——你没法让模型生成「一张好看的柱状图的质感」再由程序把高度填进去,因为高度就是这张图的全部内容。一段解说词也分不开:叙述的流畅度和陈述的准确性长在同一串字里。
**分得开的,用边界;分不开的,只能上验证。**两者的成本差得很远,所以值得先问一句能不能分——大多数时候答案是能,只是没人想过要分。
一个副作用
这条边界还带来一个我没预料到的好处:它让「哪里出了错」变得可定位。
文字错了,是数据或者合成代码的问题,去查数据库;画面错了,是生成或者素材的问题,去查提示词和源片。两条链路不交叉。
在这条规则之前,一个成片出问题,排查要从头看到尾,因为任何一个环节都可能是任何一种错。把责任划清楚的系统,出事的时候也更容易修。