RAG e Cosmos DB: Utilizzare i dati aziendali con l'intelligenza artificiale sul cloud Azure

Posted by danieleperugini on Sunday, April 21, 2024

L’intelligenza artificiale (IA) sta rivoluzionando il modo in cui le aziende accedono e gestiscono le informazioni. Uno degli aspetti fondamentali è il metodo, con cui le aziende possono utilizzare i propri dati come base di conoscenza per i modelli di linguaggio (LLM).

Ad oggi esistono diversi approcci per ottenere questo obiettivo. Ad esempio il fine-tuning dei modelli. Tuttavia questo approccio può presentare diverse sfide e svantaggi. In primo luogo, i temp ed il costo di questa operazione può essere considerevole a causa della necessità di risorse computazionali intense. Questo può rappresentare una barriera significativa, specialmente per le organizzazioni con budget limitati. In secondo luogo, c’è il rischio di overfitting, ovvero il modello potrebbe adattarsi troppo specificamente ai dati di training e perdere la capacità di generalizzare su nuovi dati. Questo è particolarmente vero quando il dataset di fine-tuning è piccolo o non completamente rappresentativo. In aggiunta, la preparazione e la gestione dei dati richiedono un’accurata selezione e pulizia, assicurando che il dataset sia di alta qualità e variato. Infine, i modelli richiedono manutenzione continua per rimanere aggiornati con le nuove informazioni e adattarsi a eventuali cambiamenti nel contesto di business, incrementando ulteriormente la complessità e il costo di gestione.

In risposta a questi svantaggi e all’esigenza comunque di far operare gli LLM sui dati aziendali, da tempo si è iniziato a parlare di RAG.

Il pattern RAG (Retrieval-Augmented Generation) presenta diversi vantaggi rispetto al fine-tuning dei modelli di linguaggio, in particolare per le applicazioni aziendali. Primo, RAG è più flessibile. Questo approccio infatti permette di aggiornare facilmente la base di conoscenza senza dover eseguire continuamente il training del modello. Il pattern RAG è anche economicamente più vantaggioso perché non richiede la costosa operazione di fine-tuning di interi modelli per specifici compiti, utilizzando invece le informazioni già esistenti in modo più efficiente.

Recentemente ho approfondito questo tema durante la mia partecipazione ad Azure Italia Podcast, e questo articolo si propone di espandere quelle considerazioni.

Quali vantaggi possono trarre le aziende dall’implementazione di sistemi di RAG?

Prima però di calarci in un contesto tecnico, è lecito porsi la domanda: perché le aziende dovrebbero prendere in considerazione la possibilità di utilizzare l’intelligenza artificiale con i propri dati?

✅ Primo, migliora l’efficienza nell’accesso alle informazioni, poiché il sistema può rapidamente recuperare dati rilevanti da un vasto archivio.

✅ Inoltre, genera risposte contestualizzate e dettagliate, migliorando la qualità del supporto decisionale.

✅ Questo approccio permette anche una maggiore scalabilità e aggiornamenti più agili della base di conoscenza, senza la necessità di costoso e continuo training del modello, riducendo così i costi e incrementando la reattività aziendale.

Facciamo qualche esempio pratico

Immaginiamo un’azienda che produce componenti automobilistici e che desidera utilizzare il pattern RAG per migliorare la gestione del suo vasto archivio di documenti tecnici e progetti di ricerca. Utilizzando il pattern RAG, l’azienda può configurare un sistema in cui il “retriever” cerca rapidamente nei documenti gli specifici dettagli richiesti (ad esempio, specifiche tecniche di un componente). Una volta individuati i dati rilevanti, il “generator” utilizza queste informazioni per formulare una risposta precisa e dettagliata che può aiutare l’ingegnere o il manager di prodotto a prendere decisioni informate, senza dover esaminare manualmente enormi volumi di documentazione.

O ancora, immaginiamo una piccola impresa di produzione che vuole utilizzare il pattern RAG per gestire le richieste dei clienti relative alla disponibilità e alle caratteristiche dei prodotti. Implementando RAG, il “retriever” può cercare rapidamente nel database dell’azienda per trovare informazioni specifiche su un prodotto, come materiali usati o tempi di realizzazione. Il “generator” poi formula una risposta che viene inviata al cliente, fornendo dettagli accurati e personalizzati basati sulla query, migliorando così l’esperienza del cliente e l’efficienza del servizio.

Il concetto è che l’intelligenza artificiale è diventata uno strumento affidabile per diversi compiti come l’analisi dei dati o la sintetizzazione di testi (e tanti altri tipi di task). Ed oltre a questo, oggi ha dei costi di accesso decisamente vantaggiosi per le aziende.

Adesso che abbiamo un’idea di alcune applicazioni pratiche, esploriamo come implementare il pattern RAG usando tecnologie avanzate come Python, Azure Cosmos DB per MongoDB, Azure OpenAI, Azure Blob Storage ed Azure Functions.

Due concetti fondamentali: embeddings e ricerca vettoriale

Gli LLM (beh, molti) funzionano “predicendo il token successivo”. Questo significa che in base alle interazioni passate nel corso della conversazione con l’utente, cercano di prevedere quale sarà la prossima parola (in realtà un token, una parte di una parola) che contribuirà a rispondere alla richiesta ok al task assegnato.

Uno dei concetti fondamentali utilizzati dagli LLM nel fare questo è il concetto di “embedding”.

Gli embeddings sono essenzialmente rappresentazioni numeriche di dati ad alta dimensionalità, come il testo, che catturano il significato semantico degli elementi. Questo permette agli LLM di “comprendere” il testo in maniera più profonda rispetto al semplice riconoscimento delle parole.

Inoltre aiutano gli LLM a generalizzare meglio a nuovi testi che non hanno mai visto durante il training. Infatti, dal momento che gli embeddings racchiudono il significato delle parole e delle frasi, il modello può fare inferenze più accurate su testi simili.

Infine, utilizzando gli embeddings, gli LLM possono eseguire ricerche semantiche nei grandi database di testo per trovare informazioni rilevanti o rispondere a domande. Questo approccio è particolarmente utile in applicazioni come assistenti virtuali, sistemi di raccomandazione e analisi di sentimenti.

Per convertire un testo in embedding, esistono modelli dedicati. Ad esempio OpenAI mette a disposizione il suo modello “text-embedding-ada-002” che converte un testo in un vettore di 1536 valori a virgola mobile.

Questo ci porta a considerare un aspetto cruciale: quando si analizza un documento, è consigliabile suddividerlo in “chunks”, ovvero in segmenti di testo, affinché ciascun vettore possa rappresentare efficacemente il contenuto di quella specifica porzione di testo. Se si applicasse un vettore all’intero documento, il risultato sarebbe troppo generico. Inoltre, è raccomandato che i confini di questi segmenti si sovrappongano leggermente, per garantire una continuità tematica e semantica tra i chunks.

Ricerca vettoriale

La ricerca vettoriale di embeddings funziona sfruttando la capacità degli embeddings di rappresentare dati come testo, immagini, o suoni in forma di vettori numerici in uno spazio multidimensionale.

Questo approccio consente di eseguire ricerche basate sulla “distanza” tra questi vettori, che rappresenta quanto sono semanticamente simili tra loro.

La ricerca vettoriale è particolarmente potente per le applicazioni di intelligenza artificiale che richiedono una comprensione del contenuto semantico, come nei motori di ricerca, nei sistemi di raccomandazione, o nelle applicazioni di elaborazione del linguaggio naturale. Questo metodo consente di superare i limiti delle ricerche tradizionali basate su parole chiave, offrendo risposte che tengono conto del contesto e del significato più ampio dei dati.

Applicata al pattern RAG, la ricerca vettoriale consente di effettuare ricerche di informazioni basate sul significato piuttosto che su parole chiave esatte, migliorando significativamente l’accuratezza e la rilevanza delle risposte generate dagli LLM.

Ecco come viene applicata:

⭕ Generazione degli Embeddings: Nel pattern RAG, la prima fase è trasformare i contenuti di un database di conoscenza (come documenti, libri, articoli ecc.) in vettori o embeddings. Questi embeddings sono rappresentazioni numeriche dei testi, che catturano il loro significato semantico.

⭕ Ricerca Vettoriale: Quando un utente fa una domanda, questa viene anch’essa convertita in un embedding. Il “Retriever” del pattern RAG utilizza la ricerca vettoriale per trovare gli embeddings nel database che sono più vicini a quello della domanda, basandosi sulla similarità semantica. Questo significa cercare gli embeddings che hanno la distanza più piccola rispetto all’embedding della query.

⭕ Selezione dei Contenuti Rilevanti: Gli embeddings che risultano più vicini alla query vengono selezionati come i contenuti più rilevanti per rispondere alla domanda. Questa selezione è cruciale perché determina la qualità delle informazioni che verranno utilizzate nella fase successiva del generatore di risposte.

⭕ Generazione della Risposta: Nella fase finale, l’LLM utilizza i contenuti selezionati per costruire una risposta coerente e contestualmente appropriata alla domanda dell’utente. Il modello di linguaggio può rielaborare, sintetizzare o integrare le informazioni dei contenuti recuperati per formulare una risposta che non solo sia informativa ma anche fluida e naturale nel linguaggio.

Quindi, è chiaro come la generazione di embeddings del testo e il sistema di confronto e ricerca dei vari vettori sia il concetto alla base del quale si appoggia il pattern RAG.

Adesso è quindi il momento di scendere ad un livello più tecnico per vedere come potrebbe essere impostato un sistema RAG. Questo è solo un concept, e come tale non prende in esame l’utilizzo di altri framework più strutturati che vengono utilizzati in produzione come “langchain” o “Semantic Kernel”.

Una overview ad alto livello della soluzione

L’obiettivo di questo Proof of Concept è creare un sistema che legge documenti PDF (ad esempio dei manuali aziendali), li suddivide in parti più piccole, e analizza queste parti per capire il loro contenuto. Queste informazioni vengono poi memorizzate in modo organizzato in un database. Infine, il sistema usa queste informazioni per rispondere in modo intelligente alle domande degli utenti, migliorando così l’accesso alle informazioni contenute nei documenti.

Quindi ci saranno due processi principali: il primo per il processing dei documenti (che avverrà tramite un pattern “reactive”), ed il secondo sotto forma di API che servirà a rispondere alle domande utente basandosi sui documenti processati dal primo.

Per essere più precisi queste sono le macro fasi in cui si alternerà tutto il processo di analisi dei documenti:

  • Conversione dei documenti PDF in testo: si trasformano i documenti PDF in testo leggibile da una macchina.
  • Suddivisione del testo in chunk: il testo viene diviso in parti più piccole per facilitare l’analisi.
  • Generazione degli embeddings per ogni chunk: si utilizza l’intelligenza artificiale per analizzare e comprendere il contenuto di ciascun chunk.
  • Caricamento dei chunk e degli embeddings su MongoDB: si salvano i dati analizzati in un database.
  • Creazione di un indice vettoriale per la ricerca: si organizzano i dati in modo che possano essere cercati rapidamente. Integrazione con GPT di OpenAI: si utilizzano le informazioni per rispondere in modo intelligente alle domande degli utenti.

Nel caso invece dell’API di interfaccia utente queste saranno le fasi:

  • Generazione degli embedding della domanda utente.
  • Ricerca vettoriale per similarità nel database dei documenti che conterranno la risposta alla domanda utente.
  • Realizzazione di un prompt ad-hoc contenente la domanda utente, il testo dei documenti “simili” trovati, e le istruzioni per indicare all’LLM di basarsi unicamente sui documenti iniettati.
  • Invio del prompt all’LLM e ricezione della risposta.

A livello di infrastruttura questo verrà gestito con i seguenti componenti: Azure blob storage: questo sarà lo storage in cui saranno depositati i pdf “grezzi” Azure Cosmos Db per Mongo DB: questo sarà il database dove verranno storicizzate le varie parti dei documenti con i propri embedding (ne parleremo dopo, ma per adesso pensiamo all’embedding come ad una chiave di lettura comprensibile alle macchine per capire il “senso” di un testo) Azure OpenAI (tramite AI Studio): questo servizio ci servirà ad esporre i modelli di IA di OpenAI che ci serviranno Azure Functions: queste rappresenteranno il “motore” per il processing dei documenti e per l’interazione con l’utente.

Perchè Azure Cosmos DB per Mongo DB?

Come visto prima, gli indici vettoriali permettono di eseguire ricerche basate sulla similarità semantica piuttosto che sulla corrispondenza esatta di parole chiave, rendendo possibile trovare contenuti che sono simili in significato anche se non esattamente uguali nelle parole.

Di conseguenza sarà necessario un database che conterrà i documenti ed i loro embeddings, ma non solo. Dovrà essere dotato di un sistema per eseguire una ricerca di similarità su tutti questi vettori.

Azure Cosmos DB per MongoDB è una soluzione che mette a disposizione tutte queste tecnologie.

E’ particolarmente vantaggioso per il pattern RAG grazie alla sua capacità di offrire prestazioni elevate e scalabilità. Questo sistema di database è progettato per gestire grandi volumi di dati e traffico elevato, caratteristiche essenziali per le applicazioni che richiedono rapide risposte basate sull’IA, come nel caso del RAG.

Ma soprattutto, Cosmos DB per MongoDB supporta nativamente la ricerca vettoriale, tramite gli indici vettoriali. Questo significa che è possibile creare un indice vettoriale (ne esistono diversi tipi, alcuni disponibili solo su certi tier) su un particolare campo di una collezione dati.

E sempre nativamente permette la ricerca per similarità tra un vettore di partenza, e tutti quelli storicizzati nella collezione, rendendo come risultato una lista ordinata con gli “N” risultati più vicini a quanto richiesto (ed un punteggio, per capire quanto vicini).

Inoltre Azure mette a disposizione un free-tier di Cosmos Db per Mongodb

Un’altro motivo che rende interessante l’utilizzo di Azure Cosmos Db per Mongo Db è la disponibilità di un free tier.

Azure Cosmos DB per MongoDB offre un “Free Tier” che fornisce una soluzione accessibile e pratica per sviluppatori e aziende che desiderano esplorare o sviluppare applicazioni. Con il Free Tier, gli utenti ottengono gratuitamente 1000 RU/s (Request Units per second) e 25 GB di spazio di archiviazione, con i consumi che superano questi limiti fatturati ai prezzi regolari.

Il Free Tier è disponibile per ogni sottoscrizione di Azure, ma è possibile attivare solo un account Free Tier per sottoscrizione.

Deploy dei modelli di linguaggio tramite Azure AI Studio

Altro aspetto necessario per il progetto è il deploy degli LLM. Ce ne serviranno due: uno per la generazione degli embeddings (OpenAI text-embedding-ada-002), ed uno per la risposta ai quesiti dell’utente (OpenAI gpt-35-turbo).

Per eseguire il deploy di modelli LLM (Large Language Models) su Azure utilizzando Azure AI Studio, è necessario seguire alcuni passaggi chiave. Inizialmente, bisogna configurare Azure AI Studio per accedere alle risorse di AI e Machine Learning disponibili su Azure creando un AI hub. Dopo ciò si dovrà crea un progetto collegato all’AI hub.

Dopo aver creato un ambiente di lavoro nel AI Studio come descritto brevemente, si potrà navigare nella sezione deployments per eseguire il deploy dei due modelli.

Una volta eseguito il deploy dei modelli sarà poi possibile vederne l’endpoint e l’api key. Inoltre con Azure AI Studio sarà possibile fare moltre altre cose come creare il modello, oppure creare dei workflow arricchiti, ma di questo ne parleremo in un altro articolo.

Quello che ci interessa al momento, è utilizzare direttamente i modelli tralasciando i workflow personalizzati ad altre implementazioni.

Le altre risorse Azure necessarie: blob storage ed azure functions

Ultimi due tasselli non meno importanti sono l’implementazione di un blob storage e delle azure functions. Il blob storage sarà il luogo dove si andranno a caricare i nuovi documenti da processare e vettorializzare.

