IBM Agent Building Environment Developer's Toolkit


Chapter 12. Additional Guidelines for Writing an Adapter

An adapter is the engine's interface to the outside world, which is anything outside the bounds of the engine. Adapters can represent an application program, a physical device or logical device. Typically, an adapter tries to represent one part of the outside world to the engine, which is referred to as the adapter's domain. Adapters tell the engine about changes in their domain, answer questions about their domain and perform actions in their domain at the engine's request.

To tell the engine about an event in the adapter's domain, the adapter notifies the engine, passing a trigger event object which describes the event. Adapters provide effectors and sensors. Engines call effectors to request that an action be performed in the adapter's domain. Engines call sensors to request more information about the adapter's domain. E-mail is a typical domain for an adapter. An e-mail adapter will do such things as:

An adapter does not have to perform all three functions. For example, you might have an adapter to represent a hard disk in a PC. The adapter might provide sensors to query the amount of free space and effectors to create new files, but the adapter does not create any trigger events.

Adapter Categories

Core adapters

Some adapters are useful to all applications, such as adapters that deal with scheduling events or with basic operating system functions, such as the file system. These types of adapters are called "core adapters". The time adapter is an example of a core adapter. The functions it provides, such as the ability to schedule alarms, are applicable to many applications.

Application Adapters

The other category of adapters are "application adapters" Application adapters can represent anything that can be described in terms of its events, conditions and actions. Adapters can represent:

The NNTP adapter is an example of an application adapter. The function it provides, representing NNTP (network news transport protocol) to the engine, is only applicable to news related applications.

Designing your Adapter

An adapter represents a domain to an agent. An agent is a particular configuration of engines and adapters. When designing your adapter, the natural tendency is to think about sensors and effectors within the context of the domain's events. However, do not assume that all sensors or effectors must be within the context of the adapter's own events. For example, an e-mail adapter may provide a reply effector, which requires a mail identifier in order to resolve the reply action. The e-mail adapter may also provide a sendmail effector, which does not require a mail identifier; any rule at any time can invoke SENDMAIL so long as it sends all the required arguments (addressee, body text, etc). In short, think about:

TriggerEvents and Engine Notification

Within a domain, there is usually one critical event that most users will consider as the most significant domain event. It is crucial to identify that event, since it serves as a significant reference point for most of the adapter design, influencing the design of the sensors and effectors. For an e-mail adapter, arrival of mail is probably the most significant event. For the time adapter, the most significant event would be a timer occurring.

Once the critical event has been identified, these questions need to be answered:

There are two ways to provide information about the event. A fact set can be constructed and passed with the trigger event. The other way is to provide sensors that can be called to get the information. Deciding between the two approaches is currently a "black art". As our best attempt to shed some light into that black art, we offer the following recommendations:

Other events within the domain can also be presented. While these other events are not as significant as the main event, including them will enable a more powerful manipulation of the adapter's domain. However, there is a balance between covering every event and covering those events with some end-user relevance.

Sensors

Sensors are queries directed to the adapter from the engine to determine the truth or falsehood of an atom in the antecedent of the rule and, sometimes, to return sets of values for unbound variables in the list of terms. Boolean sensors require a fully bound term list and determine the truth or falsehood of the atom. Non-boolean sensors have at least one free variable in the term list. Non-boolean sensors return sets of values for all the variables in the list of terms.

The key design point for sensors is to identify all the pertinent information that the adapter has in, or could extract from, its domain. An e-mail adapter has information about an incoming mail item, such as the sender of the mail and the time sent. Some of this information can be provided on the trigger event through the fact set. Sensors could be used to return additional information. Examples of e-mail sensors are:

A sensor may query other sources (its outside world) to obtain the information. A time adapter may have a sensor that would be a query with an unbound variable representing the current day of the week. The time adapter would then query some time facility to get the current day of the week and return it as a "binding" for that particular unbound variable.

