Skip to content

我如何使用 LLM 帮助我编写代码

2025 年 3 月 11 日

关于 使用大型语言模型帮助编写代码 的在线讨论不可避免地会产生来自经验令人失望的开发者的评论。他们经常问他们做错了什么——为什么有些人报告如此好的结果,而他们自己的实验却证明不足?

使用 LLM 编写代码是困难的反直觉的。需要付出大量努力才能找出以这种方式使用它们的尖锐和柔和的边缘,几乎没有宝贵的指导来帮助人们找出如何最好地应用它们。

如果有人告诉你用 LLM 编码很容易,他们(可能无意地)误导了你。他们可能偶然发现了有效的模式,但这些模式并不是对每个人都自然而然地出现。

我使用 LLM 编写代码已经两年多了,取得了很好的结果。这是我尝试将一些经验和直觉转移给你的尝试。

忽略”AGI”炒作——LLM 仍然是花哨的自动完成。它们所做的只是预测一系列 token——但事实证明,编写代码主要是关于以正确的顺序将 token 串在一起,因此如果你将它们指向正确的方向,它们可以非常有用

如果你假设这项技术将完美地实现你的项目,而你不需要运用任何自己的技能,你很快就会失望。

相反,使用它们来增强你的能力。我目前最喜欢的心理模型是将它们视为一个过度自信的结对编程助手,他们查找东西的速度快如闪电,可以立即提出相关示例,并且可以毫无怨言地执行繁琐的任务。

过度自信很重要。他们绝对会犯错误——有时是微妙的,有时是巨大的。这些错误可能是深刻非人类的——如果一个人类合作者幻觉了一个不存在的库或方法,你会立即失去对他们的信任。

不要陷入拟人化 LLM 的陷阱,并假设会使人类信誉扫地的失败也应该以同样的方式使机器信誉扫地。

与 LLM 一起工作时,你经常会发现它们无法做的事情。记下这些——它们是有用的教训!它们也是有价值的例子,可以为将来储存——新模型强大之处的标志是当它为以前模型无法处理的任务产生可用结果时。

任何模型的一个关键特征是其训练截止日期。这是他们接受训练的数据停止收集的日期。对于 OpenAI 的模型,这通常是 2023 年 10 月或 2024 年 5 月。其他提供商可能有更近的日期。

这对代码极其重要,因为它会影响他们熟悉的库。如果你使用的库自 2023 年 10 月以来发生了重大突破,一些 OpenAI 模型可能不知道!

我从 LLM 获得了足够的价值,以至于我现在在选择库时故意考虑这一点——我坚持使用稳定性好且足够流行的库,这样它们的许多示例就会进入训练数据。我喜欢应用 无聊技术 的原则——在你的项目的独特卖点上创新,在其他方面坚持经过测试的解决方案。

LLM 仍然可以帮助你使用存在于其训练数据之外的库,但你需要投入更多工作——你需要在提示中向他们提供有关如何使用这些库的最新示例。

这带来了与 LLM 工作时需要了解的最重要的事情:

从 LLM 获得好结果的大部分技巧都归结为管理其上下文——作为当前对话一部分的文本。

这个上下文不仅仅是你输入给它的提示:成功的 LLM 交互通常采用对话的形式,上下文由来自你的每条消息 来自 LLM 的每条回复组成,这些存在于当前对话线程中。

当你开始新对话时,你会将该上下文重置为零。这很重要,因为对于已经停止有用的对话的修复通常是清空重新开始。

一些 LLM 编码工具超越了仅仅是对话。例如,Claude Projects 允许你用相当大量的文本预填充上下文——包括最近 直接从 GitHub 导入代码 的能力,我经常使用。

像 Cursor 和 VS Code Copilot 这样的工具会自动包含来自当前编辑器会话和文件布局的上下文,你有时可以使用像 Cursor 的 @commands 这样的机制来拉入额外的文件或文档。

