Привет, Хабр!

Меня зовут Анатолий, занимаюсь автоматизацией бизнес-процессов и применением Искусственного Интеллекта в бизнесе.

Кейсовая задача - создать Систему генерации ответов на основе существующей истории тикетов. При этом Система должна работать в закрытом контуре.

Это вторая часть.
В первой части был рассмотрен подход Question-Answering с timpal0l/mdeberta-v3-base-squad2 (модификация BERT для задач Question-Answering).

В этой части переходим к семантическому поиску, контекстному сходству и SentenceTransformer.

SentenceTransformer

SentenceTransformer - библиотека для преобразования текста в векторные представления (эмбеддинги). SentenceTransformer позволяет преобразовывать слова и предложения в числовые представления, которые учитывают смысл, а не только лексическое совпадение. Это ключевое отличие от классического поиска по ключевым словам.

Поиск релевантных тикетов

  1. Формирование базы знаний
    Ответы сотрудников из истории тикетов собраны в массив answers. Этот массив становится нашей базой знаний для дальнейшей векторизации.

  2. Загрузка модели
    Выбрана модель deepvk/USER-bge-m3, оптимальное решение для русскоязычных текстов. Модель специализируется на понимании контекста и преобразовании текста в векторные представления, сохраняя его смысловую нагрузку. .

  3. Векторизация истории тикетов
    Каждый текст из массива answers проходит через модель и превращается в вектор фиксированной размерности. Это позволяет перейти от сравнения строк к анализу векторов, что дает возможность находить ответы, близкие по смыслу, даже если они сформулированы иначе.

  4. Векторизация вопроса
    Вопрос пользователя также векторизуется с использованием той же модели. Теперь вопрос и ответы представлены в одном векторном пространстве.

  5. Вычисление косинусного сходства
    После преобразования вопроса и ответов в векторы основная задача - найти наиболее релевантные тикеты. Для этого применяется косинусное сходство - метрика, показывающая степень близости векторов вопроса и ответа в многомерном пространстве. Значения варьируются от -1 (противоположные по смыслу) до 1 (полностью идентичные).
    Вычисляется косинусное сходство между вектором вопроса и всеми векторами ответов.

  6. Ранжирование результатов
    Ответы сортируются по убыванию косинусного сходства, чтобы определить 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 релевантных тикетов для дальнейшей работы или выдачи.

Оптимизация хранения и загрузки данных

В массив answers были добавлены идентификаторы тикетов и sorted_answers_scores стал хранить не тексты тикетов, а идентификаторы тикетов. Это давало некоторые преимущества, так как комбинация "эмбеддинги + идентификаторы" занимают значительно меньше места по объему памяти, чем "эмбеддинги + тексты тикетов".

Для хранения данных (идентификаторы, тексты, эмбеддинги) применяется MySQL.

При поиске релевантных тикетов применяются идентификаторы и эмбеддинги (минимальная нагрузка на память и передачу данных). После вычисления косинусного сходства тексты по идентификаторам подгружаются только для TOP-N релевантных тикетов.

Интеграция и веб-сервис

На основе предыдущего кода и Flask создан веб-сервер, который обрабатывает входящие HTTP-запросы с вопросами и возвращает JSON-ответ с TOP-N релевантных тикетов.

Применение

Данное решение легко адаптируется под разные задачи - от помощи сотрудникам до полной автоматизации ответов.

Два ключевых варианта применения:

  1. Ассистент для сотрудников
    Сотрудники могут отправлять вопросы и моментально получать TOP-N релевантных тикетов, ранжированных по косинусному сходству. Это позволяет сотрудникам не искать информацию вручную, упрощает формирование собственных ответов, помогает более быстрой адаптации новому сотруднику.

  2. Автоматическая генерация итогового ответа
    Для автоматической генерации итогового ответа в заданном стиле тексты соответствующих TOP-N релевантных тикетов нужно передать в генеративную модель вместе с соответствующим промптом.
    На сервер в закрытом контуре был установлен экземпляр Ollama, и итоговые ответы генерировались с применением квантизованной версии модели llama3.2 на основе соответствующих TOP-N релевантных тикетов.

Выявленные критические проблемы

Были выявлены две критические проблемы, напрямую влияющие на качество, актуальность и достоверность ответов:

  1. Проблема достоверности: противоречия в ответах в истории тикетов
    Изначально предполагалось, что ответы сотрудников в истории тикетов корректны и согласованы. Однако на практике выявилось, что иногда разные сотрудники дают различающиеся ответы на один и тот же вопрос. Например:
    "Срок обработки заявки - до 3 дней"
    "Срок обработки заявки - до 5 дней"

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

  2. Проблема актуальности: устаревание информации в истории тикетов
    История тикетов - это статичный снимок прошлого. Если процессы, правила или документация изменяются, то Система все равно продолжит выдавать устаревшие ответы, не учитывая обновления, так как база знаний для Системы не изменилась.

Валидация базы знаний необходима. Без валидации базы знаний Система не может гарантировать достоверность и актуальность ответов, и это не решается ни увеличением размера окна контекста, ни улучшением эмбеддингов, ни масштабированием аппаратных мощностей.