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 |
|
Artifact |
A Java object exposing operations and observable state |
|
Operation |
Work performed by an artifact |
|
Observable property |
Persistent state visible to agents that focus on the artifact |
|
Signal |
A transient notification from an artifact |
|
Focus |
Subscribe to an artifact’s observations |
|
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.
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 |
|
Start the local CArtAgO service once. |
Action |
|
Join the local default workspace. |
Action |
|
Create an artifact and bind a |
Action |
|
Find an existing artifact. |
Action |
|
Subscribe to observations. |
Action |
|
Execute an operation on a particular artifact. |
Action |
|
Execute using CArtAgO’s operation selection. |
Term |
|
Convert creation arguments to a Java array. |
Formula |
|
Match an observed property on an artifact. |
Formula |
|
Query a property directly by its name, without an artifact identifier. |
Term |
|
Convert a Java observable-property object to a function term. |
Action |
|
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
Agent communication: coordinate agents that share artifacts.
EIS environments: another approach to connecting agents to environments.
For HTTP resource bodies built from CArtAgO artifacts, see Multi-Agent Microservices (MAMS).