Конвейер форм 1С отчитался зелёным. Верим?

22.09.26

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

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

 

Во время работы над очередной задачей я опечатался в пути к выгрузке. Удивительно, но конвейер отработал без ошибок. Он вернул пустой индекс, добавил предупреждение и спокойно передал результат дальше:

before: индекс total = 0

warnings: cf_export_root not found or not a directory

 

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

После исправления тот же вход останавливает выполнение:

after: NotADirectoryError:

cf_export_root must be an existing directory

 

Именно с этой разницы начинается доверие к инструменту. Не с количества тестов и не с гладкости ответа модели, а со способности конвейера честно сообщить, что произошло с данными.

 

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

  • Проверяйте вход до начала обхода. Неверный каталог не должен превращаться в пустой результат.

  • Различайте три уровня неполноты: объект не найден, данные не извлечены, связь не доказана.

  • Сохраняйте причину неполноты в машинном статусе, а артефакты делайте версионированными и переносимыми.

 

Работает не значит заслуживает доверия

До эпика инструмент уже умел обходить подготовленную выгрузку, строить индекс форм, читать дерево элементов и собирать компактный контекст. Был 821 тест, десять примеров и рабочие сценарии.

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

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

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

 

Первый уровень: проверяем вход

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

Раньше несуществующий каталог давал индекс с total = 0 и предупреждением. Для вызывающего кода это был обычный результат. Пустая конфигурация, опечатка и недоступный каталог выглядели одинаково.

Теперь контракт жёсткий:

from pathlib import Path



root = Path("/путь/к/выгрузке")

if not root.is_dir():

    raise NotADirectoryError(

        f"cf_export_root must be an existing directory: {root}"

    )

Важно различить три вида пустоты:

  • каталог существует, данные действительно пусты;

  • каталог существует, но часть данных не прочитана;

  • вход вообще не существует или не является каталогом.

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

Есть и второй вопрос к входу: что именно выгружено. Файловая выгрузка конфигурации содержит читаемые XML (Extensible Markup Language - расширяемый язык разметки) файлы и подготовленные деревья элементов. Цельный контейнер конфигурации устроен иначе. Одинаковая команда поиска по этим двум представлениям отвечает на разные вопросы.

 

Второй уровень: проверяем обнаружение

Модульный тест обычно подаёт форму обработчику и проверяет результат. Такой тест отвечает на вопрос «правильно ли обработана найденная форма». Он ничего не говорит о формах, которые до обработчика не дошли. А что, удобно: нет формы - нет проблемы.

Поэтому полнота обнаружения требует отдельного сравнения:

files = list(export_root.rglob("*.elem.json"))

index = scan_forms(export_root)



assert index.total == len(files)

 

На трёх рабочих распакованных выгрузках эта проверка дала:

Выгрузка

Form.bin

*.elem.json

Форм в индексе

Потеря

A

0

2216

2216

0

B

0

3738

3738

0

C

0

76

76

0


 

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

 

Откуда взялись 84,4 процента

Я решил подготовить точку расширения под будущую обработку бинарных форм и начал с исследования трёх конфигураций. В них суммарно нашлось 4782 файла Form.bin: 1354, 6 и 3422. На крупнейшей выборке старый механизм учитывал только 535 из 3422 источников. Остальные 2887, или 84,4 %, терялись ещё до попытки разобрать содержимое: 2822 из-за коллизий имён и 65 из-за другого расположения общих форм.

Исправление ввело устойчивый form_id и перестало схлопывать разные источники в одну запись. Но здесь надо понимать, что обнаружение источника и разбор его содержимого - разные способности.

v8unpack-agent научился корректно обнаруживать и различать бинарные источники. Он не получил встроенный промышленный преобразователь Form.bin в текстовый слой. Этот шаг задан контрактом FormUnpacker:

FormUnpacker = Callable[[FormBinSource, Path], FormArtifact]