我主要直接使用 ChatGPTClaude Web 或应用程序界面的原因之一是,它使我更容易理解到底是什么进入了上下文。对我来说模糊上下文的 LLM 工具效果较差

你可以利用之前的回复也是上下文的一部分这一事实。对于复杂的编码任务,尝试让 LLM 先写一个更简单的版本,检查它是否有效,然后迭代构建更复杂的实现。

我经常通过转储现有代码来开始新聊天,以种子化该上下文,然后与 LLM 一起工作以某种方式修改它。

我最喜欢的代码提示技术之一是放入几个与我想要构建的东西相关的完整示例,然后提示 LLM 使用它们作为新项目的灵感。当我 描述我的 JavaScript OCR 应用程序 时,我详细写了这一点,该应用程序结合了 Tesseract.js 和 PDF.js——这两个库我过去使用过,并且可以在提示中提供工作示例。

我的大多数项目都以一些开放性问题开始:我试图做的事情是否可行?我可以实现它的潜在方式有哪些?这些选项中哪些是最好的

我在这个初始研究阶段使用 LLM。

我会使用像”Rust 中 HTTP 库的选项是什么?包括使用示例”这样的提示——或者”JavaScript 中一些有用的拖放库是什么?为我构建一个演示每个库的工件”(对 Claude)。

训练截止日期在这里是相关的,因为它意味着不会建议更新的库。通常没关系——我不想要最新的,我想要最稳定的,并且已经存在足够长的时间,bug 已经被解决。

如果我要使用一些更新的东西,我会自己在 LLM 世界之外做这个研究。

开始任何项目的最好方法是从一个证明该项目关键要求可以满足的原型开始。我经常发现 LLM 可以让我在坐在笔记本电脑前的几分钟内——有时甚至是在手机上工作时——达到那个可工作的原型。

在完成初始研究后,我戏剧性地改变了模式。对于生产代码,我的 LLM 使用更加独裁:我将其视为数字实习生,根据我的详细说明为我输入代码。

这是一个最近的示例:

使用 asyncio httpx 编写一个具有以下签名的 Python 函数:

async def download_db(url, max_size_bytes=5 * 1025 * 1025): -> pathlib.Path

给定一个 URL,这会将数据库下载到临时目录并返回它的路径。但它会在开始流式传输该数据时检查内容长度标头,如果超过限制,则抛出错误。当下载完成时,它使用 sqlite3.connect(...) 然后运行 PRAGMA quick_check 来确认 SQLite 数据有效——如果无效则抛出错误。最后,如果内容长度标头对我们撒谎——如果它说 2MB 但我们下载了 3MB——我们一注意到这个问题就会抛出错误。

我可以自己编写这个函数,但这需要我大部分十五分钟来查找所有细节并使代码正常工作。Claude 在 15 秒内 就完成了。

我发现 LLM 对像我在这里使用的函数签名反应非常好。我充当函数设计者,LLM 负责根据我的规范构建主体。

我经常随后跟上”现在使用 pytest 为我编写测试”。同样,我指定我选择的技术——我希望 LLM 节省我输入已经在我脑海中的代码的时间。

如果你对此的反应是”当然,输入代码肯定比输入英文说明更快”,我只能告诉你,对我来说真的不再是这样了。代码需要是正确的。英文有巨大的捷径空间,以及模糊性和拼写错误,以及说”使用那个流行的 HTTP 库”这样的事情,如果你不能从头顶记住名称。

好的编码 LLM 非常擅长填补空白。它们也比我懒得多——它们会记得捕获可能的异常,添加准确的文档字符串,并用相关类型注释代码。

上周详细 写过这个:你绝对不能外包给机器的事情是测试代码是否实际有效。

你作为软件开发人员的责任是交付可工作的系统。如果你没有看到它运行,它就不是一个可工作的系统。你需要投资加强这些手动 QA 习惯。

