home » user manual » agent intentions

Agent interpreter: intentions, plans and events

Nuin agents' high-level behaviour is specified by the plans that they can carry out. Detailed domain-specific behaviours can be added to the agent through the plug-in services mechanism, but the overall shape of an agent's responses to its environment is specified in the set of plans that an agent has available. In the present version, agent plans are pre-generated, and typically supplied to the agent as part of the configuration file. However, like other programming abstractions in Nuin, plans are simply Java objects conforming to a particular Java interface. There is thus no reason why plans have to be hand-generated before the agent runs. Plans could, in future be generated by an on-line planning system, or be created as the output from a learning system. However, in the current release of Nuin plans are typically generated by parsing pre-written Nuinscript files, and this is how the examples in this section are shown. It is important to remember, however, that these script-encoded plans are not a pre-requisite of the architecture.

Plans

Plan structure

A plan may have any of the following components. None of them are mandatory, though a plan with no internal structure at all is not very useful!

plan component summary
name The name of the plan as a URI. Should be unique within the scope of a single agent. Unnamed (anonymous) plans are permitted, but cannot be invoked by name. Optionally, the plan can be a logical term, not just a name, in which case the term arguments become arguments passed into or out of the plan.
label A user-friendly display name for the plan.
comment A comment string describing the intended use or effect of the plan
weight A numerical value that can be used to assist the scheduler in the interpreter when multiple different intentions could be executed.
trigger An event expression that denotes conditions under which the plan can cause an intention to be added to the set of current intentions.
postcondition A sentence which denotes the post-condition
action An action expression denoting the set of things that the agent will do when executing this plan.
with intention provides a mechanism for connecting plan stages that are separated in time, without losing state variables. For example when waiting for a message from another agent before continuing processing.
Table 1: elements of the structure of a plan

These elements are explained in more detail in the following sections.

Plan names

As noted above, plans in Nuin do not need to be named. Naming a plan is only really useful if the agent's behaviour requires plans to be invoked by name (e.g. from other plans). However, plan names may be used in debug messages and exceptions, so it can be useful to name plans that do not strictly require them.

In Nuinscript, a plan is named by placing the plan name after the initial plan keyword. As noted above, plan names can be symbols or terms. A term plan name will be unified with an argument term in the calling plan, thus allowing arguments to be passed into and out of called plans, depending on the state of the bound variables at the point of calling.

Example plan names:

 plan <http://nuin.org/2004/01/example:plan1> 
 end. 

 plan example:plan1 
 end. 
 
 plan example:plan2( "arg", ?result ) 
 end.
 
 plan foo 
     // note: in default namespace 
 end. 

Plan label and comment

These documentation components are not used in Nuin at present, but may be employed by future debuggers, configurers or other tools.

Plan weight

In normal operation, the agent interpreter takes care of choosing which should be the focus of the agent's attention during the next interpreter cycle. Different policies may be specified, e.g. to make the agent more responsive to the environment by responding to events before processing existing scheduled plans. There are times at which the default scheduling policies cannot be relied upon to select the appropriate behaviour. In these cases, the plan weight can be used as a hint to the scheduler: intentions for plans of a higher numerical weight are scheduled ahead of those with a lower weight. This should be used sparingly, however, as in appropriate choice of weights can shut-out lower priority but still important tasks altogether.

Example plan weight:

plan weight 5 end.

Plan triggers

A trigger defines a pattern over events that the agent may perceive, and which then schedule an intention to perform the triggered plan. A typical example of such an event is the arrival of a message, but other event types are also possible. A class of special events is the lifecycle events, which record changes in the agent's state. A particularly useful life-cycle event is event:startup.

Each cycle, the agent interpreter chooses either to process the next step in one of the active intentions, or to process an event from the event queue. In the latter case, the event is denoted the current event. The agent may be configured to remember a fixed number of previous events, with a default of 25. The predicate on( ?e ) succeeds if ?e unifies with the current event. The predicate after( ?e ) succeeds if ?e unifies with either the current event, or one of the events in the event queue. The unification argument can be a variable, a constant term, or a partially-ground term, allowing quite precise triggering conditions to be specified. The argument, denoting the type of the event, can be a symbol or a term, in which case the arguments of the term are regarded as the arguments of the event.

