What you’ll build

The aim is to revisit the classical Hello World example with a multi-agent oriented programming perspective. We start with the simplest application we can write in JaCaMo and improve it to progressively show some of the most important aspects of the programming language and the platform.

What you’ll need

Code

  • You can download the solutions from here

1. Single Agent

We start with a system having a single and very simple agent that just prints out a message using the following plan written in (and stored) in a file called hwa.asl:

1// hwa.asl
2+!say(M) : true <- .print(M).

image

This plan can be read as "whenever I have the goal !say(M), achieve it by printing "M" (M is a variable because it starts by an upper-case letter). The symbol + means "in the event of coming to believe …​"; the code after : are conditions on what the agents believes to be the current situation which are required for the plan to be used; and the code after are "deeds" (i.e., actions to execute, goals to achieve, etc.).

To run the agent, JaCaMo uses "application files" (which names end with .jcm). In our example the application file is sag_hw.jcm, where we give a name to agent (bob) and an initial goal (say("Hellow World")). The content of this file is as follows:

1mas sag_hw {   // the MAS is identified by sag_hw
2   agent bob: hwa.asl { // initial plans for bob are in hwa.asl
3     goals: say("Hello World") // initial goal for bob
4   }
5}

The result of the execution is:

JaCaMo Http Server running on ...
[bob] Hello World

To better understand the output, the steps in executing the .jcm file are as follows:

  1. An agent named bob is created with initial beliefs, goals, and plans as determined from the contents of the file named hwa.asl.

  2. The goal say("Hello World") is delegated to bob creating the event +!say("Hello World").

  3. The plan in file hwa.asl, as shown above, is triggered and used to handle the event.

  4. The execution of the plan produces the output [bob] Hello World as the result of performing the internal action .print.

  5. The agent continues to run, but has nothing to do (as it is the only agent in the system and it has not generated any further goals itself, nor changes in the environment that might lead it to further action).

  6. As shown in the execution output, there is a URL to inspect the current state of the agents (their beliefs, intentions, plans, …​), and we later see that the same applies to environment and organisations.

2. Multiple agents coordinating by communication

We now consider two agents, bob and alice.

  • Agent bob prints "Hello" and

  • Agent alice prints "World".

2.1. Tentative 1

In order to create both agents from the same code (as in the previous example, having only one plan), we can use the following .jcm file:

1mas mag_hw {
2    agent bob: hwa.asl {
3      goals: say("Hello")
4    }
5    agent alice: hwa.asl {
6      goals: say("World")
7    }
8}
  • Execute the new .jcm file (Agents are just programmed with the file hwa.asl that we defined in previous step).

  • However, the result of the execution is not exactly what we expected. It could be as follows:

[alice] World
[bob] Hello

Agents run concurrently and asynchronously pursue their goals and thus this initial implementation cannot guarantee the order of the printed messages.

2.2. Tentative 2

Some coordination is required so that bob prints first and alice later. We can solve the problem with bob sending a message to alice as soon as its word is printed. The new program is as follows (to be included in a file named bob.asl):

1// bob.asl
2+!say(M) <- .print(M);
3            .send(alice,achieve,say("World")).

image

By sending an achieve message to alice, bob delegates the goal say("World") to alice. She uses the plan +!say(M) ← .print(M). to achieve the goal, as before.

Since alice’s goal now comes from bob, rather than at initialisation, the .jcm file has to be changed as follows:

1mas mag_hw {
2    agent bob { // file bob.asl is used
3      goals: say("Hello")
4    }
5    agent alice: hwa.asl
6}
  • Execute this new .jcm file (alice reuse the hwa.asl defined in previous step)

2.3. Tentative 3

The previous solution has a weakness: bob is strongly directing the execution of alice since this agent achieves any goal say sent from another agent.

In order to coordinate the two agents, instead of commanding alice, bob can only warns it that it has finished its task.

1// bob.asl
2+!say(M) <- .print(M);
3            .send(alice,tell,done).

It does so by sending alice the information done using the tell verb in the message so that alice interprets it as a belief and reacts to this new information.

1// alice.asl
2+done : say(M) <- !say(M).
3+!say(M) <- .print(M).

The .jcm file has to be changed as follows:

1mas mag_hw {
2    agent bob { // file bob.asl is used
3      goals: say("Hello")
4    }
5    agent alice { // file alice.asl is used
6      beliefs: say("World")
7   }
8}
  • Execute this new .jcm file

3. Multiple agents coordinating via the environment

The example now considers an environment with a board artifact, as a blackboard that agents can use to write messages and to perceive message written on it. In this version of the hello world example, bob writes a message "Hello" on the board blackboard and alice, who is observing the blackboard, writes the message "World" as soon as it acquires the belief that the message "Hello" has been written.

Environments are structured into workspaces; all agent within a workspace have shared access to all artifact instances in that workspace. In the application file, we can specify the initial set of artifacts and workspaces to be created when the MAS is spawned. In this case, the file is as follows:

 1mas sit_hw {
 2  agent bob {
 3    join: workshop      // bob joins workspace workshop
 4    goals: say("Hello")
 5  }
 6
 7  agent alice {
 8    join:  workshop     // alice joins workspace workshop
 9    focus: workshop.board   // and focus on artifact board
10  }
11
12  workspace workshop {  // creates the workspace workshop
13    artifact board: tools.Blackboard  // with artifact board from Blackboard artifact class
14  }
15}
  • The initial configuration includes a workspace called workshop, hosting a board artifact of type Blackboard of the package tools.

  • Both agents join the workspace at initialisation, in order to access the artifact (being able to observe and act on it).

  • Agent alice focus on (i.e., observe) the artifact. The focus is needed so that the agent alice is attentive to changes in the observable properties of that artifact: when something is written on the board, next time alice senses the environment a belief corresponding to the observed artifact property will be automatically created and she can properly react to it.

Artifacts are implemented in Java. The source code (in file Blackboard.java) of the simple blackboard artifact follows:

 1package tools;
 2import cartago.*;
 3
 4public class Blackboard extends Artifact {
 5  // names of observable property as well as actions must start with downcase letter
 6  void init() {
 7    defineObsProperty("lastMsg","");
 8  }
 9
10  @OPERATION void writeMsg(String msg) {
11    System.out.println("[BLACKBOARD] " + msg);
12    getObsProperty("lastMsg").updateValue(msg);
13  }
14}

Java classes are used as templates for defining artifacts, using annotated methods to define artifact operations and predefined methods inherited by Artifact API to work with observable properties and the other artifact mechanisms.

image

The source code for bob in this case becomes:

1// bob.asl
2+!say(M) <- writeMsg(M).
3
4// include of basic plans for handling cartago infra.
5{ include("$jacamoJar/templates/common-cartago.asl") }

that is, the agent uses the action writeMsg provided by the artifact to write the message on the blackboard. The source code for alice instead is:

1// alice.asl
2// triggering event corresponds to obs. prop. of artifact
3+lastMsg("Hello") <- writeMsg("World!"). // action is the board artifact operation
4
5{ include("$jacamoJar/templates/common-cartago.asl") }

the agent (who is observing the board) writes the message "World" has soon as it has the belief that the last message written on the blackboard (made observable by means of the lastMsg observable property) is "Hello".

  • Execute this new .jcm file

[alice] joinned workspace workshop
[alice] focusing on artifact board (at workspace toolbox)
using namespace default
[bob] joinned workspace workshop
[BLACKBOARD] Hello
[BLACKBOARD] World!

The execution of the .jcm file produces a result similar to the previous ones, except that now it is the blackboard artifact printing out the messages (see trace starting with [BLACKBOARD]) and no communication between bob and alice are required.

4. Multiple agents participating to an organisation

