欢迎来到未来

如果把一份编程作业直接交给 AI,它就能读题、写代码、运行测试,最后交出一个像模像样的结果,那么这份作业的满分,究竟证明了谁的什么能力?

这是这门课的起点。过去,我们习惯把作品看成能力留下的痕迹:能做出一个程序,大概就掌握了相应的知识。今天,同一份作品背后,可能是深入理解系统的人,也可能是只复制粘贴了一句话的人。作品还在,作品与能力之间的对应关系却变了。

于是,计算机专业的学生有了几种很真实的焦虑:AI 帮我上完了大学,最后我什么都没学会,怎么办?老老实实学了四年,毕业时发现原来训练的技能不值钱了,怎么办?为了刷出一份完美简历,把生活变成一场漫长的资格赛,最后又得到了什么?这些问题很难再靠“把下一门课考好”来回答。我们需要亲自试一试:当实现软件的成本发生剧变,什么能力还值得训练?

一门站在交叉路口的实验课

课堂用四个节点回看计算机改变生活的过程:1975 年的 Altair 8800、1993 年的 Mosaic、2007 年的 iPhone,以及 2022 年的 ChatGPT。把它们放在一起,是为了看见一种相似的变化:原先只有少数人能使用的能力,逐渐进入普通人的日常生活。今天,“把一个想法变成能运行的软件”也在经历这样的变化。

因此,《生成式软件工程》是一门实验性的课程。我们要讨论的是 AI 时代还有用的“软件工程智慧”:怎么让工程不失败? 让一个演示跑起来,只是开始。一个系统还要承受需求变化、真实用户、错误输入,以及人与人之间的协作。AI 可以加速实现,这些问题却不会随着代码自动生成而自动消失。

大学还能教给你什么?课堂给了三个有点扎人的说法:“信息差、能坐牢、能驾驭。”知道计算机世界里有什么、知道哪里可以找到答案,是信息差;做了决定以后有人要承担后果,是“能坐牢”;能够把人、工具和反馈组织起来,最终做成一件事,才是能驾驭。课程最关心的是最后一项。

课程以案例和讨论为主,鼓励同学们动手实践,甚至喊出了“禁止古法编程”。它要打破的是把亲手敲出每一行代码当成学习成果的习惯。你当然仍然需要理解程序,只是实现可以交给 Agent,人的注意力应当更多地留给问题、设计和检验。

“没有 token 的 CS 学生应该立即退学”也是课堂里的一句狠话。第二讲已经说明,当然不会真的让大家退学。它强调的是:面对这种变化,计算机专业的学生应该亲自使用工具,形成判断,而不是永远停留在听别人评价的阶段。课程要求 token 自费,但不鼓励 tokenmaxxing——以消耗更多 token 为目标的攀比。值得投入的是问题与实践。

课程也试图减少分数对学习的支配:提供建议的实验与反馈,希望大家最后能说清楚“我在这门课里做成了什么”,并拿出别人可以打开、运行和使用的作品。一个公开的项目,可以持续修改,也会持续接受现实的检验;它所记录的成长,比一次验收更长。

连这门课本身也是一个实验。讲义首先是文本,文本经过生成和渲染成为幻灯片,课堂讨论被记录,再整理为能够阅读、搜索和重新组合的内容。“Everything is code”在这里很具体:我们希望保留可编辑的源材料和构建过程,让课程有机会不断更新,而不仅仅留下某一天的录像。如果这条路径走通,也可以用它重新组织操作系统课。

从语言模型到能够行动的 Agent

从使用者的角度,可以先把自回归语言模型理解为一个条件概率分布:给定已有上下文,预测下一个 token,即 Pr[token | context]。Token 是模型处理文本的基本单位,不必恰好对应一个汉字或一个单词。

这个接口简单得让人怀疑它能做多少事。但“预测下一个 token”描述的是计算怎样展开,不能直接拿来判定能力上限。模型输出的文字会进入后续上下文:先写下一个中间结果,再基于它继续计算;发现矛盾,再尝试修正。一个看似静态的概率分布,由此可以展开成很长的计算过程。

课堂回顾 GPT-4 时,用了一个很生活化的例子:给五岁的小朋友解释扫描电子显微镜。可以把它想成一支很细的“电子铅笔”,一行一行扫过样品,探测器收集扫描时产生的信号,再把各个位置的信号强弱画成屏幕上的亮暗点。小朋友不需要先学完仪器课程,也可以抓住“扫描、测量、拼成图像”这条因果链。这里令人惊讶的能力,是模型能够面向听众重新组织知识。

