[Java, Scala, API, Apache, Natural Language Processing] Язык определения интентов NlpCraft IDL

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
28-Май-2021 12:30

Данная статья является продолжением заметки “Проектируем интенты с Apache NlpCraft” и содержит детальное описание возможностей языка определения интентов NlpCraft IDL, созданного для использования в NLP проектах основанных на системе Apache NlpCraft. Поддержка NlpCraft IDL добавлена в систему начиная с версии 0.7.5.   Новая версия декларативного языка определения интентов, получившая название NlpCraft IDL (NlpCraft Intents Definition Language), значительно упростила процесс работы с интентами в диалоговых и поисковых системах, построенных на базе проекта Apache NlpCraft и вместе с тем расширила возможности системы.  
NlpCraft IDL - это декларативный язык, позволяющий создавать определения интентов для их последующего использования в моделях Apache NlpCraft.Начнем с примеров, демонстрирующих общие возможности языка, приведем необходимые пояснения, а далее опишем конструкции языка чуть более формально. Напомню, что в точке выбора интента, NLP системы как правило уже имеют на входе обработанный пользовательский запрос, включающий в себя все полученные при разборе запроса комбинации токенов и прочую сопутствующую информацию.Примеры интентов, определенных с помощью NlpCraft IDL
intent=xa
    flow="^(?:login)(^:logout)*$"
    meta={'enabled': true}
    term(a)={!(tok_id()) != "z"}[1,3]
    term(b)={
        meta_intent('enabled') == true &&
        month() == 1
    } 
    term(c)~{
        @tokId = tok_id()
        @usrTypes = meta_model('user_types')
        (tokId == 'order' || tokId == 'order_cancel') &&   
         has_all(@usrTypes, list(1, 2, 3) &&
         abs(meta_tok('order:size')) > 10
        )
   } 
Пояснение:
  • Имя интента - “xa”. 
  • Интент содержит три terms. Term - это элемент, определяющий правило, каждое из которых должно быть выполнено для срабатывания интента.
    • Правило первое (a) - разобранный запрос должен содержать от одного до трех токенов с идентификаторами отличными от “z”,  без учетах данных из истории диалога (тип term =) . 
    • Правило второе (b) - интент должен быть активен в момент срабатывания -  флаг ”enabled” метаданных модели. Кроме того, такой интент может сработать лишь в январе - функция month().
    • Правило третье(c) - в запросе или в истории диалога (тип term ~) должен быть найден токен с идентификатором “order” или “order_cancel”. Также должны быть учтены ограничения, наложенные на значения метаданных модели и абсолютное значение размера ордера. В определении третьего правила используются переменные, о них мы поговорим чуть позже. 
  • Flow. В данном разделе определено дополнительное правило, согласно которому для срабатывания интента необходимо, чтобы в рамках текущей сессии уже хотя бы один раз сработал интент с идентификатором “login”, и ни разу не срабатывал интент с идентификатором “logout”. Правило определено в виде регулярного выражения, основанного на идентификаторах предыдущих интентов пользовательской сессии. Ниже также будут рассмотрены другие способы создания подобных правил.     
  • Meta. Для описываемого интента определен некий набор данных, в данном случае конфигурация, с помощью которой можно включить или выключить интент.
intent=xb
    flow=/#flowModelMethod/
    ordered=true
    term(a)=/org.mypackage.MyClass#termMethod/?
    fragment(frag)
Пояснение к следующему примеру:  
  • Имя интента - “xb”. 
  • Интент может содержать один term (”a”, опционально, согласно квантификатору “?“, детальные пояснения будут приведены ниже), определенный в коде -  org.mypackage.MyClass#termMethod. 
  • Fragment с идентификатором “frag” расширяет список terms интента,  дополнительными, ранее определенными terms. Элемент “frag” должен быть определен выше в коде или доступен с помощью import.
  • Flow содержит условие, заданное в коде модели в методе flowModelMethod.
