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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jurisgrow-0.1.0.dev0.tar.gz (328.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jurisgrow-0.1.0.dev0-py3-none-any.whl (172.9 kB view details)

Uploaded Python 3

File details

Details for the file jurisgrow-0.1.0.dev0.tar.gz.

File metadata

  • Download URL: jurisgrow-0.1.0.dev0.tar.gz
  • Upload date:
  • Size: 328.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for jurisgrow-0.1.0.dev0.tar.gz
Algorithm Hash digest
SHA256 ee10beffef5d5b7676a2d068b256df6fa798e65a8f92bf73fb6e0443c7ebc8c4
MD5 98703b8a2e2accebc9a4693a26fb3c05
BLAKE2b-256 738619f43dd3ea706b44b0e700c0ceb8019d4c5e664d5bc78a125b180812bdd9

See more details on using hashes here.

File details

Details for the file jurisgrow-0.1.0.dev0-py3-none-any.whl.

File metadata

  • Download URL: jurisgrow-0.1.0.dev0-py3-none-any.whl
  • Upload date:
  • Size: 172.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for jurisgrow-0.1.0.dev0-py3-none-any.whl
Algorithm Hash digest
SHA256 d3dfd03e7ff87646ec2c6ad134e92c767b38b81e8a3783d6d3f16cf13f5bab41
MD5 6f41aad98d70cee64e16bc47c2877bfc
BLAKE2b-256 79b7a4d45f102227d4c0f1dec9338ace345fbde62dd19175ef022faca2bc684e

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0.dev0 This release

2 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