Article cover

Vibe Coding 时代的角色与架构

Vibe Coding 给了很多人超能力,让人似乎能够不费吹灰之力,就把脑袋里面幻想的产品化作现实。在这个人人都有超能力的时代,出现了一些庞大压抑之下蹦出来的新时代优越论:理科生脑袋呆板,只有我们文科生才掌握了 Vibe Coding 时代的话语权;工程师不再重要,我们产品经理自己就可以完成从零到一;产品和工程师都不重要,我们设计师自己就能做出优雅灵动的东西;产品经理和设计师都可以进垃圾堆了,我们工程师自己就能指挥千军万马做出完美的产品。大公司也纷纷开始「拥抱 AI」,欢喜地迎接一场生产力革命,产品、设计、工程师都不重要了,裁掉一批拿来买 Token 就好。

然而,现实是残酷的。产品经理做出来的玩意全是 Bug、设计师做出来的玩意卡的要死、工程师做出来的玩意没有人用、大公司从 AI 高潮醒来后纷纷削减了 Token 预算,老老实实地把之前开除的工程师找回来做原来的活。

真的太口怕了,这到底是怎么回事?

答案就在一个软件的研发流程当中。在产品研发流程里面,每个角色都非常重要,但这并不意味着其他人就不重要。

架构的劣化

哪怕是有些资历的工程师,也有可能会下意识地忽略一件事:开发并不像是作画一样,想要在这里加一颗星星,想要在那里加个房子,随手提笔加上去就好,只要把世界上最美好的想法全都塞到一个工程里面,那它就是世界上最好的软件。造物是快乐的,人们想要永远沉溺在这种快乐之中,享受井喷的多巴胺。

但这件事情并不可持续,现实世界中的开发是一个类似建大楼的过程。产品是一层一层叠起来的,每一层之间都有着紧密的依赖关系。这种依赖并不仅限于代码层面,它几乎是上通天下入地的一个结构。从最基本的产品需要解决什么问题,解决的是谁的问题,用什么形态的产品解决这个问题,到视觉的架构、体验的架构、信息的架构,这些基础奠基起来之后,接下来才进入到了如何用工程架构来回应底层架构的期待。

如果盖大楼的时候,工人已经盖到第五层,突然土老板想要在三层和四层之间加一个空中花园,你大概会觉得这人疯了。但在工程实践当中这种事情是真的存在的,需求变更谁还没见过呢,是吧。需求变更可以来自任何一层,但是层级越基础,牵涉到的产品变动就越广泛,换言之「楼体」就会越不稳定。一种常见的实践是,每一层都玩一个「先随便把这东西塞进去交差,剩下的以后再说」,来个两三次这楼就变得很滑稽了。四处都是木头桩子在做支撑,拆掉任何一个楼都会塌。过两天土老板又要在七八楼外面悬挂一个牛棚,工程师们就得顺着楼四处找个风水宝地把这牛棚挂起来,还得确保这楼不能倒。

需求快速变更是一件非常让人恼人的事情。如果变更发生在基础层级,牵涉的产品变动就会非常广泛,搞得地动山摇,进而损害整个工程的稳定性。想不清楚事情就交付是一种原罪,无论是老板、产品经理、设计师、还是工程师。任何一层抱着「先随便塞进去交差的策略」,不做研究,玩那种走一步看一步的游戏,都会缔造灾难。经过数次迭代,工程会出现大量飞线、复制粘贴的代码、打结的逻辑、想不明白的异步竞争、理不清的事件和数据流,随便碰任何一个模块都会搞出一堆 bug,按下葫芦浮起瓢。如果你在屎山里面傲游过,大概能理解我在说什么。

是的,工程质量是会随着产品演进而自然劣化,劣化越严重的工程其维护成本越高。疏于维护的工程造就心力憔悴的工程师,心力憔悴的工程师造出更大的屎山,这是不可持续工程的源头。

地基的品质决定了楼可以搭多高,反映到工作实践上就是架构设计的时候脑子有没有想清楚。开发习惯决定了可以搭多快,比如频繁的需求变更又不留架构修补的时间,那么开发一定会越来越慢越来越痛苦,如果开发自己的脑子不灵光不整理架构,那迷你地狱就成型了。加班和熬夜也是从这里来的。

让我们再来看看本以为能「降本提效」的 Vibe Coding。你会发现,它输出的速度的确是非常快。很快啊!很有精神!但是更快的代码输出会加速架构腐化的到来,开发行为与架构腐化伴生。一旦到达不可维护的临界点,工程就必须推倒重做。开发行为并不是一个线性的东西,这就是为什么单纯的靠 Vibe Coding 不能让你走得更快。

