周末刷完一部转描动画《Undone(抹去重来)》第二季,意犹未尽,顺手丢给大模型让它做个逐集解析,想看看它能不能接住我那套"科幻 vs 心理"的双线读法。

它接住了。而且接得极好:逐集结构工整,每集标【科幻视角】【心理视角】,证据两边列,结尾还客客气气地说"我倾向心理解读,但不封死科幻可能"。文笔专业,逻辑闭环,我几乎要收藏。

直到我自己对了一下官方分集表——集名、关键事件、人物线,几乎全是错的。

它把这部剧的第二季,和另一部画风相近、转描质感类似的动画在记忆里搅成了一锅粥,然后用一套极其顺滑的叙事,把错误讲成了仿佛真事。这不是普通胡说八道。对做 Agent 和 RAG 的人来说,这是一次教科书级的高阶幻觉现场。我反复想它为什么会犯这种错,越想越觉得,这事的底层和我们做 RAG 时踩的坑是同一个。

翻车现场:错得越像真的,越危险

我给的任务很简单:解析《Undone》第二季逐集剧情。

它输出的"逐集"是:医院、狂欢节之夜、卡米拉、贝卡、河流、拼图、葬礼……每一集都配了自洽的双线分析。问题是,第二季真正的分集表根本不是这套。它脑子里那套,是另一部相似转描动画的骨架——两代祖母、母系创伤、河流意象、拼图隐喻——被整个嫁接了过来。

最可怕的不是它编错,是它编得有结构:错得有框架(逐集、双线、主题升华一个不少),错得有细节(人物动机、伏笔、反转全都自圆其说),错得有分寸(结尾还"克制地"保留开放性)。如果你本身不熟悉这部剧,读完你会完全相信它。

这才是做生产级 AI 应用时最该警惕的一类——不是前言不搭后语的低级瞎编,而是专业度极高、逻辑闭环、只有底层事实是假的那种。

往深挖一层:它根本不是在"回忆剧情",而是在"续写一篇剧评"

前面三句话是结论。真正值得想的是:为什么模型会把两部小众动画搅在一起,而且搅得天衣无缝?我把它拆到了 transformer 这一层。

1. 它脑子里没有"分集表",只有 token 的共现概率

这是最容易被忽略的一点。很多人下意识把大模型当成一个装了百科的数据库,问它"Undone S2E3 叫什么",就像查 MySQL。根本不是。

自回归语言模型优化的唯一目标是:给定前面这段文字,预测下一个 token 是什么概率最大。它内部没有一张结构化的事实表,没有"Undone → 第二季 → E3 → Cave"这样的三元组存储。它压进去的是海量文本片段里,token 和 token 一起出现的统计规律。

所以当你问它"Undone 第二季第三集讲了什么",它不是去查表,而是在做"续写":前面写着’Undone 第二季第三集的标题是……’,下一个词写什么最像一篇像样的剧评?这个概率是被训练语料里所有讨论过这部剧、以及所有讨论过类似转描动画的文本共同决定的。

概率高 ≠ 事实对。 它选的是"最像剧评里会出现的那个词",不是"真实存在的那个词"。

2. 小众作品在向量空间里,本来就和相似作品挤成一团

这一点做 RAG 的人特别有体感——我们天天在外面的向量库跟它搏斗。

转描(rotoscoping)是个极小众的品类。训练语料里,“Undone"这个词出现的频次本就不高,它周围高频共现的词是什么?是"转描"“Rosa Salazar"“母系创伤"“墨西哥城”,以及——另一部同样转描、同样讲家族创伤、同样双线叙事的作品。

在模型内部的表示空间里,这两部剧离得极近,近到一个 prompt 同时激活两者的表示。这跟我们在向量库遇到的一模一样:两个语义高度相似的文档,embedding 距离近到 top-k 召回时经常串台。区别在于——外部向量库我们还能加个 namespace、加个元数据过滤把它们隔开;模型内部没有这个开关。你问它 Undone,它激活的是一整片"转描动画 + 家族创伤"的语义云团,分不清边界。边界在训练语料里就模糊,它不可能凭空切清楚。

更糟的是:Undone S2 这种很新、很小众的内容,训练语料里讨论量本来就少;而那部被串台的相似作品,如果恰好讨论量更大、剧评写得更"文学”,那么高概率词表整体就被后者拉走了。它不是"记错了”,是统计上真的更像对的。

3. “逐集解析"这个提示,直接把它切进了"剧评写作模式”

注意一个细节:我让它做的不是"查一下 Undone S2 分集名”,而是"深度逐集解析"。这个措辞本身是个强开关。

