Pular para o conteúdo principal

Llama 4 com RAG: Um guia com projeto de demonstração

Saiba como criar um pipeline de geração aumentada por recuperação (RAG) usando o Llama 4 para criar um aplicativo da Web simples.
Atualizado 14 de abr. de 2025  · 12 min lido

O Llama 4 Scout é comercializado como tendo uma janela de contexto maciço de 10 milhões de tokens, mas seu treinamento foi limitado a um tamanho máximo de entrada de 256 mil tokens. Isso significa que o desempenho pode se degradar com entradas maiores. Para evitar isso, podemos usar o Llama4 com um pipeline de geração aumentada por recuperação (RAG).

Neste tutorial, explicarei passo a passo como criar um pipeline RAG usando o ecossistema LangChain e criar um aplicativo da Web que permita aos usuários carregar documentos e fazer perguntas sobre eles.

Mantemos nossos leitores atualizados sobre as últimas novidades em IA enviando o The Median, nosso boletim informativo gratuito de sexta-feira que detalha as principais histórias da semana. Inscreva-se e fique atento em apenas alguns minutos por semana:

Por que usar o Llama 4 com o RAG?

O Llama 4 vem com uma janela de contexto de 10 milhões de tokens, o que significa que podemos alimentá-lo com 100 livros e receber respostas contextuais sobre eles. No entanto, apesar desse recurso impressionante, há motivos convincentes para você combinar o Llama 4 com o RAG. Aqui está o motivo:

1. Limitações de desempenho com grandes entradas

Embora o Llama 4 seja compatível com uma janela de contexto enorme, seu treinamento foi limitado a um tamanho máximo de entrada de 256 mil tokens. O desempenho do modelo pode se degradar quando as entradas se aproximam ou excedem esse limite. O RAG atenua esse problema recuperando apenas as informações mais relevantes, garantindo que a entrada permaneça concisa e focada.

2. Eficiência e foco

O RAG permite a recuperação de informações altamente relevantes de um banco de dados vetorialpermitindo que a Llama 4 processe entradas menores e mais direcionadas. Essa abordagem melhora a eficiência e a precisão.

3. Economia de custos

O processamento de entradas massivas com a janela de contexto total do Llama 4 pode ser computacionalmente caro. Ao usar o RAG, somente os dados mais relevantes são recuperados e processados, reduzindo significativamente a carga computacional e os custos associados.

Criação do pipeline do Llama 4 RAG

Neste tutorial, explicarei passo a passo como criar um pipeline simples de geração aumentada por recuperação que permite que você carregue um arquivo .docx e faça perguntas sobre seu conteúdo.

1. Configuração

Primeiro, precisamos criar um arquivo GroqCloud e salvá-la como uma variável de ambiente variável de ambiente. Em seguida, instalaremos todos os pacotes Python necessários para carregar, dividir, vetorizar e recuperar o contexto, bem como gerar respostas.

%%capture
%pip install langchain
%pip install langchain-community 
%pip install langchainhub 
%pip install langchain-chroma 
%pip install langchain-groq
%pip install langchain-huggingface
%pip install unstructured[docx]

2. Iniciando o LLM e a incorporação

Configuraremos o objeto de modelo usando a API do Groq, fornecendo a ele o nome do modelo e a chave da API. Da mesma forma, faremos o download do modelo de incorporação do Hugging Face e o carregaremos como nosso modelo de incorporação.

from langchain_groq import ChatGroq
from langchain_huggingface import HuggingFaceEmbeddings

llm = ChatGroq(model="meta-llama/llama-4-scout-17b-16e-instruct", api_key=groq_api_key)

embed_model = HuggingFaceEmbeddings(model_name="mixedbread-ai/mxbai-embed-large-v1")

3. Carregando e dividindo os dados

Carregaremos o arquivo .docx da pasta raiz e o dividiremos em partes de 1.000 caracteres.

from langchain_community.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# Initialize the text splitter
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=100,
    separators=["\n\n", "\n"]
)

# Load the .docx files
loader = DirectoryLoader("./", glob="*.docx", use_multithreading=True)
documents = loader.load()

# Split the documents into chunks
chunks = text_splitter.split_documents(documents)

# Print the number of chunks
print(len(chunks))
29

4. Criar e preencher o armazenamento de vetores

Inicialize o armazenamento do vetor armazenamento de vetores do Chroma e forneça os blocos de texto ao banco de dados de vetores. O texto será convertido em embeddings antes de ser armazenado. Depois disso, executaremos uma pesquisa de similaridade para testar se nosso armazenamento de vetores está preenchido corretamente.

