Статьи

Создайте диалоговый интерфейс для Android с помощью Dialogflow

Конечный продукт
Что вы будете создавать

Рост искусственного интеллекта вызывает изменение парадигмы в области разработки пользовательского интерфейса. Благодаря распространению интеллектуальных голосовых помощников, таких как Google Home, Siri и Alexa, пользователи начинают чувствовать, что нажатие многочисленных кнопок на экране или ручное заполнение форм не только неэффективно и медленно, но и старомодно. ,

К счастью, сегодня существует множество облачных сервисов, которые позволяют разработчикам легко добавлять диалоговые пользовательские интерфейсы в свои приложения. Google Dialogflow Standard Edition является одним из таких сервисов. Это бесплатно, очень мощный, многоязычный и поставляется с большим количеством хорошо разработанных шаблонов.

В этом руководстве я покажу вам, как создать простой текстовый диалоговый пользовательский интерфейс для Android с помощью Dialogflow.

Прежде чем продолжить, убедитесь, что у вас есть доступ к:

  • последняя версия Android Studio
  • устройство или эмулятор под управлением Android 5.0 или выше

Используя Dialogflow, вы всегда будете работать с агентом, системой понимания естественного языка, обученной обрабатывать определенный набор пользовательских данных.

Чтобы создать свой первый агент, используйте учетную запись Google для входа в консоль Dialogflow и нажмите кнопку « Создать агента» .

Экран приветствия DialogFlow

В появившейся форме дайте разумное имя агенту и нажмите кнопку « Создать» .

Форма создания агента

Через несколько секунд у вас появится новый агент.

В то время как визуальные интерфейсы имеют кнопки, которые пользователи могут нажимать для выражения своих намерений, диалоговые интерфейсы имеют намерения. Таким образом, в хорошо разработанном диалоговом интерфейсе все, что может сказать пользователь, сопоставлено с намерением. Работа агента состоит только в том, чтобы точно определить, какое намерение необходимо активировать, когда пользователь произносит или вводит фразу или предложение.

Для простоты давайте создадим только одно намерение для нашего агента: намерение с именем WEIGHT , которое позволит пользователям преобразовывать веса в килограммах в фунты и наоборот.

Чтобы создать намерение, нажмите кнопку « Создать намерение» на консоли.

На следующем экране введите имя цели и нажмите кнопку Добавить обучающие фразы . Теперь вы сможете использовать несколько фраз, которые агент может использовать для обучения. Например, предложение «что такое 32 килограмма в фунтах» было бы хорошей обучающей фразой для намерения WEIGHT .

После того, как вы введете предложение и нажмете клавишу Enter , вы увидите, что Dialogflow правильно угадывает, что фраза «32 килограмма» является переменной. Он также автоматически создаст для него программно доступный параметр с именем unit-weight и установит его тип в @sys.unit-weight .

Создание обучающей фразы

Точно так же он предполагает, что слово «фунты» тоже является переменным и создает для него параметр с именем unit-weight-name , тип которого равен @sys.unit-weight-name .

Я предлагаю вам ввести еще несколько похожих обучающих фраз, всегда следя за тем, чтобы параметры unit-weight unit-weight-name и имени unit-weight-name были разрешены до правильных значений.

Добавлено несколько обучающих фраз

Затем нажмите кнопку « Добавить ответы» , чтобы ввести несколько общих ответов. Поймите, что они будут показаны пользователю дословно.

Добавлено несколько общих ответов

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

Как я уже говорил ранее, хороший диалоговый интерфейс должен уметь обрабатывать все, что говорит пользователь. Это означает, что наш агент должен также иметь возможность сопоставлять любезности и предложения, которые он не понимает, с действительными намерениями. Поскольку это очень распространенное требование, Dialogflow автоматически генерирует для нас такие намерения, которые называются « Default Welcome Intent Default Fallback Intent . В то время как последний не нуждается в каких-либо изменениях, первый делает.

Default Welcome Intent не имеет обучающих фраз, поэтому вы должны предоставить несколько. Кроме того, мы не будем работать с событиями в этом руководстве, поэтому вы можете удалить событие Welcome связанное с ним.

Настройка приветствия по умолчанию

Нажмите кнопку Сохранить после внесения изменений.

Большинство пользователей вряд ли ограничатся созданным вами WEIGHT намерением. Хотя резервное намерение сможет обрабатывать все недействительные запросы, всегда полезно обучить агента так, чтобы он мог участвовать в светской беседе. Это сделает его более человечным.

В консоли Dialogflow очень легко добавить агенту возможности для разговоров. Все, что вам нужно сделать, это открыть вкладку « Небольшой разговор » и нажать кнопку « Включить» .

