-
Notifications
You must be signed in to change notification settings - Fork 40
Создание функционального модуля
Перед тем как приступать к созданию своего функционального модуля, внимательно ознакомьтесь с разделом Как создать модуль.
Это модуль, который понимает определенные голосовые команды пользователя и выполняет их.
Такой модуль может выполнять любые действия, отвечать пользователю голосом и переключать диалоговый контекст.
Кроем самого модуля вам необходимо написать грамматику запросов для вашего модуля. Чтобы Агрегат понимал, какие команды относятся именно к вашему модулю и передавал ему управление каждый раз, когда фраза пользователя в текущем контексте подходит под одну из указанных вами команд. Файл грамматики содержит шаблоны команд и их контексты. Этот файл должен иметь имя в формате <имя модуля>.xml и находиться в корне архива модуля.
Подробнее о грамматике и синтаксисе читайте здесь.
Для создания функционального модуля может пригодиться библиотека API Агрегата. Это необязательно, но она содержит абстракции, которые могут упростить написание модуля, чтобы вам не пришлось работать с данными на "низком" уровне.
Для Java можно использовать API для Java.
Функциональный модуль получает управление от Агрегата всякий раз, когда пользователь в текущем контексте произнес фразу, подходящую под одну из команд вашего модуля. Агрегат выбирает лучшее соответствие, если таких модулей несколько, и передает управление только одному из них.
В файле грамматики запросов каждая команда имеет идентификатор. Этот идентификатор является именем события, которое должен слушать модуль, чтобы получить управление от Агрегата, когда сработала эта команда.
Если в файле грамматики написано следующее:
<context>
<command id="cmd.datetime.time">
<pattern value="* который * час *"/>
<pattern value="* скольк* * врем* *"/>
<pattern value="время"/>
</command>
</context>Это значит, что Агрегат сгенерирует событие с названием cmd.datetime.time в тот момент, когда пользователь произнесет одну из фраз типа "Который сейчас час" или "Скажи сколько времени". Слушая такое событие, модуль получит управление, когда оно произойдет. Вместе с событием Агрегат передаст и сообщение, которое будет содержать дополнительную информацию о пользовательском запросе.
Агрегат не просто генерирует событие, он также "раскладывает" фразу пользователя на составляющие, соответствующие грамматике. То есть он строит некий аналог семантического дерева запроса и передает его модулю вместе с событием в сообщении.
В предыдущем примере у нас используется грамматика, в которой нет никаких составляющих в каждом из шаблонов фраз. Поэтому Агрегат в сообщении передаст данные, содержащие только исходную строку запроса пользователя.
Посмотрим на другой пример:
<command id="cmd.datetime.date">
<pattern value="[* как*] {(день [недели] | дата) [* $Date]}"/>
</command>В этой грамматике пользователь может сказать фразы типа "Какой день недели" или "Какой день недели будет через два дня". Составляющей здесь будет та дата, которую может указать пользователь. Агрегат добавит ее в запрос к вашему модулю, предварительно конвертировав дату в день, месяц и год для удобства обработки.
Вот как может выглядеть код обработчика такого события в модуле на Java:
@Override
public void start() throws Exception {
vertx.eventBus().consumer("cmd.datetime.date", m -> {
m.reply(getDate(Request.fromMessage(m)));
});
}
private String getDate(Request request) {
Markup date = request.markup.get(Pattern.DATE);
Date d;
if (date != null) {
Calendar calendar = GregorianCalendar.getInstance();
calendar.set(Calendar.DATE, (Integer) date.data.get("day"));
calendar.set(Calendar.MONTH, (Integer) date.data.get("month"));
calendar.set(Calendar.YEAR, (Integer) date.data.get("year"));
d = calendar.getTime();
} else {
d = new Date();
}
return new SimpleDateFormat("dd MMMM, EEEE").format(d);
}Как видно из этого простого кода, модуль реагирует на событие cmd.datetime.date и синхронно возвращает в ответе дату (он мог бы сделать это и асинхронно, но в данном случае операция простая и быстрая). Классы Request, Pattern и Markup - это абстракции API для Java, которые упрощают работу с данными запроса.
Модуль в методе getDate вытаскивает из запроса ту его часть, которая относится к дате. И если она есть, то получает из нее нужные составляющие (день, месяц, год), которые уже подготовил Агрегат, когда увидел, что это дата в запросе пользователя. А далее модуль просто сгенерировал дату в нужном формате и вернул ее в ответ на событие. Если модуль просто возвращает строку текста, то Агрегат просто ее озвучивает, посылая событие одному из синтезаторов. Этим данный модуль и пользуется.
Полный код данного модуля можно найти в исходниках здесь.
Как видно из примера, в модуле есть метод start, который вызывается Агрегатом ровно один раз, когда модуль стартует. Подробнее о нем читайте здесь.
В этом методе удобно подписаться на те события, которые указаны в вашем файле грамматики пользовательских запросов. В остальном код главного файла функционального модуля ничем не отличается от кода любого другого модуля.
Вы можете видеть в примере, что в коде нашего модуля мы отвечаем на запрос Агрегата синхронно - то есть сразу отвечаем на событие с помощью метода reply.
Это самый простой способ ответить на пользовательский запрос. Метод reply принимает или строку текста, или более сложный объект в формате ответа (см ниже).
Есть и асинхронный способ ответа на запрос. Например, если ваш модуль должен проделать какую-то более длительную операцию перед тем, как сформировать ответ. Чтобы использовать его, просто пошлите строку или объект в формате ответа в виде сообщения события response. Например для Java:
vertx.eventBus().publish("response", new Response("Мой ответ"));В случае с Javascript и другими языками нужно просто послать JSON сообщение в ответ:
vertx.eventBus().publish("response", {"text" : "Мой ответ"});Используйте именно publish для этого события, так как любой другой модуль может слушать это событие тоже
Конечно, объект Response может быть гораздо полезнее. Читайте о нем ниже.
Нет, ваш модуль может вообще не посылать ответ. Например, модуль Браузер ничего не отвечает, а просто открывает ссылку в браузере.
Если ваш модуль отвечает строкой, то эта строка воспринимается Агрегатом как текст, который нужно синтезировать голосом. Если строка начинается с http:// или https://, то Агрегат не будет ее синтезировать, а просто попытается открыть в браузере.
Конечно же, вы можете отправлять и более сложные ответы. В нем можно указать несколько текстов для синтеза речи, а также указать контекст, в который вы хотите переключить Агрегат, и является ли он модальным. Например:
vertx.eventBus().publish("response", new Response(speeches, "com.aggregate.dusi", true));В случае с Javascript и другими языками нужно просто послать JSON сообщение в ответ:
vertx.eventBus().publish("response", {text: "Мой ответ", context: "cmd.some.context", modal: true});В этом примере ответ переключит Агрегат в контекст com.aggregate.dusi из нашего файла грамматики и сделает его модальным (то есть никакой другой контекст не сможет перехватить следующий запрос).
Подробнее о контекстах и модальности читайте здесь.
Агрегат все равно пришлет вашему модулю событие. Но имя события будет соответствовать не идентификатору команды, а идентификатору модуля (контекста) из файла грамматики. В запросе не будет представлен объект markup, так как диалог не сработал.
При этом модальный контекст будет отключен, и чтобы снова в него войти, вашему модулю нужно будет снова отправить ответ с указанием идентификатора контекста из файла грамматики.
- Разработка в Eclipse
- Разработка в IntelliJ IDEA
- List of available modules
-
Where to get
-
How to install
-
How to use
-
Setting up
- Java
- Javascript
- Typescript
- Groovy
- Ruby
-
Query grammar
-
Pattern syntax
-
Dynamic patterns
-
Standard patterns
- Developing in Eclipse
- Developing in IntelliJ IDEA