Как дать LLM контекст формы 1С без выдуманных связей

14.08.26

Интеграция - Нейросети

Модуль формы сам по себе почти ничего не говорит LLM (Large Language Model - большая языковая модель) о поле, его типе и месте в структуре. Я собрал подтверждённые факты из elem.json, модуля и метаданных в компактный неизменяемый FormContext, не подменяя неизвестные связи догадками. Подход подойдёт разработчикам 1С, которым нужен короткий и проверяемый контекст формы для модели, а не сырая выгрузка целиком.

Когда я впервые получил распакованную форму, казалось, что задача уже решена. Вот дерево элементов, вот модуль, вот метаданные. Можно отдавать это модели.

На первом же вопросе стало ясно, что нет. Поле с Object.Counterparty не объясняет ни тип реквизита, ни синоним, ни то, действительно ли эта связь относится к конкретному элементу. А отправить в запрос всю распаковку - значит потратить бюджет на технический шум и случайно выдать модели предположение за факт.

Сначала я пытался получить максимум прямо из парсера: за один проход связать элементы формы, реквизиты и типы, в том числе по совпадению имён. На небольшом примере это выглядело убедительно, но на полном корпусе могло породить недоказанные связи. Поэтому я отказался от универсального парсера и разделил задачу на проверку отдельных фактов.

Все количественные результаты ниже получены на демонстрационной базе УТ 10.3. Они показывают эффект на этом проверенном корпусе, но не обещают такое же покрытие на других конфигурациях.

 

Коротко: что делать

  • Считайте elem.json, модуль формы и метаданные разными источниками фактов. Не соединяйте их по похожему имени.

  • Принимайте data_path только после проверки структуры. Неизвестная раскладка должна оставить пробел и добавить предупреждение.

  • Разрешайте ссылочный UUID (Universally Unique Identifier - уникальный идентификатор) через индекс всей выгрузки. Если ответа нет, сохраняйте Ref#<uuid>.

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

  • Держите в metadata только шесть безопасных полей и делайте пути относительными.

  • Формируйте фрагмент для модели в порядке FORM, SUMMARY, BSL (Built-in Script Language - встроенный язык 1С). По умолчанию возвращайте его полностью, а положительный max_chars задавайте только там, где нужен явный символьный лимит.

  • Очищайте не только поля, но и диагностические сообщения. Абсолютный путь в предупреждении - такая же утечка, как путь в метаданных.

Схема: распакованная форма, проверенные источники, FormContext и фрагмент для LLM с явным лимитом

 

Сначала доказать связь, потом обогащать

У формы есть минимум три разные правды. elem.json описывает дерево и привязки. Модуль хранит код обработчиков. Метаданные помогают понять, где лежат артефакты и можно ли им доверять.

Первой моей ошибкой было желание склеить их по именам. Это хорошо работает на демонстрационном примере и быстро ломается на реальной структуре: похожее имя ещё не делает элемент реквизитом. Поэтому наружу из декодера выходит только имя, тип и подтверждённый data_path.

Путь извлекается из data[*].raw лишь для распознанного формата. Повреждённая, неоднозначная или неизвестная раскладка не получает красивой догадки. Элемент остаётся без пути, а причина уходит в диагностику. Для модели это полезнее ложной уверенности.

 

Сегментный путь: два независимых признака

Самый неприятный случай - путь из нескольких сегментов. В сыром блоке рядом встречаются локальные идентификаторы, идентификаторы таблиц и UUID типов. Если просто найти первый UUID и назначить ему смысл, можно получить технически существующую, но неверную связь.

Я принял сегментный путь только при двух условиях:

  1. UUID таблицы объявлен среди адресов определений этой же формы.

  2. Склеенные имена всех сегментов посимвольно совпадают с именем элемента.

Это дало 377 новых подтверждённых data_path без изменения прежних путей. Именно отсутствие изменений здесь важнее самого прироста: новый разбор расширил покрытие, но не переписал уже доказанные связи.

Практическое правило: для нового формата нужны минимум два независимых подтверждения. Один найденный UUID - это повод исследовать раскладку, а не публиковать связь.

Ссылочный тип: индекс вместо догадки

