RAG em Documentos Longos: Desequilíbrio entre Contexto Global e Precisão Local
Estou trabalhando na implementação de um sistema RAG (Retrieval-Augmented Generation) para análise de documentos técnicos longos e extremamente estruturados (pensem em contratos, manuais operacionais ou editais, cheios de seções, subseções, incisos, etc.).
Estou enfrentando um problema clássico de "cobertor curto" na arquitetura de busca híbrida e gostaria de compartilhar o cenário e ouvir se alguém já passou por isso.
Atualmente, meu pipeline de ingestão e busca funciona assim:
-
Segmentação (Chunking) Hierárquica: Não uso apenas tamanho fixo. Eu parseio o documento respeitando a estrutura (Títulos, Capítulos, Seções). -
Enriquecimento: Para evitar que um parágrafo solto perca o sentido, eu injeto o "caminho" dele nos metadados e, às vezes, no próprio texto. -
Exemplo: Um chunk de texto que diz apenas "Cobre danos materiais" recebe o contexto Documento X > Seção Responsabilidade Civil > Danos a Terceiros. -
Reranking (RRF): Uso Reciprocal Rank Fusion para combinar as duas listas.Busca Híbrida (Hybrid Search): - Dense: Embeddings (usando modelos multilingues) para capturar o sentido semântico. - Sparse (BM25): Para garantir que palavras-chave específicas e IDs técnicos não sejam perdidos.
O "Envenenamento" por Keywords Genéricas
O problema surge quando o usuário faz uma pergunta que contém o nome da Entidade principal do documento (ex: o nome da empresa, do fornecedor ou da marca do produto).
Vamos supor que o usuário pergunte: "A empresa Acme oferece suporte técnico acima de para determinado produto?"
Intenção: O usuário quer saber sobre a regra de um produto específico do suporte técnico.
Resultado Esperado: Um chunk na seção de "Serviços de Assistência" que fala sobre esse produto específico.
Resultado Real (Fracasso): O sistema me retorna o chunk "CANAIS DE ATENDIMENTO ACME" (SAC/Telefone) ou outros.
Por que isso acontece? O BM25 (busca esparsa) vê a palavra "Acme" na query e na seção de cabeçalho ("Canais de Atendimento Acme"). O match é exato e muito forte. Já o chunk correto ("Assitência e manutenção para o produto específico...") muitas vezes não repete a palavra "Acme", pois está implícito no contexto do documento.
Na fusão (RRF), o score altíssimo do BM25 para o cabeçalho irrelevante acaba "ganhando" do score mediano do vetor denso para o parágrafo correto.
Estou sofrendo com uma espécie de "Cobertor Curto"
Aqui entra a dor de cabeça da engenharia:
- Tentativa 1: Enriquecer todos os chunks com o nome da Entidade.
- Coloquei "Manual Acme" no início de todo chunk de texto antes de vetorizar/indexar.
- Agora a palavra "Acme" deixa de ser discriminatória (o IDF do BM25 cai drasticamente), mas eu poluo o contexto semântico e aumento o custo de tokens.
- Tentativa 2: Busca Hierárquica (Drill Down).
- Tentar primeiro achar o "Tópico" (ex: Assistência) e só depois buscar os detalhes.
- Aumenta a latência e complexidade. Se o primeiro passo falha, o sistema inteiro falha.
- Tentativa 3: Limpeza de Query (Query Pre-processing).
- Usar um LLM para remover a entidade da query antes da busca esparsa (transformar "A Acme cobre X?" em "cobertura de X").
- Às vezes a entidade é importante (ex: documentos comparativos onde eu tenho Acme e Beta no mesmo banco).
O que estou testando agora (SAC & HSEQ)
Lendo alguns papers recentes (Towards Reliable Retrieval in RAG Systems e Hierarchical Sequence Iteration), estou caminhando para uma solução de Summary-Augmented Chunking (SAC).
A abordagem que notei nos meus logs de teste (tenho um arquivo JSON gigante com testes de busca_simples e comparativas) é que, ironicamente, chunks menores com contexto injetado via metadados funcionam melhor na busca densa, mas a busca esparsa continua sendo "sequestrada" por cabeçalhos que repetem keywords da query.
Alguém já lidou com esse viés de "Headers Genéricos" vencendo "Conteúdo Específico" em buscas híbridas? Como vocês balanceiam o peso do BM25 quando os documentos são muito repetitivos em termos de entidades nomeadas?