Como Construir um Sistema RAG em 10 Linhas de Python sem Frameworks

Desbloqueie o poder do RAG (Retrieval Augmented Generation) com este tutorial de código Python de 10 linhas. Mergulhe na recuperação de informações, construa seu próprio sistema de documentos e aproveite os LLMs para respostas robustas - tudo isso sem depender de estruturas complexas. Otimize seu conteúdo para SEO e engajamento.

24 de fevereiro de 2025

party-gif

Descubra como construir um poderoso sistema de geração aumentada por recuperação (RAG) do zero em apenas 10 linhas de código Python, sem depender de nenhuma estrutura externa. Esta abordagem concisa, porém abrangente, fornece a você um profundo entendimento dos componentes principais, capacitando-o a criar soluções robustas e personalizáveis ​​acionadas por IA para suas aplicações baseadas em documentos.

Entenda o Conceito de Geração Aumentada por Recuperação (RAG)

Retrieval Augmented Generation (RAG) é uma técnica usada para recuperação de informações de seus próprios documentos. Envolve um processo de duas etapas:

  1. Criação da Base de Conhecimento: Os documentos são divididos em sub-documentos menores, e os embeddings são calculados para cada trecho. Esses embeddings e os trechos originais são armazenados, geralmente em um armazenamento de vetores.

  2. Recuperação e Geração: Quando uma nova consulta do usuário chega, um embedding é calculado para a consulta. Os trechos mais relevantes são recuperados comparando o embedding da consulta com os embeddings dos trechos armazenados. Os trechos recuperados são então anexados à consulta original e enviados para um modelo de linguagem (LLM) para gerar a resposta final.

Essa configuração é chamada de pipeline de "geração com recuperação aumentada", pois a geração do LLM é aumentada com as informações relevantes recuperadas da base de conhecimento.

Aprenda a Dividir Documentos em Parágrafos

Ao construir um sistema de geração com recuperação aumentada (RAG), uma das etapas-chave é dividir os documentos de entrada em pedaços menores e mais gerenciáveis. Neste exemplo, estamos dividindo o documento de entrada (um artigo da Wikipédia) em parágrafos.

A lógica por trás dessa abordagem é que os parágrafos geralmente representam unidades lógicas de informação que podem ser efetivamente recuperadas e usadas para aumentar a consulta do usuário. Ao dividir o documento dessa maneira, podemos identificar melhor as seções mais relevantes a serem incluídas na resposta final.

O código para essa etapa é o seguinte:

# Dividir o documento em parágrafos
chunks = [paragraph.strip() for paragraph in text.split('\n') if paragraph.strip()]

Aqui, usamos uma abordagem simples de dividir o texto de entrada em caracteres de nova linha para extrair os parágrafos individuais. Em seguida, removemos quaisquer espaços em branco iniciais ou finais de cada parágrafo para garantir uma representação limpa.

A lista chunks resultante contém os parágrafos individuais, que podem então ser incorporados e armazenados no armazenamento de vetores. Isso nos permite recuperar eficientemente os parágrafos mais relevantes com base na consulta do usuário e incluí-los na resposta final gerada pelo modelo de linguagem.

Incorpore Documentos e Consultas de Usuário Usando Transformadores de Frases

Para incorporar os trechos do documento e a consulta do usuário, usaremos a biblioteca Sentence Transformers. Essa biblioteca fornece modelos pré-treinados que podem gerar incorporações de alta qualidade para o texto.

Primeiro, carregamos o modelo de incorporação:

from sentence_transformers import SentenceTransformer
embedding_model = SentenceTransformer('all-mpnet-base-v2')

Em seguida, calculamos as incorporações para os trechos do documento:

chunk_embeddings = embedding_model.encode(chunks, normalize_embeddings=True)

Aqui, chunks é uma lista dos trechos de texto do documento. A opção normalize_embeddings=True garante que as incorporações sejam normalizadas, o que é importante para o cálculo de similaridade posteriormente.

Para obter a incorporação da consulta do usuário, basta executar:

query_embedding = embedding_model.encode([user_query], normalize_embeddings=True)[0]

Agora, temos as incorporações dos trechos do documento e da consulta do usuário, que podemos usar para a etapa de recuperação.

Recupere Trechos Relevantes com Base na Similaridade de Cosseno