У реквизита может быть не примитивный тип, а ссылка на другой объект. В таком случае декодер честно возвращает Ref#<uuid>. Это корректно, но человеку и модели от такой строки мало пользы.

Здесь мне помог не новый поиск в каждом объекте, а индекс, который строится при общем обходе выгрузки. scan_forms() уже проходит дерево, поэтому именно там логично сопоставить UUID и читаемое имя. Декодеру передаётся только функция разрешения:

index = scan_forms(export_root)
result = decode_object_attributes(
    object_json,
    type_resolver=index.resolve_reference_type,
)

Важная граница контракта: резолвер необязателен. Если его нет, он вернул None или завершился ошибкой, строка остаётся Ref#<uuid>, а обработка формы продолжается. Примитивные и уже понятные типы в резолвер не уходят.

Я сначала проверил одну удобную гипотезу о расположении UUID. Синтетические тесты были зелёными, а на полном корпусе она разрешила ровно ноль ссылок. Пришлось вернуться к структуре и индексировать все валидные UUID эквивалентных слотов блока, а не выдавать частную выборку за правило формата.

Из 5226 ссылочных реквизитов индекс разрешил 4670 в читаемые имена, а 556 остались неизвестными. Это не дефект, который надо замазывать. Для неразрешённого UUID правильный ответ системы - всё ещё Ref#<uuid>.

Практическое правило: синтетический тест проверяет контракт, но не доказывает устройство каждой реальной раскладки. Неизвестный ссылочный тип лучше сохранить неизвестным.

Где заканчивается реестр и начинается контекст

После разбора связей возник соблазн положить в запись реестра и чтение файлов, и выжимку структуры, и модуль. Я вовремя остановился. FormEntry должен оставаться карточкой указателей: путей, хэшей и служебных признаков. Иначе объект, пригодный для индекса, внезапно начинает зависеть от файловой системы.

FormContext решает другую задачу: материализует форму для одного запроса. Его публичный API (Application Programming Interface - программный интерфейс приложения) небольшой и намеренно скучный:

@dataclass(frozen=True)
class FormContext:
    form_name: str
    container_name: str
    object_type: str
    object_name: str
    bsl_text: str | None
    summary: FormSummary
    metadata: dict


def build_form_context(form_entry, unpacked_root) -> FormContext: ...


def to_llm_prompt_fragment(context, max_chars: int = -1) -> str: ...

FormContext ровно семь полей. Он не запускает второй разбор структуры и не изобретает новый data_path: выжимка строится единственным путём через build_form_summary поверх parse_elem_json.

Внутри metadata ровно шесть полей: form_path, elem_json_path, bsl_sha256, elem_sha256, has_bsl и warnings. Первые два пути относительны. Содержимого модуля, абсолютных каталогов и лишних полей записи реестра там нет.

 

Отсутствие - не пустота

Я отдельно зафиксировал три состояния модуля формы. Это мелочь, пока не начнёшь объяснять форму модели.

 

Состояние

Что попадает в контекст

Файл есть и содержит текст

bsl_text содержит текст, has_bsl равен True

Файла нет

bsl_text is None, has_bsl равен False

Файл существует, но пуст

bsl_text == "", has_bsl равен True

 

Текст читается с явной кодировкой UTF-8 (Unicode Transformation Format, 8-bit - 8-битное представление Unicode). Если структуры нет, контекст получает пустые бакеты выжимки и предупреждения разбора. Если нет каталога формы, парсер не запускается вовсе. Эти детали не делают ответ модели красивее, но не позволяют ей принять отсутствие файла за пустой код.

Практическое правило: реестр хранит адреса, контекст хранит содержимое. И пустой модуль нельзя подменять отсутствующим, как и наоборот.

Полный фрагмент с явным лимитом

Полный FormContext удобен программе, но модели нужен предсказуемый текст. to_llm_prompt_fragment() собирает его всегда в одном порядке:

# FORM <object_type>/<object_name>/<container_name>/<form_name>
## SUMMARY
<детерминированная JSON (JavaScript Object Notation - текстовый формат обмена данными) выжимка>
## BSL
<текст модуля или сообщение об отсутствии>

