我目前的大语言模型代码生成工作流

,

作者:Harper Reed
链接:My LLM codegen workflow atm

  • 作者分享了其利用大型语言模型进行软件开发的完整工作流程,涵盖从初步构思到最终代码执行的各个环节,并强调了分步迭代的重要性。
  • 该流程主要包括三个阶段:完善想法、制定计划和执行代码生成,并为每个阶段提供了详细的操作步骤与提示词示例,适用于全新项目。
  • 作者还探讨了在非“绿地”项目中进行迭代开发的方法,以及如何利用 LLM 进行代码审查、生成 GitHub 议题和编写测试用例等辅助任务。

简而言之:先头脑风暴明确需求,然后规划计划,再利用大语言模型(LLM)进行代码生成。离散循环。然后就是见证奇迹的时刻。✩₊˚.⋆☾⋆⁺₊✧

我一直在用大语言模型(LLM)构建许多小型产品。这过程既有趣又有用。然而,其中也存在一些可能浪费大量时间的陷阱。不久前,一位朋友问我如何使用 LLM 编写软件。我当时心想:“哎呀,这可有的聊了!” 于是便有了这篇文章。

(附言:如果你是人工智能的反对者——请直接滚动到文末)

我和许多开发者朋友聊过这个话题,我们都有类似的方法,只是在不同方向上略有调整。

这是我的工作流程。它基于我自己的实践、与朋友们(感谢 NiketeKannoObraKrisErik)的交流,以及遵循那些在各种糟糕的网络“垃圾场”(指 Hacker NewsTwitter 等)上分享的最佳实践。

这套方法现在行之有效,但两周后可能就不灵了,或者效果会好上两倍。¯_(ツ)_/¯

让我们开始吧

我总觉得这些人工智能生成的图片有点可疑。来跟我的“小丑”编码机器人天使打个招呼吧!

软件开发有很多路径,但我的情况通常是以下两种之一:

  • 全新项目(Greenfield)代码
  • 遗留的现代代码

我将展示我在这两种路径下的处理流程。

全新项目(Greenfield)

我发现以下流程非常适用于全新项目的开发。它提供了一种稳健的规划和文档化方法,并允许你通过小步骤轻松执行。

严格来说,右边确实是一片绿地。徕卡 Q, 2016 年 5 月 14 日

步骤 1:想法打磨

使用对话式 LLM 来打磨想法(我为此使用 ChatGPT 4o / o3):

请一次问我一个问题,以便我们能为这个想法制定一个详尽的、分步的规格说明。每个问题都应基于我之前的回答,我们的最终目标是得到一份我可以交给开发者的详细规格说明。让我们迭代进行,并深入探讨每一个相关细节。记住,一次只问一个问题。

这是我的想法:

<想法>

在头脑风暴结束时(它会自然而然地告一段落):

既然我们已经完成了头脑风暴过程,你能否将我们的发现汇编成一份全面的、可供开发者使用的规格说明?请包含所有相关的需求、架构选择、数据处理细节、错误处理策略以及测试计划,以便开发者可以立即开始实施。

这将输出一份相当可靠且直接的规格说明,可以交给规划步骤。我喜欢将其保存为仓库中的 spec.md 文件。

这份规格说明用途广泛。我们这里用它来生成代码,但我还用它来验证想法(通过让推理模型挑刺——必须深入挖掘!)、生成白皮书或商业模型。你甚至可以将其投入深度研究,换回一份万字的支撑文档。

步骤 2:规划

将规格说明传递给一个合适的推理模型(如 o1*, o3*, r1):

(这是 TDD 提示)

为构建此项目草拟一份详细的、分步的蓝图。然后,一旦有了可靠的计划,就将其分解为相互构建的小型迭代块。审视这些块,然后再进行一轮分解,将其细化为小步骤。审查结果,确保这些步骤足够小,可以通过严格的测试安全实施,但又足够大,能够推动项目前进。迭代直到你认为这些步骤的大小适合此项目。

至此,你应该已经有了基础,可以为代码生成 LLM 提供一系列提示,使其以测试驱动的方式实现每个步骤。优先考虑最佳实践、增量进展和早期测试,确保在任何阶段都不会出现大的复杂性跳跃。确保每个提示都建立在先前提示的基础上,并以将事物连接起来结束。不应有任何未集成到先前步骤中的悬空或孤立代码。

请务必将每个提示部分分开。使用 markdown。每个提示都应使用代码标签标记为文本。目标是输出提示,但上下文等也很重要。

<规格说明>

