Vai al contenuto principale

Agentic RAG: tutorial passo dopo passo con progetto demo

Scopri come creare da zero una pipeline Agentic RAG, integrando fonti dati locali e web scraping per generare risposte contestuali alle richieste degli utenti.
Aggiornato 3 giu 2026  · 12 min leggi

Quante volte vorresti che la tua IA potesse fare più che ripetere ciò che già sa? Da persona che ha passato innumerevoli ore a perfezionare e mettere a punto sistemi di IA, posso dirti che i modelli tradizionali, per quanto impressionanti, spesso sembrano bloccati in una bolla limitata a ciò su cui sono stati addestrati. È qui che entra in gioco Agentic RAG.

Nell’Agentic RAG, le capacità decisionali dell’agentic AI incontrano l’adattabilità della retrieval-augmented generation (RAG). Insieme, formano un sistema che può accedere autonomamente alle informazioni, ragionarci sopra e generare contenuti pertinenti.

In precedenza ho scritto un articolo su Agentic RAG che ti aiuterà a capire tutto dal punto di vista teorico. Questo articolo sarà più pratico, quindi concentriamoci su quello.

Panoramica del progetto Agentic RAG

Ti guiderò passo dopo passo in quello che stiamo costruendo. L’obiettivo è creare una pipeline RAG basata sull’architettura mostrata qui:

panoramica progetto demo agentic rag

Passo 1: Richiesta dell’utente

Che si tratti di una domanda semplice o di un problema complesso, tutto parte da una richiesta dell’utente. Questa è la scintilla che mette in moto la nostra pipeline.

Passo 2: Instradamento della richiesta

Poi il sistema verifica: Posso rispondere a questo?

Sì? Attrae dalla conoscenza esistente e consegna subito la risposta.

No? È ora di scavare più a fondo! La richiesta viene inoltrata al passo successivo.

Passo 3: Recupero dei dati

Se la risposta non è immediatamente disponibile, la pipeline esplora due possibili fonti:

  1. Documenti locali: useremo un PDF pre-elaborato come base di conoscenza, in cui il sistema cerca parti di informazione pertinenti.
  2. Ricerca su Internet: se serve più contesto, la pipeline può attingere a fonti esterne per recuperare informazioni aggiornate.

Passo 4: Costruzione del contesto

I dati recuperati, che provengano dal PDF o dal web, vengono poi assemblati in un contesto coerente. È come raccogliere tutti i pezzi del puzzle prima di metterli insieme.

Passo 5: Generazione della risposta finale

Infine, questo contesto viene passato a un large language model (LLM) per comporre una risposta chiara e accurata. Non si tratta solo di recuperare dati, ma di capirli e presentarli nel modo migliore.

Alla fine, avremo una pipeline RAG intelligente ed efficiente che può rispondere dinamicamente alle richieste con contesto reale.

Fonte dati locale

Per iniziare, userò un documento reale come fonte dati locale. Il documento con cui lavoreremo è nientemeno che Generative AI Principles. È pieno di spunti utili, quindi sarà il caso di test perfetto per la nostra pipeline. Ti servirà anche un riassunto del file che uso a scopo di instradamento. Puoi scaricare il file di riepilogo da qui.

Ecco tutto ciò che ti serve per iniziare, passo dopo passo.

Prerequisiti

Prima di partire, ci sono un paio di cose che devi avere pronte:

  1. Groq API key: ti serve una chiave API di Groq, che puoi ottenere qui: Groq API Console
  2. Gemini API key: Gemini ti aiuterà nell’orchestrazione con agenti, Groq dà risposte super rapide ma potrebbe bloccarsi con i limiti di rate attuali: Gemini API Console
  3. Serper.dev API key: per qualsiasi funzionalità di ricerca su Internet useremo Serper.dev. Ottieni qui la tua chiave API: Serper.dev API Key

Installazione dei pacchetti

