Crea un sistema di gestione dei contratti multiagente con BeeAI e Granite

Autore

Anna Gutowska

AI Engineer, Developer Advocate

IBM

In questo tutorial, creerai un sistema multi-agente completamente locale con IBM® Granite utilizzando BeeAI in Python. Questi agenti collaboreranno per negoziare un accordo contrattuale per i servizi paesaggistici tra due società tenendo conto delle tendenze del mercato e dei vincoli di bilancio interni. Il workflow sarà composto da un agente consulente per il budget, un agente per la sintesi dei contratti, un agente per la ricerca web e un agente consulente per l'approvvigionamento. Dati il contratto, i dati sul bilancio, il settore dei servizi e i nomi delle società forniti dall'utente, gli agenti collaborano per produrre un'e-mail per negoziare i termini del contratto a favore del cliente.

Che cos'è BeeAI?

BeeAI, rilasciata da IBM Research e ora donata alla Linux Foundation, è una piattaforma di  agentic AI open source che offre agli sviluppatori la possibilità di creare agenti AI da qualsiasi framework.1

Un agente di intelligenza artificiale (AI) si riferisce a un sistema o programma sviluppato con l'utilizzo di un modello linguistico di grandi dimensioni (LLM) per eseguire autonomamente attività per conto di un utente o di un altro sistema progettandone il workflow e utilizzando gli strumenti disponibili. Gli agenti AI sono più avanzati dei tradizionali chatbot LLM, in quanto possono accedere a strumenti predefiniti, pianificare azioni future e richiedono poco o nessun intervento umano per risolvere e automatizzare problemi complessi.

Il predecessore di BeeAI è il Bee Agent Framework, un framework open source specifico per la creazione di agenti LLM singoli. Dal suo canto, BeeAI fornisce un ecosistema più avanzato per costruire e orchestrare workflow multiagente.

BeeAI è:

  • Indipendente dal framework.
  • Disponibile sia in TypeScript che in Python.
  • Basato sul protocollo di comunicazione degli agenti (ACP) progettato da IBM Research, che porta il protocollo di contesto del modello (MCP) un ulteriore passo avanti standardizzando il modo in cui gli agenti comunicano tra loro. Il protocollo ACP riunisce agenti provenienti da vari framework come LangGraphcrewAI e BeeAI in un tempo di esecuzione costante.2
  • È integrato con Arize Phoenix, uno strumento open source per tracciare il comportamento degli agenti, consentendo l'observability. Ulteriori informazioni sul debug degli agenti tramite la registrazione e la telemetria sono disponibili nella documentazione ufficiale.
  • In grado di funzionare interamente localmente sulla tua macchina. Gli agenti possono essere distribuiti anche in ambienti condivisi.
  • Fornisce interfacce unificate per varie funzionalità, tra cui chat, embedding e output, come JSON, che consentono un passaggio tra modelli senza interruzioni e senza richiedere modifiche al codice esistente.

Passaggi

Questa guida dettagliata è disponibile nel nostro repository GitHub sotto forma di Jupyter Notebook.

Fase 1. Configura il tuo ambiente

Per prima cosa, è necessario configurare il nostro ambiente soddisfacendo alcuni prerequisiti.
1. In questo tutorial, non utilizzeremo un'application programming interface (API) come quelle disponibili tramite IBM watsonx.ai e OpenAI. Invece, installeremo l'ultima versione di Ollama per eseguire il modello localmente.
Il modo più semplice per installare Ollama per macOS, Linux e Windows è tramite la pagina web: https://ollama.com/download. Questo passaggio permetterà l'installazione di un'app per eseguire il server Ollama in background e l'aggiornamento delle ultime versioni.
In alternativa, puoi installare Ollama con homebrew nel tuo terminale:

brew install ollama

Se l'installazione è effettuata da brew o la compilazione da sorgenti, è necessario avviare il server centrale:

ollama serve

2. Esistono diversi LLM che supportano le chiamate tramite strumenti come gli ultimi modelli Llama di Meta e i modelli Mistral di Mistral AI. Per questo tutorial, utilizzeremo il modello open source Granite 3.3 di IBM. Questo modello presenta caratteristiche avanzate di ragionamento e funzionalità di esecuzione delle istruzioni.3 Scegli l'ultimo modello di Granite 3.3, eseguendo il seguente comando nel tuo terminale.

ollama pull granite3.3:8b

