Skip to main content

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
Main.kt
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:

  • String and String, 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.
info

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 returns null if the plan is not relevant, or a context for the rest of the plan. takeIf { it == "sayHello" } makes the plan relevant for sayHello only;
  • an optional guard: onlyWhen { ... }, omitted here;
  • a body: the triggers { } block, a suspend lambda 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!
tip

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.