Привет, Хабр!
Меня зовут Анатолий, занимаюсь автоматизацией бизнес-процессов и применением Искусственного Интеллекта в бизнесе.
Кейсовая задача - создать Систему генерации ответов на основе существующей истории тикетов. При этом Система должна работать в закрытом контуре.
Это вторая часть.
В первой части был рассмотрен подход Question-Answering с timpal0l/mdeberta-v3-base-squad2 (модификация BERT для задач Question-Answering) - модели, умеющей "читать" текст и "вытаскивать" ответы.
В этой части переходим к семантическому поиску, контекстному сходству и SentenceTransformer.
SentenceTransformer
SentenceTransformer - библиотека для преобразования текста в векторные представления (эмбеддинги). SentenceTransformer позволяет преобразовывать слова и предложения в числовые представления, которые учитывают смысл, а не только лексическое совпадение. Это ключевое отличие от классического поиска по ключевым словам.
Поиск релевантных тикетов
Формирование базы знаний
Ответы сотрудников из истории тикетов собрали в массив answers. Этот массив становится нашей базой знаний для дальнейшей векторизации.Загрузка модели
Использовали модель deepvk/USER-bge-m3, оптимальное решение для русскоязычных текстов. Модель специализируется на понимании контекста и преобразовании текста в векторные представления, сохраняя его смысловую нагрузку. .Векторизация истории тикетов
Каждый текст из массива answers проходит через модель и превращается в вектор фиксированной размерности. Это позволяет перейти от сравнения строк к анализу векторов, что дает возможность находить ответы, близкие по смыслу, даже если они сформулированы иначе.Векторизация вопроса
Вопрос пользователя также векторизуется с использованием той же модели. Теперь вопрос и ответы представлены в одном векторном пространстве.Вычисление косинусного сходства
После преобразования вопроса и ответов в векторы основная задача - найти наиболее релевантные тикеты. Для этого применяется косинусное сходство - метрика, показывающая степень близости векторов вопроса и ответа в многомерном пространстве. Значения варьируются от -1 (противоположные по смыслу) до 1 (полностью идентичные).
Вычисляется косинусное сходство между вектором вопроса и всеми векторами ответов.Ранжирование результатов
Ответы сортируем по убыванию косинусного сходства, чтобы определить TOP-N релевантных тикетов.
Код на python
import torch
from sentence_transformers import SentenceTransformer
import numpy as np
# Инициализация модели
model_name = "deepvk/USER-bge-m3"
model = SentenceTransformer(model_name)
# Перемещение модели на GPU, если доступно
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = model.to(device)
# Преобразование текстов тикетов в эмбеддинги
embeddings_answers = model.encode(answers, normalize_embeddings=True)
# Преобразование текста вопроса в эмбеддинг
embeddings_question = model.encode(question, normalize_embeddings=True)
# Вычисление косинусного сходства
scores = embeddings_question @ embeddings_answers.T
# Создание массива с показателями и текстами
answers_scores = np.column_stack((scores, answers))
# Сортировка массива по убыванию косинусного сходства
sorted_answers_scores = sorted(answers_scores, key=lambda x: x[0], reverse=True)
Примечание
Согласно карточке deepvk/USER-bge-m3 на huggingface, модель поддерживает окно контекста 8194 токена - этого достаточно для обработки большинства текстов ответов в тикетах без разбиения на чанки. Это упрощает реализацию и ускоряет обработку. В будущем можно добавить чанкинг, если при оптимизации качества анализ покажет такую необходимость. На текущем этапе разбиение не делали.
В результате формируется двумерный массив, в котором каждая строка содержит значения косинусного сходства, отсортированные по убыванию, и соответствующие тексты ответов из истории тикетов. Такой формат обеспечивает мгновенный доступ к TOP-N релевантных текстов для дальнейшей работы или выдачи.
Интеграция и веб-сервис
На основе предыдущего кода и Flask создали веб-сервер, который обрабатывает входящие HTTP-запросы с вопросами и возвращает JSON-ответ с TOP-N релевантных тикетов.
Варианты применения
Данное решение легко адаптируется под разные задачи - от помощи сотрудникам до полной автоматизации ответов.
Два ключевых варианта применения:
Ассистент для сотрудников
Сотрудники могут отправлять на сервер вопросы и моментально получать TOP-N релевантных тикетов, ранжированных по косинусному сходству. Это позволяет сотрудникам не искать информацию вручную, упрощает формирование собственных ответов, помогает более быстрой адаптации новому сотруднику.Полная автоматизация ответов
Для полной автоматизации ответов разворачивается Ollama с квантизованной Llama3.2 (оптимизирована для работы на CPU/GPU средней мощности) и генерируется автоматический ответ на основе полученных TOP-N релевантных тикетов, ранжированных по косинусному сходству.
Оптимизация хранения и загрузки данных
Также мы создавали массив answers не только из текстов тикетов, но и включали в него идентификаторы тикетов. И в таком случае в sorted_answers_scores хранили не тексты тикетов, а идентификаторы тикетов. Это давало некоторые преимущества, так как комбинация "эмбеддинги + идентификаторы" занимают значительно меньше места по объему памяти, чем "эмбеддинги + полные тексты".
В финальной версии мы использовали MySQL для хранения таблицы: идентификаторы, тексты, эмбеддинги.
При запуске Системы загружались только идентификаторы и эмбеддинги (минимальная нагрузка на память и передачу данных). После вычисления косинусного сходства тексты по идентификаторам подгружались только для TOP-N релевантных тикетов.
Выявленные критические проблемы
До этого момента все было хорошо, однако в дальнейшем были выявлены две критические проблемы, напрямую влияющие на качество, актуальность и достоверность ответов:
Проблема достоверности: противоречия в исторических ответах
Изначально предполагалось, что ответы сотрудников в истории тикетов корректны и согласованы. Однако на практике выявилось, что иногда разные сотрудники дают различающиеся ответы на один и тот же вопрос. Например:
"Срок обработки заявки - до 3 дней"
"Срок обработки заявки - до 5 дней"
Система не может определить, какой ответ верный, так как для Системы оба варианта считаются одинаково корректными с точки зрения семантического сходства. Отсутствует механизм валидации - алгоритм только ищет сходство по смыслу, но не проверяет правильность.Проблема актуальности: устаревание информации в истории тикетов
История тикетов - это статичный снимок прошлого. Если процессы, правила или документация изменяются, то Система все равно продолжает выдавать устаревшие ответы, не учитывая обновления.
Стратегическое решение: отказ от истории тикетов в пользу валидированной документации
Технически Система функционировала корректно, но упомянутые критические проблемы сделали ее непригодной для реального использования.
Важно, что противоречивость и устаревание исходной информации никак не решаются технически - ни увеличением размера окна контекста, ни улучшением эмбеддингов, ни масштабированием аппаратных мощностей. Это недостаток самого подхода, но не техническая проблема. Без валидации данных Система не может гарантировать достоверность и актуальность ответов.
Поскольку валидация истории тикетов не планировалась, в итоге мы отказались от использования истории тикетов в пользу более надежного решения: поиску TOP-N релевантых фрагментов аналогичным образом, но в валидированной документации.