3. Al fine di evitare conflitti di dipendenza dei pacchetti, creiamo un ambiente virtuale. Per creare un ambiente virtuale con Python versione 3.11.9, esegui il seguente comando nel tuo terminale.

python3.12 -m venv .venv

Quindi, per attivare l'ambiente, esegui:

source .venv/bin/activate

4. Il file requirements.txt dovrebbe contenere i seguenti pacchetti. Questi pacchetti sono necessari per creare agenti con il framework BeeAI e incorporare le classi LangChain necessarie per l'inizializzazione degli strumenti.

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

Per installare questi pacchetti, esegui il seguente comando nel tuo terminale.

pip install -r requirements.txt

5. Crea un nuovo file Python intitolato bee-script.py tramite l'esecuzione di questo comando nel tuo terminale:

touch bee-script.py

Nella parte superiore del nuovo file Python, includi le istruzioni di importazione delle librerie e dei moduli necessari.

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

Fase 2. Crea istanze per l'agente e il workflow

In un metodo principale asincrono che utilizza asyncio, incorporiamo le classi ChatModel e AgentWorkflow per creare un'istanza di LLM e workflow Granite. Possiamo semplicemente fornire l'ID del modello Ollama e un nome del workflow. La creazione di un'istanza di questo workflow ci consente di aggiungere agenti e creare il nostro sistema multiagente. Aggiungi questo metodo principale al tuo file Python.

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

Per un'immagine del workflow agentico, fai riferimento al diagramma seguente.

Workflow agentico per la gestione dei contratti Workflow agentico per la gestione dei contratti

Assembleremo ogni componente di questo workflow nei passaggi seguenti.

Fase 3. Richiedi un input all'utente

Il workflow si basa sull'input dell'utente. Gli input iniziali richiesti sono i nomi sia del cliente che delle aziende appaltatrici. All'utente verrà dato il prompt del seguente testo al momento dell'esecuzione del workflow in un passaggio successivo. Aggiungi il codice seguente al metodo principale.

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

Avremo bisogno anche dei nomi dei file contenenti il report di bilancio dell'azienda cliente,budget-data.csv, nonché il file contenente il contratto tra le due società, contract.txt. Nel nostro esempio, il contratto riguarda i servizi paesaggistici forniti dalla società appaltatrice all'azienda cliente. Puoi trovare i file di esempio nel repository GitHub. La struttura del progetto dovrebbe essere simile alla seguente:

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

Nel codice seguente, verifichiamo anche le estensioni dei file per assicurarci che siano allineate al formato previsto. Se uno dei file è di tipo errato, all'utente verrà chiesto di riprovare.

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

L'ultimo input richiesto dall'utente è il settore del servizio descritto nel contratto. Questo input può essere finanziario, edilizio o altro.

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

Fase 4. Configura lo strumento di budget

Il primo strumento che possiamo creare nel nostro sistema multi-agente è per l'agente consulente di bilancio. Questo agente è responsabile della lettura dei dati del bilancio del cliente. La funzione fornita all'agente èget_budget_data, in cui viene letto il file CSV contenente i dati del bilancio e viene restituito un messaggio di errore nel caso in cui il file non esista o si verifichi un errore imprevisto. Per accedere al file utilizzando il nome del file fornito dall'utente, dobbiamo prima recuperare la directory corrente. Possiamo farlo utilizzando il seguenteos metodo.

current_directory = os.getcwd()

Ora, configuriamo la forza trainante dell'agente, get_budget_data, la funzione, che utilizza la directory corrente e l'input dell'utente per accedere e leggere il file.

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

Per contribuire a garantire l'uso appropriato di questo strumento, utilizziamo la classe StructuredTool di LangChain. Qui, forniamo la funzione, il nome dello strumento, la descrizione del tool e impostiamo il parametro return_direct in modo che sia vero o falso. Quest'ultimo parametro indica semplicemente all'agente se restituire direttamente l'output dello strumento o sintetizzarlo.

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

Utilizzando l'adattatore LangChain di BeeAI, LangChainTool, possiamo finalizzare l'inizializzazione del nostro primo strumento.

budget_tool = LangChainTool[Any](get_budget)

Fase 5. Configura lo strumento di contratto

Il prossimo strumento che possiamo creare è destinato all'agente sintetizzatore di contratti. Questo agente è responsabile della lettura del contratto tra il cliente e l'appaltatore. La funzione fornita all'agente è get_contract_data, in cui viene letto il file di testo contenente il contratto e viene restituito un messaggio di errore nel caso in cui il file non esista o si verifichi un errore imprevisto. Il codice richiesto per questo passaggio è simile al passaggio 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)