Assicuriamoci di avere gli strumenti giusti installati. Apri il terminale ed esegui i seguenti comandi per installare i pacchetti Python necessari:

pip install langchain-groq faiss-cpu crewai serper pypdf2 python-dotenv setuptools sentence-transformers huggingface distutils`

Configurazione dell’ambiente

Una volta che hai chiavi e pacchetti pronti, sei a posto! Ti consiglio di salvare le chiavi API in un file .env o in modo sicuro nel codice. Ecco un esempio di come potrebbe apparire il file .env:

import os from dotenv 
import load_dotenv from langchain.vectorstores 
import FAISS from langchain.document_loaders 
import PyPDFLoader 
from langchain.text_splitter import RecursiveCharacterTextSplitter 
from langchain_huggingface.embeddings import HuggingFaceEmbeddings 
from langchain_groq import ChatGroq 
from crewai_tools import SerperDevTool 
from crewai import Agent, Task, Crew, LLM

load_dotenv() 
GROQ_API_KEY = os.getenv("GROQ_API_KEY") 
SERPER_API_KEY = os.getenv("SERPER_API_KEY")
GEMINI=os.getenv("GEMINI")

Ho caricato le variabili d’ambiente per gestire in sicurezza chiavi API e dati sensibili senza inserirli hardcoded. Questo mantiene lo script sicuro e mi permette di cambiare le chiavi in un unico posto (file .env) se necessario.

In poche parole, ecco cosa fanno questi pacchetti e funzioni importati:

  • os: fornisce utilità per interfacciarsi con il sistema operativo.
  • dotenv: carica variabili d’ambiente dai file .env.
  • FAISS: archivio vettoriale per ricerca per similarità e retrieval.
  • PyPDFLoader: estrae contenuti testuali da documenti PDF.
  • RecursiveCharacterTextSplitter: divide il testo in parti gestibili in modo ricorsivo.
  • HuggingFaceEmbeddings: genera embedding di testo usando modelli HuggingFace.
  • ChatGroq: interfaccia chat con accelerazione hardware Groq.
  • SerperDevTool: strumento per effettuare ricerche sul web.
  • ScrapeWebsiteTool: estrae dati dalle pagine web.
  • Agent: definisce un’entità IA con ruoli e obiettivi.
  • Task: rappresenta un obiettivo o un’azione specifica per un agente.
  • Crew: orchestra agenti e task per workflow coordinati.
  • LLM: interfaccia con large language model per generare risposte testuali.

Inizializzazione degli LLM

Iniziamo configurando due modelli linguistici:

  • llm: per compiti generali come instradamento e generazione di risposte. Useremo il modello llama-3.3-70b-specdec.
  • crew_llm: specifico per l’agente di web scraping, dato che richiede una configurazione leggermente diversa (come la temperatura per output più creativi). Useremo il modello gemini/gemini-1.5-flash.
# Initialize LLM
llm = ChatGroq(
    model="llama-3.3-70b-specdec",
    temperature=0,
    max_tokens=500,
    timeout=None,
    max_retries=2,
)

crew_llm = LLM(
    model="gemini/gemini-1.5-flash",
    api_key=GEMINI,
    max_tokens=500,
    temperature=0.7
)

Aggiungere il decisore

La funzione check_local_knowledge() qui sotto funge da decisore. Ho creato un prompt in cui ho passato la richiesta dell’utente e un po’ di contesto locale (dal PDF). Il modello risponde con "Yes" o "No", indicando se ha abbastanza informazioni locali per rispondere alla richiesta. In caso contrario, ricorreremo al web scraping.

def check_local_knowledge(query, context):
    """Router function to determine if we can answer from local knowledge"""
    prompt = '''Role: Question-Answering Assistant
Task: Determine whether the system can answer the user's question based on the provided text.
Instructions:
    - Analyze the text and identify if it contains the necessary information to answer the user's question.
    - Provide a clear and concise response indicating whether the system can answer the question or not.
    - Your response should include only a single word. Nothing else, no other text, information, header/footer. 
Output Format:
    - Answer: Yes/No
Study the below examples and based on that, respond to the last question. 
Examples:
    Input: 
        Text: The capital of France is Paris.
        User Question: What is the capital of France?
    Expected Output:
        Answer: Yes
    Input: 
        Text: The population of the United States is over 330 million.
        User Question: What is the population of China?
    Expected Output:
        Answer: No
    Input:
        User Question: {query}
        Text: {text}
'''
    formatted_prompt = prompt.format(text=context, query=query)
    response = llm.invoke(formatted_prompt)
    return response.content.strip().lower() == "yes"

Agente di ricerca e scraping web

Successivamente, configuriamo un agente di ricerca e scraping web usando la crewai library. Questo agente usa uno strumento di ricerca (SerperDevTool) per trovare articoli correlati alla richiesta dell’utente. La descrizione del task assicura che l’agente sappia cosa recuperare e ne riassuma i contenuti rilevanti. È come inviare un lavoratore specializzato a prelevare dati da Internet.

Poi, la funzione get_web_content() esegue l’agente di web scraping. Invia la query come input e recupera un riassunto conciso dell’articolo più pertinente. Restituisce il risultato grezzo, che diventa il nostro contesto se il router decide che non abbiamo abbastanza informazioni locali.

def setup_web_scraping_agent():
    """Setup the web scraping agent and related components"""
    search_tool = SerperDevTool()  # Tool for performing web searches
    scrape_website = ScrapeWebsiteTool()  # Tool for extracting data from websites
    
    # Define the web search agent
    web_search_agent = Agent(
        role="Expert Web Search Agent",
        goal="Identify and retrieve relevant web data for user queries",
        backstory="An expert in identifying valuable web sources for the user's needs",
        allow_delegation=False,
        verbose=True,
        llm=crew_llm
    )
    
    # Define the web scraping agent
    web_scraper_agent = Agent(
        role="Expert Web Scraper Agent",
        goal="Extract and analyze content from specific web pages identified by the search agent",
        backstory="A highly skilled web scraper, capable of analyzing and summarizing website content accurately",
        allow_delegation=False,
        verbose=True,
        llm=crew_llm
    )
    
    # Define the web search task
    search_task = Task(
        description=(
            "Identify the most relevant web page or article for the topic: '{topic}'. "
            "Use all available tools to search for and provide a link to a web page "
            "that contains valuable information about the topic. Keep your response concise."
        ),
        expected_output=(
            "A concise summary of the most relevant web page or article for '{topic}', "
            "including the link to the source and key points from the content."
        ),
        tools=[search_tool],
        agent=web_search_agent,
    )
    
    # Define the web scraping task
    scraping_task = Task(
        description=(
            "Extract and analyze data from the given web page or website. Focus on the key sections "
            "that provide insights into the topic: '{topic}'. Use all available tools to retrieve the content, "
            "and summarize the key findings in a concise manner."
        ),
        expected_output=(
            "A detailed summary of the content from the given web page or website, highlighting the key insights "
            "and explaining their relevance to the topic: '{topic}'. Ensure clarity and conciseness."
        ),
        tools=[scrape_website],
        agent=web_scraper_agent,
    )
    
    # Define the crew to manage agents and tasks
    crew = Crew(
        agents=[web_search_agent, web_scraper_agent],
        tasks=[search_task, scraping_task],
        verbose=1,
        memory=False,
    )
    return crew

def get_web_content(query):
    """Get content from web scraping"""
    crew = setup_web_scraping_agent()
    result = crew.kickoff(inputs={"topic": query})
    return result.raw

Creazione del database vettoriale

La funzione setup_vector_db() configura il database vettoriale a partire da un file PDF. Ecco come l’ho fatto, passo dopo passo:

  1. Carica il PDF: ho usato PyPDFLoader per estrarre il contenuto.
  2. Suddividi il testo: ho diviso il contenuto del PDF in chunk più piccoli (1000 caratteri con sovrapposizione di 50) usando RecursiveCharacterTextSplitter. Questo rende i dati gestibili e ricercabili.
  3. Crea il DB vettoriale: ho creato embedding dei chunk usando un modello sentence-transformer e li ho archiviati in un database vettoriale FAISS. Questo mi consente di cercare rapidamente il testo rilevante.

Dopodiché, la funzione get_local_content() interroga il database vettoriale per i 5 chunk più pertinenti alla richiesta dell’utente. Combina questi chunk in una singola stringa di contesto. 

def setup_vector_db(pdf_path):
    """Setup vector database from PDF"""
    # Load and chunk PDF
    loader = PyPDFLoader(pdf_path)
    documents = loader.load()
    
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,
        chunk_overlap=50
    )
    chunks = text_splitter.split_documents(documents)
    
    # Create vector database
    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-mpnet-base-v2"
    )
    vector_db = FAISS.from_documents(chunks, embeddings)
    
    return vector_db

def get_local_content(vector_db, query):
    """Get content from vector database"""
    docs = vector_db.similarity_search(query, k=5)
    return " ".join([doc.page_content for doc in docs])

Generazione della risposta finale

Una volta ottenuto il contesto (da documenti locali o da web scraping), lo passo al modello linguistico insieme alla richiesta dell’utente. L’LLM genera la risposta finale combinando contesto e query in un formato conversazionale.

Ecco il flusso dell’elaborazione principale della richiesta:

  1. Instradamento: uso check_local_knowledge() per decidere se il contenuto del PDF locale ha abbastanza dati per rispondere.
    • Se "Yes", prelevo i chunk rilevanti dal database vettoriale.
    • Se "No", cerco sul web articoli pertinenti.
  2. Risposta finale: una volta ottenuto il contesto, lo passo all’LLM per generare la risposta definitiva.
def generate_final_answer(context, query):
    """Generate final answer using LLM"""
    messages = [
        (
            "system",
            "You are a helpful assistant. Use the provided context to answer the query accurately.",
        ),
        ("system", f"Context: {context}"),
        ("human", query),
    ]
    response = llm.invoke(messages)
    return response.content

def process_query(query, vector_db, local_context):
    """Main function to process user query"""
    print(f"Processing query: {query}")
    
    # Step 1: Check if we can answer from local knowledge
    can_answer_locally = check_local_knowledge(query, local_context)
    print(f"Can answer locally: {can_answer_locally}")
    
    # Step 2: Get context either from local DB or web
    if can_answer_locally:
        context = get_local_content(vector_db, query)
        print("Retrieved context from local documents")
    else:
        context = get_web_content(query)
        print("Retrieved context from web scraping")
    
    # Step 3: Generate final answer
    answer = generate_final_answer(context, query)
    return answer

Infine, mettiamo tutto insieme con la funzione main():

def main():
    # Setup
    pdf_path = "genai-principles.pdf" 
    
    # Initialize vector database
    print("Setting up vector database...")
    vector_db = setup_vector_db(pdf_path)
    
    # Get initial context from PDF for routing
    local_context = get_local_content(vector_db, "")
    
    # Example usage
    query = "What is Agentic RAG?"
    result = process_query(query, vector_db, local_context)
    print("\nFinal Answer:")
    print(result)
if __name__ == "__main__":
    main()

Sopra, abbiamo:

  1. Configurato il database vettoriale: caricato ed elaborato il PDF.
  2. Inizializzato il contesto locale: abbiamo recuperato contenuto generale dal PDF da passare come contesto al router.
  3. Eseguito una query di esempio: abbiamo lanciato una richiesta di prova ("What is Agentic RAG?") e stampato la risposta finale.

Questo è l’output del programma:

Agentic RAG is a technique for building Large Language Model (LLM) powered applications that incorporates AI agents. It is an extension of the traditional Retrieval-Augmented Generation (RAG) approach, which uses an external knowledge source to provide the LLM with relevant context and reduce hallucinations.
In traditional RAG pipelines, the retrieval component is typically composed of an embedding model and a vector database, and the generative component is an LLM. At inference time, the user query is used to run a similarity search over the indexed documents to retrieve the most similar documents to the query and provide the LLM with additional context.
Agentic RAG, on the other hand, introduces AI agents that are designed to interact with the user query and provide additional context to the LLM. These agents can be thought of as "virtual assistants" that help the LLM to better understand the user's intent and retrieve relevant documents.

The key components of Agentic RAG include:
1. AI agents: These are the virtual assistants that interact with the user query and provide additional context to the LLM.
2. Retrieval component: This is the component that retrieves the most similar documents to the user query.
3. Generative component: This is the component that uses the retrieved documents to generate the final output.

Agentic RAG has several benefits, including:
1. Improved accuracy: By providing additional context to the LLM, Agentic RAG can improve the accuracy of the generated output.
2. Enhanced user experience: Agentic RAG can help to reduce the complexity of the user interface and provide a more natural and intuitive experience.
3. Increased flexibility: Agentic RAG can be easily extended to support new use cases and applications.

However, Agentic RAG also has some limitations, including:
1. Increased complexity: Agentic RAG requires additional components and infrastructure, which can increase the complexity of the system.
2. Higher computational requirements: Agentic RAG requires more computational resources to handle the additional complexity of the AI agents and the retrieval component.
3. Training requirements: Agentic RAG requires more data and training to learn the behaviour of the AI agents and the retrieval component.

Spiegazione dell’output

Quando ho chiesto "What is Agentic RAG?", che non era esplicitamente presente nel documento fornito, il sistema ha dimostrato flessibilità e intelligenza. Gli agenti di crewAI hanno instradato correttamente la richiesta, riconoscendo tramite il router che serviva contesto esterno. Gli agenti hanno collaborato per individuare l’articolo più pertinente sul web, analizzarne il contenuto e generare una risposta ben informata.

Il sistema ha fornito una spiegazione chiara dell’Agentic RAG, dei suoi componenti (agenti IA, retrieval e parte generativa), dei benefici (accuratezza, esperienza utente e flessibilità) e dei limiti (complessità, esigenze computazionali e requisiti di training).

Questo mette in evidenza la capacità della pipeline di recuperare dinamicamente il contesto e offrire insight precisi, concisi e utili, anche di fronte a richieste che vanno oltre il dataset iniziale.

Conclusione

La pipeline che abbiamo costruito è un processo dinamico e multi-step che gestisce in modo efficiente le richieste degli utenti combinando conoscenza esistente e fonti esterne.

Il sistema può adattarsi dinamicamente a richieste diverse e fornire risposte accurate e facili da comprendere. Con l’integrazione di dati locali, ricerche live su Internet e un’interfaccia fluida, la pipeline RAG si dimostra una soluzione solida e pratica per casi d’uso reali. Come prossimo passo, prova a costruire qualcosa di tuo seguendo il workflow che abbiamo visto.


Bhavishya Pandit's photo
Author
Bhavishya Pandit
LinkedIn
Twitter

Senior GenAI Engineer e Content Creator che ha totalizzato 20 milioni di visualizzazioni condividendo conoscenze su GenAI e data science.

Argomenti

Impara l’IA con questi corsi!

Programma

Sviluppare applicazioni AI

21 h
Impara a creare applicazioni basate sull'intelligenza artificiale con i più recenti strumenti per sviluppatori di AI, tra cui OpenAI API, Hugging Face e LangChain.
Vedi dettagliRight Arrow
Inizia il corso
Mostra altroRight Arrow
Correlato

blog

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

blog

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

blog

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

Mostra altroMostra altro