Para recuperar os trechos mais relevantes com base na consulta do usuário, primeiro precisamos calcular a similaridade do cosseno entre a incorporação da consulta e as incorporações dos trechos do documento. Aqui está como podemos fazer isso:

  1. Calcular a incorporação da consulta do usuário usando o mesmo modelo de incorporação dos trechos do documento.
  2. Calcular o produto escalar entre a incorporação da consulta e cada uma das incorporações dos trechos do documento. Isso nos dá os escores de similaridade do cosseno.
  3. Classificar os trechos do documento com base nos escores de similaridade do cosseno e selecionar os K trechos mais relevantes.
  4. Recuperar o conteúdo de texto dos trechos selecionados para usar como contexto para o modelo de linguagem.

As etapas-chave são:

  1. Calcular a incorporação da consulta
  2. Calcular os escores de similaridade do cosseno
  3. Classificar e selecionar os K principais trechos
  4. Recuperar o conteúdo do texto do trecho

Essa abordagem simples nos permite recuperar de forma eficiente as informações mais relevantes da coleção de documentos para aumentar a consulta do usuário antes de passá-la para o modelo de linguagem para geração.

Aumente a Consulta do Usuário com os Trechos Recuperados e Gere a Resposta Usando o OpenAI GPT-4

Para gerar uma resposta usando os trechos recuperados e a consulta do usuário, primeiro criamos um prompt que inclui os trechos relevantes e a consulta do usuário. Em seguida, usamos o modelo OpenAI GPT-4 para gerar uma resposta com base nesse prompt.

Aqui está o código:

# Obter a consulta do usuário
user_query = "What is the capital of France?"

# Recuperar os trechos mais relevantes com base na consulta do usuário
top_chunk_ids = [6, 8, 5]
top_chunks = [chunks[i] for i in top_chunk_ids]

# Criar o prompt
prompt = "Use o seguinte contexto para responder à pergunta no final. Se você não souber a resposta, diga que não sabe e não tente inventar uma resposta.\n\nContexto:\n"
for chunk in top_chunks:
    prompt += chunk + "\n\n"
prompt += f"\nPergunta: {user_query}"

# Gerar a resposta usando o OpenAI GPT-4
response = openai_client.chat.create(
    model="gpt-4",
    messages=[
        {"role": "user", "content": prompt}
    ],
    max_tokens=1024,
    n=1,
    stop=None,
    temperature=0.7,
).choices[0].message.content

print(response)

Neste código, primeiro recuperamos os trechos mais relevantes com base na consulta do usuário, encontrando os 3 principais trechos com os escores de similaridade mais altos. Em seguida, criamos um prompt que inclui esses trechos e a consulta do usuário. Finalmente, usamos o modelo OpenAI GPT-4 para gerar uma resposta com base nesse prompt.

A resposta gerada será uma resposta concisa e relevante à consulta do usuário, aproveitando as informações dos trechos recuperados.

Conclusão

Neste tutorial, aprendemos a construir um sistema de bate-papo completo com um sistema de recuperação de documentos usando apenas 10 linhas de código Python. Cobrimos os principais componentes de um pipeline de Geração Aumentada por Recuperação (RAG), incluindo a criação da base de conhecimento, divisão de documentos, incorporação, recuperação e geração usando um modelo de linguagem de grande porte.

As principais conclusões são:

  1. Você pode implementar um pipeline RAG básico sem a necessidade de estruturas complexas como Langchain ou LlamaIndex. Python puro e algumas bibliotecas são suficientes.
  2. Dividir seus documentos com base na estrutura (por exemplo, parágrafos) é uma estratégia simples, mas eficaz, para a maioria dos casos de uso.
  3. Incorporar os trechos do documento e a consulta do usuário, em seguida, calcular os escores de similaridade, permite que você recupere as informações mais relevantes para aumentar a consulta do usuário.
  4. Integrar os trechos recuperados com a consulta do usuário e enviá-los para um modelo de linguagem de grande porte permite a geração de uma resposta relevante e informativa.

Embora este exemplo forneça uma base sólida, existem muitas oportunidades para construir sistemas RAG mais robustos e avançados. Estruturas como Langchain e LlamaIndex podem ser úteis ao se integrar com vários armazenamentos de vetores e modelos de linguagem. No entanto, começar com uma implementação em Python puro pode ajudá-lo a entender melhor os conceitos e componentes principais de um pipeline RAG.

Se você estiver interessado em explorar técnicas RAG mais avançadas, recomendo verificar meu curso "RAG Beyond Basics", que se aprofunda na construção de sistemas RAG complexos e prontos para produção.

Perguntas frequentes