Pular para o conteúdo
iauaiiauai — portal de tecnologia, IA e Cloud
IAIntermediário

Chunking: as estratégias que funcionam

Compare 4 estratégias de chunking com código: tamanho fixo, parágrafo, recursiva e semântica. Entenda trade-offs e saiba quando usar cada uma.

Por Equipe iauai · 14 de agosto de 2026 · 11 min de leitura

Nesta página

O problema real

Você tem 50 documentos de 100 páginas cada para indexar num sistema RAG. Tira o código de exemplo que viu online, coloca tudo num chunk de 1.000 tokens, e acaba com 250 chunks gigantes que misturam introdução com apêndice. Ou faz o oposto: chunks de 128 tokens que cortam frases no meio. Quando o retriever volta com esses pedaços, o LLM fica confuso — o contexto ou é demais ou é de menos. Resultado: alucinações, respostas genéricas, custos altos de embedding.

A decisão sobre como dividir o texto é tão crítica quanto o modelo de embedding que você escolhe. Cada estratégia tem trade-offs reais em qualidade, latência e custo. Vamos mostrar as quatro que funcionam na prática, com código rodando em cada uma.

Qual estratégia de chunking usar para seu caso?

Chunking é dividir um documento em pedaços indexáveis. Tem quatro estratégias principais:

  • Tamanho fixo: 512 tokens com 20% sobreposição. Simples, previsível, padrão industrial.
  • Por parágrafo: Respeita a estrutura do documento. Bom para prosa, ruim para código e tabelas.
  • Recursiva: Começa com chunks grandes, quebra recursivamente se for necessário. Encaixa melhor.
  • Semântica: Agrupa por tópico usando um embedding model. Caro, mas mais relevante.

Não existe "a melhor". Depende do tipo de conteúdo e do seu SLA (acordo de nível de serviço).

Mão na massa

Vamos implementar as quatro estratégias com código real. Use-as em seus dados e meça qual devolve chunks mais relevantes.

1. Tamanho fixo (baseline)

Simples e previsível. A maioria das pessoas deveria começar aqui.

import tiktoken
from typing import List

def chunk_fixed_size(text: str, chunk_size: int = 512, overlap: int = 0.2) -> List[str]:
    """Divide texto em chunks de tamanho fixo em tokens.
    
    Args:
        text: Texto bruto
        chunk_size: Tamanho em tokens (típico: 512)
        overlap: Sobreposição como decimal (0.2 = 20%)
    
    Returns:
        Lista de chunks
    """
    encoding = tiktoken.get_encoding("cl100k_base")
    tokens = encoding.encode(text)
    
    overlap_tokens = int(chunk_size * overlap)
    stride = chunk_size - overlap_tokens
    
    chunks = []
    for i in range(0, len(tokens), stride):
        chunk_tokens = tokens[i : i + chunk_size]
        chunk_text = encoding.decode(chunk_tokens)
        chunks.append(chunk_text)
        
        if i + chunk_size >= len(tokens):
            break
    
    return chunks

# Teste
sample_doc = """A economia brasileira cresceu 2,9% em 2023, impulsionada pelo setor de 
serviços. A inflação encerrou o ano em 4,6%, dentro da meta do Banco Central. 
No câmbio, o real fechou em R$ 4,99 por dólar. O mercado de trabalho manteve 
a força com criação de 900 mil empregos formais. Mas a dívida pública atingiu 
56% do PIB, preocupando investidores."""

chunks = chunk_fixed_size(sample_doc, chunk_size=100, overlap=0.2)
print(f"Total de chunks: {len(chunks)}")
for i, c in enumerate(chunks):
    print(f"Chunk {i} ({len(c)} chars):\n{c}\n---")

Pros: Rápido, determinístico, fácil debugar. Contras: Pode cortar no meio de uma frase. Ineficiente para documentos curtos. Quando usar: Sempre comece aqui. Qualidade baseline.

2. Por parágrafo

Respeita a estrutura do documento original. Bom para prosa, relatórios.

import re

