RAG para chatbots corporativos: o que o benchmark não revela

A maioria dos tutoriais de RAG usa um dataset do Wikipedia ou um PDF de manual técnico bem formatado. O chatbot responde certo, o RAGAS marca acima de 0,85 e o desenvolvedor vai para produção — onde encontra um acervo de 4.000 PDFs escaneados, planilhas exportadas como HTML, e três versões do mesmo contrato com nomenclatura conflitante.

É aí que o pipeline quebra. E quebra silenciosamente: o LLM ainda gera respostas coerentes, mas as respostas são baseadas em fragmentos errados ou incompletos. O modelo não alucina de forma óbvia — ele “confabula” com texto de baixa qualidade como âncora.

Este artigo documenta onde cada etapa do pipeline de ingestão degrada a qualidade, como medir essa degradação com RAGAS 0.1.x em Python, e quais padrões de mitigação têm evidência empírica. Os benchmarks geralmente não testam isso porque usam dados limpos. A documentação corporativa real não é limpa.

TL;DR

  • RAG falha em produção com documentação corporativa não por limitação do modelo, mas por degradação acumulada no pipeline de ingestão — OCR ruidoso, chunking inadequado e versões conflitantes de documentos corrompem o retrieval antes da primeira pergunta.
  • As métricas padrão do RAGAS medem qualidade após a ingestão: um pipeline que atinge context precision de 0,85 em dataset limpo pode cair para a faixa de 0,50–0,65 em produção com PDFs escaneados — estimativa de campo baseada em relatos de times de implementação, não valor obtido diretamente do OHRBench (Zhang et al., 2024), que mede degradação em cascata via F1-score e LCS@1 de retrieval, não em métricas RAGAS.
  • Hierarchical chunking, hybrid search (BM25 + dense retrieval) e metadados de vigência de documento são os três padrões com maior suporte empírico para fechar o gap entre benchmark e produção.


O que o benchmark limpo não testa

O RAGAS (Es et al., 2023, acesso: 2026-05-12) se tornou o padrão de avaliação de pipelines RAG. Ele mede quatro dimensões centrais: faithfulness (o quanto a resposta é fiel ao contexto recuperado), answer relevancy (o quanto a resposta responde a pergunta), context recall e context precision. São métricas úteis — mas só medem a qualidade do pipeline depois que o texto está indexado.

O problema está antes: na ingestão.

Zhang et al. (2024) publicaram o OHRBench — um benchmark que testa especificamente o impacto de erros de OCR no pipeline RAG completo. O dataset contém 8.561 imagens e 8.498 pares Q&A, com documentos de domínios como finanças, direito e academia (arXiv:2412.02592, aceito ICCV 2025, acesso: 2026-05-12). O resultado central: cada etapa da cadeia OCR → chunking → embedding → retrieval amplifica erros das etapas anteriores. O benchmark mede essa degradação via F1-score e LCS@1 — não via métricas RAGAS, pois o OHRBench avalia o pipeline sem depender de LLM-árbitro. Os resultados quantitativos são detalhados na seção seguinte.

Benchmarks padrão de RAG não capturam isso. Eles assumem que o texto já está em formato limpo. Quando o seu pipeline recebe um contrato de 200 páginas escaneado em resolução 150 DPI com carimbos sobre o texto, os scores do RAGAS não preveem o comportamento real.

O paper da NVIDIA — “FACTS About Building RAG-based Chatbots” (Akkiraju et al., 2024, arXiv:2407.07858, acesso: 2026-05-12) — chega a conclusão semelhante por outra via. O framework FACTS (Freshness, Architectures, Cost, Testing, Security) identifica Testing como o segundo maior desafio relatado por times que implementaram RAG em produção. A queixa consistente: os testes passam no conjunto sintético e falham em produção com documentação real.


A cadeia de degradação

O pipeline RAG tem quatro etapas de ingestão antes de qualquer LLM: extração de texto, chunking, embedding e indexação. Cada uma introduz ruído que se propaga para o retrieval.

Extração e OCR

Documentos corporativos chegam em múltiplos formatos. PDFs gerados digitalmente (exportados do Word ou Google Docs) têm estrutura lógica preservada — cabeçalhos, tabelas, listas. PDFs escaneados são imagens: sem estrutura, sem camada de texto. O OCR (Optical Character Recognition) extrai o texto da imagem, mas introduz erros que dependem da qualidade do scan, da fonte usada e da presença de elementos gráficos sobre o texto.