Экран настройки разговоров

На этом этапе агент сможет генерировать ответы по умолчанию на множество общих вопросов. При желании вы можете настроить эти ответы, чтобы придать ему уникальную индивидуальность. А пока я предлагаю вам ответить на несколько вопросов в разделе « Об агенте » и нажать кнопку « Сохранить» .

Раздел прогресса настройки для разговоров

Вашему приложению Android понадобится токен клиентского доступа при общении с агентом Dialogflow. Чтобы получить его, нажмите на значок шестеренки рядом с именем агента и откройте вкладку Общие . Прокрутив вниз до раздела ключей API , вы увидите токен. Запишите это, чтобы вы могли использовать его позже.

Раздел API ключей

Мы будем использовать сетевую библиотеку Fuel при взаимодействии с веб-службой Dialogflow, поэтому добавьте следующую зависимость implementation в файл build.gradle модуля app :

1
implementation ‘com.github.kittinunf.fuel:fuel-android:1.12.1’

Dialogflow может обрабатывать как текст, так и аудио. Однако в этом уроке мы будем работать только с текстом. Следовательно, наше приложение будет иметь пользовательский интерфейс, похожий на приложение чата. Поэтому добавьте библиотеку ChatMessageView в качестве другой зависимости.

1
implementation ‘com.github.bassaer:chatmessageview:1.10.0’

Наконец, убедитесь, что ваше приложение может подключаться к Интернету, запросив следующее разрешение в файле AndroidManifest.xml :

1
<uses-permission android:name=»android.permission.INTERNET»/>

Виджет ChatView библиотеки ChatView предлагает полноценный пользовательский интерфейс чата, способный отображать сообщения чата и принимать пользовательский ввод. Используя его в нашем макете, мы можем сэкономить много времени и усилий. Поэтому поместите виджет в виджет FrameLayout и добавьте его в XML-файл макета.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<?xml version=»1.0″ encoding=»utf-8″?>
<FrameLayout
    xmlns:android=»http://schemas.android.com/apk/res/android»
    xmlns:tools=»http://schemas.android.com/tools»
    android:layout_width=»match_parent»
    android:layout_height=»match_parent»
    tools:context=»com.tutsplus.dialogflowtutorial.MainActivity»>
 
    <com.github.bassaer.chatmessageview.view.ChatView
        android:id=»@+id/my_chat_view»
        android:layout_width=»match_parent»
        android:layout_height=»match_parent»/>
 
</FrameLayout>

Если в вашем проекте включены расширения Android Kotlin, ссылка на виджет будет доступна как свойство расширения внутри вашей деятельности.

Я предлагаю вам запустить ваше приложение сейчас, чтобы взглянуть на макет, который вы только что создали.

Интерфейс для чата

Вы можете сделать свой сетевой код намного более лаконичным, настроив клиент Fuel специально для использования веб-службы Dialogflow. Однако, прежде чем сделать это, добавьте маркер доступа клиента, который вы получили ранее, в качестве константы времени компиляции к своей активности.

1
2
3
companion object {
    private const val ACCESS_TOKEN = «1234567890abcdef»
}

Все HTTP-запросы, которые вы делаете к веб-службе Dialogflow, должны иметь заголовок Authorization на основе токена. Чтобы избежать создания заголовка вручную каждый раз при выполнении запроса, используйте свойство FuelManager класса FuelManager .

1
2
3
FuelManager.instance.baseHeaders = mapOf(
    «Authorization» to «Bearer $ACCESS_TOKEN»
)

Затем установите для свойства basePath класса FuelManager базовый URL-адрес веб-службы Dialogflow.

1
2
FuelManager.instance.basePath =
               «https://api.dialogflow.com/v1/»

Наконец, все ваши HTTP-запросы должны всегда иметь следующие параметры конфигурации: параметр v указывающий версию протокола, которую вы хотите использовать, параметр lang указывающий язык, на котором вы хотите, чтобы ответы агента были, и параметр sessionId , значение которого может быть любым случайная строка

Следующий код показывает, как использовать свойство baseParams для установки всех параметров:

1
2
3
4
5
FuelManager.instance.baseParams = listOf(
    «v» to «20170712», // latest protocol
    «sessionId» to UUID.randomUUID(), // random ID
    «lang» to «en» // English language
)

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

Следующий код показывает, как создавать объекты:

01
02
03
04
05
06
07
08
09
10
11
12
13
val human = ChatUser(
        1,
        «You»,
        BitmapFactory.decodeResource(resources,
                R.drawable.ic_account_circle)
)
 
