Tutorial: Develop an AgentSpeak Scenario in 15 Minutes

This tutorial explains how to develop a simple, but fully working scenario in 15 minutes with the most recent version of the AgentSpeak(L++) source code.

Note: This tutorial aims at developers of multi-agent systems (MAS) and requires some basic understanding in programming.

Contents [Hide]

Previous Knowledge

The basic knowledge about agents and their execution mechanism from the knowledgebase

Tools You Need

  • Working Maven greater than 3.0 installation.
  • Java JDK 1.9 installation which can be obtained here.
  • Git installation (optional, but recommended)
    • Linux: Installing git via your favourite package manager should be sufficient.
    • MacOS: Using Homebrew with brew install git.
    • Git for Windows

Introduction

This tutorial gives you a very short introduction to LightJason’s AgentSpeak(L++) structure. Our source code documentation can help you in developing your own MAS project according to your individual requirements.

This tutorial is structured as follows: First you will create your own MAS project based on the template created by the Maven tool. Then you will add the LightJason/AgentSpeak(L++), hosted on maven central as a dependency to your own MAS project.

Note: If you prefer a more bleeding-edge version of AgentSpeak(L++), see → How to Build AgentSpeak from Source.

All further configuration of xml files and programming will then take place in your own project’s directory.

For the following sections we assume that you are working inside the directory Developer/. If you want to use another directory, replace Developer accordingly.

Maven Project Configuration

  1. Create an empty Maven project (see Maven in 5 minutes tutorial) inside the Developer directory:

    cd Developer
    mvn archetype:generate -DgroupId=myagentproject -DartifactId=myagentapp -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
    

    Maven will then create a project template, resulting in the following directory structure:

    ├── Developer/
    │   └── myagentapp/ <-- created by Maven
    │       ├── pom.xml
    │       └── src/
    │           ├── main/
    │           │   └── java/
    │           │       └── myagentproject/
    │           │           └── App.java
    │           └── test/
    │               └── java/
    │                   └── myagentproject/
    │                       └── AppTest.java
    

    Developer/myagentapp/ is the directory in which your own MAS projects resides.

  2. Take a note of the current values of groupId, artifactId and version of the AgentSpeak version provided by maven central:

    <dependency>
                <groupId>org.lightjason</groupId>
                <artifactId>agentspeak</artifactId>
                <version>0.0.1</version>
    </dependency>

    Inside Developer/myagentapp/ open the pom.xml with your preferred (programming) editor, navigate to the <dependencies> section and add the complete excerpt above or below the already present <dependency> entries (for example, you will also find an entry for JUnit within this section).

  3. For LightJason/AgentSpeak to run, it is crucial to enforce Java 1.9 support in your project. Add the following entry before the <dependencies> section:

    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.source>1.9</maven.compiler.source>
            <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
        </properties>
    

  4. Put the following code inside the <project> section, e.g. after </dependencies>. This sets the current Maven components e.g. documentation build, packaging with Maven Shade Plugin which creates an executable JAR when you build your project with mvn package. The commented-out section defines the LightJason styleguide checking for the project

    
        <build>
            <!-- make "package" the default goal to run "mvn package" every time -->
            <defaultGoal>package versions:display-dependency-updates versions:display-plugin-updates versions:display-property-updates</defaultGoal>
            <!-- using build plugins -->
            <pluginManagement>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-deploy-plugin</artifactId>
                        <version>2.8.2</version>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-install-plugin</artifactId>
                        <version>2.5.2</version>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-resources-plugin</artifactId>
                        <version>3.0.2</version>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-site-plugin</artifactId>
                        <version>3.6</version>
                        <configuration>
                            <tempWebappDirectory>${basedir}/target/site</tempWebappDirectory>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-surefire-plugin</artifactId>
                        <version>2.20</version>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <version>3.0.2</version>
                    </plugin>
                </plugins>
            </pluginManagement>
            <plugins>
                <!-- enable compiler warnings -->
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.6.1</version>
                    <configuration>
                        <compilerArgument>-Xlint:all</compilerArgument>
                        <showWarnings>true</showWarnings>
                        <showDeprecation>true</showDeprecation>
                        <failOnWarning>true</failOnWarning>
                    </configuration>
                </plugin>
                <!-- let "mvn clean" clean up additional files -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.0.0</version>
                    <configuration>
                        <filesets>
                            <fileset>
                                <directory>.</directory>
                                <includes>
                                    <include>dependency-reduced-pom.xml</include>
                                </includes>
                            </fileset>
                        </filesets>
                    </configuration>
                </plugin>
                <!-- create an executable jar file -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.4.3</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <transformers>
                                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                        <mainClass>myagentproject.App</mainClass>
                                    </transformer>
                                </transformers>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <!-- enforce java and maven version -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-enforcer-plugin</artifactId>
                    <version>3.0.0-M1</version>
                    <executions>
                        <execution>
                            <id>defaults</id>
                            <goals>
                                <goal>enforce</goal>
                            </goals>
                            <configuration>
                                <rules>
                                    <requireMavenVersion>
                                        <version>[3.1,)</version>
                                    </requireMavenVersion>
                                    <requireJavaVersion>
                                        <version>[${maven.compiler.source},)</version>
                                    </requireJavaVersion>
                                </rules>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <!-- checkstyle is performed during the compile phase
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-checkstyle-plugin</artifactId>
                    <version>2.17</version>
                    <dependencies>
                        <dependency>
                            <groupId>com.puppycrawl.tools</groupId>
                            <artifactId>checkstyle</artifactId>
                            <version>[7.8.2,)</version>
                        </dependency>
                    </dependencies>
                    <executions>
                        <execution>
                            <id>checkstyle</id>
                            <phase>validate</phase>
                            <goals>
                                <goal>check</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <configLocation>src/checkstyle/style.xml</configLocation>
                        <encoding>UTF-8</encoding>
                        <consoleOutput>true</consoleOutput>
                        <failsOnError>true</failsOnError>
                        <includeTestSourceDirectory>true</includeTestSourceDirectory>
                    </configuration>
                </plugin>
                 -->
            </plugins>
        </build>
    

  5. Test-build your project by running mvn package inside Developer/myagentapp/, i.e. where your pom.xml is located:

    cd Developer/myagentapp/
    mvn package
    

    It should print BUILD SUCCESS.

    The resulting, runnable JAR is located at Developer/myagentapp/target/myagentapp-1.0-SNAPSHOT.jar.

  6. Import your Maven project into your preferred IDE.

