Mi primer chatbot


Aunque últimamente no tan populares los chatbot basados en reglas tuvieron un auge mayor hasta pocos años. Los sistemas de reglas generalmente se usan para definir la inteligencia del sistema. La estrategia que siguen es muy sencilla, se crea una lista de patrones y a esos patrones se asocia una respuesta. Si el turno del usuario hace una correspondencia con alguno de los patrones entonces se construye la respuesta asociada.

Para los patrones existen varios niveles:

  • superficial: sólo se fijan en la información léxica (palabras). Por ejemplo, en casi todo chatbot la palabra hola podría disparar la respuesta hola de regreso
  • semática: se fija en el significado de lo dicho por el usuario. Por ejemplo, en un chatbot que recibe ordenes, para poder ejecutar el comando: envíá correo a compras, el chatbot tiene que entender que el comando es envia-correo y qué el destinatario es el correo del departamento de compras
  • discursiva: este patrón pone atención al flujo de la conversación. Por ejemplo, en un chatbot para comprar boletos de avión si alguien mencionó que no le gustaba vuelos en la mañana, entonces el chatbot tiene que recordar ese aspecto.

El lenguaje AIML

AIML es un lenguaje de marcado (markup language) que se especializa en la definición de reglas para chatbots basado en XML. Uno puede escribir reglas en los tres niveles , aunque su fortaleza recae en el nivel superficial de patrones.

Un archivo aiml luce de la siguiente forma:

    <aiml version = "1.0.1" encoding = "UTF-8"?>
    <category>
      	<pattern> HOLA </pattern>
         <template> HOLA USUARIO </template>
        </category>
    </aiml>

El archivo comienza con la estructura aiml en donde se especifica la versión de AIML y en encoding del archivo. Después viene una estructura de category que define los patrones, en este caso solo hay uno que está definida por el patrón (pattern) HOLA y la plantilla de respuesta template que nos indica que cada vez que el usuario diga hola la responderá con la frase HOLA USUARIO. En este caso esta categoría se trata de una regla del nivel superficial ya que sólo se fija en la palabra hola.

Comodín *

La definición del patrón puede ser más flexible a través del uso del signos * que lo podemos pensar que significa cualquier secuencia de letras.

   <category>
      <pattern>UN * ES UNA *</pattern>
      <template>
         ¿Cuándo <star index = "1"/> no puede ser una <star index = "2"/>?
      </template>
   </category>

Este patrón está pensado para cuando el usuario dice una fresa es una fruta en este caso el chatbot contestará haciendo la pregunta ¿cuando una fresa no puede ser una fruta?. A este nivel esta funcionalidad nos permite manipular de una manera muy sencilla tratar el significado del turno del usuario.

Variabilidad en la respuesta

Muchas veces queremos dar la opción que nuestro chatbot responda de múltiples formas, no que siempre de la misma respuesta. Para lograr eso AIML da la opción de RANDOM para definir la plantilla de la siguiente forma:

   <category>
      <pattern>HOLA</pattern>
      <template>
      	   <random>
      	   	<li>hola, ¿cómo estás?</li>
      	   	<li>hola, es un  bonito día ¿verdad?</li>
      	   	<li>hola y adios</li>
      	   </random>
      </template>
   </category>

Esta opción nos permite manipular la estrategia de inteligencia para que el sistema no sea muy repetitivo.

Asignar y recuperar valores

Un mecanismo que nos proporciona AIML para lidiar con aspectos discursivos es la opción de asignar y recuperar valores a variables. Esto lo logramos a través de las etiquetas SET y GET.

   <category>
      <pattern>mi nombre es *</pattern>
      <template>
         Hello <set name = "nombre"> <star/>! </set>
      </template> 
   </category> 
   
   <category>
      <pattern>adios</pattern>
      <template>
         Adios <get name = "username"/>
      </template> 
   </category> 

De esta forma podemos pasar información de un turno a otro.

Sin embargo, a veces no deseamos notificar al usuario que estmaos guardando la variable, para eso usamos las etiquetas THINK.

   <category>
      <pattern>mi nombre es *</pattern>
      <template>
         Hello <think><set name = "nombre"> <star/>! </set></think>
      </template> 
   </category> 
   
   <category>
      <pattern>adios</pattern>
      <template>
         Adios <get name = "username"/>
      </template> 
   </category> 

Un chat básico

Nuestro primer chatbot estará basado en el siguiente proyecto:

pyaiml_charbot

Este chatbot como su nombre lo dice está basado en pyaiml, pero además está basado en un servicio web. Implementa los siguientes componentes:

  • Interface, del tipo web
  • Historía de la conversación, la registra en la pantalla y la guarda en una base de datos
  • Inteligencia definida por reglas en el lenguaje AIML
  • Turnos simples, por cada intervensión del usuario se da una respuesta

El chatbot utiliza la siguiente tecnología

  • pyAIML librería para desarrollo de servicios web en python
  • Flask librería para desarrollo de servicios web en python
  • tinydb librería que hace una base de datos local usando una estructura de json
  • socketio libería de comunicación de sockets para python y javascript

El proyecto tiene la siguiente estructura

    .
    ├── aiml			Directorio para archivos AIML 
    │   └── mibot.aiml		Inteligencia básica por default
    ├── app.py			Programa principal
    ├── LICENSE			Licencia GNU 3.0
    ├── README.md		Instrucciones para instalar
    ├── static			Directorio con archivos estáticos para el sitio
    └── templates		Directorio con plantillas html
        ├── about.html		Plantilla con información del sitio web
        ├── chat.html		Plantilla principal para el chat (incluye JS)
        ├── default.html	Plantilla default (encabezado y footer)
        └── home.html		Plantilla de home, pregunta por el nombre

El código principal consiste de las siguientes llamadas:

    k = aiml.Kernel()

    @app.route('/')
    def login():
      return render_template('home.html')

    @app.route('/about')
    def about():
      return render_template('about.html')


    @app.route('/chat')
    def chat():
      username=request.args['username']
      if not len(username)>0:
	  username='desconocido'
      return render_template('chat.html',username=username)

    @socketio.on('message', namespace='/ask')
    def receive_message(message):
	ans=k.respond(message['data'])
	emit('response', {'data': ans.lower()})

La primer línea activa a la libería aiml a través de un nucleo (kernel) K. La siguiente defini que cuando se solicite la dirección / se rendereé la página home.html; la siguientes funciones hace algo similar con respecto a about y chat. Con diferencia que en la función chat se recupera el nombre de usuario insertado en la página home. Finalmente, la función _receivemessage es la encargada de obtener el mensaje del usuario y generar una respuesta usando el nucleo de AIML K. Notar que usamos un mecanismo de socket porque no se rendereá toda la página por mensaje, pero se actualiza.

En el codigo final, uno puede encontrar más instucciones relacionadas con guardar las conversaciones en una base de datos, y manejar los argumentos de la línea de comandos.