氛围感编程的12条黄金法则

在使用大模型自动生成代码时需要注意的12大陷阱,可作为AI自动编程和Vibe编程的最佳实践,将氛围感拉满:

1、你要停止挖掘
现在的AI模型在遇到问题时,不太懂得停下来思考。比如说,你想做一个功能X,但在做的过程中发现要先做功能Y才能继续。这时候,人类会停下来先做Y,但AI模型会继续埋头做X,不管有多难。这其实也有好处,因为AI会严格按照你的要求去做,不会自作主张。

为了避免这种情况,通常我们会先做好计划,再让AI去执行。比如,先让AI分析一下任务,再开始做。有些高级的AI,比如Sonnet,会自己读取文件,然后根据内容做计划,这样它就能自己发现问题,不需要你一直告诉它该做什么。

最理想的情况是,AI能意识到出了问题,然后告诉你。但这需要很多背景上下文信息,所以最好有一个专门的AI来负责这种检测。

举个例子:我在修改蒙特卡罗模拟的随机数采样后,让Claude Code修复所有测试。有些测试是检查随机采样策略的,但新的实现在测试时结果不确定,导致测试有时通过有时失败。Claude Code没注意到测试结果在变化,它只是拼命想让测试通过,结果它放宽了测试条件,而不是建议我们重新设计模拟,让它变得确定。


2、使用静态类型
动态类型和静态类型的争论,其实就是“写代码快”和“代码好维护”之间的选择。

  • 动态类型语言(比如Python和JavaScript)写起来简单,适合快速试错,但以后改代码可能会麻烦。
  • 静态类型语言(比如Rust)写的时候更严格,但以后改代码会更轻松。

现在有了大语言模型(LLM),写代码变得更简单了,因为LLM可以帮我们搞定很多重复的代码,还能帮我们改代码。所以,你不用太纠结选哪种语言了,可以根据需要灵活选择。

LLM 的兴起大大减少了选择擅长原型设计的语言的压力,因为 LLM 可以掩盖样板和重构。请根据情况进行选择。

如果你用LLM来改代码,最好设置一个提醒,当LLM改完代码后,如果有类型错误,它能告诉你。这样你就知道在调整代码结构时,哪些文件需要跟着改。不过要注意,用LLM可能会花不少钱。

另外,LLM学了很多Python和JavaScript的代码,所以它用这两种语言写代码比较厉害。虽然这两种语言也有类型系统,但它们的类型系统比较灵活,所以你要小心设置类型检查器,让它严格一点,或者告诉LLM怎么正确设置。

理论上,Rust这种语言应该也挺适合LLM的,但LLM在写Rust代码方面还不太行,没Python和JavaScript那么熟练。

3、黑盒测试
黑盒测试的意思是说,你在测试一个东西的功能时,不需要知道它里面是怎么工作的。但是,对于大型语言模型(LLM)来说,默认情况下很难做到这一点,因为它会把实现代码放进去,或者它会自己去想办法弄清楚怎么和代码互动。比如,Cursor 里的 Sonnet 3.7 这个工具,它喜欢让代码看起来一致,所以它会试着去掉测试文件里重复的部分。但是,黑盒测试的建议是最好保留这些重复的部分,因为这样可以避免测试直接反映出代码实现里的错误。

最好的情况是,在把文件加载到上下文里的时候,可以把具体的实现代码隐藏起来,或者只总结一下它的功能,这样就不会过度依赖那些应该被隐藏的内部细节。所以,设计系统的人需要明确哪些信息是需要隐藏的,哪些是可以暴露的。

4、需求,而非解决方案
在人类做软件工程的时候,当大家想搞清楚该做什么时,一个常见的错误就是直接跳到想解决办法,而没有让每个人都先把所有需求说清楚。

通常,你的问题范围是有限的,只要你把所有的需求都写下来,解决办法其实就只有一个了;如果没有这些需求,大家就很容易陷入争论某个解决办法的迷雾里。

