Asistente de voz en la aplicación móvil: llamar pantallas y completar formularios sin manos lähde: Anna Prist | Octubre de 2020

Lee la primera parte

Habitica es una aplicación de seguimiento de hábitos con algunos elementos de gamificación que ayuda a formar buenos hábitos. Mantiene las metas de su vida como hábitos cotidianos y lo alienta a cumplirlas.

Así que ahora le enseñaremos a nuestro asistente de voz, que vive en la aplicación, a crear y realizar tareas con la voz en lugar de con las manos.

Comencemos fácilmente, veamos cómo funciona la lógica. Digamos que queremos usar comandos de voz para abrir ventanas de configuración o propiedades. abierto Manifiesto para Android y busque actividades relevantes. Usted encontrará PrefsActivity quién es responsable de la configuración; y FixCharacterValuesActivity responsable de propiedades de los personajes. Y primero, busque una actividad que abra información sobre su perfil y aplicaciones: FullProfileActivity y Acerca de la actividad.

Según la documentación, teníamos que introducir la lógica del cliente en la clase heredada de Habilidad personalizada. Primero determinamos que solo respondemos a una respuesta de robot que contiene «cambio Vista«v reacción. acción. en reacción. acción transferimos el comando exactamente a dónde ir e invocamos la actividad en consecuencia. Pero no olvide buscar el contexto de la aplicación para entonces:

clase ChangeViewSkill (contexto privado val: contexto): CustomSkill {

anular la diversión canHandle (respuesta: AimyboxResponse) = response.action == «changeView»

anular pause fun onResponse (

respuesta: AimyboxResponse,

aimybox: Aimybox,

defaultHandler: pause (respuesta) -> unidad

) {

val intent = when (response.intent) {

«Configuración» -> intención (contexto, PrefsActivity :: class.java)

«Características» -> Intención (contexto, FixCharacterValuesActivity :: class.java) //

Perfil -> Intención (contexto, FullProfileActivity :: class.java) //

«Acerca de» -> Intención (contexto, AboutActivity :: class.java)

else -> Intención (contexto, MainActivity :: class.java)

}

intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK)

aimybox.standby ()

context.startActivity (intención)

}

}

Esta habilidad se agrega al asistente de la siguiente manera:

val dialogApi = AimyboxDialogApi (

«TU LLAVE AQUÍ», unitId,

customSkills = linkedSetOf (ChangeView ()))

1. Comprensión de DialogFlow ES y CX

2. Despliegue de modelos de transformadores

3. Instalación de TensorFlow con compatibilidad con GPU en Python 3.7 en Windows

4. Diseñadores de conversación: ¿quiénes son y qué hacen?

Usamos JAICF para crear una parte de la habilidad en la nube (es un marco de código abierto basado en Kotlin, a partir del cual se pueden crear aplicaciones de voz Solo IA). Comience con un proyecto de fork https://github.com/just-ai/jaicf-jaicp-caila-template.

Necesitaremos comprender las consultas del usuario y luego enviar el JSON al dispositivo para activar la actividad correspondiente.

Desafortunadamente, no hubo integración en el momento de escribir este artículo. JAICP (Solo plataforma conversacional AI) con Aimybox (SDK de Voice Assistant en la aplicación para crear interfaces de voz), y es desafortunado porque la conexión sería mucho más fácil: simplemente podríamos agregar una línea de código a uno de los dos archivos de configuración en conexión carpeta. Pero ahora crearemos un nuevo archivo de configuración para usarlo en las pruebas.

Crear AimyboxConnection archivo

paquete com.justai.jaicf.template.connections

import com.justai.jaicf.channel.http.httpBotRouting

import com.justai.jaicf.channel.aimybox.AimyboxChannel

importar io.ktor.routing.routing

importar io.ktor.server.engine.embeddedServer

importar io.ktor.server.netty.Netty

import com.justai.jaicf.template.templateBot

entretenimiento principal () {

embeddedServer (Netty, System.getenv («PUERTO») ?. toInt ()?: 8080) {

enrutamiento {

httpBotRouting («/» en AimyboxChannel (templateBot))

}

} .start (esperar = verdadero)

}

Para utilizar las funciones de NLU, agregaremos el servicio Caila NLU. Así que nos registraremos para app.jaicp.com, busque la clave API en la configuración e introdúzcala en conf / jaicp.properties. Ahora estamos evocando las intenciones en las que hemos entrado. app.jaicp.com directamente en el guión.

De hecho, puede usar cualquier otro proveedor de NLU o simplemente usar algunas expresiones regulares, pero si desea que el usuario sea agradable y útil, debe usar una NLU.

