Skip to content

LLM

LLM integration with BRMS (Business Rule Management Systems)

Introduction

  • LLMs (Large Language Models) have revolutionized natural language processing (NLP).

    • Understanding complex structures
  • BRMS (Business Rule Management Systems) tools for managing complex business rules.

    • Critical systems

But

Benefits of BRMS-LLM integration

  • Increased compliance: LLM responses aligned with internal policies.

  • Reduced errors: expert system-based decisions.

  • Flexibility: combining the power of LLM with the rigor of BRMS. Complex reasoning

  • Clarification of decision-making processes

Architecture

Architecture As you can see from the image above, the chatbot uses LLM to generate responses, but for one specific case, it uses BRMS to provide an expert answer.

Achi_code The architecture works like an agent, using the BRSM as a tool to make a decision in a specific case. It is a kind of artificial intelligence agent that uses function calls.

Integration workflow

We will describe a possible method of integrating business decision processes within a chatbot using a BRMS.

Decision process modeling

The decision process in a BRMS system can be modeled as a set of logical rules and conditional actions, often represented by a directed graph or decision tree. This mathematical model can be used to formalize the various stages in the decision-making process, based on inputs (data supplied by the user) and defined business rules.

Consider a set of rules \(R = \{r_1, r_2, \dots, r_n\}\), where each rule \(r_i\) can be represented by a logical implication of the form :

\[ r_i: \text{If } C_i \rightarrow A_i \]

where \(C_i\) is a set of conditions (premises) and \(A_i\) is an action or decision to be taken if the conditions are met.

The decision process can be described by an algorithm that sequentially evaluates each rule in order of priority \(P(r_1) > P(r_2) > \dots > P(r_n)\). For a given input \(x\), the decision process determines the rule \(r_i\) such that the conditions \(C_i\) are satisfied, and applies the action \(A_i\).

Let's formulate this mathematically:

\[ \text{Find } r_i \text{ such that } x \models C_i \text{ and apply } A_i \]

where \(x \models C_i\) means that \(x\) satisfies the conditions \(C_i\).

If several rules \(r_i\) and \(r_j\) are satisfied simultaneously (i.e. \(x \models C_i\) and \(x \models C_j\)), then the order of priority \(P\) determines which action is chosen. This can be expressed as :

\[ A = A_k \text{ where } k = \arg\max_{i}(P(r_i) \text{ such that } x \models C_i) \]

This model can be extended by incorporating probabilities associated with each rule, to handle scenarios where the rules are not strictly deterministic. Finally, for integration with a wide language model (LLM), these rules can be used to filter or adjust LLM output, ensuring that any response generated conforms to predefined business rules.

Decision process integration modeling

Once the decision process has been modeled, we can integrate it into a chatbot.

Information extraction

First, the model extracts the relevant information from the user input text \(x\). Suppose the input is of the form: "Manon's insurance, she's 34 and lives in Paris ”. The aim is to extract the specific information, i.e. surname \(n\), first name \(p\), age \(a\), and address \(d\). This process can be modeled by a function \(f_{ext}\) such that :

\[ (n, p, a, d) = f_{ext}(x) \]

where \(f_{ext}\) is an entity extraction function.

Building the decision payload

Once the information has been extracted, it is used to build a payload that will be sent to a BRMS system. The payload is a data structure represented mathematically by a vector \(\mathbf{v}\) :

\[ \mathbf{v} = (n, p, a, d, \text{ID}, \text{Amount}) \]

where \(\text{ID}\) is a relevant identifier and \(\text{Amount}\) is an initial value.

Business rule evaluation

The vector \(\mathbf{v}\) is sent to a rules engine \(R\), represented by a function \(f_{BRMS}\) :

\[ \text{Decision} = f_{BRMS}(\mathbf{v}) \]

where decision is the BRMS output after rule evaluation.

Validation and Response

Finally, the decision is validated and a response is constructed:

\[ \text{Response} = f_{resp}(\text{Decision}) \]

If an error is detected:

\[ \text{Error} = f_{err}(\text{Decision}) \]
Process summary
\[ (n, p, a, d) \xrightarrow{f_{ext}} \mathbf{v} \xrightarrow{f_{BRMS}} \text{Decision} \xrightarrow{f_{resp}} \text{Response} \]

Integration Proposal

API integration
def payload_construction(nom:str,prenom:str,age:int,adresse:str, maisonPrice:int, sinistre:str = "Incendie"):

    payload = {
        "personne": {
            "name": prenom,
            "lastName": nom,
            "address": adresse,
            "disaster": sinistre,
            "age": age,
            "maisonPrice": maisonPrice
            }
        }

    return payload
Extract information from user input
def brmsCall(user_input:str)->str:
    request = (
                "Prompt + few shot learning: "

                "Voici la phrase cible: " f"{user_input}"
                )

    elements = ollama.generate(
        model="mistral:latest",
        prompt=request,
        stream=False,
        options= {'temperature': 1}
    )
    elements2 = re.sub(r'[^a-zA-Z0-9;]', '', elements["response"])
    elements3 = [elem.strip() for elem in elements2.split(';') if elem.strip()]

    payload = pc.payload_construction(
        nom=elements3[0] if len(elements3) > 0 and elements3[0] else None,
        prenom=elements3[1] if len(elements3) > 1 and elements3[1] else None,
        age=extract_number(elements3[2]) if len(elements3) > 2 and elements3[2] else None,
        adresse=extract_number(elements3[3]) if len(elements3) > 3 and elements3[3] else None,
        maisonPrice= extract_number(elements3[4]) if len(elements3) > 4 and elements3[4] else None
    )

    api = ap.ApiCall(url="http://localhost:8080/ruleflow", payload=payload, headers={'Content-Type': 'application/json'})
    test_completion, erreur = api.test_arguments()

    if erreur:
        sentence = "Tu dois indiquer à ton interlocuteur que tu ne peux pas répondre pour la raison suivante : ", test_completion, "Il dois ipérativement te donner toutes les informations dans l'ordre si possible. \n\n Répond uniquement que tu ne peux pas répondre sans ces informations primordiales! Aide toi des raisons données pour expliquer. \n\n"
        solve = False
    else:

        resApi:dict = api.call_api()  # Appel API

        if isinstance(resApi, dict) and 'res' in resApi:
            answer = resApi['res'].get('montantIndemnisation', "Indemnisation inconnue")
        else:
            answer = "Erreur : réponse invalide de l'API"

        sentence = f"D'après les informations renseignées parl'utilisateur, le prix de l'indemnité calculée par le modèle est : {answer}"
        solve = isinstance(resApi, dict) and 'res' in resApi  # True si la clé 'res' est bien présente

    print(f"solve : {solve}")

    return sentence, elements3, solve
Keeping information in memory
if self.assurance_phase:
    assurance_output = "Voici le retour du système expert :
" + sentence
    if solve_statue:
        self.memoire_contextuel_assurance = ""
    else:
        self.memoire_contextuel_assurance = f"Ces éléments sont à retenir : {liste_element}"
else:
    assurance_output = ""
    self.memoire_contextuel_assurance = ""

Demo

This demo is in french :

All the code is available on Github:


ChatBot Memory

Nous présentons ici une approche pour la gestion de la mémoire à court terme dans les chatbots, en utilisant une combinaison de techniques de stockage et de résumé automatique pour optimiser le contexte conversationnel. La méthode introduite repose sur une structure de mémoire dynamique qui limite la taille des données tout en préservant les informations essentielles à travers des résumés intelligents. Cette approche permet non seulement d'améliorer la fluidité des interactions mais aussi d'assurer une continuité contextuelle lors de longues sessions de dialogue. En outre, l'utilisation de techniques asynchrones garantit que les opérations de gestion de la mémoire n'interfèrent pas avec la réactivité du chatbot.

Modélisation Mathématique de la Gestion des Conversations

Dans cette section, nous formalisons mathématiquement la gestion de la mémoire de conversation dans le chatbot. La mémoire est structurée comme une liste de paires représentant les échanges entre l'utilisateur et le bot.

Structure de la Mémoire de Conversation

La mémoire de conversation peut être définie comme une liste ordonnée de paires \((u_i, d_i)\), où \(u_i\) représente l'entrée utilisateur et \(d_i\) la réponse du bot pour le \(i\)-ième échange. Cette liste est notée \(\mathcal{C}\) :

\[ \mathcal{C} = [(u_1, d_1), (u_2, d_2), \ldots, (u_n, d_n)] \]

\(n\) est le nombre total d'échanges dans l'historique actuel.

Mise à Jour de la Mémoire

Lorsqu'un nouvel échange se produit, une nouvelle paire \((u_{n+1}, d_{n+1})\) est ajoutée à la mémoire. Si la taille de \(\mathcal{C}\) dépasse une limite maximale prédéfinie \(M_{\text{max}}\), l'échange le plus ancien est retiré :

\[ \mathcal{C} = \begin{cases} \mathcal{C} \cup \{(u_{n+1}, d_{n+1})\}, & \text{si } |\mathcal{C}| < M_{\text{max}} \\ (\mathcal{C} \setminus \{(u_1, d_1)\}) \cup \{(u_{n+1}, d_{n+1})\}, & \text{si } |\mathcal{C}| = M_{\text{max}} \end{cases} \]

Comptage des Mots

Pour gérer l'espace de mémoire et décider quand la compression est nécessaire, nous calculons le nombre total de mots \(W(\mathcal{C})\) dans la mémoire :

\[ W(\mathcal{C}) = \sum_{(u_i, d_i) \in \mathcal{C}} (|u_i| + |d_i|) \]

\(|u_i|\) et \(|d_i|\) sont respectivement le nombre de mots dans \(u_i\) et \(d_i\).

Compression de la Mémoire

Lorsque \(W(\mathcal{C})\) dépasse un seuil \(W_{\text{max}}\), la mémoire est compressée pour maintenir la pertinence du contexte. Cette compression est réalisée par un modèle de résumé \(\mathcal{S}\), tel que BART :

\[ \mathcal{C}_{\text{compressed}} = \mathcal{S}(\mathcal{C}) \]

\(\mathcal{C}_{\text{compressed}}\) est la version résumée de la mémoire, réduisant le nombre total de mots tout en préservant l'essence des interactions passées.

Intégration dans le Modèle de Langage

Le modèle de langage utilise le contexte compressé pour générer des réponses pertinentes. Le prompt \(P\) utilisé par le modèle est construit comme suit :

\[ P = f(\mathcal{C}_{\text{compressed}}, \text{contexte}) \]

\(\text{contexte}\) est le contexte supplémentaire récupéré à partir d'un pipeline RAG, et \(f\) est une fonction de concaténation qui prépare le texte pour le modèle.

Cette approche assure que le chatbot dispose toujours d'un contexte conversationnel à jour, permettant des interactions plus naturelles et engageantes avec l'utilisateur.

Implémentation du Code pour la Gestion de la Mémoire d'un Chatbot

Dans cette section, nous allons examiner un exemple de code en Python qui illustre la gestion de la mémoire dans un chatbot. Le code utilise PyTorch et les transformers de Hugging Face pour gérer et compresser l'historique des conversations.

Préparation de l'Environnement

Nous commençons par vérifier si un GPU est disponible, ce qui permet d'accélérer le traitement si nécessaire.

import torch
from transformers import pipeline
import logging

if torch.cuda.is_available():
    device: int = 0
else:
    device: int = -1

MAX_MEMORY_SIZE: int = 2000

Définition de la Classe ChatbotMemory

La classe ChatbotMemory gère l'historique des conversations et effectue des opérations de mise à jour et de compression. Donc à chaques fois que update_memory est appelé, le texte en mémoire est compté et traité au besoin.

class ChatbotMemory:
    def __init__(self, conv: list = []):
        self.conversation_history = conv

    def update_memory(self, user_input: str, bot_response: str) -> None:
        self.conversation_history.append(f"'user': {user_input}, 'bot': {bot_response}")

        if memory_counter(self.conversation_history) > 1000:
            self.conversation_history = compressed_memory(self.conversation_history)
            logging.info("Mémoire compressée.")

        if len(self.conversation_history) > MAX_MEMORY_SIZE:
            self.conversation_history.pop(0)
            logging.info("Mémoire réduite.")
        return 0

    def get_memory(self):
        return self.conversation_history

Compression et Comptage de la Mémoire

La fonction _get_compressed_memory utilise le modèle BART pour résumer l'historique des conversations.

def _get_compressed_memory(sentence: str) -> str:
    summarizer = pipeline("summarization", model="facebook/bart-large-cnn", device=device)
    summary = summarizer(sentence, max_length=50, min_length=5, do_sample=False)
    return summary[0]['summary_text']

La fonction compressed_memory applique la fonction _get_compressed_memory à chaque segment de l'historique des conversations. Pour cela nous optimisons la procédure en effectuant un traitement par Batch. Cette méthode est dissocié de la fonction _get_compressed_memory de manière à pouvoir introduire de nouvelles méthodes de compression.

def compressed_memory(conv_hist: list) -> list:
    return [_get_compressed_memory(' '.join(conv_hist[i:i+5])) for i in range(0, len(conv_hist), 5)]

La fonction memory_counter compte le nombre total de mots dans l'historique. (Note qu'il pourrais être interessant de réaliser cette étape avec des tokens plutot que des mots.)

def memory_counter(conv_hist: list) -> int:
    st = ''.join(conv_hist)
    return len(st.split())

Conclusion

Ce code établit un cadre efficace pour la gestion de la mémoire dans un chatbot, en utilisant des techniques de compression pour maintenir un contexte pertinent et en améliorant la performance globale du système. L'utilisation de modèles de résumé comme BART assure que même lorsque la mémoire est compressée, le contexte essentiel est préservé.

Code sur GitHub