Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

4
respostas

[Projeto] Faça como eu fiz: lidar com exceções e IA

# Identifique diferentes erros: value, type, name e divisão por zero;
"""
RESPOSTA:
Erros de value: Ocorre quando uma operação recebe um argumento de um tipo correto, mas com um valor inapropriado. Ex.: Tentar converter uma string 'abc' para um número inteiro.
Erros de type: Quando o usuário insere um tipo de caractere que não faz parte do conjunto de caracteres delimitados no código. Ex.: Letra, em vez de apenas números.
Erros de name: Ocorre quando uma variável ou função é usada antes de ser definida. Ex.: Usar uma variável que não foi declarada.
Divisão por zero: Quando o denominador é igual a zero.
"""
# Crie um bloco try para isolar operações arriscadas;

try:
  a = int(input("Insira o valor do numerador (número inteiro): "))
  # A variável 'i' não está definida, o que causaria um NameError.
  # Para demonstração, vamos definir 'i' ou corrigir a linha.
  # Por simplicidade e para focar na exceção, vou usar um valor fixo para o denominador.
  denominador = 0 # Para forçar ZeroDivisionError
  print(a/denominador)
except ZeroDivisionError:
  print("Não é possível dividir por zero!")
# Configure except para capturar value error e alertar o usuário;
except ValueError:
  print("Digite um valor numérico válido! Ex.: 1, 2, 3, não números quebrados")
# Configure except para capturar type error com mensagem de apoio;
except TypeError:
  print("Digite um valor numérico!")
# Utilize except geral para erros não previstos;
except Exception as e: # Usar Exception para capturar outros erros, incluindo NameError
  print(f"Erro inesperado: {e}!")
# Aplique finally para executar ações de finalização (ex.: fechar arquivos);
finally:
  print("Execução concluída!")
  # exit() # Desativado para permitir a execução do restante do notebook
# Replique exemplos de tratamento de exceções conforme demonstrado;
# Filtre DataFrame por análises negativas usando pandas;
import pandas as pd

# Extraia a coluna de resenhas negativas do DataFrame;
# Una as resenhas com o método join e delimitador definido;
# Armazene o texto unificado em variável para processamento;
negativos = df[df["Sentimentos"] == 'Negativo']['reviewText'].astype(str).str.cat(sep='###')
# Prepare um prompt para enviar as resenhas ao LLM;
prompt = f"Analise as seguintes avaliações negativas de clientes, separadas por '###'. Identifique o principal problema, a reclamação mais agrupada e a sugestão de melhoria mais relevante. Responda com uma única palavra para cada categoria. \n\nResenhas: {negativos}\n\nFormato de saída: Problema: [uma palavra]\nReclamação: [uma palavra]\nMelhoria: [uma palavra]"
# Configure o cliente de LLM com parâmetros específicos;
import os
from google.colab import userdata
from groq import Groq
from google.api_core.exceptions import TooManyRequests
import time
import re # Importar o módulo re para expressões regulares

os.environ['GROQ_API_KEY'] = userdata.get('GROQ_API_KEY')

client = Groq()

# Realize chamadas à IA e capture as respostas;
# Realizando a chamada única à IA com o prompt que contém todas as resenhas negativas
print("\nRealizando análise consolidada das avaliações negativas pela IA...")
max_retries = 5
initial_delay = 2 # seconds
llm_response_consolidated = "" # Para armazenar a única resposta consolidada

continua nos comentário...

4 respostas

Continuando...

