欢迎大家来到这个学期的第一次 Hacking Day。我说过,我们每四次课、以两周为单位,会有一次课程不讲考试相关的内容。但因为我正好有课程,所以可以随便跟大家聊一聊,至少是和操作系统课、包括你们做实验有关系的内容。比如今天我会讲 Scaling Law——你们可能听说过这个词,这是一篇非常有名的论文里面的概念。我会讲一讲我对 Scaling Law 的理解,以及我对 Generative AI 的理解。
今天还有一个小彩蛋:我想让 AI 帮我做一个最小的操作系统,而且我什么也没有提前做,现场准备演示一下。今天有没有可能翻车?有可能我就翻在这了。但至少大家可以看到,今天每个人都能用到的 AI 是什么样的,我们应该怎么应对它犯下的错误,或者如果它一遍就做对了,我们又可以做什么。
每两周都会有一次 Hacking Day。下一次应该会请助教来讲 Command Line Interface——就是你们看到我在这里打开一个 Claude Code,这是一个命令行的接口。关于命令行,它有一些神秘的地方:它怎么来的,你们现在的命令行是什么样的,大家可以期待一下。另外,每周四下午四点半到五点半是 Office Hour,我会在计算机楼的 809,有想和我聊天的同学可以过来。
好,我先从大模型开始讲起。你们第一次用上大模型是什么时候?
我印象中,从它刚刚爆火开始,我就试图在上课的时候使用它,发现它还是很厉害的。那个时候我上操作系统课还会讲内核,是一门非常硬核的操作系统课。在 ChatGPT 刚发布两个月的时候,那时还是 GPT-3.5,大家还在各种微调一个叫 BERT 的语言模型,突然间我们就感觉世界好像变了。因为 ChatGPT 是一个面向所有人的聊天工具,你可以跟它聊天文地理,情绪不好可以找它发泄,甚至可以问它编程问题。
比如我们会问它:这样的程序,编译器会对它做出怎样的优化?大语言模型至少要看懂这段 C 代码里面有一个 inline assembly。相对来说,inline assembly 还是有语料的,但语料其实并不多——整个训练集里面关于内联汇编的文档并不多,它可能见过,但就这么几遍就过去了。可它回答得非常好,对编程本身有很好的直觉。
在那个时候我就感到,世界可能真的完全变了。
然后一路看到大语言模型变得越来越厉害的时候,我就持续地在想一个问题:大语言模型为什么可以做到这么厉害?我最后有一个结论。薛定谔有一本小册子,我大概也是像你们这么大的时候看的。那个册子一开篇就抛出了一个问题:我们为什么这么大?听起来比较好笑,对吧?实际上是:为什么原子这么小?这其实是一个非常深刻的问题,和今天的大语言模型有一定关系,而且已经是一百多年前的事情了。
他的逻辑是这样的:如果大家相信量子力学或者经典的原子理论,那么每一个原子携带的信息是非常少的。你们学化学的时候就知道,一个原子外面有多少个电子,两个原子之间可以怎样反应,这是一个相当精确的模型。那么一个足够小的、由极少原子组成的系统,就不应该有像生命这么复杂的行为。所以"大"其实是应对整个宏观世界复杂性的一个必要条件——生物在一个复杂环境里面,"大"是必须的。所以,大语言模型的"大",我就这样解释了。
第二个有意思的问题是:大语言模型为什么选了"语言"?你们有没有想过?
在大语言模型之前,深度神经网络那个爆炸性的成功案例是 AlphaGo。2016 年 DeepMind 开发了世界上第一个击败人类世界冠军的围棋 AI。在这之前则是 AlexNet 做了图像分类任务,大家突然发现训练深度神经网络可以看懂图像了。那为什么直到今天,产生质变的是大语言模型?
我自己的理解是:语言其实是人类已经沉淀了几千年的一个世界模型——一个非常好用的、可以在人和人之间对齐的物理世界模型。
我经常举这样一个例子:我说"我今天来上课的路上看到一个美女"。这是一句非常短的话,但它背后隐含了大量信息——我在过去三十几年里见过无数女生,"美女"对我来说有我自己的理解,每个人又有不同的理解。你们宿舍里的室友可能会就"谁是帅哥谁是美女"争执不下。
所以语言是一个抽象,它把很多信息都丢掉了。让你闭上眼睛在脑海里 diffuse 出一个"美女"来,每个人 diffuse 出来的都不一样,但不一样的同时又有结构上的相似性。因为我们每个人都在类似的环境里长大,有一个共享的物理世界,上的是同一套教材。你们之间可以用语言来对齐,语言就是找到了整个人类世界里面可以传承下去的那个抽象。
所以你可以用大语言模型来一定程度上表达这个世界,这也解释了大语言模型为什么现在工作得这么好,以及有时候为什么工作得不那么好。比方说,你的语言模型没有办法和外星人对齐——如果你想和外星人对齐,可能要用别的方法。
有趣的是,大家知道在 70 年代美国发射了旅行者号卫星,万一有一天外星人捕获了这个卫星,它可以通过里面的 CD 磁带或板上画的图案,大概知道这是智慧生物发出的东西。但今天可能已经不需要了——我们只要有一个多模态的、可以生成内容的模型,把它固化在南极的某个地方,以后外星人挖出来就可以从模型里面复原出这个世界的东西。只需要足够多的参数。
那么语言模型的工作原理是什么呢?大家都知道,语言模型有一个上下文(Context)——你们最讨厌的就是 Context 溢出、不够用了。它可以从一个 Context 不断地向外补全文本。
大语言模型有两个阶段:一个是预训练(Pre-training)阶段,然后是后训练(Post-training)阶段。所以叫 GPT——Generative Pre-trained Model,生成式的预训练模型。它在 Pre-trained 的时候,可以直接用语料文本来训练,相当于先教它怎么学会说话、学会说任何一句在人类世界里看起来正常的话。然后再把它调成一个可以跟你对话的人类助手,用一个特殊的符号:人说什么,机器说什么,人又说什么……把整个对话写在这里,人说完以后大语言模型就可以根据过去的所有文字来定义一个概率分布——下一个字是什么。
这就叫做 Next Token Prediction。那么大的一个神经网络,本质上就是一个概率分布:基于过去所有它看到的文字(当然图片最后也会变成 token),它会给每一个可能的输出分配一个概率——比如"好的"概率是 0.1,"大"的概率是 0.00001,还有各种 Unicode 字符、笑脸、哭脸,每一个都有一个概率。然后它选一个概率最大的、认为最可能的,继续往后生成。
我觉得很惊讶:为什么这样一个东西可以干好世界上所有的事情,包括编程?
这段历史也很有意思。大家知道我以前是参加程序设计竞赛的,某种程度上代表古法编程的巅峰。2022 年 ChatGPT 还没出来的时候,Google 其实发布了一个非常爆炸性的产品——AlphaCode。你们听说过吗?它声称可以写代码,用的是 BERT 那样的 Transformer 类模型。很有意思的是,它大概可以在 Codeforces(一个编程竞赛网站)上面得到 2000 多分的 rating。
那时对我们来说是一件非常不可思议的事情。相当于什么呢?你有一个语言模型,虽然那时候还不是一轮一轮 Chat 的形式,而是你把一道编程题——比如你们大一时 C++ 期末考试题,或数据结构课的上机考试,可能要写一两百行代码——直接把题目一放,然后它就可以一个一个字符地输出。比如 int,空格,main,括号……它每次输出一个字符或一小组字符,而这个字符完全就是前面所有它看到的东西的一个概率分布。它竟然就可以这样顺序地写代码。
它写代码的方式和人类很不一样。你们写代码的时候用的是结构化的程序设计:先想好为了解决这个问题要分成 ABC 三步,第一步把数据读出来建成一个 graph,第二步实现 Floyd-Warshall 最短路径算法,第三步把算法结果加工输出。你会先把 main 写出来,留一些空白,说大概先做 A 再做 B 再做 C。但机器不是这样写的。
当时 AlphaCode 出来的时候我们都很惊讶,后来想通了它为什么能写代码。无非就是你在解决这些问题的时候,会在草稿纸上做推导、写伪代码,想清楚了以后,写代码就变成了一个机器翻译过程——从一个语言翻译成另一个语言,几乎可以不经过大脑。
我自己有很深刻的体会:作为一个程序设计竞赛的职业选手,我在这个领域受的最重要的预训练,就是我可以非常自如地写代码,把想法以一种接近于顺序的形式直接写下来。以前参加比赛的时候,三个人一台计算机——听起来很奇怪,你们在今天 Agentic AI 的时代会想"我应该部署十个一百个 Parallel Agent 来解决问题",但那时候不是的。我们只有一台计算机,意味着可能要和队友抢机时。所以我训练了一个能力:我可以在纸上写代码。我真的可以在赛场上写整整三页 A4 纸的代码,看一遍修掉一些小错,然后 type 进去,程序可以编译而且是对的。
人类完全可以做到这种情况。而如果人类能做到,那个时候机器翻译已经做得很好了——BERT 系列的模型在机器翻译上已经表现出了相当好的水平,Google Translation 什么的我们每天都在用。所以我们就想通了:神经网络,像 Transformer 这样的模型,只要给它足够大的参数——回到刚才说的"大"——只要大到它在短期记忆(Short Term Memory)里能根据问题想出结果、形成"伪代码"但不用写出来。其实你们也可以,你们高考做数学题的时候,老师解题速度很可能不如你们,因为你们年轻的大脑受了过度训练,短期记忆里可以容纳大量的解题步骤。像我在你们这么大的时候,是可以心算 arctan x 的不定积分的,但现在肯定做不到了。
人是可以被训练成这样的。所以如果人能被训练成这样,大语言模型加上大量语料,没有任何道理做不到。从理论上说,这件事情是可以的。虽然那个时候地球人都不知道应该训练什么样的网络才能最终搞定,但从道理上这件事是成立的。
二、Agents
你就接着想:大语言模型是这样工作的——说白了它就是在脑子里,根据过去看到的所有东西,通过一个叫 Attention(注意力)的机制,注意到一些特别重要的、和当前任务相关的词,从而正确地预测下一个词应该是什么。而且大语言模型在输出下一个词的时候,其实后面很多词它已经基本想得七七八八了。做神经网络研究的人经常喜欢研究这种现象:虽然未来还有一些不确定的地方,但很有可能当你这个词已经想清楚的时候,后面已经想得非常清楚了。这和翻译是非常像的。
在这个框架下,我觉得这和人真的很像——它不就是人吗?我就是这样写代码的,我真的就是这样在物理世界里解决每一个问题的。比如今天晚上吃什么,我脑袋里面是这个问题、加上我现在所处的位置、我现在的心情,然后我就会往后思考。这是一个非常符合人类思考过程的架构。
所以后面很自然地,大家就把各种各样的能力往这个模型上加。比如怎样让大语言模型能够"看到"图片呢?实际上你的每一个 token 都是一个向量,比如 [0.35, 0.01, …]。如果想让模型"看到"一张图片,只要把一个 16×16 的小块变成一个向量,下一个 16×16 的再变成一个向量,就把人类的另一种感官系统也放进来了。
所以当时 OpenAI 做出 GPT-4o 的时候,我们觉得这是一个非常有趣的尝试,因为它把声音、图像、各种人类感官都对齐到同一个空间里。大语言模型的"大"是必须的,语言是人类几万年以来形成的世界模型的抽象,如果我们把所有东西都对齐到语言模型学出来的东西上,那也完全是有可能的。这样 AI 就学会了"看",有了眼睛和耳朵。
有了眼睛和耳朵以后,还可以给 AI 一个"草稿纸"。我很喜欢这样一句话,是给研究生的建议:如果你把所有东西都放在脑子里,那你就是一个有限状态机,因为你脑子的容量是有限的,你每次只是在空想。但如果你有了 paper 和 pencil,你就是一个图灵机,你就有了无限的 working memory,在计算模型上面就产生了计算能力的差异——一个有无限内存的计算机和一个有限内存的计算机,它们能算的东西是完全不一样的。
所以这也在劝说大家要把想的东西记下来、多推导。同样,我们也可以让大语言模型学会这一点。哪怕它的上下文不是无限的,但如果能处理的 context 越来越长,我们完全可以让大模型一步一步地解决问题。
打个比方:123 加 456,在早期 ChatGPT 是做不对加法的。但你们上过小学的都知道,这个式子可以展开写:123 + 456 = 100 + 20 + 3 + 400 + 50 + 6。我们上小学时是列竖式算的,因为二维平面更容易理解,但在文本空间里完全也可以做这件事。你的 Attention 注意到 123 加 456,就可以用类似"翻译"的方式写成展开形式,然后再注意到相邻的项可以加起来。看起来走了远路,但降低了训练的难度——不需要用大量数据逼着你心算每一个加法,而是一步一步展开,每一步你的注意力都可以精确地看到该加哪两个数。
这就是你们现在看到的有 thinking 的 model——能够思考的模型。比如看 DeepSeek 这样的模型思考的过程还是很有趣的:它真的很像人,有一些直觉——"我能不能试试这个?"然后试了以后,"因为这个所以这个所以这个……所以我错了,我要重新来。"只要 self-attention 训练得足够好就可以做到。在 GPT-4 时代,你加一句"think step by step",它在复杂任务上的表现就能提升一大截。当然现在不需要了。
还有一些早期非常有名的工作,比如姚顺雨的 ReAct,以及 Toolformer——它教大语言模型使用工具。你这样想:看到 123 + 456 你可以展开来算,但如果看到 123 × 456 你还会展开来算吗?不会了。你有一个本能的反应:去拿一个计算器,把 123 × 456 敲进去,按等于,不管结果是多少,你就把这个数字抄回来,你会相信这个数字总是对的。
所以大语言模型也学会了:只要给它训练数据,它可以自己输出一个特殊的 token,比如 python(123 * 456),输出完以后把结果再拼到后面。就跟我说"我按了一个计算器算出来 123 × 456 等于多少"是一样的。
到今天为止,大语言模型有了眼睛可以看图,有了耳朵可以听声音(当然都是在语言这个世界模型上面做的),有了计算器可以算东西——它就是一个活生生的人。它能够推理,所以它也会犯错,但它能纠正自己的错误,能尝试新的可能性,最终把事情做对。
在大语言模型出现之后,之前那些命令行纠错工具就全部都被取代了。现在我们只要在 Claude Code 里面跟 AI 说"我要做什么",然后它会自己去写命令、自己犯错、自己修复,流程和之前是一样的。
所以到今天,我们拥有的是一个非常全能、智力极高的概率分布——它可以 predict next token,根据过去所有你写的任务和它已经做的事情,预测下一步应该做什么。
那我们怎样把这样的模型的能力发挥到极致呢?你想想,如果你转生到一个奇怪的世界,你的导师变成了你的宠物,你跟它说什么 instruction 它都会 follow,你应该怎样最好地用好你的导师?
你应该给它一个 workspace。因为它是一个活生生的"人",它能尝试、会犯错,但随着不断尝试,它可以推进进度。所以你需要给它一个大的 working memory。在计算机世界里,什么东西可以充当 working memory?一个目录,一个 workspace。你的 workspace 里有 README,有 docs,有源代码,有 tests。软件工程师这么多年来的 best practice,其实正好给 AI 搭了一个完美的舞台。
甚至我觉得 Git 就是给 AI 量身定制的。为什么?Git 管理的是目录的快照,它有一种类似 persistent data structure 的性质:你可以在当前工作区上做任何修改,随便改成什么样,改完以后你也可以提交,但无论如何你永远可以退回到一个过去的快照上,把不要的东西扔掉,甚至把某个你要的东西 cherry-pick 过来,继续在另一个世界线上工作。Git 其实给了这个世界一个平行宇宙。
所以我有时候觉得这有点像游戏"死亡循环"——大语言模型在启动的时候只有参数和一个 system prompt,它是没有记忆的,没有 short-term memory。整个项目里的内容太多,没办法一次性全部塞给它,过去的尝试历史可能更长,还有输出的日志等等。所以就像是大语言模型每一天都从同一个地方醒来,丢掉了所有记忆,然后重新探索。它每次醒来的时候都是空的,看到一个看起来全新的 repo(虽然可能之前已经改过很多次了),然后根据任务再改。
那大模型应该怎样完成我们现实世界的任务呢?很自然的:收到一个任务,比如实现一个操作系统,它就先想"我要先实现 A、B、C、D、E"。先给一个粗的 plan,再把每一步细化一些。然后把 plan 写到文件系统里——plan.md,用 Markdown 格式,第一件事干什么、第二件事干什么、第三件事干什么,干完以后标记一下。这样即使"死亡循环"清零了,下次打开项目时看到"上一次做到这里了,下面应该做这个"。
这在开源社区里也是一样的:如果一个开源团队想做一个项目,会规划一些近期或远期的 milestone,一个一个完成。在编程完成任务的过程中,我们肯定要写代码、写测试用例、用代码检查器(linter)检查低级错误、用单元测试看实现是否符合预期。如果出错了,甚至还会加一个 printf 来调试。所有这些事情在理论上都和使用计算器等价——都是可以做到的。
所以你就非常容易理解为什么小龙虾(Agentic AI)的爆火简直是必然的。当我们说用一个目录或工作区来管理 memory 的时候,下一句话就是:这可不是程序员专属的工作方式。
任何一个人,比如你们作为学生学大学数学的时候,也可以用这个方式来组织学习进度——把每个 lecture 的知识点放进去,记录习题完成情况,做错了放到错题本里。然后打开一个 Claude Code,问它"我下一步应该做什么",或者"帮我复习一下我之前薄弱的地方",或者"期末考试了能不能给我一套模拟题"。
这虽然是程序员习以为常的工作方式,但没有道理你不用它来管理日常的任何事情。实际上我早就做了——我把所有个人文档都放到一个 repo 里用这种方式组织,然后打开一个 Claude Code 来问它"帮我填个表"。你在上面接一个 Telegram 前端(Telegram 官方支持 bot 接口),就可以在手机上跟它对话:比如"发一个数学题给我室友",它就会从库里调出你学数学的经历,把题目发给你室友。你也完全可以在机器上定一个 cron job——UNIX 里有个叫 crontab 的东西,它是一张定时任务表。比如每天早上 7 点自动爬取当天的天气预报,放到 memory 里。
所以你可以看到,今天你们看到的火爆的东西在历史上都存在一定的必然性,不需要特别惊讶。
三、Scaling Law
关于大模型,我还有一点自己的理解,算是给大家灌点鸡汤。
大家应该听说过 The Bitter Lesson(苦涩的教训),是 Richard Sutton 写的——就是因为强化学习得了图灵奖的那个 Richard Sutton。我强烈建议大家全文阅读。他说:从苦涩教训中学到的是通用方法的巨大力量。我怎么理解呢?我用一个词概括:量变引起质变。我今天再讲好几个故事。
第一个故事:在 1980 年代,人们就发现了一个现象——世界互联网上所有内容的增长速度是慢于存储系统容量增长速度的。也就是说存储工业在飞速发展,磁盘越来越大、越来越便宜,而磁盘技术进步的速度远远快于人类产生互联网数据的速度。
这意味着什么呢?直接推论就是:在不久的将来,我们就可以有一台计算机(或一组计算机、一个小的分布式系统)存储下世界上所有的网页。再下一步推论就是:因为我们有一些好的算法,比如后缀排序可以在线性时间里实现检索——你有一个非常长的文本,给一个待匹配的文本,存在一个算法可以直接查找。所以 Google 是必然的——搜索引擎就是这种量变引起质变。你看起来只是存储技术一点一点在发展,实际上一旦到达某个临界点,就会有超乎想象的效果。在 AI 来之前,互联网检索几乎是你们唯一的信息入口。
再讲一个我经历过的例子。你们知道象棋(包括编程竞赛)里面有一个评分系统:不同分值对应不同段位,比如大师级、国际大师级。你刚进入排名系统会得到一个较低的分值,不断赢过别人就会上涨,输了会掉分,击败比你段位高很多的人就会涨很多分。
背后有一个数学模型:它把一个人在象棋或编程领域的能力建模成一个正态分布,有两个参数 μ 和 σ——μ 代表战斗力,σ 代表稳定性。两个人比赛,就是各自从分布里取一个值出来比大小。整个 rating system 就是为每个人确定 μ 和 σ,经过足够多的比赛后,所有人就基本上收敛到正确的段位。象棋、围棋,甚至你们打王者荣耀的系统也是用同样的方式计算的。
在 1980 年代,包括发明了 UNIX 操作系统、得了图灵奖的 Ken Thompson,都参与到了用计算机下象棋的竞赛当中。他们发现了一个非常有趣的规律:那时候象棋主要靠暴力搜索加上 alpha-beta 剪枝,每当搜索深度增加一层,积分就稳定提升。也就是说他们看到了一点:所谓人类的智能不过是算力。
从一开始打不过人类棋手,到慢慢能和职业选手过招,他们发现如果按照这个规律下去,有一天地球上应该没有人类能打得过计算机。那这时候他们选择干什么呢?为了尽可能快地让机器打败最强的人类,最有收益的事情是——造加速器。因为搜索每多一层都是指数级的时间,为了快速增加搜索深度,最好的办法就是造专用硬件。
许峰雄他们讲怎么击败卡斯帕罗夫的时候,提到他们做的是一个下棋的专用电路。在 8×8 的棋盘上判断一步棋是否合法,需要做大量运算,在顺序的 CPU 上很慢、有很多分支跳转,但用电路实现几乎只要几个周期就可以判断出来。他们最后用了超级计算机加加速器。是不是和今天大模型时代如出一辙?
1997 年他们打败了人类,那是报纸头版报道的事情。那时候我可能还在上小学,那是我第一次正式听说"人工智能"这个名字。
再到你们经历过的事件——AlphaGo。它其实借用了当年在 Deep Blue 上大家已经发现的规律:人类的智能不过是算力。只是在 2016 年,大家发现可以用阅读图片那样的卷积神经网络去理解棋局,搜索算法也从 alpha-beta 剪枝变成了蒙特卡罗树搜索(Monte Carlo Tree Search)。我 2016 年在美国熬夜看直播,虽然高端棋局也看不明白,但当时我想到的就是那个故事——人类所谓的智能,不过是算力。
然后是今天两篇最著名的、开启了大语言模型时代的论文:一篇是 Kaplan 的 Scaling Law,一篇是 Google 的 Chinchilla paper。图上每一条曲线代表固定一个算力,从 6×10¹⁸ 到 10¹⁹、3×10¹⁹……随着算力增加,你需要越来越多的数据去喂饱越来越大的模型。纵轴是 training loss,可以理解为模型预测 next token 的能力。你会看到一条很明显的曲线往下走——随着算力增加,模型不断变好。
甚至 OpenAI 当时愿意训练一个 1750 亿参数的 GPT-3 时,没有人知道能不能成功。他们只是做了一些小规模实验,相信实验可以外推——数据还没有用完,模型还没有喂饱,所以应该可以训练一个更大的模型。然后甚至在不确定每个神经网络激活函数用哪个更好的时候,就这么试着试着,人类就造出了智能的机器。
我觉得这是一件很有意思的事情。一方面说人类运气不错,另一方面这也是历史的必然——象棋和围棋的例子都告诉我们:所谓的智能,都是算力。你只要相信这一点,就觉得这件事情实际上是情理之中的。
当然,研究者们做了很多好的工作。比如他们发现在上下文长度比较长的时候,Transformer 架构比上一代的 LSTM 模型效果好很多,所以 Transformer 活下来了。但没有人知道是否还存在一个比现在好得多的模型,用更少的参数就能得到更强的智能。很有可能存在,但我们不知道。这就和生物进化很像——人类也许是 highly suboptimal 的,因为我们带了从藻类到海洋生物到各种中间物种的基因。如果有机会从零开始设计人类,也许能得到一个高效得多的版本。模型也许也是如此。未来也许可以把设计更强模型这件事也交给机器——机器开一个工作区,尝试各种网络设计,直到设计出更好的大语言模型。
讲这么多故事,其实是告诉大家:古法编程的时代已经过去了。我上大学的时候,大家特别觉得算法很厉害。那时候 PC 普及、智能手机开始普及,我们需要很多软件工程师把物理世界中的需求建模成算法问题。那是古法手工编程,而且是一个非常值钱的技能——你可以把地图查询翻译成最短路径,可以写数值算法来分离照片前景和背景,给社会带来价值。
但今天这个世界变了。我仿照 Jim Gray 在 2006 年说的那句话——"Tape is dead, disk is tape, flash is disk, RAM locality is king"——他讲的是存储系统的变迁。今天我觉得这个时代变成了:Heuristics is dead. 你们学的很多启发式算法,那种把一个不怎么好的方案变成稍微好一点但仍然不怎么好的方案的范式,在今天已经死掉了。因为智能变得很便宜,AI 可以在一个设计空间里面探索——你只需要把问题告诉它。
回到 Richard Sutton 的 The Bitter Lesson:我们只要能做出一个能够承载复杂性的东西,一个可以扩展的机制。这个故事其实有一点操作系统的味道——当你做出了一个机制,这个机制可以无限扩展的时候,你能吸引别人往你的底座上面插东西,甚至可以吸引 AI 往里面插东西,随着插进去的东西越来越多,你就可以解决越来越多的问题。
所以我的鸡汤就是:量变引起质变——但你必须找到那个量变能引起质变的方向。不是说在任何地方做微小的东西都有意义。如果我们已经是高铁时代了,你把马车再造快一点,对高铁没有任何帮助。你要找到那种能走量的路。Agentic AI、Agent、Skills,这些都是能承载一个世界的平台。以往没有 scalability 的高级智能,现在可以做了。
所以你们在今天这个时代可以大胆地去想、做一些以前觉得很疯狂的事情。比如你们可以把所有的学位论文都扒下来,让 AI 去读——里面有明显逻辑错误的论文就非常多了,整个结论就不对、都不需要验证实验的那种,它们先死了。剩下的一部分,你还可以让 AI 尝试去复现实验结果。很多实验是一点也复现不出来的,然后它也死了。我觉得我们的时代在面临巨变。
四、豆包帮我逃课操作系统实验
接下来进入今天要讲的技术部分:我们怎样用好大语言模型的能力。
刚才已经科普过大模型怎么工作了。为了吐出下一个 token,它里面有一个非常重要的机制叫 self-attention(注意力),这和人也很像。你们看题解的时候,尤其是那些难的数学题,会发现题解里面有一个惊人的"注意力"——注意到某个关键点,这个题就解出来了。它说的都对,但你就是注意不到。这说明注意力是可以训练的,是从问题的结构里面产生的。对于太难的注意力,也需要去推理,像 reasoning model 一样去尝试。
因为注意力机制的存在,提示词工程(prompt engineering)本质上就是 attention alignment。你希望让大模型更好地看到你想让它看到的东西,引导它做出正确的事情。
比如你们想让 AI 写一个小说,它可能写得不尽如人意,是因为你只说了"写一个小说",它所有的 attention 都在这几个字上,就会以一种非常中庸平庸的方式来写。但如果你要让它写入党申请书,它所有的 attention 都在"入党申请书"这几个字上,作为一个看过世界上所有书的模型,它会非常乐意地写一个平均水平很好的入党申请书——但可能不是你要的。
这时候你需要在上下文里注入你个性化的信息:你是一个什么样的人,你希望这个东西怎样,给它提供足够的信息,使得它的 self-attention 可以注意到你给的那些信息。另外,适当的时候我们还是需要帮它分解问题。虽然今天的 AI 比如 Claude 的 Plan Mode 会做规划,但分解问题依然很重要。
我觉得分解问题的能力是今天 AI 时代你们最需要的能力。 你可以把一个 crazy 的想法——如果直接让 AI 做可能给你一个平庸的答案——以你独特的视角去分解,就可能直接得到一个产品级的东西。而所谓的分解,其实就是构建一个合适的抽象。
这其实是操作系统课的内容。在操作系统或计算机系统领域有很多经典的抽象,其中两个:
一个是 Syscall。Syscall 上面有一个巨大无比的应用生态——原神、Steam 都在操作系统上运行。Steam OS 是一个 Linux,Linux 的 System Call 就是我们上课时讲的这些,它支持了你每天打开 Steam Deck 上的所有应用程序。它底下的操作系统实现是千万行级的代码,但 Syscall 这层相对来说非常小。这是一个非常经典的抽象。
另一个是指令集。指令集底下是硬件,上面是整个 OS 和 Application——不仅仅是操作系统,是整个计算机世界。这个抽象也是非常经典的。
有了这样的抽象层,你的分解就有意义了。如果让大语言模型直接去实现一个操作系统,它可能实现实现就垮掉了。但如果你非常仔细地设计一个抽象层——脑袋里有一个粗犷的想法,比如"以后操作系统的 Syscall 不应该长这样,应该是另一套"——带着这个让 AI 去做,就可能设计实现出下一代爆火的操作系统。
你们在一个应用程序里面也可以实现这样的复杂性分解。比如你想做一个校园服务(送外卖、送洗衣服之类),首先要实现 register_user 函数。当你实现了一个函数,就有好几个地方可能调用它,这是 function call。但这也带来维护代码的负担——比如之前性别只支持了男和女,后来被投诉说世界是多元性别的,改了以后可能会 break 其他地方的调用。
这不是说 register_user 不应该被实现成函数,而是在设计系统的时候,你不可避免地会有子系统之间的关联关系。所谓的系统设计有意思的地方就在于:如果你让 AI 直接做,它会给你一个平庸的平均方案,可能不是最合适的。
比如我上课用的这个系统,我就做了一个比较反常规的设计。如果让 AI 实现一个上课系统,它大概率会实现成一个前端网页,各种数据结构在内存里,点击事件通过函数调用传递。但我做了一个不太常规的设计:我把子系统之间的接口变成了文件系统。这也跟我是教操作系统有关——为了展示 UNIX philosophy。
我的前端显示器在播放的时候,所有播放过的页面都写到文件里。我没有用函数调用,而是把内容放到文件系统中,这样前端和后端就彻底解耦了。我这个播放器在切换到一个 slide 的时候就是写一个文件,完全不用管是谁来用这个文件。中间的接口变成了目录加日志——我为日志格式做了一个约定,这就是我和其他 subsystem 之间的 protocol,只要遵循这个格式就可以正确地显示幻灯片。
所以我的幻灯片播放器实际上只有几行代码,完全是 300 行的 AI 生成代码,但我完全不用管,因为所有的复杂性都被隔离了——这个程序是独立运行的,不会污染到其他系统的任何部分,它干的事情只是解析我的日志并在终端里打印出来。
这样我把几个 subsystem 的协议分解开,可以让 AI 分别实现,甚至让多个 AI agent 并行实现。但如果是一个 monolithic 的架构,所有东西都通过函数调用相互耦合,要启动并行 agent 就有风险——两个 agent 同时改同一个文件时要处理冲突。如果项目架构做得好,就有机会更好地维护项目。
我觉得这可能是现在 AI 还做得不够好的地方。但我也觉得算力就是智能,所以 AI 没有道理做不了这件事。当 AI 真正能做好系统设计的时候,我觉得我们的末日就真正来了。但至少现在 AI 做这件事还不太擅长。
所以今天我准备做好翻车的准备:我要让 AI 写一个 OS kernel,全部交给 AI,测试一下国产模型的能力。
我的做法是先用 GPT 5.4 给我生成了一个小的任务分解——因为我暂时觉得国产模型可能还需要一些时间才能做好分解。GPT 5.4 给了我一个看起来还不错的分解。我现在假装完全不知道任何 RISC-V 和操作系统的概念,和 GPT 5.4 聊天时完全没用任何自己的操作系统知识,都是它写的。它说要启动内核、初始化一个 minimal 的 runtime environment、创建多个内核线程、做线程切换、展示调度,还煞有介事地写了一些 working style 和 think step by step 的 prompt,要求每件事都要解释、解释怎么测试。
然后它开始分解问题——它还挺聪明的,虽然我只说了"实现一个操作系统"没有说任何细节,但它已经开始列 "what to avoid"、"structure your help around"、"short plan"、"next code change"、"a minimal test or demo"、"preferred mindset"。
接下来我把它交给豆包系的 2.0 Code 模型。它思考了一下,self-attention 还是在工作的——"让我先创建必要的文件结构"。它得到了一个 plan list,开始写文件:Makefile、链接脚本、目录结构等等。
它就跟人一样,如果是我来写一个操作系统我也会这样做——这就是 AI 可怕的地方。我作为一个有操作系统知识的人,也做不了更好。它写了一个很长的 kernel.h、thread.h,知道编程里面什么是 best practice:先写注释,代码组织得和我们计算机系统基础的框架代码做的是一样的事情。
它想写一个有两个线程的操作系统:thread1 和 thread2,实现线程管理和上下文切换,还写了一段汇编。在人类世界里,只要一个东西是 well-known 的、别人做过的,大模型完全可以用它记忆里对操作系统的理解去写代码。或者你只要有一个技术文档说"我要用文件系统做这个结构",self-attention 就能看到你的意图,然后把事情做对。
然后它开始编译——就跟我们一样,改改改,make,出错了:"cast a pointer from an integer of different size"。它思考以后觉得需要修复。以前如果我自己遇到这样的问题,能干的唯一事情就是查手册,充当一个人肉索引者。
它修好了,成功了!它说:"我已经构建了一个最小 RISC-V 操作系统,在 QEMU 中运行并成功切换两个线程。"豆包还是可以的。我检查了一下它打印的十六进制输出,应该是没有问题的,出乎意料。
那你们觉得这个过程哪里不太好?或者说,人类的智慧和 AI 的智慧相比,哪里不一样?
我的感受是:AI 做的基本上是一个线性的"do it"——我说一句话,它所有的 attention 都集中在过去做的事情上,比如做一个 make、看到 error message、试图修复。它"do it"的成分很重,有一种不由自主的倾向——你说什么就做什么。
如果是我作为一个老师来给你们构建操作系统的话,我会先构建基础设施、先做抽象,不会像它一样先把所有代码写完、出了问题再一个一个修。可能是因为大模型对自己写代码的能力很自信,我没有这个自信。我至少会做这样一件事:implement 一个 skeleton for running,希望它打印一些 trace 或 log,为操作系统做很多基础设施。
你还会看到一个问题:刚才我把它中断了,原因是内核里已经输出了很多内容,如果不中断,它会等到一个相当长的 default timeout。等 timeout 以后,里面已经输出了太多日志,这些日志都会被贴到上下文里做大模型推理——这是一种浪费。当然大模型过一会儿也会说要加一个 timeout,但那一长串日志都会留在上下文里。虽然 compact 可以压缩回来,但如果做一个合适的 abstraction,哪怕是最小的只有一两行的 tester framework,都会起到很好的效果——它每次 build 完都可以直接运行测试并收到反馈。
我们在操作系统课上会给大家展示很多正向的例子。你们下个星期开始做 lab——现在退课还来得及,两周之内还可以退课。如果不退课的话,我们的 lab 里就有一个 unit test framework,大部分是 AI coding,少部分是我们手写的。有了那个 test framework 以后,测试就方便了。我最终希望有一天把整个课程全部 open source,再也不需要我们来维护。
那么你需要显式地告诉 AI:"你需要一个这样的工具。"不仅仅是 unit test,甚至还有验证的工具——像我们在计算机系统实验里引入的 differential testing。这些都是重要的人类智慧,都是合适的 abstraction,也是我觉得你们在这门课程里可以学到的东西。