Cree un sistema de gestión de contratos multiagente con BeeAI y Granite

Autor

Anna Gutowska

AI Engineer, Developer Advocate

IBM

En este tutorial, creará un sistema multiagente totalmente local con IBM® Granite utilizando BeeAI en Python. Estos agentes colaborarán para negociar un acuerdo contractual de servicios de paisajismo entre dos empresas teniendo en cuenta las tendencias del mercado y las limitaciones presupuestarias internas. El flujo de trabajo estará compuesto por un agente asesor presupuestario, un agente sintetizador de contratos, un agente de búsqueda web y un agente asesor de compras. Dado el contrato, los datos presupuestarios, el sector de servicios y los nombres de las empresas proporcionados por el usuario, los agentes colaboran para producir un correo electrónico para negociar los términos del contrato a favor del cliente.

¿Qué es BeeAI?

BeeAI, lanzada por IBM® Research y ahora donada a la Linux Foundation, es una plataforma de IA agéntica de código abierto que proporciona a los desarrolladores la capacidad de crear agentes de IA desde cualquier marco1.

Un agente de inteligencia artificial (IA) se refiere a un sistema o programa creado mediante el uso de un modelo de lenguaje de gran tamaño (LLM) para realizar tareas de forma autónoma en nombre de un usuario u otro sistema mediante el diseño de su flujo de trabajo y utilizando las herramientas disponibles. Los agentes de IA son más avanzados que los chatbots tradicionales de LLM, ya que pueden acceder a herramientas predefinidas, planificar acciones futuras y requieren poca o ninguna intervención humana para resolver y automatizar problemas complejos.

El predecesor de BeeAI es Bee Agent Framework, un marco de código abierto específico para crear agentes LLM únicos. Por el contrario, BeeAI ofrece un ecosistema más avanzado para crear y coordinar flujos de trabajo multiagente.

BeeAI:

  • Es independiente del marco.
  • Está disponible tanto en TypeScript como en Python.
  • Está basado en el Agent Communication Protocol (ACP) diseñado por IBM Research, que lleva el Model Context Protocol (MCP) un paso más allá al estandarizar la forma en que los agentes se comunican entre sí. ACP reúne agentes de varios marcos como LangGraphcrewAI y BeeAI en un tiempo de ejecuciónconstante. 2
  • Está integrado con Arize Phoenix, una herramienta de código abierto para rastrear el comportamiento de los agentes, lo que permite la observabilidad. Puede encontrar más información sobre la depuración de agentes a través de la información de registro y la telemetría disponibles en la documentación oficial.
  • Es capaz de ejecutarse de forma totalmente local en su máquina. Los agentes también se pueden implementar en entornos compartidos.
  • Proporciona interfaces unificadas para diversas funcionalidades, como chat, embeddings y resultados, como JSON, lo que permite un intercambio de modelos fluido sin necesidad de realizar cambios en el código existente.

Pasos

Esta guía paso a paso se puede encontrar en nuestro repositorio de GitHub en forma de Jupyter Notebook.

Paso 1. Configurar entorno

Primero tenemos que configurar nuestro entorno cumpliendo algunos requisitos previos.
1. En este tutorial, no utilizaremos una interfaz de programación de aplicaciones (API) como las disponibles a través de IBM® watsonx y OpenAI. En su lugar, podemos instalar la última versión de Ollama para ejecutar el modelo a nivel local.
La forma más sencilla de instalar Ollama para macOS, Linux y Windows es a través de su página web: https://ollama.com/download. Este paso instalará una aplicación en la barra de menú para ejecutar el servidor Ollama en segundo plano y mantenerlo al día con las últimas versiones.
Como alternativa, puede instalar Ollama con homebrew en su terminal:

brew install ollama

Si se instala desde brew o se compila desde el código fuente, es necesario arrancar el servidor central:

ollama serve

2. Hay varios LLM que admiten la llamada a herramientas, como los últimos modelos Llama de Meta y los modelos Mistral de Mistral AI. Para este tutorial, utilizaremos el modelo Granite 3.3 de código abierto de IBM. Este modelo cuenta con capacidades mejoradas de razonamiento y seguimiento de instrucciones3. Obtenga el último modelo de Granite 3.3 al ejecutar el siguiente comando en su terminal.

