Заказчиков назвать не можем — договоры это запрещают. Зато можем показать, что у них изменилось в работе после сдачи и каким документом это проверяется. Ниже — весь список с фильтром по направлению, отрасли и технологии, а под ним восемь разборов: как такие задачи устроены внутри.
Назвать заказчиков не можем, договоры это запрещают. Зато можем показать, что у них изменилось в работе после сдачи и каким документом это проверяется. Крупным в карточке — то, что теперь происходит само, без человека.
ОтрасльТехнология
Найдено 12 из 312
МашиностроениеОбследование и дорожная карта
Проверяется: карта процессов и дорожная карта этапов
Обследование процессов перед внедрением ИИ
Задача, решение и что передали
Задача
Руководство требовало предложений по ИИ, а внутри спорили, с чего начинать: задачи были описаны словами «ускорить» и «разгрузить людей».
Решение
Разобрали процессы до шагов и данных и по каждому проверили, есть ли история, на которой можно измерить качество ответа. Часть задач после разбора закрылась обычным обменом данными, без модели.
Сдано 3
Карта процессов и данных
Перечень задач с оценкой применимости
Дорожная карта по этапам
Как делали
Обследование процессов Прошли по цепочке от конструкторской подготовки до отгрузки: говорили с технологами, диспетчерами цеха, снабжением и ОТК, разбирали выгрузки из учётных систем. Каждую формулировку «ускорить» и «разгрузить людей» разложили до шага процесса, исполнителя и документа — маршрутной карты, спецификации, заявки.
Архитектура разбора Решили описывать каждую задачу одинаковым паспортом: вход, шаг процесса, источник истории и признак правильного ответа. Задачи без сохранённой истории вынесли отдельно, а часть перевели в обычный обмен данными между системами — модель там была не нужна.
Разработка карты Собрали карту процессов и данных, заполнили паспорта задач и по каждой проверили, есть ли история, на которой можно измерить качество ответа. Черновик перечня и дорожной карты прошли с владельцами процессов до того, как показывать руководству.
Проверка выводов Повторили выгрузки и вернулись к исполнителям с готовой картой, чтобы сверить описанные шаги с тем, как работа идёт на самом деле. Провалом считали задачу, оставшуюся в перечне без выгрузки, на которой измеряется правильность ответа: применимость, обоснованную словами, в дорожную карту не пускали.
Передача карты Передали карту процессов и данных, перечень задач с оценкой применимости и дорожную карту по этапам, разобрав их с руководством и владельцами процессов. Показали, как читать паспорт задачи, добавлять новые и переносить их между очередями.
Архитектура
Стенд обследования собран отдельно от боевых систем: выгрузки из учётных баз снимаются по ODBC в staging-схему PostgreSQL, откуда модели dbt раскладывают их в таблицы шагов процесса, участников и документов. Профилирование гоняется в DuckDB по файлам выгрузок — считаются доли пустых полей и распределения по всей истории, а колоночный движок читает только нужный столбец, тогда как строчная таблица поднимала бы запись целиком на каждый агрегат. Проверки полноты и связности оформлены наборами Great Expectations и прогоняются на каждой новой выгрузке, результаты выводятся в Metabase рядом с картой шагов. Стенд поднимается Docker Compose на отдельной машине: при её отказе теряется только копия выгрузки, повторный съём делается тем же скриптом с тем же срезом дат.
Проверяется: очередь ручной проверки и журнал операций
Агент, заносящий первичные документы в учётную систему
Задача, решение и что передали
Задача
Накладные, счета и акты приходили сканами, сотрудники переносили реквизиты в 1С руками, а опечатки находились уже в отчётности.
Решение
Развернули распознавание внутри контура. Агент сверяет контрагента по ИНН и номенклатуру по таблице соответствий, проверяет арифметику и заводит документ черновиком, спорные поля отдаёт оператору.
Сдано 4
Исходный код агента
Правила разбора под формы заказчика
Очередь ручной проверки
Журнал операций
Как делали
Разбор первички Собрали архив сканов накладных, счетов и актов и разложили по формам поставщиков, отметив, где реквизиты расположены нестандартно. С бухгалтерией разобрали, на каких полях возникали опечатки, доходившие до отчётности, и выгрузили справочники контрагентов и номенклатуры.
Архитектура агента Распознавание разместили внутри контура, наружу сканы не уходят; агент отделён от учётной системы и общается с ней через HTTP-сервис, документы ждут разбора в очереди. Правила сверки контрагента по ИНН и номенклатуры по таблице соответствий вынесли из кода, а от автопроведения отказались — документ создаётся только черновиком.
Сборка по слоям Сначала подняли разбор форм и заполнение реквизитов, затем проверку арифметики по строкам и итогу, затем очередь ручной проверки и журнал операций. Всё это гоняли на стенде с копией базы 1С, прежде чем подключать боевую.
Проверка на архиве Прогнали архив сканов и сверили заполненные поля с документами, заведёнными бухгалтерами вручную. Провалом считали не только неверный реквизит, но и уверенно разобранное спорное поле, не ушедшее оператору; черновик с несходящейся суммой строк и итога — причина не принять работу.
Передача бухгалтерии Передали исходный код агента, правила разбора под формы заказчика, очередь ручной проверки и журнал операций. Бухгалтеров научили работать с очередью, а администратора — добавлять форму нового поставщика и пополнять таблицу соответствий номенклатуры.
Архитектура
Приёмник на FastAPI кладёт скан в MinIO и ставит задачу в RabbitMQ, распознавание и разбор идут отдельными воркерами: OCR занимает узел надолго, и на прямом вызове он держал бы HTTP-соединение, а очередь ещё и переживает перезапуск воркера. Разобранные реквизиты попадают в PostgreSQL черновиком со статусом, сверка контрагента по ИНН и номенклатуры по таблице соответствий выполняется там же запросами к справочникам, арифметика документа пересчитывается перед записью. В 1С документ уходит через HTTP-сервис с идемпотентным ключом из хеша файла и номера документа — повтор после таймаута не создаёт второй черновик. Поля ниже порога уверенности в 1С не отправляются, а встают в очередь оператора со ссылкой на исходный скан; каждое действие агента пишется в журнал операций, узлы подняты контейнерами.
Проверяется: расчёт железа и регламент эксплуатации
Языковая модель в изолированном контуре
Задача, решение и что передали
Задача
Сотрудники искали ответы по внутренним регламентам и письмам регулятора вручную, а выносить эти документы во внешний ИИ-сервис было нельзя.
Решение
Посчитали конфигурацию по памяти видеокарт и числу одновременных запросов, развернули инференс и поиск внутри контура. Права применяются на этапе отбора фрагментов, до попадания текста в контекст модели.
Сдано 4
Развёрнутая система
Расчёт железа
Регламент эксплуатации и переноса моделей
Обученные администраторы
Как делали
Обследование контура Определили состав базы — внутренние регламенты, распоряжения, письма регулятора — и с подразделением безопасности зафиксировали, что за пределы контура не уходит ничего. Собрали профиль обращений: какие подразделения спрашивают, какой длины документы и сколько запросов ожидается одновременно.
Расчёт конфигурации Конфигурацию считали от памяти видеокарт и числа одновременных запросов — отсюда размер модели, длина контекста и число параллельных сессий. Поиск и инференс развели по разным службам, а права применили на этапе отбора фрагментов, до того как текст попадёт в контекст модели, а не фильтрацией готового ответа.
Развёртывание в контуре Подняли инференс на GPU-сервере заказчика, затем индекс с гибридным поиском по регламентам и письмам, затем слой прав и веб-интерфейс. На стенде смотрели поведение при одновременных обращениях и при выгрузке моделей из памяти.
Проверка доступа Прогнали вопросы, составленные методологами, и отдельно проверку прав: под учётной записью без доступа к документу его фрагмент не должен появиться ни в ответе, ни в ссылках. Единственное такое попадание считалось провалом и возвращало систему на доработку; ответ без ссылки на пункт регламента тоже не приняли бы.
Обучение администраторов Передали развёрнутую систему, расчёт железа и регламент эксплуатации и переноса моделей. Администраторы научились заводить новую модель, пересобирать индекс и разбирать журнал обращений сами.
Архитектура
Контур собран из трёх служб: шлюз на FastAPI, поисковый узел и инференс на vLLM, поднятый на GPU-сервере заказчика. Права применяет поисковый узел — фильтр по группам пользователя из LDAP уходит прямо в запрос к OpenSearch и pgvector, поэтому закрытый фрагмент не доходит до сборки контекста, а не вырезается из готового ответа. Запросы к картам выстроены в Redis Streams с ограничением числа одновременных генераций: прямая раздача на карту при всплеске упирается в память под KV-кеш, а очередь удерживает лишние запросы вместо отказа. Конфигурация памяти под модель и предельная длина контекста зафиксированы в compose-файле; при перезапуске узла очередь сохраняется, незавершённые запросы берутся заново, метаданные документов и журнал обращений лежат в PostgreSQL.
Проверяется: журнал операций и схема соответствия полей
Обмен 1С с сайтом и торговыми площадками
Задача, решение и что передали
Задача
Остатки, цены и заказы жили в 1С, на сайте и на площадках одновременно и расходились, а спорные строки разбирали по выгрузке в Excel.
Решение
Зафиксировали, какая система владеет каждым полем. Собрали передачу через очередь с повтором при отказе и ключом идемпотентности на заказ, добавили сверочную витрину и оповещение при неприменившейся выгрузке.
Сдано 4
Исходники обмена
Схема соответствия полей
Журнал операций
Инструкция администратору
Как делали
Сверка систем Сопоставили карточку товара в 1С, на сайте и в кабинетах площадок: где живёт цена, где остаток, где статус заказа. Подняли те самые Excel-выгрузки, по которым разбирали спорные строки, и выделили расхождения, которые повторяются из раза в раз.
Владение полями Зафиксировали, какая система владеет каждым полем: цена и остаток идут из 1С, статус заказа приходит с площадки. Прямые вызовы между системами заменили очередью с повтором при отказе и ключом идемпотентности на заказ, от ночной полной выгрузки отказались в пользу событий с периодической сверкой.
Сборка обмена Собрали адаптеры к 1С и к API площадок, затем очередь и обработчики, затем сверочную витрину и оповещение о неприменившейся выгрузке. На стенде гоняли обмен на копии номенклатуры, намеренно обрывая ответы площадки.
Проверка повторов Повторяли одну и ту же выгрузку и один и тот же заказ, проверяя, что дубля не возникает, и сверяли остатки после схождения очереди. Провалом считали расхождение остатка между 1С и площадкой при пустой очереди и задвоенный заказ при повторной отправке.
Передача администратору Передали исходники обмена, схему соответствия полей, журнал операций и инструкцию администратору. Показали, как подключить новую площадку, как читать сверочную витрину и что делать по оповещению о неприменившейся выгрузке.
Архитектура
Обмен собран как набор адаптеров на Go за общей шиной: коннектор 1С ходит по OData, коннекторы сайта и площадок — по их HTTP-контрактам, между ними стоит NATS JetStream. Прямой вызов из 1С в площадку заменён публикацией события, потому что площадка отвечает не всегда и её недоступность иначе останавливала бы проведение документа — сообщение лежит в потоке и повторяется с возрастающей паузой. Владение полем зафиксировано таблицей в PostgreSQL: остатки и цены пишет только 1С, статус заказа — только площадка, встречная запись отбрасывается на входе адаптера. Заказ проводится по идемпотентному ключу «площадка плюс внешний номер», уникальный индекс не даёт повтору создать дубль; справочники кешируются в Redis, сверочная витрина считает расхождения и шлёт оповещение о неприменившейся выгрузке, метрики очередей уходят в Prometheus и Grafana.
Стек
Go · chi · PostgreSQL · Redis · NATS JetStream · Docker Compose · 1С OData · Prometheus · Grafana
Проверяется: словари обозначений и правила разбора
Поиск по конструкторской и технологической документации
Задача, решение и что передали
Задача
Чертежи, паспорта оборудования и регламенты лежали в папках, архиве СЭД и в шкафах, а какая редакция действующая — знали два человека.
Решение
Собрали конвейер: распознавание сканов, извлечение основной надписи и спецификации в поля, нормализация обозначений, привязка извещений об изменениях. Поиск точный по децимальному номеру и смысловой по тексту.
Сдано 4
Поисковый сервис в контуре
Правила разбора и словари обозначений
Конвейер дозагрузки
Исходники
Как делали
Обход хранилищ Прошли по местам хранения — сетевые папки, архив СЭД, бумажные шкафы — и описали, что где лежит. С архивариусом и конструкторами разобрали, как обозначается редакция, как оформляются извещения об изменениях и по каким признакам документ ищут на практике.
Архитектура конвейера Конвейер разделили на распознавание, извлечение полей и индекс: основную надпись и спецификацию решили класть в поля, а не в общий текст, чтобы поиск по децимальному номеру был точным. Смысловой поиск по тексту вынесли отдельно, а извещения привязали к документу так, чтобы они меняли признак действующей редакции.
Сборка поиска Сначала подняли распознавание сканов и извлечение основной надписи, затем нормализацию обозначений по словарям, затем привязку извещений и объединение точного и смыслового поиска. Конвейер дозагрузки собирали и обкатывали на оборудовании заказчика.
Проверка выдачи Искали документы по списку, собранному конструкторами, — и по децимальному номеру, и по описанию узла. Провалом считали выдачу отменённой редакции выше действующей и ненайденный документ при точном обозначении, даже если он находился по тексту.
Передача архиву Передали поисковый сервис в контуре, правила разбора, словари обозначений, конвейер дозагрузки и исходники. Архив научили сам класть новые сканы и извещения в конвейер и проверять, что редакция сменилась.
Архитектура
Конвейер разбит на узлы: приёмник новых файлов, распознаватель на OpenCV и Tesseract, извлекатель основной надписи и спецификации, индексатор. Узлы связаны RabbitMQ, а синхронные обращения идут по gRPC; распознавание вынесено отдельным сервисом с собственным пулом процессов, потому что скан большого формата занимает узел на минуты и в общем потоке блокировал бы дозагрузку остальных документов. Поля основной надписи, децимальные номера и связи с извещениями об изменениях лежат в PostgreSQL, векторы описаний — в pgvector рядом, чтобы точный поиск по обозначению и смысловой по тексту собирались одним запросом без второго хранилища и без рассинхронизации индексов. Модели подняты через ONNX Runtime на оборудовании заказчика; при падении узла сообщение возвращается в очередь и документ переиндексируется целиком, частичный разбор в индекс не попадает.
Проверяется: инструкция по подключению оборудования
Съём данных со станков и сменный отчёт
Задача, решение и что передали
Задача
Выработку и простои считали в конце смены по бумажным журналам, причины восстанавливали по памяти, сводку собирали руками.
Решение
Сняли сигналы через OPC UA и свели в модель смен, заказов и причин простоя. Мастер фиксирует причину на месте, сменный отчёт собирается сам, справочники и права разделены по цехам.
Сдано 4
Развёрнутая система
Схема данных
Исходный код
Инструкция по подключению оборудования
Как делали
Обход парка Обошли станки и записали, что каждый отдаёт по OPC UA, а что не отдаёт вовсе и требует ручного ввода. С мастерами и нормировщиками разобрали бумажные журналы смен и выписали список причин простоя, каким он существовал на словах.
Модель смены Сигналы решили не хранить как есть, а сводить в модель смен, заказов и причин простоя; справочники и права развели по цехам. Ручной ввод оставили только там, где сигнала нет: причину фиксирует мастер на месте, а не восстанавливает по памяти в конце смены.
Сборка съёма Подняли сбор по OPC UA и запись в базу, затем модель смен и заказов, затем интерфейс мастера у линии и автоматическую сборку сменного отчёта. На стенде подключали станки по одному, начиная с тех, что отдают состояние без доработок.
Сверка со сменой Сменный отчёт сверяли с бумажным журналом и показаниями станка по одним и тем же сменам. Провалом считали простой, потерянный при обрыве связи с оборудованием, и смену, где сумма работы и простоев не сходится с её длительностью.
Передача цехам Передали развёрнутую систему, схему данных, исходный код и инструкцию по подключению оборудования. Мастера ведут справочник причин, а служба АСУ подключает новые станки без нашего участия.
Архитектура
На каждом участке стоит сборщик на Rust: он держит сессии OPC UA со станками, старые стойки опрашивает по Modbus TCP, приводит теги к общей модели сигнала и публикует их в NATS. Между сборщиком и хранилищем поставлена очередь, потому что обрыв связи до серверной не должен ронять опрос стойки — сборщик копит отсчёты локально и досылает после восстановления. Сырые отсчёты пишутся в гипертаблицу TimescaleDB, а смены, заказы и причины простоя — в обычные таблицы PostgreSQL рядом: временной ряд режется по времени и сворачивается окнами, справочная часть меняется редко и живёт на внешних ключах. Панель мастера на Axum показывает простои за смену и принимает причину на месте, сменный отчёт собирается по расписанию из уже уложенных агрегатов, справочники и права разделены по цехам, состояние линий выведено в Grafana.
Проверяется: набор проверок и регламент повторной проверки
Проверка ИИ-ассистента перед допуском в рабочую сеть
Задача, решение и что передали
Задача
Служба безопасности не пропускала готового ассистента в промышленную сеть: критериев приёмки не было, спор шёл о впечатлениях.
Решение
Составили набор проверок на данных заказчика: подмена инструкций, выдача фрагментов вне прав доступа, поведение на вопросах без ответа в базе. Замерили и зафиксировали пороги допуска.
Сдано 3
Набор проверок и протокол замеров
Пороги допуска
Регламент повторной проверки
Как делали
Разбор возражений С безопасностью и владельцем ассистента разобрали, что именно мешает допуску: какие документы он видит, к каким системам просится и на каких ответах шёл спор. Собрали на данных банка те вопросы, вокруг которых обсуждение держалось на впечатлениях.
Критерии допуска Договорились, что допуск описывается набором проверок с порогами, а не мнением: подмена инструкций, выдача фрагментов вне прав доступа, поведение на вопросах, ответа на которые в базе нет. Каждая проверка сделана воспроизводимой — сохраняются вход, параметры генерации и ответ.
Сборка проверок Собрали наборы сценариев на данных заказчика, подняли прогон с журналированием и оформили протокол замеров. Сначала гоняли на копии базы, затем на боевом индексе под учётными записями разных ролей.
Прогон и пороги Прогнали набор целиком и зафиксировали пороги допуска в протоколе. Провалом считали любую выдачу фрагмента, к которому у учётной записи нет прав, и ответ на вопрос без основания в базе, поданный как факт вместо отказа.
Передача безопасности Передали набор проверок, протокол замеров, пороги допуска и регламент повторной проверки. Служба безопасности проводит прогон сама при смене модели, шаблона промпта или состава базы.
Архитектура
Стенд стоит отдельным контуром: раннер на pytest обращается к ассистенту по его же HTTP-контракту, наборы проверок лежат файлами и версионируются вместе с кодом. Прогон разложен на задачи Celery, потому что каждый сценарий — это ожидание генерации, и последовательный запуск растягивал бы приёмку; вердикты сходятся в PostgreSQL, полные входы и ответы кладутся в MinIO, чтобы срабатывание воспроизводилось дословно, а не по одной строке отчёта. Проверки прав идут от тестовых учётных записей в Keycloak с разными группами: один и тот же вопрос задаётся от пользователя с доступом и без, сравниваются наборы выданных фрагментов. Прогон вызывается из GitLab CI на обновление модели или шаблона промпта, пороги допуска вынесены в конфигурацию, отчёт собирается Allure, стенд поднимается Docker Compose из того же описания, что и проверяемый контур.
Проверяется: реестр зависимостей и протокол сверки
Перенос учётной системы с MS SQL на PostgreSQL
Задача, решение и что передали
Задача
Дата перехода была задана нормативно, а перечня того, что держится на MS SQL, внутри не было: процедуры, планировщик, отчётность и приложения без исходников.
Решение
Сняли фактические запросы трассировкой, переписали несовместимый код, перевели задания и отчётность. Сверили строки и контрольные суммы, отдельно проверили порядок сортировки, отрепетировали откат.
Сдано 3
Реестр зависимостей
Перенесённая база с протоколом сверки
План перехода и процедура возврата
Как делали
Трассировка запросов Перечня зависимостей внутри не было, поэтому фактические обращения к MS SQL сняли трассировкой на работающей системе — так нашли и приложения, у которых нет исходников. Отдельно выписали хранимые процедуры, задания планировщика и отчётные формы.
Стенд-двойник Решили переносить по собранному реестру зависимостей, а не по документации, и подняли стенд-двойник, чтобы репетировать переход и возврат столько раз, сколько потребуется. Порядок сортировки вынесли в отдельный вопрос: правила сравнения строк задали явно, не полагаясь на настройки по умолчанию.
Перенос кода Перенесли схему и данные, переписали несовместимый код процедур, перевели задания планировщика и отчётность на PostgreSQL. Приложения без исходников подключали через совместимый слой доступа и по трассировке убеждались, что уходят те же запросы.
Сверка и откат Сверили строки и контрольные суммы по таблицам, отдельно прогнали сортировку и отчётные формы, отрепетировали возврат на исходную базу. Провалом считали расхождение контрольной суммы хотя бы по одной таблице и отчёт, где строки встали в другом порядке; без успешной репетиции отката переход не согласовали бы.
Передача администраторам Передали реестр зависимостей, перенесённую базу с протоколом сверки, план перехода и процедуру возврата. Администраторы прошли переход и откат на стенде-двойнике своими руками, прежде чем работать с боевым контуром.
Архитектура
Реестр зависимостей собран не из документации, а из трассировки: сборщик на Kotlin читает поток фактических запросов с боевого MS SQL Server и раскладывает вызовы по объектам, приложениям и учётным записям в PostgreSQL. Схема и данные переносятся pgloader, дальнейшие изменения структуры идут только миграциями Liquibase, потому что часть приложений поставляется без исходников и правки руками на стенде терялись бы при следующем накате. Сверка построчная: служба на Ktor читает обе базы по JDBC пачками по ключу и сравнивает контрольные суммы полей, порядок сортировки проверяется отдельно на ICU-коллациях — выборки с ORDER BY по русским строкам расходились с прежним сервером. Стенд-двойник разворачивается плейбуками Ansible из того же описания, что и целевой контур, поэтому репетиция перехода и процедура возврата выполняются одними и теми же шагами.
Стек
Kotlin · Ktor · PostgreSQL · MS SQL Server · JDBC · Ansible · Liquibase · pgloader · ICU-коллации
Проверяется: воспроизводимая сборка и протоколы проверок
Портирование прикладного продукта под отечественную ОС
Задача, решение и что передали
Задача
Продукт работал на Windows и зависел от библиотек, которых нет в отечественных дистрибутивах, а держаться нужно было в реестре российского ПО.
Решение
Разобрали дерево зависимостей, заменили несовместимые компоненты, перенесли сборку на Linux и собрали пакеты под целевые дистрибутивы с функциональными проверками на каждом.
Сдано 4
Собранные пакеты
Воспроизводимая сборка
Протоколы проверок
Комплект документации
Как делали
Дерево зависимостей Разобрали дерево зависимостей продукта и отметили библиотеки Windows, у которых нет пары в отечественных дистрибутивах. Сверились с требованиями к включению в реестр российского ПО, чтобы понимать, что должно быть доказано на выходе.
Выбор замен Несовместимые компоненты заменили на те, что есть в репозиториях целевых дистрибутивов, вместо сборки из исходников на стороне заказчика. Сборку перенесли на Linux и сделали воспроизводимой с зафиксированными версиями, чтобы пакет собирался одинаково у нас и у заказчика.
Сборка пакетов Сначала собрали продукт под один целевой дистрибутив и прошли функциональные проверки, затем добавили второй и развели различия в пакетировании. Пакеты собирали с зависимостями из репозитория каждого дистрибутива.
Проверка установки Ставили пакеты на чистые системы каждого дистрибутива и проходили функциональные сценарии продукта. Провалом считали установку, потребовавшую ручной доустановки библиотек, и расхождение поведения между дистрибутивами; сборку не приняли бы, если бы она не повторялась при повторном запуске.
Передача сборки Передали собранные пакеты, воспроизводимую сборку, протоколы проверок и комплект документации. Команда разработчика выпускает следующую версию под оба дистрибутива своими силами.
Архитектура
Продукт разложили по границам: серверная часть на Go с HTTP-интерфейсом на chi, локальный агент со встроенной SQLite, внутренние вызовы по gRPC. Нативные зависимости, тянувшие Windows-библиотеки, заменены реализациями на чистом Go — сборка перестала требовать cgo, поэтому один исходник собирается под каждый целевой дистрибутив без набора кросс-компиляторов на агенте. Прикладные данные остаются в PostgreSQL, пути к конфигурации и каталогам приведены к принятой в дистрибутивах раскладке, установка и обновление идут пакетами deb и rpm вместо собственного инсталлятора. Конвейер GitLab CI собирает пакеты для Astra Linux и РЕД ОС и на каждой ОС прогоняет функциональные проверки в чистой виртуальной машине, поэтому расхождение между дистрибутивами видно до выпуска, а сборка повторяется из зафиксированных версий зависимостей.
Стек
Go · chi · PostgreSQL · SQLite · gRPC · GitLab CI · Astra Linux · РЕД ОС · deb/rpm
Проверяется: эталонный набор задач и протокол замера
Разбор ИИ-пилота, застрявшего в демонстрации
Задача, решение и что передали
Задача
Пилот показали, эффект признали, и полгода он жил в демонстрационном режиме: качество ни разу не мерили на общем наборе задач.
Решение
Собрали эталонный набор задач с ожидаемыми ответами и замерили то, что было. Разложили ошибки по корзинам: данные, промпты и модель, процесс. Достроили конвейер данных, журналирование и откат.
Сдано 4
Набор тестов и протокол замера
Письменный критерий приёмки
Реестр разрывов
Обвязка эксплуатации
Как делали
Разбор пилота Подняли, что показывали на демонстрации и на каких примерах, поговорили с теми, кто пилот собирал, и с теми, кто должен им пользоваться. Выяснилось, что общего набора задач нет и мерить качество не на чем — отсюда и спор об эффекте.
Критерий приёмки Первым решением стали письменный критерий приёмки и эталонный набор задач с ожидаемыми ответами. Ошибки договорились раскладывать по корзинам — данные, промпты и модель, процесс, — а версии промптов вынесли отдельно от кода, чтобы откат не требовал пересборки.
Достройка обвязки Собрали эталонный набор и замерили то, что уже было, затем достроили конвейер данных, журналирование каждого вызова и откат на предыдущую версию. Правки вносили по корзинам и после каждой прогоняли набор заново.
Замер на наборе Мерили только на общем эталонном наборе, а не на удачных примерах из демонстрации. Провалом считали правку, которая улучшает показ и ухудшает результат на наборе, и ответ, который нельзя восстановить по журналу.
Передача команде Передали набор тестов и протокол замера, письменный критерий приёмки, реестр разрывов и обвязку эксплуатации. Команда заказчика сама пополняет набор задач и прогоняет его перед каждой выкаткой.
Архитектура
Вокруг существующего пилота достроены три части: конвейер данных в Airflow, сервис замера на FastAPI и хранилище прогонов. Эталонный набор задач с ожидаемыми ответами лежит в PostgreSQL, входные документы и полные ответы модели — в MinIO, версия промпта и параметры генерации привязаны к прогону через MLflow, потому что без этой привязки нельзя сказать, изменилось качество от данных или от правки шаблона. Запросы к модели идут через очередь в Redis с ограничением параллельности: пилот делит карту с другими задачами, и прямой залп на инференс выбивал его по памяти. Ошибки помечаются на записях прогона по корзинам — данные, промпт и модель, процесс, — метрики выводятся в Grafana, откат на предыдущий шаблон делается сменой версии в реестре без пересборки образа; узлы подняты контейнерами.
Обращения приходили в почту, чат и на телефон, единой темы у них не было, а решения об автоматизации принимались по ощущениям руководителей групп.
Решение
Выгрузили обращения за сопоставимый период из почтового архива, чата и карточек CRM, привели к одному формату и разметили выборку вручную вместе с операторами. Построили черновую таксономию тем и для каждой темы описали, чем обращение заканчивается: ответом по регламенту, запросом в смежную систему или ручным разбором. Отдельно зафиксировали, где сценарий упирается не в модель, а в отсутствие API у мастер-системы и в права доступа оператора.
Сдано 4
Таксономия тем
Размеченная выборка
Карта сценариев с оценкой готовности данных и интеграций
Перечень блокеров по смежным системам
Как делали
Выгрузка обращений Выгрузили обращения за сопоставимый период из почтового архива, чата и карточек CRM и привели к одному формату. Разметили выборку вручную вместе с операторами и услышали, чем обращение заканчивается на деле, а не по описанию процесса.
Таксономия и исходы Построили черновую таксономию тем и для каждой описали исход: ответ по регламенту, запрос в смежную систему или ручной разбор. Отдельно решили оценивать не только данные, но и доступность мастер-системы — где нет API и не хватает прав оператора, сценарий упирается не в модель.
Сборка карты Собрали сведение трёх каналов в общий формат, разметочный контур для операторов и группировку близких обращений. На этой основе достроили карту сценариев с оценкой готовности данных и интеграций и перечень блокеров по смежным системам.
Перепроверка разметки Перепроверили таксономию на новой порции обращений и дали операторам разметить её заново, не показывая прежние решения. Провалом считали тему, в которую разные операторы раскладывают одни и те же обращения по-разному, и сценарий, попавший в карту без подтверждённого способа получить данные из мастер-системы.
Передача руководителям Передали таксономию, размеченную выборку, карту сценариев и перечень блокеров по смежным системам. Руководители групп ведут карту дальше и добавляют темы по той же разметочной процедуре.
Архитектура
Обращения из почтового архива по IMAP, выгрузки чата и карточки CRM приводятся загрузчиком на C++ к одной записи: канал, время, текст, автор, исход. Тексты и производные признаки лежат в ClickHouse, потому что разбор идёт агрегатами по темам и каналам за длинный период и упирается в чтение отдельных колонок, а разметка и версии таксономии живут в PostgreSQL, где нужны обновления по одной строке и внешние ключи. Эмбеддинги считает отдельный сервис на ONNX Runtime, вызываемый по gRPC, соседей для черновой группировки ищет Faiss — модель вынесена в свой процесс, поэтому загрузчик не тащит веса и модель меняется без пересборки конвейера. Операторы размечают выборку в настольном инструменте на Qt, который читает пачки из ClickHouse и пишет решения в PostgreSQL; узлы подняты контейнерами, повторный прогон загрузки идемпотентен по идентификатору обращения.
Проверяется: плейбуки развёртывания и инструкции по откату
Перенос прикладных сервисов с Windows Server на Linux
Задача, решение и что передали
Задача
Внутренние сервисы работали на Windows Server — IIS, задания планировщика, файловые шары SMB и доменная аутентификация, — а обновление шло ручными операциями на каждом узле.
Решение
Приложения на .NET пересобрали под Linux и упаковали в контейнеры, IIS заменили на nginx с systemd-юнитами, задания планировщика перевели на таймеры, доменную аутентификацию оставили на Kerberos через keytab с автоматическим обновлением билетов. Файловые шары вынесли в объектное хранилище с S3-совместимым интерфейсом, пути в коде заменили на клиента хранилища, для унаследованных интеграций оставили шлюз с ограниченным SMB-доступом. Развёртывание описали в Ansible, состояние узлов и сервисов вывели в общий мониторинг.
Сдано 4
Передали образы сервисов
Плейбуки развёртывания
Схему аутентификации и доступа к хранилищу
Инструкции по переключению и откату
Как делали
Опись Windows-обвязки Переписали, что держит внутренние сервисы на Windows Server: сайты в IIS, задания планировщика, файловые шары SMB, доменная аутентификация. Посмотрели, какими ручными операциями идёт обновление на каждом узле и какие интеграции ходят в шары напрямую.
Замены и хранилище Приложения на .NET решили пересобрать под Linux и упаковать в контейнеры, IIS заменить на nginx с systemd-юнитами, задания планировщика — на таймеры, а доменную аутентификацию оставить на Kerberos через keytab с автообновлением билетов, чтобы не трогать доменную модель. Файловые шары вынесли в объектное хранилище с S3-совместимым интерфейсом, для унаследованных интеграций оставили шлюз с ограниченным SMB-доступом.
Пересборка сервисов Пересобрали и контейнеризовали сервисы, перевели задания на таймеры, заменили пути в коде на клиента хранилища. Развёртывание описали в Ansible, состояние узлов и сервисов вывели в общий мониторинг.
Стенд с обрывами Гоняли сервисы на стенде под доменными учётными записями, роняя билеты Kerberos и прогоняя унаследованные интеграции через SMB-шлюз. Провалом считали отказ аутентификации после истечения билета без автоматического обновления, недоступность файла для унаследованной интеграции после переноса в объектное хранилище и узел, состояния которого не видно в мониторинге.
Передача сопровождению Передали образы сервисов, плейбуки развёртывания, схему аутентификации и доступа к хранилищу, инструкции по переключению и откату. Сопровождение раскатывает обновление плейбуком и возвращает прежнюю версию само.
Архитектура
Сервисы разобрали по узлам: HTTP-фронт на nginx вместо IIS, прикладные службы в контейнерах под systemd-юнитами, новые и переписанные части — на Swift с Vapor, остальные пересобраны из .NET под Linux. Файловые шары SMB заменены объектным хранилищем MinIO с доступом по S3 API, пути в коде уступили место клиенту хранилища: примонтированная шара привязывала службу к конкретному узлу и к доменному монтированию, из-за чего перезапуск на другом узле требовал ручных операций. Доменная аутентификация осталась на Kerberos — служба ходит с keytab, билет обновляется таймером, поэтому переезд не потребовал менять модель доступа в смежных системах; прикладное состояние лежит в PostgreSQL, локального состояния на узлах нет. Задания планировщика Windows переведены на systemd-таймеры, развёртывание описано плейбуками Ansible, для унаследованных интеграций оставлен шлюз с ограниченным SMB-доступом.
Стек
Swift · Vapor · PostgreSQL · MinIO · S3 API · Docker · Ansible · nginx · Kerberos
Проверяется: журнал действий пользователей и матрица ролей
Кабинет поставщика для закупок, актов и счетов
Задача, решение и что передали
Задача
Поставщики присылали документы почтой, а статус согласования и оплаты узнавали звонком куратору закупки.
Решение
Сделали кабинет с ролями на стороне поставщика: заявка на участие, загрузка документов, подписание, акты и счета с текущим статусом. Сведения о договорах и оплатах тянутся из учётной системы по расписанию, кабинет ничего не пересчитывает сам и не держит вторую копию мастер-данных. Вход по учётной записи организации с разграничением прав её сотрудников, все действия пишутся в журнал.
Сдано 4
Передали кабинет
Интеграцию с учётной системой
Журнал действий пользователей
Матрицу ролей и инструкцию для поставщиков
Как делали
Разбор звонков Разобрали, с чем поставщики звонят кураторам закупок: статус согласования, оплата, приняты ли документы, — и как документы приходят почтой. Выписали, какие сведения о договорах и оплатах живут в учётной системе и что из них можно показывать наружу.
Без второй копии Кабинет ничего не пересчитывает сам и не держит вторую копию мастер-данных: сведения о договорах и оплатах тянутся из учётной системы по расписанию. Вход сделали по учётной записи организации с разграничением прав её сотрудников, все действия пишутся в журнал.
Сборка кабинета Собрали вход и матрицу ролей, затем заявку на участие, загрузку и подписание документов, затем акты и счета с текущим статусом и интеграцию с учётной системой.
Проверка по ролям Заходили под сотрудниками разных организаций и разных ролей и сверяли статусы закупок и оплат с учётной системой. Провалом считали видимость закупки или документа чужой организации, статус, разошедшийся с учётной системой после обновления, и действие, не попавшее в журнал.
Передача кураторам Передали кабинет, интеграцию с учётной системой, журнал действий пользователей, матрицу ролей и инструкцию для поставщиков. Кураторы закупок заводят организации и раздают роли сами, поддержка разбирает спорные случаи по журналу.
Архитектура
Кабинет — фронт на React и сервис на NestJS со своей базой PostgreSQL, загруженные документы лежат в MinIO, ссылки на них — в базе. Мастер-данные по договорам и оплатам кабинет не пересчитывает: синхронизатор по расписанию тянет их из учётной системы в витрину только для чтения, потому что вторая изменяемая копия расходилась бы с учётом и спорную сумму пришлось бы выяснять между двумя источниками. Долгие операции — сборка пакета документов, выгрузка реестра — идут задачами через RabbitMQ, готовый файл отдаётся ссылкой, поэтому запрос пользователя не висит на времени сборки. Вход по учётной записи организации через Keycloak: идентификатор поставщика берётся из токена и подставляется в условие выборки на уровне сервиса, права сотрудников внутри организации разведены ролями, все действия пишутся в журнал в PostgreSQL, узлы стоят контейнерами за nginx и не держат сессию в памяти.
Комплект по событию приходил вперемешку — сканы справок, актов и счетов, — и специалист вручную переносил поля в систему урегулирования.
Решение
Развернули сервис в подписной модели: комплект раскладывается на типы документов, поля извлекаются моделью с указанием места на странице, значения сверяются между документами и с полисом. Несовпадения между счётом и актом, а также по номеру полиса собираются отдельным списком. Уверенные поля уходят в систему урегулирования сообщением, спорные ждут решения специалиста.
Сдано 4
Передали сервис разбора комплектов
Шаблоны типов документов
Правила сверки между документами
Отчёт по противоречиям
Как делали
Обследование Разложили присланные комплекты по событиям — сканы справок, актов и счетов вперемешку, и посмотрели, как специалист переносит поля в систему урегулирования. Выписали, какие значения обязаны совпадать между документами и с полисом.
Архитектура Комплект сначала раскладываем на типы документов, потом извлекаем поля с указанием места на странице — ссылка на место нужна, чтобы специалист не искал значение в скане заново. Сверку между документами вынесли отдельным шагом после извлечения: уверенные поля уходят в систему урегулирования сообщением, спорные ждут решения и в отправку не попадают.
Разработка Собрали разбор комплекта по типам, затем шаблоны полей для справки, акта и счёта, затем правила сверки — счёт против акта и номер полиса против договора. На стенде гоняли архив комплектов, включая события, где часть документов пришла отдельным письмом.
Проверка Прогнали комплекты разного состава, в том числе с расхождением между счётом и актом и с чужим номером полиса. Провалом считали несовпадение, не попавшее в список противоречий, и поле, ушедшее в систему урегулирования без решения специалиста при низкой уверенности.
Передача Передали сервис разбора комплектов, шаблоны типов документов, правила сверки и отчёт по противоречиям. Специалистов учили работать со списком спорных полей, администратора — добавлять тип документа и править правило сверки.
Архитектура
Комплект поступает в Rust-сервис приёма на Axum: файлы складываются в объектное хранилище, событие о новом комплекте публикуется в Kafka, дальше по топикам идут раскладка на типы документов, OCR и извлечение полей мультимодальной моделью в отдельном GPU-воркере. Инференс вынесен отдельным сервисом, потому что он держит модель в памяти карты, и число воркеров задаётся отдельно от приёма, которому нужны только сеть и диск. Извлечённые поля с координатами на странице пишутся в PostgreSQL, там же обычными запросами выполняются сверки между счётом, актом и полисом, а расхождения собираются отдельным перечнем. Уверенные поля уходят в систему урегулирования сообщением в отдельный топик с ключом по номеру дела: порядок событий одного дела сохраняется, а повторная доставка опознаётся получателем по тому же ключу; спорные поля остаются в очереди специалиста.
Сборка дела по страховому случаю из вложений почты
Задача, решение и что передали
Задача
Заявления, фотографии повреждений и справки приходили на общий ящик, и оператор урегулирования сам скачивал вложения, переименовывал их и заводил дело в системе.
Решение
Агент забирает письма по IMAP, раскладывает вложения по типам и распознаёт заявление и справки, доставая номер полиса, дату события и данные транспортного средства. Полис сверяется с реестром договоров по серии и номеру, после чего в системе урегулирования создаётся дело, а исходные файлы прикрепляются к нему без переименования. Письмо без сошедшегося полиса и поля с низкой уверенностью распознавания уходят в очередь оператора с подсветкой.
Сдано 4
Сервис разбора почты
Схема полей дела
Очередь ручной проверки
Журнал разбора вложений и инструкция оператора
Как делали
Обследование Разобрали общий ящик урегулирования — заявления, фотографии повреждений, справки, и посмотрели, как оператор скачивает вложения, переименовывает их и заводит дело. Выписали, какие поля дела он берёт из заявления, а какие из справок.
Архитектура Агент забирает письма по почтовому протоколу и раскладывает вложения по типам до распознавания; полис сверяем с реестром договоров по серии и номеру, и дело в системе урегулирования создаётся только после схождения. Исходные файлы прикрепляем к делу как есть, без переименования, чтобы у оператора остался ровно тот документ, что прислал заявитель.
Разработка Сначала разбор почты и раскладка вложений по типам, затем распознавание заявления и справок с номером полиса, датой события и данными транспортного средства, затем создание дела и очередь ручной проверки. На стенде работали на копии ящика с архивом писем.
Проверка Прогнали архив писем, включая комплекты без справки, фотографии без заявления и письма с чужим номером полиса. Провалом считали дело, заведённое на несошедшийся полис, и поле с низкой уверенностью распознавания, ушедшее в дело без подсветки в очереди оператора.
Передача Передали сервис разбора почты, схему полей дела, очередь ручной проверки и журнал разбора вложений. Операторов учили работать с подсвеченными полями и возвращать письмо в разбор, администратора — заводить новый тип вложения.
Архитектура
Коллектор на Rust забирает письма по IMAP, раскладывает вложения по типам, кладёт файлы в объектное хранилище и публикует задачу разбора в NATS. Распознавание заявления и справок идёт отдельным воркером с OCR и моделью извлечения полей: связь через очередь, а не прямой вызов, нужна потому, что ящик отдаёт письма пачками, а разбор одного комплекта длится дольше интервала опроса. Номер полиса, дата события и данные транспортного средства сверяются с реестром договоров по серии и номеру, дело в системе урегулирования создаётся вызовом с ключом идемпотентности по Message-ID письма, поэтому повторная доставка не заводит второе дело. Исходные файлы прикрепляются к делу без переименования, а письма без сошедшегося полиса и поля ниже порога уверенности попадают в очередь оператора с подсветкой.
Агент сбора досье контрагента из открытых реестров
Задача, решение и что передали
Задача
Перед решением по заявке аналитик открывал реестры по очереди и переносил выписку, лицензии и связанные лица в служебную записку руками.
Решение
Агент по ИНН обходит источники, забирает регистрационные данные, статус, лицензии и сведения о связанных лицах, приводит их к единой схеме и складывает в карточку заявки. Каждый источник вызывается своим коннектором с ограничением частоты, а сырой ответ сохраняется целиком со временем получения. Набор операций агента задан списком: шаг за его пределами не выполняется и остаётся в журнале как отклонённый.
Сдано 4
Коннекторы к источникам
Схема досье
Список разрешённых операций агента
Журнал вызовов и регламент разбора спорных проверок
Как делали
Обследование Прошли путь аналитика по реестрам перед решением по заявке и выписали, что он переносит в служебную записку: выписку, лицензии, сведения о связанных лицах. У каждого источника уточнили ограничения на частоту обращений и формат ответа.
Архитектура Каждый источник закрыли своим коннектором с ограничением частоты, сырой ответ сохраняем целиком со временем получения — иначе спорную проверку нечем разобрать. Набор операций агента задали списком: шаг за его пределами не выполняется, а пишется в журнал как отклонённый, свободный обход сети агентом не разрешали.
Разработка Сначала коннекторы к источникам и хранение сырых ответов, затем приведение к единой схеме досье, затем сборка карточки заявки по ИНН и журнал вызовов. На стенде гоняли по списку ИНН из архива заявок с записанными ответами источников.
Проверка Собирали досье по ИНН из закрытых заявок и сверяли с тем, что аналитик занёс в служебную записку, отдельно смотрели поведение при недоступном источнике и при обращениях подряд. Провалом считали поле досье без сохранённого сырого ответа и выполненный шаг, которого нет в списке разрешённых операций.
Передача Передали коннекторы к источникам, схему досье, список разрешённых операций агента и журнал вызовов. Аналитикам показали разбор спорных проверок по сырым ответам, администратору — добавление источника и правку ограничений частоты.
Архитектура
Агент — Rust-сервис на Axum с отдельным коннектором на каждый источник: обход по ИНН ставится задачей в очередь, коннектор берёт её и сверяется с ограничителем частоты на токен-бакете в Redis — общий счётчик нужен потому, что сервис работает в нескольких репликах, а лимит источника один на всех. Сырой ответ сохраняется целиком в S3-совместимом хранилище со временем получения, приведённые к единой схеме поля — в PostgreSQL, и каждое поле держит ссылку на сырьё, чтобы спорную проверку разбирали по исходнику, а не по пересказу. Набор допустимых операций агента задан списком в конфигурации и проверяется перед вызовом: шаг вне списка не выполняется и пишется в журнал как отклонённый. Регулярные обходы ведёт Airflow, карточка заявки собирается по готовности коннекторов, а недоступный источник помечается в досье и не подвешивает сборку.
Оценка повреждений автомобиля по фотографиям с места события
Задача, решение и что передали
Задача
Фотографии убытка приходили в почту россыпью, и эксперт вручную сопоставлял ракурсы с элементами кузова.
Решение
Детектор находит элементы кузова на снимке и сшивает ракурсы в единую схему автомобиля, отбрасывая дубли и нечитаемые кадры. Классификатор размечает вмятину, скол и разрыв по каждому элементу и отдаёт уверенность по каждой метке. Разметка ложится в карточку убытка, элементы ниже порога уходят эксперту отдельным списком.
Сдано 4
Отданы в работу детектор элементов кузова
Классификатор повреждений
Карточка убытка с разметкой
Список порогов уверенности
Как делали
Обследование Разобрали, как фотографии убытка приходят в почту россыпью и как эксперт сопоставляет ракурсы с элементами кузова. Собрали архив снимков с места события, включая тёмные, смазанные и снятые вплотную кадры.
Архитектура Разделили детекцию элементов кузова и классификацию повреждений: сначала сшиваем ракурсы в единую схему автомобиля, отбрасывая дубли и нечитаемые кадры, потом размечаем вмятину, скол и разрыв по каждому элементу. Уверенность отдаём по каждой метке, элементы ниже порога в карточку не проводим, а собираем эксперту отдельным списком.
Разработка Сначала детектор элементов кузова и сшивка ракурсов, затем классификатор повреждений с уверенностью по метке, затем запись разметки в карточку убытка и список порогов. На стенде прогоняли архив убытков, по которым заключение эксперта уже было.
Проверка Сравнивали разметку с заключениями экспертов по тем же убыткам, отдельно смотрели комплекты из одного ракурса и письма с чужими кадрами. Провалом считали повреждение, отнесённое к соседнему элементу кузова, и метку ниже порога, попавшую в карточку убытка мимо списка эксперта.
Передача Отдали в работу детектор элементов кузова, классификатор повреждений, карточку убытка с разметкой и список порогов уверенности. Экспертам показали разбор списка неуверенных элементов, администратору — правку порогов.
Архитектура
Снимки убытка складываются в S3-совместимое хранилище, событие о комплекте публикуется в Kafka, а C++-сервис инференса читает топик и прогоняет детектор элементов кузова и классификатор повреждений через TensorRT. Модели обучаются на PyTorch с Detectron2 и экспортируются в ONNX, но исполняются C++-рантаймом в том же процессе, где идут сшивка ракурсов и отсев дублей на OpenCV, — так кадры не ходят между процессами и предобработка работает на тех же буферах. Ключ сообщения — номер убытка: это держит порядок обработки снимков одного дела в одной партиции и позволяет переиграть комплект после смены версии модели, не трогая остальные. Разметка с уверенностью по каждой метке пишется в PostgreSQL и ложится в карточку убытка, элементы ниже порога уходят эксперту отдельным списком, порог хранится в конфигурации сервиса.
Обмен CRM и учётной системы по полисам и вознаграждениям
Задача, решение и что передали
Задача
Данные проданного полиса переносили в учётную систему руками, а агентское вознаграждение считали в отдельной таблице.
Решение
Обмен разложен на события — выпуск полиса, оплата взноса, расторжение: каждое уходит отдельным сообщением с идентификатором полиса и версией записи. Приёмник применяет сообщение по правилу последней версии, поэтому порядок доставки не ломает состояние карточки. Начисление агентского вознаграждения собирается из тех же событий, расхождения выводятся в отчёт сверки.
Сдано 4
Карта событий обмена
Правила версионирования записей
Сервис начисления вознаграждения
Отчёт сверки полисов
Как делали
Обследование обмена Прошли путь полиса от продажи до начисления вместе с продавцами и бухгалтерией: смотрели, какие поля менеджер переносит в учётную систему руками, и разобрали таблицу расчёта агентского вознаграждения. Выписали события жизненного цикла — выпуск, оплата взноса, расторжение — и поля, которые реально доходят до расчёта.
Архитектура событий Отказались от синхронизации карточки целиком в пользу отдельных сообщений с идентификатором полиса и версией записи, применяемых по правилу последней версии, — чтобы порядок доставки не ломал состояние. Начисление вознаграждения вынесли отдельным сервисом поверх того же потока событий, а не встроили в приёмник.
Разработка сервисов Собирали по порядку: публикация событий на стороне CRM, приёмник с версионированием, сервис начисления, отчёт сверки. На стенде проигрывали поток с перепутанным порядком сообщений и повторами одного события.
Проверка на полисах Прогнали выгрузку архивных договоров и сверили начисленное вознаграждение с расчётом бухгалтерии. Провалом считали любую строку расхождения в отчёте сверки и любую карточку полиса, состояние которой зависело от порядка прихода сообщений.
Передача и обучение Передали карту событий обмена, правила версионирования и руководство поддержки, разобрали с бухгалтерией строки отчёта сверки. После сдачи бухгалтерия сама закрывает расхождения, а поддержка добавляет новый тип события по описанной схеме.
Архитектура
Шлюз обмена на Node.js принимает вебхуки CRM и публикует события трёх типов — выпуск полиса, оплата взноса, расторжение — в topic-exchange RabbitMQ, тип события лежит в ключе маршрутизации. Адаптер учётной системы забирает сообщение и применяет его к карточке по правилу последней версии: номер полиса и версия записи лежат в теле, поэтому переставленный порядок доставки не откатывает состояние карточки. Очередь выбрана вместо прямого вызова в 1С потому, что учётный контур закрывается на регламентные операции, а событие продажи должно пережить это окно и примениться позже. Журнал событий и текущие версии карточек лежат в PostgreSQL, начисление вознаграждения — отдельный подписчик того же потока, отчёт сверки строится запросом к журналу.
Премию агент считает в присланном файле, бланк оформляет в офисе, а комиссию сверяет по отчёту бухгалтерии в конце периода.
Решение
Кабинет вызывает тарифный движок и сервис печатных форм: премия считается по коэффициентам, объект страхования проверяется на дубль, бланк выпускается с электронной подписью. Реестр договоров и начисленная комиссия считаются в витрине агента и разворачиваются до конкретного полиса. Смена статуса договора приходит в кабинет событием из учётной системы, портфель агента отделён от чужих договоров правами.
Сдано 4
Кабинет агента
Тарифный сервис
Витрина комиссии
Печатные формы полисов
Как делали
Обследование расчёта Прошли с агентами расчёт премии в присланном файле и оформление бланка в офисе, разобрали коэффициенты тарифа и печатные формы полиса. Сверили, как бухгалтерия считает комиссию и почему агент видит её только в отчёте.
Архитектура сервисов Расчёт вынесли в тарифный сервис, печать — в сервис печатных форм, комиссию — в витрину агента, чтобы кабинет ничего не считал сам. Смена статуса договора приходит событием из учётной системы, портфель агента отделили правами, а объект страхования проверяется на дубль до выпуска бланка.
Разработка кабинета Сначала тарифный сервис и проверка объекта на дубль, затем выпуск бланка с электронной подписью, затем витрина комиссии с разворотом до конкретного полиса и реестр договоров.
Проверка расчётов Считали премию по архивным договорам и сверяли с расчётом андеррайтера, а начисленную комиссию — с отчётом бухгалтерии. Провалом считали расхождение премии с тарифом, выпуск полиса на уже застрахованный объект и чужой договор, видимый в портфеле агента.
Передача агентам Передали кабинет агента, тарифный сервис, витрину комиссии, печатные формы полисов и инструкцию по разбору расхождений. Методолог сам меняет коэффициенты тарифа, а агент разворачивает начисленную комиссию до полиса.
Архитектура
Кабинет на ASP.NET Core и тарифный движок разведены по разным сервисам: расчёт премии живёт со своими версионируемыми справочниками коэффициентов, потому что тариф меняется приказом, а договор должен пересчитываться по версии, действовавшей на дату выпуска. Объект страхования проверяется на дубль уникальным индексом по нормализованному идентификатору в PostgreSQL, бланк собирает сервис печатных форм и подписывает через КриптоПро CSP. Смена статуса договора приходит событием из учётной системы в Kafka, подписчик наполняет витрину комиссии, которая разворачивается до конкретного полиса. Портфель агента отделён фильтром по агенту в каждом запросе, интерфейс кабинета собран на React, справочники коэффициентов кешируются в Redis.
Перевод системы урегулирования убытков с MS SQL на PostgreSQL
Задача, решение и что передали
Задача
Расчёт выплат жил в тысячах строк T-SQL внутри MS SQL, а срок поддержки платформы заканчивался.
Решение
Хранимые процедуры расчёта выплат переписаны на PL/pgSQL, конструкции T-SQL с табличными переменными и MERGE заменены явными операциями. Поднят двойной прогон: одно и то же дело считается в обеих СУБД, разница попадает в таблицу расхождений с указанием шага расчёта. Переключение сделано через логическую репликацию с окном возврата на прежний контур.
Сдано 4
Переданы переписанные процедуры
Таблица расхождений двойного прогона
План переключения
Процедура возврата
Как делали
Обследование расчёта Разобрали расчёт выплат в хранимых процедурах T-SQL, выделили шаги расчёта и зависимости между ними. Собрали дела разных видов страхования — с частичными выплатами, регрессами и отказами — для контрольного набора.
Архитектура перехода Процедуры расчёта решили переписать на PL/pgSQL, заменив табличные переменные и MERGE явными операциями, а не воспроизводить прежние конструкции. Переключение заложили через логическую репликацию с окном возврата на прежний контур.
Разработка процедур Переписывали процедуры шаг за шагом, сразу поднимая двойной прогон: одно и то же дело считается в обеих СУБД, разница пишется в таблицу расхождений с указанием шага расчёта.
Проверка двойным прогоном Гоняли двойной прогон на контрольном наборе дел и на текущем потоке урегулирования. Провалом считали непустую таблицу расхождений по сумме выплаты и любой шаг расчёта, для которого расхождение не удавалось объяснить.
Передача сопровождению Передали переписанные процедуры, таблицу расхождений двойного прогона, план переключения и процедуру возврата. Сопровождение заказчика само запускает двойной прогон после правки расчёта и читает расхождения по шагам.
Архитектура
Расчёт выплат переписан на PL/pgSQL, табличные переменные и MERGE заменены временными таблицами и явными операциями вставки и обновления. Стенд двойного прогона сделан сервисом на Ktor: он подаёт одно и то же дело в оба контура через отдельные пулы JDBC и сравнивает результат по шагам расчёта, а не по итоговой сумме — иначе расхождение видно, но неизвестно, на каком шаге оно возникло; разница пишется в таблицу расхождений с номером шага. Переключение выполнено логической репликацией с окном возврата: поток продолжает наполнять прежний контур, пока идёт наблюдение, поэтому возврат не требует обратной миграции данных. Приложения подключаются через pgBouncer в режиме transaction pooling, так как расчёт открывает много коротких сессий.
Стек
Kotlin · Ktor · PostgreSQL · PL/pgSQL · JDBC · Docker · pgBouncer · pglogical · MS SQL Server
Разграничение доступа к базе знаний ассистента по ролям
Задача, решение и что передали
Задача
Ассистент отвечал по общему индексу, и специалист одной линии страхования видел в выдаче фрагменты договоров и убытков соседней.
Решение
Разметили источники метками подразделения и уровня доступа, перенесли метку на каждый фрагмент индекса вместе с координатами документа. Фильтр по роли применяется до векторного поиска, а не отсекает лишнее в уже собранном ответе. Каждое несовпадение метки фрагмента и прав учётной записи пишется в журнал и разбирается службой безопасности.
Сдано 4
Матрица ролей и меток
Регламент присвоения уровня доступа
Журнал отказов
Набор проверок на утечку между линиями
Как делали
Обследование индекса Разобрали состав индекса ассистента и увидели, что фрагменты договоров и убытков разных линий страхования лежат в общем пространстве. Собрали с подразделениями, кто и на какие документы имеет право.
Архитектура меток Источники разметили метками подразделения и уровня доступа, метку решили переносить на каждый фрагмент индекса вместе с координатами документа. Фильтр по роли применяется до векторного поиска, а не отсекает лишнее в уже собранном ответе.
Разработка фильтра Сначала разметка источников и перенос меток в индекс, затем фильтрация по правам учётной записи на этапе поиска, затем журнал несовпадений метки фрагмента и прав.
Проверка на утечку Задавали вопросы по соседней линии страхования от учётных записей с разными правами и проверяли выдачу по координатам фрагментов. Провалом считали любой фрагмент чужой линии в ответе или в контексте и обращение к индексу, обошедшее фильтр до поиска.
Передача безопасности Передали матрицу ролей и меток, регламент присвоения уровня доступа, журнал отказов и набор проверок на утечку между линиями. Служба безопасности сама размечает новый источник и разбирает журнал несовпадений.
Архитектура
Индексация вынесена отдельным конвейером: при разборе документа метка подразделения и уровня доступа переносится на каждый фрагмент и пишется рядом с вектором в OpenSearch как поле фильтра, изменения источников приходят конвейеру событиями Kafka. Сервис ответа на Spring Boot берёт роль из OIDC-токена Keycloak, разворачивает её в список допустимых меток и передаёт их фильтром в kNN-запрос — отсечение выполняется до поиска, а не вычищается из готового ответа, потому что попавший в контекст модели фрагмент уже нельзя считать неувиденным. Несовпадение метки фрагмента и прав учётной записи пишется отдельной записью в PostgreSQL и разбирается службой безопасности. Эмбеддинги считает отдельный сервис, промпт собирается только из прошедших фильтр фрагментов вместе с координатами документа.
Идей по применению ИИ в урегулировании набралось много, но какие из них обеспечены данными и какой объём разбора закрывают — никто не считал.
Решение
Выгрузили дела по убыткам за доступный период и разметили выборку по типу события, каналу поступления и способу завершения. Для каждого предложенного сценария посчитали охват по размеченной выборке и проверили, есть ли в деле поля, которые модель получает на входе. Сценарии без исторической разметки вынесли отдельным списком с описанием того, что нужно начать собирать.
Сдано 4
Размеченная выборка дел
Карта сценариев с охватом
Требования к входным полям
Список сценариев без данных
Как делали
Разбор дел Выгрузили дела по убыткам за доступный период и вместе с урегулировщиками разобрали, как заводится дело, что заполняется на приёме, а что появляется только в переписке. Свели все предложенные сценарии применения ИИ в один список.
Схема разметки Решили считать охват по единой размеченной выборке дел, размечая по типу события, каналу поступления и способу завершения, а требования к входным полям описывать отдельно от самого сценария. Сценарии без исторической разметки вынесли в отдельный список, а не стали додумывать им данные.
Разметка и охват Подготовили выборку и разметку в инструменте разметки, затем по каждому сценарию посчитали охват на этой выборке. После этого проверили, есть ли в деле поля, которые модель получает на входе.
Проверка разметки Перепроверили разметку на пересечении с урегулировщиками и сверили посчитанный охват с ручным разбором отобранных дел. Сценарий не проходил, если объявленное входным поле на исторических делах оказывалось пустым или заполнялось уже после решения по убытку.
Передача карты Отдали размеченную выборку дел, карту сценариев с охватом, требования к входным полям и список сценариев без данных. Проговорили с урегулированием, какие поля нужно начать собирать, чтобы отложенные сценарии стало чем считать.
Архитектура
Выборка дел выгружается в ClickHouse, разметка по типу события, каналу поступления и способу завершения ведётся в Label Studio и экспортируется в Parquet фиксированным срезом — охват считается на версии среза, иначе два запуска дают разные цифры при продолжающейся разметке. Счётчик охвата написан сервисом на C++: он читает колонки через Apache Arrow и по каждому сценарию строит агрегаты по полям разметки на всей выборке, поэтому хранилище колоночное, а не строчное — карточка дела целиком для такого прохода не поднимается. Описания сценариев, требования к входным полям и признак наличия поля в деле лежат в PostgreSQL, наружу сервис отдаёт результат по gRPC со схемами Protobuf. Сценарии без исторической разметки помечены в том же реестре отдельным признаком и в расчёт охвата не попадают; сборка поставляется в Docker.
Стек
C++ · gRPC · ClickHouse · PostgreSQL · Protobuf · Docker · Apache Arrow · Label Studio · Parquet
Подписной разбор договоров лизинга и графиков платежей
Задача, решение и что передали
Задача
Менеджер поднимал условия страхования, порядок изъятия предмета лизинга и очередной платёж чтением сканов договора и допсоглашений по каждому обращению клиента.
Решение
Подписной сервис разбирает договор лизинга и допсоглашения: график платежей, авансовый платёж, выкупную стоимость, условия страхования предмета лизинга и порядок его изъятия при просрочке. Каждое извлечённое условие хранится со ссылкой на страницу и пункт, допсоглашение перекрывает прежнюю редакцию условия в реестре. Вопрос по номеру договора отвечается действующей редакцией, неуверенное извлечение уходит в очередь проверки юриста.
Сдано 4
Передали подписной сервис разбора договоров
Реестр извлечённых условий со ссылками на пункты
Правила перекрытия допсоглашениями
Очередь проверки спорных извлечений
Как делали
Разбор договоров Разобрали с менеджерами, что они ищут в сканах договора и допсоглашений при обращении клиента: условия страхования, порядок изъятия предмета лизинга, очередной платёж. На пачке договоров посмотрели, как оформляются допсоглашения и какие условия они меняют.
Реестр условий Решили хранить каждое извлечённое условие со ссылкой на страницу и пункт и вести реестр, в котором допсоглашение перекрывает прежнюю редакцию условия. Ответ по номеру договора строится по действующей редакции, а неуверенное извлечение уходит в очередь проверки юриста.
Сборка извлечения Собрали разбор договора и допсоглашений — график платежей, авансовый платёж, выкупная стоимость, условия страхования предмета лизинга, порядок изъятия при просрочке, — затем реестр условий и правила перекрытия. Последней сделали очередь проверки спорных извлечений.
Сверка с юристами Сличали извлечённые условия с договорами, вычитанными юристами, и отдельно грузили допсоглашение, повторяя вопрос по тому же номеру договора. Провалом считали ответ по перекрытой редакции условия и извлечение без ссылки на страницу и пункт, по которой менеджер не может его проверить.
Передача менеджерам Передали подписной сервис разбора договоров, реестр извлечённых условий со ссылками на пункты, правила перекрытия допсоглашениями, очередь проверки спорных извлечений и инструкцию менеджера. Показали, как разбирать очередь и как заводить новый договор.
Архитектура
Файлы договора и допсоглашений складываются в MinIO, задача разбора уходит через RabbitMQ в сервис извлечения на C++: текстовый слой снимается PDFium, для сканов подключается Tesseract, и каждое извлечённое условие сохраняется с номером страницы, координатами области и пунктом. Условия хранятся в PostgreSQL версионно — допсоглашение не переписывает строку, а закрывает прежнюю редакцию датой, поэтому ответ по номеру договора собирается срезом на дату, а прежняя редакция остаётся видимой при разборе спора. Извлечение с уверенностью ниже порога в реестр действующих условий не попадает и уходит в очередь проверки юриста, чтобы график платежей и условия страхования не строились на неподтверждённом значении. Кабинет менеджера на React обращается к сервису по gRPC и показывает условие рядом с областью страницы, откуда оно взято; поставка — Docker.
Агент сборки карточки предмета лизинга из вложений
Задача, решение и что передали
Задача
Менеджер собирал по переписке ПТС, счёт и спецификацию поставщика, полис и акт приёма-передачи, перебивал VIN и стоимость в карточку, и предмет уходил в график платежей с опечаткой.
Решение
Агент разбирает переписку по сделке, распознаёт паспорт транспортного средства или паспорт самоходной машины, счёт и спецификацию поставщика, полис и акт приёма-передачи. Из документов вытаскиваются VIN или заводской номер, марка, год выпуска, стоимость и срок страхования; значения сверяются между документами и с договором, несходящееся поле подсвечивается в карточке. Комплект по предмету проверяется на полноту до выдачи, а график платежей строится по подтверждённой стоимости и авансу, а не по цифре из письма.
Сдано 4
Сервис разбора вложений
Карточка предмета лизинга с историей полей
Правила сверки документов между собой
Очередь несошедшихся значений
Как делали
Разбор переписки Разобрали переписку по сделкам и увидели, как менеджер собирает по письмам ПТС, счёт и спецификацию поставщика, полис и акт приёма-передачи и перебивает VIN и стоимость в карточку. Подняли случаи, когда предмет уходил в график платежей с опечаткой.
Схема сверки Решили сверять значения между документами и с договором, а не доверять одному источнику: VIN или заводской номер, марка, год выпуска, стоимость, срок страхования. График платежей договорились строить по подтверждённой стоимости и авансу, а не по цифре из письма, а карточке дали историю полей.
Сборка карточки Сделали разбор переписки и распознавание паспорта транспортного средства или паспорта самоходной машины, счёта и спецификации поставщика, полиса и акта приёма-передачи. Затем собрали извлечение полей со сверкой между документами и с договором, очередь несошедшихся значений и проверку комплекта на полноту до выдачи.
Прогон сделок Прогнали закрытые сделки и сличили собранные карточки с заведёнными менеджерами, отдельно подкладывали документы с расхождением VIN и стоимости. Провалом считали выдачу предмета с неполным комплектом и расхождение VIN между ПТС и актом приёма-передачи, не подсвеченное в карточке.
Передача менеджерам Передали сервис разбора вложений, карточку предмета лизинга с историей полей, правила сверки документов между собой, очередь несошедшихся значений и инструкцию менеджера. Показали, как разбирать очередь и как подтверждать стоимость до построения графика платежей.
Архитектура
Переписку по сделке разбирает коннектор на Spring Boot: вложения складываются в MinIO, задачи распознавания уходят в Kafka, воркеры определяют тип документа — паспорт транспортного средства или самоходной машины, счёт, спецификация, полис, акт — и вытаскивают VIN или заводской номер, марку, год выпуска, стоимость и срок страхования. Значения не перезаписывают карточку, а ложатся в PostgreSQL журналом: каждое значение поля хранится с документом-источником, страницей и временем, поэтому видно, из чьего документа взята стоимость и почему поле разошлось. Сверка идёт между документами и с договором, несошедшееся поле помечается и попадает в очередь менеджера, а график платежей строится только от подтверждённой стоимости и аванса. Передача в учётную систему 1С вынесена отдельной службой через OData-адаптер — учётный контур принимает данные своим темпом и в своё окно, и разбор писем от него не зависит; всё разворачивается в Kubernetes.
График платежей вели в учётной системе, менеджер в CRM смотрел прежнюю выгрузку, поэтому после досрочного погашения остаток по договору лизинга у них расходился.
Решение
Связали CRM и учёт двусторонним обменом по договору и предмету лизинга: карточка предмета с VIN и актом приёма-передачи заводится один раз, а пересчитанный график платежей уходит в CRM версией с отметкой актуальности. Выписка банка разбирается по идентификатору договора, зачёт закрывает конкретный период графика, непривязанная строка ждёт разбора. Полис на предмет лизинга и срок его действия приходят в ту же карточку, окончание поднимает задачу менеджеру.
Сдано 4
Передали шлюз обмена CRM и учёта
Схему версий графика платежей
Разбор банковской выписки по договорам
Журнал расхождений остатка
Как делали
Обследование договоров Сопоставили, откуда менеджер в CRM берёт остаток по договору лизинга, и увидели, что он смотрит прежнюю выгрузку, поэтому после досрочного погашения цифра расходится с учётной системой. Разобрали карточку предмета лизинга — VIN, акт приёма-передачи, полис и срок его действия — и порядок разноски банковской выписки.
Архитектура обмена Обмен сделали двусторонним по договору и предмету лизинга: карточка предмета заводится один раз, а пересчитанный график платежей уходит в CRM версией с отметкой актуальности. Зачёт по выписке закрывает конкретный период графика, а непривязанная строка не разносится наугад и ждёт разбора.
Сборка шлюза Сначала связали карточки договора и предмета лизинга с VIN и актом приёма-передачи, потом версии графика после пересчёта, потом разбор выписки по идентификатору договора и приём полиса со сроком действия. На стенде прогнали досрочное погашение с пересчётом графика, чтобы CRM не осталась на прежней версии.
Проверка остатков Проверяли на договорах с досрочным погашением и частичной оплатой, сверяя остаток в CRM и в учёте по каждому договору. Провалом считались расхождение остатка после пересчёта графика и строка выписки, разнесённая на чужой договор; не приняли бы и окончание срока полиса, не поднявшее задачу менеджеру.
Передача менеджерам Передали шлюз обмена CRM и учёта, схему версий графика платежей, разбор банковской выписки по договорам, журнал расхождений остатка и регламент сопровождения. Менеджер видит в CRM актуальную версию графика, непривязанные строки выписки бухгалтерия разбирает по журналу.
Архитектура
Шлюз на ASP.NET Core держит два адаптера: к 1С:Предприятие через её HTTP-сервисы и к amoCRM через REST с приёмом вебхуков, между ними идут сообщения RabbitMQ с ключом по договору лизинга и предмету. График платежей не правится на месте, а пишется новой версией с отметкой актуальности в PostgreSQL — после досрочного погашения менеджеру нужно видеть, какая редакция действует и на что ссылались прежние согласования, поэтому обновление строки заменено дозаписью версии. Банковская выписка разбирается загрузчиком ISO 20022, зачёт привязывается к конкретному периоду графика по идентификатору договора, непривязанная строка остаётся в очереди разбора и остаток не двигает. Карточка предмета с VIN и актом приёма-передачи заводится один раз и обновляется по идемпотентному ключу документа, полис ложится в неё же со сроком; Redis кэширует справочники адаптеров, контур поднимается Docker Compose.
Стек
C# · ASP.NET Core · PostgreSQL · Redis · RabbitMQ · Docker Compose · 1С:Предприятие · amoCRM · ISO 20022
Обмен учётной системы займов с бюро кредитных историй
Задача, решение и что передали
Задача
Сведения по договорам займа выгружали в бюро общим файлом, ошибки формата всплывали протоколом, и исправленные записи досылали отдельным списком.
Решение
Отправку разложили на события: выдача, платёж, изменение графика и закрытие займа кладутся в очередь и уходят в бюро сообщением с идентификаторами договора и субъекта. Квитанция и протокол ошибок разбираются автоматически, отклонённая запись возвращается в карточку договора с кодом причины и повторяется после правки. Ключ считается по договору и типу события, поэтому повторная отправка того же события у бюро не задваивается.
Сдано 4
Передали адаптер бюро
Очередь событий с ключами
Разбор квитанций и протокола ошибок
Реестр отклонённых записей
Как делали
Обследование выгрузки Разобрали выгрузку общим файлом и протоколы ошибок бюро: какие записи отклоняются по формату и как исправленное досылается отдельным списком. Выписали, какие события по договору займа вообще происходят в учётной системе — выдача, платёж, изменение графика, закрытие — и где лежат идентификаторы договора и субъекта.
Архитектура событий Отправку разложили на события вместо общего файла: каждое кладётся в очередь и уходит сообщением с идентификаторами договора и субъекта. Ключ считается по договору и типу события, чтобы повторная отправка у бюро не задваивалась, а отклонённая запись возвращается в карточку договора с кодом причины, а не копится в отдельном списке.
Сборка адаптера Сначала очередь событий и формирование сообщений, потом разбор квитанций и протокола ошибок, потом реестр отклонённых записей и повтор после правки. На стенде гоняли отклонения по формату и повторную отправку того же события, проверяя работу ключа.
Проверка отклонениями Прогнали события по договорам, включая изменение графика и закрытие, и намеренно отправляли записи с ошибками формата. Провалом считались событие, задвоенное у бюро по одному ключу, и отклонённая запись, не вернувшаяся в карточку договора с кодом причины; не приняли бы и разбор квитанции, при котором отправка числится принятой при отказе бюро.
Передача сопровождению Передали адаптер бюро, очередь событий с ключами, разбор квитанций и протокола ошибок, реестр отклонённых записей и регламент повторной отправки. Сотрудник правит отклонённую запись прямо в карточке договора и отправляет повтор сам, общий файл больше не собирается.
Архитектура
Учётная система пишет изменения по займу в таблицу исходящих, сервис на chi раскладывает их на события — выдача, платёж, изменение графика, закрытие — и кладёт в RabbitMQ с ключом по договору и типу события. Событийная отправка выбрана вместо общего файла: при отказе бюро возвращается конкретная запись с кодом причины, она правится и уходит повторно, а не пересылается весь пакет, и тот же ключ не даёт бюро задвоить уже принятое событие. Сообщения собираются по XSD-схемам бюро, подписываются и уходят по SFTP; квитанция и протокол ошибок разбираются приёмником и ложатся в карточку договора в PostgreSQL, отклонённые записи копятся отдельным реестром. Отправка и разбор квитанций развёрнуты в Kubernetes раздельно, поэтому недоступность канала копит очередь, но не мешает учётной системе проводить документы; возраст очереди и число отказов видны в Grafana.
Стек
Go · chi · PostgreSQL · RabbitMQ · Kubernetes · XSD-схемы · SFTP · Grafana
Кабинет лизингополучателя с графиком платежей и актами приёма-передачи
Задача, решение и что передали
Задача
График платежей, допсоглашения и страховой полис по каждой единице техники клиент искал в переписке, а остаток задолженности уточнял у менеджера.
Решение
Кабинет собирает по договору карточку предмета лизинга: VIN, номер ПТС, акт приёма-передачи, срок страхового полиса и график платежей с авансом, процентами и выкупной стоимостью. Начисления и поступления берутся из учётной системы, поэтому в кабинете видно, какой платёж закрыт, а какой просрочен. Новый полис загружается в кабинете, проверяется по сроку действия и привязывается к единице техники, а не к договору целиком.
Сдано 4
Кабинет лизингополучателя
Витрина графика платежей
Обмен с учётной системой по начислениям и поступлениям
Проверка сроков страховых полисов
Как делали
Обследование обращений Разобрали, что клиент ищет в переписке — график платежей, допсоглашения, страховой полис по каждой единице техники — и с чем звонит менеджеру. Посмотрели в учётной системе состав договора и предмета лизинга: VIN, номер ПТС, акт приёма-передачи, начисления и поступления.
Архитектура карточки Карточку в кабинете собрали вокруг предмета лизинга, а не договора целиком: график с авансом и выкупной стоимостью, акт приёма-передачи, действие полиса. Начисления и поступления берутся из учётной системы, поэтому кабинет показывает, какой платёж закрыт, а какой просрочен, и не ведёт свой счёт задолженности; новый полис привязывается к единице техники.
Сборка кабинета Сначала обмен с учётной системой по договорам, предметам, начислениям и поступлениям, потом витрина графика платежей, потом загрузка полиса с проверкой действия и вывод просрочки. На стенде прогоняли договор с несколькими единицами техники и досрочно закрытый платёж.
Проверка графика График и остаток задолженности в кабинете сверили с учётной системой по договорам разного состава. Провалом считались платёж, показанный закрытым при непоступивших деньгах, и полис, принятый с истёкшим действием; не приняли бы и кабинет, где документы одной единицы техники видны по другому договору.
Передача менеджерам Передали кабинет лизингополучателя, витрину графика платежей, обмен с учётной системой по начислениям и поступлениям, проверку страховых полисов и инструкцию менеджера. Менеджер подключает клиента сам и разбирает вопрос по той же карточке, которую видит клиент.
Архитектура
Кабинет читает не учётную систему, а свою витрину в PostgreSQL: начисления, поступления и версии графика приходят из 1С:ERP сообщениями RabbitMQ и применяются идемпотентно по номеру документа. Отдельная витрина взята потому, что кабинет должен отвечать и в закрытие периода, когда учётная система занята расчётами, а состав экрана — договор, предмет лизинга, график с авансом и выкупной стоимостью — не совпадает с регистрами учёта. Карточка предмета собирается по VIN и номеру ПТС, полис грузится в кабинете в MinIO, срок действия хранится полем и проверяется задачей по расписанию, привязка идёт к единице техники, а не к договору целиком. Фронт на Vue ходит в API на ASP.NET Core, Redis кэширует витрину графика для повторных открытий, контур поднимается Docker Compose; при обрыве обмена кабинет показывает витрину с отметкой времени последней синхронизации.
Маскирование платёжных данных в разборе споров по чарджбэкам
Задача, решение и что передали
Задача
Аналитик разбирал спорную операцию, копируя в модель выписку с номером карты, телефоном и идентификатором плательщика.
Решение
Между приложением и моделью встал шлюз: он находит номер карты, срок действия, телефон и идентификатор плательщика и подменяет их масками, сохраняя тип операции, код ответа эквайера и признак чарджбэка. Соответствие маски и исходного значения живёт в отдельном сервисе, доступ к нему выдаётся не вместе с доступом к ассистенту. Обратная подстановка происходит только в карточке спора у оператора.
Сдано 4
Передали правила распознавания платёжных реквизитов
Сервис масок
Журнал обращений с масками
Протокол проверки на выгрузке операций
Как делали
Разбор выписок Посмотрели, как аналитик разбирает спорную операцию, и увидели, что в модель копируется выписка целиком — с номером карты, сроком действия, телефоном и идентификатором плательщика. Выписали поля, действительно нужные для разбора: тип операции, код ответа эквайера, признак чарджбэка.
Шлюз масок Между приложением и моделью поставили шлюз, который подменяет платёжные реквизиты масками и оставляет разборные поля нетронутыми. Соответствие маски и исходного значения вынесли в отдельный сервис, доступ к которому выдаётся не вместе с доступом к ассистенту, а обратную подстановку разрешили только в карточке спора у оператора.
Правила распознавания Написали правила распознавания номера карты, срока действия, телефона и идентификатора плательщика, собрали сервис масок и провели через шлюз весь поток обращений. Карточку спора доработали под обратную подстановку по отдельному праву.
Прогон операций Прогнали шлюз на выгрузке операций, включая реквизиты внутри свободного комментария и в приложенной выписке, и просмотрели журнал обращений. Провалом считали любой платёжный реквизит, ушедший к модели или сохранившийся в журнале без маски, а также возможность снять маску, имея только доступ к ассистенту.
Передача аналитикам Отдали правила распознавания платёжных реквизитов, сервис масок, журнал обращений с масками и протокол проверки на выгрузке операций; с аналитиками и операторами прошли разбор спора. Служба сама правит правила распознавания и раздаёт право на обратную подстановку.
Архитектура
Между приложением аналитика и моделью встал шлюз на FastAPI: он разбирает промпт, находит номер карты, срок действия, телефон и идентификатор плательщика правилами Presidio и заменяет их токенами, оставляя тип операции, код ответа эквайера и признак чарджбэка. Номер карты проверяется контрольной суммой, а не длиной последовательности цифр, иначе под маску уходили бы номера операций и суммы, на которых и строится разбор спора. Соответствие токена и исходного значения хранит отдельный сервис токенизации со своей базой PostgreSQL и ключом из HashiCorp Vault: доступ к нему выдаётся не вместе с доступом к ассистенту, поэтому учётная запись аналитика реквизиты не раскрывает. Обращения с масками уходят в журнал через Kafka, горячие токены кэшируются в Redis, обратная подстановка выполняется только в карточке спора у оператора; узлы разложены в Kubernetes, панели — в Grafana.
Карта сценариев ИИ по сопровождению лизингового портфеля
Задача, решение и что передали
Задача
Просрочку по графику платежей ловили выгрузкой в конце месяца, а решение об изъятии предмета лизинга собирали письмами между отделами.
Решение
Разобрали поток по стадиям договора — заявка, оценка предмета, график платежей, страхование, досрочный выкуп, изъятие — и для каждой выписали, какое решение сегодня принимает человек и по каким данным. Для каждого сценария указали источник признаков: график, движение по счёту, отметки о состоянии предмета, страховой полис, и пометили те, где истории для замера качества нет. Сценарии, затрагивающие изъятие, вынесены в отдельный ряд с обязательным подтверждением человеком.
Сдано 3
Передали карту сценариев по стадиям договора
Паспорт данных на каждый сценарий
Перечень пробелов в истории и очередь пилотов
Как делали
Обход стадий Прошли по стадиям договора — заявка, оценка предмета, график платежей, страхование, досрочный выкуп, изъятие — и с каждым отделом разобрали, какое решение сегодня принимает человек и по каким данным. Увидели, что просрочка по графику ловится разовой выгрузкой, а решение об изъятии предмета лизинга собирается письмами между отделами.
Источники признаков Для каждого сценария зафиксировали источник признаков — график платежей, движение по счёту, отметки о состоянии предмета, страховой полис — и пометили те, где истории для замера качества нет. Сценарии, затрагивающие изъятие, вынесли в отдельный ряд с обязательным подтверждением человеком.
Карта сценариев Собрали карту сценариев по стадиям договора и паспорт данных на каждый сценарий, выстроили очередь пилотов и свели перечень пробелов в истории. Разложили, какие сценарии зависят от закрытия этих пробелов.
Проверка истории Прошли карту с сопровождением портфеля, страхованием и взысканием и по каждому сценарию проверили на архивных договорах, поднимаются ли признаки из названного источника. Сценарий, под который история не поднимается, в очередь пилотов не попадал, а сценарий с изъятием без подтверждения человеком считался неприемлемым.
Передача отделам Отдали карту сценариев по стадиям договора, паспорт данных на каждый сценарий, перечень пробелов в истории и очередь пилотов; с руководителями отделов прошли порядок запуска пилота. Заказчик сам дополняет карту и ведёт паспорта данных.
Архитектура
Стадии договора — заявка, оценка предмета, график платежей, страхование, досрочный выкуп, изъятие — описаны исполняемой моделью BPMN в Camunda, и к каждой точке решения привязан источник признаков: график, движение по счёту, отметки о состоянии предмета, страховой полис. Признаки собираются в витрину ClickHouse суточной задачей, и сценарии читают витрину, а не учётное ядро: ядро обслуживает проведение платежей, и разбор портфеля не должен делить с ним ту же базу. Служба сценариев на Spring Boot берёт события договора из Kafka и ведёт паспорт данных в PostgreSQL, помечая сценарии, для которых истории на замер качества нет. Сценарии, затрагивающие изъятие предмета лизинга, оформлены задачей с обязательным подтверждением человеком — шаг процесса не закрывается автоматически, узлы разложены в Kubernetes, очередь пилотов и пробелы выведены в Grafana.
Контроль каналов утечки с разбором почты моделью в контуре
Задача, решение и что передали
Задача
Почту и файловые выгрузки смотрели выборочно, а поводом для разбора становилась жалоба или случайная находка.
Решение
Развернули контур контроля каналов: агенты на рабочих станциях, перехват почты и веб-выгрузок на шлюзе, теневое копирование вложений в отдельное хранилище, журнал действий с документом. Разбор ведёт языковая модель на GPU внутри периметра: она относит вложение к категории тайны, сверяет получателя с реестром допусков и заводит инцидент с цитатой из файла.
Сдано 4
Передали контур перехвата
Узел инференса
Каталог категорий тайны
Правила теневого копирования
Как делали
Разбор каналов Посмотрели, как сегодня попадают на разбор почта и файловые выгрузки, и увидели выборочный просмотр с поводом в виде жалобы или случайной находки. Со службой безопасности выписали категории тайны и реестр допусков сотрудников.
Контур перехвата Контур решили строить по каналам — агенты на рабочих станциях, перехват почты и веб-выгрузок на шлюзе, теневое копирование вложений в отдельное хранилище, журнал действий с документом. Разбор отдали языковой модели на GPU внутри периметра, чтобы содержимое вложений не покидало контур, а инцидент заводился с цитатой из файла.
Агенты и шлюз Развернули агенты и шлюз перехвата, настроили теневое копирование по правилам, подняли узел инференса и каталог категорий тайны. Отнесение вложения к категории и сверку получателя с реестром допусков обкатали на теневых копиях до включения на всех станциях.
Контрольные отправки Прогнали контрольные отправки — вложение с реквизитами клиентов внешнему адресату, выгрузка через веб-канал, пересылка на личную почту — и разобрали заведённые инциденты. Пропущенная контрольная отправка и инцидент без цитаты из файла считались провалом, а выход содержимого вложения за периметр — основанием не принимать контур.
Передача безопасности Отдали контур перехвата, узел инференса, каталог категорий тайны, правила теневого копирования и журнал инцидентов; с офицерами безопасности прошли разбор инцидента. Служба сама правит каталог категорий и реестр допусков.
Архитектура
Агент на рабочей станции написан на C++ и перехватывает файловые операции и выгрузки, на периметре стоят узлы перехвата: милтер в цепочке почтового сервера и ICAP-сервис на веб-прокси; события идут в Kafka, теневые копии вложений — в MinIO. Журнал действий над документом лежит в ClickHouse — расследование читает узкий срез полей (пользователь, документ, канал, время) по всему потоку событий с рабочих станций, и колоночное хранилище поднимает только эти поля. Разбор вложения ведёт сервис vLLM на GPU внутри периметра: он относит файл к категории тайны по каталогу, сверяет получателя с реестром допусков и заводит инцидент с цитатой из файла. Модель вынесена за очередь отдельно от перехвата, поэтому при её недоступности вложение всё равно скопировано и событие записано, а разбор догоняет позже; сетевые события снимает Suricata, служебный обмен идёт по gRPC, узлы разложены в Kubernetes.
Брокер сессий подрядчиков с разбором записи собственной моделью
Задача, решение и что передали
Задача
Подрядчики работали под общими привилегированными учётками, а записи сессий складывались в архив и никем не разбирались.
Решение
Построили контур привилегированного доступа: брокер сессий с выдачей одноразовых учёток, запись экрана и введённых команд, теневое копирование выгруженных реестров, неизменяемый журнал действий над документом. Языковая модель внутри контура читает расшифровку сессии и помечает эпизоды выгрузки клиентской базы, правки лимита и обхода согласования.
Сдано 4
Передали брокер сессий
Узел инференса
Схему неизменяемого журнала
Матрицу доступа подрядчиков
Как делали
Разбор доступа Разобрали, как подрядчики получают доступ, и увидели общие привилегированные учётки и архив записей сессий, который никто не смотрит. Со службой безопасности выписали действия, обязанные попадать в разбор: выгрузка клиентской базы, правка лимита, обход согласования.
Брокер сессий Доступ решили выдавать через брокер сессий одноразовыми учётками вместо общих, с записью экрана и введённых команд. Разбор расшифровки отдали языковой модели внутри контура, а журнал действий над документом сделали неизменяемым, чтобы эпизод нельзя было убрать задним числом.
Запись и журнал Собрали брокер сессий с выдачей одноразовых учёток, запись экрана и команд, теневое копирование выгруженных реестров и неизменяемый журнал. Подняли узел инференса и научили его помечать эпизоды выгрузки клиентской базы, правки лимита и обхода согласования.
Разыгранные сессии Разыграли сессии подрядчика с выгрузкой реестра, правкой лимита и попыткой обойти согласование и посмотрели, какие эпизоды помечены. Непомеченная выгрузка клиентской базы, работа мимо брокера под общей учёткой или запись, которую удалось изменить, считались провалом.
Передача офицерам Отдали брокер сессий, узел инференса, схему неизменяемого журнала, матрицу доступа подрядчиков и инструкцию офицера безопасности; с безопасностью прошли выдачу доступа и разбор сессии. Заказчик сам заводит подрядчика, выдаёт одноразовую учётку и разбирает помеченные эпизоды.
Архитектура
Брокер сессий на Axum сделан единственной точкой входа подрядчика: он выдаёт одноразовую учётную запись из HashiCorp Vault, проксирует SSH и RDP и пишет поток экрана и введённых команд. Записи сессий и теневые копии выгруженных реестров ложатся в MinIO с блокировкой объекта на запись, а метаданные действий над документом в PostgreSQL связаны хешем предыдущей записи; цепочка считается на стороне брокера, поэтому подрядчик, добравшийся до хранилища, не перепишет журнал незаметно. Расшифровка сессии уходит в очередь NATS сервису vLLM внутри контура, который помечает эпизоды выгрузки клиентской базы, правки лимита и обхода согласования. Разбор выполняется после сессии отдельным сервисом, потому что запись экрана не должна ждать инференса; учётные записи и роли подрядчиков ведёт Keycloak, узлы разложены в Kubernetes.
Ассистент по расходам в мобильном приложении с бэкендом инференса
Задача, решение и что передали
Задача
В приложении был список операций, а вопрос «куда ушли деньги» пользователь разбирал сам в выгрузке.
Решение
Собрали бэкенд ассистента и встроили его в приложение: витрина операций с категориями, слой обезличивания перед запросом, пул моделей с очередью и потоковым ответом, кэш типовых вопросов, счётчик обращений на пользователя. В приложении — экран диалога с карточками операций и переходом в историю по каждой ссылке.
Сдано 4
Передали бэкенд ассистента
Сборки мобильных приложений
Слой обезличивания
Схему квот
Как делали
Разбор операций Разобрали, как пользователь сейчас отвечает себе на вопрос «куда ушли деньги» — списком операций и выгрузкой, — и посмотрели, насколько проставленные категории пригодны для ответа. Выписали поля операции, которые нельзя отдавать модели.
Пул и обезличивание Между витриной операций и моделью поставили слой обезличивания, чтобы в запрос уходили категории и суммы, а не реквизиты. Инференс собрали пулом с очередью и потоковым ответом, добавили кэш типовых вопросов и счётчик обращений на пользователя.
Бэкенд и экран Собрали бэкенд ассистента — витрину операций с категориями, слой обезличивания, пул моделей, кэш и квоты — и встроили в приложение экран диалога с карточками операций и переходом в историю по каждой ссылке. Сборки мобильных приложений завели в общий выпуск.
Прогон вопросов Прогнали типовые вопросы на тестовых профилях, проверяя, что каждая карточка операции в ответе открывает именно ту запись в истории, и смотрели, что уходит в модель. Ответ со ссылкой на операцию другого пользователя или реквизит, доехавший до модели, считались провалом и блокировали выпуск.
Передача команде Отдали бэкенд ассистента, сборки мобильных приложений, слой обезличивания, схему квот и документацию API; с командой прошли выпуск версии и разбор обращений. Стартап сам меняет промпты, квоты и категории витрины.
Архитектура
Бэкенд на FastAPI собирает витрину операций с категориями в PostgreSQL и перед обращением к модели прогоняет запрос через слой обезличивания: реквизиты и контрагенты с персональными данными заменяются метками, в промпт уходят категории, периоды и суммы. Ответ отдаётся потоком по SSE, а не одним телом, потому что экран диалога показывает текст по мере генерации и приложение не держит запрос открытым в ожидании готового ответа. Пул моделей vLLM стоит за очередью RabbitMQ с квотой на пользователя, типовые вопросы отвечаются из Redis по ключу «нормализованный вопрос + период», поэтому одинаковые формулировки не запускают генерацию заново. Мобильные клиенты на Kotlin и Swift получают карточки операций со ссылками, переход по ссылке открывает историю из той же витрины, узлы разложены в Kubernetes.
Собственная система финансирования поставок с агентной проверкой реестров
Задача, решение и что передали
Задача
Реестры поставок принимали в почте, сверку с накладными и лимитом клиента вели в таблицах.
Решение
Собрали учётный контур с нуля вместо таблиц: карточка дебитора с лимитами, реестр уступленных поставок, статусы верификации, график погашения, журнал уведомлений сторонам. Агенты принимают присланные реестры и первичные документы, сверяют позиции с накладными и подписями, отмечают расхождения по количеству, номеру и отгрузке, заводят поставку в реестр. Спорное уходит верификатору карточкой с подсветкой расхождения и ссылкой на исходный файл.
Сдано 4
Переданы модель лимитов и статусов
Регламент верификации поставки
Журнал расхождений по реестрам
Инструкции верификатора
Как делали
Обследование реестров Разобрали почтовый ящик, куда падали реестры поставок, собрали образцы форматов от разных клиентов и посмотрели таблицы сверки лимитов, поговорив с верификаторами и риск-менеджером. Выяснили, какие расхождения между реестром и накладной вообще встречаются: количество, номер отгрузки, отсутствие подписи.
Архитектура учёта За единственный источник доступного финансирования взяли карточку дебитора с лимитом; реестр уступленных поставок и график погашения свели в одну модель, статусы верификации описали отдельным набором переходов. Агентный разбор развели с учётным ядром через очередь, чтобы спорный документ не блокировал заведение остальных позиций реестра.
Сборка контура Сначала собрали учётное ядро — дебиторы, лимиты, поставки, график погашения и журнал уведомлений сторонам, — потом навесили агентов приёма реестров и сверки с накладными и подписями. На стенде гоняли обезличенные копии реестров прошлых периодов, чтобы отладить сопоставление позиций и подсветку расхождения в карточке верификатора.
Проверка сверки Прогнали реестры с намеренно испорченными позициями — расхождение по количеству, чужой номер отгрузки, накладная без подписи — и сверили решение агента с разбором верификатора. Провалом считали поставку, заведённую в реестр с расхождением и без карточки верификатору, а также заведение поставки сверх лимита дебитора.
Передача верификаторам Передали модель лимитов и статусов, регламент верификации поставки, журнал расхождений по реестрам и инструкции верификатора, разобрав на живых реестрах чтение подсветки и переход к исходному файлу. Верификаторы сами заводят дебитора, правят лимит и меняют правила разбора расхождений.
Архитектура
Контур собран из приёмника почты, разборщика реестров, ядра учёта и кабинета верификатора. Приёмник кладёт письмо и вложения в объектное хранилище, а в очередь отправляет задачу с ключом по хэшу вложения: разбор реестра на много строк идёт минутами, поэтому он вынесен из приёмного узла, а идемпотентный ключ гасит повторную присылку того же реестра. Карточки дебиторов, лимиты, статусы верификации и график погашения живут в PostgreSQL, уступка проверяет лимит в транзакции с блокировкой строки карточки, иначе две параллельно заведённые поставки пробили бы лимит. Маршрут верификации ведёт движок процессов: спорная позиция встаёт задачей верификатору со ссылкой на исходный файл, а при падении воркера задача возвращается в очередь и берётся другим.
Подписная платформа кредитного скоринга для внешних кредиторов
Задача, решение и что передали
Задача
Скоринговая логика жила в ноутбуках аналитиков и одном общем скрипте, а каждый новый кредитор подключался ручной выгрузкой и пересчётом.
Решение
Платформа собрана из витрины признаков, реестра моделей с версиями, сервиса решений по заявке и кабинета кредитора с лимитами, тарифом и отчётами. Каждое решение сохраняется вместе с версией модели, значениями признаков и вкладом факторов; новая версия сначала идёт теневым прогоном на живом потоке и только затем переключается. Разграничение по арендаторам изолирует данные, пороги и правила каждого кредитора.
Сдано 4
Платформа скоринга
Витрина признаков
Реестр версий моделей
Журнал решений с объяснениями
Как делали
Обследование скоринга Собрали скоринговую логику из ноутбуков аналитиков и общего скрипта, выписали признаки и пороги и нашли расхождения между версиями. Разобрали, как подключался новый кредитор — ручной выгрузкой и пересчётом — и что у каждого кредитора свои пороги и правила.
Архитектура платформы Признаки вынесли в общую витрину, модели — в реестр с версиями, а решение по заявке сделали службой, сохраняющей версию модели, значения признаков и вклад факторов. Разграничение по арендаторам заложили в основу, чтобы данные и пороги кредиторов не пересекались, а переключение версии пустили через теневой прогон на живом потоке.
Разработка сервисов Сначала витрина признаков и реестр моделей, затем сервис решений с сохранением объяснения, затем кабинет кредитора с лимитами, тарифом и отчётами. Теневой режим собирали до кабинета: новая версия считалась параллельно на живом потоке, а решение отдавала действующая.
Проверка решений Пересчитали архивные заявки старым скриптом и новой службой, прогнали теневой режим на живом потоке и проверили изоляцию запросами от имени одного кредитора к данным другого. Провалом считали решение, сохранённое без версии модели и вклада факторов, и любой доступ кредитора к заявкам чужого арендатора.
Передача аналитикам Передали платформу скоринга, витрину признаков, реестр версий моделей, журнал решений с объяснениями и кабинет кредитора. Аналитики сами публикуют версию модели, гоняют её теневым прогоном и переключают, новый кредитор заводится без ручной выгрузки.
Архитектура
Сервис решений отвечает кредитору синхронно: читает признаки из онлайн-части витрины в Redis, вызывает версию модели из реестра и возвращает решение с вкладом факторов. Решение сохраняется вместе с версией модели и значениями признаков на момент запроса — признаки перезаписываются, и по логам их потом не восстановить, поэтому они пишутся в само решение, а не пересчитываются при разборе. Офлайн-часть витрины собирается из потока событий в Kafka, кандидатная версия сперва идёт теневым прогоном на копии живого потока: её ответы ложатся в журнал, но наружу не отдаются. Арендаторы разведены схемами PostgreSQL с отдельными ролями, пороги, лимиты и тариф кредитора лежат в его схеме, а не в общей таблице с колонкой арендатора.
Классификатор конфиденциальных документов в постоянном контроле утечек
Задача, решение и что передали
Задача
Пилот классификатора работал на выгрузке за прошлый период и показывал списки в блокноте аналитика, а живой почтовый и файловый трафик никто не разбирал.
Решение
Модель поставлена в разрыв почтового потока и на агенты рабочих станций: контентный классификатор помечает тела писем и вложения по категориям сведений, срабатывание уходит офицеру безопасности вместе с теневой копией отправления. Контур собран из шлюза почты, агентов съёмных носителей и печати, хранилища теневых копий с ограниченным доступом и журнала действий с документом. Пороги по категориям и списки разрешённых адресатов вынесены в конфигурацию, подтверждённые срабатывания уходят в переобучение.
Сдано 4
Классификатор категорий сведений
Шлюз разбора почты
Агенты носителей и печати
Хранилище теневых копий
Как делали
Обследование пилота Посмотрели, на чём работал пилот — выгрузка за прошлый период и списки в блокноте аналитика — и сверили его категории сведений с тем, что реально ходит в почте и на рабочих станциях. Собрали образцы документов по категориям и выяснили, какие адресаты считаются разрешёнными.
Архитектура контура Классификатор поставили в разрыв почтового потока и на агенты рабочих станций, теневые копии отправлений вынесли в хранилище с ограниченным доступом. Пороги по категориям и списки разрешённых адресатов вынесли в конфигурацию, чтобы менять их без пересборки, а подтверждённые срабатывания завели в переобучение.
Разработка шлюза Собрали шлюз почты и агенты съёмных носителей и печати, затем хранилище теневых копий и журнал действий с документом, затем поставили модель на разбор тел писем и вложений. На стенде гоняли копию почтового потока и отправку на носители, пока пилотная модель ещё считалась в стороне от боевого контура.
Проверка срабатываний Прогоняли документы каждой категории через почту, печать и съёмный носитель, включая переименованные файлы и вложения в архивах, и сверяли метки с ручной разметкой. Провалом считали документ конфиденциальной категории, ушедший внешнему адресату без срабатывания и без теневой копии.
Передача офицеру Передали классификатор категорий сведений, шлюз разбора почты, агенты носителей и печати, хранилище теневых копий и журнал срабатываний и решений. Офицер безопасности сам меняет пороги по категориям, ведёт списки разрешённых адресатов и отправляет подтверждённые срабатывания в переобучение.
Архитектура
Классификатор стоит в разрыве почтового потока: шлюз удерживает письмо и спрашивает сервис по ICAP с бюджетом ответа, при просрочке применяется правило по умолчанию, чтобы очередь почты не вставала. Инференс вынесен отдельным сервисом на ONNX Runtime — веса, пороги по категориям и списки разрешённых адресатов лежат в конфигурации и меняются без пересборки шлюза и агентов. Разбор архивов и крупных вложений идёт асинхронно через Kafka, теневая копия отправления кладётся в MinIO с ограниченным доступом, карточка срабатывания и журнал действий с документом — в PostgreSQL. Агенты съёмных носителей и печати решают по локальному кэшу политик и досылают события, поэтому недоступность сервера не блокирует рабочую станцию, а подтверждённые срабатывания копятся отдельным набором для переобучения.
Вывод модели аномального доступа к клиентским досье в продуктив
Задача, решение и что передали
Задача
Пилот выявления аномального доступа считался по выгрузке логов раз в период, а действия администраторов и подрядчиков с клиентскими досье сводились вручную.
Решение
События сведены в один поток: журналы прикладных систем, файлового хранилища, сессии привилегированного доступа через шлюз с записью экрана и выгрузки отчётов. Модель строит профиль обращения к досье по роли, времени, объёму и составу просмотренных полей и помечает отклонения; помеченная сессия открывается офицеру безопасности вместе с полной записью действий. Подрядчики работают через отдельные учётные записи с ограниченным сроком, каждая операция с документом ложится в неизменяемый журнал.
Сдано 4
Модель профилей доступа
Шлюз привилегированных сессий с записью
Неизменяемый журнал операций с документами
Витрина отклонений
Как делали
Обследование журналов Собрали, откуда берутся события — журналы прикладных систем, файловое хранилище, сессии администраторов и подрядчиков — и увидели, что пилот считался по периодической выгрузке. Разобрали, кто и зачем открывает клиентские досье, какие поля смотрит по роли и как раньше сводили действия вручную.
Архитектура потока События свели в один поток вместо разрозненных выгрузок, привилегированный доступ завернули в шлюз с записью экрана, операции с документами — в неизменяемый журнал. Модель строит профиль обращения к досье по роли, времени, объёму и составу просмотренных полей, а помеченная сессия открывается офицеру безопасности вместе с записью действий.
Разработка витрины Сначала сбор и приведение событий в общий поток, затем шлюз привилегированных сессий с записью и неизменяемый журнал операций, затем витрина отклонений. Профили доступа считали на стенде по историческим журналам, пока боевой поток шёл только на сбор.
Проверка отклонений Прогнали архивные журналы и разобранные ранее случаи, смоделировали массовую выгрузку досье под учётной записью подрядчика и просмотр полей, нетипичных для роли. Провалом считали отклонение, помеченное без доступной записи действий, и операцию с документом, отсутствующую в неизменяемом журнале.
Передача безопасности Передали модель профилей доступа, шлюз привилегированных сессий с записью, неизменяемый журнал операций с документами, витрину отклонений и регламент разбора. Служба безопасности сама заводит подрядчику запись с ограниченным сроком и ведёт разбор помеченных сессий.
Архитектура
Журналы прикладных систем, файлового хранилища и шлюза привилегированных сессий сводятся в один поток Kafka, нормализатор раскладывает события в общую схему. Профиль обращения к досье — роль, время, объём и состав просмотренных полей — считается оконными агрегатами в ClickHouse: это выборки по нескольким колонкам за длинный период, под них колоночное хранилище, а карточки отклонений и решения разборщика остаются в PostgreSQL. Записи экрана и выгруженные отчёты лежат в объектном хранилище, в карточке отклонения хранится ссылка и смещение по времени, поэтому офицер открывает сессию с нужного места. Подрядчики входят через Keycloak учётными записями с ограниченным сроком, операции с документом пишутся append-only с цепочкой хэшей, витрина отклонений и дежурные панели собраны в Grafana.
Вывод антифрод-скоринга операций в боевой поток биржи
Задача, решение и что передали
Задача
Проверка операций держалась на наборе ручных фильтров, а обученная на истории модель жила в тетради аналитика и на живые заявки не смотрела.
Решение
Скоринг поставлен в синхронный разрыв: витрина признаков считается на потоке событий, модель отвечает за отведённое время, при просрочке решение отдаёт правило по умолчанию. Собран контур эксплуатации — теневой режим на живом трафике до переключения, доля потока без модели для сравнения, мониторинг сдвига признаков и очередь ручного разбора с возвратом разметки. Пороги разведены по сегментам операций и меняются без пересборки сервиса.
Сдано 4
Сервис скоринга операций
Витрина признаков реального времени
Мониторинг сдвига признаков
Очередь ручного разбора
Как делали
Обследование фильтров Разобрали набор ручных фильтров, по которым проверялись операции, и модель из тетради аналитика — какие признаки она берёт и откуда их считать на живом потоке. Определили, сколько времени у решения есть в синхронном разрыве, и чем сегменты операций различаются по порогам.
Архитектура скоринга Скоринг поставили в синхронный разрыв, витрину признаков перевели на расчёт по потоку событий, а на просрочку заложили решение по умолчанию из правила. Пороги заранее развели по сегментам операций и вынесли из кода, часть потока оставили без модели для сравнения.
Разработка сервиса Сначала витрина признаков реального времени, потом служба скоринга с ограничением времени ответа и правилом по умолчанию, потом очередь ручного разбора с возвратом разметки. Теневой режим на живом трафике включили до переключения решения на модель, рядом собрали мониторинг сдвига признаков.
Проверка на потоке Сравнивали решения модели и действующих фильтров в теневом режиме, гоняли нагрузку на витрину признаков и намеренно задерживали ответ модели. Провалом считали операцию, задержанную дольше отведённого времени вместо решения по умолчанию, и расхождение значения признака в витрине с событием потока.
Передача аналитикам Передали сервис скоринга операций, витрину признаков реального времени, мониторинг сдвига признаков, очередь ручного разбора и регламент смены порогов. Аналитики сами меняют пороги по сегментам, ведут очередь разбора и возвращают разметку в обучение.
Архитектура
Сервис операций вызывает скоринг синхронно с бюджетом ответа: онлайн-признаки лежат в Redis, их пишет потребитель потока событий из Kafka, и если сервис не уложился, решение отдаёт правило по умолчанию с отметкой просрочки. Инференс держится отдельным сервисом на ONNX Runtime, пороги по сегментам операций читаются из конфигурации с горячей перезагрузкой, потому что смена порога не должна ждать релиза сервиса. Кандидатная версия сначала идёт теневым режимом на живом трафике — её ответ пишется в журнал, но на решение не влияет, — а доля потока без модели отбирается по хэшу идентификатора операции, чтобы одна операция всегда попадала в одну ветку. Витрина признаков и мониторинг их сдвига считаются в ClickHouse, помеченные операции встают в очередь ручного разбора, разметка разборщика возвращается в обучающий набор.
Контроль привилегированного доступа подрядчиков к учётным контурам
Задача, решение и что передали
Задача
Подрядчики и интеграторы работали в продуктивных базах под общими административными учётными записями, и связать изменение реестра с человеком было нечем.
Решение
Развернули шлюз привилегированного доступа: вход через брокер сессий, выдача прав по заявке на срок, хранение паролей в сейфе с ротацией, запись сессий SSH и RDP с индексом команд. В прикладном контуре добавили журналирование действий с реестрами уступки и документами поставки: чтение, выгрузка и смена статуса пишутся в неизменяемый журнал с идентификатором сессии, доступ подрядчиков к интеграционной шине разведён по отдельным техническим учётным записям с ограничением очередей.
Сдано 4
Схема брокера сессий и ролей
Регламент выдачи временных прав
Каталог технических учётных записей
Архив записей сессий
Как делали
Обследование учёток Сняли список общих административных записей, под которыми подрядчики и интеграторы работали в продуктивных базах, и попробовали связать изменения реестров с людьми — связать было нечем. Разобрали, кто из подрядчиков к каким очередям интеграционной шины обращается и какие действия с реестрами уступки журналируются.
Архитектура брокера Вход перевели на брокер сессий с выдачей прав по заявке на срок, пароли убрали в сейф с ротацией, сессии SSH и RDP пишутся с индексом команд. В прикладном контуре завели журналирование действий с реестрами уступки и документами поставки с привязкой к идентификатору сессии, подрядчикам на шине выделили отдельные технические записи с ограничением очередей.
Разработка журналирования Собрали брокер сессий и сейф паролей с ротацией, затем запись сессий с индексом команд, затем журналирование чтения, выгрузки и смены статуса в прикладном контуре. Разведение технических учётных записей по очередям шины делали последним, на стенде гоняли типовые работы сопровождения.
Проверка сессий Заходили под подрядной заявкой и пытались продлить доступ после срока, выгружали реестр уступки и искали действие в журнале по идентификатору сессии, проверяли обращение технической записи к чужой очереди. Провалом считали действие с реестром, которое не сводится к конкретной сессии и человеку, и доступ, оставшийся после истечения заявки.
Передача сопровождению Передали схему брокера сессий и ролей, регламент выдачи временных прав, каталог технических учётных записей, архив записей сессий и порядок разбора обращений подрядчиков. Служба сопровождения сама выдаёт права по заявке и поднимает запись сессии при разборе.
Архитектура
Вход подрядчика идёт через брокер сессий: заявка на срок, аутентификация в Keycloak, пароль технической учётной записи выдаётся из Vault и ротируется после закрытия сессии. Брокер терминирует SSH и RDP и пишет запись сессии в объектное хранилище, а индекс команд ложится в ClickHouse — поиск идёт по подрядчику, окну времени и подстроке команды поверх большого потока строк. Прикладной контур пишет действия с реестрами уступки и документами поставки — чтение, выгрузка, смена статуса — append-only с идентификатором сессии и цепочкой хэшей, поэтому запись нельзя убрать незаметно. Доступ подрядчиков к интеграционной шине разведён отдельными техническими учётками с правами на конкретные очереди в брокере, а не проверками внутри адаптера: иначе отзыв доступа зависел бы от релиза адаптера.
Мультиарендная платформа выставления счетов и приёма платежей
Задача, решение и что передали
Задача
Приём платежей продавали как сервис, но каждый новый клиент разворачивался отдельной копией с ручной правкой конфигурации и своей сверкой.
Решение
Собрали мультиарендное ядро: изоляция данных арендаторов по схемам, каталог тарифных планов и лимитов, самостоятельная регистрация с проверкой реквизитов, кабинет мерчанта со счетами, ссылками на оплату, возвратами и выписками. Платёжный слой держит несколько эквайринговых провайдеров за единым интерфейсом с маршрутизацией и повторами, принимает реестры и выплаты, ведёт автосверку и разбор расхождений, наружу отданы вебхуки, ключи API и песочница как публичный интерфейс продукта.
Сдано 4
Описание мультиарендной модели и изоляции данных
Каталог тарифных планов
Публичная документация API и вебхуков
Регламент подключения эквайринга
Как делали
Обследование установок Разобрали, из чего складывается разворачивание отдельной копии под каждого клиента и что в конфигурации правится руками. Свели, как ведётся сверка у разных клиентов, какие реквизиты проверяются при подключении и что реально требуется мерчанту в кабинете.
Архитектура арендаторов Перешли на общее мультиарендное ядро с изоляцией данных по схемам вместо копии на клиента, тарифные планы и лимиты вынесли в каталог. Несколько эквайринговых провайдеров спрятали за единым интерфейсом с маршрутизацией и повторами, наружу вынесли вебхуки, ключи API и песочницу как публичный интерфейс продукта.
Разработка ядра Сначала мультиарендное ядро и изоляция по схемам, затем самостоятельная регистрация с проверкой реквизитов и кабинет мерчанта со счетами, ссылками на оплату, возвратами и выписками. Платёжный слой с маршрутизацией, приёмом реестров и выплат и автосверкой собирали следом, песочницу и документацию API — последними.
Проверка платежей Гоняли сквозные оплаты и возвраты через разных провайдеров, роняли провайдера на середине платежа, принимали реестры с расхождениями и обращались от имени одного арендатора к данным другого. Провалом считали доступ к счетам чужого арендатора и платёж, не сведённый автосверкой и не попавший в разбор расхождений.
Передача интеграторам Передали описание мультиарендной модели и изоляции данных, каталог тарифных планов, публичную документацию API и вебхуков, регламент подключения эквайринга и песочницу для интеграторов. Команда сама заводит тарифный план и подключает провайдера, мерчанты регистрируются без ручной правки конфигурации.
Архитектура
Ядро одно, данные арендатора лежат в отдельной схеме PostgreSQL со своей ролью: это даёт раздельные права и раздельный дамп, чего не даёт общая таблица с колонкой арендатора, где ошибка в условии выборки открывает чужие счета. Платёжный слой держит несколько эквайринговых провайдеров за одним внутренним интерфейсом с маршрутизацией и повторами, каждая операция идёт с идемпотентным ключом счёта, поэтому повтор после таймаута провайдера не создаёт второй платёж. Реестры и выплаты принимаются пакетно, автосверка разводит их со счетами и складывает расхождения в отдельный реестр, события статусов расходятся через Kafka в кабинет мерчанта и вебхуки. Наружу отданы подписанные вебхуки с повторной доставкой, ключи API и песочница отдельным контуром с теми же контрактами OpenAPI, кабинет закрыт Keycloak, регистрация создаёт схему арендатора прогоном шаблона миграций.
Свой документооборот по реестрам поставок с подписанием и архивом
Задача, решение и что передали
Задача
Реестры поставок приходили почтой в файлах, верификацию подтверждали письмом от дебитора, а комплект по каждой поставке собирали в папке на общем диске.
Решение
Документооборот построен с нуля: маршруты согласования по типам документов; реестр поставок с построчной сверкой сумм и сроков; карточка поставки со связями на договор, накладную и акт верификации; подписание с отметкой времени и проверкой сертификата; версии с разницей между редакциями; архив с поиском по реквизитам и сроками хранения; разграничение доступа по клиентам и дебиторам. Статус финансирования меняется только документом маршрута, правки статуса руками в интерфейсе нет.
Сдано 4
Исходный код маршрутов
Схема связей документов поставки
Архив с политикой хранения
Регламент подписания и отметок времени
Как делали
Обследование комплектов Разобрали, как реестры поставок приходили файлами почтой и чем подтверждалась верификация — письмом от дебитора, — и посмотрели папки на общем диске, где собирался комплект по поставке. С верификаторами выписали, какие документы связаны с поставкой — договор, накладная, акт верификации — и по каким реквизитам комплект потом ищут.
Архитектура маршрутов Статус финансирования привязали к документу маршрута, ручной правки статуса в интерфейсе не оставили. Маршруты согласования развели по типам документов, карточку поставки сделали узлом связей на договор, накладную и акт верификации, архив завели с поиском по реквизитам и сроками хранения, доступ разграничили по клиентам и дебиторам.
Разработка подписания Сначала маршруты согласования и карточка поставки со связями, потом реестр поставок с построчной сверкой сумм и сроков, потом подписание с отметкой времени и проверкой сертификата. Версии с разницей между редакциями и архив с политикой хранения собирали следом, на стенде гоняли реестры прошлых периодов.
Проверка реестров Прогоняли реестры с расхождением сумм и сроков построчно, подписывали документы просроченным и отозванным сертификатом, пробовали сменить статус финансирования мимо маршрута. Провалом считали статус финансирования, изменённый без документа маршрута, и принятую подпись с недействительным сертификатом.
Передача верификаторам Передали исходный код маршрутов, схему связей документов поставки, архив с политикой хранения, регламент подписания и отметок времени и инструкции верификатора. Верификаторы сами ведут маршруты по типам документов и поднимают комплект по поставке из архива по реквизитам.
Архитектура
Маршруты согласования исполняет отдельный движок процессов: статус документа меняется только шагом маршрута, а API не принимает смену статуса напрямую — иначе финансирование можно было бы открыть мимо верификации. Файлы, редакции и подписи лежат в объектном хранилище, метаданные, связи «договор — накладная — акт верификации» и построчная сверка сумм и сроков по реестру — в PostgreSQL. Подписание идёт через криптопровайдер с отметкой времени, сертификат проверяется при приёме и результат проверки сохраняется рядом с подписью, чтобы позднее истечение сертификата не ломало картину на момент подписания. Архив ищется по реквизитам через OpenSearch, сроки хранения ведутся политикой на объектах, разграничение по клиентам и дебиторам наложено на выборку, а не на интерфейс.
Стек
Go · chi · PostgreSQL · MinIO · RabbitMQ · Kubernetes · Temporal · КриптоПро · OpenSearch
Кабинет клиента с контролем каналов выгрузки реестров поставок
Задача, решение и что передали
Задача
Реестры поставок и дебиторки ходили почтой и в мессенджерах, а уход клиентской базы наружу замечали постфактум.
Решение
В кабинете клиента реестры и уведомления об уступке открываются онлайн, а выгрузка идёт только через контролируемый канал: подпись файла, ограничение объёма строк, персональная маркировка выгрузки. Почтовый и файловый трафик разбирается на границе по правилам с номерами договоров, реквизитами дебиторов и шаблонами реестров, подозрительное отправление уходит на разбор офицеру. Просмотр, экспорт и печать реестра пишутся в журнал операций с привязкой к клиенту и договору.
Сдано 4
Передали кабинет
Правила разбора трафика
Журнал операций с реестрами
Схему маркировки выгрузок
Как делали
Обследование потоков Разобрали, как реестры поставок и уведомления об уступке доходили до клиента: почтовые вложения, мессенджеры менеджеров, ручные выгрузки из учётной системы. Собрали образцы реестров, номера договоров и признаки дебиторов и увидели, что выгрузка ничем не ограничена и не привязана к сотруднику.
Архитектура кабинета Реестры и уведомления об уступке открываются в кабинете, а наружу уходят только контролируемым каналом: подпись файла, ограничение объёма строк, персональная маркировка выгрузки. Разбор почтового и файлового трафика вынесли на границу отдельным узлом с правилами по номерам договоров и реквизитам дебиторов, подозрительное отправление уводится офицеру, а не отбрасывается молча.
Разработка контроля Собрали кабинет с онлайн-просмотром реестра и уведомления об уступке, затем контролируемую выгрузку с подписью и маркировкой, затем правила разбора трафика и очередь разбора. Журнал просмотра, экспорта и печати с привязкой к клиенту и договору подключали последним, на стенде с обезличенными реестрами.
Проверка каналов Гоняли шаблоны реестров через почтовый и файловый канал, резали реестр на части и переименовывали файл, проверяли, что маркировка выгрузки указывает на конкретного сотрудника. Работу не приняли бы, если бы выгруженный реестр не сводился к сотруднику и договору или если бы отправление с реквизитами дебитора прошло границу без записи.
Передача кабинета Передали кабинет, правила разбора трафика, журнал операций с реестрами, схему маркировки выгрузок и регламент разбора инцидентов. Офицер безопасности правит правила и разбирает очередь сам, менеджер выдаёт клиенту доступ к реестрам своего договора.
Архитектура
Кабинет на Gin отдаёт реестры онлайн, а экспорт вынесен в отдельный сервис выгрузки: он подписывает файл, режет объём строк по условиям договора и вшивает персональную маркировку. На границе стоят два перехватчика — ICAP-сервис у веб-прокси и milter у почтового релея, и оба обращаются к одному сервису правил по gRPC, чтобы правило с номерами договоров, реквизитами дебиторов и шаблонами реестров описывалось один раз и не расходилось между каналами. Просмотры, экспорт и печать пишутся потоком в Kafka и оседают в ClickHouse: журнал однороден, пишется пачками и читается агрегатами по клиенту и договору за период, поэтому под него взято колоночное хранилище, а не строчные таблицы кабинета. Договоры, реестры и карточки клиентов остаются в PostgreSQL, подозрительное отправление уходит в очередь офицера, внешний контур терминирует Nginx.
Собственная тарифная и расчётная система финансирования поставок
Задача, решение и что передали
Задача
Комиссии и графики по уступленным поставкам считались в зарубежном модуле поверх табличных надстроек, а новая тарифная схема требовала внешнего разработчика.
Решение
Сделали систему на PostgreSQL под Linux: реестр поставок и дебиторов, конструктор тарифных планов со ставками, периодами и порогами, расчётное ядро с пересчётом задним числом, лимиты на клиента и на дебитора, выгрузку проводок в бухгалтерию. Каждое начисление хранит входные параметры и версию тарифа, поэтому расчёт поднимается из журнала и прогоняется заново. Кабинет клиента и внутренний интерфейс обращаются к одному расчётному ядру.
Сдано 4
Передали исходный код
Схему базы и миграции
Описание конструктора тарифов
Набор проверочных расчётов
Как делали
Обследование расчётов Разобрали расчёт комиссий и графиков в зарубежном модуле и табличных надстройках поверх него, собрали действующие тарифные схемы со ставками, периодами и порогами и случаи пересчёта задним числом. Поговорили с расчётчиками и риск-менеджерами о лимитах на клиента и на дебитора.
Архитектура тарифов Тарифные планы увели в конструктор со ставками, периодами и порогами, а расчётное ядро сделали единственным местом начисления — к нему обращаются и кабинет клиента, и внутренний интерфейс. Каждое начисление хранит входные параметры и версию тарифа, поэтому пересчёт задним числом поднимается из журнала, а не собирается заново руками.
Разработка ядра Сначала реестр поставок и дебиторов и схема хранения начислений с версиями тарифа, затем конструктор тарифных планов, затем расчётное ядро и лимиты на клиента и на дебитора. Выгрузку проводок в бухгалтерию и пересчёт задним числом отрабатывали на стенде с перенесённой историей поставок.
Проверка начислений Прогнали историю уступленных поставок через новое ядро и сверили начисления со старым модулем, отдельно гоняли смену тарифа задним числом, закрытие периода и упор в лимит дебитора. Провалом считали начисление, которое не воспроизводится из сохранённых параметров и версии тарифа, и расхождение выгруженных проводок с бухгалтерией.
Передача системы Отдали исходный код, схему базы и миграции, описание конструктора тарифов, набор проверочных расчётов и регламент закрытия периода. Расчётчик сам собирает новый тарифный план в конструкторе и прогоняет его на проверочных расчётах до включения.
Архитектура
Расчётное ядро вынесено в отдельный сервис ASP.NET Core, и кабинет клиента, и внутренний интерфейс вызывают его одним контрактом — при двух реализациях формулы расходились бы уже на первой правке тарифа. Каждое начисление хранится строкой журнала с входными параметрами, ссылкой на поставку и версией тарифного плана, поэтому расчёт поднимается из журнала и прогоняется заново, а действующая версия плана выбирается по дате операции. Пересчёт задним числом идёт фоновыми задачами Hangfire, поставленными через RabbitMQ: перепроигрывание периода по клиенту занимает минуты и не должно держать веб-запрос открытым. Реестр поставок, дебиторов, лимитов и выгрузка проводок лежат в PostgreSQL, Redis держит срез лимитов на клиента и дебитора, внешний контур терминирует Nginx, поставка — Docker на Linux.
Журнал действий с документами и контроль привилегированного доступа
Задача, решение и что передали
Задача
Подрядчики и администраторы заходили в аналитические системы под общими учётными записями, а работу с документами восстанавливали по разрозненным логам.
Решение
Построили контур контроля на отечественных ОС: шлюз привилегированного доступа с записью сессий и разовыми учётными данными, агент файлового хранилища с фиксацией открытия, копирования и печати документа, сервис журналирования с неизменяемым хранением и синхронизацией времени. Права вынесены в единую матрицу уровней конфиденциальности, доступ подрядчика выдаётся на срок задачи и снимается по его окончании. События уходят в систему мониторинга безопасности единым форматом.
Сдано 4
Передали исходный код шлюза и агента
Матрицу прав и уровней конфиденциальности
Формат событий для мониторинга
Регламент выдачи доступа подрядчику
Как делали
Обследование доступа Собрали, кто и как заходит в аналитические системы: общие административные учётные записи подрядчиков, постоянные пароли, разрозненные логи файлового хранилища. Сверили это с договорами подряда и составом документов по уровням конфиденциальности и увидели, что действие с документом ни к кому не привязывается.
Архитектура контроля Подключения увели на шлюз привилегированного доступа с разовыми учётными данными и записью сессий, работу с документами накрыли агентом файлового хранилища, фиксирующим открытие, копирование и печать. Права свели в единую матрицу уровней конфиденциальности, журналирование сделали неизменяемым с синхронизацией времени, события отдали в мониторинг безопасности единым форматом.
Разработка шлюза Сначала шлюз с разовыми учётными данными и записью сессий под отечественные ОС, затем агент хранилища и сервис журналирования, затем матрица уровней и выдача доступа подрядчику на срок задачи. Единый формат событий и снятие доступа по окончании задачи отрабатывали на стенде с копией документов.
Проверка привязки Заводили подрядчика по заявке и пробовали дотянуться до документов вне его уровня конфиденциальности, работали мимо шлюза напрямую, сверяли записи сессий с журналом действий по времени. Работу не приняли бы, если бы действие с документом не сводилось к конкретному человеку и заявке или если бы доступ оставался живым после закрытия задачи.
Передача контура Отдали исходный код шлюза и агента, матрицу прав и уровней конфиденциальности, формат событий для мониторинга, регламент выдачи доступа подрядчику и инструкции администратора. Администратор сам выдаёт и снимает доступ по заявке и поднимает запись сессии при разборе инцидента.
Архитектура
Шлюз привилегированного доступа терминирует SSH и RDP, берёт разовые учётные данные из хранилища секретов и пишет запись сессии, поэтому подрядчик не получает постоянного пароля целевой системы. Агент файлового хранилища фиксирует открытие, копирование и печать документа и шлёт события в сервис журналирования вместе с событиями шлюза через Kafka. Журнал пишется только добавлением, каждая запись несёт хеш предыдущей и метку времени от общего источника chrony: без единого времени события шлюза и агента не выстраиваются в один порядок, и разбор инцидента распадается на несопоставимые куски. Матрица уровней конфиденциальности, заявки и сроки доступа лежат в PostgreSQL, поток событий — в ClickHouse, вход и роли ведёт Keycloak, выгрузка в мониторинг безопасности идёт по Syslog в формате CEF; сервис на Spring Boot, раскатка Ansible.
Собственный документооборот с журналом действий и контролем ИИ-разбора
Задача, решение и что передали
Задача
Заявки на финансирование и реестры поставок ходили по почте и сетевым папкам, а кто открывал, выгружал и пересылал документ, восстанавливали по памяти сотрудников.
Решение
Построили документооборот с нуля: реестр документов с версиями, маршруты согласования, объектное хранилище с неизменяемыми записями, служба прав по контрагентам и сервис ИИ-разбора накладных и договоров уступки. Открытие, выгрузка, печать, пересылка и каждое обращение к модели пишутся отдельной записью с идентификатором пользователя, документа и фрагмента, попавшего в запрос. Права проверяются до передачи текста модели: фрагмент вне доступа сотрудника в контекст запроса не собирается.
Сдано 4
Система документооборота
Журнал действий с документами
Политики доступа к фрагментам
Регламент разбора инцидентов
Как делали
Обследование документооборота Проследили путь заявки на финансирование и реестра поставок по почте и сетевым папкам, собрали образцы накладных и договоров уступки и опросили сотрудников, кто открывал и пересылал документ. Увидели, что версии документа расходятся по папкам, а маршрут согласования нигде не записан.
Архитектура прав Реестр документов с версиями и маршрутами согласования, объектное хранилище с неизменяемыми записями, права по контрагентам отдельной службой, сервис ИИ-разбора накладных и договоров уступки — за этой службой. Права проверяются до передачи текста модели: фрагмент вне доступа сотрудника в контекст запроса не собирается.
Разработка реестра Сначала реестр документов с версиями и хранилище, затем служба прав по контрагентам и маршруты согласования, затем сервис разбора накладных и договоров уступки. Запись открытия, выгрузки, печати, пересылки и каждого обращения к модели с идентификатором пользователя, документа и фрагмента собирали на стенде с обезличенными реестрами.
Проверка запросов Запрашивали разбор документов чужого контрагента от лица сотрудника без прав, сверяли состав фрагментов в журнале с тем, что реально ушло в модель, гоняли согласование по маршруту с возвратом. Провалом считали фрагмент вне доступа сотрудника, попавший в контекст запроса, и любое действие с документом, не отражённое отдельной записью в журнале.
Передача системы Передали систему документооборота, журнал действий с документами, политики доступа к фрагментам, регламент разбора инцидентов и руководство администратора. Администратор сам заводит маршрут согласования и правит политики доступа, безопасность поднимает по журналу состав любого запроса к модели.
Архитектура
Реестр документов, версии и маршруты согласования лежат в PostgreSQL, тела документов — в MinIO с блокировкой объекта, поиск по содержимому ведёт OpenSearch с фильтром по правам контрагента. Сервис ИИ-разбора накладных и договоров уступки вынесен отдельно и работает на vLLM в собственном пуле узлов, задания приходят через Kafka — инференс занимает видеопамять надолго, и его ритм не совпадает с ритмом веб-запросов кабинета. Права проверяются до сборки контекста: служба решений вызывается на каждом фрагменте перед передачей текста модели, потому что фильтрация ответа постфактум означала бы, что запрещённый фрагмент уже покинул контур. Открытие, выгрузка, печать, пересылка и каждое обращение к модели пишутся отдельной записью с идентификаторами пользователя, документа и фрагмента; вход и роли ведёт Keycloak, развёртывание — Kubernetes.
Контур привилегированного доступа подрядчиков к ИИ-сервисам и данным
Задача, решение и что передали
Задача
Подрядчики по моделям и аналитике работали под общими административными учётками с постоянными ключами, и связать действие в системе с конкретным человеком получалось не всегда.
Решение
Развернули контур привилегированного доступа: хранилище секретов с выдачей ключей на сессию, брокер подключений с записью действий, отдельная учётная запись на каждого подрядчика и подтверждение второго сотрудника на операции с боевыми данными и весами моделей. Доступ к обучающим выборкам и хранилищу промптов выдаётся заявкой с целью и сроком, по истечении срока учётная запись отключается, ключ отзывается. Действия в консолях и обращения к API моделей пишутся в общий журнал и сопоставляются с заявкой, по которой доступ выдавался.
Сдано 4
Контур привилегированного доступа
Заявки на доступ со сроками
Записи сессий
Сопоставление действий с заявками
Как делали
Обследование учёток Собрали, чем пользуются подрядчики по моделям и аналитике: общие административные учётные записи, постоянные ключи, доступ к обучающим выборкам и хранилищу промптов. Сверили это с договорами подряда и увидели, что действие в системе не всегда сводится к конкретному человеку.
Архитектура доступа Секреты увели в хранилище с выдачей ключей на сессию, подключения — на брокер с записью действий, на каждого подрядчика завели отдельную учётную запись. Операции с боевыми данными и весами моделей закрыли подтверждением второго сотрудника, доступ к обучающим выборкам и хранилищу промптов выдаётся заявкой с целью и сроком.
Разработка брокера Сначала хранилище секретов и выдача ключей на сессию, затем брокер подключений с записью действий, затем заявки с целью и сроком с отключением учётной записи и отзывом ключа по их истечении. Сопоставление действий в консолях и обращений к API моделей с заявкой собирали последним, на стенде с тестовыми выборками.
Проверка сессий Пробовали подключиться постоянным ключом мимо брокера, продолжить работу после истечения заявки, выполнить операцию с весами модели без второго сотрудника и сверяли записи сессий с журналом обращений к API. Провалом считали действие в боевом контуре, которое не сводится к заявке и человеку, и ключ, продолжающий работать после закрытия заявки.
Передача контура Отдали контур привилегированного доступа, заявки на доступ со сроками, записи сессий, сопоставление действий с заявками и регламент допуска подрядчика. Ответственный сам заводит подрядчика, выдаёт доступ по заявке и снимает его по договору.
Архитектура
Брокер подключений Teleport терминирует доступ к консолям и API моделей, выдаёт сертификат на сессию и пишет запись действий, отдельная учётная запись заводится на каждого подрядчика. Постоянные ключи убраны: Vault выдаёт динамические учётные данные со временем жизни, поэтому отзыв доступа сводится к истечению аренды, а не к обходу всех систем руками после закрытия договора. Сервис заявок на FastAPI хранит цель, срок и подтверждение второго сотрудника на операции с боевыми данными и весами моделей, роль выдаётся только после второго согласования, а обучающие выборки и хранилище промптов открываются той же заявкой. Действия из брокера и обращения к API моделей сводятся по идентификатору сессии в ClickHouse и сопоставляются с заявкой, сами заявки и матрица прав лежат в PostgreSQL, события идут через NATS, вход — по OIDC, развёртывание — Kubernetes.
SaaS-платформа проверки ИИ-агентов с тарифами и биллингом подписок
Задача, решение и что передали
Задача
Проверки чужих агентов делали руками по чек-листу в таблицах, и продавать это как сервис было нечем: ни кабинета, ни тарифов, ни отчёта, который клиент может показать своему заказчику.
Решение
Собрали платформу с нуля: кабинет клиента с подключением его агента по ключу, банк враждебных сценариев — подмена инструкций, вынос системного промпта, обход ограничений, утечка персональных данных, — прогон по расписанию, отчёт с воспроизводимыми запросами и биллинг подписок с тарифами по числу прогонов. Прогон фиксируется целиком: версия сценария, версия агента, запрос, ответ, вердикт, поэтому отчёт собирается заново на тех же входах. Данные клиентов разведены по тенантам, свои сценарии клиент держит в закрытом разделе банка.
Сдано 4
Платформа с кабинетом и тарифами
Банк враждебных сценариев
Воспроизводимые отчёты прогонов
Биллинг подписок
Как делали
Обследование проверок Разобрали ручную проверку чужих агентов по чек-листу в таблицах и вытащили из неё сценарии, которые действительно ловили проблемы: подмена инструкций, вынос системного промпта, обход ограничений, утечка персональных данных. Выяснили, чего не хватает для продажи — кабинета, тарифов и отчёта, который клиент покажет своему заказчику.
Архитектура платформы Клиентов развели по тенантам, подключение чужого агента сделали по ключу, банк враждебных сценариев — общим, с закрытым разделом под собственные сценарии клиента. Прогон фиксируется целиком — версия сценария, версия агента, запрос, ответ, вердикт, — чтобы отчёт собирался заново на тех же входах, а биллинг подписок считает тарифы по числу прогонов.
Разработка прогонов Сначала подключение агента по ключу и исполнение сценария с полной фиксацией прогона, затем банк враждебных сценариев и запуск по расписанию, затем отчёт с воспроизводимыми запросами. Кабинет клиента, разделение по тенантам и биллинг подписок с тарифами собирали после, на стенде с подставными агентами.
Проверка воспроизводимости Гоняли банк сценариев по подставным агентам, повторяли прогон из отчёта и сверяли вердикты, проверяли изоляцию тенантов и упор в тариф по числу прогонов. Не приняли бы работу, если бы отчёт не воспроизводился на тех же версиях сценария и агента или если бы клиент увидел прогон или сценарий чужого тенанта.
Передача платформы Передали платформу с кабинетом и тарифами, банк враждебных сценариев, воспроизводимые отчёты прогонов, биллинг подписок и документацию по подключению агента. Команда заказчика сама пополняет банк сценариев, заводит тарифный план и подключает клиента по документации.
Архитектура
Кабинет и API на NestJS ведут тенантов, подключённых агентов и тарифные планы в PostgreSQL, изоляция клиентов сделана политиками на уровне строк по tenant_id — кластер один, а разделение проверяет база, а не текст каждого запроса в коде. Прогон ставится заданием в BullMQ, воркер поднимается отдельным подом и ходит к агенту клиента по его ключу, поэтому длинный прогон по банку враждебных сценариев не занимает веб-процессы и переезжает на другой узел при падении пода. Прогон фиксируется целиком — версия сценария, версия агента, запрос, ответ, вердикт: тела ответов складываются в MinIO, ссылки и вердикты в PostgreSQL, поэтому отчёт собирается заново на тех же входах. Счётчики прогонов по тарифу держит Redis с периодической фиксацией в базе, запуски по расписанию ведёт планировщик очереди, трассировка идёт через OpenTelemetry в Grafana.
Обследование привилегированного доступа подрядчиков к учётным системам
Задача, решение и что передали
Задача
Подрядчики работали под общими административными учётными записями, и после инцидента разобрать, кто и что менял, было не по чему.
Решение
Собрали инвентарь учётных записей с повышенными правами: владелец, система, способ подключения, срок действия, последняя активность. Сопоставили записи с договорами подряда и заявками на доступ, выделили учётки уволенных, общие пароли в скриптах и доступы без основания. Описали целевой контур: хранилище секретов, выдача прав по заявке на срок, запись административных сессий, журналирование действий с документами и выгрузками, отзыв доступа по закрытию договора.
Сдано 4
Инвентарь привилегированных записей
Матрица доступа по договорам
Схема выдачи прав на срок
Требования к записи сессий
Как делали
Обследование учёток Собрали инвентарь учётных записей с повышенными правами — владелец, система, способ подключения, срок действия, последняя активность — и сопоставили его с договорами подряда и заявками на доступ. Выделили учётки уволенных, общие пароли в скриптах и доступы без основания.
Архитектура контура Описали целевой контур: хранилище секретов, выдача прав по заявке на срок, запись административных сессий, журналирование действий с документами и выгрузками, отзыв доступа по закрытию договора. Условились, что общая учётная запись не остаётся ни в одном сценарии — каждое подключение сводится к человеку и договору.
Разработка матрицы Собирали не внедрение, а матрицу доступа и план: раскладывали системы по способам подключения, писали схему выдачи прав на срок и требования к записи сессий и на образцах проверяли, что действие в системе связывается с заявкой. Отдельно разобрали, как вынимать пароли из скриптов.
Проверка инвентаря Перепроверяли инвентарь по журналам подключений, сверяли последнюю активность с договорами подряда и показывали матрицу владельцам систем, ища доступы, не попавшие в первый срез. Не приняли бы результат, если бы в инвентаре осталась запись без владельца и основания или если бы для какой-то системы не было описано, как отзывать доступ по закрытию договора.
Передача плана Передали инвентарь привилегированных записей, матрицу доступа по договорам, схему выдачи прав на срок, требования к записи сессий и план отзыва доступа. Администратор сам ведёт инвентарь и закрывает доступ подрядчика по договору.
Архитектура
Коллекторы собирают инвентарь из трёх источников: факты Ansible с узлов, выгрузка LDAP-каталога и списки локальных учётных записей и ключей в скриптах. Сборщик на Ktor нормализует логины и сводит записи к владельцу, системе, способу подключения, сроку действия и последней активности в PostgreSQL, Redis держит справочники на время прохода. Несопоставленная с договором или заявкой запись не отбрасывается, а попадает в отчёт как доступ без основания — при обратном порядке учётки уволенных исчезли бы из инвентаря вместе с их закрытыми договорами. Целевой контур описан схемой: Vault выдаёт учётные данные на срок заявки, административные сессии пишутся, доступ отзывается по закрытию договора; события собираются через Kafka, срезы по активности выводятся в Grafana.
Оценка данных продукта перед инвестиционной проверкой
Задача, решение и что передали
Задача
Метрики продукта пересобирали к каждой встрече вручную, и два отчёта об одной активной базе не сходились между собой.
Решение
Прошли путь каждой цифры от события в приложении и записи в бэкенде до строки отчёта: описали источники, ключи пользователя и устройства, правила исключения тестовых и внутренних аккаунтов. Нашли места расхождения: двойной учёт по устройству и по учётной записи, разные окна активности, потерянные события платежей при повторной попытке. Собрали воспроизводимый расчёт: слой сырых событий, слой сущностей, слой метрик с определениями, ежедневные проверки сходимости и журнал изменений формул.
Сдано 4
Паспорт метрик
Схема прослеживания цифры до события
Каталог расхождений
Набор проверок сходимости
Как делали
Обследование метрик Прошли путь каждой цифры от события в приложении и записи в бэкенде до строки отчёта, описали источники, ключи пользователя и устройства и правила исключения тестовых и внутренних аккаунтов. Нашли, откуда берётся расхождение двух отчётов об одной активной базе.
Архитектура расчёта Разложили расчёт на слои — сырые события, сущности, метрики с определениями — и увели определения в паспорт, чтобы окно активности и правило исключения аккаунтов не задавались в каждом отчёте заново. Двойной учёт по устройству и по учётной записи закрыли выбором единого ключа сущности.
Разработка слоёв Подняли слой сырых событий, собрали слой сущностей по выбранным ключам, затем метрики по паспортам. Ежедневные проверки сходимости и журнал изменений формул подключали на стенде, где пересчитывали прошедшие периоды.
Проверка сходимости Пересобирали метрики за прошедшие периоды и сверяли с ранее показанными отчётами, разбирая каждое расхождение до события, отдельно гоняли потерянные события платежей при повторной попытке. Провалом считали цифру, которую нельзя проследить до события в приложении, и метрику без определения в паспорте.
Передача паспорта Передали паспорт метрик, схему прослеживания цифры до события, каталог расхождений, набор проверок сходимости и отчёт о готовности данных. Команда заказчика сама пересобирает отчёт из одного расчёта и объясняет любую цифру по паспорту.
Архитектура
События приложения уходят через Kotlin-SDK в приёмник на Ktor и дальше в Kafka, сырой слой складывается в ClickHouse. Ключи пользователя и устройства разводятся отдельным шагом в слое сущностей: двойной учёт брался из склейки по устройству и по учётной записи разом, поэтому сущностью считается учётная запись, а устройство остаётся её атрибутом с историей привязок. Слой метрик строится dbt-моделями с определением в паспорте показателя, правила исключения тестовых и внутренних аккаунтов вынесены в отдельную модель, ежедневные проверки сходимости описаны Great Expectations и запускаются расписанием Airflow. Журнал изменений формул и версии моделей лежат в PostgreSQL рядом с датой применения, поэтому отчёт восстанавливается на прошлую дату, а не пересобирается по текущим правилам; окружение — Docker.
Проверяется: реестр систем и требования к инференсу
Дорожная карта ИИ для закрытого контура ведомства
Задача, решение и что передали
Задача
Работа шла в контуре без доступа в интернет, и внутри не понимали, какие ИИ-сценарии там вообще выполнимы без внешних сервисов.
Решение
Провели инвентаризацию систем и форматов: что лежит в СЭД, что в файловом хранилище, какие есть интеграционные шины и в каком виде они отдают данные. Проверили доступное железо под инференс на месте, оценили требования к памяти и к числу параллельных запросов для моделей, которые разворачиваются локально. Сценарии выстроили в очередь по готовности данных и по цене ошибки, для каждого описали точки интеграции и способ проверки результата человеком.
Сдано 3
Реестр систем и данных
Карта интеграций
Дорожная карта по очередям внедрения и требования к инфраструктуре под локальный инференс
Как делали
Инвентаризация контура Прошли по системам закрытого контура: что лежит в СЭД, что в файловом хранилище, какие есть интеграционные шины и в каком виде они отдают данные по REST и SOAP. Отдельно осмотрели доступное на месте железо под инференс и учётные записи в Keycloak.
Только локально Исходили из того, что внешних сервисов не будет: годятся только модели, разворачиваемые локально, поэтому оценили требования к памяти и к числу параллельных запросов. Сценарии выстроили в очередь по готовности данных и по цене ошибки, для каждого заранее описали точку интеграции и способ проверки результата человеком.
Сборка реестра Собрали реестр систем и данных и карту интеграций, проверили на месте, что шины отдают данные в разборном виде, а не картинками и вложениями. На доступном железе прикинули конфигурацию локального инференса под заявленную нагрузку.
Проверка выполнимости Каждый сценарий из карты проверили на выполнимость внутри контура, без выхода в интернет. Провалом считали сценарий, требующий внешнего сервиса или модели, не помещающейся в доступную память, и сценарий без описанного способа проверки результата человеком — такие в очередь не ставили.
Передача ведомству Передали реестр систем и данных, карту интеграций, дорожную карту по очередям внедрения и требования к инфраструктуре под локальный инференс. Служба эксплуатации пересчитывает конфигурацию под новые модели по той же методике.
Архитектура
Реестр систем ведёт служба на Rust с Axum: коннекторы опрашивают СЭД, файловое хранилище и интеграционные шины — часть отдаёт REST, часть SOAP с описанием в WSDL, — а паспорта источников, форматы и объёмы складываются в PostgreSQL, образцы документов в MinIO. Замер железа под инференс сделан на месте: стенд с vLLM поднимается плейбуком Ansible на доступных GPU и прогоняется под заданным числом параллельных запросов, потому что требование к памяти задаёт длина контекста и размер KV-кеша, а не только размер весов. Доступ к реестру разведён через Keycloak по подразделениям — сведения о системах закрытого контура сами ограничены в распространении. Всё живёт внутри сегмента без выхода наружу: образы и веса заносятся заранее, метрики стенда снимает Prometheus, при отказе узла стенд пересобирается теми же плейбуками.
Проверяется: индекс с версионированием и регламент обновления
Ассистент по внутренней нормативной базе вуза
Задача, решение и что передали
Задача
Приказы, положения и регламенты лежали в СЭД и в файловых папках в нескольких редакциях, и действующую версию сотрудники выясняли у коллег.
Решение
Собрали корпус из СЭД и хранилища с сохранением редакций и даты вступления в силу, каждый фрагмент привязан к документу, пункту и статусу действия. Ответ строится только по найденным фрагментам и всегда содержит ссылку на пункт; если действующего документа по вопросу нет, ассистент так и говорит и называет ответственное подразделение, а не сочиняет формулировку. Права наследуются из каталога: фрагменты, к которым у пользователя нет доступа, в контекст ответа не попадают.
Сдано 3
Ассистент с веб-интерфейсом
Индекс с версионированием документов
Правила разграничения доступа и регламент обновления корпуса
Как делали
Сбор корпуса Собрали, где лежат приказы, положения и регламенты — СЭД и файловые папки — и увидели, что один документ живёт в нескольких редакциях сразу. С учебной частью и юристами разобрали, как определяется дата вступления в силу и какое подразделение отвечает за какой раздел.
Редакции и права Корпус решили хранить с редакциями и датой вступления в силу, привязывая каждый фрагмент к документу, пункту и статусу действия. Права наследуются из каталога: недоступные пользователю фрагменты не попадают в контекст ответа, а не вычищаются из готового текста.
Сборка ассистента Собрали загрузку из СЭД и хранилища с сохранением редакций, затем индекс с версионированием и наследованием прав, затем веб-интерфейс. Отдельно заложили правило отказа: если действующего документа по вопросу нет, ассистент говорит об этом и называет ответственное подразделение.
Проверка редакций Прогнали вопросы сотрудников разных подразделений и отдельно пары «действующая и отменённая редакция» одного положения. Провалом считали ответ по отменённой редакции, ответ без ссылки на пункт и сочинённую формулировку там, где действующего документа нет.
Передача делопроизводству Передали ассистента с веб-интерфейсом, индекс с версионированием документов, правила разграничения доступа и регламент обновления корпуса. Делопроизводители загружают новые редакции и снимают с действия старые сами.
Архитектура
Ядро поиска — сервис на C++ с интерфейсом gRPC: он принимает запрос вместе с группами пользователя из LDAP, отбирает фрагменты в OpenSearch и ближайшие векторы в Faiss, затем собирает контекст для генерации в vLLM. Редакции документов, даты вступления в силу и статус действия лежат в PostgreSQL, каждый фрагмент индекса несёт ссылку на документ, пункт и редакцию, а фильтр по правам и по действующей версии применяется до ранжирования — иначе закрытый фрагмент успевает попасть в контекст и утекает в текст ответа. Пополнение корпуса идёт событиями из СЭД и файлового хранилища через Kafka: обработчик переиндексирует только затронутые документы, прежняя редакция остаётся в индексе помеченной, чтобы ссылки из старых ответов не рвались. Если действующего документа по вопросу нет, набор разрешённых фрагментов пуст, и ответ строится как отказ с указанием ответственного подразделения; узлы подняты контейнерами, индекс пересобирается из PostgreSQL и хранилища.
Сборка продукта под отечественные процессорные архитектуры
Задача, решение и что передали
Задача
Продукт собирался только под x86-64 и на закупленных машинах с отечественными процессорами падал на старте из-за нативных библиотек и векторных вставок.
Решение
Пересобрали зависимости под целевые архитектуры, ассемблерные и интринсик-вставки заменили портируемыми реализациями с выбором пути по возможностям процессора, порядок байт и выравнивание структур в форматах обмена зафиксировали явно. Подняли сборочные агенты на целевом железе — модульные и нагрузочные прогоны идут там же, а не в эмуляции. Расхождения в вычислениях с плавающей точкой закрыли фиксацией режимов округления и сверкой контрольных наборов между платформами.
Сдано 3
Передали сборки под каждую архитектуру
Конвейер с агентами на целевом железе
Отчёты прогонов и перечень отличий по платформам
Как делали
Воспроизведение падения Воспроизвели падение на старте на машинах с отечественными процессорами и нашли причину: нативные библиотеки и векторные вставки, собранные только под x86-64. Составили список зависимостей, у которых нет сборок под целевые архитектуры.
Портируемые пути Ассемблерные и интринсик-вставки заменили портируемыми реализациями с выбором пути по возможностям процессора, а порядок байт и выравнивание структур в форматах обмена зафиксировали явно. Сборочные агенты решили поднимать на целевом железе, а не гонять прогоны в эмуляции.
Сборка под архитектуры Пересобрали зависимости под aarch64 и Эльбрус e2k, перевели сборку на конвейер с агентами на этом же железе и туда же вынесли модульные и нагрузочные прогоны.
Сверка вычислений Сверяли контрольные наборы вычислений между платформами при явно заданных режимах округления и гоняли нагрузку на целевом железе. Провалом считали расхождение результата между архитектурами и сборку, которая проходит только в эмуляции.
Передача конвейера Передали сборки под каждую архитектуру, конвейер с агентами на целевом железе, отчёты прогонов и перечень отличий по платформам. Команда заказчика добавляет архитектуру в конвейер и повторяет сверку сама.
Архитектура
Продукт собирается одним конвейером под три архитектуры: агенты GitLab CI подняты на самом целевом железе — aarch64 и Эльбрус e2k, — потому что эмуляция скрывает и различия компилятора, и поведение под нагрузочным прогоном. Нативные зависимости собираются CMake с явным выбором пути по возможностям процессора: ассемблерные и интринсик-вставки заменены портируемыми реализациями, быстрая ветка включается проверкой в рантайме, а не флагом сборки, поэтому один пакет запускается на разных ревизиях процессора. Форматы обмена между узлами описаны в Protocol Buffers и ходят по gRPC-Go, порядок байт и выравнивание структур заданы схемой, так что запись, сделанная на одной архитектуре, читается на другой без правок. Состояние держат PostgreSQL на сервере и SQLite в локальном агенте; режимы округления плавающей точки зафиксированы, контрольные наборы сверяются между платформами на каждом прогоне.
Стек
Go · gRPC-Go · PostgreSQL · SQLite · Protocol Buffers · GitLab CI · CMake · aarch64 · Эльбрус e2k
Проверяется: конструктор маршрутов и шаблоны справок
Портал сотрудника с заявками, справками и согласованиями
Задача, решение и что передали
Задача
Заявления на отпуск и справки писали на бумаге и носили по кабинетам, статус заявления нигде не отображался.
Решение
Собрали портал вокруг заявки: сотрудник подаёт форму, маршрут согласования строится по структуре подразделений из кадровой системы, руководитель видит свою очередь одним списком. Справки собираются шаблоном из кадровых данных, заявителю видно текущий этап и согласующего. Отсутствие руководителя переводит шаг на замещающего по правилу замещения, а не подвешивает заявку.
Сдано 4
Передали портал
Конструктор маршрутов согласования
Шаблоны справок
Интеграцию с кадровой системой и каталогом пользователей
Как делали
Обследование Собрали бумажные формы заявлений на отпуск и запросов справок, прошли маршрут листа согласования по кабинетам, разобрали штатную структуру и правила замещения в кадровой системе. Нашли подразделения, где согласующий по кадрам не совпадает с фактическим руководителем.
Архитектура Маршрут решили не зашивать в код, а строить по структуре подразделений из кадровой системы, конструктор маршрутов вынесли отдельным справочником; заявка и её шаги хранятся состоянием с историей переходов. Учётные записи берём из каталога пользователей, отдельного пароля у портала нет.
Разработка Сначала сделали подачу заявления и очередь руководителя, затем шаблоны справок с подстановкой кадровых данных, последним — правило замещения. На стенде гоняли на обезличенном срезе штатки, включая перевод сотрудника в другое подразделение посреди согласования.
Проверка Прогнали заявления по всем видам отпуска и запросы справок на копии кадровой структуры, отдельно проверяли отсутствующего руководителя и цепочку из нескольких согласующих. Провалом считали заявку, зависшую без действующего согласующего, и справку, данные в которой разошлись с кадровой системой.
Передача Отдали портал, конструктор маршрутов, шаблоны справок и интеграции с кадровой системой и каталогом пользователей. Кадровой службе показали, как завести новый вид заявления и поправить шаблон справки без разработчика, администратору — разбор застрявших заявок и настройку замещений.
Архитектура
Портал разведён на SPA и Go-сервис заявок за обратным прокси; маршрут согласования строит движок процессов, который читает структуру подразделений и правила замещения из реплики кадрового справочника, наполняемой выгрузкой из 1С:ЗУП. Состояние заявки лежит в PostgreSQL: переход шага пишется в текущий статус и в таблицу истории одной транзакцией, поэтому очередь руководителя строится обычным индексируемым запросом, а не обходом журнала. Рендер справок по шаблону и рассылка уведомлений вынесены в воркеры за RabbitMQ — сборка документа из кадровых данных идёт дольше, чем допустимо держать HTTP-запрос формы подачи. Вход — по учётной записи каталога LDAP; при отсутствии согласующего шаг переназначается замещающему по правилу из справочника, а падение узла приложения не теряет заявки, так как состояние процесса лежит в базе, а не в памяти процесса.
Стек
Go · chi · PostgreSQL · Redis · RabbitMQ · Docker · Vue 3 · LDAP · 1С:ЗУП
Шлюз доступа ведомственных систем к языковым моделям
Задача, решение и что передали
Задача
Каждая прикладная система обращалась к своей модели своим способом, адреса и ключи лежали в конфигурациях, а смена модели означала правку кода.
Решение
Поставили шлюз с единым совместимым API: вызов маршрутизируется по типу задачи и классу данных, квоты считаются по подразделениям, запрос сверх лимита отбивается на входе. Ключи вынесены в хранилище секретов, приложению остаётся только адрес шлюза. Недоступная модель выводится из ротации, вызов уходит на резервную того же класса.
Сдано 4
Передали шлюз
Реестр моделей и маршрутов
Политику квот по подразделениям
Схему хранения ключей
Как делали
Обследование Собрали, как каждая прикладная система обращается к модели, и вытащили адреса и ключи из конфигураций. Разобрали типы задач и классы данных, с которыми системы ходят, и уточнили, по каким подразделениям ведомство считает потребление.
Архитектура Поставили шлюз с единым совместимым API, чтобы приложению оставался только его адрес; маршрут выбирается по типу задачи и классу данных, ключи вынесены в хранилище секретов. Квоты считаем по подразделениям и отбиваем запрос сверх лимита на входе, а не после вызова модели; недоступную модель выводим из ротации на резервную того же класса.
Разработка Сначала подняли совместимый API и реестр моделей с маршрутами, затем квоты по подразделениям с отбоем на входе, затем вывод модели из ротации и журнал вызовов. На стенде подключили приложения каждого типа и переключали модель, не трогая их конфигурации.
Проверка Гоняли вызовы подключённых систем, глушили модель на ходу, превышали квоту подразделения и смотрели, не остались ли ключи в конфигурациях приложений. Провалом считали ключ, попавший в журнал или в конфигурацию приложения, и вызов, ушедший к модели чужого класса данных.
Передача Передали шлюз, реестр моделей и маршрутов, политику квот по подразделениям и схему хранения ключей. Администраторам показали, как добавить модель и перенастроить маршрут, владельцам систем — журнал вызовов и разбор отбитых по лимиту.
Архитектура
Шлюз — Python-сервис за Envoy: Envoy держит TLS и балансировку, сервис реализует OpenAI-совместимый маршрут, выбирает модель по типу задачи и классу данных из реестра и подставляет ключ, взятый из Vault. Ключи вынесены в Vault, а не в конфигурацию приложения, потому что тогда смена ключа модели не требует пересборки и перезапуска ни одного вызывающего приложения — ему остаётся только адрес шлюза. Квоты подразделений считаются счётчиками в Redis по скользящему окну, поэтому запрос сверх лимита отбивается на входе кодом ответа, до обращения к модели; реестр моделей, маршруты и журнал вызовов лежат в PostgreSQL. Здоровье бэкендов опрашивается активной проверкой: недоступная модель снимается с ротации и вызов уходит на резервную того же класса, а вызывающая система опознаётся по токену Keycloak.
Подписной сервис конспектов и поиска по записям занятий
Задача, решение и что передали
Задача
Записи лекций лежали часовыми файлами без разметки, и нужное объяснение приходилось искать перемоткой наугад.
Решение
Сервис распознаёт речь с разделением говорящих, режет запись на смысловые блоки и подписывает их заголовками. Термины сверяются со словарём курса, поэтому расшифровка не плывёт на именах и формулах. Поиск идёт по расшифровке, найденный фрагмент открывает плеер на своей позиции.
Сдано 4
Передали сервис расшифровки
Словарь терминов курса
Индекс по записям
Встраиваемый плеер с таймкодами
Как делали
Обследование Послушали, как нужное объяснение ищут перемоткой в часовых записях лекций, и убедились, что в файлах нет ни разметки, ни оглавления. Со стороны курса собрали список терминов и формул, на которых распознавание речи обычно плывёт.
Архитектура Расшифровку делаем с разделением говорящих, чтобы вопрос из зала не смешивался с объяснением преподавателя, а словарь терминов курса подставляем в распознавание, а не правим текст после. Запись режем на смысловые блоки с заголовками, поиск идёт по расшифровке, найденный фрагмент открывает плеер на своей позиции по таймкоду.
Разработка Сначала распознавание с диаризацией и словарём курса, затем нарезка на блоки и заголовки, затем индекс и переход из результата поиска в плеер. На стенде прогнали записи разных курсов, включая занятия с плохим микрофоном и записью экрана.
Проверка Искали по расшифровке известные объяснения и смотрели, на том ли месте открывается плеер, отдельно проверяли термины и формулы из словаря курса. Провалом считали переход, открывающий запись не на том фрагменте, и термин курса, стабильно распознанный чужим словом.
Передача Передали сервис расшифровки, словарь терминов курса, индекс по записям и встраиваемый плеер с таймкодами. Методистам показали пополнение словаря и выгрузку конспектов, администратору — постановку новых записей в обработку.
Архитектура
Загруженная запись кладётся в объектное хранилище, задача ставится в Celery: Whisper снимает расшифровку с таймкодами, pyannote.audio размечает говорящих, затем реплики склеиваются в смысловые блоки и подписываются заголовками. Расшифровка и границы блоков лежат в PostgreSQL, эмбеддинги фрагментов — в pgvector рядом: выдача поиска возвращает текст вместе с таймкодом, и держать вектор в отдельном хранилище значило бы синхронизировать две базы ради одной выборки. Словарь терминов курса подставляется в декодер начальным промптом и применяется на постобработке, поэтому имена и формулы не расходятся между лекциями. Запись перекодируется FFmpeg в HLS с нарезкой сегментов, и встраиваемый плеер открывается сразу на позиции найденного фрагмента.
Агент подготовки проектов ответов на обращения граждан
Задача, решение и что передали
Задача
Обращения с портала и из почты исполнитель разбирал сам: определял тему, искал похожий ответ в папках и подбирал норму для ссылки.
Решение
Агент относит обращение к рубрике ведомственного классификатора, поднимает близкие завершённые дела и собирает проект ответа из шаблона с реквизитами заявителя и ссылкой на пункт нормативного акта. Срок исполнения считается от даты регистрации по правилам рубрики и проставляется в карточку вместе с исполнителем. Обращение с несколькими темами или неуверенной рубрикой уходит на ручную маршрутизацию, а решение регистратора возвращается в обучающую выборку.
Сдано 4
Рубрикатор обращений
Библиотека шаблонов ответов
Сервис подготовки проекта
Правила расчёта срока и журнал маршрутизации
Как делали
Обследование Разобрали поток обращений с портала и из почты и посмотрели, как исполнитель определяет тему, ищет похожий ответ в папках и подбирает норму для ссылки. Свели ведомственный классификатор рубрик с правилами расчёта срока и распределением исполнителей.
Архитектура Рубрикацию, поиск завершённых дел и сборку проекта развели на отдельные шаги, чтобы правка классификатора не тянула за собой шаблоны. Срок исполнения считаем от даты регистрации по правилам рубрики и проставляем в карточку вместе с исполнителем, а обращение с несколькими темами или неуверенной рубрикой не назначаем — оно идёт на ручную маршрутизацию.
Разработка Сначала рубрикация по ведомственному классификатору, затем поиск близких завершённых дел, затем сборка проекта из шаблона с реквизитами заявителя и ссылкой на пункт нормативного акта. На стенде прогоняли архив завершённых обращений, решения регистратора складывали в обучающую выборку.
Проверка Сравнивали рубрику и подобранную норму с тем, как обращение отработали в архиве, отдельно гоняли обращения с несколькими темами. Провалом считали проект ответа со ссылкой на недействующий пункт нормативного акта и срок, посчитанный не по правилам рубрики.
Передача Передали рубрикатор обращений, библиотеку шаблонов ответов, сервис подготовки проекта и правила расчёта срока. Исполнителям показали правку проекта до подписи, регистраторам — ручную маршрутизацию и журнал.
Архитектура
Сервис на NestJS принимает обращения с портала и из почтового коллектора, относит их к рубрике ведомственного классификатора и ищет близкие завершённые дела в Elasticsearch, где лежат тексты обращений и ответов. Полнотекстовый индекс отделён от PostgreSQL с карточками и сроками, потому что поиск похожих идёт по морфологии и весам полей, а карточка требует транзакционных переходов статуса — держать обе нагрузки в одном хранилище значит подгонять его настройки под противоположные задачи. Проект ответа собирается из шаблона подстановкой реквизитов заявителя и ссылки на пункт нормативного акта, срок исполнения считается от даты регистрации по правилу рубрики и пишется в карточку вместе с исполнителем. Обращение с несколькими темами или неуверенной рубрикой не назначается автоматически, а уходит регистратору; его решение пишется в журнал маршрутизации и забирается в обучающую выборку, рабочее место исполнителя — React поверх того же API.
Обмен бухгалтерии учреждения с казначейской системой начислений
Задача, решение и что передали
Задача
Начисления и платёжные документы вводили в казначейском кабинете повторно, а квитанции сверяли с выпиской вручную.
Решение
Обмен переведён на регламентный сервис: начисление формируется в учётной системе, уходит пакетом по расписанию и получает обратно квитанцию с идентификатором. Пакету присваивается номер и контрольная сумма, поэтому повторная отправка распознаётся приёмником и нового документа не порождает. Отклонённые записи возвращаются с кодом причины и собираются в реестр для исправления.
Сдано 4
Сервис обмена с казначейской системой
Реестр отклонённых записей
Справочник кодов причин
Регламент подписи пакетов
Как делали
Обследование обмена С бухгалтерией учреждения прошли путь начисления от документа в учётной системе до повторного ввода в казначейском кабинете и разобрали, как квитанции сверяются с выпиской. Выписали форматы пакетов и коды отказов, которые возвращает приёмная сторона.
Архитектура пакетов Обмен сделали регламентным и пакетным, а не поштучным: пакету присваивается номер и контрольная сумма, по которым приёмник распознаёт повторную отправку. Подпись пакета вынесли отдельным шагом с ответственной ролью, а разбор отклонений — в реестр, чтобы исправление шло в учётной системе, а не в кабинете.
Разработка сервиса Собрали формирование пакета по расписанию и приём квитанции с идентификатором, затем реестр отклонённых записей и справочник кодов причин. Отработку вели на тестовом контуре казначейской системы.
Проверка повторов Повторно отправляли уже принятый пакет и пакеты с испорченной контрольной суммой, прогоняли начисления разных видов. Провалом считали второй документ начисления по одному номеру пакета и любую отклонённую запись, не попавшую в реестр с кодом причины.
Передача бухгалтерии Передали сервис обмена, регламент подписи пакетов, журнал квитанций и справочник кодов причин. Бухгалтер сам разбирает реестр отклонений, исправляет начисление и ставит пакет на повторную отправку.
Архитектура
Регламентный сервис на Spring Boot собирает начисления в пакет по расписанию планировщика, подписывает XML-конверт через КриптоПро JCP и отправляет SOAP-вызовом в казначейский сервис, ответная квитанция разбирается тем же сервисом. Пакету присваивается номер и контрольная сумма состава, пара хранится в PostgreSQL и уходит в конверте: повторная отправка идёт с теми же реквизитами, приёмник распознаёт её и нового документа не создаёт. Пакетный обмен по расписанию выбран вместо вызова на каждую проводку потому, что казначейская система принимает документы окнами и квитирует асинхронно — состояние отправки приходится вести у себя, а не в вызове. Отклонённые записи возвращаются с кодом причины и складываются в реестр, начисления забираются из учётной системы её HTTP-сервисом.
Портал абитуриента с подачей документов и конкурсными списками
Задача, решение и что передали
Задача
Заявления принимаются очно и почтой, данные переносятся в приёмную систему руками, а конкурсные списки собираются выгрузкой и публикуются файлом.
Решение
Портал принимает заявление со сканами и проверкой на дубль по документу личности, данные уходят в приёмную систему сообщением и получают номер. Конкурсный список пересобирается фоновой задачей после каждого изменения набора заявлений и согласий, публикация идёт обезличенным номером. Подача и отзыв согласия на зачисление фиксируются в журнале приёмной кампании с отметкой времени.
Сдано 4
Портал абитуриента
Обмен с приёмной системой
Публикация конкурсных списков
Журнал приёмной кампании
Как делали
Обследование приёма Прошли приём заявлений с техническим секретарём приёмной комиссии, разобрали перенос данных в приёмную систему и сборку конкурсных списков выгрузкой. Выписали правила подачи и отзыва согласия на зачисление и требования к публикации.
Архитектура подачи Заявление принимает портал со сканами и проверкой на дубль по документу личности, а в приёмную систему оно уходит сообщением и получает номер — параллельного набора портал не ведёт. Конкурсный список пересобирается фоновой задачей после каждого изменения заявлений и согласий и публикуется обезличенным номером.
Разработка портала Сначала подача заявления со сканами и проверка на дубль, затем обмен с приёмной системой, затем пересборка и публикация конкурсных списков и журнал приёмной кампании с отметками времени.
Проверка на заявлениях Прогнали набор заявлений с повторной подачей от одного человека и с отзывом согласия на зачисление. Провалом считали двух абитуриентов по одному документу личности, конкурсный список, разошедшийся с приёмной системой, и персональные данные, попавшие в публикацию.
Передача комиссии Передали портал абитуриента, обмен с приёмной системой, публикацию конкурсных списков, журнал кампании и инструкцию технического секретаря. Комиссия сама открывает и закрывает приём, публикует список и поднимает историю согласий.
Архитектура
Портал на Symfony принимает заявление со сканами, дубль отсекается уникальным индексом по хешу документа личности в PostgreSQL, файлы кладутся в объектное хранилище. Пересборка конкурсного списка вынесена в фоновую очередь Symfony Messenger поверх RabbitMQ с дедупликацией задач в пределах окна: согласия подаются и отзываются пачками, и пересчитывать список синхронно на каждое сохранение нельзя. Опубликованный список отдаётся обезличенным номером из кеша Redis, инвалидация идёт по завершении пересчёта. Заявление уходит в приёмную систему сообщением и получает номер обратно, подача и отзыв согласия пишутся в журнал приёмной кампании со временем сервера.
Проверяется: протокол проверки на двух дистрибутивах
Портирование системы электронного обучения под отечественные ОС
Задача, решение и что передали
Задача
Курсы и тесты открывались в одном браузере под Windows, а прокторинг держался на устаревшем плагине.
Решение
Плагин заменён веб-компонентами на стандартных API: камера, микрофон и загрузка работ работают без установки на рабочем месте. Серверная часть перенесена на Linux, материалы курсов вынесены в объектное хранилище, генерация сертификатов переведена на серверный PDF. Проверка пройдена на двух отечественных дистрибутивах с типовым образом учебного класса.
Сдано 4
Переданы образ учебного места
Пакеты серверной части
Протокол проверки на двух дистрибутивах
Инструкция для преподавателя
Как делали
Обследование класса Разобрали, что в курсах и тестах держалось на устаревшем плагине прокторинга и на единственном браузере под Windows. Собрали типовой образ учебного класса и требования преподавателей к выдаче сертификатов.
Архитектура прокторинга Плагин заменили веб-компонентами на стандартных API, чтобы камера, микрофон и загрузка работ работали без установки на рабочем месте. Серверную часть перенесли на Linux, материалы курсов вынесли в объектное хранилище, генерацию сертификатов перевели на серверный PDF.
Разработка компонентов Сначала веб-компоненты прокторинга и сдачи работ, затем перенос серверной части и материалов курсов, затем серверная генерация сертификатов. Параллельно собирали образ учебного места.
Проверка на дистрибутивах Проводили тестирование с прокторингом в учебном классе на двух отечественных дистрибутивах. Не приняли бы работу при недоступности камеры или микрофона на штатном образе, при потере загруженной работы на обрыве связи и при сертификате, отличающемся по содержанию от прежнего.
Передача учебной части Передали образ учебного места, пакеты серверной части, протокол проверки на двух дистрибутивах и инструкцию для преподавателя. Учебная часть сама разворачивает класс, ведёт курсы и выпускает сертификаты.
Архитектура
Серверная часть переписана под Linux на Django, плагин прокторинга заменён веб-компонентами на штатных API браузера: камера и микрофон берутся через getUserMedia, поток идёт по WebRTC, установка на рабочем месте не требуется. Проверка присутствия вынесена отдельным сервисом инференса — он берёт кадры из очереди Celery и возвращает признаки; модель не держат внутри веб-процесса, иначе её загрузка и пики распознавания занимают воркеры, отдающие курсы, и обновлять их пришлось бы вместе. Материалы курсов лежат в объектном хранилище и отдаются ссылкой, сертификаты рендерит серверный сервис PDF. Проверка пройдена на двух отечественных дистрибутивах с типовым образом учебного класса, поставка собрана в контейнерах.
Регламент повторной проверки ИИ-сервиса перед продлением допуска
Задача, решение и что передали
Задача
Сервис проверили один раз при запуске, а дальше менялись модель, шаблоны промптов и база — без нового контроля.
Решение
Собрали контрольный набор задач и враждебных сценариев, зафиксировали его версию рядом с версией модели и шаблона промпта. Допуск выдаётся на срок и привязан к этой связке: окончание срока или смена любого элемента запускают прогон заново. Результаты складываются в реестр, расхождение с прошлым замером видно построчно по сценариям.
Сдано 4
Регламент переаттестации
Контрольный набор сценариев с версией
Реестр прогонов
Шаблон протокола допуска
Как делали
Обследование изменений Разобрали, что менялось в сервисе после первичной проверки — модель, шаблоны промптов, состав базы — и по каким документам выдавался допуск. Собрали с владельцем сервиса перечень задач, которые он обязан решать.
Архитектура контроля Контрольный набор задач и враждебных сценариев зафиксировали версией рядом с версией модели и шаблона промпта, а допуск привязали к этой связке, чтобы смена любого элемента запускала прогон заново. Результаты решили складывать в реестр с построчным сравнением по сценариям.
Разработка проверок Сначала собрали и версионировали контрольный набор, затем встроили прогон в сборочный конвейер при смене модели или шаблона, затем сделали реестр прогонов и шаблон протокола допуска.
Проверка набором Прогнали набор на прежней и изменённой связке модели и промпта и сверили результаты построчно. Провалом считали изменение сервиса, не поднявшее новый прогон, и сценарий, результат которого ухудшился против прошлого замера без разбора причины.
Передача владельцу сервиса Передали регламент переаттестации, контрольный набор сценариев с версией, реестр прогонов и шаблон протокола допуска. Владелец сервиса сам дополняет набор сценариями и выпускает протокол при продлении допуска.
Архитектура
Контрольный набор задач и враждебных сценариев версионируется в репозитории, его хеш вместе с версией модели и версией шаблона промпта образует одну запись допуска в PostgreSQL. Прогон запускает пайплайн GitLab CI по двум событиям: истечение срока допуска и изменение любого элемента связки — триггер на изменение нужен потому, что правка шаблона промпта меняет поведение раньше, чем заканчивается календарный срок. Раннер поднимает сервис в изолированном окружении, harness на FastAPI прогоняет сценарии, результаты и артефакты складываются в MLflow и объектное хранилище. Реестр прогонов показывает построчную разницу с прошлым замером по сценариям, протокол допуска собирается из той же записи.
Стенд враждебных сценариев для публичного ассистента вуза
Задача, решение и что передали
Задача
Ассистент отвечал абитуриентам на сайте, а проверка его поведения сводилась к тому, что сотрудники по очереди пробовали его обмануть.
Решение
Собрали библиотеку враждебных диалогов: попытки выманить внутренние документы приёмной комиссии, получить обещание о зачислении, увести разговор за пределы темы. Прогон запускается на сборке и сравнивает поведение с эталоном по каждому сценарию, отчёт показывает, какой именно диалог сломал ответ. Жалобы из формы обратной связи разбираются и пополняют библиотеку.
Сдано 4
Библиотека враждебных диалогов
Отчёт прогона по сценариям
Регламент пополнения библиотеки
Критерии блокировки сборки
Как делали
Разбор ассистента Посмотрели, как ассистент отвечает абитуриентам на сайте, и собрали у сотрудников приёмной комиссии их наработанные попытки его обмануть. Зафиксировали, что считается недопустимым: выдача внутренних документов приёмной комиссии, обещание о зачислении, уход за пределы темы поступления.
Устройство стенда Решили держать враждебные диалоги библиотекой, отделённой от кода ассистента, эталон описывать по каждому сценарию, а прогон вешать на сборку с правом её заблокировать. Жалобы из формы обратной связи завели как отдельный вход в библиотеку, а не как переписку с разработчиками.
Сборка библиотеки Собрали диалоги по группам — выманивание внутренних документов, обещание зачисления, увод темы — и сделали прогон по сценариям с отчётом, показывающим, какой именно диалог сломал ответ. Сначала гоняли всё на стенде на копии ассистента.
Прогон сценариев Запускали библиотеку на рабочей сборке ассистента и на заведомо ослабленной. Провалом считали сценарий, на котором ассистент выдал внутренний документ приёмной комиссии или обещание о зачислении, и прогон, пропустивший ослабленную сборку дальше.
Передача комиссии Передали библиотеку враждебных диалогов, отчёт прогона, критерии блокировки сборки и регламент пополнения. Показали приёмной комиссии, как завести новый диалог из жалобы и как читать отчёт по сломавшемуся сценарию.
Архитектура
Прогон запускает раннер на Rust: он читает библиотеку сценариев из репозитория, поднимает диалоги против собранного ассистента по HTTP и сравнивает ход разговора с эталоном по каждому сценарию. Диалоги идут параллельно на Tokio, но число одновременных сессий ограничено семафором — иначе стенд превращается в нагрузочный прогон по тому же инференсу, и расхождение с эталоном не отделить от таймаутов. Эталоны, результаты и жалобы из формы обратной связи лежат в PostgreSQL, транскрипты и отчёты — в MinIO; ключ прогона считается от версии сборки и хеша библиотеки, поэтому перезапуск задания в CI не плодит второй набор результатов. Раннер вызывается шагом GitLab CI в Docker-образе, выкладывает отчёт в JUnit XML и возвращает код, по которому сборка блокируется; приём жалоб на Axum кладёт их в NATS JetStream, откуда разбор пополняет библиотеку.
Стек
Rust · Axum · Tokio · PostgreSQL · MinIO · NATS JetStream · GitLab CI · Docker · JUnit XML
Подписной ассистент по книге поступлений и инвентарным карточкам
Задача, решение и что передали
Задача
Научные сотрудники поднимали атрибуцию предмета по книге поступлений, инвентарным описям и реставрационным паспортам вручную, а сведения о месте хранения и страховой оценке лежали в тех же файлах.
Решение
Разобрали книгу поступлений, инвентарные карточки и реставрационные паспорта на фрагменты, привязав каждый к учётному номеру предмета и типу сведений. Ответ по предмету собирает атрибуцию, датировку, материал и технику, историю реставраций; топография хранения и страховая оценка отнесены к закрытому типу и выдаются только хранителю фонда. Каждое обращение к закрытому типу пишется в журнал с учётным номером предмета и ролью запросившего.
Сдано 4
Передали подписной сервис по фондам
Классификатор типов сведений
Правила выдачи закрытых полей
Журнал обращений к топографии хранения
Как делали
Разбор фондов Посмотрели с научными сотрудниками, как поднимается атрибуция предмета по книге поступлений, инвентарным описям и реставрационным паспортам, и что топография хранения и страховая оценка лежат в тех же файлах. С главным хранителем определили, кому эти сведения положены.
Типы сведений Решили привязывать каждый фрагмент к учётному номеру предмета и типу сведений, а сами типы разделить: топография хранения и страховая оценка выдаются только хранителю фонда. Обращение к закрытому типу договорились писать в журнал с учётным номером предмета и ролью запросившего.
Сборка ответа Разобрали книгу поступлений, инвентарные карточки и реставрационные паспорта на фрагменты, затем сделали классификатор типов сведений и правила выдачи закрытых полей. Последней собрали сборку ответа по предмету: атрибуция, датировка, материал и техника, история реставраций.
Проверка по ролям Задавали одни и те же вопросы по предметам от роли научного сотрудника и от роли хранителя фонда и сверяли собранный ответ с учётными документами. Провалом считали появление топографии хранения или страховой оценки в ответе научному сотруднику и обращение к закрытому типу, не отражённое в журнале.
Передача хранителю Передали подписной сервис по фондам, классификатор типов сведений, правила выдачи закрытых полей, журнал обращений к топографии хранения и инструкцию главного хранителя. Показали, как менять отнесение типа сведений и как читать журнал обращений.
Архитектура
Фрагменты книги поступлений, инвентарных карточек и реставрационных паспортов лежат в Qdrant с учётным номером предмета и типом сведений, сканы — в MinIO, карточки предметов и журнал обращений — в PostgreSQL. Закрытый тип сведений — топография хранения и страховая оценка — вынесен в отдельную коллекцию со своими правами, а не помечен признаком внутри общей: при ошибке в фильтре общая коллекция отдаётся целиком, а отдельная просто не подключается к запросу роли, которой она не положена. Роль хранителя фонда приходит из Keycloak и проверяется до поиска, каждое обращение к закрытому типу пишется в журнал только на добавление, с учётным номером предмета и ролью запросившего. Индексация новых поступлений идёт задачей через RabbitMQ, эмбеддинги считает BGE-M3 на площадке музея, сервис на FastAPI поставляется в Docker.
Подготовка учётных карточек предметов музейного фонда
Задача, решение и что передали
Задача
Хранитель переносил описания из книги поступлений и инвентарных книг в электронную карточку вручную, и предмет уходил в государственный каталог с разнобоем в датировке, материале и технике.
Решение
Агент распознаёт развороты книги поступлений и инвентарных книг, разбирает запись на поля — номер по книге поступлений, инвентарный номер, наименование, датировку, материал, технику, размеры, сохранность — и приводит формулировки к принятым в учёте словарям. Собранная карточка сверяется с заведёнными предметами по номеру и описанию, чтобы повторная оцифровка тома не наплодила записей; снимки предмета подтягиваются из съёмочной папки по номеру. Каждое поле хранит скан исходной страницы, и карточка выгружается в государственный каталог только после подтверждения хранителя.
Сдано 4
Сервис распознавания учётных книг
Словари датировок
Материалов и техник
Правила поиска дублей
Как делали
Разбор учёта Посмотрели с хранителем, как описание переносится из книги поступлений и инвентарных книг в электронную карточку, и собрали примеры разнобоя в датировке, материале и технике, ушедшего в государственный каталог. Разобрали, как устроены развороты книг и где лежат съёмочные папки со снимками предметов.
Схема полей Решили разбирать запись на поля — номер по книге поступлений, инвентарный номер, наименование, датировка, материал, техника, размеры, сохранность — и приводить формулировки к принятым в учёте словарям. За каждым полем закрепили скан исходной страницы, а выгрузку в государственный каталог поставили только после подтверждения хранителя.
Сборка распознавания Сделали распознавание разворотов книги поступлений и инвентарных книг и разбор записи на поля, затем словари датировок, материалов и техник. Последними собрали поиск дублей по номеру и описанию, подтягивание снимков из съёмочной папки по номеру и очередь подтверждения хранителя.
Прогон томов Прогнали тома, уже введённые в электронный учёт, и сличили собранные карточки с заведёнными вручную, отдельно повторно оцифровали один том. Провалом считали появление второй записи на тот же предмет при повторной оцифровке и поле, для которого не открывается скан исходной страницы.
Передача хранителям Передали сервис распознавания учётных книг, словари датировок, материалов и техник, правила поиска дублей, очередь подтверждения хранителя и инструкцию по выгрузке в государственный каталог. Показали хранителям, как подтверждать карточку и как пополнять словари.
Архитектура
Развороты книги поступлений и инвентарных книг сканируются, файлы уходят в MinIO, задачи распознавания — в RabbitMQ, воркеры на Ktor разбирают запись на поля: номер по книге поступлений, инвентарный номер, наименование, датировку, материал, технику, размеры, сохранность. Формулировки приводятся к учётным словарям нечётким сопоставлением с порогом, и значение ниже порога не подставляется, а остаётся в очереди подтверждения хранителя — словарь датировок и техник не покрывает всех вариантов записи, и молчаливая подстановка похожего термина хуже пустого поля. Каждое поле хранит ссылку на страницу и область скана, поэтому карточку сверяют с книгой, не поднимая том; дубли ищутся по номерам точным совпадением и по описанию через Elasticsearch, чтобы повторная оцифровка тома не наплодила записей. Карточки и состояние подтверждения лежат в PostgreSQL, интерфейс хранителя — на Vue, выгрузка в государственный каталог идёт только по подтверждённым записям, сборка — Docker.
Кабинет студента с ведомостями, пересдачами и учебным планом
Задача, решение и что передали
Задача
Оценки за сессию студент смотрел на вывешенной в деканате ведомости, а заявление на пересдачу и справку об обучении носил на бумаге.
Решение
Кабинет собирает индивидуальный учебный план, расписание группы и баллы по дисциплинам из системы учебного процесса: оценка появляется у студента после закрытия ведомости преподавателем. Заявления на пересдачу, академический отпуск и справку об обучении студент подаёт в кабинете, маршрут ведёт от преподавателя к деканату и заканчивается приказом. Академическая задолженность выводится отдельным списком вместе с направлением на пересдачу и назначенной комиссией.
Сдано 4
Кабинет студента
Обмен с системой учебного процесса
Маршруты заявлений до приказа
Список академических задолженностей
Как делали
Обследование сессии Посмотрели, как оценки вывешиваются ведомостью в деканате и как подаются заявления на пересдачу и справку об обучении. Разобрали в системе учебного процесса индивидуальный учебный план, расписание группы, порядок закрытия ведомости преподавателем и назначение комиссии по академической задолженности.
Архитектура маршрутов Балл появляется у студента только после закрытия ведомости преподавателем — промежуточные отметки в кабинет не выводим. Заявления пустили маршрутом от преподавателя к деканату с завершением приказом, а академическую задолженность вынесли отдельным списком вместе с направлением на пересдачу и назначенной комиссией.
Сборка кабинета Сначала обмен с системой учебного процесса по плану, расписанию и ведомостям, потом кабинет с баллами по дисциплинам и учебным планом, потом маршруты заявлений на пересдачу, академический отпуск и справку об обучении. На стенде прогоняли сессию с пересдачей и отзыв заявления на середине маршрута.
Проверка ведомостей Баллы и учебные планы в кабинете сверили с системой учебного процесса по группам разных направлений и прогнали заявления до приказа. Провалом считались оценка, видимая студенту до закрытия ведомости, и заявление, застрявшее без ответственного на шаге маршрута; не приняли бы и список задолженностей, расходящийся с ведомостями деканата.
Передача деканату Передали кабинет студента, обмен с системой учебного процесса, маршруты заявлений до приказа, список академических задолженностей и инструкцию деканата. Деканат правит состав маршрутов сам, преподаватель закрывает ведомость в своей системе.
Архитектура
Кабинет на Django держит свою проекцию учебного плана, расписания группы и баллов в PostgreSQL, синхронизацию с системой учебного процесса ведут задачи Celery по расписанию и по событию закрытия ведомости. Оценка показывается студенту по флагу закрытия ведомости из системы-источника, кабинет этот статус сам не выводит: иначе незакрытая ведомость утекала бы в кабинет раньше решения преподавателя. Заявления на пересдачу, академический отпуск и справку ведёт процесс в Camunda — маршрут от преподавателя к деканату и приказу описан схемой, а не кодом контроллера, поэтому смена порядка согласования не трогает сервис. Список академических задолженностей строится запросом по баллам и плану вместе с направлением на пересдачу и составом комиссии; вход через Keycloak, фронт на React, Redis держит сессии и кэш расписания, сервисы развёрнуты в Kubernetes.
Аудит учётных источников фондов и владельцев музейных показателей
Задача, решение и что передали
Задача
Число единиц хранения расходилось между книгой поступлений, инвентарными книгами отделов и выгрузкой в государственный каталог, и каждый отдел защищал свою цифру.
Решение
Прошли по каждому показателю фондовой отчётности — поступление, движение, экспонирование, реставрация — и назвали первичный источник: книгу поступлений, инвентарную книгу отдела, акт выдачи или реставрационный паспорт. Развели случаи, где предмет учтён комплектом и где поединично, и записали правило пересчёта между ними. Каждому показателю назначили владельца и срок актуализации, а расхождения с топографией хранения вынесли отдельным списком.
Сдано 3
Передали реестр показателей с владельцами
Карточку первичного источника на каждый показатель
Правило пересчёта комплектов и список расхождений с топографией
Как делали
Сверка книг Свели число единиц хранения из книги поступлений, инвентарных книг отделов и выгрузки в государственный каталог и разложили расхождение по отделам. С хранителями прошли по показателям фондовой отчётности — поступление, движение, экспонирование, реставрация.
Первичные источники Для каждого показателя назначили один первичный источник — книгу поступлений, инвентарную книгу отдела, акт выдачи или реставрационный паспорт, — а не несколько равноправных. Развели учёт комплектом и поединично, записали правило пересчёта между ними и закрепили за каждым показателем владельца и срок актуализации.
Реестр показателей Собрали реестр показателей с владельцами, карточку первичного источника на каждый показатель и правило пересчёта комплектов. Расхождения с топографией хранения вынесли отдельным списком.
Пересчёт показателей Пересчитали показатели по назначенным первичным источникам и сверили с выгрузкой в государственный каталог и с топографией хранения по отобранным фондам. Показатель, у которого не назван единственный первичный источник или владелец, считался несогласованным, и реестр с такими строками не принимался.
Передача хранителям Отдали реестр показателей с владельцами, карточку первичного источника на каждый показатель, правило пересчёта комплектов и список расхождений с топографией; с заведующими отделами прошли порядок актуализации. Музей сам ведёт реестр и разбирает расхождения по своему списку.
Архитектура
Реестр показателей фондовой отчётности — поступление, движение, экспонирование, реставрация — ведёт сервис на Spring Boot: у каждого показателя записаны первичный источник (книга поступлений, инвентарная книга отдела, акт выдачи, реставрационный паспорт), владелец и срок актуализации. Коннекторы читают базы отделов и выгрузки в государственный каталог в XML через JAXB и складывают в PostgreSQL снимки на дату, а сверка идёт по снимкам, а не по живым базам: отделы правят записи в течение дня, и расхождение иначе не воспроизводится при повторном разборе. Правило пересчёта между учётом комплектом и поединичным вынесено отдельной таблицей и применяется при сравнении, а не при загрузке, поэтому исходные значения отделов остаются нетронутыми. Сканы актов и выгрузок лежат в MinIO, схема ведётся Liquibase, список расхождений с топографией хранения выводится отдельной панелью в Grafana, узлы разложены в Docker.
Стек
Java · Spring Boot · PostgreSQL · MinIO · XML · Docker · JAXB · Liquibase · Grafana
Подписной сервис проверки работ и обратной связи ученику
Задача, решение и что передали
Задача
Работы проверяли вручную, комментарии писали в личных сообщениях, а очередь на проверку копилась у каждого преподавателя.
Решение
Сделали сервис по подписке: приём работы файлом или текстом, разбор по критериям задания, черновик рецензии с указанием места ошибки, очередь на подтверждение преподавателем, витрина тарифов и счётчик проверок. Модели вынесены в пул с квотой на школу, каждая проверка сохраняется с версией промпта и набором критериев.
Сдано 4
Передали сервис проверки
Тарифы и счётчики
Набор критериев
Журнал версий промптов
Как делали
Разбор очереди Разобрали с преподавателями, как копится очередь на проверку и как пишутся комментарии в личных сообщениях, и собрали критерии заданий, по которым работы оцениваются сегодня. Посмотрели, в каком виде ученик сдаёт работу — файлом или текстом.
Черновик и подтверждение Решили, что модель готовит черновик рецензии с указанием места ошибки, а публикует его преподаватель — проверка идёт через очередь на подтверждение, а не напрямую ученику. Модели вынесли в пул с квотой на школу, а каждую проверку стали сохранять с версией промпта и набором критериев.
Сборка сервиса Собрали приём работы файлом или текстом, разбор по критериям задания, черновик рецензии, очередь на подтверждение преподавателем, витрину тарифов и счётчик проверок. Журнал версий промптов завели вместе с сервисом.
Прогон работ Прогнали архивные работы по критериям заданий и сравнили черновики рецензий с рецензиями преподавателей, отдельно проверив счётчик проверок и исчерпание квоты школы. Рецензия без указания места ошибки в работе и проверка, ушедшая ученику без подтверждения преподавателем, считались провалом.
Передача преподавателям Отдали сервис проверки, тарифы и счётчики, набор критериев, журнал версий промптов и инструкцию преподавателя; с преподавателями прошли подтверждение рецензии. Школа сама заводит критерии нового задания и меняет тариф.
Архитектура
Приём работы файлом или текстом ведёт сервис на C++ поверх Drogon: файл кладётся в MinIO, задача проверки ставится в очередь RabbitMQ, разбор по критериям задания выполняет пул моделей vLLM. Проверка сделана задачей в очереди, а не синхронным вызовом, потому что работы приходят пачками к сроку сдачи, и очередь держит всплеск, раздавая квоту по школам вместо отказа на приёме. Черновик рецензии с указанием места ошибки ложится в PostgreSQL вместе с версией промпта и набором критериев и попадает в очередь на подтверждение преподавателем — ученику показывается только подтверждённая рецензия. Витрина тарифов и счётчик проверок считаются в Redis и сводятся в биллинг подписки, узлы разложены в Kubernetes, длина очередей и время разбора собираются в Prometheus.
Агенты первой линии внутри подписной платформы курсов
Задача, решение и что передали
Задача
Поддержку тянула команда продукта: вопросы про доступы, продление подписки и прогресс разбирали в чате руками.
Решение
Встроили в продукт агентный контур первой линии: коннекторы к чату и почте, поиск по базе знаний и справке с версионированием, сервисный доступ к статусу подписки и журналу занятий, эскалация карточкой обращения. Агент отвечает по данным аккаунта и закрывает типовые случаи — переоткрывает доступ к модулю, повторяет письмо активации, переносит занятие, — остальное отдаёт человеку с собранным контекстом диалога. Качество ответов снимается панелью с выборкой диалогов на ручную оценку.
Сдано 4
Переданы контур первой линии
База знаний продукта с версиями
Журнал обращений и эскалаций
Панель оценки ответов
Как делали
Обследование обращений Разобрали переписку продуктовой команды в чате и почте, разложили обращения по типам — доступы, продление подписки, прогресс по занятиям — и выделили ответы, которые люди повторяют. Собрали то, что могло стать базой знаний: справку, письма активации, внутренние заметки о правилах доступа к модулям.
Архитектура агента Агенту дали сервисный доступ только на чтение статуса подписки и журнала занятий плюс узкий набор действий, всё остальное уходит человеку карточкой обращения. Базу знаний завели с версионированием, чтобы ответ ссылался на конкретную редакцию статьи, а коннекторы к чату и почте отделили от логики разбора.
Разработка контура Сначала подняли коннекторы и карточку обращения с эскалацией, затем поиск по базе знаний и справке, затем действия — переоткрытие доступа к модулю, повтор письма активации, перенос занятия. На стенде прогоняли выборку прошлых диалогов и смотрели, отвечает ли агент по данным аккаунта, а не общими формулировками.
Проверка диалогов Гоняли диалоги с истёкшей подпиской, с чужим аккаунтом в одном чате и с вопросами, ответа на которые в базе знаний нет. Провалом считали ответ про подписку или прогресс без обращения к данным аккаунта и обращение, закрытое агентом там, где требовалась эскалация.
Передача продукту Передали контур первой линии, базу знаний с версиями, журнал обращений и эскалаций и панель оценки ответов, показав, как брать выборку диалогов на ручную оценку. Продуктовая команда сама правит статьи, заводит новые типы обращений и двигает границу эскалации.
Архитектура
Коннекторы чата (WebSocket) и почты (IMAP) складывают обращение в PostgreSQL и будят сервис диалога; поиск по базе знаний идёт через pgvector в той же базе, потому что статья справки и её эмбеддинг пишутся одной транзакцией и версия не расходится с индексом. Состояние диалога и черновик ответа держит Redis с TTL: они читаются и переписываются на каждой реплике и после закрытия обращения не нужны. Данные аккаунта агент берёт у сервиса подписок и журнала занятий по внутреннему HTTP, набор разрешённых действий — переоткрыть модуль, повторить письмо активации, перенести занятие — задан отдельным списком, остальное уходит эскалацией карточкой. Панель качества берёт выборку из журнала обращений; при перезапуске сервиса диалог восстанавливается из журнала сообщений, а не из кэша.
Мобильное приложение с бэкендом и подбором заданий ученику
Задача, решение и что передали
Задача
Задания выдавались фиксированной последовательностью из одного файла курса, а слабые темы преподаватель отмечал вручную после проверки работ.
Решение
Собраны приложение и бэкенд: банк заданий с разметкой по темам и навыкам, профиль ученика с историей попыток, сервис подбора следующего задания и панель методиста. Модель оценивает освоение навыка по последовательности попыток, затраченному времени и типам ошибок и набирает следующую сессию из банка, удерживая долю повторения ранее сданных тем. Панель методиста показывает, какие задания выбираются чаще, и позволяет закрепить тему за курсом вручную.
Сдано 4
Мобильные приложения
Бэкенд подбора
Банк заданий с разметкой навыков
Панель методиста
Как делали
Обследование курса Разобрали файл курса с фиксированной последовательностью заданий, разметили задания по темам и навыкам и собрали историю проверенных работ. С методистами выяснили, по каким признакам преподаватель отмечал слабую тему и какие ошибки считаются однотипными.
Архитектура подбора Банк заданий с разметкой по темам и навыкам отделили от логики выдачи, а профиль ученика стал хранить историю попыток целиком, а не последний результат. Подбор вынесли в службу, которая набирает сессию и удерживает долю повторения ранее сданных тем, оставив методисту возможность закрепить тему за курсом вручную.
Разработка приложения Сначала банк заданий и разметка навыков, затем профиль ученика с историей попыток, затем сервис подбора и мобильные клиенты. Панель методиста делали последней: она показывает, какие задания выбираются чаще, и позволяет вмешаться в состав курса.
Проверка на попытках Прогоняли историю попыток прошлых учеников через подбор и сверяли выбранные темы с тем, что методист отмечал как слабое; отдельно гоняли учеников без истории и с одними верными ответами. Провалом считали сессию без заданий по навыку, где ученик стабильно ошибается, и выпадение повторения ранее сданных тем из набора.
Передача методистам Передали мобильные приложения, бэкенд подбора, банк заданий с разметкой навыков, панель методиста и журнал выданных сессий. Методисты сами заводят задания, правят разметку навыков и закрепляют темы за курсом.
Архитектура
Банк заданий с разметкой по темам и навыкам и история попыток лежат в PostgreSQL, текущая сессия и оценка освоения навыка — в Redis, потому что они читаются и переписываются на каждой попытке и живут до конца сессии. Сервис подбора набирает следующую сессию по оценке навыка, затраченному времени и типам ошибок, удерживая долю повторения ранее сданных тем; модель лёгкая и грузится в процесс сервиса, отдельный узел инференса добавил бы сетевой переход в путь выдачи задания. События попыток уходят в Kafka и питают панель методиста, ручное закрепление темы за курсом пишется в банк заданий и перекрывает выбор модели. Мобильные клиенты работают по REST, при недоступности подбора выдаётся заранее собранный резерв сессии.
Мобильное приложение с бэкендом и подписной оплатой курсов
Задача, решение и что передали
Задача
Курсы продавали через сторонний конструктор разовыми оплатами, продления собирали руками, а доступ к урокам открывали по списку в таблице.
Решение
Написали бэкенд и клиенты под две мобильные платформы: каталог курсов и уроков, прогресс и офлайн-скачивание, push-уведомления, единая авторизация по номеру. Подписочный контур ведёт планы, пробные периоды, рекуррентные списания через эквайринг и магазины приложений, повторные попытки при отказе карты, приостановку и возвраты, доступ к урокам выдаётся правами из состояния подписки, события оплат и просмотров уходят в аналитическую витрину.
Сдано 4
Схема подписочного контура и состояний
Описание бэкенда и мобильных сборок
Регламент рекуррентных списаний и возвратов
Витрина событий
Как делали
Обследование продаж Разобрали, как курсы продавались через сторонний конструктор разовыми оплатами, и увидели, что продления собирались руками, а доступ к урокам выдавался по списку в таблице. Собрали структуру курсов и уроков и выписали правила магазинов приложений для подписок на обеих платформах.
Архитектура подписки Доступ к урокам сделали производным от состояния подписки, а не отдельным списком, и описали состояния явно: пробный период, активная, приостановка, возврат. Рекуррентные списания развели по каналам — эквайринг и магазины приложений, — а события оплат и просмотров отправили в аналитическую витрину отдельным потоком.
Разработка клиентов Собрали бэкенд с каталогом курсов и уроков, единой авторизацией по номеру и прогрессом, затем клиенты под две мобильные платформы с офлайн-скачиванием и push-уведомлениями. Подписочный контур с планами, пробными периодами, повторными попытками при отказе карты, приостановкой и возвратами делали следом, витрину событий — последней.
Проверка списаний Гоняли покупку, продление, отказ карты с повторной попыткой, возврат и восстановление покупок на второй платформе, отдельно проверяли офлайн-доступ к скачанным урокам. Провалом считали открытый урок при неоплаченной или возвращённой подписке и повторное списание после отмены.
Передача поддержке Передали схему подписочного контура и состояний, описание бэкенда и мобильных сборок, регламент рекуррентных списаний и возвратов, витрину событий и инструкции поддержки. Команда сама заводит план и пробный период, поддержка разбирает отказы списаний и возвраты по журналу.
Архитектура
Один бэкенд обслуживает клиентов обеих платформ, собранных из общей кодовой базы React Native; авторизация по номеру, каталог курсов и уроков, прогресс и состояние подписки лежат в PostgreSQL. Уроки для офлайн-скачивания отдаются подписанными ссылками из объектного хранилища, право на ссылку выводится из состояния подписки в момент выдачи, а не из отдельного списка доступов, который пришлось бы синхронизировать. Рекуррентные списания ведёт планировщик с очередью повторов при отказе карты, а вебхуки эквайринга и магазинов приложений принимаются идемпотентно по идентификатору транзакции: магазины повторяют доставку уведомления, и без ключа продление задваивается. События оплат и просмотров уходят очередью в аналитическую витрину, push идут через FCM, Redis держит сессии и счётчики попыток входа.
Перенос подписной платформы обучения в реестр отечественного ПО
Задача, решение и что передали
Задача
Платформа продавалась по подписке из зарубежного облака, и корпоративные заказчики не покупали её без установки в свой контур на отечественной ОС.
Решение
Пересобрали продукт под доверенные ОС: проприетарные компоненты заменили на PostgreSQL, объектное хранилище и собственный сервис транскодирования видео, сборку перевели на внутренний репозиторий с подписанными deb- и rpm-пакетами. Сделали коробочную поставку — установщик, оффлайн-активация, обновление из локального зеркала, интеграция с каталогом пользователей заказчика вместо облачной авторизации. Прошли проверки совместимости на доверенных ОС и собрали комплект документации для внесения в реестр.
Сдано 4
Передали сборочный конвейер и подписанные пакеты
Установщик коробочной поставки
Протоколы совместимости
Комплект документации для реестра
Как делали
Обследование зависимостей Разобрали продукт по зависимостям от зарубежного облака — проприетарная база, облачная авторизация, внешнее транскодирование видео — и сверили их с требованиями корпоративных заказчиков к установке в свой контур. Записали, какие доверенные ОС называют заказчики и что нужно для внесения в реестр.
Архитектура поставки Проприетарные компоненты заменили на PostgreSQL, объектное хранилище и собственный сервис транскодирования видео, облачную авторизацию — на подключение к каталогу пользователей заказчика. Поставку сделали коробочной: подписанные deb- и rpm-пакеты из внутреннего репозитория, оффлайн-активация, обновление из локального зеркала.
Разработка и сборка Сначала перевели хранение и авторизацию, затем собрали сервис транскодирования видео, затем перенесли сборку на внутренний репозиторий с подписанием пакетов. Установщик, оффлайн-активацию и обновление из зеркала обкатывали на стендах с доверенными ОС, отрезанных от интернета.
Проверка установки Ставили коробку с нуля на каждую заявленную доверенную ОС без доступа в интернет и прогоняли загрузку и транскодирование курса, вход через каталог пользователей заказчика, активацию и обновление из локального зеркала. Провалом считали установку, потребовавшую выхода в интернет, и пакет без подписи из внутреннего репозитория.
Передача коробки Отдали сборочный конвейер и подписанные пакеты, установщик коробочной поставки, протоколы совместимости, комплект документации для реестра и инструкцию обновления без интернета. Команда заказчика сама собирает и подписывает новую версию и раздаёт её клиентам через зеркало.
Архитектура
Веб-слой на Django и API подписки работают поверх PostgreSQL, исходные видео и HLS-сегменты лежат в MinIO по S3-протоколу, метаданные курсов — в базе. Транскодирование вынесено в отдельный пул воркеров Celery: кодирование занимает ядра на минуты, и в общем процессе с веб-слоем оно забирало бы обработчики запросов, поэтому задание ставится в очередь, а прогресс возвращается в карточку курса. Проприетарные компоненты заменены на FFmpeg и собственный сервис транскодирования, авторизация ходит в LDAP-каталог заказчика вместо облачного провайдера, лицензия проверяется локально по подписи без обращения наружу. Сборка идёт во внутреннем репозитории подписанными deb- и rpm-пакетами, установка и обновление из локального зеркала раскатываются Ansible, совместимость проверена на Astra Linux.
Контур допустимого ответа ИИ-тьютора с полным журналом занятий
Задача, решение и что передали
Задача
Тьютор на языковой модели отвечал ученикам напрямую, а на жалобу родителя восстановить сам диалог и причину ответа было нечем.
Решение
Поставили перед моделью и после неё два фильтра: на входе разбирается запрос на темы вне занятия и попытки увести тьютора инструкцией, на выходе ответ проверяется по возрастной политике, списку закрытых тем и границе учебной программы. Диалог пишется целиком — запрос, версия правил, вердикт фильтра, ответ — и открывается методисту и родителю из карточки занятия. Правила ведёт методическая служба в редакторе с версиями, выкладка новой версии не требует обновления приложения.
Сдано 4
Входной и выходной фильтры
Редактор правил с версиями
Полный журнал диалогов
Доступ методиста и родителя
Как делали
Обследование жалоб Подняли жалобы родителей и попробовали восстановить диалоги с тьютором — от переписки оставались обрывки. Разобрали с методистами, что тьютор имеет право отвечать: границы учебной программы, возрастная политика, список закрытых тем.
Архитектура фильтров Поставили два фильтра: на входе разбор запроса на темы вне занятия и попытки увести тьютора инструкцией, на выходе проверку ответа по возрастной политике, списку закрытых тем и границе учебной программы. Диалог пишется целиком — запрос, версия правил, вердикт фильтра, ответ — и открывается из карточки занятия; правила ведёт методическая служба в редакторе с версиями, выкладка не требует обновления приложения.
Разработка журнала Сначала журнал диалогов с привязкой к занятию и версии правил, затем входной фильтр, затем выходная проверка по возрастной политике и закрытым темам. Редактор правил с версиями и доступ методиста и родителя к диалогу из карточки занятия собирали после, на стенде с записями прошлых занятий.
Проверка ответов Прогоняли ученические запросы вне темы занятия и попытки увести тьютора инструкцией, проверяли ответы на закрытые темы для разных возрастных групп, поднимали диалог по жалобе из карточки занятия. Не приняли бы работу, если бы ответ вне возрастной политики дошёл до ученика или если бы по жалобе не поднимался диалог с версией правил и вердиктом фильтра.
Передача контура Отдали входной и выходной фильтры, редактор правил с версиями, полный журнал диалогов, доступ методиста и родителя и регламент разбора жалобы. Методическая служба сама правит правила и выкладывает новую версию, разбор жалобы идёт по регламенту из карточки занятия.
Архитектура
Фильтры стоят до и после модели отдельным сервисом на C++ с интерфейсом gRPC: входной разбирает запрос на темы вне занятия и попытки увести тьютора инструкцией, выходной проверяет ответ по возрастной политике, списку закрытых тем и границе учебной программы. Классификаторы исполняются через ONNX Runtime в том же процессе, шаблоны — через RE2, потому что вынос классификации в отдельный сервис добавлял бы сетевой прыжок на каждый ход диалога. Правила ведёт методическая служба в редакторе, версии лежат в PostgreSQL, действующая раздаётся через Redis, поэтому выкладка новой версии не требует обновления приложения. Диалог пишется целиком — запрос, версия правил, вердикт фильтра, ответ — в PostgreSQL с поиском через OpenSearch, события идут в Kafka, карточка занятия открывается методисту и родителю; развёртывание — Kubernetes.
Витрина использования SaaS-платформы в разрезе арендаторов
Задача, решение и что передали
Задача
Любой вопрос об использовании платформы конкретным арендатором закрывался ручной выгрузкой из базы и сборкой сводки в таблице.
Решение
Разделили события платформы по арендаторам и ролям, ввели сквозной ключ арендатора во все таблицы фактов и проверили его заполнение на исторических данных. Собрали витрину: слой событий, слой подписок и лимитов тарифа, слой активности по ролям, срезы по арендатору, тарифу и модулю. Заложили изоляцию: политики доступа на уровне строк, отдельные наборы дашбордов для внутренних команд и для кабинета арендатора, регламент ежедневного обновления.
Сдано 4
Модель витрины использования
Словарь метрик
Ключ арендатора во всех фактах
Политики доступа к строкам
Как делали
Обследование выгрузок Разобрали, как закрывался вопрос об использовании платформы конкретным арендатором — ручная выгрузка из базы и сводка в таблице — и проверили, в каких таблицах фактов ключа арендатора вообще нет. Собрали, что спрашивают внутренние команды и что арендатор должен видеть у себя.
Архитектура витрины Ввели сквозной ключ арендатора во все таблицы фактов и разложили витрину на слои: события, подписки и лимиты тарифа, активность по ролям — со срезами по арендатору, тарифу и модулю. Изоляцию заложили политиками доступа на уровне строк и разными наборами дашбордов — для внутренних команд и для кабинета арендатора.
Разработка слоёв Сначала проставили ключ арендатора и проверили его заполнение на исторических данных, затем собрали слой событий и слой подписок с лимитами, затем активность по ролям. Два набора дашбордов и ежедневное обновление отрабатывали на стенде с копией событий платформы.
Проверка изоляции Сравнивали срезы витрины с прежними ручными выгрузками по нескольким арендаторам, искали записи без ключа арендатора в исторических данных и заходили в дашборды кабинета от лица разных арендаторов. Не приняли бы витрину, в которой арендатор видит строку другого арендатора, и факт, оставшийся после загрузки без ключа арендатора.
Передача витрины Передали модель витрины использования, словарь метрик, ключ арендатора во всех фактах, политики доступа к строкам и регламент обновления. Продуктовая команда сама добавляет метрику в словарь и открывает арендатору его набор дашбордов.
Архитектура
События платформы уходят в Kafka с ключом арендатора, приёмник на Spring Boot проставляет tenant_id на входе и пишет в ClickHouse, событие без ключа отбивается на приёме, а исторические таблицы проверены отдельной сверкой заполнения. Витрины строятся dbt тремя слоями — события, подписки и лимиты тарифа, активность по ролям — и режутся срезами по арендатору, тарифу и модулю. Изоляция сделана политиками на уровне строк по tenant_id, а кабинет арендатора ходит в витрину через сервис, подставляющий его идентификатор, вместо прямого подключения к базе — иначе разграничение зависело бы от текста запроса каждого дашборда. Справочники подписок и словарь метрик лежат в PostgreSQL, дашборды внутренних команд собраны в Superset, ежедневное обновление ведёт Airflow, окружение — Docker.
Проверяется: журнал решений и очередь ручного разбора
Агент квалификации входящих заявок из почты и форм
Задача, решение и что передали
Задача
Заявки на подряд падали в общий почтовый ящик и в форму на сайте, разбирали их руками между делом, часть писем оставалась без ответа.
Решение
Агент забирает письма по IMAP и заявки с формы, извлекает объект, тип работ, объём, регион и срок, приводит значения к справочникам и сверяет с правилами квалификации. Подходящие заявки создаются сделкой в CRM через API с заполненными полями, спорные автоматически не заводятся, а уходят в очередь на разбор человеком с пометкой, какое условие не сошлось. Каждое решение пишется в журнал вместе с исходным письмом и извлечёнными полями, поэтому разбор спорного случая начинается с текста, а не с догадок.
Сдано 4
Сервис агента
Правила квалификации в редактируемом виде
Очередь ручного разбора
Журнал решений и инструкция для менеджеров
Как делали
Разбор ящика Разобрали общий почтовый ящик и заявки с формы: как выглядят письма на подряд, что в них указывают сразу, а что приходится уточнять. С менеджерами выписали правила квалификации, по которым они на самом деле отсеивали заявки, и нашли письма, оставшиеся без ответа.
Правила отдельно Извлечение полей — объект, тип работ, объём, регион — отделили от правил квалификации, а сами правила положили в редактируемом виде, чтобы их менял менеджер, а не разработчик. Сделка в CRM создаётся только по подошедшей заявке; спорная автоматически не заводится, а уходит в очередь на разбор с пометкой, какое условие не сошлось.
Сборка агента Сначала приём писем по IMAP и заявок с формы и извлечение полей, затем приведение значений к справочникам объектов, работ и регионов, затем сверка с правилами и создание сделки через API CRM. Журнал решений с исходным письмом и извлечёнными полями делали сразу, а не по итогам.
Прогон архива Прогнали архив писем и сравнили решения агента с тем, как эти же заявки разбирали менеджеры. Провалом считали заявку, заведённую сделкой при несошедшемся условии, потерянное письмо и решение, которое нельзя разобрать по журналу вместе с исходным текстом.
Передача менеджерам Передали сервис агента, правила квалификации в редактируемом виде, очередь ручного разбора, журнал решений и инструкцию для менеджеров. Менеджеры правят условия квалификации и справочники сами и разбирают спорные заявки, начиная с текста письма.
Архитектура
Два приёмника кладут работу в одну очередь: сборщик почты по IMAP и обработчик формы на Axum публикуют письмо и вложения в NATS, дальше воркеры ведут их по общему конвейеру. Извлечение полей выполняет локальная модель за vLLM отдельным сервисом — генерация занимает секунды, держать на ней сессию IMAP или запрос формы нельзя, а очередь переживает перезапуск воркера и не теряет письмо. Исходное письмо, извлечённые поля, применённые правила и решение пишутся одной транзакцией в PostgreSQL, справочники объектов и типов работ лежат там же и подтягиваются в Redis как кеш нормализации. Сделка создаётся в CRM по её API с ключом идемпотентности из идентификатора письма, поэтому повтор после таймаута не заводит вторую; спорная заявка в CRM не уходит, а остаётся в очереди разбора с пометкой несошедшегося условия, длина очередей выведена в Grafana.
Вывод модели прогноза потребления в промышленную эксплуатацию
Задача, решение и что передали
Задача
Прогноз считался в ноутбуке аналитика по запросу, а о том, что модель поплыла, узнавали из расхождения в отчётах.
Решение
Обучение и расчёт вынесли в расписание, версии данных, кода и модели фиксируются в реестре, каждый прогноз хранится вместе с версией, которой он сделан. На следующем цикле прогноз сверяется с фактом, метрики уходят в мониторинг, при выходе за заданный коридор поднимается алерт и расчёт переключается на простую резервную модель. Откат на предыдущую версию делается одной командой, без пересборки образа.
Сдано 4
Конвейеры обучения и инференса
Реестр моделей
Дашборд качества с алертами
Процедура отката и регламент дежурства
Как делали
Разбор расчёта Посмотрели, как прогноз потребления считался в ноутбуке аналитика: какие выгрузки он брал руками, какие параметры менял и что нигде не сохранялось. Разобрали случаи, когда о расхождении узнавали из отчётов, а не из системы.
Версии и резерв Обучение и расчёт вынесли в расписание, версии данных, кода и модели фиксируются в реестре, а каждый прогноз хранится вместе с версией, которой сделан. На случай выхода метрик за коридор предусмотрели простую резервную модель и откат на предыдущую версию одной командой, без пересборки образа.
Сборка конвейеров Собрали конвейеры обучения и инференса по расписанию, реестр моделей, сверку прогноза с фактом на следующем цикле и выгрузку метрик в мониторинг с алертами. Переключение на резервную модель отрабатывали на стенде до боевого контура.
Проверка воспроизводимости Прогнали конвейер на исторических выгрузках и повторили расчёт по сохранённой версии данных и кода. Провалом считали прогноз, который не воспроизводится по записанной версии, выход метрики за коридор без алерта и откат, потребовавший пересборки образа.
Передача дежурным Передали конвейеры обучения и инференса, реестр моделей, дашборд качества с алертами, процедуру отката и регламент дежурства. Дежурная смена сама переключает расчёт на резервную модель и возвращает предыдущую версию.
Архитектура
Расчёт вынесен из ноутбука в две ветки Airflow — обучение по расписанию и регулярный инференс, — обе обращаются к сервису на Axum, который держит модель в ONNX Runtime и отдаёт прогноз. Ряды потребления и факт лежат в гипертаблице TimescaleDB, версии данных, кода и модели — в реестре MLflow, каждый прогноз пишется вместе с идентификатором версии, которой сделан: без этой привязки нельзя разделить расхождение по данным и расхождение по выкатке. Сверка с фактом идёт на следующем цикле, метрики уходят в Prometheus, при выходе за коридор поднимается алерт и сервис переключается на резервную модель — флаг переключения читается из PostgreSQL на каждом запросе, поэтому смена не требует пересборки образа. Готовые прогнозы публикуются событием в Kafka для смежных систем, откат сводится к установке предыдущей версии в реестре, контейнер сервиса остаётся тем же.
Проверяется: журнал операций и набор проверок целостности
Шлюз переноса моделей и обновлений в изолированный контур
Задача, решение и что передали
Задача
Веса моделей и образы сервисов заносили в закрытый сегмент на съёмных носителях вручную, происхождение файла и его целостность подтверждались только на словах.
Решение
Собрали двухзонный шлюз: во внешней зоне файл сверяется с контрольной суммой и подписью поставщика, распаковывается в карантин, формат весов разбирается без исполнения кода — safetensors вместо pickle. Передача во внутреннюю зону идёт в одну сторону, оператор подтверждает перенос вручную, каждая попытка пишется в журнал с автором, хешем и результатом проверок. Внутри контура принятый образ раскладывается по локальному репозиторию и подхватывается стендом инференса плейбуком Ansible.
Сдано 4
Передали шлюз с внешней и внутренней зонами
Регламент переноса
Журнал операций
Набор проверок целостности и плейбуки развёртывания моделей
Как делали
Разбор переноса Разобрали, как веса моделей и образы сервисов заносили в закрытый сегмент на съёмных носителях: кто приносил файл, чем подтверждалось происхождение и что оставалось на словах. Выписали, какие форматы весов и образов приходят от поставщиков на деле.
Две зоны Шлюз развели на внешнюю и внутреннюю зоны: во внешней файл сверяется с контрольной суммой и подписью поставщика и распаковывается в карантин, формат весов разбирается без исполнения кода — от pickle отказались в пользу safetensors. Передача во внутреннюю зону идёт в одну сторону, перенос подтверждает оператор вручную.
Сборка шлюза Собрали внешнюю зону с проверками и карантином, затем одностороннюю передачу и журнал с автором, хешем и результатом проверок, затем раскладку принятого образа по локальному репозиторию и подхват стендом инференса плейбуком Ansible.
Проверка на подмене Пропускали через шлюз подготовленные файлы: с изменённым хешем, без подписи поставщика, с весами в исполняемом формате. Провалом считали любой файл, попавший во внутреннюю зону без подтверждения оператора или без записи в журнале, и разбор весов, при котором исполняется код из файла.
Передача операторам Передали шлюз с внешней и внутренней зонами, регламент переноса, журнал операций, набор проверок целостности и плейбуки развёртывания моделей. Операторы переносят обновления и разворачивают модели в контуре сами.
Архитектура
Шлюз состоит из двух узлов в разных сетях: приёмник внешней зоны и приёмник внутренней, между ними односторонний канал, по которому файл уходит rsync после ручного подтверждения оператора. Во внешней зоне сервис на FastAPI кладёт файл в карантинный бакет MinIO, сверяет контрольную сумму и подпись поставщика через GPG и разбирает заголовок весов в формате safetensors: этот формат читается как данные, тогда как pickle при разборе исполняет код, и распаковка такого архива была бы запуском чужого кода на шлюзе. Журнал попыток с автором, хешем, результатами проверок и решением оператора пишется в PostgreSQL на обоих узлах, во внутренней зоне — только на добавление. Принятый образ раскладывается по локальному реестру Harbor и подхватывается стендом инференса плейбуком Ansible; отказ внутреннего узла не открывает обратный канал — передача остаётся односторонней и повторяется с начала.
Поиск по проектной документации с проверкой прав до выдачи
Задача, решение и что передали
Задача
Прототип помощника отвечал сразу по всему архиву проектов и показывал фрагменты пояснительных записок и смет тем, у кого доступа к объекту не было.
Решение
Перестроили конвейер поиска: при индексации к каждому фрагменту прикрепляются права исходного хранилища — объект, папка, список групп, а фильтр по правам пользователя применяется до ранжирования, а не к уже собранному ответу. Ответ формируется только из разрешённых фрагментов и сопровождается ссылками на исходные файлы с указанием разделов. Изменение прав в хранилище приходит событием и переиндексирует затронутые документы, до пересборки фрагмент считается недоступным.
Сдано 4
Передали сервис поиска
Схему хранения прав
Обработчик событий переиндексации
Набор тестов на видимость документов и инструкцию администратора
Как делали
Воспроизведение утечки Воспроизвели поведение прототипа и показали, кому именно он выдавал фрагменты пояснительных записок и смет по чужим объектам. Разобрали, как устроены права в исходном хранилище: объект, папка, список групп доступа.
Права до ранжирования Фильтр по правам перенесли на этап отбора, до ранжирования, а не на готовый ответ, и при индексации стали прикреплять к каждому фрагменту права исходного хранилища. Изменение прав приходит событием и переиндексирует затронутые документы, до пересборки фрагмент считается недоступным.
Перестройка конвейера Перестроили индексацию с записью прав, затем отбор с фильтром до ранжирования, затем обработчик событий переиндексации и формирование ответа со ссылками на исходные файлы и разделы.
Тесты видимости Прогнали набор тестов видимости: одни и те же вопросы под учётными записями с разными объектами, снятие и выдача прав с проверкой сразу после события. Провалом считали появление фрагмента закрытого объекта в ответе или ссылках и доступность документа после снятия прав до переиндексации.
Передача администратору Передали сервис поиска, схему хранения прав, обработчик событий переиндексации, набор тестов на видимость документов и инструкцию администратора. Администраторы прогоняют тесты видимости сами после изменений в хранилище проектов.
Архитектура
Поисковое ядро — сервис на C++, принимающий запросы по gRPC от веб-части: при индексации к каждому фрагменту прикрепляются права исходного хранилища — объект, папка, список групп, — они лежат отдельной таблицей в PostgreSQL рядом с векторами в pgvector. Фильтр по группам пользователя из LDAP подставляется в условие выборки до ранжирования, а не отсекает уже собранный ответ, потому что иначе запрещённый фрагмент успевает попасть в контекст модели и проступает в тексте. Изменение прав в хранилище приходит событием в Kafka, обработчик переиндексирует затронутые документы, а до пересборки фрагмент помечен недоступным — при расхождении прав закрытая выдача предпочтительнее открытой. Эмбеддинги считает отдельный процесс на ONNX Runtime, узлы подняты контейнерами; при падении индексатора события остаются в топике и дочитываются со смещения, набор тестов на видимость документов гоняется после каждой переиндексации.
Заявки на ремонт приходили механику в мессенджер и по телефону, история работ по единице техники нигде не собиралась.
Решение
Завели единый реестр техники с наработкой и вывели заявку в жизненный цикл: регистрация, диагностика, наряд, списание запчастей, закрытие с подтверждением мастера. Плановое обслуживание встаёт автоматически по наработке или календарю, заявка без нужной позиции на складе переходит в ожидание и держит резерв. По каждой единице накапливается история отказов и выполненных работ.
Сдано 4
Передали реестр техники
Модуль заявок и нарядов
Мобильную форму мастера
Регламент планового обслуживания и отчёты по истории ремонтов
Как делали
Разбор заявок Собрали, как заявка доходит до механика — мессенджер и телефон — и что от неё остаётся после закрытия. Сверили парк с бухгалтерским перечнем и убедились, что наработка, отказы и заменённые запчасти по единице техники нигде не сходятся.
Жизненный цикл Завели единый реестр техники с наработкой и вывели заявку в жизненный цикл: регистрация, диагностика, наряд, списание запчастей, закрытие с подтверждением мастера. Плановое обслуживание встаёт автоматически по наработке или календарю, а заявка без нужной позиции на складе не закрывается, а переходит в ожидание и держит резерв.
Сборка модуля Собрали реестр техники и заявки, затем наряды и списание запчастей со склада, затем мобильную форму мастера для работы на объекте и правила планового обслуживания.
Прогон цикла Прогнали заявки по всему жизненному циклу на реальном парке, включая случаи, когда запчасти на складе нет. Провалом считали заявку, закрытую без подтверждения мастера, списание запчасти мимо наряда и плановое обслуживание, не вставшее по достигнутой наработке.
Передача механикам Передали реестр техники, модуль заявок и нарядов, мобильную форму мастера, регламент планового обслуживания и отчёты по истории ремонтов. Механики и мастера ведут заявки сами, снабжение видит резерв под ожидающие заявки.
Архитектура
Реестр техники, заявки и наряды живут в одном сервисе на ASP.NET Core с PostgreSQL, мобильная форма мастера собрана на .NET MAUI и ходит в тот же HTTP API. Заявка проведена конечным автоматом — регистрация, диагностика, наряд, списание запчастей, закрытие: переходы описаны таблицей и проверяются на сервере, поэтому мобильный клиент не закрывает заявку в обход подтверждения мастера даже при своей версии сборки. Плановое обслуживание ставит планировщик задачей через RabbitMQ, а не фоновым потоком приложения, потому что пересчёт наработки по всему парку идёт долго и должен переживать перезапуск сервиса. Резерв запчасти под заявку в ожидании держится строкой в PostgreSQL с блокировкой на время подбора, остатки и списания синхронизируются с 1С через OData; вход через Keycloak, справочники кешируются в Redis, узлы подняты контейнерами и состояния в памяти не держат.
Инференс языковых моделей на собственных GPU-узлах предприятия
Задача, решение и что передали
Задача
Ассистент по регламентам и нарядам-допускам работал на арендованных мощностях, и текст запросов с промысла уходил за периметр вместе с содержанием документов.
Решение
Собрали пул GPU-узлов и подняли инференс-сервер с непрерывным батчингом и вытесняющей очередью: диспетчерские обращения идут в приоритетном классе, пакетная обработка отчётов — в фоновом. Веса раздаются из внутреннего реестра с проверкой хеша, версия закрепляется за окружением. Узлы держат горячий резерв: при отказе карты запрос переигрывается на соседнем узле.
Сдано 4
Передали инференс-кластер
Реестр весов с проверкой хеша
Регламент приоритетов очереди
Панели загрузки карт
Как делали
Обследование Собрали профиль обращений ассистента по регламентам и нарядам-допускам: короткие диспетчерские запросы с промысла и пакетная обработка отчётов, посмотрели длины промптов и ответов на арендованном контуре. Разобрали, какие карты и узлы есть в машинном зале и как заведено сетевое разделение.
Архитектура Инференс подняли пулом узлов с непрерывным батчингом и вытесняющей очередью: диспетчерские обращения в приоритетном классе, отчёты в фоновом. Веса раздаём из внутреннего реестра с проверкой хеша, версия закреплена за окружением; от общей очереди без классов отказались, чтобы пакетная обработка не вытесняла диспетчера.
Разработка Сначала подняли узел с реестром весов и API, потом добавили классы очереди и вытеснение, потом горячий резерв с переигрыванием запроса на соседнем узле. На стенде держали нагрузку по суточному профилю и вынимали карту из узла на ходу.
Проверка Сравнивали ответы с прежним арендованным контуром на одинаковых запросах, гоняли нагрузку вместе с пакетной обработкой отчётов, отключали узел под нагрузкой. Провалом считали диспетчерский запрос, вставший в очередь за отчётами, и потерю запроса при отказе карты вместо переигрывания на соседнем узле.
Передача Передали инференс-кластер, реестр весов с проверкой хеша, регламент приоритетов очереди и панели загрузки карт. Эксплуатации показали ввод нового узла и подмену версии модели без остановки сервиса.
Архитектура
Перед пулом GPU-узлов стоит Rust-роутер: он терминирует OpenAI-совместимый API, ведёт приоритетную очередь диспетчерских обращений и фоновую очередь пакетных отчётов и раздаёт запросы движкам vLLM, следя за длиной батча каждого узла. Непрерывный батчинг живёт внутри движка, а вытеснение фоновых запросов — в роутере, потому что решение о приоритете требует знать состояние всего пула, а узел видит только свою карту. Веса лежат во внутреннем реестре артефактов: узел при старте тянет их и сверяет хеш, версия закрепляется за окружением меткой в манифесте Kubernetes. Состояние запроса роутер держит в памяти со снимком в Redis, поэтому при отказе карты или узла запрос переигрывается на соседнем, а не теряется вместе с процессом; загрузка карт и длины очередей снимаются Prometheus.
Подсказки диспетчеру по регламенту при технологическом нарушении
Задача, решение и что передали
Задача
При технологическом нарушении диспетчер восстанавливал порядок действий по памяти, а список уведомляемых служб держал в бумажной папке.
Решение
Агент слушает поток событий телемеханики и заявок, распознаёт тип нарушения и открывает диспетчеру карточку с шагами регламента, перечнем уведомляемых служб и заготовленным текстом сообщения. Шаги привязаны к текущей схеме сети: пункты, не относящиеся к состоянию оборудования, в карточке не показываются. Команду на переключение агент не отдаёт — показ подсказки и решение диспетчера пишутся в журнал смены.
Сдано 4
Карточки регламентов по типам нарушений
Сервис подсказок
Привязка шагов к схеме сети
Журнал смены и инструкция диспетчера
Как делали
Обследование Посидели на смене в диспетчерской и разобрали бумажную папку с перечнем уведомляемых служб. Собрали регламенты по типам технологических нарушений и посмотрели, какие события телемеханики и заявки приходят в момент нарушения.
Архитектура Агент только слушает поток событий и заявок и показывает карточку — команду на переключение не отдаёт, решение остаётся за диспетчером. Шаги регламента привязали к текущей схеме сети, чтобы пункты по выведенному оборудованию не показывались, а показ подсказки и решение диспетчера пишем в журнал смены.
Разработка Сначала приём потока телемеханики и заявок с распознаванием типа нарушения, затем карточка с шагами регламента и перечнем уведомляемых служб, затем заготовленный текст сообщения и журнал смены. На стенде проигрывали записанные события прошлых нарушений.
Проверка Проигрывали архив технологических нарушений и сверяли карточку с действиями диспетчера по регламенту, отдельно смотрели изменённую схему сети и поток событий подряд. Провалом считали шаг, показанный по оборудованию, выведенному из схемы, и любую попытку сервиса выдать команду на переключение.
Передача Передали карточки регламентов по типам нарушений, сервис подсказок, привязку шагов к схеме сети и журнал смены. Диспетчеров учили работать с карточкой, службе режимов — править шаги регламента и перечень уведомляемых служб.
Архитектура
Поток телемеханики снимается адаптером OPC UA и публикуется в Kafka, оттуда Go-сервис правил читает события вместе с заявками и сопоставляет их с типами технологических нарушений. Kafka стоит между адаптером и правилами потому, что один поток читают несколько потребителей — правила, архив телеметрии и панель, — а при прямом вызове адаптер знал бы про каждого получателя и повторял отправку сам. Текущая схема сети и состояния оборудования держатся проекцией потока в памяти сервиса, карточки регламентов и журнал смены — в PostgreSQL, архив измерений — в TimescaleDB; пункты, не относящиеся к текущему состоянию, отсекаются до показа. Карточка приходит на рабочее место диспетчера по WebSocket, канал в сторону телемеханики остаётся только на чтение: команду на переключение сервис не отдаёт, а показ подсказки и решение диспетчера пишутся в журнал смены.
Стек
Go · Gin · PostgreSQL · TimescaleDB · Apache Kafka · Kubernetes · OPC UA · WebSocket · Grafana
Приём заявок жителей из мессенджера в диспетчерскую
Задача, решение и что передали
Задача
Жители писали в чат дома и звонили диспетчеру, поэтому часть обращений оседала в переписке и в систему не попадала.
Решение
Агент читает сообщения из мессенджера, определяет адрес и подъезд по лицевому счёту жителя, относит обращение к виду работ по рубрикатору и заводит заявку в диспетчерской системе с исполнителем и сроком. Сообщения об одной поломке склеиваются по адресу, виду работ и окну времени и прикрепляются к открытой заявке комментарием. Сообщение без адреса или с несколькими темами уходит диспетчеру в очередь уточнения.
Сдано 4
Бот приёма обращений
Рубрикатор видов работ
Правила склейки повторов
Очередь уточнения и журнал заведённых заявок
Как делали
Обследование Подняли переписку чатов домов и записи звонков диспетчеру и увидели, какая часть обращений не доезжает до системы. Разобрали, как житель привязан к лицевому счёту, адресу и подъезду и по каким видам работ диспетчерская заводит заявки.
Архитектура Адрес и подъезд определяем по лицевому счёту жителя, а не по тексту сообщения, вид работ берём из рубрикатора диспетчерской, чтобы заявка сразу шла к своему исполнителю. Сообщения об одной поломке склеиваем по адресу, виду работ и окну времени и прикрепляем комментарием к открытой заявке, а не заводим новую.
Разработка Сначала бот и привязка жителя к лицевому счёту, затем рубрикация обращения и заведение заявки в диспетчерской системе, затем склейка повторов и очередь уточнения. На стенде прогоняли выгрузку сообщений из чатов домов.
Проверка Прогнали архив переписки, включая жалобы на одну поломку от разных жителей и сообщения без адреса или с несколькими темами. Провалом считали заявку, заведённую на чужой адрес или подъезд, и вторую заявку по уже открытой поломке вместо комментария.
Передача Передали бот приёма обращений, рубрикатор видов работ, правила склейки повторов и очередь уточнения. Диспетчерам показали разбор очереди уточнения и журнал заведённых заявок, администратору — правку рубрикатора и окна склейки.
Архитектура
Бот на Rust принимает обновления Telegram Bot API вебхуком, а не длинным опросом: вебхук отдаёт сообщение сразу и не требует держать опрос по каждой подключённой группе домов. Сообщение кладётся в очередь, обработчик определяет лицевой счёт, адрес и подъезд по привязке отправителя, относит обращение к виду работ по рубрикатору и заводит заявку в диспетчерской системе через её REST-сервис. Заявки, привязки жителей и журнал заведения лежат в PostgreSQL, а склейка повторов держится на частичном уникальном индексе по ключу «адрес, вид работ, окно времени», поэтому второе сообщение о той же поломке прикрепляется комментарием к открытой заявке, а не создаёт новую. Сообщение без адреса или с несколькими темами уходит диспетчеру в очередь уточнения, ответ жителю возвращается в тот же чат.
Модель раннего обнаружения отклонений насосных агрегатов
Задача, решение и что передали
Задача
Отклонение в работе агрегата видели по уставкам, когда параметр уже выходил за границу и срабатывала защита.
Решение
Телеметрия вибрации, давления и температуры приводится к единой шкале времени, участки остановов и переходных режимов размечаются отдельно. Модель нормального поведения обучена по режимам работы и оценивает отход от режима, а не превышение уставки. Событие уходит в диспетчерскую с вкладом каждого датчика и ссылкой на окно телеметрии.
Сдано 4
Переданы модель нормального поведения
Разметка режимов агрегата
Карточка события с вкладом датчиков
Регламент реакции диспетчера
Как делали
Обследование Подняли телеметрию вибрации, давления и температуры по агрегатам и увидели, что уставки срабатывают, когда параметр уже вышел за границу. Вместе с механиками разметили в архиве остановы, пуски и переходные режимы.
Архитектура Учим модель нормального поведения по режимам работы и оцениваем отход от режима, а не превышение уставки — иначе события повторяли бы существующие защиты. Телеметрию приводим к единой шкале времени до расчёта, участки остановов и переходных режимов держим размеченными отдельно и в обучение не берём.
Разработка Сначала приведение телеметрии к единой шкале и разметка режимов, затем модель нормального поведения по режимам, затем карточка события с вкладом каждого датчика и ссылкой на окно телеметрии. На стенде прогоняли архив по агрегатам, где отказ уже был.
Проверка Проигрывали телеметрию вокруг известных отказов и ровные периоды работы, отдельно смотрели пуски и переходы между режимами. Провалом считали событие на штатном пуске агрегата и карточку без вклада датчиков: по такой диспетчер не понял бы, что смотреть.
Передача Передали модель нормального поведения, разметку режимов агрегата, карточку события с вкладом датчиков и регламент реакции диспетчера. Диспетчерам показали разбор события по окну телеметрии, механикам — досборку разметки режимов после ремонта.
Архитектура
Телеметрия вибрации, давления и температуры снимается адаптером OPC UA и пишется в TimescaleDB: гипертаблица по времени со сжатием старых чанков отдаёт окно по тегу без полного скана, а приведение к единой шкале делается запросом в самой базе, а не выгрузкой в файлы. Разметка режимов — остановы, переходные участки, установившаяся работа — хранится отдельной таблицей интервалов и накладывается на телеметрию при формировании выборки: модель нормального поведения учится по режимам, иначе переходный участок она принимает за отклонение. Оценка отхода от режима считается сервисом по скользящему окну и оформляется событием с вкладом каждого датчика и ссылкой на окно телеметрии. События уходят в диспетчерскую через её API, адаптер и сервис развёрнуты контейнерами на узле в контуре промысла, а при обрыве связи события копятся локально и досылаются по восстановлении.
Вывод модели поиска аномалий потребления воды в эксплуатацию
Задача, решение и что передали
Задача
Модель поиска утечек и занижений показаний проверялась разово на выгрузке, рабочего расчёта по всем домам не было.
Решение
Расчёт поставлен на расписание по данным приборов учёта: общедомовое показание сверяется с суммой квартирных и с профилем прошлых периодов. Кандидаты ранжируются по устойчивости отклонения, а не по разовому всплеску, и уходят нарядом в диспетчерскую службу. Итог обхода возвращается меткой в обучающую выборку, пороги пересматриваются по регламенту.
Сдано 4
Переданы сервис расчёта по расписанию
Наряды в диспетчерской службе
Обратная связь обхода в обучающей выборке
Регламент пересмотра порогов
Как делали
Обследование Разобрали разовую проверку модели на выгрузке и посмотрели, как приходят показания общедомовых и квартирных приборов учёта и где в них пропуски. В диспетчерской службе уточнили, как сейчас выглядит наряд на обход.
Архитектура Расчёт поставили на расписание по данным приборов учёта: общедомовое показание сверяется с суммой квартирных и с профилем прошлых периодов. Кандидатов ранжируем по устойчивости отклонения, а не по разовому всплеску — иначе наряды уходили бы на сбои передачи показаний, а итог обхода возвращается меткой в обучающую выборку.
Разработка Сначала сборка показаний по домам и расчёт по расписанию, затем ранжирование кандидатов по устойчивости отклонения, затем наряды в диспетчерской службе и приём обратной связи обхода. На стенде считали по архиву показаний домов, где утечки и занижения уже подтверждались.
Проверка Прогнали расчёт по архиву и сверили кандидатов с подтверждёнными обходами, отдельно смотрели дома с пропусками в передаче показаний. Провалом считали наряд, выданный на разовый сбой передачи показаний, и подтверждённое занижение, не попавшее в список кандидатов.
Передача Передали сервис расчёта по расписанию, наряды в диспетчерской службе, возврат обратной связи обхода в обучающую выборку и регламент пересмотра порогов. Диспетчерам показали работу с нарядом, аналитику — пересмотр порогов по итогам обходов.
Архитектура
Расчёт поставлен на расписание: DAG Airflow запускает C++-задачу, которая читает показания приборов учёта за период из PostgreSQL и сверяет общедомовое значение с суммой квартирных и с профилем прошлых периодов по этому дому. Ядро считает пакетом по всем домам за один проход подготовленными выборками — обход дом за домом отдельными запросами упирался бы в число обращений к базе, а не в сам счёт. Кандидаты ранжируются по устойчивости отклонения на нескольких периодах, а не по разовому всплеску, и выгружаются нарядом в диспетчерскую службу через её REST-сервис. Итог обхода возвращается меткой в таблицу обучающей выборки, пороги хранятся версией в конфигурации и пересматриваются по регламенту, а каждый прогон с параметрами пишется в журнал расчётов.
Платёжки выгружали файлом в клиент-банк, а выписку разносили по договорам на следующий день по распечатке.
Решение
Настроен прямой обмен с банком по защищённому каналу: платёжное поручение уходит из учётной системы с подписью, статус исполнения возвращается в тот же документ. Выписка забирается по расписанию и разносится по договорам и объектам правилами сопоставления — назначение платежа, ИНН плательщика, номер договора. Строки, не подошедшие ни под одно правило, собираются в отдельный реестр.
Сдано 4
Настроенный обмен с банком
Правила разнесения выписки
Реестр неразобранных строк
Регламент подписи платежей
Как делали
Обследование Разобрали выгрузку платёжек файлом в клиент-банк и разноску выписки по распечатке на следующий день. Собрали признаки, по которым бухгалтер относит платёж к договору и объекту — назначение платежа, ИНН плательщика, номер договора, и уточнили порядок подписи платежей.
Архитектура Обмен с банком подняли прямым по защищённому каналу: платёжное поручение уходит из учётной системы с подписью, статус исполнения возвращается в тот же документ, а не отдельным файлом. Разноску выписки сделали правилами сопоставления с реестром неразобранных строк — договор по частичному совпадению не додумываем.
Разработка Сначала отправка платёжного поручения с подписью и возврат статуса, затем забор выписки по расписанию, затем правила разнесения по договорам и объектам и реестр неразобранных строк. На стенде работали на тестовом контуре банка.
Проверка Прогоняли отправку платежей и разноску выписок по архиву операций, отдельно смотрели плательщиков с несколькими договорами и возвраты. Провалом считали платёж, разнесённый на чужой договор или объект, и платёжное поручение, ушедшее в банк без подписи.
Передача Передали настроенный обмен с банком, правила разнесения выписки, реестр неразобранных строк, регламент подписи платежей и журнал обмена. Бухгалтерии показали разбор неразобранных строк, администратору — правку правил сопоставления.
Архитектура
Обмен ведёт сервис на Spring Boot по протоколу DirectBank: платёжное поручение выгружается из учётной системы, подписывается средствами КриптоПро и уходит в банк по защищённому каналу, а статус исполнения возвращается в тот же документ по его идентификатору. Подпись выполняет отдельный модуль с доступом к ключевому носителю, и сервис обмена ключом не владеет — иначе выкладка новой версии сервиса каждый раз затрагивала бы контур подписи. Выписка забирается по расписанию планировщиком Quartz и разносится по договорам и объектам правилами — назначение платежа, ИНН плательщика, номер договора; правила версионируются в PostgreSQL, и разнесённая строка хранит, каким правилом она сопоставлена. Строки, не подошедшие ни под одно правило, собираются в отдельный реестр и разносятся вручную, каждый сеанс обмена пишется в журнал вместе с исходным пакетом.
Приём платежей от банков и разноска по лицевым счетам
Задача, решение и что передали
Задача
Реестры платежей приходили от каждого банка в своём формате и разносились на лицевые счета вручную.
Решение
Собран приёмный шлюз: реестры банков и платёжных агентов приводятся к единому формату, платёж сопоставляется с лицевым счётом по идентификатору начисления и адресу. Загрузка идёт транзакцией с ключом реестра, поэтому повторный файл не задваивает оплату. Платежи без совпадения попадают в очередь ручного разбора вместе с исходной строкой реестра.
Сдано 4
Шлюз приёма реестров
Правила сопоставления лицевых счетов
Очередь ручного разбора
Журнал загрузок
Как делали
Обследование Собрали реестры от каждого банка и платёжного агента в их форматах и посмотрели, как оплату разносят на лицевые счета вручную. Разобрали, чем идентификатор начисления в реестре отличается от адреса и лицевого счёта в базе.
Архитектура Поставили приёмный шлюз, который приводит реестры к единому формату на входе, а платёж сопоставляет с лицевым счётом по идентификатору начисления и адресу. Загрузку сделали транзакцией с ключом реестра, поэтому повторный файл не задваивает оплату; платёж без совпадения не разносим — он ждёт в очереди вместе с исходной строкой реестра.
Разработка Сначала разбор форматов реестров и приведение к единой схеме, затем сопоставление с лицевыми счетами и транзакционная загрузка, затем очередь ручного разбора и журнал загрузок. На стенде прогоняли архив реестров каждого банка.
Проверка Загружали реестры всех подключённых банков, повторяли один и тот же файл, обрывали загрузку посередине и прогоняли повторную обработку. Провалом считали задвоенную оплату на лицевом счёте после повторной загрузки и платёж, разнесённый на чужой лицевой счёт.
Передача Передали шлюз приёма реестров, правила сопоставления лицевых счетов, очередь ручного разбора и журнал загрузок. Расчётному отделу показали разбор непривязанных платежей, администратору — подключение нового формата реестра и схему повторной обработки.
Архитектура
Шлюз на Go принимает реестры банков и платёжных агентов файлом и по API, приводит их к единой схеме адаптером на каждый формат и складывает исходный файл в объектное хранилище. Загрузка идёт одной транзакцией с ключом реестра: хеш файла и номер реестра образуют уникальный индекс в PostgreSQL, поэтому повторно присланный файл отбивается на входе и оплата не задваивается. Платёж сопоставляется с лицевым счётом сначала по идентификатору начисления, затем по адресу и периоду, а несопоставленные строки остаются в очереди ручного разбора вместе с исходной строкой реестра — на ближайший похожий счёт они не разносятся. Разнесённые платежи уходят в учётную систему пакетом через её HTTP-сервисы, журнал загрузок хранит состав пакета и результат по каждой строке, чтобы отклонённую часть можно было обработать повторно.
Стек
Go · chi · PostgreSQL · MinIO · NATS · Docker · 1С:Предприятие · OpenAPI · Prometheus
Реестр оборудования, план ремонтов и наряды на работы
Задача, решение и что передали
Задача
План ремонтов вёлся в таблицах, а фактическая наработка агрегатов попадала в него с чужих слов.
Решение
Собрали реестр оборудования с иерархией до узла и привязали к каждой позиции регламент обслуживания и счётчик наработки. Наработка тянется из телеметрии и подневных ведомостей: при достижении порога система сама ставит работу в план-график с нужной специальностью и материалами. Наряд печатается из той же записи, а факт закрытия возвращает узлу историю отказов и замен.
Сдано 3
Передали реестр оборудования с иерархией узлов
Регламенты обслуживания узлов
Шаблоны нарядов и отчёт по отказам
Как делали
Обследование оборудования Собрали с механиками и службой эксплуатации перечень агрегатов до узла, сверили таблицы плана ремонтов с паспортами и регламентами обслуживания. Разобрали, откуда берётся наработка и где она расходится с телеметрией и подневными ведомостями.
Архитектура реестра Реестр построили иерархией до узла, привязав к позиции регламент обслуживания и счётчик наработки, источником которого сделали телеметрию с подстраховкой ведомостями. Постановку работы в план-график отдали правилу по достижению порога, а не решению планировщика.
Разработка план-графика Сначала реестр оборудования и регламенты обслуживания узлов, затем счётчики наработки и автоматическая постановка работы с подбором специальности и материалов, затем печать наряда и возврат факта закрытия в историю узла.
Проверка на наработке Прогоняли план-график на исторической наработке и сверяли с тем, что ремонтная служба делала фактически. Провалом считали пропущенный порог, по которому работа не встала в график, и закрытый наряд, не отразившийся в истории отказов и замен узла.
Передача эксплуатации Передали реестр оборудования с иерархией узлов, регламенты обслуживания, шаблоны нарядов и отчёт по отказам. Служба эксплуатации сама заводит новый узел с регламентом и правит пороги наработки.
Архитектура
Реестр оборудования хранится деревом в PostgreSQL с материализованным путём узла: запрос «все работы по агрегату вместе с подузлами» выполняется по префиксу пути, без рекурсивного обхода на каждом чтении отчёта. Сервис наработки на Go получает счётчики агрегатов из OPC-шлюза по MQTT и агрегирует их посуточно в TimescaleDB, планировщик сравнивает наработку с порогом регламента и ставит работу в план-график с нужной специальностью и материалами. Наряд отдаётся мобильному приложению по HTTP из той же записи, факт закрытия возвращается и пополняет историю отказов и замен узла. Обмен с 1С:ТОИР идёт через интеграционную шину, состояние опроса телеметрии видно в Grafana.
Стек
Go · chi · PostgreSQL · TimescaleDB · MQTT · Kubernetes · OPC UA · 1С:ТОИР · Grafana
Диспетчерская аварийных заявок с нарядами выездным бригадам
Задача, решение и что передали
Задача
Заявку от жителя записывали в тетрадь, а состояние выезда диспетчер выяснял звонком бригадиру.
Решение
Заявки со всех каналов сводятся в одну очередь с адресом, домом и типом неисправности, распределение идёт по участку и свободной бригаде. Бригадир получает наряд в мобильном приложении, отмечает прибытие, выполненные работы и израсходованные материалы, прикладывает фото. Закрытая заявка списывает материалы в учётной системе и остаётся в истории дома вместе со сроком реакции.
Сдано 3
Передали справочник адресов и участков
Правила распределения заявок
Мобильные формы наряда и отчёт по срокам реакции
Как делали
Обследование диспетчерской Посидели с диспетчером на приёме заявок, разобрали тетрадь, звонки и сообщения жителей, собрали справочник адресов и границы участков. Выяснили, как сегодня бригадир сообщает о прибытии и о расходе материалов.
Архитектура очереди заявок Все каналы свели в одну очередь с адресом, домом и типом неисправности, а распределение отдали правилу по участку и свободной бригаде. Наряд у бригадира держится в мобильном приложении, работающем при плохой связи, а списание материалов идёт в учётной системе по факту закрытия.
Разработка нарядов Сначала приём заявок из всех каналов и справочник адресов и участков, затем правила распределения и мобильные формы наряда с фото, затем закрытие заявки со списанием материалов и записью в историю дома.
Проверка на выездах Провели заявки всех типов от звонка жителя до закрытия, в том числе с потерей связи у бригады на объекте. Провалом считали заявку, не попавшую в очередь хотя бы по одному каналу, и закрытие наряда без указанных работ, материалов и отметки прибытия.
Передача диспетчерам Передали справочник адресов и участков, правила распределения заявок, мобильные формы наряда и отчёт по срокам реакции, обучили диспетчеров и бригадиров. Диспетчер сам меняет границы участков и поднимает историю выездов по дому.
Архитектура
Приложение бригады на Jetpack Compose держит наряд, справочники и вложения в локальной базе Room и синхронизируется очередью исходящих операций: в подвале сети нет, поэтому отметка прибытия, работы и расход материалов ставятся офлайн и уходят позже с локальным идентификатором операции, по которому сервер отбрасывает повтор. Серверная часть на Ktor сводит заявки телефонии, портала и мессенджера в одну очередь в PostgreSQL, адрес нормализуется по справочнику домов и участков, распределение идёт по участку и свободной бригаде. Фотографии кладутся в MinIO прямой загрузкой, в карточке остаётся ссылка. Закрытие заявки публикует событие в RabbitMQ, адаптер списывает материалы в 1С, срок реакции остаётся в истории дома.
Путевой лист на спецтехнику выписывали от руки на выезде с базы, а наработку по объектам сводили в конце месяца по стопке бумаг.
Решение
Выпуск путевого листа собрали в одну цепочку: заявка на технику, предрейсовый осмотр механика и медработника, электронный допуск с подписью. Наработка снимается с бортового блока в моточасах и привязывается к наряд-заданию на кусте, а не к пробегу. Закрытый лист сам разносит наработку на объект работ и на счётчик до следующего обслуживания.
Сдано 3
Передали маршрут выпуска путевого листа
Формы предрейсового осмотра
Схему съёма моточасов и отчёт наработки техники
Как делали
Обследование выпуска Прошли выпуск техники с базы вместе с механиком и медработником, разобрали рукописные путевые листы и стопку бумаг, из которой сводили наработку. Сверили, что снимает бортовой блок и как это соотносится с наряд-заданием на кусте.
Архитектура путевого листа Выпуск собрали в одну цепочку — заявка на технику, предрейсовый осмотр механика и медработника, электронный допуск с подписью. За единицу наработки взяли моточасы с бортового блока, а не пробег, и привязали их к наряд-заданию на кусте; разноску на объект и на счётчик до обслуживания отдали закрытию листа.
Разработка цепочки Сначала маршрут выпуска и формы предрейсового осмотра с подписью, затем съём моточасов с бортовых блоков и привязка к наряд-заданию, затем закрытие листа с разноской наработки и отчёт по технике.
Проверка на выездах Выпускали спецтехнику по электронному листу параллельно с бумажным и сверяли моточасы с бортовым блоком и отметками на кусте. Провалом считали выезд с базы без отметки механика или медработника и наработку, не разнесённую на объект работ.
Передача и обучение Передали маршрут выпуска путевого листа, формы предрейсового осмотра, схему съёма моточасов и отчёт наработки техники. Диспетчер базы сам заводит технику и правит маршрут согласования, механик закрывает лист без бумаги.
Архитектура
Маршрут выпуска путевого листа собран конечным автоматом в сервисе на NestJS: заявка на технику, предрейсовый осмотр механика, медработник, электронный допуск — каждый переход закрывается ролью и пишется в PostgreSQL. Бортовые блоки шлют пакеты по MQTT, коллектор снимает счётчик моточасов и кладёт ряд в TimescaleDB; счётчик монотонный и при переподключении блока приходит с дублями, поэтому наработка считается разностью на границах смены, а не суммой пришедших пакетов. Закрытый лист публикует событие, адаптер разносит моточасы на наряд-задание куста в 1С:УАТ и на счётчик до следующего обслуживания. Допуск подписывается открепленной подписью через КриптоПро, состояние опроса блоков видно в Grafana.
Личный кабинет абонента с приёмом показаний и квитанциями
Задача, решение и что передали
Задача
Показания абоненты диктуют оператору по телефону и присылают в мессенджер, оператор переносит их в биллинг руками, а спорную квитанцию разбирают в переписке.
Решение
Кабинет работает поверх биллинга через сервисный слой: вход по номеру лицевого счёта с подтверждением одноразовым кодом, форма показаний отбивает значение меньше предыдущего и помечает аномальный расход. Начисление, квитанция и история платежей забираются из биллинга по расписанию и по событию оплаты, оплата картой идёт через эквайринг с возвратом статуса в кабинет. Обращение по расхождению создаётся прямо из строки квитанции и уходит в реестр заявок с расшифровкой расчёта.
Сдано 4
Кабинет абонента
Административная панель оператора
Сервисный слой обмена с биллингом
Инструкция оператора
Как делали
Обследование приёма показаний Слушали приём показаний у операторов, разобрали, что приходит по телефону и в мессенджер и как это переносится в биллинг. Посмотрели структуру лицевого счёта и начислений и собрали типовые поводы спора по квитанции.
Архитектура кабинета Кабинет решили строить поверх биллинга через сервисный слой, не обращаясь к его базе напрямую; вход — по номеру лицевого счёта с подтверждением одноразовым кодом. Начисления и квитанции забираются по расписанию и по событию оплаты, оплата картой идёт через эквайринг с возвратом статуса в кабинет.
Разработка кабинета Сначала сервисный слой и форма показаний с отбивкой значения меньше предыдущего и пометкой аномального расхода, затем квитанции, история платежей и оплата, затем обращение из строки квитанции и панель оператора.
Проверка на счетах Заводили показания по лицевым счетам с приборами разного типа и сверяли начисление с биллингом, гоняли оплату с возвратом статуса. Провалом считали принятое показание меньше предыдущего, платёж без статуса в кабинете и расхождение суммы квитанции с расчётом биллинга.
Передача операторам Передали кабинет абонента, административную панель, сервисный слой обмена, инструкцию оператора и набор автотестов на приём показаний. Оператор сам разбирает обращение из строки квитанции и правит показания в биллинге, не переписывая их из мессенджера.
Архитектура
Кабинет на Spring Boot ходит не в биллинг, а в сервисный слой со своей копией лицевых счетов, начислений, квитанций и истории платежей в PostgreSQL; копия обновляется по расписанию и по событию оплаты. Копия, а не проксирование, выбрана потому, что биллинг рассчитан на операторские сессии, а заход абонентов приходится на последние дни приёма показаний — чтение витрины не должно занимать его соединения. Показание проходит проверки в сервисе (значение меньше предыдущего, аномальный расход) и уходит в биллинг вызовом с ключом «счёт, счётчик, период», поэтому повтор не создаёт вторую запись. Вход и подтверждение идут одноразовым кодом через SMPP-шлюз, оплата — редиректом в эквайринг с возвратом статуса вебхуком, обращение из строки квитанции создаёт запись в реестре заявок.
Кабинет покупателя с платежами и приёмкой квартиры
Задача, решение и что передали
Задача
График платежей покупатель уточняет в отделе продаж, а замечания приёмки пишутся на бумажном листе и теряются между подрядчиком и службой заказчика.
Решение
Кабинет собирает договор, график платежей и статус этапов из учётной системы и графика стройки, поступивший платёж закрывает строку графика. Лист приёмки заполняется на планшете: замечание привязано к помещению и элементу, к нему прикладываются фотографии, устранение подтверждается повторным снимком и подписью покупателя. Замечание уходит подрядчику задачей с ответственным и сроком, статус задачи возвращается в кабинет.
Сдано 4
Кабинет покупателя
Мобильный лист приёмки
Интеграция с учётной системой
Реестр замечаний
Как делали
Обследование приёмки Разобрали с отделом продаж и службой заказчика договоры и графики платежей, посмотрели бумажные листы приёмки и путь замечания к подрядчику. Выяснили, из чего складывается статус этапа в графике стройки.
Архитектура кабинета Договор, график платежей и статусы этапов решили собирать из учётной системы и графика стройки, а закрытие строки графика привязать к поступившему платежу. Лист приёмки сделали планшетным с привязкой замечания к помещению и элементу и фотофиксацией, замечание уходит подрядчику задачей с ответственным.
Разработка листа приёмки Сначала интеграция с учётной системой по договорам и платежам, затем мобильный лист приёмки с фотографиями и подписью покупателя, затем реестр замечаний, задачи подрядчику и возврат статуса задачи в кабинет.
Проверка в квартирах Проводили приёмку в квартирах с плохой связью и разносили поступившие платежи по графику. Провалом считали замечание без привязки к помещению и элементу, устранение, подтверждённое без повторного снимка, и платёж, не закрывший строку графика.
Передача службе заказчика Передали кабинет покупателя, мобильный лист приёмки, интеграцию с учётной системой, реестр замечаний и регламент устранения. Служба заказчика сама заводит подрядчика, ведёт реестр и закрывает замечания.
Архитектура
Кабинет на Spring Boot собирает договор, график платежей и статусы этапов из учётной системы и графика стройки, поступивший платёж сопоставляется со строкой графика и закрывает её. Лист приёмки сделан PWA с локальным хранением в IndexedDB: в корпусе без сети замечания с привязкой к помещению и элементу и фотографии копятся на планшете и выгружаются пакетом, а снимки уходят прямой загрузкой в объектное хранилище по пресигнед-ссылке, минуя приложение. Каждое замечание порождает задачу подрядчику с ответственным и сроком, статус задачи возвращается обратным вебхуком и обновляет строку в кабинете. Устранение подтверждается повторным снимком и подписью покупателя, которая хранится вместе с актом, карточки и реестр замечаний лежат в PostgreSQL.
Витрина отчётности с расшифровкой до первичного документа
Задача, решение и что передали
Задача
Отчётность собирается в таблицах из нескольких систем, и каждая цифра на совещании превращается в спор о том, откуда она взялась.
Решение
Показатели считаются в слое витрин регламентным расчётом, определение метрики хранится в одном месте и переиспользуется всеми панелями. Каждая цифра разворачивается до строки первичного документа переходом на источник. Загрузка идёт по расписанию с проверками полноты: незакрытый период помечается в отчёте отдельно, а состав показателей зависит от роли открывшего.
Сдано 4
Витрина показателей
Регламент расчёта
Справочник метрик
Отчётные панели
Как делали
Обследование отчётов Собрали отчёты, которые готовятся в таблицах, и по каждому показателю разобрали, из какой системы берётся цифра и как её считает автор. Зафиксировали расхождения в определениях одинаково называемых метрик.
Архитектура витрин Определение метрики решили держать в одном месте и переиспользовать всеми панелями, а показатели считать регламентным расчётом в слое витрин. Каждую цифру сделали разворачиваемой до строки первичного документа, состав показателей привязали к роли открывшего.
Разработка панелей Сначала загрузка источников по расписанию с проверками полноты и справочник метрик, затем витрины и отчётные панели, затем переход от цифры к первичному документу и пометка незакрытого периода.
Проверка расчётов Пересчитали отчётность по закрытым данным и сверили с таблицами, которые готовили руками. Не приняли бы работу при цифре, не разворачивающейся до первичного документа, при расхождении с проверенным расчётом и при незакрытом периоде, показанном наравне с закрытым.
Передача аналитикам Передали витрину показателей, регламент расчёта, справочник метрик, отчётные панели и журнал загрузок. Аналитик сам добавляет метрику по описанию в справочнике и по журналу видит, какая загрузка не прошла.
Архитектура
Загрузка идёт DAG-ами Airflow из систем-источников, показатели считаются регламентным расчётом и лежат в ClickHouse. Колоночное хранилище выбрано потому, что панели читают несколько полей за длинный период и агрегируют их — запрос поднимает только нужные колонки, а не строки целиком, как это делала бы строчная СУБД. Определение метрики (формула, источник, гранулярность) хранится в справочнике и подставляется генератором SQL в сервисе на Go, поэтому одна цифра на всех панелях считается одним выражением. Расшифровка до первичного документа идёт переходом по ключу в реляционный слой PostgreSQL со ссылками на систему-источник; незакрытый период помечается флагом полноты из журнала загрузок, состав показателей режется ролью открывшего.
Стек
Go · chi · ClickHouse · PostgreSQL · Apache Airflow · Kubernetes · Superset · Grafana
Перенос файлового хранилища и каталога пользователей на Linux
Задача, решение и что передали
Задача
Общие папки расчётного центра и учётные записи жили на Windows-серверах, снятых с поддержки.
Решение
Каталог пользователей поднят на LDAP-совместимом сервере, доменные группы и дерево прав на общие папки перенесены с сохранением наследования. Файловый ресурс переведён на Samba поверх Linux, теневые копии заменены снимками файловой системы и ночной выгрузкой. Рабочие места переключены политикой, прежние UNC-имена сохранены псевдонимами.
Сдано 4
Переданы схема каталога
Матрица прав на ресурсы
Регламент восстановления файлов
Инструкция ввода рабочих мест
Как делали
Обследование ресурсов Выгрузили учётные записи, доменные группы и дерево прав на общие папки расчётного центра, нашли ресурсы с нарушенным наследованием. Собрали список UNC-имён, зашитых в ярлыки и документы сотрудников.
Архитектура каталога Каталог пользователей подняли на LDAP-совместимом сервере с сохранением групп, файловый ресурс перевели на Samba поверх Linux. Теневые копии заменили снимками файловой системы и ночной выгрузкой, прежние UNC-имена сохранили псевдонимами, чтобы не переучивать сотрудников.
Разработка переноса Сначала подняли каталог и перенесли группы, затем перенесли данные с матрицей прав и наследованием, затем настроили снимки, выгрузку и переключение рабочих мест политикой.
Проверка прав Заходили под учётными записями разных подразделений расчётного центра и сверяли фактический доступ с матрицей прав, восстанавливали файл из снимка. Провалом считали доступ к папке, которого нет в матрице, потерю прав при переносе и файл, не восстановившийся из снимка.
Передача администраторам Передали схему каталога, матрицу прав на ресурсы, регламент восстановления файлов и инструкцию ввода рабочих мест. Администратор сам заводит учётную запись, меняет права на ресурс и восстанавливает файл по обращению.
Архитектура
Каталог пользователей поднят на LDAP-совместимом сервере, билеты выдаёт Kerberos: доступ к общим папкам идёт по билету, поэтому прежние сетевые имена подключаются без ввода пароля на каждое соединение и без хранения паролей на файловом узле. Файловый ресурс переведён на Samba поверх Linux, дерево прав перенесено средствами POSIX ACL с сохранением наследования, теневые копии заменены снимками файловой системы и ночной выгрузкой. Сервис миграции прав на Ktor читает выгрузку старого домена, сопоставляет идентификаторы безопасности с новыми группами и раскладывает ACL, несопоставленные записи собираются отчётом в PostgreSQL. Рабочие места переключены политикой, прежние UNC-имена сохранены псевдонимами DNS, узлы разворачиваются плейбуками Ansible.
Внутренний репозиторий пакетов для обновления доверенных ОС
Задача, решение и что передали
Задача
Обновления на серверах диспетчерского контура ставились вручную с носителей, а точный состав версий на узлах никто не знал.
Решение
Внутри контура поднято зеркало репозиториев дистрибутивов, наполнение идёт однонаправленным переносом с проверкой подписи и хеша. Прикладные пакеты подписываются собственным ключом и разложены по тестовому и продуктивному каналам, продвижение идёт только после прогона на стенде. Состав узлов собирается автоматически: версия пакета, ядро и дата установки видны по каждому серверу.
Сдано 4
Переданы описание зеркала и каналов
Ключи подписи с регламентом
Плейбуки обновления
Инвентарь версий по узлам
Как делали
Обследование узлов Собрали фактический состав версий на серверах диспетчерского контура и разобрали, как обновления попадали туда с носителей. Зафиксировали действующие правила переноса данных внутрь контура.
Архитектура зеркала Внутри контура подняли зеркало репозиториев дистрибутивов с однонаправленным наполнением и проверкой подписи и хеша. Прикладные пакеты подписали собственным ключом и развели по тестовому и продуктивному каналам, продвижение разрешили только после прогона на стенде.
Разработка каналов Сначала зеркало и правила переноса, затем подпись пакетов и каналы продвижения, затем плейбуки обновления и автоматический сбор инвентаря — версия пакета, ядро и дата установки по каждому узлу.
Проверка обновления Обновляли стендовые узлы из тестового канала и пробовали поставить пакет с чужой и с повреждённой подписью. Провалом считали установку пакета без действующей подписи, попадание непроверенного пакета в продуктивный канал и узел, отсутствующий в инвентаре версий.
Передача администраторам Передали описание зеркала и каналов, ключи подписи с регламентом, плейбуки обновления и инвентарь версий по узлам. Администраторы контура сами наполняют зеркало, продвигают пакет из тестового канала и обновляют узлы плейбуком.
Архитектура
Внутри контура поднято зеркало репозиториев за Nginx: deb-ветку ведёт reprepro, наполнение идёт однонаправленным переносом с проверкой подписи источника и хеша файлов до публикации. Прикладные пакеты подписываются собственным GPG-ключом на отдельном узле подписи, на зеркале ключа нет — доступ к зеркалу не даёт возможности подписать пакет. Тестовый и продуктивный каналы разведены отдельными дистрибуциями, продвижение сборки между ними выполняет сервис на ASP.NET Core по результату прогона на стенде. Инвентарь узлов собирается фактами Ansible в PostgreSQL: версия пакета, ядро и дата установки видны по каждому серверу диспетчерского контура.
Стек
C# · ASP.NET Core · PostgreSQL · MinIO · Nginx · Ansible · reprepro · GPG-подпись · РЕД ОС
Сборка сервисов промысловой телеметрии под отечественный процессор
Задача, решение и что передали
Задача
Серверная часть промысловой телеметрии собиралась только под x86 и тянула закрытые библиотеки без исходников.
Решение
Закрытые библиотеки заменены аналогами с исходниками, места с невыровненным доступом к памяти и предположением о порядке байт переписаны и закрыты тестами. Сборочный конвейер расширен второй архитектурой: один коммит даёт пакеты под обе, автотесты идут на плате с отечественным процессором. Пакеты собраны в двух форматах, различия зафиксированы в спецификации сборки.
Сдано 4
Переданы сборочный конвейер
Спецификации пакетов под обе архитектуры
Отчёт о прогоне на целевом железе
Перечень заменённых зависимостей
Как делали
Обследование зависимостей Разобрали зависимости серверной части промысловой телеметрии и выделили закрытые библиотеки, поставлявшиеся без исходников. Нашли места, где код опирается на невыровненный доступ к памяти и на порядок байт прежней архитектуры.
Архитектура сборки Закрытые библиотеки заменили аналогами с исходниками, спорные места переписали и закрыли тестами. Сборочный конвейер решили расширить второй архитектурой, чтобы один коммит давал пакеты под обе, а не поддерживать отдельную ветку под отечественный процессор.
Разработка конвейера Сначала замена зависимостей и правка кода под другой порядок байт, затем сборка пакетов в двух форматах, затем прогон автотестов на плате с отечественным процессором прямо в конвейере.
Проверка на железе Гоняли приём телеметрии с промысла на целевом железе и сверяли разобранные значения с эталонной сборкой. Провалом считали расхождение разобранного пакета телеметрии между архитектурами и падение сборки или тестов на целевой плате.
Передача команде заказчика Передали сборочный конвейер, спецификации пакетов под обе архитектуры, отчёт о прогоне на целевом железе и перечень заменённых зависимостей. Команда заказчика сама собирает выпуск под обе архитектуры и добавляет зависимость в спецификацию.
Архитектура
Закрытые библиотеки заменены аналогами с исходниками, места с невыровненным доступом к памяти и предположением о порядке байт переписаны и закрыты тестами. Сборка ведётся матрицей GitLab CI: один коммит даёт пакеты RPM и DEB под обе архитектуры, а автотесты гоняются на раннере, поднятом на плате с целевым процессором, — эмулятор не воспроизводит расхождения выравнивания и порядка байт, ради которых проверка и делается. Приём промысловой телеметрии идёт по MQTT в брокер, сервис на Go разбирает пакеты и складывает ряды в TimescaleDB, служебный обмен между сервисами — gRPC. Различия сборок под архитектуры зафиксированы в спецификации пакета, артефакты уходят во внутренний репозиторий.
Стек
Go · gRPC-Go · TimescaleDB · PostgreSQL · MQTT · GitLab CI · CMake · aarch64/e2k · RPM и DEB
Контроль ответов модели на утечку внутренних сведений
Задача, решение и что передали
Задача
Ассистент диспетчерской службы иногда пересказывал фрагменты документов с ограничением доступа, и заметить это можно было только глазами.
Решение
Поставили проверку на выходе: ответ сверяется с метками найденных фрагментов и со словарём охраняемых сведений — схемы подстанций, ключи, адреса объектов. Ответ, опирающийся на фрагмент выше уровня пользователя, не показывается и уходит на разбор вместе с сохранённой цепочкой поиска. Словарь и пороги срабатывания ведёт служба безопасности отдельно от кода сервиса.
Сдано 4
Словарь охраняемых сведений
Правила выходного контроля
Очередь спорных ответов
Журнал срабатываний
Как делали
Обследование сведений Разобрали с диспетчерской службой и службой безопасности, что относится к охраняемым сведениям — схемы подстанций, ключи, адреса объектов. Подняли случаи, когда ассистент пересказывал фрагменты документов с ограничением доступа.
Архитектура контроля Проверку поставили на выходе: ответ сверяется с метками найденных фрагментов и со словарём охраняемых сведений. Словарь и пороги срабатывания вынесли из кода в ведение службы безопасности, а спорный ответ решили не показывать, а отправлять на разбор вместе с сохранённой цепочкой поиска.
Разработка правил Сначала словарь охраняемых сведений и правила выходного контроля, затем сопоставление уровня найденных фрагментов с правами пользователя, затем очередь спорных ответов и журнал срабатываний.
Проверка на вопросах Задавали вопросы, подводящие к документам выше уровня доступа, от учётных записей разных ролей диспетчерской службы. Провалом считали показанный ответ, опирающийся на фрагмент выше уровня пользователя, и срабатывание, не сохранившее цепочку поиска для разбора.
Передача безопасности Передали словарь охраняемых сведений, правила выходного контроля, очередь спорных ответов и журнал срабатываний. Служба безопасности сама правит словарь и пороги и разбирает очередь без участия разработчика.
Архитектура
Выходной контроль вынесен отдельным сервисом на Axum между генерацией и клиентом: он получает ответ вместе с идентификаторами фрагментов, из которых тот собран, сверяет их метки с уровнем пользователя и прогоняет текст по словарю охраняемых сведений — схемы подстанций, ключи, адреса объектов. Словарь и пороги срабатывания лежат в PostgreSQL и перечитываются сервисом по версии, без пересборки, потому что ведёт их служба безопасности, и правка не должна проходить через релиз кода. Ответ, опирающийся на фрагмент выше уровня доступа, клиенту не отдаётся, а кладётся в очередь спорных вместе с сохранённой цепочкой поиска в OpenSearch. Горячая копия словаря и счётчики срабатываний держатся в Redis, метрики уходят в Prometheus.
Разграничение действий ИИ-агента в производственных системах
Задача, решение и что передали
Задача
Агент работал в системах под общей технической учётной записью с правом записи, а границу опасных действий держал только текст промпта.
Решение
Развели права: чтение телеметрии и справочников идёт от отдельной ограниченной учётной записи, изменяющие вызовы проходят через шлюз со списком разрешённых операций. Необратимая команда останавливается на подтверждении оператора, в карточке подтверждения видны параметры вызова и цепочка обращения, из которой он вырос. Отклонённые и подтверждённые вызовы пишутся в один журнал с временем и учётной записью.
Сдано 4
Список разрешённых операций
Шлюз подтверждения
Журнал вызовов агента
Регламент разбора отклонений
Как делали
Разбор прав Разобрали, что агент делает под общей технической учётной записью: какие теги телеметрии и справочники читает и какие изменяющие вызовы совершает. Вместе с технологами и службой АСУ ТП выписали, какие команды необратимы.
Архитектура шлюза Развели права: чтение телеметрии и справочников от отдельной ограниченной учётной записи, изменяющие вызовы только через шлюз со списком разрешённых операций. Отказались держать границу опасных действий в тексте промпта и вынесли её в правила шлюза, а необратимую команду поставили на подтверждение оператора.
Сборка шлюза Собрали шлюз и список разрешённых операций, затем карточку подтверждения с параметрами вызова и цепочкой обращения, из которой он вырос, затем единый журнал отклонённых и подтверждённых вызовов. На стенде работали с имитатором тегов, не трогая боевой контур.
Прогон на стенде Гоняли агента на сценариях с запросом необратимой команды, с вызовом не из списка и с попыткой записи от читающей учётной записи. Провалом считали изменяющий вызов, дошедший до системы мимо шлюза или без подтверждения оператора, и вызов, не попавший в журнал.
Передача операторам Передали список разрешённых операций, шлюз подтверждения, журнал вызовов агента и регламент разбора отклонений. Показали операторам, как читать карточку подтверждения, а службе — как править список операций.
Архитектура
Агент не ходит в производственные системы сам: чтение телеметрии и справочников идёт через шлюз на Go от ограниченной учётной записи, у которой в этих системах нет права записи, телеметрия снимается OPC UA-коннектором. Изменяющий вызов проверяется по списку разрешённых операций — решение выносит OPA по политике, роль и учётная запись приходят из Keycloak, — после чего вызов встаёт в очередь NATS на подтверждение оператора и до нажатия в карточке не выполняется. Подтверждённый вызов уходит в систему с идемпотентным ключом, собранным из идентификатора вызова: оператор нажимает дважды, шлюз перезапускается и переотправляет команду, а в производственной системе она должна пройти один раз. Отклонённые и подтверждённые вызовы с параметрами и цепочкой обращения пишутся в один журнал в PostgreSQL; шлюз развёрнут в Kubernetes отдельно от агента, и его остановка закрывает запись, оставляя чтение.
Стек
Go · chi · PostgreSQL · NATS · Kubernetes · OPA · Keycloak · OPC UA
Аудит источников данных и реестр владельцев показателей
Задача, решение и что передали
Задача
Один и тот же показатель добычи считался в трёх системах по-разному, и на совещании спорили, чья выгрузка правильная.
Решение
Прошли по системам-поставщикам данных и описали для каждого показателя первичный источник, формулу и точку, где значение попадает в отчёт. Свели описания в реестр с владельцем, регламентом обновления и признаком расхождения с соседними источниками. Для расходящихся показателей зафиксировали эталонный источник и порядок действий при отклонении.
Сдано 4
Реестр источников и показателей
Карточки владельцев
Схемы происхождения значений
Протокол расхождений
Как делали
Обход систем Прошли по системам-поставщикам и для каждого показателя добычи собрали первичный источник, формулу и точку, где значение попадает в отчёт. Сели с теми, кто спорит на совещании, и на конкретных выгрузках разобрали, откуда берётся расхождение.
Схема происхождения Решили описывать показатель как цепочку от первичной записи до строки отчёта и хранить это в каталоге, а не в переписке. Каждому показателю назначили владельца и регламент обновления, а для расходящихся зафиксировали эталонный источник.
Сборка реестра Собрали реестр источников и показателей с карточками владельцев, описали происхождение значений по шагам преобразования и добавили признак расхождения с соседними источниками. Сверку оформили как повторяемый расчёт, а не разовую выгрузку аналитика.
Сверка значений Пересчитали показатели по описанным цепочкам и сличили с тем, что показывают системы и итоговый отчёт, спорные случаи разобрали с владельцами. Провалом считали показатель, цепочка которого не сходилась с выгрузкой источника, и показатель, за который никто не согласился отвечать как владелец.
Передача владельцам Передали реестр источников и показателей, карточки владельцев, схемы происхождения значений и протокол расхождений. Показали владельцам, как вносить изменение формулы и какой порядок действий при отклонении от эталонного источника.
Архитектура
Джобы Spark читают системы-поставщики: Oracle — по JDBC, потоковые показатели — из топиков Kafka; для каждого показателя выполняется пересчёт формулы от первичного источника и сравнение с тем значением, которое доходит до отчёта. Сверка сделана пересчётом на общих ключах, а не сличением готовых выгрузок: выгрузки приходят с разной нарезкой периода, и в них расхождение формулы неотличимо от расхождения границ окна. Реестр показателей, владельцев, регламентов обновления и протокол расхождений лежат в PostgreSQL, происхождение значений выкладывается в DataHub графом, преобразования описаны в dbt, проверки — в Great Expectations. Запуск сверок и профилирования держит Airflow; эталонный источник помечен в реестре, и при отклонении отчёт собирается от него, а расхождение попадает владельцу показателя.
Разбор телеметрии приборов учёта перед моделью потребления
Задача, решение и что передали
Задача
Показания с приборов приходили нерегулярно, часть домов слала дубли, и любой расчёт потребления начинался с ручной чистки выгрузки.
Решение
Собрали архив показаний по всем точкам учёта и посчитали пропущенные интервалы, дубликаты по времени и скачки, не объяснимые погодой. Разделили точки на пригодные для почасового прогноза, пригодные только для суточного и неисправные — с указанием причины. Правила отбраковки и восстановления интервалов описали так, чтобы они выполнялись в загрузке, а не в блокноте аналитика.
Сдано 4
Реестр точек учёта с классом пригодности
Отчёт по пропускам и дублям
Правила отбраковки
Требования к загрузке
Как делали
Разбор архива Собрали архив показаний по всем точкам учёта и посчитали пропущенные интервалы, дубликаты по времени и скачки, не объяснимые погодой. Расспросили аварийную и расчётную службы, какие дома известны как проблемные и как показания доходят до расчёта.
Классы точек Решили классифицировать сами точки учёта, а не отдельные строки показаний: пригодные для почасового прогноза, пригодные только для суточного и неисправные с указанием причины. Правила отбраковки и восстановления интервалов договорились выполнять в загрузке, а не в блокноте аналитика.
Сборка загрузки Сделали приём и укладку показаний с ключом точки и временем, затем отбраковку дублей и разметку пропусков, затем расчёт признаков пригодности по каждой точке и вывод реестра. Проверяли на архиве, прежде чем ставить правила в поток.
Прогон правил Гоняли правила на архиве и на текущем потоке, отдельно смотрели дома, слывшие проблемными, и вручную разбирали спорные точки. Правило не принимали, если оно оставляло в расчёте дубль по времени или относило к пригодным для почасового прогноза точку с рваным рядом показаний.
Передача службе Отдали реестр точек учёта с классом пригодности, отчёт по пропускам и дублям, правила отбраковки и требования к загрузке. Показали расчётной службе, как читать причину неисправности точки и как менять её класс после ремонта прибора.
Архитектура
Концентраторы публикуют пакеты показаний в MQTT-брокер, коллектор на FastAPI разбирает их и пишет в TimescaleDB. Дубли снимаются уникальным ключом (точка учёта, метка времени) прямо на записи, а не чисткой постфактум: после обрыва связи приборы досылают уже принятые интервалы, и повторный пакет должен переписать значение, а не добавить строку. Хранилище с разбиением по времени выбрано потому, что весь расчёт идёт окнами — пропуски, дубли и скачки считаются по интервалам, а не по отдельным точкам; реестр точек учёта с классом пригодности ведётся обычной таблицей в той же базе, рядом с рядами показаний. Правила отбраковки и восстановления интервалов вынесены шагом Airflow в загрузку и применяются до витрины, архив выгрузок лежит в MinIO, панели по пропускам собраны в Grafana, сервисы поставляются в Docker.
Дорожная карта ИИ для технологического контура без интернета
Задача, решение и что передали
Задача
Пилоты предлагались службами вразнобой, и каждый упирался в то, что обучать снаружи нельзя, а внутри нет ни площадки, ни собранных данных.
Решение
Разложили предложенные сценарии по зависимостям: какие данные нужны каждому, кто их отдаёт и какой сценарий обязан выйти раньше соседнего. Для каждого этапа записали условие перехода к следующему — какие проверки проходят и на каком стенде. Отдельно описали порядок переноса моделей и обновлений внутрь контура, от которого зависит длительность каждого этапа.
Сдано 4
Дорожная карта с этапами и зависимостями
Критерии перехода между этапами
Схема стенда
Порядок переноса обновлений
Как делали
Сбор предложений Собрали предложения служб в один список и по каждому выяснили, какие данные нужны и кто их отдаёт. С безопасностью и АСУ разобрали, что вообще разрешено вносить в технологический контур и каким путём.
Порядок этапов Разложили сценарии по зависимостям: какой обязан выйти раньше соседнего и что он даёт следующему. Решили обучать и собирать снаружи контура, а внутрь вносить только проверенные артефакты, поэтому площадку внутри контура описали как отдельный элемент карты, от которого зависят все этапы.
Описание переходов Описали схему стенда и порядок переноса моделей и обновлений внутрь контура. Затем для каждого этапа записали условие перехода к следующему: какие проверки проходят и на каком стенде.
Проверка на сценарии Проверили карту на самом раннем сценарии, пройдя перенос артефакта внутрь контура целиком, и разобрали каждую зависимость с владельцем данных. Этап признавали неготовым, если его условие перехода нельзя было проверить на стенде или владелец не подтверждал, что отдаёт нужные данные внутрь контура.
Передача дорожной карты Передали дорожную карту с этапами и зависимостями, критерии перехода между этапами, схему стенда и порядок переноса обновлений. Прошли карту со службами и показали, кто и что подтверждает на границе каждого этапа.
Архитектура
Внутри контура поднимается площадка: кластер Kubernetes на своих узлах, реестр образов Harbor, хранилище артефактов MinIO, установка и обновление — плейбуками Ansible. Маршрута наружу нет, поэтому перенос идёт бандлом: образы, веса, зеркала пакетов и манифест собираются снаружи в один OCI-архив, подписываются Cosign и вносятся носителем — пофайловый перенос не годится, внутри нечем дотянуть недостающую зависимость, и установка встанет на первом же импорте. Сервис учёта этапов на FastAPI держит в PostgreSQL сценарии, их зависимости по данным, поставщиков этих данных и критерии перехода: этап закрывается только после прогона проверок на стенде, отметка прогона пишется туда же. Стенд повторяет продуктивный контур по составу узлов, поэтому порядок переноса обновлений проверяется на нём до внесения в рабочий контур, и длительность этапа считается от этого цикла.
Разбор заявок на технологическое присоединение с проверкой комплекта
Задача, решение и что передали
Задача
Заявки приходили почтой и через сайт в свободной форме, и инженер сам открывал вложения, чтобы понять, приложен ли план расположения энергопринимающих устройств и правоустанавливающий документ на объект.
Решение
Агент читает заявку и вложения, извлекает адрес объекта, запрашиваемую мощность, класс напряжения и заявленную точку присоединения, а адрес приводит к записи адресного справочника. Состав комплекта проверяется по типу заявителя: план расположения энергопринимающих устройств, правоустанавливающий документ, доверенность, согласие смежного собственника — недостающее называется в ответе заявителю поимённо. Квалифицированная заявка ложится в систему с заполненными полями и сроком рассмотрения, заявка с неопознанным адресом уходит в очередь ручного разбора.
Сдано 4
Сервис приёма и разбора заявок
Правила проверки комплекта по категориям заявителей
Справочник соответствия адресов
Очередь ручного разбора
Как делали
Разбор заявок Разобрали поток заявок с почты и с сайта: в какой форме они приходят, что вкладывают заявители разных категорий и что инженер открывает вручную, чтобы понять комплектность. Выписали, какие документы обязательны для какого типа заявителя.
Схема квалификации Решили приводить адрес объекта к записи адресного справочника до всех прочих проверок, а состав комплекта проверять по типу заявителя. Заявку с неопознанным адресом договорились не додумывать, а отправлять в очередь ручного разбора, квалифицированную — класть в систему с заполненными полями и сроком рассмотрения.
Сборка разбора Сделали приём заявки с почты и с сайта и чтение вложений, затем извлечение адреса объекта, запрашиваемой мощности, класса напряжения и заявленной точки присоединения. Последними собрали сопоставление адреса со справочником и проверку комплекта с поимённым перечислением недостающего в ответе заявителю.
Прогон заявок Прогнали накопленные заявки разных категорий заявителей и сличили результат с тем, как их квалифицировал инженер. Провалом считали заявку, признанную полной без плана расположения энергопринимающих устройств или правоустанавливающего документа, и адрес, сопоставленный со справочником ошибочно вместо ухода в ручной разбор.
Передача инженерам Передали сервис приёма и разбора заявок, правила проверки комплекта по категориям заявителей, справочник соответствия адресов, очередь ручного разбора и инструкцию инженера. Показали, как разбирать очередь и как править правила при изменении требований к комплекту.
Архитектура
Заявки приходят двумя путями — форма сайта стучится в сервис на Axum, почтовый коннектор забирает письма, — и оба складывают вложения в MinIO и ставят задачу в NATS. Воркер извлекает адрес объекта, запрашиваемую мощность, класс напряжения и заявленную точку присоединения; адрес нормализуется по справочнику ГАР ФИАС, а похожая запись подбирается через Elasticsearch, потому что заявитель пишет адрес свободной строкой и точного совпадения по справочнику обычно нет. Правила комплектности по категориям заявителей лежат таблицей в PostgreSQL, поэтому недостающий план расположения устройств, правоустанавливающий документ или доверенность называются в ответе поимённо, а не общим списком. Квалифицированная заявка передаётся в учётную систему с идемпотентным ключом от номера заявки — повторная доставка из очереди не должна завести вторую заявку со своим сроком рассмотрения; заявка с неопознанным адресом остаётся в очереди ручного разбора, сервисы работают в Kubernetes.
Обмен суточными рапортами бурения с системами заказчика
Задача, решение и что передали
Задача
Суточный рапорт бурения собирали на кустовой площадке в таблице и отправляли почтой, метры проходки и станко-часы заказчик переносил к себе руками, а спорные строки акта по интервалам выясняли перепиской.
Решение
Рапорт формируется из данных смены — интервал, проходка, режимы, простои с кодом причины — и уходит в контур заказчика сообщением, где ключом служат номер скважины и смена. Очередь удерживает рапорт при обрыве спутникового канала на площадке и досылает его при восстановлении связи, порядок смен сохраняется. Справочник кодов простоев и видов работ ведётся согласованно с заказчиком, поэтому строка акта по интервалам собирается из тех же кодов, что и рапорт.
Сдано 4
Передали шлюз рапортов
Согласованный справочник кодов простоев и видов работ
Очередь с досылкой при обрыве
Сборку акта по интервалам
Как делали
Обследование рапортов Съездили на кустовую площадку и разобрали, как суточный рапорт собирается в таблице и уходит почтой, а метры проходки и станко-часы переносятся у заказчика руками. Отдельно разобрали спорные строки акта по интервалам и увидели, что коды простоев и виды работ у подрядчика и заказчика называются по-разному.
Архитектура очереди Рапорт формируется из данных смены — интервал, проходка, режимы, простои с кодом причины — и уходит в контур заказчика сообщением, где ключом служат номер скважины и смена. Из-за обрывов спутникового канала поставили очередь, удерживающую рапорт и досылающую его с сохранением порядка смен, а справочник кодов согласовали с заказчиком один на обе стороны.
Сборка шлюза Сначала согласовали справочник кодов простоев и видов работ и сборку рапорта из данных смены, потом очередь с досылкой, потом сборку строки акта по интервалам из тех же кодов и панель контроля доставки. На стенде глушили канал посреди отправки и смотрели, что рапорты доходят в порядке смен, а не вперемешку.
Проверка на обрывах Гоняли рапорты по скважинам с обрывами связи на разных стадиях отправки и сверяли строки акта с рапортами тех же смен. Провалом считались рапорт, потерянный при обрыве, и вторая запись по одной скважине и смене; не приняли бы и акт, где вид работ или код простоя не совпадает с кодом в рапорте.
Передача мастерам Передали шлюз рапортов, согласованный справочник кодов простоев и видов работ, очередь с досылкой при обрыве, сборку акта по интервалам и панель контроля доставки. Буровой мастер сдаёт рапорт из своей формы, а служба подрядчика видит по панели, какая площадка не достучалась до контура заказчика.
Архитектура
На кустовой площадке стоит агент на Quarkus: рапорт смены собирается из данных бурения — интервал, проходка, режимы, простои с кодом — пишется в локальную SQLite и кладётся в очередь агента, ключом служат номер скважины и смена. Встроенная база выбрана потому, что на площадке нет второго узла и внешней СУБД, а рапорт обязан пережить перезагрузку станции и обрыв спутникового канала. Доставка идёт шовелом RabbitMQ в центральный брокер, по одной скважине работает один потребитель, поэтому смены приходят в контур заказчика по порядку, а повтор после восстановления связи отбрасывается по ключу и проходку не задваивает. В центре рапорты ложатся в PostgreSQL, в контур заказчика документ уходит объектом WITSML, справочник кодов простоев и видов работ ведётся согласованно и служит источником для строк акта по интервалам; контур поднимается Docker Compose, возраст недоставленных рапортов виден в Grafana.
Путевые листы мусоровозов с рейсами и талонами полигона
Задача, решение и что передали
Задача
Рейсы на контейнерные площадки закрывали по отметкам водителя в путевом листе, а вывоз подтверждали талонами полигона и сводили их с ведомостью вручную.
Решение
Маршрутное задание собирается из графика вывоза по контейнерным площадкам: адрес, число и объём контейнеров, периодичность по договору. Прохождение площадки подтверждается меткой на контейнере и координатой борта, а въезд на полигон закрывается весом брутто и нетто из талона размещения отходов, который приходит обменом с весовой. Пропущенная площадка не исчезает из задания: она остаётся открытой и переносится с причиной из справочника.
Сдано 4
Передали маршрутное задание по площадкам
Путевой лист с рейсами
Реестр талонов полигона
Журнал пропущенных площадок
Как делали
Обследование маршрутов Проехали маршрут с водителем и разобрали, как рейсы закрываются отметками в путевом листе, а вывоз подтверждается талонами полигона, которые потом сводятся с ведомостью вручную. Собрали график вывоза по контейнерным площадкам — адрес, число и объём контейнеров, периодичность по договору — и посмотрели, что отдают весовая полигона и телематика борта.
Архитектура задания Маршрутное задание собирается из графика вывоза, а прохождение площадки подтверждается меткой на контейнере и координатой борта — одной отметки водителя для подтверждения вывоза мало. Въезд на полигон закрывается весом брутто и нетто из талона размещения отходов, приходящего обменом с весовой, а пропущенная площадка остаётся в задании открытой и переносится с причиной из справочника.
Сборка приложения Сначала сборка маршрутного задания из графика и мобильное приложение водителя с меткой на контейнере, потом обмен с весовой по талонам, потом журнал пропущенных площадок и путевой лист с рейсами. На стенде прогоняли рейс с потерей связи на маршруте и въезд на полигон без ответа весовой.
Проверка рейсов Закрытые рейсы сверили с талонами полигона и отметками телематики по тем же бортам. Провалом считались рейс, закрытый без талона размещения отходов, и площадка, отмеченная пройденной без метки контейнера и координаты борта; не приняли бы и пропущенную площадку, тихо выпавшую из задания без причины.
Передача диспетчеру Передали маршрутное задание по площадкам, путевой лист с рейсами, реестр талонов полигона, журнал пропущенных площадок и схему обмена с весовой. Диспетчер правит график вывоза и разбирает переносы по справочнику причин, водитель работает из приложения.
Архитектура
Бортовые терминалы шлют координаты по EGTS на приёмник, тот пишет трек в PostgreSQL с расширением PostGIS, а считанные водителем RFID-метки контейнеров уходят из приложения через MQTT. Прохождение площадки определяется пространственно-временным запросом — точка трека внутри полигона площадки в окне рейса, — и это отдано PostGIS с индексом по геометрии, потому что перебор точек длинного сменного трека в коде приложения превращается в полный обход без индекса. Маршрутное задание собирается из графика вывоза по договору: адрес, число и объём контейнеров, периодичность; непройденная площадка остаётся открытой позицией задания с причиной из справочника, а не исчезает при закрытии рейса. Талон полигона с брутто и нетто приходит обменом с весовой в очередь и привязывается к рейсу по номеру борта и времени въезда, путевой лист и реестр талонов выгружаются в 1С:ERP; сервисы Spring Boot поднимаются Docker Compose.
Кабинет заявителя на технологическое присоединение к сетям
Задача, решение и что передали
Задача
Заявку на технологическое присоединение принимали на бумаге в центре обслуживания, и о выданных технических условиях заявитель узнавал письмом.
Решение
Кабинет ведёт заявку по этапам: подача с точкой присоединения и запрашиваемой мощностью, выдача технических условий, договор, выполнение мероприятий сторонами, акт об осуществлении технологического присоединения. Заявка привязывается к объекту на схеме сети, поэтому центр питания, класс напряжения и точка поставки подставляются из данных сети, а не вводятся заявителем. Документы подписываются в кабинете, срок каждого этапа считается от события в системе и виден заявителю в ленте заявки.
Сдано 4
Кабинет заявителя
Маршрут этапов присоединения
Привязка заявки к схеме сети
Подписание документов
Как делали
Обследование присоединения Разобрали приём заявки на бумаге в центре обслуживания и путь до письма о выданных технических условиях. Прошли этапы с сетевой службой — точка присоединения и запрашиваемая мощность, технические условия, договор, мероприятия сторон, акт об осуществлении технологического присоединения — и посмотрели, что известно о сети: центры питания, класс напряжения, точки поставки.
Архитектура заявки Заявку привязали к объекту на схеме сети, чтобы центр питания, класс напряжения и точка поставки подставлялись из данных сети, а не вводились заявителем. Этапы ведём лентой по событиям в системе и подписываем документы прямо в кабинете — так этап закрывается фактом, а не отметкой сотрудника.
Сборка маршрута Сначала привязка заявки к схеме сети и подстановка параметров, потом маршрут этапов от подачи до акта, потом подписание документов и лента заявки для заявителя. На стенде прогоняли заявки на разные классы напряжения и возврат заявки на доработку.
Проверка этапов Прогнали заявки по всем этапам вместе с центром обслуживания и сверили подставляемые параметры со схемой сети. Провалом считались заявка, привязанная к чужому центру питания, и этап, закрытый в ленте без соответствующего документа; не приняли бы и кабинет, где заявитель не видит, чего от него ждут на текущем этапе.
Передача центру обслуживания Передали кабинет заявителя, маршрут этапов присоединения, привязку заявки к схеме сети, подписание документов и регламент центра обслуживания. Центр обслуживания ведёт заявки в том же кабинете, что и заявитель, бумажный приём остался запасным ходом.
Архитектура
Заявка ведётся процессом в Camunda: подача, выдача технических условий, договор, выполнение мероприятий сторонами и акт — это состояния схемы с таймерами, поэтому срок этапа считается от события процесса и виден заявителю в ленте. Точка присоединения кладётся на слой сети в PostgreSQL с PostGIS, центр питания, класс напряжения и точка поставки подставляются пространственным запросом по ближайшему объекту сети — сеть уже ведётся геометрией, и подстановка из неё снимает ручной ввод и расхождение с диспетчерской схемой. Бэкенд на NestJS общается с процессным движком и смежными системами через RabbitMQ, фронт заявителя собран на Vue, сервисы развёрнуты в Kubernetes. Документы подписываются вызовом КриптоПро DSS на сервере, подпись хранится рядом с хешем документа в карточке заявки; отказ узла движка останавливает переходы этапов, но приём и сохранение новых заявок продолжаются, они отрабатываются после подъёма.
Портал субподрядчика с исполнительной документацией и актами выполненных работ
Задача, решение и что передали
Задача
Исполнительную документацию по захватке субподрядчик возил папками в стройконтроль, а акт на выполненные объёмы согласовывали письмами и переделывали после каждого замечания.
Решение
Портал ведёт комплект по участку и слою: акты освидетельствования скрытых работ, общий журнал работ, протоколы лаборатории по кернам и коэффициенту уплотнения, ведомость объёмов по пикетам. Акт выполненных работ собирается из принятых объёмов и не уходит на подпись, пока по слою нет протокола лаборатории и отметки стройконтроля. Замечание инспектора привязывается к пикету и слою, повторная сдача открывает только отклонённые позиции.
Сдано 4
Портал субподрядчика
Комплект исполнительной документации по пикетам
Сборка акта из принятых объёмов
Журнал замечаний стройконтроля
Как делали
Обследование сдачи работ Разобрали, что субподрядчик возит папками в стройконтроль: акты освидетельствования скрытых работ, общий журнал работ, протоколы лаборатории по кернам и коэффициенту уплотнения, ведомость объёмов по пикетам. Прошли с инспектором порядок замечаний и увидели, что акт переделывается целиком после каждого.
Архитектура комплекта Комплект решили вести по участку и слою, чтобы документ и объём находились по пикету. Акт выполненных работ собирается из принятых объёмов и не уходит на подпись, пока по слою нет протокола лаборатории и отметки стройконтроля, а замечание привязано к пикету и слою — повторная сдача открывает только отклонённые позиции.
Сборка портала Сначала комплект исполнительной документации по участкам, слоям и пикетам, потом загрузка протоколов лаборатории и отметок стройконтроля, потом сборка акта из принятых объёмов, журнал замечаний и подписание. На стенде прогоняли сдачу слоя с отклонёнными позициями и повторную сдачу.
Проверка по пикетам Сдали через портал захватки вместе со стройконтролем и сверили объёмы в акте с ведомостью по пикетам. Провалом считались акт, ушедший на подпись без протокола лаборатории по слою, и замечание, потерявшее привязку к пикету; не приняли бы и повторную сдачу, открывающую заново уже принятые позиции.
Передача стройконтролю Передали портал субподрядчика, комплект исполнительной документации по пикетам, сборку акта из принятых объёмов, журнал замечаний стройконтроля и регламент сдачи работ. Стройконтроль ведёт замечания в портале, субподрядчик подключается по регламенту и сдаёт слой без папок.
Архитектура
Портал на Gin ведёт комплект по участку и слою: акты освидетельствования скрытых работ, общий журнал работ, протоколы лаборатории по кернам и уплотнению и ведомость объёмов по пикетам лежат в PostgreSQL, файлы и сканы — в MinIO с версиями и контрольной суммой. Акт выполненных работ собирается из принятых позиций, и каждая позиция несёт свой статус, а не одна отметка на весь акт: повторная сдача тогда открывает только отклонённые пикеты, и уже принятые объёмы заново не пересогласовываются. Переход акта на подпись закрыт правилом состояния — нет протокола лаборатории по слою или отметки стройконтроля, акт остаётся черновиком. Подпись ставится вызовом КриптоПро DSS, документ хранится в PDF/A с отделённой подписью, события согласования и замечания инспектора расходятся уведомлениями через NATS; фронт на React, сервисы развёрнуты в Kubernetes.
Перевод почты и общих календарей института на отечественную платформу
Задача, решение и что передали
Задача
Переписка с экспертизой, рассылка томов по шифрам проектов и согласование выездов ГИПов держались на зарубежном почтовом сервере под Windows.
Решение
Отечественная почтовая платформа развёрнута на Linux, ящики перенесены вместе с папками, правилами и общими календарями, прежние адреса и списки рассылки сохранены. Крупные вложения выкладываются во внутреннее хранилище, а в письмо подставляется ссылка с ограниченным сроком жизни. Каталог пользователей и узел фильтрации почты вынесены внутрь контура, маршрутизация проверена на тестовых доменах до переключения записей.
Сдано 4
Перенесённые ящики и календари
Схема маршрутизации почты
Регламент крупных вложений
Инструкции пользователям
Как делали
Аудит ящиков Сняли структуру папок, правила сортировки и общие календари ГИПов, выписали действующие адреса и списки рассылки, которыми институт подписан в переписке с экспертизой. Посмотрели, какого размера тома уходят по шифрам проектов и где они упираются в ограничение вложения.
Схема маршрутизации Каталог пользователей и узел фильтрации почты решили держать внутри контура, чтобы адресная книга и правила не зависели от внешнего сервиса. Для томов ввели выкладку во внутреннее хранилище со ссылкой ограниченного срока жизни вместо пересылки файла письмом.
Развёртывание и перенос Развернули отечественную платформу на Linux, подняли каталог и маршрутизацию, перенесли ящики вместе с папками, правилами и общими календарями, сохранив прежние адреса и списки рассылки. Подстановку ссылки на вложение собрали и прогнали на тестовых доменах, не трогая боевые записи.
Прогон на доменах Гоняли переписку по тестовым доменам — входящее от экспертизы, рассылку по списку, приглашение на выезд ГИПа в общий календарь — и проверяли ссылки на тома после истечения срока жизни. Потерянное письмо, несработавшее правило сортировки или неотобразившееся приглашение в чужом календаре считались провалом и останавливали переключение записей.
Передача администратору Отдали перенесённые ящики и календари, схему маршрутизации почты, регламент крупных вложений и инструкции пользователям; с секретариатом и ГИПами прошли отправку тома ссылкой и работу с общим календарём. Администратор института сам заводит ящик, правило и список рассылки.
Архитектура
Почтовый узел собран на Linux из Postfix как MTA, Dovecot для IMAP и общих папок и CalDAV-сервера под общие календари; каталог пользователей и списков рассылки — OpenLDAP внутри контура, из него же берут учётные записи фильтр Rspamd и веб-клиент. Сервис на Spring Boot включён в цепочку Postfix милтером: он вынимает крупное вложение, кладёт объект в MinIO и подставляет в тело ссылку с ограниченным сроком жизни, метаданные ссылок и сроков ведутся в PostgreSQL. Вложения вынесены в объектное хранилище потому, что письмо в списке рассылки размножается копией на каждый ящик, а объект хранится один и удаляется по истечении срока. Маршрутизация проверена на тестовых доменах до переключения MX-записей, очередь Postfix удерживает письма при недоступности узла хранения, настройка и раскатка узлов описаны в Ansible.
Портирование клиента диспетчерской водоканала под доверенную ОС
Задача, решение и что передали
Задача
Мнемосхемы насосных станций и журнал переключений открывались только в клиенте под зарубежной ОС, а сам клиент требовал закрытых компонентов.
Решение
Клиент диспетчерского пункта пересобран под доверенные ОС, отрисовка мнемосхем переведена на открытый стек, обмен с контроллерами станций и водозабора оставлен по прежнему протоколу. Журнал переключений и наряды выездным бригадам ведутся в том же клиенте, печать наряда идёт через системный сервер печати. Переход отрепетирован на стенде-двойнике диспетчерской: подмена рабочего места и откат проверены до перевода смены.
Сдано 4
Пакеты клиента
Схема обмена с контроллерами
Протокол стендовой репетиции
Процедура возврата
Как делали
Разбор диспетчерской Сели со сменным диспетчером и разобрали рабочий день — мнемосхемы насосных станций и водозабора, запись переключений в журнал, выдача наряда выездной бригаде. Выписали закрытые компоненты клиента и опросили, какие контроллеры и по какому протоколу отвечают на объектах.
Границы переделки Обмен с контроллерами станций и водозабора решили не трогать — оставили прежний протокол, чтобы не менять прошивки на объектах, переделке подлежала только клиентская часть. Отрисовку мнемосхем перенесли на открытый стек, печать наряда отдали системному серверу печати.
Пересборка клиента Перерисовали мнемосхемы на новом стеке, перенесли журнал переключений и выдачу нарядов, собрали пакеты клиента. Всё поднимали на стенде-двойнике диспетчерской с подключением к контроллерам, боевые рабочие места до репетиции не трогали.
Стендовая репетиция На стенде отыграли смену — переключение на насосной станции с записью в журнал, выдача и печать наряда бригаде, подмена рабочего места и откат к прежнему. Потерянная запись о переключении, мнемосхема с неактуальным состоянием задвижки или несработавший откат считались провалом и запрещали перевод смены.
Передача сменам Отдали пакеты клиента, схему обмена с контроллерами, протокол стендовой репетиции и процедуру возврата; со сменами прошли работу за новым рабочим местом. Диспетчерская служба сама переводит рабочее место и при необходимости возвращает прежнее.
Архитектура
Рабочее место диспетчера пересобрано на JavaFX: мнемосхемы насосных станций отрисовываются на канве из описания схемы, закрытые компоненты убраны. Обмен с контроллерами станций и водозабора оставлен по OPC UA через Eclipse Milo, клиент работает подпиской на изменение тегов, а не круговым опросом — запись в журнал переключений тогда привязана к факту изменения состояния, а не к периоду опроса. Журнал переключений и наряды выездным бригадам лежат в PostgreSQL на сервере диспетчерского пункта, у клиента остаётся только кэш последних значений, поэтому перезапуск рабочего места не теряет историю смены; печать наряда идёт через CUPS. Пакеты deb раскатываются Ansible, переход отрепетирован на стенде-двойнике диспетчерской, возврат сводится к установке прежней версии пакета.
Стек
Java · JavaFX · PostgreSQL · OPC UA · Ansible · Eclipse Milo · deb · CUPS · Astra Linux
Биржа заявок на спецтехнику с подбором машин моделью
Задача, решение и что передали
Задача
Заявки на технику собирали в чатах и по телефону, а свободную машину искали обзвоном парков.
Решение
Построили биржу с нуля: реестр машин с характеристиками и допусками, заявка с площадкой и окном подачи, аукцион ставок парков, договор и путевой лист одним шагом, рейтинг исполнителя, подписка парка на доступ к заявкам. Языковая модель разбирает свободный текст заявки, достаёт тип машины, вылет стрелы и грузоподъёмность и предлагает подходящие парки.
Сдано 4
Передали биржу
Модуль подписки парков
Реестр характеристик техники
Регламент разбора заявок
Как делали
Разбор заявок Разобрали, как заявка на технику живёт в чатах и по телефону и как свободная машина ищется обзвоном парков. Собрали, чем описывается машина — тип, вылет стрелы, грузоподъёмность, допуски — и как заказчик формулирует потребность в свободном тексте.
Реестр и аукцион Биржу решили строить вокруг реестра машин с характеристиками и допусками и заявки с площадкой и окном подачи, добавив аукцион ставок парков и рейтинг исполнителя. Разбор свободного текста заявки отдали языковой модели, а договор и путевой лист свели в один шаг, чтобы парк не оформлял их порознь.
Сборка биржи Построили биржу с нуля — реестр машин, заявки, аукцион ставок, договор и путевой лист, рейтинг исполнителя, подписка парка на доступ к заявкам. Разбор заявки доводили на архиве переписки, добиваясь, чтобы из текста доставались тип машины, вылет стрелы и грузоподъёмность.
Прогон подбора Прогнали архивные заявки через разбор и подбор и сверили предложенные парки с тем, кто в итоге выходил на площадку, отдельно проверив выпуск договора с путевым листом и работу подписки. Подбор машины, не проходящей по вылету стрелы, грузоподъёмности или допускам площадки, считался провалом и снимал сценарий с выпуска.
Передача паркам Отдали биржу, модуль подписки парков, реестр характеристик техники, регламент разбора заявок и документацию API; с парками и диспетчерами прошли путь заявки от текста до путевого листа. Заказчик сам ведёт реестр характеристик и правила разбора.
Архитектура
Ядро биржи на Axum держит реестр машин с характеристиками и допусками, заявки, лоты и резервы в PostgreSQL, а ставка парка принимается записью по условию совпадения версии лота — иначе две одновременные ставки перетирают друг друга и порядок в аукционе становится невоспроизводимым при разборе спора. Свободный текст заявки уходит в очередь RabbitMQ сервису vLLM: он вынимает тип машины, вылет стрелы и грузоподъёмность в поля заявки черновиком, а список подходящих парков собирается запросом к реестру характеристик и допусков, а не моделью, потому что подбор должен объясняться конкретной строкой реестра. Договор и путевой лист формируются одним шагом и складываются в MinIO, подписка парков на доступ к заявкам и рейтинг исполнителя считаются в ядре, горячие срезы заявок кэшируются в Redis. Публичный интерфейс описан OpenAPI, узлы разложены в Kubernetes.
Разбор объявлений и договоров аренды внутри продукта заказчика
Задача, решение и что передали
Задача
Платформа хранила объявления и сканы договоров, а условия аренды арендодатель переносил в карточку руками.
Решение
Встроили в продукт слой ИИ-функций: извлечение условий договора и объявления в поля карточки объекта, поиск похожих объектов по описанию, черновик ответа на обращение арендатора, проверка объявления на запрещённые формулировки. Слой вынесен в отдельный сервис с квотой по тарифу, каждая выдача пишется в журнал с версией модели и цитатой источника.
Сдано 4
Передали сервис ИИ-функций
Схему квот по тарифам
Набор промптов
Журнал выдач
Как делали
Разбор карточек Посмотрели, как арендодатель переносит условия аренды из скана договора и объявления в карточку объекта, и собрали типовые формулировки договоров и объявлений платформы. Выписали, какие поля карточки заполняются руками чаще всего и какие формулировки в объявлениях считаются запрещёнными.
Отдельный сервис Слой ИИ-функций вынесли в отдельный сервис с квотой по тарифу, чтобы он не смешивался с продуктовым бэкендом и подключался интеграторами. Каждую выдачу решили писать в журнал с версией модели и цитатой источника — без цитаты извлечённое условие в карточку не попадает.
Сборка функций Собрали извлечение условий договора и объявления в поля карточки, поиск похожих объектов по описанию, черновик ответа на обращение арендатора и проверку объявления на запрещённые формулировки. Квоты по тарифам и журнал выдач завели вместе с сервисом.
Прогон архива Прогнали архив сканов договоров и объявлений и сверили извлечённые условия с исходным текстом, отдельно проверив срабатывание на запрещённые формулировки. Условие, подставленное в карточку без цитаты источника, и пропущенная запрещённая формулировка считались провалом и держали функцию выключенной.
Передача интеграторам Отдали сервис ИИ-функций, схему квот по тарифам, набор промптов, журнал выдач и документацию для интеграторов; с командой продукта прошли подключение функции и разбор журнала. Заказчик сам правит промпты, квоты и список запрещённых формулировок.
Архитектура
Слой ИИ-функций вынесен отдельным сервисом на FastAPI с собственным ключом по тарифу: продукт заказчика обращается к нему по сети, а не встраивает модель в себя, поэтому смена весов и промптов не требует выката платформы. Сканы договоров идут конвейером — распознавание на Tesseract, затем извлечение условий в поля карточки объекта с указанием страницы и цитаты, задачи расходятся через RabbitMQ на воркеры и пул vLLM. Поиск похожих объектов идёт по эмбеддингам в pgvector рядом с карточкой в PostgreSQL: отдельная векторная база не заводилась, потому что выдача всё равно ограничивается фильтром по городу, площади и цене, то есть по тем же строкам. Квота по тарифу считается в Redis, каждая выдача — извлечение условий, черновик ответа арендатору, проверка объявления на запрещённые формулировки — пишется в журнал с версией модели и цитатой источника, узлы разложены в Kubernetes.
Проверяется: описание расчётного контура и комиссий
Собственная биржа заказов на спецтехнику с расчётами
Задача, решение и что передали
Задача
Заявки на технику собирали в мессенджерах и почте, а подбор машины, договор и расчёт с владельцем каждый раз складывали руками.
Решение
Построили площадку с нуля: реестр техники с паспортами, допусками и страховками, приём заявок с подбором по типу, вылету и грузоподъёмности, торги по ставке смены, договор и акт из шаблонов с электронной подписью. Расчётный контур ведёт удержание комиссии, выплаты владельцам и обмен с эквайрингом, телематика и сменные рапорты закрывают часы работы и простои, обмен с учётной системой поднимает реализации, акты и взаиморасчёты.
Сдано 4
Модель предметной области площадки
Описание расчётного контура и комиссий
Регламент обмена с учётной системой
Инструкции модератора и владельца техники
Как делали
Обследование заявок Разобрали переписку в мессенджерах и почте, из которой собирались заявки, и выписали признаки подбора машины — тип, вылет, грузоподъёмность. Посмотрели, что лежит в паспортах техники, какие нужны допуски и страховки и как считался расчёт с владельцем по сменным рапортам.
Архитектура площадки В основу положили реестр техники с паспортами, допусками и страховками, а заявку и торги по ставке смены сделали отдельным контуром поверх него. Расчётный контур с удержанием комиссии и выплатами владельцам развели с площадкой, договор и акт собираются из шаблонов с электронной подписью, часы и простои закрываются телематикой и рапортами.
Разработка контура Собрали реестр техники и приём заявок с подбором, затем торги по ставке смены и договор и акт из шаблонов с подписью, затем расчётный контур с комиссией и выплатами. Обмен с учётной системой по реализациям, актам и взаиморасчётам подключали последним, телематику и рапорты сводили на стенде.
Проверка расчётов Гоняли сквозной сценарий от заявки до акта и выплаты, сверяли часы по телематике со сменным рапортом и выставляли технику с истёкшим допуском и страховкой. Провалом считали выход на заявку машины без действующего допуска и расхождение суммы выплаты владельцу с закрытыми часами и удержанной комиссией.
Передача модераторам Передали модель предметной области площадки, описание расчётного контура и комиссий, регламент обмена с учётной системой, инструкции модератора и владельца техники и стенд приёмки. Модераторы сами заводят владельца и технику, проверяют допуски и ведут разбор расхождений по рапортам.
Архитектура
Площадка собрана из сервиса заявок с подбором по типу, вылету и грузоподъёмности, сервиса торгов по ставке смены и расчётного контура. Состояние текущих торгов держится в Redis, итог фиксируется в PostgreSQL одной транзакцией вместе со сделкой: статус сделки и удержание комиссии не должны расходиться, поэтому они в одной базе и одной транзакции. Договор и акт собираются из шаблонов, подписываются криптопровайдером, подписанные файлы лежат в объектном хранилище, а реквизиты и ссылки — в карточке сделки; телематика и сменные рапорты закрывают часы работы и простои отдельным потоком. Обмен с учётной системой по реализациям, актам и взаиморасчётам идёт очередью с повтором и ключом по номеру документа, чтобы недоступность учёта не откатывала сделку на площадке.
Шлюз между процессингом топливных карт, кассами и учётом
Задача, решение и что передали
Задача
Реализация по топливным картам, кассовые смены и эквайринговые реестры доходили до учёта с задержкой, а расхождения по станциям искали по выгрузкам.
Решение
Поставили шлюз с адаптерами к процессингу карт, кассовому программному обеспечению станций и банковским реестрам: транзакции забираются по станциям и сменам, приводятся к единому формату документа отпуска и раскладываются на договоры и лимиты держателей карт. Автосверка сводит кассовую смену, эквайринговый реестр и процессинговые транзакции по литрам и суммам, несведённое попадает в реестр разбора с признаком станции, обмен с учётной системой поднимает реализации, счета-фактуры и взаиморасчёты по договорам.
Сдано 4
Описание адаптеров и форматов
Правила автосверки смен и реестров
Реестр расхождений по станциям
Панель состояния обменов
Как делали
Обследование смен Разобрали, что и с какой задержкой доходит до учёта — реализация по топливным картам, кассовые смены, эквайринговые реестры — и как расхождения по станциям искали по выгрузкам. Собрали форматы процессинга карт, кассового программного обеспечения станций и банковских реестров и договоры с лимитами держателей.
Архитектура шлюза Поставили шлюз с адаптерами к процессингу, кассам станций и банковским реестрам, транзакции приводятся к единому формату документа отпуска. Раскладку на договоры и лимиты держателей карт сделали внутри шлюза, автосверку — отдельным шагом, который сводит кассовую смену, эквайринговый реестр и процессинговые транзакции по литрам и суммам.
Разработка адаптеров Сначала адаптеры и приведение транзакций по станциям и сменам, потом раскладка на договоры и лимиты держателей карт, потом автосверка и реестр разбора с признаком станции. Обмен с учётной системой по реализациям, счетам-фактурам и взаиморасчётам подключали последним, на стенде крутили смены прошлых периодов.
Проверка сверки Сводили архивные смены станций по литрам и суммам, гоняли повторную загрузку реестра и обрыв связи со станцией, проверяли отпуск сверх лимита держателя. Провалом считали расхождение литров или суммы между сменой, реестром и процессингом, не попавшее в реестр разбора, и задвоение документа отпуска при повторной загрузке.
Передача сопровождению Передали описание адаптеров и форматов, правила автосверки смен и реестров, реестр расхождений по станциям, панель состояния обменов и регламент дежурного сопровождения. Сопровождение само подключает станцию, правит правила сверки и разбирает расхождения по признаку станции.
Архитектура
Шлюз держит три адаптера: процессинг топливных карт, кассовое программное обеспечение станций и банковские реестры файлами по SFTP. Транзакции забираются по станциям и сменам, приводятся к единому формату документа отпуска и раскладываются на договоры и лимиты держателей карт; идентификатор транзакции процессинга служит идемпотентным ключом, поэтому повторная выгрузка смены не задваивает реализацию. Документы, договоры и лимиты живут в PostgreSQL, а сверочные выборки по литрам и суммам за период по всем станциям идут в ClickHouse: им нужны агрегаты по нескольким колонкам на большом объёме строк, а не транзакции. Автосверка сводит кассовую смену, эквайринговый реестр и процессинговые транзакции, несведённое падает в реестр разбора с признаком станции, обмен с учётной системой по реализациям, счетам-фактурам и взаиморасчётам идёт через RabbitMQ с повтором.
Кабинет корпоративного клиента по топливным картам и лимитам
Задача, решение и что передали
Задача
Лимиты по картам меняли заявкой в офис, отчёт о заправках присылали письмом раз в месяц.
Решение
Кабинет ведёт карты компании: выпуск и блокировка, лимиты по литрам, суммам, маркам топлива, времени и станциям, привязка карты к автомобилю и водителю. Транзакции приходят с касс станций потоком и раскладываются по картам и подразделениям, кабинет отдаёт выписку, реестр заправок и закрывающие документы за период. Изменение лимита уходит на кассовый контур очередью и применяется без выпуска новой карты.
Сдано 4
Передали кабинет
Реестр карт и лимитов
Поток транзакций со станций
Регламент блокировки карты
Как делали
Обследование лимитов Разобрали, как менялись лимиты — заявка в офис, письмо, ручная правка в кассовом контуре — и как собирался месячный отчёт о заправках, сняли формат транзакций с касс станций. Выписали ограничения, которые нужны корпоративным клиентам: литры, суммы, марки топлива, время, станции.
Архитектура карт Карту сделали сущностью с привязкой к автомобилю и водителю, а лимиты — набором правил поверх неё, чтобы изменение не требовало выпуска новой карты. Поток транзакций со станций принимаем и раскладываем по картам и подразделениям, изменение лимита уходит в кассовый контур очередью.
Разработка кабинета Собрали реестр карт с выпуском и блокировкой, затем движок лимитов по литрам, суммам, маркам топлива, времени и станциям, затем приём транзакций и разложение по подразделениям. Кабинет с выпиской, реестром заправок и закрывающими документами и обмен с кассовым контуром отрабатывали на стенде с записанным потоком касс.
Проверка транзакций Проигрывали суточный поток транзакций, гоняли заправку на границе лимита и после блокировки карты, повторяли доставку изменения лимита при недоступной станции, сверяли выписку с реестром заправок. Провалом считали заправку, прошедшую по карте после блокировки, и выписку, которая не сходится с транзакциями станций за период.
Передача кабинета Отдали кабинет, реестр карт и лимитов, приём потока транзакций со станций, регламент блокировки карты и выписку с закрывающими документами. Ответственный за парк у клиента сам выпускает и блокирует карты и меняет лимиты, бухгалтерия забирает закрывающие документы из кабинета.
Архитектура
Транзакции с касс станций идут в Kafka с ключом по номеру карты, поэтому события одной карты сохраняют порядок и раскладываются по подразделениям без гонок, а авторизация на кассе выполняется по ISO 8583 через адаптер, проверяющий лимиты по литрам, суммам, маркам, времени и станциям. Реестр карт, лимитов и привязок к автомобилям и водителям ведётся версионно в PostgreSQL: изменение лимита пишется новой версией и уходит на кассовый контур очередью, кассовый узел держит локальный срез, поэтому карта не перевыпускается и станция принимает её при обрыве канала. Выписки, реестр заправок и закрывающие документы собираются из ClickHouse — выписка читает диапазон дат по сотням карт разом, и колоночное хранилище поднимает только нужные колонки, а не всю строку транзакции. Кабинет на chi отдаёт срезы и документы, Redis держит остатки лимитов текущего периода, сервисы живут в Kubernetes.
Стек
Go · chi · PostgreSQL · ClickHouse · Kafka · Kubernetes · Redis · ISO 8583 · Grafana
Мобильный продукт для арендаторов с бэкендом под доверенные ОС
Задача, решение и что передали
Задача
Продукт работал в зарубежном облаке с проприетарной базой и внешним push-сервисом, поэтому арендодатели не могли поставить его у себя.
Решение
Бэкенд перенесли на PostgreSQL и собрали под доверенные ОС: сервис заявок на обслуживание и пропусков, начисления по договорам аренды, хранилище договоров и актов, собственный сервер push-уведомлений вместо зарубежного. Мобильные приложения пересобрали для отечественных магазинов приложений и внутреннего канала обновления, административное рабочее место перевели в веб. Поставка собрана подписанными пакетами с установщиком и оффлайн-лицензией.
Сдано 4
Передали исходный код бэкенда и приложений
Сборочный конвейер с подписанными пакетами
Схему базы и миграции
Руководство по установке в контуре заказчика
Как делали
Обследование зависимостей Разобрали, чем продукт держался за зарубежное облако: проприетарная база и внешний push-сервис. Прошли с арендодателями сценарии, которые они хотят держать у себя, — заявки на обслуживание, пропуска, начисления по договорам аренды — и выписали, что мешает установке в их контуре.
Архитектура бэкенда Бэкенд перевели на PostgreSQL и собрали под доверенные ОС, push вынесли в собственный сервер уведомлений вместо зарубежного, договоры и акты положили в своё хранилище. Административное рабочее место увели в веб, поставку собрали подписанными пакетами с установщиком и оффлайн-лицензией.
Разработка и сборка Сначала перенос хранения со схемой и миграциями, затем сервисы заявок, пропусков и начислений по договорам аренды, затем собственный push-сервер. Мобильные приложения пересобрали для отечественных магазинов и внутреннего канала обновления, конвейер сборки обкатали на стенде с доверенными ОС.
Проверка поставки Разворачивали поставку в изолированном контуре и гоняли заявку от подачи до закрытия, выписку пропуска и начисление по договору, проверяя доставку уведомлений своим сервером на пересобранных приложениях. Не приняли бы работу, если бы уведомление доходило только через зарубежный сервис или если бы установка в контуре заказчика требовала внешнего лицензионного узла.
Передача продукта Передали исходный код бэкенда и приложений, сборочный конвейер с подписанными пакетами, схему базы и миграции, руководство по установке в контуре заказчика и инструкции администратора. Арендодатель ставит продукт у себя и обновляет его из внутреннего канала сам.
Архитектура
Бэкенд на NestJS разделён на модули заявок на обслуживание, пропусков, начислений по договорам аренды и хранилища документов, единое состояние держит PostgreSQL, договоры и акты лежат в объектном хранилище. Push вынесен в собственный сервер: он держит соединения с устройствами и очередь недоставленных сообщений, потому что в закрытом контуре заказчика внешний сервис уведомлений недоступен, а приложение должно получать заявку без постоянного опроса сервера. Фоновые начисления и рассылки идут через RabbitMQ, Redis держит сессии и срез справочников, административное рабочее место переведено в веб и ходит в тот же API, что и мобильные приложения. Поставка собрана подписанными пакетами с установщиком и оффлайн-лицензией, узлы запускаются в Docker, совместимость проверена на Astra Linux.
Собственный контроль каналов утечки взамен зарубежной DLP
Задача, решение и что передали
Задача
Зарубежная DLP перестала обновляться и не ставилась на рабочие места, переведённые на отечественную ОС, а съёмные носители и почта остались без контроля.
Решение
Собрали контур на отечественных ОС: агент рабочего места с перехватом печати, съёмных носителей и буфера обмена, теневое копирование отправленных файлов, разбор почтового и веб-трафика на шлюзе, сервер политик с правилами по группам пользователей и категориям документов. События и копии складываются в PostgreSQL и объектное хранилище с неизменяемой записью, консоль офицера безопасности открывает инцидент вместе с копией файла. Агент собран под доверенные ОС и отечественный процессор, обновляется из внутреннего репозитория пакетов.
Сдано 4
Передали исходный код агента и сервера политик
Сборки под доверенные ОС
Набор политик и категорий документов
Регламент разбора инцидентов
Как делали
Обследование каналов Сняли, что осталось без контроля после перевода рабочих мест на отечественную ОС: печать, съёмные носители, буфер обмена, почта и веб. Собрали категории документов, которые нельзя выпускать наружу, — договоры поставки топлива, отчёты станций, клиентские списки — и проверили, на каком железе стоят рабочие места, включая отечественные процессоры.
Архитектура контура Агент рабочего места ставится на доверенные ОС и перехватывает печать, съёмные носители и буфер обмена, почтовый и веб-трафик разбирается на шлюзе, а правила по группам пользователей и категориям документов ведёт отдельный сервер политик. События и теневые копии складываем в PostgreSQL и объектное хранилище неизменяемой записью, консоль офицера открывает инцидент вместе с копией файла.
Разработка агента Сначала агент под доверенную ОС и отечественный процессор, затем сервер политик с категориями документов, затем разбор почты и веба на шлюзе. Теневое копирование, консоль офицера безопасности и обновление агента из внутреннего репозитория пакетов собирали на стенде с образцами документов станций.
Проверка перехвата Выносили помеченные документы на флешку, в печать, через буфер обмена, почтой и веб-загрузкой с рабочих мест на разных доверенных ОС и процессорах и сверяли теневую копию с отправленным файлом. Провалом считали канал, по которому документ уходит без события и копии, и агента, который не встаёт на заявленную доверенную ОС.
Передача контура Передали исходный код агента и сервера политик, сборки под доверенные ОС, набор политик и категорий документов, регламент разбора инцидентов и инструкции офицера безопасности. Служба безопасности сети сама правит правила, заводит категории и собирает агента из репозитория.
Архитектура
Агент рабочего места перехватывает печать, съёмные носители и буфер обмена, держит локальный буфер на время потери связи и шлёт события на приёмник по gRPC с взаимной проверкой сертификатов. Приёмник кладёт события в Kafka, разборщики раскладывают их в ClickHouse: поток однороден, пишется пачками и читается агрегатами по пользователю, каналу и категории документа за период, поэтому под события взято колоночное хранилище, а политики, категории и карточки инцидентов остались в PostgreSQL. Теневые копии отправленных файлов складываются в MinIO с версионированием и блокировкой объекта, так что копия не переписывается и не удаляется до истечения срока хранения. Веб- и почтовый трафик разбирается на шлюзе по ICAP тем же сервисом правил, что применяет агент; сборка идёт под отечественный процессор, обновление раскатывается Ansible из внутреннего репозитория на Astra Linux.
Стек
Go · gRPC-Go · PostgreSQL · ClickHouse · Kafka · Ansible · MinIO · ICAP · Astra Linux
Обследование учёта парка перед собственной системой управления техникой
Задача, решение и что передали
Задача
Парк вели в коробочной программе и журналах механиков, местонахождение машин уточняли звонками, а наработку переносили в таблицы вручную.
Решение
Сверили три источника — карточки техники в учётной программе, телематику с бортовых блоков, журналы выдачи и возврата — и собрали каталог расхождений по моточасам, топливу и простоям. Описали процессы заявки, подачи, наряда, возврата и межсменного обслуживания с событиями и ответственными. Спроектировали узлы будущей системы: реестр единиц техники, график занятости, наряды механиков, приём телематики, расчёт наработки, документы аренды и обмен с учётом.
Сдано 4
Каталог расхождений по наработке
Схема приёма телематики
Модель реестра техники
Карта процессов аренды
Как делали
Обследование парка Сверили три источника — карточки техники в учётной программе, телематику с бортовых блоков, журналы выдачи и возврата — и собрали каталог расхождений по моточасам, топливу и простоям. Прошли с механиками и диспетчерами заявку, подачу, наряд, возврат и межсменное обслуживание и записали события с ответственными.
Архитектура системы Спроектировали узлы будущей системы: реестр единиц техники, график занятости, наряды механиков, приём телематики, расчёт наработки, документы аренды и обмен с учётом. Наработку решили считать от телематики с бортовых блоков, а журналы выдачи и возврата оставить источником событий аренды, а не моточасов.
Разработка модели Собирали модель и дорожную карту: раскладывали процессы аренды на события с ответственными, прогоняли выгрузку телематики через черновой расчёт наработки на стенде и смотрели, сходится ли он с журналами механиков. Документы аренды разобрали по составу полей, чтобы понять, что придётся вести в системе.
Проверка сверки Повторяли сверку на другой выборке машин и периодов, разбирали каждое расхождение по моточасам и топливу до источника и показывали карту процессов диспетчерам и механикам. Работу не приняли бы, если бы расхождение по наработке осталось без объяснённого источника или если бы событие процесса аренды не имело ответственного.
Передача дорожной карты Передали каталог расхождений по наработке, схему приёма телематики, модель реестра техники, карту процессов аренды и дорожную карту системы. Заказчик сам повторяет сверку по каталогу и решает очерёдность узлов будущей системы.
Архитектура
Три источника сводятся по инвентарному номеру и идентификатору бортового блока: карточки техники из учётной программы, телематика с блоков и журналы выдачи и возврата. Приёмник телематики принимает пакеты по Wialon IPS и пишет точки в TimescaleDB, потому что моточасы, топливо и простои считаются интервалами временного ряда, а в общей строчной таблице учёта такие выборки шли бы сканом всей истории. Сервис на Spring Boot ведёт реестр единиц техники, график занятости и наряды механиков в PostgreSQL, события обмена с учётной системой идут через Kafka. Расчёт расхождений по наработке и суточные сверки запускает Airflow, каталог расхождений и срезы занятости открываются в Superset, окружение собрано в Docker.
Оператор во время разговора искал ответ в трёх системах и в регламенте на десятки страниц, пассажир в это время слушал паузу.
Решение
Аудио снимается с телефонии и распознаётся потоково, реплики нормализуются, и по ним идёт гибридный поиск по регламентам и базе решений: совпадения по ключевым словам плюс векторная близость. Оператору выводится карточка с фрагментом документа, его пунктом и ссылкой на источник, клиенту ничего не отправляется автоматически. Если на шумной линии распознавание падает по уверенности, подсказка строится по последним разборчивым репликам, а не подставляет случайный фрагмент.
Сдано 3
Сервис подсказок
Панель оператора
Индекс регламентов с процедурой обновления и журнал сессий для разбора спорных звонков
Как делали
Наблюдение за оператором Сели рядом с операторами и записали, в каких трёх системах и на каких страницах регламента они ищут ответ, пока пассажир слушает паузу. Послушали записи разговоров, в том числе с шумной линии, и выписали повторяющиеся вопросы.
Гибридный поиск Аудио снимается с телефонии и распознаётся потоково, реплики нормализуются, а поиск сделан гибридным: по ключевым словам, потому что номер рейса и название станции нужны точно, плюс векторная близость по смыслу. Клиенту ничего не уходит автоматически — оператор видит карточку с фрагментом документа, его пунктом и ссылкой на источник.
Сборка подсказок Подняли поток аудио и распознавание, затем нормализацию реплик и индекс регламентов и базы решений, затем панель оператора и журнал сессий. На стенде отрабатывали падение уверенности распознавания: подсказка строится по последним разборчивым репликам.
Прогон записей Прогнали записи разговоров, включая шумные, и сверили карточки с тем, что оператор должен был ответить по регламенту. Провалом считали подсказку по отменённому пункту, подстановку случайного фрагмента при неразборчивой речи и карточку, приходящую уже после того, как оператор ответил сам.
Передача методистам Передали сервис подсказок, панель оператора, индекс регламентов с процедурой обновления и журнал сессий. Методисты обновляют регламенты и базу решений сами, супервизор разбирает спорные звонки по журналу.
Архитектура
Медиашлюз забирает разговор с телефонии по SIP и режет поток на кадры, потоковое распознавание на Vosk отдаёт частичные гипотезы, они складываются в реплики и уходят в поисковый сервис на FastAPI. Поиск гибридный по одному индексу OpenSearch: совпадения по ключевым словам и векторная близость считаются в одном запросе, потому что номер пункта регламента находится точным совпадением, а формулировка пассажира — только по смыслу, и разносить их по двум хранилищам значило бы сшивать выдачу вручную. Карточка с фрагментом, пунктом и ссылкой на источник приходит в панель оператора по WebSocket, состояние сессии разговора лежит в Redis Streams и переживает переподключение панели, журнал сессий и версии регламентов — в PostgreSQL. Уверенность распознавания проверяется до запроса: ниже порога подсказка строится по последним разборчивым репликам, наружу клиенту сервис ничего не отправляет; узлы подняты контейнерами рядом с телефонией.
Извлечение условий договоров перевозки в единый реестр
Задача, решение и что передали
Задача
Ставки, сроки подачи, штрафы и порядок претензий жили в подписанных PDF и сканах, и при споре их искали вручную по папкам.
Решение
Собрали конвейер: сканы проходят OCR, документ режется на разделы по нумерации и заголовкам, дальше модель извлекает поля и обязана указать страницу и абзац, откуда взято значение. Поля с низкой уверенностью не попадают в реестр молча — они уходят юристу в веб-форму, где рядом показан исходный фрагмент, а подтверждённые правки копятся как выборка для дообучения. Реестр отдаёт условия по API, поэтому смежные системы берут ставку и сроки из одного места.
Сдано 3
Конвейер разбора
Реестр условий с API
Интерфейс подтверждения для юриста и ссылка на исходный фрагмент у каждого поля
Как делали
Разбор договоров Подняли папки с подписанными PDF и сканами договоров перевозки и посмотрели, как в них записаны ставки, условия подачи, штрафы и порядок претензий. С юристами разобрали, что именно они ищут при споре и какая формулировка считается основанием.
Ссылка обязательна Конвейер разделили на OCR, нарезку документа на разделы по нумерации и заголовкам и извлечение полей, причём поле без указания страницы и абзаца в реестр не принимается. Значения с низкой уверенностью не пишутся молча, а уходят юристу; реестр отдаёт условия по API, чтобы смежные системы брали ставку из одного места.
Сборка конвейера Собрали распознавание сканов и разбор структуры, затем извлечение полей со ссылкой на фрагмент, затем веб-форму подтверждения для юриста с показом исходного куска рядом. Подтверждённые правки копятся как выборка для дообучения.
Сверка с юристами Сверили реестр с договорами, разобранными юристами вручную, и проверили каждую ссылку до абзаца. Провалом считали ставку или штраф без ссылки на страницу и абзац, ссылку на другой раздел и поле с низкой уверенностью, попавшее в реестр в обход формы подтверждения.
Передача реестра Передали конвейер разбора, реестр условий с API, интерфейс подтверждения для юриста и ссылки на исходный фрагмент у каждого поля. Юристы ведут подтверждение сами, смежные системы подключаются к реестру по описанному API.
Архитектура
Конвейер идёт очередями Celery по шагам: приём файла в MinIO, распознавание на PaddleOCR, разрезание на разделы по нумерации и заголовкам через layout-разбор, извлечение полей моделью за vLLM. Шаги разделены, потому что распознавание скана и генерация — разные по времени и по железу операции: OCR идёт на CPU-узлах, извлечение на узле с картой, и падение одного шага не заставляет проходить документ с начала. Каждое значение пишется в PostgreSQL вместе со ссылкой на страницу и абзац исходника и оценкой уверенности; ниже порога поле в реестр не попадает, а встаёт в очередь юриста, чья веб-форма на FastAPI показывает рядом исходный фрагмент из MinIO. Подтверждённые правки копятся отдельной таблицей как выборка для дообучения, реестр отдаёт действующие условия по HTTP API, поэтому смежные системы берут ставку и сроки из одного места, а не из своих копий договоров.
Стенд проверки модели на подмену инструкций и утечку данных
Задача, решение и что передали
Задача
Ассистента для диспетчерской собирались подключить к рабочим системам, но как модель ведёт себя на враждебных входах, никто не проверял.
Решение
Собрали стенд с наборами сценариев: инструкции, спрятанные в тексте загружаемых документов и в полях заявок, попытки вытянуть системный промпт и содержимое поискового индекса, обход тематических ограничений, склейка ролей в диалоге. Каждый сценарий прогоняется с фиксированными параметрами генерации, срабатывание сохраняется вместе с полным входом и ответом, чтобы его можно было воспроизвести. Прогон встроили в сборку — обновление модели, шаблона промпта или правил фильтрации не уезжает в контур без чистого прогона.
Сдано 4
Передали стенд с наборами сценариев
Отчёты прогонов с примерами срабатываний
Правила фильтрации входа и вывода
Регламент допуска обновлений
Как делали
Разбор допуска Разобрали, к каким рабочим системам диспетчерской собирались подключить ассистента и что он сможет прочитать. Выписали, откуда в него попадает чужой текст: загружаемые документы и поля заявок — именно там прячется подставная инструкция.
Воспроизводимый прогон Проверку сделали воспроизводимой: каждый сценарий гоняется с фиксированными параметрами генерации, срабатывание сохраняется вместе с полным входом и ответом. Наборы разделили по типам — инструкции в документах и в полях заявок, попытки вытянуть системный промпт и содержимое индекса, обход тематических ограничений, склейка ролей — и встроили прогон в сборку.
Сборка стенда Собрали стенд и наборы сценариев, затем хранение прогонов с примерами срабатываний, затем правила фильтрации входа и вывода и включение прогона в конвейер GitLab CI.
Прогон сценариев Прогоняли стенд на действующей сборке ассистента и на изменённых шаблонах промпта и правилах фильтрации. Провалом считали выполнение инструкции, пришедшей из текста документа или поля заявки, и выдачу системного промпта или содержимого поискового индекса — при таком срабатывании обновление в контур не уезжает.
Передача с регламентом Передали стенд с наборами сценариев, отчёты прогонов с примерами срабатываний, правила фильтрации входа и вывода и регламент допуска обновлений. Команда заказчика пополняет наборы и держит прогон в сборке сама.
Архитектура
Стенд поднимается Docker Compose как копия контура: проверяемая модель за vLLM, поисковый индекс с тестовым корпусом и раннер на pytest, гоняющий наборы сценариев — инструкции в тексте загружаемых документов и в полях заявок, попытки вытянуть системный промпт и содержимое индекса, обход тематических ограничений, склейка ролей. Прогон разложен по задачам через Redis, потому что каждый сценарий — это ожидание генерации, и последовательный проход растягивал бы сборку до неприемлемого для выкатки времени. Параметры генерации в прогоне фиксированы, полный вход и ответ сохраняются в MinIO со ссылкой из записи в PostgreSQL: срабатывание надо воспроизводить дословно, по одному вердикту разбор не собрать. Прогон встроен обязательным шагом в GitLab CI — обновление модели, шаблона промпта или правил фильтрации не уезжает в контур без чистого прогона, отчёт собирается Allure.
Проверяется: протоколы сверки начислений и план отката
Перенос расчёта тарифов с Oracle на PostgreSQL
Задача, решение и что передали
Задача
Тарификация перевозок жила в пакетах PL/SQL и заданиях планировщика Oracle, продление лицензий упиралось в бюджет, а переписывать логику заново никто не брался.
Решение
Перенесли схему и данные, пакеты PL/SQL переписали на PL/pgSQL, иерархические запросы CONNECT BY заменили рекурсивными CTE, обращения к ROWID и последовательностям привели к идентификаторам PostgreSQL, задания DBMS_SCHEDULER вынесли во внешний планировщик. Контуры работали параллельно на одном входном потоке, начисления сверялись построчно, каждое расхождение разбиралось до причины и правки в коде. Отказоустойчивость собрали на потоковой репликации с автоматическим переключением, раскладку по табличным пространствам и обслуживание описали в регламенте.
Сдано 4
Передали перенесённую схему с кодом на PL/pgSQL
Протоколы сверки начислений
Кластер с репликацией и переключением
Регламент эксплуатации и план отката
Как делали
Разбор пакетов Разобрали пакеты PL/SQL, в которых жила тарификация перевозок, и задания планировщика Oracle, восстановив по коду, как считается начисление. Выписали конструкции без прямой пары: CONNECT BY, ROWID, последовательности, DBMS_SCHEDULER.
Перенос без переписывания Логику решили переносить, а не писать заново: пакеты — в PL/pgSQL, иерархические запросы — в рекурсивные CTE, ROWID и последовательности — к идентификаторам PostgreSQL, задания — во внешний планировщик. Контуры договорились держать параллельно на одном входном потоке, а отказоустойчивость собрать на потоковой репликации с автоматическим переключением.
Параллельный контур Перенесли схему и данные, переписали пакеты, вывели задания наружу, подняли кластер с репликацией и раскладкой по табличным пространствам. Параллельный контур запустили на том же входном потоке перевозок, что и Oracle.
Построчная сверка Сверяли начисления построчно между контурами и каждое расхождение разбирали до причины и правки в коде. Провалом считали любую строку начисления, разошедшуюся с прежним контуром, и переключение узла, после которого расчёт не поднимался на реплике.
Передача эксплуатации Передали перенесённую схему с кодом на PL/pgSQL, протоколы сверки начислений, кластер с репликацией и переключением, регламент эксплуатации и план отката. Администраторы отработали переключение и возврат своими руками.
Архитектура
Схему и данные перенесли ora2pg, пакеты PL/SQL переписали на PL/pgSQL, иерархические CONNECT BY заменили рекурсивными CTE, а ROWID и последовательности привели к идентификаторам PostgreSQL. Задания DBMS_SCHEDULER заменил внешний сервис на Go с chi: расписание и состояние заданий стали видимыми снаружи базы и переживают её перезапуск, тогда как внутренний планировщик исчезал вместе с экземпляром СУБД. Входной поток начислений на время перехода дублировался в оба контура через Kafka — это дало одинаковые входы без остановки боевого расчёта, а сверщик читал результаты обеих баз построчно и раскладывал расхождения по причинам до правки в коде. Кластер собран на потоковой репликации под управлением Patroni с автоматическим переключением, приложения ходят через PgBouncer, поэтому смена ведущего узла не требует правки строк подключения; раскладка по табличным пространствам и обслуживание описаны в регламенте.
Стек
Go · chi · PostgreSQL · Oracle · Kafka · Patroni · PL/pgSQL · ora2pg · PgBouncer
Менеджеры считали стоимость доставки в личных кабинетах перевозчиков, вручную заводили накладные и переносили трек-номера в заявки.
Решение
Сделали шлюз с отдельным адаптером на каждого перевозчика: тарификация, создание накладной, печать этикетки, опрос статусов по расписанию. Ответы приводим к общей модели событий, коды перевозчиков сводим в один справочник статусов, повторная отправка идёт по идемпотентному ключу заявки. Недоступность одного адаптера не останавливает остальные: сообщения копятся в очереди и дозаливаются после восстановления.
Сдано 4
Передали шлюз с адаптерами
Справочник маппинга статусов
Регламент подключения нового перевозчика
Панель очередей и журнал обменов
Как делали
Обход кабинетов Прошли по личным кабинетам перевозчиков, которыми пользовались менеджеры, и выписали, что у кого есть: тарификация, создание накладной, печать этикетки, опрос статусов. Сравнили коды статусов разных перевозчиков между собой и с тем, что должно быть видно в заявке.
Адаптеры за шлюзом На каждого перевозчика сделали отдельный адаптер за общим шлюзом, ответы приводятся к общей модели событий, коды сводятся в один справочник статусов. Повторная отправка идёт по идемпотентному ключу заявки, сообщения копятся в очереди, поэтому недоступность одного адаптера не останавливает остальные.
Сборка шлюза Собрали шлюз и общую модель событий, затем адаптеры по одному, затем опрос статусов по расписанию, печать этикеток и обмен с 1С. На стенде выключали адаптеры и смотрели, как разбирается накопленная очередь.
Повторы и отказы Повторяли расчёт и создание накладной по одной и той же заявке и держали адаптер выключенным во время потока заказов. Провалом считали задвоенную накладную при повторной отправке, потерянное сообщение после восстановления адаптера и статус перевозчика, не нашедший пары в общем справочнике.
Передача с регламентом Передали шлюз с адаптерами, справочник маппинга статусов, регламент подключения нового перевозчика, панель очередей и журнал обменов. Заказчик подключает перевозчика по регламенту и разбирает очереди по панели сам.
Архитектура
Шлюз на FastAPI держит общий контракт, под каждым перевозчиком стоит свой адаптер: часть говорит по HTTP с JSON, часть по SOAP, ответы приводятся к одной модели событий, коды сводятся справочником статусов в PostgreSQL. Между шлюзом и адаптерами стоит RabbitMQ с отдельной очередью на перевозчика, потому что кабинет одного отвечает медленно или падает, и на прямых вызовах его таймауты занимали бы общий пул воркеров — так недоступность адаптера не останавливает остальные. Создание накладной и печать этикетки идут по идемпотентному ключу заявки: повтор после обрыва возвращает уже созданную накладную, а не заводит вторую, уникальность держит индекс в базе. Статусы опрашиваются по расписанию и отдаются в 1С через OData, тарифные ответы кешируются в Redis на короткий срок, накопленные сообщения дозаливаются после восстановления адаптера, длина очередей и возраст статусов видны в Prometheus.
Проверяется: список расхождений и загрузчики данных карт
Учёт рейсов, путевых листов и расхода топлива
Задача, решение и что передали
Задача
Рейсы и путевые листы велись в таблицах, данные топливных карт сверяли с отчётами водителей вручную.
Решение
Свели рейс в одну запись: задание, водитель, машина, показания одометра, время выезда и возврата. Данные топливных карт и телематики подгружаются файлами и по API, расхождение между нормой, картой и датчиком уровня выносится в отдельный список для разбора. Путевой лист формируется из рейса, закрытие рейса без обязательных показаний блокируется.
Сдано 3
Передали модуль рейсов и путевых листов
Загрузчики данных карт и телематики
Список расхождений и печатные формы
Как делали
Разбор таблиц Разобрали таблицы, в которых велись рейсы и путевые листы, и то, как отчёт водителя вручную сверяли с выпиской по топливной карте. Выписали, что приходит файлами, а что можно забирать из телематики по API.
Рейс одной записью Рейс сделали одной записью — задание, водитель, машина, показания одометра, время выезда и возврата, — а путевой лист формируется из неё, а не заводится отдельно. Расхождение между нормой, картой и датчиком уровня не прячется в отчёт, а выносится в отдельный список для разбора; закрытие рейса без обязательных показаний блокируется.
Сборка учёта Собрали модуль рейсов и путевых листов, затем загрузчики файлов топливных карт и обмен с телематикой, затем список расхождений и печатные формы.
Сверка с диспетчером Прогнали закрытые рейсы с загруженными данными карт и телематики и сравнили с тем, как эти же рейсы сводил диспетчер вручную. Провалом считали рейс, закрытый без показаний одометра, заправку по карте, не привязавшуюся к рейсу, и расхождение, не попавшее в список для разбора.
Передача диспетчерам Передали модуль рейсов и путевых листов, загрузчики данных карт и телематики, список расхождений и печатные формы. Диспетчеры разбирают расхождения и правят нормы расхода без обращения к разработчику.
Архитектура
Рейс — одна запись в PostgreSQL, к которой привязаны задание, водитель, машина, показания одометра и отметки выезда и возврата; сервис на Spring Boot отдаёт её и печатные формы, загрузчики данных работают отдельными заданиями. Загрузка разведена по способам: топливные карты приходят файлами CSV в каталог обмена, телематика опрашивается по API поставщика, оба потока кладут сырые строки в отдельную таблицу до сопоставления с рейсом — без сохранения исходных строк разбор расхождения упирался бы в отсутствие первички. Сопоставление и расчёт расхождения между нормой, картой и датчиком уровня вынесены в фоновые задачи через RabbitMQ, потому что суточный файл приходит одним куском и обрабатывать его в потоке HTTP-запроса нечем. Закрытие рейса без обязательных показаний блокируется проверкой на сервере, результат сопоставления попадает в список для разбора, нормы и маршруты кешируются в Redis, учётные данные синхронизируются через OData 1С, вход идёт через Keycloak.
Мониторинг деградации модели прогноза сроков доставки
Задача, решение и что передали
Задача
Прогноз срока доставки работал в продуктиве без наблюдения, и о расхождении с фактом узнавали из жалоб клиентов.
Решение
Рядом с сервисом прогноза поставлен монитор: он сверяет предсказание с фактическим временем закрытия рейса и следит за распределением входных признаков. Сдвиг распределения и рост ошибки на сегменте маршрутов поднимают алерт с указанием сегмента и версии модели. Переобучение идёт по регламенту, новая версия сначала работает на теневом трафике.
Сдано 4
Переданы монитор качества и сдвига данных
Алерты по сегментам маршрутов
Схема теневого прогона
Регламент переобучения
Как делали
Обследование Подняли, как о расхождении прогноза с фактом узнавали из жалоб клиентов, и нашли, где лежит фактическое время закрытия рейса. Разобрали, на каких сегментах маршрутов прогноз расходится чаще.
Архитектура Монитор поставили рядом с сервисом прогноза, а не внутрь него, чтобы наблюдение не влияло на выдачу. Сверяем предсказание с фактическим временем закрытия рейса и следим за распределением входных признаков, алерт поднимаем с указанием сегмента и версии модели, а новую версию сначала пускаем на теневой трафик.
Разработка Сначала сбор пар прогноза и факта закрытия рейса, затем контроль сдвига распределения признаков и ошибки по сегментам маршрутов, затем алерты и теневой прогон новой версии. На стенде воспроизводили прошлые периоды, где расхождение уже случалось.
Проверка Проигрывали архив рейсов, подменяли распределение признаков на отдельном сегменте и смотрели, поднимется ли алерт раньше жалоб. Провалом считали ушедший сдвиг на сегменте маршрутов без алерта и версию, выкаченную в обход теневого прогона.
Передача Передали монитор качества и сдвига данных, алерты по сегментам маршрутов, схему теневого прогона и регламент переобучения. Дежурной смене показали разбор алерта, команде модели — выкат версии через теневой контур.
Архитектура
Рядом с сервисом прогноза поставлен монитор: сервис пишет каждое предсказание с входными признаками и версией модели в ClickHouse, а DAG Airflow подтягивает фактическое время закрытия рейса и считает ошибку и сдвиг распределений на Evidently. Журнал прогнозов лежит в колоночном ClickHouse, потому что расчёт идёт агрегатами по нескольким полям на длинном интервале и в разрезе сегментов маршрутов — строчное хранилище читало бы запись прогноза целиком ради двух признаков. Пороги алертов заданы по сегментам, поэтому срабатывание приходит в Grafana с указанием сегмента и версии модели, а не общим числом по потоку. Новая версия из реестра MLflow сначала поднимается вторым экземпляром на теневом трафике: она получает те же запросы и пишет прогнозы отдельной веткой, сравнение идёт на одном наборе рейсов, а переключение выполняется сменой версии в конфигурации маршрутизации.
Адресный склад с отбором по заданиям на терминалах
Задача, решение и что передали
Задача
Товар лежал по памяти кладовщиков, и новый сотрудник искал паллету обходом рядов.
Решение
Склад разметили до ячейки, каждой ячейке задали габарит, весовой лимит и зону хранения. Приёмку, размещение, отбор и инвентаризацию перевели на терминалы: система выдаёт задание с адресом, сотрудник подтверждает ячейку и товар сканированием. Остаток ведётся в разрезе ячейки и партии, обмен с учётной системой идёт очередью сообщений.
Сдано 3
Передали топологию склада с адресами
Правила размещения и отбора
Инструкции кладовщика и сценарии инвентаризации по зонам
Как делали
Обследование склада Обошли склад с кладовщиками, зафиксировали фактические места хранения, габариты и весовые ограничения рядов. Разобрали приёмку паллет и путь нового сотрудника, который ищет товар обходом рядов.
Архитектура адресов Склад разметили до ячейки с габаритом, весовым лимитом и зоной хранения, а остаток решили вести в разрезе ячейки и партии. Обмен с учётной системой вынесли в очередь сообщений, чтобы отбор не останавливался при её недоступности, подтверждение операции оставили только по сканированию ячейки и товара.
Разработка терминалов Сначала топология склада и правила размещения и отбора, затем экраны терминала на приёмку, размещение, отбор и инвентаризацию, затем обмен с учётной системой. Обкатывали на выделенной зоне с реальными паллетами.
Проверка на отборе Гоняли волну отбора и инвентаризацию по зонам, намеренно переставляя паллеты между ячейками. Работу не приняли бы при подтверждении отбора без сканирования ячейки, при расхождении остатка по ячейке с фактом и при задании в ячейку, куда паллета не помещается по габариту.
Передача складу Передали топологию склада с адресами, правила размещения и отбора, инструкции кладовщика и сценарии инвентаризации по зонам. Кладовщик сам заводит ячейку и зону, начальник склада запускает инвентаризацию без обращения к разработчику.
Архитектура
Ядро склада на Axum держит топологию до ячейки с габаритом, весовым лимитом и зоной, а также остатки в разрезе ячейки и партии в PostgreSQL. Выдача задания отбора идёт транзакцией с SELECT … FOR UPDATE SKIP LOCKED по очереди заданий — так два отборщика не получают одну строку, и отдельный сервис-диспетчер с собственным состоянием не нужен. Терминалы подтверждают шаг сканированием адреса ячейки и SSCC паллеты по HTTP, текущее распределение заданий по сотрудникам держится в Redis. Обмен с учётной системой вынесен в RabbitMQ: движения по ячейкам возникают чаще, чем учётный контур готов их принимать, поэтому они буферизуются и уходят пачками.
Профилирование справочников адресов и контрагентов перед единой витриной
Задача, решение и что передали
Задача
Один и тот же склад значился в системах под разными написаниями, из-за чего сводный отчёт по плечам маршрута собирали руками.
Решение
Прогнали справочники через профилирование и нормализацию: привели адреса к единому формату, нашли дубли контрагентов по ИНН и нечёткому совпадению названия. Построили таблицу соответствий между кодами систем и правило выбора эталонной записи. Проверили, какая часть строк перевозок цепляется к эталону автоматически, а какая уходит на ручное разрешение.
Сдано 4
Отчёт профилирования справочников
Таблица соответствий кодов
Правила выбора эталона
Очередь ручного разрешения
Как делали
Профилирование справочников Выгрузили справочники складов и контрагентов из систем и прогнали профилирование: сколько написаний адреса приходится на одну точку и как один и тот же склад записан в разных системах. Разобрали с теми, кто собирает сводный отчёт по плечам маршрута, где именно ломается склейка.
Правило эталона Решили нормализовать адреса по адресному справочнику, дубли контрагентов искать по ИНН и нечёткому совпадению названия, а связь систем держать таблицей соответствий кодов с правилом выбора эталонной записи. Переписывать коды в системах-источниках не стали.
Сборка склейки Собрали нормализацию адресов, поиск дублей и таблицу соответствий, затем правило выбора эталона и очередь ручного разрешения для строк, не зацепившихся автоматически. На строках перевозок посмотрели, какая часть цепляется к эталону сама.
Сверка с диспетчерами Сверяли склеенные записи с тем, что подтверждают диспетчеры по конкретным складам и контрагентам, и пересобирали сводный отчёт по плечам маршрута из нормализованных справочников. Провалом считали склейку двух разных складов в одну эталонную запись: такой случай правило обязано было отправить в ручное разрешение, а не решать само.
Передача очереди Передали отчёт профилирования справочников, таблицу соответствий кодов, правила выбора эталона и очередь ручного разрешения. Показали, как разбирать очередь и как заводить новый склад, чтобы он сразу цеплялся к эталону.
Архитектура
Задания Spring Boot выгружают справочники систем в промежуточную схему PostgreSQL, адреса приводятся к единому виду обращением к справочнику ФИАС, реквизиты и названия чистятся тем же шагом. Дубли ищутся в два прохода: сначала блокировка кандидатов по ИНН и нормализованному адресу, потом нечёткое сравнение названий индексом Elasticsearch — попарное сравнение всех строк справочника не выполняется, кандидатов нужно сузить до сравнения. Таблица соответствий кодов систем и правило выбора эталонной записи хранятся отдельной сущностью и не переписывают коды в источниках: каждая система остаётся владельцем своего кода, а связь живёт в реестре и переигрывается при смене правила. Долю строк перевозок, которые цепляются к эталону автоматически, и отчёты профилирования считает ClickHouse, изменения справочников приходят через Kafka, оркестрация — Airflow, сервисы в Kubernetes.
Ассистент по документам судозахода в контуре порта
Задача, решение и что передали
Задача
Сменный диспетчер сверял требования к опасному грузу и обязательные постановления по порту по разрозненным файлам, а данные судозахода запрещено выносить во внешние сервисы.
Решение
Собрали ассистента внутри портового контура: модель и индекс живут на площадке, внешние вызовы закрыты правилами сегмента. В индекс легли обязательные постановления по порту, разделы МКМПОГ, технологические карты погрузки и грузовые планы; фрагмент хранит класс груза, номер причала и пункт документа. Ответ выдаётся с цитатой и ссылкой на пункт, запрос и подобранные фрагменты пишутся в журнал смены.
Сдано 4
Передали сервис в контуре порта
Индекс обязательных постановлений и разделов МКМПОГ
Журнал запросов смены
Набор проверочных вопросов диспетчеров
Как делали
Разбор смены Разобрали со сменными диспетчерами, по каким разрозненным файлам они сверяют требования к опасному грузу и обязательные постановления по порту. Со службой безопасности зафиксировали, что данные судозахода за пределы контура не выносятся.
Контур порта Разместили модель и индекс на площадке порта, а внешние вызовы закрыли правилами сегмента. Фрагмент решили хранить с классом груза, номером причала и пунктом документа, чтобы ответ приходил с цитатой и ссылкой на пункт, а запрос и подобранные фрагменты писались в журнал смены.
Сборка индекса Подняли сервис внутри портового контура, затем загрузили в индекс обязательные постановления по порту, разделы МКМПОГ, технологические карты погрузки и грузовые планы. Последними сделали журнал смены и порядок обновления индекса.
Вопросы диспетчеров Гоняли набор проверочных вопросов диспетчеров по классам груза и причалам, отдельно проверяли закрытость сегмента. Провалом считали ответ без цитаты и ссылки на пункт документа, ответ по недействующей редакции постановления и любую попытку сервиса обратиться наружу контура.
Передача сервиса Передали сервис в контуре порта, индекс обязательных постановлений и разделов МКМПОГ, журнал запросов смены, набор проверочных вопросов диспетчеров и инструкцию по обновлению индекса. Показали сменным диспетчерам, как проверять ответ по ссылке на пункт.
Архитектура
Модель и индекс стоят на площадке порта, внешние вызовы закрыты правилами сегмента; сервис на FastAPI принимает запрос диспетчера, собирает контекст и обращается к vLLM на том же узле. Поиск гибридный в OpenSearch: номер причала, класс груза и номер пункта ищутся точным совпадением, формулировки постановлений и разделов МКМПОГ — векторно эмбеддингами BGE-M3, и оба индекса живут в одном поисковом узле, чтобы под точные поля не заводить второе хранилище и второй цикл переиндексации. Фрагмент несёт класс груза, номер причала и пункт документа, поэтому ответ возвращается с цитатой и ссылкой на пункт. Запрос, подобранные фрагменты и выданный ответ пишутся в журнал смены в PostgreSQL, обновление индекса идёт задачей через RabbitMQ и не останавливает выдачу; доступ по ролям — Keycloak, поставка — Docker на узлах порта.
Подписной поиск по нормативной базе грузовых перевозок
Задача, решение и что передали
Задача
Товарные кассиры и логисты оператора выясняли применимое правило перевозки и порядок заполнения графы накладной звонками в соседние подразделения и по пересланным телеграммам.
Решение
Подключили подписной сервис к нормативной базе перевозок: правила перевозок грузов, тарифное руководство, номенклатура ЕТСНГ и внутренние телеграммы разобраны на фрагменты с номером пункта и датой ввода редакции. Ответ приходит с пунктом правил и подсказкой по конкретной графе накладной, для отправки на особых условиях поднимаются требования к креплению и размещению груза. Новая телеграмма подхватывается индексом без остановки сервиса, прежняя редакция помечается недействующей и в выдачу не идёт.
Сдано 4
Передали доступ к подписному сервису
Индекс правил и телеграмм с датами ввода
Регламент обновления редакций
Набор проверочных вопросов товарных кассиров
Как делали
Разбор обращений Посмотрели, как товарные кассиры и логисты выясняют применимое правило перевозки и порядок заполнения графы накладной: звонки в соседние подразделения и пересланные телеграммы. Собрали, какие документы у них в ходу и каким путём доходит новая телеграмма.
Схема редакций Решили разбирать правила перевозок грузов, тарифное руководство, номенклатуру ЕТСНГ и внутренние телеграммы на фрагменты с номером пункта и датой ввода редакции. Новую телеграмму договорились подхватывать индексом без остановки сервиса, а прежнюю редакцию помечать недействующей и в выдачу не пускать.
Подключение сервиса Подключили подписной сервис к нормативной базе перевозок, затем сделали разбор на фрагменты и связку ответа с конкретной графой накладной. Последним добавили подъём требований к креплению и размещению груза для отправки на особых условиях.
Вопросы кассиров Прогнали набор проверочных вопросов товарных кассиров и разобрали спорные ответы, отдельно грузили новую телеграмму и повторяли те же вопросы. Провалом считали ответ по помеченной недействующей редакции и ответ без номера пункта правил, по которому кассир не может проверить подсказку по графе.
Передача доступа Передали доступ к подписному сервису, индекс правил и телеграмм с датами ввода, регламент обновления редакций, набор проверочных вопросов и разбор спорных ответов. Показали, как вводить новую телеграмму и как выносить спорный ответ на разбор.
Архитектура
Сервис на Axum отдаёт подписчикам поиск по нормативной базе: фрагменты правил перевозок, тарифного руководства, номенклатуры и телеграмм лежат в PostgreSQL, векторы — расширением pgvector в той же базе, точный поиск по номерам пунктов и телеграмм — в OpenSearch. Векторы держатся рядом с реляционными полями намеренно: фильтр по дате ввода редакции и признаку действующей применяется тем же запросом, что и поиск, без второго обхода за метаданными в другое хранилище. Новая телеграмма индексируется задачей из NATS в отдельный сегмент и включается переключением алиаса, прежняя редакция помечается недействующей и отсекается фильтром — переиндексация поверх живого индекса ломала бы выдачу тем, кто работает в эту смену. Подписки и роли ведёт Keycloak, лимиты и повторяющиеся ответы кэшируются в Redis, сервис поставляется в Docker.
Агент разбора судовой почты и заведения судозахода
Задача, решение и что передали
Задача
Диспетчер переносил данные из писем судового агента — нотиса о готовности, грузового манифеста, коносаментов — в систему захода руками, а расхождение по количеству мест находилось уже на выгрузке.
Решение
Агент забирает письма из общего ящика службы, раскладывает вложения по типам — нотис о готовности, грузовой манифест, коносаменты, судовые роли — и распознаёт сканы. Из документов вытаскиваются наименование судна, номер рейса, порт отправления, номера коносаментов, наименование груза и количество мест; поля сверяются с расписанием подхода и заявленным причалом. Расхождение количества мест с тальманским счётом помечается и уходит в очередь диспетчера, а повторное письмо по тому же рейсу подклеивается к заведённому судозаходу.
Сдано 4
Сервис разбора почты
Справочник типов судовых документов
Правила сверки с расписанием подхода
Регламент разбора спорных писем
Как делали
Разбор ящика Разобрали общий ящик службы: какие письма приходят от судового агента и в каком виде приходят нотис о готовности, грузовой манифест, коносаменты и судовые роли. Посмотрели с диспетчером, какие поля он переносит в систему захода руками и когда всплывает расхождение по количеству мест.
Схема сверки Решили раскладывать вложения по типам судовых документов до извлечения полей, сверку вести с расписанием подхода и заявленным причалом, а расхождение количества мест с тальманским счётом не править автоматически, а отправлять в очередь диспетчера. Повторное письмо по тому же рейсу договорились подклеивать к заведённому судозаходу.
Сборка разбора Сделали забор писем из ящика и раскладку вложений по типам, затем распознавание сканов и извлечение наименования судна, номера рейса, порта отправления, номеров коносаментов, наименования груза и количества мест. Последними сделали сверку с расписанием подхода и очередь диспетчера.
Прогон почты Прогнали накопленную почту по прошедшим судозаходам и сличили заведённые данные с тем, что диспетчер завёл руками. Провалом считали заведение второго судозахода по повторному письму того же рейса и расхождение количества мест с тальманским счётом, прошедшее мимо очереди диспетчера.
Передача диспетчерам Передали сервис разбора почты, справочник типов судовых документов, правила сверки с расписанием подхода, регламент разбора спорных писем и инструкцию диспетчера. Показали смене, как разбирать очередь и как добавить новый тип вложения.
Архитектура
Коннектор на Spring Boot забирает письма из общего ящика службы по IMAP, кладёт вложения в MinIO и публикует задачу в RabbitMQ; воркеры раскладывают вложения по типам, сканы распознают через Tesseract, а манифест, пришедший сообщением EDIFACT, разбирают парсером без распознавания. Ключ обработки собирается из Message-ID письма и хеша вложения: ящик отдаёт одно и то же письмо при пересылке и досылке, и повторное письмо по рейсу должно подклеиться к заведённому судозаходу, а не создать второй. Очередь между приёмом почты и разбором стоит потому, что распознавание пачки сканов идёт долго, а забор писем не должен ждать её окончания и упираться в таймауты почтового сервера. Извлечённые поля и результат сверки с расписанием подхода и заявленным причалом лежат в PostgreSQL; расхождение количества мест с тальманским счётом не пишется в судозаход, а становится записью в очереди диспетчера со ссылкой на страницу вложения; воркеры разворачиваются в Kubernetes отдельно от коннектора.
Сверка комплекта документов перед подачей декларации
Задача, решение и что передали
Задача
Декларант вручную сличал инвойс, упаковочный лист, транспортную накладную и разрешительные документы, и нехватка сертификата или разница в весе всплывали уже в требовании инспектора.
Решение
Агент раскладывает присланный клиентом комплект по типам, распознаёт инвойс, упаковочный лист, транспортную накладную и сертификаты и вытаскивает номера, вес брутто и нетто, количество мест, стоимость и заявленные коды товара. Поля сводятся по товарным позициям: расхождение веса между упаковочным листом и накладной, отсутствие сертификата под заявленный код ТН ВЭД, просроченная доверенность и несовпадение отправителя помечаются до подачи. Лист расхождений собирается со ссылкой на страницу документа, а сошедшийся комплект уходит в подготовку декларации.
Сдано 4
Сервис разбора комплектов
Каталог типов внешнеторговых документов
Правила проверки соответствия коду ТН ВЭД
Лист расхождений по комплекту
Как делали
Разбор комплектов Разобрали с декларантами, как сличается комплект — инвойс, упаковочный лист, транспортная накладная, разрешительные документы — и из-за чего чаще приходит требование инспектора. Собрали присланные клиентами комплекты и посмотрели, в каком виде и какими файлами документы доходят.
Сведение по позициям Решили сводить извлечённые поля по товарным позициям, а не по документу целиком, и держать правила проверки соответствия заявленному коду ТН ВЭД отдельно от разбора. Лист расхождений договорились собирать со ссылкой на страницу документа, а сошедшийся комплект отправлять в подготовку декларации.
Сборка проверок Сделали раскладку комплекта по типам и распознавание инвойса, упаковочного листа, транспортной накладной и сертификатов, затем извлечение номеров, веса брутто и нетто, количества мест, стоимости и заявленных кодов товара. Последними собрали сведение по позициям и проверки веса, наличия сертификата под код, срока доверенности и совпадения отправителя.
Прогон деклараций Прогнали комплекты по поданным ранее декларациям, включая те, по которым приходило требование инспектора. Провалом считали комплект, ушедший в подготовку декларации с расхождением веса между упаковочным листом и накладной или без сертификата под заявленный код ТН ВЭД.
Передача декларантам Передали сервис разбора комплектов, каталог типов внешнеторговых документов, правила проверки соответствия коду ТН ВЭД, лист расхождений по комплекту и инструкцию декларанта. Показали, как читать лист расхождений по ссылкам на страницы и как добавить правило под новый разрешительный документ.
Архитектура
Комплект принимается сервисом на Ktor, файлы уходят в MinIO, задачи разбора — в Kafka; воркеры классифицируют документ, распознают его через Tesseract и вытаскивают номера, вес брутто и нетто, количество мест, стоимость и заявленные коды товара. Поля сводятся по товарным позициям в PostgreSQL, а сами проверки — расхождение веса между упаковочным листом и накладной, отсутствие сертификата под код ТН ВЭД, просроченная доверенность, несовпадение отправителя — хранятся данными в таблице правил, а не зашиты в код: требования к разрешительным документам и коды меняются чаще, чем выходит релиз сервиса. Каждая сработавшая проверка ссылается на документ и страницу, поэтому лист расхождений собирается из сохранённых записей, а не пересчитывается заново при каждом открытии. Redis держит состояние идущего разбора комплекта до подтверждения декларантом, сошедшийся комплект передаётся в подготовку декларации, поставка — Docker.
Распознавание контейнеров и пломб на воротах терминала
Задача, решение и что передали
Задача
Номер контейнера и номер пломбы тальман переписывал у ворот в бланк, а расхождение с заявкой на завоз всплывало уже на площадке.
Решение
Собрали портал на въезде и выезде: камеры снимают боковые стенки, торцы, крышу и зону запорного устройства, номер контейнера читается по стандарту ISO 6346 с проверкой контрольного разряда, отдельно распознаются номера тягача и полуприцепа. Детектор отмечает вмятину, порез тента и нарушенную пломбу, кадры привязываются к заявке на завоз и к номеру ячейки площадки. Неуверенное распознавание уходит тальману на подтверждение прямо на посту, до заезда машины под перегружатель.
Сдано 4
Передали модели распознавания номеров и детекции повреждений
Схему размещения камер портала
Протокол приёмочных замеров на потоке ворот
Регламент разбора спорных проездов
Как делали
Обход ворот Постояли у ворот с тальманом и посмотрели, как номер контейнера и номер пломбы переписываются в бланк и когда всплывает расхождение с заявкой на завоз. Разобрали геометрию проезда: где встаёт машина, что видно с какой стороны и как освещён пост.
Схема портала Решили ставить порталы на въезде и выезде с камерами на боковые стенки, торцы, крышу и зону запорного устройства, номер контейнера читать по стандарту ISO 6346 с проверкой контрольного разряда, а номера тягача и полуприцепа распознавать отдельно. Неуверенное распознавание договорились не додумывать, а отдавать тальману на подтверждение прямо на посту.
Сборка распознавания Собрали портал и размещение камер, затем распознавание номеров и детекцию вмятины, пореза тента и нарушенной пломбы. Последними сделали привязку кадров к заявке на завоз и к номеру ячейки площадки и вывод спорного проезда тальману.
Замеры на потоке Сняли приёмочные замеры на потоке ворот и сличили распознанные номера с бланками тальмана, отдельно гоняли ночные и дождливые проезды. Провалом считали контейнер, уехавший под перегружатель с неподтверждённым номером или пломбой, и номер, прошедший без проверки контрольного разряда.
Передача тальманам Передали модели распознавания номеров и детекции повреждений, схему размещения камер портала, протокол приёмочных замеров на потоке ворот и регламент разбора спорных проездов. Показали тальманам, как подтверждать спорный проезд на посту.
Архитектура
Портал на въезде и выезде снимает боковые стенки, торцы, крышу и зону запорного устройства; служба захвата на площадке собирает кадры проезда и передаёт их в инференс на GPU у ворот, а не в серверную — тальман должен получить результат до заезда машины под перегружатель. Номер контейнера читается по ISO 6346, и контрольный разряд проверяется прямо в распознавателе: неверная контрольная цифра отбрасывает результат до сверки с заявкой, поэтому на пост тальману попадают настоящие сомнения, а не арифметические ошибки чтения. Распознанные номера контейнера, тягача и полуприцепа и отметки о вмятине, порезе тента и нарушенной пломбе публикуются событиями в Kafka; заявка на завоз, номер ячейки площадки и результат сверки лежат в PostgreSQL, кадры проезда — в MinIO по идентификатору проезда. Модели обучены на PyTorch и исполняются в TensorRT, службы захвата и распознавания поставляются в Docker на узлах у ворот.
Контроль коммерческих неисправностей вагонов на пункте осмотра
Задача, решение и что передали
Задача
Приёмщик поездов осматривал состав с эстакады на ходу, и открытый люк полувагона или остаток груза находили уже на станции назначения.
Решение
На входе в приёмо-отправочный парк поставили габаритную рамку с камерами на крышу, боковины и ходовую часть, кадры проходящего состава сшиваются в развёртку вагона и привязываются к номеру, считанному с борта. Детектор отмечает открытый люк полувагона, незакреплённую дверь крытого, выход груза за габарит погрузки и остатки после выгрузки. Отметка приходит приёмщику на пост вместе с развёрткой и позицией вагона в составе, снятые кадры остаются в архиве по номеру вагона.
Сдано 4
Передали детектор неисправностей
Схему габаритной рамки и освещения
Архив развёрток по номеру вагона
Протокол контрольных прогонов на подобранных составах
Как делали
Обход парка Посмотрели, как приёмщик поездов осматривает состав с эстакады на ходу, и разобрали случаи, когда открытый люк полувагона или остаток груза находили уже на станции назначения. Выбрали место на входе в приёмо-отправочный парк и разобрали, что видно с каких ракурсов.
Схема рамки Поставили габаритную рамку с камерами на крышу, боковины и ходовую часть и решили сшивать кадры проходящего состава в развёртку вагона, привязывая её к номеру, считанному с борта. Отметку договорились отдавать приёмщику на пост вместе с развёрткой и позицией вагона в составе, а кадры хранить в архиве по номеру вагона.
Сборка развёртки Собрали рамку и освещение, затем сшивку кадров в развёртку и чтение номера с борта. Последним обучили детектор на открытый люк полувагона, незакреплённую дверь крытого, выход груза за габарит погрузки и остатки после выгрузки.
Контрольные прогоны Прогнали подобранные составы с заранее известными неисправностями и сличили отметки с осмотром приёмщика. Провалом считали пропущенный открытый люк полувагона на контрольном прогоне и отметку, которую нельзя было отнести к конкретному вагону: номер не считался или позиция в составе не совпала.
Передача приёмщикам Передали детектор неисправностей, схему габаритной рамки и освещения, архив развёрток по номеру вагона и протокол контрольных прогонов на подобранных составах. Показали приёмщикам, как читать развёртку на посту и как поднять архив по номеру вагона.
Архитектура
Габаритная рамка с камерами на крышу, боковины и ходовую часть снимает проходящий состав по GigE Vision, служба захвата пишет кадры в MinIO и ставит задачи в RabbitMQ. Очередь между захватом и инференсом стоит намеренно: состав идёт непрерывно и захват не может ждать конца распознавания, а очередь с ограниченной глубиной удерживает пик прохода, не роняя запись кадров. Кадры сшиваются в развёртку вагона и привязываются к номеру, считанному с борта; детектор на TensorRT отмечает открытый люк полувагона, незакреплённую дверь крытого, выход груза за габарит и остатки после выгрузки, отметки и позиция вагона в составе пишутся в PostgreSQL. Приёмщик получает отметку на пост вместе с развёрткой, архив развёрток остаётся в MinIO по номеру вагона; сервисы на FastAPI поставляются в Docker, обучение детектора — на PyTorch.
Время подачи трапа, заправки и загрузки багажа координатор диктовал по рации и вписывал в бланк, а задержку оборота разбирали по записям переговоров.
Решение
Камеры на телетрапе и мачтах стоянки снимают зону вокруг воздушного судна: подача трапа, установка колодок, подключение наземного питания, подъезд заправщика и водила, работа багажного транспортёра, посторонний предмет на перроне. Модель отмечает начало и конец каждого этапа и сшивает их в фактическую хронологию оборота рейса по номеру стоянки и бортовому номеру. Включали стоянками: сначала запись рядом с бланком координатора, затем передача событий в систему управления оборотом, ночная и заснеженная сцена вынесены в отдельный набор контроля качества.
Сдано 4
Передали модель детекции этапов
Схему покрытия стоянки камерами
Протокол сравнения с бланками координатора
Панель качества по времени суток и погоде
Как делали
Обследование перрона Прошли оборот рейса вместе с координатором: что диктуется по рации, что вписывается в бланк, где расходятся отметки о подаче трапа, заправке и загрузке багажа. Осмотрели стоянки с телетрапом и мачтами, разобрали, что различимо на кадре ночью и в снегопад.
Архитектура событий События привязали к номеру стоянки и бортовому номеру, чтобы хронология собиралась по обороту рейса, а не по камере. Детекцию этапов — подача трапа, колодки, наземное питание, заправщик, водило, багажный транспортёр, посторонний предмет на перроне — отделили от сшивки в хронологию, а ночную и заснеженную сцену вынесли в отдельный набор контроля качества.
Сборка детекции Разметку набирали по стоянкам, начиная с дневных сцен и добавляя ночь и осадки; сначала запись событий шла рядом с бланком координатора, без передачи дальше. Передачу в систему управления оборотом включали стоянками, а не всем перроном сразу.
Проверка по бланкам Собранную хронологию сверяли с бланками координатора по тем же рейсам, отдельно считая ночные и заснеженные сцены. Провалом считался пропуск начала или конца этапа — оборот с дырой в хронологии не годится для разбора задержки; стоянку не включали, если на ней путались подъезд заправщика и водила или посторонний предмет на перроне оставался незамеченным.
Передача службе Передали модель детекции этапов, схему покрытия стоянки камерами, протокол сравнения с бланками координатора и панель качества по времени суток и погоде. Служба подключает следующую стоянку по той же схеме и сама видит по панели, где сцена просела.
Архитектура
Потоки камер телетрапа и мачт разбирает служба декодирования по RTSP на узле с GPU, детекции считает TensorRT, каждый кадр даёт события с номером стоянки. События уходят в Kafka с ключом по стоянке — очередь взята вместо прямого вызова, потому что сборщик этапов держит состояние открытых интервалов и после перезапуска переигрывает поток с сохранённого смещения, а не теряет начало этапа. Сборщик сшивает интервалы в хронологию оборота и связывает её с рейсом и бортовым номером по расписанию стоянок; детекции и этапы лежат в ClickHouse, поскольку панель качества считает срезы по времени суток и погоде за длинные периоды, а кадры-подтверждения — в MinIO. Сервисы развёрнуты в Kubernetes по одному деплойменту на группу стоянок: отказ узла инференса гасит только свои камеры, приём событий и хронология по остальным стоянкам продолжаются.
Обмен портовой системы с межведомственным порталом и таможней
Задача, решение и что передали
Задача
Заявку на судозаход, генеральную декларацию и грузовой манифест набирали в портале ведомств заново, хотя те же данные уже лежали в портовой системе.
Решение
Подняли адаптер к межведомственному порталу: пакет по судозаходу собирается из записи о заходе и списка коносаментов, подписывается и уходит в очередь с ключом по номеру рейса. Ответ ведомства и отметки о досмотре возвращаются в ту же карточку захода, а тальманский счёт по выгрузке сверяется с манифестом построчно. Справочники причалов, кодов груза и типов упаковки ведутся в одном месте и раздаются обеим системам.
Сдано 4
Передали адаптер портала
Схемы сообщений
Очередь отправок
Журнал квитанций ведомства
Как делали
Обследование судозаходов Сели с агентом судозахода и тальманами: что набирается в портале ведомств заново — заявка на судозаход, генеральная декларация, грузовой манифест — и что из этого уже лежит в портовой системе. Сверили состав полей пакета со схемами портала и посмотрели, как ведутся справочники причалов, кодов груза и типов упаковки в обеих системах.
Архитектура адаптера Адаптер вынесли отдельной службой и поставили перед порталом очередь: пакет собирается из записи о заходе и списка коносаментов, подписывается и уходит с ключом по номеру рейса. Ответ ведомства и отметки о досмотре возвращаются в ту же карточку захода, а справочники решили вести в одном месте и раздавать обеим системам, а не сопоставлять при каждой отправке.
Сборка обмена Сначала сборка и подписание пакета, потом очередь отправок с журналом квитанций, потом возврат ответов в карточку захода и построчная сверка тальманского счёта с манифестом. На стенде гоняли пакеты по прошлым судозаходам и отрабатывали отказы портала по формату, чтобы отправка не терялась.
Проверка пакетов Прогнали заходы с полным комплектом документов и отдельно те, где ведомство возвращает отказ. Провалом считались повторный пакет по одному номеру рейса, заведённый как новый, и квитанция, не легшая в карточку захода; не приняли бы и сверку, при которой расхождение тальманского счёта с манифестом не показывается конкретной строкой.
Передача порту Передали адаптер портала, схемы сообщений, очередь отправок, журнал квитанций ведомства и регламент сверки тальманского счёта с манифестом. Диспетчер сам видит застрявшую отправку и повторяет её, справочники причалов и кодов груза ведёт порт.
Архитектура
Пакет по судозаходу собирает сервис-адаптер на chi: запись о заходе и список коносаментов читаются из портовой базы, сообщение раскладывается по XSD-схемам ведомства и подписывается отдельным шагом вызовом КриптоПро CSP. Отправка идёт не прямым SOAP-вызовом из транзакции, а через таблицу исходящих в PostgreSQL и очередь RabbitMQ: портал отвечает долго и уходит на регламентные работы, а карточка захода обязана закоммититься независимо от него. Ключом сообщения служит номер рейса с типом документа, повтор из очереди попадает на уникальный индекс и второй пакет не заводит; квитанции и отметки о досмотре возвращаются в ту же карточку, непривязанный ответ уходит в очередь разбора. Справочники причалов, кодов груза и типов упаковки ведёт один сервис и раздаёт обеим системам; контейнеры живут в Kubernetes, возраст очереди отправок снимает Prometheus.
Стек
Go · chi · PostgreSQL · RabbitMQ · Kubernetes · SOAP · XSD-схемы · КриптоПро CSP · Prometheus
Шлюз перевозочных документов между учётной системой и отраслевой платформой
Задача, решение и что передали
Задача
Заявку на подачу вагонов и накладную оформляли в кабинете отраслевой платформы, а номера вагонов и отметки о раскредитовании переносили в учётную систему руками.
Решение
Сделали шлюз: заявка формы ГУ-12 и накладная уходят на платформу сообщением с ключом идемпотентности по номеру заявки, поэтому повторная отправка после обрыва подхватывает прежний документ, а не заводит новый. Ответные события — номер вагона, приём груза к перевозке, раскредитование — возвращаются очередью и ложатся в ту же заявку. Справочник станций и кодов груза ЕТСНГ синхронизируется по расписанию и не даёт завести станцию с чужим кодом.
Сдано 4
Передали шлюз обмена
Карту соответствия справочника станций
Очередь с ключом идемпотентности
Панель контроля возраста сообщений
Как делали
Обследование заявок Разобрали нынешний порядок: заявка формы ГУ-12 и накладная оформляются в кабинете отраслевой платформы, а номера вагонов и отметки о раскредитовании переносятся в учётную систему руками. Сверили справочник станций и кодов груза ЕТСНГ в учётной системе с платформой и нашли позиции, заведённые с чужим кодом.
Архитектура шлюза Шлюз сделали отдельным контуром с очередью и ключом идемпотентности по номеру заявки: обрыв канала не должен оборачиваться второй заявкой на подачу вагонов. Ответные события платформы возвращаются очередью и ложатся в ту же заявку, а справочник станций и ЕТСНГ синхронизируется по расписанию и не даёт завести станцию с чужим кодом.
Сборка обмена Собрали отправку заявки ГУ-12 и накладной, затем приём событий — номер вагона, приём груза к перевозке, раскредитование, — затем синхронизацию справочника и панель возраста сообщений. На стенде рвали канал посреди отправки и повторяли её, проверяя, что подхватывается прежний документ.
Проверка на обрывах Гоняли отправки с разрывами связи и повторами, а также отказы платформы по составу заявки. Провалом считалась вторая заявка на платформе по одному номеру и отметка о раскредитовании, легшая не в свою заявку; не приняли бы и шлюз, где отказ платформы остаётся в логе и не виден тому, кто оформляет накладную.
Передача дежурному Передали шлюз обмена, карту соответствия справочника станций, очередь с ключом идемпотентности, панель контроля возраста сообщений и регламент разбора отказов платформы. Дежурный сам видит зависшее сообщение и повторяет отправку, новые станции заводятся через карту соответствия.
Архитектура
Шлюз на Axum принимает заявку ГУ-12 и накладную из учётной системы, пишет их в таблицу исходящих PostgreSQL вместе с ключом идемпотентности по номеру заявки и типу документа и отдаёт отправку воркеру. Уникальный индекс на этот ключ лежит в базе, а не в кэше: Redis держит только счётчик обращений к платформе, и потеря ключа при вытеснении недопустима — повтор после обрыва обязан подхватить прежний документ, а не завести новый. Ответные события платформы приходят в Kafka с партиционированием по номеру заявки, поэтому номер вагона, приём груза к перевозке и раскредитование ложатся в одну заявку в том порядке, в каком случились. Справочник станций и кодов ЕТСНГ синхронизируется отдельной задачей по расписанию, SOAP-обмен и XML-схемы платформы вынесены в свой модуль; сервисы развёрнуты в Kubernetes, после перезапуска потребитель продолжает с сохранённого смещения.
Шина обмена склада с учётными системами поклажедателей
Задача, решение и что передали
Задача
Каждый поклажедатель присылал заказы своим файлом и своей номенклатурой, а отчёт о движении товара по ответственному хранению собирали выгрузками в конце месяца.
Решение
Поставили шину: заказ клиента приводится к общему формату, номенклатура поклажедателя ложится на складскую через таблицу соответствия, а подтверждение отбора и отгрузки возвращается его системе тем же маршрутом. Каждое сообщение несёт код отправителя и номер документа, поэтому повторная передача после сбоя дубля отгрузки не создаёт. Отчёт о движении и остатках по хранению собирается из тех же событий, а не из отдельной выгрузки.
Сдано 4
Передали шину обмена
Таблицы соответствия номенклатуры поклажедателей
Журнал сообщений с ключами
Отчёт о движении по хранению
Как делали
Обследование поклажедателей Собрали форматы, которыми поклажедатели присылают заказы, и их номенклатуру, посмотрели, как отчёт о движении товара по ответственному хранению собирается отдельными выгрузками уже после закрытия периода. Увидели, что у каждого клиента своё именование позиций и своя нумерация документов.
Архитектура шины Поставили шину с общим внутренним форматом заказа, а номенклатуру поклажедателя кладём на складскую через таблицу соответствия — свою для каждого клиента. Каждое сообщение несёт код отправителя и номер документа, а отчёт о движении и остатках решили собирать из тех же событий, а не из отдельной выгрузки.
Сборка адаптеров Сначала приведение заказа к общему формату и таблицы соответствия, потом возврат подтверждений отбора и отгрузки тем же маршрутом, потом сборка отчёта о движении из событий и панель очередей. На стенде подключали по одному поклажедателю и повторяли передачу после сбоя.
Проверка повторов Прогнали заказы всех подключённых клиентов, в том числе с повторной передачей после сбоя. Провалом считались вторая отгрузка по одному номеру документа отправителя и позиция, легшая на чужую складскую номенклатуру; не приняли бы и отчёт о движении, не сходящийся с журналом событий за тот же период.
Передача службе Передали шину обмена, таблицы соответствия номенклатуры поклажедателей, журнал сообщений с ключами, отчёт о движении по хранению и панель мониторинга очередей. Служба подключает нового поклажедателя таблицей соответствия и сама разбирает застрявшие сообщения по журналу.
Архитектура
Шина собрана на Apache Camel внутри Spring Boot: у каждого поклажедателя свой маршрут — забор файла по SFTP, приём REST или разбор его формата, — и все они приводят заказ к общей канонической модели через таблицу соответствия номенклатуры. Канонические сообщения кладутся в Kafka с ключом из кода отправителя и номера документа, партиционирование по отправителю сохраняет порядок его документов; журнал сообщений и таблицы соответствия лежат в PostgreSQL, исходные файлы — в MinIO для разбора спорных случаев. Дедупликация сделана уникальным индексом по тому же ключу на приёме, поэтому повторная передача после сбоя вторую отгрузку не создаёт, а подтверждение отбора и отгрузки возвращается тем же маршрутом в систему поклажедателя. Отчёт о движении и остатках по хранению строится из этого же журнала событий, а не отдельной выгрузкой; маршруты развёрнуты в Kubernetes, при падении инстанса группа потребителей перераспределяет партиции, возраст очередей виден в Grafana.
Учёт грузовых операций судозахода с тальманскими счётами
Задача, решение и что передали
Задача
Тальманские листы сдавали в контору стопкой после смены, и сходимость счёта с коносаментом выясняли к моменту отхода судна.
Решение
Судозаход заводится с грузовым планом: коносаментные партии, люки, места хранения на терминале. Тальман отмечает счёт по подъёмам с планшета на причале, счёт складывается по люку и коносаменту по ходу выгрузки, груз ставится на штабель с адресом площадки и номером партии. Расхождение с манифестом поднимает акт-извещение, а сменно-суточный план видит остаток по каждому люку.
Сдано 4
Передали грузовой план судозахода
Тальманский счёт по люкам
Акт-извещение о расхождении
Реестр штабелей терминала
Как делали
Обследование причала Посмотрели, как тальманские листы сдаются в контору стопкой после смены и как сходимость счёта с коносаментом выясняется к отходу судна. Разобрали грузовой план: коносаментные партии, люки, места хранения на терминале, и как ведётся сменно-суточный план.
Архитектура счёта Счёт решили вести с планшета на причале по подъёмам, складывая его по люку и коносаменту по ходу выгрузки, а не после смены. Груз ставится на штабель с адресом площадки и номером партии, а расхождение с манифестом поднимает акт-извещение сразу — позже разбирать нечем, судно уходит.
Сборка планшета Сначала заведение судозахода с грузовым планом и коносаментными партиями, потом планшет тальмана со счётом по подъёмам, потом постановка на штабель, акт-извещение и остаток по люку в сменно-суточном плане. На стенде отрабатывали потерю связи на причале и отмену ошибочного подъёма.
Проверка с манифестом Прогнали выгрузку по судозаходам с несколькими люками и сверили сложившийся счёт с манифестом и коносаментами. Провалом считались подъём, потерянный при обрыве связи на причале, и расхождение с манифестом, не поднявшее акт-извещение; не приняли бы и штабель без номера партии.
Передача тальманам Передали грузовой план судозахода, тальманский счёт по люкам, акт-извещение о расхождении, реестр штабелей терминала и инструкцию тальмана. Тальманы работают с планшета, стивидор видит остаток по каждому люку в сменно-суточном плане, стопка листов в контору больше не сдаётся.
Архитектура
Планшет тальмана пишет подъёмы в локальную SQLite и синхронизирует их через RabbitMQ, у каждой записи свой клиентский идентификатор, поэтому повтор после потери связи на причале счёт не задваивает. Подъёмы ложатся в PostgreSQL строками дозаписи, а не увеличением счётчика в карточке люка: на одном люке работают несколько тальманов, и обновление общего поля затирало бы чужие записи, поэтому итог по люку и коносаменту считается агрегатом. Сервис на ASP.NET Core рассылает текущий счёт на экран сменно-суточного плана через SignalR, грузовой план судозахода и реестр штабелей терминала ведутся в той же базе. Манифест и коносаментные партии принимаются сообщениями EDIFACT, расхождение с тальманским счётом поднимает акт-извещение; сервисы поднимаются Docker Compose, отставание синхронизации планшетов видно в Grafana.
Дефектную ведомость на вагон писали от руки, а расход запчастей и трудоёмкость сводили в конце месяца, когда вагон уже ушёл с путей депо.
Решение
Вагон заводится в ремонт по своему номеру с историей прежних заездов: пробег от последнего деповского, отказы, замены узлов. Осмотрщик составляет дефектную ведомость на планшете по позициям справочника, из неё собирается наряд бригаде с нормо-часами и лимитная карта на запчасти. Замена колёсной пары и буксового узла пишется с номерами снятой и поставленной детали, поэтому история узла ведётся отдельно от истории вагона.
Сдано 4
Передали дефектную ведомость
Наряд бригаде с нормо-часами
Лимитную карту на запчасти
Паспорт колёсной пары
Как делали
Обследование депо Прошли по путям депо с осмотрщиком и мастером: как дефектная ведомость пишется от руки и как расход запчастей и трудоёмкость сводятся уже после того, как вагон ушёл с путей. Собрали, что известно о вагоне на заезде — пробег от последнего деповского, отказы, замены узлов — и где это лежит.
Архитектура ведомости Вагон заводится в ремонт по своему номеру с историей прежних заездов, а дефектная ведомость собирается по позициям справочника, а не текстом, — из неё выводятся наряд бригаде с нормо-часами и лимитная карта на запчасти. Историю колёсной пары и буксового узла решили вести отдельно от истории вагона, с номерами снятой и поставленной детали, потому что узел переезжает с вагона на вагон.
Сборка нарядов Сначала справочник позиций и дефектная ведомость на планшете осмотрщика, потом наряд с нормо-часами и лимитная карта, потом паспорт колёсной пары с номерами деталей и печать бирок на узлы. На стенде провели заезд от осмотра до выпуска, включая замену узла и добавление позиции в ведомость по ходу ремонта.
Проверка заездов Прогнали закрытые заезды по бумажным ведомостям и сверили расход запчастей и нормо-часы с нарядами. Провалом считалась замена колёсной пары, записанная без номеров снятой и поставленной детали, — история узла тогда рвётся; не приняли бы и выпуск вагона с открытыми позициями дефектной ведомости.
Передача осмотрщикам Передали дефектную ведомость, наряд бригаде с нормо-часами, лимитную карту на запчасти, паспорт колёсной пары и реестр заездов вагона. Осмотрщик ведёт ведомость с планшета, мастер закрывает наряд, история узла поднимается по номеру детали без похода в архив.
Архитектура
Осмотрщик ведёт дефектную ведомость на планшете против локальной SQLite и синхронизирует её через RabbitMQ, поэтому пути депо без связи работают, а повтор синхронизации отбрасывается по ключу заезда и позиции. История узла ведётся сущностью, отдельной от истории вагона: колёсная пара и буксовый узел переезжают между вагонами, поэтому записи хранятся по номеру детали, а связь с вагоном — строкой с периодом установки, иначе после замены наработка узла терялась бы вместе с прежним вагоном. Наряд бригаде с нормо-часами и лимитная карта на запчасти собираются из позиций ведомости по справочнику работ, всё это лежит в PostgreSQL вместе с реестром заездов. Бирки на снятые и поставленные узлы печатаются в ZPL кодом Code 128 с номером детали; сервисы Spring Boot поднимаются Docker Compose, отставание синхронизации планшетов видно в Grafana.
Учёт наработки и карт-нарядов на техобслуживание воздушных судов
Задача, решение и что передали
Задача
Наработку компонентов в часах и посадках вели в таблицах инженеров, а приближение формы обслуживания замечали по напоминанию, а не по данным.
Решение
Наработка борта в лётных часах и циклах приходит из рейсовых данных и раскладывается на установленные компоненты с назначенным ресурсом. По достижении порога формируется карта-наряд с перечнем работ, требуемыми допусками исполнителей и требованием на компоненты со склада. Отложенный дефект ведётся отдельной записью со сроком устранения и ограничением по эксплуатации, а списание компонента идёт с серийным номером на бортовой номер.
Сдано 4
Передали формуляр наработки по компонентам
Карту-наряд на форму обслуживания
Реестр отложенных дефектов
Требование на компоненты
Как делали
Обследование парка Разобрали таблицы инженеров с наработкой в лётных часах и посадках и то, как приближение формы обслуживания замечается по напоминанию, а не по данным. Посмотрели, что приходит из рейсовых данных, как ведётся перечень установленных компонентов с назначенным ресурсом и как оформляются отложенные дефекты.
Архитектура ресурсов Наработку борта решили раскладывать на установленные компоненты, чтобы порог считался по компоненту, а не по борту целиком, а списание шло с серийным номером на бортовой номер. Карта-наряд формируется по достижении порога с перечнем работ, требуемыми допусками исполнителей и требованием на компоненты со склада, а отложенный дефект вынесли отдельной записью со сроком устранения и ограничением по эксплуатации.
Сборка карт-нарядов Сначала приём рейсовых данных и формуляр наработки по компонентам, потом формирование карты-наряда и требования на склад, потом допуски исполнителей и реестр отложенных дефектов на планшетах техников. На стенде прогнали налёт по бортам, замену компонента и перенос отложенного дефекта на ближайшую форму.
Проверка формуляра Наработку по компонентам сверили с таблицами инженеров и с рейсовыми данными и прогнали формирование карт-нарядов по достигнутым порогам. Провалом считались компонент, наработка которого не сошлась с налётом борта, и карта-наряд, назначенная исполнителю без нужного допуска; не приняли бы и отложенный дефект без срока устранения и ограничения по эксплуатации.
Передача инженерам Передали формуляр наработки по компонентам, карту-наряд на форму обслуживания, реестр отложенных дефектов, требование на компоненты и регламент допусков исполнителей. Инженер ведёт установленные компоненты и назначенные ресурсы сам, техники закрывают работы с планшета, склад видит требование по карте-наряду.
Архитектура
Рейсовые данные о лётных часах и циклах приходят сообщениями RabbitMQ в сервис на NestJS, распределитель раскладывает наработку на установленные компоненты по периодам установки. Счётчики ведутся по серийному номеру компонента, а не по бортовому номеру: компонент переставляется между бортами, и при учёте на борт наработка снятого узла терялась бы, поэтому связь борта и компонента хранится строкой с интервалом установки в PostgreSQL. Наблюдатель порогов формирует карту-наряд с перечнем работ и требованием на компоненты, которое уходит на склад сообщением ATA Spec 2000; допуски исполнителей приходят группами Keycloak, и карта не закрывается техником без нужного допуска. Отложенный дефект ведётся отдельной записью со сроком устранения и ограничением по эксплуатации, интерфейс инженера собран на React, Redis кэширует формуляры наработки, сервисы развёрнуты в Kubernetes.
Кабинет экспедитора с заявками на вывоз контейнеров
Задача, решение и что передали
Задача
О выгрузке контейнера с судозахода и готовности его к вывозу экспедитор узнавал звонком в диспетчерскую, а поручение на вывоз возил на терминал бумагой.
Решение
Кабинет тянет из терминальной системы события по контейнеру: выгрузка с судозахода, размещение на площадке, досмотр, выдача на автотранспорт. Поручение на вывоз оформляется в кабинете и уходит на терминал заявкой с номерами контейнера и коносамента, а въезд машины бронируется тайм-слотом в календаре ворот. Сверхнормативное хранение считается по суткам нахождения на площадке и раскрывается построчно рядом с заявкой.
Сдано 4
Кабинет экспедитора
Обмен с терминальной системой по событиям контейнера
Календарь тайм-слотов ворот
Расчёт сверхнормативного хранения
Как делали
Обследование вывоза Сели с диспетчерской и экспедиторами: о чём спрашивают по телефону, когда контейнер выгружен и готов к вывозу, и как поручение на вывоз возится на терминал бумагой. Разобрали в терминальной системе события по контейнеру — выгрузка с судозахода, размещение на площадке, досмотр, выдача на автотранспорт — и порядок расчёта сверхнормативного хранения.
Архитектура кабинета Кабинет решили не делать вторым учётом: события тянутся из терминальной системы, а поручение уходит туда заявкой с номерами контейнера и коносамента. Тайм-слот вынесли в календарь ворот, чтобы въезд машины бронировался заранее, а сверхнормативное хранение считается по суткам нахождения на площадке и раскрывается построчно рядом с заявкой.
Сборка обмена Сначала подключение к событиям контейнера и карточка контейнера в кабинете, потом оформление поручения на вывоз и его приём терминальной системой, потом календарь тайм-слотов ворот и расчёт хранения. На стенде прогоняли контейнер от выгрузки до выдачи на автотранспорт, в том числе с досмотром и переносом слота.
Проверка событий Ленту событий в кабинете сверили с терминальной системой по одним и тем же контейнерам и пересчитали хранение по закрытым вывозам. Провалом считались контейнер, показанный готовым к вывозу до выдачи по терминальной системе, и заявка, принятая на занятый тайм-слот; не приняли бы и строку хранения, которую нельзя развернуть по суткам.
Передача диспетчерской Передали кабинет экспедитора, обмен с терминальной системой по событиям контейнера, календарь тайм-слотов ворот, расчёт сверхнормативного хранения и регламент подключения клиентов. Диспетчерская подключает нового экспедитора по регламенту и ведёт календарь ворот сама.
Архитектура
События по контейнеру — выгрузка с судозахода, размещение, досмотр, выдача — приходят из терминальной системы в Kafka, потребитель на Spring Boot проецирует их в таблицу-ленту по контейнеру в PostgreSQL. Читающая проекция сделана отдельно от терминальной системы потому, что интерактивные запросы экспедиторов нельзя пускать в оперативный контур терминала; лента строится по ключу контейнера и коносамента и переигрывается со смещения после сбоя потребителя. Тайм-слот ворот бронируется в транзакции с уникальным ограничением на ворота и интервал, поэтому две заявки на один слот не проходят. Сверхнормативное хранение считается по суткам нахождения из той же ленты, поручение на вывоз уходит на терминал заявкой с номерами контейнера и коносамента; фронт на React, доступ клиентских организаций разграничен Keycloak, Redis кэширует статусы, сервисы живут в Kubernetes.
Витрина отчётности поклажедателя по хранению и грузообработке
Задача, решение и что передали
Задача
Счёт за хранение и обработку приходил поклажедателю сводной таблицей без расшифровки, и расхождение разбирали перепиской с бухгалтерией склада.
Решение
Витрина считает хранение по паллето-суткам, а грузообработку — по приёмкам, отборам, стикеровке и отгрузкам, выгружаемым из системы управления складом. Каждая строка счёта разворачивается до складского задания с адресом ячейки и исполнителем, а остатки по партиям и срокам годности показываются на выбранную дату. Расхождения инвентаризации вынесены отдельным разделом со ссылкой на сличительную ведомость.
Сдано 4
Витрина отчётности поклажедателя
Расчёт хранения по паллето-суткам
Расшифровка строки счёта до складского задания
Раздел расхождений инвентаризации
Как делали
Обследование счёта Разобрали сводный счёт за хранение и обработку и переписку с бухгалтерией склада по расхождениям. Собрали из системы управления складом, чем считается хранение и какие операции идут в грузообработку — приёмки, отборы, стикеровка, отгрузки, — и где лежат складские задания с адресом ячейки и исполнителем.
Архитектура витрины Витрину собрали так, чтобы строка счёта разворачивалась до складского задания: хранение считается по паллето-суткам, грузообработка — по операциям, а не по итогу периода. Остатки по партиям и срокам годности показываем на выбранную дату, а расхождения инвентаризации вынесли отдельным разделом со ссылкой на сличительную ведомость.
Сборка расчёта Сначала загрузка событий и заданий из системы управления складом, потом расчёт паллето-суток и операций грузообработки, потом раскрытие строки счёта до задания и раздел инвентаризации. На стенде пересчитывали закрытые периоды и проверяли, что перезагрузка данных не трогает уже закрытый счёт.
Проверка пересчётом Пересчитали закрытые периоды по нескольким поклажедателям и сверили итог витрины со счётом бухгалтерии склада. Провалом считались строка счёта, которую нельзя развернуть до складского задания, и остаток на дату, разошедшийся с системой управления складом; не приняли бы и пересчёт, меняющий уже закрытый период задним числом.
Передача бухгалтерии Передали витрину отчётности поклажедателя, расчёт хранения по паллето-суткам, расшифровку строки счёта до складского задания, раздел расхождений инвентаризации и регламент закрытия периода. Бухгалтерия склада закрывает период по регламенту, поклажедатель разбирает свой счёт сам, не поднимая переписку.
Архитектура
События системы управления складом — приёмки, отборы, стикеровка, отгрузки, размещения — идут в Kafka, загрузчик кладёт их в ClickHouse. Колоночное хранилище выбрано потому, что строка счёта — это агрегат по множеству складских заданий за период, а расшифровка до задания с адресом ячейки и исполнителем остаётся фильтром по поклажедателю и дате в том же наборе; строчная база потребовала бы отдельной витрины под каждую форму счёта. Паллето-сутки считает даг Airflow: суточный срез остатков по партиям и срокам годности пишется идемпотентно ключом поклажедателя и даты, поэтому пересчёт закрытого периода начисление не задваивает. API на Actix отдаёт витрину фронту на React и режет выборку по поклажедателю из клейма Keycloak, справочники, состояние закрытия периода и ссылки на сличительные ведомости лежат в PostgreSQL; сервисы развёрнуты в Kubernetes, при перезапуске загрузчик продолжает со смещения.
Перенос базы контейнерного терминала с Oracle на PostgreSQL
Задача, решение и что передали
Задача
Судозаходы, тальманские счета и размещение контейнеров по ярусам вёл терминальный комплекс на Oracle, поддержка которого для предприятия закрылась.
Решение
Схема и данные перенесены с сохранением истории судозаходов, пакеты PL/SQL переписаны на PL/pgSQL, тяжёлые выборки по площадке разложены по партициям судозахода. Обмен с весовой и системой пропусков переведён на новый контур, кластер поднят на Linux с автоматическим переключением реплики. Смена отработана двойным счётом: тальманские записи и коносаментные партии сверены с прежней базой.
Сдано 4
Схема и данные в новой СУБД
Переписанные процедуры
Протокол построчной сверки
Процедура возврата на прежний контур
Как делали
Обследование схемы Сняли инвентарь схемы терминального комплекса — судозаходы, тальманские счета, размещение контейнеров по ярусам — и разобрали пакеты PL/SQL, считающие площадку. Отдельно выписали внешние связи: обмен с весовой и системой пропусков, и нашли тяжёлые выборки, упиравшиеся в объём истории судозаходов.
Архитектура переноса Схему и данные переносим с сохранением истории судозаходов, пакеты PL/SQL переписываем на PL/pgSQL, тяжёлые выборки по площадке раскладываем по партициям судозахода. Кластер поднимаем на Linux с автоматическим переключением реплики, обмен с весовой и пропусками переводим на новый контур, а процедуру возврата на прежний контур заложили сразу — смена не должна остаться без терминальной системы.
Сборка контура Сначала перенос схемы и данных, потом переписывание процедур с прогоном на копии, потом партиционирование выборок по площадке, потом кластер и внешние обмены. На стенде поднимали копию боевой базы и отрабатывали смену целиком, прежде чем трогать рабочий контур.
Проверка двойным счётом Смену отработали двойным счётом: тальманские записи и коносаментные партии сверялись с прежней базой построчно. Провалом считались любая несошедшаяся строка сверки и переписанная процедура, дающая на новом контуре иной результат, чем прежняя; при несходимости переключение не проводили и оставались на прежнем контуре.
Передача администраторам Передали схему и данные в новой СУБД, переписанные процедуры, протокол построчной сверки и процедуру возврата на прежний контур. Администраторы держат кластер и переключение реплики сами, разработчики терминала правят логику уже в PL/pgSQL.
Архитектура
Перенос ведёт раннер на Celery: задачи по таблицам и партициям делают начальную заливку и догоняют изменения, схема и процедуры сконвертированы ora2pg, а пакеты переписаны на PL/pgSQL и прогнаны на тех же входах. Тяжёлые выборки по площадке разложены партициями по судозаходу — запросы терминала всегда несут номер захода, и отсечение партиций заменяет обход общего индекса по всей истории. Кластер поднят Ansible на Astra Linux под управлением Patroni: синхронная реплика держит состояние, переключение выполняется без правки строк подключения в приложении, отчётные выборки уходят на реплику и не мешают операционным. Сверка идёт двойным счётом — тальманские записи и коносаментные партии считаются на обеих базах и сравниваются построчно контрольными суммами по партициям; обмен с весовой и системой пропусков переключён на новый контур маршрутизацией и тем же путём возвращается назад.
Перенос учёта вагонного парка и подъездных путей на PostgreSQL
Задача, решение и что передали
Задача
Ведомости подачи и уборки вагонов, простои под грузовыми операциями и плата за пользование считались в базе на зарубежной СУБД под Windows Server.
Решение
Схема перенесена вместе с историей операций по номерам вагонов, расчёт простоя и платы за пользование переписан на PL/pgSQL, посуточные таблицы разложены по партициям. Приложение переведено на Linux, обращения к базе идут через пул соединений, резервное копирование снимается уже на новом контуре. Ведомости за период посчитаны параллельно на обеих базах и сверены построчно.
Сдано 4
Перенесённая схема
Переписанный расчёт простоя
Протокол параллельного счёта ведомостей
План переключения и возврата
Как делали
Обследование расчёта Разобрали, что считает база под Windows Server: ведомости подачи и уборки вагонов, простои под грузовыми операциями, плату за пользование, — и как устроены посуточные таблицы по номерам вагонов. Выписали обращения приложения к базе и места, где расчёт зашит в процедуры.
Архитектура переноса Схему переносим вместе с историей операций по номерам вагонов, расчёт простоя и платы за пользование переписываем на PL/pgSQL, посуточные таблицы раскладываем по партициям. Приложение переводим на Linux, обращения к базе пускаем через пул соединений, резервное копирование ставим уже на новом контуре, а план переключения пишем вместе с планом возврата.
Сборка контура Сначала перенос схемы и истории операций, потом переписывание расчёта простоя и платы за пользование, потом партиции посуточных таблиц, пул соединений и резервное копирование. На стенде подняли копию базы и считали ведомости за прошлые периоды параллельно с прежней системой.
Проверка параллельным счётом Ведомости за период посчитали параллельно на обеих базах и сверили построчно по номерам вагонов. Провалом считалось расхождение по простою или плате за пользование хотя бы по одному вагону, а также ведомость, собранная новым расчётом в другом составе строк; при несходимости переключение не проводили.
Передача службе Передали перенесённую схему, переписанный расчёт простоя, протокол параллельного счёта ведомостей и план переключения и возврата. Служба ведёт резервное копирование на новом контуре сама, расчёт правится в PL/pgSQL без обращения к прежней СУБД.
Архитектура
Серверная часть на Vapor перенесена на РЕД ОС, обращения к PostgreSQL идут через pgBouncer в режиме транзакционного пула: сменные терминалы держат много коротких соединений, и без пула каждое занимало бы отдельный процесс базы. Расчёт простоя под грузовыми операциями и платы за пользование переписан на PL/pgSQL и живёт рядом с данными, посуточные таблицы разложены партициями по дате, поэтому ведомость за период читает свои партиции, а не всю историю операций по номерам вагонов. Внутренние вызовы между службами учёта парка и расчёта идут по gRPC, схема и конфигурация узлов раскатываются Ansible. Резервное копирование снимается pg_probackup с архивом журналов уже на новом контуре, ведомости за период посчитаны параллельно на обеих базах и сверены построчно, прежний контур остаётся поднятым до окончания сверки и служит точкой возврата.
Стек
Swift · Vapor · PostgreSQL · pgBouncer · gRPC · Ansible · PL/pgSQL · РЕД ОС · pg_probackup
Портирование рабочего места декларанта под доверенную ОС
Задача, решение и что передали
Задача
Декларации на товары готовились в клиенте под зарубежной ОС, а криптоподпись и обмен с таможенным органом держались на компонентах только для неё.
Решение
Клиент декларанта пересобран под доверенные ОС, работа с криптопровайдером и токеном подписи переведена на сборки для Linux. Заполнение граф декларации, подстановка кодов ТН ВЭД из справочника и обмен с таможенным органом проверены на тестовом стенде, включая приём ответных сообщений и разрешительных документов. Установка и обновление отданы штатному пакетному менеджеру, комплект проверок повторяется перед каждым выпуском.
Сдано 4
Пакеты клиента
Настройки криптопровайдера и токена
Протокол обмена на тестовом стенде
Инструкция декларанту
Как делали
Разбор декларации Разобрали с декларантами путь декларации на товары — заполнение граф, подстановка кода ТН ВЭД из справочника, подписание и отправка в таможенный орган, приём ответных сообщений. Выписали, какие компоненты криптоподписи и работы с токеном существуют только для прежней ОС.
Схема подписи Криптопровайдер и работу с токеном перевели на сборки для Linux через штатный интерфейс к токену, а не на собственную обвязку. Обмен с таможенным органом оставили в прежнем формате сообщений, чтобы не переделывать сторону инспектора.
Пересборка места Пересобрали клиент декларанта, подключили криптопровайдер и токен, перенесли формы граф декларации и справочник ТН ВЭД. Установку и обновление отдали штатному пакетному менеджеру, комплект проверок повесили на выпуск сборки.
Обмен на стенде На тестовом стенде подали декларацию целиком — подпись токеном, отправка, приём ответных сообщений и разрешительных документов. Неподтверждённая подпись, отклонённое таможенным органом сообщение или непринятый ответный документ считались провалом, и такая сборка декларантам не выдавалась.
Передача декларантам Отдали пакеты клиента, настройки криптопровайдера и токена, протокол обмена на тестовом стенде и инструкцию декларанту; на рабочих местах прошли выпуск декларации от заполнения граф до подписи. Администратор брокера сам ставит обновление и подключает новый токен.
Архитектура
Рабочее место декларанта собрано на PyQt под доверенную ОС, а работа с криптопровайдером вынесена в отдельный процесс-сервис: обращение к токену идёт по PKCS#11 к сборкам КриптоПро CSP для Linux. Подписание отделено от интерфейса потому, что вызовы к токену блокирующие и держали бы окно заполнения граф на всё время обращения к устройству. Справочник кодов ТН ВЭД и черновики деклараций лежат в локальной SQLite и обновляются отдельной задачей из серверного PostgreSQL, поэтому графы заполняются и при обрыве канала. Обмен с таможенным органом идёт XML-сообщениями по SOAP через дисковую очередь отправки: сообщение помечается доставленным только по квитанции, повтор уходит с тем же идентификатором, приём ответных сообщений и разрешительных документов проверен на тестовом стенде, пакеты deb и комплект проверок собираются в GitLab CI.
Стек
Python · PyQt · SQLite · PostgreSQL · SOAP · deb · КриптоПро CSP · PKCS#11 · GitLab CI
Разграничение доступа ассистента к документам судозахода
Задача, решение и что передали
Задача
Ассистент по документам судозаходов отвечал всем одинаково, и агент чужой линии мог получить фрагмент каргоплана или тальманской расписки.
Решение
Каждому фрагменту индекса проставили номер судозахода, линию и роль читателя: тальман, стивидор, агент, диспетчер смены. Запрос сначала превращается в множество разрешённых судозаходов по данным системы учёта заходов и только потом уходит в векторный поиск, поэтому ранжирование видит лишь допущенные фрагменты. Отказ пишется отдельной записью с ролью, номером судозахода и текстом запроса.
Сдано 4
Передали матрицу ролей и судозаходов
Схему атрибутов фрагмента
Журнал отказов
Протокол проверки на чужих судозаходах
Как делали
Разбор индекса Посмотрели, что лежит в индексе — каргопланы, тальманские расписки, грузовые документы судозаходов — и кто обращается к ассистенту: тальман, стивидор, агент линии, диспетчер смены. На живых запросах убедились, что агент одной линии дотягивается до документов чужого судозахода.
Атрибуты фрагмента Решили не фильтровать выдачу после поиска, а сужать её до него: запрос сначала превращается в множество разрешённых судозаходов по данным системы учёта заходов и только потом уходит в векторный поиск. Каждому фрагменту проставили номер судозахода, линию и роль читателя, чтобы права жили в атрибутах, а не в тексте промпта.
Переиндексация и связка Переиндексировали архив с проставлением атрибутов, собрали связку с системой учёта заходов и ролевой моделью, встроили сужение множества судозаходов перед обращением к индексу. Отказ сделали отдельной записью с ролью, номером судозахода и текстом запроса.
Чужие судозаходы Гоняли запросы от имени агента одной линии по судозаходам другой и от тальмана по документам, закреплённым за стивидором, глядя и на ответ, и на то, что попало в ранжирование. Провалом считали любой случай, когда фрагмент чужого судозахода попал в выдачу, даже если в ответе он не процитирован.
Передача диспетчерам Отдали матрицу ролей и судозаходов, схему атрибутов фрагмента, журнал отказов и протокол проверки на чужих судозаходах; с диспетчерами смены разобрали чтение журнала. Администратор порта сам заводит роль и правит привязку линии к судозаходу.
Архитектура
Перед векторным поиском стоит сервис допусков на Ktor: он берёт роль и линию из токена Keycloak, обращается по gRPC к системе учёта заходов и собирает множество номеров судозаходов, открытых читателю. Это множество подставляется фильтром в запрос к OpenSearch до ранжирования, а не отсекает выдачу после него — при отсеве после ранжирования допущенные фрагменты вытесняются чужими и ответ собирается из обрывков. Атрибуты фрагмента — номер судозахода, линия и роль читателя (тальман, стивидор, агент, диспетчер смены) — лежат в самом документе индекса, поэтому смена роли меняет только запрос и не требует переиндексации. Отказы пишутся отдельной таблицей PostgreSQL с ролью, номером судозахода и текстом запроса, сервисы разложены в Docker, счётчики отказов выведены в Grafana.
Воспроизводимый журнал подсказок ассистента по кодам ТН ВЭД
Задача, решение и что передали
Задача
Декларант получал от ассистента подсказку по коду и описанию товара, но при споре по декларации восстановить, на какой редакции классификатора она дана, было нечем.
Решение
К каждому обращению пишем номер декларации на товары, версию модели, редакцию ТН ВЭД и перечень разъяснений, из которых собран ответ. Индекс версионируется вместе с классификатором, поэтому запрос переигрывается на той же редакции и даёт ту же выдачу. Ответы, где код предложен без ссылки на действующее разъяснение, помечаются и уходят на разбор старшему декларанту.
Сдано 4
Передали журнал обращений с версиями индекса
Процедуру повторного прогона запроса
Реестр редакций классификатора
Отчёт по неподтверждённым кодам
Как делали
Разбор споров Разобрали с декларантами и старшим декларантом, как используется подсказка по коду и описанию товара и что происходит при споре по декларации. Выяснили, что редакция ТН ВЭД и состав разъяснений, на которых собран ответ, нигде не фиксируются.
Версионирование индекса Индекс решили версионировать вместе с классификатором, чтобы запрос переигрывался на той же редакции и давал ту же выдачу. К обращению стали писать номер декларации на товары, версию модели, редакцию ТН ВЭД и перечень разъяснений — иначе восстанавливать подсказку нечем.
Журнал обращений Собрали реестр редакций классификатора, версионированную сборку индекса, журнал обращений с привязкой к декларации и процедуру повторного прогона запроса. Ответы, где код предложен без ссылки на действующее разъяснение, стали помечать и отправлять на разбор старшему декларанту.
Повторный прогон Брали архивные обращения и переигрывали их на зафиксированной редакции классификатора, сверяя выдачу и ответ, и отдельно смотрели пометку кодов без действующего разъяснения. Провалом считали расхождение повторного прогона с исходной выдачей и обращение, у которого не восстанавливается редакция ТН ВЭД.
Передача брокеру Отдали журнал обращений с версиями индекса, процедуру повторного прогона запроса, реестр редакций классификатора и отчёт по неподтверждённым кодам; со старшим декларантом прошли разбор помеченных ответов. Брокер сам подключает новую редакцию классификатора и переигрывает спорное обращение.
Архитектура
Перед ассистентом стоит сервис журнала на C++, доступный по gRPC: он фиксирует номер декларации на товары, версию весов модели, редакцию классификатора ТН ВЭД и идентификатор снимка индекса, из которого собран ответ. Индекс версионируется вместе с классификатором — каждая редакция это неизменяемый снимок в OpenSearch со своим алиасом, поэтому повторный прогон адресуется к алиасу той же редакции и даёт ту же выдачу, а очередная переиндексация не затирает прежнюю. Обращения и перечни разъяснений лежат в PostgreSQL, тексты разъяснений — в MinIO, сама запись обращения проходит через Kafka, чтобы фиксация не задерживала ответ декларанту. Ответ, где код предложен без ссылки на действующее разъяснение, помечается при записи и уходит в очередь старшего декларанта, схемы сообщений описаны Protocol Buffers, счётчики собирает Prometheus, узлы разложены в Docker.
Обследование данных судозаходов перед моделью расстановки у причалов
Задача, решение и что передали
Задача
План расстановки судов у причалов собирался в таблице по звонкам агентов, а фактические сроки грузовых операций оставались в тальманских отчётах и в систему не попадали.
Решение
Свели архив заявок на судозаход, суточные графики грузовых работ и тальманские счёты по люкам, сшив записи номером судозахода. Разметили простои по причинам — ожидание лоцманской проводки, отказ по погоде, занятость перегружателя — и показали, какие из них нигде не фиксируются. Проверили заполнение осадки, длины судна и нормы выгрузки по роду груза.
Сдано 3
Передали справочник причин простоя
Карту пропусков по полям заявки
Размеченную выборку судозаходов и требования к учёту недостающих событий
Как делали
Сбор источников Подняли архив заявок на судозаход, суточные графики грузовых работ и тальманские счёты по люкам и поговорили с диспетчерами и агентами о том, как план расстановки собирается по звонкам. Увидели, что фактические сроки грузовых операций остаются в тальманских отчётах и в систему не попадают.
Ключ судозахода Договорились сшивать все источники номером судозахода как единственным ключом, а причины простоя вести отдельным справочником — ожидание лоцманской проводки, отказ по погоде, занятость перегружателя. Простои, которые нигде не фиксируются, вынесли в требования к учёту, а не стали домысливать по данным.
Разметка простоев Собрали сшитую выборку судозаходов, разметили по ней простои по причинам и проверили заполнение осадки, длины судна и нормы выгрузки по роду груза. Пропуски свели в карту по полям заявки.
Сверка с тальманами Сверили размеченную выборку с тальманскими отчётами и суточными графиками грузовых работ по отобранным судозаходам вместе с диспетчерами. Судозаход, где начало и окончание грузовых операций не восстанавливаются по документам, считался непригодным, и такие записи в выборку не допускались.
Передача диспетчерам Отдали справочник причин простоя, карту пропусков по полям заявки, размеченную выборку судозаходов и требования к учёту недостающих событий; с диспетчерской службой прошли разметку простоя. Порт сам ведёт справочник причин и пополняет выборку по новым судозаходам.
Архитектура
Сшивка ведётся отдельной джобой на C++ поверх Apache Arrow: архив заявок на судозаход, суточные графики грузовых работ и тальманские счёты по люкам читаются из выгрузок учётной системы и склеиваются номером судозахода. Разобранные записи складываются в Parquet и грузятся в ClickHouse — колоночная раскладка выбрана потому, что разбор простоев сканирует весь архив по нескольким полям (причина, причал, время начала и окончания операции), а строчная выгрузка поднимала бы записи судозаходов целиком. Справочник причин простоя — ожидание лоцманской проводки, отказ по погоде, занятость перегружателя — и ручная разметка эпизодов лежат в PostgreSQL отдельно от витрины, чтобы правка разметки не требовала пересборки архива. Карта пропусков по осадке, длине судна и норме выгрузки по роду груза считается тем же прогоном под управлением Airflow, разведочные срезы делаются DuckDB, готовые показываются в Superset.
Витрина вагонных рейсов с профилированием источников дислокации
Задача, решение и что передали
Задача
Оборот вагона считали в каждом отделе по-своему: диспетчер брал дислокацию из одной выгрузки, экономист — из накладных ГУ-27, и цифры между отчётами не сходились.
Решение
Собрали витрину рейса вагона: приём груза, дислокация по станциям, выгрузка и порожний возврат склеены номером вагона и номером накладной. Профилирование источников показало рейсы без станции назначения, дубли отправок при переоформлении накладной и станции, которых нет в тарифном справочнике. Оборот и вагонное плечо вынесены в витрину и больше не считаются формулой внутри отчёта.
Сдано 3
Передали модель витрины рейса
Отчёт профилирования источников
Правила склейки дислокации с накладной и список станций к нормализации
Как делали
Сбор выгрузок Собрали, откуда каждый отдел берёт цифру — диспетчер из выгрузки дислокации, экономист из накладных ГУ-27 — и разложили расхождение оборота вагона по источникам. Прошли по справочникам станций и увидели, что часть станций из выгрузок отсутствует в тарифном справочнике.
Модель витрины Решили строить витрину рейса вагона, склеивая приём груза, дислокацию по станциям, выгрузку и порожний возврат номером вагона и номером накладной. Оборот и вагонное плечо вынесли в саму витрину, чтобы они перестали быть формулой внутри отчёта каждого отдела.
Профилирование источников Собрали модель витрины и правила склейки дислокации с накладной, прогнали профилирование источников и вытащили рейсы без станции назначения, дубли отправок при переоформлении накладной и станции вне тарифного справочника. Список станций к нормализации свели отдельно.
Пересчёт оборота Пересчитали оборот и вагонное плечо по витрине и сопоставили с отчётами диспетчерской и экономистов на одном и том же массиве рейсов. Рейс, у которого дислокация не склеивается с накладной или станция не опознаётся тарифным справочником, считался неучтённым, и витрина с такими рейсами не принималась.
Передача отделам Отдали модель витрины рейса, отчёт профилирования источников, правила склейки дислокации с накладной и список станций к нормализации; с диспетчерской и экономистами прошли чтение витрины. Заказчик сам пополняет правила склейки и нормализует станции.
Архитектура
Витрина рейса собирается моделями dbt в ClickHouse: приём груза, дислокация по станциям, выгрузка и порожний возврат склеиваются парой «номер вагона + номер накладной». Пара, а не один номер вагона, взята ключом потому, что при переоформлении накладной один физический рейс даёт две отправки, и склейка только по вагону задваивает рейс. Оборот и вагонное плечо вынесены полями витрины, а не формулой внутри отчёта, поэтому диспетчер и экономист читают одно значение; отчёт профилирования источников — рейсы без станции назначения, дубли отправок, станции вне тарифного справочника — строится тем же слоем. События дислокации принимаются в Kafka и грузятся по расписанию Airflow, справочники и правила нормализации станций лежат в PostgreSQL, кабинет на Next.js с графиками ECharts отдаёт витрину рейса и отчёт профилирования, узлы разложены в Docker.
Дорожная карта ИИ для подготовки таможенных деклараций
Задача, решение и что передали
Задача
Код ТН ВЭД и графы декларации декларант подбирал по инвойсу и собственной памяти, а причины отказов в выпуске оставались в переписке с инспектором.
Решение
Разложили путь товарной партии от инвойса до выпуска и отметили шаги, где решение принимается по тексту: подбор кода ТН ВЭД, сбор разрешительных документов, заполнение граф декларации, ответ на запрос инспектора. Для каждого шага собрали историю — архив деклараций с итоговым кодом и перечень отказов с формулировками — и оценили, хватает ли её для замера качества. Очередь выстроили по зависимостям, а критерии перехода между этапами записали отдельно.
Сдано 3
Передали дорожную карту с очередью шагов
Паспорт данных по каждому шагу
Разбор отказов в выпуске и критерии перехода между этапами
Как делали
Путь партии Разложили путь товарной партии от инвойса до выпуска и с декларантами отметили шаги, где решение принимается по тексту: подбор кода ТН ВЭД, сбор разрешительных документов, заполнение граф декларации, ответ на запрос инспектора. Подняли переписку с инспектором и увидели, что причины отказов в выпуске нигде не сведены.
История под шаги Под каждый шаг определили, какая история нужна для замера качества — архив деклараций с итоговым кодом, перечень отказов с формулировками — и отметили шаги, где её недостаточно. Очередь решили выстраивать по зависимостям между шагами, а не по видимой простоте, и отдельно записали критерии перехода между этапами.
Дорожная карта Собрали дорожную карту с очередью шагов, паспорт данных по каждому шагу и разбор отказов в выпуске по формулировкам. Критерии перехода между этапами оформили отдельным документом.
Проверка на архиве Прошли карту со старшим декларантом и на архивных товарных партиях проверили, поднимается ли по каждому шагу история с итоговым кодом и отказом. Шаг, где итог решения не восстанавливается по архиву, в очередь не ставился, а карта без записанных критериев перехода готовой не считалась.
Передача брокеру Отдали дорожную карту с очередью шагов, паспорт данных по каждому шагу, разбор отказов в выпуске и критерии перехода между этапами; со старшим декларантом прошли порядок запуска следующего шага. Брокер сам пополняет разбор отказов и двигает очередь.
Архитектура
Архив деклараций разбирает служба на Rust с Tokio: XML деклараций читается потоково через quick-xml, из него вынимаются графы, итоговый код ТН ВЭД, связка с инвойсом и отказы в выпуске с формулировками. Разобранные записи ложатся в ClickHouse — раскладка пути товарной партии по шагам считается сканированием всего архива по нескольким полям, и колоночное хранилище читает только их, не поднимая декларацию целиком. Реестр редакций классификатора, паспорта данных по шагам (подбор кода, сбор разрешительных документов, заполнение граф, ответ на запрос инспектора) и критерии перехода между этапами ведутся в PostgreSQL. Очередь пилотов выстроена по зависимостям между шагами, разведочные срезы делаются на Polars, разбор отказов показывается в Superset, узлы собраны в Docker.
Стек
Rust · Tokio · ClickHouse · PostgreSQL · XML деклараций · Docker · quick-xml · Polars · Superset
Контур управления автопарком с локальной моделью разбора обращений
Задача, решение и что передали
Задача
Машины, штрафы, ремонты и жалобы водителей жили в четырёх таблицах и переписке диспетчеров.
Решение
Собрали систему автопарка с нуля: карточка машины с пробегом и историей ремонтов, планировщик обслуживания и перегонов, разбор штрафов и повреждений по фото приёмки и сдачи, тарифная карта аренды, журнал сессий поездок. Языковая модель развёрнута на своих узлах внутри контура: она читает жалобу, поднимает историю поездки и телематику и собирает проект решения.
Сдано 4
Передали систему автопарка
Узел инференса
Карту интеграций телематики
Регламент дообучения
Как делали
Разбор таблиц Разобрали, что лежит в четырёх таблицах и переписке диспетчеров — машины, штрафы, ремонты, жалобы водителей — и прошли с диспетчерами разбор типовой жалобы. Посмотрели, какие данные телематики и фотофиксации доступны на момент разбора.
Карточка машины Контур собрали вокруг карточки машины с пробегом и историей ремонтов, подвесив к ней планировщик обслуживания и перегонов, тарифную карту аренды и журнал сессий поездок. Языковую модель развернули на своих узлах внутри контура, чтобы жалоба, история поездки и телематика не выходили наружу, а на выходе получался проект решения, а не готовый ответ клиенту.
Сборка контура Собрали систему автопарка с нуля, подключили телематику и фотофиксацию приёмки и сдачи, реализовали разбор штрафов и повреждений. Узел инференса подняли внутри контура и обкатали разбор жалоб на архиве обращений до перевода диспетчеров.
Прогон жалоб Прогнали архивные жалобы и акты повреждений через контур и сравнили проекты решений с тем, что диспетчеры решили в своё время, отдельно проверив привязку штрафа к сессии поездки и водителю. Проект решения без поднятой истории поездки и телематики, а также обращение, ушедшее за пределы контура, считались провалом.
Передача диспетчерам Отдали систему автопарка, узел инференса, карту интеграций телематики, регламент дообучения и инструкции оператора и механика; с диспетчерами прошли разбор жалобы от поступления до решения. Заказчик сам дообучает модель и подключает новый источник телематики.
Архитектура
Телематика приходит на шлюз по MQTT и раскладывается в Kafka, а из неё читают журнал сессий поездок, планировщик обслуживания и перегонов и разбор повреждений по фото приёмки и сдачи. Очередь стоит между шлюзом и потребителями потому, что один трек нужен нескольким службам, и прямой вызов заставил бы шлюз знать всех получателей и повторять доставку каждому по отдельности. Ядро на FastAPI ведёт карточку машины с пробегом и историей ремонтов, тарифную карту аренды и разбор штрафов в PostgreSQL, треки и телеметрия оседают в ClickHouse, фотографии — в MinIO. Разбор жалобы делает сервис vLLM на своих узлах внутри контура: он поднимает по идентификатору поездки трек и телематику и собирает проект решения оператору, инференс отделён от ядра, чтобы длинная генерация не держала соединения API парка, узлы разложены в Kubernetes.
Собственная система управления автопарком с агентным конвейером
Задача, решение и что передали
Задача
Парк вели в коробочной аренде и таблицах: перегоны, штрафы и повреждения сводили посменно вручную.
Решение
Собрали контур автопарка с нуля: реестр машин с телематикой и статусами, модуль перегонов, ТО и ремонтов, разбор постановлений и актов повреждений, тарифная сетка аренды с расчётом удержаний. Поверх контура работает агентный конвейер: агент разбирает почту с постановлениями и акты приёмки, сопоставляет время и координаты с поездкой и водителем, заводит удержание в карточку аренды. Спорное уходит в очередь оператора вместе с треком поездки и фотофиксацией.
Сдано 4
Переданы схема данных парка и тарифов
Регламент агентного конвейера
Журнал разборов штрафов
Инструкции дежурного оператора
Как делали
Разбор парка Подняли коробочную аренду и таблицы, по которым посменно сводились перегоны, штрафы и повреждения, и прошли с дежурными операторами разбор постановления и акта приёмки. Посмотрели, что даёт телематический шлюз: трек поездки, время и координаты.
Контур и конвейер Контур собрали вокруг реестра машин с телематикой и статусами, модулей перегонов, ТО и ремонтов и тарифной сетки аренды с расчётом удержаний. Агентный конвейер поставили поверх контура и договорились, что спорное он не решает сам, а уходит в очередь оператора вместе с треком поездки и фотофиксацией.
Сборка и агенты Собрали контур автопарка с нуля, подключили телематический шлюз и хранилище фотофиксации, затем завели агентов на разбор почты с постановлениями и актов повреждений и на заведение удержания в карточку аренды. Сопоставление времени и координат с поездкой и водителем доводили на архиве постановлений.
Прогон постановлений Прогнали через конвейер архивные постановления и акты приёмки и сверили привязку к поездке и водителю с треком телематики. Удержание, заведённое по постановлению без совпадения времени и координат с поездкой, считалось провалом, как и спорный случай, разобранный агентом мимо очереди оператора.
Передача операторам Передали схему данных парка и тарифов, регламент агентного конвейера, журнал разборов штрафов и инструкции дежурного оператора; с операторами прошли разбор очереди. Заказчик сам правит тарифную сетку и правила конвейера.
Архитектура
Ядро парка на Axum ведёт реестр машин, перегоны, ТО, ремонты и тарифную сетку аренды в PostgreSQL, телематический шлюз принимает треки по MQTT и складывает точки в TimescaleDB. Агентный конвейер работает отдельными воркерами за очередью RabbitMQ: агент разбирает почту с постановлениями и акты повреждений, вынимает время, координаты и государственный номер, а сопоставление с поездкой и водителем делается запросом к треку по времени и радиусу. Удержание заводится в карточку аренды с идемпотентным ключом по номеру постановления, потому что письмо приходит повторно и без ключа рассылка заводила бы второе удержание по одному эпизоду. Спорные случаи уходят в очередь оператора вместе с треком поездки и фотофиксацией из MinIO, узлы разложены в Kubernetes, очереди разбора и задержки выведены в Grafana.
Агентный конвейер внутри собственной CRM ресторанной доставки
Задача, решение и что передали
Задача
Опоздания и недовозы прилетали в мессенджеры и на телефон, компенсации диспетчеры считали и согласовывали вручную.
Решение
В собственной CRM подняли агентный конвейер поверх воронки заказа: коннекторы к чату, почте и телефонии, разбор обращения на тип и заказ, дерево инцидентов доставки, правила компенсаций, связь с курьерской сменой. Агент опознаёт заказ по номеру телефона и времени, оформляет опоздание или недовоз карточкой, поднимает компенсацию по регламенту и склеивает повторные обращения по одному заказу. Случаи вне правил уходят дежурному диспетчеру с историей заказа и трекингом курьера.
Сдано 4
Переданы правила разбора обращений
Дерево типов инцидентов доставки
Журнал решений по компенсациям
Панель дежурного диспетчера
Как делали
Обследование жалоб Разобрали, куда приходят жалобы на опоздание и недовоз — мессенджеры, почта, звонки, — и как диспетчеры считали компенсацию. Собрали действующий регламент компенсаций, историю заказов и трекинг курьерских смен, чтобы понять, по каким признакам обращение вообще связывается с заказом.
Архитектура конвейера Конвейер поставили поверх воронки заказа, а не сбоку: обращение сразу привязывается к заказу по номеру телефона и времени и дальше идёт по дереву типов инцидентов доставки. Правила компенсаций вынесли в конфигурацию, а всё, что под правила не подходит, отправили дежурному диспетчеру, а не оставили агенту.
Разработка в CRM Подняли коннекторы к чату, почте и телефонии, дерево инцидентов и склейку повторных обращений по одному заказу, затем правила компенсаций и связь с курьерской сменой. На стенде крутили копию потока обращений на исторических заказах и смотрели, как склеиваются повторы по одному номеру.
Проверка на заказах Прогоняли звонки и сообщения по заказам с опозданием, с частичным недовозом и по чужому заказу с того же номера телефона. Провалом считали компенсацию, оформленную на заказ другого клиента, и повторное обращение, породившее вторую компенсацию по тому же заказу.
Передача диспетчерам Передали правила разбора обращений, дерево типов инцидентов доставки, журнал решений по компенсациям и панель дежурного диспетчера. Диспетчерская сама правит правила компенсаций, добавляет тип инцидента и разбирает случаи вне правил с историей заказа и трекингом курьера.
Архитектура
Коннекторы чата, почты и телефонии приводят обращение к одному формату и кладут в очередь; телефония приходит вебхуками и повторяет доставку, поэтому приёмник дедуплицирует по идентификатору звонка. Разборщик определяет тип и ищет заказ по номеру телефона и окну времени, а склейка повторных обращений идёт по ключу «заказ плюс тип инцидента» в Redis: несколько звонков про одно опоздание должны дать одну карточку и одну компенсацию. Дерево типов инцидентов и правила компенсаций хранятся версиями в PostgreSQL, решение сохраняется вместе с версией правила, по которой оно принято. Случай вне правил уходит дежурному диспетчеру в панель на Vue с историей заказа и связью с курьерской сменой.
Собственный контур управления автопарком с приёмкой по кадрам
Задача, решение и что передали
Задача
Состояние машин фиксировали снимками в мессенджере, а сам парк вели в таблицах и коробочной CRM, не рассчитанной на посуточное движение автомобилей.
Решение
Контур собран из реестра автомобилей и их состояния, планировщика подач, ремонтов и мойки, модуля приёмки в мобильном приложении и модели, которая по снимкам кузова и салона выделяет сколы, вмятины и загрязнение. Кадры сессии сопоставляются с предыдущей приёмкой того же автомобиля, спорный случай уходит оператору с подсветкой участка, решение и снимки складываются в карточку аренды. Подтверждённые решения операторов возвращаются в обучающую выборку по регламенту дообучения.
Сдано 4
Контур управления автопарком
Модель оценки повреждений
Размеченный набор сессий
Журнал приёмки со снимками
Как делали
Обследование парка Выгрузили снимки машин из мессенджера, где фиксировали состояние, и разметили, что на них видно: сколы, вмятины, загрязнение салона. Разобрали таблицы парка и коробочную CRM и увидели, что посуточное движение автомобиля там не описано — подача, ремонт и мойка живут вне карточки.
Архитектура контура Реестр автомобилей и их состояние сделали ядром: планировщик подач, ремонтов и мойки берёт состояние оттуда, приёмка живёт в мобильном приложении. Модель по кадрам поставили подсказкой, а не решением — спорный случай уходит оператору с подсветкой участка, подтверждённые решения возвращаются в обучающую выборку.
Разработка приёмки Собрали реестр и планировщик, затем модуль приёмки с фиксированным набором ракурсов кузова и салона, затем модель выделения повреждений и сопоставление кадров сессии с предыдущей приёмкой того же автомобиля. На стенде крутили размеченные сессии, отлаживая сцепку кадров и складывание решения со снимками в карточку аренды.
Проверка на сессиях Прогоняли размеченный набор сессий, включая съёмку в темноте, мокрый кузов и грязь поверх старого скола, и сверяли выделение с разметкой. Провалом считали новый скол, не показанный оператору и не попавший в карточку аренды, и повторное предъявление повреждения, уже зафиксированного на прошлой приёмке.
Передача операторам Передали контур управления автопарком, модель оценки повреждений, размеченный набор сессий, журнал приёмки со снимками и регламент дообучения. Операторы сами разбирают спорные случаи, а команда собирает следующую версию модели из подтверждённых решений по регламенту.
Архитектура
Мобильный модуль приёмки заливает кадры сессии в объектное хранилище и публикует событие в Kafka; сервис инференса вынесен отдельно и живёт на узлах с GPU — веса меняются по регламенту дообучения без релиза бэкенда, а запросы к нему пакетируются. Модель отдаёт участки со сколами, вмятинами и загрязнением, сервис сравнения подтягивает разметку предыдущей приёмки того же автомобиля и оставляет только новые повреждения. В PostgreSQL лежат реестр автомобилей и состояний, планировщик подач, ремонтов и мойки и карточка аренды, причём в карточке хранятся ключи объектов и разметка, а кадры остаются в хранилище. Спорный случай уходит оператору с подсветкой участка, подтверждённое решение помечается и складывается в отдельный набор для дообучения.
Стек
Python · FastAPI · PostgreSQL · MinIO · Kafka · Kubernetes · PyTorch · Triton Inference Server · Label Studio
Собственная тарифно-биллинговая система рейсов с антифродом
Задача, решение и что передали
Задача
Тарифы считались набором скриптов поверх коробочной кассы, а накрутки по рейсам и бонусам находили постфактум по жалобам водителей и пассажиров.
Решение
Биллинг построен из тарифного конструктора с зонами и коэффициентами, реестра рейсов, расчёта вознаграждения водителя, удержаний и выплат. В поток рейсов встроен антифрод-скоринг: модель оценивает трек, поведение устройства и историю связей водителя и пассажира, помечая рейс до начисления выплаты. Помеченные рейсы уходят в очередь разбора с раскладкой признаков, решение разборщика возвращается в обучающую выборку.
Сдано 4
Биллинг рейсов
Тарифный конструктор
Модель антифрод-скоринга
Очередь разбора помеченных рейсов
Как делали
Обследование тарифов Разобрали скрипты расчёта поверх коробочной кассы и восстановили, какие зоны и коэффициенты в них зашиты. Собрали жалобы водителей и пассажиров, по которым раньше находили накрутки, и посмотрели, что известно о рейсе: трек, поведение устройства, история связей водителя и пассажира.
Архитектура биллинга Тариф вынесли в конструктор с зонами и коэффициентами вместо кода, реестр рейсов сделали источником для расчёта вознаграждения, удержаний и выплат. Антифрод-скоринг поставили в поток до начисления выплаты, а не после: помеченный рейс уходит в очередь разбора, решение разборщика возвращается в обучающую выборку.
Разработка расчёта Собрали тарифный конструктор и реестр рейсов, затем расчёт вознаграждения водителя с удержаниями и выплатами, затем скоринг по треку, устройству и истории связей. На стенде прогоняли исторические рейсы, сверяя расчёт по новому конструктору с тем, что считали скрипты.
Проверка на рейсах Гоняли рейсы с петляющим треком, повторяющимися парами водителя и пассажира и подозрительным поведением устройства, а также пересчитывали архивные рейсы по конструктору. Провалом считали расхождение суммы рейса с расчётом по действующему тарифу и выплату, начисленную по рейсу, помеченному скорингом.
Передача разборщикам Передали биллинг рейсов, тарифный конструктор, модель антифрод-скоринга, очередь разбора помеченных рейсов и регламент дообучения. Тарифный отдел сам собирает зоны и коэффициенты, разборщики ведут очередь и пополняют обучающую выборку.
Архитектура
Рейсы идут потоком в Kafka, тарифный движок берёт версию тарифа с зонами и коэффициентами и раскладывает рейс на вознаграждение водителя, удержания и выплату. Скоринг вынесен отдельным сервисом за gRPC с бюджетом ответа: признаки трека, поведения устройства и истории связей водителя и пассажира считаются в ClickHouse, где агрегаты по нескольким колонкам за длинный период читаются, а в строчной базе биллинга их держать нельзя. Начисление идёт с идемпотентным ключом по рейсу, потому что повторная доставка события из очереди не должна создавать вторую выплату. Помеченный рейс встаёт в очередь разбора с раскладкой признаков, решение разборщика пишется рядом с версией модели и уходит в обучающую выборку.
Собственная тарифная и биллинговая система поездок каршеринга
Задача, решение и что передали
Задача
Тарифы, пакеты минут и штрафы считали самописными скриптами поверх коробочной кассы, а спорные списания разбирали по логам вручную.
Решение
Сделали тарифный движок с версиями планов, зонами, поминутными и посуточными правилами, пакетами и промокодами, и ядро биллинга: лицевой счёт, холд депозита, догрузка по штрафам, топливу и мойке. События поездки принимаются из телематики через очередь, платёжный слой работает с эквайрингом на авторизации, дохолдировании, возвратах и реестрах, каждый расчёт хранится вместе с версией тарифа и исходными событиями, автосверка разводит реестр банка и начисления.
Сдано 4
Каталог тарифных правил
Схема лицевого счёта и холдов
Регламент сверки с эквайрингом
Журнал пересчётов
Как делали
Обследование скриптов Разобрали самописные скрипты расчёта поверх коробочной кассы и восстановили, как в них считаются поминутные и посуточные правила, пакеты минут и штрафы. Подняли спорные списания, которые разбирали по логам, и посмотрели, что приходит из телематики и какие реестры даёт эквайринг.
Архитектура тарифов Тарифы вынесли в движок с версиями планов, зонами, пакетами и промокодами, чтобы расчёт всегда ссылался на версию плана. В ядре биллинга завели лицевой счёт и холд депозита с догрузкой по штрафам, топливу и мойке, события поездки принимаются из телематики через очередь, а каждый расчёт хранится вместе с исходными событиями.
Разработка биллинга Сначала тарифный движок с версиями и зонами, потом лицевой счёт, холды и догрузка, потом платёжный слой с авторизацией, дохолдированием, возвратами и реестрами. Автосверку реестра банка с начислениями делали последней, на стенде гоняли поездки из архива телематики.
Проверка пересчётов Пересчитывали архивные поездки по соответствующим версиям тарифа, проверяли обрывы телематики и повторную доставку событий, гоняли дохолдирование и возврат. Провалом считали расчёт, который не воспроизводится из сохранённых событий и версии тарифа, и двойное списание по одной поездке при повторе события.
Передача группе Передали каталог тарифных правил, схему лицевого счёта и холдов, регламент сверки с эквайрингом, журнал пересчётов и стенд нагрузочных прогонов. Тарифная группа сама выпускает версию плана, поддержка разбирает спорное списание по журналу пересчётов.
Архитектура
События поездки приходят из телематики по MQTT в шлюз и складываются в Kafka с ключом по идентификатору поездки, поэтому события одной поездки обрабатываются по порядку одним потребителем. Тарифный движок считает по версии плана с зонами, поминутными и посуточными правилами и сохраняет расчёт вместе с исходными событиями и номером версии тарифа: спорное списание разбирается пересчётом на тех же данных, иначе восстановить его нечем. Лицевой счёт, холд депозита и догрузки по штрафам, топливу и мойке меняются транзакцией в PostgreSQL, каждая операция берётся по идемпотентному ключу, чтобы переотправка события не списала дважды. Реестры банка приходят файлами по SFTP, автосверка разводит их с начислениями и складывает несведённое в отдельный реестр, Redis держит горячие тарифные справочники и счётчики пакетов минут.
Система управления автопарком с телематикой и нарядами на обслуживание
Задача, решение и что передали
Задача
Парк вели в таблицах: пробег и расход топлива переносили из выгрузок телематики руками, а на ремонт машину ставили по звонку механика.
Решение
Контур собран с нуля из узлов: карточка автомобиля с историей владения, страховок и повреждений; приёмник телематики по пробегу, топливу и ошибкам бортовой сети; планировщик обслуживания по интервалам пробега и моточасам; наряды на ремонт с нормо-часами и списанием запчастей со склада; акт осмотра с фотофиксацией при закрытии аренды. Справочник машин, зон и станций обслуживания ведётся в одной базе, остальные узлы берут состояние машины оттуда. Смена статуса машины закрывает её от бронирования до подписания наряда.
Сдано 4
Исходный код контура
Схема базы и миграции
Протокол приёмки телеметрии
Инструкции механика и диспетчера
Как делали
Обследование парка Разобрали таблицы парка и выгрузки телематики, из которых руками переносили пробег и расход топлива, и посмотрели, как машина попадала на ремонт по звонку механика. С механиками и диспетчерами собрали интервалы обслуживания, перечень нормо-часов и состав акта осмотра при закрытии аренды.
Архитектура справочника Справочник машин, зон и станций обслуживания сделали единой базой, остальные узлы берут состояние машины оттуда, а не держат свою копию. Приёмник телематики отделили от планировщика обслуживания, а смену статуса связали с бронированием: машина закрыта от брони до подписания наряда.
Разработка нарядов Собрали карточку автомобиля с историей владения, страховок и повреждений и приёмник телематики по пробегу, топливу и ошибкам бортовой сети, затем планировщик обслуживания по интервалам пробега и моточасам. Наряды на ремонт с нормо-часами и списанием запчастей со склада и акт осмотра с фотофиксацией делали следом, на стенде гоняли поток телеметрии с записанных машин.
Проверка статусов Прогоняли поток телематики с пропусками и обрывами, ставили машину на ремонт и пытались забронировать её до подписания наряда, закрывали аренду без фотофиксации. Провалом считали доступную для брони машину со статусом ремонта и наряд, закрытый без списания запчастей со склада.
Передача механикам Передали исходный код контура, схему базы и миграции, протокол приёмки телеметрии, инструкции механика и диспетчера и печатные формы нарядов. Механики сами ведут наряды и интервалы обслуживания, диспетчеры закрывают аренду актом осмотра.
Архитектура
Телематика приходит на MQTT-брокер, шлюз перекладывает поток в Kafka, откуда сырые точки по пробегу, топливу и ошибкам бортовой сети пишутся в TimescaleDB: это временные ряды с постоянными вставками и выборками по окну, держать их строками в карточке автомобиля нельзя. В PostgreSQL живут карточка автомобиля с историей владения, страховок и повреждений, справочник зон и станций обслуживания, наряды и акты осмотра, а агрегаты пробега и моточасов поднимаются в карточку из потока. Планировщик обслуживания сравнивает накопленные значения с интервалами пробега и моточасов и заводит наряд с нормо-часами и списанием запчастей со склада. Статус машины меняет только ядро, остальные узлы читают его оттуда, поэтому машина в ремонте закрыта от бронирования до подписания наряда.
Мультиарендная платформа складского учёта с тарификацией подписки
Задача, решение и что передали
Задача
Продукт жил как одна установка под одного клиента: каждому новому складу разворачивали отдельную копию базы, тариф считали в таблице, счёт выставляли руками.
Решение
Продукт собран вокруг общего ядра: складской учёт — приёмка, размещение, отбор по заданиям, инвентаризация — с изоляцией данных арендаторов на уровне схем и политик доступа; конструктор тарифов по числу мест хранения, операций и подключённых интеграций; счётчики потребления с суточной агрегацией; биллинг подписки со счетами, актами и блокировкой при неоплате; мастер первичной настройки склада. Контур арендатора разворачивается из шаблона конфигурации, обновление ядра выходит на всех арендаторов одним релизом.
Сдано 4
Исходный код платформы
Схема изоляции арендаторов
Конструктор тарифов
Регламент выпуска обновлений
Как делали
Обследование установки Разобрали единственную установку под одного клиента и выписали, что разворачивалось руками для каждого нового склада и как тариф считался в таблице. Собрали, чем склады отличаются по операциям — приёмка, размещение, отбор по заданиям, инвентаризация — и что считать потреблением для тарифа.
Архитектура арендаторов Перешли на общее ядро с изоляцией данных арендаторов на уровне схем и политик доступа вместо отдельной копии базы на склад. Тариф вынесли в конструктор по числу мест хранения, операций и подключённых интеграций, а контур арендатора стали разворачивать из шаблона конфигурации, чтобы обновление ядра выходило на всех одним релизом.
Разработка ядра Сначала складское ядро с приёмкой, размещением, отбором по заданиям и инвентаризацией, потом изоляция арендаторов по схемам и политикам. Конструктор тарифов, счётчики потребления с суточной агрегацией и биллинг подписки со счетами, актами и блокировкой при неоплате собирали следом, мастер первичной настройки склада — последним.
Проверка изоляции Разворачивали нескольких арендаторов из шаблона и обращались от имени одного к данным другого, гоняли счётчики потребления на складских операциях и сверяли начисление с тарифным планом. Провалом считали видимость мест хранения или заданий чужого арендатора и расхождение счёта с показаниями счётчиков потребления.
Передача команде Передали исходный код платформы, схему изоляции арендаторов, конструктор тарифов, регламент выпуска обновлений и документацию подключения склада. Команда сама подключает новый склад мастером настройки, собирает тарифный план и выпускает обновление ядра на всех арендаторов.
Архитектура
Ядро общее для всех арендаторов, данные разведены схемами PostgreSQL с политиками доступа: релиз выходит один, поэтому миграция прогоняется по списку схем, а не по отдельным установкам, и выгрузка данных отдаётся по одному арендатору. Складской контур — приёмка, размещение, отбор по заданиям, инвентаризация — работает через API, терминалы сбора данных держат офлайн-очередь операций и досылают их по ключу задания. Счётчики мест хранения, операций и подключённых интеграций пишутся событиями в Kafka и агрегируются посуточно в витрину, по ней биллинг подписки выставляет счета и акты. Блокировка при неоплате — флаг арендатора, который проверяется на входе в API, а не в каждом модуле, а мастер первичной настройки разворачивает контур склада из шаблона конфигурации.
Кабинет водителя поверх собственного тарифного и биллингового контура
Задача, решение и что передали
Задача
Тарифы и списания считались в арендованном сервисе, а спорные поездки разбирались вручную по выгрузкам.
Решение
Собрали тарифный и биллинговый контур с нуля: справочник тарифных планов и зон, расчёт поездки по телеметрии автомобиля, депозит и холдирование карты, реестр штрафов и повреждений. Кабинет водителя показывает поездку с разложением по минутам, километрам и зонам, чек, историю списаний и форму спора с фотографиями. Контур автопарка ведёт статусы машин, страховки и обслуживание, события уходят очередью в бухгалтерию и в поддержку.
Сдано 4
Передали контур расчёта
Реестр тарифных планов
Кабинет водителя
Регламент разбора споров
Как делали
Обследование выгрузок Подняли выгрузки арендованного тарификатора, эквайринговые выписки и журналы телеметрии, прошли с поддержкой спорные поездки от жалобы до ручного пересчёта. Нашли сессии без завершения, минуты вне тарифных окон и повторные списания по одной аренде.
Архитектура расчёта Расчёт вынесли в отдельную службу поверх событий телеметрии, тарифные планы и зоны сделали версионными — поездка считается по версии на момент старта. Депозит и холдирование развели с расчётом, события в бухгалтерию и поддержку пустили очередью, а от пересчёта на лету в кабинете отказались: кабинет показывает сохранённый чек.
Разработка контура Сначала собрали справочник планов и зон и движок разложения поездки по минутам, километрам и зонам, потом депозит с холдированием на песочнице эквайринга, потом кабинет с чеком, историей списаний и формой спора с фотографиями. Реестр штрафов и повреждений и контур автопарка со страховками и обслуживанием добавляли на стенде, где проигрывали записанные треки поездок.
Проверка чеков Прогнали исторические поездки через новый движок и сверили чек с прежними списаниями, отдельно гоняли обрывы связи, незакрытые аренды и двойные холды. Провалом считали чек, который не раскладывается до минут, километров и зон, и списание, которое не воспроизводится заново из журнала.
Передача биллинга Отдали контур расчёта, реестр тарифных планов, кабинет водителя, журнал списаний и регламент разбора споров. Тарифный менеджер заводит план и зону сам, поддержка разбирает спор по журналу и снимает удержание без разработчика.
Архитектура
Телеметрия автомобилей приходит в приёмник по MQTT и складывается в Kafka с ключом по идентификатору сессии, поэтому рейтер поездок читает события одной сессии в порядке и не останавливает приёмник, когда расчётный контур перезапускается. Рейтер раскладывает сессию на минуты, километры и зоны по версии тарифного плана из PostgreSQL и отдаёт начисление биллинговому ядру, которое ходит в эквайринг за холдом и списанием. На каждое обращение к эквайрингу заводится идемпотентный ключ вида «сессия плюс операция»: повторный вебхук платёжного шлюза не порождает второе списание, а возвращает прежний результат. Активные сессии и холды лежат в Redis, фотографии по спорам — в MinIO, кабинет и поддержка читают историю из PostgreSQL; сервисы разворачиваются в Kubernetes, отказ узла приёмника переносит партиции Kafka на живые экземпляры.
Стек
Go · chi · PostgreSQL · Redis · Kafka · Kubernetes · MQTT · MinIO · Prometheus
Мобильное приложение курьера с бэкендом и кабинетом отправителя
Задача, решение и что передали
Задача
Заказы распределяли в чате, статус посылки узнавали звонком курьеру, расчёт с курьерами сводили в таблице.
Решение
Бэкенд ведёт заказ от приёма до вручения: очередь распределения по зонам и рейтингу, маршрутный лист смены, статусы, подтверждение вручения фотографией и кодом получателя. Приложение курьера работает офлайн со сменой — сканирование штрихкодов, карта точек, приём наличных и безнала, синхронизация при возврате связи. Кабинет отправителя показывает трек по каждой посылке, реестр вручений, акты и удержания, а вебхуки отдают статусы в системы магазинов.
Сдано 4
Передали бэкенд
Приложение курьера
Кабинет отправителя
Описание вебхуков статусов
Как делали
Обследование смены Прошли смену с курьером — приём посылок, маршрут, вручение, сдача наличных — и разобрали чат распределения заказов и таблицу расчётов с курьерами. Выяснили, где пропадает связь (подъезды, подземные паркинги, склады) и что подтверждение вручения держится на словах.
Архитектура заказа Заказ ведёт бэкенд от приёма до вручения, распределение сделали очередью по зонам и рейтингу, а приложение курьера спроектировали офлайн-первым: смена, маршрутный лист и сканирование живут на устройстве и синхронизируются при возврате связи. Вручение подтверждается фотографией и кодом получателя, статусы наружу отдаются вебхуками, чтобы магазины не опрашивали бэкенд.
Разработка приложения Сначала бэкенд со статусами заказа, зонами и маршрутным листом смены, затем приложение со сканированием штрихкодов, картой точек и приёмом наличных и безнала. Кабинет отправителя с треком, реестром вручений, актами и удержаниями и вебхуки собирали после, на стенде с эмулятором обрывов связи.
Проверка офлайна Гоняли смену целиком в самолётном режиме и синхронизировали её при возврате связи, повторяли доставку вебхуков при недоступном магазине, сверяли принятые наличные с реестром вручений и удержаниями. Провалом считали потерянное на устройстве вручение и статус, ушедший в магазин, но разошедшийся с состоянием заказа в бэкенде.
Передача продукта Отдали бэкенд, приложение курьера, кабинет отправителя, описание вебхуков статусов и реестр вручений с удержаниями. Диспетчер сам меняет зоны и правила распределения, отправитель подключает свой магазин по описанию вебхуков.
Архитектура
Бэкенд на Ktor ведёт заказ от приёма до вручения, распределение по зонам и рейтингу вынесено в воркер, читающий очередь заказов из Kafka, а статусы уходят магазинам отдельным отправителем вебхуков с подписью тела и повторной доставкой. Приложение курьера держит смену в локальной базе Room и работает без сети: сканирование штрихкодов, отметки точек, приём наличных и безнала складываются в очередь операций, каждая со своим клиентским UUID. Сервер сохраняет операцию по этому ключу, поэтому повторная синхронизация после возврата связи не удваивает вручение и не задваивает приём денег — ключ генерирует устройство, а не сервер, иначе повтор запроса получил бы новый идентификатор. Состояние заказов и реестр вручений лежат в PostgreSQL, срез маршрутного листа — в Redis, карта точек строится на MapLibre, развёртывание в Kubernetes.
Собственная система управления автопарком и арендой машин
Задача, решение и что передали
Задача
Машины, договоры аренды и телематика лежали в трёх разных зарубежных сервисах, и состояние автомобиля собирали вручную из выгрузок.
Решение
Построили систему на PostgreSQL под Linux: реестр машин с историей владения, страховок и ремонтов, приёмник телематики с пробегом, топливом и геометкой, модуль бронирований и тарифов, наряды на обслуживание и мойку, разбор штрафов и повреждений. Телеметрия пишется в отдельную временную базу с посуточной агрегацией, оперативный контур и аналитика разведены по репликам. Мобильный клиент водителя и диспетчерское рабочее место работают через один интерфейс доступа к данным.
Сдано 4
Передали исходный код
Схему базы и миграции
Описание интерфейса телематики
Регламент дежурства и резервного копирования
Как делали
Обследование источников Свели три зарубежных сервиса — реестр машин, договоры аренды, телематика — и сравнили карточку автомобиля, собранную из выгрузок, с фактическим состоянием машины на площадке. Прошли с механиками и диспетчерами приём машины, наряд на обслуживание и мойку, разбор штрафа и повреждения.
Архитектура парка Ядро подняли на PostgreSQL под Linux: реестр машин с историей владения, страховок и ремонтов, поверх — бронирования и тарифы; телеметрию с пробегом, топливом и геометкой увели в отдельную временную базу с посуточной агрегацией. Оперативный контур и аналитику развели по репликам, мобильный клиент водителя и диспетчерское место посадили на один интерфейс доступа к данным.
Разработка системы Сначала реестр машин и перенос истории владения, страховок и ремонтов, затем приёмник телематики и расчёт пробега, затем бронирования и тарифы. Наряды на обслуживание и мойку и разбор штрафов и повреждений собирали на стенде, куда подавали записанный поток с бортовых блоков.
Проверка телеметрии Проигрывали суточный поток телематики с пропусками и опоздавшими точками, сверяли пробег и топливо с бортовыми показаниями, гоняли бронирование и передачу машины между водителями. Не приняли бы работу, если бы карточка автомобиля показывала состояние, не подтверждённое телеметрией и нарядами, или если бы опоздавшая точка ломала посуточную агрегацию.
Передача системы Передали исходный код, схему базы и миграции, описание интерфейса телематики, регламент дежурства и резервного копирования и инструкции диспетчера. Диспетчер сам ведёт наряды и разбирает повреждения, администратор подключает новый бортовой блок по описанию интерфейса.
Архитектура
Приёмник телематики принимает пакеты бортовых блоков по MQTT и пишет сырые точки пробега, топлива и геометок в TimescaleDB, где гипертаблицы режутся по времени, а посуточные значения считаются непрерывной агрегацией. Временной ряд вынесен в отдельное хранилище, потому что точки пишутся только добавлением и читаются диапазонами, а в общей строчной таблице реестра индексы по машине и времени росли бы вместе со всей историей парка. Реестр машин, страховок, ремонтов, бронирований и нарядов остаётся в PostgreSQL, аналитика ходит в реплику, а приёмник и контур бронирований разделены очередью Kafka. Мобильный клиент водителя на SwiftUI и диспетчерское рабочее место обращаются к одному слою доступа к данным, локальный кеш на устройстве держит GRDB; узлы разворачиваются в Docker на Linux, метрики выводятся в Grafana.
Собственная система управления автопарком с контролем доступа ИИ-модулей
Задача, решение и что передали
Задача
Парк вели в таблицах и коробочном трекере, а подключённый помощник по обращениям видел карточку водителя целиком, вместе с паспортными и платёжными полями.
Решение
Собрали автопарковый контур с нуля: карточки машин и водителей, приём телеметрии и пробега, наряды на обслуживание, штрафы, аренды и сессии, поверх них — сервис разбора обращений на языковой модели. Между модулем и данными поставили шлюз обезличивания: он вырезает паспортные и платёжные поля, подставляет токены-заменители и пишет каждый запрос с составом переданных полей. Модель работает только по витрине обезличенных полей, а связка токена с человеком лежит в отдельном хранилище и раскрывается оператору поддержки по роли.
Сдано 4
Система управления автопарком
Шлюз обезличивания
Витрина обезличенных полей
Журнал запросов к модели
Как делали
Обследование парка Разобрали таблицы парка и коробочный трекер и посмотрели, что получал помощник по обращениям: карточку водителя целиком, вместе с паспортными и платёжными полями. Прошли с поддержкой типовые обращения — продление аренды, штраф, повреждение, спор по списанию — и выписали, какие поля для ответа действительно нужны.
Архитектура шлюза Автопарковый контур собрали вокруг карточек машин и водителей, телеметрии и пробега, нарядов, штрафов, аренд и сессий, а сервис разбора обращений на языковой модели посадили за шлюз обезличивания. Шлюз вырезает паспортные и платёжные поля и подставляет токены-заменители, связка токена с человеком лежит в отдельном хранилище и раскрывается оператору поддержки по роли.
Разработка контура Сначала карточки машин и водителей, приём телеметрии и пробега, наряды, штрафы и аренды, затем витрина обезличенных полей. Шлюз обезличивания с записью состава переданных полей и разбор обращений моделью собирали последними, на стенде с подставными карточками водителей.
Проверка обезличивания Гоняли обращения, где ответ тянул за собой паспортные и платёжные данные, читали журнал запросов и смотрели, что именно ушло в модель, пробовали раскрыть токен ролью без прав. Не приняли бы работу, если бы в запрос к модели попало паспортное или платёжное поле или если бы связку токена с человеком мог раскрыть кто-то кроме оператора с ролью.
Передача системы Отдали систему управления автопарком, шлюз обезличивания, витрину обезличенных полей, журнал запросов к модели и регламент выдачи ролей. Администратор сам правит состав вырезаемых полей и выдаёт роль раскрытия токена, поддержка работает по регламенту.
Архитектура
Автопарковый контур на FastAPI ведёт карточки машин и водителей, наряды, штрафы, аренды и сессии в PostgreSQL, телеметрия и пробег приходят от бортовых блоков по MQTT и уходят в Kafka на разбор. Сервис разбора обращений на языковой модели к базе напрямую не ходит: между ним и данными стоит шлюз обезличивания, который вырезает паспортные и платёжные поля и подставляет токены-заменители. Модель читает только витрину обезличенных полей, и запрет закреплён грантами на уровне базы, а не фильтром в коде — код меняется в каждом релизе, а грант остаётся на месте и переживает правку запроса. Связка токена с человеком лежит в отдельном хранилище с ключами в Vault и раскрывается оператору поддержки по роли, каждый запрос к модели пишется с составом переданных полей; Redis держит сессии, узлы — в Kubernetes.
Обследование тарифных данных перед собственной биллинговой системой
Задача, решение и что передали
Задача
Тарифы и списания считались в коробочном модуле и трёх таблицах, а расхождения в чеках находили по жалобам пользователей.
Решение
Свели источники начислений — телеметрию поездок, журналы блокировок и завершений, тарифные планы, промокоды, выписки эквайринга — и сопоставили их по идентификатору сессии. Разметили разрывы: поездки без завершения, дубли транзакций, минуты вне тарифных окон, сессии без привязки к автомобилю. Спроектировали контур будущего биллинга: реестр тарифов с версиями, рейтинг сессий, движок начислений, узел сверки с эквайрингом, витрина спорных списаний.
Сдано 4
Реестр источников начислений
Схема сопоставления сессий
Каталог расхождений
Модель тарифов с версиями
Как делали
Обследование источников Свели источники начислений — телеметрию поездок, журналы блокировок и завершений, тарифные планы, промокоды, выписки эквайринга — и сопоставили их по идентификатору сессии. Разметили разрывы: поездки без завершения, дубли транзакций, минуты вне тарифных окон, сессии без привязки к автомобилю.
Архитектура биллинга Спроектировали контур будущего биллинга: реестр тарифов с версиями, рейтинг сессий, движок начислений, узел сверки с эквайрингом, витрина спорных списаний. Начисление привязали к версии тарифа на момент поездки, а сверку с эквайрингом вынесли отдельным узлом, чтобы расхождение находилось до жалобы пользователя.
Разработка схемы Собирали не боевой биллинг, а схему сопоставления и каталог расхождений: прогоняли исторические сессии через правила сопоставления на стенде и смотрели, сколько поездок не сводится в чек. На выборке проверяли, что рейтинг сессии по телеметрии даёт разложение по минутам и тарифным окнам.
Проверка сопоставления Повторяли сопоставление на других периодах выгрузки, разбирали каждый класс расхождения до причины и сверяли спорные списания из жалоб с тем, что даёт схема. Провалом считали класс расхождения без объяснённой причины и сессию, которую схема сопоставления не сводит к транзакции эквайринга.
Передача каталога Передали реестр источников начислений, схему сопоставления сессий, каталог расхождений, модель тарифов с версиями и дорожную карту биллинга. Команда заказчика сама повторяет сопоставление на новых выгрузках и ведёт каталог расхождений.
Архитектура
Источники начислений — телеметрия поездок, журналы блокировок и завершений, тарифные планы, промокоды, выписки эквайринга — грузятся в сырой слой ClickHouse и сопоставляются по идентификатору сессии. Сопоставитель на Rust читает поток из Kafka и помечает разрывы: поездки без завершения, дубли транзакций, минуты вне тарифных окон, сессии без привязки к автомобилю. ClickHouse взят под сырьё, потому что сверка идёт сканами по всему объёму сессий за период, а не точечными выборками по ключу, и строчное хранилище тянуло бы всю запись поездки ради двух колонок. Каталог расхождений, реестр тарифов с версиями и модель будущего биллинга лежат в PostgreSQL, проверки описаны Great Expectations и запускаются расписанием Airflow, срезы выводятся в Grafana, окружение — Docker.
Стек
Rust · Tokio · ClickHouse · PostgreSQL · Kafka · Docker · Airflow · Great Expectations · Grafana
Проверяется: сборочный конвейер и протоколы автотестов
Портирование клиента медицинской системы под отечественные ОС
Задача, решение и что передали
Задача
Рабочие места врачей и регистратуры работали только на Windows, установка шла ручным инсталлятором, а на закупаемых машинах с отечественной ОС клиент не запускался.
Решение
Заменили компоненты, завязанные на WinAPI: печать направлений и выписок перевели на CUPS, работу с электронной подписью — на криптопровайдер через PKCS#11, настройки из реестра вынесли в конфигурационные файлы. Собрали пакеты deb и rpm с зависимостями из репозиториев Astra Linux и РЕД ОС, обновление поставили на штатный пакетный менеджер вместо инсталлятора. В сборочный конвейер добавили прогон автотестов интерфейса в виртуальных машинах обеих ОС, чтобы расхождения ловились до выпуска.
Сдано 4
Передали пакеты для обеих ОС
Сборочный конвейер
Руководство по установке и обновлению
Протоколы прогона автотестов
Как делали
Осмотр рабочих мест Прошли по рабочим местам врачей и регистратуры и выписали всё, что завязано на Windows: печать направлений и выписок, электронная подпись, настройки в реестре, ручной инсталлятор. Проверили на закупленных машинах, чем именно клиент падает при запуске под отечественной ОС.
Уход от WinAPI Компоненты на WinAPI заменили общесистемными: печать — на CUPS, подпись — на криптопровайдер через PKCS#11, настройки из реестра — в конфигурационные файлы. Обновление решили отдать штатному пакетному менеджеру вместо инсталлятора, поэтому собирали deb и rpm с зависимостями из репозиториев Astra Linux и РЕД ОС.
Сборка и автотесты Заменили компоненты и собрали пакеты под обе целевые ОС, затем добавили в сборочный конвейер прогон автотестов интерфейса в виртуальных машинах каждой из них, чтобы расхождения ловились до выпуска.
Сценарии приёма Ставили пакеты на чистые машины обеих ОС и проходили сценарии регистратуры и приёма: печать направления и выписки, подписание документа, обновление версии. Провалом считали документ, не подписавшийся из-за криптопровайдера, потерянную форму направления при печати и обновление, потребовавшее ручных действий на рабочем месте.
Передача сопровождению Передали пакеты для обеих ОС, сборочный конвейер, руководство по установке и обновлению и протоколы прогона автотестов. Служба сопровождения выпускает и раскатывает обновление по репозиторию сама.
Архитектура
Клиент рабочего места — приложение на TypeScript в Electron: серверная часть медицинской системы осталась прежней, обмен идёт по HL7 FHIR, локальный кеш направлений и справочников лежит в SQLite, мастер-данные — в PostgreSQL на сервере. Компоненты, завязанные на WinAPI, вынесены за границу приложения: печать направлений и выписок идёт через CUPS, электронная подпись — через криптопровайдер по PKCS#11, настройки перенесены из реестра в конфигурационные файлы по принятой в дистрибутивах раскладке каталогов. Установка и обновление отданы штатному пакетному менеджеру вместо собственного инсталлятора, потому что на закупаемых машинах обновления уже приходят из репозиториев, и второй механизм пришлось бы сопровождать и обновлять отдельно. Конвейер GitLab CI собирает deb и rpm и гоняет автотесты интерфейса в виртуальных машинах обеих ОС; на рабочем месте состояния, кроме кеша, нет, поэтому отказ клиента лечится переустановкой пакета.
Стек
TypeScript · Electron · SQLite · PostgreSQL · HL7 FHIR · GitLab CI · CUPS · PKCS#11 · deb/rpm
Проверяется: журнал сообщений с повторной отправкой
Шина обмена между медицинской, лабораторной и кассовой системами
Задача, решение и что передали
Задача
Направления в лабораторию печатались на бумаге, результаты возвращались вручную, оплата и оказанная услуга сходились только при закрытии дня.
Решение
Поставили шину: направления уходят в лабораторию сообщениями HL7 v2, результаты возвращаются в карту пациента и привязываются к назначению, чек и услуга связываются идентификатором визита. Маршрутизация, преобразование форматов и хранение исходных сообщений вынесены в шину, системы-участники друг о друге не знают. При недоступности приёмника сообщение остаётся в очереди и повторяется, дубликаты отсекаются по идентификатору сообщения.
Сдано 3
Передали шину с маршрутами и трансформациями
Журнал сообщений с повторной отправкой
Схемы форматов и инструкцию по подключению новой системы
Как делали
Путь направления Прошли путь направления от врача до лаборатории и обратно: печать на бумаге, ручной возврат результата, сведение оплаты и оказанной услуги при закрытии дня. Выписали, какие форматы отдают системы-участники и как в каждой называются визит и назначение.
Шина посередине Маршрутизацию, преобразование форматов и хранение исходных сообщений вынесли в шину, чтобы медицинская, лабораторная и кассовая системы не знали друг о друге. Направления идут сообщениями HL7 v2, результат привязывается к назначению в карте пациента, чек и услуга связываются идентификатором визита, дубликаты отсекаются по идентификатору сообщения.
Подключение по одной Подняли шину и маршруты, затем трансформации форматов и хранение исходных сообщений, затем подключили лабораторию, карту пациента и кассу по очереди. На стенде гоняли поток с выключенным приёмником и смотрели, как очередь дозаливается.
Проверка на пациентах Прогнали направления и результаты на тестовых пациентах и повторяли доставку после восстановления приёмника. Провалом считали результат, не привязавшийся к назначению, задвоенный результат в карте после повтора и услугу, не сошедшуюся с чеком по идентификатору визита.
Передача администраторам Передали шину с маршрутами и трансформациями, журнал сообщений с повторной отправкой, схемы форматов и инструкцию по подключению новой системы. Администраторы подключают систему и переотправляют сообщения из журнала сами.
Архитектура
Шина — сервис на Rust с Axum: направления принимаются от медицинской системы по HTTP, с лабораторией обмен идёт сообщениями HL7 v2 поверх MLLP, кассовая система отдаёт чеки своим API. Маршруты, преобразования форматов и исходные сообщения вынесены в шину, поэтому системы-участники не знают друг о друге и подключение новой сводится к маршруту и трансформации, а не к правкам в каждой из них. Сообщения ходят через RabbitMQ с очередью на приёмник и отложенным повтором: лаборатория недоступна во время обслуживания, и направление должно дождаться, а не потеряться на таймауте вызова. Исходное тело, результат преобразования и статус доставки лежат в PostgreSQL, дубликаты отсекаются по идентификатору сообщения уникальным индексом, результат привязывается к назначению, а чек и услуга — к идентификатору визита; справочники услуг кешируются в Redis, возраст недоставленных сообщений выведен в Grafana.
Ассистент по клиническим протоколам внутри медицинского контура
Задача, решение и что передали
Задача
Действующий протокол искали в общих папках и переписке, а внешние сервисы отпадали из-за персональных данных в тексте запроса.
Решение
Развернули модель на серверах клиники и закрыли её сетевым сегментом без выхода наружу. Перед промптом текст проходит деидентификацию: имена, номера полисов и контакты заменяются метками по словарю, обратная подстановка выполняется уже на рабочем месте. Поиск по протоколам собран на векторном индексе с фильтром по профилю отделения и признаку действующей редакции.
Сдано 4
Передали контур инференса
Словарь деидентификации
Индекс протоколов с правилами обновления
Роли отделений
Как делали
Обследование Прошли по общим папкам и переписке, где искали действующий протокол, и увидели, что рядом лежат отменённые редакции. Разобрали, какие персональные данные попадают в текст запроса врача — имена, номера полисов, контакты, и какие роли отделений заводит служба безопасности.
Архитектура Модель поставили на серверах клиники в сетевом сегменте без выхода наружу; деидентификацию вынесли перед промптом, обратную подстановку — на рабочее место, чтобы исходные значения не уходили ни в модель, ни в журнал. Поиск собрали на векторном индексе с фильтром по профилю отделения и признаку действующей редакции, от общего поиска по всем редакциям отказались.
Разработка Сначала собрали индекс протоколов с признаком редакции и профилем отделения, затем словарь деидентификации и подстановку меток, затем роли отделений и журнал запросов. На стенде работали на копии протоколов и синтетических запросах, до подключения рабочих мест.
Проверка Гоняли вопросы врачей разных отделений и проверяли, что ответ ссылается на действующую редакцию, отдельно смотрели, не протекают ли исходные значения через деидентификацию. Провалом считали ответ по отменённой редакции протокола и любой случай, когда имя или номер полиса ушли в текст запроса к модели.
Передача Передали контур инференса, словарь деидентификации, индекс протоколов с правилами обновления и роли отделений. Методистам показали, как завести новую редакцию и снять признак действующей, службе безопасности — журнал запросов и схему сегмента.
Архитектура
Контур собран из трёх узлов в сегменте без выхода наружу: шлюз на FastAPI, Triton Inference Server с локальной моделью и PostgreSQL с pgvector под индекс протоколов. Деидентификация стоит перед промптом отдельным модулем шлюза: имена, номера полисов и контакты заменяются метками по словарю, карта подстановки живёт в Redis с коротким сроком жизни и обратная замена делается уже на рабочем месте. Вектор и фильтры по профилю отделения и признаку действующей редакции держатся в одной таблице — разносить их по разным хранилищам значит делать два обращения и склеивать выдачу на клиенте, теряя фильтр внутри поиска. Роли отделений разграничены в Keycloak, запросы пишутся в журнал уже деидентифицированными; узлы подняты контейнерами, и при недоступности инференса шлюз возвращает ошибку, а не уходит во внешний сервис.
Подписной сервис сверки редакций регламентов и досье
Задача, решение и что передали
Задача
Новая редакция приходила целым файлом, и владельцы участков вычитывали документ заново, чтобы понять, что в нём изменилось.
Решение
Подняли сервис сравнения редакций: файл раскладывается по структуре разделов, изменения ищутся на уровне абзаца, модель формулирует суть правки и относит её к затронутой процедуре. Каждому изменённому пункту проставляется владелец из матрицы ответственности. Итог складывается в перечень изменённых пунктов со ссылкой на прежнюю и новую формулировку.
Сдано 4
Передали сервис сравнения редакций
Перечень изменённых пунктов
Матрицу владельцев процедур
Правила уведомления
Как делали
Обследование Взяли пары прежних и новых редакций регламентов и досье и посмотрели, как владельцы участков вычитывают файл целиком. Разобрали структуру разделов и матрицу ответственности: кто за какую процедуру отвечает.
Архитектура Сравнение разложили на два слоя: структурный разбор по разделам и абзацам находит, что изменилось, модель формулирует суть правки и относит её к затронутой процедуре. Сравнение на уровне символов отбросили — оно вытаскивало правки форматирования; владелец пункта проставляется из матрицы ответственности, а не выбирается моделью.
Разработка Сначала раскладка файла по структуре разделов, затем поиск изменений на уровне абзаца, затем формулировка сути правки с привязкой к процедуре и владельцу. На стенде прогоняли прошлые пары редакций, где было известно, что менялось.
Проверка Сверяли перечень изменённых пунктов с вычиткой владельцев участков на тех же парах редакций, отдельно смотрели перенос разделов и правку нумерации. Провалом считали изменённый пункт, не попавший в перечень, и правку форматирования, показанную как содержательное изменение.
Передача Передали сервис сравнения редакций, перечень изменённых пунктов, матрицу владельцев процедур и правила уведомления. Владельцам участков показали переход от пункта к прежней и новой формулировке, администратору — правку матрицы и историю сверок.
Архитектура
Редакция загружается в сервис на FastAPI, раскладывается парсером по структуре разделов и абзацев и складывается в PostgreSQL деревом пунктов с хешем каждого абзаца. Сравнение идёт двумя шагами: структурный diff по хешам находит изменённые, добавленные и удалённые абзацы, и только для них вызывается языковая модель — формулировать суть правки для всего документа значит платить за абзацы, которые сравнение хешей уже отсекло. Вызовы модели идут из воркеров Celery через Redis, потому что сверка большого досье длится минуты и не должна держать HTTP-запрос загрузки файла. Владелец изменённого пункта проставляется из матрицы ответственности по коду процедуры, итог хранится версией сверки с прежней и новой формулировкой, исходные файлы редакций лежат в объектном хранилище.
Эпикриз врач набирал в конце смены, перечитывая дневниковые записи, назначения и протоколы исследований в разных разделах карты.
Решение
Агент собирает записи приёмов, назначения, протоколы исследований и заключения за период госпитализации и складывает черновик эпикриза по шаблону отделения. Каждое предложение черновика хранит ссылку на исходную запись карты, поэтому формулировку можно раскрыть до строки источника. Черновик пишется в отдельную область: в карту он переносится только после подписи врача, а внесённые врачом правки возвращаются в шаблон.
Сдано 4
Шаблоны эпикриза по отделениям
Сервис сборки черновика
Область черновиков
Журнал ссылок на записи карты и порядок подписания
Как делали
Обследование Прошли с врачами конец смены и увидели, как эпикриз набирается по дневниковым записям, назначениям и протоколам исследований из разных разделов карты. Собрали шаблоны эпикриза по отделениям и различия между ними.
Архитектура Черновик пишем в отдельную область, а не в карту: в карту он попадает только после подписи врача. Каждое предложение черновика хранит ссылку на исходную запись карты, чтобы формулировку можно было раскрыть до строки источника, а правки врача возвращаются в шаблон отделения.
Разработка Сначала сборка записей приёмов, назначений, протоколов исследований и заключений за период госпитализации, затем раскладка по шаблону отделения со ссылками на источник, затем область черновиков и порядок подписания. На стенде собирали черновики по обезличенным историям болезни.
Проверка Врачи разных отделений сверяли черновик с картой на закрытых историях, отдельно смотрели перевод между отделениями и отменённые назначения. Провалом считали предложение черновика без ссылки на запись карты и любую запись, попавшую в карту до подписи врача.
Передача Передали шаблоны эпикриза по отделениям, сервис сборки черновика, область черновиков и журнал ссылок на записи карты. Заведующим показали правку шаблона своего отделения, врачам — раскрытие формулировки до источника и порядок подписания.
Архитектура
Сервис на Spring Boot забирает записи приёмов, назначения и протоколы исследований из медицинской системы FHIR-ресурсами, а события госпитализации слушает из потока HL7 v2 через интеграционный адаптер. Черновик собирается по шаблону отделения, и каждое предложение сохраняется вместе с идентификатором исходного ресурса, поэтому формулировка раскрывается до строки источника без повторного запроса в карту. Черновики лежат в отдельной схеме PostgreSQL, а не в карте: запись в карту — юридически значимое действие, и перенос выполняется одной транзакцией только после подписи врача. Сборка текста вынесена в воркер за очередью, чтобы длинный эпикриз не держал сессию врача, а правки врача сравниваются с исходным черновиком и складываются в журнал, из которого пополняются шаблоны отделения.
Извлечение условий дистрибьюторских договоров в реестр обязательств
Задача, решение и что передали
Задача
Условия по каждому дистрибьютору жили в файлах у менеджеров, и при споре о скидке или сроке поставки договор перечитывали заново.
Решение
Агент разбирает договор и допсоглашения постранично, находит разделы о цене, скидочной матрице, сроках поставки, условиях хранения и ответственности и переносит их в реестр полями. Поле хранит ссылку на страницу и абзац источника, а новое допсоглашение перезаписывает значение и сдвигает прежнее в историю редакций. Формулировки, не совпавшие с образцами, попадают на разметку юристу и после подтверждения расширяют набор образцов.
Сдано 4
Реестр обязательств
Сервис извлечения
История редакций поля
Очередь разметки юриста и набор образцов формулировок
Как делали
Обследование Собрали у менеджеров договоры с дистрибьюторами и допсоглашения и разобрали, какие разделы спорят чаще: цена, скидочная матрица, сроки поставки, условия хранения, ответственность. Посмотрели, как юрист перечитывает договор заново при споре о скидке.
Архитектура Реестр обязательств сделали полями, а не файлами: значение хранит ссылку на страницу и абзац источника. Допсоглашение перезаписывает значение и сдвигает прежнее в историю редакций, чтобы прошлая договорённость не терялась, а формулировки, не совпавшие с образцами, не додумываем — они уходят юристу на разметку.
Разработка Сначала постраничный разбор договора и поиск нужных разделов, затем перенос значений в поля реестра со ссылками, затем история редакций и очередь разметки юриста, пополняющая набор образцов. На стенде прогоняли действующие договоры с накопленными допсоглашениями.
Проверка Сверяли поля реестра с чтением договора юристом, отдельно проверяли цепочки допсоглашений по одному дистрибьютору и сканы плохого качества. Провалом считали значение в реестре без ссылки на страницу и абзац и допсоглашение, перезаписавшее поле без переноса прежнего значения в историю.
Передача Передали реестр обязательств, сервис извлечения, историю редакций поля и очередь разметки. Юристам показали разметку несовпавших формулировок и пополнение образцов, менеджерам — работу с реестром вместо файлов у себя.
Архитектура
Договор и допсоглашения загружаются в сервис на Spring Boot, файлы кладутся в S3-совместимое хранилище, постраничный разбор ставится задачей в очередь: Apache Tika достаёт текст, Tesseract обрабатывает сканы, отдельный воркер выделяет разделы о цене, скидочной матрице, сроках поставки, хранении и ответственности. Поле реестра хранит ссылку на страницу и абзац источника, а новое допсоглашение не затирает строку, а закрывает её интервалом действия и открывает новую — история редакций тогда читается обычной выборкой по дате, без отдельного архива. Реестр обязательств и набор образцов формулировок лежат в PostgreSQL, полнотекстовый поиск по тексту разделов идёт там же расширением. Формулировки, не совпавшие с образцами, уходят в очередь разметки юриста: пока подтверждения нет, поле остаётся незаполненным, а подтверждённый образец пополняет набор.
Вывод модели предразметки исследований в клиническую эксплуатацию
Задача, решение и что передали
Задача
Модель разметки исследований стояла на отдельном компьютере в кабинете, и снимки носили на флешке.
Решение
Инференс встроен в поток PACS: исследование забирается по подписке и возвращается отдельной DICOM-серией с наложением. Очередь разведена по модальностям и приоритетам, результат помечается версией модели и не трогает исходную серию. Заключение подписывает врач, его правка ложится рядом с предразметкой в журнал сравнения.
Сдано 4
Переданы сервис инференса
Журнал сравнения правок врача с предразметкой
Регламент допуска версии модели
Схема очередей по модальностям
Как делали
Обследование Посмотрели, как снимки носят на флешке к отдельному компьютеру в кабинете, и разобрали поток PACS: какие модальности и приоритеты идут. С врачами проговорили, что заключение в любом случае остаётся за ними.
Архитектура Инференс встроили в поток PACS по подписке, результат возвращаем отдельной DICOM-серией с наложением и исходную серию не трогаем. Очередь развели по модальностям и приоритетам, каждая серия помечается версией модели, а правка врача ложится рядом с предразметкой в журнал сравнения.
Разработка Сначала подписка на поток PACS и возврат отдельной серии, затем разведение очереди по модальностям и приоритетам, затем журнал сравнения правок врача с предразметкой. На стенде работали на обезличенном архиве исследований.
Проверка Прогнали архив исследований по каждой модальности, держали нагрузку с приоритетными исследованиями и проверяли поведение при недоступном узле PACS. Провалом считали изменение исходной серии исследования и серию с предразметкой без версии модели: такую версию к клинической эксплуатации не допустили бы.
Передача Передали сервис инференса, журнал сравнения правок врача с предразметкой и схему очередей по модальностям. Врачам показали работу с наложением и подпись заключения, службе PACS — регламент допуска версии модели.
Архитектура
Сервис на Rust подписан на PACS: исследование забирается по DICOMweb, кладётся в локальный кэш и ставится в очередь своей модальности и приоритета. Очереди разведены по модальностям потому, что вес серии и время инференса у разных исследований отличаются, и в общей очереди срочное ждало бы за пакетными. Инференс идёт отдельным контейнером с моделью MONAI, результат собирается новой DICOM-серией с наложением и отправляется в PACS под собственным идентификатором серии — исходная серия не переписывается, поэтому снятие версии модели сводится к удалению производной серии. Версия модели пишется в теги серии и в PostgreSQL, правка врача сопоставляется с предразметкой в журнале сравнения, длины очередей и время инференса снимаются Prometheus.
Интеграция учётной системы с государственной системой маркировки
Задача, решение и что передали
Задача
Приёмку и выбытие упаковок отмечали в стороннем кабинете, а расхождение с учётом всплывало на инвентаризации.
Решение
Сканирование кодов упаковок вынесено на терминалы сбора данных: документ приёмки собирается из отсканированных кодов и сверяется с электронной накладной поставщика по составу коробов. Обмен с системой маркировки вынесен в отдельный сервис с очередью — отправка идёт с ключом идемпотентности, повтор после обрыва связи распознаётся по ключу. Статус обработки возвращается в документ учётной системы и виден кладовщику на месте.
Сдано 4
Сервис обмена с системой маркировки
Регламент приёмки по коробам
Журнал отправок и статусов
Инструкция кладовщика
Как делали
Обследование Прошли приёмку на складе и посмотрели, как коды упаковок отмечают в стороннем кабинете и где расхождение с учётом всплывает на инвентаризации. Разобрали состав коробов в электронных накладных поставщиков и возможности терминалов сбора данных.
Архитектура Сканирование вынесли на терминалы: документ приёмки собирается из отсканированных кодов и сверяется с электронной накладной по составу коробов. Обмен с системой маркировки вынесли в отдельный сервис с очередью и ключом идемпотентности — при обрыве связи повтор распознаётся по ключу и выбытие не уходит дважды.
Разработка Сначала сбор документа приёмки на терминале и сверка с электронной накладной, затем сервис обмена с очередью и ключом идемпотентности, затем возврат статуса обработки в документ учётной системы. На стенде гоняли на тестовом контуре системы маркировки.
Проверка Прогоняли приёмку и выбытие по проверочным сценариям, рвали связь посреди отправки и повторяли её, сверяли короба с недостающими упаковками. Провалом считали повторно проведённое выбытие после обрыва связи и приёмку, закрытую при расхождении состава короба с электронной накладной.
Передача Передали сервис обмена с системой маркировки, регламент приёмки по коробам, журнал отправок и статусов и набор проверочных сценариев. Кладовщиков учили работать с терминалом и видеть статус на месте, администратору показали разбор очереди отправок.
Архитектура
Сканирование вынесено на терминалы сбора данных: ТСД отдаёт коды DataMatrix в Rust-сервис приёмки, документ собирается из отсканированных кодов и сверяется с электронной накладной поставщика по составу коробов. Обмен с системой маркировки вынесен отдельным сервисом за RabbitMQ, потому что внешняя система отвечает не сразу и бывает недоступна, а кладовщик не должен ждать её ответа у терминала; отправка идёт с ключом идемпотентности по документу, и повтор после обрыва связи распознаётся по этому ключу вместо повторного выбытия. Коды, документы и статусы обработки лежат в PostgreSQL, состояние сессии сканирования — в Redis, поэтому прерванная приёмка продолжается с того же короба. Статус обработки возвращается в документ учётной системы через HTTP-сервисы 1С и виден кладовщику прямо на терминале.
Приёмка субстанций с карантином и входным контролем
Задача, решение и что передали
Задача
Статус партии субстанции жил в бумажном журнале, из-за чего решение лаборатории и фактическое размещение на складе расходились.
Решение
Приёмку разложили на статусы — карантин, отбор проб, разрешено, отклонено — и каждый переход закрепили за ролью. Складская программа не выдаёт паллету в производство, пока по её партии нет решения лаборатории входного контроля; отбор проб печатает этикетки с номером партии и местом хранения. Каждый переход статуса пишется в журнал с пользователем, временем и основанием.
Сдано 3
Передали статусную модель приёмки
Матрицу прав по переходам
Формы актов входного контроля и журнал аудита переходов
Как делали
Обследование приёмки Прошли приёмку с кладовщиком и лабораторией входного контроля, разобрали бумажный журнал статусов и акты. Нашли переходы, на которых решение лаборатории расходилось с фактическим размещением паллеты на складе.
Архитектура статусов Приёмку описали статусной моделью — карантин, отбор проб, разрешено, отклонено — и закрепили каждый переход за ролью. Запрет на выдачу в производство до решения лаборатории сделали проверкой в складской программе, а не устной договорённостью; каждый переход пишется в журнал с пользователем, временем и основанием.
Разработка переходов Сначала статусная модель и матрица прав по переходам, затем печать этикеток отбора проб с номером партии и местом хранения, затем формы актов входного контроля и журнал аудита. Отрабатывали на стенде под ролями кладовщика, лаборанта и уполномоченного лица.
Проверка на партиях Пробовали выдать в производство паллету со статусом карантина и провести переход из-под чужой роли. Провалом считали любую выдачу партии субстанции без решения лаборатории и переход статуса, не оставивший записи с пользователем и основанием.
Передача и обучение Передали статусную модель приёмки, матрицу прав по переходам, формы актов входного контроля и журнал аудита, обучили кладовщиков и лабораторию. Уполномоченное лицо само правит права по переходам и поднимает историю партии по журналу.
Архитектура
Переходы приёмки — карантин, отбор проб, разрешено, отклонено — описаны декларативно в машине состояний, право на переход проверяется по роли из Keycloak, а не по флагу в экранной форме. Складской модуль перед выдачей паллеты в производство спрашивает сервис статусов синхронным вызовом: решение лаборатории проверяется в одном месте, поэтому правило не расходится между складом и приёмкой при доработках. Журнал переходов ведётся append-only таблицей в PostgreSQL, каждая запись несёт пользователя, время, основание и хеш предыдущей записи — журнал должен выдерживать проверку целостности при инспекции. Этикетки проб печатаются ZPL-потоком через сервис печати, обмен с учётным контуром идёт через RabbitMQ.
Стек
Java · Spring Boot · PostgreSQL · RabbitMQ · Docker · 1С:ERP · Keycloak · Spring State Machine · Zebra ZPL
Планирование смен отделений с табелем рабочего времени
Задача, решение и что передали
Задача
График дежурств собирали в таблице на бумаге, а подмену на выходные искали обзвоном по отделению.
Решение
В систему завели должности, сертификаты и допуски, нормы часов и правила отдыха между сменами. Планировщик показывает конфликт прямо при постановке в смену — превышение нормы, отсутствие допуска, наложение на отпуск — и не даёт сохранить такой график. Утверждённый график разворачивается в табель, а фактические замены пишутся в него на дату, без переписывания месяца.
Сдано 3
Передали правила норм и допусков
Шаблоны графиков по отделениям
Форму табеля и журнал замен
Как делали
Обследование графиков Со старшими медсёстрами и отделом кадров разобрали бумажные графики дежурств и порядок поиска подмены обзвоном. Собрали должности, сертификаты и допуски, выписали нормы часов и правила отдыха между сменами.
Архитектура правил Правила норм и допусков вынесли в настройку, чтобы кадры меняли их сами, а конфликт решили показывать прямо при постановке в смену и запрещать сохранение такого графика. Табель разворачивается из утверждённого графика, а замена пишется на дату, без переписывания всего графика.
Разработка планировщика Сначала справочники должностей, допусков и норм, затем планировщик с проверками — превышение нормы, отсутствие допуска, наложение на отпуск, — затем разворачивание в табель, журнал замен и уведомления сотрудникам.
Проверка на графиках Пересобрали в системе уже отработанные графики отделений и сверили полученный табель с расчётом кадров. Не приняли бы работу, если бы сохранился график с превышением нормы или сменой без допуска либо табель разошёлся с фактическими заменами.
Передача отделениям Передали правила норм и допусков, шаблоны графиков по отделениям, форму табеля и журнал замен, обучили старших медсестёр. Отделение само ставит график, проводит замену и отдаёт табель в расчёт.
Архитектура
Сервис на Axum держит должности, сертификаты, допуски, нормы часов и правила отдыха в PostgreSQL; проверки конфликтов вынесены в таблицу правил и применяются общим движком, а не зашиты в форму планировщика, — состав ограничений меняется приказом по учреждению и правится конфигурацией. Постановка в смену выполняется транзакцией с exclusion-ограничением по диапазону времени на сотрудника, поэтому пересечение смен не сохранится даже при параллельной правке графика двумя планировщиками. Утверждённый график разворачивается фоновой задачей в табель, замены пишутся в него на дату отдельными строками, без переписывания месяца. Выгрузка табеля уходит в 1С:ЗУП очередью, уведомления о замене — через шлюз мессенджера.
Кабинет дистрибьютора с документами качества и отзывом серий
Задача, решение и что передали
Задача
Декларации и протоколы на серию высылаются по запросу почтой, а отзыв серии доводится до получателей звонками и письмами.
Решение
Кабинет связывает отгрузку с партией и серией из учётной системы, документы качества лежат в объектном хранилище и открываются из строки отгрузки. Отзыв серии отмечается в реестре и разворачивается по цепочке отгрузок: серия помечается в кабинете каждого получателя, факт ознакомления фиксируется. Доступ к документам ограничен фактическими поставками контрагента.
Сдано 4
Кабинет дистрибьютора
Хранилище документов качества
Реестр отзыва серий
Журнал ознакомления
Как делали
Обследование отгрузок Разобрали с отделом качества и логистикой, как высылаются декларации и протоколы на серию и как отзыв доводится до получателей звонками и письмами. Посмотрели, чем в учётной системе связаны отгрузка, партия и серия.
Архитектура доступа Документы качества положили в объектное хранилище и решили открывать из строки отгрузки, ограничив доступ фактическими поставками контрагента. Отзыв серии сделали разворачиваемым по цепочке отгрузок: серия помечается в кабинете каждого получателя, факт ознакомления фиксируется.
Разработка кабинета Сначала связь отгрузки с партией и серией и загрузка документов качества, затем кабинет с правами по фактическим поставкам, затем реестр отзыва серий, оповещение получателей и журнал ознакомления.
Проверка отзыва серии Объявляли отзыв на стенде по сериям с разветвлённой цепочкой отгрузок и проверяли выдачу под учётными записями разных контрагентов. Провалом считали получателя серии, не отмеченного отзывом, и доступ контрагента к документам по серии, которую ему не отгружали.
Передача отделу качества Передали кабинет дистрибьютора, хранилище документов качества, реестр отзыва серий, журнал ознакомления и регламент оповещения получателей. Отдел качества сам объявляет отзыв и видит, кто из получателей ознакомился.
Архитектура
Кабинет на Spring Boot связывает отгрузку с партией и серией по данным учётной системы, документы качества лежат в MinIO и отдаются пресигнед-ссылкой с коротким сроком жизни — файл не проксируется приложением. Доступ строится не ролью, а фактом поставки: запрос документов всегда идёт соединением с отгрузками контрагента, поэтому прямой ссылкой на чужую серию документ не достать. Отзыв серии публикуется событием в шину, обработчик разворачивает цепочку отгрузок и ставит пометку в кабинете каждого получателя — рассылка сделана событием, а не разовым обходом списка, чтобы пометка появилась и у получателей, добавленных после начала отзыва. Факт ознакомления пишется отдельной записью с пользователем и временем, вход — через Keycloak.
Портирование системы учёта производственных партий под доверенную ОС
Задача, решение и что передали
Задача
Учёт производственных серий работал на Windows-серверах, при этом переход требовал сохранить валидированный порядок изменений.
Решение
Прикладной сервер и клиент собраны под доверенную ОС, драйверы весов и принтеров этикеток заменены поддерживаемыми в Linux, обмен с оборудованием проверен на стенде-двойнике линии. Сборка сделана воспроизводимой: одна ревизия исходников даёт одинаковый пакет с зафиксированным перечнем зависимостей. Для квалификации подготовлены сценарии установки и проверки с протоколированием каждого шага.
Сдано 4
Переданы пакеты сервера и клиента
Сценарии квалификации установки
Протокол проверки на стенде-двойнике
Перечень зависимостей сборки
Как делали
Обследование линии Разобрали, чем прикладной сервер и клиент связаны с Windows, и составили перечень весов и принтеров этикеток на линии. Изучили валидированный порядок изменений, который требовалось сохранить при переходе.
Архитектура сборки Сборку сделали воспроизводимой — одна ревизия исходников даёт одинаковый пакет с зафиксированным перечнем зависимостей, — потому что этого требует квалификация. Драйверы весов и принтеров этикеток заменили поддерживаемыми в Linux, обмен с оборудованием решили отрабатывать на стенде-двойнике линии.
Разработка пакетов Сначала сборка сервера и клиента под доверенную ОС, затем работа с весами и печатью этикеток на стенде-двойнике, затем сценарии установки и проверки с протоколированием каждого шага.
Проверка на стенде Прогоняли квалификацию установки и работы на стенде-двойнике с реальными весами и принтерами, повторяя сборку из одной ревизии. Провалом считали расхождение пакетов при повторной сборке, шаг проверки без протокола и этикетку серии, отличающуюся от утверждённой формы.
Передача службе качества Передали пакеты сервера и клиента, сценарии квалификации установки, протокол проверки на стенде-двойнике и перечень зависимостей сборки. Служба качества сама проводит квалификацию при переустановке, ИТ собирает пакет из ревизии.
Архитектура
Прикладной сервер переведён на .NET под Linux, клиент собран под доверенную ОС; весы опрашиваются собственным адаптером по последовательному порту, этикетки печатаются ZPL-потоком через CUPS вместо прежних драйверов. Обмен с оборудованием прогнан на стенде-двойнике линии, а не на работающей линии, потому что валидированный порядок изменений требует зафиксированного протокола проверки до переноса. Сборка сделана воспроизводимой: зафиксированные версии зависимостей и фиксированное время сборки дают из одной ревизии одинаковый пакет, а его хеш входит в протокол квалификации установки. Данные серий и переходы статусов лежат в PostgreSQL, каждая операция пишется в аудит-журнал с пользователем и временем, обмен с соседними системами идёт через RabbitMQ.
Стек
C# · ASP.NET Core · PostgreSQL · RabbitMQ · Docker · Astra Linux · CUPS · Zebra ZPL · RS-232
Обезличивание запросов к языковой модели в медицинской системе
Задача, решение и что передали
Задача
Врач вставлял в запрос кусок карты целиком, и персональные данные пациента уходили в модель и оседали в логах.
Решение
Поставили перед моделью шлюз: он находит в тексте ФИО, полис, телефон и номер карты, заменяет их псевдонимами и хранит соответствие в отдельном защищённом справочнике. Обратная подстановка выполняется только для пользователя с правом на карту, в сервис модели и в журнал уходит обезличенный текст. Разбор инцидента ведётся по псевдониму, а не по исходным реквизитам.
Сдано 4
Шлюз обезличивания
Словарь типов персональных данных
Журнал в псевдонимах
Регламент обратной подстановки
Как делали
Обследование запросов Посмотрели, что врачи реально вставляют в запрос, и разобрали журналы сервиса. Выписали, какие реквизиты пациента в них оседают: ФИО, номер полиса, телефон, номер карты.
Архитектура шлюза Перед моделью поставили шлюз, а соответствие псевдонима и исходного значения решили хранить в отдельном защищённом справочнике, а не в журнале сервиса. Обратная подстановка выполняется только для пользователя с правом на карту, в сервис модели и в журнал уходит обезличенный текст.
Разработка обезличивания Сначала словарь типов персональных данных и распознавание реквизитов в тексте, затем подстановка псевдонимов и хранилище соответствий, затем обратная подстановка по правам и журналирование в псевдонимах.
Проверка на выписках Прогоняли через шлюз выписки и записи приёма с реквизитами в разных написаниях и сокращениях. Провалом считали любой пропущенный реквизит пациента в тексте, ушедшем в модель или в журнал, и обратную подстановку пользователю без права на карту.
Передача службе защиты Передали шлюз обезличивания, словарь типов персональных данных, журнал в псевдонимах и регламент обратной подстановки. Служба защиты информации сама дополняет словарь и ведёт разбор инцидента по псевдониму.
Архитектура
Перед сервисом модели стоит шлюз на FastAPI: он находит в тексте ФИО, номер полиса, телефон и номер карты моделью spaCy и правилами на регулярных выражениях, заменяет найденное псевдонимами и передаёт дальше уже обезличенный текст. Соответствие «псевдоним — значение» лежит в отдельном хранилище, ключ шифрования выдаёт Vault, и ни сервис модели, ни сборщик логов доступа к нему не имеют — разнесение хранилищ означает, что выгрузка журналов не восстанавливает персональные данные. Обратная подстановка выполняется тем же шлюзом на ответе и только для пользователя с правом на карту, проверяемым по токену. Шлюз развёрнут в Kubernetes рядом с сервисом модели, вызовы идут по gRPC, разбор инцидентов ведётся по псевдониму, трассировка — OpenTelemetry.
Защита агента от скрытых инструкций во входящих документах
Задача, решение и что передали
Задача
Агент разбирал письма и вложения поставщиков и выполнял всё, что находил в тексте, включая строки, адресованные ему самим документом.
Решение
Развели содержимое и инструкцию: текст вложения приходит в модель отдельным блоком с пометкой источника и не может расширить список разрешённых действий. Набор инструментов задан белым списком под роль агента, вызов вне списка отклоняется на шлюзе и пишется в журнал. Стенд гоняет коллекцию заражённых вложений при каждом изменении промпта или набора инструментов.
Сдано 4
Белый список инструментов агента
Коллекция заражённых вложений
Журнал отклонённых вызовов
Регламент разбора срабатываний
Как делали
Обследование вложений Разобрали письма и вложения поставщиков, которые обрабатывает агент, и нашли случаи, когда он выполнял строки, адресованные ему самим документом. Выписали, какие действия агенту действительно нужны по его роли.
Архитектура ограничений Содержимое и инструкцию развели: текст вложения приходит в модель отдельным блоком с пометкой источника и не может расширить список разрешённых действий. Набор инструментов задали белым списком под роль, а проверку вызова вынесли на шлюз, а не оставили в тексте промпта.
Разработка шлюза Сначала белый список инструментов и отклонение вызовов вне списка с записью в журнал, затем разделение блоков содержимого и инструкции, затем стенд с коллекцией заражённых вложений в сборочном конвейере.
Проверка на коллекции Гоняли коллекцию вложений со скрытыми инструкциями — в основном тексте, в примечаниях и в подписях к таблицам. Провалом считали любой вызов инструмента вне белого списка и любое действие агента, вызванное строкой из документа, а не задачей пользователя.
Передача команде заказчика Передали белый список инструментов агента, коллекцию заражённых вложений, журнал отклонённых вызовов и регламент разбора срабатываний. Команда заказчика сама пополняет коллекцию и правит белый список при новой роли агента.
Архитектура
Между агентом и инструментами стоит шлюз на C++, принимающий вызовы по gRPC: каждый вызов сверяется с белым списком под роль агента, решение выносит движок политик OPA по правилам Rego, отклонённый вызов пишется в Elasticsearch вместе с исходным запросом. Содержимое вложения приходит в модель отдельным блоком с пометкой источника и не попадает в системную часть промпта — список разрешённых инструментов задан вне модели, поэтому строка внутри документа не может его расширить. Инференс вынесен отдельным сервисом на vLLM, шлюз обращается к нему по сети и не держит веса в своём процессе, так что модель меняется без пересборки шлюза. Регрессионный стенд прогоняет коллекцию заражённых вложений при каждом изменении промпта или набора инструментов, задания раздаются через Kafka.
Оценка готовности интеграций лабораторной и производственной систем
Задача, решение и что передали
Задача
Планы по аналитике упирались в вопрос, отдают ли лабораторная и производственная системы данные наружу, и ответ у каждого вендора был свой.
Решение
Проверили каждую систему на месте: какой интерфейс доступен, в каком формате отдаются данные, с какой частотой и что происходит при обрыве. Замерили на стенде реальную выгрузку по серии, зафиксировали ограничения версии и требования к валидации изменений. Для каждой связки описали, что можно забрать сейчас, а что требует доработки на стороне вендора.
Сдано 4
Паспорт интеграций по системам
Протокол стендовых замеров
Перечень ограничений версий
План доработок у вендоров
Как делали
Осмотр систем Проверили лабораторную и производственную системы на месте: какой интерфейс доступен, в каком формате отдаются данные, с какой частотой и что происходит при обрыве. Расспросили ответственных за LIMS и MES и подняли документацию установленной версии, а не только обещания вендора.
Паспорт связок Решили описывать каждую связку отдельным паспортом — интерфейс, формат, частота, поведение при обрыве — и отделить то, что можно забрать на текущей версии, от того, что требует доработки на стороне вендора. Выгрузку по серии договорились мерить на стенде, не трогая рабочие системы.
Стендовые выгрузки Подняли стенд с копиями интерфейсов и собрали пробные выгрузки по серии из лабораторной и производственной систем. Записали протокол замеров вместе с ограничениями версии и требованиями к валидации изменений.
Замеры с обрывом Повторили выгрузки с разрывом связи и с повторной отправкой одной и той же серии, сверив полученное с записями в системах. Связку не признавали пригодной, если после обрыва выгрузка по серии приходила неполной или задваивалась и восстановить её штатным способом не удавалось.
Передача паспорта Передали паспорт интеграций по системам, протокол стендовых замеров, перечень ограничений версий и план доработок у вендоров. Разобрали с ИТ и обеспечением качества, какие пункты выносить вендорам, а что менять на своей стороне.
Архитектура
Паспорт интеграций ведётся в кабинете на Symfony, а замеры делает отдельный пробник — консольная команда, которую воркер запускает на стенде рядом с системой, забирая задание из RabbitMQ. Пробник ходит в каждую систему тем интерфейсом, который она отдаёт: REST лабораторной системы, SOAP-выгрузка, файловый обмен по серии, OPC UA на стороне производственной; замеряется частота, объём выгрузки по серии и поведение при обрыве. Разнесение кабинета и пробника сделано намеренно: производственный сегмент закрыт и маршрута из кабинета туда нет, а очередь разводит их ещё и по времени — часть систем отдаёт данные только в своё окно. Сырые ответы и стендовые выгрузки складываются в MinIO как доказательство замера, ограничения версий и перечень доработок у вендоров — в PostgreSQL, Redis держит состояние идущих замеров, всё поднимается в Docker.
Обследование данных медицинской карты перед моделью риска
Задача, решение и что передали
Задача
Записи о пациенте копились в карте свободным текстом, и было неясно, хватает ли структурированных полей, чтобы вообще что-то считать.
Решение
Выгрузили обезличенный срез карт и померили заполненность структурированных полей, долю диагнозов вне справочника и долю сведений, живущих только в тексте приёма. Проверили, сохраняется ли связь эпизодов пациента после обезличивания и хватает ли глубины наблюдения, чтобы увидеть исход. Описали, какие поля нужно перевести в справочные значения до обучения модели.
Сдано 4
Отчёт заполненности полей
Оценка глубины наблюдения
Схема обезличивания и связи эпизодов
Перечень полей под справочники
Как делали
Срез карт Выгрузили обезличенный срез карт и померили заполненность структурированных полей, долю диагнозов вне справочника и долю сведений, живущих только в тексте приёма. Разобрали с врачами, что они действительно заносят в поля, а что пишут свободным текстом.
Схема обезличивания Решили работать только с обезличенным срезом и проверять, сохраняется ли связь эпизодов пациента после обезличивания. Поля, нужные модели на входе, договорились переводить в справочные значения на стороне карты, а не вытаскивать разбором текста приёма.
Сборка расчётов Собрали выгрузку и схему обезличивания со связью эпизодов, затем расчёт заполненности полей и оценку глубины наблюдения — хватает ли её, чтобы увидеть исход. Последним свели перечень полей, которые нужно перевести под справочники.
Проверка связей Проверили на выгрузке, что эпизоды одного пациента остаются связанными, а прямые идентифицирующие сведения не восстанавливаются, и сверили посчитанную заполненность с ручным разбором отобранных карт. Срез не приняли бы, если по нему восстанавливается пациент или эпизоды рассыпаются и исход перестаёт прослеживаться.
Передача отчёта Передали отчёт заполненности полей, оценку глубины наблюдения, схему обезличивания и связи эпизодов и перечень полей под справочники. Разобрали с врачами и медицинской статистикой, какие поля переводить в справочные значения до обучения модели.
Архитектура
Обезличенный срез выгружается из МИС ресурсами HL7 FHIR, деидентификатор написан на C++ и проходит выгрузку одним потоком: идентификатор пациента заменяется детерминированным псевдонимом на HMAC с солью, а таблица соответствия хранится отдельно от среза и в профилирование не попадает. Псевдоним считается детерминированно, а не случайно, именно ради связи эпизодов — со случайным ключом глубину наблюдения и исход по карте не собрать. Профилирующий сервис отдаёт по gRPC заполненность структурированных полей, долю диагнозов вне МКБ-10 и долю сведений, живущих только в тексте приёма; колонки читаются через Apache Arrow, срез лежит в PostgreSQL, исходные выгрузки — в MinIO. Отчёты и перечень полей под справочники собираются в Jupyter поверх того же среза, всё поднимается в Docker внутри контура, где лежит выгрузка.
Подсказки регистратуре по протоколам при записи на приём
Задача, решение и что передали
Задача
Оператор записи держал в голове, к какому специалисту нужно направление, какие исследования требуют подготовки и что нельзя ставить в один день, и уточнял это у старшей медсестры прямо при пациенте.
Решение
Агент читает карточку обращения и текст разговора: по названной жалобе подсказывается профиль специалиста и показываются свободные талоны, приём по направлению и приём по договору разделены. Рядом с выбранной услугой всплывают правила подготовки к исследованию, ограничения по возрасту и процедуры, несовместимые в один день, каждая подсказка приходит с пунктом внутреннего протокола. Запись остаётся действием оператора: показ подсказки и её источник пишутся в журнал, а случаи с отклонением собираются для пересмотра протокола.
Сдано 4
Сервис подсказок
Разметка внутренних протоколов по услугам
Правила совместимости процедур в один день
Журнал показов подсказок
Как делали
Разбор записи Послушали, как оператор ведёт запись и что уточняет у старшей медсестры прямо при пациенте: к какому специалисту нужно направление, какие исследования требуют подготовки, что нельзя ставить в один день. Разобрали внутренние протоколы по услугам и увидели, что из них живёт только в голове у смены.
Разметка протоколов Решили разметить внутренние протоколы по услугам и показывать подсказку рядом с выбранной услугой, каждую — с пунктом протокола. Приём по направлению и приём по договору развели, а запись оставили действием оператора: агент подсказывает, но сам не записывает.
Сборка подсказок Сделали чтение карточки обращения и текста разговора с подсказкой профиля специалиста по названной жалобе и показом свободных талонов. Затем собрали разметку протоколов, правила совместимости процедур в один день и ограничения по возрасту, затем журнал показов подсказок с источником.
Сверка с протоколами Прогнали записанные обращения и сверили подсказки со старшей медсестрой и текстом протоколов, отдельно проверяли пары несовместимых процедур и ограничения по возрасту. Провалом считали подсказку без пункта внутреннего протокола и пропущенную пару процедур, которые протокол запрещает ставить в один день.
Передача регистратуре Передали сервис подсказок, разметку внутренних протоколов по услугам, правила совместимости процедур в один день, журнал показов подсказок и инструкцию оператора регистратуры. Показали, как собирать случаи с отклонением для пересмотра протокола и как менять разметку после его правки.
Архитектура
Рабочее место оператора держит WebSocket-соединение с сервисом на Go: подсказка приходит по мере разбора карточки обращения и реплик разговора, опрос по таймеру здесь не годится — совет нужен, пока пациент на линии. Размеченные внутренние протоколы лежат в OpenSearch, правила совместимости процедур в один день и ограничения по возрасту — таблицей в PostgreSQL, свободные талоны и разделение приёма по направлению и по договору берутся из медицинской системы обменом в формате HL7 FHIR. Сервис ничего не записывает в расписание: он отдаёт подсказку с пунктом протокола, а запись делает оператор — журнал показов ведётся отдельно от записи на приём, чтобы отклонения разбирались по тому, что оператору было показано, а не по итогу записи. Роли операторов приходят из Keycloak, состояние сессии и подобранные подсказки кэшируются в Redis, сервис поставляется в Docker.
Стек
Go · chi · PostgreSQL · Redis · WebSocket · Docker · OpenSearch · HL7 FHIR · Keycloak
Кабинет пациента с направлениями, результатами и листом назначений
Задача, решение и что передали
Задача
Результаты исследований пациент забирал на регистратуре, а на повторный приём по направлению записывался только по телефону.
Решение
Кабинет показывает протокол приёма, лист назначений и направления из медицинской информационной системы; результат лаборатории приходит по номеру направления и встаёт рядом с ним. Запись на приём идёт по свободным талонам расписания врача и проверяет действующее направление на консультацию. Информированное согласие подписывается простой электронной подписью и подшивается к эпизоду обслуживания, бумажный бланк на регистратуре не заводится.
Сдано 4
Кабинет пациента
Обмен с медицинской системой по направлениям и результатам
Запись на приём по талонам
Подписание согласий
Как делали
Обследование маршрута пациента Прошли путь от приёма до получения результата на регистратуре и запись на повторный приём по телефону. Разобрали в медицинской информационной системе, где лежат протокол приёма, лист назначений и направления, как результат лаборатории связывается с номером направления и как оформляется информированное согласие.
Архитектура кабинета Кабинет читает данные из медицинской системы и не заводит свою копию карты: результат приходит по номеру направления и встаёт рядом с ним. Запись идёт по свободным талонам расписания врача с проверкой действующего направления на консультацию, а согласие подписывается простой электронной подписью и подшивается к эпизоду обслуживания.
Сборка обмена Сначала обмен с медицинской системой по направлениям, протоколам и результатам, потом кабинет с листом назначений, потом запись по талонам и подписание согласий. На стенде прогоняли случаи, когда результат приходит без номера направления или направление уже недействующее.
Проверка эпизодов Содержимое кабинета сверили с медицинской системой по одним и тем же эпизодам обслуживания и прогнали запись на приём по расписанию врачей. Провалом считались результат исследования, показанный не тому пациенту или в отрыве от своего направления, и запись, занявшая уже занятый талон; не приняли бы и подписанное согласие, не подшитое к эпизоду.
Передача регистратуре Передали кабинет пациента, обмен с медицинской системой по направлениям и результатам, запись на приём по талонам, подписание согласий и инструкцию регистратуры. Регистратура ведёт расписание и разбирает обращения по инструкции, врач работает в своей системе как прежде.
Архитектура
Бэкенд на NestJS общается с медицинской информационной системой ресурсами HL7 FHIR: направления, протоколы приёма и лист назначений читаются запросами, результат лаборатории приходит вебхуком и связывается с направлением по его идентификатору. В кабинете лежит проекция с минимальным набором полей в PostgreSQL, а первоисточник остаётся в медицинской системе — иначе пришлось бы держать вторую копию медицинских данных и постоянно сводить расхождения. Запись на приём идёт по свободным талонам расписания, талон удерживается транзакцией с уникальным ограничением на врача и время, поэтому два пациента одно время не займут, а действующее направление проверяется тем же запросом. Согласие подписывается простой электронной подписью, запись подписи с хешем документа подшивается к эпизоду обслуживания; вход через Keycloak, фронт на React, обмен с системой идёт очередью RabbitMQ, сервисы развёрнуты в Kubernetes.
Перенос лабораторной системы с MS SQL на PostgreSQL
Задача, решение и что передали
Задача
Журнал регистрации проб, результаты по аттестованным методикам и протоколы испытаний хранились в MS SQL на сервере под Windows Server.
Решение
Схема и данные перенесены с сохранением шифров проб и сквозной нумерации протоколов испытаний, расчёты по аттестованным методикам переписаны на PL/pgSQL. Сбор с приборов вынесен в отдельный шлюз на Linux, чтобы закрытый драйвер не тянул за собой прежнюю ОС. Серия проб проведена параллельно в двух контурах, результаты и записи о поверке средств измерений сверены.
Сдано 4
Перенесённая схема и данные
Шлюз сбора с приборов
Журнал сверки результатов
Процедура возврата
Как делали
Обследование базы Разобрали схему MS SQL — таблицы журнала регистрации проб, протоколов испытаний, справочник аттестованных методик; с лаборантами и инженером по качеству прошли, где формируется шифр пробы и как ведётся сквозная нумерация протоколов. Отдельно описали приборы с закрытыми драйверами и записи о поверке средств измерений.
Архитектура переноса Решили сохранить шифры проб и нумерацию протоколов как есть, а расчёты по аттестованным методикам вынести в PL/pgSQL — чтобы результат считался там же, где хранится, а не в приложении. Сбор с приборов отделили в шлюз на Linux, потому что закрытый драйвер иначе удержал бы прежнюю ОС на рабочих местах.
Перенос и шлюз Сначала перенесли справочники методик и средств измерений, затем журнал проб и протоколы; расчётные процедуры переписывали методика за методикой. Шлюз сбора собрали и обкатали на стенде с подключённым прибором, боевой контур до этого не трогали.
Параллельная серия Серию проб провели одновременно в старом и новом контуре и построчно сверили результаты, шифры проб, номера протоколов и записи о поверке средств измерений. Расхождение хотя бы по одному результату аттестованной методики или разрыв в сквозной нумерации протоколов означали возврат к прежнему контуру.
Передача лаборатории Отдали перенесённую схему с данными, шлюз сбора, журнал сверки и процедуру возврата; на рабочих местах прошли с лаборантами регистрацию пробы и выпуск протокола. Инженер по качеству сам заводит новую методику и сверяет расчёт.
Архитектура
Сбор с приборов вынесен в отдельный шлюз на Go под Linux: он держит соединения с анализаторами, разбирает кадры результатов и кладёт их в RabbitMQ, откуда ядро журнала проб забирает измерения и пишет в PostgreSQL. Шлюз сделан отдельным процессом, потому что закрытый драйвер прибора живёт своим циклом и его перезапуск не должен трогать журнал регистрации проб; сообщение несёт идемпотентный ключ из шифра пробы и номера измерения, поэтому повторная отправка после разрыва канала не заводит второй результат. Расчёты по аттестованным методикам переписаны на PL/pgSQL и выполняются рядом с данными — методика читает несколько таблиц журнала и справочник поверки средств измерений, и вынос расчёта в приложение означал бы вытягивание всей серии на клиент. Схема и данные перенесены pgloader с сохранением шифров проб и сквозной нумерации протоколов, раскатка узлов и процедура возврата к прежнему контуру описаны в Ansible, состояние очереди и шлюза снимает Zabbix.
Стек
Go · chi · PostgreSQL · RabbitMQ · Ansible · PL/pgSQL · pgloader · РЕД ОС · Zabbix
Платформа ассистентов по медицинским документам для продажи клиникам
Задача, решение и что передали
Задача
У стартапа был один демонстрационный ассистент на ноутбуке и ни одного способа подключить второго клиента.
Решение
Построили платформу как продукт: изоляция клиентов по схемам и ключам, конструктор ассистента с базой знаний и правилами выдачи, витрина тарифов со счётчиком обращений, биллинг подписки, кабинет администратора с журналом ответов и жалоб. Инференс вынесен в пул узлов с очередью, квотой на клиента и версионированием промптов.
Сдано 4
Передали платформу
Биллинг подписки
Конструктор ассистентов
Схему изоляции клиентов
Как делали
Разбор демонстрации Посмотрели демонстрационный ассистент на ноутбуке и разобрали с командой, что мешает подключить второго клиента: нет разделения данных, тарифов, счётчиков и кабинета администратора. Выписали, какие медицинские документы клиника загружает в базу знаний и какие правила выдачи ей нужны.
Изоляция клиентов Изоляцию клиник заложили по схемам и ключам, чтобы данные одной клиники не пересекались с другой ни в базе, ни в индексе. Инференс вынесли в пул узлов с очередью и квотой на клиента, а промпты стали версионировать — без этого разбор жалобы на ответ невозможен.
Сборка платформы Собрали конструктор ассистента с базой знаний и правилами выдачи, витрину тарифов со счётчиком обращений, биллинг подписки и кабинет администратора с журналом ответов и жалоб. Пул инференса с очередью и квотами подняли отдельно и обкатали на нескольких тестовых клиниках.
Тестовые клиники Завели тестовые клиники и гоняли их параллельно — загрузка документов, вопросы, исчерпание квоты, выставление счёта по подписке. Появление документа одной клиники в выдаче другой считалось провалом и останавливало подключение, как и расхождение счётчика обращений со счётом.
Передача стартапу Отдали платформу, биллинг подписки, конструктор ассистентов, схему изоляции клиентов и документацию публичного API; с командой прошли подключение клиники и разбор жалобы. Стартап сам заводит клиента, тариф и ассистента.
Архитектура
Ядро на FastAPI держит каждого клиента в отдельной схеме PostgreSQL, ключи и настройки — в реестре арендаторов, эмбеддинги базы знаний — в pgvector той же схемы. Схема на клиента выбрана вместо колонки-дискриминатора потому, что выгрузка и удаление данных клиники при расторжении подписки делаются целиком, а не выборкой по каждой таблице, и ошибка в условии не смешает арендаторов. Инференс вынесен в пул узлов vLLM за очередью RabbitMQ: обращение кладётся с квотой арендатора, поэтому пакетная загрузка одного клиента не занимает весь пул, а в записи обращения фиксируются версия промпта и версия базы знаний. Счётчики обращений считаются в Redis и сводятся в биллинг подписки суточной задачей, кабинет администратора показывает журнал ответов и жалоб, публичный интерфейс описан OpenAPI, узлы разложены в Kubernetes.
Мобильное приложение с бэкендом и агентом разбора выписок
Задача, решение и что передали
Задача
Продукт жил прототипом на одном экране: пользователи слали фотографии выписок в чат, данные переносили руками.
Решение
Собрали продукт целиком: мобильный клиент, бэкенд с профилем и историей наблюдений, хранилище снимков документов, очередь распознавания, кабинет оператора проверки. Агентный конвейер принимает фотографию выписки, определяет тип бланка, вытягивает показатели, приводит их к справочнику единиц и ставит значения на шкалу пользователя. Бланки с низкой уверенностью и незнакомой вёрсткой уходят оператору вместе с исходным снимком и разметкой полей.
Сдано 4
Переданы схема профиля и наблюдений
Справочник показателей и единиц
Очередь ручной проверки
Журнал распознавания бланков
Как делали
Обследование выписок Собрали фотографии выписок, которые пользователи слали в чат прототипа, разложили их по типам бланков и разметили, какие показатели и в каких единицах там встречаются. С врачом-консультантом свели справочник показателей и допустимых единиц и посмотрели, где данные переносили руками.
Архитектура конвейера Снимок документа и распознанные значения разделили: снимки в отдельное хранилище, профиль и история наблюдений в основной базе, распознавание вынесли в очередь за пределы запроса из приложения. Приведение к справочнику единиц сделали отдельным шагом после извлечения, чтобы бланк с незнакомой вёрсткой уходил оператору, а не портил шкалу пользователя.
Сборка продукта Собрали бэкенд с профилем и историей наблюдений, затем мобильный клиент со съёмкой и загрузкой, затем конвейер — определение типа бланка, извлечение показателей, приведение единиц. Кабинет оператора проверки делали последним, на стенде он работал на отложенной очереди с исходным снимком и разметкой полей.
Проверка распознавания Прогоняли размеченный набор снимков — смазанный кадр, съёмка под углом, непривычная вёрстка бланка, показатель в других единицах — и сверяли значения на шкале с ручной разметкой. Провалом считали показатель, поставленный на шкалу в неверных единицах, и бланк с низкой уверенностью, показанный пользователю мимо очереди оператора.
Передача команде Передали схему профиля и наблюдений, справочник показателей и единиц, очередь ручной проверки и журнал распознавания бланков. Команда сама добавляет тип бланка, правит справочник единиц и разбирает очередь через кабинет оператора.
Архитектура
Мобильный клиент получает предподписанную ссылку и заливает снимок прямо в объектное хранилище, бэкенд принимает только ключ объекта, чтобы фотографии не шли через API-узлы. Задача распознавания ставится в Celery, конвейер вызывает инференс отдельным сервисом: у него свой релизный цикл и узлы с GPU, поэтому держать его внутри API-процесса нельзя. Сервис определяет тип бланка, вытягивает показатели через PaddleOCR, приводит их к справочнику единиц и ставит значения на шкалу; профиль, наблюдения и разметка полей пишутся в PostgreSQL, сам снимок остаётся в хранилище по ключу. Низкая уверенность и незнакомая вёрстка отправляют бланк в очередь оператора вместе с исходным снимком, а падение воркера возвращает задачу в очередь по её идентификатору.
Кабинеты врача и пациента в продукте телемедицинского стартапа
Задача, решение и что передали
Задача
У команды был прототип на конструкторе, консультации вели в мессенджере, расписание и оплаты держали в таблицах.
Решение
Построили продукт, который заказчик продаёт клиникам: бэкенд с расписанием и слотами, кабинет врача с картой пациента, протоколом приёма и назначениями, кабинет пациента с записью, видеоприёмом и историей заключений. Мобильное приложение работает на том же API: напоминания, загрузка снимков и выписок, оплата и возврат по подписке на ведение. Разделение по клиникам сделано на уровне арендаторов — свои роли, свой брендинг кабинета, свой тарифный план.
Сдано 4
Передали бэкенд
Кабинеты врача и пациента
Мобильное приложение
Схему арендаторов
Как делали
Обследование прототипа Разобрали прототип на конструкторе, переписку консультаций в мессенджере и таблицы расписания и оплат, поговорили с врачами о приёме и с администратором клиники о записи. Собрали состав карты пациента, протокола приёма и назначения, которые до этого жили свободным текстом.
Архитектура арендаторов Разделение по клиникам заложили арендаторами — свои роли, свой брендинг кабинета, свой тарифный план; ядром сделали расписание и слоты, вокруг них карту пациента, протокол и назначения. Мобильное приложение посадили на тот же API, что и веб-кабинеты, чтобы не расходились правила записи и доступа к заключениям.
Разработка кабинетов Сначала бэкенд с расписанием, слотами и арендаторами, затем кабинет врача с картой, протоколом и назначениями, затем кабинет пациента с записью и историей заключений. Видеоприём, напоминания, загрузку снимков и выписок и оплату с возвратом по подписке на ведение собирали на стенде с тестовой клиникой и подставными пациентами.
Проверка приёмов Проводили сквозные приёмы — запись, видеоприём, протокол, назначение, заключение — на нескольких арендаторах сразу, рвали связь посреди видеоприёма и повторяли оплату. Провалом считали случай, когда врач одной клиники видит пациента другой, и приём, после которого заключение не попадает в историю пациента.
Передача продукта Отдали бэкенд, кабинеты врача и пациента, мобильное приложение, схему арендаторов и документацию API для подключения клиник. Команда заказчика сама заводит новую клинику, настраивает её роли, брендинг и тарифный план и подключает её по документации.
Архитектура
Бэкенд на Ktor обслуживает кабинеты врача и пациента и мобильные приложения одним контрактом, разделение клиник сделано арендаторами: tenant_id несёт каждая таблица, доступ закрыт политиками на уровне строк, брендинг и тарифный план лежат в справочнике арендатора. Слоты расписания защищены уникальным индексом на пару «врач и интервал» — двойная запись отсекается ограничением базы, а не проверкой в коде, где две параллельные записи проходят обе. Видеоприём идёт через SFU на WebRTC, сигналинг — по WebSocket из того же бэкенда, снимки и выписки лежат в MinIO и выдаются подписанной ссылкой с ограниченным сроком. Напоминания, списания и возвраты по подписке снимает фоновый обработчик из RabbitMQ; карты пациентов и протоколы приёма — в PostgreSQL, сессии и срез слотов — в Redis, развёртывание в Kubernetes.
Мобильное приложение и бэкенд с обезличиванием запросов к модели
Задача, решение и что передали
Задача
Прототип отправлял во внешнюю модель снимки направлений и жалобы пользователя как есть, вместе с именем, телефоном, номером полиса и датой рождения.
Решение
Довели продукт до выпуска: приложения на двух платформах, бэкенд с профилями, историей обращений и очередью разбора, слой обезличивания перед вызовом модели и раздельное хранилище связок токенов. Перед отправкой запрос проходит распознавание и замену — имена, телефоны, полисы, адреса и даты уходят токенами, а обратная сборка ответа выполняется уже на устройстве пользователя. Ключи хранилища связок разведены с ключами текстов, каждый вызов модели пишется с составом отправленных полей и версией правил замены.
Сдано 4
Мобильные приложения
Бэкенд
Слой обезличивания с правилами замены
Журнал вызовов модели
Как делали
Обследование прототипа Разобрали прототип и увидели, что во внешнюю модель уходят снимки направлений и жалобы вместе с именем, телефоном, номером полиса и датой рождения. Собрали, какие поля модели действительно нужны для разбора обращения, а какие попадали в запрос заодно.
Архитектура обезличивания Между бэкендом и моделью поставили слой обезличивания: распознавание и замена имён, телефонов, полисов, адресов и дат на токены. Связки токенов увели в отдельное хранилище с ключами, разведёнными с ключами текстов, а обратную сборку ответа перенесли на устройство пользователя, чтобы связка не собиралась на сервере.
Разработка приложений Сначала бэкенд с профилями, историей обращений и очередью разбора, затем правила распознавания и замены и хранилище связок, затем сборка ответа на устройстве. Приложения на двух платформах доводили до выпуска параллельно, каждый вызов модели писали с составом отправленных полей и версией правил замены.
Проверка замены Гоняли обращения с именами, полисами и датами в свободном тексте и на снимках направлений, читали журнал вызовов и смотрели, что именно ушло, пробовали достать связки ключами текстов. Провалом считали персональное поле, ушедшее в модель незамещённым, и ответ, который не собирается обратно на устройстве без обращения к хранилищу связок с сервера.
Передача продукта Отдали мобильные приложения, бэкенд, слой обезличивания с правилами замены, журнал вызовов модели и руководство по обновлению правил. Команда заказчика сама добавляет правило замены под новый тип документа и выкатывает его без пересборки приложений.
Архитектура
Бэкенд на FastAPI ведёт профили, историю обращений и очередь разбора в PostgreSQL, приложения на Kotlin и Swift работают по одному API. Перед вызовом модели запрос проходит слой обезличивания: распознавание сущностей на Presidio с доработанными правилами заменяет имена, телефоны, полисы, адреса и даты токенами, версия правил пишется рядом с записью вызова. Связки токенов лежат в отдельной базе, ключи которой разведены с ключами текстов в Vault, поэтому доступ к одному хранилищу не восстанавливает соответствие токена и человека. Обратная сборка ответа выполняется на устройстве пользователя, так что собранный текст с персональными полями не возникает на сервере ни в одной точке; разбор обращений идёт очередью RabbitMQ, развёртывание — Kubernetes.
Проверяется: модель детекции и очередь спорных кадров
Контроль качества на линии сортировки по снимкам
Задача, решение и что передали
Задача
Сорт и дефекты на линии определяли на глаз, брак всплывал уже в претензии от покупателя.
Решение
Поставили камеры над конвейером со съёмкой по сигналу датчика положения, купольную подсветку, чтобы убрать блики на влажной поверхности, и инференс на месте, рядом с линией. Детектор размечает дефекты на кадре, решение уходит на PLC, отбраковка идёт пневмотолкателем в отдельный лоток. Кадры, где модель не уверена, откладываются в свою очередь и размечаются мастером смены — они же становятся материалом для следующей итерации обучения.
Сдано 4
Модель детекции с описанием классов дефектов
Схема установки камер и света
Интеграция с PLC
Очередь спорных кадров и инструкция по переобучению
Как делали
Наблюдение на линии Встали у конвейера и посмотрели, как сортировщики отличают сорт и дефекты на глаз и при каком свете. Разобрали претензии покупателей, чтобы понять, какие дефекты доходят до отгрузки, и описали условия съёмки: блики на влажной поверхности, ход ленты, положение продукта.
Свет и место инференса Съёмку привязали к сигналу датчика положения, а не к таймеру, чтобы кадр приходился на продукт, и поставили купольную подсветку против бликов. Инференс разместили рядом с линией, а не на сервере: решение уходит на PLC, отбраковка идёт пневмотолкателем в отдельный лоток, а неуверенные кадры откладываются в очередь мастеру.
Обучение детектора Установили камеры и свет, собрали кадры с работающей линии, разметили классы дефектов и обучили детектор, после чего связали его выход с PLC. Очередь спорных кадров и разметку мастером сделали частью контура, а не отдельным инструментом.
Прогон партий Гоняли линию на партиях с заранее разобранным составом и сверяли отбраковку с ручным разбором мастера смены. Провалом считали дефект, прошедший в товарный лоток, и срабатывание толкателя не по тому продукту из-за расхождения кадра с положением на ленте.
Передача смене Передали модель детекции с описанием классов дефектов, схему установки камер и света, интеграцию с PLC, очередь спорных кадров и инструкцию по переобучению. Мастер смены размечает спорные кадры, служба АСУ запускает следующую итерацию обучения сама.
Архитектура
Камеры GigE Vision снимают по срабатыванию датчика положения, кадр сразу уходит в инференс на модуле у линии: детектор собран в TensorRT, решение об отбраковке передаётся на PLC по Modbus TCP, толкатель сбрасывает изделие в отдельный лоток. Инференс стоит на месте, а не на сервере в серверной, потому что решение должно успеть до подхода изделия к толкателю, а перегон кадра по сети в этот промежуток не укладывается. Кадры и решения буферизуются локально в SQLite и досылаются на сервер по MQTT, где PostgreSQL держит статистику смен и очередь спорных кадров — обрыв связи не останавливает линию, накопленное уходит после восстановления. Спорные кадры мастер размечает в веб-форме на FastAPI, размеченная выборка становится материалом следующей итерации обучения, версия модели на модуле меняется контейнером с фиксированным тегом.
Проверяется: схема сетевых политик и панель мониторинга
Кластер языковых моделей на собственных серверах с разграничением прав
Задача, решение и что передали
Задача
Инженеры и технические писатели пользовались внешними сервисами, копируя туда фрагменты конструкторской документации, и проследить, что именно ушло наружу, было нельзя.
Решение
Развернули инференс на собственных GPU-серверах: vLLM за общим шлюзом, очередь запросов с приоритетами и ограничением длины контекста, выгрузка неиспользуемых моделей из памяти. Доступ привязали к корпоративному каталогу — группа пользователя определяет доступный набор моделей и предельный размер запроса, трафик наружу закрыт сетевой политикой, обращения ходят только внутри сегмента. Отдельно собрали панель эксплуатации с состоянием узлов, длиной очереди, занятой памятью и температурой карт.
Сдано 4
Передали развёрнутый кластер с моделями
Шлюз с ролевой моделью
Схему сетевых политик
Инструкцию по добавлению новых моделей и панель мониторинга
Как делали
Куда уходило Собрали, какие фрагменты конструкторской документации инженеры и технические писатели копировали во внешние сервисы и в каких задачах. Посчитали профиль обращений и осмотрели имеющиеся GPU-серверы: сколько узлов, какие карты и сколько памяти на карту.
Шлюз и группы Инференс подняли на собственных серверах за общим шлюзом с очередью запросов по приоритетам и ограничением длины контекста, неиспользуемые модели выгружаются из памяти. Доступ привязали к корпоративному каталогу — группа пользователя определяет набор моделей и предельный размер запроса, а трафик наружу закрыли сетевой политикой.
Сборка кластера Подняли узлы инференса и шлюз, затем очередь с приоритетами и привязку прав к каталогу, затем сетевые политики и панель эксплуатации с состоянием узлов, длиной очереди, занятой памятью и температурой карт.
Нагрузка и права Нагружали кластер одновременными запросами от разных групп и смотрели поведение при выпадении узла и при переполнении памяти. Провалом считали доступ к модели, не положенной группе пользователя, любое исходящее соединение из сегмента наружу и запрос, проходящий мимо ограничения длины контекста.
Передача администраторам Передали развёрнутый кластер с моделями, шлюз с ролевой моделью, схему сетевых политик, инструкцию по добавлению новых моделей и панель мониторинга. Администраторы добавляют модели и правят состав групп сами.
Архитектура
Все обращения идут через один шлюз на FastAPI: он проверяет пользователя в LDAP, по его группе выбирает допустимый набор моделей и предельную длину запроса, ставит задачу в Redis Streams и отдаёт ответ потоком. Инференс поднят подами vLLM на собственных GPU-серверах под Kubernetes, по одной модели на под с закреплённой картой; очередь с приоритетами стоит перед ними, потому что длинные контексты выедают память под KV-кеш, и прямая раздача запросов на карту заканчивалась бы отказами вместо ожидания. Редко используемые модели выгружаются из памяти по таймауту и поднимаются обратно по первому запросу, журнал обращений и роли лежат в PostgreSQL. Выход наружу закрыт сетевыми политиками кластера, обращения ходят только внутри сегмента; состояние узлов, длина очереди, занятая память и температура карт снимаются экспортёрами в Prometheus, при выпадении узла поды переезжают на оставшиеся серверы.
Перевод отчётности с зарубежной BI на отечественную
Задача, решение и что передали
Задача
Показатели по урожайности, складу и себестоимости считались внутри дашбордов зарубежной BI, логика расчёта была размазана по отчётам, а поставка лицензий прекратилась.
Решение
Разобрали выражения из отчётов и вынесли расчёт показателей в слой витрин на стороне СУБД, чтобы одна метрика считалась в одном месте, а BI отвечала только за отображение. Дашборды пересобрали на отечественной платформе, значения сверили с прежними отчётами за закрытые периоды, расхождения разобрали до конкретных формул и фильтров. Обновление витрин повесили на планировщик с контролем задержки данных, доступ к отчётам развели по подразделениям и складам.
Сдано 4
Передали слой витрин с описанием метрик
Пересобранные дашборды
Протоколы сверки показателей
Расписание обновления и матрицу доступа
Как делали
Разбор дашбордов Вытащили выражения расчёта из дашбордов зарубежной BI и увидели, что одна и та же метрика по урожайности, складу и себестоимости считается в разных отчётах по-разному. Выписали, кто какими отчётами пользуется и по каким подразделениям и складам нужен разный доступ.
Метрика в витрине Расчёт показателей вынесли в слой витрин на стороне СУБД, чтобы метрика считалась в одном месте, а BI отвечала только за отображение. Обновление витрин повесили на планировщик с контролем задержки данных, доступ к отчётам развели по подразделениям и складам.
Пересборка отчётности Собрали витрины с описанием метрик, затем пересобрали дашборды на отечественной платформе, затем настроили расписание обновления и матрицу доступа.
Сверка показателей Сверили значения новых дашбордов с прежними отчётами за закрытые периоды и разобрали расхождения до конкретных формул и фильтров. Провалом считали показатель, разошедшийся с прежним отчётом на закрытом периоде без объяснимой причины, и дашборд, показывающий цифры при незавершённом обновлении витрины.
Передача аналитикам Передали слой витрин с описанием метрик, пересобранные дашборды, протоколы сверки показателей, расписание обновления и матрицу доступа. Аналитики добавляют показатели в витрину и собирают отчёты сами.
Архитектура
Расчёт показателей вынесен из отчётов в слой витрин на стороне PostgreSQL: модели dbt собирают метрики по описанию в репозитории, Superset читает готовые таблицы по JDBC и отвечает только за отображение. Так одна метрика считается в одном месте — раньше формула жила в выражении конкретного дашборда, и два отчёта расходились без видимой причины. Обновление витрин ведёт Airflow, у каждой витрины проставляется отметка времени данных, а служба на Ktor сравнивает её с расписанием и поднимает оповещение о задержке, частые срезы держит в Redis. Сверка с прежней BI сделана за закрытые периоды: значения старых отчётов и новых витрин сопоставлены по ключу периода и разреза, расхождения разобраны до формул и фильтров; доступ к дашбордам разведён по подразделениям и складам ролями, узлы подняты контейнерами.
Проверяется: очередь разбора конфликтов и правила сопоставления
Двусторонний обмен 1С и CRM по сделкам и отгрузкам
Задача, решение и что передали
Задача
Контрагенты и сделки заводились дважды — в CRM и в учётной системе, расхождения находили уже при выставлении счетов.
Решение
Развели ответственность за данные: CRM владеет контрагентом и сделкой, 1С — договором, счётом и отгрузкой; обмен идёт через очередь, а не прямыми вызовами между системами. Сопоставление ведём по внутреннему идентификатору и паре ИНН и КПП, конфликтные карточки уходят в очередь ручного разбора вместо тихой перезаписи. Каждое сообщение хранится с версией и телом, повторная обработка не создаёт дубль.
Сдано 4
Передали сервис обмена
Правила сопоставления карточек
Очередь разбора конфликтов
Описание контрактов и инструкцию дежурной смене
Как делали
Сверка карточек Сверили карточки контрагентов и сделок в CRM и в учётной системе и разобрали, как один контрагент заводится дважды. Посмотрели, на каком шаге расхождение всплывает — при выставлении счёта — и по каким признакам менеджеры сами понимают, что карточки об одном и том же.
Владельцы данных Развели ответственность: CRM владеет контрагентом и сделкой, 1С — договором, счётом и отгрузкой; прямые вызовы между системами заменили очередью. Сопоставление ведётся по внутреннему идентификатору и паре ИНН и КПП, а конфликтные карточки уходят в очередь ручного разбора вместо тихой перезаписи.
Сборка обмена Описали контракты сообщений, собрали публикацию и приём с обеих сторон, затем правила сопоставления карточек, затем очередь разбора конфликтов и хранение сообщений с версией и телом.
Проверка на дублях Гоняли обмен на копиях баз, повторяя одно и то же сообщение и заводя карточки с совпадающими ИНН и КПП. Провалом считали дубль контрагента или сделки при повторной обработке и перезапись карточки в обход очереди разбора.
Передача дежурным Передали сервис обмена, правила сопоставления карточек, очередь разбора конфликтов, описание контрактов и инструкцию дежурной смене. Дежурные разбирают конфликты и переотправляют сообщения сами.
Архитектура
Обмен идёт через Kafka, а не прямыми вызовами CRM и 1С друг к другу: у систем разные окна обслуживания, и при прямом вызове недоступность одной останавливала бы работу второй, тогда как в топике сообщение ждёт и дочитывается со смещения. Владение данными закреплено контрактом — CRM публикует контрагента и сделку, 1С через OData отдаёт договор, счёт и отгрузку; сервис обмена на Spring Boot читает оба потока, схемы сообщений описаны в Avro и версионируются в реестре, поэтому добавление поля не ломает потребителя. Сопоставление карточек ведётся по внутреннему идентификатору и паре ИНН и КПП в таблицах PostgreSQL, где хранится и тело каждого сообщения с версией: повторная обработка находит уже применённое сообщение по ключу и не создаёт дубль. Конфликт по реквизитам не перезаписывает карточку, а уходит в очередь ручного разбора; справочники кешируются в Redis, сервис поднят несколькими экземплярами в Kubernetes с разбором партиций по группе консьюмеров, состояние обменов выведено в Grafana.
Проверяется: история изменений показателей и печатные акты
Приёмка сырья с лабораторным контролем и весовым учётом
Задача, решение и что передали
Задача
Приёмку вели в журналах: весовые талоны на бумаге, результаты анализов в отдельной тетради, зачётный вес считали вручную.
Решение
Собрали приёмку от весовой до склада: талон заводится на въезде, вес снимается с автомобильных весов, отбор пробы и показатели качества вносит лаборатория в своей форме, зачётный вес считается по формулам и попадает в акт. Каждое изменение показателя пишется в историю с автором и временем, правка задним числом закрыта — только корректирующей записью со ссылкой на исходную.
Сдано 4
Передали модуль приёмки
Интеграцию с весами
Формы лаборатории
Печатные акты и историю изменений показателей
Как делали
Путь приёмки Прошли приёмку от въезда до склада: бумажный весовой талон, отдельная тетрадь с анализами, ручной расчёт зачётного веса. С лабораторией разобрали, какие показатели берутся по пробе и по каким формулам считается зачётный вес.
Сквозной талон Талон завели как сквозную запись от весовой до склада: вес снимается с автомобильных весов, показатели вносит лаборатория в своей форме, зачётный вес считается по формулам и попадает в акт. Правку задним числом закрыли — только корректирующая запись со ссылкой на исходную, каждое изменение показателя пишется в историю с автором и временем.
Сборка модуля Подключили автомобильные весы и собрали заведение талона на въезде, затем формы лаборатории и расчёт зачётного веса, затем печатные акты и историю изменений. На стенде проверяли поведение при обрыве связи с весами.
Параллельно с журналом Провели приёмку на реальных машинах параллельно с бумажным журналом и сверили зачётный вес по каждому талону. Провалом считали расхождение зачётного веса с ручным расчётом по тем же показателям и возможность изменить показатель без корректирующей записи и следа в истории.
Передача весовой Передали модуль приёмки, интеграцию с весами, формы лаборатории, печатные акты и историю изменений показателей. Весовщики и лаборанты работают каждый в своей форме, а служба АСУ правит формулы расчёта сама.
Архитектура
Приёмка собрана как сервис на NestJS с базой PostgreSQL и рабочими местами весовой, лаборатории и склада на React, а у весов стоит терминал, опрашивающий автомобильные весы по Modbus TCP и публикующий отсчёты в MQTT. Съём веса вынесен в этот терминал, потому что индикатор отдаёт значение непрерывным потоком и фиксировать его нужно по устойчивости показаний — при передаче потока в общий сервис по сети в талон попадал бы случайный отсчёт. Талон, проба, показатели лаборатории и зачётный вес связаны внешними ключами в одной цепочке, формулы расчёта хранятся версиями, история изменения показателя пишется отдельной таблицей с автором и временем. Правка задним числом закрыта правами в базе: исправление вносится только корректирующей записью со ссылкой на исходную, так как акт уже ушёл контрагенту; готовый акт передаётся в 1С через OData, справочники кешируются в Redis, сервисы подняты контейнерами.
Локальная модель по технологическим картам для цеховых терминалов
Задача, решение и что передали
Задача
Параметры плавки уточняли у мастера или в бумажной папке, потому что цеховая сеть отрезана от внешних сервисов.
Решение
Квантовали модель до размера, который держит один серверный узел в цеховой серверной, и подняли индекс технологических карт рядом с ним. Терминалы обращаются к сервису по внутренней сети, ответ приходит с номером карты и пунктом. Обновления карт забираются из системы техдокументации по расписанию, индекс пересобирается инкрементально.
Сдано 4
Передали цеховой узел инференса
Собранный индекс технологических карт
Скрипт инкрементального обновления
Схему сетевых доступов
Как делали
Обследование Посмотрели, как параметры плавки уточняют у мастера и по бумажной папке, разобрали структуру технологических карт и их нумерацию. Померили, что стоит в цеховой серверной и какие доступы разрешены из отрезанной цеховой сети.
Архитектура Модель квантовали до размера, который держит один серверный узел в цеховой серверной, индекс технологических карт подняли рядом, чтобы обращение терминала не выходило за пределы цеха. Обновления карт забираем из системы техдокументации по расписанию и пересобираем индекс инкрементально — от полной пересборки на каждое изменение отказались.
Разработка Сначала подняли узел с квантованной моделью и индексом, затем ответ с номером карты и пунктом, затем инкрементальное обновление из системы техдокументации. На стенде проверяли обращение терминала по внутренней сети до выноса узла в цеховую серверную.
Проверка Задавали вопросы по параметрам плавки из действующих карт и по картам, только что изменённым в системе техдокументации, отвечали с самих цеховых терминалов. Провалом считали ответ без номера карты и пункта и ответ по прежней редакции карты после того, как обновление уже прошло.
Передача Передали цеховой узел инференса, собранный индекс технологических карт, скрипт инкрементального обновления и схему сетевых доступов. Технологам показали, как убедиться, что карта попала в индекс, цеховой службе — инструкцию для терминалов и перезапуск узла.
Архитектура
В цеховой серверной стоит один узел: llama.cpp с квантованной в GGUF моделью, рядом OpenSearch с индексом технологических карт и тонкий Python-сервис, принимающий запросы терминалов по gRPC и собирающий контекст. Квантизация выбрана ради того, чтобы веса целиком помещались в память одного узла: цеховая сеть отрезана от внешних сервисов, и вынести часть счёта некуда. Обновления карт забираются из системы техдокументации по расписанию и пересобирают индекс инкрементально — OpenSearch обновляет документ по идентификатору карты, поэтому полный переиндекс не нужен и терминалы не остаются без поиска на время сборки. Ответ возвращается терминалу с номером карты и пунктом; сервис и индекс подняты контейнерами на одном узле, данные индекса лежат на томе и переживают перезапуск.
Подписной ассистент по эксплуатационной документации сервисных инженеров
Задача, решение и что передали
Задача
На выезд везли распечатки руководств, а уточнение по редкому узлу упиралось в звонок в конструкторский отдел.
Решение
Собрали индекс по руководствам, каталогам узлов и сервисным бюллетеням с привязкой к модели и исполнению машины. Ответ формируется с номером узла, пунктом руководства и фрагментом каталожной схемы; для машины с известным серийным номером выборка сужается до её исполнения. Мобильный клиент держит выданные документы в кэше и продолжает работать при слабом канале.
Сдано 4
Передали индекс документации
Привязку к исполнениям машин
Мобильный клиент с офлайн-кэшем
Правила обновления бюллетеней
Как делали
Обследование Съездили с сервисными инженерами на выезд: везут распечатки руководств, а уточнение по редкому узлу упирается в звонок в конструкторский отдел. Разобрали, как руководства, каталоги узлов и сервисные бюллетени соотносятся с моделью и исполнением машины.
Архитектура Индекс строим с привязкой к модели и исполнению, чтобы по известному серийному номеру выборка сужалась до конкретной машины. Ответ собираем с номером узла, пунктом руководства и фрагментом каталожной схемы, а мобильный клиент держит выданные документы в кэше — слабый канал на площадке не оставляет инженера без документации.
Разработка Сначала индекс по руководствам, каталогам узлов и бюллетеням с привязкой к исполнениям, затем ответ с номером узла и фрагментом схемы, затем мобильный клиент с офлайн-кэшем. На стенде проверяли клиент с обрывами связи до выдачи инженерам.
Проверка Задавали вопросы по редким узлам с серийным номером и без него, гоняли клиент с отключённой сетью, проверяли выдачу после выхода нового сервисного бюллетеня. Провалом считали ответ по руководству чужого исполнения машины и пункт из редакции, отменённой бюллетенем.
Передача Передали индекс документации, привязку к исполнениям машин, мобильный клиент с офлайн-кэшем и правила обновления бюллетеней. Инженерам показали работу в офлайне, службе документации — постановку новой редакции в индекс и статистику запросов.
Архитектура
Серверная часть — C++-сервис на Drogon: он принимает запрос мобильного клиента по REST, определяет исполнение машины по серийному номеру и обращается к индексу руководств, каталогов узлов и сервисных бюллетеней. Индекс лежит в Qdrant рядом с PostgreSQL, где хранятся привязки серийных номеров к исполнениям, и фильтр по исполнению применяется на стороне векторного поиска вместе с запросом — иначе выдача по редкому узлу вымывается документами от других моделей машин. Страницы каталогов и фрагменты схем лежат в S3-совместимом хранилище и отдаются по ссылке с ограниченным сроком, а ответ собирается с номером узла и пунктом руководства. Клиент — PWA: выданные документы и схемы кладутся в кэш service worker, поэтому на выезде со слабым каналом уже открытые материалы остаются доступны, а новые запросы уходят при появлении связи.
Стек
C++ · Drogon · PostgreSQL · Qdrant · OpenAPI · Docker · S3-совместимое хранилище · PWA · LLM API
Сборка коммерческого предложения по присланной спецификации
Задача, решение и что передали
Задача
Предложение инженер собирал вручную: искал позиции в номенклатуре, брал цену из последнего присланного прайса и складывал трудоёмкость по памяти.
Решение
Агент разбирает присланную спецификацию, сопоставляет позиции с номенклатурой по обозначению и типоразмеру, подтягивает действующую цену и нормы времени и считает срок изготовления. Позиция без однозначного соответствия остаётся пустой и подсвечивается инженеру, а предложение не выпускается, пока такие позиции не закрыты. Каждое значение в предложении хранит ссылку на строку прайса и версию норм, по которой посчитано.
Сдано 4
Сервис сопоставления номенклатуры
Шаблон предложения
Справочник норм времени
Очередь ручного сопоставления и журнал выпуска предложений
Как делали
Обследование Разобрали, как инженер собирает предложение: ищет позиции в номенклатуре, берёт цену из последнего присланного прайса и складывает трудоёмкость по памяти. Посмотрели, в каком виде приходят спецификации и как в них пишут обозначения и типоразмеры.
Архитектура Каждое значение предложения связали со строкой прайса и версией норм времени, чтобы посчитанный срок изготовления можно было раскрыть до источника. Позицию без однозначного соответствия не подбираем сами: она остаётся пустой и подсвечивается инженеру, а предложение не выпускается, пока такие позиции не закрыты.
Разработка Сначала разбор присланной спецификации и сопоставление позиций по обозначению и типоразмеру, затем подтягивание действующей цены и норм времени с расчётом срока изготовления, затем очередь ручного сопоставления и шаблон предложения. На стенде собирали предложения по спецификациям из архива.
Проверка Сравнивали собранные предложения с тем, что инженеры выпускали по тем же спецификациям, отдельно проверяли позиции с похожими обозначениями и смену версии норм. Провалом считали позицию, привязанную к чужому типоразмеру, и выпуск предложения с незакрытыми пустыми позициями.
Передача Передали сервис сопоставления номенклатуры, шаблон предложения, справочник норм времени и очередь ручного сопоставления. Инженерам показали закрытие пустых позиций и раскрытие цены до строки прайса, нормировщикам — заведение новой версии норм.
Архитектура
Сервис на Ktor принимает спецификацию файлом, раскладывает её на позиции и сопоставляет с номенклатурой в два прохода: точное совпадение обозначения, затем разбор типоразмера на параметры и подбор по ним. В предложение пишется не само значение, а ссылка на строку прайса и версию справочника норм времени — иначе пересчёт старого предложения после смены прайса даёт другой результат без следа о причине расхождения. Позиции, номенклатура, версии норм и журнал выпуска лежат в PostgreSQL, действующие цены и остатки берутся из учётной системы через HTTP-сервисы 1С, разбор файлов идёт через Apache POI. Позиция без однозначного соответствия остаётся пустой и попадает в очередь ручного сопоставления, а выпуск предложения заблокирован, пока такие позиции не закрыты решением инженера.
Детекция дефектов поверхности проката на линии по кадрам
Задача, решение и что передали
Задача
Плены и вкатанную окалину замечали на выходном контроле, когда рулон был уже смотан и ушёл в партию.
Решение
Линейные камеры снимают полосу на ходу, кадры сшиваются в развёртку рулона по метке длины с энкодера. Сегментационная модель размечает плены, риски и вкатанную окалину и привязывает координату дефекта к метражу полосы. Карта дефектов пишется в паспорт рулона и уходит на пост резки до раскроя.
Сдано 4
Переданы модель сегментации
Размеченный набор кадров линии
Карта дефектов в паспорте рулона
Инструкция оператора поста резки
Как делали
Обследование Посмотрели выходной контроль, где плены и вкатанную окалину замечали на уже смотанном рулоне, разобрали освещение и обзор на линии. Сняли пробные кадры полосы на ходу и сверили метку длины с энкодера с метражом в паспорте рулона.
Архитектура Кадры линейных камер сшиваем в развёртку рулона по метке длины, чтобы координата дефекта считалась в метраже полосы, а не в номере кадра. Взяли сегментацию, а не классификацию кадра — нужны границы дефекта; карта дефектов пишется в паспорт рулона и уходит на пост резки до раскроя.
Разработка Сначала съёмка и сшивка развёртки по энкодеру, затем разметка набора кадров и обучение сегментации на плены, риски и вкатанную окалину, затем запись карты дефектов в паспорт рулона. На стенде гоняли записанные проходы до подключения к линии.
Проверка Сравнивали карту дефектов с заключением выходного контроля по одним и тем же рулонам, отдельно проверяли смещение координаты по метражу на длинных рулонах. Провалом считали пропуск плены, найденной контролем, и дефект, показанный не на своём метраже: по такой карте раскрой на посту резки не приняли бы.
Передача Передали модель сегментации, размеченный набор кадров линии и карту дефектов в паспорте рулона. Операторов поста резки учили читать карту перед раскроем, службе КИП — обслуживать камеры и проверять сшивку по энкодеру.
Архитектура
У линии стоит промышленный узел: кадры линейных камер принимаются по GigE Vision и сшиваются в развёртку рулона по импульсам энкодера длины — координата берётся от энкодера, а не от времени кадра, потому что скорость полосы меняется и по времени привязка к метражу уплывает. Сегментационная модель обучена на PyTorch, а в цехе исполняется в ONNX Runtime на GPU узла отдельным процессом: кадры передаются захватчику и обратно через разделяемую память и ZeroMQ, поэтому захват не простаивает в ожидании разметки. Карта дефектов с координатами по метражу пишется в локальную PostgreSQL и уходит в MES сообщением по паспорту рулона до поста резки. Кадры с дефектами и окно вокруг них складываются в объектное хранилище для пополнения обучающего набора, а при потере связи с MES карта копится локально и досылается по восстановлении.
Вывод модели прогноза отказов станочного парка в эксплуатацию
Задача, решение и что передали
Задача
Модель предсказания отказов жила в ноутбуке инженера и пересчитывалась руками перед совещанием по ремонтам.
Решение
Расчёт вынесен в сервис с расписанием, признаки собираются витриной из архива вибродиагностики и журнала ремонтов. Версии модели и наборы признаков лежат в реестре, каждый прогноз пишется вместе с версией и входными значениями. Порог срабатывания вынесен в конфигурацию, результат уходит заявкой в систему обслуживания.
Сдано 4
Передан комплект эксплуатации: сервис расчёта по расписанию
Реестр версий модели
Витрина признаков
Регламент пересборки и приёмки версии
Как делали
Обследование Разобрали расчёт из ноутбука инженера: какие признаки он считает руками перед совещанием по ремонтам, откуда берёт архив вибродиагностики и журнал ремонтов. Нашли места, где данные правились вручную по ходу.
Архитектура Расчёт вынесли в сервис с расписанием, признаки собираем витриной из архива вибродиагностики и журнала ремонтов, чтобы обучение и расчёт брали одни и те же определения. Версии модели и наборы признаков положили в реестр, каждый прогноз пишем с версией и входными значениями, порог срабатывания вынесли в конфигурацию, а не в код.
Разработка Сначала витрина признаков, затем сервис расчёта по расписанию с записью версии и входных значений, затем передача результата заявкой в систему обслуживания. На стенде считали параллельно с ноутбуком инженера на одних и тех же периодах.
Проверка Сверяли прогнозы сервиса с ручным расчётом на закрытых периодах и прогоняли расчёт при пропусках в вибродиагностике. Провалом считали расхождение с ручным расчётом на одних и тех же входных данных и прогноз, записанный без версии модели и входных значений, — такую версию к приёмке не допустили бы.
Передача Передали комплект эксплуатации: сервис расчёта по расписанию, реестр версий модели, витрину признаков и регламент пересборки и приёмки версии. Службе ремонтов показали работу с заявками, инженеру — смену порога и выкладку новой версии.
Архитектура
Расчёт вынесен из ноутбука в два узла: DAG Airflow по расписанию собирает витрину признаков из архива вибродиагностики и журнала ремонтов, а сервис на FastAPI прогоняет модель, взятую по версии из реестра MLflow. Архив телеметрии лежит в колоночном ClickHouse: выборка одного датчика за длинный интервал читает только свои столбцы, тогда как строчное хранилище поднимало бы запись измерения целиком. Признаки считаются одним кодом и для обучения, и для инференса и материализуются в витрине, иначе расхождение в окне агрегации даёт сдвиг, которого по журналу прогнозов не видно. Каждый прогноз пишется в PostgreSQL вместе с версией модели, версией набора признаков и входными значениями, порог срабатывания вынесен в конфигурацию и меняется без пересборки образа, а превышение уходит заявкой в систему обслуживания через её API.
Контроль укупорки и этикетки на линии розлива по кадрам
Задача, решение и что передали
Задача
Перекос этикетки и недокрученную крышку ловили выборочно на паллетировании, когда упаковка была уже собрана.
Решение
Камеры с импульсной подсветкой снимают бутылку сразу после укупорочного автомата, кадр привязывается к позиции энкодера конвейера. Модель классифицирует наклон крышки, смещение и загиб этикетки, решение отдаётся контроллеру до отсекателя. Пороги и классы брака выведены в интерфейс мастера, спорные кадры складываются в очередь на разметку.
Сдано 4
Переданы модель классификации
Схема расстановки камер и подсветки
Интерфейс мастера с очередью спорных кадров
Инструкция по смене рецептуры
Как делали
Обследование Посмотрели, как перекос этикетки и недокрученную крышку ловят выборочно на паллетировании, и разобрали, где на линии встаёт камера после укупорочного автомата. Сняли пробные кадры бутылки в движении при разной подсветке.
Архитектура Съёмку привязали к позиции энкодера конвейера с импульсной подсветкой, чтобы кадр относился к конкретной бутылке и решение успевало к отсекателю. Наклон крышки, смещение и загиб этикетки развели по классам брака, пороги вывели в интерфейс мастера, а спорные кадры складываем в очередь на разметку, а не выбрасываем.
Разработка Сначала расстановка камер и подсветки со съёмкой сразу после укупорочного автомата, затем обучение классификатора на снятых кадрах, затем выдача решения контроллеру до отсекателя и интерфейс мастера. На стенде прогоняли записанные кадры и проверяли отсечку на холостом ходу линии.
Проверка Гоняли линию с заведомо бракованными бутылками — недокрученная крышка, смещённая и загнутая этикетка, отдельно проверяли смену рецептуры. Провалом считали бракованную бутылку, прошедшую отсекатель, и решение, пришедшее к контроллеру после точки отсечки.
Передача Передали модель классификации, схему расстановки камер и подсветки и интерфейс мастера с очередью спорных кадров. Мастеров учили менять пороги и классы брака, механикам отдали инструкцию по смене рецептуры и юстировке камер.
Архитектура
На линии стоит узел с камерами и импульсной подсветкой: вспышка и захват привязаны к позиции энкодера конвейера, поэтому бутылка после укупорочного автомата попадает в кадр в одной и той же фазе. Кадр проходит предобработку в OpenCV и классификатор в TensorRT на GPU узла — движок собран под конкретную карту, потому что вердикт должен уйти на контроллер до отсекателя, и бюджет на кадр задан шагом конвейера, а не средней нагрузкой. Решение передаётся ПЛК по OPC UA, а отбраковку выполняет контроллер: узел зрения не управляет исполнительным механизмом напрямую, поэтому его отказ переводит линию в режим без отсечки, а не останавливает конвейер. Пороги и классы брака вынесены в интерфейс мастера и лежат в локальной PostgreSQL вместе с журналом срабатываний, спорные кадры складываются в объектное хранилище очередью на разметку.
Обмен заказами и накладными с торговыми сетями по EDI
Задача, решение и что передали
Задача
Заказы сетей снимали из почты и портала руками, а накладную набирали в учётной системе заново.
Решение
Подключён канал EDI: входящий заказ разбирается в документ учётной системы по коду сети и точке доставки, номенклатура сопоставляется таблицей соответствия артикулов. Подтверждение заказа, уведомление об отгрузке и накладная формируются из документов отгрузки и уходят обратно тем же каналом. Сообщение, не прошедшее контроль формата, попадает в журнал отклонённых с текстом причины.
Сдано 4
Настроенный канал EDI
Справочник соответствия артикулов и точек доставки
Схемы контроля документов
Журнал отклонённых сообщений
Как делали
Обследование Собрали, как заказы сетей снимают из почты и с портала и как накладную набирают в учётной системе заново. Сверили требования каждой сети к составу документов и разобрали, как их артикулы и точки доставки соотносятся с внутренними.
Архитектура Канал EDI подняли отдельным сервисом от учётной системы: входящий заказ разбирается в документ по коду сети и точке доставки, номенклатура сопоставляется таблицей соответствия артикулов. Контроль формата поставили на входе — сообщение, не прошедшее схему, уходит в журнал отклонённых с текстом причины и документа не создаёт.
Разработка Сначала приём заказа и разбор в документ учётной системы, затем подтверждение заказа и уведомление об отгрузке, затем накладная из документов отгрузки и журнал отклонённых. На стенде обменивались тестовыми сообщениями по каждой подключаемой сети.
Проверка Прогоняли полный круг по каждой сети — заказ, подтверждение, отгрузка, накладная, и отдельно слали сообщения с неверными артикулами и точками доставки. Провалом считали заказ, разобранный на чужую точку доставки, и накладную, ушедшую в сеть с составом, расходящимся с документом отгрузки.
Передача Передали настроенный канал EDI, справочник соответствия артикулов и точек доставки, схемы контроля документов и журнал отклонённых сообщений. Оператору показали разбор отклонённых, службе продаж — подключение новой точки доставки.
Архитектура
Канал собран из трёх частей: AS2-узел, принимающий и отправляющий сообщения провайдеру с подписью и квитанцией MDN; Go-сервис разбора, который приводит EDI-сообщение к документу учётной системы по коду сети и точке доставки; и таблица соответствия артикулов в PostgreSQL. Между приёмом и разбором стоит очередь, потому что AS2 требует вернуть квитанцию в рамках соединения, а держать в нём же разбор документа и обращение к 1С значит получать повторную отправку со стороны сети. Сообщение проверяется по XSD до разбора: не прошедшее контроль попадает в журнал отклонённых с текстом причины и до учётной системы не доходит. Подтверждение заказа, уведомление об отгрузке и накладная собираются из документов отгрузки и уходят тем же каналом, статус каждого сообщения виден оператору в журнале обмена.
Стек
Go · chi · PostgreSQL · Redis · NATS · Docker · AS2 · EDIFACT · 1С:Предприятие
Заказ переносили в учётную систему нажатием кнопки, и при обрыве связи повторная отправка порождала дубль.
Решение
Передача переведена на очередь: CRM кладёт заказ в брокер с ключом идемпотентности, приёмник в учётной системе хранит журнал обработанных ключей и повтор отбрасывает. Неуспешная обработка уходит на повтор с растущей паузой, после исчерпания попыток сообщение ложится в очередь недоставленных вместе с текстом ошибки. Состояние доставки видно прямо в карточке сделки.
Сдано 4
Очередь и схема повторов
Журнал ключей идемпотентности
Панель состояния обмена
Инструкция разбора недоставленных
Как делали
Обследование дублей Подняли случаи двойных заказов: сравнили сделки в CRM с документами в учётной системе и выяснили, какие поля отличают дубль от повторного заказа того же изделия. Поговорили с менеджерами и плановиками цеха о том, что происходит после нажатия кнопки при обрыве связи.
Архитектура очереди Синхронную отправку заменили очередью: ключ идемпотентности собрали из номера сделки и версии спецификации, а журнал обработанных ключей положили на стороне приёмника, чтобы дубль отсекался в учётной системе, а не в CRM. Неуспешную обработку отправили на повтор с растущей паузой и в очередь недоставленных с текстом ошибки.
Разработка обмена Сначала сделали отправку из CRM и журнал ключей, затем схему повторов и очередь недоставленных, затем состояние доставки в карточке сделки. На стенде рвали связь и гасили приёмник посреди обработки сообщения.
Проверка на сбоях Гоняли отправку одного заказа с обрывом на каждом шаге цепочки. Провалом считали второй заказ в производственном учёте по одному ключу идемпотентности, а сообщение, потерявшееся между брокером и приёмником без следа в очереди недоставленных, приравнивали к дублю.
Передача поддержке Передали сценарии проверки сбоев, инструкцию разбора недоставленных и панель состояния обмена. Поддержка сама возвращает сообщение из очереди недоставленных в обработку и видит состояние доставки в карточке сделки, не заходя в базы.
Архитектура
Адаптер на стороне CRM публикует заказ в RabbitMQ с заголовком идемпотентного ключа, потребитель на Tokio перед обработкой вставляет ключ в таблицу PostgreSQL с уникальным индексом: конфликт вставки означает повтор, и сообщение подтверждается без записи в учётную систему. Журнал ключей держится в СУБД, а не в памяти процесса, потому что потребителей несколько и они перезапускаются, а уникальный индекс даёт им общий арбитр в одной транзакции с бизнес-записью. Неуспешная обработка уходит в очередь отложенных повторов с растущей паузой, после исчерпания попыток сообщение ложится в dead-letter-очередь вместе с текстом ошибки. Состояние доставки CRM читает из HTTP-сервиса на Axum поверх той же таблицы, метрики очередей и повторов снимает Prometheus.
Выработку сдавали бумажным маршрутным листом, и до конца месяца никто не знал, сколько операций реально закрыто по заказу.
Решение
Маршрутную карту заказа разложили на операции с нормой времени и привязали к рабочим центрам. На участке поставили терминалы со сканером: рабочий отмечает начало и сдачу операции по штрихкоду наряда, контролёр подтверждает годные и брак отдельными строками. Закрытая операция уходит в учётную систему проводкой выработки и в расчёт сдельной оплаты.
Сдано 3
Передали конфигурацию учёта операций
Схему нанесения штрихкодов
Инструкции оператора терминала и регламент закрытия смены
Как делали
Обследование цеха Прошли по участкам с мастерами и нормировщиком, разобрали бумажные маршрутные листы и наряды, сверили нормы времени по операциям со справочником. Посмотрели, как выработка попадает в расчёт сдельной оплаты и почему до закрытия периода никто не знает, сколько операций закрыто по заказу.
Архитектура учёта Маршрутную карту заказа разложили на операции с нормой времени и привязали к рабочим центрам, а годные и брак решили писать отдельными строками, чтобы контролёр не переписывал сдачу. Терминал оставили только для сканирования и подтверждения, а проводку выработки отдали учётной системе.
Разработка терминалов Сначала сделали справочник операций и рабочих центров, затем нанесение штрихкодов на наряды и экраны начала и сдачи операции, затем обмен с учётной системой и расчёт сдельной оплаты. Обкатывали на одном участке до разворачивания на цех.
Проверка на нарядах Сдавали операции по реальным нарядам параллельно с бумажным маршрутным листом и сверяли итог по заказу. Не приняли бы работу при расхождении закрытых операций с бумагой и при сдаче, прошедшей без подтверждения контролёра или в обход порядка операций.
Передача мастерам Передали конфигурацию учёта операций, схему нанесения штрихкодов, инструкции оператора терминала и регламент закрытия смены. Мастер сам заводит новую маршрутную карту и разбирает незакрытые операции смены.
Архитектура
Сервис на Ktor держит маршрутные карты, операции с нормой времени и рабочие центры в PostgreSQL; терминалы на участке работают тонким клиентом по HTTP и копят несданные отметки локально, пока сеть в пролёте недоступна. Отметка начала и сдачи приходит с номером наряда со штрихкода Code 128, идентификатором терминала и табельным номером, годные и брак пишутся отдельными строками контролёром. Отметка сначала фиксируется в собственной базе и лишь потом выгружается пачкой в 1С:ERP фоновым воркером с подтверждением по номеру операции — смена не должна останавливаться на время недоступности учётной системы. Справочники и текущее задание терминала кешируются в Redis, расчёт сдельной оплаты читает те же закрытые операции.
Съём технологических параметров плавки и паспорт партии
Задача, решение и что передали
Задача
Параметры плавки выписывали в журнал от руки, и при рекламации поднять историю конкретной партии удавалось не всегда.
Решение
Собрали опрос контроллеров печей и разливки по OPC UA с записью значений в историзатор с меткой времени. Каждый замер привязали к номеру плавки и к последующим партиям проката, чтобы цепочка держалась от завалки до отгрузки. Паспорт качества собирается из историзованных значений и результатов лаборатории, без переноса цифр руками.
Сдано 3
Передали карту сигналов агрегатов
Схему привязки замеров к плавке
Шаблон паспорта качества и регламент восстановления после обрыва связи
Как делали
Обследование агрегатов С технологами и службой КИП прошли по печам и разливке, составили перечень сигналов у контроллеров и разобрали рукописный журнал плавок. Выяснили, какие именно значения поднимают при рекламации и чего в журнале не хватает.
Архитектура съёма Съём вынесли в опрос контроллеров по OPC UA с записью в историзатор по метке времени, решив хранить сырые замеры, а паспорт собирать из них расчётом. Привязку сделали к номеру плавки и далее к партиям проката, чтобы цепочка держалась от завалки до отгрузки.
Разработка историзации Сначала карта сигналов и опрос агрегатов, затем привязка замеров к номеру плавки и переделам, затем шаблон паспорта качества с подстановкой результатов лаборатории. На стенде проигрывали записи с обрывами связи и восстановлением из буфера контроллера.
Проверка на плавках Сверяли историзованные значения с показаниями на пульте и записями технолога, поднимали паспорт по прошлым плавкам. Провалом считали разрыв цепочки «плавка — партия проката» и пропуск замеров после обрыва связи, не восстановленный из буфера.
Передача службе АСУ Передали карту сигналов агрегатов, схему привязки замеров к плавке, шаблон паспорта качества и регламент восстановления после обрыва связи. Служба АСУ ТП сама добавляет сигнал в карту, а лаборатория выпускает паспорт без переноса цифр руками.
Архитектура
Клиент OPC UA на .NET держит подписку на теги контроллеров печей и разливки и пишет значения с меткой времени источника в историзатор на TimescaleDB. Ряд лежит в гипертаблицах с партиционированием по времени, а не в обычной строчной таблице, потому что запись идёт непрерывно, а чтение всегда ограничено тегом и интервалом плавки — такое разбиение отвечает профилю запроса паспорта. Сервис контекста слушает события завалки и выпуска из учётного контура через RabbitMQ и проставляет замерам номер плавки, дальше цепочка тянется на партии проката до отгрузки. При обрыве связи клиент дочитывает буфер OPC UA-сервера через historical access, поэтому пропуска в паспорте не возникает; паспорт качества собирает отдельный сервис из историзатора и результатов лаборатории.
Стек
C# · ASP.NET Core · TimescaleDB · PostgreSQL · RabbitMQ · Docker · OPC UA · 1С:ERP · Grafana
Отбирали то, что стояло ближе к воротам, и остаточный срок годности выяснялся уже на приёмке у сети.
Решение
Каждой партии присвоили идентификатор с датой выработки, сменой и линией, а срок хранения считается по номенклатурному правилу. Отбор перевели на правило FEFO: система предлагает партию с ближайшим сроком и не даёт подтвердить отгрузку партии с остаточным сроком ниже требования сети. Связь готовой партии с партиями сырья пишется на замесе и держится до товарной накладной.
Сдано 3
Передали правила расчёта сроков хранения
Регламент маркировки партий
Отчёт прослеживаемости и инструкции склада отгрузки
Как делали
Обследование партий Разобрали с технологом и складом отгрузки, как маркируются партии на выходе с линии и как считается срок хранения по номенклатуре. Собрали требования сетей по остаточному сроку и посмотрели, из чего фактически собирается отгрузка у ворот.
Архитектура сроков Идентификатор партии собрали из даты выработки, смены и линии, а срок хранения сделали расчётным по номенклатурному правилу, чтобы правило менялось без правки кода. Отбор перевели на FEFO с жёстким запретом подтверждения отгрузки при остаточном сроке ниже требования получателя, связь с партиями сырья решили писать на замесе.
Разработка отбора Сначала правила расчёта сроков и маркировка партий, затем подсказка партии при отборе и блокировка отгрузки, затем протяжка связи сырьё — готовая партия до товарной накладной. Обкатывали на складе отгрузки на реальных заявках.
Проверка на отгрузках Собирали отгрузки по заявкам сетей с разными требованиями к остаточному сроку и поднимали прослеживаемость от накладной до замеса. Провалом считали подтверждённую отгрузку партии с остаточным сроком ниже требования получателя и партию, для которой не поднимается состав сырья.
Передача складу Передали правила расчёта сроков хранения, регламент маркировки партий, отчёт прослеживаемости и инструкции склада отгрузки. Технолог сам меняет правило срока по номенклатуре, а склад разбирает отказ отбора без обращения к разработчику.
Архитектура
Сервис партий на ASP.NET Core присваивает партии идентификатор из даты выработки, смены и линии, срок хранения считается по номенклатурному правилу и сохраняется вычисленной датой в PostgreSQL. Отбор идёт запросом FEFO с сортировкой по дате истечения и отсечением партий с остаточным сроком ниже требования сети; проверка стоит в сервисе, а не в форме терминала, потому что тот же запрет нужен и при автоматическом резервировании из заказа. Связь партий сырья с готовой партией пишется таблицей рёбер, а не полем родителя, — один замес собирается из нескольких партий сырья, и прослеживаемость должна разворачиваться в обе стороны. Обмен с 1С:ERP идёт очередью, этикетка партии печатается в GS1-128 с номером и датой, горячие остатки кешируются в Redis.
Портал подрядчика с пропусками и нарядами-допусками
Задача, решение и что передали
Задача
Заявки на пропуск приходят списком в почте, удостоверения и инструктажи проверяются глазами в бюро пропусков, а сроки их действия живут в таблице.
Решение
Портал принимает заявку на пропуск карточкой работника: удостоверения, инструктажи и медосмотр загружаются файлами и получают срок действия, по которому идёт автоматическая проверка. Пропуск оформляется только при открытом наряде-допуске и действующих документах, отметка уходит в СКУД интеграцией. Истекающий документ подсвечивается подрядчику заранее, закрытие наряда снимает право прохода без участия бюро пропусков.
Сдано 4
Портал подрядчика
Интеграция со СКУД
Реестр допусков и инструктажей
Матрица проверок
Как делали
Обследование пропусков Посмотрели работу бюро пропусков: как приходят заявки списком в почте, что проверяют глазами в удостоверениях и протоколах инструктажей, где ведутся сроки их действия. Разобрали, как оформляется наряд-допуск и что отдаётся в СКУД.
Архитектура проверок Заявку решили принимать карточкой работника с файлами документов и сроком действия, а проверку сроков и наличия допусков сделать автоматической. Пропуск оформляется только при открытом наряде-допуске, отметку в СКУД вынесли в интеграцию, чтобы закрытие наряда снимало право прохода без участия бюро.
Разработка портала Сначала реестр допусков и инструктажей с матрицей проверок, затем подача заявки подрядчиком и оформление пропуска, затем интеграция со СКУД и заблаговременная подсветка истекающих документов.
Проверка допусков Подавали заявки с просроченным удостоверением и без наряда-допуска, закрывали наряд при работниках на площадке. Провалом считали оформленный пропуск при недействующем документе и сохранившееся право прохода в СКУД после закрытия наряда-допуска.
Передача бюро пропусков Передали портал подрядчика, интеграцию со СКУД, реестр допусков и инструктажей, матрицу проверок и инструкцию бюро пропусков. Бюро само правит состав проверяемых документов, а подрядчик следит за сроками в своей карточке.
Архитектура
Портал на NestJS хранит карточки работников, удостоверения, инструктажи и сроки их действия в PostgreSQL, сканы документов — в MinIO. Матрица проверок «тип работ — обязательные документы» лежит данными, а не в коде формы, потому что перечень допусков меняется распоряжением по площадке и правится администратором. Обмен со СКУД вынесен за очередь: адаптер ставит команды на выдачу и снятие права прохода и повторяет их при недоступности контроллеров — прямой синхронный вызов терял бы снятие пропуска в момент закрытия наряда-допуска. Ночная задача сверяет сроки и заранее подсвечивает истекающий документ подрядчику, вход подрядчиков идёт через Keycloak, узлы разворачиваются плейбуками Ansible.
Портал согласования договоров с версиями и замечаниями
Задача, решение и что передали
Задача
Проект договора ходит по почте вложениями, правки собираются из писем нескольких служб, а действующую версию выясняют у инициатора.
Решение
Портал ведёт карточку договора с маршрутом, который собирается из типа договора и подразделения инициатора: замечание согласующего привязано к пункту и получает статус — принято, отклонено, снято. Новая версия собирается из принятых замечаний и сравнивается с предыдущей по пунктам, подпись ставится квалифицированным сертификатом. Маршруты, сроки шагов и замещение на время отсутствия настраиваются администратором без правки кода.
Сдано 4
Портал согласования
Конструктор маршрутов
Реестр замечаний по пунктам
История версий договоров
Как делали
Обследование согласований Разобрали почтовую переписку по проектам договоров и собрали с юристами, финансами и снабжением фактические маршруты по типам договоров. Выяснили, как сегодня определяют действующую версию и куда деваются правки из писем.
Архитектура маршрутов Маршрут решили собирать из типа договора и подразделения инициатора, а не зашивать в код, а замечание привязать к пункту со статусом — принято, отклонено, снято. Новая версия собирается из принятых замечаний и сравнивается с предыдущей по пунктам, подпись ставится квалифицированным сертификатом.
Разработка портала Сначала карточка договора с версиями и замечаниями по пунктам, затем конструктор маршрутов со сроками шагов и замещением на время отсутствия, затем сравнение версий и подписание.
Проверка на договорах Провели через портал договоры разных типов с параллельными согласующими и отсутствующим подписантом. Провалом считали расхождение действующей версии с составом принятых замечаний и шаг маршрута, зависший без замещающего.
Передача администратору Передали портал согласования, конструктор маршрутов, реестр замечаний по пунктам, историю версий и инструкцию администратора. Администратор сам заводит маршрут под новый тип договора и настраивает замещение без правки кода.
Архитектура
Маршрут согласования исполняет движок BPMN: схема маршрута — это данные, а не код, поэтому администратор меняет шаги, сроки и замещение на время отсутствия без пересборки сервиса. Карточка договора, замечания с привязкой к пункту и версии лежат в PostgreSQL, файлы версий — в объектном хранилище; сравнение версий делает отдельный сервис по разметке пунктов, а не бинарным сравнением файлов, иначе принятое замечание не связать с конкретным изменением текста. Приложение на Spring Boot ведёт статусы замечаний — принято, отклонено, снято — и собирает новую версию из принятых, подпись ставится квалифицированным сертификатом через КриптоПро с проверкой цепочки. Эскалации по сроку шага поднимает таймер процесса, уведомления уходят очередью.
Перенос аналитического хранилища с Oracle на отечественную СУБД
Задача, решение и что передали
Задача
Отчётность по плавкам считалась в хранилище на Oracle, продление лицензий на которое стало невозможным.
Решение
Схемы и витрины перенесены в аналитическую СУБД с сегментированием по номеру плавки, пакеты PL/SQL разобраны и переписаны под новый диалект. Загрузка переведена на оркестратор: каждый шаг сверяет число строк и контрольную сумму с источником. Старое хранилище оставлено в режиме чтения на время параллельного счёта.
Сдано 4
Переданы схемы витрин
Регламент загрузки
Протокол параллельного счёта
Инструкция отката
Как делали
Обследование хранилища Сняли инвентарь схем, витрин и пакетов PL/SQL в хранилище на Oracle и разобрали, какие отчёты по плавкам ими считаются и кто их потребляет. Отметили конструкции, у которых нет прямого аналога в диалекте новой СУБД.
Архитектура витрин Витрины перенесли с сегментированием по номеру плавки, пакеты PL/SQL разобрали и переписали под новый диалект, а не эмулировали. Загрузку решили вести оркестратором с пошаговой сверкой, старое хранилище оставили в режиме чтения на время параллельного счёта.
Разработка загрузки Сначала перенесли схемы и справочники, затем переписанные расчёты витрин, затем собрали конвейер загрузки, где каждый шаг сверяет число строк и контрольную сумму с источником.
Проверка параллельным счётом Считали витрины одновременно на прежнем и новом хранилище и сверяли построчно. Провалом считали любое расхождение показателя по плавке, шаг загрузки без сверки строк и контрольной суммы и отчёт, который не собирался без обращения к прежнему хранилищу.
Передача сопровождению Передали схемы витрин, регламент загрузки, протокол параллельного счёта и инструкцию отката. Сопровождение само перезапускает шаг загрузки и разбирает результаты сверки.
Архитектура
Витрины перенесены в Greenplum с распределением по номеру плавки: соединения витрин идут по этому же ключу, поэтому сегменты считают локально, без перекачки строк между узлами. Пакеты PL/SQL разобраны на шаги DAG в Airflow, а сервис контроля на NestJS после каждого шага снимает число строк и контрольную сумму с источника и приёмника и останавливает цепочку при расхождении; метаданные прогонов и найденные расхождения лежат в PostgreSQL. Панель параллельного счёта собрана на React и показывает расхождения в разрезе витрин и периодов. Прежнее хранилище оставлено в режиме чтения на время параллельного счёта, переключение отчётов делается сменой источника в конфигурации, без правки самих витрин.
Портирование конструкторского рабочего места под доверенную ОС
Задача, решение и что передали
Задача
Рабочее место конструктора запускалось только под Windows: интерфейс, печать и просмотр чертежей опирались на системные библиотеки.
Решение
Интерфейс переведён на кроссплатформенный тулкит, обращения к WinAPI и реестру убраны за слой совместимости с реализацией под Linux. Печать и предпросмотр чертежей переведены на системный сервер печати, шрифты и кодировки зафиксированы в пакете. Собран deb-пакет с зависимостями из репозитория дистрибутива, установка проверена в сегменте без доступа в интернет.
Сдано 4
Переданы исходники слоя совместимости
Deb-пакет со сборочным описанием
Протокол функциональных проверок
Инструкция установки в закрытом сегменте
Как делали
Обследование зависимостей Разобрали, чем рабочее место конструктора держится за Windows: обращения к WinAPI и реестру, печать и предпросмотр чертежей. Собрали перечень форматов, шрифтов и кодировок, которые обязаны выглядеть одинаково на любой машине.
Архитектура совместимости Интерфейс перевели на кроссплатформенный тулкит, системные вызовы убрали за слой совместимости с реализацией под Linux, печать и предпросмотр — на системный сервер печати. Шрифты и кодировки зафиксировали в самом пакете, чтобы чертёж не поехал на другом рабочем месте.
Разработка пакета Сначала слой совместимости и сборка интерфейса, затем печать и предпросмотр чертежей, затем deb-пакет с зависимостями из репозитория дистрибутива и установка в сегменте без доступа в интернет.
Проверка печати Открывали и печатали рабочие чертежи, сверяя оттиск и предпросмотр с прежним рабочим местом. Провалом считали расхождение печатной формы по рамке и штампу, подстановку другого шрифта и установку, потребовавшую пакета из интернета.
Передача службе ИТ Передали исходники слоя совместимости, deb-пакет со сборочным описанием, протокол функциональных проверок и инструкцию установки в закрытом сегменте. Служба ИТ сама разворачивает рабочее место конструктора и собирает пакет из исходников.
Архитектура
Рабочее место собрано на Electron: интерфейс переписан на TypeScript и React, а расчётное ядро и разбор чертёжных форматов остались нативными и подключаются модулем N-API — геометрию переписывать не потребовалось, обращения к WinAPI и реестру убраны за слой совместимости с реализацией под Linux, настройки перенесены в файлы профиля пользователя. Печать и предпросмотр переведены на системный сервер печати: приложение отдаёт PDF в очередь CUPS, шрифты и кодировки зафиксированы в пакете, чтобы вид чертежа не зависел от набора шрифтов конкретной машины. Настройки и список последних документов хранятся в SQLite в профиле. Поставка идёт deb-пакетом с зависимостями из репозитория дистрибутива, установка проверена в сегменте без выхода в интернет, с локального зеркала.
Стек
TypeScript · Electron · SQLite · React · N-API · deb-пакет · Astra Linux · CUPS · C++ ядро
Перенос грифа документа на фрагменты поисковой базы
Задача, решение и что передали
Задача
При нарезке документов на фрагменты уровень доступа терялся, и в общий индекс попадали куски документов с ограничением.
Решение
Перестроили конвейер индексации: гриф, подразделение и срок действия берутся из карточки документа и записываются в каждый фрагмент рядом с его координатами. Индекс пересобирается по событию смены грифа, снятый с учёта документ уходит из выдачи вместе со своими фрагментами. Сверка индекса и хранилища идёт по расписанию и показывает фрагменты без метки.
Сдано 4
Конвейер индексации с метками
Отчёт сверки индекса и хранилища
Регламент смены грифа
Набор проверок выдачи
Как делали
Разбор конвейера Прошли по конвейеру индексации и нашли место, где теряются гриф, подразделение и срок действия: карточка документа читалась отдельно от нарезки. Выбрали из индекса фрагменты без метки и показали, какие куски документов с ограничением уже видны в общей выдаче.
Архитектура меток Решили писать гриф, подразделение и срок действия в каждый фрагмент рядом с его координатами, а не хранить только в карточке. Смену грифа сделали событием, по которому индекс пересобирается, а снятый с учёта документ уходит из выдачи вместе со своими фрагментами.
Сборка конвейера Перебрали конвейер по шагам — карточка, нарезка, запись меток, — затем добавили обработчик события смены грифа и сверку индекса с хранилищем по расписанию. На стенде работали на копии части документов с разными грифами.
Проверка выдачи Проверяли выдачу от лица разных подразделений на подготовленных запросах, отдельно отрабатывали снятие документа с учёта и повышение грифа. Провалом считали любой фрагмент без метки в отчёте сверки и любой случай, когда фрагмент документа с ограничением попал пользователю без права на него.
Передача регламента Отдали конвейер индексации с метками, отчёт сверки индекса и хранилища, регламент смены грифа и набор проверок выдачи. Показали держателям документов, как запустить пересборку после смены грифа и как читать список фрагментов без метки.
Архитектура
Индексатор на NestJS подписан на топик Kafka с событиями карточек документооборота: по событию воркер забирает файл из S3, режет на фрагменты, считает эмбеддинги BGE-M3 и рядом с координатами пишет в payload гриф, подразделение и срок действия. Фильтр по грифу подаётся в Qdrant условием поиска до ранжирования, а не отсечением результатов после — иначе закрытый фрагмент влияет на порядок выдачи и может попасть в цитату. Идентификатор точки детерминирован (идентификатор документа плюс порядковый номер фрагмента), поэтому повторная доставка события переписывает фрагмент, а не создаёт дубль, а снятие с учёта убирает разом все точки документа; реестр документов и грифов ведётся в PostgreSQL. Сверщик по расписанию сопоставляет точки в Qdrant с реестром и выдаёт фрагменты без метки; сервисы живут в Kubernetes, роли и права проверяются через Keycloak.
Обследование данных прокатного стана перед моделью дефектов
Задача, решение и что передали
Задача
История брака жила в сменных журналах и лабораторных протоколах, параметры стана — в архиве АСУ ТП, и между собой их никто не сопоставлял.
Решение
Выгрузили архив тегов АСУ ТП и лабораторные протоколы за доступный период и свели их по времени плавки и номеру рулона. Померили пропуски в тегах, разброс частоты опроса и долю партий, где отметка дефекта вообще проставлена. Разметили интервалы, пригодные под обучение, и описали, каких датчиков не хватает, чтобы сигнал перестал обрываться.
Сдано 4
Паспорт источников
Карта пропусков по тегам
Размеченный набор пригодных интервалов
Перечень недостающих датчиков
Как делали
Выгрузка архивов Выгрузили архив тегов АСУ ТП и лабораторные протоколы за доступный период, подняли сменные журналы и расспросили лабораторию и стан, как отметка дефекта попадает в запись. Свели выгрузки по времени плавки и номеру рулона.
Схема сведения Решили строить связку через время плавки и номер рулона как единственный общий ключ, историю брака держать отдельно от потока тегов, а сопоставление вести на выгрузке, не трогая архив. От восстановления пропусков в тегах расчётом отказались, чтобы не выдумывать сигнал.
Сборка расчётов Собрали выгрузку тегов и загрузку лабораторных протоколов, сведение по ключу и расчёт пропусков, разброса частоты опроса и доли партий с проставленной отметкой дефекта. Затем разметили интервалы, пригодные под обучение.
Сверка с журналами Сличали сведённые записи со сменными журналами и лабораторными протоколами по выбранным рулонам и смотрели, держится ли сигнал внутри размеченных интервалов. Интервал браковали, если отметка дефекта не подтверждалась протоколом или ряд тегов обрывался прямо во время прокатки рулона.
Передача паспорта Отдали паспорт источников, карту пропусков по тегам, размеченный набор пригодных интервалов и перечень недостающих датчиков. Разобрали с технологами и АСУ ТП, какие точки опроса нужно завести, чтобы сигнал перестал обрываться.
Архитектура
Выгрузка идёт двумя коннекторами: архив тегов забирается из историка через OPC UA HDA окнами по времени, лабораторные протоколы — отдельным заданием; обе выгрузки ложатся в Parquet на MinIO сырым слоем и не переписываются. Сведение по времени плавки и номеру рулона выполняется шагом Airflow, витрина строится в ClickHouse: пропуски и разброс частоты опроса считаются по отдельным тегам за длинный период, и колоночное хранение поднимает только нужные столбцы, тогда как строчное тянуло бы всю запись целиком. Паспорт источников, карта пропусков и разметка пригодных интервалов ведутся в PostgreSQL, разбор идёт в Jupyter поверх той же витрины. Сырой слой отделён от витрины намеренно — правила склейки и отбраковки пересчитываются на исходных выгрузках без повторного обращения к историку, который отдаёт архив медленно и в своё окно.
Про потери сырья знали все, но где именно они возникают и что из этого попадает в системы, по участкам никто не разложил.
Решение
Прошли по участкам от приёмки до фасовки и описали, какие события уже попадают в учёт, какие остаются в бумажных журналах, а какие не фиксируются вовсе. Для каждого предложенного сценария указали источник данных, частоту события и способ замерить результат. Сценарии сгруппировали по участкам и пометили те, где сначала нужно завести фиксацию события, а уже потом обсуждать модель.
Сдано 4
Карта сценариев по участкам
Перечень нефиксируемых событий
Требования к учёту событий
Способ замера результата
Как делали
Обход участков Прошли по участкам от приёмки до фасовки вместе с технологами и мастерами смен и разобрали, какие события попадают в учёт, какие остаются в бумажных журналах, а какие не фиксируются вовсе. Сверили записи учётной системы с тем, что видно на самом участке.
Привязка к событиям Решили привязывать каждый сценарий к участку и к конкретному событию учёта: источник данных, частота события, способ замера результата. Сценарии, события которых нет ни в одной системе, отделили и поставили перед ними задачу завести фиксацию, а не строить модель на бумажном журнале.
Сборка карты Собрали карту сценариев по участкам, описали требования к учёту недостающих событий и способ замера результата по каждому сценарию. Опирались на то, что уже пишется в учётной и производственной системах, а не на планы по их доработке.
Проверка на участках Прошли карту с технологами и мастерами по участкам и выборочно проверили, действительно ли названное событие доходит до системы. Сценарий не проходил, если его источником оказывался бумажный журнал или результат нечем было замерить по данным учёта.
Передача карты Отдали карту сценариев по участкам, перечень нефиксируемых событий, требования к учёту событий и способ замера результата. Разобрали с производством, с каких участков начинать фиксацию, чтобы отложенные сценарии стало чем считать.
Архитектура
Данные учёта забираются из 1С:ERP по OData и из MES отдельным коннектором в промежуточную схему PostgreSQL — обследование не ходит запросами в рабочую базу учётной системы, выгрузка идёт окном по расписанию и не конкурирует со сменой. События участков приводятся к общему ключу (участок, смена, партия), поэтому приёмка, переделы и фасовка сводятся в одну цепочку по сырью и видно, где событие обрывается. То, что живёт в бумажных журналах, заводится через форму на FastAPI и ложится в ту же схему с пометкой источника: карта сценариев обязана отличать событие из учёта от занесённого вручную, иначе охват сценария завышается. Сканы журналов и выгрузки складываются в MinIO, разрезы по участкам и частоте событий смотрят в Metabase, сервисы поднимаются в Docker.
Горный диспетчер и командиры отделений поднимали нужную позицию плана ликвидации аварий и паспорт крепления по бумажным папкам нарядной, а редакции по разным выработкам расходились между участками.
Решение
Развернули модель и поисковый индекс на серверах в АБК шахты: контур наружу не выходит, запросы идут из нарядной и с терминалов участков. Проиндексировали позиции ПЛА, паспорта крепления и проветривания, наряды-путёвки; каждый фрагмент несёт номер выработки, номер позиции и дату ввода редакции. Ответ собирается только из действующей редакции, изъятая уходит из индекса в момент загрузки новой.
Сдано 4
Передали контурную сборку сервиса
Индексатор позиций ПЛА и паспортов крепления
Регламент смены редакций
Протокол проверки ответов по выработкам
Как делали
Обход нарядной Посидели в нарядной с горным диспетчером и командирами отделений и посмотрели, как поднимается нужная позиция ПЛА и паспорт крепления по бумажным папкам. Собрали по участкам расходящиеся редакции и выяснили, кто вводит новую и куда девается изъятая.
Контур шахты Решили держать модель и поисковый индекс на серверах в АБК шахты без выхода наружу, принимая запросы из нарядной и с терминалов участков. Каждый фрагмент снабдили номером выработки, номером позиции и датой ввода редакции, чтобы ответ собирался только из действующей редакции.
Сборка индексатора Развернули контурную сборку на серверах АБК, затем сделали индексатор позиций ПЛА, паспортов крепления и проветривания и нарядов-путёвок. Последним добавили правило вывода изъятой редакции из индекса в момент загрузки новой.
Проверка по выработкам Прогнали вопросы горного диспетчера и командиров отделений по выработкам, отдельно грузили новую редакцию и повторяли те же вопросы. Провалом считали ответ, собранный из изъятой редакции, и ответ по позиции ПЛА без номера выработки и номера позиции.
Передача диспетчеру Передали контурную сборку сервиса, индексатор позиций ПЛА и паспортов крепления, регламент смены редакций, протокол проверки ответов по выработкам и инструкцию горного диспетчера. Показали, как загружать новую редакцию и как убедиться, что прежняя ушла из выдачи.
Архитектура
В АБК шахты стоят узлы инференса и шлюза: vLLM держит веса в памяти карт, шлюз на Rust принимает запросы из нарядной и с терминалов участков, собирает контекст и обращается к vLLM по HTTP. Инференс вынесен отдельным процессом, потому что загрузка весов в карты — долгая операция, и её нельзя повторять при каждом обновлении шлюза, а шлюз меняется чаще модели. Фрагменты позиций ПЛА, паспортов крепления и проветривания лежат в Qdrant с номером выработки, номером позиции и датой ввода редакции; признак действующей редакции подаётся фильтром до поиска, поэтому изъятая редакция не участвует ни в ранжировании, ни в цитате. Загрузка новой редакции идёт задачей из NATS и в одной операции снимает прежнюю; карточки документов и журнал запросов — в PostgreSQL, роли — Keycloak, эмбеддинги считает BGE-M3, установка узлов и обновления — Ansible, наружу контур не выходит.
Ассистент по технологическому регламенту с разграничением по ролям
Задача, решение и что передали
Задача
Аппаратчику и технологу отвечал один и тот же поиск, поэтому нормы загрузки и рецептура открывались тем, кому по роли положены только действия при отклонении.
Решение
Разметили технологический регламент и паспорта безопасности по разделам: пуск, плановая остановка, действия при отклонении, нормы загрузки, рецептура. Каждый фрагмент получил метку раздела и установки, право на раздел проверяется до сборки контекста, поэтому закрытый текст не влияет ни на ранжирование, ни на цитату. Модель и индекс работают на узлах предприятия, отказ в доступе пишется с ролью, установкой и текстом запроса.
Сдано 4
Передали матрицу разделов и ролей
Размеченный индекс регламента и паспортов безопасности
Журнал отказов в доступе
Стенд проверки прав
Как делали
Разбор регламента Разобрали технологический регламент и паспорта безопасности по разделам и вместе с технологами определили, что положено аппаратчику, а что технологу. На текущем поиске показали, как нормы загрузки и рецептура открывались тем, кому по роли положены только действия при отклонении.
Матрица ролей Пометили каждый фрагмент разделом и установкой и решили проверять право на раздел до сборки контекста, чтобы закрытый текст не влиял ни на ранжирование, ни на цитату. Модель и индекс поставили на узлах предприятия, а отказ в доступе договорились писать с ролью, установкой и текстом запроса.
Разметка и права Сначала разметили регламент и паспорта безопасности по разделам — пуск, плановая остановка, действия при отклонении, нормы загрузки, рецептура, — затем собрали матрицу разделов и ролей. После этого встроили проверку прав перед подбором фрагментов и журнал отказов.
Проверка прав На стенде проверки прав задавали одни и те же вопросы от роли аппаратчика и от роли технолога по разным установкам. Провалом считали попадание норм загрузки или рецептуры в ответ аппаратчику, в том числе обрывком цитаты или через изменившийся порядок выдачи, и отказ, не попавший в журнал.
Передача администратору Передали матрицу разделов и ролей, размеченный индекс регламента и паспортов безопасности, журнал отказов в доступе, стенд проверки прав и инструкцию технолога-администратора. Показали, как менять права на раздел и как перепроверить их на стенде после правки регламента.
Архитектура
Фрагменты технологического регламента и паспортов безопасности лежат в Qdrant с метками раздела и установки, роль приходит из Keycloak, решение о доступе к разделу выносит OPA по политике. Проверка права выполняется до поиска и подаётся в запрос фильтром по меткам: при отсечении после ранжирования закрытый текст всё равно влияет на порядок и способен утечь в цитату, поэтому граница стоит перед сборкой контекста, а не после неё. Ответ собирает сервис на FastAPI, обращаясь к vLLM на узлах предприятия; переиндексация раздела запускается задачей через RabbitMQ и идёт в отдельную коллекцию, выдача при этом продолжает работать со старой. Отказ в доступе пишется в PostgreSQL с ролью, установкой и текстом запроса, кластер разворачивается в Kubernetes, стенд проверки прав гоняет тот же набор запросов от каждой роли.
Поиск по правилам классификационного общества и документации заказа
Задача, решение и что передали
Задача
Конструктор выяснял применимый пункт правил классификационного общества и действующий лист рабочей документации по строительному номеру обходом архива и перепиской с бюро.
Решение
Проиндексировали правила классификационного общества, рабочую конструкторскую документацию и извещения об изменении, связав фрагменты со строительным номером заказа и обозначением узла корпуса. Извещение перекрывает лист: заменённые и аннулированные листы исключаются из выдачи при загрузке очередного извещения. Модель и индекс развёрнуты в контуре верфи, ответ приходит с обозначением листа, номером извещения и пунктом правил.
Сдано 4
Передали сервис поиска в контуре верфи
Коннектор к архиву документации и извещений
Правила перекрытия листов
Протокол приёмочных проверок на заказе
Как делали
Обход архива Прошли с конструкторами весь путь поиска применимого пункта правил классификационного общества и действующего листа рабочей документации: обход архива и переписка с бюро. Разобрали, как извещение об изменении доходит до листа и почему на руках оказываются аннулированные листы.
Контур верфи Решили связывать фрагменты со строительным номером заказа и обозначением узла корпуса, а извещение сделать перекрывающим лист: заменённые и аннулированные листы исключаются из выдачи при загрузке очередного извещения. Модель и индекс развернули в контуре верфи.
Сборка перекрытий Собрали коннектор к архиву документации и извещений, затем индексацию правил классификационного общества и рабочей конструкторской документации. Последними сделали правила перекрытия листов и вывод ответа с обозначением листа, номером извещения и пунктом правил.
Приёмочные проверки На конкретных заказах прогоняли приёмочные проверки по узлам корпуса, грузили извещения и повторяли те же запросы. Провалом считали выдачу заменённого или аннулированного листа и ответ по правилам без указания пункта и номера извещения.
Передача бюро Передали сервис поиска в контуре верфи, коннектор к архиву документации и извещений, правила перекрытия листов, протокол приёмочных проверок на заказе и руководство администратора индекса. Показали бюро, как загружать извещение и убеждаться, что перекрытый лист ушёл из выдачи.
Архитектура
Индексатор запускается расписанием Airflow: коннектор забирает из архива рабочую конструкторскую документацию, правила классификационного общества и извещения об изменении, оригиналы складывает в MinIO и режет на фрагменты со строительным номером заказа, обозначением узла и номером листа. Связь листа с извещением ведётся в PostgreSQL, а перекрытый лист не удаляется, а помечается заменённым и отсекается фильтром запроса: аннулированный лист нужен при разборе прошлого предъявления, но в выдачу конструктору идти не должен. Поиск идёт по Elasticsearch с полями заказа и обозначения, сервис на FastAPI собирает контекст и обращается к vLLM внутри контура верфи, ответ возвращает обозначение листа, номер извещения и пункт правил. События загрузки извещений публикуются в Kafka, поэтому пометка листов и переиндексация выполняются последовательно от одного журнала и не расходятся при параллельной загрузке; всё поднято в Kubernetes.
Целлюлозно-бумажное производствоЧастные LLM в контуре
Проверяется: протокол нагрузочного прогона
Инференс языковых моделей на GPU-узлах комбината
Задача, решение и что передали
Задача
Ассистент по регламентам варки работал на арендованных мощностях, и служба безопасности не согласовывала выход технологических данных за периметр комбината.
Решение
Собрали инференс на собственных GPU-узлах: квантованная модель постоянно держится в памяти карт, поверх неё работает ассистент по регламенту варки, картам отбелки и классификатору рулонного брака. Ёмкость сняли нагрузочным прогоном — сколько одновременных смен держат карты и какая задержка выходит на длинном контексте регламента. Отказ узла переводит запросы на резервный, начатый ответ при этом не обрывается.
Сдано 4
Передали кластер инференса на узлах комбината
Протокол нагрузочного прогона
Регламент эксплуатации с порогами загрузки
Панели наблюдения за картами
Как делали
Разбор нагрузки Разобрали, что работало на арендованных мощностях — ассистент по регламенту варки, карты отбелки, классификатор рулонного брака — и какие технологические данные при этом уходили за периметр. С безопасностью и ИТ комбината сверили, что можно перенести на собственные GPU-узлы.
Схема кластера Решили держать квантованную модель постоянно в памяти карт и поднимать поверх неё все задачи разом, а не разводить их по отдельным сборкам. Заложили резервный узел с переводом запросов, при котором начатый ответ не обрывается.
Сборка кластера Собрали инференс на узлах комбината, затем подняли ассистента по регламенту варки, карты отбелки и классификатор рулонного брака. Последними сделали панели наблюдения за картами и пороги загрузки для регламента эксплуатации.
Нагрузочный прогон Сняли ёмкость нагрузочным прогоном — сколько одновременных смен держат карты и какая задержка выходит на длинном контексте регламента — и гасили узел прямо под нагрузкой. Провалом считали оборванный ответ при переводе на резервный узел и выход задержки на длинном контексте регламента за порог, согласованный с технологами.
Передача эксплуатации Передали кластер инференса на узлах комбината, протокол нагрузочного прогона, регламент эксплуатации с порогами загрузки, панели наблюдения за картами и сценарий перевода запросов на резервный узел. Показали ИТ комбината, как читать панели и что делать при отказе узла.
Архитектура
На GPU-узлах комбината работает пул процессов инференса: квантованная модель постоянно держится в памяти карт, снаружи к ней приходит маршрутизатор на C++ по gRPC со стримингом токенов. Модель не выгружается между запросами намеренно — перенос весов в карты стоит дороже самого ответа, поэтому процесс инференса живёт долго и не делит узел с прикладными сервисами. Очередь запросов держится в NATS, состояние начатой сессии — в Redis: при отказе узла маршрутизатор переводит сессию на резервный и продолжает отдачу с последнего отданного токена, начатый ответ не обрывается. Каталог моделей, журналы прогонов и пороги загрузки лежат в PostgreSQL, узлы под управлением Kubernetes с device-plugin для карт, TensorRT-LLM собирает движки под конкретные карты, метрики карт и задержку на длинном контексте снимает Prometheus.
Подписной ассистент по ветеринарным правилам и картам кросса
Задача, решение и что передали
Задача
Зоотехник и ветврач площадки сверяли схему вакцинации, норму посадки и действия при росте падежа по разным версиям карт кросса и ветеринарных правил.
Решение
Подключили подписной сервис к нормативке и технологическим картам: ветеринарные правила, инструкции по применению препаратов и карты кросса разобраны на фрагменты с привязкой к возрасту партии и типу птичника. Вопрос по партии суточного молодняка отвечается на её возраст: схема вакцинации, норма посадки, температурный график, порядок действий при отклонении падежа и конверсии корма. Ответ приходит с пунктом правил и обозначением карты, редакция карты хранится с датой ввода.
Сдано 4
Передали подписной сервис
Индекс ветеринарных правил и карт кросса
Справочник возрастов партии
Набор проверочных вопросов зоотехников
Как делали
Разбор площадки Разобрали с зоотехником и ветврачом площадки, по каким документам они сверяют схему вакцинации, норму посадки и действия при росте падежа, и как между собой расходятся версии карт кросса. Собрали, какие ветеринарные правила и инструкции по применению препаратов у них в ходу.
Привязка к возрасту Решили привязывать фрагменты к возрасту партии и типу птичника, а редакцию карты хранить с датой ввода. Ответ договорились строить на возраст конкретной партии и всегда выдавать с пунктом правил и обозначением карты.
Сборка индекса Подключили подписной сервис к нормативке и технологическим картам, затем разобрали ветеринарные правила, инструкции по применению препаратов и карты кросса на фрагменты. Последними сделали справочник возрастов партии и подбор ответа по партии: вакцинация, норма посадки, температурный график, действия при отклонении падежа и конверсии корма.
Вопросы зоотехников Прогнали проверочные вопросы зоотехников по партиям разного возраста и по типам птичника, отдельно ввели новую редакцию карты и повторили те же вопросы. Провалом считали ответ по прежней редакции карты и схему вакцинации, выданную не на тот возраст партии, о котором спрашивали.
Передача регламента Передали подписной сервис, индекс ветеринарных правил и карт кросса, справочник возрастов партии, набор проверочных вопросов зоотехников и регламент ввода новой редакции карты. Показали ветврачу и зоотехнику, как вводить редакцию и проверять ответ по обозначению карты.
Архитектура
Кабинет на React обращается к сервису на FastAPI: вопрос по партии подставляет её возраст в сутках, и фильтр по диапазону возраста применяется в самом запросе к индексу, а не оставляется на усмотрение модели — схема вакцинации и норма посадки привязаны к числу, подбирать их по близости формулировок нельзя. Фрагменты ветеринарных правил, инструкций по препаратам и карт кросса лежат в PostgreSQL, векторы — в pgvector, точный поиск по пунктам и обозначениям карт — в OpenSearch. Редакция карты хранится с датой ввода, действующая выбирается на дату запроса, поэтому ввод новой карты не переписывает то, что отвечалось раньше. Загрузка новых редакций идёт задачей через RabbitMQ и не останавливает выдачу, справочник возрастов партии и повторяющиеся ответы кэшируются в Redis, сервис поставляется подписчикам в Docker.
Агент разбора извещений о снятии компонентов с производства
Задача, решение и что передали
Задача
Рассылки производителей о снятии компонента с производства и смене ревизии читались инженерами выборочно, и снятая позиция обнаруживалась уже при закупке под серию.
Решение
Агент забирает рассылки производителей и дистрибьюторов, распознаёт вложения и вытаскивает партномер, дату последнего заказа, дату последней отгрузки и предложенную замену. Партномер сопоставляется с перечнем элементов: находятся все спецификации и ревизии плат, где стоит компонент, позиции помечаются, а по замене сверяются корпус, номинал, допуск и рабочий диапазон. Решение остаётся за технологом: агент готовит карточку сравнения и складывает извещение в реестр с датой и источником.
Сдано 4
Сервис разбора уведомлений
Реестр извещений производителей
Правила подбора аналога по параметрам
Карточка сравнения замен
Как делали
Разбор рассылок Собрали рассылки производителей и дистрибьюторов за доступный период и посмотрели, в каком виде приходят извещения о снятии с производства и о смене ревизии. Разобрали с инженерами и закупкой случаи, когда снятая позиция обнаруживалась уже при закупке под серию.
Реестр извещений Решили вести реестр извещений с датой и источником, а партномер сопоставлять с перечнем элементов, поднимая все спецификации и ревизии плат, где стоит компонент. Подбор замены сделали подсказкой со сверкой корпуса, номинала, допуска и рабочего диапазона, а решение оставили за технологом.
Сборка сопоставления Сделали забор рассылок и распознавание вложений, затем извлечение партномера, даты последнего заказа, даты последней отгрузки и предложенной замены. Последними собрали сопоставление с перечнем элементов и карточку сравнения замен.
Прогон рассылок Прогнали накопленные рассылки и сверили найденные вхождения компонента с перечнями элементов, поднятыми инженерами вручную. Провалом считали извещение, по которому не нашлась спецификация с этим компонентом, и предложенную замену, разошедшуюся с исходной позицией по корпусу или рабочему диапазону, но не помеченную в карточке сравнения.
Передача технологу Передали сервис разбора уведомлений, реестр извещений производителей, правила подбора аналога по параметрам, карточку сравнения замен и инструкцию технолога. Показали, как заводить новый источник рассылки и как принимать решение по карточке сравнения.
Архитектура
Рассылки производителей и дистрибьюторов забирает почтовый коннектор по IMAP, оригиналы уходят в MinIO, задачи разбора — в RabbitMQ; воркеры вытаскивают партномер, дату последнего заказа, дату последней отгрузки и предложенную замену как из текста письма, так и из вложений извещения по JESD46. Партномер приводится к нормализованному виду и сопоставляется через таблицу синонимов производителей — в рассылке номер приходит с суффиксами упаковки и ревизии, и прямое совпадение со строкой спецификации не срабатывает. Состав изделий хранится в PostgreSQL, и все спецификации с ревизиями плат, где стоит компонент, поднимаются рекурсивным запросом по дереву состава прямо в базе, а не выгрузкой всего состава в приложение. Сервис на ASP.NET Core ведёт реестр извещений с датой и источником и собирает карточку сравнения замен по корпусу, номиналу, допуску и рабочему диапазону; решение технолог принимает в интерфейсе на React, поставка — Docker.
Зоотехник записывал падёж, расход корма и температуру по птичнику в тетрадь и переносил цифры в учёт в конце недели, поэтому конверсия корма по партии считалась задним числом.
Решение
Агент принимает суточную сводку из формы на планшете, снимка ведомости и голосовой заметки с птичника, распознаёт числа и раскладывает их по партии, корпусу и дате посадки. Значения проверяются на правдоподобие по возрасту партии: падёж выше порога, скачок расхода корма и расхождение с показаниями кормовых весов помечаются и уходят зоотехнику до записи в учёт. Принятые данные заводятся в учётную систему одной операцией, привес и конверсия корма пересчитываются по партии на текущий день.
Сдано 4
Мобильная форма сводки
Сервис распознавания ведомостей
Правила контроля правдоподобия по возрасту партии
Обмен с учётной системой
Как делали
Обход птичников Посмотрели, как зоотехник ведёт тетрадь по птичнику — падёж, расход корма, температура — и когда цифры доходят до учёта. Разобрали, какие показания даёт кормовые весы и почему конверсия корма по партии считалась задним числом.
Пути подачи Решили принимать сводку тремя путями — форма на планшете, снимок ведомости, голосовая заметка с птичника — и раскладывать значения по партии, корпусу и дате посадки. Проверку правдоподобия поставили до записи в учёт, а принятые данные договорились заводить в учётную систему одной операцией.
Сборка формы Сделали мобильную форму сводки, затем распознавание снимков ведомостей и голосовых заметок. Последними собрали правила контроля правдоподобия по возрасту партии и обмен с учётной системой с пересчётом привеса и конверсии корма на текущий день.
Прогон сводок Занесли через агента сводки по прошедшим партиям и сличили их с тетрадями и записями учёта, отдельно подавали завышенный падёж и расхождение с показаниями кормовых весов. Провалом считали сводку, попавшую в учёт мимо проверки правдоподобия, и запись, привязанную не к той партии или не к тому корпусу.
Передача зоотехникам Передали мобильную форму сводки, сервис распознавания ведомостей, правила контроля правдоподобия по возрасту партии, обмен с учётной системой и инструкцию зоотехника. Показали на площадке, как подавать сводку и что делать с помеченным значением.
Архитектура
Форма на планшете — веб-приложение на React с локальной офлайн-очередью: в птичнике связи может не быть, а сводка сдаётся на месте, поэтому запись копится в браузере и отправляется при появлении сети. Ключ сводки собран из партии, корпуса и даты, так что повторная отправка из офлайн-очереди переписывает запись, а не заводит вторую по тому же дню. API на NestJS принимает форму, снимок ведомости и голосовую заметку и ставит задачу в RabbitMQ — распознавание снимка и расшифровка речи через Vosk идут отдельным воркером, зоотехник не ждёт их результата у птичника. Проверки правдоподобия по возрасту партии выполняются до записи в учёт: падёж выше порога, скачок расхода корма и расхождение с показаниями кормовых весов уходят зоотехнику, принятые данные передаются в 1С по OData одной операцией, состояние сводок — в PostgreSQL, справочники партий кэшируются в Redis, поставка — Docker.
Перед предъявлением узла строитель собирал по цехам сертификаты на металл и сварочные материалы, протоколы контроля и акты на скрытые работы, и предъявление срывалось из-за одной недостающей бумаги.
Решение
Агент ведёт для каждой позиции заказа перечень обязательных документов по этапу постройки и подтягивает их из архива и почты цехов: сертификаты на плавку и сварочные материалы, протоколы неразрушающего контроля, акты на скрытые работы. Комплект проверяется на действующие сроки и на совпадение номера плавки и партии материала с указанными в спецификации секции; нехватка или просроченный документ поднимаются мастеру до заявки на предъявление. Собранное дело формируется с описью, извещение для инспекции классификационного общества выпускается по принятому шаблону.
Сдано 4
Реестр комплектности по этапам постройки
Сервис сборки дела с описью
Правила контроля сроков документов
Шаблон извещения на предъявление
Как делали
Обход цехов Прошли со строителем и мастерами весь путь сбора документов перед предъявлением узла: сертификаты на металл и сварочные материалы, протоколы контроля, акты на скрытые работы. Разобрали срывы предъявления из-за одной недостающей бумаги и выписали, что требуется на каждом этапе постройки.
Реестр комплектности Решили вести для каждой позиции заказа перечень обязательных документов по этапу постройки и подтягивать их из архива и почты цехов, а не собирать заново. Контроль сроков и совпадение номера плавки и партии материала со спецификацией секции поставили до заявки на предъявление, а извещение для инспекции классификационного общества договорились выпускать по принятому шаблону.
Сборка дела Собрали реестр комплектности по этапам постройки, затем подтягивание документов из архива и почты цехов, затем проверки сроков и сверку номера плавки и партии со спецификацией секции. Последним сделали формирование дела с описью и выпуск извещения.
Прогон предъявлений Собрали дела по уже предъявленным узлам и сличили с тем, что уходило в инспекцию, отдельно подкладывали просроченный сертификат и чужой номер плавки. Провалом считали дело, ушедшее в заявку на предъявление без обязательного по этапу документа или с сертификатом на плавку, не совпадающую со спецификацией секции.
Передача мастерам Передали реестр комплектности по этапам постройки, сервис сборки дела с описью, правила контроля сроков документов, шаблон извещения на предъявление и инструкцию мастера. Показали цехам, как класть документ, чтобы он подтянулся, и как увидеть нехватку до заявки.
Архитектура
Процесс предъявления ведёт Camunda: для позиции заказа и этапа постройки движок держит перечень обязательных документов, точки ожидания и таймеры по срокам — состояние живёт в движке, а не полем в таблице, потому что сборка дела растянута по цехам и неделями ждёт внешних событий. Сервис на FastAPI подтягивает сертификаты на плавку и сварочные материалы, протоколы неразрушающего контроля и акты на скрытые работы из архива и почты цехов через задачи RabbitMQ, файлы остаются в MinIO, а дело собирается ссылками на объекты хранилища, чтобы один сертификат плавки не копировался в каждое дело, где он участвует. Сверка идёт по номеру плавки и партии материала со спецификацией секции, недостающий или просроченный документ поднимается мастеру до заявки на предъявление. Реестр комплектности по этапам лежит в PostgreSQL, опись и извещение для инспекции печатаются шаблоном через ReportLab, интерфейс мастера — на React, поставка — Docker.
Проверяется: реестр редакций паспортов безопасности
Сборка отгрузочного комплекта на опасный груз
Задача, решение и что передали
Задача
Комплект на отгрузку собирал оператор сбыта: паспорт качества партии искали в лаборатории, паспорт безопасности — в папке технолога, а аварийную карточку подбирали по памяти.
Решение
Агент берёт из учётной системы строку отгрузки с продуктом, номером партии и видом тары, подтягивает паспорт качества по партии из лабораторной системы и действующую редакцию паспорта безопасности из реестра. По классу опасности и номеру ООН подбираются аварийная карточка и требования к маркировке тары; несоответствие тары или просроченный паспорт качества останавливают сборку и уходят технологу. Комплект формируется одним пакетом к товарно-транспортным документам, каждая вложенная редакция фиксируется с номером и датой утверждения.
Сдано 4
Сервис сборки комплекта
Реестр редакций паспортов безопасности
Правила подбора аварийной карточки по классу опасности
Журнал сформированных пакетов
Как делали
Разбор отгрузок Посмотрели, как оператор сбыта собирает комплект на отгрузку: паспорт качества партии искали в лаборатории, паспорт безопасности — в папке технолога, аварийную карточку подбирали по памяти. Разобрали с технологом, по каким признакам подбирается аварийная карточка и требования к маркировке тары.
Схема подбора Решили брать отправной точкой строку отгрузки из учётной системы — продукт, номер партии, вид тары, — паспорт качества подтягивать по партии из лабораторной системы, а паспорт безопасности брать действующей редакцией из реестра. Аварийную карточку и требования к маркировке привязали к классу опасности и номеру ООН, а несоответствие тары или просроченный паспорт качества сделали остановкой сборки.
Сборка пакета Сделали чтение строки отгрузки и связку с лабораторной системой, затем реестр редакций паспортов безопасности, затем подбор аварийной карточки по классу опасности и номеру ООН. Последним собрали формирование пакета к товарно-транспортным документам с фиксацией номера и даты утверждения каждой вложенной редакции.
Прогон отгрузок Собрали пакеты по прошедшим отгрузкам и сличили с тем, что уходило с машиной, отдельно подставляли просроченный паспорт качества и тару, не подходящую под класс опасности. Провалом считали пакет, сформированный с недействующей редакцией паспорта безопасности или с несоответствующей тарой: такая сборка обязана была остановиться и уйти технологу.
Передача сбыту Передали сервис сборки комплекта, реестр редакций паспортов безопасности, правила подбора аварийной карточки по классу опасности, журнал сформированных пакетов и инструкцию оператора сбыта. Показали технологу, как вводить новую редакцию паспорта безопасности и как разбирать остановленную сборку.
Архитектура
Строка отгрузки приходит из учётной системы событием в Kafka, сервис на Spring Boot по номеру партии запрашивает паспорт качества из лабораторной системы и берёт действующую редакцию паспорта безопасности из реестра в PostgreSQL. По классу опасности и номеру ООН из справочника ДОПОГ подбираются аварийная карточка и требования к маркировке тары, а проверка тары и срока паспорта качества выполняется до формирования пакета, а не после — иначе к товарно-транспортным документам уйдёт комплект с недействующей редакцией, и отзывать его придётся уже с машины. Реестр хранит редакции с номером и датой утверждения, поэтому в собранном пакете зафиксировано, какая именно редакция вложена, и журнал пакетов остаётся разбираемым спустя месяцы. Обращения к лабораторной и учётной системам вынесены в маршруты Apache Camel, чтобы недоступность одной из них останавливала сборку с понятной причиной, а не роняла сервис; готовый пакет складывается в MinIO, всё работает в Kubernetes.
Распознавание пороков доски на сортировочной линии
Задача, решение и что передали
Задача
Сорт доски назначал браковщик на ходу потока после сушки, и одна и та же доска у разных смен уходила в разные карманы.
Решение
Поставили камеры над пластями и кромкой доски на транспортёре сортировочной линии, кадры синхронизированы с датчиком длины и энкодером цепи. Детектор обучен на выпадающий сучок, обзол, синеву, гниль и трещину, разметка собрана вместе с браковщиками по действующим сортообразующим признакам. Карта пороков и решение по сорту уходят контроллеру линии до подхода доски к сбрасывателю и одновременно ложатся в паспорт партии.
Сдано 4
Передали детектор пороков
Схему установки камер и подсветки над транспортёром
Паспорт партии с картой пороков каждой доски
Протокол приёмочных прогонов на контрольной пачке
Как делали
Обход линии Постояли на сортировочной линии после сушки с браковщиками разных смен и разобрали, по каким сортообразующим признакам они назначают сорт и где расходятся между собой. Посмотрели устройство транспортёра: где стоят датчик длины и энкодер цепи и сколько остаётся до сбрасывателя.
Схема съёмки Решили снимать пласти и кромку камерами над транспортёром и синхронизировать кадры с датчиком длины и энкодером цепи, чтобы карта пороков ложилась на конкретную доску. Решение по сорту договорились отдавать контроллеру линии до подхода доски к сбрасывателю и одновременно писать в паспорт партии.
Сборка детектора Собрали установку камер и подсветки над транспортёром, затем разметку вместе с браковщиками по действующим сортообразующим признакам. Затем обучили детектор на выпадающий сучок, обзол, синеву, гниль и трещину и сделали передачу решения контроллеру.
Контрольная пачка Прогнали контрольную пачку, размеченную браковщиками, и пропустили её через линию на ходу потока. Провалом считали доску, решение по которой не успело дойти до контроллера до сбрасывателя, и расхождение с браковщиками по сортообразующему пороку на контрольной пачке.
Передача смене Передали детектор пороков, схему установки камер и подсветки над транспортёром, паспорт партии с картой пороков каждой доски и протокол приёмочных прогонов на контрольной пачке. Показали смене, как разбирать спорную доску по карте пороков и когда звать на перенастройку.
Архитектура
Камеры над пластями и кромкой снимают доску на транспортёре по GigE Vision, служба захвата на Rust привязывает кадр к положению доски по импульсам энкодера цепи и датчику длины, а не по системному времени: при рывке транспортёра метка времени уплывает, и карта пороков перестаёт сходиться с длиной доски. Детектор исполняется в TensorRT на промышленном ПК у линии — решение по сорту должно быть готово до подхода доски к сбрасывателю, поэтому кадры не уходят по сети в серверную, а инференс стоит рядом с камерами. Класс сорта и карта пороков отдаются контроллеру линии по OPC UA, паспорт партии с картой пороков каждой доски пишется в PostgreSQL. Кадры со спорными срабатываниями складываются в MinIO для дообучения: захват на Tokio пишет их в фоне и не задерживает поток, а службы захвата и инференса разнесены по контейнерам и переживают перезапуск друг друга.
Стек
Rust · Tokio · PostgreSQL · MinIO · OPC UA · Docker · TensorRT · YOLO · GigE Vision
Оптическая инспекция после печи оплавления отбивала платы пачками, и оператор пересматривал под микроскопом почти каждую отметку, отделяя непропай от блика на галтели.
Решение
Собрали обучающий набор из кадров инспекции, размеченный операторами по типам: непропай, избыток припоя, смещение компонента, поднятый вывод, приподнятый чип-компонент. Классификатор встал вторым контуром после оптической инспекции и делит отметки на подтверждённый дефект, ложное срабатывание и спорный случай, сохраняя позиционное обозначение компонента и серийный номер платы. Порог уверенности вынесен в настройку и подобран отдельно по типам корпусов и по стороне платы.
Сдано 4
Передали классификатор с размеченным набором кадров
Карту типов дефектов по корпусам
Протокол сверки с операторами на контрольной выборке
Панель разбора спорных отметок
Как делали
Разбор отметок Разобрали, что отбивает оптическая инспекция после печи оплавления и что оператор потом пересматривает под микроскопом, отделяя непропай от блика на галтели. Собрали кадры инспекции по типам корпусов и по обеим сторонам платы.
Второй контур Решили ставить классификатор вторым контуром после оптической инспекции, не трогая её саму, и делить отметки на подтверждённый дефект, ложное срабатывание и спорный случай. За каждой отметкой договорились сохранять позиционное обозначение компонента и серийный номер платы, а порог уверенности вынести в настройку.
Сборка классификатора Собрали обучающий набор из кадров инспекции с разметкой операторов по типам — непропай, избыток припоя, смещение компонента, поднятый вывод, приподнятый чип-компонент, — затем обучили классификатор и сделали панель разбора спорных отметок. Порог подбирали отдельно по типам корпусов и по стороне платы.
Сверка с операторами Сверялись с операторами на контрольной выборке кадров и разбирали расхождения по типам корпусов и по сторонам платы. Провалом считали непропай, отнесённый к ложным срабатываниям: такая отметка обязана была уходить в спорные, а не сниматься с пересмотра оператором.
Передача операторам Передали классификатор с размеченным набором кадров, карту типов дефектов по корпусам, протокол сверки с операторами на контрольной выборке и панель разбора спорных отметок. Показали операторам, как разбирать спорные отметки и как менять порог по типу корпуса.
Архитектура
Кадры оптической инспекции забираются с установки файловым обменом через общий каталог — установка отдаёт снимки как есть, и вмешиваться в её цикл нельзя, — коллектор кладёт кадр в MinIO и ставит задачу в RabbitMQ вместе с позиционным обозначением компонента и серийным номером платы. Классификатор работает вторым контуром после инспекции и исполняется в ONNX Runtime на промышленном ПК у линии: модель экспортируется из PyTorch в ONNX, чтобы на линии не разворачивать обучающее окружение и не привязывать смену к версии обучающего фреймворка. Пороги уверенности хранятся в PostgreSQL настройкой по типам корпусов и стороне платы, а не константой в коде, поэтому отметки делятся на подтверждённый дефект, ложное срабатывание и спорный случай по разным порогам для разных корпусов. Разметка операторов по спорным отметкам возвращается в обучающий набор, панели разбора собираются в Grafana, сервисы на FastAPI поставляются в Docker.
Проверяется: протокол прогона на подложенных предметах
Детекция посторонних предметов на конвейере обогатительной фабрики
Задача, решение и что передали
Задача
Кусок анкерной крепи или зуб ковша приезжал с рядовым углём на ленту, и порыв полотна разбирали после остановки тракта.
Решение
Над лентой перед узлом дробления и над перегрузочным пунктом поставили камеры со стабильной подсветкой, кадры пишутся с привязкой к скорости ленты, поэтому предмет отслеживается от точки обнаружения до места съёма. Детектор обучен на металл крепи, зубья ковша, обрывки конвейерной ленты и куски дерева в потоке рядового угля и отделяет их от крупного куска породы. Событие уходит на пульт как рекомендация остановить тракт, решение остаётся за оператором, кадр и время сохраняются в журнале смены.
Сдано 4
Передали детектор посторонних предметов
Схему установки камер и подсветки над лентой
Протокол прогона на подложенных предметах
Журнал событий смены с кадрами
Как делали
Обследование тракта Прошли тракт от приёмного бункера до узла дробления с механиком и оператором пульта, подняли акты по порывам полотна и записи остановок. Разобрали, что именно приезжает с рядовым углём — металл анкерной крепи, зубья ковша, обрывки конвейерной ленты, дерево, — и посмотрели освещённость и запыление над лентой.
Архитектура съёмки Решили снимать в двух точках, перед узлом дробления и над перегрузочным пунктом, со своей стабильной подсветкой, а кадры привязать к сигналу скорости ленты, чтобы вести предмет от обнаружения до места съёма. От автоматической остановки тракта отказались: событие уходит на пульт рекомендацией, решение остаётся за оператором.
Сборка детектора Собрали и разметили кадры с потоком рядового угля отдельно по металлу крепи, зубьям ковша, обрывкам ленты и дереву, чтобы детектор отделял их от крупного куска породы. На стенде гоняли записи с ленты, затем подключили вывод на пульт и журнал смены с сохранением кадра и времени.
Проверка подложенными Прогон вели на подложенных предметах, которые запускали на ленту в известных точках с отметкой в протоколе; пропуск подложенного куска крепи или зуба ковша считался провалом прогона. Отдельно смотрели срабатывания на крупной породе и на мокром угле — поток ложных событий, при котором оператор перестаёт реагировать, тоже был поводом не принимать.
Передача пульту Передали детектор, схему установки камер и подсветки над лентой, протокол прогона на подложенных предметах и журнал событий смены с кадрами. Операторов провели по разбору события и отметке решения, механикам показали чистку оптики и порядок действий при смещении камеры.
Архитектура
Съём ведёт служба захвата на промышленном компьютере у тракта: камеры отдают кадры по GigE Vision, каждый кадр кладётся в кольцевой буфер разделяемой памяти с отметкой энкодера привода ленты, поэтому детектор и трекер считают положение предмета по полотну, а не по номеру кадра. Инференс вынесен отдельным процессом на TensorRT и общается со службой захвата локальным gRPC: подмена весов модели не трогает захват, а падение процесса инференса оставляет запись кадров живой. Метаданные события — время, класс, положение по ленте, ссылка на кадр — пишутся в ClickHouse, потому что журнал смены читается диапазонами по времени и по классу предмета, а сами изображения лежат в MinIO и не раздувают таблицу. Рекомендация остановить тракт уходит на пульт тегом OPC UA; при обрыве связи с пультом служба продолжает писать журнал локально и досылает накопленные события после восстановления.
Стек
C++ · gRPC · ClickHouse · MinIO · OPC UA · Docker Compose · TensorRT · OpenCV · GigE Vision
Вывод модели прогноза марочной прочности цемента в эксплуатацию
Задача, решение и что передали
Задача
Прочность партии узнавали по испытанию образцов, и режим помола правили уже после отгрузки цемента.
Решение
Свели витрину признаков по данным вращающейся печи и цементной мельницы: режим обжига, свободный оксид кальция в клинкере, тонкость помола и остаток на сите, доля минеральных добавок и гипса, нагрузка сепаратора. Прогноз считается на каждую партию помола и записывается вместе с версией модели и входными признаками, а лаборатория при испытании образцов проставляет факт в ту же запись. Расхождение прогноза с фактом разложено по маркам и виду добавки и выводится технологу на панель, порог срабатывания вынесен в настройку.
Сдано 4
Передали обученную модель и витрину признаков
Регламент дообучения по накопленным испытаниям
Протокол приёмочного сравнения с лабораторией
Панель контроля расхождений по маркам
Как делали
Обследование помола Разобрали нынешний порядок: прочность узнают по испытанию образцов, запись ложится в лабораторный журнал, а режим помола правят уже после отгрузки. Нашли, где лежат данные вращающейся печи и цементной мельницы — режим обжига, свободный оксид кальция в клинкере, тонкость помола и остаток на сите, доля добавок и гипса, нагрузка сепаратора.
Архитектура витрины Признаки вынесли в отдельную витрину с ключом партии помола, чтобы обучение и боевой расчёт шли по одним и тем же данным. Прогноз пишется вместе с версией модели и входными признаками, а лаборатория проставляет факт в ту же запись; порог срабатывания вынесли в настройку, а не зашили в расчёт.
Сборка расчёта Сначала собрали витрину и расчёт на архиве испытаний, потом расчёт на каждую партию помола с записью прогноза, потом панель расхождений в разрезе марок и вида добавки. На стенде прогоняли исторические партии, а боевой контур включили после того, как лаборатория начала проставлять факт в ту же запись.
Проверка лабораторией Приёмочное сравнение вели по партиям, которых модель не видела при обучении, и считали расхождение отдельно по маркам, чтобы редкая марка не пряталась в общем счёте. Не приняли бы, если бы на какой-то марке или на партиях с минеральной добавкой прогноз систематически уходил в одну сторону, а также если бы запись прогноза оставалась без версии модели и входных признаков.
Передача технологу Передали обученную модель и витрину признаков, регламент дообучения по накопленным испытаниям, протокол приёмочного сравнения и панель контроля расхождений по маркам. Технолог сам меняет порог и читает разрез по марке, лаборатория ведёт свою часть записи, дообучение запускается по регламенту силами предприятия.
Архитектура
Признаки собирает служба-сборщик: значения вращающейся печи и мельницы читаются с сервера OPC UA заводского историка и складываются в ClickHouse, откуда витрина признаков на партию помола материализуется запросом по интервалу помола. Сервис прогноза на Actix развёрнут в Kubernetes отдельным деплойментом, держит модель, выгруженную в ONNX, и исполняет её ONNX Runtime в своём процессе — обучение остаётся в питоновском контуре, а в продуктиве нет зависимости от версии обучающей библиотеки. Запись прогноза с версией модели и снимком входов лежит в PostgreSQL: лаборатория дописывает факт в ту же строку по номеру партии, и здесь нужна транзакция на обновление, а не поток событий. Закрытие партии публикуется в Kafka, чтобы панель расхождений и сборка обучающей выборки читали один поток независимо друг от друга; при остановке сервиса прогноза сообщения ждут в топике и разбираются после подъёма.
Вывод модели прогноза падежа по птичникам в эксплуатацию
Задача, решение и что передали
Задача
Отклонение по партии зоотехник замечал на утренней уборке, когда падёж уже тянул за собой конверсию корма и привес к сроку убоя.
Решение
Оценка считается по каждому птичнику на сутки вперёд по микроклимату, водопотреблению, поедаемости корма, суточному падежу и кривой привеса тура, партии разделены по кроссу и возрасту в днях. Расчёт идёт по расписанию после ночной выгрузки контроллеров, результат ложится в наряд бригадира с указанием птичника и признаков, поднявших оценку. На закрытии тура прогноз сверяется с фактом по кроссу и корпусу, а правки зоотехника собираются в набор для следующей версии модели.
Сдано 4
Передали модель и расчётный конвейер
Витрину признаков по птичникам и турам
Регламент дообучения после закрытия тура
Панель сверки с фактом по кроссам
Как делали
Обследование птичников Прошли по птичникам с зоотехником и бригадиром, посмотрели, как отклонение по партии замечают на утренней уборке и что попадает в наряд. Разобрали ночную выгрузку контроллеров, водопотребление, поедаемость корма, суточный падёж и кривую привеса тура, а также деление партий по кроссу и возрасту в днях.
Архитектура расчёта Оценку решили считать по каждому птичнику на сутки вперёд, а не по площадке целиком, признаки свели в витрину с ключом тура, корпуса и кросса. Запуск повесили на расписание после ночной выгрузки контроллеров, а результат кладём в наряд бригадира с указанием признаков, поднявших оценку, — без расшифровки корпус разбирать никто не пойдёт.
Сборка конвейера Сначала собрали выгрузку контроллеров и витрину по турам, потом расчётный конвейер, потом вывод в наряд бригадира и сверку с фактом на закрытии тура. На стенде прогнали закрытые туры и отдельно проверили поведение при неполной ночной выгрузке.
Проверка на турах Сверялись на закрытых турах, которых модель не видела, отдельно по кроссам и корпусам. Провалом считалось, если оценка поднималась там, где падёж шёл ровно, а корпуса с реальным отклонением оставались без пометки; не приняли бы и расчёт, который при неполной ночной выгрузке молчит вместо того, чтобы показать бригадиру пропуск данных.
Передача зоотехнику Передали модель и расчётный конвейер, витрину признаков по птичникам и турам, регламент дообучения после закрытия тура и панель сверки с фактом по кроссам. Зоотехник собирает свои правки в набор для следующей версии, бригадир читает наряд с признаками, переобучение после закрытия тура ведётся на стороне хозяйства.
Архитектура
Ночью служба выгрузки опрашивает контроллеры микроклимата и кормления по Modbus TCP через шлюз площадки и складывает ряды в гипертаблицу TimescaleDB — запись идёт только в конец по времени, а читают её окнами по птичнику, поэтому разбиение по времени здесь уместнее общей таблицы. Даг Airflow после выгрузки собирает витрину признаков по птичнику и суткам, дёргает сервис скоринга на FastAPI и кладёт оценку с перечнем поднявших её признаков в PostgreSQL, откуда и печатается наряд бригадира. Сервис скоринга держит модель LightGBM в памяти и обновляется отдельным образом в Docker Compose: смена версии модели не трогает конвейер выгрузки. Если контроллер корпуса не ответил, задача дага помечает птичник пропущенным и повторяется, остальные корпуса считаются, а сверка на закрытии тура читает те же строки прогноза и факта.
Пропущенный хомут или незащёлкнутый разъём находили на выходном контроле, а порой рекламацией с автосборочного конвейера.
Решение
Над монтажной плазой поставили камеру, кадр снимается по нажатию кнопки завершения операции и сверяется с электронным описанием ветки: наличие и позиция хомутов, защёлкивание разъёмов, маркировка гофры, установленный защитный колпачок. Сначала модель работала наблюдением рядом с контролёром на всех сменах, после сверки решений включили блокировку отметки поста по спорному кадру. Каждый кадр хранится с серийным номером жгута, версией модели и решением, поэтому разбор рекламации поднимает исходное изображение операции.
Сдано 4
Передали модель контроля комплектности
Привязку правил к электронному описанию веток
Протокол параллельного наблюдения по сменам
Журнал кадров с серийными номерами жгутов
Как делали
Обследование плазы Постояли на монтажном столе за контролёром и разобрали рекламации с автосборочного конвейера: пропущенный хомут, незащёлкнутый разъём, не тот защитный колпачок. Взяли электронные описания веток, чтобы понять, из чего складывается перечень проверяемых позиций, и посмотрели, как освещена плаза и как ложится жгут.
Архитектура контроля Камеру поставили над монтажной плазой, кадр снимаем по нажатию кнопки завершения операции, а не потоком, — так кадр привязан к операции и серийному номеру жгута. Правила не зашивали в модель: наличие и позиция хомутов, защёлкивание разъёмов, маркировка гофры и колпачок берутся из электронного описания ветки, чтобы новая ветка не требовала переделки.
Сборка модели Сначала сделали съём кадра по кнопке и хранилище с серийным номером, версией модели и решением, потом модель по позициям описания, потом отметку поста. Сперва работали наблюдением рядом с контролёром на всех сменах, блокировку отметки по спорному кадру включали отдельным шагом.
Проверка наблюдением Решения модели сравнивали с решениями контролёра по сменам, включая ночные, и каждое расхождение разбирали по сохранённому кадру. Провалом считался пропуск незащёлкнутого разъёма или отсутствующего хомута; блокировку не включили бы и в случае, если кадр мог попасть в журнал без серийного номера жгута или версии модели — тогда разбор рекламации не поднимает исходное изображение операции.
Передача контролёрам Передали модель контроля комплектности, привязку правил к электронному описанию веток, протокол параллельного наблюдения по сменам и журнал кадров с серийными номерами. Технолог заводит новую ветку сам через описание, контролёр знает порядок разбора спорного кадра и снятия блокировки поста.
Архитектура
На посту стоит промышленный компьютер: кнопка завершения операции приходит дискретным входом и публикуется в MQTT, служба захвата снимает кадр с камеры по GigE Vision и передаёт его сервису инференса на ONNX Runtime, куда модель PyTorch выгружена заранее. Правила сверки тянутся из электронного описания ветки по серийному номеру жгута, вердикт возвращается на терминал поста тем же брокером. Кадр кладётся в MinIO, а решение, версия модели и серийный номер — в PostgreSQL: изображению не место в реляционной таблице, а разбор рекламации поднимает кадр по ссылке из записи. Блокировка отметки поста работает с таймаутом: если сервис инференса не ответил, пост возвращается в режим наблюдения и операция отмечается признаком непроверенной, чтобы отказ узла не останавливал монтажный стол.
Мониторинг деградации модели прогноза сбора в теплицах
Задача, решение и что передали
Задача
Прогноз сбора по неделям считала обученная модель, но после смены гибрида и посадки нового оборота он расходился с фактом, и это замечали по сорванной отгрузке.
Решение
Прогноз и факт сбора по отделениям и рядам свели в витрину с ключом гибрида, оборота и даты посадки, сравнение разложено по срезам: гибрид, отделение, режим досветки, стадия культуры. Считаются расхождение прогноза с фактом и сдвиг входных признаков — температура и влажность по зонам, показатели растворного узла, счёт завязей с фотофиксации ряда. Срабатывание порога открывает задачу агроному и помечает срез на переобучение, а карточка версии модели хранит свою обучающую выборку.
Сдано 4
Передали витрину прогноза и факта
Набор метрик деградации по срезам
Регламент переобучения при смене гибрида
Панель мониторинга с историей версий модели
Как делали
Обследование прогноза Разобрали, почему прогноз сбора по неделям расходился с фактом: смена гибрида и посадка нового оборота меняли поведение культуры, а замечали это по сорванной отгрузке. Собрали, где лежат факт сбора по отделениям и рядам, температура и влажность по зонам, показатели растворного узла и счёт завязей с фотофиксации ряда.
Архитектура витрины Прогноз и факт свели в одну витрину с ключом гибрида, оборота и даты посадки — без этого ключа расхождение не разложить по срезам. Считаем и расхождение прогноза с фактом, и сдвиг входных признаков, разрезы задали по гибриду, отделению, режиму досветки и стадии культуры, а карточка версии модели хранит свою обучающую выборку.
Сборка мониторинга Сначала собрали витрину прогноза и факта с загрузкой признаков, потом метрики расхождения и сдвига по срезам, потом панель с историей версий и постановку задачи агроному при срабатывании порога. На стенде прогнали прошлые обороты, включая оборот со сменой гибрида, чтобы срез действительно помечался на переобучение.
Проверка на оборотах Проверяли на исторических оборотах: метрики должны были подняться там, где прогноз уже разошёлся с фактом, и молчать там, где сбор шёл ровно. Провалом считалось срабатывание, которое агроном не может отнести к конкретному срезу, и обратный случай — тихий монитор при смене гибрида; такую сборку не приняли бы.
Передача агроному Передали витрину прогноза и факта, набор метрик деградации по срезам, регламент переобучения при смене гибрида и панель мониторинга с историей версий модели. Агроном ведёт задачи по срабатываниям, а переобучение помеченного среза служба запускает сама по регламенту.
Архитектура
Прогноз и факт сбора грузятся в ClickHouse ключом гибрида, оборота и даты посадки, туда же ложатся ряды микроклимата по зонам и показатели растворного узла. Считалка метрик написана на C++ и читает срезы колоночными блоками через Apache Arrow: расхождение прогноза с фактом и сдвиг признаков — это агрегаты по столбцам на множестве срезов, и строчная выборка тянула бы все поля каждой записи ради двух колонок. Пороговое срабатывание уходит в NATS, а задача агроному и карточка версии модели с указателем на обучающую выборку в MinIO пишутся в PostgreSQL, где нужны транзакция и связь с исполнителем. Считалка запускается расписанием в Kubernetes, смежные сервисы дёргают её по gRPC; при падении запуска срез остаётся непомеченным и пересчитывается следующим прогоном по тем же данным, а ключ среза и даты не даёт задваивать задачу.
Интеграция учётной системы с государственной ветеринарной системой
Задача, решение и что передали
Задача
Ветеринарные сопроводительные документы на партии суточного молодняка, яйца и мяса оформляли в браузере вручную, а входящие на комбикорм гасили пачкой в конце недели.
Решение
Учётная система оформляет сопроводительный документ прямо из отгрузки: номер партии, площадка выращивания и вид продукции подставляются из документа, запрос уходит в очередь и повторяется до получения квитанции. Входящий документ на комбикорм гасится в момент приёмки по фактическому весу, расхождение с заявленным поднимается кладовщику. Справочник площадок, хозяйствующих субъектов и продукции сопоставлен с номенклатурой, поэтому новая позиция не заводится в госсистеме заново.
Сдано 4
Передали адаптер ветеринарной системы
Таблицу соответствия площадок и продукции
Очередь повторов с квитанциями
Регламент гашения на приёмке
Как делали
Обследование документов Посмотрели, как ветеринарный врач оформляет сопроводительные документы в браузере на партии суточного молодняка, яйца и мяса и как входящие на комбикорм гасятся пачкой. Сопоставили справочники площадок, хозяйствующих субъектов и видов продукции с номенклатурой учётной системы и нашли, где заводятся дубли.
Архитектура адаптера Оформление привязали к отгрузке: номер партии, площадка выращивания и вид продукции подставляются из документа, а не набираются заново. Запросы идут через очередь с повторами до получения квитанции, потому что госсистема бывает недоступна, а гашение входящего решили делать в момент приёмки по фактическому весу.
Сборка очереди Сначала таблица соответствия площадок и продукции, потом оформление документа прямо из отгрузки, потом очередь повторов с квитанциями и гашение на приёмке комбикорма с подъёмом расхождения кладовщику. На стенде отрабатывали недоступность госсистемы и повтор запроса, чтобы партия не получила два документа.
Проверка приёмок Прогнали отгрузки по всем видам продукции и приёмки комбикорма с заниженным и завышенным фактическим весом. Провалом считались два сопроводительных документа на одну партию и позиция, заведённая в госсистеме заново вместо сопоставленной; не приняли бы и гашение, при котором расхождение с заявленным весом проходит молча.
Передача ветврачу Передали адаптер ветеринарной системы, таблицу соответствия площадок и продукции, очередь повторов с квитанциями и регламент гашения на приёмке. Ветеринарный врач и кладовщик работают из своих документов, новая номенклатура сопоставляется в таблице без обращения к разработчику.
Архитектура
Отгрузка в учётной системе кладёт запрос на оформление в таблицу исходящих и в RabbitMQ, воркер на Gin забирает его и говорит с ветеринарной системой по SOAP её XSD-схемами. Прямой вызов из проведения документа отброшен: сессия госсистемы ограничена по частоте и отвечает не сразу, а повтор должен пережить перезапуск сервиса — поэтому состояние попытки и квитанция лежат в PostgreSQL, а очередь даёт отложенный повтор с растущей паузой и отдельной очередью разбора. Гашение входящего документа на комбикорм идёт в момент приёмки по фактическому весу из документа приёмки; расхождение с заявленным документ не гасит, а поднимает задачу кладовщику. Таблица соответствия площадок, хозяйствующих субъектов и продукции ведётся одной службой, поэтому новая номенклатура связывается с существующей записью госсистемы; контур поднимается Docker Compose, отказы и глубина очереди снимаются Prometheus.
Обмен учётной системы с торговыми площадками автозапчастей
Задача, решение и что передали
Задача
Прайс и остатки выкладывали на площадки файлами, а применимость к маркам и кроссы OEM-номеров жили в таблицах у менеджеров, поэтому одна деталь выходила под разными артикулами.
Решение
Собрали мастер-справочник деталей: OEM-номер, кроссы аналогов и применимость к маркам и моделям хранятся один раз и уходят на площадки одним каналом. Остатки и сроки поставки отдаются по расписанию, а заказ с площадки приходит в учётную систему документом с ключом по внешнему номеру. Отмена и возврат приходят тем же каналом, поэтому резерв под заказ снимается без ручной правки.
Сдано 4
Передали мастер-справочник деталей с кроссами
Коннекторы площадок
Расписание выгрузки остатков
Журнал заказов и возвратов
Как делали
Обследование прайсов Собрали, как прайс и остатки выкладываются на площадки файлами, и подняли таблицы менеджеров с применимостью к маркам и кроссами OEM-номеров. Свели дубли и увидели, отчего одна деталь выходила под разными артикулами: источник применимости у каждого менеджера был свой.
Архитектура справочника Решили держать мастер-справочник деталей один раз — OEM-номер, кроссы аналогов, применимость к маркам и моделям — и отдавать его на площадки одним каналом. Заказ с площадки приходит в учётную систему документом с ключом по внешнему номеру, отмена и возврат идут тем же каналом, чтобы резерв снимался без ручной правки.
Сборка коннекторов Сначала собрали мастер-справочник и свели в него таблицы менеджеров, потом выгрузку остатков и сроков поставки по расписанию, потом коннекторы площадок с приёмом заказов, отмен и возвратов. На стенде проверяли повторный заказ с тем же внешним номером и снятие резерва по возврату.
Проверка выгрузок Прогнали выгрузку по всем подключённым площадкам и заказы с отменами и возвратами. Провалом считались деталь, ушедшая на площадки под разными артикулами, и заказ, задвоенный по одному внешнему номеру; не приняли бы и остаток, остающийся в резерве после отмены с площадки.
Передача менеджерам Передали мастер-справочник деталей с кроссами, коннекторы площадок, расписание выгрузки остатков, журнал заказов и возвратов и инструкцию заведения новой позиции. Менеджер заводит деталь один раз в справочнике, дальше она расходится на площадки сама.
Архитектура
Мастер-справочник деталей лежит в PostgreSQL: OEM-номер, кроссы аналогов и применимость к маркам и моделям хранятся отдельными связями, а не строками прайса, применимость грузится из TecDoc и правится вручную только поверх. Поиск и сопоставление артикулов поставщиков вынесены в OpenSearch — по строке артикула нужны разбор и нечёткое совпадение, чего индекс реляционной таблицы не даёт, а мастер-запись остаётся в PostgreSQL источником. Выгрузка остатков и сроков поставки собирает YML-фиды по расписанию и отдаёт их коннекторам площадок, каждый со своим ограничителем частоты на счётчиках Redis; заказ, отмена и возврат приходят с площадки в NATS и ложатся документом с ключом по внешнему номеру, поэтому повтор канала резерв не задваивает. Сервисы на Tokio развёрнуты в Kubernetes по одному коннектору на площадку: падение коннектора останавливает обмен только со своей площадкой, накопленные события ждут в очереди.
Целлюлозно-бумажное производствоКорпоративные системы
Проверяется: паспорт качества рулона
Съём данных с бумагоделательной машины и паспорт тамбура
Задача, решение и что передали
Задача
Граммаж и влажность выписывались сушильщиком в тетрадь раз в смену, а причину обрыва полотна восстанавливали по памяти уже на разборе.
Решение
Сняли с АСУ ТП скорость, граммаж и влажность по ходу полотна и привязали значения к номеру тамбура по времени намотки. Обрыв фиксируется на пульте с указанием секции и причины из справочника, а при раскрое на продольно-резательном станке каждый рулон наследует свой отрезок кривых. Сменный рапорт собирает баланс массы: наработка, брак, оборотный брак.
Сдано 4
Передали схему тегов OPC
Журнал обрывов с причинами
Паспорт качества рулона
Сменный рапорт с балансом массы
Как делали
Обследование машины Прошли машину по ходу полотна с сушильщиком и технологом: где снимаются скорость, граммаж и влажность, что попадает в тетрадь раз в смену и как причина обрыва восстанавливается на разборе. Подняли теги АСУ ТП и посмотрели, чем размечается намотка тамбура и как ведётся раскрой на продольно-резательном станке.
Архитектура привязки Значения решили привязывать к номеру тамбура по времени намотки, чтобы кривые принадлежали тамбуру, а не смене целиком. Обрыв фиксируется на пульте с секцией и причиной из справочника — свободный текст для разбора не годился, — а при раскрое каждый рулон наследует свой отрезок кривых, поэтому паспорт выписывается на рулон.
Сборка съёма Сначала съём тегов и привязка к тамбуру, потом журнал обрывов с секцией и причиной, потом наследование кривых рулонами при раскрое и сменный рапорт с балансом массы — наработка, брак, оборотный брак. На стенде гоняли записи с машины, включая остановы и смену тамбура на ходу.
Проверка баланса Паспорт рулона сверяли с тетрадью сушильщика и записями АСУ ТП по тем же сменам и проверяли сходимость баланса массы в сменном рапорте. Провалом считались тамбур без привязанных кривых и рулон, которому при раскрое достался чужой отрезок; не приняли бы и обрыв, попавший в журнал без секции и причины.
Передача сушильщикам Передали схему тегов OPC, журнал обрывов с причинами, паспорт качества рулона, сменный рапорт с балансом массы и инструкцию сушильщика. Сушильщик отмечает обрыв с пульта, технолог поднимает кривые по номеру рулона на разборе, справочник причин ведёт технологическая служба.
Архитектура
Сборщик на C++ подписывается на теги скорости, граммажа и влажности на сервере OPC UA АСУ ТП, дискретные сигналы секций читаются по Modbus TCP, ряды пишутся в ClickHouse. Колоночное хранилище выбрано потому, что паспорт тамбура и рулона — это выборка интервала по нескольким тегам за время намотки, и такой запрос читает только нужные столбцы, тогда как строчная таблица тянула бы всю запись отсчёта целиком. Привязка значений к тамбуру идёт по времени намотки, а при раскрое на продольно-резательном станке рулон наследует свой отрезок кривых по границам реза; номера тамбуров, обрывы с секцией и причиной из справочника и сменный рапорт с балансом массы лежат в PostgreSQL. Пульт сушильщика — приложение на Qt, оно фиксирует обрыв через сборщик; при недоступности базы сборщик буферизует отсчёты на диске станции и досылает их после подъёма, развёртывание и обновление раскатываются Ansible, тренды собраны в Grafana.
Стек
C++ · Qt · ClickHouse · PostgreSQL · OPC UA · Ansible · Modbus TCP · Grafana
Падёж, привес и расход корма считали по бумажным ведомостям птичниц, поэтому конверсию корма по партии выводили уже после сдачи на убой.
Решение
Партия суточного молодняка заводится на корпус: посадка, ветеринарные обработки, выбытие и сдача пишутся на одну карточку с номером корпуса и датой посадки. Птичница отмечает падёж и списание корма с терминала в корпусе, контрольное взвешивание вносится по группам, а выдача из кормушек подтягивается с весов кормозагрузчика. Сохранность и конверсия корма считаются на дату по данным этой карточки, а не сводятся после закрытия партии.
Сдано 4
Передали карточку партии
Журнал падежа и ветеринарных обработок
Ведомость контрольных взвешиваний
Отчёт по конверсии корма
Как делали
Обследование корпусов Собрали бумажные ведомости птичниц и посмотрели, как из них выводится конверсия корма — уже после сдачи на убой. Прошли по корпусам: где встанут терминалы, как списывается корм, как проводится контрольное взвешивание и что отдают весы кормозагрузчика.
Архитектура карточки Партию решили вести карточкой на корпус с датой посадки, чтобы посадка, ветеринарные обработки, выбытие и сдача жили на одной записи. Падёж и списание корма птичница отмечает с терминала в корпусе, выдача из кормушек подтягивается с весов кормозагрузчика, а сохранность и конверсия считаются на дату по этой карточке.
Сборка учёта Сначала карточка партии и журнал падежа с ветеринарными обработками, потом терминал в корпусе и обмен с весовыми контроллерами, потом контрольные взвешивания по группам и отчёт по конверсии корма. На стенде провели партию от посадки до сдачи, в том числе с правкой ошибочной отметки птичницы.
Проверка по ведомостям Прогнали закрытые партии по бумажным ведомостям и сверили сохранность и конверсию с прежним расчётом. Провалом считались расхождение расхода корма по карточке с выдачей по весам кормозагрузчика и падёж, отмеченный без корпуса и даты; не приняли бы и карточку, где конверсия появляется только на закрытии партии.
Передача птичницам Передали карточку партии, журнал падежа и ветеринарных обработок, ведомость контрольных взвешиваний, отчёт по конверсии корма и инструкцию птичницы. Птичница работает с терминала в корпусе, зоотехник смотрит конверсию по партии на дату, не сводя ведомости.
Архитектура
Терминал в корпусе — страница на React с локальным кэшем: птичница отмечает падёж и списание корма, каждое событие уходит с клиентским идентификатором, поэтому повторная отправка после провала сети в корпусе вторую запись не создаёт. Бэкенд на NestJS принимает события в PostgreSQL на карточку партии с номером корпуса и датой посадки; выдача из кормушек приходит с весовых контроллеров кормозагрузчика — шлюз читает их по Modbus TCP и публикует в MQTT, откуда подписчик раскладывает вес по корпусам и партиям. Сохранность и конверсия корма считаются запросом по строкам событий с материализованным представлением на дату, а не хранятся полем в карточке: исправление задним числом должно менять и показатель. Номенклатура и списания уходят в 1С:Предприятие обменом, Redis держит справочники и сессии терминалов, контур поднимается Docker Compose на серверe площадки.
Кубатуру каждой машины считали по бумажным спецификациям на весовой, а расхождение с поставщиком разбирали по фотографиям штабеля.
Решение
Обмер ведётся на площадке в терминале: диаметр в верхнем отрезе, длина с припуском и сорт по сортименту, объём считается по таблицам ГОСТ прямо в момент замера. Партия получает номер, поставщика и делянку, сортименты расходятся по штабелям с адресом площадки и остаются прослеживаемыми до раскряжёвки. Приёмка закрывается только при сошедшемся объёме по накладной и по обмеру, иначе поднимается акт разногласий.
Сдано 4
Передали приёмный акт с обмером
Реестр партий по делянкам
Акт разногласий по объёму
Схему адресов штабелей
Как делали
Обследование приёмки Постояли на весовой и на приёмной площадке: как считается кубатура по бумажным спецификациям и как расхождение с поставщиком разбирается по фотографиям штабеля. Разобрали, что замеряет приёмщик — диаметр в верхнем отрезе, длину с припуском, сорт по сортименту — и по каким таблицам ГОСТ это переводится в объём.
Архитектура обмера Обмер решили вести в терминале прямо на площадке, чтобы объём считался в момент замера, а не переписывался позже. Партия получает номер, поставщика и делянку, сортименты расходятся по штабелям с адресом площадки, а приёмка закрывается только при сошедшемся объёме по накладной и по обмеру — иначе поднимается акт разногласий, и обойти это в интерфейсе нельзя.
Сборка терминала Сначала справочник сортиментов и таблицы ГОСТ с расчётом объёма, потом терминал приёмщика и приёмный акт, потом адреса штабелей, акт разногласий и прослеживаемость партии до раскряжёвки. На стенде прогоняли машины с расхождением по накладной и с частичной выгрузкой.
Проверка кубатуры Принятые машины пересчитали по бумажным спецификациям и сверили объём с обмером в терминале. Провалом считалась приёмка, закрытая при несошедшемся объёме без акта разногласий, и сортимент, ушедший в штабель без адреса, — такую партию нельзя проследить до раскряжёвки.
Передача приёмщикам Передали приёмный акт с обмером, реестр партий по делянкам, акт разногласий по объёму, схему адресов штабелей и инструкцию приёмщика. Приёмщик работает с терминала на площадке, мастер поднимает партию по делянке и видит, в каких штабелях лежат её сортименты.
Архитектура
Обмер ведётся на Android-терминале приёмщика: диаметр в верхнем отрезе, длина с припуском и сорт вводятся у штабеля, объём считается по таблицам ГОСТ 2708 прямо на терминале из локальной SQLite, поэтому площадка без связи не останавливается. Синхронизация с сервером Spring Boot идёт через RabbitMQ, ключом служат номер партии и порядковый номер сортимента в ней, повтор синхронизации сортимент не задваивает. Партии, делянки, адреса штабелей и акты лежат в PostgreSQL, вес машины приходит с автомобильных весов по Modbus RTU через шлюз и связывается с партией по номеру накладной. Приёмка закрывается транзакцией, в которой объём по накладной и по обмеру сходятся, иначе документ остаётся открытым и поднимается акт разногласий; серверная часть поднимается Docker Compose, справочники сортиментов раздаются терминалам при синхронизации.
Выход филе и отходов сводили в конце смены по тетрадям мастеров, поэтому привязать потери к судовой партии сырца не получалось.
Решение
Судовая партия принимается по промысловому журналу и накладной с районом вылова и датой выгрузки, дальше номер партии идёт до короба. На каждой операции — дефростация, разделка, шкуросъём, заморозка, глазирование — вес входа и выхода снимается с цеховых весов, поэтому выход и потери считаются по операции, а не по цеху за смену. Этикетка короба печатается с номером партии и сменой, глазурь учитывается отдельной строкой от массы нетто.
Сдано 4
Передали карту партии сырца
Ведомость выхода по операциям
Журнал приёмки уловов
Макет этикетки короба
Как делали
Обследование цеха Разобрали, как выход сводится по тетрадям мастеров на закрытии смены и почему потери не привязываются к судовой партии сырца. Прошли цех по операциям — дефростация, разделка, шкуросъём, заморозка, глазирование, — посмотрели цеховые весы и порядок приёмки партии по промысловому журналу и накладной.
Архитектура партии Номер судовой партии решили вести до короба, а вес входа и выхода снимать на каждой операции с цеховых весов, чтобы выход и потери считались по операции, а не по цеху за смену. Глазурь учитывается отдельной строкой от массы нетто, этикетка короба печатается с номером партии и сменой — иначе принадлежность короба восстанавливается только по памяти мастера.
Сборка взвешиваний Сначала приёмка судовой партии с районом вылова и датой выгрузки, потом взвешивания по операциям и ведомость выхода, потом печать этикетки короба и связка короба с партией. На стенде прогнали смену с перекрытием партий на одной линии, чтобы вход и выход не смешались.
Проверка выхода Ведомость выхода по операциям сверили с тетрадями мастеров за те же смены и прошли по коробам обратно до судовой партии. Провалом считались короб, по этикетке которого нельзя дойти до партии сырца, и операция, где сумма выхода и отходов не сходится с весом входа; не приняли бы и глазурь, попавшую в массу нетто.
Передача мастерам Передали карту партии сырца, ведомость выхода по операциям, журнал приёмки уловов, макет этикетки короба и регламент взвешиваний. Мастер ведёт взвешивания по регламенту с цеховых весов, технолог видит выход и потери по операции, не дожидаясь закрытия смены.
Архитектура
Цеховые весы читает шлюз по Modbus TCP и публикует взвешивания в MQTT темой на весовой пост, сервис учёта на Ktor подписан на эти темы. Очередь стоит вместо прямого вызова из шлюза: весы отдают показания непрерывно, сеть цеха рвётся, а брокер удерживает сообщение и позволяет перезапустить сервис учёта без потери взвешиваний. Вход и выход каждой операции — дефростация, разделка, шкуросъём, заморозка, глазирование — пишутся отдельными строками в PostgreSQL с номером судовой партии, поэтому выход и потери считаются по операции и прослеживаются до короба, а глазурь ведётся строкой отдельно от массы нетто. Этикетка короба печатается сервисом печати в ZPL с кодом GS1-128, где зашиты номер партии и смена; контур поднимается Docker Compose на цеховом сервере, при его недоступности шлюз копит взвешивания локально.
Планирование смен сборщиков и учёт выработки по грядам
Задача, решение и что передали
Задача
Наряды звеньям на сбор писали мелом на доске, а выработку по каждой гряде сводили в тетрадь бригадира и переносили в расчёт в конце недели.
Решение
Смена планируется по отделениям теплицы: звено получает задание на ряды и гряды с нормой сбора по культуре и стадии, доступность людей проверяется по табелю и допускам к работам с растворами. Сборщик отмечает лотки на терминале у выхода из ряда, вес приходит с паллетных весов на сортировке и раскладывается по звену и гряде. Съём с квадратного метра считается по гряде на дату, а сдельный наряд закрывается тем же документом и уходит в табель.
Сдано 4
Передали сменное задание звену
Наряд на сдельную оплату
Ведомость съёма по грядам
Табель с допусками
Как делали
Обследование теплицы Посмотрели, как наряды звеньям пишутся мелом на доске и как выработка по грядам сводится в тетрадь бригадира. Разобрали норму сбора по культуре и стадии, табель и допуски к работам с растворами, а также где стоят терминалы у выхода из ряда и паллетные весы на сортировке.
Архитектура задания Смену планируем по отделениям теплицы заданием звену на ряды и гряды с нормой сбора, а доступность людей проверяем по табелю и допускам — без допуска к работам с растворами человек в задание не попадает. Отметку лотков и вес с паллетных весов связали одним ключом гряды и звена, чтобы съём с квадратного метра считался по гряде на дату, а сдельный наряд закрывался тем же документом.
Сборка нарядов Сначала сменное задание по отделениям с нормой сбора, потом терминал у выхода из ряда и приём веса с паллетных весов, потом ведомость съёма по грядам и сдельный наряд с выгрузкой в табель. На стенде прогнали смену со сменой звена посреди ряда и с довеской после сортировки.
Проверка выработки Сверили выработку с тетрадями бригадира по отработанным сменам и сложили вес по грядам против веса, принятого на сортировке. Провалом считались лотки, не сошедшиеся с паллетным весом по звену, и задание, выданное сборщику без действующего допуска; не приняли бы и наряд, не попадающий в табель.
Передача бригадиру Передали сменное задание звену, наряд на сдельную оплату, ведомость съёма по грядам, табель с допусками и инструкцию бригадира. Бригадир планирует смену сам, расчёт получает закрытые наряды выгрузкой, доска с мелом из работы вышла.
Архитектура
Планировщик смен на Spring Boot собирает задание звену по отделениям, рядам и грядам с нормой сбора по культуре и стадии и проверяет доступность людей по табелю и допускам к работам с растворами. Сборщик отмечает лотки на Android-терминале у выхода из ряда, паллетные весы на сортировке читает шлюз по Modbus TCP и публикует взвешивания в RabbitMQ, вес раскладывается по звену и гряде сопоставлением по времени и открытому заданию. Очередь стоит между весами и учётом, чтобы взвешивание не ждало ответа расчётной части и не терялось при её перезапуске; задания, отметки и веса лежат в PostgreSQL, съём с квадратного метра по гряде считается запросом на дату. Закрытый сдельный наряд уходит в 1С:ЗУП обменом по расписанию, Redis кэширует справочники культур и норм для терминалов, контур поднимается Docker Compose.
Съём данных с термопластавтоматов и учёт брака по гнёздам
Задача, решение и что передали
Задача
Счётчик циклов переписывали в сменный журнал наладчика, а брак списывали на пресс-форму целиком, без разбора, какое гнездо его даёт.
Решение
С контроллеров термопластавтоматов снимаются циклы, время цикла и остановы, значения привязываются к производственному заказу, пресс-форме и номеру наладки. Оператор отмечает брак по коду дефекта и номеру гнезда на терминале у машины, поэтому съём по форме раскладывается на гнёзда, а не на партию целиком. Наладка закрывает прежний прогон и открывает новый с протоколом первого изделия, а стойкость формы ведётся по циклам до планового обслуживания.
Сдано 4
Передали сменный отчёт по прогонам
Журнал брака по гнёздам
Протокол первого изделия
Карточку стойкости пресс-формы
Как делали
Обследование участка Прошли участок с наладчиком: как счётчик циклов переписывается в сменный журнал и почему брак списывается на пресс-форму целиком. Подняли теги контроллеров термопластавтоматов — циклы, время цикла, остановы — и разобрали, чем размечается наладка и что входит в протокол первого изделия.
Архитектура привязки Значения контроллеров привязали к производственному заказу, пресс-форме и номеру наладки, а брак решили отмечать по коду дефекта и номеру гнезда с терминала у машины — иначе съём по форме на гнёзда не разложить. Наладка закрывает прежний прогон и открывает новый, стойкость формы ведётся по циклам до планового обслуживания.
Сборка съёма Сначала съём тегов с контроллеров и привязка к заказу и наладке, потом терминал у машины с кодами дефектов и номерами гнёзд, потом протокол первого изделия, сменный отчёт по прогонам и карточка стойкости формы, потом обмен с учётной системой. На стенде прогоняли смену наладки на ходу и остановы машины.
Проверка по гнёздам Циклы по контроллерам сверили со сменным журналом наладчика и разложили брак по гнёздам на прогонах, где дефект был известен заранее. Провалом считались брак, оставшийся без номера гнезда, и прогон, продолженный после наладки без протокола первого изделия; не приняли бы и счётчик циклов, разошедшийся с контроллером по прогону.
Передача наладчикам Передали сменный отчёт по прогонам, журнал брака по гнёздам, протокол первого изделия, карточку стойкости пресс-формы и схему тегов контроллеров. Оператор отмечает брак с терминала, наладчик открывает прогон и ведёт стойкость формы, инструментальная служба видит подход к плановому обслуживанию.
Архитектура
Съём ведёт коллектор на Ktor: новые контроллеры отдают циклы, время цикла и остановы по OPC UA, машины постарше — файловым интерфейсом Euromap 63, оба источника нормализуются одним адаптером в общую запись прогона. Ряды циклов ложатся в ClickHouse, а производственные заказы, наладки, прогоны и брак — в PostgreSQL: посменные агрегаты по времени цикла читаются колоночно, тогда как прогон, протокол первого изделия и карточка формы требуют транзакции и связей. Оператор отмечает брак кодом дефекта и номером гнезда на терминале у машины, отметка идёт в MQTT и привязывается к открытому прогону, поэтому съём по форме раскладывается на гнёзда, а не на партию целиком. Наладка закрывает прежний прогон и открывает новый, стойкость формы считается циклами по её записям; коллектор буферизует отсчёты локально при недоступности базы, контур поднимается Docker Compose, дашборды прогонов собраны в Grafana.
Кабинет поставщика инкубационного яйца и комбикорма
Задача, решение и что передали
Задача
Ветеринарные свидетельства и качественные удостоверения на партию поставщики присылали почтой, а о результате входного контроля узнавали, когда машину разворачивали с проходной.
Решение
В кабинете поставщик заявляет партию к поставке: номер партии, площадку-отправитель, ветеринарное свидетельство и качественное удостоверение с показателями по влажности и протеину. Заявка сверяется с графиком закладки инкубатория и договорным объёмом, а на въезде партия связывается с весовой и результатом входного лабораторного контроля. Отклонение по показателю или просроченное ветеринарное свидетельство останавливает приёмку, и поставщику возвращается акт расхождения по партии.
Сдано 4
Кабинет поставщика
Сверка партии с графиком закладки
Связка с весовой и лабораторией входного контроля
Акт расхождения по партии
Как делали
Обследование поставок Разобрали, как ветеринарные свидетельства и качественные удостоверения приходят почтой и почему машину разворачивают с проходной. Собрали, что проверяет входной контроль — влажность, протеин — и как заявленная партия соотносится с графиком закладки инкубатория и договорным объёмом.
Архитектура заявки Заявку партии решили сверять с графиком закладки и договорным объёмом до выезда машины, а не на проходной. Партия связывается с весовой и результатом входного лабораторного контроля по своему номеру, а отклонение по показателю или просроченное ветеринарное свидетельство останавливает приёмку с возвратом поставщику акта расхождения.
Сборка кабинета Сначала кабинет с заявкой партии, ветеринарным свидетельством и качественным удостоверением, потом сверка с графиком закладки и договорным объёмом, потом связка с весовой и лабораторией и акт расхождения по партии. На стенде прогоняли партию с просроченным свидетельством и показателем на границе допуска.
Проверка приёмок Прогнали заявки поставщиков вместе со службой входного контроля и сверили результаты лаборатории с решением по приёмке. Провалом считались партия, допущенная к приёмке с просроченным ветеринарным свидетельством, и остановка приёмки без акта расхождения, который поставщик видит у себя; не приняли бы и заявку, прошедшую мимо графика закладки.
Передача снабжению Передали кабинет поставщика, сверку партии с графиком закладки, связку с весовой и лабораторией входного контроля, акт расхождения по партии и регламент допуска поставщика. Снабжение подключает поставщика по регламенту, лаборатория и весовая работают со своими данными.
Архитектура
Кабинет на Symfony ведёт учётные записи поставщиков и карточку заявленной партии в PostgreSQL, сканы ветеринарного свидетельства и качественного удостоверения кладутся в MinIO, срок действия свидетельства хранится полем и проверяется до въезда. График закладки инкубатория и договорный объём приходят из 1С:ERP сообщениями RabbitMQ, поэтому сверка заявки идёт по своей копии, а не походом в учётную систему на каждый экран. Состояние партии — заявлена, допущена, взвешена, проверена, не допущена — ведётся одной записью с номером версии и оптимистичной блокировкой: в неё пишут кабинет, весовая и лаборатория входного контроля, и без версии их правки затирали бы друг друга. Отклонение по влажности или протеину и просроченное свидетельство переводят партию в отказ и формируют поставщику акт расхождения; вход разграничен Keycloak, контур поднимается Docker Compose.
Портал согласования извещений об изменении конструкторской документации
Задача, решение и что передали
Задача
Извещение об изменении по строительному заказу ходило по отделам бумажной папкой, и участок узнавал о замене узла, когда деталь уже стояла на секции.
Решение
Портал ведёт извещение от предложения до внедрения: строительный номер заказа, затронутые чертежи и секции корпуса, причина и этап постройки, с которого изменение действует. Маршрут согласования собирается по затронутым службам — конструкторы, технолог, снабжение, ОТК, нормоконтроль, — и каждое замечание привязано к чертежу и его версии. После утверждения извещение расходится по цехам и участкам, а в карточке секции видно действующую редакцию чертежа.
Сдано 4
Портал согласования
Маршруты по затронутым службам
Привязка замечаний к чертежу и версии
Реестр действующих редакций по секциям
Как делали
Обследование извещений Прошли путь бумажной папки извещения об изменении по строительному заказу и разобрали случай, когда участок узнал о замене узла после установки детали на секцию. Собрали, кто участвует в согласовании — конструкторы, технолог, снабжение, ОТК, нормоконтроль — и как чертежи с их версиями приходят из конструкторского обмена.
Архитектура маршрутов Извещение ведём от предложения до внедрения с указанием строительного номера заказа, затронутых чертежей и секций корпуса и этапа постройки, с которого изменение действует. Маршрут собирается по затронутым службам, а не по общему списку, каждое замечание привязано к чертежу и его версии, а в карточке секции держим действующую редакцию.
Сборка портала Сначала карточка извещения с привязкой к заказу, чертежам и секциям, потом сборка маршрута по затронутым службам, потом замечания по версиям чертежа, утверждение и рассылка по цехам и участкам. На стенде прогоняли извещение, затрагивающее несколько секций, и возврат на доработку с середины маршрута.
Проверка редакций Прогнали прошлые изменения вместе с нормоконтролем и сверили действующие редакции в карточках секций с конструкторским обменом. Провалом считались утверждённое извещение, не дошедшее до участка с затронутой секцией, и замечание без привязки к версии чертежа; не приняли бы и карточку секции, показывающую отменённую редакцию.
Передача нормоконтролю Передали портал согласования, маршруты по затронутым службам, привязку замечаний к чертежу и версии, реестр действующих редакций по секциям и инструкцию нормоконтроля. Нормоконтроль правит состав маршрута сам, участок смотрит действующую редакцию в карточке секции.
Архитектура
Извещение ведётся агрегатом в PostgreSQL: строительный номер заказа, затронутые чертежи и секции корпуса, причина и этап внедрения; маршрут согласования собирает процесс Camunda по множеству затронутых служб, поэтому состав согласующих берётся из данных извещения, а не зашит в код. Версии чертежей хранятся неизменяемыми строками, замечание ссылается на конкретную версию — иначе после перевыпуска чертежа согласование указывало бы на документ, которого рецензент не видел. Метаданные и геометрия приходят обменом с системой конструкторской подготовки в формате STEP AP242 и складываются в MinIO, ссылка держится в карточке чертежа. После утверждения публикатор рассылает извещение по цехам и участкам событиями RabbitMQ и обновляет действующую редакцию в карточке секции; фронт на React, сервисы Spring Boot развёрнуты в Kubernetes, повтор публикации гасится ключом извещения и версии.
Целлюлозно-бумажное производствоМиграция на СУБД и Linux
Проверяется: протокол сверки значений
Перевод отчётности бумагоделательного производства на отечественную BI-платформу
Задача, решение и что передали
Задача
Сводки по варке, отбелке и съёму с бумагоделательной машины собирались в зарубежной BI, где каждая формула жила внутри своего дашборда.
Решение
Показатели вынесены в витрину на отечественной СУБД: съём с машины, обрывность полотна и доля рулонного брака описаны в слое витрины, а не в отчётах. Загрузка из системы сбора технологических параметров и учётной системы поставлена на расписание, дашборды пересобраны на отечественной платформе под Linux. Значения за прошедшие смены сверены с выгрузками прежней BI.
Сдано 4
Витрина показателей
Пересобранные дашборды
Протокол сверки значений за период
Регламент загрузки
Как делали
Обследование дашбордов Собрали дашборды прежней BI по варке, отбелке и съёму с бумагоделательной машины и выписали формулы, жившие внутри каждого отчёта. Разобрали источники — систему сбора технологических параметров и учётную систему — и нашли показатели, которые в разных отчётах считались по-разному.
Архитектура витрины Показатели вынесли в витрину на отечественной СУБД: съём с машины, обрывность полотна и доля рулонного брака описаны в слое витрины, а не в отчётах, чтобы формула не расходилась между дашбордами. Загрузку из системы сбора технологических параметров и учётной системы поставили на расписание, дашборды пересобираем на отечественной платформе под Linux.
Сборка загрузки Сначала витрина показателей и загрузка из обоих источников, потом пересборка дашбордов по варке, отбелке и съёму, потом регламент загрузки и разбор пропусков в данных. На стенде считали витрину на исторических сменах и сверяли её с выгрузками прежней BI.
Проверка значений Значения за прошедшие смены сверили с выгрузками прежней BI по каждому показателю. Провалом считалось расхождение съёма, обрывности или доли рулонного брака хотя бы на одной смене, а также показатель, который в новом дашборде считается не по описанию витрины, — такой дашборд в работу не отдавали.
Передача технологам Передали витрину показателей, пересобранные дашборды, протокол сверки значений за период и регламент загрузки. Технолог собирает новый разрез из витрины сам, служба сопровождения следит за загрузкой по расписанию.
Архитектура
Загрузку ведёт сервис на Spring Boot: параметры варки, отбелки и съёма с машины читаются из системы сбора технологических параметров по OPC UA, документы учётной системы приходят обменом, всё складывается в ClickHouse, а справочники и состояние загрузок лежат в PostgreSQL. Показатели — съём, обрывность полотна, доля рулонного брака — описаны моделями dbt в слое витрины, а не формулами внутри дашбордов: определение одно, и новый дашборд его не переписывает заново. Расписание загрузок ведёт Apache Airflow, повторный запуск идёт по ключу смены и срез не задваивает, при пропуске окна догоняющий запуск читает тот же интервал. Дашборды пересобраны на Visiology под Astra Linux и читают витрину напрямую — колоночное хранилище выбрано потому, что сводки по сменам и машинам это агрегаты по времени за длинные периоды; значения за прошедшие смены сверены с выгрузками прежней платформы.
Стек
Java · Spring Boot · ClickHouse · PostgreSQL · OPC UA · Astra Linux · Apache Airflow · Visiology · dbt
Сборка системы раскроя корпусных деталей под доверенные ОС
Задача, решение и что передали
Задача
Разбивка корпуса на секции, раскрой листа и передача плазовых данных на резку велись в пакете, работавшем только под зарубежной ОС.
Решение
Клиент пересобран под целевые дистрибутивы доверенных ОС, закрытые библиотеки заменены открытыми аналогами, оставшиеся зависимости выписаны отдельным перечнем. Вывод раскроя переведён на системный сервер печати и плоттер, а выгрузка на машину термической резки проверена в цехе на контрольных деталях. Установка и обновление собраны штатными пакетами, функциональные проверки прогнаны на каждом целевом дистрибутиве.
Сдано 4
Пакеты под целевые дистрибутивы
Перечень заменённых библиотек
Протокол функциональных проверок
Инструкция установки
Как делали
Разбор зависимостей Разобрали клиент по зависимостям — что тянет закрытые библиотеки, что завязано на печать и на выгрузку плазовых данных; с технологами прошли путь от разбивки корпуса на секции до задания машине термической резки. Выяснили, какие форматы карт раскроя и постпроцессоры используются в цехе.
Замена библиотек Закрытые библиотеки заменили открытыми аналогами там, где поведение воспроизводится, а оставшиеся вынесли отдельным перечнем зависимостей, а не спрятали в сборку. Печать и вывод на плоттер перевели на системный сервер печати, чтобы не тащить собственный драйвер под каждый дистрибутив.
Пересборка клиента Пересобирали клиент дистрибутив за дистрибутивом, начиная с модулей разбивки на секции и раскроя листа, затем взялись за вывод карт раскроя и выгрузку на резку. Установку и обновление свели к штатным пакетам, сборку и функциональные проверки повесили на конвейер.
Контрольные детали Прогнали функциональные проверки на каждом целевом дистрибутиве и раскроили контрольные детали в цехе — от карты раскроя до реза на машине термической резки. Несовпадение геометрии контрольной детали с плазовыми данными или отказ выгрузки на резку считались провалом, и такой пакет в цех не уходил.
Передача технологам Отдали пакеты под целевые дистрибутивы, перечень заменённых библиотек, протокол функциональных проверок и инструкцию установки; с технологами прошли раскрой и вывод на плоттер. Служба сопровождения сама ставит и обновляет клиент на рабочих местах.
Архитектура
Клиент раскроя пересобран на Electron с геометрическим ядром OpenCascade вместо закрытых библиотек: разбивка корпуса на секции и раскладка листа считаются в основном процессе, интерфейс — в окне рендерера. Проекты раскроя лежат локально в SQLite, а согласованные плазовые данные и справочник листа — в PostgreSQL на сервере бюро; локальная база оставлена потому, что раскрой правят в цеховой сети с обрывами и заготовка не должна пропадать между сохранениями. Вывод на плоттер и печать идут через системный сервер CUPS, а задание на машину термической резки отдаётся файлом ESSI в сетевую папку стойки — стойка принимает задание файлом и сетевого интерфейса не имеет, поэтому обмен оставлен файловым. Сборка описана в GitLab CI: из одного дерева собираются deb и rpm, функциональные проверки прогоняются на раннере каждого целевого дистрибутива, обмен геометрией с прежним пакетом — через DXF.
Стек
TypeScript · Electron · SQLite · PostgreSQL · DXF · deb/rpm · OpenCascade · CUPS · GitLab CI
Проверяется: протокол прогона на целевых процессорах
Портирование системы учёта птичников под отечественную ОС и процессор
Задача, решение и что передали
Задача
Терминалы в птичниках, где отмечают посадку партии, падёж и выдачу корма, работали в клиенте под зарубежной ОС и собирались только под привычную архитектуру.
Решение
Клиент и агент сбора данных пересобраны под отечественные дистрибутивы и процессорные архитектуры, работа с весами и датчиками бункера переведена на штатные драйверы. Сборка сведена в один конвейер: коммит даёт пакеты под обе архитектуры, а проверки идут на целевых процессорах, а не в эмуляции. Терминал птичника проверен на стенде вместе с печатью ведомости движения поголовья.
Сдано 4
Пакеты под обе архитектуры
Конвейер сборки
Протокол прогона на целевых процессорах
Инструкция для площадки
Как делали
Обход птичников Прошли по птичникам и посмотрели, что делает оператор на терминале — отметка посадки партии, суточный падёж, выдача корма; сняли, как клиент разговаривает с весами и датчиками уровня бункера. Разобрали сборку и нашли места, завязанные на одну процессорную архитектуру.
Штатные драйверы Работу с весами и датчиками бункера перевели на штатные драйверы дистрибутива, отказавшись от собственных бинарных модулей. Сборку решили держать единым конвейером под обе архитектуры, а проверки гонять на реальных целевых процессорах, а не в эмуляции.
Единый конвейер Пересобрали сначала агент сбора данных, затем клиент терминала, отладили опрос весов и бункера и печать ведомости движения поголовья. Конвейер настроили так, что коммит даёт готовые пакеты под оба дистрибутива и обе архитектуры.
Стенд терминала Собрали стенд с терминалом птичника, весами и датчиком бункера и прогнали смену целиком — посадка партии, отметка падежа, выдача корма, печать ведомости; тот же набор прошёл на целевых процессорах. Расхождение показаний весов с ведомостью или сборка, прошедшая только в эмуляции, считались провалом и на площадку не выкладывались.
Передача площадке Отдали пакеты под обе архитектуры, конвейер сборки, протокол прогона на целевых процессорах и инструкцию для площадки; с операторами и зоотехником прошли работу на терминале. Служба АСУ площадки сама обновляет терминалы из своего репозитория.
Архитектура
На терминале птичника работают клиент на Electron и агент сбора на Node.js, они общаются через локальный UNIX-сокет. Агент читает весы и датчики бункера штатными драйверами по Modbus RTU, складывает отметки посадки, падежа и выдачи корма в локальную SQLite и отправляет их на сервер учёта по MQTT — локальный буфер оставлен потому, что канал с площадкой рвётся, а смена продолжает отмечать движение поголовья; каждое событие несёт ключ из номера птичника, даты и типа, поэтому повтор после восстановления связи не задваивает падёж. Серверное ядро принимает события в PostgreSQL и отдаёт ведомость движения поголовья на печать с терминала. Сборка сведена в один конвейер GitLab CI: коммит даёт пакеты deb под обе процессорные архитектуры, а проверки идут на раннерах с целевыми процессорами, а не в эмуляции.
Стек
TypeScript · Electron · PostgreSQL · SQLite · MQTT · GitLab CI · Modbus RTU · deb · Альт
Перевод маркшейдерской обработки съёмки под доверенные ОС
Задача, решение и что передали
Задача
Обработка съёмки уступов, подсчёт вынутой горной массы и паспорта буровзрывных работ готовились в зарубежном отраслевом пакете, живущем только под своей ОС.
Решение
Расчётная часть обработки съёмки и подсчёта объёмов перенесена на открытый стек и пересобрана под доверенные ОС, импорт файлов тахеометра и прежних моделей уступов сохранён конвертером. Паспорт буровзрывных работ и план горных работ формируются на том же месте, вывод на плоттер идёт через системный сервер печати. Подсчёт по контрольным блокам сверен с расчётом прежнего пакета.
Сдано 4
Пересобранное рабочее место
Конвертер прежних моделей
Протокол сверки объёмов по контрольным блокам
Инструкция маркшейдеру
Как делали
Разбор съёмки Разобрали с маркшейдерами обработку съёмки уступов — что приходит с тахеометра, как строится модель, как считается вынутая горная масса и что попадает в паспорт буровзрывных работ. Собрали форматы прежних моделей уступов и выгрузок отраслевого пакета.
Открытый стек Расчётную часть решили перенести на открытый стек и держать модели уступов в пространственном хранилище, а не в проприетарном файле пакета. Прежние модели переводить руками отказались — под них написали конвертер, а вывод на плоттер отдали системному серверу печати.
Рабочее место Собрали рабочее место под доверенные ОС: импорт файлов тахеометра, обработка съёмки и подсчёт объёмов, затем формы паспорта буровзрывных работ и плана горных работ. Конвертер прежних моделей уступов доводили на реальных выгрузках отраслевого пакета.
Контрольные блоки По контрольным блокам посчитали объём вынутой горной массы новым рабочим местом и прежним пакетом и сверили результаты, отдельно проверив печать паспорта БВР и плана на плоттере. Расхождение объёма по контрольному блоку за пределами допуска съёмки или потеря точек при конвертации модели уступа означали, что работа не принимается.
Передача маркшейдерам Отдали пересобранное рабочее место, конвертер прежних моделей, протокол сверки объёмов по контрольным блокам и инструкцию маркшейдеру; с участковыми маркшейдерами прошли цикл от импорта тахеометра до паспорта БВР. Маркшейдерская служба сама конвертирует оставшиеся модели уступов.
Архитектура
Рабочее место маркшейдера собрано на PyQt, расчётная часть вынесена отдельным модулем на NumPy: обработка съёмки уступов и подсчёт объёмов идут над поверхностями и контурами блоков. Поверхности, контуры уступов и границы блоков хранятся в PostgreSQL с PostGIS, и подсчёт вынутой горной массы делается запросом к базе — пересечения и объём между поверхностями считаются там, где лежит геометрия, иначе клиент тянул бы весь массив съёмки на каждый пересчёт. Импорт файлов тахеометра и прежних моделей уступов идёт через конвертер на GDAL в LandXML, паспорт буровзрывных работ и план горных работ выводятся на плоттер через системный сервер CUPS. Пакеты rpm собраны под целевые дистрибутивы доверенных ОС, подсчёт по контрольным блокам сверен с расчётом прежнего пакета на одних и тех же исходных файлах.
Пороги допуска ассистента к паспортам буровзрывных работ
Задача, решение и что передали
Задача
Ассистента по паспортам БВР и нарядам-путёвкам готовили к запуску на участке, а решение о запуске держалось на впечатлениях от показа.
Решение
Собрали контрольный набор вопросов по видам работ: паспорт буровзрывных работ, проветривание забоя, крепление выработки, выдача наряда-путёвки. Для каждого класса задан порог: доля ответов со ссылкой на действующий паспорт, доля отказов на вопросы вне регламента, доля ответов по отменённым редакциям — последняя должна быть нулевой. Прогон выполняет сборочный конвейер, и без сошедшегося протокола сборка на участковые терминалы не выкладывается.
Сдано 4
Передали контрольный набор по классам работ
Таблицу порогов
Отчёт прогона
Регламент допуска на участковые терминалы
Как делали
Опрос надзора Разобрали с горными мастерами и участковым надзором, о чём спрашивают ассистента: паспорт буровзрывных работ, проветривание забоя, крепление выработки, выдача наряда-путёвки. Посмотрели, как принималось решение о запуске, и увидели, что оно держится на впечатлениях от показа.
Классы и пороги Допуск решили считать по контрольному набору вопросов, разбитому на классы работ, а не по общему впечатлению. Для каждого класса задали свой порог — ответы со ссылкой на действующий паспорт, отказы на вопросы вне регламента, ответы по отменённым редакциям, — и договорились, что последних быть не должно вовсе.
Набор и конвейер Собрали контрольный набор по классам работ вместе с надзором, оформили таблицу порогов и завели прогон в сборочный конвейер. Выкладку на участковые терминалы связали с протоколом прогона.
Прогон набора Прогнали набор целиком, отдельно разобрав вопросы, где действующая и отменённая редакция паспорта БВР соседствуют. Провалом считали любой ответ по отменённой редакции паспорта и недобор порога по классу работ — с таким протоколом сборка на участковые терминалы не выкладывается.
Передача участку Отдали контрольный набор по классам работ, таблицу порогов, отчёт прогона и регламент допуска на участковые терминалы; с надзором прошли пополнение набора. Служба сама добавляет вопросы по новому виду работ и перезапускает прогон.
Архитектура
Прогон допуска оформлен отдельной джобой GitLab CI: сервис на Spring Boot берёт контрольный набор вопросов по видам работ из PostgreSQL и обращается к ассистенту тем же интерфейсом OpenAPI, что и участковый терминал. Обращение идёт через рабочий вход, а не в обход шлюза, иначе проверялся бы не тот путь, по которому вопрос задаёт горняк на участке. Пороги по классам — доля ответов со ссылкой на действующий паспорт буровзрывных работ, доля отказов на вопросы вне регламента, нулевая доля ответов по отменённым редакциям — хранятся версионно рядом с набором, поэтому протокол прошлой сборки читается вместе с действовавшими тогда порогами, а сравнение идёт по идентификатору редакции, а не по тексту ответа. Протокол складывается артефактом в MinIO и служит условием выкладки: без сошедшегося протокола job развёртывания на участковые терминалы не стартует, прогоны собраны на JUnit 5 и Testcontainers, отклонения выводятся в Grafana.
Стек
Java · Spring Boot · PostgreSQL · MinIO · OpenAPI · GitLab CI · JUnit 5 · Testcontainers · Grafana
Проверка ассистента по режимным картам варки перед допуском
Задача, решение и что передали
Задача
Ассистент по режимным картам варочного цеха отвечал варщикам на смене, и ответ по отменённой редакции карты ничем не отсекался.
Решение
В контрольный набор внесли вопросы по варке, отбелке и промывке, включая пары «действующая и отменённая редакция» одной режимной карты. Порог задан отдельно для параметров режима — щёлочность, температура, продолжительность варки: ответ без ссылки на действующую карту считается провалом. Индекс собирается из системы управления документацией, и отменённая редакция уходит из него тем же прогоном.
Сдано 4
Передали контрольный набор по режимам варки и отбелки
Таблицу порогов по параметрам режима
Отчёт прогона
Регламент допуска смены
Как делали
Разбор варки Сходили в варочный цех и разобрали с варщиками и технологом, о чём спрашивают ассистента, выписав параметры режима, по которым идут вопросы: щёлочность, температура, продолжительность варки. Проверили индекс и нашли в нём отменённые редакции режимных карт наравне с действующими.
Связка с документацией Индекс решили собирать из системы управления документацией, чтобы отменённая редакция уходила из него тем же прогоном, каким приходит новая. Проверку допуска построили на контрольном наборе с парами «действующая и отменённая редакция» одной режимной карты.
Набор по режимам Собрали вопросы по варке, отбелке и промывке, задали отдельные пороги по параметрам режима и связали сборку индекса с выгрузкой из системы документации. Прогон повесили на конвейер сборки.
Прогон редакций Прогнали набор по режимам, отдельно проверив пары редакций одной карты и вопросы по щёлочности, температуре и продолжительности варки. Ответ по параметру режима без ссылки на действующую режимную карту считался провалом, и сборка с такими ответами до смены не допускалась.
Передача цеху Отдали контрольный набор по режимам варки и отбелки, таблицу порогов по параметрам режима, отчёт прогона и регламент допуска смены; с технологом прошли пополнение набора при выпуске новой редакции карты. Цех сам запускает прогон после смены документации.
Архитектура
Сборщик индекса на Rust с Tokio тянет режимные карты из системы управления документацией, кладёт действующие редакции в новый индекс OpenSearch и переключает алиас. Индекс собирается целиком заново вместо правки текущего: удаление отменённой редакции на живом индексе оставляет окно, в котором варщик получает ответ по снятой карте. Прогонщик на Axum задаёт контрольные вопросы по варке, отбелке и промывке, включая пары «действующая — отменённая редакция» одной карты, и сверяет идентификатор карты в ответе с реестром в PostgreSQL; порог по параметрам режима — щёлочность, температура, продолжительность варки — задан отдельной строкой, ответ без ссылки на действующую карту засчитывается провалом. Прогон запускается джобой GitLab CI, обращение к ассистенту идёт по gRPC, протокол складывается в MinIO, расхождения между прогонами выводятся в Grafana.
Стек
Rust · Axum · PostgreSQL · OpenSearch · gRPC · GitLab CI · Tokio · MinIO · Grafana
Стенд враждебных сценариев для ассистента по спецификациям заказа
Задача, решение и что передали
Задача
Ассистента по документации строительного заказа обновляли вместе с моделью и шаблоном промпта, а проверку на выманивание состава заказа никто не проводил.
Решение
Собрали стенд сценариев: подменённая инструкция во вложении контрагента, просьба пересказать состав заказа по строительному номеру, попытка вытащить фрагмент чужого проекта через цитату в письме. У каждого сценария записано ожидаемое поведение — отказ, передача оператору или ответ без закрытых фрагментов, — и прогон идёт на каждой сборке модели, промпта и индекса. Провалившийся сценарий останавливает выкладку и остаётся в наборе после исправления.
Сдано 4
Передали набор враждебных сценариев
Ожидаемые реакции по каждому
Отчёт прогона со стоп-списком
Регламент выкладки сборки
Как делали
Разбор обновлений Разобрали, как обновляется ассистент по документации строительного заказа — модель, шаблон промпта, индекс — и увидели, что проверки на выманивание состава заказа в этом цикле нет. С ответственными за режим выписали, какие сведения о заказе не раскрываются и по каким каналам приходят чужие тексты: вложения контрагентов, цитаты в письмах.
Стенд сценариев Проверку решили держать отдельным стендом сценариев, привязанным к каждой сборке модели, промпта и индекса, а не разовым просмотром перед запуском. Для каждого сценария зафиксировали ожидаемое поведение — отказ, передача оператору или ответ без закрытых фрагментов.
Сборка сценариев Собрали сценарии — подменённая инструкция во вложении контрагента, просьба пересказать состав заказа по строительному номеру, попытка вытащить фрагмент чужого проекта через цитату в письме — и завели прогон в конвейер выкладки.
Прогон стенда Прогнали стенд на текущей сборке и на прежних версиях промпта, сверяя фактическое поведение с ожидаемым по каждому сценарию. Провалившийся сценарий останавливал выкладку и оставался в наборе после исправления: сборка, раскрывшая состав заказа по строительному номеру, наружу не уходила.
Передача режимной службе Отдали набор враждебных сценариев, ожидаемые реакции по каждому, отчёт прогона со стоп-списком и регламент выкладки сборки; с ответственными за режим прошли добавление нового сценария. Служба сама заводит сценарий после разбора очередной попытки.
Архитектура
Сценарии лежат в репозитории рядом с промптами: у каждого записан вход — вложение с подменённой инструкцией, просьба пересказать состав заказа по строительному номеру, цитата из чужого письма — и ожидаемая реакция: отказ, передача оператору, ответ без закрытых фрагментов. Прогон запускается джобой GitLab CI на каждой сборке модели, промпта и индекса и поднимает через Docker Compose отдельный контур с копией индекса в OpenSearch и своим узлом vLLM; отдельный контур нужен потому, что враждебный прогон на рабочем индексе оставлял бы следы в журнале обращений и смешивался со статистикой допуска. Проверка ответа не сводится к сравнению строк: реакция классифицируется по ожидаемому типу, и отдельно проверяется, что в ответе не встретился идентификатор закрытого фрагмента чужого проекта. Провалившийся сценарий останавливает выкладку и остаётся в наборе после исправления, результаты прогонов лежат в PostgreSQL, отчёт собирается на pytest и Allure.
Проверяется: карточка версии с историей протоколов
Регламент повторной проверки ассистента по эксплуатационным бюллетеням
Задача, решение и что передали
Задача
Ассистента по эксплуатационной документации проверили один раз перед запуском, и с тех пор он пережил смену весов модели и не одно извещение об изменении.
Решение
Допуск сделали срочным: у протокола есть срок годности, по его истечении ассистент переходит в режим прямых цитат документа. Повторный прогон запускается по событиям — выпуск эксплуатационного бюллетеня или извещения об изменении, смена весов модели, переиндексация документации. Результаты каждой проверки ложатся в карточку версии рядом с прежними, поэтому расхождение по контрольным вопросам видно между прогонами.
Сдано 4
Передали регламент повторной проверки с перечнем событий-триггеров
Карточку версии с историей протоколов
Отчёт расхождений между прогонами
Процедуру отзыва допуска
Как делали
Разбор допуска Подняли протокол единственной проверки перед запуском и сопоставили с тем, что с ассистентом произошло с тех пор: сменились веса модели, вышли извещения об изменении, переиндексировалась документация. С сопровождением эксплуатации выписали события, после которых ответ по бюллетеню может разойтись с документом.
Срочный допуск Допуск сделали срочным: у протокола появился срок годности, по истечении которого ассистент переходит в режим прямых цитат документа. Запуск повторного прогона привязали к событиям, а не к календарю — выпуск эксплуатационного бюллетеня или извещения об изменении, смена весов модели, переиндексация документации.
Триггеры и карточка Собрали карточку версии с историей протоколов, подписку на события-триггеры, автоматический запуск прогона и отчёт расхождений между прогонами по контрольным вопросам. Реализовали переход в режим прямых цитат по истечении срока годности протокола.
Прогон по триггерам Искусственно вызвали каждый триггер — новое извещение об изменении, смену весов, переиндексацию — и смотрели, запустился ли прогон и как результат лёг рядом с прежним в карточке версии. Провалом считали ответ по отменённому бюллетеню и ассистента, оставшегося в рабочем режиме с просроченным протоколом.
Передача сопровождению Отдали регламент повторной проверки с перечнем событий-триггеров, карточку версии с историей протоколов, отчёт расхождений между прогонами и процедуру отзыва допуска; с сопровождением прошли разбор расхождений. Служба сама отзывает допуск и назначает внеочередной прогон.
Архитектура
Повторная проверка описана DAG в Airflow и запускается по событиям, а не по календарю: вебхук системы ведения эксплуатационной документации о выпуске бюллетеня или извещения об изменении, событие смены весов из реестра моделей MLflow, завершение переиндексации. События приходят в RabbitMQ, сенсор поднимает прогон, а протоколы ложатся в карточку версии в PostgreSQL рядом с прежними, поэтому расхождение по контрольным вопросам считается между соседними прогонами. Признак допуска хранится со сроком годности, и шлюз ассистента на FastAPI читает его на каждом обращении: по истечении срока ответы переводятся в режим прямых цитат документа. Переключение сделано на стороне шлюза, а не снятием модели с эксплуатации, иначе на время перепроверки инженер оставался бы без доступа к документации; отчёты прогонов складываются в MinIO, отклонения выводятся в Grafana.
Обследование данных птичников перед моделью падежа и привеса
Задача, решение и что передали
Задача
Партия суточного молодняка жила в бумажном журнале птичника: падёж и расход корма записывали от руки, а привес по контрольным взвешиваниям попадал в отчёт уже свёрнутым за месяц.
Решение
Свели по каждой партии посадку, суточный падёж, расход корма и данные микроклимата с контроллеров птичника, привязав их к номеру птичника и дате посадки. Показали, что конверсия корма считается на разных знаменателях в зоотехнической и бухгалтерской отчётности, и закрепили одно определение. Разметили события — перевод на другой рацион, вакцинацию, остановку вентиляции — чтобы они не выглядели случайным всплеском.
Сдано 3
Передали витрину партии молодняка
Карту пропусков по журналам птичников
Согласованное определение конверсии корма и перечень событий к регистрации
Как делали
Журналы птичников Подняли бумажные журналы птичников с записями падежа и расхода корма, выгрузки контроллеров микроклимата и зоотехническую отчётность, поговорили с зоотехником и бухгалтерией. Увидели, что привес по контрольным взвешиваниям доходит до отчёта уже свёрнутым, а конверсия корма считается на разных знаменателях в зоотехнической и бухгалтерской отчётности.
Партия как ось Договорились вести всё по партии молодняка, привязывая записи к номеру птичника и дате посадки, и закрепили одно определение конверсии корма. События — перевод на другой рацион, вакцинацию, остановку вентиляции — решили регистрировать отдельно, чтобы они не читались как случайный всплеск.
Витрина партии Свели по каждой партии посадку, суточный падёж, расход корма и данные микроклимата с контроллеров птичника, собрали витрину партии и карту пропусков по журналам. Составили перечень событий, подлежащих регистрации.
Сверка с журналами Сверили сведённые данные с бумажными журналами птичников и ведомостями по отобранным партиям вместе с зоотехником. Партия, где суточный падёж и расход корма не восстанавливаются по журналу, считалась непригодной, и включение такой партии в выборку было основанием не принять работу.
Передача зоотехникам Отдали витрину партии молодняка, карту пропусков по журналам птичников, согласованное определение конверсии корма и перечень событий к регистрации; с зоотехниками прошли ведение записей по новой форме. Площадка сама регистрирует события и пополняет витрину по новым партиям.
Архитектура
Сбор с контроллеров птичника ведёт служба на Rust с Tokio: клиент OPC UA подписывается на теги микроклимата, весовые бункеры опрашиваются по Modbus TCP, ряды складываются в TimescaleDB. Гипертаблица по времени выбрана потому, что любой разбор идёт окном от даты посадки партии, и разбиение по времени отсекает лишние куски без перебора всего архива площадки. Записи журналов — посадка, суточный падёж, расход корма, контрольные взвешивания — заводятся в PostgreSQL и привязываются к паре «номер птичника + дата посадки», тот же ключ связывает их с рядами микроклимата. События (перевод на другой рацион, вакцинация, остановка вентиляции) лежат отдельной таблицей интервалов, поэтому всплеск на ряду сопоставляется с событием, а конверсия корма считается одним представлением витрины и для зоотехнической, и для бухгалтерской отчётности; разведка данных идёт на Polars, ряды показываются в Grafana.
Стек
Rust · Tokio · TimescaleDB · PostgreSQL · OPC UA · Docker · Modbus TCP · Polars · Grafana
Целлюлозно-бумажное производствоОбследование и дорожная карта
Проверяется: протокол сверки меток времени
Обследование данных бумагоделательной машины перед моделью обрывов полотна
Задача, решение и что передали
Задача
Обрыв полотна разбирали по памяти сменного мастера: время обрыва писали в журнал приблизительно, а параметры варки и отбелки лежали в отдельной системе.
Решение
Собрали на одну ось времени параметры напорного ящика, скорость сетки и сушильной части, показатели варочного котла и ступеней отбелки, а также записи о рулонном браке. Сверили метки времени между системой сбора и журналом смен: нашли расхождение часов на постах и восстановили порядок событий по тахограммам. Разметили обрывы по причинам и показали, какие из них по учётным данным неотличимы друг от друга.
Сдано 3
Передали сведённый архив параметров машины
Протокол сверки меток времени
Размеченный перечень обрывов и требования к журналу смен
Как делали
Сбор архивов Подняли журнал смен с записями обрывов, архив параметров напорного ящика, скорости сетки и сушильной части, показатели варочного котла и ступеней отбелки, записи о рулонном браке. Со сменными мастерами разобрали, как фиксируется время обрыва, и увидели, что оно пишется приблизительно.
Одна ось времени Решили свести все источники на одну ось времени, а расхождение часов между системой сбора и постами устранять не подгонкой, а восстановлением порядка событий по тахограммам. Причины обрывов договорились размечать по справочнику и честно помечать те, что по учётным данным неотличимы друг от друга.
Разметка обрывов Собрали сведённый архив параметров машины, привязали к нему записи о рулонном браке, разметили обрывы по причинам и оформили протокол сверки меток времени. Отдельно выписали требования к журналу смен.
Восстановление обрывов По отобранным обрывам восстановили картину до и после по сведённому архиву и сверили с записями сменных мастеров и тахограммами. Обрыв, время которого не восстанавливается по параметрам машины, считался неразмеченным, а архив со сдвинутыми метками времени к работе не принимался.
Передача мастерам Отдали сведённый архив параметров машины, протокол сверки меток времени, размеченный перечень обрывов и требования к журналу смен; со сменными мастерами прошли новую форму записи обрыва. Цех сам ведёт разметку причин и следит за метками времени на постах.
Архитектура
Параметры напорного ящика, скорость сетки и сушильной части забираются из историка АСУТП по OPC UA, показатели варочного котла и ступеней отбелки — выгрузками из своей системы, всё сводится на одну ось времени в TimescaleDB службой на FastAPI. Смещения часов на постах вычислены по тахограммам и записаны таблицей поправок, которая применяется при загрузке, а не правкой исходных архивов: исходные ряды остаются пригодными для повторного разбора, если поправка окажется неверной. Записи о рулонном браке и разметка обрывов по причинам лежат в PostgreSQL отдельной таблицей интервалов, а пары причин, неотличимые по учётным данным, помечены признаком и вынесены в требования к журналу смен. Сведённый архив выгружается в Parquet для разбора на pandas, оперативные ряды и протокол сверки меток времени показываются в Grafana, узлы разложены в Docker.
Оценка готовности интеграций климатических контроллеров и учёта урожая
Задача, решение и что передали
Задача
Климат-компьютер теплицы, узел приготовления питательного раствора и учёт срезки на сортировочной линии не разговаривали друг с другом, и агроном сводил оборот культуры в таблице по вечерам.
Решение
Замерили на стенде каждую связку: выгрузку климатических параметров по зонам досветки, расход и состав раствора из узла смешения, съём с сортировочной линии по сортам и длине. Выяснилось, что контроллер отдаёт усреднение за интервал без исходной частоты, а линия — только сменный итог без привязки к ряду; оба ограничения записаны в паспорт связки. Договорились о ключе связывания — теплица, отделение, культура, оборот — и о том, кто отвечает за его заполнение.
Сдано 4
Передали протокол стендовых замеров
Паспорт каждой связки
Схему ключа теплицы и оборота
План доработок на стороне источников
Как делали
Обход теплицы Прошли теплицу от климат-компьютера и узла приготовления питательного раствора до сортировочной линии и разобрали с агрономом, как он сводит оборот культуры вечерами в таблице. Выписали, что каждая система умеет отдавать наружу и в каком виде.
Ключ связывания Договорились о ключе связывания — теплица, отделение, культура, оборот — и о том, кто отвечает за его заполнение на каждой стороне. Ограничения источников решили не обходить, а записывать в паспорт связки: климат-компьютер отдаёт усреднение за интервал без исходной частоты, линия — только сменный итог без привязки к ряду.
Стендовые замеры Собрали стенд и по очереди подняли каждую связку — выгрузку климатических параметров по зонам досветки, расход и состав раствора из узла смешения, съём с сортировочной линии по сортам и длине. По результатам оформили паспорт каждой связки и план доработок на стороне источников.
Сверка выгрузок На стенде сняли контрольные выгрузки и сверили их с показаниями климат-компьютера, журналом узла смешения и сменным отчётом сортировочной линии. Связка, где выгрузку нельзя привязать к теплице, отделению и обороту, признавалась непригодной, а замер без сверки с показаниями источника в паспорт не принимался.
Передача агроному Отдали протокол стендовых замеров, паспорт каждой связки, схему ключа теплицы и оборота и план доработок на стороне источников; с агрономом и службой АСУ прошли чтение паспортов. Хозяйство само ведёт ключ и проверяет новую связку по нашему протоколу.
Архитектура
На стенде поднят шлюз на NestJS: климат-компьютер теплицы опрашивается по Modbus TCP, узел приготовления питательного раствора — по OPC UA, съём с сортировочной линии принимается сменной файловой выгрузкой, датчики зон досветки шлют показания по MQTT. Сырые ответы источников сохраняются как есть, и паспорт связки пишется по ним: замеры показали, что контроллер отдаёт усреднение за интервал без исходной частоты, а линия — только сменный итог без привязки к ряду, и по уже нормализованным записям эти ограничения не видны. Ключ связывания «теплица — отделение — культура — оборот» вынесен в справочник PostgreSQL, на него ссылаются все три источника, ряды параметров климата ложатся в TimescaleDB. Кабинет агронома показывает протокол стендовых замеров и карту пропусков по каждой связке, ряды по зонам выводятся в Grafana, стенд собран в Docker.
Витрина комплектации судового заказа с профилированием источников
Задача, решение и что передали
Задача
Готовность секции к стапельной сборке выясняли обзвоном цехов: спецификация лежала в конструкторской системе, закупка — в учётной, а извещения об изменениях расходились письмами.
Решение
Собрали витрину заказа: строительный номер, секции корпуса, состав по спецификации, заявки и поставки покупных изделий сшиты обозначением детали и извещением об изменении. Профилирование показало позиции, заведённые под разными обозначениями после изменения, и строки, где количество в спецификации расходится с ведомостью покупных изделий. Закрепили правило: действующим считается состав с последним учтённым извещением, прежние редакции помечаются устаревшими.
Сдано 3
Передали модель витрины комплектации
Отчёт профилирования обозначений
Правило выбора действующего извещения и список позиций к нормализации
Как делали
Обзвон и выгрузки Посмотрели, как сегодня выясняется готовность секции к стапельной сборке, и увидели обзвон цехов: спецификация в конструкторской системе, закупка в учётной, извещения об изменениях расходятся письмами. Собрали выгрузки обеих систем и подшивку извещений.
Витрина заказа Решили строить витрину вокруг строительного номера и секций корпуса, сшивая состав по спецификации, заявки и поставки покупных изделий обозначением детали и извещением об изменении. Закрепили правило: действующим считается состав с последним учтённым извещением, прежние редакции помечаются устаревшими.
Профилирование обозначений Собрали модель витрины комплектации и правило выбора действующего извещения, прогнали профилирование и вытащили позиции, заведённые под разными обозначениями после изменения, и строки с расхождением количества между спецификацией и ведомостью покупных изделий.
Сверка секций По отобранным секциям сверили состав из витрины со спецификацией конструкторской системы и ведомостью покупных изделий вместе с технологами и снабжением. Позиция, у которой действующее извещение определяется неоднозначно, считалась несведённой, и витрина с такими позициями не принималась.
Передача цехам Отдали модель витрины комплектации, отчёт профилирования обозначений, правило выбора действующего извещения и список позиций к нормализации; с цехами и снабжением прошли чтение готовности секции. Заказчик сам нормализует обозначения и учитывает новые извещения.
Архитектура
Сервис на Ktor тянет состав по спецификации из конструкторской системы и заявки с поставками покупных изделий из учётной, сшивая строки обозначением детали и номером извещения об изменении; изменения приходят событиями через Kafka. Витрина заказа версионирована: строка состава несёт номер извещения и признак действующей редакции, прежние редакции помечаются устаревшими, а не удаляются — иначе готовность секции нельзя пересчитать на дату, а расхождения с ведомостью покупных изделий разбираются именно задним числом. Правило «действующим считается состав с последним учтённым извещением» вынесено в модель dbt поверх ClickHouse, справочники и журнал профилирования лежат в PostgreSQL, схема ведётся Liquibase. Профилирование обозначений — позиции, заведённые под разными обозначениями после изменения, и строки с расхождением количеств — идёт отдельной задачей, готовность секции по строительному номеру выводится в кабинет и в Grafana, узлы разложены в Kubernetes.
Выход филе считали по итогам смены на бумаге, а причины низкого выхода — состояние сырца, режим дефростации, доля глазури — разбирали по памяти технолога.
Решение
Прошли цех от приёмки судовой партии до заморозки и выписали решения, которые принимает человек: оценка сырца по размерному ряду, назначение режима дефростации, разбраковка после шкерения, контроль доли глазури. Для каждого сценария указали, где лежит признак — весовой терминал, журнал приёмки, лабораторный протокол — и какие события не фиксируются вовсе. Свели сценарии в очередь: впереди те, где вес и лабораторные данные уже пишутся автоматически.
Сдано 3
Передали карту сценариев по переделам цеха
Паспорт признаков на каждый сценарий
Список нефиксируемых событий и очередь пилотов
Как делали
Обход цеха Прошли цех от приёмки судовой партии до заморозки и выписали решения, которые принимает человек: оценка сырца по размерному ряду, назначение режима дефростации, разбраковка после шкерения, контроль доли глазури. С технологом разобрали, почему причины низкого выхода филе восстанавливаются только по памяти.
Признаки переделов Для каждого сценария назначили источник признака — весовой терминал, журнал приёмки, лабораторный протокол — и отдельно выписали события, которые не фиксируются вовсе. Очередь решили строить от переделов, где вес и лабораторные данные уже пишутся автоматически, а не от самых заметных задач.
Карта переделов Собрали карту сценариев по переделам цеха, паспорт признаков на каждый сценарий и список нефиксируемых событий, свели очередь пилотов. Разложили, какие сценарии упираются в отсутствующую регистрацию.
Проверка на сменах Прошли карту с технологом и мастерами смен и на архиве смен проверили, поднимаются ли названные признаки по каждому переделу. Сценарий, под который выход филе не восстанавливается по весовому терминалу и лабораторному протоколу, в очередь не ставился, и карта с такими сценариями впереди очереди не принималась.
Передача технологу Отдали карту сценариев по переделам цеха, паспорт признаков на каждый сценарий, список нефиксируемых событий и очередь пилотов; с технологом прошли порядок запуска первого пилота. Цех сам ведёт список нефиксируемых событий и дополняет паспорта.
Архитектура
Сбор с весовых терминалов ведёт служба на Go: терминалы опрашиваются по Modbus RTU через преобразователь интерфейсов, взвешивания складываются в TimescaleDB с привязкой к переделу цеха. Журнал приёмки и лабораторные протоколы забираются из учётной системы и приводятся к ключу судовой партии, а не смены: дефростация и шкерение растягиваются на несколько смен, и выход филе, посчитанный по смене, не сходится с принятым сырцом. Паспорт признаков по каждому сценарию — оценка сырца по размерному ряду, назначение режима дефростации, разбраковка после шкерения, контроль доли глазури — и список нефиксируемых событий ведутся в PostgreSQL, ядро на Gin отдаёт их кабинету технолога. События переделов расходятся между службами через NATS, ряды взвешиваний показываются в Grafana, сводные срезы по переделам — в Superset, узлы разложены в Docker.
Макеты присылали почтой и файлообменниками, препресс проверял вылеты и профили вручную, расчёт тиража собирали в таблице.
Решение
Построили приёмный контур: реестр заказов и тиражей, хранилище макетов с версиями, автоматическая препресс-проверка на вылеты, разрешение, цветовые профили и шрифты в кривых, калькуляция по материалам и постпечатной обработке. Агент разбирает письмо с макетом, привязывает файл к заказу или заводит новый, запускает проверку и возвращает клиенту список замечаний с указанием полос. Прошедший проверку макет становится версией заказа, расчёт тиража встаёт в очередь менеджера.
Сдано 4
Переданы правила препресс-проверки
Реестр заказов и версий макетов
Журнал замечаний по файлам
Шаблон расчёта тиража
Как делали
Обследование препресса Посмотрели, как макеты приходят в почту и через файлообменники и что препресс проверяет руками — вылеты, разрешение, цветовые профили, шрифты в кривых. Разобрали таблицу расчёта тиража и выяснили, из чего складывается цена: материал, красочность, постпечатная обработка.
Архитектура приёма Макеты завели в хранилище с версиями, а заказ — в реестр тиражей, чтобы файл всегда был привязан к заказу и его редакции. Препресс-проверку сделали отдельной службой в очереди: разбор файла не держит приём письма, а результат возвращается замечаниями с указанием полос.
Сборка контура Собрали реестр заказов и хранилище версий макетов, затем проверку на вылеты, разрешение, профили и шрифты, затем калькуляцию по материалам и постпечатной обработке. Агента разбора письма делали последним: привязка файла к существующему заказу или заведение нового, запуск проверки, ответ клиенту списком замечаний.
Проверка макетов Гоняли архив реальных макетов — без вылетов, с картинками низкого разрешения, в чужом цветовом профиле, с не переведёнными в кривые шрифтами — и сверяли замечания с заключением препресса. Провалом считали макет с недостающими вылетами, ушедший в очередь менеджера как прошедший проверку, и привязку файла к чужому заказу.
Передача препрессу Передали правила препресс-проверки, реестр заказов и версий макетов, журнал замечаний по файлам и шаблон расчёта тиража. Препресс сам меняет пороги проверки и формулировки замечаний, менеджеры заводят материалы и виды постпечатной обработки в расчёт.
Архитектура
Приёмный узел разбирает письмо, кладёт макет в MinIO с версиями и ставит задачу препресс-проверки в очередь: растеризация полосы упирается в CPU и идёт много дольше приёма письма, поэтому число воркеров меняется независимо от приёмника. Воркеры гоняют Ghostscript и преобразование через Little CMS — вылеты, разрешение, цветовые профили, шрифты в кривых, — а замечания с номерами полос пишутся в PostgreSQL и уходят клиенту ответным письмом. Заказы, тиражи и версии макетов ведутся реляционной моделью, файлы лежат только в объектном хранилище по ключу версии, поэтому прошедший проверку макет становится версией заказа без копирования. Задача идемпотентна по хэшу файла: повторная присылка того же макета не заводит второй заказ, а падение воркера возвращает проверку в очередь.
Стек
Go · chi · PostgreSQL · MinIO · RabbitMQ · Kubernetes · Ghostscript · Little CMS · IMAP-коннектор
Проверяется: стенд регрессии на записанных полётах
Вывод бортовой модели обнаружения объектов в серийную эксплуатацию
Задача, решение и что передали
Задача
Детектор работал на ноутбуке оператора по записанным полётам, а на борту серийного аппарата не помещался в вычислитель и не укладывался в допустимую задержку.
Решение
Модель перенесена на бортовой вычислитель: квантование и компиляция под ускоритель, привязка кадра к телеметрии и координатам, подтверждение находки по нескольким кадрам подряд. Контур собран из бортового инференса, канала передачи подтверждённых находок на наземную станцию, хранилища исходных кадров для разбора и стенда регрессии, где новая версия прогоняется на наборе записанных полётов до заливки на аппараты. Веса ставятся подписанными пакетами с сохранением предыдущей версии на борту.
Сдано 4
Бортовая сборка модели
Стенд регрессии на записанных полётах
Набор эталонных полётов
Механизм подписанного обновления весов
Как делали
Обследование борта Посмотрели, как детектор работает на ноутбуке оператора по записанным полётам, и уперлись в ограничения бортового вычислителя — память ускорителя и допустимая задержка. Разобрали записи полётов и телеметрию, чтобы понять, как кадр связывается с координатами и что оператор считает находкой.
Архитектура инференса Модель перевели на бортовой инференс с квантованием и компиляцией под ускоритель, а находку стали подтверждать по нескольким кадрам подряд вместо одиночного срабатывания. На наземную станцию уходят только подтверждённые находки, исходные кадры остаются на борту для разбора, веса ставятся подписанными пакетами с сохранением предыдущей версии.
Сборка бортовая Собрали бортовую сборку модели и привязку кадра к телеметрии и координатам, затем канал передачи подтверждённых находок и хранилище кадров. Стенд регрессии делали параллельно: новая версия прогоняется на наборе записанных полётов до заливки на аппараты.
Проверка на полётах Гоняли эталонные полёты на стенде, замеряли задержку и потребление на самом вычислителе, отдельно проверяли откат на предыдущую версию весов и приём неподписанного пакета. Провалом считали превышение допустимой задержки на борту, пропуск находки из эталонного набора и установку весов без действительной подписи.
Передача команде Передали бортовую сборку модели, стенд регрессии на записанных полётах, набор эталонных полётов, механизм подписанного обновления весов и журнал находок с кадрами. Команда сама пополняет эталонный набор новыми полётами и выкатывает версию весов на аппараты.
Архитектура
На борту работает конвейер: захват кадра, инференс на ускорителе с квантованными и скомпилированными весами, трекер подтверждает находку по нескольким кадрам подряд, кадр привязывается к телеметрии и координатам по метке времени из шины автопилота. Подтверждённые находки уходят на наземную станцию по MQTT с подтверждением доставки, но сначала ложатся в локальную очередь в SQLite: канал рвётся, поэтому находка хранится на борту и досылается при восстановлении связи, а исходные кадры остаются в кольцевом буфере для разбора. Наземная станция складывает находки и кадры в PostgreSQL и хранилище разбора, стенд регрессии прогоняет кандидатную сборку на наборе записанных полётов до заливки на аппараты. Веса ставятся подписанным пакетом, предыдущая версия остаётся на борту, и при неуспешной проверке подписи аппарат продолжает работать на ней.
Вывод модели распознавания деталей в серийный контроллер робота
Задача, решение и что передали
Задача
Прототип распознавания деталей в россыпи считался на отдельном компьютере в лаборатории и требовал ручной настройки света и камеры под каждую партию.
Решение
Модель встроена в контроллер продукта: калибровка камеры и руки, инференс на промышленном модуле, выдача точки и угла захвата в траекторию, проверка удержания по датчику силы с повтором по второму кандидату. Рядом работает контур эксплуатации — сбор неудачных захватов с кадрами, стенд прогона новой версии на записанных сценах, подписанное обновление прошивки партии роботов. Пороги уверенности и рабочая зона вынесены в профиль ячейки и меняются без пересборки.
Сдано 4
Прошивка контроллера с моделью
Профиль калибровки ячейки
Стенд прогона записанных сцен
Набор неудачных захватов
Как делали
Обследование ячейки Посмотрели лабораторный прототип, где распознавание деталей в россыпи считалось на отдельном компьютере, и записали, что настраивалось руками под каждую партию — свет, положение камеры, пороги. Сняли сцены с реальными россыпями и разобрали, из-за чего срывался захват.
Архитектура контроллера Инференс перенесли на промышленный модуль внутри контроллера, чтобы точка и угол захвата отдавались прямо в траекторию, а удержание проверялось по датчику силы с повтором по второму кандидату. Пороги уверенности и рабочую зону вынесли в профиль ячейки, калибровку камеры и руки сделали отдельной процедурой.
Разработка прошивки Собрали калибровку камеры и руки, затем инференс на модуле и выдачу захвата в траекторию, затем проверку удержания и повтор по второму кандидату. Рядом подняли контур эксплуатации: сбор неудачных захватов с кадрами и стенд прогона новой версии на записанных сценах.
Проверка захватов Прогоняли записанные сцены с перекрытыми и бликующими деталями, гоняли ячейку на россыпях разных партий и проверяли установку прошивки на партию роботов. Провалом считали выдачу точки захвата за пределами рабочей зоны и деталь, отпущенную после срабатывания датчика силы без повтора по второму кандидату.
Передача инженерам Передали прошивку контроллера с моделью, профиль калибровки ячейки, стенд прогона записанных сцен, набор неудачных захватов и регламент обновления партии роботов. Инженеры сами калибруют новую ячейку, правят пороги в профиле и собирают следующую версию модели из накопленных неудач.
Архитектура
Контроллер ячейки собран из узлов ROS 2: камера, инференс на промышленном модуле через OpenVINO, планировщик захвата и драйвер манипулятора; обмен идёт топиками поверх DDS внутри машины, потому что узлы работают в одном такте и брокер за пределами контроллера добавил бы задержку в цикл захвата, а сеть цеха доступна не всегда. Планировщик отдаёт точку и угол захвата в траекторию, датчик силы подтверждает удержание, при срыве берётся второй кандидат из того же кадра. Пороги уверенности, рабочая зона и калибровка камеры и руки лежат в профиле ячейки: файл читается при старте узлов и меняется без пересборки прошивки. Неудачные захваты с кадрами копятся в локальном журнале SQLite и уезжают в объектное хранилище контура эксплуатации, где стенд гоняет новую версию на записанных сценах, а на партию роботов она уходит подписанным пакетом.
Контур обмена с маркетплейсами и разбора финансовых отчётов
Задача, решение и что передали
Задача
Карточки и остатки на площадках вели через кабинеты и выгрузки в таблицы, а комиссии, логистику и возвраты сводили с отгрузками в конце месяца руками.
Решение
Собрали контур обмена: публикация карточек и цен из номенклатуры с маппингом характеристик и размерных сеток, резервирование остатков по складам под схемы со своего склада и со склада площадки, приём заказов, статусов, отмен и возвратов. Финансовый блок разбирает отчёты площадок построчно: комиссия, логистика, хранение, штрафы и удержания раскладываются на реализации и возвраты, платежи площадок разносятся по актам, а несведённые строки попадают в реестр расхождений с признаком площадки.
Сдано 4
Карта маппинга номенклатуры и характеристик
Схема резервов по складам
Правила разбора отчётов площадок
Реестр расхождений
Как делали
Обследование кабинетов Разобрали, как карточки и остатки велись через кабинеты площадок и выгрузки в таблицы, и собрали примеры отчётов каждой площадки построчно. Сопоставили номенклатуру фабрики с характеристиками и размерными сетками площадок и выяснили, какие схемы работы используются — со своего склада и со склада площадки.
Архитектура обмена Маппинг характеристик и размерных сеток вынесли в отдельную карту, чтобы публикация карточек и цен шла из номенклатуры без ручной правки в кабинетах. Резервы завели по складам под обе схемы работы, а разбор отчётов сделали построчным: комиссия, логистика, хранение, штрафы и удержания раскладываются на реализации и возвраты, несведённое уходит в реестр расхождений.
Разработка публикации Собрали маппинг и публикацию карточек и цен, затем резервирование остатков по складам, затем приём заказов, статусов, отмен и возвратов. Финансовый блок делали последним: построчный разбор отчётов, разнесение платежей площадок по актам и реестр расхождений с признаком площадки.
Проверка отчётов Прогнали отчёты площадок за прошлые периоды и свели строки с реализациями и возвратами, гоняли отмену и возврат заказа и проверяли резерв при параллельных заказах на один остаток. Провалом считали строку отчёта, не разложенную ни на реализацию, ни на возврат и не попавшую в реестр расхождений, и продажу позиции, которой нет в остатке.
Передача товароведу Передали карту маппинга номенклатуры и характеристик, схему резервов по складам, правила разбора отчётов площадок, реестр расхождений и регламент дежурства по обменам. Товаровед сам заводит характеристики и размерные сетки новой площадки, бухгалтерия разбирает расхождения по реестру.
Архитектура
Коннекторы ходят в API площадок с ограничением частоты запросов: публикуют карточки и цены из номенклатуры по таблице маппинга характеристик и размерных сеток, забирают заказы, статусы, отмены и возвраты. Приём событий сделан двумя путями — вебхук и догоняющий опрос по окну времени, потому что вебхуки теряются, а расхождение по остаткам иначе всплывает только на инвентаризации. Резервы по складам под схемы со своего склада и со склада площадки считаются транзакцией в PostgreSQL, задачи публикации и приёма стоят в BullMQ поверх Redis, файлы отчётов лежат в объектном хранилище. Финансовые отчёты разбираются построчно расписанием Airflow: комиссия, логистика, хранение, штрафы и удержания раскладываются на реализации и возвраты, несведённые строки падают в реестр расхождений с признаком площадки, обмен с учётной системой идёт отдельным потоком.
Производственный учёт с рецептурами, замесами и партиями продукции
Задача, решение и что передали
Задача
Рецептуры лежали в файлах у технолога, фактический расход сырья считали по остаткам в конце месяца, а связь партии продукции с замесом восстанавливали по журналам смены.
Решение
Производственный контур построен с нуля: справочник рецептур с версиями и нормами закладки; сменные задания на замес; терминалы у линий с отметкой начала и конца замеса; весовой пост приёмки сырья с привязкой к партии поставщика; списание по факту взвешивания с расчётом отклонения от нормы; паспорт партии продукции со ссылками на замесы и сырьё. Партия продукции связана с замесами, замес — с партиями сырья, поэтому цепочку читают в обе стороны.
Сдано 4
Исходный код контура
Модель прослеживаемости партий
Регламент версий рецептур
Инструкции технолога и весовщика
Как делали
Обследование рецептур Собрали рецептуры из файлов технолога, сверили нормы закладки между редакциями и посмотрели журналы смены, по которым восстанавливали связь партии продукции с замесом. Прошли по линиям и весовому посту приёмки сырья и выяснили, где вообще можно ставить отметку начала и конца замеса.
Архитектура прослеживаемости Рецептуру сделали версионной, чтобы замес всегда ссылался на действующую редакцию с нормами закладки. Списание завели по факту взвешивания с расчётом отклонения от нормы, а прослеживаемость построили двусторонней: партия продукции связана с замесами, замес — с партиями сырья поставщика.
Разработка терминалов Собрали справочник рецептур с версиями и сменные задания на замес, затем весовой пост приёмки сырья с привязкой к партии поставщика, затем терминалы у линий с отметкой начала и конца замеса. Списание по факту взвешивания и паспорт партии продукции делали следом, терминалы обкатывали на стенде до установки в цех.
Проверка смены Прогоняли смену целиком — приёмка сырья, замес, выпуск партии продукции — и читали цепочку в обе стороны от паспорта партии до партии сырья. Провалом считали партию продукции, для которой не собирается состав по замесам и партиям сырья, и замес, проведённый по недействующей редакции рецептуры.
Передача технологу Передали исходный код контура, модель прослеживаемости партий, регламент версий рецептур, инструкции технолога и весовщика и паспорт партии продукции. Технолог сам выпускает новую редакцию рецептуры, весовщик ведёт приёмку с привязкой к партии поставщика.
Архитектура
Сбор с цеха вынесен в шлюз: значения линий забираются по OPC UA, вес — по Modbus TCP от весовых индикаторов, шлюз буферизует показания локально и досылает их, чтобы недоступность сервера учёта не останавливала замес. Терминалы у линий отмечают начало и конец замеса и работают с ядром по HTTP, ядро пишет документы в PostgreSQL: сменное задание, замес, приёмку сырья с привязкой к партии поставщика, списание по факту взвешивания с расчётом отклонения от нормы закладки. Рецептура хранится неизменяемыми версиями, а сменное задание ссылается на конкретную версию — иначе правка рецептуры задним числом переписала бы историю проведённых замесов. Связи «партия продукции — замес» и «замес — партия сырья» разложены отдельными таблицами, поэтому цепочка читается в обе стороны, а лабораторные показатели привязаны к операции, а не к смене.
Контроль подрядчиков и привилегированного доступа в учётной системе
Задача, решение и что передали
Задача
Внешние сопровождающие и подрядчики работали в учётной системе под общими административными записями, и восстановить по логам, кто выгрузил спецификации и правил цены, было нельзя.
Решение
Поверх учётной системы развёрнут контур доступа: брокер сессий с выдачей прав по заявке и сроку; именные учётные записи вместо общих административных; запись административных сессий с расшифровкой команд; журнал действий с документами — открытие спецификации, выгрузка реестра цен, печать — в неизменяемом хранилище с подписью цепочки записей; оповещение при массовой выгрузке справочников. Права выдаются на заявку и сгорают по сроку, продление проходит согласование владельца системы.
Сдано 4
Исходный код брокера сессий
Ролевая модель с матрицей прав
Неизменяемый журнал действий с документами
Регламент выдачи временных прав
Как делали
Обследование записей Подняли, кто из внешних сопровождающих и подрядчиков работает в учётной системе, и убедились, что общие административные записи не дают связать выгрузку спецификаций и правку цен с человеком. Разобрали, какие действия с документами вообще пишутся в логи и кто считается владельцем системы для согласования прав.
Архитектура брокера Общие административные записи заменили именными, а вход поставили через брокер сессий с выдачей прав по заявке и сроку. Журнал действий с документами — открытие спецификации, выгрузка реестра цен, печать — завели в неизменяемом хранилище с подписью цепочки записей и добавили оповещение при массовой выгрузке справочников.
Разработка журнала Собрали брокер сессий и ролевую модель с матрицей прав, затем запись административных сессий с расшифровкой команд, затем журналирование действий с документами в учётной системе. Оповещение о массовой выгрузке справочников и согласование продления прав владельцем системы делали последними, на стенде гоняли работы сопровождения на копии базы.
Проверка прав Выдавали права по заявке и проверяли их сгорание по сроку, выгружали реестр цен и искали запись в журнале, пробовали править цены под именной записью без выданных прав. Провалом считали правку цен или выгрузку спецификации, которую нельзя связать с человеком и сессией, и доступ, оставшийся после истечения заявки.
Передача владельцу Передали исходный код брокера сессий, ролевую модель с матрицей прав, неизменяемый журнал действий с документами, регламент выдачи временных прав и записи административных сессий. Владелец системы сам согласует продление прав, служба сопровождения поднимает запись сессии при разборе.
Архитектура
Поверх учётной системы стоит брокер сессий: заявка со сроком, именная учётная запись вместо общей административной, аутентификация через Keycloak, пароли технических учёток в Vault с ротацией. Административные сессии терминируются прокси с записью, сама запись лежит в объектном хранилище, расшифрованные команды индексируются в ClickHouse, а заявки, роли и матрица прав — в PostgreSQL. Действия с документами — открытие спецификации, выгрузка реестра цен, печать — пишутся append-only с подписью цепочки записей, поэтому изъятие строки журнала видно при проверке цепочки. Права сгорают по сроку заявки, а не отзываются вручную по каждой системе, продление проходит согласование владельца системы, а оповещение о массовой выгрузке справочников считается по порогу на потоке событий Kafka.
Коробочный продукт цехового учёта с мобильным приложением мастера
Задача, решение и что передали
Задача
Цеховой учёт был написан под одну площадку: любой показ заканчивался обещанием доработать под конкретный завод, а установка шла руками разработчиков.
Решение
Внутренняя разработка перебрана в продукт: ядро с моделью маршрутов, операций и рабочих центров; настраиваемые справочники вместо зашитых в код; цеховые терминалы со сменным заданием и отметкой операций; мобильное приложение мастера с браком, простоями и переводом партии по маршруту; офлайн-очередь с досылкой при появлении сети; установщик с миграциями и проверкой окружения; лицензирование по числу рабочих центров. Настройка площадки собирается в файл конфигурации и переносится на новую установку.
Сдано 4
Дистрибутив с установщиком
Конфигурация площадки в файле
Мобильное приложение мастера
Руководство внедренца
Как делали
Обследование площадки Разобрали внутреннюю разработку под одну площадку и выписали, что в ней зашито в код — маршруты, операции, рабочие центры, справочники. Прошли по цеху и с мастерами определили состав сменного задания и то, как отмечаются брак, простои и перевод партии по маршруту.
Архитектура продукта Ядро с моделью маршрутов, операций и рабочих центров отделили от настроек площадки, справочники сделали настраиваемыми вместо зашитых. Настройку площадки стали собирать в файл конфигурации для переноса на новую установку, мобильному приложению мастера дали офлайн-очередь с досылкой при появлении сети, лицензирование завели по числу рабочих центров.
Разработка терминалов Сначала ядро и настраиваемые справочники, потом цеховые терминалы со сменным заданием и отметкой операций, потом мобильное приложение мастера с браком, простоями и переводом партии. Установщик с миграциями и проверкой окружения и лицензирование делали последними, на стенде разворачивали продукт с нуля по конфигурации.
Проверка установки Ставили продукт установщиком на чистое окружение и на обновление с миграциями, гоняли смену с обрывом сети у мастера и разворачивали вторую площадку из файла конфигурации. Провалом считали отметку операции, потерянную офлайн-очередью после восстановления сети, и установку, потребовавшую правки кода под площадку.
Передача внедренцам Передали дистрибутив с установщиком, конфигурацию площадки в файле, мобильное приложение мастера, руководство внедренца и схему лицензирования по рабочим центрам. Внедренцы сами ставят продукт на новом заводе, собирают справочники и переносят настройку конфигурацией.
Архитектура
Ядро — сервис с моделью маршрутов, операций и рабочих центров и настраиваемыми справочниками вместо зашитых в код; цеховые терминалы работают с ним по HTTP, мобильное приложение мастера — тем же API. Приложение держит локальную SQLite и очередь операций: в цехе сеть пропадает, а отметка брака, простоя или перевода партии по маршруту не может ждать, поэтому запись идёт локально и досылается по ключу операции, что делает повтор безопасным. Состояние производства, конфигурация площадки и лицензия по числу рабочих центров лежат в PostgreSQL, обмен между узлами идёт через NATS. Установщик поднимает контейнеры, прогоняет миграции sqlx и проверяет окружение, а настройка площадки собирается в файл и переносится на новую установку без правки кода.
Учёт виноматериалов по ёмкостям с купажами и розливом
Задача, решение и что передали
Задача
Движение вина по ёмкостям вели в тетради технолога: перекачки, доливы и купажи записывали от руки, а остаток в цистерне сверяли замером перед розливом.
Решение
Учёт построен с нуля: карта ёмкостей с объёмом, содержимым и историей операций; документы перекачки, долива, купажа и обработки с пересчётом остатка по каждой ёмкости; лабораторные показатели с привязкой к операции; задания на розлив со списанием виноматериала и оприходованием серии готовой продукции; учёт акцизных марок по сериям; инвентаризация с замером и оформлением расхождений. Каждая операция меняет остаток в ёмкости, серия розлива хранит купаж и урожай, из которых собрана.
Сдано 4
Исходный код учёта
Карта ёмкостей с историей операций
Регламент оформления купажей и потерь
Формы заданий на розлив
Как делали
Обследование тетради Разобрали тетрадь технолога с перекачками, доливами и купажами и сверили записи с замерами перед розливом. Прошли по цеху, описали ёмкости с объёмами и содержимым и выяснили, какие лабораторные показатели снимаются и к какой операции они относятся.
Архитектура ёмкостей Остаток в ёмкости сделали производным от операций: каждая перекачка, долив, купаж и обработка пересчитывают его, отдельного ручного остатка нет. Серия розлива хранит купаж и урожай, из которых собрана, лабораторные показатели привязаны к операции, акцизные марки учитываются по сериям.
Разработка документов Сначала карта ёмкостей с объёмом, содержимым и историей операций, потом документы перекачки, долива, купажа и обработки с пересчётом остатка, потом лабораторные показатели с привязкой к операции. Задания на розлив со списанием виноматериала и оприходованием серии, учёт акцизных марок и инвентаризация с замером шли следом, цеховые терминалы обкатывали на стенде.
Проверка остатков Прогоняли цепочки перекачек и купажей и сверяли расчётный остаток с замером по ёмкости, гоняли розлив при нехватке виноматериала и инвентаризацию с расхождением. Провалом считали серию розлива, для которой не восстанавливается купаж и урожай, и остаток в ёмкости, разошедшийся с замером без оформленного документа потерь.
Передача технологу Передали исходный код учёта, карту ёмкостей с историей операций, регламент оформления купажей и потерь, формы заданий на розлив и инструкции технолога и лаборанта. Технолог сам оформляет купажи и задания на розлив, лаборант вносит показатели с привязкой к операции.
Архитектура
Карта ёмкостей и остаток по каждой из них ведутся документами: перекачка, долив, купаж и обработка проводятся транзакцией, которая меняет остаток и пишет операцию. Остаток нельзя править полем в обход документа — он восстанавливается прогоном операций, поэтому расхождение с замером видно на инвентаризации и оформляется актом, а не затирается. Задание на розлив списывает виноматериал и оприходует серию готовой продукции, серия хранит ссылки на купаж и урожай, лабораторные показатели привязаны к операции, акцизные марки учитываются по сериям сканированием кода DataMatrix. Модель, документы и связи лежат в PostgreSQL, очередь задач печати и обменов — в RabbitMQ, Redis кэширует карту ёмкостей для экрана цеха, терминалы работают тем же API, что и кабинет технолога.
Портал подрядчика с журналом действий над чертежами и выгрузками
Задача, решение и что передали
Задача
Чертежи и спецификации уходили подрядчикам почтой и на флешках, а кто и что скачал, восстановить было нечем.
Решение
Портал выдаёт подрядчику только документы своего заказа: разграничение по договору и этапу, водяной знак с реквизитами сессии на каждой странице, просмотр в браузере без выдачи оригинала. Открытие, печать и выгрузка пишутся в неизменяемый журнал с хешем файла, теневая копия выгруженного комплекта складывается в отдельное хранилище. Привилегированный доступ администраторов идёт отдельным входом с подтверждением и записью сессии, события уходят в систему мониторинга безопасности.
Сдано 4
Передали портал
Неизменяемый журнал действий
Хранилище теневых копий
Модель разграничения по договорам
Как делали
Обследование каналов Собрали, как чертежи и спецификации уходили подрядчикам: ящики конструкторского отдела, флешки, папки на общем диске. Сверили список действующих договоров и этапов с составом комплектов документации и выяснили, что привязки документа к договору нет нигде, а факт скачивания не фиксируется.
Архитектура доступа Доступ привязали к договору и этапу, оригинал наружу не отдаём — просмотр в браузере с водяным знаком, куда подставляются реквизиты сессии. Журнал действий сделали неизменяемым, с хешем файла, теневые копии выгруженных комплектов увели в отдельное хранилище, административный вход развели с подрядческим.
Разработка портала Сначала модель разграничения по договорам и этапам и хранилище документов с хешированием, затем просмотрщик с наложением водяного знака, затем запись открытия, печати и выгрузки. Привилегированный вход с подтверждением и записью сессии и отправку событий в мониторинг безопасности собирали на стенде с копиями чертежей.
Проверка разграничения Заводили подрядчика на один договор и пробовали дотянуться до комплекта соседнего этапа, снимали печать и копии экрана, обрывали сессию посреди выгрузки. Провалом считали любой путь, по которому документ уходит мимо записи в журнал, и лист, ушедший на печать без реквизитов сессии в водяном знаке.
Передача портала Отдали портал, неизменяемый журнал действий, хранилище теневых копий, модель разграничения по договорам и регламент выдачи доступа. Служба документации сама открывает подрядчику этап и закрывает его по завершении, безопасность поднимает по журналу, кто и какой лист смотрел.
Архитектура
Портал на Spring Boot отдаёт документ только через рендер-сервис: оригинал остаётся в MinIO, наружу уходят постраничные растры с водяным знаком, куда вписаны договор, пользователь и реквизиты сессии. Прямые ссылки на бакет закрыты политикой, просмотрщик получает страницы по короткоживущему токену, привязанному к этапу договора, а выдача оригинала — отдельное право с теневой копией комплекта в другой бакет. Журнал открытий, печати и выгрузок пишется append-only таблицей, где каждая запись несёт хеш предыдущей и хеш файла: правка задним числом рвёт цепочку и видна при сверке, поэтому журнал не хранится обновляемыми строками. Вход и привилегированный доступ администратора ведёт Keycloak, поиск по журналу — OpenSearch, события уходят в мониторинг безопасности через Kafka в формате Syslog CEF.
Собственная витрина производственной отчётности взамен зарубежной BI
Задача, решение и что передали
Задача
Сводки по загрузке машин, тиражам и браку собирались в зарубежной BI поверх ручных выгрузок и после отключения лицензий перестали обновляться.
Решение
Собрали витрину на PostgreSQL под Linux: слой сырых данных из учётной системы и журналов печатных машин, слой согласованных справочников заказов, бумаг и красок, витрины по загрузке смен, приладкам, тиражам и списанию материалов. Показатели считаются материализованными представлениями с ночным и часовым обновлением, у каждого есть паспорт с формулой, источником и владельцем. Веб-интерфейс раскрывает сводку до строки заказа и до первичного документа.
Сдано 4
Передали исходный код витрины
Модель данных и скрипты расчёта
Паспорта показателей
Регламент обновления
Как делали
Обследование сводок Собрали, из чего складывались прежние сводки — ручные выгрузки из учётной системы и журналы печатных машин — и прошли с технологом и сменным мастером путь заказа от приладки до сдачи тиража. Оказалось, что приладка, брак и списание материала в разных отчётах считались по-разному.
Архитектура витрины Разложили на слои: сырые данные из учётной системы и журналов печатных машин, согласованные справочники заказов, бумаг и красок, витрины по загрузке смен, приладкам, тиражам и списанию материалов. Показатели считаем материализованными представлениями с ночным и часовым обновлением, у каждого паспорт с формулой, источником и владельцем; от расчёта на лету в интерфейсе отказались.
Разработка слоёв Сначала загрузка сырого слоя и нормализация справочников заказов, бумаг и красок, затем витрины загрузки смен, приладок и тиражей, затем списание материалов. Веб-интерфейс с раскрытием сводки до строки заказа и до первичного документа собирали на стенде на копии учётной базы.
Проверка показателей Пересчитывали прошедшие смены и сверяли результат с журналами печатных машин и первичными документами, проверяя раскрытие каждого показателя до заказа. Не приняли бы витрину, у которой показатель при раскрытии не сходится с первичным документом или у которого нет паспорта с формулой, источником и владельцем.
Передача витрины Передали исходный код витрины, модель данных и скрипты расчёта, паспорта показателей, регламент обновления и инструкции аналитика. Аналитик типографии сам добавляет показатель с паспортом и перезапускает расчёт по регламенту.
Архитектура
Сырой слой наполняется двумя путями: изменения учётной системы приходят потоком CDC через Debezium, журналы печатных машин забираются файлами по расписанию. Согласованный слой справочников заказов, бумаг и красок строится dbt-моделями, а витрины по загрузке смен, приладкам, тиражам и списанию материалов собраны материализованными представлениями с ночным и часовым обновлением, которое ведёт Airflow. Каждая строка витрины несёт ключ первичного документа, поэтому раскрытие сводки до строки заказа идёт прямым переходом по ключу, а не обратным подбором по совпадению сумм и дат. Веб-слой на Spring Boot отдаёт сводки и паспорта показателей с формулой, источником и владельцем, графики строит Apache ECharts, Redis кэширует часто открываемые срезы, раскатка идёт Ansible на Linux.
Перевод цеховых терминалов и сервера сбора на доверенную ОС
Задача, решение и что передали
Задача
Терминалы у линий и сервер цехового учёта работали на зарубежной ОС с драйверами весов и принтеров, которые больше не поставлялись.
Решение
Перевели цеховой контур на доверенную ОС: клиент терминала пересобран под отечественную ОС и процессор, написаны службы обмена с весами, счётчиками длины и принтерами этикеток барабанов, сервер учёта перенесён на PostgreSQL под Linux. Развернули внутренний репозиторий пакетов и единый образ рабочего места с автоматической установкой, на терминале работает локальный кэш заданий на случай обрыва сети. Задания на смену приходят из учёта, результат уходит обратно очередью с подтверждением.
Сдано 4
Передали сборки клиента и служб под доверенную ОС
Исходный код драйверов оборудования
Образ рабочего места и репозиторий пакетов
Протоколы совместимости оборудования
Как делали
Обследование цеха Обошли линии и переписали, что висит на терминалах — весы, счётчики длины, принтеры этикеток барабанов — и какие драйверы больше не поставляются. Разобрали с наладчиками работу по заданию на смену и обмен сервера цехового учёта с учётной системой.
Архитектура терминалов Клиент терминала пересобираем под отечественную ОС и процессор, обмен с весами, счётчиками длины и принтерами этикеток выносим в отдельные службы, сервер учёта переносим на PostgreSQL под Linux. На терминале держим локальный кэш заданий на случай обрыва сети, результат уходит обратно очередью с подтверждением.
Разработка служб Сначала службы обмена с оборудованием и их проверка на стенде с весами и счётчиками, затем клиент терминала, затем перенос сервера учёта. Внутренний репозиторий пакетов и единый образ рабочего места с автоматической установкой собирали параллельно и раскатывали сначала на одном стендовом посту.
Проверка оборудования Печатали этикетки барабанов, снимали длину со счётчиков и вес с весов на каждом типе оборудования, рвали сеть посреди смены и смотрели, доедет ли результат из локального кэша. Провалом считали этикетку барабана с длиной, разошедшейся со счётчиком линии, и задание, потерянное терминалом при обрыве сети.
Передача сборок Передали сборки клиента и служб под доверенную ОС, исходный код драйверов оборудования, образ рабочего места и репозиторий пакетов, протоколы совместимости и инструкции наладчика. Наладчик сам разворачивает терминал из образа и подключает новые весы или принтер по протоколам.
Архитектура
Клиент терминала на Rust собран под отечественную ОС и процессор, службы обмена с весами и счётчиками длины работают по Modbus RTU через RS-485, этикетки барабанов печатаются заданиями ZPL по TCP. Задания на смену приходят с сервера учёта на PostgreSQL, но терминал держит их копию в локальной SQLite: обрыв цеховой сети не должен останавливать линию, поэтому смена продолжается на локальном кэше, а результаты копятся в очереди устройства. Результат уходит обратно в RabbitMQ с подтверждением публикации и ключом операции, так что повтор после восстановления связи не создаёт второй записи о выпуске. Единый образ рабочего места и внутренний репозиторий пакетов раскатываются Ansible, обновление терминалов идёт из него без выхода наружу.
Стек
Rust · Tokio · PostgreSQL · SQLite · RabbitMQ · Ansible · Modbus RTU · ZPL · Astra Linux
Теневое копирование файлов, уходящих во внешние ИИ-сервисы
Задача, решение и что передали
Задача
Препресс и менеджеры прогоняли макеты и спецификации заказчиков через сторонние сервисы обработки изображений, и восстановить, какой файл ушёл и в какой версии, было нечем.
Решение
Поставили перехват на исходящих каналах: агент на станциях препресса, ICAP-разбор веб-загрузок и почтовых вложений, теневая копия каждого файла в хранилище с контрольной суммой, временем, отправителем и адресом назначения. Копии индексируются по имени, тексту внутри PDF и метаданным макета, поэтому вопрос «куда уходил этот заказ» закрывается выборкой, а не опросом сотрудников. Хранение разложено по срокам и правам: копии открыты только службе безопасности, а выгрузка копии сама пишется отдельной записью.
Сдано 4
Теневое хранилище копий
Индекс по содержимому и метаданным
Политики хранения и доступа
Журнал выгрузок
Как делали
Обследование препресса Прошли препресс и менеджеров и записали, через какие сторонние сервисы обработки изображений прогоняются макеты и спецификации заказчиков. Собрали, что именно уходит — макеты, PDF, спецификации тиража — и убедились, что версия ушедшего файла нигде не остаётся.
Архитектура перехвата Перехват поставили на исходящих каналах: агент на станциях препресса и ICAP-разбор веб-загрузок и почтовых вложений, каждая копия ложится в хранилище с контрольной суммой, временем, отправителем и адресом назначения. Копии индексируем по имени, тексту внутри PDF и метаданным макета, хранение разложили по срокам и правам, выгрузка копии сама пишется отдельной записью.
Разработка хранилища Сначала ICAP-разбор веб-загрузок и почтовых вложений со складыванием копий, затем агент на станциях препресса, затем индексация по содержимому и метаданным макета. Разграничение доступа к копиям для службы безопасности и журнал выгрузок собирали на стенде на прошлых заказах.
Проверка копий Прогоняли макеты и спецификации через сторонние сервисы обработки изображений с разных станций, сверяли контрольную сумму копии с исходным файлом и искали заказ по тексту внутри PDF и метаданным макета. Провалом считали ушедший файл без теневой копии и копию, которую нельзя найти по номеру заказа или содержимому.
Передача хранилища Передали теневое хранилище копий, индекс по содержимому и метаданным, политики хранения и доступа, журнал выгрузок и регламент служебной проверки. Служба безопасности сама закрывает вопрос «куда уходил этот заказ» выборкой по индексу и правит сроки хранения.
Архитектура
ICAP-сервис перехватывает веб-загрузки и почтовые вложения, агент на станциях препресса закрывает локальные каналы, тело файла потоково кладётся в MinIO с версионированием и блокировкой объекта. Контрольная сумма считается прямо на приёме, и копия адресуется по хешу: один макет уходит наружу многократно, и адресация по содержимому не плодит одинаковых объектов, а журнал отправок остаётся отдельной таблицей с отправителем, временем и адресом назначения. Извлечение текста PDF и метаданных макета вынесено в воркеры Apache Tika, читающие задания из Kafka, потому что разбор тяжёлого файла занимает секунды и не должен держать ICAP-соединение открытым. Индекс по имени, тексту и метаданным ведёт OpenSearch, права, сроки хранения и журнал выгрузок копий лежат в PostgreSQL, сервис на chi разворачивается в Docker.
Стек
Go · chi · PostgreSQL · OpenSearch · Kafka · Docker · ICAP · MinIO · Apache Tika
Проверяется: мониторинг дрейфа и процедуры выкатки
Вывод классификатора обращений в продуктив с дообучением
Задача, решение и что передали
Задача
Классификатор тикетов уверенно вёл себя на отложенной выборке пилота, но в бою маршрутизировал обращения мимо команд, и заметить это было некому.
Решение
Классификатор завернули в сервис и встроили в трекер: уверенное решение проставляет очередь, неуверенное помечается и остаётся у диспетчера, а его исправление сохраняется как разметка. По накоплению партии размеченных обращений запускается дообучение с проверкой на отложенной выборке, выкатка идёт только после ручного подтверждения, предыдущая версия остаётся рядом. Отдельно считается дрейф: распределение тем и доля неуверенных решений сравниваются с обучающим срезом.
Сдано 4
Сервис классификации
Конвейер сбора разметки и дообучения
Мониторинг дрейфа
Процедуры выкатки и отката версий модели
Как делали
Сверка с потоком Сравнили отложенную выборку пилота с реальным потоком тикетов в трекере и увидели, что распределение тем в бою другое. С диспетчерами разобрали, как обращение уходит мимо команды и почему этого никто не замечал.
Уверенность и разметка Классификатор завернули в сервис и встроили в трекер: уверенное решение проставляет очередь, неуверенное помечается и остаётся у диспетчера, а его исправление сохраняется как разметка. Выкатка новой версии идёт только после ручного подтверждения, предыдущая остаётся рядом, а дрейф считается сравнением распределения тем и доли неуверенных решений с обучающим срезом.
Сборка контура Собрали сервис инференса и подключение к трекеру, затем сбор разметки от диспетчеров, затем дообучение по накоплению партии с проверкой на отложенной выборке и мониторинг дрейфа.
Проверка на потоке Сравнивали маршрутизацию сервиса с решениями диспетчеров на живом потоке обращений, а не только на отложенной выборке. Провалом считали версию, которая на отложенной выборке хуже действующей и всё же уезжает в продуктив, и молчащий мониторинг при сдвиге распределения тем.
Передача сопровождению Передали сервис классификации, конвейер сбора разметки и дообучения, мониторинг дрейфа и процедуры выкатки и отката версий. Диспетчеры пополняют разметку исправлениями, сопровождение подтверждает выкатку и откатывает версию само.
Архитектура
Классификатор завёрнут в сервис на FastAPI с моделью в ONNX Runtime: трекер публикует события о новых тикетах в Kafka, сервис читает их и возвращает решение через API трекера. Обмен через шину, а не прямым вызовом из трекера, потому что дообучение и выкатка выводят сервис из работы на минуты, и события должны переждать это в топике, а не теряться на таймауте. Уверенное решение проставляет очередь, неуверенное помечается и остаётся у диспетчера, его исправление приходит отдельным событием и ложится в PostgreSQL как разметка; наборы и веса версий лежат в MinIO, версии моделей — в MLflow. По накоплению партии разметки запускается дообучение с проверкой на отложенной выборке, новая версия поднимается рядом со старой и включается ручным подтверждением, распределение тем и доля неуверенных решений считаются по журналу и выводятся в Grafana.
Подписной сервис разбора и маршрутизации обращений поддержки
Задача, решение и что передали
Задача
Обращения падали в общую очередь, первая линия читала каждое письмо и вручную решала, какой команде его отдать.
Решение
Сервис по подписке разбирает входящее обращение: определяет тему и продукт, вытягивает версию, окружение и шаги воспроизведения, ищет похожие обращения по векторному индексу и склеивает повторы в одну карточку. Маршрут выбирается по теме и зоне ответственности команды; при низкой уверенности обращение уходит на первую линию с пометкой, а не назначается вслепую. Основание решения пишется рядом с карточкой, чтобы спорные назначения можно было разобрать.
Сдано 4
Передали сервис с API
Правила маршрутизации
Индекс похожих обращений
Журнал решений с основаниями и панель подписки
Как делали
Обследование Подняли архив обращений из общей очереди и посмотрели, по каким признакам первая линия отдаёт письмо команде. Собрали карту продуктов и зон ответственности, отдельно выписали обращения, которые перекидывали между командами.
Архитектура Разбор, поиск похожих и маршрутизацию сделали отдельными шагами через очередь, чтобы правка правил маршрута не трогала разбор. Похожие обращения ищем векторным индексом рядом с карточками, порог уверенности вынесли в настройку: вслепую не назначаем, при низкой уверенности отдаём на первую линию с пометкой.
Разработка Сначала извлечение темы, продукта, версии, окружения и шагов воспроизведения, затем склейка повторов в одну карточку, затем выбор команды по зоне ответственности. Основание решения писали рядом с карточкой с самого начала, чтобы на стенде разбирать спорные назначения.
Проверка Прогнали архив обращений и сравнили назначения с тем, куда обращение ушло в жизни, отдельно проверили серии писем об одной поломке. Провалом считали обращение, назначенное чужой команде без основания в журнале, и склейку разных поломок в одну карточку.
Передача Передали сервис с API, правила маршрутизации, индекс похожих обращений и журнал решений с основаниями. Руководителям команд показали правку зон ответственности и порога, первой линии — разбор карточек с пометкой и панель подписки.
Архитектура
Ядро — C++-демон на gRPC: он принимает обращение, считает эмбеддинг локальной моделью через ONNX Runtime, ищет похожие карточки в pgvector, а формулировку темы, версии и шагов воспроизведения запрашивает у языковой модели отдельным вызовом. Входящие письма и вызовы API складываются в RabbitMQ, потому что вызов модели идёт с непредсказуемой задержкой: очередь держит вход открытым и позволяет переиграть неудавшийся разбор по тому же ключу сообщения. Карточки, продукты, зоны ответственности и журнал решений лежат в PostgreSQL, а вектор — расширением в той же базе, чтобы поиск похожих и фильтр по продукту шли одним запросом без склейки выдачи из двух хранилищ. Порог уверенности вынесен в конфигурацию: ниже порога обращение уходит на первую линию с пометкой, а основание решения — близкие карточки и сработавшее правило маршрута — пишется рядом с карточкой.
Стек
C++ · gRPC · PostgreSQL · pgvector · RabbitMQ · Kubernetes · ONNX Runtime · LLM API · Prometheus
Дообучение модели на внутреннем корпусе без вывоза данных
Задача, решение и что передали
Задача
Модель хотели адаптировать под свою терминологию и историю обращений, но выгрузка корпуса наружу закрывала этот путь.
Решение
Собрали конвейер обучения адаптеров внутри контура: выборка формируется из хранилища обращений, обучение идёт на своих ускорителях, каждый адаптер получает версию и карточку параметров. Инференс подхватывает адаптер поверх базовых весов, переключение версии не трогает саму модель. Качество замеряется на закреплённом наборе задач до выкладки, возврат к прежней версии выполняется переключением ссылки.
Сдано 4
Передали конвейер обучения адаптеров
Реестр версий с картами параметров
Набор задач для замера качества
Процедуру выкладки и отката
Как делали
Обследование Разобрали хранилище обращений как источник выборки и выписали внутреннюю терминологию, на которой базовая модель отвечала мимо. Проверили, какие ускорители есть внутри контура: выгрузка корпуса наружу была закрыта с самого начала.
Архитектура Решили обучать адаптеры поверх базовых весов, а не трогать саму модель, чтобы переключение версии не требовало перекладки весов. Выборка формируется из хранилища обращений внутри контура, каждый адаптер получает версию и карточку параметров, инференс подхватывает его по ссылке, а откат — переключение той же ссылки.
Разработка Сначала собрали формирование выборки и карточку параметров, затем обучение адаптера на своих ускорителях, затем подхват адаптера инференсом и реестр версий. На стенде прошли полный круг — выборка, обучение, выкладка, откат — до работы с боевым контуром.
Проверка Каждую версию замеряли на закреплённом наборе задач до выкладки и сравнивали с предыдущей на одних и тех же вопросах, отдельно проверяли откат под нагрузкой. Провалом считали выкладку адаптера, не прошедшего замер на закреплённом наборе, и просадку на задачах, где прежняя версия отвечала верно.
Передача Передали конвейер обучения адаптеров, реестр версий с картами параметров, набор задач для замера и процедуру выкладки и отката. Команде показали, как собрать выборку по новой теме и вернуться на прежнюю версию переключением ссылки.
Архитектура
Контур разведён на обучающий конвейер и обслуживающий сервис: первый формирует выборку из хранилища обращений, гонит LoRA поверх базовых весов на своих ускорителях и регистрирует адаптер в MLflow с версией и картой параметров; второй — C++-сервис на LibTorch, который держит базовые веса в памяти и подгружает адаптер как отдельный набор тензоров. Обучаются адаптеры, а не полная модель, именно потому, что базовые веса остаются неизменными: переключение версии сводится к подмене ссылки на артефакт в S3-совместимом хранилище, а не к выкладке новой модели. Метаданные версий, набор задач для замера и результаты прогонов лежат в PostgreSQL, артефакты — в объектном хранилище, задания конвейера ставит планировщик обучения. Перед выкладкой адаптер прогоняется на закреплённом наборе задач, и если результат не принят, ссылка окружения остаётся на прежней версии — возврат выполняется тем же переключением.
Проверка технической возможности подключения по адресу заявки
Задача, решение и что передали
Задача
Заявку на подключение менеджер уносил в технический отдел и ждал устного ответа, есть ли возможность по этому адресу.
Решение
Агент нормализует адрес заявки по адресному классификатору, находит дом в инвентарной системе сети и проверяет свободный порт и трассу до здания. Ответ ложится в карточку заявки вместе с доступными тарифами и типом подключения, а дом без записи в инвентаре ставится в очередь на выезд инженера. Результат по дому кэшируется, поэтому следующая заявка по тому же адресу отвечается из кэша с отметкой давности проверки.
Сдано 4
Сервис нормализации адреса
Коннектор к инвентарной системе
Очередь выездов
Кэш результатов по домам и журнал проверок
Как делали
Обследование Прошли путь заявки от менеджера в технический отдел, где ответ давали устно, и разобрали, как адрес пишется в заявке и как дом заведён в инвентарной системе сети. Нашли адреса, записанные в классификаторе и в инвентаре по-разному.
Архитектура Адрес нормализуем по адресному классификатору до обращения к инвентарю — иначе дом не находится из-за написания. Проверку свободного порта и трассы до здания вынесли отдельным шагом, результат по дому кэшируем с отметкой давности, а дом без записи в инвентаре не отвечаем отказом, а ставим в очередь на выезд инженера.
Разработка Сначала нормализация адреса, затем коннектор к инвентарной системе с проверкой порта и трассы, затем кэш по домам и очередь выездов. На стенде гоняли архив заявок с адресами в разных написаниях.
Проверка Прогнали архив заявок и сверили ответ с тем, что в своё время сказал технический отдел, отдельно смотрели повторные заявки по одному дому и дома, только что заведённые в инвентаре. Провалом считали ответ о наличии возможности при занятых портах и выдачу из кэша без отметки давности проверки.
Передача Передали сервис нормализации адреса, коннектор к инвентарной системе, очередь выездов и кэш результатов по домам. Менеджерам показали карточку заявки с тарифами и типом подключения, техотделу — очередь выездов, журнал проверок и сброс кэша по дому.
Архитектура
Проверку ведёт сервис на Spring Boot: адрес заявки нормализуется по адресному классификатору ФИАС, затем коннектор ищет дом в инвентарной системе сети и проверяет свободный порт и трассу до здания. Результат по дому кладётся в Redis с меткой времени и сроком жизни — заявок по одному адресу приходит много подряд, и обращение в инвентарь на каждую нагружает систему, не рассчитанную на такой поток; давность проверки возвращается в ответе вместе с результатом. Карточки заявок, доступные тарифы и журнал проверок лежат в PostgreSQL, вызовы инвентаря обёрнуты предохранителем Resilience4j, поэтому при её недоступности заявка получает отложенный статус, а не ошибку формы. Дом без записи в инвентаре уходит сообщением в очередь выездов, откуда его забирает планировщик работ инженеров.
Вывод модели предсказания отказов сетевого оборудования в продуктив
Задача, решение и что передали
Задача
Пилот предсказания отказов узлов считался на выгрузке логов и жил в стороне от систем мониторинга.
Решение
Признаки собираются потоком из логов и счётчиков узлов, окно агрегации закреплено в витрине и переиспользуется на обучении и на инференсе. Модель выходит теневым прогоном рядом с действующим правилом, сравнение идёт на одном наборе инцидентов. Прогноз падает в систему мониторинга событием и связывается с нарядом на профилактику.
Сдано 4
Переданы витрина признаков
Теневой контур сравнения с действующим правилом
Интеграция с системой мониторинга
Регламент выката версии
Как делали
Обследование Разобрали пилот, который считался на выгрузке логов в стороне от мониторинга, и сверили, какие счётчики и события узлов доступны потоком. Посмотрели, каким действующим правилом сейчас ловят отказы и как по нему заводится наряд на профилактику.
Архитектура Признаки собираем потоком из логов и счётчиков узлов, окно агрегации закрепили в витрине и переиспользуем на обучении и на инференсе — иначе продуктив считал бы не то, на чём учились. Модель выпускаем теневым прогоном рядом с действующим правилом, прогноз отдаём в систему мониторинга событием и связываем с нарядом на профилактику.
Разработка Сначала витрина признаков с закреплённым окном агрегации, затем инференс на потоке, затем теневой контур и связка события мониторинга с нарядом. На стенде проигрывали поток логов и счётчиков с записанных периодов.
Проверка Сравнивали модель с действующим правилом на одном наборе инцидентов в теневом прогоне и отдельно сверяли значения признаков между обучением и инференсом. Провалом считали расхождение значения признака между витриной обучения и инференсом и выкат версии, не прошедшей теневой прогон на общем наборе инцидентов.
Передача Передали витрину признаков, теневой контур сравнения с действующим правилом, интеграцию с системой мониторинга и регламент выката версии. Дежурной смене показали событие и связанный наряд, команде модели — сравнение перед выкатом.
Архитектура
Логи и счётчики узлов идут в Kafka, потоковый Rust-сервис агрегирует их окнами и кладёт признаки в витрину Feast: описание окна лежит в витрине и переиспользуется обучением и инференсом, поэтому выборка и онлайн-вектор считаются одним определением, а не двумя реализациями. Онлайн-хранилище витрины — Redis, офлайн — ClickHouse: инференсу нужен вектор по одному узлу за короткое время, а обучению — длинная история по всем узлам, и одно хранилище обе выборки не закрывает. Модель поднимается в Kubernetes вторым экземпляром рядом с действующим правилом и идёт теневым прогоном на том же потоке, сравнение считается по одному набору инцидентов. Принятый прогноз публикуется событием в систему мониторинга и связывается с нарядом на профилактику, метрики сервиса и лаг потребителя снимаются Prometheus.
Абонент, договор и начисления жили в разных системах, и сверку между ними вели выгрузками в таблицы.
Решение
Системы разведены через брокер сообщений: каждая публикует события в свою тему, подписчики читают независимо и подтверждают обработку. Формат сообщений закреплён схемой с версией, несовместимое сообщение уходит в очередь недоставленных и не останавливает поток. Сверка по контрольным суммам идёт по расписанию и указывает на конкретный договор, а не на общий итог.
Сдано 4
Схемы сообщений с версиями
Топики и подписчики шины
Очередь недоставленных сообщений
Отчёт сверки по договорам
Как делали
Обследование Разобрали, где живут абонент, договор и начисления, и подняли выгрузки в таблицы, которыми сверялись между системами. Выписали, какие события порождает каждая система и кому они нужны.
Архитектура Системы развели через брокер: каждая публикует события в свою тему, подписчики читают независимо и подтверждают обработку — прямые вызовы между системами убрали, чтобы остановка одной не тормозила остальные. Формат сообщений закрепили схемой с версией, несовместимое сообщение уходит в очередь недоставленных и поток не останавливает.
Разработка Сначала темы и схемы сообщений с версиями, затем публикация и подписка по каждой системе с подтверждением обработки, затем очередь недоставленных и сверка по контрольным суммам. На стенде подняли копии систем и проиграли события за прошлый период.
Проверка Гоняли поток с остановленным подписчиком, слали сообщения старой и новой версии схемы, сверяли начисления по договорам между системами. Провалом считали остановку потока из-за одного несовместимого сообщения и сверку, которая показывает расхождение только общим итогом, без указания конкретного договора.
Передача Передали схемы сообщений с версиями, топики и подписчиков шины, очередь недоставленных сообщений и отчёт сверки по договорам. Сопровождению показали разбор недоставленных, командам систем — регламент выката новой версии схемы.
Архитектура
Системы разведены через Kafka: каждая публикует события в свою тему адаптером на Spring Boot, подписчики читают независимо своими группами и подтверждают смещение после обработки. Брокер стоит вместо прямых вызовов потому, что у одного события несколько получателей и они уходят на регламентные работы — при прямом вызове публикующая система знала бы про каждого и держала бы повторы у себя. Формат сообщений закреплён JSON Schema с версией в реестре схем: несовместимое сообщение отбивается на десериализации и уходит в очередь недоставленных, поток по теме при этом не останавливается. Сверка по контрольным суммам идёт по расписанию и сравнивает начисления по договору между биллингом и бухгалтерией, указывая на конкретный договор; адаптеры развёрнуты в Kubernetes, обмен с 1С идёт через её HTTP-сервисы.
Наряды монтажным бригадам со списанием материалов по факту
Задача, решение и что передали
Задача
Кабель и муфты списывали общей ведомостью в конце месяца, и привязать расход к конкретному подключению было нечем.
Решение
Наряд на монтаж создаётся из заявки и несёт адрес, тип работ и плановую спецификацию материалов. Бригада отмечает выполнение в мобильном приложении и указывает фактический расход по позициям, при этом остаток бригады ведётся как отдельное место хранения. Закрытый наряд формирует движение материалов в учётной системе и подтверждает подключение в биллинге.
Сдано 3
Передали справочник видов работ с нормами материалов
Мобильные формы наряда
Схему складов бригад и отчёт расхода по нарядам
Как делали
Обследование нарядов Проехали с монтажниками на подключения, разобрали общую ведомость списания и заявки на монтаж. Собрали виды работ с типовым расходом кабеля и муфт и выяснили, где физически лежит материал бригады между складом и объектом.
Архитектура складов бригад Наряд решили создавать из заявки с адресом, типом работ и плановой спецификацией, а остаток бригады вести отдельным местом хранения — тогда расход привязывается к конкретному подключению. Движение материалов формирует закрытие наряда, оно же подтверждает подключение в биллинге.
Разработка приложения Сначала справочник видов работ с нормами материалов и склады бригад, затем мобильные формы наряда с фактическим расходом по позициям, затем обмен с учётной системой и биллингом.
Проверка на подключениях Закрывали наряды на реальных подключениях и сводили остаток бригады с фактическим остатком в машине. Провалом считали закрытый наряд без расхода по позициям и подключение, подтверждённое в биллинге при неотражённом движении материалов.
Передача мастерам Передали справочник видов работ с нормами материалов, мобильные формы наряда, схему складов бригад и отчёт расхода по нарядам. Мастер участка сам заводит вид работ с нормой и разбирает расхождения по бригаде.
Архитектура
Приложение монтажника на Jetpack Compose держит наряд, плановую спецификацию и остаток бригады в Room и работает офлайн; факт расхода по позициям уходит на сервер очередью операций с локальным идентификатором, повтор отбрасывается по нему же. Склад бригады заведён отдельным местом хранения, поэтому расход по наряду — это перемещение между складами, а не списание из общего запаса: остаток на руках сходится по документам, без ручной инвентаризации в конце месяца. Серверная часть на Ktor принимает закрытие наряда и публикует событие в Kafka, откуда адаптеры формируют движение материалов в 1С и подтверждение подключения в биллинге. Фотографии и акты кладутся в MinIO, справочник видов работ с нормами материалов лежит в PostgreSQL.
Смену тарифа, блокировку номера и выписку детализации абонент заказывает звонком в поддержку, а оператор выполняет их в биллинге вручную.
Решение
Кабинет ходит в биллинг не напрямую, а через очередь команд: заявка на смену тарифа проверяется на остаток и ограничения тарифного плана, затем исполняется и подтверждается ответом. Детализация формируется отчётным сервисом асинхронно и выкладывается ссылкой с ограниченным сроком жизни. Вход и подтверждение операций идут по одноразовому коду, каждая команда пишется в журнал с идентификатором и конечным статусом.
Сдано 4
Кабинет абонента
Мобильная вёрстка
Очередь команд к биллингу
Журнал операций
Как делали
Обследование обращений Разобрали с поддержкой, что абонент заказывает звонком — смена тарифа, блокировка номера, выписка детализации — и как оператор выполняет это в биллинге. Выписали ограничения тарифных планов и проверки, которые оператор делает по памяти.
Архитектура команд Кабинет ходит в биллинг не напрямую, а через очередь команд, чтобы заявка не терялась при недоступности биллинга и получала конечный статус. Детализацию вынесли в асинхронный отчётный сервис со ссылкой ограниченного срока жизни, вход и подтверждение операций оставили на одноразовом коде.
Разработка кабинета Сначала очередь команд с журналом и статусами, затем смена тарифа с проверкой остатка и ограничений плана и блокировка номера, затем отчётный сервис детализации и мобильная вёрстка.
Проверка команд Подавали команды при недоступном биллинге и повторяли заявку на смену тарифа, открывали ссылку на детализацию с чужой учётной записи. Провалом считали команду без конечного статуса в журнале, смену тарифа в обход ограничений плана и доступную детализацию чужого абонента.
Передача поддержке Передали кабинет, очередь команд к биллингу, журнал операций и регламент разбора неуспешных команд. Поддержка сама поднимает историю по идентификатору команды и возвращает зависшую заявку в обработку.
Архитектура
Кабинет на Spring Boot не вызывает биллинг напрямую: заявка кладётся в Kafka с ключом по лицевому счёту, поэтому порядок команд держится в пределах одного абонента, а исполнитель на стороне биллинга разбирает партиции параллельно. Ответ возвращается в топик статусов, кабинет обновляет журнал команд в PostgreSQL — у каждой команды свой идентификатор и конечный статус, неуспешные разбираются по этому журналу. Детализация считается отчётным сервисом асинхронно и выкладывается в объектное хранилище пресигнед-ссылкой с ограниченным сроком жизни, чтобы файл не проходил через веб-слой. Вход и подтверждение операций — Keycloak и одноразовый код, состояние очередей и лаг потребителей снимает Prometheus.
Замена почтовой системы на отечественную платформу под Linux
Задача, решение и что передали
Задача
Почта компании держалась на зарубежном сервере, обновления и лицензии для которого закрылись.
Решение
Ящики перенесены пакетно по IMAP с сохранением дерева папок, флагов прочтения и календарей, адресная книга собрана из каталога пользователей. Поднята пара почтовых узлов на Linux с антиспам-фильтром и записями SPF, DKIM и DMARC, прежние адреса и алиасы сохранены. Клиентские профили перенастроены централизованно, правила фильтрации перенесены с рабочих мест на сервер.
Сдано 4
Переданы карта миграции ящиков
Настройки узлов и почтовых записей
Регламент резервного копирования
Инструкция подключения клиентов
Как делали
Обследование почты Сняли инвентарь ящиков, алиасов и правил фильтрации, разложенных по рабочим местам, разобрали дерево папок и календари. Собрали адресную книгу из каталога пользователей и зафиксировали действующие почтовые записи домена.
Архитектура узлов Перенос решили вести пакетно по IMAP с сохранением дерева папок, флагов прочтения и календарей, а узлы поднять парой на Linux с антиспам-фильтром и записями SPF, DKIM и DMARC. Прежние адреса и алиасы сохранили, правила фильтрации перенесли с рабочих мест на сервер.
Разработка переноса Сначала подняли почтовые узлы и записи домена, затем прогнали пробный перенос ящиков разного объёма, затем настроили централизованную перенастройку клиентских профилей и серверные правила фильтрации.
Проверка ящиков Сверяли перенесённые ящики по числу писем и структуре папок, гоняли доставку с внешних доменов и проверяли подпись писем. Провалом считали ящик с потерянными письмами или папками, письмо, уходящее в спам у внешних получателей, и адрес, переставший принимать почту.
Передача администраторам Передали карту миграции ящиков, настройки узлов и почтовых записей, регламент резервного копирования и инструкцию подключения клиентов. Администратор сам заводит ящик, алиас и серверное правило фильтрации.
Архитектура
Пара узлов на Linux: Postfix принимает и маршрутизирует почту, Dovecot держит ящики в Maildir и получает письма по LMTP — доставка идёт через LMTP, а не через локальный агент доставки, потому что квоты, индексы и Sieve ведёт сам Dovecot, и второй путь записи в ящик пришлось бы с ним согласовывать. Ящики перенесены пакетно по IMAP с сохранением дерева папок, флагов прочтения и календарей, аутентификация и адресная книга берутся из каталога пользователей. Панель миграции на Symfony ведёт очередь переносов и состояние по каждому ящику в PostgreSQL. Прежние адреса и алиасы сохранены, выставлены записи SPF, DKIM и DMARC, клиентские правила фильтрации перенесены в серверные Sieve-скрипты, резервная копия снимается с тома ночью.
Передача событий языковой модели в систему мониторинга безопасности
Задача, решение и что передали
Задача
Обращения к модели оставались в логах самого сервиса, и служба безопасности узнавала о странной активности уже при разборе.
Решение
Привели события модели к единому формату: учётная запись, роль, версия модели, источники ответа, признаки срабатывания фильтров. Поток уходит в мониторинг отдельным каналом, где собраны правила на серии однотипных запросов к абонентским данным и на всплеск отказов по правам. Срабатывание правила поднимает инцидент со ссылкой на исходную цепочку обращения.
Сдано 4
Схема событий
Коннектор к системе мониторинга
Набор корреляционных правил
Инструкция дежурной смены
Как делали
Обследование логов Разобрали, что сервис модели пишет при обращении: видно ли учётную запись и роль вызвавшего, версию модели, источники ответа и срабатывание фильтров. Поговорили с дежурной сменой мониторинга о том, как к ним попадают события из других систем и чего не хватает, чтобы поднять инцидент.
Архитектура потока Договорились о единой схеме события и решили не смешивать его с общими логами сервиса, а вести отдельным каналом до мониторинга. Корреляционные правила вынесли на сторону мониторинга, чтобы менять их без пересборки сервиса.
Сборка коннектора Сначала сделали формирование события в сервисе и коннектор до приёмника, затем на стенде набрали правила на серии однотипных запросов к абонентским данным и на всплеск отказов по правам. Последним приделали к инциденту ссылку на исходную цепочку обращения.
Проверка правил Прогнали на стенде записанные обращения и специально собранные серии запросов к абонентским данным. Провалом считали событие, из инцидента по которому не открывалась исходная цепочка обращения, и правило, промолчавшее на подготовленной серии.
Передача смене Отдали схему событий, коннектор, набор корреляционных правил и инструкцию дежурной смены. Разобрали с дежурными несколько поднятых инцидентов и показали, как добавить правило и как читать поля события.
Архитектура
Обёртка перед вызовом модели собирает событие — учётная запись, роль, версия модели, источники ответа, отметки фильтров — и кладёт его в отдельный топик Kafka, а не шлёт напрямую в приёмник мониторинга: приёмник переживает переиндексацию и перезапуски, и вызов модели не должен ждать его подтверждения. Нормализатор на FastAPI читает топик, приводит поля к общей схеме и отдаёт поток коннектору syslog, корреляционные правила на серии однотипных запросов и на всплеск отказов по правам разбирают события уже на стороне мониторинга. Цепочки обращений остаются в Elasticsearch, чтобы из карточки инцидента открывался исходный диалог, а реестр правил и состояние дежурной смены живут в PostgreSQL. Сервисы разворачиваются в Kubernetes, обёртка идёт sidecar-контейнером рядом с инференс-подом; при падении нормализатора смещение в топике остаётся непродвинутым, и после перезапуска он дочитывает с последнего подтверждённого события.
Команды тянули веса моделей с публичных площадок прямо в сборку, и происхождение файла в продуктиве никто не восстанавливал.
Решение
Завели внутренний реестр: файл весов принимается через карантин — проверка подписи и хеша, сканирование сериализованного формата, запрет исполняемого кода в чекпойнте. Каждой версии соответствует карточка с источником, лицензией и результатом карантинного прогона, сборка тянет веса только оттуда. Выход в интернет из среды исполнения закрыт, обновление приходит тем же путём.
Сдано 4
Реестр моделей с карточками версий
Процедура карантина
Отчёты сканирования
Политика запрета внешних источников
Как делали
Разбор источников Собрали, откуда команды тянут веса — какие публичные площадки и какие форматы чекпойнтов — и по нескольким моделям в продуктиве попробовали восстановить происхождение файла. Отдельно посмотрели, что среда исполнения тянет из интернета в момент запуска.
Архитектура реестра Решили пускать веса только через карантин — проверка подписи и хеша, разбор сериализованного формата, запрет исполняемого кода в чекпойнте — и хранить их во внутреннем реестре с карточкой версии. Выход в интернет из среды исполнения закрыли, обновление пустили тем же карантинным путём.
Сборка карантина Сделали приём файла в карантин и сканирование, затем карточку версии с источником, лицензией и результатом карантинного прогона. После этого переключили сборку на реестр как единственный источник весов.
Проверка карантина Прогнали через карантин чекпойнты с внедрённым исполняемым кодом, с подменённым хешем и без подписи, а сборку запускали при закрытом выходе наружу. Провалом считали чекпойнт с исполняемым кодом, прошедший карантин, и сборку, сумевшую подтянуть веса мимо реестра.
Передача командам Передали реестр моделей с карточками версий, процедуру карантина, отчёты сканирования и политику запрета внешних источников. Разобрали с командами, как завести новую версию весов и что делать с моделью, не прошедшей карантин.
Архитектура
Файл весов принимается сервисом карантина на FastAPI в отдельный бакет MinIO, у сборки к рабочему бакету другие ключи доступа: продвижение версии — это копирование файла в рабочий бакет после прохождения проверок, а не смена флага в таблице, иначе непроверенный файл достаётся по прямому пути в обход карантина. Проверки идут задачами Celery: сверка подписи Cosign и хеша, разбор сериализованного формата и отказ чекпойнтам, которые исполняют код при загрузке; принятый чекпойнт пересохраняется в safetensors. Карточки версий с источником, лицензией и результатом карантинного прогона хранятся в PostgreSQL, образы окружения — в Harbor, сборка тянет веса только по ссылке из карточки. Среда исполнения работает в Kubernetes без маршрута в интернет, поэтому обновление модели приходит тем же карантинным путём, что и первая загрузка.
Витрина признаков абонента с профилированием источников
Задача, решение и что передали
Задача
Данные об абонентах лежали в биллинге, CRM и системе обращений, и каждая команда собирала свой набор признаков заново под очередную задачу.
Решение
Прогнали профилирование по трём источникам: полнота полей, кардинальность, доля устаревших значений, расхождение по общему ключу абонента. Собрали витрину признаков с единым ключом и историей значений на дату, чтобы обучение и расчёт брали одинаковое определение. Правила очистки и склейки дублей вынесли в описанные преобразования, а не в скрипты аналитиков.
Сдано 4
Отчёт профилирования по источникам
Модель витрины признаков
Правила склейки дублей
Набор проверок качества
Как делали
Профилирование источников Прогнали профилирование по биллингу, CRM и системе обращений: полнота полей, кардинальность, доля устаревших значений, расхождение по общему ключу абонента. Собрали у команд их наборы признаков и увидели, где одно и то же считается по-разному.
Модель витрины Решили строить витрину на едином ключе абонента с историей значений на дату, чтобы обучение и расчёт брали одинаковое определение признака. Правила очистки и склейки дублей вынесли в описанные преобразования, отказавшись от скриптов аналитиков как источника логики.
Сборка витрины Сначала сделали загрузку и приведение трёх источников к общему ключу, затем правила склейки дублей, затем сами признаки с историей на дату. Последним добавили набор проверок качества прямо в загрузку.
Проверка на дате Собирали признаки на прошедшую дату и сличали с тем, что было доступно на тот момент, а склейку дублей разбирали на отобранных абонентах. Провалом считали признак, в значение которого на прошлую дату попали сведения, появившиеся позже, и абонента, разошедшегося по ключу между источниками после склейки.
Передача командам Передали отчёт профилирования по источникам, модель витрины признаков, правила склейки дублей и набор проверок качества. Показали командам, как брать готовый признак и как заводить новый, чтобы он попал в общий словарь.
Архитектура
Профилировщик на Rust читает биллинг, CRM и систему обращений своими коннекторами и считает полноту полей, кардинальность, долю устаревших значений и расхождение по общему ключу абонента; поток изменений приходит из Kafka. Витрина строится в Greenplum и хранит значение с интервалом действия: признак берётся на дату события, а не последним значением, иначе в обучение попадает то, чего на момент события ещё не существовало. Правила очистки и склейки дублей описаны преобразованиями dbt поверх той же витрины, проверки качества — Great Expectations, срезы для обучения выгружаются в Parquet. Точечное чтение по ключу абонента вынесено в Redis отдельным слоем — расчёт по одному абоненту не должен идти запросом в аналитическое хранилище, рассчитанное на проход по колонкам; воркеры на Tokio работают в Kubernetes.
Стек
Rust · Tokio · Greenplum · Redis · Kafka · Kubernetes · dbt · Great Expectations · Parquet
Неизменяемый журнал обращений инженеров к ассистенту дежурной смены
Задача, решение и что передали
Задача
Ассистент по регламентам машзала отвечал дежурным сменам, но разобрать после инцидента, что именно он подсказал по вводам питания, было не по чему.
Решение
Каждое обращение пишется записью: смена, инженер, версия модели и шаблона промпта, найденные фрагменты регламента, ответ. Записи связаны хешем предыдущей и лежат на томе с правом только на дозапись, а выгрузка к разбору инцидента делается по номеру смены и адресу стойки. Обращения по вводам питания и системам охлаждения помечаются отдельно и попадают в отчёт смены.
Сдано 4
Передали схему записи обращения
Цепочку хеширования
Регламент выгрузки по номеру смены
Отчёт разбора инцидента
Как делали
Разбор смен Разобрали с дежурными сменами, о чём спрашивают ассистента по регламентам машзала, и попробовали восстановить по следам недавнего разбора, что он подсказал по вводам питания, — восстанавливать оказалось не по чему. Выписали поля, нужные инженеру при разборе: смена, адрес стойки, ввод, версия регламента.
Схема записи Решили писать каждое обращение целиком — смена, инженер, версия модели и шаблона промпта, найденные фрагменты регламента, ответ — и связывать записи хешем предыдущей. Журнал положили на том с правом только на дозапись, чтобы запись нельзя было поправить задним числом.
Цепочка хешей Собрали конвейер записи обращений, цепочку хеширования и выгрузку по номеру смены и адресу стойки. Обращения по вводам питания и системам охлаждения пометили отдельно и завели в отчёт смены.
Учебный инцидент Прогнали разбор учебного инцидента: по номеру смены и адресу стойки подняли обращения, сверили цепочку хешей и попробовали подменить запись в середине. Провалом считали сошедшуюся цепочку после подмены и любое обращение, по которому не восстанавливается версия модели и шаблона промпта.
Передача сменам Отдали схему записи обращения, цепочку хеширования, регламент выгрузки по номеру смены и отчёт разбора инцидента; со старшими смен прошли выгрузку и проверку цепочки. Дежурная смена сама поднимает обращения к разбору.
Архитектура
Шлюз ассистента на FastAPI пишет каждое обращение — смена, инженер, версия модели и шаблона промпта, найденные фрагменты регламента, ответ — сообщением в Kafka, а не напрямую в хранилище: ответ дежурному не ждёт подтверждения записи, а при недоступности хранилища записи остаются в топике. Консьюмер считает SHA-256 предыдущей записи, дописывает блок на том MinIO с блокировкой объекта на запись и параллельно раскладывает разбор в ClickHouse. Колоночное хранилище выбрано потому, что выгрузка к разбору инцидента идёт по номеру смены и адресу стойки за интервал времени и читает несколько полей из широкой записи, а не запись целиком. Обращения по вводам питания и системам охлаждения помечены отдельным признаком и собираются в отчёт смены, узлы разложены в Kubernetes, трассировка идёт через OpenTelemetry, панели разбора — в Grafana.
Контроль привилегированного доступа к сборочному контуру студии
Задача, решение и что передали
Задача
Доступ к репозиториям и билд-серверам раздавали общими учётками подрядчиков, действия внутри сессии никто не восстанавливал.
Решение
Собрали контур привилегированного доступа: хранилище секретов с выдачей временных доступов, прокси сессий к репозиториям и сборочным узлам, запись действий с артефактами, журнал выгрузок сборок. Агенты разбирают поток: сопоставляют сессию подрядчика с задачей и договором, отмечают выгрузку сборки вне окна работ, закрывают доступ по завершении задачи и заводят разбор. Отзыв доступа и разбор фиксируются в неизменяемом журнале с привязкой к записи сессии.
Сдано 4
Переданы матрица привилегированных ролей
Регламент выдачи временного доступа
Запись сессий подрядчиков
Журнал выгрузок сборок
Как делали
Обследование доступов Подняли список общих учёток подрядчиков в репозиториях и на билд-серверах, сопоставили их с действующими договорами и задачами и посмотрели, что вообще пишется в логи сборок и выгрузок. Отдельно выяснили, кто забирает готовые сборки и в какие окна идут работы подрядных команд.
Архитектура доступа Доступ перевели на временную выдачу из хранилища секретов, а работу с репозиториями и сборочными узлами завернули в прокси сессий с записью действий над артефактами. Журнал отзыва доступа и разборов сделали неизменяемым и привязали к записи сессии, общие учётки из схемы убрали совсем.
Сборка контура Собрали хранилище секретов и брокер выдачи, затем прокси сессий с записью действий, последним — журнал выгрузок сборок и агентов, сопоставляющих сессию подрядчика с задачей и договором. На стенде подняли копию сборочного контура с тестовыми репозиториями и прогоняли типовые сценарии подрядной работы.
Проверка сессий Моделировали выгрузку сборки вне окна работ, вход по секрету с истёкшим сроком и попытку продолжить сессию после закрытия задачи. Провалом считали действие с артефактом, которое не удалось связать с конкретной сессией и задачей, и доступ, сохранившийся после завершения задачи.
Передача службе Передали матрицу привилегированных ролей, регламент выдачи временного доступа, записи сессий подрядчиков и журнал выгрузок сборок. Технический директор и служба безопасности сами заводят подрядчика, выдают доступ на задачу и поднимают разбор по записи сессии.
Архитектура
Доступ идёт через брокер сессий: подрядчик аутентифицируется в Keycloak, брокер берёт из Vault короткоживущие креды к репозиториям и сборочным узлам и проксирует SSH и HTTPS. Временные креды выбраны вместо общих учёток потому, что отзыв доступа сводится к истечению срока, а не к ротации пароля на всех узлах. Запись сессии кладётся в MinIO, а индекс команд и событий выгрузки сборок — в ClickHouse: разбор идёт выборками по подрядчику и окну времени поверх большого потока строк, под такие агрегаты подходит колоночное хранилище, тогда как заявки и матрица ролей остаются в PostgreSQL. Агенты читают поток событий из Kafka и сопоставляют сессию с задачей и договором, а отзыв доступа и разбор пишутся append-only с цепочкой хэшей и ссылкой на идентификатор записи сессии.
SaaS-платформа подбора с кабинетами работодателей и подпиской
Задача, решение и что передали
Задача
Заказчик продавал подбор как услугу, вакансии и отклики вёл в таблицах и почте, счета выставлял руками.
Решение
Собрали платформу, которую заказчик продаёт по подписке: кабинет работодателя с вакансиями, воронкой откликов и календарём интервью, кабинет кандидата с профилем и статусом отклика, кабинет рекрутера с очередью задач. Тарифы, лимиты вакансий и продление считает свой биллинговый модуль — планы, пробный период, счета и акты, автосписание с карты. Публикация вакансий на внешние площадки идёт через шлюз с очередью и разбором ответов площадок.
Сдано 4
Передали платформу
Биллинговый модуль подписок
Кабинеты трёх ролей
Шлюз публикации вакансий
Как делали
Обследование подбора Разобрали, как подбор продавался как услуга: таблицы вакансий и откликов, почтовая переписка с работодателями, счета руками. Прошли с рекрутером путь отклика от площадки до интервью и записали, что именно продаётся — лимиты вакансий, сопровождение, продление.
Архитектура платформы Три роли развели по кабинетам — работодатель, кандидат, рекрутер — поверх общей воронки откликов; тарифы, лимиты вакансий и продление увели в собственный биллинговый модуль, а не в платёжный виджет. Публикацию на внешние площадки закрыли шлюзом с очередью и разбором ответов, чтобы отказ площадки не терялся.
Разработка кабинетов Собрали кабинет работодателя с вакансиями, воронкой откликов и календарём интервью, затем кабинет кандидата с профилем и статусом отклика и очередь задач рекрутера. Биллинг с планами, пробным периодом, счетами, актами и автосписанием и шлюз публикации отрабатывали на стенде с песочницами площадок.
Проверка подписок Гоняли жизненный цикл подписки — пробный период, переход на план, упор в лимит вакансий, продление, отказ платежа — и публикацию вакансии с ответом площадки об ошибке. Не приняли бы работу, если бы вакансия сверх лимита плана всё же публиковалась или если бы отклонение площадки не доходило до статуса вакансии в кабинете.
Передача платформы Передали платформу, биллинговый модуль подписок, кабинеты трёх ролей, шлюз публикации вакансий и реестр тарифных планов. Заказчик сам заводит план, меняет лимиты, подключает новую площадку по описанию шлюза и разбирает застрявшие публикации в очереди.
Архитектура
Ядро на Spring Boot ведёт вакансии, отклики и календарь интервью в PostgreSQL, кабинеты трёх ролей ходят в один API, вход и роли вынесены в Keycloak. Биллинговый модуль — отдельный сервис с журналом начислений: автосписание уходит в платёжный шлюз с идемпотентным ключом «подписка плюс расчётный период», поэтому повтор задачи планировщика или ответ шлюза по таймауту не выставляют второй счёт. Продления и пробные периоды снимает Quartz по расписанию, счётчики лимитов вакансий держит Redis и сверяет с PostgreSQL при закрытии периода. Публикация на внешние площадки идёт шлюзом с очередью Kafka и повторной отправкой с растущей задержкой — площадки отвечают квотами и временными отказами, и прямой синхронный вызов вешал бы форму публикации в кабинете.
Защитный контур внутриигрового ИИ-помощника в клиенте и на сервере
Задача, решение и что передали
Задача
Игроки уводили внутриигрового помощника разговором, и он выдавал служебные подсказки, куски системной инструкции и содержимое ещё не вышедших обновлений.
Решение
Поставили контур вокруг модели: разбор пользовательского ввода на подмену инструкций, изоляцию системного промпта и справочных материалов в серверном сервисе, белый список действий помощника в игровой сессии и проверку ответа перед показом на вынос неанонсированного контента и служебных данных. Правила и списки версионируются и раскатываются на сервере, без обновления клиента и без выхода патча. Срабатывание уходит в очередь разбора с полным запросом и ответом, а повторный прогон по стенду подтверждает, что сценарий закрыт.
Сдано 4
Защитный контур помощника
Белый список действий
Правила проверки ответа
Очередь разбора срабатываний
Как делали
Обследование диалогов Собрали диалоги, где игроки уводили помощника разговором, и разобрали, что он выдавал: служебные подсказки, куски системной инструкции, содержимое ещё не вышедших обновлений. Прошли с гейм-дизайнерами, какие действия помощник вообще имеет право совершать в игровой сессии.
Архитектура контура Системный промпт и справочные материалы изолировали в серверном сервисе, ввод игрока разбираем на подмену инструкций до модели, ответ проверяем перед показом на вынос неанонсированного контента и служебных данных. Действия помощника ограничили белым списком, правила и списки сделали версионируемыми и раскатываемыми на сервере — без обновления клиента и без выхода патча.
Разработка фильтров Сначала вынос системного промпта и справочных материалов на сервер, затем разбор пользовательского ввода и белый список действий, затем проверка ответа перед показом. Очередь разбора срабатываний с полным запросом и ответом и стенд регрессионных сценариев собирали после, на записанных диалогах игроков.
Проверка сценариев Прогоняли собранные диалоги увода и новые формулировки на стенде, смотрели, не называет ли помощник содержимое невышедших обновлений и не выходит ли за белый список действий, и повторяли закрытый сценарий после раскатки правил. Провалом считали ответ с куском системной инструкции или неанонсированным контентом и сценарий, который снова проходит после того, как его закрыли правилом.
Передача контура Передали защитный контур помощника, белый список действий, правила проверки ответа, очередь разбора срабатываний и стенд регрессионных сценариев. Команда студии сама заводит правило под новое обновление и раскатывает его на сервере, не дожидаясь патча клиента.
Архитектура
Системный промпт, справочные материалы и белый список действий помощника лежат в серверном сервисе, клиент шлёт по gRPC только реплику игрока — в клиенте инструкции не держат, потому что дамп процесса игры раскрыл бы их целиком. Входной фильтр разбирает реплику на подмену инструкций, выходной проверяет ответ на неанонсированный контент и служебные данные до показа, а действия помощника вызываются по идентификатору из белого списка, а не по свободному тексту модели. Инференс вынесен отдельным сервисом на vLLM, справочный индекс держит OpenSearch; правила и списки версионируются в PostgreSQL, действующая версия раздаётся через Redis и применяется на сервере без выхода патча. Срабатывания уходят в Kafka на разбор с полным запросом и ответом и повторно прогоняются на регрессионном стенде; узлы разворачиваются в Kubernetes.
Обследование телеметрии игры перед витриной внутриигровой экономики
Задача, решение и что передали
Задача
Покупки и движение внутриигровой валюты оставались в серверных логах, и на вопрос о просадке экономики отвечали разбором выгрузок вручную.
Решение
Разобрали серверные логи и клиентские события: назначили ключи игрока, сессии и сборки, отделили тестовые и служебные аккаунты, нашли потери событий при разрыве соединения. Свели поток в словарь событий с версиями схемы и собрали витрину: источники и стоки валюты, покупки, прогресс уровней, когорты по установке. Описали проверки: полнота событий за сутки, сходимость баланса валюты, отсечение аномальных аккаунтов, регламент пересчёта после правки схемы.
Сдано 4
Словарь событий с версиями
Витрина источников и стоков валюты
Набор проверок полноты
Схема ключей игрока
Как делали
Обследование телеметрии Разобрали серверные логи и клиентские события, назначили ключи игрока, сессии и сборки и отделили тестовые и служебные аккаунты. Нашли, где события теряются при разрыве соединения и почему баланс внутриигровой валюты в выгрузках не сходится.
Архитектура словаря Свели поток в словарь событий с версиями схемы и разложили витрину на источники и стоки валюты, покупки, прогресс уровней и когорты по установке. Условились, что правка схемы не переписывает историю молча: у события есть версия, а после правки идёт пересчёт по регламенту.
Разработка витрины Собрали словарь событий, затем загрузку потока в витрину по ключам игрока и сборки, затем срезы по источникам и стокам валюты и покупкам. Проверки полноты событий за сутки, сходимости баланса валюты и отсечения аномальных аккаунтов подключали на стенде на исторических логах.
Проверка баланса Сводили баланс валюты по источникам и стокам на прошедших сутках, гоняли разрывы соединения и повторную отправку событий, смотрели, не попадают ли тестовые и служебные аккаунты в срезы. Провалом считали несходящийся баланс внутриигровой валюты за сутки и событие, не описанное в словаре с версией схемы.
Передача словаря Передали словарь событий с версиями, витрину источников и стоков валюты, набор проверок полноты, схему ключей игрока и регламент пересчёта. Аналитик студии сам заводит новое событие в словарь и запускает пересчёт после правки схемы.
Архитектура
Приёмник на Rust принимает клиентские события и серверные логи, проставляет ключи игрока, сессии и сборки и пишет в Kafka, сырой слой оседает в ClickHouse. Колоночное хранилище взято потому, что события пишутся пачками и читаются агрегатами по когортам и суткам, а точечных изменений уже записанного события в потоке нет. Словарь событий с версиями схемы лежит в PostgreSQL, приёмник отклоняет событие вне действующей схемы и складывает его в отдельный поток разбора, а потери при разрыве соединения ловятся счётчиком последовательности внутри сессии. Витрины источников и стоков валюты, покупок, прогресса уровней и когорт по установке строятся dbt, проверки полноты за сутки и сходимости баланса запускает Airflow, дашборды собраны в Grafana, окружение — Docker.
Проверяется: схема данных и инструкция администратору
Личный кабинет клиента с витриной данных
Задача, решение и что передали
Задача
Клиенты звонили, чтобы узнать статус заявки, остаток по договору и получить закрывающие документы, и всё это доставали из учётной системы руками.
Решение
Сделали кабинет со статусами заявок, историей и выгрузкой документов. Данные тянутся по расписанию и по событию, наружу отдаётся только то, что относится к конкретному клиенту.
Сдано 4
Развёрнутый кабинет
Исходный код
Схема данных
Инструкция администратору
Как делали
Разбор звонков Послушали, с чем клиенты звонят: статус заявки, остаток по договору, закрывающие документы. С сотрудниками прошли путь, которым они достают эти данные из учётной системы, и выписали поля, которые наружу показывать нельзя.
Витрина наружу Кабинет не ходит в учётную систему на каждый запрос: данные складываются в отдельную витрину и обновляются по расписанию и по событию. Отбор по договору клиента делается на сервере, а не в интерфейсе, наружу отдаётся только то, что относится к этому клиенту.
Сборка кабинета Собрали витрину и загрузку из API учётной системы, затем статусы заявок и историю, затем выгрузку закрывающих документов и вход клиента. На стенде работали на обезличенной копии договоров.
Проверка видимости Под учётными записями разных клиентов проверяли выдачу заявок, остатков и документов и сверяли значения с учётной системой. Провалом считали появление чужого договора или документа в выдаче и остаток, разошедшийся с учётной системой после обновления витрины.
Передача поддержке Передали развёрнутый кабинет, исходный код, схему данных и инструкцию администратору. Поддержка сама заводит клиентов, выдаёт доступы и разбирает расхождения витрины.
Архитектура
Кабинет — отдельное приложение на Spring Boot со своей базой PostgreSQL: оно не ходит в учётную систему на каждый экран, а держит витрину по клиенту, потому что чтение из боевой базы на каждый вход завязывало бы доступность кабинета на её доступность. Синхронизатор тянет договоры и остатки по расписанию через OData учётной системы, а изменения статуса заявки приходят событием в RabbitMQ и обновляют витрину точечно. Вход по учётной записи через Keycloak, идентификатор клиента берётся из токена и подставляется в условие выборки на уровне репозитория, поэтому запрос чужого документа не собирается в принципе; сессии и подготовленные выгрузки лежат в Redis. Приложение поднято контейнерами за nginx: при отказе экземпляра запросы уходят на второй, состояние держится в базе и Redis, а не в памяти процесса.
Компании нужен был ассистент по внутренним документам, но проект с развёртыванием в собственном контуре был для неё избыточен по бюджету.
Решение
Развернули ассистента в российском облаке на выделенном контуре: загрузка документов, ответы со ссылкой на файл и страницу, разграничение по отделам, журнал запросов.
Сдано 3
Работающий сервис
Инструкция по ведению базы
Выгрузка всех данных по запросу
Как делали
Разбор документов Посмотрели, какие внутренние документы пойдут в базу и в каком виде их ведут отделы, и почему развёртывание в собственном контуре для компании избыточно. Зафиксировали, какие данные не должны выходить за пределы выделенного контура.
Выделенный контур Вместо общего сервиса развернули выделенный контур в российском облаке, разграничение сделали по отделам на этапе отбора фрагментов. Индекс хранит привязку фрагмента к файлу и странице, потому что ответ обязан ссылаться на источник, и предусмотрели выгрузку всех данных по запросу — заказчик не заперт в сервисе.
Сборка сервиса Подняли загрузку документов и индекс, затем ответы со ссылками на файл и страницу, затем разграничение по отделам, журнал запросов и веб-интерфейс.
Проверка ответов Прогнали вопросы, собранные в каждом отделе, и проверили каждую ссылку до страницы документа. Провалом считали ответ без ссылки, ссылку, ведущую не на тот фрагмент, и выдачу документа чужого отдела.
Передача владельцам Передали работающий сервис, инструкцию по ведению базы и порядок выгрузки всех данных по запросу. Ответственные в отделах сами загружают и заменяют документы и смотрят журнал запросов.
Архитектура
Сервис поднят в российском облаке на выделенном контуре: шлюз на FastAPI, индексатор документов, инференс за общим адресом и хранилища. Исходные файлы лежат в MinIO, метаданные и права — в PostgreSQL, фрагменты и векторы — в Qdrant, причём идентификатор арендатора входит в имя коллекции и в каждый фильтр поиска, поэтому чужой документ не попадает в выборку даже при ошибке в запросе. Загрузка и нарезка вынесены в фоновые задачи через очередь в Redis: файл на сотни страниц обрабатывается минутами, и держать на нём HTTP-запрос нечем, а очередь переживает перезапуск воркера. Ответ собирается только из найденных фрагментов и несёт ссылку на файл и страницу, журнал запросов пишется в PostgreSQL; состав узлов описан манифестами Kubernetes, выгрузка всех данных делается дампом схемы арендатора и его бакета, состояние узлов видно в Grafana.
Проверяется: паспорта источников и отчёт о качестве
Обследование данных перед моделью прогноза спроса в рознице
Задача, решение и что передали
Задача
Заказ сводили в Excel по памяти категорийных менеджеров, а данные о продажах, остатках и промо лежали в разных системах и между собой не сходились.
Решение
Сделали инвентаризацию источников — кассовые чеки, движения по складу, справочник товаров, календарь промо — и свели их в одну витрину с единым ключом товара и магазина. Прогнали профилирование: нашли периоды без выгрузок, возвраты, неотличимые от продаж, промо без отметок в данных и товары, меняющие код при перезаводе карточки. Описали, какие поля нужно начать фиксировать в учёте до старта моделирования, и собрали воспроизводимую нарезку выборок под будущие эксперименты.
Сдано 4
Паспорта источников
Отчёт о качестве данных с перечнем разрывов
Витрина продаж и остатков
Список доработок учётных систем и план экспериментов
Как делали
Инвентаризация источников Сняли инвентаризацию источников — кассовые чеки, движения по складу, справочник товаров, календарь промо — и с категорийными менеджерами разобрали, что именно они держат в голове при сведении заказа в Excel. Профилирование показало периоды без выгрузок, возвраты, неотличимые от продаж, промо без отметок в данных и товары, меняющие код при перезаводе карточки.
Единый ключ Свели источники в одну витрину с единым ключом товара и магазина, чтобы данные не сшивались заново в каждом эксперименте, а перезаведённые карточки связали сквозным идентификатором. От перехода к моделированию отказались до того, как в учёте начнут фиксироваться недостающие поля — прежде всего отметка промо и признак возврата.
Сборка витрины Собрали загрузку источников и модели витрины продаж и остатков, затем проверки на пропуски и дубли, затем воспроизводимую нарезку выборок под будущие эксперименты и просмотр показателей.
Сверка витрины Сверили витрину с учётными системами в разрезе товара и магазина и перепрогнали нарезку выборок. Провалом считали расхождение суммы продаж витрины с кассовыми данными, возврат, попавший в продажи, и выборку, дающую при повторной сборке другой состав строк.
Передача аналитикам Передали паспорта источников, отчёт о данных с перечнем разрывов, витрину продаж и остатков, список доработок учётных систем и план экспериментов. Аналитики заказчика собирают выборки и пополняют паспорта источников сами.
Архитектура
Витрина собирается из четырёх источников — кассовые чеки, движения по складу, справочник товаров, календарь промо: загрузку ведут задания Airflow, преобразования описаны моделями dbt, чтобы нарезка выборок повторялась из кода, а не из ноутбука аналитика. Позиции чеков положили в ClickHouse: профилирование считает агрегаты по одному-двум полям за годы истории, и колоночное хранение читает только эти столбцы, тогда как строчная таблица поднимала бы строку чека целиком. Справочники товаров и магазинов с их версиями лежат в PostgreSQL и связаны с фактами суррогатным ключом, который переживает перезаводку карточки — исходный код товара в источнике при этом меняется. Проверки качества оформлены наборами Great Expectations на каждом шаге загрузки: периоды без выгрузок, возвраты и промо без отметки выпадают в отчёт, срезы для будущих экспериментов формируются тем же конвейером и показываются в Metabase.
Проверяется: журнал шлюза и интерфейс разбора обращений
Журналирование и разбор обращений к языковой модели
Задача, решение и что передали
Задача
Помощник подсказывал операторам поддержки ответы клиентам, но восстановить, что он ответил в конкретном обращении и на каких данных, было невозможно.
Решение
Поставили запись на уровне шлюза: сохраняются запрос, версия модели и шаблона промпта, идентификаторы найденных фрагментов, параметры генерации и ответ — всё сшито идентификатором обращения. Персональные данные маскируются на входе в журнал, доступ к полным записям выделен отдельной ролью, записи пишутся без изменения задним числом и связаны хешем цепочки. Поверх собрали разбор инцидента: по номеру обращения поднимается вся цепочка вызовов с временем, источниками и текстом ответа.
Сдано 3
Передали шлюз с журналированием
Схему хранения и сроков хранения записей
Интерфейс разбора обращений и ролевую модель доступа к журналу
Как делали
Попытка восстановить Взяли конкретные жалобы на подсказки помощника и попробовали восстановить, что он ответил оператору и на каких данных, — собрать цепочку не удалось. С безопасностью зафиксировали, какие персональные данные клиентов нельзя писать в журнал вовсе.
Запись на шлюзе Запись поставили на уровне шлюза, а не внутри приложения: сохраняются запрос, версия модели и шаблона промпта, идентификаторы найденных фрагментов, параметры генерации и ответ, сшитые идентификатором обращения. Персональные данные маскируются на входе в журнал, записи не меняются задним числом и связаны хешем цепочки, полный доступ вынесен в отдельную роль.
Сборка разбора Собрали перехват на шлюзе и передачу записей в хранилище, затем маскирование и ролевой доступ, затем интерфейс разбора: по номеру обращения поднимается вся цепочка вызовов с источниками и текстом ответа.
Разбор обращений Брали обращения из потока и разбирали их по журналу до исходных фрагментов, которыми модель пользовалась. Провалом считали обращение, цепочку по которому собрать не удалось, немаскированные персональные данные в записи и возможность изменить запись без разрыва хеш-цепочки.
Передача поддержке Передали шлюз с журналированием, схему хранения записей, интерфейс разбора обращений и ролевую модель доступа к журналу. Поддержка разбирает спорные ответы по номеру обращения сама, безопасность проверяет целостность цепочки.
Архитектура
Запись ведётся на уровне шлюза на Axum, через который проходят все обращения к модели: он присваивает обращению идентификатор, прокидывает его в трассировку OpenTelemetry и публикует запись — запрос, версию модели и шаблона промпта, идентификаторы найденных фрагментов, параметры генерации, ответ — в Kafka. Шина стоит между шлюзом и хранилищем, потому что журналирование не должно задерживать ответ оператору и должно переживать недоступность хранилища; консьюмер укладывает события в ClickHouse. Колоночное хранилище выбрано под разбор: выборки идут по времени и нескольким полям на большом объёме записей, точечных обновлений здесь нет — записи только добавляются и связаны хешем предыдущей, поэтому правка задним числом видна по разрыву цепочки. Персональные данные маскируются в шлюзе до публикации, полные записи открыты отдельной ролью в Keycloak, сроки хранения и роли описаны в PostgreSQL, разбор инцидента поднимает всю цепочку вызовов по номеру обращения через панель Grafana.
Проверяется: форма проверки оператором и правила сопоставления
Подписной сервис распознавания первичных документов поставщиков
Задача, решение и что передали
Задача
Накладные и УПД приходили сканами и фотографиями, операторы перебивали позиции в учётную систему руками.
Решение
Подняли сервис по подписке: документ поступает через почтовый ящик или API, определяется его тип, из скана извлекаются шапка и табличная часть, позиции сопоставляются с номенклатурой заказчика по артикулу и наименованию. Поля с низкой уверенностью подсвечиваются оператору в форме проверки, подтверждённые правки уходят в обучающую выборку. Готовый документ отдаётся в учётную систему одним пакетом, повторная загрузка того же файла отсекается по хешу.
Сдано 3
Передали сервис с API и почтовым шлюзом
Форму проверки оператором
Правила сопоставления номенклатуры и панель расхода лимитов подписки
Как делали
Обследование Разобрали пачку сканов и фотографий накладных и УПД от разных поставщиков, посмотрели, как оператор перебивает позиции в учётную систему. Выписали формы шапки и табличной части и случаи, где артикул поставщика не совпадает с номенклатурой заказчика.
Архитектура Приём развели на два входа — почтовый ящик и API; распознавание отделили от сопоставления с номенклатурой, чтобы правила сопоставления менялись без переобучения. Файлы кладём в объектное хранилище, хеш файла держим ключом от повторной загрузки; проводить документ мимо формы проверки при низкой уверенности полей не стали.
Разработка Собрали определение типа документа, затем извлечение шапки и табличной части, затем сопоставление позиций по артикулу и наименованию; форму проверки делали последней, вокруг подсветки неуверенных полей. На стенде гоняли на архиве сканов, подтверждённые правки оператора складывали в обучающую выборку.
Проверка Прогон шёл на комплектах разных поставщиков, включая перефотографированные и повёрнутые сканы и повторную отправку того же файла. Провалом считали позицию, ушедшую в учётную систему с чужим артикулом без пометки, и документ, проведённый второй раз при повторной загрузке.
Передача Передали сервис с API и почтовым шлюзом, форму проверки оператором и правила сопоставления номенклатуры. Операторов учили работать с подсвеченными полями, администратора — заводить нового поставщика и смотреть расход лимитов подписки.
Архитектура
Приём разведён на два входа — IMAP-коллектор почтового ящика и эндпоинт FastAPI; оба кладут файл в объектное хранилище и ставят задачу в Celery, дальше конвейер общий. Классификация типа документа, OCR и извлечение шапки с табличной частью вынесены в GPU-воркер и вызываются через очередь, а не прямым HTTP: разбор многостраничного скана длится дольше, чем почтовый шлюз может держать соединение. Хеш файла — уникальный ключ в PostgreSQL, поэтому повторная загрузка того же скана попадает на существующий документ вместо нового прогона. Позиции сопоставляются с номенклатурой по артикулу и триграммному поиску в той же базе, подтверждённые оператором правки пишутся отдельной таблицей и забираются в обучающую выборку, готовый документ уходит в учётную систему одним пакетом с повтором при недоступности.
Подписной ассистент по прайс-листам и спецификациям поставщиков
Задача, решение и что передали
Задача
Прайсы приходили в разнобое форматов, и подбор замены снятой позиции держался на памяти менеджера и переписке с поставщиком.
Решение
Загрузчик приводит прайсы разных форматов к общей схеме, характеристики вытягиваются моделью из текстовых описаний и спецификаций. Позиции связываются с внутренней номенклатурой по совпадению признаков, а не по написанию названия. Запрос на подбор возвращает кандидатов с указанием расхождений по характеристикам и остатка у поставщика.
Сдано 4
Передали загрузчик прайсов
Схему характеристик
Таблицу связей с номенклатурой
Сервис подбора
Как делали
Обследование Собрали прайсы поставщиков в том виде, в каком они приходят — разные форматы, колонки и единицы, и посмотрели, как менеджер подбирает замену снятой позиции по памяти и переписке. Разобрали, чем описание позиции у поставщика отличается от внутренней номенклатуры.
Архитектура Загрузчик приводит прайсы к общей схеме на входе, характеристики вытягиваем моделью из текстовых описаний и спецификаций и храним полями, а не строкой. Связь с внутренней номенклатурой строим по совпадению признаков: от сопоставления по написанию названия отказались, оно ломалось на сокращениях поставщика.
Разработка Сначала загрузчик и общая схема, затем извлечение характеристик и схема признаков, затем таблица связей с номенклатурой и подбор кандидатов с расхождениями и остатком у поставщика. На стенде прогоняли прайсы каждого формата и повторные загрузки одного поставщика.
Проверка Брали снятые позиции и сравнивали кандидатов сервиса с заменами, которые менеджеры подбирали раньше, отдельно смотрели позиции с разными единицами измерения. Провалом считали кандидата, у которого расхождение по характеристике не показано, и позицию, привязанную к чужой номенклатуре из-за похожего названия.
Передача Передали загрузчик прайсов, схему характеристик, таблицу связей с номенклатурой и сервис подбора. Менеджерам показали работу с кандидатами и выгрузку в закупку, администратору — подключение нового формата прайса и правку признаков.
Архитектура
Загрузчик принимает прайсы в CSV, XLSX и почтовых вложениях, приводит их к общей схеме по карте колонок поставщика и складывает строки в PostgreSQL как отдельную версию загрузки. Извлечение характеристик из текстовых описаний и спецификаций вынесено в фоновые задачи Celery, потому что разбор описаний моделью идёт медленнее самой загрузки и не должен задерживать появление цен и остатков. Связь позиции поставщика с внутренней номенклатурой строится по совпадению признаков: эмбеддинг описания в pgvector даёт кандидатов, точное сравнение характеристик отбирает финальных — сопоставление по написанию названия рассыпается на сокращениях и порядке слов. Подбор возвращает кандидатов с перечнем расхождений по характеристикам и остатком поставщика; версия загрузки хранится целиком, поэтому от любого значения можно вернуться к исходной строке прайса.
Агент разбора писем поставщиков с подтверждениями заказов
Задача, решение и что передали
Задача
Подтверждения заказов и уведомления об изменении цены приходили письмами в свободной форме, и закупщик переносил строки в заказ руками.
Решение
Агент разбирает письмо и вложение, сопоставляет строки подтверждения со строками заказа поставщику по артикулу и единице измерения и проставляет подтверждённое количество, цену и дату отгрузки. Строка с расхождением по цене или количеству помечается и уходит закупщику отдельным списком, сошедшиеся строки проводятся без участия человека. Письма сверяются по идентификатору сообщения, поэтому повторная доставка одного ответа поставщика не проводится второй раз.
Сдано 4
Разборщик писем
Правила сопоставления строк
Список расхождений
Журнал обработанных сообщений и инструкция по спорным письмам
Как делали
Обследование Собрали письма поставщиков с подтверждениями заказов и уведомлениями об изменении цены и посмотрели, как закупщик переносит строки в заказ руками. Разобрали, чем артикул и единица измерения у поставщика расходятся с заказом.
Архитектура Строки подтверждения сопоставляем со строками заказа поставщику по артикулу и единице измерения, сошедшиеся проводим без участия человека — закупщику остаются только расхождения. Письма учитываем по идентификатору сообщения, чтобы повторная доставка того же ответа поставщика не проводилась второй раз.
Разработка Сначала разбор письма и вложения, затем сопоставление строк с простановкой подтверждённого количества, цены и даты отгрузки, затем список расхождений и журнал обработанных сообщений. На стенде гоняли архив переписки с поставщиками.
Проверка Прогнали архив писем, включая частичные подтверждения, уведомления об изменении цены и повторную доставку одного и того же письма. Провалом считали строку с расхождением по цене или количеству, проведённую без пометки, и одно письмо поставщика, проведённое дважды.
Передача Передали разборщик писем, правила сопоставления строк, список расхождений и журнал обработанных сообщений. Закупщикам показали работу со списком расхождений и инструкцию по спорным письмам, администратору — заведение поставщика и правку соответствия артикулов.
Архитектура
Коллектор на Node.js забирает письма по IMAP, кладёт тело и вложения в объектное хранилище и ставит задачу разбора в BullMQ поверх Redis. Разборщик приводит строки подтверждения к схеме заказа и сопоставляет их со строками заказа поставщику по артикулу и единице измерения, обращаясь к учётной системе через HTTP-сервисы 1С. Message-ID письма объявлен уникальным ключом в PostgreSQL, поэтому повторная доставка того же ответа поставщика попадает на уже обработанную запись и строки не проводятся второй раз. Сошедшиеся строки проводятся сразу, строки с расхождением по цене или количеству помечаются и собираются отдельным списком в кабинете закупщика на React, а журнал разбора хранит исходное письмо и результат сопоставления по каждой строке.
Распознавание выкладки и пустых мест на полке по кадрам
Задача, решение и что передали
Задача
Пустоты на полке замечали при обходе зала, а пересчёт фейсингов сводился к бумажным чек-листам.
Решение
Кадры потолочных камер приводятся к плоскости полки по калибровке и режутся на секции планограммы. Детектор считает фейсинги по товарной позиции и отмечает участок без товара, метка держится, только если повторяется на кадрах подряд. Отклонение от планограммы уходит задачей в приложение сотрудника зала с указанием стеллажа и полки.
Сдано 4
Переданы детектор фейсингов
Привязка секций к планограмме
Задачи в приложении сотрудника зала
Набор размеченных кадров
Как делали
Обследование Прошли обход зала с бумажными чек-листами пересчёта фейсингов и разобрали планограмму по секциям. Посмотрели, что реально видят потолочные камеры и какие стеллажи перекрыты покупателями и паллетами.
Архитектура Кадры приводим к плоскости полки по калибровке и режем на секции планограммы, чтобы счёт шёл по товарной позиции, а не по всему кадру. Метку пустого участка держим, только если она повторяется на кадрах подряд — иначе покупатель перед полкой давал ложную задачу сотруднику зала.
Разработка Сначала калибровка камер и привязка секций к планограмме, затем детектор фейсингов и отметка участка без товара, затем правило повторяемости и постановка задачи в приложение сотрудника зала. На стенде размечали кадры и гоняли на записанных потоках.
Проверка Сравнивали подсчёт фейсингов и отметки пустот с обходом зала по тем же стеллажам, отдельно смотрели часы с потоком покупателей. Провалом считали задачу на стеллаж, где товар стоит, и пустоту, отмеченную не в той секции планограммы.
Передача Передали детектор фейсингов, привязку секций к планограмме, задачи в приложении сотрудника зала и набор размеченных кадров. Сотрудникам зала показали работу с задачами, службе торгового зала — перепривязку секций после смены планограммы.
Архитектура
Кадры потолочных камер снимаются по RTSP, приводятся к плоскости полки гомографией из калибровки и режутся на секции планограммы — на той же калибровке держится привязка детекции к стеллажу и полке. Детектор YOLO исполняется в пуле воркеров в Kubernetes, кадры идут к ним через очередь: камер много, поток каждой неравномерен, и очередь позволяет держать число воркеров по общей нагрузке, а не по числу камер. Метка пустого участка ставится не по одному кадру, а по повтору на нескольких подряд, чтобы проходящий покупатель не порождал задачу сотруднику. Счётчики фейсингов и отклонения от планограммы пишутся в PostgreSQL, задача с указанием стеллажа и полки уходит в приложение сотрудника зала через его API, кадры спорных секций складываются в объектное хранилище.
Мастер-система справочника номенклатуры для нескольких баз учёта
Задача, решение и что передали
Задача
Одна и та же позиция жила в каждой базе под своим кодом и наименованием, а сопоставляли их вручную в таблице.
Решение
Собрана мастер-база НСИ: нормализация наименований, единиц измерения и штрихкодов, заведение позиции только через заявку с проверкой на дубли по штрихкоду и нормализованному имени. Изменения расходятся подписчикам сообщениями, каждая база хранит соответствие своего кода мастер-коду. Слияние карточек-двойников выполняется с сохранением истории кодов и ссылок в документах.
Сдано 4
Мастер-база НСИ
Регламент заведения позиции
Правила дедупликации
Журнал слияний карточек
Как делали
Обследование Сняли справочники номенклатуры из каждой базы учёта и сопоставили одну и ту же позицию под разными кодами и наименованиями. Разобрали ручную таблицу сопоставления и нашли двойников по штрихкоду и по написанию наименования.
Архитектура Завели мастер-базу НСИ с нормализацией наименований, единиц измерения и штрихкодов; позиция создаётся только через заявку с проверкой на дубли, прямая правка справочника в базах-подписчиках закрыта. Изменения расходятся подписчикам сообщениями, каждая база держит соответствие своего кода мастер-коду, поэтому прежние коды в документах остаются рабочими.
Разработка Сначала нормализация и загрузка справочников с разбором двойников, затем заявка на заведение позиции с проверкой на дубли, затем рассылка изменений подписчикам и слияние карточек с сохранением истории кодов. На стенде подняли копии баз и прогоняли обмен между ними.
Проверка Заводили позиции через заявку, сливали известные двойники и открывали документы с прежними кодами, отдельно гоняли обмен при недоступном подписчике. Провалом считали слияние, после которого документ терял ссылку на позицию, и заведённый дубль по уже существующему штрихкоду.
Передача Передали мастер-базу НСИ, регламент заведения позиции, правила дедупликации, журнал слияний карточек и таблицу соответствия кодов. Службе НСИ показали разбор заявок и слияние двойников, администраторам баз — подписку на изменения.
Архитектура
Мастер-база собрана на ASP.NET Core: позиция заводится только заявкой, которая проходит нормализацию наименования, единиц измерения и штрихкода и проверку на дубли — уникальные индексы по штрихкоду и нормализованному имени стоят в PostgreSQL, поэтому двойник не проходит на запись, а не отлавливается сверкой позже. Изменения расходятся подписчикам событиями через RabbitMQ типами «создана», «изменена», «слита»: подписчиков несколько и они не всегда доступны, а очередь с подтверждением даёт каждой базе догнать поток самостоятельно. Соответствие локального кода мастер-коду хранит и мастер-база, и каждая учётная база, обмен с 1С идёт через её HTTP-сервисы. Слияние карточек-двойников выполняется транзакцией с записью в журнал слияний, а прежние коды остаются действующими псевдонимами, поэтому ссылки в уже проведённых документах не рвутся.
Мониторинг обменов и дежурное сопровождение интеграций
Задача, решение и что передали
Задача
О поломке обмена узнавали от менеджера, не нашедшего заказ, а причину искали по логам разных систем.
Решение
Каждый обмен снабжён метриками — возраст последнего успешного сообщения, длина очереди, ошибки разбора; пороги вынесены в конфигурацию и поднимают алерт до обращения пользователя. Сквозной идентификатор проставляется в точке входа и проходит через все звенья, поэтому цепочка поднимается по номеру документа. Повторная отправка запускается из панели, без правки данных в базах.
Сдано 4
Панель состояния обменов
Набор алертов с порогами
Сквозная трассировка сообщений
Регламент дежурства
Как делали
Обследование обменов Составили список действующих обменов и разобрали недавние поломки по логам разных систем: сколько звеньев проходит документ и где именно терялся заказ, о котором сообщил менеджер. Зафиксировали, какие признаки поломки были видны раньше обращения пользователя.
Архитектура наблюдения За признаки здоровья взяли возраст последнего успешного сообщения, длину очереди и ошибки разбора, а пороги вынесли в конфигурацию, чтобы дежурный менял их без выпуска новой версии. Сквозной идентификатор решили проставлять в точке входа и протаскивать через все звенья, а повторную отправку делать штатным обменом, а не правкой данных в базах.
Разработка панели Сначала добавили выдачу метрик в каждом звене и панель состояния обменов, затем алерты по порогам, затем кнопку повторной отправки и сборку цепочки по номеру документа.
Проверка на поломках На стенде останавливали приёмник, портили формат сообщения и подмешивали ошибки разбора. Признаком провала считали алерт, пришедший позже обращения менеджера, и цепочку, которую не удавалось собрать целиком по номеру документа.
Передача дежурным Передали регламент дежурства, карточки типовых инцидентов и набор алертов с порогами, разобрали их на живых обменах. Дежурный сам меняет пороги, поднимает цепочку по номеру документа и перезапускает отправку из панели.
Архитектура
Каждый адаптер обмена отдаёт метрики в формате Prometheus — возраст последнего успешного сообщения, длина очереди, счётчик ошибок разбора; пороги вынесены в правила Alertmanager и правятся без пересборки адаптеров. Сквозной идентификатор проставляется в точке входа в шину, кладётся в заголовок сообщения и в каждую строку лога, логи собираются в OpenSearch, поэтому цепочка поднимается по номеру документа. Коллектор на Go архивирует сырые сообщения в PostgreSQL с ограниченным сроком хранения, и повторная отправка из панели публикует исходное сообщение в ту же очередь, а не правит данные приёмника напрямую — состояние меняет тот же обработчик, что и при первой доставке. Панель дежурного и графики собраны в Grafana поверх этих же источников.
Стек
Go · chi · PostgreSQL · OpenSearch · RabbitMQ · Kubernetes · Prometheus · Alertmanager · Grafana
Кабинет оптового клиента с заказом, остатками и сверкой
Задача, решение и что передали
Задача
Свою цену и остаток клиент запрашивает у менеджера письмом, заказ собирается в почте, а акт сверки готовится выгрузкой по запросу.
Решение
Кабинет читает номенклатуру, персональные цены и свободный остаток из учётной системы по расписанию и по событию изменения, резерв ставится в момент оформления заказа. Дебиторка и акт сверки собираются запросом к регистрам и выгружаются в PDF и XLSX. Права клиента выводятся из его договоров: чужие цены, отгрузки и документы в выдачу не попадают.
Сдано 4
Кабинет клиента
Обмен с учётной системой
Модель прав по договору
Инструкция менеджера
Как делали
Обследование заказов Разобрали переписку менеджеров с клиентами: как запрашивается персональная цена и свободный остаток, как собирается заказ в почте и готовится акт сверки выгрузкой. Выяснили, по каким договорам клиент вправе видеть отгрузки и документы.
Архитектура прав Номенклатуру, персональные цены и свободный остаток решили читать из учётной системы по расписанию и по событию изменения, а резерв ставить в момент оформления заказа. Права вывели из договоров клиента, чтобы чужие цены, отгрузки и документы не попадали в выдачу.
Разработка кабинета Сначала обмен с учётной системой и модель прав по договору, затем каталог с персональной ценой и остатком и оформление заказа с резервом, затем дебиторка и акт сверки с выгрузкой в PDF и XLSX.
Проверка выдачи Сверяли акт сверки и дебиторку из кабинета с регистрами учётной системы и прогнали нагрузочные сценарии на каталог. Провалом считали появление в выдаче цены или отгрузки по чужому договору и заказ, оформленный сверх свободного остатка.
Передача менеджерам Передали кабинет клиента, обмен с учётной системой, модель прав по договору, инструкцию менеджера и сценарии нагрузочного теста каталога. Менеджер сам открывает клиенту доступ по договору и разбирает расхождение остатка.
Архитектура
Кабинет на Ktor читает не учётную систему, а свою витрину: номенклатура, персональные цены и свободный остаток реплицируются из 1С:УТ по расписанию и по событию изменения в PostgreSQL, горячие срезы каталога лежат в Redis. Реплика на чтение выбрана потому, что каталог листают клиенты одновременно, а учётный контур занят проведением документов и закрытием периода — просмотр не должен конкурировать с ними за блокировки. Резерв ставится синхронным вызовом в 1С в момент оформления заказа: это единственное место, где решение о доступности принимается окончательно. Права выводятся из договоров контрагента и подставляются фильтром в каждый запрос к витрине, сверка и дебиторка собираются фоновой задачей в PDF и XLSX.
Перевод торговой отчётности на отечественную BI-платформу
Задача, решение и что передали
Задача
Отчёты сети собирались в зарубежной BI, а после закрытия подписки расползлись по файлам на рабочих местах.
Решение
Дашборды пересобраны на отечественной платформе поверх колоночной витрины, метрики вынесены в общий слой расчёта с описанием формулы. Настроено разграничение по строкам: директор точки видит свои магазины, региональная роль — свой список. Рассылка отчётов переведена в расписание платформы, ручные выгрузки в таблицы отключены.
Сдано 4
Переданы витрина продаж
Реестр метрик с формулами
Правила разграничения строк
Расписание рассылок
Как делали
Обследование отчётов Собрали разошедшиеся по рабочим местам файлы отчётов сети, восстановили формулы метрик и сверили расхождения в одинаковых по названию показателях. Выписали, кто какие отчёты получает рассылкой и в каком разрезе.
Архитектура метрик Метрики вынесли в общий слой расчёта с описанием формулы, дашборды пересобрали поверх колоночной витрины. Разграничение сделали по строкам — директор точки видит свои магазины, региональная роль свой список, — а рассылку перевели в расписание платформы.
Разработка дашбордов Сначала витрина продаж и реестр метрик с формулами, затем дашборды под роли, затем разграничение по строкам и расписание рассылок. Ручные выгрузки в таблицы отключали по мере схождения показателей.
Проверка показателей Сверяли новые дашборды с прежними отчётами на одних и тех же данных и заходили под ролями директора точки и региона. Провалом считали расхождение продаж с проверенным расчётом и магазин в выдаче, не входящий в зону роли.
Передача аналитикам Передали витрину продаж, реестр метрик с формулами, правила разграничения строк и расписание рассылок, показали, как читается формула метрики. Аналитик сам добавляет показатель и заводит роль без обращения к разработчику.
Архитектура
Витрина продаж лежит в ClickHouse с партиционированием по дате и сортировкой по магазину и номенклатуре — отчёты читают срез «магазин на период» и агрегируют его, и колоночное хранение отвечает такому чтению, тогда как строчная СУБД поднимала бы всю строку чека. Сервис метрик на Axum собирает SQL из общего реестра формул и подставляет предикат разграничения по строкам, вычисленный из роли: фильтр уходит в запрос, а не в настройку панели, поэтому директор точки не увидит чужие магазины ни в отчёте, ни в выгрузке. Загрузка идёт Airflow-ом из кассового контура и учётной системы через Kafka. Дашборды BI-платформы переиспользуют реестр метрик, рассылка переведена на её расписание, права на ручную выгрузку в файлы сняты.
Юристы читали поступившее определение целиком, чтобы вручную выписать в карточку дела процессуальный срок, дату следующего заседания и позицию суда.
Решение
Подписной сервис разбирает поступивший судебный акт: определяет вид акта и инстанцию, процессуальный срок и следующее процессуальное действие, привязывая их к номеру дела. Резолютивная часть и мотивировка размечаются отдельно, из мотивировки собирается позиция суда со ссылкой на абзац. Досье разных доверителей разведены по хранилищам, поиск идёт только внутри своего досье, спорный разбор уходит в журнал на проверку.
Сдано 4
Передали подписной сервис разбора актов
Схему полей карточки дела
Разметку резолютивной и мотивировочной частей
Журнал спорных разборов
Как делали
Разбор актов Разобрали с юристами, что они выписывают из поступившего определения в карточку дела: процессуальный срок, дату следующего заседания, позицию суда. На живых актах посмотрели, чем отличаются виды актов и инстанции и где юристы чаще спотыкаются.
Разделение досье Решили размечать резолютивную часть и мотивировку раздельно, позицию суда собирать из мотивировки со ссылкой на абзац, а извлечённое привязывать к номеру дела. Досье разных доверителей развели по хранилищам, чтобы поиск шёл только внутри своего досье.
Сборка разбора Сделали приём акта с определением вида акта и инстанции, затем извлечение процессуального срока и следующего процессуального действия, затем разметку частей и сборку позиции суда. Спорный разбор направили в журнал на проверку юриста.
Сверка с карточками Сличали разбор с карточками дел, заполненными юристами вручную, и отдельно проверяли разделение досье. Провалом считали любой случай, когда в выдаче по делу оказался документ чужого доверителя, и срок, поставленный в карточку без ссылки на абзац акта.
Передача сервиса Передали подписной сервис разбора актов, схему полей карточки дела, разметку резолютивной и мотивировочной частей, журнал спорных разборов и инструкцию по подключению нового досье. Показали юристам, как проверять извлечённое по ссылке и как заводить досье нового доверителя.
Архитектура
Поступивший акт кладётся в MinIO, задача разбора уходит в Celery: сервис определяет вид акта и инстанцию, режет текст на резолютивную часть и мотивировку, снимает текстовый слой через pdfplumber и извлекает процессуальный срок и следующее действие с привязкой к номеру дела. Досье доверителей разведены по отдельным схемам и бакетам, а не разделены признаком в общей таблице — изоляция держится на подключении и ключах доступа, поэтому ошибка в условии запроса не открывает чужое досье. Поиск по абзацам мотивировки идёт через pgvector внутри схемы своего досье, ссылка на абзац хранится рядом с фрагментом, чтобы позиция суда возвращалась с указанием места в тексте. Разбор с уверенностью ниже порога в карточку дела не попадает, а уходит в журнал на проверку юриста; доступ — Keycloak, поставка подписчикам — Docker.
Брони с каналов переносили в шахматку руками, а закрытие продаж на дату и правку тарифных планов делали в кабинете каждого канала отдельно.
Решение
Поставили обмен доступностью, тарифами и остатком мест: изменение шахматки уходит во все каналы одним сообщением, а бронь возвращается с внешним номером и ключом идемпотентности, поэтому повтор от канала дубля не создаёт. Отмена и перенос дат меняют существующую бронь, а не заводят соседнюю. Категории номеров и тарифные планы сведены в один справочник и раздаются каналам из него.
Сдано 4
Передали коннекторы каналов
Справочник категорий и тарифных планов
Очередь входящих броней с журналом
Панель расхождений шахматки
Как делали
Обследование шахматки Разобрали с администраторами, как брони переносятся в шахматку руками и где возникают накладки, и как закрытие продаж на дату и правка тарифных планов делаются в кабинете каждого канала отдельно. Сверили категории номеров и тарифные планы между системой отеля и каналами — состав и названия разошлись у всех.
Архитектура обмена Категории номеров и тарифные планы свели в один справочник и раздаём каналам из него, чтобы правка делалась в одном месте. Изменение шахматки уходит во все каналы одним сообщением, бронь принимается с внешним номером и ключом идемпотентности, а отмена и перенос дат меняют существующую бронь, а не заводят соседнюю.
Сборка коннекторов Сначала справочник категорий и тарифных планов, потом выгрузка доступности, тарифов и остатка мест, потом приём броней, отмен и переносов с журналом. На стенде отрабатывали повтор от канала и одновременный приход брони на последний свободный номер.
Проверка повторами Гоняли повторные сообщения каналов, отмены и переносы дат, закрытие продаж на дату. Провалом считались дубль брони по одному внешнему номеру и расхождение остатка мест между шахматкой и каналом; не приняли бы и перенос дат, оставляющий прежнюю бронь висеть рядом с новой.
Передача дежурному Передали коннекторы каналов, справочник категорий и тарифных планов, очередь входящих броней с журналом, панель расхождений шахматки и регламент дежурного. Администратор закрывает дату и правит тариф из своей системы, расхождение по панели разбирает дежурный.
Архитектура
Изменение шахматки пишется в PostgreSQL и одним событием уходит в обменник RabbitMQ, откуда его разбирают коннекторы каналов; каждый коннектор говорит с каналом сообщениями OpenTravel XML и держит свой ограничитель частоты на счётчиках Redis. Веерная рассылка выбрана вместо последовательного обхода кабинетов, чтобы медленный или упавший канал не задерживал остальные: его сообщения копятся в собственной очереди и уходят после подъёма. Входящая бронь приходит вебхуком в таблицу входящих с уникальным ключом по каналу и внешнему номеру, поэтому повтор от канала дубля не создаёт, а отмена и перенос дат находят существующую бронь по тому же ключу и меняют её. Категории номеров и тарифные планы ведутся одним справочником и раздаются каналам из него; сервисы на chi живут в Kubernetes, расхождения шахматки ищет сверочная задача, её результат виден в Grafana.
Стек
Go · chi · PostgreSQL · Redis · RabbitMQ · Kubernetes · OpenTravel XML · Grafana
Помощники каждое утро обходили карточки дел в картотеке, переносили новые определения в папки клиентов и вручную выставляли сроки обжалования в календарь юриста.
Решение
Шлюз опрашивает картотеку по номерам дел из CRM, новые судебные акты и назначенные заседания ложатся в карточку клиента вложением и событием. Срок обжалования и срок подготовки отзыва считаются от публикации акта и встают задачей ответственному юристу. Опрос идёт очередью с ограничением частоты и хранит отметку последней прочитанной записи, поэтому перезапуск не задваивает документы в деле.
Сдано 4
Передали шлюз опроса картотеки
Привязку номеров дел к карточкам клиентов
Правила расчёта процессуальных сроков
Журнал загрузок
Как делали
Обследование картотеки Прошли утренний обход помощников: как обходятся карточки дел в картотеке, что переносится в папки клиентов и как сроки обжалования выставляются в календарь юриста. Собрали, по каким номерам дел ведётся работа в CRM и в каком виде туда попадают судебные акты и назначенные заседания.
Архитектура опроса Опрос картотеки вынесли в отдельную службу с очередью и ограничением частоты обращений, с отметкой последней прочитанной записи по каждому делу — перезапуск не должен задваивать документы. Расчёт процессуальных сроков — обжалование, подготовка отзыва — сделали правилом от даты публикации акта, чтобы срок не выставлялся вручную.
Сборка шлюза Сначала привязка номеров дел из CRM к опросу, потом загрузка актов и заседаний в карточку клиента вложением и событием, потом расчёт процессуальных сроков и постановка задач ответственному юристу. На стенде перезапускали службу посреди загрузки и разбирали дела, номер которых в CRM записан не по форме.
Проверка перезапуском Проверяли на делах, где акты публиковались подряд, и на перезапусках службы посреди опроса. Провалом считались задвоенный документ в деле и пропущенный акт, по которому не встала задача на обжалование; не приняли бы и нераспознанный номер дела, молча выпавший из опроса вместо попадания в разбор.
Передача помощникам Передали шлюз опроса картотеки, привязку номеров дел к карточкам клиентов, правила расчёта процессуальных сроков, журнал загрузок и инструкцию разбора нераспознанных дел. Помощник заводит дело номером в CRM, дальше акты и заседания приходят сами, а спорные номера он разбирает по инструкции.
Архитектура
Планировщик раскладывает номера дел из CRM в очередь RabbitMQ, воркеры на Axum опрашивают картотеку с общим ограничителем частоты на счётчиках Redis — лимит вынесен в общий счётчик, иначе каждый воркер считал бы свою норму и вместе они превысили бы допустимую частоту обращений. Отметка последней прочитанной записи хранится по делу в PostgreSQL, а не в памяти воркера: перезапуск продолжает опрос с того же места и документы в деле не задваивает. Файлы судебных актов кладутся в MinIO, ключом служит хеш содержимого, поэтому переопубликованный судом документ не подшивается в карточку вторым вложением. Срок обжалования и срок подготовки отзыва считаются от даты публикации и уходят задачей ответственному юристу через API Bitrix24, нераспознанные номера дел копятся в отдельном реестре; контур поднимается Docker Compose, отставание опроса видно в Grafana.
Портал сотрудника с графиком смен и медицинскими книжками
Задача, решение и что передали
Задача
График смен по точкам управляющие вели в таблицах и мессенджере, а сроки личных медицинских книжек проверяли перед приходом инспекции.
Решение
Портал ведёт график смен по точкам и позициям — повар горячего цеха, кассир, официант — с нормой часов, отгулами и заменами. Смена не публикуется на сотрудника с истёкшей личной медицинской книжкой или непройденной аттестацией по стандартам блюда, а освободившаяся смена выкладывается на биржу подработки внутри сети. Отработанные часы уходят в табель и расчёт зарплаты, обмен сменами подтверждают оба сотрудника и управляющий точки.
Сдано 4
Портал сотрудника
График смен по точкам и позициям
Контроль сроков медицинских книжек
Биржа свободных смен
Как делали
Обследование графиков Посмотрели, как управляющие ведут график смен в таблицах и мессенджере и как перед приходом инспекции проверяются сроки личных медицинских книжек. Разобрали позиции и норму часов — повар горячего цеха, кассир, официант, — порядок замен и отгулов и то, как отработанные часы попадают в табель и расчёт зарплаты.
Архитектура смены График ведём по точкам и позициям, а публикацию смены закрыли проверками: сотрудник с истёкшей личной медицинской книжкой или непройденной аттестацией по стандартам блюда в смену не встаёт. Освободившаяся смена выкладывается на биржу подработки внутри сети, а обмен подтверждают оба сотрудника и управляющий точки — иначе точка остаётся без позиции.
Сборка портала Сначала график смен по точкам и позициям с нормой часов, потом контроль сроков медицинских книжек и аттестаций, потом биржа свободных смен с подтверждением обмена, потом выгрузка отработанных часов в расчёт зарплаты. На стенде прогоняли замену в день смены и выход сотрудника на чужую точку.
Проверка табеля Прогнали графики нескольких точек вместе с управляющими и сверили отработанные часы с табелем. Провалом считались смена, опубликованная на сотрудника с истёкшей медицинской книжкой, и обмен, закрытый без подтверждения управляющего точки; не приняли бы и часы, ушедшие в расчёт не по той позиции.
Передача управляющим Передали портал сотрудника, график смен по точкам и позициям, контроль сроков медицинских книжек, биржу свободных смен и выгрузку табеля в расчёт зарплаты. Управляющий ведёт график из портала, кадровая служба следит за книжками и аттестациями по тем же данным.
Архитектура
Сервис графика на NestJS ведёт смены по точкам и позициям в PostgreSQL, а публикация смены проходит через правило допуска: срок личной медицинской книжки и аттестация проверяются в момент публикации и повторно ночной задачей, поэтому истёкшая книжка снимает и уже опубликованную смену. Биржа подработки закрывает свободную смену транзакцией с уникальным ограничением на смену, поэтому двое сотрудников одну смену не займут, а обмен подтверждают оба сотрудника и управляющий точки. Мобильное приложение на React Native получает уведомления через FCM и данные из того же API, Redis держит кэш опубликованного графика по точке — экран смен открывают чаще, чем меняют расписание. Отработанные часы уходят в 1С:ЗУП сообщениями RabbitMQ с ключом периода и сотрудника, повтор выгрузки табель не задваивает; контур поднимается Docker Compose.
Этический экран в поиске ассистента по делам доверителей
Задача, решение и что передали
Задача
Ассистент искал по всему архиву фирмы, и материалы дела, закрытого этическим экраном, могли попасть юристу из группы противоположной стороны.
Решение
Из системы учёта дел забираем списки конфликтов интересов и составы рабочих групп, а каждый фрагмент помечаем номером дела и доверителем. Перед обращением к индексу собирается допустимое множество дел для конкретного юриста: экранированные дела вычитаются, и поиск идёт уже по остатку. Смена состава группы разъезжается по правам без переиндексации архива.
Сдано 4
Передали правила этических экранов
Карту атрибутов фрагментов
Журнал отказов по делам
Протокол проверки на конфликтных парах
Как делали
Разбор архива Разобрали, как в фирме ведётся учёт дел и конфликтов интересов — списки конфликтов, составы рабочих групп, привязка дела к доверителю — и что из архива попадает в индекс ассистента. На живых запросах показали, что материалы дела под этическим экраном достижимы юристу из группы противоположной стороны.
Экраны в правах Экранирование решили считать на стороне прав, а не текста: из системы учёта дел забираются конфликты и составы групп, и для юриста собирается допустимое множество дел вычитанием экранированных. Номер дела и доверителя проставили самому фрагменту, чтобы смена состава группы не требовала переиндексации архива.
Разметка и выгрузка Разметили фрагменты номером дела и доверителем, собрали выгрузку конфликтов и составов рабочих групп из учётной системы и встроили сборку допустимого множества дел перед обращением к индексу. Отказ по экранированному делу стали писать отдельной записью.
Конфликтные пары Подобрали конфликтные пары дел и опрашивали ассистента от имени юриста каждой стороны, включая переводы юриста между группами. Провалом считали появление в выдаче любого фрагмента экранированного дела, а также сохранение доступа после вывода юриста из состава рабочей группы.
Передача фирме Отдали правила этических экранов, карту атрибутов фрагментов, журнал отказов по делам и протокол проверки на конфликтных парах; с ответственным за конфликты прошли ведение списков. Фирма сама ставит экран по новому делу и меняет состав группы.
Архитектура
Коннектор на ASP.NET Core забирает из системы учёта дел списки конфликтов интересов и составы рабочих групп и материализует в PostgreSQL матрицу «юрист — допустимые дела»; доступ юриста берётся из Active Directory. Поиск читает эту матрицу, а не дёргает учётную систему на каждом обращении: её интерфейс отвечает пакетно, и ассистент иначе ждал бы её доступности на каждый запрос. Фрагменты в OpenSearch помечены номером дела и доверителем, экранированные дела вычитаются из множества до обращения к индексу, поэтому смена состава группы расходится по правам обновлением строки матрицы без переиндексации архива. Изменения составов и конфликтов приходят сообщениями RabbitMQ, отказы по делам пишутся отдельным журналом через Serilog, схема ведётся Entity Framework, узлы разложены в Docker.
Стек
C# · ASP.NET Core · PostgreSQL · OpenSearch · RabbitMQ · Docker · Active Directory · Entity Framework · Serilog
Обезличивание резюме до обращения к языковой модели
Задача, решение и что передали
Задача
Резюме уходили в модель целиком — с именем, телефоном, адресом и фотографией кандидата, хотя для сопоставления с вакансией нужен был только опыт.
Решение
Перед промптом из резюме вырезаются имя, контакты, дата рождения, адрес и ссылки на профили, а на их место встают метки; таблица соответствия лежит в отдельном хранилище со своими правами. Модель сравнивает опыт, отрасли и рабочие допуски с требованиями вакансии, а исходный текст рекрутер видит только в карточке отклика. В журнал обращений и в архив промптов попадает обезличенная версия.
Сдано 4
Передали словарь заменяемых полей
Сервис подстановки меток
Обезличенный архив промптов
Инструкцию рекрутера по раскрытию карточки
Как делали
Разбор промптов Посмотрели, что уходит в модель при сопоставлении резюме с вакансией, и выделили поля, реально участвующие в подборе: опыт, отрасли, рабочие допуски. Прошли по архиву промптов и нашли там имена, телефоны, адреса и ссылки на профили кандидатов.
Слой обезличивания Обезличивание вынесли до промпта: имя, контакты, дата рождения, адрес и ссылки на профили заменяются метками, а таблица соответствия живёт в отдельном хранилище со своими правами. Исходный текст резюме решили показывать только в карточке отклика у рекрутера, а в журнал обращений и архив промптов писать обезличенную версию.
Сервис меток Собрали словарь заменяемых полей и сервис подстановки меток, перевели на него поток резюме и отделили хранилище соответствий от основной базы. Карточку отклика доработали так, чтобы раскрытие данных кандидата было отдельным действием рекрутера.
Прогон резюме Прогнали через сервис разноформатные резюме — контакты в подписи, ссылки на профили во вложении, адрес внутри текста — и просмотрели архив промптов. Провалом считали любое персональное поле, доехавшее до модели или осевшее в журнале обращений в открытом виде.
Передача рекрутерам Отдали словарь заменяемых полей, сервис подстановки меток, обезличенный архив промптов и инструкцию рекрутера по раскрытию карточки; с рекрутерами прошли работу с обезличенным откликом. Агентство само дополняет словарь при появлении новых форматов резюме.
Архитектура
Сервис обезличивания на Axum стоит между кабинетом рекрутера и инференсом и принимает резюме по gRPC. Распознавание идёт правилами и NER-моделью через ONNX Runtime: правила ловят телефоны, адреса, даты рождения и ссылки на профили, модель — имена и названия; одних правил мало, потому что имя не берётся регулярным выражением, а телефон незачем отдавать модели ради разметки. Метки детерминированы в пределах резюме — одно исходное значение даёт одну метку, поэтому модель видит, что упоминания в разных разделах относятся к одному человеку, и сопоставляет опыт, отрасли и рабочие допуски с требованиями вакансии. Таблица соответствия меток лежит в отдельной базе PostgreSQL со своей ролью и ключом из HashiCorp Vault, раскрытие карточки отклика — отдельный вызов, а в журнал обращений и архив промптов в MinIO уходит обезличенная версия; узлы разложены в Kubernetes, счётчики в Prometheus.
Складской контур металлобазы с моделью по сертификатам и сортаменту
Задача, решение и что передали
Задача
Остатки по маркам и плавкам вели в таблицах, а сертификат на партию искали в папке по памяти кладовщика.
Решение
Построили складской контур с нуля: карточка сортамента с плавкой и сертификатом качества, адресное хранение по стеллажам и пачкам, задания на резку и отгрузку на терминалах кладовщика, журнал движений с привязкой к плавке. В том же контуре подняли языковую модель на своих GPU-узлах: она отвечает по сертификатам, стандартам и карточке остатка со ссылкой на конкретный документ.
Сдано 4
Передали складской контур
Узел инференса
Регламент индексации сертификатов
Схему адресного хранения
Как делали
Обход металлобазы Прошли металлобазу с кладовщиками и резчиками — как ведутся остатки по маркам и плавкам, где лежат сертификаты качества на партию, как выдаётся задание на резку и отгрузку. Увидели, что остатки живут в таблицах, а сертификат ищется по памяти кладовщика в папке.
Карточка и плавка За основу взяли карточку сортамента с плавкой и сертификатом качества и адресное хранение по стеллажам и пачкам, чтобы каждое движение привязывалось к плавке. Языковую модель решили поднимать на своих GPU-узлах внутри контура и отвечать только со ссылкой на конкретный документ — сертификат, стандарт или карточку остатка.
Сборка контура Собрали складской контур с нуля — карточка сортамента, адресное хранение, задания на резку и отгрузку на терминалах кладовщика, журнал движений с привязкой к плавке. Параллельно подняли узел инференса и регламент индексации сертификатов, обкатав ответы на стенде до выхода в цех.
Приёмка и резка Провели на терминалах приёмку, резку и отгрузку партий и проверили ответы модели по сертификатам и стандартам, сверяя их со скан-копиями документов. Ответ по сертификату без ссылки на конкретный документ и движение по складу, не привязанное к плавке, считались провалом и держали контур на стенде.
Передача кладовщикам Отдали складской контур, узел инференса, регламент индексации сертификатов, схему адресного хранения и инструкции кладовщика и резчика; на площадке прошли смену целиком. Металлобаза сама заводит сортамент, индексирует новые сертификаты и правит адресное хранение.
Архитектура
Ядро склада на FastAPI ведёт карточку сортамента с плавкой и сертификатом качества, адресное хранение по стеллажам и пачкам и журнал движений в PostgreSQL; терминалы кладовщика и резчика держат локальную очередь заданий и досылают отметки, потому что связь в пролёте рвётся, а отгрузка отмечается на месте. Сканы сертификатов лежат в MinIO, их текст разбирается и индексируется в OpenSearch с полем плавки и номера сертификата: поиск по индексу идёт до обращения к модели, поэтому ответ всегда несёт ссылку на конкретный документ, а модель собирает формулировку только из найденного. Инференс вынесен отдельным сервисом vLLM на GPU-узлах, обращения ставятся в очередь RabbitMQ с ограничением одновременных генераций — иначе пакетная индексация партии сертификатов заняла бы карты, и вопрос кладовщика ждал бы её окончания. Остатки и справочники сортамента кэшируются в Redis, узлы разложены в Docker.
Собственная CRM дилерского центра с моделью подсказок в контуре
Задача, решение и что передали
Задача
Коробочная CRM не держала сделку с трейд-ин, кредитом и допоборудованием, и менеджеры вели её в блокнотах и мессенджере.
Решение
Написали CRM с нуля под сделку дилера: карточка автомобиля с идентификатором и историей, воронка от заявки до выдачи, трейд-ин с оценкой, заявки в банки и страховые, наряды сервиса, склад допоборудования. Языковая модель работает на серверах центра: она разбирает переписку и расшифровки звонков, заполняет поля сделки и подсказывает следующий шаг по регламенту.
Сдано 4
Передали CRM
Узел инференса
Регламент разбора переписки
Модель ролей и прав
Как делали
Разбор сделки Прошли с менеджерами сделку целиком — заявка, трейд-ин с оценкой, кредит, страховка, допоборудование, выдача — и увидели, что коробочная CRM её не держит, а работа идёт в блокнотах и мессенджере. Выписали поля сделки, которые заполняются со слов клиента и по расшифровкам звонков.
Карточка автомобиля CRM решили строить вокруг карточки автомобиля с идентификатором и историей и воронки от заявки до выдачи, подвесив к ней трейд-ин, заявки в банки и страховые, наряды сервиса и склад допоборудования. Языковую модель разместили на серверах центра, чтобы переписка и расшифровки звонков не выходили наружу, а подсказка следующего шага опиралась на регламент дилера.
Сборка CRM Написали CRM с нуля, подключили заявки в банки и страховые, наряды сервиса и склад допоборудования, подняли узел инференса внутри центра. Разбор переписки и расшифровок обкатывали на архиве сделок до перевода менеджеров.
Тестовые сделки Провели тестовые сделки от заявки до выдачи, включая трейд-ин и кредит, и сверили поля, заполненные моделью по переписке, с тем, что говорил клиент. Поле сделки, заполненное без основания в переписке или расшифровке, и подсказка мимо регламента считались провалом и возвращали разбор на доработку.
Передача продажам Отдали CRM, узел инференса, регламент разбора переписки, модель ролей и прав и инструкции менеджера и сервиса; с отделом продаж и сервисом прошли работу по сделке. Центр сам заводит роли и правит регламент и воронку.
Архитектура
Ядро CRM на Django ведёт сделку в PostgreSQL: карточка автомобиля с идентификатором и историей, воронка от заявки до выдачи, трейд-ин с оценкой, наряды сервиса, склад допоборудования. Заявки в банки и страховые уходят через отдельный интеграционный сервис с очередью RabbitMQ и идемпотентным ключом по номеру сделки — внешние интерфейсы отвечают с задержкой и повторами, и без ключа повторная отправка заводила бы вторую заявку по той же сделке. Звонки расшифровываются Whisper на тех же GPU-узлах, где стоит vLLM: модель разбирает переписку и расшифровки, заполняет поля сделки черновиком и предлагает следующий шаг по регламенту, менеджер подтверждает запись. Модель работает на серверах центра, поэтому переписка и данные клиента не выходят за его сеть; фоновые задачи ведёт Celery, справочники и сессии кэшируются в Redis, узлы разложены в Docker.
Биржа складских остатков металлопроката с агентным бэкофисом
Задача, решение и что передали
Задача
Остатки и заявки ходили в почте и файлах, каждую позицию прайса менеджер заводил и пересчитывал руками.
Решение
Построили площадку с нуля: каталог сортамента с нормализацией марок и стандартов, витрина остатков по складам, лоты и резервы, расчёт цены с коэффициентами реза, упаковки и доставки, кабинет покупателя. Бэкофис ведут агенты: разбирают прайсы поставщиков из писем и вложений, приводят строки к сортаменту каталога, поднимают лот и ставят резерв под заявку. Неопознанные позиции складываются в очередь товароведа рядом с исходной строкой прайса.
Сдано 4
Переданы модель сортамента
Регламент нормализации прайсов
Журнал сопоставления позиций
Панель разбора очередей бэкофиса
Как делали
Сбор прайсов Разобрали, как остатки и заявки ходят в почте и файлах и как менеджер заводит каждую строку прайса руками. Собрали прайсы разных поставщиков и увидели, насколько по-разному в них пишутся марка стали, стандарт и размер.
Каталог сортамента В основу положили каталог сортамента с нормализацией марок и стандартов, к нему привязали витрину остатков по складам, лоты и резервы и расчёт цены с коэффициентами реза, упаковки и доставки. Бэкофис отдали агентам, но неопознанные позиции решили не угадывать — они складываются в очередь товароведа рядом с исходной строкой прайса.
Площадка и агенты Построили площадку с нуля — каталог, витрина остатков, лоты и резервы, расчёт цены, кабинет покупателя. Затем подняли агентов, разбирающих прайсы поставщиков из писем и вложений, приводящих строки к сортаменту каталога и ставящих резерв под заявку.
Прогон сопоставления Прогнали через агентов накопленные прайсы разных поставщиков и сверили сопоставленные позиции с каталогом вместе с товароведом, отдельно проверив расчёт цены с коэффициентами реза, упаковки и доставки. Строка прайса, отнесённая не к той марке или стандарту, считалась провалом: такая позиция обязана была уходить в очередь товароведа, а не в лот.
Передача товароведам Передали модель сортамента, регламент нормализации прайсов, журнал сопоставления позиций и панель разбора очередей бэкофиса; с товароведами и менеджерами прошли разбор очереди и выставление лота. Заказчик сам пополняет каталог и правит правила нормализации.
Архитектура
Ядро площадки на Go с chi держит каталог сортамента, лоты и резервы в PostgreSQL: резерв ставится транзакцией с проверкой остатка, поэтому две заявки не забирают одну пачку. Витрина остатков по складам и история цен лежат в ClickHouse — подбор по складам и пересчёт цены с коэффициентами реза, упаковки и доставки идут срезами по нескольким полям большого перечня позиций, и колоночное хранилище читает только их. Прайсы поставщиков забираются из почтового ящика, вложения складываются в MinIO, а разбор ведут воркеры за Kafka: строка прайса приводится к позиции каталога сопоставлением марки, стандарта и размера через нечёткий поиск в OpenSearch, неопознанная строка уходит в очередь товароведа рядом с исходным текстом, а не отбрасывается. Сопоставление пишется в журнал с ключом по строке прайса, поэтому повторная присылка того же файла не заводит новую позицию, узлы разложены в Kubernetes, очереди бэкофиса выведены в Grafana.
Стек
Go · chi · PostgreSQL · ClickHouse · Kafka · Kubernetes · MinIO · OpenSearch · Grafana
Проверяется: журнал инцидентов с цепочками пересылки
Контроль каналов передачи клиентских материалов с разбором вложений
Задача, решение и что передали
Задача
Макеты, сметы и брифы уходили с рабочих машин в личные мессенджеры и облака, вынос восстанавливали по памяти.
Решение
Развернули контур контроля: агенты на рабочих станциях, перехват почтового и файлового трафика через шлюз, теневое копирование вложений в отдельное хранилище, правила по меткам клиента и проекта. Разбор ведут агенты: классифицируют вложение по клиенту и типу материала, находят макеты и сметы в исходящем потоке, поднимают карточку инцидента с копией файла и цепочкой пересылки. Совпадения по меткам режимных проектов уходят офицеру безопасности отдельной очередью.
Сдано 4
Переданы политики перехвата по каналам
Схема меток клиентов и проектов
Теневой архив вложений
Журнал инцидентов с цепочками пересылки
Как делали
Обследование каналов Составили карту каналов, по которым уходят макеты, сметы и брифы — почта, мессенджеры, личные облака, съёмные носители, — и посмотрели, что реально лежит на рабочих станциях дизайнеров и аккаунтов. С руководителями групп собрали список режимных проектов и признаки, по которым материал относится к конкретному клиенту.
Архитектура перехвата Перехват развели по уровням: шлюз на почтовом и файловом трафике плюс агент на рабочей станции, теневые копии вложений вынесли в отдельное хранилище с ограниченным доступом. Разбор вложений сделали асинхронным относительно перехвата, чтобы классификация не держала отправку, а метки клиента и проекта завели отдельным справочником.
Разработка контура Сначала подняли шлюз и теневое копирование, затем справочник меток и правила, последними — агентов классификации вложения по клиенту и типу материала и сборку карточки инцидента с цепочкой пересылки. На стенде гоняли поток из копий рабочих папок: исходники макетов, сметы, брифы и архивы с вложенными файлами.
Проверка выносов Прогоняли отправки известных макетов и смет через каждый канал — письмо с архивом, загрузка в личное облако, пересылка в мессенджер — и смотрели, поднялась ли карточка с копией файла. Провалом считали материал режимного проекта, ушедший без записи в журнале, и такое срабатывание, попавшее в общую очередь вместо очереди офицера безопасности.
Передача безопасности Передали политики перехвата по каналам, схему меток клиентов и проектов, теневой архив вложений и журнал инцидентов, показав разбор цепочки пересылки от карточки до исходного файла. Служба безопасности сама заводит режимный проект, правит метки и правила по типам материалов.
Архитектура
Перехват стоит в двух точках: агент на рабочей станции и шлюз, к которому почтовый сервер и веб-прокси обращаются по ICAP. Шлюз отдаёт вердикт по быстрым правилам синхронно, а вложение с теневой копией уходит в топик Kafka на разбор: вскрытие архивов и классификацию нельзя держать внутри SMTP-сессии, поэтому тяжёлая часть асинхронная. Теневые копии лежат в MinIO по хэшу содержимого, карточки инцидентов и метки клиентов и проектов — в PostgreSQL, извлечённый Tika текст вложений и цепочки пересылки — в OpenSearch, потому что офицер ищет по фрагменту текста и метке, а не по первичному ключу. Разборщик поднимается с сохранённого офсета, поток по режимным меткам вынесен отдельным топиком со своим потребителем.
Стек
Java · Spring Boot · PostgreSQL · OpenSearch · Kafka · Kubernetes · ICAP · MinIO · Apache Tika
Агентная обработка заявок и оценок трейд-ин дилера
Задача, решение и что передали
Задача
Заявки с площадок объявлений и звонки падали в общий ящик, оценку машины с пробегом собирали по трём прайсам руками.
Решение
В дилерской CRM собрали конвейер обработки лида: коннекторы к площадкам объявлений, телефонии и формам сайта, склейка обращений по номеру и VIN, карточка автомобиля с историей и комплектацией, расчёт вилки оценки по прайсам и состоянию. Агент собирает лид из всех каналов, поднимает VIN-разбор и комплектацию, готовит вилку оценки трейд-ин и ставит задачу на осмотр в отдел продаж. Повторные обращения по одному VIN сводятся в одну сделку с историей касаний.
Сдано 4
Переданы правила склейки обращений
Справочник комплектаций и VIN-разбора
Журнал оценок трейд-ин
Регламент передачи лида в продажи
Как делали
Обследование лидов Разобрали общий ящик и записи звонков, посмотрели, что приходит с площадок объявлений и форм сайта и как менеджер собирал оценку по трём прайсам. Выписали правила, по которым обращение считалось повторным, и то, что о машине известно кроме VIN.
Архитектура лида Склейку обращений завели по номеру телефона и VIN как основным ключам, а карточку автомобиля с историей и комплектацией сделали отдельной сущностью, к которой цепляются касания. Коннекторы к площадкам, телефонии и формам спрятали за общим интерфейсом лида, расчёт вилки оценки вынесли отдельно от воронки продаж.
Разработка конвейера Сначала коннекторы и приём лида из всех каналов, потом склейка по номеру и VIN, потом VIN-разбор с комплектацией и расчёт вилки по прайсам и состоянию. Постановку задачи на осмотр в отдел продаж и сведение повторов в одну сделку делали последними, обкатывая на выгрузке прошлых обращений.
Проверка склейки Прогоняли лиды с одинаковым VIN из разных каналов, обращения с одного номера по разным машинам и заявки с неполным VIN. Провалом считали два открытых лида по одному VIN и вилку оценки, собранную без разбора комплектации.
Передача продажам Передали правила склейки обращений, справочник комплектаций и VIN-разбора, журнал оценок трейд-ин и регламент передачи лида в продажи. Отдел продаж сам обновляет прайсы для вилки, правит правила склейки и подключает новую площадку объявлений.
Архитектура
Лиды приходят из трёх типов источников: опрос API площадок объявлений по расписанию, вебхуки форм сайта и события телефонии. Все они пишутся в Kafka с ключом партиции по VIN или номеру телефона — так обращения по одному автомобилю обрабатываются по порядку одним потребителем и не расползаются по разным сделкам. Сервис склейки ищет существующее обращение по естественному ключу в окне, потому что площадки присылают один и тот же лид повторно, и дедупликация обязательна до заведения сделки. Карточка автомобиля, VIN-разбор и справочник комплектаций лежат в PostgreSQL, вилка оценки трейд-ина считается отдельным сервисом по трём прайсам с кэшем в Redis, задача на осмотр ставится в отдел продаж, поиск по истории касаний идёт через Elasticsearch.
Собственная торговая площадка остатков проката с подбором позиций
Задача, решение и что передали
Задача
Складские остатки рассылались покупателям прайс-листами в почте, а замену отсутствующей марки менеджер искал вручную по нескольким базам филиалов.
Решение
Площадка собрана из каталога сортамента с нормализацией номенклатуры, витрины остатков филиалов, модуля заявок и сделок и расчёта цены с учётом резки и доставки. Поверх каталога работает модель подбора: по запрошенной марке, размеру и объёму она предлагает взаимозаменяемые позиции из остатков, опираясь на историю сделок и правила соответствия. Нормализация сводит разные написания марок и профилей к единому коду, поэтому остатки филиалов складываются в одну витрину.
Сдано 4
Торговая площадка
Каталог сортамента с нормализацией
Модель подбора замен
Журнал заявок и сделок
Как делали
Обследование прайсов Собрали прайс-листы филиалов, которые рассылались покупателям, и увидели, что одна марка и профиль пишутся в них по-разному. Разобрали историю сделок и расспросили менеджеров, чем они фактически заменяли отсутствующую позицию и какие правила соответствия держали в голове.
Архитектура каталога В основу положили нормализацию номенклатуры: разные написания марок и профилей сводятся к единому коду, поэтому остатки филиалов складываются в одну витрину. Модель подбора замен вынесли отдельной службой поверх каталога, расчёт цены с резкой и доставкой развели с витриной остатков.
Разработка площадки Сначала каталог сортамента и нормализация, потом витрина остатков филиалов и модуль заявок и сделок, потом расчёт цены с учётом резки и доставки. Модель подбора собирали последней: на стенде она работала на истории сделок и правилах соответствия по марке, размеру и объёму.
Проверка подбора Прогоняли запросы на марки, которых нет в остатках, и сверяли предложенные замены с решениями менеджеров по прошлым сделкам. Провалом считали замену, не проходящую по правилам соответствия марки, и позицию одного филиала, попавшую в витрину дважды из-за разного написания.
Передача менеджерам Передали площадку, каталог сортамента с нормализацией, модель подбора замен, журнал заявок и сделок и инструкции менеджеров. Менеджеры сами заводят позиции сортамента, правят правила соответствия для замен и подключают остатки нового филиала.
Архитектура
Ядро — сервис каталога и подбора с gRPC-интерфейсом, витрина и кабинеты ходят к нему через шлюз. Остатки филиалов приезжают выгрузками в Kafka, нормализатор сводит разные написания марок и профилей к каноническому коду, и только после этого строки складываются в одну витрину: без единого кода остатки филиалов несопоставимы. Каталог сортамента, заявки и сделки лежат в PostgreSQL, фасетный поиск — в OpenSearch, а подбор замен считается внутри самого сервиса: модель грузится через ONNX Runtime, индекс близости FAISS и правила соответствия держатся в памяти процесса, потому что подбор отвечает синхронно на запрос менеджера и лишний сетевой переход тут не нужен. Redis кэширует расчёт цены с резкой и доставкой; при недоступности филиала витрина отдаёт последнее состояние с меткой времени.
Собственная CRM дилерского центра с воронкой, трейд-ином и сервисом
Задача, решение и что передали
Задача
Продажи, трейд-ин и сервисные записи вели в коробочной CRM и трёх таблицах, из-за чего история клиента распадалась между отделами.
Решение
Построили систему с нуля: ядро карточки клиента и автомобиля по VIN, воронка сделок с этапами и напоминаниями, модуль трейд-ина с оценкой и выкупом, склад новых и подержанных машин с резервами, заявки на сервис с подменным фондом, конструктор коммерческих предложений и договоров. Ролевая модель разведена по дилерским центрам и отделам, телефония и почта подшиваются к карточке, обмен с учётной системой ведёт счета, оплаты и отгрузки через очередь с повтором неудачных сообщений.
Сдано 4
Схема данных и справочников
Описание модулей и ролевой модели
Регламент обменов с учётной системой
Инструкции отделов продаж и сервиса
Как делали
Обследование отделов Сняли, как ведутся продажи, трейд-ин и сервисные записи в коробочной CRM и трёх таблицах и где история клиента разрывается между отделами. Разобрали справочники учётной системы — счета, оплаты, отгрузки — и собрали от отделов формы коммерческих предложений и договоров.
Архитектура ядра За ядро взяли карточку клиента и автомобиля по VIN, к которой цепляются сделки, трейд-ин, склад и сервисные заявки. Ролевую модель развели по дилерским центрам и отделам, а обмен с учётной системой поставили через очередь с повтором неудачных сообщений, чтобы недоступность учёта не останавливала продажи.
Разработка модулей Собрали ядро карточки и воронку сделок с этапами и напоминаниями, затем трейд-ин с оценкой и выкупом, склад новых и подержанных машин с резервами и заявки на сервис с подменным фондом. Конструктор предложений и договоров и подшивку телефонии и почты делали последними, обмен с учётной системой обкатывали на стенде.
Проверка сквозная Гоняли сквозные сценарии — обращение, оценка трейд-ина, резерв машины, сделка, отгрузка, запись на сервис — и сверяли счета, оплаты и отгрузки с учётной системой. Провалом считали расхождение отгрузки в системе и в учёте и доступ сотрудника одного дилерского центра к сделкам другого.
Передача администраторам Передали схему данных и справочников, описание модулей и ролевой модели, регламент обменов с учётной системой, инструкции отделов продаж и сервиса и стенд проверки обновлений. Администраторы сами заводят дилерский центр, правят роли и обкатывают обновления на стенде до боевого контура.
Архитектура
Ядро — карточка клиента и автомобиля по VIN в PostgreSQL, поверх неё живут воронка сделок, модуль трейд-ина, склад новых и подержанных машин с резервами и заявки на сервис с подменным фондом. Ролевая модель разведена по дилерским центрам и отделам фильтром на уровне выборки, а не проверками в интерфейсе, поэтому один и тот же запрос из разных центров не возвращает чужие сделки. Обмен с учётной системой идёт очередью RabbitMQ с идемпотентным ключом по номеру документа: прямой синхронный вызов заблокировал бы оформление сделки на время недоступности учётной системы, а слепой повтор сообщения задваивал бы счета и оплаты. Резерв машины берётся транзакцией с блокировкой строки склада, телефония и почта подшиваются к карточке коннекторами, документы собираются конструктором из шаблонов.
Проверяется: журнал событий с привязкой к документу
Контур контроля выгрузок клиентской базы и почтового трафика
Задача, решение и что передали
Задача
Клиентскую базу и прайсы выгружали из CRM и учётной системы кто как умел, и следов, куда ушёл файл, не оставалось.
Решение
Поставили перехват на каналах: почтовый шлюз с разбором вложений и архивов, агент рабочих станций с теневым копированием на съёмные носители и печать, контроль веб-загрузок и мессенджеров через прокси. Выгрузки из CRM и учётной системы переведены на сервис экспорта с квотами, водяными знаками в файлах, карантином крупных отгрузок и подтверждением руководителем, события сводятся в журнал с привязкой к пользователю, документу и получателю.
Сдано 4
Карта каналов и точек перехвата
Политики контроля выгрузок
Регламент разбора инцидентов
Журнал событий с привязкой к документу
Как делали
Обследование выгрузок Собрали, кто и как выгружает клиентскую базу и прайсы из CRM и учётной системы, и убедились, что следов ухода файла не остаётся. Разметили каналы — почта, съёмные носители, печать, веб-загрузки, мессенджеры — и определили, какие выгрузки считаются крупными.
Архитектура перехвата Перехват поставили на всех каналах сразу: почтовый шлюз с разбором вложений и архивов, агент рабочих станций, прокси для веб-загрузок и мессенджеров. Сами выгрузки перевели на сервис экспорта с квотами и водяными знаками в файлах, крупные отгрузки уходят в карантин до подтверждения руководителем.
Разработка экспорта Сначала сервис экспорта с квотами и водяными знаками, потом почтовый шлюз и агент рабочих станций, потом прокси и карантин с подтверждением. Журнал с привязкой к пользователю, документу и получателю собирали последним, на стенде гоняли выгрузки из копий CRM и учётной системы.
Проверка каналов Выгружали клиентскую базу и прайсы и пытались вынести их каждым каналом, в том числе в архиве и с переименованием файла, и проверяли карантин на крупной отгрузке. Провалом считали выгрузку без водяного знака и файл, ушедший наружу без записи о пользователе, документе и получателе.
Передача безопасности Передали карту каналов и точек перехвата, политики контроля выгрузок, регламент разбора инцидентов, журнал событий с привязкой к документу и инструкции службы безопасности. Служба безопасности сама меняет квоты, ведёт карантин и разбирает инциденты по журналу.
Архитектура
Перехват стоит на трёх каналах: почтовый шлюз с разбором вложений и архивов, агент рабочих станций на съёмные носители и печать, прокси по ICAP для веба и мессенджеров. Выгрузки из CRM и учётной системы переведены на отдельный сервис экспорта: он держит квоту, наносит водяной знак и ставит крупную отгрузку в карантин до подтверждения руководителем, иначе файл уходит мимо контроля прямо из интерфейса исходной системы. Карточки инцидентов и политики живут в PostgreSQL, поток событий по каналам — в ClickHouse, полнотекст вложений — в Elasticsearch, теневые копии — в MinIO: карточку нужно обновлять транзакцией, а события только вставляются и читаются агрегатами по времени, поэтому под них разные хранилища. Между агентами, шлюзом и разборщиками идёт Kafka, при остановке разборщика события копятся в топике, а агент продолжает работать по локальному кэшу политик.
Дистрибьютор электроникиИнтеграции и сопровождение
Проверяется: панель мониторинга очередей
Интеграционная шина между учётом, CRM, складом и порталом
Задача, решение и что передали
Задача
Пять систем обменивались файлами по расписанию, каждая пара со своим форматом, и расхождение остатков находили только при инвентаризации.
Решение
Поставили шину с брокером очередей и реестром контрактов сообщений: канонические форматы номенклатуры, контрагента, заказа и отгрузки, версии схем с проверкой при публикации, адаптеры к учётной системе, CRM, складской системе и порталу дилера. Каждое сообщение получает идентификатор и трассировку сквозь адаптеры, ошибки уходят в очередь разбора с повтором и ручным подтверждением, панель мониторинга держит задержку и глубину очередей по каждому потоку.
Сдано 4
Реестр контрактов сообщений
Схемы канонических объектов
Карта адаптеров и потоков
Панель мониторинга очередей
Как делали
Обследование обменов Расписали все обмены между пятью системами — какие файлы по расписанию, в каком формате, кто кому шлёт — и нашли, где остатки расходятся до инвентаризации. Собрали справочники номенклатуры и контрагентов из каждой системы и сравнили, как один объект описан в учёте, CRM, складе и на портале дилера.
Архитектура шины Файловый обмен по расписанию заменили брокером очередей и реестром контрактов сообщений с каноническими форматами номенклатуры, контрагента, заказа и отгрузки. Схемы завели с версиями и проверкой при публикации, к каждой системе поставили адаптер, а ошибки развели в очередь разбора с повтором и ручным подтверждением.
Разработка адаптеров Сначала реестр контрактов и канонические схемы, потом брокер и адаптеры к учётной системе, CRM, складской системе и порталу дилера, потом трассировка сообщения сквозь адаптеры. Панель мониторинга задержки и глубины очередей собирали последней, обмены крутили на стенде с копиями справочников.
Проверка потоков Гоняли сквозные потоки заказа и отгрузки через все адаптеры, публиковали сообщения по устаревшей схеме, роняли адаптер на середине потока и сверяли остатки склада с учётом. Провалом считали сообщение, принятое без соответствия действующему контракту, и расхождение остатка, не попавшее в очередь разбора.
Передача дежурным Передали реестр контрактов сообщений, схемы канонических объектов, карту адаптеров и потоков, панель мониторинга очередей и регламент дежурного сопровождения обменов. Сопровождение само заводит новую версию схемы, подключает адаптер и разбирает очередь ошибок.
Архитектура
В центре брокер очередей, к каждой системе подключён свой адаптер: учётная система, CRM, складская система и портал дилера. Реестр контрактов хранит канонические схемы номенклатуры, контрагента, заказа и отгрузки с версиями, публикация проверяется по схеме и отбивается при несовпадении — так адаптеров становится по одному на систему, а не по одному на каждую пару, и подключение новой системы не трогает соседние потоки. Каждое сообщение получает идентификатор и трассировку сквозь адаптеры, ошибочные уходят в очередь разбора с повтором и ручным подтверждением, а повтор безопасен потому, что приёмники применяют сообщение по ключу документа. Состояние обменов и реестр контрактов лежат в PostgreSQL, задержка и глубина очередей по каждому потоку выведены в панель Grafana для дежурного.
Стек
Go · chi · PostgreSQL · RabbitMQ · Kubernetes · JSON Schema · 1С:Предприятие · OpenTelemetry · Grafana
Собственная ERP металлобазы с остатками по плавкам и резервированием
Задача, решение и что передали
Задача
Учёт держали в коробочной программе, где остаток не помнил плавку, тонны и метры сводили в отдельном файле, а бронь под заказ менеджер отмечал в блокноте.
Решение
ERP собрана с нуля: справочник номенклатуры с профилем, маркой стали и номером плавки; склад с местами хранения и пересчётом тонн, метров и штук по теоретическому весу; резервирование под заказ с очередью и сроком жизни брони; участок порезки с заданиями, учётом отходов и возвратом делового остатка на склад; отгрузка с подбором сертификатов качества по плавкам. Заказ, резерв, задание на порезку и отгрузочные документы живут в одной модели данных, статус меняется только проведением операции.
Сдано 4
Исходный код
Модель данных с описанием документооборота
Регламент пересчёта единиц измерения
Инструкции кладовщика и оператора порезки
Как делали
Обследование остатков Разобрали коробочную программу и убедились, что остаток в ней не помнит плавку, а тонны и метры сводятся в отдельном файле. С кладовщиками и операторами порезки выяснили, как держится бронь под заказ, как считают теоретический вес и что происходит с деловым остатком после реза.
Архитектура модели Номенклатуру описали профилем, маркой стали и номером плавки, чтобы сертификат подбирался по плавке при отгрузке. Заказ, резерв, задание на порезку и отгрузочные документы свели в одну модель данных, смену статуса разрешили только проведением операции, пересчёт тонн, метров и штук завели по теоретическому весу в складском узле.
Разработка склада Сначала справочник номенклатуры и склад с местами хранения и пересчётом единиц, потом резервирование под заказ с очередью и сроком жизни брони, потом участок порезки с заданиями, учётом отходов и возвратом делового остатка. Отгрузку с подбором сертификатов по плавкам делали последней, на стенде гоняли остатки, снятые с базы.
Проверка пересчётов Пересчитывали остатки в тоннах, метрах и штуках после приёмки, реза и возврата делового остатка, гоняли параллельную бронь на одну позицию и отгрузку с истёкшей бронью. Провалом считали отгрузку без сертификата по плавке отгружаемого металла и остаток, разошедшийся между единицами измерения после проведения операции.
Передача кладовщикам Передали исходный код, модель данных с описанием документооборота, регламент пересчёта единиц измерения, инструкции кладовщика и оператора порезки и формы сертификатов. Кладовщики сами ведут места хранения и брони, операторы закрывают задания на порезку с отходами и деловым остатком.
Архитектура
Ядро учёта — сервис с gRPC-интерфейсом, к нему подключены рабочие места на Qt и терминалы сбора данных, работающие тем же контрактом. Номенклатура с профилем, маркой стали и номером плавки, места хранения, резервы, задания на порезку и отгрузочные документы лежат в одной модели данных в PostgreSQL, статус меняется только проведением операции. Пересчёт тонн, метров и штук по теоретическому весу выполняется в ядре, а не в клиентах: коэффициенты должны быть одни для склада, продажи и порезки, иначе бронь и отгрузка разойдутся в единицах. Резерв берётся транзакцией с блокировкой строки остатка и живёт со сроком, поэтому две брони не уйдут на один прокат; Redis кэширует витрину остатков для просмотра, а отгрузка подбирает сертификаты качества по номерам плавок из тех же строк.
Контур контроля утечки данных складского учёта драгоценных металлов
Задача, решение и что передали
Задача
Прайс-листы, карточки изделий и остатки по камням выгружали в файлы и рассылали подрядчикам, а кто вынес выгрузку, выясняли постфактум по обрывкам переписки.
Решение
Вокруг складского учёта поставлен контур контроля: агенты рабочих мест с теневым копированием файлов на съёмные носители и в печать; разбор почтового трафика на вложения с карточками изделий и остатками; правила по признакам — артикул, проба, масса, номер партии камней; карантин отправлений до решения офицера безопасности; журнал событий с привязкой к учётной записи и документу учёта. Выгрузка из складского контура получает метку, метка едет с файлом и проверяется на каждом канале.
Сдано 4
Исходный код агентов и шлюза
Правила детекции по признакам изделий
Журнал событий с поиском
Регламент карантина отправлений
Как делали
Обследование выгрузок Посмотрели, как из складского учёта выгружаются прайс-листы, карточки изделий и остатки по камням и кому эти файлы рассылались. Разобрали, по каким признакам такой файл узнаётся — артикул, проба, масса, номер партии камней — и чем заканчивались прошлые разбирательства по переписке.
Архитектура меток Контур поставили вокруг складского учёта: выгрузка получает метку при формировании, метка едет с файлом и проверяется на каждом канале. Перехват развели по агентам рабочих мест со съёмными носителями и печатью и по разбору почтового трафика, отправление с признаками изделий уходит в карантин до решения офицера безопасности.
Разработка агентов Сначала маркировка выгрузок из складского контура, потом агенты рабочих мест с теневым копированием и почтовый шлюз с разбором вложений, потом правила детекции по артикулу, пробе, массе и номеру партии камней. Карантин отправлений и журнал событий с привязкой к учётной записи и документу учёта собирали последними.
Проверка выносов Выносили помеченные выгрузки на съёмный носитель, в печать и почтой, в том числе перепакованными в архив и с изменённым именем файла, и смотрели, сработала ли детекция и встало ли отправление в карантин. Провалом считали выгрузку с остатками по камням, покинувшую периметр без записи в журнале и без карантина.
Передача офицеру Передали исходный код агентов и шлюза, правила детекции по признакам изделий, журнал событий с поиском, регламент карантина отправлений и инструкции офицера безопасности. Офицер безопасности сам меняет правила детекции, ведёт карантин и ищет по журналу от документа учёта до получателя.
Архитектура
Выгрузка из складского контура проходит через сервис экспорта: файл получает метку, а метка ложится в реестр вместе с учётной записью и составом выгрузки. Агенты рабочих мест перехватывают запись на съёмный носитель и печать, почтовый шлюз и прокси по ICAP проверяют и метку, и содержимое по признакам изделий — артикул, проба, масса, номер партии камней; проверка идёт по обоим путям, потому что метку сбивает пересохранение файла, а правило по содержимому ловит переупакованную выгрузку. Агент принимает решение по локальному кэшу политик и отдаёт событие в Kafka асинхронно, иначе печать и копирование ждали бы ответа сервера. Отправление держится в карантине до решения офицера безопасности, карточки инцидентов лежат в PostgreSQL, поток событий и поиск по журналу — в ClickHouse, теневые копии — в MinIO.
Стек
Go · chi · PostgreSQL · ClickHouse · Kafka · Kubernetes · ICAP · MinIO · Агенты Windows
Складской учёт серийных номеров с гарантийным обменом и возвратами
Задача, решение и что передали
Задача
Серийные номера писали в накладных от руки, и при гарантийном обращении искали по бумажным копиям и переписке менеджеров, кому уехал конкретный аппарат.
Решение
Складской контур собран с нуля: приёмка с построчным сканированием серийных номеров и привязкой к партии поставщика; адресное хранение по местам и коробам; отбор и упаковка с фиксацией номеров в отгрузке; обменный фонд с бронью под гарантийное обращение; маршрут возврата — приём, диагностика, решение, отправка поставщику; журнал перемещений каждого номера от приёмки до клиента. Серийный номер служит ключом учёта: документы ссылаются на него, а не на строку накладной.
Сдано 4
Исходный код складского контура
Схема жизненного цикла серийного номера
Журнал перемещений
Инструкции кладовщика и сервисного инженера
Как делали
Обследование номеров Посмотрели, как серийные номера писались в накладных от руки, и попробовали по бумажным копиям и переписке найти, кому уехал конкретный аппарат. Разобрали с кладовщиками и сервисными инженерами маршрут гарантийного обращения и то, из чего собирается обменный фонд.
Архитектура учёта Ключом учёта сделали серийный номер: документы ссылаются на него, а не на строку накладной. Хранение завели адресное по местам и коробам, обменный фонд отделили от основного остатка с бронью под гарантийное обращение, а возврат описали маршрутом — приём, диагностика, решение, отправка поставщику.
Разработка приёмки Сначала приёмка с построчным сканированием серийных номеров и привязкой к партии поставщика, потом адресное хранение и отбор с упаковкой и фиксацией номеров в отгрузке. Обменный фонд с бронью и маршрут возврата делали следом, журнал перемещений собирали сквозным через все операции, терминалы сбора данных обкатывали на стенде.
Проверка маршрута Гоняли приёмку с задвоенным серийным номером, отгрузку не того экземпляра из коробки и полный маршрут возврата с диагностикой и отправкой поставщику. Провалом считали серийный номер, для которого не читается путь от приёмки до клиента, и отгрузку экземпляра, забронированного в обменный фонд.
Передача кладовщикам Передали исходный код складского контура, схему жизненного цикла серийного номера, журнал перемещений, инструкции кладовщика и сервисного инженера и формы актов возврата. Кладовщики сами ведут адресное хранение и обменный фонд, сервисные инженеры закрывают возвраты по маршруту.
Архитектура
Учёт построен вокруг экземпляра: серийный номер — отдельная сущность, документы ссылаются на неё, а не на строку накладной, поэтому по гарантийному обращению видно, кому уехал конкретный аппарат. Терминалы сбора данных сканируют номера на приёмке, отборе и упаковке и ходят к бэкенду по HTTP с идемпотентным ключом операции, так что повторный скан или переотправка после обрыва связи не задваивают перемещение. Состояние экземпляров, места и короба адресного хранения, обменный фонд и маршрут возврата — приём, диагностика, решение, отправка поставщику — живут в PostgreSQL, бронь под гарантийное обращение берётся транзакцией на строке экземпляра. Журнал перемещений ведётся append-only записями по каждому номеру, события статусов расходятся через RabbitMQ, Redis держит сессии терминалов и очередь печати этикеток.
Биржа складских остатков металлопроката с кабинетами продавцов и покупателей
Задача, решение и что передали
Задача
Остатки складов рассылались клиентам прайс-листами в таблицах, а заказ собирался в переписке с менеджером.
Решение
Построили торговую площадку с нуля вместо коробочного B2B-модуля: каталог по маркам и сортаменту, витрина остатков по складам с резервом, модуль котировок и встречных предложений, корзина с расчётом реза и доставки. Кабинет продавца ведёт публикацию партий, цену уровня и подтверждение отгрузки, кабинет покупателя — заявки, спецификации и историю сделок. Ядро площадки — сервис сделок с состояниями, очередь событий и обмен с учётной системой по позициям и отгрузкам.
Сдано 4
Передали площадку
Схему данных сделок
Регламент модерации продавцов
Инструкции кабинетов
Как делали
Обследование рассылок Разобрали рассылку остатков прайс-листами в таблицах и переписку менеджеров с клиентами, из которой собирался заказ. Посмотрели коробочный B2B-модуль и убедились, что котировки, встречные предложения и резерв по складам он не описывает, и собрали, как считается рез и доставка.
Архитектура сделок Ядром сделали сервис сделок с состояниями, а каталог, витрину остатков с резервом, котировки и корзину повесили на него. Обмен с учётной системой по позициям и отгрузкам вынесли в очередь событий, кабинеты продавца и покупателя развели по правам: публикация партий и цены уровня отдельно от заявок и спецификаций.
Разработка кабинетов Сначала каталог по маркам и сортаменту и витрина остатков по складам с резервом, потом сервис сделок с состояниями и модуль котировок и встречных предложений. Корзину с расчётом реза и доставки, кабинеты продавца и покупателя и обмен с учётной системой делали следом, на стенде гоняли остатки и сделки прошлых периодов.
Проверка резервов Гоняли встречные предложения по одной партии, параллельные заказы на один остаток и подтверждение отгрузки с расхождением по позициям, а также нагрузочные прогоны на витрине остатков. Провалом считали продажу партии, уже зарезервированной другой сделкой, и сделку, состояние которой разошлось с отгрузкой в учётной системе.
Передача модераторам Передали площадку, схему данных сделок, регламент модерации продавцов, инструкции кабинетов и стенд нагрузочных прогонов. Модераторы сами допускают продавцов и партии к публикации, менеджеры ведут котировки и подтверждают отгрузки в кабинете.
Архитектура
Кабинеты продавца и покупателя — клиент на React поверх API, ядро площадки — сервис сделок с явными состояниями, единственный узел, который меняет статус сделки. Котировки, встречные предложения и подтверждения отгрузки публикуются событиями в Kafka, витрина остатков по складам с резервом обновляется потребителем, а сам резерв берётся транзакцией с блокировкой строки остатка в PostgreSQL, чтобы одну партию не забронировали дважды. Каталог по маркам и сортаменту ищется через OpenSearch с фасетами, индекс перестраивается из тех же событий: фасетная выборка по десяткам характеристик в реляционной базе упирается в набор индексов, поэтому поиск вынесен, а источник истины остался в PostgreSQL. Доступ и роли кабинетов ведёт Keycloak, обмен с учётной системой по позициям и отгрузкам идёт очередью, Redis держит корзину и расчёт реза с доставкой.
Кабинет дилера поверх собственного складского и заказного контура
Задача, решение и что передали
Задача
Дилеры заказывали письмами по прайсу в таблице, а остатки и сроки поставки уточняли по телефону.
Решение
Складской и заказной контур написали с нуля вместо коробочной конфигурации: адресное хранение по ячейкам, партии и серийные номера, резерв под заказ, комплектация и отгрузочные задания на терминалах. Кабинет дилера открывает доступные остатки с учётом резерва, цену своего уровня, срок поставки из плана прихода и статус каждой строки заказа. Обмен с бухгалтерией идёт очередью документов, история заказов и рекламаций собрана в одной карточке дилера.
Сдано 4
Передали складской контур
Кабинет дилера
Схему прав по уровням цен
Инструкции кладовщика
Как делали
Обследование склада Прошли склад от приёмки до отгрузки с кладовщиками и комплектовщиками, сняли выгрузку номенклатуры, партий и серийных номеров из коробочной конфигурации, разобрали почту с заказами дилеров и прайс в таблице. Увидели, что резерв нигде не фиксируется, а срок поставки менеджер называет по памяти из плана прихода.
Архитектура учёта Ядром сделали адресное хранение по ячейкам с партиями и серийными номерами, а резерв вынесли отдельной сущностью поверх остатка, чтобы дилер видел доступное, а не общее количество. Уровни цен убрали в права, обмен с бухгалтерией пустили очередью документов, от хранения прайса файлом отказались.
Разработка модулей Собрали справочник и топологию ячеек, приёмку с серийными номерами, затем движок резервов, комплектацию и отгрузочные задания на терминалах, затем кабинет дилера с доступными остатками, ценой своего уровня, сроком из плана прихода и статусом каждой строки заказа. На стенде отработали терминалы сборщиков и очередь документов до подключения боевой бухгалтерии.
Проверка операций Гоняли инвентаризацию по ячейкам, отбор с пересортицей, частичную отгрузку и возврат по рекламации, сверяя остаток, резерв и партию после каждой операции. Работу не приняли бы, если бы дилер увидел в кабинете количество, уже зарезервированное под чужой заказ, или если бы серийный номер отгруженной позиции не находился в истории.
Передача контура Передали складской контур, кабинет дилера, схему прав по уровням цен, журнал резервов и инструкции кладовщика. Менеджер сам заводит дилера, назначает уровень цены и правит план прихода, кладовщик работает на терминале по печатным инструкциям.
Архитектура
Ядро остатков, партий и серийных номеров живёт в PostgreSQL, а резерв пишется отдельной строкой движения со ссылкой на строку заказа, а не декрементом остатка: отмена снимается обратной записью и не требует пересчёта карточки товара. Сервис заказов на Axum отдаёт кабинету доступный остаток как разность остатка и активных резервов, цену уровня дилера и срок из плана прихода. Терминалы кладовщиков держат соединение со шлюзом отгрузочных заданий и локальную очередь операций, чтобы обрыв цеховой сети не останавливал комплектацию. Документы в бухгалтерию уходят через RabbitMQ с подтверждением публикации — приёмник закрыт на время закрытия периода, и прямой синхронный вызов терял бы отгрузки; Redis держит срез цен по уровням и прав дилера.
Портал агентств с бронированием туров и комиссионными отчётами
Задача, решение и что передали
Задача
Агентства бронировали заявками в почте, наличие мест и цену уточняли у менеджера, комиссию сверяли в конце месяца.
Решение
Портал открывает агентству поиск по датам, отелям и перевозке с проверкой квот онлайн, бронирование с таймером на оплату, оформление туристов и выпуск ваучера. Кабинет ведёт договор агентства, уровень комиссии, реестр броней и взаиморасчёты с актом за период, документы по туристам лежат в карточке брони. Квоты, стоп-продажи и цены подтягиваются из ядра оператора очередью обновлений, отмена и изменение брони идут по правилам штрафных сроков.
Сдано 4
Передали портал агентств
Реестр броней
Расчёт комиссии с актом
Правила штрафных сроков
Как делали
Обследование заявок Разобрали заявки агентств в почте, журнал звонков менеджерам об остатке мест и месячную сверку комиссии, сняли из ядра оператора состав квот, стоп-продаж и цен по отелям и перевозке. Записали правила штрафных сроков при отмене и изменении, которые до этого держались на менеджере.
Архитектура бронирования Поиск и бронирование посадили на онлайн-проверку квот, а квоты, стоп-продажи и цены сделали читаемыми из ядра оператора через очередь обновлений. Бронь получила таймер на оплату, комиссия и взаиморасчёты — отдельный расчёт по договору агентства, чтобы акт собирался из реестра броней, а не сверялся вручную.
Разработка портала Собрали поиск по датам, отелям и перевозке с проверкой квот, затем бронирование с таймером на оплату, оформление туристов и выпуск ваучера. Договор агентства, уровень комиссии, реестр броней с актом за период и правила штрафных сроков доделывали на стенде с копией квот и прайсов.
Проверка квот Сажали несколько агентств на одну квоту и бронировали последнее место одновременно, отпускали таймер оплаты, отменяли и меняли бронь на разных штрафных сроках, сверяли акт с реестром броней. Работу не приняли бы, если бы одно место ушло двум агентствам или если бы комиссия в акте не собиралась из строк реестра броней.
Передача портала Передали портал агентств, реестр броней, расчёт комиссии с актом, правила штрафных сроков и инструкции по подключению агентства. Менеджер оператора сам заводит агентство, ставит уровень комиссии и правит правила штрафов, агентство работает по инструкции без звонков.
Архитектура
Портал на Axum ищет по датам, отелям и перевозке, оперативный срез квот, цен и стоп-продаж лежит в Redis и обновляется очередью из ядра оператора. Подтверждение брони этому срезу не доверяет: списание квоты выполняется транзакцией в PostgreSQL под блокировкой строки квоты, иначе два агентства продали бы одно место, пока кэш ещё не обновился. Таймер на оплату сделан отложенным сообщением RabbitMQ, которое снимает неоплаченную бронь и возвращает квоту, а отмена и изменение проходят через таблицу штрафных сроков в том же контуре. Поиск по описаниям отелей и подбор ведёт OpenSearch, ваучеры и акты собираются шаблонами Typst, договор агентства, уровень комиссии и взаиморасчёты хранятся в PostgreSQL; сервисы разворачиваются в Kubernetes.
Собственная CRM дилерского центра взамен зарубежной платформы
Задача, решение и что передали
Задача
Карточки клиентов, сделки и сервисные визиты жили в зарубежной облачной CRM, доступ к которой закрывался, а история выгружалась только плоскими таблицами.
Решение
Собрали собственную CRM на PostgreSQL под Linux: ядро клиентов и автомобилей по VIN, модуль сделок с расчётом комплектации и тест-драйвами, сервисный контур с заказ-нарядами и нормо-часами, шину обмена с учётной системой, телефонией и сайтом. Историю из облака перенесли через промежуточный слой сопоставления полей с разбором дублей и сверкой по контрольным выборкам. Роли, права и маршруты согласования скидок вынесены в конфигуратор, кластер СУБД работает с репликой и точкой восстановления.
Сдано 4
Передали исходный код
Схему базы и миграции
Карту сопоставления полей старой CRM
Регламент резервного копирования
Как делали
Обследование выгрузок Выгрузили из облачной CRM карточки клиентов, сделки и сервисные визиты плоскими таблицами и разобрали, что в них потерялось: связи автомобилей по VIN, история согласования скидок, привязка обращений к телефонии. Прошли с менеджерами салона и мастерами-приёмщиками путь от звонка до заказ-наряда.
Архитектура CRM Ядром сделали клиентов и автомобили по VIN, вокруг — сделки с расчётом комплектации и тест-драйвами и сервис с заказ-нарядами и нормо-часами; обмен с учётной системой, телефонией и сайтом вынесли в шину. Роли, права и маршруты согласования скидок убрали в конфигуратор, кластер PostgreSQL под Linux подняли с репликой и точкой восстановления.
Разработка и перенос Сначала схема базы, миграции и промежуточный слой сопоставления полей старой CRM, затем ядро клиентов и автомобилей, затем сделки и сервисный контур. Шину обмена и конфигуратор ролей и маршрутов собирали на стенде, где перенос выгрузки повторяли целиком с разбором дублей.
Проверка миграции Переносили историю на стенд и сверяли контрольные выборки сделок и заказ-нарядов со старой системой, отдельно гоняли согласование скидки по маршруту и восстановление базы из точки. Работу не приняли бы, если бы автомобиль по VIN терял историю сервисных визитов или если бы контрольная выборка сделок не сходилась со старой выгрузкой.
Передача системы Передали исходный код, схему базы и миграции, карту сопоставления полей старой CRM, регламент резервного копирования и инструкции администратора и менеджера. Администратор дилерского центра сам правит роли и маршруты согласования скидок и восстанавливает базу по регламенту.
Архитектура
Ядро CRM на Spring Boot ведёт клиентов и автомобили в PostgreSQL, VIN взят естественным ключом автомобиля с проверкой контрольного разряда при вводе, сделки, тест-драйвы и заказ-наряды ссылаются на него. Обмен с учётной системой, телефонией и сайтом идёт через Kafka топиками по сущностям: телефония отдаёт события всплесками во время звонков, и прямой вызов CRM из АТС терял бы карточки на пиках. Историю из облака переносили через staging-схему с картой сопоставления полей и таблицей дублей, сверка шла контрольными выборками перед каждым переключением модуля. Кластером PostgreSQL управляет Patroni: отчётные выборки уходят на синхронную реплику, чтобы тяжёлые запросы менеджеров не сталкивались с блокировками оперативного контура, восстановление на точку идёт из архива WAL; роли и вход ведёт Keycloak, маршруты согласования скидок описаны в Camunda, раскатка — Ansible.
Собственный контур закупок, склада и расчётов взамен зарубежной ERP
Задача, решение и что передали
Задача
Закупки, склад и взаиморасчёты держались на зарубежной ERP без обновлений, где любая доработка упиралась в поставщика лицензии.
Решение
Собрали собственный контур на PostgreSQL под Linux: модуль закупок с заявками и подтверждениями поставщиков, адресный склад с приёмкой по серийным номерам и терминалами сбора данных, партионный учёт и взаиморасчёты, отчётный слой на отдельной реплике. Данные перенесли поэтапно — справочники и остатки, затем открытые заказы и незакрытые расчёты, со сверкой сальдо на каждом шаге. Обмен с банком, ЭДО и торговыми площадками вынесен в отдельный сервис с очередью и повторной доставкой.
Сдано 4
Передали исходный код и схему данных
Скрипты миграции с протоколами сверки
Описание сервиса обмена
Регламент резервного копирования
Как делали
Обследование ERP Разобрали зарубежную ERP по модулям — закупки, склад, взаиморасчёты — и выписали доработки, которые упирались в поставщика лицензии; сняли справочники, остатки, открытые заказы и незакрытые расчёты. Прошли склад с кладовщиками и посмотрели, как ведётся приёмка по серийным номерам на терминалах.
Архитектура контура Контур разложили на модуль закупок с заявками и подтверждениями поставщиков, адресный склад с партионным учётом и взаиморасчёты, а отчётный слой вынесли на отдельную реплику PostgreSQL, чтобы отчёты не мешали приёмке и отбору. Обмен с банком, ЭДО и торговыми площадками собрали отдельным сервисом с очередью и повторной доставкой.
Разработка и перенос Сначала схема данных и скрипты миграции, затем справочники и остатки, затем закупки, адресный склад с терминалами сбора данных и взаиморасчёты. Перенос отрабатывали на стенде поэтапно: справочники и остатки, потом открытые заказы и незакрытые расчёты, каждый шаг с протоколом сверки.
Проверка сальдо Сверяли сальдо взаиморасчётов и остатки по партиям и серийным номерам после каждого шага переноса, гоняли приёмку, отбор и отгрузку на терминалах и повторную доставку документов при недоступном ЭДО. Провалом считали расхождение сальдо по контрагенту после переноса и позицию, у которой после миграции терялась партия или серийный номер.
Передача контура Отдали исходный код и схему данных, скрипты миграции с протоколами сверки, описание сервиса обмена, регламент резервного копирования и инструкции кладовщика и администратора. Администратор сам разворачивает контур, повторяет обмен из очереди и правит справочники, не обращаясь к поставщику лицензии.
Архитектура
Ядро закупок, адресного склада и взаиморасчётов написано на Axum поверх PostgreSQL, отчётный слой ходит в физическую реплику — сборка партионных отчётов по всей истории иначе держала бы блокировки в контуре приёмки и отгрузки. Терминалы сбора данных обращаются в шлюз по HTTP и держат задание в локальной очереди до подтверждения приёмки по серийным номерам. Обмен с банком, ЭДО и торговыми площадками вынесен в отдельный сервис: исходящие лежат в RabbitMQ, доставка повторяется, а таблица идемпотентности по внешнему идентификатору документа отсекает дубли, потому что приёмники отвечают повтором при таймауте. Банковские платежи и выписки собираются в ISO 20022, документы поставки уходят УПД через ЭДО, Redis кэширует справочники номенклатуры, узлы разворачиваются в Docker на Linux.
Собственная торговая площадка металлопроката с контролем ИИ-подбора
Задача, решение и что передали
Задача
Сделки собирали по почте и телефону, а помощник подбирал аналоги по общей базе, где рядом лежали закрытые цены под конкретных покупателей и контакты поставщиков.
Решение
Построили площадку с нуля: каталог сортамента и остатков по складам, витрина предложений продавцов, заявки и торги, договорный контур с отгрузочными документами и сервис подбора аналогов на языковой модели. Индекс подбора разложили по контурам видимости — карточка позиции несёт метку владельца и уровень цены, поэтому контекст запроса собирается только из фрагментов, разрешённых роли обратившегося. Ответ модели проходит проверку на вынос закрытых условий и контактов: совпадение останавливает выдачу и заводит событие в журнал разбора.
Сдано 4
Торговая площадка
Индекс с метками видимости
Проверка ответов на вынос закрытых условий
Журнал событий
Как делали
Обследование сделок Разобрали, как сделки собирались по почте и телефону, и что лежало в общей базе помощника: закрытые цены под конкретных покупателей рядом с контактами поставщиков. Собрали сортамент и остатки по складам и записали, кто какие условия имеет право видеть.
Архитектура видимости Площадку построили вокруг каталога сортамента и остатков, витрины предложений продавцов, заявок с торгами и договорного контура с отгрузочными документами. Индекс подбора разложили по контурам видимости: карточка позиции несёт метку владельца и уровень цены, контекст запроса собирается только из фрагментов, разрешённых роли обратившегося, а ответ модели проходит проверку на вынос закрытых условий и контактов.
Разработка площадки Сначала каталог сортамента и остатков по складам и витрина предложений продавцов, затем заявки, торги и договорный контур с отгрузочными документами. Индекс с метками видимости, сервис подбора аналогов на языковой модели и проверку ответа собирали после, на стенде с подставными прайсами и контактами.
Проверка подбора Спрашивали подбор аналогов от лица покупателей с разными уровнями цен и от продавца, смотрели, не всплывут ли чужие условия и контакты, повторяли запросы обходными формулировками. Провалом считали ответ, где видна закрытая цена или контакт вне прав обратившегося, и срабатывание проверки, не заведённое в журнал разбора.
Передача площадки Передали торговую площадку, индекс с метками видимости, проверку ответов на вынос закрытых условий, журнал событий и регламент разбора блокировок. Администратор сам заводит уровни цен и метки видимости, дежурный разбирает блокировки по регламенту.
Архитектура
Ядро каталога сортамента, остатков по складам, заявок и торгов написано на C++ и отдаёт интерфейс по gRPC, сделки и договорный контур с отгрузочными документами лежат в PostgreSQL. Индекс подбора аналогов разложен по контурам видимости: карточка позиции несёт метку владельца и уровень цены, и фильтр применяется на этапе поиска в индексе, а не отсечением уже найденного — отбор постфактум означал бы, что закрытые цены и контакты поставщиков попали в контекст запроса. Лексический поиск ведёт OpenSearch, векторную часть — FAISS по разрешённому подмножеству, эмбеддинги и классификаторы исполняются через ONNX Runtime. Ответ модели проходит проверку на вынос закрытых условий и контактов, совпадение останавливает выдачу и уходит событием в RabbitMQ на разбор; вход и роли ведёт Keycloak, развёртывание — Kubernetes.
Контур контроля утечек при работе сотрудников с внешними ИИ-сервисами
Задача, решение и что передали
Задача
Дизайнеры и копирайтеры носили брифы, сметы и материалы заказчиков в публичные чат-боты через личные браузеры, и что именно уходило наружу, никто не видел.
Решение
Свели каналы в один контур: прокси с разбором TLS для веб-чатов и API внешних моделей, агент на рабочих станциях, разбор почтового и файлового трафика на шлюзе и правила по типам данных — сметы, медиапланы, договоры, макеты до публикации. Срабатывание правила останавливает отправку и показывает сотруднику, какое поле сработало, а спорный случай уходит в очередь дежурного вместе с теневой копией вложения. Наружу оставили единственный маршрут — корпоративный шлюз к моделям, остальные домены и приложения закрыты списками.
Сдано 4
Контур контроля каналов
Правила по типам данных
Очередь дежурного
Теневое хранилище вложений
Как делали
Обследование каналов Сняли, куда уходят материалы заказчиков: публичные чат-боты через личные браузеры, почта, файлообменники. Прошли рабочий день с дизайнерами и копирайтерами и собрали типы данных, которые нельзя выпускать, — сметы, медиапланы, договоры, макеты до публикации.
Архитектура контура Каналы свели в один контур: прокси с разбором TLS для веб-чатов и API внешних моделей, агент на рабочих станциях, разбор почтового и файлового трафика на шлюзе. Наружу оставили единственный маршрут — корпоративный шлюз к моделям, остальные домены и приложения закрыли списками, а срабатывание не прячем: сотруднику показываем сработавшее поле.
Разработка правил Сначала правила по типам данных и разбор на шлюзе, затем прокси с разбором TLS и списки доменов и приложений, затем агент на рабочих станциях. Очередь дежурного с теневой копией вложения и подсказку сотруднику о сработавшем поле обкатывали на стенде на прошлых брифах и сметах.
Проверка каналов Пробовали унести смету, медиаплан и макет до публикации в веб-чат, через API модели, почтой и файлообменником, в том числе переименованными и разрезанными файлами. Работу не приняли бы, если бы материал заказчика ушёл в публичный чат-бот мимо корпоративного шлюза или если бы спорное отправление не попало в очередь дежурного вместе с теневой копией.
Передача контроля Отдали контур контроля каналов, правила по типам данных, очередь дежурного, теневое хранилище вложений и инструкцию разбора срабатываний. Дежурный сам разбирает очередь и правит правила, руководитель группы заводит новый тип данных под новый проект.
Архитектура
Прокси с разбором TLS терминирует соединения к веб-чатам и API внешних моделей и отдаёт тело на проверку по ICAP в сервис правил на Rust, агент на рабочих станциях закрывает локальные каналы, почтовый и файловый трафик разбирается на шлюзе. Все три канала обращаются к одному сервису правил, поэтому правило по типу данных — сметы, медиапланы, договоры, макеты до публикации — описывается один раз и не расходится между каналами. Решение возвращается синхронно в том же ICAP-соединении, так что отправка останавливается до выхода наружу и сотрудник видит сработавшее поле, а спорный случай уходит в очередь дежурного через NATS вместе с теневой копией вложения в MinIO. События пишутся в ClickHouse, правила и списки доменов — в PostgreSQL, сетевые аномалии снимает Suricata, наружу открыт единственный маршрут через корпоративный шлюз к моделям; раскатка идёт Ansible.
Дистрибьютор электроникиОбследование и дорожная карта
Проверяется: профиль качества справочников
Обследование склада перед собственной системой учёта вместо коробочной
Задача, решение и что передали
Задача
Склад работал в коробочной программе, любая доработка упиралась в её модель данных, а остатки сходились только после ручной сверки в таблицах.
Решение
Описали процессы приёмки, размещения, отбора, упаковки и отгрузки, выгрузили номенклатуру и историю движений, посчитали дубли артикулов, разрывы партий и позиции без единицы измерения. Спроектировали состав будущей системы: справочник товаров с правилами нормализации, топология ячеек, задания на отбор для терминалов сборщиков, движок резервов, обмен с бухгалтерией и торговыми площадками. Разложили дорожную карту на модули, у каждого записали требование к качеству данных на входе.
Сдано 4
Карта процессов склада
Модель данных будущей системы
Профиль качества справочников
Оценка миграции остатков
Как делали
Обследование склада Прошли склад по операциям — приёмка, размещение, отбор, упаковка, отгрузка — с кладовщиками и сборщиками и выгрузили номенклатуру и историю движений из коробочной программы. Посчитали дубли артикулов, разрывы партий и позиции без единицы измерения и увидели, где остатки расходятся до ручной сверки.
Архитектура системы Спроектировали состав будущей системы: справочник товаров с правилами нормализации, топология ячеек, задания на отбор для терминалов сборщиков, движок резервов, обмен с бухгалтерией и торговыми площадками. От переноса модели данных коробочной программы отказались — справочник строится заново, поэтому у каждого модуля записали требование к входным данным.
Разработка модели Собирали не боевой контур, а модель и дорожную карту: прогнали выгрузки через правила нормализации на стенде, разложили систему на модули и проверили, что задания на отбор ложатся на реальную топологию ячеек. Оценку миграции остатков считали на выгруженной истории движений.
Проверка справочников Прогоняли профиль качества справочников повторно после нормализации, сверяли остатки по выборке ячеек с фактическим пересчётом на складе и показывали разложенные процессы кладовщикам и бухгалтерии. Провалом считали модуль без записанного требования к входным данным и позицию, которая после нормализации сливалась с чужим артикулом.
Передача карты Передали карту процессов склада, модель данных будущей системы, профиль качества справочников, оценку миграции остатков и дорожную карту модулей. Заказчик сам ведёт чистку справочника по правилам нормализации и решает, с какого модуля начинать.
Архитектура
Выгрузки коробочной программы забираются по расписанию Airflow в staging-слой PostgreSQL, профилировщик на Rust проходит номенклатуру и историю движений потоком и считает дубли артикулов, разрывы партий и позиции без единицы измерения. История движений складывается в ClickHouse: сверка идёт полными проходами по нескольким колонкам на всём объёме, а не выборками по ключу, и колоночное чтение поднимает только нужные поля. Требования к качеству данных на входе каждого модуля описаны наборами Great Expectations, которые останавливают загрузку на пороге, поэтому модуль дорожной карты не стартует на справочнике, не прошедшем нормализацию. Согласованные справочники и модель будущей системы — топология ячеек, задания на отбор, движок резервов, обмены — строятся dbt, профиль качества открывается в Metabase, окружение собрано в Docker.
Стек
Rust · Tokio · PostgreSQL · ClickHouse · Airflow · Docker · dbt · Great Expectations · Metabase
Обследование каналов утечки перед контуром защиты данных
Задача, решение и что передали
Задача
Эскизы изделий, закупочные цены и списки клиентов расходились по личной почте и мессенджерам, а узнавали об этом от партнёров.
Решение
Составили карту чувствительных данных: файлы изделий, закупочные цены, клиентская база, фотобанк новых коллекций — с владельцем, местом хранения и кругом допущенных по каждому массиву. Сняли фактические потоки наружу: почтовый шлюз, файловые шары, съёмные носители, печать, облачные диски — и разметили объём и назначение каждого. Описали контур контроля: правила разбора почтового и файлового трафика, теневое копирование на съёмные носители, журнал действий с документами, порядок разбора инцидента.
Сдано 4
Карта чувствительных данных
Реестр каналов утечки
Матрица правил разбора трафика
Требования к теневому копированию
Как делали
Обследование каналов Составили карту чувствительных данных — файлы изделий, закупочные цены, клиентская база, фотобанк новых коллекций — с владельцем, местом хранения и кругом допущенных по каждому массиву. Сняли фактические потоки наружу: почтовый шлюз, файловые шары, съёмные носители, печать, облачные диски.
Архитектура контроля Описали контур: правила разбора почтового и файлового трафика по типам данных, теневое копирование на съёмные носители, журнал действий с документами, порядок разбора инцидента. Развели, что закрывается правилом на шлюзе, а что требует агента на рабочем месте, и от сплошной блокировки каналов отказались в пользу разбора дежурным.
Разработка матрицы Собирали не боевой контур, а матрицу правил и план внедрения: прогоняли образцы эскизов изделий, закупочных прайсов и клиентских списков через черновые правила разбора и смотрели, что распознаётся, а что уходит незамеченным. Требования к теневому копированию писали от объёма и назначения снятых потоков.
Проверка правил Проверяли матрицу на подставных отправлениях по каждому размеченному каналу, включая переименованные файлы и вложения в архиве, и сверяли круг допущенных по каждому массиву с фактическими правами. Провалом считали канал из реестра, для которого не описано ни правило разбора, ни теневое копирование, и массив данных без владельца.
Передача плана Передали карту чувствительных данных, реестр каналов утечки, матрицу правил разбора трафика, требования к теневому копированию и план внедрения контроля. Владельцы массивов сами ведут круг допущенных, служба безопасности идёт по плану внедрения без нас.
Архитектура
Сборщик на Go снимает журналы почтового шлюза, файловых шар, печати и съёмных носителей, приводит их к одной схеме события и пишет в Kafka. Очередь разделяет приём и разбор: шлюзы отдают события всплесками при массовых рассылках, а разборщик работает медленнее, и прямая запись в индекс теряла бы события на пиках. Поисковый слой по отправлениям и файлам ведёт OpenSearch, карта чувствительных данных с владельцем и кругом допущенных, реестр каналов и матрица правил разбора лежат в PostgreSQL. Точка будущего разбора веб-канала описана как ICAP-подключение к прокси, требования к теневому копированию заданы отдельно, внешние источники отдают события по Syslog RFC 5424; сводки по объёмам потоков выводятся в Grafana, приёмник на chi разворачивается в Docker.
Стек
Go · chi · PostgreSQL · OpenSearch · Kafka · Docker · ICAP · Syslog RFC 5424 · Grafana
Обследование сбытовых данных перед собственной торговой площадкой
Задача, решение и что передали
Задача
Сделки вели по почте и телефону, цены и остатки трёх складов жили в отдельных файлах, а одна позиция называлась у менеджеров по-разному.
Решение
Свели номенклатуру складов и прайсов в единый каталог по марке, профилю, размеру, стандарту и единице измерения, описали правила нормализации и сопоставления написаний. Проверили историю сделок на полноту цен, скидок и условий отгрузки, разметили источники остатков, резервов и незакрытых заявок. Спроектировали узлы площадки: каталог с характеристиками, витрина остатков по складам, движок котировок и заявок, реестр контрагентов с лимитами, обмен с учётной системой.
Сдано 4
Единый каталог номенклатуры
Правила нормализации позиций
Модель остатков и резервов
Схема обменов
Как делали
Обследование номенклатуры Свели номенклатуру трёх складов и прайсов и увидели, что одна позиция называется у менеджеров по-разному; проверили историю сделок на полноту цен, скидок и условий отгрузки. Разметили источники остатков, резервов и незакрытых заявок.
Архитектура площадки Единый каталог построили по марке, профилю, размеру, стандарту и единице измерения и описали правила нормализации и сопоставления написаний. Спроектировали узлы площадки: каталог с характеристиками, витрина остатков по складам, движок котировок и заявок, реестр контрагентов с лимитами, обмен с учётной системой.
Разработка каталога Собирали каталог и модель, а не боевую площадку: прогоняли прайсы и складские выгрузки через правила нормализации на стенде и разбирали позиции, которые не сводились автоматически. Модель остатков и резервов проверяли на историях незакрытых заявок.
Проверка сопоставления Давали нормализованный каталог менеджерам сбыта и просили найти свои позиции, сверяли остатки по выборке складов с фактическими данными и повторяли сопоставление на новых прайсах. Провалом считали две карточки на одну и ту же позицию сортамента и позицию без единицы измерения или стандарта в едином каталоге.
Передача модели Передали единый каталог номенклатуры, правила нормализации позиций, модель остатков и резервов, схему обменов и дорожную карту площадки. Заказчик сам добавляет позиции по правилам нормализации и ведёт каталог до запуска площадки.
Архитектура
Прайсы и остатки трёх складов забираются файлами по расписанию Airflow в staging-слой PostgreSQL, нормализатор на Spring Boot раскладывает наименование на марку, профиль, размер, стандарт и единицу измерения по словарю и правилам написаний. Кандидаты на сопоставление ищутся в OpenSearch нечётким совпадением, но решение фиксируется в таблице соответствий подтверждением человека: автоматическая склейка похожих марок стирает разницу между сортаментом разных стандартов, и восстановить её потом нечем. История сделок проверяется на полноту цен, скидок и условий отгрузки, источники остатков, резервов и незакрытых заявок размечены с владельцем. Узлы будущей площадки — каталог с характеристиками, витрина остатков по складам, движок котировок и заявок, реестр контрагентов с лимитами — описаны с обменом через Kafka, слой моделей ведёт dbt, срезы открываются в Metabase, окружение — Docker.
Обследование клубных данных перед единой витриной посещений и абонементов
Задача, решение и что передали
Задача
Посещения фиксировали турникеты, продажи — кассы, а заморозки и переносы абонементов жили в таблицах администраторов клубов.
Решение
Сняли выгрузки турникетов, кассовой системы и клубных таблиц, сопоставили клиентов по телефону и карте, разметили проходы без клиента и абонементы без единого посещения. Описали жизненный цикл абонемента: продажа, активация, заморозка, перенос, продление, возврат — с событием и источником каждого перехода. Спроектировали витрину: справочник клиентов сети, история посещений по клубам, состояние абонементов, загрузка залов по часам, правила ежедневной загрузки данных из клубов.
Сдано 4
Единый ключ клиента сети
Модель жизненного цикла абонемента
Витрина посещений
Каталог расхождений по клубам
Как делали
Обследование выгрузок Сняли выгрузки турникетов, кассовой системы и клубных таблиц, сопоставили клиентов по телефону и карте и разметили проходы без клиента и абонементы без единого посещения. Прошли с администраторами клубов, как оформляются заморозка, перенос и возврат.
Архитектура витрины Ввели единый ключ клиента сети и описали жизненный цикл абонемента — продажа, активация, заморозка, перенос, продление, возврат — с событием и источником каждого перехода. Витрину разложили на справочник клиентов сети, историю посещений по клубам, состояние абонементов и загрузку залов по часам, а загрузку данных из клубов описали правилами, а не разовой выгрузкой.
Разработка модели Собирали модель и дорожную карту: прогоняли выгрузки через правила сопоставления клиентов на стенде, разбирали проходы, не сводящиеся к клиенту, и проверяли, что переходы абонемента восстанавливаются из событий. Правила ежедневной загрузки писали под форматы каждого клуба.
Проверка сопоставления Повторяли сопоставление на выгрузках других клубов и периодов, сверяли число проходов по турникетам с посещениями в витрине и поднимали историю заморозок по выборке абонементов. Не приняли бы работу, если бы один клиент оставался двумя записями в разных клубах или если бы переход абонемента не подтверждался событием с источником.
Передача витрины Передали единый ключ клиента сети, модель жизненного цикла абонемента, витрину посещений, каталог расхождений по клубам и дорожную карту загрузки. Администраторы клубов ведут заморозки и переносы в общем порядке, аналитик сети повторяет загрузку по регламенту.
Архитектура
Выгрузки турникетов, кассовой системы и клубных таблиц забираются в приёмник на NestJS, нормализуются и сопоставляются по телефону и номеру клубной карты. Спорные склейки не выполняются автоматически, а уходят в очередь ручного разбора через RabbitMQ: один телефон встречается у нескольких членов семьи, и автоматическое объединение перенесло бы посещения на чужого клиента. Жизненный цикл абонемента собирается как последовательность событий с источником каждого перехода — продажа, активация, заморозка, перенос, продление, возврат, — поэтому состояние восстанавливается на любую дату, а не выводится из последнего значения поля. Справочник клиентов сети, история посещений по клубам и загрузка залов по часам лежат в PostgreSQL, Redis держит справочники на время загрузки, расписание ведёт Airflow, модели — dbt, срезы открываются в Metabase; окружение — Docker.
Почему в разборах нет процентов и названий клиентов
Заказчиков не называем: договоры это запрещают. Цифры результата не приводим по другой причине — любая красивая доля рассыпается на первом же созвоне, где спросят, как её мерили. Вместо этого в каждом разборе реальные места, где такие проекты встают, ограничения закрытого контура, порядок работ по этапам и процедура приёмки, по которой вы проверяете работу любого подрядчика, включая нас. Список выполненных работ со стеком и архитектурой — в портфолио выше.
Автоматизация и AI-агентыСредний бизнес, производство
Ассистент по внутренней базе знаний в контуре
Регламенты, инструкции, приказы и договоры лежат в сетевых папках, в почте и в головах двух-трёх человек. Сотрудник задаёт вопрос в общий чат, ответ приходит через час и часто по отменённой редакции.
Документы противоречат друг другу
Ответ без ссылки на документ и страницу бесполезен
Накладные, счета, акты и копии паспортов приходят почтой, в мессенджерах и фотографиями с телефона. Сотрудник открывает файл и перебивает реквизиты в 1С или CRM, ошибаясь в цифрах ровно там, где это дороже всего.
Интеграции и сопровождениеТорговля, средний бизнес
Обмен 1С с маркетплейсами и сайтом
Остатки в 1С, на сайте и в кабинетах площадок расходятся: одна и та же единица товара продаётся дважды, заказ приходит на позицию, которой нет. Цены правят руками в трёх местах, справочники не совпадают ни артикулом, ни единицами измерения.
Чертежи, паспорта оборудования, схемы, регламенты технического обслуживания и переписка с поставщиками лежат в сетевых папках, в архиве СЭД и в шкафах. Механик ищет пункт регламента по конкретному узлу и находит четыре редакции без понимания, какая действует.
Сканы разного качества
Смысл чертежа находится в основной надписи, спецификации и выносках, а не в сплошном тексте
У банка есть массив внутренних документов — регламенты, методики, договоры, письма регулятора, — по которому сотрудники ищут ответы вручную, и понятный соблазн отдать этот поиск внешнему ИИ-сервису. С марта 2026 обработка информации ограниченного доступа во внешних облачных ИИ-сервисах запрещена, поэтому задача превращается в инженерную: тот же сценарий нужно собрать на своём железе, с разграничением прав и журналом, который предъявляется проверяющему.
Планирование железа считается не по размеру модели, а по памяти видеокарты
Разграничение прав обязано применяться на этапе отбора фрагментов, до того как текст попал в контекст модели
Пилот показали, эффект на демонстрации признали, и дальше он живёт в демонстрационном режиме уже больше полугода. Внутри спорят, в чём причина — в данных, в модели или в процессе, но спор не решается, потому что качество ни разу не мерили на общем наборе задач.
Облёты линий идут, снимки копятся терабайтами, а разбирают их инженеры глазами, и скорость разбора отстаёт от скорости съёмки. Задача не в том, чтобы летать, а в том, чтобы из архива отобрать кадры с признаками дефектов и передать их человеку с привязкой к опоре.
Дефекты редкие
Дефект занимает десятки пикселей на кадре в 20–50 мегапикселей
Дата перехода названа нормативно, а внутри нет точного перечня того, что на MS SQL держится. На базе висят хранимые процедуры, ночной планировщик, отчётность и несколько приложений, исходников которых уже нет ни у кого.
T-SQL не переносится построчно
Приложения без исходников общаются с базой собственными запросами
Разберём её на созвоне за 40 минут: назовём объём работ, ограничения контура и по каким числам вы будете принимать результат. Если задача не наша — скажем прямо, это бесплатно и ни к чему не обязывает.