我是如何利用大语言模型(LLM)帮助我写代码的(译)

原文链接:Here's how I use LLMs to help me write code

原文作者:Simon Willison

声明:本篇翻译是借助了 ChatGPT 辅助翻译完成~

关于使用大语言模型帮助编写代码的在线讨论中,总会出现一些开发者表示失望的评论。他们常常会问:自己是不是做错了什么?为什么有些人声称效果很好,而他们自己的尝试却没有收获?

使用大语言模型写代码确实是件困难且不直观的事情。要想摸清其中的门道,需要花费大量时间和精力,而目前市面上几乎没有什么指导可以帮助人们掌握最佳的使用方法。

如果有人告诉你"用 LLM 编码很简单",那他们很可能是在(无意中)误导你。他们可能确实偶然掌握了一些有效的使用模式,但这些模式对所有人来说并不是天然易懂的。

我已经用 LLM 写代码超过两年了,效果非常好。以下是我试图将这些经验和直觉传授给你的一种方式。


设定合理的预期

不要被"通用人工智能(AGI)"的炒作所迷惑------LLM 归根结底只是一个高级自动补全工具。它们只是预测一个个标记(token)的序列------而编写代码恰好本质上就是把正确的标记按顺序拼起来,因此只要你能正确引导它们,它们就会非常有用。

如果你指望这种技术能完美实现你的项目,而你自己不需要动脑筋,那你肯定会大失所望。

相反,应该将它们视为增强你能力的工具。我目前最喜欢的心智模型是:把它们想象成一个过于自信的结对编程助手,它查资料飞快,随时都能给出相关示例,并且能毫无怨言地完成那些枯燥重复的工作。

"过于自信"这一点很重要。它们绝对会犯错------有时候是细微的错误,有时候是严重的。有些错误非常不合常理------如果是一个人类搭档凭空编造出一个不存在的库或方法,你很快就会对他们失去信任。

但别犯一个错误:把 LLM 当人看待,并认为它犯的错误也应像人类那样被"取消资格"。

在使用 LLM 的过程中,你经常会发现它们"做不到"的地方。请把这些记录下来------这些是非常有价值的经验教训!它们也是评估新模型是否强大的参考标准之一:如果新模型能完成旧模型做不到的任务,那它确实有所进步。


注意训练数据的截止日期

每个模型都有一个非常关键的特性------训练数据的截止日期。也就是说,它们被训练的数据收集到某个时间点就停止了。OpenAI 的模型通常截止于 2023 年 10 月或 2024 年 5 月。其他厂商可能更晚。

对于写代码来说,这点尤为重要,因为它决定了模型熟悉哪些库。如果你用的库在 2023 年 10 月之后经历了重构或重大更新,那么某些 OpenAI 模型可能完全不了解它!

我从 LLM 得到的价值大到什么程度?大到我在选择库时会优先考虑:选那些稳定、流行、并且能确保有足够多示例被收录进训练数据的库。我会遵循"无聊技术"的原则:把创新留给你项目的独特卖点,其他部分就选成熟、靠谱的解决方案。

即使你选择的是 LLM 未接触过的库,它们仍然可以帮上忙------你只需要提供一些最近的用法示例,把这些作为提示的一部分提供给模型。

这就引出了和 LLM 协作的最重要原则:


上下文为王

大多数使用 LLM 获得好结果的技巧,都归结为如何管理它的上下文------也就是你和模型当前对话中的所有文本内容。

这个上下文不仅包括你当前输入的提示(prompt),还包括你之前所有的输入、以及 LLM 的回复。每条信息都构成了当前上下文的一部分。

你一旦开启一个新对话,LLM 的上下文就会被清空。知道这一点很重要,因为如果一个对话变得越来越没用,最好的做法就是"重开一个"。

一些 LLM 编码工具的上下文管理能力更强。例如 Claude 的 Projects 功能就支持预加载大量文本作为上下文,甚至现在可以直接从 GitHub 仓库导入代码。我现在经常在用这个功能。

像 Cursor 和 VS Code 的 Copilot 这类工具也会自动引入你当前编辑器中的上下文,包括文件布局。有些工具比如 Cursor 还能通过 @commands 机制进一步拉取文件或文档。

