Torna alle notizie per sviluppatori

Aggiungere WhatsApp Flows all'esperienza del chatbot

20 marzo 2024DiGafi G & Iryna Wagner

Oggi sempre più aziende usano i chatbot tramite WhatsApp per interagire con i clienti, conoscere le loro esigenze e raccogliere dati essenziali. Tuttavia, il processo di raccolta e organizzazione dei dati può porre problemi in termini di efficienza. Accedi a WhatsApp Flows.

L'integrazione dei chatbot con WhatsApp Flows consente alle aziende di interpretare più efficacemente le informazioni ricevute dai clienti. I chatbot possono quindi avviare flussi specifici per la raccolta dei dati, a seconda del contesto della conversazione.

In questo tutorial, creerai un chatbot con Llama 2 e Python e lo collegherai a WhatsApp Flows per migliorarne le funzionalità di raccolta dati. In questo modo, scoprirai come WhatsApp Flows aumenta la facilità d'uso del chatbot e l'accuratezza e l'efficienza della raccolta dei dati dei clienti.

Miglioramento dell'esperienza del chatbot con WhatsApp Flows

Il tuo chatbot risponderà alle richieste e alle sollecitazioni degli utenti con WhatsApp Flows per la raccolta dei dati. Nello specifico, consentirà agli utenti di conversare con il chatbot, ricercare informazioni sui servizi offerti da un hotel fittizio in Spagna e contattare l'azienda per ricevere assistenza.

Il chatbot userà semplici istruzioni if insieme al modello standard Llama 2 per dare accesso a una base di conoscenze generali sufficiente.

Passaggi da seguire

Parte 1:

Parte 2:

  • Conversione del modello Llama 2 da GGML a GGUF.

  • Scrittura del codice Python per eseguire l'integrazione dei flussi e del chatbot.
  • Creazione di un webhook per ascoltare i messaggi.
  • Esecuzione dell'app.

Parte 1. Come creare i flussi

In questa parte, userai lo Strumento di creazione dei flussi per creare alcuni flussi. In alternativa, puoi usare l'API Flows, di cui non tratteremo in questa sede.

Parte 1. Prerequisiti

Ecco i prerequisiti necessari per procedere:

Infine, devi aver completato i passaggi necessari per usare WhatsApp Flows. Puoi anche visualizzare l'anteprima del codice di progetto completo.

Primi passi

Per iniziare, accedi alla pagina Flows nel tuo account WhatsApp Business. Se è la prima volta che interagisci con Flows, dovresti vedere il pulsante Inizia a creare flussi. In caso contrario, vedrai il pulsante Crea flusso in alto a destra della pagina.

Clicca sul pulsante visualizzato per aprire una finestra di dialogo in cui inserire alcuni dettagli sul flusso:

Primi passi con il chatbot di WhatsApp

Il flusso Richiesta di servizi

Innanzitutto, devi creare un flusso che consenta all'utente di cercare informazioni sui servizi offerti dall'azienda alberghiera.

Inserisci il nome nel campo Nome. Quindi, alla voce del menu a discesa Categorie, scegli Iscriviti e lascia la voce Modello impostata su Nessuno. Clicca su Invia.

La pagina successiva mostra un editor a sinistra e un'anteprima a destra.

Sostituisci i contenuti dell'editor con un markup JSON per il flusso. (per maggiori dettagli sul flusso JSON, consulta la documentazione per gli sviluppatori).

Dopo aver salvato il flusso, l'anteprima dovrebbe avere questo aspetto:

Flusso del chatbot

Il flusso contiene una schermata che consente all'utente di inserire le sue informazioni, selezionare i servizi di suo interesse e un messaggio aggiuntivo opzionale. Quando l'utente clicca su Invia, il flusso si chiude e invia i dati acquisiti all'azienda per l'elaborazione. Uno dei metodi di trasmissione dei dati utilizza gli endpoint. Per questo progetto, tuttavia, non sono necessari. I dati saranno trasferiti allo stesso webhook che attiva il chatbot.

Puoi ottenere il trasferimento usando le azioni. I dati da trasferire alla schermata successiva si possono impostare usando l'oggetto 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"
    }
}
...
        

Nello snippet, quando l'utente clicca sul pulsante attiva on-click-action, acquisendo i dati nel playload. Dopodiché, il playload viene inviato al server webhook e il flusso chiuso tramite l'azione complete.

Puoi assegnare le chiavi payload come qualsiasi altra variabile. I valori corrispondenti possono rappresentare dati o i nomi di componenti del flusso (come accade nel caso di attributo del nome del modulo HTML).

Ora, puoi vedere il flusso in azione e simulare un'esperienza utente reale selezionando il pulsante Anteprima interattiva:

Anteprima interattiva del chatbot

Dopo il test, puoi pubblicare il flusso, che al momento si trova allo stato di bozza. Per farlo, apri il menu a destra di Salva e clicca su Pubblica. A questo punto, il flusso è pronto per essere utilizzato.

Il flusso Contattaci

Ora creerai un flusso "Contattaci".

Ripeti lo stesso processo di creazione del flusso iniziale. Per quanto riguarda la categoria, scegli Contattaci. Sostituisci i contenuti dell'editor con questo markup JSON per ottenere:

Flusso Contattaci del chatbot

Pubblica il flusso e vai alla sezione successiva per configurare un chatbot. La sezione offre tre funzioni: send_message, flow_details e flow_reply_processor, che contengono la logica essenziale per l'invio dei flussi agli utenti. La sezione include anche la logica per elaborare i playload del flusso in entrata. Pertanto, consigliamo di consultarla anche se hai già creato un chatbot.

Parte 2. Come configurare un chatbot

A questo punto, devi configurare un chatbot e integrarlo nei flussi.

Parte 2. Prerequisiti

Prima di continuare, assicurati di disporre dei requisiti necessari per procedere:

  • Conoscenza di base e una versione recente di Python.

  • Aver scaricato la versione HuggingFace di Llama 2. Il modello HuggingFace di Llama 2 non richiede strumenti aggiuntivi o hardware particolari. Puoi anche usare la versione ufficiale, ma richiede una configurazione aggiuntiva.

  • Token d'accesso e ID del numero di telefono dell'account.

  • Un editor del codice.

Usare Python per integrare flussi e chatbot

Il chatbot funzionerà attraverso uno script predefinito progettato per assistere gli utenti in base ai loro input. Al momento della prima interazione, presenta un testo di saluto personalizzato e un menu testuale basato sul messaggio dell'utente. Queste opzioni rispondono a esigenze specifiche: richiedere i servizi offerti dall'hotel, contattare uno dei suoi agenti o interagire con un chatbot fornito da Llama.

Rispondendo con un carattere alfanumerico, l'utente viene collegato al servizio o all'azione corrispondente. Tuttavia, qualsiasi altra risposta attiva la funzionalità predefinita del chatbot, che assiste l'utente con richieste generali o lo guida attraverso i servizi disponibili in base a ulteriori conversazioni.

Per iniziare, crea un ambiente virtuale eseguendo il comando seguente nel terminale:

python -m venv venv
        

Attivalo:

source venv/bin/activate
        

Quindi, installa i pacchetti richiesti:

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

Userai Flask per creare percorsi e interagire con l'API, requests per inviare richieste Internet, llama-cpp-python per interagire con il modello e python-dotenv per caricare variabili ambientali.

In seguito, crea un file ambientale chiamato .env e il contenuto seguente, assegnando correttamente i valori (puoi usare qualsiasi stringa per il TOKEN).

TOKEN = 
ACCESS_TOKEN = 
PHONE_NUMBER_ID = 
        

Nella stessa directory, crea un file chiamato main.py e comincia ad aggiungere i pacchetti che userai:

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
        

A questo punto, inizializza le variabili e le classi. Lo snippet inizializza anche Flask e chiama il metodo load_dotenv() per il caricamento delle variabili:

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"
]

Service_list raccoglie i servizi offerti dall'hotel. L'elenco code_prompt_texts contiene le opzioni corrispondenti alle scelte di input degli utenti, che sono: 1, 2, Y e N, usando la seguente funzione. La funzione di seguito consente di associare le risposte dell'utente all'opzione corrispondente.

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
        

Il codice converte le stringhe in lettere minuscole per evitare errori di corrispondenza durante l'esecuzione della logica condizionale. La relativa struttura match…case collega le richieste inserite dall'utente ai risultati. Ad esempio, se un utente inserisce "1", attiverà la funzionalità "Contact us".

Quindi, la seguente funzione contiene l'istruzione if che usa il pacchetto RegEx di Python, re, per cercare alcuni termini nel messaggio del cliente che determineranno il tipo di risposta da inviare:

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)
        

A questo punto, un messaggio come "Hello there" attiverà il metodo send_message con SEND_GREETINGS_AND_PROMPT come secondo argomento. Di seguito è illustrato il metodo send_message. Sostituisci correttamente il contenuto tra <xxx>.

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")

Se il messaggio è un semplice messaggio di saluto (SEND_GREETINGS_AND_PROMPT), la risposta conterrà ulteriori richieste (greetings_text_body).

Esempio di saluto nella chat

Allo stesso modo, quando un utente pone una domanda sui servizi offerti, gli sarà inviato un messaggio di testo (service_intro_text) contenente l'elenco dei servizi. Oltre ai servizi, il messaggio contiene anche una richiesta per l'utente, che dovrà scegliere se desidera parlare con un agente.

Esempio di offerte nella chat

Se la risposta inserita richiede la risposta di un chatbot (CHATBOT), inizializzerai il modello, gli fornirai il contenuto del messaggio ed elaborerai la risposta da inviare all'utente. FLOW_RESPONSE mostra la risposta acquisita di un flusso.

Le altre opzioni, CONTACT_US e TALK_TO_AN_AGENT, inviano i playload del flusso all'utente. I playload del flusso hanno origine dalla funzione flow_details, il cui corpo è mostrato di seguito. Il playload incorpora i dettagli essenziali del flusso, incluso il FLOW_ID, che puoi recuperare dalla pagina Flows del tuo account WhatsApp Business. È anche possibile definire questi ID all'interno delle variabili ambientali.

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
        

Questo metodo crea action.parameters.flow_token generando un UUID casuale. action.parameters.flow_action_payload.screen viene passato come parametro (screen_id). Idealmente dovrebbe rappresentare l'ID della schermata iniziale che intendi mostrare all'utente quando action.parameters.flow_cta è in esecuzione.

Infine, aggiungi i percorsi webhook. La richiesta GET del webhook viene avviata quando aggiungi il webhook alla tua applicazione su Meta for Developers e restituisce hub.challenge della richiesta quando l'operazione va a buon fine.

@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 richiesta POST estrae ed elabora il playload del messaggio usando il metodo user_message_processor introdotto in precedenza. Dato che il codice è dedicato al payload del messaggio, viene restituito un errore in caso di acquisizione di qualsiasi altro payload. Per tale motivo, userai un'istruzione if per verificare la presenza del corpo dei 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)
        

Inoltre, consigliamo di usare la funzione dell'helper seguente chiamata flow_reply_processor per estrarre la risposta dal flusso e rimandarla all'utente:

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)
        

La funzione utilizza una chiave (flow_key) per differenziare i due flussi e quindi estrarre le risposte in modo appropriato, fornendo gli ID della prima schermata corretti.

Prima di eseguire il codice, confrontalo con la versione completa e conferma che tutti gli elementi corrispondano.

Come configurare il webhook

Prima di procedere, esegui questo comando dal terminale:

flask --app main run --port 5000 
        

Se tutto funziona correttamente, dovresti visualizzare il messaggio seguente:

* Running on http://127.0.0.1:5000
        

A questo punto, esegui ngrok http 5000 per ricevere un URL associato alla tua app. Copia il link.

Quindi, nel tuo account sviluppatore su Meta for Developers, clicca sulla voce Configurazione del menu WhatsApp nel pannello di navigazione a sinistra:

Configurazione del chatbot

Nella scheda Webhook, clicca su Modifica.

Dopodiché, nel campo URL di callback della casella di dialogo che si apre, aggiungi l'URL copiato e inserisci /webhook.

Aggiungi il token della variabile TOKEN del file .env nel campo Verifica il token. Clicca su Verifica e salva per chiudere la finestra di dialogo.

Ora, dalla stessa scheda, clicca su Gestisci e seleziona il campo Messaggi. Ecco come dovrebbe apparire la scheda:

Webhook del chatbot di WhatsApp

Il webhook è ora pronto.

Esecuzione dell'app

Puoi inviare un messaggio come "Ciao" al tuo numero di account. Dovresti ricevere la risposta appropriata. Prova a rispondere con un messaggio mostrato nel menu per testare i flussi:

Esecuzione dell'app chatbot di WhatsApp

Ecco un'altra schermata che mostra la risposta del chatbot. L'utente chiede una rapida traduzione, che il bot è in grado di fornire.

Esecuzione dell'app chatbot di WhatsApp

Conclusioni

WhatsApp Flows è uno strumento molto efficace per le aziende che desiderano raccogliere informazioni strutturate, migliorare le interazioni con i clienti e semplificare le comunicazioni con i consumatori. Uno dei modi per creare flussi è tramite lo Strumento di creazione dei flussi di WhatsApp Manager, che offre un'interfaccia facile da usare per la progettazione dei flussi.

Questa app dimostrativa mostra brevemente come puoi sfruttare WhatsApp Flows per migliorare le interazioni con i clienti e l'analisi dei dati. Per usi più specifici, puoi configurare Llama 2 e WhatsApp Flows per collegare il chatbot a un modello personalizzato o per istruirlo in base a una fonte di dati proprietaria, consentendogli di rispondere a domande sul tuo prodotto e su altre funzionalità usando un linguaggio naturale.

Sfrutta WhatsApp Flows per migliorare le interazioni con i clienti e semplificare la raccolta dei dati nelle tue app oggi stesso. Prova subito.