Реализацию передаёт вызывающая сторона. На всех 14 проверенных образцах попытка передать распаковщику одиночный Form.bin вместо контейнера целиком завершилась ошибкой. Это оказался неподдерживаемый сценарий входа, а не доказанный дефект upstream-инструмента, поэтому отдельная задача для него не создавалась.

Проверка полноты должна отвечать на вопрос о вашей выгрузке. Чужой процент нельзя переносить между разными представлениями конфигурации.

 

Третий уровень: проверяем, что удалось понять

Форма может быть найдена и при этом остаться понятой лишь частично. У неё может не быть объекта-владельца. Данные объекта-владельца могут отсутствовать или не прочитаться. Ссылочный тип может присутствовать только в виде уникального идентификатора.

Раньше часть этих состояний схлопывалась в одинаковую пустоту. Для модели разницы не было:

{

  "Properties": []

}

Но «у объекта нет реквизитов» и «реквизиты не удалось прочитать» означают противоположные вещи. В первом случае пустой список является фактом. Во втором он скрывает отказ.

На контрольной выгрузке 162 из 2216 форм имеют состояние no_owner_object. Это отдельный штатный класс форм без определённого объекта-владельца, а не ошибка чтения. Другие причины получили отдельные предупреждения вместо общей пустоты.

Недоказанный тип остаётся недоказанным

У ссылочного реквизита в служебных данных может быть только UUID (Universally Unique Identifier - универсальный уникальный идентификатор). Чтобы получить имя, его нужно сопоставить с индексом типов конфигурации или с подтверждённой таблицей платформенных типов.

Если сопоставление не удалось, конвейер сохраняет:

Ref#a1b2c3d4-0000-0000-0000-000000000000

Имя реквизита может подталкивать к очевидной догадке. Но угаданное имя выглядит в контексте так же, как доказанное. Поэтому остаток не достраивается по позиции, виду объекта или смыслу имени.

На полном пути обработки контрольной выгрузки получилось:

Показатель

До второго уровня резолюции

После

Применимых ссылочных вхождений

15 723

15 723

Резолвлено

14 422

15 448

Доля

91,73 %

98,25 %

Остаток

1301

275


 

Из оставшихся 275 вхождений 117 относятся к семи UUID без доказанного имени, ещё 158 - к 28 идентификаторам с известным определением, но недоказанной связью с машинным именем типа. Исследование комбинации «вид объекта плюс позиция» не дало ни одного устойчивого имени на трёх конфигурациях. Поэтому остаток сохранён.

Почему null недостаточно

В комментариях к прошлой статье возник ещё один вопрос: что должна увидеть модель, если связь, вероятно, существует, но путь к ней не установлен. Обычный null этого не объясняет. Он может означать как «значения нет», так и «значение не удалось определить».

Для строгого контракта полезно различать хотя бы два состояния:

data_path: null
status: unresolved
reason: unknown_layout

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

data_path: null
status: unresolved
reason: not_found

Здесь соответствующий объект не найден в доступной выгрузке. Пока это только пример того, как контракт должен выглядеть: таких полей в пакете ещё нет.

Важно не смешивать две неопределённости. data_path относится к резолюции пути данных, а Ref#uuid - к имени ссылочного типа. Задача #289 касается второй. Для первой отдельный отрицательный статус рядом с полем также остаётся открытой границей.

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

 

Ответ читателю: что стало с 556 ссылками

Также в комментариях к предыдущей статье читатель спросил, что скрывается за 556 неразрешёнными ссылками и видно ли самой модели, что связь не доказана. Я обещал вернуться с результатами.

Методика изменилась, поэтому напрямую сравнивать 556 и 275 нельзя даже в процентах: отличаются область обхода, знаменатель и единица подсчёта.

Измерение

Что считалось

Всего ссылок

Остаток

Раннее

прямой вызов декодера

5226

556

Текущее

полный путь от индекса до контекста

15 723

275


 

