四、案例研究
以下介紹幾個主體的例子,以及如何在eXAT中實現。
(1)訊息模式比對
這是第一個例子,主體等待特定訊息的到來,然後做些事情。具體地,從主體“Alice”發來LISP寫的“inform”訊息,我們就令主體“Bob”開始action行動。
-module (agent_bob).
-export ([action/3, start/0]).
action ([inform, alice, Receiver, Ontology, lisp, Content, Slots], State, Agent) ->
% ’inform’ from Alice ... do the action
start () ->
agent:new (bob, receiver, {agent_bob, action}).
這段代碼展示了主體Bob的eXAT實現:
函數start/0調用agent:new/4啟動主體Bob。參數意思是:bob:主體名字,receiver:作為行為骨架的FSM模板的名字,agent_bob:實現FSM模板規定行為的函數名字。這裡的receiver是eXAT內建的,它只有一個狀態,收到訊息後它引發行動並返回狀態。
訊息的模式比對,通過Erlang的匹配機制實現。訊息採用ACL格式:
[ Performative-name, Sender, Receiver,Ontology, Language, Content, Slots ]
上面的那段代碼中,函數action的第一個參數,就是以列表表示的訊息模式。
再來看看用JADE寫的相同樣本:
import jade.core.*;
import jade.core.behaviours.*;
import jade.lang.acl.*;
public class ReceiverAgent extends Agent
{
public void setup ()
{
AID aid[] = new AID[1];
aid [0] = new AID ("Alice", false);
addBehaviour (new ReceiverBehaviour (this, -1, MessageTemplate.and (
MessageTemplate.MatchPerformative (ACLMessage.INFORM),
MessageTemplate.and ( MessageTemplate.MatchReceiver (aid),
MessageTemplate.MatchLanguage ("lisp"))
)
)
{
public void action () {
//.. do something
}
});
}
}
看上去,Erlang代碼的可讀性明顯高於Java的。
(2)多種訊息的模式比對
-module (agent bob).
-export ([action/3, start/0]).
action ([inform, alice, Receiver, Ontology, lisp, Content, Slots], State, Agent) ->
% ’inform’ from alice: ... do the action
action ([inform, Sender, Receiver, Ontology, lisp, Content, Slots], State, Agent) ->
% ’inform’ from another agent ... do the action
action (M, State, Agent) ->
acl:reply (M, notunderstood).
start () ->
agent:new (bob, receiver, {agent bob, action}).
這是第二個樣本,它為第一個樣本增加了2個action子句。當收到“Alice”以外的其他主體發來的“inform”時,由第二子句處理;當收到的資訊類型不明時,由第三子句予以回複,告知對方,其所發資訊“我讀不懂”。
這個樣本說明,簡單地增加函數子句,就能輕鬆應對各種情況。
(3)使用者定義的模板
這是最後一個樣本,用以展示開發使用者定義的FSM的模板。該模板的事件機制,與ERES引擎相關聯。
假設有個主體的行為機制是FSM,1所示。
圖1
該主體等待在ERES引擎“goods” (state init) 中,事實 {price, computer,X} 的斷言出現,其中 X < 1000;然後,該主體向主體
Seller (state s1)發出“request”請求訊息,並一直等待回覆。“回覆”是個“inform”訊息(final state stop)。主體收到回覆後,停止運行。
為了實現這個主體,我們寫個叫做buyeragent的模組,其中有FSM模板。該模組要遵守eXAT引擎規定的格式,定義狀態轉換機制的多子句函數 templatw/4,並且,template/4 必須返回的,是函數 event/2 處理的與“狀態”相關的事件映射。template 形式如下:
template ( Event, State, Agent, AgentFun )
它返回的是FSM的下個狀態。參數Agent是主體的名字,AgentFun是實現主體行為的多子句函數。參數Event是函數events返回的元組列表,列表中的元組的格式是{狀態名字,事件列表}。
下面是FSM模板的代碼:
-module (buyeragent).
-export ([template/4, events/2]).
template (Fact, init, Agent, AgentFun) ->
apply ( AgentFun, [Fact, init, Agent] ),
s1;
template ([inform | T] = M, s1, Agent, AgentFun) ->
apply ( AgentFun, [M, s1, Agent] ),
stop.
events (Agent, AgentFun) ->
[ {init, [ {eres, goods, {price, computer, fun (X) -> X < 1000 end }} ] },
{s1, [ {acl} ] } ].
狀態init對應的事件,是在ERES引擎“goods”中宣布的事實,格式為元組:
{eres, goods, {price, computer, fun (X) -> X < 1000 end }}
其中的lambda函數,即“fun”語句表示的匿名函數,要求X的值必須小於1000。如果存在著這一事實,template/4的第一子句得以匹配,它通過函數 apply 調用主體的行為函數AgentFun,並且,返回 s1 作為 FSM 的新狀態。
在新狀態下,對應的事件是收到ACL訊息“inform”。template/4 的第二子句處理此事後,返回狀態“stop”,結束主體的運行。
這個FSM模板,可用於前面的2個樣本,完整實現圖1所示的行為機制。使用這個模板的主體,必須實現“request”訊息的發送,和對接收到的“inform”訊息進行解釋。
最後,應該承認,eXAT目前只能實現幾種簡單的主體agent,沒有誰用它實現過完整的多主體應用程式。
因此,本文說eXAT是Erlang面向Agent編程的利器,有些誇張。