Agent and Generator Classes

Note: The file names and paths provided in the following sections are relative to your project folder. For example src/main/java/myagentproject/MyAgent.java refers to the file MyAgent.java located at Developer/myagentapp/src/main/java/myagentproject/.

Agent Class

Each agent you use must be inherited from our base class IAgent interface, but we recommend our IBaseAgent with a complete execution mechanism. Please note that you need to pass your agent class as a generic parameter to the definition of a LightJason agent class. A necessary property is the serialVersionUID which is defined by the Java Serializable interface - this allows to serialise the agent e.g. to transfer the agent over the network

Create an agent class MyAgent.java in src/main/java/myagentproject/ as follows:


package myagentproject;
import org.lightjason.agentspeak.agent.IBaseAgent;
import org.lightjason.agentspeak.configuration.IAgentConfiguration;
import javax.annotation.Nonnull;
final class MyAgent extends IBaseAgent<MyAgent>
{
    
    private static final long serialVersionUID = -2111543876806742109L;
    
    MyAgent( @Nonnull final IAgentConfiguration<MyAgent> p_configuration )
    {
        super( p_configuration );
    }
}

Agent Generator Class

Next create your own agent generator (agent factory). This component is based on the UML factory pattern. Within the factory the agent script (ASL) is parsed once and you can generate a lot of agents with a single factory. We support a general implementation of the factory the IBaseAgentGenerator.

Create an agent generator class MyAgentGenerator.java in src/main/java/myagentproject/ as follows:


package myagentproject;
import org.lightjason.agentspeak.common.CCommon;
import org.lightjason.agentspeak.generator.IBaseAgentGenerator;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.InputStream;
import java.util.stream.Collectors;
final class MyAgentGenerator extends IBaseAgentGenerator<MyAgent>
{
    
    MyAgentGenerator( @Nonnull final InputStream p_stream ) throws Exception
    {
        super(
            
            p_stream,
            
            CCommon.actionsFromPackage().collect( Collectors.toSet() )
        );
    }
    