Чтобы ответить именно про прежние 556, старую ссылочную выборку повторно прогнали текущим резолвером. Из них 397 получили подтверждённое платформенное имя, 159 остались Ref#uuid. Обещанное исследование десяти самых частых UUID выполнено. В повторном замере всей старой выборки имена получили 13 из 49 уникальных идентификаторов, а 36 остались без доказанного имени. Всего на той же ссылочной выборке разрешено 5067 из 5226.

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

Вторая часть обещания пока не закрыта. В итоговом LLM-фрагменте нет отдельного поля type_resolved: false. Единственный сигнал для модели - сама строка Ref#uuid. Существующий флаг resolved относится к разрешению пути данных, а не ссылочного типа.

На оставшиеся части заведены задачи #288 и #289. Продолжение следует, но срок на этот раз обещать не буду.

 

Четвёртый уровень: проверяем статус прогона

Человек прочитает предупреждение. Регламентный скрипт обычно смотрит только на код возврата.

У запуска появились обязательный post-run report и явные классы завершения:

Код

Результат

0

обработка завершена полностью

2

ошибка аргументов или корня выгрузки

3

есть частично обработанные или ошибочные объекты

4

управляемая фатальная ошибка конвейера

5

не удалось записать отчёт

6

фатальная ошибка вмест

Ключевой код здесь 3. Частичный результат больше не выглядит успехом для вызывающей автоматизации.

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

before:

skipped (no .obj.bsl): Catalog/Demo/CatalogForm/ФормаСписка



after:

skipped (no .obj.bsl): Catalog/Demo/CatalogForm/ФормаСписка

[code=FORM_MODULE_MISSING]

 

Граница текущего отчёта тоже важна. Он показывает полноту извлечения: какие объекты обработаны полностью, частично или с ошибкой. Он пока не показывает полноту разрешения ссылочных типов. Объект с

Если частичный результат возвращает код 0 и не имеет машинной причины, для автоматизации он неотличим от полного.

 

Пятый уровень: проверяем переносимость

До эпика индекс содержал абсолютные пути. На трёх рабочих выгрузках число вхождений корня было таким:

Выгрузка

До

После

A

6648

0

B

11 214

0

C

228

0


 

Абсолютный путь делает артефакт зависимым от машины и раскрывает структуру рабочего каталога. Теперь пути относительные и записываются в едином формате.

Но эта проверка доказывает только переносимость штатного сериализованного индекса. Она не доказывает, что абсолютный путь никогда не попадёт в текст исключения, traceback или служебное представление объекта. Для такого утверждения нужен отдельный тест: намеренно выбросить исключение с синтетическим абсолютным путём и проверить весь аварийный канал перед выдачей результата.

Поэтому корректная граница звучит так: абсолютные пути удалены из проверенных штатных артефактов. Универсальная санитизация исключений и traceback этим замером не подтверждена.

Одновременно у схемы появилась версия:

{

  "schema_version": 2,

  "total": 2216,

  "forms": []

}

Без версии старый файл можно принять за актуальный и получить тихо неверный результат. С версией несовместимость становится явным состоянием.

Вывод примеров стал детерминированным. Два запуска на неизменных данных дают побайтово одинаковый результат, поэтому изменения можно проверять обычным diff.

На тех же трёх выгрузках проверился второй уровень разрешения платформенного UUID:

before: None

after:  'cfg:CatalogRef'

Это уже не исследовательская метрика на чужом корпусе, а воспроизведённое изменение на трёх собственных выгрузках. Артефакту можно доверять в автоматизации, только если его можно перенести, сравнить и однозначно прочитать другой версией инструмента.

 

Чем подтверждается результат

Я прогнал одну и ту же проверку на последнем коммите до эпика и на итоговом коммите после него:

Проверка

До

После

Pytest

821

1194

Ruff

211 ошибок

0

Mypy

15 ошибок в 5 из 30 файлов

0 в 41 файле

