Agent Communication
Agents in a multi-agent system cooperate by exchanging messages. ASTRA bases its messages on FIPA ACL, the Agent Communication Language defined by the Foundation for Intelligent Physical Agents (FIPA). This section explains the main ideas behind FIPA ACL and how they are expressed in ASTRA. For a step-by-step introduction, see the Multi-Agent Communication with ASTRA tutorial; for conversations that involve several messages, see Interaction Protocols.
Messages as actions
FIPA ACL builds on speech act theory: the idea that saying something is a kind of action, performed with an intention. When one agent tells another that the door is open, it is trying to change what the other agent believes. When it asks another agent to open the door, it is trying to get the other agent to adopt a goal.
To make this intention explicit, every message carries a performative that says what kind of act the message is (informing, requesting, refusing, and so on), along with the content that the act is about. The same content can mean very different things with different performatives: inform with the content open(door) tells the receiver that the door is open, while request with the same content asks the receiver to open it.
The parts of a message
A FIPA ACL message has a number of fields. The table below lists the main ones and how they relate to ASTRA.
Field |
Meaning |
In ASTRA |
|---|---|---|
performative |
The kind of communicative act |
The first argument of |
sender |
The agent sending the message |
Set automatically; available as the second argument of |
receiver |
The agent (or agents) the message is for |
The second argument of |
content |
What the act is about |
A formula, which must be declared in a |
language |
How the content is written |
Always ASTRA formulae |
protocol |
The interaction protocol the message is part of |
Optional: |
conversation-id |
Identifies the conversation the message belongs to |
Optional: |
reply-with, in-reply-to, reply-by |
Link replies to the messages they answer, and set deadlines |
Not set by |
Performatives
FIPA defines a library of performatives. ASTRA supports the following ones, which can be used anywhere a performative is expected:
Performative |
The sender… |
|---|---|
|
tells the receiver that the content is true |
|
confirms that the content is true, when the receiver was unsure |
|
tells the receiver that the content is false, when the receiver believed it |
|
tells the receiver whether or not the content is true |
|
tells the receiver the object that the content describes |
|
asks the receiver whether the content is true |
|
asks the receiver for the object(s) that the content describes |
|
asks the receiver to perform an action |
|
asks the receiver to perform an action when a condition becomes true |
|
asks the receiver to perform an action each time a condition becomes true (note the underscore, unlike FIPA’s |
|
asks to be told whenever the value of something changes |
|
agrees to perform a requested action |
|
refuses to perform a requested action |
|
tells the receiver that an attempted action failed |
|
withdraws an earlier request |
|
calls for proposals to perform an action |
|
makes a proposal, usually in answer to a |
|
accepts a proposal |
|
rejects a proposal |
|
tells the receiver that its message was not understood |
The meaning of a performative is a convention between the agents: ASTRA delivers the message, but it is up to the receiving agent’s plans to respond in a way that fits the performative.
Sending messages
Messages are sent with the send statement:
send(performative, receiver, content);
send(performative, receiver, content, params);
performative is one of the performatives above, or a variable of type
speechact.receiver is the name of an agent (a
string), or alistof names to send the same message to several agents at once.content is a formula. Like beliefs, the predicates used in message content must be declared in a
typesblock.params is an optional list that can contain
protocol(...)andconversation_id(...).
For example:
send(inform, "bob", price("apple", 10));
send(query-ref, ["shop1", "shop2"], price("apple", int P));
send(request, "bob", deliver("apple"), [protocol("fipa-request"), conversation_id("order-1")]);
The second example shows that the content can contain unbound variables: a query-ref asks the receivers to fill in the price.
Receiving messages
When a message arrives, ASTRA generates a message event, which is handled by plans whose triggering event has the form:
plan @message(performative, sender, content) { ... }
plan @message(performative, sender, content, params) { ... }
A message plan is selected in the same way as any other plan: the event is matched against the plan’s triggering event and context, in the order in which the plans are written. This means you can use variables to pick out the parts of a message you are interested in, and write separate plans for different performatives and contents:
plan @message(query-ref, string Sender, price(string Item, int P)) {
send(inform, Sender, price(Item, 10));
}
plan @message(inform, string Sender, price(string Item, int P)) {
C.println(Sender + " says " + Item + " costs " + P);
}
The sender is always available, so a plan can reply to whoever sent the message, as the first plan above does.
Example: asking for prices
In this example, a Buyer agent creates two Shop agents, asks both of them for the price of apples with a single query-ref message, and prints the answers.
agent Buyer {
module Console C;
module System S;
types shopping {
formula price(string, int);
}
plan +!main(list args) {
S.createAgent("shop1", "Shop");
S.createAgent("shop2", "Shop");
S.sleep(200);
send(query-ref, ["shop1", "shop2"], price("apple", int P));
S.sleep(500);
S.exit();
}
plan @message(inform, string Sender, price(string Item, int P)) {
C.println(Sender + " says " + Item + " costs " + P);
}
}
agent Shop {
module System S;
types shopping {
formula price(string, int);
}
plan @message(query-ref, string Sender, price(string Item, int P)) {
int Q = 10;
if (S.name() == "shop2") Q = 12;
send(inform, Sender, price(Item, Q));
}
}
Running mvn -Dastra.main=Buyer produces (the order of the two lines may vary):
[main] shop1 says apple costs 10
[main] shop2 says apple costs 12
Things to know
Agent names. Messages are addressed by agent name. The first agent is called
mainby default (see Building & Deploying ASTRA Programs), and agents created withS.createAgent(name, type)have the name you give them.Write one plan per performative. A variable of type
speechactcan be used in a@message(...)plan to match any performative, and can be passed tosend(...). In ASTRA 2.0.13, however, aspeechactvalue cannot be compared with==or joined into a string with+, so write a separate plan for each performative you handle.Message parameters. The parameters given to
send(...)set the protocol and conversation-id of the outgoing message. In ASTRA 2.0.13 they are not passed on to the receiving agent: theparamsargument of a@message(...)plan is always an empty list.Message delivery. By default, messages are delivered between agents running in the same program. Other message services can be installed to connect agents running elsewhere.