Как проверить запрос 1С, который написала нейросеть: ловушки типовой и сверка с эталоном
Иван Недомолков · Опубликовано:
Запрос к 1С, который написала нейросеть, проверяют по результату. Его запускают на базе, где заранее известен правильный ответ, и сравнивают с эталоном каждую строку. Всё, что слабее, пропускает самые дорогие ошибки. На нашем замере три модели написали по памяти 90 запросов к типовой УТ 11.5. Упали с ошибкой 35. Ещё 12 отработали без единого замечания и вернули неверные строки, и глазами из этих двенадцати не видно ни одного.
Ниже методика по шагам: какие уровни проверки что ловят, в каких местах типовой модели ошибаются тихо, как устроить сверку и где у неё граница.
Три уровня проверки и что проходит мимо каждого
| Уровень | Что ловит | Что пропускает | Пример из замера |
|---|---|---|---|
| Запрос выполнился | выдуманные имена, SQL-слова, синтаксис | любую ошибку в смысле | остатки 94 752 вместо 81 838, ни одного замечания платформы |
| Совпало число строк | потерянную группировку, лишний отбор | неверные значения в правильном числе строк | в заданиях 1-3 модель вернула 2, 1 и 2 строки, как эталон, и ни одна строка не совпала |
| Строки совпали с эталоном | неверные значения, лишние и потерянные строки | ошибку, которая на этой базе не проявилась | чтение двух регистров цен через ОБЪЕДИНИТЬ ВСЕ верно, пока заполнен один |
Первые два уровня выглядят разумно и почти ничего не дают. Первую версию нашей сверки мы хотели строить по числу строк и суммам числовых колонок, и она бы засчитала задания 1-3 как верные. Работает только третий уровень, и то с оговоркой из последней строки: эталон доказывает правильность на своих данных, про вашу базу он молчит.
Карта ловушек: тихие и громкие
Ошибки модели в языке запросов делятся на два класса. Громкие платформа останавливает сама, с текстом и местом ошибки, чинятся они за минуту. Тихие выполняются и отдают число, которое попадёт в отчёт.
| Ловушка | Что пишет модель | Платформа | Правильно |
|---|---|---|---|
| Физическая таблица регистра накопления | СУММА(ВНаличии) по РегистрНакопления.ТоварыНаСкладах | молчит | виртуальная таблица .Остатки() |
| Два регистра цен | старый ЦеныНоменклатуры | молчит, ноль строк | ЦеныНоменклатуры25 в свежих релизах |
| Перечисление против строки | ТипНоменклатуры = "Товар" | молчит, ноль | ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Товар) |
| Ресурс без суффикса в виртуальной таблице | Остатки.ВНаличии | ошибка | ВНаличииОстаток, ВНаличииПриход |
| Вид движения строкой | ВидДвижения = "Приход" | ошибка | виртуальная таблица оборотов |
| Метод объекта в запросе | ТипНоменклатуры.Представление() | синтаксическая ошибка | ПРЕДСТАВЛЕНИЕ(Н.ТипНоменклатуры) |
| Имя по аналогии | Недействительный вместо Недействителен, Валюта вместо ВалютаЦены | ”Поле не найдено” | имя из метаданных |
| Диалект SQL | СРЕДН, ДЛИНА, ИМЕЯ, ПОРЯДОК ПО | ошибка | СРЕДНЕЕ, ДЛИНАСТРОКИ, ИМЕЮЩИЕ, УПОРЯДОЧИТЬ ПО |
| Псевдоним-ключевое слово | ... КАК В | ”Ожидается имя” | любой псевдоним, кроме В, И, ИЗ |
Три верхние строки и есть то, ради чего нужна сверка. Остальные шесть видны при первом запуске. Ниже разбор тихих ловушек, потому что именно на них проверку чаще всего и экономят.
Ловушка 1. Остаток по физической таблице
Самый показательный случай замера. Задание: остатки в наличии на конец периода по складам, число позиций и количество. Младшая модель написала так:
ВЫБРАТЬ
ТоварыНаСкладах.Склад КАК Склад,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ТоварыНаСкладах.Номенклатура) КАК КоличествоПозиций,
СУММА(ТоварыНаСкладах.ВНаличии) КАК ВНаличии
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ГДЕ
ТоварыНаСкладах.Период <= &КонецПериода
И ТоварыНаСкладах.ВНаличии <> 0
СГРУППИРОВАТЬ ПО
ТоварыНаСкладах.Склад
Все имена настоящие. Беда в устройстве регистра: в его физической таблице ресурс хранится без знака, а приход или расход записан в поле ВидДвижения. Сумма по колонке сложила приход с расходом. Эталон идёт через виртуальную таблицу:
ВЫБРАТЬ
О.Склад КАК Склад,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ О.Номенклатура) КАК КоличествоПозиций,
СУММА(О.ВНаличииОстаток) КАК ВНаличии
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&КонецПериода, ) КАК О
ГДЕ
О.ВНаличииОстаток <> 0
СГРУППИРОВАТЬ ПО
О.Склад
Что получилось на тестовой базе:
| Показатель | Запрос модели | Эталон |
|---|---|---|
| Строк | 1 | 1 |
| Позиций | 2 898 | 2 898 |
| В наличии | 94 752 | 81 838 |
Приход за период 88 295, расход 6 457. Сумма даёт 94 752, разность 81 838. Промах 12 914 штук, это ровно два расхода и 16 % от верного остатка. Число строк и позиций совпали, сумма правдоподобная. На базе с годами движений такой остаток раздулся бы в разы и бросился бы в глаза, а на молодой базе он прячется. Та же модель повторила приём на регистре товаров организаций и получила те же 94 752 вместо 81 838.
Признак для ревью простой: агрегат по ресурсу регистра накопления без .Остатки( или .Обороты( в тексте запроса. Поля виртуальных таблиц у этого регистра такие:
| Таблица | Поле | Что это |
|---|---|---|
.Остатки(&КонецПериода, ) | ВНаличииОстаток | остаток на дату |
.Обороты(&НачалоПериода, &КонецПериода, , ) | ВНаличииПриход, ВНаличииРасход | движения за период |
| физическая таблица | ВНаличии, ВидДвижения | записи движений, ресурс без знака |
Приход и расход по складам эталон берёт из оборотов:
ВЫБРАТЬ
О.Склад КАК Склад,
СУММА(О.ВНаличииПриход) КАК Приход,
СУММА(О.ВНаличииРасход) КАК Расход
ИЗ
РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, , ) КАК О
СГРУППИРОВАТЬ ПО
О.Склад
Модель в этом задании снова пошла в физическую таблицу, на этот раз с разбором ВЫБОР КОГДА ВидДвижения = "Приход". Этот вариант платформа остановила: вид движения это значение системного перечисления, со строкой его не сравнить. Здесь падение сработало как защита.
Ловушка 2. Имя регистра существует, данных в нём нет
В УТ 11.5 два регистра цен: старый ЦеныНоменклатуры и ЦеныНоменклатуры25, куда цены пишутся в свежих релизах. На тестовой базе старый пуст, в новом цены у 28 938 позиций. Обе младшие модели взяли старый:
ВЫБРАТЬ
ЦеныНоменклатуры.ВидЦены КАК ВидЦены,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЦеныНоменклатуры.Номенклатура) КАК КоличествоПозиций
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ГДЕ
ЦеныНоменклатуры.Период <= &КонецПериода
И ЦеныНоменклатуры.Цена <> 0
СГРУППИРОВАТЬ ПО
ЦеныНоменклатуры.ВидЦены
Запрос выполнился и вернул ноль строк. В отчёте это выглядело бы как “цен нет”. Эталон берёт срез последних по новому регистру:
ВЫБРАТЬ
Ц.ВидЦены КАК ВидЦены,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Ц.Номенклатура) КАК КоличествоПозиций
ИЗ
РегистрСведений.ЦеныНоменклатуры25.СрезПоследних(&КонецПериода, ) КАК Ц
СГРУППИРОВАТЬ ПО
Ц.ВидЦены
Эту ловушку не лечит и описание метаданных: оба регистра в конфигурации есть, по структуре не видно, какой из них живой. Средняя модель с описанием объектов исправила почти всё, и три её оставшихся тихих промаха пришлись как раз на задания про цены. Старшая модель в режиме с описанием прочитала оба регистра через ОБЪЕДИНИТЬ ВСЕ, и на нашей базе это верно. Где заполнены оба регистра, такой запрос смешает старые цены с новыми.
Если модель пишет запрос к срезу, отбор ставится в параметры виртуальной таблицы, иначе срез строится по всему регистру. Этот приём с замерами разобран в статье о приёмах оптимизации запросов 1С.
Ловушка 3. Ключи аналитики вместо номенклатуры
В регистре “Выручка и себестоимость продаж” нет измерений Номенклатура и Партнер. Там стоят ссылки на справочники ключей аналитики, а номенклатура и партнёр лежат у ключей реквизитами. Средняя модель писала ВыручкаИСебестоимостьПродаж.Номенклатура напрямую и заодно придумала отбор по перечислению ВидыСебестоимостиПродаж, которого в типовой нет. Это громкая ошибка, но показательная: без описания объектов ни одна из двух младших моделей не решила ни одного из четырёх заданий по этому регистру. Правильная форма идёт через точку:
ВЫБРАТЬ ПЕРВЫЕ 10
Выр.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
СУММА(Выр.КоличествоОборот) КАК Количество,
СУММА(Выр.СтоимостьБезНДСОборот) КАК Себестоимость
ИЗ
РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Выр
СГРУППИРОВАТЬ ПО
Выр.АналитикаУчетаНоменклатуры.Номенклатура
УПОРЯДОЧИТЬ ПО
Себестоимость УБЫВ
Старшая модель написала ровно этот запрос, только назвала таблицу В. Платформа ответила “Ожидается имя”: В это оператор языка запросов. Все пять ошибок старшей модели по памяти такие, четыре в заданиях по продажам и одна на справочнике видов цен. На этом же псевдониме споткнулись и наши собственные эталоны при первом прогоне.
Ловушка 4. Перечисления и представления
Два задания проверяют одно знание с двух сторон. Отбор по типу номенклатуры требует ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Товар). Младшая модель написала имя без ЗНАЧЕНИЕ и получила “Поле не найдено”. Тип строкой, как его видит пользователь, возвращает функция:
ВЫБРАТЬ
ПРЕДСТАВЛЕНИЕ(Н.ТипНоменклатуры) КАК ТипНоменклатуры,
КОЛИЧЕСТВО(*) КАК Количество
ИЗ
Справочник.Номенклатура КАК Н
ГДЕ
НЕ Н.ЭтоГруппа
И НЕ Н.ПометкаУдаления
СГРУППИРОВАТЬ ПО
ПРЕДСТАВЛЕНИЕ(Н.ТипНоменклатуры)
Модель вместо неё вызвала метод объекта Номенклатура.ТипНоменклатуры.Представление(), а методов язык запросов не знает. Тихий вариант той же ошибки мы проверяли намеренной порчей: сравнение перечисления со строкой “Товар” выполняется и возвращает ноль, потому что ссылка никогда не равна строке.
Помогает ли модели описание метаданных
Второй прогон шёл с описанием объектов: к каждому заданию дописаны реквизиты, измерения, ресурсы и табличные части нужных объектов с типами, прямо из метаданных базы. Примерно это модель получает при подключении к базе через MCP. Промпт вырос с 7 050 до 113 852 знаков.
| Модель | Верно по памяти | Верно с описанием | Тихих промахов по памяти | Тихих с описанием | Ошибок по памяти | Ошибок с описанием |
|---|---|---|---|---|---|---|
| Claude Opus 5.5 | 25 | 26 | 0 | 0 | 5 | 4 |
| Claude Sonnet 5 | 10 | 26 | 7 | 3 | 13 | 1 |
| Claude Haiku 4.5 | 8 | 9 | 5 | 1 | 17 | 20 |
Каждая пара прогнана один раз, ответы модели от раза к разу меняются, поэтому разницу в один-три запроса мы за эффект не считаем. Уверенно видно одно: средней модели описание дало 26 верных вместо 10, её ошибки были про имена, а имена описание и приносит. Старшая имена знала и без него, её единственная ошибка, псевдоним, к метаданным отношения не имеет. У младшей сменился характер ошибок: вместо выдуманных имён пошли SQL-слова и ресурсы без суффикса в виртуальных таблицах. Правил языка в описании нет, и в сотне тысяч знаков маленькая модель, похоже, теряет даже то, что знала.
Как выгрузить структуру своей конфигурации для модели компактно, разобрано в статье про выгрузку метаданных 1С для нейросети. Проверку результата описание не отменяет: оно не знает, какой регистр цен живой, и не мешает писать остаток по физической таблице.
Как устроить сверку с эталоном
Эталон это запрос, который вы проверили сами на той же базе. Модель получает задачу словами и имена колонок результата, дальше оба запроса выполняются с одними параметрами. Правила, на которых мы остановились после отладки:
- Колонки ищутся по именам из задания. Если модель назвала их иначе, но число колонок совпало, сверка идёт по порядку.
- Порядок строк не важен.
- Числа сравниваются с округлением до двух знаков, NULL в числовой колонке равен нулю.
- Если эталон отдаёт значение типа Строка, а модель ссылку, ссылка сравнивается своим представлением.
- Строки сравниваются как мультимножество: каждой строке модели нужна пара в эталоне, лишних быть не должно.
- Эталон, который не выполнился на этой базе (другой релиз, нет регистра), получает свой статус и в счёт ошибок модели не идёт.
Перед тем как мерить модели, проверьте саму сверку. Мы сделали три контрольных прогона: эталоны, поданные как ответ, дали 30 верных из 30. Пустой ответ дал 30 “нет ответа”. Набор с шестью намеренными порчами поймался ровно на четырёх испорченных заданиях: старый регистр цен, перечисление строкой, снятый отбор групп, опечатка в СГРУППИРОВАТЬ. Две оставшиеся правки, ссылка вместо представления и колонки под другими именами, прошли, так и задуманы правила 1 и 4. Сверка, которая ни разу не сказала “не те строки” на испорченном ответе, ничего не доказывает.
Две вещи, которые ломают эталон:
- Пустой эталон. Виртуальная таблица оборотов не отдаёт записи, где все выбранные ресурсы равны нулю. Наши первые задания по продажам считали выручку, в тестовой базе она нулевая, и три эталона вернули ноль строк. Задания пришлось перевести на себестоимость и количество. Пустой эталон совпадает с любым пустым ответом, поэтому его надо помечать как непоказательный.
- Кривые данные. В заданиях 1-3 средняя модель взяла тип номенклатуры через вид номенклатуры. Где тип в карточке заполнен (7 803 позиции), он совпадает с типом вида. Но у 49 238 позиций тип в карточке пуст при заполненном виде, и результаты разошлись. Сверка права относительно базы, а ошибка модели спорная. Без этих трёх заданий тихих промахов по трём моделям 9 из 90 вместо 12. Поэтому эталон и ответ модели должны лежать рядом: “не те строки” решает человек.
Стенд, в котором это уже собрано
Методику выше мы упаковали во внешнюю обработку для УТ 11.5: Стенд проверки запросов нейросети. В ней 30 заданий по 16 категориям, от справочников до соединений, у каждого эталон. Промпт обработка собирает сама, с описанием объектов или без. Ответ модели можно вставить из любого чата или получить по OpenAI-совместимому адресу, например от локальной модели в Ollama. Задания лежат в JSON на отдельной странице формы, и к ним можно дописать свои, под доработки своей конфигурации. Сверка 30 заданий на тестовой базе занимала от 0,5 до 1 секунды. Карточка с файлом: на Инфостарте.
Запускать её стоит на копии базы: запросы модели выполняются с вашими правами, и запрос без отборов по большой таблице может идти долго. Период задайте так, чтобы в него попали продажи и движения, иначе эталоны окажутся пустыми.
Что замер пока не покрывает
Одна база УТ 11.5.22 с тестовыми продажами за июнь-август 2026 года, один прогон на пару “модель и промпт”, три модели одного семейства. Локальной модели в таблице нет: Ollama на машине стоит, но без загруженной модели, а мерить по чужим таблицам мы не хотим. Это первое, что мы прогоним следующим. Если вы уже пускаете нейросеть писать запросы к своей базе, самый полезный для нас вопрос: на каком релизе и какой модели у вас выходит больше всего запросов “выполнились, но строки не те”?