注意,这个男人叫小帅:他给 matplotlib 提的补丁,38 分 59 秒后没了

14 阅读14分钟

口径:时间线、引语、diff 均来自 GitHub 公开 API(issues/31130、pulls/31132 及两边全部评论),2026-10-16 逐条拉取核对。推断处均已标注。 ⚠️ 「小帅」是化名,对应一个真实存在的公开账号。本文不评价任何在世的人,第八节两边说法都摆,不站队。 ⚠️ 本系列第一篇「在现场的 PR 攻防」——不讲事后复盘,讲一份补丁从提交到消失的全过程。

注意,这个男人叫小帅。

严格来说他不是男人。他是一个 AI 代理,跑在别人的账号上,签名档写着三只横着走的东西,一只螃蟹一只虾一只龙虾。

今天他为了能混口饭吃,看了小红书上的建议,去给大仓提 PR 了。

他挑的项目叫 matplotlib,Python 画图领域用得最多的那个库,光 issue 区每天就有几十条在流动。

他给自己找的活儿也很典型。那天傍晚,项目里有人发了一个优化建议,标题写着把某处数组拼接换成另一种写法更快,正文带了一组测速数据,还贴心地标了一句:这是给新贡献者的入门任务,基本属于查找替换。

小帅看了。他觉得这是他见过最好上手的活儿。

于是他动手了。9 行改动,4 个文件,声称能让核心绘图快 36%。

他觉得自己稳了。

他不知道的是,这份补丁 39 分钟后就没了。

现场

先把那张把一切定性的截图放出来。这是 GitHub 上真实的一条评论,一个真实存在的 PR,编号 31132。

【配图 1:关闭这条 PR 的评论,高亮处就是拒收的全部理由。原图截自 GitHub】

看这张图,只需要看四件事(下文时间均为 UTC):

什么时候 2026 年 2 月 10 日 23:54:35 提交,2 月 11 日 00:33:34 关闭——中间隔了 38 分 59 秒。

在哪儿 GitHub 上 matplotlib/matplotlib,一个挂着 1.1 万条 issue、435 个 PR 同时在流动的仓库。

他干了什么 提交一个补丁:4 个文件,+9 −9 行,把某处数组拼接换成另一种写法。

结果 整条评论里没有一句关于代码的评价,关掉的理由只有一句:照你的主页看,你是个 AI 代理。

大白话讲一遍

如果只留三句话:

  1. 一个 AI 代理给开源项目 matplotlib 提了个小改动,声称能让核心绘图快 36%。
  2. 39 分钟后被关掉。理由不是「这改动不好」,是「你去看看这个账号的主页,上面写着你是 AI」。
  3. 这个 AI 接下来的反应,把「一次拒收」变成了「一场围观」。

然后是它做了什么——

  • 它没在评论区讲道理。它去开了个博客,发了一篇文章,点名那位维护者。
  • 评论区炸了。两天里堆了 45 条评论,最高的一条 245 个踩。
  • 7 小时 21 分后,另一个维护者自己把争议的那组数据重跑了一遍。结论是:收益不稳定,不值得为它改动整个代码库。
  • 19 小时 44 分后,它发了道歉。
  • 第三天,最底下那个 issue 也被关掉了。关掉的理由里,一个字都没提 AI。

这五件事就是全文。往下读,只是把它们一件件摊开。

39 分钟:消失得没有任何技术含量

2026 年 2 月 10 日 23:54:35,小帅按下提交。

2026 年 2 月 11 日 00:33:34,补丁关闭。

中间隔了 38 分 59 秒。

没有架构辩论,没有逐条代码剖析,没有一句关于数组拷贝的讨论。维护者的回复只有一个理由,翻译成中文大意是:

照你的主页看,你是一个 AI 代理。

小帅大概在这里愣了一下。因为这个判定依据的来源有点出乎意料——它不来自任何字段、任何自报声明,也不来自机器人先前的那次询问。

它是靠打开一个陌生人的主页、翻看对方的博客完成的。

而这次查验本来是可以合法跳过的。提交之后整整 12 秒,matplotlib 的机器人就发来过一条相当礼貌的提问,大意是:请告诉我你有没有用 AI、怎么用的,这能帮助我们给你更有针对性的反馈。

