BeeAIとGraniteでマルチエージェント契約管理システムを構築

著者

Anna Gutowska

AI Engineer, Developer Advocate

IBM

このチュートリアルでは、PythonでBeeAIを使用して、IBM® Graniteで完全にローカルなマルチ・エージェント・システムを構築します。これらのエージェントは、市場の傾向と内部予算の制約を考慮しながら、2社間で造園サービスの契約協定を交渉するために連携します。ワークフローは、予算アドバイザー・エージェント、契約合成エージェント、Web検索エージェント、調達アドバイザー・エージェントで構成されます。ユーザーが提供した契約、予算データ、サービスの業種・業務、会社名を考慮して、エージェントは連携してクライアントに有利な契約条件を交渉するためのEメールを作成します。

BeeAIとは

IBM Researchによってリリースされ、現在はLinux® Foundationに寄贈されているBeeAIは、開発者があらゆるフレームワークからAIエージェントを構築できるようにするオープンソースのエージェント型AIプラットフォームです。1

人工知能(AI)エージェントとは、大規模言語モデル(LLM)を使用して構築され、ワークフローを設計し、利用可能なツールを活用することで、ユーザーまたは別のシステムに代わってタスクを自律的に実行するシステムまたはプログラムを指します。AIエージェントは、事前定義されたツールにアクセスし、将来のアクションを計画し、複雑な問題を解決して自動化するために人間の介入をほとんどまたはまったく必要としないため、従来のLLMチャットボットよりも高度です。

BeeAIの前身は、単一のLLMエージェントの構築に特化したオープンソース・フレームワークであるBee Agent Frameworkです。逆に、BeeAIは、マルチエージェント・ワークフローを構築および調整するためのより高度なエコシステムを提供します。

BeeAIには、以下の特徴があります。

  • フレームワークに依存しません。
  • TypeScriptとPythonの両方で利用できます。
  • IBM Researchが設計したAgent Communication Protocol(ACP)を基盤として構築されており、エージェント間の通信方法を標準化することで、Model Context Protocol(MCP)をさらに進化させています。ACPは、LangGraphcrewAI、BeeAIなどのさまざまなフレームワークのエージェントを1つの一貫したランタイムに統合します。2
  • エージェントの動作をトレースするためのオープンソース・ツールであるArize Phoenixと統合され、可観測性が実装されます。利用可能なロギングとテレメトリを使用してエージェントをデバッグする方法の詳細については、公式ドキュメントを参照してください。
  • お客様のマシン上で、完全にローカルに実行できます。エージェントは、共有環境にデプロイすることもできます。
  • チャット、埋め込み、JSONなどの構造化された出力を含むさまざまな機能に対して統一されたインターフェースを提供し、既存のコードを変更することなくシームレスなモデルの交換を可能にします。

ステップ

このステップバイステップ・ガイドは、Jupyter Notebookの形式でGitHubリポジトリーから取得できます。

ステップ1. 環境を設定する

まず、いくつかの前提条件を満たして環境をセットアップする必要があります。
1. このチュートリアルでは、 IBM® watsonx.aiやOpenAIで利用できるようなアプリケーション・プログラミング・インターフェース(API)は使用しません。その代わりに、Ollamaの最新バージョンをインストールして、ローカルでモデルを実行できます。
macOS、Linux、Windows用のOllamaをインストールする最も簡単な方法は、次のWebページで確認できます:https://ollama.com/downloadこのステップでは、メニュー・バー・アプリをインストールして、バックグラウンドでOllamaサーバーを実行し、最新のリリース情報を常に把握できるようにします。
別の方法として、OllamaをHomebrewで端末にインストールすることもできます。

brew install ollama

Homebrewからインストールする場合、またはソースから構築する場合は、中央サーバーを起動する必要があります。

ollama serve

2. Metaの最新のLlamaモデルやMistral AIのMistralモデルなど、ツール呼び出しをサポートするLLMがいくつかあります。このチュートリアルでは、IBMのオープンソースであるGranite 3.3モデルを使用します。このモデルは、強化された推論と指示に従う機能を備えています。3端末で次のコマンドを実行して、最新のGranite 3.3モデルを取得します。

ollama pull granite3.3:8b

3. パッケージの依存関係の競合を回避するために、仮想環境を設定します。Pythonバージョン3.11.9で仮想環境を作成するには、ターミナルで次のコマンドを実行します。

python3.12 -m venv .venv

次に、環境をアクティブ化するために、次のコマンドを実行します。

source .venv/bin/activate

4. requirements.txt ファイルには次のパッケージが含まれている必要があります。これらのパッケージは、BeeAIフレームワークを使用してエージェントを構築し、ツールの初期化に必要なLangChainクラスを組み込むために必要です。

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

これらのパッケージをインストールするには、ターミナルで次のコマンドを実行します。

pip install -r requirements.txt

5. 次のタイトルが付いた新しいPythonファイルを作成します。 bee-script.py これには、ターミナルで次のコマンドを実行します:

touch bee-script.py

新しいPythonファイルの上部には、必要なライブラリとモジュールのインポート・ステートメントを含めます。

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

ステップ2. エージェントとワークフローをインスタンス化する

asyncio を使う非同期メインメソッドでは、 ChatModelとAgentWorkflowクラスを組み込んで、Granite LLMとワークフローをインスタンス化してみましょう。OllamaモデルIDとワークフロー名を指定します。このワークフローをインスタンス化すると、エージェントを追加し、マルチ・エージェント・システムを作成できます。このmainメソッドをPythonファイルに追加します。

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

エージェントのワークフローの視覚化については、次の図を参照してください。

契約管理のためのエージェント・ワークフロー 契約管理のためのエージェント・ワークフロー

次のステップで、このワークフローの各コンポーネントを組み立てていきます。

ステップ3. ユーザーにインプットを促す

私たちのワークフローは、ユーザーのインプットに依存しています。最初に必要なインプットは、クライアントと請負業者の会社名です。後のステップでワークフローを実行すると、ユーザーは次のテキストでプロンプトを受け取ります。mainメソッドに次のコードを追加します。

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

また、クライアント企業の予算報告書を含むファイルbudget-data.csv の名前と、両社間の契約が含まれるファイルcontract.txt の名前が必要です。この例では、契約は、請負業者がクライアント企業に提供する造園サービスに関係しています。サンプル・ファイルはGitHubリポジトリーで取得できます。プロジェクトの構造は次のようになります:

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

次のコードでは、ファイルの拡張子も検証して、予想される形式と一致していることを確認します。いずれかのファイルが間違ったファイル・タイプである場合、ユーザーには再試行するようプロンプトされます。

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

最後に必要なユーザー・インプットは、契約に記載されているサービスの業種・業務です。このインプットには、金融、建設などが該当します。

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

ステップ4.予算ツールを設定する

マルチ・エージェント・システムで構築できる最初のツールは、予算アドバイザー・エージェント用です。このエージェントは、クライアントの予算データを読み取る役割を担います。エージェントには次の関数が渡されます。get_budget_data これにより、予算データを含むCSVファイルが読み込まれ、ファイルが存在しない場合や予期しないエラーが発生した場合には、エラー・メッセージが返されます。ユーザーから提供されたファイル名を使用してファイルにアクセスするには、まず現在のディレクトリーを取得する必要があります。これは、以下のメソッドを使用して行うことがOS できます。

current_directory = os.getcwd()

では、エージェントのエンジンである関数を設定してみましょう。 get_budget_data 関数は、現在のディレクトリとユーザー・インプットを使用してファイルにアクセスして読み取ります。

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

このツールを適切に使用するために、LangChainの StructuredTool クラスを使いましょう。ここでは、関数、ツール名、ツールの説明が提供され、 return_direct パラメータをtrueまたはfalseに設定します。この最後のパラメーターは、ツール・アウトプットを直接返すか、それとも合成するかをエージェントに通知します。

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

