DeepReinfo / evaluator.py
Danielfonseca1212's picture
Create evaluator.py
0b86fa5 verified
# evaluator.py — LLM-as-Judge: Relevance Grader + Self-Critique
"""
Implementa dois avaliadores LLM clássicos de produção:
1. Relevance Grader — chunks recuperados são relevantes para a pergunta?
2. Self-Critique — a resposta gerada é boa? (score 0-10 + feedback)
"""
import re
import json
from openai import OpenAI
# ── RELEVANCE GRADER ──────────────────────────────────────────
GRADER_PROMPT = """Você é um avaliador especialista em sistemas RAG.
Avalie se o contexto recuperado é RELEVANTE para responder a pergunta.
Pergunta: {question}
Contexto recuperado:
{context}
Responda APENAS com JSON válido, sem texto adicional:
{{"relevant": true/false, "reason": "justificativa em 1 frase", "confidence": 0.0-1.0}}
Critério: relevant=true se o contexto contém informação útil para responder."""
def grade_relevance(client: OpenAI, question: str, chunks: list, model: str = "gpt-4o-mini") -> dict:
"""
Avalia se os chunks recuperados são relevantes para a pergunta.
Retorna: {relevant, reason, confidence, raw_chunks_used}
"""
context = "\n\n---\n\n".join(
f"[Chunk {i+1}: {c['title']}]\n{c['text'][:400]}"
for i, c in enumerate(chunks[:4])
)
prompt = GRADER_PROMPT.format(question=question, context=context)
try:
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.0,
max_tokens=150,
)
raw = resp.choices[0].message.content.strip()
# Remove markdown se presente
raw = re.sub(r'```json|```', '', raw).strip()
result = json.loads(raw)
result["raw_chunks_used"] = len(chunks)
return result
except Exception as e:
return {"relevant": True, "reason": f"Erro na avaliação: {e}", "confidence": 0.5, "raw_chunks_used": len(chunks)}
# ── SELF-CRITIQUE JUDGE ───────────────────────────────────────
CRITIQUE_PROMPT = """Você é um juiz especialista avaliando respostas de sistemas RAG.
Pergunta original: {question}
Contexto usado (base de conhecimento):
{context}
Resposta gerada:
{answer}
Avalie a resposta nos seguintes critérios e responda APENAS com JSON válido:
{{
"score": <0-10>,
"faithfulness": <0-10>,
"relevance": <0-10>,
"completeness": <0-10>,
"issues": ["lista de problemas encontrados"],
"improvements": "sugestão concreta de melhoria em 1-2 frases",
"verdict": "APPROVE" ou "REFINE"
}}
Critérios:
- faithfulness (0-10): resposta é fiel ao contexto? Sem alucinações?
- relevance (0-10): resposta responde a pergunta?
- completeness (0-10): resposta é completa e informativa?
- score: média ponderada (faithfulness*0.4 + relevance*0.35 + completeness*0.25)
- verdict: APPROVE se score >= 7, REFINE se score < 7"""
def self_critique(client: OpenAI, question: str, context: str, answer: str, model: str = "gpt-4o-mini") -> dict:
"""
Avalia a qualidade da resposta gerada.
Retorna: {score, faithfulness, relevance, completeness, issues, improvements, verdict}
"""
prompt = CRITIQUE_PROMPT.format(
question=question,
context=context[:1500],
answer=answer
)
try:
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.0,
max_tokens=300,
)
raw = resp.choices[0].message.content.strip()
raw = re.sub(r'```json|```', '', raw).strip()
return json.loads(raw)
except Exception as e:
return {
"score": 7,
"faithfulness": 7,
"relevance": 7,
"completeness": 7,
"issues": [],
"improvements": f"Erro na auto-avaliação: {e}",
"verdict": "APPROVE"
}