from langchain_chroma import Chroma

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embed_model,
    persist_directory="./Vectordb",
)

query = "What is this tutorial about?"
docs = vectorstore.similarity_search(query)
print(docs[0].page_content)
Learn how to Fine-tune Stable Diffusion XL with DreamBooth and LoRA on your personal images. 

Let's try another prompt:

Prompt:

5. Criação do pipeline do RAG

Em seguida, converteremos nosso armazenamento de vetores em um retriever e criaremos um modelo de prompt para o pipeline RAG.

# Create retriever
retriever = vectorstore.as_retriever()

# Import PromptTemplate
from langchain_core.prompts import PromptTemplate

# Define a clearer, more professional prompt template
template = """You are an expert assistant tasked with answering questions based on the provided documents.
Use only the given context to generate your answer.
If the answer cannot be found in the context, clearly state that you do not know.
Be detailed and precise in your response, but avoid mentioning or referencing the context itself.

Context:
{context}

Question:
{question}

Answer:"""

# Create the PromptTemplate
rag_prompt = PromptTemplate.from_template(template)

Por fim, criaremos a cadeia RAG que fornece o contexto e a pergunta no prompt RAG, passaremos pelo modelo Llama 4 e, em seguida, geraremos uma resposta limpa.

from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | rag_prompt
    | llm
    | StrOutputParser()
)

Agora, perguntaremos à cadeia RAG sobre o documento.

from IPython.display import display, Markdown

response = rag_chain.invoke("What this tutorial about?")
Markdown(response)
This tutorial is about setting up and using the Janus project, specifically Janus Pro, a multimodal model that can understand images and generate images from text prompts, and building a local solution to use the model privately on a laptop GPU. It covers learning about the Janus Series, setting up the Janus project, building a Docker container to run the model locally, and testing its capabilities with various image and text prompts.

Como você pode ver, a resposta é altamente precisa e sensível ao contexto. Se você tiver algum problema ao executar o código acima, consulte esta pasta de trabalho do DataLab: Llama 4 RAG pipeline.

Criação do aplicativo de bate-papo Llama 4 Docx

Nesta seção, transformaremos o código do pipeline RAG discutido anteriormente em um aplicativo de bate-papo funcional usando o pacote Gradio. Para começar, você precisa instalar o Gradio usando o seguinte comando pip:

$ pip install gradio

O aplicativo foi projetado para ser simples e fácil de usar, permitindo que os usuários carreguem um único arquivo .docx, vários arquivos .docx ou um arquivo .zip contendo arquivos .docx. Os usuários podem então fazer perguntas sobre o conteúdo de todos os arquivos carregados, fornecendo uma maneira rápida e eficiente de recuperar informações.

Os recursos do aplicativo:

  • Carregamento de documentos: O aplicativo extrai e carrega documentos diretamente dos arquivos carregados.
  • Divisão de texto: Os documentos são divididos em partes gerenciáveis para processamento e recuperação eficientes.
  • Integração com a loja de vetores: Em vez do ChromaDB, ele usa o armazenamento de vetores na memória para gerenciar os embeddings de documentos.
  • Pipeline RAG: Combina a recuperação de documentos com um modelo de linguagem para fornecer respostas precisas com base no conteúdo carregado.
  • Interface do usuário: Apresenta uma interface de bate-papo em que os usuários podem fazer perguntas e receber respostas detalhadas com base nos documentos fornecidos.
  • Tratamento de erros: Fornece feedback sobre tipos de arquivos não suportados e problemas durante o upload de arquivos, aprimorando a experiência do usuário.
  • Redefinir a funcionalidade: Os usuários podem redefinir o estado do aplicativo para começar do zero com novos uploads de documentos.

Vamos juntar tudo isso:

main.py:

# ========== Standard Library ==========
import os
import tempfile
import zipfile
from typing import List, Optional, Tuple, Union
import collections


# ========== Third-Party Libraries ==========
import gradio as gr
from groq import Groq
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import DirectoryLoader, UnstructuredFileLoader
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_groq import ChatGroq
from langchain_huggingface import HuggingFaceEmbeddings

# ========== Configs ==========
TITLE = """<h1 align="center">🗨️🦙 Llama 4 Docx Chatter</h1>"""
AVATAR_IMAGES = (
    None,
    "./logo.png",
)

# Acceptable file extensions
TEXT_EXTENSIONS = [".docx", ".zip"]

