Orchestrazione di agenti LLM: una guida dettagliata con LangChain e Granite

Autore

Vrunda Gadesha

AI Advocate | Technical Content Author

L'orchestrazione degli agenti LLM si riferisce al processo di gestione e coordinamento delle interazioni tra un modello linguistico di grandi dimensioni (LLM) e vari strumenti, API o processi per eseguire attività complesse all'interno dei sistemi di AI. Si tratta di strutturare i workflow in cui un agente AI, alimentato dall'intelligenza artificiale, funge da motore di ragionamento o responsabile decisionale centrale, orchestrando le sue azioni in base agli input, al contesto e agli output di sistemi esterni. Utilizzando un framework di orchestrazione, gli LLM possono integrarsi perfettamente con API, database e altre applicazioni AI, abilitando funzionalità come chatbot e strumenti di automazione. I framework di agenti open source migliorano ulteriormente l'adattabilità di questi sistemi, rendendo gli LLM più efficaci negli scenari del mondo reale.

Molte persone fraintendono la differenza tra l'orchestrazione degli LLM e l'orchestrazione degli agenti LLM. La seguente illustrazione evidenzia le principali differenze:

In questo tutorial imparerai a creare un agente autonomo basato su modelli linguistici di grandi dimensioni (LLM) utilizzando i modelli IBM® Granite e LangChain. Esploreremo come gli agenti utilizzano componenti quali memoria, pianificazione e azione per eseguire attività intelligenti. Implementerai anche un sistema pratico che elabora il testo di un libro, risponde alle domande in modo dinamico e valuta le sue prestazioni utilizzando metriche di accuratezza come BLEU, precisione, richiamo e punteggio F1.

Framework per agenti autonomi basati su LLM

Il framework presentato nella figura 1 fornisce una progettazione olistica per agenti autonomi basati su modelli linguistici di grandi dimensioni (LLM), enfatizzando l'interazione tra le componenti chiave: profilo, memoria, pianificazione e azione. Ogni componente rappresenta una fase critica nello sviluppo di un agente autonomo in grado di ragionare, prendere decisioni e interagire con ambienti dinamici.1

1. Profilo: definizione dell'identità dell'agente

Il profilo conferisce all'agente la sua identità mediante l'embedding di informazioni quali dati demografici, tratti della personalità e contesto sociale. Questo processo garantisce che l'agente possa interagire in modo personalizzato. I profili possono essere creati manualmente, generati da modelli gen AI come i modelli IBM Granite o il GPT (generative pretrained transformer) di OpenAI, oppure possono essere allineati con set di dati specifici per soddisfare i requisiti delle attività. Utilizzando i prompt engineering, i profili possono essere perfezionati dinamicamente per ottimizzare le risposte. Inoltre, nell'ambito dell'orchestrazione multiagente, il profilo aiuta a definire ruoli e comportamenti, garantendo un coordinamento perfetto tra algoritmi AI e sistemi di processo decisionale.

2. Memoria: memorizzare e utilizzare il contesto

La memoria aiuta l'agente a conservare e recuperare le interazioni passate, consentendo risposte contestuali. Può essere unificata (tutti i dati in un unico posto) o ibrida (strutturata e non strutturata). Le operazioni, tra cui lettura, scrittura e riflessione, consentono all'agente di imparare dall'esperienza e fornire output coerenti e informati. Una memoria ben strutturata migliora l'orchestrazione multiagente garantendo che diversi agenti, compresi agenti specializzati progettati per un'attività specifica, possano condividere e recuperare i dati rilevanti in modo efficiente. Nei framework come AutoGen e Crew AI, la memoria svolge un ruolo cruciale nel mantenere la continuità all'interno dell'ecosistema degli agenti che collaborano, garantendo un coordinamento perfetto e un'esecuzione ottimizzata delle attività.

3. Pianificazione: definizione di azioni strategiche

La componente di pianificazione consente all'agente di elaborare strategie per raggiungere obiettivi. Può seguire passaggi predefiniti o adattarsi dinamicamente in base al feedback dell'ambiente, degli esseri umani o dello stesso LLM. Integrando algoritmi AI e sfruttando una base di conoscenze, la pianificazione può essere ottimizzata per migliorare l'efficienza del ragionamento e la precisione nella risoluzione dei problemi. Nelle applicazioni LLM, la pianificazione svolge un ruolo cruciale nel garantire che il natural language understanding e il processo decisionale siano in linea con gli obiettivi dell'agente. Inoltre, le tecniche retrieval-augmented migliorano la capacità dell'agente di accedere alle informazioni pertinenti in modo dinamico, migliorando la precisione della risposta. Questa flessibilità garantisce che l'agente rimanga efficace in scenari mutevoli, specialmente nell'orchestrazione multiagente, in cui vari agenti coordinano i loro piani per raggiungere obiettivi complessi mantenendo la scalabilità per la gestione di attività ampie e diverse.

4. Azione: esecuzione delle decisioni

Le azioni sono il modo in cui l'agente interagisce con il mondo, sia completando compiti, raccogliendo informazioni o comunicando. Utilizza la memoria e la pianificazione per guidare l'esecuzione, utilizza strumenti quando necessario e adatta il suo stato interno in base ai risultati per un miglioramento continuo. L'ottimizzazione dell'algoritmo di esecuzione delle azioni garantisce l'efficienza, soprattutto quando si integrano modelli di ragionamento basati su GPTe tecniche di gen AI per un processo decisionale in tempo reale.

Combinando queste componenti, il framework trasforma gli LLM in agenti adattabili in grado di ragionare, apprendere ed eseguire attività in modo autonomo. Questo design modulare lo rende ideale per applicazioni come il servizio clienti, l'assistenza alla ricerca e la risoluzione creativa dei problemi.

Caso d'uso: sviluppo di un agente di conoscenza interrogabile

Questo tutorial dimostra la creazione di un agente di conoscenza interrogabile progettato per elaborare documenti di testo di grandi dimensioni (come libri) e rispondere con precisione alle domande degli utenti. Utilizzando i modelli IBM Granite e LangChain, l'agente è sviluppato seguendo i principi delineati nel framework per gli agenti autonomi basati su LLM. I componenti del framework si allineano perfettamente con il workflow dell'agente per garantire adattabilità e risposte intelligenti.

