Skip to content

RAG e Agentes – Parte 1

Para um determinado aplicativo, as instruções do modelo são comuns a todas as consultas, enquanto o contexto é específico para cada consulta. Textos anteriores discutiram como escrever boas instruções para o modelo. Este capítulo se concentra em como construir o contexto relevante para cada consulta.

Dois padrões dominantes para a construção de contexto são RAG, ou retrieval-augmented generation e agentes. O padrão RAG habilita o modelo a recuperar informações relevantes de fontes de dados externas. O padrão do agente permite que o modelo use ferramentas como APIs de pesquisa e notícias da web para coletar informações.

RAG

RAG é uma técnica que aprimora a geração de um modelo, recuperando as informações relevantes de fontes externas de memória. Uma fonte externa de memória pode ser um banco de dados interno, as sessões de bate-papo anterior do usuário ou a Internet.

Com RAG, apenas as informações mais relevantes para a consulta, conforme determinado pelo Retriever (recuperador), são recuperadas e inseridas no modelo.

Você pode pensar no RAG como uma técnica para construir o contexto específico para cada consulta, em vez de usar o mesmo contexto para todas elas.

Arquitetura de Rag

Um sistema RAG possui dois componentes: um retriever que recupera informações de fontes de memória externas e um gerador, que gera uma resposta com base nas informações recuperadas.

O sucesso de um sistema RAG depende da qualidade de seu retriever. Um retriever tem duas funções principais: indexação e consulta. Indexação envolve o processamento de dados para que possam ser recuperados rapidamente posteriormente. Consulta envolve enviar uma consulta para recuperar dados relevantes. Como a indexação dos dados ocorrerá depende de como você deseja recuperá-los mais tarde.

Vamos considerar um exemplo de como um sistema RAG funciona. Vamos supor que a memória externa é um banco de dados de documentos, como memorandos, contratos e notas de reunião de uma empresa. Um documento pode conter 10 tokens ou 1 milhões de tokens. A recuperação ingênua de documentos inteiros pode fazer com que seu contexto seja arbitrariamente longo. Para evitar isso, você pode dividir cada documento em pedaços (chunks) mais gerenciáveis. As estratégias de chunking serão discutidas mais adiante. Por enquanto, vamos supor que todos os documentos tenham sido divididos em pedaços viáveis. Para cada consulta, nosso objetivo é recuperar os pedaços de dados mais relevantes a ela. Pós-processamentos menores frequentemente serão necessários para unificar pedaços de dados recuperados com o prompt do usuário, a fim de gerar o prompt final. Este prompt final é então alimentado no modelo generativo.

Algoritmos de recuperação

Recuperação baseada em termos

Dada uma consulta, a maneira mais direta de encontrar documentos relevantes é com palavras-chave. Algumas pessoas chamam essa abordagem de recuperação lexical. Essa abordagem apresenta dois problemas:

  • Muitos documentos podem conter o termo especificado, e seu modelo pode não ter espaço de contexto suficiente para incluir todos eles no contexto. Uma heurística é incluir os documentos que apresentam o termo o maior número de vezes. A suposição é de que, quanto mais um termo aparece em um documento, mais relevante este documento é para este termo. O número de vezes que um termo aparece em um documento é chamado de frequência de termo (TF).
  • Um prompt pode ser longo e conter muitos termos. Alguns são mais importantes que outros. Por exemplo, o prompt “Receitas rápidas, fáceis de seguir, de comida vietnamita, para cozinhar em casa” contém vários termos: Fácil de seguir, receitas, para, vietnamita, comida, cozinhar, em casa. Você quer se concentrar em termos mais informativos, como “vietnamita” e “receitas”, não “para” e “de”. Você precisa identificar os termos importantes.

Uma intuição é que quanto mais documentos contêm um termo, menos informativo é esse termo. “Para” e “em” provavelmente aparecerão na maioria dos documentos, portanto, eles são menos informativos. Então, a importância de um termo é inversamente proporcional ao número de documentos em que aparece. Esta métrica é chamada frequência inversa do documento (IDF).

TF-IDF é um algoritmo que combina essas duas métricas: frequência de termo (TF) e frequência inversa do documento (IDF).

