答辩前夜,我听见导师对院长说:「这个课题,周衍那边的进度太慢了,答辩材料就用陈昊的版本吧。」
我站在走廊里,愣了三秒。
掏出手机看了一眼Git记录。
最后一次push,是陈昊六个月前。
而那个版本,有一个我故意埋进去的致命Bug。
注释里写满了我对这段代码的亲切问候。
明天答辩,有好戏看了。
【第一章】
实验楼B栋的走廊灯坏了两盏,剩下的几盏也跟便秘似的,一闪一闪往外挤着昏黄的光。
晚上十点四十七分。
我蹲在409实验室门口的消防栓旁边系鞋带。
准确说,鞋带五分钟前就系好了。
我在等一个答案。
409的门没关严,留了一指宽的缝。里头的声音顺着缝儿往外淌,跟自来水管漏了似的,挡都挡不住。
「……周衍那边你也看到了,三个月没新进展,答辩PPT到现在还是框架。」
说话的是我导师,张培文。计算机学院副教授,博导,省级人才计划入选者。
简历很漂亮。
人也很漂亮——漂亮到能当着我的面,把我做了两年的课题成果署上别人的名字,脸不红心不跳。
「培文,你的意思是……」
第二个声音属于院长赵德明。六十多岁,头发比实验室的经费还稀薄。
「老赵,明天答辩委员会那边,就用陈昊的版本汇报。」张培文的声音压得很低,但每个字都像钉子,「周衍的代码我看了,核心模块的逻辑跟陈昊的方案重合度极高,但陈昊那边整合得更完整。答辩嘛,谁拿得出完整的东西,谁就是第一作者,这个道理你懂的。」
我蹲在门外,右手无名指的指甲掐进了左手掌心的肉里。
重合度极高。
好一个重合度极高。
那套核心算法是我从研一开始写的。凌晨三点的实验室,发了霉的方便面桶,敲到指纹都快磨平的键盘。
两年。
七百三十天。
陈昊呢?
陈昊是张培文的亲外甥。研二插进课题组,连开发环境都配不明白的废物。
他做了什么?他把我的私有仓库权限拿到手之后,fork了一份,改了署名。
就这么简单。
六个月前的事。
我发现的时候,差点把键盘摔穿显示器。
但我没声张。
「那周衍那边怎么交代?」赵德明的声音犹豫了一下,「毕竟课题是挂在他名下的……」
「交代什么?」张培文笑了一声,那种笑我太熟了,每次他让我免费帮他写横向项目的结题报告时,就是这个笑,「周衍这孩子,老实,听话。答辩完跟他说一声,二作的位置给他留着,他能翻出什么浪花?」
老实。
听话。
翻不出什么浪花。
我低头看着自己的手,掌心有四个月牙形的血印。
张培文对我的判断,前两年确实准确。
但他漏算了一件事。
六个月前,我发现代码被盗的那天晚上,我没有去找他理论,没有去找学院申诉,甚至没有告诉任何人。
我做了一件更有趣的事。
那个被陈昊fork走的代码版本,核心模块里有一个函数——`data_integrity_check()`。
这个函数负责整个系统的数据校验。论文里最核心的创新点,就建立在这个函数的输出结果上。
陈昊fork走那个版本之后,再也没有pull过我后续的任何更新。
他不知道的是,在他fork的前一天,我刚好在那个函数里埋了一个东西。
一个看起来完全正常,但在特定数据集上会产生系统性偏差的逻辑错误。
说人话就是:跑小数据集,结果完美;一上大数据集,全部跑崩。
而答辩现场,评审专家一定会要求跑完整数据集验证。
注释里我写的是:`// TODO: fix this later, f**k this sh*t, 陈昊你个狗东西,抄到这里算你有本事`
当然,这行注释在我后来修复的版本里已经删掉了。
但陈昊的版本里,它还安安静静地躺在那儿。
我站起来,拍了拍裤子上的灰。
膝盖蹲麻了,走路有点飘。走廊的声控灯感应到我的脚步,啪地亮了一盏,刺得我眯了眯眼。
掏出手机。
Git的提交记录还停留在六个月前那个页面。
陈昊的最后一次commit,时间戳2024年11月15日,凌晨2:03。
commit message写的是:「优化核心模块」。
我笑出了声。
走廊空荡荡的,笑声撞在墙壁上弹回来,听着像是别人在笑。
优化。
你连那个Bug在第几行都不知道,优化个屁。
手机震了一下,微信消息。
陈昊发的。
「衍哥,明天答辩你来吗?导师说让你也到场,帮忙控制一下PPT翻页,辛苦了哈【抱拳】」
我盯着屏幕看了十秒钟。
拇指悬在键盘上方,来回晃了两下。
最终打了三个字:「好的,来。」
发出去之后,我把手机揣回兜里,往宿舍楼走。
五月的夜风从教学楼的穿堂灌进来,带着草坪刚浇过水的潮湿气。
我深吸了一口。
明天,答辩委员会五个评审专家,全是外校请来的。
张培文打点不到。
陈昊求爷爷告奶奶也没用。
当着全院师生的面,跑完整数据集。
那个被我精心喂养了六个月的Bug,会像一颗定时炸弹一样,在所有人面前,把陈昊的所谓「研究成果」炸成一地碎片。
而我只需要做一件事。
站在旁边。
翻PPT。
微笑。
【第二章】
答辩那天,天气好得过分。
五月中旬的阳光从行政楼三楼会议室的落地窗砸进来,把长条形会议桌照得跟停尸台似的——白晃晃,冷森森。
我到得很早。
七点五十,会议室门还锁着。
我站在门口,手里捏着一杯从一楼自动贩卖机买的冰美式,三块钱一杯,难喝得像是用拖布水兑的。
但便宜。
读研三年,我已经习惯了什么叫便宜。
张培文每个月给课题组发的补助是一千五。但到我手里只有八百。剩下七百去了哪儿,我没问过。
问了也没用。师门里的事,比明朝的税制还复杂。
八点整,陈昊到了。
西装,皮鞋,发型打了啫喱,锃亮。
看见我的一瞬间,他脸上的表情很有意思——先是愣了零点三秒,然后迅速切换成那种令人作呕的热情。
「衍哥!来这么早!辛苦辛苦!」
他伸手拍了一下我的肩膀。
我肩膀上的肌肉条件反射地绷了一下。
「没事。」我笑了笑,「你今天状态不错。」
「还行还行,昨晚对着PPT练了一宿。」他压低声音,凑过来,「衍哥,导师跟你说了吧?今天答辩我来讲,你帮我翻页就行。放心,论文出来二作给你留着。」
二作。
我做了两年的课题。
写了十二万行代码。
推了三个核心算法。
他给我留二作。
我低头喝了一口拖布水美式,把涌上喉咙的那股东西咽了回去。
「知道了。」
「衍哥你真是好人。」陈昊笑得眼睛眯成一条缝,「等论文发了,我请你吃海底捞,随便点。」
海底捞。
两年的工作量,换一顿海底捞。
我算了一下,大概相当于每行代码值三分钱。比扫码领红包还便宜。
八点二十,张培文到了。
深蓝色POLO衫,卡其色休闲裤,腕上一块浪琴。他见了我,眼神有一个极快速的闪烁——大概是在判断昨晚的对话我有没有听到。
「周衍来了?不错不错,今天帮陈昊把控一下节奏。」
他笑着拍了拍我另一边肩膀。
今天我两边肩膀待遇很公平。
「好的张老师。」
八点四十,评审专家陆续到齐。
五个人。
我扫了一眼名单。
主席:李振邦,隔壁省理工大的教授,做分布式系统的,论文引用过万。
这位爷,是个出了名的较真狂。
上个月听说有个博士生答辩,PPT里一张图的坐标轴标注错了,被他追着问了四十分钟,问到那学生当场哭出来。
我的嘴角动了一下。
很好。
非常好。
九点整。
答辩开始。
陈昊站到投影幕前面,激光笔握在手里,西装笔挺,看着很像那么回事。
「各位老师好,我是计算机学院2022级硕士研究生陈昊,今天汇报的课题是基于图神经网络的城市交通流量预测系统……」
我坐在角落里,笔记本电脑摆在面前,负责翻PPT。
说实话,这个PPT做得不差。
因为PPT也是我做的。
去年十一月,张培文让我把课题的阶段性成果整理成汇报材料。我花了两周做了一套六十页的PPT,每一页都有动画,每一张图都是我自己用matplotlib画的。
然后那套PPT就出现在了陈昊的答辩材料里。
连动画切换的速度都没改。
陈昊讲得很流畅。毕竟PPT的逻辑线是我理的,就算是条金毛循着这个逻辑讲,也能讲出个七八成。
前四十分钟,一切正常。
理论框架、模型设计、实验方案,陈昊讲得头头是道。
台下五个评审专家有的点头,有的记笔记,有的喝茶——总之,没人找茬。
张培文坐在第一排,翘着二郎腿,脸上的笑容很松弛。那种笃定的松弛。就像一个赌徒知道底牌在自己手里。
可惜。
你不知道,那张底牌上面有虫蛀。
四十五分钟的时候,陈昊翻到了第四十二页。
「……下面进行实验演示,我将在完整数据集上运行核心算法,展示模型的预测精度。」
我的手指按在触控板上,把翻页的光标挪到下一页。
然后停住了。
不是犹豫。
是享受这一秒。
台下,李振邦教授放下了茶杯,身体前倾了两度。
那双眼睛像是CT扫描仪。
我点了翻页。
屏幕切换到实时演示界面。
陈昊打开终端,输入运行命令。
进度条开始跑。
我看到了那个熟悉的函数名——`data_integrity_check()`——在终端的日志输出里一闪而过。
像一颗已经拉了弦的手雷,安静地滚进了房间。
进度条走到38%的时候,终端上开始出现红色的报错信息。
一行。
两行。
十七行。
然后整个屏幕,被铺天盖地的Error淹没了。
会议室安静了三秒。
那三秒里,我听见空调出风口的嗡嗡声,听见隔壁会议室有人在笑,听见陈昊的呼吸突然变得很重。
李振邦教授的眉毛先动了。
「暂停一下。」他的声音不大,但每个字都带着一种冷冰冰的精确,「陈同学,你这个数据校验模块,跑崩了。」
陈昊的后背肉眼可见地僵硬了。
「呃……李老师,可能是环境配置的问题,我重新跑一下——」
「不用重跑。」李振邦抬手制止他,「我刚才看到报错日志里有一个data_integrity_check函数抛出的异常。这个函数是你写的?」
陈昊的喉结上下滚了一下。
「是、是我写的。」
他的声音开始发飘。
我低下头,盯着笔记本屏幕,嘴角的弧度控制在零点五毫米以内。
翻PPT的手,稳得像做手术。
【第三章】
李振邦的下一个问题,像一把手术刀。
「那麻烦你解释一下,这个函数的校验逻辑里,为什么在处理超过十万条数据时,哈希碰撞率会指数级上升?」
会议室的温度没变,但我看到陈昊额角渗出了一层细密的汗珠。
那层汗珠在投影仪的光线下,亮得跟镶了一圈碎钻似的。
「这个……呃……这是因为……」
陈昊开始翻他的备忘录。
A4纸,密密麻麻的字。
我知道那上面写的是什么——张培文昨晚连夜帮他准备的「可能被问到的问题及标准答案」。
但这个问题不在上面。
因为张培文也不知道这个Bug的存在。
那个Bug太安静了。安静到在小数据集上完美运行,安静到所有单元测试都能通过,安静到只有在真正的完整数据集上才会暴露。
我花了三天设计它。
不是随便塞进去的低级错误。那种东西一眼就能看出来,太蠢了。
我做的是一个精密的逻辑陷阱。
`data_integrity_check()`里有一个循环变量的边界条件。在数据量低于五万条时,这个边界条件永远不会被触发。但数据量一旦超过十万条,哈希桶的溢出链表就会产生错误的归并,导致校验结果出现系统性偏移。
这种偏移不会让程序崩溃——程序会继续跑,但输出的结果全是垃圾。
而今天,答辩现场用的是完整的城市交通数据集。
三十七万条。
足够把那个Bug喂饱。
「陈同学?」李振邦的语气没有不耐烦,反而更平淡了,那种平淡比愤怒可怕一百倍,「我在等你的回答。」
陈昊的嘴唇动了两下,没发出声音。
他回头看了一眼张培文。
那个眼神我太熟了。是一种「导师救我」的求助信号,就像溺水的人在抓救命稻草。
张培文的表情在那一瞬间出现了一个极细微的裂痕。
他清了清嗓子,开口:「李教授,这个问题可能涉及到一些底层实现的细节,陈昊平时主要负责上层架构的设计,底层模块是团队其他成员——」
「张老师。」李振邦打断了他,声音不大,但每个字都像钉子钉在了桌面上,「答辩的基本规则是,汇报人独立回答问题。您是导师,不是代答人。」
张培文的脸色变了。
不是变白,是变青。一种从颧骨开始蔓延到整张脸的青灰色。
他张了张嘴,没说出话,最终坐了回去。
陈昊站在投影幕前面,像一条被甩上岸的鱼。
我坐在角落里,翻页的手很稳。
心跳六十八次每分钟。
我数过了。
「我……我再检查一下代码。」陈昊转身,手指开始在键盘上敲击。
他打开了代码编辑器。
文件目录一层一层展开。
`src/core/integrity/data_integrity_check.py`
光标定位到第47行。
就是那个循环。
我看着他盯着那段代码的后背。
那件西装很合身。但后背的肩胛骨正在发抖,把西装料子顶出两个不规则的凸起。
他看不懂。
我知道他看不懂。
因为那段代码他从来没读过。
fork的时候是整个仓库一起拉的,他只改了README里的署名和论文摘要里的名字。核心代码连一行注释都没看。
但现在,会议室里所有人都在等他解释他「自己写的」代码。
沉默持续了将近一分钟。
一分钟不长,但在答辩现场,一分钟能把人的脊梁骨压弯。
旁边的评审专家开始交头接耳了。
我听到有人低声说了一句:「这代码到底是不是他写的……」
音量不大。
但足够让陈昊听到。
他的脖子肉眼可见地缩了一下。
这时候,李振邦做了一件事。
他站起来,走到陈昊旁边,自己拿过鼠标,开始翻代码。
从第47行一直翻到第183行。
然后停住了。
我知道他停在哪一行。
183行。
注释区。
那行注释我记得很清楚,因为是我在凌晨四点、喝了第七杯拖布水咖啡后写的:
`// TODO: fix this later, fk this sht, 陈昊你个狗东西,抄到这里算你有本事`
李振邦盯着那行注释看了五秒钟。
然后缓缓转头,看向陈昊。
「陈同学。」他的声音平静得像湖面,但湖底有岩浆在翻滚,「你能解释一下这行注释吗?」
投影幕上,那行注释被放大了。
四十号字体。
会议室前三排的人看得清清楚楚。
后排的人探着脑袋,也看得清清楚楚。
整个会议室的空气像是被人拿钳子夹住了,一丝声响都挤不出来。
陈昊的脸从红变白,从白变灰,从灰变成了一种我从没在活人脸上见过的颜色。
他的嘴唇在抖。
手指在抖。
激光笔从他手里滑了出去,砸在地上,发出一声清脆的塑料碰撞声。
那声响在寂静的会议室里,格外刺耳。
「我、我不知道……这个注释……我……」
「你不知道?」李振邦的眉毛抬了一度,「你自己写的代码,你不知道里面有什么注释?」
陈昊没说话。
他的视线开始在会议室里乱转。
像一个被困在玻璃缸里的苍蝇。
他看张培文。
张培文的脸色比会议桌的桌面还白。
他看院长赵德明。
赵德明正在低头喝茶,茶杯挡住了大半张脸,但那只握杯的手在微微颤抖,茶水在杯口荡出了波纹。
他看我。
我抬起头,对上他的目光。
我的表情很平静。
一种真诚的、关切的、「兄弟你怎么了」的平静。
然后我听到自己的声音,不大,但刚好够全场听见:
「陈昊,你还好吗?需要我帮你倒杯水吗?」
【第四章】
水没倒成。
因为陈昊开始往回找补了。
人在绝境里的求生本能是很可怕的。他的大脑在那五秒钟的空白之后,突然开始高速运转——尽管运转的方向完全错误。
「李、李教授,这行注释是我开玩笑写的!」陈昊扯出一个比哭还难看的笑容,「就是写代码的时候太累了,吐槽自己,您知道的程序员文化……」
他试图用手势辅助说明,但两只手抖得像帕金森晚期,比划出来的动作更像是在驱赶什么看不见的东西。
李振邦没说话。
他不需要说话。
因为那行注释写的是「陈昊你个狗东西」。
自己骂自己名字?
这不是程序员文化,这是精神科文化。
会议室后排有人没忍住,发出了一声极轻极快的笑声。
像放了个屁——声音不大,但所有人都听到了。
陈昊的耳根红透了。那种红不是害羞的红,是毛细血管在压力下爆开的红。
「注释的事先放一边。」李振邦坐回了自己的位置,拧开矿泉水瓶盖喝了一口,「我更关心的是代码本身。陈同学,这个data_integrity_check函数的时间复杂度是多少?你用的什么哈希策略?碰撞处理用的是开放寻址还是链地址法?」
三个问题。
全是基础问题。
任何写过这段代码的人,闭着眼睛都能答出来。
陈昊的嘴巴张开了。
然后合上了。
然后又张开了。
像一条被端上桌的鲈鱼——嘴在动,但已经没有任何声音能从里面出来了。
我坐在角落,把笔记本屏幕的亮度调低了两格。
不是为了省电。
是怕自己嘴角的弧度被投影仪的光映出来。
七秒钟。
陈昊一个字都没答出来。
李振邦缓缓转头看了一眼旁边的评审专家。
几个人交换了一个眼神。
那个眼神的含义我看懂了。大概是——「这玩意儿不是他写的吧?」
张培文也看懂了。
他坐不住了。
「各位老师,」张培文站起来,语速比平时快了一倍,「陈昊这段时间压力比较大,状态不太好,答辩的事我们能不能——」
「张老师,我说过了。」李振邦的声音还是那么平淡,但这次多了一丝温度——冰箱冷冻层的温度,「答辩人独立回答问题,这是规则。如果答辩人无法回答关于自己论文核心模块的基本问题,我们有理由怀疑这篇论文的原创性。」
原创性。
三个字。
像三颗子弹,精准地打在了张培文的太阳穴上。
他的嘴角抽搐了一下。非常快,大概零点二秒。但我看到了。
这个表情我也很熟。每次他在学术委员会上被人当面质疑的时候,就是这个表情。
「李教授,」他的声音降下来了,带上了一种久经学术江湖的老练,「话不能这么说。课题组的工作是协作性质的,分工不同——」
「那这个data_integrity_check函数,是谁写的?」
李振邦问得很直接。
张培文愣了。
这个问题的答案他知道。
但他不能说。
因为如果他说是「团队其他成员写的」,那等于承认核心模块不是陈昊的工作。而答辩材料上白纸黑字写着「第一作者:陈昊」。
如果他说是「陈昊写的」,那陈昊刚才连基本的技术问题都答不上来,这就是公开撒谎。
两条路,都是死路。
他的喉结滚动了一下。
然后他做了一个我意料之中的选择。
他看向了我。
「周衍,」他的语气很温和,温和得像一个慈父在请求儿子帮忙端菜,「你来解释一下这段代码的技术实现?毕竟你也参与了这个模块的开发。」
我抬起头。
所有人的目光聚过来了。
五个评审专家,两排旁听的老师和学生,投影幕前站着的陈昊,第一排坐着的张培文和赵德明。
几十双眼睛。
我感觉自己像是站在了一个巨大的十字路口中间。
「张老师,」我的声音很平,很轻,像是在讨论今天中午食堂吃什么,「这段代码是我写的。」
会议室又安静了。
但这次的安静跟之前不一样。
之前是尴尬的安静。
这次是所有人同时屏住呼吸的安静。
「核心模块里的data_integrity_check,包括整个图神经网络的数据预处理流水线,都是我从研一开始独立完成的。」
我站起来,把笔记本电脑合上了。
「Git提交记录里有完整的时间线。从2023年3月到2024年11月,所有commit都是我的账号提交的。陈昊的fork记录是2024年11月15日,距今整整六个月。」
我的声音不大。
但每个字都砸在了地上。
张培文的脸彻底没了血色。
他张嘴想说什么,但喉咙像是被一只看不见的手掐住了。
陈昊站在投影幕前面,两条腿在发软。我能看到他的膝盖在西装裤管里抖动,把裤线都抖歪了。
李振邦靠在椅背上,手指交叉放在腹前。
他的表情,怎么说呢——不像是在看一场学术答辩,更像是在看一出戏。
而且是一出他已经猜到了结局的戏。
「周衍同学,」他开口了,「你说的这些有证据吗?」
「有。」
我转身打开了笔记本电脑。
屏幕上是我昨晚就准备好的页面——Git仓库的commit history。
时间线清清楚楚。
每一次提交的哈希值、时间戳、修改内容、代码差异——全部都在。
「这是我的个人仓库。所有提交记录从未篡改,GitHub的后台服务器上有完整的时间戳验证。」
我把屏幕转向评审专家。
「同时,这是陈昊的fork记录。」
点开另一个页面。
fork时间:2024年11月15日,凌晨2:03。
我看了陈昊一眼。
他的眼睛里已经没有任何光了。
【第五章】
答辩现场从学术交流变成了事故现场。
李振邦看完Git记录之后,摘下了眼镜。
他擦眼镜的动作很慢,慢到我一度以为他是在给所有人留时间消化刚才发生的事情。
「我有一个程序性的建议。」他把眼镜重新戴上,目光扫过在场的每一个人,最后落在赵德明身上,「赵院长,鉴于目前出现的情况,我建议暂停本次答辩,启动学术诚信调查程序。」
学术诚信调查。
这六个字落在会议室里,像一颗深水炸弹。
赵德明的茶杯终于放下了。
他的手不抖了。不是因为镇定了,是因为已经抖到麻木了。
「李教授,这个……会不会太——」
「赵院长,」李振邦打断了他,语气没有任何波动,「答辩材料上的第一作者是陈昊同学,但核心代码的完整开发记录指向周衍同学。这不是‘太不太’的问题,这是‘必须’的问题。」
赵德明的嘴唇蠕动了两下。
他看了张培文一眼。
张培文没有回看他。
因为张培文此刻正盯着自己的手机屏幕,两根拇指飞速地敲着什么。
我猜他在给院办的人发消息。
但已经晚了。
答辩委员会是独立的。外校专家占多数。这不是张培文能打点的场子。
「好。」赵德明最终点了头,声音像从压缩空气罐里挤出来的,「暂停答辩,后续按学校学术委员会的流程走。」
他说完这句话的时候,没有看任何人。
会议室开始骚动。
后排的旁听席上,嗡嗡的讨论声像蚊子群一样炸开了。
我听到了一些碎片。
「——卧槽,当场翻车了?」
「——代码是那个角落里坐着翻PPT的人写的?」
「——张培文这次完了吧……」
陈昊还站在投影幕前面。
他没动。
不是不想动,是动不了。
两条腿像是被灌了水泥。
他的嘴唇还在微微翕动,但已经没有声音了。
我走过去。
走到他旁边。
弯腰,捡起了掉在地上的激光笔。
然后把激光笔递给他。
「给你。」
他低头看着我手里的激光笔,眼眶里有东西在打转。
但没掉下来。
他是个死要面子的人。这一点我很清楚。
「周、周衍……你——」
「回头聊。」
我拍了拍他的肩膀。
力道跟他早上拍我肩膀时一模一样。
然后转身,走出了会议室。
走廊里的声控灯啪啪地亮起来,一盏跟着一盏,像是在给我鼓掌。
我走到楼梯间,靠在墙上。
掏出手机。
微信。99+。
全是吃瓜群众的消息。
我没看。
打开了Git。
那个有Bug的旧版本,安安静静地躺在陈昊的fork仓库里。
而我自己的仓库,在六个月前的那个深夜修复了Bug之后,早已更新到了第37个版本。
核心算法重构了三次。
预测精度从91.2%提升到了97.8%。
论文初稿写了两万四千字。
参考文献引了一百二十三篇。
这些东西,全部存在我的私有仓库里,从未推送到任何公共平台。
我在等今天。
等答辩。
等陈昊拿着那个六个月前的过期版本、带着那个我精心设计的Bug、站到评审专家面前。
等他自己把脸凑上去。
我只需要在旁边翻PPT。
微笑。
然后在合适的时候,说一句实话。
手机又震了。
张培文的消息。
「周衍,你来一下我办公室。」
我看了一眼时间。
十点十二分。
答辩从开始到结束——准确说是到崩盘,前后不到一个小时二十分钟。
比我预期的快了十五分钟。
我回了一条消息:「好的张老师,五分钟后到。」
发出去之后,我把手机调成了勿扰模式。
靠在楼梯间的墙上,闭了一会儿眼睛。
墙壁很凉。
五月份的阳光照不进楼梯间,这里永远是阴的。
我在这面墙上靠过很多次。
研一那年,第一次在会议上做汇报,被张培文当着全组的面骂「PPT做得像狗屎」,我靠在这面墙上站了二十分钟。
研二那年,连续三个月没休息写出来的算法被张培文拿去给陈昊挂名发了会议论文,我靠在这面墙上站了半小时。
六个月前,发现整个代码仓库被fork走的那个凌晨,我靠在这面墙上坐了一整夜。
但今天不一样。
今天,这面墙很凉,但我的脊背很热。
从尾椎骨一直热到后脑勺。
不是愤怒。
愤怒早就在六个月前烧完了。
这是一种更安静的东西。
像是把一件攒了很久的事情终于做完了之后,身体自动释放出来的余温。
我睁开眼,深吸了一口气。
楼梯间的空气里有水泥和灰尘的味道。
该去见张培文了。
这出戏,还有后半场。
【第六章】
张培文的办公室在实验楼四楼尽头。
门牌上写着「计算机学院 张培文副教授」,铜质的字体,一笔一划很端正。
门虚掩着。
我敲了两下。
「进来。」
推门进去。
办公室不大,大概十五平方,一张L型办公桌占了三分之一的空间。桌上摆着三个显示器、一堆论文打印稿,还有一个相框——他和他老婆在青岛海边的合影,笑得跟刚评上正教授似的。
但他还没评上。
这也是他这么着急拿成果的原因之一。
张培文坐在转椅上,背对着窗户。阳光从他背后打进来,把他整个人框成一个逆光的剪影。
看不清脸。
但我能闻到他身上的烟味。
他不抽烟。
至少在我认识他的三年里,他从来不抽烟。
办公桌上多了一个烟灰缸,里面杵着两个烟头。
「坐。」
他指了指对面的椅子。
我坐下了。
两个人隔着一张办公桌对视。
沉默了大概十秒钟。
是他先开的口。
「周衍,你今天在答辩会上说的那些话,很不合适。」
他的语气很克制。那种经过长期学术训练的克制——声调平稳,逻辑完整,遣词精准。
但眼底有血丝。
「不合适?」我重复了一下这个词。
「你有问题,应该先跟我反映,走内部沟通渠道。你在答辩现场直接说出来,让外校专家看笑话,这是什么性质?你知不知道这对整个课题组的影响?对学院的影响?」
他的手指敲了一下桌面。
不重,但有节奏。
我认识这个节奏。每次他想施压的时候,都会用手指敲桌面。
一下一下,像催命鬼叩棺。
我没说话,等他说完。
「周衍,我对你不好吗?」他的语气软了一点,软得像烂泥巴,「你研一进组的时候,谁手把手教你写论文?谁帮你联系的实习?你的研究方向是谁帮你定的?」
每个问题都带着一种「你欠我的」的潜台词。
我听得很清楚。
「张老师,」我的声音很平,「您确实教过我怎么写论文。但我的论文署名,从研一到现在,您挂了四篇一作。」
他的手指停了。
「您帮我联系的实习,是在您朋友的公司里做免费劳动力,每天工作十二个小时,没有任何报酬。」
办公室的空调嗡嗡地响。
「至于研究方向——是您定的没错。然后方向出了成果,署名给了陈昊。」
我顿了一下。
「张老师,您对我不好吗?不好。从来都不好。但我以前觉得这是读研的代价。直到六个月前。」
我靠在椅背上,双手交叉放在腹前。
「代码被fork那天,我凌晨两点还在实验室改Bug。三点钟的时候收到Git的推送通知,说有人fork了我的仓库。我点进去一看,陈昊的账号。fork完之后两小时,他就把README里的作者名改成了自己的。」
张培文的眼神闪了一下。
「我当时第一反应是找您。凌晨四点,我给您发了消息。您回了我一句话。您记得吗?」
他没说话。
我掏出手机,翻到那条聊天记录。
屏幕亮了。
聊天界面上,我的消息是:「张老师,陈昊fork了我的代码仓库,把作者名改了,这个问题怎么处理?」
张培文的回复是:「知道了,小事,你们师兄弟之间多沟通,我明天再说。」
然后就没有然后了。
第二天他没说。
第三天他没说。
第三十天他没说。
第一百八十天他没说。
直到今天,他在答辩会上让陈昊用我的代码,用我的PPT,用我两年的心血,站在我面前,拿一作。
我把手机屏幕转向他。
「张老师,‘小事’。您的原话。」
张培文的脸上终于出现了一种不受控的表情。
不是愤怒,不是心虚。
是一种被剥光了伪装之后的狼狈。
像一个习惯了在暗处操作的人,突然被人把灯打开了。
「周衍。」他的声音变了,不再是导师对学生的腔调,而是一种谈判者的口吻,「你想怎样?」
「我不想怎样。」我站起来,「我只是在答辩会上说了实话。至于后续学术诚信调查的结果,那是学校的事。」
走到门口的时候,我停了一下。
没回头。
「张老师,那行注释,您应该去看看。」
「我看到陈昊的仓库里还有一些有意思的东西。不止是那行注释。」
我拉开了门。
「比如,commit message里有几条记录,时间戳和您的邮件发送时间高度吻合。您凌晨一点给他发的那封邮件,标题叫‘把这个版本整合一下,署你的名’。」
我转过头看了他一眼。
他的瞳孔在那一瞬间放大了。
像一个硬盘突然被格式化了——所有的数据、所有的伪装、所有精心构建的解释,在那一瞬间全部清零。
「Git记录不会说谎,张老师。」
我走出了办公室。
身后传来一声闷响。
大概是他的拳头砸在了桌面上。
或者是什么东西倒了。
我没回头确认。
走在四楼的走廊里,脚步声在空荡荡的水泥地面上回响。
手机震了一下。
一条陌生号码的消息。
「你好,我是李振邦教授的研究生。李老师想找你单独聊聊,方便吗?」
我看着屏幕,把那几个字读了两遍。
嘴角终于不用再控制弧度了。
因为走廊里没有人。
我笑了。
笑出了声。
笑声在走廊里弹来弹去,跟前天晚上一模一样。
但这次,是我自己在笑。
不是回声。
【第七章】
和李振邦见面的地点在行政楼一楼的咖啡厅。
说是咖啡厅,其实就是学校后勤在大厅角落塞了六张桌子和一个卖美式的窗口,水平跟一楼自动贩卖机差不多。
但环境比自动贩卖机体面。
至少有椅子坐。
我到的时候,李振邦已经在了。
面前摆着一杯没怎么动过的拿铁,笔记本电脑打开着,屏幕上是GitHub的界面。
是我的仓库。
他在翻我的commit history。
「坐。」他抬了一下手,没有多余的客套。
我坐下了。
「周衍同学,我花了半小时看了你的代码。」他把屏幕转向我,指着一段commit记录,「这个图神经网络的注意力机制模块,改了多少版?」
「七版。」
「我数的也是七版。」他点了一下头,「最后这版的multi-head attention的实现方式,跟我去年在ICML上审过的一篇论文的思路很像,但你的特征聚合策略更激进。你参考过那篇论文?」
「没有。」我想了一下,「但我参考了另一篇,2023年NeurIPS的那篇Graph Transformer——」
「Ying et al。?」
「是。」
他靠在椅背上看了我几秒钟。
那种看法跟答辩会上不一样。答辩会上是审视。现在是评估。
像是在看一块原石,判断里面有没有玉。
「你的论文写了多少?」
「两万四千字。初稿完成了百分之九十。」
「发我看看。」
他推了一张名片过来,上面印着邮箱。
我接过名片,没有马上放进口袋,而是翻了一下。
名片背面是空白的。
但正面的信息够用了。
李振邦,某省理工大学计算机科学与技术学院,教授,博士生导师。
下面一行小字:教育部「新世纪优秀人才」,IEEE Senior Member。
「我今晚发。」我把名片收好。
「好。」他合上了笔记本电脑,端起拿铁喝了一口,然后皱了一下眉——大概也觉得跟拖布水有亲缘关系,「还有一件事。」
他放下杯子,看着我。
「你在代码里埋Bug这件事,你清楚后果吧?」
我的手指微微收紧了一下。
「学术诚信调查是双向的。查陈昊的代码来源是一方面,但如果查出你故意在代码中植入缺陷——即便是在你自己的代码里——并且利用这个缺陷在答辩现场制造事故,这个性质就复杂了。」
他说得很平静。
但每个字都像是用尺子量过的。
我沉默了三秒。
「李教授,那个Bug是我六个月前的一个开发过程中的遗留问题。」
他看着我。
「我后来修复了。在我自己的仓库里。commit记录里有修复的时间戳。」
他还是看着我。
「至于陈昊fork走的版本里为什么还有那个Bug——因为他从来没有同步过我后续的任何更新。」
李振邦的眼睛眯了一下。
很细微。
但我捕捉到了。
那是一种「我知道你在说什么,我也知道你没说的那部分」的表情。
他知道我在撒谎。
至少知道我在技术性地避开真相。
那个Bug不是「遗留问题」。是我精心设计的。
但Git记录是干净的。
从提交时间线来看,那个Bug的引入时间早于陈昊的fork时间。而修复时间晚于fork时间但在我自己的后续开发周期内。
一切看起来都是正常的开发迭代。
写Bug,发现Bug,修Bug。
程序员的日常。
至于为什么我没有通知陈昊同步修复——他fork了我的代码之后,从来没有跟我沟通过任何一行代码。
我有什么义务通知他?
李振邦沉默了大概十秒钟。
然后他笑了。
不是大笑,是那种嘴角微微上扬的、带着某种欣赏的笑。
「论文今晚发我。」他站起来,拿起笔记本电脑,「另外,你有没有考虑过读博?」
我愣了一下。
「我这边明年有一个名额。」他把电脑包的拉链拉上了,「方向和你现在做的东西有重合。如果你感兴趣,可以聊聊。」
他说完就走了。
没有多余的话。
走得很快。
我坐在咖啡厅的椅子上,看着他的背影消失在行政楼大厅的拐角。
手机震了。
陈昊的消息。
十七条。
我没打开。
直接划掉了通知。
又震了。
张培文的消息。
一条。
「周衍,明天上午来我办公室,我们好好谈谈。把你的论文和代码都带过来。」
我看着这条消息,反复读了三遍。
「把你的论文和代码都带过来。」
这句话翻译成人话就是:把你的东西交出来,我们想办法把这件事摁下去。
我把手机屏幕关了。
拿起桌上那杯没喝的、不知道是谁的、已经凉透了的美式,喝了一口。
凉的。
苦的。
但今天的苦味跟之前三年的不一样。
之前三年的苦是咽下去心也苦的那种苦。
今天这个苦,只苦到舌头。
咽下去之后,胃里是热的。
【第八章】
第二天上午,我没去张培文的办公室。
我去了学校学术委员会。
行政楼五楼,502室,门牌上写着「学术道德与监督委员会」。
我在门口站了十秒钟。
不是犹豫。
是在回忆。
三年前研一入学的时候,张培文带着我从这层楼路过,指着这扇门说过一句话:「这个办公室你永远不需要来。跟着我好好干,学位和论文都不用愁。」
呵。
学位和论文确实不用愁。
愁的是署名归谁。
我推门进去了。
接待我的是学术委员会的秘书,一个四十来岁的女老师,姓刘,戴着银框眼镜,头发在脑后扎了一个利落的髻。
「同学你好,有什么事?」
「我要举报学术不端。」
我把提前准备好的材料放在了她面前的桌子上。
一份纸质的情况说明,三千字。
一个U盘,里面是Git仓库的完整导出、commit日志的公证截图、张培文那封邮件的时间戳比对、以及陈昊fork记录的网页存档。
刘老师接过材料,翻了翻纸质说明,眉毛逐渐拧了起来。
她抬头看了我一眼。
「你是张培文老师的学生?」
「是。」
她沉默了几秒钟。
「这件事……你有没有先跟学院沟通过?」
「沟通过。」我说,「昨天答辩现场,赵德明院长在场。」
她的眉毛拧得更紧了。
「好,材料我们收下。按照流程,会在五个工作日内成立调查小组。」
她把材料收进了一个牛皮纸袋,在封口上贴了一张标签,写上了日期和编号。
「周衍同学,」她抬头看着我,银框眼镜后面的眼神有一种我说不上来的温度,「举报学术不端是你的权利。但你要做好心理准备,这个过程会比较……漫长。」
她顿了一下,大概是在斟酌用词。
「而且不会太舒服。」
我点了一下头。
「我知道。」
走出502的时候,手机响了。
张培文的电话。
不是微信,是直接打的电话。
这是三年来的第一次。
之前他找我从来都是微信消息,或者让陈昊转达。直接打电话,说明他已经坐不住了。
我接了。
「喂,张老师。」
「周衍,你怎么没来?」他的声音很急,喘气的频率比平时快了一倍,「我让你带材料来我办公室——」
「张老师,我刚从学术委员会出来。」
电话那头安静了。
安静了很久。
久到我以为他挂了。
然后他说了一句话。
声音很轻,但每个字我都听得清清楚楚。
「周衍,你知不知道你在做什么?」
我站在行政楼五楼的走廊里,阳光从走廊尽头的窗户射进来,把我的影子拉得很长。
「知道。」
「你——」
「张老师,三年了。」我的声音比我预想的要平静,「该走的路,我走完了。现在该走的程序,我也走完了。」
他没说话。
粗重的呼吸声通过听筒传过来,带着一种濒临失控的压迫感。
「你会后悔的。」
他说完就挂了。
挂断的声音很干脆。
像一扇门被摔上了。
我把手机放回口袋。
后悔?
研一那年,我帮他写了三个横向项目的结题报告,没有任何署名,没有任何报酬。我后悔了吗?没有。我告诉自己这是学习机会。
研二那年,我的第一篇论文被他拿走挂了一作,发在了国内一个二区期刊上。我后悔了吗?没有。我告诉自己导师挂名是行业惯例。
研三那年,我的核心代码被他亲外甥偷走,他跟我说「小事」。
那一刻我唯一后悔的,是没有更早地在代码里埋下那个Bug。
走廊尽头的窗户开着。
五月的风灌进来,带着楼下紫荆花的甜味。
我靠在窗台上,往下看。
校园的主干道上,学生三三两两地走着。有背着书包赶去上课的,有骑着共享单车往食堂冲的,有情侣手牵手在树荫下散步的。
一切正常。
没有人知道五楼的502室里刚刚发生了什么。
也没有人需要知道。
手机又震了。
李振邦发的邮件回复。
「论文已阅。第三章的模型部分写得很扎实,第五章的实验分析需要补充消融实验。另外,你的写作风格需要调整——太像工程文档了,缺少学术论述的层次感。修改完再发给我。」
没有寒暄。
没有鼓励。
纯粹的技术反馈。
我看着这封邮件,突然觉得鼻子有点酸。
不是委屈。
是一种被认真对待的感觉。
三年了。
第一次有人认真地看我的论文,然后给我具体的修改意见。
不是「写得不错继续加油」。
不是「你这个不行重写」。
而是「第三章扎实」「第五章要补消融」「写作风格像工程文档」。
具体的。
精准的。
有建设性的。
我站在窗台边,把那封邮件从头到尾读了三遍。
然后打开备忘录,开始列修改计划。
楼下有人在唱歌。
大概是社团在排练,走了调的吉他和压不住的人声,从草坪上飘上来。
五月的风很暖。
我在窗台上站了很久。
直到手机再次响起。
这次是一个陌生号码打来的电话。
「你好,请问是周衍同学吗?我是校学术道德委员会的调查员,关于你提交的举报材料,我们需要你配合做一次正式的笔录……」
我直起身,把备忘录关了。
「好的,什么时候?」
「今天下午两点,行政楼五楼502室。」
「我到。」
挂了电话。
看了一眼时间。
十一点二十三分。
还有两个半小时。
够吃一顿饭了。
我走下楼,往食堂走。
今天中午,我决定不吃三块钱的拖布水美式了。
吃一顿带肉的。
【第九章】
学术委员会的调查比刘老师说的「五个工作日」快得多。
确切说,从我提交举报材料到调查结论出来,总共花了十一天。
这个速度在高校学术调查里,大概属于光速。
原因有三。
第一,证据太硬了。Git的提交记录有GitHub服务器端的时间戳背书,每一次commit的SHA哈希值都是唯一的,不可篡改。
第二,张培文那封邮件太蠢了。「把这个版本整合一下,署你的名」——这种话用学校邮箱发,等于在犯罪现场留了一把刻着自己名字的刀。
第三,李振邦在答辩委员会提交了一份独立的情况说明。五个外校评审专家联合签字,把答辩现场的情况原原本本写了下来。
五个外校教授的联名说明。
张培文就是长了八只手也摁不住。
调查结论出来的那天是周五。
下午三点,学术委员会在行政楼的大会议室开了一个通报会。
到场的人不多——学院领导、涉事师生、委员会成员,加上几个负责记录的行政人员。
赵德明坐在最左边。
这十一天里他瘦了一圈。眼袋像两个沙包挂在眼睛下面,头发又稀疏了几分。
张培文坐在他旁边。
西装倒是换了一身新的,深灰色,但领带打歪了,也不知道是手抖还是故意的。
陈昊坐在最后一排的角落。
他不敢坐前面。
十一天里,答辩翻车的消息在学院里传得人尽皆知。那段现场视频——不知道谁录的——在研究生群里转了不下五十遍。
视频最火的那个片段,就是李振邦把注释放大到四十号字体,投影在幕布上,然后转头看陈昊的那个镜头。
有人把那个画面截了图,配了一行字:「此刻他的内心毫无波动,甚至有点想笑。」
陈昊这十一天没来过实验室。听说人一直待在出租屋里,外卖都是放在门口的。
我坐在中间靠后的位置。
学术委员会主任宣读了调查结论。
「……经查实,计算机学院2022级硕士研究生陈昊在其硕士学位论文中使用的核心代码模块,系抄袭同课题组成员周衍的独立研究成果,相关行为构成严重学术不端。」
「指导教师张培文在知情的情况下纵容并参与了上述行为,对学术不端负有直接责任。」
通报很长。
但核心结论就两句。
陈昊:学术不端成立,撤销答辩资格,硕士学位申请作废,退回重修。
张培文:取消硕士生导师资格,暂停科研项目主持人资格两年,行政记过一次。
另外,学校将把调查报告抄送省教育厅学术诚信数据库。
这意味着,这个污点会跟着他们一辈子。
通报宣读完毕后,会议室安静了几秒钟。
然后陈昊从最后一排站了起来。
他的椅子往后滑了半米,发出一声刺耳的金属摩擦声。
所有人都看向他。
他的脸色很差。不是苍白,是那种灰暗的、缺乏血色的黄。十一天没怎么见阳光的颜色。
他的嘴唇动了几下。
然后他开口了,声音沙哑得像砂纸在摩擦玻璃。
「我、我知道错了。」
他的声音在发抖。
「但是……但是这件事不是我一个人——」
「陈昊。」张培文的声音突然插了进来,很低,但很硬,像一把刀片横在了空气中间。
陈昊闭嘴了。
他回头看了张培文一眼。
张培文的眼神我看不太清——逆光,加上距离远——但我猜那个眼神里有警告。
别说了。
别把事情搞得更大了。
我已经是记过了,你别让我变成开除。
陈昊的嘴巴张了张。
最终什么都没说。
他坐了回去。
两只手交叉搅在一起,指关节捏得发白。
通报会结束后,人们陆续离开。
我走到门口的时候,一个声音在身后响起来。
「周衍。」
我回头。
张培文站在会议桌旁边,没有走。
其他人都出去了。
会议室里只剩下我们两个人。
阳光从百叶窗的缝隙里筛进来,把他的脸切成了一条一条的。
「你赢了。」
他的声音很轻。
比打电话那天还轻。
我看着他。
他的眼睛里没有愤怒了。愤怒这种情绪需要能量,而他现在看起来已经耗尽了所有的能量。
「张老师,」我的声音也很轻,「不是我赢了。是代码不会说谎。」
我转身走出了会议室。
走廊里的声控灯一盏一盏地亮起来。
我低头看了一眼自己的手。
没有抖。
指甲干干净净的,掌心里也没有血印了。
从六个月前那个凌晨到现在,一百八十三天。
够了。
手机震了。
李振邦的消息。
「论文修改版收到了。消融实验部分做得不错。来我这边读博的事,考虑得怎么样了?」
我站在走廊的阳光里,把这条消息读了两遍。
然后回复了三个字。
「考虑好了。」
发完消息,我把手机揣进口袋,走下了楼梯。
校园的主干道上,紫荆花开得正盛。
风吹过来,有几片花瓣落在了我的肩膀上。
我没有拍掉。
走到校门口的时候,我拐进了路边那家烧烤摊。
老板认识我——三年来我只在这里吃过两次,一次是研一入学庆祝,一次是第一篇论文被张培文拿走的那晚来买醉。
「老板,两串羊肉,一串板筋,一瓶啤酒。」
「好嘞!」
烤架上的炭火烧得正旺。
油脂滴在炭上,滋啦一声,冒起一团白烟,裹着孜然和辣椒面的香气往脸上扑。
我坐在塑料凳子上,等烤串。
啤酒先到了。
我拧开瓶盖,灌了一口。
凉的。
苦的。
但跟之前所有的苦都不一样了。
这种苦,是一种到了终点之后才能品出来的味道。
不苦。
是爽。
【第十章】
九月。
我在李振邦的课题组报到的那天,天气已经开始转凉了。
另一座城市,另一所大学,另一间实验室。
实验室在计算机楼的七楼,朝南,采光很好。
工位是提前分好的——一张一米二的办公桌,两个二十四寸的显示器,一把网面的人体工学椅。
比张培文实验室那把坐了三年、海绵都塌了的破椅子强了不止一个档次。
我把笔记本电脑放下来,把充电线插上,打开了终端。
习惯性地敲了一行命令:`git log --oneline -5`
屏幕上弹出来五条最新的提交记录。
最新的一条,是我昨晚改的论文第五章消融实验的结果分析。
commit message写的是:「update ablation study, add Table 5.3」
很正常。
很普通。
一个研究生日常的代码提交记录。
但我盯着这行记录看了很久。
因为这是我四年来,第一次在一个署着自己名字的仓库里,提交署着自己名字的代码,给一篇署着自己第一作者的论文。
没有人会来fork。
没有人会来改署名。
没有人会在凌晨一点给别人发一封「署你的名」的邮件。
七楼的窗户开着。
九月的风从窗口灌进来,凉飕飕的,带着远处某个食堂炒菜的油烟味。
「周衍!」
声音从实验室门口传来。
我转头。
一个戴着圆框眼镜的男生站在门口,短袖T恤上印着一个Linux企鹅,拖鞋,头发跟鸡窝一样。
「你就是新来的周衍学长吧?我叫方旭,博一的,李老师让我带你熟悉一下环境。」
他笑起来的时候露出两颗虎牙,看着像个还没毕业的本科生。
「走走走,先带你去食堂。咱们七楼的传统——新人第一天,学长请吃饭!」
我站起来。
「行,吃什么?」
「食堂三楼的麻辣香锅。」他一边说一边往外走,拖鞋在走廊的水泥地上啪嗒啪嗒响,「他们家的牛肉片贼厚,每次我都点三份——」
他絮絮叨叨地说着,声音在走廊里弹来弹去。
我跟在他后面。
走过走廊的时候,经过一扇窗户。
窗外的天很蓝。
九月的阳光晒在皮肤上,暖的。
手机震了一下。
我掏出来看了一眼。
一条新闻推送。
标题是:「某高校教师因学术不端被撤销导师资格,省教育厅通报批评」
配图是一份红头文件的截图。
文件上的名字是张培文。
我盯着那个名字看了两秒钟。
然后把手机揣回了口袋。
没有截图。
没有转发。
没有在朋友圈发一段意味深长的感慨。
往前走就好了。
楼下的食堂飘来麻辣香锅的味道,花椒和辣椒混在一起,又麻又辣又香。
方旭已经走到楼梯口了,回头冲我喊:「学长你快点啊!去晚了牛肉片就没了!」
我加快了脚步。
鞋子踩在楼梯上,一步一步,嗒嗒嗒嗒。
每一步都很踏实。
像踩在自己的路上。
亲~能在评论区给本故事打个分吗?用十分制的方式,求求了,喜欢这类的点点关注哦~