Projet débuté en 2014
Etat du projet : en cours - pas encore publié

Projet d'agent conversationnel ou "chatbot", "chatterbot", codé en javascript avec Node.js et Socket.io

objectifs

  • ne pas utiliser de dictionnaires de mots ni d'analyse grammaticale (pourquoi ? lire cet article)
  • s'éloigner au maximum du principe mot-clé = réponse tout en conservant un système relativement simple
  • se diriger vers un système d'apprentissage simple (j'en suis encore loin)

technologies

  • Node.js (serveur javascript)
  • Socket.io (transmission données au client via websockets)

fonctionnement

Fonctionnement général

Le bot a 2 modules :

  • La "ram"
  • La "memory"

La ram est une sorte de mémoire vive qui permet de retenir les patterns détectés dans les phrases de l'utilisateur (motifs) à différentes échelles de temps. Elle stocke également le nom du bot, et les informations sur l'utilisateur (IP, socket, nom de l'utilisateur, préférences...)

//ram object
global.ram = {
    "botName"           : "",
    "user"              : [],
    "tempFlags"         : [], //drapeaux temporaires
    "tempSuperFlags"    : [], //supers-drapeaux temporaires
    "lastSuperFlags"    : [], //supers-drapeaux phrase précédente    
    "historySuperFlags" : [] //historique superdrapeaux
};

La memory est structurée avec différents fichiers .json, un format permettant de stocker des informations et des correspondances :

  • des motifs associés à des drapeaux : flags.json
    Ce fichier permet de détecter des "concepts" à partir de motifs (« avec moi », « aimes-tu ? », « avec quelqu'un »...). Les motifs étant des expressions régulières. Quand le pattern est reconnu, le ou les drapeaux associés sont injectés dans la mémoire vive ram.tempFlags

Le rôle de ce fichier est de faire des supositions sur le sens de la phrase utilisateur en choisissant des concepts possibles. Certains motifs soulèvent des concepts différents (ambiguïté : certains mots ont plusieurs sens, c'est pourquoi ce fichier liste en majorité des patterns associant 2 éléments qui forment un sens, plutôt qu'un seul mot, sauf quand ce mot a déjà un sens assez précis).

[
    {
        "regex" : "avec moi",
        "flags" : ["{{withMe}}"]    
    }
    {
        "regex" : "tu aimes? |aimes?[\\s-]tu ",
        "flags" : ["{{doYouLike}}"]    
    },
    {
        "regex" : "avec quelqu'un",
        "flags" : ["{{withSomebody}}"]
    }    
]    

Les drapeaux sont en anglais pour permettre un jour plus facilement la traduction du robot. Cependant, les drapeaux forment une sorte de langage à part entière.

Le fait que les motifs soient détectés avec des expressions régulières me semble problématique pour pouvoir un jour traduire le robot ou pour permettre à des néophytes de construire leur propre robot. Elles permettent cependant de gagner du temps, du code et de permettre des fautes d'orthographe. Il reste possible d'associer une phrase ou un mot à un drapeau, mais le bot fonctionnera mieux si l'on garde à l'esprit le rôle de chaque fichier. Celui ci étant de détecter des "briques de sens".

  • des supers-drapeaux associés à des drapeaux superflags.json
    Ce fichier est une liste d'associations entre plusieurs drapeaux et 1 super-drapeau. Son rôle est de limiter l'ambiguité. Il permet de vérifier quelles sont les "briques de sens" qui forment un sens, et celles que l'on doit abandonner.

Si un super-drapeau est associé à des drapeaux qui apparaissent tous dans la mémoire vive ram.tempFlags, alors ce super-drapeau est injecté dans la mémoire vive ram.tempSuperFlags

Ce fichier permet de recontextualiser la première couche de drapeaux détectée.

[
    {
        "flags"         : ["{{youAre}}", "{{beautiful}}"],
        "superFlags"    : ["{{youAreBeautiful}}"]
    },
    {
        "flags"         : ["{{your}}", "{{name}}", "{{funny}}"],
        "superFlags"    : ["{{funnyName}}"]
    }    
]    
  • des réponses associées à des supers-drapeaux responses.json
    Ce fichier établit des correspondances entre un groupe de réponses possibles et un super-drapeau.
    Un score est ensuite calculé pour chaque groupe de réponses.
    En général le score sera de 0 si aucune correspondance et 1 si le super-drapeau associé au groupe est égal au super-drapeau stocké dans ram.tempSuperFlags
[
    {
        "flags" : ["{{whatsYourName}}"],
        "res" : ["Je m'appelle {{botName}}", "Mon nom est {{botName}}", "J'ai choisi le nom {{botName}}"]
    },
    {
        "superflags" : ["{{funnyName}}"],
        "res" : ["Tu trouves que mon nom est drôle ? Moi aussi, c'est pour ça que je l'ai choisi", "Je suis contente que ça t'amuse"]
    } 
]    

Chaque réponse est un tableau car le bot ne répond pas la même chose si le pattern a déjà été reconnu.

A chaque tour, les drapeaux circulent dans la "ram" et changent de couche temporelle. Plus ils sont "récents", plus ils ont d'importance.

Contexte

Je tente de finaliser un module permettant de répondre à des questions liées au contexte comme « pourquoi ? », « combien ? ». Le contexte étant stocké dans la mémoire vive ram.lastSuperFlags pour la réponse précédente.

Ces questions injectent un super-drapeau spécial {{CALLBACK}} qui aura pour conséquence de recharger l'ancien super-drapeau dans les drapeaux temporaires. Le prochain super-drapeau est donc calculé en fonction des supers-drapeaux {{question}} + {{dernier super-drapeau}}.

{{why}} {{CALLBACK}} = {{why}} {{areYouAngry}} = {{whyAreYouAngry}} = réponse

Le but est d'affiner ce système qui est très complexe étant donné que les utilisateurs posent souvent des questions hors contexte. A terme, j'espère pouvoir mieux distinguer les sujets temporaires, les sujets plus généraux, donc les questions liées à la réponse précédente voire une réponse plus ancienne, ou les questions directes.