它收到这个提示后,激活的不是"事实检索模式",而是"影评写作模式"。在这个模式下,它被鼓励写出"狂欢节之夜"“河流"“拼图"“葬礼"这种有画面感、有隐喻、看起来很懂行的词——而真实集名"Cave(洞穴)““Painting(画)“反而干巴巴、不够"影评”。

换句话说:提示要求的东西越像一篇好文章,它越倾向于把文章写漂亮,而不是把事实写对。 模板补全机制在这里被拉满——它先激活"逐集影评长什么样”(每集一个小标题、双线对照、结尾升华),再往模板里填概率最高的剧情走向,空白处用同类作品的通用结构补齐,逻辑冲突自动抹平。

4. 最致命的一步:第一步选错,后面就被 context 锁死了

这是我觉得最反直觉、也最值得工程上警惕的一点。

自回归生成是一步一步来的,而已经生成的 token 会成为下一步的条件。它一旦在 E1 落笔时选了那部错误作品的骨架,这个错误骨架就被写进了上下文。从 E2 开始,它不是在"独立回忆 E2”,而是在"续写一篇前面已经讲了错误 E1 的剧评”——后面每一集都在被自己第一步写错的那个前提条件化。

于是出现了一个看似矛盾的现象:整篇逻辑自洽。不是因为它知道真剧情,而是因为它把一篇连贯的假剧评写完了。错误一旦进入上下文,就会自我强化、自我辩护,后面的"双线分析"“伏笔回收"全是围绕错误前提自洽地推下去的。

这也解释了为什么长 Chain-of-Thought 有时反而放大幻觉:开头一步错,后面洋洋洒洒的推理都是在给错误前提圆场,而且圆得越顺,你越信。

给 Agent 开发提个醒:幻觉是要分级的

想通上面四层,再看怎么防就清楚了。我现在评幻觉不只说"它又瞎编了”,而是分级,不同级别对应不同手段。

最低级的,前言不搭后语、逻辑崩坏,扫一眼就露馅,不用太防。

中间级的,细节错:人名、数字、时间线歪了,但骨架对,加引用、加校验能压下去。

最危险的是这次这种——我管它叫全局事实置换:框架对、结构对、口吻对,连你要的"双线分析"都对,唯独底层那部作品、那个案例、那个业务实体是错的,是从隔壁相似领域整个搬过来的。它在你不熟悉的领域里伪装成专家。前面两种错会"露怯”,这一种错得越专业你越信任,越不会去核对——给客户做方案、给医生做辅助诊断、给求职者做岗位匹配的 Agent,犯的要是这种错,后果远比一句瞎编严重。

落到工程上,我改了三件事

机制想通了,对策就不是"写个提示词让它别编"那么隔靴搔痒了:

第一,先检索后生成,而且锚点要在生成前注入,不是生成后纠错。 既然错误一旦写进上下文就自我锁死,那光靠生成完再让它自查没用——它会顺着错误前提继续自洽。正确做法是在生成之前就把正确事实锚点(真实分集名、真实案例原文)喂进 context,让它照着实的组织语言,而不是写完再问它"你确定吗”。没检索命中,宁可回"资料不足",也不许裸模型自由发挥长文。

第二,向量库和知识源要做隔离,正面解决"相似就串"。 这次翻车的根是相似题材在表示空间里挨太近。工程上对应:不同业务域、不同客户、不同项目独立索引或强 namespace/tag 过滤;语义特别像的文档(两个版本手册、两个相似案例),检索时强制加元数据过滤,别让 A 业务的片段进 B 业务的上下文。混叠这件事,在数据层拦,比在提示词里求它"你别串"管用。

第三,结构化长文先过一道事实锚点校验。 既然"逐集解析"“周报"“复盘"这类强格式任务最容易被模板牵着走、把事实检查预算都花在满足格式上,那就显式加一步:生成前先列核心实体清单(剧名、人物、集名、关键事件),拿这几个硬实体去和检索原文对,对不上就停。这步看似多此一举,但它正好卡在"全局事实置换"成稿之前。

最后

以前觉得做大模型应用,是想办法让模型更聪明、说更多。这次翻车反倒让我想明白了:模型负责文采、逻辑、表达,工程负责真实、准确、边界。所谓 Agent 工程,本质不是把模型训练得更博学,而是一层层加锁,告诉它哪些地方不许发挥。

它会把人类"记混了还一本正经"这个毛病,放大成规模化、结构化、产业化的幻觉。而我们这些做工程的,真正的活儿就是在它下笔之前,把正确的锚点放到它够得着的地方。