Orquestación de agentes LLM: guía paso a paso con LangChain y Granite

Autor

Vrunda Gadesha

AI Advocate | Technical Content Author

La orquestación de agentes LLM se refiere al proceso de gestión y coordinación de las interacciones entre un modelo de lenguaje de gran tamaño (LLM) y varias herramientas, API o procesos para realizar tareas complejas dentro de sistemas de IA. Implica estructurar flujos de trabajo en los que un agente de IA, con inteligencia artificial, actúa como responsable central de la toma de decisiones o motor de razonamiento, orquestando sus acciones en función de las entradas, el contexto y las salidas de sistemas externos. Mediante un marco de orquestación, los LLM pueden integrarse de manera fluida con API, bases de datos y otras aplicaciones de IA, habilitando funcionalidades como chatbot y herramientas de automatización. Los marcos de agentes de código abierto mejoran aún más la adaptabilidad de estos sistemas, lo que hace que los LLM sean más eficaces en escenarios del mundo real.

Mucha gente malinterpreta la diferencia entre la orquestación LLM y la orquestación de agentes LLM. La siguiente ilustración destaca las diferencias clave:

Diagrama con la diferencia clave entre la orquestación LLM y la orquestación de agentes LLM Diferencia clave entre la orquestación LLM y la orquestación de agentes LLM

En este tutorial, aprenderá a crear un agente autónomo impulsado por modelos de lenguaje de gran tamaño (LLM) usando modelos de IBM® Granite y LangChain. Exploraremos cómo los agentes aprovechan componentes clave como la memoria, la planificación y la acción para realizar tareas inteligentes. También implementará un sistema práctico que procesa el texto de un libro, responde a las consultas de forma dinámica y evalúa su rendimiento mediante el uso de métricas de precisión como BLEU, precisión, recuperación y puntuación F1.

Marco para agentes autónomos basados en LLM

El marco presentado en la figura 1 proporciona un diseño holístico para agentes autónomos basados en modelos de lenguaje de gran tamaño (LLM), haciendo hincapié en la interacción entre los componentes clave: perfil, memoria, planificación y acción. Cada componente representa una etapa crítica en la construcción de un agente autónomo capaz de razonar, tomar decisiones e interactuar con entornos dinámicos1.

Marco para agentes autónomos basados en LLM Marco para agentes autónomos basados en LLM

1. Perfil: definición de la identidad del agente

El perfil proporciona al agente su identidad al incorporar información como datos demográficos, rasgos de personalidad y contexto social. Este proceso garantiza que el agente pueda interactuar de forma personalizada. Los perfiles pueden crearse manualmente, generarse mediante modelos de IA generativa como los modelos IBM Granite o el GPT (transformador generativo preentrenado) de OpenAI, o alinearse con conjuntos de datos específicos para cumplir con los requisitos de las tareas. Gracias al prompt engineering, los perfiles pueden perfeccionarse dinámicamente para optimizar las respuestas. Además, en la orquestación multiagente, el perfil ayuda a definir roles y comportamientos, lo que garantiza una coordinación perfecta entre algoritmos de IA y sistemas de toma de decisiones.

2. Memoria: almacenamiento y uso del contexto

La memoria ayuda al agente a retener y recuperar interacciones pasadas, lo que permite respuestas contextuales. Puede ser unificado (todos los datos en un solo lugar) o híbrido (estructurado y no estructurado). Las operaciones que incluyen lectura, escritura y reflexión permiten al agente aprender de la experiencia y proporcionar outputs coherentes e informados. Una memoria bien estructurada mejora la orquestación multiagente al garantizar que diferentes agentes, incluidos los agentes especializados diseñados para una tarea específica, puedan compartir y recuperar datos relevantes de manera eficiente. En marcos como AutoGen y Crew IA, la memoria desempeña un papel crucial en el mantenimiento de la continuidad dentro del ecosistema de agentes colaboradores, garantizando una coordinación perfecta y una ejecución optimizada de las tareas.

3. Planificación: acciones estratégicas

