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——我们一注意到这个问题就会抛出错误。

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

我可以让它编写测试来确认它有效。然后我可以要求它添加类型提示、文档字符串,甚至根据代码生成 Markdown 文档。

我可以让它编写一个脚本来自动化整个过程。我可以要求它考虑边缘情况、错误处理、性能优化。

让 LLM 编写它们的工作文档是一种确保它们实际做了它们说它们做的事情的好方法。

我最近构建了一个名为 Showboat 的工具,用于帮助 coding agent 编写演示它们工作的文档。

这是一个我经常使用的提示:

使用 showboat 在仓库中创建 walkthrough.md 文件,并在其中构建演示,使用 showboat note 添加注释,使用 showboat exec 加上 sed 或 grep 或 cat 或任何你需要的工具来包含你正在谈论的代码片段

Showboat 是我构建的帮助 coding agent 编写演示他们工作的文档的工具。你可以在这里看到 showboat --help 输出,它旨在为模型提供使用该工具所需的一切。

showboat note 命令向文档添加 Markdown。showboat exec 命令接受 shell 命令,执行它然后将命令和输出都添加到文档中。

通过告诉它使用”sed 或 grep 或 cat 或任何你需要的东西来包含你正在谈论的代码片段”,我确保 Claude Code 不会手动将代码片段复制到文档中,因为这可能会引入幻觉或错误的风险。

这非常有效。这是 Claude Code 用 Showboat 创建的文档,它详细讲解了所有六个 .swift 文件,并提供了关于代码如何工作的清晰且可操作的解释。

通过阅读这个文档,我学到了很多关于 SwiftUI 应用如何构建的知识,并吸收了一些关于 Swift 语言本身的可靠细节。

如果你担心 LLM 可能会降低你学习新技能的速度,我强烈建议采用像这样的模式。即使是一个大约 40 分钟的 vibe coded 玩具项目也可以成为探索新生态系统和学习一些有趣新技巧的机会。


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