Больше примеров - здесь.  Разберем элементы языка более детально. Хочу обратить ваше внимание на то, что данная статья является лишь кратким обзором возможностей NlpCraft IDL и не пытается быть исчерпывающим мануалом. Перед началом полноценной работы с системой рекомендуется изучить детальное описание синтаксиса и всех возможностей языка в соответствующих разделах документации.Место определения интентов
  • Интенты, определяемые с помощью NlpCraft IDL могут быть объявлены  непосредственно в файлах статического определения модели. Данный подход очень удобен для простых случаев. Пример доступен по ссылке
  • Также интенты могут быть определены непосредственно в коде модели с помощью аннотаций. Пример по ссылке.  
  • Кроме того, интенты могут быть определены в отдельных файлах. Модели при этом будет ссылаться на определение интентов согласно указанному пути к этим файлам или URL ресурсам с помощью элемента import. Данный подход удобен при работе с большими моделями, при редактировании определений которых может оказаться полезной подсветка синтаксиса и прочие возможности предоставляемые IDE (так, например, Intellij Idea обеспечивает подсветку ключевых слов, подсказки и проверку синтаксиса файлов произвольных типов согласно заданной конфигурации). Кроме того данный подход может быть полезен при работе специалистов, не имеющих возможности или желания редактировать код напрямую. Пример доступен по ссылкам: 1, 2.  
Ключевые слова NlpCraft IDLflow, fragment, import, intent, meta, ordered, term, true, false, null.
  • intent, flow, fragment, meta, ordered, term - составные части определения интента. 
  • Ключевое слово fragment также используется для создания именованных списков terms, включаемых в определения интентов. 
  • import - необходим при подключении внешних файлов для возможности использования определенных в них элементов fragment, intent или других import.
  • true, false, null - используются при работе со встроенными функциями, о которых мы поговорим чуть ниже.
Структура программы NlpCraft IDLПрограмма содержит набор следующих необязательных элементов, расположенных в произвольном порядке:
  • import
  • fragment
  • intent
