Conteúdo da trilha
8 minNarração em inglês, legenda em portuguêsPodcast (RSS)
Capítulos
  1. 0:00Abertura
  2. 0:45A ideia
  3. 1:16Antes e depois
  4. 1:48A mesma pergunta
  5. 2:44Um desafio
  6. 3:30Passo a passo seguro
  7. 4:58Riscos
  8. 6:28O código, por intenção
  9. 7:07Fechamento
Transcrição

Abertura

De volta ao assistente jurídico da aula passada. Um contrato de serviços, dezoito páginas, uns doze mil tokens.

O pipeline quebra o contrato em pedaços de quinhentos tokens e busca os quatro mais próximos de cada pergunta. Quatro pedaços de quinhentos dá dois mil tokens. De doze mil.

Então, quando alguém perguntou sobre rescindir por atraso, o modelo respondeu olhando um sexto do documento. E não sabia disso. Disse que não tinha multa. A cláusula 14.3 dizia outra coisa.

Nesta aula, você vai tirar a busca, com segurança, colocar o contrato inteiro na frente do modelo e manter a conta sob controle.

A ideia

A refatoração cabe em duas frases. Mande o material inteiro no contexto, num bloco fixo no começo da chamada. E ligue o cache de prompt, para não pagar a leitura cheia em toda pergunta.

A busca vetorial existe para escolher o pouco que cabe, quando o material é grande. Com material pequeno, essa escolha só tira informação. A quebra separa cláusulas que dependem uma da outra, e a busca erra quando as palavras da pergunta não batem com o texto.

Antes e depois

Antes, o contrato vira pedaços e embeddings. A pergunta escolhe quatro deles. E o modelo responde com o que a busca escolheu.

Depois, o documento inteiro vai num bloco fixo, logo depois das instruções.

Na primeira pergunta, esse prefixo é gravado no cache.

Nas seguintes, o modelo lê o contrato do cache, por uma fração do custo.

E a resposta pode ligar cláusulas distantes, porque nada foi cortado.

A mesma pergunta

Então, a mesma pergunta de novo. Com quatro pedaços, o contexto tinha a rescisão e não a multa. Com o contrato inteiro, as duas cláusulas estão no mesmo prefixo.

E a instrução pede ao modelo que cite artigo e parágrafo. Então a resposta vira esta. Não. A cláusula 8 permite rescindir por atraso, mas a 14.3 aplica multa por inadimplemento, e atraso na entrega entra nessa definição. É uma resposta que um advogado confere em dez segundos.

Um alerta continua valendo. Um contexto enorme não garante que o modelo ache a cláusula do meio. A pesquisa chama isso de lost in the middle. Por isso a condição é caber com folga. Doze mil tokens numa janela grande é folga. Cem mil tokens de anexos empilhados é outro projeto.

Um desafio

Uma pergunta para você. Depois da troca, a segunda pergunta do usuário mostra zero tokens lidos do cache. Olhando o prompt, o bloco de sistema começa com a data de hoje, para o modelo saber o dia, e depois o contrato. Qual é a correção?

Tire a data do prefixo e mande na mensagem do usuário, depois do documento em cache. O cache reaproveita um começo de prompt idêntico. Uma data que muda todo dia quer dizer que nunca tem nada para reaproveitar.

Não é culpa do fornecedor, e fazer cache da mensagem do usuário também não ajuda, porque ela muda a cada pergunta. Instruções e contrato ficam estáveis. Data, usuário e pergunta vêm depois.

Passo a passo seguro

Agora, o passo a passo.

Passo zero, medir. Rode o eval de perguntas e inclua perguntas que cruzam seções, como uma regra e a sua exceção. Anote acerto, custo e latência por pergunta. Essas perguntas cruzadas são o motivo da troca.

Confira o tamanho. Conte os tokens com o contador do fornecedor, não no chute. Deixe espaço para a conversa e a resposta. Se não sobra espaço, esta refatoração não se aplica.

Monte o prefixo fixo. Instruções primeiro, depois o documento. E nada que varia antes do fim do documento. Nem data, nem id de usuário, nem hora.

Ligue o cache de prompt no bloco do documento.

Confirme que funciona. Da segunda chamada em diante, o relatório de uso deve mostrar tokens lidos do cache. Se mostrar zero, o prefixo está mudando em algum lugar.

Meça de novo e compare acerto e custo por pergunta. As perguntas cruzadas são o teste que importa. No exemplo, a busca acertou 61%, e o documento inteiro, 88%.

E só depois que a versão nova estabilizar, remova o pipeline de busca. Não deixe atrás de uma flag, só por garantia. Enquanto os dois existirem, alguém vai só ajustar o número de pedaços, no meio de um incidente.

Riscos

Alguns riscos. O material cresce. Hoje é um contrato. Amanhã são cento e vinte contratos de fornecedores, e o prefixo não cabe mais com folga. O tamanho mudou, então a ferramenta muda. Volte para um índice, filtrado pelo contrato do usuário, e mantenha o contexto direto para o documento único que ainda cabe.

Perguntas muito espaçadas. O cache expira depois de alguns minutos sem uso. Com pouco tráfego, o desconto aparece menos. O ganho de acerto nas perguntas cruzadas continua.

Partes que o usuário não pode ver. Se alguns usuários só podem ver o anexo de preços, um prompt pedindo ao modelo que esconda o resto não é controle de acesso. Uma pergunta capciosa vaza. A permissão vale antes de montar o contexto, com busca filtrada ou um prefixo por perfil.

E achar que caber basta. Se o eval de perguntas cruzadas continua ruim com o documento inteiro, o problema não era a busca. Pode ser o prompt, o modelo, ou um contrato que é ambíguo de verdade.

E uma fronteira rápida. Uma loja online pensa em colar o catálogo de oito mil produtos no prefixo, com cache. Isso não é esta refatoração. Não cabe com folga, cada pergunta precisa de poucas linhas, e o preço de um produto é uma consulta pelo código. É uma ferramenta de consulta, ou busca, não um prefixo de oito mil itens.

O código, por intenção

O código fica menor, o que é um bom sinal.

O contrato é carregado uma vez, como texto.

A parte de sistema tem dois blocos. As instruções, e depois o contrato, marcado para cache.

E a pergunta vai sozinha na mensagem do usuário, depois de tudo que é estável. As três linhas que geravam o embedding da pergunta, buscavam no banco e juntavam os pedaços simplesmente somem.

Na página, você encontra a mesma mudança nas abas de código. O que muda entre elas é só como cada uma marca o bloco para cache, e onde informa os tokens lidos do cache.

Fechamento

Então, a ideia para levar. Quando o material cabe com folga, a busca é um filtro que só tira. Ponha o documento primeiro, a pergunta depois, deixe o cache carregar o custo e meça nas perguntas que cruzam seções.

Mas às vezes o material realmente não cabe, e você precisa de pedaços. Na próxima aula, a gente faz cada pedaço carregar o contexto que perdeu quando foi cortado: contextual chunking. Até lá.

RefatoraçãoOrganizando dados

Trocar RAG por Contexto Direto

Replace RAG with Direct Context

Trocar RAG por Contexto Direto (Replace RAG with Direct Context) é uma refatoração de IA para sistemas com LLM, do grupo Organizando dados. Quando o material cabe na janela, mandar inteiro e usar cache de prompt.

Elemento
18/35
Nível
Iniciante
Leitura
5 min
Atualizado
Nesta página 8
  1. 01Problema
  2. 02Solução
  3. 03Antes e depois
  4. 04Por que funciona
  5. 05Passo a passo seguro
  6. 06Riscos
  7. 07Desafio
  8. 08Teste seu entendimento
  9. ··Perguntas rápidas

Problema

Um pipeline de busca vetorial atende um material pequeno, que caberia inteiro no contexto, e perde trechos importantes no caminho. É o smell RAG Desnecessário.

O assistente jurídico responde perguntas sobre um contrato de prestação de 18 páginas (cerca de 12 mil tokens). A arquitetura que saiu da demo: quebrar em pedaços de 500 tokens, embedding, buscar os 4 mais parecidos. A pergunta “posso rescindir sem multa se o fornecedor atrasar a entrega?” trouxe a cláusula de rescisão e não a de penalidades, que falava de “inadimplemento” em vez de “atraso”. O modelo respondeu “sim, sem multa”. A cláusula 14.3 dizia o contrário.

O contrato inteiro cabia na janela com folga. Quatro pedaços de 500 tokens são 2 mil dos 12 mil: o modelo respondeu olhando para um sexto do documento, sem saber disso. A busca fez o trabalho para o qual ela existe quando o corpus não cabe. Aqui o corpus cabia.

