Skip to main content

DSL Reference

A complete list of the public DSL provided by jakta-api, jakta-dsl and jakta-core, independent of the incarnation. The generated API docs have the full signatures; this page shows what each element is for and where to import it from.

In the signatures below, B, G and Body are the belief, goal and body types, and Ctx is a plan's context type.

Structure of a MAS​

Entry points​

Package it.unibo.jakta.dsl (jakta-core).

FunctionReturnsMeaning
mas(​builderFactory) { .​.​.​ }MasBuilderDefines a multi-agent system. builderFactory creates the builder of each node, e.g. NodeBuilders.​baseNode(​).
node(​builderFactory) { .​.​.​ }ExecutableNode<​Body>Defines a stand-alone node, to be combined with withNodes(​.​.​.​).
agent<​B,​ G,​ Body>​ { .​.​.​ }(​Node<​Body>​) ->​ AgentSpecificationDefines an agent factory with a random BaseAgentID. The agent is built when added to a node.
agent<​B,​ G,​ Body>​(​id) { .​.​.​ }(​Node<​Body>​) ->​ AgentSpecificationAs above, with an explicit AgentID.
plans<​B,​ G,​ Body>​ { node ->​ .​.​.​ }(​Node<​Body>​) ->​ List<​Plan>Defines a reusable list of plans, installed with withPredefinedPlans(​.​.​.​). The lambda receives the node the agent lives in.

Other entry-level utilities:

ElementPackageMeaning
NodeBuilders.​baseNode<​Body>​(​)it.​unibo.​jakta.​dsl.​nodeBuilder factory for in-memory BaseNodes.
MasBuilder.​runLocally(​)it.​unibo.​jakta.​dsl.​masSuspending shortcut for run(​CoroutineNodeRunner(​SharedMemoryNetwork(​))).
BaseAgentID(​name)it.​unibo.​jakta.​agentAn agent identifier: name is the displayName, the identity is a random UUID (its toString(​)). Two BaseAgentID(​"Bob") are different agents.

MasBuilder​

Package it.unibo.jakta.dsl.mas.

MemberMeaning
node { .​.​.​ }Adds a node built with the MAS builder factory. The receiver is a NodeBuilder.
withNodes(​vararg nodes)Adds nodes built separately with node(​.​.​.​) { }.
suspend run(​runner:​ NodeRunner)Runs every node with runner, and returns when all nodes have terminated.

NodeBuilder​

Package it.unibo.jakta.dsl.node.

MemberMeaning
node:​ Node<​Body>The node being built. Use it to create node-bound skills, e.g. MessagingSkill(​node).
agent<​B,​ G>​ { .​.​.​ } / agent<​B,​ G>​(​id) { .​.​.​ }Defines an agent living in this node. The receiver is an AgentBuilder.
withAgents(​vararg factories)Adds agents defined elsewhere with the top-level agent { }.

AgentBuilder​

Package it.unibo.jakta.dsl.agent.

MemberMeaning
embodiedAs { id ->​ Body }Required. Creates the agent's body from its id.
believes { +belief }Initial beliefs; + adds one.
hasInitialGoals { !goal }Initial goals, pursued when the agent starts; ! adds one.
hasPlanLibrary { .​.​.​ }The agent's plans. The receiver is a PlanLibraryBuilder.
withPredefinedPlans(​vararg plans)Adds plans created with plans { node ->​ .​.​.​ }.
handlesPerceptionEvents { perception ->​ AgentUpdate? }Maps perceptions to updates of the agent. By default perceptions are ignored.
handlesMessageEvents { message ->​ AgentUpdate? }Maps received messages to updates of the agent. By default messages are discarded.
addBelief(​b), addGoal(​g)Programmatic alternatives to believes and hasInitialGoals.
addBeliefPlan(​plan), addGoalPlan(​plan)Adds an already-built plan.
node:​ Node<​Body>The node the agent will live in.

The handlers run with the agent's AgentState as receiver, so they can read beliefs, intentions, beliefPlans and goalPlans.

Plans​

