04-03-01 — Estratégias de chunking
TL;DR
Chunking é como você divide documentos antes de embeddar. A estratégia certa depende do tipo de conteúdo: use recursive character splitter (LangChain padrão) para textos genéricos, document-aware para PDFs com estrutura, e semantic chunking quando os limites de parágrafo não capturam bem as ideias completas. Chunk de 512 tokens com 10% overlap é um bom ponto de partida.
Por que chunking importa tanto
Chunks são a unidade de busca do RAG. Se o chunk for grande demais, o embedding captura muitos assuntos diferentes e fica "difuso" — difícil de encontrar por similaridade. Se for pequeno demais, perde contexto e a resposta fica incompleta.
Imagine um contrato de 50 páginas:
- 1 chunk = embedding de 50 páginas → completamente inutilizável para busca precisa
- Chunks de 50 tokens → embedding preciso, mas a resposta precisa de 20 chunks para fazer sentido
- Chunks de 512 tokens → captura um parágrafo ou seção completa, semanticamente coeso
Estratégias principais
1. Fixed-size chunking
Mais simples: corte em N tokens/caracteres, independente do conteúdo. Rápido de implementar, previsível. Problema: pode cortar frases no meio, separar conceitos relacionados.
def fixed_size_chunks(text: str, size: int = 512, overlap: int = 50) -> list[str]:
words = text.split()
chunks = []
for i in range(0, len(words), size - overlap):
chunk = " ".join(words[i:i + size])
chunks.append(chunk)
return chunks
2. Recursive character text splitter
O padrão do LangChain e o mais usado na prática. Tenta separar nos separadores mais naturais primeiro (\n\n, \n, ". ", " "), recorrendo a cortes menores apenas se necessário. Respeita estrutura do texto sem depender de metadados do documento.
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=512, # em tokens ou chars (depende do tokenizer)
chunk_overlap=51, # ~10% overlap
length_function=len,
separators=["\n\n", "\n", ". ", " ", ""] # hierarquia de separadores
)
chunks = splitter.split_text(documento_texto)
print(f"{len(chunks)} chunks gerados")
O overlap garante que conceitos que cruzam fronteiras de chunk não sejam perdidos. Se um parágrafo começa com "Esse valor" referenciando o número da cláusula anterior, sem overlap o chunk 2 perderia esse contexto. Regra prática: 10-20% de overlap. Mais que isso desperdiça armazenamento e cria ruído.
3. Semantic chunking
Usa embeddings para detectar onde o assunto muda. Gera chunks de tamanho variável mas semanticamente coesos. Mais caro (precisa embeddar tudo duas vezes) mas produz qualidade superior em textos longos e complexos.
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import AzureOpenAIEmbeddings
embeddings = AzureOpenAIEmbeddings(
azure_deployment="text-embedding-3-small",
azure_endpoint="https://seu-recurso.openai.azure.com/",
api_key="SUA_CHAVE"
)
# breakpoint_threshold_type: "percentile" | "standard_deviation" | "interquartile"
splitter = SemanticChunker(
embeddings,
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=95 # quebra quando similaridade cai abaixo do percentil 95
)
chunks = splitter.split_text(documento_texto)
4. Document-aware chunking
Usa a estrutura do documento (headers, seções, tabelas, páginas) como guia natural de chunking. Ideal para documentos bem estruturados como contratos, manuais, relatórios com seções claras. Requer extração prévia da estrutura (ex: Azure AI Document Intelligence).
from langchain.document_loaders import AzureAIDocumentIntelligenceLoader
# Carrega PDF preservando estrutura (headers, tabelas, listas)
loader = AzureAIDocumentIntelligenceLoader(
api_endpoint="https://seu-doc-intel.cognitiveservices.azure.com/",
api_key="SUA_CHAVE",
file_path="contrato.pdf",
mode="markdown" # preserva headers como ## e tabelas como markdown
)
docs = loader.load()
# Depois splitar respeitando headers markdown
from langchain.text_splitter import MarkdownHeaderTextSplitter
headers_to_split = [("#", "H1"), ("##", "H2"), ("###", "H3")]
md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split)
header_chunks = md_splitter.split_text(docs[0].page_content)
Tamanho ideal — o que os benchmarks dizem
Pesquisas empíricas (Llamaindex, Pinecone, Microsoft) convergem para:
| Tipo de documento | Tamanho recomendado | Overlap |
|---|---|---|
| Documentos técnicos (manuais, código) | 256-512 tokens | 10-15% |
| Contratos e documentos jurídicos | 512-1024 tokens | 15-20% |
| Artigos e notícias | 512 tokens | 10% |
| FAQs (pares Q&A) | 1 par por chunk | 0% |
| Código-fonte | Função/método completo | 0% |
Tamanho ideal é empírico para seu corpus. Faça benchmark: indexe o mesmo corpus com chunks de 256, 512 e 1024 tokens, rode um conjunto de queries de teste, meça context recall (% das queries cujas respostas estão nos top-k chunks). O melhor tamanho é aquele que maximiza context recall no seu domínio.
Dicas práticas
- Preserve metadados no chunk: sempre mantenha source, página, data, autor. Crucial para citação de fontes e filtros de acesso.
- Não misture idiomas no chunk: se o documento tem seções em PT e EN, prefira separar.
- Tabelas são especiais: não corte tabelas no meio. Ou mantenha a tabela inteira como chunk, ou converta para prosa.
- Headers como contexto: prefixe o chunk com o header da seção. Ajuda o embedding a entender o contexto sem precisar ler o documento todo.
def add_header_context(chunk: str, headers: list[str]) -> str:
"""Prefixa o chunk com a hierarquia de headers para melhor embedding."""
if headers:
header_context = " > ".join(headers)
return f"[{header_context}]\n\n{chunk}"
return chunk
# Exemplo: "[Contrato de Prestação de Serviços > Cláusula 5 > Penalidades]\n\nNo caso de atraso..."
Como isso se conecta
- 04-04-02 — HyDE, parent-document retriever — parent-doc é uma estratégia avançada de chunking + retrieval combinados
- 04-04-03 — Contextual retrieval — adicionar contexto ao chunk antes de embeddar
- 04-05-01 — Métricas de RAG — como medir se seu chunking está bom via context recall
Fontes
- LangChain. Text Splitters. python.langchain.com/docs/concepts/text_splitters
- LlamaIndex. Node Parser Modules. docs.llamaindex.ai
- Pinecone. Chunking Strategies for LLM Applications. pinecone.io/learn/chunking-strategies
- Microsoft. Chunk documents for Azure AI Search vector search. learn.microsoft.com