Voltar para Notícias para desenvolvedores

Adicionando o WhatsApp Flows à experiência do seu chatbot

20 de março de 2024PorGafi G & Iryna Wagner

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.

Melhorando a experiência do chatbot com o WhatsApp Flows

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.

Etapas para seguir

Parte 1:

Parte 2:

  • Faça a conversão do modelo Llama 2 de GGML para GGUF.

  • Escreva o código Python para integrar o Flows e o chatbot.
  • Crie um webhook para ouvir as mensagens.
  • Execute o app.

Parte 1: como criar fluxos

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.

Pré-requisitos para a parte 1

Para conseguir acompanhar o tutorial, você deve ter:

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.

Começar

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:

Como começar a usar a caixa de bate-papo do WhatsApp

O fluxo de dúvidas sobre serviços

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:

Fluxo da caixa de bate-papo

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:

Prévia interativa da caixa de bate-papo

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.

O fluxo de fale conosco

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:

Fluxo de fale conosco para caixa de bate-papo

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.

Parte 2: como configurar o chatbot

Depois, você configurará o chatbot e o integrará aos seus fluxos.

Pré-requisitos para a parte 2

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

Usando Python para integrar fluxos e chatbot

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

Exemplo de saudação da caixa de bate-papo

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.

Exemplo de ofertas da caixa de bate-papo

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.

Como configurar o webhook

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:

Configuração da caixa de bate-papo

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:

Webhook da caixa de bate-papo do WhatsApp

Agora, o webhook está pronto.

Executando o app

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:

Caixa de bate-papo do WhatsApp - Execução do app

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.

Caixa de bate-papo do WhatsApp - Execução do app

Conclusão

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á.