MindsDB + Docker: Utilizando SQL e containers para integrações com IA
Tags: docker, sql, openai
Published: 2024-05-24 22:10:07

Já pensou em criar sua própria IA, treiná-la com seus dados e utilizar esse "conhecimento" para alavancar seu app ou algum projeto em que você está trabalhando? Eu também pensei nisso e tive a mesma reação: "Nossa, que preguiça! Imagina quanto tempo eu ia perder fazendo isso..."

Quando pensamos em IA, geralmente imaginamos ter que começar do zero, aprender uma nova linguagem, escrever milhares de linhas de código para integrar com o app. Mas e se eu te disser que dá para fazer isso apenas usando SQL ou então fazendo uma requisição HTTP? É exatamente isso que o MindsDB faz por nós.

## O que é o MindsDB?

O MindsDB é uma plataforma que facilita a criação e integração de modelos de aprendizado de máquina (machine learning) diretamente com bancos de dados, utilizando SQL - sim, você não precisa aprender outra linguagem para trabalhar com IA 😅.

Ela permite que desenvolvedores, como nós, aproveitem os dados existentes em seus bancos de dados e construam modelos de aprendizado de máquina sem a necessidade de conhecimentos avançados em ciência de dados ou machine learning.

Em resumo, ele permite termos nossa IA personalizada para nossos dados direto da nossa aplicação. Se liga no diagrama abaixo, nele podemos ver como é a estrutura do MindsDB e um pouco de como é sua integração.


![MindsDB DIAGRAM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bw09zehz67e0hmnmwzqi.png)

### **Benefícios do MindsDB**

- **Facilidade de Uso:** Ideal para desenvolvedores que já estão familiarizados com SQL, eliminando a necessidade de aprender novas linguagens de programação.
- **Agilidade:** Reduz o tempo e esforço necessários para desenvolver e implementar modelos de machine learning.
- **Flexibilidade:** Funciona com uma ampla gama de bancos de dados e pode ser integrado em diversas aplicações.

Depois disso, você deve estar se perguntando: "Mas e aí, Rafa, será que é tão simples a integração e a brincadeira com ele?" Vamos colocar a mão na massa e ver se é assim mesmo…

## Contexto e Problema

Vamos imaginar que temos uma loja de café onde nossos usuários deixam seus reviews sobre o que acharam do nosso café. A cada 3 meses renovamos o cardápio da loja e queremos trocar os cafés que têm opiniões “neutras” ou “negativas” sobre eles.


![Coffee shop](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mkfkp917c45zu9blolxl.jpeg)

Queremos fazer isso de uma forma rápida e simples, sem ter que ficar lendo milhões de comentários para analisar um a um e, no fim, contar as opiniões sobre o que temos. Então, como todo bom programador, vamos utilizar a hype do momento: a IA para classificar esses comentários para nós e, de quebra, aprender uma tecnologia nova.

## Deploy com Docker

Eu gosto de facilitar tudo na minha vida, então a maneira mais simples de instalar uma ferramenta ou algum app sem ter que ficar configurando mil coisas em nossa máquina local é usando Docker 😄.

O MindsDB utiliza várias dependências para funcionar, como Python, MongoDB, MySQL, e utilizando a imagem Docker não temos que nos preocupar em instalar nada disso em nossa máquina.

> Para seguir esse tutorial, você deve ter o Docker instalado e rodando em sua máquina. Caso ainda não tenha ele instalado, basta seguir o tutorial da [documentação oficial](https://docs.docker.com/desktop/).
> 

Como eu quero trazer um exemplo de uso real, vamos ter dois containers rodando: um com o MindsDB e outro com um MySQL, que é o banco de dados onde já existem nossos comentários sobre os cafés.

Para ter acesso ao projeto, [basta baixar a pasta dele no meu Github](https://github.com/rflpazini/articles/tree/main/mindsdb_docker), onde teremos a seguinte estrutura dentro da pasta:

```bash
mindsdb_docker
    ├── compose.yml //incia os nossos containers
    └── dump.sql    //insere os dados em nosso DB
```

Com o projeto baixado, é hora de rodá-lo localmente. Para executar o docker compose, digite o seguinte comando:

```bash
$ docker compose up -d --build
```

Após realizar o comando, você receberá a seguinte mensagem em seu terminal dizendo que tudo foi inicializado:

```bash
 ✔ Network mindsdb_sample_default      Created      0.0s 
 ✔ Container mindsdb_sample-mysql-1    Started      0.3s 
 ✔ Container mindsdb_sample-mindsdb-1  Started      0.3s 
```

Agora é só acessar a URL do MindsDB que é [`http://localhost:47334`](http://localhost:47334/) para ver a interface gráfica dele.

![Interface mindsDB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15na0umza7kdm8h2r8iv.png)

## Configurando a conexão com DB

Como já temos a instância do MindsDB rodando localmente, vamos configurar a conexão dele com nossa base de dados, de onde serão retiradas as reviews.

Para isso, basta digitar o seguinte comando SQL:

```sql
CREATE DATABASE mysql_sample
WITH ENGINE = 'mysql',
PARAMETERS = {
    "user": "root",
    "password": "r00tPassword123",
    "host": "<ip-local-do-seu-computador>", --troque pelo seu ip local
    "port": "3306",
    "database": "cool_data"
};
```

> Um detalhe importante é que no host devemos colocar o IP local do nosso computador. Por exemplo, o meu é **`10.0.2.10`** pois não podemos esquecer que estamos dentro de um container e que a porta mapeada para acesso é a do nosso host. Se colocarmos **`localhost`**, ele não encontrará o banco de dados. Caso não saiba qual é seu IP, aqui vai um comando que pode ajudar 😄
> 

```bash
$ ifconfig | grep \"inet \" | grep -v 127.0.0.1 | cut -d\  -f2 
```

O que estamos fazendo com esse comando SQL:

- Criando a conexão com o banco e dizendo que ela se chamará **`mysql_sample`**.
- Dizendo qual é a ENGINE de banco de dados que queremos utilizar.
- Passando os parâmetros de conexão: usuário, senha, host e o nome do banco de dados que vamos nos conectar.

Agora vamos validar se nossa conexão está funcionando. Para isso, vamos usar o bom e velho SELECT do nosso querido SQL:

```sql
SELECT * 
FROM mysql_sample.coffee_review
LIMIT 2;
```

Ele deve retornar os valores que existem em nosso banco de dados. No caso, temos uma tabela chamada **`coffee_review`** e os seguintes campos: **`id`**, **`coffee_name`**, **`origin`** e **`review`** com os dados já preenchidos. Note também que durante a consulta SQL utilizei o nome da conexão que criamos no passo acima para dizer para o MindsDB que quero pegar a tabela **`coffee_review`** que está dentro do meu banco **`mysql_sample`**.

Assim sabemos que temos uma conexão de sucesso entre a nossa base de dados e o MindsDB

## Criando uma ML Engine

O MindsDB, como eu disse no início, tem suporte para vários plugins e um deles são as ML Engines. No nosso caso, como queremos fazer a análise dos comentários, vamos utilizar a OpenAI engine e para isso precisamos configurá-la.

> Para o próximo passa você precisará de uma API Key e consegue gera-la no próprio site da OpenAI:  [platform.openai.com](https://platform.openai.com/settings/organization/billing/payment-methods)
> 

Com a API Key em mãos podemos criar a ML Engine dentro do MindsDB, é bem tranquilo:

```sql
CREATE ML_ENGINE openai
FROM openai
USING
    openai_api_key = 'sk-xxxx'; --substitua o valor pela sua chave
```

O MindsDB disponibiliza o handler, que é a integração do MindsDB com as APIs das IAs. Então, o que temos que fazer é apenas dizer que queremos criar uma nova engine usando o handler específico para ela. Existem vários e você pode encontrar a lista deles na documentação. No código acima, estamos criando a **`openai`** engine, usando o handler da **`openai`** e passando a nossa API key para ele.

Essa simples configuração já faz a integração do nosso MindsDB com a IA que escolhemos.

## Hora de criar o Modelo

Quando falamos de modelo, em palavras simplificadas, é o modelo de aprendizagem que a IA vai utilizar para fazer a tarefa que queremos que ela execute.

No MindsDB vamos criar este modelo de aprendizagem utilizando SQL e passando algumas informações para ele. 

Assim ficará nosso modelo que classifica os comentários sobre o café como positivo, negativo e neutro.

```sql
CREATE MODEL coffee_review_sentiment
PREDICT sentiment
USING
    engine = 'openai',
    model_name = 'gpt-4',
    prompt_template = 'descreva o sentimento dos comentários apenas como "positivo", "negativo" ou "neutro".
                    "Amei este café":positivo
                    "Não me agradou": negativo
                    "{{review}}.":';
```

No código acima, a primeira coisa que fazemos é dar um nome para o modelo, que se chama **`coffee_review_sentiment`**. Logo em seguida, falamos para o modelo que ele vai “prever” a propriedade **`sentiment`**, ou seja, estamos falando que o resultado do nosso prompt será exibido nessa coluna. Por fim, dizemos qual engine queremos usar: **`openai`**, que é a que acabamos de configurar. Dizemos também que queremos utilizar o **`gpt-4`** como modelo de IA e como estamos usando a OpenAI, ela sempre espera um prompt de comando, que é onde passamos para o modelo o que esperamos que ele faça e com qual informação ele vai trabalhar.

Após executar o comando de criar, você provavelmente receberá o seguinte output:

```markdown
| TABLES              | NAME                      | ENGINE | PROJECT | ACTIVE | VERSION | STATUS     | ACCURACY | PREDICT   | UPDATE_STATUS | MINDSDB_VERSION | ERROR  | SELECT_DATA_QUERY | TRAINING_OPTIONS                                                                                                                                                                                                                                                                                                                 | TAG    |
|---------------------|---------------------------|--------|---------|--------|---------|------------|----------|-----------|---------------|-----------------|--------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|
| ["args","metadata"] | coffee_review_sentiment_6 | openai | mindsdb | true   | 1       | generating | [NULL]   | sentiment | up_to_date    | 24.4.3.0        | [NULL] | [NULL]            | {'__mdb_sql_task': None, 'target': 'sentiment', 'using': {'model_name': 'gpt-4', 'prompt_template': 'descreva o sentimento dos comentários apenas como "positivo", "negativo" ou "neutro".\n                    "Amei este café":positivo\n                    "Não me agradou": negativo\n                    "{{review}}.":'}} | [NULL] |

```

Repare que na coluna **`STATUS`** temos o valor **`generating`**, ou seja, o nosso modelo está sendo gerado ainda. Ainda não podemos utilizá-lo em nossas consultas; para ele se tornar útil o status deve ser **`complete`**. Para consultar o valor do status, utilizamos o comando **`DESCRIBE`** para descrever o que temos na nossa tabela de modelo:

```sql
DESCRIBE coffee_review_sentiment;
```

Quando o status for **`complete`**, você verá no resultado da query:

```sql
| TABLES              | NAME                      | ENGINE | PROJECT | ACTIVE | VERSION | STATUS   | ACCURACY | PREDICT   | UPDATE_STATUS | MINDSDB_VERSION | ERROR  | SELECT_DATA_QUERY | TRAINING_OPTIONS                                                                                                                                                                                                                                                                                                                 | TAG    |
|---------------------|---------------------------|--------|---------|--------|---------|----------|----------|-----------|---------------|-----------------|--------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|
| ["args","metadata"] | coffee_review_sentiment_6 | openai | mindsdb | true   | 1       | complete | [NULL]   | sentiment | up_to_date    | 24.4.3.0        | [NULL] | [NULL]            | {'__mdb_sql_task': None, 'target': 'sentiment', 'using': {'model_name': 'gpt-4', 'prompt_template': 'descreva o sentimento dos comentários apenas como "positivo", "negativo" ou "neutro".\n                    "Amei este café":positivo\n                    "Não me agradou": negativo\n                    "{{review}}.":'}} | [NULL] |

```

## Aplicando a classificação

Agora que temos o modelo pronto e configurado, é hora de classificar nossos reviews. Faremos isso utilizando os comandos mais clássicos de SQL: **`SELECT`** e **`FROM`** 😊.

Relembrando o que queremos fazer, queremos classificar os reviews dos nossos cafés como neutro, positivo e negativo. Para nosso exemplo, acho legal mostrar primeiro qual é o sentimento e depois o comentário caso for necessário ler o comentário que recebeu aquela avaliação.

Então vamos para o nosso select:

```sql
SELECT m.sentiment, t.review         --campos que quero exibir na minha consulta
FROM mysql_sample.coffee_review AS t --tabela do DB que contem o review
JOIN mindsdb.coffee_review AS m;     --tabela que contém a previsão da IA
```

Aqui fazemos um pequeno select usando join para obter o nosso sentimento e logo em seguida o review que teve aquela análise de nossa IA. Seu resultado deve ser o seguinte:

```markdown
| sentiment | review                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| positivo  | Assim que eu levei a xÃ­cara aos lÃ¡bios, parecia que eu estava num mercado de especiarias. O aroma floral e cÃ­trico tomou conta e me deixou super curioso. No primeiro gole, senti uma explosÃ£o de sabores complexos: limÃ£o e bergamota, com um toque sutil de jasmim. A acidez brilhante equilibrada por uma doÃ§ura suave, quase como mel, foi incrÃ­vel. A cada gole, o cafÃ© ia mostrando mais camadas, deixando um gostinho refrescante na boca. Ã‰ como uma danÃ§a de sabores no paladar, perfeito para comeÃ§ar o dia cheio de energia e inspiraÃ§Ã£o.                        |
| positivo  | Desde que o espresso comeÃ§ou a sair, o cheiro rico e achocolatado tomou conta do lugar. O primeiro gole foi como um abraÃ§o quentinho - notas de chocolate amargo, nozes torradas e um toque de caramelo. A crema espessa dÃ¡ uma textura cremosa, deixando tudo ainda mais gostoso. A doÃ§ura natural e o corpo encorpado desse blend sÃ£o bem tÃ­picos dos cafÃ©s brasileiros. Cada gole traz uma sensaÃ§Ã£o de conforto e familiaridade, perfeito para uma pausa Ã  tarde ou um bate-papo com amigos.                                                                              |
| positivo  | Quando comecei a prensar o cafÃ© na PrenÃ§a Francesa, o cheiro doce e terroso comeÃ§ou a se espalhar pelo ambiente. No primeiro gole, jÃ¡ senti uma profundidade incrÃ­vel: notas de frutas vermelhas, como cereja e amora, com um toque leve de cacau. A acidez suave e a doÃ§ura das frutas se equilibram perfeitamente, e a textura Ã© super aveludada. Esse cafÃ© me fez sentir super conectado com a natureza, como se eu estivesse saboreando os frutos direto da terra. Ã‰ uma experiÃªncia revigorante e autÃªntica, perfeita para quem curte um cafÃ© cheio de personalidade. |
| neutro    | Preparei esse cafÃ© na mÃ¡quina automÃ¡tica e o aroma que surgiu foi ok, nada demais. No primeiro gole, o sabor era simples e direto, sem muita complexidade. Tinha notas de nozes levemente tostadas e um toque sutil de chocolate ao leite. A acidez estava bem equilibrada e o corpo era mÃ©dio, dando uma sensaÃ§Ã£o satisfatÃ³ria, mas sem impressionar. Esse cafÃ© nÃ£o tem nada de muito especial, mas Ã© uma escolha segura pra quem quer um cafÃ© consistente e previsÃ­vel, sem grandes surpresas.                                                                           |
| negativo  | Ao coar esse cafÃ©, o aroma foi bem fraquinho e nada convidativo. O primeiro gole foi uma decepÃ§Ã£o - um sabor raso e sem vida, com um amargor queimado que lembrava borra de cafÃ© velha. A acidez estava desbalanceada e a textura era bem aquosa, deixando uma sensaÃ§Ã£o desagradÃ¡vel na boca. NÃ£o havia qualquer traÃ§o de doÃ§ura ou complexidade para salvar a experiÃªncia. Cada gole foi um esforÃ§o para terminar a xÃ­cara, e fiquei frustrado e arrependido de nÃ£o ter escolhido um cafÃ© melhor.                                                                      |
```

Pronto, temos todos nossos reviews classificados com o “sentimento”.

Ótimo!! mas e se formos um pouco além e melhorarmos esse select com outro modelo para termos acesso aos motivos que levaram esse comentário a expressar aquele sentimento? hehehe

### Detalhando a classificação

Agora vamos melhorar um pouco mais nossa classificação e inserir os motivos para ela. Isso facilitaria, por exemplo, a vida de um executivo que não quer ficar horas lendo comentários para entender o que o seu cliente escreveu, mas quer otimizar o tempo indo direto para os principais motivos do que aconteceu. A famosa leitura dinâmica virando leitura eficiente 😀

Para facilitar nosso lado de “dev”, vamos criar outro modelo, onde diremos a ele para listar os principais motivos do sentimento por trás daquele review.

```sql
CREATE MODEL coffee_review_reasons
PREDICT reasons
USING
    engine = 'openai',
    model_name = 'gpt-4',
    prompt_template = 'liste resumidamente os principais motivos do sentimento: "{{review}}";';
```

Como estamos acostumados, criamos o modelo com o nome **`coffee_review_reasons`**, onde ele vai ter o output na coluna **`reasons`**. No prompt, pedimos de uma forma direta para que ele liste resumidamente os principais motivos do sentimento; esse sentimento virá do nosso campo **`review`** que está em nosso DB.

Agora é só sentar e melhorar nosso select que acabamos de fazer, e como melhoraremos ele? Através de `JOINS`:

```sql
SELECT t.id, m.sentiment, m2.reasons 
FROM mysql_sample.coffee_review AS t
JOIN coffee_review_sentiment AS m
JOIN coffee_review_reasons AS m2
WHERE m.sentiment = 'negativo' OR m.sentiment = 'neutro'
```

Para explicar esse nosso novo select: Primeiro dizemos que queremos exibir o **`id`**, o sentimento **`sentiment`** e os motivos **`reasons`**. Logo em seguida pegamos o review do nosso banco de dados, depois fazemos os JOINS com o primeiro modelo que é o **`coffee_review_sentiment`** e depois com o novo modelo **`coffee_review_reasons`**. Como acho que o que importa neste momento são apenas termos mais detalhes dos negativos e neutros, colocamos uma cláusula WHERE filtrando apenas por neutro e negativo. Mais uma análise completa!

Esse é o resultado de nossa análise final, com os cafés que têm sentimento negativo e neutro sobre eles e os principais motivos de terem tido este julgamento resumidos para nós:

```markdown
| id | sentiment | reasons                                                                                                                                                                                                                                                                                                                                                                      |
|----|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 5  | negativo  | 1. Aroma fraco e não convidativo: O aroma do café é um dos primeiros indicadores de sua qualidade. Neste caso, o aroma fraco pode ter indicado que o café não seria satisfatório.2. Sabor raso e sem vida: Um bom café deve ter um sabor rico e complexo. O sabor raso e sem vida deste café foi uma grande decepção.3. Amargor queimado: O amargor                          |
| 4  | neutro    | 1. Aroma: O aroma do café preparado na máquina automática foi considerado apenas "ok", não se destacando de maneira especial.2. Sabor: O sabor do café foi descrito como simples e direto, sem muita complexidade. As notas de nozes levemente tostadas e um toque sutil de chocolate ao leite foram identificadas, mas não trouxeram um diferencial significativo.3. Acidez |

```

### Inserindo nossa consulta em uma nova tabela

Agora você deve estar se perguntando: “Poxa, mas como que eu salvo esses dados da análise?”. Bom, temos algumas formas de fazer isso; para facilitar nossa vida, que tal salvá-las no banco de dados em uma nova tabela?

Vamos criar essa tabela com a ajuda do SQL novamente e usando o **`CREATE TABLE`**. Então reescreva o último select que fizemos, mas agora vamos inseri-lo junto com o comando de criar uma nova tabela em nosso banco de dados:

```sql
CREATE TABLE mysql_sample.reviews_processed (
    SELECT t.id, m.sentiment, m2.reasons 
		FROM mysql_sample.coffee_review AS t
		JOIN coffee_review_sentiment AS m
		JOIN coffee_review_reasons AS m2
		WHERE m.sentiment = 'negativo' OR m.sentiment = 'neutro'
);
```

Dessa forma, garantimos que temos um registro salvo e não precisamos ficar reprocessando a nossa análise toda vez que quisermos consultar o que os clientes estão achando de nossos cafés.

E assim terminamos nosso sistema de análises de reviews sobre cafés e podemos tirar os cafés menos desejados de nosso cardápio 🥰

## Benefícios da analise

A análise de sentimento é uma ferramenta poderosa que permite entender as opiniões e emoções dos clientes em relação a produtos ou serviços. No contexto de uma loja de café, como exemplificado no artigo, a implementação de uma IA para realizar essa análise pode trazer inúmeros benefícios estratégicos, como por exemplo: 

- **Melhoria da Experiência do Cliente:** permite uma resposta rápida às críticas dos clientes, ajudando a resolver problemas antes que eles se tornem amplamente conhecidos e afetem a reputação da marca.
- **Tomada de Decisões Baseada em Dados:** com insights sobre quais produtos são mais apreciados e quais não agradam, a empresa pode tomar decisões informadas sobre quais itens manter, modificar ou remover do cardápio.
- **Vantagem Competitiva:** negócios que utilizam a análise de sentimento conseguem se adaptar rapidamente às mudanças nas preferências dos consumidores, mantendo-se à frente da concorrência, podendo inovar de maneira contínua.

Entre outros que não citei aqui, mas hoje em dia o produto mais valioso que temos em mãos são os dados, então devemos dar um carinho especial para eles.

## Conclusão

A combinação do MindsDB e Docker revoluciona a maneira como integramos IA e machine learning em nossos projetos, tornando todo o processo incrivelmente simples e acessível. Com o MindsDB, desenvolvedores podem criar e implementar modelos de aprendizado de máquina usando apenas SQL, eliminando a necessidade de aprender novas linguagens de programação complexas. Docker, por sua vez, facilita a implantação dessas tecnologias, permitindo que várias dependências sejam gerenciadas de forma eficiente através de containers. Essa sinergia não só acelera o desenvolvimento e a implementação de soluções de IA, mas também reduz significativamente a complexidade operacional, permitindo que negócios de todos os tamanhos aproveitem o poder da IA para melhorar a experiência do cliente e impulsionar o crescimento.