我的智能体编码
前言
回到24年的秋天
虽然已经是10月份,但是天气仍旧很热。那时候我在一家外贸公司上班,负责支付网关的开发工作。 Cursor推送了最新的模型过来,我像往常一样使用它的tab提示和小规模的函数编写。
当我完成了一部分代码,转向去书写单元测试时,我发现Cursor在执行过程中和之前表现不同,它在书写完成后向我申请执行单元测试然后自己执行了一遍, 并依据结果做了几处修改,那一刻我突然意识到:Hey!我想要的东西终于出现了。
随后我反复在我的场景中用测试了多个模型的表现,并试着引导它自主追随我的技术方案进行工作,直到我限额完全用光,我又往里充值了10美元,因为我太兴奋了,完全停不下来。 在我把自己充值的10美元也花光后,我才肯彻底停手,那天晚上不知不觉就加班到了九点半,事实上那大概是我在那加过最晚的班,但事实是,我一晚上几乎干了整周的工作。
这真的非常的cool,你要知道我已经忍受Cursor和Copilot很久了,它们有用,但工作的并不顺利,大多数时间它们都在不断猜测我书写的代码后面应该补全什么, 有时候我会让它帮我写几个单元测试,然后手动执行它, 查看结果并继续喊AI帮我调整代码。我时常会想,执行测试-观察-重写 这个模式为什么一定要我来做,这太浪费时间了,我甚至申请了一个实习生来帮我, 即便你的构建工作已经非常复杂,也不得不承认复杂度往往是其中的1%,剩余的99%仍旧是最简单的设计模式/CRUD工作,仍旧可以找一个实习生帮助你事半功倍。
工作中心偏移
从那天开始我的工作就逐步发生了变化,总的来说,是在新工具的特性面前改造了我的工作流,我努力让自己的工作流适应新的工具,避免要求工具迁就旧流程。 这类变化在我的职业生涯中也出现过很多次,前后端分离/Vue/领域驱动建模/测试驱动开发/Docker/Kubernetes/Microservice,如果你有一个时间跨度较长的职业生涯, 那这种挑战肯定很常见。当你从环法自行车赛转向去参加世界摩托车锦标赛,你一定不会对团队说:嘿,除非你给摩托车装上脚蹬子,否则我是不会骑上去的。
同样的,曾经在我工作中占比最大的Coding正在逐步压缩,更多的精力事实上是放在架构规划/技术方案/文档撰写/沟通协调,包括一些测试和devops工作。但这不能说明AI 无法在这些领域中帮助我,正好相反,Agent很大程度上加速了我的工作效率。在最初的阶段,工作流还没有完善。有很多可以由Agent来做的事情我仍然在手动处理。 但随后,大部分的测试工作和文档类任务逐步使用AI完善。虽然表面上看起来进度没有改变,但实质上最初阶段中提升的应当是项目质量而非速度。
从环法自行车赛到WSBK
速度飞快
毫无疑问,构建应用时最耗时的编码工作,已经从以字节为单位的吞吐提升到了以“篇”为单位。 市面上甚至还有很多Flash/Haiku/Mini这样的快速模型,当你的场景很小的时候,它们也是好选择。最夸张的还是Codex-Spark,它甚至能达到每秒钟数千token的吞吐。 像是倾泻一样地写代码,并且能保证相当高的One-Shot(我们姑且这么叫它)准确率。
事实上“快速”本身是个中性词,它并不决定工程成功与否,它只代表速度,如果只是局部的速度飞快,那其实对整体的工程进度影响是非常有限的,另一方面 如果你以速度为目标,但是最终却用工程是否成功为基准评估这个目标,那么责任应该落在目标设定上,不能简单推给AI。
举个例子:如果你的工程A原本需要10个工作日书写代码,但是现在变成1个工作日完成,那么这个工程就该被称为一个“敏捷”工程吗?
并不一定,我来举个反例,你花了1个工作日写代码,然后花了3个工作日调整这些代码,最终4个工作日审核代码,在对接期间又花了两个工作日用来改变输入输出。 虽然在最初的编码阶段,速度提升得飞快,但是如果后续花了更多时间去维护它,那么你第一个工作日的产出就变成“10个工作日的代码和九个工作日的技术债”。这种情况 在短期交付并脱离责任的团队中,不会有什么问题。但是一旦团队需要长期维护一个项目,那么问题就出现了,而且你要特别考虑到,大部分团队的习惯,是把后面的9个工作日省略, 并将其视作“敏捷”的做法。
我听过很多例子,某个团队在使用AI一段时间后,虽然进度明显提升,但是代码逐渐无法继续维护,难以察觉的bug越积越多,代码因为缺少维护文档和项目开发手册变得越来越难以为继。 这表面上看起来都是AI导致的,但事实是:大多数团队从来未曾考虑维护技术文档,也不写单元测试,当出现了一些难以复现的bug,会选择性地忽略它,AI并没有改变你们的团队习惯, 只是在疯狂的加速这一过程,让一个12个月才会难以为继的代码仓,在两个月时间就维护不下去了。
适应更多路况
正如同我们讨论过的,你在前面几个周期(迭代/自然周/工作日)中,理所应当地该把Agent Coding的重心放在项目质量而非速度上。只有你在设法提升项目质量,才能逐步熟悉 如何构建一个工作流,如何构建一个可交付的产品。
在早期的LLM交互中,我们仅仅可以做到的事情是:在一问一答之间寻求问题的解决办法。但实际上你没办法让AI帮你“做”任何事情。因此在Agent能力健全后, AI实际能做到更多的事情,比如:单元测试,代码审查,MR/PR, 重构,质量提升。曾经我们认为的复杂场景往往因为时间/人力成本的关系,被心理预期所拒绝。 AI在这一场景中扮演了很重要的角色,它最大的作用未必是直接解决问题,更在于降低人们对工作难度的心理预期,让人对更多复杂的场景更有信心。 不要小看信心的问题,很多执行不下去的事,往往就是因为信心不足,包括使用AI改造工作流。
让我来给具体的场景举一些例子:
对于前端同学而言,在最近的半年内,Agent编码最大的问题其实是它很难理解你对UI进行的描述。在之前的场景中,我们只能逐个描述给AI听,这种方式不仅低效 而且成功率相当低,很大程度上都是依赖“抽卡”与人工。在目前的旗舰模型中,大部分具备原生多模态的能力,我们完全可以借助类似“浏览器截图”的方式,将UI描述成图片,让AI进行识别, 这样的编程成功率远高于我们对UI的描述。
车祸
我们对AI的错误认知往往会走向两个极端:AI无所不能,它应该代理手头的一切工作。或者AI什么都不能做,即便能做,成果也是一堆没有价值的劣质品。 但我的观点是:AI能帮助你完成你本就能完成的工作,如果你本就能完成很好,那么AI会帮你更快地完成,如果你本就会搞砸,那么AI会帮你更快地搞砸。
如果你骑自行车,湿滑的路面不会影响你太多,通常你只要避开路上的水坑就行了,但摩托车不一样,任何湿滑的路面配合急刹都可能是一场灾难。 编程也一样,Agent Coding能迅速的进行编码-运行命令,在这些过程中如果你没有认真审查,那么造成的后果往往比手动编码更糟糕。虽然本世代的AI模型已经 在构建工作中表现得非常好,但是仍旧有很多灾难级的事故发生。你要思考的其实是如何在工作流中系统性地规避这些问题,别把问题简单归结为“哦,这错误是AI搞的”。
从Coding到Engineering
软件工程
我们的实际工作流中,编码其实只是其中的一部分工作,我们需要产品替我们挡在客户那里,沟通并总结规划,随后设计同学帮助我们制定好美学的实践方向,然后 我们建模,编码,交付给测试同学帮助我们验证质量,最后由运维同学部署,产品同学进行验证,这样才完成了整个软件工程工作。如你所见我们的工作流中, 你至少要和三个角色进行工程进度协调和目标对齐的沟通。编码之所以一直被视为很重要的原因是,它消耗了太多的时间。这个过程里我们几乎无力支付快速试错的成本, 每个决策都会花费数小时到数周的人力成本,因此对于编码做出的每一项决定最终都会慎之又慎。
同样的,作为软件工程师,至少你应该以“工程”的视角去观察整个工作流,不要只考虑编码。
停止非必须的编码,但是请继续思考
当编码的成本被压低以后,我觉得最重要的一件事是:停止做那些非必须由你亲手完成的编码。 这句话听起来很像偷懒,但它真正讨论的是注意力转移。“停止编码”和“停止工作”是两件完全不同的事情,甚至在Agent Coding的场景里,它们经常是相反的。 你越是把自己从低价值的机械编码里释放出来,就越需要把注意力放在更高密度的判断上。
什么是非必须的编码?大概就是那些已经被你理解清楚、输入输出足够明确、模式足够稳定、失败以后也容易被测试或审查发现的工作。 比如根据已有风格补一个CRUD接口,给一个函数补齐单元测试,为一个已经确定的数据结构书写DTO和Mapper,按照既有规范修改若干处字段名称, 或者把一份已经定稿的技术方案落到代码里。它们当然也是工作,而且有时候数量很大、很烦、很消耗心智,但它们不一定需要你逐字逐句亲手敲出来。 在过去,你不得不亲手完成这些事情,是因为没有人能帮你承担这部分劳动;而现在,如果你仍旧把自己的主要精力耗在这里,那么就像骑着摩托车却仍然坚持用脚蹬地一样。
但请注意,真正危险的地方就在这里:你可以停止亲手写一部分代码,但绝不能停止思考。 因为Agent能替你生产代码,却不能替你承担工程判断的后果。它可以很快写出一个支付回调接口,但它未必知道这条链路在你的业务中是否必须幂等; 哪些异常应该进入对账,哪些异常只应该作为用户侧的普通失败返回,这些事情都需要你来考虑,不能交给AI边写边想。
这些东西属于工程问题,不能归结于“编码问题”。工程问题真正难的地方,是你是否知道这一行代码背后承担了什么责任。 一个看起来很普通的字段默认值,可能会让测试环境正常运行,却在生产环境中埋下三个月以后才爆炸的问题。 AI可以处理这些问题,但前提是你已经意识到它们,并且把它们作为上下文交给它。
所以在Agent Coding中,人真正应该做的事情,是把更多注意力前移和后移,别和AI比赛谁打字更快。 前移,是在编码开始之前想清楚目标、边界、约束、接口、数据模型和验收标准。后移,是在代码生成以后审查它是否符合这些约束,是否真的可测试、可维护、可部署、可回滚。 过去我们因为编码太慢,经常把很多思考压缩进编码过程中:写到哪里算哪里,遇到问题再调整,等代码堆出来以后再回头补设计。 但Agent会把这个过程加速到一种近乎危险的程度,如果你没有提前想清楚,它就会非常勤奋地沿着错误方向写下去,并且很快写出一大坨看起来像是完成了的东西。
做些需要你做的—决策,构建与交付
我后来逐渐形成了一个很朴素的判断标准:凡是我能清楚描述“它应该做什么”和“我如何确认它做对了”的工作,都可以考虑交给Agent去做; 凡是我还说不清楚“为什么要这样做”“边界在哪里”“失败时应该怎么办”的工作,就必须先停下来继续思考。 这不妨碍AI参与思考。恰好相反,你应该让它参与讨论,让它列风险、提方案、找反例场景。 但在这个阶段,它更像是一个陪你推演问题的同事,还不适合立刻变成开工的施工队。
例如我要做一个支付网关的回调处理,我不会一开始就让Agent直接“帮我写一个回调接口”。 我会先让它和我一起确认几个问题:网关是否可能重复通知?签名失败要不要记录原始报文?订单不存在时是返回成功还是失败?状态已经成功时再次收到成功回调是否直接幂等返回? 如果本地事务提交成功但后续消息发送失败,系统靠什么补偿?如果回调字段未来新增,我们的解析逻辑是严格拒绝还是向后兼容? 这些问题想清楚以后,编码反倒成了最轻松的部分。因为到那一步,Agent要把一个已经稳定的工程判断翻译成代码,不能让Agent在一团模糊的需求里替你猜业务。
因此,“停止非必须的编码”真正的含义是重新分配你的注意力。 不要把自己绑定在每一个控制器、每一个服务提供者、每一个测试样例的逐字输入上;但也不要把方向盘完全交出去。 你应该更频繁地阅读diff,更频繁地要求它解释关键选择,更频繁地检查测试覆盖了什么、没有覆盖什么,更频繁地追问这段代码在异常、并发、回滚、权限、性能上的行为。 你的工作并没有消失,只是从“代码的生产者”转向“工程负责人”。
这也是为什么我认为Agent Coding最终改变的是“人应该把注意力放在哪里”。 当代码本身变得廉价,真正昂贵的东西就会浮出水面:需求理解、技术取舍、抽象边界、质量标准、交付路径,以及你愿不愿意在一个问题还没变成事故之前,就认真把它想明白。
Agent是如何工作的
工具-手眼与大脑
在早期的LLM使用方式里,我们和模型之间的关系基本是一问一答。 你把问题、代码、报错和自己的猜测放进输入框,模型基于这些文本给出回答。 如果它建议你执行测试,你要自己去终端执行;如果测试失败,你要自己把结果复制回来;如果它给出修复代码,你要自己放进项目里验证。 这时候模型像是一个很聪明但坐在黑屋子里的人,它能推理,却看不到你的工程现场,也碰不到你的代码仓库。
Agent客户端改变的是它把原本由人手动驱动的循环接了过去。 你提出一个目标后,模型会判断下一步需要什么信息,再由客户端调用工具完成读取文件、搜索代码、执行命令、修改文件、查看页面等动作。 工具执行的结果会重新回到上下文里,模型再继续判断下一步,直到任务完成或者遇到无法继续推进的问题。
所以Agent更像是给大模型装上了手和眼睛。 读文件、搜索代码、查看终端输出、打开网页、截图、运行测试,这些是它的眼睛;编辑文件、执行命令、提交补丁、调用接口,这些是它的手。 模型本身仍然是大脑,负责理解目标、制定计划、选择工具、解释结果和调整方向。
大脑没有手眼,就只能坐在原地猜;工具没有大脑,也只是一些孤立的脚本。 Agent真正有价值的地方,是把大脑、手、眼接进了一个连续的工作循环里,让它可以像工程师一样不断观察、行动、再观察。 当然,这不代表它一开始就理解了整个项目,它只是获得了持续获取上下文的能力。 因此我们评价一个Agent好不好用,不能只看模型本身,也要看它的工具设计、权限边界、上下文组织方式和工作循环是否顺畅。
Agent.md/Skill/Plugin/MCP 全是上下文
既然Agent的核心能力之一是持续获取上下文,那么接下来的问题就是:这些上下文从哪里来? Agent.md、Skill、Plugin、MCP看起来是四种不同的东西,但从使用者的角度看,它们都在做同一件事: 把原本需要你反复解释、手动复制、临时补充的内容,提前放到Agent可以读取、选择或调用的位置上。
| 形式 | 补全的上下文 | 使用办法 |
|---|---|---|
| Agent.md | 项目规则:目录结构、常用命令、测试方式、代码风格、注意事项 | 写稳定且高频的项目约定,作为Agent进入代码仓库后的工作手册 |
| Skill | 任务流程:代码审查怎么做、技术方案怎么写、文档或表格怎么处理 | 把重复出现的方法沉淀下来,让Agent在同类任务中复用 |
| Plugin | 能力边界:浏览器、GitHub、邮件、表格、文档、外部系统 | 给Agent安装一组手眼,让它能实际操作某个系统 |
| MCP | 外部资源:数据库结构、内部文档、业务系统、知识库、远程工具 | 用标准接口把动态信息和外部动作交给Agent,减少复制粘贴 |
所以它们的形态虽然不同,本质却很接近:Agent.md管项目,Skill管方法,Plugin管能力,MCP管外部资源。 换句话说,它们是在把隐性的工程上下文显性化。 当这些上下文被组织好以后,Agent就不需要每次从一段孤零零的提示词开始猜,它可以先读项目规则,选择合适的工作方法,调用必要工具,再根据真实结果继续行动。
这也是为什么上下文补全比“提示词技巧”更底层。 提示词当然重要,但提示词更像是一次性的任务说明;Agent.md、Skill、Plugin、MCP则是在搭建长期可复用的工作环境。
当然,上下文也讲究质量,不能只看数量。 无关的文档、过期的规则、混乱的工具、权限过大的接口,都会让Agent做出更稳定但更错误的判断。 真正有价值的上下文补全,应该是清晰、可维护、可验证的:稳定规则写进Agent.md,重复流程沉淀成Skill,外部能力通过Plugin提供,动态资源和系统操作通过MCP暴露。 它们共同服务的目标只有一个:让Agent少猜一点,多看一点,多验证一点。
如何让Agent更稳健的编码
局限性
在开始讨论如何让Agent更稳健之前,我们要先承认一件事:Agent还不能算一个真正意义上的工程师。 它可以读代码、改代码、跑测试、总结结果,看起来已经非常像一个可以独立工作的同事,但它的工作方式和人仍然有很大差别。 如果你忽略这些差别,只是把它当成一个便宜、快速、永不疲惫的程序员,那么问题迟早会出现。
Agent的第一个局限,是它并没有真正拥有项目的长期记忆。 它能看到的内容,本质上取决于当前上下文、工具返回结果,以及你为它准备好的那些文档和规则。 如果关键约束没有写进Agent.md,没有出现在代码里,也没有被测试覆盖,那么它很可能就不知道这件事。 它不会像一个在团队里工作了两年的同事一样,记得某个接口为什么当年被迫设计成这样,也不会天然知道某个奇怪字段背后其实是客户、财务和合规三方妥协的结果。
第二个局限,是它对“正确”的理解经常依赖可见反馈。 测试通过,它会倾向于认为事情已经完成;类型检查通过,它会倾向于认为代码至少没有明显问题;页面截图看起来正常,它会倾向于认为UI已经可用。 但很多工程问题并不会立刻暴露出来:并发下的重复处理、异常路径里的状态污染、日志里缺失的排查信息、权限边界上的模糊判断、生产数据规模下的性能问题。 这些东西如果没有被测试、监控、约束和审查捕捉到,它就很容易从旁边走过去。
第三个局限,是工具会给它一种“我已经看到了真实世界”的错觉。 它确实可以读取文件、搜索代码、执行命令,但工具返回的结果并不总是完整的。 搜索关键词可能没搜到真正相关的实现,测试命令可能只覆盖了很小一部分场景,终端输出可能被截断,浏览器截图可能只展示了一个视口。 如果Agent基于这些不完整的观察继续推理,它就会非常认真地在一个局部真实的世界里做出全局判断。
第四个局限,是它很容易顺着你的目标一路往前冲。 你说“帮我把这个功能做完”,它就会尝试完成;你说“修复这个测试”,它就会尝试让测试变绿。 但工程里有很多时候,正确的动作是停下来追问:这个需求本身是否清楚?这个测试是不是写错了?这个接口是不是不应该继续扩展?这个抽象是不是已经到了该拆分的时候? Agent可以帮助你思考这些问题,但前提是你的工作流允许它停下来,不要只奖励它不断产出diff。
所以Agent的局限性,不能简单概括为“它会犯错”。 人也会犯错,甚至人类工程师犯的错一点都不少。 真正的问题在于,Agent犯错的速度很快,而且它的错误经常混在大量看起来合理的代码里。 一段手写代码出错时,你通常还记得自己为什么这样写;一大段Agent生成的代码出错时,如果你没有审查和测试,很可能连它为什么这样写都说不清楚。
因此,让Agent更稳健的关键,不能寄托在一句万能提示词上,也不能寄托在下一代模型完全消灭错误上。 更现实的做法,是围绕它的局限性搭建工作流:用清晰上下文减少猜测,用小步任务减少偏航,用测试和审查提供反馈,用工具权限控制风险,用交付路径确认结果。 你要做的,是承认它有局限,并让这些局限被工作流吸收掉。
提示词没有技巧
我一直不太喜欢“提示词技巧”这个说法。 它听起来像是某种咒语,好像你只要在句子里加上“你是一个资深工程师”“请一步一步思考”“如果你做得好我会给你小费”,模型就会突然进入某种隐藏模式。 这些话有时候当然有用,但Agent Coding稳定工作的核心不在这里。 真正有用的提示词,通常并不神秘,只是把任务说明得像一个正常工程任务。
你可以把它想象成给同事分配工作。 如果你对一个同事说“帮我优化一下这个模块”,他大概率也会反问你:优化哪里?性能、可读性、结构、还是接口体验?能不能改外部行为?有没有测试?什么时候算完成? 同样的,如果你只对Agent说“帮我重构一下”,它也只能根据自己看到的局部上下文开始猜。 猜对了你会觉得它很聪明,猜错了你会觉得它又在乱改,但问题往往出在目标和边界没有说明白。
我后来更习惯把提示词看成一份很短的任务单,别把它当成一句魔法咒语。 一份好的任务单通常要说清楚几件事:我要你完成什么,为什么要做,哪些地方可以改,哪些地方不能改,最后用什么方式确认它完成了。 比如“帮我优化订单导出接口”就非常模糊;但如果你说“在不改变对外接口的前提下,把订单导出从内存过滤改成数据库分页, 保留现有返回结构,补充大数据量和空结果的测试,最后运行相关测试并总结改动”,Agent的成功率就会高很多。
第二句话真正有效的地方,不在什么高级技巧上,而在它减少了很多猜测空间。 Agent最怕目标模糊,复杂任务反而可以继续拆。 模糊任务只能猜。 当你把目标、约束和验收方式说清楚以后,模型不需要花太多精力猜你的意图,就可以把注意力放在读代码、做修改、跑测试和修复问题上。
当然,我们也不能把提示词写成一篇论文。 很多人走到另一个极端,会试图在一次输入里把所有背景、所有规则、所有可能性都塞进去。 这会带来另一个问题:Agent会被大量不重要的信息干扰,甚至分不清哪些是当前任务真正相关的约束。 所以提示词的重点在于有效,不在于长。 稳定规则应该进Agent.md,重复流程应该进Skill,工具能力应该交给Plugin和MCP,而当前提示词只需要说明这一次任务的目标、边界和验收。
还有一点也很重要:提示词只能提高开始方向的准确率,不能替代验证。 你把任务说得再清楚,也不能跳过测试、审查和运行结果。 Prompt负责让Agent少走弯路,测试和审查负责告诉你它到底有没有走到地方。 如果没有后面的反馈闭环,再漂亮的提示词也只是把错误包装得更像正确答案。
所以我认为提示词没有所谓的技巧,只有工程沟通的基本功。 不要试图用几句神奇的话控制模型,你应该像安排真实工作一样安排Agent:给它目标,给它上下文,给它边界,给它验收标准。 如果任务还不清楚,就先让它和你一起澄清;如果任务已经清楚,就让它小步执行并持续验证。 这听起来一点也不酷,但大多数时候,稳定的工程实践靠的是耐心和反馈。
脏活 - OODA
在软件工程里,有很多工作没有明确目标、清楚路径,也很难在改完以后立刻验证。 更多时候,我们面对的是一堆不完整的信息:环境不明确,目标不明确,错误原因不明确,实现手段也不明确。 线上为什么偶尔出现重复回调?这个字段到底还有没有人在用?某个接口变慢是数据库问题、网络问题,还是调用方突然放大了请求量? 这类工作我一般称之为“脏活”。
脏活的麻烦之处在于,单靠聪明很难解决。 你当然需要经验,但经验本身并不会自动告诉你答案。 它更像是在雾里走路,你不能指望一开始就看清整条路,只能先看清脚下这一小块地方,再决定下一步往哪里走。 很多工程事故之所以会扩大,问题常常不在第一个判断错了,真正麻烦的是人们在第一个判断之后没有继续观察,直接把猜测当成了事实。
我更喜欢用OODA来处理这类问题:Observe,Orient,Decide,Act。 翻译过来就是观察、判断、决策、行动。 这原本是一个指导特种作战的军事理论,虽然软件工程中有类似的方法论,但OODA放到工程里其实更加朴素:先看发生了什么, 再理解它意味着什么,然后决定下一步做什么,最后执行并重新观察结果。
比如一个测试失败了,第一步应该先观察,别急着马上修改代码。 它失败在哪个用例?报错是类型错误、依赖服务不可用,还是环境变量缺失?本地能不能复现?最近相关文件改过什么? 这一阶段的重点是尽量收集事实,先别急着证明自己是对的。 很多时候,你多看一眼日志、多跑一次指定测试、多查一下提交记录,就能避免后面半小时完全错误的修复。
接下来是判断。 判断要把观察到的事实放回工程上下文里,不能拍脑袋。 如果只有CI失败,本地不失败,那它可能是环境差异、时序问题、缓存问题;如果只有某个边界用例失败, 那它可能是业务规则遗漏;如果失败出现在一次重构之后,那你要先确认外部行为有没有被改变。 这一步很重要,因为同样一个报错,在不同上下文里可能指向完全不同的问题。
然后才是决策。 决策要选择一个足够小、足够可验证的下一步,不能停留在“我要把它修好”这么宽泛的表述上。 比如先补一个复现用例,先加一条日志确认输入,先回退一个不确定的抽象,先把大任务拆成两个更小的提交。 好的决策往往不追求宏大,它真正要带来的是下一轮更清晰的反馈。
最后是行动。 行动之后不要急着宣布完成,需要重新观察:测试是否通过?新的日志是否符合预期?修复有没有影响其他路径?有没有引入新的边界问题? 如果结果符合预期,再继续往下推进;如果结果不符合预期,就回到观察和判断。 这就是OODA真正有用的地方,它不要求你一次性做出完美判断,它的价值在于让你在不完整信息里持续校正方向。
所以处理脏活时,真正重要的是建立一个足够短的反馈循环,别指望自己“一下子想明白”。 观察越具体,判断越不容易飘;决策越小,行动越容易验证;行动之后越快回到观察,错误扩大的概率就越低。 这和Agent的工作流其实是一个道理:先看环境,再形成判断,再小步行动,再根据结果继续修正。
交付 - 代码审查 测试 与交付质量
如果说OODA解决的是“如何在不完整信息里推进”,那么交付讨论的是另一个问题:你怎么证明这件事真的做完了。 Agent Coding最容易让人产生错觉的地方,是它可以很快给你一个看起来完整的结果。 代码写好了,文件改了,测试也许跑过了,最后它还会给你一段非常自信的总结。 但软件工程里,完成感和完成本身是两回事,这也是我为什么非常讨厌“氛围编程”。
我觉得开发者在使用Agent时,最重要的习惯之一,就是不要把“AI说完成了”当成完成。 它说完成了,只代表它认为当前任务在它能看到的上下文里已经结束。 真正的完成,应该来自你自己的交付标准:代码是否符合设计意图,测试是否覆盖关键路径,异常和边界是否处理清楚,改动范围是否可控,部署以后是否能观察和回滚。 这些东西没有人替你兜底,尤其是在你把大量编码工作交给Agent以后,反而更应该认真看一遍。
先说测试。 我不认为开发者测试只是“跑一下单元测试”。 跑测试当然重要,但更重要的是你要知道自己在验证什么。 一个很常见的误区是,Agent修完问题以后会运行相关测试,测试通过,于是大家都松了一口气。 但相关测试到底相关到什么程度?它只覆盖了正常路径,还是覆盖了异常路径?它只是验证函数返回值,还是验证了状态流转、数据持久化、副作用和幂等性? 如果你不知道这些问题的答案,那么“测试通过”只能说明一件事:已有测试没有发现问题。
好的开发者测试,应该先从风险出发。 你要问自己:这次改动最可能破坏什么?最昂贵的错误会发生在哪里?哪些行为必须保持不变?哪些边界过去没有被覆盖? 比如支付回调类代码,正常成功路径当然要测,但真正值得紧张的往往是重复通知、签名失败、订单不存在、状态已终结、事务提交后消息发送失败这些场景。 如果这些路径都没有测试,你让Agent跑一百遍正常用例,也很难证明这段代码真的可靠。
所以我更建议让Agent参与测试设计,别只让它补测试代码。 你可以先让它列出这次改动的风险点,让它根据风险点设计测试场景,再让它实现其中最关键的一部分。 然后你自己去审查这些测试到底在验证行为,还是只是在迎合当前实现。 很多AI生成的测试看起来覆盖率很高,但实际上只是把代码内部逻辑重新抄了一遍;这种测试最大的价值,往往只是让覆盖率数字变漂亮,并不能真正提高你的交付信心。
再说代码审查。 Agent生成的代码尤其需要代码审查,因为它的问题经常绕开语法错误,表现为“看起来很合理,但放在这个项目里不合适”。 比如它可能引入一个和现有架构不一致的工具函数,可能绕过已有的领域模型直接操作数据库,可能为了让测试通过把异常吞掉,可能在一个局部修复里顺手改了不该改的公共行为。 这些问题很难靠一次编译发现,也很难靠一个正常路径测试发现。
审查Agent代码时,我通常不会从“这段代码写得漂不漂亮”开始看。 我会先看改动范围:它有没有改到不该动的地方?有没有把一个小问题扩散成大面积重构?有没有新增不必要的依赖、抽象和配置? 然后看行为变化:对外接口有没有变?状态流转有没有变?异常处理有没有变?日志、权限、事务、并发这些隐蔽行为有没有被影响? 最后才看代码质量:命名是否清楚,结构是否符合项目习惯,重复是否可以接受,测试是否真的覆盖了关键风险。
这里有一个很实用的办法:让Agent自己先做一遍审查。 这一步不需要它夸自己写得很好,你要明确要求它从风险角度审查自己的diff:找潜在bug,找未覆盖测试,找行为变化,找和项目约定冲突的地方。 它不一定能找全,但通常能提前暴露一部分问题。 然后你再带着它的审查结果去看代码,会比从一大段diff里裸看轻松很多。 当然,最终判断仍然要回到开发者自己身上,Agent的自审只能当成第一轮过滤,不能当成最终批准。
最后是交付质量。 交付质量来自前面每一步的累积,不会在最后一刻凭空出现。 任务说明是否清楚,Agent.md是否写明项目约定,OODA循环是否足够短,测试是否围绕风险设计,代码审查是否关注行为变化,这些都会在最终交付时体现出来。 如果前面每一步都在猜,那么最后的交付就只能靠运气。
所以我会把Agent Coding里的交付理解成三句话:先用测试证明关键行为还在,再用代码审查确认改动没有越界,最后用可观察、可回滚的方式把它交出去。 代码写完只是交付的开始,离真正结束还有测试、审查和上线后的观察。 当编码变得越来越快,交付质量反而变成真正拉开差距的地方。 因为任何人都可以很快生成一堆代码,但愿意认真确认这些代码真的可以进入长期维护系统的人并不多。
如何提升代码质量
代码质量上限的约束
讨论AI编码时,很多人会下意识地把代码质量问题归因到大模型上。 模型不够强,所以代码不够好;模型上下文不够长,所以架构不稳定;模型推理能力不够,所以容易写出奇怪的实现。 这些说法当然有一部分道理,但如果把问题全部推给模型,我觉得反而会错过最重要的东西。 在Agent Coding里,代码质量的上限往往是人。
模型更像是一个执行力很强的放大器。 如果你知道自己要什么,知道什么样的代码是好的,知道项目应该保持怎样的边界和风格,那么它会把这些判断很快落成代码。 但如果你自己也说不清楚什么是好设计,分不清必要抽象和过度抽象,看不出一个接口的状态流转是否合理,也没有测试和审查的习惯,那么模型再强,也只是在更快地放大这些问题。 这和前面说的一样:如果你本来就能做好,它会帮你更快地做好;如果你本来就会搞砸,它也会帮你更快地搞砸。
很多时候,AI生成低质量代码的原因,未必是它“不会写代码”,更可能是人没有给它一个足够清晰的质量标准。 比如你让它“实现一个订单查询功能”,它可能会很快写出控制器、服务、数据库查询、返回结构和测试。 但是这个查询要不要分页?要不要权限控制?是否允许跨租户查询?空结果怎么返回?慢查询如何处理?字段暴露是否符合产品约定? 这些问题如果你不提出,它未必会主动替你承担完整责任。 即便它主动想到了,也未必想得符合你的项目。
所以开发者的价值并没有因为Agent出现而降低,反而被迫暴露得更清楚。 过去你写代码慢,很多质量问题会被编码速度掩盖掉,大家还可以把大量时间花在“实现功能”上。 现在功能实现变快了,真正的差距就会转移到判断上:你能不能判断一个方案是否合理,能不能发现边界条件,能不能看出抽象是否过早,能不能识别测试是否只是在刷覆盖率。 这些能力需要你自己补齐,模型只能在你已有标准的基础上帮你执行。
我见过一些团队使用AI以后,代码质量不但没有上升,反而下降得非常快。 表面上看,是AI写了很多不稳定的代码。 但更深层的问题是,团队本来就没有清晰的代码规范,没有可靠的测试习惯,没有认真审查的文化,也没有人愿意维护文档和架构边界。 AI只是让这些原本需要一年才显现的问题,在几个月里集中爆发出来。 这时候说“模型质量不行”,就像赛车冲出赛道以后只怪引擎太强,而不讨论车手是否真的懂刹车、是否知道具体的出湾点在哪里。
因此,想提升Agent Coding的代码质量,第一件事是让自己变得更强,别把希望全放在下一个模型上。 模型当然会继续变强,但你不能把工程质量寄托在模型升级上。
这也是为什么我说人才是代码质量上限的约束。 Agent可以提高吞吐,可以降低试错成本,可以帮你完成大量过去懒得做、没时间做的事情。 但它不能替你建立品味,不能替你承担责任,也不能替你定义什么叫“这段代码可以长期维护”。 当代码变得便宜以后,真正昂贵的东西变成了人的判断力。
抓牢基本功
既然人才是代码质量上限的约束,那么接下来的问题就很直接:开发者到底应该提升什么? 很多人在面对AI编码时,会本能地觉得自己需要学更多框架、更多工具、更多语言特性,或者继续把大量时间投入到LeetCode刷题里。 这些东西当然也有用,但如果从日常工程产出的角度看,它们的投产比经常没有想象中那么高。
框架会变得很快。 你今天花很多时间研究某个前端框架的最佳实践,明年它可能就换了一套状态管理方式;你今天熟悉某个ORM的高级用法,换一个团队可能就完全用不上。 框架当然重要,也当然要会用,但框架知识更多是“当前项目的工具知识”,它解决的是你怎么在某个生态里干活。 而真正决定代码质量的,往往是更底层的东西:你怎么组织代码,怎么控制依赖,怎么处理复杂度,怎么设计边界,怎么让系统在变化时不至于立刻崩掉。
刷题也是类似。 算法能力当然有价值,尤其当你真的在做搜索、推荐、编译器、数据库、图计算这类系统时,它会非常重要。 但对大多数业务开发来说,每天遇到的问题通常并非“如何在白板上写出一个最优解”,更多是“这个需求应该放在哪一层”“这个状态是否应该持久化” “这个接口未来怎么扩展”“这段代码三个月后别人还能不能看懂”。 如果一个开发者把大量学习时间都投入到刷题,却很少训练这些工程判断,那么他在真实项目里依然很容易写出难以维护的代码。
我更建议把时间花在那些能长期迁移的基本功上。 第一是编码规范。 听起来很朴素,但它非常重要。 命名是否清楚,函数是否足够短,错误处理是否一致,日志是否能帮助排查问题,代码是否符合项目习惯, 这些东西不会让你看起来像一个算法天才,但会直接决定别人愿不愿意维护你的代码。 尤其在Agent Coding里,规范越清楚,AI越容易沿着稳定风格继续工作;规范越混乱,它就越容易学到项目里最糟糕的部分。
第二是设计模式。 我说的设计模式,不要求你背诵二十三种模式,也不鼓励你在每个地方都强行套一个Factory、Strategy、Observer。 真正有用的是理解这些模式为什么出现:它们在解决什么变化,隔离什么依赖,降低什么耦合,推迟什么决策。 当你理解这些东西以后,你就不会因为AI写了一个“看起来很优雅”的抽象就立刻接受它,也不会在一个简单需求里放任它搭出一整套没有必要的继承结构。
第三是架构模式。 分层架构、领域模型、事件驱动、CQRS、微服务、模块化单体,这些名字本身不重要,重要的是你要知道它们分别在处理什么问题。 什么时候应该把逻辑放在应用层,什么时候应该进入领域层;什么时候一个服务应该拆开,什么时候继续放在一个进程里反而更可靠; 什么时候事件是解耦,什么时候事件只是把调用链藏起来。 这些判断会直接影响Agent生成代码的方向,因为你如果不懂边界,它就只能根据局部代码继续堆实现。
第四是数据结构和复杂度意识。 很多业务代码不需要你手写红黑树,但它需要你知道自己正在制造什么复杂度。 一个循环里查数据库,一次请求里加载过多关联,一个看似方便的全量导出,一个没有索引意识的模糊查询,都可能在数据量上来以后变成事故。 Agent很擅长把“能跑”的代码写出来,但“现在能跑”和“数据量上来以后还能跑”是两件事。 如果开发者自己没有复杂度意识,就很难在审查时发现这些问题。
这些基本功不够性感,也很难在短时间里给你一种“我又学会了一个新东西”的兴奋感。 但它们的复利非常高。 你掌握了一个框架,可能只在某个项目里有用;你掌握了组织复杂度的方法,会在几乎所有项目里有用。 你刷出一道困难题,可能对面试有帮助;你能把一个混乱模块拆成清晰边界,会对团队接下来半年的维护都有帮助。
所以在AI时代,我反而觉得开发者更应该回到基本功。 因为AI会越来越擅长补齐具体实现,越来越擅长查文档、写样板、迁移API、生成测试。 但它越擅长这些东西,人的价值就越会集中到判断上。 而判断力不会凭空出现,它来自长期的工程训练:写过足够多代码,读过足够多坏代码,修过足够多事故, 也认真思考过为什么有些代码能活很多年,有些代码上线那天就开始腐烂。
开拓视野
抓牢基本功不意味着把自己关在当前项目里。 恰好相反,越是在技术变化很快的时候,开发者越需要定期抬头看一眼外面的世界。 如果你只关注自己公司、自己团队、自己项目里的日常工作,很容易把局部环境误认为整个行业。 你会以为大家都在这样写代码,大家都在这样交付,大家都在这样测试,直到某一天你突然发现,外面的人已经换了一套完全不同的工作方式。
这几年AI编码给我的一个很强的感受是,很多变化是突然越过某个临界点的。 在那之前,你会觉得它还不成熟,还只是玩具,还不能进入严肃工程。 但一旦越过那个点,它就会迅速改变工作流,改变协作方式,甚至改变团队对一个开发者的期待。 如果你完全不看技术社区,不看工具链变化,不看那些前沿探索,你很可能直到变化已经落到自己头上时,才开始被迫适应。
当然,开拓视野也不等于每天追热点。 今天一个新框架,明天一个新数据库,后天一个新的AI工具,如果每个都追,你的精力很快就会被切碎。 知道最多名词并不代表真正有视野,更重要的是客观判断哪些变化只是短期噪音,哪些变化正在改变底层生产方式。 比如某个编辑器又加了一个聊天窗口并不构成Agent Coding真正重要的变化;真正重要的是,“模型可以调用工具、读取上下文、执行任务”正在成为新的软件生产接口。
所以我会建议开发者保留一部分时间,去看技术社区的风向。 看大家在讨论什么问题,看优秀团队在用什么方式解决问题,看开源项目为什么选择某种架构,看新工具到底解决了什么旧痛点。 你不需要立刻把这些东西搬进生产环境,但你要知道它们存在,知道它们成熟到了什么程度,知道它们可能会在什么时候影响你的工作。 这会让你在真正需要做技术选择时,不至于只在自己熟悉的那一小块地方打转。
前沿探索也很重要。 很多东西在刚出现时都很粗糙,看起来不可靠,甚至有点可笑。 但前沿探索的价值不一定体现在“今天就能上线”,它更大的意义是让你提前理解一种可能性。 当你理解了这种可能性,等它真正成熟时,你就不会只是一个被动接受变化的人。 你会更早知道该调整什么工作流,该补什么能力,该警惕什么风险。
这件事对Agent Coding尤其明显。 如果你只把它当成一个更快的代码补全工具,你会错过它对测试、审查、文档、需求澄清、运维排查、知识管理这些环节的影响。 但如果你经常观察社区里的人如何使用Agent,观察不同工具如何组织上下文、权限和工作循环,你就会更容易发现自己的工作流还能怎么改造。 很多时候,差距来自对方更早看到了新的协作方式。
所以基本功和视野其实可以同时成立。 基本功让你不容易被风吹倒,视野让你知道风是从哪里吹来的。 只练基本功而不看外部变化,容易变成一个很稳但很慢的人;只追外部变化而没有基本功,又很容易被每一个新概念牵着走。 比较理想的状态,是你既能在项目里认真写好一段代码,也能在行业变化出现时判断它到底意味着什么。
到最后,开发者真正要培养的是一种持续校准自己的能力。 你要知道哪些东西是长期有效的,哪些东西正在变化;哪些东西值得现在投入,哪些东西只需要保持观察。 AI会改变很多具体工作,但不会改变这件事:一个工程师如果长期只待在自己的舒适区里,他的判断力一定会慢慢变窄。 而判断力一旦变窄,哪怕手里拿着再强的工具,也很难做出真正高质量的工程决策。