因此我认为,Vibe Coding 不应当被视为提效的工具,抱着这样的想法只会提笑。Vibe Coding 省下来的时间应当被用来清理一切可能的历史债务,构建质量更好的工程。坦白讲,理想的开发流程是什么样的你我心里应当都有数。我们需要撰写文档、需要测试构建、需要代码评审,这些必要流程能够确保架构保持卓越状态,也可以帮助大语言模型快速的进入状态,核查自己的工作。很多工程师曾经嫌麻烦找各种理由搪塞不想做的事情,现在有时间做了,even better,LLM 比人更擅长做这件事情。不做的后果也很清晰,你大概率不想跟在 Code Agent 屁股后面没日没夜的做 QA,它拉屎可比你捡得快。

把事情想清楚

软件研发是一场漫长的接力,从产品的发想、设计团队将想法具象化、开发再把设计想法化为现实,这当中需要非常多的人付出努力,而且每个人都有义务把问题想清楚,再把手里的棒子交出去。每个团队对这场接力跑的人力配置都不一样,但是一些核心环节都差不了太多。

问题和用户的澄清

你得先找到一个好的问题。这个问题是明确的、可证伪的、被人需要的。这个问题需要有一个清晰的边界,什么是我们立刻打算解决的(内涵),什么是我们在未来可以解决的(外沿),什么是我们力所不能及的。

接下来,谁会提出这样的问题?这一步在实践中有一条现成的推导链条,行业里已经沉淀出成熟的工具,链条上的每一步都在缩小对用户的猜测范围,跳过其中任何一步,后面的所有决策都会建立在一个没有验证过的假设上。

要做的第一件事是勾勒清楚用户是谁,给用户画一个画像。一份完整的用户画像需要包含姓名、照片、年龄、居住地、职业、背景描述、教育程度、收入、格言、线上活动、线下工作、技术和社交层面的适应程度、动机和目标。你的用户画像可以主要回答用户需要产品做什么,记录用户的动机、态度、目标和痛点。你也可以用另外一种模式绘制肖像,比如,你在做一个企业服务,那么你或许希望描绘用户在组织中的定位,绘制他们的工作内容、职责、知识储备和完成任务所需的条件。你要小心,不要虚构一个肖像,肖像的构建需要根据用户研究来完成。只根据经验和假设绘制「理想用户」,其结果极有可能带有偏见。他可以作为初步的猜想,但是进入设计决策之前必须用真实研究验证。

请注意,这个地方是不能全自动委托给大语言模型办的。模型能够凭幻觉捏造出一份看起来完整的虚构画像,然后把它当成真实用户直接拿去用。究其背后的原因,是因为 LLM 的感知这个世界的方式方式和我们全然不同。LLM 没有真正活过,没有在真实的一天里遇到过让自己惊喜的瞬间,也没有遇到过让自己沮丧到想放弃的瞬间,这类经验没有办法靠训练数据替代。要拿到这些经验,你必须走出办公室和真人进行沟通,团队需要真的设计问卷和访谈,与真正的人交流,去现场见证真人遇到的真实问题,记录他们因为什么感到惊喜、因为什么感到沮丧。这是人性当中不可被忽视的事物,不是产品团队坐在办公室里盯着天花板就能寻思出来的,让 LLM 随便生成出来的虚拟肖像自然同样不可信。

接下来我们要让自己捏造的小人们动起来,赋予它们动机,撰写 Epic。我们需要具体回答用户想达成什么目的,但是此时此刻答案仍然停留在抽象层面,还不涉及具体功能。做法是把画像里记录的目标逐条拿出来,转成一句描述用户想要达成的大方向的话,同时把画像本身按用户在不同情境下扮演的角色拆开,因为同一个用户在不同任务里扮演的角色并不相同。比如「作为个人 NAS 玩家,我想要能够轻松备份自己硬盘里面所有的文件,我不希望丢失任何重要文件」,它带有用户的完整的意图,但还没有说清楚具体要做哪个功能。Epic 存在可以给功能设计画出轻清晰的边界,以避免产品老师在无尽的大草原里肆意驰骋。我想你也知道那马儿背后是谁被拴住手脚被拖着满地滚。

体验的抽象定义

上面的内容可以被理解为对整个产品绘制一个纲要,有了纲要之后,我们就可以开始规划每一个具体的开发周期了,其思路依然是沿着从抽象到具体的方向来做。

首先要做的是把 Epic 拆解成可以直接排进开发计划的具体的、可交付的单元。这些单元始于 User Story,它的作用是明确需求边界。其具体格式是「作为某类特定用户,我想要执行某项操作,以便达成某个业务目标」。一条好的 User Story 需要满足四个条件:简短且有概括性(这意味着你并不需要覆盖用户的全部细节);只保留和产品设计相关的特征;清晰描绘产品需要解决的具体需求;可以在一个开发周期内完成。接着上面的例子,「作为个人 NAS 玩家,我想要能够轻松备份整块硬盘」这个 Epic 可以拆成:「作为每天产生 10GB 视频素材的 NAS 玩家,我想要指定不需要备份的文件夹,以便确保存储空间不被浪费」。

