Bend2这个编程语言想当数学证明工具,结果差点在自己最得意的设计上翻车。AI助手Fable揪出了一个藏得比地铁站还深的漏洞,还顺带证明了设计者自己都没发现的后门。这不是AI替你写代码的故事,这是AI替你“找茬”的故事。
数学证明语言第一条铁律:你的程序必须能停下来
你玩游戏卡死过吧?屏幕定格,鼠标转圈,只能强行关掉。这事在游戏里顶多让你摔个鼠标,但在数学证明语言里,程序停不下来就是世界末日。
简单解释一下证明语言是干啥的:你写一段代码,它不是用来算工资或者P图的,它是用来证明数学定理的。比如你想证明“所有偶数都能分成两个质数之和”,你把这证明翻译成代码,让计算机帮你检查每一步逻辑对不对。如果代码通过了,那这个定理就是真的。
但这里有个天大的坑:如果这个语言本身有漏洞,能写出永远跑不完的程序,那整个系统就废了。
为啥?因为在一个能死循环的语言里,你可以玩一个极其阴险的把戏。想象一下,你写了个程序,它先进入死循环,然后在死循环的某个分支里,你塞了一句“证明1=0”。由于程序永远跑不到那个分支,类型检查器就认为这代码没问题。但理论上,只要能证明1=0,你就能证明任何命题,包括“外星人今晚会来我家吃饭”这种鬼话。
这就是为什么所有正经的证明语言,第一件事就是确保所有程序都能在有限步内结束。这叫“停机性保证”。
常规操作:把循环和递归都关进笼子里
那怎么保证程序一定停下来呢?最直接的办法就是:不让你写循环。
对,你没听错。在Lean、Agda这些证明语言里,你找不到while或者for。压根没这功能,你想用也用不了。
但光禁循环还不够。递归这玩意儿也能让程序跑飞。你写个函数:
def f(x): return f(x)
这代码啥都没干,就一直在喊自己,参数永远不变,停不下来。
所以证明语言又加了一条规矩:递归调用的时候,参数必须越来越小。比如算阶乘,f(5)调用f(4),f(4)调用f(3),一路降到f(0)就停了。这叫“结构递归”,每条递归调用都必须带着一个证据,证明参数确实在变小。
好,循环没了,递归也管住了。这下总安全了吧?
隐藏Boss:一个既没循环也没递归的死循环程序
接下来这个操作,可能会让你觉得编程语言是个大骗子。
你看这段Python代码:
evil = (lambda f: f(f))(lambda f: f(f))
你运行一下,电脑直接卡死。
但你仔细看这代码:没有while,没有for,没有递归(函数没有调用自己)。那它为啥会死循环呢?
因为它用了一个叫“λ表达式”的东西。简单说,就是“匿名函数”。你可以写一个函数,不给他起名字,直接用。这本来是个很方便的功能,但有人发现了一个骚操作:写一个函数,它接受一个参数f,然后调用f(f)。再把这个函数自己传给自己。
这就变成无限套娃了:f调用f,f又调用f,永远停不下来。
这玩意儿在编程语言理论里叫“自我复制的λ项”,听着挺科幻,实际上就是个死循环的变种。而且它比循环和递归更隐蔽,因为它根本不像是会死循环的样子。
更狠的是,在数学证明系统里,这种自我复制能直接造出逻辑悖论。1901年罗素就干过这事。当时数学界用的基础理论叫“朴素集合论”,罗素问了个问题:所有不包含自己的集合,它们组成的集合,包不包含自己?这个问题直接把数学大厦的地基炸了。整个数学界都慌了神,因为这意味着所有数学证明都可能建立在沙子上。
今天的证明语言对付这种自我复制,用的是打补丁策略。Lean搞了个“正性检查器”,专门去抓某些长得危险的递归类型。又搞了个“宇宙层级”,把类型分成好多种,让“所有类型的类型”这种自指说法无法成立。这些补丁确实能挡住漏洞,但也特别烦人——它们会误伤很多正常的程序。
比如你想写一个高效率的解释器,用那种优雅的“高阶抽象语法”写法,对不起,正性检查器会一巴掌把你拍死。你得用各种极其扭曲的变通方案。这就让这些证明语言离“正常人能用的编程工具”越来越远。
Bend的骚操作:直接禁止函数被复制
Bend这个语言的想法不太一样。它的目标是:既能当证明语言,也能写真实世界的程序。所以它不想用那些把人捆得死死的补丁。
设计者的思路是:循环我禁了,递归我也限制了参数递减。现在还剩下的隐患就只是那个自我复制的λ表达式。那我把它复制的能力直接ban掉不就行了吗?
具体怎么ban呢?Bend用了“量化类型理论”里的一个功能。这个理论里有个概念叫“线性”,意思就是:有些变量只能用一次。
比如说,你写了个函数f = (x) => x + 1,在普通语言里,你可以把这个f复制一百遍到处用。但在Bend里,如果f被标记成“线性”,那你就只能用一次。用完就没了,没法复制。
设计者说:我把所有λ表达式都强制设成线性的。那你就没法写f(f)这种东西了,因为f(f)意味着f要被复制一份传给f自己当参数。复制被禁了,自我复制的λ项就压根造不出来。
这个方案比Lean的补丁简洁多了。一条规则搞定所有问题,不用搞什么正性检查、宇宙层级。而且Bend本来就为了实现高效的数组操作而实现了量化类型理论,所以这条规则几乎是“买一送一”附带的。
听起来完美,对吧?
理论有多漂亮,实现就有多打脸
逻辑上这套方案天衣无缝。但问题出在代码实现上。
量化类型理论这个东西,有一篇正儿八经的学术论文。论文里为了让语言更好用,允许在某些地方“放松”一下线性检查。
啥叫放松呢?就是:大部分地方你得严格遵守“用且只用一次”,但在某些特定位置,可以稍微通融一下。比如有些地方不可能出现危险的操作,那就让你多用几次,省得写代码时处处受限制。
这个放松检查在别的语言里一点问题都没有。比如Idris2也用这个理论,但人家不靠这个来保证停机性,所以放松不放松的无所谓。
但Bend不一样啊!Bend可是指着这个规则来保命的。那些放松检查的位置,在设计者脑子里应该是不存在λ表达式的。但代码是程序员写的,程序员也是人,人就会犯错。
结果就是,Bend2的实现里,有些放松检查的位置实际上能出现λ表达式。这等于在病毒的隔离区开了扇门,门口还没人站岗。
这个漏洞藏得有多深呢?深到设计者自己都没发现。他当时满脑子都是论文里写的“这个位置可以放松”,完全没意识到这个“可以”在他的系统里意味着什么。
AI助手Fable:我不是来写代码的,我是来挑错的
这时候Fable出现了。
Fable是个AI助手,但它跟那些“帮我写个贪吃蛇”的AI完全是两码事。设计者的用法是:把我整个终止性证明的论证过程给你,把Bend2的源代码给你,把量化类型理论的原始论文也给你。你给我审查一遍,看看有没有逻辑漏洞。
Fable就真的去啃这些东西了。
它读完了设计者的终止性论证,翻遍了量化类型理论的论文,又把Bend2的源码一行一行地审计。然后它跑回来说:老板,出大事了。
Fable发现了那个放松检查的后门,并且证明了这个后门能用来构造自我复制的λ项。它甚至给了完整代码,展示怎么在Bend2现有的实现下造出一个死循环程序。
这还不算完。因为在一个证明语言里,只要能造出死循环,就等于能证明一切假命题。Fable直接给出了一个“假命题证明”的完整推导过程。换句话说,它用设计者自己的语言,证明了这个语言是不安全的。
你想想这个画面:你花了好几年设计一个保险箱,号称全世界最安全。然后你的AI助手跑来说:“老板,我研究了你的设计图和所有材料报告。这个锁芯的第三颗弹子,理论上应该用不锈钢,你实际用的是黄铜。我拍了段视频,用一把螺丝刀就把保险箱捅开了。你看,这是开箱全过程。”
这已经不是帮忙了,这是救命。
漏洞怎么补:把类型世界劈成两半
Fable也提了好几个修补方案,但设计者试完之后觉得都不太行。有的太复杂,有的会破坏语言的其他特性,有的等于把之前禁掉的东西又放回来了。
最后设计者自己想了个办法:把类型系统里的“类型”拆成两种。
一种叫“任意类型”。这种类型里啥都能装,包括函数、包括那种可能自我复制的东西。这种类型走严格检查通道,所有线性规则都必须遵守,一点商量的余地都没有。
另一种叫“低阶值类型”。这种类型里只能放简单数据,比如数字、布尔值、字符串这种。这些数据跟函数没关系,跟自我复制也没关系,所以在这里可以放松检查,允许变量多用几次。
这么一拆,那些放松检查的位置就全被限定在“低阶值类型”的范围内。在这些位置里,压根就不会出现λ表达式,所以就算放松了检查,也没东西可以被复制。而在会出现λ表达式的位置,检查依然是铁板一块,函数就是不能被复制。
这招就像学校食堂分两个窗口:一个窗口卖快餐,排队快,但只卖套餐,没得选;另一个窗口卖小炒,可以定制,但排队慢。你把那些不重要的午餐全赶去快餐窗口,小炒窗口的人就少了,整体效率就上去了。但在Bend这里,“不重要的午餐”正好也不会出事,所以慢一点也安全。
这事最邪门的地方在哪儿
你仔细品品这整个故事的诡异之处。
Fable是个AI,它干了啥?它读论文、读源码、做逻辑推导、找出漏洞、甚至给出了利用漏洞的完整代码。这些活儿放以前,得一个博士级别的形式验证专家干上好几个月。
但更邪门的是,Fable提出来的修补方案全都不行。设计者试了之后发现,AI想出来的修补方法要么会让语言变得跟Lean一样严格(那就违背了Bend的设计初衷),要么会在别的地方开更大的后门。
最后还是那个学了十年类型理论的人,自己想出了那个“把类型劈成两半”的优雅解法。
这就有意思了。Fable在“发现问题”这个环节上,表现出了超越人类的敏锐度。它能在一个庞大系统的犄角旮旯里,找到人类设计者自己都没注意到的细节偏差。但在“解决问题”这个环节,它就变成了一个只会提馊主意的实习生。
这就像你的AI助手是个顶级侦探,能发现所有犯罪线索,但怎么审判、怎么判刑、怎么修补法律漏洞,它还得指望你。
那个设计者学了十年,不是学怎么让AI替他思考。他是学了十年,才攒够本事能让AI替他检查自己有没有想错。
现在外面一大堆人,看见AI能写代码了,就嚷嚷着“程序员要失业了”。但Bend这个故事告诉我们,写代码只是最表层的东西。真正的难题是:你写的这段代码,在一个规模庞大的系统里,十年后会不会突然变成一颗炸弹?你依赖的数学证明,会不会因为某个角落的放松检查而整个崩塌?
这些东西,AI暂时只能告诉你:你看,这儿出问题了。至于怎么修,它还得指望那个学了十年的人。
所以下次你再看见有人说“AI能替代数学家了”,你就想想Fable找出的那个放松检查漏洞。AI确实挺能找茬的,但它更擅长的是用极其精准的方式告诉你:你错了。
至于怎么才能对,你得自己来。