再往后,推理型模型让“先尝试、再检查、再修正”变得更有效。Chain-of-thought,也就是思维链,把中间步骤留在上下文中;test-time scaling 则是在使用模型时投入更多计算,换取更深入的尝试。多花计算是否值得,要看这些尝试能不能得到有效反馈。

Agent 又向前走了一步。它可以调用工具:读文件、执行命令、查询网页,观察操作结果,再决定下一步做什么。语言模型与运行它的程序、工具和环境共同形成一个反馈循环。承载这个循环的外围系统,常被称为 harness。一次对话可以告诉你怎样安装系统,一个 Agent 则可能真的开始安装,遇到报错后继续排查,直到拿到可以验收的结果。

因此,理解 Agent 不能只看模型答题有多聪明,还要看它能观察什么、操作什么,以及操作以后能否知道自己做对了没有。模型未来是否仍以逐 token 生成作为主要计算方式,课堂也提出了开放的设想:上下文本身能否成为更灵活的可修改状态?这不是本课要求先解决的问题。我们可以从已有工具出发,先学会使用这个不断演进的系统。

先用好,再理解原理

很多人第一次使用 Agent,仍然把它当成一个更会聊天的搜索框。真正有意思的变化,发生在你开始把日常任务交给它的时候。

课堂上运行的 Debian 系统安装在 USB 设备上。安装和配置由 AI 完成,还在 QEMU 虚拟机里运行验证;终端和编辑器可以用自然语言提出配置要求,不熟悉的桌面操作也可以让它整理成速查文档。过去,这些任务常常意味着翻教程、记配置项、搜索报错,光是准备开始就让人失去耐心。现在,我们可以先得到一个能用的环境,再围绕实际遇到的问题理解原理。

这会改变学习的入口。“我不会,所以不能做”可以改成“先让工具帮我做出一个能观察、能检查的结果”。但先用好仍然包含验收:系统到底能不能启动,快捷键是否符合预期,数据是否保存正确,都需要证据。学习曲线被改写了,判断结果的工作还在。

邮件也是一个例子。一个能够查询历史邮件的 skill,可以把过去的回复变成参考材料:收到新邮件后,判断如何归档,需要回复时拟出符合本人语气的草稿,再通过提醒事项通知我。我也可以从提醒事项向 Agent 发出指令。这里的价值不仅是写出一封通顺的信,还在于让检索、判断、起草和提醒连接起来。

读书和读论文同样如此。Agent 可以整理电子材料,也可以把视频转成讲义;但一份漂亮的摘要,并不能证明阅读已经发生。更深入的用法,是让它帮助追踪论证:结论是什么,证据在哪里,有哪些没有说出的前提?也可以请它提出反例,然后合上材料,自己复述,再回到原文检查。让 AI 替你接触更多材料很容易,让这些材料成为自己的理解,需要另一套工作方式。

这几个例子共同提示了一件事:限制我们的,经常是还没想到“这件事也能委托”。试着观察一天里那些重复、烦琐、又能判断对错的数字任务,它们可能就是最好的起点。

工具可以组合,成功经验可以复用

通用 Agent 已经能完成不少任务,专门的工具和插件还能缩短它探索的路程。以浏览器为例,普通使用者看到网页、按钮和输入框;计算机专业的学生还知道它背后有 DOM、网络请求、事件和状态。浏览器的调试接口可以提供比逐个猜像素更直接的观察与操作方式。

这种“对计算机世界里有什么有概念”的能力非常重要。你不一定要亲手实现每个接口,但要知道任务可能在哪一层被解决。用网页操作处理学校办事大厅里的重复事务,再把成功的操作沉淀成脚本,就是一个例子。第一次让 Agent 探路,之后重复使用可检查、可修改的程序,昂贵的探索便成为了可以复用的资产。

而且这些能力能够相互增强。网页、视频、论文可以作为信息源;检索、分类、总结可以作为处理过程;文档、提醒、文件上传可以作为输出动作。只要接口接得起来,它们就不再是孤立的功能。这也是软件工程很早就有的思想:分解问题,再通过组合获得比单个部件更大的能力。

当然,插件数量并不等于系统质量。一个环节误解了结果,下一个环节可能继续放大错误。把工具连起来以后,仍然要知道数据是什么、动作完成到哪里、失败后怎样恢复。Agent 产品的愿景很吸引人,实际体验是否可靠,还取决于这整条链。