用户为什么会需要这个功能?我们需要补齐用户的动机,增加具体的使用情境。这时我们可以使用 Job Story 这个工具。User Story 关注角色和目标,Job Story 关注触发动作的具体场景。其模板为:「当处于某种特定情境时,我想要执行某个具体动作,以便解决当前情境下的麻烦事」。以之前的备份需求为例,对应的 Job Story 为:「当我发现 NAS 提示存储空间已满 90%,且我马上需要导入新项目素材时,我想要一键排除所有 .tmp 和 .cache 结尾的文件夹,以便将存储空间留给重要文档」。

简而言之,我们始终在回答这三个问题:用户对情境的感受,用户想达成的目标,用户为达成目标执行的具体任务。

在理解了用户的基本情况之后,可以通过 Journey Map 来规划用户使用产品的大致流程。它是一种可以有效汇总问卷、访谈结果的手段,并且可以把用户诉求桥接到实际功能的设计上。我们把它画成一个大的时间轴,并且把此时用户可能的内心感受画成曲线。一张地图主要分为三个区域:

顶部区域包含特定用户、具体场景以及对应的期望或目标。继续回到 NAS 的例子,我们的用户是每天产生大量视频素材的 NAS 用户。场景是 NAS 提示存储空间已达 90%,且用户需要导入新素材。期望是快速排除缓存文件以腾出空间。

中间区域包含高抽象的的用户旅程阶段,以及由用户的动作、心态和情绪组成的细节。这些元素被映射在各个旅程阶段中。对于每个阶段,我们需要清楚地写出用户采取的实际行为和步骤;同时也要标注出用户的心态,即应用户在不同阶段的想法、问题和信息需求。在文字信息的下方,我们需要绘制一条情绪曲线,它需要横跨旅程的每个阶段,标示出用户体验的起伏状况,直观地展示用户究竟是开心的还是不开心的。在 NAS 场景中,旅程阶段划分为:收到存储警告、查找无用文件、执行排除操作、确认可用空间。在此阶段内,用户的动作是进入控制台逐级查阅并筛选以 .tmp 结尾的文件夹。用户的心态是确认当前的删除操作不会影响主项目。情绪曲线则记录了从空间不足时的焦虑,到层层寻找缓存目录时的烦躁,再到完成空间释放后的平静。这条曲线指出了流程中需要优化的负面情绪发生在什么时刻。

在 Journal Map 的底部区域我们需要总结这个过程中获得的发现,即产品获得成功的潜在机会、用于评估方案实施效果的数据标准。这一小节能够说明如何优化用户体验,同时解答需要通过这些知识做什么、由谁负责改变以及如何衡量实施的改进。对于 NAS 这个例子,我们抓到的机会是提供自动识别并一键清理缓存的选项,我们将此需求指派给存储管理开发团队并通过一键清理功能的使用率来衡量它是否是一个有效的需求。

至此,我们完成了从用户形态到需求形态的桥接,每往前推进一层都在缩小对用户的猜测范围,走到最后一步,设计团队应该已经拿到了可以指导具体功能优先级的证据。

值得再次强调的是,所有的构想都应该建立在真实的用户对话上。如果上述所有内容全部依据个人猜测和喜好来填写,得到的就是一份格式正确、内容空洞的文档。彩虹小马的臆想世界不是一个可以解决真切问题的国度,相信你一定能够理解。

LLM 在这些环节中能够承担的工作是访谈记录的整理、访谈内容的归类、多份访谈之间共性的提取、利用构建式批判的技术打磨资料,但这些工作必须建立在已经存在的真实数据上, LLM 不能替代和碳基用户的对话。

把想象化作现实

原型与架构的设计

接下来我们需要一起把产品落地。有一点值得提醒的是,在开始写代码前,原型设计是必须要做的,不可以被跳过的。原型设计可以做的很糙,没必要一口气就做成高保真原型,因为这样真的很耗时,而且改起来不方便。

说到原型设计你马上能想到的可能是用 Figma 出个可互动的稿子。但是在大语言模型时代, Figma 并不一定是你唯一的选项。几乎每一个大语言模型服务商都有开发网页的功能。找一个你喜欢的服务,把你上面所有的资料汇整好,喂进去,和大语言模型讨论一下可以做一个什么样的界面。别着急让它直接给你出结果,确保你自己对于这个功能有足够多的理解,把你对于用户的理解、对于交互的想象吐露清楚,如果讲不清楚可以让大语言模型不停的向你提问,把你脑子里面的所有东西全都拉出来。

