Conteúdo da trilha
8 minNarração em inglês, legenda em portuguêsPodcast (RSS)
Capítulos
  1. 0:00Abertura
  2. 0:53A ideia
  3. 1:44Como funciona
  4. 2:17O mesmo pedaço
  5. 3:31Um desafio
  6. 4:15Quando não usar
  7. 5:41Em produção
  8. 6:47O código, por intenção
  9. 7:31Fechamento
Transcrição

Abertura

O Eduardo cuida da busca sobre o manual interno de uma operadora de plano de saúde. Alguém cortou os documentos a cada quinhentos tokens, ignorando a estrutura. E um dos pedaços diz só isto.

O prazo é de 21 dias úteis a partir do pedido, e pode ser prorrogado uma vez, com justificativa. Nos casos do item 3.2.

Prazo de quê? Para quem? De qual versão do manual? A busca acha esse trecho para o prazo de autorização de cirurgia. Mas também para o prazo de reembolso, porque o trecho nunca diz de que assunto trata. E o modelo responde com confiança, e às vezes erra o assunto.

Nesta aula, você vai aprender a cortar documentos de um jeito que cada pedaço possa ser entendido, e encontrado, sozinho.

A ideia

Pense numa ficha de arquivo. Uma boa ficha não diz só o que tem dentro. Diz de qual pasta, de qual gaveta e de que ano ela veio. O padrão faz duas mudanças, as duas na hora de indexar.

Primeiro, cortar pela estrutura. Títulos, seções, cláusulas, itens de lista e tabelas são as fronteiras. Um pedaço nunca começa no meio da frase, e nunca separa uma tabela do seu cabeçalho. Ainda existe um tamanho máximo, mas ele é o limite, não a regra.

Segundo, anexar o contexto. Cada pedaço ganha um cabeçalho com o caminho, documento, capítulo e seção, a versão e, opcionalmente, uma frase, escrita por um modelo, que situa o trecho.

Esse cabeçalho entra no texto indexado, para a busca. E no texto que vai para o modelo, para a resposta e a citação.

Como funciona

O documento chega, em PDF, página web ou texto puro.

Um parser encontra os títulos, as seções, as listas e as tabelas.

O corte segue essa estrutura, dentro do tamanho máximo.

Um modelo pequeno lê o documento e o trecho, e escreve uma frase dizendo onde ele se encaixa.

Cada pedaço vira um cabeçalho, com caminho, versão e essa frase, mais o texto original.

E o pedaço completo é indexado, para busca por palavra e busca vetorial.

O mesmo pedaço

Agora o mesmo trecho, depois do padrão. Três coisas mudaram, e cada uma ensina algo.

O caminho. Autorizações, depois cirurgia eletiva, agora entra no embedding e na busca por palavra. Uma pergunta de reembolso não parece mais tão parecida.

A frase de contexto. Ela traduz o trecho vago para o vocabulário da pergunta. Autorização, cirurgia, plano.

E a cláusula vizinha. Se a seção cabe no limite, o item 3.2 vai junto. Se não cabe, o cabeçalho ainda aponta para a seção 4.3, e a citação não finge que o trecho está completo.

Quanto custa? Digamos oitocentos tokens de corpo e oitenta de cabeçalho. Cinco pedaços por pergunta vão de quatro mil tokens para quatro mil e quatrocentos. O custo de verdade fica na ingestão, uma chamada pequena por pedaço, com o documento inteiro em cache. Não na pergunta.

E pôr o caminho no começo ajuda duas vezes. A busca acha o assunto certo. E o modelo, que lê melhor o começo de um contexto do que o meio, vê primeiro de onde o trecho veio.

Um desafio

Uma pergunta para você. Você ligou a frase de contexto, mas gerou lendo só o pedaço, não o documento. No eval, a pergunta de reembolso ainda traz o trecho de autorização. Por quê?

Porque a frase é tão vaga quanto o pedaço. O modelo que escreveu também nunca viu o capítulo. Então ele escreve algo como, este trecho fala de um prazo.

A frase existe para injetar o contexto que o pedaço perdeu. Sem o documento nessa chamada, não tem contexto para injetar. Por isso a chamada leva o documento inteiro, em cache, e reaproveita em todos os pedaços.

Quando não usar