A sensor is used by an engine to express a query to an adapter. Sensors are defined as a part of an adapter's interface and are established at agent start-up time when an adapter registers its sensors. A sensor's arguments have the form of an atom, which is like a fact. Instead of a predicate name, a sensor has a sensor name. Like atoms, sensors have terms and these terms have signatures. The signatures are also established at the time of sensor registration. Sensors can be thought of as providing a virtual pool of facts (through queries) to an inference engine.

Sensors are used by rules by mapping the predicate names of atoms to sensor names. In the metadata that is associated with a RuleSet, the Sensors section defines the mapping between predicate names and sensor names. This mapping occurs when the rule (in a RuleSet) is edited, establishing the intention of the rule author that a sensor be used to dynamically obtain more information at run time for rule inferencing.

Predicate signatures in the RuleSet metadata must agree with the signature of the associated sensor based on the adapter's registration of the sensor. Multiple predicates can share the same sensor as long as the predicates all have the same signature.

During rule evaluation, after matching rule atoms with facts for binding variables and establishing the truth of the atoms, as described above, atoms are checked to see if they employ sensors. This is done by comparing the predicate name of the atom with the predicate-to-sensor mapping names in the metadata of the RuleSet. Refer to "RuleSet Metadata Required for RuleSets" for details of this metadata. When a match occurs, the sensor is employed by the engine.

Now, more specifically, how can an engine use a sensor?

When a sensor's signature specifies that a particular term of the sensor is a variable, there are two possibilities:

  1. The term can be bound before invoking the sensor. This can be done when the same variable term in another atom in the same rule antecedent is bound through the fact pool. In this case the sensor invocation only does truth validation as described in the first bullet above.

  2. The term can be bound through sensor invocation (by the associated adapter). This only occurs when the term is not bound through the fact pool as described in 1 above. This results in truth validation and binding as described in the second bullet above.

The difference between 1 and 2 above is significant and is affected by the way the rule is written. In the first case, the adapter's capability for binding the variable is overridden. In the second case, the adapter is in control of the binding. In either case, multiple bindings can occur for the same variable. That is, multiple values can be substituted for the same variable name and the rule is evaluated for each such value substitution. When multiple variables are involved, there is an evaluation of the rule for each of the combinations of value substitutions.

An engine uses testCondition to invoke a boolean sensor (type 1 above), and answerQuery to invoke a non-boolean sensor (type 2 above). Refer to Chapter 3. "Adapter Reference Material" of the Components and Adapter Reference book for details of this interface. Refer to "Registering Sensors and Effectors" for details of adapter registration of sensors.

Examples of sensors include determining if a keyword exists in a mail item (Mail Adapter) and determining the current date and time (Time Adapter).

Restrictions:

  1. One sensor per atom. This means that a predicate should be mapped to only one sensor.

Effectors

To handle actions that may result from an event, effectors must be provided to perform actions in the adapter's domain. Effectors provide a way for an engine to affect the adapter's domain. An effector action is caused by an effector atom in the consequent of a rule. Only bound terms are allowed in the term list for an effector and there is no return value.

Effectors should be provided to perform actions that may result from an event from the adapter's domain. For example, in response to receiving new mail, a user might want to reply to that mail. Therefore, one effector an e-mail adapter might want to provide is a reply effector.

However, the effector set should not be limited to only those actions that are associated with an event. The effectors should represent all the functions of the adapter's domain. Continuing with the e-mail example, the reply effector requires a mail identifier in order to resolve the reply action. However, a sendmail effector assumes no context; any rule at any time can invoke sendmail so long as it sends all the required arguments (addressee, body text, etc).

An effector can create new trigger events, which it can pass to the engine. For example, a checkNews effector may tell the news adapter to look for the arrival of new news items. If the news adapter finds new items, it will create trigger events and notify the engine. Those trigger events will be processed by the engine in subsequent inferencing episodes.

Because there are no return values on effectors, if an effector encounters an error, it must report this error to the engine by the notification process.

Effectors, like sensors, are defined as a part of an adapter's interface and are established at agent start-up time when an adapter registers its effectors. Also like sensors, effectors' arguments take the form of an atom, which is like a fact. Instead of a predicate name, an effector has an effector name. Like atoms, effectors have terms and these terms have signatures. The signatures are also established at the time of effector registration.

