我是如何借助大语言模型编写软件的

分类技术博客
作者Stavros
来源跳转
发表时间

内容

我并不热衷于编程带来的乐趣

最近,我又重新开始制作各种东西,而且主要是因为大型语言模型(LLMs)。我曾经以为自己喜欢编程,但后来发现我真正喜欢的是创造东西,而编程只是实现这一目标的一种方式。由于LLMs在编程方面表现良好,我一直在不停地使用它们来制作各种东西,令人兴奋的是,我们正站在另一个全新的、尚未探索的前沿。

关于LLMs目前存在很多争论,但一些朋友询问了我的具体工作流程,我决定详细写出来,希望能帮助他们(和你们)比以前更轻松、快速、高质量地创作。

我还在文章末尾附上了实际的(注释过的)编码会话,如果你想跳过工作流程的细节,可以直接去看。

收益

自从Codex 5.2(感觉像一个世纪以前)和最近的Opus 4.6发布以来,我惊讶地发现,我现在可以用LLMs编写软件,缺陷率非常低,可能比我自己手写的代码还要低,而且我仍然了解整个系统的运作方式。在此之前,代码在编程两三天后就会迅速变得难以维护,但现在我已经连续工作了几周,代码已经增长到数万行,每次更改都像第一次一样可靠。

我也注意到,我的工程技能并没有变得无用,只是发生了变化:我不再需要知道如何正确地编写代码,但现在更重要的是要了解如何正确地设计系统,以及如何做出正确的选择,使产品变得可用。

在那些我不了解底层技术(例如移动应用)的项目中,代码仍然会迅速变成一团糟,充满糟糕的选择。然而,在那些我熟悉技术的项目(例如后端应用,尽管不一定是Python),这种情况还没有发生,即使代码已经达到了数万行。大多数情况是因为模型变得更好,但我认为也有很大一部分原因是由于我改进了与模型合作的方式。

我注意到,不同的人使用LLMs会得到截然不同的结果,所以我怀疑与模型交流的方式会影响结果。因此,我将在这篇文章中深入探讨,甚至会发布实际的会话记录,这样你可以看到我开发的全部细节。

另一个值得提到的点是,我不知道未来模型将如何演变,但我已经注意到一个趋势:在LLMs的早期(不是GPT-2,因为它非常有限,而是从davinci开始),我不得不检查每一行代码,确保它是正确的。到了后来的LLM世代,这个要求提高到了函数层面,所以我不需要检查代码,但需要检查函数是否正确。现在,这个要求主要在“总体架构”层面,可能会在明年出现一个不需要人类干预的时代。但就目前而言,你仍然需要一个具有良好编程技能的人。

我通过这种方式构建了什么

我最近通过这种方式构建了很多东西,我想列出一些,因为人们普遍批评LLMs只适用于玩具脚本。这些项目从日常驱动程序到艺术项目,但它们都是我每天使用的真正的、维护良好的项目:

Stavrobot

我最近构建的最大项目是 Stavrobot,一个专注于安全性的OpenClaw替代品。多年来,我一直想要一个LLM个人助理,终于通过这个项目实现了。很多人会说“但你无法让LLMs安全!”,这是一种误解,认为安全性是关于权衡的,我的代理试图在给定的可用性水平下最大化安全性。我认为它非常成功,我已经使用它一段时间了,真的很喜欢我可以准确地推理它能做什么和不能做什么。

它管理我的日历,并智能地做出关于我的可用性或冲突的决定,为我做研究,通过编写代码来扩展自己,提醒我曾经忘记的事情,并自主管理琐事等。助理是一种无法真正解释其好处的东西,因为它们没有一个杀手级的功能,而是可以缓解一千个小问题,这些问题因人而异。因此,试图向某人解释拥有助理的好处,最终会得到这样的反应:“但我不需要你需要的任何东西”,而忽略了每个人都有不同的需求,有一个可以访问工具并可以做出明智的决定来解决问题代理对任何人来说都是一个很大的帮助。

我计划很快更详细地写这篇文章,因为在设计它时遇到了一些非常有趣的挑战,我喜欢我解决它们的方式。

中间

也许我最近的命名并不出色,但这是一个 小型挂件,记录语音笔记,转录它们,并可选地将它们发送到您选择的Webhook。我让它将语音笔记发送到我的LLM,随时随地拿出它,按下一个按钮,将想法或问题记录进去,知道答案或待办事项在下次我查看助理的消息时就会出现。