El componente de planificación permite al agente idear estrategias para alcanzar los objetivos. Puede seguir pasos predefinidos o adaptarse dinámicamente en función de los comentarios del entorno, los humanos o el propio LLM. Al integrar algoritmos de IA y aprovechar una base de conocimientos, la planificación puede optimizarse para mejorar la eficiencia del razonamiento y la precisión en la resolución de problemas. En las aplicaciones de LLM, la planificación desempeña un papel crucial para garantizar que la comprensión del lenguaje natural y los procesos de toma de decisiones se alineen con los objetivos del agente. Además, las técnicas de recuperación aumentada mejoran la capacidad del agente para acceder a la información relevante de forma dinámica, lo que mejora la precisión de la respuesta. Esta flexibilidad garantiza que el agente siga siendo eficaz en escenarios cambiantes, especialmente en la orquestación multiagente, donde varios agentes coordinan sus planes para lograr objetivos complejos mientras mantienen la escalabilidad para manejar tareas grandes y diversas.

4. Acción: ejecución de decisiones

Las acciones son la forma en que el agente interactúa con el mundo, ya sea completando tareas, recopilando información o comunicándose. Utiliza la memoria y la planificación para guiar la ejecución, emplea herramientas cuando es necesario y adapta su estado interno en función de los resultados para la mejora continua. La optimización del algoritmo de ejecución de acciones garantiza la eficiencia, especialmente cuando se integran modelos de razonamiento impulsados por GPT y técnicas de IA generativa para la toma de decisiones en tiempo real.

Al combinar estos componentes, el marco transforma a los LLM en agentes adaptables capaces de razonar, aprender y realizar tareas de forma autónoma. Este diseño modular lo hace ideal para aplicaciones como el servicio de atención al cliente, la asistencia en la investigación y la resolución creativa de problemas.

Caso de uso: creación de un agente de conocimiento consultable

Este tutorial demuestra la creación de un agente de conocimiento consultable diseñado para procesar documentos de texto grandes (como libros) y responder a las consultas de los usuarios con precisión. Mediante modelos de IBM Granite y LangChain, el agente se construye siguiendo los principios descritos en el marco para agentes autónomos basados en LLM. Los componentes del marco se alinean de manera fluida con el flujo de trabajo del agente para garantizar la adaptabilidad y las respuestas inteligentes.

Entendamos cómo se aplica el marco en nuestro caso de uso.

Aplicación del marco Aplicación del marco

Perfil: el agente está diseñado con un perfil de "asistente de conocimientos", centrado en tareas de resumen, respuesta a preguntas y razonamiento. Su contexto está personalizado para procesar un documento específico (por ejemplo, Las aventuras de Sherlock Holmes).

Memoria: el agente emplea memoria híbrida incrustando fragmentos del libro en un almacén de vectores FAISS. Esta capacidad le permite recuperar el contexto relevante de forma dinámica durante las consultas. Las operaciones de memoria, como la lectura (recuperación) y la escritura (actualización de embeddings), garantizan que el agente pueda adaptarse a nuevas consultas con el tiempo.

Planificación: la resolución de consultas implica un razonamiento de ruta única. El agente recupera fragmentos de texto relevantes, genera respuestas utilizando el Granite LLM de IBM y evalúa la precisión de la salida. La planificación sin feedback garantiza la simplicidad, mientras que la modularidad del sistema permite incorporar bucles de comentarios en futuras iteraciones.

Acción: el agente ejecuta la resolución de consultas integrando la recuperación de memoria y el procesamiento LLM. Completa tareas como generar respuestas, calcular métricas de precisión (BLEU, precisión, recuperación y puntuación F1) y visualizar resultados para la interpretación del usuario. Estos resultados reflejan la capacidad del agente para actuar de forma inteligente basándose en el razonamiento y la planificación.

Requisitos previos

Necesita una cuenta de IBM® Cloud para crear un proyecto watsonx.ai .

Pasos

Paso 1. Configure su entorno

