Речевая аналитика в контакт-центре, или в отделе продаж — это ключевой инструмент для оценки качества обслуживания и соблюдения стандартов общения. С развитием больших языковых моделей (LLM), таких как YandexGPT, подход к анализу диалогов кардинально изменился. Теперь вместо поиска словосочетаний и настройки сложных шаблонов — достаточно передать весь диалог модели и задать ей понятные вопросы

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

Скриншот со скриптами диалогов

Теперь достаточно интегрировать аналитику с YandexGPT. Рассмотрим чуть более сложную аналитику, чем оценка «было ли приветствие». Оценим эмоциональность диалога (положительно, нейтрально, негативно), и пытался ли менеджер решить основную задач. Например, для автосалон, основная задача менеджера — пригласить клиента посетить автосалон. Еще желательно вылавливать случаи «Нет в наличии», когда клиент искал автомашину, которой нет в наличии.

Шаг 1. Собрать системный промпт

В запросе на API Яндекс есть два промпта, system и user. В системном промпте описываем общее задание для GPT, и добавляем описание, как результаты обработки разложить в JSON.

const mainPrompt = "Твоя компания продает автомашины. Ты аналитик, ты оцениваешь работу менеджеров отдела продаж. Оцени разговор менеджера с клиентом. Основной задачей менеджера является убедить клиента посетить автосалон.";

const jsonPrompt = 
'Дай следующие результаты анализа:\n' +
'1. Тема разговора (результат в поле topic);\n' +
'2. Суммаризируй разговор (результат в поле summary);\n' +
'3. Подведи итоги разговора (результат в поле result);\n' + 
'4. Поставь оценку менеджеру (от 0 до 5), пытался ли менеджер выполнить свою основную задачу (результат в поле rate);\n' + 
'5. Сообщал ли менеджер клиенту, что какого-то товара нет в наличии: да (out), нет (nope) - (результат в поле stock);\n' +
'6. Оцени эмоции в разговоре: негативно (negative), нейтрально (neutral), позитивно (positive) - (результат в поле emotional);';

Соединяем через точку оба промпта, получаем системный промпт:

const systemPrompt = mainPrompt + '. ' + jsonPrompt;
Скриншот раздела с промтами

Шаг 2. Собрать данные по диалогу

Во второй промпт, который user, собираем простынь переписки. Автор реплики, и через двоеточие сама реплика. Следующая реплика через новую строку. Так как в системном промпте используются «Менеджер» и «Клиент», то соответственно в переписке тоже используем «Менеджер» и «Клиент», а не «сотрудник», «покупатель», или как то еще.

const userPrompt = 
'Клиент: здравствуйте, меня интересует автомашина;\n' + 
'Менеджер: да, есть автомашины, но в наличии осталось мало. Не хотите ли приехать в автосалон?;\n' + ...

Шаг 3. Отправляем запрос в YandexGPT

У YandexGPT в API есть несколько методов для работы с LLM, самый дешевый это асинхронный. Цены зависят от токенов. Если не в токенах, а в понятных единицах измерения, то оценка одного диалога (примерно 2-3 минуты разговор) будет стоить около 4-5 тенге за диалог, это порядка 1 руб за диалог. Может чуть больше, не считал эти токены.

Запрос отправляем на «штатную» LLM. Есть еще вариант дообучить модель, и работать со «своей» LLM, но для общей оценки диалога, достаточно «штатной» LLM.

Соответственно url для асинхронного запроса:

const urlYandex = 'https://llm.api.cloud.yandex.net/foundationModels/v1/completionAsync';

Для запроса на API еще нужен folderId, это ID папки в консоли Яндекс. Так же в папке должен быть сервисный экаунт с ролью ai.languageModels.user, и для этого экаунта создать API-ключ.

Тело запроса:

const requestBody = {
    "modelUri": "gpt://" + folderId + "/yandexgpt/latest", // "Это если испольуем общую LLM
    // "modelUri": "ds://" + modelId, // Это если используем дообученную "свою" LLM
    "completionOptions": {
      "stream": false,
      "temperature": 0.3,
      // "maxTokens": "20"
    },
    "messages": [
        {
            "role": "system",
            "text": systemPrompt,
        },
        {
            "role": "user", 
            "text": userPrompt
        }
    ],
    "jsonObject": true,
    "jsonSchema": {
        "schema": {
            "type": "object",
            "properties": {
                "topic": {
                    "type": "string"
                },
                "summary": {
                    "type": "string"
                },
                "result": {
                    "type": "string"
                },
                "rate": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 5,
                },
                "stock": {
                    "type": "string",
                    "enum": [ "out", "nope" ]
                },
                "emotional": {
                    "type": "string",
                     "enum": [ "negative", "neutral", "positive" ]
                }
            },
            "required": [ "topic", "summary", "result", "rate", "stock", "emotional" ] // Если не указать required, то LLM может игнорировать поля
        }
    }
}

Выше, в системном промпте описано, как результаты оценки разложить в JSON. Соответственно в запрос вкладываем эту же самую JSON схему, и указываем, что поля обязательные (required). Если не указать required, то LLM может игнорировать поля.

При отправке запроса заголовок x-client-request-id можно не присваивать, он не влияет ни на что. Пустой заголовок x-data-logging-enabled означает, что я не хочу, чтобы Яндекс использовал мои данные для обучения своих моделей, то есть я не хочу, чтобы Яндекс хранил у себя мои данные.

По результатам запроса API должен вернуть resuestId, если тело корректное. Если некорректное, то в ответе не будет data.id.

    let result = {
      success: false,
      state: 'dummy',
      requestId: 'no',
    }

    axios.defaults.headers.common['Authorization'] = 'Api-Key ' + gptKeyAPI;
    axios.defaults.headers.common['x-client-request-id'] = 'snt_' + UtilRandomNum( 10000000, 90000000 );
    axios.defaults.headers.common['x-data-logging-enabled'] = '';

    try {
      const res = await axios.post(urlYandex, requestBody);
      result.requestId = res.data.id;
      result.state = 'gpt';
      result.success = true;
      return result;
    } catch (e: any) {
      result.success = true;
      result.state = 'bad';
      return result;
    }

Полученный requestId сохраняем.

Шаг 4. Получаем результаты YandexGPT

Далее через пару минут запрашиваем результаты, с url:

axios.defaults.headers.common['Authorization'] = 'Api-Key ' + gptKeyAPI;

const url = 'https://operation.api.cloud.yandex.net/operations/' + requestId;

Если в ответе есть поле done, и в нем значение true, то результаты готовы. Если не результаты не готовы, то через минуту повторяем запрос на результаты.

Парсим результаты:

const responseString = response.data.response.alternatives[ 0 ].message.text;

const responseData: any = JSON.parse( responseString );

В responseData будет JSON схема с ответом от YandexGPT.

Далее сохраняем, и показываем результаты довольным пользователям в карточке звонка на вкладке «GPT суммаризация».

Скриншот вкладки с суммаризацией

В целом оценка работает хорошо. Пишите звоните, переходите на Демо стенд.

Комментарии (0)