Побочные модули при импорте

4

2

Примеры

10

15

Версия индекса

нет

2

Runtime-зависимости

pytest и v8unpack из git

v8unpack>=1.2.13


 

Итоговый коммит прошёл шесть обязательных проверок CI (Continuous Integration - автоматическая сборка и проверка) на Ubuntu и Windows с Python 3.10 и 3.12. Пакет опубликован в PyPI как версия 0.1.0.

Эти числа не доказывают полноту конкретной конфигурации. Они доказывают воспроизводимость контракта инструмента. Полнота входа всё равно проверяется отдельно на каждой выгрузке.

Да, чуть не забыл: это ещё и первый релиз пакета. Теперь его можно попробовать обычной установкой из PyPI. Отдельное спасибо авторам upstream-пакета v8unpack за свежий релиз, без которого этот выпуск пришлось бы отложить. Это тот релиз, который в своём проекте иногда ждёшь сильнее, чем GPT-6 и GTA VI.

Тесты подтверждают заявленный контракт, но не заменяют измерение данных, которых не было в тестовой выборке.

 

Что эпик не сделал

После длинного списка проверок легко написать слишком сильный вывод. Поэтому перечислю границы отдельно.

  • Пакет не получил встроенный промышленный распаковщик Form.bin. Он обнаруживает источник и передаёт его внешнему FormUnpacker.

  • Исправленная идентичность источников не означает, что их бинарное содержимое успешно разобрано.

  • Результат на одной конфигурации не гарантирует такое же покрытие на другой.

  • Оставшиеся ссылочные типы не получили выдуманных имён.

  • Post-run report пока не показывает долю разрешённых типов. Это задача #288.

  • LLM-фрагмент пока не имеет отдельного признака недоказанного типа. Это задача #289.

  • Относительность путей подтверждена для штатных артефактов, но универсальная санитизация исключений и traceback отдельно не доказана.

  • Полный технический прогон не доказывает правильность бизнес-вывода модели.

Иначе говоря, эпик не сделал агента умнее. Он сделал происхождение и границы переданных агенту данных заметнее.

 

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

  1. Сначала описал бы состояния complete, partial и failed. Я добавлял обработку раньше, чем договорился о смысле результата, поэтому тихие отказы пришлось разбирать задним числом. Сейчас статус является частью контракта.

  2. Не менял бы методику метрик без воспроизводимого моста к старому замеру. Из-за разных знаменателей 556 и 275 легко принять за прямое улучшение. Теперь сравнение повторяется на одной ссылочной выборке.

  3. Проверял бы несколько видов выгрузки до общего вывода. Отсутствие Form.bin в распакованном дереве я однажды принял за отсутствие бинарных форм в исходной конфигурации. Сейчас сначала фиксируется тип входа.
     

Чек-лист

  1. Определите, что перед вами: файловая выгрузка, подготовленное дерево или цельный контейнер.

  2. Перед обходом проверьте, что корень существует и является каталогом.

  3. Посчитайте входные файлы форм по каждому поддерживаемому виду.

  4. Сравните число файлов с числом записей в индексе.

  5. Запустите конвейер на несуществующем пути и убедитесь, что он падает явно.

  6. Разделите в модели данных «пусто», «не применимо» и «не удалось прочитать».

  7. Посчитайте остаток Ref#uuid командой python examples/unresolved_refs_report.py <корень>.

  8. Убедитесь, что неизвестный тип не достраивается по имени реквизита или позиции.

  9. Проверьте ненулевой код завершения частичного прогона.

  10. Проверьте наличие стабильных машинных кодов у предупреждений.

  11. Найдите абсолютный корень выгрузки в сохранённом индексе. Вхождений должно быть ноль.

  12. Намеренно выбросьте исключение с синтетическим абсолютным путём и проверьте отчёт, текст ошибки и traceback.

  13. Проверьте версию схемы и поведение на старом артефакте.

  14. Запустите обработку дважды и сравните результаты через diff.
     

