Почему ваша LLM врёт: как мы строим SCADA.AI с мозгами
Или: что делать, когда нейросеть видит цифры, но не понимает, что они значат
Пролог: мы уперлись в стену
Представьте ситуацию. У вас есть умная система мониторинга промышленного здания. 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):
- Проверяем сколько новых качественных примеров (с фидбеком [+] от инженеров)
- Если >50 примеров → запускаем LoRA обучение
- A/B тестируем новую модель vs старую
- Если новая лучше на 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 и сталкиваетесь с похожими проблемами:
- Модель даёт общие фразы
- Не хватает окна контекста
- Качество падает на больших данных
Не пытайтесь “впихнуть всё в модель”. Вместо этого:
- Создайте базу знаний (даже простую YAML)
- Начните с инъекции контекста в промпт
- Постепенно добавляйте RAG
- Думайте о fine-tuning через 6-12 месяцев
Это эволюционный путь, который даёт результат на каждом шаге.