最新文章

实习有感

今年暑假,我体验了我人生中第一次实习。作为法学生,公检法自然是实习的不二之选。我就在检察院。原本规定两个月时间,现在还剩下两周时间。但是根据这一个半月的体验,接下来的两周也基本可以一眼望到头了。所以可以就此作出一个总结。 体制内工作的本质 在我看来,体制内的工作,特别是公检法单位的工作,一切以稳字当头。凡事都有规定的程序和办法,只需要照办,那么个人就几乎不会承担风险。同时,收入也比较稳定(虽然我没有收入)。眼下大厂纷纷裁员,体制内工作者就不需要担心这个问题。 但是凡事都有两面性。如果你的工作没有什么出错的余地,那么说明这份工作缺乏开创性。凡是有开创性的工作,必然是危险的,有时候甚至是犯法的。正如我认识的一位检察官所说,「检察工作是法律行业中的农民工」。原先我认为检察官是非常高端的工作,但是经过一个月的观察体会,也算是打破了一个滤镜。每天都要开会、讯问或询问当事人、处理案件当中的疑难问题、和多方协调。总而言之:和人打交道。法律工作并不像很多人想象中那样照本宣科、照章办事,而是存在大量琐碎的细节,消耗大部分的精力。日复一日,年复一年。所以,说是农民工,其实并没有太大的问题。而且,我们中国的体制有一个特殊性。举个例子:检察院受理的所有案件,在案卷前几页都会附上一个红头文件:案件社情舆情风险告知书。这是检察院下达给检察官的,内容很短,但要求检察官办案必须要考虑社会影响,有风险及时上报。再举个例子:检察院门口写着三行标语,其中第一行就是「为大局服务」。所以检察官办案时要考虑的问题非常多,而且这些问题几乎没有什么自己发挥的空间,大多数都是应用现有的流程,或者遵循上级的指示。真正能够体现检察官职业品位的,我想是在办案的释法说理部分。比如部分案件案情复杂、争议重大,检察官就要运用自己的法学知识,作出经得起考验的判断。但是这种发挥的空间也不大,同样要遵循以上所述的条条框框。总而言之,在我看来,法律工作总体上还是对既定规则的适用与解释,相对于其他创造性的活动而言,还是略显保守。 由小见大,体制内的工作,本质上是维护社会秩序、处理社会纠纷。不需要过多激进的做法,只需要维持一个社会的底线,然后其上的各种经济、文化活动就可以自由开展。西方政治学中有「守夜人式政府」的说法,我们的政府虽然结构上仍然处于强势地位,管得很宽,但是在治理理念上,却与「守夜人式政府」没有本质区别——无不为而无为。你不能够指望检察院动用雷霆手段,从根本上降低社会的犯罪率。同理,你不能指望公权力机关动用一些激进的手段,短期内搞个「大跃进」,提升社会发展水平。这既不符合现行治理观念,也不符合现有的体制。我们的治理观念是按部就班,在「少出错」的基础上「多收益」,在维护社会稳定的前提下促进发展。所以体制内的工作,同样也遵循这一普遍观念。 数字赋能的矛盾性 因为我考研志在计算机,所以比较感兴趣的一个问题是检察机关能不能运用智能化、信息化手段,业内称之为「数字赋能」。 经我观察,检察机关现在已经有了全国统一的办案系统,在上面可以撰写文书、填写案件信息、处理案件状态,还有统计系统可以用。功能上大体相当于一个全国性的ERP。但是我觉得它的智能化程度还是不够高。通过我对书记员工作的观察就可以得出这一结论。 比如,书记员有一项工作叫做「台账」。其内容大体就是按照一定的要求,从一系列案件中统计出各类信息,供办案人员或上级查阅,从而为案件提供参考或研究制定政策。这项工作其实并不需要非常深厚的法学知识。但是问题在于,要求是千变万化的、细粒度的,办案系统的字段是固定的、粗粒度的。如果你想像查数据库那样打一条超长SQL,然后看着热乎乎的结果洋洋洒洒吐出来,那你就大错特错了。文科的东西的一大特点是边界模糊,特别是对于法学,一些表述虽然意思一样,但是说法千变万化,无法被机械筛选、比较。所以也就不可能有固定的字段、固定的枚举来支撑你的统计。 但是现在有了AI,这些工作就可以被Agent完成。讲到这里,可能一些人觉得检察院推行AI是理所应当的事情。但是根据我的观察,至少有以下几个阻却理由: 检察院用的都是保密系统,不能连接外网,更不用说调用各家大厂的API。即便自部署内网LLM,算力成本谁来承担? 如果台账工作能由AI代替,那是不是意味着书记员无用武之地了?辛辛苦苦考公考编,进入体制内工作,却被AI代替——这种情形可能出现在企业,但是不可能出现在体制内。如果大家认为的铁饭碗都不受保障,那么社会稳定性也是可以想见的了。 用AI做出来的台账,如果有问题,谁来承担责任? 依据目前公众对AI的态度,如果大家知道了检察院在用AI办案,会不会有所抵制,从而产生舆情问题? 所以这是非常微妙的一件事情。并不是说AI不好,恰恰相反,AI很好,好到我们不敢轻易使用。我个人相信将来的趋势当然是AI代替重复性的文书工作,或许将来也不再需要那么多的书记员(现在一个检察官配两三个书记员)。但是在体制内推行新技术,绝对没有想象中那么简单。 如果以上所说都不能构成理由,那么还有一个更加现实的原因:AI是快速发展的行业,需要最顶尖的人才才能落地。可能有人觉得推行AI就是装几个Harness、接几个模型,然后开始干活。但是看看现在互联网大厂推行Agent治理,哪一个不是豪掷千金?哪一个不是一条龙?如果想在法律行业推行AI,单单让法律工作者用上AI是不够的,整个基础设施都要建构在AI上。而且,既然它是快速发展的行业,那就说明它会快速迭代。现在每过几个月就会出来一批顶尖模型,更不用说中端模型的血战。新模型需要有人部署、调试、验证,与之配套的一整套基础设施都要频繁迭代。Money, money, money. 体制内哪来的这么多长期工作的顶尖人才呢?哪来的这么多钱呢?据我所知,我们现在用的办案系统,尚且都有一部分是外包。本质上,体制内是与外界隔离的独立生态。大厂发布新模型、新工具,我们很快就能用上;但是在体制内是行不通的,必须要经过漫长的论证、审批过程。还是那句话:稳字当头。 实习生的地位 我们实习生在检察院的地位其实是不用多说的,既没有正式编制,也不能承担责任,在检察院看来,我们不算是「自己人」。给其他同事分配工作,可能多少还要考虑一些「比例原则」和社交观瞻,量力而行。但是给实习生派遣工作,就完全没有这个顾虑。随时on call,随叫随到。而且,一些前辈在派遣工作的时候,不愿意花时间教学。检察院的办案系统很复杂,流程也很多,经验性的做法到处都是,而且容易出错。如果没有人告诉你,你自己是不可能悟出来的。但是这些前辈把工作交给我们,说「这个很简单,你看着办就行了」,然后留我们在那里抓耳挠腮。不懂的问题太多了,只能一次一次跑去请教。我觉得这难免过于草率了些。固然前辈们长年累月积累起来的经验不可能短时间内传授给几个实习生,但是既然要安排工作,是否应当有必要的「新手教程」?我在这个过程中完全感受不到前辈们提升我的能力的意思,只感觉到想让我快点帮他们搞定手头的杂务的意思。这不管是与学院的要求相比,还是与实习的性质相比,都是严重不匹配的。 其中最使我难以接受的是,个别几位前辈给我交办工作,并不是找一个帮手的态度,而是完全想要转移工作的态度。比如一位前辈,经常交给我一大摞卷宗让我标注页码、打印卷皮,又耗时间又无聊。我自然是想快点做完,好腾出时间复习数学和408。但是只要我进一次她的办公室,她就永远能交给我新的卷宗。一次,我交接工作时留心观察了一下,发现原来她也在复习考试,桌上放着试卷和资料。既然大家都是求学深造者,何必来这样一个移花接木?你的考试重要,我的考研难道就不重要吗?况且,我只是一个实习生,而你是享受公务员待遇的正式职工,理应比我承担更多的责任,现状却是我承担了大部分的杂务,换取你光明的前途。再如,另一位前辈一天交给我接近20cm高的一摞卷宗,让我扫描。然后她自己提前一小时下班,把我留在办公室工作。到此我认为实习的性质已经变了,已经谈不上「习」这个字,完全是把我们当作工具人来看待。虽然我所处的检察院位于基层,没有那么多高大上的工作,但是我认为基本的性质不能变。如果实习只是打杂跑腿,60天如一日,分文不取,那么我认为这样的实习无论在体验工作的意义上还是在提升认知的意义上,都是没有必要的。 我们其实跟普通的力工没有本质区别。力工在工地上出卖体力,换取报酬;我们在检察院也出卖体力,换取实习证明。之所以不是脑力而是体力,原因前面已经提过,大部分劳动其实是体力劳动。 我觉得在体制内工作,能力并不是首要的,你所处的等级才是首要的。体制内的底层逻辑很好地体现了「吃得苦中苦,方为人上人」这句话。在你地位低下的时候,你要能吃苦,能逆来顺受,接受上位者的领导。在你熬足了资历,自己成为上位者的时候,即便你能力平平,也可以反过来向你的下位者施压。原因其实很好理解,这些单位都是科层制单位,层层领导,层层传达。越往底层,压力自然越大。我们可以说是最底层,各个上级提出的要求,我们都要满足,又不能说什么。 我记得在我实习第二周的时候,来了一位选调生。那自然是非常高贵的了。办公室主任带着她,逐个办公室介绍、打招呼。回想我来的时候,连一张自己的桌子都很难得到,随便找了间办公室把我塞进去,更没有人知道我的名字,最多只知道我的姓。像古代嫁了人的女子一样,只知「王氏」「赵氏」而不知其名。但是安排工作的时候,我绝对是排在那位选调生前面的首选。因为她是通过「科举考试」考进来的,出身比我好,等级位阶比我高。我只是一介白丁。 再讲一件令我印象非常深刻的事情。一次,办公室主任要求我们写一些实习感悟,将来发表在检察院的公众号上,成为一篇推文。我的文风向来是比较放浪形骸的,不过为了体现检察工作的庄重威严,还是收敛了不少。因为也没有从事太多有价值的工作,就写了一些自己的感受,比如「犯罪离我们并不遥远」,以及日常工作中的一些收获等等。总体来说,比较中规中矩。但是主任看过后认为「不够成熟」,写得「较为感性」。她指出,我们的实习感悟一定要体现出检察工作的导向,起到良好的宣传作用,体现出实习生在检察院丰富的实习经历。我寻思我一个卖苦力的,每天的工作就是搞搞文书,上哪去写那么多「丰富的实习经历」呢?主任听说我们主要从事的都是基础工作,自己想了个办法,说在本周内联系检察官,带我们去参与提审提讯、旁听庭审。有了这样的经历,争取在下周就能形成一篇比较成熟的稿件。听完我是很哭笑不得的:我在这里干了一个半月,都没人带我去提审、旁听,现在有了宣传任务,总算想起来了。事实上,体制内的这种政绩本位、官本位思想处处可见。你不需要有什么真情实感,「实习感悟」只是一个名字,你真正要写的是一篇冠冕堂皇的宣传文,只是挂在你的名下显示真实感。主任的这个要求,我相信也并不是她临时起意,而是她也受到了上级指派的任务。任务就是这样层层传达下来,下面的人又没有什么变通的余地,所以就会变质。 如是种种,你可以在中国的公权力机关里找到很多类似古代官场的现象。我不得不再次提到之前那位检察官,他是我在检察院知道的所有检察官中最通透的一个。他说,当年他毕业的时候,法学就是红牌专业,现在依然是红牌专业。「法学生都想进公检法单位,但是……」他没有明说,但其中含义可以会意。他说他的儿子学习一般,现在也偏文科,但是他不想让儿子学文科,希望他学理。随后这位检察官要出差,出差前最后一次见面,他跟我说,进入体制内要谨慎,能深造还是要深造。还说在他们那个年代,考公、进体制内并不是就业的首选。听说我将来要考计算机,他开玩笑说让我好好努力,争取开发出AI法律的技术,好让他们这些人早点退休。我听了后只能苦笑,感触良多:我眼中的高端职业检察官,尚且不愿让自己的儿子学法,尚且劝退我进体制内。如果一个行业真的好,那么从业者自然倾向于把自己的后辈也推荐进这个行业吃红利。如果这个命题大家同意,那么同样也该同意它的逆否命题。 个人感悟 就我个人而言,实习的体验可以说是中等偏下的。一开始选择来检察院实习,是因为志不在法学,想找一个相对清闲的单位,方便复习考研。但是一个半月下来,没见得有多么清闲,却也几乎没学到什么实务知识。可以毫不夸张地说,我所做的工作98%以上和法学没有任何关系。像是打印文书、排列卷宗、标注页码、整理台账、搬东西、送文件等等,不需要任何法学的专业知识,我相信一位身强体壮的初中生能比我做得更好。这时候看着手中的985学生证,你会怀疑读书的意义。反观身边的少数优秀的同学,能够在层次较高的法院、律所实习,他们在那里可以学到真本事,将来想必也可以成为一流的人才。但是大部分的同学去实习,从事的都是像我一样平凡而琐碎的工作。 我觉得这从某种程度上可以反映出文科的就业现状。知乎上有个答主说得不错:文科生就像猫,理工科生就像牛马;个别的猫被人豢养,过上锦衣玉食的生活,但大部分猫都是流浪猫,随时可能死掉;也有个别牛马被人豢养,成为马中赤兔,不过大部分牛马可以依靠卖苦力得以维生。我觉得这个类比可谓鞭辟入里。我们不否认各行各业都有一流的人才,这样的人才可能是百里挑一,享受最顶尖的待遇和威望。但是对于我们绝大多数人来说,充其量也就是二流或三流人才。文科二三流人才的待遇与理科相比,几乎是质的差异。我认识的计算机专业的在校居于二流的同学,去面试一些中大厂,仍然有不少机会。我认识的身边法学的二流同学甚至部分成绩名列前茅的同学,却做跟我一样的工作。这个差距在我看来是非常悬殊的。文科的斩杀线是非常高的,如果你达不到非常精通的水平,那么你的就业质量将断档下滑。 归根结底,中国的文科从历史上来看,就是精英学科。科举考试只录取人中龙凤,中举者一夜之间鸡犬飞升,落榜者没有功名,只得回家务农。到了现代,大学纷纷开设文科专业以显示其「综合性」,社会却提供不了那么多的就业机会,导致文科生中滥竽充数者比比皆是。一个专业能力欠缺的人,也可以通过包装,摇身一变成为佼佼者。而且,作为精英学科,社会资源往往被少数人把持,普通人更加难以拿到入场券。 总而言之,一个半月的实习很短,却足以使我厌恶眼下的处境。最大的感触就是:除非你个性并不鲜明,想过那种安安稳稳、一眼望到头的生活,或者内心迷恋文科,觉得自己一定能力争上游,否则,若你还有一丝创造力和生机活力,应当毫不犹豫地选择理工科。千万不要妄想做一个「体面的中端人才」,只要踏进文科大门,一流人才以下一切免谈。如果你还未上大学,你应当拿起数理化的书本;如果你已经上大学,你仍然应当拿起数学和专业课的书本,准备考研跨考理工科。