Solução

Mande o material inteiro no contexto, num bloco fixo no começo da chamada, e use cache de prompt para não pagar a leitura completa a cada pergunta.

Antes e depois

-chunks = vector_db.search(embed(question), k=4)
-context = "\n".join(c.text for c in chunks)
-reply = llm(SYSTEM, f"{context}\n\nPergunta: {question}")
+CONTRACT = load_text("contrato.pdf")          # 18 páginas, cerca de 12 mil tokens
+
+reply = client.messages.create(
+    model=MODEL, max_tokens=2000,
+    system=[
+        {"type": "text", "text": SYSTEM},
+        {"type": "text", "text": f"<contrato>\n{CONTRACT}\n</contrato>",
+         "cache_control": {"type": "ephemeral"}},       # prefixo cacheado
+    ],
+    messages=[{"role": "user", "content": question}],
+)

Antes: o modelo vê os 4 pedaços que a busca escolheu.

flowchart LR
  D(["1. Contrato"]):::io --> C["2. Pedaços e embeddings"]:::tool
  Q(["Pergunta"]):::io --> S["3. Buscar 4 pedaços"]:::tool
  C --> S
  S --> M(["4. Modelo com parte do contrato"]):::llm

Depois: o modelo vê o contrato inteiro, e o cache absorve o custo.

flowchart LR
  D(["1. Contrato inteiro no prefixo"]):::io --> K[("2. Cache de prompt")]:::store
  Q(["Pergunta"]):::io --> M(["3. Modelo com o contrato todo"]):::llm
  K --> M
  M --> OUT(["4. Resposta que considera todas as cláusulas"]):::io
  1. O documento inteiro vai num bloco fixo, depois das instruções.
  2. Na primeira pergunta, o prefixo é gravado no cache.
  3. Nas seguintes, o modelo lê o contrato do cache, a uma fração do custo.
  4. A resposta pode cruzar cláusulas distantes, porque nada foi cortado.

A pergunta que o RAG errava

Com os 4 pedaços, o contexto tinha a rescisão e não a multa. Com o contrato inteiro, a mesma pergunta vê as duas cláusulas no mesmo prefixo. A instrução pede artigo e parágrafo. A resposta passa a ser do tipo: “não: a cláusula 8 permite rescisão por atraso, mas a 14.3 aplica multa sobre o inadimplemento, e atraso de entrega entra nessa definição”.

Os números desta página seguem o contrato de 18 páginas já usado no smell. Não são uma medição nova de produto.

Por que funciona

A busca vetorial existe para escolher o pouco que cabe no contexto quando o material é grande. Com material pequeno, essa escolha só tira informação: o corte separa cláusulas que dependem umas das outras, e a busca erra quando as palavras da pergunta não batem com as do texto.

Com o documento inteiro, o modelo faz o que faz bem: lê, relaciona e cita. O cache de prompt resolve a objeção do custo, porque o prefixo idêntico (instruções e documento) é cobrado com desconto a partir da segunda chamada. E o sistema fica mais simples: sem índice, sem embeddings, sem ajuste de busca.

Esta refatoração é o outro lado de Tirar o Conhecimento de Dentro do Prompt. Lá o corpus não cabe e muda o tempo todo. Aqui o material cabe e a busca é o que está atrapalhando.

O paper “Lost in the Middle” (Liu et al., 2023) ainda vale como alerta: contexto enorme não garante que o modelo ache a cláusula do meio. Por isso o “cabe com folga” importa. Doze mil tokens num modelo de janela grande é folga. Cem mil tokens de anexos empilhados já não é o mesmo desenho.

Checagem +20 XP de primeiraO assistente jurídico erra 'posso rescindir sem multa se atrasar a entrega?' porque a busca devolve a cláusula de rescisão e não a de multa, que usa a palavra 'inadimplemento'. O contrato tem 18 páginas e cabe na janela. Qual é o movimento certo?

