Skip to content

Repository files navigation

CUTPOINT

Советчик оператору цепочки АВТ → гидроочистка → смешение дизельного топлива.
Мультиагентная система проверяет данные, прогнозирует серу, сравнивает планы
и выдаёт рекомендацию, сохранение режима или объяснённый отказ.

Открыть живое демо

Решение задеплоено и доступно онлайн:
neftecode.goatwhistle.ru

Главное За три минуты Куда смотреть Что измерили Чего не доказали


Что сейчас задеплоено

В Docker развёрнуто решение с агентным слоем, который работает через LLM. LLM помогает проводить разбор условий, выбирать действия и объяснять результат, а детерминированное ядро, Gate и жёсткие ограничения проверяют каждого кандидата и не позволяют модели ослабить требования.

Если нужен полностью детерминированный прогон без внешней модели и ключа, в .env укажите:

AGENTIC_DECISION_ENABLED=0
LLM_PROVIDER=scripted

В этом режиме решение принимает детерминированное ядро по правилам и расчётным моделям; LLM не вызывается. Шаблон настроек находится в .env.example. Решение задеплоено: https://neftecode.goatwhistle.ru/

Главное за 30 секунд

Система работает на реальных данных завода: 189 217 десятиминутных строк телеметрии за 2023–2026 годы, лабораторные анализы ЛИМС и поточный анализатор ПАК. Название — из термина перегонки: cut point, температура отсечки, граница между фракциями. Система проводит такую же границу между допустимым и недопустимым.

Три решения, которых обычно не делают

① Каждое число знает
своё происхождение

У любой величины есть метка: given · derived · scenario · open.

Расчёт, которому нужен open, возвращает «неизвестно» — не подставляет правдоподобную константу. Сценарное значение нельзя прочитать как измерение.

② Отказ — это ответ,
а не сбой

Если данные непригодны или допустимого плана нет, система говорит об этом и называет, какое измерение изменило бы вердикт.

На срезе 16.04.2026 с устаревшей лабораторией ответ — refuse. И это правильный ответ.

③ LLM не может ослабить
ограничение

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

При недоступности модели решение принимается без неё — с явной пометкой в результате.

Как это работает

  ДАННЫЕ                  ПРОВЕРКА              ПРОГНОЗ
  ├─ телеметрия АВТ       ├─ возраст            ├─ сера после ГО
  ├─ телеметрия 24-2000   ├─ пропуски      ──▶  ├─ интервал
  ├─ ЛИМС (+4 ч)     ──▶  ├─ зависание          └─ отклик на T6
  └─ ПАК                  └─ конфликт                  │
                                                       ▼
  ОТВЕТ ОПЕРАТОРУ         ВЫБОР ПЛАНА           КАНДИДАТЫ
  ├─ hold                 выпуск                ├─ постоянные планы
  ├─ recommend_scenario   → стоимость      ◀──  ├─ переходные планы
  └─ refuse          ◀──  → тяжесть             └─ Gate: сера ≤10 мг/кг,
     + причина            → число изменений        доли 100%, уставки,
     + что изменило бы                             запасы, резервуары
     решение                    ▲
                                │
                         АГЕНТЫ (поверх ядра)
                         Orchestrator · Quality · Reliability
                         только ужесточают ограничения и veto

Подробный разбор каждого этапа — docs/pipeline.md.

Схема расчёта: восемь этапов от условий прогона до итога

Схема расчёта: контур данных, контур решения и слой агентов.
Каждый узел раскрывается и показывает, что именно этап посчитал;
пунктиром — ветви отказа, которые на этом прогоне не сработали.

За три минуты

Нужны Python 3.12 и uv. Все команды — из корня проекта. Внешние API и ключи не нужны: расчёт целиком локальный.

uv sync --frozen
AGENTIC_DECISION_ENABLED=0 uv run neftecode serve

Открыть http://127.0.0.1:8765/ — и пройти пять шагов:

1 · Выбрать сцену и нажать «Запустить расчёт» Норма, риск качества или плохие данные. Этапы приходят по мере счёта через SSE, работа агентов видна событиями.

2 · Раскрыть узел 00 схемы Условия расчёта, у каждого числа — бейдж происхождения.

3 · «Закрепить для сравнения» → «Что изменится, если…» Отключить источник или резервуар и получить парное сравнение — с оговорками о несопоставимости срезов, моделей и горизонтов.

4 · «Карта компромиссов» Недоминируемое множество по выпуску, стоимости и тяжести режима.

5 · «Скачать протокол» или «Открыть запись» JSON с контрольной суммой; запись проигрывается заново, без обращения к расчёту и LLM.

Другие способы запуска — тесты, агенты без LLM, Docker

Проверка тестами — 1551 тест, внешняя сеть не нужна:

LLM_PROVIDER=scripted uv run pytest -q

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

uv run neftecode agent-demo

Через Docker:

docker compose up --build
docker compose -f docker-compose.offline.yml up --build   # закрытый контур

Четыре отдельных процесса — data 8766, model 8767, decision 8768, gateway 8765:

uv run neftecode-stack --root . --artifacts artifacts

Куда смотреть

Если вы открыли репозиторий впервые и хотите понять решение, а не читать всё подряд:

Вопрос Файл Что там
Как устроено решение docs/architecture.md Слои, контракты, цикл агентов
Где какой код docs/code-tour.md Карта модулей: с чего начать чтение
Что происходит при расчёте docs/pipeline.md Конвейер от входа до ответа, по фактическому коду
Выполнено ли ТЗ docs/requirements-map.md Пункт ТЗ → модуль → тест → статус и границы
Насколько точен прогноз docs/forecast-evidence.md Протокол, rolling-проверка, финальная оценка 2026
Откуда взялся отклик на T6 docs/response-model.md Оценка β, интервал, область применимости
Какие периоды выброшены и почему docs/excluded-periods.md Реестр с воспроизводимой командой
Все зафиксированные эксперименты research/README.md Методики, сырые результаты, границы
Контракты HTTP-сервисов docs/services.md Четыре процесса, порты, протокол

Если хотите прочитать код — в таком порядке:

  1. src/neftecode/application/use_cases/decision/ — цикл принятия решения целиком. Здесь MakeDecision, Gate и обзоры качества и надёжности.
  2. src/neftecode/domain/ — сущности предметной области: планы, партии резервуаров, ограничения. Зависимостей наружу нет.
  3. src/neftecode/infrastructure/data/ — загрузка источников, синхронизация по времени, правила доверия.
  4. src/neftecode/services/ — четыре HTTP-процесса и поток /api/stream.

203 модуля разложены на domain, application, infrastructure, evaluation, presentation, services, composition. Направления зависимостей — не договорённость, а тест: AST-проверки в tests/architecture/ падают, если слой нарушит границу.

Интерфейс — src/frontend/ (разработка). Собранная версия входит в Git, поэтому Node.js для запуска не нужен.

Что мы измерили

Прогноз серы после гидроочистки

MAE 1.802 → 1.616 мг/кг

Последний пригодный ПАК с медианной поправкой по 20 доступным парам ЛИМС−ПАК. Метод выбран на пяти rolling-периодах до 2026 года, затем один раз проверен на 2026 — на одинаковых 233 пробах.

Протокол и доказательства →

Отклик серы на температуру реактора

β = −0.4227 мг/кг/°C

Доверительный интервал [−0.4768; −0.389] с объявленной областью применимости. Оценивается по ht.T6 на данных строго до момента решения — без утечки из будущего.

Модель отклика →

Пороги доверия к источникам выведены из истории, а не заданы руками. ТЗ требует проверку данных, но конкретных чисел не даёт — мы посчитали их по обучающему периоду:

Правило Порог Откуда
ЛИМС устарел старше 48 ч два суточных интервала отбора проб
ПАК устарел старше 30 мин три периода опроса
Датчик завис 4 одинаковых показания подряд так редко бывает у живого прибора
Конфликт ПАК–ЛИМС расхождение выше 4.75 мг/кг 95-й перцентиль расхождения
Телеметрия неполна от 10 пропавших датчиков из 96 провал между режимом и сбоем опроса

Шесть сцен демонстрации (на комплекте с сохранёнными срезами):

Сцена Момент Ответ ядра
Нормальный режим 05.01.2026 08:00 hold
Ухудшение сырья синтетическая hold
Зависший анализатор 20.07.2026 19:10 hold с резервным источником
Устаревшая лаборатория и сломанный ПАК 16.04.2026 10:10 refuse
Резервуар выведен из работы реальный срез + изменение доступности recommend_scenario
Риск ухудшения качества 24.07.2026 03:00 recommend_scenario

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

Карта обмена агентов: оркестратор, агент качества, агент надёжности

Карта обмена строится во время прогона: слева оркестратор, справа специалисты,
рядом с каждым — инструменты, которые агент выбрал сам. Стрелка к специалисту — запрос,
обратно — вердикт. Здесь оба специалиста дали ACCEPT, и план hold прошёл.

Три вердикта, а не один

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

Итог прогона: сохранить режим

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

Итог прогона: решение не выдано

Решение не выдано. Отказ назван числом, а не общей фразой: сера 25,243 мг/кг
против предела 10 на всём интервале 0 — 2 ч. Сказано и то, что режим остаётся прежним,
и что риск, из-за которого план не найден, никуда не делся.

Третий исход — изменить режим: система предлагает конкретный план с уставками,
когда он и допустим, и заметно лучше текущего. На сценарных данных он выпадает реже двух
других: если текущий режим проходит проверки, система намеренно его не трогает.

Как считается решение

Планировщик строит постоянные и переходные планы, считает качество, массовый баланс, выпуск, условную стоимость и тяжесть режима. Gate проверяет серу ≤10 мг/кг, объявленные свойства продукта, доли компонентов (сумма = 100%), границы уставок, доступность компонентов и ограничения запасов. Неизвестное обязательное условие не считается выполненным.

Парк резервуаров моделируется партиями по стадиям filling → awaiting_passport → ready → draining: проверяются вместимость, 12 часов паспортизации, предел слива за 24 часа, массовый баланс и прогноз паспорта наливаемой партии.

Порядок выбора плана: выпуск → стоимость → тяжесть режима → число изменений → идентификатор. Политика min_useful_gain удерживает текущий режим при малой экономии. Поиск дискретный с бюджетом 1200; глобальная оптимальность не заявляется.

Агенты. OrchestratorAgent, QualityAgent и ReliabilityAgent работают поверх ядра. Специалисты получают ограниченный набор инструментов и возвращают мнения со ссылками на результаты. Код применяет только разрешённые ужесточения и veto — ослабить ограничение агент не может. Финальный план перепроверяется. Если ошибка случилась после принятого ограничения, fallback сохраняет ограничение и выбирает оставшийся план либо отказывает.

По умолчанию агентный слой включён с провайдером Z.AI; AGENTIC_DECISION_ENABLED=0 отключает его, LLM_PROVIDER=local переключает на локальный OpenAI-совместимый endpoint. Ключи — в окружении или локальном .env, в Git не входят. Живые вызовы занимают минуты; temperature=0 не гарантирует повторяемость внешней модели.

Четыре процесса запускает uv run neftecode-stack: data 8766, model 8767, decision 8768, gateway 8765. Gateway реализует /api/stream и передаёт этапы и события агентов по мере расчёта. HTTP-протокола отмены у decision-service нет: кнопка прекращает показ, сервер может продолжать счёт.

Команды

Команда uv run neftecode … Что делает Нужен task/
serve Интерактивная демонстрация: страница, изменение условий, поток расчёта нет
scenes Пять синтетических сцен, шесть при наличии срезов нет
screen --scenario config/scenarios/sour_crude.json Экран оператора по одному сценарию нет
benchmark Шесть сценариев: hold, пороговое правило, советчик, две абляции нет
agent-demo Четыре сценария scripted-политики: трасса выбора инструментов нет
advise --at 2026-01-05T08:00:00 Решение на исторический момент да + model.pkl
train Обучение, временная проверка прогноза, отклика и порогов доверия да
snapshot --all Заморозить срезы из config/snapshot_moments.json да + model.pkl
vak Проверка 17 формул виртуальных анализаторов да
episodes Статистика превышений ПАК и предупреждений да
tank-check Сверка оценок серы потока и окна с ЛИМС да
expert-grid Перебор изменяемых условий демонстрации нет

--out меняет каталог результатов и загружаемых оттуда моделей и срезов.

Данные

Выданный организаторами каталог task/ и каталог artifacts/ в Git не входят. Чистый клон отличается от полного архива сдачи — и система сообщает об этом сама, а не показывает синтетику под видом реального среза.

Комплект Что доступно
Чистый клон Синтетические сцены, benchmark, agent-demo, интерфейс
+ artifacts/ (срезы, manifest, response_model, source_rules) Демонстрация на сохранённых исторических состояниях
+ task/ и совместимый model.pkl Исторический advise, построение срезов, исследования
+ обучение Переобучение, отчёты, модель и пороги доверия

Ожидаемая структура исходного пакета:

task/
  data/avt_tags.csv                     телеметрия АВТ (71 сигнал)
  data/242000_tags.csv                  телеметрия 24-2000 (26 сигналов)
  ЛИМСы ... .xlsx                       лабораторные анализы
  Выгрузка ПАК ... .xlsx                поточный анализатор
  Теги_хакатон.xlsx                     справочник тегов
  ТЗ_нефтекод.docx
  АВТ_схемы.pdf
  2026-09-16/теги АВТ_24-2000.xlsx      уточнённый справочник
  2026-09-16/формулы_ВАК.xlsx

Телеметрия — 189 217 строк с шагом 10 минут, 01.01.2023–07.08.2026. ЛИМС и ПАК имеют собственные временные сетки; синхронизация только по времени, никогда по номеру строки. Для ЛИМС принята консервативная задержка публикации 4 часа, чтобы будущее знание не попало в решение. Служебные индексы не используются как признаки.

ЛИМС — контрольный факт; неисправный ПАК не считается достоверным. Происхождение правил доверия показывается прямо в результате: derived:artifacts/source_rules.json или fallback:config/experiment.json.

Исключённые периоды (остановки, сбои опроса, зависание ПАК, конфликт ПАК–ЛИМС) задокументированы и воспроизводятся одной командой:

uv run python scripts/excluded_periods.py --root <каталог с task/>

Для класса «плановые остановки установки» в данных нет отдельного флага — это указано в реестре как ограничение, а не домысленный список дат.


Живое демо

Документация Архитектура Исследования Трассировка ТЗ Лицензия MIT

About

Every number carries its origin — given, derived, measured, scenario or open — and a calculation that needs an unknown returns "unknown" instead of a plausible constant. A refinery advisor for the crude unit → hydrotreater → diesel blending chain

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Used by

Contributors

Languages