L1 SOC без усталости. Как мы настроили LLM для разбора инцидентов - Академия Selectel

L1 SOC без усталости. Как мы настроили LLM для разбора инцидентов

Антон Дятлов
Антон Дятлов Инженер по ИБ
22 сентября 2026

Разбираем архитектуру L1-триажа инцидентов ИБ на локальной LLM в закрытом контуре: почему RAG вместо файнтюнинга, как устроен двухпроходный анализ с валидатором и какие ошибки мы поймали при внедрении.

Изображение записи


В SOC значительная часть нагрузки L1-аналитика — разбор однотипных инцидентов, которые в итоге оказываются ложными срабатываниями. При этом каждый алерт нужно проверить, обогатить контекстом и задокументировать. Результат — много ручной работы, которая отнимает время от более сложных задач и повышает риск ошибки из-за усталости. 

Особенно остро эта проблема стоит в командах ИБ, которые одновременно занимаются инфраструктурой, клиентскими задачами и другими проектами. Можно ли автоматизировать первичный разбор, не отдавая данные во внешнюю LLM и не превращая внедрение в многомесячный проект по файнтюнингу?

Мы попробовали это сделать с помощью локальной LLM, RAG и второго прохода с валидатором. В статье покажу, как устроен этот процесс, почему мы выбрали RAG вместо файнтюнинга и какие проблемы обнаружили уже после внедрения.

Как мы жили до внедрения LLM

Представьте картину: L1-аналитик SOC получает инцидент, читает и анализирует события в SIEM (Security Information and Event Management, система управления ИБ), ищет CVE, проверяет во внутренней документации адреса и хосты, пишет разбор по шаблону. Далее ему нужно принять решение: закрыть инцидент как FP (False Positive) или эскалировать на L2.

У нас таких инцидентов приходило около 100 в месяц, примерно 80% из них оказывались ложными срабатываниями. Но даже очевидный FP нужно было проверить и задокументировать. На полный разбор инцидента уходило до двух рабочих дней. Такие затраты времени были особенно заметны на фоне остальной работы команды: помимо разбора инцидентов, мы занимались настройкой и администрированием оборудования, коммуникацией с клиентами и разработкой различных решений.

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

Так и появилась цель — освободить аналитика от рутинного разбора. Не заменить его нейросетью, а делегировать ей первичный анализ, обогащение данных и подготовку решения, чтобы человек получил готовый разбор в Telegram, а не сырой JSON из SIEM.

Из чего состоит наш SOC

Прежде чем переходить к автоматизации, расскажу о нашем SOC. Под ним подразумевается SIEM-система, в которую с части наших служебных и клиентских систем стекаются логи:

  • сетевого оборудования;
  • серверов и контроллеров доменов;
  • антивирусов и межсетевых экранов;
  • средств защиты информации (СЗИ), в том числе при несанкционированном доступе и событиях криптографии.

SIEM коррелирует события по заданным правилам и создает инциденты. 

Схема состава SOC клиентской ИБ Selectel.
Состав SOC клиентской ИБ Selectel.

Как мы выбирали модель

При выборе LLM было одно важное условие: согласно внутренней ИБ-политике, мы не можем передавать информацию во внешние сервисы. Это требование сразу отсекло облачные LLM-сервисы независимо от их условий обработки данных. Трафик наружу в нашем контуре недопустим, так что речь даже не о доверии к вендору.

В Selectel есть несколько моделей, развернутых локально. В шорт-лист попали две: Qwen 3.6-27B и GLM-5.1, которую мы затем заменили на более свежую GLM-5.2.

МодельЧем не устроилаВыводы
ChatGPTНе предполагается локального исполнения, чего требует наш закрытый контекст.Отказались на начальном этапе: не соответствует требованиям безопасности.
Qwen 3.6-27BТеряет фокус на разборе длинных инцидентов, галлюцинирует.Не обеспечила нужной стабильности при расследовании длинно-контекстных инцидентов.
GLM-5.1 / 5.2Тяжеловесная, отвечает медленнее остальных.Высокая точность, приемлемое качество на длинном контексте, открытая архитектура.

Также проверили бенчмарки для понимания способностей моделей.

Таблица бенчмарков Qwen3.6-27B и GLM-5.2.
Таблица бенчмарков Qwen3.6-27B и GLM-5.2.

Почему GLM лучше Qwen

Я прогнал набор инцидентов через обе модели и составил сравнительную таблицу:

МетрикаQwen 3.6 27BGLM-5.2
Accuracy48,5%67,6%
Error Rate32,5%24,4%
Miss Rate12,5%7,1%
False Escalation9,1%4%
Mean Confluence0,42%0,62%
Mean Agreement0,7%0,92%