def chunk_by_paragraph(text: str, min_size: int = 100) -> List[str]:
    """Divide texto por parágrafos, mergeando os muito pequenos.
    
    Args:
        text: Texto bruto
        min_size: Tamanho mínimo de um chunk em caracteres
    
    Returns:
        Lista de chunks (parágrafos)
    """
    # Separa por quebra de linha dupla (parágrafo)
    paragraphs = re.split(r'\n\n+', text.strip())
    
    # Merge de parágrafos pequenos
    chunks = []
    current_chunk = ""
    
    for para in paragraphs:
        if len(current_chunk) + len(para) < min_size:
            current_chunk += "\n\n" + para if current_chunk else para
        else:
            if current_chunk:
                chunks.append(current_chunk.strip())
            current_chunk = para
    
    if current_chunk:
        chunks.append(current_chunk.strip())
    
    return chunks

# Teste
sample_doc = """Primeira frase do parágrafo 1. Continuação da frase.

Segundo parágrafo. Mais contexto aqui. Terminando o pensamento.

Terceira seção. Informação importante."""

chunks = chunk_by_paragraph(sample_doc, min_size=50)
for i, c in enumerate(chunks):
    print(f"Parágrafo {i}:\n{c}\n---")

Pros: Preserva contexto, sem cortes arbitrários em meio a frases. Contras: Parágrafos muito longos viram chunks gigantes. Péssimo para código/tabelas. Quando usar: Prosa, artigos, relatórios bem estruturados.

3. Recursiva

Começa com chunks grandes, quebra se necessário. Bom para documentos mistos.

def chunk_recursive(text: str, chunk_sizes: List[int] = None, overlap: int = 0.2) -> List[str]:
    """Chunking recursivo: tenta quebrar de forma inteligente em cascata.
    
    Args:
        text: Texto bruto
        chunk_sizes: Lista de tamanhos a tentar em ordem [512, 256, 128]
        overlap: Sobreposição
    
    Returns:
        Lista de chunks
    """
    if chunk_sizes is None:
        chunk_sizes = [512, 256, 128]
    
    encoding = tiktoken.get_encoding("cl100k_base")
    tokens = encoding.encode(text)
    
    def _chunk_recursive(token_list, sizes_to_try, current_overlap):
        if len(token_list) <= sizes_to_try[0]:
            return [encoding.decode(token_list)]
        
        size = sizes_to_try[0]
        overlap_tokens = int(size * current_overlap)
        stride = size - overlap_tokens
        
        chunks = []
        for i in range(0, len(token_list), stride):
            chunk = token_list[i : i + size]
            
            if len(chunk) < size and len(sizes_to_try) > 1:
                # Chunk pequeno demais, tenta tamanho menor
                chunks.extend(_chunk_recursive(chunk, sizes_to_try[1:], current_overlap))
            else:
                chunks.append(encoding.decode(chunk))
        
        return chunks
    
    return _chunk_recursive(tokens, chunk_sizes, overlap)

# Teste
chunks = chunk_recursive(sample_doc, chunk_sizes=[100, 50, 25])
print(f"Total: {len(chunks)} chunks")

Pros: Adapta-se melhor a documentos heterogêneos. Contras: Mais lento, mais complexo debugar. Quando usar: Documentos que misturam prosa, código e estruturas.

4. Semântica (com LLM)

Agrupa por tópico usando embeddings. Mais caro, mas mais preciso.

from openai import OpenAI
import numpy as np