我自己主要还是喜欢直接用 ChatGPTClaude 的网页 / 应用界面进行操作,这样我更容易掌握到底哪些内容进了上下文。那些把上下文隐藏起来的工具,对我来说效率反而低。

你也可以主动利用上下文的存在。例如处理复杂编程任务时,可以先让 LLM 实现一个简化版、验证能正常工作,然后逐步迭代升级。

我通常会在开始一个新对话时先把已有的代码粘进去,作为上下文种子,然后再和模型一起修改。

我最喜欢的提示技巧之一是:扔进几个完整的示例,告诉 LLM:"我想做个类似的项目,用这些当灵感"。我曾写过一个结合了 Tesseract.js 和 PDF.js 的 JavaScript OCR 应用,就是这么干的------因为我用过这两个库,可以直接把工作代码粘进提示中。


让它给你选项

我的大多数项目一开始都有一堆待解的问题:我想做的这件事是否可行?有哪些方式可以实现?哪种方式最好?

我会把 LLM 当作调研工具来用。

我会提类似这样的提示:"Rust 有哪些 HTTP 库可用?附带用法示例" 或者 "JavaScript 里有哪些好用的拖放库?给我分别实现一个 demo"(这类我一般用 Claude)。

这时候训练截止日期同样重要,因为模型不会推荐很新的库。对我来说通常没问题------我并不想用最新的库,而是想用最稳定、问题最少的那个。

如果我决定用一个比较新的库,那我会自己去查资料,而不是依赖 LLM。

做项目最好的起点,是尽快做出一个原型,验证核心需求能不能实现。我经常能靠 LLM 在几分钟内就搞定原型------有时甚至是在手机上搞定的。


明确告诉它该做什么

一旦完成了前期调研,我的使用模式就会发生巨大变化。进入生产代码阶段后,我会非常"专制"地使用 LLM:我把它当成一个数字实习生,完全按照我详细的指令来敲代码。

以下是一个真实的例子:

写一个使用 asyncio + httpx 的 Python 函数,函数签名如下:

python 复制代码
python
复制编辑
async def download_db(url, max_size_bytes=5 * 1025 * 1025) -> pathlib.Path

给定一个 URL,该函数将数据库下载到临时目录并返回路径。但它需要在下载开始前检查 content-length 头,如果超出限制就抛出异常。下载完成后用 sqlite3.connect(...) 打开数据库并运行 PRAGMA quick_check 验证 SQLite 数据是否有效------如果无效就抛出异常。如果 content-length 撒谎(比如写了 2MB 实际下了 3MB),在检测到不一致时立刻抛出异常。

我完全可以自己写这个函数,但可能要花 15 分钟查各种细节才能写对。而 Claude 用了 15 秒就搞定了。

我发现 LLM 对这种函数签名特别敏感,响应效果非常好。我来设计函数,LLM 负责填充具体实现。

我接着会说:"现在用 pytest 写测试。"我依然指定技术栈------因为我要的是让 LLM帮我节省打字和查资料的时间。

如果你觉得"直接写代码不是比写一段英文指令快吗?",我的回答是:对我来说完全不是。因为代码必须正确,而英文指令可以含糊,可以简略,可以拼错词,甚至可以直接写"用那个常见的 HTTP 库"------如果你一时忘了库的名字。

好的 LLM 编码模型非常擅长"填空题"。而且它们比我勤快多了------会主动处理异常,加上准确的注释和类型标注。

你必须测试它写出来的代码!

上周详细写过:唯一绝对不能外包给机器的事情就是测试代码是否真正可运行

作为一名软件开发者,你的职责是交付能运行的系统。如果你没有亲眼看到它跑起来,那它就不算是一个"能用"的系统。你需要加强自己的手动 QA(质量保证)习惯。

这可能不够"高大上",但无论有没有使用 LLM,这始终是编写高质量代码不可或缺的一部分。


记住,这是一场对话

如果我不喜欢 LLM 生成的代码,它**永远不会在意你让它重构!**你可以说:

  • "把这段重复代码提取成函数"
  • "别用正则了,试试字符串操作"
  • 甚至直接说:"写得更好一点!"