(这是非 TDD 提示)

为构建此项目草拟一份详细的、分步的蓝图。然后,一旦有了可靠的计划,就将其分解为相互构建的小型迭代块。审视这些块,然后再进行一轮分解,将其细化为小步骤。审查结果,确保这些步骤足够小,可以安全实施,但又足够大,能够推动项目前进。迭代直到你认为这些步骤的大小适合此项目。

至此,你应该已经有了基础,可以为代码生成 LLM 提供一系列提示,使其实现每个步骤。优先考虑最佳实践和增量进展,确保在任何阶段都不会出现大的复杂性跳跃。确保每个提示都建立在先前提示的基础上,并以将事物连接起来结束。不应有任何未集成到先前步骤中的悬空或孤立代码。

请务必将每个提示部分分开。使用 markdown。每个提示都应使用代码标签标记为文本。目标是输出提示,但上下文等也很重要。

<规格说明>

它应该输出一个提示计划,你可以用 aider、cursor 等工具执行。我喜欢将其保存为仓库中的 prompt_plan.md 文件。

然后我让它输出一个可以勾选的 todo.md。

你能否创建一个我可以作为清单使用的 todo.md 文件吗?请务必详尽。

你可以将其保存为仓库中的 todo.md 文件。

你的代码生成工具应该能够在处理过程中勾选 todo.md。这对于跨会话保持状态很有用。

太棒了,计划完成!

现在你有了一个稳健的计划和文档,这将帮助你执行和构建项目。

整个过程大概只需要 15 分钟。速度相当快。老实说,太疯狂了。

步骤 3:执行

执行阶段有很多选择。成功与否很大程度上取决于步骤 2 完成得如何。

我曾将此工作流程用于 GitHub WorkspaceaiderCursorClaude Engineersweep.devChatGPTClaude.ai 等等。我试过的所有工具效果都还不错,我想它也适用于任何代码生成工具。

然而,我个人更偏爱原生的 Claude 和 aider:

Claude

我基本上是与 claude.ai 进行结对编程,迭代地输入每个提示。我发现这种方式效果很好。来回沟通可能有点烦人,但大体上是有效的。

我负责初始的样板代码,并确保工具配置正确。这在开始阶段提供了一些自由度、选择和指导。Claude 有时倾向于直接输出 React 代码——因此,为你选择的语言、风格和工具打下坚实的基础会很有帮助。

然后,当遇到困难时,我会使用像 repomix 这样的工具进行迭代(稍后详述)。

工作流程如下:

  • 设置仓库(样板代码、uv init、cargo init 等)
  • 将提示粘贴到 Claude
  • 将 claude.ai 生成的代码复制粘贴到 IDE
  • 运行代码、运行测试等
  • 如果可行,则转到下一个提示
  • 如果不行,使用 repomix 将代码库传递给 Claude 进行调试
  • 冲洗并重复 ✩₊˚.⋆☾⋆⁺₊✧

Aider

Aider 用起来既有趣又有点怪异。我发现它能很好地承接步骤 2 的输出。只需很少的工作就能取得很大进展。

工作流程与上述基本相同,只是把提示粘贴到 aider 而不是 Claude。

然后 Aider 就会“搞定一切”,我就可以玩 Cookie Clicker 了。

题外话:Aider 在其 LLM 排行榜上对新的代码生成模型进行了非常出色的基准测试。我发现这是一个了解新模型效果的绝佳资源。

用 aider 进行测试很方便,因为它可以更自动化——aider 会运行测试套件并为你调试问题。

工作流程如下:

  • 设置仓库(样板代码、uv init、cargo init 等)
  • 启动 aider
  • 将提示粘贴到 aider
  • 看着 aider 跳舞 ♪┏(・o・)┛♪
  • aider 会运行测试,或者你可以运行应用进行验证
  • 如果可行,则转到下一个提示
  • 如果不行,与 aider 进行问答以修复问题
  • 冲洗并重复 ✩₊˚.⋆☾⋆⁺₊✧

结果

我用这个工作流程构建了许许多多的东西:脚本、Expo 应用、Rust CLI 工具等等。它适用于各种编程语言和场景。我确实很喜欢它。

如果你有某个或大或小的项目迟迟没有动手,我建议你试试这个方法。你会在短时间内惊讶于自己能取得多大进展。

我的“待办事项”列表已经空了,因为我把所有东西都做完了。我会一边看电影或做别的事情,一边构思新点子并迅速完成它们。这是多年来我第一次花时间接触新的编程语言和工具。这促使我拓宽了我的编程视野。

