Practical work on Environment Oriented Programming: The CArtAgO Platform

Introduction

The CArtAgO distribution is available from http://sourceforge.net/projects/cartago/ or here locally (current version is cartago-2.0.2).

In the following exercises you will use a MAS with an environment deployed on multiple machines. You will develop some agent joining a remote workspace of this environment. This workspace is launched on a server machine. The code is thus organized into two JaCaMo projects: one for the MAS "kind-of" server, one for the MAS "kind-of" client.

  • The MAS server ("step_env") provides you with some artifacts deployed in the "server" workspace.
  • The MAS client ("step") is composed of agents that you will have to customize or develop. These agents will connect on the remote workspace to use artifacts that are deployed on this remote workspace.
  • For an agent deployed in the MAS client, use the action in the agent code: "joinRemoteWorkspace("default","localhost",WspID2)" where you replace "localhost" by the machine IP number.

Configuration

  • You can download and install the CArtAgO distribution in the MAOP directory to have the complete distribution with examples, documentation and tutorial.
  • Let's note that by default, from the work done in the previous practical work, you already have CArtAgO installed in your local environment.

Note

In order to create a MAS using Jason agents and CArTAgO artifacts and workspaces, just include in the MAS configuration file (the one ending with .mas2j):

  • cartago.jar and c4jason.jar libraries in the declared class path (if you classpath doesn't already include them)
  • the declaration of c4jason.CartagoEnvironment as environment (see below for further configurations in case of using multiple workspaces);
  • the declaration of c4jason.CAgentArch as agent architecture, for each agent that needs to work with artifacts in workspaces.

Preamble

Default artifacts at the disposal of agents:

By default, each workspace contains a basic set of predefined artifacts that provide core functionalities to the agents.

  • workspace artifact (see the Workspace API): the workspace artifact (cartago.WorkspaceArtifact) provides functionalities to create, dispose, lookup, link, focus artifacts of the workspace. It provides also operations to set roles and policies related to the RBAC security model.
  • node artifact (see the Node API): the node artifact (cartago.NodeArtifact) provides functionalities to create new workspaces, to join local and remote workspaces
  • blackboard artifact: the blackboard artifact (cartago.tools.TupleSpace) provides a tuple space that agents can exploit to communicate and coordinate
  • console artifact: the console artifact (cartago.tools.Console) provides functionalities to print messages on standard output.

How to make agents invoque operations of the artifact

  • In case of operation invocation with no specification of target artifact: the artifact is automatically selected from the workspace. If there are no artifacts providing such action, the action fails. if more than one artifact is found, artifacts created by the agent itself are considered first. If more than one artifact is found, one is selected non deterministically. Then, the rest of the artifacts are considered, and one is selected non deterministically.
  • In case of operation invocation with specification of a target artifact: This is done by adding the annotation [artifact_id(Id)], where Id must be bound to the artifact identifier. Alternatively, the annotation [artifact_name(Name)] can be used, where Name must be bound to the logic name of the artifact.
  • In case of operation invocation with specification of the target workspace: done by adding the annotation [wsp_id(WspID)], where WspID must be bound to the wsp identifier.

How to make agents work in workspaces

  • By default an agent, when launched, joins the default workspace on current node.
  • Agents can create, join and work in multiple workspace at a time. However there is always a current workspace, to which are routed actions with no artifact id or workspace id specified. Current workspace info are automatically tracked by the current wsp(WspId,Name,NodeId) belief.
  • Agents can join workspaces that are hosted on remote nodes, by means of a joinRemoteWorkspace action. As soon as the join succeed, the interaction within remote workspaces is the same as local workspace.
  • Infrastructure options:
    • by default, Jason programs using CArtAgO environment create a standalone CArtAgO node, i.e. not accessible through the network
    • To install a CArtAgO node accessible also to remote agents further parameters can be specified to the c4jason.CartagoEnvironment:
      • c4jason.CartagoEnvironment("infrastructure"{,WspName, protocol(ProtName, Address), …}): installs an infrastructure layer specifying the protocols to support and the local address where to start the service;
      • c4jason.CartagoEnvironment("remote"{,WspName, protocol(ProtName, Address), …}): does not install any node – agents directly join the specified remote workspace;
      • c4jason.CartagoEnvironment("local"{,WspName}): does not install any node – agents directly join the specified locak workspace.

Step 1: Joining workspaces and using Artifacts

For this step:

  • In the workspace running on the server, a very simple artifact, called "env" has been deployed. This artifact will be enriched step by step with actions and observable properties.
  • The first version of this artifact has one action "printMsg(String msg)". This action prints the parameter "msg" on the standard output adding the name of the emitter of this message
  • On the server, the "majordomo" agent creates the workspace and the artifact, and leaves a message
"majordomo" agent code:

!setup_and_monitor.

+!setup_and_monitor
        <-      createWorkspace("server");
                joinWorkspace("server",Id);
                !setupArtifacts.
                
+!setupArtifacts
        <- makeArtifact("env","easss.step1.Env",[]);
           printMsg("ready. Your turn!").

Exercise A: execution of a MAS acting on a simple artifact for leaving a message in different workspaces

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • Look at the code of the "guest_agent": it joins the remote workspace and leaves one message. Add an action in the plan of this agent so that the "guest_agent" leaves a message consisting of you name.
  • Launch your MAS client as soon as you see the message from the "majordomo" agent.

Note

  • You can execute this exercise in group where one member of the group launches the "server" and the other members of the group launch their "guest_agent" that joins this remote workspace.
  • You can use the internal action ".my_name(Name)" to get the name of the agent, the internal action ".concat(str1,str2,Result)" to concat str1 to str2 in Result.

Exercise B: test of a MAS acting on a more complex artifact for leaving a message in different workspaces

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • The artifact "env" running on the server has been changed. Its action "printMsg(String msg)" has an effect on a GUI version now.
  • Reuse the "guest_agent" that you have developed in the previous exercise and launch your MAS client as soon as the "majordomo" agent invites you to do so.

Step 2: Observing Artifacts

For this step:

  • On the server, the artifact "env" has been enriched with an observable property showing the number of messages left so far.

Exercise: execution of a MAS for "observing" artifacts situated in the environment

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • Look at the code of the "guest_agent": it joins the workspace and leaves a message for ever. Adapt this agent so that it leaves a message corresponding to the room ID in which you are situated.
  • Look at the code of the "observer" agent aiming at observing the "env" artifact. The "observer" agent joins the remote workspace, lookups for the artifact "env", focuses on this artifact and gets the value of the "numMsg" observable property of this artifact.
  • Launch your MAS client as soon as the "majordomo" agent invites you to do so.

Step 3: Computing with Artifacts

For this step:

  • The artifact "env" has been enriched with a "computePi" action (with output parameters "OpFeedbackParams")

Exercise: development of your first agent for computing

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • Write your first Jason Agent "YOURNAME_agent" with code for joining the remote workspace, using this "computePi" action. After computing PI, the agent prints the value and your name using the action "printMsg"
  • Modify the "main.mas2j" file for launching this new agent
  • Launch your MAS client as soon as the "majordomo" agent invites you to do so.

Step 4: Coordination with the help of the artifact

For this step,

  • the "env" artifact has been enriched to install mutual exclusion in the actions for leaving messages. It has a private attribute "locked" and two operations "lock" and "unlock" insuring the mutual exclusion. The multiple agents that will be launched in the system will use these actions to access in an exclusive way to the artifact and print the messages in sequence.
  • The "observer" agent in the client MAS, joins the workspace, lookups for the artifact "env", focuses on the artifact "env" and gets the value of the "numMsg" observable property.

Exercise: "let's coordinate - Don't interfere!"

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • Develop your own Jason Agent "YOURNAME_agent" based on the "guest_agent" code so that your agent leaves three messages in sequence.
  • Launch your MAS as soon as the "majordomo" agent invites you to do so.
  • Transform your Jason Agent using the "lock" and "unlock" operations to leave three messages in sequence in a synchronized way.
  • Relaunch your MAS client.

Step 5: Coordination again by the way of artifact

For this step:

  • The "env" artifact has been enriched with a synchronisation operation "sync" and with the possibility to setup a number of participants for the synchronization "setupBarrier".
  • The "majordomo" agent has been enriched to setup the barrier in the "env" artifact.
majordomo agent code:

!setup_and_monitor.

+!setup_and_monitor
        <-      createWorkspace("server");
                joinWorkspace("server",Id);
                !setupArtifacts;
        setupBarrier(3).
                
+!setupArtifacts
        <- makeArtifact("env","easss.step5.Env",[]);
           printMsg("ready. Your turn!"). 

Exercise: Synchronization by the way of an artifact

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • Write a "YOURNAME_agent" writing 5 messages in sequence and then use the synchronisation between all agents provided by the artifact operation "sync"

Note

  • You can do this exercise using a common env server and connecting the agents developed by different students.

Step 6: Modularity / Instances of Artifacts

With the previous implementation, we had a monolithic "env" artifact. It could cause some problems, for instance, if adding a long-term action such as computePi. A better way could be to decompose the "env" in multiple artifacts (types and instances). Let's do a little bit of software engineering.

For this step:

  • the artifact "env" has been refactored and decomposed into "msg_console", "calculator", "lock", "barrier"

Exercise: Using a world of artifacts

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • Add in the "YOURNAME_agent" the code for using these artifacts.

Step 7: Creating a Counter Artifact

In this step, you are going to create your own artifacts building a simple counting world example. In this world, counter agents and observer agents are situated in the default workspace and use counting artifacts.

  • The "Counter" artifact is a simple artifact composed of a "count" observable property initialized to 0 when the artifact is created. The artifact has a single operation "inc" without any parameters. This operation increments "count" and sends a "tick" signal every time it is executed by an agent.
  • The "observer" agent:
    • sends a message to the "ticker" agent and starts focusing on this artifact, as soon as it receives a message telling it the name of the counter artifact that has been created "artifact_counter_is(Name)".
    • stops focusing on the artifact as soon as the observable property "count" is equal to 6.
    • prints a message "observed new value" with the value, each time the observable property is changed.
    • prints a message "/perceived a tick in /" with the name of the artifact in which a tick has been done.
// Agent observer code
/* Initial beliefs and rules */

/* Initial goals */
!observe.

/* Plans */ 

+!observe : true 
<-      println("Observer starting to work");
        lookupArtifact("counter",Count);
        +myTool(Count); 
        focus(Count).
...
  • The "ticker" agent:
    • creates the "counter" artifact and tells to all the agents that are in the MAS the name of this artifact ("tell", "artifact_counter_is(counter)"),
    • starts to use this artifact to increment the value of the counter as soon as it receives an answer of an agent
    • Being a lazy agent, it rests for a while (let's say 100 ticks), after each increment. It does this job for 100 cycles. Every time, it increments the artifact and prints a message.
// Agent ticker code
/* Initial beliefs and rules */

/* Initial goals */
!setup.

/* Plans */

+!setup : true <- 
        !setupCounter(Id);
        +counter(Id);
        !increment.

+!increment : ready[source(Ag)] & counter(Id) <-
        for (.range(I,1,100)) {
                .wait(100);
                inc[artifact_id(Id)];
                .print("incrementing");
                }.
        
+!increment : not ready[source(Ag)] <-
        !increment.
        
+!setupCounter(C) : true <-
        makeArtifact("counter","easss.step7.Counter",[],C);
        .broadcast(tell,artifact_counter_is(counter)).

Exercise: Counter Artifact

  • Download the code for this step and install it in your eclipse workspace as jason projects.
  • Write the code of the "Counter" artifact type.
  • Write the code of the "observer" agent (see template above)

Step 8: Creating a Bounded Counter Artifact

In this step, we are going to extend the previous simple counting world example. Keeping the same agents, the "Counter" artifact will be transformed into a "bounded" counter artifact, i.e. its observable property "count" cannot be higher than a maximum value fixed at the initialisation. The artifact generates a failure signal when its action "inc" is no more possible.

Handling failure

  • The failed primitive is used to specify the failure of an operation:
failed(String failureMsg)
failed(String failureMsg, String descr, Object... args)
  • An action feedback is generated, reporting a failure msg and optionally also a tuple descr(Object…) describing the failure.
  • The annotation that you can use in the agent code, is a follows:
[error_msg(Msg),env_failure_reason(inc_failed("max_value_reached",Value))]

Exercise: Bounded Counter Artifact

  • Create a Jason project called "step8".
  • Reuse the code of the previous step and install it in "step8"
  • Change the code of the "Counter" artifact type, so that it behaves as described above
  • Change the code of the "ticker" agent so that it executes a plan in case of failure of the action "inc"

Step 9: Going deeper in the use of Cartago

Step 10: Artifact-Based Auction House

  • This last exercise is dedicated to the development of a MAS where agents use an artifact to execute an auction protocol to allocate a set of tasks between them. You can get here the specification of the global application.
  • The contractor agent, "giacomo" creates an artifact of this type for each of the tasks it needs to allocate the tasks to "company" agents: e.g. one auction artifact for building the walls, another one for setting the floors, …
  • The "company" agents observe the artifacts that propose a task compatible with goals and skills. They use these artifacts for bidding according to their own strategies.

Auction Artifact

  • The "AuctionArt" artifact has the following observable properties:
    • "task": task description,
    • "maxValue": maximum payment value,
    • "currentBid": current best bid (lower service price),
    • "currentWinner": current winning agent ID
  • It has the following operations:
    • "bid(p)": places a new bid for doing the task for price "p". If the bid is better than the previous one, the "currentBid" and the "currentWinner" are updated.
    • "init(task,maxp)": initializes the artifact with the task description "task" that will be auctionned and the maximum value for the bid "maxp".

How to get the agent executing an action:

  • In an artifact, to obtain the agent that has launched the execution of an action, use the "getOpUserName" method

Exercise: Auction Artifact

  • Create a Jason project called "auction".
  • Write the "AuctionArt" artifact

Owner Agent

  • The "giacomo" agent is in charge of creating the auction house, i.e. to create an artifact of the type "AuctionArt" for each of the tasks to allocate.
  • It is also in charge of driving and managing the auctions (the creation of an auction artifact consists in declaring the task that is managed by the artifact and the maximum possible price for it).
  • The agent waits for a certain amount of time for the bids. At the fixed time, the agent gets the winner for each of the tasks.

Exercise: Owner Agent

  • Complete the "giacomo" agent for managing the auctions as described above
// Agent giacomo

/* Initial beliefs and rules */
my_task("SitePreparation").
my_task("Floors").
my_task("Walls").

my_max_bid("SitePreparation",2000).
my_max_bid("Floors",1000).
my_max_bid("Walls",3000).

/* Initial goals */

!contract.

/* Plans */

+!contract : true <- 
        !create_auction_house;
        !wait_for_bids.

 +!create_auction_house <-
        for (my_task(Task) & my_max_bid(Task,MaxPrice)) {
                !create_auction_artifact(Task,MaxPrice);
                .println("creating an auction artifact for ",Task);     
        }.
        
+!create_auction_artifact(Task,MaxPrice) <-
        makeArtifact(Task,"easss.auction.AuctionArt",[Task,MaxPrice],ArtId);
        focus(ArtId).

-!create_auction_artifact(Task,MaxPrice)[error_code(Code)] <-
        .print("Error creating auction artifact ",Code).

+!wait_for_bids <-
        .print("Waiting for bids for 5 seconds ...");
        .wait(5000);
        !show_winners.
...

Iteration implementation

  • The implementation of the iteration on the auction artifacts can be done using the for construct:
Implementation of for.

Syntax:
  for ( logical formula ) {
     plan_body
  }

for all unifications of logical formula, the plan_body is executed.

Example:

+event : context
  <- ....
     for ( vl(X) ) {
        .print(X);     // print all values of X
     }
     for ( .member(X,[a,b,c]) ) {
        .print(X);    // print all members of the list
     }
     for ( .range(I,1,10) ) {
        .print(I);    // print all values from 1 to 10
     }
     ....

The unification resulting from the evaluation of the logical formula is used only inside the loop, i.e., the unification after the for is the same as before. 

Company Agents

  • The company agents discover the auction artifacts corresponding to the tasks they are interested in. They build the bids according to their own strategies.

Exercise: Company agents

  • Write a "company_blind" agent interested in the tasks "sitePreparation", "Floors", "Walls" having a "blind" strategy for bidding: it decreases the current bid by a constant value until his/her lowest limit is reached.
  • Write a "company_strategies" agent having multiple strategies for each task (choose which strategy to use)
  • Write a "company_coordinated" agent having a maximum value for all the tasks it bids for (i.e. coordination of the bids it realizes on each task)

How to make agents aware that it is winning an auction

  • You can use the following code for making an agent aware that it is winning or not an auction:
i_am_winning(Art)   // check if I placed the current best bid on auction artifact Art
   :- currentWinner(W)[artifact_id(Art)] & .my_name(Me) & .term2string(Me,MeS) & W == MeS.
  • You can use the following code for summing all the current bids placed by the agent
sum_of_my_offers(S) :- 
   .my_name(Me) & .term2string(Me,MeS) &
   .findall( V,      // artifacts/auctions I am currently winning
             currentWinner(MeS)[artifact_id(ArtId)] &
                      currentBid(V)[artifact_id(ArtId)], 
             L) & 
   S = math.sum(L).

Olivier Boissier, January 2014