Passo a passo seguro

  1. Meça. Rode o eval de perguntas, incluindo perguntas que cruzam seções. Guarde acerto, custo e latência por pergunta. As perguntas cruzadas são o motivo da troca.
  2. Confira o tamanho. Conte os tokens do material com o contador da API. Deixe folga para a conversa e a resposta. Se não houver folga, esta refatoração não se aplica.
  3. Monte o prefixo fixo: instruções primeiro, documento depois, sem nada variável (data, user id, hora) antes do fim do documento.
  4. Ative o cache de prompt no bloco do documento.
  5. Confira o cache funcionando: a partir da segunda chamada, o usage deve mostrar tokens lidos do cache. Se vier zero, o prefixo está mudando.
  6. Meça de novo e compare acerto e custo por pergunta. O acerto das perguntas cruzadas é o teste que importa.
  7. Remova o pipeline de busca só depois que a nova versão estiver estável. Enquanto os dois existirem, alguém vai “só ajustar o k”.

Riscos

  • O material cresce. Hoje é um contrato; amanhã são cem. Monitore o tamanho e volte para busca quando passar do limite em que ainda há folga.
  • Cache que não acerta. Uma data ou um id antes do documento muda o prefixo e zera o cache. Mantenha o que varia depois, na mensagem do usuário.
  • Perguntas muito espaçadas. O cache expira depois de alguns minutos sem uso (há opções de duração maior). Com tráfego baixo, o desconto aparece menos. O ganho de acerto das cláusulas cruzadas continua valendo.
  • Documento com partes que o usuário não pode ver. Mandar tudo ignora permissões por trecho. Nesse caso, a busca com filtro é necessária, mesmo que o arquivo caiba.
  • Achar que “cabe” é o bastante. Se o eval de perguntas cruzadas continuar ruim com o documento inteiro, o problema não era o RAG. Pode ser o prompt, o modelo ou um contrato realmente ambíguo.

Erro comum: prefixo que parece fixo e não é

O time coloca o contrato no system prompt e, no mesmo bloco, a data de hoje “para o modelo saber o dia”. Cada dia o prefixo muda, o cache não acerta, e a fatura volta ao valor da leitura completa. Data, nome do usuário e id da sessão vão na mensagem, depois do documento.

Checagem +20 XP de primeiraDepois de mandar o contrato inteiro, o usage da segunda pergunta do mesmo usuário mostra zero tokens lidos do cache. O system prompt inclui a data de hoje antes do contrato. Qual é o conserto?

Desafio

Um assistente responde dúvidas sobre o regulamento de um condomínio (25 páginas). Hoje ele usa RAG e erra perguntas como “posso fazer obra no sábado à tarde?”, porque a regra geral está numa seção e a exceção de sábado em outra. Como fica a versão com contexto direto, e o que conferir?

Ver solução

O regulamento inteiro vai num bloco fixo do system prompt, com cache, e as instruções pedem para citar artigo e parágrafo. Confira: o tamanho em tokens com folga para a conversa; o cache_read_input_tokens positivo a partir da segunda pergunta; e o eval com perguntas que dependem de regra e exceção em seções diferentes, que eram justamente as que o RAG errava.

Teste seu entendimento

Checagem +20 XP de primeiraO corpus passou de um contrato de 18 páginas para 120 contratos de fornecedores, e o prefixo não cabe mais com folga. O eval de perguntas cruzadas contra um contrato só continua bom. O que você faz?

Checagem +20 XP de primeiraDois times usam o mesmo PDF de 18 páginas. O time A tem usuários com permissão só ao anexo de preços. O time B pode ver o contrato inteiro. Contexto direto serve para os dois?

Checagem +20 XP de primeiraVocê mediu o eval cruzado: RAG 61% de acerto, contexto direto 88%, custo por pergunta a partir da segunda chamada ficou menor com cache. Qual é o último passo da refatoração?

Checagem +20 XP de primeiraUm e-commerce imagina colar o catálogo de 8 mil produtos no prefixo, com cache, no lugar do RAG. Por que esta refatoração não se aplica?

Perguntas rápidas

O que é Trocar RAG por Contexto Direto?

Trocar RAG por Contexto Direto (Replace RAG with Direct Context) é uma refatoração de IA para sistemas com LLM, do grupo Organizando dados. Quando o material cabe na janela, mandar inteiro e usar cache de prompt.

Que problema Trocar RAG por Contexto Direto resolve?

Trata RAG Desnecessário.

Aonde Trocar RAG por Contexto Direto leva?

Ao padrão de projeto Cache de Prompt.

Para ir além

Relações

Smells que trata 1
Padrões de destino 1
Aparece nos cenários 1
Aparece nas trilhas 1
Buscar