非全新项目:迭代与增量

有时你面对的并非全新项目,而是需要在已有的代码基础上进行迭代或增量工作。

这不是一片绿地。我祖父相机里的一张随机照片——摄于 60 年代的乌干达某地。

对此,我有一套略微不同的方法。它与上述流程类似,但更少依赖“规划”。规划是针对每个任务进行的,而不是整个项目。

获取上下文

我想每个深耕于人工智能开发的开发者都有自己的一套工具来做这件事,但你确实需要某种工具来抓取你的源代码并高效地将其“塞”给 LLM。

我目前使用一个名为 repomix 的工具。我在全局配置文件 ~/.config/mise/config.toml 中定义了一个任务集合,允许我对代码库执行各种操作(mise 规则)。

这是 LLM 任务列表:

LLM:clean_bundles 使用 repomix 生成 LLM 包输出文件
LLM:copy_buffer_bundle 将生成的 LLM 包从 output.txt 复制到系统剪贴板供外部使用
LLM:generate_code_review 使用 LLM 从存储在 output.txt 中的仓库内容生成代码审查输出
LLM:generate_github_issues 使用 LLM 从存储在 output.txt 中的仓库内容生成 GitHub 问题
LLM:generate_issue_prompts 使用 LLM 从存储在 output.txt 中的仓库内容生成问题提示
LLM:generate_missing_tests 为存储在 output.txt 中的仓库内容中的代码生成缺失的测试用例
LLM:generate_readme 使用 LLM 从存储在 output.txt 中的仓库内容生成 README.md

我生成一个 output.txt 文件,其中包含代码库的上下文。如果 token 消耗过快,文件过大,我会编辑生成命令,忽略掉与当前任务无关的代码部分。

关于 mise,一个很棒的特性是任务可以在工作目录的 .mise.toml 文件中被重新定义和重载。我可以用不同的工具来转储/打包代码,只要它能生成一个 output.txt 文件,我就能使用我的 LLM 任务。当不同代码库差异巨大时,这非常有用。我经常覆盖 repomix 步骤以包含更广泛的忽略模式,或者干脆使用更高效的工具进行打包。

一旦 output.txt 生成,我就将其传递给 LLM 命令进行各种转换,然后将结果保存为 Markdown 文件。

最终,mise 任务执行的是类似这样的命令:cat output.txt | LLM -t readme-gen > README.md 或 cat output.txt | LLM -m claude-3.5-sonnet -t code-review-gen > code-review.md。这并不复杂,LLM 命令承担了主要工作(支持不同模型、保存密钥和使用提示模板)。

例如,如果我需要快速审查和修复测试覆盖率,我会这样做:

Claude

  • 进入代码所在的目录
  • 运行 mise run LLM:generate_missing_tests
  • 查看生成的 Markdown 文件 (missing-tests.md)
  • 获取代码的完整上下文:mise run LLM:copy_buffer_bundle
  • 将其与第一个缺失测试的“问题”一起粘贴到 Claude
  • 将 Claude 生成的代码复制到我的 IDE 中
  • 运行测试
  • 冲洗并重复 ✩₊˚.⋆☾⋆⁺₊✧

Aider

  • 进入代码所在的目录
  • 运行 aider(始终确保在新的分支上进行 aider 工作)
  • 运行 mise run LLM:generate_missing_tests
  • 查看生成的 Markdown 文件 (missing-tests.md)
  • 将第一个缺失测试的“问题”粘贴到 aider
  • 看着 aider 跳舞 ♪┏(・o・)┛♪
  • 运行测试
  • 冲洗并重复 ✩₊˚.⋆☾⋆⁺₊✧

这是一种逐步改进代码库的相当不错的方法。它对于在大型代码库中完成少量工作非常有帮助。我发现用这种方法可以处理任何规模的任务。

提示词魔法

这些小技巧在挖掘项目可改进之处以使其更健壮方面效果显著。它们非常快速且有效。

以下是我用来深入研究现有代码库的一些提示词:

代码审查

你是一名资深开发者。你的工作是对这份代码进行彻底的代码审查。你应该将其写成 markdown 格式输出。包括行号和上下文信息。你的代码审查将传递给另一位团队成员,所以请务必详尽。在编写代码审查之前请深入思考。审查每一个部分,不要产生幻觉。

GitHub 问题生成

(我需要自动化实际的问题发布!)

