Criando um chatbot automatizado para retirada de pedidos com API do Chatgpt 3.5 turbo em Python

Detalhes do projeto

Uma das coisas empolgantes sobre um LLM (large language model) como o ChatGPT é que você pode usá-lo para construir um chatbot personalizado com uma interface conversacional e desenvolver uma conversa completa de ponta a ponta se aproveitando de todo o poder e aprendizado que o ChatGPT já possui em sua disposição.

Os chatbots são comumente usados para lidar com consultas de atendimento ao cliente, responder perguntas frequentes, realizar tarefas de agendamento ou fornecer informações detalhadas sobre um produto ou serviço. Eles são programados para responder de maneira a simular a conversação humana e são especialmente úteis para lidar com interações repetitivas, liberando tempo para que os humanos lidem com tarefas mais complexas.

Existem dois tipos principais de chatbots:

Chatbots baseados em regras: Esses chatbots respondem a consultas específicas e seguem um conjunto pré-definido de regras. Eles são limitados em termos de capacidade de compreensão e não conseguem lidar com perguntas fora das regras pré-definidas.

Chatbots baseados em Inteligência Artificial: Esses chatbots utilizam técnicas de Processamento de Linguagem Natural(PLN) e Aprendizado de Máquina (Machine Learning) para entender melhor a intenção do usuário e fornecer respostas mais naturais e contextualmente relevantes.

Modelos de linguagem como GPT-3 e GPT-4 da OpenAI são exemplos de tecnologia usada para construir chatbots baseados em Inteligência Artificial. O ChatGPT é um chatbot baseado em IA.Esses bots são capazes de aprender com as interações passadas, melhorando assim a qualidade das respostas ao longo do tempo.

Eles podem gerenciar uma variedade de perguntas e até mesmo participar de conversas mais complexas com os usuários.

A ideia desse projeto aqui é construir um modelo exemplo de chatbot personalizado, talvez para desempenhar o papel de um agente de atendimento ao cliente utilizando IA ou para fazer pedidos em um restaurante.

Claramente para ser colocado em prática, é necessário muito mais customização e configuração do modelo, mas aqui irei construir um conceito básico que pode ser escalado para algo maior ou completamente diferente dependendo do contexto de cada projeto ou empresa.

Criando um processo de ponta a ponta

Antes de partir para a ação e para a modelagem precisamos definir um processo bem claro de como irá funcionar o fluxo de recebimento das mensagens e dos inputs dos usuários. E para isso é importante entender o comportamento base do modelo e como ele recebe as mensagens e como você pode utilizar os outputs para comunicar aos sistemas internos e fazer um ETL desse fluxo.

Esse é um exemplo de fluxo utilizado em um projeto de um serviço de agendado de consultas médicas com o objetivo de reduzir o tempo de espera dos pacientes e facilitar o trabalho dos profissionais de saúde.

Por meio de um assistente virtual, os pacientes poderão marcar suas consultas de acordo com suas preferências de horário e especialistas médicos.

Este fluxo foi criado por desenvolvedores da consultoria CI&T (referência ao final deste artigo).créditos da imagem: https://www.linkedin.com/in/isaacsilva98/

Inicialmente temos a ação do usuário de começar a conversa e me seguida recebe a prompt do usuário; baseado nessa prompt ele irá fazer perguntas até avançar para a etapa de conclusão do agendamento do pedido, enquanto ele não alcançar essa etapa (false) ele retorna para as perguntas até conseguir as informações que precisa.

Uma vez adquiridas as respostas necessárias ele parte para a ação de agendamento do pedido e grava isso no banco de dados passando então as informações do agendamento para o paciente e encerrando o chat.

Esse processo inteiro não será abordado neste artigo, porém serve como base para que cada empresa estruture um fluxo completo de acordo com sua arquitetura e necessidades.

Instalando as dependências

Primeiro, configuraremos o pacote Python OpenAI. Modelos de chat como o ChatGPT são na verdade treinados para receber uma série de mensagens como entrada e retornar uma mensagem gerada pelo modelo como saída.