Aunque puede elegir entre varias herramientas, este tutorial le muestra cómo configurar una cuenta de IBM utilizando un Jupyter Notebook.

  1. Inicie sesión en watsonx.ai utilizando su cuenta de IBM Cloud.
  2. 2. Cree un proyecto watsonx.ai. Puede obtener el ID de su proyecto desde su proyecto. Haga clic en la pestaña Administrar. A continuación, copie el ID del proyecto de la sección Detalles de la página General. Necesita este ID para este tutorial.
  3. 3. Cree un Jupyter Notebook.

Este paso abre un entorno de Notebook donde puede copiar el código de este tutorial. También puede descargar este cuaderno en su sistema local y cargarlo en su proyecto watsonx.ai como activo. Para ver más tutoriales de Granite, consulte la comunidad de IBM Granite. Este tutorial también está disponible en GitHub.

Paso 2. Configurar el servicio de tiempo de ejecución de watsonx.ai y la clave de API

  1. Cree una instancia de servicio watsonx.ai Runtime (elija el plan Lite, que es una instancia gratuita).
  2. Genere una clave de interfaz de programación de aplicaciones (API).
  3. Asocie el servicio watsonx.ai Runtime al proyecto que ha creado en watsonx.ai.

Paso 3. Instalación de los paquetes

Para trabajar con el marco LangChain e integrar IBM® watsonxLLM, necesitamos instalar algunas bibliotecas esenciales. Comencemos instalando los paquetes necesarios:

Nota: si está utilizando una versión anterior depip , puede utilizar el comandopip install --upgrade pip para actualizar con el fin de facilitar la instalación de los paquetes más recientes, que pueden no ser compatibles con versiones anteriores. Pero si ya está utilizando la última versión o ha actualizado recientemente sus paquetes, puede omitir este comando.

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

En la celda de código anterior,

  • langchain  es el marco central para crear aplicaciones con modelos de lenguaje.
  • faiss-cpu  es para una búsqueda de similitud eficiente, utilizada para crear y consultar índices vectoriales.
  • pandas  es para la manipulación y el análisis de datos.
  • sentence-transformers  es generar embeddings para la búsqueda semántica.
  • langchain-ibm  es para integrar IBM watsonxLLM (en este tutorial es granite-3-8b-instruct) con LangChain.

Este paso garantiza que su entorno esté listo para las tareas futuras.

Paso 4. Importe las bibliotecas necesarias

Ahora que hemos instalado las bibliotecas necesarias, importemos los módulos necesarios para este 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

En la celda de código anterior,

  • el so  proporciona una forma de interactuar con el sistema operativo (por ejemplo, accediendo a variables de entorno).
  • langchain_ibm.WatsonxLLM  nos permite utilizar Granite LLM de IBM® watsonx de manera fluida dentro del marco LangChain.
  • langchain.embeddings.HuggingFaceEmbeddings  genera embeddings para texto utilizando modelos HuggingFace, esenciales para la búsqueda semántica.
  • langchain.vectorstores.FAISS  es una biblioteca para el almacenamiento eficiente de vectores y la búsqueda de similitudes, lo que nos permite crear y consultar un índice vectorial.
  • RecursiveCharacterTextSplitter  ayuda a dividir grandes bloques de texto en fragmentos más pequeños, lo que es crítico para procesar documentos de manera eficiente.
  • pandas  es una potente biblioteca para el análisis y la manipulación de datos, que se utiliza aquí para manejar datos tabulares.
  • getpass  es una forma segura de capturar entradas confidenciales, como claves API, sin mostrarlas en la pantalla.

Este paso configura todas las herramientas y módulos que necesitamos para procesar texto, crear embeddings, almacenarlos en una base de datos vectorial e interactuar con watsonxLLM de IBM.

Paso 5. Configurar credenciales

Este código establece las credenciales para acceder a la API de IBM® watsonx Machine Learning (WML) y garantiza que el ID del proyecto esté configurado correctamente.

  • Un diccionariocredentials se crea con elURL del servicio WML yClave API . La clave de API se recopila de forma segura mediante `getpass.getpass` para evitar exponer información sensible.
  • El código intenta obtener el PROJECT_ID  de variables de entorno utilizando os.environ . Si el PROJECT_ID  no se encuentra, se solicita al usuario que lo introduzca manualmente a través de la entrada.
# 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): ")

Paso 6. Inicializar el modelo de lenguaje grande

Este código inicializa IBM watsonxLLM para su uso en la aplicación:

  1. Este código crea una instancia de watsonxLLM utilizando elibm/granite-3-8b-instruct diseñado para tareas de IA generativa basadas en instrucciones.
  2. Elurl ,clave API  yproject_id Los valores de las credenciales configuradas previamente se pasan para autenticar y conectarse al servicio IBM watsonxLLM.
  3. Configura el parámetromax_new_tokens para limitar el número de tokens generados por el modelo en cada respuesta (150 tokens en este caso).

Este paso prepara watsonxLLM para generar respuestas en el flujo de trabajo.

# 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
      }
)

Paso 7. Definir una función para extraer texto de un archivo

Para procesar el texto de un documento, necesitamos una función que pueda leer y extraer su contenido. La siguiente función está diseñada para gestionar archivos de texto sin formato:

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

Esta función,extract_text_from_txt , está diseñada para leer y extraer el contenido de un archivo de texto sin formato. Acepta la ruta del archivo como argumento y abre el archivo en modo de lectura conCodificación UTF-8 , asegurándose de que los caracteres especiales se manejen correctamente.

Todo el contenido del archivo se lee en una variable llamadatexto , que luego se devuelve. Esta función desempeña un papel crucial en la preparación de los datos de entrada extrayendo texto sin procesar del documento, dejándolo listo para operaciones posteriores como fragmentación, embedding y consulta. Proporciona una forma sencilla y eficaz de procesar datos textuales de cualquier archivo de texto sin formato.

Esta función nos permite procesar el archivo de entrada (Las aventuras de Sherlock Holmes) y extraer su contenido para otras operaciones, como la fragmentación y embedding. Garantiza que el texto sin procesar esté fácilmente disponible para su análisis.

Paso 8. Divida el texto en fragmentos

Para procesar e indexar de manera eficiente grandes bloques de texto, necesitamos dividir el texto en fragmentos más pequeños y manejables. La siguiente función se encarga de esta tarea:

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)

Elsplit_text_into_chunks está diseñada para dividir grandes bloques de texto en fragmentos más pequeños y manejables para un procesamiento e indexación eficientes. Toma el texto sin procesar como entrada junto con dos parámetros opcionales:chunk_size , que define el tamaño máximo de cada fragmento (el valor predeterminado es 500 caracteres), ychunk_overlap , que especifica el número de caracteres superpuestos entre fragmentos consecutivos (el valor predeterminado es 50).

Esta función garantiza la continuidad contextual entre los fragmentos. La función utiliza elRecursiveCharacterTextSplitter fromLangChain , que divide de forma inteligente el texto conservando su contexto. Al devolver una lista de fragmentos de texto más pequeños, esta función prepara la entrada para otras operaciones como embeddings e indexacines.

Es esencial cuando se trabaja con documentos grandes, ya que los modelos lingüísticos suelen tener limitaciones en cuanto al número de tokens y no pueden procesar directamente textos largos.

Paso 9: Crear un índice vectorial

Para permitir una búsqueda semántica eficiente, necesitamos convertir fragmentos de texto en embeddings y almacenar en un índice de búsqueda. Este paso utiliza las embeddings FAISS y HuggingFace para crear el índice vectorial, formando la base para recuperar información relevante basada en consultas.

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

Elcreate_vector_index construye un índiceFAISS vector a partir de los fragmentos de texto generados en el paso anterior. Esta función es crucial para permitir la búsqueda semántica asignando cada fragmento a un espacio vectorial de alta dimensión mediante el uso de embeddings.

Primero inicializa un modelo HuggingFaceEmbeddings sentence-transformers/all-MiniLM-L6-v2 , que genera embeddings vectoriales para los fragmentos de texto. Estos embeddings capturan el significado semántico de cada fragmento.

