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.
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.
Parte 1:
Creazione di flussi usando lo Strumento di creazione dei flussi di WhatsApp Manager.
Parte 2:
Conversione del modello Llama 2 da GGML a GGUF.
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.
Ecco i prerequisiti necessari per procedere:
Aver installato ngrok.
Avere un account sviluppatore su Meta for Developersverificato e conoscenza di API Cloud ospitata da Meta.
Infine, devi aver completato i passaggi necessari per usare WhatsApp Flows. Puoi anche visualizzare l'anteprima del codice di progetto completo.
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:
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:
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:
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.
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:
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.
A questo punto, devi configurare un chatbot e integrarlo nei flussi.
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.
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
).
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.
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.
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:
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:
Il webhook è ora pronto.
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:
Ecco un'altra schermata che mostra la risposta del chatbot. L'utente chiede una rapida traduzione, che il bot è in grado di fornire.
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.