CArtAgO Environments

CArtAgO provides a shared environment for agents. Agents decide what to do; artifacts provide tools and maintain environment state. An artifact might represent a counter, a robot gripper, a shared resource or a service. Artifacts live in workspaces, where agents can discover them, execute operations and observe changes.

ASTRA integrates CArtAgO through the Cartago module. This guide uses ASTRA 2.6.1. It assumes you have completed the Quick Start.

Concepts

Concept

Meaning

ASTRA interface

Workspace

A container for artifacts and participating agents

cartago.link() joins the local default workspace

Artifact

A Java object exposing operations and observable state

makeArtifact, lookupArtifact, cartago.ArtifactId

Operation

Work performed by an artifact

cartago.operation(id, operation(...))

Observable property

Persistent state visible to agents that focus on the artifact

cartago.property(id, property(...)) and property events

Signal

A transient notification from an artifact

$cartago.signal(...)

Focus

Subscribe to an artifact’s observations

cartago.focus(id)

An artifact is shared; an ASTRA module instance belongs to an agent. Focusing on an artifact makes its properties available through the CArtAgO adapter, rather than adding ordinary beliefs to the agent’s belief base. Several agents can focus on the same artifact.

Project setup

The CArtAgO archetype supplies the appropriate Maven parent and integration dependencies:

mvn archetype:generate \
    -DarchetypeGroupId=com.astralanguage \
    -DarchetypeArtifactId=astra-cartago-archetype \
    -DarchetypeVersion=2.6.1 \
    -DgroupId=examples \
    -DartifactId=counter-demo \
    -Dversion=0.1.0
cd counter-demo

The generated POM uses com.astralanguage:astra-cartago-base:2.6.1 and astra-maven-plugin:2.6.1. For an existing project, replace the astra-base parent with astra-cartago-base at the same version. Keep the ASTRA Maven plugin enabled.

Put ASTRA programs in src/main/astra, and artifact classes in src/main/java. Use Java package names when referring to artifact classes.

Use the environment

Build a counter artifact

Create src/main/java/artifacts/Counter.java:

package artifacts;

import cartago.Artifact;
import cartago.OPERATION;

public class Counter extends Artifact {
    void init(int initialValue) {
        defineObsProperty("count", initialValue);
    }

    @OPERATION
    void increment() {
        int next = getObsProperty("count").intValue() + 1;
        getObsProperty("count").updateValue(next);
        signal("incremented", next);
    }
}

init receives the artifact’s creation arguments. defineObsProperty publishes its initial state. The operation changes that state and emits a signal. The property remains queryable; the signal is an event, not a stored fact. Unlike ASTRA module actions, artifact operations use CArtAgO’s @OPERATION annotation and do not return a success boolean.

Use the artifact from ASTRA

Replace src/main/astra/Main.astra with:

agent Main {
    module Cartago cartago;
    module Console console;
    module System system;

    plan +!main(list args) {
        cartago.startService();
        cartago.link();
        cartago.makeArtifact("counter", "artifacts.Counter",
            cartago.params([0]), cartago.ArtifactId Id);
        cartago.focus(Id);
        wait(cartago.property(Id, count(0)));
        cartago.operation(Id, increment());
        wait(cartago.property(Id, count(1)));
        query(cartago.property(Id, count(int N)));
        console.println("counter = " + N);
        system.exit();
    }
}

Start a local CArtAgO service once for the application, then call link() for each agent that will use it. makeArtifact creates the named artifact and binds its identifier. cartago.params([0]) converts the ASTRA list into the Java argument array expected by CArtAgO.

focus subscribes this agent to the artifact. The waits synchronise with observable state, so the program does not assume observations are already available as soon as an operation returns. The explicit artifact identifier in operation and property avoids ambiguity when several artifacts expose the same operation or property.

Run:

mvn clean compile astra:run

The agent prints counter = 1 with its agent-name prefix. The example exits the whole ASTRA application after checking the result; a long-running application should keep its agents alive to receive observations.

Observations and events

Add these plans to an agent that remains running after focusing on the counter:

plan +$cartago.property(string PropertyId, count(int N)) {
    console.println("observed count = " + N);
}

plan -$cartago.property(string PropertyId, count(int Old)) {
    console.println("previous count removed: " + Old);
}

plan $cartago.signal(string ArtifactName, incremented(int N)) {
    console.println(ArtifactName + " was incremented to " + N);
}

Focusing produces addition events for existing properties. Updating a property produces a removal event for its old value and an addition event for its new value. A property event’s first argument is the property identifier, not the artifact name or an ArtifactId. A signal’s first argument is the artifact name. The second argument in each event is a funct pattern.

Use a property query when you need current state. Use an event plan when you need to react to a change. Do not rely on a signal being retained for an agent that starts observing later, or on a particular ordering between separate signal and property events.

Share the environment

A second agent must link to the environment and focus independently. It can find the counter by name:

cartago.link();
cartago.lookupArtifact("counter", cartago.ArtifactId Id);
cartago.focus(Id);
wait(cartago.property(Id, count(int N)));
cartago.operation(Id, increment());

The creator must have made the artifact before the lookup. Coordinate that readiness through a goal or message, rather than guessing a delay. Do not call startService() again for every agent in the same local application.

The example uses the local default workspace. CArtAgO also provides workspace-management and remote-environment facilities; those require additional setup beyond the local link() path shown here.

Results and failures

Artifact operations can return values through Java OpFeedbackParam<T> parameters. In ASTRA, pass a typed unbound variable in the corresponding argument position; the adapter binds it when the operation completes. This is also how makeArtifact and lookupArtifact return an ArtifactId.

An operation suspends its calling intention while CArtAgO executes it. An operation failure fails that intention’s action, so it can be handled with try/recover or failed-goal events:

try {
    cartago.lookupArtifact("counter", cartago.ArtifactId Id);
    cartago.operation(Id, increment());
} recover {
    console.println("could not find or increment the counter");
}

A successful operation and an updated observation are separate concerns: confirm the state you require with wait(...) or a property event. cartago.dumpProperties() prints the adapter’s observations and is useful when diagnosing missing focus or a mismatched property name.

API reference

Declare module Cartago cartago;. Actions are statements; terms return values; formulae are queried in guards, query(...) or wait(...).

Kind

Call

Purpose

Action

startService()

Start the local CArtAgO service once.

Action

link()

Join the local default workspace.

Action

makeArtifact(name, className, params, Id)

Create an artifact and bind a cartago.ArtifactId.

Action

lookupArtifact(name, Id)

Find an existing artifact.

Action

focus(Id)

Subscribe to observations.

Action

operation(Id, operation(...))

Execute an operation on a particular artifact.

Action

operation(operation(...))

Execute using CArtAgO’s operation selection.

Term

params(list arguments)

Convert creation arguments to a Java array.

Formula

property(Id, property(...))

Match an observed property on an artifact.

Formula

count(int N) (example)

Query a property directly by its name, without an artifact identifier.

Term

toFunction(cartago.ArtifactObsProperty property)

Convert a Java observable-property object to a function term.

Action

dumpProperties()

Print the current observations.

makeArtifact, lookupArtifact, focus and operation are dynamically dispatched CArtAgO operations, rather than individually declared Java module actions. Direct property queries use the property name, for example cartago.count(int N); property(Id, count(int N)) selects an artifact explicitly.

Property events use +$cartago.property(PropertyId, property(...)) and -$cartago.property(PropertyId, property(...)). Signals use $cartago.signal(ArtifactName, signal(...)). See the examples above for argument types.

Further reading

For HTTP resource bodies built from CArtAgO artifacts, see Multi-Agent Microservices (MAMS).