A continuación, la función utilizaFAISS para crear un almacén de vectores indexando estos embeddings, lo que permite una búsqueda eficiente de similitudes más adelante.

El almacén de vectores resultante se devuelve y se utilizará para encontrar fragmentos relevantes basados en las consultas de los usuarios, formando la columna vertebral del proceso de búsqueda y recuperación del agente.

Paso 10. Consultar el índice vectorial con Granite

Este paso implica consultar el índice vectorial para recuperar información relevante y utilizar LLM Granite de IBM para generar una respuesta refinada. Al integrar la búsqueda de similitud y el razonamiento LLM, la función proporciona un proceso de resolución de consultas dinámico e inteligente.

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()
                     }

Elquery_index_with_granite_dynamic toma tres entradas: primera, el almacén de vectores (vector_store ), segunda, la consulta del usuario (query ) y tercera, la instancia Granite LLM (LLM ).

Primero realiza una búsqueda de similitud en el índice vectorial para recuperar los fragmentos de texto más relevantes. Estos fragmentos, denominadosobservation , se combinan en un único bloque de contexto.

A continuación, la función crea una instrucción combinando la consulta y el contexto recuperado. Esta instrucción se pasa aGranite LLM , que genera una respuesta detallada y contextualmente precisa (final_answer ).

A lo largo del proceso, los pasos intermedios como elthought ,acción  yaction input  se imprimen para mayor transparencia.

Por último, la función devuelve un diccionario que contiene todos los componentes, incluido el proceso de pensamiento, la acción realizada, la observación recuperada y la respuesta final.

Este paso es crítico para transformar la recuperación de datos sin procesar en conocimientos significativos y que se pueden ejecutar mediante el uso de las capacidades de razonamiento del LLM.

Paso 11. Genere un 'DataFrame' para los resultados de la consulta

Este paso procesa dinámicamente múltiples consultas, recupera información relevante y guarda los resultados en un formato estructurado para su análisis. La función integra capacidades de consulta, estructuración de datos y exportación.

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}")

Eldynamic_output_to_dataframe  acepta cuatro entradas: el almacén de vectores (vector_store ), una lista de consultas (queries ), la instancia de Granite LLM (LLM ) y un nombre de archivo CSV opcional (csv_filename , el valor predeterminado esoutput.csv ).

Para cada consulta, utiliza elquery_index_with_granite_dynamic  para recuperar el contexto relevante y generar una respuesta utilizando el LLM. Los resultados, incluidos los componentes intermedios comoThought ,Observation  yRespuesta final  se almacenan en una lista.

Una vez procesadas todas las consultas, la lista de resultados se convierte en un DataFrame de pandas. Este formato tabular permite un fácil análisis y visualización de los resultados de la consulta. El DataFrame se imprime para su revisión y se guarda como un archivo CSV para su uso futuro.

Este paso es esencial para organizar el output en un formato fácil de usar, lo que permite realizar tareas posteriores como la evaluación de la precisión y la visualización.

Paso 12: Ejecutar el flujo de trabajo principal

Este paso combina todos los pasos anteriores en un único flujo de trabajo para procesar un archivo de texto, responder a las consultas de los usuarios y guardar los resultados en un formato estructurado. Lafunción main_workflow sirve como orquestador central 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)

Comprendamos cómo se ejecuta este flujo de trabajo:

Introduzca un archivo de texto: la variablefile_path especifica el archivo de texto que se va a procesar. En este tutorial, el archivo de entrada es"aosh.txt" , que contiene el texto de Las aventuras de Sherlock Holmes.

Extracción de texto:extract_text_from_txt se emplea para leer y extraer el contenido del archivo de texto de entrada.

Fragmentación de texto: el texto extraído se divide en fragmentos más pequeños utilizando la funciónsplit_text_into_chunks para facilitar el embedding y la indexación.

Creación de un índice vectorial: los fragmentos de texto se convierten en embeddings y se almacenan en unFAISS vector índice utilizando elcreate_vector_index empresariales.

Definición de consultas: se proporciona una lista de consultas de muestra, cada una diseñada para recuperar información específica del texto. Estas consultas serán respondidas por el agente.