LLM(大型语言模型)对你的需求一无所知。当你让它做某件事但没有说清楚所有限制条件时,它会用它在训练时学到的最可能的答案来填补这些空白。也许这样也行。但如果你需要更特别的东西,那你就得明确告诉LLM。如果你问LLM的问题太模糊,它理解错了,最好的办法是修改你原来的问题,然后重试;因为之前的对话会留在上下文里,如果错误的解释留在上下文里,LLM就更难找到符合你需求的那个正确答案了。

顺便说一下,如果你确定解决办法的某些部分应该按照特定的方式工作,那么告诉LLM会非常有帮助,因为这也会帮你找到正确的答案空间。而且,正确地告诉LLM这一点也很重要,因为LLM会尽力按照你的要求去做,即使这个要求不合适。

5、尊重规范
LLM(大型语言模型)不太擅长严格遵守规则。它们可能会随便删掉测试、改掉API,或者在写代码时做出一些奇怪的事情。有些规则是常识,你可以在提示里告诉它,但有些规则只有当LLM想出一些新的、有趣的方法去“钻空子”时,你才会发现。这就是为什么检查LLM生成的代码非常重要——你要确保它没有乱改规则,或者用不一致的方式去改变原本的规范。

在设计变更时,重要的是要记住系统的哪些部分可以更改,哪些部分不能更改。

6、推土机方法
一种阅读教科书的技巧,他称之为 "推土机法",就是把所有的练习都做完。

“推土机方法”告诉我们,有时候你只需要静下心来,做一些看似笨拙的重复工作,然后从中学到东西,就能提高效率,最后取得让人惊讶的成果。

人工智能(AI)写代码就是这种重复工作的典型例子:如果你愿意花足够的资源(token),你可以用蛮力解决大规模的代码重构问题,或者你可以让大型语言模型(LLM)帮你设计一个工作流程,然后用这个流程去蛮力解决问题。

以前人们觉得“工作量太大”的问题,现在可能就有机会解决了。

不过,一定要检查LLM到底在做什么,因为它会不厌其烦地重复同样的事情,不像人类会觉得无聊然后去找更好的办法。

7、本末倒置
“本末倒置”指的是小事情或者不重要的事情反而控制了大事情或者更重要的事情的情况。

在软件工程中,出现这种情况的一个常见原因是,当你太专注于解决一些细节问题时,你可能会忘记你最初写代码的真正目的。

LLM(大型语言模型)特别容易受到这个问题的影响。

问题在于,在最常见的聊天模式中,LLM做的所有事情都会被放到上下文里。

虽然LLM能理解什么是重要的、什么是不重要的,但如果你在上下文里放了很多不相关的东西,它就会越来越难记住它本来应该做什么。

一开始小心地给出提示会有帮助,保持上下文的干净也很重要。

Claude Code做了一些聪明的事情,它可以让子代理在专门的上下文窗口里执行任务,这样就不会弄乱全局的上下文。

8、了解你的极限
重要的是要知道何时您无法胜任或没有可用的工具来完成您的工作,这样您就可以上报并寻求帮助。

Sonnet 3.7 不太擅长了解自己的局限性。如果你想让它告诉你它不知道如何做某事,至少你必须明确提示它(例如,当它被问及一个非常小众的话题时,Sonnet 的系统提示会指示它明确警告用户有关幻觉的问题。)只要求 LLM 做它实际上可以做的事情非常重要,尤其是当它是一个代理时。

9、文化影响战略
《文化吃掉战略(早餐)》这本书里说,不管你的计划有多好,如果你的团队文化不行,那这个计划也执行不了。如果你的问题是执行不到位,那就去改变文化,而不是一直想更复杂的计划。

默认情况下,你的 LLM(大型语言模型)会处于“潜在空间”的某个部分:当你让它生成代码时,它会根据它的训练方式和上下文窗口里的内容(包括系统提示和它读进去的任何文件)来生成代码。

这种风格会自我强化:如果上下文窗口里有很多文本用了某个库,LLM 就会继续用这个库;反过来,如果上下文里根本没提到这个库,而且 LLM 也没有被训练成默认用这个库,它就不会用(虽然有例外,但这基本能描述 Sonnet 3.7 的行为)。