    @Nullable
    @Override
    public final MyAgent generatesingle( @Nullable final Object... p_data )
    {
        return new MyAgent( m_configuration );
    }
}

Write Your Own Runtime

In this section you will write your own runtime1 within the main method of the App class. The runtime is responsible for running the agents in each cycle. We are using Java streams to execute the agent, but you can use also a thread-pool, because all agents implement the Callable interface (the Future object is the agent in the state $cycle + 1$)


package myagentproject;
import java.io.FileInputStream;
import java.util.Collections;
import java.util.Set;
import java.util.logging.LogManager;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
final class App
{
    static
    {
        
        LogManager.getLogManager().reset();
    }
    
    private App()
    {
    }
    
    public static void main( final String[] p_args )
    {
        if ( p_args.length < 2 )
            throw new RuntimeException( "arguments are not set: ASL script, number of agents" );
        
        
        
        
        final Set<MyAgent> l_agents;
        try
            (
                final FileInputStream l_stream = new FileInputStream( p_args[0] );
            )
        {
            
            l_agents = Collections.unmodifiableSet(
                new MyAgentGenerator( l_stream )
                    .generatemultiple( Integer.parseInt( p_args[1] ) )
                    .collect( Collectors.toSet() )
            );
        }
        catch ( final Exception l_exception )
        {
            l_exception.printStackTrace();
            return;
        }
        
        IntStream
            .range(
                0,
                p_args.length < 3
                ? Integer.MAX_VALUE
                : Integer.parseInt( p_args[2] )
            )
            .forEach( j -> l_agents.parallelStream()
                                   .forEach( i ->
                                   {
                                       try
                                       {
                                           i.call();
                                       }
                                       catch ( final Exception l_exception )
                                       {
                                           l_exception.printStackTrace();
                                           throw new RuntimeException();
                                       }
                                   } ) );
    }
}

Write Your Agent Script

Create a simple Hello World agent for testing purposes. Add a file agent.asl in the top-level directory of your project with the following content:


!main.
+!main <-
    generic/print("Hello World!");
    !mynextgoal
.
+!mynextgoal <-
    generic/print("Hello World! (again)");
    !mynextgoal
.

The agent starts in cycle $0$ with the initial goal !main. As the plan main matches, it gets executed, i.e. it prints “Hello World” and adds mynextgoal to be triggered in the next cycle. In cycle $1$ and following cycles $1+n$ the agent will execute the plan mynextgoal, printing Hello World! (again) with the current cycle number and adding the trigger for the same plan for the following cycle.

Run It

  1. Run Maven within your project directory to build the program:

    mvn package
    
  2. Run the program to create 500 agents based on the agent.asl and the agents will run 1000 cycles:

    java -jar target/myagentapp-1.0-SNAPSHOT.jar agent.asl 500 1000
    
  3. Observe the CPU load and time with the print actions (code above) and without (code below):

    
    !main.
    +!main <-
        !mynextgoal
    .
    +!mynextgoal <-
        !mynextgoal
    .
    

    i.e. run

    java -jar target/myagentapp-1.0-SNAPSHOT.jar agent.asl 500 1000
    

    and compare it with

    java -jar target/myagentapp-1.0-SNAPSHOT.jar agent_noprint.asl 500 1000
    

    With an i7-3770 4C/8T CPU (“benchmarked” with the Linux/Unix tool time) this yields

    time java -jar target/myagentapp-1.0-SNAPSHOT.jar agent.asl 500 1000
    ...
    62.57s user 6.33s system 438% cpu 15.727 total
    

    vs.

    time java -jar target/myagentapp-1.0-SNAPSHOT.jar agent_noprint.asl 500 1000
    ...
    44.06s user 1.47s system 595% cpu 7.638 total
    

    It is noteworthy that

    • the load gets well distributed across the CPU-cores and
    • too many prints have a negative impact on the performance, as you are then basically benchmarking your shell ;-)

Reference Solution

If you struggled at some point or wish to obtain our exemplary solution with code documentation of this tutorial, you can download the archive containing the source code and an executable jar file:


  1. For creating a complex and fast runtime, have a look at general object-orientated programming patterns. Here we only provide a short example to show you how you can work with AgentSpeak(L++) agents. [return]