Сначала идут структура и связи, затем код. По умолчанию max_chars=-1, поэтому функция возвращает полный фрагмент без скрытой обрезки. Положительный лимит применяется последним и по символам: при max_chars > 0 длина результата никогда не превышает заданное значение. max_chars == 0 и значения меньше -1 возвращают пустую строку.

Вот минимальный сценарий с синтетической выгрузкой:

from pathlib import Path

from v8unpack_agent import build_form_context, to_llm_prompt_fragment
from v8unpack_agent.scan_forms import scan_forms

root = Path("sample_export")
index = scan_forms(root)
context = build_form_context(index.forms[0], root)
fragment = to_llm_prompt_fragment(context, max_chars=4000)

assert len(fragment) <= 4000
assert fragment.index("## SUMMARY") < fragment.index("## BSL")

 

Сначала я поставил лимит 8000 значением по умолчанию. Решение выглядело практично, но у него оказался неприятный побочный эффект: вызывающий код мог потерять хвост модуля, хотя явно обрезку не просил. Теперь безопасное умолчание - полный фрагмент, а ограничение включается положительным max_chars в том месте, где уже известен бюджет запроса.

Символьный лимит остаётся сознательным компромиссом. Он прозрачен и не привязан к конкретной модели, но не обещает точный бюджет токенов. При очень малом лимите выжимка может оборваться в середине JSON, поэтому этот текст предназначен для модели, а не для машинного разбора.

Практическое правило: соберите полный фрагмент, проверьте порядок разделов и только затем режьте. Лимит на одном контексте не заменяет распределение бюджета между несколькими формами.

Диагностика не должна выдавать локальное окружение

Одна из самых неприятных поломок пришла не через поле metadata, а через warning парсера. Локально там был абсолютный путь к каталогу формы. Для отладки удобно, для внешнего контекста - нет.

Сначала я очистил путь на границе FormContext. Но те же предупреждения попадают в FormSummary и могут использоваться без FormContext, поэтому одной защиты на выходе оказалось мало. Очистка появилась у источника сообщений и осталась вторым барьером при сборке контекста.

Например, синтетическое предупреждение до очистки могло выглядеть так:

Метаданные владельца не найдены для
C:\Users\demo\dump\Catalog\Объект\CatalogForm\ФормаЭлемента

После очистки смысл сохраняется, а сведения о локальной машине исчезают:

Метаданные владельца не найдены для
.../Catalog/Объект/CatalogForm/ФормаЭлемента

 

Корень выгрузки, имя пользователя, буква диска и сетевой хост удаляются, но полезный хвост пути остаётся.

На контрольном прогоне число warning-текстов с абсолютными путями снизилось с 42 до 0. Проверка покрывает Unix-подобные и Windows-подобные пути, путь с буквой диска и сетевой путь. Не стоит ослаблять такой тест из-за одной необычной строки исключения: OSError вполне способен вернуть имя файла в собственной форме записи.

Эту же границу я проверил в четырёх вариантах CI (Continuous Integration - автоматическая сборка и проверка): Python 3.10 и 3.12 на Ubuntu и Windows. Отдельно тестируется ленивый корневой экспорт: обычный import v8unpack_agent не загружает модуль form_context. Он импортируется только при первом обращении к FormContext, build_form_context() или to_llm_prompt_fragment().

Практическое правило: диагностический текст - часть внешнего контракта. Очищайте его у источника и повторно проверяйте перед передачей за пределы локального окружения.

Границы слоя, которые я не стал прятать

FormContext собирает одну форму, но не ищет формы по большому корпусу и не решает, какой из них отдать модели. Он также не классифицирует форму и не токенизирует общий запрос. Попытка закрыть всё одним объектом быстро превратила бы прозрачный контракт в смесь несвязанных обязанностей.

Ограничения у решения тоже вполне земные:

  • max_chars измеряет символы, а не токены конкретной модели.

  • При жёстком лимите фрагмент может заканчиваться внутри JSON.

  • Без резолвера или при отсутствии записи в индексе ссылочный тип остаётся Ref#<uuid>.

  • Ноль привязок не всегда означает ошибку: сервисная или диалоговая форма может не иметь связанных с данными элементов.

  • Равенство агрегатных счётчиков не доказывает равенство множеств. Сравнивать изменения нужно по формам и обезличенным ключам.