val agent = ChatUser(
        2,
        «Agent»,
        BitmapFactory.decodeResource(resources,
                R.drawable.ic_account_circle)
)

Обратите внимание, что код использует встроенный ресурс с именем ic_account_circle в качестве аватара для обоих объектов. Не стесняйтесь использовать любой другой нарисованный ресурс, если хотите.

Всякий раз, когда пользователи нажимают кнопку ChatView виджета ChatView , вы должны создавать объекты Message основе ChatView ими текста. Для этого вы можете использовать класс Message.Builder . При создании объекта вам необходимо убедиться, что он принадлежит пользователю, вызвав метод setUser() .

Как только объект Message будет готов, вы можете передать его методу send() виджета ChatView для его визуализации. В следующем коде показано, как это сделать внутри setOnClickSendButtonListener() виджета ChatView .

01
02
03
04
05
06
07
08
09
10
11
my_chat_view.setOnClickSendButtonListener(
    View.OnClickListener {
        my_chat_view.send(Message.Builder()
                .setUser(human)
                .setText(my_chat_view.inputText)
                .build()
        )
 
        // More code here
    }
)

Чтобы фактически отправить сообщение пользователя вашему агенту, вы должны сделать HTTP-запрос GET к конечной точке /query веб-службы Dialogflow. В качестве входных данных он ожидает параметр query , значением которого может быть любая фраза или предложение, введенное пользователем.

В качестве HTTP-ответа вы получите JSON-документ, в котором значение result/fulfillment/speech содержит ответ агента.

01
02
03
04
05
06
07
08
09
10
Fuel.get(«/query»,
        listOf(«query» to my_chat_view.inputText))
        .responseJson { _, _, result ->
    val reply = result.get().obj()
                    .getJSONObject(«result»)
                    .getJSONObject(«fulfillment»)
                    .getString(«speech»)
 
    // More code here
}

Чтобы отобразить ответ внутри виджета ChatView , вы должны снова создать другой объект Message . На этот раз, однако, его владелец должен быть агентом. Кроме того, чтобы отобразить сообщение с правой стороны, необходимо передать значение true его setRight() .

1
2
3
4
5
6
my_chat_view.send(Message.Builder()
        .setRight(true)
        .setUser(agent)
        .setText(reply)
        .build()
)

Если вы запустите приложение сейчас, вы сможете общаться с агентом.

Ответы на небольшие разговоры

Однако если вы попросите приложение перевести вес в килограммах в фунты, оно даст только общий ответ. Чтобы иметь возможность фактически выполнить преобразование, вы должны сначала определить, было ли WEIGHT намерение ВЕСА. Для этого вы можете проверить значение ключа result/metadata/intentName .

1
2
3
4
5
6
7
8
val intent:String?
                    .getJSONObject(«result»)
                    .optJSONObject(«metadata»)
                    .optString(«intentName»)
 
if(intent!! == «WEIGHT») {
    // More code here
}

Убедившись, что намерение WEIGHT было запущено, вы можете определить значения параметров unit-weight-name и unit-weight , которые будут присутствовать внутри объекта result/parameters .

01
02
03
04
05
06
07
08
09
10
11
12
// Convert to what
val unitWeightName = result.get().obj()
        .getJSONObject(«result»)
        .getJSONObject(«parameters»)
        .getString(«unit-weight-name»)
 
// The weight that needs to be converted
val unitWeight = result.get().obj()
        .getJSONObject(«result»)
        .getJSONObject(«parameters»)
        .getJSONObject(«unit-weight»)
        .getDouble(«amount»)

С указанными выше значениями все, что требуется, это простая математика и оператор if-else для выполнения преобразования. Чтобы отобразить результат, вам понадобится другой объект Message . Его владелец тоже должен быть агентом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
// Perform conversion
val result = if(unitWeightName == «lb») {
    unitWeight * 2.20462
} else {
    unitWeight / 2.20462
}
 
// Render result
my_chat_view.send(Message.Builder()
    .setRight(true)
    .setUser(agent)
    .setText(«That’s ${«%.2f».format(result)} $unitWeightName»)
    .build()
)

Наше приложение готово. Вы должны быть в состоянии запустить его сейчас, чтобы убедиться, что он выполняет все преобразования правильно.

Завершено приложение запущено

Теперь вы знаете, как создать дружественный и полезный агент с помощью Google Dialogflow. В этом руководстве вы также узнали, как создать привлекательный интерфейс, который люди могут использовать при общении с агентом.

Чтобы узнать больше о Dialogflow, обратитесь к его официальной документации .