Почему конфигуратор — тупик
Разговоры про ИИ в 1С почти всегда крутятся вокруг одного сценария: агент пишет код, вы копируете его в конфигуратор, запускаете, смотрите результат. Быстрее, чем писать руками, но принципиально это тот же ручной процесс, где человек работает буфером обмена.
У такого подхода три ограничения, и все три упираются в интерфейс.
Конфигуратор — графическое приложение. На Linux-сервере без графической сессии он вообще не запустится без виртуального экрана, а в виртуальном экране любое модальное окно вешает процесс молча — вы будете смотреть на команду, которая «выполняется» третий час.
Результат не структурирован. Агент получает текст в окне сообщений, который надо парсить глазами. Автоматизировать нечего.
Цикл медленный. Каждая проверка гипотезы — это правка, применение изменений к базе, запуск. Минуты вместо секунд. Агент, который в принципе способен проверить двадцать гипотез за минуту, простаивает.
Вывод, к которому я пришёл: агенту нужна не среда разработки, а контракт. Не «место, где можно писать код», а «набор операций с предсказуемым входом и выходом».
То есть 1С должна стать для агента обычным сервисом с API. Ниже — как это устроить и какие правила проектирования таких методов оказались важными.
Единственный работающий транспорт
Спойлер, за который я заплатил вечером: из всех способов выполнить произвольный код в боевой 1С на Linux-сервере работает ровно один — HTTP-метод в расширении.
Коротко про остальные, чтобы вы не повторяли путь:
1cv8 ENTERPRISE /Executeтолько открывает обработку. Тело модуля объекта не исполняется, аПриОткрытии— это метод формы, которой у обработки без формы нет;- собрать
.epfс формой из исходников пакетной командой не выходит: падает с исключением XDTO при чтении описания формы, причём даже на нетронутом дампе, сделанном той же версией платформы; - готовые консоли кода тянут за собой серверный модуль редактора, который в headless-окружении не поднимается;
- регламентное задание код выполнит, но результат ему некуда вернуть, и цикл правки получается длинным.
Остаётся HTTP. И это, как выяснилось, не компромисс, а лучший вариант из возможных.
Как устроен метод-инструмент
Здесь лежит мина, на которой застревают почти все: написать функцию-обработчик в модуле недостаточно. Маршрут не появится, вы будете получать 404 и перечитывать код в поисках опечатки.
Нужны две вещи одновременно:
- Функция-обработчик в модуле HTTP-сервиса;
- Описание маршрута в XML сервиса — элементы
<URLTemplate>и вложенный<Method>с именем обработчика и HTTP-глаголом.
Если вы правите расширение из командной строки, XML сервиса — просто ещё один файл в каталоге дампа рядом с модулем.
Скелет обработчика:
Функция ПолучитьДанные(Запрос)
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Результат = Новый Структура;
Результат.Вставить("ok", Истина);
Результат.Вставить("data", СобратьДанные());
Ответ.УстановитьТелоИзСтроки(ЗначениеВJSON(Результат), КодировкаТекста.UTF8);
Возврат Ответ;
КонецФункции
Вызов:
curl -u "<пользователь>:<пароль>" \
"http://127.0.0.1/<база>/hs/<корень сервиса>/<метод>" | jq
Две практические детали, которые сбивают с толку в первый раз:
Маршруты кэшируются по сессии. Первый вызов свежесозданного метода вполне может отдать 404, хотя всё сделано правильно. Подождите секунд двадцать и повторите, прежде чем искать ошибку. Я успел дважды перезалить расширение, пока не понял, в чём дело.
Ошибка прав приходит не как 403. Она приезжает невнятной пятисоткой с текстом исключения в теле ответа. Читайте тело целиком, а не только код статуса.
И приятный бонус: если внешний адрес базы требует клиентский сертификат, то при обращении с самого сервера на локальный адрес он не нужен. Отладка от этого сильно упрощается.
Правила проектирования инструментов
Метод, который дёргает человек раз в месяц, и метод, которым пользуется агент, — разные вещи. Правила ниже выведены практикой.
1. Сухой прогон по умолчанию
Самое важное правило. Любой метод, который меняет данные, по умолчанию только считает и показывает, что будет сделано. Реальная запись включается отдельным явным флагом.
# посмотреть
curl -X POST ".../ВыпуститьКарты" -d '{"диапазон": [1000, 1100]}'
# сделать
curl -X POST ".../ВыпуститьКарты" -d '{"диапазон": [1000, 1100], "apply": true}'
Ошибиться в диапазоне или в условии отбора очень легко, а вычищать сотню лишних элементов справочника — вечер. Сухой прогон превращает такую ошибку в строчку вывода.
Дополнительно это меняет поведение агента: он привыкает сначала смотреть, потом делать, потому что интерфейс сам к этому подталкивает.
2. Явное разделение чтения и записи
Метод либо читает, либо пишет. Метод, который «читает и заодно немножко обновляет статус», — источник неприятных сюрпризов.
У меня есть метод переотправки данных во внешнюю систему: он заново собирает структуру документов за период и отправляет её приёмнику, в 1С при этом не записывая ничего. Благодаря этому им можно пользоваться свободно, в том числе для отладки. Как только такой метод начнёт что-то писать, свобода закончится.
3. Идемпотентность на стороне приёмника
Если метод отправляет данные наружу — приёмник обязан быть идемпотентным. У меня внешние системы дедуплицируют по идентификатору документа вместе с фискальным номером и по своему идентификатору продажи.
Без этого повторный вызов задвоит данные, и разбираться с задвоениями вы будете дольше, чем с исходной проблемой. Проверять идемпотентность нужно заранее, а не в момент аварии: отправьте технический документ со сгенерированным идентификатором, повторите отправку, убедитесь, что записи не задвоились, удалите тестовую.
4. Ответ всегда JSON, ошибки — тоже
Агент разбирает вывод программно. Текст в свободной форме превращает разбор в угадайку.
Держите единый конверт ответа: признак успеха, данные, текст ошибки. Тогда любой инструмент обрабатывается одинаково, и агенту не нужно знать особенности каждого.
5. Узкая ответственность
Соблазн сделать «универсальный метод, который умеет всё по параметру» велик. Не поддавайтесь: агент будет путаться в комбинациях параметров, а вы — в том, что метод делает при конкретном наборе.
Лучше десять маленьких методов с понятными именами, чем один со швейцарским ножом внутри.
Прежде чем писать: инвентаризация
Правило, которое сэкономило мне больше всего работы, звучит скучно: сначала посмотри, что уже есть.
Недавняя задача: выгрузить актуальный каталог с остатками в Excel. Первый импульс — написать новый метод выгрузки. Вместо этого я потратил двадцать минут на инвентаризацию того, что уже опубликовано в базе.
Результат: всё собралось из существующих методов, править конфигурацию не пришлось вообще. Номенклатура, характеристики, штрихкоды, дополнительные свойства, склады, виды цен, остатки — всё это уже отдавалось наружу разными методами, накопленными за годы интеграций.
Суммарное время получения данных — около двадцати секунд. Время, которое ушло бы на разработку новой выгрузки, — день минимум, плюс правка боевого расширения со всеми рисками.
Заведите карту существующих методов. Для агента это входная точка: он должен уметь узнать, какие инструменты уже есть, прежде чем предлагать новые.
Грабли при выборе метода
Инвентаризация мало что даёт, если не знать, чем методы отличаются. Три случая из практики.
Похожие методы ведут себя принципиально по-разному. Один метод выгрузки номенклатуры тянет вместе с данными содержимое присоединённых файлов в base64, другой отдаёт только имена файлов. Разница в размере ответа — на порядок, и для массовой выгрузки первый непригоден совсем. Внешне названия почти одинаковые.
Один запрос против сорока восьми. Остатки по всем складам одним вызовом отдаёт только один метод. Другой требует идентификатор конкретного склада — то есть сорок восемь вызовов вместо одного, и возвращает при этом закупочную цену вместо розничной. Выбор метода определяет, займёт задача пять секунд или пять минут.
Агрегация по дереву врёт. В дереве «группа -> товар -> характеристика» количество у групповых узлов бывает нулевым. Суммировать надо только листья, иначе получите неверный итог, который выглядит правдоподобно. Классифицировать узлы приходится по идентификаторам через отдельный справочный запрос.
И общий вывод из той же задачи: не полагайтесь на поля, которые «должны быть заполнены». У меня артикул оказался заполнен ровно у одного товара из четырёх с лишним тысяч — де-факто идентификатором служит штрихкод. Одно свойство было заведено дважды разными элементами и склеивалось по имени. Прежде чем строить логику на поле, посмотрите, насколько оно реально заполнено.
Песочница: где размещать инструменты
Правка расширения означает применение изменений к базе, а это риск. Поэтому размещение имеет значение.
Держите временные и диагностические методы в самом маленьком непрофильном расширении. Логика простая: ошибка в расширении, которое обслуживает кассу, останавливает торговлю; ошибка в расширении, считающем что-то третьестепенное, не стоит почти ничего.
У меня выделено отдельное расширение под эту роль: маленькое, редко меняется, ни на что критичное не влияет. Все эксперименты живут там. Расширение, которое кормит кассу, для экспериментов закрыто.
И обязательно: токен доступа к методу — в константу, а не в код модуля. У меня до сих пор в боевом расширении живёт метод с зашитым токеном, потому что «уберу потом». Не повторяйте.
Что меняется, когда база стала API
Эффекты, ради которых всё затевалось.
Цикл проверки гипотезы сокращается с минут до секунд. Агент дёргает метод, получает JSON, разбирает, делает вывод. Никакого применения изменений к базе на каждый чих.
Результат воспроизводим. Вызов метода — это команда, которую можно положить в скрипт, повторить, показать коллеге, приложить к задаче. Клики в конфигураторе так не документируются.
Отладка интеграции переезжает на приёмник. Вместо того чтобы разбираться, что именно сериализовала 1С, вы смотрите тело запроса в логах принимающей стороны. Там видно ровно то, что пришло.
Появляется граница ответственности. У инструмента есть контракт: вход, выход, коды ошибок. Это то, что можно тестировать и о чём можно договариваться, в отличие от «функции где-то в модуле».
1С перестаёт быть особенной. Для агента она становится обычным сервисом, с которым он работает так же, как с любым другим HTTP API. Вся экзотика платформы остаётся внутри и наружу не протекает.
Выводы
1. Агенту нужен контракт, а не среда. Конфигуратор — интерфейс для человека, и попытки автоматизировать работу через него упираются в графику, модальные окна и медленный цикл.
2. HTTP-метод в расширении — единственный работающий транспорт. Остальные способы выполнить произвольный код в боевой базе на Linux-сервере не работают, и это проверено.
3. Функции в модуле мало. Без описания маршрута в XML сервиса метод не появится, а 404 вы будете списывать на что угодно другое.
4. Сухой прогон по умолчанию — самое дешёвое из полезного. Одна строчка в контракте метода превращает потенциальную катастрофу в строчку вывода.
5. Инвентаризация перед разработкой. В базе, где интеграции копились годами, нужный метод часто уже существует. Двадцать минут поиска против дня разработки и правки боевого расширения.
Смысл подхода не в том, чтобы дать ИИ доступ к 1С. Смысл в том, чтобы описать, что именно можно делать с базой, в виде явного набора операций. Это полезно само по себе — просто агент делает отсутствие такого описания болезненно очевидным.
Платформа 8.3.27, УТ 11.5, сервер 1С на Linux. Подход обкатан на боевых базах розничной сети.
Вступайте в нашу телеграмм-группу Инфостарт