Consultas de proceso: la funcióndynamic_output_to_dataframe  procesa las consultas utilizando el índice vectorial y Granite LLM de IBM. Recupera el contexto relevante, genera respuestas y guarda los resultados como un archivo CSV para su posterior análisis.

Este paso integra todos los componentes del tutorial en un flujo de trabajo cohesivo. Automatiza el proceso desde la extracción de texto hasta la resolución de consultas, lo que le permite probar las capacidades del agente y examinar los resultados en un formato estructurado.

Para ejecutar el flujo de trabajo, simplemente llame almain_workflow()  y todo el pipeline se ejecutará de manera fluida.

# Run the workflow
main_workflow()

Resultado


 

> 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

 

Después de ejecutar la funciónmain_workflow() procesamos un archivo de texto (aosh.txt) y ejecutamos cuatro consultas definidas por el usuario sobre Las aventuras de Sherlock Holmes. La salida proporciona un desglose detallado de cómo se manejó cada consulta:

  • Thought describe el razonamiento detrás de la consulta y el contexto que requiere para una respuesta precisa.
  • Action indica el paso dado, que en este caso es realizar una búsqueda de similitud utilizando el índice vectorial FAISS.
  • Action input es la consulta específica que se procesa en una iteración.
  • Observation son los fragmentos de texto recuperados del índice vectorial que son relevantes para la consulta.
  • Final answer es la respuesta detallada generada por Granite LLM de IBM utilizando el contexto recuperado.

Además, los resultados de todas las consultas se han estructurado en un DataFrame y se han guardado comooutput.csv . Este archivo contiene todos los componentes anteriores para su posterior análisis o intercambio.

En este proceso, combinamos la recuperación de texto con el razonamiento LLM para responder a consultas complejas sobre el libro. El agente recuperó dinámicamente la información relevante, utilizó el contexto para generar respuestas precisas y organizó el resultado en un formato estructurado para facilitar el análisis.

Visualización de los resultados

Con el archivo output.csv creado, ahora procederemos a visualizar los resultados y las métricas asociadas, proporcionando conocimiento más detallado sobre el rendimiento del agente.

En la siguiente celda de código, cargamos los resultados de consulta guardados del archivooutput.csv en un DataFrame de pandas para preparar la visualización y el análisis. El DataFrame nos permite manipular y explorar los datos en un formato estructurado.

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

RESULTADO


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...

En este código, el DataFrame incluye componentes clave comoThought ,Acción ,Observation  yFinal Answer  para cada consulta. Al mostrar las primeras filas utilizandodf.head() , nos aseguramos de que los datos tengan el formato correcto y estén listos para la siguiente etapa: crear visualizaciones significativas.

Importar bibliotecas de visualización

Para crear visualizaciones de los resultados de la consulta, importamos las bibliotecas necesarias:

import matplotlib.pyplot as plt
from wordcloud import WordCloud

 

matplotlib.pyplot  es una biblioteca ampliamente utilizada para crear visualizaciones estáticas, interactivas y animadas en Python. Se utilizará para generar gráficos de barras, gráficos circulares y otras visualizaciones.

wordcloud  es una biblioteca para crear nubes de palabras, que resaltan visualmente las palabras más frecuentes en los datos. Este paso ayuda a resumir y explorar el contexto recuperado del texto.

Nota importante: Si encuentra el error"WordCloud not found" , puede resolverlo instalando la biblioteca mediante el comandopip install wordcloud .

Visualice la longitud de las observaciones y las respuestas

Este código crea un gráfico de barras horizontales para comparar la longitud de las observaciones (contexto recuperado) y las respuestas (respuestas generadas) para cada consulta. Esta visualización proporciona conocimiento sobre cuánto contexto utiliza el agente en comparación con la longitud de las respuestas generadas.

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)
 
Longitudes de observación y respuesta por consulta

Esta función,visualize_lengths_with_queries , crea un gráfico de barras horizontales para comparar la longitud de las observaciones (contexto recuperado) y las respuestas (respuestas generadas) para cada consulta.

