# 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" }