gpt-engineer
是一个使用大模型自动开发应用程序的 LLM 应用,其官方仓库中介绍的功能是:
- 使用自然语言定义软件功能;
- 由 AI 自动编写和执行代码;
- 提供反馈让 AI 进行改进。
使用 gpt-engineer 创建一个应用程序
根据官方文档的说明,下面将使用 stable 版编 gpt-engineer
创建一个应用程序。Python
版本为 3.10,使用 venv
为 gpt-engineer
创建一个虚拟环境并初始化相关文件。
mkdir gpt-engineer-sample
cd gpt-engineer-sample
python3 -m venv venv
source venv/bin/activate
python -m pip install gpt-engineer
mkdir app
touch app/prompt
完成初始化后,首先在 app/prompt
目录下编写对应用程序的要求:
使用 Next.js@14 实现一个 Web 应用程序,这个 Web 应用程序有以下 3 个页面: 1. 登录 2. 用户主页 3. 广场页 在“登录”页面中,用户需要: 1. 输入用户名 2. 输入密码 3. 点击登录 3.1 用户存在且密码匹配则跳转至“用户主页” 3.2 用户不存在或密码匹配失败则显示“登录失败”弹框 系统预置的用户为: 用户名:admin 密码:admin 在“用户主页”中,用户可以: 1. 上传新的图片 1.1 如果图片的名称中不包含“cat”,则弹框提示“图片名称中不包含 cat,请重新上传” 1.2 如果图片名称中包含“cat”则完成上传 2. 查看我上传的图片 2.1 以瀑布流的形式展示用户已经上传的图片,按照上传时间逆序排序 3. 退出登录 在“广场页”中,用户可以: 1. 查看所有用户上传的图片 1.1 以瀑布流的形式展示所有用户上传的图片,按照上传时间逆序排序
app/prompt
完成上述操作后,使用下面的脚本开始创建应用程序:
export OPENAI_API_KEY=<api-key>
gpte app
在 gpt-engineer
创建过程中,会输出其思考过程及需要执行的操作,同意操作后即可开始创建项目。测试项目是使用 Next.js 框架开发,因此初始化时需要等待一段时间。创建完成后项目文件会写入到 app
目录中,使用 npm run dev
启动应用程序。
生成的代码中可能会存在一些细微的语法错误,比如缺少 import 语句,进行简单的修复后打开应用,发现生成的应用中没有添加任何的样式,但是 prompt
中的要求基本上都完成了。对于许多细节可以继续使用 gpt-engineer
improve 模式进行优化。
使用 gpt-engineer 优化已有项目
如上所述,gpt-engineer
能够生成可用的应用,但是仍有许多可以调整的地方。下面将说明使用 gpt-engineer
优化已有项目的方法。
首先创建一个文件夹用于保存用户 prompts:
mkdir app/.prompts
mv app/prompt app/.prompts/prompt.0_init
然后创建一个新的 prompt 用于指导 gpt-engineer
如何优化:
touch app/prompt
echo '在“用户主页”中,添加前往“广场”页的按钮' >> app/prompt
输入完成后使用下面的指令让 gpt-engineer
开始优化代码:
gpte app -i # -i 或 --improve
gpt-engineer
在开始优化前会在 shell 中使用 vim 显示一个文件选择器,在这个文件中会列出项目目录中的文件列表,需要通过修改此文件(取消文件前的注释 #)来选择需要优化的文件。完成后使用 :wq
保存并退出,然后 gpt-engineer
就会开始优化相关文件中的代码。
gpt-engineer
优化的结果中可能存在错误,在完成后需要检查并修复文件中的问题。由于 gpt-engineer
在修改文件前并不会进行备份,为了避免错误的修改导致的问题,在进行优化前最好还是手动将创建一次 git commit
。
示例项目迭代了 15 次 improve,虽然依旧不能算得上是一个完备的应用程序,但是基本功能也算是 OK。
生成效果
在简单使用了一段时间后(约莫两个小时,模型是 gpt-4-1106-preview
,开销 0.9$),总的来说 gpt-engineer
的生成的应用程序的代码质量还算不错,至少没有明显的错误,并且要求的需求也完成了。结合 improve 模式进行迭代,开发体验尚可。只是依旧有以下几个问题需要注意:
- 没有使用网络请求的能力,因此能够使用的知识完全依赖于 LLM,无法使用较新的技术;
- 输出的代码大概率可用,但是还是会出现一些问题(缺少 import 语句、语法错误等),使用者还是需要具备一定的开发能力;
- improve 前需要手动选择相关的文件,没有办法识别 import 语句并自动读取,因此如果项目复杂的话,还是需要手动确定需要调整的文件。
gpt-engineer 源码分析
gpt-engineer 的源码算不上复杂,主流程非常简单:
- 解析参数;
- 初始化 LLM(model、azure_endpoint);
- 读取用户 prompt;
- 确定代码生成函数;
- 确定代码执行函数;
- 初始化 preprompts;
- 初始化
DiskMemory
、DiskExecutionEnv
和CliAgent
; - 执行
agent.init
(生成应用程序代码)或agent.improve
(优化已有代码) - 更新文件。
下面是一些核心概念的说明。
代码生成函数
用于生成项目代码。根据启动时的参数,有以下三种生成方法:
gen_code
:默认的生成方法,直接使用用户的 prompt 生成代码。该方法会使用setup_sys_prompt
方法生成系统 PROMPT,具体步骤是拼接preprompts/roadmap
、preprompts/generate
、preprompts/file_format
以及preprompts/philosophy
,然后调用 LLM 生成代码,并通过chat_to_files_dict
方法从 chat 中提取代码块并保存为FilesDict
对象。clarified_gen
:当使用-c
选项时使用此方法,首先通过 AI 整理需求,然后使用整理后的 prompt 生成应用代码。具体而言首先使用prompt/clarify
以交互的方式整理prompt
(使用文件输入的 prompt),后续步骤和gen_code
方法相同。lite_gen
:使用-l
选项时使用此方法,类似gen_code
方法,区别在于直接将preprompts/file_format
作为系统 PROMPT。
应用 ENTRYPOINT 文件
项目代码的入口文件。
生成方法:调用 gen_entrypoint
方法生成,在该方法中使用 preprompts/entrypoint
和代码文件调用 LLM 生成入口文件。
执行方法:调用 execute_entrypoint
执行代码入口文件。
代码执行函数
该方法用于执行应用 ENTRYPOINT 文件。根据使用的参数不同有以下两种方式:
self_heal
:使用-sh
选项时使用此方法。该方法会读取 ENTRYPOINT 文件并尝试执行,如果执行失败则会将文件内容、执行的输出内容以及preprompts/file_format_fix
整合为一条消息发送给 AI 以获取修复结果,并返回修复后的FilesDict
;execute_entrypoint
:直接执行 ENTRYPOINT 文件。
代码优化函数
improve
方法用于优化已有项目的代码的方法,在使用 -i
选项时会使用此方法。
该方法的执行步骤是使用 setup_sys_prompt_existing_code
方法拼接 improve
PROMPT 和 philosophy
PROMPT 以生成系统 PROMPT,然后将使用 FileSelector
选中的文件的内容和用户 prompt 作为消息调用 LLM 获取优化意见,最后通过 overwrite_code_with_edits
方法覆写源文件。
代码执行环境 DiskExecutionEnv
DiskExecutionEnv
是一个在本地文件系统上运行代码并捕获执行输出的执行环境。其主要职责是通过子进程执行磁盘中的代码(ENTRYPOINT),并处理用户中断。
上下文记忆 DiskMemory
DiskMemory
时一个基于文件的键值存储,其中键对应于文件名,值对应于文件内容。该类实现了一个简易的支持 CRUD 的数据库。
Preprompts 容器 PrepromptsHolder
PrepromptsHolder
用于加载并读取存储在磁盘上的预提示文本的容器。之所以需要该方法是为了方便通过 --use-custom-preprompts
使用自定义的 Preprompts
临时文件存储 FileStore
FileStore
用于管理临时目录中文件存储的模块。
文件选择器 FileSelector
FileSelector
用于管理项目目录内文件选择和交互,提供了从终端交互式选择文件、保存选择以供以后使用,并与系统编辑器集成以直接修改文件的方法。
CliAgent 智能体
CliAgent
管理使用 AI 模型进行代码生成和改进的生命周期。根据给定的提示协调生成新代码和改进现有代码,并利用记忆系统和执行环境进行处理。
在 Agent 实例中主要使用两个方法: