Tentando não ficar pobre antes de ficar rico criando uma Startup de serviços de inteligência artificial
Tags: gpt, firestore, googlcloudfunction
Published: 2023-04-04 20:17:18


Neste post, vou mostrar como utilizar um banco de dados vetorial para diminuir os [custos com tokens](https://openai.com/pricing) do GPT em uma aplicação de perguntas e respostas. O banco de dados vetorial que eu escolhi foi o [Pinecone](https://www.pinecone.io/), que permite armazenar e consultar vetores de alta dimensão de forma eficiente e escalável. A ideia é transformar as perguntas e as respostas em vetores usando um modelo de linguagem natural pré-treinado, como o modelo [text-embedding-ada-002](https://platform.openai.com/docs/models/embeddings), e depois usar o [Pinecone](https://www.pinecone.io/) para encontrar as respostas mais similares às perguntas dos usuários.
>  Bancos de dados vetoriais podem oferecer melhores resultados de consultas de texto do que os bancos de dados SQL porque eles usam uma representação matemática dos dados chamada vetor, que permite medir a similaridade entre documentos e consultas usando operações como distância ou ângulo. Os bancos de dados SQL, por outro lado, usam uma linguagem de consulta estruturada (SQL) que pode ser limitada para pesquisar correspondências exatas ou difusas de palavras ou frases usando o comando CONTAINS. Além disso, os bancos de dados SQL podem exigir mais recursos e tempo para indexar e pesquisar grandes quantidades de dados de texto do que os bancos de dados vetoriais.

Para gerenciar as perguntas, eu usei o [Firebase Firestore](https://firebase.google.com/docs/firestore?hl=pt-br), um banco de dados NoSQL na nuvem que oferece sincronização em tempo real e suporte offline. Cada pergunta é armazenada em um documento com um identificador único e um campo para a resposta. O [Firestore](https://firebase.google.com/docs/firestore?hl=pt-br) também permite criar funções na nuvem que são acionadas por eventos no banco de dados, como a criação, atualização ou exclusão de documentos. Eu usei uma [Cloud Function](https://cloud.google.com/functions/docs/concepts/functions-and-firebase?hl=pt-br) para disparar um evento sempre que uma nova pergunta for cadastrada no Firestore. Essa função é responsável por enviar a pergunta para a API do [ChatGPT](https://platform.openai.com/docs/guides/chat), um serviço que usa o modelo [GPT-3.5-turbo](https://openai.com/pricing) para gerar respostas conversacionais. O [ChatGPT](https://platform.openai.com/docs/guides/chat) retorna a resposta em formato de texto, que é então convertida em um vetor usando o mesmo modelo de linguagem natural que foi usado para as perguntas. Esse vetor é enviado para o [Pinecone](https://www.pinecone.io/), que armazena o vetor para futuras novas perguntas.

![Infra estrutura na versão 1.](https://cdn-images-1.medium.com/max/2000/1*6BRpmbl720xouv8qlbBj9Q.png)

Uma vantagem de usar o [Pinecone](https://www.pinecone.io/) é que ele permite fazer consultas por similaridade usando os vetores das perguntas e das respostas. Assim, quando um usuário faz uma pergunta, eu não preciso enviar a pergunta para o [ChatGPT](https://platform.openai.com/docs/guides/chat) e gastar tokens do GPT. Eu posso simplesmente converter a pergunta em um vetor e enviar para o [Pinecone](https://www.pinecone.io/), que me retorna os identificadores dos vetores mais similares. Eu posso adotar uma resposta existente como a resposta da pergunta baseado no score de similaridade entre a nova pergunta e as perguntas que já foram feitas anteriormente. Isso reduz significativamente os custos com tokens do GPT, já que eu só preciso usar o [ChatGPT](https://platform.openai.com/docs/guides/chat) para gerar respostas para perguntas novas ou muito diferentes das existentes.
>  Pode ser um grande desafio encontrar a resposta exata para a pergunta do usuário com base nas perguntas realizadas anteriormente. Exemplo: ‘Quanto custa 1kg do seu produto?’ ou ‘Quanto custa 1g do seu produto?’. Possivelmente o score do texto vetorizado das duas perguntas terá um score quase de 1.0. Isso pode ser um problema dependendo do seu interesse. Não cheguei a pensar em algo concreto para resolver esse problema, mas acredito que haja formas de contornar isso definindo diferenciações entre algumas palavras tais como ‘kilograma’ e ‘grama’.

Para fazer um comparativo entre o custo entre as consultas do [Pinecone](https://www.pinecone.io/) e o modelo [GPT-3.5-turbo](https://openai.com/pricing), eu usei os seguintes valores:

- O preço do [Pinecone](https://www.pinecone.io/) é de $0.096 / hora.
- O preço do [ChatGPT](https://platform.openai.com/docs/guides/chat) é de $0.002 / 1K tokens do gpt-3.5-turbo.

>  *Tokens* são sequências comuns de caracteres encontrados no texto. O GPT processa o texto usando tokens e entende as relações estatísticas entre eles. Os tokens podem incluir espaços e até sub-palavras. O número máximo de tokens que o GPT pode receber como entrada depende do modelo e do tokenizador usados. Por exemplo, o modelo text-embedding-ada-002 usa o tokenizador cl100k_base e pode receber até 8191 tokens.

Cheguei a considerar o uso do [Cloud Function](https://cloud.google.com/functions/docs/concepts/functions-and-firebase?hl=pt-br) como plataforma de hospedagem do meu código de consulta ao [ChatGPT](https://platform.openai.com/docs/guides/chat), mas me traria altos custos financeiros. Isso porque o [Cloud Function](https://cloud.google.com/functions/docs/concepts/functions-and-firebase?hl=pt-br) cobra de acordo com o tempo de execução da sua função, além do número de invocações e dos recursos provisionados. Como a API do [ChatGPT](https://platform.openai.com/docs/guides/chat) pode demorar para responder, dependendo da complexidade da sua consulta, você pode acabar pagando muito pelo tempo que a sua função fica aguardando a resposta da API.

Uma forma que encontrei de reduzir os custos com o [ChatGPT](https://platform.openai.com/docs/guides/chat) é usar um web service no [Render.com](https://render.com/), onde é possível criar uma aplicação em [Flask](https://flask.palletsprojects.com/) onde eu consegui utilizar threads para não aguardar a API do GPT responder antes de já responder ao [Cloud Function](https://cloud.google.com/functions/docs/concepts/functions-and-firebase?hl=pt-br). O [Render.com](https://render.com/) é uma plataforma que permite hospedar aplicações web de forma simples e barata, com planos a partir de $7 por mês. A ideia é criar uma camada intermediária entre o [Cloud Function](https://cloud.google.com/functions/docs/concepts/functions-and-firebase?hl=pt-br) e o [ChatGPT](https://platform.openai.com/docs/guides/chat), que recebe a pergunta do [Cloud Function](https://cloud.google.com/functions/docs/concepts/functions-and-firebase?hl=pt-br) e envia uma resposta imediata dizendo que a resposta está sendo gerada. Em seguida, a aplicação em Flask cria uma thread para enviar a pergunta para a API do [ChatGPT](https://platform.openai.com/docs/guides/chat) e assim que obtiver a resposta, atualize a pergunta no [Firestore](https://firebase.google.com/docs/firestore?hl=pt-br).

![Infra estrutura na versão 2.](https://cdn-images-1.medium.com/max/2000/1*CYRWTNaKwipU3b2KEEo7fw.png)

Havia pensado em descrever o código fonte do projeto nesse post. Mas acho que por hora, vale a reflexão sobre a infra aplicada. Posteriormente estarei estarei criando novos post para detalhar mais sobre o projeto em NodeJS inserido no Google Cloud Function e o projeto em Python (Flask) hospedado no Render.com.

A aplicação web responsável por inserir e consultar as perguntas no Firestore ainda está em fase de desenvolvimento. Pretendo publicá-la em breve também.