for attempt in range(max_retries):
    try:
        completion_consolidated = client.chat.completions.create(
            model="openai/gpt-oss-20b",
            messages=[
                {
                    "role": "user",
                    "content": prompt
                }
            ],
            temperature=1.0,
            max_completion_tokens=700,
            top_p=1,
            reasoning_effort="medium",
            stream=False,
            stop=None
        )
        llm_content = completion_consolidated.choices[0].message.content.strip()
        # Tenta encontrar o JSON puro na resposta, caso o LLM adicione texto extra
        json_match = re.search(r'Problema: (\w+)\s*\nReclamação: (\w+)\s*\nMelhoria: (\w+)', llm_content, re.DOTALL)
        if json_match:
            llm_response_consolidated = f"Problema: {json_match.group(1)}\nReclamação: {json_match.group(2)}\nMelhoria: {json_match.group(3)}"
        else:
            llm_response_consolidated = llm_content # Se não encontrar o padrão, usa o conteúdo original

        print("Análise da IA recebida com sucesso:\n")
        print(llm_response_consolidated)
        break # Sai do loop se for bem-sucedido
    except TooManyRequests as e:
        wait_time = initial_delay * (2 ** attempt)
        print(f"\nERRO: Limite de requisições excedido. Tentando novamente em {wait_time} segundos (Tentativa {attempt + 1}/{max_retries}).\nDetalhes do erro: {e}\n")
        time.sleep(wait_time)
    except Exception as e:
        print(f"\nERRO inesperado ao chamar a IA: {e}\n")
        llm_response_consolidated = "Erro ao gerar análise consolidada."
        break # Sai do loop em caso de erro inesperado
else:
    print(f"\nNão foi possível obter a análise consolidada após {max_retries} tentativas devido a limites de requisição.")
    llm_response_consolidated = "Análise não disponível devido a limites de requisição."
# Extraia categorias das respostas conforme solicitado;
# Exemplo de resposta para fins de demonstração se a chamada LLM falhar ou ainda não tiver sido executada
# Remova esta linha se você tiver certeza de que llm_response_consolidated sempre será preenchido pela LLM
if not llm_response_consolidated or "Erro" in llm_response_consolidated:
    llm_response_consolidated = "Problema: Demora\nReclamação: Atrasos\nMelhoria: Otimizar"

# Usando expressões regulares para extrair as categorias
problema_match = re.search(r"Problema: (\w+)", llm_response_consolidated)
reclamacao_match = re.search(r"Reclamação: (\w+)", llm_response_consolidated)
melhoria_match = re.search(r"Melhoria: (\w+)", llm_response_consolidated)

principal_problema = problema_match.group(1) if problema_match else "N/A"
principal_reclamacao = reclamacao_match.group(1) if reclamacao_match else "N/A"
principal_melhoria = melhoria_match.group(1) if melhoria_match else "N/A"

print("\n--- Categorias Extraídas (Uma Palavra) ---")
print(f"Principal Problema: {principal_problema}")
print(f"Principal Reclamação: {principal_reclamacao}")
print(f"Principal Melhoria: {principal_melhoria}")

# Converta a resposta em lista utilizando o método split;
print("\n--- Demonstrando split() para a resposta consolidada ---")
response_lines = llm_response_consolidated.split('\n')
response_list_of_tuples = []
for line in response_lines:
    if ': ' in line:
        category, value = line.split(': ', 1)
        response_list_of_tuples.append((category, value))
print(f"Resposta dividida em linhas e pares (categoria, valor): {response_list_of_tuples}")

# Implemente exemplo de extração de JSON para cada resenha;

# Função para extrair JSON de cada resenha
import json

