В сфере разработки программного обеспечения, особенно в такой специфической и динамично развивающейся области как 1С:Предприятие, инструменты на основе искусственного интеллекта (ИИ) призваны ускорять процесс создания кода и автоматизировать рутинные задачи. Ожидается, что они будут выступать в роли надежных помощников, предлагая готовые и оптимальные решения. Однако, как показывает практика, их использование требует значительной осторожности и критического подхода со стороны разработчика.
В данной статье мы с вами подробно рассмотрим ситуацию, возникшую при тестировании 1С:Напарника – интеллектуального помощника для разработчиков 1С, встроенного в 1С:Enterprise Development Tools (EDT). Эта ситуация ярко демонстрирует как потенциальные возможности, так и существенные недостатки ИИ-помощников в генерации корректного, оптимального и соответствующего стандартам 1С кода. Мы проанализируем различные варианты решения одной и той же задачи, выявленные в ходе обсуждения на форуме, от заведомо некорректных до наиболее эффективных.
Наш коллега задал 1С:Напарнику, чату с ИИ, работающему с платформой 1С, достаточно простую и распространенную задачу: написать код, который проверяет, находится ли текущее время в диапазоне между 8 и 12 часами. Казалось бы, такая элементарная проверка должна быть легко реализована любым ИИ-ассистентом. Однако полученный результат вызвал удивление и породил вопросы о реальном уровне "знаний" 1С у этого помощника. Рассмотрим код, предложенный 1С:Напарником в первом варианте:
// Функция проверяет, находится ли текущее время в диапазоне с 8:00 до 12:00.
//
// Возвращаемое значение:
// Булево - Истина, если время в диапазоне, иначе Ложь.
//
Функция ТекущееВремяВДиапазонеРабочихЧасов Экспорт
ТекущаяДата = ТекущаяДата; // Получаем полную текущую дату и время
ТекущееВремя = ТекущаяДата - Дата(ТекущаяДата)); // Выделяем только время
НачалоДиапазона = "08:00";
КонецДиапазона = "12:00:00";
Если ТекущееВремя >= НачалоДиапазона И ТекущееВремя = 8 И Час < 12 Тогда
Сообщить("Текущее время в диапазоне 8-12 часов");
Иначе
Сообщить("Текущее время НЕ в диапазоне 8-12 часов");
КонецЕсли;
КонецФункции;
Если мы внимательно проанализируем этот код, мы обнаружим несколько фундаментальных ошибок и неоптимальных решений, которые делают его непригодным для использования:
ТекущаяДата = ТекущаяДата; не имеет смысла, так как переменная присваивает сама себе значение. Более того, выделение времени путем вычитания даты ТекущееВремя = ТекущаяДата - Дата(ТекущаяДата)); является хоть и рабочим, но крайне нелогичным, неочевидным и избыточным способом получения значения типа Дата (но содержащего только временную компоненту) из полной даты. Для получения текущей даты и времени достаточно использовать функцию ТекущаяДата().ТекущееВремя, которая имеет тип Дата (хотя и содержит только временную компоненту), со строковыми значениями НачалоДиапазона = "08:00"; и КонецДиапазона = "12:00:00";. Платформа 1С не выполняет автоматическое преобразование строк в даты при сравнении таким образом, что неизбежно приведет к ошибке выполнения или неверному результату.Если ТекущееВремя >= НачалоДиапазона И ТекущееВремя = 8 И Час < 12 Тогда содержится несколько серьезных проблем:
ТекущееВремя = 8 является абсурдом, так как мы пытаемся сравнить значение типа Дата с числом 8.Час, которая не была объявлена и инициализирована в данном контексте, приведет к ошибке "Переменная не определена".// Возвращаемое значение: // Булево), но фактически только выводит сообщения с помощью Сообщить(), не возвращая никакого значения. Это нарушает объявленный интерфейс функции и сделает ее бесполезной для использования в других частях кода.Такой результат вызывает серьезные опасения относительно компетентности ИИ-помощника и подчеркивает необходимость тщательного ревью и тестирования всего генерируемого им кода.
После демонстрации такого "качества" кода от 1С:Напарника, возник естественный вопрос: а может ли другой экземпляр ИИ-помощника или другой интерфейс (например, через 1С:EDT) дать лучший результат? Один из участников форума поделился результатом, полученным из 1С:EDT (Enterprise Development Tools) с подключенным Напарником. Представленный код оказался значительно лучше, хотя и не идеальным. Посмотрим на этот вариант, который уже представляет собой гораздо более осмысленный подход:
// Функция проверяет, находится ли текущее время в диапазоне с 8:00 до 12:00.
//
// Возвращаемое значение:
// Булево - Истина, если время в диапазоне, иначе Ложь.
//
Функция ПроверитьВременнойДиапазон() Экспорт
Перем Час;
ТекущееВремя = ТекущаяДата();
Час = Час(ТекущееВремя);
Если Час >= 8 И Час < 12 Тогда
Возврат Истина;
Иначе
Возврат Ложь;
КонецЕсли;
КонецФункции
Этот вариант уже гораздо ближе к рабочему решению. Он корректно использует функцию Час() для извлечения часа из текущей даты и выполняет логичное сравнение. Это безусловно шаг вперед по сравнению с первым примером. Однако, и к нему были высказаны замечания, направленные на дальнейшую оптимизацию и соответствие лучшим практикам 1С:
ТекущееВремя: Участники форума справедливо отметили, что переменная ТекущееВремя является избыточной. Мы можем напрямую использовать результат функции ТекущаяДата() в вызове Час(), что делает код короче и более читаемым, не снижая при этом его понятности.Час: Аналогично, переменную Час можно избежать, если подставить вызов Час(ТекущаяДата()) непосредственно в условие. Это позволяет уменьшить количество промежуточных переменных, делая код еще более лаконичным, что особенно ценится в простых функциях.ТекущаяДатаСеанса(): Для кода, исполняемого на сервере, рекомендуется использовать функцию ТекущаяДатаСеанса() вместо ТекущаяДата(). Это связано с тем, что ТекущаяДата() возвращает локальное время сервера, а ТекущаяДатаСеанса() – время сеанса пользователя, скорректированное с учетом его часового пояса и системной даты сеанса. Это важное соображение для многопользовательских систем и распределенных решений, где время на сервере и у клиента может отличаться.ПроверитьВременнойДиапазон как не совсем типичное для русского языка, более характерное для машинного перевода или обучения на данных от нерусскоязычных источников. Для 1С-разработки предпочтительнее использовать более естественные для русского языка формулировки, например, ВремяВДиапазоне или ТекущееВремяВДиапазонеРабочихЧасов.Эти замечания направлены не на критику, а на улучшение качества кода, приведение его к стандартам и повышение эффективности.
Учитывая все замечания и стремясь к максимально чистому, понятному и эффективному коду, участники форума предложили наиболее оптимальный вариант решения задачи. Этот код минимизирует количество переменных, использует встроенные функции 1С наиболее прямым способом и легко читается, являясь отличным примером "чистого кода" в 1С.
// Функция проверяет, находится ли текущее время в диапазоне с 8:00 до 12:00.
//
// Возвращаемое значение:
// Булево - Истина, если время в диапазоне, иначе Ложь.
//
// Вариант с промежуточной переменной для часа (чуть менее лаконичный, но понятный)
Функция ТекущееВремяВДиапазонеРабочихЧасов() Экспорт
Перем ЧасТекущейДаты;
// Для клиентского кода
ЧасТекущейДаты = Час(ТекущаяДата());
// Для серверного кода лучше использовать:
// ЧасТекущейДаты = Час(ТекущаяДатаСеанса());
Возврат ЧасТекущейДаты >= 8 И ЧасТекущейДаты < 12;
КонецФункции
// Еще более лаконичный вариант (наиболее оптимальный):
// Функция ТекущееВремяВДиапазонеРабочихЧасов() Экспорт
// // Для клиентского кода
// Возврат Час(ТекущаяДата()) >= 8 И Час(ТекущаяДата()) < 12;
// // Для серверного кода
// // Возврат Час(ТекущаяДатаСеанса()) >= 8 И Час(ТекущаяДатаСеанса()) < 12;
// КонецФункции
Этот код является эталонным для данной задачи. Он предельно прост, понятен и эффективен. Мы используем функцию Час() для непосредственного извлечения компоненты часа из текущей даты (или даты сеанса) и сразу же сравниваем ее с заданными границами. Таким образом, мы избегаем создания лишних переменных и сложных манипуляций с датами, что делает код более производительным и легким для поддержки.
Важно отметить логику сравнения Час < 12: это означает, что время от 8 часов 00 минут 00 секунд включительно до 11 часов 59 минут 59 секунд включительно попадает в диапазон, а ровно 12:00:00 уже нет. Как верно заметили в обсуждении, "12:00:00 относится 'направо' по оси времени, к следующему интервалу". Это классический способ определения временного интервала, который является лево-включительным и право-исключительным [Начало, Конец). Если бы мы использовали Час <= 12, то время 12:00:00 также попало бы в диапазон, что обычно не соответствует семантике "до 12 часов" в контексте рабочего дня, когда начало следующего часа не включается в предыдущий интервал.
Проблемы с 1С:Напарником не ограничились только первоначальной задачей. Дальнейшие эксперименты и запросы показали, что ИИ склонен не только к созданию избыточного кода, но и к так называемым "галлюцинациям" – выдумыванию несуществующих методов или неправильному толкованию стандартных механизмов платформы. Рассмотрим еще несколько ярких примеров из обсуждения на форуме, которые демонстрируют эти недостатки.
Когда задачу усложнили, попросив проверить диапазон с учетом минут, например, с 8:15 до 12:15, 1С:Напарник вновь продемонстрировал склонность к чрезмерному усложнению и неоптимальным решениям. Посмотрим на его подход к этой задаче:
Функция ЭтоВремяДиапазонаРаботы Экспорт
ТекущееВремя = ТекущаяДата; // Получаем границы рабочего интервала
НачалоРаботы = '20240101' + "081500"; // Произвольная дата + время начала
КонецРаботы = '20240101' + "121500"; // Произвольная дата + время конца
// Преобразуем время в количество минут от начала дня для удобства сравнения
ТекущиеМинуты = Час(ТекущееВремя) * 60 + Минута(ТекущееВремя);
НачальныеМинуты = Час(НачалоРаботы) * 60 + Минута(НачалоРаботы);
КонечныеМинуты = Час(КонецРаботы) * 60 + Минута(КонецРаботы);
// Проверяем, попадает ли текущее время в интервал
// Включая левую и правую границу
Если ТекущиеМинуты >= НачальныеМинуты И ТекущиеМинуты <= КонечныеМинуты Тогда
Возврат Истина;
Иначе
Возврат Ложь;
КонецЕсли;
КонецФункции
Здесь мы видим избыточное количество действий для сравнительно простой задачи. Давайте разберем основные недостатки:
'20240101' для создания временных границ НачалоРаботы и КонецРаботы является избыточным. Нам нужны только компоненты времени, а добавление произвольной даты лишь усложняет чтение и понимание кода.ТекущиеМинуты = Час(ТекущееВремя) * 60 + Минута(ТекущееВремя); и аналогично для границ) является функционально рабочим, но делает код менее читаемым и добавляет ненужную логику. Для сравнения временных интервалов с минутами можно использовать более прямолинейные методы, например, создавая объекты Дата с "нулевой" датой и нужными часами/минутами, или даже сравнивая строковые представления времени, если они имеют единообразный формат (хотя это менее надежно и не рекомендуется).ТекущееВремя: Снова используется избыточная переменная ТекущееВремя = ТекущаяДата;, когда можно было бы сразу работать с результатом функции ТекущаяДата() или ТекущаяДатаСеанса().Оптимальное решение для такой задачи могло бы выглядеть примерно так, используя конструктор Дата() с указанием времени:
Функция ЭтоВремяВДиапазонеСМинутами() Экспорт
// Получаем текущее время
ТекущееВремя = ТекущаяДата(); // Или ТекущаяДатаСеанса() для серверного кода
// Создаем объекты Дата с нужными временными границами, но без привязки к конкретной дате
НачалоДиапазона = Дата(1, 1, 1, 8, 15, 0); // Условная дата 01.01.0001 08:15:00
КонецДиапазона = Дата(1, 1, 1, 12, 15, 0); // Условная дата 01.01.0001 12:15:00
// "Обнуляем" дату у текущего времени, оставляя только временную компоненту
ТекущееВремяБезДаты = Дата(1, 1, 1, Час(ТекущееВремя), Минута(ТекущееВремя), Секунда(ТекущееВремя));
// Проверяем, попадает ли текущее время в интервал
// Предполагаем, что правая граница не включается в диапазон, как было в первом примере.
// Если нужно включить правую границу, используйте "<="
Возврат ТекущееВремяБезДаты >= НачалоДиапазона И ТекущееВремяБезДаты < КонецДиапазона;
КонецФункции
Этот подход более прямолинеен и использует возможности платформы 1С по работе с типом Дата, что делает код более интуитивно понятным.
Один из наиболее тревожных аспектов работы ИИ-ассистентов – это их способность "галлюцинировать", то есть генерировать информацию, которая кажется правдоподобной, но на самом деле является ложной или неточной. 1С:Напарник продемонстрировал это, "придумав" несуществующий предопределенный метод объекта ПриСохраненииНомераДокумента. Вот что он предложил:
// 1С:Напарник предложил:
// "Чтобы добавить дополнительные данные к автоматически формируемому номеру документа,
// необходимо использовать метод ПриСохраненииНомераДокумента или ПередЗаписью модуля объекта документа.
// Однако, для изменения номера документа, предпочтительнее использовать ПриСохраненииНомераДокумента,
// так как этот метод предназначен специально для этих целей.
// Метод ПриСохраненииНомераДокумента вызывается после автоматического присвоения номера документу,
// но перед его записью. Это позволяет модифицировать номер, добавляя к нему дополнительные данные."
Метода ПриСохраненииНомераДокумента не существует в платформе 1С:Предприятие. Это классический пример "галлюцинации" большой языковой модели. ИИ синтезировал описание метода, которое звучит логично и правдоподобно в контексте работы с нумерацией документов, но на самом деле не соответствует действительности. Для изменения номера документа до его записи обычно используется предопределенный обработчик события ПриУстановкеНовогоНомера() или ПередЗаписью() в модуле объекта. Подобные "советы" от ИИ могут привести начинающих разработчиков в тупик и заставить их тратить часы на поиск несуществующего функционала или попытки его реализации, что, безусловно, снижает доверие к инструменту и увеличивает стоимость разработки. Это является ярким примером того, почему весь генерируемый ИИ код и советы должны проходить строгую проверку и верификацию опытными специалистами.
Еще один пример, демонстрирующий недостаточную "глубину" понимания ИИ платформы 1С, касается обработки события ПриКопировании для запрета копирования табличных частей документа или другого объекта. ИИ предложил следующий код:
Процедура ПриКопировании(ОбъектКопирования, ПараметрыКопирования)
// Получаем коллекцию всех табличных частей объекта
КоллекцияТЧ = ОбъектКопирование.ТабличныеЧасти;
// Перебираем каждую табличную часть в цикле
Для Каждого ИмяТЧ Из КоллекцияТЧ Цикл
// Очищаем текущую табличную часть
ИмяТЧ.Очистить;
КонецЦикла;
КонецПроцедуры
В этом коде содержится две критические ошибки, которые делают его неработоспособным:
КоллекцияТЧ = ОбъектКопирование.ТабличныеЧасти; является некорректной. Свойство ТабличныеЧасти у объекта конфигурации (в данном контексте ОбъектКопирования) возвращает не коллекцию *объектов* табличных частей, а их метаданные. Если мы хотим перебрать все табличные части объекта для работы с их данными, необходимо обращаться к метаданным объекта, например, через ОбъектКопирования.Метаданные().ТабличныеЧасти, а затем уже использовать имена табличных частей для доступа к соответствующим коллекциям в самом объекте.Очистить: Во-вторых, в цикле Для Каждого ИмяТЧ Из КоллекцияТЧ Цикл ИмяТЧ.Очистить; переменная ИмяТЧ будет содержать *объект метаданных* табличной части (который содержит ее имя, синоним и другие свойства), а не сам объект табличной части, содержащий данные. Соответственно, попытка вызвать метод Очистить у объекта метаданных приведет к ошибке, так как такого метода у него нет. Правильно было бы обратиться к коллекции табличной части по её имени, которое хранится в свойстве Имя объекта метаданных: ОбъектКопирования[ИмяТЧ.Имя].Очистить();.Таким образом, для очистки всех табличных частей в обработчике ПриКопировании, если мы хотим полностью запретить их копирование, более корректный и функциональный код выглядел бы следующим образом:
Процедура ПриКопировании(ОбъектКопирования, ПараметрыКопирования)
// Перебираем метаданные всех табличных частей объекта
Для Каждого МетаданныеТЧ Из ОбъектКопирования.Метаданные().ТабличныеЧасти Цикл
// Обращаемся к коллекции табличной части по ее имени и очищаем ее
ОбъектКопирования[МетаданныеТЧ.Имя].Очистить();
КонецЦикла;
КонецПроцедуры
Такой подход обеспечивает правильный обход и очистку всех табличных частей, предотвращая их копирование при создании нового объекта.
Обсуждение на форуме и примеры работы 1С:Напарника ярко иллюстрируют текущее состояние генерации кода с помощью ИИ в специфических доменах, таких как 1С:Предприятие. Мы можем сделать несколько ключевых выводов:
1С:Напарник, как и другие ИИ-инструменты, является перспективным направлением, способным автоматизировать часть работы и повысить продуктивность. Однако, мы, как разработчики, должны всегда сохранять критическое мышление, проверять и понимать каждую строку генерируемого кода. ИИ должен быть нашим напарником, ускоряющим и упрощающим работу, но никогда не полностью заменяющим наш профессиональный опыт, ответственность и глубокие знания. Продолжаем совместную работу с ИИ, но теперь с ещё большим вниманием к деталям, чтобы создавать по-настоящему надежные, эффективные и качественные решения в мире 1С.