Documentos curtos que cabem num pedaço só. Uma política de trabalho remoto de duas páginas não precisa ser cortada. Título e versão no começo bastam. Cortar só inventa fronteiras, e pode partir a regra no meio.

Texto sem estrutura, como transcrição. Não tem título para seguir. Corte por assunto, ou por janelas com sobreposição.

E material que cabe inteiro no contexto. É o smell de duas aulas atrás. Não corte o que o modelo poderia ler.

Agora as armadilhas comuns. Cortar por caractere e chamar de estrutural. Se a fronteira ignora o título da tabela, você só mudou o tamanho.

Que foi exatamente o que aconteceu na operadora. As linhas com os números dos prazos caíram num pedaço. O cabeçalho, com as colunas, autorização, reembolso, recurso, ficou no anterior. Então o bot começou a citar 21 sem dizer qual prazo. Uma tabela vira um pedaço próprio, com título e colunas.

E deixar versões revogadas no índice. Um cabeçalho que diz versão seis não impede a busca de trazer a versão seis. Filtre por vigência na hora da consulta e remova o documento antigo ao reindexar. Isso se resolve no índice, não no prompt.

Em produção

O que medir. Recall e precisão da busca, antes e depois, no mesmo eval.

Monte perguntas gêmeas de propósito. O prazo de reembolso e o prazo de autorização. Se o trecho de reembolso fica fora dos cinco primeiros na pergunta de autorização, o cabeçalho está fazendo o seu trabalho.

Confira as citações. A seção citada na resposta tem que ser a certa.

E crie um alerta para a taxa de caminho vazio na ingestão. Se trinta por cento dos pedaços saem sem caminho, o parser quebrou num tipo novo de PDF. Não tape com a frase gerada. Corrija a conversão e reindexe.

Um ponto de segurança. Um pedaço pode carregar dado pessoal, o nome de um beneficiário, o número da carteirinha. Pela LGPD, o índice é mais um lugar onde esse dado mora. Se a sua política proíbe mandar esses documentos para o modelo que escreve a frase de contexto, fique no cabeçalho estrutural, montado só com título e seção.

O código, por intenção

Na página, tem duas abas. A de TypeScript faz só a parte estrutural. Ela corta o documento pelos títulos, guarda o caminho conforme desce e põe título, caminho e versão no topo de cada pedaço. Nenhuma chamada a modelo.

A de Python acrescenta a frase de contexto. Ela manda o documento inteiro como um bloco em cache e pergunta a um modelo pequeno, para cada pedaço, de que assunto e de que caso aquele trecho trata.

E essa é a ordem para adotar. Comece pelo cabeçalho estrutural. É barato, e já resolve boa parte do problema.

Ligue a frase gerada só se o eval de confusão de assunto ainda falhar.

Fechamento

Então, a ideia para levar. Um pedaço cortado às cegas é uma frase sem endereço. Corte pela estrutura do próprio documento e dê a cada pedaço o caminho, a versão e, quando ajuda, uma frase dizendo do que ele trata. Aí a busca e o modelo sabem onde estão.

Esse caminho no cabeçalho é feito de palavras exatas, e palavra exata é o que a busca por palavra-chave adora. Na próxima aula, a gente junta busca por palavra-chave com embeddings: hybrid search. Até lá.

PadrãoContexto e conhecimento

Chunking com Contexto

Contextual Chunking

Chunking com Contexto (Contextual Chunking) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Contexto e conhecimento. Cortar documentos respeitando a estrutura e anexar a cada pedaço o contexto de onde ele veio.

Elemento
17/70
Nível
Intermediário
Leitura
8 min
Atualizado
Nesta página 18
  1. 01Intenção
  2. 02Problema
  3. 03Solução
  4. 04Estrutura
  5. 05Pseudocódigo
  6. 06Cenários reais
  7. 07Aplicabilidade
  8. 08Quando não usar
  9. 09Como implementar
  10. 10Variações
  11. 11Custo, latência e falhas
  12. 12Como avaliar
  13. 13Em produção
  14. 14Segurança
  15. 15Prós e contras
  16. 16Relações
  17. 17Código
  18. 18Teste seu entendimento
  19. ··Perguntas rápidas

Intenção

Dividir documentos seguindo sua estrutura (seções, cláusulas, tabelas) e anexar a cada pedaço o contexto de onde ele veio: documento, seção, data e uma frase que situa o trecho. Serve para RAGs em que os pedaços, sozinhos, perdem o sentido.

Em uma frase
Uma ficha de arquivo que diz não só o conteúdo, mas de qual pasta, gaveta e ano ela saiu.

Problema

Eduardo mantém a busca do manual interno de uma operadora de saúde. Os documentos foram cortados a cada 500 tokens, sem olhar a estrutura. Um dos pedaços diz apenas:

...o prazo é de 21 dias úteis, contados a partir da solicitação, e pode ser
prorrogado uma vez mediante justificativa. Nos casos previstos no item 3.2...

Prazo de quê? Para quem? De qual versão do manual? A busca acha esse trecho para a pergunta “qual o prazo de autorização de cirurgia eletiva?”, mas também para “qual o prazo de reembolso?”, porque o trecho não diz de que assunto trata. O modelo responde com confiança e às vezes erra o assunto.

O corte cego também separou uma tabela do seu título. As linhas com números de prazo ficaram num pedaço, o cabeçalho com as colunas (autorização, reembolso, recurso) ficou no pedaço anterior. O bot passou a citar “21” sem dizer de qual prazo.

Há ainda o item 3.2. Ele está em outro pedaço. Quem lê só o trecho acima não tem como saber o que o item autoriza. Na operadora, isso mistura regras da ANS com regras internas do plano.

A pergunta que este padrão responde: como dividir documentos de modo que cada pedaço seja compreensível e encontrável sozinho?

Solução

Duas mudanças no momento da indexação:

  • Dividir pela estrutura: usar títulos, seções, cláusulas, itens de lista e tabelas como fronteiras. Um pedaço nunca começa no meio de uma frase nem separa uma tabela do seu cabeçalho. Tamanho máximo existe, mas é o limite, não a regra.
  • Anexar contexto: cada pedaço ganha um cabeçalho com o caminho (documento, capítulo, seção), a data ou versão e, opcionalmente, uma frase gerada por um modelo que situa o trecho (“Este trecho trata do prazo de autorização de cirurgias eletivas para planos individuais”).

O cabeçalho entra no texto indexado (para a busca) e no texto enviado ao modelo (para a resposta e a citação).

A Anthropic descreveu essa ideia no texto Contextual Retrieval (setembro de 2024). Antes de indexar, um modelo lê o documento e o pedaço e escreve uma frase que situa o trecho. Essa frase entra no texto que vai para o embedding e para a busca por palavra-chave.

Exemplo trabalhado

Pegue o trecho do prazo e o documento de onde ele saiu. Depois do corte cego, o índice guarda só o miolo. Depois deste padrão, o mesmo miolo ganha um cabeçalho:

[Manual do Beneficiário | Autorizações | 4.1 Cirurgias eletivas | v7, vigência 2026-03]
Este trecho trata do prazo de autorização de cirurgias eletivas em planos individuais.
O prazo é de 21 dias úteis, contados a partir da solicitação, e pode ser
prorrogado uma vez mediante justificativa. Nos casos previstos no item 3.2
(urgência e emergência, ver seção 4.3), o prazo não se aplica.

Três coisas mudaram, e cada uma ensina uma lição.

  1. O caminho. “Autorizações > 4.1 Cirurgias eletivas” entra no embedding e no BM25. A pergunta sobre reembolso deixa de competir com a mesma similaridade.
  2. A frase de contexto. Ela traduz o trecho vago para o vocabulário da pergunta (“autorização”, “cirurgia eletiva”, “plano individual”).
  3. A cláusula vizinha. Se a seção 4.1 cabe no limite, o item 3.2 viaja junto. Se não cabe, o cabeçalho ainda aponta para 4.3, e a citação não finge que o trecho é completo.

A conta de tokens, com números ilustrativos. Suponha 800 tokens de miolo e 80 de cabeçalho. Em 2.000 pedaços, o índice engorda uns 160 mil tokens. Na consulta, cinco pedaços passam de 4.000 para 4.400 tokens. O custo extra está na ingestão (uma chamada pequena por pedaço, com o documento em cache), não na pergunta.