这是一个简单的东西,但其有用性不在于 它做了什么,而在于 它做事的方式。它总是可用,总是可靠,使用时没有摩擦。

手法

我也计划写关于这篇文章,但它更像是一件艺术品:它是一个滴答作响的墙壁时钟,秒针不规则地跳动,但始终准确到分钟(其时间通过互联网同步)。它有多种模式,一种模式具有可变的滴答时间,从500毫秒到1500毫秒,这令人愉快地令人沮丧。另一种模式比一秒钟稍微快一点,但随后随机暂停一秒钟,让毫无防备的观察者质疑自己的理智。另一种模式则以双倍速度冲向59秒,然后等待30秒,最后一种模式是一个普通的时钟,因为所有不规则的滴答声都让我发疯。

松树镇

Pine Town 是一个充满幻想的无限多人在线画布,类似于一片草地,你拥有自己的一个小地块来进行绘画。大多数人会画出……值得怀疑的内容,但偶尔会有成年人访问并画出一些不错的东西。有些绘画是真正的珍宝,浏览其他人创作的作品通常很有趣。

我通过LLMs构建了所有这些项目,甚至从未阅读过大部分代码,但我仍然对每个项目的架构和内部工作原理非常熟悉。原因如下:

框架

对于框架,我使用 OpenCode。我非常喜欢它的功能,但显然有许多其他选择,我也曾与 Pi 有过良好的体验。然而,无论您使用什么框架,它都需要允许您:

  • 使用不同公司的多个模型。 大多数第一方框架(Claude Code、Codex CLI、Gemini CLI)都无法满足此要求,因为公司只希望您使用他们的模型,但这是必要的。

  • 定义可以自主调用其他代理的自定义代理。

根据您的项目和技术栈,还有其他一些您可能希望拥有的优点,例如会话支持、工作树管理等,但这些取决于您。我将解释上述两个要求,以及它们为什么是必要的。

多个模型

您可以将特定模型(例如Claude Opus)视为一个人。当然,您可以重新开始,拥有干净的上下文,但模型在很大程度上仍将具有与之前相同的意见/优势/劣势,并且很可能同意自己的观点。这意味着让模型审查它刚刚编写的代码基本上是无用的,因为它往往会同意自己的观点,但这也意味着让不同的模型审查代码将带来很大的改进。实质上,您是从第二双眼睛那里获得审查。

不同的模型在这些方面会有不同的优势和劣势。例如(这非常具体到今天的模型),我发现Codex 5.4非常吹毛求疵和迂腐。当我想要编写代码时,我不想要这种特性,但它绝对是审查代码时想要的。Opus 4.6做出的决定与我自己做出的决定非常吻合,而Gemini 3 Flash(是的,Flash!)在想出其他模型没有看到的解决方案方面非常出色。

每个人都对哪个模型适合哪项工作有不同的看法,而且模型往往会交替使用(例如,我在11月份使用Codex作为我的主要模型,后来又换回了Opus)。要获得最佳结果,您需要混合使用所有模型。

相互调用的代理

我使用的流程由不同的代理组成,如果集成环境不允许代理之间相互通信,你就需要花费大量时间在大型语言模型之间传递信息。你可能希望减少这种信息传递,因此这个功能非常有用。

我的工作流程

我的工作流程包括一个架构师、一个开发者和一到三个评审者,具体取决于项目的优先级。这些代理被配置为 OpenCode 代理(基本上是技能文件,即包含我希望每个代理如何行为的指令的文件)。

我使用多个代理(而不是只使用一个代理完成所有事情)有三个原因:

  • 它允许我使用昂贵的模型(Opus)进行规划和生成详细计划,但使用较便宜的模型(Sonnet)进行实际的代码编写。这样可以节省 tokens,而不是让 Opus 完成整个过程。

  • 它允许我使用不同的模型来审查代码,这确实可以提高质量,因为不同的模型在审查时可以发现不同的问题。

  • 它允许我使用具有不同能力的代理(例如,一个可能具有代码的只读访问权限,而另一个可能具有写入访问权限)。

我不认为使用具有相同模型和相同能力的两个代理有太大意义,因为这就像一个人假装戴不同的帽子,但我并没有深入研究过这个问题。

我也倾向于手动编写技能文件,因为我发现如果让大型语言模型编写技能文件并没有太大帮助。这就像让某人编写关于如何成为优秀工程师的说明,然后给他们自己的说明,说:“这就是如何成为优秀工程师,现在就这样做。”显然,这不会真正使他们变得更好,所以我尝试自己编写说明。