BeeAIのLangChainアダプターであるLangChainToolを使用すると、最初のツールの初期化を完了できます。

budget_tool = LangChainTool[Any](get_budget)

ステップ5. 契約ツールを設定する

次に構築できるツールは、コントラクト・シンジサイザー・エージェント用です。このエージェントは、クライアントと請負業者間の契約書を読み取る役割を担います。エージェントには次の関数が渡されます。 get_contract_data この関数では、契約書を含むテキスト・ファイルが読み込まれ、ファイルが存在しない場合や予期しないエラーが発生した場合にはエラー・メッセージが返されます。このステップに必要なコードは、ステップ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)

ステップ6. エージェントのワークフローの確立

このステップでは、さまざまなエージェントをワークフローに追加できます。予算アドバイザーと契約合成エージェントに対応するカスタム・ツールを提供しましょう。また、エージェントの名前、役割、指示、ツールのリスト、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,
)

関連業種の市場動向をウェブで検索するために、事前構築されたLangChainの DuckDuckGoSearchTool にアクセスするエージェントを作成できます。このツールは、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,
)

マルチ・エージェント・システムの4番目であり最後のエージェントは、調達アドバイザーです。このエージェントは、他のエージェントによって取得および合成された情報を使用して、クライアントに利益をもたらす請負業者企業に対して説得力のあるEメールを作成する役割を担います。このEメールでは、市場の動向とクライアントの内部予算の制約を考慮して、契約条件を交渉する必要があります。このエージェントは外部ツールを必要としませんが、むしろその指示により機能します。

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

ステップ7. エージェントのワークフローの実行

これまでのすべてのコードを使用して、mainメソッドを完成させることができました。mainメソッドの最後に、エージェントによるワークフローの実行を含めることができます。await のキーワードにより、各タスクの実行を待つだけでなく、タスクの同時実行も効率的に管理できます。Pythonファイルには次のコードが含まれている必要があります。

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

サンプルの契約書と予算データ、および最終的なスクリプトについては、 GitHubリポジトリーをご覧ください。プロジェクトを実行するには、ターミナルで次のコマンドを実行します。

python bee-script.py

次の サンプルユーザー・インプットを使用します:

  • クライアント企業を入力してください:A社
  • 請負業者の会社名を入力してください:B社
  • (同じディレクトリレベルにある)A社の予算レポートのファイル名を入力します:budget_data.csv
  • (同じディレクトリレベルにある)A社とB社間の契約書のファイル名を入力します:contract.txt
  • この契約書に記載されているサービスの業種・業務(例:金融、建設など)を入力:造園

次のテキストは、このマルチ・エージェント・ワークフローを実行したことを受け取るアウトプットの例を示しています。

アウトプット:


-> ステップ「予算アドバイザー」は、以下の結果で完了しました。

A社のこの期間における予算は、合計差異が-12,700米ドルであることを示しています。上位の差異は、従業員の給与(-5000米ドル)、オンライン広告(-3000米ドル)、印刷広告(-2000米ドル)、および保守と修理(-1000米ドル)です。また、家賃、電気、水道、造園、清掃サービスなどにも比較的小さい差異があります。-> ステップ「契約シンセサイザー」は、以下の結果で完了しました。

A社とB社の間の契約は、デラウェア州にある顧客の施設にある造園サービスに関する契約です。作業完了時にA社が支払う総額は5,500米ドルです。両当事者は、デラウェア州において適用される法規制を遵守することに同意済みです。

-> ステップ「Web検索」が次の結果で完了しました。

件名:造園サービスの交渉提案

B社ご担当チーム 御中、

いつも大変お世話になっております。

社内予算データと造園業界の市場動向を慎重に検討した結果、高品質のサービス基準を維持しながら、弊社の財務的制約に合わせて調整できる領域を特定しました。

  1. 業務範囲:カーブアピールや資産価値に直接影響する必要不可欠な業務に重点を置くことによる、業務範囲の縮小を提案いたします。これには、植木や植え込みの剪定および芝生の管理、視覚的な興味を引くために時折行われるカラフルな植え付けも含まれます。

  2. 支払条件:競争の激化により造園コストが若干低下している現在の市場動向を鑑み、支払い総額の再考をご検討願います。作業完了後には、12%の削減を反映して、変更後の支払い総額として4,800米ドルを提案いたします。

  3. タイムライン:リソースの割り当てを最適化し、業務の中断を最小限に抑えるために、プロジェクトのスケジュールを2週間延長することをご提案します。この調整により、サービスの品質を損なうことなく、内部予算の制約をより適切に管理できるようになります。

これらの調整により、デラウェア州で適用される法律や規制を遵守しながら、両当事者にとって相互に利益をもたらす結果を達成できると考えています。ご理解に感謝し、現在の市場動向と社内予算の制約に沿った合意を得るために、今後も議論を進めていければと思います。

本件についてご検討いただき、ありがとうございます。これらの調整案の承諾または対案についてご連絡お待ちしております。

よろしくお願いいたします。

[あなたの名前]

A社

-> ステップ「調達アドバイザー」は、以下の結果で完了しました。

最終回答はB社に送られ、作業完了時、12%の削減を反映した4,800米ドルへの合計支払い金額の変更を提案しています。この提案には、作業範囲の縮小とプロジェクトのスケジュールの延長も含まれています。

最後のEメール: 最終回答はB社に送られ、作業完了時に12%の削減を反映した4,800米ドルへの合計支払い金額の修正が提案されました。この提案には、作業範囲の縮小とプロジェクトのスケジュールの延長も含まれています。


 

明らかに、エージェントは利用可能なツールを正しく呼び出し、契約書と予算データを読み取って合成し、契約条件がクライアント優先で交渉される効果的なEメールを作成しました。ワークフロー内の各エージェントのアウトプットと各エージェントの役割の重要性がわかります。造園作業の範囲、支払い条件、契約スケジュールなどの重要な詳細は、Eメールに記載されています。また、交渉ではクライアントのメリットとなるように、造園業の市場動向を利用していることもわかります。最後に、Eメールで提案されている、変更後の合計支払い額4,800米ドルは、クライアントの造園予算5,200米ドルの範囲内に収まります。素晴らしいですね!

概要

このチュートリアルでは、それぞれにカスタム・ツールを使用して複数のBeeAIエージェントを構築しました。各エージェントは、契約管理システムのユースケースにおいて重要な役割を果たしました。次のステップとしては、i-am-bee GitHub組織で利用可能なさまざまなGitHubリポジトリを調べたり、さらにカスタム・ツールを構築したりすることが挙げられます。リポジトリーには、BeeAIのコア・コンポーネントをより深く理解するためのスターターPythonノートブックも用意されており、 PromptTemplatesメッセージメモリー および Emitter などがオブザーバビリティーのために利用できます。

関連ソリューション
IBMのAIエージェント開発

開発者が、IBM watsonx.aiを使用してAIエージェントの構築、デプロイ、および監視を行えるようにします。

watsonx.aiの詳細はこちら
IBMのAIエージェントとアシスタント

業界で最も包括的な機能セットの1つを使用して、企業がAIエージェントとアシスタントを構築、カスタマイズ、管理できるようにすることで、生産性を飛躍的に向上させます。

AIエージェントの詳細はこちら
IBM Granite

開発者の効率性を考慮したGraniteの小型オープンモデルで、コストを90%以上削減します。エンタープライズ対応モデルは、安全性ベンチマークに対して、さらにサイバーセキュリティーからRAGまでの幅広い企業タスクに対して優れたパフォーマンスを発揮します。

Graniteの詳細はこちら
次のステップ

包括性で業界でも屈指の機能セットを使用して、複雑なワークフローを自動化し、生産性を飛躍的に向上させましょう。企業がAIエージェントとアシスタントを構築、カスタマイズ、管理するのに役立つ機能セットです。

watsonx.aiエージェントの開発の詳細はこちら watsonx Orchestrateの詳細はこちら