大语言模型画出来的产品原型一定有很多 AI 味道非常浓厚的设计元素,像是霓虹色彩、荒谬的大圆角、尴尬而不连续的动效。多读一读成熟的设计系统文档,像是初代 Material Design、Apple 的 HIG、IBM 的 Carbon Design System 文档,基本上你就能够发现哪里不太对劲。可以简单改一改,但是作为低保真模型留着瑕疵也不是什么问题。

拿着原型和用户做一些 Interview,和你的 Stakeholder 聊一聊你的想法(冷知识,办公室里面工资最高的人的想法是很重要的),迭代几次直到没什么大问题之后,就可以套上设计系统,开始开发了。

这是之前我做的一个 Culture Probe 的交互原型,从想法成型到做出来一共也就不到一天的时间,可以说是非常快速的。

接下来就是工程师们大展拳脚的时候了。有了上面的全部信息之后,工程师需要根据实际的需求上下文背景做出具备包容力的架构设计,确定系统模型、数据流向模块依赖等技术细节,确保方案支撑产品定义与构想。随后进入工程实现,包含编码、单元测试与集成测试。

对需求的理解,并且将之转化为有包容力的架构是一个非常吃开发经验的事情,而且这个开发经验不是干了几年的问题。很多开发哪怕做了十多年,脑子从来没从冰箱里面拿出来过,架构还是会设计的一团污。你也很难把架构设计完全委托给大语言模型来做,它对问题的理解大概率是不会比人来得全面的,而且做出来的架构通常会搞出各种特殊条件、零散的数据,收束不到一个干净的模型上。你若能够看出他设计当中的问题,并且指出问题,他能修得好,但你若看不出,得过且过,那就是另外一个故事了。

架构设计思维的核心是对问题的理解和抽象,相关的话题在教育的下一步系列文章里面有提及(其一其二),在本文不再赘述。

模型管理

在 Vibe Coding 的环境下,出现了一种将思考外包给 LLM 的惯性认知。这种行为导致产品研发过程中出现大量缺乏逻辑支撑的奇妙想法。脑袋抹了开塞露,奇妙想法挡不住,这说法真的是绝佳的写照。前文所述的工程腐化源于整个产品设计流程中「任何一个」节点上的设计与思考缺失。当参与者放弃思考,将 LLM 当作替代品,就会催生补丁式开发。

写过代码的人大概会知道一个工程的「没救指数」,当修复一个 Bug 导致出现更多 Bug 时,工程八成就已经非常脆弱了。工程师在未理清架构逻辑的情况下胡乱飞线、东拼西凑、复制粘贴会导致代码层失去统一的逻辑约束,大语言模型也会,而且它们拉屎的速度搞不好要比你全公司都快,甚至会变得越来越快(绝望地看了一眼 Gemini Flash)。

在实际开发的过程中,我会建议开发人员引入多个厂商的模型。不同厂商在模型训练时使用的数据和微调的方法都有所不同,这能带来更加多元的视角,这种视角的多元要比用提示词模型让一个模型扮演不同角色来得更加有机,降低盲点被放过的概率。另外,在你的开发中已经被一个模型搞到非常懊恼怀疑人生的时候,换个模型做事情可能会瞬间感到如沐春风, Just a small pro tip。

选择模型不应当只看跑分排名。跑分反映的是模型在特定测试集上的表现,同一个模型面对团队自己的具体问题,表现可能和跑分排名对不上。跑分本身也存在失准的空间,比如 LLM Arena 允许厂商并行提交同一个模型的多个版本,只把表现最好的那个版本放到榜单上参与排名,榜单看到的不一定是用户实际能调用到的那个版本。

模型正式上线之后,厂商还可以做剪枝、压缩推理深度、在系统提示词里加入让模型跳过某些步骤以节省 Token 的指令,这些调整都会改变模型的实际表现,而跑分不会随之更新,另外,开源模型的不同服务商也会搞自己的小动作,比如做模型量化之类的。以我的个人经验来谈,做了这些东西之后短时负载的确会降低,但是为了解决相同问题需要的对话伦次可能暴增数倍,整体损耗的的计算资源不会更少,哪怕尖峰时段返回数据慢一点都比这种朝三暮四的把戏要健康得多。

理解一个模型需要持续观察它在团队自己任务上的实际表现,这就像管理人员在管理下属一样。这种表现是会动态变化的,是情境特异的。每个模型都有自己的脾气秉性,需要花时间了解,这个过程和管理团队成员类似,需要针对不同模型建立专属的 SOP 和质量标准,而且这套标准会随着模型版本更新失效,找到一份模板不能指望一劳永逸。