今天就让我大哭一场吧

在一个风和日丽的下午,我一边在校园里漫步,一边打了一通电话。这大概是我迄今为止人生中最沉痛的一通电话。电话的那边是一个考研咨询机构的老师。我打过去,是因为在B站看到了他们的广告,正好我也准备考研,而且我的考研是不那么被人理解的,所以便想要作为一个小白了解一些信息。 「我想考计算机的学硕」——这么平平无奇的一句话,如果从本欲保研的CS学生口中说出,是一种妥协;如果从本欲考研的CS学生口中说出,是一种值得鼓励的远大志向;然而,如果从一位本科和computer science毫不沾边,甚至于背道而驰的学生口中说出,那么人们就会讥笑、讽刺、怀疑——这恰恰是我作为一个法学生所说出的话。 计算机,computer science,对我来说不是一种工作、一种谋生手段,而是一种纯粹的兴趣。我对它的情结是宗教式的。宗教不容背叛,然而我就是那个异教徒。高中,出于逃避、恐惧、赌气,以及其他许多中二病十足的原因,断然离开了理科班,进入了更加轻松的文科班。从此断送了自己自幼的理想,从此看到学习计算机的同学,只有满眼的羡慕与悔恨。我的人生在我17岁那年留下了一个永远的遗憾。我大概永远无法进入科研界了,永远无法成为「计算机科学家」了。不能成为这样的人,成为其他的人又有什么分别呢? 我所抓住的唯一一根救命稻草是考研。我想考计算机的学硕。何必是学硕?专硕又奈何?但是我想要成为学硕,这意味着我不是在谋生,而是在研究。大概是一种心理性的补偿吧,毕竟事实上,学硕和专硕都是广受认可的。只是学硕有一个更加响亮的title,好像有了这个title,我就能洗掉从前打在我身上的「文科生」的烙印,就能够让所有人用真正研究者的标准来看待我,而不是说一句「那个法学生计算机搞得还不错」。——即便不考又奈何?只要自己想,什么专业都可以和计算机打交道。但是我想让计算机成为我人生的主线,而不是一个业余爱好。这是地狱难度的考验,但是此举不成功便成仁——我是抱有这样的心理的。 但是那通电话让我的梦想破灭了。对方说,学硕可以不用考虑了。那么多考研的优秀计算机本科生,学硕的名额又那么少,导师为什么要录取你一个文科出身的学生呢?即便是专硕,也要认真择校,复试期间风险也很高。而且本科期间,我没有上过计算机的基础课,能力本来就差,还要补数学。总之,她让我不要抱有太大希望了。 挂了电话,我可谓心灰意冷。人生当中第一次听到了梦想破碎的声音。如果不能从事自己喜欢的工作,那么人生在很大程度上是没有意义的。我本来具备这样的机会,可惜我自动打碎了它,如今再想捡起来,已经是无处可寻。 那些科目的考试成绩,尚且可以用日复一日的苦学来弥补;复试对于出身的歧视,却完全无计可施。 我不禁想到自进入大学以来,没有一日不在想计算机;还有那些绞尽脑汁去学的数学;还有那些在深夜给自己加油打气;还有凭一腔热血加入的升华工作室;还有花费半年,和计算机学院最优秀的同学参加的软创竞赛和获得的国二——它已经被那位老师判决为「无用」。我的大学生活全部建立在对计算机的希望上,可是这个希望现在不幸离线了。 You picked the wrong house, fool! 今天就让我大哭一场吧!

