Обработка естественного языка (NLP)

Внимание. Обратите внимание: эта возможность работает в тестовом режиме и сейчас активно развивается, поэтому протокол ее использования может меняться.
  1. Что такое интент, форма и слоты
  2. Как создать правила для обработки реплик
  3. Синтаксис
    1. Типизированные слоты
    2. Пользовательские сущности в слотах
    3. Директивы
    4. Оператор []
    5. Квантификаторы
  4. Встроенные интенты
  5. Какие данные передаются в навык

Чтобы с навыком можно было общаться на естественном языке, он должен уметь корректно обрабатывать реплики пользователей — распознавать задачу, которую хочет решить пользователь (например, заказать такси), а также извлекать нужные детали (на какое число и по какому адресу).

Для упрощения задач NLP (Natural Language Processing) Диалоги предоставляют специальный инструмент — встроенный язык описания пользовательского запроса. С его помощью вы можете описать правила, по которым Диалоги будут классифицировать запросы и извлекать из них нужные данные, в консоли разработчика.

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

Что такое интент, форма и слоты

Чтобы формализовать разбор реплик, Диалоги используют интенты, формы и слоты.

Интент — это задача, которую пользователь формулирует в конкретной реплике. Например, узнать погоду. Каждому интенту соответствует одна форма.

Форма — контейнер с информацией, который Диалоги заполняют, распознавая запрос пользователя. Форма всегда соответствует одному интенту и содержит набор типизированных слотов.

Слот — поле формы. Каждый слот имеет название, тип данных и признак обязательности. Например, из реплики погода на завтра в Питере для заполнения слотов будут извлечены дата (завтра) и место (в Питере).

При обработке реплики Диалоги сначала определяют, к какому интенту она относится. После этого извлекают из реплики необходимые параметры и заполняют ими слоты формы. Распознанные данные Диалоги отправят в навык в поле запроса request.nlu. Если реплика не относится ни к одному интенту, поле request.nlu будет пустым (подробнее).

Как создать правила для обработки реплик

  1. Откройте консоль разработчика. На странице навыка Алисы перейдите во вкладку Настройки и выберите раздел Интенты.
  2. Создайте форму интента. Эта форма будет однозначно определять интент — содержать его название и грамматику, а также указывать набор слотов.
  3. Зайдите в сохраненную форму. Заполните поля:
    • Название — человекочитаемый текст, который будет отображаться во вкладке NLU в списке форм.
    • ID — идентификатор интента. Будет указан в JSON, который Диалоги отправят навыку.
    • Грамматика — описание интента согласно синтаксису.
  4. Протестируйте грамматику. Для этого укажите одну или несколько тестовых фраз в полях Положительные тесты или Отрицательные тесты. Для положительных тестов будет проверяться, что фраза классифицируется текущим интентом. Для отрицательных, наоборот. Далее, нажмите кнопку Протестировать. Результат отобразится в поле Результаты тестирования, либо, если указана некорректная грамматика, ошибка подсветится в редакторе.
  5. Если грамматика срабатывает корректно, нажмите Сохранить. После этого на вкладке Тестирование Диалоги начнут отправлять данные в навык согласно протоколу.
  6. Когда вы протестируете работу навыка, опубликуйте или переопубликуйте навык для того, чтобы интенты стали доступны пользователям. При изменении настроек интентов навык также нужно будет опубликовать заново.

Синтаксис

Ниже показан пример описания интента:

# Описание интента "turn.on" для включения устройств.
# Эта грамматика позволит распознавать такие фразы как "включи свет на кухне"
# или "включи кондиционер в спальне". 

# Корневой элемент грамматики. Описывает шаблон, по которому будет
# отбираться реплика.
root:
    включи $What $Where

# Описание слотов. Диалоги будут отправлять это описание навыку.
slots:
    what:
        source: $What                   
    where:
        source: $Where
$What:
    свет | кондиционер
$Where:
    в ванной | на кухне | в спальне
