丢了一万六千字
停更的两个月 · 第 2 篇,共 4 篇
上一篇讲到,我停更是为了做演示,结果发现很多”这张画画错了”,其实是”我们让它画错了东西”。这一篇是同一类错误,只不过出在更上游的一个位置。
阅读智能体负责接过小说,把它变成结构:这一段是旁白,那一段是对话,这句话是谁说的。这个位置为什么压着整条流水线,我在前面那篇里讲过:它后面没有一个环节真的去读原著。配音智能体读的是一行记录,作画智能体读的也是一行记录。阅读智能体要是丢了一句话,这句话在片子里就没了;要是把话安到了别人头上,那就是别人来说,用错的声音,配错的脸,而后面的环节谁也没办法知道。
七月的时候我说过,阅读智能体能用了,《三国演义》一百二十回全部跑通。我当时是真这么认为的,也不是没有根据。只是其中的来龙去脉,我那时没讲。
不是”大部分的书”,是整本书。
六月,也就是写那篇文章之前几个星期,我把阅读智能体写下的所有片段,一回一回首尾接起来,再跟磁盘上的原文逐字比对。不是凭感觉看看差不多,而是实打实地数汉字。
结果是 15,753 个字,凭空失踪了,分布在 120 回里的 104 回。
这可不是哪个名字打错了一个字,而是整段整段的原文。第二十二回陈琳那篇讨曹的檄文,整篇没了。第四十六回草船借箭那场大雾,到处是窟窿。第三回丁原当庭反对的那番话,正好卡在两个片段中间,前后两段严丝合缝地接上,就好像他从来没开过口。
到现在想起来还让我不舒服的是,这件事本来是有人管的。代码里有一个审查的环节,专门看每一块文本:模型交回来的东西,到底有没有把原文盖全。它就躺在代码库里,却从来没有被调用过。函数写好了,流水线却从它旁边径直走了过去,模型交回来什么,就原样记下什么。
模型在”调用成功”的时候照样会丢内容,这再正常不过了。光看一份整整齐齐的 JSON,你是看不出来的。只有拿它回头去跟原文逐字对,才看得出来。
于是我把这个审查接了回去,加上重试,再加一道兜底:重试完还缺的,就当作旁白补回去。覆盖率从大约 89% 升到了 99.96%。七月我说”能用了”,说的就是这个版本。而我接下来犯的错,就是把 99.96% 当成了已经做完。
覆盖率,不等于真的覆盖到了。
你可以把片段里的汉字加起来,再把原文的汉字加起来,得到一个看上去很漂亮的数字。可这个数字不管字的顺序对不对,不管有没有一句话被念了两遍,也不管模型是不是”好心”把一句话改写了一遍,只要字数大致对得上就行。
我们是在把一本书念出来。少一个字,普通听众就听得出来。多念一句,是同一个毛病反过来:你会听见这本书结巴了一下。等我真去找的时候,全书有 24 句话会被念两遍;还有 3 句,模型给它们安了一个自己编出来的”某某曰”,把书里根本不是这个人说的话,塞进了他嘴里。至于改写过的句子,那就已经是另一本书了。
所以真正的检验,不是算一个总数,而是从头往后一段一段地走:这个片段是不是原文里的一段,是不是就在这个位置,顺序对不对,前面有没有已经用过。只要有一条对不上,就说明有东西是编出来的、被挪了位置的,或者是从这一页别处搬过来的。
我之所以在这一点上翻来覆去地讲,是因为我见过太多人拿一个百分比来安慰自己,包括我自己。画画那一头我也这么干过:审帧智能体的总数看着挺好,可我亲眼看得出来的毛病,它连一半都没抓到。同一个错误,换了个位置而已。
我不再要求模型做到一字不漏。
听上去像是放弃了,其实正好相反。
本地模型切分一回文言小说,永远做不到百分之百覆盖。不是偶尔做不到,也不是提示词写得再严厉一点就能做到。这一点我接受。可产品的要求不接受:原文的每一个字,都必须进到音频和字幕里,每一部小说都一样,这一条没有商量的余地。切得再漂亮、说话人认得再准,也换不回一句丢掉的原文。先后顺序是定死的:字第一,谁说的第二,停顿落在哪儿第三。
所以等模型把几次机会都用完了,还有一道确定性的工序,拿这些片段去跟磁盘上的原文逐字比对,把还缺的部分硬塞回去。它不会为了顺口去改写缺掉的那一段,只会把它接到旁边的片段上,或者把某个片段拆开,给它腾出一个位置。如果文字和说话人没法两全,它保文字,然后把这一条标出来,留给我去看。
这里有个小插曲,说起来有点好笑,但我想提一下,因为这种毛病,只有当你认定 99.96% 不算数的时候才会去找。最早那道兜底,不补长度不到 4 个字的缺口。剩下的是 167 个字,散成 76 个小碎片,落在 38 回里,几乎全是结构性的字眼:诗前面的”诗曰”,段落开头的”却说”,还有一个名字被切成两半,一半在切口这边,一半在那边。每一个碎片都短到在图表上可以忽略不计,可全是连孩子都能听得出少了的字。现在按整回做的那道比对,把这些碎片也一并补回去了。
《三国演义》,也就是我实际在用的这个版本,到八月初:120 回,回回一字不差。零缺失,零重复。这个数字,我接受。
然后,一个更好的模型把书改了。
再说一个,道理一样,只是多拐了一个弯。八月下旬,我在第二台电脑上(最后一篇会讲到它)试了一个大得多的模型来当阅读智能体。它切分得确实更好,可它也悄无声息地把小说校对了一遍:原文没有的后引号,它给补上了;原文只写一个”曰”,它在显示的文字里展开成”曹操曰”;原文里一个看起来像错字的名字,它也顺手改成了它觉得”应该”是的那个字。每一处改动都是好意,每一处也都违反了一字不改的原则。而且重试也没用,因为它每次都改得一模一样。
越大、越聪明的模型,反而越容易这么干,因为”帮你改好”正是它被训练出来的本事。修法还是一样,不是写提示词,而是再加一道确定性的工序:把每个片段跟原文对齐,把它当初认领的那一段原文,一字不差地还给它。
如果你做的不是古典小说,这件事跟你有什么关系?
说到底,是别把模型的自信当成约定。
阅读智能体是一个智能体,有自己的活儿、自己的模型、自己对”做完了”的判断。放着不管,它的”做完了”指的是”我交出了一份像模像样的 JSON”。这和”这份 JSON 就是这一回书”是两句完全不同的话。第二句话,必须由一个说不动、糊弄不了的东西来检查。在我这里,这个东西是跟磁盘上原文的逐字比对。在你那里,也许是一份数据结构的约束,一组测试,一次来回转换,或者一个哈希值。工具可以换,这个做法不变。
代价最大的,是我把第一个好看的数字信了那么久。好在我没有停在那儿,因为从一句丢掉的原文画出来的每一张画,画的都是一本我其实没放进去的书。
下一篇讲”会读、会说”的另一半:我想让旁白变成我自己的声音,结果摸到了一小堆录音到底能教会模型多少的上限。