Использование ChatGPT для автоматизации генерации кода в JS-проекте

в 13:05, , рубрики: chatgpt, grammy, javascript, llm, nodejs, regexp, teqfw, кодогенерация

В своих предыдущих статьях о разработке телеграм-ботов я делился опытом создания базового функционала и работы с командами через аргументы (раз, два). На этапе подготовки третьей статьи передо мной встал вопрос генерации однотипного кода для новых команд, что превратилось в рутинную задачу. Чтобы избежать этого и ускорить процесс, я решил задействовать ChatGPT. К моему удивлению, его помощь оказалась значительно весомее, чем я ожидал.

Если коротко, то использование ChatGPT для автоматизации генерации кода эффективно при работе с шаблонами. Генерация однотипных фрагментов на основе шаблонов ускоряет процесс разработки и снижает количество рутинных задач. Чем точнее структура и наименования в шаблонах, тем легче модели корректно выполнять замены,

Для тех же, кто хочет более детально ознакомиться с процессом взаимодействия человека и ChatGPT при создании JavaScript-проекта - добро пожаловать под кат.

Введение в контекст

Я разрабатываю телеграм-ботов на платформе Node.js с использованием библиотеки grammY и собственного инструментария TeqFW. Основная архитектура моего фреймворка основана на использовании контейнера объектов, который позволяет осуществлять позднее связывание зависимостей во время выполнения кода через конструкторы классов, что упрощает масштабируемость и поддержку приложений.

В моём npm-пакете flancer32/teq-telegram-bot реализован базовый функционал для работы с телеграм-ботами, такой как запуск и остановка бота, а также подключение зависимостей (grammY, teqfw/di и другие). В дополнение, есть GitHub-шаблон flancer32/repo-tmpl-tg-bot, который позволяет быстро создавать новые проекты, задавая базовую структуру каталогов и файлов Node.js-приложения.

Принципы современной разработки программного обеспечения предполагают декомпозицию программы на логические единицы (пакеты, модули, файлы, классы, объекты) и их последующую интеграцию на этапе компиляции или выполнения кода. В связи с этим важно учитывать три ключевых аспекта:

  • Формализацию структуры каталогов и файлов для упрощённого управления кодовой базой.

  • Правила наименования, облегчающие навигацию по проекту.

  • Шаблонизацию однотипных фрагментов кода.

Что же касается использования ChatGPT, то я работаю с платной версией, которая предоставляет мне расширенные возможности для более точной генерации кода и упрощает взаимодействие в рамках задач по автоматизации.

Постановка задачи

В своей третьей статье (пока ещё в работе) я решил рассмотреть работу с диалогами с использованием библиотеки grammy/conversations. Для того чтобы сделать демо-проект более интересным и приближенным к реальным задачам, я выбрал сценарий записи клиента на услугу (например, маникюр, стрижка или замена шин). Проект было бы можно постепенно усложнять, добавляя такие аспекты, как работа с сессиями, уведомлениями и другие элементы разработки ботов, что могло бы стать темой для последующих статей.

В общем, вот:

постановка задачи на естественном языке

Формализуй техническое задание и оцени время разработки телеграм-бота в виде nodejs-приложения с использованием библиотеки grammY. Вот описание на естественном языке.

По команде /start пользователь выбирает одну из ролей - Клиент или Вендор (поставщик коммерческих услуг). Если пользователь уже зарегистрирован, то он может сменить текущую роль на другую.

Если пользователь Вендор, то он может пользоваться такими командами для манипуляции сервисами, как /service_create, /service_read, /service_update, /service_delete, /service_list.

Если пользователь Клиент, то он может пользоваться такими командами, как /service_list, /service_read, /visit_service (регистрация визита на сервис), /visit_list (просмотр своих визитов), /visit_delete (отказ от посещения услуги)

Дополнительно Вендор имеет доступ к командам, обрабатывающим визиты: /visit_list (просмотр всех визитов на все свои сервисы), /visit_queue (просмотр заявок Клиентов на посещение сервисов), /visit_approve (подтверждение визита Клиента), /visit_decline (отклонение визита Клиента).

Для облегчения функционала принимаем, что все сервисы работают с 10:00 и до 18:00 во все рабочие дни недели (с понедельника по пятницу).

Сохрани текст в виде markdown, используя тройные апострофы для всех блоков кода, в виде, доступном для скачивания на локальный диск.

ответ ChatGPT в формате markdown

# Техническое задание на разработку телеграм-бота

## Описание

Бот позволяет пользователям выбирать одну из ролей: **Клиент** или **Вендор** (поставщик коммерческих услуг). Функционал бота зависит от выбранной роли.

### Основные команды

#### Общие команды:

- `/start` — выбор роли: **Клиент** или **Вендор**. 
  Если пользователь уже зарегистрирован, он может сменить текущую роль на другую.

#### Команды для Вендора:

- `/service_create` — создание нового сервиса.
- `/service_read` — просмотр информации о конкретном сервисе.
- `/service_update` — обновление информации о сервисе.
- `/service_delete` — удаление сервиса.
- `/service_list` — просмотр списка всех своих сервисов.
- `/visit_list` — просмотр всех визитов на все свои сервисы.
- `/visit_queue` — просмотр заявок Клиентов на посещение сервисов.
- `/visit_approve` — подтверждение визита Клиента.
- `/visit_decline` — отклонение визита Клиента.

#### Команды для Клиента:

- `/service_list` — просмотр списка всех сервисов.
- `/service_read` — просмотр информации о конкретном сервисе.
- `/visit_service` — регистрация визита на сервис.
- `/visit_list` — просмотр своих визитов.
- `/visit_delete` — отказ от посещения услуги.

### Дополнительные требования

- Время работы всех сервисов: с 10:00 до 18:00 с понедельника по пятницу.
- Доступ к информации и действиям зависит от выбранной роли.

## Оценка времени разработки

1. **Настройка проекта и базовая архитектура** — 4 часа.
2. **Регистрация и смена роли пользователя** — 6 часов.
3. **Реализация команд для Вендора** — 12 часов.
4. **Реализация команд для Клиента** — 10 часов.
5. **Обработка визитов для Вендоров (подтверждение, отклонение)** — 8 часов.
6. **Тестирование и отладка** — 6 часов.
7. **Документация** — 4 часа.

**Итого**: ~50 часов.

Генерация кода

Для этой статьи я из шаблона flancer32/repo-tmpl-tg-bot создал отдельный репозиторий - flancer64/tg-demo-ai-generate, который используется для чистой генерации кода и разработки примеров, о которых идёт речь в статье. Шаблонное пространство имён Ns_App я заменил на CodeGen (операция Replace in Files в IDE).

Кодификатор команд

Если коротко, то в ТЗ получилось 11 дополнительных команд. В моём шаблонном репозитории есть кодификатор команд с тремя стандартными командами:

const CodeGen_Back_Enum_Bot_Command = {
    HELP: 'help',
    SETTINGS: 'settings',
    START: 'start',
};

Мне было лень копипастить имена команд из ТЗ, а так как LLM отлично манипулируют текстом, то я дал этот фрагмент кода и просто попросил ChatGPT добавить получившиеся команды код:

Вот шаблон кода с кодификатором команд бота:

const CodeGen_Back_Enum_Bot_Command = {
    HELP: 'help',
    SETTINGS: 'settings',
    START: 'start',
};

Добавь в этот кодификатор все новые команды бота из получившегося ТЗ. Клиент и Вендор могут использовать одни и те же команды. Результат выведи прямо в чат.

ChatGPT предложил мне скачать вот такой фрагмент:

const CodeGen_Back_Enum_Bot_Command = {
    HELP: 'help',
    SETTINGS: 'settings',
    START: 'start',
    SERVICE_CREATE: 'service_create',
    SERVICE_READ: 'service_read',
    SERVICE_UPDATE: 'service_update',
    SERVICE_DELETE: 'service_delete',
    SERVICE_LIST: 'service_list',
    VISIT_LIST: 'visit_list',
    VISIT_QUEUE: 'visit_queue',
    VISIT_APPROVE: 'visit_approve',
    VISIT_DECLINE: 'visit_decline',
    VISIT_SERVICE: 'visit_service',
    VISIT_DELETE: 'visit_delete',
};

Мне оставалось только закопипастить его по месту. Один раз вместо десятка, как если бы я делал это построчно из ТЗ.

Обработчики команд

Если ChatGPT может генерировать файл на основании шаблона, то может быть он может генерировать и множество однотипных файлов?

В качестве обработчика команды /start у меня фигурирует вот такой код, который находится в файле ./src/Back/Bot/Cmd/Start.js. :