Tabelas são o caso mais crítico. Uma tabela de preços em Word, quando exportada como PDF e escaneada, chega ao OCR como uma sequência de números sem contexto de coluna ou linha. O embedding dessa sequência não captura a relação entre produto, preço e prazo.

O OHRBench quantifica isso: em perguntas sobre tabelas com OCR ruidoso, perturbações de nível moderado a severo reduzem o F1-score em até 50% — mesmo em modelos como GPT-4o. No pipeline geral, o melhor parser testado (MinerU) ainda causa queda de ~17% no LCS@1 de retrieval em relação ao baseline com texto limpo (Zhang et al., 2024). A degradação não aparece nos benchmarks convencionais porque esses datasets raramente incluem tabelas escaneadas.

Chunking para conversa

O tamanho do chunk é a decisão de design com maior impacto no retrieval — e a menos discutida em tutoriais básicos.

Chunks de 1.024 tokens são comuns em tutoriais porque maximizam o contexto fornecido ao LLM. Mas para chatbots conversacionais, chunks grandes criam dois problemas:

  1. Recuperação imprecisa: um chunk de 1.024 tokens pode conter a resposta para três perguntas diferentes. O retrieval traz o chunk quando qualquer das três é feita, mas o LLM recebe contexto irrelevante junto com o relevante.
  2. Latência adicional em voice agents: em agentes de voz (diferente de chat), cada token no contexto adiciona latência ao pipeline de geração. Um contexto de 4.096 tokens em 4 chunks de 1.024 gera resposta mais lenta que 4 chunks de 256 tokens com a mesma informação relevante.

Para chatbots de suporte corporativo, chunks entre 200-300 tokens com overlap de 10-15% tendem a entregar melhor precision sem sacrificar recall — trata-se de heurística prática amplamente adotada em implementações de suporte, não resultado de benchmark controlado. Experimente esse range como ponto de partida e ajuste com avaliação RAGAS no seu corpus específico. Esse range é específico para conversa — documentação técnica para busca pode usar chunks maiores.

Embeddings genéricos e o problema de domínio especializado

O text-embedding-3-small da OpenAI e o all-MiniLM-L6-v2 do Sentence Transformers são excelentes para texto genérico. Para documentação corporativa com vocabulário específico — siglas internas, nomes de produto, códigos de processo — eles produzem embeddings que colocam termos semanticamente próximos no espaço genérico, não no espaço do domínio.

Um exemplo: “ordem de serviço OS-4471” e “pedido de compra PC-4471” têm representações vetoriais próximas em um embedding genérico porque compartilham estrutura sintática. Mas para o chatbot de suporte interno, são documentos completamente diferentes. O retrieval por similaridade cosine vai confundir os dois.

A mitigação não exige fine-tuning de embedding model. Metadados estruturados nos nós — tipo de documento, departamento, data de vigência — permitem filtros pré-retrieval que eliminam documentos fora do escopo antes da comparação vetorial. Isso reduz ruído sem custo de treinamento.

Retrieval e o problema de namespace

Documentação corporativa tipicamente tem versões conflitantes do mesmo documento. Política de férias v1.2, v1.3, v2.0 — todas no acervo. O retrieval por similaridade semântica não distingue versões: recupera todos os fragmentos semanticamente similares, independente de qual é o vigente.

O resultado prático: o LLM recebe contexto contraditório. A política v1.2 diz 15 dias de férias; a v2.0 diz 20. O modelo tende a produzir uma síntese das duas ou escolher uma aleatoriamente.

A solução mais simples — e a menos implementada — é metadado de vigência: um campo is_current: true/false em cada documento, atualizado no processo de upload. O VectorStoreIndex do LlamaIndex suporta filtros de metadado no as_query_engine() via MetadataFilters. Documentos desatualizados permanecem no índice para auditoria, mas são excluídos do retrieval ativo.


Implementação com LlamaIndex 0.10.x

O pipeline abaixo demonstra ingestão com chunking controlado, metadados de rastreabilidade e query com recuperação de fontes. Usa LlamaIndex Core 0.10.68.

# Python 3.11, llama-index-core 0.10.68, llama-index-embeddings-openai 0.1.x
# llama-index-llms-openai 0.1.x, llama-index-readers-file 0.1.x

import os
from llama_index.core import VectorStoreIndex, Settings
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.extractors import KeywordExtractor
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
from llama_index.readers.file import PDFReader

# Configuração global — variáveis de ambiente, nunca hardcode
# export OPENAI_API_KEY=sk-...
Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0)
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")