Package it.unibo.jakta.dsl.plan. Inside hasPlanLibrary { } (a PlanLibraryBuilder<B, G>):

adding.goal {
// trigger: G.() -> Ctx?
} onlyWhen {
// guard (optional): GuardScope<B, Ctx>.() -> Ctx?
} triggers {
// body: suspend PlanScope<B, G, Ctx>.() -> PlanResult
}

Triggers​

TriggerRelevant for
adding.​goal { }the addition of a goal (initial goals, achieve, alsoAchieve, AgentUpdate.​Goal additions)
removing.​goal { }the removal of a goal (AgentUpdate.​Goal removals)
failing.​goal { }the failure of a goal: no applicable plan, or the plan body threw an exception
adding.​belief { }the addition of a belief not already in the belief base
removing.​belief { }the removal of a belief that was in the belief base

The trigger block receives the goal or belief as this and returns the plan context if the plan is relevant, null otherwise. The type of the context is inferred from the block.

note

A goal removal only triggers removing.goal plans: intentions already pursuing that goal are not stopped.

Guards​

onlyWhen { } is optional. It runs with a GuardScope<B, Ctx> receiver (package it.unibo.jakta.plan):

MemberMeaning
beliefs:​ Collection<​B>A snapshot of the belief base.
context:​ CtxThe context returned by the trigger.

It returns the context for the body (usually context, possibly refined) or null if the plan is not applicable. Without a guard, every relevant plan is applicable.

Plan selection​

When an event occurs, the plans of the matching kind are filtered by trigger (relevant) and then by guard (applicable), and the first applicable plan in declaration order is executed. If no plan is applicable for a goal addition, the goal fails. If none is applicable for a belief event, the event is just logged.

Body​

triggers { } is a suspend lambda with a PlanScope<B, G, Ctx> receiver (package it.unibo.jakta.plan), and the context is also available as a Kotlin context parameter. The value of its last expression is the result of the plan, returned to whoever pursued the goal with achieveWithResult.

MemberMeaning
agent:​ MutableAgentState<​B,​ G>The agent's state, see below.
context:​ CtxThe context produced by the trigger and the guard.
nodeNot a member of PlanScope: it is the node of the enclosing builder (or the parameter of plans { node ->​ }), captured by the lambda.

Plus any skill available in the enclosing context(...) blocks, reachable with contextOf<Skill>() or through the skill's extension functions.

Agent state​

MutableAgentState<B, G> (package it.unibo.jakta.agent), available as agent in plan bodies.

MemberMeaning
id:​ AgentIDThe agent's identifier.
beliefs:​ Collection<​B>A snapshot of the belief base (a set: adding an existing belief generates no event).
intentions:​ Set<​Intention>The intentions currently executing.
believe(​b)Adds a belief, generating a belief-addition event if it was not present.
forget(​b)Removes a belief, generating a belief-removal event if it was present.
suspend achieve(​g)Pursues a sub-goal in the current intention and suspends until it is achieved.
suspend achieveWithResult<​R>​(​g):​ RAs achieve, returning the result of the plan that achieved the goal.
alsoAchieve(​g)Pursues a goal in a new intention, without waiting for it.
suspend wait(​filter,​ timeout = null):​ T?Suspends until an event for which filter returns non-null happens, and returns that value, or null on timeout.
print(​message:​ String)Logs a message tagged with the agent's name. It is logged at the highest severity, so it survives Logger.​setMinSeverity(​Severity.​Assert).
addPlan(​plan)Adds a plan to the plan library at runtime.
setPerceptionHandler { }, setMessageHandler { }Replaces the perception or message handler at runtime.

achieve and achieveWithResult are extension functions: import it.unibo.jakta.agent.achieve and it.unibo.jakta.agent.achieveWithResult.

For example, wait can suspend a plan until a belief appears:

adding.goal {
takeIf { it == "waitForGo" }
} triggers {
val go = agent.wait({ event ->
(event as? AgentEvent.Internal.Belief.Add<*>)?.belief?.takeIf { it == "go" }
}, timeout = 5.seconds)
agent.print(if (go != null) "Go!" else "Timed out")
}