AI客户端的上下文问题

我感觉虽然现在LLM大发展,但是应用端的软件还不是很完善,特别是一些平时不会注意到但很核心的功能。 比如AI聊天客户端——就拿Cherry Studio来说吧——在第一轮对话中,模型调用MCP工具获得一些内容,然后输出回答。紧接着第二轮对话中,Cherry Studio就会丢弃第一轮中MCP工具获取的内容,只发送用户的问题和模型的回答作为上下文。这就会导致从第二轮对话开始,每一轮对话都可能缺失完整的上下文,从而有效信息越来越少,最后自说自话。 我们对于客户端的期望是,能够尽可能长时间多轮次工作而不丢失回答质量。显然这样的策略并没有落实到Cherry Studio中。 我想出来的一个很不雅的解决方法是:在所有MCP服务器前面挂一个透明代理,每次调用工具的时候,都把工具的返回缓存到本地文件。再在客户端上挂一个脚本,每轮对话都自动读取缓存的文件,作为上下文一并上传。这样能勉强解决问题,但是治标不治本。其本在于,目前的客户端没有一个比较统一的上下文处理策略。各家客户端处理上下文的方式都不怎么公开透明,什么时候压缩,什么时候裁切,都无法精细控制。这方面做得比较好的是Codex CLI和Claude Code——它也不敢做得不好,因为服务的是开发者,代码上下文要是随意裁掉,是要出大问题的。但是普通用户用这些工具问问题,就给人一种很奇怪的感觉。我们用Cherry Studio的原因,就是因为它支持的提供商多,GUI比较方便,上传文件、配置MCP什么的也比较直观。结果它在上下文这个要命的问题上出了岔子,那地位就很尴尬了。 总之,现有的AI客户端都处于一种各自为政的状态。毕竟LLM的接口才刚基本统一没多长时间,各种协议也还在野蛮生长期,只能等待社区和开发者的贡献了。或许有机会的话,我也可以贡献一点点。

Agent是一些人的弥赛亚

之前经常听到一些朋友说,网上现在有很多「文科生用AI开发软件,脚踢理科生」的说法,十分引人发笑。我原先觉得怎么可能,我们文科生再不行也不会狂妄到这种地步——污名化!一定是污名化!然而今天我就看到了这样的一篇文章,打了我的老脸。作者在文中洋洋洒洒,描述自己和朋友怎样使用Agent做出软件,赚到第一桶金,把Agent快捧成新时代的珍妮机了。这不禁使我想到一位著名文科生巴金的一篇文章《机器的诗》。看来一些文科生多少是有点技术拜物教倾向的。圣经退出了书店和课本,弥赛亚却仍然活在一些人的脑中。文科生也好,理科生也好,赚钱的也好,没赚钱的也好,凡是对AI带有这种态度的,在我看来都是身处一场虚妄的狂欢节中而不自知。 那么让我们正本清源——他们所说的Agent赚钱的本质究竟是什么呢? 软件是一种商品,商品可以用来买卖。通过生产软件—出售软件,就可以赚钱——这就是软件这门生意的本质原理。 过去只有懂得一定开发技术的人才能生产软件,而在中国,这门技术在大学中一般只有理科生才可以正式学习。所以一些做题家们便自然而然地把「写软件」和「理科生」联系在一起,「文科生」是「写软件」这件事的对立面。这个预设的立场首先就糊弄了很多人。这个预设可谓威力无穷,因为这样一来,理科生写出软件就变成天经地义,文科生写出软件就成了一件惊天地泣鬼神的事。这和「女生居然会修电脑」、「理科生居然会作诗」本质相同。 立场立住了,接下来就要考虑文科生怎么写出软件。以前基本上是没有办法的,现在有了Agent,文科生也可以写软件。实际上本质还是花钱雇人帮他写,只是薪酬低到令人发指,但我们对此暂且按下不表。总之现在文科生也能写出软件。以前我们连镰刀都没有,现在我们用上了联合收割机,不管怎么说,这也算一种进步。 那好吧,理科生也能写软件,文科生也能写软件,这有什么区别呢?但是有了刚才那个预设立场,区别就很大了:文科生明显更厉害一点嘛!这就是一种典型的自我矮化再自我拔高的手法。 随后,文理科生都想赚钱,但是商业上没有人管你是文科生还是理科生。你的软件能不能卖出去,由很多因素决定。这是单纯读几本书、学几门语言所解决不了的。比如我认识某个公司的老板,他说他想要一套ERP系统,我手头刚好就有一套,那么我就可以卖给他,其他人就没有机会。所以这个钱才能被我赚到。现实生活中,大量的商机不都是这样产生的吗?特别是私活,更是依赖人脉疏通。不然有货无市,不管是文科生还是理科生,恐怕都是束手无策的。 到这里,一些人中龙凤各显神通,成功地把自己的软件卖出去了。但是其中香槟开得最多的,恰恰是一部分文科生。因为他们觉得,自己能卖出去软件,是一个技术成就,而不是一个商业成就。技术因素是$A$,商业因素是$B$,那笔钱是$C$。$A+B \Rightarrow C$和$A \Rightarrow C$,他们搞混淆了。好像把软件写出来,就能自然而然地卖出去。实际上怎么可能是这样?多少人的软件砸手里了。 幸存者偏差!我只看到一些幸运儿,把自己怎么接单、怎么洽谈的过程隐藏起来,把功劳全部归于Agent,以此误导广大不明真相的群众。 而且,这类人往往是最有「高人一等」的错觉的。我看的那篇文章的作者,就觉得大部分人用AI改论文、问问题,已经是很低级的用法了,他已经看不上了。还告诉大家要用AI来「帮你干活」。有一种「新大陆是我发现」的感觉,有一种「众人皆醉我独醒」的感觉,有一种「悟已往之不谏,知来者之可追」的感觉,有一种「Let there be light」的感觉。 这种行为本身恰恰是对我们文科生的污名化。

免费的才是最贵的