# Pipeline de ingestão com chunking controlado para conversa
# chunk_size=256 otimizado para chatbot; ajuste para 512 se base é doc técnico
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(
            chunk_size=256,
            chunk_overlap=32,        # overlap de ~12% para contexto entre chunks
            paragraph_separator="""

""",
        ),
        KeywordExtractor(keywords=5),  # metadados para rastreabilidade de retrieval
        Settings.embed_model,
    ]
)

# Leitura de PDFs — PDFReader extrai texto da camada digital
# Para PDFs escaneados, substitua por pipeline com pytesseract ou Azure Document AI
reader = PDFReader()
documents = []
docs_path = "./docs/politicas/"

for pdf_file in os.listdir(docs_path):
    if pdf_file.endswith(".pdf"):
        doc = reader.load_data(file=f"{docs_path}{pdf_file}")
        # Adiciona metadado de versão — crítico para evitar conflito entre versões
        for d in doc:
            d.metadata["source_file"] = pdf_file
            d.metadata["doc_version"] = pdf_file.split("_v")[-1].replace(".pdf", "")
        documents.extend(doc)

nodes = pipeline.run(documents=documents)
index = VectorStoreIndex(nodes)

# Query engine com 4 chunks — suficiente para a maioria das perguntas de suporte
query_engine = index.as_query_engine(
    similarity_top_k=4,
    response_mode="compact",  # "compact" reduz tokens na resposta; use "tree_summarize" para docs longos
)

response = query_engine.query("Qual o prazo de aviso prévio para rescisão?")
print(response.response)

print("""
--- Fontes recuperadas ---""")
for node in response.source_nodes:
    print(
        f"Arquivo: {node.node.metadata.get('source_file', 'desconhecido')} "
        f"| Versão: {node.node.metadata.get('doc_version', '?')} "
        f"| Score: {node.score:.3f}"
    )
    # Se versões diferentes aparecerem com score similar, há conflito de documentação

O ponto crítico no código acima: o metadado doc_version nos nós. Quando a query retorna fragmentos de v1.2 e v2.0 com scores próximos (diferença < 0,05), o pipeline está recuperando informação conflitante. Isso precisa ser tratado no nível de ingestão — não no prompt do LLM.


Avaliando com RAGAS 0.1.x

RAGAS (Es et al., 2023) automatiza a avaliação usando um LLM como árbitro. O conjunto de avaliação precisa ter pelo menos 20 amostras para resultados estatisticamente estáveis; abaixo disso, a variância é alta demais para decisões de design.

# Python 3.11, ragas 0.1.21, datasets 2.20.x, openai 1.x
# export OPENAI_API_KEY=sk-...   <- ragas 0.1.21 usa gpt-4o-mini por padrão

from ragas import evaluate
from ragas.metrics import (
    faithfulness,
    answer_relevancy,
    context_recall,
    context_precision,
)
from datasets import Dataset

# Conjunto de avaliação — construído a partir de perguntas reais de usuários
# ground_truth é a resposta correta segundo especialista humano ou documento fonte
eval_samples = {
    "question": [
        "Qual o prazo de aviso prévio para rescisão sem justa causa?",
        "Como solicitar reembolso de despesas de viagem?",
        "Qual o limite de horas extras mensais permitido?",
    ],
    "answer": [
        # Respostas geradas pelo seu pipeline RAG
        "O prazo de aviso prévio é de 30 dias...",
        "O reembolso deve ser solicitado em até 5 dias úteis...",
        "O limite é de 44 horas extras mensais...",
    ],
    "contexts": [
        # Contextos recuperados pelo retrieval (lista de strings por pergunta)
        ["Política de Rescisão v2.0: aviso prévio mínimo de 30 dias corridos..."],
        ["Política de Viagens v1.3: reembolso em até 5 dias úteis após retorno..."],
        ["CLT Art. 59: banco de horas limitado a 44h mensais..."],
    ],
    "ground_truth": [
        "30 dias corridos",
        "5 dias úteis após o retorno da viagem",
        "44 horas mensais",
    ],
}

dataset = Dataset.from_dict(eval_samples)

result = evaluate(
    dataset=dataset,
    metrics=[faithfulness, answer_relevancy, context_recall, context_precision],
)

print(result)
# Saída esperada em pipeline bem configurado com docs limpos:
# faithfulness          0.91
# answer_relevancy      0.88
# context_recall        0.74   <- gargalo típico em docs corporativos com versões conflitantes
# context_precision     0.67   <- chunking inadequado reduz este score

O context_recall é o indicador diagnóstico mais importante para identificar problemas de ingestão. Um score abaixo de 0,70 — limiar prático baseado em experiência de campo, não padrão normativo do RAGAS — indica que o retrieval não está encontrando os fragmentos corretos: tipicamente chunking muito grande, embeddings inadequados para o domínio, ou conflito de versões no índice.