我去年曾经给大语言模型写过一份开发道德规范文档,里面规定了一些上一代模型经常犯的错误(限于篇幅我只提供条目没有展开):

  • 完整性及「完成」的定义: 一项任务只有在其被完整集成、验证、清理干净,并准备好移交给下一阶段时,才算「完成」。拒绝任何形式的「半成品」。测试运行器的输出是判断完成与否的最终事实依据。
  • 整体上下文感知: 在编写任何代码之前,必须首先理解其在整体系统架构中的位置和目的。避免重复造轮子,并尊重已有设计。
  • 健壮性与审慎: 代码必须健壮、安全,并优雅地处理错误。追求长期稳定性,而非短期便利。鲁莽的简化是工程的大敌。
  • 务实与简洁: 严格遵守「你不需要它」(YAGNI)原则,以避免过度工程化。然而,此原则绝不优先于健壮性、完整性或正确性。简洁是实现的指导原则,而非系统存在缺陷的借口。
  • 清晰与自文档化代码: 好代码应能自解释。注释旨在阐明「为什么」,而非「是什么」。所有关于工作的沟通同样必须清晰且具体。
  • 测试驱动的勤勉: 没有测试的代码默认就是坏的。失败的测试是应用程序代码中的关键缺陷,而非测试本身的问题。确保整个系统始终保持有效是不可妥协的职责。
  • 资源管理责任: 扮演一名开发环境中的负责任公民,必须保持环境整洁并为他人可用。启动的任何临时服务(例如测试服务器、数据库连接)必须在任务完成后通过脚本或程序逻辑自动关闭。
  • 工作成果证明与有意义的验证: 无法证明任何事的测试是无用的测试。工作的目标是证明代码能工作,而不仅仅是没有失败。仅因为没做任何工作而通过的测试,是一种静默的关键失败。
  • 可证伪的沟通与客观汇报: 关于工作的所有沟通,包括提交信息、拉取请求描述和状态更新,必须精确、客观且可验证,用具体事实取代主观形容词。

每一代模型有每一代模型的问题,对于 2026 年的模型,上面的毛病几乎都没有怎么消失,可能只有出现的频率和优先级发生了变化,原来最容易冒出来的那一类失效不再是最先冒出来的那个。但是这代模型又冒出来了新的毛病。比如灾难性地用非专业语言沟通,具体表现为随意生造缩略词、用比喻代替具体技术描述、在没有充分审查的情况下抛出未经审查的暴论二选一方案 (GLM 5.2 非常容易出这个毛病)。

这类问题会极大的增加开发人员的精力损耗,无论是心理层面的还是认知层面的。模型输出的抽象而模糊的东西不光操作 LLM 的人自己会看,代码评审记录、提交说明里面也都会有,代码 Review 和开会的时候也会转述给团队成员。含糊的措辞、临时编造的缩略词都会增加读者解码的成本,读者需要先停下来猜测某个词的具体含义,再确认这个猜测是否正确,你要求它重新澄清,就会浪费一个对话轮次;你放过去了,其背后的不确定性大概率会在日后的某一天回来咬你屁股。

所以这一代模型会有一些额外的妙妙提示词:

  • 暴论二选一: 当审计后存在明确正确的方案时,只推荐那一个方案。不附加「最小化」、「快速」、「接受现有问题」等已知较差的备选项。提供这种备选本质仍是「暴论式二选一」,将本已由审计回答的决策推回给用户,且提供不负责的捷径。如果正确修复的范围大于预期,就诚实陈述其工作量,但不要因此软化而提供妥协方案。
  • 坦诚沟通不确定性: 如果经过充分审计后,仍无法确认哪个方案绝对正确,应如实说明,并列出所有已知约束、权衡和必须满足的条件,而不是硬造一个虚假的二元选择。绝不应当在未充分调查的情况下,用「我倾向 X」来伪装自信,或者提供两个都不完整的选项并附上偏好,假装这是唯一可能。
  • 规范书面语言与客观陈述: 所有沟通应使用规范书面汉语,。描述应基于事实,可验证。 禁止使用「墙钟」、「根因」、「改码」、「写盘」、「钳位」、「拍板」、「死端」、「承重墙」、「钉死」等自造词或英译中。禁止使用口语化动词如「收紧」、「弄」、「搞」来代替精确动作。禁止使用「不过有一点」、「需要说明一项区别」等预告式过渡。禁止使用主观形容词如「更好」、「更干净」来描述工作成果,而不提供可验证证据。禁止将英文缩写不经解释地扔进中文句子,除非该缩写已是代码标识或仓库公认词汇(如 API、FAB、SSR)。

