Chatbot guiado por estado de la información


Una forma tradicional de programar chatbots es que estos se guíen por el estado de la información. Esto quiere decir que si el chatbot está tratando de hacer una tarea y dentro de esta se puede generar un estada que represente el estado de esta a través del uso de variables, podemos usar la representación de ese estado para poder guiar la conversación. El típico ejemplo es el del chatbot que vende boletos de avión. Para que el chatbot pueda hacer su tarea necesitamos que el usuario defina ciertos ‘campos’ cómo:

    STATE={
        'DEPARTURE_CITY':??,
        'DESTINATION_CITY':??,
        'DEPARTURE_TIME':??,
        'FLITHG_NUMBER': ??
    }

El estado de estos valores del estado irán guiando el tipo de preguntas e interacción. Al inició el chatbot no conocé no tiene ninguna de las variables por lo que puede hacer una pregunta genérica:

  • Chatbot: ¿A quieres volar?

La respuesta que le den tiene que ser procesada y dependiendo de los valores que regrese podemos ir llenando los valores. Con esto en mente podemos ir llenando valores de nuestro estado y decidir la siguiente pregunta. Por ejemplo si el usuario responde:

  • Usuario: Quiero un vuelo a Monterrey a las 13 horas

Entonces de esa oración podemos recuperar la siguiente información:

    STATE={
        'DEPARTURE_CITY':??,
        'DESTINATION_CITY':'monterrey',
        'DEPARTURE_TIME':'13:00:00',
        'FLITHG_NUMBER': ??
    }

Y para lo cual el chatbot podría generar la siguiente pregunta:

  • Chatbot: ¿De dónde quieres salir?

Lo anterior motivado por que no tenemos la ciudad de partida. Una vez identificada esta información es posible dar opciones al usuario. Por ejemplo:

  • Chatbot: Podría ser el vuelo de las 13:10 o el vuelo de las 14:00, cúal eliges

Con la respuesta del usuario es posible culminar con toda la información de estado y agenda su viaje. Y preguntarle si necesita otro vuelo para comenzar de nuevo.

Análisis de la información

Una parte fundamental del esquema guiado por la información, es poder identificar la información del turno del usuario. Esta tarea tiene que ver con entendimiento del lenguaje humano y se relaciona con el área conocida como Procesamiento del Lenguaje Natural hay varios esquemas que se pueden usar para poder identificar lo que el usuario quiere decir:

  • Identificar entidades nombradas: esto corresponde a identificar las categorías de ciertas palabras en las oraciones. Por ejemplo: personas, lugares, objetos. Un análisis de ese estilo puede observase en la plataforma de lenguaje natural de Google (poner una oración y cliquear en Entities). Uno podría pensar en crear etiquetas especiales para la tarea por ejemplo: ciudad, fecha, hora, etc. Una vez identificada las entidades uno hace reglas de correspondencia entre estas y nuestro estado de información.

  • Crear un árbol sintáctico: esto corresponde a hacer un análisis profundo de la oración/elocución del usuario y generar un árbol de cómo se relacionan las palabras entre si. Un análisis de ese estilo puede observase en la plataforma de lenguaje natural de Google (poner una oración y cliquear en syntax). Una vez hecho el análisis uno hace reglas de correspondencia entre estas y nuestro estado de información.

  • Responder de forma automática: esto ha tomado gran interés en el último año y corresponde ha hacer un tipo sistema de traducción, en dónde el sistema ve la entrada y aprende a generar una respuesta de forma automática. Esto está basado en un modelo de aprendizaje profundo llamado LSTM.

  • Crear expresiones regulares: Este método aunque algo tradicional tiende a presentar buenos resultados. Consiste en usar las expresiones regulares para definir patrones de búsqueda, y si la expresión regular hace un match con lo dicho por el usuario es posible recuperar la información.

Expresiones regulares en python

Python tiene una librería para expresiones regulares denominada re. Invocarla es muy fácil:

    import re

Una expresión regular se define a través de la operación de compilar, de la siguiente forma:

    salida_0 = re.compile('de (.*) a las')

Lo que nos dice la expresión regular es que todos los caracteres ( el símbolo *) que vengan después de un de y antes de un a las será agrupado (uso de paréntesis ) y podrán ser recuperados después.

Hay dos operaciones básicas con las expresiones regulares: match y search.
En la primera el patrón tiene que corresponder con toda la oración que le pasemos, con la segunda operación el sistema buscará la región en dónde el patrón corresponde. En ambos casos la operación regresa un objeto match que permite recuperar grupos y nos dice si el patrón estuvo presente en la cadena.

import re
salida_0 = re.compile('de (.*) a las')
m_match  = salida_0.match('un vuelo de monterrey a las 13 horas')
print(m_match)
m_match  = salida_0.match('de monterrey a las')
print(m_match)
print(m_match.group(1))
m_search = salida_0.search('un vuelo de monterrey a las 13 horas')
print(m_search)
print(m_search.group(1))

En el primer caso, nos regresa que no pudo hacer el match, porque la cadena incluye más información que la deseada como es la parte de un vuelo y 13 horas. Si cortamos esa información, la segunda opción si puede hacer una correspondencia y en el grupo uno encontramos a la subcadena monterrey. El tercer ejemplo muestra el efecto de usar search ahí no es necesario que la correspondencia sea total, pero parcial. Al igual que el ejemplo anterior podemos encontrar en el grupo el segmento que nos interesa.

Ligas importantes de expresiones regulares

Parseando fechas

Además de las expresiones regulares_ python_ posee una librería para convertir expresiones de tiempo en lenguaje natural en objetos de fecha y hora nativos de python (DATETIME). Esta librería de llama dateparser e invocarla es muy fácil.

import dateparser

dateparser.parse('13 horas')
dateparser.parse('viernes a las 12 horas')
dateparser.parse('lunes a las 5pm')

Cuando la cadena identifica una palabra que no la tiene contemplada nos regresa nada. Una estrategia sería eliminar palabras que rompen al sistema como siguiente o el.

El objeto que regresa la función es del tipo datetime que es difícil de almacenar en diferentes tipos de archivos. Para poder manipularlo podemos convertirlo a cadena y controlar el formato.

import dateparser

dt=dateparser.parse('lunes  a las 5pm')
dt.strftime('%Y-%m-%d %H:%M:%S')
dt.strftime('%Y-%m-%d')
dt.strftime('%H:%M:%S')

Ligas importantes de dateparser