This release is a pre-release and may not be stable for production use.
JurisGrow
Expansão contínua de classes para classificadores de documentos jurídicos.
O JurisGrow envolve um XGBoost já treinado e permite acrescentar classes novas ao longo do tempo — sem retreinar o modelo original e medindo, a cada passo, quanto as classes antigas sofreram.
pip install jurisgrow
O JurisGrow não realiza nenhuma comunicação de rede por padrão: sem telemetria, sem analytics, sem chamadas a serviços externos, sem dependência de LLM.
O que é
Um classificador em produção é um ativo: foi validado, tem métricas conhecidas e alguém confia nele. Quando aparece um tipo de documento novo, a saída óbvia — retreinar tudo — custa caro, exige o dataset histórico completo e coloca em risco tudo o que já funcionava.
O JurisGrow é a alternativa. O modelo base vira conhecimento histórico congelado; cada classe nova ganha um residual pequeno que compete com ele na inferência. Replay dá memória, mineração de negativos duros dá precisão de fronteira, e a política de aceitação recusa qualquer residual que danifique as classes antigas.
BASE (congelado) estabilidade
+
RESIDUAIS plasticidade
+
REPLAY memória
+
NEGATIVOS DUROS fronteira
+
DETECÇÃO DE UNKNOWN mundo aberto
Por que não simplesmente retreinar o XGBoost?
| Retreino completo | JurisGrow | |
|---|---|---|
| Dataset histórico | necessário por inteiro | só o replay compacto |
| Custo por classe nova | treino completo | um residual pequeno |
| Risco às classes antigas | não medido por construção | medido e vetado — leia o ≥ |
| Modelo validado | substituído | preservado |
Retreinar continua sendo uma opção legítima — e às vezes a melhor. O JurisGrow existe para quando ela é cara, arriscada ou impossível.
Como envolvo um classificador existente
from jurisgrow import JurisGrowClassifier
from jurisgrow.core.features import TfidfFeaturePipeline
clf = JurisGrowClassifier.from_xgboost(
model=modelo_existente, # XGBClassifier já treinado
feature_pipeline=pipeline_existente,
class_names=["peticao_alpha", "certidao_beta", "decisao_gamma"],
)
clf.predict("documento fictício requerendo operação alfa")
# 'peticao_alpha'
Sem residuais, o JurisGrow é indistinguível do modelo base — mesmo rótulo, mesma probabilidade. Isso é verificado por teste.
O modelo base nunca é reajustado. Não existe caminho de código que chame
base.fit() depois do from_xgboost.
Documentos estruturados, não só texto
Se o seu classificador consome um documento estruturado através de um
ColumnTransformer, o JurisGrow o reaproveita como está:
from jurisgrow.core.features import SklearnColumnPipeline
pipeline = SklearnColumnPipeline(
column_transformer=transformador_ja_ajustado,
row_builder=minha_funcao_documento_para_tabela,
)
Como adiciono uma classe
# 1. memória das classes antigas, a partir de dados de TREINO
clf.fit_replay(textos_de_treino, rotulos_de_treino)
# 2. a classe nova
relatorio = clf.add_class("termo_delta", texts=documentos_novos)
print(relatorio.summary())
# [ACEITO] termo_delta: F1=0.9231 (n=80) | esquecimento=+0.0111 (proxy_fpr) |
# ativação falsa=≥0.0111 (limite inferior)
A operação é transacional. Se o treino falhar, ou se a política recusar, o classificador continua exatamente como estava — mesmas predições, bit a bit.
Como o esquecimento é medido
Toda add_class() responde duas perguntas, não uma:
relatorio.new_class_f1 # aprendemos o novo?
relatorio.forgetting # o antigo sofreu?
relatorio.forgetting_method # COMO esse número foi obtido
relatorio.false_residual_activation_rate # quanto o residual invadiu?
relatorio.false_activation_is_lower_bound # é estimativa ou piso?
Leia o ≥ antes de confiar no número
Se o replay foi construído com os documentos em que o modelo base treinou — que é o que o passo 1 acima faz, e o padrão da biblioteca — a taxa de ativação falsa é um limite inferior, não uma estimativa. O base acerta quase 100% nesses documentos, as meta-features do residual derivam dessas probabilidades, e o residual quase não ativa falso ali. Medido em corpus com classes sobrepostas: 0,03 relatado contra 0,44 real.
Particionar o replay não corrige — as duas metades estão igualmente contaminadas. Para uma medida honesta, reserve documentos das classes antigas que o base não viu:
clf.fit_replay(textos_holdout, rotulos_holdout, seen_by_base=False)
O summary() imprime ≥ sempre que o número for piso, e
forgetting_method diz se o esquecimento foi medido, aproximado, garantido por
construção, ou não avaliado.
A AcceptancePolicy recusa por padrão se o dano passar do orçamento — F1 alto
na classe nova não compra aceitação:
from jurisgrow import AcceptancePolicy
politica = AcceptancePolicy(
min_new_class_f1=0.85,
max_old_false_positive_rate=0.02, # guarda principal
max_old_class_f1_drop=0.01, # guarda secundária
)
relatorio = clf.add_class("termo_delta", texts=docs, policy=politica)
if not relatorio.accepted:
for razao in relatorio.reasons: # todas as razões, não a primeira
print(razao)
A guarda principal é a taxa de ativação falsa, não o macro-F1. Com n
classes, uma única delas colapsando move o macro-F1 em 1/n — com 31 classes
isso é 0,032, mais que o triplo de um orçamento de 0,01. A ativação falsa mede o
dano diretamente e não muda de escala com o número de classes.
Documentos de tipo desconhecido
Um classificador closed-set diante de um tipo novo devolve, em silêncio, o rótulo conhecido mais parecido — com confiança alta e acerto nulo.
clf.fit_unknown_detector(proba_conhecidos, proba_novos)
p = clf.predict_detailed(documento_estranho)
print(p.label, p.reason) # UNKNOWN low_margin
Os limiares são calibrados, nunca constantes arbitrárias. Sem calibração o detector funciona, mas emite aviso.
Como salvo e carrego
clf.save("meu_modelo")
recarregado = JurisGrowClassifier.load("meu_modelo")
XGBoost em UBJ nativo, limiares e manifesto em JSON legível, replay em matriz esparsa. O round-trip preserva rótulo e confiança.
O manifesto traz contains_raw_text explícito, para um revisor humano decidir
num relance se o artefato pode sair da instituição.
Segurança: arquivos
.joblibsão desserializados com pickle e podem executar código arbitrário. Carregue apenas modelos de origem confiável.
O JurisGrow envia meus dados para algum lugar?
Não. Sem telemetria, sem analytics, sem relatório remoto de erros, sem requisição HTTP, sem chamada a LLM, sem serviço de nuvem.
Por padrão o ReplayBuffer guarda vetores esparsos, não texto, e o
save() recusa gravar texto cru sem allow_raw_replay=True. Predições não
retêm a entrada. O relatório de treino registra contagens e nomes de classe,
nunca conteúdo de documento.
Tutoriais
Sete notebooks executáveis em notebooks/ — do primeiro modelo à
adição de classes e à escolha da ordem de decisão, com dados fictícios:
pip install "jurisgrow[notebooks]"
jupyter lab notebooks/
Desenvolvimento
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest # testes (dados sintéticos apenas)
ruff check . # lint
mypy jurisgrow # tipos
python scripts/privacy_check.py . # varredura antes de publicar
A especificação completa do projeto está em
docs/especificacao-original.md; o plano de
implementação por etapas, em docs/plano/.
Licença
MIT.
Metadata
Release files for jurisgrow 0.1.0.dev0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jurisgrow-0.1.0.dev0.tar.gz | 328.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jurisgrow-0.1.0.dev0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 501.0 kB
Release files / jurisgrow-0.1.0.dev0.tar.gz
| Download URL | jurisgrow-0.1.0.dev0.tar.gz |
|---|---|
| Size | 328.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ee10beffef5d5b7676a2d068b256df6fa798e65a8f92bf73fb6e0443c7ebc8c4
|
|
BLAKE2b-256 checksum How to use checksums |
738619f43dd3ea706b44b0e700c0ceb8019d4c5e664d5bc78a125b180812bdd9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / jurisgrow-0.1.0.dev0-py3-none-any.whl
| Download URL | jurisgrow-0.1.0.dev0-py3-none-any.whl |
|---|---|
| Size | 172.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d3dfd03e7ff87646ec2c6ad134e92c767b38b81e8a3783d6d3f16cf13f5bab41
|
|
BLAKE2b-256 checksum How to use checksums |
79b7a4d45f102227d4c0f1dec9338ace345fbde62dd19175ef022faca2bc684e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|