Comprendiamo come si applica il framework nel nostro caso d'uso.

Profilo: l'agente è progettato con un profilo di "knowledge assistant", incentrato sulle attività di riepilogo, risposta alle domande e ragionamento. Il suo contesto è personalizzato per elaborare un documento specifico (ad esempio, Le avventure di Sherlock Holmes).

Memoria: l'agente utilizza la memoria ibrida con l'embedding di parti del libro in un database vettoriale FAISS. Questa capacità consente di recuperare dinamicamente il contesto pertinente durante le query. Le operazioni di memoria come la lettura (recupero) e la scrittura (aggiornamento degli embedding) assicurano che l'agente possa adattarsi alle nuove query nel tempo.

Pianificazione: la risoluzione delle query implica un ragionamento a percorso singolo. L'agente recupera parti di testo pertinenti, genera risposte utilizzando gli LLM Granite di IBM e valuta l'accuratezza dell'output. La pianificazione senza feedback garantisce la semplicità, mentre la modularità del sistema consente di incorporare i cicli di feedback nelle iterazioni future.

Azione: l'agente esegue la risoluzione delle query integrando il recupero della memoria e l'elaborazione LLM. Completa attività come la generazione di risposte, il calcolo delle metriche di precisione (BLEU, precisione, richiamo e punteggio F1) e la visualizzazione dei risultati per l'interpretazione da parte dell'utente. Questi output riflettono la capacità dell'agente di agire in modo intelligente sulla base del ragionamento e della pianificazione.

Prerequisiti

Per creare un progetto watsonx.ai è necessario un account IBM Cloud .

Passaggi

Passaggio 1. Configura il tuo ambiente

Sebbene sia possibile scegliere tra diversi strumenti, questo tutorial illustra come configurare un account IBM utilizzando un Jupyter Notebook.

  1. Accedi a watsonx.ai utilizzando il tuo account IBM Cloud.
  2. 2. Crea un progetto watsonx.ai. Puoi ottenere l'ID del tuo progetto dall'interno del tuo progetto. Clicca sulla scheda Gestisci. Quindi, copia l'ID del progetto dalla sezione Dettagli della pagina Generali. Per questo tutorial ti serve questo ID.
  3. 3. Crea un Jupyter Notebook.

Questo passaggio apre un ambiente notebook in cui è possibile copiare il codice da questo tutorial. In alternativa, puoi scaricare questo notebook sul tuo sistema locale e caricarlo nel tuo progetto watsonx.ai come asset. Per visualizzare altri tutorial su Granite, visita l'IBM Granite Community. Questo tutorial è disponibile anche su GitHub.

Passaggio 2: configura un servizio watsonx.ai runtime e una chiave API

  1. Crea un'istanza di servizio watsonx.ai Runtime (scegli il piano Lite, che è un'istanza gratuita).
  2. Genera una chiave API (application programming interface).
  3. Associa il servizio watsonx.ai Runtime al progetto che hai creato in watsonx.ai.

Passaggio 3. Installazione dei pacchetti

Per lavorare con il framework LangChain e integrare IBM WatsonXLLM, dobbiamo installare alcune librerie essenziali. Iniziamo installando i pacchetti richiesti:

Nota: se stai usando una vecchia versione dipip , puoi usare il comandopip install --upgrade pip per eseguire l'aggiornamento per una facile installazione dei pacchetti più recenti, che potrebbero non essere compatibili con le versioni precedenti. Ma se stai già utilizzando l'ultima versione o hai aggiornato di recente i tuoi pacchetti, puoi saltare questo comando.

!pip install --upgrade pip
!pip install langchain faiss-cpu pandas sentence-transformers
%pip install langchain
!pip install langchain-ibm

Nella cella di codice precedente,

  • LangChain  è il framework principale per la creazione di applicazioni con modelli linguistici.
  • faiss-cpu  è utilizzato per la ricerca efficiente di similarità e per creare e interrogare indici vettoriali.
  • pandas  è per la manipolazione e l'analisi dei dati.
  • sentence-transformers  è per generare embedding per la ricerca semantica.
  • langchain-ibm  è per integrare IBM WatsonXLLM (in questo tutorial è granite-3-8b-instruct) con LangChain.

Questo passaggio garantisce che l'ambiente sia pronto per le attività future.

Passaggio 4. Importa le librerie necessarie

Ora che abbiamo installato le librerie necessarie, importiamo i moduli necessari per questo tutorial:

import os
from langchain_ibm import WatsonxLLM
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
import pandas as pd
import getpass

Nella cella di codice precedente,

  • os  fornisce un modo per interagire con il sistema operativo (ad esempio, accedendo alle variabili di ambiente).
  • langchain_ibm.WatsonxLLM  ci consente di utilizzare gli LLM Granite di IBM Watson senza problemi all'interno del framework LangChain.
  • langchain.embeddings.HuggingFaceEmbeddings  genera embedding per il testo utilizzando i modelli HuggingFace, essenziali per la ricerca semantica.
  • langchain.vectorstores.FAISS  è una libreria per lo storage efficiente di vettori e la ricerca per somiglianza, che consente di creare e interrogare un indice vettoriale.
  • RecursiveCharacterTextSplitter  aiuta a dividere grandi blocchi di testo in parti più piccole, il che è critico per l'elaborazione efficiente dei documenti.
  • pandas  è una potente libreria per l'analisi e la manipolazione dei dati, qui utilizzata per gestire i dati tabulari.
  • getpass  è un modo sicuro per acquisire input sensibili come le chiavi API senza visualizzarli sullo schermo.

Questo passaggio configura tutti gli strumenti e i moduli necessari per elaborare testo, creare embedding, memorizzarli in un database vettoriale e interagire con WatsonxLLM di IBM.

Passaggio 5. Configura le credenziali

Questo codice configura le credenziali per l'accesso all'API IBM Watson machine learning (WML) e garantisce che l'ID del progetto sia configurato correttamente.

  • Un dizionariocredenziali è creato conl'URL del servizio WML echiave API . La chiave API viene raccolta in modo sicuro utilizzando `getpass.getpass` per evitare di esporre informazioni sensibili.
  • Il codice tenta di recuperare il PROJECT_ID  dalle variabili di ambiente utilizzando os.environ . Se il PROJECT_ID  non viene trovato, all'utente viene chiesto di inserirlo manualmente tramite input.
