Spaces:
Running
Running
| """ | |
| Pipeline de avaliacao da Fase 0. | |
| Gate: AUC > 0.70 sobre as 100 questoes curadas. | |
| Nota sobre nao_trivialidade: eh uma metrica relacional (Q vs Q0) e nao faz | |
| sentido em avaliacao standalone de questoes individuais. O score aqui usa | |
| apenas respondibilidade + tratabilidade, que sao propriedades intrinsecas de Q. | |
| nao_trivialidade entra apenas no loop RLHF quando temos pares (Q0, Q*). | |
| Uso: | |
| python -m src.eval.evaluate [--no-tratabilidade] [--output results.json] | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import time | |
| from pathlib import Path | |
| import numpy as np | |
| from dotenv import load_dotenv | |
| from sklearn.metrics import classification_report | |
| from sklearn.metrics import roc_auc_score | |
| from tqdm import tqdm | |
| load_dotenv(override=True) | |
| from src.corpus.fetch import fetch_corpus | |
| from src.corpus.index import CorpusIndex | |
| from src.corpus.index import build_index | |
| from src.ee.respondibilidade import respondibilidade | |
| from src.ee.tratabilidade import tratabilidade | |
| from src.eval.curated import CuratedQuestion | |
| from src.eval.curated import get_curated | |
| # Pesos normalizados para avaliacao standalone (sem nao_trivialidade) | |
| # B1 + B2 = 1.0 | |
| _B1 = 0.5 # respondibilidade | |
| _B2 = 0.5 # tratabilidade | |
| def score_question( | |
| q: CuratedQuestion, | |
| index: CorpusIndex, | |
| use_tratabilidade: bool = True, | |
| ) -> dict: | |
| resp = respondibilidade(q.text, index, top_k=10) | |
| if use_tratabilidade: | |
| tract_out = tratabilidade(q.text) | |
| tract = tract_out["prob_tractable"] * tract_out["confidence"] | |
| trajectory = tract_out.get("trajectory", "") | |
| else: | |
| tract = 0.5 # valor neutro quando API indisponivel | |
| trajectory = "unknown" | |
| # Score standalone: apenas respondibilidade + tratabilidade | |
| # nao_trivialidade entra apenas no loop RLHF com pares (Q0, Q*) | |
| ee = _B1 * resp + _B2 * tract | |
| return { | |
| "text": q.text, | |
| "label": q.label, | |
| "domain": q.domain, | |
| "respondibilidade": round(resp, 4), | |
| "tratabilidade": round(tract, 4), | |
| "trajectory": trajectory, | |
| "ee": round(ee, 4), | |
| } | |
| def evaluate( | |
| corpus_dir: Path, | |
| use_tratabilidade: bool = True, | |
| output_path: Path | None = None, | |
| delay_between_calls: float = 0.3, | |
| ) -> dict: | |
| questions = get_curated() | |
| fetch_corpus(corpus_dir) | |
| index = build_index(corpus_dir) | |
| results = [] | |
| for q in tqdm(questions, desc="Avaliando questoes"): | |
| r = score_question(q, index, use_tratabilidade=use_tratabilidade) | |
| results.append(r) | |
| if use_tratabilidade: | |
| time.sleep(delay_between_calls) | |
| labels = np.array([r["label"] for r in results]) | |
| scores = np.array([r["ee"] for r in results]) | |
| auc = roc_auc_score(labels, scores) | |
| threshold = float(np.median(scores)) | |
| preds = (scores >= threshold).astype(int) | |
| report = classification_report( | |
| labels, preds, target_names=["baixa_EE", "alta_EE"], output_dict=True | |
| ) | |
| summary = { | |
| "auc": round(auc, 4), | |
| "gate_passed": auc >= 0.70, | |
| "threshold_used": round(threshold, 4), | |
| "n_questions": len(questions), | |
| "classification_report": report, | |
| "per_question": results, | |
| } | |
| print(f"\n{'='*50}") | |
| print(f" AUC = {auc:.4f} | Gate (>= 0.70): {'PASSOU' if auc >= 0.70 else 'FALHOU'}") | |
| print(f"{'='*50}") | |
| print(f" Threshold (mediana): {threshold:.4f}") | |
| print(f" Precision alta_EE: {report['alta_EE']['precision']:.3f}") | |
| print(f" Recall alta_EE: {report['alta_EE']['recall']:.3f}") | |
| if output_path: | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| output_path.write_text(json.dumps(summary, indent=2, ensure_ascii=False), encoding="utf-8") | |
| print(f"\n Resultados salvos em: {output_path}") | |
| return summary | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser(description="Fase 0 — avaliacao de EE(Q)") | |
| parser.add_argument( | |
| "--no-tratabilidade", | |
| action="store_true", | |
| help="Desabilita chamadas API (usa valor neutro 0.5)", | |
| ) | |
| parser.add_argument( | |
| "--output", | |
| type=str, | |
| default="data/results/fase0_eval.json", | |
| help="Caminho para salvar resultados JSON", | |
| ) | |
| args = parser.parse_args() | |
| corpus_dir = Path(os.getenv("CORPUS_DIR", "data/corpus")) | |
| output_path = Path(args.output) | |
| evaluate( | |
| corpus_dir=corpus_dir, | |
| use_tratabilidade=not args.no_tratabilidade, | |
| output_path=output_path, | |
| ) | |