Primero establecemos las intenciones. Necesitamos saber cuándo nuestro usuario necesita ir a una parte específica de la aplicación. Así que configuramos las entidades y agregamos una entidad específica para cada parte de la aplicación; agregamos sinónimos y especificamos cómo lo reconocemos a nivel de aplicación (configuraciones, propiedades, etc. de las líneas de código anteriores).

Lo tengo así:

Ahora escribiremos la forma en que esperamos encontrarnos con esta entidad en el idioma del usuario. Para ello, creamos una intención y escribimos variaciones de frases. Además, realmente necesitamos saber dónde tenemos que ir exactamente, así que tenemos que marcar puntos de vista entidad según sea necesario.

Esto es lo que parece:

Usaremos el nombre para identificar la entidad en el código JAICF. Para asegurarse de que todas las intenciones se reconozcan como deberían, puede ingresar varias frases de prueba usando el botón de prueba.

Se ve bien:

Como precaución, eliminé todos los estados estándar, pero dejé atrapar todo solo: esto es algo que el robot dice cuando no te entiende. Crear cambio Vista Estado e ingrese la intención para la que acaba de crear el JAICP activadores. Escribe la lógica en acción – Necesitamos agregar toda la información a la respuesta del robot y a las respuestas estándar del canal Aimybox para hacer la transición.

Ahora entiendo puntos de vista ranura de lo que sabía Caila, insertó cosas que habíamos ingresado antes acción por lo que Aimybox sabía qué habilidades tenía que evocar. Luego colocamos la ranura reconocida en el proyecto. Para hacerlo ordenado, agregaremos «Espera un minuto».

estado («changeView») {

activadores {

intención («changeView»)

}

acción {

response.say («Volveré»)

var slot = «»

activator.caila? .run {slot = slots[“views”].Encadenar ()}

response.aimybox? .¿respuesta? .action = «changeView»

reacción. aimybox?. ¿responder? .intent = ranura

}

}

Más bien, lleve las habilidades a un paquete de habilidades separado con un conjunto de clases para cada habilidad. Ahora tiene varias opciones. Puede implementar el robot en el dispositivo a través de ngrok o puede usar Heroku. A continuación, tome el enlace que recibió y colóquelo en app.aimybox.com a través de su propio desarrollo de habilidades de voz, en el campo URL URL del webhook de Ahologic. Escriba algunos ejemplos de habilidades: vaya a la configuración, vaya a la información.

Una vez que haya agregado un canal, puede probar el retorno para obtener errores directamente en la consola usando Prueba en acción mando.

O puede habilitar una habilidad sin una consola o cualquier otra habilidad, como se describe aquí. Ahora solo tenemos que ejecutarlo y ver si todo está en orden.

¡Está!

Muy bien, ahora la parte delicada.

Quiero completar una tarea, asegurarme de que todo esté en orden y corregir algunos pequeños errores con un comando de voz. Y solo entonces finalmente lo crearé.

Para lograr esto, creamos una segunda habilidad. Distinguiremos entre el primero y éste mediante response.action == “createTask. Y especificamos el tipo de tarea a través de response.intent.

Cuando examines todos los recursos de la aplicación, verás que los precios, diarios, hábitos y tareas se crean a través de TaskFormActivity, pero usando diferentes tipos. Déjame mostrarte la lógica.