Although consequents of rules may have atoms that map to effectors that also have unbound terms, all such terms must be bound by the engine before the associated effector is invoked. This binding is accomplished just as for any non-effector consequent atom.

Effector signatures must agree with the signature of the associated predicate in the RuleSet metadata. Of course, many predicates can share the same effector as long as the predicates all have the same signature.

Effectors are used by rules by mapping the predicate names of consequent atoms to effector names. In the metadata that is associated with a RuleSet, the Effectors section defines the mapping between predicate names and effector names. This mapping occurs when the rule (in a RuleSet) is edited, establishing the intention of the rule author that a effector be used to cause an action when rule is fired successfully.

Predicate signatures in the RuleSet metadata must agree with the signature of the associated effector, based on the registration of the effector by the adapter. Multiple predicates can share the same effector as long as the predicates all have the same signature.

An effector is executed with a performAction invocation by an inference engine. Refer to Chapter 3. "Adapter Reference Material" of the Components and Adapter Reference book for details of this interface. Refer to "Registering Sensors and Effectors" for details of adapter registration of effectors.

Examples of effector actions include setting an alarm (Time Adapter) which causes a later trigger event, sending mail (Mail Adapter), sending a message to a pager (using a Pager Adapter), and writing to a file (using a File Adapter).  

Adapter Selector Handling

One of the design decisions that an adapter must make is how to exploit the selector information in the trigger event. The event header associated with a trigger event contains a piece of information called the selector. It is up to each engine and adapter to determine how it uses the selector. The selector can be used to "select" what rule set should be used for inferencing a particular trigger event. If the selector is null, that "selects" all rule sets should be used for inferencing. The RAISE inference engine uses the selector in this manner.

Selector handling on a sensor or effector call

When an adapter is called to perform a sensor or effector call, it receives as input the event header associated with the trigger event that caused the inferencing episode to be started. If the adapter wants to exploit the selector information, it can get the information from the event header and use it when performing the function. For example, when an effector in the Time adapter sets an alarm, it associates the alarm with the selector.

Selector handling on a notify call

When an adapter is creating a trigger event to pass to the engine on a notify call, it must decide what value to supply for the selector. If the adapter wants all rule sets to get the trigger event, it can leave the selector null. If the adapter wants a specific rule set to get the trigger event, it can supply the selector value.

A typical case of supplying a non-null selector value is when the trigger event is created as a result of an effector call and the selector in the event header on the effector call was non-null. To continue with the Time adapter example, when an alarm goes off, the Time adapter checks to see it a selector was provided on the effector call that set the alarm. If it was, then the Time adapter uses that same selector value on the trigger event that represents the alarm.

Writing Rules using your Adapter

It may be helpful, as an adapter writer, to write some sample rules for the adapter and some scenarios with rules to verify the adapter interface. Also, it is helpful to look at other adapters' sensors, effectors, and actions that are related to the current adapter. Consider the scenarios for using the adapter alone or along with other adapters and then write some rules for those scenarios.

Rules are written to guide the engine in inferencing and to tell the engine how to relate to its environment. Since it is through the adapter that the engine relates to its environment, the power of the rules is determined by the power of the specific adapter interface.

Here is the flow of rules inferencing related to adapters:

  1. An adapter recognizes a change in its domain. It builds an event header to identify the event and a fact set to describe the event. It then passes the event and fact set to the engine.

    Note that the fact set may be empty.

    This notification starts the engine inferencing its rules. The fact set passed with the event is turned into short terms facts and are used in the inferencing.

  2. As part of evaluating the antecedent of the rule, the engine calls sensors to get more information.

  3. Once a rule has been determined to be true, the effectors in the consequent of the rule are called.

  4. After the inferencing episode has completed, the agent calls all adapters involved in the inferencing episode to tell them to do any necessary clean-up.

The sensor and effector names do not directly appear in the rules. Sensor and effector names are mapped to predicate names in the conduct set. The predicate names appear in the rules. Facts passed with the trigger event are written to reflect the predicate as they appear in the rules.

