This guide builds on the previous guide that described how to Create a Multi-Agent System with ASTRA. Here, we explore how to create multiple agents that communicate with one another using an Agent Communication Language (ACL). ACLs are a type of message format that is inspired by theories of human communication. Messages broadly consist of a sender, a (set of) receivers, a performative (message type) and some content. ACL messages also include other features relating to the meaning of the content and to support conversation management. We will not discuss these other features in this guide.

What you’ll build

You’ll build and deploy a simple application consisting of a self-replicating agent program. The agent replicates upon receipt of a message from another agent. Specifically, we will require the sender of the message to be the agent that created the replica. The message will contain a number (initially 1 for the first replica). Upon receipt of this message, the agent will create another agent and send it a message with the number 2. This behaviour will continue until an agent is created that receives a message containing the number 5. This agent will stop the replication behaviour.

What you’ll need

Learn what you can do with ASTRA

In this guide, we will start to explore how agents interact with one another using an Agent Communication Language (ACL). ACLs are based on a set of performatives that apply context to some associated content. In ASTRA, we use the Foundation for Intelligent Physical Agents (FIPA) ACL. Details of FIPA ACL can be found here.

An example of a FIPA ACL performative is request. This defines a type of message that can be used to ask another agent to do something (e.g. I request that you open the door). In this example, the creator agent will use an request message to ask the agent it created to replicate. The agent acts (does what is requested) if the associated replication number is less than 5. Otherwise, it does nothing. Later in this guide we will explore how this behaviour can be implemented more comprehensively using a protocol.

Create a Replicator agent

In this example, a Replicator agent is an agent that is able to create another copy of itself. The agent code is given below:

src/main/astra/Replicator.astra
agent Replicator {
    module Console console;
    module System system;

    types replicate {
        formula replicate(int);
    }

    rule @message(request, string sender, replicate(int value)) {
        console.println("Created agent with: " + value);
        if (value == 5) {
            console.println("Limit reached - replication stopped");
        } else {
            system.createAgent("agt_"+value, "Replicator");
            send(request, "agt_"+value, replicate(value+1));
        }
    }
}

There are a few things to note:

  • This agent receives and sends a request, replicate(...) message. The content of all messages must be declared explicitly using a types ... { } construct.
  • The @message(...) event type is used to declare a mesasge event. All incoming messages are placed on the agents internal event queue and are processed in order. The event contains 3 arguments: the performative (message type), the name of the sender, and the content of the message.
  • The @message rule handles an incoming message from any sender with type request and whose content matches the pattern replicate(int value).
  • After thecreateAgent(...) action is used to create an agent, the send statement is used to send a message to that agent. This message is of type request and the content is replicate(…) where the parmeter is 1 greater than the value the agent received. This triggers the replicate behaviour in the receiver agent.

Create a Main agent

The Replicator agent program defines the replication behaviour, but it does not define how to start the behaviour. This is done through the creation of othe first replicator and the transmission of an initial replication message to that replicator. Implementing this initial behaviour is dones through by a separate Main agent.

src/main/astra/Main.astra
agent Main {
    module Console console;
    module System system;
    
    types replicate {
        formula replicate(int);
    }

    rule +!main(list args) {
        console.println("Starting Replication...");

        system.createAgent("agt_0", "Replicator");
        send(request, "agt_0", replicate(1));
    }
}

As can be seen, this agent does the same thing as the else branch of the Replicator agent. One intresting point to note is the replication of the types construct. This happens because both the Replicator and the Main agent programs interact with one another. In ASTRA, we try to minimise replicated code, so when such situations arise, we make use of the built in inheritance mechanism and move the shared code into a separate agent program. We discuss this after we run our first version.



Run the application

To run the application, simply type the following command in the project root folder:

mvn

The maven build file includes a default goal that is executed. You can do the same by entering the following line:

mvn clean compile astra:deploy

While there will be a lot of output, at the end you should see:

loading class: Replicator
[agt_0]Created agent with: 1
[agt_1]Created agent with: 2
[agt_2]Created agent with: 3
[agt_3]Created agent with: 4
[agt_4]Created agent with: 5
[agt_4]Limit reached - replication stopped

Creating a ReplicatorCommon agent

Agents that are instances of the Replicator and Main agent programs interact with one another by sending and receiving messages. As we have seen above, the content of any message must be declared explicitly using a types construct which defines the signature of the different predicate formulae that an agent understands.

Because both agent programs share the same types definition, best practice in ASTRA is to move the shared code into a separate agent program so that it is declared in only one place. For this example, the shared program will be called ReplicatorCommon.

src/main/astra/ReplicatorCommon.astra
agent ReplicatorCommon {
    types replicate {
        formula replicate(int);
    }
}

This above code can then be reused in the Replicator and Main agent programs

Revised Replicator agent

agent Replicator extends ReplicatorCommon {
    module Console console;
    module System system;

    rule @message(request, string sender, replicate(int value)) {
        console.println("Created agent with: " + value);
        if (value == 5) {
            console.println("Limit reached - replication stopped");
        } else {
            system.createAgent("agt_"+value, "Replicator");
            send(request, "agt_"+value, replicate(value+1));
        }
    }
}

Revised Main agent

agent Main extends ReplicatorCommon {
    module Console console;
    module System system;

    rule +!main(list args) {
        console.println("Starting Replication...");

        system.createAgent("agt_0", "Replicator");
        send(request, "agt_0", replicate(1));
    }
}

Running this code will not change the output, but internally, the code is better structured as there is no replication.

Summary

Congratulations! You created and deployed your first interaction-based multi-agent system using ASTRA and Maven. This should allow you to provide a basis for understanding how to agent communication works. From this point, your next step should be to explore the concept of protocols, which provide the agent with more feedback about the success or failure of its request.

See Also

The following guides may also be helpful: