← Назад к блогу

Почему ваша LLM врёт: как мы строим SCADA.AI с мозгами

AIPythonLLMMachine LearningYandexGPTRAGLoRAMLOpsSCADA

Или: что делать, когда нейросеть видит цифры, но не понимает, что они значат

Пролог: мы уперлись в стену

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

Вы берёте результат этого анализа и скармливаете его языковой модели. “Вот данные, объясни что происходит.”

Модель смотрит на цифры. Видит “1469 аномалий в KITCHEN2-CO2, корреляция r=0.62 с R201-CO2, сезонность 288 точек”. И отвечает:

“В системе обнаружены значительные аномалии. Рекомендуется проверить датчики и вентиляцию.”

Стоп. А что такое KITCHEN2? Это кухня ресторана на 200 мест или лаборатория? Что значит “проверить вентиляцию” — какую именно? Какая норма CO2 для этой зоны? Что было два месяца назад, когда меняли датчик?

Модель не знает. И начинает галлюцинировать общими фразами.

Это и есть наша стена. И сегодня я расскажу, как мы её ломаем.


Грабли №1: Окно контекста — это не баг, это фича (но не для нас)

Первая проблема, с которой мы столкнулись — 400 Bad Request от YandexGPT.

Мы пытались скормить модели весь результат анализа:

  • Статистика по всем тегам
  • Аномалии с индексами
  • Сезонность с паттернами
  • Корреляционные матрицы
  • Визуализации

Итог: 120 000 символов. Это ~30 000 токенов.

YandexGPT имеет лимит ~32 000 токенов на весь запрос (system prompt + user prompt + ответ модели). Мы даже в запрос не влезаем, не говоря уже о месте для ответа.

Первая мысль: “Нужна модель с большим окном!”

GPT-4-turbo имеет 128K токенов. Claude 3 — 200K. Проблема решена?

Нет. И вот почему:

Механизм внимания размывается

Представьте, что вы читаете книгу. На первых 10 страницах вы помните всё. На 100-й странице вы уже забыли, что было на 5-й. На 500-й — вы потеряли нить.

То же самое происходит с LLM. Механизм attention (внимание) распределяет “фокус” модели по всем токенам контекста. Чем больше контекст — тем меньше внимания на каждый отдельный токен.

Результат: даже если технически влезло — качество ответа падает. Модель “утонула в шуме”.

Вывод: проблема не в размере окна, а в подходе

Мы пытались решить задачу не тем инструментом. Давать модели “всё подряд” и надеяться, что она сама разберётся — это как дать студенту 1000-страничный учебник и сказать “найди ответ на вопрос”.

Правильный подход: дать только релевантную информацию.


Грабли №2: LLM без контекста — это галлюцинация

Но даже если мы ужмём данные до 3K токенов (убрав всё лишнее), останется главная проблема:

Модель не знает, что такое KITCHEN2.

Для неё это просто строка. Она не знает:

  • Что это кухня ресторана на 80 м²
  • Что там стоит вентиляция V-201, установленная в 2019 году
  • Что по СанПиН норма CO2 для кухни — 1000 ppm
  • Что два месяца назад (2025-05-02) был инцидент — превышение CO2, чистили вентиляцию
  • Что R201 — это смежный офис, и корреляция между ними ожидаема (общая вентсистема)

Без этого контекста модель может только гадать. И она гадает:

“Возможные причины: проблемы с датчиками, общие источники выбросов, сезонные колебания.”

Это правильные, но бесполезные фразы. Они верны для любого объекта в мире. Но они не помогают инженеру принять решение.

Аналогия: врач без истории болезни

Представьте, что вы приходите к врачу. Он видит анализы:

  • Лейкоциты: 15 000 (норма 4-9)
  • Температура: 38.5°C
  • СОЭ: 40 мм/ч

Врач говорит: “У вас воспаление. Рекомендуется проверить возможные источники инфекции.”

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

Без истории болезни, без контекста пациента — врач может только гадать.

То же самое с LLM. Без контекста объекта — модель даёт общие рекомендации, которые не помогают.


Решение: Трёхслойная архитектура

Мы пришли к архитектуре, которая решает обе проблемы:

┌─────────────────────────────────────────────────────┐
│ Слой 3: Fine-tuned модель (LoRA) │
│ Знает SCADA-терминологию и формат ответов │
└─────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────┐
│ Слой 2: RAG (Retrieval-Augmented Generation) │
│ Динамически подгружает контекст объекта │
└─────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────┐
│ Слой 1: База знаний объекта │
│ Структурированное описание + история инцидентов │
└─────────────────────────────────────────────────────┘

Разберём каждый слой.


Слой 1: База знаний объекта (Knowledge Base)

Это фундамент. Место, где хранится вся информация об объекте.

Что там хранится:

1. Общее описание

project:
  name: "Промышленное здание ООО Ромашка"
  type: "food_production"
  address: "г. Москва, ул. Ленина, 15"
  
building:
  floors: 3
  area_m2: 5000
  year_built: 2015
  occupancy: 150

2. Описание зон

zones:
  KITCHEN2:
    type: "kitchen"
    area_m2: 80
    max_occupancy: 20
    equipment: ["V-201", "E-15", "G-1"]
    norms:
      CO2_ppm: 1000
      temp_c: [18, 24]

3. Оборудование

equipment:
  V-201:
    type: "ventilation"
    manufacturer: "Systemair"
    model: "TA-3000"
    installed: "2019-05-15"
    power_kw: 15

4. История инцидентов

incidents:
  - date: "2025-05-02"
    zone: "KITCHEN2"
    type: "threshold_exceeded"
    description: "Превышение CO2 до 2500 ppm, чистка вентиляции"
    resolved_by: "Иванов И.И."

5. Нормативы

norms:
  sanpin_kitchen:
    CO2_ppm: 1000
    source: "СанПиН 2.4.1.3490-17"

Где храним: гибрид MD + PostgreSQL

Мы не выбираем между “файлами” и “базой данных”. Используем оба:

  • Markdown файлы — source of truth, версионируются в git, читаемы человеком
  • PostgreSQL + pgvector — быстрый поиск, семантические эмбеддинги, связи между документами

Когда инженер редактирует MD файл — система автоматически парсит его, извлекает контент, создаёт эмбеддинги и индексирует в БД.

UI: Вики по объекту

В интерфейсе SCADA.AI добавляем раздел “База знаний”:

  • Слева — дерево категорий (Зоны, Оборудование, Нормативы, Инциденты)
  • Справа — Markdown редактор с live preview
  • Инженер может добавлять новые документы, редактировать существующие, фиксировать инциденты

Результат: со временем накапливается полная история объекта. Каждый инцидент, каждый ремонт, каждая замена оборудования — всё задокументировано.


Слой 2: RAG (Retrieval-Augmented Generation)

RAG — это динамическая подгрузка контекста. Вместо того чтобы давать модели “всё подряд”, мы даём ей только релевантное.

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

Шаг 1: Пользователь нажимает “Интерпретировать анализ тегов KITCHEN2-CO2 и R201-CO2”

Шаг 2: Backend извлекает ключевые сущности:

  • Теги: KITCHEN2-CO2, R201-CO2
  • Зоны: KITCHEN2, R201
  • Период: последние 30 дней

Шаг 3: Делаем запрос в Vector DB (Qdrant или pgvector):

SELECT chunk_text, similarity
FROM kb_document_embeddings
WHERE embedding <=> query_embedding < 0.3
  AND tags && ARRAY['KITCHEN2', 'R201']
ORDER BY similarity DESC
LIMIT 5

Шаг 4: Получаем топ-5 релевантных кусков (~2K токенов):

  • Описание зоны KITCHEN2 (кухня, 80 м², вентиляция V-201)
  • Описание зоны R201 (офис, 120 м²)
  • Последние 3 инцидента в KITCHEN2
  • Связь между KITCHEN2 и R201 (общая вентиляция)
  • Нормативы СанПиН для кухни

Шаг 5: Формируем промпт для LLM:

=== КОНТЕКСТ ОБЪЕКТА ===
[2K токенов релевантного контекста из RAG]

=== РЕЗУЛЬТАТЫ АНАЛИЗА ===
[3K токенов сжатых данных анализа]

=== ЗАДАЧА ===
Дай интерпретацию с учётом контекста объекта.

Итого: ~5K токенов вместо 120K. Модель получает только нужное.

Почему RAG останется даже после fine-tuning

Многие думают: “Обучим модель — и RAG станет не нужен”. Это ошибка.

RAG решает проблему актуальности:

  • Fine-tuned модель знает данные на момент обучения (например, январь 2026)
  • RAG даёт доступ к свежим инцидентам (сегодня, вчера)
  • Пример: модель обучена в январе, но в марте был инцидент — RAG его подтянет

RAG решает проблему прозрачности:

  • Fine-tuning = “чёрный ящик” (модель знает, но не скажет откуда)
  • RAG = “вот документ, из которого я это взял”

Правильная архитектура: Fine-tuning + RAG = максимальное качество.


Слой 3: Fine-tuning через LoRA (Yandex DataSphere)

Через 6-12 месяцев, когда накопится 500+ качественных примеров, мы запустим дообучение модели.

Что такое LoRA?

LoRA (Low-Rank Adaptation) — это метод дообучения больших моделей, при котором обучаются не все веса, а только небольшая их часть (low-rank матрицы).

Аналогия: представьте, что вы учите повара готовить новое блюдо. Вы не переучиваете его с нуля (это долго и дорого). Вы даёте ему рецепт (LoRA-адаптер), который он применяет к своим базовым навыкам.

Почему Yandex DataSphere?

Мы используем YandexGPT Pro. Логично дообучать её же через Yandex DataSphere:

  • Не нужно покупать GPU (A100 стоит $10K+)
  • LoRA обучается за 1-24 часа (не недели)
  • Стоимость: ~$50-200 за обучение
  • Модель остаётся в Yandex Cloud — быстрая интеграция

Что обучаем?

Training dataset (формат JSONL):

{"input": "KITCHEN2-CO2: 1469 аномалий, r=0.62 с R201-CO2, сезонность 288 точек", "output": "В кухне ресторана обнаружены синхронные пики CO2 с соседней зоной R201 (корреляция 0.62). Это указывает на общий источник выбросов — вероятно, вентиляционная система V-201. Учитывая инцидент 2025-05-02 (чистка вентиляции), проблема может повторяться. Рекомендуется внеплановая проверка."}

Модель учится:

  • Понимать SCADA-терминологию
  • Использовать конкретные цифры
  • Ссылаться на оборудование и историю
  • Давать рекомендации в правильном формате

Автоматизация: MLOps pipeline

Мы не запускаем обучение вручную. Автоматизируем через Celery + Cron:

Каждую неделю (воскресенье 2:00):

  1. Проверяем сколько новых качественных примеров (с фидбеком [+] от инженеров)
  2. Если >50 примеров → запускаем LoRA обучение
  3. A/B тестируем новую модель vs старую
  4. Если новая лучше на 5%+ → переключаем продакшен на неё
  5. Помечаем примеры как “использованные для обучения”

Результат: модель постоянно улучшается без ручного вмешательства.


Дорожная карта: 4 фазы

Фаза 1: Quick Wins (сейчас, 1-2 недели)

Задача: Создать базовый контекст объекта

  • Создать project_context.yaml с описанием объекта
  • Реализовать ContextInjector — инъекция контекста в промпт
  • Тестирование: сравниваем качество интерпретаций “до” и “после”

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

Фаза 2: База знаний (2-3 недели)

Задача: Создать UI для управления знаниями

  • Схема БД (таблицы kb_documents, kb_incidents, kb_maintenance_log)
  • Миграции через Alembic
  • API для CRUD документов
  • UI страница “База знаний” с Markdown редактором
  • Дерево категорий

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

Фаза 3: RAG интеграция (2-3 недели)

Задача: Автоматическая подгрузка контекста

  • Vector DB (pgvector) для семантического поиска
  • Эмбеддинги через Yandex AI Studio
  • KnowledgeRetriever класс
  • Интеграция в DDAInterpreter
  • Синхронизация MD файлов → БД (file watcher)

Ожидаемый результат: при интерпретации модель автоматически получает релевантный контекст из базы знаний.

Фаза 4: Сбор training data (1 неделя)

Задача: Начать накапливать данные для fine-tuning

  • UI для оценки интерпретаций ([+]/[-], 1-5 звёзд)
  • TrainingDataCollector — сбор качественных примеров
  • БД ml_training_data

Ожидаемый результат: через 6-12 месяцев накопится 500+ примеров для LoRA обучения.

Фаза 5: LoRA + MLOps (через 6-12 месяцев)

Задача: Дообучить модель и автоматизировать процесс

  • LoRATrainer класс — интеграция с Yandex DataSphere
  • A/B тестирование моделей
  • Celery задачи для автоматического переобучения
  • Мониторинг качества моделей

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


Манифест: наши принципы

1. Контекст важнее данных

Модель может видеть идеальные цифры, но без контекста объекта она бесполезна. Контекст — это то, что превращает данные в знания.

2. Эволюция, а не революция

Мы не пытаемся построить “идеальную систему” сразу. Начинаем с простого (YAML + инъекция в промпт), постепенно добавляем сложность (RAG, fine-tuning). Каждый шаг даёт ценность.

3. Накопление ценности

Каждый инцидент, каждый ремонт, каждое описание оборудования — это инвестиция в будущее. Через год у нас будет богатейшая база знаний, которая сделает систему умнее.

4. RAG + Fine-tuning = синергия

Это не “или/или”. RAG даёт актуальность и прозрачность. Fine-tuning даёт специализацию и качество. Вместе они дают максимальный результат.

5. Автоматизация MLOps

Мы не хотим вручную запускать обучение моделей. Автоматизируем всё: сбор данных → обучение → A/B тестирование → деплой. Система должна улучшаться сама.

6. Прозрачность

Инженер должен понимать, откуда модель взяла эту информацию. RAG даёт ссылки на документы. Fine-tuning — нет. Поэтому RAG останется навсегда.


Эпилог: что дальше?

Сейчас мы на Фазе 1. У нас есть базовый project_context.yaml и ContextInjector. Мы уже видим улучшение качества интерпретаций.

Следующие 2-3 недели — Фаза 2: создаём полноценную базу знаний с UI.

Через месяц — Фаза 3: RAG интеграция.

Через 6-12 месяцев — Фаза 5: LoRA обучение.

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

Главный инсайт

Текущая архитектура “весь результат → LLM” — это тупик.

Правильная архитектура:

  • Математический анализ (паттерны)
  • База знаний (контекст)
  • RAG (актуальность)
  • Fine-tuning (специализация)
  • = Качественные интерпретации

Это не “или/или”. Это последовательное наращивание. Каждый слой усиливает предыдущий.


P.S. Для тех, кто дочитал

Если вы работаете с LLM и сталкиваетесь с похожими проблемами:

  • Модель даёт общие фразы
  • Не хватает окна контекста
  • Качество падает на больших данных

Не пытайтесь “впихнуть всё в модель”. Вместо этого:

  1. Создайте базу знаний (даже простую YAML)
  2. Начните с инъекции контекста в промпт
  3. Постепенно добавляйте RAG
  4. Думайте о fine-tuning через 6-12 месяцев

Это эволюционный путь, который даёт результат на каждом шаге.