* JADE チュートリアル JADE プログラミング入門 ** JADE プログラミング入門 本チュートリアルでは、簡単な JADE エージェントを作成する方法と、エージェント同士が互いにコミュニケーションを取り合ってタスクを実行する方法について説明します。 JADE はすべて Java で書かれており、エージェントを開発する際に JADE プログラマは、すべて Java で作業します。 したがって、読者は Java プログラミング言語を熟知しているとします。 このチュートリアルは以下のように構成されています。 第1章では JADE の概要を説明し、Platform や Container、AMS、DF の概念を紹介します。 第2章では JADE で簡単なエージェントベースのアプリケーションを開発するために必要となるステップを例示するため、このチュートリアルを通して使用される簡単なサンプルを挙げます。 第3章ではエージェントを作成することに焦点を当て、Agent と AID クラスの基本的な特徴を説明します。 第4章では JADE エージェントでのタスクの実行方法を説明し、Behaviour クラスを紹介します。 第5章ではエージェントの通信方法を解説し、ACLMessage と MessageTemplate クラスを紹介します。 第6章では DFService クラスの DF エージェントによって提供されるイエローページサービスの利用方法を例示します。 JADE はこのチュートリアルで例示した基本的な特徴のほかに、複雑なインタラクションプロトコルのサポートやユーザーが定義したオントロジーの使用法などの拡張機能を提供します。 これらの機能については JADE のウェブサイトで入手できる JADE Programmer's ガイドと JADE Administrator's ガイドを参照してください。 ** 1 JADE 概要 JADE はマルチエージェントシステムの開発を容易にするミドルウェアです。これは次の機能を含みます。 - JADE エージェントが既定のホスト上で「生存する」ことができる''ランタイム環境''。「生存する」とは、1人以上のエージェントが既定のホスト上で実行することができるまで、アクティブでなければならないこと。 //- JADE エージェントは「生存する」ことができ、1人以上のエージェントが既定のホスト上で実行できるまで、そのホスト上でアクティブでなければならない''ランタイム環境''。 - プログラマがエージェントを開発するために (直接または限定して) 使用できる/使用しなければならない''クラスライブラリ''。 - 実行中のエージェントの活動を管理し、モニターすることを可能にする''グラフィックツール''のセット。 *** 1.1 コンテナとプラットフォーム JADE ランタイム環境でそれぞれの実行中のインスタンスは'''コンテナ'''と呼ばれ、数人のエージェントを含むことができます。 アクティブなコンテナのセットは、'''プラットフォーム'''と呼ばれています。 1つの特別な'''メーンコンテナ'''は、プラットフォーム上で常にアクティブでなければなりません。 それ以外のコンテナは、起動するとすぐにメーンコンテナに登録します。 要するにプラットフォーム上で最初に起動するコンテナは、メーンコンテナでなければならないのに対して、それ以外のコンテナは「ノーマル」(すなわち、メーンでない) コンテナでなければならないということになります。 そのため、どこ (ホストとポート) にメーンコンテナ (すなわち、登録するためのメーンコンテナ) があるのか示さなければなりません。 別のメーンコンテナがネットワーク上のどこかで起動したときは、新しいノーマルコンテナをできる限り登録できる異なるプラットフォームを構成します。 それぞれ3つと1つのコンテナから成る2つの JADE プラットフォームを表している図1のサンプルシナリオを通して上記の概念を例示します。 JADE エージェントはユニークな名前で識別されます。また、エージェントは互いの名前を知っているならば、現在位置に関係なく透過的にコミュニケーションすることができます: 同じコンテナ (例えば、図1のエージェント A2 と A3)、同じプラットフォーム上の異なるコンテナ (例えば、エージェント A1 と A2)、または異なるプラットフォーム上の異なるコンテナ (例えば、エージェント A4 と A5) JADE ランタイム環境がどのように動作するのかを知る必要はなく、エージェントを実行する前に JADE を起動すればいいのです。 JADE ランタイム環境においてメーンコンテナまたはノーマルコンテナでエージェントを実行するように JADE を起動する方法は、JADE のウェブサイトで入手できる ''JADE Administrative Tutorial'' で解説してあります。 *** 1.2 AMS と DF メーンコンテナはノーマルコンテナと異なり、別のコンテナから登録を受け付ける能力のほかに、(メーンコンテナが起動すると、自動的に起動する) 2人の特別なエージェントを持ちます。 AMS (Agent Management System) はネーミングサービスを提供しています (すなわち、プラットフォーム上の各エージェントはユニークな名前を持つことを保障します)。 また、プラットフォーム上で権限を表しています (例えば、AMS にリクエストすることでリモート・コンテナのエージェントを create/kill することが可能です)。 このチュートリアルでは AMS との相互作用する方法は例示しません。それは JADE プログラミング発展に含まれています。 DF (Directory Facilitator) はイエローページサービスを提供しています。 イエローページサービスとは、別のエージェントが提供している目的を達成するために必要なサービスを検索することができるエージェントのことです。 DF エージェントが提供しているイエローページサービスの使い方は、第6章で例示しています。 ** 2 "BOOK TRADING" サンプル 本章では、JADE でエージェントベースのアプリケーションを開発するために必要となるステップを例示するため、このチュートリアルを通して使用される簡単なサンプルを紹介します。 このサンプルは、ユーザーの代わりにあるエージェントは本を売り、別のエージェントは本を買う、ということを考慮したシナリオです。 買い手エージェントはそれぞれコマンドライン引数として買いたい本 (「目的の本」) のタイトルを受け取り、すべての売り手エージェントに対してオファーをするよう定期的にリクエストします。 売り手エージェントはオファーを受け取るとすぐに、そのオファーを受理して注文書を発行します。 2人以上の売り手エージェントがオファーをするならば、買い手エージェントは最高のもの (最低価格) を受理します。 目的の本を購入すると、買い手エージェントは終了します。 売り手エージェントはそれぞれ最小の GUI を持ちます。 最小の GUI は、ユーザーが新しいタイトル (と、それに関連する価格) を商品の本のローカルカタログに書き込むことができる機能を持ちます。 売り手エージェントは買い手エージェントからのリクエストを継続的に待機します。 売り手エージェントはある本にオファーをするよう頼まれると、リクエストされた本がカタログにあるかどうかを調べます。そして、ある場合はその価格を返信し、ない場合は拒否します。 売り手エージェントは注文書を受け取ると、リクエストされた本を出してカタログからそれを削除します。 電子決済に関連するすべての問題は、このチュートリアルの範囲外であり、考慮されていません。 このサンプルの完全なソースは、JADE が提供している examples.bookTrading パッケージのサンプルの中から入手できます。 ** 3 JADE エージェントの作成 - エージェントクラス JADE エージェントの作成は、下記のコードで示すように jade.core.Agent クラスを拡張してクラスを定義し、setup() メソッドを実装するだけで簡単です。 import jade.core.Agent; public class BookBuyerAgent extends Agent { protected void setup() { // Printout a welcome message System.out.println("Hallo! Buyer-agent " + getAID().getName() + " is ready."); } } setup() メソッドは、エージェントを初期化することを目的としています。 エージェントが行わなければならない実際のジョブは、通常「behaviours」の内部で実施します。 詳細は第4章で解説します。 *** 3.1 エージェント識別子 各エージェントは、jade.core.AID クラスのインスタンスとして表現された「エージェント識別子」によって識別されます。 Agent クラスの getAID() メソッドは、エージェント識別子の取得を可能にします。 AID オブジェクトは、グローバルにユニークな名前に加えていくつかのアドレスを含みます。 JADE の名前には、'''@''' 形式があります。 '''P1''' と呼ばれるプラットフォームに生存している '''Peter''' と呼ばれるエージェントが、グローバルにユニークな名前として '''Peter@P1''' を持つように。 AID に含まれるアドレスは、エージェントが生存しているプラットフォームのアドレスです。 これらのアドレスは、あるエージェントが異なるプラットフォームに生存している別のエージェントに対して、コミュニケーションする必要があるときにのみ使用されます。 開発者はこのチュートリアルの範囲外である特定のケースに限り、それらに注意する必要があります。 エージェントのニックネームを知っていると、そのエージェントの AID は以下のように取得できます: String nickname = "Peter"; AID id = new AID(nickname, AID.ISLOCALNAME); ISLOCALNAME 定数は、第1引数が (プラットフォームに位置する) ニックネームを表し、エージェントのグローバルにユニークな名前ではないことを示します。 *** 3.2 エージェントの実行 作成したエージェントは以下のようにコンパイルできます: javac -classpath BookBuyerAgent.java コンパイルされたエージェントを実行するには、JADE ランタイムを起動しなければなりません。 また、実行するエージェントのニックネームを選択しなければなりません: java -classpath ;. jade.Boot buyer:BookBuyerAgent エージェントをコンパイルして実行することに関する詳細は、JADE のウェブサイトで入手できる JADE Administrative チュートリアル、または JADE Administrator's ガイドに載っています。 入力したコマンドの実行結果は以下のようになります。 C:\jade> java -classpath jade.Boot buyer:BookBuyerAgent This is JADE snapshot - 2003/10/24 13:43:39 downloaded in Open Source, under LGPL restrictions, at http://jade.cselt.it/ IOR:000000000000001149444C3A464950412F4D54533A312E300000000000000001000000000000 0060000102000000000A3132372E302E302E310009A600000019AFABCB0000000002BCE5528F0000 000800000000000000000A0000000000000100000001000000200000000000010001000000020501 000100010020000101090000000100010100 Agent container Main-Container@JADE-IMTP://IBM8695 is ready. Hallo! Buyer-agent buyer@IBM8695:1099/JADE is ready. 上記の結果の最初の部分は、JADE ランタイムが起動するたびにプリントアウトされる JADE の免責条項です。 次に来るのは、新しく起動したプラットフォームの IIOP アドレス (IOR の形式) です。 最後のは、「メーン・コンテナ」と呼ばれるコンテナの準備ができたことを示し、JADE ランタイムのスタートアップが完了します。 JADE ランタイムが立ち上がると、エージェントが起動し、ウェルカム・メッセージをプリントします。 上記のコマンドラインで指定したようにエージェントのニックネームは「buyer」です。 プラットフォーム名「IBM8695:1099/JADE」は、JADE を実行中のホストとポートに基づいて自動的に割り当てられます (プラットフォームに名前を割り当てる方法は JADE Administrator's ガイドを参照のこと)。 *** 3.3 エージェントの終了 ウェルカム・メッセージをプリントする以外に何もしなくても、エージェントはまだ実行中です。 エージェントを終了するには、doDelete() メソッドを呼ばなければなりません。 setup() メソッドは、エージェントが起動するとすぐに、JADE ランタイムによって呼び出されます。このメソッドは、エージェントを初期化することを目的としています。 takeDown() メソッドも同様に、エージェントが終了する前に呼び出されます。 このメソッドは、エージェントをクリーンアップすることを目的としています。 *** 3.4 エージェントに引数を渡す エージェントは、コマンドラインで指定したスタートアップ引数を取得することができます。 その引数は、Agent クラスの getArguments() メソッドで Object 配列として取得することができます。 第2章で述べたとおり、BookBuyerAgent にコマンドライン引数として買いたい本のタイトルを取得できるようにします。 それを達成するために以下のように変更します。 注意: リクエストを送信する売り手エージェントのリストは固定されています。 第6章で、エージェントを動的に発見する方法を解説します。 import jade.core.Agent; import jade.core.AID; public class BookBuyerAgent extends Agent { // The title of the book to buy private String targetBookTitle; // The list of known seller agents private AID[] sellerAgents = {new AID("seller1", AID.ISLOCALNAME), new AID("seller2", AID.ISLOCALNAME)}; // Put agent initializations here protected void setup() { // Printout a welcome message System.out.println("Hallo! Buyer-agent "+getAID().getName()+" is ready."); // Get the title of the book to buy as a start-up argument Object[] args = getArguments(); if (args != null && args.length > 0) { targetBookTitle = (String) args[0]; System.out.println("Trying to buy "+targetBookTitle); } else { // Make the agent terminate immediately System.out.println("No book title specified"); doDelete(); } } // Put agent clean-up operations here protected void takeDown() { // Printout a dismissal message System.out.println("Buyer-agent "+getAID().getName()+" terminating."); } } コマンドライン引数は、括弧の中にスペースで区切って指定します。 JADE の LEAP アドオンを使用している場合は、引数はスペースの代わりにコロン (『;』) で区切ります。 C:\jade> java jade.Boot buyer:BookBuyerAgent(The-Lord-of-the-rings) This is JADE snapshot - 2003/10/24 13:43:39 downloaded in Open Source, under LGPL restrictions, at http://jade.cselt.it/ IOR:000000000000001149444C3A464950412F4D54533A312E300000000000000001000000000000 0060000102000000000A3132372E302E302E310009A600000019AFABCB0000000002BCE5528F0000 000800000000000000000A0000000000000100000001000000200000000000010001000000020501 000100010020000101090000000100010100 Agent container Main-Container@JADE-IMTP://IBM8695 is ready. Hallo! Buyer-agent buyer@IBM8695:1099/JADE is ready. Trying to buy The-Lord-of-the-rings ** 4 エージェントのタスク - BEHAVIOUR クラス 第3章で述べたとおり、エージェントが行わなければならない実際のジョブは、通常「behaviours」の内部で実施します。 behaviour はエージェントが実施することができるタスクを表し、jade.core.behaviours.Behaviour の拡張クラスのオブジェクトとして実装します。 エージェントが behaviour オブジェクトによって実装されたタスクを実行するために Agent クラスの addBehaviour() メソッドで、エージェントに behaviour を追加すればいい。 Behaviours はいつでも追加することができます: エージェントが (setup() メソッドで) 起動するときに、または、別の behaviours の内部から。 Behaviour の拡張クラスはそれぞれ action() メソッドを実装しなければなりません。 action() メソッドは behaviour の実行時に、実行される操作を実際に定義します。 done() メソッド (論理値を返す) は behaviour が完了したかどうかを指定します。 そして、エージェントが実施している behaviours のプールから削除しなければなりません。 *** 4.1 Behaviours のスケジューリングと実行 エージェントは同時に数個の behaviours を実行することができます。 しかしながら、エージェントの behaviours のスケジューリングは (Java のスレッドに関しては) プリエンプティブ [pre-emptive] ではなく、協調的 [cooperative] であることに注意することが重要です。 &color(red){ これは behaviour の実行をスケジュールした場合、そのエージェントの action() メソッドが呼び出され、そのメソッドから戻るまで実行することを意味します。 }; したがって、エージェントがいつ behaviour の実行から、次のものの実行へ切り替えるのかを定義するのはプログラマです。 プログラマに小さな努力を必要とするが、この方法にはいくつかの利点があります。 - Java シングルスレッドごとにエージェントを持つことを可能にします (これは特に、携帯電話のような限られた資源環境では極めて重要です)。 - behaviour スイッチの方が Java スレッドスイッチより非常に速いので、より良いパフォーマンスを提供します。 - すべての behaviours が同一の Java スレッドで実行されるので、同一資源にアクセスする並行 behaviours の間のすべての同期問題を回避します (パフォーマンスもスピードアップします)。 - behaviour スイッチが発生する場合、エージェントの状態はスタック情報を含んでいません。そこで、エージェントの状態の「スナップショット」をとることが可能です。 これは、重要な拡張機能を実装することを可能にします。 例えば、後で再開するために、固定記憶域にエージェントの状態を保存すること (エージェントの持続性 [agent persistency])。 もしくは、リモート実行するために、別のコンテナに転送すること (エージェントの移動性 [agent mobility])。 持続性と移動性は、高度な JADE の特徴ですが、このチュートリアルの範囲外です。 エージェントスレッドの実行の軌跡は、図2に表されています。 JADE には、Java シングルスレッドごとにエージェントがいます。 しかしながら、JADE エージェントは Java で書かれているので、他のスレッドを必要とするときはいつでも、プログラマは新しい Java スレッドを起動することができます。 そうした場合、この章で述べた利点はもはや有効ではないので、注意することを忘れないでください。 スケジューリングメカニズムの解説を考慮することは、 下記にレポートしたような behaviour を強調することにとって重要です。 その behaviour は action() メソッドから決して戻らないので、他のどの behaviour も実行されない。 public class OverbearingBehaviour extends Behaviour { public void action() { while (true) { // do something } } public boolean done() { return true; } } 実行に使用可能な behaviours がない場合、エージェントのスレッドは CPU 時間を消費しないようにスリープ状態に移行します。 それは、再び実行に使用可能な behaviour があると、すぐに立ち上がります。 *** 4.2 1回限りの behaviours, 周期的な behaviours, 一般的な behaviours 3つのタイプの behaviour で、区別することができます。 1) "One-shot" behaviours は、どこかの action() メソッドで1回だけ実行され、すぐに完了します。 jade.core.behaviours.OneShotBehaviour は、true を返す done() メソッドをすでに実装し、 one-shot behaviours を実装するために、便利に拡張することができます。 public class MyOneShotBehaviour extends OneShotBehaviour { public void action() { // perform operation X } } オペレーション X は1回だけ実行されます。 2) "Cyclic" behaviours は、どこかの action() メソッドで実行され、action() メソッドが呼び出されるたびに、同一のオペレーションを実行し、決して完了しません。 jade.core.behaviours.CyclicBehaviour は、false を返す done() メソッドをすでに実装し、 cyclic behaviours を実装するために、便利に拡張することができます。 public class MyCyclicBehaviour extends CyclicBehaviour { public void action() { // perform operation Y } } オペレーション Y は永遠に繰り返し実行されます (上記の behaviour を実施しているエージェントが終了するまで)。 3) Generic behaviours は状態を組み込み、状態に応じて異なるオペレーションを実行します。 特定の条件が満たされる場合に、それは完了します。 public class MyThreeStepBehaviour extends Behaviour { private int step = 0; public void action() { switch (step) { case 0: // perform operation X step++; break; case 1: // perform operation Y step++; break; case 2: // perform operation Z step++; break; } } public boolean done() { return step == 3; } } オペレーション X, Y, Z は次々に実行され、その後、behaviour は完了します。 JADE は複雑な behaviours を作成するために、シンプルな behaviours を一緒に組み合わせる可能性を提供します。 しかしながら、この特徴はこのドキュメントの範囲外です。 詳細に関しては、SequentialBehaviour、ParallelBehaviour、および FSMBehaviour の Javadoc を参照してください。 *** 4.3 任意の時点でのスケジューリングオペレーション JADE は任意の時点で特定のオペレーションを実行する behaviours を簡単に実装することが可能な、2つの既存の (jade.core.behaviours パッケージにある) クラスを提供します。 1) タイムアウトの有効期限 (コンストラクタで指定した) が切れた後に handleElapsedTimeout() 抽象メソッドを実行するような方法を用いた action() メソッドと done() メソッドがすでに実装されている WakerBehaviour があります。 handleElapsedTimeout() メソッドの実行後、behaviour は完了します。 public class MyAgent extends Agent { protected void setup() { System.out.println("Adding waker behaviour"); addBehaviour(new WakerBehaviour(this, 10000) { protected void handleElapsedTimeout() { // perform operation X } } ); } } オペレーション X は「Adding waker behaviour」プリントアウトが表示されてから、10秒後に実行されます。 2) 実行後に (コンストラクタで指定した) 一定時間待機して繰り返し実行する onTick() 抽象メソッドを実行するような方法を用いた action() メソッドと done() メソッドがすでに実装されている TickerBehaviour があります。 TickerBehaviour は永遠に完了しません。 public class MyAgent extends Agent { protected void setup() { addBehaviour(new TickerBehaviour(this, 10000) { protected void onTick() { // perform operation Y } } ); } } オペレーション Y は10秒ごとに定期的に実行されます。 *** 4.4 book trading サンプルで必要な Behaviours behaviour 基本的なタイプを解説して、どの behaviours が book trading サンプルの Book-buyer エージェントと Book-seller エージェントによって実施されなければならないのか、についての分析に移りましょう。 **** 4.4.1 Book-buyer エージェントの behaviours 第2章で解説したように、Book-buyer エージェントは売り手エージェントに対して、買うように指示された本を定期的にリクエストします。 それは TickerBehaviour を用いて、簡単に達成することができます。 実際には売り手エージェントのリクエストに掛けで対処する別の behaviour を追加します。 BookBuyerAgent クラスの setup() メソッドの変更点は以下のとおりです。 protected void setup() { // Printout a welcome message System.out.println("Hallo! Buyer-agent "+getAID().getName()+" is ready."); // Get the title of the book to buy as a start-up argument Object[] args = getArguments(); if (args != null && args.length > 0) { targetBookTitle = (String) args[0]; System.out.println("Trying to buy "+targetBookTitle); // Add a TickerBehaviour that schedules a request to seller agents every minute addBehaviour(new TickerBehaviour(this, 60000) { protected void onTick() { myAgent.addBehaviour(new RequestPerformer()); } } ); } else { // Make the agent terminate System.out.println("No target book title specified"); doDelete(); } } myAgent の protected 変数の使用上の注意事項: 各 behavior には、それを実行しているエージェントへのポインタがあります。 実際に売り手エージェントのリクエストに対処する RequestPerformer の behaviour は、エージェントコミュニケーションについて議論している第5章で解説します。 **** 4.4.2 Book-seller エージェントの behaviours 第2章で解説したように、 Book-seller エージェントはそれぞれ買い手エージェントからのリクエストを待機して、リクエストされた本を出します。 ここでのリクエストとは、本または注文書にオファーをするリクエストのことです。 それを達成可能な設計とは、Book-seller エージェントに2つの周期的な behaviours を実行させることです: 1つはオファーのリクエスト専用で、もう1つは注文書専用です。 実際に買い手エージェントから入ってくるリクエストを検出してサーブする方法は、エージェントコミュニケーションについて議論している第5章で解説します。 さらに、ユーザーが GUI から新しい本を追加するときはいつでも、Book-seller エージェントに販売中の本のカタログを更新する1回限りの behaviour を実行させる必要があります。 BookSellerAgent クラスの実装方法は以下のとおりです (OfferRequestsServer クラスと PurchaseOrdersServer クラスは第5章で表します)。 import jade.core.Agent; import jade.core.behaviours.*; import java.util.*; public class BookSellerAgent extends Agent { // The catalogue of books for sale (maps the title of a book to its price) private Hashtable catalogue; // The GUI by means of which the user can add books in the catalogue private BookSellerGui myGui; // Put agent initializations here protected void setup() { // Create the catalogue catalogue = new Hashtable(); // Create and show the GUI myGui = new BookSellerGui(this); myGui.show(); // Add the behaviour serving requests for offer from buyer agents addBehaviour(new OfferRequestsServer()); // Add the behaviour serving purchase orders from buyer agents addBehaviour(new PurchaseOrdersServer()); } // Put agent clean-up operations here protected void takeDown() { // Close the GUI myGui.dispose(); // Printout a dismissal message System.out.println("Seller-agent "+getAID().getName()+" terminating."); } /** This is invoked by the GUI when the user adds a new book for sale */ public void updateCatalogue(final String title, final int price) { addBehaviour(new OneShotBehaviour() { public void action() { catalogue.put(title, new Integer(price)); } } ); } } BookSellerGui クラスはシンプルな Swing GUI ですが、このチュートリアルの範囲外なので、ここでは表しません。 そのコードはこのチュートリアルのソースパッケージの中から入手できます。 ** 5 エージェントコミュニケーション - ACLMESSAGE クラス *** 5.1 The ACL language *** 5.2 Sending messages *** 5.3 The book trading example messages *** 5.4 Receiving messages *** 5.5 Blocking a behaviour waiting for a message *** 5.6 Selecting messages with given characteristics from the message queue *** 5.7 Complex conversations *** 5.8 Receiving messages in blocking mode ** 6 イエローページサービス - DFSERVICE クラス これまで書いたコードでは、固定された売り手エージェント (seller1 と seller2) がいて、買い手エージェントはそれぞれ売り手エージェントをすでに知っていると仮定していました (3.4節で表したコードの BookBuyerAgent クラスの AID[] sellerAgents メンバー変数を参照のこと)。 この章ではその仮定を排除して、買い手エージェントに任意の時点で利用可能な売り手エージェントを動的に発見させるため、 JADE プラットフォームが提供しているイエローページサービスを利用する方法を解説します。 *** 6.1 DF エージェント "yellow pages" サービスは図4に表されているように、別のエージェントがエージェントを発見できて、引き続いて利用することができるように DF エージェントが提供している1つ以上のサービスをエージェントに公開することができます。 JADE (FIPA 仕様準拠) のイエローページサービスは、DF (Directory Facilitator) と呼ばれるエージェントによって提供されています。 FIPA 準拠のプラットフォームはそれぞれデフォルト (ローカルネームが「df」の) DF エージェントをホストします。 別の DF エージェントはアクティブになることができて、1人でイエローページのカタログの配布を提供できるように、数人の DF エージェント (デフォルトのものを含む) は連合することができます。 *** 6.2 DF との相互作用 DF がエージェントであるということは、FIPA 仕様準拠の固有オントロジー (FIPA-agent-management オントロジー) と固有内容言語 (SL0 言語) を使用する ACL メッセージを交換することで従来どおりに DF と相互作用することが可能です。 しかしながら、これらのインタラクションをシンプルにするため、JADE はメソッドの呼び出しを通じて、サービスを公開して検索することを可能にする jade.domain.DFService クラスを提供します。 **** 6.2.1 公開サービス 1つ以上のサービスを公開したいエージェントは、エージェントの AID を含む記述、もしかすると別のエージェントと相互作用するために知っている必要がある言語とオントロジーのリスト、そして、公開サービスのリストを DF に提供しなければなりません。 公開サービスごとに、サービスタイプ、サービス名、そのサービスを利用するために要求される言語とオントロジー、そして、数個の servicespecific プロパティを含む記述を提供します。 DFAgentDescription、ServiceDescription、および、Property クラス (それらは jade.domain.FIPAAgentManagement パッケージに含まれる) は、3つの抽象概念を表現します。 サービスを公開するために、エージェントは固有記述 (DFAgentDescription クラスの例として) を作成し、DFService クラスの static メソッドの register() を呼び出さなければなりません。 典型例として、必然的ではないが、サービスの登録 (公開) は Book seller エージェントの場合は以下に示すように setup() メソッドで行われます。 protected void setup() { ... // Register the book-selling service in the yellow pages DFAgentDescription dfd = new DFAgentDescription(); dfd.setName(getAID()); ServiceDescription sd = new ServiceDescription(); sd.setType("book-selling"); sd.setName("JADE-book-trading"); dfd.addServices(sd); try { DFService.register(this, dfd); } catch (FIPAException fe) { fe.printStackTrace(); } ... } このシンプルな例では、言語、オントロジー、または service-specific プロパティを一切指定しないことに注意してください。 エージェントが終了する際に、公開サービスを登録削除することは良い習慣です。 protected void takeDown() { // Deregister from the yellow pages try { DFService.deregister(this); } catch (FIPAException fe) { fe.printStackTrace(); } // Close the GUI myGui.dispose(); // Printout a dismissal message System.out.println("Seller-agent "+getAID().getName()+" terminating."); } **** 6.2.2 サービスの検索 サービスを検索したいエージェントは、テンプレート記述を DF に提供しなければなりません。 検索結果は、提供されたテンプレートにマッチするすべての記述のリストです。 テンプレートで指定されたすべてのフィールドが、同一の値の記述で表現されているならば、その記述はテンプレートにマッチします。 "book-selling" タイプのサービスを提供するすべてのエージェントを動的に発見するために、Book buyer エージェントで用いられるコードに例示したように DFService クラスの static メソッドの search() を使用することができます。 public class BookBuyerAgent extends Agent { // The title of the book to buy private String targetBookTitle; // The list of known seller agents private AID[] sellerAgents; // Put agent initializations here protected void setup() { // Printout a welcome message System.out.println("Hallo! Buyer-agent "+getAID().getName()+" is ready."); // Get the title of the book to buy as a start-up argument Object[] args = getArguments(); if (args != null && args.length > 0) { targetBookTitle = (String) args[0]; System.out.println("Trying to buy "+targetBookTitle); // Add a TickerBehaviour that schedules a request to seller agents every minute addBehaviour(new TickerBehaviour(this, 60000) { protected void onTick() { // Update the list of seller agents DFAgentDescription template = new DFAgentDescription(); ServiceDescription sd = new ServiceDescription(); sd.setType("book-selling"); template.addServices(sd); try { DFAgentDescription[] result = DFService.search(myAgent, template); sellerAgents = new AID[result.length]; for (int i = 0; i < result.length; ++i) { sellerAgents[i] = result[i].getName(); } } catch (FIPAException fe) { fe.printStackTrace(); } // Perform the request myAgent.addBehaviour(new RequestPerformer()); } } ); ... ことに注意してください。 売り手エージェントのリストの更新が known Note that the update of the list of known seller agents is done before each attempt to buy the target book since seller agents may dynamically appear and disappear in the system. The DFService class also provides support for subscribing to the DF to be notified as soon as an agent publishes a given service (see the searchUntilFound() and createSubscriptionMessage() methods), but this is outside the scope of this tutorial.