Pular para o conteúdo

Funcionalidade: Chunking Inteligente v3

pipeline/rechunk_v3.py — O componente mais sofisticado e critico do pipeline. Divide markdown juridico (saida do process_books.py) em chunks semanticamente coerentes usando heuristicas de dominio calibradas para livros doutrinarios brasileiros e internacionais.

PropriedadeValor
Scriptpipeline/rechunk_v3.py (890 linhas)
EntradaArquivos markdown em _staging/processed/{book}/
SaidaArquivos markdown rechunked (mesmo diretorio, sobrescritos)
Chunk minimo1.500 caracteres (MIN_CHUNK_CHARS)
Chunk maximo15.000 caracteres (MAX_CHUNK_CHARS)
Cobertura de testes0% — sinalizado como F26 (P1, v0.3)

O rechunker processa cada livro atraves de cinco passagens sequenciais. Cada passagem tem uma responsabilidade especifica, e a saida de uma alimenta a proxima.

flowchart TD
INPUT["Markdown do process_books.py"]
P1["Passagem 1: Divisao por Secao"]
P2["Passagem 2: Classificacao"]
P3["Passagem 3: Merge de Pequenos"]
P4["Passagem 4: Split de Grandes"]
P5["Passagem 5: Limpeza"]
OUTPUT["Chunks semanticos"]
INPUT --> P1
P1 -->|"Divide por 14 padroes de secao"| P2
P2 -->|"Classifica: noise, bibliography, summary, content"| P3
P3 -->|"Merge de chunks < 1500 chars com vizinhos"| P4
P4 -->|"Split de chunks > 15000 chars em limites de sentenca"| P5
P5 -->|"Remove vazios, normaliza whitespace"| OUTPUT

Detecta limites de secao usando 14 padroes regex (veja abaixo) e divide o documento em cada limite. Cada header de secao detectado vira o titulo de um novo chunk.

Classifica cada chunk por tipo de conteudo usando classify_block_content():

ClassificacaoLogica de DeteccaoTratamento
examplePadroes: “por exemplo”, “imagine que”, “e.g.”, “for instance”Mantido com o principio anterior
tableMais de 5 pipes e 2+ quebras de linhaPode ser chunk independente
characteristics3+ termos de caracteristica contratual (bilateral, oneroso, consensual…)Mantido como bloco indivisivel
law_articleInicia com Art. {numero}Mantido com o comentario subsequente
bibliography>50% das linhas comecam com nomes de autor em CAIXA ALTA, 5+ linhasExtraido como chunk separado
regularPadraoProcessamento normal

Chunks abaixo de MIN_CHUNK_CHARS (1.500) sao mesclados com seus vizinhos. A estrategia de merge preserva a coerencia semantica:

  • Exemplos sao mesclados com o principio anterior que ilustram
  • Notas de rodape sao mescladas com o paragrafo que as referencia
  • Artigos de lei sao mesclados com seu comentario

Chunks que excedem MAX_CHUNK_CHARS (15.000) sao divididos em limites de sentenca, garantindo que nenhum chunk ultrapasse o maximo e que sentencas sejam mantidas intactas.

Passagem final que remove chunks vazios, normaliza whitespace e valida que todos os chunks atendem ao limite minimo de conteudo (200 caracteres de texto real).

O detector de secoes usa estes padroes regex em ordem de prioridade. O primeiro match vence.

