RAG de produção: os padrões que separam demo de sistema
- ⬜🗄️ Serviços que somam ao Bedrock VI: dados, retrieval e conhecimento(AWS Bedrock — GenAI em Produção)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Todo RAG funciona na demo. Você indexa 20 PDFs, faz três perguntas, as três acertam, e o time acha que o problema está resolvido. Aí sobe para 200 mil documentos e a taxa de acerto despenca — sem que nada no código tenha mudado. O motivo é que a demo escondeu o problema real: RAG não é um problema de geração, é um problema de recuperação. Se o trecho certo não entrou no contexto, nenhum modelo do mundo salva a resposta. Este módulo cobre os padrões que separam demo de sistema — e o custo de cada um, porque metade das decisões de RAG em produção é decisão de custo disfarçada de decisão de qualidade.
Onde o RAG realmente quebra
Antes de escolher técnica, aprenda a diagnosticar. Quase todo erro de RAG cai em um destes seis pontos — e cada um tem um conserto diferente. Tratar tudo como "o modelo alucinou" é o que faz times trocarem de modelo quando o problema estava no chunking.
| Onde quebra | Sintoma | Conserto |
|---|---|---|
| Conteúdo ausente | Responde errado com confiança sobre algo que não está na base | Curadoria: a resposta não existe. Detecte e diga que não sabe |
| Chunking | A resposta está no documento mas cortada ao meio entre dois pedaços | Estratégia de chunk por tipo de documento; hierárquico ou overlap maior |
| Recuperação (top-k) | O trecho certo existe e está indexado, mas não veio nos k primeiros | Busca híbrida, reranking, query rewriting |
| Ranking | O trecho certo veio, mas em 8º lugar, e o modelo se ancorou no 1º | Reranking — é exatamente o problema que ele resolve |
| Extração | O trecho certo estava no contexto e o modelo não usou | Prompt e posição: o meio do contexto é onde a atenção se perde |
| Fidelidade | O modelo misturou o contexto com conhecimento próprio | Grounding no guardrail, citação obrigatória, instrução explícita |
Meça os dois lados separadamente
A pergunta "o RAG está bom?" não tem resposta. As perguntas úteis são duas e independentes: o trecho certo entrou no contexto (retrieval) e, dado que entrou, a resposta o usou corretamente (geração). Times que medem só a resposta final ficam otimizando o prompt quando o problema estava na busca — e gastam meses assim.
O pipeline completo, com os pontos de decisão
Nem todo sistema precisa dos dez passos. Mas todo sistema toma uma decisão em cada um deles — mesmo que por omissão. "Não fazemos reranking" é uma decisão; "usamos o chunk padrão de 300 tokens" é uma decisão. O que diferencia o profissional é tomá-las de propósito, com número.
- → contexto + chunk vão juntos para o índice
- → consulta autocontida
- → candidatos: busca larga é barata
- → top-k pequeno: o contexto é o que custa
- Armazenamento
- IA e machine learning
- Analytics
- Compute
- Gestão e governança
Buscar largo e entregar estreito: 30 a 50 candidatos saem baratos do índice, e o reranker escolhe os 3 a 5 que vão para o contexto — o que você paga em todo request.
- Curar e normalizar. Arquive o obsoleto antes de indexar e passe o escaneado por OCR. Documento contraditório indexado vira resposta contraditória com citação.
- Contextualizar e indexar. Um LLM situa cada chunk no documento antes do embedding — viável porque o documento fica no prefixo cacheado e roda em batch.
- Transformar a consulta. "E o segundo caso?" vira pergunta autocontida usando o histórico. Obrigatório em qualquer chat multi-turno.
- Buscar largo. Busca híbrida (vetorial + léxica) com filtro de metadados aplicando a permissão. Traga mais candidatos do que vai usar.
- Entregar estreito. O reranker corta para 3 a 5 trechos. Melhora a resposta e reduz o custo ao mesmo tempo.
- Gerar e medir. Citação obrigatória, grounding no guardrail, e avaliação separando retrieval de geração a cada mudança.
Chunking: a decisão que ninguém revisita
Chunk é o átomo do seu sistema: é a menor unidade que pode ser recuperada. Chunk grande demais traz ruído junto com o sinal e infla o contexto (custo). Chunk pequeno demais perde a referência e corta a resposta ao meio. Não existe tamanho universal — existe estratégia por tipo de documento
| Estratégia | Como funciona | Melhor para |
|---|---|---|
| Tamanho fixo com overlap | Corta a cada N tokens com sobreposição entre pedaços vizinhos | Prosa homogênea; é o padrão razoável para começar |
| Hierárquico (pai-filho) | Indexa o pedaço pequeno, mas entrega o pedaço pai maior ao modelo | Manual, norma, documentação técnica — precisão na busca com contexto na resposta |
| Semântico | Corta onde o assunto muda, medindo distância entre sentenças | Texto sem estrutura clara, transcrições, atas |
| Por estrutura | Respeita seções, cláusulas, tabelas do próprio documento | Contrato, laudo, formulário — onde a estrutura carrega significado |
| Documento inteiro | Sem corte; o documento todo é uma unidade | Documentos curtos e autocontidos, como FAQ e política de uma página |
O padrão pai-filho é o mais subestimado
Você quer duas coisas conflitantes: pedaço pequeno para a busca ser precisa e pedaço grande para o modelo ter contexto. O hierárquico entrega as duas — indexa o filho pequeno e devolve o pai. Na prática é a mudança de chunking com maior ganho por hora de trabalho em base técnica ou normativa, e não exige nenhuma técnica exótica.
Trocar de chunking custa reindexação inteira
Mudar a estratégia significa recalcular embeddings de tudo. Numa base grande isso é tempo e dinheiro — e é o motivo pelo qual times deixam o chunking ruim de pé por meses. Duas defesas: teste as estratégias num subconjunto de alguns milhares de documentos antes de indexar a base toda, e gere embeddings de reindexação em modo batch, que costuma sair pela metade do preço.
Busca híbrida: por que só vetorial falha em empresa
Busca vetorial encontra por significado — é excelente quando o usuário descreve o conceito com outras palavras. E é justamente por isso que ela falha no vocabulário corporativo: códigos de produto, número de norma, sigla interna, nome de campo de sistema, SKU. Esses termos não têm sinônimo semântico; eles têm que bater literalmente. Busca léxica (BM25) faz isso e é ruim em paráfrase. A resposta profissional é usar as duas e fundir os resultados.
Como as duas listas viram uma
A fusão mais usada em produção é por posição, não por nota: cada documento ganha pontos com base em onde ficou em cada lista, e as posições se somam. Isso evita o problema de comparar escores em escalas diferentes — a nota de similaridade de cosseno e o escore BM25 não são comparáveis entre si. As Knowledge Bases do Bedrock expõem o modo híbrido como configuração de busca em vector stores que suportam os dois índices; confirme o suporte do store que você escolheu.
Contextual retrieval: a técnica com melhor retorno
O problema estrutural do chunk é que ele perde o contexto de onde veio. Um pedaço que diz "a receita cresceu 3% no trimestre" não diz de qual empresa, de qual ano, de qual segmento — e por isso é difícil de recuperar corretamente. Contextual retrieval conserta isso na ingestão: antes de gerar o embedding, um LLM escreve uma ou duas frases situando aquele pedaço dentro do documento inteiro, e esse prefixo vai junto para o índice.
import boto3
br = boto3.client("bedrock-runtime")
PROMPT_CONTEXTO = (
"Escreva 1-2 frases curtas situando o trecho abaixo dentro do documento, "
"para melhorar a recuperacao em busca. Cite entidade, periodo e secao "
"quando existirem. Responda SO com o contexto, sem preambulo."
)
def contextualizar(doc_inteiro: str, chunk: str, model_id: str) -> str:
resp = br.converse(
modelId=model_id, # tier volume: e uma tarefa barata
system=[{"text": PROMPT_CONTEXTO}],
messages=[{
"role": "user",
"content": [
# O documento inteiro vai PRIMEIRO e e identico para todos os
# chunks dele: e exatamente o formato que o cache foi feito para.
{"text": f"<documento>\n{doc_inteiro}\n</documento>"},
{"cachePoint": {"type": "default"}},
{"text": f"<trecho>\n{chunk}\n</trecho>"},
],
}],
inferenceConfig={"maxTokens": 120}, # contexto curto, saida barata
)
return resp["output"]["message"]["content"][0]["text"].strip()
def indexar(doc_inteiro: str, chunks: list[str], model_id: str):
# O primeiro chunk paga o documento inteiro; os demais leem do cache.
# Sem cache, um documento de 100 chunks pagaria o documento 100 vezes.
for chunk in chunks:
ctx = contextualizar(doc_inteiro, chunk, model_id)
yield f"{ctx}\n\n{chunk}" # e ISTO que vai para o embedding e o BM25
É a técnica que o prompt caching viabilizou
Sem cache, contextualizar seria proibitivo: cada chunk exigiria reenviar o documento inteiro. Com cache de prefixo, o documento é pago uma vez e relido a uma fração do preço por todos os chunks seguintes — e o processamento pode rodar em batch, com desconto adicional. É o melhor exemplo de como uma alavanca de custo destrava uma técnica de qualidade que antes não fechava a conta.
A combinação que costuma resolver
Contextual retrieval + busca híbrida + reranking é a pilha que, na prática, resolve a maior parte dos casos corporativos difíceis. Cada peça ataca uma falha diferente: o contexto conserta o chunk órfão, o híbrido conserta o identificador literal, o reranking conserta a ordem. Aplique nessa sequência e meça a cada passo, para saber qual delas pagou.
Transformação de consulta e reranking
A pergunta que o usuário digita quase nunca é a melhor query de busca. Ela vem curta, com pronome ("e o segundo caso?"), com erro, ou juntando duas perguntas. Três técnicas resolvem isso, com custos bem diferentes:
| Técnica | O que faz | Custo e quando usar |
|---|---|---|
| Reescrita com histórico | Transforma "e o segundo?" em pergunta autocontida usando os turnos anteriores | Barata (modelo de volume, saída curta). Obrigatória em qualquer chat multi-turno |
| Decomposição | Quebra "compare A e B" em duas buscas independentes | Média: N buscas em vez de 1. Use quando o domínio tem perguntas comparativas |
| Expansão / HyDE | Gera uma resposta hipotética e busca por ela, em vez da pergunta | Média. Ajuda quando pergunta e documento usam vocabulários muito diferentes |
| Reranking | Reordena os candidatos com um modelo especializado em relevância | Barato por consulta e alto ganho — o melhor primeiro investimento depois do híbrido |
O padrão de recuperação profissional é buscar largo e entregar estreito: recupere de 30 a 50 candidatos na busca híbrida (barato) e deixe o reranker escolher os 3 a 5 que vão para o contexto (caro). Isso melhora a qualidade e reduz o custo ao mesmo tempo — porque o que pesa na conta é o token que entra no prompt, não o candidato que o índice devolveu.
import boto3
agent = boto3.client("bedrock-agent-runtime")
def recuperar(pergunta: str, usuario_areas: list[str], kb_id: str,
rerank_arn: str, top_final: int = 4) -> list[dict]:
resp = agent.retrieve(
knowledgeBaseId=kb_id,
retrievalQuery={"text": pergunta},
retrievalConfiguration={
"vectorSearchConfiguration": {
# Busca LARGO: candidatos sao baratos.
"numberOfResults": 40,
"overrideSearchType": "HYBRID", # vetorial + lexico
# Permissao como FILTRO, nunca como instrucao no prompt:
# o que a pessoa nao pode ler nao entra no contexto.
"filter": {"in": {"key": "area", "value": usuario_areas}},
# Entrega ESTREITO: o reranker corta para o que cabe no prompt.
"rerankingConfiguration": {
"type": "BEDROCK_RERANKING_MODEL",
"bedrockRerankingConfiguration": {
"numberOfRerankedResults": top_final,
"modelConfiguration": {"modelArn": rerank_arn},
},
},
}
},
)
return [
{
"texto": r["content"]["text"],
"fonte": r["location"],
"score": r.get("score"),
}
for r in resp["retrievalResults"]
]
Top-k alto não é seguro, é caro
A reação intuitiva a "o RAG não achou" é aumentar o top-k de 5 para 20. Isso multiplica os tokens de contexto em todo request e piora a resposta: o modelo passa a ter 15 trechos irrelevantes competindo com os 5 bons, e a atenção se dispersa — o efeito conhecido de perder informação no meio de contextos longos. Buscar 40 e entregar 4 é melhor e mais barato do que buscar 20 e entregar 20.
Sua avaliação mostra recall@20 de 91% e recall@5 de 48%. Qual é a intervenção correta?
Quando o RAG clássico não basta
| Situação | Por que o RAG padrão falha | Padrão adequado |
|---|---|---|
| "Quantos contratos vencem em março?" | É agregação sobre dados, não recuperação de trecho | Text-to-SQL ou tool que consulta o banco — não force isso no RAG |
| "Como o produto A se relaciona com o fornecedor B?" | A resposta está na conexão entre documentos, não em um deles | GraphRAG: índice de grafo capturando relações entre entidades |
| "Resuma tudo que mudou na política desde 2023" | Precisa de cobertura completa, não dos k mais relevantes | Sumarização hierárquica em batch, com resultado materializado |
| Pergunta sobre dado que muda a cada minuto | O índice está sempre defasado | Tool call no sistema de origem, com o RAG só para o texto explicativo |
| Base pequena e estável (algumas dezenas de páginas) | Você montou infraestrutura para um problema que não tem | Coloque tudo no contexto com cache — mais simples, mais barato e mais preciso |
O anti-padrão mais caro: RAG onde cabia contexto
Com janelas de contexto grandes e cache de prefixo, uma base de algumas dezenas de páginas que muda pouco não precisa de vector store nenhum. Colocar o conteúdo inteiro no prefixo cacheado é mais preciso (nada se perde na busca), mais simples (sem pipeline de ingestão) e frequentemente mais barato — porque você elimina a capacidade mínima do índice, que cobra 24 horas por dia mesmo com tráfego baixo. Faça a conta antes de subir infraestrutura de retrieval.
Como medir: as métricas que importam
Monte um conjunto de 100 a 300 perguntas reais com o trecho correto anotado. Esse conjunto é o ativo do projeto: é ele que permite afirmar que uma mudança melhorou, comparar técnicas e detectar regressão. As métricas se dividem nos dois lados que você mede separadamente:
| Lado | O que responde | Use para decidir | |
|---|---|---|---|
| Recall@k | Retrieval | O trecho certo apareceu entre os k primeiros? | A métrica principal do RAG — se ela é baixa, nada depois adianta |
| Precision@k | Retrieval | Que fração do que veio era relevante? | Quanto ruído você está pagando para injetar no contexto |
| MRR | Retrieval | Em que posição média o primeiro acerto aparece? | Mede se você precisa de reranking |
| nDCG | Retrieval | A ordem entregue é boa, considerando graus de relevância? | Comparar rerankers e ajustar a fusão híbrida |
| Fidelidade | Geração | A resposta se sustenta no contexto fornecido? | Detecta alucinação e mistura com conhecimento próprio do modelo |
| Relevância | Geração | A resposta atende à pergunta que foi feita? | Detecta resposta correta, ancorada e inútil |
Recall@k é a métrica que você olha primeiro
Se o trecho certo não entrou no top-k, a resposta certa é impossível — é o teto duro do sistema. Comece medindo recall@5 e recall@20 no seu conjunto: se recall@20 é alto e recall@5 é baixo, seu problema é ordenação e reranking resolve. Se recall@20 já é baixo, o problema é anterior — chunking, indexação ou conteúdo — e nenhum reranker vai salvar.
O custo real de um RAG
| Alavanca de custo no RAG | Efeito típico | Risco de qualidade |
|---|---|---|
| Buscar largo e rerankear para top-k pequeno | Corta boa parte dos tokens de contexto | Negativo — costuma melhorar a resposta |
| Cache no prefixo estável (instrução + few-shot) | Corta o custo do prefixo repetido | Nenhum, se o prefixo for realmente estável |
| Right-sizing do vector store | Elimina capacidade ociosa | Nenhum, se dimensionado pelo pico real |
| Modelo de volume na reescrita e na classificação | Tarefas auxiliares saem de barato | Baixo — são tarefas simples e verificáveis |
| Embeddings em batch na reindexação | Aproximadamente metade do preço | Nenhum — é assíncrono por natureza |
| Materializar respostas de perguntas frequentes | Elimina a chamada inteira nas repetidas | Precisa de invalidação quando o conteúdo muda |
A conta que surpreende todo mundo no piloto
Num piloto com poucas centenas de consultas por dia, a inferência costuma ser a MENOR linha da conta. O que domina é a capacidade mínima do vector store, que cobra igual com dez ou dez mil consultas. Isso tem uma consequência estratégica: o custo por consulta despenca conforme o uso cresce — então não mate um projeto pelo custo unitário medido no piloto, e não prometa esse custo unitário para a escala.
Checklist de RAG de produção
- Monte o conjunto de 100 a 300 perguntas com trecho correto anotado antes de otimizar qualquer coisa.
- Meça recall@5 e recall@20 para saber se o problema é ordenação ou é anterior.
- Arquive o conteúdo obsoleto antes de indexar — documento contraditório é pior que documento ausente.
- Escolha a estratégia de chunking por tipo de documento, testando num subconjunto antes da base inteira.
- Torne o metadado de confidencialidade, dono e validade obrigatório na ingestão.
- Ligue busca híbrida se o domínio tem códigos, siglas ou identificadores.
- Aplique permissão como filtro de metadados na consulta, nunca como instrução no prompt.
- Busque largo (30 a 50 candidatos) e entregue estreito (3 a 5) via reranking.
- Reescreva a consulta com o histórico em qualquer chat multi-turno.
- Exija citação da fonte na resposta e trate a ausência de fonte como resposta honesta de que não sabe.
- Marque o cache point no prefixo estável e monitore a taxa de leitura do cache.
- Considere contextual retrieval quando o recall continuar baixo depois de híbrido e reranking.
- Rode a avaliação a cada mudança de chunking, modelo, prompt ou top-k — e guarde o histórico.
- Revise o dimensionamento do vector store trimestralmente; capacidade ociosa é a fuga silenciosa.
Um assistente interno de engenharia responde bem a perguntas conceituais, mas erra sempre que o usuário cita um código de norma ou um identificador de sistema. Qual é a causa mais provável?
No piloto, o RAG custa caro por consulta e o time considera cancelar o projeto. A base tem 40 páginas de políticas que mudam duas vezes por ano, e o volume é de 200 consultas por dia. Qual é a leitura correta?
Próximo passo
RAG resolve a pergunta cuja resposta está escrita em algum lugar. O que fazer quando a resposta depende de consultar um sistema ou executar uma ação? É tool use — e no próximo módulo ele deixa de ser hello world e vira o contrato entre o modelo e os sistemas da empresa, com schema, erro, paralelismo, segurança e o efeito que o conjunto de tools tem no seu cache.
Perguntas frequentes
❓ Por que busca puramente vetorial falha?
❓ O que é recuperação com contexto no corte?
❓ Quantos trechos enviar no contexto?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…