LightJason/AgentSpeak is the main component providing the agent functionality.
The component allows to build agents from the ASL script and contains all built-in actions.
The video presents a performance test of our framework with 15.000 agents in an emergency scenario. The video shows the system running in real-time on a single computer (iMac with 2,9 GHz Intel Core i5, 16 GB RAM and OSX El Captain 10.11.6).
The agents calculate their landmarks to the exit point (position 140 / 140 in the middle) in a grid world with 250x250 cells and start walking. If a cell is blocked by another agent, agents try to move to the right-side cell, if this is not possible to the left side cell; if neither is possible, the agent stops walking and waits for a random time until trying again to continue walking. If the agent doesn’t reach a landmark within five attempts, it skips the landmark and starts walking to the next one except the exit point.
We present a short overview of language examples of the AgentSpeak(L++) syntax. You can find the full EBNF description of the language syntax on the project documentation page. The Railroad / Syntax diagrams of the AgentSpeak(L++) language:
The language does not support looping directly; however, we support lambda expressions which are based on lambda calculus. Similar to a for each call, each element in an input list (variable) can be looped.
The example below creates a list of the number [1,20) and we are looping over the elements, first in sequential order and call the print action for each element and in the second call we add each value to the variable R. R is in this case the returning variable
L = collection/list/range(1, 20); (L) -> Y : generic/print(Y); (L) -> Y | R : R = Y+1;
AgentSpeak(L++) supports the implementation of repair planning with the default behaviour
-!. With this additional structure we also support repair action chains.
The following example shows the execution of three actions actionA, actionB, actionC. The system executes the actionA first, if the action fails, actionB will be executed, if this also fails actionC will be executed. If the last action in the statement fails (here: actionC), the whole plan fails.
actionA << actionB << actionC;
You can also use this technique if you don’t want a plan to fail: If an action might fail you can append a
<< trueto its invocation. This models the behaviour anything can go wrong, but the agent ignores the error(s).
actionA << true;
In general Prolog uses only logical rules. AgentSpeak(L) and AgentSpeak(L++) also use rules but additionally provide a plan structure. Within a Prolog structure the ordering is relevant to the execution semantic (see in Learn Prolog Now!).
But in AgentSpeak(L++) the ordering of rules and plans are not relevant for the execution semantic. Furthermore plan and rule structures can be grouped within the source code to simplify modelling different paths of execution.
This example shows the Ackermann function. The first line defines the rule name (literal) similar to Prolog. Each rule will be added by the
:-rule-sign under the literal. In classical Prolog the rule-literal must be for each different rule and Prolog executes the rules in sequential order. In our case we change this behaviour, so that each rule, which can be executed, will be executed. So we put a condition first to the rule, so this condition will deny or allow the execution. After that the rule-body will be added. For calling a rule from a plan or a rule, you need to put a
$-sign in front of the rule-name.
ackermann(N, M, R) :- N == 0; M > 0; R = M+1 :- M == 0; N > 0; TN = N-1; $ackermann(TN, 1, RA); R = RA :- N > 0; M > 0; TN = N-1; TM = M-1; $ackermann(N, TM, RI); $ackermann(TN, RI, RO); R = RO
The second example shows the structure for plans. For plans it is similar to the rule structure, but the
<-plan-sign is used and a condition of the plan execution can be added. Here exist three plans, the first will be executed, iif there is a belief
hellowith a string value, the second plan will be executed, iif there exists a belief
hellowith a numeric value which is greater than $1000$ and the third plan will be run every time (default plan).
: >>( hallo(X), generic/type/isstring(X) ) <- generic/print(“—”, “first plan”, “—”, “unification variables”, X)
: >>( hallo(X), generic/type/isnumeric(X) && X > 1000 ) <- generic/print(“—”, “second plan”, “—”, “unification variables”, X)
<- generic/print(“—”, “third (default) plan”, “—”) .
The examples creates a list if number within in the range [1,20) and 1 will be put into the variable A, 2 in B, 3 in C, 4 will be ignore, 5 in D, 6 in E, 7 in F and the list [8,20) in G
L = collection/list/range(1, 20); [A|B|C|_|D|E|F|G] = L;
The sign for parallelism is the at-sign (@) character. If the
@ is put in front of an action or variable the action will be executed in parallel and the variable will be thread-safe. Note: Not every action supports a parallel execution.