悦动青春项目组群里讨论了一个在我看来很有意思的话题:如果我们用的开源库有漏洞,可能出现安全问题并造成损失,应该由谁来承担责任?这个洞是我们修还是contributors修? 我的观点是我们和contributors都得修,只是谁先修谁后修的问题。并且简单说明了理由。当然,我的想法是很幼稚的,也并不希望获得大家的赞同。 这就涉及到一个开源库的安全责任问题。开源当然好,大家贡献,集思广益。但是出现了问题,也是大家有难同当。总不能说,库是某些人开发的,出了问题就要由他们去承担责任,赔钱!因为人家从来也没承诺过这个库100%安全,并且也没有要求你用,更没有收钱,甚至你还可以改。是你自己选择去用,出了问题怎么能怪人家呢? 所以从中可以看出,「互联网不是法外之地」,这句话某种程度上是错的——互联网就是法外之地。出了问题没有人承担责任,而是所有的开发者共同稀释责任。之前人们说开源精神实际上就是共产主义精神,但是真正到了「有难同当」的时候,大家难道真的就那么舒服? 这种精神将所有的开发者绑定在一起了,再也不可能有个别一群人为一个严重问题负责。开发者群体在实质上是风险共同体。 比如我们开发了一个产品,普通用户使用的过程中,出现了安全问题,被人攻击,导致数据泄露或经济损失。经过调查发现,问题出在我们用的一个开源库中。按照一些人的想法,这时候我们应该发表一条声明,称「该问题由第三方库所致,并非本团队责任」,然后事不关己高高挂起。 但是用户不会相信我们的鬼话。按照常人的逻辑,这就像买了一辆汽车,结果里面的零件出了问题,当然要同时追究汽车商和零件商的责任。因此用户觉得也应该同时追究我们和开源库作者的责任。但是对于主张发声明的那些持有「开源精神」的人来说,这怎么能是我们的责任呢?明明是开源库作者提供产品给我们用,我们也是用户呀! 汽车商和零件商之间一般有订货合同,约定出现质量问题当由双方共同负责。我们和开源作者间一般也有一份「合同」,那就是各种各样的开源协议。这些协议充满了共产主义精神,却唯独没有说明责任的分配。甚至很多协议明确指出,开源作者不承担任何后果。 因此最后导致的结果,往往是大家共担风险。没有任何法律可以告诉我们,谁是真正的责任方。换言之,当没有一个明确责任方的时候,所有人都是责任方。 如果大家谁都不愿修,时间就这样一分一秒地流逝,损失就这样一步一步地扩大。最后我们发现,开源作者损失的是信誉,而我们作为产品开发方,损失的不仅有信誉,还有真金白银。 因此我们不得不着手自己修复漏洞、给开源库提交pull request。虽然在开源社区里,我们的无私奉献获得了同行的赞誉,可惜付出的代价却只对自己和股价可见。 「共产主义可能首先在少数几个国家取得胜利」,这时候我们不禁怀疑这个著名论断。 在我看来,真正正确的做法是:先自己修复漏洞,弥补用户的损失,再发表声明,说明问题出在开源库,而不是我们自身。物质上要给予补偿,精神上要正本清源。 从前我认为一些企业花钱去买软件是一种很傻的做法:这么多的免费开源库为什么不用呢?现在想来,这才是一种大智慧:那份合同所能规避的损失远比使用一个开源库所能节省的成本多得多。

国赛,粉墨登场

去年的这个时候,我还在为该死的小组作业焦头烂额,替躺平的组员承担责任。今年的这个时候,我已经挺进了软创的全国决赛,跻身全国18强。怎么说呢?只能说如梦似幻。我从来没想过软创能拿奖,甚至拿省奖我就已经很开心了。没想到现在居然要在国一和国二中间选座位。如梦似幻。这是我人生中第一次参加国家级竞赛。很多从小就参加各种竞赛的竞赛生、OIer之类,想必对这种经历早已轻车熟路了吧。但是这对于曾经走了一些弯路的、没有任何背景和天赋的「线粒体」而言,难道不是一件值得纪念的事件吗? 或许将来某天,我会觉得软创也“不过如此,水赛而已”,但是往往能说出这样的话的人,早就有了这样的经历。吃了葡萄以后才说葡萄是酸的,和那些没有吃葡萄,听到吃了葡萄的人说葡萄酸,而自己也跟着说葡萄酸,我想还是有本质区别的。 可能在一些人眼里,我已经是一个「不可方物」的人士。但是在我自己看来,我什么也算不上。真正懂计算机、热爱计算机的人,早就在各大核心项目中发光发热,产出学术和工程成果。而我目前能够产出的,只是一系列非常不成熟的MVP。而且,软创的评价体系很大程度上在于包装,代码写得好不好是在所不问的。在这上面得了奖,只能说明我能够讲一个好的故事,并不代表我能够做一个好的产品。 同样道理,我认为我和这些真正的大佬相比,也是有本质区别的。我觉得我缺乏的是「钻进去」的境界。若想成活,必先疯魔。我现在的杂务还是有点多,什么都想做,但精力却很有限。如果我真的敢去成为像他们一样的大佬,首先我应该做到心无旁骛、全身心投入。而且,不应该被一些世俗的琐事所困扰,更不应该拿它们当作驱动力。 一个人真正能够大进步大发展的时间段,恰恰是达到一种忘我境界之时。追求自己想追求之事,毫无顾虑地去尝试、创造新事物,单纯为了精神上的满足去探索。这种纯粹的心境其实就是儿童的心境,对于成年人而言是很不易得的。 并不只是计算机。推而放诸各个领域,包括我的专业法学,有哪个不是这样呢?想做法学研究就要坐冷板凳,就要参与实务。不可能每天东边瞅瞅西边看看,那样成为不了真正的法学大家。 换言之,如果用数学来类比,不妨设世界和个体是两个变量。一些人的人生是:个体是世界的因变量(「我反应世界」,注意不是「反映」,而是react),而另一些人是:世界是个体的因变量(「世界反应我」)。这话听起来可能有点哲学,实际上就是个体占据主动还是世界占据主动的问题。在我看来,个体占据主动比较有利于个人的成长。虽然我也说不出个所以然,让我把这个问题留给哲学家,但我的直觉告诉我是这样的。 当你想要为了寻求别人认同而去做一件事情的时候,动机就已经不纯粹了。因为这意味着你的动机是想要对别人的看法作出反应。不要把对个体精神的控制权轻易地移交给第三方。 回到软创上来,获得这样一个成绩,实则也是我自主选择的结果。若非为了跨考计科时不至于在复试吃亏,或许我也没有特别强烈的欲望去参加。我觉得这一步迈出来是对的。付宇轩的人生可能在这里发生一个重大转折。

使用归纳法证明二项式定理

