Estratégias de divisão: fixa, por sentença e recursiva
Implemente e compare divisores de texto de tamanho fixo, baseados em limites de sentenças e recursivos e entenda como o tamanho e a sobreposição das partes afetam a qualidade da recuperação.
Estratégias de divisão: fixa, por sentença e recursiva é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.
Por que a Qualidade da Divisão em Blocos Importa
A divisão em blocos é o processo de separar documentos carregados em partes menores que caibam na janela de contexto do LLM e possam ser indexadas e recuperadas individualmente. A maneira como você divide os documentos determina a qualidade da recuperação mais do que quase qualquer outro fator. Quando uma divisão separa uma resposta em duas partes, nenhum dos blocos é suficiente sozinho para responder à pergunta. Um bloco que mistura dois tópicos não relacionados é recuperado para perguntas sobre ambos, mas não é útil para nenhum deles.
Divisão em Blocos de Tamanho Fixo
A divisão em blocos de tamanho fixo separa o texto em blocos com exatamente N caracteres ou N tokens, independentemente dos limites de frases ou parágrafos. É a estratégia mais simples e rápida de implementar. A principal desvantagem é que ela frequentemente corta frases ao meio, criando blocos que começam ou terminam no meio de um raciocínio. Isso é aceitável para textos densos e uniformemente formatados, como despejos de exportação de bancos de dados, mas produz uma qualidade de recuperação ruim em prosa narrativa ou documentação técnica.
def fixed_size_chunks(text, chunk_size=500, overlap=50):
'''Split text into fixed-size character chunks with overlap'''
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start += chunk_size - overlap # overlap keeps context at boundaries
return chunks
example = 'This is a long document. ' * 100
chunks = fixed_size_chunks(example, chunk_size=200, overlap=20)
print(f'Produced {len(chunks)} chunks, first: {chunks[0][:80]}...')Adicionando Sobreposição aos Blocos Fixos
A principal melhoria para a divisão em blocos de tamanho fixo é a sobreposição: cada bloco compartilha N caracteres com o bloco anterior. Isso garante que as informações próximas ao limite de um bloco apareçam nos dois blocos adjacentes. Com uma sobreposição de 50 a 100 tokens, uma frase que atravesse um limite será capturada integralmente em pelo menos um dos dois blocos. Mais sobreposição significa melhor cobertura, mas também um índice maior e conteúdo redundante nos resultados da recuperação.
# Overlap example with a sentence at the boundary
text = 'A B C D E F G H I J'
chunk_size = 6 # characters
overlap = 2
i = 0
while i < len(text):
print(repr(text[i:i+chunk_size]))
i += chunk_size - overlap
# Output shows overlapping content:
# 'A B C D'
# 'C D E F'
# 'E F G H'
# Each adjacent pair shares 2 charsDivisão nos Limites das Frases
A divisão nos limites das frases usa bibliotecas de PLN, como nltk ou spacy, para detectar o fim das frases antes de dividir o texto. Isso garante que nenhuma frase seja cortada ao meio. Você acumula frases até que adicionar a próxima ultrapasse o tamanho desejado e então inicia um novo bloco. O resultado são blocos que sempre contêm raciocínios completos, o que produz uma qualidade de incorporação significativamente melhor do que a divisão por quantidade fixa de caracteres.
import nltk
nltk.download('punkt', quiet=True)
def sentence_chunks(text, max_tokens=300):
sentences = nltk.sent_tokenize(text)
chunks = []
current = []
current_len = 0
for sent in sentences:
sent_tokens = len(sent.split())
if current_len + sent_tokens > max_tokens and current:
chunks.append(' '.join(current))
current = []
current_len = 0
current.append(sent)
current_len += sent_tokens
if current:
chunks.append(' '.join(current))
return chunksDivisão Recursiva por Caracteres
A divisão recursiva por caracteres é a estratégia mais usada em sistemas RAG de produção. Ela tenta uma hierarquia de separadores nesta ordem: primeiro as quebras de parágrafo (\n\n), depois as quebras de linha (\n), em seguida os pontos que encerram frases, depois os espaços e, por fim, os caracteres individuais. Ela divide no maior limite significativo que mantenha o bloco abaixo do tamanho desejado, resultando em blocos que respeitam a estrutura do documento tanto quanto possível.
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # target size in characters
chunk_overlap=200, # overlap between consecutive chunks
separators=['\n\n', '\n', '. ', '! ', '? ', ' ', ''],
length_function=len
)
with open('document.txt') as f:
text = f.read()
chunks = splitter.split_text(text)
print(f'Split into {len(chunks)} chunks')
for i, chunk in enumerate(chunks[:3]):
print(f'Chunk {i}: {len(chunk)} chars — {chunk[:60]}...')Divisão Orientada por Tokens
A contagem de caracteres é uma aproximação imprecisa da contagem de tokens. Um bloco de 1.000 caracteres pode ter 200 ou 400 tokens, dependendo do comprimento das palavras e do idioma. Para obter controle preciso, divida pelo número de tokens usando tiktoken. Isso é importante para ajustar os blocos à janela de contexto do modelo e estimar os custos com precisão. O TokenTextSplitter do LangChain encapsula o tiktoken para realizar uma divisão orientada por tokens.
from langchain_text_splitters import TokenTextSplitter
import tiktoken
# Split by actual token count, not characters
splitter = TokenTextSplitter(
encoding_name='cl100k_base', # GPT-4 tokenizer
chunk_size=256, # target tokens per chunk
chunk_overlap=32 # overlap in tokens
)
chunks = splitter.split_text(text)
# Verify token count
enc = tiktoken.get_encoding('cl100k_base')
for chunk in chunks[:3]:
tokens = len(enc.encode(chunk))
print(f'Chunk: {tokens} tokens')Escolhendo o Tamanho de Bloco Adequado
O tamanho do bloco é um hiperparâmetro crítico. Blocos pequenos (100–200 tokens) são precisos: contêm informações bem focadas que geram boas incorporações. Porém, perdem o contexto ao redor, então o LLM pode não ter informações suficientes para responder. Blocos grandes (500–1.000 tokens) fornecem mais contexto, mas suas incorporações representam um tópico mais amplo em média, o que dificulta a recuperação para consultas específicas. A maioria dos sistemas de produção usa de 256 a 512 tokens, com testes empíricos nos próprios dados.
Adicionando Contexto aos Blocos
Uma melhoria poderosa são os cabeçalhos contextuais dos blocos: anteponha o título do documento e o cabeçalho da seção ao texto de cada bloco antes de gerar a incorporação. Assim, a incorporação captura não apenas o conteúdo do bloco, mas também sua origem no documento. Um bloco que diz Benefícios e Política de Férias: os funcionários acumulam 15 dias por ano... é recuperado com muito mais precisão para perguntas sobre a política de férias do que o mesmo texto sem o cabeçalho.
def create_contextual_chunks(doc, splitter):
title = doc['metadata'].get('title', '')
section = doc['metadata'].get('section', '')
text = doc['text']
raw_chunks = splitter.split_text(text)
contextual_chunks = []
for chunk in raw_chunks:
# Prepend document context to each chunk
context_header = f'{title}\n{section}\n\n' if title else ''
contextual_chunks.append({
'text': context_header + chunk,
'metadata': doc['metadata']
})
return contextual_chunksComparando Estratégias com Seus Dados
Nenhuma estratégia de divisão em blocos é universalmente melhor. Faça uma avaliação rápida: selecione 20 perguntas cujas respostas você conhece, execute a recuperação com cada estratégia e meça com que frequência o bloco correto aparece entre os 5 primeiros resultados (taxa de acerto@5). Isso leva uma hora para configurar e economiza semanas de tentativas. Você frequentemente descobrirá que o formato específico dos seus documentos (prosa jurídica densa ou documentação técnica estruturada) favorece bastante uma estratégia em relação às outras.
def evaluate_chunking_strategy(questions_and_answers, retriever):
hits = 0
for qa in questions_and_answers:
results = retriever.retrieve(qa['question'], top_k=5)
result_texts = [r['text'] for r in results]
# Check if answer text appears in any retrieved chunk
if any(qa['answer'] in text for text in result_texts):
hits += 1
hit_rate = hits / len(questions_and_answers)
print(f'Hit rate@5: {hit_rate:.1%} ({hits}/{len(questions_and_answers)})')
return hit_rateLidando com Tipos Especiais de Documentos
Alguns tipos de documentos precisam de uma divisão específica. Arquivos de código devem ser divididos pelos limites de funções ou classes, não pela contagem de caracteres. Arquivos Markdown devem ser divididos nos limites dos cabeçalhos, para que cada bloco corresponda a uma seção. Tabelas devem ser mantidas intactas como um único bloco (dividi-las no meio torna o conteúdo ininterpretável). Planeje sua estratégia de divisão com base nos tipos de documentos do seu corpus, em vez de aplicar um divisor único para todos os casos.
from langchain_text_splitters import MarkdownHeaderTextSplitter
# Split Markdown by header hierarchy
md_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
('#', 'h1'),
('##', 'h2'),
('###', 'h3')
]
)
with open('documentation.md') as f:
md_text = f.read()
# Each chunk gets metadata from its heading hierarchy
chunks = md_splitter.split_text(md_text)
for chunk in chunks[:3]:
print('Section:', chunk.metadata)
print('Text:', chunk.page_content[:80])
print()Rastreamento da Linhagem dos Blocos
Todo bloco precisa de um ID estável e exclusivo derivado do documento de origem e da posição do bloco. Use esse ID para atualizar blocos específicos quando os documentos mudarem, sem precisar reindexar todo o corpus. Um hash determinístico do caminho de origem combinado com o índice do bloco funciona bem. Registre também a posição do bloco nos metadados (bloco 3 de 12 do documento X); isso ajuda a remontar seções completas quando vários blocos adjacentes são recuperados juntos.
import hashlib
def assign_chunk_ids(chunks, source_path):
for i, chunk in enumerate(chunks):
key = f'{source_path}::chunk_{i}'
chunk_id = hashlib.sha256(key.encode()).hexdigest()[:16]
chunk['id'] = chunk_id
chunk['metadata']['chunk_index'] = i
chunk['metadata']['total_chunks'] = len(chunks)
return chunks
# IDs are stable across re-runs if source and position match
chunks = sentence_chunks(text)
chunks = [{'text': c, 'metadata': {}} for c in chunks]
assign_chunk_ids(chunks, 'docs/policy_v3.pdf')Verificação Rápida
Teste sua compreensão dos conceitos de Engenharia de IA desta lição.
Recapitulação da Lição
Nesta lição, você aprendeu que: a divisão em blocos de tamanho fixo é simples, mas corta frases ao meio; a divisão nos limites das frases preserva raciocínios completos; a divisão recursiva por caracteres respeita a estrutura do documento e é a escolha mais comum em produção; e as técnicas avançadas incluem divisão orientada por tokens, cabeçalhos contextuais, divisores específicos para Markdown e código e IDs estáveis para atualizações incrementais. A seguir, geraremos incorporações desses blocos e os armazenaremos em um banco de dados vetorial.
Aprenda Python com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 30
- Aulas
- 120
Perguntas Frequentes
A aula “Estratégias de divisão: fixa, por sentença e recursiva” é grátis?
Sim — o texto completo de “Estratégias de divisão: fixa, por sentença e recursiva” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.
O que vou aprender em “Estratégias de divisão: fixa, por sentença e recursiva”?
Implemente e compare divisores de texto de tamanho fixo, baseados em limites de sentenças e recursivos e entenda como o tamanho e a sobreposição das partes afetam a qualidade da recuperação. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Engineering Academy?
Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Estratégias de divisão: fixa, por sentença e recursiva”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Engineering Academy?
Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Carregamento de documentos e extração de texto
- Estratégias de divisão: fixa, por sentença e recursiva
- Indexação: gerando embeddings e armazenando partes
- Consultar, recuperar e gerar