home » user manual » design principles

Nuin: key design principles

This section outlines some of the key principles that are themes in the Nuin design. By stating these explicitly, I hope it will help readers to see the regular structures in the Nuin design, and hence tame some of the complexity.

Subsections

» Factories and abstractions
» Agent control: intentions and plans
» Internal control flow - events
» Extending agent capabilities with services
» Names are URI's

Factories and abstractions

As figure 1 shows, there is a large number of key functional components in Nuin, ranging from knowledge representation primitives (such as beliefs and desires), up to reasoners and pluggable services. In order to make these as flexible as possible, we consistently use Java interfaces to represent these objects, thus making it explicit what the public contract of the objects is. So long as the public contract is honoured, any implementation of the interface should perform equally well. In order to encourage developers to program to the public interface and not to a given implementation, we hide the implementation classes, and rely on the factory design pattern to generate instances.

Thus, rather than Intention i = new Intention(...) to create an intention object, we provide a factory class that creates instances of KS sentences, including intentions: Intention i = sentenceFactory.newIntention(...). In principle then, a developer can change to a new implementation of KS sentences, simply by changing the behaviour of the factory. Of course, if a particular sentence factory implementation is always relied on, the actual flexibility to change implementations is curtailed. To counter this, we add a further layer of indirection: each of the factories is accessible through a central factory registry (or factory-factory, if you prefer). To prevent names from getting overly long, and to make the design intent apparent, this central registry is the class The. Thus, the common pattern for creating objects for which there is a factory, such as sentences, is:

Intention i = The.sentenceFactory().newIntention(...);

Summary: use factory pattern to produce objects, relying only on the public contract defined in the Java interface. Use the factory registry The to get hold of, or change, specific factories at run time.

nuin architecture

Figure 1: Nuin architecture schematic

Agent control: intentions and plans

Agents respond to changes in their own state, and sense events from their environment. They can do many things at once. The key control objects are the interpreter, which schedules the agent's attention to the percepts it receives and executes the agent's plans, and the agent's intentions which represent the theoretical notion of a commitment to some course of action, and the practical sense of bookkeeping the state variables that are germane to that course of action. For example, consider an agent that can seek for media objects (music, pictures, etc.) in a set of media repositories. Two users, A and B separately request the agent to perform two searches, for titles T0 and T1. The agent uses the same plan to determine which sequence of actions it should carry out to fulfill these requests, but uses two separate intentions to keep A's request distinct from B's.

Generally speaking, intentions are central to the way that the agent schedules and controls the actions it performs. For example, when invoking a plug-in service, the agent loop will pass the current intention object to the service object so that it has access to current variable bindings, the identity of the owning agent, etc.

A plan is an element of the agent's knowledge base, and represents a collection of tests and actions to achieve some high-level behaviour. Plans can be anonymous or named, and can be triggered by events (forward chaining) or adopted as a means to achieve some specified end (backward chaining). Currently, there is no on-line planner: agents are given a set of plans as part of their configuration. However, there is nothing in the architecture to prevent agents from learning or otherwise acquiring new plans dynamically, or from adding a on-line planning service that can generate plans in response to local conditions.

Summary: intentions represent the agent's control over its actions, in both practical and theoretical terms. Intention objects contain important state information relating to the execution of a plan. One plan can be being executed simultaneously by many intentions, but not vice-versa.

Internal control flow: events

The agent has many stimuli that it can respond to, including: messages or other percepts arriving from the environment, completion of plan stages, failure to complete plan steps, and activity steps in plug-in services. In order to give a uniform treatment of these stimuli in the interpreter, all of these occurrences are mapped to events. Events are queued internally by the agent, and a selection policy allows the agent to determine event to focus on next. Choices include FIFO (the default), priority-based ordering, or domain specific heuristics. Plans can be set to trigger on the occurrence of a certain event or pattern of events, so high-level behaviours can be easily matched to event streams.