Implementing an Adapter

C++ Considerations

This section describes the key implementation considerations for coding an adapter in C++. Those considerations are:

  1. Supported C++ compilers for developing adapters are:

  2. Your adapter class must be a subclass of the class IAAdapter.

  3. Adapters must be packaged as a DLL.

  4. All sensors and effectors must be registered with the agent.

Java Considerations

This section describes the key implementation considerations for coding an adapter in Java. Those considerations are:

  1. Your adapter class must be a subclass of the class IAAdapter.

  2. All sensors and effectors must be registered with the agent.
 

Inheriting from IAAdapter Class

Adapters must be a subclass of the IAAdapter class. The C++ statement for including the IAAdapter header follows:

     #include <iatk/adapter.h>
For C++, this is the only IBM Agent Building Environment Developer's Toolkit header file that you must explicitly include in your code. The above header file takes care of including the header files for the other classes mentioned in this section.

The following member functions of IAAdapter are available for use by subclasses:

notify
This is called by the adapter to inform the engine that it has another event that it needs to process.

registerProcedure
This is called by the adapter to register a procedure, either a sensor or an effector, with the agent.

Note: Both the notify and registerProcedure member function will throw an object of type IAError as an exception when an error occurs. In this case, the errormsg member function can be used to obtain information about the cause of the failure.

The following member functions are virtual functions of IAAdapter and should be overridden in the subclass:

answerQuery
This is the interface from the engine to a sensor with one or more unbound variables. In this case, the sensor has to return all the facts it knows for the input argument list.

If the adapter does not have any non-boolean sensors, it does not have to provide an implementation of this member function.

eventComplete
This is a notification to the adapter that the agent has completely processed the subject trigger event and so the adapter can discard any data related to that event. This member function will be called if the adapter created the subject trigger event or if any of its sensors or effectors were called during the inferencing episode for the subject trigger event.

If the adapter has no clean up to do for events, it does not have to provide an implementation of this member function.

identify
This is called by the agent to give the adapter an opportunity to register all of its sensors and effectors. All sensors and effectors for an adapter must be registered before the identify member function completes for the adapter.

Note: The identify member function is a pure virtual function, so the subclass must provide an implementation of this function.

performAction
This is the interface from the engine to an effector. This is called when the antecedent of a rule is found to be true. This implies that all predicates in the consequent of a rule are true. When a predicate in a consequent of a rule is inferenced to be true, then the effector associated with that predicate is called via the performAction member function.

If the adapter does not have any effectors, it does not have to provide an implementation of this member function.

quiesce
When this is called, the adapter should stop generating events for the selector passed as input on the call. However, the adapter should be prepared to handle answerQuery, performAction and testCondition calls for events that reference the selector that have not yet been processed by the engine.

reset
When this is called, the adapter should clean up its state for the selector passed as input on the call.

restart
When this is called, the adapter can begin to call notify for the selector passed as input on the call.

shutdown
When this is called, the adapter cleans up its state and terminates processing.

start
Until this is called, the adapter should not call notify. Any events it detects before the Start call can either be saved in the adapter or discarded.

If the adapter does not generate trigger events and it is always ready to perform sensor or effector calls, the adapter does not have to provide an implementation of this member function.

stop
This should stop all notifications and clean up the adapter's current state.

If the adapter does not generate trigger events and it is has no termination clean up to perform, the adapter does not have to provide an implementation of this member function.

testCondition
This is the interface from the engine to a boolean sensor. The sensor has to return true or false about the atom represented by the fully bound term list.

If the adapter does not have any boolean sensors, it does not have to provide an implementation of this member function.

 

C++ Adapter Packaging Considerations

Once your adapter is coded, the executable form of your adapter should be packaged as a DLL. As part of the DLL, you must include an implementation of the newAdapter function, which has the following signature:

extern "C"
   IAEXPORT1 IAAdapter * IAEXPORT2 IACLINK
     newAdapter(const char *domain, void *parm);

