[Перевод] Какие уроки я извлёк из создания расширения VSCode с помощью GPT-4
В последнее время я много экспериментировал с написанием кода при помощи LLM (Large Language Model, большая языковая модель). На мой взгляд, эти инструменты отлично справляются с генерацией небольших самодостаточных фрагментов. К сожалению, что-то большее уже требует человеческого участия для оценки результата LLM и предоставления дальнейших инструкций.
В большинстве случаев, когда кто-то утверждает, что «GPT написал X», человек выступает для LLM в роли своеобразного REPL (Read-Eval-Print Loop, цикл чтение-оценка-вывод), внимательно подводя модель к функциональному результату. Я нисколько не хочу принизить ценность этого процесса — очень здорово, что он работает. Но можем ли мы шагнуть дальше? Можем ли использовать LLM для генерации ВСЕГО кода сложной программы за раз без человеческого вмешательства?
Написание расширения VSCode
Чтобы проверить способность GPT-4 генерировать сложные программы, я попросил этот инструмент создать расширение VSCode, позволяющее пользователю корректировать уровень заголовка выбранного текста Markdown. Эта задача требует:
- Предметного понимания того, как выполнить скаффолдинг и внедрить расширение в VSCode.
- Комбинирования разных языков и платформ: расширения VSCode создаются на TypeScript, что требует написания конфигурации для самого Typescript, а также Node.js и VSCode.
- Генерации множества файлов.
- Создания схемы для отладки, сборки и выполнения кода.
▍ Конфигурация проекта
В этом эксперименте для всех задач генерации я использовал GPT-4. Эту модель я нахожу наиболее эффективной среди всех современных аналогов.
Вдобавок к этому — я использовал для генерации кода фреймворк smol-ai
.
Описание smol-ai
из его README:
Это прототип агента «джуниор-разработчика» (он жеsmol dev
), который создаёт структуру всей базы кода за вас на основании предоставленной спецификации продукта, но не захватывает мир и не обещает фантастических возможностей AGI (Artificial General Intelligence, общий искусственный интеллект). Вместо создания специализированных, косных, одноразовых стартовых систем в стиле создать приложение react или создать приложение nextjs, этот инструмент, по сути, позволяет создать любое приложение, указания для скаффолдинга которого вы прописываете в тесном взаимодействии с вашимsmol dev
.
Я ценю smol-ai
за его простоту. Вся логика генерации кода находится в одном файле Python, состоящем из трёх основных функций:
- Генерации списка файлов, необходимых для передачи инструкций (например, package.json, index.js, …).
- Генерации списка общих зависимостей, необходимых для передачи инструкций (например, axios, react, …).
- Для каждого файла в списке также генерируется код, который будет внесён в этот файл с использованием общих зависимостей, если они применимы.
Имейте в виду, что smol-ai
также имеет и другие возможности вроде отладки ошибок и генерации инструкций на основе текущей базы кода. В этой статье мы их затрагивать не будем.
▍ Инструкции
Я начал с простой инструкции, описывающей мою задачу:
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"
Перевод:python main_no_modal.py «расширение vscode, позволяющее пользователю корректировать уровень заголовка выбранного текста. Оно должно поддерживать три команды: повышение уровня заголовка, понижение уровня заголовка и установка уровня заголовка».
Обратите внимание, что я задал короткую и обобщённую инструкцию. Мне хотелось посмотреть, насколько хорошо 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 set heading level
the files we have decided to generate are: tsconfig.json, package.json, utils.ts, extension.ts
Shared dependencies:
1. Exported variables:
- adjustHeadingLevel (from utils.ts)
2. Data schemas:
- None
3. ID names of DOM elements:
- None
4. Message names:
- None
5. Function names:
- increaseHeadingLevel
- decreaseHeadingLevel
- setHeadingLevel
6. Dependencies:
- None
▍ Код
Файл extension.ts является точкой входа расширения VSCode. В нём мы регистрируем три команды, которые указали в инструкции.
import * as vscode from 'vscode';
import { increaseHeadingLevel } from './commands/increaseHeadingLevel';
import { decreaseHeadingLevel } from './commands/decreaseHeadingLevel';
import { setHeadingLevel } from './commands/setHeadingLevel';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('extension.increaseHeadingLevel', increaseHeadingLevel),
vscode.commands.registerCommand('extension.decreaseHeadingLevel', decreaseHeadingLevel),
vscode.commands.registerCommand('extension.setHeadingLevel', setHeadingLevel)
);
}
export function deactivate() {}
Ниже я приведу логику, содержащуюся в decreaseHeadingLevel.ts:
import * as vscode from 'vscode';
export async function decreaseHeadingLevel() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const document = editor.document;
const selection = editor.selection;
const selectedText = document.getText(selection);
const headingRegex = /^(#+)\s(.*)$/;
const match = selectedText.match(headingRegex);
if (!match) {
vscode.window.showErrorMessage('Invalid selection. Please select a heading to decrease its level.');
return;
}
const currentLevel = match[1].length;
if (currentLevel === 1) {
vscode.window.showErrorMessage('Cannot decrease heading level. Already at the lowest level.');
return;
}
const newLevel = currentLevel - 1;
const newHeading = '#'.repeat(newLevel) + ' ' + match[2];
await editor.edit((editBuilder) => {
editBuilder.replace(selection, newHeading);
});
}
Код проверяет наличие активной программы-редактора, получает выбранный текст и ищет заголовок Markdown, используя регулярное выражение. Если заголовок найден, и его текущий уровень не является наименьшим, он этот уровень понижает.
На первый взгляд, никаких проблем в этой логике нет. Код выполняет команду и проверяет возможные пограничные случаи. Он даже предоставляет полезные сообщения об ошибках, чем уже опережает большинство программ, создаваемых людьми…
Тестирование расширения
Для проверки расширения нам нужно успешно проделать следующие шаги:
- Установить зависимости.
- Скомпилировать код.
- Запустить расширение.
▍ Шаг 1: установка
При попытке установить зависимости мы сталкиваемся с первой проблемой:
$ yarn
Couldn't find any versions for "vscode-test" that matches "^1.6.2"
? Please choose a version of "vscode-test" from this list: (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",
"typescript": "^4.4.2",
"vscode": "^1.1.37",
"vscode-test": "^1.6.2"
},
}
Движок VSCode определяет минимальную версию редактора. На сегодня (23.05.2023) это версия 1.78. Версия 1.62.0 была выпущена 21 октября 2021 года.
Это согласуется с крайним сроком имеющихся у GPT-4 знаний:
GPT-4, как правило, ничего не знает о событиях, произошедших после сентября 2021, так как этой датой ограничивается актуальность большей части известной ему информации.
Версия vscode-test 1.6.2
выглядит подозрительно похожей на 1.62, и это говорит о том, что GPT, скорее всего, эти числа выдумал.
Как бы то ни было, это легко исправить, указав верный номер версии и повторив установку:
- "vscode-test": "^1.6.2"
+ "vscode-test": "^1.6.1"
Повторная установка завершилась успешно.
$ yarn
...
[3/5]