Разница заметна, но не критична. Ключевая метрика здесь — False Escalation, эскалация ложного инцидента на L2 (Level 2, углубленный анализ). Лишние задачи и ложные отчеты по инцидентам никому не нужны, а GLM куда точнее по этому критерию, да и по всем остальным превзошла конкурента.

Я прогонял набор «золотых инцидентов» с известным решением, по которым и проверял, что модель вообще способна дать осмысленный и похожий на человеческий ответ.

RAG или файнтюнинг

Удобно убрать лишние слои из модели, дообучить ее на H100 и эффективно закрывать инциденты. Но тут есть несколько нюансов.

  1. У нас нет огромного датасета. Для файнтюнинга нужны тысячи размеченных примеров формата «как делать надо». Времени на разметку датасета, как и двух тысяч инцидентов, не было.
  2. Времени на дообучение модели тоже не было. При каждом обновлении документации пришлось бы заново размечать и готовить данные, да еще и выделять GPU для дообучения — все это долго и дорого.
  3. Хотелось сделать быстро и эффективно. RAG позволяет собрать данные из внутренней документации, которая содержит информацию по хостам, доменам и инфраструктуре. Модель получает только нужную информацию и на ее основе принимает решение.

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

Иллюстрация с преимуществами RAG в сравнении с Fine-tuning: скорость, стоимость, регулярность обновления БД.

Почему n8n

Как оркестратор был выбран n8n. Он развернут локально, данные не покидают сеть. У него удобный визуальный воркфлоу, и новичку легко втянуться и понять суть. Продуманные интеграции облегчают жизнь — все запросы выполняются через стандартные ноды. Настроить автоматизацию легко и удобно.

Обработка инцидента по шагам

Теперь взглянем на общую схему нашего решения. Если вкратце, то в SIEM стекаются логи со всех систем. Далее по заранее заданным корреляционным правилам события связываются в единые инциденты, которые и отправляются в n8n.

Общая схема работы решения.

Шаг 1. Сырой инцидент

Инцидент из SIEM приходит как JSON и его структура сильно варьируется:

  • событий может быть 3 или 200 — заранее неизвестно;
  • поля приходят как null, как пустая строка или как undefined;
  • src.ip — иногда строка, а иногда и массив;
  • есть лишние поля, которые модели не нужны.

Корреляция содержит symptoms, tags, correlation.description, raw_event с полным JSON события. Если отдать все это модели как есть, она утонет в контексте и потеряется в 200 строках лишней информации.

Фрагмент кода.

Шаг 2. Нормализация

Неструктурированный JSON необходимо привести к нормализованному виду. С этой задачей справляется специальный скрипт.

Фрагмент кода скрипта, который приводит JSON к структурированному виду.

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

Шаг 3. Обогащение через OSV API

Если indicators.cve не пустой — выполняем запрос к OSV (Open Source Vulnerability, открытая база уязвимостей, созданная и поддерживаемая Google). В результате получаем следующую информацию:

  • оценку (score), критичность (severity) и вектор атаки (vector) из CVSS (Common Vulnerability Scoring System, балльная оценка опасности уязвимости);
  • идентификатор уязвимости по реестру CVE;
  • информацию о публичной доступности эксплойта;
  • ссылки на подробности и бюллетени безопасности.
Фрагмент кода.

Поскольку данные подтягиваются из внешних систем еще до вызова модели, та оперирует готовыми фактами и не придумывает метрики.

Шаг 4. Сбор CQL для Confluence

Из нормализованных полей строится запрос на языке CQL (Confluence Query Language). Информацию по hostname, ip, CVE, user ищем в спейсах отделов в Confluence.

Фрагмент запроса на языке CQL.

Шаг 5. RAG обработка + промпт

Чтобы не забивать контекст модели, чистим HTML и вырезаем релевантные куски, которые нужны для анализа инцидента. Так мы получаем обработанный RAG.

Перед вызовом модели задаем промпты:

  • User prompt — собранная информация и RAG по инциденту;
  • System prompt — жесткие правила, чтобы модель не выходила за рамки L1.
Фрагмент кода с указанием системного и пользовательского промптов.

Шаг 6. Анализ инцидента 

Системный и пользовательский промпты склеиваются в один запрос.

Фрагмент кода со «склеиванием» пользовательского и системного промптов.

На выходе отдается JSON с нужными полями: classification, severity, confidence, l1_decision

Шаг 7. Валидация ответа

Результат анализа передаем валидатору. Он получает только нужную информацию — классификацию, severity, confidence, решение L1, — и далее проверяет их на противоречия.

Фрагмент кода с проверкой информации на противоречия.