编程能力的边界,比作业大得多

对计算机专业学生来说,把 Agent 只用来做作业,实在有点大材小用。我们可以现场制造工具,解决以前不值得专门开发软件的小问题。

例如考试统分。可以设计这样一条流程:按下回车拍摄试卷,离线模型识别姓名、学号和各题小分;然后按相反顺序复核,让程序报出姓名、学号以及“11 加 12 加 13 加 14 加 15 等于 65”,教师检查后誊写总分;最后将分数汇总到教务系统需要的 Excel 模板。

这个例子的重点不只是识别文字。原始照片、小分、计算式、总分和表格形成了一条可以回查的链;反序复核和读出算式,则把错位、漏项、误识别暴露出来。机器承担重复劳动,人把注意力放在可能影响结果的环节上。需求描述得足够清楚,一个临时需要的工具就有机会被做出来。

课堂还提出了一个思考题:一台严格管控文件流出的机器,如果仍然能够显示画面,信息真的“只进不出”吗?这个问题的有趣之处,是重新认识现有部件。Excel 公式也能表达计算,屏幕也能承载编码后的信息,鼠标也能提供输入。课堂的二维码 Excel 案例正是从这种重新解释出发:让表格把文本转换成二维码,再通过画面呈现。

这里要建立的是一种计算机科学的眼光:不要只按应用程序的名字想象它的用途,要看它实际上提供了什么输入、计算和输出能力。同样,视频可以由脚本、分镜、字幕和时间线描述;物体可以由 CAD 模型和制造指令描述。Everything is code,让 Agent 的工作范围从“写一个程序”扩展到了许多能够由计算过程驱动的产物。

那么,直接让 AI 造航母不就行了?

二维码 Excel 为什么掉进了泥潭

当然不行。“Idea 有了,就差一个程序员”这个老梗,在 AI 时代还没有过时。一个产品的前端、后端、交互、部署和运营需要共同成立,任何关键环节出了问题,整体都可能失败。能够生成每个部件,不代表能够组织好整个系统。

二维码 Excel 就给出了一个很小、却足够完整的失败案例。课堂提出的要求是:只用 Excel 2019 及以前兼容的公式,把一个可能长达数千字符的字符串先做 Base64 编码,再分成二维码帧;刷新显示时循环播放这些帧,扫描得到的数据拼接后,应当还原为完整的 Base64 字符串。同时,需要用接近真实运行环境的办法验证公式确实算对了。

这段需求看起来已经很具体,但里面仍藏着大量选择。字符串用什么编码?一帧装多少内容?输入变化时,公式和工作表是否要重建?“按 F9 刷新”和“一秒一帧”究竟怎样配合?工作簿能打开,是完成;还是每帧都能被真实解码器读取,拼接后能够还原原文,才算完成?

课堂记录中,一次 GLM-5.3 尝试的第一轮思考就持续了 25 分钟,随后撞上输出长度限制,最终花了 6 小时才完成;一次 DeepSeek V4 Pro 尝试则给出了硬编码版本,被指出问题后承认需要重新设计。得到的 Excel 文件约有 2 MB,而且难以维护。这些具体实验暴露了一个共同问题:新增一点需求,就要付出很大的修改代价。

硬编码尤其说明问题。用户需要的是一个对不同输入都成立的计算过程,模型却可能交出一个只让当前输入看起来正确的结果。演示成功和需求实现之间,隔着一个很大的差别。

后来,通过少量提示调整开发方向,GLM-5.3 在 200K 上下文内完成了一个正确且更可维护的版本:公式能够复用,工作表结构不随着文本长度不断膨胀。真正改变局面的,是对计算结构的设计。输入变长,数据量、帧数和计算量当然可以增加;但表达算法的模板,不应当因此被一份份复制。

所以,AI slop 的问题不能只看文件有多大、代码有多丑。更关键的是修改有没有局部性:改一个地方,会不会迫使整个系统重来?如果每次变化都要重新解释全部隐含关系,项目就已经接近《人月神话》里的焦油坑了。越挣扎,越消耗;加入更多生成能力,也可能只是更快地把自己埋进去。

Intent、Spec 与 Impl 之间的缺口

二维码案例引出了本课的一条主线:意图、规格和实现之间存在缺口。

