Chunking e Indexação para RAG: Tamanho, Overlap e Recall
Como definir tamanho de chunk, overlap e indexação dual (embedding + tsvector) para maximizar recall em pipelines RAG de produção — com código Python e tradeoffs reais.
Chunking e Indexação para RAG: Tamanho, Overlap e Recall
Antes de tunar embeddings ou rerankers, o gargalo silencioso de um pipeline RAG é como você fatia o conhecimento. Chunk grande demais dilui relevância; chunk pequeno demais quebra contexto. Indexação só semântica perde termos exatos. Este artigo cobre chunking consciente de estrutura e indexação dual na ingestão.
Por que chunking domina o recall
O retriever só encontra o que foi indexado. Se a resposta certa está dividida entre dois chunks sem overlap, ou enterrada num bloco de 4.000 tokens com ruído de TOC, o LLM nunca recebe contexto útil. Chip Huyen enfatiza em AI Engineering (p. 118) que retrieval quality — e chunking faz parte disso — limita a performance end-to-end do RAG.
Tamanho de chunk: regra prática
Não existe número mágico universal. Para livros técnicos em inglês com ~500 tokens por parágrafo denso:
| Estratégia | Chunk size | Quando usar |
|---|---|---|
| Precisão | 800–1200 chars | Queries com termos exatos, APIs, siglas |
| Balanceado | 1500–2000 chars | Tutoriais, capítulos mistos |
| Recall amplo | 2500–3000 chars | Conceitos que cruzam seções |
Em produção, 2000 chars com overlap 200 é um ponto de partida sólido para PDFs técnicos — alinhado ao que Unlocking Data with Generative AI and RAG (p. 307) discute sobre preservar unidades semânticas.
Overlap: evite cortes no meio da ideia
Overlap de 10–15% do chunk size reduz perda de contexto nas bordas. Sem overlap, uma definição que termina no chunk N e continua no N+1 pode não rankear nenhum dos dois para a query completa.
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=2000,
chunk_overlap=200,
separators=["\n## ", "\n### ", "\n\n", "\n", " ", ""],
)
chunks = splitter.split_text(markdown_section)
A ordem dos separadores prioriza quebras em headings Markdown antes de quebrar parágrafos — structure-aware chunking.
Chunking por seção vs. token-blind
Dividir o PDF inteiro em janelas fixas ignora capítulos. Pipeline melhor:
- Extrair Markdown estruturado (headings
#,##,###). - Agrupar por capítulo/seção.
- Subdividir seções longas com splitter token-aware.
- Anexar metadados:
book_title,chapter,page.
Metadados alimentam rerank por capítulo e citações (p. N) no gerador — padrão descrito em LLM Design Patterns (p. 414) para RAG com proveniência.
def build_chunks(pages: list[dict]) -> list[dict]:
"""Split by section; track page number per chunk."""
chunks = []
for section in detect_sections(pages):
for piece in splitter.split_text(section["text"]):
if len(piece.strip()) < 120:
continue
chunks.append({
"content": piece,
"chapter": section["heading"],
"page": section["page"],
})
return chunks
Indexação dual na ingestão
Cada chunk precisa de dois índices complementares:
- Embedding (E5 multilingual, prefixo
passage:) → HNSW para busca semântica. - tsvector (config
englishpara livros EN) → GIN para keyword search.
async def upsert_chunk(session, chunk, embedding):
tsv = await session.execute(
text("SELECT to_tsvector('english', :content)"),
{"content": chunk["content"]},
)
row = BookChunk(
content=chunk["content"],
chapter=chunk["chapter"],
page=chunk["page"],
embedding=embedding,
tsv=tsv.scalar(),
)
session.add(row)
Sem tsvector na ingestão, busca híbrida na query fica impossível — você depende só de paráfrase semântica e perde siglas.
Filtros de ruído antes de indexar
Chunks de TOC, índice remissivo e copyright poluem o índice. Gates heurísticos na ingestão (regex de .... em linhas de TOC, linhas de índice termo, 42) e no runtime (filter_book_noise) evitam que lixo domine o top-k.
Medindo impacto
Golden set com queries técnicas + keywords esperadas. Métricas:
- Precision@K: keyword aparece nos top-K chunks?
- Context recall: termos do chunk aparecem no draft gerado?
Altere chunk size em ±500 chars, reingira, compare MRR. Chip Huyen (p. 285) trata indexação e eval de retrieval como ciclo iterativo — não one-shot.
Conclusão
Chunking não é detalhe de ingestão — é decisão de arquitetura. Comece com seções Markdown + 2000/200, indexe embedding e tsvector juntos, filtre ruído, meça recall. Só depois suba para hybrid fusion e rerank.
Referências técnicas
- Chip Huyen, AI Engineering (p. 118) — retrieval quality e chunking como gargalo de RAG.
- Chip Huyen, AI Engineering (p. 285) — indexação e avaliação iterativa de retrieval.
- Unlocking Data with Generative AI and RAG (p. 307) — unidades semânticas e estratégias de chunk.
- LLM Design Patterns (p. 414) — RAG com metadados de proveniência e rerank.