where domain is the domain of your adapter and parm are any start-up parameters your adapters requires. The newAdapter function is responsible for creating your adapter object.

Java Adapter Packaging Considerations

Your adapter must override the newAdapter member function of the IAAdapter class. The signature of the newAdapter function is:

public static IAAdapter newAdapter(String domain, String parm)
      throws IAAdapterException

where domain is the domain of your adapter and parm are any start-up parameters your adapters requires. The newAdapter function is responsible for creating your adapter object.

Start up Execution Steps

This section contains steps in the life span of an adapter, starting with the loading of the adapter's DLL and continuing through inferencing on loaded conduct sets. The execution steps are as follows:

  1. The appropriate member functions are called on the IAAgent object, instructing the object to load the specified engines. For each engine, its DLL is loaded and an engine object is created by a call to the newEngine function in the engine's DLL.

  2. The appropriate member functions are called on the IAAgent object, instructing the object to load the specified adapter.

    For adapters written in C++, the following information is in the configuration file:

    For adapters written in Java, the following information is in the configuration file:

    Once the agent has that information, it does the following for each adapter:

  3. The agent loads the specified conduct sets for the engines.

  4. The agent then builds a trigger event with a EventName of AGENT:CONFIG and passes it to the engine to process. This event applies only to conduct sets loaded with a selector of AGENT_CONFIG. which is especially appropriate for configuring adapters (through the rules that are based on this event).

    During the inferencing episode for the "AGENT:CONFIG" trigger event, sensors and effectors will be called as necessary. All adapters will receive event complete (by invoking the eventComplete()) method of IAAdapter) for this event.

  5. The agent then builds a trigger event with a EventName of AGENT:STARTING and passes it to the engine to process. This trigger event allows for inferencing on all conduct sets that were loaded at the time of initially starting the agent.

    During the inferencing episode for the "AGENT:STARTING" trigger event, sensors and effectors will be called as necessary. Only adapters that have sensors effectors in rules/conduct sets conditioned by this event will receive event complete (by invoking the eventComplete()) method of IAAdapter) for this event.

  6. After the AGENT:STARTING trigger event has been processed, the agent calls the Start member function of your adapter object. After Start has been called, your adapter:

    Your adapter should now be set up to handle answerQuery, performAction and testCondition calls from the agent.

 

Dynamic Conduct Set Loading and Unloading Execution Steps

The following discusses the steps that the agent goes through as part of loading or unloading a conduct set after the agent start up procedure has been completed. Those steps are:

  1. The agent calls the quiesce member function of your adapter, which should cause your adapter to stop generating trigger events for the selector passed as input.

    The agent will then complete processing of all trigger events that it has on its internal queues.

  2. The agent will then cause inferencing to occur for the event "AGENT:CSCHANGE" for the selector that has been reset.
  3. Once inferencing has completed on the "AGENT:CSCHANGE" event, the agent calls the restart member function of your adapter. Your adapter is now allowed to start generating trigger events for the selector passed as input.

The following discusses the steps that the agent goes through as part of doing a reset of a selector after the agent start up procedure has been completed. Those steps are:

  1. The agent calls the quiesce member function of your adapter, which should cause your adapter to stop generating trigger events for the selector passed as input.
  2. The agent will then complete processing of all trigger events that it has on its internal queues.
  3. The agent calls the reset member function of your adapter. Your adapter should clean up all data that relates to the selector passed as input.
 

Shutdown Execution Steps

This section contains steps that the agent goes through as part of shutdown processing. .When the agent is ready to stop, it performs a two-step operation for each adapter:

  1. The agent calls the stop member function of your adapter, which should cause your adapter to stop generating trigger events. After Stop has been called, your adapter should stop generating trigger events.
  2. The agent will then complete processing of all trigger events that it has on its internal queues.
  3. The agent calls the shutdown member function of your adapter. During shutdown execution, your adapter should "clean up" itself and terminate all processing.
 

Registering Sensors and Effectors