Duas soluções de recuperação baseadas em termos comuns são Elasticsearch e BM25.

Um processo ainda não mencionado é a tokenização, o processo de quebrar uma consulta em termos individuais. O método mais simples é dividir a consulta em palavras, tratando cada palavra como um termo separado. No entanto, isso pode levar a separar termos que são palavras compostas, perdendo seu significado. Por exemplo “cachorro-quente” seria dividido em “cachorro” e “quente”. Quando isso acontece, nenhum deles mantém o significado original. Uma maneira de mitigar esse problema é tratar os n-gramas mais comuns como termos. Se o termo “cachorro-quente” é comum, será tratado como um termo.

Recuperação baseada em embedding

A recuperação baseada em termos calcula a relevância em um nível lexical, em vez de um nível semântico. Mas o aparecimento de um texto não necessariamente captura seu significado. Isso pode resultar no retorno de documentos irrelevantes para sua intenção.

Por outro lado, os retrievers baseados em embedding pretendem classificar documentos com base em quão intimamente seus significados estão alinhados com a consulta. Essa abordagem também é conhecida como recuperação semântica.

Com a recuperação baseada em embedding, a indexação tem uma função extra: a conversão dos pedaços de dados originais em embeddings. O banco de dados onde o os embeddings gerados são armazenados é chamada de banco de dados vetorial. A consulta consiste em duas etapas:

  • Modelo de embedding: converte a consulta em um embedding usando o mesmo modelo de embedding usado durante a indexação.
  • Recuperador (retriever): busca k blocos de dados cujos embeddings são os mais próximos do embedding da consulta, conforme determinado pelo recuperador.

Como lembrete, um embedding é tipicamente um vetor que visa preservar as propriedades importantes dos dados originais. Um recuperador baseado em embedding não funciona se o modelo de embedding for ruim.

A recuperação baseada em embedding também apresenta um novo componente: bancos de dados vetoriais. Um banco de dados vetorial armazena vetores. No entanto, armazenar é a parte fácil de um banco de dados vetorial. A parte difícil é a pesquisa vetorial. Dado um embedding de consulta, um banco de dados vetorial é responsável por encontrar os vetores no banco de dados próximo à consulta e retorná-los. Os vetores precisam ser indexados e armazenados de uma maneira que torne a pesquisa de vetores rápida e eficiente.

A pesquisa vetorial é normalmente enquadrada como um problema de pesquisa do vizinho mais próximo. Por exemplo, dada uma consulta, encontre os k vetores mais próximos.

Essa solução ingênua garante que os resultados sejam precisos, mas ela é computacionalmente pesada e lenta. Deve ser usado apenas para pequenos conjuntos de dados.

Para conjuntos de dados grandes, a pesquisa de vetores geralmente é feita usando um algoritmo vizinho mais próximo aproximado (ANN). Devido à importância do vetor de pesquisa, muitos algoritmos e bibliotecas foram desenvolvidos para isso.

Comparando algoritmos de recuperação

Devido à longa história da recuperação, muitas soluções maduras tornam a recuperação baseada em termos e baseada em embedding relativamente fácil de iniciar. Cada abordagem tem seus prós e contras.

A recuperação baseada em termos é geralmente muito mais rápida que a recuperação baseada em embedding durante a indexação e a consulta. A extração de termo é mais rápida que a geração de embedding, e mapear o termo aos documentos que o contêm pode ser menos caro computacionalmente do que a pesquisa do vizinho mais próximo.

A recuperação baseada em embedding, por outro lado, pode ser significativamente melhorada ao longo do tempo para superar a recuperação baseada em termos. Você pode realizar um ajuste fino (finetuning) do modelo de embedding e do retriever, separadamente, os dois juntos, ou em conjunto com o modelo generativo. No entanto, converter dados em embeddings pode obscurecer palavras-chave, como códigos de erro específicos, ou nomes de produtos, tornando-os mais difíceis de pesquisar mais tarde. Essa limitação pode ser endereçada combinando a recuperação baseada em embeddings com a recuperação baseada em termos.

A qualidade de um retriever pode ser avaliada com base na qualidade dos dados que ele recupera. Duas métricas frequentemente usadas por estruturas de avaliação de RAG são precisão de contexto e recordação de contexto:

  • Precisão de contexto: De todos os documentos recuperados, qual porcentagem é relevante para a consulta?
  • Recordação de contexto: De todos os documentos que são relevantes à pesquisa, qual percentual foi recuperado?

Para recuperação semântica, você também precisa avaliar a qualidade de seus embeddings. Embeddings podem ser avaliados independentemente – eles são considerados bons se documentos mais semelhantes tiverem embeddings mais próximos. Embeddings também podem ser avaliadas por quão bem eles funcionam para tarefas específicas. O benchmark MTEB avalia embeddings para uma ampla gama de tarefas, incluindo recuperação, classificação e cluster.

A qualidade de um retriever também deve ser avaliada no contexto de todo o sistema de RAG. Por fim, um retriever é bom se ajuda o sistema gerar respostas de alta qualidade.

Com os sistemas de recuperação, você pode fazer certas trocas entre indexação e consulta. Quanto mais detalhado o índice é, mais preciso o processo de recuperação será, mas o processo de indexação será mais lento e mais consumidor de memória.

Para resumir, a qualidade de um sistema do RAG deve ser avaliada componente por componente, de ponta a ponta. Para fazer isso, você deve fazer o seguinte:

  1. Avaliar a qualidade da recuperação
  2. Avaliar as saídas do RAG
  3. Avaliar os embeddings (para recuperação baseada em embedding)

Combinando algoritmos de recuperação

Dadas as vantagens distintas de diferentes algoritmos de recuperação, um sistema de recuperação de produção normalmente combina várias abordagens. Combinar a recuperação baseada em termos e a recuperação baseada em embedding é chamada de pesquisa híbrida.

Diferentes algoritmos podem ser usados ​​na sequência. Primeiro, um retriever barato e menos preciso, como um sistema baseado em termos, busca candidatos. Então, o mecanismo mais preciso, porém mais caro, como os vizinhos mais próximos, encontra o melhor desses candidatos. Este segundo passo também é chamado reclassificação (reranking).

Otimização de recuperação

Dependendo da tarefa, certas táticas podem aumentar a chance de os documentos relevantes serem buscados.

Estratégia de Chunking

Como seus dados devem ser indexados depende de como você pretende recuperá-los mais tarde. A estratégia mais simples é dividir documentos em pedaços (chunks) de comprimento igual com base em uma determinada unidade. Unidades comuns são caracteres, palavras, frases e parágrafos. Por exemplo, você pode dividir cada documento em pedaços de 2.048 caracteres ou 512 palavras.

Você também pode dividir os documentos recursivamente usando unidades cada vez menores até que cada pedaço se encaixe no tamanho máximo do pedaço. Por exemplo, você pode começar dividindo um documento em seções. Se uma seção for muito longa, divida-a em parágrafos. Se um parágrafo ainda estiver muito longo, divida-o em frases. Isso reduz a chance de os textos relacionados serem arbitrariamente quebrados.

Documentos de perguntas e respostas podem ser divididos por pares de perguntas e respostas, onde cada par compõe um chunk.

Quando um documento é dividido em pedaços sem sobreposição, os pedaços podem ser cortados no meio de um contexto importante, levando à perda de informações críticas. Considere o texto “Eu deixei uma nota para minha esposa”. Se for dividido em “Eu deixei uma nota” e “para minha esposa”, nenhum desses dois pedaços transmite a informação-chave do texto original. A sobreposição garante que informações importantes em limites sejam incluídas em pelo menos um pedaço. Se você definir o
tamanho do pedaço (chunk) em 2.048 caracteres, talvez você possa definir o tamanho de sobreposição em 20 caracteres.

Você também pode dividir os documentos usando tokens, determinados pelo tokenizador do modelo generativo, como uma unidade. Digamos que você queira usar Llama 3 como seu modelo generativo. Você primeiro tokenizar documentos usando o Tokenizer de Llama 3. Você pode dividir documentos em pedaços usando tokens como separadores. Dividir por tokens facilita o trabalho para os modelos que os utilizarão. No entanto, a desvantagem dessa abordagem é que, se você mudar para outro modelo generativo com um tokenizador diferente, você precisa reindexar seus dados.