首先需要证明一个引理: $$\binom{n}{k-1}+\binom{n}{k} = \binom{n+1}{k}.$$ 证:首先,左边 $$ \binom{n}{k-1}+\binom{n}{k} = \frac{n!}{(k-1)!(n-k+1)!} + \frac{n!}{k!(n-k)!}. $$ 而右边 $$ \binom{n+1}{k} = \frac{(n+1)!}{k!(n+1-k)!}. $$ 试图使左右两边相等,需要使得左边分母为$k!(n+1-k)!$,所以通分得: $$ \begin{align*} & \frac{n!}{(k-1)!(n-k+1)!} + \frac{n!}{k!(n-k)!} \\ &= \frac{n!k}{k!(n-k+1)!} + \frac{n!(n-k+1)}{k!(n-k+1)!} \\ &= \frac{n!k+n!(n-k+1)}{k!(n-k+1)!} \\ &= \frac{n!(n+1)}{k!(n-k+1)!} \\ &= \frac{(n+1)!}{k!(n+1-k)!} \\ &= \binom{n+1}{k}. \tag*{$\blacksquare$} \end{align*} $$ 随后证明二项式定理 $$(a+b)^n = \sum_{k=0}^{n}\binom{n}{k}a^{n-k}b^k.$$ 证:使用归纳法证明. 首先证明$P_0$,显然, $$(a+b)^0 = 1 = \sum_{k=0}^{0}\binom{0}{k}a^{0-k}b^k = a^0b^0.$$ 故$P_0$得证. 接下来,假设$P_n$成立,验证$P_{n+1}$是否成立: $$ \begin{aligned} & (a+b)^{n+1} \\ &= (a+b)(a+b)^n \\ &= (a+b) \sum_{k=0}^{n} \binom{n}{k} a^{n-k}b^k \\ &= \sum_{k=0}^{n} \binom{n}{k} (a^{n+1-k}b^k + a^{n-k}b^{k+1}) \\ &= \sum_{k=0}^{n} \binom{n}{k} a^{n+1-k}b^k + \sum_{k=0}^{n} \binom{n}{k} a^{n-k}b^{k+1} \\ &= a^{n+1} + \sum_{k=1}^{n} \binom{n}{k} a^{n+1-k}b^k + b^{n+1} + \sum_{k=0}^{n-1} \binom{n}{k} a^{n-k}b^{k+1} \\ &= a^{n+1} + \sum_{k=1}^{n} \binom{n}{k} a^{n+1-k}b^k + b^{n+1} + \sum_{k=1}^{n} \binom{n}{k-1} a^{n-(k-1)}b^{(k-1)+1}. \\ \end{aligned} $$ 注意到这里,我们给$\sum_{k=0}^{n-1} \binom{n}{k} a^{n-k}b^{k+1}$更换索引为$\sum_{k=1}^{n} \binom{n}{k-1} a^{n-(k-1)}b^{(k-1)+1}$,观察易得这两个式子是相等的(坦诚地说,我第一次学的时候并没有观察出来). 继续进行变换: $$ \begin{aligned} & a^{n+1} + \sum_{k=1}^{n} \binom{n}{k} a^{n+1-k}b^k + b^{n+1} + \sum_{k=1}^{n} \binom{n}{k-1} a^{n-(k-1)}b^{(k-1)+1}. \\ &= a^{n+1} + \sum_{k=1}^{n} \binom{n}{k} a^{n+1-k}b^k + b^{n+1} + \sum_{k=1}^{n} \binom{n}{k-1} a^{n+1-k}b^{k}. \\ &= a^{n+1} + b^{n+1} + \sum_{k=1}^{n} \left[ \binom{n}{k} + \binom{n}{k-1} \right] a^{n+1-k}b^{k} . \end{aligned} $$ 利用一开始证得的引理,即有 $$ \begin{aligned} & a^{n+1} + b^{n+1} + \sum_{k=1}^{n} \left[ \binom{n}{k} + \binom{n}{k-1} \right] a^{n+1-k}b^{k} \\ &= a^{n+1} + b^{n+1} + \sum_{k=1}^{n} \binom{n+1}{k} a^{n+1-k}b^{k} \\ &= \sum_{k=0}^{n+1} \binom{n+1}{k} a^{n+1-k}b^{k}. \end{aligned} $$ 所以, $$ \begin{align} (a+b)^{n+1} = \sum_{k=0}^{n+1} \binom{n+1}{k} a^{n+1-k}b^{k}. \tag*{$\blacksquare$} \end{align} $$ 虽然在这里写得洋洋洒洒,但是说实话,在下天资愚钝,第一次见到这个证明,还是没那么容易理解的. 而且很有可能,现在证得出来,将来某天就会忘掉. 所以数学的学习是一个不断积累的过程,现在吃下的每一口,将来都会成长为构成身体的肌肉和骨骼.

写代码从来不是核心

以前对于一些程序员,写出来的代码实际上原创的成分很少。遇到问题就上Stack Overflow、CSDN或者各种博客,然后照抄。稍微复杂一点的工程,就涉及到架构问题。这时候就从各种架构书里去找现成答案,然后套用在自己的项目里。境界再高一点,才可以稍微有一点自己的创造性思考和实现。诚实地说,这种程序员的核心能力,究竟有多少价值?能看懂语法、懂设计模式、懂算法、会运用现有的工具解决问题,这些能力在过去成了程序员的护身符。但是现在有了AI,程序员发现自己会做的AI也会做。于是很有挫败感,觉得AI抢了自己的饭碗。 现在回过头来审视,过去程序员们那些引以为傲的能力,真的有不可替代性吗?或者说不应该被替代吗?假设这些能力不能被替代,那么人类将始终处于一个停留在低层次劳动的阶段。 比如,过去老百姓不识字,于是知识阶层便使用文字作为权力垄断的工具。后来人人识字,有一些迂腐的知识分子觉得“斯文扫地”,自己风光不再。但是很显然,人人识字是一种历史的进步。而且权力的垄断依旧存在,这时的垄断者正是那些不依靠文字,而依靠更高层次治理能力的人,比如军事才能、政治才能等等。 类比到程序员身上,从计算机科学诞生以来直到今天,行业内的大佬很少有纯以代码输出能力作为自身核心能力的。很多是搞数学的,然后是搞算法的,还有设计架构的、搞底层的。有几个大佬是依靠组装轮子把自己立起来的呢?但是过去程序员用组装轮子把自己立起来,今天有了更厉害的组装轮子的工具(AI),却还觉得这是一种非常珍贵的能力,不应该被替代。组装轮子当然算一种能力,它可以创造产品、换取报酬、养家糊口。但是假设不允许这种进步发生,人类又怎么能够迈向更高的一个层次呢?哪次技术进步不是伴随着大规模的就业洗牌呢?无论这个牌怎么洗,王牌永远都是香饽饽。对程序员来说,跳出代码外的思考能力、数学能力、创新能力、系统思维能力,才是香饽饽。今天已经不太需要抄写员了,正如将来某一天可能不再需要程序员。 细细分析一下,AI能够替代程序员,一个关键的能力就是代码生成。但是从computer science这门学科诞生开始,代码生成从来就不是一个核心领域。 这个世界上所有的学术科目,根据公认,都是从两个源头演化而来:数学和哲学。若没有跟这两个科目扯上关系,那它就不能被称为学术。 比如,每个人都可以在夜晚看星星,但看星星不是学术。后来引入了数学,成为了观星学,再后来成了天文学,乃至天体物理学,才是学术。因为引入了数学。 计算机按照图灵的表述,一开始是一种纯粹的数学模型,有四大要素。其中,指令只是衍生而出的一种应用工具。在冯·诺依曼的架构里,指令更是一种计算机的“粮食”,喂给它它就转,即使不喂,它本身也完备。也就是说,在这个模型上,指令是可被应用并且有效的。这正如人类有了充分发育的智慧大脑才产生语言,但语言并不代表大脑的智慧本身。 后来为了使得计算机这种东西不仅在数学上,更在现实中也有价值,就有了机器码。机器码是人为约定的,目的是为了操作计算机。也就是说,到了“编码”这一步,已经是计算机发展的中下游链路了。 一开始用的是纸带打孔。所以那时候的“程序员”其实就是打孔员。一张纸带代表一个指令,成本非常高昂。 后来发明了高级语言,人类只需要写这种语言,机器就可以把它自动编译成机器码。而且有了电子存储器,也不需要纸带了。到了70年代左右,才有了我们今天熟悉的所谓“编程语言”。 这个时候我们看看,编程语言在计算机科学发展的过程中,扮演了一个什么样的角色呢?它是学术吗?它是数学?还是哲学?把代码写得像证明一样严谨,那么它就是数学咯?或者写得像诗篇一样优美,那么它就是哲学咯? 我们有一门学问叫作编译原理,专门研究编译这件事。编译就是指把高级语言编译成机器码,这是一门数学性很强的科学。但是它和编程语言本身有什么关系?我们见过大量的程序员对编译原理知之甚少,也就是说只要他们不接触机器码和底层开发,编译原理并没有很多人想象中那么重要。 所以写代码本身一开始就和学术扯不上太大关系。发明高级语言,只是为了减轻程序员的工作量。高级语言的编译是一门大学问,但是使用高级语言本身,好像并没有获得太多学术上的关注,倒是很多大企业在用它创造产品。这就又出现一门学科叫作软件工程。 根据Uncle Bob的说法,高级语言编程的三大范式早在上世纪就确定了:函数式、过程式、对象式。所有的软件工程书籍,都在教你怎么用这三种方法写出最稳健、最易于维护的代码。但是它是语言不相关的,几乎任何一个图灵完备的语言都可以采取一定的架构,解决一定的问题。至于能实现多少,那就看这门语言的设计。而语言的设计又跟编译脱不开干系,这就又回到编译原理上。 说到底,代码本身从来不是一个目的。写代码没什么了不起,设计代码才是核心领域。 所以我们发现,指令的使用,乃至编程语言的使用,是计算机发展的大树上伸得很长但离得很远的一枝。它属于典型的应用侧。 既然是应用,显然用得越方便、越快速、越稳定越好,因为用它是为了看成果。所以自然而然的想法就是自动生成代码。 代码生成上迈出的第一步,恰恰是高级语言的发明。今天很多程序员抨击AI写代码是生成的,不好,但是他们忘记了他们使用的五花八门的高级语言,本质上也是一个机器码生成器。 有了高级语言,开发容易多了,这才有了软件的大爆发。 后来又有了五花八门的硬件、五花八门的操作系统。同一套软件、同一套业务,为了适配不同平台,就需要好几个团队来分别为不同平台做开发。比如Windows端用C#,Android端用Java或Kotlin,iOS端用Swift,Linux鬼知道该用什么。一套软件四套团队,怎么办? 于是就有了各种各样的跨平台方案,人类在代码生成上迈出伟大的第二步。比如现在学会JavaScript,几乎什么都能做,各大成熟的跨平台框架任君挑选。本质上就是:用一套高级语言的代码生成多个平台的机器码。这时候另外三套团队就会失业、被裁员。只不过那时候互联网应用野蛮生长,编程也没有今天这般高的热度,没有太多人会去倾听他们的心声。 所以今天的程序员,早就开始用代码生成技术了。如今有了AI,可以直接生成高级语言。形式虽更高级,本质却没有变化。只是过去人们手动编写编译器,将高级语言编译为机器码;如今AI能够深度学习现有的代码,人工可以少干预,仅此而已。这是软件这门生意发展的必然结果。 另一个方面,深度学习的理论框架早就有了,为什么近年才发展起来呢? 因为技术的发展本身需要一定的时间,而且,还有一个重要原因:硬件变强了。 硬件为什么能变强?因为基础研究的进步。基础研究为什么能进步?因为基础学科的进步。什么基础学科?数理化。好了,我们又回到了计算机科学的核心。 综上所述,计算机科学发展的第一动力,绝对不是代码。它被替代,完全合乎情理。所以程序员们,确实没有必要致力于守成。如果真的想提升不可替代性,应该尽快修正自己的错觉,尽量靠近computer science的核心,那就是从“做”到“设计”。 以上80%都是我突发奇想瞎说的。

点菜型经济

中国现在的综合国力,按照世界公认的说法,应该是可以排到第二名了。而且,在一些前沿领域,比如AI、新能源、航空航天、芯片,也可以和美国掰掰手腕。但是相较于国家整体的强盛,普通民众能够感知到的生活,似乎没有发生太大变化。并且,人们口中“经济下行”的语言也非常流行。什么原因呢?我觉得关键的原因,就是中国的经济体制很大程度上是一种“战略型经济”。 为了说明什么是战略型经济,我想拿点菜打比方。假设我们开了一个餐馆,一般的餐馆都会保证十几种最基本的菜品。但是我们的经营规模决定了我们的厨师资源有限,只能雇少数厨师把个别一些菜做好。为了和同行展开竞争,我们当然要把绝大部分的资源押在同行们竞争最激烈的那几道菜上,因为顾客选餐馆实际上看的就是这几道菜。比如川菜馆,顾客看的就是麻婆豆腐、鱼香肉丝,假如你做不好这两道菜,那你的餐馆也就不用开了。所以我们雇的厨师,绝大多数都是擅长热门菜的。虽然能把热门菜做好,但是这也导致那些普通菜做得一般。 这些热门菜,就是我们的“战略”。我们这个餐馆之所以能在餐饮行业立起来,就是靠这个“战略”,把有限的资源押在“战略”上。类比到经济,一个国家的资源(自然和人才)是有限的,但是想要在世界上取得一个领先的地位,就要点这些“热门菜”,把资源倾斜到新兴领域。所以,我又把“战略型经济”称为“点菜型经济”。 遗憾的是,国家的经济和餐馆有一个显著不同,那就是餐馆可以自己选择细分赛道,比如川菜馆、湘菜馆、西餐厅,选哪个赛道,就做哪些热门菜,就这样一直开到倒闭。一般来说,不会有人把西餐厅改成川菜馆。但是对于国家发展,新兴行业可能一两年就换一次。因为国家并不是在一个固定的可预见的赛道上跑,科技、经济的形势很难预料。其次,餐馆普通菜做不好,顾客可以不点。但是国家在普通领域没有资源,背后可是有着千千万万的劳动者。他们赚不到钱,失了业,可是要影响到政治稳定性的。 所以中国采取战略型经济的缺点,一下子就暴露出来。 首先,为了和美国竞争,我们把大量的人力物力,都押在个别几个行业。比如现在竞争最激烈的AI,各个大厂开出高薪,吸引名校毕业生。一时间相关专业的学生感到前途不可限量。但是与此同时,前两年的一些热门专业,降温极快。进学校的时候是人中龙凤,毕业才发现是人中牛马。因为资源是“注入”的,而不是“生长”的。政策一倾斜,资源也跟着倾斜。一个地方重,另一个地方就会轻。 其次,经济增长点非常不稳定。一个健康的经济体,各个领域的经济增长,应该是一个比较平稳的过程。但是中国的战略型经济,导致各个经济领域很容易快速过冷或过热,进一步导致没有办法进行长期的深度研究,留不住人才,很难出现一些颠覆性的成果。这就是为什么中国在应用导向的领域很容易快速赶上,却在基础研究上比较乏力。基础研究是需要长期沉淀的,而应用追求的是快速落地。假如你要在实验室坐十年冷板凳,而且十年后很有可能成果不受重视,这时候若不是因为热爱,还有多少人会坚持下去呢? 再次,它导致人心焦虑。这种经济环境下,想赚钱就要找到风口。所以就看到人跟着资源跑的现象。网上很多博主都在预测风口,比如某某行业、某某专业是下个万金油,等等。国家对于经济的宏观调控作用太强,就会影响民众心理。因为这给人一种政府操控经济的感觉,所以人们自然会想到跟着政府走,而不是自己寻求创新创业的方向。找不到政府的方向怎么办呢?于是张雪峰火了,各类“职业规划”火了。 这一系列连锁反应,导致出现一个非常明显的现象:国家的战略目标往往都能实现,但老百姓兜里还是没有钱。因为能同时满足跟上政策导向、有卓越才智、具备快速落地能力的人,在任何社会中永远是少数。只有这些人才能够成为赢家,绝大部分人生活在迷茫和焦虑之中,享受战略领域的发展成果的残羹剩饭。

音乐哲学的数学原理

