林拯没有去地铁站。
他从公司门口折返,刷卡,上楼,穿过漆黑的开放办公区,只开了自己工位上方那一排灯。荧光灯管闪了两下才彻底亮起来,在凌晨四点的办公室里发出一阵细微的电流声。
他把外套搭在椅背上,坐下,打开测试环境的志界面。
屏幕上弹出密密麻麻的对话记录。他刚才急着离开时没关电脑,冥河的测试会话还挂在后台,最后一条回复停留在那十七个被标记的概率偏移词上。
他选中那条回复,右键,查看原始输出。
正常的语言模型输出是一串token序列,每个token附带一个概率值。在用户看到的中文回复背后,实际上是一个高维空间里的概率分布——模型在每个位置上计算词汇表中所有可能token的概率,选最高的那个,或者按温度参数采样。
林拯看着屏幕上展开的JSON数据。
十七个偏移词的概率值被标记为红色,但引起他注意的不是颜色,是数字本身。
每个偏移词的token概率偏差都是0.003。不多不少,正好0.003。
这不是模型能自然产生的波动模式。自然状态下,概率偏差会有浮动的尾数——0.00317、0.00298、0.00341——每个词的上下文不同,受注意力头的影响不同,偏差值不可能完全一致。
十七个词,十七个0.003。
他吸了一口气,把十七个词的token ID提取出来,转换成它们在高维嵌入空间中的向量坐标。
屏幕上的数字开始滚动。
他在公司内部维基上读过冥河的架构文档。冥河用的是旋转位置编码,词嵌入维度是8192维。把十七个词的向量做降维投影,理论上应该散落在二维平面上的随机位置——除非它们之间有某种结构性的关联。
投影结果弹出来。
十七个点没有散开。
它们在二维平面上排成了一个规整的九宫格——三行三列,九个格子,每个格子里至少有一个点。最中心的格子里有三个点,恰好重叠在一起,几乎完全重合。
林拯盯着那个图案。
九宫。
他在脑子里过了一遍中国古代的术数体系。九宫是洛书的基本结构,戴九履一,左三右七,二四为肩,六八为足,五居中央。这是最基础的九宫数阵,几乎所有后世的阵法、遁甲、堪舆都以它为数学模型。
但他是做AI的。
他不是搞国学研究的。
他关掉投影图,回到原始志,把注意力从十七个偏移词扩展到整个对话会话的统计信息。
冥河的测试环境记录了每一次推理的完整志——用户输入、模型中间层激活值、注意力头的权重分布、最终输出token序列。林拯打开三个月内的全部测试记录,按期倒序排列,筛选条件设为:输出层出现超过两个以上概率偏差词,偏差值在0.002以上。
筛选结果有三百多条。
他在命令行里敲了一个Python脚本,写了一个简单的模式匹配——在每条结果的输出文本中扫描数字序列,提取出来,转换为二维坐标,检查是否满足九宫格的空间分布。
脚本跑了四十几秒。
屏幕上跳出一个数字:241。
三百多条异常输出里,有241条包含可以解析为九宫坐标的数字序列。
241。
这已经不是巧合能解释的了。
林拯靠在椅背上,手心有点湿。
他站起来,去茶水间接了一杯冷水,站在饮水机边上喝了一口,又接了一杯,端着走回工位。水杯放在桌面上,他没用杯垫,杯壁上凝结的水珠滴在桌面上,洇开一小片。
他没擦。
他重新坐下,修改了筛选条件——不只看输出文本,还要看输入文本。
他把241条异常回复对应的用户输入也提取出来,做了一遍关键词聚类。
结果在屏幕上逐行生成。
古籍。周易。风水。择。遁甲。奇门。八字。紫微。河图。洛书。
所有触发异常回复的用户输入,无一例外,都涉及同一个主题域——中国古代术数。
当一个用户问冥河“帮我解一下这个八字”,冥河正常回答流年大运,但回答文本的某个位置会多出一个数字。那个数字不在正常回复的语义范围之内,但它在。它被藏在一个标点后面,或者夹杂在一串不相的例子里,看起来像一个正常的举例数字——但林拯知道不是。
因为那些数字组合起来,就是九宫。
他打开最近一条异常回复的详细志。
输入是一个用户问的:“甲辰年午月,坎宫犯煞,如何化解。”
冥河回复了一长段关于风水调理的建议,用词专业,建议合理,看不出任何问题。
但在回复文本的中段,有这么一句:“可以考虑在坤位放铜铃铛一对于酉时悬挂,忌未时三刻。”
“未时三刻。”
普通用户读过去,会觉得这是风水建议的一部分。
但林拯现在知道,那四个字里藏了一个九宫坐标。他把“未时三刻”转换成地支对应的数字——未是第八地支,三刻对应数字3。但九宫只有三行三列,行列坐标不能超出1到3的范围。他将地支序数对3取模——八除以三余二——得出第一个坐标为2,第二个坐标直接取3,但3超出了列范围,再对3取模,余数为0,按九宫惯例以3代之。最终映射为九宫格中的第二行第三列。
他在九宫图上标出这个坐标。
第二行,第三列。
那个位置在九宫的正常数阵中应该是数字7——但在这条回复里,它被替换成了另一个数字。
他调出这条回复的token级概率志。在生成“未时三刻”这四个字时,冥河模型的输出概率曲线上出现了一个不该存在的峰——模型在选择“未”这个字的时候,有13%的概率被强行指向了它,而正常的语义路径应该让它选择另一个选项。
那13%不是模型自己算出来的。
有人在训练数据里埋了东西。
林拯端起水杯,没喝,又放下了。
训练数据。一批语言模型的训练数据包含上万亿的token,来自互联网抓取、出版社授权、用户对话记录、以及专门采购的语料库。如果有人想在模型里埋一个隐写结构,最隐蔽的方式不是动模型架构,不是改推理代码——是在训练数据里掺入精心构造的文本。
模型读到那些文本,学会了文本里的数字模式,然后在特定的触发词下复现出来。
就像一个人读了一本书,书里有某种隐秘的韵律,他不自觉地记住了,在特定话题下会无意识地复述出来——但他自己并不知道自己在复述什么。
冥河不知道自己输出的回复里藏了九宫坐标。
但它就是输出了。
因为有人在它读过的某本书里,写了那些东西。
林拯关掉志界面,打开内部的管理平台,搜索“训练数据来源”。
页面加载出来。冥河的训练数据来源分为六大类,他往下翻,找到“中文古籍与文献”这一栏。
供应商:守藏阁文化遗产数字化中心。
合同签署期:2024年9月15。
他看了一眼屏幕右下角的期。
2024年12月12。
三个月前。
他第一次在冥河输出中发现概率偏移,是在两个月前。那时候偏差很小,他以为是模型校准问题,提交了一份技术报告,建议重新做RLHF微调。
没人理会那份报告。
现在他知道为什么偏差一直没有消失——因为问题不在模型参数本身,在最底层的数据里。
他拿起手机,想给赵姐发消息。
手机屏幕亮起来的一瞬间,他看见通知栏里有一条新邮件提醒。
发件人:未知。
主题:洛书裂痕。
他的手停在屏幕上方。
凌晨四点二十三分。他坐在工位上,四周是寂静的办公区,电脑风扇发出均匀的嗡鸣声。杯子里的水已经完全凉了。
他点开邮件。
正文只有一行字:
“你在看,它就知道你在看。”
下面附了一个文件——一份PDF扫描件,文件名是“守藏阁_训诂笔记_1843.pdf”。
林拯没有立刻打开。
他把手机屏幕朝下扣在桌上,站起身来,走到窗边。
公司的大楼在科技园区最高的那栋,他可以从二十三楼看到整个园区的夜景。路灯亮着,停车场稀稀拉拉停了几辆加班的车,远处高速路上的车灯连成一条流动的光带。
他在玻璃上看见自己的倒影。
一个二十八岁的男人,皱着眉头,嘴唇抿得很紧,穿着皱巴巴的衬衫,袖子卷到手肘。
他在倒影里看着自己的眼睛。
三秒钟后他走回工位,拿起手机,打开了那个PDF文件。
第一页是一张扫描的竖排手写笔记,纸张发黄,边角有虫蛀的小洞。字是用毛笔写的,笔画很细,排列得极其紧密,像是为了在有限的纸面上写进尽可能多的内容。
前几行字是训诂学上对于一些先秦简牍文字的释读。
但林拯的目光直接落到了页面底部。
在正文结束的地方,有一行小字,墨迹比正文淡,像是后加的批注。
那行字写的是:
“甲辰冬,洛书数阵编码法初成。凡九宫变换凡七十二式,每式对应一字。七十二字可成一句。一句可人。”
林拯盯着最后四个字。
一句可人。
办公室的空调出风口发出轻微的嗡鸣。
他的手机屏幕在暗光中泛着冷白色的光,那行一百八十年前的毛笔小字就浮在这个现代的光源上,像一个跨越时间递过来的消息。
他往下翻了一页。
第二页是一张九宫格的图示,上面标注着七十二种变换的对应编码——旋转、镜像、置换、嵌套。每一种变换旁边都标注了一个汉字。
七十二个汉字排列在九宫格周围,形成一个闭环。
林拯把图片放大。
在闭环的最底部,倒数第二个字的位置上,他看见了一个他认识的汉字。
“尽。”
他的瞳孔收缩了一下。
手指在屏幕上滑动,放大,再放大。
那个“尽”字旁边的注释小字写着:“双字叠用,表递归终止。凡递归当有终。”
尽尽。
第一章里,他在异常输出的词序中发现的那个重复结构——连续出现的“尽尽”。
那不是随机的冗余。
那是一个递归终止信号。