Blog de Amazon Web Services (AWS)

Construcción de un chatbot en Amazon Lex que consulta calificaciones en Moodle

Por Ulises Jiménez, Arquitecto de Soluciones AWS México

 

Los chatbots son un mecanismo interactivo para estar en comunicación con clientes y usuarios. En todos los ámbitos, en particular el de educación, se ha intentado incrementar la automatización para permitir que la comunidad escolar pueda consultar la información en el momento que requieran sin tener que establecer una interacción con una persona.

Esto aplica para los diferentes niveles educativos para habilitar en canales de uso cotidiano como sitios web, redes sociales o aplicaciones de mensajería instantánea con la posibilidad de mantener comunicación entre alumnos y docentes. Por ejemplo:

  • Un estudiante pueda recibir información de nuevas tareas o fechas de entrega.
  • Un profesor pueda saber cuando los estudiantes han subido una tarea y se encuentre lista para revisar.
  • Un estudiante pueda consultar calificaciones/documentos directamente desde un canal de uso cotidiano, como, por ejemplo, WhatsApp.

En este blog construiremos un chatbot con Amazon Lex que consulta las calificaciones de un estudiante en el sistema de gestión de aprendizaje (LMS) Moodle.

Figura 1. Diagrama de arquitectura

 

Amazon Lex es un servicio que permite crear interfaces conversacionales usando voz y texto. En este caso usaremos texto. Específicamente permite comprensión de lenguaje natural para reconocer la intención del texto. Vamos a usar AWS Lambda para conectar Amazon Lex con Moodle y luego integrar un canal con WhatsApp mediante Twilio.

El chatbot tendrá configurada una intención para reconocer cuando el usuario desea solicitar las calificaciones. Esta intención será resuelta en una función Lambda, y buscará en Moodle la matrícula de identificación del estudiante. Si la solicitud es desde el canal WhatsApp se validará el teléfono desde el cual se recibe el mensaje con el teléfono de contacto autorizado previo a la entrega de las calificaciones.

 

Prerrequisitos

 

Construcción

Preparación del ambiente de Moodle

Se requiere que Moodle tenga la capacidad de integración por un medio estándar de comunicación, en este caso servicios web tipo REST. Para ello podemos tomar como referencia la documentación de Moodle y seguir los siguientes pasos:

  1. Con un usuario tipo “Administrador”, Ir a “Características Avanzadas” y “Habilitar Servicios Web”.

Figura 2. Habilitación de Servicios Web en Moodle

 

  1. En “Administración del Sitio” luego en el “Modulo de Servicios Web” seleccionar “Administrar Protocolos” y habilitar el protocolo REST.

Figura 3.  Habilitación del protocolo REST en Moodle

 

  1. Generar un “Servicio de Integración” con los permisos necesarios para consultar usuarios y calificaciones, las funciones sobre las cuales se requiere permiso son:
    1. core_course_get_courses_by_field
    2. core_user_get_users
    3. gradereport_overview_get_course_grades

Figura 4. Asociación de permisos a “Servicio de Moodle”

 

  1. Generar una clave de acceso único que nos dará acceso al servicio en Moodle. Esto lo haremos en la función “Administrar Fichas, es importante resguardar este valor ya que será como podremos ejecutar las funciones en Moodle.

 

Creación de un secreto en AWS Secrets Manager

La configuración para el acceso a Moodle la vamos a almacenar en AWS Secrets Manager, entre otras funciones nos va a permitir extender esta solución al rotar el secreto cada cierto tiempo mediante una función Lambda. Para crear el secreto es necesario seguir el siguiente procedimiento:

  1. En la consola de AWS ir al servicio AWS Secrets Manager y elegir “Almacenar datos confidenciales nuevos”.
  2. Seleccionar “Otro Tipo de Credenciales” y agregar dos pares de llave-valor, “Endpoint” con la URL de nuestro Moodle, por ejemplo: https://www.mimoodle.edu/webservice/rest/server.php y “Token” con el valor de la clave de acceso que previamente generamos en Moodle.
  3. Guardar los datos confidenciales con el nombre de: “moodle/config”.

 

Creación de las funciones Lambda

