Text Classification
Transformers
Safetensors
Portuguese
support-automation
e-commerce
portuguese
ticket-routing
decision-model
multi-task
ensemble
Instructions to use syvorai/decisum with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use syvorai/decisum with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="syvorai/decisum")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("syvorai/decisum", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 5,308 Bytes
ce88da3 efb3a40 ce88da3 efb3a40 ce88da3 efb3a40 ce88da3 efb3a40 ce88da3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | #!/usr/bin/env python3
"""
servir_ensemble.py — API do ensemble (média das probs calibradas de N decisors).
Mesmos endpoints do servir_decisor (GET /saude, POST /decidir); carrega 2+
checkpoints, roda cada pergunta em cada modelo e devolve a probabilidade
MÉDIA (calibrada por modelo antes da média — média de distribuições
calibradas continua calibrada). Roteamento: mínima confiança da média.
Uso (host .144, GPU 1, porta de produção ou teste):
python3 scripts/servir_ensemble.py \
--checkpoints models/decisor-v10 models/decisor-v11_cons \
--device cuda --porta 8101
"""
import argparse
import json
import sys
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from chamados_para_decisor import CATEGORIAS, DECISOES, URGENCIA # noqa: E402
from inferencia_decisor import DecisorCPU # noqa: E402
PERGUNTAS = {
"categoria": {"tipo": "choice",
"instrucao": "Qual a categoria principal do chamado?",
"opcoes": CATEGORIAS},
"decisao": {"tipo": "choice",
"instrucao": "Qual decisão tomar para este caso?",
"opcoes": DECISOES},
"urgencia": {"tipo": "score",
"instrucao": "Qual a urgência? (0=baixa, 3=crítica)",
"opcoes": URGENCIA},
}
DECISORES = []
def decidir(estado: str, limiar: float) -> dict:
respostas, confs = {}, []
for nome, pergunta in PERGUNTAS.items():
ops = pergunta["opcoes"]
media = None
votos = []
for d in DECISORES:
r = d["modelo"].prever(estado, pergunta)
probs = [r["distribuicao"].get(op, 0.0) for op in ops]
votos.append(probs)
votos.append(None) # placeholder p/ manter par modelo/(peso)
votos.pop()
media = [sum(v[k] for v in votos) / len(votos) for k in range(len(ops))]
i = max(range(len(ops)), key=lambda k: media[k])
respostas[nome] = {
"resposta": ops[i],
"confianca": round(media[i], 4),
"distribuicao": {op: round(media[k], 4)
for op, k in zip(ops, range(len(ops)))},
}
confs.append(media[i])
conf = min(confs)
return {"respostas": respostas,
"confianca_roteamento": round(conf, 4),
"limiar": limiar,
"destino": "automatico" if conf >= limiar else "system2"}
class Handler(BaseHTTPRequestHandler):
def _json(self, obj, status=200):
corpo = json.dumps(obj, ensure_ascii=False).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(corpo)))
self.end_headers()
self.wfile.write(corpo)
def log_message(self, *a):
pass
def do_GET(self):
if self.path == "/saude":
self._json({
"status": "ok",
"ensemble": [d["nome"] for d in DECISORES],
"n_modelos": len(DECISORES),
"calibrado": all(bool(d["modelo"].temps) for d in DECISORES),
"categorias": len(CATEGORIAS),
})
return
self._json({"erro": "use GET /saude ou POST /decidir"}, 404)
def do_POST(self):
if self.path != "/decidir":
self._json({"erro": "rota desconhecida"}, 404)
return
try:
corpo = json.loads(self.rfile.read(int(self.headers["Content-Length"])))
estado = str(corpo["estado"]).strip()
if not estado:
raise ValueError("estado vazio")
limiar = float(corpo.get("limiar", 0.8))
if not 0.0 <= limiar <= 1.0:
raise ValueError("limiar fora de [0,1]")
except (KeyError, ValueError, TypeError) as e:
self._json({"erro": f"payload inválido: {e}",
"formato": {"estado": "texto", "limiar": 0.8}}, 400)
return
self._json(decidir(estado, limiar))
def _device_auto() -> str:
"""Escolhe o melhor device disponível: cuda → mps → cpu."""
import torch
if torch.cuda.is_available():
return "cuda"
if getattr(torch.backends, "mps", None) and torch.backends.mps.is_available():
return "mps"
return "cpu"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--checkpoints", nargs="+", required=True)
ap.add_argument("--device", default="auto",
choices=["auto", "cuda", "mps", "xpu", "cpu"],
help="default: auto (cuda → mps → cpu)")
ap.add_argument("--porta", type=int, default=8101)
args = ap.parse_args()
device = _device_auto() if args.device == "auto" else args.device
print(f"device: {device}")
for ck in args.checkpoints:
DECISORES.append({"nome": ck.rstrip("/").split("/")[-1],
"modelo": DecisorCPU(ck, device=device)})
print(f"✓ carregado: {ck}")
print(f"ensemble de {len(DECISORES)} modelos na porta {args.porta}")
ThreadingHTTPServer(("0.0.0.0", args.porta), Handler).serve_forever()
if __name__ == "__main__":
main()
|