# ========== Models & Clients ==========
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
client = Groq(api_key=GROQ_API_KEY)
llm = ChatGroq(model="meta-llama/llama-4-scout-17b-16e-instruct", api_key=GROQ_API_KEY)
embed_model = HuggingFaceEmbeddings(model_name="mixedbread-ai/mxbai-embed-large-v1")

# ========== Core Components ==========
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=100,
    separators=["\n\n", "\n"],
)

rag_template = """You are an expert assistant tasked with answering questions based on the provided documents.
Use only the given context to generate your answer.
If the answer cannot be found in the context, clearly state that you do not know.
Be detailed and precise in your response, but avoid mentioning or referencing the context itself.
Context:
{context}
Question:
{question}
Answer:"""
rag_prompt = PromptTemplate.from_template(rag_template)


# ========== App State ==========
class AppState:
    vectorstore: Optional[InMemoryVectorStore] = None
    rag_chain = None


state = AppState()

# ========== Utility Functions ==========


def load_documents_from_files(files: List[str]) -> List:
    """Load documents from uploaded files directly without moving."""
    all_documents = []

    # Temporary directory if ZIP needs extraction
    with tempfile.TemporaryDirectory() as temp_dir:
        for file_path in files:
            ext = os.path.splitext(file_path)[1].lower()

            if ext == ".zip":
                # Extract ZIP inside temp_dir
                with zipfile.ZipFile(file_path, "r") as zip_ref:
                    zip_ref.extractall(temp_dir)

                # Load all docx from extracted zip
                loader = DirectoryLoader(
                    path=temp_dir,
                    glob="**/*.docx",
                    use_multithreading=True,
                )
                docs = loader.load()
                all_documents.extend(docs)

            elif ext == ".docx":
                # Load single docx directly
                loader = UnstructuredFileLoader(file_path)
                docs = loader.load()
                all_documents.extend(docs)

    return all_documents


def get_last_user_message(chatbot: List[Union[gr.ChatMessage, dict]]) -> Optional[str]:
    """Get last user prompt."""
    for message in reversed(chatbot):
        content = (
            message.get("content") if isinstance(message, dict) else message.content
        )
        if (
            message.get("role") if isinstance(message, dict) else message.role
        ) == "user":
            return content
    return None


# ========== Main Logic ==========




def upload_files(
    files: Optional[List[str]], chatbot: List[Union[gr.ChatMessage, dict]]
):
    """Handle file upload - .docx or .zip containing docx."""
    if not files:
        return chatbot

    file_summaries = []  # <-- Collect formatted file/folder info
    documents = []

    with tempfile.TemporaryDirectory() as temp_dir:
        for file_path in files:
            filename = os.path.basename(file_path)
            ext = os.path.splitext(file_path)[1].lower()

            if ext == ".zip":
                file_summaries.append(f"📦 **{filename}** (ZIP file) contains:")
                try:
                    with zipfile.ZipFile(file_path, "r") as zip_ref:
                        zip_ref.extractall(temp_dir)
                        zip_contents = zip_ref.namelist()

                        # Group files by folder
                        folder_map = collections.defaultdict(list)
                        for item in zip_contents:
                            if item.endswith("/"):
                                continue  # skip folder entries themselves
                            folder = os.path.dirname(item)
                            file_name = os.path.basename(item)
                            folder_map[folder].append(file_name)

                        # Format nicely
                        for folder, files_in_folder in folder_map.items():
                            if folder:
                                file_summaries.append(f"📂 {folder}/")
                            else:
                                file_summaries.append(f"📄 (root)")
                            for f in files_in_folder:
                                file_summaries.append(f"   - {f}")

                    # Load docx files extracted from ZIP
                    loader = DirectoryLoader(
                        path=temp_dir,
                        glob="**/*.docx",
                        use_multithreading=True,
                    )
                    docs = loader.load()
                    documents.extend(docs)

                except zipfile.BadZipFile:
                    chatbot.append(
                        gr.ChatMessage(
                            role="assistant",
                            content=f"❌ Failed to open ZIP file: {filename}",
                        )
                    )

            elif ext == ".docx":
                file_summaries.append(f"📄 **{filename}**")
                loader = UnstructuredFileLoader(file_path)
                docs = loader.load()
                documents.extend(docs)

            else:
                file_summaries.append(f"❌ Unsupported file type: {filename}")

    if not documents:
        chatbot.append(
            gr.ChatMessage(
                role="assistant", content="No valid .docx files found in upload."
            )
        )
        return chatbot

    # Split documents
    chunks = text_splitter.split_documents(documents)
    if not chunks:
        chatbot.append(
            gr.ChatMessage(
                role="assistant", content="Failed to split documents into chunks."
            )
        )
        return chatbot

    # Create Vectorstore
    state.vectorstore = InMemoryVectorStore.from_documents(
        documents=chunks,
        embedding=embed_model,
    )
    retriever = state.vectorstore.as_retriever()

    # Build RAG Chain
    state.rag_chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | rag_prompt
        | llm
        | StrOutputParser()
    )

    # Final display
    chatbot.append(
        gr.ChatMessage(
            role="assistant",
            content="**Uploaded Files:**\n"
            + "\n".join(file_summaries)
            + "\n\n✅ Ready to chat!",
        )
    )
    return chatbot


