Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру 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