Intent 是你真正想达成的目标。它往往连提出者自己也没有完全想清楚:“帮我做一个好用的工具”“做一个国民级应用”,都包含大量尚未表达的期待。Spec 是把意图具体化以后,能够描述行为与约束的规格。Impl 则是程序实际执行的计算。计算机必须在每一个执行分支上做出确定的动作,不能留一句“到时候看着办”。

从 Intent 到 Spec,需要做产品判断;从 Spec 到 Impl,需要做设计与实现判断。同一个意图允许许多规格,同一个规格又允许许多实现。每一步看似合理,最终仍然可能离用户想要的东西很远。即使程序通过了现有测试,也只能说明它满足了这些检查,不能自动推出规格忠实表达了真实意图。

模型怎样填补这些空白?很大程度上靠猜。总不能每选一种颜色、每命名一个变量都询问用户;但在架构、数据表示和验收标准上猜错,代价就大得多。我们要研究的,是哪些决定可以先做再验证,哪些决定值得先讨论或做一个小实验。

这也解释了为什么“立即遵循指令”有时会把事情做坏。模型迅速进入实现,许多重要选择还没有被看见,就已经固化在代码里。之后每一步都在维护早期决定,终于走进一个很难掉头的方向。好的协作需要让重大假设及时显露,也需要给推翻假设留出空间。

让大工程获得及时的反馈

软件工程讨论的是:怎样在人与工具的共同协作下,把一件大事做成。它包含技术,也包含组织、沟通、责任和动机。LLM 时代,失败的定义没有根本改变:软件没有满足真实意图与约束,就是失败。

学校里的题目通常在几分钟或几个小时内有答案,交卷就结束。真实工程的反馈可能很晚才来:用户开始使用才发现流程不对,需求改变才发现结构无法维护。长程任务难就难在,还没有明确分数的时候,也要持续判断方向。

拆解、验证和迭代因此仍然有用。拆解让一个大问题变成能看清因果的小问题;测试与持续集成让已发现的错误不再轻易回流;小步修改让新的失败尽早出现,便于定位和回退。这些动作共同缩短了“做了一个决定”与“知道决定是否正确”之间的距离。

如果重新安排二维码 Excel 的开发,可以先拿一个短字符串,完成单帧二维码,并让独立解码器真实读回原文。这一步验证最核心的计算。随后扩展到不同输入、Base64 编码和分片,再验证多帧的顺序与拼接,最后处理刷新、兼容性和性能。每一步都留下可执行的检查;每一步都在消除一种具体的不确定性,而不是等整个文件写完才看它能不能用。

学校办事大厅也可以从类似角度思考。先选择学生真正要办成的一件事,弄清楚数据由谁提供、状态由谁改变、卡住以后谁来处理,再实现一条完整流程。撤回、重试和人工接管也是流程的一部分。等这个小系统能够观察、核对和修正,再扩展到更多事务。把所有表单先堆出来,通常不能替代这些设计。

当生产变便宜,什么成为瓶颈

课堂用一个夸张的算式说明新的成本结构:假设生成能力提高一千倍,即使只有百分之一的垃圾,垃圾的绝对量也达到旧时代全部产量的十倍。这是一个假设计算,却提醒我们,错误率很小和维护负担很小,并不是一回事。

生成可以越来越快,逐行阅读却难以同样加速。新的工程工作因此更依赖好的规格、独立检查器、测试、可观测性和清晰接口:让判断也能够跟上生产,让人的注意力花在最值得判断的地方。

“古法编程死了”表达的是一个正在发生的机会:对于边界清楚、能得到有效反馈的任务,手工实现的稀缺性正在下降。软件工程暂时还没有死。一个人有机会调动过去需要一个团队才能拥有的实现能力,去解决以前连开始都不敢想的问题。

至于 AI 将来是否连设计、品味和工程判断都能胜任,课堂没有给人类留下一块永远安全的领地。它提出的是一个值得认真面对的问题:如果这些能力也继续变便宜,我们如何决定什么值得做,以及由谁承担后果?

课堂把这个问题说得更直接:“为什么我要招你,而不是买一个 $200 的 Claude Max?”除了你可以坐牢,你还提供了什么?

先把目光放回这个学期。下一次展示项目时,试着回答:我发现了什么值得解决的问题?我怎样把模糊愿望变成可以检验的目标?失败发生时,我凭什么知道该换一条路?这会比单独展示 AI 替你生成了多少代码,更能说明你在这门课里学到了什么。