O paper “Lost in the Middle” (Liu et al., 2023) mostra que modelos usam melhor o começo e o fim do contexto do que o meio. Colocar o caminho no início do pedaço ajuda duas vezes: a busca acha o assunto certo, e o modelo lê primeiro de onde o trecho veio.

Checagem +20 XP de primeiraA busca devolveu o trecho do prazo de 21 dias para uma pergunta sobre reembolso. O trecho veio do capítulo de autorizações. Qual correção ataca a causa?

Estrutura

flowchart LR
  D(["1. Documento"]):::io --> P["2. Analisar a estrutura"]:::tool
  P --> S["3. Dividir por seção e cláusula"]:::tool
  S --> C(["4. Gerar frase de contexto"]):::llm
  C --> H["5. Montar cabeçalho e trecho"]:::tool
  H --> I[("6. Índice")]:::store
  1. O documento chega em PDF, HTML ou texto.
  2. Um parser identifica títulos, seções, listas e tabelas.
  3. A divisão segue essa estrutura, respeitando um tamanho máximo.
  4. Um modelo pequeno lê o documento e o trecho e escreve uma frase que situa o trecho.
  5. Cada pedaço vira cabeçalho (caminho, versão, frase de contexto) mais o texto original.
  6. O pedaço completo é indexado para busca lexical e vetorial.

Pseudocódigo

schema ChunkContext { summary: string /* uma frase */ }

function index_document(doc):
    tree = parse_structure(doc)                         # 2. títulos, seções, tabelas
    for section in tree.leaves(max_tokens=800):         # 3. pela estrutura
        path = " > ".join(section.ancestors_titles())
        ctx = llm(CONTEXT_PROMPT, doc.summary, section.text, model="small") -> ChunkContext   # 4
        header = f"[{doc.title} | {path} | versão {doc.version}] {ctx.summary}"
        index.add(id=section.id, text=header + "\n" + section.text,
                  metadata={doc: doc.id, path: path, version: doc.version})               # 5 e 6

Cenários reais

  • Chat com a base de conhecimento interna. Políticas divididas por seção, com o caminho e a versão em cada trecho. A busca para “prazo de reembolso” não traz mais o prazo de autorização.
  • Assistente jurídico. Contratos divididos por cláusula e item, com o número da cláusula no cabeçalho, para citação exata. Em Mata v. Avianca (2023), advogados protocolaram decisões inventadas pelo ChatGPT. Um índice que guarda o número da cláusula não inventa a cláusula, mas só ajuda se o trecho certo for o que a busca devolve.
  • Manuais da Receita e da ANS. Normas brasileiras citam umas às outras (“nos termos do art. 12”). Sem o caminho no pedaço, o modelo trata a remissão como se fosse a regra.

Aplicabilidade

  • Documentos longos e estruturados (manuais, políticas, contratos, normas).
  • Trechos que, sozinhos, usam termos vagos (“o prazo”, “neste caso”).
  • Várias versões ou vários documentos parecidos no mesmo índice.
  • Respostas que precisam citar a seção exata.

Quando não usar

  • Documentos curtos, que cabem num pedaço só. Título e versão no começo bastam; cortar só cria fronteiras artificiais.
  • Conteúdo sem estrutura (transcrições de conversa). Divida por tema ou por janelas com sobreposição.
  • O material cabe inteiro no contexto. Veja RAG Desnecessário.
Checagem +20 XP de primeiraSua política de home office tem 2 páginas e cabe num pedaço só. Um colega quer cortar a cada 500 tokens e gerar frase de contexto. O que você faz?

Como implementar

  1. Olhe os documentos reais e identifique as marcas de estrutura (títulos numerados, cláusulas, tabelas).
  2. Escreva o parser por tipo de documento; converta PDF para um formato com estrutura antes.
  3. Defina o tamanho máximo e o que fazer com seções maiores (dividir por parágrafo, mantendo o título).
  4. Monte o cabeçalho com caminho e versão.
  5. Gere a frase de contexto com um modelo pequeno, com cache de prompt para o documento inteiro.
  6. Reindexe e compare recall e precisão no eval de busca.
  7. Use o caminho do cabeçalho nas citações da resposta.