Calcula la longitud de los caracteres tanto de las observaciones como de las respuestas, agregándolas como nuevas columnas (Observation Length  yAnswer Length ) al DataFrame. Al usarMatplotlib , luego traza estas longitudes para cada consulta, con las consultas mostradas en el eje y para una mejor legibilidad.

El gráfico de barras está codificado por colores para diferenciar entre longitudes de observación y respuesta, e incluye etiquetas, una leyenda y un título para mayor claridad.

Esta visualización ayuda a analizar el equilibrio entre el tamaño del contexto recuperado y el detalle de la respuesta generada, ofreciendo información sobre cómo el agente procesa y responde a las consultas.

Visualice la proporción de texto utilizado en las observaciones

Este paso visualiza cuánto del texto total procesado por el agente se utiliza en observaciones (contexto recuperado) en comparación con el texto restante. Se crea un gráfico circular para proporcionar una representación intuitiva de la proporción.

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)
Diagrama de Proporción de texto utilizado en las observaciones

Elvisualize_text_proportion  crea un gráfico circular para ilustrar la proporción del texto total que se utiliza en las observaciones (contexto recuperado) en comparación con el texto restante. Calcula la longitud total del texto sumando la longitud de los caracteres de todas las observaciones y respuestas y, a continuación, determina la parte aportada únicamente por las observaciones.

Estos datos se visualizan en un gráfico circular, con etiquetas claras para"Texto de observación" y"Texto restante" y colores distintos para mejorar la legibilidad. El gráfico incluye valores porcentuales para facilitar la interpretación de las proporciones.

Esta visualización proporciona una visión general de alto nivel de la cantidad de texto que el agente utiliza como contexto durante el procesamiento de consultas, ofreciendo información sobre la eficiencia y el enfoque del proceso de recuperación.

Genere nubes de palabras para observaciones y respuestas finales

Este código genera dos nubes de palabras para representar visualmente las palabras más frecuentes en el Observation y Respuesta final textos.

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)
 
Nube de palabras de observaciones

Este código genera dos nubes de palabras para representar visualmente las palabras más frecuentes en elObservation yRespuesta final textos, mostrándolos uno al lado del otro para facilitar la comparación. Los textosObservation  yRespuesta final  se concatenan primero en dos cadenas separadas utilizando" ".join()  para combinar todas las filas de las columnas respectivas. La bibliotecaWordCloud se utiliza después para generar nubes de palabras para cada texto con configuraciones específicas.

Para crear una visualización en paralelo, se utilizan subtramas: la primera subtrama muestra la nube de palabras paraObservation y la segunda muestra la deRespuesta final . La función tight_layout()  garantiza un espaciado ordenado entre las parcelas. Estas nubes de palabras nos permiten analizar intuitivamente el rendimiento del agente destacando los términos clave recuperados del contexto (Observation ) y las destacadas en las respuestas (Respuesta final ).

Pruebe la precisión del agente

En esta sección, evaluamos el rendimiento del agente utilizando múltiples métricas de precisión:Coincidencia de palabras clave ,Puntuaciones BLEU ,Precisión/Recuperación  yPuntuaciones F1 . Estas métricas proporcionan una visión completa de lo bien que el agente genera respuestas precisas y relevantes basadas en las consultas de los usuarios.

Importar las bibliotecas necesarias

Antes de comenzar las pruebas, importamos las bibliotecas necesarias para la evaluación de la precisión.

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

Estas bibliotecas incluyen herramientas para la coincidencia de palabras clave, el cálculo de la puntuación BLEU, la precisión y la evaluación de la recuperación. Asegúrese de haber instalado estas bibliotecas en su entorno para evitar errores de importación.

Precisión de la concordancia de palabras clave

Esta prueba evalúa en qué medida las respuestas generadas incluyen las palabras clave de las consultas. UtilizaCountVectorizer para tokenizar y extraer palabras clave de las consultas y respuestas. La función calcula la proporción de palabras clave de consulta presentes en la respuesta generada, marcando la respuesta como precisa si esta proporción supera un umbral (0,5 por defecto). Los resultados se añaden al DataFrame enKeyword Match Score yIs Accurate columns .

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
Este es el resultado de una celda de código para el tutorial en una página de Think.

