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 adquisiciones. Dado el contrato, los datos presupuestarios, la industria 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 Linux Foundation, es una plataforma de código abierto de IA agéntica que brinda a los desarrolladores la capacidad de crear agentes de IA desde cualquier marco.1

Un agente de inteligencia artificial (IA) se refiere a un sistema o programa creado mediante un Modelo de lenguaje grande (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 y planificar acciones futuras, además de que 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 proporciona un ecosistema más avanzado para crear y orquestar flujos de trabajo de múltiples agentes.

BeeAI es:

  • Independiente del marco.
  • Disponible en TypeScript y Python.
  • Construido sobre el Protocolo de Comunicación de Agentes (ACP) diseñado por IBM Research, que lleva el Protocolo de Contexto del Modelo (MCP) un paso más allá al estandarizar la forma en que los agente se comunican entre sí. ACP reúne agentes de varios marcos, como LangGraphCrewAI y BeeAI, en un tiempo de ejecución constante.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 sus agentes a través del registro y la telemetría disponibles en la documentación oficial.
  • Es capaz de ejecutarse completamente de manera local en su máquina. Los agentes también se pueden desplegar en entornos compartidos.
  • Proporciona interfaces unificadas para diversas funcionalidades, como chat, incorporación y resultados, como JSON, lo que permite un intercambio de modelos perfecto sin necesidad de realizar ningún cambio 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: Configure el entorno

Primero debemos 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.ai y OpenAI. En su lugar, podemos instalar la última versión de Ollama para ejecutar el modelo localmente.
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 de barra de menú para ejecutar el servidor Ollama en segundo plano y mantenerlo actualizado con las últimas versiones.
Como alternativa, puede instalar Ollama con homebrew en su terminal:

brew install ollama

Si instala desde brew o compila desde la fuente, debe iniciar el servidor central:

ollama serve

2. Hay varios LLM que admiten llamadas a herramientas, como los últimos modelos Llama de Meta y los modelos Mistral de Mistral IA. Para este tutorial, utilizaremos el modelo Granite 3.3 de código abierto de IBM. Este modelo presenta capacidades mejoradas de razonamiento y seguimiento de instrucciones.3 Obtenga el último modelo de Granite 3.3 ejecutando 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

Luego, 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 la infraestructura 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 de Python titulado bee-script.py ejecutando este comando en su terminal:

touch bee-script.py

En la parte superior del archivo Python nuevo, 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. Cree una instancia de su agente y flujo de trabajo

En un método principal asíncrono usando asyncio , incorporemos las clases ChatModel y AgentWorkflow para crear instancias de nuestro LLM y flujo de trabajo Granite. Simplemente podemos proporcionar el ID del modelo de Ollama, así como un nombre de flujo de trabajo. La creación de instancias de este flujo de trabajo nos permite agregar agentes y crear nuestro sistema multiagente. Agregue 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 obtener una imagen del flujo de trabajo de los agentes, consulte el siguiente diagrama.

Flujo de trabajo de agentes para la gestión de contratos Flujo de trabajo de agentes para la gestión de contratos

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

Paso 3. Solicite al usuario que ingrese una entrada

Nuestro flujo de trabajo se basa en la entrada del usuario. Las entradas iniciales requeridas son los nombres de las empresas cliente y contratista. Se le solicitará al usuario el siguiente texto tras la ejecución del flujo de trabajo en un paso posterior. Agregue 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 expediente que contiene el contrato entre las dos empresas, contract.txt . En nuestro ejemplo, el contrato se refiere a los servicios de paisajismo proporcionados por la empresa contratista a la empresa cliente. Puede encontrar los archivos de muestra en nuestro repositorio de GitHub. La estructura del proyecto debe 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 archivo para ayudar a garantizar que se alineen con nuestro formato anticipado. Si alguno de los archivos es del tipo incorrecto, se le dará una instrucción al usuario para que intente nuevamente.

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 del usuario requerida es la industria del servicio descrito en el contrato. Esta entrada puede ser financiera, de construcción u otras.

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

Paso 4. Configure la herramienta de presupuesto

La primera herramienta que podemos construir en nuestro sistema multiagente es para el agente asesor presupuestario. Este agente es responsable de leer los datos del presupuesto del cliente. La función proporcionada al agente esget_budget_data , en la que se lee el archivo CSV que contiene los datos del presupuesto 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 utilizando el nombre de archivo proporcionado por el usuario, primero debemos recuperar el directorio actual. Podemos hacerlo usando el siguientemétodo os.

current_directory = os.getcwd()

Ahora, vamos a configurar la fuerza motriz del agente, la get_budget_data función, que utiliza el directorio actual, así como la entrada del usuario para acceder al archivo y leerlo.

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 ayudar a 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 para que sea verdadero o falso. Este último parámetro simplemente informa al agente si debe devolver la salida de la herramienta directamente o sintetizarla.

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. Configure la herramienta de contratos

La siguiente herramienta que podemos crear es para el agente sintetizador por contrato. Este agente es responsable de leer el contrato entre el cliente y el contratista. La función proporcionada 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 requerido para este paso es similar al 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. Establezca el flujo de trabajo de los agentes

En este paso, podemos agregar los distintos agentes a nuestro flujo de trabajo. Proporcionemos al asesor presupuestario y a los agentes de síntesis contractual sus herramientas personalizadas correspondientes. También podemos establecer el nombre del agente, el rol, 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 la industria relevante, podemos crear un agente con acceso al LangChain prediseñado DuckDuckGoSearchTool . Esta herramienta obtiene datos de la web mediante 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 adquisiciones. Este agente es responsable de utilizar la información recuperada y sintetizada por los otros agentes para formular un correo electrónico convincente a la empresa contratista a favor del cliente. El correo electrónico debe considerar las tendencias del mercado y las limitaciones presupuestarias internas del cliente para negociar los términos del contrato. Este agente no requiere ninguna herramienta externa, sino que se rige 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. Ejecute el flujo de trabajo de los agentes

Ahora podemos finalizar nuestro método principal con todo nuestro código hasta ahora. Al final del método principal, podemos incluir la ejecución del flujo de trabajo agéntico. Dada lapalabra clave await, podemos gestionar de manera eficiente la ejecución simultánea de tareas, así como esperar la ejecución de cada tarea. Su archivo Python debe 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 de muestra y datos presupuestarios, así como el script final, consulte nuestro repositorio de GitHub. Para ejecutar el proyecto, podemos ejecutar el siguiente comando en nuestra terminal.

python bee-script.py

Emplee esta entrada de usuario de ejemplo:

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

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

Resultado:


-> El paso 'Budget Advisor' se completó 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 'Contract Synthesizer' se completó con el siguiente resultado.

El contrato entre la empresa A y la empresa B es para servicios de jardinería en la propiedad del cliente en Delaware. El pago total que debe realizar la empresa A es de 5500 USD al finalizar el trabajo. Ambas partes acordaron cumplir con las leyes y regulaciones aplicables en Delaware.

-> El paso 'Web Search' se completó con el siguiente resultado.

Asunto: Propuesta de negociación para servicios de paisajismo

Estimado equipo de la empresa B:

Espero que se encuentren bien.

Después de una cuidadosa revisión de nuestros datos presupuestarios internos y las tendencias del mercado en la industria del paisajismo, hemos identificado áreas en las que creemos que se pueden hacer ajustes para alinearnos mejor con nuestras limitaciones financieras sin dejar de mantener 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 el atractivo exterior y el valor de la propiedad. Esto puede incluir podar árboles y arbustos y mantener el césped, con plantas coloridas 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 en los costos de paisajismo debido al aumento de la competencia, solicitamos amablemente una reconsideración del monto total del pago. Proponemos un pago total revisado de 4800 USD al finalizar el trabajo, lo que refleja una reducción del 12 %.

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

Creemos que estos ajustes permitirán a ambas partes lograr un resultado mutuamente beneficioso mientras se adhieren a las leyes y regulaciones aplicables en Delaware. Agradecemos su comprensión y estamos abiertos a más conversaciones para llegar a un acuerdo que se alinee con las tendencias actuales del mercado y nuestras limitaciones presupuestarias internas.

Gracias por su atención a este asunto. Háganos saber si estos ajustes propuestos son aceptables o si tiene alguna contrapropuesta.

Atentamente,

[Su nombre]

Empresa A

-> El paso 'Procurement Advisor' se completó con el siguiente resultado.

La respuesta final se envió a la empresa B, proponiendo un pago total revisado de 4800 USD al finalizar el trabajo, lo que refleja una reducción del 12 %. La propuesta también incluye un alcance de trabajo reducido y un cronograma extendido del proyecto.

Correo electrónico final: la respuesta final se envió a la empresa B, proponiendo un pago total revisado de 4800 USD al finalizar el trabajo, lo que refleja una reducción del 12 %. La propuesta también incluye un alcance de trabajo reducido y un cronograma extendido del proyecto.


 

Evidentemente, los agentes invocaron correctamente sus herramientas disponibles para leer y sintetizar los datos del contrato y el presupuesto para luego formular un correo electrónico efectivo 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 rol de cada agente. Los detalles clave, como el alcance del trabajo de paisajismo, las condiciones de pago y el cronograma del contrato, se destacan en el correo electrónico. También podemos ver que la negociación utiliza las tendencias del mercado en paisajismo en beneficio del cliente. Por último, el pago total revisado de 4800 USD propuesto en el correo electrónico se encuentra dentro del presupuesto de paisajismo del cliente de 5200 USD. ¡Esto parece excelente!

Resumen

Con este tutorial, creó varios agentes BeeAI, cada uno con herramientas personalizadas. Cada agente desempeñó un papel crítico en el caso de uso del sistema de gestión de contratos. Algunos siguientes pasos pueden incluir explorar los diversos repositorios de GitHub disponibles en la organización i-am-bee GitHub y crear más herramientas personalizadas. En los repositorios, también encontrará notebooks iniciales de Python para comprender mejor los componentes principales 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, desplegar y monitorear agentes de IA con el estudio IBM watsonx.ai.

Explore watsonx.ai
Agentes de IA y asistentes de IBM

Cree una productividad revolucionaria con uno de los conjuntos de capacidades más completos de la industria para ayudar a las empresas a crear, personalizar y gestionar asistentes y agentes de IA. 

Explore los agentes de IA
IBM Granite

Logre un ahorro de más del 90 % en costos de energía con los modelos más pequeños y abiertos de Granite, diseñados para mejorar la eficiencia de los desarrolladores. Estos modelos preparados para empresas ofrecen un rendimiento excepcional contra puntos de referencia de seguridad y en una amplia gama de tareas empresariales, desde la ciberseguridad hasta el RAG.

Explorar Granite
Dé el siguiente paso

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

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