# Set up credentials
credentials = {
      "url": "https://us-south.ml.cloud.ibm.com", # Replace with the correct region if needed
      "apikey": getpass.getpass("Please enter your WML API key (hit enter): ")
     }
# Set up project_id
try:
     project_id = os.environ["PROJECT_ID"]
except KeyError:
     project_id = input("Please enter your project_id (hit enter): ")

Passaggio 6. Inizializza il modello linguistico di grandi dimensioni

Questo codice inizializza IBM WatsonxLLM per l'utilizzo nell'applicazione:

  1. Questo codice crea un'istanza di WatsonxLLM utilizzando il modelloibm/granite-3-8b-instruct, progettato per attività di AI generativa basate su istruzioni.
  2. I valori delurl ,chiave api  Eproject_id delle credenziali precedentemente configurate vengono passati per l'autenticazione e la connessione al servizio IBM WatsonXLLM.
  3. Configura il parametromax_new_tokens per limitare il numero di token generati dal modello in ogni risposta (150 token in questo caso).

Questo passaggio prepara WatsonxLLM per la generazione di risposte nel workflow.

# Initialize the IBM Granite LLM
llm = WatsonxLLM(
      model_id="ibm/granite-3-8b-instruct",
      url=credentials["url"],
      apikey=credentials["apikey"],
      project_id=project_id,
      params={
           "max_new_tokens": 150
      }
)

Passaggio 7. Definisci una funzione per estrarre testo da un file

Per elaborare il testo di un documento, abbiamo bisogno di una funzione in grado di leggerne ed estrarne il contenuto. La seguente funzione è progettata per gestire file di testo normale:

def extract_text_from_txt(file_path):
      """Extracts text from a plain text file."""
           with open(file_path, "r", encoding="utf-8") as file:
           text = file.read()
return text

Questa funzione,extract_text_from_txt , è progettata per leggere ed estrarre il contenuto di un file di testo normale. Accetta il percorso del file come argomento e apre il file in modalità di lettura concodifica UTF-8 , garantendo che i caratteri speciali siano gestiti correttamente.

L'intero contenuto del file viene letto in una variabile denominatatesto , che viene quindi restituita. Questa funzione svolge un ruolo cruciale nella preparazione dell'input estraendo il testo non elaborato dal documento, rendendolo pronto per le operazioni successive come la suddivisione in blocchi, l'embedding e l'interrogazione. Fornisce un modo semplice ed efficiente per elaborare dati testuali da qualsiasi file di testo normale.

Questa funzione ci consente di elaborare il file di input (Le avventure di Sherlock Holmes) ed estrarne il contenuto per ulteriori operazioni come la suddivisione in blocchi e l'embedding del testo. Garantisce che il testo grezzo sia immediatamente disponibile per l'analisi.

Passaggio 8. Suddividi il testo in blocchi

Per elaborare e indicizzare in modo efficiente blocchi di testo di grandi dimensioni, è necessario dividere il testo in blocchi più piccoli e gestibili. La funzione seguente gestisce questa attività:

def split_text_into_chunks(text, chunk_size=500, chunk_overlap=50):
           """Splits text into smaller chunks for indexing."""
           splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
return splitter.split_text(text)

I valori delsplit_text_into_chunks La funzione è progettata per dividere grandi blocchi di testo in parti più piccole e gestibili per un'elaborazione e un'indicizzazione efficienti. Accetta il testo grezzo come input insieme a due parametri facoltativi:chunk_size , che definisce la dimensione massima di ogni blocco (il valore predefinito è 500 caratteri) echunk_overlap , che specifica il numero di caratteri sovrapposti tra blocchi consecutivi (il valore predefinito è 50).

Questa funzione garantisce la continuità contestuale tra i blocchi. La funzione utilizza ilRecursiveCharacterTextSplitter daLangChain , che divide in modo intelligente il testo preservandone il contesto. Restituendo un elenco di blocchi di testo più piccoli, questa funzione prepara l'input per ulteriori operazioni come l'embedding e l'indicizzazione.

È essenziale quando si lavora con documenti di grandi dimensioni, poiché i modelli linguistici hanno spesso limiti di token e non possono elaborare i testi lunghi direttamente.

Passaggio 9: Crea un indice vettoriale

Per consentire una ricerca semantica efficiente, dobbiamo convertire blocchi di testo in embedding vettoriali e memorizzarli in un indice ricercabile. Questo passaggio utilizza gli embedding FAISS e HuggingFace per creare l'indice vettoriale, che costituisce la base per il recupero di informazioni pertinenti in base alle query.

def create_vector_index(chunks):
           """Creates a FAISS vector index from text chunks."""
               embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
               vector_store = FAISS.from_texts(chunks, embeddings)
return vector_store

I valori delLa funzione create_vector_index crea unindice vettoriale FAISS dai blocchi di testo generati nel passaggio precedente. Questa funzione è fondamentale per abilitare la ricerca semantica mappando ogni blocco in uno spazio vettoriale ad alta dimensione utilizzando gli embedding.

Per prima cosa inizializza un modello HuggingFaceEmbeddings sentence-transformers/all-MiniLM-L6-v2 , che genera embedding vettoriali per i blocchi di testo. Questi embedding catturano il significato semantico di ogni blocco.

La funzione utilizza quindiFAISS creare un database vettoriale indicizzando questi embedding, consentendo una ricerca efficiente delle somiglianze in un secondo momento.

Il database vettoriale risultante viene restituito e verrà utilizzato per trovare parti pertinenti in base alle richieste degli utenti, costituendo la colonna portante del processo di ricerca e recupero dell'agente.

Passaggio 10. Interroga l'indice vettoriale con Granite

Questo passaggio prevede l'interrogazione dell'indice vettoriale per recuperare informazioni pertinenti e l'utilizzo dell'LLM Granite di IBM per generare una risposta raffinata. Integrando la ricerca per somiglianza e il ragionamento LLM, la funzione fornisce un processo di risoluzione delle query dinamico e intelligente.

