В нашем предыдущем посте, Twilio для телефонных деревьев, мы показали вам, как создать телефонное дерево с использованием Twilio, Ruby и Sinatra. В этом посте мы применим один из самых мощных инструментов веб-разработчиков — мультивариативное тестирование — к любимому средству связи многих компаний: телефонному дереву.
Мультивариативное тестирование позволит нам ответить на множество различных вопросов о нашем телефонном дереве:
— Насколько понятны варианты в телефонном дереве?
— Сколько абонентов завершают звонок, используя опции самообслуживания?
— Есть ли шаги в телефонном дереве, которые мы могли бы улучшить?
— Сколько шагов в среднем проходит пользователь?
— Улучшаем ли мы пользовательский опыт с каждым новым релизом?
Идея мультивариативного тестирования проста: вы начинаете с гипотезы о том, что изменение одного аспекта вашего веб-сайта улучшит метрику, определяющую успех. Например, предположим, у нас есть веб-сайт, продающий игрушечных сов. В настоящее время на сайте есть изображение сипухи, но наша гипотеза состоит в том, что полярная сова приведет к большему количеству продаж.
Чтобы проверить нашу гипотезу, мы покажем половине наших пользователей полярную сову, а другой половине — сипуху. Затем для каждой продажи мы записываем тип отображаемой совы. Как только у нас будет достаточно данных за несколько недель, мы можем составить простое сравнение:
Из этого мы видим, что полярная сова приводит почти в два раза большему количеству продаж, чем сипуха. Наша гипотеза подтверждена измеримыми фактами. Мы должны заменить сипуху на полярную сову. Мы используем научный метод для улучшения нашего продукта или услуги.
Как мы можем использовать мультивариативное тестирование, чтобы улучшить нашу систему IVR?
Что, если бы мы могли иметь два телефонных дерева и случайным образом распределять клиентов между ними? Мы могли бы проводить всевозможные интересные эксперименты с помощью мультивариативных тестов:
— Сколько клиентов полностью завершают звонок в режиме самообслуживания?
— Сколько клиентов соединяются с оператором?
— Какова продолжительность звонков?
— Каково настроение абонента при разговоре с оператором?
На самом деле нам не нужны две полноценные телефонные системы, мы можем использовать одно телефонное дерево и адаптировать его для использования разного контента в определенных частях системы. Вместо того чтобы создавать новую систему IVR с нуля, мы можем взять код из моего предыдущего поста Twilio для телефонных деревьев. В том посте мы описали, как создать простую систему IVR на Ruby и Sinatra. Код из того поста предоставляет хорошую основу для модификации, чтобы использовать различные варианты каждого шага в меню, что позволит нам научно протестировать нашу гипотезу.
Шаг в меню, который мы хотим протестировать, позволяет пользователю запросить конкретную информацию. Ваше телефонное меню может позволить абонентам «услышать баланс своего счета», а мое телефонное меню позволяет им услышать «сколько у нас сов». Моя гипотеза заключается в том, что следующий вариант меню не очень понятен:
Вам нужно услышать, сколько у нас стригиформных, 1.
Я считаю, что следующий текст гораздо понятнее:
Чтобы услышать, сколько у нас сов, нажмите 1.
Чтобы начать, нам нужен способ хранения вариантов. Мой предпочтительный подход состоит в том, чтобы отделить контент от структуры дерева. Давайте начнем с изменения класса Step, который мы создали в прошлый раз. Ранее мы использовали этот класс для генерации как структуры, так и контента нашей системы IVR. Чтобы мы могли легко проводить A/B-тестирование контента, мы создадим новый класс под названием Content.
Теперь нам нужно удалить из класса Step строку, содержащую «контент» каждого шага, и добавить ссылку на несколько записей Content.
Вместо хранения произносимого текста для каждого шага, мы просто свяжем один или несколько объектов Content с объектом Step.
Метод ‘create_tree’ класса Step теперь можно упростить. Сначала мы создаем объекты Step, которые определяют структуру нашей системы IVR:
Наконец, мы можем добавить вариант одного из этих объектов контента для использования в нашем эксперименте. В этом случае мы протестируем разный текст для варианта 1:
Теперь у нас есть полное дерево с полным контентом. Один из шагов имеет 2 варианта контента. Мы могли бы использовать тот же подход для изменения голоса или используемого языка. Мы также могли бы добавить вариант в класс Step, чтобы позволить нам легко проводить эксперименты над структурой IVR. Пока давайте поработаем только над контентом.
Ранее мы использовали класс Step для генерации TwiML. Чтобы наш код оставался чистым, мы будем использовать класс Content для генерации ‘
В этом методе параметр `parent` — это родительский XML-элемент в ответе TwiML. Прежде чем мы изменим существующую версию to_twiml в классе Step, нам нужен способ получения правильной записи Content.
Цель этого метода — получить правильную запись Content на основе указанного варианта. Если для этого варианта нет контента, мы используем вариант по умолчанию. В результате мы можем запустить несколько экспериментов одновременно, которые затрагивают лишь крошечный аспект дерева.
Нам нужно использовать новый метод `get_content` класса Step и метод `to_twiml` класса Content для рендеринга TwiML. Это по-прежнему обрабатывается классом Step:
Здесь есть три основных изменения. Мы используем метод `get_content` для поиска записи `Content`, а затем вызываем метод `to_twiml` возвращаемого им экземпляра. Тонкое изменение атрибута ‘action’ в глаголе `
Поскольку мы закончили с классами Step и Content, нам нужно внести несколько изменений в действия в нашем `call_handler.rb`. Нам нужна глобальная переменная, которая содержит все настроенные нами варианты. Было бы гораздо разумнее хранить это в базе данных, но для этого простого примера вполне подойдет переменная с жестко заданным значением.
Далее нам нужно изменить действие по умолчанию ‘/step’ для новых вызовов, чтобы использовать вариант. Мы случайным образом выбираем вариант с помощью Array.sample. Вы можете не захотеть разделения 50/50, поэтому используйте любой алгоритм распределения вариантов, который вам больше нравится. Затем нам нужно передать это в объект Metric, который мы создаем для вызова, и в объект Step.
Теперь нам нужно изменить второе действие, которое обрабатывает последующие шаги. Мы изменим URL для ответа по ID шага и Варианту. Мы изменим объект Metric, чтобы он обновлял запись о вызове, а не регистрировал каждый шаг. Наконец, мы передаем вариант (на этот раз из URL) в класс Step для корректного отображения контента.
Нам нужно внести несколько простых изменений в класс Metric. Ранее мы использовали его для отслеживания каждого шага вызова, но теперь мы используем его для отслеживания результата каждого вызова. Это означает, что нам нужно обновлять записи, а не создавать новые. Мы уже изменили выше действие ‘/step/:id/:variant’, поэтому теперь нам нужно изменить действие ‘/fin’. Оно используется для обновления состояния вызова после его завершения с помощью Twilio Status Callback.
Нам нужно изменить только одну строку в классе Metric. Нам нужно добавить новое свойство под названием ‘variant’.
И…
…мы закончили! Изменений довольно много, поэтому я закоммитил их все в новый репозиторий GitHub. Вы можете посмотреть diff всех изменений здесь.
Если вы хотите попробовать рабочую версию этого приложения, вы можете позвонить и прослушать его по одному из номеров ниже:
+1 312-234-0386
+44 333 344 1070
Вы можете легко увидеть, какой из двух вариантов наиболее успешен, на графике ниже.
Вот так мы можем легко использовать Twilio для применения мультивариативного тестирования к нашим телефонным системам. Мы можем использовать это для постоянного внесения улучшений и изменений в наши телефонные меню, а также для предоставления все более качественного обслуживания клиентов. Отлично!