Agent events and messages
Events are central to the operation of Nuin agents. Events convey signals from the agents' environment, representing the agents' sensing of the world. A particular case is the exchange of messages between agents in a multi-agent system. Events are also used to co-ordinate the operation of the Nuin interpreter loop, and provide one means of interaction between the agent core and the services that the agent is configured with. Patterns of events and messages are used to trigger plans, causing agents to form intentions.
As noted here, detecting an event, or pattern of events, is a key means for agents to form an intention to act. For example, the arrival of an incoming message is such an event. This message might, for example, request the agent to answer a query, and so the agent forms an intention to reply to the message. Responding to an event in this way is such a common pattern in agent programming that Nuin defines message to be a sub-class of event, so a message may be used directly as a component of an event pattern (see examples).
Events are crucial also to the internal operation of agent interpreter. Most of the internal signalling of agent processes occurs with events. Thus, when an intention I0 suspends until sub-intention I1 completes (see section TODO for various means of achieving this), intention I0 will be re-awoken from the dormant intentions queue when the intention-completed or intention-failed event is raised from I1.
Consequently, events and messages are first-class values in the Nuin KS representation language.
An event is encoded as a term (TODO hyperlink), which has functor ks:event, and
always has arity one. The single term-argument denotes the type of the event. This event type
is represented by a symbol or term. If a term is used for the event type, the functor of the
term denotes the actual event type, and the term-arguments denote arguments to the event itself.
Event arguments may also be specified using the named argument syntax (which is the typical
way to encode the arguments of messages).
Because events are central to the Nuin architecture, a special syntax is used to encode events
in Nuinscript, rather than the standard term syntax. Specifically, the keyword event
is followed directly by the event type, and optionally by the named arguments. Some examples:
// an event of type e
event e
// an event of type e with argument 10
event e( 10 )
// an event of type e with a single named argument
event e {sender = market:seller}
Messages are encoded as sub-types of events. Specifically, a message is an event whose
event type is ks:message. The Nuinscript syntax for a message is the keyword
message followed by the message arguments:
// a message with two fixed (positional) arguments
message( market:seller, 10 )
// a message with two named arguments
message() {sender = market:seller, qty = 10}
Messages typically use the named argument syntax, since different messages may have very
different roles to play and different content to transmit.
While messages may use any encoding scheme, the FIPA ACL standard is a commonly used
choice. The FIPA vocabulary is encoded in Nuin via a set of built-in OWL ontologies:
faa.owl presenting terms from the
FIPA abstract architecture, fipa-acl.owl
presenting terms from the FIPA ACL standard, and
fipa-sl.owl presenting terms from
the FIPA SL content language standard. Important note these terms
do not use a FIPA-approved ontology namespace. FIPA has not yet, at the
time of writing, defined or formally sanctioned set of OWL-compliant names for the
components of messages. The Nuin mappings of FIPA terms may well change in future as
this situation changes. The Nuin-Jade gateway (TODO ref) transparently handles mapping
between the OWL-encoded FIPA terms used by Nuin agents, and the s-expresion syntax used for
message encoding by Jade agents.
The namespace prefixes for the FIPA vocabularies is built-in to the standard prefix mapping, and hence available in Nuinscript. Thus the following shows a message in FIPA ACL s-expression syntax, and the corresponding message in Nuinscript:
(request
:sender a@hap
:receiver (set (agent-identifier :name b@hap)) )
:content "buy buy buy, bye-bye"
)
message() {
acl:performative = acl:request
acl:sender = a1,
acl:receiver = [b],
acl:content = "buy buy buy, bye-bye"
}
Notice in the above example that the agent in Nuinscript is addressed using the name of the agent only. This is in keeping with the principles of the FIPA abstract architecture, but differs from the implementation in Jade, where the current location of the agent is regarded as part of the name. This problem is addressed in detail here.
Using events and messages from Java
While much of the agent's event and message handling can be specified with Nuinscript, there are occasions, e.g. when programming more complex services, when events must be created or accessed from Java. The Javadoc supplies full details of the available methods, but we summarise some of the key points below.
Events are represented by instances of the KsEvent interface, and messages by
the KsMessage interface. Both are defined in the org.nuin.ks.values
package. As is common throughout Nuin, instances of these interfaces are created by factory
objects. While redefining the factories represents a useful extension point for advanced
applications, the default value factory (accessed by The.valueFactory())
suffices for most purposes. To create a new event of type "alarm" in some
namespace NS but with no event arguments, use:
KsSymbol alrm = The.valueFactory().newSymbol( NS + "alarm" );
KsEvent alrmEv = The.valueFactory().newEvent( alrm );
Creating a message is similar, noting that message bodies are usually specified using named parameters:
KsSymbol agentA = ... ; // URI of agent A
Map params = new Hashmap();
params.put( FipaACLVocab.RECEIVER, agentA );
KsMessage msg = The.valueFactory().newMessage( params );
The built-in vocabularies FipaACLVocab, FipaSLVocab,
and FipaVocab provide Java constants that correspond to the symbols
defined in the (Nuin versions of) the FIPA ACL, SL and abstract architecture
terms respectively.
To deliver an event or message to an agent, use the method
deliver. This can be called safely from another thread, and will
add the event to the agent's event queue for processing.
Event patterns
An event pattern is used to trigger a plan. It is also used
to suspend a current intention until some condition becomes true of the agent's environment.
Event patterns are defined from a restricted propositional language, containing
only two predicates on and after, and two connectives
&& for conjunction, and || for disjunction.
The on predicate takes a single event (or message) as argument.
It is true exactly if the given event is the current event which is defined
as the event object that the agent interpreter is currently processing. There is
always one current event. The agent can also record a number of past events in its
event history. Each agent has a separate current event and event history. The
predicate after also takes an event as argument, and is true
exactly if the given event is the current event, or it appears on the event history.
This allows event expressions in which events are contextualised by recent events
in the event history. For example, a robot might treat the following event pattern
with some urgency:
on event alarm( edgeDetected ) && after event proximityBeacon( cliff )
The length of the event queue can be configured in the agent's initial configuration.
The current history queue can also be cleared by the library action
lib:clearEventHistory.
Note that matching of event patterns to actual events is performed by unification,
so it is possible for non-ground patterns to extract information from concrete
events. For example:
on event alarm( ?alarmType ).
This binds the variable ?alarmType to the event argument. It will unify
against event whose type is alarm with one argument. To bind the
whole event object, we can use a variable in place of an event term:
on event ?e which binds ?e to any detected
event. This can be useful, but it is unselective. To bind a variable to an
entire event or message object while still performing unification on the event
structure, we use the following idiom:
on ?e as event alarm( ?eventType )
This binds event ?e to the whole event object, and ?eventType
to the argument value, assuming the event unifies with the pattern. This idiom is
particularly useful with messages. For example, to trigger a plan on any event
arriving from a certain named agent (say, 'ex:vendor'), we could use the following event pattern
as trigger in a plan:
on ?msg as message() {acl:sender = ex:vendor}
Special events
Some special events are built-in to the agent interpreter. These are specified in
events.owl, and available as
vocabulary constants in
EventVocab.
Some of these events are for the internal use of the agent interpreter, and
will not be commonly used by agent developers. More useful events include:
event:startupan event that is fired by the interpreter when the agent starts processing; triggering off this event is a good way to provide initialisation behaviour to agentsevent:shutdownfired to shut-down the agentevent:exceptionan event that is introduced at the agent script level to indicate that a lower-level Java exception was caught by the interpreterevent:noMatchingPlanraised when the interpreter is requested to perform a plan for which it cannot find a matching plan head; at the moment, this is a debugging aide, in future we hope to extend the interpreter to allow on-demand plan acquisition
« prev: nuinscript | events and messages | next: agent configuration »