clase CreateTaskSkill (contexto val privado: Contexto): CustomSkill {

anular la diversión canHandle (respuesta: AimyboxResponse) = response.action == «createTask»

anular pause fun onResponse (

respuesta: AimyboxResponse,

aimybox: Aimybox,

defaultHandler: pause (respuesta) -> unidad

) {

val intent = Intent (contexto, TaskFormActivity :: class.java)

val additionalData = HashMap ()

type val = response.intent

datos adicionales[“viewed task type”] = cuando (tipo) {

«Personalizado» -> Tarea.TYPE_HABIT

«Diario» -> Tarea.TYPE_DAILY

Todo -> Task.TYPE_TODO

«Recompensa» -> Task.TYPE_REWARD

más -> «»

}

Cada tarea, incluidos los premios, tiene un título y una descripción. Las tareas también tienen complejidad y sentimiento. Veamos cómo enviar datos allí. Haremos esto a través de response.data y tendremos algunos valores predeterminados en caso de que esté vacío. Luego combinamos los datos y creamos una tarea con este paquete. También necesitaremos procesar los datos enlazados en la función onCreate TaskFormActivity.

// Código introducido para activación por voz

textEditText.setText (bundle.getString («nombre_actividad»)) // nombre de la tarea preestablecida

notesEditText.setText (bundle.getString («activity_description»)) // descripción de la tarea preestablecida

if (bundle.getBoolean («sentimiento»)) { // sentimiento de tarea preestablecido

habitScoringButtons.isPositive = true

habitScoringButtons.isNegative = false

} más {

habitScoringButtons.isNegative = true

habitScoringButtons.isPositive = false

}

when (bundle.getString («activity_difficulty»). toString ()) { // dificultad preestablecida de la tarea

«Trivial» -> taskDifficultyButtons.selectedDifficulty = 0.1f

«Fácil» -> taskDifficultyButtons.selectedDifficulty = 1f

Media -> taskDifficultyButtons.selectedDifficulty = 1.5f

«Difícil» -> taskDifficultyButtons.selectedDifficulty = 2f

else -> taskDifficultyButtons.selectedDifficulty = 1f

}

Ahora configuraremos el reconocimiento y las habilidades de JAICF con Caila NLU.

Preparación del correo: haz que la entidad reconozca el tipo, la dificultad y el sentimiento de las tareas (por ejemplo, lo tengo con patrones, puedes elegir Patrones en lugar de un sinónimo en el lado izquierdo del formulario)

No olvide ingresar los datos que procesaremos en el lado del cliente: personalizado, patrón, etc. Puede haber cualquier nombre y descripción, así que crearemos nombre y Descripción entidades donde escribimos una expresión regular y coincidimos con cualquier palabra. Por ahora, nuestro nombre y descripción tendrán solo una palabra.

Crear intención:

Declarar que lo necesitamos tipo_tarea y complejidad. Podemos establecer Necesario tanto el nombre como la descripción, por lo que si el usuario no menciona nada, el robot determina la ranura.

Ahora escribiremos diferentes variantes que se pueden usar como nombre o descripción con tipo (falta su orden, nombre o descripción). Y la perfección no conoce límites, pero para hacer un mínimo, solo unas pocas plantillas de arriba.

También mostraré un ejemplo aquí, utilizo un idioma de muestra que se puede cambiar presionando (a la izquierda de Enter).

@ – patrones y expresiones regulares, « – ejemplos y similitud semántica.

Ahora el escenario JAICF:

estado («createTask») {

activadores {

intent («createTask»)

}

acción {

val taskType = activator.getCailaSlot («taskType»). asJsonLiteralOr («»)

response.say («Volveré»)

response.aimybox? .¿respuesta? .action = «createTask»

response.aimybox? .¿respuesta? .intent = taskType.content

response.imybox? .¿responder? .correr {

datos[“taskName”] = activator.getCailaSlot («nombreTarea»). asJsonLiteralOr («»)

datos[“taskDescription”] = activator.getCailaSlot («taskDescription»). asJsonLiteralOr («»)

datos[“taskSentiment”] = activator.getCailaSlotBool («taskSentiment»). asJsonLiteralOr (verdadero)

datos[“taskDifficulty”] = activator.getCailaSlot («dificultad de la tarea»). asJsonLiteralOr («fácil»)

}

}

}

entretenimiento privado ActivatorContext.getCailaRequiredSlot (to: String): String =

getCailaSlot (k)?: error («Falta la ranura de Caila para la clave: $ k»)

entretenimiento privado ActivatorContext.getCailaSlot (k: String): String? =

caila ?. ranuras? .obtener (k)

entretenimiento privado ActivatorContext.getCailaSlotBool (k: String): ¿Booleano? =

caila? .slots? .get (k)? toBoolean ()

cadena de diversión privada? .asJsonLiteralOr (otro: String) = esto? .let {JsonLiteral (este)}?: JsonLiteral (otro)

diversión privada booleana? .asJsonLiteralOr (otro: booleano) = esto? .let {JsonLiteral (este)}?: JsonLiteral (otro)

Adjunte la intención usando el activador, recupere el tipo de las ranuras e insértelo en la intención. Ingrese un nombre y una descripción datosy no te olvides de marcar acciónpara que Aimybox del lado del cliente sepa qué habilidades tiene que elegir.

Ahora asegurémonos de que funcione. ¡Y así es! Encendemos el volumen y lo probamos.

Por supuesto, esta es una demostración tecnológica, y ciertamente puede presentar escenarios más útiles en términos del producto.

¡Discutiremos esto en nuestros próximos artículos!

Almacenamiento con capacidades JAICF

Almacenamiento Aimybox

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *