本開発者用ガイドは Open Agent Architecture (OAA) の設計と実装について述べ, このフレームワークの中でエージェントを作る方法を記述する.
このドキュメントは OAA プロジェクトで定義されたゴールとコンセプト
(Specification Report や Definition Report といった関連ドキュメントに
書かれている)に精通しているものとする.ある程度 Prolog 言語の知識も仮定する.
用語 "エージェントベースのプログラミング" は今日の技術用語では多くの 意味を持っている.OAA フレームワークの中では,エージェントは分散ネットワーク をまたがってお互い通信し協調する独立のプロセスのように見える. 我々は,エージェントは単なる分散オブジェクト以上のものと考える.これは エージェントの高いレベルの通信言語と,単なる受動的な参加者とは対照的に 計算にアクティブに寄与する能力による.
OAA のゴールとコンセプト詳細は他(OAA Specification Report,OAA Definition Report) で述べられているが,ここでアーキテクチャのいくつかの設計目標について簡単に 再び述べることは有用だろう.
OAA はユーザに対してタスクを解くために一緒に働くエージェントのコミュニティ という考えに焦点を当てている.見通されるタスクを全て自律的に完成させる役割の エージェントを一つ作ることは原則的には可能であるが,エージェントベースの アプローチの利点は計算がたくさんの特別な "エキスパート" エージェント に広がっているときに一番わかる. OAAのフレームワークでは,ユーザからのリクエストはたいてい多数のエージェントの 連合した奮闘によって処理される(例:自然言語入力/アクションのプラン/ データベースアクセス/結果表示などのエージェントが参加するだろう.). このことはエージェント間の通信が,タスクの実行中に許容できないオーバーヘッドを 招かないように,効率的に実装されることを要求する.
エージェントは選んだプラットフォームのどこでも動き,多くのプログラミング言語で 記述できるように,エージェントのコミュニティは種々さまざまであるべきである. OAA はエージェントがこうした条件下で一緒に動くように標準的な変換セットを提供する. 更に,分散された計算は並列計算(そこでは複数のエージェントがタスクのさまざまな 面で各々協力,あるいは競争して動くだろう)への扉を開く.
新しいメンバーがコミュニティに加わるときに,インタラクションの全体の影響が 変わるべきである.もしあるエージェントが遅参者として共同プロセスに加わる なら,新しいエージェントが計算に参加できるようにエージェント間の インタラクションは十分フレキシブルでなければならない. "plug & play" アーキテクチャにより,新しいエージェント(達)の 形で後から追加された新しい機能を簡単に利用して,先に存在するエージェント と一緒にシステムを構築することができる.
人間のユーザが共同エージェントの経験に携わることが望まれているため, エージェント通信言語(ICL)は定義されたものの,これは自然言語(人間の)入力を 表わすのに十分強力でなければならない. もし ICL が完全な自然言語表現を表せたら,手続き的(プログラム的)な インタラクションも可能だろう.
エージェントは,リクエストされたときだけアクションを実行し情報を返すような
単なる受動的なデータソースを越えるものである.
エージェントは自分の回りの世界のアクションを監視して,
アクションを起こすべき時を決めることができるべきであり,おそらく関連状況に
ついてユーザや他のエージェントグループに注意を促すだろう.
更に,エージェントは他のエージェントのインタラクションを見張ることができる
べきであり,おそらくよりよい方法について提案するだろう.
Figure 1 は,典型的なエージェントの例として Office Assistant プロトタイプから 数個のエージェントを使って,Open Agent Architecture の簡単な構造を示している. Figure 1 に示されたコンフィギュレーションは,マルチモーダル・ユーザインタフェース エージェント(ユーザに対する入力を解析しマルチメディア出力の提示を調和させる), オフィス関連の様々なタスクに対するドメインエージェントの集まり, 特別なサーバエージェント - facilitator エージェント (エージェント通信とコントロールを調整したり,自分のクライアントエージェント に対するグローバルデータストアを提供する責任がある)から構成されている. システムコンフィギュレーションは一つの facilitator に制限されないことに 注意すること.複数の facilitator 間の接続はこのドキュメントの後で議論する (Multiple Facilitator Configurations参照).
Figure 1: OAA structure
OAA の各エージェントは,facilitator エージェントか クライアント エージェントのどちらかである.クライアントエージェントは各々が(いくつかの点で) ある facilitator(クライアントに対して通信と他の不可欠なサービスを提供する)の クライアントとしてふるまうためそう呼ばれる. 起動されると,クライアントエージェントは facilitator(その "親 facilitator" として知られる)につなぐ.コネクション上でエージェントは親 facilitator に 提供可能なサービスについて知らせる.そのエージェントが必要とされる時, facilitator は エージェント通信言語 (ICL)を使って リクエストを送る.エージェントはこのリクエストをパースし,それを処理し facilitator に答かステータスレポートを送る.リクエストの処理において, エージェントは OAA が提供する様々な能力を利用できる. 例えば,他のエージェントのサービスをリクエストしたり,トリガをセットしたり, facilitator へデータを掲示したりできる.
各 facilitator は,自分のクライアントエージェントのセットに対する 3種のタスク管理に責任を持つエージェントである.
Controller - facilitator はコントローラとしてふるまい, どの enable なエージェントが矛盾なく実行できるかを決め, 彼らに進むための制御スレッドを与える. 所定の機能エージェントからの結果は,他のエージェントによる アクションのために再度 facilitator に掲示される. 従って,エージェントは非同期なプロセスであるが,コントローラに よって起動のときに調整されることができる.
Communication Router - facilitator はルータとしてふるまい, エージェント間にメッセージとデータを配る. わずかな例外を除いて,全てのエージェント通信は facilitator を通って送信される. エージェント間通信に小さなオーバーヘッドを加えるが,フレキシビリティと適用性 の価値はこの欠点をおおきく上回る.
Knowledge Base Server -facilitator は, 文法,語彙,通信と観察の両方から蓄積されたユーザに関する事実, インタラクション/ルール/選択の履歴に関する事実,のためのリポジトリとして働く. 知識ベースはユーザ入力の分析,エージェント達の推論のサポート, インタラクションの文脈の維持のために必要とされる.各エージェントは 生成時に特定の知識ベースを知識ベースサーバに登録する. エージェントに自分の機能に特有な知識を提供させることにより, 他のエージェントは必要な時にこの情報にアクセスすることができる.
Figure 1 に表わされたエージェント コンフィギュレーションは,一つの facilitator エージェントとその facilitator が 責任を負ういくつかのクライアントエージェントを持つ典型的な配置を含んでいる. facilitator は各エージェントの能力を知っていて,実行すべきタスクが与えられると, 望ましい結果を生み出すためにエージェントがどうインタラクトすべきかを決める. しかし,OAA 内では,多重 facilitator エージェントがインタラクトできるような もっと複雑なコンフィギュレーションを含む,他のネットワーク・ コンフィギュレーションも可能である.
前章で書いたように,facilitator は実際一つの OAA エージェントにすぎなく, ドメインエージェントと同じエージェントライブラリや通信標準を使う. 多重 facilitator コンフィギュレーションの場合,各 facilitator は 自分のクライアントエージェントによって解けるゴールを全て解くことができる "スーパー" エージェントのように考えることができる. つまり,facilitator は入ってくる各ゴールを一つ以上のクライアントエージェント に委譲することによってこれを成し遂げる.
OAA 内で多重 facilitator コンフィギュレーションはいくつか考えられる. OAA エージェントのネットワーク構造を設計するときに考えるべき一つの問題は, 循環するリクエストが起こらないことを保証することである. (あるエージェントが他のエージェントにタスクを頼むときに, 二番目が最初のエージェントに頼むことによってタスクを完成させようする.)
我々は facilitator エージェントの階層(Figure 2)を使って,可能性のある一つの 多重 facilitator コンフィギュレーションを実験した. ゴール(G) がローカル facilitator (BB1) に掲示され,BB1 にいる facilitator エー ジェントが自分の子エージェントが誰もゴールを成し遂げるのに必要な知識を持って いないと判断したとき,階層のより上の facilitator エージェント (BB4) に ゴールを伝達する.(ここでは "子 " エージェントは 非 facilitator エージェントのことを指し,図には表れていない.) このより上の facilitator エージェントは自分の下のレベルの facilitator が 解けるゴールに関する知識ベースを保つ. このようなリクエストを上の facilitator エージェントが受け取ったとき, 子エージェント(彼ら自身 facilitator エージェントである)に リクエストを順番に下に伝える. 子エージェントは,ゴールを評価できる直接の子エージェントを持っているか, あるいは彼ら自身でゴールを別の子エージェントに渡すことができる. Figure 2 の場合,BB4 は子 facilitator が誰もゴールを処理できないと決定し, よって上の facilitator エージェント (BB5) にゴールを送る. BB5 はゴールを BB6 に送り,BB6 は BB9 に順番に送る. こうした " 委託されたゴール " が facilitator の階層を通って渡される とき,始めた facilitator のアドレスに関する情報 (G 付きの BB1 によって示される)を伴う.
この " 続き " 情報によってリターン通信(答付きあるいは失敗)が facilitator 階層を再びたどることなく,発信源の facilitator に直接送られることが 可能になる. また,応答している知識ソースBB9 のIDを発信源に返すことができるので, 将来 BB1 からの同種の問い合わせは facilitator 階層を通ることなしに BB9 に直接出されるかもしれない.
Figure 2: Multiple facilitators arranged in a hierarchy
OAA ベースのシステムに加わる全エージェントは,ICL で表現された能力仕様のセット (提供するサービスを記述)を定義し公表する.これらはエージェントに対する 高いレベルのインタフェースを確立する.これは facilitator によってエージェントと 通信する中で使われ,最も重要なのはサービスリクエスト(やリクエストの一部)を エージェントに委任する中で使われる.ICL の基礎として Prolog を使用したことも あって,我々はこの能力仕様のことを solvables と呼ぶ.
例えば,メールシステムに対するエージェントを作成する場合 solvables は, 人へメッセージを送ること,あるサブジェクトに関するメッセージがメールキューに 届いたかどうかを調べること,あるいは,あるメッセージをスクリーン上に表示する ことに対して定義されるだろう. database wrapper エージェントでは,データベース中にある各々のリレーションに 対応した明確な solvable が定義されるだろう.
実装言語やプラットフォーム,システムのエージェント間に残っていそうな起源の混成 により,システムのスタートアップや動作の持続にかなりの複雑さが伴いそうである. それに伴う努力をいくらか緩和するために,OAA は実行マネージャ Start-It を含む.
いったん相互運用可能なエージェントの集合が一式のタスクに取り組むように 組み合わされると,Start-It は各エージェントを正しいプラットフォーム上で 起動する手段を提供し,エージェントが OAA facilitator に対する必要な結合を 行なうことを保証する. 同じく重要なのは,正しく機能し続けていることを調べるため Start-It は 各エージェントの状態を監視する.Start-It があるエージェントの失敗を 見つけたというイベントにおいて,失敗から回復し自動的にエージェントを再スタート する手段を講じることが可能である.
各エージェントに対するスタートアップ仕様と失敗を扱う方法の命令は, コンフィギュレーション・ファイルに入っていて,これはエージェント開発ツールの コンポネントによって自動的に生成可能である. Start-It の使用は以下でより詳しく述べられている (10.1 章,Start-It 参照).
OAA の一つのゴールは広範囲の実装言語を使ったエージェントの作成を容易にする ことである.本開発者用ガイドは現在サポートしているどの言語で作業している 開発者によっても使われることを意図している.しかし,このことはプログラミング 概念に言及するときに使う適切な用語の選択において相当な挑戦を引き起こす.
OAA の根底は Prolog プログラミングであり,いくつかの Prolog の概念 (特に, ユニフィケーション と バックトラッキング)が OAA に用いられた.したがって,開発者はこうした概念を少し理解した方がよく, このガイドの中でこれらの用語が適したところは使っている.
一般的な 設計 の概念と 実装 の問題を区別すると役に立つ. 設計の概念を議論する場合,本マニュアルはどのプログラミング言語にも 明らかには関係しない一般的な用語を使う.例えば,エージェントは 他のエージェントに``リクエストされる''ような``サービスを提供する'' と言う.
実装の問題を議論する場合,Prolog 固有の用語と言語中立の用語の混合が 注意深く選ばれている.Prolog 用語が,本質的に Prologライクである OAA のある特徴 を描写する助けとなる場合には,そうした用語を使う.例えば,エージェントは ``ゴールを解く''(あるいは``問い合わせを解く'')ことができると言う.これは 第一にゴールはエージェントの 能力仕様 の一つに ユニファイされるはずであり,第二にゴールは複数の解を持ちうることを 強調するためである.
それ以外は,広く知られていてプログラミング言語中立と考えられる用語を使う.
例えば Prolog 用語``predicate''の代わりに``手続き''や``宣言''を
文脈に応じて使う.
エージェントライブラリは OAA の全てのエージェント(facilitator エージェント を含む)に共通な機能を提供する. ある意味でエージェントライブラリによって提供される能力は OAA エージェントで あることを意味するものを決める.ライブラリは様々な異なるプログラミング言語に ポーティングされており,ライブラリの包含は各エージェントの実装の一部である. エージェント開発者は,ライブラリが提供する手続きを呼んだり,ライブラリが 期待する所定の宣言を与えたりコールバック手続きの定義を行なう必要がある. これらの手続きや宣言は本開発者用ガイドを通して記述されている. この章ではエージェントライブラリの基本的な特徴と全てのエージェントに共通な ライブラリの使用についていくつか述べる.
OAA が基盤とする送信プロトコルとして TCP/IP (Transmission Control Protocol / Internet Packets) が選ばれた.TCP は多くの OS (UNIX, Macintosh, DOS, Microsoft Windows など) 上で標準化されているので,その使用により多様なプラットフォーム上でエージェント を相互運用するタスクが容易になる.
エージェントライブラリがこのプロトコル層をわかりやすく提供しているので, エージェント開発者は TCP/IP の詳細を気にする必要はない.
エージェントライブラリは,セットアップファイル setup.pl(各エージェント に必要とされる)を読み込んだコードを含む. これはエージェントのカレントディレクトリか(UNIX 下では) エージェントを動かしているユーザのホームディレクトリになければならない. このファイルは少数のセットアップパラメータを含んでいて,これは エージェントシステムが新しいマシンやファイルシステムに動かされたときに 変わる.
セットアップファイル中の最も重要なパラメータは
rootdata(PortNumber, HostName)これは,ルート facilitator エージェントがインストールされるべき固定のマシン アドレスを指定する.エージェント(あるいは階層コンフィギュレーションの場合の 新しい facilitator エージェントやドメインエージェント)がシステムに接続するとき, この情報がルート facilitator エージェントの場所を見つけたり 接続情報をリクエストするのに使われる. 次の例で示すように,PortNumber は integer,HostName は Prolog のアトムとして 表わされる.
rootdata(3333, 'trestle.ai.sri.com')
PortNumber と HostName の値はコマンドラインや環境変数としても指定することが でき,この場合セットアップファイルに現れる値をどれもオーバーライドする. 優先順位は次のとおり.
これらの値をコマンドラインで表すために,エージェントのコマンドラインに 次のフラグを含む.
-oaa_host <HostName> -oaa_port <PortNumber>
これらを環境変数で表すために,変数 OAA_HOST と OAA_PORT を使う. UNIX 下では通常次のコマンドを使う.
setenv OAA_HOST <HostName>
setenv OAA_PORT <PortNumber>
これらの値をコマンドライン引数(あるいは環境変数)で表す場合,両方 の値が定義されていなければらない.二つのうち片方しか定義されない場合は無視される.
もう一つエージェントのコマンドラインに設定可能な変数は,エージェントの内部的な 名前(たいていエージェント中にハードコードされている)である. この値はコマンドライン上の引数 -oaa_name NAME を使って設定できる. (また,環境変数 <HostName> を使っても設定される.)
solvable(GoalList).例えば,非常に簡単な mail エージェントの solvable 宣言は次のように見えるだろう.
solvable(
[last_message(_MessageNum),
get_message(_MessageNum, _Msg),
send(mail, _ToPerson)
]).
solvable 宣言を書く場合に,各々のゴール仕様が Prolog の変数と定数に 関するセマンティクスに従って使われることを理解することは有用であろう. すなわち,facilitator があるゴールを解くためのリクエストを受けとる時に, そのリクエストが自分とつながっているエージェントの solvable 宣言の中に 含まれるゴールに反するかを調べるためにユニフィケーションを使う.
エージェントが自分のサービスの一つに対するリクエストを受けとる時, そのエージェントの do_event 手続きが呼ばれる. これらの呼び出しを処理するために,各エージェントは solvable 宣言で 列挙されたゴールの各々に対して実装コードを定義しなければならない.
'do_event' は入ってくる解くべきゴールを伴って呼ばれ,また 引数 CallingKS は実行すべきサービスをリクエストしたエージェントを指定する. CallingKS は,付加的な情報をリクエストしているエージェントに送り返すために, solve 関数の address(KS) パラメータ と共に使われる.
例えば,上述の solvable に対して,Prolog の実装では 次のような do_event ルールを定義する必要があるだろう.
do_event(CallingKs, last_message(MessageNum)) :-
< last_message solvable の実装コード ...>
do_event(CallingKs, get_message(MessageNum, Msg)) :-
< get_message solvable の実装コード ...>
do_event(CallingKs, send(mail, ToPerson)) :-
< send solvable の実装コード ...>
C, Delphi, Visual Basic のような言語用エージェントライブラリでは
do_event は関数であり,その中に各 solvable リクエストイベントを
処理するための if や switch ステートメントがある.
上記の例を C で書くと以下のようになる.
int do_event(CallingKs, func, args, answers)
char *CallingKs, *func, *args;
char **answers;
{
int arity = 0;
int success = 1;
/* Default: predicate succeeds without returning variables */
*answers = malloc(strlen(func)+strlen(args)+10);
if (*args) {
sprintf(*answers, "[%s(%s)]", func, args);
arity = list_len(args);
}
else
sprintf(*answers, "[%s]", func);
/*--- last_message(Message) ---*/
if ((strcmp(func, "last_message") == 0) && (arity == 1)) {
/* Code implementing the last_message solvable */
} else
/*--- get_message(MsgNum, Msg) ---*/
if ((strcmp(func, "get_message") == 0) && (arity == 2)) {
/* Code implementing the get_message solvable */
} else
/*--- send(mail,ToPerson) ---*/
if ((strcmp(func, "last_message") == 0) && (arity == 1)) {
/* Code implementing the send solvable */
} else
return 0; /* Unknown predicate not handled */
return 1; /* Solvable processed */
}
C, Visual Basic, Delphi ライブラリでは 'do_event' 関数は成功か失敗,あるいは
入ってくるリクエストに対する複数の解を変数 answers に返す.
イベントリクエスト send(mail,'Adam') に対して,成功のアクションなら
answers 中に "[send(mail,'Adam')]" を返し,失敗なら
空の解リスト "[]" を返すことによって示される.
複数解も同様に返される.すなわち,問い合わせ manager(adam,X) は
answers 中に "[manager(adam,jerry),manager(adam,doug)]" をストアする
ことによって 2つの解を返すかもしれない.
また,ある先の時間まで 'do_event' リクエストに対する解の提供を遅らす 必要があるかもしれない.例えば,ロボットがある位置に到達するよう 求められた場合,これは即座に達成できるゴールではない.
some_delay_id = delay_solution(answers)を使ってエージェントは,即時の解を与えずに do_event から抜け出すことができ, 少し後でゴールが達成された(あるいは失敗)時に,エージェントは以下を使って ゴールに対する応答を送ることができる.
return_delayed_solutions(some_delay_id, solutions)遅延解の機能は Prolog ライブラリにはまだ加えられていない.
タスクの実行中にエージェントは,他のエージェントの知識ソースによって与えられた 情報やサービスを利用することができる.他のエージェントにリクエストする メカニズムは solve() と呼ばれる一つの手続きの中にカプセル化されている. solve() 手続きは,パラメータリストで指定された問い合わせに応じて, 他のエージェントから情報を獲得するのに異なる方法を提供する.
ある問い合わせに対する解をリクエストする(Prolog における)標準的な方法は 次の手続きを呼ぶことである.
solve(Goal, ParameterList)
注: C や Delphi のような他のプログラミング言語は本開発者用ガイドで 述べられているライブラリルーチン全てに対して各々のシンタックスの別形を持つ. 例えば C では,solve 手続きは solve(char *Goal, char *ParameterList, char **answers) のように書かれる.
デフォルトでは(つまり,空 ParameterList,あるいは,空パラメータリストを 想定する solve/1 手続きを呼ぶ),この手続きは Goal 手続きが Prolog システムによってローカルに実行されるかのようにふるまう. すなわち,失敗か成功になり,バックトラッキングを介して複数解が得られる かもしれない. これは,エージェントの機能の作成において,とても便利でありコードを書く 自然な方法である.
ゴールが solve リクエストを介して facilitator に送られるとき,facilitator は 接続している全エージェントの solvable リスト とゴールをマッチさせることによって,ゴールに応答を与えられるエージェントを探す. もし複数のエージェントが質問のゴールに解を返すことができると示したら, facilitator が使うデフォルトのふるまいは,リクエストを適切な全エージェントに 送り,全エージェントが応答するのを待ち,各エージェントからの解を集め, リクエストしているエージェントへ全ての解のセットを送ることである. このデフォルトのふるまいをオーバーライドしたいなら,おそらく個々のエージェント に固有の解を別々にリクエストしているエージェントに送り返させる場合, asynchronous パラメータを使いなさい.
しかし,このデフォルトの solve 手続きの使用は,Prolog の全パワーと表現を 与える一方でまた短所を持つ.例えば solve を呼んでいるエージェントの実行 は,成功か失敗のどちらにせよゴールが解かれるまで一時停止される. facilitator に掲示され,その後処理のために別のマシン上の他のエージェントに 発送されるとき,このゴールを解くのに所定の時間がかかるかもしれない. この期間,問い合わせを掲示しているエージェントは解が返るのを待ちながら 休止状態にある.しかし,以下で説明するように ParameterList を使った, 問い合わせの掲示の方法が他にいくつかある.
ある特別な場合,エージェントは cache パラメータを伴って solve/2 を使おうと決めるかもしれない.すなわち,
solve(Goal, [cache])これは,ゴールが一度計算されたら解はそのエージェントのデータベース中に ローカルに保存されることを除けば,solve(Goal, []) のようにふるまう. 次に同じゴールが再計算される時は,リモートエージェントにアクセスせずに 答えはすぐに見つかる. エージェントが自分のキャッシュの一貫性を維持する責任を持つため, この最適化は注意して使わなくてはならない.すなわち,解が時間で変わりやすい ものなら cache パラメータを省く方が安全である.
cache オプションは包括を正しく扱うべきである.ユーザが最初に solve(hotel('fairmont', Info),[cache]) リクエスト(一つの解を返し, これをキャッシュ中に保持)を出すとしよう.その後,ユーザは 問い合わせ solve(hotel(Any,Info),[cache]) を尋ね,キャッシュ中に保持 されている解だけを返すことは間違いかもしれない. むしろ,システムは,キャッシュがこの問い合わせを包括する情報を保持していないと 認識しなければならず,新しい問い合わせをネットワーク上に掲示しなければならない. 包括は現在 Prolog エージェントライブラリでは正しく扱われているが,他の プログラミング言語のエージェントライブラリではちゃんと処理されていない.
エージェントはプリミティブ clear_cache を使って 自分のキャッシュのクリアを選ぶことができる.
上述したように,デフォルトの solve/2, solve(Goal,[]) の使用は 応答するエージェントに全ての可能性のある解を Goal に生成させる. 呼ばれた手続きがどう書かれているかによって,ある場合は複数解を探すことは 望ましくない副作用を持つかもしれない.したがって,もし呼んでいるプログラムが 一つの解(あるいは限られた数の解)だけ必要だということを知っているならば, solution_limit(N) パラメータを使うことによって, 応答するプログラムが可能性のある解の全てを見つけることを防ぐことができる.
solve(Goal, [solution_limit(N)])ここで N は正の integer である. このパラメータは応答するプログラムに多くとも N 個の解を見つけることを 知らせる.
通常はゴールに対する解をリクエストする際,エージェントは どのリモートエージェントがそのゴールを解くかを知らないまま, リクエストを facilitator に掲示する -- facilitator が適切なエージェント(達) の位置を見通して探す責任を負う.しかし,ある特定のエージェントのアドレスを 知っている時(例えば,階層コンフィギュレーションの場合,他の facilitator にいる 特定のエージェントが解を試みるものであるようリクエストしたいかもしれない), 以下のようにアドレスをパラメータリスト中に示すことができる.
solve(Goal, [address(AgentAddress)])アドレスはエージェントの名前に続いて,階層中の facilitator のパスで指定される. 例えば database.navy.military では,'database' という名のエージェント に対応し,接続している facilitator の名前が 'navy',次につながっている facilitator 名が 'military',順にルート facilitator エージェントに接続されている.
エージェントはまた各自の唯一の内部的な ID (エージェントの facilitator に よって作られる)で参照されることができる. エージェントに対するこの内部的な ID は,can_solve(Goal, AgentList) ( これは,望ましい Goal にマッチする solvable リストを持つエージェント達の ID リストを返す) のようなプリミティブを介して得ることができる. あるいは,facilitator によって do_event() 中のパラメータ としてエージェントに送られる.
ときどきエージェントが,応答を期待せずに適切なエージェント全てにメッセージを 送りたいだけのことがある. 普通の solve 呼び出しはエージェントが問い合わせに一致(成功)か不一致(失敗) のどちらかで応答することを期待する. 問い合わせに対する応答を待つことや応答メッセージの生成を避けるためには, solve/2 のパラメータリスト中に broadcast 引数を使いなさい.
test パラメータを使って,あるローカルな facilitator 上で実行すべき テストを指定することができる.テストが局所的に成功した場合だけ その facilitator 上でゴールが解かれる.これは例えば, "interface エージェントにメッセージを送れ.ただし,ユーザの名前が 'phil'であるところの facilitator 上に限る." といったゴールを指定するとき に便利である.
solve(ui_inform('message'), [test(name('phil')), broadcast])(test パラメータは多重 facilitators のときのみ意味を持つ.)
エージェントが多重 facilitator の階層中に配置されている時, 探索は上に登りながら問い合わせに対する解が見つかるまで続く. 探索をあるレベル数に制限するために,solve/2 に level_limit パラメータ を使いなさい.例えば,
solve(Goal, [level_limit(0)])は直接の facilitator を使ってローカルにゴールを解こうとするだけである.
broadcast や asynchronous パラメータが指定されたときを除き,solve/2 手続きは問い合わせに 対する解が(肯定的な方法 - 成功 - ,あるいは否定的な方法 - 解が 得られない - のどちらかで)見つかるまでブロックする. この理由から,リモートエージェントが問い合わせを満足するために使う時間の 長さに制限を設けることが望ましいことがある.time_limit パラメータが 解く時間を与えられた秒数に制限するのに使われ, 解がこの時間内に見つからない場合は solve/2 は失敗する.
time_limit パラメータは次の例のように正の実数を囲う.
solve(Goal, [time_limit(10.5)])これは応答するエージェントが応答に 10.5 秒より長く割り当てられないことを示す. 応答するエージェントが割り当てられた時間内に応答しないとなれば,facilitator は solve/2 に対する呼び出しを失敗にする.
しかし,応答するエージェントが制限時間を過ぎた後に解を返す可能性もある. そうなった場合,解はまだリクエストしているエージェントに返される. つまり,solved イベントがリクエストしているエージェントに送られる. このことはリクエストしているエージェントがそのイベントを利用するために 必要なステップを取っていれば,遅くなった応答を利用することを可能にする. このやり方は 6.11 章 に述べられている.
また,エージェントライブラリはゴールを並列に実行する手段を提供する. いくつかのゴールは and_parallel パラメータを使ってもっと効率的に書ける. 例えば,
send_appointment(Person1, Person2) :-
solve(get_email(Person1,Email )),
solve(get_appointment(Person2, App),
send_mail(Person1, App).
を次のように書くことができる.
send_appointment(Person1, Person2) :-
solve([get_email(Person1,Email),
get_appointment(Person2, App)],
[and_parallel]),
send_mail(Person1, App).
後者の場合,二つの solvable が異なるエージェントにコントロールされると 仮定すると,両方が同時に実行される.両方とも終わると実行は通常通り続けられる. これは,プロセス 1 が完成するまで二番目のプロセスを始めるのを待つ 最初の例よりずっとよい. プロセス間の手続きに順序(例. プロセス 1 はプロセス 2 の前に実行しなければ ならない)を必要としない限り,変数バインディングは並列プロセス間で共有可能である.
同じ様な感じで,or_parallel パラメータは並列にいくつかのゴールを実行し, ゴールが 一つでも 成功するとすぐに続けられる. バックトラッキング時には,同時に起こっているプロセス各々の全ての解を返す.
print_info(Person) :-
solve ([ info_in_db1(Person, Info),
info_in_db2(Person, Info)],
[or_parallel]),
display(Info),
fail. % backtrack over all solutions
このゴールはデータベース #1, #2 両方の中で Person について見つかった 全ての情報を表示する.しかし,解は二つのデータベースから到着するので非同期に 出力されるだろう.変数 Info は and_parallel パラメータで行ったように並列ゴールをまたがってユニファイしないことに注意すること.
ブロッキングしない方法でリモートエージェントから情報を リクエストするために,asynchronous パラメータと共に solve() を 使いなさい.
solve(Goal, [asynchronous])このようにして,問い合わせが解かれている間,エージェントは計算を続けることができる. そのローカルエージェントにとってはより効率的であるが,プログラムが難しくなる.
非同期問い合わせの結果は,次の型のイベントを生成する.
solved(WhoSolved, Query, Params, SolutionList)WhoSolved パラメータは問い合わせを解いたエージェントの内部 ID,あるいは, 複数エージェントが SolutionList (複数エージェントが問い合わせに答える方法の 記述については solve の デフォルトのふるまい を参照) に寄与したなら ID のリストを含む.
非同期問い合わせの結果を処理する一つの方法は,do_event 中に solved/4 メッセージ に対するハンドラを書くことである.もう一つの方法はローカルイベント トリガ (問い合わせが終わった時に実行すべきアクションを 与える)によってイベントをトラップする.
次の例では,解くべき問い合わせを掲示し,その後ゴールの結果が返ったときに 非同期に表示するためにトリガを設定する. ゴールがリモートで実行されている間,エージェントは別のアクションを 実行することができる.
interface :-
get_query_from_user(PostableQuery),
solve(PostableQuery, [asynchronous]),
add_local_trigger(self, event, when, on_receive,
solved(_WhoSolved, PostableQuery, _Params, Solutions),
true,
display(Solutions)).
各エージェントは自分自身のローカルにもリモートにも,そして facilitator 上にも 他のエージェント上にもトリガを取り付けることができる. 現在四つのタイプのトリガが扱われている.
Event triggers: 出入りするイベント(メッセージ)はどれも監視され得る. 例えば,簡単なイベントトリガは次のように言うかもしれない.
" 問い合わせに対する解が facilitator によって返されるときはいつでも, ユーザに表示されるようにプレゼンテーション・マネージャに結果を送ってくれ."
Data triggers: データトリガは facilitator に書かれたグローバル情報の 状態を監視する.データトリガは例えば,
" リアクタの状態が不安定に なったときはいつも,シャットダウンシステムに警戒メッセージを送れ." と いったものである.
facilitator がプロセスに対する全てのグローバルデータを監視するため, データトリガは常に facilitator に取り付けられる. 個々のデータベースエージェントは, facilitator エージェントによって与えられた データトリガ機能をまねた彼ら自身のトリガメカニズムを提供すると思われる。
Test triggers: テストトリガは入ってくる各イベントが処理された後で, かつ,イベントポーリング中にタイムアウトが起きたときはいつでも監視される. テスト条件はローカルエージェント・メタインタプリタによって実行可能な ゴールを指定するだろう.テストトリガは,facilitator を通って来ない内部イベント を調べるのに便利である.例えば,mail エージェントは新しく来るメールを見張る だろうし,航空機データベースエージェントはどの飛行機が予定より遅れて到着する かを監視するだろう.
Alarm triggers: アラームトリガ(タイムトリガとしても知られる)は 時間の条件を監視する.アラームトリガは,一つの固定の時刻(例. 12月23日午後3時) に絶えず注意したり,周期的なトリガ(例. 今から正午までの3分毎)を 扱うことができる.
ローカルなトリガを取り付けるためのコマンドは,
add_local_trigger(self, Kind, Type, OpMask, Template, Condition, Action)リモートのトリガを取り付けるためには次のどちらかのコマンドを使う.
add_trigger(KS, Kind, Type, OpMask, Template, Condition, Action)または,
add_trigger(Kind, Type, OpMask, Template, Condition, Action)
add_local_trigger/7 はテストトリガやイベントトリガをそれを 呼んだエージェント上にローカルに取り付ける.add_trigger/7 は トリガを指定のエージェント (KS) 上に取り付ける. データトリガやイベントトリガの場合,add_trigger/6 はトリガ を facilitator 上に取り付ける.テストトリガの場合,add_trigger/6 は与えられた Condition を解ける全てのエージェント (彼らの solvable 宣言によって示される)上に トリガを取り付ける.アラームトリガの場合,add_trigger/6 は 現在 facilitator に接続されている alarm エージェント上に, 指定されたトリガを取り付ける.
上述したように,データトリガは facilitator 上にだけ取り付けられる. すなわち,add_local_trigger/7 や add_trigger/7 を使って データトリガを取り付けようとするのは間違いである. 同様に,アラームトリガは alarm エージェント上にのみ取り付けられる. もっとも特定の alarm エージェント上にトリガを取り付けようと指定するために add_trigger/7 を使うかもしれないが.
add_local_trigger/7 の最初の引数 (self) と add_trigger/7 の最初の引数 (KS) はエージェント(知識ソース)を 指すが,意味が異なる.add_local_trigger の場合は,この引数は どのエージェントがトリガをリクエストしたかを示す.add_trigger/7 の 場合は,最初の引数はトリガが取り付けられるべきエージェントを示す.
Kind パラメータは,上述のトリガの種類('event', 'data', 'test', 'alarm') のどれかを指定する. Type パラメータは,トリガの持続期間を指定する.'if' および 'when' トリガ は一度だけ実行する(両者は英語による見解の違いだけ:'if' はイベントが 起こるかもしれない(might),一方 'when' はイベントが起こる(will) と述べている).一方,'whenever' トリガは発火後もアクティブのままであり, 適合するイベントを監視し続ける. 注: Type パラメータは 'alarm' トリガによっては使われない(アラーム トリガは循環の観念を組み込みで持つため).
OpMask パラメータはイベントトリガとデータトリガにだけ使用され, どんな状況下でそのトリガの実行を考えることになっているかを指定する. イベントトリガでは,OpMask は 'on_send' か 'on_receive' のどちらか, あるいはこの両方を含むリストや変数であろう. 変数の使用は,'on_send' と 'on_receive' の両方を含むリストと同じ効果を持ち, トリガが両方の状態で考えられるべきであることを意味するだけである. データトリガに対しては,OpMask は 'on_write', 'on_retract', 'on_replace', 'on_write_replace' のいずれか,あるいはこれらの組み合わせを含む リストや変数であろう. 'on_write', 'on_retract', 'on_replace', 'on_write_replace' は,エージェント ライブラリの手続き write_bb, retract_bb, replace_bb, write_replace_bb の使用と対応している.
Template パラメータもイベントトリガとデータトリガにだけ使われ, ユニフィケーションによって,どのイベントやデータアイテムがトリガを発火させるか をコントロールする.イベントトリガの場合,Template は トリガが取り付けられているエージェントによって送られるか受け取ることに なっているイベントと同じ型を持つべきである. データトリガの場合, Template は data(Item, Value) の型を 持つべきである.
テストトリガやアラームトリガでは,OpMask や Template は 使われないので,値は何でもいいだろう.
Condition パラメータは,トリガが発火するために成功しなければならない テストを含んでいる. イベントトリガやデータトリガの場合,Condition は任意のテスト表現 を含んでいて,これは OpMask および Template パラメータの適合が 成功したのに加えて評価されるものである. 次のような例になる.
add_trigger(data, whenever, [on_replace], position(car1, X, Y), (solve(position(target, X2,Y2)),solve(distance(X,Y,X2,Y2,D)), D < 100), perform(some_action)).
この例において,car1 の位置が変わる時にトリガの条件は, ターゲットに対する距離が 100 より小さくなったらトリガを発火すべきと指定している.
見てきたように Condition パラメータは,イベントトリガとデータトリガ に対して任意の付加的なテキスト表現を与える. しかし,アラームトリガとテストトリガでは OpMask と Template パラメータが使われないため,Condition パラメータがトリガを発火するか どうかを決める唯一のものである.
アラームトリガは alarm エージェントによって管理され, alarm エージェントが現在システムに接続されているときのみアクティブである. 他のトリガタイプは全て facilitator とエージェントライブラリによって実装されて いて,常に利用できる.
Action パラメータはトリガの本体である.つまり,トリガが発火したときに 何を実行することになっているかである. これは ICL 表現の型であり,いったんトリガの条件が合えば 実行するために facilitator に掲示される.
上述したとおり,トリガは add_trigger と add_local_trigger エージェントライブラリのルーチンを使ってプログラミング的に取り付けられる. トリガ(特にテストトリガとアラームトリガ)を付け加える他の優れた方法は, 自然言語を介してである.ここに自然言語トリガ表現とそのプログラムの等価物の 例をいくつか示す.
add_trigger(test, when, _OpMask, _Template, arrives(mail, args(for('Adam Cheyer'),about(security))),notify('Adam Cheyer')).
add_trigger(alarm, when, _OpMask, _Template, time_expr(date(96,4,13,13,29,19),date(96,4,13,13,29,19),recurrent(0,0)), call('David Martin')) (will fire on May 13, 1996 at 1:29:19 pm)
add_trigger(alarm, when, _OpMask, _Template, time_expr(date(96,4,13,13,51,19),date(96,11,25,0,0,0),recurrent(3,minute)), check(mail))
write_bb, read_bb, retract_bb, write_replace_bb, replace_bb 手続きはこのグローバルデータを読み書きするのに使われる.
read_bb を除き,これらのどの呼び出しも facilitator にその アクティブなデータトリガを調べさせ,このうち一つ以上が発火する結果になる かもしれない.
クライアントエージェントは次の呼び出しによって,親 facilitator 上にデータを掲示するだろう.
write_bb(Item, Data)これは facilitator にデータ要素 <Item, Data>(正確には facilitator に送られた型)を記録させる. Item と Data の両方とも制約はなく,ある Item の値に対して 記録された Data の値は複数かもしれない.
例として,他の様々なエージェントに対して自然言語処理サービスを提供する DCG-NL エージェント (Definite Clause Grammar Natural Language agent)は, これら他のエージェントが応答するのに用意しているボキャブラリ (各スピーチ単語の部分の指示と,その単語の使用の結果生じるべき logical form の 指示を伴う)を掲示することを期待する.オフィスアシスタント・システムにおいて, 多くのエージェントがこれらのサービスを利用する. 例えば database エージェントは,名詞 'boss' を掲示し,boss の ``意味''が 概念 'manager' であることを示すために,次の呼び出しを使う.
write_bb(noun, [manager, [atom(boss)]])
どのクライアントエージェントも次の呼び出しによって,親 facilitator からデータを 読み出すかもしれない.
read_bb(Item, Data)この呼び出しはバックトラッキングを介して,Item と Data 両方で ユニファイする全てのストアされたデータアイテムを返す.
replace_bb(Item, OldValue, NewValue)
これは facilitator にまず Item と OldValue 両方でユニファイした 全てのデータ要素を削除させ,次にデータ要素<Item, NewValue> を 記録させる.
replace_bb の呼び出しは,facilitator にアクティブなデータトリガを
調べさせ,このうち一つ以上のトリガを発火させる結果になるかもしれない.
新しいエージェントを作成するプロセスは次のステップを含む.
* timeout(TimeoutDelay) 宣言は, イベントキューをポーリングする時に使われるタイムアウト遅れを取り付ける.
* app_init/0 手続きは, アプリケーションにエージェント固有データを初期化させるフックを提供する.
* app_done/0 手続きは, エージェントが終了する時に自分自身の後のクリーンアップをさせるフックを提供する.
* idle/0 手続きを使ってエージェントは, キューにイベントがなくてタイムアウトが起こった時に起動すべきアクションを 定義するだろう.
go(Class, KSName, FacilitatorName)
適切な facilitator に接続してその他の必須な初期化を行なった後で, go/3 手続きはそのエージェントに対するイベントループを始める.
新しい OAA エージェントを作成するプロセスを部分的に自動化する方法について は Agent Development Tools User Manual を参照のこと.
しばしばドメインエージェントはある既存のアプリケーション(データベースや カレンダプログラムなど)に対する OAA の "wrapeer" として働く. エージェント "wrapper" 中に既存のアプリケーションを囲むために 様々な方法が使われるだろう.
その他の場合(calendar エージェントや mail エージェントなど), エージェントがオリジナルアプリケーションによって操作されるデータファイルを 見るだけで十分かもしれず,直接の統合は必要ない.
Prolog で書かれていないエージェントは入ってくる ICL リクエスト(Prolog 文法を使用)をパースする必要がある. こうした各言語用エージェントライブラリは,入ってくるリクエストを 容易に適切なコンポネントに分解するための ICL パース手続きのセットを含んでいる.
ここでは,OAA 内のエージェントをプログラミングするためのいくつかの推奨を示す.
本開発者用ガイドのほとんどの章は個々のエージェントの設計と構造, あるいは,ある OS 中のエージェント間で生じるインタラクションと関連してきた. ここでは,一つのシステムとして一緒に働くように選択 and/or 構築された エージェントの集合が与えられたときに,システムを動作させるために またその操作が続くことを保証するために何が起こらなければならないか について考える.
以下に,OAA の実行マネージャ Start-It を利用する方法について述べる. Start-It はシステムの起動や監視に伴う努力を最少にするよう設計された. しかし,特にデバッグ時は,エージェント(やエージェント達のシステム)を ``マニュアル''で(すなわちエージェントの実行プラットフォームのコマンド ラインから)スタートしたりストップできることが重要なときがある.
個々のエージェントをスタートする際,一般に二つだけ覚えておく必要がある. 一つは,エージェントは facilitator の正しいホストとポートを得なければならない. これは,通常 セットアップファイル から得られる. 二つ目は,エージェントは facilitator への接続をリクエストしなければならない. これは,通常 go/3 を呼び出すことによって達成される.
エージェントライブラリは各エージェントにコールバック app_init (go/3 の呼び出しの結果起こる) を提供する.エージェントを実装する時,どんなエージェント固有の初期化も app_init 手続きによって処理されるべきである. これはエージェントにとってスタートアップ手続きを可能な限り簡単にするものである.
システム全体のスタートアップにおいて,一般に覚えておかなければならないことは 唯一,各 facilitator をクライアントエージェントの前にスタートアップ しておかなければならないことである.より正確には,facilitator はクライアント が接続しようとする前に,割り当てられたポートを listen していなければならない.
Figure 3: Start-It's user interface
startit <コンフィギュレーション・ファイル>例えば,Figure 3 に示す Start-It の例は以下を使って起動された.
startit oaa.config
Start-It 自身エージェントなので,起動時に facilitator に接続しよう
とする.従って,Start-It の起動前に facilitator が
該当 セットアップファイル に現在示されている
ホストとポートで動いていることを確かめなければならない.
Start-It の起動が他のエージェントのスタートアップを直ちに 引き起こすことはない.むしろ,このインタフェースは他のエージェントを 起動させるコントロールを提供する.
Start-It は,オプションでコマンドライン引数 -no_expect を取る. もし,このオプションがスタートアップで与えられたとき,Start-It は `expect' プログラム(これは TCL があなたのシステムにインストールされていること を要する)を利用しないようにする. expect は Start-It のいくつか小さい問題を解決する.
図に示したように,Start-It は常に Display コントロールと Start ラベル 付きの大きなボタンを提供する.Display コントロールは ウィンドウの表示に使用するマシンを示す(そのウィンドウ中で 様々なエージェントが起動される.エージェントが動いているマシンと同じ である必要はない).小さいボックスをクリックすると選択リストが現れ,これは 選択するか違うディスプレイをタイプするのに使われる. デフォルト値は``Local''で,Start-It が動いているマシンという意味である.
Start ボタンは Start-It にコンフィギュレーション・ファイル中 に列挙された全てのエージェントを起動させる.
Display と Start コントロールの下に個々のエージェントに対する コントロールのグループがある.コンフィギュレーション・ファイル で指定された各エージェントに対して,Start-It はステータスボックス と基本的なメニューとホストセレクタを提供する.
Start-It がエージェントの起動を頼まれた時,エージェントの ステータスボックスは黄色(エージェントは初期化されているけど統合される用意が できていない)か緑色(エージェントは facilitator に接続され いつでも統合できる)か赤色(エージェントが死んだか普通に機能していない) のどれかになる.
エージェントの基本的なメニューは,エージェント名でラベル付けされた 大きなボタン(Figure 3 の``Database''というラベル のボタンなど)をクリックすることによってアクセスされる. クリックすると,Hide, Show Options, Start, Kill の四つのコマンドを含んだ メニューが表示される.
Host コントロールはエージェントを実行すべきマシンを指定するのに使われる. 小さいボックスをクリックすると選択リストが現われ,これは 選択するか違うホストをタイプするのに使われる.デフォルト値は``Local''で, Start-It が動いているマシンという意味である.
#------------------------------------------------------ # Employee Database Agent #------------------------------------------------------ appname Database oaaname employee_db.root appdir /home/zuma1/OAA/demo.bin appline ./d2 end #------------------------------------------------------ # Calendar Agent #------------------------------------------------------ appname Calendar oaaname calendar.root appdir /home/zuma1/OAA/demo.bin preline setenv ENV_VAR something appline ./c end
ここに示した各エージェントに対する仕様 `appname', `oaaname', `appdir', `appline' は,各エージェントに対して示されなければならない. これらは次のように使われる.
コンフィギュレーション・ファイル中に,様々なエージェントに対する 付加的なパラメータを選んで使うためにエージェント固有のメニューを 指定することも可能である.今は本開発者用ガイド中に 記述されていない.