Bonjour,
J'ai codé un petit ChatBot en javascript qui renvoie des réponses en fonction d'une chaîne de caractères.
Le code fonctionne mais j'obtiens des comportements inattendus des méthodes.
Les réponses du Bot :
Chaque réponse du Bot est un objet dont la clé est un sujet (ex: 'hello'), qui contient un tableau de réponses, une regex, et un état. Le but est de renvoyer les réponses du tableau à tour de rôle en fonction de l'état, quand une correspondance entre l'input utilisateur et la regex est détectée.
var responses = {
hello : {
res : ["Bonjour !", "Salut !"],
regex : /bonjour|salut|yo|^lu$|coucou/gi
},
imGood : {
res : ["Je vais très bien, merci.", "Très bien"],
regex : /comm?ent? (vas?[\s-]tu|all?ez[\s-]vous)/gi
}
};
La classe du Bot :
function Bot(name, langage) {
this.name = name;
this.userName = "{{userName}}";
this.langage = langage;
this.responses = {};
}
Méthodes d'initialisation du Bot :
Après instanciation du bot, il faudra charger les réponses et initialiser l'état de chaque réponse grâce à deux méthodes :
Bot.prototype.addResponses = function (responses) {
this.responses = responses;
}
Bot.prototype.initResponsesState = function () {
for (var r in this.responses) {
this.responses[r].state = 0;
}
}
Méthode d'affichage d'une réponse en fonction de sa clé :
Quand le sujet de réponse est choisi, il faut choisir la réponse en fonction de son état, et convertir les {{terme}} en this.terme.
Enfin, on incrémente l'état de la réponse, et si cet état+1 == le nombre de réponse max du sujet, on réinitialise l'état à 0.
Bot.prototype.speak = function (subject) {
var r = this.responses[subject].res; //tableau des réponses
var rState = this.responses[subject].state; //valeur de l'état de la réponse
var res = this.responses[subject].res[this.responses[subject].state]; //réponse
res = res.replace(/\{\{([^\}]+)\}\}/gi,"this.$1");
console.log("output: " + res);
this.responses[subject].state++;
if ((rState) == (r.length - 1)) {
this.responses[subject].state = 0;
}
}
Méthode pour formater l'input utilisateur :
Avant de comparer l'input utilisateur aux regex, je formate l'input en une chaine de caractères analysable.
Bot.prototype.translate = function (input) {
var output = input;
//mettre des espaces autour de la chaine
output = " " + output + " ";
//supprimer les doublons de caractères de ponctuation
output = output.replace(/([?!])+/gi, " $1 ")
//entourer d'espaces les caractères de ponctuation
.replace(/([?!])/gi, " $1 ")
//espaces multiples remplacés par un espace
.replace(/(\s)+/gi, "$1")
//supprimer les suites espace + caractère de ponctuation
.replace(/(\s[?!])\1+(?!\1)/gi, "$1")
//supprimer les espaces de début et de fin de la chaine
.replace(/^\s|\s$/gi, "");
return output;
}
Méthode pour trouver une réponse en fonction de l'input :
On trouve une réponse en fonction de l'input formaté en parcourant les regex de chaque sujet de réponse.
Bot.prototype.findResponse = function (input) {
var translatedInput = this.translate(input);
console.log("input: " + translatedInput);
for (r in this.responses) {
if(this.responses[r].regex.test(translatedInput)) {
this.speak(r);
}
}
}
Instanciation et initialisation du Bot :
var bot = new Bot("Bot", "fr");
bot.addResponses(responses);
bot.initResponsesState();
Tester le bot :
Dans le fichier javascript :
bot.findResponse("Comment vas tu ?");
//retourne : Je vais très bien, merci.
Avec un server Node et une interface qui utilise sockert.io :
io.on("connection", function(socket) {
socket.on("client-message", function(message) {
socket.emit("bot-message", bot.findResponse(message));
});
});
//retourne : Je vais très bien, merci.
Le fait de soumettre plusieurs fois de suite le même input ou simplement d'envoyer plusieurs inputs à la suite a des résultats innantendus.
Avec le fichier javascript seul :
bot.findResponse("Comment vas tu ?");
//retourne : Je vais très bien, merci.
bot.findResponse("Comment vas tu ?");
//ne retourne rien
bot.findResponse("Comment vas tu ?");
//retourne : Très bien
bot.findResponse("Comment vas tu ?");
//ne retourne rien
Une fois sur deux, le robot ne répond rien.
Après quelques tests, le problème semble venir de la méthode "findresponse" et plus particulièrement de la fonction .test() qui teste la regex et qui renvoie "true" seulement une fois sur deux.
J'ai donc tenté de remplacer le if(){} par un while(){} :
//méthode findResponse
for (r in this.responses) {
while(this.responses[r].regex.test(translatedInput)) {
this.speak(r);
}
Résultat, ça fonctionne sur mon fichier js, mais quand je rapatrie le code sur mon serveur Node, je retrouve le comportement innatendu (une réponse sur deux).
Pour finir... le plus rageant :
Finalement j'arrive à faire fonctionner le code correctement sur le serveur node, uniquement quand je place un console.log dans le while. Ce qui est illogique.
//méthode findrResponse
for (r in this.responses) {
while (this.responses[r].regex.test(this.translate(input))) {
//ici un console log
console.log(this.responses[r].regex.test(this.translate(input)));
return this.speak(r);
}
}
Donc, quelque chose m'échappe, si quelqu'un à le courage de comprendre ce qu'il peut se passer !
Merci,
Et au cas où, voici les deux versions du code (avec ou sans node.js) :
http://we.tl/3VPx36YhZV