All sensors and adapters must be registered when the adapter's identify is called. To register a procedure (which can be either a sensor or effector) the adapter invokes the IAAdapter::registerProcedure member function, which has the following C++ signature:

   IAAdapter::registerProcedure(const char *procedure_ name,
                                IAProcType procedure_type ,
                                const char *signature_list,
                                IAProcToken procedure_token)

and the following Java signature:

   public boolean registerProcedure(String  procName,
                                    int  ProcType,
                                    String  signature,
                                    IAProcToken  token)

The input parameters are:

procedure_ name
The name that you want to be associated with the procedure. Since sensor and effector names are mapped to predicate names in the conduct set, the sensor or effector name will not appear in the rules.

procedure_type
The type of procedure. The symbols to use for this parameter are defined in "registerProcedure" in Chapter 3. "Adapter Reference Material" of the Components and Adapter Reference book.

For C++, The IAProcType enumeration is defined in iatk/adapter.h.

signature_list
The signature list of the procedure, the terms that have to be supplied when the engine calls the procedure. For each term, the data type and the binding requirement must be specified. Supported term types are:

Integer - bound integer term

IntegerLVar - free integer term

String - bound string term

StringLVar - free string term

Symbol - bound symbol term

SymbolLVar - free symbol term

procedure_token
An adapter defined token that is used by the adapter to correlate the procedure name with an executable target.

The following is an example of registering an effector with two terms, a bound integer and a bound string.

      registerProcedure(
            "setIntervalAlarm",
             IAEffectorProc,
             "Integer String",
             IA_SETINTERVALALARM);

The following is an example of registering a sensor with a single term, and a bound string.

      registerProcedure(
             "isWeekend",
             IASensorProc,
             "String",
             IA_ISWEEKEND)

The following is an example of registering a sensor with a single term, and a free string.

registerProcedure(
             "currentTime",
             IASensorProc,
             "StringLVar",
             IA_TIME)

Using the Time Adapter

While an adapter is free to manage its own event scheduling, all adapters should rely on the time adapter to schedule alarms. Rather than each adapter defining its own scheduling system and accompanying rule editing and schedule administration graphical interface, the time adapter should handle this specialty for all adapters.

Future releases of the IBM Agent Building Environment Developer's Toolkit will include view components, so there should be a single visual metaphor for dealing with time functions.  

Notifying the Engine of an Event

Before an adapter can notify the engine of an event, it must construct an event header object, a fact set object and a trigger event object. The event header contains information about the event such as the type of the event, the time that the event occurred and a unique identifier of the event. The fact set contains the adapter's facts about the event. The trigger event object is composed of an event header and a fact set.

Fact Set Manipulation

The fact set is an object of type IAFactSet. Fact set objects are composed of objects of type IAAtom The steps for creating and manipulating the fact set object for the trigger event are:

Trigger Event Creation

The trigger event is an object of type IATriggerEvent. An event header object and a fact set must be supplied to the IATriggerEvent object before it is passed on the IAAdapter::notify() member function: After the trigger event object is passed to the engine, the engine will start an inferencing episode for the trigger event. Since the engine could be receiving trigger events from multiple adapters, there is no guarantee of when the inferencing episode will start.

Event Header Information

When the trigger event object is created, it creates the event header, which is an object of type IAEventHeader. The following information is contained in the event header. This same information is converted to short term facts that become part of the fact pool that is used for inferencing about the event.

EventDomain
This is the "domain" of the adapter, the external object or application it is representing to the engine. All trigger events from a specific adapter should have the same domain value.

The EventDomain information is turned into a fact about the EventDomain predicate. The signature of the EventDomain predicate is (string).

EventName
The EventName predicate is generated from a combination of the EventDomain and the EventType in the form "domain:type". The signature of the EventName predicate is (string).

EventSelector
This identifies the selector that the adapter wants associated with the trigger event. The EventSelector information is turned into a fact about the EventSelector predicate. The signature of the EventSelector predicate is (string).

EventTime
Identifies the time that the event occurred. If a time stamp is not supplied on the constructor call, the constructor will generate a time stamp.

The EventTime information is turned into a fact about the EventTime predicate. The signature of the EventTime predicate is (string,string), where:

EventType
This identifies the type of the trigger event within the adapter domain. It is an adapter's choice to determine how many different event types to generate. The EventType information is turned into a fact about the EventType predicate. The signature of the EventType predicate is (string).

EventId
A unique identifier for the event. This is generated automatically by the IATriggerEvent constructor. The unique identifier can be retrieved by using the getID member function. The EventId information is turned into a fact about the EventId predicate. The signature of the EventId predicate is (string).

This event ID is passed in on subsequent sensor calls related to this event and also with the eventComplete call.

Note: The event type is the only information that the adapter must explicitly supply for the event header creation.

Implementing an Effector

An effector causes an invocation of the adapter's performAction method. The information needed for the action is passed as a binding string, which is a list of terms from the atom in the consequent of the rule representing the effector. The effector is asynchronous, in that, it returns a void and may not necessarily have completed the action at that time. The adapter writer must determine whether the needed actions can be performed quickly and therefore before returning on the effector call or whether the actions should be started on a separate thread and then returning from the effector call.

All effector calls are funneled to the adapter through the performAction member function. One of the parameters on the performAction call is the procedure_token, which is used by the adapter to determine which effector to execute.

The event header object which caused the start of the inferencing episode, is also passed as a parameter on every effector call.  

Implementing a Sensor

A sensor is a query that is delivered from the engine interface in the form of a testCondition or a answerQuery member function call to the adapter. The adapter gathers the information by either inquiring of its outside world interface or from information it has stored from an event. The event header object which caused the start of the inferencing episode, is also passed as a parameter on every sensor call, so that the adapter knows what event the information it needs to return is in reference to.

Sensors are synchronous calls to the adapter. The sensor first gathers the information it needs and then returns to the engine. The adapter may "cache" information it has received either from events or sensor processing for use in future sensor calls.

In the case where sensors are used to determine the truth or falsehood of an atom in a rule, all the terms in the term list are bound terms. The sensor is invoked through the testCondition member function. If the sensor determines that the atom is true, it returns a "1". If the sensor determines that the atom is false, it returns a "0".

In the case where the term list contains unbound variables, the sensor is invoked through the answerQuery member function. The sensor returns all possible sets of correct answers for the unbound variables. Note that this set can be empty.

All sensor calls are funneled to the adapter through the testCondition and answerQuery member functions. One of the parameters on the performAction call is the procedure_token, which is used by the adapter to determine which sensor to execute.  

Sample Rules

The following section contains some sample rule sets, illustrating different aspects of how rules and adapters work together. Although the rules are presented individually, remember that the inferencing engine treats the rules as a set. Every rule in the set is evaluated during every inferencing episode. To keep the examples readable, rules that are false, their antecedent is evaluated to be false, during an inferencing episode are omitted.

Another simplification made in these rules is that the procedure name is the same as the predicate name. Remember that predicate names are mapped to effector and sensor names in the conduct set.

Configuration Rules Example

This example illustrates how rules can be used to configure an adapter. The news adapter has a CheckNews effector, that needs to be called to check for new news items. To place control to the frequency of checking under user control, it is implemented as rules. The following rules show how this can be accomplished:

   (=>(EventName "AGENT:CONFIG")(setIntervalAlarm 30 "minutes"))

For the inferencing episode for the "AGENT:CONFIG" trigger event, the above antecedent is true. This results in a call to the setIntervalAlarm effector, which sets an alarm for every 30 minutes.

When the alarm goes off, the time adapter will create a "Time:IntervalAlarm" trigger event. It will also create a fact set for the event, which will include the fact:

   (Interval 30 "minutes")

After the engine has been notified of this event, it will start an inferencing episode for the event. During this inferencing episode, the following rule will be found to be true:

   (=>(AND(EventType "IntervalAlarm")(Interval 10 "minutes"))
      (CheckNews "token"))

Invocation of the CheckNews effector will cause the adapter to look for new news items. If any are found, the notification mechanism is used to inform the engine of their existence. This will result in future inferencing episodes by the engine.


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]