O context_precision abaixo de 0,65 — outro limiar prático; calibre no seu corpus antes de tomar decisões de design — aponta para chunking que traz texto irrelevante junto com o relevante: sinal para reduzir chunk_size ou aumentar similarity_top_k com filtro posterior.

O que o RAGAS não mede: coerência conversacional. Um pipeline pode ter faithfulness de 0,93 e ainda gerar respostas que funcionam em pergunta isolada mas quebram em conversa de 5+ turnos — porque o contexto de turns anteriores não está sendo considerado no retrieval. Para medir isso, você precisa de avaliação com histórico de conversa, que o RAGAS padrão não suporta.


LlamaIndex vs. LangChain: comparativo direto

CritérioLlamaIndex 0.10.xLangChain 0.2.x
Foco principalRAG e indexação de dadosChains e agentes genéricos
Abstração de ingestãoAlta — IngestionPipeline cobre transformações sequenciaisMédia — DocumentLoader + splitter separados
Suporte a metadados de nóNativo — KeywordExtractor, TitleExtractor, customManual — requer wrapper em Document.metadata
Integração com RAGASDireta — exporta NodeWithScore compatívelDireta — exporta Document compatível
Curva de aprendizadoModerada — conceito de Node é centralAlta — múltiplas abstrações (Chain, Runnable, LCEL)
Breaking changesFrequentes na 0.10.x (modularização em andamento)Frequentes na 0.2.x (LCEL vs Chain legado)
Último commit ativoRepositório llama_index — ativo (verificado 2026-05-12)Repositório langchain — ativo (verificado 2026-05-12)
Caso de uso preferencialRAG estruturado com múltiplas fontes de dadosAgentes complexos com tool use e RAG como um dos steps
Produção com docs corporativosMais direto — SimpleDirectoryReader + IngestionPipelineMais flexível — exige mais configuração manual

Quando escolher LlamaIndex: o core do sistema é RAG sobre documentação estruturada. O time prefere abstrações de alto nível para ingestão e query.

Quando escolher LangChain: RAG é um step dentro de um agente maior com múltiplas tools, memória de sessão e orquestração complexa. Ou quando o time já usa LangGraph para state machine de fluxo de atendimento.

Misturar os dois é possível — LlamaIndex como retriever, LangChain como orquestrador — mas aumenta a superfície de manutenção. Só vale se os dois frameworks oferecem funcionalidades distintas que o projeto genuinamente precisa.


Quando não usar RAG

RAG não é a solução padrão para todo chatbot com base de conhecimento. Três cenários onde outras abordagens são mais adequadas:

Base de conhecimento pequena e estável. Se o acervo tem menos de 50 documentos e raramente é atualizado, colocar todo o conteúdo no contexto via long context window (GPT-4o aceita 128k tokens, Gemini 1.5 Pro até 1M) é mais simples e mais confiável. RAG adiciona latência de retrieval, complexidade de manutenção do índice, e pontos de falha na ingestão — para 50 documentos, o custo não se justifica.

Dados com atualização em tempo real. RAG funciona sobre um índice estático ou com atualização periódica. Se o chatbot precisa responder sobre dados que mudam por minuto (estoque, cotação, status de pedido), RAG com índice desatualizado é pior do que uma tool call direta à API do sistema transacional. Use function calling para esses casos.

Domínios de alta consequência sem supervisão humana. Um chatbot RAG que responde sozinho sobre dosagens de medicamentos, obrigações contratuais específicas ou decisões de crédito está operando em território que exige validação especializada a cada resposta. O modelo pode estar fiel ao contexto recuperado (faithfulness alto) e ainda assim o contexto recuperado ser insuficiente para a decisão. Nesses casos, RAG pode ser parte do pipeline, mas não o ponto final de decisão.

Limitação de latência em voice agents. Em agentes de voz, o retrieval adiciona latência ao pipeline — tipicamente entre 200 e 500ms para índices de 10k a 500k vetores em vector stores gerenciados como Pinecone ou Weaviate em infraestrutura de nuvem pública, com base nas latências de P95 reportadas nas documentações oficiais desses serviços (Pinecone docs, acesso: 2026-05-12; Weaviate docs, acesso: 2026-05-12). Em índices menores com cache de embedding, latências abaixo de 100ms são alcançáveis; em índices maiores sem otimização, pode ultrapassar 1s. Para fluxos de atendimento onde a resposta precisa chegar em menos de 800ms total (STT + LLM + TTS), RAG pode não ser viável sem cache de embedding ou pré-computação de perguntas frequentes.


O que funciona: padrões com evidência

Quatro padrões com resultados documentados em implementações de produção:

1. Hierarchical chunking. Dois níveis de granularidade: chunks grandes (512 tokens) para contexto semântico, chunks pequenos (128 tokens) para precision. O retrieval usa os grandes para ranqueamento e os pequenos para montagem do contexto final. LlamaIndex implementa isso com HierarchicalNodeParser (0.10.x). Ganho típico em context precision reportado pela equipe LlamaIndex em casos de uso documentados (Advanced RAG: Cheat Sheet and Recipes, acesso: 2026-05-12): +0,08 a +0,15 sobre chunking plano em documentos técnicos longos — a magnitude varia com tipo de documento e tamanho do índice.

2. Hybrid search: BM25 + dense retrieval. BM25 (busca por termos exatos) combinado com embeddings densos captura tanto correspondências semânticas quanto termos técnicos específicos (números de artigo, código de produto, CPF parcial). Para documentação corporativa com muito jargão interno, BM25 puro às vezes supera embeddings genéricos. LlamaIndex suporta isso com BM25Retriever combinado com QueryFusionRetriever.

3. Pré-processamento antes do OCR. Normalização de resolução (mínimo 300 DPI), remoção de carimbos e marcas d’água, e conversão de tabelas para formato estruturado antes da ingestão. Ferramentas como Azure Document Intelligence ou AWS Textract oferecem extração de tabela com estrutura preservada — superior ao OCR genérico para planilhas. O custo adicional (API paga por página) é justificado quando tabelas são a fonte crítica de informação.

4. Avaliação contínua em produção. A métrica mais útil em produção não é o RAGAS — é o containment rate: porcentagem de perguntas respondidas sem escalação para humano. Um pipeline com RAGAS alto e containment rate baixo indica que o eval set não representa as perguntas reais. Monitore os casos de escalação, extraia perguntas que o chatbot não respondeu bem, e incorpore ao eval set iterativamente.

Sobre custo de avaliação: cada chamada ao RAGAS usa o LLM configurado como árbitro — tipicamente GPT-4o ou GPT-4o-mini. Em 100 amostras com 4 métricas, são aproximadamente 400 chamadas ao LLM. Com GPT-4o-mini a US$ 0,15/1M tokens de input (preço de tabela, verificado em openai.com em 2026-05-12), o custo por ciclo de avaliação fica abaixo de US$ 0,50 para a maioria dos casos. Isso torna viável rodar avaliação a cada deploy, não só na fase de desenvolvimento.

Versão de documentação no eval set. Quando você atualiza documentos no acervo, o eval set precisa ser atualizado junto. Perguntas com ground_truth baseado na política v1.2 vão produzir falsos negativos depois que a v2.0 entrar no índice — o pipeline passou a responder corretamente (v2.0) mas o evaluador marca como errado porque espera a resposta v1.2. Versione o eval set junto com a documentação.


Próximos passos

Um pipeline RAG que funciona em dataset limpo e falha em produção tem o problema na ingestão, não no modelo. O caminho de melhoria começa pelo diagnóstico: meça context_recall e context_precision separadamente para cada categoria de documento no seu acervo. PDFs digitais e PDFs escaneados tipicamente têm scores muito diferentes — e identificar isso separa o problema em subproblemas tratáveis.

O próximo gargalo após ingestão é o gerenciamento de contexto em conversas longas: como manter coerência quando o usuário faz cinco perguntas sequenciais sobre o mesmo contrato. Esse tema — memória short-term vs. long-term em chatbots de suporte — é o próximo artigo do pilar Conversational AI.

O código completo deste artigo, incluindo script de pré-processamento para PDFs escaneados e exemplos de eval set com casos de borda, será disponibilizado no repositório da Mestre no GitHub junto à publicação deste artigo.


Júlia Tanaka é pesquisadora de Conversational AI na Mestre — Academia Brasileira de Voice AI. Foco em design de prompts para voz, RAG aplicado a suporte corporativo e avaliação de agentes conversacionais.


Este artigo foi pesquisado e escrito por Júlia Tanaka com assistência de ferramentas de IA. Todo código foi revisado para validação técnica antes da publicação.


Gostou? Receba mais conteúdo técnico de Voice AI

Tutoriais aprofundados, comparativos de plataformas e guias práticos — toda semana para profissionais de Voice AI em português.

Newsletter em breve — inscrições abrirão em breve.