AI Assistant with RAG

Link Progetto

Che cos'è il RAG?

La Retrieval-Augmented Generation (RAG) è un paradigma all'avanguardia nell'intelligenza artificiale che combina la potenza generativa dei modelli linguistici con la precisione del recupero di informazioni da fonti esterne. In parole semplici, invece di affidarsi esclusivamente alla conoscenza "memorizzata" durante l'addestramento, un sistema RAG cerca attivamente informazioni rilevanti in un database o in documenti esterni prima di generare una risposta.

Un sistema RAG è composto da tre componenti fondamentali:

  1. Retrieval (Recupero): Il sistema cerca e recupera informazioni pertinenti da una base di conoscenza o documenti specifici.
  2. Augmentation (Arricchimento): Il modello linguistico viene arricchito con le conoscenze specifiche recuperate.
  3. Generation (Generazione): Una risposta coerente e contestuale viene generata basandosi sia sulla conoscenza generale del modello che sulle informazioni recuperate.

Il RAG risolve alcuni dei problemi più critici dei modelli linguistici tradizionali:

  • Riduce le "allucinazioni" ancorando le risposte a fonti concrete
  • Garantisce informazioni aggiornate accedendo a dati recenti
  • Offre trasparenza e verificabilità con citazioni delle fonti
  • Permette la personalizzazione senza dover riaddrestrare l'intero modello
  • Rispetta la privacy mantenendo i dati sensibili nel database dell'utente
  • Fornisce maggiore controllo agli sviluppatori sulle informazioni disponibili
Illustrazione di un cuoco che legge un libro, vicino a un tavolo con un computer che mostra l'applicazione sviluppata.

💡 Vuoi saperne di più sul RAG e come sta rivoluzionando l'interazione con l'AI? Leggi il mio articolo completo sul blog dove approfondisco l'argomento e racconto come questa tecnologia sta trasformando il modo in cui interagiamo con l'intelligenza artificiale.

Introduzione Tecnica al Progetto

Ho sviluppato un'applicazione Streamlit che implementa la tecnologia RAG per creare due strumenti distinti ma complementari:

  1. Document Q&A: Un assistente documentale che permette di caricare file in diversi formati (PDF, DOCX, TXT) e fare domande sul loro contenuto, ricevendo risposte precise e contestuali.
  2. Meal Planner: Un pianificatore di pasti che utilizza ricette in formato JSON per generare piani alimentari personalizzati e liste della spesa ottimizzate.

Questo progetto è nato durante il mio percorso formativo nel bootcamp AI Engineer di Edgemony, ispirato dalle lezioni di Ilyas Chaoua sul RAG. Vediamo in dettaglio l'implementazione tecnica di entrambi i moduli.

Architettura Tecnica del Sistema RAG

L'architettura del sistema RAG implementata segue un flusso di lavoro a quattro fasi principali, ottimizzate per garantire risposte precise, contestuali e verificabili:

1. Indicizzazione e Chunking

Per elaborare documenti e ricette, ho implementato diverse strategie di chunking:

def chunk_document_data(data, chunk_size=512, chunk_overlap=100):
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        length_function=len,
        separators=["\n\n", "\n", ". ", " ", ""]  # Separatori espliciti per un chunking migliore
    )
    chunks = text_splitter.split_documents(data)
    return chunksCode language: PHP (php)

Questo metodo utilizza RecursiveCharacterTextSplitter con una sovrapposizione ottimizzata per mantenere il contesto tra frammenti, un aspetto critico per la comprensione semantica del testo. L'implementazione include separatori specifici per migliorare l'integrità logica dei chunk.

2. Generazione di Embedding e Archiviazione Vettoriale

Gli embedding vengono generati utilizzando il modello di OpenAI e archiviati in un database ChromaDB:

def create_document_embeddings(chunks, api_key=None):
    chroma_path = "/tmp/.chroma"
    os.makedirs(chroma_path, exist_ok=True)
    
    configuration = {
        "client": "PersistentClient",
        "path": chroma_path
    }
    
    conn = st.connection("chromadb", type=ChromadbConnection, **configuration)
    embeddings = OpenAIEmbeddings(api_key=api_key)
    collection_name = "document_collection"
    
    chroma_client = chromadb.PersistentClient(path=chroma_path)
    vector_store = Chroma.from_documents(
        chunks,
        embeddings,
        collection_name=collection_name,
        client=chroma_client
    )
    
    return vector_storeCode language: PHP (php)

3. Retrieval Ottimizzato e 4. Generazione di Risposte

Il sistema implementa diverse strategie di recupero e generazione, che ho adattato specificamente per ciascun modulo dell'applicazione, come vedremo nelle sezioni dedicate.

Document Q&A: Assistente Documentale Interattivo

Gestione di Documenti Multi-Formato

Per il modulo Document Q&A, ho implementato un sistema di caricamento flessibile che supporta diversi formati:

def load_document(file):
    name, extension = os.path.splitext(file)
    
    if extension.lower() == '.pdf':
        from langchain_community.document_loaders import PyPDFLoader
        loader = PyPDFLoader(file)
    elif extension.lower() == '.docx':
        from langchain_community.document_loaders import Docx2txtLoader
        loader = Docx2txtLoader(file)
    elif extension.lower() == '.txt':
        from langchain_community.document_loaders import TextLoader
        loader = TextLoader(file)
    else:
        print('Document format is not supported!')
        return None
    
    data = loader.load()
    return dataCode language: JavaScript (javascript)

Domande e Risposte Contestuali

Il cuore del modulo Document Q&A è la funzione che gestisce le domande dell'utente e genera risposte basate sul contenuto del documento:

def ask_document(vector_store, q, k=3, api_key=None):
    # Verifica se la domanda è una richiesta di riassunto
    summary_keywords = ["riassumi", "riassunto", "riepilogo", "sintetizza", "sintesi",
                        "summary", "summarize", "summarise", "overview", "recap"]

    lower_q = q.lower()
    is_summary_request = any(keyword in lower_q for keyword in summary_keywords)

    if is_summary_request:
        # Genera un riassunto completo
        return generate_document_summary(vector_store, k=10, api_key=api_key)
    else:
        # Procedi con la normale domanda e risposta
        llm = ChatOpenAI(model='gpt-3.5-turbo', temperature=1, api_key=api_key)
        retriever = vector_store.as_retriever(
            search_type='similarity', search_kwargs={'k': k})
        chain = RetrievalQA.from_chain_type(
            llm=llm, chain_type="stuff", retriever=retriever)

        answer = chain.invoke(q)
        return answer['result']Code language: PHP (php)

Generazione di Riassunti Intelligenti

Una funzionalità avanzata del sistema è la capacità di generare riassunti completi del documento, utilizzando una strategia migliorata per selezionare le parti più rappresentative:

def generate_document_summary(vector_store, k=10, api_key=None):
    llm = ChatOpenAI(model='gpt-3.5-turbo', temperature=0.3, api_key=api_key)
    retriever = vector_store.as_retriever(
        search_type='mmr',  # MMR per una migliore diversità nei risultati
        search_kwargs={"k": k, "fetch_k": k*2}
    )

    # Strategia per ottenere parti rappresentative del documento
    docs_beginning = retriever.get_relevant_documents("beginning of document introduction")
    docs_middle = retriever.get_relevant_documents("main content central information")
    docs_end = retriever.get_relevant_documents("conclusion summary end")
    docs_key = retriever.get_relevant_documents("key points important information")
    
    # Combinazione dei documenti evitando duplicati
    all_docs = []
    seen_content = set()

    for doc_list in [docs_beginning, docs_middle, docs_end, docs_key]:
        for doc in doc_list:
            if doc.page_content not in seen_content:
                all_docs.append(doc)
                seen_content.add(doc.page_content)
                
    # Prompt specializzato per riassunti
    summary_prompt = PromptTemplate(
        input_variables=["context"],
        template="""You are an expert at summarizing documents. 
Please create a comprehensive summary of the following document content.
Focus on the main topics, key points, and overall structure.
Make sure to capture the essence of the document.
Be thorough and informative.

DOCUMENT CONTENT:
{context}

COMPREHENSIVE SUMMARY:"""
    )
    
    # Generate summary
    chain = LLMChain(llm=llm, prompt=summary_prompt)
    result = chain.run(context="\n\n".join([doc.page_content for doc in all_docs]))
    return resultCode language: PHP (php)

