
Привет! Меня зовут Ирина, я занимаюсь NLP для автоматизации поддержки в Центре искусственного интеллекта Т-Банка. В статье расскажу, как мы исследовали применение LLM в автоматизации поддержки: какие подходы попробовали, какие сложности возникли и какие решения оказались наиболее эффективными.
Классические системы поддержки строятся на предсказании интентов с последующим запуском сценариев. Но такие решения часто не учитывают контекст, из-за чего пользователи вынуждены переформулировать вопросы или обращаться к оператору. Мы решили проверить, сможет ли LLM заменить традиционный пайплайн классификации интентов, повысить точность понимания и улучшить пользовательский опыт.
Наш эксперимент показал, что LLM действительно открывает новые возможности, но не все оказалось так просто. В статье я расскажу, какие техники сработали, какие проблемы пришлось решать и как LLM можно эффективно встроить в реальную систему поддержки.
Как работает автоматизация поддержки
Классический подход выглядит так. Допустим, пользователь спрашивает в чате поддержки: «Как заказать платежный стикер для телефона?» По этой фразе мы определяем интент, в данном случае — «Оформить стикер T-Pay». И после этого запускаем соответствующий сценарий.

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

У классического подхода есть недостатки. Если мы будем оценивать сообщение клиента без контекста, то вряд ли что-то поймем:

В данном случае человек спрашивает про валюту, но что именно его интересует и какая валюта? По идее, для понимания контекста нужно посмотреть предыдущие и последующие сообщения. А чтобы получить эту информацию, нужно вести разговор естественно, то есть задавать уточняющие вопросы, ведь люди очень часто формулируют свои запросы без подробностей.

Нам нужно учитывать контекст и задавать вопросы, чтобы избавиться от недостатков, характерных для классической автоматизации. Это можно успешно делать с помощью больших языковых моделей.
Например, пользователь пишет в чат слово «кэшбэк». С помощью нашей внутренней ML-модели мы определяем потенциальные интенты, к которым может относиться сообщение: какие условия получения кэшбэка, максимальное количество бонусов и другие. Потенциальные интенты мы добавляем в промпт для LLM и просим решить, нужно ли запустить какой-то из предложенных ей сценариев.

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

Важно, что мы генерируем вопросы исключительно на основе тех предложенных интентов, которые передаем в промпт. То есть мы не хотим, чтобы LLM придумывала отсебятину, поэтому просим генерировать интенты-аргументы и на их основе генерировать вопрос.
Если из диалога понятно, что мы уже помогли клиенту, тогда беседу можно заканчивать и прощаться. А если модель видит, что из переданного нами списка ни один интент не подходит и невозможно сгенерировать уточняющие вопросы, то беседу переводят на оператора, потому что явно речь идет о достаточно сложном вопросе.
Метрики и генерация кандидатов
В первую очередь мы измеряем, насколько точно распознаем каждое из предложенных в промпте действий action_accuracy, а также точность для аргументов argument_accuracy — интентов и уточняющих вопросов. А как оценивать релевантность вопросов?

Мы выделили потенциальные интенты: под каждую реплику клиента разметчики выбирали потенциально подходящие интенты из списка предложенных. LLM задает вопросы только на их основе. Благодаря такой разметке мы можем измерять качество на исторических данных и смотреть, насколько верно LLM выбирает интенты.
Для генерирования кандидатов у нас уже было внутреннее решение, которое мы переиспользовали для этой задачи, — классификатор интентов на основе эмбеддингов и KNN. В нашем внутреннем KNN-индексе лежат размеченные реплики клиентов. На его основе можно делать поиск и по новым репликам из диалога: искать ближайших соседей — кандидатов, которые мы будем подавать в промпт.
В качестве эмбеддера у нас используется RoBERTa, обученная на банковском домене. Дополнительно мы научили ее хорошо разделять интенты в векторном пространстве: если интенты одинаковые, то векторы сближаются, а если разные — отдаляются. Благодаря этому поиск работает очень точно, эмбеддер знает про нашу тематическую область и хорошо ориентируется в текстах.
Из поиска мы выбираем большой список интентов, и нужно отбросить часть из них, оставив наиболее релевантные, которые мы затем отправим в промпт. Фильтруем с помощью порогового значения, или реранкера. В качестве метрики считали recall списка кандидатов, так как нам нужно получить полный список. А чтобы в него обязательно попадало целевое предсказание из разметки, мы стремились в каждом случае добиться recall как можно ближе к единице.
Что сработало и не сработало
Сработало: индекс с разнообразными данными. Нужно положить в индекс разнообразные данные по каждому интенту, чтобы поиск работал хорошо.

