Data Science Portfolio
FinTech

Integração de Processamento de Linguagem Natural, Machine Learning e IA Generativa para Gestão de Respostas Automáticas em E-Commerce.

L. G. G. Pimenta

Este projeto utiliza o dataset público da Olist, para construir uma solução de inteligência de dados sobre 40.977 avaliações de consumidores. O objetivo é demonstrar como técnicas de NLP (Natural Language Processing) e Machine Learning podem ser integradas para automatizar a triagem de mensagens e gerar respostas automáticas em massa.

CARACTERÍSTICAS DO PROJETO:

  1. Otimização de Performance: Processamento paralelo escalável (Multi-core)
  2. Arquitetura Generativa (RAG): Implementação de Retrieval-Augmented Generation com busca semântica via banco vetorial (FAISS) e SentenceTransformers.
  3. Explicabilidade (XAI): Análise de impacto de variáveis utilizando SHAP (SHapley Additive exPlanations) para identificar os termos determinantes nas predições.
  4. Métricas de Robustez: AUC-ROC (0.92), F1-Score estratificado e análise de similaridade por distância euclidiana (L2) no espaço vetorial.
  5. Integração de Diferentes Modelos: Resultados de medidas estatísticas são entregues diretamente de um modelo para outro, funcionando como "trava de segurança" e como contexto para as respostas automatizadas.

1.1 - O Pipeline

Neste projeto utilizamos: Machine Learn (ML), Natural Language Processing (NLP) e IA Generativa.

1.1.1 - NLP e IA Genertiva

O NLP (Processamento de Linguagem Natural) é o conjunto de processos que dá origem à nossa "matemática linguística". Através de suas técnicas, realizamos a limpeza dos dados — incluindo a remoção de stopwords e a lematização via biblioteca spaCy — para transformar textos em vetores numéricos (embeddings). É nesse espaço vetorial que calculamos as distâncias semânticas utilizando o modelo SentenceTransformer (MiniLM). Para mitigar alucinações e garantir resultados alinhados ao histórico da empresa, implementamos a arquitetura RAG (Retrieval-Augmented Generation): o sistema realiza uma busca por similaridade em nosso banco de dados (via biblioteca FAISS) e fornece os casos reais como contexto para que a IA local (Llama 3) redija uma resposta personalizada. Todo este fluxo, desde a recuperação de dados até a construção do prompt, é orquestrado pela biblioteca LangChain.

1.1.2 - ML

A camada de Machine Learning (ML) inicia-se na transformação do dataset em uma Matriz de Importância via TfidfVectorizer (Scikit-learn). Cada linha representa uma reclamação, onde os termos são convertidos em pesos estatísticos: o valor é proporcional à frequência da palavra na reclamação, mas inversamente ao quanto ela aparece no Corpus. O XGBoost atua na interpretação desses padrões; ele processa as colunas da matriz para aprender quais combinações de pesos indicam determinados review_scores. O modelo é então treinado para calcular a probabilidade de uma nova entrada corresponder a um cenário de insatisfação, utilizando processamento paralelo em 8 núcleos para garantir rapidez e eficiência nessa análise preditiva.

1.1.3 - Dados

O dataset foi obtido na plataforma Kaggle (link para download na bibliografia) e é constituído de vários arquivos. Neste projeto, utilizamos exclusivamente o arquivo olist_order_reviews_dataset.csv, pois nele residem os scores e as mensagens de texto dos clientes. Como a modelagem de Machine Learning requer dados etiquetados, filtramos apenas os registros que continham tanto a avaliação quanto o comentário, resultando em um total de 40.977 registros.

  • review_comment_message: Comentários reais do cliente sobre o produto, abrangendo desde reclamações críticas (ex: produto danificado) até elogios (ex: entrega rápida).
  • review_score: Nota de 1 a 5 atribuída pelo cliente, servindo como a etiqueta (label) fundamental para o treinamento supervisionado do modelo.

ETL (Extraction, Transform and Load)

2.1 - Limpando Dados

Reunimos as três features de interesse em um só dataframe (score, mensagem e target), sendo target uma feature binária criada por nós, onde o valor 0 representa scores 4 ou 5 (target = 0, cliente satisfeito) e o valor 1 representa os demais (target = 1, cliente insatisfeito). Utilizamos a biblioteca spaCy para a limpeza dos dados, realizando a remoção de stopwords, conversão para lower case e o processo de lematização — que normaliza variações como "atrasou", "atrasado" e "atrasaram" para um radical único. As linhas vazias resultantes deste processo de tratamento foram excluídas.

Ao fim da limpeza, o dataset consolidou 39.528 registros, distribuídos em uma proporção de 64% de avaliações positivas (target = 0) e 36% de avaliações negativas (target = 1), configurando um cenário realista de dados desbalanceados para o modelo.

Código:

Data Cleaning - Python

import os
import pandas as pd
import spacy
from collections import Counter

# Setando hardware e caminhos de arquivos
N_CORES = 8
DATA_PATH = "DATA/olist_order_reviews_dataset.csv" 



def carregar_e_limpar_dados(path):
    if not os.path.exists(path):
        raise FileNotFoundError(f"Arquivo nao encontrado em: {path}")
        
    df = pd.read_csv(path)
    # Filtramos os registros que não contem mensagens
    df = df.dropna(subset=['review_comment_message'])
    
    # Aqui salvamos o resultado do teste lógico: se a nota dada ao 
    # cliente é <= 3 então resultado é True (1), caso contrário é 0.
    df['target'] = (df['review_score'] <= 3).astype(int)
    
    return df[['review_comment_message', 'target']].copy()

if __name__ == "__main__":
    try:
        print(f"Iniciando processamento de registros.")
        
        
        df = carregar_e_limpar_dados(DATA_PATH)
        
        # Setup do spaCy para processamento em partes.
        nlp = spacy.load("pt_core_news_lg", disable=["ner", "parser"])
        
        print(f"Processando textos com nlp.pipe em {N_CORES} cores...")
        
        # Usamos o nlp.pipe pois ele lida melhor com grandes volumes e evita o erros de buffer
        texts = df['review_comment_message'].astype(str).tolist()
        processed_texts = []
        
        for doc in nlp.pipe(texts, n_process=N_CORES, batch_size=1000):
            # Aqui nós retiramos stopwords e deixamos tudo em minúsculo
            tokens = [token.text.lower() for token in doc if token.is_alpha and not token.is_stop]
            processed_texts.append(" ".join(tokens))
        
        df['text_clean'] = processed_texts
        
        # Algumas linha ficam vazias depois da limpeza, abaixo nós fazemos
        # a remoção dessas linhas.
        df = df[df['text_clean'] != ""]
        
        # Verificamos algumas características do dataframe final obtido.
        all_words = " ".join(df['text_clean']).split()
        common_words = Counter(all_words).most_common(10)
        
        print("\n--- Relatorio de Preprocessamento ---")
        print(f"Top 10 palavras mais frequentes: {common_words}")
        print(f"Distribuicao das Classes Target (0=Neg, 1=Pos):")
        print(df['target'].value_counts(normalize=True))
        
        # Salvando o dataset.
        df.to_csv("dataset_processed.csv", index=False)
        print(f"\nSucesso! {len(df)} registros salvos em 'dataset_processed.csv'.")

    except Exception as e:
        print(f"Erro durante a execucao: {e}")

                    
Output:
Output Código Data cleaning

Iniciando processamento de registros.
Processando textos com nlp.pipe em 8 cores...

--- Relatorio de Preprocessamento ---
Top 10 palavras mais frequentes: [('produto', 18380), ('prazo', 8443), ('entrega', 6501), ('chegou', 5541), ('recebi', 5267), ('recomendo', 4296), ('entregue', 3777), ('veio', 3278), ('qualidade', 2765), ('comprei', 2756)]
Distribuicao das Classes Target (0=Neg, 1=Pos):
target
0    0.639091
1    0.360909
Name: proportion, dtype: float64

Sucesso! 39528 registros salvos em 'dataset_processed.csv'.



                

2.2 - Treinamento