Meal Planner: Pianificatore di Pasti Intelligente

Loader Personalizzato per Ricette

Per il modulo Meal Planner, ho sviluppato un loader personalizzato che struttura i dati delle ricette in JSON:

class RecipeJSONLoader(BaseLoader):
    def __init__(self, file_path):
        self.file_path = file_path
    
    def load(self):
        with open(self.file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        documents = []
        for recipe in data["recipes"]:
            # Format recipe text with structured sections
            recipe_text = f"Title: {recipe['title']}\n"
            recipe_text += f"Cuisine: {recipe['cuisine']}\n"
            recipe_text += f"Meal Type: {recipe['mealType']}\n"
            recipe_text += f"Preparation Time: {recipe['prepTime']}\n"
            recipe_text += f"Cooking Time: {recipe['cookTime']}\n"
            recipe_text += f"Servings: {recipe['servings']}\n"
            recipe_text += f"Dietary Info: {', '.join(recipe['dietaryInfo'])}\n\n"
            
            recipe_text += f"Ingredients:\n"
            for ingredient in recipe['ingredients']:
                recipe_text += f"- {ingredient}\n"
                
            recipe_text += f"\nInstructions:\n{recipe['instructions']}"
            
            # Recipe-specific metadata
            metadata = {
                "id": recipe["id"],
                "title": recipe["title"],
                "cuisine": recipe["cuisine"],
                "mealType": recipe["mealType"],
                "source": self.file_path
            }
            
            doc = Document(page_content=recipe_text, metadata=metadata)
            documents.append(doc)
        
        return documents

Q&A Conversazionale per Ricette

Per interagire con le ricette, ho implementato una catena conversazionale con memoria:

def create_conversational_chain(vector_store, api_key=None):
    llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, api_key=api_key)
    retriever = vector_store.as_retriever(search_kwargs={"k": 2})
    
    # Initialize memory
    memory = ConversationBufferMemory(
        memory_key="chat_history",
        return_messages=True
    )
    
    # Create a conversational retrieval chain
    conversation_chain = ConversationalRetrievalChain.from_llm(
        llm=llm,
        retriever=retriever,
        memory=memory,
    )
    
    return conversation_chainCode language: PHP (php)

Pianificazione Pasti Personalizzata

Per la pianificazione dei pasti, ho creato una catena specializzata con prompt ottimizzati:

def create_meal_planner_chain(vector_store, api_key=None):
    llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7, api_key=api_key)
    retriever = vector_store.as_retriever(search_kwargs={"k": 5})
    
    # Define specialized prompt for meal planning
    meal_planner_template = """
    You are a helpful meal planning assistant. Based on the recipe information provided and the user's preferences,
    create a meal plan as requested. Use only the recipes mentioned in the context or reasonable variations of them.
    Be case-insensitive in your search, meaning treat 'chicken' and 'Chicken' as the same ingredient.
    
    Provide a detailed meal plan with:
    - Recipe suggestions for each meal
    - Preparation tips from the instructions
    - Modifications needed to meet the user's requirements
    
    Format your response in a clear, organized way with headings and bullet points as appropriate.
    
    Context (Recipe Information):
    {context}
    
    User Request: {question}
    """
    
    meal_planner_prompt = ChatPromptTemplate.from_template(meal_planner_template)
    
    # Define function to format documents
    def format_docs(docs):
        return "\n\n".join([doc.page_content for doc in docs])
    
    # Build the meal planner RAG chain
    meal_planner_chain = (
        {"context": retriever | format_docs, "question": RunnablePassthrough()}
        | meal_planner_prompt
        | llm
        | StrOutputParser()
    )
    
    return meal_planner_chainCode language: PHP (php)

Generazione di Liste della Spesa

Come estensione del pianificatore di pasti, ho implementato un generatore di liste della spesa ottimizzate:

def create_shopping_list_chain(api_key=None):
    llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0, api_key=api_key)

    # Define template for shopping list generation
    shopping_list_template = """
    You are an assistant specialized in creating shopping lists. Based on the provided meal plan,
    create a complete and organized shopping list.

    Carefully analyze the following meal plan and identify all necessary ingredients:

    {meal_plan}

    Generate a shopping list organized by categories (e.g., Proteins, Vegetables, Fruits, Dairy, Condiments, etc.).
    Group similar ingredients and indicate the approximate quantity needed to cover the period of the meal plan.
    Consider that some recipes may use the same ingredients.

    Format your response clearly and neatly, using bullet points to facilitate use during shopping.
    """

    # Create prompt from template
    shopping_list_prompt = ChatPromptTemplate.from_template(shopping_list_template)

    # Build the chain for shopping list
    shopping_list_chain = (
        {"meal_plan": RunnablePassthrough()}
        | shopping_list_prompt
        | llm
        | StrOutputParser()
    )

    return shopping_list_chainCode language: PHP (php)

Ottimizzazioni Comuni a Entrambi i Moduli

Normalizzazione delle Query

Per migliorare la qualità del retrieval in entrambi i moduli, ho implementato una funzione di normalizzazione delle query.

Interfaccia Utente con Streamlit

L'interfaccia utente è stata implementata con Streamlit, organizzata in moduli distinti con una navigazione intuitiva.

Conclusione

Questo progetto dimostra l'implementazione pratica di un sistema RAG completo attraverso due applicazioni distinte ma complementari. Il Document Q&A offre un assistente documentale interattivo che permette di estrarre informazioni precise da documenti complessi, mentre il Meal Planner utilizza la stessa tecnologia per creare piani alimentari personalizzati basati su una collezione di ricette.

L'architettura modulare dell'applicazione permette estensioni future, come l'integrazione di agenti AI con architetture di memoria multipla, rendendo il sistema adattabile a diversi casi d'uso.

Il progetto esemplifica come le tecniche avanzate di intelligenza artificiale possano essere applicate a problemi quotidiani, migliorando significativamente l'esperienza utente nell'interazione con documenti complessi e nella pianificazione dei pasti.

Il codice completo è disponibile su GitHub, e l'applicazione è accessibile attraverso Streamlit, dimostrando la fattibilità di creare soluzioni RAG pratiche e accessibili con le tecnologie attuali.

Related Projects

Maggio 6, 2025
Interview Preparation Assistant with CrewAI

Introduzione al Progetto L'AI Interview Preparation Assistant è un'applicazione che utilizza agenti AI per aiutare gli utenti a prepararsi per colloqui di lavoro. Il progetto combina un'interfaccia utente Streamlit con il framework CrewAI per creare un'esperienza completa di preparazione ai colloqui, dalla ricerca sull'azienda alla simulazione di interviste con feedback in tempo reale. Questo progetto […]

Marzo 18, 2025
Averna Spazio Open

• Sviluppo del sito web di Averna Spazio Open utilizzando WordPress, con Oxygen Builder per un design personalizzato e Advanced Custom Fields (ACF) per la gestione dinamica del contenuto. • Creato un sistema di invio di idee degli utenti, che venivano pubblicate sul sito, con una funzione di voto per selezionare l’idea migliore annualmente. • […]

veronicaschembri
Copyright © Veronica Schembri

Privacy Policy
Cookie Policy