Um pruner de contexto RAG não é melhor do que o trade-off que consegue: compressão por ponto de recall perdido.
A kapa.ai cria assistentes de IA que respondem a perguntas complexas sobre bases de conhecimento técnico — documentação, APIs, PDFs, fóruns. O pipeline é o clássico: um retriever encontra os chunks relevantes, um generator (LLM caro) escreve a resposta. Mas há um problema silencioso: a maior parte do que o generator lê é ruído.
A equipa descobriu que os chunks recuperados representam cerca de dois terços do custo de cada query. E num agente, onde cada tool call despeja mais conteúdo no contexto, o problema escala. Foi aí que decidiram meter um terceiro passo entre o retriever e o generator. Este artigo explica como fizeram pruning de contexto e os resultados que obtiveram.
Porque é que cortar contexto é difícil
O retriever é um funil. Embeddings e pesquisa por palavras-chave reduzem uma base de milhares de chunks a algumas centenas. Um reranker ordena-os. Os 15 melhores chegam ao generator, o modelo mais caro da cadeia.
Mesmo assim, a maioria dos chunks que o generator lê não são precisos para responder à pergunta. Isso é deliberado — os retrievers privilegiam a recall máxima e confiam no generator para ignorar o ruído. Mas o generator paga por cada chunk que ignora. Cada chunk a menos corta o custo em cerca de 4%.
O problema é que a recall não pode sofrer. Cortar um chunk que faz falta à resposta significa trocar cêntimos por uma resposta errada.
As soluções óbvias não funcionam
Corte por pontuação do reranker. Expor as pontuações do reranker e cortar tudo abaixo de um limiar parece lógico. Mas falha por duas razões.
Primeiro, a pontuação de um reranker é uma ordenação, não uma medição absoluta. Diz que o chunk A é melhor que o B para aquela query, nada mais. As pontuações não são calibradas entre queries. O próprio Cohere confirma isto. Um corte fixo não funciona.
Segundo — e mais importante — a relevância não é uma propriedade de um chunk isolado. Os rerankers são cross-encoders pontuais: avaliam cada par query-chunk sozinho, sem ver os outros chunks com que foi recuperado. Um chunk que sozinho parece irrelevante pode ser metade da resposta quando combinado com outro. Chunks também dividem perguntas de múltiplas partes entre si — cada um é inútil sozinho.
A questão real nunca é se um chunk é relevante por si só, mas se pertence a um conjunto que em conjunto responde à pergunta.
Documentos âncora. A equipa testou a abordagem de Sinhababu et al.: plantar chunks sintéticos com níveis de relevância conhecidos no ranking para calibrar as pontuações. Elegante, mas não funcionou — o reranker continuava a colocar chunks parcialmente relevantes abaixo de chunks claramente irrelevantes. A âncora tinha de ficar tão baixa que quase nada era cortado.
A conclusão foi clara: qualquer solução de pruning tem de ver a pergunta e todos os chunks ao mesmo tempo. O que está a ser julgado é o conjunto, não os elementos individuais.
A solução: um LLM a classificar o conjunto
O que a kapa.ai implementou é uma chamada LLM listwise entre o reranker e o generator. Recebe a pergunta e todos os chunks, e classifica cada um numa escala de 5 níveis definida no prompt:
| Pontuação | Nível | Significado |
|---|---|---|
| 5 | ESSENTIAL | A resposta não pode ser produzida sem este chunk |
| 4 | CONTRIBUTING | Não responde sozinho, mas é necessário em combinação com outros |
| 3 | SUPPORTING | Relacionado e potencialmente útil, mas a resposta provavelmente dispensa-o |
| 2 | TANGENTIAL | Mesmo domínio, sem contribuição concreta |
| 1 | UNRELATED | Sem ligação significativa |
Os chunks com pontuação igual ou superior a um limiar sobrevivem. Esta abordagem resolve os dois problemas anteriores: cada nível é definido por palavras, por isso um 4 significa o mesmo em qualquer query (corte fixo funciona), e o modelo vê todos os chunks em conjunto (consegue julgar o conjunto).
Três variáveis importam:
- O modelo: o pruner é pago pelo que poupa, por isso modelos topo-de-gama estão excluídos. Escolheram o mais rápido e barato com low reasoning effort.
- O limiar: o dial principal entre compressão e recall.
- keep-top-k: os primeiros chunks do reranker passam sempre, protegendo os mais fortes de erros de classificação.
Resultados: 68% de compressão com 96% de recall
A equipa testou a solução num conjunto de perguntas reais onde sabiam exatamente quais os chunks necessários para a resposta. Depois verificaram compressão, custo e latência num mês de produção.
O resultado: cerca de 68% dos chunks eliminados com 96% da recall preservada. Uma em cada 25 perguntas perde um chunk necessário; em troca, dois terços do contexto desaparecem e a fatura por query cai cerca de 34% — já descontando o custo do pruner.
Em termos de latência, o pruner demora cerca de 0.7 segundos por query com a configuração escolhida. O generation mal acelera (poupa uns décimos ao receber menos tokens), por isso o pruner acrescenta latência líquida. Num agente, que já faz várias chamadas por turno, uma chamada leve é marginal.
Onde faz sentido usar
A kapa.ai ativou o pruning primeiro em contextos baseados em agentes, onde a pesquisa é uma ferramenta entre muitas. Um agente carrega dezenas de ferramentas, cada chamada despeja conteúdo no contexto, e uma pesquisa que devolve dois terços menos chunks compra espaço para tudo o resto.
E a recall perdida é menos perigosa num agente — se notar que falta alguma coisa, pode pesquisar outra vez.
Baseado no artigo original de Lars Baltensperger na kapa.ai.
Comentários (0)
Nenhum comentário ainda. Seja o primeiro!
Deixar comentário