Vibe Coding 一年实践后的冷思考
前言
最近一年我在 Google/Anthropic/OpenAI 三家烧了超过 1 万美金的 token 账单。所以本文内容基于 opus4.6、codex-5.3-xhigh、gemini3-pro 等最强模型不限量使用所表现出来的编码能力进行评价。
现象:Agent 的信任危机
就好像保健品销售拿着他的《大数据量子 AI 生物磁场治疗仪》,忽悠我说这台原价 20 万、现在活动价 8 万 8 的仪器,可以彻底根治我的颈椎病腰椎病高血压糖尿病,还能逆转我的动脉血管粥样硬化、冠心病、阳痿早泄等等
Agent 编程现在就是这么个状态。
Agent 给我一堆 emoji 庆祝刚才生成的七八万行屎山通过了全部测试用例,告诉我可以替换生产环境了。你信吗?
假设你是一位项目 leader,你最靠谱的组员同事,交给他的开发任务 80% 可以在预期时间内高质量交付。这位同事拿头给你保证下周就可以上线,那么你大概率能信任他最迟下下周也搞定了。但是 agent 给你保证现在质量和完成度可以上线生产了,你信吗?
此时此刻,无数知识星球、自媒体、AI 导师教父们正在到处收割韭菜的学费。大意基本上都是教你如何 prompt(tool/skill 换汤不换药),然后让你多开 agent 并行干活。
一个真实案例
Agent 的盲目自信不仅会误导使用者,也会误导 agent 自己。
我曾给 agent 这个任务:为当前 Kotlin 项目集成 GCP Transcoding 服务。我给了 agent 该产品的页面和文档作为参考,让它开始 plan。Agent 做出了如下计划:
- 通读文档后,发现该服务仅提供了 Java SDK,而当前项目使用的是 JVM 上的其他语言,并非原生支持
- 根据 RESTful 文档指示,结合文档定义字段,使用 ktor-client 进行手动接入
- 编写代码并执行测试
你发现这份计划中存在的问题了吗?
事实上,如果你曾经「古法手工编程」做过此类工作,你会发现手动实现 RESTful 远没有想象中那么简单。哪怕仅实现 Transcoding 服务的基础能力,也涉及到 5-10 个 endpoint 调用。每个 endpoint 的输入输出参数又有几十甚至上百个字段嵌套定义,agent 在应对这类长上下文任务时会频繁犯错。
而如果 agent 选择对 Java SDK(Google 也是从 protobuf 生成出来的)进行简单包装隔离,大概半天到一天就可以让这个功能稳定上线。
若是让 agent 按照 RESTful 文档手动实现,agent 可能会陷入 debug 泥潭——因为当 AI 幻觉导致写错了可选字段的字段名(大小写、驼峰、下划线),程序不会立即报错。你需要多久才能发现它实现错了?等上线生产后客户投诉吗?
为什么我们无法信任 agent? 经过一年的实践,我认为问题的根源在于:我们缺乏有效的验证手段。
原因:验证手段的全面失效
Code Review 失效
常见观点:某种意义上来说 AI 并没有取代程序员,只不过是一个新的高级工具罢了。你作为生产代码的人,还是得弄明白要干啥,合入的代码就得弄明白。
但我认为,这个在实际项目里很难做到。
像我们之前内部 review 的时候,大部分时候 review 的是 code style,作者讲一下设计思路,我们也就是大概一听就过了。以前这套方法是有效的:
- 代码风格差的 PR,设计思路也一团糟,性能也差,也没什么可扩展性
- 代码风格好的 PR,设计思路都挺清晰,性能考虑也周到,就算有性能瓶颈也容易改,最后扩展性也不错
但是这个相关性在 agent 编码时代不存在,甚至相反。
Agent 一分钟就能生成出来注释齐全、风格优秀的——屎山代码。反正我肉眼看过去的时候,经常会被这第一层假象蒙蔽,放松警惕。主要是这个屎山有点难在 review 阶段发现,经常是上线后出了问题,回头细查的时候才发现是「巧克力味的屎」。
你信我,opus、codex-xhigh 这些你们舍不得用的模型,我开 thinking+max 模式站起来蹬,一样有这个问题。
测试失效
更不用说测试了。现在的 test cases 也是 AI vibe 出来的,agent 又当裁判又当运动员,它说什么就是什么。蒙我坑我也不是一次两次了。写了几千行 getter/setter 的 test case,最后测试全绿告诉我可以上生产环境发布了。
就像前面 GCP Transcoding 的例子,agent 写错了可选字段的字段名,测试照样能过,因为测试也是它自己写的,错的一致就是「对」的。
与传统行业的对比
说到这里,有人可能会问:其他行业被机械、智能赋能后,难道就没有这个问题吗?
让我用 CNC 机床打个比方:
CNC 机床精度比我高,但机床产出工件后,我们可以对工件进行客观的物理测量——用卡尺量一下,公差是不是在 ±0.01mm 以内,一目了然。即便我没有手搓出这个精度的能力,但我依然有评价 CNC 机床和工件质量的能力。
这就是传统制造业被机械赋能后的状态:机器精度高,质量统一且稳定,而人依然能评价机器的产出。
那么软件开发行业被 Agent 变革后,理想状态应该是什么样的?Agent 交付的代码确实覆盖了需求,具备基本的安全防护,且更容易长期维护(哪怕仅考虑 agent 自己维护,不考虑对人类的可读性),性能更高,资源占用更少。
但程序不仅需要完成眼下需求文档中的功能,还需要考虑到基本的安全防护。一个功能完成但安全漏洞百出的项目代码,同样是不合格的。
而目前我们还无法评价 agent 是否达到了这个状态。单就「功能实现」这一基础要求,agent 还不能脱离人的引导和测试验证——更别提安全性、可维护性、性能这些更高阶的指标了。
而且程序不是物理工件,不能用物理手段去测量。你没法拿卡尺量一下这段代码的「质量公差」是多少。
所以没法用衡量物理工件的标准去衡量程序,反而应该像衡量 CNC 机床本身一样——而一次生产(all tests passed)远不能衡量机床质量,更不能衡量程序质量。
CNC 机床加工塑料、铝合金小件精度高,不代表加工钛合金、不锈钢精度也能达标。后者更考验整体刚性,以及工件质量大了以后热胀冷缩对程序进刀补偿的要求。
同理,vibe coding 出来的代码,本地点两下鼠标测试通过了,上线也是极大概率会直接炸掉。
传统行业:机器精度高、质量稳定,人能评价。软件行业:Agent 产出快、覆盖广,但人还没法可靠地评价。这就是问题所在。
方案:让编译器替你把关
既然人工评价(Code Review)和自动测试都靠不住,我们需要另一种评价手段——一种不依赖 agent 自己判断的、客观可验证的评价手段。
我的观点和主流 AI 编码观点相反:
Agent 编程时代,更需要强类型,更需要严格可验证的语言,而不是放任 agent 去写 python/js/go,还有 anyscript。
为什么?
AI 堆屎山这么快,别说生成个几万行了,就是生成超过 100 行我都已经懒得逐行去细读了。但是读类型签名、pre/post-condition 明显要快于通读逻辑代码。而这些东西只有 Rust/Scala/Haskell 甚至 formal method 能提供。
我在 agent 编码前就一直用这种风格写自己的代码,主要是代码量大了以后,编译器检查比我肉眼检查更靠谱。现在 agent 编码流行起来了,我发现让 agent 遵循我的这个要求,更能控制产出代码质量——当然也只能说一定程度上,起码比什么都不做好。
回到 GCP Transcoding 的例子:如果 agent 用的是强类型语言,字段名写错了至少还能在编译期被类型系统拦住一部分。但 RESTful + 弱类型的组合,错了就是悄无声息地错,等你发现的时候已经晚了。
实践效果
y1s1,该夸的还是要夸。现在的最新最强模型,过编译问题不大了,除非你比我还执着于类型体操。
放手让 agent 做工程还是拉垮的一批,但过编译已经问题不大了。Pure-FP Scala、tagless final,opus 4.5 和 codex-xhigh 遵循得挺不错了,过编译也是自动的。函数式类型体操的编译错误基本上都是几十上百行的类型天书,agent 读懂并修复这些编译错误,在我写这篇文章时已经不再是困难。
局限性
当然,这个方案也有局限。
实际上现在的 formal method 工具链和生态还是很贫瘠,基本上只支持一门语言很有限很小的一个子集。有些工程上常用的语法/模式在 FM 那边都是 unsound,或者尚未证明。更不用说动不动就陷入死循环/无解证明了——稍不注意,z3 求解器要在比宇宙空间还大的可能性里搜索,到宇宙毁灭那一天都证明不出来。
强类型能解决一部分问题,但不是全部。
更深的困境:Plan 与 Execute
即使有了强类型作为评价手段,还有一个更深层的问题:agent 对计划的理解和执行。
GCP Transcoding 的例子其实已经暴露了这个问题:agent 选择手动实现 RESTful 而不是包装 Java SDK,这不是代码写错了,而是路线选错了。编译器能告诉你代码有没有语法错误,但没法告诉你该不该走这条路。
再举个更极端的例子:给 agent 一个复杂任务,研制一款火箭发动机。Plan 决定了做全流量分级燃烧循环,路线选择了共轴方案。
Agent 不遵循的话: 可能就偏离到抽气循环也说不定。编译器能告诉你代码有没有语法错误,但没法告诉你这是不是你要的火箭发动机。
Agent 遵循太好: 真的做出来共轴方案,那可能上线后会碰到更大的问题——共轴以后动密封系统做不好,氧化剂和燃料随着涡轮轴互相泄漏,俩预燃室要炸一个。编译器能保证类型正确,但没法保证设计合理。
现在的 plan/edit mode 切换也只是现阶段的权宜之计、无奈之举。这个问题比「评价手段缺失」更难解决,因为它涉及到对需求和设计的理解,而不仅仅是代码质量。
初见即巅峰
Agent 编程有一个显著的特点:初见即巅峰。
让 agent 开始一个全新的 CRUD 项目,或者一个 React 管理系统页面,agent 第一次的表现着实让所有人都大吃一惊——干净利落,结构清晰,甚至还贴心地加上了注释和错误处理。
但随着项目维护越来越久,那些「不可明说的」、没有被文档记录的、约定俗成的隐藏上下文越来越长。哪个字段其实已经废弃了但没删、哪个 API 有个历史遗留的 quirk、哪个模块之间有个微妙的依赖关系——这些东西,老员工心里都有数,但从来没人写下来。
而 agent 无法处理无限长的上下文,只能通过压缩、总结来选择性遗忘细节。可能被丢弃的是几次失败尝试的经验,也可能被丢弃的是关键数据结构的偏移量、寄存器地址、枚举定义。
每次新开一个 session 的时候,开发者不得不面对一个几乎全新的「员工」——它似乎继承了压缩后的上下文(claude.md / agents.md),但细节完全不知。你得重新跟它解释一遍:「不是,这个接口虽然文档上写的是这样,但实际上我们从来不传这个参数……」
对于 CRUD、Spring、React 这类重复度高的任务,这似乎不是什么痛点——反正每次都差不多,忘了就忘了。
但对于嵌入式系统开发,任何被遗忘的细节都可能被 agent 天马行空的幻觉填充。寄存器地址错了?中断优先级配错了?DMA 通道冲突了?轻则系统崩溃,重则永久烧坏硬件。这不是「改个 bug 重新部署」能解决的问题。
Agent 时代,CS 基础还要学吗?
既然评价 agent 产出是核心问题,那开发者的基础知识就必然还是要学的。不然你拿什么去评价 agent 生成的代码、模块、架构设计质量到底如何?没有评价能力的开发者,和保健品店里待宰的老头老太没有区别。
那么该如何学习呢?
打开 LeetCode,题目还没读完呢,Copilot 已经把答案补全出来了。点一下 Submit & Run,前 1%。就这?
我的意见是:既然有 AI 了,当然不能局限于过去的难度,得上强度,上到 AI 做不出来的程度。
放心,该学的不会落下。上了强度以后 AI 幻觉越来越多,该补的课全都得补上。期间 AI 还会给你帮不少倒忙——但这恰恰是学习的机会。
比如你要实现 Red-Black Tree、B-Tree、AVL Tree,那就上点强度:给算法加上形式化验证,再把泛型支持也加上。放心,当下最强模型也写不出来。
其实幻觉反而会帮助你学习——因为幻觉里包含了常见的误解,你去验证和纠正幻觉的过程,本身就加深了学习效果。
结语
AI 框架、模型、工具、方法论层出不穷,日新月异。但说到底,这些都是在给模型做加法、打补丁。
人类完成一个完整工作流的时候,不需要把自己拆解成多个「子 agent」去协作——因为人类是真的有记忆能力,且会学习的。做的时间越长,成长越多,越熟练。项目里那些隐藏的上下文、踩过的坑、约定俗成的规矩,都会沉淀成经验。
而 agent 则相反。当前上下文越长,智力下降越明显。即便细节仍然在上下文内,agent 也开始频繁地忽略这些细节,自顾自地幻觉出一些「看起来合理」的东西来。
核心问题始终没变:我们依然缺乏可靠的手段来评价 agent 的产出。强类型是目前我找到的最实用的部分解,但也只是部分解。
一天不学,错过很多。一年不学,好像也没错过什么。
框架工具更新迭代,爆款层出不穷,但其炒作因素远大于实际能力和价值。而 CS 基础知识才是久经时间考验的硬通货。与其追新框架新工具,不如把精力放在强化自己「评价 Agent 产出」的能力上——这才是 agent 时代真正稀缺的东西。