Примечание. Вложенные элементы следует обозначать отступом в 4 пробела.
Описание интента состоит из ключевых слов root, slots и filler, а также нетерминалов — фраз на естественном языке, описывающих, на какие запросы должна срабатывать грамматика. Нетерминалы обозначаются символом $. Они эквиваленты переменным в языках программирования. Нетерминалы можно скрыть, чтобы они были доступны только внутри родительского нетерминала.
Пример
$PlayGame:
    $Play в $Game
    $Play:
        %lemma
        играть
    $Game:
        игру

$Game:
    %lemma
    игра

Внутри нетерминала $PlayGame $Game сработает только на слово «игру», а снаружи — на все падежи слова «игра».

Поддерживаемые ключевые слова:

  • root — обозначение корневого элемента. Описывает шаблон, по которому будет отбираться вся реплика целиком.
    Пример
    root:
        [включи $What $Where (и $Where)*]

    В этом примере используются квантификатор и оператор [].

  • slots — описание слотов запроса. Это поле будет присутствовать в JSON, который Диалоги отправят в навык после обработки запроса. Подробнее см. Какие данные передаются в навык.
    Пример
    slots:
        what:
            source: $What                   
        where:
            source: $Where     
    $What:
        свет | кондиционер
    $Where:
        # Подойдет любая строка, которую введет пользователь.
        .+
  • filler — стоп-слова, которые можно отбросить при разборе запроса. Для исключения незначащих, неинформативных слов используется специальный классификатор, использующий контекст предложения. Например, для разбора из примера выше срабаботает как фраза «включи свет», так и «включи свет, пожалуйста».
    Пример
    filler:
        мне | как всегда | еще раз | нужно
Примечание. Порядок описания элементов в грамматике не имеет значения.

Типизированные слоты

Слоты могут содержать не только строковое значение, но и именованные сущности:

  • YANDEX.NUMBER — числа.
  • YANDEX.FIO — имена.
  • YANDEX.DATETIME — даты.
  • YANDEX.GEO — гео-объекты.

Для указания типизированного слота используются поля type и нетерминал, содержащий этот тип:

slots:
    from:
        source: $From
        type: YANDEX.NUMBER
    to:
        source: $To
        type: YANDEX.NUMBER
root:
    назови число от $From до $To
$From:
    $YANDEX.NUMBER
$To:
    $YANDEX.NUMBER

Пользовательские сущности в слотах

Чтобы задать собственные типы слотов, опишите их в разделе Сущности, например:

entity ChessPiece:
    values:
        queen:
            ферзь
            королева
        pawn:
            пешка

После этого тип станет доступен в качестве нетерминала грамматики и типа слота:

slots:
    piece:
        type: ChessPiece
        source: $Piece
root:
    ход $Piece
$Piece:
    $ChessPiece
При указании lemma: true в описании сущности все ее элементы будут сравниваться без учета формы слова.
Пример
entity ChessPiece:
    lemma: true
    values:
        queen:
            ферзь
            королева
        pawn:
            пешка

Сработает на пешка, пешку, пешкой.

Примечание. lemma: true распространяется на всю сущность и не отменяется при помощи директивы %exact

Директивы

Директива — это специальная команда, переключающая парсер запросов в определенный режим работы. Директивы всегда начинаются с символа %. Например:

# Все последующие нетерминалы будут сравниваться без учета формы слова.
# Сработает как "включи свет", так и "включай свет".
root:
    %lemma
    включи свет
Действие директивы распространяется на все последующие нетерминалы. Директиву можно указать в начале всей грамматики, а также непосредственно перед нетерминалом. В последнем случае директива будет действовать до конца отступа или до отменяющей директивы. Пример:
filler:
    # Директива %lemma действует до %exact. Формы слов не учитываются.
    %lemma
    большое спасибо

    # Отменяем действие %lemma. Сработает только "всегда пожалуйста".
    %exact
    всегда пожалуйста

Ниже перечислены поддерживаемые директивы.

%lemma
Нетерминалы будут сравниваться без учета формы слова. Пример (запросы включи свет и включай свет будут засчитаны как совпадение):
root:
    %lemma
    включи свет
%exact
Нетерминалы будут сравниваться по точному совпадению. Пример (под правило попадет только запрос включи свет):
root:
    %exact
    включи свет
