Рост искусственного интеллекта вызывает изменение парадигмы в области разработки пользовательского интерфейса. Благодаря распространению интеллектуальных голосовых помощников, таких как Google Home, Siri и Alexa, пользователи начинают чувствовать, что нажатие многочисленных кнопок на экране или ручное заполнение форм не только неэффективно и медленно, но и старомодно. ,
К счастью, сегодня существует множество облачных сервисов, которые позволяют разработчикам легко добавлять диалоговые пользовательские интерфейсы в свои приложения. Google Dialogflow Standard Edition является одним из таких сервисов. Это бесплатно, очень мощный, многоязычный и поставляется с большим количеством хорошо разработанных шаблонов.
В этом руководстве я покажу вам, как создать простой текстовый диалоговый пользовательский интерфейс для Android с помощью Dialogflow.
Предпосылки
Прежде чем продолжить, убедитесь, что у вас есть доступ к:
- последняя версия Android Studio
- устройство или эмулятор под управлением Android 5.0 или выше
1. Создание агента
Используя Dialogflow, вы всегда будете работать с агентом, системой понимания естественного языка, обученной обрабатывать определенный набор пользовательских данных.
Чтобы создать свой первый агент, используйте учетную запись Google для входа в консоль Dialogflow и нажмите кнопку « Создать агента» .
В появившейся форме дайте разумное имя агенту и нажмите кнопку « Создать» .
Через несколько секунд у вас появится новый агент.
2. Создание намерения
В то время как визуальные интерфейсы имеют кнопки, которые пользователи могут нажимать для выражения своих намерений, диалоговые интерфейсы имеют намерения. Таким образом, в хорошо разработанном диалоговом интерфейсе все, что может сказать пользователь, сопоставлено с намерением. Работа агента состоит только в том, чтобы точно определить, какое намерение необходимо активировать, когда пользователь произносит или вводит фразу или предложение.
Для простоты давайте создадим только одно намерение для нашего агента: намерение с именем 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
связанное с ним.
Нажмите кнопку Сохранить после внесения изменений.
3. Включение Small Talk
Большинство пользователей вряд ли ограничатся созданным вами WEIGHT
намерением. Хотя резервное намерение сможет обрабатывать все недействительные запросы, всегда полезно обучить агента так, чтобы он мог участвовать в светской беседе. Это сделает его более человечным.
В консоли Dialogflow очень легко добавить агенту возможности для разговоров. Все, что вам нужно сделать, это открыть вкладку « Небольшой разговор » и нажать кнопку « Включить» .
На этом этапе агент сможет генерировать ответы по умолчанию на множество общих вопросов. При желании вы можете настроить эти ответы, чтобы придать ему уникальную индивидуальность. А пока я предлагаю вам ответить на несколько вопросов в разделе « Об агенте » и нажать кнопку « Сохранить» .
4. Получение токена доступа
Вашему приложению Android понадобится токен клиентского доступа при общении с агентом Dialogflow. Чтобы получить его, нажмите на значок шестеренки рядом с именем агента и откройте вкладку Общие . Прокрутив вниз до раздела ключей API , вы увидите токен. Запишите это, чтобы вы могли использовать его позже.
5. Добавление зависимостей проекта
Мы будем использовать сетевую библиотеку 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»/>
|
6. Определение макета
Виджет 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, ссылка на виджет будет доступна как свойство расширения внутри вашей деятельности.
Я предлагаю вам запустить ваше приложение сейчас, чтобы взглянуть на макет, который вы только что создали.
7. Настройка топлива
Вы можете сделать свой сетевой код намного более лаконичным, настроив клиент 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
)
|
8. Настройка интерфейса чата
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
в качестве аватара для обоих объектов. Не стесняйтесь использовать любой другой нарисованный ресурс, если хотите.
9. Отправка и получение сообщений
Всякий раз, когда пользователи нажимают кнопку 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, обратитесь к его официальной документации .