We now "organise" the set of agents to produce the "Hello World" message. An agent organisation can be used to regulate and coordinate the agents. Although the example is simple, the use of an organisation makes explicit and facilitates the changing of the specified coordination and regulation patterns. We assume here that agents are compliant and use the organisation as a coordination tool. Coordination will be used to achieve the goal show_message, a goal that should be achieved by the two agents working together. This goal is thus called an organisational goal to distinguish it from an agent goal.

We use a social scheme to program how the organisational goal is decomposed into sub-goals that are assigned to the agents. For the decomposition, the goal has one sub-goal for each word of the message. For their assignment to agents, we define a mission for each sub-goal. In order to participate in the scheme execution, agents should commit to a mission and achieve the corresponding goal(s). Committing to a mission is a form of promise to the group of agents collectively working on a scheme: "I promise that, when required, I will do my part of the task". When agents have committed to all missions, the scheme can be performed with the guarantee that we have enough agents to work on all required sub-goals.

This organisation example also defines a single role that all agents will play: the role greeter played in a group type identified by gg (standing for "greeting group"). Agents playing this role (and only them) are permitted to commit to the missions of the scheme as defined by the norm. The program of this organisation is written in XML as follows:

 1<?xml version="1.0" encoding="UTF-8"?>
 2
 3<organisational-specification
 4  id="hello_world"
 5  os-version="0.8"
 6
 7  xmlns='http://moise.sourceforge.net/os'
 8  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
 9  xsi:schemaLocation='http://moise.sourceforge.net/os
10                      http://moise.sourceforge.net/xml/os.xsd' >
11
12<structural-specification>
13  <group-specification id="gg">
14    <roles>
15      <role id="greeter" max="2"/>
16    </roles>
17  </group-specification>
18</structural-specification>
19
20<functional-specification>
21  <scheme id="hw_choreography">
22    <goal id="show_message">
23      <plan operator="sequence">
24        <goal id="show_w1"/>
25        <goal id="show_w2"/>
26      </plan>
27    </goal>
28
29    <mission id="mission1"  min="1" max="1"> <goal id="show_w1"/> </mission>
30    <mission id="mission2"  min="1" max="1"> <goal id="show_w2"/> </mission>
31  </scheme>
32</functional-specification>
33
34<normative-specification>
35  <norm id="norm1"  type="permission" role="greeter" mission="mission1"/>
36  <norm id="norm2"  type="permission" role="greeter" mission="mission2"/>
37</normative-specification>
38
39</organisational-specification>

image

