Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Guia3/src/alternativa.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from typing import List, Tuple, Dict

class Alternativa:
pass
def __init__(self, texto: str, correta: bool, explicacao: str = None):
self.texto = texto
self.correta = correta
self.explicacao = explicacao

17 changes: 17 additions & 0 deletions Guia3/src/correcao.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Dict
from src.perguntadiscursiva import PerguntaDiscursiva
from src.llmservice import LLMService

class Correcao:
@staticmethod
def corrigir_discursiva(pergunta: PerguntaDiscursiva, resposta_aluno: str, service: LLMService = None) -> Dict:
# Se nenhum serviço for passado, instancia o padrão automaticamente
if service is None:
service = LLMService()

return service.corrigir_resposta(pergunta, resposta_aluno)

@staticmethod
def criar_prompt_correcao(pergunta: PerguntaDiscursiva, resposta_aluno: str) -> str:
# Método utilitário para isolar a montagem do prompt se necessário
return f"Pergunta: {pergunta.texto} | Esperado: {pergunta.resposta_esperada} | Aluno: {resposta_aluno}"
82 changes: 82 additions & 0 deletions Guia3/src/llmservice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
import json
from typing import Dict
from groq import Groq
from src.perguntadiscursiva import PerguntaDiscursiva

class LLMService:
def __init__(self, api_key: str = None, model: str = "llama-3.3-70b-versatile"):
# Se não passar api_key por parâmetro, busca da variável de ambiente
self.api_key = api_key or os.getenv("GROQ_API_KEY")
self.model = model
# Definindo uma base_url padrão de acordo com o diagrama UML
self.base_url = "https://api.groq.com"

if self.api_key:
self.client = Groq(api_key=self.api_key)
else:
self.client = None

def corrigir_resposta(self, pergunta: PerguntaDiscursiva, resposta_aluno: str) -> Dict:
# Prompt robusto para garantir o formato JSON esperado pelo diagrama UML
prompt = f"""
Você é um professor avaliador rigoroso.
Analise a resposta do aluno com base na resposta esperada para a pergunta dada.

Pergunta: {pergunta.texto}
Resposta Esperada: {pergunta.resposta_esperada}
Resposta do Aluno: {resposta_aluno}

Responda estritamente em formato JSON com a seguinte estrutura:
{{
"correta": true ou false,
"pontuacao": valor float de 0.0 a 1.0,
"feedback": "Uma justificativa curta sobre a nota do aluno",
"explicacao": "A explicação conceitual correta"
}}
"""

try:
resposta_texto = self._fazer_chamada_api(prompt)
# Converte a string JSON purgada pela IA em um dicionário Python
dados_correcao = json.loads(resposta_texto)
return dados_correcao

except Exception as e:
# Tratamento interno em caso de falhas
return self._tratar_erro(e, pergunta, resposta_aluno)

def _fazer_chamada_api(self, prompt: str) -> str:
if not self.client:
raise ValueError("API Key do Groq não configurada.")

chat_completion = self.client.chat.completions.create(
model=self.model,
temperature=0.1, # Baixa para avaliação precisa e sem invenções
response_format={"type": "json_object"}, # Força o Groq a devolver JSON válido
messages=[
{
"role": "system",
"content": "Você é um assistente acadêmico que responde exclusivamente em formato JSON estruturado."
},
{
"role": "user",
"content": prompt
}
]
)
return chat_completion.choices[0].message.content

def _tratar_erro(self, e: Exception, pergunta: PerguntaDiscursiva, resposta_aluno: str) -> Dict:
print(f"[LLMService Error] Falha na comunicação ou processamento: {e}")

# Fallback estático usando a lógica tradicional baseada em correspondência de texto
resposta_esperada = pergunta.resposta_esperada or ""
eh_correto = str(resposta_aluno).strip().lower() == str(resposta_esperada).strip().lower()

return {
"correta": eh_correto,
"pontuacao": 1.0 if eh_correto else 0.0,
"feedback": f"Correção em modo de segurança (Fallback). Erro na API: {str(e)}",
"explicacao": pergunta.get_explicacao() or "Sem explicação disponível no momento."
}
18 changes: 16 additions & 2 deletions Guia3/src/pergunta.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
from typing import List, Tuple, Dict
from abc import ABC, abstractmethod

class Pergunta:
pass
class Pergunta(ABC):
def __init__(self, texto: str, explicacao_geral: str = None):
self.texto = texto
self.explicacao_geral = explicacao_geral

@abstractmethod
def validar_resposta(self, resposta) -> bool:
pass

def get_explicacao(self) -> str:
return self.explicacao_geral

@abstractmethod
def get_tipo(self) -> str:
pass
15 changes: 13 additions & 2 deletions Guia3/src/perguntadiscursiva.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
from typing import List, Tuple, Dict
from src.pergunta import Pergunta

class PerguntaDiscursiva:
pass
class PerguntaDiscursiva(Pergunta):
def __init__(self, texto: str, resposta_esperada: str = None, explicacao_geral: str = None):
super().__init__(texto, explicacao_geral)
self.resposta_esperada = resposta_esperada

def validar_resposta(self, texto: str) -> bool:
if self.resposta_esperada is None:
return True
return str(texto).strip().lower() == str(self.resposta_esperada).strip().lower()

def get_tipo(self) -> str:
return "discursiva"
24 changes: 22 additions & 2 deletions Guia3/src/perguntamultiplaescolha.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
from typing import List, Tuple, Dict
from src.pergunta import Pergunta
from src.alternativa import Alternativa