Durante o treinamento do XGBoostClassifier, otimizamos o tempo de processamento utilizando os 8 núcleos disponíveis da CPU. Para equilibrar a performance com a explicabilidade do modelo, limitamos o vocabulário a 2.000 features durante a vetorização por TF-IDF. Além disso, aplicamos a técnica de RandomizedSearchCV para realizar uma busca num espaço predeterminado de hiperparâmetros, garantindo a seleção da combinação que resultasse na melhor AUC-ROC para os dados de teste.

Código treinamento - Python

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import joblib
import shap
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics import (
    classification_report, 
    roc_auc_score, 
    ConfusionMatrixDisplay, 
    precision_recall_curve, 
    PrecisionRecallDisplay
)
from xgboost import XGBClassifier

print("Carregando dados")
df = pd.read_csv("dataset_processed.csv")
# Excluindo qualquer observação com "na's"
df = df.dropna(subset=['text_clean'])

# Amostragem treino/teste, estratificada
X = df['text_clean']
y = df['target']
X_train_raw, X_test_raw, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# Vetorização TF-IDF
print("Vetorizando textos...")
vectorizer = TfidfVectorizer(max_features=2000, ngram_range=(1, 2))
X_train = vectorizer.fit_transform(X_train_raw)
X_test = vectorizer.transform(X_test_raw)

# OTIMIZAÇÃO DE HIPERPARÂMETROS (RANDOMIZED SEARCH)
print("Iniciando busca de hiperparâmetros (30 tentativas nos 8 núcleos)...")

# Espaço de possibilidades (Hiperparâmetros)
param_dist = {
    'n_estimators': [100, 300, 500],
    'max_depth': [3, 6, 10],
    'learning_rate': [0.01, 0.1, 0.2],
    'subsample': [0.7, 0.8, 0.9]
}

# Modelo base
xgb_base = XGBClassifier(
    n_jobs=8, 
    random_state=42, 
    use_label_encoder=False, 
    eval_metric='logloss'
)

# Buscador aleatório com Validação Cruzada (CV=3)
random_search = RandomizedSearchCV(
    estimator=xgb_base, 
    param_distributions=param_dist, 
    n_iter=10, 
    cv=3, 
    n_jobs=8, 
    random_state=42,
    verbose=1
)

random_search.fit(X_train, y_train)

# Definindo o melhor modelo após a busca
model = random_search.best_estimator_
print(f"\nMelhores parâmetros encontrados: {random_search.best_params_}")

# Analisando resultados
preds = model.predict(X_test)
probs = model.predict_proba(X_test)[:, 1]

print("\n--- Métricas de Performance ---")
print(classification_report(y_test, preds))
print(f"AUC-ROC Score: {roc_auc_score(y_test, probs):.4f}")

# Matriz de Confusão
print("\nGerando Matriz de Confusão...")
fig, ax = plt.subplots(figsize=(8, 6))
ConfusionMatrixDisplay.from_predictions(
    y_test, 
    preds, 
    display_labels=['Satisfação', 'Insatisfação'], 
    cmap='Blues', 
    ax=ax
)
plt.title("Matriz de Confusão: Classificação de Risco")
plt.savefig("matriz_confusao.png")

# Curva Precision-Recall
print("Gerando Curva Precision-Recall...")
plt.figure(figsize=(8, 6))
precision, recall, _ = precision_recall_curve(y_test, probs)
disp = PrecisionRecallDisplay(precision=precision, recall=recall)
disp.plot(color="darkorange")
plt.title("Curva Precision-Recall (Foco na Classe 1)")
plt.grid(True, linestyle='--', alpha=0.6)
plt.savefig("precision_recall_curve.png")

# Explicabilidade (SHAP)
print("Gerando análise de explicabilidade (SHAP)...")
# Amostragem aleatória para o SHAP para representar melhor o dataset
indices_aleatorios = np.random.choice(X_test.shape[0], 500, replace=False)
X_sample = X_test[indices_aleatorios].toarray()

explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_sample)

plt.figure(figsize=(10, 6))
shap.summary_plot(
    shap_values, 
    X_sample, 
    feature_names=vectorizer.get_feature_names_out(),
    plot_type="bar",
    show=False
)
plt.title("Impacto das Palavras na Decisão do Modelo (SHAP)")
plt.savefig("feature_importance_shap.png")

print("\nSalvando modelos pkl para uso no Script 03...")
joblib.dump(model, "legal_model_xgb.pkl")
joblib.dump(vectorizer, "tfidf_vectorizer.pkl")

print("Processo concluído com sucesso!")


                    
Output:
Output treinamento - Python

Melhores parâmetros encontrados: {'subsample': 0.8, 'n_estimators': 500, 'max_depth': 6, 'learning_rate': 0.2}

--- Métricas de Performance ---
              precision    recall  f1-score   support

           0       0.89      0.91      0.90      5053
           1       0.84      0.79      0.81      2853

    accuracy                           0.87      7906
   macro avg       0.86      0.85      0.86      7906
weighted avg       0.87      0.87      0.87      7906

AUC-ROC Score: 0.9255

Gerando Matriz de Confusão...
Gerando Curva Precision-Recall...
Gerando análise de explicabilidade (SHAP)...

Salvando modelos pkl para uso no Script 03...
Processo concluído com sucesso!


                

Após treino e teste, obtivemos um AU-ROC score de 0,9255, o que, como veremos adiante, entregará uma grande segurança para o Llama 3 formular corretamente uma mensagem de resposta personalizada para o cliente. Obtivemos também um F1-score de 0,9 para negativos (cliente satisfeito) e 0,81 para positivos (clientes insatisfeitos), demonstrando um alto equilíbrio entre precisão e sensibilidade. Seguem abaixo os gráficos de Matriz de Confusão, Curva Precision-Recall e um gráfico para análise de explicabilidade (SHAP), que detalham o comportamento do modelo.

Matriz de Confusão XGBoost
Fig 1. Matriz de Confusão: considerando um banco de dados real, o modelo tem um desempenho interessante, não apresentando indicios de data leakage ou overfitting.
Curva Precision-Recall
Fig 2. Curva PR: a precisão mantém-se acima de 80% mesmo com um recall elevado, garantindo que as respostas automáticas sejam disparadas com alta confiança.
Feature Importance XGBoost
Fig 3. Importância das Palavras na Decisão:"atrasada", "demorando" e "péssima" como os principais gatilhos semânticos para a detecção de insatisfação.

2.3 - Integração com IA Generativa

Depois de treinado e testado nosso modelo de ML, partimos para a integração com uma IA Generativa instalada localmente; neste projeto, utilizamos a Llama 3, que é gratuita e de fácil manipulação. Também fizemos questão de paralelizar a computação nesta fase, utilizando os 8 núcleos da CPU, pois esta é a etapa que mais consome tempo de processamento.

Para otimizar a performance, carregamos uma amostra estratégica de 5.000 mensagens, entre as quais realizamos uma busca utilizando a biblioteca FAISS para selecionar os registros mais próximos semanticamente da entrada fornecida pelo cliente.

Após inserirmos a mensagem do cliente como input, as três tarefas fundamentais executadas pelo sistema são:

  1. Busca RAG: Realizamos a busca de mensagens com semelhança semântica transformando cada frase em um vetor de 384 dimensões (via modelo paraphrase-multilingual-MiniLM-L12-v2). A biblioteca FAISS calcula a distância geométrica (L2) entre o vetor da "nova reclamação" e os demais da amostra, extraindo as duas mensagens mais semelhantes.
  2. Classificação XGBoost: A mensagem de input é processada pelo modelo treinado para classificar o sentimento entre satisfação ou insatisfação, calculando simultaneamente a probabilidade percentual (intensidade) da predição.
  3. Integração: O contexto recuperado, a classificação e a intensidade do sentimento são integrados ao prompt estruturado entregue para a Llama 3. Isso permite que a IA elabore uma resposta baseada não apenas em seu conhecimento prévio, mas no contexto real e situacional fornecido pelos dados.

Com estes três passos, aumentamos significativamente a qualidade das respostas geradas e mitigamos o risco de alucinações por parte da IA.

Código Integração de Modelos - Python


import joblib
import pandas as pd
import numpy as np
import faiss
import os
from sentence_transformers import SentenceTransformer
from langchain_ollama import OllamaLLM

os.environ["OMP_NUM_THREADS"] = "8"

# Carregando de Recursos
print("Carregando Modelos e Banco de Dados...")
df = pd.read_csv("dataset_processed.csv")
model_xgb = joblib.load("legal_model_xgb.pkl")
tfidf = joblib.load("tfidf_vectorizer.pkl")

print("Carregando modelo de linguagem (SentenceTransformer)")
embedder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

# Criação do Index de Busca (FAISS)
print("Vetorizando amostra para RAG")
df_sample = df.sample(5000, random_state=42).reset_index(drop=True)
embeddings = embedder.encode(df_sample['text_clean'].tolist(), show_progress_bar=True)

d = embeddings.shape[1]
index = faiss.IndexFlatL2(d)
index.add(np.array(embeddings).astype('float32'))

def consultar_ia(nova_reclamacao):
    # Predição do Modelo (XGBoost) - A "Intensidade"
    text_vector = tfidf.transform([nova_reclamacao])
    prob_negativa = model_xgb.predict_proba(text_vector)[0][1]
    
    # Busca RAG (Similaridade Semântica)
    query_embedding = embedder.encode([nova_reclamacao])
    D, I = index.search(np.array(query_embedding).astype('float32'), k=2)
    contexto = df_sample.iloc[I[0]]['review_comment_message'].values
    
    # Lógica de Intensidade para o Prompt
    intensidade = "CRÍTICA" if prob_negativa > 0.70 else "MODERADA"
    
    # Llama3 recebe o score do XGBoost para ajustar o tom da resposta automática
    prompt = f"""
    
    BASEANDO-SE NAS SEGUINTES INFORMAÇÕES:
    DADOS DO MODELO ESTATISTICO (XGBoost):
    - Alerta de Insatisfacao do Cliente: {intensidade}
    - Nivel de Intensidade: {prob_negativa*100:.2f}%
    
    RECLAMACAO DO CLIENTE: "{nova_reclamacao}"
    
    CASOS SIMILARES JÁ OCORRIDOS ANTERIORMENTE:
    1. {contexto[0]}
    2. {contexto[1]}
    
    EXECUTE AS SEGUINTES TAREFAS:
    1. Classifique o problema em: Entrega, Integridade, Financeiro, Atendimento, Sistema.
    2. Redija uma resposta para o cliente ponderada pelo "Alerta de Insatisfacao do Cliente",
     "Nivel de Intensidade" e "CASOS SIMILARES 1 e 2 JÁ OCORRIDOS ANTERIORMENTE".
    """
    
    print("\n" + "="*50)
    print("--- ANÁLISE HÍBRIDA (XGBOOST + RAG + LLM) ---")
    print("="*50)
    print(f"Probabilidade XGBoost: {prob_negativa*100:.2f}% ({intensidade})")
    
    print("\nSolicitando geração ao Llama 3...")
    
    try:
        llm = OllamaLLM(model="llama3", temperature=0.1)
        resposta = llm.invoke(prompt)
        
        print("\n--- RESPOSTA DA IA ---")
        print(resposta)
        
    except Exception as e:
        print(f"\nErro ao conectar com Ollama: {e}")

if __name__ == "__main__":
    # Teste com uma frase que deve disparar o alerta do seu modelo
    reclamacao_teste = "Meu produto não foi entregue até hoje. Já paguei mas não chegou."
    consultar_ia(reclamacao_teste)

                    
Output:
Output Integração de Modelos - Python

                    
Carregando Modelos e Banco de Dados...
Carregando modelo de linguagem (SentenceTransformer)
Loading weights: 100%|████████████████████████████████████████████████████████████████████████████████████████| 199/199 [00:00<00:00, 780.43it/s, Materializing param=pooler.dense.weight]
BertModel LOAD REPORT from: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
Key                     | Status     |  | 
------------------------+------------+--+-
embeddings.position_ids | UNEXPECTED |  | 

