此地有兽:当智能体干活时,你还需要知道什么?

CI 测试失败了。这很出乎意料。我已经把我的代理工作流调整到了一个程度,以至于如果某个代理提交了一个带有失败测试的分支,我都会感到惊讶。
也许是 CI 的不稳定或环境问题?等等,是 SonarQube 在抱怨 App.tsx 吗?我们是不是从 npm 引入了什么可疑的新依赖?不是。SonarQube 报告的是这个文件的圈复杂度过高。
这种问题已经有一阵子没见过了。
没错。App.tsx 大概只有 30 行代码,但其中有 15 行实际上是整个组件,而 Codex 决定把每个组件都写在一行上。这本身倒也无妨,用 Prettier 格式化一下就搞定了。
但这并不能解释圈复杂度的问题。这些组件每一个都相当复杂,包含不少展示逻辑和业务逻辑。以前的我会仔细重构,确保代码可读。
然而,Codex 并不在意组件被压缩成单行,更不在意代码的复杂性。它一直愉快且稳定地在这个文件上迭代了好几项功能。
CI 工具在提示一个问题,可能是对的,也可能不对。但毕竟这是个真实的系统,所以我不得不深入调查。格式化代码后,我逐个检查了那些组件和逻辑。
Codex 是否在边缘情况的覆盖上超出了普通开发者的水平,甚至有点过度设计了?是的,但所有代码都是正确的,我没有任何异议。所有的复杂性都是业务需求所决定的。
于是我让 Codex 满足 SonarQube 的要求,它通过重构并把代码拆分到 10 个文件中做到了这一点。CI 顺利通过,项目如期上线。虽然这次事件很快得到了解决,但你也能想象,事情本可能变得更糟——毕竟那是一段我从未见过的代码。
我们的行业正朝着这个方向发展:拥有一个软件系统,不再那么依赖于亲手编写它(或类似的系统);相反,最重要的是具备足够的知识,能在出现问题时及时发现,并在必要时愿意且有能力深入排查。
潜水机器人与金块
想象一下,你坐在一艘船上(你可以选择自己喜欢的船型),你的任务是从海底收集小小的金块。直到最近,如果你想拿到一袋金子,就得跳进水里,游到海底,四处搜寻,然后把金块装进袋子里。
在清澈、浅水的海域这很简单,但你也知道,一旦水域变深、环境更险恶,事情就会变得棘手得多。于是,随着浅海矿藏逐渐枯竭,你和同事们开始研发各种机械设备,以便从更深、更危险的海域开采黄金。
大部分时候,这套方案运行良好:你和团队会精心组装、维护、轮换设备,并定期亲自下潜到海底作业现场,确保采矿持续进行。偶尔也会听到某些采矿作业因水压过大、意外事故或操作失误而崩溃的消息。
问题是,地球的地壳活动十分活跃,海底地形不断变化,有的矿区逐渐耗尽,新的矿脉又会在别处冒出来。
不过好消息是,过去几年里,越来越先进的潜水机器人问世了,它们可以自行下潜并建立一套完整的采矿系统。你只需告诉它们该在哪里建造,它们就会自动出发,只在需要汇报进展或送上金块时才浮出水面。
一切都很顺利,你现在可以安心待在船上,让潜水机器人忙活,自己唯一要操心的就是能装多少金子上船。这就是乐观派的观点。
直到……
“等你的潜水机器人潜得太深,迷路了怎么办?”“哈哈,你的潜水机器人给你带回来的‘金块’,其实只是发光的章鱼粪便。”随着潜水机器人的数量激增,越来越多的骇人故事浮出水面:潜水机器人迷失方向,忘记了自己该建什么;造出的东西过于复杂怪异,连自己都难以维护,最终被下一代机器人弃之不用。
曾经那些精心设计、精工细作的海底采矿设施,如今更像是临时拼凑的豪宅,到处藏着密道,暗处潜伏着未知的危机。你离现场越远,失败就越离奇。
这就是怀疑派的看法。
怪兽出没之地
软件工程行业(我猜想其他许多行业也很快会面临同样的局面)正在经历上述情景的翻版。你大概已经知道自己更偏向乐观派还是怀疑派,尽管还不确定到底想站哪一边。
传统观点认为,要开发和维护一个非 trivial 的系统,就必须对其有充分的理解。我想很少有人会反对这一点。而在软件工程领域,这种理解通常来自直接接触代码:多年乃至数十年的编码与阅读经验,让你掌握必要的知识与工具,从而真正理解自己负责的系统。
每一次生产事故、每一个深夜调试、每一次成功发布,都在塑造你对系统的全局观。在这篇文章的语境下,“所有权”意味着当系统出问题时,第一个被叫来的人就是你。
而代理(潜水机器人)正在彻底颠覆这一切。潜水机器人产出的速度远远超过人类的消化能力;在过去复杂的系统中,我们还能假设每一段代码都曾经过人工之手,但现在这已不再成立。
如今,经验丰富的从业者可以管理成群的潜水机器人,以前需要团队协作才能完成的工作,现在他们却能以空前的规模一手包办。这些“操作员”技艺娴熟,凭借丰富的经验管理和架构大型系统,其效率远非昔日团队所能及。
但这同时也暴露了我们行业面临的最大挑战:我们该如何培养下一代工程师?
在极端情况下,乐观派和怀疑派对此问题的回答截然不同。一方面,一些权威人士认为,要掌控这些系统,就必须读懂潜水机器人生成的每一行代码,能够亲手写出这些代码,清楚每一段代码的作用及其在应用中的位置。
另一方面,同样权威的声音则建议根本不必去看代码,把精力集中在“做什么”上,至于“怎么做”,交给模型去处理就行了。我认为这两种观点都不够合理。
事实是,如今资深工程师们赖以成长的方式——亲手编码、无数小时的调试、应对生产事故等等——对于今天的初级工程师来说已不可行。更确切地说,这种方式在经济上不可行:企业几乎不可能长期资助新人,让他们花上几年时间“学着亲自动手”。
这不过是美好的愿望。另一种常被提出的替代方案是“确保理解代理写的每一行代码”、“与资深工程师结对编程”或“严格进行代码审查”。
同样,除了少数几家公司外,这也不过是空想。如果你认为整个行业会采纳这些做法,那我倒真有一座桥要卖给你,请随时联系我。
为了找到一条切实可行的成长路径,我们必须先弄清楚“拥有一个非 trivial 的软件系统”究竟意味着什么。想想典型的架构师、技术负责人或 CTO 是如何工作的:要想成功,他们必须在脑海中构建起系统的整体模型,也就是一张“海图”。
真正的关键在于抽象的粒度应该有多细?设想一个历史悠久、长达百万行的代码库,多年来由许多人编写和扩展,其中甚至可能包括 CTO 自己的手笔。
现在假如生产环境出了问题,而你这个倒霉的 SRE 却要在凌晨两点面对来自不同时区的愤怒短信,试图查明原因。这时你需要的是一张怎样的系统地图?
你需要一张高屋建瓴、涵盖全局的宏观蓝图,同时也要掌握足够细节,知道该往哪里深入、怪物藏在何处;更要能在任何陌生的代码库中迅速定位问题、打上热修复补丁,并在事故报告中清晰描述发生的一切。
请注意,这并不是对整个系统的详尽了解。我们管理复杂系统的关键在于层层抽象。每一层都能让我们进一步构建,但无论你掌握的是哪种地图,都要牢记:自始至终都是抽象在支撑,没有人能真正理解所有层次。
所谓“我亲手写过模块 X,对其了如指掌”,与“我对系统的抽象模型有恰当把握,能在任何模块出问题时迅速介入并修复”,两者之间有着天壤之别。
亲手编码只能带来前者,而作为系统的所有者、那个被呼叫的人,则需要后者。
那么,这张“海图”究竟是如何绘制出来的?如果一个人从未亲手采集过金块,又怎能指望他深入未知的海域,修复潜水机器人留下的烂摊子呢?
基础、广度与即时深度
复杂系统中最隐蔽的故障往往是那些看似无害的异常:系统大部分时间运转正常,但总有那么点不对劲。也许突然变慢了,也许错误率上升了,或者绝大多数时候都好好的,偏偏每第 19 次就会失败。
不至于让你在凌晨两点被紧急叫醒,却足以让你隐隐觉得有些不对劲。作为系统的所有者,你必须理解系统的各种失效模式。而作为一个行业,我们已经有工具教会人们如何识别并分析这些故障——那就是基础知识。
好消息是,我们已经把这些内容教给毫无察觉的计算机科学专业学生几十年了。我指的是数据结构与算法、网络、并发、分布式系统等等。
而且这并不一定非要通过正规教育获得,很多内容都可以自学。
为了便于理解(资深工程师们请多包涵),假设你派了一台潜水机器人执行任务。你在浅水区,让它潜到水下 2 米去捡几块金子。它 4 秒后回来了,一切正常。
接下来,你让它潜到 4 米。这次却花了 16 秒。这就有点奇怪了。再下一次,它潜到 16 米,结果用了 256 秒。你明白了吧,显然有问题。
在计算机科学中,这被称为二次复杂度,而识别它的最佳方式就是理解相关理论,知道这种现象的存在。
这就引出了所有权的第二个支柱:广度。我有幸共事过的许多优秀工程师和技术领导者,几乎都能就任何话题展开讨论,无论是技术还是非技术领域。
他们或许在某些方面是专家,但最令人钦佩的是他们能把看似毫不相关的不同学科概念融会贯通,并用这些知识解决问题。这就是那张高瞻远瞩的“海图”,它告诉你哪些海域比其他地方更危险。
正是这种广博的知识让你能果断地说:“我们就往这里潜下去吧。”当然,我并不是说要在每个领域都达到专家级水平,而是只要具备足够的常识,能在发现问题时知道该查些什么——就像指引你探索海洋的浮标一样。
有人问我:“那我们要怎么培养广度呢?开一门‘万事通’的通识课吗?”当时我也没个好答案。但后来仔细想想,这似乎并不是一门课程就能教会的。
没有所谓的“万物入门课”。我们能做的,就是鼓励大家多读书、多探索、保持好奇心,尽可能多地吸收信息。记住那些奇怪的事故、那些让人惊呼“糟糕”的瞬间,思考它们与自己工作的关联。
随着经验的积累,要有意识地把日常经历与实际工作结合起来。我已经记不清有多少次,多年前在某个随机项目上学到的一点知识,在关键时刻救了场。
这种经验和广度会不断叠加,但也需要持续维护。
所有权的最后一个支柱,我称之为“即时深度”。这是一种能力和意愿:面对任何问题,都有勇气深入探究并解决它,哪怕身处未知的海域,哪怕得穿上防护服、与鲨鱼共游一阵,哪怕得召集团队、乘坐大型潜艇,穿过巨型乌贼的领地,直捣系统故障的核心。
这是为了完成任务不惜学习任何所需知识的魄力,也是承认自己能力有限、适时寻求帮助的谦逊。并非每次潜水都能独自完成,也不是每次潜水都值得冒险。
几个月前,我在开发自己的代理编排器时,遇到了稳定性问题和生命周期漏洞,这些问题全都源于下属代理的设计不够严谨,导致各种 bug 纷纷涌现。
当我盯着屏幕,看着代理们在一小时内第无数次崩溃,尝试多次让它们自己修复却屡屡失败时,我突然想起几年前读过一本关于 Kubernetes 的书(没错,我就是个极客),书中提到的“电平触发状态机”几个字赫然映入眼帘。
于是我一头扎了进去,比原计划还要深入,甚至追溯到了“电平触发”的电气工程起源。我把代理们也带上,从头重新设计了核心架构。
从那以后,系统一直稳定可靠。这次成功的重写,主要得益于我恰好记得多年前读过的内容,并且愿意投入精力,学习完成任务所需的一切。
所以,首先要有一张系统的宏观地图,明白它是由层层抽象构成的,直至最底层。而由于所有抽象都会泄漏(参见),作为系统所有者,你的职责就是知道该往哪里潜、潜多深。
这不再是“通用型”与“专家型”的对立,甚至也不是那种传说中的“T 字型人才”。
新的开发范式需要的是这样一种通用型人才:能够在未知领域深入钻研,是“压力下的快速学习者”,而不是简历上堆砌的标签。你需要真的能在与“海怪”搏斗的过程中迅速成长。
这些年我面试过很多人,每当看到简历上写着“快速学习者”时,我总会问他们:“能否讲讲你曾经如何快速掌握一个复杂系统?你是怎么做到的?”你会怎么回答呢?
未来的主人
基础可以通过学习获得,广度则靠日积月累。而即时深度,则需要不断实践。这三大支柱共同决定了一个人在系统所有权中的份额。也许你一开始只在浅水区活动,派潜水机器人下潜。
只要你有扎实的基础和不错的广度,就可以放手去做点什么。但也要做好准备,一旦出现问题,就穿上装备,亲自下水与“怪物”搏斗。
未来谁将负责那些庞大的海底采矿作业?没人知道,但我敢打赌,一定是那些能够灵活运用上述三大支柱的人。比如那个曾经拆过一台旧收音机并修好的孩子,他对收音机一无所知,只是单纯想听听音乐——即便最后把所有零件装回去时多出了几颗螺丝。