Node​

Node<Body> (package it.unibo.jakta.node), available as node in builders and plan bodies.

MemberMeaning
id:​ NodeIDThe node identifier.
agents:​ Map<​AgentID,​ Body>The agents currently in the node, with their bodies.
publishEvent(​event,​ filter = { true })Delivers an external event. A Perception is delivered to the agents of this node whose body satisfies filter. A Message is sent over the network, to the agents of every node whose body satisfies filter.
addAgent(​factory,​ nodeID = id)Adds an agent created with agent { }, by default to this node.
removeAgent(​id)Removes an agent.
terminateNode(​error = null,​ nodeID = id)Stops a node, by default this one, with all its agents.
getAgentIDfromBody(​body)The id of the agent with the given body.

For instance, a perception for a single agent, found by its body:

node.publishEvent(Temperature(21)) { body -> body == node.agents[targetAgentId] }

Events and updates​

Package it.unibo.jakta.event.

TypeMeaning
AgentEvent.​Internal.​Goal.​Add / .​Remove / .​FailedA goal was added, removed or failed. Has goal.
AgentEvent.​Internal.​Belief.​Add / .​RemoveA belief was added or removed. Has belief.
AgentEvent.​Internal.​StepScheduling of an intention (internal).
AgentEvent.​External.​Message<​P>​(​payload,​ sender)A message received from sender.
AgentEvent.​External.​PerceptionInterface to implement for your own perceptions.
AgentUpdate.​Belief(​additions,​ removals = emptySet(​))Returned by handlers: beliefs to add and remove.
AgentUpdate.​Goal(​additions,​ removals = emptySet(​))Returned by handlers: goals to add and remove.

When an update is applied, elements present both in additions and removals are ignored, removals are applied first, and each change generates the corresponding internal event.

Custom perceptions are plain classes:

data class Temperature(val celsius: Int) : AgentEvent.External.Perception

handlesPerceptionEvents { perception ->
when (perception) {
is Temperature -> AgentUpdate.Belief(
additions = setOf("temperature(${perception.celsius})"),
removals = beliefs.filter { it.startsWith("temperature(") }.toSet(),
)
else -> null
}
}

Skills​

Package it.unibo.jakta.skills (jakta-core). All skills extend Skill<Body>(node).

SkillFunctionsMeaning
MessagingSkill(​node)Agent.​sendTo(​receiver,​ payload)Sends payload to the agent receiver, in any node.
Agent.​broadcast(​payload)Sends payload to every other agent, in any node.
AgentTerminationSkill(​node)Agent.​terminate(​)Removes the agent from the node. Also available with just a context(​node) in scope.
NodeTerminationSkill(​node)PlanScope.​terminateNode(​)Stops the node.

Skills are made available with context(skill) { ... } around agent or plan definitions, and the corresponding functions must be imported (e.g. it.unibo.jakta.skills.sendTo). The built-in skills take a Node<Any>, so they are meant for nodes built with NodeBuilders.baseNode() (body type Any).

node {
context(MessagingSkill(node), NodeTerminationSkill(node)) {
agent<String, String>(BaseAgentID("worker")) {
embodiedAs { Any() }
hasInitialGoals { !"work" }
hasPlanLibrary {
adding.goal {
takeIf { it == "work" }
} triggers {
agent.broadcast("done")
terminateNode()
}
}
}
}
}

Runners and networks​

Package it.unibo.jakta.node (jakta-core).

TypeMeaning
NodeRunner<​N>Executes nodes: suspend run(​node).
CoroutineNodeRunner(​network)Runs every agent of a node as a coroutine, stepping its reasoning cycle; each plan is a child coroutine.
NodeNetworkConnects nodes, delivering system events (messages, agent additions and removals, shutdowns) among them.
SharedMemoryNetwork(​)A NodeNetwork for nodes running in the same process.

The Alchemist incarnation provides its own runtime, see Simulate a MAS with Alchemist.