LLM 输出的代码很少第一次就是最终版本 ,但它可以为你重写几十次,而不会感到烦躁或疲惫。

偶尔我会第一次就获得很棒的结果,随着练习越来越多,这种情况也更常见了。但我一般都会准备好多次跟进。

我经常在想,这或许是很多人没意识到的关键技巧之一:糟糕的首次输出不是失败,而是你引导模型朝着正确方向调整的起点。


使用能运行代码的工具

越来越多的 LLM 编码工具具备"执行代码"的能力。我对这类工具保持一定谨慎态度------有时候错误的指令可能造成真实破坏------所以我倾向于使用运行在安全沙箱中的工具

我目前最喜欢的几个:

  • ChatGPT Code Interpreter(代码解释器) :ChatGPT 可在 OpenAI 管理的 Kubernetes 沙箱虚拟机中直接写并运行 Python 代码。非常安全------甚至无法进行网络连接,最多也就是把临时文件系统搞乱(然后系统会重置)。
  • Claude Artifacts:Claude 可以为你构建完整的 HTML + JavaScript + CSS Web 应用,并在 Claude 界面中展示。运行环境是高度限制的 iframe 沙箱,防止出现数据泄漏等问题。
  • ChatGPT Canvas:ChatGPT 最近推出的新功能,功能与 Claude Artifacts 类似。我自己还没深入使用。

如果你愿意冒点险

  • Cursor 有 "Agent" 功能,Windsurf 等其他编辑器也逐步提供类似功能。我还没深入研究它们,暂不推荐。
  • Aider 是目前最领先的开源实现之一,也是"自我试用(dogfooding)"的一个典范 。最有趣的是:它的最新版本有 80% 是它自己写的!
  • Claude Code 是 Anthropic 最新发布的产品,我会在下面用一个详细例子展示如何使用。

"运行代码 + 自动修复"这一能力非常强大,所以我在选择 LLM 编码工具时,最看重的一点就是它是否能安全地执行并迭代我的代码。


"Vibe-coding" 是非常棒的学习方式

Andrej Karpathy 在一个多月前发明了一个新术语叫 vibe coding(氛围编码) ,这个词现在已经广为流传:

这是一种全新的编码方式,我称之为 vibe-coding。在这个过程中,你完全"沉浸于氛围",拥抱指数式爆发,甚至忘记代码本身的存在。[...]

我会随便说些"把侧边栏的 padding 缩小一半"这种懒得去找位置的指令。我总是点"全部接受",不再读 diff。出错了我就直接复制错误贴进去,一般都能修好。

Andrej 建议这种方式"适合周末做些丢得掉的小项目"。但它同时也是探索 LLM 能力的极佳方式------而且真的很好玩

学习 LLM 的最佳方式就是"玩它" 。随便抛出一些荒唐的想法,不断 vibe-coding 直到它"差不多能跑"------这是一个快速建立模型使用直觉、了解哪些能成哪些不行的好方法。

我其实在 Andrej 发明这个词之前就已经开始 vibe-coding 了!我的 GitHub 仓库 simonw/tools 目前有 77 个 HTML + JavaScript 应用,和 6 个 Python 应用,每一个都是通过 LLM 提示构建的

我从这个过程中学到了很多知识,并且每周还会加好几个新原型。你可以在 tools.simonwillison.net 直接体验这些小工具------这是该仓库的 GitHub Pages 展示站点。

我在去年 10 月写过一篇更详细的总结:Everything I built with Claude Artifacts this week

如果你想看每个工具对应的对话记录,它们几乎都在该页面的 Git commit 中有链接,也可以去我新建的 colophon 页面------里面汇总了所有对话链接。


使用 Claude Code 的一个完整例子

在写这篇文章时,我突然想到要做一个 tools.simonwillison.net/colophon 页面,用来更明显地展示每个工具的 Git 提交记录链接。

于是我就借这个机会,演示一下我的 AI 编码流程

这次我用的是 Claude Code,因为我希望它可以直接在我的本地机器上、对已有仓库运行 Python 脚本。

