Перейти к основному содержимому

Разработка сценария

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

Создание сценария

Создайте файл main.sc в папке src. В нем будет сценарий работы бота:

require: slotfilling/slotFilling.sc
module = sys.zb-common

require: common.js
module = sys.zb-common

theme: /

state: Правила
q!: $regex</start>
intent!: /Давай поиграем
a: Игра больше-меньше. Загадаю число от 0 до 100, ты будешь отгадывать. Начнём?
go!: /Правила/Согласен?

state: Согласен?

state: Да
intent: /Согласие
go!: /Игра

state: Нет
intent: /Несогласие
a: Ну и ладно! Если передумаешь — скажи "давай поиграем"

state: Игра
# сгенерируем случайное число и перейдем в стейт /Проверка
script:
$session.number = $jsapi.random(100) + 1;
# $reactions.answer("Загадано {{$session.number}}");
$reactions.transition("/Проверка");

state: Проверка
intent: /Число
script:
# сохраняем введенное пользователем число
var num = $parseTree._Number;

# проверяем угадал ли пользователь загаданное число и выводим соответствующую реакцию
if (num == $session.number) {
$reactions.answer("Ты выиграл! Хочешь еще раз?");
$reactions.transition("/Правила/Согласен?");
}
else
if (num < $session.number)
$reactions.answer(selectRandomArg(["Мое число больше!", "Бери выше", "Попробуй число больше"]));
else $reactions.answer(selectRandomArg(["Мое число меньше!", "Подсказка: число меньше", "Дам тебе еще одну попытку! Мое число меньше."]));

state: NoMatch || noContext = true
event!: noMatch
random:
a: Я не понял.
a: Что вы имеете в виду?
a: Ничего не пойму

В начале сценария под тегом require подключаем дополнительные модули slotFilling и common.js. Их использование мы рассмотрим в разделе Структура сценария.

Сценарий бота состоит из следующих основных стейтов:

  • Правила — начало работы. Бот приветствует пользователя, предлагает сыграть в игру и объясняет правила.
  • Игра — начало игры. Здесь бот загадывает случайное число.
  • Проверка — стейт содержит логику проверки введенного пользователем числа и вывода сообщения в зависимости от результата проверки.
  • NoMatch — стейт, предусмотренный для случаев, когда сообщение пользователя не подходит ни под один установленный стейт.

Структура сценария

Правила

В стейте Правила запускается сценарий. Бот посылает приветственное сообщение и предлагает сыграть в игру.

state: Правила
q!: $regex</start>
intent!: /Давай поиграем
a: Игра больше-меньше. Загадаю число от 0 до 100, ты будешь отгадывать. Начнём?
go!: /Правила/Согласен?

state: Согласен?

state: Да
intent: /Согласие
go!: /Игра

state: Нет
intent: /Несогласие
a: Ну и ладно! Если передумаешь — скажи "давай поиграем"

Предусмотрим переход в стейт Правила из любого другого стейта по фразе давай поиграем. Для этого создадим интент /Давай поиграем.

Перейдите на вкладку NLU > Интенты, расположенную в боковом меню. Создайте интент и добавьте в поле Тренировочные фразы следующие фразы: хочу играть, давай поиграем, играть.

Интент Давай поиграем

Далее с помощью тега go! осуществляется переход во вложенный стейт Согласен?. Затем в зависимости от ответа пользователя бот переходит в следующие стейты /Согласие или Несогласие.

Несогласие

Отрицательный ответ обрабатывается в интенте /Несогласие. Создайте интент и добавьте в поле Тренировочные фразы фразы, несущие отрицательную окраску. Например: не буду, не хочу, нет. Тогда, если бот получит одно из таких сообщений от пользователя, сработает интент /Несогласие.

Интент Несогласие

Дополните тренировочные фразы своими вариантами. Чем больше вариаций фраз, тем больше вероятность того, что ваш бот сможет правильно среагировать на сообщения пользователя.

Согласие

Если ответ пользователя оказался положительным, выполняется переход в стейт Да. Создайте интент /Согласие и добавьте как можно больше тренировочных фраз, выражающих согласие. Например: да, согласен, начнем, хочу. Теперь, если бот получит одно из таких сообщений от пользователя, сработает интент /Согласие.

Интент Согласие

Число

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

Настроим параметры слота. Каждый слот имеет поле Сущность. Оно определяет тип данных, которые попадут в слот. Будем использовать системную сущность @duckling.number с помощью сервиса Duckling.

Чтобы подключить системную сущность Duckling, перейдите на вкладку NLU > Сущности > Системные, расположенную в боковом меню. Найдите в списке доступных сущностей @duckling.number и переведите переключатель в активное положение.

Системная сущность Duckling

Вернитесь к настройке интента /Согласие. В поле Сущность установите @duckling.number. Переведите переключатель Обязательно в активное положение и заполните поле Вопросы уточняющими вопросами. Например, Я загадал число. Твоя догадка?, Предположи число.

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

Заполнение слотов

Заполненный слот будет передан в переменную $parseTree._Number в стейт Проверка.

подсказка
Подробнее о настройке слот-филлинга

Игра