#PadraoTipoExemplo de Match
1^#{1,3}\s+(.+)$md_header# Chapter 1
2^\*\*Chapter\s+\d+[\.:]?\*?\*?\s*(.*?)chapter_en**Chapter 5:** Title
3^\*?\*?Cap[ií]tulo\s+\*?\*?\w+\*?\*?\.?\s*(.*?)capitulo_ptCapitulo V - Dos Contratos
4^CHAPTER\s+\d+\.?\s*(.*)$chapter_capsCHAPTER 5 BILATERAL CONTRACTS
5^CAP[ÍI]TULO\s+\w+\.?\s*(.*)$capitulo_capsCAPITULO V
6^\*?\*?T[ÍI]TULO\s+\w+\*?\*?\.?\s*(.*)$titulo**TITULO VI** Dos Contratos
7^\*?\*?PARTE\s+\w+\*?\*?\.?\s*(.*)$partePARTE ESPECIAL
8^\*?\*?Part\s+\w+\*?\*?\.?\s*(.*)$part_en**Part One** General Theory
9^(?:#{1,3}\s+)?\*?\*?Art\.?\s+\d+[\.\)]?\*?\*?\s*(.*)$artigoArt. 481. ou ### Art. 481
10^(?:#{1,3}\s+)?_?\*?\*?Se[çc][ãa]o\s+\w+_?\*?\*?\.?\s*(.*)$secaoSecao I - Disposicoes Gerais
11^\*?\*?Section\s+\w+\*?\*?\.?\s*(.*)$section_en**Section 3** Formation
12^(\d{1,3})\.\s+([A-Z][A-Z\s,]{8,80})$numbered_caps1. CONTRATOS BILATERAIS
13^\*\*(\d{1,3})\.?\*?\*?\s+(.{5,80})$numbered_bold**1.** Conceito e Natureza
14^([A-Z\s,]{15,80})$allcaps_titleCONTRATOS BILATERAIS E UNILATERAIS

Conteudo com estas palavras-chave no titulo e filtrado como nao-substantivo:

NOISE_TITLES = {
# Portuguese
'prefácio', 'prefacio', 'agradecimentos', 'agradecimento',
'dedicatória', 'dedicatoria', 'palavras do coordenador',
'nota do editor', 'notas do editor', 'nota à edição',
'sobre o autor', 'sobre os autores', 'dados catalográficos',
'ficha catalográfica', 'expediente',
'editora forense', 'editora saraiva', 'editora atlas',
'editora renovar', 'editora revista dos tribunais',
'no_content_here',
# English
'preface', 'foreword', 'acknowledgements', 'acknowledgments',
'dedication', 'about the author', 'about the authors',
"editor's note", "publisher's note",
}

Secoes que correspondem a estes titulos sao extraidas como chunks de bibliografia separados:

BIBLIOGRAPHY_TITLES = {
'bibliografia', 'referências bibliográficas', 'referências',
'bibliography', 'references', 'works cited', 'further reading',
'leituras complementares', 'obras consultadas',
}

Sumarios sao marcados como metadados (nao descartados, mas sinalizados):

SUMMARY_TITLES = {
'sumário', 'sumario', 'índice', 'indice',
'table of contents', 'contents', 'summary',
'índice remissivo', 'indice remissivo',
'table of cases', 'table of legislation', 'table of statutes',
}

A extracao de PDF frequentemente produz linhas repetidas (titulo do livro, nome do autor, heading do capitulo) no topo de cada pagina. O rechunker detecta essas repeticoes por analise de frequencia ao longo do documento e as filtra antes do chunking.

Livros juridicos fazem uso intensivo de notas de rodape. O rechunker agrupa notas de rodape com o paragrafo que as referencia usando duas funcoes de deteccao:

def is_footnote_line(line: str) -> bool:
"""Detect footnote lines at bottom of text."""
stripped = line.strip()
# "1 Author, Book, p. 123" or "¹ Author..." or "[1] Author..."
if re.match(r'^\d{1,3}\s+[A-ZÁÉÍÓÚÀÂÊÔÃÕÇ]', stripped):
return True
if re.match(r'^[¹²³⁴⁵⁶⁷⁸⁹⁰]+\s+', stripped):
return True
if re.match(r'^\[\d{1,3}\]\s+', stripped):
return True
return False

Quando um chunk contem a transcricao de um artigo de lei (ex.: Art. 476 do Codigo Civil...), o rechunker garante que o comentario do autor que se segue nunca seja separado do artigo. Isso e critico porque o comentario so faz sentido no contexto do artigo sendo discutido.

Listas de caracteristicas contratuais (bilateral, oneroso, consensual, comutativo…) sao detectadas quando 3+ termos de caracteristica aparecem juntos e sao mantidas como um unico bloco indivisivel, pois dividi-las destruiria a analise comparativa.

Terminal window
# Rechunk de todos os livros em staging
python3 pipeline/rechunk_v3.py
# Rechunk de um livro especifico
python3 pipeline/rechunk_v3.py contratos-orlando-gomes
# Definir tamanho minimo de chunk (caracteres)
python3 pipeline/rechunk_v3.py --min-chars 1500
# Simular alteracoes sem gravar
python3 pipeline/rechunk_v3.py --dry-run
# Forcar rechunk mesmo se ja processado
python3 pipeline/rechunk_v3.py --force
ConstanteValorDescricao
MIN_CHUNK_CHARS1.500Minimo de caracteres para um chunk valido
MAX_CHUNK_CHARS15.000Maximo de caracteres antes de split forcado
  • 0% de cobertura de testes para 890 linhas de logica baseada em regex. Um unico falso positivo na deteccao de secao pode se propagar pelo documento inteiro. Rastreado como F26.
  • Parser YAML customizado usa regex em vez de PyYAML. Caracteres especiais em titulos (dois pontos, aspas) podem corromper o frontmatter. O mesmo parser regex esta duplicado em enrich_chunks.py e embed_doutrina.py. Rastreado como F23.
  • Assume estrutura hierarquica — livros sem padroes claros de H1/H2/secao (ex.: dicionarios, compilacoes legislativas, coletaneas multi-autor) produzem chunks de baixa qualidade.
  • Deteccao de running headers e heuristica — linhas repetidas que NAO sao headers (ex.: um brocardo juridico que aparece varias vezes) podem ser falsamente filtradas.
  • Padrao allcaps_title e amplo — qualquer linha de 15-80 caracteres maiusculos e tratada como limite de secao, o que pode gerar falsos positivos em entradas bibliograficas, nomes de editora ou texto enfatizado.
  • Sem tokenizacao especifica para portugues — o divisor de sentencas para chunks grandes nao usa um tokenizador especifico para portugues, o que pode dividir em abreviacoes (ex.: “Art.”, “Dr.”).