Agents play the role greeter and commit to missions to show their words (bob shows "Hello" and alice shows "World"). Each agent has its own mission/goal/word to show. Through the definition of the norm norm1 and norm2 a greeter is permitted to commit to any mission. Since we do not want every agent to commit to all missions they possibly can, each agent has a belief for the mission it should commit to. These beliefs are my_mission(mission1) for bob and `my_mission(mission2) for alice. The decision to commit to a mission is implemented by the following plan:

 1// hwa.asl
 2// when the organisation gives me permission to commit to a mission M in scheme S,
 3// do that if it matches the belief my_mission
 4+permission(A,_,committed(A,M,S),_)
 5    : .my_name(A) & // the permission is for me
 6      my_mission(M) // my mission is M
 7<- commitMission(M).
 8
 9// when I have goal show_w1, create sub-goal say(...)
10+!show_w1 <- !say("Hello").
11+!show_w2 <- !say("World").
12
13+!say(M) <- writeMsg(M).
14
15// basic behaviors to handle artifacts
16{ include("$jacamoJar/templates/common-cartago.asl") }
17// basic behaviors to handle organisations
18{ include("$jacamoJar/templates/common-moise.asl") }
19// basic behaviors to obey norms
20{ include("$moiseJar/asl/org-obedient.asl") }
  • The symbol + in line 4 means "in the event of coming to believe …​"; the code after : are conditions on what the agents believes to be the current situation which are required for the plan to be used; and the code after are "deeds" (i.e., actions to execute, goals to achieve, etc.).

  • This plan is thus triggered by the addition of a belief that agent has the permission to commit to mission M in scheme S.

    If the value of variable M in the agent belief my_mission(M) matches the permitted mission M, the plan is applicable for the event and the agent does the action of committing to mission M.

  • The leaf goals of the social scheme should be achieved by the agents and so they have plans for that: The plan in line 10 can be read as "in the event of having a new goal" show_…​, create a new sub-goal !say(…​)

  • As for the permission belief, goals show_w…​ will come from the organisation.

    The organisation informs the agents about the goals they have to pursue considering the current execution state of the scheme and the commitments of the agent.

  • Agents have the know-how to execute the complete task, i.e. they all have plans for all show_w…​ goals (i.e., the agents have the know-how to show both words, which one will do what depends on the missions they commit to).

Briefly, agents have plans to react to events produced by the organisation (new permissions and new goals) and do not need to explicitly coordinate among themselves through communication; that is, bob does not need to send a message to alice anymore. Neither is the environment used for coordination.

The file .jcm for this implementation of the Hello World is as follows:

 1mas hello_world {
 2
 3  agent bob : hwa.asl {
 4    focus: workshop.board
 5    roles: greeter in ghw
 6    beliefs: my_mission(mission1)
 7  }
 8
 9  agent alice : hwa.asl {
10    focus: workshop.board
11    roles: greeter in ghw
12    beliefs: my_mission(mission2)
13  }
14
15  workspace workshop {
16    artifact board : tools.Blackboard
17  }
18
19  organisation greeting : org1.xml {
20    group ghw : gg {
21      responsible-for: shw
22    }
23    scheme shw : hw_choreography
24  }
25}

As before, this file has entries for agents and workspaces, but now an organisation field is added.

  • In line 19, an organisation entity is created based on the XML file that describes the type of groups and schemes available in the organisation.

  • One group entity is created in line 20 (identified by ghw) and one scheme entity is created in line 23 (identified by shw). Line 21 states that group ghw provides the agents for the execution of scheme shw.

  • Lines 5 and 11 assign role greeter to our agents in group ghw.

  • Lines 6 and 12 add beliefs in the agents regarding the missions to which they should commit.

The execution of the file happens as follows:

  1. The workspace greeting and its artifact are created.

  2. The group and scheme entities are created and linked (responsible_for).

  3. Agents bob and alice are created, they join the workspace workshop.

  4. The agents are assigned the role greeter.

  5. By playing this role, they start believing

    • permission(bob,,committed(bob,mission1,shw),)

    • permission(bob,,committed(bob,mission2,shw),)

    • permission(alice,,committed(alice,mission1,shw),)

    • permission(alice,,committed(alice,mission2,shw),)

  6. The addition of these beliefs trigger their first plan and they commit to their missions.

  7. When the agents have committed to their missions, the scheme shw has enough agents for it to be carried out and the goal show_w1 can be finally pursued.

  8. Agent bob, being committed to mission1, is informed that goal shwo_w1 can be adopted and it does so; the message "Hello" is written on the blackboard.

  9. Agent alice is then told to achieve show_w2 and it does so; the message "World" is written on the blackboard.

  10. The scheme is finished.

  • Execute this new .jcm file

  • We can notice a coordinated behaviour: the words are always shown in the correct order. Moreover, the coordination is implemented in the organisation, not in the agents (there is no code in the agents to coordinate their individual actions so that the overall system behaviour is as expected).

5. Further steps

  1. Change the implementation all versions of the Hello World application to consider:

    • a new message with three words: "Hello Wonderful World" and a third agent for the new word;

    • that the order the words are printed is not relevant and they can be printed in parallel;

    • that words should be printed in reverse;

    • that, as soon as the message is printed, it should be printed again and again;

      For each proposed change, evaluate which version (coordination by message, artifact or organisation) is easier to implement.

  2. Change the agents so that they are capable to print the hello world message in different languages and implement a mechanism to easily change the language.

  3. In the last version using the organisation, change permission to obligations.