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!

2
respostas

Desafio

import subprocess
import time
import os

print("Instalando Ollama...")
subprocess.run("curl -fsSL https://ollama.com/install.sh | sh", shell=True, check=True)
print("Iniciando servidor Ollama...")
subprocess.Popen("nohup ollama serve > ollama.log 2>&1 &", shell=True)
for _ in range(20):
try:
r = subprocess.run("curl -s http://localhost:11434/api/tags",
shell=True, capture_output=True, text=True, timeout=2)
if r.returncode == 0:
print("Servidor Ollama pronto!\n")
break
except:
pass
time.sleep(1)
print("Baixando modelo llama3.2:1b...")
subprocess.run("ollama pull llama3.2:1b", shell=True, check=True)
print("Modelo pronto!\n")
import requests
import json
import re
import ollama
import threading
from concurrent.futures import ThreadPoolExecutor
from typing import Optional
from tqdm.notebook import tqdm
url = "https://cdn3.gnarususercontent.com.br/4790-python/Resenhas_App_ChatGPT.txt"
response = requests.get(url, timeout=30)
response.encoding = "utf-8"
resenhas = [linha.strip() for linha in response.text.split("\n") if linha.strip()]
print(f"{len(resenhas)} resenhas carregadas\n")
def extrair_json(texto: str) -> Optional[dict]:
# 1) JSON puro
try:
return json.loads(texto)
except json.JSONDecodeError:
pass

# 2) ```json ... ```
match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", texto, re.DOTALL)
if match:
    try:
        return json.loads(match.group(1))
    except json.JSONDecodeError:
        pass

# 3) Bloco {...} genérico
match = re.search(r"\{.*\}", texto, re.DOTALL)
if match:
    try:
        return json.loads(match.group(0))
    except json.JSONDecodeError as e:
        print(f"JSON inválido: {e}\n   Trecho: {texto[:200]}...")

return None

CAMPOS_OBRIGATORIOS = {"resenha_original", "sentimento"}
SENTIMENTOS_VALIDOS = {"positivo", "negativo", "neutro"}

def validar_json(dados: dict) -> bool:
if not isinstance(dados, dict):
return False
if not CAMPOS_OBRIGATORIOS.issubset(dados.keys()):
return False
if str(dados.get("sentimento", "")).lower() not in SENTIMENTOS_VALIDOS:
return False
if not isinstance(dados.get("resenha_original"), str):
return False
return True

def analisar_resenha(texto: str, tentativas: int = 3) -> Optional[dict]:
prompt = f"""Você é um sistema que retorna APENAS JSON válido.
NÃO escreva nada fora do JSON. Sem markdown, sem explicações.

Formato obrigatório:
{{
"usuario": "<apelido fictício coerente com a resenha>",
"resenha_original": "<a resenha fornecida, sem alterações>",
"sentimento": "positivo | negativo | neutro"
}}

Resenha:
"""{texto}"""
"""

for i in range(tentativas):
    try:
        resposta = ollama.chat(
            model="llama3.2:1b",
            messages=[{"role": "user", "content": prompt}],
            options={"temperature": 0.1},
        )
        conteudo = resposta["message"]["content"]
        dados = extrair_json(conteudo)

        if dados and validar_json(dados):
            # normaliza sentimento
            dados["sentimento"] = dados["sentimento"].lower()
            return dados

    except Exception as e:
        print(f"Tentativa {i+1} falhou: {e}")

return None

ARQUIVO_SAIDA = "resultados.json"
ARQUIVO_CHECKPOINT = "checkpoint.json"
TRAVA = threading.Lock()
if os.path.exists(ARQUIVO_CHECKPOINT):
with open(ARQUIVO_CHECKPOINT, "r", encoding="utf-8") as f:
resultados = json.load(f)
ja_feitos = {r["__indice"] for r in resultados if "__indice" in r}
print(f"♻ Retomando: {len(ja_feitos)} já processados\n")
else:
resultados = []
ja_feitos = set()

def processar(item):
i, texto = item
if i in ja_feitos:
return i, None

dados = analisar_resenha(texto)
if dados:
    dados["__indice"] = i
return i, dados

def salvar(dados):
with TRAVA:
resultados.append(dados)
with open(ARQUIVO_CHECKPOINT, "w", encoding="utf-8") as f:
json.dump(resultados, f, ensure_ascii=False, indent=2)

2 respostas

print(" Analisando resenhas com Ollama...\n")
with ThreadPoolExecutor(max_workers=3) as executor:
for i, dados in tqdm(
executor.map(processar, enumerate(resenhas)),
total=len(resenhas),
desc="Progresso"
):
if dados:
salvar(dados)
elif i not in ja_feitos:
print(f" Falha na linha {i+1}")
resultado_final = [
{k: v for k, v in r.items() if k != "__indice"}
for r in resultados
]

with open(ARQUIVO_SAIDA, "w", encoding="utf-8") as f:
json.dump(resultado_final, f, ensure_ascii=False, indent=2)
total = len(resultado_final)
positivos = sum(1 for r in resultado_final if r["sentimento"] == "positivo")
negativos = sum(1 for r in resultado_final if r["sentimento"] == "negativo")
neutros = total - positivos - negativos

print(f"\n{'='*45}")
print(f"{total} resenhas processadas com sucesso!")
print(f" Positivas: {positivos}")
print(f" Negativas: {negativos}")
print(f" Neutras: {neutros}")
print(f"{'='*45}")

Olá, Marcelo! Como vai?

Parabéns pela realização das atividades!

Você conseguiu apresentar um fluxo completo de análise de resenhas, demonstrou domínio na integração com o Ollama e ainda aplicou paralelismo com ThreadPoolExecutor para otimizar o processamento. Além disso, mostrou preocupação com validação de JSON e checkpoints, o que evidencia cuidado com consistência e robustez. Isso reforça a importância de unir boas práticas de programação com inteligência artificial para gerar relatórios confiáveis e escaláveis.

Se quiser aprofundar ainda mais, algumas boas práticas são:

  • Monitorar logs: registrar falhas e sucessos para facilitar depuração.
  • Normalizar dados: padronizar campos antes de salvar para evitar inconsistências.
  • Automatizar testes: validar cada etapa do pipeline com casos simulados.

Ah uma pergunta: O que você considera mais desafiador neste projeto, garantir a integridade dos dados processados ou otimizar a performance para lidar com grandes volumes de resenhas?

Fico à disposição! E se precisar, conte sempre com o apoio do fórum.

Abraço e bons estudos!

AluraConte com o apoio da comunidade Alura na sua jornada. Abraços e bons estudos!