Автор: Александр Летуновский, Место работы — руководитель центра, Сбербанк страхование. Выпускник курса «Natural Language Processing»
Проблематика
Крупные организации сталкиваются с большим числом ИТ-инцидентов: иногда – с тысячами в месяц. Инциденты нередко повторяются, однако найти похожий случай в базе знаний или в системе регистрации непросто: стандартный поиск по ключевым словам часто неэффективен, а помнить детали всех инцидентов невозможно.
Здесь и возникает проблема: при поступлении нового инцидента инженеру трудно выяснить, был ли подобный инцидент раньше, и как его разрешили. Многие знания рассеиваются по прошлым тикетам и не используются вновь. В лучшем случае на помощь приходит опытный коллега, который вдруг вспоминает: «Да, в прошлом месяце у нас уже было нечто подобное по такой-то причине».
Эта ситуация натолкнула меня на мысль о создании системы, которая выступила бы искусственной «памятью» ИТ-команды: мгновенно просматривала бы все предыдущие инциденты и находила релевантную информацию в нужное время. Я хотел, чтобы при разборе нового инцидента система играла роль опытного инженера, видевшего все предыдущие проблемы, и подсказывала возможное решение.
Цель работы
Цель проекта – автоматизировать сопоставление новых инцидентов с решёнными ранее и предоставлять инженерам рекомендации по решению в виде комментария в тикете.
Я задался целью разработать помощника, который при каждом новом инциденте будет искать в базе знаний несколько самых похожих случаев и на основе описаний решений подсказывать причины проблемы и давать советы по способам её устранения. Такой подход, совмещающий поиск по базе знаний с генерацией текста, известен как «Retrieval-Augmented Generation» (RAG) – генерирация ответа с дополнением из внешней базы знаний.
Схема RAG
Мне предстояло реализовать минимально жизнеспособный прототип (MVP) и интегрировать его в промышленную среду системы Service Desk. Основные критерии успеха – ускорение анализа инцидентов (уменьшение времени на поиск решения) и увеличение частоты использования накопленных знаний об инцидентах.
Ограничения и особенности внедрения
Проект развёрнут и эксплуатируется в условиях существенных ограничений инфраструктуры.
I. Среда полностью офлайн. У меня не было возможности воспользоваться облачными сервисами вроде OpenAI API или управляемыми векторными базами. Ни база инцидентов, ни сервис генерации не имеют доступа к интернету или облачным API: все компоненты от моделей до баз данных должны быть размещены локально. Из-за офлайн-режима сложными вопросами становятся обновления и поддержки. Все обновления моделей (как эмбеддинг модели, так и LLM) требуют отдельной процедуры загрузки моделей в контур. Тем не менее, подобный автономный режим – частая ситуация в корпоративных внедрениях. И, как выяснилось позже, даже без доступа к облачным мощностям можно эффективно применять методы NLP на локальных ресурсах.
II. Отсутствие производительных графических процессоров (GPU) на этапе MVP. Сервер, на котором разворачивается решение, оснащён только CPU. Это повлияло на выбор моделей и инструментов: предпочтение отдавалось легковесным и оптимизированным вариантам.
Ход работы
Для решения поставленной задачи я спроектировал архитектуру RAG-агента. Архитектура включала в себя:
- интеграцию с системой Service Desk
- векторную базу данных для знаний об инцидентах
- использование большой языковой модели (LLM) для генерации подсказок.
Агент был реализован на Python. Ниже привожу схему логики работы разработанного агента.
Схема решения
Как показано на схеме, процесс состоит из семи шагов:
- Получение инцидента: агент отслеживает появление нового инцидента в системе Service Desk и через API выгружает его описание в текстовом формате: HTML-разметка очищается до простого текста.
- Дополнение описания: чтобы уточнить проблему, исходное описание инцидента при необходимости автоматически дополняется или переформулируется с помощью большой языковой модели. Например, извлечь ключевые симптомы, дополнить короткое описание, удалить лишние детали из описания.
- Векторизация описания: текст инцидента (как в оригинале, так и переформулированный вариант) преобразуется в векторное представление – embedding – при помощи модели эмбеддинга.
- Поиск похожих инцидентов: полученный вектор сравнивается с векторами ранее решённых инцидентов, хранящимися в векторной базе данных, с использованием метрики косинусного расстояния. В результате извлекаются несколько наиболее похожих описаний из базы (топ-5 по оригинальному тексту и топ-5 по переформулированному).
- Генерация рекомендаций: собранная информация подаётся на вход большой языковой модели – формируется запрос (prompt), содержащий описание новой проблемы и сведения о найденных похожих инцидентах (описания и решения). LLM анализирует этот контекст и генерирует рекомендацию – комментарий с предположением причины проблемы и советом по её решению.
- Обновление базы знаний: векторное представление нового инцидента сохраняется в векторной базе (вместе с текстом описания и решения после закрытия инцидента) для использования в будущем.
- Публикация решения: сгенерированная LMM рекомендация и список ссылок на похожие инциденты автоматически отправляются в систему Service Desk в виде комментария к соответствующему тикету, где его видят инженеры.
Далее рассмотрены ключевые компоненты этой системы и принятые решения при её реализации.
Извлечение текста инцидентов из HTML
Первым шагом стало подключение к системе управления инцидентами (Service Desk) и извлечение необходимых данных. Интеграция реализована через REST API: агент периодически опрашивает систему на наличие новых тикетов или получает уведомление о них. Для каждого нового инцидента с помощью API вытягивается текст поля «Описание» (Description). Поскольку в Service Desk описания хранятся с разметкой HTML, этот контент приводится к простому тексту: удаляются HTML-теги, лишние символы, приводятся в порядок списки и форматирование. Разбор HTML выполняется с помощью библиотеки парсинга BeautifulSoup4. В результате на выходе получается связный текст, пригодный для последующей обработки.
Пример кода для очистки HTML от лишней информации:
def clean_html(html_content):
«»»Remove HTML tags from the content.»»»
if html_content is None:
return None
soup = BeautifulSoup(html_content, ‘html.parser’)
return soup.get_text(strip=True)
При извлечении текста описания агент также может получить связанные с инцидентом данные, например дату и время создания инцидента, категорию проблемы, затронутый сервис или компоненты. Эти сведения в текущей версии напрямую не используются для поиска, но в будущем могут помочь фильтровать результаты (например, ограничивать поиск схожих инцидентов тем же сервисом или близким временем). Пока же основным источником информации для поиска выступает текстовое описание симптомов инцидента.
Векторизация описаний инцидентов
Когда от нового инцидента получен текст описания, следующей задачей становится получение его векторного представления (embedding). Векторное представление – это набор чисел (координат векторов в многомерном пространстве), которые отражают смысл текста. Современные трансформерные модели способны превращать фразу или документ в такой числовой «отпечаток», близкий для похожих по содержанию текстов и далекий для непохожих. Иными словами, если два описания инцидентов описывают схожие проблемы, их векторы будут расположены близко друг к другу в пространстве; если же тематика разная – векторы будут далеко друг от друга. Важное свойство эмбеддингов (в отличие от простых хеш-функций) состоит в том, что концептуально схожий ввод приводит к близким векторным значениям, даже если тексты формально не совпадают по словам. Благодаря этому можно сравнивать семантическое сходство. То есть алгоритм обучается сравнивать «яблоки и апельсины» по смыслу, а не по буквальному совпадению.
Для вычисления степени близости векторов используется метрика косинусного расстояния (cosine similarity). Эта метрика возвращает значение от 0 до 1, отражающее степень сходства, и широко применяется для ранжирования похожести текстов. Чем меньше значение метрики, тем ближе по смыслу тексты.
Выбор модели эмбеддинга. В проекте были протестированы несколько предобученных моделей для векторизации текстов инцидентов. Основными критериями выбора были: качество семантического соответствия для русскоязычных и англоязычных текстов (другие языки не требовались), производительность и возможность локального запуска.
Среди протестированных моделей были:
- all-MiniLM-L6-v2: компактная модель размерности 384, давала хорошую скорость, но уступала по качеству: в исторических данных пропускала релевантные случаи или ранжировала их ниже;
- LaBSE-en-ru: «урезанная» модель от Google с размерностью вектора 768, содержащая только токены русского и английского языков. Модель очень хорошо справлялась с текстами инцидентов;
- ru-en-RoSBERTa: русско-английская модель с размерностью векторов 1024, специально дообученная для работы с двуязычными текстами.
По итогам экспериментов победителем по качеству поиска схожих инцидентов стала модель ru-en-RoSBERTa: она показала наилучшее ранжирование и точность сопоставления, особенно при анализе инцидентов, содержащих технические термины, коды ошибок и смешанную лексику. В частности, она лучше других справлялась с короткими описаниями и контекстами без явных ключевых слов.
Использование модели для получения эмбеддингов текста. Пример кода:
# Загрузка модели
model = SentenceTransformer(‘ru-en-RoSBERTa’)
# Векторизация текста
vectors_description = model.encode(df[‘description’].tolist())
Переформулировка описаний с LLM. Особенностью подхода стало использование большой языковой модели (LLM) для улучшения качества векторизации. Как было сказано выше, иногда описания инцидентов бывают скудными или неочевидными, или наоборот слишком длинными. Например, тикет может содержать лишь фразу «Приложение не работает» или фрагмент лога, не коррелирующий напрямую с настоящей проблемой. В таких случаях даже хорошая модель эмбеддинга может не понять суть и «выдать» не самых релевантных соседей. Поэтому был реализован шаг дополнительной обработки описания с помощью LLM: перед векторизацией агент может отправить языковой модели запрос, чтобы та переформулировала, дополнила или сжала описание, подчеркнув ключевые детали. По сути LLM выступает как «улучшатель» запроса для поиска. Получив краткий текст ошибки, модель генерирует более развернутое описание проблемы (например, предполагает, что именно не работает и при каких условиях), которое затем тоже превращается в вектор. Далее поиск похожих инцидентов производится и по исходному, и по переформулированному описанию с выбором наиболее близких по смыслу инцидентов. Такой прием позволил в ряде случаев извлекать из базы инцидентов более точные совпадения.
Пример промта для LLM с целью переформулировки текстов инцидентов:
prompt_template = «»»
Вы являетесь ИТ-специалистом, который обрабатывает ИТ-инциденты.
Ваша задача — обработать описания ИТ-инцидентов, чтобы улучшить качество извлечения похожих текстов из векторного хранилища. Следуйте этим инструкциям:
Если запрос совсем короткий и недостаточно информативен, дополните его, добавив возможные причины, контекст или дополнительные детали.
Если запрос длинный и содержит много лишних деталей, в том числе повторяющихся — сократите его, оставив только ключевую информацию.
Уберите излишние подробности, но сохраните контекст, необходимый для понимания проблемы.
Верните только измененное описание инцидента, ничего больше.
Описание инцидента:
{query}
Ваш вариант:
«»»
Векторная база данных
Для хранения всех эмбеддингов прошлых инцидентов и выполнения поиска по ним понадобилась специализированная база данных, поддерживающая операции с векторами. Рассматривалось несколько вариантов векторных баз данных (Vector DB): интегрированные решения и отдельные движки. Среди популярных – полнофункциональные векторные хранилища Weaviate, Chroma, FAISS, а также расширения для классических СУБД, такие как PGVector для PostgreSQL.
PGVector добавляет тип данных «Vector», обеспечивая хранение эмбеддингов в виде векторов и поиск ближайших векторов по косинусному расстоянию, а также другими способами. Из достоинств такого решения – привычная работа с СУБД PostgreSQL и SQL запросами, надёжность, масштабируемость.
Взвесив все варианты, я выбрал именно PGVector
Пример SQL-кода для создания таблицы-хранилища с инцидентами:
#Создание таблицы с инцидентами
conn = psycopg2.connect(**DB_CONFIG)
cur = conn.cursor()
cur.execute(«»»
CREATE TABLE IF NOT EXISTS incidents (
id SERIAL PRIMARY KEY,
number INTEGER,
creation_date DATE,
report_text TEXT,
theme TEXT,
description TEXT,
service TEXT,
embedding_description vector(1024),
llm_response TEXT
);
«»»)
conn.commit()
cur.close()
conn.close()
В реализованной системе векторная база содержит таблицу, где для каждого ранее решённого инцидента хранится:
- идентификатор;
- время создания;
- ИТ-сервис, по которому зарегистрирован инцидент;
- текстовые поля (описание, решение и др.);
- векторное представление описания.
При поступлении нового инцидента выполняется SQL-запрос с оператором поиска ближайших соседей по вектору. В запросе указывается количество требуемых ближайших соседей k и условие по метрике (косинусной близости). Например, можно найти 5 наиболее похожих инцидентов по косинусному расстоянию, превышающему некоторый порог.
Пример SQL-кода для поиска похожих инцидентов:
# Функция для поиска похожих векторов по embedding
def search_similar_vectors(query, k=5, distance_threshold=0.5):
query_vector = model.encode([query])[0].tolist()
try:
cur.execute(«»»
SELECT id, number, creation_date, report_text, theme, description, service, embedding_combined,
(embedding_combined <=> %s::vector) AS distance
FROM incidents
WHERE embedding_combined <=> %s::vector < %s
ORDER BY distance
LIMIT %s
«»», (query_vector, query_vector, distance_threshold, k))
results = cur.fetchall()
return results
except Exception as e:
conn.rollback()
print(f»Ошибка при поиске векторов: {e}»)
return []
На практике порог пришлось откорректировать. Изначально допускались совпадения с близостью >0.5, но это приводило к попаданию нерелевантных случаев. После наблюдения за работой сервиса на протяжение пары недель порог был снижен до 0.15. Качество отбора существенно улучшилось – в список кандидатов стали попадать действительно только похожие тикеты. В дальнейшем можно будет настроить более тонкое ограничение: например, по категории услуги или по давности, чтобы сузить контекст поиска.
Найденные похожие инциденты извлекаются из БД вместе с их описаниями и решениями. Отбирается до 5 записей, которые передаются языковой модели на анализ. После создания рекомендации информация о новом инциденте заносится в базу; сохраняется эмбеддинг инцидента, чтобы будущие запросы находили и этот случай, и текст решения, связанный с данным описанием. Так база знаний постоянно пополняется новыми примерами, со временем качество подсказок будет расти.
Выбор и использование LLM: Ollama + Gemma 3
Важнейший компонент системы – большая языковая модель (LLM). Модель используется дважды: для улучшения описания и финальной генерации текста рекомендации. Поскольку проект развёртывается в офлайн-среде (без доступа к облачным API), выбор пал на модель с открытым исходным кодом, которую можно запускать локально на имеющемся оборудовании. Ограниченные ресурсы (отсутствие GPU) на этапе MVP накладывали в свою очередь на модель ограничения по числу параметров, иначе скорость ответа была бы неудовлетворительной. После тестирования нескольких моделей приемлемую скорость удалось получить на моделях размером не более 4В (4 млрд. параметров).
Были рассмотрены несколько современных открытых LLM. Среди них: Qwen 2.5, T-lite 1.0, YandexGPT-5-Lite, Gemma-3. В итоге для прототипа была выбрана модель Gemma 3-4b-instruct – LLM с 4 миллиардами параметров, обученная следовать инструкциям (instruct-tuned). Хотя 4 млрд параметров – это существенно меньше, чем у самых продвинутых моделей, она справилась с задачами проекта. Модель была загружена в квантованном виде (4-бит), что еще больше снизило требования к памяти (до нескольких гигабайт) при минимальной потере качества. Использование 4-битной квантованной модели позволило запускать инференс LLM на CPU достаточно быстро – создание рекомендации занимало примерно минуту.
Для простоты развёртывания модель Gemma 3b запускалась через инструмент Ollama – оптимизированный для LLM локальный сервис, который предоставляет простой API для генерации текста. Ollama облегчает использование моделей вроде Llama/Gemma: он берёт на себя загрузку модели в память, управление контекстом, ускорение за счёт квантования и так далее. Агент взаимодействует с Ollama по API, отправляет промпт и получает законченный ответ.
При работе я выявил некоторые проблемы со стабильностью Ollama – иногда мне не удавалось получить ответ от модели без видимых проблем в логах. В дальнейшем планирую постоянно обновлять версию Ollama, пока проблема со стабильностью не будет решена. В перспективе планирую полную замена Ollama на другой подобный инструмент: например VLLM (если появится сервер с GPU).
Качество ответов выбранной модели оказалось вполне приемлемым. Несмотря на небольшой размер, Gemma-3-4b в большинстве случаев формулировала корректные и уместные рекомендации для инцидентов. Модель редко «галлюцинировала», генерируя посторонний контент: все выводы действительно основывались на предоставленном контексте, как и требовалось. Очевидно, ограничение области знания только информацией об инцидентах компании (через примеры из базы) дисциплинирует даже небольшую LLM и помогает ей генерировать правдоподобные и целевые советы.
Применение LangChain и RAG
Для организации описанного взаимодействия компонентов использовался фреймворк LangChain, который предоставляет абстракции для построения цепочек вызовов LLM с дополнением из внешних источников. Сценарий работы RAG-агента был реализован как последовательность из нескольких шагов (Chain) с промежуточным хранением результатов. По сути, была сформирована связка «Retriever – Analyzer – Action»: сначала блок Retriever выполняет векторный поиск похожих случаев, затем блок Analyzer (LLM) на основе найденного анализирует ситуацию и формирует ответ, и наконец блок Action доставляет этот ответ обратно в систему, в нашем случае – публикует комментарий.
В LangChain предусмотрены компоненты для интеграции с векторными хранилищами (VectorStore). Однако прямой поддержки PGVector нет, поэтому обращение к базе PostgreSQL происходит через собственный запрос. Но концептуально это тот же retriever: агент получает на вход текст запроса (описание инцидента), обращается к векторному индексу и получает топ-N документов (описаний прошлых инцидентов) в качестве релевантного контекста. Далее формируется промт для LLM, содержащий описание нового инцидента и извлеченные описания прошлых.
Пример кода, реализующего запрос к LLM через LangChain:
#Обработка инцидента с помощью LLM
def get_llm_response (query, context):
# Настройка LangChain
llm = OllamaLLM(model=«gemma-3-4b-it.Q4_K_M:latest», base_url=«http://localhost:11434», num_ctx=8192)
# Создание промпта
prompt_template = «»»
Вы являетесь ИТ-специалистом, который обрабатывает ИТ-инциденты.
На основе предоставленной информации о ранее закрытых инцидентах, дайте заключение о том, как лучше решить текущий инцидент.
Ответ должен быть не более 3000 символов.
Не повторяйтесь.
Информация о ранее закрытых инцидентах:
{context}
Текущий инцидент:
{query}
Ваше заключение:
«»»
prompt = PromptTemplate(
input_variables=[«context», «incident»],
template=prompt_template
)
# Создание цепочки
chain = RunnableSequence(prompt | llm | StrOutputParser())
# Обработка результата с помощью LLM
llm_response = chain.invoke({«context»: context, «query»: query})
# Вывод результатов
return llm_response
После генерации текста LangChain-цепочка выполняет финальный шаг: передаёт полученный ответ обратно через API Service Desk, добавляя комментарий в тикет. Инженер, работающий с системой, видит новый комментарий от виртуального ассистента (агента) с рекомендацией и списком ссылок на похожие инциденты. Дальше он может следовать совету либо игнорировать его: решение всегда остается за человеком, агент лишь предоставляет информацию для поддержки решения.
Статистика внедрения и эффективность
Разработанный RAG-агент был развёрнут как MVP на части потока инцидентов для тестирования. На момент подготовки статьи система обработала около 500 инцидентов. Среднее время, затрачиваемое агентом на полный цикл (от получения данных до публикации комментария), составило 1–2 минуты на инцидент. Это время существенно меньше того, которое затратил бы инженер на ручной поиск похожих случаев и анализ.
Экономия составляет в среднем 10 минут на каждый инцидент. Таким образом, например при ~1000 инцидентах в месяц можно сэкономить около 167 человеко-часов, что эквивалентно работе примерно 1 инженера полной занятости.
Некоторые ключевые метрики и результаты внедрения MVP:
- Сокращение времени анализа: ~10 минут экономии при анализе инцидента, более 80 часов в месяц экономии труда специалистов. Инженеры могут направить это время на более сложные задачи вместо поиска в документации.
- Оптимизация штата: высвобождение эквивалента 0,5 инженера за счет автоматизации.
- Снижение финансовых затрат: экономия фонда оплаты труда оценочно достигает 200-300 тыс. рублей в месяц за счёт автоматизации рутины, даже с учётом стоимости вычислительных ресурсов для работы агента.
- Ускорение решения инцидентов: чем больше скорость поиска решение, тем меньше время простоя сервисов и сопутствующие убытки компании. Выгода от сокращения времени простоя может составлять до нескольких миллионов рублей в месяц для инцидентов, влияющих на продажи или критически важные процессы.
- Затраты на поддержку решения: использование открытых моделей позволило избежать прямых расходов на лицензии. Стоимость инфраструктуры, поддержка и доработка совокупно обходится не более чем в 100 тыс. руб/мес. С учётом достигаемой экономии, окупаемость решения – порядка 1-2 месяцев. Первоначальные вложения (включая разработку) были невелики и тоже быстро окупились.
В целом, пилотное внедрение подтвердило ценность системы: агент экономит время и улучшает качество решения инцидентов.
Ниже приведены примеры комментариев, которые RAG-агент добавил в тикет, предложив вероятное решение на основе похожих случаев.
Выводы и дальнейшее развитие
Подход с использованием векторных представлений и больших языковых моделей может эффективно помочь в управлении инцидентами. По итогам работы MVP принято решение развивать проект дальше, расширять его охват и функциональность.
Качество рекомендаций. Анализ итогов работы MVP показал, что в большинстве ситуаций советы агента корректны и полезны. Инженеры подтвердили, что полученные рекомендации часто совпадают с их собственными выводами, соответствуют реальным причинам проблем и помогают выработать способы решения. Конечно, были и ситуации, когда агент мог предложить неподходящее решение: например, если в базе ещё нет похожего кейса или описания инцидента слишком размыты. Но даже в таких случаях наличие подсказки расширяет круг решений и может навести инженера на правильную мысль.
Проблемы и уроки
- Иногда алгоритм подбирал нерелевантные инциденты в качестве похожих инцидентов. Причина крылась в слишком мягких критериях сходства; ситуацию удалось улучшить с помощью настройки порога косинусной близости. В дальнейшем я планирую решать проблему с помощью фильтрации по услугам и времени.
- Качество исходных данных накладывает ограничения: если текст решения в прошлом инциденте был заполнен формально (например: «Исправлено» или «Решение применено»), то такой кейс принесёт мало пользы. Нужно улучшать качество закрытия инцидентов. Например, регламентировать заполнение поля «Решение» или автоматически проверять тексты решений инцидентов с помощью той же LLM.
- Обнаружилось, что для некоторых специфических услуг или систем даже семантический поиск не даёт результатов из-за малого числа примеров. Поэтому необходимо использовать и накапливать базу знаний по редким категориям: например, создавать отдельные векторные базы по каждому направлению, куда можно добавлять не только инциденты, но и связанные артефакты (документацию, FAQ) для повышения качества.
Планы развития
Следующими шагами в развитии системы станут:
- улучшение стабильности и производительности (в том числе перенос вычислений на GPU-сервер при появлении такового)
- качественное обновление LLM-модели и подключение полного потока инцидентов.
По мере накопления данных откроются новые возможности аналитики: автоматический анализ качества решений инцидентов, поиск скрытых закономерностей в причинах сбоев. В перспективе такой агент может эволюционировать в полноценного помощника SRE/DevOps-команды: не только подсказывать решения, но и проактивно уведомлять о повторяющихся проблемах, предлагать классификацию инцидентов, а возможно, и взаимодействовать с пользователями для сбора дополнительной информации.
Проект RAG-агента для инцидент-менеджмента показал, что современные методы NLP могут успешно применяться для интеллектуальной поддержки операционных процессов. Объединение методов семантического поиска и генерации позволяет извлекать ценность из корпоративной памяти, делает знания полезными и своевременными.