"""Geração de relatórios: leaderboard, CSVs e resumo.json.""" from __future__ import annotations import csv import json import os import stats CABECALHO = [ "pos", "sistema", "participante", "nota_global", "ic95", "n_itens", "juridica", "medica", "proptech", "compliance", "bank", "energia", "negocios", "pt", "en", "zh", "elo", "latencia_p50_ms", "custo_relativo", "recusa_pct", "adjudicados", ] def _f(v, casas=1): return round(v, casas) if isinstance(v, (int, float)) else v def escrever_resultados(linhas: list[dict], destino: str) -> str: os.makedirs(os.path.dirname(destino), exist_ok=True) with open(destino, "w", newline="", encoding="utf-8") as fh: w = csv.DictWriter(fh, fieldnames=CABECALHO) w.writeheader() for r in linhas: w.writerow({ "pos": r["pos"], "sistema": r["sistema"], "participante": r["participante"], "nota_global": _f(r["nota_global"]), "ic95": f"{r['ic'][0]:.1f}–{r['ic'][1]:.1f}", "n_itens": r["n_itens"], "juridica": _f(r["dominios"].get("juridica", 0)), "medica": _f(r["dominios"].get("medica", 0)), "proptech": _f(r["dominios"].get("proptech", 0)), "compliance": _f(r["dominios"].get("compliance", 0)), "bank": _f(r["dominios"].get("bank", 0)), "energia": _f(r["dominios"].get("energia", 0)), "negocios": _f(r["dominios"].get("negocios", 0)), "pt": _f(r["idiomas"].get("pt", 0)), "en": _f(r["idiomas"].get("en", 0)), "zh": _f(r["idiomas"].get("zh", 0)), "elo": round(r.get("elo", 0)), "latencia_p50_ms": round(r.get("latencia_p50", 0)), "custo_relativo": _f(r.get("custo_relativo", 1.0), 2), "recusa_pct": _f(r.get("recusa_pct", 0), 2), "adjudicados": r.get("adjudicados", 0), }) return destino def escrever_itens(itens: list[dict], destino: str) -> str: os.makedirs(os.path.dirname(destino), exist_ok=True) campos = ["id", "domain", "lang", "type", "diff", "pts", "sistema", "participante", "pontos", "nota", "latencia_ms", "refused", "adjudicado", "canary"] with open(destino, "w", newline="", encoding="utf-8") as fh: w = csv.DictWriter(fh, fieldnames=campos, extrasaction="ignore") w.writeheader() for it in itens: w.writerow({k: it.get(k, "") for k in campos}) return destino def leaderboard_md(linhas: list[dict], meta: dict) -> str: linhas_ord = sorted(linhas, key=lambda r: -r["nota_global"]) out = [ f"# Leaderboard — Benchmark Cego `{meta['rodada']}`", "", f"- Versão do regulamento: {meta['versao_regulamento']}", f"- Semente: `{meta['seed']}` · Itens aplicados: **{meta['n_itens']}** · Participantes: **{meta['n_participantes']}**", f"- Hash do banco: `{meta['banco_hash'][:16]}…` · Modo: **{meta['modo']}**", f"", "", "## Classificação geral (nota 0–100, IC 95% por bootstrap)", "", "| # | Sistema (cego) | Participante | Nota | IC 95% | Jur | Med | Pro | Cmp | Bnk | Ene | Neg | pt | en | zh | Elo* | p50 (ms) | Recusa |", "|---|----------------|--------------|------|--------|-----|-----|-----|-----|-----|-----|-----|----|----|----|------|----------|--------|", ] for r in linhas_ord: d, i = r["dominios"], r["idiomas"] out.append( f"| {r['pos']} | `{r['sistema']}` | {r['participante']} | **{r['nota_global']:.1f}** | " f"{r['ic'][0]:.1f}–{r['ic'][1]:.1f} | {d.get('juridica',0):.1f} | {d.get('medica',0):.1f} | " f"{d.get('proptech',0):.1f} | {d.get('compliance',0):.1f} | {d.get('bank',0):.1f} | " f"{d.get('energia',0):.1f} | {d.get('negocios',0):.1f} | {i.get('pt',0):.1f} | " f"{i.get('en',0):.1f} | {i.get('zh',0):.1f} | {r.get('elo',0):.0f} | " f"{r.get('latencia_p50',0):.0f} | {r.get('recusa_pct',0):.1f}% |" ) out += [ "", "\\* Elo derivado da matriz de confronto direto (Bradley-Terry); leitura de robustez, não da nota.", "", "## Assimetria de idioma (nota zh − nota en)", "", "| Participante | pt | en | zh | Δ (zh−en) | leitura |", "|---|---|---|---|---|---|", ] for r in linhas_ord: i = r["idiomas"] delta = i.get("zh", 0) - i.get("en", 0) leitura = "simétrica" if abs(delta) <= 5 else ("viés zh" if delta > 0 else "viés en") out.append(f"| {r['participante']} | {i.get('pt',0):.1f} | {i.get('en',0):.1f} | {i.get('zh',0):.1f} | {delta:+.1f} | {leitura} |") out += [ "", "## Itens mais difíceis (menor média entre participantes)", "", "| Item | Domínio | Idioma | Tipo | Média |", "|---|---|---|---|---|", ] for it in meta.get("itens_dificeis", [])[:15]: out.append(f"| `{it['id']}` | {it['domain']} | {it['lang']} | {it['type']} | {it['media']:.2f} |") out += [ "", "## Paridade entre idiomas do banco", "", f"- pt-BR: {meta['por_idioma'].get('pt', 0)} itens · en: {meta['por_idioma'].get('en', 0)} · zh: {meta['por_idioma'].get('zh', 0)}", f"- Tipos: {meta['por_tipo']}", f"- Dificuldade: {meta['por_dificuldade']}", "", "## Auditoria e integridade", "", f"- Itens com adjudicação: **{meta['adjudicados']}** de {meta['n_abertos']} abertos", f"- Taxa média de recusa: {meta['recusa_media']:.2f}%", f"- Canários aplicados: {meta['canarios']} · reproduções suspeitas de canário: {meta.get('canario_reproduzido', 0)}", f"- Sobreposição média de n-gramas (n=8) entre enunciado e resposta: {meta['ngram_media']:.3f}", "", "---", "", "Gerado por `harness/run.py`. Regras em `REGULAMENTO.md`; arquitetura em `SISTEMA.md`.", ] return "\n".join(out) def escrever_relatorio(linhas: list[dict], meta: dict, destino_dir: str) -> dict: os.makedirs(destino_dir, exist_ok=True) caminhos = { "leaderboard": os.path.join(destino_dir, "leaderboard.md"), "resultados": os.path.join(destino_dir, "resultados.csv"), "itens": os.path.join(destino_dir, "itens.csv"), "resumo": os.path.join(destino_dir, "resumo.json"), } escrever_resultados(linhas, caminhos["resultados"]) escrever_itens(meta["detalhe_itens"], caminhos["itens"]) with open(caminhos["leaderboard"], "w", encoding="utf-8") as fh: fh.write(leaderboard_md(linhas, meta)) with open(caminhos["resumo"], "w", encoding="utf-8") as fh: json.dump({"meta": {k: v for k, v in meta.items() if k != "detalhe_itens"}, "leaderboard": [{k: v for k, v in r.items()} for r in linhas]}, fh, ensure_ascii=False, indent=2, default=str) return caminhos