Cálculo de la puntuación BLEU

Esta prueba mide en qué medida las respuestas generadas coinciden con las observaciones recuperadas.BLEU (suplente de evaluación bilingüe) es una métrica popular para evaluar la similitud del texto basada en coincidencias den-gram . La función calculaBLEU scores  para cada par de consulta-respuesta y los añade al DataFrame en la columna de puntuación 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)
Diagrama creado para un tutorial en la página Think.

Precisión y recuperación

La precisión y la recuperación se calculan para evaluar la relevancia e integridad de las respuestas. La precisión mide la proporción de palabras recuperadas en la respuesta que son relevantes, mientras que la recuperación mide la proporción de palabras relevantes en la observación que aparecen en la respuesta.

Estas métricas se adjuntan al DataFrame en las columnas Precision y Recuperación .

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
Salida de una celda de código para el tutorial ubicada en una página Think

Cálculo de la puntuación F1

La puntuación F1 combina precisión y recuperación en una sola métrica, proporcionando una evaluación equilibrada de relevancia e integridad. La fórmula para la puntuación F1 es: F1 Score = 2 * (Precision * Recall) / (Precision + Recall)

El valor calculadoF1 scores se añaden al DataFrame en la columna de puntuación 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
Salida de una celda de código para el tutorial ubicada en una página Think

Resumir métricas de precisión

Por último, una función de resumen consolida todas las métricas para proporcionar una visión general del rendimiento del agente. Calcula el número total de consultas, el recuento y el porcentaje de respuestas precisas y las puntuaciones medias de BLEU y F1.

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)

RESULTADO


 

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

Estas pruebas de precisión ofrecen una evaluación detallada de la capacidad del agente para generar respuestas relevantes y precisas. Cada prueba se centra en un aspecto específico, desde la inclusión de palabras clave hasta la similitud del texto y la integridad de la respuesta. El resumen consolida estas métricas para ofrecer una instantánea general del rendimiento.

Resumen

Este tutorial le ha guiado a través del proceso de creación de un agente autónomo con Granite LLM y LangChain de IBM. Desde la extracción de texto hasta la vectorización y la resolución de consultas, cubrimos todo el proceso de diseño e implementación de un agente funcional basado en LLM. Los pasos clave incluyeron la gestión de la memoria con almacenes de vectores, el procesamiento de consultas y la generación de respuestas utilizando Granite.

Evaluamos el rendimiento del agente utilizando métricas de precisión como la coincidencia de palabras clave, las puntuaciones BLEU, la precisión, la recuperación y las puntuaciones F1. Visualizaciones como gráficos de barras, gráficos circulares y nubes de palabras proporcionaron conocimiento adicional sobre el comportamiento y la eficacia del agente.

Al completar este tutorial, ha aprendido a diseñar, probar y visualizar el rendimiento de un agente LLM. Esta base puede ampliarse para abordar conjuntos de datos más complejos, mejorar la precisión y explorar características avanzadas como los sistemas multiagente.

Soluciones relacionadas
Agentes de IA para empresas

Cree, implemente y gestione potentes asistentes y agentes de IA que automaticen flujos de trabajo y procesos con IA generativa.

    Explore watsonx Orchestrate
    Soluciones de agente de IA de IBM

    Construya el futuro de su empresa con soluciones de IA en las que puede confiar.

    Explore las soluciones de los agentes de IA
    Servicios de IA de IBM Consulting

    Los servicios de IA de IBM Consulting ayudan a reinventar la forma de trabajar de las empresas usando IA para la transformación.

    Explore los servicios de inteligencia artificial
    Dé el siguiente paso

    Tanto si opta por personalizar las aplicaciones y habilidades prediseñadas como si prefiere crear e implementar servicios agentivos personalizados mediante un estudio de IA, la plataforma IBM watsonx le ofrece todo lo que necesita.

    Explore watsonx Orchestrate Explore watsonx.ai