%negative
С помощью директивы %negative можно указать отрицательные примеры для элемента. Пример (такая форма сработает для условия включи игру города, и не сработает для включи игру престолов):
form start_game:
    root:
        включи игру .*
        %negative
        включи игру $NotAGame
$NotAGame:
    %lemma
    престол

Директива %positive делает все последующие правила положительными.

Оператор []

Позволяет игнорировать порядок слов в грамматике. Пример:
root:
    [включи свет] 

В этом примере положительными срабатываниями будут включи свет и свет включи.

Квантификаторы

В описании элементов грамматики можно использовать квантификаторы:

  • ? — одно или ноль вхождений;
  • * — ноль или больше вхождений;
  • + — хотя бы одно вхождение.
Пример «включи свет»
root: 
    включи (пожалуйста)? свет

Совпадением будет как включи свет, так и включи, пожалуйста, свет.

Пример «включи свет на кухне»
root:
    включи свет $Where (и $Where)*
$Where:
    .+ # Хотя бы одно произвольное слово.

Совпадениями будут включи свет на кухне, включи свет на кухне и во всем доме и т. д.

Пример «включи свет на кухне и в ванной»
root:
    включи свет $Where (и $Where)+
$Where:
    на кухне | в ванной | в коридоре

Совпадениями будут включи свет на кухне и в ванной, включи свет на кухне и ванной, и в коридоре и т. д., но не просто «включи свет на кухне».

Примечание.

В слот попадет только первый распознанный нетерминал, если в одном интенте он используется несколько раз (только первый $Where из примеров выше попадет в слот).

Встроенные интенты

Если в навыке есть хотя бы один интент, Яндекс.Диалоги дополнительно отправляют интенты, универсальные для большинства навыков:

  • YANDEX.CONFIRM — согласие.
  • YANDEX.REJECT — отказ.
  • YANDEX.HELP — запрос подсказки.
  • YANDEX.REPEAT — просьба повторить последний ответ навыка.

Какие данные передаются в навык

После того как запрос будет обработан, Диалоги отправят навыку распознанные данные — в поле request.nlu. Это поле содержит идентификатор интента, а также описание заполненных слотов. Например:

"request": {
    "command": "включи свет на кухне, пожалуйста",
    "nlu": {
      "intents": {
        "turn.on": { // Интент.
          "slots": {  // Список слотов.
            "what": {
              "type": "YANDEX.STRING", 
              "value": "свет"          
            },
            "where": {
              "type": "YANDEX.STRING",
              "value": "на кухне"
            }
        }
      }
    },
    ...
}

Если реплика не относится ни к одному интенту, поле request.nlu будет пустым.

В случае типизированных слотов в запросе будет указан тип слота и его каноническое значение.

Пример для чисел
"request": {
    "command": "назови число от одного до шести",
    "nlu": {
        "intents": {
            "random_number": { // Интент.
                "slots": {  // Список слотов.
                    "from": {
                        "type": "YANDEX.NUMBER",
                        "value": 1
                    },
                    "to": {
                        "type": "YANDEX.NUMBER",
                        "value": 6
                    }
                }
            }
        }
    },
    ...
}
Пример «Закажи такси»
"request": {
    "command": "закажи такси на льва толстого 16 на 14:00",
    "nlu": {
      "intents": {
        "taxi": {
          "slots": {
            "where": {
              "type": "YANDEX.GEO",
              "tokens": {
                "start": 2,
                "end": 6
              },
              "value": {
                "street": "льва толстого",
                "house_number": "16"
              }
            },
            "time": {
              "type": "YANDEX.DATETIME",
              "tokens": {
                "start": 6,
                "end": 9
              },
              "value": {
                "hour": 14,
                "minute": 0
              }
            }
          }
        }
      }
    },
    ...
}
Пример для пользовательского типа
{
  "request": {
    "command": "включи свет на кухне, пожалуйста",
    "nlu": {
      "intents": {
        "turn.on": {
          "slots": {
            "what": {
              "type": "YANDEX.STRING",
              "value": "свет"
            },
            "where": {
              "type": "YANDEX.STRING",
              "value": "на кухне"
            }
          }
        }
      }
    }
  },
  ...
}