def query_index_with_granite_dynamic(vector_store, query, llm):
         """Searches the vector index, uses Granite to refine the response, and returns all components."""
             # Perform similarity search
             print("\n> Entering new AgentExecutor chain...")
             thought = f"The query '{query}' requires context from the book to provide an accurate response."
             print(f" Thought: {thought}")
             action = "Search FAISS Vector Store"
             print(f" Action: {action}")
             action_input = query
             print(f" Action Input: \"{action_input}\"")
             # Retrieve context
             results = vector_store.similarity_search(query, k=3)
             observation = "\n".join([result.page_content for result in results])
             print(f" Observation:\n{observation}\n")
            # Generate response with Granite
            prompt = f"Context:\n{observation}\n\nQuestion: {query}\nAnswer:"
            print(f" Thought: Combining retrieved context with the query to generate a detailed answer.")
            final_answer = llm(prompt)
            print(f" Final Answer: {final_answer.strip()}")
            print("\n> Finished chain.")
            # Return all components as a dictionary
            return {
                    "Thought": thought,
                     "Action": action,
                     "Action Input": action_input,
                     "Observation": observation,
                     "Final Answer": final_answer.strip()
                     }

I valori delquery_index_with_granite_dynamic è una funzione che accetta tre input: il primo è il database vettoriale (vector_store ), il secondo è la query dell'utente (query ) e il terzo è l'istanza dell'LLM Granite (LLM ).

Esegue innanzitutto una ricerca per similarità sull'indice vettoriale per recuperare i blocchi (o parti) di testo più pertinenti. Questi blocchi, denominatiobservation , vengono combinati in un unico blocco di contesto.

La funzione crea quindi un prompt combinando la query e il contesto recuperato. Questo prompt viene passatoall'LLM Granite , che genera una risposta dettagliata e contestualmente accurata (final_answer ).

Durante tutto il processo, passaggi intermedi come quelli dipensiero ,azione  Einput di azione  dell'agente sono stampati per garantire la trasparenza.

Infine, la funzione restituisce un dizionario contenente tutte le componenti, inclusi il processo di pensiero, l'azione intrapresa, l'osservazione recuperata e la risposta finale.

Questo passaggio è critico per trasformare il recupero dei dati non elaborati in informazioni attuabili e significative utilizzando le capacità di ragionamento dell'LLM.

Passaggio 11. Genera un 'DataFrame' per i risultati delle query

Questo passaggio elabora dinamicamente più query, recupera le informazioni pertinenti e salva i risultati in un formato strutturato per l'analisi. La funzione integra funzionalità di query, strutturazione dei dati ed esportazione.

def dynamic_output_to_dataframe(vector_store, queries, llm, csv_filename="output.csv"):
           """Generates a DataFrame dynamically for multiple queries and saves it as a CSV file."""
           # List to store all query outputs
           output_data = []
           # Process each query
           for query in queries:
           # Capture the output dynamically
           output = query_index_with_granite_dynamic(vector_store, query, llm)
           output_data.append(output)
           # Convert the list of dictionaries into a DataFrame
           df = pd.DataFrame(output_data)
           # Display the DataFrame
           print("\nFinal DataFrame:")
           print(df)
           # Save the DataFrame as a CSV file
           df.to_csv(csv_filename, index=False)
           print(f"\nOutput saved to {csv_filename}")