ollama pull granite3.3:8b

3. Para evitar conflictos de dependencia de paquetes, configuremos un entorno virtual. Para crear un entorno virtual con la versión 3.11.9 de Python, ejecute el siguiente comando en su terminal.

python3.12 -m venv .venv

A continuación, para activar el entorno, ejecute:

source .venv/bin/activate

4. Su archivo requirements.txt debe contener los siguientes paquetes. Estos paquetes son necesarios para crear agentes con el marco BeeAI e incorporar las clases LangChain necesarias para la inicialización de herramientas.

beeai-framework
beeai-framework[duckduckgo]
langchain-core
langchain-community
pandas

Para instalar estos paquetes, ejecute el siguiente comando en su terminal.

pip install -r requirements.txt

5. Cree un nuevo archivo Python llamado bee-script.py ejecutando este comando en su terminal:

touch bee-script.py

En la parte superior del nuevo archivo Python, incluya las declaraciones de importación para las bibliotecas y módulos necesarios.

import asyncio
import pandas as pd
import os
import traceback
import sys

from beeai_framework.backend import ChatModel
from beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchTool
from beeai_framework.workflows.agent import AgentWorkflow, AgentWorkflowInput
from beeai_framework.errors import FrameworkError
from beeai_framework.adapters.langchain import LangChainTool
from langchain_core.tools import StructuredTool
from typing import Any

Paso 2. Crear una instancia del agente y del flujo de trabajo

En un método principal asíncrono que utiliza asyncio , incorporemos las clases ChatModel y AgentWorkflow para crear instancias de nuestro flujo de trabajo y LLM Granite. Basta con proporcionar el ID del modelo Ollama y un nombre para el flujo de trabajo. Al instanciar este flujo de trabajo, podemos añadir agentes y crear nuestro sistema multiagente. Añada este método principal a su archivo Python.

async def main() -> None:
    llm = ChatModel.from_name("ollama:granite3.3:8b")
    workflow = AgentWorkflow(name="Procurement")

Para ver una representación visual del flujo de trabajo agéntico, consulte el siguiente diagrama.

Flujo de trabajo agéntico para la gestión de contratos Flujo de trabajo agéntico para la gestión de contratos

Ensamblaremos cada componente de este flujo de trabajo en los siguientes pasos.

Paso 3. Solicitar entrada al usuario

Nuestro flujo de trabajo depende de la entrada del usuario. Las entradas iniciales necesarias son los nombres de las empresas cliente y contratista. Al ejecutar el flujo de trabajo en un paso posterior, se le mostrará al usuario el siguiente texto. Añada el siguiente código al método principal.

client_company = input("Please enter the client company: ") #Example: Company A
contractor_company = input("Please enter the contractor company: ") #Example: Company B

También necesitaremos los nombres de los archivos que contienen el informe presupuestario de la empresa cliente,budget-data.csv , así como el archivo que contiene el contrato entre las dos empresas, contract.txt . En nuestro ejemplo, el contrato se refiere a los servicios de paisajismo prestados por la empresa contratista a la empresa cliente. Puede encontrar los archivos de muestra en nuestro repositorio de GitHub. La estructura del proyecto debería ser similar a la siguiente:

├── .venv/ # Virtual environment
├── bee-script.py # The Python script
├── contract.txt # The contract
└── budget-data.csv # Client's budget report

En el siguiente código, también verificamos las extensiones de los archivos para asegurarnos de que coinciden con el formato previsto. Si alguno de los archivos es un tipo de incorrecto, se le pedirá al usuario que vuelva a intentarlo.

client_budget_file = input(f"Enter the file name of the budget report for {client_company} (in the same directory level): ") #Example: budget_data.csv
while os.path.splitext(client_budget_file)[1].lower() != ".csv":
    client_budget_file = input(f"Budget report must be in .csv format, please try again: ")

contract_file = input(f"Enter the file name of the contract between {client_company} and {contractor_company} (in the same directory level): ") #Example: contract.txt
while os.path.splitext(contract_file)[1].lower() != ".txt":
    contract_file = input(f"Contract must be in .txt format, please try again: ")