По правилам бот случайно загадывает число. Для этого напишем скрипт, который сначала загадает случайное число, а затем вызовет переход в следующий стейт Проверка.

state: Игра
script:
# сгенерируем случайное число
$session.number = $jsapi.random(100) + 1;

# перейдем в стейт /Проверка
$reactions.transition("/Проверка");
Случайный выбор числа

Как уже было сказано ранее, по правилам бот случайно загадывает число от 0 до 100. Для этого воспользуемся генератором случайных чисел с помощью встроенного сервиса $jsapi.random(). Метод возвращает целочисленные значения от 0 до max(не включая max), поэтому в качестве параметра укажем 100 и прибавим к получившемуся случайному значению 1. Запишем в тег script команду:

$session.number = $jsapi.random(100) + 1;

В JS API переменная $session является объектом для сохранения сессионных данных. Присвоим переменной $session.number функцию случайного выбора числа. В дальнейшем будем сравнивать число, хранящееся в $session.number, с числом, введенным пользователем.

Чтобы убедиться, что бот действительно загадывает числа в случайном порядке, выведем загаданное число на экран в виде сообщения Загадано *число*. Вставьте в скрипт следующую строку:

$reactions.answer("Загадано {{$session.number}}");
Переходы по стейтам

Переход в другой стейт осуществляется посредством вызова метода $reactions.transition. В параметре указывается путь к стейту /Проверка.

$reactions.transition("/Проверка");
подсказка
Подробнее ознакомьтесь с использованием встроенных сервисов $jsapiи видах переменных в JS API

Проверка

Настройка интента

Перейдите на вкладку NLU > Интенты, расположенную в боковом меню. Создайте интент /Число и добавьте в поле Тренировочные фразы фразу @duckling.number, чтобы бот мог распознать введенное пользователем число и сохранить его.

Интент число

Выполнение условия
    state: Проверка
intent: /Число
script:
# сохраняем введенное пользователем число
var num = $parseTree._Number;

# если пользователь угадал число
if (num == $session.number) {
$reactions.answer("Ты выиграл! Хочешь еще раз?");
$reactions.transition("/Правила/Согласен?");
}
else
# если число пользователя меньше загаданного
if (num < $session.number)
$reactions.answer(selectRandomArg(["Мое число больше!", "Бери выше", "Попробуй число больше"]));

# если число пользователя больше загаданного
else $reactions.answer(selectRandomArg(["Мое число меньше!", "Подсказка: число меньше", "Дам тебе еще одну попытку! Мое число меньше."]));

Переменная $parseTree._Number представляет собой число, которое ввел пользователь. Создадим отдельную переменную num. Присвоим ей значение, полученное в стейте Согласие из слота Number.

Далее с помощью условного оператора if/else сравниваем загаданное значение с введенным. Если числа равны, то бот выдаст сообщение Ты выиграл! Хочешь еще раз? и совершит переход в стейт /Правила/Согласен?.

Подсказки

Если введенное число меньше, то бот посредством функции $reactions.answer() выдаст соответствующую фразу из массива selectRandomArg(). Например: Попробуй число больше.

Функция selectRandomArg() предназначена для случайного выбора ответов из предложенных вариантов. Чтобы воспользоваться этой функцией, подключите JS-файлы common.js посредством require в начале файла main.sc.

require: common.js
module = sys.zb-common

Затем перечислите в selectRandomArg() через запятую возможные варианты ответов. Например:

$reactions.answer(selectRandomArg("Мое число больше!", "Бери выше", "Попробуй число побольше"));

Если введенное число не подошло под первые два условия, то сработает последний else и бот напишет случайно одно из сообщений массива.

Таким образом, пользователь будет находиться в стейте Проверка до тех пор, пока не отгадает загаданное число.

NoMatch

Управление контекстом

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

Предположим, что пользователь ввел следующее сообщение Сегодня солнечно. Такое сообщение не попадет ни под один интент, поэтому активируется событие noMatch, указанное под глобальным тегом event! в стейте NoMatch.

state: NoMatch || noContext = true
event!: noMatch
random:
a: Я не понял.
a: Что вы имеете в виду?
a: Ничего не пойму

Если мы попадем в этот стейт, например, из стейта Правила, и пользователь затем введет сообщение хочу, то бот отправит сообщение я вас не понял. Несмотря на соответствие введенного сообщения интенту Согласие, запрос не будет распознан, потому что контекст диалога изменился, и вложенный стейт Согласен? не доступен.

Для того чтобы контекст не изменился при попадании в NoMatch установим в стейте флаг noContext = true. Теперь при попадании в стейт NoMatch контекст не изменится, сообщение хочу будет обработано в контексте стейта Согласен? и попадет во вложенный стейт Да.

Случайные реакции

В языке DSL есть тег random, предназначенный для случайного выполнения одной из вложенных реакций.

Разнообразим ответы бота в случае, когда пользователь ввел сообщение, отличающееся от всех описанных вариантов. Для этого в стейте NoMatch под тегом random перечислите подходящие варианты ответов:

random:
a: Я не понял.
a: Что вы имеете в виду?
a: Ничего не пойму

В результате перехода в стейт NoMatch будет случайным образом выведено только одно сообщение из предложенных.

Теперь перейдем к тестированию разработанного сценария.