Fase 6. Stabilisci il workflow dell'agentic AI

In questo passaggio, possiamo aggiungere i vari agenti al nostro workflow. Forniamo al consulente per il bilancio e agli agenti sintetizzatori di contratti i loro strumenti personalizzati corrispondenti. Possiamo anche impostare il nome dell'agente, il ruolo, le istruzioni, l'elenco degli strumenti e l'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,
)

Per cercare sul web le tendenze del mercato nel settore in questione, possiamo creare un agente con accesso alla LangChain predefinita DuckDuckGoSearchTool. Questo strumento recupera dati dal web utilizzando il motore di ricerca 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,
)

Il quarto e ultimo agente del nostro sistema multiagente è il consulente per il procurement. Questo agente è responsabile dell'utilizzo delle informazioni recuperate e sintetizzate dagli altri agenti per formulare un'e-mail convincente per la società appaltatrice a favore del cliente. L'e-mail dovrebbe considerare le tendenze del mercato e i vincoli di bilancio interni del cliente per negoziare i termini del contratto. Questo agente non richiede strumenti esterni, ma è guidato dalle proprie istruzioni.

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,
)

Fase 7. Esegui il workflow dell'agentic AI

Ora possiamo finalizzare il nostro metodo principale con tutto il codice scritto finora.m A conclusione del metodo principale, possiamo includere l'esecuzione del workflow agentico. Datala parola chiave await, possiamo gestire in modo efficiente l'esecuzione simultanea delle attività e attendere l'esecuzione di ognuna di esse. Il tuo file Python dovrebbe contenere il codice seguente.

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

Per un esempio di dati sul contratto e sul bilancio, nonché per lo script finale, consulta il repository GitHub. Per avviare il progetto, possiamo eseguire il seguente comando nel terminale.

python bee-script.py

Usa questo input utente di esempio:

  • Inserisci l'azienda cliente: azienda A
  • Inserisci il nome dell'azienda appaltatrice: azienda B
  • Inserisci il nome del file del rapporto sul bilancio per l'azienda A (nello stesso livello di directory): budget_data.csv
  • Inserisci il nome del file del contratto tra l'azienda A e l'azienda B (nello stesso livello di directory): contract.txt
  • Inserisci il settore del servizio descritto in questo contratto (ad esempio, finanza, edilizia o altro): paesaggistica

Il testo seguente mostra un esempio di output che abbiamo ricevuto dall'esecuzione di questo workflow multiagente.

Output


-> La fase "Consulente di bilancio" è stata completata con il seguente risultato.

Il bilancio dell'azienda A per il periodo mostra una varianza totale di -12.700 USD. Le variazioni maggiori riguardano gli stipendi dei dipendenti (-5000 USD), la pubblicità online (-3000 USD), la pubblicità su carta stampata (-2000 USD) e la manutenzione e le riparazioni (-1000 USD). Ci sono anche variazioni minori riguardanti affitto, elettricità, acqua, paesaggistica e servizi di pulizia. -> La fase "Sintetizzatore di contratti" è stata completata con il seguente risultato.

Il contratto tra l'azienda A e B riguarda i servizi paesaggistici presso la proprietà del cliente nel Delaware. Il pagamento totale che l'azienda A dovrà effettuare è di 5.500 USD al termine dei lavori. Entrambe le parti hanno accettato di rispettare le leggi e i regolamenti applicabili nel Delaware.

-> La fase "Ricerca sul Web" è stata completata con il seguente risultato.

Oggetto: Proposta di negoziazione per servizi di paesaggistica

Gentile team dell'azienda B,

spero vada tutto bene.

