InfoQ 中文 · 10/20/2026, 15:16:04
Chief Architect Proposes 5 Counter-Intuitive Principles for Agentic Coding
To address the degradation of system maintainability caused by AI coding tools, Chief Architect Carlos Kelkboom proposes five counter-intuitive principles. Key mandates include human-defined code skeletons, prohibiting main Agents from directly modifying files, isolating execution in Git Worktrees to discard unreadable code, requiring one-shot delivery of large tasks to avoid fragmented patches, and using parallel Agents to explore solutions. The framework emphasizes that as code generation costs drop, human engineers must retain control over system structure, evolutionary intent, and collaboration processes.
SOURCE COVERAGEFull Original Report
Contents5 sections
AI 编程工具提升效率的同时,正加剧系统可维护性与架构一致性的退化。Carlos Kelkboom 提出五条反常识原则,聚焦人对代码结构、演进意图和协作过程的主导权。
禁止主 Agent 直接改代码,强制人工定义骨架;生成代码不可理解即舍弃,隔离执行于 Git Worktree;大任务一次性交付,避免碎片化补丁。
适合资深工程师、技术负责人、系统架构师阅读。

翻译|林绮蓓
“我已经很久没好好看过代码了。”在 AI 编程工具越来越强的今天,这句话开始成为一些开发者描述自己工作方式的方式。需求交给 Agent,代码由它生成,测试和审查还可以交给其他 Agent。软件似乎从来没有这么容易做出来过。
但另一种情况也越来越常见:项目启动得很快,做了几轮后却很难继续。Agent 悄悄换掉原有部署脚本,同一个功能在代码库里被实现了五遍;一旦需要大幅调整架构,它往往选择继续打补丁。开发者拥有了大量能运行的代码,却未必还看得清系统究竟是怎么搭起来的。
在首席架构师 Carlos Kelkboom 看来,问题已经延伸到人与人的协作。开发者遇到问题越来越习惯先问 Agent,PR 下的讨论逐渐减少,与用户沟通、理解产品需求、反复思考架构的过程也更容易被跳过。代码可以更快生成,但一个项目为什么做、为谁做、以后怎样演进,仍需要有人作出判断。
在 Patrick Akil 主持的这期播客中,Carlos 分享了五条颇为反常识的 Agent 编程原则:第一,亲自搭建代码骨架,提前确定目录、关键文件和函数签名;第二,禁止主 Agent 直接修改代码和文件,充分理解需求后才启动执行 Agent;第三,生成的代码看不懂就扔,执行结果隔离在 Git Worktree 中,一旦发现偏离预期结构,就直接舍弃重来;第四,尽可能让 Agent 一次性完成较大任务,避免反复修改导致代码不断打补丁;第五,让多个 Agent 并行探索不同方案,例如三个 Agent 分别开发,再由第四个 Agent 比较结果,只合并好的部分。
两人的分歧也贯穿了整场对话。Patrick 更看重工具能否尽快做出有用的东西,Carlos 则反复追问:开发者从中学到了什么?如果代码越来越便宜,工程判断、代码审查和初级工程师的培养该如何改变?讨论最终从编程实践延伸到未来编程语言、AI 泡沫,以及一个更根本的问题:当 Agent 能写越来越多的代码,人类工程师还应该把时间花在哪里?
太长不看版:
Patrick:软件工程这几年最值得警惕的变化是什么?
Carlos:开发者越来越习惯与 Agent 一起工作,却减少了与同事、用户的交流。PR 里的讨论变少了,产品意识也可能因此被削弱。能很快做出一个演示,和真正理解用户、把软件长期做好,是两回事。
Patrick:Agent 随时能回答问题,为什么还要花时间找人讨论?
Carlos:因为即时答案也会改变思考习惯。Agent 回答得快,还可能顺着你的预期说;人与人的讨论慢一些,却能带来不同经验、质疑和反馈。学习不只是得到答案,还要留出时间自己消化。
Patrick:为什么你坚持先搭代码骨架,再让 Agent 动手?
Carlos:Agent 擅长迅速找到一条实现路径,却未必顾得上新代码与整体架构的关系。我会先建目录、文件和关键接口,明确模块职责。否则,代码重复、“上帝对象”和莫名其妙的新脚本都可能出现。能不能把方向交代清楚,是我认为拉开 Agent 工程师效率差距的关键。
Patrick:不熟悉一门语言,也能先搭出合理的骨架吗?
Carlos:可以先研究成熟的开源项目,让 Agent 对比设计模式、解释取舍,再自己动手写些注释和初步结构,反复讨论。关键不是让它替你作出所有决定,而是借助它培养自己的判断力。
Patrick:为什么你的主 Agent 被禁止写代码、修改文件?
Carlos:我希望它先专心理解我在问什么,而不是听到问题就急着实现。我让主 Agent 负责对话、检索和确认意图,用多个模型辅助评分;只有当它对理解我的目标有超过 90% 的把握时,才向我提出启动执行 Agent 的建议。
Patrick:Agent 写完代码,你怎么审?发现不对又怎么办?
Carlos:先看是否符合我预先设定的结构。如果打开代码就认不出那些应该存在的东西,我宁可扔掉重做。我会在执行一两个小时后就暂停检查 Worktree;出了问题,就回到对话里查清是骨架、需求还是上下文信息相互冲突,再开新会话重试。
Patrick:为什么主张“代码便宜了,就应该经常推倒重来”?
Carlos:因为在我的经验里,Agent 对一次性完成较大任务比较擅长,对已有代码反复大改却不太擅长。既然生成代码的成本降低了,就可以并行探索不同方案,甚至让三个 Agent 分别开发、第四个 Agent 评估,不必为了保住已有代码而不断叠加补丁。
Patrick:如果人人都能靠 AI 写软件,还需要专业工程师吗?
Carlos:更需要工程判断。资深工程师要先厘清产品目标、架构、部署方式和安全边界,也要帮助新手学习这些取舍。我担心的一种情况是,我们培养了一代很会使用 Agent、却缺少独立开发能力的人。
Patrick:AI 生成的代码越来越多,代码审查应该看什么?
Carlos:除了最终 Diff,还应看前期需求和架构决策、每次提交如何推进方案,以及实际增加了哪些复杂性。可以借助静态分析、变异测试和新的可视化方式,但团队围绕 PR 的讨论不能因此消失。
Patrick:你怎么看 AI 的未来?这轮热潮会不会是泡沫?
Carlos:要分开看。AlphaFold、医学和材料科学等方向的突破让我觉得像魔法;LLM 在编程上的价值也很大,但巨额投入与产业承诺是否都能兑现,我有疑问。与此同时,权力向大型科技公司集中,以及 AI 是否真的缩小机会差距,也令我担忧。
开发者开始只和 AI 说话了
Patrick:今天的嘉宾是 Carlos Kelkboom,他是我认识的最出色的工程师之一。Carlos 你觉得跟几年前比,软件工程最大的变化是什么?
Carlos:首先,我观察到一个我不喜欢的现象:开发越来越像一个人的工作,同事之间的合作越来越少。过去大家重视拉取请求(PR,Pull Request),相互协作,也会在自己和同事的 PR 下交流意见;现在这样的交流越来越少,甚至有人说 PR 以后可能会消失。现在你经常和智能体一起工作,却不再和团队一起工作。这是我观察到的趋势,也是我认为很危险的一件事。
Patrick: 你觉得为什么会这样?我有个猜测,可能和我们在 Reddit、YouTube 上看到的情况有关。那里有很多独立开发者,往往是他们最先尝试新技术。当然,这也可能只是我的信息茧房,不知道这个观察是否准确。那么,在规模更大的企业里,开发团队依然有很多人,大家既要和 AI Agent 协作,也要和同事协作,实际情况又是怎样的?
Carlos:我有个好处,就是完全不用社交媒体。我连 Reddit 账号都没有,LinkedIn 也只是拿来和你联系。我不太关注社交媒体上的讨论,但我读很多东西,包括文章和白皮书,也跟很多公司和员工交流,所以我的印象不完全来自网上的展示。
我认为快速做出个东西却不收尾,和认真做一款希望能长期使用的产品,完全是两回事,这就像是马拉松和短跑的区别。一个人独立做项目,往往差最后一口劲把它真正做完;把东西放到社交媒体或 YouTube 上展示,和真正完成产品,也是不一样的。
比如,现在网上经常有人展示自己如何快速“做出”一款游戏。我之所以给“做出”加引号,是因为游戏开发最大的难点其实在于倾听玩家的反馈,并把游戏设计做好。这些事情当然也可以借助 AI Agent 不断迭代,但开发者仍然需要与外界交流。
与人沟通本来就是最难的事情之一,而现在,开发者可以待在自己的小圈子里,不怎么与外界交流,就能独自做出很多看得见的成果。这反而进一步强化了这种倾向。但开发软件时,始终需要想清楚两个问题:为什么要做这个软件?它是为谁而做的? 所以我才说,产品意识正变得越来越重要,但它恰恰也是当下被削弱得最厉害的东西。
Patrick:你思考过怎么解决这个问题吗?以我自己的经历来说,以前我觉得开始最难;有人说自己启动了很多项目却没做完,我一直不理解,因为我总是在有把握能完成时才开始。而现在,开始一件事对我来说从未如此容易。
Carlos:是的。
Patrick: 我用的工具有额度限制,界面上还有一个进度条。每到周末或者月底,我就总想着尽量把额度用满。甚至临睡前还会琢磨:要不做个游戏?我确实做过一些游戏,后来甚至还搭了一套能自动生成游戏的 Harness,让其他人也能用它来制作游戏,相当于在游戏开发之上又加了一层工具。我会试着解决一些自己预想未来可能出现的问题,看看最终能做出什么。但往往做到这一步就停下了,没有再往前推进:既没有把项目打磨到足够稳定,也没有真正完成,更没有把它做成可以出售的产品。我启动过很多项目,但真正做完的没几个。
Carlos:不过,这种情况以前就有。我自己就做过大概五个编译器,每个都动手写了,也都差不多快完成了,但真正称得上做完的只有一个。软件开发一直如此:有了一个有意思的想法,想顺便学点东西,于是开始动手,最后却不一定能做完。
现在的区别在于,你得先想清楚为什么要启动这个项目。是为了学习?为了试验 AI Agent?还是为了真正做出一款产品?我觉得,现在开发者需要对自己做一件事的目的有更明确的认识。一旦目标清晰了,你刚才说的那种困扰也就没那么严重了。
如果你只是想玩一玩、找点乐趣,那你其实已经达到目的了。但如果你是想以此创办一家公司,那就必须把东西真正做完。
**Patrick:**说到这种情况,我发现自己和 AI Agent 好像也困在了一个小圈子里。遇到问题时,我第一反应往往是去问 Agent,而不是找你这样的同事交流。至少在我身边,这种工作方式已经很常见了。你觉得,开发者该怎么跳出这个圈子,更多地与其他人交流、协作?
Carlos:这个问题我已经想了很久。我觉得我们一起工作的方式、做软件的方式,恐怕都要改变。开源已经发展了这么多年,我觉得现在还得多办聚会,多分享经验,围绕具体项目共同工作。最好能够组织小组,成员愿意互相看看彼此的产品、提提意见。大家得重新走到一起,我们需要真正聚在一起合作。虽然待在自己的圈子里更容易,也得想办法降低门槛,让你更容易找到对你的项目感兴趣的人。
这也意味着我们需要更开放一点,不要害怕把想法讲出来。人们常说点子不值钱;放到全世界来看,点子也许确实不稀缺;可对想到它的人来说,它很珍贵,也很私人。分享想法时,人们可能担心被别人抢先实现,也担心自己的想法不够成熟。要消除这种顾虑,就需要一个包容、愿意互相支持的社区。我想到一个例子:最近推出的《魔兽世界 Forever》,不知道你有没有听说过。人们在测试版里问一句“这个设置怎么关”这样很基础的问题,也会有四五个人来帮你。做软件的人需要这样的互助氛围。
Patrick:但我看到的趋势是大家都会选择去问自己的智能体,似乎没必要问人了。有些人本来就不愿意回答那些他们觉得太简单的问题。我甚至听过一句话:“先问你的智能体,再来问我。”这话有些好笑,却也让人感到不安。我感觉大家比过去更容易怀疑自己的能力,即使智能体可能把人带偏,或者让人看不到全局,大家也更倾向于依赖智能体指路;从学习的角度看,只和智能体交流,学到的东西可能不如和人讨论时多。
Carlos:是的,问人确实慢,因为你无法立刻得到答案。智能体则能立即回应,而且往往倾向于给出你希望听到的答案。你可以同时问几个模型,让它们互相质疑,或者设计一套对抗式的流程,多少能缓解这个问题。但这会带来一个更深的变化:你会越来越习惯只和这些立刻回答的机器交谈,不再留时间给自己安静地想。那些纷乱的念头,本来得在经过一段时间的沉淀,才能慢慢理清,从而让你生出新想法,或者让你发现原来还有另一条路可以尝试。
等会儿我们可以聊聊我具体怎么用智能体写软件,但创造力这件事值得我们先讨论。John Cleese 讲过创造力的开放模式和封闭模式:进入开放模式,首先得进入一种开放、平静的心理状态;什么都不做,让纷乱的想法逐渐沉淀,才有可能真正放开思路。可社交媒体、AI 和智能体却持续提供新的信息。我们几乎没有时间坐下来,闭上眼睛,整理自己的想法。
Patrick:这有什么坏处?我有问题只要向智能体提问,马上就能得到答案,紧接着处理下一个问题,一路往前赶。我一直觉得做软件就是要快。有了智能体,我们推进得更快;虽然也更容易出现故障,但修起来也快。速度在我看来还是很重要的。如果我不坐下来想清楚自己在做什么,那我会失去什么?
Carlos:我认为代价是学到的东西变少了。知识需要经过思考和消化,才能真正被掌握。让智能体直接生成代码,自己不一定学到了什么。以前认真学过、如今会用智能体的人,会比未掌握基础、仅依赖智能体的人更有优势。工程实践的乐趣,本来就在于不断学习:进入陌生领域,读一份新规范,学一门语言,试一个新框架。JavaScript 为什么会有那么多框架?也是因为做框架的人一路从别人那里学到了东西。而要真正做到这一点,必须给自己时间去消化。
Patrick:那我学习怎么操作智能体,学习相关领域知识、为智能体准备的各类配置和说明文件,以及不断出现的智能体工作框架的新功能,从长远看,这也算学习吗?
Carlos:当然算。
先搭骨架再让 AI 写代码,效率可能差 10 倍
Patrick:那你从智能体身上学到的最重要的一点是什么?具体工作时,你又是怎么把它用好的?
Carlos: 这其实是两个不同的问题。先说我是怎么与 Agent 协作的吧。现在,我会先亲自搭好代码骨架:建立目录结构和文件,在文件里写一些注释,有时还会提前写好类名、函数名或函数签名。这些内容不需要特别规范,写个大概就行。关键是通过这些准备工作,明确告诉 Agent 我想要怎样实现,让它按照我设定的方向去写代码。
因为我发现,Agent 很擅长沿着一条路径快速解决问题,甚至可以说是暴力求解,但它经常忽略这些代码该如何融入应用的其他部分。结果就是代码重复、模块内聚性差,甚至出现“上帝对象”(God Object)等问题。这些都是我们不希望看到的,因为它们会让代码越来越缺乏灵活性。
所以,我现在都会亲自搭建这套骨架。这个过程可能花 20 分钟、半小时,有时甚至要一个小时。比如开发编译器时,我会先规划目录结构,写一个词法分析器(Lexer),再写一点语言规范。我还会提前设计好抽象语法树(AST)如何转换为中间表示(IR),并通过目录和文件结构体现这些设计,再用我想实现的语言写出大量示例。有了这些准备,Agent 就能非常出色地生成符合我要求的编译器。
Patrick: 因为你提前搭好了代码骨架?
Carlos: 对,没错。我也试过完全不做这些准备,直接让 Agent 从头到尾自己写。结果做上一两个月,就会明显感觉越来越难推进:Agent 的速度变慢了,生成的东西也越来越不符合预期,有些功能开始出问题。打开代码文件时,你甚至已经认不出自己的应用了,完全不知道中间发生了什么。
这种情况我遇到过很多次。比如,项目原本有一个部署脚本,但不知道为什么,Agent 突然决定不再使用它,反而另外创建了好几个脚本,最后谁也不知道这些脚本到底是干什么的。
所以,不要跳过前期的结构设计,而且必须非常明确地告诉 Agent 你希望它做什么。我认为,这种能力可能就是区分 10 倍效率和 100 倍效率的 Agent 工程师的关键。
Patrick: 但要做到这种有意识的设计,我得依靠过去积累的经验。我对项目结构确实有自己的判断,不过主要限于那些我熟悉、亲手实践过的编程语言。如果现在让我做一件完全没做过的事,比如开发编译器,我该怎样培养这种能力和品味,才能判断什么样的代码骨架才是好的?
Carlos:好问题。在智能体出现之前,你会怎么做?
Patrick:我会向有经验的人请教。我过去不喜欢从零启动项目,工作里接触的大多是已经启动的项目。有了现成约定,我通常能高效开展工作;可让自己从头建立项目约定,我没怎么尝试过。我会找那些擅长做这件事的人,一起做,问他们哪些决定最重要。因为没有亲手反复做过,这些经验也没有真正的内化。但是现在再遇到这种事,我大概会先问智能体。如果还是卡住了,也依然会找人帮忙。
Carlos:如果是现在,我会这样做:面对一门完全陌生的语言或一个新领域,我先去找做得不错的开源项目,下载后研究。如果你更愿意待在“智能体的小圈子”里,也可以让它对照几个项目,找出值得学的做法。用 Claude 之类的工具,还可以准备一些“追问我”“教教我”这样的技能:让它讲清这些项目的基础,哪里做得好、为什么好,哪里有问题。花上一两天,你对好的、坏的做法多少会有感觉。然后我通常会出去拿杯咖啡,坐下来独立思考,整理刚学到的内容。遇到不明白的地方,再回去提问,之后继续思考。
只有当我对想做的事有了比较清楚的感觉,才会从很小的概念验证开始,搭一个骨架,请智能体评估:这样搭可行吗?能同时解决刚才提到的两个问题吗?就这样来回琢磨,直到心里感觉对方有了底,才让它动手实现。这些年我学到的一点,就是不要贪图马上看到成果的那一下快感。
Patrick:照我的理解,如果我没有先搭建骨架,智能体就会自行决定代码结构。它能积极完成眼前的任务,却未必能充分考虑结构是否合理。
Carlos:没错,而且它也不管你将来想要什么,只顾着把你眼下提出的要求硬做出来。
Patrick:可软件以后总是会变的。你是这个意思吗?
Carlos:做软件,唯一不变的就是变化。就拿编译器来说,没人会写完一个编译器就觉得大功告成了,它总会不断演进。可能需要增加新的语言特性,也可能要调整优化方式。
举个我自己的例子。我曾经开发过一个编译器,最初只实现了一套很简单的列表机制,后来想进一步扩展它。当时我已经和 Agent 一起开发了大约一个月,而且用的是一门我不太熟悉的编程语言。最后,我让 Agent 回过头来检查列表功能,结果它告诉我:同一个列表功能竟然被重复实现了五次。
如果只是让 Agent 一路埋头写代码,就很容易出现这种情况。后面当然可以通过不断迭代来清理和重构代码,但前提是,你得先能识别出问题在哪里。
Patrick:对,这恰恰是我觉得最难的地方。我很喜欢你刚才提到的方法:先看看开源社区里有哪些现成的实现,了解好的设计模式,再让 Agent 帮你解释这些东西,让自己真正理解其中的原理。
你可以不断向 Agent 追问,直到把自己好奇的问题彻底弄明白,再消化吸收这些知识,然后才开始动手。我觉得自己平时还没有充分利用这种方式,但以后如果要用一门不熟悉的语言开发项目,我想试着这样来设计代码骨架。
毕竟我掌握的编程语言没你多。如果我不熟悉一门语言,能不能先通过和 Agent 讨论来完成这些准备?这样开始是否可行?你会有什么建议?
Carlos:当然可以。你可以先问它:“这门语言最重要的理念是什么?能不能拿我熟悉的语言来类比讲讲?”比如你会 C#,想学 Go,就问:“我会 C#。要写出符合 Go 语言惯例的代码,我需要改掉哪些习惯?两门语言差在哪儿?我现在的写法哪里不对?”然后自己先写点东西,哪怕只是几段注释和一个很粗的结构,写错也没关系。智能体可以帮你改。先请它看看你搭的架子,它通常会直接指出这里不对、那里不对,然后你再提出质疑,和它继续讨论。
Patrick: 你觉得现在还有必要学习编程语言吗?
Carlos:我觉得,我们可能很快就需要一门全新的编程语言了。我知道很多人都这么说,但我确实认为,编程本身有几个根本性的东西需要改变。
首先,我认为应该彻底抛弃堆内存(Heap)。我们不再需要指针、动态内存分配,以及围绕它们建立的那一整套机制。我希望未来的编程语言具有很强的确定性,而且可以分为三个层次。
- 第一层是人类编写的代码,可以把自然语言和某种结构化语言结合起来。甚至不需要太复杂的语法,也许只要有一个
component关键字,再加上组件名称就够了。然后在组件内部,通过缩进和类似 Markdown 的写法,描述这个组件应该完成什么任务。 - 第二层是某种中间表示,可以是图表、视频,也可以是其他直观的视觉或音频形式。Agent 会通过这些形式,向你解释它理解的目标是什么。开发者则可以通过语音或其他方式提出质疑、进一步明确要求,再把这些反馈转化回第一层的描述。
- 最后一层才是真正运行在机器上的程序表示。我认为,这一层应该抛开传统编程中许多沿用至今的假设。
具体来说,while 语句和其他循环都应该有明确的执行边界,还可以采用 Actor 模型。我们应该真正摆脱堆内存,重新采用基于记录(Record)的数据结构,或者过去被称为 Plex 的那种结构。
最终,我们需要重新获得一种能力:能够清楚地推理自己的机器上究竟在运行什么,而不是像现在这样,面对大量难以预测的程序行为。
Patrick: 你之所以强调确定性,主要是因为现在的程序存在太多不可预测的行为吗?
Carlos:对,但还有一个原因:如果采用这样的运行时,性能也会非常高。
另外,现在的计算系统其实有些割裂。比如,为 GPU 编写程序和为 CPU 编写程序,往往需要采用完全不同的方式。CPU 针对分支预测、数据预取等做了大量复杂优化,而 GPU 的计算模型相对直接,也更具有确定性。你分配一块内存,填入数据,再在上面执行一些计算,整个过程就完成了。按照这种模型,不需要那些围绕堆内存和动态分配建立起来的复杂机制。
要让 GPU 适应 CPU 的那套编程方式很难,我也不认为这是正确的方向。我觉得我们应该反过来,让 CPU 向 GPU 那种更具确定性的计算方式靠拢,同时保留 CPU 所需的不同计算能力。 这样,我们就能更充分地利用 SIMD(单指令多数据)等能力,也能支持不同的数据结构。比如需要处理图结构时,就可以把它映射到合适的表示形式上。
这就是我认为未来编程语言需要具备的核心特征:高度确定性。
我刚才可能还没解释清楚,为什么确定性这么重要。想象一下,你把一颗卫星送上太空,它的内存容量是有限的。如果程序运行时还在不断进行堆内存分配,就可能出现内存不足异常,或者空指针异常。你肯定不希望卫星在太空中遇到这些问题。
而现在,软件正在越来越多地应用于机器人、卫星、机器学习等领域,我们需要尽可能确保程序的运行行为是可预测的。如果一台设备只有 2GB 内存,你就应该能够明确知道,这个应用在任何情况下都不会超出这个限制。所以我才认为,我们不能再继续依赖动态内存分配了。
Patrick:我觉得这可能也是为什么,至少在卫星这类场景里,很多人会选择 Rust。因为我理解,Rust 能让开发者尽可能在早期发现这类问题。
Carlos:Rust 还有一套规范,通过限制可使用的语言特性,让程序行为更具确定性。不过,真要靠人手写,确实挺费劲,至少我自己的体验是这样。
Patrick:那你试过让 Agent 写 Rust 吗?
Carlos:试过。写起来比很多其他语言慢一些,但也挺有意思。Agent 其实很擅长写 Rust,只是它们拆分代码的方式往往不符合我的预期。而且,我一直觉得 Rust 代码后期很难修改。一段 Rust 代码只要能正常运行,我就不太想再碰它了,觉得这样就算完成了。相比之下,其他语言重构起来要容易得多。
当然,这只是我个人使用 Rust 的感受。我并不想劝大家放弃这门语言。Rust 是一门设计得很出色的语言,拥有很多现代编程语言应该具备的特性,比如可辨识联合类型(Discriminated Unions)等。但在我看来,借用检查器(Borrow Checker)本质上是为了管理堆内存中的数据。如果我们彻底取消堆内存,也就不再需要借用检查器了。
想象一下,如果有一条记录(Record),它在内存中的位置是固定的,而且从程序启动开始,所有数据就已经安排好了。这样,你只需要知道记录的起始位置,再通过偏移量定位各个字段,就能非常快速地访问数据。
Patrick: 有意思。那你设想的这种新语言,最终会是一门统一的语言吗?大家会逐渐收敛到同一种语言上吗?
Carlos:这正是我提出三层结构的原因。我认为最终生成的代码,以及底层运行时,可以是统一的,但中间各层基本上都可以像插件一样自由替换。甚至可能发展到这样一种程度:每个人只需要和自己的 Agent 对话,就能定义出一门属于自己的编程语言。
Patrick:不过,这又让我想到一个问题。我在想办法提高 Agent 的工作效率时,经常遇到一个难题,其实人与人之间、不同团队之间,甚至组织的不同层级之间也一样:大家如何使用同一套语言交流?比如我说“组件”,在 Agent 的语境里究竟是什么意思?是自定义 Agent、本地 Agent,还是主 Agent 下面的子 Agent?不同的人可能有完全不同的理解。如何建立共同语言,一直是个难题。而按照你设想的未来,每个人都可能有自己的编程语言和交互方式。那大家又该如何建立一套彼此都能理解的共同语言?
Carlos:这可是个价值百万美元的问题。说说我自己的看法吧。回到二十年前,如果我要开发一套 CRM 系统,大家其实不会一直争论“客户”到底是什么,因为数据模式(Schema)已经明确规定了“客户”的定义。真正困难的是,怎样形成这套 Schema,以及之后如何让它不断演进。但在某个阶段,这套 Schema 就是大家对“客户”的共同定义。所以,最难的是先达成一个初步共识,再在此基础上不断迭代。
如果我没理解错你的问题,我觉得我们应该从比现在简单得多的东西开始。比如,哪怕一开始只规定“客户有一个 ID”,大家也能立即达成共识:没错,客户当然需要一个 ID。然后我们就可以在此基础上逐步完善。数据建模已经为我们提供了很多现成的方法。比如星型模型,或者分别从销售和营销的角度建立客户模型。我们可以有不同类型的模型,满足不同的需求。
但关键在于,共识不应该只是某个时间点上确定下来的东西,我们还需要就它未来如何演进达成共识。
过去软件开发的速度比较慢,人们更关注眼前要做什么,而不是系统今后如何演进。这也是为什么我觉得,今天的敏捷开发已经不像它原本应该有的那样敏捷了。这个问题我们之前讨论过很多次,我觉得敏捷开发在某种程度上已经偏离了初衷。而现在有了 Agent,我们或许可以重新回到敏捷开发最初的理念:先从一个简单的起点出发,然后快速迭代、不断演进。
Patrick:这个观点很有意思。
AI 让代码不值钱了,但工程经验越来越重要
Patrick: 我们聊了这么久 Agent 辅助编程,我还真没想到,提前搭建代码骨架竟然是这么关键的一步。因为至少在我身边,有些人的工作方式完全不同。他们会说:“我已经很久没看过代码了。”他们觉得最高效的做法,就是让 Agent 包办所有事情,自己根本不用看代码。最后要么请其他人审查 Agent 生成的代码,要么让多个 Agent 进行对抗式审查或盲审。这些人一直在摸索、调整自己的工作方式,希望即使不亲自阅读代码,也能保持很高的效率。但显然,你不是这么做的。
Carlos:确实不是。而且说实话,我甚至不太理解这么做有什么乐趣。当然,如果你是在开发一款准备拿去卖的产品,想靠它赚钱,需要尽快推向市场,那我能理解。可如果只是为了自己做点东西,我就不明白了。你让 Agent 去干活,自己出门逛个街、看场电影,回来看到 Agent 交付的成果,就觉得很满足?我实在体会不到这有什么乐趣。
Patrick: 拿我自己来说吧,有时候我想要的就是最终结果。我未必在乎这个结果是怎么实现的,代码写成什么样,或者将来好不好维护。我只是想得到那个东西。
Carlos:可你刚才不是还说,自己启动了很多项目,却没几个真正做完的吗?那这两件事怎么解释?
Patrick:因为有时候,我确实没能得到最初想要的结果。我可能突然想到一个问题,琢磨该怎么解决,然后就让 Agent 花一整晚去研究。我觉得这种习惯也和工具的使用额度有关。我每周都有一定的额度,如果没用完,就总觉得浪费了自己花钱买来的东西,有种错过了什么的感觉。所以我总想着尽量把额度用足,尤其现在,我觉得以后可能还会更贵。
于是,一遇到什么问题,我就想:不如先扔给 Agent 试试,看看它能做出什么。然后我再去探索、研究它生成的东西。有时候虽然没达到最初预想的目标,但我至少学到了一些东西,或者摸索出一些组织任务、使用 Agent 的新方法,也算有所收获。这就是我现在的工作方式。
举个例子。有一次,公司给了我们一天的创新探索时间,可以自由做任何想做的项目,我就利用那一天搭了一套能够生成游戏的 Harness。我之前在一场会议上看到 Google 展示过类似的东西,当时还问他们:“你们打算开源吗?我很想让身边的人也试试。”
因为我有个比我小 16 岁的弟弟,他不懂编程,但我觉得他一定会喜欢自己做游戏。如果能给他这样一套工具,让他通过语音输入描述自己的想法,就能生成游戏,那一定很有意思。可 Google 后来并没有开源,所以我就想,那干脆自己做一个吧。于是我真的动手做了。对我来说,最终想要的就是这样一套工具,至于底层代码具体怎么实现,我其实没那么在意。
当然,我也会深入考虑一些细节,比如生成的游戏应该是什么效果、什么风格,要不要使用模板引擎,以及如何保证结果的确定性。这些才是我真正投入时间和精力的地方。比如,用户提出一些具体的游戏功能需求,我就希望通过这套 Harness 生成的游戏,能够尽可能符合他的预期。
后来,我弟弟真的用它做出了一个游戏。我又针对那个游戏做了不少深入的调整和完善,之后他又做出了更多游戏。我还把工具交给了我的伴侣,以及其他不懂编程的人,他们也都能用它来制作游戏。而在整个过程中,我其实并没有怎么查看代码。但我已经得到了自己想要的结果。
Carlos:但这完全没问题啊!因为你的目标本来就不是学习怎么开发游戏,也不是学习怎么写游戏引擎。你只是想让身边的人开心,看到自己在乎的人露出笑容。这是件很美好的事。如果一款百分之百靠 Vibe Coding 做出来的应用能实现这个目标,那就尽管这么做,完全没问题。
不过,你做出来的东西,和一支专业团队精心打造的游戏,肯定还是有区别的。当然,就算是专家团队花费大量心血制作的游戏,也未必能让玩家满意。玩家可能花了 50 欧元买游戏,玩完却觉得:“我花了 50 欧元,就买了这么个东西?”
我想换个角度问你:如果当时和你一起开发这套 Harness 的,是一位拥有 25 年经验的游戏设计师或游戏工程师,结果会有什么不同?
Patrick:有意思。如果当时能和这样一位经验丰富的人合作,我想我会更深入地研究一些技术和设计上的问题,因为我觉得自己能从中学到很多东西。比如,我和 Agent 为了尽快得到结果而做出的那些选择,到底是不是明智的?
假设我已经做好了这套 Harness,现在想把游戏画面彻底升级一遍。有些生成的游戏虽然采用了复古风格,但画面看起来还是比较粗糙,像素感很重。我也见过那些 3A 大作,知道高质量的画面能给玩家带来怎样的体验,所以会想在这方面再深入一步。但我不确定,光靠自己和 Agent 能不能做到。如果有一位经验丰富的合作者,也许我应该在项目早期就把这些问题提出来,和他一起讨论,把相关的设计考虑提前纳入决策。
不过,现在这么说出来,我又有点犹豫:这样会不会反而过度设计了? 毕竟,我很喜欢自己当时能够快速做出成果,而且确实把东西交付给了用户。后来我也更新过模板和图形部分,中途还卡住了,不得不把之前做的东西全部推翻重来,第二次才成功。所以,也许有些问题确实应该更早考虑。
Carlos:你刚才描述的,其实就是在搭建代码骨架。你先确定哪些部分最重要,把它们的结构设计好,再通过这套骨架告诉 Agent 应该优先考虑什么。软件开发一直是这样。过去开发一套 CRM 系统,第一版可能先从界面入手,后来发现数据库也很重要,于是第二版就会更关注数据库设计。接着又发现,数据库虽然设计好了,但用起来很复杂,于是下一版开始更多地调整应用层。我一直说,一个应用往往要做上三四遍,就是这个意思。每次重新来过,都会让它变得更好。
Agent 编程也是一样,但前提是你愿意停下来,回头看看自己做了什么,想清楚哪些地方需要改变、哪些方面值得投入更多精力,这样下一次迭代才可能做得更好。不过,我发现 Agent 编程发展到一定阶段就会遇到瓶颈,因为 Agent 还不擅长彻底推翻一个旧的设计,再重新实现。它往往只会在原来的基础上不断打补丁,甚至在同一个代码库里把同一套东西重复实现两三遍。Agent 擅长写代码,但还不擅长从工程层面构建一个完整的应用。
所以,你不妨拿之前那套游戏生成工具再做一次实验。这次先搭好代码骨架,建立几个目录,在里面放上 Markdown 文件,说明每个目录负责什么、模块之间的依赖关系应该是怎样的。我不太了解游戏引擎的具体设计,但比如你可以指定用 raylib 开发一款 2D 平台游戏,把素材放在哪里、各部分应该如何组织,都提前写清楚。然后再让 Agent 从头试一次。我觉得这样不仅能做出同样好的结果,速度可能还会更快。
Patrick:听你这么说,我就在想,这中间的界限到底在哪里?什么时候应该花时间提前搭建代码骨架,什么时候可以直接奔着结果去?也许取决于我打算把项目做多远。如果准备长期投入、深入开发,那就值得先打好基础;但如果只是想快速验证一个想法,那不看代码、不搭骨架也没什么问题。比如最初做游戏生成工具时,我完全可以把第一版当成概念验证,先看看能不能做出来,再把它扔掉,从头做一遍,看看后面能不能不断迭代、走得更远。我挺喜欢这种思路。我们平时似乎很少主动丢弃已经写好的代码,我做过的东西几乎都还留在 GitHub 或电脑里,只有尝试失败了,才会把它们扔掉。
Carlos:没错,这也是 Agent 现在带给我们的改变。代码越来越便宜了,所以可以快速迭代,经常推倒重来。 我还发现一个挺有意思的做法:创建三个 Git 分支,让三个 Agent 分别在各自的分支里开发,也可以使用 Git Worktree,让它们并行尝试不同方案。然后再找第四个 Agent,对比前三个 Agent 的成果,只把好的部分合并进来。
Patrick:相当于先同时探索几条路径,还能从不同方案里学到东西。你前面说,提前搭建代码骨架是提高效率的关键。那骨架搭好之后,Agent 就能一路顺利完成开发吗?你的工程工作流里还有哪些重要环节?
Carlos:代码骨架真的非常管用。不过我得承认,我自己通常会搭得比建议别人搭的更深入、更细致。
比如开发 Rust 应用时,我会先创建好各个 Crate;开发 C# 应用时,我会先建立项目、给项目命名,再放一些 Markdown 文件,说明每个项目的职责边界。我还会快速画一张架构图,标明这里需要实现什么功能、那里负责什么,同时提前建好目录结构和一些核心文件。
就像之前说的,开发编译器时,我会先创建词法分析器(Lexer)的文件,甚至提前写好函数签名。这意味着,我已经认真想过 Token 应该采用什么样的数据结构。
如果不提前定义,Agent 可能就会自行生成一个枚举,列出所有 Token 类型,再附带一些简单的源码位置信息。但我希望 Token 的定义更具体,比如它来自哪个源文件,从文件的哪一行、哪个位置开始,到哪一行、哪个位置结束。把这些要求提前体现在代码骨架里,Agent 最终生成的代码就会更符合我的预期,我以后也更容易理解和分析这些代码。而且,Agent 很乐意沿着我提前设定的结构继续实现。
Patrick:你有没有什么开源项目,可以放在节目备注里,让大家看看你具体是怎么搭建代码骨架的?
Carlos:有,不过我暂时还不太想公开。我们刚才讨论的那门新编程语言,就是我现在正在开发的项目,而且完全是用 Agent 来做的。我很愿意让大家看看,但它还处在非常早期的实验阶段。人们对一件事物的第一印象往往很难改变,所以我有点犹豫:如果现在就让大家看到这个项目,却不了解我背后的想法,也不知道它未来会如何演进,可能会产生误解。也许我应该在 README 里放上这期播客的链接,让大家先了解这些背景。
Patrick:这其实又回到了我们刚才讨论的“共同语言”问题。你说要搭建代码骨架,我原本觉得自己理解这个概念,但后来听你讲到具体会搭到什么程度、会提前确定哪些东西,才发现你的做法比我想象中深入得多。所以我才想看看真实的项目是什么样的。对了,我之前还和另一个人聊过,他也在思考未来人们会如何开发软件。我可以介绍你们认识,感觉你们应该会聊得很投机。
Carlos:那挺好的。我还觉得,现在整个软件行业可能到了一个需要重新行动起来的时候。我不太想用“革命”这个词,但如果软件开发真正变得更加普及,让更多人都有能力开发软件,那我们也应该利用这种能力去帮助更多的人。
我很想发起一个组织或者社区,把有实际软件需求的人和愿意动手开发的人连接起来。比如某个群体遇到了困难,需要软件帮助解决,而另一些人手头正好有一些富余的 Agent 使用额度,就可以利用这些资源为他们开发真正需要的工具。举个例子,我知道有些人很难获得轮椅,或者需要等待很长时间,也许就有人能通过开发软件帮忙缓解这个问题。但前提是,我们得先把这些实际需求收集起来,清楚地整理在一个地方,让开发者能够挑选自己有能力解决的问题。
现在软件开发的门槛降低了,几乎人人都可以开始尝试。但我们还需要有经验的工程师参与,帮助这些新开发者理解一些基本的工程要求,比如安全措施。如果你没有明确要求 AI 检查代码安全性,或者进行渗透测试,它通常不会主动完成这些工作。所以,我们需要让专业工程师与那些希望通过软件帮助社区的人建立联系。我觉得整个行业都应该把更多精力放在这件事上。过去那种“我们是专家,只有我们才能开发软件”的观念,现在已经行不通了。
Patrick:确实,现在开发软件的门槛一下子降低了很多。人们可以很快上手,把各种想法变成实际成果。但我觉得,在这个行业里工作过一段时间的人,多少已经知道哪些做法是好的、哪些可能有问题,而新一代开发者还没有形成这样的判断力和直觉。
Carlos:是的。而且,如果再看看学校里工程专业、软件工程专业的人数下降的情况,就更让人担心未来会变成什么样。我们现在已经不怎么培养初级工程师了,只是直接让他们使用 Agent。甚至连完整的 PR(Pull Request)评审流程都不再走了。以前大家会在代码审查中留下评论,认真讨论软件质量,互相交流为什么某种实现更好。现在这些事情越来越多地交给 AI 完成。
我最担心的是,我们正在培养一代只会使用工具、却没有真正学会如何开发软件的人。如果有一天,Agent 的使用额度变得非常昂贵,贵到大家用不起,或者 AI 因为某种原因突然无法使用,那会怎么样?我们依赖的整个体系可能都会崩塌。这个想法真的很可怕。
Patrick:你觉得真的会这样吗?AI 会不会只是一场泡沫,等泡沫破了,就什么都不剩了?
Carlos:说实话,我觉得这里面有三个不同的层面。首先,AI 确实带来了革命性的突破。比如 AlphaFold、Google 正在做的细胞相关研究,还有医学、材料科学领域的进展,都让我感到非常震撼。机器人、自动驾驶也是如此。在我看来,这些才是真正展现 AI 能力的地方。我还跟孩子们聊过这件事:我已经在没有这些技术的世界里生活了 47 年,现在突然进入了这样一个时代,该怎么形容这种感受?我能想到的词只有一个——魔法。
然后是大语言模型。LLM 当然也非常了不起,但它们的资源消耗很大,能源利用效率也不高。各家公司投入了巨额资金,行业也作出了很多承诺,可这些承诺究竟能兑现多少,我并不确定。所以,尽管 LLM 在编程方面带来的帮助确实非常大,我仍然觉得围绕它们的热潮带着炒作和泡沫的成分。
第三个层面是权力格局的变化。看看如今最有钱、规模最大的公司,几乎都是科技公司。尤其从隐私和安全的角度来看,这让我感到不安。
Patrick:具体让你不安的是什么?
Carlos:AI 一直被宣传成一种能够缩小差距、让机会更加平等的技术。但到目前为止,我并没有看到它真正缩小了这些差距,也不确定将来能不能做到。
Patrick:也就是说,未来可能是富人越来越富,而人与人之间的差距并没有缩小?
Carlos:对。我真的不知道这一切最后会走向哪里。
Patrick:说实话,我现在有一种挺奇怪的感觉,因为我以前从没亲身经历过这样一场技术变革。互联网刚兴起的时候,也许有人体验过类似的变化,但我没有。所以我不知道,现在这种感受和当年是不是一样。如今行业变化实在太快了,哪怕出去休假两周,回来都可能发现自己错过了某个新东西,而它已经彻底改变了人们开发软件的方式。
无论以后我的工作怎么变,哪怕越来越偏向管理、不再经常亲自写代码,我依然很认同自己软件工程师的身份。有时我甚至会想,自己还配不配得上这个称呼,但从自我认同上说,我始终觉得自己是一名软件工程师。
更奇怪的是,身处同一个行业,大家对变化的感受却很不一样。有些人通过社交媒体等渠道追踪每一项新进展,几乎什么都不想错过;另一些人还是照常做每天的工作,觉得这些技术暂时还用不到自己身上,也许只是泡沫,生活并没有太大改变。现在行业里存在着很大的这种差异。
Carlos:是的。不过对我来说,这最终还是要回到学习上。早期几代 ChatGPT 刚出现时——我记不清具体是哪一代了——我买了一本机器学习的书,自己动手搭建神经网络。很快我就发现,我做的网络远远比不上那些大模型,也因此认识到规模确实很重要。但整个学习过程让我收获特别大。
我并不是说,学完这些就能自己造出一个大语言模型。如今构建大模型早已不只是神经网络本身的问题,还涉及围绕它的一整套技术体系。不过,学习基础知识,弄明白这些技术大致是怎么运作的,会给你一个非常扎实的根基。有了这样的基础,你就不会因为休假两周而觉得自己错过了什么。我甚至觉得,如果假期过得不错,你回来时的收获可能比出发前还多。
Patrick:对,我还是会照常休假的。说回来,我们最开始聊的是软件工程的工作流程,我还想把这个话题再深入讨论一下。
主 Agent 不准碰代码,生成的代码看不懂就舍弃
Patrick:你之前说,提前搭建代码骨架能显著提高效率。我最近还听到一个叫“循环工程”(Loop Engineering)的概念,我的理解是,整个循环既需要推动任务向前的力量,也需要在方向偏离时把它拉回来的约束。搭骨架就像是在正式开始前先确定方向。那么,后面的约束是怎么实现的?你会不会设置一些明确的执行路径或工具,让 Agent 始终待在你划定的边界内?还是说,只要骨架搭好了,它自然就会照着做?
Carlos:当然会,我也做了不少这样的工具。不过麻烦在于,每次有新模型出来,我几乎都要重新设计一遍。我总是尽量从第一性原理出发:如果要让 Agent 作出判断、与你交流,它首先需要知道什么?由于上下文会不断重置,你得持续为它补充信息;但上下文又不能无限膨胀,否则 Agent 的表现会变差,成本也会越来越高。
所以,我借鉴了社交网络的思路来设计自己的 Harness。先找到与当前任务有关的信息连接和重要节点,再给每个节点附上一点元数据,比如一句话摘要、一个 Git 提交哈希,或者能找到更多资料的目录路径。随后按照这些信息与当前任务的相关程度重新排序。必要时还可以让人参与确认:“你要找的是这个吗?”确认之后,Agent 才沿着线索继续深入,最终找到相关的提交历史和代码,弄清楚发生了什么。
我还有一个习惯:总是先让 Agent 查看某次提交的差异(Diff),再去读完整的代码文件。我发现,先看改动能很好地帮助它建立上下文,就像是在真正阅读代码之前先热身。
Patrick:有意思。我知道“渐进式披露”(Progressive Disclosure)这个概念:先给 AI 留下一些线索,让它顺着线索逐步展开,找到需要的信息。但你的做法似乎又深入了一层:面对大量分散的信息,你会先做相关性重排,甚至在读完整文件之前先查看提交记录。这些都已经设计进你的 Harness 里了吗?
Carlos:对。
Patrick:也就是说,先看提交记录,再通过渐进式披露和相关性重排,一步步找到正确的信息。
Carlos:对,而且这些信息是分布式存放的。比如,我把负责检索资料的 Agent 叫作“研究 Agent”,虽然它做的事情还算不上真正的研究。它会查找本地文件、网站等不同来源。就像刚才说的,每条信息先提供一句简短说明,再附上目录结构、路径、哈希值或者 URL,需要时就可以沿着这些线索继续深入。说到底,这就是一张信息关系图,很适合逐层检索。
Patrick:但你并没有把它们放进图数据库,而是让这些关联在使用过程中自然建立起来?
Carlos:没错。我不用图数据库,就是为了让信息保持分布式。目前,我用 Markdown 文件表示节点,再结合索引检索以及 Markdown 文件头部元数据(Front Matter)中的关键词搜索,来查找节点之间的关联。我还在摸索怎样把这些机制和路径结合起来,管理所有信息。不过目前这套方法运行得不错,也确实很有价值,因为我还按照同样的思路组织了 Agent 的分工。
我最先对话的主 Agent 始终处于一种“开放模式”(Open Mode):它不会直接修改代码,也不会对本地开发环境做任何改动,只负责和我交流。我可以教它一些东西,也可以让它教我。对话中形成的信息会重新写回那张信息关系图,方便下次会话检索。等到充分理解任务之后,它才会启动子 Agent 去执行具体工作。我希望它在真正动手之前尽可能多地向我提问,再把它对任务的理解复述给我,供我确认。
Patrick:那这是不是意味着,你不会同时运行多个主 Agent 的工作循环?还是说你也会并行运行?
Carlos:会,我也会同时运行多个主 Agent。
Patrick:也就是说,你平时对话的主 Agent 不会修改代码,也不会改动文件系统。需要实际执行任务时,它才会启动其他 Agent。但与此同时,你也会并行运行另一个遵循同样规则的主 Agent 会话?
Carlos:对。
Patrick:有意思。那你为什么要默认禁止主 Agent 修改文件?它们明明有这种能力,而且非常积极,总想主动做点什么。但你却有意限制了它。为什么?
Carlos:因为我希望它专心回答我提出的问题。我之前做过一个游戏引擎。有一次,我在那个项目里问 Agent:“你怎么看 John Cleese 关于创造力的开放模式与封闭模式的演讲?”结果它反过来问我:“要不要我把这个实现到你的项目里?”
那一刻我突然意识到,它满脑子想的只有一件事:能不能帮我写代码?如果它只想着替我写代码,又怎么可能认真回答我的问题? 所以后来我干脆给它设了硬性限制:先好好跟我对话,不要急着动手。
我还设计了一套评分机制,会让几个模型参与评估,其中包括一个在本地后台运行、负责评分的 Gemma 模型。只有当主 Agent 对自己已经理解我的意图有超过 90% 的把握时,才可以向我提议启动子 Agent。
等子 Agent 真正开始执行后,它们可以一直做下去,运行 12 个小时也无所谓,同时启动多个也没关系。但在主 Agent 非常确信自己理解了我的要求之前,绝对不能开工。
Patrick:有意思。你现在会不会把更多工作放到 Agent 开始执行之前?比如一个想法可以只涉及某项功能的一部分,也可以是需要深入设计的一个界面,或者一整套功能。你通常会把任务拆得很小,还是尽量一次做大?
Carlos:我会尽量做大。因为我发现,Agent 不太擅长修改已有代码,却很擅长一次性完成任务。感觉它们接受的训练更偏向一次把事情做完,而不是在同一个代码库里持续迭代开发。至少我从一些基准测试中看到的是这样。
Patrick:真的?那我得把之前采访 Dex Horthy 的那期节目发给你。他提出了“上下文工程”(Context Engineering)这个说法,还向我介绍过一个叫 Slop Code Bench 的基准测试。它测的正是这种连续的软件开发:先做一次修改,再增加一项功能,过一段时间又要修改之前写好的功能,必要时还得重构。它让模型在一段时间里不断面对新的变更要求。我记得当时某个 6.0、也可能是 5.6 版本的模型,得分大约只有 15%。
模型做到一定阶段就卡住了,只会不断往已有代码上打补丁,后来甚至开始编造内容,或者无法管理好上下文。也就是说,连续开发过程中容易不断出错,而一次性完成任务的表现却好得多。这和你的观察很一致。不过,你的做法正好和我原先想的相反。我本来以为,改动越小,我越容易理解、查看和审查代码,判断它是否符合预期。你却倾向于在动手前把整套功能的要求都谈清楚,甚至用 Gemma 这样的本地模型评估 Agent 对任务的理解程度,再让它去执行。
Carlos:对,但这正是我提前搭建代码骨架的原因。大部分结构在执行前就已经确定了。
Patrick:那你会查看最终产出、审查代码吗?
Carlos:当然会。如果我不喜欢,或者看不懂,我就直接扔掉。
Patrick:那反馈是怎么进入下一轮的?毕竟你在前期花了不少时间,把一整套功能想清楚,Agent 又可能连续跑上几个小时。等你看完结果,发现不符合预期,接下来怎么处理?
Carlos:我始终让 Agent 在独立的 Git Worktree 里工作。如果我看不懂它写的东西,就直接丢弃。我不会急着花很多时间深挖,因为这通常说明它没有沿着我给出的代码骨架实现。如果把这样的代码直接合并进主项目,后面很可能会出问题。
我说的“看不懂”,并不是说我完全没有能力理解。我当然可以花很多时间逐行研究。但如果我打开代码,没办法立刻找到自己原本预期会出现的结构和内容,那就说明方向不对。我宁愿把这一版扔掉,重新做。
Patrick:所以这还不是传统意义上的代码审查,而是先快速扫一眼:我认不认得出这套代码?它是不是按照我预期的结构组织的?
Carlos:对,这是第一步,而且我会在 Agent 完成长时间任务之前就做这一步。通常运行一两个小时,我就会让它先暂停,打开 Worktree 检查当前的结果。如果一看就觉得照这个方向做下去肯定不对,我会直接回滚,关闭这个 Agent,开启一个全新的会话。
然后我会告诉新的 Agent,上一次哪里出了问题,问它怎样才能避免重蹈覆辙。于是我们重新讨论,查清究竟是我搭的代码骨架有问题,还是前面的沟通不够明确,或者多层信息图里的某些想法互相冲突。大多数时候,问题就在于一些设计想法彼此矛盾。我会先把矛盾解决,再让 Agent 重试。通常第二次就能做好,极少需要第三次。
Patrick:也就是说,问题主要还是通过重新沟通来解决?还是说,你也会把这些经验固化成明确的规则,让下一次执行更可预测?
Carlos:两者都会做。我一直在努力把不确定的东西变得更确定。大语言模型本身具有不确定性,同一个问题可能每次给出不同的答案,但我希望尽可能把它推向确定性的输出,让相同要求得到稳定的结果。为此,有时需要写一个严格执行指定操作的脚本,有时需要把代码骨架设计得更具体、约束得更严。我会尽可能用这些办法减少模型行为的不确定性。
别只审查 AI 写了什么,还要看它为什么这么写
Patrick:最后还有一件事我一直在想,就是我们以后该怎么看代码、审查 PR。我见过有人根据代码变更制作不同的可视化。假设你和主 Agent 已经把一整套功能讨论清楚,对需求也很满意,然后几个 Agent 连续工作了 12 个小时,最终交出大量代码。这个时候,评审者该怎么审查?有人把代码库可视化成一座城市,用建筑的分布和高度展示哪里增加了代码、复杂度集中在哪里;也有人用其他界面来呈现代码结构和复杂度。你已经通过确定性的脚本约束架构和编码规范,但既然代码生成越来越便宜,我们是不是也该重新设计查看和审查代码的方式?你想过这个问题吗?
Carlos:想过很多。这正是现在面临的一个问题。我发现,越来越多的 PR 下面连一条评论都没有。我不喜欢这样。我甚至希望大家能为了某一行末尾为什么没加分号,争得差点把椅子扔出去。团队需要通过这样的讨论找到彼此的边界,学会怎样合作。我们在节目一开始就问过:为什么开发者越来越不和彼此说话了?现在写代码的方式、管理 Agent 的方式都在变,但人与人协作的部分却正在慢慢消失。
大家常拿代码量太大当理由。可如果代码太多,为什么不让 Agent 少写一些?把每项功能控制得小一点,增加审查环节,像以前人与人协作时那样正常迭代,审查就容易多了。要是任由 Agent 一次提交一大堆改动,最后连验证都做不到,那就很可怕,因为你也失去了从开发过程中学习的机会。
Patrick:我见过一些完全不同于 Git 的可视化界面。我在想,如果你的 Harness 保存了你和主 Agent 之间的完整交流,那么这些对话是不是也能出现在 PR 里,让评审者看到你是怎样思考和作决定的?我很喜欢这个方向。你的工作方式是前期花大量精力想清楚问题,再利用 Agent 放大执行能力。既然代码生成已经很便宜,作为评审者,我反而更想看你前面做了哪些思考和取舍,而不只是盯着最后生成的代码。如果这些前期要求能可靠地落实到代码中,也许我就不必逐行看代码,而可以换一种方式审查,比如关注复杂度增加在什么地方。
Carlos:对,我也希望有一天能有足够的信心,不必再逐行看代码。我认为这是有可能的。这还涉及测试。单元测试当然有用,但只看测试通过,很容易产生过度的安全感。单元测试之外,还有变异测试(Mutation Testing):故意对代码进行修改,看看测试能不能发现问题。不过它对计算资源的消耗很大,我自己跑过,CPU 的负担非常重。所以我们对测试的思考还得再深入一些。
比如,可以从最初的代码骨架出发,利用静态代码分析,检查其中有多大比例的结构仍然保留;还应该逐个分析提交记录,看看每一次变更是怎样推动方案前进的。这些过程也许都可以可视化。重要的不只是代码最初是什么样、最后变成什么样,还包括它究竟是怎么一步步走到这里的。
我遇到过不少情况:Agent 明明在之前的迭代中犯了错,过了几轮却反过来说:“我之前就是这么做的,不知道你现在在问什么。”这些问题必须从流程中消除。在我看来,和 Agent 一起开发,有点像带一支全部由初级工程师组成的团队。你要是只跟他们说“去做吧”,最后可能会收到代码,有时甚至是很漂亮的代码,但不一定能得到一个完整的应用。
资深工程师应该先和产品负责人(PO)沟通,把产品需求和方向弄清楚。我与 Agent 的对话,以及那张信息关系图,本质上就是在梳理产品和产品愿景。然后我会搭建代码骨架、决定架构,这些也是资深工程师或架构师该做的事。我还会提前考虑应用怎么部署:放在 AWS 上,还是运行在用户本地?如果用 AWS,数据要不要放在 S3?数据库选 PostgreSQL 还是 SQLite?如果用 SQLite,数据库文件应该存放在哪里?应用最终怎样运行、用户怎样与它交互?这些决定不应该全交给 Agent 自己做。事先认真想清楚这些边界和取舍,就是搭建骨架的第一步。
Patrick:听你说完,我越发觉得还有很多东西要学,既兴奋又有些压力。你提到的这些前期决定,我过去参与的项目里,有些早就被别人定好了,还有些我根本没认真想过。我当然有自己的看法,但现在也很想去尝试不同的方法。比起以前,如今能试验的东西多了太多。我想多做一些实验,看看什么适合自己的工作方式,并从中学到东西。
Carlos:那就一起试试吧。我们可以办一场社区活动,找个场地,让大家带着笔记本电脑聚在一起。有人先搭代码骨架,有人不搭,分别试一试,看看结果有什么不同。大家既能探索怎样更有效地使用 Agent,也能借这个机会面对面交流。我觉得这才是第一步。现在我们越来越容易困在各自的小圈子里,要改变这种状态,就得重新聚到一起,动手尝试,一起犯错、一起笑,也一起学习。
Patrick:完全赞同。Carlos,非常感谢你来节目分享。下次我们别再隔三年了,也许一年左右就再聊一次。
Carlos:谢谢你再次邀请我。这次聊得很开心。