NLU Practice - Denire/jaicf-template-for-jaicp-developers GitHub Wiki
Практическое задание для закрепления знаний по CAILA и JAICF
CAILA - мощный NLU движок с кучей возможностей. Для того, чтобы познакомиться с ним получше, предлагаем выполнить несколько заданий.
Простое - Подключите к проекту два Кайла-активатора
Необходимо:
- Создать два проекта в Админке
- Подключить к JAICF-боту два кайла-классификатора
- Сделать так, чтобы один классификатор занимался интентом покупки коз:
- Пример запроса:
Купить козу за 100 рублей
. - Классификатор понимает сущность коза и количество денег.
- Пример запроса:
- Второй классификатор занимался арендой коров:
- Пример запроса:
Я бы хотел взять в аренду корову в пятницу
- Классификатор понимает сущность корова и время, на которое ее хотят взять в аренду.
- Пример запроса:
Посложнее - CAILA Direct API Spec
Зная, как работать с CAILA напрямую, можно не ограничиваться тем, что предлагает вам платформа в своих встроенных методах.
Работать с кайлой можно из GUI, а можно с помощью REST-API-Интерфейса. Например, можно написать код, который будет ходить по HTTP для вызова conform - метода, схожего по функционалу с $nlp.conform. Вот пример кода (все необходимые зависимости есть в текущем темплейте):
class CailaAPIClient(
val accessToken: String,
val baseUrl: String = "https://app.jaicp.com/cailapub/api/caila/p",
) : WithLogger {
fun conform(text: String, number: Int): String = runBlocking {
httpClient.get("$baseUrl/$accessToken/nlu/conform") {
parameter("text", text)
parameter("number", number)
header("accept", "text/plain")
}
}
}
Во время встреч в зуме был задан вопрос - а есть ли в JAICF метод $nlp.inflect
. Ответ - в JAICF его сейчас нет,
возможно, он появится позже.
Ваша задача - написать $nlp.inflect
. Ожидается, что это займет не больше 20 строк кода, если делать по аналогии
с confirm
.
Спецификация кайлы здесь.
Самое сложное - Пишем CailaEntityActivator
JAICF
может работать с любой NLU системой. К некоторым (CAILA
/RASA
/Lex
) мы предосталяем готовые клиенты. Для
некоторых, если это необходимо Вам, активатор напишете Вы сами.
JAICF - фреймворк, а не библиотека. Вы можете кастомизировать его, встравать свою логику в любое место выполнения запроса.
Процесс написания активатора состоит из нескольких составляющих:
- Пишем класс, который имплементирует интерфейс
Activator
- Пишем к нему
companion object
, имплементирующий интерфейсActivatorFactory
, чтобы актиавтор с моделью сценария мог создаватьBotEngine
. - Пишем класс, который наследуется от
ActivatorContext
. Этот класс передается в сценарий. Посмотрим наCailaIntentActivatorContext
:- В самом обьекте мы говорим о полях и классах, которые будут доступны из сценария
- Мы добавляем extension-функцию, чтобы в сценарии можно было написать
activator.caila?.slots
и получить найденные слоты. Без нее пользователю пришлось бы писать что-то в стиле(activator as CailaIntentActivatorContext).slots
.
data class CailaIntentActivatorContext(
val result: CailaAnalyzeResponseData,
val intentData: CailaInferenceResultData
) : IntentActivatorContext(
intent = intentData.intent.name,
confidence = intentData.confidence.toFloat()
), java.io.Serializable {
val topIntent = intentData.intent
var slots = intentData.slots?.map { it.name to it.value }?.toMap() ?: emptyMap()
val entities = result.entitiesLookup.entities
companion object {
private const val serialVersionUID = 4934755046273038374L
}
}
val ActivatorContext.caila
get() = this as? CailaIntentActivatorContext
В качестве референса вы можете смотреть на класс CailaIntentActivator
.
На момент написания статьи разработчки JAICF не успели воплотить свои идеи по кастомизации активаторов. Будь наша задача выполнена - задча бы решалась значительно проще. Однако, это не повод не воплотить функционал, аналогичный функционалу JAICP DSL с именовванными паттернами.
Описание задачи
Вот недописанная реализация CailaEntityActivator
. Ваша задача - дописать его и использовать в вашем проекте.
class CailaEntityActivator(token: String, model: ScenarioModel) : BaseActivator(model) {
override val name: String = "cailaEntityActivator"
private val client = CailaKtorClient(token, inferenceNBest = 5, url = "$DEFAULT_CAILA_URL/api/caila/p")
override fun provideRuleMatcher(botContext: BotContext, request: BotRequest): ActivationRuleMatcher {
val entities: List<CailaEntityMarkupData> = detectEntities(request)
return ruleMatcher<CailaEntityActivatorRule> { rule ->
TODO()
}
}
private fun detectEntities(request: BotRequest): List<CailaEntityMarkupData> = TODO()
override fun canHandle(request: BotRequest) = request.hasQuery()
class Factory(private val token: String) : ActivatorFactory {
override fun create(model: ScenarioModel): Activator = TODO()
}
}
class CailaEntityActivatorRule(val entityName: String) : ActivationRule
class CailaEntityActivatorContext(
val entity: CailaEntityMarkupData,
override val confidence: Float = 1.0F,
) : ActivatorContext
fun ActivationRulesBuilder.entity(entityName: String) = TODO()
val ActivatorContext.entity get() = TODO()