声音是由物体的振动产生的。从听觉上来说,振动频率越快,音调就越高;振动频率越慢,音调就越低。我们用$F=a\ \mathrm{Hz}$表示振动频率,其中$a \in (0, +\infty)$。 那么既然$a$可以有无限个取值,是不是代表,我们可以用的音也是无穷?显然不是。对于人类来说,人耳生理上的限制决定我们并不能准确分辨所有的声音。比如,某两个音可能频率不同,但听起来一样;或者某个音频率太高或太低,根本听不见。 首先我们探讨第一个问题,即人耳能听到的频率处于什么范围。根据研究,大部分人能够听到的频率介于$20\ \mathrm{Hz}$到$20000\ \mathrm{Hz}$之间。所以说,如果你想演奏人类能够欣赏的音乐,首先应当使得$a \in [20,20000]$。 现在来探讨第二个问题:频率不同,是不是我们的耳朵就一定能准确感知这种不同? 你可以使用一些设备播放一个特定频率的正弦波。首先将频率调到$440\ \mathrm{Hz}$,聆听一小会。然后调到$441\ \mathrm{Hz}$,你能分辨这两个音之间的不同吗? 如此不断将频率调大,直到你可以明显感知到“音变高了”,观察频率数字,看看和$440\ \mathrm{Hz}$相差多少。对于一些听觉比较敏感的人,可能在调到$441\ \mathrm{Hz}$时马上就能感知到变化。但是对于一些听觉不那么敏感的人,可能要调到$443\ \mathrm{Hz}$乃至更高。这个简单的实验说明人耳对频率的分辨力是有一定限度的。所以,我们可用的频率$a$又缩小了,它的取值变成了一个离散集。 让我们继续这个实验,依然从$440\ \mathrm{Hz}$开始,逐步上调频率。当然,这次你可以调得快一点,甚至直接按住按键,让频率连续快速增长。当一串滑音进入你的耳朵后,敏锐的听觉会捕捉到有一个“回归”的音。这个音听起来跟$440\ \mathrm{Hz}$非常相似,但是明显更高。这时候找到这个音的频率,发现是$880\ \mathrm{Hz}$。 $880\ \mathrm{Hz}$可以有两种含义:一种是$440\ \mathrm{Hz}+440\ \mathrm{Hz}$,另一种是$440\ \mathrm{Hz} \times 2$。这时候我们会产生一个想法:如果我想再找到一个“回归”的音,应该将频率加上$440\ \mathrm{Hz}$还是乘以$2$呢? 依然可以用实验来验证。将频率调到$880\ \mathrm{Hz} + 440\ \mathrm{Hz} = 1320\ \mathrm{Hz}$,和$880\ \mathrm{Hz}$比较,发现两个音好像没什么关系。再将频率调到$880\ \mathrm{Hz} \times 2 = 1760\ \mathrm{Hz}$,马上可以分辨出这个音又是一个“回归”的音。 所以我们发现一个定律:频率$2^ka$在听觉上是回归的,其中$k \in \mathbb{Z}$。 这样一来,我们能用的频率虽然没有发生什么变化,但是由于$\forall a$都有$2^ka$,所以真正可以称得上听起来“不重复”的频率,实际上只有$[2^ka, 2^{k+1}a]$这个区间。不管你选定怎样的$a$,对于任意一个$k$的取值所形成的各个区间,音都是“重复”的,只是音调的高低有所变化。 所以我们的发挥空间一下子就变得非常有限。 这时候你可能会说:没关系,反正人耳可以分辨$2\ \mathrm{Hz}$的频率变化。$[2^ka, 2^{k+1}a]$之中不是还有很多个$2\ \mathrm{Hz}$吗?所以可用的频率还是非常多的。 确实,从数学上看, $$ m = \frac{2^{k+1}a-2^ka}{2\ \mathrm{Hz}} $$ 随着$k$和$a$的增长,$m$会越来越大,也就是说我们可用的频率会越来越多。可惜很不幸的是,频率越高或越低,人耳的分辨力越差。而且即便是$2\ \mathrm{Hz}$的差异,也不足以用来形成音乐。因为音乐的要求是人能够快速分辨不同的音高,这就要求不同音高间差异应当足够明显。而且,如果这个区间内不同的音高太多,会增加大脑处理的负担。我们的大脑天生倾向于处理具有相似性的事物,包括音乐。所以我们采用的音高应该尽可能少,但又足以充分利用这个区间内的可用频率。 我们使用的音,应该在各个区间内数量相等且“回归”。也就是说,第二个区间的第$n$个音,应当等于第一个区间第$n$音的$2$倍,以此类推。因此就不能采用$m$的算法,因为那样会导致不同的区间内音的数量不一样。 那么每个区间内应该有多少个音最合适? 经过世界各国在漫长历史过程中的实践,最终在当下,绝大多数音乐家都认为,每个区间内有$12$个音,最符合人类听觉和美感方面的要求。 也就是说,假设以$a$作为第一个音,那么$2^{\frac{1}{12}}a$就是第二个音,$2^{\frac{2}{12}}a$就是第三个音……这样一来,对于任意$a$,在经过$12$个音后,都会“回归”到$2a$。 比如,假设第一个音是$2^{\frac{2}{12}}a$,经过$12$个音,也就是 $$ 2^{\frac{2}{12}}a \cdot 2^{\frac{1}{12} \cdot 12} = 2^\frac{14}{12}a = 2 \cdot 2^{\frac{2}{12}}a $$ 这称为“十二平均律”。 你可能会产生疑问:为什么要把$2^ka$中的$k$分成$12$份,而不是把$2^{k+1}a - 2^ka$本身分成$12$份,也就是为什么不使用等差形式: $$\frac{2^{k+1}a - 2^ka}{12}$$ 这个问题又是另一个有趣的话题,可能还需要一篇文章来解释。

二叉树

2015年左右,三年级的我第一次见到了代码。那时候见到了一位正在上大学的姐姐的Visual Basic作业,本身就对电脑很感兴趣的我,很快就被吸引了。我在她的电脑上乱敲键盘,打出一大堆乱码,说这就是我的代码。她还夸我很厉害。 2017年,我第一次学编程。我的第一门编程语言是C,第一本编程书是谭浩强。我每天看一点点,写一点点。为什么函数要先声明再实现?这是在编程中困扰我的第一个重大问题。我花了两个月的时间,才理解函数的声明和函数的实现是分离的,后来我才知道这个叫封装。 我用stdio写了一些命令行程序。那时候不知道用代码编辑器,而是用记事本写代码。 很快我就觉得命令行不是我想要的,我想要炫酷的GUI。于是我的第二门语言是VB,那时候我觉得VB就是写GUI最好的语言。但是一开始我完全没有理解面向对象,不知道什么叫实例。所以我又开始啃VB的书。不过这一次比学C快多了,不到半个月我就能用VB写程序了,而且还用上了Visual Studio。 但是我的VB生涯只持续了很短的时间,一个原因是这门语言本身不太流行,框架也很少。于是我的第三门语言是Java,看的书是臭名昭著的《从入门到精通》。这样一本烂书却让我学会了Java大部分的基础知识。现在人们学Java可能是为了做互联网开发,但那时候,我从C学到Java,乃至后面的C#,都没有对互联网开发展现出任何兴趣,更不懂什么SaaS,只是一心想做一个“买断制产品”。所以我对桌面软件非常感兴趣,也可以说是我的技能树点歪了。 我学做Java Swing,用Thread手搓动画,做了一个工具箱软件。这是我的第一个称得上“作品”的项目,总共有三千多行。但后来我换了电脑,一行都没有保存下来。年轻人总觉得自己将来有无限可能,有谁不把过去通通抛在脑后呢? 当我觉得Java也不过瘾的时候,我开始学C#,做Unity 3D。从Java转C#并不需要多少成本,倒是把大部分时间都放在了研究U3D上。我做了一个小游戏。此外还学WinForms,做了一个文本编辑器,还在省级比赛上获了一等奖。 以上都是2017到2018年间发生的事,这两年我完成了我迄今所用几乎所有技术的原始积累。可惜的是,2018年开始直到2025年,7年间,我没有学习任何一门新语言,Android和WPF的学习也无疾而终。 2022年,我做出了我人生中一个极为不妥的决定:转到文科班。在2022年以前,我从没想过有一天我会去学文科,我一直以为自己是个纯正的理科生。但是人在环境极度压抑的情况下会做出一些极端的决定,我只是为了赌气,或者说想逃离那个班级环境,就让我的理想和前途大打折扣。 同年的某一天,一位老师给我一份资料,问我要不要去参加信竞。那时候我只觉得数竞、化竞、生竞才是“正规竞赛”,对信竞并不上心,便敷衍过去。 后来上了大学,才发现大量CS领域的大佬都是信竞出身,有些甚至从小学开始到高中,一直参加信竞。 而我自小学毕业到高考的6年内,再也没有2017年那样深入钻研过编程。 而我在2025一年间学到的编程知识,却比我过去人生中所学的总和还要多。 当我今天只能通过考研来进入这个自幼向往的行业时,不禁回想起十年前独自啃书本写代码的那些时光。 种一棵树最好的时间是十年前,其次是现在。

论Vibe Coding替代程序员