There are actually two different Event classes in Nuin, which play very different roles. The class org.nuin.agent.AgentEvent represents the event objects that are queued up by the agent, and processed by the interpreter. The class org.nuin.ks.values.KsEvent is a value type in the knowledge representation formalism that represents an event as member of the domain of discourse. If a plug-in service wishes to cause some behaviour to occur in the agent's main loop, it will typically create an appropriate AgentEvent and add it to the agent's queue. This provides a natural means for user actions, through a GUI, to be communicated to the control level in the agent.

Summary: coordination of control flow between active elements with the agent is generally achieved by posting events onto the internal events queue.

Extending agent capabilities with services

It is neither desirable nor possible to have the agent's scripting language provide all of the capabilities that any foreseeable agent might need. For one thing, this prevents software re-use. Agent platforms such as Jade exist to provide FIPA-compliant messaging, and it makes little sense to re-implement the work done by the FIPA team in another programming language. The other major reason is to try to keep the design of the scripting language relevant to its task. We are not trying to create a general-purpose scripting language like Perl or Python. Rather, the goal is to create a highly-specialised language that is optimised for a particular range of tasks - i.e. specifying agent behaviours.

In order to allow the scripting language to be specific to its purpose, and yet not be constraining and inflexible for agent designers, there is a general mechanism for invoking arbitrary actions on arbitrary services from the script. An agent is configured with, or, in principle, can acquire at runtime, a set of agent services. These can be named, or be identified by a service type. The script can invoke actions on the service, passing values that are bound from variables in the current intention. For example, the script can be written to invoke the send action on the current service that has type message service. Depending on the configuration of the agent, this may be a simple service for passing messages to other agents within the same Java virtual machine, or it may be a bridge to the Jade message passing mechanism, or it could be a SOAP service endpoint.

The service abstraction has two main uses in Nuin agents. The first is illustrated in the above example: to allow the script to be written to the capabilities of an abstract service, which is then grounded in a specific concrete service at runtime. The second use is to allow the agent developer to write complex capabilities using all of the general power of Java, yet still have the high-level behaviour of the agent determined by the script. A typical example of this behaviour is a specialised service that at run-time presents a graphical user interface for the user to interact with. The service object plays the role of mediating between the (say) Swing UI events in the interface, and the actions and events of the agent control layer.

Summary: plug-in services allow the agent to be both more flexible and more extensible, without demanding too much of the scripting language. The cost is that the designer must choose whether a given capability is best programmed at the script level, or deeper down in the internal logic of the service instance.

Names are URI's

Since Nuin is intended to work with Semantic Web information sources, a number of key principles have been adopted from the Semantic Web approach. One of these is that all names are Universal Resource Identifiers, or URI's. Using URI's for all symbolic constants has a number of distinct advantages, including the increased likelihood that we can use domains or other naming schemes judiciously to help avoid accidental name clashes. The most common form of URI is the HTTP URL, (e.g. http://www.hp.com), but Nuin allows any string conforming to URL syntax to be used as a name. [In fact, checking for syntactic conformance is expensive, and so performed rarely - but the intent is that all names should be valid URI's].

A further advantage is that using URI's as names makes interoperation with RDF and OWL easier. In fact, this interrelationship is exploited in the way in which constants are defined throughout Nuin and the related examples. Rather than manually define Java constants that correspond to the URI's that are being used, I define an OWL or RDF vocabulary containing the definitions, and then use Jena's schemagen tool to auto-generate a Java vocabulary class containing the constant definitions. This process is fully automated via an Ant script.

One of the costs of URI's is that constant names become very long. This is mitigated by the qname form, in which the namespace part of the URI is replaced with a namespace prefix. A number of standard prefixes are defined, and this set can be extended by the user. Thus http://nuin.org/2003/07/action#send becomes action:send in qname form, and is available to Java code as the constant org.nuin.vocabulary.ActionVocab.SEND.

Summary: symbolic constants names are URI's, and typically defined by OWL vocabularies, from which Java constants are auto-generated.

« prev: design objectives | design principles | next: developing agents (overview) »