在会话结束时,我运行了 /cost 命令,它告诉我:

bash 复制代码
/cost
  ⎿  总花费:$0.61
     API 总时长:5 分 31.2 秒
     总耗时(真实时间):17 分 18.7 秒

从启动到完成,这个小项目一共花了我 17 分钟,API 成本是 61 美分。

我使用的是一种"威权式"(authoritarian)提示风格------我直接告诉模型要构建什么 。以下是我的提示流程(完整对话点此查看):

这个目录里的 HTML 文件几乎都是用 Claude 提示生成的,对应的提示内容都写在提交信息中。请写一个 Python 脚本,依次检查每个 HTML 文件的 Git 提交历史,从中提取出所有 URL,然后保存成一个结构如下的 JSON 文件:

json 复制代码
{
  "pages": {
    "file1.html": ["url1"],
    "file2.html": ["url1", "url2"]
  }
}

脚本命名为 gather_links.py,输出文件为 gathered_links.json

我其实对这个提示没有太多思考,基本就是一边想一边打字发给 Claude 的,更像是头脑风暴式起步。 我检查了初始结果,发现了一些问题:

它似乎只获取了 URL 的开头部分,而我需要的是完整的 URL,这些 URL 可能来自不同的网站------所以我需要抓取任何以 https:// 开头、以空格或提交信息结尾为终止的部分。

然后我改变了主意------我还想要完整的提交信息:

我更新了脚本------我想获取完整的提交信息 URL------新的格式应该是这样的:

json 复制代码
{
  "pages": {
    "aria-live-regions.html": {
      "commits": [
        {"hash": hash, "message": message, "date": iso 格式日期}
      ],
      "urls": [URL 列表,如之前一样]
    }
  }
}

像这样给出示例是快速获得你想要结果的绝佳方式。

注意,我从没看过 这个脚本(gather_links.py)是怎么写的!这完全是"凭直觉编码":我看的是它的结果,而不是具体的实现细节,全交给了 LLM 来处理。

生成的 JSON 看起来没问题,于是我说:

这运行得很棒。帮我写一个新的脚本叫 build_colophon.py,它会读取之前生成的 JSON 文件,然后构建并保存一个 HTML 页面。这个页面需要支持移动端,列出所有页面(每个页面提供超链接),然后对每个页面展示它的提交信息(将换行转换为 <br>,将 URL 转换为超链接,但不要做其它格式化)------另外还要展示提交时间和每条提交信息对应的链接,这些链接来自:github.com/simonw/tool...

Claude 知道 GitHub 的 URL 是怎么构成的,所以只要我说"链接到提交信息",并提供了 repo 名称,它就能自动推断出像这样的链接:
https://github.com/simonw/tools/commit/fd9daf885c924ba277806b3440457d52b0ad90a8

我发现 Claude 的网页设计默认风格还挺不错的------我说"页面需要移动端友好",就留它自由发挥了。

Claude 写出了一个页面,但有问题,于是我说:

"页面不对。ocr.html 有很多提交记录,但 colophon.html 中只显示了第一个提交的链接和标题,其余的都混在同一块里面了------每条提交信息都应该是单独的 HTML 区块,并带有链接和格式化日期。还有,日期显示应该包含小时和分钟。"

它自动修复了这个 bug,只剩下两个我自己想修改的小地方:

差不多完美了,但每个页面的提交信息应该按时间顺序排列------从最旧到最新。

接着又加了一条:

最后一个修改------现在页面是按字母排序的,我希望按最近修改的时间排序,最近的排在最前面。

就这样,这个项目就完成了!以下是 build_colophon.py 脚本,生成的页面效果相当不错

Tools Colophon

这个页面记录了 tools.simonwillison.net 上所有工具的创建过程,包括生成它们时使用的 Claude 对话链接。

例如:
socia-media-cropper.html

还有一个任务没完成:我需要把这个 colophon 页面部署到我的网站,但我不想把 colophon.html 文件本身提交到代码仓库里。我需要一个自定义的 GitHub Pages 构建流程。

于是我开启了一个新的 Claude Code 会话(从头开始),看看它是否能搞定这事:

"这个仓库是用 GitHub Pages 部署的。我想在部署前稍微修改下构建流程------先运行 python gather_links.py,生成 gathered_links.json 文件------再运行 python build_colophon.py,用它生成 colophon.html------我希望这两个文件都发布到 GitHub Pages 上,但不把它们提交进代码库。"

这次我非常仔细地观察它的操作------因为我对 GitHub Pages 的自定义构建流程并不熟,我既想学习这部分内容,也想避免它出错。

Claude 说它想创建这个文件,并征求我的许可:

yaml 复制代码
name: Deploy to GitHub Pages

on:
  push:
    branches: [ main ]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 需要完整的 git 历史记录供 gather_links.py 使用

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip

      - name: Generate links and build colophon
        run: |
          python gather_links.py
          python build_colophon.py

      - name: Setup Pages
        uses: actions/configure-pages@v4

      - name: Build with Jekyll
        uses: actions/jekyll-build-pages@v1

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3

  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

我觉得这看起来还不错。我很欣赏它注释的那句:"需要完整的 git 历史记录供 gather_links.py 使用"------我自己可能会漏掉这点。

然后它说需要把这些生成文件加到 .gitignore 里------听起来确实是个好主意。

它输出了一个总结:

我创建了一个 GitHub Actions 工作流用于 GitHub Pages,主要内容如下:

  • 创建了 .github/workflows/pages.yml 文件,配置如下:
  • 监听对 main 分支的 push 操作
  • checkout 时拉取完整 git 历史记录(供 gather_links.py 使用)
  • 设置 Python 3.12
  • 顺序执行两个脚本生成文件
  • 构建并部署到 GitHub Pages

创建了 .gitignore 文件,避免将生成文件提交到仓库中

现在当你 push 到 main 分支时,GitHub Actions 会:

  1. 运行 gather_links.py 脚本生成 gathered_links.json
  2. 运行 build_colophon.py 生成 colophon.htm
  3. 部署这两个文件(以及其它所有文件)到 GitHub Pages

我检查了下费用:

/cost

总花费:$0.1788

API 总用时:44.6 秒

墙钟时间:10 分钟 18 秒(我中途走神了)

所以通过 Claude API 花了 17 美分和 45 秒。(我中途分心了,所以总共花了 10 分钟。)这是完整的对话记录

这段代码看起来不会造成不可逆的破坏,于是我推送代码到 GitHub 看效果。

......它成功了!我新的 colophon 页面上线了。

但也有一个小问题。我在 GitHub Actions 的界面上看到不太对劲:

我本来只期待那个"测试"工作流,但为什么还有两个部署流程?

我猜可能是默认的 Jekyll 构建流程还在跑,而新的部署流程也同时运行了------最后只是运气好,新流程晚一点完成,覆盖了默认结果。

是时候自己动手看文档了!

我查到了 GitHub Pages 关于 自定义工作流的页面,但它没有解答我的问题。

我又尝试查看 GitHub Pages 设置页面,果然找到了这个选项:

我的仓库被设置为"从分支部署",所以我改成了"使用 GitHub Actions"。

然后我手动更新了 README.md,加了 colophon 页面的链接,推送了这个提交,触发了另一次构建。

这次只跑了两个作业,结果就是正确部署的网站:

只剩两个工作流在运行,一个是测试,一个是部署。

(之后我还发现一个 bug ------有些链接的 href 里错误地包含了 <br> 标签。我又开了一个 Claude Code 会话花了 11 美分修好了。)


更新:我进一步改进了 colophon 页面,加入了每个工具的 AI 生成简介

让人类接管的时刻

我很幸运,这个例子很好地说明了我最后想表达的一点:你需要准备好随时接手。

LLM 不是人类直觉和经验的替代品。我花了足够多的时间和 GitHub Actions 打交道,知道要注意哪些细节,而这次手动修复反而更快,比不停试 prompt 更有效。


最大优势是开发效率的提升

这个新的 colophon 页面从构思到最终上线只花了我不到半小时。

如果没有 LLM 协助,我可能根本不会动手做这个项目------花时间不值。

这就是我如此关注 LLM 生产力提升的原因:不是说工作做得更快了,而是我可以完成原本不会动手去做的项目

我在 2023 年 3 月就写过这事:AI 增强开发让我更大胆去实现新点子。

两年过去了,这种效果依旧没消失。

它也让我更快学习新东西------比如今天,我学会了如何使用 GitHub Actions 自定义 GitHub Pages 的构建过程------这是我将来一定还会用到的知识。

LLM 让我执行想法更快,就意味着我能实现更多想法,从而学到更多东西。


LLM 能放大已有的专业能力

其他人能像我一样做这个项目吗?可能不能!

我的 prompt 背后是 25+ 年的专业开发经验,包括对 GitHub Actions、GitHub Pages、GitHub 本身和各种 LLM 工具的了解。

我也知道这事是可行的。我非常熟悉这些工具,知道用 git 历史组装一个 HTML 页面是可以让一个好 LLM 做出来的。

我的 prompt 也体现了这一点------没有特别新颖的东西,我只是在指导它、测试它的结果,并在必要时纠正 bug。

如果我要写的是 Linux 内核驱动(一个我几乎不了解的领域),那我的流程会完全不同。


彩蛋:用 LLM 回答代码库的问题

如果你觉得让 LLM 给你写代码完全没有吸引力,还有一个可能更适合你的用法:

LLM 非常适合回答你对代码的疑问。

这几乎没有风险:最糟的情况是回答错了,但你还是可能比自己查几千行代码快得多。

关键技巧是:把整个代码库扔进一个大上下文的模型中,开始提问。

我现在最喜欢的模型是 Google 的 Gemini 2.0 Pro,目前可免费试用。

最近就这么用了:我试了一个新工具叫 monolith,是个 Rust 写的 CLI 工具,用于把网页及其依赖资源(CSS、图片等)打包成一个归档文件。

我想知道它是怎么实现的,于是:

bash 复制代码
cd /tmp
git clone https://github.com/Y2Z/monolith
cd monolith

files-to-prompt . -c | llm -m gemini-2.0-pro-exp-02-05 \
  -s 'architectural overview as markdown'

我用的是我自己写的 files-to-prompt 工具(去年由 Claude 3 Opus 帮我完成),它可以将整个目录下的代码内容汇总起来。然后我用我的 LLM 工具(带有 Gemini 插件)发送 prompt,系统提示是 "以 markdown 写出架构概述"。

Gemini 给了我一份详细文档,介绍这个工具的工作原理、每个源文件负责什么、用到了哪些 Rust crates,比如 reqwest, html5ever, markup5ever_rcdom, cssparser,还指出它不支持 JavaScript 执行,这是一个重要限制。

我每周用这个技巧好几次。这是研究新代码库的好方法------而且通常替代方案不是多花点时间,而是永远不会搞清楚。

我最近还写了这篇文章分享了更多例子。


相关推荐
EdisonZhou3 小时前
MAF快速入门(18)Agent Skill 快速开始
llm·aigc·agent
KEEN的创享空间4 小时前
AI编程从0到1之10X提效(Vibe Coding 氛围式编码 )09篇
openai·ai编程
AlienZHOU5 小时前
为 AI Agent 编写高质量 Skill:Claude 官方指南
agent·ai编程·claude
恋猫de小郭5 小时前
移动端开发稳了?AI 目前还无法取代客户端开发,小红书的论文告诉你数据
前端·flutter·ai编程
KaneLogger7 小时前
【翻译】打造 Agent Skills 的最佳实践
agent·ai编程·claude
王小酱7 小时前
Everything Claude Code 文档
openai·ai编程·aiops
雮尘8 小时前
如何在非 Claude IDE (TARE、 Cursor、Antigravity 等)下使用 Agent Skills
前端·agent·ai编程
会写代码的柯基犬8 小时前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
刘贺同学8 小时前
Day12-龙虾哥打工日记:OpenClaw 子 Agent 到底看到了什么?
aigc·ai编程
程序员鱼皮10 小时前
离大谱,我竟然在 VS Code 里做了个视频!
github·aigc·ai编程