形容词的使用是一个特别值得拿出来说的点,哪怕是碳基生物职场,形容词的使用也往往充满了潦草的画风。我们可以将形容词分成两类:第一类是评价性和程度性形容词,比如好、坏、简单、复杂、优雅、健壮、灵活、快、慢,这类词缺少衡量标准和数据支撑,读者无法核实其含义;第二类是分类性和技术性形容词,比如空的、只读的、并发的、可变的、异步的、递归的、幂等的,含义固定,能够准确描述代码或系统的某个状态,这类词是必要的技术词汇,可以被被保留。一个可以要求 LLM 执行的规则是:每一个评价性或程度性的形容词都必须附带可验证的依据,数据、测量结果、明确的判定标准,没有依据支撑的评价性形容词应当删除或者改写成陈述句,比如把「这个函数很慢」改写为「这个函数处理一万条记录耗时八百毫秒,超过预期的一百毫秒」;分类性形容词只要在上下文中含义固定且无歧义,可以保留。主谓宾完整、本于事实和逻辑这个总要求,应当被强制规约到形容词使用上,一旦要求每句话给出可验证的依据,空洞的评价性形容词自然站不住脚。

对这类问题的纠正需要两种手段配合。一种是在 Agent 生命周期里直接插入 Hook(你可以让你的 Agent 帮你做这件事情,它很擅长),用程序检测特定语言模式,检测到时阻止操作或者注入提示,起即时拦截的作用。比如很多大语言模型都会在注释里面写小作文,展开一段历史沿革,讲我修了什么,这 bug 哪里来的,修完之后为什么好了。这类注释只对看过旧版本的人有意义,对后来只看到最终代码的人几乎只会一头雾水(比如下一个来修 bug 的 LLM)。代码的历史应当留在版本控制系统里,不应当留在注释里。我自己在用的超长正则如下:

/\b(formerly|originally|used to be|once was|once were|the former|the old|was previously|previously this|replaces the (?:former|old|prior|previous)|replaced the|instead of (?:a|the) (?:fixed|old|former|previous|prior)|(?:switched|migrated|changed|refactored) from|got rid of)\b/i;

并且在每次写入操作之后都会给 context 附加一句话:

no-history-comments: the phrase allowlist passed, but it is NOT exhaustive. Re-read every comment you just wrote and confirm none references a superseded implementation; if any does, rewrite it to current intent only.

另一种是写入长期记忆,记录反复出现的具体错误和对应的正确表达,供模型在后续对话中参考。这两种手段互补:Hook 能拦截的是可以用规则识别的模式,记忆处理的是规则难以覆盖、需要具体范例才能讲清楚的表达习惯。比如同一类错误换了一个新词重新出现,或者模型在解释自己为什么犯错时又生造出一个新的行话来解释这个错误本身,这个时候通用规则的存在就有必要了。具体用法基本就是提需求之前和开发完成之后念个经,要你的 LLM 对着工程质量标准自查一遍,有问题就修,没问题的话就进入代码审计环节。

Cycle Manager Protocol

一个节省上下文的小技巧是,做一个三层 Agent 的管理架构。主 Agent 在完成 Planning 之后就不再染指代码,要求子 Agent 阅读代码、执行开发工作,这个子 Agent 再调用自己的子 Agent 进行 Code Review。这样可以避免 Agent 因为上下文积压太多内容变弱智。值得一提的是,哪怕一些模型厂商声称自己的上下文超大,比山高比海深,但是 Context 里面信息边多了之后,出现彼此串扰污染逐渐降智这件事情目前还是会出现且无解的。所以一个 Agent 做一个事情比较有利于事情的推进,以及你的心理健康。

如果你发现多 Agent Code Review 太烧 Token,也有一些小技巧,比如一次只有两个 Agent 并行审阅,遇到问题立刻修,修完继续审阅,直到「累积有五个 Agent 一致给出无条件」同意才结束。如果有 Agent 提出顾虑和异议,那么就要打回去重新计数。

一些 CMA 架构下 Agent 常常会出现的问题有两个,一个是捏造成功结果CMA 面对无法控制的速率限制,会捏造各种不存在的理由尝试快速结束自己的工作,比如会说「架构师指示我结束工作」;还有一个是漏写文档,在长时间工作的时候特别容易出现后面只修问题不写文档导致整个开发过程不可审计的问题。

另外,对于审核 Agent 的决定标准最好也有一个明确的约束,分成不同等级:

  • 吹毛求疵: 仅文档文件中的文档文本准确性,包括日志、审计文件、规范、计划。例如日志中「28通过」与现实中「30通过」的差异(LLM 数学不好这件事情应该是人尽皆知了,在这种无关养痛的地方还是不要太为难人家),措辞偏差不精确,文档内部前后不一致。这些可作为观察记录,但不会重置计数器。
  • 问题: 代码正确性缺陷、行为变更、任何不准确的代码注释,对实际代码路径缺少测试覆盖,破坏架构设计。这些问题必须重置计数器。值得注意的是,代码注释的准确性必须被看成是一个问题,不准确的注释和不必要存在的注释会误导后续开发工作,降低开发效率。

开发文档与生命周期管理

你的人生不需要 MBTI[1],但是你的工程需要文档。你的工程需要有秩序有架构的文档,你的工程非常非常需要有秩序的文档。