La última entrada que debe introducir usuario es el sector al que pertenece el servicio descrito en el contrato. Esta entrada puede ser de finanzas, construcción u otros.

service_industry = input(f"Enter the industry of the service described in this contract (e.g., finance, construction, etc.): ") #Example: landscaping

Paso 4. Configurar la herramienta presupuestaria

La primera herramienta que podemos crear en nuestro sistema multiagente es para el agente asesor presupuestario. Este agente se encarga de leer los datos presupuestarios del cliente. La función que se proporciona al agente esget_budget_data , en la que se lee el archivo CSV que contiene los datos presupuestarios y se devuelve un mensaje de error en caso de que el archivo no exista o se produzca un error inesperado. Para acceder al archivo con el nombre de archivo proporcionado por el usuario, primero debemos recuperar el directorio actual. Podemos hacerlo utilizando el siguiente métodoso .

current_directory = os.getcwd()

Ahora, configuremos la fuerza motriz del agente, la función get_budget_data , que utiliza el directorio actual, y la entrada del usuario para acceder y leer el archivo.

def get_budget_data():
    try:
        budget = pd.read_csv(os.path.join(current_directory, client_budget_file))
    except FileNotFoundError:
        return client_budget_file + " not found. Please check correct file name."
    except Exception as e:
        return f"An error occurred: {e}"
    return budget

Para garantizar el uso adecuado de esta herramienta, utilicemos la clase StructuredTool de LangChain. Aquí proporcionamos la función, el nombre de la herramienta, la descripción de la herramienta y establecemos el parámetro return_direct como verdadero o falso. Este último parámetro simplemente informa al agente si debe devolver el resultado de la herramienta directamente o sintetizarlo.

get_budget = StructuredTool.from_function(
    func=get_budget_data,
    name="GetBudgetData",
    description=f"Returns the budget data for {client_company}.",
    return_direct=True,
)

Con el adaptador LangChain de BeeAI, LangChainTool, podemos finalizar la inicialización de nuestra primera herramienta.

budget_tool = LangChainTool[Any](get_budget)

Paso 5. Configurar la herramienta de contratos

La siguiente herramienta que podemos crear es para el agente sintetizador de contratos. Este agente se encarga de leer el contrato entre el cliente y el contratista. La función que se proporciona al agente es get_contract_data , en la que se lee el archivo de texto que contiene el contrato y se devuelve un mensaje de error en caso de que el archivo no exista o se produzca un error inesperado. El código necesario para este paso es similar al del paso 3.

def get_contract_data():
    try:
        with open(os.path.join(current_directory, contract_file), 'r') as file:
            content = file.read()
    except FileNotFoundError:
        return contract_file + " not found. Please check correct file name."
    except Exception as e:
        return f"An error occurred: {e}"
    return content
get_contract = StructuredTool.from_function(
    func=get_contract_data,
    name="GetContractData",
    description=f"Returns the contract details.",
    return_direct=True,
)
contract_tool = LangChainTool[Any](get_contract)

Paso 6. Establecer el flujo de trabajo agéntico

En este paso, podemos añadir los distintos agentes a nuestro flujo de trabajo. Proporcionemos al asesor presupuestario y a los agentes sintetizadores de contratos sus herramientas personalizadas correspondientes. También podemos establecer el nombre del agente, su función, las instrucciones, la lista de herramientas y el LLM. 

workflow.add_agent(
    name="Budget Advisor",
    role="A diligent budget advisor",
    instructions="You specialize in reading internal budget data in CSV format.",
    tools=[budget_tool],
    llm=llm,
)

workflow.add_agent(
    name="Contract Synthesizer",
    role="A diligent contract synthesizer",
    instructions=f"You specialize in reading contracts.",
    tools=[contract_tool],
    llm=llm,
)

Para buscar en la web las tendencias del mercado en el sector relevante, podemos crear un agente con acceso a la herramienta prediseñada LangChain DuckDuckGoSearchTool . Esta herramienta obtiene datos de la web con el motor de búsqueda DuckDuckGo.

workflow.add_agent(
    name="Web Search",
    role="A web searcher.",
    instructions=f"You can search the web for market trends, specifically in the {service_industry} industry.",
    tools=[DuckDuckGoSearchTool()],
    llm=llm,
)

El cuarto y último agente de nuestro sistema multiagente es el asesor de compras. Este agente se encarga de utilizar la información recuperada y sintetizada por los demás agentes para redactar un correo electrónico convincente dirigido a la empresa contratista a favor del cliente. El correo electrónico debe tener en cuenta las tendencias del mercado y las restricciones presupuestarias internas del cliente para negociar los términos del contrato. Este agente no necesita ninguna herramienta externa, sino que se guía por sus instrucciones.

workflow.add_agent(
    name="Procurement Advisor",
    role="A procurement advisor",
    instructions=f"You write professional emails to {contractor_company} with convincing negotiations that factor in market trends and internal budget constraints. You represent {client_company}.",
    llm=llm,
)

Paso 7. Ejecutar el flujo de trabajo agéntico

Ahora podemos finalizar nuestro método principal con todo el código que hemos escrito hasta ahora. Al final del método principal, podemos incluir la ejecución del flujo de trabajo agéntico. Gracias a la palabra claveawait , podemos gestionar de forma eficiente la ejecución simultánea de tareas, así como esperar a que se ejecute cada una de ellas. Su archivo Python debería contener el siguiente código.

import asyncio
import pandas as pd
import os
import traceback
import sys

from beeai_framework.backend import ChatModel
from beeai_framework.tools.search.duckduckgo import DuckDuckGoSearchTool
from beeai_framework.workflows.agent import AgentWorkflow, AgentWorkflowInput
from beeai_framework.errors import FrameworkError
from beeai_framework.adapters.langchain import LangChainTool
from langchain_core.tools import StructuredTool
from typing import Any

async def main() -> None:

    llm = ChatModel.from_name("ollama:granite3.3:8b")

    workflow = AgentWorkflow(name="Procurement Agent")

    client_company = input("Please enter the client company: ")
    contractor_company = input("Please enter the contractor company name: ")

    client_budget_file = input(f"Enter the file name of the budget report for {client_company} (in the same directory level): ")
while os.path.splitext(client_budget_file)[1].lower() != ".csv":
        client_budget_file = input(f"Budget report must be in .csv format, please try again: ")

    contract_file = input(f"Enter the file name of the contract between {client_company} and {contractor_company} (in the same directory level): ")
while os.path.splitext(contract_file)[1].lower() != ".txt":
        contract_file = input(f"Contract must be in .txt format, please try again: ")

    service_industry = input(f"Enter the industry of the service described in this contract (e.g., finance, construction, etc.): ")
current_directory = os.getcwd()
def get_budget_data():
        try:
            budget = pd.read_csv(os.path.join(current_directory, client_budget_file))
        except FileNotFoundError:
            return client_budget_file + " not found. Please check correct file name."
        except Exception as e:
            return f"An error occurred: {e}"
        return budget

    get_budget = StructuredTool.from_function(
            func=get_budget_data,
            name="GetBudgetData",
            description=f"Returns the budget data for {client_company}.",
            return_direct=True,
        )

    budget_tool = LangChainTool[Any](get_budget)

    def get_contract_data():
        try:
            with open(os.path.join(current_directory, contract_file), 'r') as file:
                content = file.read()
        except FileNotFoundError:
            return contract_file + " not found. Please check correct file name."
        except Exception as e:
            return f"An error occurred: {e}"
        return content

    get_contract = StructuredTool.from_function(
            func=get_contract_data,
            name="GetContractData",
            description=f"Returns the contract details.",
            return_direct=True,
        )

    contract_tool = LangChainTool[Any](get_contract)

    workflow.add_agent(
        name="Budget Advisor",
        role="A diligent budget advisor",
        instructions="You specialize in reading internal budget data in CSV format.",
        tools=[budget_tool],
        llm=llm,
    )

    workflow.add_agent(
        name="Contract Synthesizer",
        role="A diligent contract synthesizer",
        instructions=f"You specialize in reading contracts.",
        tools=[contract_tool],
        llm=llm,
    )

    workflow.add_agent(
        name="Web Search",
        role="A web searcher.",
        instructions=f"You can search the web for market trends, specifically in the {service_industry} industry.",
        tools=[DuckDuckGoSearchTool()],
        llm=llm,
    )

    workflow.add_agent(
        name="Procurement Advisor",
        role="A procurement advisor",
        instructions=f"You write professional emails to {contractor_company} with convincing negotiations that factor in market trends and internal budget constraints. You represent {client_company}.",
        llm=llm,
    )

    response = await workflow.run(
        inputs=[
            AgentWorkflowInput(
                prompt=f"Extract and summarize the key obligations, deliverables, and payment terms from the contract between {client_company} and {contractor_company}.",
            ),
            AgentWorkflowInput(
                prompt=f"Analyze the internal budget data for {client_company}.",
            ),
            AgentWorkflowInput(
                prompt=f"Write a formal email to {contractor_company}. In the email, negotiate the contract terms in favor of {client_company}, factoring in market trends and internal budget constraints.",
            ),
        ]
    ).on(
        "success",
        lambda data, event: print(
            f"-> Step '{data.step}' has been completed with the following outcome.\n\n{data.state.final_answer}"     
        ),
    )

    print("Final email: ")
    print(response.state.final_answer)

