Skip to content

Создание функционального модуля

Dmitry Che edited this page Apr 4, 2016 · 8 revisions

Перед началом

Перед тем как приступать к созданию своего функционального модуля, внимательно ознакомьтесь с разделом Как создать модуль.

Что такое функциональный модуль

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

Что нужно для его создания

Кроем самого модуля вам необходимо написать грамматику запросов для вашего модуля. Чтобы Агрегат понимал, какие команды относятся именно к вашему модулю и передавал ему управление каждый раз, когда фраза пользователя в текущем контексте подходит под одну из указанных вами команд. Файл грамматики содержит шаблоны команд и их контексты. Этот файл должен иметь имя в формате <имя модуля>.xml и находиться в корне архива модуля.

Подробнее о грамматике и синтаксисе читайте здесь.

API

Для создания функционального модуля может пригодиться библиотека 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, так как диалог не сработал.
При этом модальный контекст будет отключен, и чтобы снова в него войти, вашему модулю нужно будет снова отправить ответ с указанием идентификатора контекста из файла грамматики.

Russian Version (Русская версия)





IDE


English Version

Main Page

Modules

  • List of available modules

User Guide

  • Where to get

  • How to install

  • How to use

  • Setting up


Developer Guide

Aggregate Architecture

Events List

Command Line


How to Create a Module

  • Java
  • Javascript
  • Typescript
  • Groovy
  • Ruby

Functional Modules

  • Query grammar

  • Pattern syntax

  • Dynamic patterns

  • Standard patterns

Activation Modules

Speech Recognition Modules

Text-to-Speech Modules


IDEs

  • Developing in Eclipse
  • Developing in IntelliJ IDEA

Change Log

Clone this wiki locally