Nodos
Los chatbots se construyen en base a nodos. En Botlang, un nodo es una función que recibe un contexto (diccionario) y un mensaje, y retorna una tupla (c, m, n)
. Donde c y m son el contexto y mensaje de salida, respectivamente, y n es el nodo siguiente.
De este modo, cuando un usuario envía un mensaje al chatbot, este se le pasa al nodo correspondiente (dependiendo del flujo definido) junto al contexto (datos del usuario y otras variables almacenadas). El nodo luego entrega una respuesta y un puntero al nodo a invocar al recibir un nuevo mensaje (puede ser el mismo). Esto permite implementar flujos de conversación complejos.
Bot node¶
En Botlang los nodos son valores de tipo bot node (funciones con ciertas particularidades). La tupla de salida se encapsula en un valor de tipo node result.
El flujo de un chatbot comienza con el valor del nodo ubicado al final del código principal del chatbot (punto de entrada).
Ejemplo de flujo simple:
1 2 3 4 5 6 7 8 9 10 11 12 | (define node-b (bot-node (context message) (if (equal? message (plain "chao")) (node-result context "Adiós" end-node) (node-result context message node-b) ) ) (define node-a (bot-node (c m) (node-result c "Hola! Te remedo." node-b) ) node-a |
Y la conversación:
1 2 3 4 5 6 7 8 9 10 11 | >> Hola Hola! Te remedo. >> Qué tal? Qué tal? >> Deja de imitarme Deja de imitarme >> Chao Adiós |
Cuando el flujo encuentra un nodo terminal (en este caso end-node), la conversación finaliza. Si el usuario vuelve a enviar un mensaje, este cae en el nodo de entrada.
Nodos terminales¶
Hay dos tipos:
end-node
nodo terminal por defecto es end-node.(terminal-node arg)
termina la sesión con un mensaje de estado (arg). Dicho mensaje puede usarse para controlar el paso a un asistente humano, por ejemplo.
slots-node¶
Slots esta pensado para ahorrar la cantidad de nodos en un bot, y para un comportamiento de toma
de datos en la que permite al usuario cambiar de tema y luego seguir íngresando datos.
Un slot-node
recibe un nombre, contexto y mensaje y en su cuerpo puede tener tres tipos
de "casos":
-
before
recibe un bloque que se ejecuta antes del comportamiento normal del nodo. -
digress
recibe un bloque que se evalúa para identificar un "cambio de tema". Si dicho valor es un node-result, el slot-node lo retorna. Si el valor es nil, el slot-node continúa con la ejecución de los slots. Cuando se activa digress el siguiente mensaje del usuario volverá al slot-node. -
slot
recibe una llave, contexto, cuerpo y opcionalmente un mensaje. Ejecutará el cuerpo y, en caso que su valor sea distinto de nil, lo asignará a llave en el contexto. Si se entrega el mensaje opcional, el slot obligará a que el valor asociado a llave sea distinto de nil. Si no lo es, el slot-node retornará el mensaje y apuntará a sí mismo como nodo siguiente. -
then
recibe un cuerpo que se evalúa luego de ejecutar todos los slots y retorna un node-result.
A continuación un ejemplo de uso:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | (defun default-behavior (c m) (cond [(match? "ctm" m) (node-result c "No me insultes" (return end-node)) ;retorna al slot-node ] [(match? "chao" m) (node-result c "Adiós" end-node)] [else nil] ) ) (slots-node node2 (c m) [slot confirm c (cond [(match? "si" m) #t] [(match? "no" m) #f] [else nil] ) "¿Confirmas tu pedido?" ] [then (begin (remove! c "type") (remove! c "size") (remove! c "with-cream") (if (get c "confirm") (node-result c "Confirmado" end-node) (node-result c "Bueno, ¿qué café quieres?" node1) ) )] ) (slots-node node1 (c m) [digress (default-behavior c m)] [slot type c (match ".*(americano|latte|mocha).*" m 1) "¿De qué tipo quieres tu café?" ] [slot size c (match ".*(chico|mediano|grande).*" m 1) "¿De qué tamaño quieres tu café?" ] [slot with-cream c (cond [(match? "si(\\s.*)?" m) #t] [(match? "no(\\s.*)?" m) #f] [else nil] ) "¿Lo quieres con crema?" ] [then (node-result c (append "¿Tu pedido es un " (get c "type") " " (get c "size") " " (if (get c "with-cream") "con crema?" "sin crema?") ) node2 ) ] ) node1 |
Conversación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | >> quiero un americano grande ¿Lo quieres con crema? >> chao Adiós >> quiero un americano chico ¿Lo quieres con crema? >> si ¿Tu pedido es un americano chico con crema? >> no Bueno, ¿qué café quieres? >> un latte ¿De qué tamaño quieres tu café? >> ctm No me insultes >> Bueno, lo quiero mediano ¿Lo quieres con crema? >> no ¿Tu pedido es un latte mediano sin crema? >> lalalala ¿Confirmas tu pedido? >> miau ¿Confirmas tu pedido? >> si Confirmado |