Como dividir documentos para RAG?
Chunking é cortar cada documento em pedaços do tamanho que a busca vai devolver. Comece em 200 a 500 tokens, com um pouco de overlap quando o corte for cego. Mas o número é a parte menos importante: o que decide a qualidade é cortar respeitando a estrutura do texto — seção, cláusula, linha de tabela — em vez de contar caracteres até o documento acabar.
O chunk é a peça mais consequente da arquitetura de um RAG porque acumula duas funções que puxam para lados opostos. Ele é a unidade de recuperação, e para isso quer ser pequeno: um vetor que representa cinco mil tokens não representa nada em particular. Ele também é o texto que o modelo lê para responder, e para isso quer ser grande o bastante para conter a resposta inteira.
Essa tensão é o assunto todo. Quase toda técnica de chunking que apareceu desde 2023 — contextual retrieval, late chunking — existe para pegar o recall de um chunk pequeno sem pagar a perda de contexto que vem junto. E em PDF o problema começa antes: você não corta o documento, corta o que o parser conseguiu extrair dele.
As quatro estratégias
Tamanho fixo conta caracteres ou tokens e corta. É a mais criticada e a que todo projeto deveria usar primeiro, por um motivo pragmático: ela é determinística e barata, então serve de linha de base. Sem uma linha de base, você não sabe se a estratégia sofisticada melhorou alguma coisa.
Por parágrafo corta na quebra de linha dupla. Funciona bem em texto corrido e mal em qualquer coisa com estrutura: um documento técnico tem parágrafos de duas linhas e tabelas de quarenta, e cortar nas quebras produz chunks de tamanhos absurdamente diferentes.
Por seção usa a hierarquia de títulos. É a estratégia com melhor relação entre esforço e resultado na maioria dos corpora corporativos, porque a hierarquia já foi desenhada por alguém que sabia o que ficava junto. Um trabalho de 2024 que cortou relatórios financeiros por tipo de elemento estrutural, em vez de por parágrafo, relatou melhora nos resultados de RAG e — o detalhe que interessa — um tamanho de chunk que sai bom sem ajuste1.
Semântico embeda frase a frase e corta onde a distância entre frases vizinhas pula, na hipótese de que o pulo marca mudança de assunto. É a estratégia mais vendida e a que tem a evidência mais fraca. Um estudo de 2024 avaliou chunking semântico em recuperação de documento, recuperação de evidência e geração de resposta, e concluiu que o custo computacional não é justificado por ganho consistente sobre o corte de tamanho fixo2.
Na prática, o que roda na maioria dos pipelines é uma combinação: o corte recursivo. Você define uma lista de separadores em ordem de força — quebra de seção, quebra de parágrafo, quebra de linha, fim de frase, espaço — e tenta o mais forte primeiro. Se o pedaço resultante ainda passa do limite, desce um nível e tenta de novo dentro dele. É por isso que um chunker razoável tem umas trinta linhas e não uma: o corte por caractere é só o último recurso, o que sobra quando nenhum separador melhor apareceu.
Existe uma quinta abordagem, mais cara: usar um LLM para decidir onde cortar. O LumberChunker, de 2024, percorre grupos de passagens perguntando ao modelo onde o conteúdo começa a mudar, e reportou 7,37% de ganho de recuperação (DCG@20) sobre a linha de base mais competitiva em um benchmark de cem livros do Project Gutenberg3. O escopo importa: narrativa longa, onde a fronteira de assunto é genuinamente difícil de detectar por regra. Em documentação com títulos, você já tem a fronteira de graça.
O que o tamanho realmente controla
Chunk pequeno recupera melhor. Isso não é intuição: um trabalho de 2023 comparou granularidades de indexação e mostrou que indexar por unidades mais finas que a passagem — proposições, definidas como expressões atômicas autossuficientes — supera o nível de passagem em recuperação, e melhora perguntas e respostas subsequentes dentro de um orçamento fixo de computação4.
O motivo é mecânico. O embedding é uma média comprimida do texto inteiro do chunk. Quanto mais assunto entra, mais o vetor vira o centro de massa de tudo e menos ele fica perto de qualquer pergunta específica. Um chunk com a política de reembolso, o prazo de entrega e o horário do suporte não fica perto de nenhuma das três perguntas.
E chunk pequeno responde pior, pelo motivo oposto. O trecho chega ao modelo sem o que estava em volta. “O prazo é de 30 dias” é verdadeiro, recuperável e inútil sem a frase anterior, que dizia de qual prazo se trata.
O tamanho, então, não é uma escolha de qualidade. É onde você posiciona esse compromisso. E a pergunta que resolve o impasse não é “qual o melhor tamanho” e sim “de quanto texto contíguo a resposta típica da minha base precisa”. Numa base de FAQ, uma frase. Num contrato, a cláusula com as exceções dela. Em código, a função inteira com a assinatura.
Existe uma saída para não ter que escolher, e ela é mais simples do que parece: indexar por um tamanho e entregar outro. Você gera o vetor a partir do chunk pequeno, que recupera bem, e no momento de montar o prompt expande o trecho recuperado para incluir o parágrafo anterior e o seguinte, ou a seção inteira de onde ele saiu. Custa guardar o deslocamento do chunk dentro do documento — dois inteiros por chunk — e desmonta boa parte da discussão sobre tamanho ideal.
O dano de um corte cego
Tabela é o caso mais fácil de enxergar, e por isso vale detalhar. Uma tabela de preços tem o significado das células no cabeçalho. Corte no meio dela e a segunda metade vira uma lista de valores sem nome de coluna. O chunk continua sendo recuperável — “Pro” e “50 GB” estão lá, a pergunta sobre o plano Pro casa com ele — e o modelo recebe “Pro | 50 GB | R$ 2/GB” sem saber o que cada campo é.
O mesmo acontece de forma menos visível em texto. Uma cláusula separada do parágrafo “salvo nos casos previstos no item 4.2”. Um item de lista sem o cabeçalho que dizia do que era a lista. Um bloco de código sem a linha que dizia em qual arquivo ele fica.
Nenhum desses casos gera erro. Todos geram uma resposta bem escrita e parcialmente errada, que é a forma mais cara de errar, porque ninguém revisa o que parece certo.
Overlap: o que ele compra e o que ele custa
Overlap é repetir o fim de um chunk no começo do seguinte. Serve para uma coisa específica: garantir que a frase que caiu exatamente na fronteira apareça inteira em pelo menos um dos dois chunks. De 10% a 20% do tamanho do chunk cobre a maior parte dos casos.
O que ele custa aparece em três lugares. Você indexa mais vetores, na mesma proporção do overlap. Você paga embedding pelo texto repetido. E os chunks vizinhos ficam mais parecidos entre si, o que aumenta a chance de o top-5 vir com dois chunks quase idênticos ocupando duas das cinco vagas.
A conclusão prática é chata e verdadeira: overlap é remendo para corte cego. Quando o corte respeita seção ou cláusula, a fronteira já está num lugar onde ninguém termina uma frase pela metade, e overlap vira desperdício com passo extra.
Onde falha
O limite de tokens do modelo de embedding. Todo modelo tem um comprimento máximo de entrada. Texto acima disso é truncado, e a maioria das APIs trunca em silêncio. Um chunk de 2.000 tokens num modelo de 512 vira um vetor dos primeiros 512 tokens, e você tem um índice onde o fim de cada chunk simplesmente não existe. É a falha mais silenciosa desta lista.
Chunk grande empurra o custo para todas as perguntas. Dobrar o tamanho do chunk dobra o contexto de cada consulta, para sempre. Indexar é uma vez; o prompt é toda vez.
Boilerplate contamina todo vetor. Rodapé de confidencialidade, cabeçalho com o nome da empresa, aviso legal de três linhas repetido em cada página exportada de PDF. Se esse texto entra em todo chunk, ele empurra todos os vetores na mesma direção e a distância entre eles encolhe. O sintoma é uma busca que devolve resultados quase aleatórios com pontuações todas parecidas, e a causa não está no tamanho do chunk: está no que você deixou de limpar antes de cortar.
O tamanho ideal do teste não é o da produção. Medir com trinta perguntas bem escritas, todas respondíveis por uma frase, aponta para chunks curtos. As perguntas reais dos usuários são mais vagas e mais amplas, e favorecem chunks maiores. Se o conjunto de teste não parece com o tráfego real, ele otimiza para o sistema errado.
A perda de contexto é real e tem solução conhecida. O late chunking, proposto em 2024, contorna o problema embedando o texto longo inteiro e só depois aplicando o corte, antes do pooling — cada vetor de chunk sai carregando o contexto do documento em volta5. Vale saber que existe antes de gastar uma semana ajustando o tamanho.
Como decidir na sua base
- Corte por estrutura sempre que houver estrutura. Título, seção, cláusula, função, célula de notebook. A estrutura foi escrita por alguém que sabia o que pertencia junto.
- Use tamanho fixo só para o que sobrar, com 300 tokens e 15% de overlap como ponto de partida.
- Cole o caminho dos títulos no começo do texto do chunk. Um chunk que começa com “Preços > Excedente > Plano Pro” recupera melhor e dá ao modelo o contexto que o corte tirou. É a mudança mais barata desta lista.
- Nunca parta uma tabela sem repetir o cabeçalho em cada fragmento.
- Meça uma variável por vez. Trinta perguntas reais, com o trecho que deveria ser recuperado anotado, e a métrica sendo quantas vezes esse trecho aparece no top-5. Varie só o tamanho, depois só a estratégia.
- Rode o teste em mil documentos, não no corpus inteiro. Reindexar para testar é o que faz um time desistir de testar.
Quanto custa mudar de ideia
Todo teste de chunking é uma reindexação, porque o chunk é a unidade que virou vetor. Numa base de 3 milhões de caracteres, cerca de 900 mil tokens, cada variação custa uma passada completa de embedding — a chamada mais barata do catálogo, mas multiplicada pelo número de variações que você quiser comparar.
Do outro lado, o custo que se repete: cinco chunks de 300 tokens são 1.500 tokens de contexto por pergunta; cinco chunks de 1.000 são 5.000. Numa base com mil perguntas por dia, essa diferença é o argumento inteiro a favor de medir antes de escolher, em vez de copiar o valor padrão de um tutorial.
Há um custo escondido no meio dos dois, e é o que costuma decidir na prática: guardar o texto original ao lado do vetor, com o deslocamento de cada chunk dentro do arquivo de origem. São alguns bytes por chunk e é o que permite mudar de ideia sem reindexar — expandir o trecho, juntar vizinhos, refazer o corte a partir do documento em vez do banco. Índice que guardou só o vetor e o texto recortado obriga a começar do zero a cada teste, e é assim que a decisão de chunking vira permanente sem ninguém ter decidido isso.
Footnotes
-
Jimeno Yepes et al. (2024) cortaram relatórios financeiros por tipo de elemento estrutural em vez de por parágrafo e relataram melhora nos resultados de RAG, com um tamanho de chunk que sai bom sem ajuste. ↩
-
Qu et al. (2024) avaliaram chunking semântico em recuperação de documento, de evidência e em geração de resposta, e concluíram que o custo computacional não é justificado por ganho consistente sobre tamanho fixo. ↩
-
Duarte et al. (2024) usaram um LLM para achar o ponto em que o conteúdo muda e mediram 7,37% de ganho em DCG@20 sobre a linha de base mais competitiva, em um benchmark de cem livros de domínio público. ↩
-
Chen et al. (2023) compararam granularidades de indexação e mostraram que unidades mais finas que a passagem — proposições — superam o nível de passagem em recuperação e em perguntas e respostas subsequentes. ↩
-
Günther et al. (2024) propuseram embedar o texto longo inteiro e aplicar o corte depois do transformer, antes do pooling, de modo que cada vetor de chunk carregue o contexto ao redor. ↩
Perguntas frequentes
- Qual é o tamanho de chunk ideal?
- Não existe um universal. De 200 a 500 tokens funciona bem na maioria das bases de texto corrido, e é onde começar. O tamanho certo depende do tamanho da resposta que você precisa: se a informação vive em uma frase, chunks curtos; se ela depende de três parágrafos de contexto, chunks curtos vão falhar sempre.
- Preciso usar overlap entre chunks?
- Precisa quando o corte é por tamanho fixo, porque aí a fronteira cai no meio de frases. De 10% a 20% do tamanho do chunk resolve a maior parte dos casos. Quando o corte respeita seção ou cláusula, overlap é quase só desperdício: você paga vetores a mais para repetir um limite que já era natural.
- Chunking semântico vale a pena?
- A evidência é mais fraca do que a popularidade sugere. Um estudo de 2024 avaliou chunking semântico em três tarefas de recuperação e concluiu que o custo computacional não se justifica por ganho consistente sobre o corte de tamanho fixo. Cortar por estrutura do documento costuma entregar mais, por muito menos.
- Como dividir uma tabela em chunks?
- Não divida. Trate a tabela como uma unidade e, se ela for grande demais, repita o cabeçalho em cada fragmento e acrescente o título da seção. Uma linha de tabela sem o cabeçalho é uma sequência de valores sem nome de coluna, e nem a busca nem o modelo têm como recuperar esse contexto.
- Chunk pequeno ou grande recupera melhor?
- Pequeno recupera melhor e responde pior. Um trabalho de 2023 mediu isso variando a granularidade e encontrou ganho de recuperação com unidades mais finas que a passagem. O problema aparece depois: o trecho fino chega ao modelo sem o contexto ao redor, e ele responde a partir de um fragmento verdadeiro e insuficiente.
- Mudar o tamanho do chunk obriga a reindexar?
- Obriga. O chunk é a unidade que virou vetor, então mudar o corte invalida todos os vetores existentes. É por isso que vale medir o efeito do tamanho em um recorte de mil documentos antes de rodar no corpus inteiro, e por isso que o corte é uma decisão de arquitetura, não um parâmetro.
Referências
- Chen, T. et al.. Dense X Retrieval: What Retrieval Granularity Should We Use? (2023)arXiv:2312.06648
- Qu, R. et al.. Is Semantic Chunking Worth the Computational Cost? (2024)arXiv:2410.13070
- Jimeno Yepes, A. et al.. Financial Report Chunking for Effective Retrieval Augmented Generation (2024)arXiv:2402.05131
- Duarte, A. V. et al.. LumberChunker: Long-Form Narrative Document Segmentation (2024)arXiv:2406.17526
- Günther, M. et al.. Late Chunking: Contextual Chunk Embeddings Using Long-Context Embedding Models (2024)arXiv:2409.04701