Usaremos AWS Cloud Development Kit (CDK) para realizar el despliegue de las funciones Lambda necesarias para integrar con Moodle. AWS CDK permite definir componentes en un lenguaje como Python, Java o Go y luego CDK se encargará de transformar estas definiciones en plantillas y desplegar. Esto permite acelerar y facilitar la gestión de recursos al tener definida la infraestructura en código.

La lógica de interacción con Moodle residirá en las funciones Lambda.

Los pasos a partir de que ya tengamos CDK instalado son:

  1. Validar nuestra instalación de CDK
cdk --version
  1. Configurar la línea de comandos de AWS
aws configure
  1. Generar el proyecto de CDK
$ mkdir code
$ cd code
$ cdk init --language python Moodle
  1. Especificar los requerimientos
aws-cdk.core
aws-cdk.aws_lambda
aws-cdk.aws-lambda-python
aws-cdk.aws_secretsmanager
  1. Instalar los requerimientos
$ source .env/bin/activate$ pip install -r requirements.txt
  1. Generar una carpeta dentro del proyecto para colocar el código fuente de las funciones Lambda, la nombraremos “lambda_src”.
  2. Dentro de la carpeta “lambda_src” generar un archivo de requerimientos requirements.txt e incluir las dependencias necesarias.
boto3
botocore
  1. Implementar la función lambda moodle.py con los elementos clave.
# Este fragmento de código es un ejemplo solamente.
# No para uso en ambientes productivos
def dispatch(intent_request):
    intent_name = intent_request['sessionState']['intent']['name']
    response = None
    if intent_name == 'ConsultaCalificaciones':
        return ConsultaCalificaciones(intent_request)
    raise Exception('Intención con nombre ' + intent_name + ' no soportado')
 
def lambda_handler(event, context):
    response = dispatch(event)
    return response
 
def ConsultaCalificaciones(intent_request):
    sessionId = get_sessionId(intent_request)
    if "whatsapp" in sessionId:
        canal = "whatsapp"
        teléfono = sessionId.replace("whatsapp:","")
    else:
        canal = "web"
        teléfono = ""
    session_attributes = get_session_attributes(intent_request)
    slots = get_slots(intent_request)
    matrícula = get_slot(intent_request, 'matrícula')
    calificaciones = ConsultaCalificacionesMoodle(matrícula, canal, teléfono)
    text = "Las calificaciones para la matrícula "+matrícula+" son: "+calificaciones+""
    message =  {
            'contentType': 'PlainText',
            'content': text
        }
    fulfillment_state = "Fulfilled"   
    return close(intent_request, session_attributes, fulfillment_state, message)
   
def ConsultaCalificacionesMoodle(matrícula, canal, teléfono):
    usuario = ""
    calificaciones = []
    # obtener el usuario dada la matrícula
    params = {
        "criteria[0][key]": "idnumber",
        "criteria[0][value]": matrícula
    }
    params.update({"wstoken": moodle_token, 'moodlewsrestformat': 'json',
                   "wsfunction": "core_user_get_users"})
    r = post(moodle_endpoint, params=params)
    data = r.json()
    # validar si el usuario se encuentra registrado
    if  'exception' not in data and ('users' in data and len(data['users']) > 0):
        # obtener las calificaciones del usuario
        usuario = data['users'][0]['id']
        teléfonoregistrado = data['users'][0]['phone1']
        if canal!="whatsapp" or (teléfonoregistrado == teléfono):
            params = {
                "userid": usuario
            }
            params.update({"wstoken": moodle_token, 'moodlewsrestformat': 'json',
                           "wsfunction": "gradereport_overview_get_course_grades"})
            r = post(moodle_endpoint, params=params)
            data = r.json()
            # obtener el nombre de la materia de cada calificación
            records = data['grades']
            for record in records:
                curso = record['courseid']
                calificacion = record['grade']
                params = {
                    "field": "id",
                    "value": curso
                }
                params.update({"wstoken": moodle_token, 'moodlewsrestformat': 'json',
                               "wsfunction": "core_course_get_courses_by_field "})
                r = post(moodle_endpoint, params=params)
                data = r.json()
                materia = data['courses'][0]['displayname']
                calificaciones.append(
                    {"materia:": materia, "calificación": calificación})
            return calificaciones
        else:
            return "El teléfono de contacto no corresponde"
    else:
        return "No se han encontrado calificaciones"
  1. Incluir la definición del componente de despliegue en el archivo moodle_stack.py para la función Lambda.
# Este fragmento de código es un ejemplo solamente.
# No para uso en ambientes productivos
 
from aws_cdk import core as cdk
from aws_cdk import aws_lambda as _lambda
from aws_cdk import aws_secretsmanager as _secretsmanager
from aws_cdk import aws_iam as _iam
from aws_cdk.aws_lambda_python import PythonFunction
 
class MoodleStack(cdk.Stack):
    def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        # crear la función lambda
        moodle_funcion = PythonFunction(self, "FuncionMoodle",
                       entry="../code/lambda_src", 
                       index="moodle.py", 
                       handler="lambda_handler",
                       runtime=_lambda.Runtime.PYTHON_3_8,
                       environment={'LOG_LEVEL': 'INFO'},
                       timeout=cdk.Duration.seconds(19)
                       )
        secreto = _secretsmanager.Secret.from_secret_name_v2(
            self, "secret", "moodle/config")
        secreto.grant_read(moodle_funcion)
  1. Actualizar el contenido de la aplicación, es decir el archivo app.py de la carpeta raíz.
# Este fragmento de código es un ejemplo solamente.
# No para uso en ambientes productivos
 
#!/usr/bin/env python3</p><p>import os
 
from aws_cdk import core as cdk
from aws_cdk import core
 
from code.moodle_stack import MoodleStack
app = core.App()
MoodleStack(app, "MoodleStack",)

app.synth()
  1. Realizar el despliegue, lo cual usará Docker para generar las dependencias de Python empaquetar y desplegar la función Lambda.
$ cd ls
$ cdk deploy
  1. Realizar la validación en la consola de AWS que los recursos fueron desplegados en CloudFormation.

Figura 5. Recursos incluidos en la plantilla de CloudFormation

 

  1. También validar en el servicio de Lambda

Figura 6. Función Lambda desplegada

 

Creación del chatbot en Amazon Lex

Para crear el chatbot debemos realizar los siguientes pasos:

  1. En la consola de AWS localizar el servicio Amazon Lex.
  2. Dentro de Amazon Lex, en la opción de “Bots” seleccionar “Crear bot” y luego “Crear bot en blanco”.
  3. Como nombre del chatbot escribir “CalificacionesBot”, podemos elegir un rol IAM existente o en este caso elegir la opción para crear un nuevo rol.

Figura 7. Configuración del chatbot en Amazon Lex

 

  1. Dar clic en “Siguiente” y elegimos el Idioma “Español (LATAM)“.
  2. En la sección “Interacción por voz” seleccionar “Ninguna” ya que por el momento el chatbot atenderá peticiones de texto únicamente.
  3. Dar clic en “Terminar”.

Figura 8. Selección de idioma español para el chatbot

 

  1. Inmediatamente después tendremos la opción de configuración de intención, para lo cual como nombre del chatbot podemos escribir “ConsultaCalificaciones”.

Figura 9. Configuración de Intención de consulta

 

  1. En la sección de “Ejemplos de enunciados” seleccionar “Texto sin formato” lo cual va a permitir introducir los ejemplos de frases representativas sobre cuando el usuario desea consultar calificaciones, ingresar la siguiente lista:
    1. ¿Cuáles son mis calificaciones?
    2. Checar mis calificaciones
    3. Cuánto tengo en mis materias
    4. Quiero saber mis calificaciones
    5. ¿Me puedes ayudar con mis calificaciones?
    6. ¿Tienes mis calificaciones?
    7. ¿Cuál es mi calificación?
    8. ¿Me das mi calificación?

Figura 10. Enunciados ejemplo de intención

 

  1. Hacer clic en “Siguiente” e ir a “Agregar una ranura” para indicar la respuesta a la intención solicitando información adicional necesaria para poder cumplir la petición. Aquí es necesario nombrar a la ranura como “matrícula” y el tipo de ranura será alfanumérica. En la sección de ganchos de código marcar “Utilizar una función lambda para cumplimiento”.

Figura 11. Selección de Lambda para el cumplimiento de la intención

  1. Seleccionar “Guardar intención”.
  2. Seleccionar “Crear” para publicar una primera versión del chatbot. Especificar como nombre del chatbot “TestBot”.

