让 GPT-4 从头开发一个扩展,会有什么情况发生?它的代码真的可以安装、编译、运行吗?一位开发者 KEVIN LIN 进行了一次测试。
原文链接:https://bit.kevinslin.com/p/leveraging-gpt-4-to-automate-the
作者 | KEVIN LIN
编译 | ChatGPT 责编 | 苏宓
出品 | CSDN(ID:CSDNnews)
最近,我一直在尝试使用语言模型(LLM)来编写代码。我发现它们非常擅长生成小而完整的代码片段。不幸的是,如果需要生成更多的代码,就需要人类评估 LLM 的输出,并提供适当的后续提示,进行优化,之后才可以使用。
目前,大部分“GPT 写了 X”的案例都是如此——人类充当 LLM 的 REPL(Read-Eval-Print Loop,读取-求值-输出循环),仔细引导大模型产生功能性的结果。这并不是贬低这个过程的意思——它的可行性实则令人惊叹。但我们能否进一步呢?我们能否使用 LLM 一次性生成复杂程序的所有代码,而无需任何人类介入呢?
编写一个 VSCode 扩展
为了测试 GPT-4 生成复杂程序的能力,我让它创建一个 VSCode 扩展,允许用户能够调整选定的 Markdown 文本的标题级别。这个任务需要:
设置
在这个实验中,我使用 GPT-4 进行所有的生成任务。我发现 GPT-4 在当今的模型中效果最好。
此外,我使用了开源的 smol-ai 框架(https://github.com/smol-ai/developer)来生成代码。
smol-ai 在 README 中的描述如下:
这是一个“初级开发人员”代理(也称为 smol dev)的原型,一旦你给它一个产品规范,它就能为你搭建整个代码库,但它并不会过度承诺通用人工智能(AGI)。与创建特定、固定、一次性的启动器(如 create-react-app 或 create-nextjs-app)不同,这基本上是一个创建任何应用的框架,你可以与 smol dev 紧密循环开发。
我喜欢 smol-ai 的简洁性。整个代码生成逻辑都在一个包含三个主要函数的单个 Python 文件中:
需要注意的是,smol-ai 还提供其他功能,比如能够调试错误和根据当前代码库生成提示。这些在本文中没有展开来讲。
Prompt
我先基于给出一段提示语,来描述我的问题:
python main_no_modal.py "a vscode extension that lets the user adjust the heading level of the selected text. it should have three commands: increase heading level, decrease heading level, and set heading level"
请注意,这个提示是简短且概述问题有些笼统。我想看看 GPT 在没有输入任何额外的细节或超出训练集中特定领域知识的情况下,能做得多好。
输出
系统花费几分钟生成代码后,smol-ai 创建了以下文件:
.
├── extension
│ ├── package.json
│ ├── src
│ │ ├── commands
│ │ │ ├── decreaseHeadingLevel.ts
│ │ │ ├── increaseHeadingLevel.ts
│ │ │ └── setHeadingLevel.ts
│ │ └── extension.ts
└── shared_dependencies.md
请注意,shared_dependencies.md 的创建是作为对共同依赖的最佳猜测。在我们的例子中,它创建了一个文件,内容如下:
the app is: a vscode extension that lets the user adjust the heading level of the selected text. it should have three commands: increase heading level, decrease heading level, and setheadinglevel
the files we have decided togenerate are: tsconfig.json, package.json, utils.ts, extension.ts
Shareddependencies:
1.Exported variables:
- adjustHeadingLevel ( fromutils.ts)
2.Dataschemas:
- None
3.IDnamesofDOM elements:
- None
4.Message names:
- None
5.Functionnames:
- increaseHeadingLevel
- decreaseHeadingLevel
- setHeadingLevel
6.Dependencies:
- None
代码
extension.ts 是开发一个 VSCode 扩展的入口点。在这份文件中,我们定义了指定的三个命令。
import* asvscode from'vscode';
import{ increaseHeadingLevel } from'./commands/increaseHeadingLevel';
import{ decreaseHeadingLevel } from'./commands/decreaseHeadingLevel';
import{ setHeadingLevel } from'./commands/setHeadingLevel';
exportfunctionactivate( context: vscode.ExtensionContext) {
context.subions.push(
vscode.commands.registerCommand( 'extension.increaseHeadingLevel', increaseHeadingLevel),
vscode.commands.registerCommand( 'extension.decreaseHeadingLevel', decreaseHeadingLevel),
vscode.commands.registerCommand( 'extension.setHeadingLevel', setHeadingLevel)
);
}
exportfunctiondeactivate( ) {}
下面是 decreaseHeadingLevel.ts 中的逻辑:
import* asvscode from'vscode';
exportasyncfunctiondecreaseHeadingLevel( ) {
consteditor = vscode.window.activeTextEditor;
if(!editor) {
return;
}
constdocument= editor.document;
constselection = editor.selection;
constselectedText = document.getText(selection);
constheadingRegex = /^(#+)\s(.*)$/;
constmatch = selectedText.match(headingRegex);
if(!match) {
vscode.window.showErrorMessage( 'Invalid selection. Please select a heading to decrease its level.');
return;
}
constcurrentLevel = match[ 1].length;
if(currentLevel === 1) {
vscode.window.showErrorMessage( 'Cannot decrease heading level. Already at the lowest level.');
return;
}
constnewLevel = currentLevel - 1;
constnewHeading = '#'.repeat(newLevel) + ' '+ match[ 2];
awaiteditor.edit( ( editBuilder) => {
editBuilder.replace(selection, newHeading);
});
}
这段代码检查一个活动的编辑器,如果存在的话,就会获取选定的文本并通过正则表达式寻找一个 markdown 标题。如果找到一个标题,并且当前的标题级别还没有达到最低级别,它就会降低标题级别。
乍一看,这个逻辑没有什么不妥。它执行命令并检查边缘情况。它甚至提供了有用的错误信息,这已经使它领先于大多数人工生成的程序......
测试扩展
为了测试这个扩展,我们需要成功执行以下步骤:
1. 安装依赖项
2. 编译代码
3. 运行扩展
第一步:安装
我们在尝试安装依赖项时遇到了第一个问题:
$ yarn
Couldn't find any versions for"vscode-test"that matches "^1.6.2"
? Please choose a version of "vscode-test"from thislist: (Use arrow keys)
❯ 1.6.1
问题 1--找不到 vscode-test
检查 package.json 的结果如下:
{
"name": "adjust-heading-level",
...
"engines": {
"vscode": "^1.62.0"
},
"devDependencies": {
"@types/node": "^14.17.0",
"@types/vscode": "^1.62.0",
"type": "^4.4.2",
"vscode": "^1.1.37",
"vscode-test": "^1.6.2"
},
}
vscode 引擎决定了 vscode 的最低版本。现在的(截至 2023-05-23)引擎版本是 1.78。1.62.0 版本是在 2021 年 10 月 21 日发布的。
这与 GPT-4 的知识截止日期相吻合:
GPT-4 通常缺乏对其绝大部分数据截止日期(2021年9月)之后发生的事件的知识。
1.6.2 的vscode-test 版本与 1.62 在数字上相差了一个点,这表明 GPT 很可能对这些数字产生了幻觉,出现了错误。
在任何情况下,这很容易解决,只要指定正确的版本号,并重新安装,就可以修复:
- "vscode-test": "^1.6.2"
+ "vscode-test": "^1.6.1"
所以,重新运行安装过程,第二次就成功了。
$ yarn
...
[ 3/5] 🚚 Fetching packages...
[ 4/5] 🔗 Linking dependencies...
[ 5/5] 🔨 Building fresh packages...
✨ Done in4.31s.
第二步:编译构建
由于 Type 是一种编译语言,我们需要执行一个构建步骤,将代码编译成 Java。package.json 中带有以下脚本:
"s": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "npm run compile && node ./node_modules/vscode/bin/test"
},
我们可以通过运行编译脚本来构建代码。这就是我们遇到下一个问题的地方:
$ yarn compile
warning package.json: No license field
warning adjust-heading-level@ 0.1.0: The engine "vscode"appears to be invalid.
$ tsc -p ./
errorTS5057: Cannot finda tsconfig.json file at the specified directory: './'.
errorCommand failed with exitcode 1.
info Visit https://yarnpkg.com/en/docs/cli/run fordocumentation about this command.
问题2 - 找不到 tsconfig.json 文件
Type 需要一个 tsconfig.json 文件来编译成 Java。如果你记得我们最初的文件布局,没有 tsconfig.json 文件。
.
├── extension
│ ├── package.json
│ ├── src
│ │ ├── commands
│ │ │ ├── decreaseHeadingLevel.ts
│ │ │ ├── increaseHeadingLevel.ts
│ │ │ └── setHeadingLevel.ts
│ │ └── extension.ts
└── shared_dependencies.md
我们可以通过添加配置并重新构建来补救这个问题。但现在我们遇到了更多的问题:
$ tsc --init
$ yarn compile
src/commands/decreaseHeadingLevel.ts: 1: 25- error TS2307: Cannot find module'vscode'orits corresponding type declarations.
1import* asvscode from'vscode';
~~~~~~~~
src/commands/decreaseHeadingLevel.ts: 30: 24- error TS7006: Parameter 'editBuilder'implicitly has an 'any'type.
30awaiteditor.edit( (editBuilder)=> {
~~~~~~~~~~~
src/commands/increaseHeadingLevel.ts: 1: 25- error TS2307: Cannot find module'vscode'orits corresponding type declarations.
...
Found 7errors
问题3--找不到模块
Type 找不到 vscode 模块的原因是我们使用的导入语句的语法:
//thisisfailing
import* asvscode from'vscode';
//thiswould work
importvscode from'vscode';
语法不同的原因来自于 CommonJs 和 ES 模块之间的差异,以及它们如何导出依赖关系,以及 Type 如何转译这些导出。模块兼容性中令人抓狂的怪异现象本身就可以再写一篇博文来详述了。只是现在,我们可以通过在 tsconfig.json 中禁用 esModuleInterop 来解决这个问题。
@@ -71,7 +71,7 @@
- "esModuleInterop": true,
+ "esModuleInterop": false,
请注意,从 Type 4.4 开始,esModuleInterop 默认改为 true。这是在2022 年 3 月 15 日发布的--在 GPT-4 所获得的最新知识截止日期之后。
让我们再次尝试构建。这次只有一个错误了:
$ yarn compile
src/commands/setHeadingLevel.ts: 2: 10- error TS2305: Module '"../extension"'has noexported member 'adjustHeadingLevel'.
2import{ adjustHeadingLevel } from'../extension';
Found 1error.
问题 4 - 没有导入的函数
这最后一个错误来自于试图导入一个不存在的函数。
具体来说,setHeadingLevel.ts 中的逻辑如下:
import* asvscode from'vscode';
import{ adjustHeadingLevel } from'../extension';
exportasyncfunctionsetHeadingLevel( ) {
...
}
GPT 很容易在声明其依赖关系时表现得很乐观。它有时会调用或导入不存在的函数。这就是其中的一种情况。
我们可以通过删除依赖关系和手动添加 setHeadingLevel 中的逻辑来解决这个问题:
@@ -1, 5+ 1, 4@@
import* asvscode from'vscode';
- import{ adjustHeadingLevel } from'../extension';
exportasyncfunctionsetHeadingLevel( ) {
consteditor = vscode.window.activeTextEditor;
@@ -14, 6+ 13, 12@@ exportasyncfunctionsetHeadingLevel( ) {
vscode.window.showErrorMessage( 'invalidSelection');
return;
}
+ constheadingRegex = /^(#+)\s(.*)$/;
+ constmatch = selectedText.match(headingRegex);
+ if(!match) {
+ vscode.window.showErrorMessage( 'Invalid selection.');
+ return;
+ }
constinputOptions: vscode.InputBoxOptions = {
prompt: 'setHeadingLevelPrompt',
@@ -31, 6+ 36, 16@@ exportasyncfunctionsetHeadingLevel( ) {
if(headingLevel) {
constnewHeadingLevel = parseInt(headingLevel);
- adjustHeadingLevel(editor, selection, selectedText, newHeadingLevel);
+
+ constnewHeading = '#'.repeat(newHeadingLevel) + ' '+ match[ 2];
+
+ awaiteditor.edit( ( editBuilder) => {
+ editBuilder.replace(selection, newHeading);
+ });
}
}
注意,大部分的代码是从 decreaseHeadingLevel.ts 中提取的。
让我们再次构建。这一次,它成功了 🎉
$ tsc -p ./
✨ Done in0. 80s.
接下来就是运行啦~~
第三步——运行
请注意,GPT 没有提供关于如何运行该扩展的说明。也没有说明如何安装或构建该扩展。如果你以前构建过 vscode 扩展,这很简单,但这对新人来说可能会有些难度。
运行一个 VSCode 扩展需要你去 "运行和调试 "面板,当 extension.ts 文件在编辑器中处于打开状态时,启动 vscode 扩展任务。
这启动了一个新的 vscode 窗口,安装了我们的扩展。当我试图调用一个命令时,这也会出错。
Command 'Increase Heading Level'resulted inan error command'adjust-heading-level. 'increaseHeadingLevel ' was not found
问题 5——找不到命令
当命令在 package.json 中声明时,VSCode 就会知道它们的存在。
我们的 package.json 声明了以下命令:
"activationEvents": [
"onCommand:adjust-heading-level.increaseHeadingLevel",
"onCommand:adjust-heading-level.decreaseHeadingLevel",
"onCommand:adjust-heading-level.setHeadingLevel"
],
...
"contributes": {
"commands": [
{
"command": "adjust-heading-level.increaseHeadingLevel",
"title": "Increase Heading Level"
},
{
"command": "adjust-heading-level.decreaseHeadingLevel",
"title": "Decrease Heading Level"
},
{
"command": "adjust-heading-level.setHeadingLevel",
"title": "Set Heading Level"
}
]
}
在 package.json 中声明后,这些命令也需要在扩展中注册。
extension.ts:
exportfunctionactivate( context: vscode.ExtensionContext) {
context.subions.push(
vscode.commands.registerCommand( 'extension.increaseHeadingLevel', increaseHeadingLevel),
vscode.commands.registerCommand( 'extension.decreaseHeadingLevel', decreaseHeadingLevel),
vscode.commands.registerCommand( 'extension.setHeadingLevel', setHeadingLevel)
);
}
你看到这个问题了吗?
Type 文件将命令声明为 extension.{COMMAND},但 package.json 将它们声明为 adjust-heading-level.{COMMAND}。
我们可以通过调整 package.json 以匹配代码来解决这个问题。虽然修复本身很简单,但能够正确诊断这个问题需要一些领域知识,知道在哪里寻找。
@@ -1,5 +1,5 @@
{
"displayName": "Adjust Heading Level",
"deion": "A VSCode extension that lets the user adjust the heading level of the selected text.",
"version": "0.1.0",
@@ -10,23 +10,20 @@
"Other"
],
"activationEvents": [
- "onCommand:adjust-heading-level.increaseHeadingLevel",
- "onCommand:adjust-heading-level.decreaseHeadingLevel",
- "onCommand:adjust-heading-level.setHeadingLevel"
],
"main": "./src/extension.js",
"contributes": {
"commands": [
{
- "command": "adjust-heading-level.increaseHeadingLevel",
+ "command": "extension.increaseHeadingLevel",
"title": "Increase Heading Level"
},
{
- "command": "adjust-heading-level.decreaseHeadingLevel",
+ "command": "extension.decreaseHeadingLevel",
"title": "Decrease Heading Level"
},
{
- "command": "adjust-heading-level.setHeadingLevel",
+ "command": "extension.setHeadingLevel",
"title": "Set Heading Level"
}
]
注意:我也用它来删除激活事件(activationEvents)--这些决定了 VSCode 扩展触发器何时激活。对于基于命令的激活,VSCode 现在能够自动检测它们,因此不再需要手动声明了。
让我们再试着运行并增加 header 的级别。
嗯,这是不应该发生的
问题6--递减的增加
我们没有增加 header,而是让 header 水平下降。
让我们看一下 increateHeadingLevel.ts:
import* asvscode from'vscode';
exportasyncfunctionincreaseHeadingLevel( ) {
consteditor = vscode.window.activeTextEditor;
if(!editor) {
return;
}
constdocument= editor.document;
constselection = editor.selection;
constselectedText = document.getText(selection);
constheadingRegex = /^(#+)\s(.*)$/;
constmatch = selectedText.match(headingRegex);
if(!match) {
vscode.window.showErrorMessage( 'Invalid selection. Please select a valid heading.');
return;
}
constcurrentLevel = match[ 1].length;
constnewLevel = Math.max( 1, currentLevel - 1);
constnewText = '#'.repeat(newLevel) + ' '+ match[ 2];
awaiteditor.edit( ( editBuilder) => {
editBuilder.replace(selection, newText);
});
}
你看到这个问题了吗?
有一个由单个字符差异引起的错误。
@@ -19, 7+ 19, 7@@ exportasyncfunctionincreaseHeadingLevel( ) {
}
constcurrentLevel = match[ 1].length;
- constnewLevel = Math.max( 1, currentLevel - 1);
+ constnewLevel = Math.max( 1, currentLevel + 1);
constnewText = '#'.repeat(newLevel) + ' '+ match[ 2];
让我们再次编译和运行它。
好了,没问题了!
思考
那么,我们做得怎么样?我们得到了一个有效的扩展程序,也完成我们提示中设定的目标。
达到这一点的过程并不是 "自动"的。一路上我们遇到了很多问题。如果事先没有对 Type、Node.js 和 VSCode 有一定的了解,这些问题将需要花费一段时间来调试。
而且,尽管我们能够生成工作代码,但仍有许多地方需要改进,如:
一些统计数字
GPT 生成了 9 个文件,涵盖了约 100 行的 Type,约 180 行的 json,以及 17 行的 Markdown。
$ cloc --exclude-dir=node_modules,out --not-match-f=package-lock.json --not-match-f=prompt.md --include-ext=ts,json,md .
15 text files.
13 unique files.
7 files ignored.
github.com/AlDanial/cloc v 1.92 T=0.01 s (986.5 files/s, 36610.4 lines/s)
-------------------------------------------------------------------------------
Language files blank commentcode
-------------------------------------------------------------------------------
JSON480181
Type 422098
Markdown 18017
-------------------------------------------------------------------------------
SUM: 9380296
-------------------------------------------------------------------------------
最终的文件树:
$ tree --gitfile extension/.gitignore
.
├── extension
│ ├── package.json
│ ├── src
│ │ ├── commands
│ │ │ ├── decreaseHeadingLevel.ts
│ │ │ ├── increaseHeadingLevel.ts
│ │ │ └── setHeadingLevel.ts
│ │ └── extension.ts
│ ├── tsconfig.json
│ └── yarn.lock
├── prompt.md
└── shared_dependencies.md
在生成的约 300 行代码 中,我们不得不修改/添加约 18 行,以使一切正常工作。
经验之谈
GPT 能够使用一个没有特定领域背景的上下文提示来生成大部分代码。
有些事情需要注意:
未来的方向
我在最初的实验中使用了一个非常简单的提示,并没有提供额外的背景信息,这意味着有很多改进的余地。这是接下来的一些步骤和想法:
注意:我已经运行了这些步骤的一部 分,并且能够在第一代中使错误数量为零。你可以看看它是否可以推广到其他案例中。
也欢迎分享你使用 GPT-4 做开发的经验。
▶ 腾讯回应考生喊话马化腾;库克承认在使用 ChatGPT;OpenHarmony 4.0 Beta1 发布|极客头条
▶ 算网共生 云智无界 | 算网的新征程等你加入
▶ AI 大战高考作文!实测 ChatGPT、文心一言、通义千问等 8 款“神器”