你是一名资深开发者。你的工作是审查这份代码,并写出你看到的关于代码的主要问题。这些问题可能是 bug、设计选择或代码整洁性问题。你应该具体明确,并且做得非常好。不要产生幻觉。先静静思考,然后行动——写出问题。这些问题将交给一位开发者去执行,所以它们应该采用与 GitHub 问题兼容的格式。

缺失的测试

你是一名资深开发者。你的工作是审查这份代码,并写出一份缺失的测试用例列表,以及应该存在的代码测试。你应该具体明确,并且做得非常好。不要产生幻觉。先静静思考,然后行动——写出问题。这些问题将交给一位开发者去执行,所以它们应该采用与 GitHub 问题兼容的格式。

这些提示词已经相当陈旧过时了(恕我直言,可以称之为“老古董提示词”)。它们需要一些重构。如果你有改进建议,请告诉我。

滑雪 ᨒ↟ 𖠰ᨒ↟ 𖠰

当我向别人描述这个过程时,我会说:“你必须非常积极地追踪进展,因为你很容易就超前失控了。”

不知为何,在谈论 LLM 时,我经常说“滑雪滑过了头”(over my skis,意指操之过急、力不从心)。我不清楚原因,但这个说法与我产生了共鸣。也许是因为它就像在美丽的粉雪上平稳滑行,然后突然之间你大喊:“这他妈到底是怎么回事!”,完全迷失方向,然后突然掉下悬崖。

我发现使用规划步骤(如上述全新项目流程)有助于控制局面。至少你有一份可以反复核对的文档。我也相信测试很有帮助——尤其是在你进行狂野风格的 aider 编码时。它有助于保持代码的良好和严谨。

尽管如此,我仍然经常发现自己“滑雪滑过了头”。有时短暂休息或散散步会有所帮助。在这方面,这是一个正常的解决问题的过程,只是速度被加快到了极致。

我们经常让 LLM 在我们并不那么荒谬的代码中加入一些荒谬的东西。例如,我们曾让它创建一个传说(lore)文件,然后在用户界面中引用这些传说。这是针对 Python CLI 工具的。于是突然间,代码里就有了传说、故障风格的界面等等。所有这些都是为了管理你的云函数、待办事项或其他任何东西。可能性是无限的。

我好孤独 (。•́︿•̀。)

我对这些工作流程的主要不满在于,它们基本上是单人作业——也就是说,这些界面都是单人模式。

我曾独自编码多年,也曾结对编程多年,还在团队中编码多年。与人协作总是更好的。这些工作流程在团队中不易使用。机器人会冲突,合并代码简直是噩梦,上下文也变得复杂。

我真心希望有人能解决这个问题,让使用 LLM 编码变成一种多人游戏,而不是单打独独的黑客体验。这里有巨大的机会去改进并使之变得出色。

快去干活!

ⴵ 时间 ⴵ

所有这些代码生成都加速了我个人能够产出的代码量。然而,这带来了一个奇怪的副作用。我发现自己在等待 LLM 消耗完它的 token 时,有大量的“空闲时间”。

我对此记忆犹新,仿佛就在昨天。

我已经调整了我的工作方式,开始引入一些实践来尝试利用这些等待时间:

  • 我开始另一个项目的“头脑风暴”过程
  • 我听唱片
  • 我玩 Cookie Clicker
  • 我和朋友以及机器人聊天

能像这样高效工作真是太棒了。黑客!黑客!黑客!我想不出还有什么时候我在编码方面有如此高的生产力。

“黑子”们 ╭∩╮( •̀_•́ )╭∩╮

我的很多朋友都说:“去他妈的 LLM。它们什么都做不好。”我不介意这种观点。我虽然不认同,但我认为保持怀疑态度很重要。讨厌人工智能的理由有很多。我主要的担忧是能源消耗和环境影响。但是……代码必须流动起来。对吧……唉。

如果你愿意了解更多,但又不想深入研究成为一名“赛博格程序员”——我的建议不是改变你的观点,而是去读读 Ethan Mollick 关于 LLM 及其应用的书:《与 AI 共存:与人工智能共同生活和工作》。

这本书很好地解释了其益处,而不是那种技术无政府资本主义兄弟类型的著作。我觉得它非常有帮助,并且和读过这本书的朋友们进行了许多有益且细致入微的对话。强烈推荐。

如果你持怀疑态度,但又有点好奇——随时联系我,让我们一起聊聊这疯狂的一切。我可以向你展示我们是如何使用 LLM 的,也许我们可以一起构建些什么。

感谢 DerekKannoObraErik审阅此文并提出修改建议。我非常感激。