Практическое правило: честная деградация - это не исключение и не догадка. Это частичный контекст с понятным признаком того, чего в нём нет.

Что бы я сделал иначе

  • Раньше проверил бы гипотезу о слоте ссылочного UUID на полном корпусе. Синтетические тесты подтвердили слишком узкую модель, и она дала нулевой полезный результат. Теперь индексируются все валидные эквивалентные слоты.

  • Сразу разделил бы FormEntry и FormContext. Поначалу было удобно дать записи реестра самой читать файлы, но это смешало бы адреса, содержимое и кэширование. Сейчас материализация происходит только в build_form_context().

  • Добавил бы проверку абсолютных путей в warnings вместе с первым текстовым выводом. Защита лишь в контексте закрывает один маршрут, а не проблему целиком. Теперь есть очистка у источника и на внешней границе.

  • Проверял бы ленивость каждого нового корневого экспорта в чистом дочернем процессе. Один «удобный» импорт способен незаметно вернуть тяжёлую цепочку зависимостей.

  • Не ставил бы 8000 лимитом по умолчанию. Такой контракт незаметно обрезал данные без явного решения вызывающего кода. Сейчас -1 возвращает полный фрагмент, а положительный лимит задаётся только там, где уже известен бюджет.

 

Чек-лист для применения

  1. Пропустите elem.json, модуль и метаданные как независимые источники и не связывайте их по имени.

  2. Добавляйте data_path только после структурного подтверждения формата.

  3. Для сегментного пути проверьте адрес таблицы определений формы и точное совпадение собранного имени с именем элемента.

  4. Постройте индекс ссылочных типов во время общего обхода и передайте его резолвер в decode_object_attributes().

  5. Оставляйте неразрешённый ссылочный тип в виде Ref#<uuid> и записывайте причину вместо угадывания имени.

  6. Создавайте контекст через build_form_context(form_entry, unpacked_root) и не добавляйте в него второй парсер структуры.

  7. Ограничьте metadata шестью полями контракта и проверьте относительность обоих путей.

  8. Покройте тестами модуль с текстом, отсутствующий модуль и существующий пустой модуль.

  9. Проверьте, что to_llm_prompt_fragment(context) возвращает полный фрагмент в порядке FORM, SUMMARY, BSL, max_chars > 0 не превышается, а 0 и значения меньше -1 дают пустую строку.

  10. Прогоните очистку warnings на Unix-подобном, Windows-подобном и сетевом синтетических путях.

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

  12. Оставьте поиск по нескольким формам и токенизацию общего бюджета отдельному слою.

 

Что дальше

Контекст одной формы теперь можно собрать без подмены неизвестных связей. Следующий шаг - выбрать несколько релевантных форм из большого корпуса и распределить между ними общий бюджет запроса, не теряя признаки неполноты.

 

Ссылки

Вступайте в нашу телеграмм-группу Инфостарт

LLM FormContext elem.json контекст формы метаданные модуль формы data_path v8unpack-agent промпт

Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.

См. также

Инструментарий разработчика Нейросети Платные (руб)

Первые попытки разработки на 1С с использованием больших языковых моделей (LLM) могут разочаровать. LLMки сильно галлюцинируют, потому что не знают устройства конфигураций 1С, не знают нюансов синтаксиса. Но если дать им подсказки с помощью MCP, то результат получается кардинально лучше. Далее в публикации: MCP для поиска по метаданным 1С, справке синтакс-помощника и проверки синтаксиса.

15250 руб.

25.08.2025    66294    134    36    

141

SALE! %

Банковские операции Обмен с интернет-банком Мастера заполнения Нейросети Программист Бухгалтер Пользователь 1С:Предприятие 8 1C:ERP 1С:Бухгалтерия 3.0 1С:ERP Управление предприятием 2 1С:Управление холдингом 1С:ERP. Управление холдингом 1С:Комплексная автоматизация 2.х 1С:Управление нашей фирмой 3.0 1С:Управление торговлей 11 1С:Розница 3.0 Платные (руб)