/**
 * The handler for the 'start' command.
 */
export default class CodeGen_Back_Bot_Cmd_Start {
    constructor() {
        return (ctx) => {
            ctx.reply('Welcome! You can start using the bot.');
        };
    }
}

Вот промпт, который просит ChatGPT создать набор файлов, согласно шаблона:

Вот шаблон для реализации отдельной команды:

/**
 * The handler for the 'start' command.
 */
export default class CodeGen_Back_Bot_Cmd_Start {
    constructor() {
        return (ctx) => {
            ctx.reply('Welcome! You can start using the bot.');
        };
    }
}

Этот код находится в файле ./src/Back/Bot/Cmd/Start.js

Сделай мне аналогичные файлы с JS-кодом для всех новых команд. Пространство имён (${NS}) для всех классов - CodeGen_Back_Bot_Cmd. Оно соответствует каталогу ./src/Back/Bot/Cmd/. Класс для команды /service_create должен называться ${NS}_Service_Create и должен находиться по адресу ./src/Back/Bot/Cmd/Service/Create.js

Все файлы сверни в один архив, доступный для загрузки на локальный диск.

В результате ChatGPT создаёт архив с исходниками, где в нужных файлах расположены нужные классы, а в коде стоят нужные комментарии и сообщения:

Архив с исходниками

Архив с исходниками

Это содержимое отдельного файла:


/**
 * The handler for the 'service_create' command.
 */
export default class CodeGen_Back_Bot_Cmd_Service_Create {
    constructor() {
        return (ctx) => {
            ctx.reply('Executing service_create command...');
        };
    }
}

Остаётся только распаковать этот архив в свой проект и поставить файлы под контроль версий (git).

Добавление команд в help

Промпт для ChatGPT:

Вот шаблон для обработчика команды /help:

