作者:Addy Osmani
版权所有 © 2025 Addy Osmani。保留所有权利。
在美国印刷。
由 O’Reilly Media, Inc. 出版,地址:141 Stony Circle, Suite 195, Santa Rosa, CA 95401。
O’Reilly 图书可用于教育、商业或销售促销用途。大多数书籍也提供在线版本 (http://oreilly.com)。如需更多信息,请联系我们的企业/机构销售部门:800-998-9938 或 corporate@oreilly.com。
采购编辑: Louise Corrigan
开发编辑: Sarah Grey
制作编辑: Katherine Tozer
文字编辑: nSight, Inc.
校对: Piper Content Partners
索引: Ellen Troutman-Zaig
内页设计: David Futato
封面设计: Susan Brown
封面插画: José Marzan Jr.
内页插画: Kate Dullea
2025年8月: 第一版
有关发布详情,请参阅 http://oreilly.com/catalog/errata.csp?isbn=9798341634756。
O’Reilly 标志是 O’Reilly Media, Inc. 的注册商标。《超越氛围编程》、封面图片及相关商业外观是 O’Reilly Media, Inc. 的商标。
本书中表达的观点为作者观点,不代表出版商的观点。尽管出版商和作者已尽诚信努力确保本书中包含的信息和说明准确无误,但出版商和作者对所有错误或遗漏不承担任何责任,包括但不限于因使用或依赖本书而造成的损害。使用本书中包含的信息和说明风险自负。如果本书包含或描述的任何代码示例或其他技术受开源许可或他人的知识产权约束,您有责任确保您的使用符合此类许可和/或权利。
本书不作为法律建议。如需法律建议,请咨询合格的专业人士。
979-8-341-63475-6
LSI
我们正处于软件构建方式深刻转变的时代。专业的氛围编程(vibe coding)——与AI协作创建软件的艺术——正在将开发者从代码工匠转变为产品愿景家和编排者。
氛围编程是关于利用AI的能力来处理编码的繁重工作,让开发者能够更多地专注于想法、设计和高层次的问题解决。正如 Andrej Karpathy 所言,这就像”忘记代码的存在”,只需构建——描述你需要什么,让AI填充实现细节。这可以带来数量级的生产力提升,使传说中的”10x工程师” 可能成为100x的现实。
本书面向三类核心读者。第一类是希望倍增其影响力的经验丰富的开发者和工程领导者。如果你已经编码多年并感受到重复性任务的负担,本书将向你展示如何将平凡的工作委托给AI,同时将你的角色提升为架构师和战略家。你将学会在不牺牲多年职业生涯中形成的质量标准的情况下更快地构建。
第二类是具有产品思维的工程师,他们将代码视为达成目的的手段而非目的本身。如果你对愿景与实现之间的差距感到沮丧,氛围编程可以极大地缩短这一距离。你将发现如何快速原型设计、迭代并交付使用传统方法需要数月时间的产品。
关于AI工具,我发现了一件最违反直觉的事情:它们对经验丰富的开发者的帮助大于初学者。这似乎很矛盾——AI不应该使编程民主化吗?
现实是,AI就像团队中有一个非常热心的初级开发者。他们可以快速编写代码,但需要持续的监督和纠正。你知道的越多,就越能更好地指导他们。
这产生了我所说的知识悖论(knowledge paradox):高级工程师和开发者使用AI来加速他们已经知道如何做的事情,而初学者试图用它来学习应该做什么,结果截然不同。
我观察到高级工程师使用AI来:
与此同时,初学者通常:
第三类是应对AI对其团队和流程影响的工程经理和CTO。你将深入了解如何在一个单个工程师可以完成过去需要团队完成的工作的时代里,构建团队、评估人才并保持代码质量。这里的策略将帮助你在保持工程文化完整性的同时顺利过渡。
你在这里找不到的是编程入门指南。虽然AI使编程更易于访问,但有效地使用它仍然需要来自经验的判断力。将本书视为为那些准备超越传统编程并拥抱软件创建新范式的人提供的高级培训。
本书探讨了开发者角色如何演变,从动手编程转向产品工程。这意味着运用人类判断来引导AI,确保质量、架构和用户需求得到满足。我们仍然提供创造力、系统思维和共情能力,将功能性程序转化为优秀产品。AI不会取代我们;它会增强我们——如果我们明智地使用它。
在第一部分中,我将识别氛围编码(vibe coding)擅长的领域:启动新产品、原型设计功能、生成标准的CRUD应用或集成代码——所有这些领域中,速度和模式匹配胜过深度原创性。相反,我也会审视我们对依赖AI仍需谨慎的地方:真正复杂、底层或新颖的算法,AI可能会在这些地方出错。认识到AI当前的局限性可以防止挫折和失败;仍有大量工作只有人类的创造力才能完成。
人的因素仍然是关键。我们确保架构合理、调试棘手的错误,并判断代码质量是否超越”能运行”。关键的是,我们将以用户为中心的思维融入开发——这是AI无法做到的。由我们来确保软件不仅能工作,而且能以有意义的方式为用户工作。简而言之,开发者成为AI输出的策展人和编辑者,始终使其与现实需求和高标准保持一致。
第二部分关注氛围编码的实践方面。采用新工作流程至关重要。“重新生成而非修复”这样的技术提醒我们不要陷入困境——有时重新生成代码比调试更快。并行提示(parallel prompting)让我们同时从多个角度解决问题。我们必须平衡快速迭代与最终改进,确保不会积累难以维护的混乱。模块化AI代码、全面测试和迭代改进等最佳实践有助于在高速开发的情况下保持代码库的整洁和健壮。
随着项目规模扩大,我们必须管理加速流入的代码和潜在的技术债务。AI可能让你的代码仓库充斥代码;只有纪律和良好的工程实践(加上可能的AI辅助重构)才能保持其可维护性。在人员方面,我们将招聘和培训擅长使用AI工具的工程师,重视适应性和系统设计技能。我们还会知道何时退回到传统模式——比如为了长期维护而巩固产品,或处理谨慎胜过速度的关键系统时。
第三部分涵盖安全性和可靠性、伦理,以及一系列使氛围编码成为可能的工具:像Cursor和Windsurf这样的AI增强IDE,它们集成了Anthropic、Google的Gemini和OpenAI的模型,以理解你的整个代码库并在每个环节提供帮助。知道应用哪些工具和模型(Claude的各种变体用于不同任务,ChatGPT用于一般问答)是新开发者技能集的一部分。它们各有优势:Cursor用于交互式编辑,Windsurf用于上下文密集型任务,聊天界面用于头脑风暴和故障排除等。
展望未来,我预计会有更抽象的软件构建方式(通过GUI和更高级别的输入进行”氛围设计”),对通用库的依赖会减少,因为AI会生成更多定制代码,甚至软件会基于AI反馈循环自主演进。在这个未来中,软件领域的成功将严重依赖于人类的创造力、分发能力和利用网络效应的能力,因为编码的暴力破解门槛将非常低。新的用户体验范式可能会出现,由AI的普遍性驱动——从对话界面到自适应UI等等。
在所有这些中,有一个主题突出:人类和AI优势的融合。单独任何一方都不如两者结合强大。AI带来速度、广泛的知识和不知疲倦的执行力。人类带来方向、深度理解和价值观。未来的最佳工作流程是一种共生关系——想象一下将大师级工匠与能够即时获取任何工具或参考资料的超级学徒配对。工匠的专业知识对于创造真正卓越的作品仍然至关重要。
对于阅读本书的开发者:是时候拥抱这些工具和范式了。本书将鼓励你在下一个项目中尝试AI编码助手,练习将问题分解让AI解决部分问题,并培养制作提示和策展结果的技能。但它也会敦促你加倍投入你的独特价值——你设计系统、与用户共情以及做出使软件与现实保持一致的判断的能力。
本书使用以下排版约定:
表示新术语、URL、电子邮件地址、文件名和文件扩展名。
等宽字体用于程序清单,以及段落中引用程序元素,如变量或函数名、数据库、数据类型、环境变量、语句和关键字。
此元素表示一般注释。
此元素表示警告或注意事项。
40多年来,O’Reilly Media一直提供技术和商业培训、知识和见解,帮助公司取得成功。
我们独特的专家和创新者网络通过书籍、文章和在线学习平台分享他们的知识和专业技能。O’Reilly 的在线学习平台为您提供按需访问的实时培训课程、深入的学习路径、交互式编码环境,以及来自 O’Reilly 和 200 多家其他出版商的大量文本和视频资源。如需了解更多信息,请访问 https://oreilly.com。
请将有关本书的意见和问题发送给出版商:
我们为本书建立了一个网页,在那里我们列出了勘误表和任何补充信息。您可以访问此页面:https://oreil.ly/BeyondVibeCoding。
有关我们的书籍和课程的新闻和信息,请访问 https://oreilly.com。
在 LinkedIn 上找到我们:https://linkedin.com/company/oreilly-media。
在 YouTube 上观看我们:https://youtube.com/oreillymedia。
AI 正在重塑我们构建软件的方式,引入了从自由形式提示(prompting)到结构化辅助的新编程范式(paradigm)。想象一下,通过简单地描述你想要做什么来编写软件——几乎就像和队友交谈一样——而 AI 将这些想法转化为代码。这就是氛围编程的本质,一种提示优先的探索性方法,你用自然语言描述你想要什么,让大语言模型(LLM)来填补空白。这个术语最近由 AI 先驱 Andrej Karpathy 提出,用来描述这种新的编程方式,开发人员”完全顺应 AI 辅助的氛围”。
在本书中,我将深入探讨氛围编程对专业开发人员的意义,以及它如何与我所说的 AI 辅助工程(AI-assisted engineering)——一种更正式的增强编码过程——进行比较和互补。我将探讨在这个 AI 优先的时代,开发人员的角色如何演变,哪些工具和工作流可以最大化你的效率,以及如何应对让 AI 在你的代码库中自由发挥的独特挑战。我还将研究氛围编程在哪些方面表现出色,在哪些方面遇到困难,以及如何平衡 AI 生成的速度与人类监督的智慧。最后,你应该清楚地了解如何在自己的编码实践中负责任且有效地利用”氛围”——在 AI 时代成为不仅更快,而且更具创造力和影响力的软件产品工程师。
在本章中,我们探讨开发人员的角色如何从为机器编写详细指令转变为通过表达意图(intent)与 AI 协作(见图 1-1)。我们将看到为什么编程中的这种”氛围转变”如此重要,它在高层次上如何工作,以及它带来了哪些机遇和挑战。
在过去的一年里,我观察到开发人员——尤其是中高级 Web 开发人员——在工作流程中接受 AI 的方式出现了有趣的分化。谱系的一端是氛围编程。另一端是我所说的 AI 辅助工程:一种有纪律的方法,在明确的约束下,将 AI 融入软件开发的各个阶段,从设计到测试。这两种方法都利用了强大的 AI,但它们的目标、受众和期望明显不同。在本书中,我将探讨这两个极端及其对现代 Web 开发的意义。
在 vibe coding 中,你利用强大的大语言模型(LLM)作为编程伙伴,让它们处理代码生成的繁重工作,这样你就可以专注于更高层次的目标。正如《商业内幕》的一篇总结所说,vibe coding “意味着使用 AI 工具……来处理编码中的繁重工作,从而快速构建软件。” 正如英伟达(NVIDIA)首席执行官黄仁勋所说,得益于 AI,“最热门的新编程语言”是英语,而不是 Java 或 Python。你不需要手动输入每个函数和错误修复,而是用自然语言与 AI 交互——勾勒出功能、审查建议,并根据 AI 的输出进行迭代。
这种方法代表了从传统编程到 AI 辅助开发的巨大转变。传统编码需要仔细规划、精确的语法,以及经常令人痛苦的调试过程。Vibe coding 颠覆了这一模式:“这不是真正的编码——我只是看东西、说东西、运行东西、复制粘贴东西,大部分情况下都能正常工作,”Karpathy 在接受《商业内幕》采访时开玩笑说,强调了 AI 如何将高层次的指令转化为可工作的代码,而只需最少的手动工作。
开发者从为计算机编写详细指令转变为在 AI 的帮助下编排结果。例如,Karpathy 描述了通过不断接受 AI 的建议来构建 Web 应用:“我总是’全部接受’,我不再阅读差异了……当我收到错误消息时,我只是复制粘贴进去……有时候大语言模型无法修复错误,所以我就绕过它或要求随机更改直到错误消失。” 代码”增长”超出了他通常自己编写的范围,但通过迭代提示和修复,项目很快就完成了。本质上,vibe coding 将编码视为与你的 AI 配对程序员进行的互动对话,而不是独自在语法和堆栈跟踪中艰难前行。目标是速度和探索——以最小的阻力获得可工作的解决方案。
几个趋势汇聚在一起,使 vibe coding 成为可能。首先,现代 AI 编码助手(如 OpenAI 的 Codex、ChatGPT、Anthropic 的 Claude 等)在生成和纠正代码方面变得惊人地出色。在同一篇文章中,Karpathy 指出这”之所以可能,是因为大语言模型……变得太好了”——它们已经消化了 GitHub 代码的大量内容,可以为许多任务生成合理的解决方案。
其次,新的开发者工具已经出现,可以将这些模型无缝集成到编码工作流程中(稍后会详细介绍这些工具)。最后,开发者社区的心态正在演变,越来越信任 AI 协助处理越来越大的工作块。这不再只是增强版的自动补全;而是将整个函数或文件交给 AI。实际上,vibe coding 通常感觉就像拥有无限供应的热心初级开发者来实现你要求的任何东西——只不过他们以云计算的速度工作。
Vibe coding 最令人瞩目的承诺之一是生产力的提升。早期采用者报告说,他们能够比以前快十到一百倍地创建软件功能或原型。例如,Codeium Windsurf 工程师 John Hoestje 思考道:“为什么要成为 10 倍工程师,而你可以成为 100 倍工程师呢?” 这表明,使用正确的 AI 驱动的集成开发环境(IDE),超凡的生产力是触手可及的。像 Windsurf 这样的工具,一个 AI 增强的 IDE,“可以显著加快开发时间,让你实现那 100 倍的生产力。” 虽然 100 倍可能是一个极端场景,但即使是更保守的研究也发现了巨大的收益。
开发者可以在几秒钟内生成样板代码,在眨眼间修复错误,甚至让 AI 编写测试或文档,将过去需要几天的工作流程压缩到几个小时。不再受打字速度或内存的限制,一个配备 AI 的开发者通常可以在一个周末内原型化一个全栈应用程序——这在过去可能需要一个小团队花费数周才能完成。这不仅仅是炒作;正如我在 2025 年 1 月为《务实工程师》撰写的博客文章中指出的那样,调查显示 75% 的开发者已经将某种形式的 AI 整合到他们的工作流程中,许多公司报告开发速度提高了两位数或三位数的百分比。简而言之,AI 配对程序员正在将神话般的”10 倍工程师”变成一个非常真实(且可达到的)100 倍工程师现象。
要理解这有多革命性,让我们看一个具体的例子。一位开发者想要构建一个简单的网页应用,用于统计播客脚本的字数并估算阅读时间。他们不是从零开始,而是打开一个AI驱动的编程环境,向AI描述自己的想法。几分钟内,AI就生成了一个可用的原型。然后开发者说:“把统计计数器改成亮色,并添加PDF导出功能”,AI就相应地更新了代码。结果是一个功能完整的工具,一键部署——所有这些在10分钟内完成。这个真实场景(由一位使用Replit AI的创作者报告)展示了vibe coding如何实现由高层次需求驱动的极快速迭代开发。同样,非工程师也加入进来:同一篇文章描述了一位被裁员的营销人员,没有任何编程背景,使用AI编程助手构建了100个简单的网页工具,这些工具集体登上了Product Hunt榜首。当创建软件的门槛降到如此之低时,我们不仅仅是在提高资深开发者的生产力——我们从根本上扩大了谁能够开发软件。
然而,vibe coding也有严重的注意事项。因为你将太多工作委托给AI,你最终可能得到一个在正常路径下”能用”但隐藏着大量bug或糟糕设计决策的代码。没有可靠的计划或约束,LLM可能会生成缺乏适当错误处理、安全检查或可扩展性的解决方案。事实上,AI生成的代码有时可能是建立在沙子上的:它看起来很稳固,但有隐藏的问题,只有在实际条件下才会暴露出来。我见过这样的案例:开发者以创纪录的速度通过vibe的方式完成了一个完整的功能,却在后来发现代码效率低下且难以维护。这种”纸牌屋”代码在压力下会崩溃。
例如,想象一下要求AI”快速搭建一个用户登录系统”。AI可能会快速生成一个可用的身份验证流程,但也许它使用了简化的加密方法或已知有漏洞的库。如果你在没有深入检查的情况下部署它,你就是在盲目相信一切都是健全的。资深工程师知道这是有风险的:在生产环境中运行的代码必须是被理解和信任的。正如一位专家所说:“通过vibe coding方式构建生产代码库显然是有风险的。我们作为软件工程师所做的大部分工作涉及演进现有系统,其中底层代码的质量和可理解性至关重要。”极端的vibe coding可能会绕过这些质量关卡。
另一个挑战是vibe coding倾向于轻视前期规划。传统软件工程重视为清晰和约束而设计——思考数据模型,选择适当的模式,并至少写出最小化的规格说明。Vibe coding颠覆了这一点:它从没有脚手架开始,通过提示直接投入实现。这可能导致曲折的开发过程。你可能通过提示把自己逼入困境——比如AI选择了你不打算使用的状态管理方法或库,现在你要么引导它回到正轨,要么接受它。没有初始蓝图,最终的架构可能是杂乱无章的。这对于快速的概念验证(proof of concept)来说还好,但在需要一致性的大型代码库中就有问题了。
Vibe coding本质上并不”坏”。事实上,它的出现是编程民主化(democratization of programming)持续进程的一部分。它降低了创建软件的门槛,就像早期的低代码平台或脚本语言所做的那样。一个有明确想法的积极的非工程师可能仅通过vibe就能构建一个简单的应用。对于经验丰富的开发者来说,vibe coding可以是一个强大的头脑风暴工具——它就像伪代码但能立即得到可运行的结果。关键是要认识到它的局限性。没有纪律的速度可能导致脆弱的软件,所以vibe coding需要一个警觉的人在循环中。我经常提醒开发者(和我自己)“vibe coding不是低质量工作的借口”。它应该是解决方案的起点,而不是终点。
在我们的范围的另一端是AI辅助工程——一种更结构化、更有条理的软件构建方式,AI在每一步都作为副驾驶。在这里,开发者仍然牢牢地坐在驾驶座上。AI辅助工程包括在传统软件开发生命周期(SDLC)中使用AI,例如AI驱动的自动补全、聊天、代码迁移、bug检测、测试生成,以及细粒度(函数、模块、组件)和完整代码生成(见图1-2)。
你从一个计划开始(即使是轻量级的),概述你需要构建什么,并预先定义约束和验收标准。然后你以有针对性的方式融入AI工具来加速或增强计划的某些部分。与提示优先的氛围编码相反,我们可以称之为”计划优先”的AI支持开发。这可以正式到像一个迷你产品需求文档(功能的简短PRD),也可以简单到像一个任务清单。关键区别在于,在让AI开始工作之前,你将工作建立在明确的意图和约束之上。
考虑一个React开发者被要求创建一个新的交互式仪表板组件。在AI辅助工程方法中,他们可能会从写下组件的职责和API开始:
仪表板组件显示分析卡片列表,支持按日期范围过滤,并有刷新和导出按钮。它应该从我们的API获取数据(具有适当的错误处理),并且必须遵循我们的设计系统进行样式设置。
这个大纲本质上就是一个规范。开发者甚至可能快速勾画一个数据模型或识别要重用的现有实用函数。只有在那之后,他们才引入AI:例如,使用支持AI的IDE或编码助手根据该描述生成组件的框架。AI可能会提供React组件的初始实现,包含数据获取的占位符和存根事件处理程序。因为开发者提供了清晰的指导,AI的输出更有可能与项目需求保持一致(例如使用正确的设计系统类或调用正确的API端点)。代码不是意外的产物;它是精心设计的请求的产物。
AI辅助工程不会止步于单个组件的代码生成。它以受控的方式渗透到整个开发生命周期中。对于日常编码任务,像GitHub Copilot这样的AI自动补全工具可以在你输入时建议接下来的几行,当你实现已知模式时节省击键次数。例如,当你编写单元测试时,你的AI助手可能会根据函数名自动建议断言。说到测试,一旦功能就位,你可能会使用AI生成测试用例——将组件的规范或代码输入提示中,以获得你应该检查的边缘情况的建议。这个想法是增强工程师的工作,而不是取代它。你仍然在思考逻辑并验证正确性;AI只是减轻了一些繁重的工作。
当涉及到代码迁移或重构时,AI可以是天赐之物。想象一下需要将基于类的React组件转换为带有hooks的现代函数组件。与其全部手动完成,你可以要求AI助手转换代码或至少概述步骤。通过对旧模式和新模式的良好理解,LLM可以生成重构代码的草稿,然后你进行审查和完善。这种结构化的AI使用逐个处理明确定义的任务(例如”将此代码从Redux迁移到React Context API”),而不是给AI一个开放式的”构建任何东西”的任务。
也许AI辅助工程最引人注目的形式是使用AI从详细规范生成完整的迷你应用程序或功能。现在有几个工具允许你输入应用程序的描述,类似于迷你PRD,并返回一个工作代码库或原型。例如,开发者可以提供以下规范:
一个带有React前端和Node.js后端的待办事项列表应用,支持用户认证和实时更新
这个 AI 工具将搭建(scaffold)项目、创建关键组件并设置数据库模式(schema)。
这并非魔法,而是一个勤勉的工程师在启动新项目时可能会做的事情的加速版本(设置目录、选择库、编写样板代码)。重要的是,AI 的创造力受到规格说明中给出的约束条件的限制。结果是一个遵循您提供的需求的最小可行产品(MVP)。有经验的开发者正确对待这个输出时,不会假设它在第一次生成时就已经可以投入生产。相反,他们会将其视为初稿。他们会运行应用程序、编写或重新生成测试来验证每个功能、检查代码是否存在任何不一致或不安全的配置,并根据需要进行优化。简而言之,他们会应用所有常规的工程严谨性——只是通过 AI 从蓝图生成大量代码的能力来加速。
AI 辅助工程的目标与随性编码不同。这里的目标不仅仅是快速获得能用的代码,而是更高效地获得高质量的代码。这是在保持(甚至改善)结果可靠性的同时提升生产力。实践 AI 辅助工程的团队可能会说:“我们希望以两倍的速度交付这个功能,但在我们的标准上零妥协。”
这种方法的受众通常是专业开发者和已经建立了流程(代码审查、测试、部署流水线)且不愿放弃的团队。这些是中高级工程师,他们将 AI 视为工具箱中一个强大的新工具,而非工具箱的替代品。他们可能见过走捷径的后果,因此重视保持软件可维护性的实践。(相比之下,随性编码的受众包括拼凑演示的独立开发者、具有一定编码知识的产品导向人士,甚至是利用 AI 来弥补专业知识空白的相对较新的程序员。)
AI 辅助工程的预期是人类保持对决策的控制,而 AI 提供建议或加速器。代码质量、性能和安全性仍然至关重要,因此每一段 AI 生成的代码都要接受与初级开发者编写的代码相同的审查。将 AI 视为您的实习生,而非您的替代者。您可能会将任务委派给它,但必须审查它的工作。正如您永远不会在没有代码审查的情况下部署人类实习生编写的代码,您也不应该在不理解的情况下部署 AI 编写的代码。这种思维方式将工程纪律置于首要位置。
随性编码和 AI 辅助工程是两种截然不同的思维方式。随性编码是自上而下和探索性的:您从一个宽泛的想法开始,通过与 AI 的交互让实现逐渐显现。这有点像即兴爵士乐——结构最少,大量创意即兴空间,您在演奏过程中发现曲子的形态。AI 辅助工程是系统化和迭代的:更像古典作曲,您从一个主题或动机(您的需求)开始,有条不紊地发展它,可能在乐谱的小节内使用一些即兴创作(AI 建议)。两者都能产生”音乐”,但过程和结果类型会有所不同。
对于中高级 Web 开发者来说,您对每种方法的预期是关键。如果您在进行随性编码,您预期会有惊喜。AI 可能会想出一种您自己不会编写的方法——也许它使用了不同的库或您不太熟悉的编程习语。部分吸引力在于从这些惊喜中学习,或快速越过您觉得乏味的事情。但您也需要预期会有小波折。随性编码爱好者应该睁大眼睛意识到他们将对那段棘手的最后阶段负责。魔力是真实的,但不是全面的。
如果您在实践 AI 辅助工程,您的预期会更加谨慎,对于长期项目来说可以说更加现实。您期望 AI 为您节省时间,也许启发一两个解决方案,但不会做您的全部工作。事实上,一个优秀的 AI 辅助工程师可能会在更大的框架内小剂量使用随性风格的提示。例如,在实现一个明确指定的模块时,他们可能会暂时切换到”随性模式”问:“嘿 AI,生成一个快速的实用函数来格式化这些日期”,然后立即切换回工程师模式来集成和检查该函数。这种思维方式是将 AI 视为在您指导下工作的协作者。您将任务分配给它擅长的地方(如样板代码、重复性代码、粗略实现),而您自己处理其余部分(关键逻辑、集成、最终审查)。
这里的期望包括提高生产力,减少机械性错误(例如,AI 不太可能拼错变量名),以及可能更广泛的解决方案搜索空间(AI 可能会建议你没有想到的算法)。但你也需要投入时间进行验证。调试 AI 辅助代码仍然是调试:你运行测试,必要时在调试器中逐步执行代码。不同之处在于,你可能会发现自己在调试 AI 为你编写的代码,这是一种新体验,伴随着学习曲线。第 5 章将详细讨论这一体验。
这两种方法的目标突出了它们之间的根本差异:氛围编程(vibe coding)优化的是短期速度,而 AI 辅助工程优化的是持续速度和可靠性。氛围编程者可能会说:“我需要在今晚之前让这个应用运行起来,看看这个想法是否可行。”AI 辅助工程师则会说:“我需要快速构建这个功能,但它应该足够健壮,能够在我们的代码库中存在多年。”前者只要代码基本能运行就满意;后者关心的是代码是否足够清晰,以便其他人在此基础上继续构建。
这些差异自然会吸引不同的受众。经验较少的开发者或工程学科之外的人可能倾向于氛围编程,因为它降低了入门门槛并提供了即时满足感。我遇到过产品经理和设计师通过氛围提示(vibe prompts)涉足编码,将 AI 几乎当作一个超强版的 Stack Overflow,为他们提供完整的解决方案。另一方面,经验丰富的开发者和工程团队往往更青睐 AI 辅助工程。他们曾因脆弱的代码吃过亏,所以他们从”让我们做对,即使我们使用新工具来加快速度”的立场出发。他们在前期投入更多努力(编写那个迷你 PRD,设置项目结构),以换取长期回报。
人们很容易问:哪种方法更好?事实上,氛围编程和 AI 辅助工程并不是互相排斥的类别:它们代表了光谱的两端,现实世界的工作流程通常会融合两者的元素。开发者可能以一阵氛围编程开始项目,以搭建一些新颖的东西,然后切换到工程模式来巩固它。或者他们可能通常遵循 AI 辅助规范,但偶尔——对于一个微不足道的一次性脚本或一个临时原型——会说:“你知道吗,我就用氛围编程来做这个,看看会得到什么。”关键是理解权衡,并在正确的情境中使用正确的方法。
把氛围编程想象成一辆高速探索车:它可以快速带你离开既定路径,非常适合探索发现。AI 辅助工程更像是轨道上的可靠列车:你必须先铺设轨道(计划),但这是一个更安全的选择,更有可能到达既定目的地而不脱轨。中级和高级开发者应该能够驾驭这两种工具,但他们会根据手头的任务进行选择。如果目标是快速创新或构思(比如在黑客马拉松中或验证想法的可行性时),氛围编程提供了动力。只需记住,如果你计划重用该代码,要加以完善。如果目标是在专业环境中构建可维护的产品功能,倾向于 AI 辅助工程可确保你不会在代码库中留下一个没人真正理解的黑盒代码块。
我观察到的一个有趣现象是,随着开发者使用 AI 工具的经验增加,他们的使用往往自然地从氛围端转向工程端。最初,让 AI 从单个提示生成整个代码块的新奇感很诱人——谁不想尝试本质上”谈话”出一个应用程序呢?
但在蜜月期之后,实用主义开始发挥作用。开发者开始看到 AI 的优势所在以及它的不足之处。他们学会分解问题,并将它们分块提供给 AI,而不是在一次性要求整个解决方案。实际上,他们从”提示艺术家”转变为 AI “管弦乐团指挥”——仍然利用 AI 的创造力,但用熟练的手引导它,并遵循清晰的乐谱。在我自己的实践中,我在提示方面变得更加deliberate(深思熟虑),经常编写小段伪代码或注释,并要求 AI 完成它们,而不是只是提出开放式问题。这样,我在自己控制的结构内获得了类似氛围的流畅性的好处。
值得注意的是,工具正在演进以支持整个谱系。一方面,我们有基于聊天的界面和自然语言编码环境,专门为氛围式编码设计,你甚至可能在询问之前都看不到代码。另一方面,IDE 正在添加 AI 功能,无缝融入传统编码:例如,建议改进的 AI 代码检查工具(linter)、解释代码的文档生成器,以及可以自动创建拉取请求并建议审查更改的版本控制机器人。这些工具通过融入常规开发工作流程(编辑、审查、测试等)来鼓励工程思维,同时仍然利用 AI。
随着最佳实践的出现,氛围式编码和 AI 辅助工程之间的区别甚至可能会随着时间的推移而模糊。我们可能会发现,今天感觉像”氛围式”的方式将获得更多护栏,而感觉像”结构化工程”的方式将变得更加流畅。事实上,我认为理想的未来是我们可以毫不费力地在这个谱系上下移动:当我们想要时与 AI 一起探索创造性解决方案,但在需要强化和交付软件时始终用扎实的工程实践加以约束。
这种方法谱系代表了我们今天使用 AI 工具方式的重大演变。然而,即使我们改进了与 AI 协作的技术——无论是通过快速的氛围式编码还是结构化的工程工作流程——一个更根本的转变正在形成。编程本身的本质正在发生变化。我们正在从传统的范式转变,在传统范式中,开发人员必须将他们的想法转化为明确的指令,而转向未来,我们可以直接表达我们的意图,让 AI 处理到代码的转换。
这种转变挑战了我们关于成为程序员意味着什么的最基本假设。几代人以来,我们的价值一直与我们像机器一样思考的能力相关——将问题分解为计算机可以执行的离散逻辑步骤。但是,当机器能够理解我们想要什么,而不仅仅是我们告诉它们做什么时,会发生什么?这就是意图编程进入画面的地方,它不仅代表了一个新工具或技术,而是对开发人员角色的根本重新构想。
几十年来,编程意味着编写指令:一行又一行的代码告诉计算机如何做某事。每个函数、循环和条件语句都必须由人类精心制作。意图编程翻转了这个脚本。开发人员不再关注底层实现,而是关注结果或目标:你希望程序完成什么。你以高层次的方式(通常用自然语言)表达这种意图,AI 系统会找出实现它的代码。
这样想:传统编码就像给某人一步一步的指示,而基于意图的编码就像告诉他们你的目的地,让他们找出最佳路线。通过关注什么而不是如何,开发人员可以在更高的抽象级别上工作。这种方法并不完全是新的——可视化编程、低代码平台和代码生成器等工具长期以来一直承诺提高抽象级别。但今天的 AI 进步终于使用简单语言描述复杂行为并获得可工作的代码变得切实可行。
这种转变的核心是简单的提示词。提示词是你提供给 AI 编码系统的输入或问题。本质上,它是对你希望程序做什么的描述,而不是如何做的指令。这与编写代码的感觉可能非常不同。例如,与其编写一个循环来解析文件,你可能会提示:
读取这个 CSV 文件并提取所有年龄大于 18 岁的用户的电子邮件地址。
AI 将尝试生成完成该描述的代码。
为什么现在会发生这种情况?LLM 在理解和生成文本(包括编程语言)方面的快速进步一直是一个改变游戏规则的因素。这些 AI 模型已经在大量代码和自然语言文本上进行了训练。它们可以解释看起来像软件行为描述的提示词,并将其转换为实现该行为的实际代码。换句话说,它们已经学会了人类如何描述任务以及这些任务如何转化为代码的模式。
基于提示词的开发的兴起意味着,作为开发者,你越来越多地用自然语言或伪代码来描述功能和逻辑,让 AI 来处理编写语法正确代码的繁重工作。提示词成为你的新思维单元。它是意图的简洁表达。我们已经从告诉计算机”先做 X,然后做 Y,再做 Z”转变为说”我需要完成 X、Y 和 Z”,并相信 AI 会填补空白。
重要的是要注意,编写好的提示词本身就是一项技能(我们将在[第3章]中深入探讨)。模糊的提示词可能导致错误或低效的代码,就像模糊的需求会让人类程序员感到困惑一样。你越能清楚地表达你的意图,AI 的输出就越能满足你的需求。这就是为什么许多人称提示词编写为新的编程素养(programming literacy)。
那么 AI 是如何从你的自由形式描述转化为实际的、可运行的代码的呢?魔力在于大语言模型(LLM)解释上下文和生成文本的能力。“大语言模型”中的”大”指的是它拥有的参数数量(内部配置),通常是数十亿或更多,这使它能够捕捉自然语言和编程语言的复杂性。这些模型已经在公共代码仓库、论坛、文档和问答网站上进行了训练,学习了编程语言的语法以及如何使用代码解决问题的语义。当你与 AI 编码器交互时,你正在利用这种广泛的学习知识。让我们用简单的术语分解一下:
当你提供提示词时(例如,“生成一个检查数字是否为质数的函数”),AI 模型会分析该提示词的文本。Google、OpenAI 和 Anthropic 的现代模型已经在无数语言和代码示例上进行了训练,因此它们使用统计模式来推断你在问什么。本质上,AI 试图预测最有可能用有意义的代码完成提示词。
这些 AI 系统通常会考虑除单行提示词之外的额外上下文。例如,如果你在带有 AI 助手的 IDE 中工作,模型可能还会考虑当前文件内容、你的编码风格、注释,甚至相关文件。所有这些上下文都有助于 AI 生成适合你项目的代码。这类似于人类开发者阅读周围的代码和文档来理解下一步该做什么。
一旦模型理解了(或至少对你的意图做出了最佳猜测),它就会开始生成代码。在底层,它使用训练期间学到的概率一次生成一个标记(token)(标记是单词或代码符号的一部分)。该模型不会以传统意义上”思考”;它没有编译器或运行时来检查代码。它只是非常擅长以一种很有可能是正确代码的方式继续文本,因为它之前见过很多例子。如果提示词和上下文清晰,它生成的代码可能非常准确,甚至遵循它在训练数据中看到的最佳实践。
重要的是,AI 不会自行运行并为你部署应用程序。你仍然在循环中。你审查生成的代码,测试它,可以接受或修改它。在许多情况下,如果被问到,AI 还可能提供代码的解释,帮助你理解结果。AI 的角色就像一个为你起草代码的助手——但你,开发者,仍然是决策者,确保代码正确并符合项目的需求。
真正令人印象深刻的是,这个过程在几秒钟或更短的时间内就能完成。高层概述是,你的描述(提示词)进入预测引擎(大语言模型),它产生可能的代码作为输出。虽然模型的内部工作涉及复杂的数学和神经网络层,但在用户层面,它感觉几乎像是在与一位能够立即回忆起如何实现几乎任何东西的专家合作。
关于氛围编码(vibe coding)(基于意图的编程)要理解的关键之一是,它是人类和 AI 之间的迭代协作过程。你不只是写一个完美的提示词,然后坐等 AI 完美无瑕地编写整个程序。在实践中,你进行来回交流,一个反馈循环,逐渐将模糊的想法转化为完善的代码。
以下是一个典型的循环:
这是你的初始提示词或请求。例如:
生成一个函数,根据本金、利率和期限计算月度贷款还款额。
AI 为该函数生成代码,包括参数和贷款还款公式。它甚至可能包含解释公式的注释。
你查看代码。它合理吗?它处理边缘情况(edge case)了吗?你快速测试:如果利率为0会怎样?它能正确运行吗?你注意到它可能无法很好地处理这种情况。
如果代码不完美(通常第一次尝试都不会完美),就优化它。也许你再次提示AI(“修改函数以优雅地处理0%利率”),或者自己编辑代码并告诉AI,“解释这部分”,如果有什么不清楚的话。这种指导有助于纠正任何误解。
AI接受你的反馈或新提示并调整代码。现在函数检查零利率并适当地处理它。
你继续这个循环直到满意。也许接下来你要求AI为这个函数生成单元测试(unit test)以确保它正常工作。它这样做了,然后你运行它们来验证一切正常。
这种协作非常像结对编程(pair-programming)场景,其中一个伙伴是人类,另一个是AI助手。人类设定方向并了解高层需求,而AI提供建议,编写样板代码(boilerplate),并加快繁琐的部分。对于复杂任务,两者单独都不够有效:AI依赖人类提供方向和验证,而人类将一些工作卸载给AI以更快地推进。
至关重要的是,迭代不仅仅是修复错误;它也是关于演化解决方案。你可能从一个非常粗略的提示开始,然后随着你看到AI产生的内容,逐步优化你的意图。
这鼓励了一种实验心态。如果第一次尝试不对,你没有浪费太多时间——只需优化提示或调整代码并再次尝试。在传统编码中,编写一个模块却只能丢弃它可能令人沮丧,但使用AI生成的代码,错误开始的成本很低,这鼓励探索不同的方法。
为什么带有意图的编程(programming with intent)如此重要?这种转变有几个深远的影响:
: 也许最直接的好处是速度。当AI处理日常工作时,开发者可以更快地完成任务。手工编写可能需要数小时的常规代码(如设置数据库模型、API端点或数据清理脚本)通常可以在几分钟内生成。关于AI编码助手的早期研究支持这一点:使用GitHub Copilot等工具的开发者被证明可以显著更快地完成任务(一项研究发现,在Copilot协助下,给定任务的时间减少了55%)。当你将这些收益乘以整个项目时,它暗示了一个软件开发周期大幅缩短、团队可以更快迭代的未来。
: 除了原始速度之外,还有心理上的好处。编写样板代码或查找语法可能会打断程序员的心流和思路。有了AI处理许多这些中断,开发者可以专注于他们正在解决的问题。许多用户报告说,在AI的帮助下,他们对繁琐任务感到不那么沮丧,可以专注于编码的创意和设计方面。换句话说,它可以通过卸载无聊的部分使编码更有趣,这反过来可以提高工作质量(更快乐的编码者通常会产生更好的代码)。
: 传统上,编程需要学习严格的代码语法以及各种库和框架(framework)的怪癖。通过基于意图的编程,其中一些负担转移到AI身上。新手可能不记得打开文件的确切语法或绘图函数的参数,但如果他们能描述他们想要什么,AI可以填充这些细节。这并不意味着任何人都可以在零知识的情况下编写复杂系统(你仍然需要理解程序应该做什么),但这确实意味着产生有用结果的学习曲线更短。可以想象,领域专家(如生物学家或经济学家)可以通过描述他们的需求在他们的领域编写原型(prototype),即使他们不是专业开发者。从这个意义上说,编程对那些有想法和意图但没有深厚编码技能的人来说变得更容易获得。
随着 AI 承担更多代码生成工作,人类开发者的角色正在演变。架构设计、问题分解和验证等技能变得更加重要。你可能会发现自己花更多时间决定要构建什么,以及审查代码为什么有效(或无效),而不是敲出语法。“懂得如何编码”的本质可能会转向”懂得如何让 AI 编码”。这可能会使软件开发的某些方面民主化,同时也会提升专业人士的操作层次。我们可能会看到围绕如何有效引导 AI 的新最佳实践(我将在[第3章]中介绍这个主题,并在整本书中重新审视)。
有趣的是,随着 AI 处理更多常规编码工作,人类开发者可以专注于更高层次的创造性任务,比如优化用户体验、头脑风暴新功能,或解决 AI 可能无法很好独立解决的棘手算法问题。在这种理想情况下,AI 提高了 80% 重复性编码工作的生产力,释放你的精力用于那 20% 的创造性工作。这是我们分配精力方式的转变。
然而,这并不全是好消息。这种新的开发方式也带来了挑战:
你能信任 AI 编写的代码吗?如果你没有看到每一行代码,就有可能会忽略错误。开发者需要彻底测试和审查 AI 生成的代码。确保输出正确、安全和高效的责任在人类身上。盲目信任 AI 输出是有风险的,我们将讨论这一点。
如果你依赖 AI 进行常规编码,你是否会逐渐失去从头编写代码或深入调试问题的能力?这种担忧类似于过度依赖计算器会削弱算术能力。开发者需要有意识地在便利性和保持对基础知识的扎实理解之间取得平衡。
随着意图编程(programming with intent)变得普遍,行业可能会重视不同的技能。对那些只擅长编写样板逻辑的人的需求可能会减少,而对那些能够设计系统、集成组件和验证正确性的人的需求会更多。软件工作的性质可能会转变,AI 处理更多实现工作,而人类专注于设计和监督。
此外,“vibe coding”中最关键的因素之一是上下文窗口(context window)大小。Gemini 提供了所有 AI 模型中最长的上下文窗口,这在处理大型项目时可能改变游戏规则。一些模型现在支持超过一百万个 token 的上下文窗口,使它们能够保持对整个应用程序的感知。开发者可以将整个代码库提供给 AI 以获得全面的理解。
我们将在本章末尾更深入地探讨这些权衡。但首先,让我们熟悉一下支持这种新编码方式的新兴工具。
Vibe coding 可能是一种哲学,但它是由新一代 AI 驱动的工具实现的。想要采用这种工作流程的经验丰富的开发者需要熟悉一些使 AI 辅助编码有效的关键平台和模型。
本节快速介绍 vibe coder 工具包中的基本工具。这些工具包括 Visual Studio Code (VSCode) 及其不断增长的 AI 功能和扩展生态系统、像 Cursor 和 Windsurf 这样的下一代 AI 集成 IDE、像 Claude(各种版本)这样的 LLM,以及 ChatGPT。本节不涵盖后台编码代理(background coding agents),但我会在[第10章]中详细讨论它们。
当你阅读本节时,不要担心记住特定的工具名称或功能;格局正在快速发展。目标是了解可用解决方案的类型。
VSCode 已经从世界上最受欢迎的代码编辑器转变为一个全面的 AI 辅助开发平台,通过与 GitHub Copilot 的深度集成。这种演变代表了微软的愿景,即在数百万开发者每天已经使用的熟悉的 VSCode 环境中保持 AI 能力。
GitHub Copilot 是一个集成到 VSCode 中的 AI 驱动编码助手。它基于自然语言提示和现有代码上下文提供代码建议、解释和自动化实现。这种集成的独特之处在于其无缝性——Copilot 不仅仅是一个附加组件,而且感觉像是编辑器本身的自然延伸。
VSCode的AI能力核心围绕三种主要的交互模式。首先是内联代码自动补全,Copilot在您输入时提供内联代码建议,从单行补全到整个函数实现。当您编写代码时,会出现带有建议的幽灵文本,您可以用Tab键接受或逐字部分接受。
其次是聊天界面,通过侧边栏面板访问,您可以在其中就代码进行对话、提问或请求特定实现。第三个,也许是最强大的,是agent模式,它使用工具调用(tool calling)来访问Visual Studio内不断增长的能力集。当给定一个目标时,它会逐步选择并执行正确的工具。这个agent模式可以分析您的代码库,跨多个文件提出编辑,运行终端命令,响应构建错误,并在循环中自我纠正直到任务完成。
VSCode的Copilot实现特别引人注目的是它对模型上下文协议(Model Context Protocol, MCP)的支持。MCP为AI模型提供了一种标准化的方式来发现和与外部工具、应用程序和数据源交互。这意味着VSCode中的Copilot可以连接到数据库、调用API、访问文档,并与您的整个开发生态系统集成。例如,启用GitHub MCP服务器后,您可以要求Copilot”为我们讨论的每个bug创建一个issue”,它将直接与GitHub的API交互来创建这些issue。通过MCP的可扩展性(extensibility)将Copilot从代码生成器转变为一个全面的开发助手,它不仅理解您的代码,还理解您的整个工作流。
要在专业开发中有效利用VSCode和Copilot,首先根据任务复杂性探索不同的交互模式。对于简单的代码补全和重构,依赖内联建议和出现在错误附近的闪光图标—点击它获取AI驱动的修复。
对于更复杂的任务,通过打开聊天面板并从下拉菜单中选择”Agent”来切换到agent模式。Agent模式针对在项目中跨多个文件进行自主编辑进行了优化。它特别适用于不仅需要代码编辑还需要调用工具和终端命令的复杂任务。VSCode熟悉的界面与Copilot不断发展的AI能力的结合,为希望在不离开其既有开发环境的情况下获得企业级AI辅助的团队提供了一个引人注目的选择。
在探索专用AI IDE之前,值得研究Cline(以前称为Claude Dev)如何将VSCode转变为强大的AI辅助开发环境。Cline代表了与Microsoft的Copilot不同的理念。它不是一个紧密集成的助手,而是作为一个自主编码agent运行,可以从头到尾承担复杂的多步骤开发任务。这个开源扩展为VSCode带来的能力通常超过专有AI编辑器中的能力,同时保持了VSCode用户期望的灵活性和可扩展性。
Cline的独特之处在于其真正的代理式(agentic)软件开发方法。当您给Cline一个高级请求,如”创建一个带身份验证的用户管理REST API”时,它不会简单地生成样板代码。相反,它会分析您的项目结构,规划跨多个文件的实现,创建适当的文件夹层次结构,安装必要的依赖项,甚至可以运行测试来验证实现。在整个过程中,Cline通过向您展示每个计划的操作—文件创建、修改和终端命令—并让您有机会批准或修改每个步骤来保持透明度。这种人在回路中(human-in-the-loop)的设计在自动化和控制之间提供了完美的平衡,允许开发人员利用AI的能力,同时保持对代码库的监督。
Cline的技术能力远远超出了代码生成。它可以使用浏览器自动化来研究API文档,通过分析跨多个文件的错误跟踪来调试复杂问题,甚至可以通过其MCP支持与外部服务交互。对于调试,您可以粘贴错误消息,Cline将跟踪您的代码库以识别根本原因,提出修复方案,实现它,并添加适当的错误处理以防止类似问题。它的MCP集成意味着Cline可以连接到您的数据库以在生成查询之前理解模式(schema),访问您的项目管理工具以使实现与需求保持一致,或与任何其他兼容MCP的服务交互。这种可扩展性将Cline从代码生成器转变为一个全面的开发伙伴,它理解您的整个技术生态系统。
对于团队而言,Cline 提供了几个引人注目的优势。作为开源项目,团队可以检查其代码、贡献改进或为定制需求进行分支——这对于有特定安全性或合规性要求的组织至关重要。它支持多个 AI 提供商,包括 Anthropic 的 Claude、OpenAI 的模型、Google 的 Gemini,甚至可以通过 Ollama 支持本地模型,让团队能够根据性能、成本或数据驻留要求灵活选择模型。
要有效使用 Cline,需要编写包含项目上下文和约束条件的详细提示词(prompt),利用其在进行更改前分析整个代码库的能力,并充分利用其迭代开发能力。在 Cline 实现某个功能后,你可以立即测试并在同一对话上下文中请求改进。VSCode 成熟的生态系统与 Cline 的自主能力相结合,为团队提供了一条强大、灵活且成本效益高的 AI 辅助开发路径,而无需放弃现有的工具和工作流程。
氛围编程(vibe-coding)运动的旗舰工具之一是 Cursor,这是一个 AI 增强的 IDE,在寻求更流畅编程体验的开发者中迅速流行起来。Cursor 本质上是一个 AI 优先的代码编辑器(实际上是 VSCode 的一个分支),它将最先进的代码生成和理解功能直接内置到你的开发环境中。
它的标语是”AI 代码编辑器”,旨在让你使用纯语言指令来编写和修改代码。例如,你可以高亮显示一个函数并要求 Cursor”优化这个函数”或”在这里添加错误处理”,它会立即建议代码更改。Cursor 的 AI 具有项目感知能力——它会索引你的代码库并理解文件的上下文,因此可以提供更相关的建议(远超简单的自动完成)。Cursor IDE 将 LLM 能力集成到其核心界面中。它就像是了解你代码库的 ChatGPT。
在底层,Cursor 利用先进的语言模型(通常是 Anthropic 的 Claude 或 OpenAI 的模型,取决于你的设置)来支持其功能。它有一个聊天侧边栏,你可以在其中与代码进行对话,甚至还有一个用于多步骤代码生成的”Composer”模式。Andrej Karpathy 本人在他的氛围编程实验中使用了 Cursor 的 Composer 配合一个名为”Sonnet”的模型。这种设置允许他真正地与编辑器对话(通过”SuperWhisper”使用语音转文本),然后代码就会出现,他随后可以接受或改进。
Cursor 不仅可以生成代码,还可以在收到指令时编辑现有代码。例如,你可以询问:
你能让传输监听器中切换证书变得更容易吗?
Cursor 会理解你指的是你的代码,并在相关文件中提出直接编辑建议,或从相关文件(如规范 markdown 文件)中读取(见图 1-3)。在免费版本中,它通常会在聊天中提供差异(diff)供你批准;在专业版中,它可以自动将更改应用到你的工作区。
要在专业工作流中高效使用 Cursor,你应该系统地利用其功能。首先在 Cursor 中打开聊天窗口,描述你想要的功能或修复。例如:添加一个用户登录表单,包含电子邮件和密码,以及验证和错误信息。Cursor 会生成所需的代码(创建新文件或修改现有文件),并以草稿状态呈现。你可以审查这些更改(它会显示差异或预览),然后点击”应用”将它们合并到你的代码库中。许多开发者遵循这个循环:提示 → 审查 → 接受。如果建议不够完美,你可以优化提示(例如,“使用 Tailwind CSS 来设置表单样式”)或直接要求 Cursor 修复你发现的任何问题(“现在处理电子邮件已被注册的情况”)。本质上,你与代码进行对话,直到它看起来满意为止。
Cursor 在理解错误和日志方面也表现出色。如果你运行代码并遇到错误追踪或错误信息,你可以将其粘贴到 Cursor 聊天中,AI 通常会分析它并建议修复方案。这将调试转变为一种协作体验:你不再需要手动搜索 Google 或 Stack Overflow,Cursor 的 AI 通常能够精准定位问题,甚至编写补丁。也就是说,明智的做法是验证修复方案,因为 AI 可能不会总是第一次就做对。
另一个专业提示:利用 Cursor 考虑多个文件的能力。你可以选择一组文件(或在提示中告知项目上下文),这样它在生成代码时会考虑整个代码库。例如:在后端添加一个新的 API 端点以支持登录表单,并将其连接到我们刚刚创建的前端表单。Cursor 会回忆起刚刚编写的前端代码,并帮助制作相应的后端逻辑。这种项目级的上下文理解能力,相比早期只能逐个文件工作的编码助手来说,是一个游戏规则改变者(game changer)。
总之,Cursor 就像在你的 IDE 内部拥有一个全天候的 AI 结对编程员。它直观(你用简单的语言与它对话),可以直接更新你的代码。你越是练习分解任务并用清晰的指令提示 Cursor,就会发现在短时间内能完成更多工作。它特别适合迭代开发:你构建一点,运行并查看输出,然后立即要求 Cursor 调整或扩展代码,然后重复这个过程。
在 vibe-coding 工具箱中另一个冉冉升起的新星是 Windsurf,这是一个 AI 驱动的开发环境,将代码理解提升到了新的水平。Windsurf 由 Codeium 团队构建,它通过索引整个代码库并使用检索技术在你工作时将相关部分提供给 AI 模型来实现差异化。实际上,这意味着 Windsurf 在处理大型项目时表现极佳,即使问题的答案可能分散在多个文件中。其核心使用了所谓的检索增强生成(RAG, retrieval-augmented generation),这是一种巧妙的方式,即它查找与你的提示相关的代码部分,并将该上下文提供给 AI,使其建议与你现有的代码保持一致。
对开发者来说这意味着什么?假设你刚接触一个大型代码库并需要添加功能。使用 Windsurf,你可以用自然语言询问:
代码库中用户身份验证逻辑在哪里处理?
它会搜索索引并指向正确的文件甚至函数。然后,你可以打开聊天(Windsurf 称之为”Cascade”视图,通过 Cmd+L 触发)并说:
在登录流程中添加基于手机的双因素身份验证。
因为 Windsurf 拥有你的身份验证逻辑的上下文,它可以生成跨多个文件(数据库、API、前端)的更改来实现这个功能,做出与你的系统结构一致的明智选择。
Windsurf 的写入模式(Write mode)可以大胆地为你应用更改:它会自动创建新文件或编辑现有文件,而不仅仅是在侧边栏中建议差异。这可以极大地节省时间:你不必从建议中复制粘贴,而是看到项目在原地演变。Windsurf 本质上试图在有信心时代表你采取行动,表现得像一个自主的初级开发人员在代码库中实现功能。(Cursor 的理念更加保守,会要求确认,尽管其 Pro 版本也有”自动应用”功能。)
要有效利用 Windsurf,理解其优势会很有帮助:
代码库问答
您可以用简单的英语查询您的代码库,几乎就像一个为您的项目定制的 Stack Overflow。这对于大型遗留项目非常有用,在这些项目中查找某个定义可能需要数小时。Windsurf 将通过从索引代码中提取信息,在几秒钟内给出答案。
因为它会将相关文件提供给模型,Windsurf 可以很好地处理诸如”将支付模块重构为使用我们编写的新日志工具”之类的任务,因为它同时了解支付模块和日志工具。
Windsurf 有多种模式(如前所述的自动完成、聊天、命令和级联)。级联模式类似于超级聊天,它可以考虑更广泛的上下文。写入模式(在聊天中)实际执行更改。作为工程师,您可以决定给予它多少自主权。
对于团队而言,Windsurf 可以像 Cursor 一样集成到日常开发中。在两者之间选择时,一些开发人员更喜欢 Windsurf,因为它的速度和大胆性(注意到它生成和应用更改的速度感觉更快),以及由于其索引功能而适合处理非常大的项目。另一方面,Cursor 的界面对 VSCode 用户来说可能感觉更熟悉。这不一定是非此即彼的选择——一些工程师会同时使用两者,或者团队可能会标准化使用其中一个。
总之,如果您想要一个在编写代码之前真正”阅读文档/代码”的 AI 编程助手,Windsurf 是一个出色的工具。它最大限度地减少了函数幻觉或变量名错误的机会,因为它可以查找信息。要充分利用它,请向它提供清晰的指令,并在写入模式下让它处理大任务,但对于细微的更改,也可以以更受控的方式使用它。始终检查它所做的更改(它会向您显示),特别是对于关键代码。Windsurf 很智能,但并非万无一失。明智地使用,它就像一个了解您整个项目的超智能 IDE,可以跨项目实现想法,大幅提升您的产出。
AI 编码格局已经发生了巨大变化,现在有多个强大的模型在竞争开发人员的注意力,包括来自 Claude、Gemini 和 OpenAI 系列的模型。 曾经可能只有一个模型占据主导地位,而如今的生态系统提供了丰富的选择,每个选择都有独特的优势,使它们适合不同的编码场景。
当今的编码模型通常分为几个类别,基于它们的方法和优势:
这些模型优先考虑快速响应,非常适合实时代码补全和快速迭代。它们通常提供较低的延迟,代价是在复杂任务上的准确性略有降低。
这些模型花更多时间”思考”问题,但在复杂调试、架构决策和多步骤问题解决方面表现出色。具有高级推理能力的模型可以逐步分解复杂的错误。
一些模型不仅可以处理代码和文本,还可以处理图像、图表,甚至视频内容。这使它们在理解可视化文档或处理 UI/UX 元素时特别有价值。
DeepSeek 的突出之处在于提供了与闭源模型相当的 AI 能力水平,无需付费或注册,尽管它可能缺少一些功能,如图像生成或网页浏览能力。
成功的开发人员现在不再寻求单一的”最佳”模型,而是将模型与特定任务相匹配:
对于快速原型设计和通用编码,针对速度和广泛语言支持优化的模型效果很好。
对于复杂的调试和系统设计,能够系统性地追踪逻辑的深度推理模型是一个好选择。
对于处理大型代码库,选择具有广泛上下文窗口的模型,可以保持项目范围的感知能力。
对于预算有限的团队,开源模型无需订阅费用即可提供出色的价值。
许多工具现在支持多种AI模型,包括OpenAI、Claude和Gemini变体,以及专有模型,允许开发者根据手头的任务在它们之间切换。
无论您选择哪种AI模型,某些实践始终能改善结果。首先,提供丰富的上下文。不要只是要求”一个支付处理函数”。相反,分享您的数据模型、现有代码模式、错误处理方法以及任何具体要求。您提供的上下文越多,输出就越能与您的代码库保持一致。
大多数现代编码模型都擅长审查自己的输出。在收到生成的代码后,要求模型检查潜在问题、提出改进建议或解释其推理过程。这种自我批评通常能发现细微的错误或建议优化方案。
利用模型维护对话上下文的能力。从基本实现开始,然后通过后续请求逐步完善它。这种迭代方法通常比试图预先指定所有内容产生更好的结果。
每个模型在处理问题的方式上都有细微差别。有些在解释时更冗长,而另一些则更简洁。有些默认使用较新的语法,而另一些则更保守。了解这些倾向有助于您制定更好的提示。
AI编码领域每月都在演变,新模型定期挑战既有领导者。竞争变得如此激烈,以至于开发者从前所未有的选择和能力改进中受益。最重要的不是选择”完美”的模型,而是理解如何利用可用工具的优势。
许多开发团队现在采用组合方法——利用快速模型处理日常任务,利用强大模型应对复杂挑战,并利用专门模型处理特定领域,如数据库优化或前端开发。一些IDE甚至允许在任务中途无缝切换模型。
成功来自于理解这些选项并策略性地应用它们来加速您的开发工作流程。
Google的Gemini系列通过其原生多模态能力代表了AI辅助开发的根本性转变。与主要在文本和代码上训练的模型不同,Gemini从底层架构上就被设计为能够无缝理解和处理文本、代码、图像、视频和其他数据格式。这使其在视觉上下文与文本信息同等重要的现代开发工作流程中异常强大。
Gemini的多模态特性在Web开发场景中特别有价值。开发者可以分享设计原型的截图,Gemini能够生成与视觉风格完美匹配的像素级实现。它擅长理解图表、图示和UI元素,使其成为将视觉设计转化为功能代码时的理想合作伙伴。这种能力不仅限于简单的图像识别:Gemini可以对视觉元素进行推理,理解设计模式(design patterns),并在整个项目中保持美学一致性。
Gemini 通过流行的编辑器(VSCode、Cursor、Windsurf)以及 Cline 和 Code Assist 等插件与开发工作流程集成,为开发者提供了强大的自定义选项,可以从个人偏好扩展到团队标准。开发者可以为重复性任务创建自定义命令,建立适用于每次代码生成的规则,并在大型代码库中维护一致的编码模式。其慷慨的免费套餐使学生、爱好者和初创公司都能使用,而企业功能则支持复杂的组织需求。
Gemini 在编码领域的独特之处在于其能够深入思考问题的同时保持实用的速度。该模型可以在简单任务的快速响应和复杂挑战的深入推理之间切换,根据手头的问题调整其方法。这种灵活性与其视觉理解能力相结合,使其在全栈开发中特别有效,因为后端逻辑和前端美学同等重要。
Anthropic Claude 的编码辅助方法以透明度和深度推理能力为中心。Claude 系列,特别是 Sonnet 模型,已确立自己在需要仔细分析和逐步解决问题的复杂软件工程任务方面的卓越能力。Claude 的独特之处在于其能够展示思考过程,让开发者能够跟随其推理并在实施解决方案之前验证其逻辑。
Artifacts 功能代表了开发者与 AI 编码助手交互方式的范式转变(paradigm shift)。Claude 不是简单地在聊天界面中提供代码,而是创建一个专用工作空间,可以实时查看、编辑和预览代码。这种交互式环境对于前端开发、数据可视化以及即时视觉反馈可以加速开发过程的任何场景都特别强大。开发者可以在同一对话中迭代设计、测试功能并改进实现。
Claude 在实际软件工程基准测试中表现出色,在错误修复、功能实现和代码重构等任务方面始终位居顶级模型之列。其优势不仅在于生成代码,还在于理解软件项目的更广泛上下文。Claude 可以分析现有代码库、识别模式和反模式(antipattern)、建议架构改进,并保持与既定编码风格的一致性。这使其对于绿地项目(greenfield project)和遗留系统维护都非常有价值。
该模型的内存和上下文管理方法使其能够在扩展的编码会话中建立理解。在处理大型项目时,Claude 可以提取并保留有关代码库结构、设计决策和项目特定模式的关键信息。这种累积的知识使其能够随着开发进展提供越来越相关和符合上下文的建议,让人感觉更像是一个随着时间推移熟悉项目的团队成员,而不是一个无状态的助手。
ChatGPT 已确立自己作为 AI 编码助手中的瑞士军刀地位,其价值不在于专业化功能,而在于其卓越的多功能性和广泛的知识库。它在开发者工具包中的地位是独特的。虽然其他模型可能直接集成到 IDE 中或提供专门的编码环境,但 ChatGPT 作为一个始终可用的编程顾问,开发者在整个工作日都会在浏览器中保持其打开状态。
ChatGPT 的对话界面使其在探索性问题解决和学习方面表现出色。开发者经常使用它进行橡皮鸭调试法(rubber-duck debugging),粘贴有问题的代码并通过自然对话思考问题。其广泛的训练使其能够识别几乎所有常用编程语言、框架和工具的模式。无论是调试正则表达式、理解晦涩的错误消息,还是探索不熟悉的库文档,ChatGPT 都能从其全面的知识库中提供相关见解。
ChatGPT 的优势在于其能够弥合人类意图与代码实现之间的鸿沟。它擅长双向翻译(bidirectional translation)——将自然语言描述转换为可运行的代码,并用简单的英语解释复杂的代码。这使得它在团队内的文档编写、代码审查和知识传递方面非常有价值。开发者可以粘贴不熟悉的代码并获得其功能的清晰解释,或者描述期望的行为并获得跨多种编程范式的适当实现。
该模型的多功能性超越了传统编程语言,延伸到配置文件、脚本、数据格式和领域特定语言(domain-specific languages)。虽然专业编码工具在其专注领域表现出色,但 ChatGPT 在整个软件开发任务范围内提供有价值的帮助。这种广度使其在跨不同技术的边界工作或遇到跨多个领域的问题时特别有用。它在扩展对话中保持上下文的能力允许开发者迭代地探索复杂问题,通过协作对话完善解决方案。
这些强大的 AI 编码助手的可用性代表了软件开发实践的根本性转变。成功的开发者不会将它们视为竞争选项,而是认识到每个模型家族在开发过程的不同方面都带来了独特的优势。当视觉上下文和多模态理解(multimodal understanding)至关重要时,Google 的 Gemini 表现出色,特别是在 UI/UX 开发和处理设计规范时。在需要深度推理、复杂重构和透明问题解决方法的场景中,Anthropic 的 Claude 表现突出。OpenAI 系列模型提供无与伦比的多功能性和广泛知识,使其成为学习、调试和跨领域挑战的理想选择。
现在许多开发团队采用组合策略(portfolio approach),在同一项目中针对不同任务利用不同模型。典型的工作流程可能包括使用 Gemini 将设计原型转换为初始实现,使用 Claude 进行复杂的架构决策和代码审查,使用 ChatGPT 进行一般问题解决和文档编写。这种多模型方法通过将每个工具的优势与特定开发挑战相匹配来最大化生产力。
随着这些模型的持续发展,有效进行 AI 辅助开发的关键不在于选择单一的”最佳”选项,而在于理解如何协调多个 AI 助手来加速和增强软件开发生命周期的每个方面。
这个生态系统还很年轻且快速变化。每隔几个月就会出现新的参与者和能力。关键要点是,你不必从头构建自己的 AI 来利用意图编程(programming with intent)——有许多工具将这种能力带到你的指尖。在本书中,我将讨论各种平台以及它们如何融入氛围编码(vibe coding)工作流程。
重要的是要认识到 AI 辅助开发真正发光的场景——以及它可能仍然不足的地方。让我们探讨一些氛围编码表现出色的理想用例,以及当今 AI 仍然存在困难或需要大量人工干预的情况。
正如某些架构适合某些问题一样,氛围编码在软件开发领域也有其”最佳场景”。
Vibe编程对于从零开始启动一个全新项目来说是一个游戏规则改变者。“零到一”一词(由Peter Thiel推广)指的是从零开始创造全新的东西。借助AI,你可以以闪电般的速度从一张白纸变成一个功能原型。需要建立一个从未存在过的Web应用程序?你可以在一次疯狂的提示会话中生成前端、后端、数据库架构甚至部署脚本的样板代码。这对于目标是快速验证想法的初创公司或黑客马拉松项目来说非常完美。你可以让AI在几分钟内完成这些工作,而不是花费数周时间来设置项目的”脚手架”(所有重复的设置代码)。
许多开发者讲述了他们如何在AI配对程序员的帮助下在一个周末构建了一个MVP(最小可行产品)——这在以前独自工作可能需要一个月的时间。通过快速将想法具体化为工作产品,你可以更快地开始与用户或利益相关者进行测试。AI擅长处理通用的东西(设置路由、基本UI组件、标准CRUD操作),这让你可以专注于产品的新颖方面。
然而,一旦你的MVP获得关注并走向生产环境,你的方法就必须改变。这就是AI辅助工程变得至关重要的地方。虽然vibe编程帮助你快速探索和验证,但现在扩展需要更加深思熟虑的实践。你需要用适当的错误处理重构快速生成的代码,添加全面的测试覆盖率,并建立清晰的架构边界。从原型到产品的过渡标志着从vibe编程的探索性自由到工程的结构化规范的自然演变。聪明的团队会认识到这个拐点并相应地调整他们的AI使用方式——在保持速度的同时引入可持续增长所必需的护栏。
许多软件工程,特别是在业务应用程序中,涉及围绕数据的CRUD——创建、读取、更新、删除——功能。这是AI特别擅长的公式化工作,因为它见过无数的例子。如果你需要向系统添加一个新的”库存”模块,包含CRUD界面和API,vibe编程将处理得非常好。它可以生成数据库迁移、ORM模型、API端点和带验证的UI表单——基本上是全栈——基本上没有错误,因为这些模式在其训练数据中非常常见。即使你的应用程序有自定义规则,你也可以在提示中指定这些规则并获得不错的初稿。结果是:曾经需要一周时间的枯燥连接工作变成了一个下午的提示和测试。对于内部工具或管理面板(本质上是大型CRUD应用程序),考虑到它们通常如此简单却又耗时,你几乎可以完全依赖AI来生成它们。
当这些CRUD操作涉及复杂的业务逻辑、数据验证规则或与现有系统的集成时,工程方法变得至关重要。虽然vibe编程可以快速生成基本结构,但AI辅助工程确保你的库存模块能够正确处理边缘情况,如并发更新,维护引用完整性,并遵循组织既定的模式。例如,你可以使用vibe编程生成初始CRUD脚手架,然后切换到工程模式来实现特定领域的规则,如库存阈值警报、多仓库分配逻辑或与现有身份验证和授权系统的集成。关键是识别何时从快速生成过渡到仔细完善。
需要将两个服务或API集成在一起?这通常涉及阅读文档并编写代码以将数据从一种格式转换为另一种格式。AI模型通常在API文档和代码示例上进行过训练,这意味着它们可以加速集成工作。要求ChatGPT展示如何从语言B调用服务A的API——很可能它会生成带有正确端点甚至可能包含身份验证示例的示例代码。当AI可以建议样板和要处理的边缘情况时,组合多个系统(如将支付网关与订单系统连接或连接第三方分析SDK)变得更容易。它擅长这些标准集成模式。
AI编程助手已经有效地阅读了所有流行框架(frameworks)的手册:React、Angular、Django、Rails、Node/Express、Flutter等等。这意味着如果你使用知名框架,AI可以为这些框架生成符合惯用法的代码。例如,它可以生成一个带有hooks和状态管理的新React组件,或者一个带有适当admin类和serializer的新Django模型。好处是你不必记住每个细节,AI会填补这些空白。Vibe编程在现代Web开发任务中表现尤其出色,比如生成带有正确类的HTML/JSX或连接控制器端点,因为这些是AI模型反复见过的任务。这就像有一个框架专家总是在你身边编写样板代码,而你只需决定功能的具体实现。
有时你需要创建大量相似的代码(比如为某个模式(schema)中的每种类型创建许多相似的端点或类)。这对人类来说可能既繁琐又容易出错。另一方面,AI喜欢重复性结构——一旦你展示一两个示例,它就能一致地生成其余部分。这种批量代码生成可以节省大量时间。例如,如果你要为50种类型的记录编写数据模型类,你可以提供一个示例并要求AI按照该模式为所有50种类型生成类。它很可能会在几秒钟内完美完成。结果是:你避免了一整天的单调编码工作。
虽然vibe编程在某些场景中表现出色,但AI辅助工程(AI-assisted engineering)在其他情况下变得不可或缺。理解这些情况有助于开发者从一开始就选择正确的方法,避免代价高昂的重写或技术债务(technical debt)。复杂的算法实现需要工程方法。当你构建复杂的数据结构、实现性能关键的算法或解决新颖的计算问题时,你需要对实现的每个方面进行精确控制。
在这里,AI充当知识丰富的助手而非代码生成器。你可能会要求它解释算法方法或审查你的实现的正确性,但你保持对架构和优化决策的直接控制。AI帮助你思考问题,而不是完全解决它们。
关键任务系统(mission-critical systems)从一开始就需要工程严谨性。金融交易、医疗应用、安全基础设施和其他高风险领域承受不起vibe编程的探索性特质。在这些环境中,每一行代码都需要仔细考虑、全面测试,并且通常需要符合监管要求。AI通过建议最佳实践、识别潜在漏洞和帮助确保符合标准来提供协助,但开发者保持对实现的严格控制。
这些系统中失败的成本远远超过快速生成带来的任何速度优势。遗留系统集成(legacy system integration)带来了独特的挑战,在这些挑战中工程纪律被证明是必不可少的。当处理几十年前的代码库、专有协议或具有大量技术债务的系统时,vibe编程的模式匹配往往会失败。这些场景需要深入理解现有约束、仔细规划集成点以及有条不紊的重构。AI可以通过解释遗留代码模式或建议现代化策略来提供帮助,但实际实现需要只有结构化工程才能提供的精确性。
性能优化(performance optimization)代表了工程胜过vibing的另一个领域。虽然AI可以快速生成功能性代码,但它很少为性能关键路径产生最优解决方案。内存管理、缓存优化、并行处理和延迟降低等任务需要对硬件、操作系统和算法复杂度有深入理解。在这里,AI最好充当研究助手,帮助你探索优化技术或对不同方法进行基准测试,而你做出关于实现的明智决策。
在这些场景中,AI的模式识别和速度与任务完美契合。本质上,vibe编程在编程中的常见任务(如CRUD或典型的Web应用结构)和受益于快速试错的任务(原型、新想法)上表现出色。这就像有一个初级开发者,他读过每个GitHub仓库,可以立即回忆起通常是如何完成的,并为你编写供审查的代码。这对于快速推进事情来说非常强大。
现代 AI 增强开发的艺术不在于选择其中一种方法,而在于识别何时在它们之间转换。成功的开发者会培养出对这些拐点的直觉。开始一个新功能?从氛围编码(vibe coding)开始快速探索可能性。注意到代码变得复杂或涉及关键系统?切换到工程模式。为客户演示构建概念验证?氛围编码能让你快速完成。将概念验证转换为生产系统?是时候采用工程规范了。
这种流动性——在快速探索和仔细构建之间无缝切换的能力——区分了真正高效的 AI 增强开发者。他们明白氛围编码和 AI 辅助工程是工具箱中的互补工具,各自适用于开发生命周期的不同阶段。目标不是选边站,而是策略性地利用这两种方法,在整个软件开发过程中最大化速度和质量。
尽管当前的 AI 编码工具令人印象深刻,但它们并非魔法。有些类别的问题对于 AI 来说仍然难以可靠地处理,通常需要人类洞察力或传统编码技术。了解这些局限性有助于设定正确的期望,并让你规划何时依赖 AI,何时重新掌控。
这些局限性包括以下几点:
如果你正在处理非常复杂的算法或 AI 可能没有见过的新颖问题,它可能会陷入困境。例如,从研究论文编写全新算法,或者做一些像编写编译器或高度并发系统的事情——这些涉及需要真正理解和通常需要创造性飞跃的复杂逻辑。AI 可以尝试,但它可能会在细节上出错。
在这些复杂领域中,AI 倾向于生成大致正确但不完全正确的代码,这可能导致大量的来回修改。正如第 3 章和第 4 章将讨论的,最后 30% 左右的正确性对 AI 来说很难钉住。这与我所说的 70% 问题有关——AI 能快速让你完成大部分工作,但最后部分很困难。有经验的开发者可能会使用 AI 为这些复杂任务生成骨架或辅助函数,但核心逻辑自己完成。
当前的 AI 模型主要在高级语言和抽象上训练。如果你需要进行底层位操作,为特定微控制器编写高度优化的 C 代码,或生成矢量化 SIMD 指令,AI 可能不可靠。它可能会生成看起来合理但在硬件层面上并不真正优化甚至不正确的代码。
同样,对于内存管理或实时约束之类的事情,AI 没有真正的概念(它不会在脑海中模拟 CPU 缓存)。因此,对于性能关键的代码,你需要彻底测试 AI 的建议或手动编写这些部分。话虽如此,AI 仍然可能通过提供起始模板或解释汇编来提供帮助,但在这些场景中你不能盲目信任它。
如果你使用的是在 AI 训练期间不存在的非常新或晦涩的框架,它就不会知道。在这种情况下,AI 可能会尝试泛化,或者可能生成看起来合适但实际上调用不存在的函数(幻觉)或使用过时版本 API 的代码。例如,如果一个新的 Web 框架版本上个月发布并带有破坏性更改,AI 就不会知道这些更改。它可能会给你旧版本的代码。在这些情况下,你必须依靠文档,甚至可能通过在提示中提供文档中的上下文来帮助训练 AI(基本上是即时教学)。
如果你要求 AI 设计一个完全新颖的用户界面或体验,它在这种创造性飞跃上并不擅长。它可以为已知模式生成 UI 代码(如标准表单或仪表板),但如果你想要一个没有明确先例的创新 UI,AI 可能不会给你带来灵感的东西。它可能只是将熟悉的组件拼接在一起。人类设计师和前端开发者仍然非常需要去构想新的用户体验。从编码角度来说,AI 可以快速为你制作一个标准外观的界面,但对于那种特殊的定制感觉,你需要引导它或手动调整。
有时当需求是隐含的或矛盾的时候,AI 会遇到困难。除了你明确告诉它的内容之外,它对最终目标没有真正的理解。如果需求含糊不清(“让它高效”——这到底意味着什么?),AI 可能会错误地猜测你关心的是什么(例如内存与速度)。人类更擅长澄清意图,特别是与非技术利益相关者沟通时。AI 也可能误解指令,特别是如果存在它不知道的特定领域上下文(如业务规则)。它可能生成一个逻辑上正确的解决方案,但实际上并不能解决真正的问题,因为细微差别在翻译中丢失了。
一个结合这些的好例子场景:想象一下在 Rust(系统级、性能关键)中开发一个新的 3D 图形引擎(复杂系统)。你有用于渲染的新颖算法(独特问题)。AI 可能能帮助编写一些样板代码,但核心部分你主要依赖人类的创造力。AI 可能会帮你开始设置窗口和基本的渲染循环(常见任务),但对于定制部分,你需要采用传统的仔细编码方式,也许可以从 AI 那里以伪代码的形式获得一些算法帮助。如果你让它优化汇编语言中的热循环,你必须验证每一条指令。
AI 也缺乏真正的问题解决洞察力。归根结底,它是模式匹配。所以如果你的问题需要一个恍然大悟的洞察,AI 可能只会四处摸索,呈现看起来像代码但并不能解决问题的东西。这时人类退一步、抽象思考或借鉴真实经验就能挽救局面。一旦你有了洞察,你就可以使用 AI 快速实现它。
理解这些优势和劣势可以确保你在正确的情况下部署氛围编码(vibe-coding)技术。为了最大化成功,利用 AI 擅长的方面(已知模式),并将你的创造力应用于应用程序的独特部分。准备好在 AI 已知会遇到困难的领域进行干预。例如,仔细审查它编写的任何安全敏感代码,因为它可能会遗漏一两个边缘情况。
使用 AI 来补充人类优势:让它处理广度(大量代码、样板代码),而你处理深度(复杂逻辑、架构)。在它擅长的地方将其作为助推器使用,不要害怕在那些更艰难的路段接管方向盘。这既发挥了双方的优势,又产生了最佳结果。知道何时使用 AI 以及何时依靠人类技能,将使你在这个新时代成为一名高效的开发人员。
每一项新技术都有其优势和注意事项。当我们拥抱 AI 辅助开发带来的生产力和创造力提升时,重要的是以细微差别的理解来对待它的局限性和权衡。主要优势包括:
: 项目可以更快地从概念到原型到成品。AI 可以快速生成脚手架代码(如为新项目设置样板代码),因此你可以将更多时间花在应用程序的独特部分上。
: 因为尝试某件事的成本更低(只需向 AI 描述你想要什么并获得快速草稿),开发人员可能会更自由地进行实验。你可以通过以不同方式提示 AI 来原型化问题的多种方法,然后选择最好的一个。这种迭代构思可以带来更具创造性的解决方案。
: LLMs 是在大量编程知识语料库上训练的。它通常”知道”晦涩的 API 或错误消息解决方案。实际上,它可以呈现你可能没有想到的解决方案或想法,使你成为更有效的问题解决者。
: 在团队环境中,AI 助手可以通过生成一致风格的代码来帮助执行编码标准和最佳实践。如果配置了你项目的风格指南,它可以确保每个人的代码遵循类似的模式。即使没有明确的训练,AI 模型通常也会产生惯用代码(因为它们从数百万个示例中学习)。这可以减少代码审查所涉及的工作量,因为它的函数可能看起来很熟悉并且默认遵守常见约定。
需要考虑的一些局限性和权衡包括:
: 这些模型并非万无一失。它们可能会生成看起来正确但有细微错误或低效的代码。它们可能会选择过时的方法,因为它们的训练数据包含了大量较旧的代码。作为开发人员,你必须保持警惕。就像你不会在不理解的情况下从互联网复制粘贴代码一样,你也不应该不假思索地接受 AI 代码。本书的[第二部分]将讨论彻底验证和测试 AI 生成代码的技术。
如果你的提示不够明确,AI 必须猜测你的意图——而它可能猜错。例如,如果你告诉它”对名字列表进行排序”,它可能默认按字母顺序排序,但也许你想要的是其他方式(比如按名字长度排序)。除非你明确说明,否则 AI 无法知道这种差异。这就是为什么提示的明确性([第2章]的主题)至关重要——你需要学会预见哪些细节需要明确说明。
如果新开发人员总是依赖 AI 来编写代码,他们是否还能形成对算法和调试的深入理解?这存在技能退化的风险,类似于依赖 GPS 导航可能会削弱你自己的方向感。为缓解这一问题,重要的是将 AI 作为学习工具使用(关注它提供的代码并询问原因),有时还要在不使用 AI 的情况下练习编码,以确保保留基本技能。
使用基于云的 AI 编码工具通常意味着将你的代码(可能是专有或敏感的)发送到第三方服务进行分析。公司需要考虑这一点。许多工具正在通过允许本地部署模型或保证不存储代码来解决这个问题,但这仍然是一个需要考虑的因素。此外,AI 可能会无意中生成与其训练数据中某些内容非常相似的代码,而这些代码可能受开源许可证(如 GPL)约束。虽然这种情况不太可能(并且已有措施防止逐字输出长内容),但这强调了在集成之前需要审查和理解 AI 产出的必要性。[第8章]深入探讨安全性和可靠性问题。
AI
模型可能反映其训练数据中存在的偏见。在编码环境中,这可能像偏好某些变量名这样无害,也可能像使用假设特定用户属性的示例这样重要。例如,它可能在每个示例变量中都使用
foo/bar(因为许多示例都这样做),或者它可能对用户区域设置做出假设。与其他
AI
应用相比,这在代码生成中通常不是一个大问题,但值得注意这种可能性。更微妙的是,AI
可能偏向于它更常见到的解决方案,即使这些方案不是最适合你的情况。[第9章]讨论偏见和其他道德考量。
并非所有开发人员都能立即适应这种工作方式。编码有其独特的乐趣和艺术性,有些人可能觉得 AI 的介入削弱了这一点。还可能存在最初的不信任——“它真的做对了吗?”——只有良好的实践和时间才能克服。采用 AI 的团队应该允许一段适应期,并鼓励分享经验和技巧。随着时间推移,就像对待任何工具一样,大多数人会找到一个平衡点,在这个平衡点上 AI 的贡献得到重视,人类专业知识则专注于人类最擅长的领域。
转向意图编程的氛围转变为加快软件开发、提高可访问性以及在许多方面使开发更愉快提供了巨大潜力。但要实现这一潜力,需要理解新的动态:如何有效地与 AI 沟通、如何验证其输出,以及如何负责任地将其整合到开发流程中。
我的观点基于使用这些工具的经验和观察众多项目得出,即 AI 的最佳用途在于将创造性的”氛围”与扎实的工程规范相结合。鼓励 AI 能够提供的大胆想法和快速草稿——这些是我们掌握的新超能力。但要用软件开发数十年积累的智慧来引导它们:规划、测试和理解你所构建内容的重要性。
当我们取得这种平衡时,我们就能获得两全其美的效果。我们可以更快、可能更富想象力地构建软件,同时也是我们信任、维护并充满信心发展的软件。这最终就是我们如何在 AI 时代提升我们的技艺:不是在氛围与工程之间二选一,而是掌握两者之间的整个光谱。
接下来,[第2章]探讨制作提示和与 AI 协作的艺术。有了本章的基础概念,你已经准备好探索这个新编程时代的实践方面。这将为后续章节中的实践示例和更深入的提示技术奠定基础。
在vibe coding中,提示词(prompt)就是新的源代码。
你向AI传达意图的方式直接影响它生成代码的质量。编写好的提示词既是艺术也是科学,通常被称为提示词工程(prompt engineering)。本章将为你提供一些技巧,帮助你充分利用AI编程助手。我们将从提示词为何重要的基本原理开始,然后深入探讨从简单到高级的提示词技巧工具箱。通过学习如何编写有效的提示词以及如何迭代优化它们(图2-1),你将能够更高效、更准确地与AI协同创作。
如果说vibe coding是你与AI模型之间的对话,那么提示词工程(prompt engineering)就是掌握AI语言以获得最佳结果的技能。精心设计的提示词可以决定AI是给出不相关或有bug的代码建议,还是完美的解决方案。掌握提示词工程意味着理解如何有效引导AI、如何提供上下文,以及当第一次回答不够理想时如何与AI迭代。
当你使用AI编程时,本质上是通过AI使用自然语言进行编程。你提供的提示词就像一种高级编程语言,AI解释器随后将其转换为实际代码。就像编译器的输出质量取决于输入的源代码质量一样,AI的输出质量也取决于提示词的质量。
为什么提示词如此重要?尽管LLM(大型语言模型)非常复杂,但它们并不能读心。它们只对给定的输入做出响应。模糊或措辞不当的提示词可能导致不相关或不正确的代码,而清晰且具体的提示词可以在第一次尝试时就产生精准的解决方案。在传统编程中,你花时间思考算法和编写代码;在vibe coding中,你花时间思考如何向AI传达你的需求。这是”编写代码”含义的转变:你可能会写一段话而不是一个函数,但你仍然需要精确和符合逻辑。
可以把编写提示词想象成为一个非常字面化和迂腐的初级开发者编写文档或用户故事,这个开发者会完全(且仅仅)按照文档所说的去做,拥有大量知识但除了他们见过的模式之外没有常识。如果你的指令(提示词)留有解释空间,AI可能会以你意想不到的方式填补空白。因此,学会与AI沟通与学习编程语言的语法一样重要。
提示词至关重要的另一个原因是可重现性和面向未来。如果你发现一个提示词能够可靠地为某个特定模式或任务生成良好的代码,那么这个提示词就成为一项有价值的知识(几乎像代码片段或模板)。你可能会保存它或在类似的上下文中重用它。在团队中,开发者可能会相互分享有效的提示词模式,就像他们分享编程最佳实践一样。
最后,随着模型变得更好、集成度更高,它们可能允许更复杂的交互。擅长提示词编写将使你能够快速利用新功能。例如,一些高级系统允许你附加详细的指令或提供整个参考文档作为模型上下文的一部分。知道如何构建这种输入是充分利用这种能力的关键。
因此,将提示词编写视为一项新的基本技能。在很多方面,提示词编写就是编程。主要区别在于你用AI随后转换为代码的语言(如英语)编写。但你仍然必须清晰、符合逻辑,并在描述中预见边界情况。
提示词编写的黄金法则之一(我将在第3章更全面地阐述)是对你想要的内容要具体和清晰。与人类协作者不同,AI并不会真正理解你提供的文字之外的目标。一个常见的错误是给AI一个非常高层次的提示词,比如”做一个网站”,然后期待奇迹发生。AI在具体细节下工作得更好。
始终假设它对你的项目一无所知,除非你提供相关信息。包含相关细节,如编程语言、框架和库,以及所涉及的具体函数或代码片段。如果有错误,提供确切的错误消息并描述代码应该做什么。任何模糊或可解释的空间都可能导致意外的输出。
例如,不要说”编写一个排序函数”,而应该说:
编写一个Python函数sort_by_lastname(customers),接收一个客户记录列表(每条记录包含first_name和last_name字段),返回按last_name字母顺序排序的列表。包含简要的文档字符串(docstring),并通过将缺失的last_name视为空字符串来处理这种情况。
这个提示词明确了语言(Python)、函数名称和用途、输入结构、排序键、附加要求(文档字符串)以及边界情况。它很可能产生你所需要的结果,或者非常接近。本质上,要像规格说明书编写者那样思考:你对任务的规格说明越精确,AI需要猜测的就越少,你需要修改的次数也就越少。
提高具体性的策略包括:
如果你想要JavaScript的解决方案,明确说明:“编写一个JavaScript函数…”,而不是仅仅说”编写一个函数…“。如果你想要特定框架或版本的代码,包含这些信息(”使用React Hooks…“或”在Python 3中…“)。
你只想要一个函数吗?一个完整的文件或模块?包含测试吗?例如,“仅提供函数实现”和”提供一个完整的可运行脚本”会产生不同的响应。
在登录示例中,我们指定了密码长度和尝试次数限制。考虑边界情况或约束条件并将它们放入提示词中。如果你需要代码针对性能优化或使用特定算法,请说明:“使用O(n)时间和O(1)空间”或”使用二分搜索方法”。
不要在没有明确前置词的情况下使用像它这样的词。不要说”处理它并返回结果”,而应该说”处理数组并返回结果数组”。
如果你希望AI仅输出代码、带注释的代码或包含解释的代码,你可以明确指示:“仅提供代码,不要解释”或”提供代码和每个步骤的简要注释”。
清晰的提示词为AI的成功奠定基础。如果你发现AI的答案经常需要大量修正,检查一下你的提示词是否可能规格说明不足。
以下是不应该做的:
包含无关信息的冗长提示词可能会混淆模型或导致它关注错误的内容。在描述中做到简洁但完整。例如,在编码环境中通常不需要以”你是一位世界级的程序员…“作为开头(有些人在一般的ChatGPT使用中会这样做,但对于编码任务,这通常是不必要的,可能会增加噪音)。
如果某些内容很重要(如线程安全、特殊字符的处理等),请提及它。如果没有提及,假设AI可能不会处理它。
例如,说”编写一些代码来分析数据”可能会导致AI猜测你想要什么分析。相反,应该明确指定:
计算一个数字列表的平均值和标准差。
总之,准确表达你的意思。AI对你真正想要的内容”了解”得越多,它就能提供越好的结果。如果你发现自己必须多次纠正AI,问问自己:我的初始提示词是否可以更清晰?
即使有清晰的提示词,你也不会总是第一次就得到完美的答案。将与AI的交互视为对话或迭代开发过程。这就是我在第1章中提到的反馈循环。
当AI给你代码时,像审查人类编写的代码一样批判性地审查它。它是否满足要求?如果不满足,找出缺少或错误的地方。然后提供反馈或优化后的提示词。这可以在对话式AI中通过简单地继续对话来完成,或者在编辑器中通过编写另一条注释让AI响应来完成。
通过向AI提供反馈,你可以引导它更接近你期望的结果。从某种意义上说,你正在针对你的具体问题即时训练它。高级提示工程就像图2-2中的循环:提示 → AI输出 → 审查 → 改进提示 → AI输出 →…直到满意为止。保持每次迭代的改动较小是有用的;如果你过度改动提示,可能会丢失上一次输出中的一些好的部分。
例如,你可以提示:
编写一个函数,接收一个整数列表并返回它们的总和。
AI随后返回一个函数,但它的代码假设列表非空,并且没有很好地处理空列表。然后你可以回复:
看起来不错。但是,请修改它以在列表为空时返回0。
AI会相应地更新函数。这样,你不必从头开始提示;你只是告诉AI做一个调整。AI已经有了它之前给出的代码的上下文。
如果你使用的是内联助手(inline
assistant),改进可能看起来像编辑代码,也许写一个注释如# TODO: handle empty list,然后看AI是否建议修复。
另一种改进方法是,如果第一次输出不正确,用更多信息重新提示。假设你说”对名字列表进行排序”,它给出了区分大小写排序的代码,但你想要不区分大小写。你可以重新表述:
对名字列表进行不区分大小写的排序。
或者甚至:
之前的代码是区分大小写排序的。修改它为不区分大小写。
在调试中,对于更复杂的逻辑错误(没有明显的错误消息抛出但输出错误),你可以提示AI逐步执行代码。例如:
逐行执行这个函数,跟踪每一步total的值。它没有正确累加—逻辑在哪里出错了?
这是一个”橡皮鸭”调试提示的例子:你实际上是在要求AI模拟人类可能用打印或调试器进行的调试过程。这样的提示通常会揭示微妙的问题,如变量未重置或错误的条件逻辑,因为AI会详细说明每一步的状态。如果你怀疑代码的某个特定部分,你可以聚焦:
解释这里的filter调用在做什么,以及它是否可能排除了比应该排除的更多项目。
让AI扮演解释角色可以在解释过程中暴露错误。
在解释之后,直接要求你需要的内容通常是有效的:
可能是什么导致了这个问题,我该如何修复它?
这邀请AI既诊断又提出解决方案。如果AI的第一个答案不清楚或部分有帮助,不要犹豫提出后续问题:
那个解释说得通。你能展示如何修复代码吗?请提供修正后的代码。
在聊天环境中,AI有对话历史,所以它可以直接输出修改后的代码。如果你使用的是像VSCode中的Copilot或Cursor这样的内联工具而没有聊天,你可以在代码上方写一个注释:
// BUG: 返回NaN,修复这个函数并看它如何自动完成
不过一般来说,交互式聊天能产生更全面的解释。
另一种后续模式:如果AI给出了修复但你不理解为什么,询问:
你能解释为什么那个改动解决了问题吗?
这样,你为下次学习,并且你再次确认AI的推理是合理的。
LLM在示例和修正中蓬勃发展。如果你指出什么是错的或给出一个快速示例,AI可以将其纳入:
如果输入是[],它应该返回0,但现在它报错了。
这个迭代过程是正常的。事实上,试图将每个细节塞进一个提示可能不如几轮来回交流有效。利用这一点来发挥你的优势。
在反馈中要有耐心和具体。不要说”不,那是错的”,而是说什么是错的或需要什么:
这段代码没有正确处理负数。它应该在求和时将它们视为0。
另外,如果AI偏离方向,你可以引导它回来:有时重置或重新表述比试图挽救一个完全错误的尝试更容易。运用你的判断。如果AI输出显示它完全误解了你,从头澄清你的提示。
在改进过程中,你也会了解AI如何解释你的提示。这可以告诉你如何编写未来的提示。你可能会意识到,“哦,它把’登录系统’理解为整个UI。下次我会指定仅后端。”
把它想象成调试代码:如果AI输出错误,“bug”可能在你的提示中,而不在AI的处理中。就像当你的代码产生不正确的结果时你会检查和修复它一样,当AI生成意外或不正确的输出时,你应该改进你的提示。你和AI之间的对话就像一个调试会话,每次交流都帮助你确定和修复问题。
假设你有一个简单的 Node.js 函数,用于将用户对象列表转换为按用户 ID 索引的查找映射。然而,它抛出了一个错误。以下是有问题的代码:
// 有bug的函数: 将用户数组转换为按ID索引的映射
function mapUsersById(users) {
const userMap = {};
for (let i = 0; i <= users.length; i++) {
// 注意: 使用 <= 会在最后一次迭代时越界
const user = users[i];
userMap[user.id] = user;
}
return userMap;
}
// 使用示例:
const result = mapUsersById([{ id: 1, name: "Alice" }]);
这段代码应该遍历用户数组并构建一个对象,其中键是用户
ID。然而,这里有一个 bug:循环使用了 i <= users.length
而不是 i < users.length。这将导致 users[i]
在最后一次迭代时变为 undefined,很可能导致运行时错误(比如尝试访问
undefined 的 ID 属性)。让我们看看 AI
助手对于这个问题的两种不同提示词会如何响应(见图 2-3)。
以下是一个糟糕提示词的示例:
为什么我的 mapUsersById 函数不工作?
AI 对糟糕提示词的响应(模拟):
没有更多信息很难说。你确定函数调用时传入的是正确的用户数组吗?也许问题在于 users 是空的或者不是数组,导致循环无法正确执行。也可能是在 userMap 上设置属性时出现了错误。确保每个用户都有 id 属性。如果 users 是 undefined 或 null,那也会导致它不工作。
这个回答相当模糊。提示词除了函数名之外没有给出任何细节,所以 AI 只能猜测一些通用原因(“也许数组是空的或不是数组”,“确保每个用户都有 ID”)。它没有识别出实际的 bug。
这就是当问题描述不够具体时得到的响应类型——模型只能重复一些常见的调试技巧。如果你是开发者,这并不太有帮助,因为它没有解决真正的错误。提示词中缺乏上下文和具体性导致了一个通用的答案:本质上是 AI 版本的”你试过重启了吗?“这印证了社区的建议:模糊的提示词会导致不相关或通用的答案。
让我们再试一次:
我有一个 JavaScript 函数
mapUsersById,它应该将用户对象数组转换为按用户 ID 作为键的映射(对象)。然而,当我运行它时会抛出错误。例如,当我传入[ {id: 1, name: "Alice"} ]时,我得到TypeError: Cannot read property 'id' of undefined。以下是函数代码:function mapUsersById(users) { const userMap = {}; for (let i = 0; i <= users.length; i++) { const user = users[i]; userMap[user.id] = user; } return userMap; }它应该返回
{ "1": {id: 1, name: "Alice"} }。bug 是什么,我该如何修复它?
AI 对改进提示词的响应(模拟):
错误 Cannot read property
'id'of undefined 表明在某个时刻,user 是 undefined,然后你尝试访问 user.id。查看你的循环,问题出在循环边界上。你使用了i <= users.length。如果users.length是1(数组中有一个用户),循环会在i = 0和i = 1时运行。当i是1时,users[1]是 undefined(因为数组中唯一的索引是 0)。因此 user 在那次迭代中变成 undefined,user.id抛出TypeError。修复方法是将循环条件改为i < users.length,这样i从0到users.length-1。例如:for (let i = 0; i < users.length; i++) { const user = users[i]; userMap[user.id] = user; }这样,
i对于第一个(也是唯一的)元素将是0,然后循环停止。通过这个改变,你的函数将不再遇到 undefined 的 user,应该能按预期工作,对于给定的输入返回{"1": {id: 1, name: "Alice"}}。
这个响应直接指出了 bug(在循环中使用了
<=)并解释了为什么它会导致 user 变为
undefined。然后它提供了正确的代码解决方案(i < users.length)并确认了预期的结果。改进的提示词之所以成功,是因为它遵循了我们的原则:它指定了语言(JavaScript),描述了函数的目的,包含了确切的错误消息和示例输入,甚至提供了有问题的代码片段。
注意AI如何利用错误信息作为线索,将重点放在循环边界上——有针对性的提示使AI能够进行真正的问题解决,有效地模拟人类调试者的思考方式:“undefined可能从哪里来?很可能来自循环索引。”这具体展示了详细提示的好处。
现在让我们深入了解一些可以增强你提示技能的具体技术。这些就像你在直接指令不够用,或者想以特定方式引导AI时可以使用的模式或配方。
通过掌握这些技术,你可以处理一系列情况:用简单的英语指导AI、给它举例、让它解释或构建输出、或将其设置为不同的思维模式或角色。所有这些都有助于你引导AI准确生成你需要的内容。
提示技术并不相互排斥;你通常会将几种技术结合使用以获得最佳结果,特别是在复杂任务上。
当你使用这些技术时,要根据模型调整你的语气。许多模型对礼貌或中性的指令响应良好。你不需要使用古老或过于正式的语言。直接但礼貌通常有效:“请执行X”或”让我们做Y。“例如,在链式思考(CoT)提示中,一个流行的短语是”让我们一步步思考。“像GPT-4这样的模型会将其识别为显示推理的提示。
零样本提示只是在不提供任何示例或额外指导的情况下,要求模型做某事,只提供指令。本质上,模型是从”零”个示例中解决任务。
何时使用:这是最常见的场景:你只是用简单的语言要求你想要的东西。如果任务是标准的并且提示清晰,这通常就足够了。
示例:
编写一个检查数字是否为质数的Python函数。
这是零样本。AI很可能会使用循环或试除法生成一个质数检查函数。
优点:快速且依赖于模型的学习知识。现代模型在许多编程任务的零样本响应方面表现出人意料地好,特别是如果它们很常见(如质数检查、排序或字符串操作)。
缺点:如果任务不寻常或输出格式很具体,零样本可能在第一次尝试时产生不太符合你需要的结果,因为模型可能有多种解释方式。
通常,对于简单的事情,首先尝试零样本是个好主意。如果结果不对,你可以转向改进或其他技术。
单样本提示意味着你提供恰好一个你想要的示例(输入和期望输出)作为提示的一部分;少样本提示意味着在要求模型对新输入执行任务之前提供几个示例(通常是两到五个)。
这就像向模型展示,“这是我如何解决一个实例的。现在你用类似的方法做下一个。”
何时使用:当模型可能不确切知道你需要的格式或风格,或者当任务有点不寻常时,这种类型的提示很有用。通过给出示例,你减少了歧义。
示例(单样本):假设你正在使用模型可能不太常见的语言或某种特定风格。假设你想要特定格式的伪代码。你的提示可能是:
将以下英文指令转换为类似Python的伪代码。
示例指令:“计算n的阶乘”:
示例伪代码:
function factorial(n):
if n <= 1:
return 1
else:
return n * factorial(n-1)
指令:"在列表中查找最大的数"
伪代码:
你提供了一个示例(阶乘)和你想要的格式。现在模型更有可能以类似的格式为”最大数”指令生成伪代码(带有函数,带有if/else或根据需要的循环逻辑)。
示例(少样本):假设你想让AI使用特定的算法。你可以给它一个该算法实际运行的较小示例作为提示。或者如果任务有多个正确答案但你更喜欢某一个,示例可以将其推向那个方向。
少样本提示对于格式化非常强大;例如:
将以下英文语句转换为SQL查询。 1.”获取2020年后雇用的所有员工” → SELECT * FROM Employees WHERE Hire_Date > ‘2020-01-01’; 2. “列出上个月购买过商品的客户姓名” → SELECT Name FROM Customers JOIN Purchases ON … WHERE Purchase_Date > …; 3. “缺货产品的数量” →
这里,一旦你给出两个英语到SQL的示例,AI就可能通过遵循这个模式正确回答第三个查询。少样本示例(few-shot examples)也可以应用于编码:向AI展示你想要的风格的小样本,然后要求更多。这就像在你的提示词(prompt)中给它一个迷你训练数据集。
优点: 你可以实现非常特定风格的输出。这种技术还帮助模型处理涉及遵循模式或重复应用概念的任务。
上下文窗口(context window)这个术语指的是语言模型在单次交互中可以处理的最大文本量(以token计量),包括输入提示词和生成的响应。这代表了当前AI模型的一个限制——一旦你达到这个阈值,模型就无法处理额外的信息。当你在制作提示词时,你包含的所有内容(指令、示例、数据和响应所需的空间)都必须适应这个固定容量。
缺点: 特别是少样本提示会使提示词变长(这会占用上下文窗口)。对于非常大/复杂的示例,它可能会”吃掉”模型的大量容量。但通常一两个小示例是可以的。
提示: 如果你想让模型严格遵守某个输出结构,给出一个示例几乎可以保证它匹配该结构,而不是提供一个你必须解析的自由形式回复。
思维链(Chain-of-thought, CoT)提示涉及要求模型在给出最终答案之前逐步思考或展示其推理过程。换句话说,你鼓励模型分解问题。
何时使用: 这对涉及推理和多步计算的复杂问题很有用,或者当你怀疑模型如果直接跳到答案可能会犯错时。当你想要在输出中获得解释时也很有用。
组合数学(Combinatorial math)处理根据特定规则或约束对对象进行计数、排列和选择。常见问题包括计算排列(permutations)(顺序重要的排列)、组合(combinations)(顺序不重要的选择)和其他计数原理。关键符号包括”n选k”(写作C(n,k)或nCk),表示从n个总项目中选择k个项目的方式数量,使用公式n!/(k!(n-k)!)计算。这些计算经常出现在概率、统计和离散数学应用中。
示例: 与其只是问一个组合数学问题,比如”12选4是多少?“并得到答案,你可以说:
逐步求解12选4。
模型可能会列出:
12选4 = 12!/(4!*8!) = ... = 495
在编码中,思维链(CoT)对于棘手的算法任务很有用。你可以尝试以下提示:
逐步解释如何合并两个排序列表,然后提供Python代码。
模型首先会概述如下内容:
我们将使用两个指针从每个列表的头部开始,比较元素,将较小的元素追加到结果列表,并移动该指针,依此类推…
然后它可能会给出代码。这确保它在编码之前已经正确构建了解决方案。
另一个用途是调试或理解输出:
演示判断数字19是否为素数的逻辑,然后给出结果。
模型可能会列出被素数整除的情况,然后得出结论:
19是素数。
优点: 提高需要推理的任务的正确性。有研究证据表明,提示模型”大声思考”可以在数学和逻辑任务上获得更好的结果。它还让你深入了解模型的过程,这可以增强理解或帮助你更信任答案。
缺点: 输出更长(这可能不是你在最终代码中想要的)。此外,一些界面(如典型的代码补全)不适合单独显示推理。这种技术在问答或聊天场景中更常见。然而,你可以指示模型将推理作为注释包含在代码中,这是获得完整注释代码的一种巧妙方式。
角色提示(Role prompting)意味着你要求AI扮演某个可能影响其响应方式的特定身份或角色。
何时使用: 当你想影响答案的风格或细节程度,或获得某个特定视角时,这很有用。例如,扮演”专家”角色的AI可能会给出更高级的解决方案或更多解释,而”初学者”角色可能会让它解释更多基本概念。
示例:
你是一名 Python 教员。解释以下代码,然后将其修改为更符合 Python 风格的版本。
扮演一名安全分析师。这里有一些代码。识别任何安全漏洞。
假装你是一个检查代码风格问题的代码检查工具(linter)。
这可以显著影响响应结果。给 AI 分配安全分析师角色可能会让它关注那些原本不会提及的方面(如数据验证、安全编码实践或潜在漏洞)。教员角色可能会使其提供更清晰的解释,并且可能不会假设你有先验知识。
在编程中,你可能会在请求代码之前这样说:
你是一位精通优化的 C++ 专家程序员,正在指导一名初级开发者。
结果很可能会使用更高级的 C++ 特性,并解释为什么做出某些选择,在技术复杂性和教育清晰度之间取得平衡。
优点:这种技术可以引导答案的语气和深度。这可以将解决方案定制到特定的复杂程度或彻底性。如果你想要一个非常简单的解决方案(告诉它扮演新手,也许它会避免复杂的技巧),或者一个非常优化的解决方案(告诉它扮演性能大师),这很有用。
缺点:有时模型可能会过度关注角色而非必要(“教员”可能会开始解释你已经知道的事情)。此外,某些 AI 安全系统对某些角色描述更敏感——特别是那些可能暗示欺骗、冒充权威或潜在有害活动的描述——尽管像”数据分析师”或”软件工程师”这样直接的技术和专业角色通常不会出现问题。
上下文提示是指给 AI 提供超出直接任务描述之外的额外上下文或信息。AI 模型不会持久记忆你的整个项目,除非你在提示中提供(或通过高级 IDE 集成中的某些集成上下文窗口)。因此,如果你希望 AI 编写适合现有代码库的代码,就需要给它提供该上下文。基本上,你将相关数据或背景作为提示的一部分提供。
何时使用:当解决问题需要了解模型可能不知道或可能无法从训练中正确回忆起的某些数据或定义时使用。或者当你想确保与某些外部信息(如 API 规范或对话的先前部分)保持一致时使用。
示例:
如果你有一个数据结构并希望代码能够使用它,你可以粘贴其定义:
给定以下类,实现函数 X。
class Node:
def __init__(self, value, next=None):
self.value = value
self.next = next
# 现在编写一个函数来计算从 head 开始的链表中的节点数。
通过包含类定义,你可以让 AI 更有可能在代码中正确使用
Node.value 和 Node.next。
如果你想使用特定的 API,在提示中包含文档片段:
使用 requests 库从 API 获取数据。(API 返回格式为 {…} 的 JSON)
如果你包含文档中 API 使用的简短示例,AI 可以模仿它。
用于消除歧义:
使用术语
student来指代高中学生,编写一个函数…
如果 student 在上下文中可能有歧义,你已经澄清了它。
优点:你将 AI 定位在你关心的上下文中。如果你提供事实,它就不太可能做出错误的假设。如果 AI 可能不记得或不知道你特定用例的详细信息,这非常有帮助。
缺点:这种技术会使提示变长。此外,模型可能偶尔会将提供的上下文重新输出到答案中(如果不小心,可能会将文档片段中的行复制到代码中)。但通常它会适当地使用它。
提示:如果你有大量上下文(如大型模式或多行代码),有时最好为模型总结关键元素,而不是逐字包含所有内容。这种方法可以帮助你保持在上下文限制内,同时确保模型接收到最相关的信息。然而,如果内容足够小,直接原样包含即可。
在上下文提示中提及约束(constraints)也很有用:性能约束(“优化到 O(n log n) 或更好”)、兼容性约束(“必须在 Python 3.8 上运行”)或库选择(“仅使用标准库,不使用外部依赖”)。这些就像护栏,确保 AI 不会建议超出可接受范围的内容。
元提示(Metaprompting) 是对输出本身给出指令,而不仅仅是解决方案应该做什么。这就像是告诉 AI 如何格式化或处理解决方案。
何时使用: 当您需要特定格式或风格的答案时,或者当您想控制 AI 解决问题的方式时很有用。
示例:
首先用两句话解释方法,然后提供代码。
这确保 AI 不会直接开始写代码:
解决方案中不要使用任何库。
这对解决方案施加了约束:
将输出格式化为 JSON。
如果您使用 AI 生成数据而不是代码,这很有用:
仅提供函数体,不包括定义行。
如果您想将函数插入现有代码,这很方便:
如果输入无效,不要返回错误,而是返回 None。
这不完全是输出格式,但它指示 AI 在某些情况下如何行为。
优点: 您可以准确地得到所需的内容,以您需要的方式,无需额外编辑。这对某些场景至关重要。如果您计划在流水线中自动使用 AI 的输出,那么您真的需要一致的格式。
缺点: 如果指令与模型的默认风格冲突,有时它可能只部分遵循,或者您必须强调它们。例如,即使您说”仅代码,无解释”,偶尔模型也可能包含一点注释之类的。通常,将其表述为直接命令会有所帮助:
不要包含任何解释;仅在单个代码块内输出代码。
像 GPT 这样的模型很好地遵循这一点。
自洽性(Self-Consistency) 更像是一种策略而不是提示风格。其思想是对同一提示获取多个输出,然后决定最佳或最常见的输出。正如 Learn Prompting 的 Sander Schulhoff 所指出的,自洽性利用了这样一个概念:如果您多次询问模型(带有轻微的随机性),并且它的许多答案一致,那么这种共识很可能是正确的。
何时使用: 这对于复杂问题很有用,当您不确定模型的第一个答案是否正确时,特别是如果您自己无法轻易验证它,或者如果您想通过查看它是否重复给出相同答案来从 AI 获得置信度检查。
如何手动使用: 在某些平台上(如 ChatGPT),您可以点击”重新生成答案”。或者您可以将提示复制到新会话中,看看它是否给出相同的结果。如果您得到三个答案,其中两个相同,一个不同,您可能会相信这两个(假设问题有一个正确答案)。
在编程上下文中,如果它为某个确定性任务生成代码,通常每次都会给出非常相似的代码(变量名或风格略有不同)。但如果这是一个算法问题(如”这段代码的输出是什么?“),您可以检查多次运行。
这种技术在非编码任务(如逻辑谜题)中更强大,但值得注意。
另一个角度——集成提示: 您实际上可以在一个提示中要求模型考虑多种可能性:
为这个问题提供两种不同的解决方案。
然后也许您可以看到您喜欢哪一个或测试两者。这就像是一次性的自洽性,因为您得到了多个答案。
优点: 如果多次尝试收敛,这种技术可以增加对解决方案的信心。此外,您可能会获得多样性(如果您想从许多方案中选择最优雅的解决方案,这很好)。
缺点: 进行多次调用并比较输出很耗时。
在实践中,如果我对答案不确定,我通常会以不同的方式重新提问,看看是否得到相同的答案。如果得到了,我会更有信心它是正确的。
ReAct 是一种更高级的提示技术,它结合了推理和行动。它使模型不仅像 CoT 那样思考,而且还采取行动,如进行计算、调用 API 或使用工具。(更多信息请参见 ReAct 提示工程指南)。在当前实践中,这通常与 LangChain 等框架一起使用,其中 AI 可以输出程序解释为操作的特殊格式(如要执行的命令或要运行的查询),然后将结果反馈回来。
对于我们的范围(没有这样的执行环境在循环中),您仍然可以通过指示 AI 首先概述计划,然后输出结果来进行一种形式的 ReAct。它类似于 CoT,但专门面向使用工具或执行子任务。
示例:
使用 Python,确定巴黎的当前天气并打印它。
除非AI具有浏览功能,否则它无法真正获取当前天气。ReAct方法会让AI首先通过陈述来推理问题:
我需要访问巴黎的当前天气数据,这需要调用天气API。
然后AI会尝试使用可用的工具来进行API调用。如果成功,它将收到实际的天气数据;如果没有这样的工具,它可能会承认局限性或使用假设数据。最后,AI会编写Python代码来显示天气信息,整合通过这个推理和行动过程获得的任何数据。
如果没有外部工具访问权限,ReAct对于简单的提示任务可能不太相关。然而,在为您的组织评估AI工具时,确定它们是否可以从互联网访问当前信息是一项关键的能力评估。许多AI模型的知识有截止日期,这意味着它们的训练数据只延伸到特定日期,这可能导致快速变化的主题信息过时。
如果您使用的环境中AI可以执行代码(例如Jupyter集成或类似平台),您可以通过指示系统来实现ReAct:
首先为这个函数编写一个测试,运行它,然后相应地调整代码。
这通过推理步骤(编写测试)、行动(执行测试)和基于结果调整代码来展示ReAct模式。然而,通过纯提示编排这样的工作流需要高级提示技术和适当的技术基础设施。
更简单的用法:您可以模拟一个问答,其中AI有模仿行动的中间步骤:
逐步思考,如果需要的话,进行计算。
这实际上是CoT,但语气更加命令式。
优点:当可用时,它可以解决需要外部信息或迭代试验的问题(例如AI可以通过实际运行代码来纠正自己)。在调试环境中,可以执行代码来测试的AI非常棒。
缺点:没有特定工具,这种技术不太容易获得。如果您只是在普通ChatGPT中这样提示,它要么会想象这些行动,要么只是执行CoT。
对于我们在提示编写中的目的,请记住,一些系统(如OpenAI的工具使用代理或其他系统)是存在的,但在普通提示中,我们主要执行CoT,我们自己处理诸如运行代码或测试之类的行动。
提示技术可以组合使用。例如,您可能会做一个少样本提示,同时在示例中演示CoT。或者您可能将角色与CoT结合起来:
作为一名高级工程师,逐步思考问题,然后给出代码。
现在我们已经探索了各种提示技术,让我们通过一两个场景来看看它们的实际应用,然后讨论如何审查和改进AI的输出(这将引入下一章关于理解和掌握生成代码的内容)。
想象一下您有一个无法正常工作的函数。您可以使用角色和CoT提示的组合:
您是一名Python调试器。让我们逐步思考以找到以下代码中的错误。
接下来是代码。AI可能会对每一行进行分析并精确定位错误。
或者假设您想为一个稍微复杂的算法生成代码,确保它有良好的注释,并且还要获得测试用例。组合提示可能如下所示:
您是一名专业的Python开发人员。让我们逐步解决这个问题。我们需要一个函数
merge_sorted_lists(list1, list2),它将两个排序列表合并为一个排序列表。首先,解释方法,然后提供带注释的Python代码。之后,给出2-3个代码示例测试来演示它的工作原理。
这个单一的提示相当全面。第一句设定了角色。第二句请求逐步推理。第三句给出主要任务。第四句要求提供带解释性注释的代码,第五句甚至要求测试。
AI随后可能会输出一个解释,然后是带内联注释的代码,最后是一些测试用例。这是一个高级用法,但它显示了您如何通过多方面的响应来指导AI。
提示工程(Prompt Engineering)还涉及到知道什么不该问以及如何避免陷阱。如果提示变得太大或包含太多指令,模型可能会感到困惑或截断某些输出。如果你发现它开始忽略提示的某些部分,你可能需要简化或分部分进行。如果AI模型有时会产生不正确的事实或代码(它会”产生幻觉(hallucinate)“),你要学会仔细检查,不要将其作为事实的权威来源。如果你发现它倾向于给出过于冗长的代码,你可以预先用”使解决方案尽可能简洁”来防止。如果它有时使用不存在的函数,你可以指示”仅使用下面列出的API函数”并列出它们。你越了解AI的行为,就越能塑造你的提示以规避任何弱点。
如果任务非常复杂,你也可以将其分解为子任务让AI处理。例如,你可以首先提示:
列出为简单算术表达式语言实现基本编译器的步骤。
一旦AI给出步骤,你就用单独的提示来处理每个步骤,甚至可能在单独的文件或会话中:
现在实现步骤1:词法分析(tokenization)。
这就像与AI进行系统设计:你可以先概述然后细化每个部分。它利用了AI在规划方面的能力(不仅仅是编码)。
在聊天环境中,你有对话历史,称为状态(state)。你可以通过与AI讨论来建立上下文。在IDE补全环境中,上下文主要是你的文件内容和注释。两者都以不同的方式允许累积上下文。如果你需要AI记住所说的内容(比如优化答案),使用对话。如果你想确保它只关注现在相关的内容,使用新的提示或文件上下文。有时清除上下文可以防止模型坚持可能错误的早期假设。
通过在各种示例上练习这些技术,你将熟练掌握何时使用哪种方法:
如果输出格式很重要,给出示例(few shot)或明确的格式指令。
如果逻辑很复杂,使用思维链(CoT)或逐步分解。
如果解决方案的质量可能有所不同,设置角色(如”资深工程师”)以获得更好的风格。
如果模型不配合,也许将提示分成几部分,简化它们,或对约束使用更强硬的措辞。
并非所有提示都是平等的。到目前为止,我们已经看到许多有效提示的例子,但同样有益的是识别反模式(antipattern)——导致AI回复不佳的常见错误。本节涵盖一些常见的提示失败案例以及如何修复它们。
这是经典的”它不工作,请修复它”或”写点做X的东西”而没有足够细节。“为什么我的函数不工作?”这样的问题通常会得到无用的答案。模糊的提示迫使AI猜测上下文,通常会导致通用建议或不相关的代码。
修复方法很简单:添加上下文和具体信息。如果你发现自己提出问题后得到的答案感觉像魔术8球的回应(“你试过检查X吗?”),停下来并用更多细节重新构建你的查询(错误消息、代码摘录、期望与实际结果等)。一个好的做法是阅读你的提示并问:“这个问题是否适用于数十种不同的场景?”如果答案是肯定的,那就太模糊了。让它具体到只能适用于你的场景。
这是相反的问题:一次要求AI做太多事情。例如:
生成一个完整的Node.js应用,包含身份验证、React前端和部署脚本。
或者,在较小的规模上:
一次性修复这5个bug并添加这3个功能。
AI 可能会尝试处理,但你很可能得到混乱或不完整的结果,或者它可能会忽略请求的某些部分。即使它处理了所有内容,响应也会很长且更难验证。
解决办法是拆分任务。优先处理:一次做一件事,正如我们之前强调的那样。这样更容易发现错误并确保模型保持专注。如果你发现自己写的指令段落中多次使用”和”,考虑将其分解为单独的提示词或顺序步骤。
有时用户会提供大量信息但从未明确提出问题或说明他们需要什么,比如只是转储一大段代码片段并说”这是我的代码”。这可能会让 AI 困惑——它不知道你想要什么。
始终包含明确的要求:
识别上述代码中的任何错误。
解释这段代码的作用。
完成代码中的待办事项。
提示词应该有明确的目的。如果你只是提供文本而没有问题或指令,AI 可能会做出错误的假设(比如总结代码而不是修复它等)。确保 AI 知道你为什么向它展示某些代码。即使是简单的补充,如”这段代码有什么问题?“或”请继续实现这个函数”,都能给它方向。
这是一个微妙的问题。有时你可能会要求优化或改进,但没有定义成功的标准——例如,“让这个函数更快”。以什么指标衡量更快?如果 AI 不知道你的性能约束,它可能会微观优化一些无关紧要的东西,或者使用理论上更快但实际上可以忽略不计的方法。或者”让这段代码更清晰”:“更清晰”是主观的。我们通过明确说明目标来处理这个问题,比如”减少重复”或”改进变量名”等。
解决办法:量化或限定改进:
优化这个函数使其以线性时间运行(当前版本是二次时间)。
重构这段代码以移除全局变量并使用类。
基本上,明确说明你要通过重构或功能解决什么问题。如果你让它太开放,AI 可能会解决与你关心的问题不同的问题。
有时 AI 可能会用澄清性问题或假设来回应:
你使用的是 React 类组件(class component)还是函数组件(functional component)?
我假设输入是字符串——请确认。
如果你忽略这些并只是重申你的请求,你就错过了改进提示词的机会。AI 在发出信号表明它需要更多信息。始终回答它的问题或优化你的提示词以包含这些细节。
此外,如果 AI 的输出明显偏离(比如它误解了问题),不要只是逐字重试相同的提示词。花点时间调整你的措辞。也许你的提示词有一个模棱两可的短语或省略了一些关键内容。把它当作对话:如果一个人误解了,你会用不同的方式解释;对 AI 也这样做。
如果你不断改变提问方式或在一次提问中混合不同的格式,模型可能会感到困惑。两个例子包括在指令中在第一人称和第三人称之间切换,或以令人困惑的方式混合伪代码(pseudocode)和实际代码。
尝试在单个提示词中保持一致的风格。如果你提供示例,确保它们被清楚地划分(使用 Markdown 三个反引号标记代码,使用引号标记输入/输出示例等)。一致性有助于模型正确解析你的意图。此外,如果你有首选风格(比如 ES6 与 ES5 语法),请始终如一地提及它;否则,模型可能在一个提示词中建议一种方式,而在稍后建议另一种方式。
在使用聊天时,如果你说”上述函数”或”之前的输出”,请确保引用是清晰的。如果对话很长并且你说”重构上述代码”,AI 可能会失去跟踪或选择错误的代码片段进行重构。
在对话中再次引用代码或明确指定要重构的函数名会更安全。模型的注意力窗口是有限的,尽管许多大语言模型可以引用对话的早期部分,但再次给出明确的上下文可以帮助避免混淆。如果距离代码展示已经过去了一段时间(或若干条消息),这一点尤其重要。
提示的艺术是迭代的和创造性的。随着模型的演进,提示最佳实践可能会改变(例如,未来的模型可能在使用较少文字的情况下更好地理解意图)。但底层原则保持不变:有效沟通,AI就会更好地为你服务。
本质上,掌握提示工程就像掌握一门新的编程语言——与AI交互的指令语言。它是技术写作、前瞻性思维和对提示本身的交互式调试的结合。但一旦你擅长它,AI真的会开始感觉像是你自己思维的延伸,因为你可以以最小的摩擦可靠地提取出你设想的解决方案(甚至是那些你还没有完全设想但可以引导AI去发现的解决方案)。这项技能可能会变得和知道如何使用谷歌搜索或如何使用调试器一样基础——它是氛围编程时代现代开发者技能集的一部分。
如果AI可以解决大约70%的问题,你如何将它作为编程合作伙伴来对待?第三章探讨了开发者如何真正使用AI,并为氛围编程设定了一些”黄金规则”。
基于AI的编程工具在某些任务上表现惊人地出色。它们擅长生成样板代码、编写常规函数,并将项目完成到大部分程度。事实上,许多开发者发现AI助手可以实现一个涵盖大约70%需求的初始解决方案。
Peter Yang在X平台的一篇帖子中完美地捕捉到了我在实践中观察到的情况:
作为非工程师使用AI编程的真实感受:
它可以让你完成70%的工作,但最后30%令人沮丧。它不断前进一步又后退两步,出现新的bug、问题等等。
如果我知道代码是如何工作的,我可能可以自己修复它。但由于我不知道,我质疑自己是否真的学到了什么。
非工程师使用AI编程时发现自己遇到了令人沮丧的瓶颈。他们可以惊人地快速完成70%的工作,但最后30%变成了一场收益递减的练习。
这个”70%问题”揭示了关于AI辅助开发当前状态的一些关键信息。最初的进展感觉很神奇:你可以描述你想要什么,v0或Bolt等AI工具会生成一个看起来令人印象深刻的工作原型。但随后现实出现了。
70%通常是工作中直接的、模式化的部分——遵循常规路径或通用框架的那类代码。正如一位Hacker News评论者观察到的,AI在处理软件的”偶然复杂性”(accidental complexity)(重复性的、机械性的东西)方面非常出色,而”本质复杂性”(essential complexity)——理解和管理问题的固有复杂性——仍然由人类承担。用Fred Brooks的经典术语来说,AI处理的是附带的而不是内在的开发困难。
这些工具在哪里遇到困难?经验丰富的工程师一致报告存在”最后一公里”的差距。AI可以生成一个看似合理的解决方案,但最后30%——覆盖边缘情况、改进架构和确保可维护性——“需要严肃的人类专业知识”。
例如,AI可能会给你一个在基本场景下技术上可行的函数,但除非明确告知,否则它不会自动考虑异常输入、竞态条件(race conditions)、性能约束或未来需求。AI可以让你完成大部分工作,但最后关键的30%(边缘情况、保持可维护性和稳固的架构)需要严肃的人类专业知识。
此外,AI有一个已知的倾向,即生成令人信服但不正确的输出。它可能引入微妙的bug或”幻觉”出不存在的函数和库。Steve Yegge讽刺地将当今的大语言模型比作”疯狂高产的初级开发者”——速度惊人且充满热情,但”可能受到改变思维的药物影响”,容易想出疯狂或不可行的方法。
用 Yegge 的话来说,大语言模型(LLM)可以生成乍看之下很精致的代码,但如果经验较少的开发者天真地说”看起来不错!“并直接使用它,接下来的几周就会出现荒谬(或灾难性)的结果。AI 并不真正理解问题;它只是将通常有意义的模式拼接在一起。只有人类才能辨别一个看似不错的解决方案是否隐藏着长期隐患。Simon Willison 也呼应了这一点,他看到 AI 提出了一个极具迷惑性的巧妙设计,但只有对问题有深刻理解的资深工程师才能识别出它的缺陷。教训是:AI 的自信远远超过了它的可靠性。
至关重要的是,当前的 AI 无法创造出超越其训练数据的全新抽象或策略。它们不会为你发明新颖的算法或创新的架构——它们只是重新组合已知的东西。它们也不会为决策承担责任。正如一位工程师指出的:“AI 没有比其训练数据所包含的’更好的想法’。它们不会为自己的工作负责。”
所有这些意味着创造性和分析性思维——决定构建什么、如何构建以及为什么构建——仍然牢牢属于人类领域。总之,AI 是开发者的力量倍增器(force multiplier),可以处理重复性的 70% 工作,为我们的生产力提供”涡轮增压”。但它不是可以取代人类判断的银弹(silver bullet)。软件工程剩余的 30%——困难的部分——仍然需要只有训练有素、思考周全的开发者才能带来的技能。这些才是需要重点关注的持久技能,[第 4 章]专门讨论这些内容。正如一个讨论所说:“AI 是一个强大的工具,但它不是魔法子弹……人类判断和良好的软件工程实践仍然至关重要。”
我观察到团队在利用 AI 进行开发时有两种不同的模式。我们称之为”引导者(bootstrappers)“和”迭代者(iterators)“。两者都在帮助工程师(甚至非技术用户)缩小从想法到执行(或 MVP)的差距。
首先是引导者,他们通常将新项目从零开始做到 MVP(最小可行产品)。像 Bolt、v0 和截图转代码 AI 这样的工具正在革新这些团队引导新项目的方式。这些团队通常:
结果可能令人印象深刻。我最近看到一位独立开发者使用 Bolt 在极短的时间内将 Figma 设计转换为可工作的 Web 应用。它还没有达到生产就绪状态,但已经足够获取最初的用户反馈了。
第二阵营是迭代者,他们在日常开发工作流程中使用 Cursor、Cline、Copilot 和 Windsurf 等工具。这不太引人注目,但可能更具变革性。这些开发者:
但问题在于:虽然这两种方法都可以显著加速开发,但它们都有一些不会立即显现的隐藏成本。
当你观看资深工程师使用 Cursor 或 Copilot 等 AI 工具工作时,看起来像魔术一样。他们可以在几分钟内搭建整个功能,包括测试和文档。但仔细观察,你会注意到一些关键的东西:他们不只是接受 AI 的建议。他们不断将生成的代码重构为更小、更专注的模块。他们添加 AI 遗漏的全面错误处理和边缘情况处理,加强其类型定义和接口,并质疑其架构决策。换句话说,他们正在应用多年艰苦获得的工程智慧来塑造和约束 AI 的输出。AI 正在加速他们的实现,但他们的专业知识才是使代码保持可维护性的关键。
初级工程师经常错过这些关键步骤。他们更容易接受 AI 的输出,导致我所说的”纸牌屋代码(house of cards code)“——看起来完整,但在现实世界的压力下会崩溃。
接下来通常发生的事情遵循我称之为”退两步(two steps back)“的可预测反模式(antipattern)(如图 3-1 所示):
你尝试修复一个小错误。
AI 建议一个看起来合理的更改。
这个修复破坏了其他东西。
你要求 AI 修复新问题。
这又产生了两个新问题。
不断重复这个过程。
对于非工程师来说,这种循环特别痛苦,因为他们缺乏理解实际出了什么问题所需的心智模型(mental models)。当经验丰富的开发者遇到 bug 时,他们可以基于多年的模式识别(pattern recognition)来推理潜在的原因和解决方案。如果没有这种背景知识,你基本上就是在玩打地鼠游戏,对付着你并不完全理解的代码。这就是我在本书前言中提到的”知识悖论(knowledge paradox)“:高级工程师和开发者使用 AI 来加速他们已经知道如何做的事情,而初学者试图用它来学习应该做什么。
对于使用 AI 的”自力更生者(bootstrapper)“模式的非工程师来说,这种循环特别痛苦,因为他们缺乏构建 MVP 时解决这些问题所需的心智模型。然而,即使是经验丰富的”迭代者(iterators)“,如果过度依赖 AI 建议而不进行深入验证,也可能陷入这种打地鼠陷阱。
这里有一个更深层次的问题:让 AI 编码工具对非工程师来说易于使用的特性——它们代表你处理复杂性的能力——实际上可能会阻碍学习。当代码”凭空出现”而你不理解底层原理时,你就无法培养调试技能。你会错过学习基本模式的机会。你无法对架构决策(architectural decisions)进行推理,因此你难以维护和演进代码。这会产生一种依赖关系,你需要不断回到 AI 模型来修复问题,而不是培养自己处理这些问题的专业能力。
随着自主 AI 编码代理(autonomous AI coding agents)的出现,这种依赖风险进入了一个新的维度——我将在[第10章]深入探讨这个主题。与当前建议代码片段的工具不同,这些代理代表了软件开发方式的根本转变。在我写这本书的时候,我们正在见证能够以最少的人工监督独立规划、执行和迭代整个开发任务的系统的早期部署。
从辅助型 AI 到自主型 AI 的演变引入了关于开发者专业知识和控制权的深刻问题。当 AI 系统可以处理从初始实现到测试和部署的完整开发工作流时,技能退化(skill atrophy)的风险变得严重。那些严重依赖这些代理而不保持基础知识的开发者,可能会发现自己无法有效地审计、指导或干预 AI 的决策偏离预期结果时的情况。
当我们考虑这些自主系统如何在整个项目中做出级联决策(cascading decisions)时,挑战变得更加复杂。每个单独的选择可能在孤立的情况下看起来都很合理,但累积效应可能会将开发引向意想不到的方向。如果没有及早识别和纠正这些轨迹偏移的专业知识,团队可能会在他们并不完全理解的基础上构建越来越复杂的系统。
正如我们将在后面更深入地探讨的那样,自主编码代理的出现并没有降低软件工程基础知识的重要性——而是放大了它。我们的 AI 工具越强大,我们就越需要保持专业知识,以确保我们仍然是系统的架构师(architects)而不仅仅是操作员(operators)。只有通过对软件原理的深刻理解,我们才能确保这些强大的工具增强我们的能力而不是侵蚀它们。
这正在成为一种模式:团队使用 AI 快速构建令人印象深刻的演示。正常路径(happy path)运行得很好。投资者和社交网络为之惊叹。但当真实用户开始点击操作时呢?那时候问题就出现了。
我亲眼见过这种情况:对普通用户来说毫无意义的错误消息,导致应用程序崩溃的边缘情况(edge cases),从未清理的混乱 UI 状态,完全被忽视的无障碍性(accessibility),以及在较慢设备上的性能问题。这些不仅仅是低优先级的 bug——它们是人们能容忍的软件和人们喜爱的软件之间的区别。
创建真正自助服务的软件——那种用户永远不需要联系支持的软件——需要一种不同的思维方式,这完全关乎打磨(polish)这门失传的艺术。你需要专注于错误消息;在慢速连接和真实的非技术用户身上进行测试;使功能易于发现;并优雅地处理每个边缘情况。这种对细节的关注(也许)无法由AI生成。它来自同理心、经验和对工艺的深切关注。
在我们深入本书第二部分的编码之前,我们需要讨论现代开发实践以及AI辅助编码如何融入团队工作流。毕竟,软件开发不仅仅是编写代码——它是一个完整的工作流,包括规划、协作、测试、部署和维护。而vibe编码(vibe coding)不是一个独立的新奇事物——它可以融入敏捷方法论(agile methodologies)和DevOps实践中,在保持质量和可靠性的同时增强团队的生产力。
在本节中,我们将探讨团队成员如何集体使用vibe编码工具而不会相互干扰,如何平衡AI建议与人类洞察,以及持续集成/持续交付(CI/CD)管道如何整合AI或适应AI生成的代码。我还将涉及版本控制策略等重要考虑因素。
在观察了数十个团队后,以下是我发现在单人和团队工作流中持续有效的三种模式:
在本节中,我将依次介绍每种模式,讨论工作流和成功的技巧。
在你要求AI模型起草任何代码之前,确保团队中的每个人都在同一页面上是很重要的。沟通是关键,这样开发者就不会让他们的AI助手执行冗余任务或生成冲突的实现。
在每日站会(敏捷工作流的主要组成部分)中,不仅值得讨论你正在做什么,还要讨论你是否计划将AI用于某些任务。例如,两个开发者可能正在开发不同的功能,这两个功能都涉及日期格式化的实用函数。如果两人都要求AI创建一个formatDate辅助函数,你可能最终会得到两个相似的函数。提前协调(“我会生成一个我们都可以使用的日期实用工具”)可以防止重复。
成功整合AI工具的团队通常从就编码标准和提示实践达成一致开始。例如,团队可能决定采用一致的风格(代码检查规则、项目约定),甚至将这些指南反馈给他们的AI工具(一些助手允许提供风格偏好或示例代码来引导输出)。正如Codacy博客中提到的,通过让AI熟悉团队的编码标准,你可以获得更统一的生成代码,每个人都更容易使用。在实践层面,这可能意味着在项目README中添加一个”AI使用技巧”部分,在其中注明诸如”我们只使用函数式组件”或”优先使用Fetch API而不是Axios”之类的内容,开发人员在提示AI时可以记住这些。
另一个实践是使用工具的协作功能(如果可用)。一些AI辅助IDE允许用户共享他们的AI会话或至少是他们使用的提示。如果开发人员A通过一个提示获得了复杂组件的良好结果,与开发人员B共享该提示(可能通过问题跟踪器或团队聊天)可以节省时间并确保一致性。
至于使用版本控制,基本原则保持不变——只是有所变化。在现代开发中使用Git(或其他版本控制系统)是不可协商的,这在氛围编码中不会改变。事实上,当AI快速生成代码时,版本控制变得更加重要。提交充当安全网来捕获AI的失误;如果AI生成的更改破坏了某些内容,你可以恢复到以前的提交。
一个策略是在使用AI辅助时更频繁地提交。每次AI产生重要的代码块(如生成功能或进行一些重大重构)并且你接受时,考虑用清晰的消息进行提交。频繁的提交确保如果你需要二分查找问题或撤销AI引入的部分代码,历史记录足够细粒度。
此外,尝试隔离不同的AI引入的更改。如果你让AI在不同领域进行许多更改并将它们全部一起提交,那么在出现问题时就更难以解开。例如,如果你使用代理来优化性能,它还调整了一些UI文本,请分别提交这些内容。(你的两个提交消息可能是”优化列表渲染性能 [AI辅助]“和”更新锻炼完成消息的UI文案 [AI辅助]“)。描述性提交消息很重要;一些团队甚至标记涉及大量AI的提交,只是为了可追溯性。这不是关于责备,而是关于理解代码的起源——标记为”[AI]“的提交可能向审查者表明,代码可能需要对边缘情况进行额外彻底的审查。
本质上,团队应该将AI使用视为开发对话的正常部分:分享经验、成功技术以及关于不该做什么的警告(比如”Copilot建议对X使用过时的库,所以要小心”)。
审查和改进对这种模式至关重要。开发人员应该手动审查和重构代码以提高模块化,添加全面的错误处理,编写详尽的测试,并在改进代码时记录关键决策。下一章将详细介绍这些流程。
传统的结对编程涉及两个人在一个工作站上协作。随着AI的出现,出现了一种混合方法:一个人类开发人员与AI助手一起工作。这种设置可以特别有效,提供了人类直觉和机器效率的融合。
在人类-AI配对中,开发人员与AI交互以生成代码建议,同时审查和改进输出。这种动态允许人类利用AI在处理重复任务方面的速度,例如编写样板代码或生成测试用例,同时保持监督以确保代码质量和相关性。
例如,在集成新库时,开发人员可能会提示AI起草初始集成代码。然后开发人员审查AI的建议,与官方文档交叉参考以验证准确性。这个过程不仅加速了开发,还促进了知识获取,因为开发人员深入参与AI的输出和库的复杂性。
让我们将此与传统的人类-人类结对编程进行比较:
人类-AI配对提供快速代码生成,可以高效处理平凡的任务。它对于独立开发人员或团队资源有限时特别有益。
人人配对 在复杂的问题解决场景中表现出色,这些场景需要细致的理解和协作式头脑风暴。它培养共同的所有权和集体代码理解。
两种方法都有其优点,你可以根据项目的复杂性、资源可用性和开发过程的具体目标来选择。
为了最大化AI辅助开发的好处,请考虑以下实践:
这有助于保持上下文清晰,并确保AI的建议与当前的具体任务相关。
提供清晰具体的指令可以提高AI输出的质量。
定期集成和测试AI生成的代码有助于早期发现问题并保持项目进度。
持续评估AI的贡献,根据需要提供更正或改进,以指导其学习并改善未来的建议。
除了代码生成,AI还可以作为有价值的验证者,协助代码审查和质量保证。AI工具可以分析代码中的潜在错误、安全漏洞以及对最佳实践的遵循情况。例如,DeepCode和Snyk的AI驱动代码检查器等平台可以识别缺失输入清理或不安全配置等问题,直接在开发环境中提供可操作的见解。Qodo和TestGPT等平台可以自动生成测试用例,确保更广泛的覆盖范围并减少手动工作。许多AI工具还可以协助监控应用程序性能,检测可能表明潜在问题的异常情况。
通过将AI验证者集成到开发工作流中,团队可以提高代码质量,减少缺陷的可能性,并确保符合安全标准。这种主动的验证方法补充了人工监督,从而产生更强大、更可靠的软件。这些工具通过处理重复和耗时的任务来提高质量保证(QA)流程的效率和有效性,使人工测试人员能够专注于QA中更复杂和更细致的方面。
将AI融入开发过程,无论是作为配对程序员还是验证者,都提供了提高生产力和代码质量的机会。通过深思熟虑地集成这些工具,开发人员可以利用人类和人工智能的优势。
为了最大化AI和人类能力在QA中的好处,我建议以下几个最佳实践:
使用AI进行初步评估和初步扫描,以识别明显的问题。
优先对关键领域进行人工审查,如复杂功能、用户体验和AI局限性。
培养持续协作的环境,让AI工具和人工测试人员协同工作,通过持续的反馈循环来改进AI性能和人类决策。
虽然vibe coding在软件开发中提供了前所未有的速度和创造性自由,但其灵活性本身要求采用结构化方法来确保一致的质量和团队凝聚力。如果没有平衡创造性探索与工程规范的明确指导原则,快速、直观的AI辅助开发可能很快导致混乱。
这些黄金法则来自成功将vibe coding集成到工作流程中的团队的集体经验。它们代表了关于AI擅长的领域、AI遇到困难的领域以及人类判断如何在整个过程中保持必要性的来之不易的见解。这些原则不是限制创造力,而是创建一个框架,让团队能够自信地进行实验,同时保持生产就绪软件所需的标准。
这些规则涉及vibe coding的三个关键维度:人类与AI之间的交互、将AI生成的代码集成到现有系统中,以及培养支持可持续AI辅助开发的团队实践。通过遵循这些指导原则,团队可以利用vibe coding的变革力量,同时避免导致技术债务、安全漏洞或无法维护的代码库的常见陷阱:
: 在与AI交互时,清楚地阐明你的需求、任务和预期结果。精确的提示词产生精确的结果。
: AI生成的代码必须始终与你的原始目标进行核对。在接受之前验证功能、逻辑和相关性。
: 将AI输出视为需要你仔细监督的草稿。提供反馈、完善,并确保质量和正确性。
: 利用AI自动化常规或复杂任务,但在解决问题和做决策时始终保持积极参与。
: 在开始AI驱动的开发之前,与团队就AI使用标准、代码期望和实践达成一致。
: 定期与团队讨论AI经验、技术、成功和陷阱。将AI规范化为集体改进的另一个工具。
: 在版本控制中清楚地识别和分离AI生成的变更,以简化审查、回滚和跟踪。
: 通过让所有贡献都接受相同的严格审查流程来保持一致的标准,提高代码质量和团队理解。
: 除非你完全理解其功能和影响,否则永远不要集成AI生成的代码。理解对于可维护性和安全性至关重要。
: 清楚地记录AI生成代码的基本原理、功能和上下文。良好的文档确保长期清晰度并减少未来的技术债务。
: 记录能够产生高质量AI输出的提示词。维护一个经过验证的提示词库,以简化未来的交互并增强一致性。
: 定期审查和完善你的AI开发工作流。利用过去经验的洞察来持续增强团队的方法。
通过遵守这些黄金法则,你的团队可以有效地利用AI,在保持清晰度、质量和控制的同时提高生产力。
70%问题定义了AI辅助开发的当前状态:这些工具擅长生成样板代码和常规函数,但在最后30%的工作上遇到困难,包括边缘情况、架构决策和生产就绪性。我们确定了两种主要使用模式——快速构建MVP的启动者和将AI集成到日常工作流的迭代者——以及常见的失败模式,如”后退两步”反模式和”演示质量陷阱”,其中令人印象深刻的原型在现实世界压力下失败。
已经出现了三种经过验证的工作流模式:AI作为初稿者(生成然后完善)、AI作为结对程序员(持续协作)和AI作为验证者(人工编写的代码加上AI分析)。Vibe编码的黄金法则提供了重要的防护措施,强调清晰的沟通、彻底的验证、团队协调,以及在合并之前理解所有代码的不可妥协的要求。
个人开发者应该选择一种工作流模式进行系统性实验,同时在日常实践中实施黄金法则。专注于培养第4章中涵盖的持久技能:系统设计、调试和架构——而不是在代码生成上与AI竞争。
团队需要建立AI使用标准,创建有效提示词的共享库,并将AI考虑因素整合到现有的敏捷实践中。定期分享成功和陷阱的知识将帮助团队避免常见陷阱,同时最大化AI的好处。
随着自主AI编码代理(autonomous AI coding agent)的出现,人类的角色将转向架构监督和战略决策。下一章探讨如何最大化这种不可替代的人类贡献,帮助各个级别的工程师作为日益强大的AI系统的合作伙伴而不是竞争对手而蓬勃发展。
1 本章基于最初发表在我的Substack新闻通讯上的一篇文章。参见Addy Osmani,“70%问题:关于AI辅助编码的硬道理”,与Addy Osmani一起提升,2024年12月4日。
你已经看到像Cursor、Cline、Copilot和Windsurf这样的AI编码助手如何改变了软件的构建方式,承担了大部分繁重工作和样板代码——大约70%。1 但是将玩具解决方案与生产就绪系统分开的最后”30%“的工作呢?这个差距包括困难的部分:理解复杂需求、设计可维护的系统、处理边缘情况和确保代码正确性。换句话说,虽然AI可以生成代码,但它经常在工程方面遇到困难。
Tim O’Reilly 在回顾几十年的技术变革时提醒我们,每一次自动化的飞跃都改变了我们如何编程,但并未改变我们为什么需要熟练的程序员。我们面临的不是编程的终结,而是”我们今天所知的编程方式的终结”,这意味着开发者的角色正在演变,而不是消失。
当今工程师面临的挑战是拥抱 AI 在其最擅长领域的能力(前 70%),同时加倍投入剩余 30% 所需的持久技能和洞察力。本文深入探讨专家见解,以识别哪些人类技能仍然至关重要。我们将探讨高级和中级开发者应继续利用什么,以及初级开发者必须投资什么才能在 AI 时代蓬勃发展。
因此,本章的目标是为您提供实用指导,以最大化那不可替代的 30% 的价值,并为各个级别的工程师提供可操作的要点。
如果您是高级工程师,您应该将 AI 编码工具的出现视为放大您影响力的机会——前提是以正确的方式利用您的经验。高级开发者通常拥有深厚的领域知识、对可能出错之处的直觉,以及做出高层技术决策的能力。
这些优势是 AI 无法单独处理的 30% 的一部分。本节探讨经验丰富的开发者如何最大化其价值。
让 AI 处理代码的第一稿,而您专注于架构解决方案,然后完善 AI 的输出。在许多组织中,Steve Yegge 写道,我们可能会看到团队只需要”高级员工”的转变,他们”(a) 描述要完成的任务;即创建提示,以及 (b) 审查结果工作的准确性和正确性”。拥抱这种模式。作为高级开发者,您可以将复杂需求转化为对 AI 助手有效的提示或规范,然后用您的批判性眼光审查生成的每一行代码。您实际上是在与 AI 结对编程——它是快速打字员,但您是大脑。
在审查期间保持高标准:确保代码符合您组织的质量、安全性和性能基准。通过充当架构师和编辑,您可以防止”高审查负担”压倒您。(一个警示说明:如果初级员工只是将原始 AI 输出直接扔给您,请予以回击——建立一个流程,要求他们必须首先验证 AI 生成的工作,这样您就不是唯一的安全网。)
高级工程师经常推动大型项目或处理初级工程师无法单独应对的复杂重构。AI 可以通过处理大量机械性变更或在您的指导下探索替代方案来增强这些工作。Yegge 为这种工作方式引入了术语面向聊天的编程 (CHOP,chat-oriented programming)——“通过迭代提示优化进行编码”,将 AI 作为协作者。利用 CHOP 在您承担的工作上更加雄心勃勃。
拥有 AI 辅助降低了项目值得投入时间的门槛,因为原本可能需要几天的工作现在可以在几小时内完成。因此,高级开发者可以尝试那些总是看似略微遥不可及的”如果能……就好了?“项目。
关键是保持引导性思维:您决定追求哪些工具或方法,并将各个部分整合成一个连贯的整体。您的经验使您能够筛选 AI 的建议——接受那些合适的,拒绝那些不合适的。
另一个高级工程师的关键职责是指导经验较少的团队成员有效使用 AI 以及永恒的最佳实践。你可能拥有通过艰难经验获得的知识,了解初级工程师可能看不到的陷阱,例如内存泄漏、差一错误(off-by-one errors)和并发危险。
由于初级工程师现在可能通过 AI 生成代码,因此教他们如何自我审查和测试这些代码非常重要。通过展示如何彻底测试 AI 贡献来树立榜样,并鼓励质疑和验证机器输出的文化。一些组织(甚至包括律师事务所)已经制定了规则:如果有人使用 AI 生成代码或文字,他们必须披露并自己验证结果——而不是假设高级同事会发现错误。
作为高级工程师,在你的团队中倡导这样的规范:欢迎使用 AI,但需要保持严谨。通过这种方式指导初级工程师,你可以分担一些监督负担,并帮助他们更快地成长到那 30% 的技能水平。
你的广泛经验和背景比以往任何时候都更重要。高级开发人员通常拥有关于公司为什么以某种方式构建事物或行业如何运作的历史知识。这种领域精通能力让你发现 AI 的失误,而新手可能察觉不到。
继续投资于深入理解问题领域。这可能意味着及时了解业务需求、用户反馈或影响软件的新法规。除非你告诉 AI,否则它不会自动纳入这些考虑因素。当你将领域洞察力与 AI 的速度相结合时,就能获得最佳结果。
此外,利用你的远见来引导 AI。例如,如果你知道快速修复会在未来造成维护困难,你可以指示 AI 实现更可持续的解决方案。相信你多年来磨练的直觉——如果一段代码看起来”不对劲”或好得令人难以置信,就深入研究。十次中有九次,你的直觉发现了 AI 没有考虑到的问题。能够预见代码的二阶和三阶效应是高级工程师的标志;不要让 AI 的便利性削弱这种习惯。相反,将其应用于 AI 产生的任何内容。
随着 AI 承担一些编码工作,高级开发人员可以将更多精力投入到工程的人性化方面:与利益相关者沟通、主持设计会议,以及做出将技术与业务战略相结合的判断。Tim O’Reilly 和其他人建议,随着常规编码变得更容易,价值转移到决定构建什么以及如何编排复杂系统上。
高级工程师通常是编排和看到全局的人。承担起这个角色。主动编写架构路线图,评估采用哪些工具(AI 或其他),或定义你组织的 AI 编码指南。这些是 AI 无法完成的任务——它们需要经验、人类判断力,并且通常需要跨团队的共识建设。通过增强你的领导存在感,你确保自己不仅仅是一个代码生成器(可以被另一个工具替代),而是一个指导团队的不可或缺的技术领导者。
简而言之,继续做经验丰富的开发人员最擅长的事情:见树木更见森林。AI 将帮助你砍伐更多树木,但仍然需要有人决定砍哪些树以及如何用木材建造一座稳固的房子。你的判断力、战略思维和指导能力现在比以往任何时候都更重要。有效利用 AI 的高级开发人员可以比不使用的人生产力高得多——但真正出类拔萃的将是那些运用人类优势来放大 AI 输出的人,而不仅仅是让它肆意运行。
正如一位Reddit用户观察到的,“AI是一个编程力量倍增器”,“大大提高了高级程序员的生产力”。倍增效应是真实存在的,但被倍增的是你的专业知识。保持这种专业知识的敏锐性,并将其置于开发过程的中心。
中级工程师可能面临最大的演进压力。许多传统上占用你时间的任务——实现功能、编写测试、调试简单问题——正变得越来越自动化。
这并不意味着过时;而是意味着提升。重点从编写代码转向构建更专业的知识,以下部分将探讨这一点。
随着系统变得更加复杂,理解和管理组件之间的边界变得至关重要。这包括API设计、事件模式(schema)和数据模型——所有这些都需要仔细考虑业务需求和未来的灵活性。深化你的计算机科学基础知识,包括获得对以下学科的深入理解:
这些知识帮助你理解AI生成代码的影响,并做出更好的架构决策。
同时也要学会处理边缘情况和模糊性。现实世界的软件充满了异常场景和不断变化的需求。AI倾向于默认解决一般情况。由开发者来提出”如果…会怎样?“的问题,并探寻弱点。
这里持久的技能是批判性思维和前瞻性——列举边缘情况、预测失败并在代码或设计中解决它们。这可能意味着考虑空输入、网络中断、异常用户操作或与其他系统的集成。
理解业务背景或用户环境将揭示通用AI根本不知道的边缘情况。有经验的工程师习惯性地考虑这些场景。系统地实践测试边界和质疑假设。专注于人类理解仍然至关重要的复杂领域。通用领域包括:
软件工程特定领域包括前端和后端工程、移动开发、DevOps和安全工程等。领域专业知识提供了当前AI工具所缺乏的背景知识,并帮助你更好地决定在何处以及如何应用它们。
虽然LLM可以提供基本的优化建议,但识别和解决系统级性能问题需要对整个技术栈的深入理解,从数据库查询模式到前端渲染策略。随着代码生成变得更加自动化,理解系统如何在生产环境中运行变得更有价值。
关注以下领域:
监控和可观测性(observability)
性能分析与优化
安全实践与合规
成本管理与优化
随着AI编写大量代码,严格审查和测试这些代码的能力变得更加关键。Yegge强调:“每个人都需要对测试和审查代码变得更加认真。”像对待初级开发人员的输出一样对待AI生成的代码:你是代码审查员,负责发现错误、安全漏洞或草率的实现。这意味着要加强你在单元测试、集成测试和调试方面的技能。
编写良好的测试是一项持久的技能,它迫使你理解规范并验证正确性。明智的做法是假设在证明之前什么都不工作。AI通常会产生功能性但未经优化的代码,直到你引导它进行迭代改进。这可能是由于多种原因,包括用于训练编码模型的训练数据并没有尽可能完整地反映所有最佳实践。
培养测试思维:验证每个关键逻辑路径,使用静态分析或代码检查工具,如果AI给出的代码不符合你的质量标准,不要回避重写它。即使你遵循前一章讨论的”AI作为验证器”模式,质量保证也不是一个可以简单外包给AI的领域——这正是人类勤奋的闪光之处。当软件没有按预期工作时,你需要真正的解决问题的能力来诊断和修复它。AI可以协助调试(例如,通过建议可能的原因),但它缺乏对应用程序运行的特定上下文的真正理解。人类测试人员拥有特定领域的知识和对用户期望的理解,而AI目前缺乏这些。这种洞察力在评估潜在问题的相关性和影响时至关重要。诊断复杂的错误通常需要创造性的问题解决和考虑广泛因素的能力——这些都是人类固有的技能。评估软件行为的伦理影响,如公平性和可访问性,需要人类的敏感性和判断力。
能够推理复杂的错误——重现它、隔离原因、理解底层系统(操作系统、数据库、库)——是一项永恒的工程技能。这通常需要对基础知识有深刻的理解(内存和状态如何工作、并发等),初级开发人员必须通过实践来学习。将AI用作帮手(它可能会解释错误消息或建议修复方法),但不要盲目依赖它。在调试时有条不紊地排查问题和应用第一性原理的技能,使优秀的开发人员脱颖而出。这也是一个反馈循环:调试AI编写的代码将教会你下次更好地提示AI或避免某些模式。
软件项目不仅仅是孤立的编码任务;它们存在于用户需求、时间表、遗留代码和团队流程的更大背景中。AI对大局没有天生的感知,比如项目的历史或某些决策背后的理由(除非你明确将所有这些内容输入到提示中,这通常是不切实际的)。人类需要承载这些上下文。
这里的持久技能是系统思维——理解系统一个部分的变化如何影响另一个部分,软件如何服务于业务目标,以及所有活动部件如何连接。这种整体视角让你能够适当地使用AI输出。例如,如果AI建议一个与监管要求或公司惯例相矛盾的巧妙捷径,你会发现它,因为你了解上下文。努力学习项目的背景并阅读设计文档,这样你就可以培养关于什么合适、什么不合适的判断力。
最后,一个元技能(metaskill):学习新工具和适应变化的能力。AI 辅助开发领域正在快速发展。保持开放心态并学习如何有效使用新 AI 功能的工程师将保持领先地位——Tim O’Reilly 建议”渴望学习新技能”的开发者将从 AI 中获得最大的生产力提升。投资于深入学习基础知识并对新技术保持好奇心。这种组合使你能够将 AI 作为工具来使用,而不会变得依赖它。
这是一个平衡的过程:使用 AI 来加速你的成长,但也要偶尔不使用它来练习,以确保你不会跳过核心学习(一些开发者会定期进行”AI 排毒(AI detox)“,以保持他们的原始编码技能的敏锐性)。简而言之,成为那种不断学习的工程师——这是任何时代都能证明职业价值的技能。
当实现时间减少时,在业务需求和技术解决方案之间进行转换的能力变得更加有价值。能够有效地与产品经理、设计师和其他利益相关者沟通的工程师将变得越来越有价值。这里的重点领域包括:
需求收集和分析
技术写作和文档编写
项目规划和估算
团队领导力和指导
中级工程师可能不再花费数天时间实现新功能,而是花时间设计能够优雅处理规模和故障模式的健壮系统。这需要对分布式系统原理、数据库内部机制和云基础设施有深入的理解——这些领域目前 LLM 提供的价值有限。
练习设计解决大规模实际问题的系统。无论代码如何生成,这些技能都保持价值,因为它们需要理解业务需求和工程权衡。
设计一个连贯的系统需要理解权衡、约束以及超越编写几个函数的”大局”。AI 可以生成代码,但不会自动为复杂问题选择最佳架构。
整体设计——组件如何交互、数据如何流动、如何确保可扩展性和安全性——是那 30% 需要人类洞察力的部分;这包括以下内容:
负载均衡(load balancing)和缓存(caching)策略
数据分区(data partitioning)和复制(replication)
故障模式(failure modes)和恢复程序
成本优化(cost optimization)和资源管理(resource management)
高级开发者长期以来一直在磨练这项技能,中级和初级开发者应该积极培养它。从模式和原则的角度思考(如关注点分离和模块化)——这些指导 AI 生成的解决方案走向可维护性。记住,扎实的架构不会偶然出现;它需要有经验的人来掌控方向。
记住,AI 应该成为你工作流程中不可或缺的一部分——这不是应该抵制的东西。将 AI 融入日常工作的实用方法包括:
搭建初始代码结构
快速原型和概念验证(proof of concepts)
结对编程以加快调试和问题解决
建议优化方案和替代方法
处理重复的代码模式,让你专注于架构和设计决策
有一种日益增长的说法认为,中级软件工程师应该”直接退出”——因为随着 AI 处理实现细节,纯工程技能将变得过时。虽然这个结论被夸大了,但关于工程之外技能(如设计)重要性的讨论值得关注。在 2024 年 12 月 X 平台上的一次代表性交流中,@fchollet 写道:
我们即将进入一个可以将测试时计算转化为能力的世界——这在软件历史上是第一次,边际成本将变得至关重要。
对此,@garrytan 回应道:
UX、设计、对工艺的实际投入将在下一个时刻成为中心舞台。
真正做出人们想要的东西。软件和编码不会成为限制因素。创造优秀软件的关键是成为通才(polymath)的能力,以及在多个领域同时保持聪明和高效。
成功的软件创作始终需要的不仅仅是编码能力。正在改变的不是工程的消亡,而是纯实现障碍的降低。这种转变实际上使工程判断力和设计思维变得更加关键,而非相反。
想想是什么让 Figma、Notion 或 VSCode 这样的应用成功。不仅仅是技术卓越——而是对用户需求、工作流程和痛点的深刻理解。这种理解来自以下方面:
用户体验设计思维
深厚的领域知识
对人类心理和行为的理解
考虑性能、可靠性和可扩展性的系统设计
商业模式对齐
最优秀的工程师一直以来都不仅仅是编码者。他们是既理解技术约束又理解人类需求的问题解决者。随着 AI 工具降低实现的摩擦,这种整体性理解变得更加宝贵。
然而,这并不意味着每个工程师都需要成为 UX 设计师。相反,这意味着要培养更强的产品思维能力,与设计师和产品经理建立更好的协作技能。这意味着更多地思考用户,理解他们的心理和行为模式,并学会做出支持用户体验目标的技术决策。你正处于实现技术优雅的阶段:现在要通过密切关注实际用户需求来平衡它。
Tan 继续发帖说:
UX、设计、对工艺的实际投入将在下一个时刻成为中心舞台。
真正做出人们想要的东西。软件和编码不会成为限制因素。创造优秀软件的关键是成为通才的能力,以及在多个领域同时保持聪明和高效。
未来属于那些能够在人类需求和技术解决方案之间架起桥梁的工程师——无论是通过自己培养更好的设计敏感性,还是通过与专业设计师更有效的协作。
如果你是初级或经验较少的开发者,你可能对 AI 感到既兴奋又焦虑。AI 助手可以编写你可能不知道如何自己编写的代码,有可能加速你的学习。然而,有关于“初级开发者之死”的头条新闻,暗示入门级编程工作面临风险。与流行的猜测相反,虽然 AI 正在显著改变早期职业体验,但初级开发者并未过时。
你需要主动培养能力,确保自己贡献的价值超越AI能够产出的内容。随着这些任务日益自动化,通过实现基本的CRUD应用和简单功能来学习的传统路径将会演变。
考虑一个典型的初级任务:按照现有模式实现一个新的API端点。以前,这可能需要一天的编码和测试时间。有了AI辅助,实现时间可能会降到一小时,但关键技能变成了:
充分理解现有系统架构,以便正确指定需求
审查生成的代码以发现安全隐患和边界情况
确保实现与现有模式保持一致
编写验证业务逻辑的全面测试
这些技能无法纯粹通过遵循教程或AI提示来学习——它们需要生产系统的实际经验和资深工程师的指导。
这种演变对早期职业开发者来说既是挑战也是机遇。入门级职位的门槛可能会提高,需要更强的基础知识来有效审查和验证AI生成的代码。然而,这种转变也意味着初级工程师可能在职业生涯早期就能解决更有趣的问题。
以下是如何投资自己以有效应对那30%差距的方法。
依赖AI回答每个问题(“如何在Python中做X?”)而从不真正吸收底层概念,这很诱人。抵制这种冲动。将AI用作导师,而不仅仅是答案售货机。例如,当AI给你一段代码时,询问它为什么选择那种方法,或让它逐行解释代码。
确保你理解数据结构(data structures)、算法(algorithms)、内存管理(memory management)和并发(concurrency)等概念,而不是总是依赖AI。原因很简单:当AI的输出错误或不完整时,你需要自己的心智模型来识别和修复它。如果你没有积极思考AI为什么生成某些代码,你实际上可能学到的更少,阻碍你的成长。所以花时间阅读文档,从头编写小程序,巩固你的核心知识。这些基础知识是持久的;即使你周围的工具发生变化,它们也会为你服务。
要建立真正的信心,有时你必须独自飞行。许多开发者倡导设立”无AI日”或定期限制AI辅助。这确保你仍然可以仅凭自己的技能解决问题,这对于避免技能退化(skill atrophy)很重要。你会发现这迫使你真正思考问题的逻辑,这反过来使你更擅长使用AI(因为你可以更智能地指导它)。
此外,每当你在AI生成的代码中遇到bug或错误时,在要求AI修复之前,先亲自跳进去调试它。通过使用调试器或添加打印语句来查看出了什么问题,你会学到更多。
将AI建议视为提示,而非最终答案。随着时间推移,解决任务中那些棘手的最后部分将培养你在AI困难领域的技能——正是这使你有价值。
作为初级开发者,你可以养成的最佳习惯之一是为代码编写测试。如果你使用AI生成代码,这一点尤为重要。
当你从 LLM 获得一段代码时,不要假设它是正确的——要质疑它。编写单元测试(或使用手动测试)来验证它是否真正处理了需求和边界情况。这样做有两个目的:它能捕获 AI 输出中的问题,并且训练你在信任实现之前先思考预期行为。
你甚至可以使用 AI 来帮助编写测试,但你要定义测试什么。Yegge 的建议关于认真对待测试和代码审查适用于所有级别。如果你培养出仔细验证工作的声誉(无论是否有 AI 辅助),资深同事会更加信任你,你也会避免他们觉得你只是在向他们”倾倒”有问题代码的情况。
实际操作中,开始将测试视为开发的组成部分,而不是事后补充。学习如何使用测试框架,如何进行探索性手动测试,以及如何系统地重现 bug。这些技能不仅让你在 30% 的工作中表现更好,还能加速你对代码实际工作方式的理解。
记住:如果你捕获了 AI 引入的 bug,你刚刚做了 AI 做不到的事情——这就是附加价值。
初级开发者通常专注于”让代码运行起来”。但在 AI 时代,获得一个基本可运行的版本很容易——AI 能做到这一点。更难的部分(也是你应该关注的)是编写可读、可维护和简洁的代码。
开始培养对良好代码结构和风格的敏感度。将 AI 的输出与你知道的最佳实践进行比较;如果 AI 代码混乱或过于复杂,主动重构它。例如,如果 LLM 给你一个做太多事情的 50 行函数,你可以将其拆分为更小的函数。如果变量名不清楚,重命名它们。
本质上,假装你在审查同事的代码,像同事写的一样改进 AI 的代码。这将帮助你内化良好的设计原则。随着时间推移,你会开始以产生更简洁代码的方式提示 AI(因为你会指定你想要的风格)。软件维护者(通常在几个月或几年后工作)会感谢你,你也会证明你的思考超越了”让它运行”——你在像工程师一样思考。保持可维护性正是那 30% 人类驱动工作的核心,所以从职业生涯一开始就把它作为你的关注点。
不可否认,“提示工程”(prompt engineering)——有效地与 AI 工具交互的技能——是有用的。作为初级开发者,你绝对应该学习如何向 AI 提问,如何给它提供适当的上下文,以及如何迭代提示以改进输出(本书的[第 2 章]是一个很好的起点)。这些是能让你脱颖而出的新技能(许多经验丰富的开发者也还在摸索!)。但是,记住善于提示通常是理解问题的代理。如果你发现无法让 AI 做你想要的事,可能是因为你需要先澄清自己的理解。将此作为信号。
一个策略是在要求 AI 实现之前,自己先用简单的英语概述解决方案。此外,尝试不同的 AI 工具(Copilot、Claude 等)来了解它们的优势和劣势。你越熟练使用这些助手,就越有生产力——但永远不要将它们的输出视为绝对正确。将 AI 想象成一个超强版的 Stack Overflow:一个辅助工具,而不是权威。
你甚至可以使用 AI 帮助构建小型个人项目来挑战自己的极限(“我能借助 AI 的帮助构建一个简单的 Web 应用吗?”)。这样做会教你如何将 AI 集成到开发工作流程中,这是带入团队的一项很好的技能。只需在如前所述的时间段内平衡无网络工作。
最后,一个能加速你成长的持久技能是寻求反馈和向他人学习的能力。AI 不会因为你忽略它的建议而生气,但你的人类队友和导师对你的发展是无价的——尤其是在软技能、领导力、沟通和应对办公室政治方面。
不要犹豫向资深开发者询问为什么他们偏好某个解决方案而不是另一个,特别是当它与AI建议的不同时。与更有经验的同事讨论设计决策和权衡——这些对话揭示了经验丰富的工程师如何思考,这对你来说是宝贵的财富。在代码审查中,要特别乐于接受关于你的AI生成代码的评论。如果审查者指出”这个函数不是线程安全的”或”这种方法会有扩展性问题”,花时间去理解根本问题。这些正是AI可能遗漏的东西,而你需要学会发现它们。随着时间推移,你会建立一个心理检查清单。
此外,寻找结对编程(pair programming)的机会(即使是远程)。也许你可以与一位在工作流程中使用AI的资深开发者”结对”——你会观察到他们如何提示AI以及如何纠正它。但更重要的是,你会看到他们如何沟通、引导讨论以及处理微妙的团队动态。对反馈保持开放并主动寻求指导,将帮助你从执行AI能做的任务成长为执行只有人类才能做的高价值任务。从某种意义上说,你正在尝试尽可能高效地获得通常伴随经验而来的智慧。这使你不仅仅是房间里的另一个编码者——而是让你成为团队渴望留住和提升的那种工程师。
构建软件是一项团队运动。AI不参加会议(谢天谢地)——人类仍然必须与其他人交谈以澄清需求、讨论权衡并协调工作。强大的沟通技能和以往一样有价值。练习提出好问题并清晰地描述问题(对同事和对AI都是如此)。
有趣的是,提示AI本身就是一种沟通形式;它要求你精确表达你想要什么。这与一项核心工程技能重叠:需求分析(requirements analysis)。如果你能制定清晰的提示或规范,这意味着你已经思考了问题。
此外,分享知识、编写文档和审查他人的代码都是AI无法替代的协作技能。在未来,随着开发者”与”AI一起工作,团队中的人与人之间的协作——确保正在解决正确的问题——仍然至关重要。一个新兴趋势是,开发者可能会更多地专注于高层设计讨论(通常AI作为参与者)和协调任务,本质上承担更多的指挥者角色。沟通和领导技能将在那个指挥者的位置上很好地服务于你。
值得注意的是,在AI时代,初级开发者需要转变思维方式:你需要从消费解决方案转变为创造理解。在过去,你可能会费力地阅读文档最终编写一个功能;现在AI可以将解决方案直接呈现给你。如果你只是消费它(复制粘贴然后继续),你并没有成长多少。
相反,将每个AI给出的解决方案作为学习案例。剖析它,实验它,并思考你自己可能如何得出它。通过将AI输出不视为终结所有问题的答案,而是作为互动学习材料,你确保你——人类——持续提升。这样,AI不是取代你的成长,而是加速它。
许多专家认为,虽然AI可能会减少对大量初级”代码工”团队的需求,但它也提高了成为初级开发者的门槛。这个角色正在转变为能够有效地与AI合作并快速攀升价值链的人。如果你采纳本节讨论的习惯,你将把自己区分为一个初级开发者,不仅仅带来AI能带来的东西(任何公司都可以通过订阅获得),而是带来洞察力、可靠性和持续改进——未来资深开发者的特质。
为了在AI增强的开发世界中茁壮成长,各个级别的工程师都应该加倍投入AI尚未(还)能够复制的持久技能和实践。无论我们的工具变得多么先进,这些能力都将保持至关重要。特别要关注以下领域:
加强你的系统设计和架构专业知识
练习系统思维并保持对全局的上下文理解
磨练你在批判性思维、问题解决和前瞻性方面的技能
在专业领域建立专业知识
代码审查、测试、调试和质量保证
提高你的沟通和协作技能
适应变化
持续学习,在获得新技能和更新知识的同时保持基础扎实
使用AI
这些技能构成了软件工程中的人类优势。它们是持久的,因为它们不会随着下一个框架或工具的变化而过期;实际上,AI的崛起使它们更加突出。Simon Willison认为,AI辅助实际上使强大的编程技能变得更有价值,而不是更少,因为那些具有专业知识的人可以更有效地利用这些工具。
一台强大的机器在不熟练的人手中可能是危险的或被浪费的,但在有能力的人手中它是变革性的。在AI时代,一位经验丰富的工程师就像一位拥有新的先进副驾驶的资深飞行员:旅程可以走得更快更远,但飞行员仍必须驾驭风暴并确保安全着陆。
软件工程一直是一个持续变化的领域——从汇编语言到高级编程,从本地服务器到云,现在从手动编码到AI辅助开发。每一次飞跃都自动化了编程的某些方面,然而每一次,开发人员都适应了并找到了更多要做的事情。在回应Tim O’Reilly的一篇笔记时,一位HN评论者指出,过去的创新”几乎总是为开发人员带来更多工作、更多增长、更多机会”。AI的崛起也不例外。它不是让开发人员变得无关紧要,而是重塑成功所需的技能集。平凡的70%编码工作正在变得更容易;具有挑战性的30%成为我们价值的更大部分。
为了最大化人类的30%,专注于永恒的工程技能:深入理解问题,设计简洁的解决方案,审查代码质量,并考虑用户和上下文。经验丰富的程序员从AI中获得更多,因为他们知道如何引导它以及当它失败时该做什么。那些将这些技能与AI工具结合起来的人将超越那些只有其中一种的人。事实上,专家们达成的共识是,AI是熟练者的工具:“LLMs是为强大用户(power users)设计的强大工具(power tools)。”这意味着我们每个人都有责任成为那个”强大用户”——培养让我们有效使用这些新工具的专业知识。
最终,软件工程的技艺不仅仅是编写可以工作的代码。它是关于编写运行良好的代码——在真实世界环境中,随着时间推移,在不断变化的需求下。今天的AI模型可以帮助编写代码,但还不能确保代码在所有这些维度上都运行良好。这是开发人员的工作。
通过加倍投入刚才概述的技能,资深开发人员可以继续领导和创新,中级开发人员可以加深他们的专业知识,初级开发人员可以加速他们通往精通的旅程。AI将处理越来越多的例行工作,但你的创造力、直觉和深思熟虑的工程将把原始输出转化为真正有价值的东西。AI是一个强大的工具,但关键在于我们如何使用它。良好的工程实践、人类判断和学习意愿将继续是必不可少的。
在实际操作中,无论你是与一个”渴望的初级”AI进行结对编程来编写你的函数,还是审查一个充满AI生成代码的差异,永远不要忘记应用你独特的人类视角。问问自己:这是否解决了正确的问题?其他人能够理解和维护这个吗?风险和边缘情况是什么?这些问题是你的责任。编程的未来确实将涉及更少的手动输入每个分号,更多的指导和策划——但它仍然需要掌舵的开发人员,他们有智慧正确地完成它。
最终,伟大的软件工程始终关乎问题解决,而不仅仅是编写代码。AI 并没有改变这一点:它只是挑战我们将问题解决能力提升到新的层次。拥抱这一挑战,你将在我们行业的新篇章中蓬勃发展。
[1] 本章基于我首次在 Substack 新闻通讯上发表的两篇文章:Addy Osmani,“超越 70%:最大化 AI 辅助编程中人类的 30%”,Elevate with Addy Osmani,2025 年 3 月 13 日;以及 Addy Osmani,“让你的软件工程职业生涯面向未来”,Elevate with Addy Osmani,2024 年 12 月 23 日。
[2] 要了解更多关于系统思维(systems thinking)的内容,请参阅 Donella H. Meadows,系统思维:入门,第 2 版(Rizzoli,2008);以及 Peter M. Senge,第五项修炼:学习型组织的艺术与实践(Crown,2010)。
[3] 有关此主题的更多信息,请参阅 Mark Richards 和 Neal Ford,软件架构基础,第 2 版(O’Reilly,2025);以及 Mark Richards、Neal Ford 和 Raju Gandhi,深入浅出软件架构(O’Reilly,2024)。
你已经学会了如何提示 AI 生成代码,到目前为止,你可能已经使用这些技术产生了一些代码。现在进入了一个关键阶段:确保代码是正确的、安全的和可维护的。
作为开发者,你不能只是接受 AI 的输出并盲目地交付它。你需要审查它、测试它、可能改进它,并将其与代码库的其余部分集成。本章重点介绍如何理解 AI 提供给你的内容,迭代地编辑和调试它,并完全掌控代码作为项目的一部分。
本章涵盖:
根据你的原始意图解释 AI 的代码
“多数解决方案”现象,或者为什么 AI 生成的代码通常看起来像常见解决方案
审查代码清晰度和潜在问题的技术
当 AI 编写的代码未按预期工作时进行调试
为样式或效率重构代码
编写测试来验证代码的行为
通过掌握这些技能,你将能够自信地将 AI 的贡献集成到你的项目中。
当你获得 AI 的代码时,你的第一步应该是将其与你的意图(你给出的提示)进行比较。代码是否满足你设定的要求?有时 AI 可能会轻微误解或只是部分实现你所要求的内容。
仔细阅读代码。在脑海中或纸上逐步执行它:
追踪它对典型输入的处理。
如果你的提示有多个部分(“做 X 和 Y”),验证 AI 是否全部完成了。
确保 AI 没有添加你未要求的功能——有时它会添加它”认为”有用的额外功能,比如添加日志记录或参数,这可能是好的也可能不是。
就像你对待同事的代码一样,如果有什么不清楚的地方,记录下来。如果你为它的存在寻找一个好的理由,你可能会找到一个。如果找不到,就质疑它或考虑删除它。
例如,如果你要求一个素数检查器,而 AI 代码还为每个数字打印类似”正在检查 7…“的内容,这可能是你如何提示它的副产品或来自其训练数据的模式(一些教程代码会打印其进度)。如果你不想要那个,计划删除它或提示 AI 删除它。
还要确保边缘情况(edge
cases)按你的预期处理。如果你希望它处理空输入,它做到了吗?如果输入可能是
None 或负数,AI 考虑到了吗?
如果你的提示中有什么是模糊的,AI 必须选择一种解释,识别发生这种情况的地方。也许你没有指定输出格式,它选择打印结果而不是返回它们。现在你必须决定是接受它还是修改代码。
这个理解阶段至关重要;不要跳过它。即使你要测试代码,通过阅读来理解它也很重要,因为测试可能无法覆盖所有内容(而且对于一些明显的事情,阅读更快)。
最后,考虑 AI 的假设。AI 通常倾向于”多数”或最常见的解释(这引出我们下一节的内容)。
在大量代码上训练的AI模型通常会生成训练数据中最常见的解决方案(或最符合条件的最简单解决方案)。 我称之为多数解决方案效应(majority solution effect)。它在一般情况下是正确的,但可能不是你特定情况下的最佳选择。
例如,如果你在没有进一步上下文的情况下请求一个搜索算法,AI可能会输出一个基本的线性搜索,因为这很直接且常见。也许你实际上需要二分搜索,但AI不知道效率至关重要,因为你没有说明。线性搜索适用于许多中等情况,但如果性能是关键则不适用。
类似地,AI可能使用全局变量,因为许多简单示例都这样做,但也许在你的项目中,这不是可接受的实践。
要注意AI的解决方案可能针对通用场景进行优化。作为人类开发者,你对AI缺乏的上下文有深入了解。
为了解决这个问题:
识别代码中的假设。如果它假设列表已排序或输入有效,这个假设可以吗?你指定了吗?如果没有,也许它应该包含一个检查。
考虑替代方案:如果你知道解决问题的多种方法(如不同的算法),AI选择了哪一个?是你想要的那个吗?如果不是,你可以提示要求替代方案或直接更改它。
如果AI代码适用于”常规”情况但不适用于对你重要的边缘条件,那就需要修复。例如,也许它没有考虑某些数学运算中的整数溢出。在许多训练示例中,这可能没有被处理,但在你的上下文中,这可能很重要。
理解AI倾向于通用解决方案将使你更擅长审查其代码。它不是魔法或量身定制的;它是对解决方案的非常有根据的猜测。定制是你的工作。
AI生成的代码通常有一些明显的模式。 它可能:
包含比平常更多的注释或措辞奇怪的注释(因为它从教程代码中学习,而教程代码往往有大量注释)
一致地使用某些变量名(如循环中的i、j、k)
以某种冗长的风格编排代码(以涵盖一般情况)
检查这些并考虑它们是否与你项目的风格匹配。 代码在功能上可能没问题,但需要可读性检查。 在该检查中,你可能想要:
重命名变量以更具描述性或与你的代码库保持一致。
删除或优化注释。如果它在一个不言自明的if语句上方添加了像# 检查数字是否为质数这样的注释,你可以删除它。但如果它有一个解释复杂逻辑位的注释,那很好——保留或改进它。
通过运行代码通过linter或formatter(如Python的Black或Go的gofmt)来确保一致的格式化,以匹配你想要的间距和括号样式。
还要查找任何不寻常的结构。 AI是否定义了多个类或函数,而你期望一个?有时它可能将问题分解为多个函数,因为训练示例就是这样做的。如果这过度了,你可以内联它们(或相反)。代码是太聪明还是太简单?AI有时会产生一个非常直接的解决方案,或者偶尔是一个花哨的单行代码。这是否符合你团队的偏好?如果不符合,请相应调整。
其他需要注意的潜在问题包括:
是的,AI也会犯这种错误。例如,循环边界可能很棘手。如果有时间,在脑海中测试一个简单的情况通过循环。
代码是否假设文件成功打开或所有输入都采用正确格式?如果需要,添加错误处理。
性能陷阱
也许AI在大型数据集上使用内循环进行成员检查,尽管存在更好的方法,比如使用集合。AI的解决方案可能是正确的,但不是最优的。
如果代码使用了库,请确保这是你想要使用的库(并且它是可用的)。有时它可能会使用numpy来做简单的求和(因为它在训练数据的示例中看到过这种用法)。如果引入该依赖不值得,你可以切换到纯Python或你原本打算使用的库。
有时,AI代码可能会有一些小的不一致,比如函数文档字符串说的是一回事,但代码做的是另一回事(例如,如果它修改了逻辑但没有修改注释)。请修复这些问题。
这在经过充分测试的模型中很少见,但在某些语言中可能会混淆某些东西,这并非不可能。
AI可能会使用库函数的旧版本,例如该函数已经发生了变化。如果你看到一个不认识的函数调用,快速查看库文档以确保它对于你使用的版本是正确的。
如果AI输出使用了像”Your code here”这样的占位符(很少见,但在通用模板中可能会发生),请填充这些内容。
简而言之,将AI代码视为实习生写的并已经下班离开的代码。你需要审查它的质量并正确集成它。
假设你运行了代码(或为其编写测试,我们很快会讲到)但某些东西不工作。调试AI生成的代码与调试你自己或其他人的代码没有什么不同——只是你没有编写它,所以你可能不太熟悉。但因为你已经仔细阅读过它,你已经处于良好的状态(见图5-1)。
以下是调试的六步方法:
复现问题。
使用会失败的输入运行函数或代码。观察输出或错误。
定位问题的源头。
使用典型的调试技术,如打印语句,或使用调试器逐步执行。如果是逻辑错误(错误的输出),手动跟踪逻辑或使用打印语句查看它在哪里偏离了你的预期。
将提示与代码进行对照检查。
有时错误只是代码没有完全实现需求,比如如果你要求对某些东西进行排序但它没有正确排序。这可能意味着AI的逻辑有缺陷,或者没有处理边缘情况(如空列表)。
利用AI来调试!
你实际上可以将有问题的代码反馈给AI并说:“这段代码对X给出了错误的结果。你能帮我找到bug吗?”通常,它会分析它(就像代码审查)并指出问题。例如,也许它会发现循环应该到len(arr)但却到了len(arr)-1。它可能会更快地发现这一点。(也要注意不要完全信任它——但这就像请同事帮忙调试。)
修复代码。
现在你有一个选择:手动修复它或提示AI提供修正版本。如果修复很明显,就直接做。如果不明显,你可以尝试这样说:“上述函数在输入X时失败(预期Y,得到Z)。请纠正它。”然后AI可能会相应地调整代码。
再次测试。
确保bug已解决,并且没有引入新问题。
我建议使用测试驱动调试(test-driven debugging)。如果可能,为关键函数编写一些测试(本章后面的测试部分将详细介绍)。任何失败的测试都会直接显示出问题所在。对于除最简单函数之外的任何东西,这可能比手动检查更快。
最后,在调试时,确保你问的是为什么,而不仅仅是什么。试着理解为什么 AI 会犯这个错误。是提示词在这一点上不够清晰吗?这可以帮助你了解下次如何提示,或者是否需要始终仔细检查 AI 输出的这个方面。例如,如果你注意到 AI 经常不处理空输入(除非明确告知),你就会开始在提示词中始终指定这一点,并在输出中对此进行审查。
一旦代码在功能上是正确的,就要考虑重构它,使其符合项目的标准,并使其在未来更易于使用。AI 的工作是快速为你生成代码;你的工作是对其进行打磨。
以下是重构的六步流程:
符合风格指南。
通过格式化工具或代码检查工具运行代码。修复任何警告,如”变量名应为小写”或”行太长”。这会立即使代码看起来与代码库的其余部分一致。许多 AI 工具在风格方面做得不错,但可能需要稍作调整。
改进命名和结构。
如果 AI 在类中将函数命名为 _helper1 和
_helper2,而你更喜欢有意义的名称,那就重命名它们。如果它创建了一堆只使用一次的小函数,也许可以内联它们,除非它们能增加清晰度。
删除任何不必要的部分。
例如,也许 AI 在输出中包含了你没有要求的主程序块或测试代码。如果你不需要这些,就删除它。相反,也许它将所有内容都写在一个函数中,但你想将其拆分成更小的部分以提高清晰度;如果是这样,现在就进行拆分。
添加文档。
如果这段代码打算成为其他人将使用的库或模块的一部分,请在适当的地方添加文档字符串或注释。AI 可能已经注释了一些,但要确保它符合你的标准。例如,也许你的项目需要特定的文档字符串格式,其中包含参数和返回值的文档。
根据需要优化。
既然代码能工作了,它是否足够高效?如果这段代码可能在紧密循环中或在大数据上调用,请检查其复杂度。AI 可能没有使用最优的方法(同样,“多数解决方案”可能是一个简单的循环,而不是更优化的方法)。如果性能是一个问题,重构为更好的算法。你可以再次让 AI 参与:
通过使用集合而不是列表进行查找来优化这段代码,使其运行更快。
但是,作为开发者,你通常知道你想要什么模式,所以你可能只是实现那个更改。
根据需要简化。
有时 AI 代码可能过于冗长。例如,它可能使用带有返回语句的 if-else,而使用带条件的单个返回语句就足够了。虽然明确的代码不一定不好,但你可能想将其简化为更少的行,以提高可读性而不失清晰度。
重构的目标是,如果另一个开发者稍后查看这段代码,不应该明显看出”这是 AI 写的”。它应该看起来就像好代码。这通常意味着赋予它使代码简洁的细微人工润色。
当你重构时,需要验证你没有破坏任何东西。那么让我们转到测试。
测试始终很重要,但对于 AI 生成的代码尤其重要,原因有二。首先,由于你不是从头开始编写的,你希望确保它在所有情况下都能工作。其次,如果你稍后提示 AI 进行更改或集成更多 AI 代码,测试可以帮助你确保任何新更改不会破坏现有功能。让我们快速看看不同类型的测试:
为你从AI获得的每个函数或模块编写测试,特别要覆盖边界情况。对于我们的质数示例,你可能需要测试一个质数、一个非质数、1(边界情况)、0或负数(可能需要定义预期行为)、一个大质数等等。如果代码通过了所有这些测试,它很可能是正确的。
你甚至可以要求AI生成这些测试:
为上述函数编写PyTest单元测试,覆盖边界情况。
它通常做得不错。不过,仍要审查它们以确保它们有效并覆盖你认为必要的内容。
如果AI代码与代码库的其他部分交互,比如使用数据库的函数,编写一个在上下文中调用它的测试。它实际上是否将应该存储的内容存储到数据库了?如果它产生被另一个函数使用的输出,在测试中将它们链接起来。
如果这段代码是更大工作流的一部分,从头到尾运行一个场景。例如,如果AI代码是Web路由的一部分,在测试环境中对该路由进行测试请求,看看格式、错误处理和其他所有内容是否正常。
你需要进行的测试级别取决于代码的关键性和复杂性。但即使是快速的手动测试运行或脚本中的简单断言语句,也比没有验证要好。记住,测试不仅仅是发现错误;它还锁定行为。如果你以后更改某些内容(或AI这样做),测试可以帮助你确保代码的功能不会退化。
测试也是主张所有权(ownership)的好方法。一旦你测试并修复了任何问题,你就可以对代码充满信心。在这一点上,可以说代码是”你的”,就像代码库中的任何其他代码一样。你理解它,信任它,并有测试来保护它。
一些AI编码工具开始集成测试建议。例如,CodeWhisperer有时会在一段代码后建议一个断言。将这些建议作为起点,但不要假设它们100%全面。想想创造性的边界情况——这是人类直觉仍然非常有价值的地方。
我们已经完成了代码的生成、理解、调试和重构。对于一个小函数,这个循环可能在很短的时间内发生(几分钟内);对于复杂的模块,可能需要更长的时间(数小时或数天,需要间歇性的AI协助)。
重要的是要承认你,开发者,对最终代码负责。AI是加速创建的工具,但如果出现故障,它不会承担责任。还存在许可或版权风险:一些AI提供商表示,超过一定长度的输出可能在统计上包含复制的材料。这种情况很少见,提供商在很大程度上缓解了这个问题,但就像你扫描Stack Overflow答案以查找任何明显的许可文本或归属一样,快速检查一下——特别是如果输出很大或过于干净。例如,如果你提示”实现快速排序”,AI给你20行原始代码,这可能没问题且是常识。但如果你询问一些晦涩的东西并获得大量代码,尝试在线搜索其中的唯一字符串,看看它是否是从某处逐字提取的。这个问题最近变得更加明显,有记录在案的案例显示AI系统从期刊文章和其他受版权保护的来源中复制文本。作为负责任的代码所有权的一部分,开发者应该验证任何AI生成内容的来源,如果它似乎超出了通用模式或对特定来源异常具体。
最后,将代码集成到你的项目中:将其添加到版本控制系统中,也许在提交消息中提到AI提供了帮助。这不是必需的,但有些团队喜欢跟踪它。
随着时间的推移,随着需求的变化,你可能会修改这段AI生成的代码。像对待任何其他代码一样对待它:不要想,“哦,那是AI的代码;我会要求AI更改它。”如果你愿意,可以这样做,但你也可以随意手动修改它。做最高效和可维护的事情。
通过仔细审查和测试,AI生成的代码成为你项目中的又一段代码。在那时,AI是否编写了第10行或你编写的已经无关紧要——重要的是它满足项目的需求和标准。
通过遵循这些实践,你在确保质量的同时利用AI编码的速度。你避免了毫无疑问地信任AI输出的陷阱,而是将其集成到专业的开发工作流程中。
接下来,[第6章] 探讨了AI工具如何从根本上改变软件开发的原型设计阶段。我将探索利用AI助手的实用技术,以加速从初始概念到可工作原型的过程,通常将开发时间从数天缩短到数小时。讨论涵盖了具体的AI驱动原型设计工具,包括Vercel v0和截图转代码工具,以及在AI指导下进行迭代改进的策略。
我还将探讨从AI生成的原型过渡到生产就绪代码的关键过程,分析当AI成为开发工作流程的核心部分时所产生的机遇和潜在挑战。通过真实世界的案例研究,我将展示开发人员如何成功使用AI快速测试想法同时保持代码质量——并避免从概念快速实现时可能出现的常见陷阱。
本章探讨AI驱动的氛围编码(vibe coding)如何加速软件开发的原型设计阶段。原型设计的核心是将想法快速转化为可工作的模型。借助AI助手,开发人员可以在几小时内完成通常需要数天的工作,快速迭代概念。我将讨论使用AI从概念到原型的技术,比较流行的AI原型设计工具(包括Vercel v0和截图转代码工具),并探讨如何在AI指导下迭代改进原型。我还将讨论将粗糙的AI生成原型过渡到生产质量代码的关键步骤。在整个章节中,我还将研究AI驱动原型设计带来成功结果的案例,并展示这种方法的潜力和陷阱。
原型设计从AI生成代码的速度中获益匪浅。原型设计的目标不是完善的、生产就绪的代码,而是可以评估和改进的概念验证(proof of concept)。AI编码助手在这方面表现出色,能够从最少的输入快速生成可运行的代码。例如,开发人员无需手动编写UI模型代码,而是可以用自然语言描述所需的界面,让AI生成HTML/CSS或React组件。这使得从想法到实现的周期非常快速。
一位UX工程师描述了使用生成式AI工具如何彻底改变了他的工作流程:“我使用生成式AI生成功能原型的速度令人惊叹。我在几个小时内构建的原型通常需要数天时间。”AI自动处理常规样板代码和重复模式,使他能够专注于更高层次的设计决策。在原型设计中,这意味着你可以在相同的时间内尝试更多想法。另一个好处是AI可以几乎即时填充原型的繁琐部分(基础UI组件、表单处理、示例数据),使开发人员能够专注于正在测试的核心概念或独特功能。
然而,重要的是要记住原型代码通常是一次性代码。AI可能快速生成可工作的解决方案,但该代码可能没有为可维护性或扩展性而构建。这在原型中是可以接受的,因为速度和实验比优雅更重要——只要你计划为生产环境重构或重写关键部分(我将在本章后面讨论这个过程)。正如第[3]章和第[4]章所探讨的,AI可以自信地处理原型约70%的编码工作,而你指导整体架构并纠正任何关键缺陷。
带有意图的编程(programming with intent)是[第1章]中介绍的概念,在这里至关重要。你提供高层次的指令(你希望原型做什么或看起来如何的意图),让AI填充实现细节。为了说明这一点,让我们使用AI助手完成一个非常简单的原型设计示例。假设你有一个为新服务创建注册页面的想法。你想快速原型化前端,因此从以下开发者提示开始:
创建一个简单的 HTML 页面,包含一个注册表单,字段包括姓名、邮箱和密码,以及一个提交按钮。使用基本样式使其看起来美观。
现代代码助手如 Cline 可能会提供一个完整的 HTML/CSS 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sign Up</title>
<style>
body { font-family: Arial, sans-serif; max-width: 400px; margin: 2em auto; }
form { display: flex; flex-direction: column; }
label { margin-top: 1em; }
input, button { padding: 0.5em; font-size: 1em; }
button { margin-top: 1em; background: #4CAF50; color: #fff; border: none; }
</style>
</head>
<body>
<h2>Sign Up</h2>
<form>
<label>Name: <input type="text" name="name" required></label>
<label>Email: <input type="email" name="email" required></label>
<label>Password: <input type="password" name="password" required></label>
<button type="submit">Create Account</button>
</form>
</body>
</html>
这段 AI 生成的代码是注册表单原型的良好起点。它包含了基本布局和一些内联 CSS 样式。作为开发者,你不需要手写任何 HTML/CSS——只需描述想要的结果,AI 就会提供实现方案。这展示了意图编程(programming by intent):你专注于 UI 应该做什么,AI 会找出如何实现。
当然,这只是一个简单的例子。在实践中,原型可能更复杂,涉及多个文件、框架或数据。但原理是相同的:使用 AI 快速创建初稿。然后你可以在浏览器中打开这个原型,查看效果并进一步优化。这引出了下一个主题:使用超越纯语言提示的专业 AI 原型工具。
虽然 Gemini、ChatGPT 和 Claude 等通用助手可以从提示生成原型代码,但专业 AI 辅助原型工具的生态系统持续快速发展。在我写作时,可用的工具提供了不同的方法来处理 AI 生成原型中保真度(fidelity)和控制度之间的基本权衡。
这个生态系统已经成熟到可以服务于不同的原型需求。一些工具擅长将视觉设计转换为代码,允许设计师上传截图或草图,并在几秒钟内获得可工作的 HTML、CSS 或 React 组件。这种”截图转代码”(screenshot-to-code)能力极大地加速了设计到代码的过程,特别是当你有手绘草图或 Figma 设计需要快速实现时尤为有价值。Vercel v0 等工具体现了这种方法,提供对原始设计的高保真度,同时在代码结构控制上做出一些权衡。
其他平台专注于通过对话界面生成完整应用程序。这些工具允许用户用自然语言描述功能并获得全栈实现。例如,想要在不编码情况下快速构建应用的设计师可能会使用 Lovable 或 Bolt.new 等工具,它们提供高级提示界面,可以搭建整个应用程序的脚手架。一些设计师报告称,使用这些工具在几小时内构建了传统上需要几天才能完成的功能原型,AI 自动处理繁琐的组件和模式。
第三类工具将AI直接集成到开发环境中,在原型设计阶段充当智能结对编程伙伴。这些AI增强的IDE(集成开发环境),如Cursor、Windsurf和Cline,允许开发者在代码生成过程中保持更多控制权,同时仍能从AI加速中受益。这些工具的区别通常在于工作流理念:有些优先考虑自动应用更改以实现快速实验,而另一些则要求明确接受修改以进行更谨慎的迭代。
所有这些方法的共同点是它们能够缩短从概念到工作原型的过程。然而,它们在两个关键维度上存在显著差异,这些维度决定了它们对不同用户和用例的实用性。
保真度(Fidelity)指的是生成的输出与你的输入或意图的匹配程度。截图转代码工具通常对视觉设计提供高保真度,但可能产生与你的架构偏好不一致的代码。对话式工具可能对你的需求解释得更宽松,生成功能性但通用的实现,需要进一步完善。
控制力涵盖你引导和修改生成过程的能力。有些工具像黑盒一样运作,产生完整的输出,而其他工具则允许通过持续对话或直接代码编辑进行迭代优化。当你需要特定的架构模式、性能优化或与现有代码库集成时,这个维度变得至关重要。
理解这些权衡有助于为你的原型设计需求选择合适的工具。验证新交互模式的设计师可能会优先考虑保真度和速度,接受对实现细节的较少控制。探索技术可行性的开发者可能更看重控制力和透明度,即使这意味着在生成过程中需要更多手动干预。
这些工具都不能在没有人工监督的情况下生成生产级质量的代码。它们通常提供我所说的”80%原型”——功能足以测试概念并向利益相关者演示,但需要额外工作才能用于生产部署。剩余的20%通常涉及安全加固、性能优化、错误处理和架构改进。
即使在快速原型设计期间,快速代码审查仍然是必不可少的。虽然你可能不会打磨原型中的每个细节,但扫描明显问题(如暴露的API密钥或不安全的数据处理)可以防止问题传播到后续开发阶段。大多数现代工具都提供对生成代码的透明度,允许你检查和理解正在创建的内容。
随着AI原型设计领域的持续发展,具体工具无疑会发生变化,但保真度与控制力之间的基本考虑,以及对人工监督的需求,将保持不变。关键是理解你的原型设计目标,并选择符合你特定需求的方法,无论是快速视觉实现、功能演示还是技术探索。
AI驱动的原型设计的优势之一是迭代循环:你可以生成初始版本,然后通过与AI交互来完善它。无需手动编辑代码,你只需告诉AI你想要改变什么(见图6-1)。虽然我主张采用比纯粹的”凭感觉编码”更负责任的方法,但快速反馈循环绝对是原型设计中值得拥抱的特性。

大多数AI原型工具会保留你的请求历史或上下文,这非常有用。这意味着AI会记住你应用的目的和之前的指令,所以你不必每次都重新解释所有内容。这种上下文持久性(context persistence)是vibe-coding环境的标志:与AI的对话成为开发日志。
以下是典型的迭代优化可能的过程:
你提供提示词或输入来创建原型:
生成一个基础的费用跟踪应用,包含一个添加费用的表单和一个列出费用的表格。
你获得生成的代码并运行它。也许它能工作,但你注意到一些可以改进的地方。例如,UI是功能性的但很简陋,或者表格不能对费用进行排序。
你回到AI那里并提供额外的指令。例如:
让费用列表可以按金额或日期排序。
AI可能会修改代码以包含排序逻辑或使用可排序表格的库:
添加一些颜色样式,也许使用一个现代的CSS框架。
AI可以集成一个CSS库(如Tailwind或Bootstrap)或只是添加自定义样式使其看起来更好:
验证表单,这样就不能在没有名称和金额的情况下添加费用。
AI可能会添加简单的前端验证。
每一个这样的提示词都会修改原型。因为AI理解上下文(像Cursor这样的工具和持续的聊天工具会保持代码状态),它通常可以在正确的位置应用更改——例如,在表单中插入验证代码或重写表格渲染以包含可排序的列。
在每次优化之后,你检查结果。如果AI引入了新问题或没有完全按照你的意图做,你通过提示词进行澄清或修复:
排序是反向的——请默认按升序排序。
新的配色方案很好,但请将标题改为深蓝色而不是黑色。
每个迭代周期都相当快——通常只需要几秒钟的处理——这意味着你可以在一个小时内完成十几次迭代。与手动编码和检查所有这些更改相比,AI方法可以显著更快。这对于广泛的更改尤其如此,比如重新设计样式或添加功能。
重要的是,与AI迭代需要清晰的沟通。这就是你的提示词工程(prompt engineering)技能发挥作用的地方。你对想要的更改越明确和清晰,AI就越有可能正确执行。例如,说”让它看起来更好”是模糊的。一个更具体的提示词给助手一个具体的方向:
应用浅色主题,带有蓝色标题,并增加字体大小以提高可读性。
如果你使用像Vercel v0或Lovable这样的工具,你甚至可能使用其界面指向UI的某个部分并说,“将这个改为X。”
让我们看一个开发者和AI编码助手之间的简短对话迭代过程:
生成一个待办事项列表的React组件。它应该显示一个任务列表和一个添加新任务的文本输入框。
AI生成一个React组件,包含任务的状态(state)并渲染一个列表、一个输入框和一个添加任务的按钮:
很好。现在让每个任务可编辑,并添加一个复选框来标记完成。
AI更新代码,为每个任务渲染一个复选框和编辑功能,并相应地修改状态。开发者运行应用并发现编辑可以工作,但没有删除按钮:
为每个任务添加一个删除按钮。
AI在代码中添加删除功能:
布局有点拥挤。添加一些基本的CSS来间隔列表项并使字体稍大一些。
AI输出CSS样式,可能作为组件中的<style>或单独的CSS片段:
看起来好多了!
这种来回交流可以持续,直到原型符合愿景。最后,开发者获得了一个可工作的待办事项列表应用原型,具有创建、编辑、完成和删除功能——所有这些都是通过自然语言请求和快速的AI代码输出构建的。
在整个过程中,请记住开发者始终是决定发生什么的导演。AI 可能会提出一种实现功能的方式,但你来决定这是否符合你的需求。有时 AI 的实现是正确的,但不是你所期望的(也许它使用了与你想象中不同的 UI 方法)。你可以接受它(如果它不影响原型目标),或者指示 AI 改用你偏好的方法。
原型旨在作为概念验证和学习什么有效的工具。一旦它达到了这个目的——比如,你已经与用户验证了设计或证明了某个功能是可行的——下一步通常是将其转化为生产应用程序。这种转变是一个关键时刻。AI 仍然可以提供帮助,但人类开发者必须打磨原型的粗糙边缘。本节将探讨从原型转向生产代码时的一些关键考虑因素。
首先,仔细审查架构和代码结构。原型在底层可能很混乱。也许你所有的代码最终都放在一个文件中,或者为了速度而绕过了某些最佳实践。现在是引入适当结构的时候了。例如,如果原型是单页脚本,你可能会将其分成多个模块;对于 Web UI,你可能会引入适当的组件结构;对于后端,你可能会建立正式的模型-视图-控制器(MVC)架构模式。
虽然 AI 编写了原型的大部分内容,但你作为开发者最了解架构目标。你甚至可能启动一个全新的项目,将原型用作参考或脚手架,可能会重用一些原型代码,但通常将其视为一次性代码。其他人可能会逐步将原型代码库重构成型,让 AI 建议重构或生成测试以确保在清理过程中不会出现问题。
接下来,添加错误处理和边缘情况。原型代码通常专注于理想场景,但如果 API 调用失败怎么办?如果输入为空怎么办?系统地检查每个功能并考虑潜在的故障模式。
AI 可以帮助你头脑风暴边缘情况,给出如下提示:
这个功能有哪些潜在的错误情况以及如何处理它们?
助手可能会列出一些场景(网络错误、错误输入、并发问题),你可以为这些场景实现处理(或请求 AI 帮助实现)。确保代码的健壮性是使其为生产做好准备的一部分。
你的原型代码可能没有优化,因此检查任何低效或可能带来安全问题的部分。例如,也许原型中的 AI 使用了一种简单的算法,在小型测试数据集上有效,但在真实数据上会很慢。识别这些地方并优化它们。(我将在第8章介绍常见的 AI 生成代码缺陷。)
一种策略是对原型运行性能测试或使用分析器来查看瓶颈,然后请 AI 帮助优化该函数。一定要审查安全功能,如身份验证和数据处理——AI 原型使用没有适当参数化的 SQL 查询(存在 SQL 注入攻击风险)或包含敏感信息的情况并不少见。这些问题必须修复。2021年的一项研究发现,大约40% 的 AI 生成代码存在潜在漏洞。因此,生产化的一部分就是保持警惕。手动对代码运行静态分析和/或安全测试,或提示 AI “扫描此代码以查找安全问题”。
原型通常缺少文档,在你正式化代码时需要添加:对每个模块进行清晰的人工审查的解释将帮助未来的团队成员,以及在几个月后重新访问代码时帮助你自己。一旦你清理了代码,你可能会提示 AI 工具基于描述系统如何工作的代码生成 Markdown API 文档或 README。第1章讨论了 AI 如何生成代码解释;这是利用这一点的绝佳时机。
彻底测试你的原型至关重要,正如你在第5章中学到的。你可能为核心逻辑编写单元测试,为主要流程编写集成测试等。你可以通过要求 AI 生成测试用例来加速这一过程:
为待办事项列表组件编写 Jest 测试,覆盖添加、编辑、完成和删除任务的功能。
然后运行并调整它生成的测试。拥有一个良好的测试套件能让你在重构原型代码时更有信心。
有时你可能会决定完全替换代码的某些部分——比如原型使用了某个快速而粗糙的库或不适合长期使用的临时方案。AI 也可以加速这个过程。假设你的原型代码使用本地数组存储数据,但现在你需要适当的数据库集成。你的提示词可能是这样的:
集成 SQLite 数据库来存储任务,替代内存数组。
AI 可以为这个集成提供起点,然后你再进行优化。
在进行这些更改时,明智的做法是将思维模式从”快速原型模式”切换到更严谨的工程方法。AI 现在是你提升代码质量的助手——它不再只是快速输出功能。这种动态关系有些不同:由于稳定性和质量现在是你的首要任务,你可能会更批判性地评估 AI 的每个建议。正如我在第 4 章中提到的,资深开发者可以从 AI 中获得巨大收益,因为他们知道该接受什么、该修复什么。在这个阶段,你将大量运用这种资深思维:你对最终系统有清晰的愿景,因此你让 AI 负责特定的改进或实现。
为了使讨论更具体,让我们考虑一个简短的例子。想象一个独立开发者 Jane,她想构建一个小型 Web 应用,将 CSV 文件中的数据转换为图表。她使用 AI 助手在一个周末就完成了快速原型:一个基础的 Node.js 脚本和 API,加上一个简单的前端来上传 CSV 并使用 JavaScript 图表库渲染图表。
她向几个潜在用户演示了这个原型并获得了积极反馈,因此 Jane 决定将其转变为真正的产品(一个 Web 服务)。以下是她如何完成这个转变:
原型的 Node.js API 没有身份验证(任何人都可以上传数据)。对于生产环境,她需要用户账户和认证。她使用 AI 集成身份验证系统(可能基于 JWT)。AI 提供了框架,但她仔细审查以确保密码被正确哈希且令牌是安全的。她还为上传端点添加了输入验证(AI 没有做到这一点),结合了 AI 建议的代码和她自己的调整。
最初的前端是一个单独的 HTML 文件,其中的 script 标签指向依赖项的
CDN。Jane 决定重构为结构化的 React 应用以提高可维护性。她首先要求 AI
通过使用构建系统和 npm 而不是 script
标签来重构项目,使其更适合生产环境。然后她让 AI 帮助将它们集成为 React
组件。例如,它将原型中的图表渲染代码转换为 <Chart>
组件。Jane 使用 AI
来加快编写这些组件的速度,但她确保状态管理和组件层次结构遵循最佳实践(这是原型没有深入考虑的)。
Jane 为关键函数(CSV 解析、数据转换)编写单元测试。当她不确定边界情况时,她询问 AI:
CSV 解析应该测试哪些边界情况?
它建议了空字段和不规则列等场景,她将这些纳入测试中。她还注意到原型将整个 CSV 文件加载到内存中;对于大文件,这可能会崩溃。她修改代码以流式处理,并使用 AI 来复查她的流逻辑。现在应用可以更可靠地处理更大的文件。
原型 UI 是实用主义的。对于她的产品,Jane 在用户体验上花了更多时间。她让 AI 推荐响应式布局,也许还集成一个 CSS 框架。AI 添加了 Bootstrap,然后她用它来改善外观(表单、按钮、布局)。之后她手动微调了一些 CSS。这个完善阶段不太涉及大量编码,更多的是设计选择,但 AI 仍然通过为标准 UI 模式(如导航栏和加载旋转器)提供快速代码来提供帮助。
经过这些努力,曾经粗糙的原型已经成为一个更加简洁、更加安全、更具可扩展性的应用程序,可以供真实用户使用了。Jane部署了它,感到很有信心,因为她添加了测试并审查了AI生成的代码。从原型到生产的这个过程可能花了她几周时间,而从头开始编写整个产品则需要更长的时间。AI加速了最初的原型开发,并在过渡过程中继续提供帮助,但Jane的人工监督和重构对于达到生产质量是不可或缺的。
虽然AI驱动的原型开发功能强大,但也并非没有挑战。作为开发者,你应该意识到这些挑战并知道如何缓解它们。两个特别值得关注的领域是范围蔓延(scope creep)和集成。
因为使用AI添加功能非常容易,你可能会忍不住不断添加”再多一个东西”到原型中,这种现象被称为范围蔓延。这可能导致一个不断膨胀的原型,试图成为最终产品。记住原型的目的:专注于你想要回答的关键问题或要展示的核心体验。如果你发现自己在实现登录系统、支付处理等功能,问问自己这在原型阶段是否真的需要。最好将这些功能模拟出来(AI可以生成一个虚假的登录流程,不是真实的,只是为了模拟)。保持原型的专注性将节省你的时间,并使其更容易在后期丢弃或重做。
写下你的原型目标(“演示用户可以上传CSV并获得图表以测试可行性”),并将其作为北极星。使用AI快速达到这个目标,抵制完美化原型的诱惑。
其次,还有集成到真实系统的问题。原型通常使用模拟数据或简化的子系统。如果你的AI原型使用虚拟数据或本地文件,将其与生产环境中的真实数据库或服务集成可能并不简单。在原型开发时要注意采取了哪些捷径。例如,也许原型邮件实际上并没有发送,只是记录到控制台。在生产环境中,你需要一个真实的邮件服务。AI可以帮助稍后集成这些,但最好记录下来:在原型开发时维护一个”如果我们继续推进需要解决的事项”列表。这样你就不会忘记哪些部分是临时的。如果在团队中工作,要清楚地沟通这些。例如,你可以在代码中留下注释:// TODO: 在此集成真实邮件服务。许多AI工具在生成简化解决方案时实际上会自己包含这样的TODO注释,这很有帮助。
通过预见这些挑战,你可以有效地使用AI原型开发而不会落入其陷阱。当有思虑地使用时,结果是一个在创纪录的时间内开发出的稳健原型,准备好转化为最终产品或在提取了它提供的经验教训后搁置。
在本章中,你看到了AI辅助的vibe coding如何为原型开发过程加速。通过让AI处理代码生成的繁重工作,开发者可以以前所未有的速度从概念转向工作模型。我介绍了用于UI生成的Vercel v0、用于全栈原型的Lovable,以及像Cursor和Windsurf这样的AI增强IDE——每个都支持快速原型开发的不同方面。我还强调了AI原型开发的迭代性质:在快速循环中生成、测试和改进,用自然语言提示指导变更。
虽然AI驱动的原型开发可以在几小时内产生一个功能性演示,我们也讨论了向生产环境过渡的关键。信息很明确:原型不是最终产品。它是第一稿。人类开发者必须重构和加固代码,AI在这个过程中继续提供帮助(建议改进、生成测试等)。使用这些技术的个人和团队的案例研究突显了真实的生产力提升——原型在几天而不是几周内构建完成,实现更快的用户反馈和业务决策。
到目前为止,你应该理解vibe coding如何使原型开发感觉更像是与助手进行头脑风暴,而不是艰难地编写样板代码。这是一种根本不同的感觉:更加对话化、更加高层次,而且快得多。然而,你也看到了即使在快速原型中保持代码质量意识的重要性——当然,在将其发展到原型阶段之外时更是如此。
在[第7章]中,我将把重点从快速原型设计转向在AI辅助下进行全面的Web应用开发。虽然原型设计是在探索可能性,但全面的开发需要系统化的方法来处理架构、实现和部署。
本章将重点从快速原型设计转向使用AI辅助开发完整的Web应用。Web应用通常包括前端(通常使用React、Angular或Vue等框架编写)、后端(APIs、数据库、服务器)以及连接所有组件的粘合代码。Vibe编码可以加速这些层的开发。
我将引导您完成使用AI结对编程构建Web应用的端到端工作流程,包括:
设置项目及其脚手架
编写前端UI代码
实现后端逻辑
集成数据库
测试和验证整个技术栈
在此过程中,我将重点介绍前端的AI开发模式(例如,让AI根据描述生成React或Vue组件)和后端的开发模式(通过自然语言提示编写路由、业务逻辑和数据库查询)。我还将介绍如何在全栈项目中优化人类与AI之间的协作,确保双方都能发挥各自的优势。在本章结束时,您应该能够清楚地了解如何使用AI不仅处理独立的编码任务,还能高效地管理整个Web开发工作流程。
每个Web应用都从某些脚手架开始——构建工具、文件结构、依赖项等的初始设置。AI可以自动创建大量样板代码。现代Web框架通常配备命令行界面(CLI)工具,可以生成基础项目,但您可能仍需要配置某些内容或集成其他库。AI助手可以通过引导您使用这些CLI工具或按需设置自定义项目结构来提供帮助。
例如,假设您想使用React作为前端、Express作为后端来启动一个新的应用项目。在AI出现之前,这项任务的工作流程可能如下所示:
运行CLI工具或Vite来设置React项目。
初始化Express应用(可能使用npm init并安装Express)。
为开发设置代理或配置跨域资源共享(CORS),以便React前端可以与Express后端通信。
可能集成像MongoDB这样的数据库,或设置SQLite文件以供简单使用。
使用像Cursor或Cline这样的AI编码环境,您可以一次性描述您想要的设置:
设置一个新项目,包含React前端(使用Vite)和Express后端。后端应为待办事项列表提供REST API,并使用内存数组作为起点。配置前端以在开发环境中代理API请求到后端。
高级AI IDE可以接受此指令并执行以下操作:
创建两个目录(frontend和backend)。
运行npm create vite@latest(如果它有shell访问权限)或模板化一个基础React应用。
在后端初始化一个基础Express服务器文件,包含类似/api/to-dos的端点(返回一些示例数据)。
在每个目录中包含一个package.json文件,其中包含相关脚本(如start both)。
通过在React开发服务器中配置代理或提供实现CORS头的说明,设置前端和后端之间的通信。
几分钟内,您就会拥有一个全栈Web应用的骨架。即使AI没有自动完成所有操作,它也可能会为您提供代码和完成设置所需的说明(例如,“将此代理设置添加到您的React package.json文件中”)。这节省了大量无意义的设置时间,让您可以立即专注于功能开发。
如果您没有使用AI IDE,您仍然可以逐步使用ChatGPT或其他助手;例如:
我想创建一个新的React应用。我应该运行什么命令?
AI 可以引导你完成各个步骤,或推荐像 Vite 或 Next.js 这样的新选择:
现在设置一个带有 /api/to-dos 路由的 Express 服务器。
它可以生成 Express 服务器的代码,你可以复制到文件中:
在开发过程中,我如何将 React 应用连接到这个 API?
它可能会建议代理配置,或告诉你如何调用 API(如果不使用代理,会包括完整的 URL)。
这样,即使是设置基本架构也变成了一次对话,而不是在文档中寻找答案。如前面章节所述,意图编程意味着你告诉 AI 想要什么结果,它会找出实现步骤。设置项目是这种方式的完美场景。
在这个阶段,明确你的架构决策很重要。AI 会跟随你的引导。架构和高层决策需要人来做,所以你要自己决定技术栈和主要模式:你想要单体仓库还是前后端分离的仓库?使用 REST 还是 GraphQL?使用哪个数据库?
一旦你有了这些想法,就可以相应地指导 AI:
同时为 SQLite 数据库设置一个基本的 Prisma schema。
或者:
使用 GraphQL 服务器而不是 REST。
AI 可能无法完美执行复杂的设置,但它会完成大部分工作,你可以在此基础上进行优化。
许多有经验的开发者将这些步骤整合到项目模板中,或使用样板代码生成器,但 AI 提供了更灵活的方法:你可以使用自然语言即时定制。这意味着如果你的项目有些特殊(也许需要三个服务而不是常见的两层架构,或者想预配置像 Tailwind CSS 这样的特定库),只需要求 AI 包含你想要的内容即可。
一旦脚手架准备就绪,开发 Web 应用的前端是工作的主要部分。本节探讨如何利用 AI 结对编程助手来编写前端代码。
你可以通过描述组件的功能和外观来要求 AI 创建组件;例如:
创建一个名为 TodoList 的 React 组件,它接收一个待办事项列表并显示它们。每个项目应显示其标题和一个复选框来标记完成状态。
AI 应该生成函数式组件的代码,根据需要包含 props 和 state:
创建一个 Vue 组件用于登录表单,包含用户名和密码输入框,并在提交时发出包含表单数据的事件。
AI 可能会相应地输出
<template>、<script> 和
<style>
部分。作为开发者,你跳过了编写样板代码,直接得到需要的结构。之后很容易根据需要进行调整。AI
通常甚至会包含基本的验证或状态处理,如果你的提示暗示需要它们的话。
在这个阶段确保一致性很重要。如果你孤立地生成多个组件,可能需要调整它们以协同工作。例如,如果
TodoList 期望项目是某种 prop 形式,确保使用
TodoList
的任何组件都提供该形式。你可以在一个提示中生成所有组件(这样 AI
就知道所有内容),或者自己连接它们并要求 AI 修复任何不匹配。
CSS 和样式可能很繁琐。描述你想要的外观,让 AI 处理 CSS 细节:
为待办事项列表组件添加样式:使用 flex 列布局,添加一些间距,将已完成项目的文本颜色改为灰色并加上删除线。
对于登录表单组件,将其在页面上居中,并使输入框更大且带有圆角边框。
助手可以输出 CSS-in-JS、纯 CSS 或内联样式,具体取决于上下文。如果你使用像 Tailwind CSS 这样的框架,甚至可以要求它输出适当的类(尽管请记住,并非所有模型都完全了解 Tailwind)。
重点是:你可以在设计上迭代,而无需手动调整 CSS
值。这让你的注意力保持在更高的抽象层次上—指定什么看起来好,而不是编写每个
margin 和 color。
Web 前端通常需要从后端获取数据,并使用 Redux、context 或简单的组件状态来管理状态(state)。AI 可以帮助编写这些集成部分;例如:
添加代码,在 TodoList 组件挂载时从 /api/to-dos
获取待办事项列表,并存储到状态中。
在 TodoList 中实现一个函数,当复选框被切换时,向
/api/to-dos/{id}/complete 发送 POST
请求,然后相应地更新状态。
AI 可以生成 React 中的 useEffect
钩子(hook)来执行获取操作,或生成 Vue 中的 mounted()
钩子。它还可以生成 HTTP 调用的存根(使用 fetch 或 Axios
等)。你需要确认 API
端点和负载与后端的预期匹配(如果你已经构建了后端或有相关规范)。
如果你还没有构建后端,你可能同时在使用 AI 创建它——我们很快就会讲到这一点。但你可以在 AI 的辅助下并行开发前端和后端,因为只要你跟踪它们之间的接口,每一部分都可以相对独立地指定和生成。
如果你的前端有复杂的逻辑,例如动态表单验证规则、条件渲染或复杂的用户交互,你可以在 AI 的帮助下逐步实现这些功能。一个好的做法是将问题分解:
添加一个功能:当用户勾选待办事项的”完成”复选框时,淡出该列表项(CSS 过渡),然后在 1 秒后将其从列表中移除。
AI 可能会生成代码,在勾选时添加一个 CSS 类,并使用超时来移除该项,包括淡出所需的 CSS:
表单有一个可选的”备注”字段。仅当勾选了”添加备注”复选框时才显示备注文本区域。
AI 可以修改组件状态和 JSX 以条件渲染备注字段。
这些都可以是迭代式的提示。本质上,你描述 UX 行为,AI 编写代码。在每次添加后始终进行测试,以确保其按预期运行。
不同的框架有不同的习惯用法:
在 React 中,AI 可能使用钩子(如
useState、useEffect)。仔细检查它是否遵循最佳实践(例如,useEffect
中的依赖项数组(dependencies)是否正确)。
在 Vue 中,AI 可能会根据它所见过的内容输出选项式 API 风格或组合式 API 风格。如果你偏好其中一种,应该明确指定(例如,“使用 Vue 3 组合式 API”)。
在 Angular 中,AI 可以生成组件,但 Angular 的学习曲线更陡峭。AI 可能能够根据请求生成模板、TypeScript 类和基本的服务注入,但你可能需要做更多手动工作或使用 Angular CLI 构建结构,然后要求 AI 填充特定部分(如表单验证逻辑)。
现在让我们转向后端。使用 AI 构建 Web 应用程序的服务器端遵循类似的范式:你描述你想要的端点、数据模型和逻辑,AI 生成代码。常见的后端组件包括路由处理器、业务逻辑、数据库交互和验证。AI 可以帮助处理所有这些。
假设你正在为待办事项应用构建一个 RESTful API。你可能会有这些端点:GET /to-dos、POST /to-dos、PUT /to-dos/:id、DELETE /to-dos/:id。你可以逐个端点进行开发:
在 Express 应用中,添加一个 GET /api/to-dos
路由,返回待办事项列表(暂时使用内存中的数组即可)。
添加一个 POST /api/to-dos 路由,接受 JSON
主体并将新待办事项添加到列表中。返回带有 ID 的新待办事项。
AI 会相应地编写 Express 路由处理程序,可能会使用类似
app.get('/api/to-dos', ...) 的代码。如果你指定使用 Express
和 JSON,它可能会包含必要的中间件(如果尚未存在):
app.use(express.json())
随着后端的增长,你可以要求 AI 进行重构:
将 Express 路由重构到单独的路由器模块中。
它可能会将路由拆分到单独的文件中,这是保持可维护性的良好实践。
你可能会在原型中使用内存数据,但对于更完整的应用,你需要一个数据库。假设你选择 MongoDB 或 PostgreSQL。你可以这样提示:
使用 Mongoose 将 MongoDB 集成到 Express 应用中。创建一个包含以下字段的待办事项模型:title(字符串)、completed(布尔值)。修改 GET/POST 路由以使用数据库而不是内存数组。
AI 可能会输出 Mongoose 模型定义,并调整路由处理程序以查询数据库(例如
GET 使用 Todo.find(),POST 使用
Todo.create())。同样,对于 SQL,你可以要求它设置一个对象关系映射(ORM),如 Prisma 或
Sequelize。请记住,你可能需要提供配置详细信息(如连接字符串)。AI
可能不知道你的数据库 URI;你需要自行填入。但它会处理通用代码。
如果你的后端有特定规则(例如,用户无法删除标记为重要的待办事项,或者列表标题必须唯一),你可以通过 AI 编码这些规则:
为
POST /api/to-dos路由添加验证:如果标题为空或超过 100 个字符,则拒绝并返回 400 状态。
AI 会包含检查并发送适当的响应。
添加逻辑:当待办事项标记为完成时(通过
PUT /api/to-dos/:id),如果所有待办事项都已完成,记录消息”全部完成!”
它可以在 PUT 处理程序中插入该逻辑。
你用简单的语言描述这些需求,AI 会相应地修改代码。你仍然需要测试代码是否符合预期。
许多 Web 后端使用框架,而不仅仅是原始的 Express(如 Node 的 NestJS 或 Python 的 Django)。AI 也可以处理这些框架,尽管你可能需要将更复杂的任务进一步分解:
对于 Django(Python),你可以这样提示:
创建一个包含字段 X 的 Django 待办事项模型,以及相应的列表和创建视图。
AI 可能会输出模型代码和通用视图,或者如果它了解上下文的话,会输出 DRF(Django REST Framework)序列化器/视图集。
[内容]
当在你的代码中集成数据库时,你可能需要比简单CRUD更复杂的查询。假设你想获取所有项目及其任务和分配给每个任务的用户——这需要跨Project、Task、User表进行连接。你可以这样提示:
编写一个SQL查询,检索项目及其任务和每个任务的分配用户名。
AI可以为你生成一个SQL连接查询。
或者如果你使用ORM:
使用Sequelize,获取所有项目及其关联的任务和每个任务的用户。
你可以期待代码会包含类似这样的内容来加载相关数据:
include: [Task, { model: User, as: 'assignedUser' }]
数据库操作需要仔细验证,以确保AI生成的代码与你的实际模式(schema)一致并保持数据完整性。除非你在提示中明确提供这些信息,否则AI无法自动知道你的具体表名、字段名或关系。即使模型具有对话记忆,你也应该在每个复杂的数据库相关提示中包含模式详细信息以确保准确性。这种明确的方法可以防止AI生成的查询引用通用字段名(如user_id)而你的模式实际使用userId或customer_ref的常见问题。
性能考虑通常需要人工监督。虽然AI模型理解主键和连接等基本数据库概念,但它们可能不会自动建议性能优化,例如在频繁查询的字段上添加索引或考虑查询执行计划。请审查生成的查询的效率,特别是对于将频繁运行或针对大型数据集的操作。
数据一致性规则(data consistency
rules)代表另一个需要明确说明的关键领域。在实现删除操作时,请清楚定义您期望的级联行为(cascading
behavior)。例如,在删除Project记录时,您必须决定数据库是否应该通过级联删除(cascading
deletes)自动删除关联的Task记录,或者您的应用程序逻辑是否应该处理此清理。向AI清楚地传达这些业务规则:
当删除项目时,配置数据库级联删除所有相关任务。
或者:
当删除项目时,首先检查现有任务,如果存在任何任务则阻止删除。
当给出明确的方向时,AI可以有效地实现任一方法。对于级联删除,它可能生成带有ON DELETE CASCADE的外键约束。对于应用程序级别的处理,它可以生成在允许删除之前查询相关记录的代码。关键在于明确说明您的数据完整性要求,而不是假设AI会为您的特定领域推断出适当的行为。
现在您已经在AI的帮助下构建了前端和后端,下一个挑战是将它们集成到一个无缝的Web应用程序中。这涉及确保从前端正确调用API端点(API endpoints),数据正确流动,以及整个系统是连贯的。
这至关重要:前端期望以某种形式接收数据,因此后端发送的内容应该与该期望相匹配。如果您让AI独立地在每一端工作,可能会出现小的不匹配(也许后端返回{ success: true, data: [...] },但前端期望直接接收数组)。为了避免这种情况,您可以在编写两端代码时明确指示AI使用响应格式。或者,一旦两者都完成,测试端到端调用:例如,打开Web应用程序并查看列表是否加载。如果没有,请对照服务器日志检查浏览器控制台。
我经常使用AI来调整一端以匹配另一端:
如果后端返回的JSON键名与前端期望的略有不同,并且您注意到一个错误,您可以对AI(在任一端)说:
修改代码以在JSON中使用’tasks’(复数)而不是’taskList’(单数)。
如果前端将表单数据作为表单编码发送,但后端期望JSON,您可以要求AI进行转换,可能通过在前端使用JSON.stringify或在后端添加body-parser。
在集成阶段,能够掌握整个项目上下文的AI增强IDE(如Cline或Cursor)会特别有帮助。你可以在基于IDE的工具中并排打开前端和后端文件,然后提示:
确保前端从 /api/to-dos 的fetch与Express路由的预期请求/响应匹配。修复任何差异。
AI可能会协调内容(比如在前端缺少时添加 await response.json(),或调整JSON结构)。
在全栈应用中,考虑在前端实现加载状态和错误处理以应对失败的API调用,从而获得专业的结果。你可以使用以下提示:
添加加载指示器:当React组件正在获取任务时,显示”加载中…“文本,直到数据加载完成。
或者:
处理错误:如果API调用失败(非200响应),在UI上显示错误消息。
它会添加 isLoading 状态和条件渲染,或在
fetch 周围实现 try/catch
来捕获错误并显示消息。这种改进使你的应用感觉更加健壮。
如果你的应用需要实时更新(比如使用WebSockets或SSE),你可以这样提示:
使用Socket.io设置WebSocket。当服务器上创建新任务时,将其广播给所有连接的客户端。修改前端以监听新任务并将它们实时添加到列表中。
这很复杂,但AI可能会生成服务器端Socket.io设置(如添加
io.on('connection', ...)
并在创建新任务时发出事件),以及客户端代码来连接并监听该事件。你需要仔细集成这些,但令人惊讶的是,这些描述可以产生可工作的实时代码。如果一开始不能完美运行,通过迭代提示和测试可以实现目标。
为了说明,让我们想象你正在构建一个简单的联系人管理器Web应用:
你搭建一个React前端和一个Node/Express后端,就像本章前面所做的那样。
首先,对于前端,提示创建一个 ContactList 和一个
ContactForm 组件。然后提示添加API调用:
在ContactList中,在挂载时从/api/contacts获取联系人。
在ContactForm中,在提交时,使用表单数据向/api/contacts发送
POST请求,然后在成功时更新联系人列表。
对于后端,你可能想使用内存数组或首先集成数据库。然后提示创建Express路由
GET /api/contacts(返回列表)和
POST /api/contacts(将联系人添加到数据库或内存)。
尝试通过UI添加联系人。如果它出现在列表中,很好。如果没有,调试。也许
POST
路由没有正确返回新联系人,或者表单代码没有刷新列表。识别差距并提示AI修复:
添加联系人后,后端应在响应中返回新的联系人对象,前端应将其添加到列表中而无需完全重新加载。
这可能导致AI调整后端响应和前端状态逻辑来推送新联系人(可能使用React状态更新)。
类似地实现编辑和删除功能,每次都让AI处理常规部分,并将你的输入集中在功能应该做什么上。
手动完成所有这些对于初级开发人员来说可能需要一到两周的工作,但使用AI代码开发者,考虑到大量模板代码和连接是自动化的,可以在一两天内完成。
在处理整个技术栈时,与AI助手建立高效的协作节奏非常有用。以下是优化协作的一些策略:
识别哪些代码部分是常规的,哪些是独特的核心逻辑。让AI生成CRUD API或标准组件——但如果有特别棘手的逻辑片段,比如专有算法或更容易直接实现的特定业务规则,手动完成这部分,然后要求AI审查或测试它。可以把它看作是将重复性任务委托给AI,而你处理新颖的任务。
在开发过程中,跟踪任务(如要添加的功能和要修复的bug)。然后逐一向AI解释每个任务,让它提出解决方案。例如,假设你有一条备注写着”在用户注册时实现密码哈希”。尝试这样的提示:
在保存用户之前,在
POST /api/register路由中使用bcrypt添加密码哈希。
这种有针对性的系统化方法有助于确保你不会忘记任何事情。
在实现功能后,你可以提示:“重构此代码以提高可读性”或”优化此函数”。AI通常可以使代码更清晰或提出性能改进建议,就像助手在你的监督下进行第二遍润色。确保验证任何更改仍然能通过测试。
如果你对设计方法不确定,可以询问AI:
使用数组在内存中存储联系人可以吗,还是应该使用数据库?各有什么优缺点?
虽然你可能知道答案(使用数据库实现持久化),但这就像与同事交流想法。有时AI可能会提到你没有想到的考虑因素:
如果有多个服务器实例,内存存储不会在它们之间同步。
如果你在团队中工作,不是每个人都可能直接使用AI。在这种情况下,确保要求AI记录你所做的工作。同时,向团队传达你的方法也很重要:“我使用AI快速生成了这些控制器。我已经检查过了,但请留意任何不寻常的模式。”鼓励代码审查文化,让每个人像审查其他代码一样审查AI编写的代码,以发现任何问题。
采用AI的实际团队(如Snyk的团队)报告称,它可以提高生产力,但他们也强调要保持人工参与验证。在GitHub 2024年的一项调查中,97%的开发者报告在工作中以某种方式使用AI编码工具。
在借助AI构建web应用程序后,要进行彻底测试,以确保一切按预期工作,并捕获你或AI可能引入的问题。以下是在这种AI辅助环境中进行测试的方法:
单元测试(Unit tests)
对于后端逻辑,为关键函数(如计算函数或验证输入的函数)编写单元测试。如果函数是由 AI 编写的,为其编写测试可以揭示任何隐藏的 bug。你甚至可以让 AI 生成这些测试,如前所述。但要谨慎:AI 生成的测试有时过于简单或假设某种实现,因此你可能需要引导它测试边缘情况:
为密码强度函数编写测试,包括边缘情况,如空密码、超长密码、包含特殊字符的密码等。
使用 Supertest(用于 Node)或直接 HTTP 调用等工具测试 API 端点。检查每个端点是否返回预期结果。AI 可以帮助你构建这些测试的框架:
使用 Jest 和 Supertest 为 /api/to-dos 端点编写集成测试。
它可能会生成启动应用、调用端点并对响应进行断言的测试。
Web UI 测试可以使用 Jest(用于组件逻辑)和 Cypress 或 Playwright 等工具进行端到端 UI 测试。你当然可以让 AI 生成 Cypress 测试场景:
编写一个 Cypress 测试,加载应用,通过表单添加一个新的待办事项,并检查它是否出现在列表中。
你会得到一个测试脚本,可以运行它。这非常强大——通过利用 AI 来编写用户交互脚本,你可以快速获得端到端的测试覆盖。
无论你运行什么自动化测试,始终要进行一些手动探索性测试。亲自在 Web 应用中点击(或者如果你在团队中工作,让 QA 来做)。AI 可能无法预料到每个真实世界的场景:例如,也许使用浏览器的后退按钮会破坏某些状态,或者特定的操作序列会导致故障。当你发现 bug 时,修复它们或请 AI 帮助修复。手动测试对于 UI/UX 判断也很重要——应用使用起来感觉好吗?有没有任何不流畅的流程?AI 不知道如何判断这些主观的 UX 问题,因此人工反馈是关键。
如果你与他人合作,让他们审查 AI 生成的代码。新的视角可以发现你可能忽略的问题——他们可能会发现安全疏漏,或者只是建议一种更符合惯例的编写方式。使用 AI 的团队通常会维持正常的代码审查流程,只是更加关注审查 AI 可能无意中引入的细微 bug 或安全问题。
第 8 章将深入探讨安全性,但即使在开发阶段,也值得扫描你的代码以查找已知的漏洞模式。你可以运行自动化工具,如 linter 和静态应用安全测试(SAST)工具,或者你可以提示 AI:
审查 Express 应用代码,列出任何潜在的安全漏洞或最佳实践违规。
AI 可能会标记一些令人惊讶的事情,比如”你没有在这里清理用户输入”或”你应该正确设置 CORS”。将其用作加固应用的检查清单。
使用 AI 的一个有趣效果是,你可能会编写原本不会编写的测试,因为 AI 使创建它们变得如此容易。这实际上可以最终导致更加健壮的代码。如果你在生成功能后立即采用生成测试的做法(本质上是 AI 辅助的测试驱动开发,或至少是事后测试),你可以确保快速开发不会影响质量。可以这样想:既然 AI 为你节省了编写代码的时间,那就把节省下来的一些时间投入到编写和运行测试中。
如果用户不小心,AI 可能会建议不安全的代码。例如,如果没有明确提示避免这种情况,早期的 AI 版本可能会生成容易受到注入攻击的 SQL 查询。通过测试和审查,你可以捕获这些问题。一项研究发现,使用 AI 辅助的开发人员往往对其代码的安全性过于自信,即使它比手动编写的代码更差。
永远不要仅仅因为是 AI 编写的代码就跳过验证。假设它可能有 bug,就像任何人工编写的代码一样。
让我们重点介绍几个例子(从各种报告中汇总的综合案例),其中AI辅助在交付真实Web应用程序方面发挥了重要作用。
一位独立开发者想要创建一个小型电商Web应用来销售定制T恤,但时间有限。他通过IDE扩展使用GPT来构建整个技术栈。他提示AI生成一个带有产品列表、购物车和结账页面的React前端,以及一个带有产品和订单端点的Node.js后端。他使用Stripe进行支付,通过让AI帮助处理Stripe的API来完成集成。在晚上工作两周后,他拥有了一个可运行的网站。
这位开发者表示,AI可能完成了70%的编码工作,特别是重复性的UI部分和表单处理,而他则专注于正确配置Stripe并微调UI以符合品牌形象。最终,客户可以浏览产品、将它们添加到购物车并进行购买——所有这些都在一个主要通过vibe编码构建的系统中实现。这也突出表明,只要模型能够参考文档或你提供文档,外部服务集成(如Stripe)在AI指导下是可行的。
一位具有一定编码技能的产品经理使用AI配对程序员为她的团队创建了一个内部分析仪表板。通常情况下,她必须等待工程资源,但使用Replit的Ghostwriter或GitHub Copilot等工具在Web项目中,她成功地自己构建了一个基本的Web应用。AI帮助设置了一个简单的Flask后端来查询他们的数据库(使用安全的只读凭证)和一个Vue.js前端来显示图表(使用图表库)。她描述了每个图表应该显示什么(“随时间变化的总注册数”、“按地区划分的活跃用户”),AI编写了SQL查询和图表代码。
整个过程花费了几周的修改和测试时间,但最终她交付了一个可运行的仪表板。代码质量并未达到企业级标准,但由于是内部使用,这已经足够了。更重要的是,她在短时间内为团队提供了一个工具。这个例子说明了AI工具如何使非专业程序员能够生产有用的Web应用,解决了可能会一直搁置在待办事项列表中的任务。这是我将在第10章讨论的”程序员解绑”的一个例子,该章节完全关于个人现在如何更容易地创建个人或团队特定的软件。
一家小型创业公司(只有两位联合创始人:一位负责商务,一位负责技术)需要一个MVP Web应用程序来向投资者展示。技术联合创始人广泛使用vibe编码在创纪录的时间内构建了一个MVP。使用AI助手,他使用Next.js构建了一个现代Web应用的脚手架,用于SSR React前端和一个简单的Node API。他利用AI实现了诸如社交登录(AI编写了OAuth流程)、图片上传(AI与云存储API集成)等功能,以及产品本身内部基于AI的功能。他们甚至使用AI帮助从API集成了一个NLP模型。在几个月内,一位开发者完成了通常需要一个小团队四到六个月才能完成的工作。结果是一个有些粗糙但可运行的产品,他们可以进行演示,甚至可以让测试用户加入平台。
当联合创始人后来雇用更多开发人员来完善产品时,新开发人员发现AI编写的代码大部分是可理解的,尽管他们确实为了可扩展性而重构了大部分代码。这强调了AI可以让你快速到达第一阶段,但随着进入下一阶段,你可能需要在质量上进行投资。
这些故事虽然是轶事性质的,但与行业中的新兴模式相符。特别是在Web开发中,通常涉及将许多组件连接在一起,生产力提升是非常明显的。微软和其他机构报告的研究发现,使用AI的开发者比没有使用AI的开发者能够更快地完成任务。
然而,也出现了一些警示性案例。例如,开发者可能会部署一个带有安全漏洞的AI生成的Web应用,因为他们并不完全理解代码。这种风险强化了为什么测试和审查至关重要。
总之,使用AI辅助构建Web应用程序正在成为一种主流方法。它并不会消除对熟练开发人员的需求;相反,它增强了他们的能力。开发人员仍然需要规划架构、确保正确性并处理代码中复杂或新颖的方面,而AI则处理将所有内容粘合在一起的重复样板代码(boilerplate code)。我们所演示的端到端工作流程——从脚手架搭建到前端到后端再到测试——证明了Web开发的几乎每一步都可以通过AI加速,只要你在整个过程中运用人类的判断和专业知识。
在本章中,你已经看到了氛围编码(vibe coding)如何扩展到全栈Web应用程序开发。通过将AI视为一个随时可用的结对编程伙伴,你可以并行处理前端和后端任务,从自然语言描述生成组件和API,并迭代地将原型应用优化至生产质量。成功的关键包括清晰地传达你的意图(以便AI知道你在每一步想要什么)、仔细验证(以捕获AI输出中的问题),以及不仅利用AI生成代码,还可以用于头脑风暴模式设计和编写测试等事情。
本章还探讨了开发人员如何有效地成为全栈工程师,通过AI增强能力,在他们不太熟悉的领域建议代码来弥合知识差距。这大大减少了常见功能的开发时间,并在某种程度上实现了开发的民主化,使人们能够在没有大型团队的情况下创建定制化的Web解决方案(我将在第10章重新讨论这个主题)。
AI并不能取代对需求的理解或确保质量;它加速了执行过程。
现在你的Web应用程序已经启动并运行,下一个关注点是确保它是安全、可靠和可维护的。第8章深入探讨了AI生成代码库中的安全性和可靠性挑战,识别可能出现的常见漏洞,如何审计和修复它们,以及最佳实践(如我们在这里已经开始应用的测试和审查)以确保使用AI快速开发不会破坏系统。本质上,我们将从构建转向加固——确保你的氛围编码软件能够经受住现实世界的条件和威胁。
本章直面氛围编码和AI辅助工程的一个关键方面——确保你使用AI辅助生成的代码是安全、可靠和可维护的。如果生成的软件充满漏洞或容易崩溃,那么速度和生产力就毫无意义。
首先,我将审视AI生成代码中出现的常见安全陷阱,从注入漏洞(injection vulnerabilities)到密钥泄露(secrets leakage)。你将学习审计和审查AI编写代码中此类问题的技术,有效地充当AI结对编程伙伴的安全防护网。
接下来,我将讨论围绕AI生成代码构建有效的测试和QA框架,以便尽早捕获错误和可靠性问题。性能考虑也将被涵盖。AI可能会编写正确的代码,但它并不总是最高效的代码,因此我将概述如何识别和优化性能瓶颈。我还将探讨确保可维护性的策略,例如强制执行一致的样式或重构AI代码,因为AI建议有时可能不一致或过于冗长。
我将向你展示如何使你的代码审查实践适应AI辅助工作流程,强调在审查部分或全部由机器生成的代码时,人类审查者应该关注什么。最后,我将总结自信地部署AI辅助项目的最佳实践,从持续集成管道(continuous integration pipelines)到生产环境监控。在本章结束时,你将拥有一套工具包方法来保持AI加速开发的安全和健壮性。
AI编码助手虽然强大,但如果没有得到适当的引导,可能会无意中引入安全问题。它们从大量公共代码中学习——其中既包括好的实践也包括坏的实践——如果提示或上下文没有将它们引导开,可能会重现不安全的模式。了解这些常见陷阱至关重要,以便你能够发现并修复它们。这可以包括使用手动和自动化手段来检测潜在的安全问题(见图8-1)。
在 AI 生成的代码中观察到的一些典型安全问题包括:
有时 AI 会在代码中输出 API
密钥、密码或令牌,特别是当类似的示例出现在其训练数据中时。例如,如果你让它集成
AWS,它可能会将一个虚拟的 AWS
密钥直接放在代码中。如果保留这些内容会很危险——如果代码被分享,可能会泄露敏感信息。始终确保通过环境变量或配置文件正确管理秘密。如果
AI 建议类似 api_key = "ABC123SECRET"
的内容,请将其视为一个标记——真正的密钥不应该出现在源代码中。
如果让你的 AI 模型生成 SQL 查询或 ORM 使用,请检查它是否没有通过直接拼接用户输入来构造查询。例如,一个不安全的模式是:
sql = "SELECT * FROM users WHERE name = '" + username + "'";
这容易受到注入攻击。如果你没有特别告诉 AI
使用参数化查询,它可能会产生这样的代码。始终使用预处理语句或参数绑定。许多
AI 助手如果记得最佳实践(如在 SQL 中为用户输入使用 ?
或占位符),会这样做,但不能保证。你需要验证,如果需要,要求 AI
修复:
修改此查询以使用参数来防止 SQL 注入。
在生成 Web 代码时,AI
工具并不总是自动转义输出中的用户输入。例如,你的 AI 可能生成一个直接将
{{comment.text}} 插入 HTML
而不转义的模板片段,这可能允许在评论中放置的恶意脚本运行。如果使用框架,AI
通常默认会转义,但如果它们处理原始 HTML
构造,要小心。实现输出编码或清理例程。你可以提示 AI:
为用户输入添加清理以防止 XSS。
许多现代框架都有内置机制,因此确保 AI
使用它们,比如在文档对象模型(DOM)操作中使用 innerText
而不是 innerHTML。
AI 可以编写身份验证流程,但可能会出现细微的错误:例如,在没有足够强秘密的情况下生成 JSON Web Token (JWT),或者没有正确检查密码哈希。
授权也是如此:AI 可能不会自动强制执行某个操作(如删除资源)仅限于拥有该资源的用户。这些逻辑问题很难自动捕获——它们需要思考安全模型。在编写此类代码时,请明确指定:
确保只有资源的所有者可以删除它。添加用户 ID 检查。
然后测试这些条件。AI 很容易遗漏检查,因为除非被告知,否则它不会真正”理解”上下文。
除非提示,否则 AI 可能会选择便利性而不是安全性。示例包括:
使用 HTTP 而不是 HTTPS 进行 API 调用(如果未指定 TLS)
不验证 SSL 证书(互联网上的一些代码示例在请求中使用
verify=false,AI 可能会复制)
无限制地为所有源和方法广泛启用 CORS(可能会使应用程序对任何跨源请求开放)
选择过时的加密技术(如用于哈希的 MD5 或 SHA1,它们很弱,而不是用于密码的 SHA-256/Bcrypt/Argon2)
这些问题通常很微妙,这就是为什么审核配置文件和初始化代码很好的原因之一。如果
AI 设置了类似 app.UseCors(allowAll)
的内容或选择了旧的加密算法,你应该发现并纠正它。
AI 生成的错误处理可能会打印或返回堆栈跟踪。例如,Node.js API
可能会捕获错误并执行
res.send(err.toString()),这可能会泄露内部详细信息。确保向用户发送的错误消息经过清理,并正确处理日志。根据需要进行调整,以避免向攻击者提供完整错误消息或文件路径等线索。
如果AI向你的项目中添加了依赖项(如库),请确保它们是最新的且来自可靠来源。AI可能会选择一个在其训练数据中很流行的库,但该库可能已不再维护或存在已知漏洞。例如,如果它建议使用某个包的旧版本,你应该将其升级到最新的稳定版本。在生成代码后运行
npm audit 或类似命令也是明智的做法。或者询问AI:
这个库是否仍在维护且安全?
它可能不完全知道,但可以告诉你是否存在已知的弃用情况。
2023年一项针对GitHub Copilot在真实项目中的大规模分析显示,根据不同编程语言,高达25%–33%的生成代码包含潜在的安全弱点,包括高严重性的CWE(Common Weakness Enumeration,通用弱点枚举),如命令注入、代码注入和跨站脚本攻击。这些发现强调了Copilot反映了其训练数据中存在的不安全模式,而不是故意产生有缺陷的代码。一致的建议是什么?开发人员必须保持警惕:手动审查AI生成的代码,使用具有安全意识的工具,并保持严格的代码规范。特别是在”氛围编程”(vibe coding)期间,AI生成内容的速度和范围要求更高的警惕性。更短时间内生成更多代码意味着需要审计的攻击面更大。
让我们看一个简短的例子。
想象一下,你要求AI在Express应用中创建一个登录路由。它可能会生成如下代码:
// 不安全的示例
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await Users.findOne({ username: username });
if (!user) return res.status(401).send("No such user");
if (user.password === password) { // 明文密码比较
res.send("Login successful!");
} else {
res.status(401).send("Incorrect password");
}
});
这里存在哪些问题?
它直接比较密码,这意味着密码以明文形式存储在数据库中——这是一个大忌。
它发送非常通用的响应,这可能适合安全性要求,但也可能无意中暴露敏感信息。
将身份验证错误消息视为一个关键示例。一个适当安全的系统应该在登录失败时返回一个通用消息,如”凭据无效”(Invalid credentials),无论是用户名还是密码不正确。然而,AI生成的代码可能会产生更具体的错误,如”用户名未找到”(Username not found)或”该用户密码不正确”(Incorrect password for this user)。
这些具体的消息会造成安全漏洞,因为它们向潜在攻击者确认了他们掌握的哪部分信息是正确的。如果攻击者收到”密码不正确”作为错误消息,他们现在知道他们已经发现了系统中的一个有效用户名。这使他们能够通过重复尝试建立合法用户名列表,然后集中精力破解这些已确认账户的密码。这种技术被称为用户枚举(user enumeration),它将猜测游戏转变为更有针对性的攻击。AI倾向于提供有用、具体的错误消息,除非你明确指示它为安全敏感操作保持适当的模糊响应,否则会无意中帮助恶意行为者。
虽然代码中没有显示,但这里也没有包含速率限制或其他保护措施。
一个安全的实现应该包含几个关键保障措施:
首先也是最重要的,密码绝不应以明文形式存储。相反,系统应该存储经过加密哈希的密码,并使用安全的比较函数来验证登录尝试。
这个比较过程必须采用恒定时间算法来防止时序攻击(timing attacks),攻击者通过分析响应时间来推断密码正确性的信息。许多注重安全的库专门提供这些恒定时间比较函数来解决此漏洞。
此外,认证系统应该实施速率限制或节流机制(throttling mechanisms),以防止暴力破解攻击。失败的登录尝试应该被记录用于安全监控,让管理员能够检测和响应可疑模式。这些措施协同工作,创建一个纵深防御方法,即使某一层安全被攻破,也能保护用户凭证。
你可以要求 AI 帮助修复这个问题:
改进登录路由,使用 bcrypt 对密码进行哈希和比较,并确保数据库中的密码已被哈希。
它可能会输出:
const bcrypt = require('bcrypt');
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await Users.findOne({ username });
if (!user) {
return res.status(401).send("Invalid credentials");
}
const match = await bcrypt.compare(password, user.passwordHash);
if (!match) {
return res.status(401).send("Invalid credentials");
}
res.send("Login successful!");
});
这样更好:它使用 bcrypt 与哈希密码进行比较(假设变量
user.passwordHash
存储了该密码)。在创建用户时,你还需要确保使用 bcrypt.hash
对他们的密码进行哈希。
在稍加指导下,AI 可以做正确的事情,但它最初的简单输出很可能是不安全的。这强调了一个模式:审查和优化。
另一个常见的漏洞类别是包管理。AI 有时会虚构一个库或记错名称,这个问题被称为包幻觉(package hallucination)。这样的包可能不存在,但理论上攻击者可以用常见的幻觉包名发布包含恶意代码的软件包。如果你在没有确认包既存在又正确的情况下安装这样的包,可能会带来严重的风险。如果你不确定某个特定的包,可以尝试快速的网络搜索或直接检查 npm/PyPI。
此外,AI 可能会无意中生成与训练数据中的授权代码片段完全相同的代码。这更多是知识产权问题而非安全问题,但值得仔细关注。例如,GitHub Copilot 包含一个重复检测功能,可以标记生成的代码何时与公共仓库非常匹配,帮助开发人员避免潜在的许可冲突。类似的工具正在出现,以解决 AI 生成代码来源的这一特定挑战。第 9 章将更详细地探讨许可和知识产权考虑事项,为应对这些复杂问题提供全面指导。
总之,主要信息保持不变——是的,我意识到我在整本书中强调这一点,以至于你可能在睡梦中都能背诵它——那就是 AI 输出需要与你审查初级开发人员代码相同的仔细审查。重复是有意的,因为这个原则几乎支撑着安全有效的 AI 辅助开发的各个方面。无论你是在原型设计、构建后端还是实现安全功能,这种思维模型都提供了正确的信任和验证平衡,使 AI 成为强大的盟友而非风险捷径。它可以快速编写大量代码,但你需要向其灌输安全最佳实践并仔细检查漏洞。小说家弗兰克·赫伯特在《沙丘神帝》(Putnam, 1981)中经常被引用的一句话这样说:“它们增加了我们不经思考就能做的事情的数量。我们不经思考做的事情——那才是真正的危险。”
使用 AI 可能会让你对常规代码的思考变少,你应该有意识地思考如何应用安全审查的思维模式。这对于捕捉那些”我们不经思考就能做的事情”至关重要。
鉴于上述漏洞类型,我们如何有效地审计和保护 AI 生成的代码?本节将介绍你可以使用的几种技术和工具。
静态分析工具(SAST)可以扫描您的代码以查找已知的漏洞模式,例如:
ESLint + 安全插件可以检测JavaScript和Node代码中的不安全函数或未经清理的输入。
Bandit Python工具可以标记生产环境中assert的使用、弱加密、硬编码的密钥等问题。
GitHub CodeQL允许您在代码库中运行查询,以查找SQL注入、XSS和其他常见模式。
Semgrep为许多语言提供规则,包括社区维护的JavaScript、Python、Java、Go等规则,可以立即发现主要问题。
您可以将这些工具集成到CI/CD或开发流水线中。在AI生成的代码上运行它们——虽然无法捕获所有问题,但可能会标记出明显的错误(例如,明文密码检查、未清理的SQL、不安全的加密)。这是一个可靠的安全网。
两种不同的方法可以利用AI对生成的代码进行安全审查,每种方法都有独特的优势。第一种方法涉及使用生成代码的同一AI模型,要求它切换视角并审计自己的输出。生成代码后,您可以使用如下提示词:
审查此代码的安全漏洞并解释您发现的任何问题。
这种方法通常会产生令人惊讶的有效结果,因为模型可以识别常见的安全问题,例如明文密码存储、缺少输入验证或潜在的SQL注入漏洞。
第二种方法使用不同的AI模型作为独立审查者。例如,如果您使用ChatGPT生成代码,您可能会将该代码粘贴到Claude或Gemini中进行安全分析。这种跨模型审查可以提供不同的视角,并捕获原始模型可能忽略的问题,就像不同的安全工具或人工审查者带来不同的专业知识和关注领域一样。不同的模型可能使用不同的重点或数据集进行训练,可能会捕获不同类别的漏洞。
这两种技术都可以作为有价值的额外安全审查层,补充但永远不会取代适当的安全测试和人类专业知识。虽然AI审查者偶尔会标记误报或遗漏细微的漏洞,但它们擅长快速捕获常见的安全反模式(antipattern)。将此过程视为专门针对安全考虑的自动化结对编程。关键在于将这些AI生成的安全审查视为安全评估过程的另一个输入,而不是作为最终的安全许可。
如果您在团队中工作,请使用检查清单来审查代码的安全性。AI通常会生成在预期情况下”有效”的代码,但未经加固以处理恶意情况。对于AI生成的代码,请务必考虑:
认证流程(Authentication flow):是否可靠?
数据进入系统的任何位置:我们是否在验证输入?
数据离开系统的任何位置:我们是否在清理输出?我们是否在保护敏感数据?
外部API的使用:我们是否在处理失败?我们是否暴露了密钥?
数据库访问:我们是否安全地使用ORM?我们是否使用参数化查询?
低级代码中的内存管理:如果AI正在编写C/C++或Rust,是否存在溢出?是否存在任何误用?
使用动态方法。对于模糊测试(fuzz testing),向你的函数或端点输入随机或特制的输入,看看它们是否会崩溃或出现异常行为。AI可以帮助生成模糊测试用例,或者你可以使用现有的模糊测试工具,例如Google的OSS Fuzz。
针对AI生成的Web应用运行渗透测试(penetration testing)工具,如OWASP的ZAP,可以自动扫描XSS和SQL注入等漏洞。例如,ZAP可能会尝试注入脚本并使其被反射,从而检测到某个输入未被清理。
如果你正在构建API,可以使用Postman等工具或自定义脚本尝试发送格式错误的数据,观察系统的行为:它是抛出500错误还是优雅地处理错误?
对于关键代码片段,编写断言安全属性的测试。例如,你可以测试登录速率限制器在X次错误尝试后是否触发,或者某些输入(如"<script>alert(1)</script>")在响应中是否被转义。要测试未授权用户无法访问受保护资源,可以模拟授权和未授权调用,并确保应用程序行为正确。
你可以要求AI帮助生成这些测试:
编写测试以确保未授权用户在访问/deleteUser端点时收到403响应。
然后运行测试。
AI模型存在一个直接影响安全性的基本限制:它们的知识在特定时间点冻结。当模型完成训练时,它无法了解之后发现的漏洞、随后发布的安全补丁或新出现的最佳实践。这种知识截止(knowledge cutoff)在AI所知与当前安全标准之间造成了关键差距。
考虑一个在2023年训练的模型在2025年生成代码。在这些间隔年份中,已经发现、修补和记录了大量安全漏洞。新的攻击向量已经出现,框架已经添加了安全功能,最佳实践也在不断演进。然而,除非你在提示中明确提供更新信息,否则AI仍然不知道这些发展。
这种限制在快速演进的安全标准和漏洞数据库中变得尤为严重。例如,OWASP Top 10会定期更新以反映不断变化的威胁形势。如果你提示AI”编写一个安全的文件上传函数”,它可能会基于其训练数据实现合理的保护措施——可能包括文件类型验证、大小限制和Web根目录外的存储。然而,它可能会遗漏最近发现的攻击向量或未能实现新推荐的缓解措施。
解决方案是主动用当前安全信息补充AI的知识。在请求安全敏感代码时,在提示中包含当前最佳实践的引用。例如,与其简单地要求安全代码,不如这样提示:
编写一个文件上传函数,解决2025 OWASP Top 10中的安全问题,特别关注注入攻击和服务器端请求伪造。
这种方法使AI的响应基于当前安全标准,而不是可能过时的训练数据。
同样,特定框架的安全功能通常在AI的训练截止日期之后出现。例如,Express.js应用程序从Helmet中间件设置安全头中受益匪浅。在Helmet成为标准实践之前训练的AI可能会生成不包含这一关键安全层的Express应用程序。通过在提示中明确提及当前的安全工具和实践,你可以帮助AI生成符合当代安全标准而非历史标准的代码。
确保代码(AI 和人工编写的)具有良好的日志记录,特别是在关键操作或潜在故障点周围。这有助于在生产环境中调试问题。如果 AI 编写的部分日志记录很少,请考虑添加更多。例如,如果有一个 AI 生成的 catch 块只是吞掉了错误,将其修改为记录错误(可能还包括一些上下文信息)以便查看。此外,清理日志以确保它们不包含敏感信息。
一些 AI 编码工具旨在将代码生成与内置安全扫描相结合。Snyk 是一个典型例子:它使用混合方法,结合 LLM 生成的建议和基于规则的污点分析(taint analysis)。根据 Snyk 的说法,当您请求代码时(即使来自 OpenAI、Anthropic 或 Hugging Face 等 LLM 库),Snyk Code 会跟踪潜在的不安全数据流,并在不受信任的输入到达敏感接收点之前标记它们。实际上,这意味着如果 AI 建议一个数据库查询,Snyk 会确保它是参数化的,从而防止 SQL 注入——即使您自己忘记这样做。这类工具特别有用,因为它致力于避免通过 AI 生成的建议引入不安全的代码。
如果您使用的是 IDE,通常会看到警告或波浪线来突出显示可疑代码。具有 IntelliSense 功能的现代 IDE 有时可以捕获,例如,看起来可疑的 SQL 字符串拼接。不要仅仅因为是 AI 编写的就忽略这些警告和标记——要解决这个问题。AI 在生成代码时并没有这些实时警告的帮助。
在使用 AI 快速生成大量代码后,当需要审计时切换思路并放慢速度。当您可以快速生成功能时,很容易想要追逐下一个功能,但要安排时间进行彻底审查。可以将其视为”AI 加速开发,人工加速安全”。Snyk 的最佳实践建议直接在 IDE 中扫描 AI 代码,并警告不要让 AI 的速度超过您的安全检查。换句话说,将安全扫描集成到您的开发循环中,这样您就可以在代码编写后立即捕获漏洞。
总之,当您审计 AI 生成的代码时,您将使用许多在传统开发中使用的相同工具——静态分析、动态测试、代码审查——但您可能会更频繁地应用它们,因为代码生成得更快。将每个 AI 输出都视为需要检查的内容。
虽然安全性构成可靠性的一个支柱,但更广泛的概念涵盖了软件系统的基本可靠性。可靠性,在软件架构术语中,解决了关于系统故障及其后果的关键问题。您的系统需要故障安全吗?它在可能影响人类生命或安全的方面是否至关重要?如果系统故障,是否会给您的组织造成重大财务损失?这些考虑因素决定了开发和测试实践所需的严格程度。
在使用 AI 辅助构建软件时,这些可靠性要求保持不变。使用 AI 辅助生成的银行应用程序,在交易准确性和数据完整性方面的要求与完全由人类编写的应用程序相同。医疗保健系统必须满足相同的患者安全标准,无论其代码如何产生。AI 参与代码生成并不会降低这些基本的可靠性要求。
这一现实强调了为什么全面测试在 AI 辅助开发中变得更加关键。强大的测试框架可以确保您的代码正确执行预期功能,并在项目演进过程中保持这种正确性。虽然测试 AI 生成的代码遵循与测试人工编写代码相同的基本原则,但 AI 开发过程中会出现某些值得特别关注的细微差别和机会。
以下部分探讨如何利用 AI 不仅生成代码,还创建强大的测试套件来验证可靠性、维护系统稳定性,并提供信心确保您的软件在关键时刻能够正确运行。
首先,尽早并经常采用自动化测试。当开发缓慢时,很容易跳过编写测试,因为您想要推进功能。讽刺的是,当开发速度很快时(使用 AI),同样很容易跳过测试,因为新功能不断涌现。但是当代码快速生成时,这正是您最需要测试来捕获回归或集成问题的时候。因此,在 AI 帮助实现功能后,养成立即为其编写测试的习惯(甚至使用 AI 编写这些测试)。这既验证了功能,也在您稍后更改时保护它。
2022 年的一项研究发现,使用 AI 助手的开发人员对他们编写的代码的安全性更有信心,即使客观上它不如那些没有 AI 辅助的开发人员编写的代码安全。您需要用实际测试来抵消这种过度自信。
正如我在第 4 章中提到的,您可以使用 AI 不仅生成代码,还生成一套测试。这样,AI 可以帮助检查自己。就像让它同时进行实现和初步验证。例如,在编写新模块后,您可以询问:
为此模块编写单元测试,覆盖边缘情况。
如果它们通过了,很好。如果失败了,要么有错误,要么测试期望的是其他内容。根据情况调查并修复代码或测试。
要注意 AI 可能会错误地假设某些输出或行为;像对待代码一样,将其测试视为建议,而不是绝对真理。您可能需要调整测试的期望以匹配预期行为——但即使是这个过程也是有价值的,因为它迫使您清楚地定义预期行为。
将您的测试套件整合到 CI
管道中,在每次提交时运行。这样,每当添加或更改 AI
生成的代码时,所有测试都会自动运行。如果出现问题,您将及早发现。有时 AI
可能会引入细微的破坏性变更(例如稍微更改函数签名或输出格式),强大的测试套件会检测到这一点。在
CI 中也包括安全扫描(如 npm audit
或静态分析),以便标记任何新引入的风险模式。可以尝试的测试类型包括:
基于属性的测试(使用 Python 的 Hypothesis 或 JavaScript 的 fast-check 等工具)是另一种有价值的技术。与编写具有特定输入和预期输出的单个测试用例不同,您定义代码应始终满足的高级属性。然后框架生成各种输入来检查这些属性是否成立。
以排序为例。与其断言
sort([3, 1, 2]) === [1, 2, 3],您可以定义属性:
输出应该是有序的
它应该包含与输入相同的元素
然后工具生成数十或数百个输入数组来测试这些条件——并找到您可能手动想不到的边缘情况。
这对于 AI 生成的代码特别有用。如果您的 AI 编写了一个规范化电子邮件地址的函数(例如通过将域名小写),属性测试可能会检查输出是否是幂等的——这意味着运行函数两次与运行一次的结果相同。如果边缘情况违反了该不变量,测试框架将生成一个反例来帮助您诊断错误。
AI 可能编写未优化的代码。在负载下测试您的系统是个好主意。这是性能方面的可靠性。使用 JMeter、Locust 或 k6 等工具模拟大量请求或大量数据,看看系统是否能够支撑。如果不能,识别瓶颈。
例如,AI可能会写一个简单的O(n^2)算法,在100个项目上运行良好,但在10,000个项目时会崩溃。如果没有性能测试,你可能直到生产环境才会注意到这一点。因此,如果适用,请纳入一些性能场景。对关键操作使用不断增加的输入规模进行计时,或使用性能分析工具查看CPU时间或内存在繁重任务中的消耗情况。
故意制造错误以确保系统能够优雅地响应,例如:
对于API,关闭数据库并查看API是返回友好的错误还是崩溃。如果崩溃,添加代码(或要求AI添加代码)来处理数据库连接错误。
对于前端,模拟后端返回500错误并确保UI显示错误消息,而不是空白页面或无限旋转图标。
AI在编写代码时可能不会自行考虑这些故障模式,因此你必须测试它们然后进行优化。测试这些场景将通过促使你添加适当的回退逻辑、重试机制或用户反馈来提高可靠性。
纳入日志记录,并可能在测试中使用日志进行验证。例如,如果某个操作应该触发审计日志条目,请对此进行测试。AI可以生成日志行;验证它们是否按预期输出。
此外,考虑设置监控(如模拟服务在生产环境中将如何被监控的内存模拟)。例如,你可以跟踪测试运行期间是否记录了任何未捕获的异常。如果有,将其视为测试失败;这意味着存在某些未正确处理的情况。
可维护性测试,如确保代码风格和标准,是很重要的。使用代码检查工具(linters)和格式化工具(formatters)来保持代码一致性,因为AI可能会从不同的提示中产生略有不同的风格。格式化工具如Prettier或Black(用于Python)可以统一风格。为了获得更多的逻辑一致性并捕获可能需要重构的过于复杂的AI生成代码,考虑添加强制执行诸如函数复杂度限制等规则的代码检查规则。(有关更多信息,请参见[“确保AI加速代码库的可维护性”]。)
一旦你的测试就位,你就可以更自信地重构AI代码。也许AI产生了一个可行但笨拙的解决方案;你可以改进它并依靠测试来确保你没有破坏其行为。你甚至可以要求AI重构其自己的代码:
在保持通过当前测试的同时,为了清晰起见重构这个函数。
如果你的测试很好,你可以检查重构是否没有破坏任何东西。
理解AI系统中的非确定性(nondeterminism)需要区分两种根本不同的场景。当AI在生产系统的运行时运行时,例如聊天机器人响应客户查询或推荐引擎个性化内容时,即使输入相同,输出也可能不同。这种可变性源于模型温度设置、随机种子或不断演化的模型状态等因素。测试此类系统需要专门的方法来考虑可接受的变化范围,而不是期望精确匹配。
然而,AI辅助代码生成呈现了一个完全不同的范式。一旦AI生成代码并且该代码被提交到你的仓库,它就会像任何人工编写的代码一样具有确定性。计算税率的函数将在每次相同输入时产生相同的输出,无论最初是人类还是AI编写的。这种确定性对系统可靠性至关重要,并使传统的测试方法完全适用于AI生成的代码。
当集成多个AI生成的组件时,会出现更微妙的挑战。考虑一个来自电商系统的具体例子。你可能会提示AI生成一个订单处理模块,指示它处理国际订单。另外,你要求AI为同一系统创建一个运费计算服务。订单处理模块遵循美国惯例,将日期格式化为”12/25/2024”表示12月25日。与此同时,运费服务可能受到其生成过程中欧洲示例的影响,期望日期格式为”25/12/2024”。这两个组件在独立运行时都完美工作,通过了各自的单元测试。
不匹配只在集成测试期间才显露出来,当订单处理器将日期传递给运费计算器时。运费服务将”12/01/2024”解释为1月12日而不是12月1日,可能会基于完全错误的月份来计算运输时间。这种假设不匹配在AI生成的组件中特别常见,因为AI在独立生成每个部分时可能会借鉴不同的示例或约定。全面的集成测试能够检验组件之间的实际数据流,这对于在生产故障之前捕获这些微妙的不兼容性至关重要。
AI辅助项目的QA(质量保证)过程可能需要更多创造性,因为AI可能引入不寻常的边缘情况(edge cases)。例如,AI可能输出一个你没有明确考虑的功能—如果是这样,也要测试它。如果它添加了隐藏行为,要么删除它,要么正确地测试它。
最后,如果可能的话,在类似于生产环境的环境中测试你的应用程序,使用真实的数据负载。有时性能问题只会在更大的数据量或更高的并发情况下出现。使用这些测试结果来精确定位低效率之处。
虽然AI经常编写正确的代码,但它可能不总是编写最优的代码。LLMs(大语言模型)本身不会进行性能分析;它们通常复现训练数据中常见的内容。因此,要对潜在的性能问题保持警惕,特别是在关键路径或大规模使用中。
你甚至可以与AI聊天以获得关于性能优化的提示:
这段代码的复杂度是多少?可以改进吗?
这个函数很慢—有什么加快速度的想法吗?
它可能不总是正确的,但有时可以给出有用的建议,或者至少确认你的想法。
话虽如此,不要过度优化,也不要过早优化或在不需要的地方优化。有时AI解决方案是完全可以的,如果数据量很小或操作不频繁的话。使用你的性能分析(profiling)数据来关注真正的瓶颈,优化真正需要优化的部分。氛围编码(vibe coding)的优势在于,你没有花费大量时间从头手工编写代码,所以你可以让一些非关键部分保持简单且不超级优化,只要它们不影响用户体验或成本。这种方法与敏捷实践一致:先让它工作,然后让它快速(如果需要的话)。
以下是一些在确保AI增强项目高效运行时需要覆盖的领域:
当AI生成算法时,花点时间考虑它的复杂度。有时它会在存在更高效算法的情况下使用暴力解决方案。例如,它可能会对列表进行双重排序,因为它没有想起单步方法,导致O(n log n × 2)而O(n log n)就可以(大写字母O代表内存使用)。或者它可能使用嵌套循环,使操作变成O(n2),而存在已知的O(n)方法。如果你发现这样的情况,要求改进:
我们能优化这个以避免嵌套循环吗?也许使用集合进行查找。
如果你提示方法,AI通常会给出更好的解决方案。如果没有,你可能需要手动实现那部分。
要识别慢速函数,运行性能分析器(profiler)或使用代表性或最坏情况数据测量关键代码路径的执行时间。如果某些东西太慢,你可以尝试手动优化或在AI协助下优化:
优化这个函数,它目前是一个瓶颈;尝试降低其复杂度。
AI 可能会重构代码以提升性能。使用测试来确保代码仍然正常工作。
对于关键算法,编写一个小型基准测试工具。如果 AI 给你一段代码来计算某些内容,请将其与另一种方法进行测试对比,或至少测量它如何随输入大小扩展。如果需要,你可以决定用更高效的方式重写。
AI
生成的解决方案可能使用超过必要的内存:例如将整个文件读入内存而不是流式处理,从而持有大型数据结构。如果你的用例涉及大数据,请检查系统的内存使用情况,并在需要时通过流式处理或分块来优化。例如,如果你需要处理数百万条记录,你会想要重构
AI 生成的函数 loadAllRecords()
来分批处理或从数据库流式处理。
还要检查 AI 生成的代码是否释放了资源。在 Java 或 C#
等语言中,可能打开了文件或数据库连接但没有关闭。在前端单页应用中,可能事件监听器没有被移除,导致泄漏。工具可以提供帮助(如前端的
Chrome 开发工具的内存检查器或 C++ 泄漏检测的
Valgrind),但通常只需阅读代码就有帮助。识别这些问题并修复它们。如果你看到打开的文件句柄没有关闭,在
finally 块中添加关闭操作。
如果你使用支持线程或异步的语言,寻找 AI 代码可能是单线程但可以并行的地方。AI 可能不会在适当的地方自动使用 async/await,也可能不知道将繁重的 CPU 任务卸载到工作线程。识别这些机会。例如,对于 Node 或 Python 中的 I/O 密集型任务,确保异步使用以便系统不会阻塞。对于 CPU 密集型任务,AI 在代码方面可能帮助不大,但你可能决定用性能更高的语言实现或卸载到后台作业。
AI 并不总是自动添加的一个常见性能优化是缓存昂贵操作的结果。查看你的代码:它是否重复计算某些内容?如果是,实现缓存(内存中或使用像 Redis 这样的外部缓存)。你可以提示 AI:
为此函数添加缓存以避免冗余计算。
它可能实现简单的记忆化或建议使用缓存库。
如果你的应用程序使用数据库,检查 AI
创建的查询。它们是否正确使用了索引?也许 AI 写了 SELECT *
而只需要几列。或者它获取大量数据在代码中过滤,造成性能瓶颈,如 N + 1
查询问题。这些低效需要通过将更多工作推送到数据库或利用适当的索引来优化。
例如,如果生成的代码在循环中重复调用
findOne,导致多次数据库往返,你可以将其重构为使用
WHERE id IN (...) 的单个批量查询。同样,如果 AI
在迁移中省略了对频繁查询字段的索引创建,添加这些索引对于维持可接受的性能变得至关重要。AI
通常生成功能正确但次优的数据库交互,需要人类专业知识来识别和解决。
举例说明,让我们看一个例子。假设 AI 为你编写了一个函数来合并两个排序数组,只是简单地连接并排序结果:(O(n log n))—尽管有一个已知的线性算法可以用来合并两个排序列表(如归并排序的合并步骤,O(n))。在代码审查中,你意识到这对于大数组可能是瓶颈,所以你提示 AI 实现线性合并:
优化 mergeSortedArrays 函数以在线性时间内执行合并,不使用内置排序。
AI 识别出这是经典的归并算法并编写它。解决方案通过了你的测试,所以恭喜:你在不牺牲正确性的情况下获得了性能提升。
AI 辅助开发并没有消除性能调优的需求;它只是改变了你进行调优的时机。你通常会首先获得正确的解决方案(这非常有价值),然后将注意力转向测量和优化目标部分。当你确实需要优化某些东西时,AI 可以提供帮助,只要你引导它了解你的需求。
代码库的可维护性描述了随着时间推移,修改、扩展和理解代码的难易程度。一些人担心AI生成的代码可能会混乱或不一致,特别是当多个建议具有不同的风格或模式时。本节介绍了几种实践方法,可以用来解决这些问题,并保持你的vibe-coded项目的整洁性和可维护性。
当你准备提示词时,需要记住以下几点:
使用linters和formatters来强制执行一致的风格。如前所述,AI有时可能在不同的输出中使用不同的命名约定或格式。在代码生成后对所有代码运行formatter(如JS的Prettier、Python的Black、Go的gofmt等)可以确保代码符合统一的风格。这使得阅读代码变得更容易(无需在切换风格时产生认知负担)。此外,为你的项目定义命名约定并坚持使用它们。如果AI在一个地方输出get_user_data,在另一个地方输出fetchUserData,那么请决定你更喜欢哪种约定(snake_case还是camelCase等),并重构为一种风格。
通过提示AI编写模块化代码来鼓励关注点分离。例如,与其要求它编写一个实现所有功能的巨大文件,不如将工作分解为多个任务:
创建一个UserService类来处理用户逻辑。
创建一个单独的模块来发送电子邮件。
这会产生一个逻辑上分离的代码库。当每个模块都有明确的职责时,维护起来会更容易。你可以引导架构:
将数据库访问代码放在与API路由代码分离的文件或类中。
因为使用AI添加功能非常容易,所以防止功能蔓延(feature creep)和代码膨胀至关重要。如果没有严格的架构思维,你的代码库可能会退化为软件架构师所说的大泥球:这是一种反模式(antipattern),代码缺乏清晰的结构或边界。这种风险在AI辅助下会加剧,因为传统上与添加功能相关的阻力消失了,可能会加速架构的衰退。
为了应对这一点,将你的AI辅助开发建立在经过验证的架构模式和原则之上。在指导AI时,明确引用你的项目遵循的模式:
按照项目中使用的repository/service模式添加这个新功能。
使用我们领域层中建立的六边形架构(hexagonal architecture)来实现这个功能。
这种具体性有助于在功能快速积累时保持一致性。
对于寻求更深入架构基础的开发人员,几本基础文献提供了重要指导:
Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides(“四人帮”)的Design Patterns: Elements of Reusable Object-Oriented Software(Addison-Wesley, 1994)仍然是可重用设计解决方案的权威目录。
Mark Richards和Neal Ford的Fundamentals of Software Architecture: An Engineering Approach全面涵盖了跨技术栈的架构模式和原则。
Eric Evans的Domain-Driven Design: Tackling Complexity in the Heart of Software(Addison-Wesley, 2003)提供了将软件设计与业务领域对齐的关键技术—当AI生成必须反映复杂业务逻辑的代码时尤其有价值。
这些资源使你能够有效地引导AI工具,确保生成的代码遵循合理的架构原则,而不是增加技术债务。请记住:AI擅长实现模式,但无法确定哪些模式适合你的特定场景。这种架构判断从根本上仍然是人类的工作。
一旦AI响应并生成代码,可以使用以下可维护性技术:
持续重构
不要犹豫在需要时重构AI生成的代码。有时第一次生成是正确的,但结构并不理想:例如,AI可能会写一个非常长的函数,或在两个地方重复其逻辑。一个常见的挑战是无意中重复的代码:AI可能没有意识到两个函数做类似的事情而创建了两者。如果你注意到类似的代码块,重构为一个。像代码检查器(linter)这样的工具可以检测重复(有专门用于检测过于相似代码的检查器)。运行这些工具可以突出显示需要”DRY化”(不要重复自己)的地方。
要让AI帮助重构,你可以这样提示:
重构这段代码以消除重复并提高清晰度。
它可能会创建辅助函数或简化某些逻辑。重构后始终要测试。
本章已经介绍了测试,所以我只想指出,良好的测试套件使维护更容易。当你或其他人将来修改代码时(可能再次使用AI),你的测试将捕获更改是否破坏了任何东西,这样你就可以放心地重构或更改实现。测试将”它做什么”与”它如何做”解耦,使你能够灵活地维护或改进”如何做”而不改变”做什么”。
有时AI可能会使用一个巧妙的技巧或其他开发者可能不知道的不太常见的函数。虽然这本身并不坏,但要考虑可维护性:如果普通开发者会对代码感到困惑,也许应该简化它。例如,如果AI使用了一些正则表达式魔法或过于简洁的列表推导式,将其重写为更明确的循环以提高清晰度(或至少添加注释)。
同样,试图提供帮助的AI可能会过度设计解决方案,比如添加不需要的层次。例如,也许直接的方法就可以,但AI引入了一个没有发挥作用的抽象。删除它以保持简单明了。更简单的代码通常更容易维护。
考虑失败情况下的回退策略(fallback)。例如,如果AI编写的组件调用外部API,而该API宕机或返回意外数据,我们是否有回退机制(比如使用缓存数据或默认响应)?实现这样的弹性模式(断路器、带退避的重试等)可以使系统更加健壮。除非被要求,否则AI可能不会自己做这些。确保系统能够优雅地处理部分失败。如果可能的话,一个微服务的宕机不应该导致整个应用程序崩溃。使用超时和回退逻辑。
一旦你对代码满意,还有一些实践可以帮助保持其可维护性:
确保代码得到适当的文档化。除非被提示,否则AI通常只写最少的注释。你可以通过提示请求文档字符串或注释:
添加注释来解释此代码中每个部分的目的。
为此函数编写文档字符串。
这些可以节省未来读者的时间。AI通常可以生成相当好的解释,但有时会误解微妙的要点,所以要检查准确性。
还要考虑为项目维护高级文档(如README或设计文档),描述其架构、主要组件等。你可以主要自己编写,但如果需要,AI可以通过总结代码库来提供帮助。
如果你遇到一些怪癖,比如”AI总是奇怪地命名这个参数”,在你的开发笔记中为其他人提及它。这是新协作环境的一部分。如果只有你使用AI生成的代码,一些怪癖是可以的——但如果其他人加入项目,他们可能会想,“为什么这个东西这样命名?”也许只是标准化这些名称。
这不是一种普遍的做法。它在代码审查(code review)期间可能有帮助,但如果人类已经审查过代码,现在它只是代码,你可能不需要它。如果你确实保留了任何记录或提示词(prompt),可以在复杂代码的注释中链接它们:“此算法基于提示词X通过GPT-4派生;推导过程见文档。”审查者不需要在审查强度方面区别对待(你应该仔细审查所有代码),但这可以帮助理解上下文。例如,如果代码有某种风格不匹配或奇怪的习惯用法,知道它来自AI可能会提示审查者这不是刻意的作者选择,而是AI的产物。
如果你在团队中工作,让所有团队成员审查代码—即使是一个人和AI共同编写的。他们可能会发现不合适的模式或违反团队规范的东西。随着时间的推移,你会逐渐掌握如何提示AI以匹配你团队的风格(可能在系统提示词(system prompt)或初始指南中包含具体要求)。如果多个开发者使用AI,确保每个人都知道期望的风格模式,以便他们可以相应地提示(如”用函数式风格编写”或”使用async/await,不要用回调”)。有关AI代码审查的一些技巧,请参见下一节。
如果在开发过程中,你接受了一个你知道不理想的AI解决方案,请在注释或项目待办事项中将其作为技术债务(technical debt)进行跟踪:“TODO:该解决方案有效但是O(n2);如果数据增长,需要优化”,或”TODO:为简单起见使用了全局变量;稍后改进此处。“如果你要求,AI甚至可以自己插入TODO注释:
如果有任何需要未来改进的地方,请添加待办注释。
只要最终解决这些待办事项即可。
如果AI引入了你不熟悉的设计模式或库,花时间深入了解它,而不是忽略它。理解特定的缓存方法或它使用的库将帮助你在未来自信地维护或修改该部分。如果太过晦涩,你可能会决定删除它,转而使用你熟悉的东西—但有时AI可以让你惊喜地发现你不知道的有用库或模式。如果这是一个你和团队可以学习的知名解决方案,这甚至可以提高可维护性。
实际上,可维护性归结为应用与以往相同的良好软件工程原则—只是将它们应用于部分由AI编写的代码。幸运的是,由于AI减少了繁重的工作,你可能有更多时间专注于清理代码和编写文档,这反而提高了可维护性。
一些公司报告在用AI生成代码的初期爆发之后,他们投入时间进行”加固冲刺(hardening sprint)“来重构和记录所有代码。考虑在生成密集型冲刺和清理冲刺之间交替作为一种潜在策略。
如[第4章]所述,代码审查在传统开发中是一个关键过程,在AI辅助开发中仍然如此。本节讨论当审查的代码块是机器建议的时需要考虑的一些细微差别。因为AI可以如此快速地生成代码,担心代码审查会成为瓶颈是合理的—但不要让这种担心妨碍审查过程。为审查分配适当的时间至关重要。不要基于”我们快速编写,就快速合并”的假设而草率行事。如果可以的话,更频繁地提交较小的更改以使审查更容易(无论如何这通常是一个好做法)。频繁的、较小的拉取请求(PR)比一个巨大的PR更容易彻底审查。如果你提前规划,AI也可以帮助将任务分解为较小的PR。
不要仅仅因为”AI编写了它并且测试通过”就假设代码是正确的。批判性地思考并尝试推理逻辑。如果可能,在提供的测试之外用额外的用例进行心理测试或测试,因为测试可能无法涵盖所有情况。你还可以运行代码,甚至通过用棘手的输入运行代码片段来试验,看看它是否按预期运行。
代码审查也可以是重要的学习时刻。如果AI引入了一个实际上很好的新颖解决方案,审查者在验证其正确性的同时可能会学到新东西。同样,如果AI/人类组合做了一些次优的事情,审查者可以解释更好的方法。随着时间的推移,这个反馈循环可以改善团队使用AI的方式(比如帮助每个人了解应该避免哪些事情或以不同方式提问)。从某种意义上说,代码审查有助于闭合人类学习循环,因为人类作者应该学习和理解AI编写的任何对他们来说是新的东西。
当您审查代码时,首要任务应该是确保它满足需求和预期设计。这段代码是否实现了功能/缺陷修复应该做的事情?它是否涵盖了规范中提到的任何边界情况?如果提示词有偏差,AI 可能会解决一个稍微不同的问题:也许它处理了一个不需要的情况,或者遗漏了某个情况。这很正常,但要注意开发人员不要只是接受仅部分解决问题的 AI 输出。例如,AI 可能会生成格式化日期的代码,但假设某个特定时区,这可能与需求一致,也可能不一致。
如果代码中有不明显的地方,请要求作者解释它是如何工作的或为什么这样做。如果他们难以解释或求助于”AI 这样做的,我认为是对的”,这是一个危险信号。团队应该理解代码库中的所有内容。鼓励作者与 AI 或文档再次确认,并提供适当的解释,可能作为代码中的注释。
同时注意本章前面讨论的安全性和性能漏洞,如果违反了任何已知的最佳实践,请指出来——比如输出没有转义(在 Web 开发中)或在代码中发现凭证。
如果您看到代码可以工作但可以更简单或更符合团队风格,请要求更改或重构(refactoring):
AI 为不同的用户角色创建了 3 个独立的函数,它们大部分是重复的。我们能否将它们合并为一个带有角色参数的函数?
然后代码作者可以这样做(可能在 AI 的帮助下)。如果 AI 建议没有使用团队的一致风格或标准库,也要提出来:
我们通常使用 requests 库进行 HTTP 调用,但这段代码使用的是 http.client。让我们坚持使用 requests 以保持一致性。
然后作者可以提示 AI 使用首选库重写。
如果 AI 编写了非常复杂的东西,比如一个棘手的算法,可以考虑与另一位审查者或团队讨论,进行更深入的审查。
您可能想尝试一些使用 AI 辅助代码审查的新兴工具——比如 GitHub’s Copilot for Pull Requests,它可以生成摘要并标记潜在的错误和其他问题。这样的工具可能会突出显示诸如”这段代码片段与模块 X 中的代码相似但略有不同”(指出可能的重复)。这些提示可以补充人工审查,但不应取代它。
最后,在审查时要尊重和建设性,即使代码由于 AI 而存在缺陷。避免因可能是 AI 产物而责备开发人员:虽然他们仍然对自己的代码负责,但要认识到上下文。AI 是一个工具,作者和审查者都在使用它。目标是改进代码和分享知识,而不是指责。例如:“这部分似乎有安全问题——可能是 AI 建议的疏忽;让我们修复它。”
最终,在氛围编码(vibe coding)中的代码审查是我们充分发挥人机合作中人类智能一方的方式。这是监督和专业知识发挥作用的地方,以捕获 AI 可能遗漏的内容并保持高质量标准。这也是团队知识共享的时刻,因为在审查中讨论代码可以传播对领域和如何最好地使用 AI 的理解。
代码审查还形式化了 Grant Gross 在 CIO 中提出的”开发人员作为编辑者”的概念:审查者是编辑者,确保代码经过润色并适合生产。这与氛围编码(vibe coding)的概念完美契合,其中氛围(AI 建议)存在,但人类判断对其进行完善。
一旦您知道代码是安全的、经过测试的和可维护的,您需要将其部署并在生产环境中保持可靠运行。
虽然 AI 辅助开发不会改变软件部署的核心原则,但它确实引入了关于部署速度和运营复杂性的考虑因素。对于那些寻求部署基础全面覆盖的人,Gene Kim、Jez Humble、Patrick Debois、John Willis 和 Nicole Forsgren 的《DevOps 手册》(The DevOps Handbook)(IT Revolution Press,2016)提供了权威指南,涵盖从持续集成(continuous integration)和部署管道(deployment pipelines)到监控(monitoring)、安全性和组织转型的所有内容。当 AI 加速您生成可部署代码的能力时,这些基础知识变得更加关键,因为这些原则确保您的部署实践能够随着您增加的开发速度而扩展。
在准备部署时,请考虑以下最佳实践:
自动化您的 CI/CD 管道(pipeline)
始终制定回滚计划。尽管进行了所有测试和审查,有时仍会有问题漏网。如果新版本出现问题,准备好回退到最后一个稳定版本。如果你使用 Kubernetes 等容器化策略,请保留以前的部署以便快速切换。如果是无服务器函数(serverless function),在确信新版本没有问题之前,保持旧版本处于活动状态。
在生产环境中设置全面的监控,包括系统指标和应用程序日志:
使用 Sentry 等工具跟踪错误并捕获异常。如果 AI 代码在生产环境中抛出意外错误(可能是某个边缘情况未被覆盖),你将收到警报以便进行修复。
使用应用程序性能监控(Application Performance Monitoring, APM)等性能监控工具来跟踪响应时间、吞吐量和内存使用情况。这将显示新部署中是否有任何代码引入了速度减慢或内存泄漏。
监控可用性:例如,ping 服务端点以确认它们处于运行状态。如果某些东西崩溃了(可能是由于某些未测试的场景),应该触发警报,以便你能快速响应。
部署后,这些策略可以帮助保持一切可靠运行:
为运维团队提供手册,描述代码中AI生成部分的任何特殊方面:“此服务使用AI模型执行X功能;如果模型输出看起来有误,请尝试重启服务或检查模型版本。”或者”功能Y严重依赖缓存来保证性能;如果出现性能问题,请检查缓存命中率。“本质上,记录任何可能不明显的运维注意事项。如果AI引入了依赖项(如使用临时文件),请注明,这样运维人员就知道要监控磁盘空间等。
除了在开发期间和作为发布的一部分进行测试之外,一些公司还会以安全的方式在生产环境中进行测试(TiP, Testing in Production),例如持续运行小规模实验。例如,您可以使用功能标志为一小部分用户启用AI生成的功能,并观察错误率是否发生变化。这与金丝雀发布重叠,但您可以使用功能开关使其更加细粒度。
安排定期对代码库进行安全性和性能审计,特别是随着越来越多AI贡献的积累。这类似于管理技术债务:它帮助您捕获最初没问题但随着规模或上下文变化可能变得有问题的事情。还要注意”漂移”——如果AI代码正在生成SQL查询,请确保您的迁移和代码保持同步,并且部署在新代码接收流量之前正确运行迁移。
主题继续——人类应该监控自动化。AI可能帮助您编写代码,但它不会在凌晨2点修复生产事故。安排了解系统的人值班。随着时间的推移,您可能会让AI帮助排除故障,例如分析日志(这是一些新兴工具的功能),但归根结底,应该由人类来决定修复措施。
没有任何流程是100%完美的。如果错误突破了您的防御并导致事故,请进行事后分析。确定问题是否与AI使用有关(例如”我们在这里信任了AI代码,但它在场景X下失败了”),并更新您的流程和测试以防止该类问题。每次进行这种分析都会持续提高可靠性。
当然,可靠性不仅仅关乎代码;它还涉及代码周围的基础设施和运维。AI主要在代码方面提供帮助。强大的运维实践(可以部分由AI辅助)保持整个系统的可靠性。
从本质上讲,在部署方面,对待 AI 密集型项目应该与对待任何高质量软件项目一样:采用全面的测试、逐步推出、大量监控,并确保可以快速回滚。因为 AI 可以更快地创建变更,你最终可能会更频繁地部署(如果你的 CI/CD 流水线足够好,这是可以的)。频繁的小型部署实际上已知可以降低风险,相比不频繁的大型部署。原因在于每个单独的变更都更小,使得识别和修复出现的任何问题变得更容易。如果出现问题,回滚小型变更也更简单、更快。这种方法与大型、不频繁的发布形成对比,后者将众多变更捆绑在一起,使得难以确定任何问题的原因,并增加了部署失败的潜在影响。
通过遵循这些最佳实践,你可以确信即使大量代码是机器生成的,你的整个系统也会为用户可靠地运行。自动化测试、谨慎部署和监控的组合形成了闭环,可以捕获早期阶段遗漏的任何问题。因此,你可以获得 AI 开发的速度和生产力优势,而不必牺牲在生产环境中信任软件的能力。
总之,氛围编码(vibe coding)并不能消除对工程严谨性的需求——它放大了应用这种严谨性的工程师的生产力。你的座右铭应该是古老的俄罗斯谚语:信任但要验证。信任 AI 处理繁重的工作,但用你的工具和专业知识验证一切。
安全性和可靠性是负责任开发的一个维度;伦理是另一个维度。AI 辅助编码引发了关于知识产权、偏见、对开发人员工作的影响等重要问题。第9章将深入探讨这些更广泛的影响。你如何负责任且公平地使用 AI 编码工具?你如何处理 AI 生成代码的许可问题,并确保你的模型和提示词(prompt)得到合乎伦理的使用?
随着 AI 辅助开发变得越来越普遍,解决这种新范式的伦理和社会影响至关重要。本章从技术细节中退后一步,从伦理角度审视氛围编码:这些新的开发方法可能是有效的,但它们也需要负责任地实施,并使个人和整个社会受益。
我从知识产权(IP)的问题开始。谁拥有 AI 生成的代码,是否允许使用可能源自开源代码但未注明出处的 AI 输出?从那里,我考虑偏见和公平性。透明度是另一个关注点:开发人员是否应该披露代码库的哪些部分是 AI 生成的,团队如何确保代码质量和 bug 的问责制?
我概述了 AI 使用中负责任的开发实践,从建立透明度和问责制到避免在提示词中使用敏感数据,再到确保可访问性和包容性。本章以一套负责任使用 AI 工具的指南结束。
以下部分涉及复杂的法律话题,特别是关于版权和知识产权法,主要从美国的角度出发。法律体系和解释在全球范围内不断演变,尤其是在人工智能方面。此信息仅供教育目的,不构成法律建议。在根据此信息做出任何决定之前,你应该咨询合格的知识产权律师,特别是如果你对自己或 AI 工具生成的代码的所有权或许可有疑虑。
谁拥有 AI 生成的代码?使用它是否尊重 AI 训练所依据的源材料的许可证和版权?像 GPT 这样的 AI 模型已经在来自互联网的大量代码上进行了训练,包括具有各种许可证(MIT、GPL、Apache 等)的开源存储库。如果 AI 生成的代码片段与来自 GPL 许可项目的某些内容非常相似(或相同),在专有代码库中使用该片段可能会无意中违反 GPL,因为 GPL 通常要求共享衍生代码。
根据开源规范和一般版权原则,几行代码的小片段可能不受版权保护,如果它们缺乏足够的原创性而无法被视为独立的创作作品,或者它们的使用可能被认为是微不足道的(de minimis)(太琐碎而不值得法律关注)。然而,任何实质性的或表达独特创意选择的内容更有可能受到版权保护。理解”开源”并不意味着”公共领域”是至关重要的。默认情况下,包括代码在内的创作作品由其作者享有专有版权。开源许可证明确授予的权限,否则会受到版权法的限制。
如果你想了解更多关于开源规范的信息,可以从以下几个地方开始:
OSI 定义和推广开源软件,维护开源定义(Open Source Definition),并批准符合其标准的许可证。
FSF 倡导”自由软件”(与开源原则有很大重叠),并且是 GNU 通用公共许可证(GPL)等许可证的管理者。
各个开源项目通常包含 LICENSE 文件、README 文件和 CONTRIBUTING 指南,详细说明该特定项目的使用和贡献条款。
像 GitHub 这样的网站提供关于开源实践的大量文档和讨论。像 Linux 基金会和法律信息网站这样的组织也提供关于开源合规性和法律方面的宝贵资源。
使用小代码片段是否与合理使用原则(在美国;许多其他司法管辖区称为”公平交易”)重叠的问题是复杂的,并且高度依赖于具体情况。合理使用允许在未经许可的情况下有限使用受版权保护的材料,用于批评、评论、新闻报道、教学、学术或研究等目的。美国法院通常考虑四个因素来确定合理使用:
使用的目的和性质(商业性与非营利性,转换性与复制性)
受版权保护作品的性质(高度创造性与事实性)
相对于整个受版权保护作品而言,使用部分的数量和实质性
使用对受版权保护作品的潜在市场或价值的影响
虽然有些人可能会认为,为了互操作性或访问不受版权保护的想法而复制非常小的功能性代码片段可能属于合理使用,特别是如果使用是转换性的,但这在代码法律中并不是一个明确解决的领域,并且没有普遍认同的行数被明确定义为”合理使用”或微不足道。最安全的做法通常是获得许可,或者理解底层想法并用你自己的方式重写代码。美国最高法院案例 Google LLC v. Oracle America, Inc. 处理了软件 API 背景下的合理使用问题,认为 Google 对 Java API 声明代码的重新实现属于合理使用,但这是一个具体而复杂的裁决,专注于 API 声明,而不是所有代码。人们普遍理解,版权保护的是想法的具体表达,而不是想法、程序或操作方法本身。
通常,使用 AI 的开发者被视为”作者”,因为 AI 是一种工具,类似于编译器或文字处理器。因此,如果代码是在工作环境中生成的,开发者的公司可能会拥有开发者使用该工具产生的代码,但需遵守 AI 工具的服务条款和底层知识产权问题。然而,AI 工具的服务条款(ToS)至关重要。大多数 ToS 授予用户对其生成的输出的权利。例如,OpenAI 的 ToS 声明:“你拥有使用 GPT-4 创建的输出,包括代码。”
然而,这个”所有权”需要仔细考虑。它通常意味着AI提供商并不声称拥有你使用他们的工具创建的内容的所有权。但这假设你拥有所提供输入内容的权利,并且这并不自动意味着输出本身有资格获得版权保护,或者它不受第三方知识产权声明的影响。如果你将自己的原创代码输入到工具中进行修改或扩展,输出很可能是你的(或你雇主的),同样取决于AI如何处理它以及它从训练数据中融入了什么。但如果你输入别人的受版权保护的代码来修复或转换,输出可能被视为该第三方代码的衍生作品。
在美国和许多其他司法管辖区,与训练数据基本相似的AI生成输出,或基于受版权保护输入的输出,是否构成衍生作品是一个正在进行的法律辩论主题,缺乏完全的清晰度。不要将不属于你的大块受版权保护的代码(或未获得适当许可)输入AI工具,因为输出可能被视为衍生作品,从而受该原始受版权保护代码的许可约束。
鉴于这些不确定性,为了安全起见,将AI生成的代码视为处于模糊许可之下,并且仅在你确信它不侵犯现有版权并且你可以遵守任何潜在的开源许可义务时才使用它。关于AI输出本身的版权状态,美国版权局已声明,完全由AI生成而没有足够人类作者身份的作品是不受版权保护的。如果人类以创造性方式显著修改或编排AI生成的材料,该人类贡献可能是可受版权保护的,但单独的AI生成元素则不能。因此,通常明智的做法是假设纯粹的AI生成输出可能不受任何人的版权保护,或者版权仅延伸到人类的创造性贡献。
这不是假设性的担忧。事实上,存在正在进行的法律辩论。一项著名的集体诉讼Doe v. GitHub, Inc.针对GitHub、Microsoft和OpenAI提起,声称GitHub Copilot生成的代码与许可的开源代码过于相似,而没有适当的署名或遵守许可条款。虽然此案中的一些索赔已被驳回或正在上诉中(截至2025年中期,该案涉及正在进行的诉讼程序,包括向第九巡回法院上诉DMCA索赔和剩余的违约索赔),但它突出了一个真正的担忧:AI可以并且有时确实会逐字复制或紧密转述其训练数据中的受版权保护代码。1
GitHub自己的一项较早(但仍然相关并后来得到证实)研究指出,在某些情况下,Copilot的输出包括与训练数据匹配的建议,包括罕见的较长逐字片段实例。虽然大多数AI工具旨在避免直接、大量复制可识别的代码,除非特别提示或处理非常标准的算法,但风险依然存在。此外,令人担忧的不仅仅是开源代码;许多作者、艺术家和媒体公司已经提起诉讼,声称他们的完全受版权保护的私有知识产权在未经许可或补偿的情况下被用于训练大型语言模型和其他生成式AI系统。专有代码的挑战在于,与开源不同,它通常不是公开可见的,这使得最终用户更难确认AI的输出是否无意中与此类私有代码相似。
尽管如此,合乎道德和审慎的做法是将你从AI工具接受的任何代码视为你的责任。在将任何AI生成的代码整合到你的项目之前,彻底审查、测试和理解它,并确保其使用符合所有适用的许可和版权法。
如果AI输出看起来像是已知代码的逐字或近似逐字副本(特别是如果它包含独特的注释或作者名称),请谨慎对待。考虑使用抄袭检测工具运行相似性检查,或对独特字符串进行网络搜索,看看是否能找到任何可能表明复制的匹配项。
另一个要遵循的原则是如有疑问,就不要使用。要么避免使用该输出,要么确保它在兼容的许可证下,并在需要时注明出处。例如,如果 Copilot 生成了一个你从 Stack Overflow 或开源项目中认出的知名算法实现,请引用来源或用你自己的方式重写它,将 AI 的答案作为指南但不要逐字引用。
如果你怀疑输出与现有库解决方案匹配,考虑直接包含该库本身(并遵守适当的许可证)。你也可以这样提示 AI:
请提供原创实现,而不是从库中复制的实现。
然后它可能会合成一个更独特的解决方案。(无法保证它不会受到训练代码的影响,但至少它会尝试不直接复制)。
这里的伦理问题还涉及不使用 AI 故意删除署名。例如,通过 AI 从 Stack Overflow 复制代码而不注明出处以规避应该注明答案来源的政策,这是不道德的。这会侵蚀开放知识生态系统中的信任。更好的做法是在适当注明出处的情况下整合材料。根据具体情况,这可能意味着以下内容:
如果 AI 从某个有作者姓名的来源编写了代码注释(比如复制了一个注释中带有”John Doe 2018”的代码片段),你应该保留它或将其移至适当的署名部分并提供完整引用,而不是删除它。这尊重了原作者的署名权。
如果 AI 提供的解决方案你知道来自某个已知算法或代码片段,就像你自己查找时一样引用该来源。
如果 AI 工具创建了可以说是有创意的东西(比如独特的方法或文档文本),承认它的贡献。虽然它没有权利,但这关乎透明度(也许是对技术的认可)。
一些开源许可证(如 MIT)足够宽松,只要注明出处就可以满足许可证要求。其他许可证,如 GPL 或 AGPL,如果你包含该代码会”感染”你的整个代码库,这对于闭源项目是不可取的。
简而言之:如果你怀疑 AI 给你的东西可能导致知识产权问题,要么避免使用它,要么充分转换它以确保你遵守任何可能的许可证。
即使在我写这篇文章的时候,AI 工具仍在不断提出关于知识产权、版权和伦理的新问题。例如:
如果你的氛围编程(vibe coding)包括使用 AI 生成非代码资产,如文档文本、配置文件或图像,也会出现类似的知识产权问题。例如,如果你通过一个基于受版权保护图像训练的 AI 工具生成图标图像,谁拥有这个新图像?
如果 AI 编写了软件产品的重要部分,AI 训练所基于的代码的原作者是否应该获得署名?
有人能否声称你的 AI 生成代码侵犯了他们的版权,因为它看起来与他们的相似?如果非平凡长度的部分可能相同,这就是需要进行相似性检查的地方。
有一种新兴观念认为,AI 公司可能需要实施尊重许可证的过滤器,或允许团队选择不将他们的代码包含在 AI 训练数据中。这正在发展中,但实际开发者应该保守行事,不要侵犯他人权利。
法院需要时间来解决所有法律问题,但与此同时,知识诚信和尊重应该指导我们。如果 AI 使用了已发表论文中的已知算法,在注释中引用该论文。如果它使用了常见的开源辅助代码,注明该项目。这关乎对作者身份的尊重。如果你认出某些内容的来源,宁可多给出署名。这是一个促进透明度的良好实践。
请记住,在底层,AI 的知识来自成千上万公开分享代码的开发者。从伦理上讲,软件行业欠那个社区以尊重开源许可证和规范的态度。在应该署名的地方给予署名,不要在”是 AI 写的,不是我”的幌子下滥用他人的工作。
透明度指的是对在开发过程和输出中使用 AI 保持开放,署名指的是当 AI 衍生代码来自可识别来源时给予适当的署名。
透明度对于问责制很重要。例如,如果AI生成的代码引入了一个bug或安全漏洞,透明地说明”这段代码是AI建议的”可能有助于你分析根本原因——也许应该重写一个模糊的提示词(prompt)。在代码注释或项目的README或文档中,你可能会笼统地提到”该项目是在ChatGPT等AI工具的帮助下构建的。“或者更具体地说:”添加了一个解析CSV的函数(在ChatGPT的帮助下生成,然后进行了修改)。“这有点像承认你使用了框架或库。
透明度也是信任的关键:利益相关者(你的团队、客户、最终用户或行业监管机构)可能想知道你的软件是如何开发和验证的。如果AI参与了代码生成,一些利益相关者可能会错误地过度信任或过度怀疑它。透明度允许就可靠性展开对话:“是的,我们使用了AI,但我们进行了彻底的测试”或”这部分很棘手——我们让AI生成了初始代码,但我们已经验证过了。”
在许多学术场合,对AI生成代码的归属也是预期的或必需的。一些开源项目由于知识产权问题限制甚至禁止AI贡献,因此在使用AI之前请查看贡献者指南。如果补丁是AI生成的,对维护者保持透明有助于他们评估它,特别是当许可是一个问题时。
事实上,一些高度监管的行业要求软件供应商出于审计目的披露任何AI使用。欧盟的AI法案要求对影响个人的自动化决策(如信用评分算法)保持透明。如果氛围编码(vibe coding)导致此类系统,告知用户”建议是自动生成的,可能反映数据中的模式”就成为法律/道德上的必要性。
同样,如果你的产品将用户数据或专有数据(如用户提供的代码示例)输入AI模型以对其进行微调并帮助编程其分析,你可能需要在隐私政策中说明用户数据可能在获得许可的情况下用于改进AI模型(一如既往,法律事务请咨询律师)。透明度在这里与隐私交叉。
承认你使用的工具和来源在总体上也是符合道德的。如果你30%的代码是由Copilot生成的,在你的文档或内部沟通中提到这一点是公平的——不是为了贬低你自己的作用,而是为了对过程保持诚实。
一些开发者可能担心承认AI提供了帮助,担心这可能会削弱他们被认知的贡献或技能,或被视为”作弊”。随着氛围编码变得更加规范化,这种污名应该会减少;最终,如果你不使用可用的AI,你可能会被视为落伍。我们需要将AI规范化为一种工具——它不比使用Stack Overflow或IDE更”作弊”。
另一方面,提供过多的免责声明可能会引起不必要的担忧。如果你告诉客户,“我们使用AI来编写这个产品的代码,”他们可能会质疑其安全性(即使这是由于误解)。你如何表述很重要。在同一时间强调质量措施:“我们利用先进的编码助手来加快开发速度,所有AI生成的代码都经过严格审查和测试,以符合我们的质量标准。”
总之,透明度和归属培养信任和社区价值。它们确保功劳归于人类创造者,并确保我们对软件的构建方式保持诚实。这类似于艺术家列出他们的工具或灵感;它不会削弱艺术;它将其置于背景中。如果像我一样,你希望氛围编码被广泛接受,那么公开使用AI以及如何减轻其风险是很重要的。
正如你在本书的这一部分已经很清楚的那样,AI模型的输出反映了它们所训练的数据。如果这些数据包含偏见或排斥性模式,模型可能会产生有偏见或不公平的输出。
你可能会问:“代码怎么会有偏见?这又不像LLM在做招聘决策之类的事情。”但偏见可能以微妙的方式渗入你的编码:
代码通常反映创建者的假设。AI生成的面向用户的文本或内容可能反映其训练数据中存在的文化偏见或不敏感语言。例如,微软的Tay,一个2016年的早期聊天机器人,在推出后的几小时内就因与Twitter的互动而学会了模仿种族主义和厌女口号,这一点臭名昭著。
假设(assumption)也可能针对特定的文化规范,比如北美中产阶级的生活方式(如假设拥有汽车或普遍可以使用某些技术)。一个由于未经审查的假设导致排他性产品的典型例子是 2014年发布的苹果健康应用,它缺少经期追踪功能——这一重大疏忽很可能源于设计团队缺乏多样性和不同视角。即使在示例代码、注释或合成数据中,模型也可能总是使用 he/him 代词,从而强化性别偏见。
众所周知,代码仓库和更广泛的软件开发领域主要反映的是西方视角和英语使用者。因此,在这些仓库上训练的AI可能会忽略关键的国际化(internationalization)方面,比如对Unicode和多字节字符的正确支持(这对中文、日文、韩文、阿拉伯文、印地文以及许多其他使用非拉丁或音节文字的语言至关重要),或者它可能默认使用以英语为中心的示例来命名类型。开发者必须提高意识并为国际化进行设计和编码,即使AI不会自发地这样做。
如果编写算法,要警惕种族、性别、年龄等变量。除非被问到,AI可能不会自发地包含它们,但如果它幻觉出某些标准,或者你在数据集上使用像Code Assistant这样的AI,请应用公平性约束(fairness constraints);AI不会天生就知道道德或法律背景。
除了编码之外,模型还可能在其内容领域中反映数据偏见(data bias):其训练数据中存在的历史偏见。例如,考虑一个负责为贷款审批编写信用评分算法代码的AI。在美国,信用评分系统有记录显示反映并延续了种族偏见。这些偏见源于历史上的做法,如红线政策(redlining)和其他形式的系统性歧视,这些歧视产生了持久的金融影响,特别是对黑人社区和其他边缘化群体。(参见Richard Rothstein的《法律的颜色》[经济政策研究所,2017],了解政府政策如何隔离美国的全面历史。)
如果训练数据反映了这些历史偏见,AI可能会纳入歧视性变量,例如使用邮政编码(由于隔离的住房模式,这可能是种族人口统计的代理变量)或其他看似中立但与受保护特征相关的数据点。如果没有得到适当的指导,AI可能会生成导致银行做出不公平贷款决策的代码,从而延续历史不平等并影响真实人们的生活。类似的问题也出现在预测性警务算法等领域,其中历史逮捕数据(本身可能存在偏见)可能导致AI系统不成比例地针对某些社区。
同样,如果你使用专门的模型(比如针对医疗软件进行微调的AI代码助手),请确保模型不会被锁定在该领域数据的偏见中。例如,从历史上看,一些医疗指南存在偏见,因为研究主要使用男性受试者,导致对其他性别的误诊或治疗效果较差。如果AI推荐医疗诊断的代码或解决方案,你需要仔细检查它是否无意中编码了那些偏见。
有一些工具正在出现,用于检测AI输出中的偏见,尽管这些工具在用于生成内容的GPT模型中更常见,AI提供商自己也试图过滤明显有偏见或有毒的输出。面向代码的AI很少自发产生仇恨言论,但它们有内容过滤器是好事。在许多AI工具中内置道德约束意味着,如果用户试图让AI创建恶意软件或歧视性算法,它会拒绝。不要试图破解这些过滤器来获得不道德的输出。
在开发过程的不同阶段,还有很多其他方法来识别和减轻偏见。这些包括:
如果你的AI生成面向用户的组件或处理与人类相关数据的逻辑,请使用多样化的输入进行测试。例如,如果AI生成的表单验证期望”名字”和”姓氏”,它是否允许单一名字,这在某些文化中很常见?如果不允许,那就是假设中的偏见。如果它生成示例用户名,它们是否都像”JohnDoe”?如果是这样,请考虑在示例中纳入更多多样性。
你可以明确指示AI保持中立或包容:“生成使用来自不同文化的各种名字的示例。”如果它总是将用户称为”他”,你可以提示:
在此代码注释中避免使用性别化语言;使用中性措辞或they/them代词。
将我们所讨论的内容汇总起来,值得明确一套负责任的vibe编码(vibe coding)实践规则:
始终保持人工监督
再次强调:绝不让AI在无人监督的情况下工作。负责任的AI辅助开发意味着你,作为开发者,要审查每一行代码并做出决策,而不是在没有人工验证的情况下部署原始的AI输出。
对你的代码负责
如果出现问题,这不是AI的错——而是开发团队的责任。保持这种心态可以避免自满。无论你是从头编写代码还是接受了AI代码,都要准备好为你的代码辩护。如果有人问你,“代码为什么这样做?”不要说,“我不知道;是Copilot做的。”这就是为什么第3章的黄金法则之一是”永远不要提交你不完全理解的代码。“这才是负责任的工程实践。
保护用户隐私并征得他们的同意
从道德角度讲,你有责任对用户和公司保守他们的机密数据。在使用AI工具时,特别是基于云的工具,要小心不要在提示词或对话中暴露敏感数据。例如,如果你正在调试用户数据库的问题,不要将实际的用户记录输入到ChatGPT中。应该使用经过脱敏或合成的数据。
现在许多工具允许用户(至少是商业用户)选择不将他们的输入数据用于训练。如果你是企业用户,请使用这些设置或对敏感代码使用本地部署的解决方案。如果你确实将任何用户数据输入到模型中,或者如果任何AI功能直接接触用户(比如应用中使用LLM的聊天机器人),请征得用户同意,并在适当时允许他们选择退出。像”此功能使用AI服务;你的输入将被发送到该服务进行处理”这样的警告是透明的,让注重隐私的用户自己决定。
遵守法律法规
关注AI相关的法律要求,这些要求在不断演变。例如,数据保护法,如欧盟的《通用数据保护条例》(GDPR)和《人工智能法案》(AI Act)认为,如果AI输出包含任何个人数据,则将其视为个人数据。在用户数据上训练模型可能需要征得这些用户的同意。监管机构可能将代码生成归类为”通用AI”,并施加透明度或风险管理义务。保持信息畅通,并与你的法律和合规专业人员密切合作,避免违反任何法规。
虽然这应该不言而喻,但不要使用AI生成恶意软件、没有道德正当理由的漏洞利用代码,或自动化不道德或非法的做法。虽然AI可能可以写出非常有效的钓鱼邮件或代码注入攻击,但将其用于此目的违反了道德、大多数国家的法律,并且可能违反AI的服务条款。专注于建设性的使用。
在组织中培养负责任的AI文化
如果你的团队采用vibe编码,鼓励关于道德的讨论,并提供相关的道德培训。考虑让开发者和代码审查者使用如图9-1所示的简短检查清单。

每个人都应该对道德的AI使用负责;这是集体的努力,而不仅仅是某个时刻使用工具的个人的负担。为了将其正式化,考虑在你的团队或组织中指定一名”道德倡导者”或一个小型道德委员会。这个个人或小组不会是道德的唯一所有者(因为这一责任仍然是共同的),但他们将负责:
紧跟AI道德领域的最新发展、新兴最佳实践和新的监管环境
促进关于特定项目中道德考虑的讨论
倡导将道德原则融入开发生命周期
帮助整理和传播相关资源和培训材料给更广泛的团队
作为团队成员提出道德问题或疑虑时的联系人
由于这个领域发展极快,团队合作以了解AI工具的新版本及其能力、局限性和负责任使用的演进最佳实践至关重要。
由于这个领域发展迅速,团队应共同努力以了解AI工具和最佳实践的新版本。一个需要整合到工作流程中的重要概念是使用模型卡(model cards)。模型卡本质上是提供机器学习模型透明度的标准化文档。可以把它们看作AI模型的营养标签。它们通常包括以下详细信息:
模型是什么、版本以及开发时间
模型设计和测试的具体用例
由于局限性或潜在危害而不应使用模型的场景
模型在各种基准测试上的表现,包括跨不同人口群体的公平性和偏见评估
用于训练模型的数据集信息,包括数据中的任何已知局限性或偏见
潜在风险和社会影响以及采用的任何缓解策略
每当你使用预训练模型或评估模型以供使用时,查找其模型卡。如果你在微调或开发模型,创建自己的模型卡是最佳实践。
创建护栏和安全网。
实践负责任的设计意味着你的AI生成系统应该有安全网。例如,如果AI建议一个可能掩盖潜在问题的越界索引修复,系统安全失败比导致静默错误要好。如果AI生成的推荐系统可能出错,提供用户纠正或覆盖的方式显示了对其人类自主权的尊重。努力构建在AI组件出现问题时能够优雅降级的系统。
在团队内部记录AI使用决策。
保留一个内部日志,记录你为什么使用某些AI建议(或没有使用):“我们尝试将AI用于模块X,但它倾向于产生过多重复代码,所以我们手动编写了那部分。”这可以帮助你改进流程,为新团队成员提供关于AI在代码库历史中角色的背景,并增强团队的集体记忆。在审计期间这也很有用。
主动避免偏见、歧视和不公平。
警惕AI使用可能导致歧视的迹象,并在这些情况发生之前努力避免。例如,如果你的应用是全球性的,你的AI是多语言的还是偏向说英语的人?你的所有团队成员是否都能平等地访问AI工具和培训?
提示和代码生成(开发者)
确认你的提示不包含机密或敏感数据,如客户信息、个人身份信息(PII)或密钥。
检查所有输出的许可证,并确认其不包含专有或GPL代码,除非获得允许。使用FOSSA等工具进行扫描检查。
测试输出的偏见,以确保代码和注释不会强化刻板印象或歧视。
通过提示安全默认值来确认安全卫生。确认代码避免不安全的模式(eval、未消毒的输入)。
在提示中指定任何约束,包括风格、框架、性能需求和兼容性指南。
代码审查检查(开发者和代码审查者)
确认在应得的情况下给予归属和致谢。
审查逻辑、语言和命名中的偏见和公平性——尤其是在面向用户/UI的层面。
确保代码不会助长伤害、滥用、操纵或歧视。
验证输入清理、数据处理和日志记录,并检查密钥泄漏。
通过单元测试、边界情况、错误处理和测试覆盖率确认代码的功能性和正确性。
检查是否存在低效或高耗能的模式。
检查依赖项以确保它们不包含未经审查的库或隐藏的许可证风险。
检查可读性和可维护性:代码应遵循风格指南并使用清晰的命名约定。
检查是否已删除所有未使用的代码。
确认代码注释解释了代码的意图,特别是对于AI生成的逻辑。
确认你的代码审查反馈是尊重的、具体的和有同理心的。
治理和流程(组织)
确认已部署集成的许可证扫描器、审计日志和来源跟踪。
提供伦理和AI辅助编码培训,并定期分享更新。
维护经过审查的AI工具列表;禁止未经批准或高风险的工具。
建立事件响应流程,为发现不道德代码的任何人提供升级渠道和举报选项。
监控负责任的AI指标,如偏见事件、安全发现和许可证违规。维护这些指标的检查清单并定期修订。
征求并倾听社区反馈。通过回顾会议或外部审计纳入多样化的观点。
自定义此列表以包含特定于你的组织和业务领域的问题,以及你团队的技术、风险承受能力和价值观。
从小处开始:从关键问题开始,如”我们是否避免了敏感数据?“和”我们是否扫描了许可证?”
通过PR模板、CI流水线(CI pipeline)和代码审查工具将检查和检查清单集成到你的工作流程中。
每季度或在重大事件后安排对此检查清单的审查。使用这些审查来迭代列表,添加新项目或删除不需要的项目。
不要将此检查清单视为严格的规则手册,而是作为对话的起点,就像飞行员和外科医生使用他们的检查清单一样。
随着AI领域的持续变化和发展,软件行业可能会引入AI标准或认证。虽然还处于早期阶段,但你的公司甚至可以通过参与标准化工作来帮助制定这些指南,比如IEEE或ISO关于AI软件工程的工作组。从伦理角度来看,由开发社区帮助制定规则比完全交给监管机构或法院更好。
负责任的氛围编码(vibe coding)意味着以尊重所有利益相关者的方式将AI集成到软件开发生命周期中:原创作者(通过尊重他们的知识产权(IP))、同事(通过透明度和公平性)、用户(通过隐私、安全和结果的公平性)以及社会(通过防止滥用造成伤害)。这是关于利用AI的优势,同时勤勉地防范其弱点。
我经常说氛围编码不是低质量工作的借口。它也不是伦理捷径的借口。作为负责的人类,开发人员必须确保速度不会损害价值观。
接下来,第10章将探讨一项正在改变我们与AI模型工作方式的新技术:自主编码代理(autonomous coding agent)。
[1] 案件信息通常可以在法庭文档上找到,如美国加利福尼亚北区地方法院和第九巡回上诉法院的文档,或通过法律新闻媒体和案件跟踪器找到。
[2] 存在一些伦理上合理的例外。渗透测试人员和安全研究人员可以在负责任的披露协议下,合乎道德地使用AI来查找应该修复的漏洞。
自主后台编码代理正在迅速成为AI编码工具的下一个演进。与你在输入时提供代码建议的熟悉”副驾驶”助手不同,这些代理的运作更像是你可以派遣去异步处理整个任务的后台初级开发人员。代码在为代理启动的隔离环境中生成,可以运行测试,结果通常作为一个完整的拉取请求(pull request)返回供你审查。
在本节中,我将探讨后台编码代理(background coding agents)是什么、它们如何工作、当前的工具格局(OpenAI Codex、Google Jules、Cursor、Devin等),以及它们与传统IDE内助手的比较。我还将研究它们的能力、局限性,以及它们对软件工程未来所预示的实际变化。
传统的AI编码助手(如Cursor、GitHub Copilot或VSCode扩展如Cline)是监督式编码代理(supervised coding agents)——它们是交互式助手,响应开发者的提示或内联上下文。它们本质上是增强版的自动补全,在聊天中或在你编写代码时生成建议,但人类开发者仍处于主导地位,指导每一步。
相比之下,自主后台编码代理(autonomous background coding agents)具有更大的独立性。你给它们一个高层次的任务或目标,然后”让它们去工作”,让它们自己解决问题,无需持续监督。这些代理会读取和修改你的代码库,制定计划,执行代码(甚至运行测试或命令),并产生结果(通常是提交或拉取请求)——所有这些都在异步工作流中完成。
可以这样理解副驾驶和自动驾驶之间的区别:你的副驾驶(就像GitHub Copilot)始终在驾驶舱中陪伴你,等待你的输入;而自动驾驶(后台代理)可以独立驾驶飞机一段时间。这种自主性意味着后台代理可以处理多步骤的编码任务,而你可以专注于其他事情。使用像Codex和Jules这样的异步代理(async agents)就像扩展了你的认知带宽:你可以向AI发出任务,然后忘记它,直到完成为止。你不再是与AI进行单线程的来回交流,而是突然拥有了多线程工作流:代理与你并行工作,就像一个能干的初级开发者在后台工作一样。
关键的是,后台代理在隔离的开发环境(通常是云虚拟机或容器)中运行,而不是直接在你的编辑器中。它们通常会将你的代码库克隆到沙箱中,安装依赖项(dependencies),并拥有构建和测试项目所需的工具。为了安全起见,这些沙箱是受限的(比如”除非明确允许,否则不能访问互联网”)且是临时的。代理可以运行编译器、测试、代码检查工具等,而不会对你的本地机器造成任何风险。当任务完成时,代理会输出代码变更(差异)和它所做工作的摘要。通常这会以拉取请求的形式呈现(包含代码差异、提交消息,有时还有解释),你可以审查并合并。
总而言之,后台编码代理是一个由AI驱动的自主编码器,它理解你的意图,在沙箱环境中通过读写代码和测试来完成整个任务,然后交付结果供你审查。它不仅仅是建议一两行代码——它可以处理更大范围的任务:
在整个代码库中编写新功能X。
重构模块Y以提高效率。
升级该项目的依赖项。
这是我们将AI融入开发工作流方式的重大转变,从辅助性建议转向委托实际的实现工作。
在底层,大多数后台代理遵循类似的操作模式:规划(plan)、执行(execute)、验证(verify)和报告(report)。让我们逐步了解这些步骤及其能力。
当你给代理一个任务(通常通过描述你想要什么的提示或命令)时,代理首先解析请求并制定攻击计划。一些代理在开始之前会明确向你展示这个计划。例如,Google的Jules会呈现一个执行计划,你可以在它开始编码之前审查和调整,这”可以防止你担心代理是否正确理解了你的请求”。一个好的代理会将任务分解为子步骤:
步骤1:搜索代码库中的相关部分;步骤2:在文件A、B、C中进行更改;步骤3:运行测试;步骤4:提交更改。
这个规划阶段是有效自主性的关键:这是AI在深入研究之前,对如何完成你的目标进行推理的方式。
该代理为任务启动一个专用开发环境。例如,Jules “将您的代码库克隆到安全的 Google Cloud 虚拟机中”,并在那里异步工作。OpenAI 的 Codex 类似地在自己的云沙箱中运行每个任务,预先加载您的存储库。Cursor IDE 的后台代理等工具使用具有互联网访问权限的远程 Ubuntu 机器来安装包,并可以通过 Docker 或快照进行自定义。确保环境具有所有需要的依赖项(如正确的语言运行时和构建工具)既至关重要又非常复杂。正如我在之前的分析中指出的那样,“找出一种流畅的体验来为代理启动恰到好处的环境是关键…而配置它的用户体验和 CI 管道一样令人沮丧,甚至更加令人沮丧。” 尽管如此,代理正在通过允许配置文件指定设置步骤来解决这个问题。目标是在云中创建一个开发环境,镜像人类开发人员成功运行项目代码和测试所需的环境。
值得注意的是,许多代理在设置后会禁用对其代码的互联网访问,因此可以沙箱化运行,而不会出现未经授权的数据泄露或不受限制的互联网调用。有些允许针对特定需求进行受控的互联网使用:例如,OpenAI 最近为 Codex 任务启用了可选的互联网访问,例如获取包更新或文档。
接下来是主要内容:代理根据计划开始编写和修改代码。配备了为编码微调的大型语言模型(或模型组合),它可以读取多个文件、生成新代码,甚至在需要时创建新文件。这是代理本质上像程序员一样行事的地方:定位应该在哪里进行更改、编辑代码以及插入新逻辑。
早期运行中的一个有趣观察是,代理经常使用暴力文本搜索(如 Unix 的
grep
命令)来查找代码库的相关部分。例如,代理可能搜索函数名或关键字以找出在存储库中的何处进行更改。这似乎出奇地简单——它们不应该使用高级语义代码搜索或基于
AST 的分析吗?然而,它是有效且可靠的。正如 Birgitta Böckeler
所指出的,许多编码代理默认使用直接的全文搜索,尽管存在更先进的技术,但可能发现它是最广泛有效的方法。
当代理编辑代码时,一些系统会提供实时日志或状态更新,以便您可以根据需要跟进。OpenAI Codex 在处理任务时公开代理的”思考”和命令(摘要)日志。Cursor 允许您”查看它们的状态并进入代理正在运行的机器”以观察甚至在任务中途进行干预。但实际上,这个想法是您不需要照看——您可以让代理在自动驾驶模式下运行。
这些代理的一个决定性能力是它们不会止步于编写代码——它们通常会编译代码并运行测试来验证它们的更改。例如,OpenAI 的 Codex 被设计为迭代运行测试,直到收到通过结果。如果代理可以运行项目的测试套件(或至少是测试的相关子集),它可以捕获错误并在后续迭代中自动纠正它们。这是巨大的:它将 AI 从仅仅生成代码转变为也调试和验证其代码。
理论上,具有强大测试工具的代理可以尝试修复、看到测试失败、调整代码,并循环直到测试通过——无需人工干预。在实践中,环境问题有时会阻碍这一点。在我研究的一个案例中,Codex 由于环境不匹配(缺少某些工具)而无法运行完整的测试套件,导致提交的拉取请求仍有两个失败的测试。如果环境完全对齐,代理本可以在发起 PR 之前修复这些琐碎的问题。
这强调了为什么环境设置对自主代理如此重要:如果它们可以运行开发人员会运行的所有内容(linter、测试、构建),它们可以自动纠正许多错误。像 Devin 这样的代理强调这个循环——Devin “编写代码、在代码中发现错误、纠正代码,并运行自己的端到端测试以验证其工作”,这是其操作的正常部分。事实上,Devin 甚至会启动它构建的前端应用程序的实时预览部署,以便您可以在浏览器中手动验证功能,这是验证步骤的巧妙扩展。
一旦智能体(agent)有了候选解决方案(所有测试都通过了,或者它认为代码已经准备好),它就会为你准备结果。根据平台的不同,这可能是 GitHub 上的 PR、聊天中的差异和解释,或者准备合并的文件。
此时,作为人类的你需要进行审查。这里我们回到”信任但验证”的原则:你信任智能体能产生有用的东西,但你通过代码审查和额外测试来验证这些更改。许多智能体系统明确集成了 PR 审查流程,因为这是开发人员熟悉的工作流。例如,Jules 会接入你的 GitHub,并为其更改创建一个分支和 PR。OpenAI 的 Codex 在 ChatGPT 中呈现差异,供你批准或提出后续问题。如果你发现问题或有变更请求,通常可以将反馈提供给智能体进行下一次迭代。
一些智能体通过聊天处理这个过程(Devin 可以从关联的 Slack 线程接收反馈:如果你指出问题或要求调整,它会”开始着手回复”来解决问题)。其他智能体可能需要使用调整后的提示重新运行,或使用审查评论界面。令人印象深刻的是,Devin 甚至会回应 GitHub PR 评论,询问它为什么做出某些更改——它会用”眼睛”表情符号表示看到了评论,然后发布对其推理的详细解释。(在那个案例中,解释并不完全正确,但它能够讨论 PR 这一事实说明了这些智能体可以变得多么具有交互性。)
如果一切看起来都很好,你就合并智能体的 PR 或集成这些更改。如果不好,你可能会放弃它或让智能体再试一次。团队面临的一个实际问题是,如果智能体的输出几乎不错但不完全正确,该怎么办。你是否要花时间修复智能体生成的补丁的最后 10%-20%,即使这是一个你外包给 AI 的低优先级任务?这就是我所说的 AI 贡献的”沉没成本”困境。Birgitta Böckeler 思考道,如果一个智能体 PR 只是部分成功,团队将不得不决定”在哪些情况下会放弃拉取请求,在哪些情况下会投入时间让它完成最后 20%“,而这个任务最初并不值得花费太多开发时间。没有唯一的答案——这取决于更改的上下文和价值——但这是自主智能体引入的一种新的权衡。
总之,后台编程智能体处理编程任务的端到端循环:理解 → 规划 → 编码 → 测试 → 交付。它们本质上模拟了一个勤奋、有条理的开发人员在分配任务时可能会做的事情,尽管是在当前 AI 的限制范围内(见图 10-1)。
值得在我们使用了几年的编程 AI 工具(GitHub Copilot、ChatGPT 编程模式等)和这一代新的自主智能体之间划清界限。两者都很有用,但它们扮演不同的角色,有不同的优势和劣势。
最明显的区别是它们的自主程度。IDE 内助手如 Copilot 或 VSCode 的 AI 扩展与你同步工作——它们在被调用时生成建议或回答问题,它们的范围通常限于即时上下文(如你正在编辑的文件或函数,或你给出的特定提示)。你决定何时接受建议、请求另一个建议或应用更改。
使用后台代理(background agents)时,一旦你对某个任务点击”开始”,代理将自主执行数百个可能的操作(文件编辑、运行、搜索),而无需进一步确认。它以异步方式运行。这需要更高程度的信任(你让它自行更改内容),但也使你摆脱了微观管理。我经常将其描述为拥有AI结对程序员与AI助理开发人员之间的区别。结对程序员(Copilot)与你一起逐个按键工作;而助理开发人员(Codex/Jules等)则在另一个问题上并行工作。
AI工具的copilot风格意味着它们擅长微任务—编写函数、完成一行代码、生成小代码片段、回答关于如何使用API的问题。它们不会维护长篇叙述或项目级的理解,除非是编辑器中打开的文件或有限的窗口内容。
自主代理在项目级别运行。它们加载你的整个代码仓库(或至少对其进行索引),并可以跨多个模块进行协调更改。它们跟踪多步骤计划。例如,GitHub Copilot可能在你提示时帮助你编写单元测试,但后台代理可以自主决定在一个文件中添加相应的实现,在另一个文件中添加测试,在第三个文件中修改配置—所有这些都作为一个统一任务的一部分。这使得代理更适合处理诸如重构横切关注点(logging、错误处理)、执行升级(通常涉及多个文件)或实现涉及后端和前端的功能等任务。IDE助手无法轻松处理这些,因为它们缺乏长期任务记忆和全仓库可见性。
Copilot风格的助手是被动的—它们响应你的代码或查询。它们不会主动发起操作。后台代理是主动的,一旦激活,它们将主动采取行动以达到目标。Jules或Devin代理可能会决定”我需要在这里创建一个新文件”或”让我现在运行测试”,而无需在每一步都被明确告知。它们还可以主动通知你事情,例如:
我在另一个地方找到了应用此更改的位置,所以我也会包含它。
它们的行为更像一名员工,可能会说:“我在代码中注意到X,所以我也修复了它。”也就是说,自主性也意味着它们可能会做一些你没有预料到或不一定想要的事情。这种工具的监督性质意味着它只会执行你接受的操作(除了可能存在你没注意到的细微错误建议)。因此,强大的能力(主动性)伴随着更大监督的需求。
一个主要区别是后台代理可以执行代码和命令,而传统的IDE助手通常不能(除非你算上ChatGPT的代码解释器模式之类的功能,但那更多用于数据分析,而不是与你的项目构建集成)。
代理会运行你的测试套件、启动开发服务器、编译应用程序,甚至可能部署它。它们在沙箱中运行,但实际上就像拥有一个可以使用终端的自动化开发人员。这改变了游戏规则—它完成了验证/修复的循环。IDE助手可能生成看起来合理的代码,但如果它实际上没有运行它,可能会存在运行时问题或测试失败。
使用运行代码的代理,你有更大的机会获得实际可用的输出。它还卸载了调试步骤;如果出现故障,代理可以立即尝试修复它。反过来说,这要求代理的环境正确(如前所述),并且为潜在的副作用打开了大门。想象一下代理运行数据库迁移或修改数据—通常它们处于沙箱模式,所以这不会影响生产环境,但要小心。
GitHub Copilot 和类似工具运行在编辑器中,这对于流畅的编码体验非常有用。代理(Agent)通常还会与项目管理和 DevOps 工具集成。例如,你可以创建一个 GitHub issue,让代理接手并生成 PR,或者从 CI 流水线触发代理运行来完成特定任务(如自动修复 PR 上的 lint 错误)。事实上,CodeGen 宣传其代理能够连接到问题跟踪器,当问题状态变为”进行中”时,AI 代理就会开始处理它。这种集成超出了 IDE 工具的范围。这暗示 AI 代理可以成为 CI/CD 循环的一部分——例如,自动尝试修复构建失败或自动为小问题创建后续 PR。这是一种不同的协作模式:不仅仅是帮助开发者编写代码,而是作为团队工具链中的机器人用户。
使用 copilot 类型的助手通常仍然像编程,只是更快——你输入,它们建议,你接受,你测试。使用后台代理更像是委托后再审查。人工工作从编写代码转向编写好的任务描述,然后审查生成的代码。我将此称为”生成者与审查者不对称”(generator versus reviewer asymmetry)——从零开始生成解决方案(或代码)很难,但审查和改进它更容易。异步代理利用了这一点:它们处理大量生成工作,留给你(通常更快的)审查和调整工作。这可以大幅提升生产力,但也意味着作为工程师,你需要提升代码审查和验证技能。
代码审查一直很重要,但现在不仅仅是审查其他人类同事的代码——还包括 AI 生成的代码,这些代码可能有不同的错误模式。我的原则是:你应该把代理生成的代码当作一个稍微过于热心的初级开发者写的:假设意图良好且能力尚可,但要验证一切,如果不符合标准,不要犹豫请求修改或拒绝。
在实践中,我发现我会同时使用 copilot 风格的工具和后台代理。例如,当我主动编写复杂逻辑时,我可能会使用 Copilot 或 Cursor 的内联建议,因为我想对该逻辑保持严格控制。同时,我可能会将外围但耗时的任务(如为新端点更新所有 API 客户端库)委托给后台代理并行处理。它们填补不同的需求。一个不一定取代另一个。事实上,我预见 IDE 将提供统一体验:一系列选项,从”完成这一行”到”生成一个函数”到”嘿,AI,请帮我实现整个工单”。你可以根据范围选择工具。
到目前为止,我经常把”AI”称为一个单一的助手。实际上,有许多 AI 模型,各自有不同的优势。有些擅长自然语言理解,其他擅长生成代码,还有些可能专注于特定领域(如数学问题求解器或 UI 生成器)。氛围编码(vibe coding)的高级实践者可以协调多个 AI 一起工作,让每个 AI 做它最擅长的事。这就像拥有一个专家团队,而不是单个通才。
考虑一个未来的工作流程,你拥有:
一个在编程方面高度训练的 CodeGen AI,可以高效地生成和修复代码
一个专门生成测试用例和发现边缘情况的 TestGen AI
一个编写清晰文档和解释的 Doc AI
一个擅长生成 UI 布局或图形的 Design AI
一个专注于性能调优甚至了解底层细节的 Optimization AI
你可以通过这些 AI 来传递你的任务。例如,你让 CodeGen AI 编写实现。然后立即将输出提供给 TestGen AI 来为其生成测试(或进行批评)。然后将代码和测试都提供给 Doc AI 来生成文档或使用指南。如果代码涉及用户界面,也许 Design AI 会更早使用来提出布局结构,然后 CodeGen AI 再实现它。通过链接它们,你利用每个模型的领域专长。这类似于软件流水线或装配线,但不是不同的人类角色,而是不同的 AI 角色。
即使在相似的模型之间,组合它们也能提高可靠性。如果你有两个来自不同提供商或不同架构的代码生成模型,你可以让它们都尝试解决方案,然后比较或测试两个输出。如果一个模型的输出通过了所有测试而另一个没有,你就选择通过的那个。如果两个都通过但采用了不同的方法,你可以手动选择更易读的那个。如果一个失败了,你甚至可以向失败的模型展示成功的代码作为学习提示。这种AI交叉对话可以减少错误,因为两个不同的模型不太可能犯完全相同的错误。这就像获得第二意见。你已经可以找到使用一个AI检查另一个AI推理的研究和工具——例如,一个生成答案,另一个评判它。
使用正确的工具来完成工作。大型语言模型(LLMs)是很好的通才,但有时较小的专用模型或工具表现更好。例如,对于算术或某些算法,确定性工具(或更受约束的AI)可能更好。一些高级开发设置将符号求解器或旧的基于规则的AI用于特定子任务,而将LLMs用于其他任务。作为高级vibe程序员,你可能会维护一个工具箱:当你需要正则表达式时,你调用一个专门的正则表达式生成器;当你需要提交消息时,可能使用一个针对摘要进行微调的模型。优点在于这些可以通过简单的脚本或提示包装器集成。例如,你可以有一个本地脚本如ai_regex_generator,它在内部提示AI,但带有一些预处理和后处理来确保输出是有效的正则表达式,并可能在提供的示例上测试它。
如果你发现自己经常组合模型,你可能会使用或构建一个编排系统(orchestration system),这是一个新兴的框架类别,通常被称为AI编排或代理(agents)。这些系统允许你定义一个流程;例如:
步骤1:使用模型A解释用户请求。
步骤2:如果请求是关于数据分析的,使用模型B生成SQL;如果是关于文本的,使用模型C…
步骤3:将结果提供给模型D进行解释。
如果你正在构建由多个AI步骤驱动的应用或服务,这会更相关。但即使在个人开发中,你也可以编写多步骤方法的脚本。例如,一个自定义CLI工具ai_dev_assist,接受一个提示,在幕后使用AI将提示分类为code、design、test和optimize等类别。根据类别,它将提示转发给相应的专业AI。当它收到结果时,可以选择将结果传输到另一个AI进行审查或改进。
这种协调其他AI的元AI听起来很复杂,但高级用户可以使用现有技术设置它。随着我们开始看到IDE或云平台的专门支持,这可能会变得更容易。
在谈论多种智能的主题时,我们不要忘记人类协作者。高级vibe程序员也知道何时让其他人类开发者参与进来。例如,你可以使用AI为一个功能生成两到三个不同的设计原型,然后将这些原型提交给团队的UX设计师征求反馈。哪一个符合我们的品牌?哪一个感觉直观?如果AI编写了一段复杂的代码,你可能会与同事进行代码审查会议,重点关注那段代码,承认”AI帮助编写了这个,所以我也想要另一双人眼来审查它”。从某种意义上说,“多模型”方法可以将人类也包括在内,作为高度先进的模型——每个实体(人类或AI)都有独特的优势。未来的开发可能经常是人类+AI结对编程,甚至是团队编程,其中一些”团队成员”是AI。
想象一下通过vibe编码构建一个小型Web应用程序。你的工作流程可能如下所示:
你使用UI布局AI根据描述生成页面的HTML/CSS(专门用于前端)。
你使用内容AI生成所需的一些占位符文本或图像(如营销文本,可能使用面向文案写作的模型)。
然后你使用主要的代码AI生成JavaScript中的交互功能,向它提供HTML,以便它知道要连接到哪些元素ID。
然后你要求测试AI为界面交互生成Selenium或Playwright测试。
最后,你使用安全 AI 扫描代码以检查常见漏洞。这可以是一个模型,也可以是一个由 AI 增强的静态分析工具。
这种多模型方法在一个集成流程中涵盖了前端、后端(如果有的话)、内容、测试和安全。每个 AI 处理其各自的部分,而你作为协调者,确保它们全部保持一致。
虽然今天你可能需要手动将一个工具的输出复制到另一个工具,或使用一些粘合脚本,但未来的 IDE 可能会让你配置这个流水线,使其感觉无缝衔接。关键要点是:如果你可以访问多个 AI 模型,不要只依赖一个。针对每项工作使用最好的模型,并让它们协同工作。这会带来更好的结果,也减少了单点故障——如果一个模型不擅长某些事情,另一个可能会弥补这个弱点。
组合 AI 模型是一个高级操作,但它是专业化原则的逻辑延伸,这一原则在软件工程中众所周知(想想微服务,每个服务做好一件事)。在这里,每个 AI 服务做好一件事。作为 vibe coder(氛围编码者),你的角色扩展为 AI 指挥家,而不仅仅是 AI 提示者。这需要更多的设置和思考,但回报是一个 AI 协作者的交响乐,每个都为高质量的最终产品做出贡献。
现在你知道它们如何工作了,让我们来看看一些领先的例子,看看它们如何表现。
在我写这篇文章的 2025 年,自主编码代理领域在过去一年中迅速发展,不同平台上出现了独特的方法。这些工具代表了从被动完成代码到作为能够独立执行复杂任务的主动开发伙伴的转变。
OpenAI 的 Codex 体现了基于云的代理方法,通过 ChatGPT 的界面或开源 CLI 运行。它启动隔离的沙箱来并行执行编码任务,处理从 React 升级到单元测试创建的所有事情。Codex 的独特之处在于它在真实编码任务上的强化学习训练,使其能够遵循最佳实践,例如迭代运行测试直到通过。虽然结果可能在运行之间有所不同,但 Codex 通常会收敛到对于边界明确的任务的可行解决方案。它的优势在于在类似 CI 的环境中实际执行代码,代表了第一波真正与开发流水线”配对”的代理。
Google Jules 采用不同的方法,深度集成 GitHub 工作流。Jules 在 Google Cloud VM 上运行,具有完整的仓库克隆,强调可见的、结构化的规划——展示其推理过程并允许在执行前修改计划。这种”先规划,再执行”的理念,结合实时反馈能力,将 Jules 定位为受监督的助手而不是黑盒自动化。它的 GitHub 原生设计意味着它直接在团队工作的地方运行,创建分支和 PR 而无需切换上下文。该代理甚至尝试了新颖的功能,如音频变更日志,指向更易访问的代码审查流程。
Cursor 的后台代理代表了以 IDE 为中心的方法,直接从编辑器启动但在远程机器上执行。这种混合模型让开发者从他们的指挥中心编排多个 AI 工作者,同时保持本地控制。Cursor 提供具有可定制环境的 Ubuntu 实例(通过 environment.json 或 Dockerfiles),为代理提供完整的互联网访问和软件包安装能力。关键创新是无缝的 IDE 集成:开发者可以监控代理进度,在需要时进行干预,并在完成时立即在本地访问更改。这种方法模糊了本地 AI 辅助和云执行能力之间的界限。
团队集成代理:Devin
Devin 将自己定位为”AI队友”而非仅仅是一个工具,它与Slack、GitHub和Jira等问题跟踪器集成。由Cognition Labs构建,它使用专为长期推理和多步骤执行调优的定制AI模型。Devin擅长并行执行小型维护任务,如错误修复、测试添加和代码检查清理等经常被降低优先级的工作。其协作设计包括状态更新、澄清请求,甚至自动预览部署。虽然它能很好地处理简单任务,但复杂问题仍可能需要大量人工干预,这凸显了当前自主编码的边界。
该领域正在快速扩张,老牌企业和初创公司都在竞相定义这一类别。微软已暗示将推出”Copilot++“,从内联建议转向代理能力。CodeGen等初创公司(使用Anthropic的Claude)向企业承诺提供”永不休息的软件工程师”。与此同时,开源项目和学术研究继续推动边界,探索如何使代码生成更加可靠和符合上下文。
这种激增表明我们正在见证一种新的开发范式的诞生,单个开发者可以编排多个AI代理,每个代理专门负责软件生命周期的不同方面。出现的关键差异点包括:
虽然自主编码代理继承了AI辅助开发的基础性挑战,正如本书中讨论的那样——特别是第3章探讨的70%问题——但它们的自主特性引入了值得单独研究的独特复杂性:
与人类在每一步都进行干预的交互式AI辅助不同,自主代理会做出一连串决策,这些决策可能以独特的方式复合错误。当代理误解初始需求时,它不仅仅生成一个有缺陷的函数:它会基于这种误解构建整个实现架构。每个后续决策都会强化最初的错误,创造出我所说的”连贯的错误性”(coherent incorrectness):代码内部一致但从根本上与实际需求不符。
这种顺序决策特别挑战处理多文件更改的代理。实现新功能的代理可能正确修改了后端API,但随后通过前端、数据库模式和测试套件传播了错误的假设。当你审查完整的拉取请求时,解开这些相互关联的错误往往比传统AI辅助可能实现的交互式、增量式纠正需要更多努力。
虽然第8章讨论了一般的环境配置挑战,但自主代理从其沙箱执行模型中面临独特的复杂性。每次代理运行都需要启动一个精确镜像你开发设置的隔离环境——这是一个扩展性很差的挑战。当你并发运行多个代理时,即使是环境中的轻微变化也可能导致截然不同的结果。
考虑这样一个场景:五个代理同时处理不同的功能。代理A的容器中可能有一个稍旧的Node版本,代理B可能缺少特定的系统库,代理C可能有不同的时区设置。这些在执行期间不可见的变化,在你开始集成它们的工作时会表现为微妙的错误。这种代理沙箱之间的”环境漂移”(environmental drift)代表了单开发者工作流中不存在的新一类集成挑战。
自主代理承诺并行开发,但这引入了与人类团队动态截然不同的协调挑战。当多个代理修改重叠的代码部分时,它们缺乏人类使用的隐式沟通渠道——没有快速的Slack消息询问”你在处理认证模块吗?“或对同事正在做什么的非正式感知。
这创造了我所说的异步协调悖论(async coordination paradox):你并行运行的代理越多以提高生产力,集成它们就变得越复杂。与通过站会和非正式沟通自然协调的人类开发者不同,代理是孤立运行的。你可能会发现代理A重构了一个实用函数,而代理B正忙于向旧版本添加新调用,创造了如果代理具有人类开发者对彼此工作的自然感知就不会发生的冲突。
虽然代码审查(code review)对所有AI生成的代码仍然至关重要(如前面章节所讨论的),但自主代理(autonomous agent)通过庞大的数量和时机放大了这一挑战。与交互式AI辅助不同——代码在你工作时逐步到来——代理生成的PR以完整的实现形式出现,通常是在通宵运行后同时到达的多个PR。
这创造了一种不同于审查人类PR时的认知过载。对于人类贡献,你通常可以依赖提交消息和PR描述来反映编码者的实际思维过程。然而,代理PR要求你从代码本身反向推导代理的”推理”。当五个代理在周一早上各自提交500行或更多代码的PR时,审查负担从协作质量检查转变为更像是考古探险。
也许最重要的是,自主代理以交互式AI工具所没有的方式挑战我们的信任模型。当你将任务委托给代理并离开时,你在对可接受的风险做出隐含的赌注。这与有监督的AI辅助根本不同,后者让你保持每时每刻的控制。
考虑代理技术的安全影响。具有仓库写入权限和执行能力的自主代理呈现出独特的攻击面。被攻破或误导的代理不仅仅是建议糟糕的代码,它会主动提交代码,甚至可能部署它。我们对代理的沙箱化和访问控制必须比基于建议的工具更加复杂(在第8章中介绍)。
随着团队扩大其代理使用规模,正在出现传统AI辅助所不存在的新组织模式。当请求的开发人员生病时,谁”拥有”代理生成的代码?你如何跨团队跟踪代理资源使用?当代理的一个月长的重构项目与紧急功能开发冲突时会发生什么?
这些不是技术限制,而是组织挑战,并且它们是自主系统独有的。它们需要新的角色(代理协调员?)、新的流程(代理影响评估?)以及新的工具(代理集群管理?),这些超出了本书在早期章节中讨论的个体开发者考虑因素。
这些代理的自主性——它们独立工作、做出连续决策和大规模操作的能力——将它们从生产力工具转变为类似团队成员的东西。这种转变不仅需要本书中讨论的技术实践,还需要全新的协调、信任和集成框架,而我们才刚刚开始理解这些。
虽然许多通用AI开发实践适用于自主编码代理,但基于代理的开发的某些方面需要特别考虑。基于Codex、Jules、Devin和Cursor的后台代理等工具的集体经验,这些实践解决了将整个开发任务委托给独立运行的AI系统的独特挑战。
AI助手和自主代理之间的根本区别在于它们的范围和独立性。代理擅长具有明确成功标准的、定义明确的、封装良好的任务——特别是那些涉及并行执行许多小任务的任务。理想的代理分配包括全面的测试覆盖率改进、系统化的依赖项更新、批量重构操作以及跨多个组件的标准化功能实现。
考虑要求AI助手帮助编写单个测试与要求代理在整个模块中实现80%测试覆盖率之间的区别。代理可以有条不紊地遍历每个未测试的函数,生成适当的测试用例,运行它们以验证正确性,并迭代直到达到覆盖率目标。这种系统化、可衡量的工作是自主代理的最佳选择。
相反,需要做出重大架构决策、解释复杂利益相关者需求或设计新颖算法的任务仍然更适合由人类主导的开发与AI辅助。关键在于识别较大任务的哪些方面可以有效地委托给代理,哪些需要人类的判断和创造力。
现代自主智能体(autonomous agents)通过复杂的规划和执行透明度功能来区分自己,这些功能需要主动参与。当Jules在开始工作之前展示其执行计划,或者当Cursor显示智能体活动的实时日志时,这些代表了基于智能体开发所特有的关键干预点。
规划阶段是你的主要质量关卡。审查提议的计划不仅要看正确性,还要看效率以及与代码库约定的一致性。如果Jules计划更新Next.js应用程序但遗漏了关键的webpack配置更改,在规划期间发现这一点可以防止后期的大量返工。这种主动审查与被动代码审查有本质区别,代表了开发者工具包中的一项新技能。
运行时监控提供了另一层特定于智能体的监督。虽然你不需要观察每一个操作,但定期检查可以防止智能体追求低效的解决方案或进行不必要的广泛更改。Cursor能够在任务执行中途”进入”智能体的环境,这体现了现代工具如何支持干预而不完全放弃自主工作流。为了最大化效率,你需要学会何时介入,何时让智能体自我纠正。
与传统开发不同,传统开发中单个开发者一次只做一项任务,而智能体实现了真正的并行开发。这种能力需要新的协调策略。当同时运行多个智能体时——也许一个在更新依赖项,而另一个在添加日志基础设施——你必须考虑它们工作之间的潜在冲突和依赖关系。
为每个智能体的范围建立明确的边界,以最小化合并冲突。尽可能将智能体分配到应用程序的不同模块或层。考虑集成顺序:添加新功能的智能体可能需要等待另一个智能体的基础设施改进完成。这种编排更像是管理分布式团队,而不是传统的单人开发。
自主智能体的引入从根本上改变了团队动态和审查流程。与审查同事精心制作的PR不同,智能体生成的PR可能包含技术上正确但风格不一致的代码。团队必须制定新的审查实践来应对这种差异。
考虑建立特定于智能体的审查清单,不仅强调正确性,还强调与团队约定和架构模式的一致性。记录你在与智能体合作时发现的常见怪癖:也许你选择的智能体始终使用某些反模式(antipatterns)或错过特定的优化机会。这种组织知识帮助审查者快速识别和解决反复出现的问题。
也许最重要的是,自主智能体实现了一种新形式的迭代开发,其中反馈循环超越了单纯的代码审查。当智能体的拉取请求需要改进时,你通常可以将其发回并要求根据具体指导进行另一次迭代。这与传统开发不同,在传统开发中,将工作退回给人类同事会带来社交和时间成本。
努力开发与你选择的智能体配合良好的提示模式(prompting patterns)。当你发现能够持续产生高质量结果的成功提示公式时,将其记录下来。为常见任务类型创建模板,包括所有必要的上下文和约束。这是一种专门针对智能体的提示工程(prompt engineering),它考虑了智能体的规划、执行和修订周期,代表了一项不同于一般AI交互的独特技能。
目标保持不变:高效地交付高质量软件。自主智能体只是提供了实现这一目标的新工具,你应该深思熟虑地将其集成到现有实践中,而不是全盘替换已建立的方法。通过理解这些智能体并利用它们的独特能力,同时保持严格的质量标准,团队可以在不牺牲代码质量或架构完整性的情况下实现显著的生产力提升。
总结一下,我想呼应[第4章]中的一个观点:AI不会取代开发者,但能够有效使用AI的开发者很可能会取代那些不能的人。自主编码代理(autonomous coding agents)的出现是朝这个方向迈出的一大步——那些学会驾驭这些”无头同事”的人将能够用更少的时间完成更多的工作。对于软件工程师来说,这是一个激动人心的时代,只要我们适应并继续保持高标准。工具可能在变化,但目标依然不变:构建可靠、高效和创新的软件。有了AI代理在我们身边(或在后台),我们有了实现这些目标的新方式——也许还能在机器人们通宵工作时好好睡一觉。
接下来,本书的最后一章将更广泛地展望AI在编程中的未来,包括代理式AI的未来。
Vibe编程可能始于AI根据我们的提示生成代码,但其影响远远超出了编写代码本身。随着AI技术的进步,它们有望改变软件开发生命周期的每一个方面。在本章中,我将以推测但有根据的方式展望AI在软件中的角色如何在未来扩展。我将探讨AI驱动的测试、调试和维护;AI如何影响软件设计和用户体验个性化;AI辅助下项目管理的演进;甚至编程语言本身的未来。目标是想象一个未来,在那里AI不仅仅是代码生成器,而是软件工程的全方位参与者——同时将讨论建立在基本原则之上,使其在特定技术不断变化的情况下仍然保持相关性。
想象一下未来的开发环境,当你编写一个函数时(无论是手工编写还是通过vibe编程),AI工具会立即为它编写一套单元测试,发现潜在的bug,甚至可能修复它们——所有这些都在几秒钟内完成。这种场景正在迅速变得可行。让我们分解一下AI在质量保证和维护方面的潜力(以及已经出现的)贡献。
编写全面的测试既耗时又常常因截止日期而被忽视。正如你在[第7章]中看到的,当前的AI助手可以通过自动生成测试来缓解这个问题。例如,给定一段代码,AI可以建议一组涵盖典型情况、边界情况和错误条件的单元测试。
在未来,这可能会更进一步:AI可以检查你的整个代码库,识别测试覆盖率不足的函数或模块,并生成额外的测试。它甚至可能模拟人类测试人员不会想到的输入(如模糊测试),从而可能捕获边界情况的bug。好处是以最少的手动测试编写获得更健壮的代码库。
需要注意的是,测试的质量取决于AI对规范的理解。因此,人类应该审查AI生成的测试,以确保它们符合软件的预期行为。例如,AI可能会断言某个输出在技术上是代码所做的,但也许需求是不同的——只要有人在循环中,这实际上可以帮助在代码或测试中捕获误解。
调试通常涉及繁琐的日志搜索或逐步执行代码以定位错误源。[第5章]向你展示了AI如何充当智能调试伙伴。一些当前的AI工具可以将错误消息和有问题的代码作为输入,返回解释和修复它的代码更改。
要了解AI辅助调试工作流可能如何演进,可以考虑一个监控程序执行的AI系统,当发生崩溃或异常时,它会分析堆栈跟踪和变量状态以确定可能的原因。它可能不只是给你一个错误消息,而是说:
应用程序崩溃是因为在调用getEmail()时userProfiles为null。这表明在加载用户配置文件时缺少空值检查。
此外,AI可以建议修复方案:
一个可能的解决方案是在userProfiles为null时初始化它,或者在调用getEmail()之前添加一个条件。您想让我应用这个修复吗?
未来的调试AI可以直接与运行时环境集成,实时捕获问题。它们甚至可能通过分析代码路径在问题发生之前预测问题:
如果使用y = 0调用此函数,可能会抛出除零异常;请考虑处理该情况。
这类似于静态分析,但由AI从无数代码库和错误模式中学到的知识提供支持,使其可能更有洞察力或更灵活。
随着需求随时间变化,代码会变得过时或次优。维护涉及诸如重构(在不改变行为的情况下改进代码结构)、更新依赖项和优化性能等活动。AI可以在这些领域中提供帮助:
未来的AI可以识别代码异味(如重复代码或过长函数)并自动重构它们。例如,它可能检测到你在三个地方有相似的代码块,并建议将它们抽象为单个辅助函数。或者它可以将深度嵌套的循环转换为更易读的形式。由于AI已经看过许多”好”代码的示例,它可以建议风格改进以保持代码库的整洁和可维护性。我们可能有一天会在编辑器中拥有一种模式,AI在后台持续重构代码,开发人员审查和批准更改。
未来的AI服务可能会监控你项目的依赖项(如库和框架),并自动生成拉取请求(pull request)以将它们更新到更新版本,包括适应破坏性更改所需的任何代码更改。例如,如果Web框架的新版本更改了API,AI可以将你的代码适配到新API。这将使开发人员免于阅读迁移指南和修复版本问题的重复工作。
维护通常包括随着数据规模或使用模式变化而改进性能。AI可以通过分析性能配置文件并指出低效之处来发挥作用。例如,AI可能注意到代码中某个数据库查询耗时很长,并建议添加索引或重写查询。它可能识别出代码中的循环正在进行冗余计算并提议缓存。这类似于拥有一位性能专家始终关注你的应用程序——主动检测低效之处,如冗余循环或次优数据结构。
AI可能在预发布环境中运行,模拟高负载,然后向你提供报告:“在高负载下,模块X由于Y成为瓶颈。考虑使用方法Z进行重构。”本质上,AI不仅发现问题,还教育团队更好的模式。
除了代码和逻辑之外,AI还将影响我们设计软件的方式以及用户体验软件的方式。好的软件不仅在底层是正确的;它还应该是直观的、易访问的,并让用户满意。AI将继续开发新方法来创建更好的用户界面并根据个人用户需求定制体验。
当今的AI设计工具使用生成对抗网络(generative adversarial networks)或变换器(transformers)的技术,从描述中生成UI模型。产品经理可以用自然语言描述功能:
我们需要一个移动注册界面,带有欢迎消息、有趣的插图,以及姓名、电子邮件和密码的表单。它应该使用我们的品牌颜色并具有友好的外观。
AI设计助手可以接受这个描述,并在几秒钟内生成几个候选UI设计,包括布局、占位符文本,甚至样式化组件。然后设计师或开发人员选择最接近的一个并进行完善。
未来,这些工具可以直接集成到设计软件甚至编码环境中,使”设计”和”编码UI”之间的界限变得模糊。AI可能直接输出它生成的设计的HTML/CSS或Flutter代码,使其可以立即测试。这将极大地加快设计迭代周期。你不再手工或在软件中绘制草图,而是与AI协作,AI基于最佳实践和用户认为有吸引力或可用的大量训练数据来提议设计。
即使在设计的创作过程中,AI也可以成为灵感来源。当设计师为应用程序头脑风暴配色方案或插图风格时,AI工具可以生成颜色情绪板,甚至即时创建自定义图标。例如,他们可能告诉AI:
我需要一个结合代码和音乐理念的标志。
它会生成几个将编程符号(如花括号)与音符混合的示例logo。虽然专业设计师最终可能会手工制作最终资产,但AI的建议可以激发灵感并加速探索阶段。从本质上讲,AI可以扮演快速原型制作者和创意伙伴的角色,拓宽设计师的选择范围。
需要注意的是,在设计和UX领域,人类判断至关重要。审美品味、对人类情感的理解、品牌识别——这些是AI可以近似但无法真正拥有的东西。因此,设计中的AI是增强人类创造力的工具,而不是取代它。它可以处理生成变体和处理用户数据的繁重工作,让设计师专注于同理心和创意决策。
对于开发者来说,AI驱动的设计意味着设计和开发之间的传统交接可能会变得更加流畅。开发者可以与设计师合作使用AI生成UI代码,反之亦然。这也意味着前端开发者可能会花更少的时间逐像素调整布局,而将更多时间用于确保设计与功能保持一致并以可访问的方式实现。他们还可能致力于创建个性化的钩子(hooks)——编写允许AI根据用户数据在布局A或B之间进行选择的代码,并确保两种布局都具有良好的性能和稳定性。
在vibe coding的未来,你也可以进行”vibe design”:只需描述你想要应用程序外观和感觉的氛围(vibe),AI就会帮助将其具体化。结果是一个整体的AI开发流程——不仅仅是在AI辅助下编写后端逻辑或数据库查询,而是与AI合作打造整个产品体验。
设计的另一个方面是理解用户行为。AI可以分析你应用程序的使用数据(考虑到隐私因素),突出显示用户遇到困难的地方。例如,AI可能检测到许多用户将鼠标悬停在某个图标上,期望它可以点击,但实际上不能——这表明存在UX改进的机会。或者它可能注意到来自某个特定人口统计群体的用户始终在工作流程的某个特定步骤流失,这表明该步骤对他们来说可能不够直观。
在未来,AI甚至可以模拟用户交互(使用用户行为模型),在真实用户遇到UX问题之前预测这些问题。这种”虚拟UX测试”可以在开发过程中捕获诸如过于复杂的导航或不清晰的标签等问题,此时修复它们更容易。
个性化(Personalization)已经成为一个热门词汇有一段时间了——即根据偏好或历史记录向不同用户提供不同内容的意义上。AI可以将个性化提升到新的水平,通过实时微调每个用户的软件行为和界面。例如,应用程序的AI可以了解到某个特定用户倾向于通过搜索而不是菜单来导航应用程序。然后AI可以通过为该用户使搜索栏更突出来进行调整,甚至可以根据上下文预加载它预期用户可能想要的搜索结果(就像人类助理预测老板的需求一样)。
另一种情况可以改善可访问性:如果AI检测到用户正在使用屏幕阅读器技术(因此可能存在视觉障碍),它可以自动将应用程序切换到高对比度、大字体模式,并优化屏幕阅读器标签,甚至超越静态可访问性设置可能做到的。从本质上讲,软件可以变得具有适应性(adaptive)。
想象一个电子商务网站可以即时重新排列其布局——一些用户可能会看到产品网格,而另一些用户则看到包含更多详细信息的列表,这取决于什么似乎更能吸引他们。这些变化可能是微妙且持续的,因为AI会进行实验和学习——有点像A/B测试的工作方式,但是在个人层面上并且是自主的。
软件开发不仅仅是编写代码和制作设计;它还包括规划、协调和决策——这是项目管理和团队领导的领域。AI的分析和预测能力可以极大地协助项目管理,从任务分配到风险管理和决策支持。以下是AI如何重塑我们规划和执行软件项目的方式:
管理一个团队需要了解每位开发者的优势、劣势和当前工作量,然后相应地分配任务。AI项目管理助手可以分析各种数据点——代码提交历史、专业领域(可能从开发者在代码库的哪些部分工作中获得)、甚至个人生产力模式(有些人在早上编码更有效率,其他人则在深夜)——并推荐谁应该处理新任务。
例如,如果一个新功能涉及数据库工作,而AI知道Alice成功完成了大量数据库相关任务且工作量不大,它可能会建议将任务分配给Alice。此外,AI可以通过将任务与过去的类似任务进行比较并考虑个人的工作速度来预测任务可能需要多长时间。这有助于项目经理设定更现实的时间表,避免过度负担任何单个团队成员。随着时间的推移,这样的AI可以学会像一个熟练的管理者那样平衡工作量,确保没有人闲置,也没有人不堪重负。
AI可以协助将高层目标分解为可操作的项目。你可以向AI提供一个功能请求或用户故事,它可以建议实现它所需的子任务列表。本质上,它可以生成一个草案计划或工作分解结构(work breakdown structure)。在冲刺规划(sprint planning)(在敏捷方法中),AI可以分析待办事项列表,并根据团队过去的速度,建议哪组任务适合下一个冲刺。
它甚至可以突出显示任务之间的依赖关系,确保计划在逻辑上是有序的:
任务B应该在任务A之后完成,因为它建立在该功能之上。
对于长期路线图规划,AI工具可能会模拟不同的场景:
如果我们现在优先考虑功能X,模型预测由于资源需求重叠,我们可能会将功能Y延迟2周。
拥有这些模拟和数据驱动的洞察可以帮助人类管理者在优先级方面做出明智的决策。
风险管理通常涉及预测可能出现的问题——延迟、技术障碍、集成问题——并规划缓解措施。AI非常适合模式识别,因此它可以分析历史项目数据(在公司内部或甚至整个行业,如果可用)来识别风险因素。
例如,AI可能会标记如下内容:
根据历史数据,涉及技术栈切换的项目超出时间表的可能性高30%。
在过去三个项目中,我们在集成测试阶段都有延误;这个项目也可能存在风险。
有了这些信息,管理者可以预先为这些阶段分配更多时间或资源。另一个角度是监控当前进度:AI系统可以监视任务完成率、bug发现率等,并在感知到问题时发送警报:
团队在本次冲刺中以预期速度的一半完成任务;可能存在需要关注的阻碍因素。
本质上,AI可以成为一个时刻警觉的项目审计员,在问题升级之前发现它们。
项目管理涉及许多决策——比如是否削减功能以满足截止日期,或者是否投资重构而不是添加新功能。AI无法做出这些决策,因为它们涉及业务和人为因素,但它可以提供数据来支持它们。例如,如果在讨论重构时,AI可能会报告:
如果我们重构模块Z,基于复杂度指标和团队输入,它可以将相关功能的未来开发时间减少20%。现在可能会增加两周的延迟,但在六个月后会有回报。
虽然这些数字是估计值,但拥有客观的分析有助于利益相关者更具体地权衡取舍。
另一个例子可能是决定是采用一个新库还是自主开发。AI可以扫描该库的文档、社区支持和已知问题,并总结其优缺点,为团队节省数小时的研究时间。
利益相关者或管理者有朝一日可以用自然语言向AI查询项目状态:
支付集成功能进展如何?有哪些阻碍因素?
AI在解析了工单更新、提交消息和测试结果后,可能会回答:
支付集成已完成70%。一个阻碍因素是与货币转换相关的失败测试,目前有两位开发者正在调试。如果明天解决,该功能有望在周五前完成。
这种易于访问的状态报告可以改善沟通,特别是在大型团队或有非技术利益相关者的团队中。AI基本上成为了一个无所不知的项目助手,了解所有细节并可以根据需要进行总结。
我们仍处于这项技术的早期阶段,但它发展迅速。值得思考的是长期影响以及未来几年可能如何演变。以下是对自主编码代理时代软件工程未来的展望,基于当前趋势和一些有依据的推测:
就像今天使用源代码控制或 CI/CD 已成为标准一样,让 AI 代理参与开发可能会成为常规操作。每天早上检查”AI 助手看板”,查看代理在夜间完成的任务,这可能会成为正常现象——当你登录时,代码审查已经在等着你了。今天已经有了这方面的迹象,代理在”你睡觉时”运行。工程师可能会习惯在一天结束时委派一批任务,让 AI 在第二天早上之前尝试完成。“开发者工作”的含义将发生转变:减少输入样板代码或进行常规更新,更多地专注于定义问题、集成解决方案和指导 AI。可以说,开发者将更像是 AI 开发者的产品经理——他们指定需要做什么,并确保满足需求。
目前,每个代理基本上都是独立完成你分配给它的任务。但未来可能会出现多个具有不同专长的代理协作的场景。一个代理可能擅长前端任务,另一个擅长后端任务,你给它们分配一个协调任务(或者它们甚至可能自己找出如何分工)。或者,一个代理可以并行探索多个解决方案路径——Kojo 称之为多分支探索。
想象一下,你给 AI 一个复杂的问题,它启动三个具有不同方法或架构的子任务,然后选择最好的一个——甚至询问你更喜欢哪个方向。这可能会大大减少评估不同实现所需的时间(目前这可能需要多名工程师花费数天时间进行原型设计)。当然,协调这一切并非易事,但随着代理框架变得更加先进,这并非不可能。
未来的代理可能足够智能,能够在决策点主动寻求指导。这不仅仅是推测:关于大语言模型的不确定性估计和自我反思的研究正在积极进行,早期迹象表明模型可以被训练或提示来识别何时不确定,并寻求帮助而不是盲目前进。例如,一个代理可能会遇到这样的情况:可以使用两个库来实现某些功能,它不会盲目猜测,而是暂停(就像初级开发者可能会做的那样)并询问你:
我可以使用库 A 或 B 来实现这个功能——你有偏好吗?
行业内越来越多的人相信,添加这种”智能检查点”(intelligent checkpointing)会让智能体(agent)感觉更可信,更像是协作者而不是黑盒子。这也与人类团队的工作方式非常契合——知道何时询问而不是虚张声势。虽然目前还处于早期阶段,但我们开始看到更多模型通过工具使用反思(tool-use reflection)、带有不确定性阈值的规划(planning with uncertainty thresholds)以及为运行中的用户反馈提供明确支持等技术来支持这种行为。智能检查点要求智能体了解自己的不确定性,这是一个挑战,但研究人员正在研究AI自我意识,包括置信度(confidence)。
随着我们将更多工作交给智能体,我们需要更好的方法来跟踪它们正在做什么。Kojo提出了智能体收件箱(agent inbox)的概念——一个统一的视图,显示智能体正在处理哪些任务、已完成什么以及哪些需要你的关注。这可以是一个仪表板,显示所有正在运行的智能体任务及其进度(“已完成3/5步”或”等待审查”)和结果。
取代当前日志和PR的混合方式,将会出现一个清晰的界面来管理智能体。也许IDE会有一个”智能体”侧边栏,列出活跃任务和更新动态。你可能还会收到通知:
智能体X已完成任务Y并打开了PR #123。
智能体Z需要输入才能继续。
这种基础设施对于在不失去总览的情况下扩大使用规模至关重要。毕竟,没有人希望10个沉默的机器人在没有中央控制的情况下做着谁知道是什么的事情。
我预见到一个更紧密的循环,你的跟踪器(Jira、GitHub Issues、Linear)中的问题可以由智能体从头到尾处理。事实上,CodeGen的Linear集成已经暗示了这一点。
工作流程可能如下:
PM提交一个带有规格说明的工单。
开发人员(或技术负责人)批准用于AI处理。
AI智能体接手,完成任务,并将PR附加到工单上。
人类审查并测试修复,然后关闭工单。
这可以使开发过程更加连续。类似地,CI系统可能在某些检查失败时自动调用智能体。例如,如果安全扫描发现漏洞,智能体可以尝试升级易受攻击的库或重构有风险的代码,然后打开一个带有修复的PR。或者如果代码覆盖率在PR之后降到阈值以下,智能体可以生成额外的测试来提高覆盖率。可以把它看作自动化维护(automated maintenance)。
作为一个具体例子,Dependabot目前会打开PR来更新依赖项。AI智能体不仅可以打开PR,还可以调整因更新而破坏的任何代码,运行测试,并确保一切正常——基本上是一个增强版的Dependabot。
主要的AI模型本身(GPT-4、Gemini、Claude等)将继续改进它们的代码理解和生成能力。随着它们变得更强大,那”最后30%“的差距可能会缩小。我们可能会看到几乎不会错过明显重用或边缘情况的智能体,因为模型已经在更多场景上进行了训练或具有更好的推理能力。
有了更好的模型,智能体将犯更少的错误,需要更少的监督,并可能处理更复杂的任务。话虽如此,软件本质上是复杂的,所以我怀疑总是会有一些需要人类判断的差距。也许它会变成最后5%-10%而不是最后30%。
我还预计模型将变得更高效,使那些担心数据隐私(或成本)的人能够运行本地或自托管的智能体。开源编码模型可能会迎头赶上,以至于你可以拥有一个本地部署的智能体,在许多任务上几乎和大型云智能体一样好。
我们可能会看到针对不同领域或角色的专业化编码智能体。想象一个”BugFixer”智能体,你将它指向一个失败的测试或错误日志,它就会锁定bug;一个”PerformanceGuru”智能体,专注于分析和优化热点;或者一个专门从现有代码库编写文档和代码注释的智能体。
通过专业化,智能体可以整合更多特定领域的知识或工具。我们可能会看到一个与游戏引擎集成的智能体来帮助游戏开发任务,或者一个擅长数据工程管道的智能体。一组狭义的AI专家可以与人类团队内部的专业知识分布相呼应,其中一些开发人员以前端工作闻名,另一些则以基础设施闻名。你可以拥有像DocsBot、TestBot、RefactorBot和SecurityBot这样的AI队友,每个都针对这些目的进行了调优。事实上,Cursor已经有了一个叫做BugBot的东西用于自动PR审查,这是朝这个方向迈出的一步。BugBot不编写代码;它在PR上发表评论,重点关注bug风险,就像增强版的静态分析。
如果智能体处理更多常规编码工作,开发人员的技能组合将更多地转向设计、架构和监督,如第4章所讨论的。软技能,比如清晰地传达需求(对人类和AI都是如此),变得更加重要。代码阅读和审查技能可能会变得与代码编写技能一样重要。我们可能还会更加强调测试:由于测试是验证AI输出的关键方式,擅长编写测试用例(或指导AI编写它们)仍然很有价值。
本质上,“人类的30%”将专注于软件开发中更高层次的批判性思维和质量控制方面。我怀疑我们还会看到初级开发者成长方式的变化。也许他们会从管理AI代理完成简单任务开始,而不是自己编写大量代码,这可能既有好处(他们可以快速交付价值),也有挑战(他们需要学习基础知识,而不是把AI当作拐杖)。对于那些愿意适应的人来说,这是一个令人兴奋的时代,但对于那些喜欢旧方式的人来说,可能会感到不适。正如我在[第4章]中指出的,在这个AI时代”让职业面向未来”的一个重要部分是拥抱这些工具并强调你独特的人类优势。
我们可能会看到工程团队中出现”AI管理员”或”自动化负责人”等角色——这些人特别擅长利用AI代理,围绕它们设计工作流程,并维护它们的配置。这类似于当构建系统变得复杂时出现的”构建/发布工程师”,或随着基础设施自动化发展而出现的”DevOps工程师”。同样,检查AI是否引入任何不安全模式的审计可能会成为代码审查的标准环节。
可能会更加强调测试文化以提供额外的信心:也许每个代理PR都必须包含测试(由代理或人类编写)才能被考虑合并。如果AI代理正在编写大量代码,也许人类工程师应该编写更多测试(或反之亦然)以确保独立验证。
从本质上讲,有后台编码代理的未来看起来像是开发者进行编排和验证,而AI代理执行和实现(见[图11-1])。软件工程可能会更多地变成监督一队自动化编码者,而不是手动完成每一步。这可以释放巨大的生产力,减少无聊的苦差事,甚至允许团队处理以前从未有时间处理的技术债务和维护任务。(想象一下清除所有那些小错误和不一致,因为现在你可以直接让AI处理它们!)它还可能降低新想法原型制作的门槛:你可以让AI起草整个原型应用,然后自己进行微调。我们还可能在做出决策之前探索更多解决方案,因为AI可以快速生成替代方案。
然而,我们的行业必须谨慎地整合这些变化。人类元素——凭借其创造力、直觉和道德判断——仍然是不可替代的。AI可以放大我们的能力,但如果不加以控制,它也可以放大错误。
我的愿景是乐观的:如果明智地使用,自主编码代理将使开发者更有生产力,并让我们专注于构建软件中真正具有挑战性和有趣的部分,最终带领我们更快地构建更好的软件。实现这一目标意味着培养良好的实践,并意识到我们作为开发者和工程师的角色正在演变。
关于vibe编码(vibe coding)未来最有趣的问题之一是它将如何塑造编程语言。如果我们可以”只是告诉AI我们想要什么”,我们还需要传统的语法和语言吗?英语(或任何人类语言)会成为新的编程语言吗?本节探讨这些可能性。
我们已经看到自然语言作为代码的迹象,在这些工具中,你用普通语言描述一个任务,AI就会编写代码。如果这一趋势继续下去,我们可能会将更多的编程工作转移到指定意图(intent)和需求(requirements)上,而不是实现上。未来的开发环境可能允许开发者(甚至非开发者)编写这样的内容:
每小时检查我们的数据库中的不活跃用户,并向任何90天未登录的用户发送电子邮件提醒,使用模板X。如果电子邮件退回,在数据库中将该用户标记为”无效电子邮件”。
AI 可以接收这个规范并将其转换为相应的代码(比如设置 cron 作业或定时函数、编写 SQL 查询或使用 ORM,或者调用邮件 API)。本质上,程序员的角色更多地变成了策略和行为描述。
这并不意味着编程语言会在一夜之间消失。相反,可能会出现一种分层:自然语言用于高层编排,现有的编程语言在底层用于细粒度控制。
编程语言存在的一个原因是自然语言可能存在歧义。如果我们完全移除形式化语言,我们就有可能与机器产生误解。AI 可能通过基于上下文的消歧和提出澄清性问题来弥合这一差距,但这可能存在极限;某些复杂的算法或优化可能仍然需要非常具体的指令,而这些指令用代码表达比用散文更容易。因此,可以想象未来的程序员在某种意义上需要是双语的:流利使用人类语言与 AI 交流,同时精通底层技术概念以验证和调整 AI 产出的内容。
我们还可能看到领域特定自然语言的兴起——经过约束的英语(或其他语言)形式,AI 可以可靠地理解,并针对软件领域进行定制:例如,一种”需求语言”,用于编写用例,AI 可以将其转换为测试或代码。
即使没有完全发展到纯自然语言,AI 的影响也可能将编程语言带到更高的抽象层次。过去,我们从汇编语言发展到高级语言,从手动内存管理发展到垃圾回收环境,每次都提高了抽象级别。AI 可以让我们即时定义抽象。可以从本书中讨论的”意图编程”的角度来思考这个问题:你指定一个目标,AI 找出如何实现它,可能会根据需要编写底层代码。
另一种可能是融合自然语言和代码的混合语言。例如,未来的语言可能允许这样的代码:
// 从订单中创建客户名称列表,排除重复项
// 并按字母顺序排序,然后打印它。
list<Customer> customers = orders -> map(order -> order.customerName)
-> unique()
-> sort()
-> print();
在这个伪示例中,英语注释几乎可以由 AI 执行。也许在实际环境中,你甚至不需要 lambda 语法;AI 可以从注释中推断出你的意图并将其填充。或者该语言可能具有看起来像英语但足够形式化以供 AI 解析的构造。可以把它看作是注释和伪代码演变为实际代码,AI 解释其背后的意图。
编码已经变得更加对话化。你不是编写完整的程序,而是与 AI 开始对话:
提示: “我需要一个函数,根据客户的购买历史来计算忠诚度积分。”
AI: “好的。所有产品的计分都一样吗,还是有些产品给更多积分?时间应该如何影响(例如,最近的购买与较早的购买)?”
提示: “电子产品的积分是其他类别的 2 倍。最近一个月的购买额外获得 10% 的奖励。”
AI: “明白了。这是实现该功能的函数… [AI 展示代码]。我还需要为不同场景生成测试吗?”
提示: “是的,还要确保它能妥善处理没有购买历史的情况(应该返回 0 积分)。”
在这个场景中,编程是用自然语言与 AI 进行来回交流,AI 编写代码并提出澄清问题。这类似于结对编程(pair programming),只不过搭档是 AI。我们已经在 ChatGPT 插件和 Copilot Chat 等工具中看到了早期版本。未来,这可能成为创建软件的主导模式:一种逐步完善软件的对话,比从头开始编写严格的静态文本文件要流畅得多。
即使大部分编码都由自然语言驱动,理解代码和计算机的工作原理仍然很重要。计算器和电子表格并没有消除数学家或会计师理解算术的需求。你需要知道 AI 的输出在某种程度上何时不对。如果 AI 误解了英语指令,熟练的开发人员可能会切换到伪代码或实际代码来明确表达。因此,虽然语法细节可能变得不那么重要(如果 AI 可以填充,就不需要记住某个 API 参数的确切顺序),但算法思维(algorithmic thinking)和调试仍然至关重要。语言可能会改变,但底层逻辑和问题解决技能会持续存在。
然而,编程的入门门槛已经降低。非开发人员和领域专家可以通过氛围编程(vibe coding)与 AI 对话,直接创建简单的应用程序。这种民主化令人兴奋:更多人可以在没有深厚编程知识的情况下创建软件解决方案。专业开发人员将处理更困难的问题,安全地集成那些公民开发的脚本,或构建允许此类交互的平台。
即使 AI 帮助我们用自然语言编码,AI 本身也可能演化出介于两者之间的新”语言”。也许会出现新的编程范式,这些范式本质上对 AI 友好——这意味着它们为 AI 留出了填充空白的空间,例如,一种允许部分程序带有 AI 可以解决的占位符的语言(“[此处为速度优化]”),或者带有模糊逻辑(fuzzy logic)的语言,AI 可以将其细化为确定性逻辑。
最终,可能的结果不是用英语完全取代编程语言,而是两者的融合:为开发者提供更强的表达能力和一种更直观的方式来告诉计算机该做什么。正如 Andrej Karpathy 恰当地指出,“也许编程的未来不再是编写完美的代码。也许是完美地解释你想要什么。”编程的本质——清晰地思考问题并指定解决方案——仍然存在。然而,规范的形式将演变得更加自然,AI 作为翻译器将我们的高层意图转化为低层执行。
这个未来充满希望:更快的开发速度、更高的可及性,以及通过专注于我们想要实现什么而非如何输入的细节来创建日益复杂系统的能力。一如既往,每一次抽象层次的飞跃都带来了创造力的爆发(高级语言使得汇编语言永远无法扩展的软件成为可能)。自然语言驱动的开发可能释放另一波创新浪潮,氛围编码者处于最前沿,通过软件字面意义上地谈论出新世界的存在。
在本书中,出现了几个基本原则和思想:
氛围编码将重点从编写逐步代码转移到表达意图或期望的结果,并让 AI 处理实现细节。这改变了我们处理问题的方式:我们更多地思考想要实现什么,而更少地思考如何输入。这是一种更高层次的软件开发思维方式。
在氛围编码中,AI 不是孤立使用的工具,而是像结对程序员或助手一样。它是交互式和迭代的。我们看到引导 AI(通过提示词工程(prompt engineering))、审查其输出以及结合我们的优势与 AI 的优势是多么重要。未来不是”AI 取代程序员”,而是程序员与 AI 并肩工作以提高生产力。
我们强调,AI 的强大力量伴随着巨大的责任。减轻偏见、确保公平、保持流程透明和维护问责制都至关重要。行业正在认识到,在没有防护措施的情况下依赖 AI 可能会导致问题,因此围绕测试 AI 输出、记录 AI 参与以及解决法律问题(如 AI 生成代码的知识产权(IP))的最佳实践正在成为标准程序的一部分。
AI 的作用延伸到测试、调试、设计、项目管理等方面。这种整体集成意味着整个软件生命周期都被 AI 加速和增强。工具将越来越多地支持这些阶段——有些已经支持,比如 IDE 中的 AI 测试生成或基于 AI 的项目调度工具。
采用前面提到的实践的程序员会发现他们的技能组合正在演变——转向包括提示词工程、AI 监督、数据分析和高层设计,以及传统的编码和算法技能。核心的解决问题思维方式仍然至关重要,但日常任务看起来不同了。
然而某些基础知识仍然成立:深入理解你的问题领域、编写清晰的规范(提示词基本上就是规范)、维护严格的测试和验证,以及关注用户需求。AI 不会改变这些;如果有什么不同的话,它会放大它们的重要性,因为任何歧义或缺乏清晰度都可能被 AI 的超快执行放大。
这种vibe编码的新范式正在以实际的方式重塑整个行业。采用AI工具的团队报告称生产力显著提升:开发者可以在更短的时间内完成功能,或者用相同的资源处理更复杂的项目。它也在降低进入门槛:经验较少的开发者可以在AI指导下实现更多,可能会更快地提升水平。另一方面,它也在推动经验丰富的开发者拓展视野,避免对旧工作流程产生自满情绪。
公司开始招聘时不仅看重编程知识,还看重”AI素养(AI literacy)“——有效利用AI工具的能力。职位描述可能很快就会包含对AI编码助手的熟悉程度,就像今天它们包含对版本控制或云平台的熟悉程度一样。因此,成为vibe编码的先驱者能够提供职业优势。
重要的是,vibe编码在一定程度上让编程更加民主化。更多人——包括那些不是传统软件工程师的人——可以通过描述他们想要什么来参与软件创建。这可能会导致专为小众需求定制的软件蓬勃发展,由领域专家在AI的帮助下创建(专业开发者则专注于为他们提供防护栏、平台和精良的核心组件)。
这是一个令人振奋的时代。我们站在一场变革的边缘,而我们作为开发者有机会塑造它。回想计算机的早期:那些拥抱个人计算机革命的人最终创造了我们现在拥有的世界。今天,编程中的AI是一个类似的拐点。拥抱它意味着参与定义未来几十年软件的构建方式。
编程的未来不是会发生在我们身上的事情——它是我们将要创造的东西。开发者社区中的每一个人都在vibe编码和AI工具如何被采用、监管和推进方面扮演着重要角色。这是对你作为读者和实践者的行动号召:
不要等待所有答案都给你。走出去,在不同的场景中尝试vibe编码。使用AI构建一些新奇有趣的东西。突破这些工具能做什么的界限。也许你会发现一个新颖的用例或一个尚未被记录的局限性。每个实验,无论成功还是失败,都为社区贡献了知识。
写下你的经验,或至少与同行讨论它们。如果你发现一种非常有效的技术,就发布它。如果你遇到一个陷阱,就警告其他人。在这个快速演进的过程中,社区知识共享是我们所有人保持同步的方式。通过发布你找到的AI怪癖(quirk)的解决方案,你可能会为某人节省数天的调试时间,或者通过分享一个很酷的AI辅助项目来激发某人的创造力。
如果你有这个意愿,可以为AI工具本身的开发做出贡献。这可能意味着为开源AI框架贡献代码,或者只是向工具制作者提供详细的反馈(他们中的许多人非常渴望听取用户关于改进建议的意见)。通过帮助塑造工具,你可以直接影响未来的样子。今天的许多AI编码助手已经取得了长足的进步,因为像你这样的开发者测试了测试版本并提供了见解。
在你的组织或社区中,倡导使用AI来提高生产力,同时倡导培训人们正确使用它。鼓励管理者为学习AI工具留出时间,或更新可能因误解而禁止使用AI的政策。展示如何安全且有益地使用它。AI积极增强团队的成功案例越多,行业就会越倾向于拥抱它。
在这个新时代,采用我们都是学生的心态。保持谦逊和开放的心态。未来的新手可能会原生地了解AI工具(就像今天的应届毕业生可能比一些年长的人在成长过程中有更多的编码接触)。准备好向任何人学习,无论经验水平如何,因为这在某种程度上对每个人来说都是新的。如果你保持学生心态,你将始终找到成长,避免陷入认为自己已经弄清楚一切的陷阱。
对可能性充满热情——你的兴奋会激励他人。但在需要时也要成为谨慎的声音,确保热情不会导致粗心使用。例如,倡导AI驱动的开发,但也推动对AI输出进行单元测试和代码审查。这种平衡的方法将使vibe编码可持续且受到尊重。
当你获得精通时,帮助新手。Vibe编码降低了进入门槛,这意味着更多的初学者可能会投入编程。他们需要指导来学习AI可能抽象掉的扎实基础知识。通过指导他们,你可以确保下一代开发者不会过度依赖AI而不理解。你将传承良好软件工程实践的火炬,现在由AI增强。
我们看到的指数级变化是一个难得的机会。想想之前的技术飞跃,从工业革命到互联网繁荣——那些参与其中的人塑造了整个行业。我们正处于AI在软件开发中的这样一个关键时刻。这不仅仅是关于保住你的工作或让工作变得更容易;这是关于在技术如何演进以及如何影响社会方面拥有发言权。
通过阅读本书,你已经展现出自己是一个具有前瞻思维的人。现在,我鼓励你将这种前瞻思维付诸行动。你用AI编写的每一行代码,你设计的每一个提示词(prompt),你教导的每一位同事,你影响的每一项政策——这一切都在为vibe coding的未来做出贡献。
最后,请记住,编程的核心始终是关于创造和解决问题。由AI驱动的vibe coding是一种极其强大的新型创造媒介。以乐观和好奇的心态拥抱它。用它来构建有意义的东西。在这样做的同时,始终将人的因素放在中心——我们的创造力、我们的判断力、我们的价值观。
编程的未来正在此刻被书写,不仅体现在代码中,还体现在我们如何选择将这些AI伙伴融入我们的工作中。这是一条令人兴奋的、未经探索的道路,我们每个人都有机会成为先驱。所以勇敢向前,大胆实验,慷慨分享,以人类智慧和精神的精华引领前行。这样做,你不仅仅是在适应未来——你是在积极地塑造未来。
祝你vibe coding愉快,我们将在你帮助创造的未来中相见!
仪表板(dashboards)
数据偏差(data bias), [Bias and Fairness]
数据一致性规则(数据库), [Checking AI-Generated Queries]
数据分区和复制(data partitioning and replication), [Learn System Design and Architecture]
数据保护法律, [Golden Rules for Responsible AI Use]
数据结构(data structures), [Learn the Fundamentals—Don’t Skip the “Why”]
数据库(databases)
日期格式化,使用AI, [AI as first drafter]
最低限度原则(de minimis), [Intellectual Property Considerations]
初级开发者的消亡, [Junior Developers: Thrive Alongside AI]
调试(debugging)
项目管理中的决策支持, [The Evolution of Project Management with AI]
深度推理模型(deep reasoning models), [Understanding Model Categories]
DeepSeek模型, [Understanding Model Categories]
AI生成代码中的不安全默认设置, [Common Security Vulnerabilities in AI-Generated Code]
删除操作(deletes)
演示级质量陷阱(demo-quality trap), [The demo-quality trap]
编程的民主化, [The Vibe-Coding Approach: Code by Conversation]
对AI编码工具的依赖导致的问题, [Two steps back]
依赖项(dependencies), [Setting Up the Project: Scaffolding with AI]
使用未来AI进行更新, 预测性维护和重构
依赖管理和更新,AI生成代码中的安全漏洞, AI生成代码中的常见安全漏洞
部署
设计
开发者
开发
Devin, 验证, [后台代理与IDE内AI助手相比如何?]
DevOps
差异对比(diffs), 报告
AI使用导致的歧视, [负责任使用AI的黄金法则]
招聘多元化团队, [偏见和公平性]
Django, 使用框架或样板
文档
在原型向生产环境演进时添加, 将原型演进到生产环境
Doe诉GitHub公司集体诉讼, 知识产权考量
领域专业知识,构建, 构建你的领域专业知识
领域精通和前瞻性,培养, [持续培养领域精通和前瞻性]
特定领域的自然语言, [编程语言的未来:自然语言驱动的开发?]
软件工程特定领域, 构建你的领域专业知识
故障模式和恢复程序, 学习系统设计和架构
使用AI辅助编码的失败模式, 常见失败模式
失败
合理使用, [知识产权考虑]
AI生成输出的公平性, [偏见和公平性]-[负责任使用AI的黄金法则]
AI生成代码中的后备方案, 处理代码输出
功能特性
反馈
少样本提示(few-shot prompting), 单样本和少样本提示
AI生成原型对输入或意图的保真度, [AI原型工具]
AI作为初稿撰写者, [实际有效的方法:实用工作流模式], AI作为初稿撰写者
AI作为力量倍增器, [磨练软技能和领导力]
使用远见引导AI, [继续培养领域专精和远见], 学习管理系统集成和边界
格式化工具, [为AI生成系统构建有效的测试框架], 在提示时
格式化
框架
前端
使用AI的全栈流程(示例), 示例:使用AI的全栈流程
编码代理默认进行全文搜索, 执行
AI代码中你不想要的功能, [从意图到实现:理解AI的解释]
functions(函数)
fundamentals(基础知识)
future of AI-augmented development(AI 增强开发的未来) (参见 AI 增强开发的未来)
future-proofing your career with durable engineering skills(用持久工程技能为职业生涯做好准备), 总结与下一步
fuzz testing of AI-generated code(AI 生成代码的模糊测试), 渗透测试和模糊测试, 为 AI 生成系统构建有效的测试框架
领导力和软技能,磨练, 磨练你的软技能和领导力
学习的重要性, 保持适应性——永不停止学习
AI的法律要求, 负责任AI使用的黄金法则
AI生成代码中的库使用, [代码可读性和结构:模式与潜在问题]
AI代码中的许可证考虑因素, 包管理问题, [知识产权考虑因素]
代码检查工具, [在谱系中找到你的位置], [为AI生成系统构建有效的测试框架], [提示时]
大语言模型(LLMs), [引言:什么是氛围编码?], 按任务类型区分模型
负载均衡, 学习系统设计和架构
负载测试, [为AI生成系统构建有效的测试框架]
日志记录
Lovable, [AI原型工具]
包幻觉(package hallucination), 包管理问题
AI生成代码中的包管理问题, 包管理问题
结对程序员,AI作为, [真正有效的方法:实用工作流模式], AI作为结对程序员
AI配对编程的最佳实践, AI配对编程的最佳实践
配对编程
并行性, 性能优化
密码
模式(AI),从中学习, [后续跟进]
AI生成代码中的模式, 代码可读性和结构:模式和潜在问题
AI生成代码的渗透测试, 渗透测试和模糊测试
性能
性能约束,在提示词中提及, [上下文提示]
个性化用户体验, 个性化用户体验
AI代码中的占位符, 代码可读性和结构:模式和潜在问题
规划和监督功能,代理特定的,利用, [利用代理特定的规划和监督功能]
规划阶段,自主编码代理, 规划
糟糕提示词的示例, 糟糕的提示词
PostgreSQL, 数据库集成
PR(拉取请求)审查流程
隐私和安全问题(AI编码), [AI仍然困难的领域]
用户数据的隐私问题, [负责任使用AI的黄金法则]
问题解决
AI辅助编码带来的生产力提升
编程
编程语言
项目级别,自主编码代理在此级别运作, [后台代理与IDE内AI助手的对比如何?]
项目管理,随AI演进, [AI时代项目管理的演进]-[AI时代项目管理的演进]
提示词反模式,避免, [常见的提示词反模式及如何避免]-[像”上述代码”这样的模糊引用]
忽略 AI 的澄清或输出, 忽略 AI 的澄清或输出
提示词中的不一致性, [不一致性]
遗漏问题, 遗漏问题
过载的提示词, [过载的提示词]
模糊的提示词, [模糊的提示词]
模糊的引用, [像”上述代码”这样的模糊引用]
模糊的成功标准, 模糊的成功标准
提示工程(prompt engineering), [提示的艺术:与 AI 有效沟通]
提示技术, [提示技术:有效沟通的工具箱]-ReAct(推理 + 行动)提示
提示词
概念验证, [使用 AI 助手快速原型开发]
基于属性的测试, [为 AI 生成的系统构建有效的测试框架]
原型开发
PR(拉取请求), 代码审查策略
安全系统(AI),对角色描述的敏感性,角色提示
SAST(静态分析工具),利用自动化安全扫描器
脚手架
调度和冲刺规划,使用AI,[AI驱动的项目管理演变]
范围蔓延,应对AI原型开发中的挑战
截图转代码能力,[AI原型工具]
机密数据,保密,负责任AI使用的黄金法则
密钥管理工具,[部署前和部署期间]
secrets或凭证,硬编码在AI生成的代码中, AI生成代码中的常见安全漏洞
安全性, [安全性、可维护性和可靠性]-放慢速度
安全分析师角色,分配给AI, 角色提示
自我一致性, [自我一致性(Self-Consistency)(多个输出和多数投票)]
Semgrep, 利用自动化安全扫描器
高级工程师 (参见 工程师)
Sequelize, [使用ORM]
Web应用程序后端中的服务器, [使用AI构建Web应用程序]
70%问题, [70%问题:实际有效的AI辅助工作流程]-[开发人员实际如何使用AI]
影子测试, [部署之前和部署期间]
注册页面,使用AI助手进行原型设计, [使用AI助手快速原型设计]
AI辅助编码导致的技能退化, [AI仍然困难的领域]
技能
Snyk, 使用具有安全重点的更新模型或工具
软技能和领导力,磨练, [磨练您的软技能和领导力]
软件工程
解决方案
Sonnet模型, Claude:推理大师
来源,确认AI生成代码的来源, [透明度和归属]
提示中的具体性, 模糊的提示
速度优化的模型, 理解模型类别
蔓延,在AI生成的代码中避免, 在提示时
冲刺规划,使用AI进行, [使用AI的项目管理演变]
SQL
AI生成代码中的SQL注入漏洞, AI生成代码中的常见安全漏洞
分阶段推出策略, [部署之前和部署期间]
初创公司最小可行产品网络应用程序, [成功的AI构建Web项目示例]
状态(state), 有状态对话与一次性提示
有状态对话与一次性提示, 有状态对话与一次性提示
静态分析工具(SAST), 利用自动化安全扫描器
样式指南
使用AI进行Web应用程序的样式和布局, 样式和布局
提示中的模糊成功标准, 模糊的成功标准
监督式编码代理(supervised coding agents), 从副驾驶到自主代理:什么是后台编码代理?
全栈Web应用中的同步和状态管理, 状态管理和同步
AI代码中的小语法问题, 代码可读性和结构:模式和潜在问题
系统
管理系统集成和边界, 学习管理系统集成和边界
Addy Osmani是Google的高级工程领导者,致力于开发者体验、性能和AI驱动的软件开发工具。他拥有25年的行业经验,构建Web技术,并撰写了多本关于软件工程最佳实践的书籍。
Addy在AI驱动的开发者工具方面拥有丰富的经验,测试和评估新兴平台,如Cursor、Cline、Copilot、Bolt、v0、Lovable和Claude Code。他关于AI辅助软件开发的著作影响了成千上万的开发者,他在Google Chrome的领导地位帮助塑造了Web性能和AI增强开发者工作流的未来。
本书提炼了他在软件工程方面的深厚专业知识以及在AI驱动编码助手方面的实践经验,为开发者提供了将AI集成到日常工作流程中的实用策略,并帮助他们适应快速变化的软件开发环境。
《Beyond Vibe Coding》封面上的鸟类是红翅旋壁雀(wallcreeper)(Tichodroma muraria),这是一种原产于欧洲、中东和亚洲内陆悬崖和山峰的物种。据说红翅旋壁雀在飞行时看起来像大型蝴蝶,但它们在岩壁上休息时很难被发现。翅膀具有丰富的红色,羽毛尖端为黑白色,而身体大部分为蓝灰色。繁殖期的雄性有黑色的颈部。
成年红翅旋壁雀长约6英寸,重约0.6盎司。它们每年养育一窝雏鸟,在楔入岩石缝隙的苔藓和草制成的杯状巢中产下三到五枚卵。它们是食虫动物。
IUCN濒危物种红色名录将红翅旋壁雀的保护状态列为”无危”。O’Reilly封面上的许多动物都濒临灭绝;它们都对世界至关重要。
封面插图由José Marzan Jr.创作,基于《Lydekker’s Royal Natural History》中的古董线条版画。系列设计由Edie Freedman、Ellie Volckhausen和Karen Montgomery完成。封面字体为Gilroy Semibold和Guardian Sans。正文字体为Adobe Minion Pro;标题字体为Adobe Myriad Condensed;代码字体为Dalton Maag的Ubuntu Mono。