I valori deldynamic_output_to_dataframe  è una funzione che accetta quattro input: il database vettoriale (vector_store ), un elenco di query (queries ), l'istanza dell'LLM Granite (LLM ) e un nome file CSV facoltativo (csv_filename , l'impostazione predefinita èoutput.csv ).

Per ogni query, utilizza la funzionequery_index_with_granite_dynamic  per recuperare il contesto pertinente e generare una risposta utilizzando l'LLM. I risultati, comprese le componenti intermedie comePensiero ,Osservazione  ERisposta finale  vengono memorizzati in un elenco.

Una volta elaborate tutte le query, l'elenco dei risultati viene convertito in un DataFrame pandas. Questo formato tabulare consente una facile analisi e visualizzazione dei risultati delle query. Il DataFrame viene stampato per la revisione e salvato come file CSV per un uso futuro.

Questo passaggio è essenziale per organizzare l'output in un formato intuitivo, consentendo attività a valle come la valutazione dell'accuratezza e la visualizzazione.

Passaggio 12: Esegui il workflow principale

Questo passaggio combina tutti i passaggi precedenti in un unico workflow per elaborare un file di testo, rispondere alle query degli utenti e salvare i risultati in un formato strutturato. Lafunzione main_workflow funge da orchestratore centrale del tutorial.

def main_workflow():
           # Replace with your text file
           file_path = "aosh.txt"
           # Extract text from the text file
           text = extract_text_from_txt(file_path)
           # Split the text into chunks
           chunks = split_text_into_chunks(text)
           # Create a vector index
           vector_store = create_vector_index(chunks)
           # Define queries
           queries = [
                     "What is the plot of 'A Scandal in Bohemia'?",
                     "Who is Dr. Watson, and what role does he play in the stories?",
                     "Describe the relationship between Sherlock Holmes and Irene Adler.",
                     "What methods does Sherlock Holmes use to solve cases?"
                     ]
           # Generate and save output dynamically
          dynamic_output_to_dataframe(vector_store, queries, llm)

Comprendiamo come viene eseguito questo workflow:

Inserisci un file di testo: La variabilefile_path specifica il file di testo da elaborare. In questo tutorial il file input è"aosh.txt" , contenente il testo del libro "Le avventure di Sherlock Holmes".

Estrazione del testo:extract_text_from_txt viene chiamata per leggere ed estrarre il contenuto del file di testo di input.

Suddivisione del testo: il testo estratto viene suddiviso in blocchi più piccoli utilizzando la funzionesplit_text_into_chunks per facilitare l'embedding e l'indicizzazione.

Creazione di un indice vettoriale: i blocchi di testo vengono convertiti in embedding e memorizzati in un indiceindice vettoriale FAISS utilizzando laLa funzione create_vector_index funzioni aziendali.

Definizione delle query: viene fornito un elenco di query di esempio, ciascuna pensata per recuperare informazioni specifiche dal testo. Sarà l'agente a rispondere a queste domande.

Query di processo: la funzionedynamic_output_to_dataframe  elabora le query utilizzando l'indice vettoriale e l'LLM Granite di IBM. Recupera il contesto pertinente, genera le risposte e salva i risultati come file CSV per ulteriori analisi.

Questo passaggio integra tutte le componenti del tutorial in un workflow coeso. Automatizza il processo dall'estrazione del testo alla risoluzione delle query, consentendoti di testare le funzionalità dell'agente ed esaminare i risultati in un formato strutturato.

Per eseguire il workflow, è sufficiente chiamare la funzionemain_workflow()  e l'intera pipeline funzionerà senza problemi.

# Run the workflow
main_workflow()

Output


 

> Entering new AgentExecutor chain...
 Thought: The query 'What is the plot of 'A Scandal in Bohemia'?' requires context from the book to provide an accurate response.
 Action: Search FAISS Vector Store
 Action Input: "What is the plot of 'A Scandal in Bohemia'?"
 Observation:
I. A SCANDAL IN BOHEMIA


I.
“I was aware of it,” said Holmes dryly.

“The circumstances are of great delicacy, and every precaution has to
be taken to quench what might grow to be an immense scandal and
seriously compromise one of the reigning families of Europe. To speak
plainly, the matter implicates the great House of Ormstein, hereditary
kings of Bohemia.”

“I was also aware of that,” murmured Holmes, settling himself down in
his armchair and closing his eyes.
Contents

   I.     A Scandal in Bohemia
   II.    The Red-Headed League
   III.   A Case of Identity
   IV.    The Boscombe Valley Mystery
   V.     The Five Orange Pips
   VI.    The Man with the Twisted Lip
   VII.   The Adventure of the Blue Carbuncle
   VIII.  The Adventure of the Speckled Band
   IX.    The Adventure of the Engineer’s Thumb
   X.     The Adventure of the Noble Bachelor
   XI.    The Adventure of the Beryl Coronet
   XII.   The Adventure of the Copper Beeches

 Thought: Combining retrieved context with the query to generate a detailed answer.
/var/folders/4w/smh16qdx6l98q0534hr9v52r0000gn/T/ipykernel_2648/234523588.py:23: LangChainDeprecationWarning: The method `BaseLLM.__call__` was deprecated in langchain-core 0.1.7 and will be removed in 1.0. Use :meth:`~invoke` instead.
  final_answer = llm(prompt)
 Final Answer: Step 1: Identify the main characters and their roles.
- Sherlock Holmes: The detective who is approached by a client with a delicate matter.
- An unnamed client: A representative of the great House of Ormstein, hereditary kings of Bohemia, who seeks Holmes' help to prevent a potential scandal.

Step 2: Understand the main issue or conflict.
- The main issue is a delicate matter that, if exposed, could lead to a massive scandal and compromise one of the reigning families of Europe, specifically the House of Ormstein.

Step 3: Ident

> Finished chain.

> Entering new AgentExecutor chain...
 Thought: The query 'Who is Dr. Watson, and what role does he play in the stories?' requires context from the book to provide an accurate response.
 Action: Search FAISS Vector Store
 Action Input: "Who is Dr. Watson, and what role does he play in the stories?"
 Observation:
“Sarasate plays at the St. James’s Hall this afternoon,” he remarked.
“What do you think, Watson? Could your patients spare you for a few
hours?”

“I have nothing to do to-day. My practice is never very absorbing.”
“Try the settee,” said Holmes, relapsing into his armchair and putting
his fingertips together, as was his custom when in judicial moods. “I
know, my dear Watson, that you share my love of all that is bizarre and
outside the conventions and humdrum routine of everyday life. You have
shown your relish for it by the enthusiasm which has prompted you to
chronicle, and, if you will excuse my saying so, somewhat to embellish
so many of my own little adventures.”
“My God! It’s Watson,” said he. He was in a pitiable state of reaction,
with every nerve in a twitter. “I say, Watson, what o’clock is it?”

“Nearly eleven.”

“Of what day?”

“Of Friday, June 19th.”

“Good heavens! I thought it was Wednesday. It is Wednesday. What d’you
want to frighten a chap for?” He sank his face onto his arms and began
to sob in a high treble key.

“I tell you that it is Friday, man. Your wife has been waiting this two
days for you. You should be ashamed of yourself!”

 Thought: Combining retrieved context with the query to generate a detailed answer.
 Final Answer: Dr. Watson is a character in the Sherlock Holmes stories, written by Sir Arthur Conan Doyle. He is a former military surgeon who becomes the narrator and chronicler of Holmes' adventures. Watson is a close friend and confidant of Holmes, often accompanying him on cases and providing a more human perspective to the stories. He is known for his enthusiasm for the bizarre and unconventional, as well as his skill in recording the details of their investigations. Watson's role is crucial in presenting the narrative and offering insights into Holmes' character and methods.

> Finished chain.


Final DataFrame:
                                             Thought  \
0  The query 'What is the plot of 'A Scandal in B...   
1  The query 'Who is Dr. Watson, and what role do...   
2  The query 'Describe the relationship between S...   
3  The query 'What methods does Sherlock Holmes u...   

                      Action  \
0  Search FAISS Vector Store   
1  Search FAISS Vector Store   
2  Search FAISS Vector Store   
3  Search FAISS Vector Store   

                                        Action Input  \
0        What is the plot of 'A Scandal in Bohemia'?   
1  Who is Dr. Watson, and what role does he play ...   
2  Describe the relationship between Sherlock Hol...   
3  What methods does Sherlock Holmes use to solve...   

                                         Observation  \
0  I. A SCANDAL IN BOHEMIA\n\n\nI.\n“I was aware ...   
1  “Sarasate plays at the St. James’s Hall this a...   
2  “You have really got it!” he cried, grasping S...   
3  to learn of the case was told me by Sherlock H...   

                                        Final Answer  
0  Step 1: Identify the main characters and their...  
1  Dr. Watson is a character in the Sherlock Holm...  
2  Sherlock Holmes and Irene Adler have a profess...  
3  Sherlock Holmes uses a variety of methods to s...  

Output saved to output.csv

 

Dopo l'esecuzione della funzionemain_workflow() abbiamo elaborato un file di testo (aosh.txt) ed eseguito quattro query definite dall'utente sul libro "Le avventure di Sherlock Holmes". L'output fornisce un'analisi dettagliata del modo in cui è stata gestita ogni query:

  • Il pensiero descrive il ragionamento alla base della domanda e il contesto necessario per una risposta accurata.
  • L'azione indica il passo intrapreso, che in questo caso consiste nell'effettuare una ricerca per similarità utilizzando l'indice vettoriale FAISS.
  • L'input di azione è la query specifica che viene elaborata in un'unica iterazione.
  • L'osservazione è costituita dai blocchi di testo recuperati dall'indice vettoriale rilevanti per la query.
  • La risposta finale è la risposta dettagliata generata dall'LLM Granite di IBM utilizzando il contesto recuperato.

Inoltre, i risultati di tutte le query sono stati strutturati in un DataFrame e salvati comeoutput.csv . Questo file contiene tutte le componenti sopra menzionate per ulteriori analisi o condivisioni.

In questo processo, abbiamo combinato il recupero del testo con il ragionamento LLM per rispondere a query complesse sul libro. L'agente ha recuperato dinamicamente le informazioni pertinenti, ha utilizzato il contesto per generare risposte precise e ha organizzato l'output in un formato strutturato per una facile analisi.

Visualizzazione dei risultati

Una volta creato il file output.csv, procederemo ora con la visualizzazione dei risultati delle query e delle relative metriche di accuratezza, fornendo informazioni più approfondite sulle prestazioni dell'agente.

Nella cella di codice seguente, carichiamo i risultati delle query salvati dal fileoutput.csv in un DataFrame pandas per prepararlo per la visualizzazione e l'analisi. Il DataFrame ci consente di manipolare ed esplorare i dati in un formato strutturato.

# Load the output.csv file into a DataFrame
df = pd.read_csv("output.csv")
print(df.head()) # Display the first few rows

OUTPUT


Thought  \
0  The query 'What is the plot of 'A Scandal in B...   
1  The query 'Who is Dr. Watson, and what role do...   
2  The query 'Describe the relationship between S...   
3  The query 'What methods does Sherlock Holmes u...   

                      Action  \
0  Search FAISS Vector Store   
1  Search FAISS Vector Store   
2  Search FAISS Vector Store   
3  Search FAISS Vector Store   

                                        Action Input  \
0        What is the plot of 'A Scandal in Bohemia'?   
1  Who is Dr. Watson, and what role does he play ...   
2  Describe the relationship between Sherlock Hol...   
3  What methods does Sherlock Holmes use to solve...   

                                         Observation  \
0  I. A SCANDAL IN BOHEMIA\n\n\nI.\n“I was aware ...   
1  “Sarasate plays at the St. James’s Hall this a...   
2  “You have really got it!” he cried, grasping S...   
3  to learn of the case was told me by Sherlock H...   

                                        Final Answer  
0  Step 1: Identify the main characters and their...  
1  Dr. Watson is a character in the Sherlock Holm...  
2  Sherlock Holmes and Irene Adler have a profess...  
3  Sherlock Holmes uses a variety of methods to s...

In questo codice, il DataFrame include componenti chiave comePensiero ,Azione ,Osservazione  ERisposta finale  per ogni query. Visualizzando le prime righe utilizzandodf.head() , ci assicuriamo che i dati siano formattati correttamente e pronti per la fase successiva: la creazione di visualizzazioni significative.

Importazione di librerie di visualizzazione

Per creare visualizzazioni dei risultati delle query, importiamo le librerie necessarie:

import matplotlib.pyplot as plt
from wordcloud import WordCloud

 

matplotlib.pyplot  è una libreria ampiamente utilizzata per creare visualizzazioni statiche, interattive e animate in Python. Verrà utilizzata per generare grafici a barre, grafici a torta e altre visualizzazioni.

wordcloud  è una libreria per creare cloud, che evidenziano visivamente le parole più frequenti nei dati. Questo passaggio aiuta a riassumere ed esplorare il contesto recuperato dal testo.

Nota importante: se riscontri l'errore"WordCloud non trovato" , puoi risolverlo installando la libreria utilizzando il comandopip install wordcloud .

Visualizza le lunghezze delle osservazioni e delle risposte

Questo codice crea un grafico a barre orizzontali per confrontare le lunghezze delle osservazioni (contesto recuperato) e delle risposte (risposte generate) per ogni query. Questa visualizzazione fornisce insight sulla quantità di contesto utilizzata dall'agente rispetto alla lunghezza delle risposte generate.

def visualize_lengths_with_queries(df):
"""Visualizes the lengths of observations and answers with queries on the y-axis."""
df["Observation Length"] = df["Observation"].apply(len)
df["Answer Length"] = df["Final Answer"].apply(len)
# Extract relevant data
queries = df["Action Input"]
observation_lengths = df["Observation Length"]
answer_lengths = df["Answer Length"]
# Create a horizontal bar chart
plt.figure(figsize=(10, 6))
bar_width = 0.4
y_pos = range(len(queries))
plt.barh(y_pos, observation_lengths, bar_width, label="Observation Length", color="skyblue", edgecolor="black")
plt.barh([y + bar_width for y in y_pos], answer_lengths, bar_width, label="Answer Length", color="lightgreen", edgecolor="black")
plt.yticks([y + bar_width / 2 for y in y_pos], queries, fontsize=10)
plt.xlabel("Length (characters)", fontsize=14)
plt.ylabel("Queries", fontsize=14)
plt.title("Observation and Answer Lengths by Query", fontsize=16)
plt.legend(fontsize=12)
plt.tight_layout()
plt.show()
# Call the visualization function
visualize_lengths_with_queries(df)
 

Questa funzione,visualize_lengths_with_queries , crea un grafico a barre orizzontali per confrontare la lunghezza delle osservazioni (contesto recuperato) e delle risposte (risposte generate) per ogni query.

Calcola la lunghezza dei caratteri sia delle osservazioni che delle risposte, aggiungendole come nuove colonne (Lunghezza osservazione  ELunghezza risposta ) al DataFrame. Usandomatplotlib , traccia quindi queste lunghezze per ogni query, visualizzando le query sull'asse y per una migliore leggibilità.

Il grafico a barre è codificato a colori per distinguere tra la lunghezza dell'osservazione e la lunghezza delle risposte e include etichette, una legenda e un titolo per maggiore chiarezza.

Questa visualizzazione aiuta ad analizzare l'equilibrio tra la dimensione del contesto recuperato e i dettagli della risposta generata, offrendo informazioni su come l'agente elabora le query e risponde ad esse.

Visualizza la proporzione di testo utilizzata nelle osservazioni

Questo passaggio visualizza la quantità del testo totale elaborato dall'agente che è utilizzata nelle osservazioni (contesto recuperato) rispetto al testo rimanente. Per fornire una rappresentazione intuitiva della proporzione viene creato un grafico a torta.

def visualize_text_proportion(df):
     """Visualizes the proportion of text used in observations."""
     total_text_length = sum(df["Observation"].apply(len)) + sum(df["Final Answer"].apply(len))
     observation_text_length = sum(df["Observation"].apply(len))
     sizes = [observation_text_length, total_text_length - observation_text_length]
     labels = ["Observation Text", "Remaining Text"]
     colors = ["#66b3ff", "#99ff99"]
     plt.figure(figsize=(4, 4))
     plt.pie(sizes, labels=labels, colors=colors, autopct="%1.1f%%", startangle=140)
     plt.title("Proportion of Text Used in Observations", fontsize=16)
     plt.show()
# Call the visualization function
visualize_text_proportion(df)

I valori delvisualize_text_proportion  è una funzione che crea un grafico a torta per illustrare la proporzione del testo totale utilizzata nelle osservazioni (contesto recuperato) rispetto al testo rimanente. Calcola la lunghezza totale del testo sommando le lunghezze dei caratteri di tutte le osservazioni e le risposte, quindi determina la parte apportata dalle sole osservazioni.

Questi dati vengono visualizzati in un grafico a torta, con etichette chiare per"Testo osservazione" e"Testo rimanente" e colori distinti per migliorare la leggibilità. Il grafico include valori percentuali per facilitare l'interpretazione delle proporzioni.

Questa visualizzazione fornisce una panoramica di alto livello della quantità di testo utilizzata dall'agente come contesto durante l'elaborazione delle query, offrendo informazioni dettagliate sull'efficienza e il focus del processo di recupero.

Genera nuvole di parole per le osservazioni e le risposte finali

Questo codice genera due nuvole per rappresentare visivamente le parole più frequenti nei Osservazione e Risposta finale testi.

def generate_wordclouds_side_by_side(df):
      """Generates and displays word clouds for Observations and Final Answers side by side."""
      # Combine text for Observations and Final Answers
      observation_text = " ".join(df["Observation"])
      final_answer_text = " ".join(df["Final Answer"])
      # Create word clouds
      observation_wordcloud = WordCloud(width=800, height=400, background_color="white").generate(observation_text)
      final_answer_wordcloud = WordCloud(width=800, height=400, background_color="white").generate(final_answer_text)
      # Create a side-by-side visualization
      plt.figure(figsize=(16, 8))
      # Plot the Observation word cloud
      plt.subplot(1, 2, 1)
      plt.imshow(observation_wordcloud, interpolation="bilinear")
      plt.axis("off")
      plt.title("Word Cloud of Observations", fontsize=16)
      # Plot the Final Answer word cloud
      plt.subplot(1, 2, 2)
      plt.imshow(final_answer_wordcloud, interpolation="bilinear")
      plt.axis("off")
      plt.title("Word Cloud of Final Answers", fontsize=16)
      plt.tight_layout()
      plt.show()
# Call the function to generate and display the word clouds
generate_wordclouds_side_by_side(df)
 

Questo codice genera due nuvole per rappresentare visivamente le parole più frequenti neiOsservazione eRisposta finale testi, visualizzandoli uno accanto all'altro per facilitarne il confronto. IOsservazione  ERisposta finale  testi vengono prima concatenati in due stringhe separate utilizzando" ".join()  per combinare tutte le righe delle rispettive colonne. La libreriaWordCloud viene quindi utilizzata per generare nuvole di parole per ogni testo con configurazioni specifiche.

Per creare una visualizzazione affiancata, vengono utilizzate delle sottotrame: la prima sottotrama mostra la nuvola di parole perOsservazione , e la seconda mostra quella perRisposta finale . La funzionetight_layout()  garantisce una spaziatura ordinata tra i grafici. Queste nuvole di parole ci consentono di analizzare in modo intuitivo le prestazioni dell'agente evidenziando i termini chiave recuperati dal contesto (Osservazione ) e quelli sottolineati nelle risposte (Risposta finale ).

Testa l'accuratezza dell'agente

In questa sezione, valutiamo le prestazioni dell'agente utilizzando diverse metriche di precisione:Corrispondenza delle parole chiave ,Punteggi BLEU ,Precisione/Richiamo  EPunteggi F1 Queste metriche forniscono una visione completa dell'efficacia con cui l'agente genera risposte accurate e pertinenti in base alle query degli utenti.

Importa le librerie necessarie

Prima di iniziare i test, importiamo le librerie necessarie per la valutazione dell'accuratezza.

from sklearn.feature_extraction.text import CountVectorizer
from nltk.translate.bleu_score import sentence_bleu
from sklearn.metrics import precision_score, recall_score

Queste librerie includono strumenti per la corrispondenza delle parole chiave, il calcolo del punteggio BLEU, la precisione e la valutazione dei richiami. Assicurati di aver installato queste librerie nel tuo ambiente per evitare errori di importazione.

Accuratezza della corrispondenza delle parole chiave

Questo test valuta quanto le risposte generate includono le parole chiave delle query. UtilizzaCountVectorizer per tokenizzare ed estrarre parole chiave dalle query e dalle risposte. La funzione calcola la percentuale di parole chiave di ricerca presenti nella risposta generata, contrassegnando la risposta come accurata se questa percentuale supera una soglia (0,5 per impostazione predefinita). I risultati vengono aggiunti al DataFrame sotto le colonneIl punteggio di corrispondenza delle parole chiave eè accurato .

def keyword_matching_accuracy(df):
      """Checks if key phrases from the query are present in the final answer."""
      vectorizer = CountVectorizer(stop_words='english')
      def check_keywords(query, answer):
      query_keywords = set(vectorizer.build_tokenizer()(query.lower()))
      answer_keywords = set(vectorizer.build_tokenizer()(answer.lower()))
      common_keywords = query_keywords & answer_keywords
      return len(common_keywords) / len(query_keywords) # Proportion of matched keywords
      df["Keyword Match Score"] = df.apply(lambda row: check_keywords(row["Action Input"], row["Final Answer"]), axis=1)
      df["Is Accurate"] = df["Keyword Match Score"] >= 0.5 # Set a threshold for accuracy
      return df
# Apply keyword matching
df = keyword_matching_accuracy(df)
df.to_csv("output_with_accuracy.csv", index=False)
df

Calcolo del punteggio BLEU

Questo test misura quanto le risposte generate corrispondano alle osservazioni recuperate.BLEU (Bilingual Evaluation Understudy) è una metrica popolare per valutare la somiglianza del testo basata su sovrapposizionin-gram . La funzione calcolai punteggi BLEU  per ogni coppia query-risposta e le aggiunge al DataFrame sotto la colonna del punteggio BLEU.

def calculate_bleu_scores(df):
    """Calculates BLEU scores for answers against observations."""
    df["BLEU Score"] = df.apply(
       lambda row: sentence_bleu([row["Observation"].split()], row["Final Answer"].split()),
       axis=1
       )
    return df
# Apply BLEU score calculation
df = calculate_bleu_scores(df)
df.to_csv("output_with_bleu.csv", index=False)

Precisione e richiamo

La precisione e il richiamo sono calcolati per valutare la pertinenza e la completezza delle risposte. La precisione misura la percentuale di parole recuperate nella risposta che sono pertinenti, mentre il richiamo misura la percentuale di parole pertinenti nell'osservazione che appaiono nella risposta.

Queste metriche vengono aggiunte al DataFrame sotto le colonne Precisione e Richiamo .

def calculate_precision_recall(df):
     """Calculates precision and recall for extractive answers."""
         def precision_recall(observation, answer):
                observation_set = set(observation.lower().split())
                answer_set = set(answer.lower().split())
                precision = len(observation_set & answer_set) / len(answer_set) if answer_set else 0
                recall = len(observation_set & answer_set) / len(observation_set) if observation_set else 0
         return precision, recall
        df[["Precision", "Recall"]] = df.apply(
        lambda row: pd.Series(precision_recall(row["Observation"], row["Final Answer"])),
        axis=1
        )
return df
# Apply precision/recall
df = calculate_precision_recall(df)
df.to_csv("output_with_precision_recall.csv", index=False)
df

Calcolo del punteggio F1

Il punteggio F1 combina precisione e richiamo in una metrica, fornendo una valutazione equilibrata di pertinenza e completezza. La formula per il punteggio F1 è: F1 Score = 2 * (Precision * Recall) / (Precision + Recall)

IPunteggi F1 calcolati vengono aggiunti al DataFrame sotto la colonna del punteggio F1.

def calculate_f1(df):
      """Calculates F1 scores based on precision and recall."""
          df["F1 Score"] = 2 * (df["Precision"] * df["Recall"]) / (df["Precision"] + df["Recall"])
          df["F1 Score"].fillna(0, inplace=True) # Handle divide by zero
          return df
# Apply F1 calculation
df = calculate_f1(df)
df.to_csv("output_with_f1.csv", index=False)
df

Riepiloga le metriche di accuratezza

Infine, una funzione di riepilogo aggrega tutte le metriche per fornire una panoramica delle prestazioni dell'agente. Calcola il numero totale di query, il conteggio e la percentuale di risposte accurate e i punteggi BLEU e F1 medi.

def summarize_accuracy_metrics(df):
      """Summarizes overall accuracy metrics."""
          total_entries = len(df)
          accurate_entries = df["Is Accurate"].sum()
          average_bleu = df["BLEU Score"].mean()
          average_f1 = df["F1 Score"].mean()
          print(f"Total Entries: {total_entries}")
          print(f"Accurate Entries: {accurate_entries} ({accurate_entries / total_entries * 100:.2f}%)")
          print(f"Average BLEU Score: {average_bleu:.2f}")
          print(f"Average F1 Score: {average_f1:.2f}")
# Call summary function
summarize_accuracy_metrics(df)

OUTPUT


 

Total Entries: 4
Accurate Entries: 4 (100.00%)
Average BLEU Score: 0.04
Average F1 Score: 0.24

Questi test di accuratezza offrono una valutazione dettagliata della capacità dell'agente di generare risposte pertinenti e accurate. Ogni test si concentra su un aspetto specifico, dall'inclusione delle parole chiave alla somiglianza del testo e fino alla completezza delle risposte. Il riepilogo aggrega queste metriche per fornire una panoramica istantanea complessiva delle prestazioni.

Riepilogo

Questo tutorial ti ha guidato nella creazione di un agente autonomo basato sull'LLM Granite di IBM e LangChain. A partire dall'estrazione del testo fino alla vettorializzazione e alla risoluzione delle query, abbiamo coperto l'intero processo di progettazione e implementazione di un agente funzionale basato su LLM. I passaggi chiave includevano la gestione della memoria con database vettoriale, l'elaborazione delle query e la generazione di risposte utilizzando Granite.

Abbiamo valutato le prestazioni dell'agente utilizzando metriche di precisione come la corrispondenza delle parole chiave, i punteggi BLEU, la precisione, il richiamo e i punteggi F1. Visualizzazioni come grafici a barre, grafici a torta e nuvole di parole hanno fornito ulteriori dettagli approfonditi sull'efficacia e il comportamento dell'agente.

Completando questo tutorial, hai imparato a progettare, testare e visualizzare le prestazioni di un agente LLM. Questa base può essere estesa per affrontare set di dati più complessi, migliorare la precisione ed esplorare caratteristiche avanzate come i sistemi multiagente.

Soluzioni correlate
Agenti AI per il Business

Crea, implementa e gestisci assistenti e agenti AI potenti che automatizzano workflow e processi con l'AI generativa.

    Scopri watsonx Orchestrate
    Soluzioni per agenti AI IBM

    Costruisci il futuro della tua azienda con soluzioni AI di cui puoi fidarti.

    Esplora le soluzioni basate su agenti AI
    Servizi AI di IBM Consulting

    I servizi di AI di IBM Consulting aiutano a reinventare il modo in cui le aziende lavorano con l'AI per la trasformazione.

    Esplora i servizi di intelligenza artificiale
    Prossimi passi

    Sia che tu scelga di personalizzare app e competenze precostituite o di creare e implementare servizi di agenti personalizzati utilizzando uno studio di AI, la piattaforma IBM watsonx è la soluzione che fa per te.

    Scopri watsonx Orchestrate Esplora watsonx.ai