Amine Multi-Agent Systems (MAS) Layer

by

Adil KABBAJ*

 

 

* This document has been written by Adil but the development/implementation of Amine's MAS layer was done mainly by Kaoutar EL HARI and Karim BOUZOUBAA.

 

Introduction

Amine MAS Layer

Applications

 

 

Introduction

Instead of developing our own "lower level" for Amine's multi-agents system layer, we (Adil and Karim) decided to use available open-source Java “Agent Development Environments” (ADE) in conjunction with Amine: the ADE should provide the lower/physical level for Agents and MAS programming (i.e. creation and communication between agents using network protocols) and Amine for the higher/cognitive level (i.e. cognitive and reactive capabilities of the agents, like ontology, conceptual structures and operations, inference engines, learning capabilities, etc.).

Thus, our approach in Amine's MAS layer is to incorporate open-source Java "Agent Development Environments (ADE)" and then to develop various classes of "Amine Agents" and "Amine MAS" that join Amine components to constructs provided by various ADEs. We have implemented this approach with Jade and we plan to perform the same task with other Java open-source ADEs like Beegent.

In her diplôma (DESA), Kaoutar ElHari explored the use of Amine and Jade. Jade allows the creation of Agents (i.e. it offers a Java class Agent and manages the underlying network processing), the use of different kinds of behaviours and the communication with ACL according to FIPA specification. The Renaldo MAS that is presented in Application has been developed with this "integration" of Amine-Jade platform. Several (previous) students (that worked with Adil) have participated to the development of Renaldo: Amal Zouaq, Meriem Benoutmane and Omar Merroun.

Adil Kabbaj and Karim Bouzouba worked on the conception of Amine's MAS layer. Its development/implementation has been done mainly by Karim and Kaoutar. The current status of Amine's MAS layer is just the first step toward a more general and operational MAS layer.

 

Amine MAS Layer

Amine MAS layer is composed of two packages: agent and mas. There are also simple GUIs that are associated to Agents and MAS respectively (see Figure 1 for an example).

 

Package agent

Package Agent provides the Java interface AmineAgent which is implemented by the class AmineJadeAgent. Indeed, class AmineJadeAgent extends Jade Agent and implements AmineAgent interface. If another JADE is used, like BeeGent for instance, then a new class should be defined: AmineBeeGentAgent that should extend BeeGent Agent and implement AmineAgent interface.

Here is the interface AmineAgent: it specifies a method to setup the agent, a method to initiate goal satisfaction, methods to send messages (synchronous and asynchronous sending operations) and a method to listen to incoming messages.

    public interface AmineAgent {

        public void setup();

        public void satisfyGoal(String goal);

        public void send(AmineAgent receiver, String content);

        public void send(String receiver, String content);

        public Object sendAndWait(AmineAgent receiver, String content);

        public Object sendAndWait(String receiver, String content);

        public void listen();

    }

Class AmineJadeAgent provides a partial implementation of the interface AmineJadeAgent : it provides a general implementation, using Jade platform, of setup, send/sendAndWait and listen methods, but it doesn't provide an effective implementation of satisfyGoal method. The class AmineJadeAgent is itself specialized by another class: PPCGAmineJadeAgent. Each agent PPCGAmineJadeAgent has a proper Prolog+CG interpreter that is used to satisfy its goals and to handle the received messages. Thus, an effective implementation of the method satisfyGoal is provided by the class PPCGAmineJadeAgent (i.e. it uses the Prolog+CG interpreter of the agent). All cognitive/intentional capabilities of a PPCGAmineJadeAgent agent (ontology of the agent, knowledge, beliefs, intentions, plans, etc.) are represented/implemented as Prolog+CG programs (see Application for more detail).

Also, PPCGAmineJadeAgent redefines methods of AmineJadeAgent to take into account the presence of its Prolog+CG interpreter. See the source code for more detail and for the definition of other related classes (like Answer and Ask).

 

Package mas

Package mas provides the interface IAmineMAS which is implemented by the abstract class AmineMAS. The class AmineMAS implements most of the methods of IAmineMAS that are basic methods to handle a Multi-Agents System: a) AmineMAS has a hashtable that is used to store information about the agents in the current MAS, b) basic methods to initiate the activation of a MAS. Here is the specification of the interface IAmineMAS:

    public interface IAmineMAS {

        public boolean isIdAgent(Identifier id);

        public void setNbrAgents(int i);

        public int getNbrAgents();

        public void init(String name, String[] args);

        public void init(String[] args);

    }

 