La risorsa azure functions invece servirà per i due elementi computazionali della soluzione:

  • una funzione “triggerata” dal blob storage visto prima. Al momento del caricamento di un documento la funzione viene invocata in modo reattivo ed eseguirà il processing del documento appena caricato.
  • una funzione http che sarà l’endpoint, l’API, utilizzata dai client per inviare le richieste utente. Questa funzione quindi integrerà la vettorializzazione della richiesta utente, la ricerca vettoriale sul database e l’integrazione con il modello di linguaggio GPT 3.5.

Codice python per il processing dei documenti

Come detto prima, un processo si occuperà di analizzare i documenti inseriti sul blob storage.

La prima cosa da fare è quella di creare una funzione per convertire i pdf in testo. Per fare questo in python esiste un pacchetto chiamato “pdfplumber” che ha dimostrato una buona affidabilità.

import pdfplumber


def extract_text_from_pdfs(directory):
    docs = []
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.pdf'):
                print(f"Processing file: {file}")
                path = os.path.join(root, file)
                # Estrazione del testo
                try:
                    with pdfplumber.open(path) as pdf:
                        text = ""
                        for page in pdf.pages:
                            text += page.extract_text() or ""
                        docs.append({'file_name': file, 'text': text})
                        print(text)
                except Exception as e:
                    print(f"Failed to process {path}: {str(e)}")


    return docs

Questa funzione ritornerà quindi una lista di dizionari contenenti il nome file ed il testo estratto. Tuttavia, si è visto come, per preservare una migliore specificità dell’embedding rispetto al testo, sia necessario dividere i grandi documenti in “chunk”, ovvero porzioni più piccole con un certo “overlapping” per mantenere la continuità semantica.

Per fare questo si realizza una seconda funzione che utilizza il pacchetto “langchain”. Questo pacchetto contiene diverse classi pronte all’uso per eseguire questo task.

from langchain.text_splitter import RecursiveCharacterTextSplitter


def chunk_text(text):
    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size = 256,
        chunk_overlap  = 20,
        separators = ["\n\n", "\n"]
    )
    chunks = text_splitter.create_documents([text])
    return chunks


def chunk_documents(docs):
    chunks = []
    # Assuming we have a function `chunk_text` from langchain that chunks the text
    for doc in docs:
        try:
            # Placeholder: Replace `chunk_text` with the actual function call
            doc_chunks = chunk_text(doc['text'])
            for i, chunk in enumerate(doc_chunks):
                chunks.append({'document_name': doc['file_name'], 'chunk': chunk.page_content, 'sequence': i+1})
        except Exception as e:
            print(f"Failed to chunk {doc['file_name']}: {str(e)}")


    return chunks

Il risultato sarà una nuova lista di dizionari contenenti nome del file di partenza, la porzione di testo, ed una sequenza (in realtà la sequenza viene usata solo per debug), ma non serve ai fini della generazione dell’embedding.

Ora che quindi sono disponibili i chunk di tutti i documenti, dovranno essere passati ad una funzione che permetterà di invocare un modello di linguaggio (in questo caso il modello di OpenAI tramite Azure) che restituirà il vettore associato al testo.

from openai import AzureOpenAI


# openai configuration
OPENAI_API_KEY = “l’api key del servizio Azure AI”
OPENAI_API_ENDPOINT = “l’url del servizio"
OPENAI_API_VERSION = “2023-03-15-preview”


azure_openai_client = AzureOpenAI(
    api_key=OPENAI_API_KEY,
    azure_endpoint=OPENAI_API_ENDPOINT,
    api_version=OPENAI_API_VERSION
)


def generate_embeddings(text):
    response = azure_openai_client.embeddings.create(
input=text, 
model=”text-embedding-ada-002”)
    embeddings =response.model_dump()
    return embeddings['data'][0]['embedding']


for item in chunks:
    embedding = generate_embeddings(item['chunk'])
    item['chunk_vector'] = embedding

In questo ultimo snippet viene istanziato il client per OpenAI e vengono iterati i chunk ottenuti prima. Per ogni chunk si ottiene il suo vettore e viene memorizzato nello stesso dizionario.

Così la struttura dati risultante sarà una lista di dizionari contenente: nome file, contenuto testuale del chunk, rappresentazione vettoriale del chunk.

Adesso i dati sono pronti per essere caricati sul database vettoriale per la successiva ricerca.

