Skip to main content
Pre-release

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 .joblib sã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)

Source distribution for jurisgrow 0.1.0.dev0
File Size Uploaded
jurisgrow-0.1.0.dev0.tar.gz 328.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jurisgrow 0.1.0.dev0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.1.0.dev0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page