Agent Communication
JADE Agent 通信基本原理
Agent之间的通信遵循FIPA规范,FIPA规范对Agent间的通信方式、通信策略与通信协议等均有规定。
共七层:网络基础设施层、传输层、报文传输协议层、消息封装层、Agent通信语言层、内容语言层、会话层。
远程机器上的Agent间的通信
编写通信程序的关键步骤是使用AID类设置接收方的名称和地址,远程通信亦是如此。AID类用来表示JADE的Agent标识符,JADE的内部Agent列表使用这个类记录Agent的名称和地址。可以使用该类的构造方法设置接收方的名称,使用该类的addAddresses()方法设置接收方的地址。
说明:
- AMSAgentDescrip类
该类是JADE.domain.FIPAAgentManagement.AMSAgentDescription包中的一个类,使用该类可获取AMS上所有Agent的列表,常用的方法是getName()。 - SearchConstraints类
该类是JADE.domain.FIPAAgentManagement.SearchConstraints包中的一个类,用来设置搜索的约束体条件。如,使用setMaxResults()设置查询的最大结果集。 - AMSService类
该类负责对agent中的管理,如使用该类的search()方法搜索AMS中的所有Agent。
package MyFirstAgent;
import jade.core.AID;
import jade.core.Agent;
import jade.lang.acl.ACLMessage;
public class remotesend extends Agent {
@Override
protected void setup() {
ACLMessage msg=new ACLMessage(ACLMessage.INFORM);
AID dest=new AID("receive@172.168.1.142:1099/JADE");
dest.addAddresses("http://xxxxxx:xxxx/acc");
msg.addReceiver(dest);
msg.setContent("你好");
send(msg);
}
}
接收方:
package MyFirstAgent;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;
public class ReceiverAgent extends Agent {
@Override
protected void setup() {
this.addBehaviour(new CyclicBehaviour() {
@Override
public void action() {
ACLMessage msg=receive();
if (msg!=null){
System.out.println("I received this message:"+msg.getContent()+",this message is from" +
msg.getSender());
}
}
});
System.out.println(getAID());
}
}
输出:
( agent-identifier :name receive@172.168.1.142:1099/JADE :addresses (sequence http://xxxxxx:xxxx/acc ))
I received this message:你好,this message is from( agent-identifier :name send@172.168.1.142:1099/JADE :addresses (sequence http://xxxxxx:xxxx/acc ))
基于对象序列化机制的Agent间的通信
Agent间发送的消息从形式上说可以分为三类:
(1)原子消息。指以字符串形式发送的简单消息。
(2)Java对象。在很多情况下,Agent间发送的消息并非是简单的数值或字符串。例如发送图书的基本信息,就可以把书名、价钱、作者等属性封装在一个Book类中,然后把该图书作为Book类的对象,设置各属性值后作为消息发送出去。
(3)Ontology对象。即本体对象。对于一些复杂应用,用户需要为消息内容定义自己的词汇或语义,即Ontology。
消息内容的类型 | 读消息内容的方法 | 写消息内容的方法 |
---|---|---|
字符串 | getContent() | SetContent() |
Java对象 | getContentObject() | SetContentObject() |
Ontology对象 | extractContent() | fillContent() |
消息模板
为实现对消息进行过滤,并创建不同的Behaviour分别处理来自不同Agent的不同种类的消息。
JADE提供了MessageTemplate类和接收方法,该接收方法将消息模板作为参数,并且只返回与消息模板匹配的消息。
MessageTemplate类利用MessageTemplate可以针对ACLMessage的每个属性设置模式,以达到过滤消息的目的。为了可以构建更复杂的匹配规则,多个模式也可以进行and,or,not运算。最有用的一些规则或方法包括:通信行为匹配、发送者匹配、会话ID匹配。例如,MatchPerformative(perforamtive)是通信行为的匹配,这里performative可能是:ACLMessage.INFORM、ACLMessage.PROPOSE或ACLMessage. AGREE等,还有发送者匹配MatchSender(AID)、会话匹配MatchConversationID(String)、通信协议匹配MatchProtocol(String)、本体匹配MatchOntology(String)等。
例如,建立一个消息模板,消息匹配规则为:消息原语是INFORM并且消息的发送者是"a1",则有:
MesssageTemplate mt = MessageTemplate.and(MessageTemplate.MatchPerformative(ACLMessage.INFORM
),MessageTemplate.MatchSender(new AID("a1",AID.ISLOCALNAME)));
//isGUID-指示传递的名称是否已经是全局唯一标识符。 还定义了两个常量ISGUID,
//ISLOCALNAME,用于设置此参数的值。 如果名称是本地名称,则将HAP(本地代理平台)
//连接到名称,并用“ @”分隔。
public AID(String name, boolean isGUID) {
this.addresses = new ArrayList(1);
this.resolvers = new ArrayList(1);
this.userDefSlots = new Properties();
if (isGUID) {
this.setName(name);
} else {
this.setLocalName(name);
}
}
// boolean ISLOCANAME false
//boolean ISGUID true
消息模板实例
例程包含两个Agent类:Template.class和Responder.class。程序运行时,启动两个Responder类的实例Agent,分别命名为A1和A2;同时也要启动一个Template类的实例Agent,命名为T1,如图:
首先,T1向A1和A2发送INFORM消息,消息内容为“ping”,测试是否能与这个两个Agent联通。A1和A2收到来自T1的“ping”消息后,创建并向T1发送回复消息,他们都会向T1发送两条具有不同原语的消息,T1利用消息模板过滤掉其他消息,只接收来自A1并具有Propose原语的消息。
Behaviour
类的block()
方法,与其名称的含义不同,block()方法并不是一个真正的阻塞命令,它只是将行为标记为“已阻塞”,使得Agent不再安排这个行为执行。当新消息插入到消息队列时,阻塞的行为被激活,再次执行处理接收到的消息 。
除了receive()
方法,Agent类也提供和blockingReceive()
方法,是真正阻塞了Agent线程,该方法还有一个重载版本,包含一个MessageTemplate
参数(直到有与之匹配的模板时才返回)。在调用blockingReceive()
返回命令前,它会阻止其他所有行为的执行。
ACLMessage
类的方法createReply()
自动创建一个新的ACLMEssage
,并舍此那个接收者和其他控制对话的必要选项(conversation-id、reply-with、in-reply-to)
Template类:
package MySecondAgent;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
public class Template extends Agent {
MessageTemplate mt1 = MessageTemplate.and(MessageTemplate.MatchPerformative(ACLMessage.PROPOSE),
MessageTemplate.MatchSender(new AID("a1", AID.ISLOCALNAME)));
@Override
protected void setup() {
//给a1和a2发送消息
ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
msg.setContent("Ping");
for (int i = 1; i <= 2; i++) {
msg.addReceiver(new AID("a" + i, AID.ISLOCALNAME));
}
send(msg);
//行为1 带过滤的
addBehaviour(new CyclicBehaviour(this) {
@Override
public void action() {
System.out.println("The message:");
ACLMessage msg = receive(mt1);
if (msg != null) {
System.out.println("gets " + msg.getPerformative() + " from " + msg.getSender().getLocalName() +
" = " + msg.getContent());
} else {
System.out.println("gets NULL");
}
block();
}
});
//接收全部消息
/*addBehaviour(new CyclicBehaviour(this) {
@Override
public void action() {
System.out.println("Behaviour two");
ACLMessage msg = receive();
if (msg != null) {
System.out.println("gets " + msg.getPerformative() + " from " + msg.getSender().getLocalName() +
" = " + msg.getContent());
}
else {
System.out.println("gets NULL");
}
block();
}
});
*/
}
}
ResponderAgent类:
package MySecondAgent;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;
public class ResponderAgent extends Agent {
@Override
protected void setup() {
addBehaviour(new CyclicBehaviour() {
@Override
public void action() {
ACLMessage msg=receive();
if (msg!=null){
ACLMessage reply=msg.createReply();
reply.setPerformative(ACLMessage.INFORM);
reply.setContent("yes,i am here");
send(reply);
reply.setPerformative(ACLMessage.PROPOSE);
String msgcontent = "Tell me your opinion about"+reply.getSender().getLocalName();
reply.setContent(msgcontent);
send(reply);
}
block();
}
});
}
}
输出(每次的结果不同,与线程有关系):
The message:
gets NULL
The message:
gets NULL
The message:
gets 11 from a1 = Tell me your opinion abouta1
The message:
gets NULL