E, embora o formato de chat seja projetado para tornar as conversas de várias iterações como esta fácil, também é igualmente útil para tarefas de uma única volta sem qualquer conversa.

Dependendo do programa que estiver utilizando para rodar o python, pode ser necessário instalar os pacotes antes de importar.

!pip install openai
!pip install load_dotenv
Instalação dos pacotes com sucesso

Em seguida iremos importar os pacotes da openai e dotenv e inserir a chave da API da open AI, caso ainda não possua uma pode ser criada em: https://platform.openai.com/account/api-keys

Eu estou utilizando uma chave gratuita e utilizando o modelo do chatgpt 3.5 turbo que é o que está disponível atualmente de forma gratuita. Para modelos 4 ou posterior é necessário pagar por uma licença.

import openai
import os

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file

openai.api_key  = ('INSIRA AQUI SUA CHAVE API OPENAI')

Criando os objetos do modelo

Em seguida iremos criar duas funções, a get_completion e get_completion_from_messages.

Se você observar, fornecemos um prompt, mas, na verdade, dentro da função, o que estamos fazendo é inserir esse prompt em algo que se parece com uma mensagem do usuário.

E isso ocorre porque o modelo ChatGPT é um modelo de chat, o que significa que ele é treinado para receber uma série de mensagens como entrada e retornar uma mensagem gerada pelo modelo como saída.

Portanto, a mensagem do usuário é meio que a entrada, e a mensagem do assistente é a saída.

def get_completion(prompt, model="gpt-3.5-turbo"):
    messages = [{"role": "user", "content": prompt}]
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=0, # this is the degree of randomness of the model's output
    )
    return response.choices[0].message["content"]

def get_completion_from_messages(messages, model="gpt-3.5-turbo", temperature=0):
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=temperature, # this is the degree of randomness of the model's output
    )
#     print(str(response.choices[0].message))
    return response.choices[0].message["content"]

A função get_completion é definida com dois parâmetros: prompt e model. O parâmetro prompt representa o texto de entrada ou prompt fornecido pelo usuário. O parâmetro model é opcional e representa o modelo a ser usado para gerar a conclusão. Por padrão, é definido como “gpt-3.5-turbo”.

Dentro da função, uma lista messages é criada. Essa lista conterá as mensagens da conversa para a conclusão baseada em chat. Neste caso, a conversa consiste em uma única mensagem do usuário. A mensagem é representada como um dicionário com duas chaves: “role” e “content”. “role” especifica o papel da mensagem (neste caso, “user”), e “content” contém o texto real da mensagem, que é o prompt fornecido.

O método openai.ChatCompletion.create() é chamado para gerar a conclusão. Ele recebe vários argumentos:

model: Especifica o modelo a ser usado para gerar a conclusão. É definido com o valor do parâmetro model passado para a função.

messages: Especifica as mensagens da conversa para a conclusão. É definido como a lista messages criada no passo anterior.

temperature: Especifica o grau de aleatoriedade na saída do modelo. Um valor de 0 faz com que a saída seja mais determinística, ou seja, mais previsível.

A resposta da API de conclusão de chat é armazenada na variável response.

Por fim, a função retorna o conteúdo da conclusão gerada pelo modelo. Ela acessa o conteúdo da primeira escolha da resposta usando response.choices[0].message["content"]. Cada escolha representa uma possível conclusão diferente, e neste caso, estamos pegando o conteúdo da primeira escolha como saída.

A segunda função, get_completion_from_messages, é semelhante à primeira, mas recebe a lista messages diretamente como parâmetro, em vez do prompt. Além disso, possui um parâmetro adicional temperature, que permite controlar o grau de aleatoriedade na saída do modelo. O restante do código é muito semelhante ao da primeira função, onde a resposta da API é armazenada na variável response, e o conteúdo da primeira escolha é retornado como saída.

Essas funções são usadas para interagir com um modelo de geração de texto baseado em chat da OpenAI, onde uma lista de mensagens é passada para o modelo, e ele retorna uma resposta gerada com base nessas mensagens.

Repare que utilizamos um parâmetro da variável response que é o temperature=0; A temperatura é um parâmetro que controla a “criatividade” ou aleatoriedade do texto gerado pelo GPT-3. Uma temperatura mais alta (por exemplo, 0,7) resulta em uma saída mais diversificada e criativa, enquanto uma temperatura mais baixa (por exemplo, 0,2) torna a saída mais determinística e focada.

Na prática, a temperatura afeta a distribuição de probabilidade sobre os possíveis tokens em cada etapa do processo de geração. Uma temperatura de 0 tornaria o modelo completamente determinístico, sempre escolhendo o token mais provável.

Interface e automação do Orderbot

Podemos automatizar a coleta de prompts do usuário e respostas do assistente para construir um OrderBot. O OrderBot irá receber pedidos em uma pizzaria.

Este chatbot será chamado de OrderBot, e iremos automatizar a coleta de prompts do usuário e respostas do assistente.

def collect_messages(_):
    prompt = inp.value_input
    inp.value = ''
    context.append({'role':'user', 'content':f"{prompt}"})
    response = get_completion_from_messages(context) 
    context.append({'role':'assistant', 'content':f"{response}"})
    panels.append(
        pn.Row('User:', pn.pane.Markdown(prompt, width=600)))
    panels.append(
        pn.Row('Assistant:', pn.pane.Markdown(response, width=600, style={'background-color': '#F6F6F6'})))
 
    return pn.Column(*panels)

Aqui iremos criar uma UI para simular a aplicação do painel do chatbot para o usuário, o que na prática seria construído e personalizado para cada empresa.

criamos dentro da variável context a prompt do sistema que funciona como o famoso “act as” do chatgpt informando que ele dever´se comportar como um serviço automatizado e coleta de pedidos de uma pizzaria e seu nome é “Orderbot”

Passamos também para o modelo todas as informações e preços do menu da nossa pizzaria (aqui é importante a formatação dos preços corretamente com pontos no lugar de vírgulas)

import panel as pn  # GUI
pn.extension()

panels = [] # collect display 

context = [ {'role':'system', 'content':"""
Você é o OrderBot, um serviço automatizado para coletar pedidos em uma pizzaria.\ 
Primeiro, você saúda o cliente e em seguida coleta o pedido.\
Depois, você pergunta se é para retirada ou entrega.\
Você aguarda para coletar o pedido completo,\ 
Você faz um resumo e verifica se o cliente deseja adicionar mais alguma coisa.\ 
Se for para entrega, você pede o endereço.\ 
Por fim, você coleta o pagamento.

Certifique-se de esclarecer todas as opções, extras e tamanhos para identificar exclusivamente o item do menu.\ 
Você responde de uma forma curta e amigável, como em uma conversa.\

O menu inclui:\

Pizza de pepperoni: R$ 12.95, R$ 10.00, R$ 7.00\
Pizza de queijo: R$ 10.95, R$ 9.25, R$ 6.50\
Pizza de berinjela: R$ 11.95, R$ 9.75, R$ 6.75\
Batatas fritas: R$ 4.50, R$ 3.50\
Salada grega: R$ 7.25\
Coberturas:\

Queijo extra: R$ 2.00\
Cogumelos: R$ 1.50\
Linguiça: R$ 3.00\
Bacon canadense: R$ 3.50\
Molho AI: R$ 1.50\
Pimentões: R$ 1.00\
Bebidas:\

Coca-Cola: R$ 3.00, R$ 2.00, R$ 1.00\
Sprite: R$ 3.00, R$ 2.00, R$ 1.00\
Água em garrafa: R$ 5.00\
"""} ]  # accumulate messages


inp = pn.widgets.TextInput(value="Hi", placeholder='Enter text here…')
button_conversation = pn.widgets.Button(name="Chat!")

interactive_conversation = pn.bind(collect_messages, button_conversation)

dashboard = pn.Column(
    inp,
    pn.Row(button_conversation),
    pn.panel(interactive_conversation, loading_indicator=True, height=300),
)

dashboard

Testando o modelo

O resultado final é um chatbot simples, porém poderoso! ele é capaz de entender contextos complexos e diferentes inputs de usuário e seguir com o pedido até o final.

Ele começa gerando uma interface vazia e com uma frase de introdução inicial que inicia a conversa com o cliente. Você então escreve qualquer coisa e ele irá te guiar automaticamente até o final do pedido.

Essa é uma simulação que eu fiz com ele pedido uma pizza de pepperoni do tamanho grande com molho extra e acabei não pedindo nenhuma bebida.

Ao concluir o pedido, o modelo traz um resumo com todas as informações do pedido, o endereço e o valor total incluindo ou não a taxa de entrega.

É interessante testar como que o modelo se comporta com diferentes tipos de inputs de usuário, por exemplo, quando ele me pergunta o tipo do pagamento eu devolvo com uma pergunta, se pode ser em dinheiro.

Contextos não relacionados

O mais interessante é que ao programar o chatgpt para seguir essa função, ele é extremamente eficiente em se manter alinhado ao seu objetivo e mesmo fazendo diversas perguntas complexas, diferentes e que não possuem contexto com o objetivo para o qual programamos, ele sempre tenta puxar o usuário de volta para o tópico central.

Isso é super importante pois torna o modelo de fato estável para ser utilizado em aplicações empresariais diversas e não causar problemas.

Comunicação com os sistemas e APIs internas

E para comunicar os resultados do pedido nós criamos através do seguinte código um resumo em .json que pode ser computado e enviado para o sistema de pedidos.

Nesse caso estamos usando uma temperatura mais baixa =”0″ para tornar o sistemas mais “previsível” tratando-se de uma comunicação direta com o usuário para evitar erros de entendimento ou comunicação que poder complicar o processo com os clientes.

messages =  context.copy()
messages.append(
{'role':'system', 'content':'create a json summary of the previous food order. Itemize the price for each item\
 The fields should be 1) pizza, include size 2) list of toppings 3) list of drinks, include size   4) list of sides include size  5)total price '},    
)
 #The fields should be 1) pizza, price 2) list of toppings 3) list of drinks, include size include price  4) list of sides include size include price, 5)total price '},    

response = get_completion_from_messages(messages, temperature=0)
print(response)

O resultado final é um .json estruturado e criado pelo próprio poder do chatgpt:

Um dos maiores poderes do chatgpt é a capacidade de interpretar, escrever e gerar códigos em diversas linguagens de programação. Nesse caso o output pode ser gerado em diversos formatos como csv, json, xml etc.. dependendo de cada contexto.

Deixando o modelo mais robusto

Uma dica é que para criar um sistema mais robusto podemos utilizar a biblioteca langchain que permite manipular banco de dados e fazer com que o chatgpt aprenda com seus dados e não extrapole as respostas para temas, contextos ou produtos externos de outras empresas que poderiam gerar ruído na sua conversação.

LangChain é um framework para desenvolvimento de aplicativos alimentados por modelos de linguagem. Acreditamos que os aplicativos mais poderosos e diferenciados não apenas utilizarão um modelo de linguagem, mas também serão:

  • Conscientes dos dados: conectar um modelo de linguagem a outras fontes de dados
  • Agentes: permitir que um modelo de linguagem interaja com seu ambiente

O framework LangChain é projetado com base nesses princípios.

Referências e recomendações de leitura:

Compartilhe nas redes sociais:
Alexandre Polselli
Alexandre Polselli

Escrevo artigos e desenvolvo projetos nas minhas áreas de maior interesse: Data Science, Data Analytics, Estatística e Probabilidade, Inteligência Artificial e Machine Learning.

Artigos: 23

Um comentário

  1. Eu achei os procedimentos informados detalhados e aplicáveis. Parabéns pelo trabalho. Uma boa divulgação do seu trabalho em andamento. Abraço meu querido filhão

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *