Mais empresas do que nunca usam chatbots via WhatsApp para interagir com clientes, entender suas necessidades e reunir dados essenciais. No entanto, esse processo de coletar e organizar dados pode apresentar desafios em termos de eficiência. Conheça o WhatsApp Flows.
Integrar chatbots ao WhatsApp Flows permite que as empresas interpretem as informações recebidas do cliente de forma mais eficaz. Os chatbots podem iniciar fluxos específicos personalizados para a coleta de dados, dependendo do contexto dessa conversa.
Neste tutorial, você criará um chatbot usando Llama 2 e Python, conectando-o ao WhatsApp Flows para melhorar seus recursos de coleta de dados. Dessa forma, você verá como o WhatsApp Flows facilita ainda mais para o usuário a experiência do chatbot e aprimora a precisão e a eficiência da coleta de dados do cliente.
Seu chatbot responderá às solicitações e aos comandos do usuário com o WhatsApp Flows para a coleta de dados. Mais especificamente, ele permitirá que os usuários conversem com o chatbot, pesquisem informações sobre serviços oferecidos por um hotel fictício na Espanha e entrem em contato com a empresa para suporte.
O chatbot usará instruções if
simples juntamente com o modelo padrão Llama 2 para fornecer acesso a uma base de conhecimento geral suficiente.
Parte 1:
Crie fluxos usando o configurador de fluxos do Gerenciador do WhatsApp.
Parte 2:
Faça a conversão do modelo Llama 2 de GGML para GGUF.
Aqui, você usará o Configurador de fluxo para criar fluxos. Como alternativa, você pode usar a API de Fluxos. No entanto, não falaremos dela neste artigo.
Para conseguir acompanhar o tutorial, você deve ter:
O ngrok instalado.
Uma conta de desenvolvedor verificada no Meta for Developers e conhecer a API de Nuvem hospedada pela Meta.
Por fim, você deve concluir as etapas necessárias para usar os fluxos. Também é possível ver uma prévia do código do projeto concluído.
Para começar, navegue até a página Fluxos na sua conta do WhatsApp Business. Se essa for a sua primeira interação com os fluxos, você verá o botão Começar a criar fluxos. Caso contrário, haverá um botão Criar fluxo à direita da página.
Clique no botão exibido para abrir uma caixa de diálogo onde você pode inserir mais detalhes sobre seu fluxo:
Primeiro, você criará um fluxo que permite que o usuário busque informações sobre serviços oferecidos pela empresa de hotéis.
Insira um nome no campo Nome. Depois, no menu Categorias, escolha Cadastrar-se e deixe o menu Modelo definido para Nenhum. Clique em Enviar.
A próxima página exibirá um editor à esquerda e uma prévia à direita.
Substitua o conteúdo do editor pela marcação JSON para seu fluxo. (Você pode saber mais sobre o JSON de fluxo na documentação do desenvolvedor.)
Salve o fluxo. Sua prévia deve ser semelhante à imagem abaixo:
O fluxo contém uma tela que permite que o usuário insira suas informações, selecione os serviços de interesse e uma mensagem adicional opcional. Quando o usuário clica em Enviar, o fluxo fecha e envia os dados coletados para sua empresa para tratamento. Um método de transmitir esses dados envolve o uso de pontos de extremidade. No entanto, esse projeto não precisa de um ponto de extremidade. Os dados serão transmitidos para o mesmo webhook que alimenta o chatbot.
Essa transferência pode ser realizada usando ações. Você pode definir os dados para que eles sejam movidos para a próxima tela usando o objeto 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" } } ...
No trecho, a ação do usuário de clicar no botão aciona o on-click-action
, coletando os dados na carga. Depois, ela envia a carga ao servidor do seu webhook e fecha o fluxo via ação complete
.
Você pode atribuir as chaves payload
como faria com qualquer variável. Os valores correspondentes podem representar objetos de dados ou nomes de componentes de fluxo (semelhante a um atributo de nome para o formulário HTML).
Agora, você pode ver o fluxo em ação e simular uma experiência de usuário real usando o botão Prévia interativa:
Depois de testar, você pode publicar o fluxo, pois no momento ele está no estado de rascunho. Para isso, abra o menu à direita de Salvar e clique em Publicar. O fluxo agora está pronto e pode ser usado.
Agora, criará um fluxo "fale conosco".
Comece repetindo o mesmo processo de criação inicial do fluxo. Para a categoria, escolha Fale conosco. Substitua o conteúdo do editor pela marcação JSON para renderizar o seguinte:
Publique o fluxo e avance para a próxima seção para configurar um chatbot. A seção apresenta três funções (send_message
, flow_details
, e flow_reply_processor
) que contêm lógica vital para enviar os fluxos aos usuários. A seção também inclui a lógica para processar cargas recebidas de fluxo. Portanto, recomendamos analisá-la mesmo se já tiver criado um chatbot.
Depois, você configurará o chatbot e o integrará aos seus fluxos.
Antes de continuar, você deve ter:
Conhecimento básico e uma versão recente do Python
A versão HuggingFace do Llama 2 baixada. O modelo HuggingFace Llama 2 não requer ferramentas adicionais nem hardware especializado. Você também pode usar a versão oficial, mas ela necessita de configuração adicional.
O token de acesso à sua conta e a identificação do seu número de telefone
Um editor de código
O chatbot vai operar por meio de um script predefinido criado para ajudar usuários com base nos dados inseridos por eles. Na interação inicial, ele oferece um texto de saudação personalizado e um menu baseado em texto dependendo da mensagem do usuário. Essas opções atendem a necessidades específicas: fazer perguntas sobre os serviços oferecidos pelo hotel, entrar em contato com um dos agentes ou interagir com chatbot com tecnologia da Llama.
Responder a um caractere alfanumérico conectará o usuário ao serviço ou ação correspondente. No entanto, qualquer outra resposta aciona a funcionalidade padrão do chatbot, que ajuda com perguntas gerais ou guia o usuário quanto aos serviços disponíveis com base em mais conversas.
Para começar, crie um ambiente virtual executando o comando abaixo no seu terminal:
python -m venv venv
Ative-o:
source venv/bin/activate
Depois, instale os pacotes obrigatórios:
pip install requests flask llama-cpp-python python-dotenv
Use Flask
para criar rotas e interagir com a API, requests
para enviar solicitações da internet, llama-cpp-python
para interagir com o modelo e python-dotenv
para carregar variáveis de ambiente.
Depois, crie um arquivo de ambiente chamado .env
e o conteúdo a seguir, atribuindo os valores de forma adequada. (Você pode usar qualquer cadeia de caracteres para o TOKEN
.)
TOKEN = ACCESS_TOKEN = PHONE_NUMBER_ID =
No mesmo diretório, crie um arquivo chamado main.py
e comece adicionando os pacotes que você usará:
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
Agora, inicialize as variáveis e as classes. O trecho também inicia o Flask e chama o método load_dotenv()
para ajudar a carregar as variáveis:
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" ]
A lista de serviços service_list armazena os serviços oferecidos pelo hotel. A lista code_prompt_texts
contém as opções correspondentes às escolhas do usuário, que são 1, 2, Y e N usando a função abaixo. A função abaixo ajudará a mapear as respostas do usuário de acordo com a opção correspondente.
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
O código converte as cadeias de caracteres para letras minúsculas a fim de evitar correspondências erradas ao executar a lógica condicional. A estrutura match…case
corresponde o comando inserido pelo usuário às ações resultantes. Por exemplo, se o usuário digitar "1", isso acionará a funcionalidade "Contact us"
.
Depois, a função seguinte contém uma instrução if
que usa o pacote Python RegEx, re
, para pesquisar certos termos de uma mensagem do cliente a fim de decidir qual tipo de resposta enviar ao usuário:
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)
Agora, uma mensagem como "Hello there"
vai disparar o método send_message
com SEND_GREETINGS_AND_PROMPT
como segundo argumento. Abaixo, confira o método send_message
. Substitua o conteúdo entre <xxx>
adequadamente.
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 a mensagem for uma simples saudação (SEND_GREETINGS_AND_PROMPT
), a resposta vai conter comandos adicionais (greetings_text_body
).
Da mesma forma, quando um usuário faz uma pergunta sobre os serviços oferecidos, uma mensagem de texto (service_intro_text
) contendo os serviços é enviada ao usuário. Além dos serviços, ela também contém um comando para o usuário escolher se deseja falar com um agente.
Se a entrada requer uma resposta do chatbot (CHATBOT
), você inicializa o modelo, alimenta-o com o conteúdo da mensagem e processa a resposta para enviar de volta ao usuário. FLOW_RESPONSE
exibe uma resposta capturada do fluxo.
As outras opções, CONTACT_US
e TALK_TO_AN_AGENT
, enviam as cargas de fluxo para o usuário. As cargas do fluxo se originam da função flow_details
, cujo corpo é mostrado abaixo. A carga incorpora detalhes essenciais do fluxo, incluindo o FLOW_ID
, que você pode recuperar da página Fluxos da sua conta do WhatsApp Business. Também é possível definir essas identificações nas variáveis do ambiente.
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
Esse método cria a action.parameters.flow_token
gerando um UUID aleatório. O action.parameters.flow_action_payload.screen
é transmitido como parâmetro (screen_id
). Idealmente, ele deve representar a identificação da tela inicial que você pretende mostrar para o usuário quando action.parameters.flow_cta
é executado.
Por fim, adicione as rotas do webhook. A solicitação GET
do webhook é iniciada ao adicionar o webhook ao app no Meta for Developers. Ela retorna o hub.challenge
da solicitação quando bem-sucedida.
@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)
A solicitação POST
extrai e processa a carga da mensagem usando o método user_message_processor
introduzido anteriormente. Como o código só atende à carga da mensagem, ele vai gerar erros ao capturar qualquer outra carga. Por esse motivo, você pode usar uma instrução if
para verificar se há um corpo 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)
Além disso, você pode usar uma função auxiliar chamada flow_reply_processor
para extrair uma resposta do fluxo e enviá-la para o usuário:
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)
Ela usa uma chave (flow_key
) para diferenciar entre os dois fluxos e, assim, extrair as respostas adequadamente ao transmitir as primeiras identificações de tela corretas.
Antes de executar o código, compare-o com a versão completa e confirme se há correspondência entre tudo.
Antes de continuar, execute este comando do seu terminal:
flask --app main run --port 5000
Se tiver sucesso, você poderá usar a seguinte mensagem:
* Running on http://127.0.0.1:5000
Em seguida, execute ngrok http 5000
para obter um URL que mapeia seu app. Copie o link.
Depois, na conta de desenvolvedor do Meta for Developers, clique no menu Configuração sob WhatsApp no painel de navegação esquerdo:
No cartão Webhook, clique em Editar.
Depois, no campo URL de retorno na caixa de diálogo aberta, adicione o URL copiado e anexe o /webhook
a ele.
Adicione a variável TOKEN
do arquivo .env
ao campo Verificar token. Clique em Verificar e salvar para fechar a caixa de diálogo.
Agora, no mesmo cartão, clique em Gerenciar e marque o campo mensagens. O cartão deve aparecer conforme segue:
Agora, o webhook está pronto.
Você pode enviar uma mensagem como "Olá" ao número da sua conta. Você receberá a resposta correspondente. Tente responder com um comando mostrado no menu para testar os fluxos:
Abaixo está outra captura de tela mostrando a resposta do chatbot. O usuário pede uma tradução rápida, que o bot pode fornecer.
O WhatsApp Flows é uma ferramenta poderosa para empresas coletarem informações estruturadas, melhorando as integrações do cliente e simplificando a comunicação entre empresas e consumidores. Uma das formas de criar fluxos é com o Configurador do fluxo do Gerenciador do WhatsApp, que oferece uma interface fácil de usar para a criação de fluxos.
Esse app de demonstração oferece um vislumbre de como você pode usar o WhatsApp Flows para melhorar o engajamento do cliente e a análise baseada em dados. Para usos mais especializados, você também pode configurar o Llama 2 e o WhatsApp Flows para conectar seu chatbot a um modelo personalizado ou treiná-lo em relação a fontes de dados proprietários, permitindo que ele responda a perguntas sobre seus produtos e outras funcionalidades usando uma linguagem natural.
Use o WhatsApp Flows para melhorar as integrações com seus clientes e simplifique a coleta de dados nos seus apps hoje mesmo. Experimente já.