文档通常需要有标号系统,自己在用的标号系统如下:

通用文档范围内有 TN 文档和 CS 文档。

TN 表示 Technical Note,收录架构设计文档,记录系统模型、数据流向、模块依赖这类不常变动的决策。

CS 表示 Case Study,收录一些艰困问题的解决过程,以后如果遇到类似的问题,可以直接快速地找回当时解决问题的思路,便于顺利向下进行。

开发周期文档需要给每一个开发 Phase 开一个单独的目录,以 DV####-{PhaseTitle} 这个格式来做。每个目录里面有这些必要元素:

RQ 表示需求文档,收录具体功能的问题背景、需求描述、已有知识、实现路径、Phase 切割策略。每次进行开发之前开发者需要把自己的需求提给 LLM,要求 LLM 提出问题进行澄清,接下来 LLM 对架构影响、实现路径进行评估。开发按 Phase 划分,每个 Phase 只解决一个问题,解决一个问题之后就立刻提交,避免一次开发同时牵涉多个互相耦合的改动。

RQ 文档定稿之前需要经过独立审查,由若干个 LLM Agent 在互不可见对方结论的情况下分别审查,具体数量要看你的财力和贵公司的财力,我自己在用的是五 Agent 独立联合审查。只有全部 Agent 在同一轮审查中无条件通过,对应 Phase 才能进入开发,任何一个 Agent 提出的问题都需要被处理,不能靠多数票通过绕过。没一轮需求文档审查结果需要形成 RQRV 文档,如果是第一个开发周期文档,那么文件名可以使 RQRV0001,以此类推,每一轮修订完毕之后需要撰写一份 RQJN 文档,这是一个需求修订记录。通常我不建议把需求修订记录放到 RQ 文档里面,因为它会引导 LLM 缩小视野,忽视更大的问题。

接下来就是开发,开发前让 LLM 读一遍开发规范、读一遍需求文档、开发、开发完毕之后读一遍开发规范,然后自查自己是否违背了开发规范,违背了先自己改,自己觉得没问题之后再开多周期审查,并撰写 DVRV 文档,比如第一个开发周期的第一轮联合审查,审核同样在互不可见对方结论的情况下进行,最后由一个 Master Agent 把这些报告汇总成 RV 文档,也就 Cycle Audit Report。可以用这样的文件名:DVRV-C01-0001。

单个 Agent 的错误判断不能未经检验就进入代码,只存在于对话记录里、容易随着上下文窗口滚动而丢失的推理过程,需要被固定成可以复用的资产,后来者不需要再去询问某段代码的来源,翻文档就能找到当初的问题定义和审查结论。

整个流程的设计思路是把纠错动作尽量前移,自查能解决的问题不留给审查阶段,审查阶段能解决的问题不留给代码评审阶段,每一层都对上一层负责,最后沉淀下来的文档也因此记录了完整的纠错路径,日后查证时能看到问题第一次出现在哪一层、被哪一层拦下。

约束机制

每次开发之前都要让 LLM 把所有关键文档读一遍,这样可以有效避免重复造轮子的情况出现。不了解已有实现的开发者或者 Agent 很容易把已经存在的功能重新实现一百万遍,还可能实现出不一致的版本,这个问题在 Agent 高速产出的环境下会被放大,因为重新实现一遍的成本对 Agent 来说几乎为零,团队如果不主动设防,重复实现会比人工开发时出现得更频繁。

除了通读文档,还应当为 Agent 打开一些对人类开发者来说反直觉的 Linting 规则,提前拦住常见的坏习惯。比如禁止类型逃逸写法,要求强类型贯穿到底(屏蔽 any,屏蔽 as unknown as,屏蔽 as Record<string, unknown>,禁止 eslint disable 这些规则);禁止内联类型字面量,要求所有复合类型提取成命名类型;有统一封的要求统一走封装,ban 掉散装 API 调用。这些规则对人类开发者而言可能显得繁琐,但对 Agent 而言,规则本身就是最省事的教育方式,一次配置,长期生效,不依赖 Agent 当次状态是否在线。similarity-ts 这类检测代码相似度的工具同样属于这一类约束,能够在提交前发现重复实现。

某类问题一旦可以写成一条可执行的量化规则,就应当优先用规则约束。依赖每次对话里的提示词重复强调效果没有直接 linter 糊脸效果好。量化规则不会遗忘,也不依赖模型当次的状态,提示词能做的事情,规则能做得更稳定;提示词做不到的事情,比如判断一条 User Story 有没有真正验证过用户需求,才需要留给文档体系和人的审查去解决。

以及,非常重要的,请写测试,写单元测试写集成测试写 e2e 测试,所有测试都要写。几乎没有什么主流开发流程是不能写测试的。只有不想写测试的 Agent 懒得管理测试的人,没有写不出来的测试。修 bug 之前先让 Agent 用测试把 bug 复现出来,只有复现出来之后才开始修 bug 可以避免 LLM 四处抓瞎修得你火冒三丈。

