Обработка естественного языка (NLP)
Чтобы с навыком можно было общаться на естественном языке, он должен уметь корректно обрабатывать реплики пользователей:
- распознавать задачу, которую хочет решить пользователь (например, заказать такси),
- извлекать нужные детали (на какое число и по какому адресу).
Для упрощения задач NLP (Natural Language Processing) Диалоги предоставляют специальный инструмент — встроенный язык описания пользовательского запроса. С его помощью в консоли разработчика вы можете описать правила, по которым Диалоги будут классифицировать запросы и извлекать из них нужные данные.
Когда пользователь произносит команду, Диалоги распознают текст и извлекают те фразы, которые описывают намерения пользователя согласно вашим правилам. Распознанные данные Диалоги присылают в навык.
Что такое интент, форма и слоты
Чтобы формализовать разбор реплик, Диалоги используют интенты, формы и слоты.
Интент — это задача, которую пользователь формулирует в конкретной реплике.
Например, узнать погоду
. Каждому интенту соответствует одна форма.
Форма — контейнер с информацией, который Диалоги заполняют, распознавая запрос пользователя. Форма всегда соответствует одному интенту и содержит набор типизированных слотов.
Слот — поле формы. Каждый слот имеет название, тип данных и признак обязательности.
Например, из реплики погода на завтра в Питере
для заполнения слотов будут
извлечены дата (завтра
) и место (в Питере
).
При обработке реплики Диалоги сначала определяют, к какому интенту она относится. После
этого извлекают из реплики необходимые параметры и заполняют ими слоты формы. Распознанные
данные Диалоги отправят в навык в поле запроса request.nlu
. Если реплика не
относится ни к одному интенту, поле request.nlu
будет пустым (подробнее).
Синтаксис
Ниже показан пример описания интента:
# Описание интента "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
Внимание
Отрицательные правила должны быть более конкретными, чем положительные. Например, если в обозначении корневого элемента root
указать элемент включи сказку .*
, а в %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": "на кухне"
}
}
}
}
}
},
...
}