def extrair_json_resenha(feedback):
    max_retries = 3
    initial_delay = 2 # seconds

    for attempt in range(max_retries):
        try:
            prompt_json = f"Analise a seguinte resenha de cliente e retorne um JSON com a classificação do sentimento ('Positivo', 'Negativo', 'Neutro') e, se aplicável, o 'problema_principal', 'aspecto_principal' e 'sugestao' em uma palavra. Se não for aplicável, use 'N/A'.\n\nResenha: {feedback}\n\nFormato JSON esperado: {{'sentimento': '...', 'problema_principal': '...', 'aspecto_principal': '...', 'sugestao': '...'}}"

            completion = client.chat.completions.create(
                model="openai/gpt-oss-20b",
                messages=[
                    {
                        "role": "user",
                        "content": prompt_json
                    }
                ],
                temperature=0.7,
                max_completion_tokens=150,
                top_p=1,
                reasoning_effort="low",
                stream=False,
                stop=None
            )
            
            llm_content = completion.choices[0].message.content.strip()
            # Tenta encontrar o JSON puro na resposta, caso o LLM adicione texto extra
            json_match = re.search(r'\{.*\}', llm_content, re.DOTALL)
            if json_match:
                json_string = json_match.group(0)
                return json.loads(json_string)
            else:
                print(f"[Debug] Conteúdo não JSON esperado: {llm_content}")
                return {'sentimento': 'Erro', 'problema_principal': 'N/A', 'aspecto_principal': 'N/A', 'sugestao': 'N/A'}

        except TooManyRequests as e:
            wait_time = initial_delay * (2 ** attempt)
            print(f"\nERRO: Limite de requisições excedido. Tentando novamente em {wait_time} segundos. (Tentativa {attempt + 1}/{max_retries}).\nDetalhes do erro: {e}\n")
            time.sleep(wait_time)
        except json.JSONDecodeError as e:
            print(f"\nERRO de decodificação JSON para a resenha '{feedback}'. Conteúdo recebido: '{llm_content}'. Detalhes: {e}\n")
            return {'sentimento': 'Erro', 'problema_principal': 'N/A', 'aspecto_principal': 'N/A', 'sugestao': 'N/A'}
        except Exception as e:
            print(f"\nERRO inesperado ao processar a resenha '{feedback}'. Detalhes: {e}\n")
            return {'sentimento': 'Erro', 'problema_principal': 'N/A', 'aspecto_principal': 'N/A', 'sugestao': 'N/A'}
    return {'sentimento': 'Falha', 'problema_principal': 'N/A', 'aspecto_principal': 'N/A', 'sugestao': 'N/A'}

# Aplicar a função a uma amostra de resenhas para demonstrar
print("\nIniciando extração de JSON para cada resenha...")

# Filtrando para pegar apenas as resenhas que foram classificadas como 'Negativo' anteriormente
negative_reviews_df = df[df["Sentimentos"] == 'Negativo']

# Vamos aplicar a função apenas às 5 primeiras resenhas negativas para demonstração
json_extraido_list = []

# Verifica se existem resenhas negativas para processar
if not negative_reviews_df.empty:
    for index, row in negative_reviews_df.head(5).iterrows(): # Iterar sobre as 5 primeiras resenhas negativas
        review_text = row['reviewText']
        print(f"\nProcessando resenha {index}: {review_text[:70]}...")
        json_data = extrair_json_resenha(review_text)
        json_extraido_list.append(json_data)
else:
    print("Nenhuma resenha negativa encontrada no DataFrame para extração de JSON.")

# Exibir os resultados
print("\n--- JSON Extraído por Resenha (Amostra) ---")
for i, data in enumerate(json_extraido_list):
    print(f"Resenha {i+1}: {data}")


#DA ATIVIDADE ANTERIOR
# Demonstre execução de modelos de IA localmente (LM Studio/Ollama);
# Selecione e baixe um modelo adequado (ex.: Gema 3 1B);
# Configure ambiente virtual Python e IDE de desenvolvimento;
# Crie script integrando chamadas LLM com código Python;
# Desenvolva função para processar em lote resenhas e contagem de avaliações;
# Implemente função que una os resultados num único texto.

Olá, Paulo, tudo bem?

Parabéns por concluir mais essa etapa e compartilhar seu projeto com a comunidade Alura. Seu código ficou muito bem construído e mostra bastante cuidado na aplicação dos conceitos da aula.

Gostei bastante da forma como você tratou os erros com try, except e finally, separando exceções como ZeroDivisionError, ValueError e TypeError. Também foi muito boa a sua implementação das tentativas automáticas com atraso progressivo para lidar com limites da API, algo bem próximo do que acontece em projetos reais com LLMs.

Outro ponto positivo foi o uso de expressões regulares para organizar e extrair melhor as respostas da IA. Isso mostra atenção aos detalhes e preocupação em deixar o código mais seguro e preparado para diferentes retornos.

Você mandou muito bem. Continue explorando essas melhorias, porque esse cuidado faz bastante diferença no desenvolvimento de soluções com Inteligência Artificial.

Bons estudos!

Sucesso

Imagem da comunidade