The JACK Agent Language is built on top of Java. Like C++, the JACK Agent Language does more than extend the functionality of Java – it also provides a framework to support an entirely new programming paradigm. The JACK Agent Language is an Agent Oriented programming language and is used for implementing Agent Oriented software systems.
The JACK Agent Language extends Java to support Agent Oriented programming:
All the language extensions are implemented as Java plug-ins. This makes the language as extensible and flexible as possible. Flexibility is important in the JACK Agent Language because it facilitates ongoing research into agent-oriented programming. Developers may, for example, want to investigate how different beliefset implementations affect agent performance. Because the beliefset component is supplied as a plug-in, this can be altered with minimal changes to the JACK development environment. All that is required is to replace the beliefset implementation in the kernel package.
Each of the JACK Agent Language extensions is strictly typed. This minimises implicit type casting and the opportunity for programmer error. Strict typing also allows for more efficient program compilation by the JACK Agent Compiler.
The JACK Agent Language introduces five main class-level constructs. These constructs are:
Agent – The agent construct is used to define the behaviour of an intelligent software agent. This includes capabilities an agent has, what type of messages and events it responds to and which plans it will use to achieve its goals.
Capability – The capability construct allows the functional components that make up an agent to be aggregated and reused. A capability can be made up of plans, events, beliefsets and other capabilities that together serve to give an agent certain abilities. An agent can, in turn, be made up of a number of capabilities, each of which has a specific function attributed to it.
BeliefSet – The beliefset construct represents agent beliefs using a generic relational model. It has been specifically designed so that a beliefset can be queried using logical members. Logical members are like normal data members, except that they follow the rules of logic programming (as in programming languages like Prolog).
View – The view construct allows general purpose queries to be made about an underlying data model. The data model may be implemented using multiple beliefsets or arbitrary Java data structures.
Event – The event construct describes an occurrence in response to which the agent must take action.
Plan – An agent's plans are analogous to functions. They are the instructions the agent follows to try to achieve its goals and handle its designated events.
For a detailed description of each of these extensions, including the specific interfaces and methods provided with them, refer to the appropriate chapters in this manual.
JACK Agent Language provides a number of variations and extensions to the standard Java syntax. These extensions exist purely to support the syntactic and semantic differences between object-oriented and agent-oriented programming.
An example piece of code written in the JACK Agent Language to implement an agent plan is given below. The syntactic elements that are unique to JACK Agent Language have been highlighted in bold. All other elements follow normal Java syntax.
plan MovementResponse extends Plan
{
#handles event RobotMoveEvent moveResponse;
#uses agent implementing RobotInterface robot;
static boolean relevant (RobotMoveQEvent ev)
{
return (ev.ID == RobotMoveQEvent.REPLY_SAFE ||
ev.ID == RobotMoveQEvent.REPLY_DEAD);
}
body()
{
if (moveResponse.ID==REPLAY_SAFE)
{
System.err.println("Robot Safe to Move");
robot.updatePosition(moveResponse.Lane,
moveResponse.Displacement);
}
else
{ // robot didn't make it
System.err.println("Robot is Dead");
robot.die();
}
}
}
In this example, the plan being defined inherits its core functionality from the JACK Agent Language class: Plan. It then identifies how the plan will be used through a number of JACK Agent Language plan declarations. JACK Agent Language declarations are each preceded with a # symbol to distinguish them from Java syntax elements, for example #handles event and #uses agent implementing.
The #handles event declaration identifies the goal or event to which this plan will respond. The #uses agent implementing declaration constrains the agent(s) that can use this plan. Only those agents that present the specified interface (RobotInterface) can include this plan.
The block of code in this example contains only regular Java code. The JACK Agent Language however provides its own statements that can be used when required. These statements are known as reasoning method statements, and are identified by a preceding @ character. Reasoning methods specify operations that are only meaningful in agent-oriented programming, such as posting an event or waiting until the agent acquires a particular belief. They are called reasoning method statements because you can only use them in a reasoning method belonging to a plan. These reasoning methods describe the reasoning and behaviour that an agent undertakes when it executes an instance of that plan.
Hence, JACK Agent Language extends Java syntax at three levels:
the class definition level, providing a set of classes that agent-oriented constructs such as agent, plans and events can inherit from;
the declaration level, providing a set of statements that identify relationships between the classes mentioned above; and
the statement level, providing a set of statements that can operate on JACK Agent Language-specific data structures.
For more information on the JACK Agent Language syntax extensions to Java, see the following chapters.
Because agent-oriented programming follows a different modelling paradigm to object-oriented programming, there are significant differences in how programs written in each language behave at runtime.
The JACK Agent Language extends the Java execution engine in the following ways:
A detailed description on the semantic consequences of each new JACK Agent Language construct can be found in the appropriate chapters of this manual (Agents, Capabilities, Events, Plans, Beliefset Relations and Views).
The JACK Agent Language is closely related to Java and extends the regular Java syntax. It allows the programmers to develop the components that are necessary to define BDI agents and their behaviour. These functional units are:
Agents – which have methods and data members just like objects, but also contain capabilities that an agent has, beliefset relations that they can use to store beliefs, descriptions of events that they can handle and plans that they can use to handle them.
Capabilities – which serve to encapsulate and aggregate functional components of the JACK Agent Language for use by agents. Capabilities can include events, plans, beliefsets or even other capabilities.
Beliefsets – which are used to store beliefs and data that the agent has acquired. Agents can also use regular Java data structures for storing information, but an advantage of a beliefset is that it will generate events when particular changes are made. This can be used to initiate further action within the agent and hence make it more responsive to its own internal state. Also beliefsets can be queried using logical members.
Views – which provide a powerful way of modelling any data in a way easily manipulated by JACK.
Events – which identify the circumstances and messages that it can respond to.
Plans – which are executed in response to these events.
Each of the events, plans and beliefsets used are implemented as Java classes. They inherit certain fundamental properties from a base class and extend these base classes to meet their own specific needs. The base classes are defined within the kernel and form the 'glue' that holds a JACK agent-oriented program together. However, the JACK Agent Language is more than just a specific organisation of Java objects and inheritance structures – it provides its own extended syntax, which has no analogous representation in Java.
JACK Agent Language constructs can be categorised as follows:
In addition, each of the JACK Agent Language classes supplies a number of normal Java members and methods that can be made use of in JACK programs.
The constructs available in each of these categories are listed in the following sub-sections.
These classes define functional units within JACK. The functional units are implemented as Java classes, with their agent-oriented properties embedded within the class as private methods. They also provide some base members and methods that JACK Agent Language programmers can use. Each JACK Agent Language type and the base members and methods it provides are listed below:
Agent – which models the main reasoning entities in JACK.
Capability – which aggregates functional components (events, plans, beliefsets and other capabilities) for agents to make use of.
Event – which models occurrences and messages that these agents must be able to respond to. Events may arise externally from messages from other agents, or internally as a consequence of one of the agent's own actions, or in response to one of its internal goals.
Plan – which models procedural descriptions of what an agent does to handle a given event. All the action that an agent takes is prescribed and described by the agent's plans.
BeliefSet – which models an agent's knowledge as beliefs that follow either a closed world (omniscient) or open world (with unknown) semantics. Beliefsets represent an agent's beliefs as first order relational tuples, and maintain their consistent logical and key constraints.
Note that a View does not have a base class.
JACK Agent Language #-declarations define agent-oriented properties of a JACK Agent Language type, or relationships and dependencies that exist between JACK Agent Language types. They are used to specify relationships between classes in a JACK program. For example, JACK Agent Language declarations specify which plans an agent uses and which event a plan handles.
The range of #-declarations available in JACK Agent Language are listed below:
#chooses for event – is used in plan definitions to identify a plan that allows the agent to reason about which applicable plan instance it will execute for a given event occurrence. This is in contrast to normal plans, which describe how the agent will respond to a given event instance when that plan is selected. Because it provides the agent with the ability to reason about how it will respond to (reason about) a given event, plans that contain a #chooses for event declaration are known as meta-level reasoning plans.
#complex query – is used in view and beliefset definitions to define a complex query.
#exports data – is used in capability definitions to declare that a user-defined data structure or a JACK beliefset is exported from the capability so that it is accessible from its parent capability. The export statement can also be used to make a user-defined data structure or JACK beliefset available at the agent level, and hence accessible from other capabilities in the agent.
#function query – is used in view and beliefset definitions to define a function query.
#handles event – is used in agent definitions and plan definitions. In both cases, it identifies an event that an agent or plan handles. Event handling is declared in the agent that uses a plan and the plan itself. This allows JACK to perform type checking and ensure that the agent has at least one plan to handle every event that it claims to handle, and conversely that an agent doesn't have access to a plan that is beyond its stated event-handling responsibilities.
#handles external event – is used in capability definitions to declare that there are plans within the capability that handle events of a given type.
#has capability – is used in agent definitions and capability definitions. In an agent definition, it gives the agent access to all of the functional components enclosed by the capability. In a capability definition, it declares the use of an inner capability.
#imports data – is used in capability definitions to declare a user-defined data structure or JACK beliefset that is to be used within the capability, but is brought in from the enclosing capability or agent.
#indexed query / #linear query – are both used in beliefset definitions to specify how a relation can be queried. This declaration indicates which parameters it expects to be supplied in the query and which parameters it will need to return (through binding logical members) when the query succeeds.
#key field / #value field – are both used in beliefset definitions to specify the fields that belong to a relation. Key fields identify an object that the relation describes, while value fields identify attributes of that object.
#posted as – is used in an event definition to define a posting method for that event. An event's posting methods are used to construct instances of the event when the event needs to be posted. Multiple posting methods allow the event to be posted in different ways (even having different parameters) in different circumstances.
#posts event – is used in agent definitions, capability definitions and plan definitions to identify events that agent or plan is capable of posting. Neither an agent nor a plan can create an instance of an event unless they declare that they can post (#posts event) or send (#sends event) it. Whether an event is posted or sent depends on its type.
#posts external event – is used in capability definitions to declare that there are plans (or Java code) within this capability that post events of a given type.
#posted when – is added to an event definition to specify the condition which must arise for the event to be posted automatically.
#private data / #agent data / #global data – are used in agent definitions and capability definitions to identify a user-defined data structure or JACK beliefset that the agent can use to store information. A private data instance is unique to each agent, and hence can be read or modified by that agent and that agent only. An agent data instance is available to all agents of a given agent type (i.e. instances of the same Agent class). A global data instance is available to all agents in a given process. Agents should only modify the data that appears in their private user-defined data structures or in their private JACK beliefsets.
#propagates changes– marks that a beliefset may be a source beliefset in a team belief connection, and it provides an implementation of the connection dynamics, so that changes to the beliefset are propagated correctly. Belief propagation is only available when using JACK Teams. Refer to the Teams manual for more details.
#reasoning method – is used in plan definitions to define methods that an agent may execute when it runs this plan. Reasoning methods are different from normal Java methods in that they execute as finite state machines, and may succeed or fail, depending on whether the agent can complete each statement that they contain. The top-level reasoning method is called body(). This is the only reasoning method that must be present in all plans and is not preceded by an # symbol. The body() method can call other reasoning methods if they have been included amongst the plan's #reasoning method declarations. It can also initiate the execution of other tasks and subtasks by executing JACK Agent Language statements that post new events. Examples of such statements are: @subtask, @maintain and @achieve.
#reasoning method pass / fail – is used in plan definitions to identify processing that should occur after an instance of the plan has either succeeded or failed. When either of these methods is present in a plan, that plan's execution does not end when the plan has succeeded or failed. First, the relevant pass() or fail() method is executed.
#sends event – is used in agent definitions and plan definitions to identify message events that the agent or plan is capable of sending to other agents. Neither an agent nor a plan can send a message event to another agent unless it include a corresponding #sends event declaration for that class of message event.
#set behavior – is used in BDI event definitions to declare how an agent processes an instance of this event when it arises. Options that can be configured include whether the agent will re-try the event if an attempted plan fails and whether or not the agent can reason about which plan it executes in response to the event.
#set transport – is used to signal which transport format is to be used for message events.
#uses agent implementing – is used in plan definitions to declare that any agent using these plans must implement the required interface. Interfaces allow multiple agents to use a given plan.
#uses data / #reads data / #modifies data – are used in plan definitions to identify user-defined data structures and JACK beliefsets that the plan can access. If the plan reads a data instance it should only perform queries on it. If the plan modifies a data instance, it can read and modify the data. Similarly, if the plan uses a data instance, it can read and modify the data.
#uses data – is used in view definitions to declare that a view requires data of a specified type. It can also be added to event and plan definitions to provide access to a belief structure.
#uses interface – is used in plans to declare that one of the enclosing capabilities (or the enclosing agent) implements the required interface. Note that this declaration supersedes #uses agent implementing for most purposes.
#uses plan – is used in agent and capability definitions to specify that an agent or capability includes this plan in its set of available plans. This is a containment relationship.
#uses taskManager– is used in agent definitions to specify how an agent shares its processing capacity between active tasks. JACK provides a simple task manager, which persists with the current task until completion, and a round robin task manager which allows each task to execute a specified number of plan steps before switching to another waiting task.
Reasoning statements are JACK Agent Language specific statements that can only appear in reasoning methods. They describe actions that the agent can perform to execute behaviour. Steps such as posting events, sending messages to other agents or waiting until a particular condition is true are expressed using reasoning method statements.
Each reasoning method statement is listed below.
@action– is a reasoning statement that can be used if there is a need to include a lengthy computation within a plan.
@achieve – tells the agent to make a certain condition true. If the condition is already true, the agent does nothing, but if not a BDIGoalEvent will be posted. Typically, this event will initiate the execution of a plan to make the condition hold. The current plan is suspended while this plan is being executed, and the @achieve statement succeeds or fails based on whether the plan that is executed succeeds or fails.
@determine – tells the agent to find a binding for a logical condition that causes a BDIGoalEvent to succeed. Typically, the logical condition will contain one or more beliefset queries. For each binding that these queries return, the agent posts a BDIGoalEvent. As soon as one of these BDIGoalEvents succeeds, the @determine statement succeeds. If all bindings are tried and fail, the @determine statement fails.
@insist – tells the agent to ensure that a certain condition holds true. Like the @achieve statement, the agent will do nothing if the condition is already true and will post a BDIGoalEvent otherwise, but when the BDIGoalEvent has been handled, the agent re-tests the condition to ensure that it holds. If the condition holds, the @insist statement succeeds, but if not the BDIGoalEvent is posted again. The agent will keep testing the condition and posting the BDIGoalEvent until the specified condition is satisfied or the event processing fails.
@maintain– is used in a reasoning method to specify that a subtask be performed. However, while the agent is performing the subtask, it must ensure that a particular logical condition is never violated. If the logical condition is found to be false between plan steps, the subtask will fail immediately. Other than specifying a maintenance condition, the @maintain statement operates in exactly the same way as the @subtask statement (see below for details).
@parallel – allows concurrent sub-tasking of a set of statements within reasoning methods. The @parallel statement suspends execution of the calling plan while all enclosed statements are executed in parallel.
@post – posts an Event or BDIFactEvent within an agent. This initiates another task execution within the agent. The event is posted using one of its posting methods and is handled asynchronously by the current task execution thread.
@reply– sends a message event (MessageEvent or BDIMessageEvent) in response to another message event that the agent has received. It is only available in plans that are executed within a task initiated by the arrival of a message event. If the task execution was initiated by any other kind of event, the @reply statement will fail.
@send – sends a message event to another agent. Unlike the @post method which posts an event internally, an event is sent to another agent (potentially running in another process). The receiving agent then has the option of replying to the message. The sender has a mechanism for checking that a reply has been sent, and has methods for handling replies when they arrive.
@sleep – suspends execution of the task for a given period of time.
@subtask – posts an Event or BDIFactEvent , but instead of handling the event asynchronously in a separate task, the agent handles it synchronously as part of the same task.
@test – tests a condition. If the condition is true, it returns true. If the condition is false, it returns false. If the condition is unknown, it posts a BDIGoalEvent to find out whether it is true or false.
@wait_for – identifies a condition that the agent should wait for. The plan cannot proceed until this condition is true. The task is suspended, and waits until some other task performs an action that makes the condition true. To prevent the agent from waiting indefinitely, a timeout condition can also be specified.
Each JACK Agent Language class offers a number of public members and methods that can be called in the user's programs. These base members and methods are listed below:
Timer timer – a public agent member that keeps the timer for this agent. This timer is used by the plan methods elapsed() and after(), and by the @sleep reasoning method statement.
finish()– terminates the current agent instance. This is distinct from, and should not be confused with, the standard Java finalize() callback (used by the garbage collector to allow objects to take a final action before termination).
String name() – returns the full name of a given agent instance. An agent's full name takes the following form: local_name@portal_name, where:
void postEvent() – posts an event. The event is handled asynchronously by a separate task in the agent. This method behaves in the same way as the @post reasoning method statement. The postEvent() method allows Java code other than reasoning methods to initiate task execution within an agent by posting an event.
boolean postEventAndWait() – also posts an event. The event must belong to an event class that the agent has declared it can post (i.e. by identifying this class in one of its #posts event declarations). However, instead of posting the event asynchronously, the event is posted synchronously (as though it has been posted by a @subtask statement, for example), to be handled as a 'subtask' of the calling thread. The current execution thread stops what it is doing while the event is handled and waits for the event to either succeed or fail.
An attempt to call postEventAndWait() from a JACK thread (i.e. through a call chain originating in a plan) will be caught by the JACK kernel, which will issue a warning message. PostEventAndWait provides a means to post events synchronously from outside reasoning method execution (i.e. from a Java thread).
void reply(MessageEvent query, MessageEvent reply) – replies to a message event that an agent has received from another agent. It takes two message events as arguments:
An agent can reply to any message event that it receives. This method behaves identically to the @reply statement in the JACK Agent Language. Like send() and postEvent() , this method allows code outside plans to engage in inter-agent communication and to initiate task execution within an agent.
void send(String to, MessageEvent message) – sends a message event to another agent. It takes a message event and the (full) name of the agent to which it should be sent. Partial names resolve to the same portal as the sender.
String from – appears in message events only, and enables identification of the agent that sent the message carried by this message event.
String message – appears in message events only (MessageEvents and BDIMessageEvents), and contains a message that will be passed to the application's Agent Interaction Diagram, so that each instance of this event can be identified when it appears in the diagram. The Agent Interaction Diagram in discussed in the Tracing and Logging Manual.
String mode – appears in BDIGoalEvents only and identifies the type of JACK Agent Language statement that posted the instance of this goal event (@achieve, @insist, @test or @determine).
Cursor replied()– appears in MessageEvents only, and enables the user to determine whether the agent has received at least one reply to a given message event instance. It returns a triggered cursor, which will test whether the given message event's reply queue is empty. Because the cursor is triggered, it can be used in a @wait_for statement to wait for message event replies to arrive.
MessageEvent getReply() – appears in message events only and allows the user to obtain a reference to each message event that has been returned to the agent as a reply to a given message event instance. To be a reply, a message event must have been sent by the destination agent for the original message, using the reply() method, from within the task that the destination agent was using to handle the original message event.
Agent agent – identifies the agent that this plan belongs to. Whenever an instance of a plan is created, this member is assigned a reference to the agent that created the plan instance.
Note: The preferred way to access the agent's methods and members is through the #uses interfacestatement described in the Plans chapter.
PlanInstanceInfo getInstanceInfo() – a callback that has been supplied so that agents can perform meta-level reasoning about plan instances. It provides a base class for recording information about plan instances. This base class can be extended and accessed in plans that perform meta-level reasoning. For example, one application may extend the PlanInstanceInfo class so that it assigns a precedence rating to each plan instance. This precedence rating may simply be a constant, or it may be calculated in some way. The agent can choose between its available plan instances on the basis of this precedence rating, taking the highest precedence plan it has available as its first choice.
static boolean relevant() – used to determine if a plan is relevant to the actual event. When the agent is looking for a plan to execute in response to an event, it executes the plan's relevant() method to determine whether the given plan is relevant. A plan is relevant if, and only if, it handles the given event and the event's parameters match the pattern specified in the plan's relevant() method. Since events are polymorphic, this can be used to weed out those plans that can handle this event type, but not this particular instance of the event type. Unless the event's parameters satisfy the relevant() method, the agent will deem the plan not relevant to this event.
context() – used to determine if the plan should be executed in the current context. The context specifies a logical condition that must be satisfied if the plan is to be applicable for handling a given event in the current situation. Context often refers to values in an agents knowledge base, which are its beliefs about the state of the world. When the agent needs to handle an event, it looks for a plan instance that is applicable to this event. A plan instance is applicable if it satisfies the plan's context. Typically, the context() method will include logical members in beliefset queries. When an applicable instance of the plan is found, it indicates that the query found a tuple and bound the logical member(s) to its value(s). If there is more than one way to satisfy a context() method's logical expression, there will be multiple instances of the plan that are applicable. One applicable instance will be generated for each set of bindings that satisfy the context() condition.
body() – the plan's main or 'top-level' reasoning method. It describes what it is that the agent actually does when it executes an instance of this plan.
add() – adds new tuples to an agent's private beliefset, or modifies its existing tuples by supplying updated information.
remove() – removes tuples from an agent's beliefset.
public int nFacts() – returns the number of facts (tuples) that are currently held in a given beliefset. For Open World relations, this is the sum of both positive (true) and negative (false) beliefs, while for Closed World relations this is only the number of positive (true) beliefs.
void postEvent(Event e) – the postEvent() method is used to post events within capability code. This method is actually just a convenience method that refers to getAgent().postEvent().
Agent getAgent() – this method is called on a capability instance to return the containing agent.
void autorun() – this method can be overridden in order to provide some initialisation when the capability is actually brought into being.