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。