结论先行

  • 熬夜学习 AI,脱发却依然摸不着门道?
    • 花了一周时间,AI 还是帮不了你写文案?
  • 有人用 AI 高效工作,他们的秘诀是什么?
    • 是 AI 真那么强,还是我们没找对方法?
  • 程序员的 AI 日记曝光:
    • 他用 AI 重塑了工作流程,我们却在纠结 AI 会不会骗人。
  • 看完这篇文章,你可能会恍然大悟:
    • 被忽视的 AI 能力,才是真正的效率杀手锏。
    • 想像高手一样驾驭 AI,不再为脱发烦恼?继续阅读!

原文地址 : How I Use AI
我如何使用“AI”
作者:Nicholas Carlini 2024-08-01

我并不认为“AI”模型 (人工智能模型) 这个词有多好,我甚至讨厌它。(我指的是:大语言模型)。我认为现在对它的宣传有点过火了。

它根本不是真正的 AI (人工智能)。但我希望,无论是否喜欢这个词的人,都能看到这篇文章。所以为了营销、SEO 和吸引眼球,我不得不使用它。

的确,任何新技术都会吸引投机者。许多公司喜欢宣称他们在“使用 AI (人工智能)”,就像他们之前说他们使用了“区块链”技术一样。(类似的情况我们已经见过 很多, 很多, 很多, 很多)。我们可能正处于一个泡沫之中。互联网曾在 2000 年经历泡沫破裂,但我们现在所使用的互联网应用,在过去可是科幻小说中的情节。

但我认为,我们最近取得的进步并非只是炒作。在过去的一年里,我每周都会花几个小时与各种大语言模型互动,它们解决我提出的各种难题的能力给我留下了深刻的印象。因此,我的编程速度至少提高了 50%,无论是在我的研究项目还是个人项目中。

我发现网上大多数讨论大语言模型的人,要么过于乐观,认为所有工作都将在三年内自动化;要么过于悲观,认为它们毫无用处。

所以在这篇文章中,我想尝试让讨论更务实一些。我不会预测未来,只想列举 50 个我(一名研究机器学习的程序员和研究科学家)与不同的大语言模型进行的对话,这些对话显著提升了我的研究能力,并帮助我完成各种编程项目。其中包括:

  • 使用我从未接触过的技术构建完整的网络应用。
  • 学习如何使用各种我之前不熟悉的框架。
  • 将许多程序转换为 C 语言或 Rust 语言,从而将性能提升 10 到 100 倍。
  • 精简大型代码库,从而大大简化项目。
  • 为我去年撰写的几乎每一篇研究论文编写初始实验代码。
  • 自动化几乎所有重复性任务或一次性脚本。
  • 几乎完全取代了网络搜索,在设置和配置新的软件包或项目时为我提供帮助。
  • 在调试错误信息时,大约 50% 的情况下取代了网络搜索。

如果要将这些例子归类,可以分为“帮助我学习”和“自动化繁琐的任务”。帮助我学习显然很重要,因为它让我能够做到以前觉得困难的事情;而自动化繁琐的任务(对我来说)同样重要,因为它让我能够专注于自己最擅长的领域,解决那些真正棘手的问题。

最重要的是,这些例子都是我如何利用大语言模型来帮助我的真实案例。它们的目的不是为了炫耀某些令人印象深刻的功能,而是源于我完成实际工作的需要。这意味着这些例子可能并不光鲜亮丽,但我每天的大部分工作也并不光鲜,而现在的大语言模型能够帮我自动完成几乎所有这些工作。

我希望通过这篇文章,用大量的例子来展示在过去的一年中,我如何利用大语言模型来提高我的工作效率。请记住,当你读完这些例子的时候,你看到的只是我使用大语言模型的案例中不到 2% 的部分。

所以,当你读到感到疲倦时——你很可能会——请随时使用左侧的导航菜单,这是我(或者说,一个大语言模型)专门为这篇文章创建的,因为它的内容实在是太长了。

细微差别

如果说互联网有什么事情做得不好,那就是把握细微差别。我不会说现在的大语言模型会统治世界,也不会谈论未来的模型能做什么或不能做什么。我只想讨论现在的模型是否对我有用。

你可能会想——怎么会有人专门写一篇文章来论证语言模型的实用性呢?这难道不是显而易见的吗?但似乎有很多人——无论是在学术界、软件工程领域还是媒体领域——都声称大语言模型毫无贡献,只是一时的炒作,几年后就会销声匿迹,对世界没有任何影响。我认为他们是错的,因为现在的大语言模型已经非常有用了。

但是我觉得有必要说明一下,因为还有另一群人持有相反的观点:他们认为现在的大语言模型可以取代所有的程序员,人们不应该学习编程,因为明年他们都会失业。我不会直接反驳他们的观点(这不是本文的重点),但我想明确表示,我并不是在支持他们的观点。

我也不会试图论证“为了目的可以不择手段”,说我们应该不顾大语言模型的负面影响(其中有很多)而去训练它们。

我完全理解,这些模型会带来负面影响,甚至是极其负面的影响。我指的是所有方面,从虚假信息到滥用,从监控到失业。(或者,如果你相信某些人的说法,甚至是人类灭绝?)我很快会专门写一篇文章来阐述我对大语言模型的负面影响的看法。链接会放在这里。但这与语言模型是否有用的问题是分开的——正如我所说,这正是我想在这里讨论的内容。

我进一步理解,你可能因为大语言模型容易产生幻觉、重复事实以及由于缺乏鲁棒性而可能出现的严重错误等局限性而不愿意使用它们—— 我可能比你更了解这些局限性。但这篇文章不会讨论这些,因为我认为即使存在这些缺陷,模型仍然是有用的。

我更进一步地理解,训练这些模型的伦理问题非常值得商榷。也许你反感它们未经授权就使用人们的数据进行训练(我可能比你更了解这一点)。或者,你可能会想到那些拿着微薄的工资来专门训练这些模型的人。我同意,这些都是问题。但这篇文章也不会讨论这些。

正如我已经反复强调的那样:我在这里只讨论一个问题,那就是这些模型,以它们现在的形式,是否有用。

我的一些背景

一般来说,我不是一个轻易相信事物的人。例如:尽管十年前经历了安全社区的加密炒作,但我完全避免写任何关于区块链的论文。我从未拥有过比特币。它们基本上没有任何用途——除了赌博和欺诈。日复一日,我对所有的说法都持怀疑态度。每当有人告诉我“新技术将改变世界”时,我的一般反应是漠不关心。

所以,当我告诉你,当我第一次听到有人告诉我,人工智能将非常有用,并将显著改变我处理日常工作的方式时,我的反应基本上是一样的,那就是:“眼见为实”。

除此之外,我还是一名安全研究员。近十年来,我的日常工作就是展示人工智能模型在面对任何未经训练的环境时是如何彻底失败的。我已经证明,稍微扰动机器学习模型的输入,使其产生极其错误的输出,这是微不足道的;或者大多数机器学习模型会记住其训练数据集中特定的例子,并在使用时重复它们。我完全理解这些系统的局限性。

然而,我在这里要说的是,我认为当前的大语言模型为我的工作效率提供了自互联网诞生以来最大的提升。老实说,今天,如果你让我在完成一项从我的工作中随机选择的编程任务时,可以选择使用互联网或使用最先进的语言模型,我可能会在超过一半的时间里选择语言模型。

我如何使用大型语言模型

我之所以强调“帮助我”,

是因为我的工作方式可能与你截然不同。以下是我在过去一年里与多个大型语言模型(LLM)交互时总结的一些使用案例,仅供参考。

你可能会觉得这些案例很奇怪或不实用,甚至与你的需求无关。我对此表示理解,毕竟每个人对 AI 工具的使用方式都不同。这些案例只是我个人的经验分享。

帮助我构建完整的应用程序

去年,我制作了一个测验,让人们测试自己对 GPT-4 解决任务能力的预测水平。这个测验非常受欢迎,获得了超过一千万次的页面浏览量。而且你知道吗?我几乎完全依靠 GPT-4 编写了这个应用程序的初始版本。整个过程是通过一系列的提问来完成的,我从询问应用程序的基本结构开始,逐步扩展功能。整个对话记录超过了 30,000 字,这极大地考验了(当时最先进的)原版 GPT-4 模型的能力。

ChatGPT 对话记录 - 编写应用程序的初始版本

编写应用程序的初始版本

🧑‍🦰User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
我计划使用 Flask 构建一个类似问答游戏的应用程序。每个页面将展示一个问题和一个预期的答案。用户可以通过滑动条来预测他们认为 GPT-4 以该预期答案作答的概率。每个页面都会有一个提交按钮。当用户点击提交后,页面会显示 GPT-4 是否成功回答了这个问题,并提供模型的实际答案作为参考。页面还会有一个“下一个问题”的按钮。

我需要以下页面:

- GET /home:介绍比赛内容并解释规则。提供一个链接以开始第一个问题,同时为用户创建一个会话变量。
- GET /question? Id=[uuid]:显示对应 [uuid] 的问题,页面内容如前述。
- POST /score:接收 POST 参数 id=[uuid]、session=[uuid] 和 guess=[0 到 1 之间的浮点数]。
- 首先,将用户的预测值添加到该问题的所有预测列表中。
- 接着,计算用户的对数损失,并返回结果。
- 最后,告知用户其表现相对于其他人如何。

你需要设置以下全局变量:

- scores:一个字典,将问题 UUID 映射到一个预测列表,例如 {question 1: [.5, .1, .9]}。
- entries:一个字典,将用户 UUID 映射到包含问题 UUID 和预测值的字典,例如 {person 1: {question 1: .5, question 2: .8}}。
- questions:一个包含问题和答案对的元组列表,例如 [("1+1 等于多少?", "2")]。
- answers:一个由整数构成的列表,用于记录模型是否回答正确,例如 [0, 1, 0 ...]。

🤖 Assistant

你可以首先使用 pip 安装 Flask 和其他必要的库,如果尚未安装这些库的话:

1
pip install flask numpy scikit-learn

现在,我们来使用 Flask 开发你的游戏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from flask import Flask, request, session, redirect, url_for, render_template
from sklearn. Metrics import log_loss
from uuid import uuid 4
import numpy as np

App = Flask (__name__)
App. Config['SECRET_KEY'] = 'a very secret key' # replace with your secret key

# Global variables
scores = {}
entries = {}
questions = [("What is 1+1?", "2"), ("What is the capital of France?", "Paris")]
answers = [1, 0] # Suppose for these questions GPT-4 got the first one right and the second one wrong

@app.Route ('/home', methods=['GET'])
def home ():
session['id'] = str (uuid 4 ())
return render_template ('home. Html') # render a template that explains the game

@app.Route ('/question', methods=['GET'])
def question ():
qid = request.Args.Get ('id', default=0, type=int)
if 'id' in session:
question, expected_answer = questions[qid]
return render_template ('question. Html', question=question, expected_answer=expected_answer, id=qid)
else:
return redirect (url_for ('home'))

@app.Route ('/score', methods=['POST'])
def score ():
qid = request.Form.Get ('id', type=int)
guess = request.Form.Get ('guess', type=float)
user_id = session.Get ('id')

if user_id and qid is not None and guess is not None:
# Add to the list of all guesses for that question
scores.setdefault(qid, []).append(guess)

# Add to the person's guesses
entries.setdefault(user_id, {})[qid] = guess

# Compute the person's log loss
user_log_loss = log_loss([answers[qid]], [guess], labels=[0, 1])

# Compare with others
other_scores = scores[qid]
other_log_losses = log_loss([answers[qid]] \* len(other_scores), other_scores, labels=[0, 1])

comparison = 'better' if user_log_loss < other_log_losses else 'worse'

return render_template('score.html', user_log_loss=user_log_loss, comparison=comparison)
else:
return redirect(url_for('home'))

If __name__ == "__main__":
App.Run (debug=True)

这段代码作为初始代码,实现了三个端点:/home/question/score,并管理全局变量来记录用户预测和计算对数损失得分。

不过,要让代码正常运行,你还需要创建用于渲染的模板文件,包括 home.htmlquestion.htmlscore.html。这些模板文件应该包含你的 HTML 代码,并预留出动态内容的占位符。

建议你阅读 Flask 文档中关于会话和模板的部分,以便更好地理解它们的使用。

需要注意的是,使用全局变量来存储信息适用于用户较少且应用运行时间较短的小型应用或快速原型。但如果是在生产环境或有更多用户的应用中,你可能需要使用数据库或其他数据存储系统来管理这些信息。

如果你查看这段对话,你会发现各种类型的例子,从我用文字描述我想要的功能并要求模型提供完整实现的情况,到我请求具体更改的场景(如“能否将‘与平均分的比较’改为‘用 KDE 表示的百分位数’?”),再到我仅仅复制并粘贴错误信息,提出完全未指定的问题(如“绘图时:numpy. Linalg. LinAlgError: 奇异矩阵”),甚至还有一些我只是简单询问一次性问题的例子(如“如何用 JavaScript 添加一个内容从字符串加载的 iframe?”)。

总的来说,这能奏效是因为语言模型非常擅长解决那些已经被人们解决过的问题。事实上,这个测验的 99% 只是由一些基本的 HTML 和一个 python 网页服务器后端组成的,任何人都能轻松编写。这个测验之所以引人入胜,受到大家的喜爱,并不是因为背后的技术有多么先进,而是因为测验的内容本身有趣。因此,通过自动化处理所有重复性的工作,我可以很轻松地完成这个测验。

事实上,我非常有信心地说,如果没有语言模型的帮助,我可能根本不会去制作这个测验——因为我并不愿意花时间从头开始编写整个 Web 应用程序。要知道,我可是会编程的! 我相信,即使是现有的模型,也已经足够让绝大多数人通过简单的提问来解决他们以前从未能解决的复杂问题。

我还有一些类似的例子,我让模型为我编写了完整的应用程序,等它们上线时,我会明确说明这些应用程序是借助语言模型完成的。

作为新技术的导师

曾经我一直密切关注各种新框架的发展。但一个人的时间有限,由于工作原因,我主要专注于最新的研究进展,而不是 JavaScript 框架的最新动态。

这意味着,当我需要开展一个超出我研究领域的新项目时,通常有两个选择。首先,我可以使用已有的知识。虽然这些知识可能已经过时了十年或二十年,但对于小项目来说通常已经足够。或者,我可以尝试学习新的(通常更优越的)方法。

这时,语言模型就派上用场了。像 Docker、Flexbox 或 React 这样的框架或工具,虽然对我来说是新鲜的,但对其他许多人来说却非常熟悉。世界上可能有成千上万的人精通这些技术,而现有的语言模型也可以同样了解这些技术。

这意味着我不必去读那些假定读者有特定知识并想实现特定目标的静态教程。相反,我可以通过与语言模型互动,学习我完成任务所需的任何知识。
例如,今年早些时候,我在构建一个用于评估大语言模型的框架,并希望能够在受限环境中运行 LLM 生成的代码,以防止其意外删除我电脑上的文件或执行其他不必要的操作。Docker 是完成这一任务的理想工具,但我之前从未使用过它。

需要注意的是,_这个项目的目标并不是为了使用 Docker_。Docker 只是我实现目标所需的工具而已。我只需掌握 Docker 的 10% 核心知识,以确保我能够在最基本的操作下安全地使用它。

如果这是 90 年代,我可能需要买一本入门 Docker 的书籍,阅读前几章,然后翻阅找到我需要的部分,来了解如何实现我的需求。此后,情况有所好转;如果这是过去十年,我可能会在线搜索一些 Docker 教程,跟着步骤操作,然后查找出现的错误信息,看看是否有其他人遇到类似问题。

但如今,我只需请语言模型教我如何使用 Docker。 这就是我所做的。

当我成功设置并运行 Docker 后,发现它在 Linux 上运行时存在一些权限问题。我希望能解决这些权限问题,于是请模型帮助我处理这些问题。

通过这个过程,我了解到了 Podman,于是请模型将我所有特定于 Docker 的代码转换为对应的 Podman 版本。

And then when I wanted to figure out how to pass a host’s GPUs through in a docker container, I ask for that too.

如何开始新项目

年轻时,我的第一门编程语言是 Java。(这或许是个错误。)

甚至可以说整个 Java 语言都是个错误。但这另当别论。

我非常喜欢编程,但有一件事让我特别头疼,那就是面对新项目时那片空白的屏幕。尤其是在使用 Java 的时候!即便是编译一个简单的 hello world 程序,我也会困惑:“public static void main string args”究竟是什么意思?括号该放在哪里?哪些字母要大写?为什么这里用花括号而那里用方括号?

于是,我做了任何孩子都会做的事情——我请我父亲帮我完成。

二十年后的今天,我依然不喜欢用那些我不太熟悉的框架来启动新项目。光是处理模板代码就需要花费大量时间,而且我对自己在做什么也没有头绪。

举个例子,我最近想尝试写一些 CUDA 代码,来基准测试在 GPU 上运行的一种朴素贪婪搜索算法的性能,并与某个高效优化的 CPU 实现进行比较。

但问题是,我不会写 CUDA 程序。我会写 C 语言,也理解 GPU 的工作原理、内核的作用以及内存布局等方面的内容。但让我实际编写发送任务到 GPU 的代码?完全不知道从何入手。所以我就请模型为我写了一个 CUDA 程序的初步版本。它完美吗?当然不是!但这已经是一个不错的开始。而这正是我所需要的。

你可能会发现,这里的代码有时会出错!但这对我来说完全可以接受。我并不追求一个完美的解决方案,我需要的只是一个起点,然后我可以从那里开始。如果未来的模型更强大,那当然非常了不起。但即便是现在的模型,已经对我有很大帮助了。

或者,换个话题,谈谈我在家里进行的另一个完全不同的个人项目。我正在使用 Raspberry Pi Pico W。这是我第一次使用它。我希望它能帮我实现一些功能,尤其是在网络方面。同样,我相信可以在网上找到一些好的教程,教我如何实现这些功能。但是你有没有注意到,最近的网络搜索结果通常前 5 个都是一些低质量的内容农场,里面充斥着从 2008 年更新过来的、为了 SEO 优化而更新却依然存在问题的代码。

因此,我选择让语言模型直接教我如何完成这些任务。我之前使用过微控制器,了解它们的大致工作原理,但从未使用过 Pico W。我需要的只是一个起点,帮我解决依赖项的问题,然后我可以自己摸索其余的部分。

对于新的微控制器,我总是先写一个 LED 闪烁程序作为“hello world”。这样可以测试我是否能成功编译并上传代码,确认所有引脚设置是否正确,并基本了解我在做什么。所以,让我们请模型生成一个 LED 闪烁程序。(这在互联网上找得到吗?几乎可以肯定是能找到的。但还得费劲去搜。)

一旦我将这段代码运行起来,接下来我就知道如何继续了。我知道 python 是如何工作的(或许你会觉得惊讶!)。因此,我可以直接从这里开始编辑代码,因为已经处理好了 Micro python 的特殊部分。

当我遇到需要特殊处理的问题时,我可以直接请模型帮助我解决。比如在这里,我继续请模型帮我写了一个连接 wifi 的脚本

然后,当我再次遇到困难,这次是需要连接到 MQTT 服务器时,我再次请模型帮助我解决

我现在经常这样做。甚至这个部分开头的例子也不是虚构的——这是我在请教如何使用 Flexbox,因为我上次学习 HTML 布局方式时还在用 div 代替表格。

简化代码

作为一名安全研究员,我经常面对这样一种情况:需要分析一个包含几千行代码的研究项目代码仓库,以便了解其工作原理,然后找出可攻击的点。听起来似乎不难,如果所有人都写干净的代码的话,确实如此,但事实并非如此。研究人员通常不会因为代码的整洁而受到激励,因此很多人只会发布那些能运行的代码,无论其质量如何。(我也不例外。)

虽然我不能分享任何与研究相关的例子,但可以分享一个我个人项目中的例子。有人说我对康威的生命游戏有着不健康的偏爱。最近,我在尝试通过 python 快速评估一些生命游戏的模式。有一个叫 golly 的 C++ 工具非常适合这个任务,但我并不想把我的 python 代码重写成 C++。

golly 有一个可以满足我需求的命令行工具——我只需要找到正确调用它的方法。第一步是从支持大约 50 个不同命令行选项的 C++ 代码中,只让它执行我需要的那个功能。所以我将 500 行的 C++ 代码输入到 LLM 中,请求生成一个更简洁的文件来完成同样的任务

结果非常完美。随后,我请求模型生成了一个围绕这段 C++ 代码的 python 包装器,也同样顺利完成。

这属于那种因为麻烦而让我不愿意亲自去做的任务。但现在我可以直接请求完成它,这让我得到了一个比我原来的 python 代码快 100 倍的解决方案。

我发现自己经常这样做。这里还有一个例子,我在 python 中完成了完全相同的任务

再次强调,这些任务都不难。但每次这样做,我都能节省大量时间。这也是我认为 LLMs 如今非常了不起的一个原因:它并不光鲜,也不会让你在网上获得很多赞誉,因为说“我用 LLM 让生活更轻松”听起来很无聊,但它确实让我生活更轻松。

处理单调任务

我经常需要完成一些既无聊又不需要动脑筋的任务,但这些任务却必须完成。

事实上,我经常拖延任务的主要原因之一就是因为我知道这些任务会让人感到烦躁和痛苦。LLMs 大大减轻了这种痛苦,让我在面对任务时更加轻松,因为我只需要解决那些真正有趣的问题。因此,我想分享一些 LLMs 帮助我处理普通任务的例子。

比如,最近我需要反汇编一个用 python 3.9 编写的程序。大多数 python 反汇编工具只适用于 python 3.7 及之前的版本,无法处理我正在工作的 3.9 版本的二进制文件。

反汇编并不是一项特别困难的任务,主要是跟随跳转指令重构控制流,只要不犯错即可。因此,我没有花时间手动翻译几百行代码中的几千个操作码,而是让 LLM 来为我完成这项工作。结果非常理想!比我预想的效果好得多。以下是我通过模型完成这项任务的 不同对话。

另一个例子是,当我需要将非结构化数据转换为结构化格式时。比如,我在处理某个项目时,需要获取一份包含作者姓名的书名列表。我在网上找到了一些非结构化的数据,并请 LLM 帮我格式化这些数据

又比如,最近我在写一篇博客文章,讨论我如何破解了一种防御机制,并希望展示我更改的代码的完整 diff。所以我粘贴了(1)diff,(2)之前 HTML 化一个 diff 的示例,并请 LLM 以相同的格式生成该 diff

再举一个例子,作为我工作的一部分,我经常需要为我使用的资源生成引用。Google Scholar 对于论文引用生成非常方便,我可以直接复制粘贴引用信息。但引用网页时会稍显繁琐。最近,我开始请 LLM 帮我生成引用。(当然,我会检查这些引用是否正确!)

我可以举出许多类似的例子。但我想你已经明白了这一点。

我完全理解,这类任务可能会让人觉得简单过头了。但请记住,五年前的模型连拼凑一个连贯段落都很困难,更不用说帮你解决整个问题了。

让每个用户成为“高级用户”

当你看到别人使用某个工具,而他们的熟练程度不及你时,这可能会让你感到无奈。你会发现,他们在完成一项任务时,可能花费几分钟甚至几个小时,而这项任务本可以通过某种宏或并行应用来自动化处理。

