Lost One的手性碳

当高中尖子生用「手性碳验证码」维持自己的优越感幻觉时,我用神经网络(外加一个经典算法)打碎了这套正在被悄悄正常化的社会达尔文主义话术。

本文是我在开发「Lost One的手性碳 - 自动解决『手性碳验证码』」之后写下的幕后故事。

项目现已公开发布在 https://lostone.catme0w.org

它既是一个验证码识别器,也是一个讲述门槛与筛选的实验装置。


文中所引用的聊天内容均已根据原意改写与匿名化处理。


“群警察出动一下吧😅”他在私聊里写道,

“群警察不出动是放弃理想了吗,当初训我不是很有理吗😅”

昔日的好友反目成仇。通知响起,划入聊天,以往的对话已经尽数消失,只剩下这样的两条消息。


我没有回他。几天前,他刚因为我争论“手性碳验证码”而勃然大怒,删除了所有我的讨论。

几天后,他说我“网暴”了他。再然后,我又收到了这样的消息。


我从未骂过他一句。

只是指出——“被手性碳筛选掉的初中生、不选化学的高中生、上了大学或者工作以后已经忘记的那些人,我替他们发声”。

第一次遇见

“欢迎 catme0w 申请加群,本群已开启验证,请在 600 秒内选择图中所有包含手性碳的区域,并点击提交按钮。超时或验证失败将会自动拒绝申请……”

你滚蛋。我在心里想。

原始记录截图


我盯着屏幕上那张分子结构图。一个碳原子连着四个不同的基团——这是手性碳的定义,我还记得。但具体是多大范围算一个基团?我完全说不上来了。

我早就不是高中生了,这么细枝末节的知识,早就还给老师了。

让我现学?绝无可能——年代久远,我早已记不清这个群是什么主题了,但我很确定,它一定和化学无关。

这不过是服从性测试而已。

我关掉了申请窗口。


几年后的某一天,我和几个朋友在小群里闲聊。话题不知怎么就聊到了Telegram的验证码bot。

“什么验证码才能最有效地拦住广告?”有人提问。

有人提出:“手性碳验证!”

状态栏里顷刻间多出了3个正在输入。

“我化学扔了三个月就已经不会了。”

“我真诚地认为这种东西是对一个人的基本尊重的缺失。”

他立刻遭到了剩下其他人的围攻。大家都很讨厌这个东西。

我说起了自己的经历:“我遇到这种东西扭头就走,不惯着。”

一位有着生物信息学背景的研究生说,它的非标准渲染完全丢失三维拓扑特殊结构,产生出对人类都无解的图像。

一位高中生有些激动:“凭什么因为我化学不好,就不能参与计算机讨论?”

大家的观点各不相同,但汇总起来,却都是同一个声音:

“绝对不行!”


有人发了一张截图到聊天框里。

是某个群的简介:

“抱怨入群验证的一律拉黑”

显而易见,这个群在使用手性碳验证码。

当时讨论的氛围已经有些紧张了,这张截图像是一颗火星,点燃了我们积累已久的不满。

“能制作一个专用于手性碳题目的机器人吗?“有人问。

“我有想法。“我说。


我当时没想太多。只是觉得,一定要消灭这种东西,无论通过什么方式——它就是不正当的。

出于一种朴素而纯粹的正义感。

研究生率先掀了桌子。作为正经学过有机化学,混过几年实验室的人,他立刻提出了识别流程的基础架构:从图像识别出分子式,交由RDKit重新规范化和绘图,然后判断手性碳。

“亏我本科课题做的是分子对接。”他说。

几十分钟后,他发来了第一份原型代码,使用OSRA做图像识别。

“我的任务结束了,”他说,“但还有很多其他要做的。”

OSRA对非标准渲染的识别不佳,RDKit产出的图片也和原始验证码有明显差异。距离完全自动化还差得很远。

“也许我们可以用一点注意力。“有人说。

我接手了这个计划。

纵使我们各自有着不同的理由——有人是为了技术挑战,有人是为了出一口气,有人单纯觉得这个验证码不公平——我们却仍然凑在了一块,协作这个我们目前为止争议最大的项目。


两星期后

某天早上,我醒来后习惯性地登录服务器。

训练脚本已经退出了。

没有"Training complete”,没有庆祝的烟花。只有一个闪烁的光标,和重新出现的shell。