class PerguntaMultiplaEscolha:
pass
class PerguntaMultiplaEscolha(Pergunta):
def __init__(self, texto: str, alternativas: list[Alternativa], explicacao_geral: str = None):
super().__init__(texto, explicacao_geral)
self.alternativas = alternativas

def validar_resposta(self, indice: int) -> bool:
if 0 <= indice < len(self.alternativas):
return self.alternativas[indice].correta
return False

def get_alternativa_correta(self) -> Alternativa:
for alt in self.alternativas:
if alt.correta:
return alt
return None

def get_tipo(self) -> str:
return "multipla_escolha"


12 changes: 11 additions & 1 deletion Guia3/src/questionario.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
from typing import List, Tuple, Dict
from src.pergunta import Pergunta

class Questionario:
pass
def __init__(self, titulo: str):
self.titulo = titulo
self.perguntas = []

def adicionar_pergunta(self, p: Pergunta):
self.perguntas.append(p)

def criar_attempt(self, usuario: str):
from src.tentativaquestionario import TentativaQuestionario
return TentativaQuestionario(self, usuario)
13 changes: 11 additions & 2 deletions Guia3/src/resposta.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
from typing import List, Tuple, Dict
from abc import ABC, abstractmethod
from src.pergunta import Pergunta

class Resposta:
pass
class Resposta(ABC):
def __init__(self, pergunta: Pergunta):
self.pergunta = pergunta
self.esta_correta = False
self.pontuacao_obtida = 0.0

@abstractmethod
def calcular_pontuacao(self) -> float:
pass
14 changes: 12 additions & 2 deletions Guia3/src/respostadiscursiva.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
from typing import List, Tuple, Dict
from src.resposta import Resposta
from src.perguntadiscursiva import PerguntaDiscursiva

class RespostaDiscursiva:
pass
class RespostaDiscursiva(Resposta):
def __init__(self, pergunta: PerguntaDiscursiva, texto_resposta: str):
super().__init__(pergunta)
self.texto_resposta = texto_resposta
self.calcular_pontuacao()

def calcular_pontuacao(self) -> float:
self.esta_correta = self.pergunta.validar_resposta(self.texto_resposta)
self.pontuacao_obtida = 1.0 if self.esta_correta else 0.0
return self.pontuacao_obtida
20 changes: 18 additions & 2 deletions Guia3/src/respostaobjetiva.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
from typing import List, Tuple, Dict
from src.resposta import Resposta
from src.perguntamultiplaescolha import PerguntaMultiplaEscolha

class RespostaObjetiva:
pass
class RespostaObjetiva(Resposta):
def __init__(self, pergunta: PerguntaMultiplaEscolha, indice_escolhido: int):
super().__init__(pergunta)
self.indice_escolhido = indice_escolhido

if 0 <= indice_escolhido < len(pergunta.alternativas):
self.alternativa_selecionada = pergunta.alternativas[indice_escolhido]
else:
self.alternativa_selecionada = None

self.calcular_pontuacao()

def calcular_pontuacao(self) -> float:
self.esta_correta = self.pergunta.validar_resposta(self.indice_escolhido)
self.pontuacao_obtida = 1.0 if self.esta_correta else 0.0
return self.pontuacao_obtida
40 changes: 39 additions & 1 deletion Guia3/src/tentativaquestionario.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
from typing import List, Tuple, Dict
from datetime import datetime
from src.questionario import Questionario
from src.respostaobjetiva import RespostaObjetiva
from src.respostadiscursiva import RespostaDiscursiva

class TentativaQuestionario:
pass
def __init__(self, questionario: Questionario, usuario: str):
self.questionario = questionario
self.usuario = usuario
self.data_inicio = datetime.now()
self.data_fim = None
self.respostas = []

def registrar_resposta(self, indice_pergunta: int, valor):
if self.is_finalizado():
return

if 0 <= indice_pergunta < len(self.questionario.perguntas):
pergunta = self.questionario.perguntas[indice_pergunta]

if pergunta.get_tipo() == "multipla_escolha":
resposta = RespostaObjetiva(pergunta, valor)
else:
resposta = RespostaDiscursiva(pergunta, valor)

resposta.calcular_pontuacao()
self.respostas.append(resposta)

def is_finalizado(self) -> bool:
return self.data_fim is not None

def calcular_pontuacao(self) -> float:
return sum(r.pontuacao_obtida for r in self.respostas)

def finalizar(self) -> tuple[float, str]:
if not self.is_finalizado():
self.data_fim = datetime.now()

pontuacao_total = self.calcular_pontuacao()
mensagem = f"Questionário finalizado. Pontuação: {pontuacao_total}"
return (pontuacao_total, mensagem)
6 changes: 5 additions & 1 deletion Guia4/src/alternativa.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from typing import List, Tuple, Dict

class Alternativa:
pass
def __init__(self, texto: str, correta: bool, explicacao: str = None):
self.texto = texto
self.correta = correta
self.explicacao = explicacao

18 changes: 16 additions & 2 deletions Guia4/src/correcao.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
from typing import List, Tuple, Dict
from typing import Dict
from src.perguntadiscursiva import PerguntaDiscursiva
from src.llmservice import LLMService

class Correcao:
pass
@staticmethod
def corrigir_discursiva(pergunta: PerguntaDiscursiva, resposta_aluno: str, service: LLMService = None) -> Dict:
if service is None:
service = LLMService()

return service.corrigir_resposta(pergunta, resposta_aluno)

@staticmethod
def criar_prompt_correcao(pergunta: PerguntaDiscursiva, resposta_aluno: str) -> str:
return f"Pergunta: {pergunta.texto} | Esperado: {pergunta.resposta_esperada} | Aluno: {resposta_aluno}"


CorrecaoUtil = Correcao
Loading