Для этого мы собирали разметку через active learning. Реплики, на которых модель показывала низкую уверенность, отправляли на переразметку. Благодаря этому удалось расширить наш набор данных, и поиск стал работать гораздо лучше. Recall вырос примерно на 1,5%.
Сработало: подбор порогового значения интентов. У нас из поиска формируется довольно большой список кандидатов. Нужно отфильтровать небольшой пул для отправки в LLM. В этом помог подбор оптимальных пороговых значений, позволивший максимизировать recall на наших данных, но при этом не дать списку слишком разрастаться. Метрику recall повысили примерно на 1,4%.
Что не сработало. Очень часто для задач вроде нашей используется переранжирование с помощью кросс-энкодера. Он просматривает большой пул кандидатов, ранжирует их и выбирает тех, что лучше всего подходят. В нашем случае пороговое значение оказалось довольно хорошим бейзлайном, и в целом кросс-энкодер не сильно уменьшал количество кандидатов. Метрика recall не выросла, поэтому решили, что можно использовать пороговое значение, а кросс-энкодер оказывается лишним.
Что подавать в LLM
Конечно, промпт. Расскажу подробнее о том, с помощью какой информации мы расширяли промпт, чтобы добиться от LLM более качественных решений в рамках нашей тематики.
Примеры по каждому кандидату — и не случайные, а ближайшие к реплике клиента из каждого интента. Когда мы для какой-то реплики хотим определить интент, то через KNN-индекс ищем ближайших кандидатов для каждого предложенного интента. Благодаря этому LLM лучше ориентируется, где зона сравнения каждого интента, и ей гораздо проще принять решение, потому что эти фразы похожи на основную реплику клиента. Метрика распознавания интентов argument_accuracy выросла на 1%.
Описания интентов. Хотя у нас большая база из 2000 примеров, но их названия раньше записывали не слишком информативно.

По такому названию модели вряд ли будет понятно, что хочет клиент. Нужно добавить информацию. Благодаря новым описаниям получилось увеличить метрику argument_accuracy на 1%.
Мы улучшили названия и описания интентов. Нужно понимать, как делать это правильно, на каких интентах модель плохо срабатывает.

Мы прогоняли через нашу систему стандартный датасет классификации и смотрели, на каких интентах LLM показывает низкое качество, то есть вообще ничего не понимает по информации, которую мы в нее подаем. Переделали им названия и описания, и благодаря этому метрика argument_accuracy выросла на 1%.
Как проверять точность работы пайплайна
В нашем примере для проверки нужны были не просто реплики из клиентских сообщений, а целый диалог. Поэтому на начальном этапе запустили для сотрудников прототип, чтобы собрать эти самые диалоги и потом использовать их для оценки качества нашей системы. Затем разметили полученную информацию: если разметчики соглашались с предсказанием, то отправляли его в тестовый датасет, а если нет — на мультиклассовую разметку. Это важно, потому что в таких ситуациях наше решение сработало плохо и нужно улучшать. Пример разметки:

Для оценки качества модели важно проверять ее работу на диалогах разной длины и сложности, поэтому мы решили соответствующим образом разделить тестовый датасет: у нас есть короткие и длинные диалоги, на которых наше решение наверняка будет работать по-разному; у нас есть диалоги с контекстом и без контекста; есть очень популярные интенты; есть интенты с низкой точностью.
Итоговые метрики, которые мы получили на исторических данных: action_accuracy = 0,84, argument_accuracy = 0,8. С такими метриками мы решили запускать А/Б-тест.
Результаты и выводы
Мы запускали А/Б-тесты на сотрудниках. В основном измеряли:
-
автоматизацию — насколько хорошо бот закрывает диалоги и не переводит на оператора;
-
качество диалогов — точность распознавания интентов и релевантность уточняющих вопросов.
При полной замене текущего пайплайна на LLM-решение А/Б-тест не показал улучшения. Текущая система создавалась годами, и ее замена требует учесть множество нюансов. Однако LLM открывает возможности для автоматизации новых типов запросов, особенно контекстных вопросов. Сейчас мы ищем оптимальную зону встраивания LLM в общий пайплайн, чтобы повысить эффективность всей системы.
Основные выводы
-
LLM не заменяет весь пайплайн, но помогает обрабатывать новые зоны, которые ранее не удавалось автоматизировать: позволяет лучше понимать последовательность сообщений и уточнять запросы пользователей.
-
LLM расширяет возможности автоматизации: использование модели делает диалоги более естественными за счет уточняющих вопросов. В перспективе можно интегрировать дополнительные данные о пользователе (история обращений, информация о пользователе) для персонализированного опыта.
-
Для работы с LLM недостаточно просто передавать интенты. Успешное внедрение требует пересмотра структуры данных и интеграции LLM в существующую экосистему поддержки.
LLM — это мощный инструмент для улучшения клиентского сервиса, но его внедрение требует не замены существующих решений, а их разумного дополнения. Чтобы достичь максимального эффекта, важно грамотно определять области применения и тщательно выстраивать процесс интеграции в существующую систему поддержки.
Автор: ira_step