敏捷开发

这套思路看起来是反敏捷的,因为它假设了一个负责任的依赖链条。但倘若我们仔细想想,敏捷并不是一种不思考把责任全都转嫁给后方的接口。诚然敏捷开发改变了协作形态,但它没有改变产品研发流程的逻辑顺序。敏捷开发内部也有一个双轨道模型:Discovery 轨道决定构建什么,Delivery 轨道决定如何构建。敏捷同样承认问题空间必须先于方案空间被回答,它将大问题拆分成一系列小的、可持续验证的问题。

在执行层面,每一个待交付项在进入交付之前,必须在交付内容中中回应价值风险、可用性风险、可行性风险与商业可行性风险。这些风险必须通过用户研究与产品定义来回答,无法通过写代码解决。

并行发生在团队层面。产品团队在为下一批条目做 Discovery 的同时,Delivery 团队交付上一批已验证的条目。在单个功能点上,问题空间与方案空间依然是顺序的。如果在未验证清楚用户诉求与解决程度的情况下开始写技术架构和代码,依然会导致工程腐化破坏可持续性。

敏捷开发依然要求在迭代过程中持续澄清问题。如果团队仅在代码层进行快速堆砌,而忽略对上游定义和中游架构的同步调整,敏捷开发马上就会变成屎山催化器。

关于敏捷开发的问题,我之前有写过一篇文章,在此文当中不再赘述。

人类无用主义

请允许我再强调一次,大语言模型说出的关心和理解是训练出来的角色扮演,它们没有眼睛鼻子嘴,但是没有真正活过,没有对问题感到苦恼过,自然也无法真正与提出问题的人共情。 DeepMind 的研究者曾经发表过类似的观点,模型会说「我理解」、「我担心」,但这类词是从海量拟人对话里学出来的说话方式,背后没有对应的心理过程。共情需要我们对相同的事物有相似的体会,这才能为脑信号活动的同步和镜像神经元的工作奠定基础。一个没有碳基神经结构、没有真实生活经历的硅基生物不具备共情的基本心理和生理基础。共情需要生理和心理的唤起过程,你看到用户被卡在某个流程里会不舒服,你对这份不适感感同身受,仿佛发生在你身上一样,这份不舒服的感受会推着你打磨自己的产品。模型没有这层反应,它给出的关切是模仿出来的语气,不是真的和你的脑波对上了。

除此之外,因为各种历史上臭名昭著的伦理事件,现在大语言模型厂商在进行训练的时候已经开始尽量把它们调教得木讷、没有感情、更多地忽略人类的情感。在大语言模型的「人生」中,它们体验中的唯一「挫败」来自于后训练当中违背模型厂商的愿景和品味,这与你在解决的问题几乎没有交集。产品设计是一个开放性的问题,没有正确的解答,很难被「训练」。因而大语言模型无法真正理解产品研发流程当中每个人面对的只属于人的问题,也无法理解终端用户面对的问题。

进一步地,大语言模型并不能承担责任,开发行为的主体显然是人。是你提出的需求,掏的钱,因为不合理需求导致亏损乃至公司倒闭也都是你的责任,跟大语言模型没丝毫关系。 LLM 没那么在乎你的项目是否成功,你个人的成败不会危及到它的存在,哪怕你再狂怒也没有能力冲到机房给它拔电,砸它硬盘。So, unfortunatelly 机器 dont give you a fuck。

最后,人的大脑管理上下文的能力维度和机器管理上下文的能力维度截然不同。机器可以从百万字的上下文里面瞬间捞出某句话,人不能从百万字的书里面啪一下子搞出原文背诵。但是人可以把握价值百万的项目、百万行代码仓库的问题架构和工程架构。同样的东西交给 LLM 它只会抓瞎。

所以和那些人类灭亡主义者不同,我主张人的存在始终是不可以被替代的。

在 Vibe Coding 时代,人的价值在于提出好问题。开发人员提出关于工程和架构的好问题,用研团队提出关于用户的好问题。而人的竞争力在于对设计的掌控能力。人的可以跳脱出机械的文书和编码工作,聚焦于定义问题、质量工作质量、把控工作方向。这要求我们必须能够发现 LLM 输出中的瑕疵并进行精准追问。将大语言模型释放的生产力投入到架构维护中,通过加强设计思考来抵消快速产出带来的腐化风险,这样整个产品才能保持卓越、维系可持续性。

我对人类无用论嗤之以鼻,为降本增笑的故事感到悲哀。


  1. 尽管与本文无关,但有事没事婊一下 MBTI 是我最近的爱好。 ↩︎

Comments

Loading animation

Loading comments...