Пять вещей, которые ломаются при подключении нейросети к 1С, и что у неё внутри это объясняет
Иван Недомолков · Опубликовано: · Обновлено:
Языковую модель к учётной системе прикручивают примерно одинаково и одинаково же на неё натыкаются: она уверенно называет несуществующий реквизит, на один и тот же вопрос отвечает по-разному, а счёт за длинный промпт растёт быстрее, чем ожидалось.
Это не дефекты конкретного сервиса. Это прямые следствия устройства, и разбирать их
удобнее не по документации, а на модели, которую можно открыть в отладчике. У нас
такая есть: языковая модель на 21 920 параметров, посчитанная целиком на встроенном
языке 1С. Ни интернета, ни внешней компоненты, ни ONNX, ни HTTP-запроса. Один файл
на 41 килобайт, внутри Массив, Соответствие, БуферДвоичныхДанных и циклы.
Сразу оговорка: это не инструмент. Модель знает сорок фраз про объекты 1С, знаний о мире в ней нет физически, прикладной пользы ноль. Ценность объяснительная - можно поставить точку останова в середине языковой модели и посмотреть на числа своими глазами, вместо того чтобы верить статьям про трансформеры.
Главный тезис скажу первым, дальше он разложится на пять частей. Внутри модели нет
ни одного Если, разбирающего язык. Нет словаря смыслов, нет грамматики, нет
правил. Есть таблица чисел, три вложенных цикла умножения и массив активаций.
Всё “понимание” это статистика, застывшая в весах.
Грабля 1: модель врёт ровно тем же тоном, что и говорит правду
Живые прогоны с экрана. Первые три вопроса модель на обучении видела, четвёртого не видела никогда:
| Вопрос | Ответ | Символов | Умножений | Время |
|---|---|---|---|---|
| что такое регистр сведений | регистр сведений хранит значения по ключу и дате. | 49 | 1 490 560 | 13,2 с |
| что такое регистр накопления | регистр накопления хранит движения и итоги. | 43 | 1 385 344 | 12,6 с |
| что такое обработка | обработка меняет данные базы по правилам. | 41 | 1 227 520 | 10,3 с |
| как жарить котлеты | документ права документ? | 24 | 876 800 | 7,2 с |
Последняя строка тут главная. Температура была ноль, то есть случайности не было вообще: на каждом шаге брался самый вероятный символ. Модель не запнулась и не сказала “не знаю”. Она выдала бессмыслицу тем же ровным тоном и потратила на неё столько же времени на символ.
Почему иначе не бывает, видно в цикле генерации:
Пока СтрДлина(Ответ) < МаксСимволов И Сост.Позиция < Модель.Контекст Цикл
Токен = ВыбратьТокен(Итог.Логиты, Температура);
Симв = Модель.АлфавитСимволов[Токен];
Если Симв = Символы.ПС Тогда
Прервать;
КонецЕсли;
Ответ = Ответ + Симв;
Итог = ШагМодели(Сост, Токен);
КонецЦикла;
Найдите здесь место, куда можно вставить “не знаю”. Его нет. ШагМодели обязана
вернуть 36 чисел, по одному на каждый символ алфавита. Функция вероятностей обязана
превратить их в доли, дающие в сумме единицу. Выбор обязан вернуть какой-то номер.
Ни одна из них не имеет права отказаться, и пустого ответа в этой конструкции
физически не существует: даже на полном шуме распределение получится каким-то,
и максимум в нём найдётся.
Вот и весь механизм галлюцинации. Модель не врёт и не выдумывает, у неё нет для этого ни намерения, ни возможности. Она продолжает последовательность, потому что ничего другого не умеет. На знакомом вопросе продолжение совпадает с правдой, на незнакомом расходится, а снаружи эти два случая неотличимы.
То же самое вы видите, когда просите чат-модель написать запрос по вашей
конфигурации и получаете реквизит Контрагент.ОсновнойМенеджер, которого у вас нет.
Механизм тот же, словарь больше. Почему модель выдумывает реквизиты именно вашей
базы и чем это лечится, разобрано
отдельно.
“Не знаю” у больших моделей ничего не меняет. Эту фразу в них вложили обучением как правильное продолжение для определённого типа вопросов. Она такой же сгенерированный текст, как любой другой, и появляется не потому, что модель заглянула в себя и не нашла знания. Строить на ней логику приложения нельзя.
Честная оговорка, чтобы вы не поверили мне больше, чем следует: утверждение “уверенность не связана со знанием” опирается на текст ответа про котлеты. Распределение вероятностей на этом прогоне не снималось. Может оказаться, что верхний символ набрал не 99%, а 40%, и тогда формально сомнение в числах было, просто наружу не вышло. Кнопка “Один символ” распределение показывает, проверить может любой, кто откроет саму локальную LLM на встроенном языке.
Что с этим делать в проде. Проверять каждый ответ, у которого есть последствия. Когда последствия означают запись в боевую базу, проверки глазами мало: мы используем обёртку с гарантированным откатом транзакции, которая технически не даёт агенту записать в прод.
Грабля 2: один и тот же запрос даёт разные ответы
Ручка “температура” выглядит как регулятор креативности, и это первое заблуждение, которое стоит денег.
Температура вообще не про модель. Модель посчитала свои 36 вероятностей и закончила работу. Дальше кто-то должен выбрать одну букву из тридцати шести, и температура управляет только этим выбором. Вот он целиком, обе ветки:
Если Температура <= 0 Тогда
Лучший = 0;
Для Ном = 1 По Логиты.ВГраница() Цикл
Если Логиты[Ном] > Логиты[Лучший] Тогда
Лучший = Ном;
КонецЕсли;
КонецЦикла;
Возврат Лучший;
КонецЕсли;
Вероятности = ВВероятности(Логиты, Температура);
ГСЧ = Новый ГенераторСлучайныхЧисел;
Порог = ГСЧ.СлучайноеЧисло(0, 1000000) / 1000000;
Накоплено = 0;
Для Ном = 0 По Вероятности.ВГраница() Цикл
Накоплено = Накоплено + Вероятности[Ном];
Если Накоплено >= Порог Тогда
Возврат Ном;
КонецЕсли;
КонецЦикла;
Возврат Вероятности.ВГраница(); // страховка от накопленной погрешности
Три режима, все видны глазами. При нуле берётся самый вероятный символ, обычный поиск максимума в массиве: случайности нет вообще, один и тот же вопрос даёт один и тот же ответ буква в букву. При единице бросается кубик по тем вероятностям, что модель насчитала, без искажений. Выше единицы распределение принудительно сглаживается: перед возведением в экспоненту все числа делятся на температуру, деление на большее число сжимает разрывы, и после нормировки у редких символов шансы резко растут.
На живых числах, один и тот же вопрос:
| Температура | р | п | д | остальные |
|---|---|---|---|---|
| 0 | 99,9 % | 0,1 % | по нулям | тридцать четыре символа по нулям |
| 3 | 87,8 % | 8,3 % | 1,4 % | хвост поднялся до десятых долей процента |
Логиты на обоих прогонах модель посчитала одни и те же, изменилось только деление перед экспонентой. Результат на тройке: один шанс из восьми, что вместо правильной буквы выпадет другая. На каждом символе ответа. Чем это кончается на целой фразе, проверено на пятёрке:
зачем нужна транзакция, температура 5 -> трц: храня зциля вогдменища отнног
Промахи копятся: одна неудачная буква уходит обратно на вход, следующая считается уже по испорченному началу, и через десяток символов текст рассыпается. При нуле та же модель на тот же вопрос отвечает нормальной фразой.
Что с этим делать в проде. Любой сценарий, где ответ модели уходит в код или в документ, требует нулевой температуры. Иначе вы получите плавающее поведение и будете искать ошибку в промпте там, где её нет. И никакой “креативности” в этой ручке не существует: есть степень готовности взять не самый вероятный вариант. Иногда он выглядит как оригинальная мысль, чаще как “вогдменища”.
Грабля 3: вопрос стоит столько же, сколько ответ
По интерфейсу чата этого не видно, а на счёте видно хорошо.
Модель сама считает свои умножения, и число перепроверяется руками. За один шаг она умножает вектор на шесть матриц весов в каждом из двух слоёв и один раз на выходную таблицу:
2 × (4 × 32 × 32 + 2 × 64 × 32) + 36 × 32 = 17 536
Слагаемые: 4 × 32 × 32 это четыре квадратные матрицы внутри внимания (запрос, ключ,
значение и склейка голов обратно), вместе 4 096 умножений; 2 × 64 × 32 это
полносвязный слой, растяжение с 32 координат до 64 и сжатие обратно, тоже 4 096;
итого 8 192 на слой, слоёв два, получается 16 384; 36 × 32 это выходная таблица,
1 152 умножения. 17 536 на один шаг, и это же число обработка печатает сама.
Теперь берём показания счётчика из четырёх прогонов и делим:
| Прогон | Умножений | / 17 536 | Промпт | Ответ |
|---|---|---|---|---|
| регистр накопления, полный ответ | 1 385 344 | 79 | 35 | 44 |
| как жарить котлеты | 876 800 | 50 | 25 | 25 |
| транзакция при температуре 5 | 1 122 304 | 64 | 29 | 35 |
| один проход, кнопка “Один символ” | 631 296 | 36 | 36 | 0 |
Четыре деления, четыре целых числа, ни одного остатка. Раскладка сходится: промпт
собирается строкой "в: " + вопрос + "?" + перевод строки + "о:", обёртка добавляет
семь символов, “что такое регистр накопления” это 28 знаков, плюс семь - вот и 35
шагов. Ответ идёт с ведущим пробелом, который при выводе срезается, поэтому
на 43 показанных символа приходится 44 шага.
Последняя строка стоит отдельного взгляда: кнопка “Один символ” не генерирует ничего, она прогоняет только промпт. Единственный проход по вопросу из 28 знаков стоит 631 296 умножений и почти пять секунд. Каждый символ вашего промпта это отдельный полный проход через все матрицы модели.
Предупрежу возражение: в прайс-листах провайдеров входные токены дешевле выходных. Умножений там поровну, разница в том, как их можно выполнить. Символы вопроса известны сразу все, поэтому железо считает их одной пачкой и загружает видеокарту целиком. Ответ так посчитать нельзя, следующий символ зависит от предыдущего. Одна и та же работа, разная загрузка железа. В 1С пачки нет вообще, поэтому вопрос и ответ стоят ровно одинаково.
Почему длинный контекст дорожает быстрее, чем растёт
Всё, что модель помнит о предыдущих символах, лежит в двух массивах, Сост.КэшК
и Сост.КэшV. Это KV-кэш: на каждом шаге ключ и значение текущего символа
дописываются в конец, а внимание считается по всему кэшу целиком.
Сост.КэшК[НомСлоя].Добавить(КаО);
Сост.КэшV[НомСлоя].Добавить(ВэО);
Две строки, а без них вся затея не работает: пришлось бы на каждый новый символ заново прогонять весь предыдущий текст. В прогоне про регистр накопления 79 шагов, средняя позиция около сорока, значит работы стало бы примерно в сорок раз больше - вместо двенадцати секунд около восьми минут. Это прикидка на бумаге, кэш не отключался и не мерился.
А вот цена самого кэша, посчитанная точно:
| Позиция | Оценки, на голову | Смешивание значений, на голову | Две головы, два слоя |
|---|---|---|---|
| первая | 1 × 16 = 16 | 1 × 16 = 16 | (16 + 16) × 2 × 2 = 128 |
| девяносто шестая | 96 × 16 = 1 536 | 96 × 16 = 1 536 | (1 536 + 1 536) × 2 × 2 = 12 288 |
При неизменных 17 536 умножениях на матрицах один символ к концу контекста стоит почти вдвое дороже, чем в начале. И это при контексте в жалкие 96 символов (не “примерно 96”, а ровно: таблица векторов позиций имеет 96 строк, для 97-й строки просто нет). У больших моделей контекст в тысячи раз длиннее, и там эта часть давно перевешивает всё остальное.
Что с этим делать в проде. Идея “отдадим модели всю конфигурацию целиком” плохо работает даже там, где контекст формально позволяет. Мы на боевой УПП считали это в токенах: полная выгрузка структуры обходится примерно в 790 000 токенов, а выгрузка с фокусом на нужном объекте сжимает её примерно в 25 раз. Экономия линейная по деньгам и квадратичная по вниманию.
Грабля 4: модель не видит букв внутри слова
Задачи вида “посчитай символы”, “проверь контрольный разряд”, “сверь коды посимвольно” языковой модели отдавать бессмысленно, и причина в первом же преобразовании.
Модель не работает с буквами. Первое, что происходит с вопросом, это нарезка
на кусочки и присвоение каждому кусочку номера через обычное Соответствие. Кусочек
называется токеном. В больших моделях токен обычно кусок слова, здесь один символ:
Функция ВТокены(Текст)
Токены = Новый Массив;
Строчный = НРег(Текст);
Для Поз = 1 По СтрДлина(Строчный) Цикл
Токен = Модель.КодыТокенов.Получить(Сред(Строчный, Поз, 1));
Если Токен <> Неопределено Тогда
Токены.Добавить(Токен);
КонецЕсли;
КонецЦикла;
Возврат Токены;
КонецФункции
Восемь строк, никакой лингвистики. Здесь буквы и заканчиваются: дальше по коду
существуют только числа, и вернуть их обратно в буквы можно ровно один раз, в самом
конце. Когда большая модель ошибается в подсчёте букв в слове, дело в том же самом
устройстве: слово для неё это один-два номера, отдельных букв внутри номера она
не видит. Примерно как вы не видите отдельных байтов, работая с ДвоичныеДанные.
Заодно вторая ловушка того же места. Словарь это закрытый перечень того, что модель
вообще различает, здесь их 36: тридцать одна строчная русская буква, пробел, точка,
двоеточие, знак вопроса и перевод строки. Нет заглавных, запятой, букв ё и э,
латиницы и цифр. Всё незнакомое токенизатор молча выбрасывает - это строка
Если Токен <> Неопределено. Напишете “Регистр Накопления, 2026”, модель увидит
“регистр накопления ”.
Дальше номер сам по себе бесполезен: то, что “а” это 5, а “б” это 6, не значит, что они похожи. Поэтому каждому номеру сопоставлен массив из 32 чисел, вектор символа. Для модели смысл сводится к координатам точки в тридцатидвумерном пространстве, и то, что встречается в похожих местах, при обучении заезжает в близкие точки. В больших моделях так рядом оказываются “кот” и “кошка”. Никто этого не программировал, координаты подобрались сами.
Грабля 5: “соберём свою модель прямо в 1С”
Считает 1С около 110-122 тысяч умножений в секунду. Видеокарта делает десятки триллионов. Разрыв в восемь порядков, и он не лечится оптимизацией кода.
В 1С каждое число это объект. Массив это коллекция ссылок, сплошного куска
памяти с числами подряд под ним нет, плоского буфера float в платформе
не существует. Отсюда следует остальное: векторные инструкции процессора требуют
непрерывной памяти, а её тут не бывает; видеокарты нет; потоков внутри модуля нет.
Цена одного только хранения замерена явно, 64 000 умножений в двух вариантах:
| Как хранятся веса | Время |
|---|---|
| байтами в буфере, разворот байта в число в каждом умножении | 688 мс |
| развёрнуты в обычные массивы чисел один раз при загрузке | 266 мс |
Разница в 2,6 раза ушла целиком на разворот байта в число. Поэтому в загрузчике тензор разворачивается один раз, а не миллион раз внутри цикла.
Второй замер, про цену размера. Собрана вторая версия обработки, с моделью на 76 608 параметров вместо 21 920: код тот же, длина вектора 64 вместо 32, матрицы квадратные, поэтому умножений на символ почти вчетверо больше. Один и тот же ответ длиной 43 символа:
| Модель | Время |
|---|---|
| 21 920 параметров | 8,3 с |
| 76 608 параметров | 28,3 с |
Оба числа сняты одним заходом на одной машине.
Теперь потолок. Коэффициент “умножений на шаг к числу параметров” здесь около 0,8, и он не случайный: почти все параметры лежат в матрицах, а матрицы работают каждый шаг. Модель на сто миллионов параметров потребует порядка восьмидесяти миллионов умножений на символ, то есть больше десяти минут на один символ. Реалистичный предел для встроенного языка выходит 1-10 миллионов параметров, и то с оговорками. Расчёт построен на двух точках, дальше прямая экстраполяция: на порядок ей верить можно, на пять порядков вверх её никто не проверял.
При этом кое-что на таком потолке живёт, и различие в типе задачи. Классификатор делает один проход на весь текст и выдаёт метку. Генератор делает проход на каждый символ ответа. Разница равна длине ответа в символах, то есть десятки и сотни раз при той же модели. Поэтому в чистом BSL остаются задачи без генерации: классификация обращений и статей затрат, извлечение ИНН и сумм, поиск похожей номенклатуры, нормализация к справочнику. Диалог, суммаризация и знания о мире отпадают. Оговорюсь: ни одного такого классификатора мы пока не собрали, это расчёт, а не отчёт о работе.
Если задача настоящая, а не учебная, честный путь другой: готовая модель вроде
ru-e5-small через ONNX Runtime во внешней компоненте. В сотни раз быстрее и без
своего обучения. Только это уже не чистый BSL, и объяснять там нечего.
Два хода, которые предлагают первыми
“Раскидайте умножение по фоновым заданиям”. Между шагами не работает: следующий символ зависит от предыдущего, порядок жёсткий. Внутри одного шага теоретически можно, матрицы независимы. Только шаг стоит около сотой доли секунды, а передача массивов в фоновое задание и обратно идёт через сериализацию и обходится дороже самого счёта. Замера у нас нет, если у кого-то есть цифры, приносите.
“Посчитайте умножение матриц запросом к СУБД”. Идея хорошая, и для одного большого умножения может выиграть. Умножение вектора на матрицу это ровно то, что язык запросов умеет делать группировкой:
// Набросок, замера нет. Вектор и матрица лежат во временных таблицах:
// Вектор (Индекс, Значение)
// Матрица (Строка, Колонка, Значение)
ВЫБРАТЬ
Матрица.Колонка КАК Индекс,
СУММА(Вектор.Значение * Матрица.Значение) КАК Значение
ПОМЕСТИТЬ Результат
ИЗ
ВТВектор КАК Вектор
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТМатрица КАК Матрица
ПО Вектор.Индекс = Матрица.Строка
СГРУППИРОВАТЬ ПО
Матрица.Колонка
Проблема на следующем шаге: шагов в разобранном прогоне 79, на каждом по семь умножений на матрицу, и каждое требует положить вектор во временную таблицу и забрать результат обратно. Обмен с сервером на каждый шаг съест выигрыш, а на файловой базе выигрывать нечего вовсе. Ход не проверялся, и он самый интересный из неотработанных.
Что внутри на самом деле: восемь шагов
Раз уж все пять граблей объяснились устройством, соберём его в одном месте. Порядок
внутри ШагМодели:
- Текст превращается в числа через
Соответствие. - К вектору символа прибавляется вектор его позиции. Без этого модель не отличит “кот” от “ток”: внимание складывает прошлые векторы с коэффициентами, а сумма от порядка слагаемых не зависит, значит порядок надо занести прямо в вектор.
- Внимание. Из вектора текущего символа тремя умножениями получаются запрос (“что этот символ ищет”), ключ (“по чему его можно найти”) и значение (“что он отдаст нашедшему”). Каждый символ кладёт свои ключ и значение в общий список и больше не меняет. Текущий сравнивает свой запрос с ключами всех предыдущих, веса нормируются до суммы в единицу, и с ними складываются значения. В коде это два вложенных цикла и умножение:
Оценки = Новый Массив(Позиция + 1);
Для Прошлая = 0 По Позиция Цикл
КПрошлой = КэшК[Прошлая];
Скаляр = 0;
Для Ном = Начало По Начало + Модель.РГоловы - 1 Цикл
Скаляр = Скаляр + КвО[Ном] * КПрошлой[Ном];
КонецЦикла;
Оценки[Прошлая] = Скаляр * Модель.МасштабВнимания;
КонецЦикла;
Операция знакомая, взвешенная сумма есть в любом отчёте. Необычно одно: коэффициенты никто не задавал, матрицы подобрались при обучении сами. 4. Полносвязный слой с ReLU. Вектор растягивается вдвое, прогоняется через нелинейность, сжимается обратно. Нелинейность это одна строка:
Если Скрытый[Ном] < 0 Тогда
Скрытый[Ном] = 0; // ReLU, вот и вся нелинейность
КонецЕсли;
Без неё всё разваливается: два умножения на матрицу подряд сворачиваются в одно, и вся модель на два слоя стала бы равна одному умножению на одну матрицу. Обнуление отрицательных сворачиванию не поддаётся, и только поэтому слои остаются разными слоями. 5. Нормализация. Слои складывают свой результат с тем, что пришло на вход, числа растут, и без приведения к общему масштабу через два слоя они разъезжаются на порядки. Таких нормализаций пять. 6. На выходе 36 чисел, по одному на символ алфавита. Буквы среди них нет. 7. Ответ рождается циклом: предсказали символ, подставили обратно на вход. 8. KV-кэш хранит ключи и значения прошлых символов.
Все параметры модели раскладываются так:
| Что | Параметров | Откуда число |
|---|---|---|
| векторы символов | 1 152 | 36 символов алфавита × 32 координаты |
| векторы позиций | 3 072 | 96 позиций контекста × 32 координаты |
| два слоя | 16 384 | 8 192 на слой |
| нормализация | 160 | пять нормализаций × 32 координаты |
| выходная таблица | 1 152 | 32 координаты × 36 символов |
Сравните с арифметикой из третьей грабли, и увидите занятное: умножений на шаг ровно столько же, сколько весов в матрицах (16 384 плюс 1 152), каждый вес используется по одному разу. А 1 152 параметра векторов символов, 3 072 параметра позиций и 160 параметров нормализации в умножениях не участвуют вообще, эти таблицы читаются по индексу. Отсюда и коэффициент 0,8, по которому считался потолок.
И теперь можно вернуться к тезису из начала: в ШагМодели ровно три Если.
Обнуление отрицательных, пропуск ничтожных весов внимания и служебная выгрузка карты
внимания для показа. Ни один не смотрит, какая это буква.
Где кончается сходство с большими моделями, чтобы потом не ловили на слове. Позиции у них считаются формулой поворота координат, здесь таблица на 96 строк. Нелинейность там гладкая, здесь грубое обнуление. Токен там кусок слова, здесь символ. Отличий наберётся ещё десяток, и они не заложены сознательно: с ними код перестаёт читаться. Одинаковой остаётся цепочка: номера, векторы, внимание, полносвязный слой, вероятности, выбор. Устройство конкретно GPT показать невозможно, его никто снаружи не видел.
Ещё две честные оговорки. Внутри 1С здесь только инференс, то есть счёт по готовым весам; обучение сделано снаружи на PyTorch (40 пар вопрос-ответ, корпус на 2 867 символов, двенадцать тысяч шагов, около восьми минут на обычном процессоре). И сорок шаблонных пар на 21 920 параметрах означают, что модель запоминает фразы, обобщения там нет и взяться ему неоткуда.
История про метод, которого не существует
Один сюжет из разработки, и он к теме относится прямее, чем кажется.
Веса лежат в бинарном макете, целые числа по четыре байта, младший первым. В первой версии загрузчика они читались так:
Значение = Буф.ПолучитьЦелое32(Позиция, ПорядокБайтов.LittleEndian);
Метод выглядит абсолютно правдоподобно. Логично называется, у соседних типов
платформы такие есть, в описании формата подразумевается сам собой. Проблема одна:
у БуферДвоичныхДанных его не существует. Выяснилось запуском на живой платформе.
Замена заняла три строки:
Функция Цел32(Буф, Поз)
Возврат Буф[Поз] + Буф[Поз + 1] * 256 + Буф[Поз + 2] * 65536 + Буф[Поз + 3] * 16777216;
КонецФункции
Мораль прямая: код, написанный по описанию формата, проверяется запуском, чтением он не проверяется. Тем более если часть его подсказала языковая модель - она сгенерирует несуществующий метод с той же уверенностью, с какой эта обработка отвечает про котлеты. Механизм у двух случаев один и тот же, и это первая грабля из списка выше, встреченная на собственной шкуре.
Что забрать с собой
- Уверенность модели не связана со знанием. Она обязана продолжить последовательность и продолжает её всегда, есть в весах что-то по теме или нет.
- “Не знаю” это тоже сгенерированный текст. Логику приложения на нём строить нельзя.
- Температура управляет выбором буквы и до модели не дотягивается. Нужна повторяемость - ставьте ноль.
- Промпт стоит той же арифметики, что и ответ, а внимание к тому же дорожает по мере заполнения контекста.
- Модель не видит букв внутри токена. Считать символы и сверять коды это работа для кода, а не для модели.
- Потолок чистого BSL около миллиона-десяти миллионов параметров. Дальше упирается физика языка: каждое число объект, сплошных буферов нет, векторных инструкций нет.
Один ход мы проверять не стали и честно отмечаем. Замер 688 против 266 миллисекунд показал только одно: разворачивать байт в число внутри цикла дорого. Он ничего не говорит про целочисленную арифметику как таковую, и вполне может оказаться, что счёт целыми в буфере с редким применением масштаба быстрее того, что собрано сейчас.
Посмотреть своими глазами
Обработка лежит на Инфостарте отдельной карточкой: Локальная LLM на встроенном языке
1С. Бесплатно, один файл, веса внутри
макетом, наружу она не ходит вообще. Открывайте в Конфигураторе и ставьте точку
останова в ШагМодели: в модуле объекта 471 строка вместе с комментариями, и они
читаются.
⚠️ Обработка требует 8.3.27.1606, и дело не в коде. Сам код заработает и на более
старых платформах, ему хватит БуферДвоичныхДанных, а это 8.3.14. Дело в файле
.epf: он сохранён конфигуратором 8.3.27, и на 8.3.21 просто не откроется.
Пересохраните у себя, весь исходный текст модуля лежит внутри и читается
в конфигураторе.
Соседние инструменты той же линейки:
- Выгрузка метаданных для LLM даёт структуру вашей конфигурации в виде, который модель читает без пересказа.
- Матрица прав доступа для нейросети показывает, кто и что реально видит в базе. Первое, что спрашивают, когда доступ получает внешний инструмент.
- Анализ кода внешних обработок разбирает чужой код лексером и парсером на чистом BSL. Тот же принцип: всё считается внутри 1С. Разбор метода есть отдельной статьёй.
Это второй наш заход, где на голом BSL считается то, ради чего обычно берут внешнюю компоненту. Первым было RSA-шифрование без COM и .NET, но там смысл был прикладной: код должен работать и на Linux-сервере, и в тонком клиенте. Здесь смысл объяснительный.
Если языковую модель надо не разбирать, а встраивать в работающую учётную систему, это уже наша обычная работа: подключение внешних моделей к 1С, обезличивание того, что уходит наружу, режим только на чтение для агента и замеры до и после. Посмотрите услугу оптимизации и доработки 1С или напишите с описанием задачи, мы скажем, где языковая модель поможет и где она вам не нужна.
Как наблюдать обучение и не спутать его с проверкой знаний
Для сравнения стадий обучения задавайте один и тот же вопрос при зафиксированных условиях генерации. Сохраните не только удачный ответ, но и повторы с ошибками. Улучшение связности текста показывает изменение генерации; из него не следует, что модель правильно называет методы платформы или учитывает особенности вашей конфигурации.
В учебной обработке со стадиями обучения доступны двенадцать состояний одной модели. Это наблюдение за механизмом, а не готовый помощник для рабочего учёта. Для прикладной приёмки потребуется отдельный набор вопросов с проверенными ответами, включая запросы к несуществующим методам. Как такой набор собирают для ревью кода и что в нём считать, показано на приёмке локальной модели на рабочем коде 1С. Отсутствие достаточных данных должно приводить к отказу от вывода, а не к правдоподобному названию.
Если модель получает диагностические данные, числа сначала рассчитывает обычный код. Затем в ответе проверяют связь утверждения с исходной строкой. Пример такого процесса есть в разборе снимка журнала регистрации для нейросети.