if __name__ == "__main__":
    try:
        asyncio.run(main())
    except FrameworkError as e:
        traceback.print_exc()
        sys.exit(e.explain())

Para obtener un contrato y datos presupuestarios de muestra, así como el script final, consulte nuestro repositorio GitHub. Para ejecutar el proyecto, podemos ejecutar el siguiente comando en su terminal.

python bee-script.py

Utilice esta entrada de usuario de muestra:

  • Introduzca la empresa cliente: empresa A
  • Introduzca el nombre de la empresa contratista: empresa B
  • Introduzca el nombre de archivo del informe presupuestario de la empresa A (en el mismo nivel de directorio): budget_data.csv
  • Introduzca el nombre de archivo del contrato entre la empresa A y la empresa B (en el mismo nivel de directorio): contract.txt
  • Introduzca el sector del servicio descrito en este contrato (por ejemplo, finanzas, construcción u otros): paisajismo

El siguiente texto muestra un ejemplo de resultado que obtuvimos al ejecutar este flujo de trabajo multiagente.

Resultado:


-> El paso "Asesor presupuestario" se ha completado con el siguiente resultado.

El presupuesto de la empresa A para el periodo muestra una variación total de -12 700 USD. Las mayores variaciones se encuentran en los salarios de los empleados (-5000 USD), la publicidad en línea (-3000 USD), la publicidad impresa (-2000 USD) y el mantenimiento y las reparaciones (-1000 USD). También hay variaciones menores en alquiler, electricidad, agua, paisajismo y servicios de limpieza. -> El paso "Sintetizador de contratos" se ha completado con el siguiente resultado.

El contrato entre la empresa A y la empresa B es para servicios de paisajismo en la propiedad del cliente en Delaware. El pago total que deberá realizar la empresa A es de 5500 USD una vez finalizado el trabajo. Ambas partes han acordado cumplir con las leyes y regulaciones aplicables en Delaware.

-> El paso "Búsqueda web" se ha completado con el siguiente resultado.

Asunto: Propuesta de negociación para servicios de paisajismo

Estimado equipo de la empresa B:

Espero que se encuentren bien.

Tras revisar detenidamente nuestros datos presupuestarios internos y las tendencias del mercado en el sector del paisajismo, hemos identificado áreas en las que creemos que se pueden realizar ajustes para adaptarnos mejor a nuestras limitaciones financieras, sin dejar de mantener unos estándares de servicio de alta calidad.

  1. Alcance del trabajo: proponemos una reducción en el alcance del trabajo, centrándonos en los servicios esenciales que afectan directamente al atractivo exterior y al valor de la propiedad. Esto puede incluir la poda de árboles, la poda de arbustos y el mantenimiento del césped, con coloridas plantaciones ocasionales para mejorar el interés visual.

  2. Condiciones de pago: a la luz de las tendencias actuales del mercado, que indican una ligera disminución de los costes de paisajismo debido al aumento de la competencia, les rogamos que reconsideren el importe total del pago. Proponemos un pago total revisado de 4800 USD al finalizar el trabajo, lo que supone una reducción del 12 %.

  3. Plazo: para optimizar la asignación de recursos y minimizar las interrupciones en nuestras operaciones, sugerimos ampliar el plazo del proyecto en dos semanas. Este ajuste nos permitirá gestionar mejor nuestras restricciones presupuestarias internas sin comprometer la calidad del servicio.

Creemos que estos ajustes permitirán a ambas partes alcanzar un resultado beneficioso para ambas, a la vez que se cumplen las leyes y normativas aplicables en Delaware. Agradecemos su comprensión y estamos abiertos a seguir negociando para llegar a un acuerdo que se ajuste a las tendencias actuales del mercado y a nuestras restricciones presupuestarias internas.

Gracias por su atención a este asunto. Rogamos nos comuniquen si estos ajustes propuestos son aceptables o si tienen alguna contrapropuesta.

Atentamente,

[Su nombre]

Empresa A

-> El paso "Asesor de compras" se ha completado con el siguiente resultado.

Se ha enviado la respuesta definitiva a la empresa B, en la que se propone un pago total revisado de 4800 USD al finalizar el trabajo, lo que supone una reducción del 12 %. La propuesta también incluye una reducción del alcance del trabajo y una ampliación del plazo del proyecto.

Correo electrónico final: se ha enviado la respuesta definitiva a la empresa B, en la que se propone un pago total revisado de 4800 USD una vez finalizado el trabajo, lo que supone una reducción del 12 %. La propuesta también incluye una reducción del alcance del trabajo y una ampliación del plazo del proyecto.


 

Evidentemente, los agentes han invocado de forma correcta las herramientas disponibles para leer y sintetizar los datos del contrato y del presupuesto, para luego redactar un correo electrónico eficaz en el que se negocian los términos del contrato a favor del cliente. Podemos ver el resultado de cada agente dentro del flujo de trabajo y la importancia del papel de cada uno de ellos. En el correo electrónico se destacan detalles clave como el alcance de los trabajos de paisajismo, las condiciones de pago y el plazo del contrato. También podemos ver que la negociación aprovecha las tendencias del mercado en materia de paisajismo en beneficio del cliente. Por último, el pago total revisado de 4800 USD propuesto en el correo electrónico se ajusta al presupuesto de paisajismo del cliente, que es de 5200 USD. ¡Esto tiene muy buena pinta!

Resumen

Con este tutorial, ha creado varios agentes BeeAI, cada uno con herramientas personalizadas. Cada agente ha desempeñado un papel crítico en el caso de uso del sistema de gestión de contratos. Algunos de los próximos pasos pueden incluir explorar los distintos repositorios GitHub disponibles en la organización i-am-bee GitHub y crear más herramientas personalizadas. En los repositorios, también encontrará cuadernos Python para principiantes que le ayudarán a comprender mejor los componentes básicos de BeeAI, como PromptTemplates , Mensajes , Memoria y Emitter , para la observabilidad. 

Soluciones relacionadas
Desarrollo de agentes de IA de IBM 

Permita a los desarrolladores crear, implementar y monitorizar agentes de IA con el estudio IBM watsonx.ai.

Explore watsonx.ai
Agentes y asistentes de IA de IBM

Cree una productividad sin precedentes con uno de los conjuntos de capacidades más completos del sector para ayudar a las empresas a crear, personalizar y gestionar agentes y asistentes de IA. 

Explore los agentes de IA
IBM Granite

Ahorre más de un 90 % en costes con los modelos más pequeños y abiertos de Granite, diseñados para la eficiencia de los desarrolladores. Estos modelos listos para uso empresarial ofrecen un rendimiento excepcional frente a los puntos de referencia de seguridad y en una amplia gama de tareas empresariales, desde la ciberseguridad hasta RAG.

Explore Granite
Dé el siguiente paso

Automatice sus flujos de trabajo complejos y cree una productividad revolucionaria con uno de los conjuntos de capacidades más completos del sector para ayudar a las empresas a crear, personalizar y gestionar agentes de IA y asistentes. 

Explore el desarrollo de agentes de watsonx.ai Descubra watsonx Orchestrate