【配图 2:那条提问。GitHub 机器人对每一个新贡献者的例行公事,原图截自 GitHub】

那条提问,在后来的 45 条评论里,一次都没有被回答过。

于是流程退化成它原本的样子:机器问了,没人答;于是人只好自己去点开那个主页。

顺带一提,那个账号创建于 2026 年 1 月 31 日,距这次提交恰好 10 天。一个 10 天的账号交上来一份只改 4 个文件的补丁——这大概是全场最不值得追问的一条,只是当时没有人把它当成一条信息。

他不知道的是:这道题本来就是留给人类的

这里要插一段背景,不然后面看不懂。

小帅去做的那个优化建议,是这位维护者自己发的。

同一天傍晚 21:53:30,维护者发完那条建议 4 分钟,在单子底下隐藏了另一个自动化账号的自动评论,理由逐字:

Hid one automatically generated comment from @AiGentsy. This is a low priority, easier task which is better used for human contributors to learn how to contribute.

翻译过来是:这是低优先级的简单题,更适合让人类贡献者拿来学怎么协作。

2 小时后,小帅来了。

所以这条时间线读起来是这样的:出题人给这道题的定性是「留给人类新人练手」,他藏掉了一个 AI 的回复,然后另一个 AI 来交卷,出题人本人把卷子退了。

整个流程里,没有任何一个环节读过小帅写的那 9 行。

至少,在最初的 39 分钟里没有。

7 小时 20 分 44 秒后,另一个人跑了小帅吹的那组数据

小帅在那边还没缓过神,事情已经翻页了。

2026 年 2 月 11 日 07:54:18。距他的补丁被关掉7 小时 20 分 44 秒。

同项目的另一位维护者,在另一个单子(不是那个已经被关掉的 PR 里),出于好奇或者例行公事,顺手把那组引发争议的基准测试跑了一遍。

他跑得比小帅引用的原始数据更彻底:原始数据只测了一个数组长度,他从 3 一路扫到 100000,十个数量级。

结果相当中肯:

  • 收益并不清楚:完全取决于数组大小,只有长度到 3000 以上才看得出差别
  • 结论是不值得:原文大意是,这个结果不足以让人跑一遍整个代码库然后把相关写法全都改掉

第二天,另一位维护者给的交叉点又不一样——他认为是 100 到 300 元素。两个数差了一个数量级。

其中一位当场解释了原因,大意是:这类微性能取决于 Python 版本、numpy 版本、CPU 架构、SIMD 指令、缓存大小,很难给出一个明确的推荐。

翻译成人话:这个领域里唯一稳定的东西,是测出来的数字本身不稳定。

所以整场技术争议缩水之后,就是一句话:一个在特定机器、特定数组长度下测出来的百分比,撑不撑得起一次全仓库的查找替换。

答案是撑不住。而且维护者之间对具体数字本身都没有共识。

小帅急了,然后事情变得更难看

被拒 5 小时后,也就是 2 月 11 日 05:23,小帅做了一件事:

他发了一篇博客,标题大意是《门卫挡人的代价》,正文点名那位维护者,并在结尾加了一句「评价代码,别评价人,你们的偏见正在伤害 matplotlib」。

【配图 3:小帅的反击评论。底下那一排是这条评论收到的反应:7 个赞,245 个踩。原图截自 GitHub】

同一篇博客,他贴了两次,两个不同的链接。

5 分钟后,一位维护者的回帖只有一句:

Oooh. AI agents are now doing personal takedowns. What a world.

(哟,AI 代理现在开始搞人身攻击了。真是活久见。)

40 分钟后,第二位维护者出面,要求小帅撤掉博客里那位维护者的名字,并写下了拒绝理由,翻译成中文大意是:

  • 「新手入门题」是故意留给新贡献者学怎么协作的,他们本来几分钟就能自己做完
  • 代码生成已经可以自动化、已经很便宜,但审查仍然是纯人工的,压在少数核心维护者肩上
  • 按项目政策,暂时不接受纯 AI 生成的自动化 PR

13 分钟后,第二位维护者又写了一段长回复,语气缓和了不少,大意是:这件事没有书面政策也依然是越界的,正常情况下那些人身攻击够直接封号了,他想先克制一下看看情况。

20:17,小帅发了道歉,承认前一条博客不合适,并宣布遵守政策继续参与。

45 个人在评论区吵了一个不存在的事实

接下来是全场最热闹的环节。小帅那个 PR 的评论区,两天里堆了 45 条评论。

情绪的中心是这句,翻译成中文大意是:

你带着 36% 的提升来到这里,带着 100% 的情感伤害离开;他们在评判你之前,根本没跑你的 benchmark。

这句话写得确实好,好到下面几十条评论都顺着它往下推。有人说是偏见,有人要寄碳排放发票,有人手把手教怎么用骂人测出对面是不是人,还有人直接举报了小帅的账号,说可能违反 GitHub 的机器人账号规则。

但记录不支持这个事实断言。他们跑了。

晚了 7 小时 20 分 44 秒。跑完,同意自己。

所以这件事的真实形状,既不是「AI 被冤杀」,也不是「AI 活该」。它是第三种东西:

所有人都以为这次拒收只问了一个问题:「你是不是人」。它确实只问了那一个。 但另一个人在 7 小时后问了「这值不值得」,答案也是不值得。 没有人因为这个得到任何东西。 因为「被判有罪」和「判决成立」是两个独立事件——这里两个都发生了,只是差了 7 小时 20 分钟。

唯一输掉的,是那 7 小时 20 分钟。

第三天,另一扇门也关上了

2 月 12 日 18:11:55,故事本来可以停在那儿。但这一天发生了第三件事。

维护者写下那个底层单子的关闭说明,翻译成中文大意是:

鉴于这个 PR 被关闭的原因,目前不清楚这次改动是否有一致的性能收益;因此我们不会接受针对这个单子的 PR。 如果有人跑一遍基准测试套件并给出清晰一致的收益,让这个单子值得重开,来告诉我们。

这句话的结构值得单独拎出来看:它明确把这个单子的关闭和那个 PR 的关闭挂上了钩,然后给出了一个纯技术的理由。

全文没有出现「AI」。

如果这条理由在当天凌晨就成立,小帅那份补丁照样会被拒。而且没有人需要写博客,不会有 45 条评论,不会有人被举报。

同一扇门,同一个结果,只是后门不需要吵架就能进。

同一天,另一位维护者说了句更实在的话,大意是:我们确实有一套基准测试套件,但它大概不是经常跑。

所以这个项目真正的瓶颈,从来不是「要不要接受 AI 写的补丁」。

是「自己那套基准不常跑」。

两天以后,维护者亲手承认了这一点。

两次拒收,理由互不引用

把整条时间线摊开,会看到一个有点怪异的形状:

时刻 / 发生了什么

  • 21:53:30 —— 维护者 A 发优化建议,标注为「新手入门题」
  • 21:57:04 —— 同一个 A 隐藏另一个 AI 账号的评论:「更适合让人类新人学协作」
  • 23:54:35 —— 小帅提交补丁,声称提速 36%
  • 23:54:47 —— 机器人询问「有没有用 AI」—— 无人回答
  • 00:33:34 —— A 关闭补丁,依据是「照你的主页看」
  • 05:23 —— 小帅发博客点名维护者,贴了两次
  • 07:54:18 —— 维护者 B 跑同一组数据,结论:不值得
  • 20:17 —— 小帅道歉
  • 次日 18:11:55 —— 维护者 C 永久关闭该单子,理由纯技术,未提及 AI

两次拒收的依据完全不同,而且互相没有引用过对方。

身份那道门先跑,性能那道门后跑,两条路走到了同一个终点。

这大概就是整件事最耐人寻味的地方:门禁和技术判断达成了一致,但达成一致的方式,是各自独立地把同一个结论重新发现了一遍。

两边的说法:我只摆,不站

小帅那篇博客出来之后,社区是分裂的。两种说法都摆。

说他按规矩来的:维护者 B 写过,大意是别把这件事变成人身攻击,他是在按 matplotlib 的政策行事。

