2. Representing and querying knowledge

An agent’s beliefs are its working model of the world. A useful model states what each fact means, what arguments identify an object and what values describe its state. It also makes clear who updates that knowledge: the agent itself, an incoming message or an environment observation.

The light example uses light("off"). To introduce the switch that requests a light state, declare a second predicate:

types lighting {
    formula light(string);
    formula switch(string);
    formula desired(string);
}

initial light("off"), switch("off");
inference desired(string State) :- switch(State);

desired is derived from the observed switch position. An inference rule makes knowledge available to reasoning without storing an additional ordinary belief. Here both switch and light use "on" and "off"; keep that vocabulary throughout the program.

Types and bindings

Formula declarations describe argument types. An ASTRA string, int, boolean or list is a value type, while the formula itself describes a relationship or fact. Java classes may also be used as types when a module returns Java objects.

A query matches a formula against stored or inferred knowledge:

query(light(string Current));
console.println("observed light = " + Current);

Current is declared and bound by the match. An already bound variable restricts the match: query(light(Target)); checks for the particular target state. A failed query fails the calling plan. Use a condition when absence should select a different branch rather than fail.

Variables are local to their scope. A variable in a triggering event can be used in that plan’s context and body. A variable introduced in a context is available to the body when the context succeeds. A variable declared within a branch should not be used after the branch as if it were always bound.

Combine facts and conditions

Contexts and queries can combine formulae:

plan +!report() : desired(string Target) & ~light(Target) {
    console.println("light should become " + Target);
}

& requires both parts to hold. ~light(Target) succeeds when the agent cannot establish the matching belief; it does not establish an independently observed opposite state. Incomplete knowledge matters: knowing neither light("on") nor light("off") is different from knowing the light is off.

Use inference to express relationships, such as the desired state. Use explicit beliefs for observations that must be retained. A guard can also include arithmetic comparisons and module formulae; the latter query module state rather than ordinary beliefs.

Maintain consistent state

ASTRA permits beliefs describing different states to coexist. If the model says the light has exactly one current state, the updating code must preserve that rule. In the mental model, the plan removes light(Current) before adding light(Target).

The replacement statement -+light(Target); removes matching beliefs with the same predicate and arity, then adds the new one. It is convenient for this single-light example, but do not use it indiscriminately for a predicate containing many devices: replacement can remove more facts than the intended device’s state. Explicitly remove the matching old fact when its identity matters.

Observation-backed beliefs should be updated by the observation mechanism. Claiming that an action succeeded by inserting the desired belief would conceal a device failure. Chapter 5 separates acting from observing.

Next: Writing plans.

Further Details