Dopo un'attenta analisi dei nostri dati sul bilancio interno e delle tendenze del mercato nel settore del paesaggio, abbiamo identificato le aree in cui riteniamo sia possibile apportare modifiche per allinearci meglio ai nostri vincoli finanziari, pur mantenendo standard di servizio di alta qualità.

  1. Portata del lavoro: proponiamo una riduzione della portata del lavoro, concentrandoci sui servizi essenziali che hanno un impatto diretto sull'attrattiva e sul valore della proprietà. Ciò può includere la potatura degli alberi, degli arbusti e la manutenzione dei prati, con qualche piantumazione colorata per aumentare l'interesse visivo.

  2. Termini di pagamento: alla luce delle attuali tendenze del mercato, che indicano una leggera diminuzione dei costi paesaggistici dovuta all'aumento della concorrenza, chiediamo gentilmente di riconsiderare l'importo totale del pagamento. Proponiamo un pagamento totale rivisto di 4.800 USD al termine dei lavori, che riflette una riduzione del 12%.

  3. Tempistica: per ottimizzare l'allocazione delle risorse e ridurre al minimo le interruzioni delle nostre operazioni, suggeriamo di allungare i tempi del progetto di due settimane. Questo adeguamento ci consentirà di gestire meglio i nostri vincoli di bilancio interni senza compromettere la qualità del servizio.

Riteniamo che questi adeguamenti consentiranno a entrambe le parti di ottenere un risultato reciprocamente vantaggioso, nel rispetto delle leggi e dei regolamenti applicabili nel Delaware. Apprezziamo la vostra comprensione e siamo aperti a ulteriori discussioni per raggiungere un accordo in linea con le attuali tendenze del mercato e i nostri vincoli di bilancio interni.

Grazie per l'attenzione prestata alla questione. Vi preghiamo di farci sapere se le modifiche da noi proposte sono accettabili o se avete delle controproposte.

Cordiali saluti.

[Nome]

Azienda A

-> La fase "Consulente per gli approvvigionamenti" è stata completata con il seguente risultato.

La risposta finale è stata inviata alla società B, proponendo un pagamento totale rivisto di 4.800 USD al termine dei lavori, con una riduzione del 12%. La proposta include anche una riduzione della portata del lavoro e un'estensione della tempistica del progetto.

E-mail finale: la risposta finale è stata inviata all'azienda B, proponendo un pagamento totale rivisto di 4.800 USD al termine dei lavori, con una riduzione del 12%. La proposta include anche una riduzione della portata del lavoro e un'estensione della tempistica del progetto.


 

Com'è evidente, gli agenti hanno correttamente richiamato gli strumenti disponibili per leggere e sintetizzare i dati del contratto e del bilancio per poi formulare un'e-mail efficace in cui i termini del contratto sono negoziati a favore del cliente. Possiamo vedere l'output di ogni agente all'interno del workflow e l'importanza del ruolo di ognuno di loro. I dettagli chiave come l'ambito dei lavori paesaggistici, i termini di pagamento e la tempistica del contratto sono evidenziati nell'e-mail. Possiamo anche notare che la negoziazione sfrutta le tendenze del mercato nel settore paesaggistico a beneficio del cliente. Infine, il pagamento totale rivisto di 4.800 USD proposto nell'e-mail rientra nel bilancio paesaggistico del cliente di 5.200 USD. Sembra fantastico!

Riepilogo

Con questo tutorial, hai creato diversi agenti BeeAI, ognuno con strumenti personalizzati. Ogni agente ha svolto un ruolo critico nel caso d'uso del sistema di gestione dei contratti. Alcuni prossimi passaggi possono includere l'esplorazione dei vari repository GitHub disponibili nella organizzazione GitHub i-am-bee e la creazione di altri strumenti personalizzati. Nei repository troverai anche notebook Python di base per comprendere meglio i componenti principali di BeeAI, come ad esempio PromptTemplates , Messaggi , Memoria e Emitter, per l'observability 

Soluzioni correlate
Sviluppo di agenti AI IBM 

Consenti agli sviluppatori di creare, distribuire e monitorare agenti AI con lo studio IBM watsonx.ai.

Esplora watsonx.ai
Agenti e assistenti AI di IBM

Migliora la produttività con uno dei set di funzionalità più completi del settore che aiuta le aziende a creare, personalizzare e gestire agenti e assistenti AI. 

Scopri gli agenti AI
IBM Granite

Ottieni un risparmio sui costi di oltre il 90% con i modelli più piccoli e aperti di Granite, progettati per l'efficienza degli sviluppatori. Questi modelli pensati per le imprese offrono prestazioni eccellenti rispetto ai benchmark di sicurezza e in un'ampia gamma di attività aziendali, dalla cybersecurity alla RAG.

Esplora Granite
Prossimi passi

Automatizza i tuoi workflow complessi e migliora la produttività con uno dei set di funzionalità più completi del settore che aiuta le aziende a creare, personalizzare e gestire agenti e assistenti AI. 

Esplora lo sviluppo di agenti watsonx.ai Scopri watsonx Orchestrate