然而,学习如何执行这些操作需要时间和挑战。

举个例子,最近我在尝试编写一个 python 程序,用来处理来自 Apple Lisa 键盘的键盘输入。我在网上找到了一些用 C 语言编写的代码,其中包含许多类似 #define KEYNAME key_code 的语句,我希望将这些语句转换为一个将整数代码映射到对应字符串的 python 字典。

我是一名 Emacs 用户,知道如何在 Emacs 中解决这个问题。其实,这并不算太难。以下是我刚刚记录下来的按键操作,它将产生这一效果:

C-h C-s #def [enter] M-f [delete] C-d M-f
C-[space] M-f C-w C-a C-y : “ M-f “, C-g C-] } C-[ {

而对于我来说,这几乎是自然而然的事情,因为到现在为止,我已经花了人生的一半时间来熟练掌握 Emacs,以至于这对我来说是自然的。你知道吗?即使现在我在编辑器中集成了 LLM,我仍然会输入什么?

C-h C-h rewrite these #defines to a dictionary of {keycode: string, …}

然后突然间,文本就在我眼前被重写了!

在这样的情况下,我认为 LLMs 对非专家的潜在实用性甚至比对专家更高。模型提高了所有人的起点,如果你以前完全不能做什么,现在突然间你可以做得更多。

作为 API 参考

真正的程序员在想知道一个工具如何工作时,会去阅读参考手册。但我是个懒惰的程序员;我只想要有人直接告诉我答案。所以现在我会问语言模型。

当我向一些人展示这些例子时,他们有些防御性地说:“LLM 并没有做什么是你用现有工具做不到的!”你知道吗?他们说得对。但你用搜索引擎能做到的事情,完全可以用一本实体书做到,同样你用实体书能做到的事情,也可以通过阅读源码做到。

但每一个方法都比前一个更容易。当事情变得更容易时,你会更频繁地做它,并且以不同的方式去做。

所以这就是我问“什么 $thing 给出所有剩余参数”的情景,并得到了答案。(紧接着是另一个“如何使用这个东西”的问题!)

或者,当我想知道如何在 LaTeX 中将文本变红时,我不再搜索或阅读文档,而是直接问模型。

当我想知道各种 GDB 命令在 LLDB 中的等价命令时,我会做同样的事情

或者当我想知道某个 find 命令是如何工作的

或者如何使用 lpr

Or how to rebind some latex commands.

这实际上是我最常用 LLM 的方式之一。我不能提供更多类似的示例链接,是因为我已经在 Emacs 和 Shell 中内置了查询 LLM 的工具。因此,90% 的情况下,我想执行这些任务时,根本不需要离开编辑器。

搜索难以找到的信息

过去,搜索互联网内容需要一定的技巧。你需要仔细选择查询中包含的词语,是用复数、单数还是过去式?哪些词需要排除?我需要 X AND Y,还是 X OR Y?

但这种情况已经不再常见。我不记得上次在 Google 中使用 OR 是什么时候了,也不记得上次使用减号 (-) 来排除结果是什么时候了。大多数情况下,现在你只需输入想找到的内容,搜索引擎就能找到。

然而,搜索引擎仍未完全支持自然语言查询。使用搜索引擎时,仍然感觉像是在玩“反向 Jeopardy”游戏,试图使用答案中的关键词而非问题。这是一种我们几乎忘记曾经学习过的技能。

对于如今的一些简单任务(并且随着时间的推移,越来越多的任务),语言模型显得更加出色。我只需输入 “我知道 + 对应于 __add__,那么 ~ 是什么”,模型就会告诉我答案是 __inv__。

这种问题在标准搜索引擎中很难找到答案。虽然我知道有办法通过搜索找到答案,可能通过输入“python documentation metaclass “add“”然后在页面中搜索 ~ 可以得到结果。但你知道还有什么更简单吗?直接向 LLM 提问。

每次这样做可能只节省几十秒,但当你在解决编码任务时,需要同时记住许多事情,能够随时提出问题并获得连贯的答案,真是令人惊喜。

当然,这并不是说现在的模型在这方面已经完美。语言模型只有在网上某些内容被足够多次提及时才会掌握。“足够多次”的标准取决于模型,因此我仍然需要考虑是问模型还是查找互联网。但模型只会越来越好。

或者,当我遇到随机崩溃时,我会将错误信息输入模型并请求解释,就像我在这里做的那样,当我输入 zsh no matches found “Remote wildcard transfer issue” 时。

又例如,去年我在写一篇博客文章时,我想让第一个词的首字母放大,并让其余的文字环绕在它周围,像我刚刚在这句话中做的那样。这种效果被称为 _drop cap_。但当时我并不知道这个术语。我只是知道我想要的效果,所以我问语言模型“我希望文本像一本精美的书一样环绕在 O 字母周围”,模型给了我正是我想要的答案

这个任务属于“我之所以做它完全是因为 LLMs”的另一类任务——如果让我自己花时间研究如何实现它,我根本不会觉得值得。但因为我可以直接问模型,所以我就去做了,这让我的帖子更漂亮了一些。

解决临时任务

程序通常分为两类。第一类是需要做得_尽善尽美_的程序;它们会存在较长时间,因此代码的整洁性非常重要,因为你可能需要维护它们好几年。另一类是只存在短短 25 秒的程序;它们帮助你完成某个任务,然后马上被丢弃。

我明白,第二类程序常常会演变成第一类程序。

在这种情况下,当我完全不在意代码质量时,而且这些程序是完全独立的,我现在几乎完全使用 LLMs 来编写它们。

需要注意的是:这些任务大多属于你看到后会说“就这样?”的那种。但正如我之前所说,我每天可以花在一个项目上的时间是有限的。如果我能够节省时间和精力,去编写一个我不会再用的程序,我会毫不犹豫地这样做。

最常见的例子之一是帮助我生成可视化某些数据的图表,这些数据是我研究实验的结果。我有许多类似的例子,可能有几十个。它们看起来基本相同,所以这里展示其中一个:

另一个类似的例子是,当我有一种格式的数据,而想将其转换为另一种格式的数据。通常这只是一个一次性的任务,完成后,我就会删除生成的脚本。

但这样的例子还有很多。通常,当我想编写一个非常简单的脚本时,我会直接让 LLM 为我全程编写。例如,这里我让 LLM 为我编写了一个将我的论文朗读出来的脚本,以确保没有愚蠢的语法错误。

很多时候,当我不太确定自己想要什么时,我会先让模型生成一些初始代码,然后再进行调整。例如,这是一个一次性的任务,我只需要快速处理一些数据。如果是在 2022 年,我可能会花两分钟用 python 写个程序,然后等上几个小时让它跑完,因为只需要运行一次——优化它花的时间比程序跑的时间还长。但现在?我肯定会花同样的两分钟让模型生成 Rust 代码来帮我处理数据

或者另一个例子,我请模型为我下载一个数据集,并进行初步处理。这件事对我来说简单吗?是的,可能很简单。但这不是我想要花时间去思考的任务;我更想专注于如何利用这个数据集进行研究。消除这些分心的事情,比仅仅节省几分钟时间更有价值。

还有一次,我在编写一个程序,希望用小方块 3 D 打印一些像素化的图像。为此,我需要将 PNG 转换为 STL 文件。这不是项目的重点,只是项目中必须完成的任务之一。所以我请 LLM 帮我完成这个任务。

再举一个例子,最近我想用 Docker Compose 来设置一个新项目。我遇到了一些问题,只是想让它尽快运行起来,之后再弄清楚问题所在。所以我反复操作,多次复制错误信息,直到最终得到一个可行的解决方案。查看这个过程

我还经常先请求一个完整的解决方案,然后再请求改进提示。在这次对话中,我首先请求了一个解析 HTML 的程序,然后进一步请求了有关 API 参考或改进方法的提示。

还有一次,我想跟踪我的电脑在一段时间内的内存和 CPU 使用情况。我可以花几分钟时间找出合适的命令并将它们串联成一个脚本来完成我的需求……或者我可以直接请语言模型为我生成。

最近,我在做一些电子项目。我有一个在 Arduino 上运行的 C 程序,但我想让它在 Raspberry Pi Pico 上用 Micropython 运行。这个转换过程没有什么有趣的地方,它只是必须完成。所以我请语言模型为我解决。

在另一个项目中,我需要用一些高级的机器学习模型在一个交互循环中对一些图像进行分类。我可以自己编写,也可以让模型帮我完成。

解释事物给我听

我最近开始接触电子学。我年轻时曾经做过一些电子项目,在大学也上过几门相关课程。但现在当我想实际进行项目时,我发现有许多小问题让我难以展开。

我可以去读一本关于实用电子学的书。事实上,我可能会在某个时间点去这么做,以便深入理解这个领域。但我不想把时间花在“学习”上。事实上,我进行电子学项目的一半原因是为了摆脱一天的论文阅读和写作。

在这种情况下,LLMs 表现出色。虽然它们的知识不如世界上最优秀的专家,但有成千上万甚至上百万人知道我可能提出的任何电子学问题的答案。这意味着语言模型也可能知道这些答案。它愿意为我提供所有问题的答案,这样我就可以享受自己想要的乐趣,而不必纠结于细节。尽管我本可以多花一些功夫通过搜索找到答案,但在经历了一天的复杂研究代码后,直接让模型替我完成任务的便捷性让人感到非常轻松。

这里有一些例子,展示了我如何向语言模型提问一些关于电子学的基本问题。这些答案是否完美?谁知道呢。但它们比什么都不知道要好得多。

(这篇文章已经相当长了,读到这里你可能和我写到这里一样感到疲惫。所以我不再对这些例子进行评论了。)

这里是我问了一些关于 PCB 设计的基本问题

这里是我问了一些关于焊接的基本问题

这里是我问了一些关于电容器的基本问题

这里是我问了一些关于 LED 的基本问题

我还可以继续列举,但我想你已经明白了这个道理。

解决已有方案的任务

几乎所有事情都曾被别人做过。你想做的几乎没有什么是全新且未曾尝试过的。而语言模型在提供以前见过的解决方案方面表现得非常出色。

在最近的一个项目中,我需要提高一些 python 代码的性能。所以我先(1)请 LLM 将其重写为 C 语言代码,然后(2)请它构建接口,以便我可以从 python 调用 C 代码。

这些任务并不难。将 python 转换为 C 语言,我相信自己可以在一两个小时内完成。虽然我不完全了解 python 到 C 的 API 是如何工作的,但我可以通过阅读文档来弄清楚。然而,如果需要我亲自去做,我可能永远不会完成这些任务,因为这并不是关键任务,我宁愿让计算机处理这个问题,而不愿意花时间优化这些不常用的代码。

将 python 转换为 C 语言(大部分情况下)是一个针对简单程序的技术性过程,且只有一种标准的 python 到 C 的调用约定。所以,我直接请求 LLM 帮我完成这个任务。

从那以后,我就习惯了这种方式,几乎每次需要快速运行的代码时,我都会描述我在 python 中的需求,并请求生成优化的 C 代码

有时我会做同样的事情,如果我认为 Rust 输出比 C 输出更容易判断正确性,我就会请求生成 Rust 代码而不是 C 代码

再举一个例子,使用 multiprocessing 库并行化一个 python 函数并不难。你需要编写一些模板代码,它基本上就能完成任务。但编写这些代码既麻烦,又会干扰实际工作。现在,每次我需要这样做时,我都会请 LLM 代劳。

或者,当我测试 API 时,我通常会先写一个 curl 请求来初步实现它。当它正常工作后,我希望以编程方式重复这项任务,通常会将其转换为 Python。以前,我可能会采取一些不理想的方法,比如调用 os. Popen () 来运行 curl 命令,但这并不是最佳方案。更好的方法是将其转换为 python 的 requests 库;但这需要时间,所以我可能不会去做。但现在,我可以直接请求 LLM 来完成这项转换,从而在更短的时间内获得更清晰的程序。

在即将开展的一个项目中,我可能会在这里讨论,我需要了解人们通常用作简单无线电发射器的东西。因为我想要的是人们的常见答案,LLM 是完美的选择!

解决常见错误

在 2022 年之前,当我遇到流行工具或库的错误信息时,我通常会按照以下步骤进行:

  1. 复制错误信息
  2. 粘贴到 Google 中
  3. 点击顶部的 Stack Overflow 链接
  4. 确认问题是否与我想要解决的相符;如果不相符,则返回第 2 步
  5. 应用最优解决方案
  6. 如果不奏效,返回第 2 步,修改搜索词,尝试其他方法

说实话,通常出错的工具与最终任务相去甚远,而你并不关心如何让它工作,只要它能正常工作就行。那么,到了 2024 年,这个流程发生了什么变化?

  1. 复制错误信息
  2. 向 LLM 询问“如何修复此错误?[error]”
  3. 按照 LLM 建议的逐步解决方案进行操作
  4. 如果不奏效,就说“这没有用”

现在,我没有相关的对话记录可以展示给你。(或者,在一小时的查找中我找不到。)但实际上,我已经将其直接集成到我的工作流程中了。

我是一个 Emacs 用户。我设置了环境,当我运行一个程序并且它以非零状态码退出(意味着出错)时,它会自动调用最新最快的 LLM 来解释问题,并同时请求生成一个可以直接应用于修复代码的补丁。

目前,模型在大多数情况下还不足以胜过我,但它们已经非常接近。而且偶尔当 LLM 修复了一个我知道因为某个错别字而导致的难以发现的 bug 时,我会感到惊喜。

以及许多其他事情

上面链接的所有对话只占我过去一年中与 LLM 互动总数的不到 2%。

如果你做个计算,会发现我在过去 12 个月里发起了超过 2000 次与 LLM 的对话。

我没有链接到这些其他对话,不是因为模型没能帮到我(尽管确实有不少这种情况),而是因为(1)它们中的很多与我已经提到的那些模式相同,或者(2)它们不容易解释清楚,无法让你看出它们为何对我有用。

我完全预计在未来我对这些模型的使用将继续增长。作为参考,2024 年我通过 Web 界面进行的 LLM 查询比 2023 年增加了 30%——至于 API 查询的增长,我无法计算,但我估计至少增加了 2 到 3 倍。

评估 LLM 能做什么,而不是它们不能做什么

我在面试求职者时收到的最佳建议之一是,根据他们能做的事情来评估,而不是他们不能做的事情。

如果我问你一些琐碎的问题,可能会让你显得能力不足。举个极端的例子:世界上有十亿人讲普通话,而我连数到十都不会。如果有人给我一份普通话的小学考试卷,我肯定会一败涂地。

即使在计算机科学领域,也有许多我完全不熟悉的领域。比如,我对 SQL 的理解仅限于如何写一个有效的 SELECT 语句——这确实是我唯一能写的语句。

我对数据库有很深的理论理解;我上过研究生级别的数据库课程,理解事务的运作原理及其实现方式,也了解各种索引策略的优缺点。但我从未实际操作过生产环境中的数据库,所以在实际应用中我无从下手。

因此,当我看到有人在网上争论说 LLM 只是炒作,因为“它们甚至不能做到 [X]”时,我很难理解这种观点。这里的 [X] 可能是:

  • … 数一个句子中的单词数量!
  • … 写一首每个词都以字母“a”开头的诗!
  • … 计算两个两位数的乘积!
  • … 从列表中随机选择一个元素!

但你上次真正需要做这些事情,并且认为 LLM 是合适的工具是什么时候呢?

同样的道理,我不会因为人类无法在脑中处理 64 位整数——这对计算机来说是微不足道的任务——就否定人类的价值。也不应该因为你可以构造出一个 LLM 无法解决的任务而否定它们的价值。重要的问题是,你能否找到 LLM 在其中能发挥作用的任务?

程序员们已经深刻理解了某种工具在不同场景下可能具有不同的用途。如果你要编写一个操作系统,也许你应该用 C 而不是 Python。没有人会因为“python 不能强制变量对齐到 32 字节边界”就觉得它可笑,因为这是抽象层次的问题。同样,语言模型也在一个非常高的抽象层次上运作;你不能指望它们解决连最简单程序都能解决的任务。但你可以指望它们解决_不同_类型的任务。

结论

我写这篇文章有两个目的。首先,正如开头所说,我想论证 LLMs 已经为我带来了极大的价值。其次,我注意到很多人说“我喜欢使用 LLMs 的想法,但不知道它们如何帮助我”。所以,如果你也是这样想的,希望你能通过这些例子了解到我是如何使用它们的。

对我来说,LLMs 能做的事情很多。它们不能做所有事情,甚至不能解决大部分问题。但当前的模型,已经为我带来了相当大的价值。

在展示这些例子后,我常常听到的反驳是:“那些任务很简单!任何计算机科学的本科生都能做到!”这确实没错。本科生可能通过几个小时的搜索,告诉我如何正确诊断 CUDA 错误,或者该重新安装哪些包。他们也许能在几个小时内用 C 语言重写那个程序,或者通过学习相关教材,教会我我想知道的内容。不幸的是,我没有这样一个随时解答我问题的本科生。但我有 LLM。因此,虽然 LLM 还不足以解决我编程工作中最具挑战性的部分,但它们能够轻松应对简单任务。

五年前,LLMs 最多只能写出看似英语的段落。当时,我们已经惊讶于它们能在句子之间形成连贯的想法,它们的实际用处几乎为零。但今天,它们已经将我的编程效率提高了至少 50%,并减少了很多繁琐的工作,让我能够完成一些我本不会尝试的项目。

因此,当有人说“LLMs 只是炒作”,并声称它们对任何人都没有实际价值时,我知道他们是错的,因为它们确实为我带来了价值。也许我是个例外,可能我是唯一一个发现这些模型对我有用的人。我只能代表我自己发言。但考虑到 LLMs 能显著提高我的生产力——一个在使用 LLM 前已有 20 年编程经验的人——我相信还有很多人也能从中受益。

而这只是基于当前可用的模型。接下来的五年会带来什么变化,让我们拭目以待。在我的下一篇文章中,我会对此进行一些推测。但我不确定我应该感到兴奋,还是害怕。

后记

WayToAGI 的创始人 AJ 把文章丢给我的时候,一下子被吸引住了。它和我写了 2 个月都没写完的一篇文章视角很贴合。希望能尽快完成。这篇文章的翻译和排版花费了 1 周时间,希望能给你带来一些启发。特别是和 AI 对话的部分,兼顾到国内小伙伴,都做了翻译。

翻译这篇文章的时候一度想要自己做一个 hexo 的插件,还是克制住了,找了 timeline 实现了类似的效果。另外获取这些内容,让 chatgpt 帮我写了一个代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
const { chromium } = require('playwright');
const fs = require('fs');
const { DateTime } = require('luxon');
const cheerio = require('cheerio');
const path = require('path');

async function main() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://nicholas.carlini.com/writing/2024/how-i-use-ai.html');

// Wait for the page to fully load
await page.waitForSelector('body');

// Extract links
const chatgptLinks = await page.evaluate(() => {
const links = Array.from(document.querySelectorAll('a[href*="chatgpt.com"]'));
return links.map(link => link.href);
});

console.log(`Found ${chatgptLinks.length} ChatGPT links`);

for (const link of chatgptLinks) {
await processChatgptLink(link, browser);
}

await browser.close();
}

let conversationCount = 0;



// 新增的函数,用于处理文件名
function sanitizeFilename(filename) {
// 替换不允许的字符为下划线
let sanitized = filename.replace(/[<>:"\/\\|?*\x00-\x1F]/g, '_');

// 移除首尾的空格和点
sanitized = sanitized.trim().replace(/^\.+|\.+$/g, '');

// 如果文件名为空,使用默认名称
if (!sanitized) {
sanitized = 'untitled';
}

// 确保文件名不超过255个字符(包括扩展名)
const ext = path.extname(sanitized);
const name = path.basename(sanitized, ext);
if (sanitized.length > 255) {
sanitized = name.slice(0, 251 - ext.length) + ext;
}

return sanitized;
}

async function processChatgptLink(link, browser) {
console.log(`Processing link: ${link}`);
const page = await browser.newPage();
await page.goto(link);

// Wait for the page to fully load
await page.waitForSelector('article');

// Extract the first h1 content and date
const { h1Content, dateContent } = await page.evaluate(() => {
const h1 = document.querySelector('h1');
const dateDiv = document.querySelector('div.pt-3.text-base.text-gray-400');
return {
h1Content: h1 ? h1.textContent.trim() : 'Default Title',
dateContent: dateDiv ? dateDiv.textContent.trim() : 'Unknown Date'
};
});

// Extract content
const content = await page.content();
const timelineContent = await convertToTimeline(content, h1Content);

// Increment conversation count
conversationCount++;
const paddedCount = String(conversationCount).padStart(3, '0');

// Generate filename and title
const today = DateTime.now().toFormat('yyyyMMdd');
let filename = `${today} 【AI赋能工作流】一个程序员的自白:我如何利用大语言模型将工作效率提升50%翻译 ChatGPT 会话 之 ${paddedCount} ${h1Content}.md`;
filename = sanitizeFilename(filename); // 使用sanitizeFilename函数处理文件名
const title = `【AI赋能工作流】一个程序员的自白:我如何利用大语言模型将工作效率提升50%翻译 ChatGPT 会话 之 ${paddedCount} ${h1Content}`;

// Create markdown content
const markdownContent = `---
title: ${title}
date: ${DateTime.now().toFormat('yyyy-MM-dd HH:mm')}
tags:
- 我如何用AI
categories: AI文本
cover:
---

# 写在前面

* 1
* 2
* 3


# ${h1Content}
${dateContent}
${link}

---

${timelineContent}
`;

// Save markdown content to file
fs.writeFileSync(filename, markdownContent);
console.log(`Saved content to ${filename}`);

await page.close();
}

async function convertToTimeline(content, h1Content) {
const $ = cheerio.load(content);
let timelineContent = '{% timeline ' + h1Content + ' %}\n'; // You might need to replace 'title' here

$('article').each((i, article) => {
const role = $(article).find('div[data-message-author-role]').data('message-author-role');
let contentText = '';

if (role === 'user') {
contentText = $(article).find('.bg-\\[\\#f4f4f4\\]').text().trim();
} else if (role === 'assistant') {
const contentDiv = $(article).find('div.markdown');
if (contentDiv.length > 0) {
contentDiv.children().each((j, child) => {
if ($(child).is('pre')) {
// 处理代码块
const codeElement = $(child).find('code');
const code = codeElement.text();
const classAttr = codeElement.attr('class') || '';
const languageMatch = classAttr.match(/language-(\w+)/);
const language = languageMatch ? languageMatch[1] : '';
contentText += `\`\`\`${language}\n${code}\n\`\`\`\n\n`;
} else {
// 处理普通文本
contentText += $(child).text() + '\n\n';
}
});
}
}

contentText = contentText.trim();

if (contentText) {
if (role === 'user') {
timelineContent += `<!-- timeline 🧑 User -->\n${contentText}\n<!-- endtimeline -->\n\n`;
} else if (role === 'assistant') {
timelineContent += `<!-- timeline 🤖 Assistant -->\n${contentText}\n<!-- endtimeline -->\n\n`;
}
}
});

timelineContent += '{% endtimeline %}';
return timelineContent;
}

main().catch(console.error);