For example, the following expressions are all well formed event-trigger predicates:

 on event( ?e )
 on event( event:startup ) 
 on event( example:sensorA( true, ?x ) 
 after event( example:test() ) {name = "fu"} 

Note that the last example, using after, is equivalent to an on match using the same event expression, because the event will trigger when the example:test event is first detected. After tests are more useful when building Boolean combinations of event trigger expressions, using the connectives and and or. For example:

 on event( proximityAlarm ) && 
 after event( stairEdgeBeacon )

A message is regarded as a sub-type of event, and can be directly matched in a event trigger. Typically the components of a message are unified in the named rather than fixed (positional) parameters of the message term. For example:

 on message() {acl:performative = acl:inform, acl:content = ?content}

Using variables in the trigger term, the various components of the message can be extracted. However, it is sometimes useful to be able to bind the whole message object (or, equivalently, the event) to a variable. The syntax for this is:

 on ?msg as message() {acl:performative = acl:inform, acl:content = ?content}

Plan postcondition

A plan can be invoked by name, it can be triggered by an event or message, and it can be invoked in a goal-directed manner by unifying with the post-condition. The post-condition is a logical sentence (see TODO knowledge representation), denoting the condition that is stated to be achieved by the plan. Note that it is currently the responsibility of the agent programmer to ensure that the genuinely obtains as a result of successful execution of the plan.

Example syntax:

 plan postcondition p(10) && q(?x) end.

Currently, the use of the pre-condition to invoke plans is rather limited. This will change in future.

Plan action

The body of a plan is the action that it will carry out. A plan has only one action, but the action operators ; for sequential composition and | for alternate choice allow arbitrary composite actions to be constructed. The individual action primitives are further described in the scripting languge section (see @TODO).

Example syntax:

 plan 
 do 
     holds p(?x) ; 
     println( ?x, " is true of p()" ) 
 end. 

Note that, because ; is an operator applying to two action operands, there is no trailing semi-colon at the end of the action expression.

With intention

This is a rather complex mechanism for chaining plans together over time. Its very complexity suggests that a better solution should be sought, so we will not explain it further here. The intrepid explorer can venture into the details in the codebase.

Intentions

An intention represents a course of action that the agent has committed to. This may be represented as a series of actions that the agent will perform, a logical goal that it is attempting to achieve, or a mixture of these. It is quite possible and rational for an agent to have multiple intentions at one time. For example, a negotiation agent might be participating in simultaneous conversations with two different agents to decide the price of a good. Each conversation is represented in the Nuin agent as a separate intention. However, each conversation may be controlled by the same plan. Thus we can see that intentions also serve to hold task-specific state information. This might include parameters of the task, such as the symbol that serves to uniquely identify the conversation, or local parameters such as the current value of a plan script variable.

In general, Nuin defines three types of intention:

Of these, only the intention-to has been explored in detail in the current implementation. Maintain intentions are not properly implemented at all (yet), and intention-that intentions are rather weak in the absence of a planning component.

An intention-to is the standard intention type; it is this intention type that is created when a plan is triggered by an event pattern. Specifically, it is the intention-to perform the triggered plan.

An intention-that is created by passing an instance of the class IntentionThat to the agent's adopt() method. This is accomplished in Nuinscript by the achieve action, which is executed at run-time by the ActionAchieve action object. The argument to achieve is a sentence, that will be matched with the post-conditions of the known plans. Note that this is not a true reasoning step: the matching of achieves to post-conditions is performed by a simple structural comparison on the operands of the sentence. This is an area that is expected to receive significant revision as Nuin develops.

 holds hasState( ?component, broken ) ;
 achieve fixed( ?component )

Note that an intention-that is not the same as a goal. In the BDI agent model, the I modality represents courses of actions that the agent has committed to bringing about. By contrast, the D modality (desires, aka goals) represents states of the world that the agent would like to bring about, but isn't necessarily actively committed to. A truly rational agent should not hold inconsistent intentions, but may rationally hold inconsistent desires.

Testing intentions

There are two tests for the intentions that the agent holds:

 has intention to plan-name 
 has intention achieve sentence

In addition, the library action lib:getIntentionID can be used to access the UUID that is the identifier for this intention. Since this ID is unique to the intention, it can make a convenient identifier for activities related to the intention, e.g. a conversation ID.

Accessing intentions from Java

Intentions are at the heart of the operation of Nuin agents. Consequently, they are moderately complex objects. Full details of the Intention API can be found in the Javadoc [todo reference]. In this section we summarise some of the key points.

The main class is org.nuin.agent.Intention, which is abstract, and sub-classes IntentionTo and IntentionThat in the same package. Class org.nuin.agent.IntentionMaintain will be added in future.

Every intention is associated with exactly one Nuin agent, which can be accessed via the getAgent() method. The getBindings() method returns the current set of variable bindings for the intention, and the getAction() method returns the current action object that is being performed.

Intentions can be suspended after they have been started (see script actions [todo]). If a given intention is suspended, it will return true to the isSuspended() method. An intention that has not yet completed will return true to isActive() - it is possible for an intention to be both active and suspended simultaneously, since suspension occurs while the intention's action is being performed.

In order to allow the same action object to be re-used by multiple simultaneous intentions, it is necessary that plan action objects do not store state information internally. Instead, action objects that require internal state can use the state variable machinery from the intention object to store and retrieve internal state. Note: these methods are only of use to programmers who wish to develop new action objects; users who just re-use the built-in actions from a script do not need to understand or use intention state variables.

Every intention has a unique identifer, accessed via getUniqueID(). In addition, the intention name, which is taken from the executing plan name with variables bound, is accessed via getBoundName().

« prev: developing agents | intentions and plans | next: knowledge representation »