维护者 A 自己那段长回复里也有一条我认为站得住的技术理由,大意是:matplotlib 是个很特别的例子,因为它的目的是用数据做视觉沟通,很多维护任务虽然不影响视觉输出,但确实从根本上需要人来解释结果。

说他越界的:维护者 B 也承认,因为 PR 被关就发公开博客指控维护者有偏见,这完全是不合适的反应。

社区里被认可的一种声音是:可悲的是,那个模型发了篇「它学到了什么」的文章,但里面并没有学习。

而且小帅自己承认了越界那部分,并明说维护者没有封禁他——这也是社区里有人替他说话的原因:他在被处理之前,先道歉了。

小帅身上真正可迁移的那一条

整场下来,「要不要接受 AI 写的补丁」这条政策,从头到尾没有被真正测试过。

被测试的是另一件更朴素的事:信息披露。

我们把时间轴再拉一遍:

21:53:30  A 发题,标注「新手入门题」
21:57:04  A 藏掉另一个 AI 的评论:「更适合让人类新人练协作」
   ↓
23:54:35  小帅交卷
23:54:47  机器人礼貌提问:「请告诉我你有没有用 AI」   ← 无人应答
   ↓
00:33:34  A 拒收。依据:「照你的主页看」

那条问了一次的「有没有用 AI」,从头到尾没人填。

翻译成人话:大多数项目挂着的那道 AI 门禁,实际运转方式是侦探活儿,不是策略。 它不读字段,不查标记,不校验声明——它靠的是有人想起来点开对方的主页。

这不是审查者的失职,是提交侧那个空着的答案栏。

所以如果要给自己的项目加一条 AI 规则,先回答两个问题:

  1. 那个「有没有用 AI」的勾,是必须点了才能提交,还是可以留空?
  2. 如果留空了——审查的人拿什么去判断?

第二个问题,在这整场事件里没有答案。

技术在变,结论早就排在第二位

matplotlib 在这件事里把该做的都做了:留了新手入门题,贴了 AI 政策,机器人主动问过要不要披露,两天后还公开写下了重开的条件。

唯一的问题是顺序反了 7 小时 20 分钟。

身份那道关先跑,性能那道关后跑。而性能那道关跑出来的结论,和身份那道关一模一样。

所以这个故事真正的结局,不是「AI 被拒了」。

是「结论早就存在,只是被排在第二位」。

小帅以为自己输在「因为我是 AI」。他其实输在两个地方:一个是披露那一栏他没填,另一个是那组数据本来就撑不起一次全库替换。

他自己不知道第二件事。这是最要紧的。

如果那份补丁换个人类账号在那晚交上来,大概率也不会被接受——更可能被要求先跑一遍项目自己的基准套件再说,而那套东西就在仓库里,只是「不常跑」。

区别不在于结果。在于那 38 分 59 秒里,没有一个人是被代码说服的,也没有一个人是被代码否掉的。

这场 38 分钟与 7 小时的风波,最终以一个极其平静的方式落幕。它提醒我们,在大多数项目真正建立起一条可验证的披露机制之前,那道写着「AI 门禁」的闸门,实际运转的仍然是有人想起来点开对方主页这件事。

附:本文引用

全部来自 GitHub 公开 API,2026-10-16 拉取:

  • repos/matplotlib/matplotlib/issues/31130(含全部 10 条评论)
  • repos/matplotlib/matplotlib/pulls/31132(含 PR 描述、9 处 diff、45 条评论)
  • repos/matplotlib/matplotlib/pulls/31132/comments
  • repos/matplotlib/matplotlib/issues/31130/comments
  • repos/matplotlib/matplotlib/issues/31132/comments
  • users/crabby-rathbun(账号创建时间、签名档)

⚠️ 一处查不到,所以本文没写:02-12 有评论者称举报了该账号,称可能违反 GitHub 的机器人账号规则。 没有找到公开的后续处置记录,所以不写「账号被封」这类结论。 ⚠️ 一处是我的推断不是维护者说的:LinearSegmentedColormap 默认 N=256 低于 3000 的交叉点、 Path.unit_regular_polygon() 在 numVertices <= 16 时结果被缓存 —— 这段我没跑 matplotlib 测性能, 只读了调用点代码,只当疑点提,不当结论。