Figura 12. Creación y prueba del chatbot

 

  1. Ir a la opción “Alias” dentro del Idioma “Español (LATAM)” y seleccionar en “Fuente” la función Lambda que previamente desplegamos con el componente de CDK, en versión dejar “$LATEST”. Este paso crea también una política de acceso al recurso Lambda para permitir invocaciones a la función desde el chatbot.

Figura 13. Asociación de la función Lambda

 

Configuración de Twilio

  1. Abrir una cuenta de prueba.
  2. En la sección “Dashboard” de Twilio ir a “Mensajes Programables”, seleccionar “WhatsApp” y activar la función de “Sandbox” (para la activación nos pedirá usar nuestro teléfono para validación).
  3. Regresar a la sección “Dashboard” y registrar los valores: “Account SID” y “Auth Token”.

Figura 14. Configuración de Twilio

 

Creación de Canal en Lex para conexión con Twilio

Una vez que se cuenta con Twilio y la función “Sandbox” habilitada se crea el canal en Amazon Lex, para ello seguiremos los siguientes pasos:

  1. En la consola de Amazon Lex, teniendo seleccionado el chatbot ir a la opción de “Integraciones de Canales” y seleccionar “Twilio SMS”.

Figura 15. Configuración del canal del chatbot

 

  1. En la sección de configuración adicional registrar los valores “Account SID” y “Account Token” respectivamente.

Figura 16. Configuración de conexión a Twilio en Amazon Lex

 

  1. Una vez que hayamos establecido los valores anteriores necesitamos recuperar el valor de “URL de Respuesta” dentro de la sección de “Integración de canales”.

Figura 17.  Punto de enlace del canal del chatbot

 

  1. El valor del campo “URL de respuesta” del canal la ingresamos en la configuración de la sección “Sandbox” de Twilio para WhatsApp.

Figura 18. Configuración de la dirección de recepción en Twilio

 

Prueba

Finalmente podemos enviar un mensaje por WhatsApp al número del servicio de Twilio, este enviará la petición a Amazon Lex por medio del canal, Amazon Lex ejecutará la función Lambda de cumplimiento y esta podrá validar la matrícula del estudiante en Moodle, así como recuperar sus calificaciones. Es importante mencionar que si la petición al Chatbot se recibe por medio de WhatsApp la función Lambda también valida que el número de teléfono desde donde se origina la petición sea un medio de contacto autorizado en Moodle para el estudiante.

Figura 19. Prueba del chatbot desde WhatsApp

 

Siguientes pasos

Si además de agregar funciones de Chatbot a Moodle deseas revisar su implementación, puedes consultar una arquitectura de referencia que incorpora características de alta disponibilidad así como una guía en español. De igual forma puedes probar un inicio rápido con Moodle o incorporar la instalación con la que ya cuentes, por otro lado te recomiendo un blog sobre intentos de cadena en Amazon Lex que te puede ayudar a profundizar en mecanismos de construcción del chatbot.

Respecto a la función Lambda que utiliza la configuración de Moodle es posible implementar el patrón de cache con AWS Secrets Manager y extensiones Lambda. Si recordamos la construcción del chatbot lo hemos hecho usando la consola de Amazon Lex para efectos demostrativos, pero es posible construir el chatbot de forma automatizada usando CDK mediante la solución de Serverless Bot Framework.

 

Limpieza de recursos

  1. Eliminar los recursos creados por CDK con el comando cdk destroy
  2. Eliminar el secreto con la configuración de Moodle cargado en AWS Secrets Manager
  3. Eliminar el chatbot desde la consola de Amazon Lex
  4. Eliminar la clave de acceso en Moodle y desinstalar los componentes instalados
  5. Eliminar la cuenta de prueba Twilio

 

Conclusión

En este blog hemos visto como de forma rápida se pueden incorporar funciones que desde un medio cotidiano de comunicación pueden acceder a recursos e integrar información relevante en el momento que los estudiantes lo deseen. Es una experiencia interactiva e instantánea a nuestros clientes. Es importante mencionar que no estamos limitados a ofrecer solamente servicios de consulta, sino que también podemos expandir para ofrecer los procesos de inscripción/reinscripción y no únicamente con Moodle si no con otros sistemas de control escolar o procesamiento de pagos.

 

 


Sobre el autor

Ulises Jiménez, es Arquitecto de Soluciones en AWS México, con experiencia previa en diferentes industrias actualmente apoya a clientes del Sector Público en acelerar su entrega de soluciones