Валидатор — вторая пара глаз, которая соглашается или не соглашается с выводами первой модели.


      if (v.validation_result === "confirmed") {
    // Валидатор согласен с решением модели, ничего не меняем
    finalResult = {
        final_severity: l1.severity, // значение от L1
        final_true_positive: l1.true_positive, // значение от L1
        final_decision: l1.l1_decision, // решение от L1
        final_escalate_to_l2: l1.escalate_to_l2 || false,
        validation_status: "confirmed",
        agreement_score: v.agreement_score,
        human_review_required: false
    };
} else if (v.validation_result === "corrected") {
    // Валидатор не согласен с решением модели — учитываем его правки
    finalResult = {
        classification: (v.corrections && v.corrections.classification) || l1.classification,
        final_severity: v.final_severity, // значение от валидатора
        final_true_positive: v.final_true_positive, // значение от валидатора
        final_decision: v.final_decision, // решение валидатора
        final_escalate_to_l2: v.final_escalate_to_l2,
        validation_status: "corrected",
        agreement_score: v.agreement_score,
        human_review_required: false
    };
} else {
    // disputed / insufficient — ручная проверка человеком
    finalResult = {
        final_decision: "insufficient_data",
        validation_status: v.validation_result,
        agreement_score: v.agreement_score,
        human_review_required: true,
        disputed_fields: v.disputed_fields,
        validator_notes: v.validator_notes
    };
}

Валидатор возвращает два ключевых параметра:

  • validation_result — статус проверки: подтверждение исходного решения (confirmed), исправление его (corrected), признание спорным (disputed) или нехватка данных (insufficient).
  • agreement_score — число от 0 до 1, которое показывает степень согласия валидатора с результатами первичного анализа.

Если agreement_score ≥ 0,8 — результат подтвержден, идем по решению L1 от модели (но проверяем это решение по-человечески, просто без исследования инцидента), в противном случае — валидатор поправил. Если validation_result принимает значение disputed или insufficient — инцидент уходит на ручную проверку человеком.

Шаг 8. Финальное решение

По результатам анализа и валидации формируется итог с полем action_type:

  • close_fp — инцидент закрывается как ложный;
  • escalate_l2 — эскалация до L2, с заведением задачи и оповещением в Telegram;
  • monitor — помечаем инцидент как мониторящийся;
  • human_review — отправляем на разбор человеком (могло не хватить информации для принятия решения).

Аналитик открывает Telegram и видит заключение по инциденту. 

Результаты работы флоу: задача в Jira и Оповещение в Telegram.
Результаты работы флоу.

Как это работает на практике

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

Здесь инцидент обогащается информацией из OSV API и Confluence, а затем на этапе принятия решения демонстрируются две ветки развития: когда нейросеть уверена, а также когда она сомневается и создает задачу на разбор инцидента живым аналитиком.

Типовые ошибки при внедрении

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

  1. Markdown-разметка в ответах. Нейросеть часто возвращает результат не в виде чистого текста, а оборачивает его в Markdown-блоки с тегами JSON и обратными кавычками. Если передать такую строку напрямую в JSON.parse, скрипт выдаст ошибку. Проблема легко решается с помощью регулярных выражений. Перед парсингом необходимо вырезать из ответа лишние символы, оставляя только чистый код.
  2. 502 от API при последовательных вызовах. GLM — тяжелая модель, а два последовательных запроса без паузы могут вызвать ошибку 502. Решить проблему можно либо вызовом двух разных моделей, либо задержкой перед вторым запросом.
  3. Захламление контекста. Длинные инциденты забивают память модели, и она теряет фокус. Проблема решается фильтрацией лишней информации: нормализацией, точечными обогащениями, очисткой обертки и «шелухи», которая модели не нужна.

Итоги и планы

LLM в контуре SOC — это освобождение сотрудника от части рутины. Связка промт‑инжиниринга, валидации и RAG — это проще, быстрее и эффективнее, чем файнтюнинг. При этом защита от галлюцинаций, достигаемая за счет жесткого промпта и логических проверок, делает систему управляемой.

Около 80% инцидентов закрываются практически без участия аналитика, но обязательно под его присмотром. Помимо прочего, время на разбор тех инцидентов, которые требуют внимания, заметно сократилось, так как аналитик получает готовый анализ, а не сырой JSON.

Сейчас есть идеи, как улучшить решение:

  • перейти с полнотекстового CQL на семантический поиск по Confluence через внутреннюю ИИ, чтобы контекст стал релевантнее по смыслу, а не по строковому совпадению;
  • протестировать валидатор на независимой второй модели по типу Qwen;
  • проработать документирование разбора инцидента аналитиком, чтобы переиспользовать информацию в промптах и обогащать RAG-документацию;
  • изменить канал оповещения, заменив Telegram внутренним мессенджером.