The class AmineMAS is abstract (it can't be instantiated) because the method init(args) is declared as abstract; it is not implemented. The method init(name, args) creates a MAS with the specified name (the first parameter) and the specified array of "agent parameters". init(name, args) calls the method init(args) which is responsable for the creation of the agents and for the effective boot of the MAS. This method has no body in the abstract class AmineMAS

The abstract class AmineMAS is specialized (extended) by the class AmineJadeMas. This class provides a specific implementation of the method init(args). If another JADE is used, like BeeGent for instance, then a new class should be defined: AmineBeeGentMas that should specialize/extend AmineMAS.

The class AmineJadeMas provides the following implementation for the method init(args):

public void init(String[] args) {

    new Boot(args);  // Boot() is a method of Jade

}

 

init(args) is really the method responsible for the creation of the MAS. args is an array of Strings. Each element of this array represents parameters of one agent. That means that the size of the array corresponds to the number of agents of the MAS. It is not to the programmer to create the agents (as processes with network protocols, etc.), the programmer only provides parameters for every agent. The call to Boot() from method init(args) would create the associated processes for every agent. The programmer can then have access to these agents (pointer to the corresponding process) using the getAgentById method.

Thus, args is an array of Strings containing parameters of agents. For AmineJadeAgent, each String in the array of Strins args should respect the following structure:   

    agent_name:completeName_of_agent_class(Param1 Param2 Param3 ... ParamN)

The last parameter should be the ontology of the agent. For example, the String "John:aminePlatform.agent.amineJade.AmineJadeAgent(John.pcg  ontology.xml)" represents an agent that has the name John, its class is aminePlatform.agent.amineJade.AmineJadeAgent and its arguments are the two files "John.pcg" and "ontology.xml".

Creation of a MAS AmineJadeMas can be done with a simple call to the constructor of AmineJadeMas. Here is an example:

         // Specification of the agents. In our example, we have two agents: John and Arthur. Both are PPCGAmineJadeAgent

        String arg1 = "John:aminePlatform.agent.amineJade.ppcgAgent.PPCGAmineJadeAgent(John.pcg Environnement.pcg ontology.xml)";

        String arg2 = "Arthur:aminePlatform.agent.amineJade.ppcgAgent.PPCGAmineJadeAgent(Arthur.pcg Environnement.pcg ontology.xml)";

        // put these arguments in an array

        String[] args = {arg1, arg2};

        // call the constructor of a MAS by specifying its name and the agent arguments array. MAS Renaldo will create the two agents

         new AmineJadeMas("Renaldo", args);

See the application Renaldo below for more detail.

 

Applications

In this section, we present Renaldo; a MAS that concerns the simulation of a child story. The setting of the story is a forest; it corresponds to the environment of Renaldo. The characters of the story (the bear John, the bird Arthur, the bee Betty, etc.) are the agents of Renaldo. Each type of agents (bear, bird, bee, etc.) has a set of attributes, knowledge, goals, plans and actions that are specified as Prolog+CG programs. A specific agent can have, in addition, specific attributes, knowledge, goals, plans and actions, specified also as Prolog+CG programs.

The idea of Renaldo comes from TALE-SPIN; a program that has been developed in 70 in order to generate a child story.

We give first the definition of the MAS Renaldo and then we present some details on the agents of Renaldo. See source code (aminePlatform/applications/mas/renaldo) for more detail.

 

The MAS Renaldo:

The MAS Renaldo is implemented as a Java class RenaldoApplication:

public class RenaldoApplication {

public static void main(String[] arguments) {

         // Specification of agent: AgentName:CompleteName_of_Agent_Class(Param1 Param2 Param3  ... ParamN)

        String arg1 = "John:aminePlatform.agent.amineJade.ppcgAgent.PPCGAmineJadeAgent(John.pcg Environnement.pcg ontology.xml)";

        String arg2 = "Arthur:aminePlatform.agent.amineJade.ppcgAgent.PPCGAmineJadeAgent(Arthur.pcg Environnement.pcg ontology.xml)";

        // put these arguments in an array

        String[] args = {arg1, arg2};

        // call the constructor of a MAS by specifying its name and the agent arguments array. MAS Renaldo will create two agents: John and Arthur

        new AmineJadeMas("Renaldo", args);

        // you can retrieve each agent object by calling the static getAgentById method. The Id of the agent is specified as a String

        PPCGAmineJadeAgent John = (PPCGAmineJadeAgent) AmineJadeMas.getAgentById("John");

        // use this agent object to call the predefined methods to satisfy goal or send messages between agents.

        // In the following case, we assign to john a goal to satisfy that is expressed as a CG

        John.satisfyGoal("Animal(John, Goal)::[Animal : John]<-pat-[SatisfyHungry]-intensityOf->[Hungry = _Level]");

    }

}

 

The agent John in the MAS Renaldo

We will focus on the agent John. The same principles hold for the other agents like Arthur. The "body" of John is the PPCGAmineJadeAgent class. The "mind" of John is specified by its parameters: Prolog+CG files "john.pcg", "Environment.pcg" and "ontology.xml". These parameters are considered by the Prolog+CG interpreter that is bind to the agent (recall that each agent PPCGAmineJadeAgent has its own Prolog+CG interpreter).

File "Environment.pcg" is a Prolog+CG program that is common to all Renaldo agents and that contains information about the environment, like position of various objects, trajectories, etc. Here is parts of file Environment :

// The fact that Food honey is at position (3, 11) with quantity 100

Environment( Food ):: [Food = "honey"] -

                                            -locatedIn-> [Position = [3,11]],

                                            -exists-> [Quantity = 100 ].

Environment( Food ):: [Food = "honey"] -

                                            -locatedIn-> [Position = [24,6]],

                                            -exists-> [Quantity = 250 ].

// The fact that Agent John is at position (5, 5) and it is of type Bear

Environment( Agent ):: [Agent = John] -

                                            -locatedIn-> [CurrentPosition = [5,5]],

                                            -typeOf-> [Type = Bear ].

// The fact that trajectory Food1 is defined by the points (5, 5), (3, 5), (4, 7), (2, 9), (3, 11), that there is honey at position (3, 11) which is Food

Environment( Trajectory ):: [ Trajectory = "Food1"]-

                                            -has-> [ List = [ [5,5] , [3,5] , [4,7] , [2,9], [3,11] ] ],

                                            -obj-> [ Object = "honey" ],

                                            -typeOf-> [ Type = Food ].

 

File "john.pcg" is a Prolog+CG file that contains knowledge, belief, goals, plans and actions of agent John. Here is parts of "john.pcg":

    Bear( John).

    Bear( John, Personality):: [Bear : John] <-actor- [Character] -has-> [List = [Naive, dishonest, Vanity, Lasy]].

    //knowledge and relationships of the agent

    Bear( John, Friend, Knowledge ):: [Bear : John] -has-> [List = [ [Agent ] <-typeOf- [ Type = Bird] -actor-> [Name = Arthur],

                                                                                            [Agent ] <-typeOf- [ Type = Bee] -actor-> [Name = Betty]

                                                                                        ]].

    Bear( John, Friend, Knowledge ):: [Type = Agent ] -

                                                                -locatedIn-> [Position = [16,7]],

                                                                -follow-> [Trajectory = "Agent1"],

                                                                <-typeOf- [ Type = Bird] -actor-> [Name = Arthur].

    Bear( John, Food, Knowledge ):: [Type = Food ] -

                                                                -locatedIn-> [Position = [3,11]],

                                                                -follow->[Trajectory = "Food1"].

    Bear( John, Knowledge ):: [Bear : John] -locatedIn-> [CurrentPosition = [5,5]].

    Bear( John, Perception ):: [Bear : John] -has-> [Perception = 2].

 

// Goal of an Animal

Animal(_Agent, Goal)::[Animal : _Agent]<-pat-[SatisfyHungry]-intensityOf->[Hungry = _Level] :-

    Animal(_Agent, Plan)::[Animal : _Agent]<-actor-[Plan2_SatisfyHungry]-intensityOf->[Hungry = _Level].

 

// Plan for SatisfyHungry

Animal( _Agent, Plan ):: [Animal : _Agent] <-actor-[Plan1_SatisfyHungry] -intensityOf-> [Hungry = _Level] :-

    getAnimalPosition( _Agent, _CurrentPosition ),

    Animal( _Agent, Procedure ):: [Animal : _Agent] <-actor- [PerceptionOf]-

                                                                                            -id-> [Name = "Neigberhood"],

                                                                                            -obj-> [Food = _Food],

                                                                                            -locatedIn-> [Position = _FoodPosition],

    Animal( _Agent, Procedure ):: [Animal : _Agent] <-actor- [Trajectory]-

                                                                                            -id-> [Name = "Final"],

                                                                                            -locatedIn-> [CurrentPosition = _CurrentPosition],

                                                                                            -to-> [TargetPosition = _FoodPosition],

    getQuantityOfFoodForAnimal( _Agent, _Food, _Level, _Quantity ),

    Animal( _Agent, PrimitiveAction ):: [Animal : _Agent] <-actor- [Eat] -

                                                                                            -obj-> [Food = _Food]

                                                                                            -numberOf-> [Quantity = _Quantity],

                                                                                            -locatedIn-> [Position = _FoodPosition], !.

 

// Another plan is to ask a friend

Animal( _Agent, Plan ):: [Animal : _Agent] <-actor-[Plan2_SatisfyHungry] -intensityOf-> [Hungry = _Level] :-

    getAnimalPosition( _Agent, _CurrentPosition ),

    print("I am now in "), print (_CurrentPosition),

    Animal( _Agent, Procedure ):: [Animal : _Agent] <-actor- [PerceptionOf]-

                                                                                            -id-> [Name = "Neigberhood"],

                                                                                            -obj-> [Agent = _OtherAgent],

                                                                                            -locatedIn-> [Position = _OtherAgentPosition],

    Animal( _Agent, Procedure ):: [Animal : _Agent] <-actor- [Trajectory]-

                                                                                            -id-> [Name = "Final"],

                                                                                            -locatedIn-> [CurrentPosition = _CurrentPosition],

                                                                                            -to-> [TargetPosition = _OtherAgentPosition],

    AskAnimalFriendForFood( _Agent,_OtherAgent),

    Animal( _Agent, Plan ):: [Animal : _Agent] <-actor- [Plan1_SatisfyHungry] -intensityOf-> [Hungry = _Level],!.

 

Goal AskAnimalFriendForFood involves sending messages: agent _Agent will send two messages to his friend _Friend. Let us consider how it is done in detail. Note first that values of _Agent and _Friend will be two Individuals, in this case Individuals John and Arthur. They are specified in the Ontology: John as an Individual of Bear and Arthur as an Individual of Bird. Prolog+CG considers identifier John as a reference to an Individual Conceptual Structure, and the same for Arthur. So, the first task is to get the string name of the Individuals Arthur and John, which are "Arthur" and "John" respectively. That it is why the body of (the plan to satisfy the goal) AskAnimalFriendForFood contains:

    getLexicon(_lexicon),

    _sFriend is _Friend:toString(_lexicon),

getLexicon(_lexicon) is a primitive goal of Prolog+CG: it returns in _lexicon the current lexicon. The method toString(_lexicon) of an Individual returns the string name of the Individual. We need the string name of _Friend to constitute the content of the message which is:

    "Bird(_Friend, Food, Knowledge )::[Type = Food]-locatedIn->[Position=_FoodPosition]"

In the above message, sent by agent _Agent to agent _Friend, the variable _Friend should be replaced by its value. But in our case, its value is a Conceptual Structure Individual. So the first step it to get the name (i.e. "Arthur") from the CS Individual (i.e. Arthur). The above code (call to getLexicon() followed by the use of toString()) implements this first step. As a consequence of the difference between _Friend that referes to an Individual (Arthur) and _sFriend that referes to the string name ("Arthur") of that Individual, the above message has to be re-expressed as follows (where the operator + stands for the concatenation of string) :

    "Bird(" + _sFriend + ", Food, Knowledge )::[Type = Food]-locatedIn->[Position=_FoodPosition]"

The following Prolog+CG code implements the above instruction:

    _content1 is "java.lang.StringBuffer":New(),  // a Java message that enables the creation of a StringBuffer object

    concat(["Bird(", _sFriend, ", Food, Knowledge )::[Type = Food]-locatedIn->[Position=_FoodPosition]"], _content1), // defined goal: concatene the

                                                                                                elements of the list in the StringBuffer refered by  _content1

    _scontent1 is _content1:toString(), // get the String because "sendAndWait" primitive requires a String (not a StringBuffer)

The agent _Agent will send two messages to his agent _Friend: the first message is about the FoodPosition and the second message about NameTrajectory (to know what trajectory to follow in order to reach FoodPosition). Here is the two sending messages (the goal agentSend_Wait is defined below):

    // _Agent send to _sFriend a message with content _scontent1 and it will wait for a result that will be put in _FoodPosition

    agentSend_Wait(_Agent, _sFriend, _scontent1, _FoodPosition)

    agentSend_Wait(_Agent, _sFriend, _scontent2, _NameTrajectory) // the same holds for this second sending message

 

Here is now the complete definition of AskAnimalFriendForFood :

AskAnimalFriendForFood(_Agent,_Friend) :-

    print("\nI will ask my friend for food"),

    getLexicon(_lexicon),

    _sFriend is _Friend:toString(_lexicon), // get the String name of _Friend

 

    _content1 is "java.lang.StringBuffer":New(), // construct the content of the first message, but replace _Friend by the value of _sFriend

    concat(["Bird(", _sFriend, ", Food, Knowledge )::[Type = Food]-locatedIn->[Position=_FoodPosition]"], _content1),

    _scontent1 is _content1:toString(),

    agentSend_Wait(_Agent, _sFriend, _scontent1, _FoodPosition), // sending first message

 

    _content2 is "java.lang.StringBuffer":New(), // construct the content of the second message

    concat(["Bird(", _sFriend, ", Food, Knowledge )::[Type = Food]-follow->[Trajectory = _NameTrajectory]"],_content2),

    _scontent2 is _content2:toString(),

    agentSend_Wait(_Agent, _sFriend, _scontent2, _NameTrajectory), // sending second message

 

    getTrajectory( _Object, _ListTrajectory, _NameTrajectory),

    memberFood( _Agent, _Object ),

    fullfilFoodAnimalKnowledge( _Agent, _FoodPosition, _NameTrajectory),

    print("I am going to this food "),!.

 

Here is now the definition of agentSend_Wait : since the value of _sender will be a CS Individual (for instance Individual John), we need first the string name of _sender because the static method "sendAndWait" of PPCGAmineJadeAgent requires a String (not an Individual).

    agentSend_Wait(_sender, _receiver, _content, _result):-

        getLexicon(_lexicon),

        _senderName is _sender:toString(_lexicon),

        _result is "aminePlatform.agent.amineJade.ppcgAgent.PPCGAmineJadeAgent":sendAndWait(_senderName , _receiver, _content).

 

Execution of Renaldo MAS

The activation of the MAS Renaldo, which is implemented as a Java class RenaldoApplication (see above), provides the following outcome: Figure 1.a shows the frame of the MAS Renaldo. It provides a summary of the creation of and communication between agents in Renaldo MAS. Figure 1.b shows the frame of the agent John and Figure 1.c shows the frame for the agent Arthur. Recall that John and Arthur are two agents (implemented as processes or threads) that are concurrents. So the two frames (Figures 1.b and 1.c) should be read in parallel !

(a) Frame for the MAS Renaldo

(b) Frame for agent John

 

(c) Frame for agent Arthur

Figure 1: Renaldo MAS

 

Conclusion

While the above work illustrates our method/approach to integrate MAS platforms (like Jade) to Amine to provide constructs for the development of agents and Multi-Agents Systems (like Renaldo), much is needed to get an operational MAS layer for Amine. Also, our current implementation of MAS Renaldo is just a prototype that needs a lot of work to make it an effective MAS. For instance, the current formulation of agent knowledge, goal, plan etc., using CG and Prolog+CG, is very "ad hoc". Much work is needed on this aspect.

 

Future works

We plan to enhance the implementation of Amine MAS layer. Another important future work will concern a better formulation and organization of Renaldo.

Also, we plan to investigate the development of several kinds of agents (especially agents with more and more "cognitive" capabilities). For instance, components of a typical agent in Amine platform can include an ontology (with lexicons) that play the role of the agent’s memory, a description and conceptual language (CG in occurrence) with processing capabilities (Matching operations), the dynamic ontology engine to perform an automatic and incremental integration in memory of the continues stream of information that an agent receive from his environment, Prolog+CG for the inference-based processes and Synergy for the activation and propagation-based processes.

Karim Bouzoubaa worked in his Ph. D. on agents communication. In CG-KQML, we showed how Prolog+CG can be used to support agent communication language and agent inferences and planning processes. An important future work will be to illustrate Bouzoubaa work on agent communication using Amine MAS layer.

 

References

- K. Bouzouba, Modélisation des interactions basée sur le point de vue des agents, Ph.D. Thesis, Laval University, Canada, 1998.

- K. Bouzoubaa, B. Moulin and A. Kabbaj, CG-KQML+: An Agent Communication Language and its use in a Multi-Agent System, in 9th Int. Conf. on Conceptual Structures, ICCS'2001, Springer-Verlag, 2001.

- K. El Hari, The use of Amine Platform in the development of Multi-Agent Systems, DESA, EMI, Rabat, 2005.