export default class CodeGen_Back_Bot_Cmd_Help {
    /**
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        return async (ctx) => {
            // https://core.telegram.org/bots/api#sendmessage
            await ctx.reply(
                `This is a test bot for demo.
                
Available commands are:

/${CMD.HELP} - display this text. 
/${CMD.SETTINGS} - configure this bot. 
/${CMD.START} - start the bot.`,
                {parse_mode: 'HTML',}
            );
        };
    }
}

Добавь в обработчик все новые команды бота из получившегося ТЗ. Пояснения к командам должны быть на английском языке. Результат выведи прямо в чат.

Вот результат:

export default class CodeGen_Back_Bot_Cmd_Help {
    /**
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        return async (ctx) => {
            // https://core.telegram.org/bots/api#sendmessage
            await ctx.reply(
                `This is a test bot for demo.
                
Available commands are:

/${CMD.HELP} - display this text. 
/${CMD.SETTINGS} - configure this bot. 
/${CMD.START} - start the bot. 
/${CMD.SERVICE_CREATE} - create a new service (Vendor only). 
/${CMD.SERVICE_READ} - view details of a service. 
/${CMD.SERVICE_UPDATE} - update an existing service (Vendor only). 
/${CMD.SERVICE_DELETE} - delete a service (Vendor only). 
/${CMD.SERVICE_LIST} - list all available services. 
/${CMD.VISIT_SERVICE} - register for a service (Client only). 
/${CMD.VISIT_LIST} - view your visit history (Client only). 
/${CMD.VISIT_QUEUE} - view visit requests (Vendor only). 
/${CMD.VISIT_APPROVE} - approve a client's visit (Vendor only). 
/${CMD.VISIT_DECLINE} - decline a client's visit (Vendor only). 
/${CMD.VISIT_DELETE} - cancel your visit (Client only).`,
                {parse_mode: 'HTML',}
            );
        };
    }
}

Модификация в нескольких точках

Класс CodeGen_Back_Bot_Setup имплементирует интерфейс Telegram_Bot_Back_Api_Setup из базового пакета flancer32/teq-telegram-bot и позволяет встраивать код нашего бота в код базового пакета (запускающий приожение в CLI и в виде web-сервера). В этот раз ChatGPT должен внедрить код для новых команд в трёх местах:

  • в конструкторе в виде зависимости

  • в методе commands, который добавляет команды в меню

  • в методе handlers, который добавляет обработчики событий

Вот промпт для ChatGPT:

Вот код для подключения команд к боту:

export default class CodeGen_Back_Bot_Setup {
    /**
     * @param {TeqFw_Core_Shared_Api_Logger} logger -  instance
     * @param {CodeGen_Back_Bot_Cmd_Help} cmdHelp
     * @param {CodeGen_Back_Bot_Cmd_Settings} cmdSettings
     * @param {CodeGen_Back_Bot_Cmd_Start} cmdStart
     * @param {CodeGen_Back_Bot_Filter_Message} filterMessage
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            TeqFw_Core_Shared_Api_Logger$$: logger,
            CodeGen_Back_Bot_Cmd_Help$: cmdHelp,
            CodeGen_Back_Bot_Cmd_Settings$: cmdSettings,
            CodeGen_Back_Bot_Cmd_Start$: cmdStart,
            CodeGen_Back_Bot_Filter_Message$: filterMessage,
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        // INSTANCE METHODS
        this.commands = async function (bot) {
            bot.api.setMyCommands([
                {command: CMD.HELP, description: 'Get help.'},
                {command: CMD.SETTINGS, description: 'Configure bot settings.'},
                {command: CMD.START, description: 'Start using the bot.'},
            ]);
            logger.info(`A total of ${Object.keys(CMD).length} commands have been set for the bot.`);
            return bot;
        };

        this.handlers = function (bot) {
            bot.command(CMD.HELP, cmdHelp);
            bot.command(CMD.SETTINGS, cmdSettings);
            bot.command(CMD.START, cmdStart);
            bot.on('message', filterMessage);
            return bot;
        };
    }
}

Добавь в этот код все новые команды бота из получившегося ТЗ. Пояснения к командам должны быть на английском языке. Результат выведи прямо в чат.

Результат в спойлере
export default class CodeGen_Back_Bot_Setup {
    /**
     * @param {TeqFw_Core_Shared_Api_Logger} logger -  instance
     * @param {CodeGen_Back_Bot_Cmd_Help} cmdHelp
     * @param {CodeGen_Back_Bot_Cmd_Settings} cmdSettings
     * @param {CodeGen_Back_Bot_Cmd_Start} cmdStart
     * @param {CodeGen_Back_Bot_Cmd_Service_Create} cmdServiceCreate
     * @param {CodeGen_Back_Bot_Cmd_Service_Read} cmdServiceRead
     * @param {CodeGen_Back_Bot_Cmd_Service_Update} cmdServiceUpdate
     * @param {CodeGen_Back_Bot_Cmd_Service_Delete} cmdServiceDelete
     * @param {CodeGen_Back_Bot_Cmd_Service_List} cmdServiceList
     * @param {CodeGen_Back_Bot_Cmd_Visit_Service} cmdVisitService
     * @param {CodeGen_Back_Bot_Cmd_Visit_List} cmdVisitList
     * @param {CodeGen_Back_Bot_Cmd_Visit_Queue} cmdVisitQueue
     * @param {CodeGen_Back_Bot_Cmd_Visit_Approve} cmdVisitApprove
     * @param {CodeGen_Back_Bot_Cmd_Visit_Decline} cmdVisitDecline
     * @param {CodeGen_Back_Bot_Cmd_Visit_Delete} cmdVisitDelete
     * @param {CodeGen_Back_Bot_Filter_Message} filterMessage
     * @param {typeof CodeGen_Back_Enum_Bot_Command} CMD
     */
    constructor(
        {
            TeqFw_Core_Shared_Api_Logger$$: logger,
            CodeGen_Back_Bot_Cmd_Help$: cmdHelp,
            CodeGen_Back_Bot_Cmd_Settings$: cmdSettings,
            CodeGen_Back_Bot_Cmd_Start$: cmdStart,
            CodeGen_Back_Bot_Cmd_Service_Create$: cmdServiceCreate,
            CodeGen_Back_Bot_Cmd_Service_Read$: cmdServiceRead,
            CodeGen_Back_Bot_Cmd_Service_Update$: cmdServiceUpdate,
            CodeGen_Back_Bot_Cmd_Service_Delete$: cmdServiceDelete,
            CodeGen_Back_Bot_Cmd_Service_List$: cmdServiceList,
            CodeGen_Back_Bot_Cmd_Visit_Service$: cmdVisitService,
            CodeGen_Back_Bot_Cmd_Visit_List$: cmdVisitList,
            CodeGen_Back_Bot_Cmd_Visit_Queue$: cmdVisitQueue,
            CodeGen_Back_Bot_Cmd_Visit_Approve$: cmdVisitApprove,
            CodeGen_Back_Bot_Cmd_Visit_Decline$: cmdVisitDecline,
            CodeGen_Back_Bot_Cmd_Visit_Delete$: cmdVisitDelete,
            CodeGen_Back_Bot_Filter_Message$: filterMessage,
            CodeGen_Back_Enum_Bot_Command$: CMD,
        }
    ) {
        // INSTANCE METHODS
        this.commands = async function (bot) {
            bot.api.setMyCommands([
                {command: CMD.HELP, description: 'Get help.'},
                {command: CMD.SETTINGS, description: 'Configure bot settings.'},
                {command: CMD.START, description: 'Start using the bot.'},
                {command: CMD.SERVICE_CREATE, description: 'Create a new service (Vendor only).'},
                {command: CMD.SERVICE_READ, description: 'View details of a service.'},
                {command: CMD.SERVICE_UPDATE, description: 'Update an existing service (Vendor only).'},
                {command: CMD.SERVICE_DELETE, description: 'Delete a service (Vendor only).'},
                {command: CMD.SERVICE_LIST, description: 'List all available services.'},
                {command: CMD.VISIT_SERVICE, description: 'Register for a service (Client only).'},
                {command: CMD.VISIT_LIST, description: 'View your visit history (Client only).'},
                {command: CMD.VISIT_QUEUE, description: 'View visit requests (Vendor only).'},
                {command: CMD.VISIT_APPROVE, description: 'Approve a client's visit (Vendor only).'},
                {command: CMD.VISIT_DECLINE, description: 'Decline a client's visit (Vendor only).'},
                {command: CMD.VISIT_DELETE, description: 'Cancel your visit (Client only).'}
            ]);
            logger.info(`A total of ${Object.keys(CMD).length} commands have been set for the bot.`);
            return bot;
        };

        this.handlers = function (bot) {
            bot.command(CMD.HELP, cmdHelp);
            bot.command(CMD.SETTINGS, cmdSettings);
            bot.command(CMD.START, cmdStart);
            bot.command(CMD.SERVICE_CREATE, cmdServiceCreate);
            bot.command(CMD.SERVICE_READ, cmdServiceRead);
            bot.command(CMD.SERVICE_UPDATE, cmdServiceUpdate);
            bot.command(CMD.SERVICE_DELETE, cmdServiceDelete);
            bot.command(CMD.SERVICE_LIST, cmdServiceList);
            bot.command(CMD.VISIT_SERVICE, cmdVisitService);
            bot.command(CMD.VISIT_LIST, cmdVisitList);
            bot.command(CMD.VISIT_QUEUE, cmdVisitQueue);
            bot.command(CMD.VISIT_APPROVE, cmdVisitApprove);
            bot.command(CMD.VISIT_DECLINE, cmdVisitDecline);
            bot.command(CMD.VISIT_DELETE, cmdVisitDelete);
            bot.on('message', filterMessage);
            return bot;
        };
    }
}

Заключение

В результате сотрудничества с ChatGPT на основе шаблонного кода был создан каркас бота с 14 командами (3 основных и 11 дополнительных).

Использование ChatGPT для автоматизации генерации кода в JS-проекте - 2

В статье я не стал подробно описывать проблемы, с которыми столкнулся при генерации (например, некорректный регистр имён классов, неверный префикс, удвоение экранирующих символов при записи кода в файл и т.п.). Некоторые из этих проблем решались с помощью "Replace in Files", а другие — изменением промпта.

Тем не менее, ChatGPT справляется с генерацией довольно неплохо, если работать в режиме "делай, как я" — когда есть готовый пример для подражания и требуется выполнить замены по заданному шаблону.

Важно помнить, что ChatGPT — это инструмент, а не мыслящий помощник. Конечно, можно ожидать от него "творческих решений" или "полёта мысли", но это скорее приведёт к разочарованию в его возможностях. Если же воспринимать его как мощный инструмент наподобие "большого регулярного выражения" с инструкциями на естественном языке, то результаты могут приятно удивить. Поскольку LLM работает с текстом, то чем больше в коде текстовых подсказок (например, правильное наименование и комментарии), тем легче модели ориентироваться и соответствовать вашим ожиданиям.

Спасибо за прочтение и удачного вам взаимодействия с LLM!

Автор: flancer

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js