def user_message(
    text_prompt: str, chatbot: List[Union[gr.ChatMessage, dict]]
) -> Tuple[str, List[Union[gr.ChatMessage, dict]]]:
    """Add user's text input to conversation."""
    if text_prompt.strip():
        chatbot.append(gr.ChatMessage(role="user", content=text_prompt))
    return "", chatbot


def process_query(
    chatbot: List[Union[gr.ChatMessage, dict]],
) -> List[Union[gr.ChatMessage, dict]]:
    """Process user's query through RAG pipeline."""
    prompt = get_last_user_message(chatbot)
    if not prompt:
        chatbot.append(
            gr.ChatMessage(role="assistant", content="Please type a question first.")
        )
        return chatbot

    if state.rag_chain is None:
        chatbot.append(
            gr.ChatMessage(role="assistant", content="Please upload documents first.")
        )
        return chatbot

    chatbot.append(gr.ChatMessage(role="assistant", content="Thinking..."))

    try:
        response = state.rag_chain.invoke(prompt)
        chatbot[-1].content = response
    except Exception as e:
        chatbot[-1].content = f"Error: {str(e)}"

    return chatbot


def reset_app(
    chatbot: List[Union[gr.ChatMessage, dict]],
) -> List[Union[gr.ChatMessage, dict]]:
    """Reset application state."""
    state.vectorstore = None
    state.rag_chain = None
    return [
        gr.ChatMessage(
            role="assistant", content="App reset! Upload new documents to start."
        )
    ]


# ========== UI Layout ==========

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.HTML(TITLE)
    chatbot = gr.Chatbot(
        label="Llama 4 RAG",
        type="messages",
        bubble_full_width=False,
        avatar_images=AVATAR_IMAGES,
        scale=2,
        height=350,
    )

    with gr.Row(equal_height=True):
        text_prompt = gr.Textbox(
            placeholder="Ask a question...", show_label=False, autofocus=True, scale=28
        )
        send_button = gr.Button(
            value="Send",
            variant="primary",
            scale=1,
            min_width=80,
        )
        upload_button = gr.UploadButton(
            label="Upload",
            file_count="multiple",
            file_types=TEXT_EXTENSIONS,
            scale=1,
            min_width=80,
        )
        reset_button = gr.Button(
            value="Reset",
            variant="stop",
            scale=1,
            min_width=80,
        )

    send_button.click(
        fn=user_message,
        inputs=[text_prompt, chatbot],
        outputs=[text_prompt, chatbot],
        queue=False,
    ).then(fn=process_query, inputs=[chatbot], outputs=[chatbot])

    text_prompt.submit(
        fn=user_message,
        inputs=[text_prompt, chatbot],
        outputs=[text_prompt, chatbot],
        queue=False,
    ).then(fn=process_query, inputs=[chatbot], outputs=[chatbot])

    upload_button.upload(
        fn=upload_files, inputs=[upload_button, chatbot], outputs=[chatbot], queue=False
    )
    reset_button.click(fn=reset_app, inputs=[chatbot], outputs=[chatbot], queue=False)

demo.queue().launch()

Testando o aplicativo de bate-papo Llama 4 Docx

Agora, executaremos o aplicativo Web e testaremos suas funcionalidades. Para iniciar o aplicativo, execute o seguinte comando no terminal:

$ python main.py 

Quando o servidor da Web estiver em funcionamento, navegue até a URL a seguir ou copie e cole-a manualmente no navegador para acessar a interface da Web: http://127.0.0.1:7860

Esta é a aparência da nossa interface de bate-papo:

Interface de gradiente de lhama 4 rag

Faremos upload de um arquivo .zip contendo arquivos .docx e, ao fazer isso, você verá todas as pastas e arquivos dentro do arquivo zip.

Teste de interface do gradio de lhama 4 rag

Em seguida, podemos fazer perguntas específicas sobre os documentos:

Teste de interface do gradio de lhama 4 rag

Você também pode pressionar o botão "Reset" para carregar um novo arquivo e conversar sobre ele em apenas alguns segundos.

Teste de interface do gradio de lhama 4 rag

Depois de testá-lo localmente, você pode implantar facilmente o aplicativo da Web no Hugging Face Space gratuitamente. Aqui está o link para o aplicativo implantado: kingabzpro/Llama-4-RAG 

Teste de interface do gradio de lhama 4 rag

Fonte: kingabzpro/Llama-4-RAG 

O código-fonte, o notebook e os arquivos de configuração podem ser encontrados no diretório kingabzpro/Llama-4-RAG que você pode encontrar no repositório kingabzpro/Llama-4-RAG.

Conclusão

Mesmo com a introdução de modelos de janelas de contexto longo, o RAG nunca se tornará obsoleto. O RAG continua sendo um método eficiente e econômico para recuperar informações de recursos externos, fornecendo contexto adicional aos modelos.

Além disso, os sistemas RAG são inerentemente mais econômicos para operar em comparação com a alimentação de um contexto extenso diretamente em um modelo, o que exige recursos computacionais significativos. Isso torna o RAG uma solução prática e dimensionável para muitos casos de uso.

Para saber mais sobre a Llama 4, confira estes recursos:


Abid Ali Awan's photo
Author
Abid Ali Awan
LinkedIn
Twitter

Sou um cientista de dados certificado que gosta de criar aplicativos de aprendizado de máquina e escrever blogs sobre ciência de dados. No momento, estou me concentrando na criação e edição de conteúdo e no trabalho com modelos de linguagem de grande porte.

Tópicos

Aprenda IA com estes cursos!

Programa

Llama Fundamentals

0 min
Experiment with Llama 3 to run inference on pre-trained models, fine-tune them on custom datasets, and optimize performance.
Ver detalhesRight Arrow
Iniciar curso
Ver maisRight Arrow
Relacionado

blog

O que é Retrieval Augmented Generation (RAG)?

Saiba como o Retrieval Augmented Generation (RAG) aprimora grandes modelos de linguagem ao integrar fontes de dados externas.
Natassha Selvaraj's photo

Natassha Selvaraj

6 min

Tutorial

RAG With Llama 3.1 8B, Ollama e Langchain: Tutorial

Aprenda a criar um aplicativo RAG com o Llama 3.1 8B usando Ollama e Langchain, configurando o ambiente, processando documentos, criando embeddings e integrando um retriever.
Ryan Ong's photo

Ryan Ong

12 min

Tutorial

Como criar aplicativos LLM com o tutorial LangChain

Explore o potencial inexplorado dos modelos de linguagem grandes com o LangChain, uma estrutura Python de código aberto para criar aplicativos avançados de IA.
Moez Ali's photo

Moez Ali

12 min

Tutorial

Llama.cpp Tutorial: Um guia completo para inferência e implementação eficientes de LLM

Este guia abrangente sobre o Llama.cpp guiará você pelos fundamentos da configuração do seu ambiente de desenvolvimento, compreendendo suas principais funcionalidades e aproveitando seus recursos para solucionar casos de uso no mundo real.
Zoumana Keita 's photo

Zoumana Keita

11 min

Tutorial

Guia para iniciantes do LlaMA-Factory WebUI: Ajuste fino dos LLMs

Saiba como fazer o ajuste fino dos LLMs em conjuntos de dados personalizados, avaliar o desempenho e exportar e servir modelos com facilidade usando a estrutura com pouco ou nenhum código do LLaMA-Factory.
Abid Ali Awan's photo

Abid Ali Awan

12 min

Tutorial

Guia de Introdução ao Ajuste Fino de LLMs

O ajuste fino dos grandes modelos de linguagem (LLMs, Large Language Models) revolucionou o processamento de linguagem natural (PLN), oferecendo recursos sem precedentes em tarefas como tradução de idiomas, análise de sentimentos e geração de textos. Essa abordagem transformadora aproveita modelos pré-treinados como o GPT-2, aprimorando seu desempenho em domínios específicos pelo processo de ajuste fino.
Josep Ferrer's photo

Josep Ferrer

11 min

Ver maisVer mais