Armadilhas comuns

  • Cortar por caracteres e chamar de estrutural. Se a fronteira ignora o título da tabela, você só mudou o tamanho.
  • Gerar a frase só com o pedaço. Sem o documento, o modelo escreve “este trecho fala de um prazo”, tão vago quanto o miolo. A chamada precisa ver o caminho e um resumo do documento.
  • Deixar versões revogadas no índice. O cabeçalho com “v6” não impede a busca de devolver o v6. Filtre por vigência na consulta e remova o documento antigo na reindexação.
  • Confiar na frase e relaxar o parser. Uma frase errada envenena o embedding. Amostra de pedaços na ingestão pega isso antes do usuário.

Variações

  • Só cabeçalho estrutural. Caminho e versão, sem frase gerada. Barato e já resolve boa parte.
  • Frase de contexto gerada. Melhora a busca para trechos vagos; custa uma chamada pequena por pedaço na indexação.
  • Pedaços em níveis. Pedaços pequenos para a busca, e a seção inteira (pai) enviada ao modelo quando o pequeno é encontrado.
  • Tabelas como pedaços próprios. Cada tabela vira um pedaço, com título e legenda, em formato de texto.

Comece pelo cabeçalho estrutural. Só ligue a frase gerada se o eval de confusão de assunto continuar falhando.

Custo, latência e falhas

Aspecto O que acontece Como aparece
Custo Na indexação: uma chamada pequena por pedaço, com cache do documento custo de ingestão por documento
Latência Nenhuma na consulta; o trabalho é feito ao indexar tempo de ingestão
Tamanho Cabeçalhos aumentam um pouco cada pedaço tokens por pedaço
Falha: parser errado Estrutura mal lida gera pedaços sem sentido pedaços com caminho vazio ou trocado
Falha: frase de contexto errada Situa o trecho no assunto errado resultados de busca fora do tema
Falha: versão antiga Documentos revogados continuam indexados respostas citando versão anterior

Como avaliar

  • Recall e precisão da busca antes e depois, no mesmo eval.
  • Confusão de assunto: perguntas parecidas sobre temas diferentes (“prazo de reembolso” e “prazo de autorização”).
  • Citações: a seção citada na resposta é a certa.
  • Fronteira de tabela e cláusula: casos em que o corte cego partia um bloco.
{ "question": "Qual o prazo de autorização de cirurgia eletiva?", "relevant": ["manual-v7#autorizacoes-4.1"], "not_relevant": ["manual-v7#reembolso-2.3"] }

Monte o par de perguntas gêmeas de propósito. Se o trecho relevante do reembolso não está no top 5 da pergunta de autorização, o cabeçalho está funcionando.

Checagem +20 XP de primeiraVocê gerou a frase de contexto lendo só o pedaço, sem o documento. No eval, a pergunta sobre reembolso ainda traz autorização. Por quê?

Em produção

  • Registro do caminho e da versão dos trechos usados em cada resposta.
  • Rotina de reindexação quando um documento muda, removendo versões antigas.
  • Amostra de pedaços para conferir parser e frases de contexto.
  • Alerta para taxa de caminho vazio na ingestão: parser quebrado depois de um novo tipo de PDF.

Segurança

O pedaço indexado pode carregar dados pessoais (nome de beneficiário, número de carteirinha). Na versão brasileira, isso cai na LGPD: o índice é mais um lugar onde esses dados moram. Não mande documentos com dados pessoais para o modelo da frase de contexto se a política da empresa não permitir. Nesses casos, fique no cabeçalho estrutural, gerado só com título e seção.

Prós e contras

Prós Contras
Cada pedaço é compreensível sozinho Parser por tipo de documento
Busca acerta o assunto certo Custo de indexação com a frase gerada
Citação com seção exata Pedaços um pouco maiores
Versões distinguíveis no índice Exige reindexar quando a estrutura muda

Relações

  • Padrão de destino: RAG. Chunking com contexto é a etapa de ingestão que faz a busca devolver o trecho certo.
  • Vizinhos: Busca Híbrida junta palavra-chave e embedding, o que aproveita bem o caminho no cabeçalho. Embeddings explica por que um trecho vago vira um vetor vago.
  • Quando não se aplica: RAG Desnecessário, se o material cabe inteiro.

Código

As duas abas dividem um documento pelos títulos e montam o cabeçalho com o caminho; a aba Python também gera a frase de contexto, com o documento inteiro em cache.

Python. Use quando os documentos estão em Markdown ou foram convertidos para um formato com títulos.

import re

import anthropic

client = anthropic.Anthropic()


def split_by_headings(markdown: str, max_chars: int = 3000) -> list[dict]:  # 2 e 3
    chunks, path = [], []
    for block in re.split(r"\n(?=#{1,4} )", markdown):
        m = re.match(r"(#{1,4}) (.+)", block)
        if m:
            level = len(m.group(1))
            path = path[: level - 1] + [m.group(2).strip()]
        for i in range(0, len(block), max_chars):  # seção grande: divide mantendo o caminho
            chunks.append({"path": " > ".join(path), "text": block[i : i + max_chars]})
    return chunks


def situate(doc_text: str, chunk_text: str) -> str:  # 4
    reply = client.messages.create(
        model="claude-haiku-4-5", max_tokens=100,
        system=[{"type": "text", "text": f"<documento>\n{doc_text}\n</documento>",
                 "cache_control": {"type": "ephemeral"}}],  # documento cacheado entre os pedaços
        messages=[{"role": "user", "content": "Em uma frase, diga de que assunto e caso trata este trecho, "
                                              f"para ajudar a encontrá-lo numa busca.\n<trecho>{chunk_text}</trecho>"}],
    )
    return reply.content[0].text.strip()


def index_document(doc: dict) -> list[dict]:
    out = []
    for c in split_by_headings(doc["markdown"]):
        header = f"[{doc['title']} | {c['path']} | versão {doc['version']}] {situate(doc['markdown'], c['text'])}"
        out.append({"id": f"{doc['id']}#{c['path']}", "text": f"{header}\n{c['text']}", "path": c["path"]})  # 5
    return out

O que você ganhou: pedaços com caminho, versão e frase de contexto, e o documento lido do cache a cada pedaço. O que você perdeu: o parser por títulos depende de o documento ter títulos; PDFs sem marcação precisam de conversão melhor antes.

TypeScript. Use quando você quer começar só com o cabeçalho estrutural, sem frase gerada.

type Chunk = { id: string; path: string; text: string };

export function chunkByHeadings(doc: { id: string; title: string; version: string; markdown: string }, maxChars = 3000): Chunk[] {
  const chunks: Chunk[] = [];
  let path: string[] = [];
  for (const block of doc.markdown.split(/\n(?=#{1,4} )/)) {
    const m = block.match(/^(#{1,4}) (.+)/);
    if (m) path = [...path.slice(0, m[1].length - 1), m[2].trim()];
    const p = path.join(" > ");
    const header = `[${doc.title} | ${p} | versão ${doc.version}]`; // 5. cabeçalho estrutural
    for (let i = 0; i < block.length; i += maxChars) {
      chunks.push({ id: `${doc.id}#${p}#${i}`, path: p, text: `${header}\n${block.slice(i, i + maxChars)}` });
    }
  }
  return chunks;
}

O que você ganhou: um passo de indexação sem custo de modelo, que já resolve a maior parte da perda de contexto. O que você perdeu: trechos muito vagos continuam difíceis de achar; acrescente a frase gerada se o eval mostrar falhas.

Teste seu entendimento

Checagem +20 XP de primeiraUma tabela de prazos perdeu o cabeçalho: o título ficou no pedaço anterior e as linhas no seguinte. O bot cita números sem dizer de qual prazo. Qual é o diagnóstico?

Checagem +20 XP de primeiraO índice tem o manual v6 (revogado) e o v7. Os cabeçalhos trazem a versão. Mesmo assim a resposta cita a regra antiga. O que falta?

Checagem +20 XP de primeiraO parser de PDF devolveu caminho vazio em 30% dos pedaços. O eval de busca piorou depois que você ligou o chunking com contexto. Qual é o próximo passo?

Perguntas rápidas

O que é Chunking com Contexto?

Chunking com Contexto (Contextual Chunking) é um padrão de projeto de IA para apps com LLM e agentes, do grupo Contexto e conhecimento. Cortar documentos respeitando a estrutura e anexar a cada pedaço o contexto de onde ele veio.

Onde Chunking com Contexto aparece na prática?

Nos cenários reais Chat com a base de conhecimento interna e Assistente jurídico.

Para ir além

Relações

Aparece nas trilhas 1
Buscar