Independentemente de qual estratégia você escolhe, o tamanho dos chunks importa. Um tamanho de pedaço menor permite informações mais diversas. Pedaços menores significam que você pode encaixar mais pedaços no contexto do modelo. Se você reduzir pela metade o tamanho do pedaço, poderá caber duas vezes mais pedaços. Mais pedaços podem fornecer um modelo com uma gama mais ampla de informações, que pode permitir que o modelo produza uma resposta melhor.

Pequenos tamanhos de pedaços, no entanto, podem causar a perda de informações importantes. Imagine um documento que contém informações importantes sobre o tópico X em todo o documento, mas X é mencionado apenas no primeiro momento. Se você dividir este documento em dois pedaços, a segunda metade do documento pode não ser recuperada e o modelo não poderá usar suas informações.

Os tamanhos de pedaços menores também podem aumentar a sobrecarga computacional. Isso é especialmente um problema para a recuperação baseada em embeddings. Dividir o tamanho do chunk pela metade significa que você tem o dobro de pedaços para indexar e o dobro de vetores incorporados para gerar e armazenar. Seu espaço de pesquisa vetorial será duas vezes maior, o que pode reduzir a velocidade da consulta.

Não existe o melhor tamanho universal de pedaços ou tamanho de sobreposição. Você precisa experimentar para encontrar o que funciona melhor para você.

Reranking

As classificações iniciais do documento geradas pelo Retriever podem ser reclassificadas para serem mais precisas. Reclassificar é especialmente útil quando você precisa reduzir o número de documentos recuperados, seja para encaixá-los para o contexto do seu modelo ou para reduzir o número de tokens de entrada.

Um padrão comum para reclassificar foi discutido em “Combinando algoritmos de recuperação”. Um retriever barato, mas menos preciso, busca candidatos, e então um mecanismo mais preciso, mas mais caro, reclassifica esses candidatos. Os documentos também podem ser reclassificados com base no tempo, dando mais peso para dados mais recentes.

Recuperação contextual

A idéia por trás da recuperação contextual é aumentar cada chunnk com o contexto relevante para facilitar a recuperação dos pedaços relevantes. Uma simples técnica é alimentar um chunk com metadados, como tags e palavras-chave.

Você também pode alimentar cada chunk com as perguntas que ele pode responder. Se um documento for dividido em vários pedaços, alguns pedaços podem não ter o contexto necessário para ajudar o retriever a entender o que é o chunk. Para evitar isso, você pode aumentar cada chunk com o contexto do documento original, como o título e seu resumo.

RAG além de textos

Fontes de dados externas também podem ser dados multimodais e tabulares.

RAG multimodal

Usarei imagens nos exemplos para manter a redação concisa, mas você pode substituir imagens por qualquer outra modalidade. Dada uma consulta, o retriever busca textos e imagens relevantes para ele.

Se as imagens tiverem metadados – como títulos, tags e legendas – elas podem ser recuperadas usando os metadados. Por exemplo, uma imagem é recuperada se sua legenda for considerada relevante para a consulta.

Se você deseja recuperar imagens com base no conteúdo delas, precisará ter uma maneira de comparar imagens com consultas. Se consultas forem textos, você precisará um modelo de embedding multimodal que pode gerar embeddings de imagens e textos.

RAG com dados tabulares

O fluxo de trabalho para aumentar um contexto usando dados tabulares é significativamente diferente do fluxo de trabalho clássico do RAG.

Para executar esse fluxo de trabalho, seu sistema deve ter a capacidade de gerar e executar a consulta SQL:

  1. Conversão de texto para SQL: com base na consulta do usuário e nos esquemas de tabela fornecidos, determine qual consulta SQL é necessária. A conversão de texto para SQL é um exemplo de análise semântica.
  2. Execução de SQL: execute a consulta SQL.
  3. Geração: gere uma resposta com base no resultado SQL e na consulta original do usuário.

Na segunda parte deste artigo, apresentaremos o conceito de Agentes. Até lá!

Referências

Huyen, Chip. AI Engineering: Building Applications with Foundation Models

Comments

Comments (0)

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Previous
Next
Back To Top