Корректируйте банковские документы быстро и легко! Создайте правило обработки — и оно автоматически применится при загрузке выписки (отбор по любому реквизиту или регулярному выражению). Решение заполняет расшифровку платежа, комиссию эквайринга, подбирает ведомости на выплату зарплаты, помечает дубли из банка на удаление и многое другое. Доплачивать за алгоритмы не нужно — они включены в решение. Обработка работает при загрузке из файлов клиент-банка и через DirectBank. Новое — искусственный интеллект: модель приводит нестандартные назначения платежа к виду, понятному алгоритмам, а ИИ-ассистент прямо в 1С консультирует по решению и разбирает код правил и алгоритмов. Поддерживаются локальные и облачные OpenAI-совместимые модели — данные могут не покидать ваш контур.

15250 руб.

20.12.2024    17644    85    29    

77

Нейросети Системный администратор Программист Бизнес-аналитик Бухгалтер Пользователь Руководитель проекта 1С 8.3 1С:Документооборот 1С:Бухгалтерия 3.0 1С:Зарплата и Управление Персоналом 3.x Россия Платные (руб)

Подключите Codex к открытой или серверной базе 1С и ставьте задачи обычным языком: получайте данные, находите ошибки и связанные документы, проверяйте права, работайте с вложениями и контролируемо вносите изменения.

12078 руб.

30.07.2026    4320    14    4    

12

Нейросети Бесплатно (free)

Разбираем новые, более дешевые и эффективные паттерны работы внешнего 1С:Эксперта, а именно – прямое использование LLM-ассистентов и создание с их помощью инструментов для аудита производительности и нагрузочного тестирования. Показываем, как модели помогают анализировать таймауты и взаимные блокировки по технологическому журналу, проверять сложные запросы, а также находить неочевидные взаимосвязи между показателями загрузки оборудования. Рассказываем о создании скриптов для построения графиков по данным atopsar и для поиска и визуализации стеков горячих запросов, а также о создании неинвазивной оснастки для реалистичных нагрузочных и сценарных тестов без программирования на 1С. Отдельно разбираем антипаттерны и ограничения такого подхода. Делаем вывод, что LLM остается лишь помощником, не превращает джуна в сеньора, а ответственность за итоговый результат по-прежнему несет 1С:Эксперт.

10.08.2026    2295    jf2000    15    

16

Нейросети Программист Бесплатно (free)

Практический кейс автономной разработки для бизнес-платформы OneBase: ИИ-агент под управлением Claude Code и недорогой модели GLM за 37 минут с нуля создаёт полную конфигурацию с метаданными, формами, отчётами и дашбордами. Процесс проходит полностью без участия человека — агент сам нарезает задачи, генерирует демо-данные и исправляет ошибки до успешного прогона всех проверок.

07.08.2026    4644    Ibrogim    4    

11

Нейросети Программист Бесплатно (free)

Вокруг 1С выросла целая индустрия MCP-серверов: свой сервер в контуре, HTTP-сервисы, расширения, докер. Это отличные инструменты для разработчика. Но большинству людей вокруг 1С нужно другое: быстро подключить ИИ к базе, спросить словами, получить таблицу и отключиться. Без программиста, без изменений конфигурации и без единого открытого порта. Рассказываю, как сделали такой шлюз, почему он работает даже на УПП 1.3 на обычных формах, и разбираем живые кейсы: консультант без программиста, бухгалтер без сопровождения и вайб-кодинг Б24 по живой базе.

04.08.2026    4985    svcoopers    5    

9

Нейросети Программист Бесплатно (free)

SFT-адаптация Qwen/Qwen3.6-27B для разработки на платформе 1C:Enterprise: код на BSL, структура выгрузок BSL+XML, схемы XML и типовые практики конфигураций. Модель ориентирована на ассистента разработчика 1С: навигация по метаданным/XML-выгрузке, пояснение и правка BSL, следование внутренним стандартам кодирования, работа с открытыми кодовыми базами 1С.

03.08.2026    5334    andrew.ab    38    

20

Облачные сервисы, хостинг Сервера Нейросети Программист Бесплатно (free)

В последние годы спор «облако или локалка» стал одним из самых горячих в мире работы с нейросетями. Давайте разберемся.

03.08.2026    2309    dsdred    48    

10
Для отправки сообщения требуется регистрация/авторизация