Отладка и запускИнтент компилируется при запуске модели и может быть отлажен лишь в процессе отладки модели. Для задания сложных интентов рекомендуется создавать их в отдельных файлах и использовать возможности редактирования, предоставляемые IDE для обеспечения начальной валидации сложных конструкций. Элементы раздела import и fragment могут быть задействованы и проверены только совместно с использующим эти элементы интентом.  Структура определения импортаСодержит ключевое слово “import” и имя файла или URL ресурса в круглых скобках.Пример: import('http://mysite.com/nlp/idls/external.idl)Структура определения фрагментаСодержит ключевое слово “fragment” с именем и список terms. Terms в данном случае могут быть параметризованными. Пример простого fragment:fragment=buzz term~{tok_id() == 'x:alarm'}Пример параметризованного fragment c аргументами ‘a’ и ‘b’:
fragment=p1
    term={
        meta_frag('a') &&
        has_any(get(meta_frag('b'), 'Москва'), list(1, 2))
    }
Ниже приведен пример использования данного fragment в интенте. Аргументам ‘a’ и ‘b’ ставятся в соответствие значения параметров. 
intent=i1
    fragment(p1, {'a': true, 'b': {'Москва': [1, 2, 3]}})
Структура определения интентаИмя интентаОбязательный элемент. Имя - это уникальный в рамках модели идентификатор, необходимый для создания в колбеках ссылок на интент. Ниже приведен пример использования ссылки на интент "timeIntent". 
@NCIntentRef("timeIntent")
fun onTimeMatch(
    ctx: NCIntentMatch,
    @NCIntentTerm("t1") tok: NCToken
): NCResult { ... }
Набор termsОбязательный элемент. Term - это основной элемент определения интента. В каждом term задается правило срабатывания интента на основе сконфигурированного условия, тела term. Составные части правил могут относиться к токену или опираться на какие-то иные факторы. Как определятеся term:
  • Ключевое слово term. Обязательный элемент.
  • Имя в круглых скобках. Опционально. Служит для создания ссылок на найденный токен в аргументах колбека, смотри пример выше, токен “t1”.
  • Тип term. Обязательный элемент. Поддерживается два типа term: 
    • “~“ - токен может быть получен из истории диалога или из текущего запроса.
    • “=“ - токен должен быть получен только из текущего запроса.  
    Пример: term(nums)~{tok_id() == 'nlpcraft:num'}Выбор типа term имеет смысл только для terms относящихся к токенам, в противном случае значение выставленного типа игнорируется. 
  • Тело term. Обязательный элемент. Существуют два способа задания тела term: с помощью встроенных функций или с помощью программного кода. Примеры:
    • term(nums)={tok_id() == 'nlpcraft:num'}
    • term(nums)~{true}
    • term~/org.mypackage.MyClass#termMethod/?
    Обратите внимание на специальный синтаксис последнего term. 
  • Квантификатор. Опционально. Поддерживаются следующие типы квантификаторов: 
    • [M, N] - условие должно сработать от N до M раз.
    • * - условие должно сработать хотя бы один раз, эквивалентно [0, ∞]
    • + - условие должно сработать более одного раза, эквивалентно [1, ∞]
    • ? - условие должно сработать 0 или 1 раз, эквивалентно [0, 1]
    Примеры:
    • term(nums)={tok_id() == 'nlpcraft:num'}[1,2] - запрос должен содержать один или два токена с идентификатором “nlpcraft:num”.
    • term(nums)={tok_id() == 'nlpcraft:num'}* - запрос должен содержать один или более токенов с идентификатором “nlpcraft:num”.
Подробнее о встроенных функциях NlpCraft IDL. Поддерживаемые в языке функции условно подразделяются на следующие типы:
  • Основанные на базовых свойствахтокенов - идентификаторах, группах. Примеры: tok_id(), tok_groups(), tok_parent().
  • Основанные на NLP свойствахтокенов - стеммах, леммах, частях речи, признаках стоп-слов. Примеры: tok_lemma(), tok_is_wordnet(), tok_swear().
  • Основанные на информации о том, как токен был обнаружен в пользовательском запросе - значениях синонимов и т.д.  Примеры: tok_value(), tok_is_permutated(), tok_is_direct().
  • Основанные на данных о пользовательском запросе - времени запроса, типе user agent. Примеры: req_tstamp(), req_addr(), req_agent().
  • Основанные на различных метаданных - токенов, модели, запроса и т.д.  Примеры: meta_model('my:prop'), meta_tok('nlpcraft:num:unit'), meta_user('my:prop').
  • Основанные на данных, предоставляемых NER провайдерами токенов. Пример, для “geo:city“ это может быть количество жителей или координаты,  полученные из метаданных. 
  • Основанные на данных пользователя системы и его компании - статусы, время регистрации. Примеры: user_admin(), comp_name(), user_signup_tstamp().
  • Основанные на системных данных - значениях переменных окружения, системном времени и т.д. Примеры: meta_sys('java.home'), now(), day_of_week().
  • Математические, текстовые функции, функции работы с коллекциями и т.д. Примеры: lowercase("TeXt"), abs(-1.5), distinct(list(1, 2, 2, 3, 1)).
Более детальная информация и описание каждой функции - по ссылке. Тело term - это предикат, построенный на основе комбинации этих функций. Для предотвращения необходимости повторных вычислений при работе с функциями, могут быть использованы локальные переменные, пример ниже:
term(t2)={
    @a = meta_model('a')
    @list = list(1, 2, 3, 4)
    (@a == 42 || @a == 44) && has_all(@list, list(3, 2))
}
Локальные переменные определяются с помощью специального префикса @. Их использование помогает избежать повторных вычислений и сокращает запись. Подразумевается, что существующего набора встроенных функций и средств NlpCraft IDL достаточно для определения большинства возможных term любого интента. Но при необходимости пользователь может создать свои собственные предикаты на java, scala, kotlin, groovy или другом java based языке и прописать их в теле term. То есть NlpCraft IDL может содержать ссылки на фрагменты кода, полностью написанного на других языках.    Пример: term(a)=/MyClass#myMethod/На входе пользовательская функция получает аргумент, содержащий все имеющиеся данные о пользовательском запросе, а на выходе должна вернуть значение определенного типа.FragmentFragment это поименованный набор terms, создаваемый для возможности повторного использования в разных интентах. Пример по ссылкеFlowЗдесь мы определяем дополнительное правило срабатывания интента, опирающееся на данные по предыдущим срабатываниям в рамках сессии. Данное правило может быть определено на основе regex, составленного на основе идентификаторов предыдущих сработавших интентов.  Пример такого определения: flow="^(?:login)(^:logout)*$" Данное правило означает, что для срабатывания интента необходимо, чтобы в рамках текущей сессии уже было срабатывание интента с идентификатором “login”, и не было с идентификатором “logout”. В случае необходимости задания более сложной логики, она также может быть вынесена в пользовательский код, написанный на Java-based языке, как и тело term. Пример определения в интенте: 
@NCIntent("intent=x
    flow=/com.company.dialog.Flow#customFlow/
    term~{tok_id() == 'some_id'}"
)
def onX(): NCResult = { .. }
Предикат, определенный в методе customFlow(), получает на входе список с информацией по всем интентам, ранее вызванным в рамках текущей сессии, и возвращает значение типа boolean. MetaОпциональный элемент. Набор данных для задания дополнительных условий срабатывания интента представленный в формате JSON.Ordered flagОпциональный элемент. По умолчанию false. Определяет правило - должны ли токены сработавших terms быть упорядочены в запросе.  Зачем вообще нужен отдельный языкЕще раз обратите внимание на то, что вся логика создания интентов, определенная с помощью NlpCraft IDL, может быть написана на любом java based языке. Зачем тогда вообще нужен этот новый язык? Даже если его синтаксис краток, прост и понятен, все равно придется потратить какое-то время на его изучение. Ниже приведем ряд аргументов в пользу использования NlpCraft IDL.
  • Краткость записи. Запись на специализированном DSL всегда короче записи той же логики на базовом языке. Для интентов с небанальными правилами это может быть важно. 
  • В случае если NlpCraft IDL код был вынесен в отдельный файл, то редактирование IDL программы, например для изменения логики срабатывания интента, не влечет за собой необходимости пересобирать код модели и ее колбеков.
  • Разнесение логики написания колбеков и логики срабатывания интентов. Данными вопросами могут заниматься разные специалисты. В силу осознанной ограниченности языковых средств, DSL проще для изучения.  
  • В настоящее время модели могут быть созданы на любом java based языке. В ближайших планах Apache NlpCraft расширение списка поддерживаемых языков. Использование NlpCraft IDL позволит сохранить один общий язык определения интентов для разных типов моделей в рамках одного проекта.
ЗаключениеНадеюсь вы смогли получить первое представление о возможностях языка определения интентов NlpCraft IDL и типах задач, которые можно решить с его помощью. Здесь вы найдете детальное описание языка и его возможностей. Дополнительные примеры моделей созданных на java, kotlin, groovy и scala, использующих для определения интентов NlpCraft IDL, доступны в гитхабе проекта.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_java, #_scala, #_api, #_apache, #_natural_language_processing, #_apache, #_open_source, #_natural_language_processing, #_nlp, #_nlpcraft, #_intenty (интенты), #_java, #_scala, #_api, #_apache, #_natural_language_processing
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 22-Ноя 19:00
Часовой пояс: UTC + 5