Basic: Hello world
Your first agent says hello and stops. Beliefs and goals are plain strings, matched with ordinary Kotlin code, so the project from Getting Started has everything it needs.
Replace the content of app/src/main/kotlin/org/example/App.kt with the code below, keeping its first line,
package org.example, and delete the generated test, app/src/test/kotlin/org/example/AppTest.kt.
Imports
import co.touchlab.kermit.Logger
import co.touchlab.kermit.Severity
import it.unibo.jakta.dsl.agent
import it.unibo.jakta.dsl.mas
import it.unibo.jakta.dsl.mas.runLocally
import it.unibo.jakta.dsl.node.NodeBuilders
import it.unibo.jakta.dsl.plan.triggers
import kotlinx.coroutines.runBlocking
val helloWorldAgent = agent<String, String, Any> {
embodiedAs { Any() }
hasInitialGoals {
!"sayHello"
}
hasPlanLibrary {
adding.goal {
takeIf { it == "sayHello" }
} triggers {
agent.print("Hello, world!")
node.terminateNode()
}
}
}
fun main(): Unit = runBlocking {
Logger.setMinSeverity(Severity.Assert)
mas(NodeBuilders.baseNode()) {
node { withAgents(helloWorldAgent) }
}.runLocally()
}
Step by step
The agent
agent<Belief, Goal, Body> { ... } defines an agent specification. The three type parameters are:
StringandString, the belief and goal types. The engine accepts any type: an incarnation such as the Prolog one gives you richer ones;Any, the type of the agent body: the agent's embodiment in the node it lives in.embodiedAs { Any() }creates it. This agent needs no body, so any object will do.
agent { } at the top level returns a factory: the agent is created only when it is added to a node.
Goals
hasInitialGoals { !"sayHello" } gives the agent the goal sayHello when it starts.
The ! operator adds a goal. See Goals.
Plans
hasPlanLibrary { } contains the agent's plans. A plan has:
- a trigger:
adding.goal { ... }receives each new goal and returnsnullif the plan is not relevant, or a context for the rest of the plan.takeIf { it == "sayHello" }makes the plan relevant forsayHelloonly; - an optional guard:
onlyWhen { ... }, omitted here; - a body: the
triggers { }block, asuspendlambda of ordinary Kotlin code.
Inside the body, agent is the agent's mutable state (print, believe, achieve, ...),
and node is the node the agent lives in: node.terminateNode() stops it.
Running the MAS
mas(NodeBuilders.baseNode()) { node { withAgents(helloWorldAgent) } } describes a multi-agent system
with a single node containing a single agent. .runLocally() executes it on coroutines, with all nodes in the
same process. Since it suspends, main uses runBlocking.
Logger.setMinSeverity(Severity.Assert) hides the engine logs, so that only what agents print is shown
(see Logging).
What runLocally() stands for
runLocally() is a shorthand for running the MAS with a CoroutineNodeRunner over a SharedMemoryNetwork:
mas(NodeBuilders.baseNode()) {
node { withAgents(helloWorldAgent) }
}.run(CoroutineNodeRunner(SharedMemoryNetwork()))
Run it from the project folder:
./gradlew run
The agent prints its message through the logger, with its id:
Assert: (Agent-1caa7468-e043-462f-ad5b-799f3f7591d2) Hello, world!
The same code is the hello-world example in the
JaKtA repository, where it runs with ./gradlew :examples:hello-world:run.
Next: learn about beliefs, or continue with Intermediate: Ping-pong agents.