Upload
barnard-taylor
View
221
Download
0
Embed Size (px)
DESCRIPTION
SECTION 1: SETUP
Citation preview
AFAS/ACRE Examples
Introduction• This guide contains practical examples of how to program
protocols in AF-AgentSpeak.– It covers programming both with and without ACRE.
• Each section of the document outlines an example protocol and discusses its implementation.
• Section 5 introduces the Directory Facilitator Service, which you will need to use to locate key agents.
SECTION 1: SETUP
Setting Up• General Instructions:
http://www.agentfactory.com/index.php/ACRE_User_Guide
• Step 1: Download and install the Graphviz application from http://www.graphviz.org
• Step 2: Download the following JAR files and store them in your Eclipse project root:• MAS-ACRE.jar (http://www.agentfactory.com/~daithi/latest/)• AF-ACRE.jar (http://www.agentfactory.com/~daithi/latest/)• Log4J.jar (http://logging.apache.org/log4j/)
NOTES• There are updates to both ACRE & AFSE – please download
them.– Relevant jar files / update center are available via the Fudan FTP
Server.
• These programs reflect an improved version of ACRE that includes functionality not present in the previous version.
• You can use these slides as notes in the practical exam.– In most cases, you will only be implementing the initiator role in the
protocols (the participant implementation will be given)
SECTION 2: FIPA-REQUEST
Protocol Overview
• Usage: Requesting an activity / service be performed
Example• For this example, we study how to implement a protocol and
develop two simple task tasks:– Drinking beer task: prints “Mmmm tasty beer”– Time task: returns the time in the form time(?h, ?m, ?s)
• The example consists of two agents:– requester.aspeak: this agent is the participant and performs the tasks– requestee.aspeak: this agent is the initiator and requests that the tasks be
performed.
• First I will discuss requester implementation and then the requestee implementation.– Both programs will be preceded by explanations.
Non ACRE: Requester• A protocol can be viewed as having a “main” flow together
with a number of “exceptional” flows (a flow is a sequence of messages).– For the request protocol, the main flow is where the participant
agrees to perform the task, performs the task successfully, and tells the initiator that the task has been performed.
– The first exceptional flow relates to the case where the participant refuses the request.
– The second exceptional flow relates to the case where the requested task fails.
• Let’s look at each flow individually…
Non-ACRE: Requester (MAIN)• For the request protocol, the main flow can be encoded through
a single rule:
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {!performTask(?initiator, ?task)} recover {.send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
• When the agent receives the “request” message for ?task, if it believes that is canDo(?task), then the main flow is followed (actions in italics).
Non-ACRE: Requester (MAIN)• For the request protocol, the main flow can be encoded
through a single rule:
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {!performTask(?initiator, ?task)} recover {.send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
• First, it adopts a belief that it is performing the task, and then it sends a message to the initiator agreeing to perform the task.
Non-ACRE: Requester (MAIN)• For the request protocol, the main flow can be encoded
through a single rule:
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {
!performTask(?initiator, ?task)} recover {
.send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
• Now it performs the task by raising the !performTask(…) goal.
Non-ACRE: Requester (MAIN)• Default behaviour for performTask(…):
+!performTask(?agentID, ?task) : true <-.fail;
– If this rule is triggered then there is no implementation of the task, so the agent views the task as having failed.
• Drink beer task:
+!performTask(?agentID, drink(beer)) : name(?name) <-.println("[" + ?name + "] Mmmm tasty beer"),.send(inform, ?agentID, done(drink(beer)));
– The agent prints out “[<name>] Mmmm tasty beer” and informs the initiator that the task is done.
– This rule must be written before the default rule (see full code)
Non-ACRE: Requester (MAIN)• For the request protocol, the main flow can be encoded
through a single rule:
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {
!performTask(?initiator, ?task)} recover {
.send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
• Finally, the agent drops the performingTask(…) belief.
Non-ACRE: Requester (FAILURE)• For the request protocol, the main flow can be encoded through a
single rule:
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {!performTask(?initiator, ?task)} recover {.send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
• NOTE: the failure exceptional flow is implemented here by the try {…} recover {…} operator.– if the agent fails to perform the task, then the recovery plan involves the agent
sending a failure message to the initiator.
Non-ACRE: Requester (REFUSE)• For the request protocol, the refuse exceptional flow can be
encoded through a single rule:
+message(request, ?initiator, ?task) : ~canDo(?task) <-.send(refuse, ? initiator, ?task);
• Here, the participant refuses to perform a requested task if it does not have the belief canDo(?task).
Non-ACRE: Requester (CANCEL)• In our discussion of the basic protocol, we did not consider how to
cancel a request.– Doing this requires that we make use of a previously un-described feature of
AF-AgentSpeak that is not part of the basic AgentSpeak language.– Specifically, we associate a maintenance condition (see code in bold below)
with the rule that implements each task. If this condition becomes false, the agent causes the associated intention to fail.
+!performTask(?initiator, drink(beer)) : name(?name) <-[performingTask(?initiator,drink(beer))].println("[" + ?name + "] Mmmm tasty beer"),.send(inform, ?initiator, done(drink(beer)));
• For the performTask(…) rules, the maintenance condition is that you believe that you are performing the task.
Non-ACRE: Requester (CANCEL)• Now, we can add a rule for handling cancellation requests:
+message(cancel, ?initiator, protocol(request, ?task)) : performingTask(?initiator, ?task) <--performingTask(?initiator, ?task);
– This rule basically has the effect of removing the performingTask(…) belief whenever the participant receives a request to cancel a task that it is currently performing.
– The side effect of removing this belief is that the maintenance condition on the !performTask(…) rule becomes false, causing the agent to fail the intention.
– The last thing we need to do is to modify our MAIN flow to cater for the task that the !performTask(…) rule has been intentionally failed…
Non-ACRE: Requester (CANCEL)• The updated rule is:
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {
!performTask(?initiator, ?task)} recover {
if (performingTask(?initiator, ?task)) {.send(failure, ?initiator, ?task)
}}-performingTask(?initiator, ?task);
• The full code is presented on the next slide.
Non ACRE: Requester (Code)#agent requester
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {
!performTask(?initiator, ?task)} recover {
if (performingTask(?initiator, ?task)) .send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
+message(request, ?initiator, ?task) : ~canDo(?task) <-.send(refuse, ?initiator, ?task);
+message(cancel, ?initiator, protocol(request, ?task)) : performingTask(?initiator, ?task) <--performingTask(?initiator, ?task);
canDo(drink(beer));
+!performTask(?initiator, drink(beer)) : name(?name) <- [performingTask(?initiator, drink(beer))].println("[" + ?name + "] Mmmm tasty beer"),.send(inform, ?initiator, done(drink(beer)));
+!performTask(?initiator, ?task) : true <-.fail;
Non-ACRE: Requestee• For the requestee, the protocol flows are not so complicated:
– Send a request for the participant to drink beer.– If this request is completed successfully, send a request for the participant
to drink wine.– Notice that the agent identifier uses the address: “local:localhost”.
• This can now be used for agents on the same platform
• The full code for the requestee is:#agent requestee
+initialized : true <-.send(request, agentID(requester, addresses("local:localhost")), drink(beer));
+message(inform, ?agentID, done(drink(beer))) : true <-
.send(request, agentID(requester, addresses("local:localhost")), drink(wine));
Non-ACRE: Deploymentpublic class Main {
public static class DebugConfig extends AgentSpeakDebugConfiguration { public DebugConfig() { super(“afas", null); }
public void configure() { super.configure();
addAgent("requester", “acre/requester.aspeak"); addAgent("requestee", “acre/requestee.aspeak");
}}
public static void main(String[] args) { new DebugConfig().configure();}
}
Non-ACRE Summary• The participant code has been designed to be extensible.
– To add another task, all you need to do is add a canDo(…) belief, and implement a !performTask(…) rule.
– Example: time
canDo(tell_time);
+!performTask(?initiator, tell_time) : name(?name) & time(?h, ?m, ?s) <- [performingTask(?initiator,
tell_time)].send(inform, ?initiator, result(tell_time, time(?h, ?m, ?s)));
– NOTE: The inform message this case is result(?task, ?value) which corresponds to the inform-result message in the protocol.
ACRE: Adaptation• To implement this protocol in ACRE, the main differences lie in
the protocol rules:+message(request, ?initiator, ?task) : canDo(?task) <-
+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {!performTask(?initiator, ?task)} recover {if (performingTask(?initiator, ?task)) .send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
+message(request, ?initiator, ?task) : ~canDo(?task) <-.send(refuse, ?initiator, ?task);
+message(cancel, ?initiator, protocol(request, ?task)) : performingTask(?initiator, ?task) <--performingTask(?initiator, ?task);
ACRE: Adaptation• To start with, acre requires installation and initialization, so
we need the following lines of code:
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <- acre.init;
• Next, we need to modify any rule that uses a message event as a trigger or which uses the .send(…) action…
ACRE: Adaptation• The first rule to consider is the main flow rule:
+message(request, ?initiator, ?task) : canDo(?task) <-+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {!performTask(?initiator, ?task)} recover {if (performingTask(?initiator, ?task)) .send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
• To adapt this rule to ACRE:– we replace the triggering event with a conversationAdvanced(…) event– We replace every .send(…) action with an acre.advance(…) action
ACRE: Adaptation• So, the ACRE equivalent rule becomes:
+conversationAdvanced(?cid,requested,?index) :conversationHistory(?cid, ?index, received, request, ?task) &conversationProtocolName(?cid,request) & canDo(?task) <-acre.advance(?cid, agree, ?task),try {!performTask(?cid, ?task)} recover {acre.advance(?cid, failure, ?task)};
• Key Points:– The performingTask(…) belief is no longer required– We use the conversationHistory(…) belief to access the last message sent
(this is because event processing can be delayed depending on the size of the agents event queue)
– This also accounts for the failure exceptional flow
ACRE: Adaptation• In the non-ACRE implementation, the refuse exceptional flow
is realised through the following rule:
+message(request, ?initiator, ?task) : ~canDo(?task) <-.send(refuse, ?initiator, ?task);
• The ACRE equivalent is:+conversationAdvanced(?cid,requested,?index) :
conversationHistory(?cid, ?index, received, request, ?task) &conversationProtocolName(?cid,request) & ~canDo(?task) <-
acre.advance(?cid, refuse, ?task);
ACRE: Adaptation• The cancel exceptional flow is supported through a standardised
rule that is protocol independent:
+conversationCancelRequest(?cid) : true <-acre.confirmCancel(?cid),acre.forget(?cid);
• The last place where the code must be adapted is in the !performTask(…) rules.– These rules implement the requested tasks and include the final inform-
done and inform-result messages.– The main difference is the use of acre.advance(…) instead of .send(…)– This has been supported through the modification of the parameters passed
to !performTask(…) which includes the conversation identifier instead of the agent identifier.
ACRE: Adaptation• The modified task rules are:
+!performTask(?cid, drink(beer)) : name(?name) <-[conversationHistory(?cid, 1, received, request, drink(beer))].println("[" + ?name + "] Mmmm tasty beer"),acre.advance(?cid, inform, done(drink(beer)));
+!performTask(?cid, tell_time) : name(?name) & time(?h, ?m, ?s) <-[conversationHistory(?cid, 1, received, request, tell_time)]acre.advance(?cid, inform, result(tell_time, time(?h, ?m, ?s)));
+!performTask(?cid, ?task) : true <-.fail;
• Key Point:– The maintenance condition changes to conversationHistory(…,1,…) where 1 is
the index of the first message in the conversation.– The tell_time task is omitted from the program in the next slide.
ACRE: Adaptation#agent requester
module acre -> is.lill.acre.agent.module.ACREModule;+initialized : true <- acre.init;
+conversationAdvanced(?cid,requested,?index) : conversationProtocolName(?cid,request) & canDo(?task) &conversationHistory(?cid, ?index, received, request, ?task) <-acre.advance(?cid, agree, ?task),try {!performTask(?initiator, ?task)} recover {acre.advance(?cid, failure,? task)};
+conversationAdvanced(?cid,requested,?index) : conversationProtocolName(?cid,request) & ~canDo(?task) &conversationHistory(?cid, ?index, received, request, ?task) <-acre.advance(?cid, refuse, ?task);
+conversationCancelRequest(?cid) : true <- acre.confirmCancel(?cid), acre.forget(?cid);
canDo(drink(beer));
+!performTask(?cid, drink(beer)) : name(?name) <- [conversationHistory(?cid, 1, received,request,tell_time)].println("[" + ?name + "] Mmmm tasty beer"),
acre.advance(?cid, inform, done(drink(beer)));
+!performTask(?cid, ?task) : true <- .fail;
ACRE: Adaptation• The code for the requestee is:
#agent requestee
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <-acre.addRepository("file:///C:/Users/rem/acre"), acre.init,acre.addContact(agentID(requester,addresses("local:localhost"))),acre.start(org.fipa_request_1.3,requester,request,drink(beer));
+conversationAdvanced(?cid,refused,?index) :conversationProtocolName(?cid,request) <-acre.forget(?cid);
+conversationAdvanced(?cid,done,?index) : conversationProtocolName(?cid,request) <-acre.start(org.fipa_request_1.3,requester,request,tell_time);
+conversationMessage(?cid,inform,result(tell_time, time(?h, ?m, ?s))) : true <-.println("The time is: "+?h + ":"+?m+":"+?s);
ACRE: Deploymentpublic class Main {
public static class ACREDebugConfig extends AgentSpeakDebugConfiguration { public ACREDebugConfig() { super("acre", null); }
public void configure() { addServiceInspectorFactory( new ProtocolManagerServiceInspectorFactory() );
super.configure(); addPlatformService(
ProtocolManagerPlatformService.class,ProtocolManagerPlatformService.NAME );
addAgent("requester", “acre/requester.aspeak"); addAgent("requestee", “acre/requestee.aspeak");
}}
public static void main(String[] args) { new ACREDebugConfig().configure();}
}
SECTION 3: FIPA-SUBSCRIBE
Protocol Overview
• Usage: Requesting updates on some topic
Example• This example will implement a clock service, which sends
subscribers a message every second.– The message contents will be the number of seconds since the service
was started.
• As with the FIPA-REQUEST example, this example involves two agents:– Subscriber.aspeak: the participant (implements the service)– Subscribee.aspeak: the initiator (uses the service) and cancels the
subscription once the clock tick reaches 3.
• The discussion assumes you have read the previous example
Non ACRE: Subscriber• The flows in the subscribe protocol are:
– Main flow: participant agrees to the subscription request and sends updates until the subscription is cancelled.
– The first exceptional flow relates to the case where the participant refuses the subscription request.
– The second exceptional flow relates to the case where the subscription service fails.
• NOTE: The service is implemented separately to the protocol, but uses knowledge of who the agent has accepted.– We discuss the service implementation first
Non ACRE: Subscriber (Service)• The clock service involves a long-running intention which sends
messages to subscribed agents every second.+topic(clock) : true <-?i = 0,while(true) {durative(.sleep(1000)),?aids = list[?aid | subscription(?aid, clock) ].send(inform, ?aids, value(clock, ?i))?i = ?i + 1};
• Key Points:– A subscription(…) belief is used to keep track of who is subscribed.– The list[…] operator creates a new list whose values are the agent Ids from the
subscription beliefs.– The .send(…) action accepts a list of agent ids!
Non ACRE: Subscriber (Service)• The service is started in the initialized rule by adopting the
belief topic(clock).+initialized : true <-
+topic(clock);
• Key Points:– The topic(?topic) beliefs are used to keep track of the topics that
initiators can request subscriptions for.
Non ACRE: Subscriber (MAIN)• The main flow is triggered by the receipt of a message from
an initiator requesting a topic that the agent provides:+message(subscribe, ?agentID, ?topic) : topic(?topic) <-
.send(agree, ?agentID, ?topic),+subscription(?agentID, ?topic);
• Key Points:– The agent sends the agree message to the initiator and adopts the
subscription(…) belief, adding the agent to the service.– The agent only does this if it has a topic(…) belief relating to the
requested subscription.
Non ACRE: Subscriber (MAIN)• The main flow only stops on receipt of a cancel request by the
initiator:+message(cancel, ?agentID, protocol(subscribe, ?topic)) :
subscription(?agentID, ?topic) <--subscription(?agentID, ?topic);
• Key Points:– This rule is similar to the FIPA-REQUEST cancellation rule.– The rule removes the subscription(…) belief, resulting in the service no
longer adding the agents id to the list of subscribed agent ids.
Non ACRE: Subscriber (REFUSE)• The refuse exceptional flow is triggered by the receipt of a
message from an initiator requesting a topic that the agent does not provide:+message(subscribe, ?agentID, ?topic) : ~topic(?topic) <-
.send(refuse, ?agentID, ?topic);
• Key Points:– The agent does this if it does not have a topic(…) belief relating to the
requested subscription.
Non-ACRE: Subscriber (Code)#agent subscriber
+initialized : true <-+topic(clock);
+topic(clock) : true <-?i = 0,while(true) {durative(.sleep(1000)),?aids = list[?aid | subscription(?aid, clock) ],if (?aids != []) .send(inform, ?aids, value(clock, ?i)),?i = ?i + 1};
+message(subscribe, ?agentID, ?topic) : topic(?topic) <-
.send(agree, ?agentID, ?topic),+subscription(?agentID, ?topic);
+message(subscribe, ?agentID, ?topic) : ~topic(?topic) <-.send(refuse, ?agentID, ?topic);
+message(cancel, ?agentID, protocol(subscribe, ?topic)) : subscription(?agentID, ?topic) <--subscription(?agentID, ?topic);
Non-ACRE: Subscribee• The initiator is more complex to implement than for the FIPA-
Request protocol.– The agent needs to keep track of its subscriptions so that it can cancel the
subscription when necessary.– This requires that the agent use the agree message to record subscriptions.
• The code for requesting and recording the subscription is:+initialized : true <-.send(subscribe, agentID(subscriber, addresses("local:localhost")), clock);
+message(agree, ?agentID, ?topic) : true <-+subscribed(?topic, ?agentID);
• Issue:– The problem here is that agree is quite generic (it is also used in the FIPA-
request protocol), so it is possible that this rule will not only be used by subscription requests.
Non-ACRE: Subscribee• To handle this, we must also keep track of subscription requests:
+initialized : true <-+subscribing(clock, subscriber),.send(subscribe, agentID(subscriber, addresses("local:localhost")), clock);
+message(agree, agentID(?name, ?addr), ?topic) : subscribing(?topic, ?name) <--subscribing(?topic, ?name),+subscribed(?topic, agentID(?name,?addr));
• Key Points:– Initially, we only record the name of the participant because the agent
identifier we use may not be complete.– When the agree message is returned, the full agent identifier is known as it
was provided by the participant - it can be used in the subscribed(…) belief.– It is actually required here because otherwise the initiator will not be able
to cancel the subscription!
Non-ACRE: Subscribee• Because we keep track of subscription requests, we also need
to handle refuse messages (to update our beliefs accordingly):
+message(refuse, agentID(?name, ?addr), ?topic) : subscribing(?topic, ?name) <--subscribing(?topic, ?name);
• Now, we need to consider receipt of inform messages from the service.– These messages contain information that may be needed by the agent.– In keeping with the ethos adopted in the FIPA-request protocol, we
separate receipt of the message from handling of its content, which is done by a subgoal.
Non-ACRE: Subscribee• The inform message handling rules for the clock example are
given below:
+message(inform, ?agentID, value(?topic, ?value)) : subscribed(?topic, ?agentID) <-!handleSubscriptionResult(?topic, ?value);
+handleSubscriptionResult(clock, ?x) : true <-if (clock(?y)) .replace(clock(?y), clock(?x))else +clock(?x);
• Key Points:– This could equally be implemented by a single rule, but in my view,
this separation of concerns offers greater flexibility in implementation.• It does not mix conversation logic and application logic.
Non-ACRE: Subscribee• Finally we need a rule to trigger the cancellation of the
subscription:
+clock(?x) : subscribed(clock, ?agentID) <--subscribed(clock, ?agentID),.send(cancel, ?agentID, protocol(subscribe, clock));
• Key Points:– Here, we assume that sending the cancel message cancels the
subscription.• This is NOT guaranteed in the cancel protocol!
Non-ACRE: Subscribee (Code)#agent subscribee
+initialized : true <-+subscribing(clock, subscriber),.send(subscribe, agentID(subscriber, addresses("local:localhost")), clock);
+message(agree, agentID(?name, ?addr), ?topic) : subscribing(?topic, ?name) <-
-subscribing(?topic, ?name),+subscribed(?topic, agentID(?name, ?a-ddr));
+message(refuse, agentID(?name, ?addr), ?topic) : subscribing(?topic, ?name) <--subscribing(?topic, ?name);
+message(inform, ?agentID, value(?topic, ?value)) : subscribed(?topic, ?agentID) <-!handleSubscriptionResult(?topic, ?value);
+handleSubscriptionResult(clock, ?x) : true <-if (clock(?y)) .replace(clock(?y), clock(?x))else +clock(?x);
+clock(?x) : subscribed(clock, ?agentID) <--subscribed(?topic, ?agentID),.send(cancel, ?agentID, protocol(subscribe, clock));
Non-ACRE: Deploymentpublic class Main {
public static class DebugConfig extends AgentSpeakDebugConfiguration { public DebugConfig() { super(“afas", null); }
public void configure() { super.configure();
addAgent(“subscriber", “acre/subscriber.aspeak"); addAgent("subscribee", “acre/subscribee.aspeak");
}}
public static void main(String[] args) { new DebugConfig().configure();}
}
ACRE: Adaptation• As in the first example, ACRE requires installation and
initialization, so we need to add the following lines of code:
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <-acre.init,+topic(clock);
• Next, we need to modify any rule that uses a message event as a trigger or which uses the .send(…) action…
• As we will see in this example, because ACRE manages conversations, some of the rules will not be necessary…
ACRE: Subscriber• The main topic rule needs only minor adjustment:
+topic(clock) : true <-?i = 0,while(true) {.sleep(1000),?cids = list[?cid |conversationHistory(?cid, 1, received, subscribe, clock) &conversationProtocolName(?cid, subscribe) &conversationState(?cid, agreed) ],if (?cids != []) acre.advance(?cids,inform,result(clock, ?i)),?i = ?i + 1};
• Key Points:– The list construction conditions to reflect those conversations that started
with a message to subscribe to the clock topic that are subscribe conversations and which are in an agreed state.
ACRE: Subscriber• The agree and refuse subscription message rules become:
+conversationAdvanced(?cid,subscribing,?index) :conversationHistory(?cid, ?index, received, subscribe, ?topic) &conversationProtocolName(?cid,subscribe) & topic(?topic) <-
acre.advance(?cid,agree,?topic);
+conversationAdvanced(?cid,subscribing,?index) :conversationHistory(?cid, ?index, received, subscribe, ?topic) &conversationProtocolName(?cid,subscribe) & ~topic(?topic) <-
acre.advance(?cid,refuse,?topic);
• And the standard cancel protocol use replaces the custom rule used in the non-ACRE version:+conversationCancelRequest(?cid) : true <-
acre.confirmCancel(?cid),acre.forget(?cid);
ACRE: Subscriber#agent subscriber
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <-acre.init, +topic(clock);
+topic(clock) : true <-?i = 0,while(true) {.sleep(1000),?cids = list[?cid | conversationHistory(?cid, 1, received, subscribe, clock) &conversationProtocolName(?cid, subscribe) &conversationState(?cid, agreed) ],if (?cids != []) acre.advance(?cids,inform,result(clock, ?i)),?i = ?i + 1};
+conversationAdvanced(?cid,subscribing,?index) :
conversationHistory(?cid, ?index, received, subscribe, ?topic) &conversationProtocolName(?cid,subscribe) & topic(?topic) <-acre.advance(?cid,agree,?topic);
ACRE: Subscriber+conversationAdvanced(?cid,subscribing,?index) :
conversationHistory(?cid, ?index, received, subscribe, ?topic) &conversationProtocolName(?cid,subscribe) & ~topic(?topic) <-
acre.advance(?cid,refuse,?topic);
+conversationCancelRequest(?cid) : true <-acre.confirmCancel(?cid),acre.forget(?cid);
ACRE: Subscribee• Similar modifications are made to the initialization process for
the Subscribee:
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <-acre.addRepository("file:///C:/Users/rem/acre"),acre.init,acre.addContact(agentID(subscriber,addresses("local:localhost"))),acre.start(org.fipa_subscribe_1.0,subscriber,subscribe,clock);
• Key Points:– Again note the new facility to use the address: “local:localhost” for the
local message transport service!
ACRE: Subscribee• The agreement and refusal message rules are modified as
follows:+conversationAdvanced(?cid,agreed,?in) :conversationHistory(?cid,?in,received,inform,result(?topic, ?value)) &conversationProtocolName(?cid,subscribe) <-!handleSubscriptionResult(?topic, ?value);
+conversationAdvanced(?cid,refused,?index) :conversationProtocolName(?cid,subscribe) <-acre.forget(?cid);
• Key Points:– The implementation of the !handleSubscriptionResult(…) goal remains
the same.– We need to forget the conversation when the subscription is refused…
ACRE: Subscribee• Finally, the cancellation rule (which triggers cancellation once
the clock reaches 2) is modified as follows:+clock(?x) : ?x > 2 & conversationHistory(?cid, 1, sent, subscribe, clock) <-
acre.cancel(?cid),acre.forget(?cid);
ACRE: Subscribee#agent subscribee
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <-acre.addRepository("file:///C:/Users/rem/acre"),acre.init,acre.addContact(agentID(subscriber,addresses("local:acre.agentspeak.ucd.ie"))),acre.start(org.fipa_subscribe_1.0,subscriber,subscribe,clock);
+conversationAdvanced(?cid,agreed,?index) :
conversationHistory(?cid, ?index, received, inform, result(?topic, ?value)) &conversationProtocolName(?cid,subscribe) <-!handleSubscriptionResult(?topic, ?value);
+conversationAdvanced(?cid,refused,?index) : conversationProtocolName(?cid,subscribe) <-are.forget(?cid);
+!handleSubscriptionResult(clock, ?x) : true <-if (clock(?y)) .replace(clock(?y), clock(?x))else +clock(?x);
+clock(?x) : ?x > 2 & conversationHistory(?cid, 1, sent, subscribe, clock) <-acre.cancel(?cid),acre.forget(?cid);
ACRE: Deploymentpublic class Main {
public static class ACREDebugConfig extends AgentSpeakDebugConfiguration { public ACREDebugConfig() { super("acre", null); }
public void configure() { addServiceInspectorFactory( new ProtocolManagerServiceInspectorFactory() );
super.configure(); addPlatformService(
ProtocolManagerPlatformService.class,ProtocolManagerPlatformService.NAME );
addAgent(“subscriber", “acre/subscriber.aspeak"); addAgent("subscribee", “acre/subscribee.aspeak");
}}
public static void main(String[] args) { new ACREDebugConfig().configure();}
}
SECTION 4: COMBINED PROTOCOLS
Combined Protocols• In this example, we combine the request and subscribe protocols.
– Specifically, we introduce a task that returns a list of topics in the form [topic1, topic2, …]
– The initiator agent uses this list to find out which topics are available, and subscribes to all such topics.
• In this case, there is only one topic: the clock topic.
• Two agents are created in this example:– combined.aspeak: the agent that provides the subscription service &
supports subscription discovery though the listTopics task.– combinee.aspeak: the agent that queries for topics and subscribes to any
topic listed.
non-ACRE: Combined#agent combined
+initialized : true <-+topic(clock);
+topic(clock) : true <-?i = 0,while(true) {durative(.sleep(1000)),?aids = list[?aid | subscription(?aid, clock) ],if (?aids != []) .send(inform, ?aids, value(clock, ?i)),?i = ?i + 1};
+message(subscribe, ?agentID, ?topic) : topic(?topic) <-
.send(agree, ?agentID, ?topic),+subscription(?agentID, ?topic);
+message(subscribe, ?agentID, ?topic) : ~topic(?topic) <-.send(refuse, ?agentID, ?topic);
+message(cancel, ?agentID, protocol(subscribe, ?topic)) : subscription(?agentID, ?topic) <--subscription(?agentID, ?topic);
canDo(listTopics);
non-ACRE: Combined+message(request, ?initiator, ?task) : canDo(?task) <-
+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {!performTask(?initiator, ?task)} recover {if (performingTask(?initiator, ?task)) .send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
+message(request, ?initiator, ?task) : ~canDo(?task) <-.send(refuse, ?initiator, ?task);
+message(cancel, ?initiator, protocol(request, ?task)) : performingTask(?initiator, ?task) <--performingTask(?initiator, ?task);
// Custom Tasks+!performTask(?agentID, listTopics) : true <-
?list = list[?topic | topic(?topic)],.send(inform, ?agentID, result(listTopics, ?list));
// Default Task - no implementation = task failure+!performTask(?agentID, ?task) : true<-
.send(failure, ?agentID, ?task);
non-ACRE: Combinee#agent combinee
+initialized : true <-.send(request, agentID(combined, addresses("local:localhost")), listTopics);
+message(inform, ?agentID, result(?task, ?value)) : true <-!handleRequestResult(?agentID, ?task, ?value);
+!handleRequestResult(?agentID, listTopics, ?topics) : true <-?list = ?topics,while (?list != []) {
+subscribing(clock, combined),.send(subscribe, ?agentID, head(?topics)),?list = tail(?list)
}; +message(agree, agentID(?name, ?addr), ?topic) : subscribing(?topic, ?name) <-
-subscribing(?topic, ?name),+subscribed(?topic, agentID(?name, ?addr));
+message(refuse, agentID(?name, ?addr), ?topic) : subscribing(?topic, ?name) <--subscribing(?topic, ?name);
non-ACRE: Combinee+message(inform, ?agentID, value(?topic, ?value)) : subscribed(?topic, ?agentID) <-
!handleSubscriptionResult(?topic, ?value);
+!handleSubscriptionResult(clock, ?x) : true <-if (clock(?y)) .replace(clock(?y), clock(?x))else +clock(?x);
+clock(?x) : ?x > 2 & subscribed(clock, ?agentID) <--subscribed(clock, ?agentID),.send(cancel, ?agentID, protocol(subscribe, clock));
non-ACRE: Summary• The combined agent is simply the requester and subscriber agents
combined:– The requester code has been modified to introduce support for the listTopics
task.
• The combinee agent is more complex as it must process the topic list and subscribe to each topic:+!handleRequestResult(?agentID, listTopics, ?topics) : true <-?list = ?topics,while (?list != []) {+subscribing(clock, combined),.send(subscribe, ?agentID, head(?topics)),?list = tail(?list)};
• The bold lines above represent the start of the subscribe protocol…
ACRE: Combined#agent combined
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <-acre.init,+topic(clock);
+topic(clock) : true <-?i = 0,while(true) {.sleep(1000),?cids = list[?cid | conversationHistory(?cid, 1, received, subscribe, clock) & conversationProtocolName(?cid, subscribe) &conversationState(?cid, agreed) ],if (?cids != []) acre.advance(?cids,inform,result(clock, ?i)),?i = ?i + 1};
+conversationAdvanced(?cid,subscribing,?index) :
conversationHistory(?cid, ?index, received, subscribe, ?topic) &conversationProtocolName(?cid,subscribe) & topic(?topic) <-acre.advance(?cid,agree,?topic);
ACRE: Combined+conversationAdvanced(?cid,subscribing,?index) :
conversationHistory(?cid, ?index, received, subscribe, ?topic) &conversationProtocolName(?cid,subscribe) & ~topic(?topic) <-
acre.advance(?cid,refuse,?topic);
+conversationCancelRequest(?cid) : true <-acre.confirmCancel(?cid),acre.forget(?cid);
canDo(listTopics);
+conversationAdvanced(?cid,requested,?index) :conversationHistory(?cid, ?index, received, request, ?task) &conversationProtocolName(?cid,request) & canDo(?task) <-
acre.advance(?cid,agree,?task),try {
!performTask(?cid, ?task)} recover {
acre.advance(?cid,failure,?task)};
ACRE: Combined+conversationAdvanced(?cid,requested,?index) :
conversationHistory(?cid, ?index, received, request, ?task) &conversationProtocolName(?cid,request) & ~canDo(?task) <-
acre.advance(?cid,refuse,?task);
+conversationCancelRequest(?cid) : true <-acre.confirmCancel(?cid),acre.forget(?cid);
// Custom Tasks+!performTask(?cid, listTopics) : true <-
?list = list[?topic | topic(?topic)],acre.advance(?cid,inform, result(listTopics, ?list));
// Default Task - no implementation = task failure+!performTask(?agentID, ?task) : true<-
acre.advance(?cid,failure, ?task);
ACRE: Combinee#agent combinee
module acre -> is.lill.acre.agent.module.ACREModule;
// Get the topic list+initialized : true <-
acre.addRepository("file:///C:/Users/rem/acre"),acre.init,acre.addContact(agentID(combined,addresses("local:localhost"))),acre.start(org.fipa_request_1.3,combined,request,listTopics);
// Subscribe to all listed topics+conversationAdvanced(?cid,done2,?index) :
conversationHistory(?cid, ?index, received, inform, result(?task, ?value)) & conversationProtocolName(?cid,request) <-!handleRequestResult(?cid, ?task, ?value);
+!handleRequestResult(?cid, ?task, ?topics) : ?task == listTopics <-?list = ?topics,while (?list != []) {acre.start(org.fipa_subscribe_1.0,combined,subscribe,head(?topics)),?list = tail(?list)};
ACRE: Combinee+conversationAdvanced(?cid,agreed,?index) :
conversationHistory(?cid, ?index, received, inform, result(?topic, ?value)) &conversationProtocolName(?cid,subscribe) <-
!handleSubscriptionResult(?topic, ?value);
+conversationAdvanced(?cid,refused,?index) : conversationProtocolName(?cid,subscribe) <-arce.forget(?cid);
+!handleSubscriptionResult(clock, ?x) : true <-if (clock(?y)) .replace(clock(?y), clock(?x))else +clock(?x);
+clock(?x) : ?x > 2 & conversationHistory(?cid, 1, sent, subscribe, clock) <-acre.cancel(?cid),acre.forget(?cid);
SECTION 5: DIRECTORY FACILITATOR SERVICE
Overview• The Directory Facilitator (DF) Service is a FIPA-specified platform
service that provides support for advertising and locating services provided by agents.– The service is constrained to the agent platform.– Agents register with the service– Once registered, agents can advertise services that they offer and look for
agents that provide services they need.
• The DF Service is supported in AF-AgentSpeak through the com.agentfactory.clf.library.DFSLibrary module.
• We will illustrate the use of this service by modifying the request protocol example.
non-ACRE• For the requester agent, the only modifications involve connecting to
and registering with the DF Service:module dfs->com.agentfactory.clf.library.DFSLibrary;
+initialized : true <-dfs.setup,dfs.register;
• For the requestee agent, the only modification is to use the service to identify the participant in the conversation:
module dfs->com.agentfactory.clf.library.DFSLibrary;
+initialized : true <-dfs.setup,dfs.register,when (dfsService(agentID(requester, ?addr))) {.send(request, agentID(requester, ?addr), drink(beer))};
non-ACRE: Requester#agent requester
module dfs->com.agentfactory.clf.library.DFSLibrary;
+initialized : true <-dfs.setup,dfs.register;
// FIPA REQUEST PROTOCOL RULES +message(request, ?initiator, ?task) : canDo(?task) <-
+performingTask(?initiator, ?task),.send(agree, ?initiator, ?task),try {!performTask(?initiator, ?task)} recover {if (performingTask(?initiator, ?task)) .send(failure, ?initiator, ?task)}-performingTask(?initiator, ?task);
+message(request, ?initiator, ?task) : ~canDo(?task) <-.send(refuse, ?initiator, ?task);
non-ACRE: Requester+message(cancel, ?initiator, protocol(request, ?task)) :
performingTask(?initiator, ?task) <--performingTask(?initiator, ?task);
// TASKScanDo(drink(beer));canDo(tell_time);
+!performTask(?initiator, drink(beer)) : name(?name) <-[performingTask(?initiator, drink(beer))]
.println("[" + ?name + "] Mmmm tasty beer"),
.send(inform, ?initiator, done(drink(beer)));
+!performTask(?initiator, tell_time) : name(?name) & time(?h, ?m, ?s) <-[performingTask(?initiator, tell_time)]
.send(inform, ?initiator, result(tell_time, time(?h, ?m, ?s)));
+!performTask(?initiator, ?task) : true <-.fail;
non-ACRE: Requestee#agent requestee
module dfs->com.agentfactory.clf.library.DFSLibrary;
+initialized : true <-dfs.setup,dfs.register,when (dfsService(agentID(requester, ?addr))) {
.send(request, agentID(requester, ?addr), drink(beer))};
+message(inform, ?agentID, done(drink(beer))) : true <-
.send(request, agentID(requester, addresses("local:localhost")), tell_time);
+message(inform, ?agentID, result(tell_time, time(?h, ?m, ?s))) : true <-.println("The time is: "+?h + ":"+?m+":"+?s);
ACRE• For the ACRE implementation, the modifications are largely
the same, except that the initiator adds the agent as a contact once it has been discovered.
module dfs->com.agentfactory.clf.library.DFSLibrary;
+initialized : true <-dfs.setup,dfs.register,when (dfsService(agentID(requester, ?addr))) {
acre.addContact(agentID(requester,?addr)),acre.start(org.fipa_request_1.3,combined,request,drink(beer));
};
ACRE: Requester#agent requester
module acre -> is.lill.acre.agent.module.ACREModule;module dfs->com.agentfactory.clf.library.DFSLibrary;+initialized : true <- acre.init, dfs.setup, dfs.register,
+conversationAdvanced(?cid,requested,?index) : conversationProtocolName(?cid,request) & canDo(?task) &conversationHistory(?cid, ?index, received, request, ?task) <-acre.advance(?cid, agree, ?task),try {!performTask(?initiator, ?task)} recover {acre.advance(?cid, failure,? task)};
+conversationAdvanced(?cid,requested,?index) : conversationProtocolName(?cid,request) & ~canDo(?task) &conversationHistory(?cid, ?index, received, request, ?task) <-acre.advance(?cid, refuse, ?task);
+conversationCancelRequest(?cid) : true <- acre.confirmCancel(?cid), acre.forget(?cid);
canDo(drink(beer));
+!performTask(?cid, drink(beer)) : name(?name) <- [conversationHistory(?cid, 1, received,request,tell_time)].println("[" + ?name + "] Mmmm tasty beer"),acre.advance(?cid, inform, done(drink(beer)));
+!performTask(?cid, ?task) : true <- .fail;
ACRE: Requestee#agent requestee
module acre -> is.lill.acre.agent.module.ACREModule;
+initialized : true <-acre.addRepository("file:///C:/Users/rem/acre"), acre.init,dfs.setup,dfs.register,when (dfsService(agentID(requester, ?addr))) {acre.addContact(agentID(requester,?addr)),acre.start(org.fipa_request_1.3,combined,request,drink(beer));};
+conversationAdvanced(?cid,refused,?index) :
conversationProtocolName(?cid,request) <-acre.forget(?cid);
+conversationAdvanced(?cid,done,?index) : conversationProtocolName(?cid,request) <-acre.start(org.fipa_request_1.3,requester,request,tell_time);
+conversationMessage(?cid,inform,result(tell_time, time(?h, ?m, ?s))) : true <-.println("The time is: "+?h + ":"+?m+":"+?s);