Notes:
- UNEXPECTED	:can be ignored when loading from different task/architecture; not ok if you expect identical arch.
Vetorizando amostra para RAG
Batches: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 157/157 [00:45<00:00,  3.44it/s]

==================================================
--- ANÁLISE HÍBRIDA (XGBOOST + RAG + LLM) ---
==================================================
Probabilidade XGBoost: 96.25% (CRÍTICA)

Solicitando geração ao Llama 3...

--- RESPOSTA DA IA ---
**Tarefa 1: Classificação do problema**

O problema é classificado como **Entrega**, pois o cliente está reclamando da falta de entrega do produto apesar de ter pago.

**Tarefa 2: Redação da resposta para o cliente**

Considerando o "Alerta de Insatisfacao do Cliente" (CRÍTICA) e o "Nivel de Intensidade" (96.25%), é necessário uma resposta imediata e eficaz para resolver a situação.

Resposta:

"Sinto muito pelo incômodo causado. Estamos trabalhando arduamente para resolver este problema. Como você já pagou, vamos proceder à entrega do seu produto o mais rápido possível. Além disso, gostaria de saber se houve algum erro na entrega ou se há alguma informação que possa ajudar a identificar o problema. Se necessário, vamos também discutir opções de reembolso ou substituição do produto. Por favor, contate-nos novamente para obter atualizações sobre o status da sua entrega."

Essa resposta é ponderada pelas seguintes considerações:

* A situação é crítica e requer uma resposta imediata e eficaz.
* O cliente está insatisfeito e precisa de uma solução rápida e eficaz.
* As experiências similares do passado (CASOS SIMILARES 1 e 2) devem ser consideradas para evitar que o problema se repita.

Espero que essa resposta atenda às suas expectativas!


                    

                	        

A implementação do modelo XGBoost, aliada ao processamento paralelo de 8 núcleos, alcançou um AUC-ROC de 0,92 e F1-Score de 0,90 para clientes satisfeitos. A análise de explicabilidade via SHAP confirmou a precisão do pipeline de NLP ao identificar gatilhos semânticos críticos como 'atrasada', 'péssima' e 'demorando' como os principais termos de insatisfação. Essa acurácia estatística serviu como uma 'trava de segurança' essencial, garantindo que a triagem automatizada operasse com total confiança antes de encaminhar os dados para a camada generativa.

A integração da arquitetura RAG (Retrieval-Augmented Generation) com o modelo Llama 3 local permitiu a geração de respostas personalizadas e contextuais, mitigando o risco de alucinações da IA. Ao utilizar o banco vetorial FAISS para recuperar casos históricos semelhantes, o sistema foi capaz de modular o tom da comunicação para situações de alta probabilidade de crise (intensidade crítica > 70%). O resultado final é um ecossistema de atendimento preventivo que não apenas automatiza a triagem, mas entrega uma resolução estratégica que reduz o tempo de resposta e previne potenciais escaladas de crise.

Esse projeto apresenta inúmeros potenciais a serem explorados, como o acionamento automático de determinados setores do time de operação através da triagem e classificação da mensagem do cliente e a utilização de outras informações (histórico do cliente/vendedor, dados sobre rastreamento da enterga, etc) na construção do contexto para formulação de uma resposta ainda mais interessante.

Downloads e Links

Repositório GitHub
  • BROWNLEE, Jason. XGBoost With Python: Gradient Boosted Trees With XGBoost and scikit-learn. Edition v1.15. Machine Learning Mastery, 2020.
  • Matthew Wiens, Alissa Verone-Boyle, Nick Henscheid, Jagdeep T. Podichetty, Jackson Burton. A Tutorial and Use Case Example of the eXtreme Gradient Boosting (XGBoost) Artificial Intelligence Algorithm for Drug Development Applications. Disponível em: ascpt.onlinelibrary.wiley.com . (Acesso em: 05 mar. 2026)
  • Olist and 3 collaborators Brazilian E-Commerce Public Dataset by Olist. Disponível em: https://www.kaggle.com/datasets/olistbr/brazilian-ecommerce?select=olist_order_reviews_dataset.csv . (Acesso em: 05 mar. 2026)