Objetivo da Aula
Escolher granularidade de chunking com critério arquitetural, não com o valor default de uma biblioteca
Explicar por que overlap existe, quanto custa e quando ele deixa de compensar
Desenhar o schema de metadados que acompanha cada chunk como parte do contrato de indexação, não como extra opcional
Identificar quando um índice único por domínio é a decisão certa e quando particionar em múltiplos índices é necessário
Definir uma política de reindexação (cadência, versionamento, invalidação) como decisão de operação, não de implementação pontual
Por que isso importa
Você já sabe o que é chunking: dividir um documento longo em pedaços menores porque o embedding de um documento inteiro perde granularidade e o contexto do LLM tem limite. O que a maioria dos pipelines RAG que você já viu não trata com cuidado é que a decisão de chunking é a decisão que mais determina a qualidade do sistema inteiro — mais do que a escolha do modelo de embedding, mais do que o algoritmo de ranking.
Isso acontece porque chunking é irreversível sem reindexação completa. Se você errar o modelo de embedding, troca o modelo e reindexar é caro mas direto. Se você errar a granularidade — chunks grandes demais que diluem a similaridade, ou pequenos demais que perdem contexto —, o sintoma não aparece como erro óbvio: aparece como "o retrieval está meio ruim" três meses depois, quando ninguém mais lembra qual foi a decisão original.
Impacto direto no seu trabalho: quando você aprova a arquitetura de indexação de um novo domínio de conhecimento (contratos, tickets de suporte, código-fonte), está decidindo o teto de qualidade que qualquer agente que consumir esse índice vai ter — para sempre, até a próxima reindexação completa. É uma decisão de arquitetura com custo de reversão alto, não um parâmetro de configuração trivial.
Conceitos Fundamentais
Granularidade: o trade-off central do chunking
Todo chunk carrega uma tensão entre dois extremos. Não existe tamanho "certo" universal — existe tamanho certo para um tipo de documento e um tipo de consulta esperada.
| Atributo | Chunks pequenos (ex.: 128-256 tokens) | Chunks grandes (ex.: 1000-2000 tokens) |
|---|---|---|
| Precisão do embedding | + Mais "puro": captura um único tópico, alta precisão de match | − "Diluído": um chunk que mistura 3 assuntos gera um vetor que não representa bem nenhum dos três |
| Custo de armazenamento/consulta | + Menor por chunk | + Menos chunks → índice mais enxuto |
| Preservação de contexto | − Perda de contexto: uma frase isolada de um contrato pode não fazer sentido sem a cláusula anterior | + Preserva contexto local: parágrafos inteiros, seções completas |
| Ruído / uso da janela do LLM | − Mais chunks no índice → mais ruído potencial no top-k | − Desperdiça janela de contexto do LLM: um chunk de 2000 tokens recuperado por causa de uma frase relevante traz 1900 tokens de ruído |
A pergunta arquitetural não é "qual tamanho de chunk", é "qual é a unidade de sentido mínima e completa para este tipo de documento". Um contrato tem cláusulas como unidade natural. Documentação de código tem função ou classe. Um ticket de suporte tem a mensagem completa. Chunking eficaz respeita fronteiras semânticas do domínio, não corta por contagem fixa de caracteres.
Chunking ingênuo (por contagem de caracteres):
"...a Contratada se compromete a entregar o produto em até 30 dias
corridos, contados a partir da assinatura deste instrumento. Em caso
de atraso superior a 5 dias, aplica-se multa de 2% ao dia sobre o valor
total..." → corta aos 500 caracteres, no meio da frase da multa
Chunking estrutural (por fronteira semântica do domínio):
Chunk 1: Cláusula 4 — Prazo de Entrega (completa)
Chunk 2: Cláusula 5 — Penalidades por Atraso (completa)
→ cada chunk é uma unidade de sentido fechada, com metadado
"cláusula: 5, tipo: penalidade" anexadoFundamento: Chunking Semântico vs. Chunking Fixo
Chunking fixo divide o texto por contagem de tokens/caracteres, com ou sem respeitar limites de sentença — é rápido de implementar e funciona igual para qualquer tipo de documento, mas ignora a estrutura do conteúdo. Chunking semântico (ou estrutural) usa a estrutura do documento — headings, parágrafos, funções de código, cláusulas contratuais — como fronteira de corte, geralmente combinado com um teto de tamanho para evitar chunks gigantes. Na prática, arquiteturas maduras usam uma combinação: corte estrutural como regra primária, com fallback para corte fixo quando uma seção estrutural excede o teto de tamanho definido.
Overlap: por que existe e quanto custa
Overlap é a prática de repetir uma fração do fim de um chunk no início do próximo, para que uma informação que fica "na fronteira" entre dois chunks não seja perdida por nenhum dos dois.
Documento original (fluxo contínuo):
"...O SLA de resposta é de 4 horas úteis. Em caso de descumprimento
reiterado (3 ou mais ocorrências no mês), o cliente tem direito a
cancelamento sem multa..."
Sem overlap (corte em 15 palavras):
Chunk A: "...O SLA de resposta é de 4 horas úteis. Em caso de
descumprimento reiterado (3 ou mais"
Chunk B: "ocorrências no mês), o cliente tem direito a cancelamento
sem multa..."
→ busca por "cancelamento por descumprimento de SLA" pode recuperar
só o Chunk B, que sozinho não menciona SLA
Com overlap de 30% :
Chunk A: "...O SLA de resposta é de 4 horas úteis. Em caso de
descumprimento reiterado (3 ou mais"
Chunk B: "Em caso de descumprimento reiterado (3 ou mais)
ocorrências no mês), o cliente tem direito a cancelamento
sem multa..."
→ Chunk B sozinho já contém "descumprimento" e "cancelamento" juntosOverlap não é gratuito: um overlap de 30% significa, no limite, 30% mais chunks armazenados e indexados para o mesmo documento — mais custo de armazenamento vetorial, mais chunks concorrendo no top-k, mais chance de o mesmo trecho aparecer duplicado no contexto final montado para o LLM.
Regra prática de dimensionamento: overlap_ideal ≈ tamanho médio de uma "unidade de referência cruzada" do domínio.
| Tipo de documento | Overlap recomendado | Justificativa |
|---|---|---|
| Documentos jurídicos | Alto (15-20%) | Cláusulas se referenciam entre si |
| Documentação técnica | Médio (10-15%) | Exemplos de código raramente dependem do parágrafo anterior |
| Tickets de suporte | Baixo (0-5%) | Cada ticket já é uma unidade fechada, sem chunking necessário na maioria dos casos |
Metadados: o contrato que acompanha cada chunk
Um chunk sem metadados é um vetor sem contexto de governança. O schema de metadados não é um "extra" adicionado depois — é parte do contrato de indexação definido no capítulo 2.1, porque é o que permite ao harness aplicar RBAC e filtros na hora da recuperação (capítulo 2.3).
Schema mínimo de metadados por chunk:
{
"chunk_id": "uuid",
"documento_origem": "contrato_fornecedor_xyz.pdf",
"posicao": { "secao": "Cláusula 5", "pagina": 3 },
"data_ingestao": "2026-01-15T10:00:00Z",
"data_documento": "2025-11-02", // quando o conteúdo foi criado
"dono": "juridico", // domínio/time responsável
"classificacao": "confidencial", // nível de sensibilidade
"escopos_permitidos": ["juridico", "diretoria"], // RBAC
"versao_chunking": "v3", // qual política gerou este chunk
"modelo_embedding": "text-embedding-3-large" // rastreabilidade
}Dois campos merecem destaque porque são os que mais frequentemente faltam em implementações amadoras: escopos_permitidos (sem ele, RBAC de recuperação não é aplicável — o índice vira uma sala sem portas) e versao_chunking (sem ele, uma migração de política de chunking não consegue identificar quais chunks estão desatualizados e precisam reindexação).
Aprofundamento Técnico
Um índice por domínio, ou um índice único particionado?
Depois de estabelecer, no capítulo 2.1, que grounding é serviço de plataforma e não feature isolada, surge a pergunta de implementação física: os índices vetoriais de domínios diferentes (jurídico, suporte, código) devem ser bases separadas ou uma base única com filtro por metadado?
| Aspecto | Índices fisicamente separados por domínio | Índice único, particionado por metadado (namespace/filtro) |
|---|---|---|
| Isolamento de dados | + Total: um bug de filtro não vaza dado entre domínios | − Depende inteiramente da correção do filtro — um bug de RBAC nesse ponto vaza dado entre domínios inteiros |
| Política de chunking | + Cada domínio pode ter política própria sem afetar outros | − Tende a convergir para "um tamanho serve a todos", perdendo a otimização por tipo de documento |
| Escala/reindexação | + Reindexação de um domínio não impacta os demais | − Não aplicável separadamente (base única) |
| Consulta federada | − Exige orquestração explícita no harness (fan-out + merge de scores) | + Trivial: uma query, filtro amplo |
| Overhead operacional | − Manter N bases | + Menos infraestrutura para operar |
Para dados de sensibilidade alta (jurídico, RH, financeiro), a separação física é a escolha defensável: o custo de um vazamento por bug de filtro é maior do que o custo operacional de manter índices separados. Para domínios de sensibilidade baixa a média onde consulta federada é um requisito real de produto (ex.: um agente investigativo que precisa cruzar documentação técnica com tickets de suporte), namespace único com filtro rigoroso — testado como se fosse controle de segurança, não só lógica de aplicação — é aceitável.
Reindexação: cadência, versionamento e invalidação
Indexação não é evento único — é processo contínuo com um ciclo de vida que precisa de política explícita, do contrário o índice diverge silenciosamente do conteúdo real.
Três gatilhos de reindexação, cada um com trade-off próprio:
| Gatilho de reindexação | Evento | Custo | Risco / uso típico |
|---|---|---|---|
| 1. Incremental (documento novo/alterado) | Evento de criação/edição no sistema de origem | Baixo, só o(s) documento(s) afetado(s) | Se o evento falhar silenciosamente, o índice fica desatualizado sem sinal visível |
| 2. Por expiração (TTL de metadado) | data_ingestao + TTL definido por domínio | Médio, reprocessa mesmo sem mudança de conteúdo | Uso típico: domínios onde "atualidade" importa mais que "o documento mudou" (ex.: políticas internas) |
| 3. Completa (mudança de política de chunking ou de modelo de embedding) | Decisão arquitetural — nova versão de chunking, troca de modelo de embedding | Alto, reprocessa 100% do domínio | Durante a janela de reindexação, coexistem chunks de v_antiga e v_nova — sem o campo versao_chunking do schema de metadados, não dá para saber quais migrar |
O ponto que costuma faltar em pipelines RAG de primeira geração é o terceiro gatilho. Trocar o modelo de embedding (por custo, por qualidade, por descontinuação do provedor) é uma decisão que parece de infraestrutura, mas na prática invalida todo o índice — vetores gerados por modelos diferentes não são comparáveis entre si. Sem o campo modelo_embedding no metadado, uma migração parcial mistura vetores incompatíveis no mesmo índice sem erro explícito — o sintoma é retrieval silenciosamente pior, exatamente o tipo de falha mais difícil de diagnosticar.
Custo de indexação como decisão de arquitetura, não detalhe operacional
Cada decisão de granularidade e overlap tem um custo direto em tokens processados (embedding) e em armazenamento (vetores), que se acumula em escala corporativa.
Exemplo numérico — base de 10.000 contratos, média de 8.000 tokens cada:
| Cenário | Tamanho do chunk | Overlap | Chunks por documento | Total de chunks indexados |
|---|---|---|---|---|
| A | 500 tokens | 20% | ≈ 8000 / (500 × 0.8) ≈ 20 | 200.000 |
| B | 1000 tokens | 10% | ≈ 8000 / (1000 × 0.9) ≈ 9 | 90.000 |
Diferença: Cenário A gera 2.2× mais chunks — mais custo de embedding na indexação inicial, mais armazenamento vetorial, mas potencialmente retrieval mais preciso para perguntas pontuais. Cenário B é mais barato e preserva mais contexto por chunk, mas corre risco de diluir a similaridade em documentos que tratam de vários assuntos na mesma seção.
Não existe resposta certa fora do contexto do domínio — mas existe erro certo: decidir esse número sem calcular o impacto em escala, e descobrir o custo real só quando a fatura de indexação (ou o FinOps de embeddings, tema da Parte 5) já é grande demais para reverter sem dor.
Chunking multi-resolução: quando um único tamanho não serve a duas finalidades
Um trade-off que o modelo "granularidade única por índice" não resolve bem: às vezes você precisa de um chunk pequeno para encontrar a informação (alta precisão de embedding) e de um chunk grande para entregar a informação ao LLM (contexto suficiente para responder). Chunking multi-resolução — também chamado de estratégia pai-filho (parent-child) — separa essas duas responsabilidades em vez de forçar um único tamanho a cumprir as duas.
Estratégia parent-child:
Nível "filho" (indexado para busca):
chunks pequenos (128-256 tokens), um embedding por chunk,
otimizados para precisão de similaridade
Nível "pai" (recuperado para contexto):
cada chunk filho carrega metadado "parent_id" apontando para
uma unidade maior (seção completa, documento inteiro, ou um
chunk de 1000+ tokens que o envolve)
Fluxo de recuperação:
1. busca vetorial roda sobre os embeddings dos chunks FILHOS
(mais precisos para o match)
2. ao montar o contexto final para o LLM, o harness troca cada
filho recuperado pelo seu PAI correspondente
3. LLM recebe menos chunks, mas cada um mais completo e coerente
Custo: um filho pode compartilhar o mesmo pai que outro filho
recuperado — o harness precisa deduplicar pais antes de montar
o contexto final, para não repetir o mesmo trecho duas vezes.Essa técnica resolve diretamente o Modo de falha 3 do capítulo 2.3 (fragmento sem contexto suficiente): em vez de aumentar overlap para todo o índice — o que infla custo de armazenamento uniformemente —, você paga o custo de contexto adicional só nos casos em que o filho recuperado realmente precisa do pai para fazer sentido. É mais complexidade de implementação (dois níveis de índice, ou um índice único com metadado de hierarquia), mas é uma resposta mais cirúrgica ao problema do que simplesmente aumentar o tamanho do chunk único.