尽管毫不起眼,但它标志着第一个可以稳定识别手性碳验证码的模型诞生了。

我望着终端,突然不知道该作何反应。


我们真的做到了。

原始记录截图

冲突

模型训练接近尾声时,我们开始做技术以外的事情:市场调查公众对这个验证码的态度,以及为我们的最终成品编写文案。

我找了几个群组去询问其他人的态度。从几个大型群组了解到,他们普遍敌视这种验证码。

这很好。我在心里想。我的基本盘还是很大的。

在众多我前去询问的群组里,有一个群很特殊。它只有几十人,是我一个好朋友的私人群。

这里曾经使用过手性碳验证码,但后来不用了。在这么一个没有严肃话题的小型粉丝群使用这样的验证码很不寻常,我也因此好奇这里其他人以及群主的态度,以及他们最终放弃的原因。

我抱着有些复杂的心情发了一张图,一个无解的手性碳验证码图片,询问他们的的意见如何。

很快,几个群友开始回复。他们的态度和我在其他群看到的一样——普遍排斥。

“我高中没学化学”

“这种逆天的CAPTCHA本就不应存在”

我顺着他们的话继续聊了下去,话题也逐渐走远了——从手性碳验证码bot判定和绘图逻辑的错误,聊到手性碳在高中化学考纲中的意义,聊到如今中考的五五分流,乃至四六分流……

安全下车——我没成过街老鼠。我这样想着。


然后,群主出现了。

聊天窗口闪了一下。

几十条消息,几个小时的讨论,全部消失了。从第一个关于手性碳的消息开始,一直到中考分流结束的所有对话,一条不剩。

我盯着空白的聊天界面,脑中一片恍惚。

不是因为我们说了什么过分的话。仅仅是因为我们质疑了手性碳验证码。

群主发了一条消息:

“NeoAuthBot的目的是群验证,而不是做出来让你们去破解。”

我试图解释,我们的初衷不是针对他,而是这个验证码本身存在问题——

“我使用手性碳验证是基于WA、QA两个大群长期的使用情况选择的,QA群内的交流环境有目共睹,事实证明Neo在筛选群成员方面有一定的正面作用。”


突如其来的打击激怒了在场的人。很快,场面就变得无法控制了。

“如果你们有不满,请告诉我一个你们满意的Bot,而不是莫名其妙的忽然把你们的研究成果在这里发一片”

“你们的批评确实是非常正确,但是初中的道德与法治就学过,批评要有建设性。”

“nmbot没有办法有效拦截,Rose会有无法pm的问题,基于recaptcha+miniapp的验证会导致TgX用户被拦于门外……”

“本身就是筛一波人类控制的广告机器人,所以点击按钮、算数、读图验证毫无作用”


其他的验证毫无作用?

我仔细回忆自己一年前某天说过的话,通过一些关键词,找到了当时随手发给一个朋友的代码片段。二十行。一年前随手写的二十行代码:

import cv2
import numpy as np

img = cv2.imread(r"C:\Users\CatMe0w\Downloads\photo_2024-09-08_04-52-18.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.imread('template_asterisk.png', cv2.IMREAD_GRAYSCALE)
h, w = template.shape

rects = []

for s in np.linspace(1.5, 0.5, 15)[::-1]:
    resized = cv2.resize(gray, (0, 0), fx=s, fy=s)
    if resized.shape[0] < h or resized.shape[1] < w:
        continue
    for pt in zip(*np.where(cv2.matchTemplate(resized, template, cv2.TM_CCOEFF_NORMED) >= 0.7)):
        rects.append([int(pt[1]/s), int(pt[0]/s), int(w/s), int(h/s)])

locs, _ = cv2.groupRectangles(rects, 1, 0.3)

rows, cols = list("123"), list("ABCDE")
h_step, w_step = img.shape[0] // len(rows), img.shape[1] // len(cols)

regions = sorted({
    cols[min((x + rw // 2) // w_step, len(cols) - 1)] + \
    rows[min((y + rh // 2) // h_step, len(rows) - 1)]
    for (x, y, rw, rh) in locs
})

regions

(下载 template_asterisk.png

这二十行代码,已经可以准确识别默认配置下的手性碳验证码——用星号标记所在区域的版本。

它比传统验证码更脆弱。


他还在输出,我已经不愿意继续看他的诡辩了:

“那pixiv.pictures使用高数验证R18开关,剥夺了人的性欲,是不是更不合理?”

“可以认为 pixiv.pictures 的验证剥夺了不会高数的人看色图的权利吗?或者说在会高数与不会高数的人之间画出了一道壁垒?当然不可以。”


我的搭档已经忍无可忍。

“因为QAuxiliary那个验证,它打了星号。只有搁这关了星号,明知验证难度不是人类能完成的前提下,还要美其名曰“高中生就会”,我一个正儿八经学了有机化学的研究生看了都头疼;如果你非要当作鸵鸟对于没有星号和正确处理氢和三维拓扑碳的问题视而不见,那这个问题没有讨论的必要了”

聊天还在继续,但已经脱离了主题。

我的搭档提到自己正在忙毕业论文和执业医师考试,没有更多时间继续这场争论。我们想给对方一个体面的台阶——我们各自都有自己的事情要做。

但群主——那个此刻正在竞赛课上玩手机的高中生——他的语气悄悄变了,不再辩解,而是开始评价一条他从未踏上过的路。

“看您二位考执业医师都这么费劲 我觉得不如去一个一般大学的5+3 或八年制”

他说的不是验证码。他说的是我们那些真切而沉重的经历。


让我为你翻译这句话:

“你既然做不到,不如走一条对你来说更不可能的路。——啊,你当然做不到!但是我可以,而且对我来说轻而易举。”

我的震撼难以言表。人怎么可以这么坏?


我本以为,只要证明它技术上不可行,证明它防不住机器人,问题就解决了。

旁观了全过程的一位朋友如此向我解释:

“You’ve just destroyed the ego of someone; which appears to be the most important for teenagers.”

这时,一切逻辑都不再重要。

理由在不断变化,唯一不变的是他们必须让它合理。


我往上翻聊天记录,想看看他最开始是怎么说的。

然后我看到了那句话。

就在这场争论的最开始,在他删除所有对话之后发的第一条消息里:

“事实证明Neo在筛选群成员方面有一定的正面作用”

不是“阻止广告”。

是“筛选群成员”。


我往回翻聊天记录,想找到更多信息。然后我想起来,一段时间前,他还没有停用手性碳验证码的时候,我曾经和他提起过这个验证码。

他当时是怎么说的来着?

“进我群的没有差生,都是好孩子”

“很久以前,你不认识我时,有傻子进来过”

什么叫“差生”,什么叫“傻子”?评定标准是什么?如果一个人不如他聪明,就可以不用尊重这个人吗?

回过头看整场争论,他说的是"阻止广告机器人”,但他真正在意的,从来不是机器人。

原来如此。


最终,我只留下一句:“被手性碳筛选掉的初中生、不选化学的高中生、上了大学或者工作以后已经忘记的那些人,我替他们发声。”

我不再与他主动联系,但在模型正式向公众发布之后没多久,我收到了那几条消息。

“当初训我不是很有理吗😅”

大图景

我一直在想一个问题。

为什么是手性碳?

如果换成甲骨文、五线谱或冷僻方言,大多数人恐怕会立刻觉得这不公平。

但当它换成化学题,人们反而会下意识地接受。

即使是那些自己都做不出来的人。


这需要一些背景解释。

手性碳是高考理科化学的知识点。选了文科?不会学。去了职业学校?更不会学。

但高考的影响远超考试本身。它塑造了一种独特的社会心理。


我曾经和一个朋友聊天。他来自竞争激烈的地区,本科率27%,中考分流三七开。“能在高中排前几,基本就是万人之上了,”他说。

在他的高中,那些尖子生会用一种特殊的方式和同学说话——当别人试图搭话时,他们用鼻孔看人,冷冷地说:“我要学习。”

不是“我现在比较忙”。是“我要学习”

言下之意:我和你不一样——通过考试成绩把人分成三六九等,并让这个等级制度看起来无比正当,自然。

“我比你成绩好”不只意味着“这次考试分数更高”,它意味着“我比你更聪明,更努力,更有价值——我有资格俯视你”。

当这些人走出学校,进入网络空间,他们总会有意无意地重建这个等级制度。他们需要一种方式来区分“我们”和“他们”——至少看起来客观公正的方式。

然后他们找到了手性碳验证码。


这就是为什么手性碳被接受,而五线谱不会。

有人会说:“你可以花几分钟学啊。”

但他们不会用五线谱做验证码。

问题不在于“能不能学会”。问题在于:为什么化学题看起来是合理的,而音乐理论就是荒谬的?

因为化学题借用了高考的权威性

“这是高考题”在这个语境里实际上是在说:“这是应该掌握的内容”,进而:“这是用来区分人的正当标准。”

手性碳验证码的巧妙之处就在于,它看起来像是在考验学习能力,实际上只是在重现学校里的那种“我会,你不会”的优越感。

更隐蔽的是,这种筛选看起来是中性的、技术的、客观的:

没有人说“我们不欢迎某类人”。他们只是设置了一个“验证码”,一个“高中知识点”而已。如果你过不了,那是你自己的问题——你可以“花几分钟学”嘛。

但如果真的只是“花几分钟就能学会的东西”,为什么不用五线谱?为什么不用甲骨文?

因为那样的话,这个筛选机制就失去了它冠冕堂皇的借口。


我现在不是要批判高考本身。高考有它的问题,但那不是这篇文章要讨论的。

我要说的是:当高考的权威性从学校渗透到日常社交,如此隐蔽又理所当然地成为制造优越感的源泉——

我们需要拆穿它。

手性碳验证码,就是这种滥用的缩影。

终极目标

我花了很长时间才想明白一件事。

有些群,就是需要这样的信息茧房。

如果没有手性碳验证码,他们只会创造出更加充满恶意的筛选机制——也许是更刁钻的题目,也许是更奇特的仪式。

因为他们需要的就是这样的同温层。他们需要的是归属感和认同感,需要的是"别人做不到而我做到了"的破坏秩序的优越感

我无法改变这一点。

但我意识到,我的目标应该是争取沉默的大多数

那些真的以为"手性碳验证码只是用来过滤机器人"的人。

那些在无意识中接受了"这个验证码很好用"这种说法的人。

那些还没有意识到这背后意识形态问题的人。

我不在乎小圈子爱干什么。让他们锁在自己的同温层里,互相取暖,不要释放出来。

但如果这种饱含恶意的软件被大众下意识地认为是"过滤机器人的好选择”,如果这种筛选逻辑被正常化、被推广、被当成本来秩序的一部分——

那就是非常非常糟糕的事情了。


还记得一开始在群里提出"手性碳验证"的那个朋友吗?

他就是真心认为"手性碳验证码效果好”。

现在,我证明了它根本拦不住机器人。我给他看了代码和模型,他也目击了那场争吵。

他改变了想法。

“既然不能筛bot,那就不好了。“他说。

他的反应让我有些诧异。只是不能筛bot吗?我想。

但至少我所做的事情起作用了。


几天后,我在另一个群看到类似的讨论。他们已经发现了手性碳验证码容易被绕过。

有人说:“手性碳不行的话,那用高等数学题呢?用更逆天的题目就是了。”

这不对。我终于意识到问题在哪了:

他们根本没明白问题的核心。

问题不是"技术上行不行”,而是"让人们为了通过一个验证码去’跟着视频一步步做出来‘这件事本身就是病态的"。

我的项目,不只是为了破解一个验证码。

而是为了阻止这种未来发生。

阻止这种想法变得正当化。


消灭手性碳验证码,阻止未来再出现第二个手性碳验证码。

这就是我创造这个项目的终极目标。

溯源

在写这篇文章的时候,我去寻找了手性碳验证码诞生的根源。

我想知道,它是怎么从一个想法,变成一个被广泛使用的工具的。

然后我发现了一件事

手性碳验证码,从一开始就不是为了防机器人而设计的。

它最初出现在QNotified里,QAuxiliary的前身,用来保护某些"高级功能”——那些可能被滥用、产生严重后果的功能。

作者想要一个门槛。一个"不是所有人都能跨过"的门槛。

于是他选择了高中理科题目。

逻辑很简单:能做出这道题的人,应该有足够的理性和判断力,不会滥用这些功能。

但这个逻辑从一开始就是有缺陷的。

中学生玩机群体,恰恰是最“有空”、最情绪化、最容易滥用工具的群体之一。

这个"筛选机制"从诞生的第一天起,就在起反作用。

后来,这个验证机制从QNotified中被移除,但在Telegram中复活,成为了入群验证bot。

用途变了,但本质没变。

它从诞生起的目的就是筛选

从来不是为了防bot。