RuleEngine (Embodiment)

From OpenCog


Note: this RuleEngine was deprecated in April 2011 and replaced by the OpenPsi action/motivation system.

The Rule Engine module is a planner used to select a combo schema that will be executed by the an agent. It is inspired on STRIPS Planner. A set of rules is evaluated at each processing cycle to decide which will be the agent's next schema.

A rule has a name, a weight, a precondition and an effect. If the rule's preconditions were satisfied, regarding the current agent & world states, during a given cycle, that rule will be considered a candidate rule. The candidate rules will compete each other. The candidate rule marked which has the greater strength will be the winner and will have it's effect executed by the agent.

There are actually three kinds of rules:

  • Schema rules are the ones that effect action/schema.
  • Relation rules are the ones that effect a relation between the agent and some world entity (another agent, a structures, a toy, etc.)
  • Feelings rules are those that change the agent's emotional feelings (anger, hate, happiness).

Note: Relations and Feelings rules do not compete among themselves, rules of these types are always executed, changing their respective states within the agent. However, just one Schema rule will be selected.

Rules, Preconditions and Effects

During the RuleEngine startup, a file containing all available rules is loaded. This file is written in Lua. RuleEngine uses a Lua interpreter to load the rules into AtomSpace and other internal structures (It makes our lives easier while tuning the rules).

The following is an example of how a rule is defined inside the rules file (in Lua). Note that there are 3 weights for this rule. These values correspond to the rule's strength inside each Embodiment execution modes (playing, learning and scavenger hunt).

rule( "eatToRestoreEnergy",

Rule preconditions and effects are coded in Combo and hence are evaluated by the ComboInterpreter. Below are two examples of, respectively, a rule precondition and a rule effect.

A precondition combo script. The _*_ character used as a wild card (explained later). For now just assume that this script is evaluated in ComboInterpreter and if it evaluates true (logical_true in ComboInterpreter syntax) we obtain a candidate rule in the given cycle.

drinkToKillThirstPrecondition(0) :=
	and(greater_than(get_thirst(self) 0.8)
		near(self _*_))

A rule effect example, a simple combo script. See EmbodimentCombo for more info about the combo syntax.

goto_nearest_and_grabit(0) :=

Implementation details

When the OAC receives a tick message, a RuleEngine cycle is started. At each RuleEngine cycle, the current world and agent states, besides the loaded rules, are evaluated to decide which action/schema will be sent to Embodiment Proxy to be executed into the virtual world. If there is an actions/schema already being executed, the selected one is simply discarded since the world and agent state could have changed when the selected actions/schema is finally sent to Embodiment Proxy.

Considering the steps of one RuleEngine cycle we have:

  • update the known objects: RuleEngine keeps a cache of known objects information to optimize some operations. At each cycle it retrieve, from LocalSpaceMap, all the objects and set properties like last seen cycle, novelty, etc.;
  • update actions/schema execution status: verify if the last actions/schema sent to Embodiment Proxy has already finished its execution and log some information about execution results;
  • evaluate rules preconditions: invoke ComboInterpreter to evaluate available rules preconditions and build a list of candidate rules;
  • choose the best rule effect: among all candidate rules, select the effect whose rule has the highest weight. There will be times when more than one rule lead to the same effect, hence we compute the average strength for that effect.
  • prepare itself for the next cycle: clean up all caches to avoid problems during the next cycle.

Wild Cards and Unification

In order to support preconditions where we need to consider all the available elements in the world, like for example check all near objects to see if one of them is drinkable, we needed to add wild cards variables into ComboInterpreter. These kind of variables are represented by the _*_ symbol.

So, from the example given above (repeated here for convenience):

drinkToKillThirstPrecondition(0) :=
	and(greater_than(get_thirst(self) 0.8)
		near(self _*_))

we mean that: _to get the pet to drink water, its thirsty level should be greater than 0.8, there should be a drinkable object among the pet's known objects list and this object must be near the pet itself_. The wild cards represents the _pet's known objects_ in this sentence.

To avoid considering too many candidates during the whole combo tree evaluation, a unification process is carried within the tree parsing. For example, only the objects, from the pet's known object lists, that are drinkable are considered for the near(self _*_) evaluation. Also we used logical short circuit to avoid evaluation a whole and_seq when the first test fails or the evaluation of a whole or_seq when the first test succeeds.