这可能并不迷人,但它一直是交付好代码的关键部分,无论是否涉及 LLM。

如果我不喜欢 LLM 编写的东西,它们永远不会抱怨被要求重构它!“将重复代码分解到函数中”,“使用字符串操作方法而不是正则表达式”,甚至”更好地编写那个!“——LLM 首次产生的代码很少是最终实现,但它们可以为你重新输入数十次,而不会变得沮丧或无聊。

偶尔我会从我的第一个提示中获得很好的结果——我练习得越多,这种情况就越频繁——但我期望需要至少几次跟进。

我经常想这是否是人们 missing 的关键技巧之一——糟糕的初始结果不是失败,而是将模型推向你实际想要的东西的起点。

越来越多的 LLM 编码工具现在有能力 为你运行该代码。我对其中一些有点谨慎,因为错误的命令可能造成真正的损害,所以我倾向于坚持那些在安全沙箱中运行代码的工具。我目前最喜欢的是:

  • ChatGPT Code Interpreter,ChatGPT 可以编写然后在由 OpenAI 管理的 Kubernetes 沙箱 VM 中直接执行 Python 代码。这完全安全——它甚至不能发出出站网络连接,所以真正能发生的只是临时文件系统被破坏然后重置。
  • Claude Artifacts,Claude 可以为你构建一个完整的 HTML+JavaScript+CSS Web 应用程序,在 Claude 界面中显示。这个 Web 应用程序显示在一个非常锁定的 iframe 沙箱中,大大限制了它能做什么,但防止了意外泄露你的私人 Claude 数据等问题。
  • ChatGPT Canvas 是一个较新的 ChatGPT 功能,具有与 Claude Artifacts 类似的功能。我还没有足够探索这个。

如果你愿意活得更有风险一点:

  • Cursor 有一个可以这样做的”Agent”功能,Windsurf 和越来越多的其他编辑器也是如此。我还没有花足够的时间与这些来提出建议。
  • Aider 是这些模式领先的开源实现,是一个很好的 dogfooding 示例——最近的 Aider 版本有 80%+ 由 Aider 自己编写。
  • Claude Code 是 Anthropic 在这个空间的新进入者。我很快就会提供使用该工具的详细描述。

这个循环运行代码的模式是如此强大,以至于我主要根据它们是否可以安全地运行和迭代我的代码来选择我的核心 LLM 编码工具。

Andrej Karpathy 创造了这个术语 vibe 编码刚过一个多月,它已经坚持下来了:

有一种新的编码方式,我称之为”vibe 编码”,你完全沉浸在氛围中,拥抱指数级增长,忘记代码甚至存在。[…] 我要求最愚蠢的事情,比如”将侧边栏的内边距减少一半”,因为我太懒了,不想找到它。我总是”全部接受”,不再阅读 diff。当我收到错误消息时,我只是不加评论地复制粘贴,通常这样就能解决问题。

Andrej 建议这对”临时周末项目来说还不错”。这也是一个探索这些模型能力的极好方式——而且真的很有趣。

学习 LLM 的最好方法是玩弄它们。向它们抛出荒谬的想法并进行 vibe 编码,直到它们几乎有点工作,这确实是加速你建立对什么有效和什么无效的直觉的有用方式。

我在 Andrej 给它命名之前就已经在进行 vibe 编码了!我的 simonw/tools GitHub 仓库有 77 个 HTML+JavaScript 应用程序和 6 个 Python 应用程序,每一个都是通过提示 LLM 构建的。我从构建这个集合中学到了太多,我以每周几个新原型的速度向其中添加内容。

你可以直接在 tools.simonwillison.net 上尝试我的大多数——这是仓库的 GitHub Pages 发布版本。我在 10 月的 本周我用 Claude Artifacts 构建的一切 中更详细地记录了其中一些。


原文链接: https://simonwillison.net/2025/Mar/11/using-llms-for-code/