如果 LLM 一直在做你不喜欢的事情,你需要改变它的“文化”:你需要把它放到潜在空间的不同部分。这可以通过给你的 Cursor 规则加一条新规则(修改提示)来实现,但也可以通过重构现有代码,让它变成你希望 LLM 遵循的风格,因为 LLM 是被训练成根据上下文预测下一个内容的。

微调、提示和代码库就是它的文化。你没法改变这一点,代码库比提示大得多,最终它会起主导作用。

10、自动代码格式化
自动代码格式化工具(比如 gofmt、rustfmt 和 black)可以帮助整个代码库保持统一的代码风格。LLM(大型语言模型)通常不太擅长遵守一些机械化的规则,比如“即使缩进不是零,空行也不能有末尾的空格”或者“确保一行在 78 个字符的地方换行”。所以,用合适的工具来做这些事会更好。
这也适用于 lint 检查(一种代码检查工具):优先选择那些能自动修复问题的 lint 工具,别浪费 LLM 的能力让它去修这些简单的问题;把 LLM 的能力留着去处理那些更复杂的事情。

11、三的法则
软件中的“三原则”是说,你可以容忍复制一段代码一次,但如果第三次复制同样的代码,你就应该考虑重构它了。这是对 DRY(不要重复自己)原则的改进,因为有时候怎么消除重复并不明显,等到第三次出现时,可能才会清楚该怎么改。(也可以参考“错误的抽象”这个概念。)

LLM(大型语言模型)特别喜欢复制代码。想想看,如果你没有特别说明,只是让 LLM 修改程序,它可能会直接给你一个包含修改内容的新程序副本。

对 LLM 来说,决定重构代码、消除重复,需要它主动花力气去清理代码。(实际上,Sonnet 3.7 也有点这种倾向,但如果你看看 Claude Code 的系统提示,他们特别告诉模型不要太主动,因为模型很容易忘记自己在做什么。)

更糟糕的是,如果代码已经在代码库的某个地方被复制粘贴过,LLM 会觉得你希望它继续到处复制粘贴代码。

所以,你需要让模型减少重复

12、记忆
你用的 LLM(大型语言模型)是没有记忆的。每次你让它完成任务时,它都得重新理解上下文,才能知道该做什么。这个上下文包括提示(比如项目的 Cursor 规则)、你明确或隐式加进去的内容,还有模型在代理模式下决定要的东西。就是这样!每次你开始新的聊天时,模型都得从头开始快速理解你的代码库。

“让人惊讶的不是熊跳得好,而是熊居然会跳舞。” LLM 能根据每个请求从头重新理解你的代码库,这确实很神奇。但它也很脆弱;如果模型没法把正确的文件放进它的上下文里,它很快就会搞砸,因为它对你想要做的事情产生了错误的理解。

帮模型做对的事情:确保有可以引用的文档,并且模型能找到它(比如通过提示,或者直接把文档放在模型期望的位置)。避免在不考虑整个项目的情况下要求进行重大改动(因为目标不一致更容易造成破坏)。

总结:

  • 重大改动风险:如何避免项目目标不一致
  • 避免代码重复与重构陷阱
  • 三原则:复制与重构的平衡
  • 潜在空间:如何引导LLM生成理想代码
  • 自动格式化工具:更适合代码风格管理
  • 本末倒置:如何避免细节失控
  • 蛮力法则:如何用LLM解决复杂重构问题
  • 的黑盒测试:如何避免过度拟合
  • 上下文管理:如何让LLM记住重要信息
  • 规范遵守:如何避免代码失控
  • 需求明确性:如何避免LLM误解你的意图
  • 执行问题:文化吃掉战略、如何改变AI行为
制造十足的氛围感,让AI自动吐出你想要的代码,取决于你制造氛围的能力,如同你约会制造氛围让女友动心一样,如同放出一段音乐,让人们能和曲而舞一样。