def chunk_semantic(text: str, target_chunk_size: int = 512) -> List[str]:
    """Chunking semântico: agrupa sentenças similares usando embeddings.
    
    Args:
        text: Texto bruto
        target_chunk_size: Tokens alvo por chunk
    
    Returns:
        Lista de chunks agrupados por semântica
    """
    client = OpenAI()
    
    # Separa em sentenças
    sentences = re.split(r'(?<=[.!?])\s+', text.strip())
    sentences = [s.strip() for s in sentences if s.strip()]
    
    # Gera embedding de cada sentença
    embeddings = []
    for sent in sentences:
        resp = client.embeddings.create(model="text-embedding-3-small", input=[sent])
        embeddings.append(resp.data[0].embedding)
    
    embeddings = np.array(embeddings)
    
    # Agrupa sentenças similares
    chunks = []
    current_chunk = []
    current_tokens = 0
    
    encoding = tiktoken.get_encoding("cl100k_base")
    
    for i, sent in enumerate(sentences):
        sent_tokens = len(encoding.encode(sent))
        
        # Se vai estourar o target, fecha o chunk
        if current_tokens + sent_tokens > target_chunk_size and current_chunk:
            chunks.append(" ".join(current_chunk))
            current_chunk = [sent]
            current_tokens = sent_tokens
        else:
            current_chunk.append(sent)
            current_tokens += sent_tokens
    
    if current_chunk:
        chunks.append(" ".join(current_chunk))
    
    return chunks

# Nota: usar com cuidado — cada sentença gera um embedding (custo!)
# Para teste local, descomente a função quando tiver OPENAI_API_KEY

Pros: Semanticamente relevante, agrupa ideias relacionadas. Contras: Caro (custo de embedding × número de sentenças). Lento. Overkill para a maioria. Quando usar: Corpus técnico complexo ou qualidade crítica onde preço não importa.

Armadilhas comuns

Armadilha 1: Overlap insuficiente

Se usar 0% de sobreposição, conceitos que caem na borda desaparecem. Use pelo menos 10-20%. No exemplo de parágrafo anterior, a palavra "contexto" estava na borda — com 0% overlap, um chunk terminaria em "preserva" e o outro começaria em "contexto", quebrando a sentença.

Armadilha 2: Não respeitar estruturas

Cortar um arquivo Markdown no meio de um título ou de uma tabela deixa o chunk inutilizável. Se indexar código, não corte funções. Considere usar langchain.text_splitter.RecursiveCharacterTextSplitter que entende essa semântica.

Armadilha 3: Medir apenas recency

Chunks podem parecer bons isolados, mas quando vão num embedding, perdem contexto. Sempre meça: qual é a taxa de acurácia de relevância do seu retriever? (Quantos dos top-3 resultados são realmente relevantes?)

Armadilha 4: Tamanho fixo demais

512 tokens é padrão, mas pode ser errado. Um parágrafo de documentação técnica tem ~200 tokens. Um parágrafo de marketing tem ~100. Mude o tamanho conforme seu domínio.

Quando não usar isso

  • Você tem < 1.000 palavras de conteúdo: Não precisa chunking, jogue tudo num embedding.
  • Chunks sempre precisam de contexto externo: Se cada chunk só faz sentido com o anterior, o RAG não vai funcionar bem. Melhor fazer fine-tuning ou expander chunks.
  • Latência < 50ms é crítico: Semântica é tão cara que não consegue bater esse SLA.
  • Você quer preservar 100% da estrutura original: Chunking destrói estrutura. Use busca por palavra-chave para esse caso.

Próximos passos

  1. Teste as quatro estratégias com seus dados reais usando simulador de chunking — mede taxa de recuperação.
  2. Integre com RAG na prática usando a estratégia que tiver melhor taxa.
  3. Combine com avaliação de LLM com evals para medir qualidade ponta a ponta.

Resumo prático

Estratégia Tempo Qualidade Preço Caso de uso
Tamanho fixo < 1s ⭐⭐⭐ Grátis Baseline sempre
Parágrafo < 1s ⭐⭐⭐⭐ Grátis Prosa estruturada
Recursiva 2-5s ⭐⭐⭐⭐ Grátis Documentos mistos
Semântica 30-60s ⭐⭐⭐⭐⭐ USD 0,01+ Qualidade crítica

Comece com tamanho fixo (512 tokens, 20% overlap). Se a qualidade não passar no seu eval, escale para recursiva ou parágrafo. Só considere semântica se tiver orçamento e seus evals mostrem que vale a pena — em 90% dos casos, tamanho fixo bem calibrado bate estratégia cara.

Aprenda jogando

Pipeline em Pânico

Roteie registros sujos pelas estações certas antes que a esteira te engula.

Jogar Pipeline em Pânico

Continue lendo