mongo_conn = f"mongodb+srv://…"
mongo_client = pymongo.MongoClient(mongo_conn)
db = mongo_client[“nome del database”]


COSMOS_MONGO_COLLECTION = “nome della collezione”
if COSMOS_MONGO_COLLECTION not in db.list_collection_names():
    db.create_collection(COSMOS_MONGO_COLLECTION)


# creazione dell’indice vettoriale
db.command({
  'createIndexes': COSMOS_MONGO_COLLECTION,
  'indexes': [
    {
      'name': 'VectorSearchIndex',
      'key': {
        "chunk_vector": "cosmosSearch"
      },
      'cosmosSearchOptions': {
        'kind': 'vector-ivf',
        'numLists': 1,
        'similarity': 'COS',
        'dimensions': 1536
      }
    }
  ]
})


#inserimento dati
collection = db[COSMOS_MONGO_COLLECTION]
collection.insert_many(data)

Tutto questo potrebbe essere realizzato con un modello reattivo sfruttando i trigger delle Azure Functions.

Le Azure Functions infatti sono profondamente integrate con tutti i servizi di Azure, e possono essere invocate in molti modi diversi (trigger).

Uno di questi è il blob trigger, che scatena l’esecuzione di una funzione quando in un particolare container del blob storage avvengono dei cambiamenti, ad esempio il caricamento di un file.

Di seguito la definizione della azure function che reagisce al caricamento di un file su blob storage:

import json
import azure.functions as func
import logging


from application.document_service import DocumentService


app = func.FunctionApp()




@app.blob_trigger(arg_name="myblob", path="docs-input",connection="openaiyourdatastorage_STORAGE")
def DocumentProcessor(myblob: func.InputStream):
    logging.info(f"Python blob trigger function processed blob"
                f"Name: {myblob.name}"
                f"Blob Size: {myblob.length} bytes")
    document_service = DocumentService()
    document_service.process_document(blob_name=myblob.name)

Nella funzione che si vede sopra, la classe DocumentService è uno strato software che contiene ed invoca le funzioni principali già viste.

Codice python per l’api interfaccia con l’utente

Ma che dire del modo in cui verrà trattata la richiesta dell’utente? Come già visto, il testo della richiesta verrà convertito in un embedding, dopo di che verrà eseguita una ricerca per similarità sul database ed infine, verrà iniettato il contenuto dei documenti trovati come fonte di conoscenza nel prompt utente.

Anche in questo caso si farà uso di una Azure Function che risponderà ad una richiesta HTTP:

@app.route(route="AskDocuments", auth_level=func.AuthLevel.FUNCTION)
def AskDocuments(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')


    user_request = req.params.get('req')
    if not user_request:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            user_request = req_body.get('req')


    if user_request:
        document_service = DocumentService()
        response = document_service.query_documents(user_request=user_request)
        json_response = json.dumps(response, indent = 4)
        return func.HttpResponse(json_response)
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a request in json body 'req' field",
             status_code=200
        )

Ma cosa fa esattamente il document service in questo caso?

Genererà l’embedding della richiesta utente utilizzando esattamente la stessa funzione usata per convertire i chunks dei documenti in vettori.

Poi eseguirà ricerca vettoriale su Cosmos DB per Mongo DB usando il vettore appena generato. Il codice che si occupa di eseguire la ricerca è il seguente:

def vector_search(self, collection_name:str, field_name:str, query_embedding, num_results=5):
        collection = self.db[collection_name]
        pipeline = [
            {
                '$search': {
                    "cosmosSearch": {
                        "vector": query_embedding,
                        "path": field_name,
                        "k": num_results
                    },
                    "returnStoredSource": True }},
            {'$project': { 'similarityScore': { '$meta': 'searchScore' }, 'document' : '$$ROOT' } }
        ]
        results = collection.aggregate(pipeline)
        return results

Ottenuti i documenti con il vettore simile alla richiesta utente, viene passato tutto al LLM con un prompt specifico per il caso d’uso:

def generate_completion(self, vector_search_results, user_prompt):
        system_prompt = '''
        Sei un assistente di HardwareTools Spa, azienda produttrice di utensileria.
        Il tuo ruolo è quello di dare informazioni sul catalogo prodotti aziendale.
        Rispondi solo alle domande relative alle informazioni fornite di seguito.
        Scrivi due righe di spazio bianco tra ogni risposta nell'elenco.
        Cita sempre il nome documento da cui hai tratto la risposta.
        Se non sei sicuro di una risposta, puoi dire "Non lo so" o "Non sono sicuro" e consigliare agli utenti di cercare da soli.
        Fornisci solo risposte che sono parte dei seguenti prompt.
        Alla fine ringrazia il cliente da parte di HardwareTools Spa.
        Questa è la base di conoscenza che devi utilizzare:
        ###
        '''


        for item in vector_search_results:
            system_prompt += '\n' + " - nome documento: " + item['document']['document_name'] + " -> contenuto documento: " + item['document']['chunk']
        system_prompt += '\n'
        system_prompt += "###"
        messages=[{"role": "system", "content": system_prompt}]
        messages.append({"role": "user", "content": user_prompt})


        response = self.azure_openai_client.chat.completions.create(
            model=os.environ['openai_completions_deployment'],
            messages=messages,
            temperature=0)
       
        model_dump = response.model_dump()
        return model_dump['choices'][0]['message']['content']

Conclusioni

Come visto quindi, il pattern RAG è un approccio efficace e flessibile per eseguire il grounding del modello di IA.

Tuttavia le varianti possono essere tantissime, a partire dai tipi di database utilizzati, al framework.

In questo caso non è stato utilizzato alcun framework per la pipeline di richiesta utente, ma esistono soluzioni production ready che permettono di eseguire uno streamline di tutto il processo, ad esempio langchain o Semantic Kernel di Microsoft. Ovviamente anche la parte di caricamento documenti non è che un concept. Anche questa deve essere oggetto di ingegnerizzazione incorporando il tutto in una data pipeline orchestrata a dovere.

Però questo prototipo mostra chiaramente come sia possibile connettere l’IA con i dati aziendali in modo tutto sommato semplice e soprattutto sostenibile da un punto di vista economico.

Perché non valutare come questo potrebbe essere utile nella tua azienda?

Contattami per una consulenza gratuita! Daniele Perugini

Ah! Dimenticavo! Il codice sorgente intero di questo POC lo puoi ottenere qui!

P.S.: Un ulteriore approfondimento al tema RAG e fine-tuning a confronto è disponibile a questo paper su Arxiv scritto da diversi componenti di Microsoft.


IL MIO LIBRO: WHY YOUR DATA MATTER

Il libro dedicato ai manager e CIO che hanno a cuore i dati della propria azienda e vogliono avere sonni tranquilli (anticipando problematiche poco piacevoli legate al recupero, alla gestione o alla sicurezza dei dati)

Leggi il libro

REGISTRATI ALLA NEWSLETTER

Una piccola newsletter su data, azure e AI.

Dicono di me

Ricordo ancora bene cosa mi spinse a coinvolgerlo per la prima volta. Oltre che a trasmettermi competenza ed affidabilità, Daniele mi è sembrato fin da subito propenso a mettersi in gioco e a fare squadra con Fapim. Ho percepito in maniera marcata che questa persona avrebbe fatto suo il problema e avrebbe cercato di risolverlo concretamente.

(Leggi la testimonianza completa)

Fapim S.p.a.Ombretta Pacini, responsabile comunicazione e immagine aziendale
È orbitando nell’area Microsoft che abbiamo conosciuto Daniele. Abbiamo iniziato a collaborarci nel 2016 in un momento nel quale, dopo aver introdotto in azienda la metodologia di Agile grazie ad un lungo periodo di formazione interna su questo argomento, iniziavamo a metterla in pratica su nuovi progetti e necessitavamo di Project Manager e Team Leader con esperienza.

(Leggi la testimonianza completa)

Vivido S.r.l.Claudio Menzani, Paolo Ciccioni
Ho conosciuto Daniele grazie ad una sua ex collega che mi ha parlato molto bene di lui e dei suoi servizi di consulenza e collaborazione nel campo della consulenza. Mi ha spinta a rivolgermi a Daniele la sua preparazione, professionalità e disponibilità.

(Leggi la testimonianza completa)