自从有了AI,用AI写代码,替代这个替代那个的论调就越来越多了,给人的感觉是程序员很不值钱——确实啊,要是值钱怎么能叫“码农”呢!我自己编程水平有限,平时也大量用AI写代码,所以我应该是AI的受益者而不是受害者,是AI让我变得更值钱了。但是我也觉得,AI替代一切、低代码开发这类论调,言过其实。与其考虑AI能替代什么,不如考虑作为开发者,最不能被AI替代的东西是什么。 首先我觉得第一个层级的不可替代性,是判断力。众所周知,现有的AI模型是一个黑箱。我们在数学上可以通过严密的逻辑推导,证明一个命题的真假。但是据我了解,AI模型是通过概率推算的方式,得到一种“最可能的结果”。固然这个正确率会很高,但是即便是99.99%,也是和逻辑推导有本质区别的。“像”和“是”是完全不同的两个概念。所以AI如果按照现在的这种模型进行发展,那永远都可能出现错误的结果。这时候只能依赖开发者进行判断。 一些重复性的工作、简单的工作、答案很明确的工作,确实没必要身体力行。直接交给AI就能做,而且说不定还是最佳实践。但是不写不代表不会看。双手解放出来,大脑还得工作。这就像AI写小说,本质上,我脑子里想好一个情节和中心思想,交给AI,它就能帮我完成一部小说。只是文笔和细节上会很差,但是我相信它会越来越好。前提是我自己首先是一个懂文学的人,不然我怎么判断他写的符不符合要求呢?写代码同理,开发者可以不关心AI的实现方式,但是起码要清楚它的意图,能看到全局。这就要求开发者具备高水平的判断力。 但是一些极力推崇Vibe Coding而贬低开发者的人,恰恰没有认识到这一点,所以他们认为程序员没有必要存在,自己不会编程也可以做出好的产品。实际上他们也落入了一个经济学概率的陷阱,那就是他们在做的实际上不是真正的开发,而是一种商业投机或管理,这是完全不同的两个领域。他们所做的实际上只是雇人帮他们写代码做了一个产品,只是员工是按token计费的便宜的AI模型。而程序员却要对代码的整体质量负责,还要关注DevOps等等方面的问题,这些事情没有一定的开发知识是完全无法解决的。 一开始人们打孔;嫌打孔太慢,就写汇编;嫌汇编太慢,就写高级语言……今天有人说打孔的人很蠢、写汇编的人很蠢,但是当时的硬件条件和需求就是那样。会打孔的人肯定会汇编,会汇编的人肯定会高级语言。一言以蔽之,工具的高级性掩盖不了它的工具性。来来回回折腾到今天,发现贯通“古今”的核心能力,还是判断力和系统思维能力,这是真正属于人的主体能力。 将来的AI或许也会出现这种能力,那时候人类的思维想必也会来到更高的一个层次,思考一些我们今天想象不到且将来的AI也不能思考的问题。 这就将我们引入第二个层级的不可替代性,那就是元认知的能力。我们总是在考虑AI替代程序员,可曾设想,我们发明计算机是为了解决问题,将来即便AI真的替代了程序员,我们的问题却不完全需要由计算机来解决?虽然计算机神乎其技,但是推而放诸人类历史,它也只是一个重大事物。将来会不会有超级计算机、超超级计算机,乃至全新的计算形态?那时候现有的AI还能处理这些事务吗?考虑到这些问题,我们说,那么好,AI替代程序员,就让它替代吧,因为我们有更深奥的问题要研究。 程序员——或者更广义的计算机科学研究者——在我不成熟的思考下,至少有这样两个不可替代性,其实也就是进化性。

糖原1.1

之前设计的糖原2.0,可以说想象力天马行空,但是现在看来,难免有过度设计之嫌。这个架构主要是为了保证糖原的核心竞争力——弱信息茧房,于是设计了前端(闭源)—中心服务器(闭源)—节点服务器(开源)的三层架构。本质上,这种做法是想通过将技术神秘化,获取对推荐算法的绝对掌控。但是实际上,这种做法是没有必要的。因为真正好的软件,即便开源也能保证最初的愿景不被篡改。而且对于软件的运营,代码只是其中的一个部分,能够吸引人的还有很多方面。再者,客观上,这个方案也几乎没有办法实现。因为所有的流量都需要经过中心服务器转发,社区越大压力越大,中心一挂社区就挂,显然很不科学。因此,我准备放弃这个方案,转而将糖原全面开源。让我们为糖原1.1鼓掌欢呼!(误) 全面开源的意思就是,所有程序代码全部开源,并且允许自部署。 有人担心,糖原的初心是为了减弱信息茧房,需要一个中心化的控制权。而全面开源以后,人人都可以部署,反而变成去中心化,那怎么能减弱信息茧房呢? 在我看来,信息茧房是一种结构化的社会问题。单纯依靠一两个软件是解决不了的。因为即便有了糖原,大家照样还是会部署Misskey这种平台。因此糖原只能面向自愿加入这个弱茧房社区的用户,如果这种理念受到欢迎,那么自然会受到推广,从而吸引越来越多的人加入。也就是说,真正吸引人的不是程序本身,而是环境、价值与氛围,这就是开放的哲学。 更进一步而言,既然我们的用户群体都是自愿抛弃信息茧房的人,那么他们为什么要自己部署我的开源程序呢?如果是为了圈地自萌,他们显然有更好的专精的开源项目可以用,何必用糖原呢? 因此并不需要担心网络上出现大量的“糖原实例”,变成一个又一个的独立王国。从现在的知名度上来说,这是杞人忧天;即便将来有了知名度,也不太可能出现这种情况,原因刚才已经说过了。相反,大家可能会更倾向于给我的项目提Pull Request,不断优化。 这种现象的原理是,任何软件都有一种无形的资产,即用户群体。对于独立的论坛社区,用户群体是分离的。而对于这种自愿抛弃茧房的社区,用户群体必然是趋向合一,因为要想抛弃茧房就意味着要联合,不联合就没必要用这个软件。这是一个反证法。 当然,退一步讲,糖原也完全可以允许自部署,但前提是各个实例间要保持开放和联合。原理也和刚才说的一样,如果服主不联合,他也不会选糖原。这样就可以造就一个去中心化的、分布式的统一弱茧房社区。 这并不是新鲜概念,很多论坛项目早就有这种机制。糖原的意义在于,站在巨人的肩膀上更进一步,做到更公平、更开放。并且,将来我们可以在各个平台上做出高性能的原生前端。

Codex CLI简单配置第三方兼容API

引言 Codex CLI是OpenAI推出的AI编程Agent工具,类似Claude Code。据说它可以取代Claude Code,成为最好的CLI编程工具;不过在下对此不置可否。CLI相较于GUI插件的一个显著的好处是,大部分代码编辑器都可以用,因为它的运行只依赖终端,而且也比较稳定。所以我个人还是比较喜欢CLI。 最近我有幸用到了大佬的Codex中转站,故在此将我配置Codex CLI的经验作一分享。 本文所用环境是Windows和Windows PowerShell,Linux可以参考适用,大同小异。 安装 安装Codex CLI,运行如下命令: npm i -g @openai/codex 验证安装是否成功,运行: codex --version 若输出版本号,即代表安装成功。 配置文件介绍 如果仅仅是使用OpenAI官方提供的接口来使用模型,那么直接运行: codex 然后按照交互式指引,登录OpenAI账号或填入API Key即可。但是本文介绍的是使用第三方中转站(即OpenAI兼容API)来使用模型,因此需要关注一些配置文件。 Codex CLI的配置文件位于用户家目录(即C:\Users\<用户名>\.codex)中。为了配置第三方API,需要修改这里的config.toml。在初次安装且未进行配置的情况下,config.toml应该是不存在的,因此需要手动创建。创建好后,编辑该文件,写入如下模板内容: model_provider = "custom" model = "gpt-5.3-codex" model_reasoning_effort = "medium" preferred_auth_method = "apikey" [model_providers.custom] name = "custom" base_url = "http://api.custom.ai/v1" wire_api = "responses" env_key = "CUSTOM_OPENAI_API_KEY" requires_openai_auth = true 这个内容等一下还要进行修改。我们要确认已经获得了以下三项必要信息: 第三方API的Base URL 第三方API的Key 第三方API可用的模型名 才能进行这里的配置。因此需要先从第三方站那里获取这些信息,各家不同,此不赘述。 修改配置文件 首先,Codex CLI支持配置多个model_provider。每个定义时用[model_providers.xxx]开头,然后写入各项参数。文件开头的: model_provider = "xxx" 即CLI默认选用的model_provider。接下来我们自定义一个[model_providers.custom]。 修改模板中的base_url为第三方API的Base URL: base_url = "http://api.custom.ai/v1" 修改env_key为一个自定义的环境变量名,比如: env_key = "CUSTOM_OPENAI_API_KEY" 这行配置的意思是,CLI要去这个环境变量里找API Key。所以需要修改环境变量。对于Windows 11,打开系统设置—系统信息—高级系统设置—环境变量,然后添加一个用户变量,名为刚才定义的名字,值为第三方API提供的API Key。 有些人认为配置环境变量很麻烦,所以在此也有第二种方法。 首先,删去env_key字段。当一个model_provider没有配置env_key的时候,CLI就会去读取和config.toml同目录的auth.json,来获取API Key。 新建auth.json文件,写入: { "OPENAI_API_KEY": "apikey-xxxxxx" } 这里"OPENAI_API_KEY"这个变量名是不能改的,否则就会读取不到。只要将后面的值替换成自己的API Key即可。 这样做的缺点是,如果配置了多个model_provider,且都没有设置env_key,那么它们就会读取同一个Key。 最后配置使用的模型。这个配置在CLI里也可以以交互式方式设置(通过/model指令)。修改文件开头的model为第三方API提供的任一可用模型,例如: model = "gpt-5.3-codex" 然后就可以使用了。至于详细的配置文档,可以参考官方文档。 使用 在任意文件夹运行: codex 即可启动Codex CLI,使用的是我们刚才配置的custom。可以根据提示进行个性化配置。 结语 本文所述方法只是最简单的配置,更多用法可以参考官方文档。不当之处,欢迎指正!