为什么人类程序员仍然比大语言模型(LLMs)强大? 作者:antirez
这是一个简短的小故事,告诉你为什么人类的编程能力仍然远远领先于当前的AI技术。请注意,我并不是反AI的人,熟悉我的朋友都知道这一点。我日常都会使用大语言模型(LLMs),包括今天也是如此。当我需要快速验证自己的想法、进行代码审查、了解是否有更好的实现方法,或者探索一些自己并不完全擅长的领域时,我都会用到它们。(两年前,当LLMs还没有流行起来的时候,我就曾经专门写过一篇博客,讲述自己如何用LLMs辅助编码。如今也许是时候再写篇新文章更新一下了,不过今天的重点不在这里。)
不过,我想强调的是,尽管当前的AI技术非常有用甚至很棒,但和人类的智慧相比,它依旧差得太远了。我特别想指出这一点,是因为最近关于AI的讨论越来越极端,很难有中立理性的观点。
遇到的问题
今天我在为Redis开发一个叫做向量集合(Vector Sets)的功能,遇到了一个非常棘手的Bug。事情是这样的:
在我之前离开Redis期间,同事们新增了一个防护功能,用来抵抗Redis数据文件(RDB)和恢复命令(RESTORE)的数据损坏问题,即使数据校验看起来是正常的。这个功能默认关闭,但用户如果想增强安全性,可以主动启用。
但这引发了一个巨大问题:
为了高效地保存和恢复数据,我把HNSW向量搜索算法的图结构序列化了,而不是直接保存向量元素的原始数据。如果直接存储原始向量数据,在加载时重新构建索引会非常慢(可能慢100倍以上)。所以我选择了一个巧妙的技巧——直接保存节点之间的连接关系(用整数表示节点编号),再在加载时恢复指针。
但问题在于,如果保存的节点连接数据出现了随机损坏,由于我改进的HNSW实现会强制节点之间的链接是双向的,以下问题就可能发生:
因此,加载数据之后,我必须检查每个连接是否双向有效。然而,用最简单的方法实现这个功能需要花费O(N²)的时间复杂度,这对于大数据量来说非常糟糕。
人类 vs 大语言模型(LLM)
一开始,我用最原始的方法实现了这个检查,虽然问题解决了,但加载2000万个向量的大型数据集时,速度从原来的45秒延长到90秒以上,这显然不能接受。
于是,我打开了Gemini 2.5 PRO(谷歌的LLM),向它询问有没有更好的方案。
Gemini建议了一个最好的方案——对节点的链接数组进行排序后再用二分搜索提高效率。这一点我早就知道,但链接数组长度只有16或32时,排序可能并没有什么性能提升。所以我继续问它,还有其他方案吗?Gemini表示没有更好的方案。
然后我告诉Gemini一个自己的想法:
我们在扫描链接时,每次看到节点A连接节点B,就把链接信息以A:B:X的格式存入哈希表(这里X是层级信息,A>B保持顺序统一)。第二次遇到同一链接时,就从哈希表删除它。最后如果哈希表非空,就说明有链接存在单向链接的问题。
Gemini认为我的方案不错,但担心构建哈希键的时间损耗,比如使用snprintf()
来构建哈希键。于是我提醒它,这里完全可以用memcpy()
来直接拷贝指针到固定大小的键中,Gemini同意我的想法。
突然,我意识到还能更进一步:
“等等!” 我告诉Gemini:“我们根本不需要哈希表!可以用一个固定的累加器,每次处理一个链接(A:B:X,总共12个字节)时,我们都对这12字节数据进行异或(xor)运算。如果链接存了两次,它们互相抵消,最终若累加器不为零,就知道链接出现了问题!”
但我同时也提醒Gemini,这种方案可能出现碰撞(多个链接偶然导致异或为零),并要求它评估这种风险。
Gemini对我的想法印象深刻,但也提出了担忧:因为指针通常结构类似,如果出现3个错误链接(L1、L2、L3),可能L1和L2异或后的结果刚好和L3相同,从而导致误判。此外,我也考虑到内存分配器分配的地址往往比较容易预测,可能容易被攻击者利用。
接着我向Gemini询问改善的方法,Gemini却想不出更好的方案。
于是我再次提出了改进方案:
/dev/urandom
生成随机种子S
,与链接信息一起构造为S:A:B:X
。murmur-128
)对S:A:B:X
哈希。Gemini对此方案非常满意,认为这种方法不仅大大减少了误判的可能性,也极难被攻击者利用(因为随机种子S
未知,而且指针难以被操控)。这本就是一种额外安全的保护措施,默认关闭,性能损耗也小。
我写这篇博客时刚完成了这个方案的评估,还没最终决定是否正式采用(但很可能会用)。不过,这次的经历清楚地表明:
人类在创造力和思维的“跳跃性”方面,仍然远远领先于AI。我们能提出许多“怪异但有效”的解决方案,这对LLMs来说太难了。但LLM确实也起到了重要的辅助作用,也许正是因为有了这个“智能小伙伴”的启发,我才得以想到这样的点子吧!