Revenir aux actualités des développeurs

Ajouter WhatsApp Flows à votre expérience de bot de discussion

20 mars 2024DeGafi G et Iryna Wagner

Un nombre croissant d’entreprises utilisent aujourd’hui des bots de discussion sur WhatsApp afin d’entrer en contact avec leurs client·es, de comprendre leurs besoins et de recueillir des données essentielles. Ce processus de collecte et d’organisation des données peut néanmoins représenter un défi en matière d’efficacité. Découvrez WhatsApp Flows.

L’intégration de bots de discussion à WhatsApp Flows permet aux entreprises d’interpréter plus efficacement les informations obtenues sur la clientèle. Les bots de discussion peuvent lancer des flux spécifiques destinés à recueillir des informations en fonction du contexte de la conversation.

Dans ce tutoriel, vous développerez un bot de discussion à l’aide de Llama 2 et de Python, et vous le connecterez à WhatsApp Flows afin de booster ses capacités de collecte de données. Ce faisant, vous découvrez comment WhatsApp Flows améliore la convivialité de l’expérience du bot de discussion, ainsi que la précision et l’efficacité de la collecte des données de la clientèle.

Améliorer l’expérience du bot de discussion grâce à WhatsApp Flows

Votre bot de discussion répond aux questions et invites des utilisateur·ices à l’aide de WhatsApp Flows afin de collecter des données. Il leur permet plus précisément d’échanger, de chercher des informations concernant les services proposés par un hôtel fictif en Espagne et d’entrer en contact avec l’entreprise afin d’obtenir de l’aide.

Le bot de discussion utilise de simples instructions if en parallèle du modèle Llama 2 standard afin d’offrir un accès à une base de connaissances générale suffisante.

Marche à suivre

Étape 1 :

Étape 2 :

  • Convertissez le modèle Llama 2 du format GGML au format GGUF.

  • Rédigez le code Python permettant d’intégrer les flux et le bot de discussion.
  • Créez un webhook pour prendre en compte les messages.
  • Exécutez l’application.

Étape 1 : Créer des flux

Pour cette étape, vous utiliserez l’éditeur de flux afin de créer des flux. Vous pouvez également utiliser l’API Flows, dont nous ne parlerons pas ici.

Prérequis pour l’étape 1

Avant de continuer, vérifiez que les conditions suivantes sont remplies :

Pour finir, assurez-vous que vous avez effectué les étapes obligatoires pour pouvoir utiliser WhatsApp Flows. Vous pouvez également prévisualiser le code de l’intégralité du projet.

Premiers pas

Pour commencer, accédez à la page Flux sur votre compte WhatsApp Business. Si vous utilisez les flux pour la première fois, vous devriez voir un bouton intitulé Commencer à créer des flux. Si ce n’est pas le cas, vous trouverez un bouton Créer un flux en haut à droite de la page.

Cliquez sur le bouton disponible pour afficher une boîte de dialogue dans laquelle vous pouvez saisir des informations relatives à votre flux :

Premiers pas avec la fenêtre de discussion WhatsApp

Flux de demande de renseignements sur des services

Vous allez commencer par créer un flux permettant à l’utilisateur·ice de chercher des informations sur les services proposés par une société hôtelière.

Saisissez un nom dans le champ Nom. Sous le menu déroulant Catégories, choisissez ensuite Inscription. Ne modifiez pas le menu déroulant Modèle, qui doit être défini sur Aucun. Cliquez sur Envoyer.

Sur la page suivante, vous pouvez voir un éditeur sur la gauche et un aperçu sur la droite.

Remplacez les données de l’éditeur par le balisage JSON de votre flux. (Pour en savoir plus sur les flux JSON, consultez la documentation de développement.)

Enregistrez le flux. Votre aperçu devrait ressembler à l’image ci-dessous :

Flux de fenêtre de discussion

Le flux comporte un écran permettant à l’utilisateur·ice de saisir ses données, de sélectionner les services qui l’intéressent et d’ajouter un message facultatif. Lorsque l’utilisateur·ice clique sur Envoyer, le flux se ferme et envoie les données capturées à votre entreprise pour qu’elle les traite. L’une des méthodes de transfert des données consiste à utiliser des points de terminaison. Nous n’avons néanmoins pas besoin de point de terminaison dans le cadre de ce projet. Les données seront transférées au webhook qui gère le bot de discussion.

Vous pouvez réaliser ce transfert grâce aux actions. Il vous suffit de définir les données à transférer vers l’écran suivant à l’aide de l’objet payload :

...

"on-click-action": {
    "name": "complete",
    "payload": {
        "firstname": "${form.first_name}",
        "secondname": "${form.second_name}",
        "services_interested": "${form.services_interested}",
        "additional_info": "${form.additional_info}",
        "flow_key": "agentconnect"
    }
}
...
        

Dans l’extrait de code, le clic de l’utilisateur·ice sur le bouton déclenche on-click-action, qui permet de capturer les données de la charge utile. La charge utile est ensuite envoyée à votre serveur webhook, ce qui a pour effet de fermer le flux via l’action complete.

Vous pouvez affecter les clés payload comme pour toute autre variable. Les valeurs correspondantes peuvent représenter des objets de données ou bien les noms des composants du flux (à l’instar d’un attribut de nom du formulaire HTML).

Votre flux est désormais opérationnel, et vous pouvez simuler l’expérience réelle des utilisateur·ices grâce au bouton à bascule Aperçu interactif :

Aperçu interactif de la fenêtre de discussion

Après avoir effectué des tests, vous pouvez publier le flux, qui se trouve actuellement à l’état Brouillon. Pour ce faire, ouvrez le menu à droite du libellé Enregistrer et cliquez sur Publier. Le flux est désormais opérationnel et fonctionnel.

Flux Nous contacter

Vous allez désormais créer un flux Nous contacter.

Commencez par répéter le processus de création de flux initial. Sélectionnez Nous contacter comme catégorie. Remplacez les données de l’éditeur par ce balisage JSON de votre flux pour obtenir le rendu suivant :

Flux Nous contacter de la fenêtre de discussion

Publiez le flux et passez à la section suivante pour configurer un bot de discussion. La section comprend trois fonctions (send_message, flow_details et flow_reply_processor) qui renferment la logique permettant l’envoi des flux aux utilisateur·ices. Cette section contient également la logique de traitement des charges utiles de flux entrantes. C’est pourquoi nous vous recommandons de la vérifier même si vous avez déjà développé un bot de discussion.

Étape 2 : Configurer le bot de discussion

Vous allez ensuite configurer le bot de discussion et l’intégrer dans vos flux.

Prérequis pour l’étape 2

Avant de continuer, vérifiez que les conditions suivantes sont remplies :

  • Vous disposez de connaissances de base sur Python, ainsi que d’une version récente du logiciel.

  • La version HuggingFace de Llama 2 doit être téléchargée. Le modèle HuggingFace de Llama 2 ne nécessite aucun outil supplémentaire ou matériel spécialisé. Vous pouvez également utiliser la version officielle, mais celle-ci nécessite une configuration supplémentaire.

  • Vous disposez du token d’accès à votre compte et de l’ID du numéro de téléphone.

  • Vous disposez d’un éditeur de code.

Utiliser Python pour intégrer les flux et le bot de discussion

Le bot de discussion fonctionne grâce à un script prédéfini conçu pour aider les utilisateur·ices en fonction des informations saisies. Lors de la première interaction, il affiche un texte de bienvenue personnalisé et un menu textuel en fonction du message de l’utilisateur·ice. Ces options répondent à des besoins spécifiques : consulter les services proposés par l’hôtel, contacter l’un des membres de son personnel ou échanger avec un bot de discussion fonctionnant grâce à Llama.

En répondant par un caractère alphanumérique, l’utilisateur·ice sera redirigé·e vers l’action ou le service correspondants. Toute autre réponse déclenche cependant la fonctionnalité par défaut du bot de discussion, qui répond aux demandes d’ordre général ou oriente l’utilisateur·ice vers les services disponibles en fonction de la suite de la conversation.

Pour commencer, créez un environnement virtuel en exécutant la commande ci-dessous sur votre terminal :

python -m venv venv
        

Activez-la :

source venv/bin/activate
        

Installez ensuite les packages nécessaires :

pip install requests flask llama-cpp-python python-dotenv
        

Utilisez Flask pour créer des routes et interagir avec l’API, requests pour envoyer des requêtes Internet, llama-cpp-python pour interagir avec le modèle, et python-dotenv pour charger les variables de l’environnement.

Créez ensuite un fichier d’environnement intitulé .env, ainsi que le contenu suivant. Veillez à affecter les valeurs appropriées. (Vous pouvez utiliser n’importe quelle chaîne pour TOKEN.)

TOKEN = 
ACCESS_TOKEN = 
PHONE_NUMBER_ID = 
        

Dans le même répertoire, créez un fichier intitulé main.py et commencez par ajouter les packages à utiliser :

import os
import re
import time
import uuid
import requests
from dotenv import load_dotenv
from flask import Flask, request, make_response, json
from llama_cpp import Llama
        

Vous devez désormais définir les variables et les classes. L’extrait de code initialise également Flask et appelle la méthode load_dotenv() pour charger les variables :

app = Flask(__name__)

load_dotenv()
PHONE_NUMBER_ID = os.getenv('PHONE_NUMBER_ID')
url = f"https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}/messages"
TOKEN = os.getenv('TOKEN')
ACCESS_TOKEN = os.getenv('ACCESS_TOKEN')

code_prompt_texts = ["Contact us", "Chat with our chatbot", "YES", "NO"]


service_list = [
    "Accommodation Services",
    "Spa Services",
    "Dining Services",
    "Recreational Facilities",
    "Business & Conference Services",
    "Transportation Services",
    "Accessibility Services",
    "Pet-Friendly Services"
]

La liste service_list répertorie les services proposés par l’hôtel. La liste code_prompt_texts répertorie les options correspondant aux saisies des utilisateur·ices, à savoir 1, 2, Y et N, en utilisant la fonction indiquée au-dessous. Cette dernière vous permet de mapper les réponses de l’utilisateur·ice avec l’option correspondante.

def extract_string_from_reply(user_input):
    match user_input:
        case "1":
            user_prompt = code_prompt_texts[0].lower()
        case "2":
            user_prompt = code_prompt_texts[1].lower()
        case "Y":
            user_prompt = code_prompt_texts[2].lower()
        case "N":
            user_prompt = code_prompt_texts[3].lower()
        case _:
            user_prompt = str(user_input).lower()

    return user_prompt
        

Le code modifie la casse des chaînes et les passe en minuscules afin d’éviter les incohérences lors de l’exécution de la logique conditionnelle. Sa structure match…case associe les invites saisies par les utilisateur·ices aux sorties. Par exemple, lorsqu’un·e utilisateur·ice saisit « 1 », la fonctionnalité "Contact us" est déclenchée.

Par ailleurs, la fonction suivante comporte une instruction if qui utilise le package Python RegEx, re, afin de rechercher certains termes dans le message d’un·e client·e pour décider du type de réponse à envoyer :

def user_message_processor(message, phonenumber, name):
    user_prompt = extract_string_from_reply(message)
    if user_prompt == "yes":
        send_message(message, phonenumber, "TALK_TO_AN_AGENT", name)
    elif user_prompt == "no":
        print("Chat terminated")
    else:
        if re.search("service", user_prompt):
            send_message(message, phonenumber, "SERVICE_INTRO_TEXT", name)

        elif re.search(
            "help|contact|reach|email|problem|issue|more|information", user_prompt
        ):
            send_message(message, phonenumber, "CONTACT_US", name)

        elif re.search("hello|hi|greetings", user_prompt):
            if re.search("this", user_prompt):
                send_message(message, phonenumber, "CHATBOT", name)

            else:
                send_message(message, phonenumber, "SEND_GREETINGS_AND_PROMPT", name)

        else:
            send_message(message, phonenumber, "CHATBOT", name)
        

Désormais, un message tel que "Hello there" déclenchera la méthode send_message avec SEND_GREETINGS_AND_PROMPT comme deuxième argument. Vous trouverez ci-dessous la méthode send_message. Remplacez le contenu entre <xxx> par les informations appropriées.

def send_message(message, phone_number, message_option, name):
    greetings_text_body = (
        "\nHello "
        + name
        + ". Welcome to our hotel. What would you like us to help you with?\nPlease respond with a numeral between 1 and 2.\n\n1. "
        + code_prompt_texts[0]
        + "\n2. "
        + code_prompt_texts[1]
        + "\n\nAny other reply will connect you with our chatbot."
    )

    # loading the list's entries into a string for display to the user
    services_list_text = ""
    for i in range(len(service_list)):
        item_position = i + 1
        services_list_text = (
            f"{services_list_text} {item_position}. {service_list[i]} \n"
        )

    service_intro_text = f"We offer a range of services to ensure a comfortable stay, including but not limited to:\n\n{services_list_text}\n\nWould you like to connect with an agent to get more information about the services?\n\nY: Yes\nN: No"

    contact_flow_payload = flow_details(
        flow_header="Contact Us",
        flow_body="You have indicated that you would like to contact us.",
        flow_footer="Click the button below to proceed",
        flow_id=str("<FLOW-ID>"),
        flow_cta="Proceed",
        recipient_phone_number=phone_number,
        screen_id="CONTACT_US",
    )

    agent_flow_payload = flow_details(
        flow_header="Talk to an Agent",
        flow_body="You have indicated that you would like to talk to an agent to get more information about the services that we offer.",
        flow_footer="Click the button below to proceed",
        flow_id=str("<FLOW-ID>"),
        flow_cta="Proceed",
        recipient_phone_number=phone_number,
        screen_id="TALK_TO_AN_AGENT",
    )

    match message_option:
        case "SEND_GREETINGS_AND_PROMPT":
            payload = json.dumps(
                {
                    "messaging_product": "whatsapp",
                    "to": str(phone_number),
                    "type": "text",
                    "text": {"preview_url": False, "body": greetings_text_body},
                }
            )
        case "SERVICE_INTRO_TEXT":
            payload = json.dumps(
                {
                    "messaging_product": "whatsapp",
                    "to": str(phone_number),
                    "type": "text",
                    "text": {"preview_url": False, "body": service_intro_text},
                }
            )
        case "CHATBOT":
            LLM = Llama(
                model_path="/home/incognito/Downloads/llama-2-7b-chat.ggmlv3.q8_0.gguf.bin",
                n_ctx=2048,
            )
            # create a text prompt
            prompt = message
            # generate a response (takes several seconds)
            output = LLM(prompt)
            payload = json.dumps(
                {
                    "messaging_product": "whatsapp",
                    "to": str(phone_number),
                    "type": "text",
                    "text": {
                        "preview_url": False,
                        "body": output["choices"][0]["text"],
                    },
                }
            )
        case "CONTACT_US":
            payload = contact_flow_payload
        case "TALK_TO_AN_AGENT":
            payload = agent_flow_payload
        case "FLOW_RESPONSE":
            payload = json.dumps(
                {
                    "messaging_product": "whatsapp",
                    "to": str(phone_number),
                    "type": "text",
                    "text": {"preview_url": False, "body": message},
                }
            )

    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + ACCESS_TOKEN,
    }

    requests.request("POST", url, headers=headers, data=payload)
    print("MESSAGE SENT")

S’il s’agit d’un simple message de bienvenue (SEND_GREETINGS_AND_PROMPT), la réponse comporte automatiquement des invites supplémentaires (greetings_text_body).

Exemple de message de bienvenue dans la fenêtre de discussion

De la même manière, lorsqu’un·e utilisateur·ice pose une question concernant les services proposés, un message (service_intro_text) énumérant les différents services est lui envoyé. Celui-ci comporte également une invite qui permet d’échanger avec un·e agent·e en cas de besoin.

Exemples d’offres dans la fenêtre de discussion

Si les données saisies nécessitent une réponse du bot de discussion (CHATBOT), vous devez initialiser le modèle, lui transmettre le contenu du message et traiter la réponse à envoyer à l’utilisateur·ice. FLOW_RESPONSE affiche la réponse capturée par un flux.

Les autres options, CONTACT_US et TALK_TO_AN_AGENT, envoient les charges utiles du flux à l’utilisateur·ice. Les charges utiles du flux proviennent de la fonction flow_details, dont le corps est présenté ci-dessous. La charge utile comprend les informations essentielles du flux, notamment l’élément FLOW_ID, que vous pouvez récupérer sur la page Flux de votre compte WhatsApp Business. Il est également possible de définir ces ID dans vos variables d’environnement.

def flow_details(flow_header, 
    flow_body, 
    flow_footer, 
    flow_id, 
    flow_cta, 
    recipient_phone_number, 
    screen_id
):
    # Generate a random UUID for the flow token
    flow_token = str(uuid.uuid4())

    flow_payload = json.dumps({
        "type": "flow",
        "header": {
            "type": "text",
            "text": flow_header
        },
        "body": {
            "text": flow_body
        },
        "footer": {
            "text": flow_footer
        },
        "action": {
            "name": "flow",
            "parameters": {
                "flow_message_version": "3",
                "flow_token": flow_token,
                "flow_id": flow_id,
                "flow_cta": flow_cta,
                "flow_action": "navigate",
                "flow_action_payload": {
                    "screen": screen_id
                }
            }
        }
    })

    payload = json.dumps({
        "messaging_product": "whatsapp",
        "recipient_type": "individual",
        "to": str(recipient_phone_number),
        "type": "interactive",
        "interactive": json.loads(flow_payload)
    })
    return payload
        

Cette méthode crée l’élément action.parameters.flow_token en générant un UUID aléatoire. L’élément action.parameters.flow_action_payload.screen est transmis en tant que paramètre (screen_id). Dans l’idéal, il doit comporter l’ID du premier écran que vous souhaitez montrer à l’utilisateur·ice lorsque l’élément action.parameters.flow_cta s’exécute.

Pour finir, ajoutez les routes de webhook. La requête GET de webhook s’initialise lors de l’ajout du webhook à votre application Meta for Developers. Elle renvoie l’élément hub.challenge de la requête en cas de succès.

@app.route("/webhook", methods=["GET"])
def webhook_get():
    if request.method == "GET":
        if (
            request.args.get("hub.mode") == "subscribe"
            and request.args.get("hub.verify_token") == TOKEN
        ):
            return make_response(request.args.get("hub.challenge"), 200)
        else:
            return make_response("Success", 403)
        

La requête POST extrait et traite la charge utile du message à l’aide de la méthode user_message_processor présentée précédemment. Comme le code ne s’occupe que de la charge utile du message, il émet des erreurs lors de la capture de toute autre charge utile. C’est pourquoi vous pouvez utiliser une instruction if pour vérifier s’il y a un corps messages.

@app.route("/webhook", methods=["POST"])
def webhook_post():
    if request.method == "POST":
        request_data = json.loads(request.get_data())
        if (
            request_data["entry"][0]["changes"][0]["value"].get("messages")
        ) is not None:
            name = request_data["entry"][0]["changes"][0]["value"]["contacts"][0][
                "profile"
            ]["name"]
            if (
                request_data["entry"][0]["changes"][0]["value"]["messages"][0].get(
                    "text"
                )
            ) is not None:
                message = request_data["entry"][0]["changes"][0]["value"]["messages"][
                    0
                ]["text"]["body"]
                user_phone_number = request_data["entry"][0]["changes"][0]["value"][
                    "contacts"
                ][0]["wa_id"]
                user_message_processor(message, user_phone_number, name)
            else:
                # checking that there is data in a flow's response object before processing it
                if (
                    request_data["entry"][0]["changes"][0]["value"]["messages"][0][
                        "interactive"
                    ]["nfm_reply"]["response_json"]
                ) is not None:
                    flow_reply_processor(request)

    return make_response("PROCESSED", 200)
        

En outre, vous pouvez utiliser la fonction d’aide appelée flow_reply_processor pour extraire la réponse du flux et la renvoyer à l’utilisateur·ice :

def flow_reply_processor(request):
    request_data = json.loads(request.get_data())
    name = request_data["entry"][0]["changes"][0]["value"]["contacts"][0]["profile"]["name"]
    message = request_data["entry"][0]["changes"][0]["value"]["messages"][0]["interactive"]["nfm_reply"][
        "response_json"]

    flow_message = json.loads(message)
    flow_key = flow_message["flow_key"]
    if flow_key == "agentconnect":
        firstname = flow_message["firstname"]
        reply = f"Thank you for reaching out {firstname}. An agent will reach out to you the soonest"
    else:
        firstname = flow_message["firstname"]
        secondname = flow_message["secondname"]
        issue = flow_message["issue"]
        reply = f"Your response has been recorded. This is what we received:\n\n*NAME*: {firstname} {secondname}\n*YOUR MESSAGE*: {issue}"

    user_phone_number = request_data["entry"][0]["changes"][0]["value"]["contacts"][0][
        "wa_id"]
    send_message(reply, user_phone_number, "FLOW_RESPONSE", name)
        

Elle se sert d’une clé (flow_key) pour différencier les deux flux et ainsi extraire correctement les réponses tout en transmettant les ID exacts du premier écran.

Avant d’exécuter le code, comparez-le à la version complète et vérifiez que tout est correct.

Configurer le webhook

Avant de continuer, exécutez la commande suivante à partir de votre terminal :

flask --app main run --port 5000 
        

Si tout se passe bien, vous devriez obtenir le message suivant :

* Running on http://127.0.0.1:5000
        

Exécutez ensuite ngrok http 5000 afin d’obtenir une URL qui correspond à votre application. Copiez le lien.

Dans votre compte de développeur sur Meta for Developers, cliquez ensuite sur le menu Configuration sous WhatsApp dans le volet de navigation de gauche :

Configuration de la fenêtre de discussion

Dans la fiche Webhook, cliquez sur Modifier.

Ensuite, dans le champ URL de rappel de la boîte de dialogue qui s’affiche, ajoutez l’URL copiée, puis /webhook.

Ajoutez le token de la variable TOKEN de votre fichier .env dans le champ Vérifier le token. Cliquez sur Vérifier et enregistrer pour fermer la boîte de dialogue.

Depuis la même fiche, cliquez maintenant sur Gérer et activez le champ Messages. La fiche devrait ressembler à ce qui suit :

Webhook de la fenêtre de discussion WhatsApp

Le webhook est maintenant prêt.

Exécuter l’application

Vous pouvez envoyer un message tel que « Bonjour » à votre numéro de compte. Vous devriez recevoir la réponse appropriée. Essayez de répondre à l’aide d’une invite affichée dans le menu afin de tester les flux :

Application en cours d’exécution dans la fenêtre de discussion WhatsApp

Vous trouverez ci-dessous une autre capture d’écran montrant la réponse du bot de discussion. L’utilisateur·ice demande une traduction rapide, que le bot peut lui fournir.

Application en cours d’exécution dans la fenêtre de discussion WhatsApp

Conclusion

WhatsApp Flows représente un outil performant pour les entreprises. Il leur permet de recueillir des informations structurées, d’améliorer les interactions avec la clientèle et de simplifier la communication entre les entreprises et les consommateur·ices. L’une des méthodes pour créer des flux consiste à utiliser l’éditeur de flux du Gestionnaire WhatsApp, qui propose une interface conviviale.

Cette application de démonstration propose un aperçu de la manière dont vous pouvez utiliser WhatsApp Flows pour améliorer les interactions avec la clientèle et l’analyse orientée données. Pour des usages plus spécialisés, vous pouvez également configurer Llama 2 et WhatsApp Flows afin de connecter votre bot de discussion à un modèle personnalisé ou de l’entraîner sur une source de données dont vous êtes propriétaire. Ce procédé lui permet de fournir des réponses qui semblent naturelles concernant votre produit et d’autres fonctionnalités.

Utilisez WhatsApp Flows dès maintenant pour améliorer vos interactions avec la clientèle et simplifier la collecte de données dans vos applications. Essayez sans plus attendre.