如果你想自己尝试这种方法,可以下载我的代理文件

架构师

架构师(目前是 Claude Opus 4.6)是我唯一直接交互的代理。这个代理需要非常强大的模型,通常是我能访问的最强大的模型。这个步骤不会消耗太多 tokens,因为它主要是聊天,但你希望它非常有道理。

我会告诉大型语言模型我的主要目标(这将是一个非常具体的特性或错误修复,例如 “我想为 Stavrobot 添加重试机制和指数退避,以便在 LLM 提供商宕机时可以重试”),然后与它交谈,直到我确定它理解我想要的。这个步骤需要最多的时间,有时甚至需要半个小时的反复沟通,直到我们最终确定所有目标、限制和权衡,并就最终架构达成一致。

它会生成一个相当详细的计划,包含个别文件和函数的细节。例如,任务可能是 “我将在这个文件中为这两个组件的这三个代码路径添加指数退避,因为没有其他组件与 LLM 提供商通信”。

我知道有些人在这一步更喜欢让大型语言模型将计划写入文件,然后他们在文件上添加反馈,而不是直接与大型语言模型交流。这是一个个人喜好问题,因为我认为这两种方法都可以很好地工作,所以如果你更喜欢这样做,可以采用这种方式。个人而言,我更喜欢与大型语言模型聊天。

为了澄清,在这一步,我不仅仅是提示,我是在大型语言模型的帮助下塑造计划。我仍然需要经常纠正大型语言模型,要么是因为它错了,要么是因为它没有按照我希望的方式做事,而这正是我贡献的重要部分,也是我获得乐趣的部分。

这种方向让我能够称这些项目为“我的”,因为如果其他人使用相同的 LLM,他们会想出不同的东西。

当我满意我们已经解决了所有问题(LLM 在这里非常有帮助,它会问出它还不确定的问题,并给我提供选项)时,我终于可以批准计划。我已经要求架构师在我说“批准”之前不要开始任何事情,因为有些模型倾向于过于急切,在它们认为自己理解时就开始实施,而我希望确保我自己对它有信心。

然后,架构师会将工作分解为任务,并将每个任务写入计划文件,通常比我们的聊天更详细(和更底层),然后调用开发者开始工作。这给开发者提供了具体的方向,并最大限度地减少了开发者可以做的高级选择,因为这些选择已经为它做好了。

开发者

开发者可以使用较弱、更节省 tokens 的模型(我使用 Sonnet 4.6)。计划不应该给它太多自由,它的任务是严格按照计划实施更改。当它完成后,它会调用评审者来审查其工作。

评审者

每个评审者将独立查看刚刚实施的特性和差异,并对其进行批评。在这一步,我将始终至少使用 Codex,有时我会添加 Gemini,对于重要的项目,我还会添加 Opus。

这些反馈会返回给开发者,如果评审者同意,开发者会将其整合;如果评审者不同意,则会升级到架构师。我发现 Opus 在选择要实施的反馈方面非常出色,有时会忽略过于吹毛求疵的反馈(即难以实施且不太可能在实践中成为问题)。

显然,当我使用诸如“非常出色”这样的客观评估时,我真正的意思是“我非常同意它”。

总体方法

这种工作方式意味着我仍然了解在函数级别以上做出的所有选择,并且可以在后续运行中使用这些知识。我经常注意到 LLM 推荐可能在其他代码库中不错但在我的代码库中不可行或不是最优的东西,这表明 LLM 在研究代码时存在一些盲点。

我经常说“不,你应该使用 Y”,此时 LLM 会意识到 Y 实际上存在于代码中,并且比它推荐的方法更好。

失败模式

另一方面,当我对技术不够熟悉,无法掌握架构时,我往往无法发现 LLM 做出的糟糕决定。这会导致 LLM 在这些糟糕的决定之上构建更多内容,最终陷入无法解开混乱的境地。

你会知道这种情况发生了,当你不断告诉 LLM 代码无法运行时,它会说“我知道原因!让我修复它”,然后继续破坏更多东西。

这是一个真正的失败模式,现在已经发生过很多次了,这就是为什么我最终采用了这种工作流程。出于这个原因,我在规划时尽量多地了解相关知识,即使我不熟悉特定的技术。如果我能很好地引导 LLM,它就能在以后节省很多麻烦。

评论

(0)
未配置登录方式
暂无评论