Интерпретация естественного языка

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

При разработке ботов для Telegram и других месенджеров, периодически возникает задача распознания и выполнения запросов на человеческих языках. Именно эта "фишка", по некоторому мнению, и является главным отличием ботов от приложений командной строки. Под катом описан фреймворк собственного приготовления для интерпретации запросов на естественных языках. Описания ключевых концепций сопровождены примерами на языке Kotlin.

За основу для распознания речи возьмем нормализованные семантические представления. Их выбор обусловлен прежде всего простотой и легкостью реализации. Начнем с базиса, пример из исходников фреймворка:

/** Правило проверяет лексему на соответствие */
typealias Rule = (String) -> Boolean

/** Нормализованное семантическое представление (ставятся в соответствие лексемам) */
open class Semnorm(vararg val rules: Rule)

/** Правило задает стемы для Semnorm */
fun stem(vararg stems: String): Rule = { stems.any(it::startsWith) }

/** Правило задает точные соответствия для Semnorms */
fun word(vararg words: String): Rule = { words.any(it::equals) }

/** Проверяем слово на соответствие Semnorm (case sensetive) */
fun String.matches(norm: Semnorm) = norm.rules.any { it(this) }

В результате появилась возможность задавать предпопределенные нормализованные семантические представления в виде объектов:

object Day : Semnorm(stem("day", "суток", "сутк", "дня", "ден", "дне"))

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

assertThat(
  "забань васю на 5 минут".tokenize(), 
  equalTo(
   listOf(
     Token("забань", Ban), 
     Token("васю", null),
     Token("на", null), 
     Token("5", Number),
     Token("минут", Minute)
   )
  )
)

Итак, с распознаванием речи мы разобрались. Код токенизатора приложен в репозитории, доступном в конце статьи.
Перейдем к исполнению полученного из речи потока токенов. А вот здесь и начинается самое интересное: фреймворк позволяет навесить к каждому семантическому представлению заданное поведение! Снова простейший пример для начала, как распознать запрос справки, на русском и английских языках

object Help : ExecutableSemnorm(stem(
  "помощ", "справк", "правил", "help", 
  "rule", "faq", "start", "старт",
)) {
  override fun execute(bot: Botm: Message) {
    val faq = message.from.relatedFaq()
    bot.sendMessage(m.chat.id, faq)
  }
}

Что насчет более сложного поведения, зависящего от разных слов в предложении? Оно тоже поддерживается, вот как, например, исполняется, уже известное из тестов предложение забанить Васю:

object Ban : DurableSemonrm(stem(
  "ban", "block", "mute", "бан", "блок",
  "забан", "завали", "замьют",
)) {
  override fun execute(bot: Bot, attackerMessage: Message, duration: Duration) {

    val victimMessage = attackerMessage.replyToMessage
    val victimId = victimMessage.from.id
    val untilSecond = now().epochSecond + duration.inWholeSeconds

    bot.restrictChatMember(attackerMessage.chat.id, victimId, untilSecond)
    bot.sendTempMessage(
      m.chat.id, "​", replyToMessageId = victimMessage.messageId)
    }
  }
}

Откуда это семантическое представление знает о своей продолжительности? Дело в том, что ему вовсе не обязательно парсить всю цепочку токенов целиком. Достаточно задать только минимально необходимое поведение для каждого представления, например для времени:

object Week : Semnorm(stem("week", "недел")) {
  override fun toDuration(number: Long) = days(number) * 7
}

Или для любых команд, зависящих от времени:

class DurableSemnorm(vararg rules: Rule) : ExecutableSemnorm(*rules) {

  final override fun execute(token: Iterator<Token>, bot: Bot,m: Message) = 
    execute(bot, message, token.parseDuration(), coins, pins)

  abstract fun execute(bot: Bot, m: Message, duration: Duration)
}

Благодаря такой архитектуре, нам больше не приходится думать о запутанной логике работы интерпретатора. Достаточно просто определить семантические представления и навесить к ним поведение. Пример бота, использующего эту концепцию, можно посмотреть на Github: https://github.com/demidko/timecobot

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

Интересен ли вам этот фреймворк?

  • 100,0%Да, было бы неплохо оформить и выложить отдельно1
  • 0,0%Нет, банальная вещь, я знаю как сделать лучше0
Источник: https://habr.com/ru/post/560914/


Интересные статьи

Интересные статьи

Умение модели распознавать намерения собеседника, то есть понимать зачем человек совершил то или иное действие, применимо в большом числе прикладных NLP-задач. К примеру, чат-ботам, г...
Устраивать конкурсы в инстаграме сейчас модно. И удобно. Инстаграм предоставляет достаточно обширный API, который позволяет делать практически всё, что может сделать обычный пользователь ручками.
О том, что такое база KDB+, язык программирования Q, какие у них есть сильные и слабые стороны, можно прочитать в моей предыдущей статье и кратко во введении. В статье же мы реализуем на Q сервис...
Эта статья для тех, кто собирается открыть интернет-магазин, но еще рассматривает варианты и думает по какому пути пойти, заказать разработку магазина в студии, у фрилансера или выбрать облачный серви...
Одной из «киллер-фич» 12й версии Битрикса была объявлена возможность отдавать статические файлы из CDN, тем самым увеличивая скорость работы сайта. Попробуем оценить практический выигрыш от использова...