Что дальше

На ближайшие технические доработки уже заведены задачи #288 и #289. Следующий большой шаг - обогащение уже подготовленного контекста через RAG (Retrieval-Augmented Generation - метод дополнения LLM контекстом из индекса). Хочу связать поиск релевантных форм и сбор данных для LLM в единый воспроизводимый путь, не пряча границы распаковки и разрешения связей.
 

Ссылки

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

LLM агент качество данных полнота диагностика детерминизм v8unpack-agent

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

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

См. также

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-совместимые модели — данные могут не покидать ваш контур.

17500 руб.

20.12.2024    19436    99    32    

83

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

Задавайте вопросы базе 1С обычными словами: получайте данные, находите ошибки и связанные документы, проверяйте права, работайте с вложениями и контролируемо вносите изменения. Всё это работает в самой программе, а Codex и Claude подключаются по желанию.

15989 руб.

30.07.2026    12726    27    4    

27

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

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

15250 руб.

25.08.2025    70888    141    41    

149

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

ИИ-сканер документов с REST API для интеграции с 1С и корпоративными системами. Извлекайте данные из счетов, паспортов, дипломов, патентов и трудовых книжек за секунды. Точность человека - скорость машины. Приложение поддерживает восемь типов документов, четырех провайдеров ИИ (имеется возможность использования локальных ИИ), локальный REST API и экспорт в JSON. Важно! модель должна поддерживать функцию Vision (распознавание файлов и картинок). Запускайте используя локальные ИИ, без подписок и ограничений

6100 руб.

24.08.2026    496    2    0    

1

Нейросети 1С:Управление торговлей 11 Бесплатно (free)

Я не считаю покупку специализированных платных инструментов обязательной для разработки с ИИ: нужную обвязку тоже можно создать с агентом. Показываю этот подход на расширении УТ 11 с динамическим списком остатков. Одно задание Codex, 37 минут до проверки, работающая форма. Рассказываю, как устроено окружение, почему первую попытку пришлось переснять и что получилось в повторном прогоне.

17.09.2026    5208    71    Ibrogim    51    

18

Нейросети Разработчик Руководитель проекта 1C:ERP Бесплатно (free)

Служба на Rust, через которую Claude Code, Cursor или другой MCP-клиент вызывает узких ИИ-агентов. Агент — папка с prompt.md и config.toml, модель — строка в конфиге: DeepSeek, Claude Code по подписке, Codex или локальная модель. Агент получает MCP-инструменты, работает в фоне, каждый ход записывается. Внутри — цифры за три месяца: 10 351 вызов, 78 агентов.

17.09.2026    1579    0    Sorm    7    

10

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

Отладка кода 1С традиционно выглядит примерно одинаково: поставить точку останова, запустить клиент, воспроизвести сценарий, дождаться остановки, посмотреть локальные переменные, пройти несколько строк, раскрыть очередную структуру или коллекцию, вычислить выражение — и повторить все это еще несколько раз. А что, если значительную часть этой рутины поручить AI-агенту?

15.09.2026    3471    andrew.ab    5    

15

Нейросети Разработчик Аналитик Руководитель проекта Бесплатно (free)

Я принёс команде приём, с которым нейронка наконец начала понимать нашу конфигурацию: у меня он работал, у коллег — нет. Дело было не в постановке задач и не в настройках: причина в том, что на их машинах индекс конфигурации считался бы несколько дней. Замер на одном и том же своде из 26 035 записей: три часа на процессоре против трёх с половиной минут на видеокарте. Разбираю, что такое индексация конфигурации и почему она дорогая ровно один раз, почему наша основная серверная машина — 64 ядра, 768 гигабайт памяти — на этой задаче проигрывает домашнему компьютеру, и почему приём одного человека упирается в вопрос, который никто не любит задавать.

09.09.2026    4652    solbol    9    

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