exagent: an agent oriented language for the beam virtual...
TRANSCRIPT
ExAgent: an agent oriented
language for the BEAM virtual
machine
Omar Abdelhafith, Bachelor of science
A thesis submitted to University College Dublin
in fulfilment of the requirements for the degree of
Master of Science
College of Engineering, Mathematical and Physical Sciences
School of Computer Science and Informatics
Head of School: Dr Padraig Cunningham
Supervisor: Dr Rem Collier
May 2017
c© by Omar Abdelhafith, 2017
All Rights Reserved.
ii
Abstract
Agent-oriented programming (AOP) has proven itself to be a successful program-
ming paradigm. It especially shines when solving complicated problems and
simulations that might be otherwise difficult to solve with other programming
paradigms like object oriented programming. These problems range from military
grade simulations to large scale factory automation solutions.
There are multiple AOP languages and frameworks available that cover
different types of AOP architectures. The most popular of these languages are
built using Java and the Java Virtual Machine (JVM).
In this dissertation, we make the case for using Erlang and Elixir programming
languages and the Bogdan/Bjrn’s Erlang Abstract Machine (BEAM) virtual
machine as the frameworks more suitable to develop powerful, performant, and
scalable AOP languages.
We discuss Erlang’s language features that make it a good fit for AOP. And
we present the case for using Elixir programming language, which runs on top of
Erlang’s BEAM VM, as the programming language and platform used to develop
the presented AOP language. We then proceed at introducing ExAgent; an AOP
language implemented using Elixir that runs on the BEAM VM.
Finally, we present a comparison between ExAgent, that runs on BEAM
VM, and JASON AOP language, that runs on JVM, in terms of operational
performance, feature set and ease of use.
CONTENTS
List of Tables v
List of Figures vi
Code Listing vii
1 Introduction 1
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Agent languages, architectures, and frameworks 6
2.1 What is an agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 Intentional Agents . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.3 Agent architectures . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4 AOP languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.1 AGENT0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4.2 AGENTSPEAK(L) . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.3 GOAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2.4.4 JASON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4.5 ASTRA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.5 Agent Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5.1 Jade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5.2 Jadex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5.3 Cougaar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
i
2.5.4 eXAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3 Erlang as a viable agent environment 17
3.1 The actor model . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2 Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.3 Elixir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.4 A comparison between actors and agents . . . . . . . . . . . . . . . 23
3.4.1 Actors are passive, agents are active . . . . . . . . . . . . . . 23
3.4.2 Actors and Agents affect their systems differently . . . . . . 23
3.4.3 Actors and Agents use messaging as a mean of communication 24
3.4.4 The actor state is comparable to the agent state . . . . . . . 24
3.5 Using Erlang and its actor-model for agent development . . . . . . 25
3.5.1 The actor model is suitable to implement an AOP runtime . 25
3.5.2 Actor messaging is suitable for AOP agent communication . 27
3.5.3 Erlang provide capabilities for agents distribution . . . . . . 27
3.5.4 Erlang help building fault tolerant systems . . . . . . . . . . 28
3.5.5 Erlang provide useful tools for AOP languages . . . . . . . . 29
3.5.6 Elixir provide diverse targets and platform to use AOP . . . 29
3.5.7 Elixir provides a quick way to prototype a language . . . . . 30
3.6 Related studies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4 ExAgent AOP language 33
4.1 Why ExAgent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.2 ExAgent’s agent model . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.2.1 AGENTSPEAK archetype . . . . . . . . . . . . . . . . . . . 35
4.2.2 Deviation from AGENTSPEAK . . . . . . . . . . . . . . . . 36
4.2.3 Agent environments . . . . . . . . . . . . . . . . . . . . . . . 38
4.3 Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.4 Language syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.5 ExAgent language concepts . . . . . . . . . . . . . . . . . . . . . . 42
4.5.1 Creating an agent . . . . . . . . . . . . . . . . . . . . . . . . 42
4.5.2 Initial beliefs and goals . . . . . . . . . . . . . . . . . . . . . 43
ii
4.5.3 ExAgent instructions . . . . . . . . . . . . . . . . . . . . . . 45
4.5.4 Plan rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.5.5 Message handlers . . . . . . . . . . . . . . . . . . . . . . . . 47
4.5.6 Recovery handlers . . . . . . . . . . . . . . . . . . . . . . . . 48
4.5.7 Roles and reusability . . . . . . . . . . . . . . . . . . . . . . 49
5 ExAgent design and implementation 51
5.1 Quick tour of Elixir programming language . . . . . . . . . . . . . . 51
5.2 ExAgent Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.3 Code parsing and generation . . . . . . . . . . . . . . . . . . . . . . 54
5.4 Agent creation and state handling . . . . . . . . . . . . . . . . . . . 58
5.5 Agent reasoning cycle . . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.5.1 Message processing and Event selection . . . . . . . . . . . . 60
5.5.2 Plan selection . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.5.3 Intent processing . . . . . . . . . . . . . . . . . . . . . . . . 62
5.5.4 Error handling . . . . . . . . . . . . . . . . . . . . . . . . . 63
5.5.5 New state generation . . . . . . . . . . . . . . . . . . . . . . 63
6 Evaluating ExAgent 67
6.1 Performance evaluation . . . . . . . . . . . . . . . . . . . . . . . . . 67
6.1.1 Counter Agent . . . . . . . . . . . . . . . . . . . . . . . . . 68
6.1.2 Token ring . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
6.2 Feature set evaluation . . . . . . . . . . . . . . . . . . . . . . . . . 72
6.2.1 Agent language interoperability . . . . . . . . . . . . . . . . 72
6.2.2 Plan recovery and error handling . . . . . . . . . . . . . . . 75
6.2.3 Code sharing and reusability . . . . . . . . . . . . . . . . . . 77
6.3 Generating a JSON API server for ExAgent agents . . . . . . . . . 81
7 Conclusions and future work 89
7.1 Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
7.2 Conclusion and final remarks . . . . . . . . . . . . . . . . . . . . . 90
References 93
iii
A Building ExAgent from source 100
A.1 Counter Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
A.2 Ping Pong Agent . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
B Creating an ExAgent counter agent 103
iv
LIST OF TABLES
6.1 ExAgent vs JASON counter agent performance . . . . . . . . . . . 69
6.2 ExAgent vs JASON token ring performance . . . . . . . . . . . . . 71
v
LIST OF FIGURES
5.1 Reasoning cycle process flow diagram . . . . . . . . . . . . . . . . . 66
vi
LISTINGS
4.1 ExAgent language BNF Grammar . . . . . . . . . . . . . . . . . . . 40
5.1 ”ExAgent reasoner main function” . . . . . . . . . . . . . . . . . . 65
6.1 ”JASON Counter Agent Test” . . . . . . . . . . . . . . . . . . . . . 68
6.2 ”ExAgent Counter Agent Test” . . . . . . . . . . . . . . . . . . . . 69
6.3 ”ExAgent Token Ring Test” . . . . . . . . . . . . . . . . . . . . . . 71
6.4 ”JASON Java code to read system time” . . . . . . . . . . . . . . . 74
6.5 ”JASON agent code to read system time” . . . . . . . . . . . . . . 74
6.6 ”ExAgent code to read system time” . . . . . . . . . . . . . . . . . 75
6.7 ”ExAgent plan recovery mechanism” . . . . . . . . . . . . . . . . . 76
6.8 ”ExAgent defining roles” . . . . . . . . . . . . . . . . . . . . . . . . 79
6.9 ”ExAgent using roles in agents” . . . . . . . . . . . . . . . . . . . . 79
6.10 ”Elixir Plug sample code” . . . . . . . . . . . . . . . . . . . . . . . 82
6.11 ”ExAgent CarDealer agent definition” . . . . . . . . . . . . . . . . 83
6.12 ”ExAgent using ExAgent.Web.webify method” . . . . . . . . . . . . 84
6.13 ”ExAgent.Web JSON metadata API endpoint” . . . . . . . . . . . 86
vii
Acknowledgements
I would like to thank my dissertation supervisor Dr. Rem W. Collier for all
the support and guidance that he provided.
To my wife Maisoon and daughter Maria, thank you for putting up with me
in all the time I was absent. This would not have been possible without you.
Finally, I would also like to thank my company, Zendesk, for giving me time
to attend the course.
viii
CHAPTER
ONE
INTRODUCTION
1.1 Motivation
Agent-oriented programming (AOP) is a relatively new programming paradigm
introduced by Yoav Shoham in the early nineties [1]. This paradigm differs from
the familiar Object Oriented Programming (OOP) and Procedural Programming
(PP) in its introduces the concept of mental states of a program.
The main component of a program in an Agent Programming Language (AOP)
program, as put by Shoham, is the Agent. The agent state is described as a set of
mentalistic concepts, which is called the agent mental state. This state consists
of beliefs, decisions, capabilities and obligations.
As part of the introductory article, Shoham introduced AGENT-0; a simplistic,
lisp-like agent-oriented language. In addition to AGENT-0, Shoham introduced
a simple interpreter that can interpret AGENT-0 language and provide a testing
ground to prototype solving problems with agent programming.
The introduction of AOP and AGENT-0 spiked the interest of many software
engineering practitioners and language designers, which lead to the creation of
multiple different agent-oriented languages and frameworks.
Bordini et al. [2] conducted a survey study in 2005 about the status and
development of AOP languages, platforms, and tools. These languages surveyed
were divided into three paradigms; declarative, imperative and hybrid. The survey
showed that Java and the Java Virtual Machine (JVM) are the most popular
underlying language and virtual machine used to develop agents. This finding was
also observed by Di Stefano et al [3] and Van Bien et al [4]. Among the famous
AOP languages backed by the JVM is JASON [5], a Java-Based interpreter for an
extended version of AgentSpeak, and JADE [6], which is an open source platform
for peer-to-peer agent based applications.
Java is an OOP language, and hence, the JVM is optimised to work in that
programming paradigm. In order to develop an agent model on top of the OOP
model that Java provides, the AOP language creators have to provide multiple
intermediate levels of abstractions to bridge the conceptual gap between OOP and
AOP. It has been argued by multiple sources [7] [3] that due to these conceptual
differences between OOP and AOP, other development environments are better
suited to be used for building AOP languages.
In this dissertation we started with a belief that other programming paradigms,
namely the actor programming model, are better suited to build performant,
scalable and optimised AOP languages.
The topics of using actor model languages, such as Erlang and Scala, to build
AOP languages has been aptly covered with research from Di Stefano et al. [3],
Santoro et al. [8], Mitrovi et al. [2] and others. In these research, actor model
was argued to be a better fit for designing AOP languages.
In this dissertation we focus on Erlang [9] and the Bogdan/Bjrn’s Erlang
Abstract Machine (BEAM) [9] as the development environment to design and
implement the presented agent language, ExAgent. We believe that the actor
model is conceptually closer to the agent model from than the OOP model. This
observation is strongly inspired from the research resources listed before.
Instead of using Erlang directly, we used Elixir [10], which is a dynamic,
functional programming language designed for building scalable and maintainable
applications. Elixir is built on top of Erlang and utilises the BEAM VM.
We decided to use Elixir since it provides powerful meta-programming features;
especially procedural macros, that helped us prototype creating ExAgent in a
timely manner.
2
We will reflect on Erlang, Elixir and the BEAM VM in this dissertation,
covering the aspects that make them successful programming environments to
build ExAgent AOP language.
3
1.2 Objectives
Following on the motivations described in the previous section, which can be
broadly put as; to provide the reasons that lead the effort to create an AOP
language that operates on top of the Erlang’s BEAM VM. We state bellow the
main objectives for this dissertation:
1. Provide a comparison between the actor programming and the agent
programming models. As part of this comparison, we cover some of the
current AOP languages that are implemented using JAVA and the JVM.
2. Reflect on Erlang and the BEAM VM covering the important features that
make them a good environment to implement agent oriented languages.
Including topics as Erlang processes, inter-process communication, fault
tolerance, distribution, debugging and profiling. In addition, we provide the
rationale behind choosing Elixir as the programming language to develop
ExAgent.
3. Present ExAgent AOP language. In this objective, we discuss ExAgent
language design and implementation. Starting with the languages that
inspired ExAgent’s agent model and architecture. Following with its lan-
guage syntax and Backus-Naur Form (BNF) notation, the code interpreter,
language features and the agent sensing, deliberation and reasoning cycle.
4. Provide an evaluation of ExAgent AOP language by comparing it with
JASON language. The evaluation is divided into performance and feature set
evaluation and concludes with the features that make ExAgent an interesting
AOP language for Elixir and Erlang developers. The evaluation also presents
a work-in-progress library that automatically generates a web server for
agents written in ExAgent.
To cover the main objectives stated above, we organised the dissertation in
seven chapters:
4
• Chapter 1 - Introduction. This chapter discusses the motivation and the
objectives for the dissertation.
• Chapter 2 - Agent languages, architectures, and frameworks. This chapter
starts by defining what an agent is in AI and AOP terms. It follows
by introducing the concept of intentional and rational agents. Finally, it
covers the different agent programming languages, agent architectures, and
frameworks selected for this study.
• Chapter 3 - Erlang as a viable agent environment. In this chapter, we cover
a short introduction to Erlang, Elixir and the actor model of computation.
We also discuss previous attempts in using actor-oriented languages to
implement AOP languages. Finally, we present the reasons behind using
Erlang as the software environment to develop ExAgent AOP language.
• Chapter 4 - ExAgent AOP language. EXAgent, the language developed
for this dissertation, will be covered in this chapter. This chapter includes
ExAgent agent model and architecture, the language syntax, the code
interpreter and the language features implemented.
• Chapter 5 - ExAgent design and implementation. In this chapter, we dig
deeper into some of ExAgent’s implementation details and ideas. Covering
ExAgent’s code interpreter, reasoner logic, and beliefs unifier.
• Chapter 6 - Evaluating ExAgent. We will evaluate ExAgent operational
performance and feature set by comparing it with JASON AOP in this
chapter.
• Chapter 7 - Conclusions and future work. This chapter contains the
dissertation conclusions, closing remarks and invitation to future research
and work.
5
CHAPTER
TWO
AGENT LANGUAGES, ARCHITECTURES, AND
FRAMEWORKS
In this chapter, we will define what the word agent means to different software
engineering groups. We then define the terms of intentional and mentalistic agents
and how those terms differ from the wider use of the term agent. We next examine
the different agent reasoning architectures. Finally, we conclude the chapter with
listing a set of popular AOP languages and frameworks.
2.1 What is an agent
Agent, in the broad definition, is someone or something acting on behalf of
someone else. Acting here means it interact and react in an environment to
perform a task that will benefit the entity this agent is acting in behalf. While
the broad definition of an agent is not difficult to grasp, its technical definition is
not that direct and precise. That is perhaps due to the wide usage of the term
’agent’ in everyday software engineering.
In the artificial intelligence (AI) field the term agent is defined as any ”software
program” that acts on user behalf [11]. While this definition still preserves
elements from the broad definition, it is not the only meaning of ”agent” in AI
groups, the other definition being: an agent is an entity or program that keeps
6
functioning continuously in an AI application without stopping and without user
interactions. This AI definition of an agent comes very close to Wooldridge and
Jennings AOP agent definition in their popular paper titled ”Intelligent agents:
Theory and practice” [12].
Another popular use of the term agent is among OOP software practitioners.
Their definition of what an agent is has a different and more technical meaning.
An agent in OOP describes the way a piece of software is built, organised and
implemented [13] . An agent-based approach is used when a piece of software is
built in a way that a) is decoupled, encapsulated and separated from the rest of
the program, b) is decentralised; in a sense that it can either run on the same
thread and process, or it can run in a separate thread, process or machine, and
c) It’s autonomous and self-organised; the piece of functionality provided by the
agent can be added to a system as a plugin. Adding it won’t affect the running
system architecture in any measurable way.
Since the term ”agent” has a different meaning to different software engineering
groups, it is important to specify the meaning of the term ”agent” used in this
context. In this dissertation, the term agent is derived from Shoham [1] definition
of an agent. That being, an agent is an entity that state is viewed as consisting of a
set of mental components, such as beliefs, capabilities, choices, and commitments.
In this definition, any piece of software and hardware can be viewed as an agent
if it utilises these mental concepts to perform its tasks.
Programming languages that structure their programs as a set of communi-
cating agents are called Agent-Oriented Languages (AOP). These programming
languages paradigms can be considered a specialisation of the object-oriented
programming (OOP) language paradigm. While OOP languages have a very
generic and weakly-defined state, AOP languages state follow a set of strong, well-
defined rules and constraints. Their state consists of a set of mentalistic concepts;
namely, beliefs, capabilities, and decisions.
Solving a problem in AOP systems requires agents to communicate and
interact with each other. The communication occurring between these agents
takes the form of informing, requesting, offering, accepting, rejecting, competing,
7
and assisting. The set of the interaction between agents can either be defined
at build time (when designing the agents system) or it can be left for agents
to figure it out at runtime. In the latter case, agents will be able to find the
capabilities, responsibilities, and configurations of other agents at runtime. It is
important to note that while agents depend on each other to achieve common
goals, each individual agent operates in an autonomous way; it requires minimal
configurations and has no operational dependencies on other agents in the system.
A system containing multiple agents that are geared toward a common wide goal
is known as a multi-agent system (MAS).
2.2 Intentional Agents
Wooldridge and Jennings [12] provided a two-tier definition of agency. A lower
tier, or weak notion, that defined the most general and easy-to-meet requirements
of an agent. Such as; autonomy, social ability, reactivity, and pro-activity. And a
higher tier, or strong notions that contained characteristics that are more specific
and specialised; like benevolence, rationality, mobility, learning and intentionality.
The lower tier definition is used not only in AOP but also in other fields,
like OOP, where any autonomous piece of software is referred to being an agent.
The higher tier, on the other hand, is only used when referring to ”agent” in the
narrower AOP use of the term.
Perhaps one of the most important traits and constraints of an agent, in
the strong notion sense, is the intentionality. Agent-oriented systems, based on
this notion, are intentional systems. Intentional systems are systems that follow
the intentional stance which is aptly defined by DC. Dannett in his book, The
Intentional Stance [14], as:
a perspective which invokes the family of mentalistic concepts, such as
belief, desire, knowledge, fear, pain, expectation, intention
DC. Dannet’s definition of intentional systems: systems that reason about
their own goals and objectives by using mentalistic concepts, is what shaped the
8
mentalistic movement in AOP. In this definition, agents are considered intentional
systems since they invoke mentalistic concepts when reasoning about their goals,
when deciding their next actions, when interacting with their environments, and
when communicating with other agents.
The topic of the agent mental state in the mentalistic AOP movement has been
a major research area. Multiple mentalistic models have been introduced over
the years. Bratman et al. [15], proposed a plans and resource-bounded practical
reasoning architecture for intentional mental-agents. Their solution addressed two
problems: a) that a rational agent architecture must allow for means-end reasoning
and b) that this architecture must address the problem of resource boundedness.
Soham [1], on the other hand, proposed the introduction of two mental
categories, belief and decision (or choice), which was lately formalised as obligation
or commitment. These two categories were chosen to simplify the creation of
mental agents. In this mental model, beliefs are the facts that an agent knows to
be true about itself and its environment. While obligations are actions that the
agent has decided to undertake, and hence, it’s obligated to do.
Another popular view on the intentional mental state was formalised by Rao
and Georgeff [16] [17]. They formalised earlier concepts based on a previous
work from Bratman et al. [15]. They view an intentional, rational agent as
an entity which has three mental attitudes beliefs, desire, and intention. These
three form the popular beliefs, desire, and intention (BDI) agent mental stance.
By formalising the BDI architecture they were able to build tools that can be used
to verify the correctness of multi-agent systems.
In BDI agent architecture, an agent’s beliefs stand for the current state of
its environment. Desire is the agent ideal future state of the environment. And
intentions are a subset of its desires that the agent committed to achieving. BDI
is considered the defacto standard for mental agent architectures.
9
2.3 Agent architectures
Agents are highly dynamic software entities. They exist in dynamic environments
where they are able to sense and gather data about their surroundings and react
to changes in their environments. In most of the circumstances, multiple agents
are organised in systems where they should be able to interact with each other
to achieve a common goal. Since agents need to be able to decide what goals to
attempt to achieve and what obligations to select, they must be able to reason
about their environment and about their own beliefs. The reasoning abilities of
the agent are very important for it to be considered a rational, intentional and
intelligent system.
There have been many agent reasoning frameworks proposed that have roots in
classical AI plan construction and execution. Among these proposed frameworks
is procedural reasoning system (PRS) [18]. The reasoning described in PRS aims
to exhibit behaviours expected in rational systems. In PRS reasoning, a robot (or
an agent) is able to operate efficiently in a highly dynamic environment. When
considering a new action to take, a PRS reasoner takes into account not only the
current status of the environment but it also depends on its current beliefs and
on previous (partially adopted) intentions. By taking its current mental state
into account, the PRS reasoner is able to avoid falling into over constrained plans
of action and unreasonable expectations about the environment. PRS reasoners
make use of the beliefs, desires, and intentions described in the BDI mental model
state as inputs to decide the next action to execute.
In AOP systems that use the PRS reasoning model, the agent defines a set of
procedures or plans that describe what actions to take to satisfy specific goals. In
each PRS reasoning cycle, the agent’s interpreter (or runtime) inspects the agent’s
currently held beliefs (that are stored in a database-like storage) and plans to
decide which plans are applicable under the current agent state. The PRS reasoner
then selects one of these applicable plans for execution.
Another prominent reasoning architecture is the Teleo-Reactive (TR) archi-
tecture, which was proposed by Nilsson [19]. In this architecture, the program
10
execution produces electrical circuits; these circuits are used to control the
operations of the agent. A TR system is composed of a set of TR rules. Each
rule is composed of a condition, which takes into account the sensory inputs and
the status of the world, and an action, which affects the world hence changes the
agent model. When deciding on the next action to undertake, an interpreter scans
the rules, top to bottom, and selects the first rule which conditions are met. The
matched rule’s action is then selected for execution.
Among these two reasoning architectures, PRS reasoning architecture, es-
pecially when used with the BDI mental architecture, is more widely used in
practical AOP languages. Some of the popular AOP languages that use PRS are
AGENTSPEAK [20], AGENT 0[21], JASON [22] and ASTRA [23].
2.4 AOP languages
When Bordini et al. [2] conducted their research of the current state of AOP
programming, they listed a large number of languages and framework. Between
the writing of that paper and the writing of this dissertation, another group of
modern AOP languages has been introduced. For this dissertation, we scoped the
research on AGENT0, AGENTSPEAK(L), GOAL, JASON, AGENTFACTORY,
and ASTRA.
2.4.1 AGENT0
AGENT0 is a language introduced by Shoham’s [21] as a simple base-level AOP
language implementation. It was designed with simplicity in mind by simplifies the
mental state of the agent. It also had limited the communicative commands the
agents can send and receive. And had a minimal flow of control in the interpreter.
AGENT0 mental state consisted of beliefs and commitments. Other more
involved mental concepts, as desires, goals, intentions and plans, were intentionally
left out of the languages. The AGENT0 interpreter was responsible for a)
processing the incoming messages (from other agents or from the environment)
11
and updates the beliefs and commitments, and b) execute a single step from a
selected commitment.
2.4.2 AGENTSPEAK(L)
In an attempt to bridge the gap between BDI agents theory and practice,
AGENTSPEAK(L) [20] was introduced. In AGENTSPEAK(L) the behaviour
of the agent and his interactions with the environment are controlled by a set of
first-order logic statements written in the AGENTSPEAK(L) program. The agent
execution in AGENTSPEAK(L) is controlled by the agent’s beliefs, desires, and
intents. The agent beliefs in AGENTSPEAK(L), which is the mental model of
the agent and the environment, represents its state. Desires in AGENTSPEAK(L)
represent the change the agent wants to bring to the environment. And intentions
represent the adopted desires the agent selected for execution.
A very important concept introduced in AGENTSPEAK(L) is the way it
defined plans; A plan in AGENTSPEAK(L) consists of a head and a body. The
plan head is divided into a plan triggering event which specifies which events can
trigger the plan, and a triggering context, that defines the agent beliefs that should
hold in order for this plan to be selected. The plan body, on the other hand, is a
sequence of goals and actions that the agent will execute upon plan selection.
2.4.3 GOAL
GOAL language [24] is an AOP language that focuses on declarative goals.
GOAL takes a different approach on agent orientation; while other languages as
AGENT0 and AGENTSPEAK(L) defined agents in terms of beliefs, goals, plans
and capabilities; where goals are a set of actions or plans. GOAL defines its goals
in a declarative way; they describe the end goals which are the final state desired
for the environment.
By defining goals in a declarative way, GOAL language argues, that the
agent code written in GOAL programs gains new and more powerful abstraction
mechanisms.
12
Agent programs written in GOAL, which use declarative goals, are described
by the language creators as Goal-Oriented Agent (GOA) Programs. In GOAL
programmers point of view, GOA programs are more suitable for building
intelligent, deliberate and intentional MAS.
2.4.4 JASON
JASON [25] [22] [5] is an open source, Java-based, practical implementation
of an extended version of AGENTSPEAK(L). JASON is built upon the BDI
agent architecture and supports the creation of reactive agents. The inter-
agent communication in JASON is possible by leveraging speech-act based
communication methodologies.
JASON makes it is also possible to create network distributed MAS solutions
by choosing the distribution functionality of SACI or JADE (discussed in the next
subsection).
An important feature that JASON introduced is the ability to develop external
environments. What makes these environments attractive is that developers can
develop and create environments using Java. By defining a formal interface for
JASON environments, JASON made it possible for developers to create, share
and reuse environments built using the JASON environment interface. This made
it easy to interoperate between JASON agent code and general purpose Java
framework and libraries.
2.4.5 ASTRA
Astra [23] was created with the purpose of reducing the cognitive gap for
OOP developers that wish to learn an AOP language. Similar to JASON,
Astra is implemented with Java and works on the JVM. It draws some of it
implementations ideas from JASON AOP and other ideas from the simplicity and
practicality of the Java programming language.
What makes Astra an attractive language, is its pragmatic choice in language
syntax; to an OOP developer, Astra look familiar. The focus on the familiarity
13
with Java was drawn by a research [26] conducted to compare AOP paradigm and
OOP paradigm as an attempt to bridge the conceptual gap bet the two.
In addition to implementing most of the features of AGENTSPEAK and
JASON, Astra adds a set of important features, among those are: a) a strict
static type system, b) an extended set of control statements: such as if statements,
foreach loops and while statements, c) mutual exclusion (mutex) in plans
execution, and d) the introduction of message receiving plan rules as a first-class
language construct.
2.5 Agent Frameworks
Agent frameworks provide a set of functionalities that help building AOP systems,
such as agent distribution, communication, deployment, and coordination. These
features can be utilised by different AOP systems. These frameworks are generally
language agnostic; they can operate with different AOP languages, and in some
cases, they can even function with multiple agents built with different AOP
languages in the same MAS environment.
In this section, we will cover Jade, Jadex, Cougaar and eXAT.
2.5.1 Jade
Jade [6][27] is an open source, Java-based agent development framework. It pro-
vides the capabilities for distributing a MAS application over multiple computers
connected in a network. The communication of agents in a Jade distributed MAS
application is governed by the communication semantics of FIPA-ACL [28].
The FIPA communication standard defines the reference model for agent
communication and interoperability. It also defines the set of services that must
be present in a system to support a truly distributed MAS application.
The environment provided by Jade is dynamic; agents can appear and
disappear from this environment depending on the system current needs. When
distributed in Jade, agents, are wrapped in a Jade container which spawns a JVM
low-level container under the hood for each agent. An agent registers its name and
14
the service it provides. This information is exposed by the agent’s Jade container
and is available to other Jade agents. When an agent requires a specific service
to be handled, it searches for the agent that registered that service in the Jade
environment using well-defined search capabilities provided by Jade.
2.5.2 Jadex
Jadex [29] is a software framework for creating goal-oriented agents (similar to the
agents created with GOAL AOP language) that follow the BDI agent architecture.
It aims to facilitate the creation of agent-oriented software solutions using well-
established software languages and approaches. It does that by enabling the
creation of agents using well-proven technologies like Java and XML.
One of the design ideas behind Jadex is to provide a bridge between reasoner
systems that focus on solving the problem of creating a rational and intentional
singular agent, and communication platforms, like FIPA-ACL that focus on
implementing communication standards that govern inter-agent interactions and
communications.
2.5.3 Cougaar
Cougaar [30] is a Java-based, open source, agent-oriented system developed and
funded by the American Defense Advanced Research Projects Agency (DARPA).
DARPA used Cougaar for military grade logistics applications. Cougaar has
scalability, reliability, and survivability needs in mind.
Out of the box, Cougaar supports the creation of agent application solutions
that are massively scalable. The problems that can be solved with Cougaar are too
difficult to be solved on one computer. For that reason, Cougaar comes equipped
with the capabilities to distribute the agent solution to multiple computers. While
scaling the solution, Cougaar ensures that the distributed system is resilient, fault-
tolerant and secure.
The inter-agent communication capabilities in Cougaar are provided by
implementing a classical blackboard system to manage the communication. This
15
system supports standard publisher/subscriber semantics were agents can register
to specific topics; an agent receives all the messages sent to that topic, and can,
in turn, send messages to that topic.
Similar to Jade and Jadex, Cougaar is designed to strongly blend with Java
patterns and technologies. At its heart, Cougar runtime system is designed with
an interface that looks like a Java bean. Furthermore, Cougaar supports the
development and publishing of Java servlets which allows Cougaar components to
expose part of the agent system functionality as a standard HTTP Java servlet.
2.5.4 eXAT
Among the list of agent frameworks we explored, eXAT [3] is the only non-Java-
based AOP framework. eXAT is an experimental agent platform built using
Erlang. It provides multiple AOP related functionalities, such as agent intelligence
logic, agent behaviour, and agent communication.
eXAT designers argue that since Erlang is a functional programming language
with functional pattern matching and multiple function heads, it makes it a better
fit to be used for Finite State Machine (FSM) agent reasoning architectures.
By choosing Erlang as the base environment, eXAT leveraged many high-
level features provided by Erlang; such as the zero-cost distribution of agents,
inter-agent asynchronous communication, fault tolerance in case of failures, and
debugging and inspection capabilities.
The benefits of choosing Erlang as the base environment to develop AOP
languages will be discussed in details in the next chapter.
16
CHAPTER
THREE
ERLANG AS A VIABLE AGENT ENVIRONMENT
Erlang [31] is a functional programming language built to address the parallel
processing nature of the telecommunication switches. Erlang uses the Actor model
of computation where each actor is implemented as an Erlang process. In Erlang,
a single application can involve the communication between several hundred of
processes.
In this chapter, we start by explaining the actor model of computation. We
then present an introduction of Erlang and Elixir explaining some of the features
that make them suitable for agent-oriented development.
Next, we elaborate on the differences and similarities between the actor and
the agent models of computation. We use Erlang actor model as the comparison
base.
After that, we list the reasons that make the actor model of computation better
suited than OOP to implement agent-oriented languages.
Finally, we reflect on why we choose Erlang, Elixir, and the actor model to
build ExAgent, and mention some of the previous similar attempts.
3.1 The actor model
In 1973, Carl Hewitt [32] introduced the Actor model of computation. This model
was inspired by the study of physics, general relativity, and quantum theory. It
17
is influenced by multiple programming languages, among these are LISP, Simula,
and Smalltalk. At its heart, the actor model aims to solve the problem of parallel
computation by modeling a problem and splitting it to multiple small, single
purpose, self-contained processes. Theses processes, called actors, contain the
state, the memory storage, the messaging receiving and sending mechanisms, and
the processing capabilities to solve a particular problem.
In contrast with the OOP paradigm, where the main entity is an object, in the
actor model of computation, the actor is the only entity in the system. Programs
are composed of a set of communicating actors that are geared toward solving a
common goal.
A single task in an application in the actor model entails the communication
between several hundred, or even thousands of actors. Each Actor gets its inputs
by the mean of message sending. In order to send a message, an actor needs to
know the address of the actor (sometimes known as its ”mailing address”) it wants
to send the message to. For that reason, actors can only send messages to actors
they have the address for.
When an actor is triggered, by the mean of receiving a message, it can process
this message by changing its own internal memory or state, or it can trigger other
actors by preparing a message and sending it to these actors.
All the communications between actors are asynchronous. When an actor
sends a message to anther actor, it does not wait for the message to be delivered
and processed by the receiving actor. If the sending actor requires a response
from another actor, it will receive this response by a new message sent to it
asynchronously.
Two of the most important features of actors in the actor model are data
encapsulation and problem abstraction. By controlling the access to the internal
memory (or state) of an actor to only itself, data access and encapsulation is
enforced. Similarly, by enforcing asynchronous message passing communication
semantics between actors as the only way of interaction, conceptualising an
application involves the thinking about each actor responsibilities and domains.
This way of designing systems will result in highly abstracted solutions.
18
Since actors encapsulate the data and logic to perform a task, they provide a
good entity to represent a domain concept or a system resource. For example, in
an actor-oriented program, the access to a physical printer can be managed by an
actor or a group of actors. These printer actors are responsible for processing a
printer task. They control the capabilities and limitations of the printer by holding
the printer state in their internal state. When an external actor desires to print, it
sends a print message command asynchronously to the printer actor. The printer
actor, in turn, receives the message and executes the print operation. Optionally,
the printer actor can communicate back to the originator actor informing it of the
successful print operation. Similarly, an actor can be used to represent a domain
concept. We can, for example, have a banking actor, that is able to process
messages to alter the state of a bank account.
The data and logic encapsulation of actors, the asynchronous communication
mechanisms between actors, and the structure of actors applications as a network
of communicating actors make it a very good software engineering paradigm to
build highly scalable programs. These programs, due to the nature of the actor
model, can be executed on parallel processor cores and even distributed on multiple
computers. In the recent years, computers evolution has shifted from having faster
CPU clocks, to having multiple cores packaged in the same processor. Due to this
shift, actor-enabled programming languages, libraries, and frameworks have gained
considerable attraction and popularity.
It has been noted by several studies [33] [34] [35] [36] [7] [37] [38] that the
characterisitcs and capabilities provided by the actor model of computation makes
it more suitable than the OOP to be used as a the software paradigm to implement
AOP languages.
3.2 Erlang
Erlang [31] [39] is a programming language created at the Ericsson Computer
Sciences Laboratory by Joe Armstrong, Robert Virding, and Mike Williams in
1986. The name Erlang, as stated by Joe Armstrong, was selected carefully, as
19
it can stand for the Danish mathematician Agner Krarup Erlang (1878 - 1929)
whose name is associated with the telecoms industry, and it can also stand for an
acronym of Ericsson Languages.
At its heart, Erlang is a functional programming language that supports the
actor model of computation. Erlang was created to be used in telecom switching
which requires highly concurrent processing. A single switch must be able to
handle hundreds of thousands of simultaneous transactions at the same time.
An actor, in the Erlang’s actor model, is called a process. This process
represents both the unit of distribution and of concurrency; Erlang programs are
organised as a set of interacting processes. These processes can exist on the same
physical computer, or they can exist on different computers. In the view of the
developer, and even the Erlang runtime, the interaction between two processes is
the same whether they are on the same machine or on machines accessible through
a network. In addition, if a section of a program is to be run asynchronously in
Erlang, a process is created and used for that task making the process the unit of
concurrency.
Erlang provides its own processing scheduler. Processes in Erlang are not
supported by operating system (POSIX) threads. They, in fact, are handled by
the Erlang scheduler which is able to spawn, stop, pause and provide concurrency
for them. A single process in Erlang is estimated to be around 1 kilobyte of
memory. That small footprint makes it comparable to object instances in OOP
programming languages. Processes interact with each other by the mean of
message sending. Each process has its own mailbox that stores all the messages it
receives. In Erlang, message sending and receiving are asynchronous operations;
both the sender and receiver are not expected to block the process execution while
sending or receiving messages.
Erlang has built-in fault tolerance. It does that by introducing a supervision
operation on processes. When an Erlang application is started it starts its
own supervision tree. Processes spawned in the application are added to this
supervision tree to be monitored. When a process crashes or terminates forcefully,
the supervision tree receives a process termination notification. It analyses the root
20
cause of termination and restarts the process if needed. By providing a supervision
tree, Erlang is able to maintain application running without failing for years. That
helps Erlang applications reach a nine-nine availability; systems built with Erlang
are available and fault free 99.9999999% of their running time.
On the language syntax level, Erlang supports the creation of pattern matched
functions; a function in Erlang consists of a head, a guard, and a body. The head
defines the name of the function. The guard specifies a set of conditions on the
function parameters that have to hold in order for the function to be executed.
And the function body represents the set of instruction to be executed when
the function is selected. The Erlang function evaluation mechanism is based on
pattern matching; calls to functions are matched sequentially until a function with
successful guard clause is found.
In addition to these features, Erlang ships with a built-in real-time distributed
database system, Mnesia [40] that is built on top of Erlang Term Storage (ETS).
Depending on how it’s configured, Mnesia can either store data on disk or
in memory. Tables and records in Mnesia are automatically replicated to all
the Erlang nodes connected in a particular application. What makes Mnesia
interesting is that it’s also built using Erlang, that means it gains all the benefits
of Erlang such as fault tolerance, distribution, and scalability.
Finally, Erlang comes with a good set of analysis and debugging tools. Among
these is Monitor which allows developers to visually set watches and breakpoints
that help in debugging the application. And Observer; a GUI tool to observe
Erlang applications. Observer exposes the application system information, its
supervision trees, the messages passed between processes, and Mnesia table
transactions, reads, and writes operations.
3.3 Elixir
Elixir [10] is a modern functional programming language that supports the actor
model of computation. It’s built on top of the Erlang VM (Bogdan/Bjrn’s Erlang
Abstract Machine BEAM) and exposes all the powerful features of Erlang.
21
What makes Elixir shine is its powerful macro capability; Elixir uses a special
type of macros called procedural macros. These macros are executed as a pre-
compilation step and have the power to alter the Abstract Syntax Tree (AST) of
the current program before sending it to the compiler. Elixir macros are hygienic,
meaning they don’t alter the code around the location where macros are used.
This is important since macros can cause unexpected problems and in languages
like C/C++ where macros are unhygienic, they are associated with errors and
crashes.
The procedural macros in Elixir facilitate the creation of Domain Specific
Languages (DSLs). A developer can define a set of concepts and constructs that
make sense for a specific domain and introduce new keywords to the language
using macros. Using these domain keywords increase the code readability and
improve the abstraction levels in the program. Macros are used heavily in Elixir
programming, in fact, most of Elixir core functionality, such as defining modules,
methods, behaviours, and events are implemented as macros that extend Elixir
language itself.
Elixir ecosystem is famous for its practical applications and projects. Two of
those popular projects are Phoenix web framework and the Nerves project.
Phoenix Framework [41] is a production ready, scalable web framework. It
leverages the powerful distribution, fault tolerance and scalability capabilities
provided by the Erlang BEAM VM. Phoenix is used by many companies in
production and has been described as an easy to use and productive web
framework.
Nerves [42], on the other hand, is a project that allows the deployment of
Elixir applications on embedded devices. Although Nerves is a young project,
it already allows Elixir to be deployed to Raspberry Pi, Intel Galileo, and other
similar embedded devices. Applications built with Nerves are small in packaging
size with the footprint of 12 MB which makes them suitable to be deployed to
these embedded systems.
22
Elixir is a promising language. It has already been adopted by major companies
and provides a modern and more approachable entry point to the battle-tested
Erlang VM features and capabilities.
3.4 A comparison between actors and agents
There are many similarities and differences between the actor model of compu-
tation and agent-oriented programming. In the following sections, we provide a
comparison between the two.
3.4.1 Actors are passive, agents are active
Actors are considered as passive entities. In order for an actor to start operating,
it needs to receive a message. Upon the message receive, the actor can either
decide to handle the message itself or forward it to another actor.
Agents, on the other hand, are active entities. They exist in an environment
where they make observations about it. If the environment changes or the state
of some other agents is changed, agents can self-activate to react to the changes
in the system.
3.4.2 Actors and Agents affect their systems differently
Introducing an actor in a system does not affect the system at all. Since actors
are passive entities, when we introduce them into the system, the system is not
affected. These added actors, as they are passive, will need to receive a message
in order for them to start operating. For that reason, it is considered safe to
introduce new actors to an already running system.
In comparison, introducing agents into a system might change the system
organisation and chemistry. Agents, due to their active nature, can affect the
system they are introduced to. When a new agent is introduced to a stable system.
It changes the interactions and communication patterns; like introducing a new
path for solving a particular task, or even introducing new features and capabilities
23
in the system that were not planned when the system was first built. MAS systems
are built with dynamism and adaptability in mind. When agent systems notice a
new agent being added, they can adapt and reorganise to factor the added agent
capabilities and responsibilities.
3.4.3 Actors and Agents use messaging as a mean of
communication
Actors and agents are similar in that both of them use messages as the main form
of communication. In the actor model, processing a message by an actor might
involve creating one or more new actors, or updating the local state of that actor.
Similarly, in AOP, when an agent receives an event, it can either spawn new agents
to handle that message or update its own mental states (such as updating its belief
base).
However, there is a major difference in message sending semantics between
actors and agents. In the actor model, the sender can only send a message to an
actor if it knows its address (or Mailing box). If an actor does not possess that
info, communication cannot be conducted. Agents, however, are more flexible.
While it is still possible to send a message to an agent by specifying its name or
address. It is also possible to indirectly interact with an agent by modifying an
entity that the other agent is observing. In that way, the sending agent does not
need to know the name, the address, or even the existence of the receiving agent.
3.4.4 The actor state is comparable to the agent state
The state in the actor model is stored internally by the actor. Any information
required by the actor to complete a task must be stored in the actor internal
memory. For an actor to update its state, it has to receive a message that causes
it to alter the state. It is not possible for an actor to access information stored in
another actor. If an actor required some external information to complete its task
it can request it by sending a message with a query request.
24
In the agent model the agent information is kept as a mental state. Similar to
the actor model, this information is internal to the agent and cannot be accessed
by external agents. This agent state is mostly managed by the AOP runtime
environment. In some AOP system the mental state, in form of beliefs, is located
in a shared memory that is maintained by the runtime. This shared memory
represents the global belief base that all agents will query to get their respective
beliefs.
3.5 Using Erlang and its actor-model for agent
development
Java and the JVM are the most popular programming language and virtual
machine used to develop AOP languages and frameworks. Among the most
popular are Jade, Jadex, Jason, AF-APL and Astra. While Java is perfect
for general purpose programming. It might be less so for agent programming.
Developing agents require a special set of functionalities, utilities, and tools to
support highly distributed production ready AOP systems. In languages like Java,
these tools and capabilities have to be created since Java does not provide them
in the standard library. Erlang and Elixir, on the other hand, come with these
capabilities bundled in the language and the VM.
The following sections provide a list of observations that led us into choosing
Erlang, Elixir, and the actor model of computation to build ExAgent AOP
language. In most of the subsections, we compare how the technological stack
chosen for ExAgent is more convenient and suitable to be used than Java and the
JVM.
3.5.1 The actor model is suitable to implement an AOP
runtime
In the classical multithreading computation model, threads, which are a sequence
of instructions that can be executed by the operating system, are used to
25
provide concurrency and parallelism. When a process needs to execute some task
concurrently, it creates a new thread with that task. This newly created thread
has access to the internal memory and resources (such as file handles) of its parent
process. It can communicate with the parent process by changing the shared state
and by observing changes to it.
When implementing agents in AOP languages such as JASON and Jade which
are implemented in Java, threads are used to support having multiple agents in
the same process. For each agent, we normally have at least one thread.
Using threads for agent runtime poses two problems. First, All the threads
launched from a process will have access to that process shared memory. This
can cause race conditions, invalid memory access problems, and deadlocks which
happen when many threads try to access the same shared memory segment. The
problem becomes more apparent when the number of threads increases. When
dealing with programs with hundreds, or even thousands of threads, the developer
must apply caution and must be aware of what memory or resources each thread
is accessing. This conceptual overhead might prove to be a problem when scaling
an agent system to thousands of agents in production.
Second, the number of threads a machine can create is limited. While a system
can create millions of instances and objects, it can only create threads in the
order of thousands. Threads, while cheaper than processes, are still considered as
expensive resources.
Actors target both of the drawbacks of using threads. Actors adopt the share-
nothing architecture; each actor has its own internal memory that cannot be
accessed or altered by other actors. By adopting the share-nothing architecture,
that actor-model eliminates the race conditions, deadlocks, and other memory
problems that happen when sharing memory between threads.
Additionally, actors are considered as cheap entities. It is normal for a problem
in an actor-model program to involve hundred of thousands or even millions of
actors. Actors in a sense, are comparable to object or instances in the OOP
paradigm. Actors-capable languages, such as Erlang, provide their own process
26
scheduler. They typically don’t create a thread for each actor, instead, they will
manage the concurrency of the actors in their own process schedulers.
These two benefits of actors, share nothing architecture and low actor resource
footprint, are what make actors more suitable to implement agents runtime than
threads.
3.5.2 Actor messaging is suitable for AOP agent commu-
nication
When implementing an agent-oriented system in an OOP language like Java,
the infrastructure for agents communication has to be built. The capabilities
to send messages, wait for messages, queuing messages, and receiving messages
has to be developed. This infrastructure can be complicated since agent
communications can happen between agents distributed in a network. Network
agents communication is more involved than local communication since it requires
TCP socket connections or remote method innovations (RMI).
Since actors, in the actor model, communicate by sending messages, the
infrastructure for communication is already natively implemented in the actor-
powered languages. Erlang, as an example, provides native capabilities to send,
receive, and queue messages.
By choosing an actor-powered language, the implementer of an AOP language
can leverage the already built and tested message sending infrastructure to power
agents communications.
3.5.3 Erlang provide capabilities for agents distribution
The distribution of agents is a desired feature in AOP systems. When a problem
gets large enough, the vertical scalability of the system (i.e adding more processing
to the same computer) becomes unachievable. In order to scale in these situations,
a horizontal scalability for the system (distributing agents to multiple computers)
is needed.
27
Distributing an agent solution on multiple machines has its intricacies. In Java-
based agent languages, like Jason for example, there is a need to use a third party
agent distribution framework, like Jade or SACII. These frameworks provide the
tools and infrastructure capabilities needed to deploy and connect agents between
different machines.
Erlang, and other actor-oriented languages and systems, already provide the
infrastructure needed for horizontal scaling and network distribution. Erlang, for
instance, provides the runtime components and capabilities required to distribute
actors among different machines. The interactions between Erlang actors are
opaque; when two actors communicate, they don’t need to worry if they are on
the same machine, or if they are on machines distributed in a network. This
battle-tested distribution infrastructure provided by Erlang saves countless hours
of development and maintenance when compared to other solutions.
3.5.4 Erlang help building fault tolerant systems
Once we start distributing agents across several machines, maintaining these
agents up and operational becomes a complicated task. What to do when an
agent crashes and is terminated? How do we decide when to restart an agent and
when not to? How to monitor and report that status of the distributed agents?
These questions and others have already been addressed by languages that are
designed for highly distributed processing. Erlang, for example, comes bundled
with the Open Telecom Platform (OTP). This platform contains a set of libraries
and patterns that help maintaining an application running without failures for
years. One of the most popular patterns is the supervision tree. The supervision
tree makes sure that processes are always operational. In case a process terminates,
the supervisor will start it up and report the crash to the health checking and
monitoring mechanisms.
These powerful libraries bundled with Erlang, such as the OTP, have been
developed after years of trial and error. AOP languages built on top of Erlang
will be able to leverage these production-ready, battle-tested features.
28
3.5.5 Erlang provide useful tools for AOP languages
In addition to the more considerable reasons listed before, the Erlang ecosystem
provides a set of tools and utilities that benefit AOP languages. Such as Observer,
which is a tool to monitor actors, messages, and events. Observer provides a user
interface that can be helpful in tracing messages sent between actors which helps in
discovering issues in large systems. In applications that require highly performant
solutions, Observer can be used to pinpoint the actors that are acting as the system
bottleneck.
Mnesia, the database that comes packaged with Erlang, can be used as a
belief base storage for the agents. Additionally, since Mnesia is a distributed soft
real-time database, it can be used to store information about the environment
in distributed MAS programs. When an agent updates the environment, Mnesia
will be updated accordingly. These updates will be automatically propagated to
all the agent nodes using Mnesia in the network. Having a distributed database
like Mnesia can prove to be a useful component in MAS projects where data
propagation and synchronisation is required.
Erlang and the Erlang VM are packed with practical tools, utilities, libraries,
and patterns. We believe that these tools will help develop AOP languages that
are scalable, performant and production ready.
3.5.6 Elixir provide diverse targets and platform to use
AOP
As we discussed in a previous section 3.3, one of Elixir attractive features is being
able to deploy an Elixir application to different targets. With the use of Phoenix
Framework, an agent, or a group of agents, can be deployed to a web server.
These agents will then be able to expose an HTTP/HTTPs or even UDP and
TCP interface that other agents can interact with. This will allow agents to
leverage the standard communication protocols and enable a whole range of new
interaction between them.
29
Nerves project, on the other hand, allows the developer to deploy agents to
embedded devices. A Raspberry Pi can be used to host an agent. The agent will
then be able to utilise the sensing abilities of the embedded device to sense and
read the parameters of the environment. Deploying agent software on modern
embedded architectures allows an AOP language to be used in a new range of
applications and setups. This is even more important nowadays with the increased
popularity of internet of things (IOT) devices and appliances.
3.5.7 Elixir provides a quick way to prototype a language
The powerful Elixir procedural macros provide a quick way of introducing domain
terms to Elixir programs. These macros allow the change of the AST before
the final code get compiled. By changing the AST, a developer could drastically
change and adapt the source code being compiled which increases the abstraction
and readability of the written code.
In this dissertation, we used Elixir’s procedural macros to prototype ExAgent
AOP language which allowed us to experiment different solutions and test our
assumptions in a timely manner. Doing that helped us quickly create a set of
terms, keywords, and concepts that we used in ExAgent without losing the ability
to inter-operate with Elixir and Erlang libraries and frameworks.
3.6 Related studies
Actor-enabled language, such as Erlang and Scala, have inspired and shaped the
ideation and creation of many agent-oriented platforms. In this section, we will
cover some of the most prominent studies in this area.
In a paper titled ”An Erlang-based framework for controlling robots” [8]
the authors introduced a framework built with Erlang to control robots. This
framework was divided into six layers, among them; at the bottom, there is the
driver layer, which is the layer that interacts with the underlying hardware. In the
middle, are the logic and control layers. And on top, the Intelligence layer that
introduces deliberate thinking to the robot. The authors state that by dividing the
30
framework to layers, they increased the framework adaptability, customisability,
and reusability. In addition, when customising the framework in their proposed
solution, the developer will quickly know which layers he has to alter.
Mitrovic et al. [38] researched the topic of implementing AOP languages in
Scala. AgScala was introduced in the paper as an in-progress AOP language.
Their language was built to extend Scala to enable the development of both
reactive and BDI agents. They argued that Scala would be a better fit for agent
development when compared with other JVM languages like Java. Among the
attributed advantages of using Scala for developing an AOP language was; a) Scala
offers the actor concurrency model of computation and b) Scala has a powerful
metaprogramming capabilities that allow easy creation of DSLs. Their reasoning
behind using Scala is what further encouraged us to experiment with Erlang and
Elixir in this dissertation.
Varela et al. [43] provided a set of guidelines to be used when developing
agents on Erlang. The authors used Erlang for a year to understand what makes
it suitable for agent development. They found that the messaging capabilities,
actor-model concurrency, and fault tolerance to be very effective features when
developing a MAS. They also found that Erlang’s roots in logical programming
and its pattern matching functionality help in implementing agent reasoner and
deliberation systems. In addition, Erlang’s transparent distribution was also found
to be a very desirable feature especially when scaling the MAS is required.
ERESYE [44] is a tool written in Erlang that enables the realisation of
intelligent agents. With ERESYE a developer can write prediction rules as normal
Erlang functions. Their research describes how general purpose languages, like
C++ and Java, need third party expert system to provide rules parsing and
reasoning. The reason being, assertive programming languages are not suitable
for writing rule solving engines. While, on the other hand, expert systems and
languages, like JESS and CLIPS are not suitable for general purpose programming.
The research then shows that Erlang contains language features that make it
suitable for both developing general purpose programs and rule solving engines.
Among these features;
31
• Erlang atoms and tuples are suited to represent beliefs.
• Function clauses and guards in Erlang can be used to represent rules in a
rules system.
• Pattern matching facilities in Erlang are suitable for representing rule-
handling algorithms.
Since Erlang can be used to build rules solving engines and also for general
purpose programming, Erlang decreases the concepts the developers has to learn
and comprehend in order to use ERESYE. That eventually improves the developer
performance and removes obstacles that are introduced when using third party
expert systems.
Erlang Experimental Agent Tool (eXAT) [45] [3] is a project related to
ERESYE. eXAT is an experimental agent platform written in Erlang. It provides
an all-in-one solution to design, develop, and deploy intelligent agents. eXAT
provides the communication semantics between agents based on the FIPA agent
communication language. It also provides a runtime environment to execute agent
behaviours based on an object-oriented finite-state machine. eXAT utilises many
of the high-level features provided by Erlang and the OTP. Among these are the
Erlang communication mechanism, zero-cost transparent distribution, and fault
tolerance obtained with supervision trees.
Finally, eJason [35] is a prototype implementation of the JASON language that
works on Erlang. eJason, at its core, is an interpreter that translate JASON code
to work on a runtime developed using Erlang. eJason, similar to eXAT, utilises
features from the Erlang ecosystem such as Erlang distribution, communication
semantics, and the supervision tree.
32
CHAPTER
FOUR
EXAGENT AOP LANGUAGE
In the previous chapter, we reviewed some of the most powerful features that
come bundled in Erlang language and the BEAM VM. We believe that the Erlang
ecosystem is suitable to implement an AOP language. For that reason, we choose
Erlang and Elixir as the languages to build ExAgent. In this chapter, we formally
state the rationale behind creating ExAgent, the objectives, and goals planned
for ExAgent in this dissertation. We also cover the features and capabilities that
ExAgent language provides.
4.1 Why ExAgent
When reviewing the current landscape of AOP languages, framework, and libraries
the reviewer will notice that most of these languages, frameworks, and libraries
are built using Java and the JVM. While Java and the JVM can be used to solve
a wide range and variety of problems. They might not be the best tools to use to
implement an AOP language.
Chapter three (3.1) introduced the actor model of computation as a more
suitable programming paradigm to implement AOP languages. It provided
a comparison (3.4) between the actor-model and the AOP paradigms. The
comparison showed that these two programming paradigms share a good group
of common traits and characteristics. The resemblance makes the actor-model
33
more suitable than the OOP to implement AOP languages since the conceptual
gap between the two is smaller. This observation was also shared by several other
studies and research [33] [34] [35] [36] [7] [37] [38].
Based on those observations, we decided that ExAgent was going to be built
with a programming language that provides the actor-model of computation. After
researching the different actor-model powered languages, we decided that Erlang
provides powerful features beyond the actor-model that would help in this task.
Those features, listed in the previous chapter (3.5), would allow ExAgent to be
performant, distributed, fault tolerant, and production ready.
We proceeded at studying previous attempts on using Erlang and the BEAM
VM to build an AOP language or framework. Among the projects reviewed in this
dissertation, we chose to inspect eXAT and eJason as they are the closest projects
to our use case. Following are some observations we made on these projects.
First, while eXAT provided a platform for deploying and distributing agents,
it did not provide any concepts of an environment. The authors did not plan to
support standard environments like EIS [46]. Similarly, eJason did not provide a
way for using standard environments. When thinking about ExAgent, we decided
to support EIS as it will allow the communication with agents built with different
languages.
Second, both of the projects did not receive any substantial development in
the last couple of years; with eXAT not receiving any update on their GitHub
repository [47] in 5 years and eJason not receiving commits in 2 years [48]. This
raises the question whether the authors are planning to continue supporting and
growing the platforms, or if they decided to abandon the projects and move on to
a different research topic. We assumed that based on the inactivity period, the
projects are in fact abandoned and won’t receive any new substantial development
in the future.
Third, eXAT and eJason did not provide any practical usage examples. In
our research, we were not able to find examples and tutorials that describe how
to use them in practical areas. The only examples seen are in their GitHub
repositories. One of the main goals of ExAgent is to be usable in practical
34
areas. For that reason, we choose Elixir as the BEAM compatible programming
language. Applications written in Elixir, thanks to Phoenix framework and the
Nerves project, are deployable to servers and to embedded devices which will allow
ExAgent programs to act as web servers and as embeddable devices applications.
The practical aspects of Elixir, that are adopted in ExAgent, will allow agent-
oriented programmers to target devices and use cases that are not possible with
other AOP languages.
Lastly, eJason provides a literal translation of JASON AOP language. The
Jason syntax is based on AGENTSPEAK which poses a learning barrier for Erlang
and Elixir developers. That could result in poor adoption and lack of community
contribution to the project.
For ExAgent, we decided to design and engineer the language syntax and
semantics to look as close as possible to Elixir and Erlang. Since we are aiming for
ExAgent to be embedded in Elixir/Erlang applications, we want the syntactical
gap between both platforms to be minimal. From an Erlang/Elixir developer
perspective, ExAgent should look and feel familiar. Those developers should be
able to focus on learning how to build robust agents and focus on solving their
programming problems and not at learning the syntax of a new language.
4.2 ExAgent’s agent model
4.2.1 AGENTSPEAK archetype
When we reviewed the agent mental architectures for intentional rational agents
(2.2), we mentioned two popular approaches; Soham’s [1] beliefs and decisions
approach and Rao-Georgeff’s [16] [17] belief-desire-intention (BDI).
The BDI mental architecture is by far the most popular in modern AOP
languages. It is used in JASON, Astra, Agent-Speak, and many other AOP
languages and frameworks. Due to its popularity and flexibility, we decided to
use the BDI mental architecture for ExAgent.
35
In the agent introduction chapter, we reviewed two different reasoning
architecture (2.3), Teleo-Reactive (TR) architecture [19] and procedural reasoning
system PRS [18]. We expanded more on PRS since it’s used more widely
among AOP languages and frameworks. What makes PRS interesting, is that
its reasoning cycle takes into account the state of the agent environment (which
include the entities the agent can interact with and sense), the agent’s current
beliefs and observations, and the previously partially adopted intentions. By
taking these three factors into consideration, PRS reasoners are able to overcome
most of the common reasoners problems (like unsatisfiable constraints). The wide
adoption of PRS in agent languages and its advantages over other reasoner systems
made us decided to use it as the reasoner model in ExAgent.
Because of the choices of using BDI for the ExAgent mental architecture, and
PRS for its reasoning architecture, ExAgent can be in fact considered an archetype
AGENTSPEAK implementation.
4.2.2 Deviation from AGENTSPEAK
ExAgent implements many features and constructs that are derived from AGENTS-
PEAK language. Among these are: a) the predicate syntax for adding, removing
and querying beliefs, b) the predicate syntax for achieving goals and other
instructions that are covered in 4.5.3 section, and c) the syntax for defining plan
rules which is described in 4.5.4 section.
The operational semantics of ExAgent is also heavily inspired from AGENTS-
PEAK. As with AGENTSPEAK operational semantics, ExAgent’s reasoning cycle
starts with processing the events and messages received by the agent. After
selecting an event, the reasoner tries to find a plan rule that matches that event.
When a plan rule is found, an intent is created or updated with that plan. In the
last step of the reasoner cycle, an intent is selected to be executed. Section 5.5
explain the ExAgent’s reasoning cycle in depth.
ExAgent introduces a number of changes to the AGENTSPEAK agent’s defini-
tion syntax and operational semantics. These are the agent’s initialization section,
36
first-class message handlers, first-class recovery handlers, and the introduction of
roles that describe agents responsibilities.
First, ExAgent allows multiple agents to be defined in the same file, for
that reason, ExAgent introduces an agent initialization section. This section
includes any beliefs or goals that the agent wants to execute as part of its
initialization. In OOP terms, this section is comparable to the constructor function
that defines the object initial state. Any instruction that is part of the initialization
section, whether it is an added or removed belief or an achieve goal request, when
executed will produce an event that can trigger plan rules selection in the reasoning
cycle. Section 4.5.1 covers the creation of an agent and the agent initialization
keyword in more depth.
A second design decision took in ExAgent was to make the message handling
a first-class plan rule in the language. This decision was heavily inspired by
Astra Langue [23], where messages plan rules are defined with a special syntax.
We believe that agents communication is one of the major interactions in MAS
programs and having its own special treatment in the language will make it easier
to reason about the overall agent behaviour and communication patterns. Section
4.5.5 covers the message handlers in depth. While section 5.5.1 talks about how
messages and events are handled in ExAgent reasoning cycle.
The third major deviation from AGENTSPEAK is how ExAgent handles plan
execution failure. ExAgent puts the failure handling and recovery as a first-class
construct by introducing a recovery plan handler. In ExAgent, recovery plan rules
can be defined by using the recovery plan rule definition keyword. By introducing
a recovery plan rule keyword, the event that caused the failure in execution is made
more clear and easier to spot. Section 4.5.6 goes into deeper details about this
language feature.
Finally, ExAgent allows developers to extract common and shared responsibil-
ities that multiple agents play into roles. A role represents a responsibility and
encapsulates the state, which is the set of beliefs required for the responsibility, the
behaviour, expressed by the plan rules, and the message and recovery handlers,
needed for the successful execution of the responsibility. Agents can then state
37
into their agent definition block that they are using a role and will inherit the
role’s state and behaviour. By having roles, ExAgent, reduces the need to
duplicate the code required to implement common responsibilities and tasks. It
helps to introduce higher-level abstraction in the agent program and it enables the
creation of self-contained libraries that can be shared between ExAgent developers
in source code repositories like GitHub. Section 4.5.7 expands on the role feature
in ExAgent.
4.2.3 Agent environments
The Environment Interface Standard (EIS) is a Java-based agent environment
framework. It provides a standard framework for connecting agents to diverse
environments, such as games, web services, and user-interface applications. EIS
standardises the interactions between the agent and the environment by exposing
a set of functions to read the environment and to act on it.
Agent frameworks that support EIS interfaces are able to connect and utilise
any environment built on EIS. As an example, an EIS compatible agent will be
able to connect to starcraft game or blocks world using the EIS interface. Any
developer can wrap a Java application in an EIS interface and make it available
to be used by these AOP languages.
One other important benefit of using EIS is that it allows inter-agent
communication between different agents. These agents will communicate with
each other through sensing and acting on a shared environment. The environment,
in this context, acts as an abstract communication medium that links agents
together.
EIS is already implemented in some AOP languages and frameworks such as
JASON and Astra. Due to its adoption and popularity, we decided to attempt to
implement an Erlang wrapper for EIS that will allow us to create EIS compatible
agents in ExAgent.
38
4.3 Objectives
We sparsely mentioned in multiple sections the objectives and goals we set for
ExAgent. The following is a more formal listing of these objectives.
• Implement the BDI agent model architecture.
• Implement a PRS reasoner that takes the agent’s mental state and surround-
ing environment into consideration in its reasoning cycle.
• Create a programming language with syntax and semantics that are familiar
to Erlang and Elixir developers.
• Provide a communication mechanism between agents build on top of the
robust messaging infrastructure that Erlang provides.
• Enable ExAgent the ability to create fault-tolerant agents by leveraging the
supervision strategies that Erlang vendors.
• Interoperate with EIS Java-based agent environment to enable connecting
ExAgent with other AOP languages.
4.4 Language syntax
The following BNF grammar describes ExAgent’s syntax. In the grammar a
<Constant> is an identifier starting with a uppercase letter such as CounterA-
gent. <Var> is an identifier starting with a uppercase letter like X, Y and Car.
A <Name> is snake cased identifier that starts with a lower case letter like print
or print string. An <ElixirEx> is any valid Elixir function-call expression or
arithmetic expression. <String> is any valid Elixir string which takes the form
of "string". And <Atom> is an Elixir atom with the form of :atom_name.
39
Listing 4.1: ExAgent language BNF Grammar
agent = "defagent" agent_name "do"[roles][initial_beliefs][initial_actions][plans]
"end"
role = "defrole" role_name "do"[initial_beliefs][plans]
"end"
agent_name = <Constant >
role_name = <Constant >
roles = "roles" "do"(role_name "\n")*
"end"role_name = <Constant >
initial_beliefs = "initial_beliefs" "do"(initial_belief )*
"end"
initial_actions = "initialize" "do"plan_body
"end"
plans = ( plan )*plan = rule
| atomic_rule| message| atomic_message| recovery
rule = "rule" "(" plan_trigger [ plan_context ] ")" "do"plan_body
"end"
atomic_rule = "atomic_rule" "(" plan_trigger [ plan_context ] ")" "do"plan_body
"end"message = "message" "(" message_trigger [ plan_context ] ")" "do"
plan_body"end"
atomic_message = "atomic_message" "(" message_trigger [ plan_context ] ")" "do"plan_body
"end"
40
recovery = "recovery" "(" plan_trigger [ plan_context ] ")" "do"plan_body
"end"
plan_trigger = goal | belief
message_trigger = performative "," sender_name ","" message_contentperformative = <Atom >sender_name = <Name >message_content = literal
plan_context = "when" [ "not" ] literal ("&&" [ "not" ] literal )* [ "&&" test_func]
test_func = "test" <ElixirEx >
plan_body = ( plan_body_item "\n" )*plan_body_item = goal
| belief| query| internal_action
goal = "!" literalbelief = ("+" | "-" | "-+") literalquery = "query" "(" literal ")"internal_action = "&" literal
initial_belief = literalliteral = <Name > [ "(" list _of_terms ")" ]
list _of_terms = term ( "," term )*term = list
| arithmatic_expression| <Var >| <String >| <Atom >| <ElixirEx >
list = "[" [ term ("," term)* ] "]"
arithmatic_expression = arithmatic_term[ ( "+" | "-" ) arithmatic_expr ]
arithmatic_term = arithmatic_simple [ ( "*" | "/" ) arithmatic_term ]
arithmatic_simple = <Number >| <Var >| "-" arithmatic_simple| "(" arithmatic_expression ")"
41
4.5 ExAgent language concepts
ExAgent is fully implemented in Elixir using its procedural macros. These type of
macros are executed as a pre-compilation step that takes effect before the Elixir
compiles the full project. As a result of that, and since one of the main goals
of ExAgent is to look and feel familiar to Elixir developers, some of ExAgent
language syntaxes are derived and heavily inspired by Elixir.
Among the list of ExAgent keywords that are inspired by Elixir are defagent,
which is used to define an agent and defrole, which introduces a new role. Both
these keywords are derived from defmodule which is an Elixir keyword that creates
a new module.
It’s important to state that we expect for the first version of ExAgent language
to be solely used by developers that are familiar with Elixir. In order to understand
ExAgent syntax, the developer has to have some prior understanding of Elixir.
Section 5.1 contains a small introduction to Elixir language. It describes the main
features that Elixir provides.
In the following sections, we will describe the major language concepts and
features provided by ExAgent.
4.5.1 Creating an agent
ExAgent can be easily used in any Elixir project, to do that the developer should
state that he is using ExAgent in the current Elixir file by including use ExAgent
at the top of that file. The use statement introduces ExAgent’s keywords needed
to create agents and roles in the current Elixir file.
The core component of an ExAgent program is an agent. Agents can be defined
by invoking the defagent macro. The do end keywords denote the start and the
end of the agent definition block.
defagent MyAgent do#Beliefs , Goals , Rules , Etc...
end
42
The name ”MyAgent” in the example above, represents the agent module
which is equivalent to a class in OOP terms. Multiple agents of ”MyAgent” type
can be defined, each of these agents will have its own internal state. To start
an agent, we call ExAgent.start_agent(MyAgent, "agent1"). The start agent
function requires the agent module and its name. The agent name (”agent1” in
the previous example) is unique and cannot be reused with other agents that share
the same agent module.
The developer can specify different aspects and characteristics of the agent by
using their specific keywords in the agent definition block. The following listing
describes most of the keywords available:
• Agent initial beliefs (using initial_beliefs).
• Agent initial goals (using initialize).
• Agent plan rules to handle the received events (using rule).
• Agent message handlers that gets executed when the agent receives a
message (using message_handler).
• Agent recovery actions (using recover).
• Agent roles and responsibilities defined with defrole (using roles).
Each of the above keywords is covered in more depth in the upcoming sections.
4.5.2 Initial beliefs and goals
The developer can specify the agent’s initial beliefs and goals by setting them in
the correct section of the agent definition block. To specify the initial beliefs of an
agent, the developer has to utilise the initial_beliefs keyword. The following
listing shows an example of setting the initial beliefs of an agent.
43
defagent MyAgent do
initial_beliefs docolor(:red)has_money (1000)owns(:car , :blue)
end
end
In ExAgent, we tend to use Elixir atoms, such as :red, :blue and :car to
define constants.
In addition to the initial beliefs, the agent might need to specify a set of initial
goals to start its operation. For example, we might want a Counter agent to start
counting when it’s initialized. To do that, we use the initialize keyword in the
agent definition.
defagent Counter do
initialize do+counter (5)!count
end
end
In the previous listing, the initialize section of the Counter agent contains
two instructions; an add belief instruction +counter(5), which when executed
will add a belief that the counter value is 5 to the agent belief base. And an
achieve goal instruction !count, which tells the agent to achieve the count goal.
Upon executing each of these instructions, the agent will create an event that may
trigger a rule adoption in the agent reasoning cycle.
The initialize keyword in the agent definition acts as the agent constructor
which specifies the instructions that the agent will execute first when its started.
44
4.5.3 ExAgent instructions
ExAgent provides four main type of instructions; adding/removing/substituting
beliefs, achieving goals, querying beliefs and internal actions.
Adding, removing and substituting a belief is done by prefixing a predicate
with the ”+”, ”-” and ”-+” respectively.
-counter (5)+counter (4)
In the previous listing, the counter(5) belief is removed and the counter(4)
is added. The same action can be done with the substitute belief instruction which
takes the form of -+counter(4). The substitute belief instruction removes all the
counter beliefs in the agent and adds a new one with the new value. Adding and
removing a belief creates an event that is appended to the agent list of events.
This event may cause a rule to be selected in the next agent reasoning cycle.
An achieve goal request is done by prefixing a predicate with the ”!” character.
For example, to achieve buying of a red car goal, we execute !buy(:car, :red).
As with beliefs, goals achieving instructions, when executed, cause new events to
be created and possibly new plan rules to be selected in the agent reasoning cycle.
Querying for a belief is performed by surrounding a belief with the query
instruction. To query for the current counter we execute query(counter(X)).
When querying for a belief, a parameter (or group of parameters) can be passed.
This parameter can be a variable or a constant. In case a variable is passed, the
query instruction will unify the agent beliefs with the query and bind a value to
the passed variable. This process is referred to as the ”variable unification”. After
the unification, the variable will be substituted with the unified value in a process
called variable binding. If a constant is passed, on the other hand, the unifier will
use that constant when unifying the agent beliefs.
In both the cases, if the agent belief base does not include the searched query,
the unifier fails and an error is generated. The generated error activates the agent
recovery handling process which is covered in the recovery handling 4.5.6 section.
45
Finally, internal actions are prepended by ”&” character. At the moment three
internal actions have been implemented; send which is used to send a message to
another agent, print which allows the agent to prints a string to console, and
exit to halt the execution of the agent.
The following listing shows an example of using the different instructions
available in ExAgent.
!buy(:car)query(owns(:car , Color))&print ("I own the #{Color} car")&send(" agent1", :inform , bought(Color ))
These instructions can appear in the plan body for any of the agent plan rules,
message handlers or recovery handlers.
4.5.4 Plan rules
The agent behaviour is defined in its plan rules, message handlers, and recovery
handlers. Each of these handlers contains a set of instructions that gets executed
when the handler is selected. To select a plan handler the agent has to receive an
event that matches that handler selection criterium.
The first handler type, the plan rule handler, has the following structure:
rule(trigger when context) do# Plan body
end
Plan rules consist of three parts; the trigger, the context and the body.
• The plan trigger defines the event that needs to happen in order for the plan
to be selected. This event can either be an added or removed belief, or an
achieved goal event.
• The plan context is a collection of belief queries that the agent has to hold
in its belief base for the plan to be considered.
46
• The plan body contains the set of instructions that define the plan behaviour.
These instructions will be executed in sequential order when the plan is
chosen for execution.
The following example shows the three parts of the plan rule.
rule (+! buy(Car , Color) when not has(Car) &&cost(Car , Money) &&money(Pocket) &&test Pocket > Money) do
# Plan bodyend
The example shows a plan rule that gets selected when the buy(Car, Color)
achieve goal event is received. Car and Color, in the example, are variables that
will be bound with real values when the plan is executed. For the plan to be
selected, the agent should believe that it does not own that car (not has(Car)),
that it knows the cost of the car (cost(Car, Money)), and that it owns enough
money to buy the car ((test Pocket > Money)).
Notice that in the example above the context contains two variables. These
variables will be given real values when the reasoner unifies the plan trigger event
with the agent currently held beliefs.
If the plan trigger and context checks succeed, the reasoner will select the plan
for execution and will execute the instructions in the plan body.
4.5.5 Message handlers
Agents are social entities; they communicate with each other when conducting
their tasks in order to achieve their goals. When an agent receives a message, it
has to decide whether it can handle it or it should forward it to other agents. This
decision is defined in the agent message handlers.
Agents can define message handlers that get selected when specific messages
are sent to it. The format of the message handler is as following.
47
message(performative , sender , content when context) do# Handler body
end
The message handler head takes three parameters; first, the FIPA performative
which can be any of :inform :request or :achieve. While this parameter needs
to be defined, the semantics behind it is not yet implemented in ExAgent and is
reserved for future versions of the language. Second, the sender parameter which
will hold the address of the agent that sent the message. In the message handler
body, the sender can be used to send a reply message back to the message event
originator. Finally, the message content defines the payload (or content) that
was sent in the message. This can be any literal (check the full example below).
The message handler context and body are similar to the context and body
we specify in a plan rule (4.5.4).
A concrete example of a message handler can be seen in the following listing.
message (:inform , sender , sell(Car)when has(Car) &&cost(Car , Price) &&test Price > 1000) do end
# Handler bodyend
This message handler is selected when an :inform message is sent to the agent.
The message content has to be in the form of sell("Anything"). A message that
follows this format can be sent using a send message instruction that looks like
&send("name", :inform, sell(:bmw)).
4.5.6 Recovery handlers
The final form of plan handlers is the recovery handler. This handler is activated
when an error is encountered while executing an instruction. For example, if the
reasoner is executing a query belief in a plan rule and the beliefs unification fails,
the reasoner will try to select a recovery handler that matches the event that
48
triggered the failing plan (given that the plan context also matches the recovery
handler context). The format of the recovery handler is as follows.
recovery(trigger when context) do# Plan body
end
The recovery handler trigger, context and body are identical to the ones we
described in the plan rule section (4.5.4). As an example, we can define a recovery
handler that handles a failure when achieving the buy(Car) goal as following:
recovery (+! buy(Car) when owns(Car)) do# Handler body-owns(Car)
end
Recovery plan handlers help make agents in ExAgent resilient and provide a
way to return the agent to a correct state in case an error occurs while executing
a plan.
4.5.7 Roles and reusability
ExAgent allows developers to extract and package cross-agent responsibilities and
tasks conveniently in roles. A Role is heavily inspired by OOP inheritance which
allows classes to inherit state and behaviour from parent classes. In ExAgent,
when a developer identifies a cross-agent responsibility he can move the state and
behaviour needed for that responsibility in a role. For example, a simple counter
role that counts from ten to zero looks like the following.
49
defrole CounterRole do
initial_beliefs docounter (10)
end
rule (+! count when counter (0)) do&print ("Done !")
end
rule (+! count when counter(X)) do-+counter(X - 1)&print (" Counter Value #{X}")!count
end
end
Agents can decide to inherit the CounterRole by stating they use the role in
their agent definition block.
defagent MyAgent doroles do
CounterRole# Other roles
end
initialize do!count ()
endend
When created, MyAgent agent, will inherit CounterRole role beliefs defined
in its initial_beliefs and its behaviours defined with plan rules, message
handlers, and recovery handlers. The agent can then execute instructions that
trigger behaviours defined in the role; in the previous listing MyAgent is starting
the counting by requesting the achievement of !count() goal in its initialize
section. That will eventually trigger the +!count plan rule that CounterRole role
defines.
50
CHAPTER
FIVE
EXAGENT DESIGN AND IMPLEMENTATION
In the previous chapter, we discussed ExAgent main concepts and ideas. In this
chapter, we dig deeper into ExAgent’s design and implementation. We will cover
how to create an agent, how to start this newly created agent, how to set the
agent’s initial beliefs and goals, and how to specify the agent’s behaviour by
defining plan rules, message handlers and recovery handlers.
Additionally, this chapter will describe ExAgent’s overall architecture and
runtime. That includes the agent reasoning logic that is the heart of the agent
runtime. And the unification logic used to unify the plan context queries with the
agent’s beliefs.
Since ExAgent was built with Elixir and relies heavily on its idioms, before
we venture into the implementation of ExAgent, this chapter presents a quick
introduction to the Elixir language.
5.1 Quick tour of Elixir programming language
ExAgent is built by leveraging Elixir’s procedural macros. It uses these macros
to alter the Abstract Syntax Tree (AST) of the Elixir program prior to its
compilation. For that reason, it’s important to understand some of Elixir’s
programming language concepts and ideas.
51
As we already described in previous chapters, Elixir is a dynamic functional
programming language built on top of the Erlang BEAM VM. It is a general
purpose programming language designed to build scalable and maintainable
applications. Elixir gives the developer the possibility to extend the language
to fit his needs. It does that by introducing hygienic procedural-macros in the
language; developers can use these macros to edit the AST prior to the final code
compilation.
Being a functional programming language, as opposed to OOP languages,
Elixir programs are not organised into classes and inheritance trees. Instead, they
are organised into functions that are grouped and categorised into modules. Each
module contains a set of functions that share a common area of responsibility.
For example, Elixir standard library provides the String module that contains
functions that deal with strings.
> String.upcase ("hello ")HELLO
> String.capitalize (" hello world")HELLO WORLD
> String.replace ("a,b,c", ",", "-")a-b-c
Elixir modules are defined using the defmodule macro. And functions are
introduced using the def macro. The following listing shows a very simple module
that adds two numbers.
defmodule NumberAdder dodef add_numbers(first , second) do
first + secondend
end
The module in the previous listing can be invoked by calling the add numbers
function as NumberAdder.add_numbers(1, 2). Elixir uses snake case for function
names and camel case for the module names. In normal cases, each Elixir module
is saved into its own elixir file with the ”.ex”/”.exs” extension.
52
Elixir and Erlang are known for their powerful pattern matching functionality.
When used with function parameters, pattern matching allows functions to be
clear, concise and readable. For example, a divide function can be written as
follows.
defmodule NumberAdder dodef divide(_, 0) do
:cant_divideend
def divide (10000000000 , _) do:too_big
end
def divide(first , second) dofirst / second
endend
Notice how we defined three version of the divide function. By using pattern
matching, we were able to separate the actual divide logic from the error handling
code. It’s also visible how pattern matching removes the need to write conditional
if statements which make the code easier to understand.
In Elixir, all code runs inside processes. These processes differ from the
operating system ones as they are much lighter and cheaper to create. It is
considered common to start hundred of thousands or even millions of them in
a single Elixir application. In the following listing, we show how to start a process
and how to send a message to that process.
# Creating a processpid = spawn fn ->
# Wait for messagesreceive do
message -> ...end
end
# Sending a messagesend(pid , {"hello", "world "})
53
One last feature we will cover in this section is Elixir meta-programming
functionality. Elixir gives the developer the possibility to extend the language
and create DSLs by using macros. These type of macros are called procedural
macros and they give the developer the power to rewrite the whole program AST
before compiling it.
Using Elixir macros enabled the development and implementation of ExAgent,
which can be seen as a heavily customised DSL that introduces AOP concepts
such as agent, belief, rules and goals. Since ExAgent is a DSL it still maintains
interoperability with normal Elixir code and it also looks familiar when viewed by
Elixir and Erlang developers. We believe doing that will lower the adoption bar
for Elixir/Erlang developers that want to try ExAgent in particular and AOP in
general.
5.2 ExAgent Architecture
ExAgent codebase can be split into three main areas.
• Code parsing and generation
• Agent creation and state handling
• Agent reasoning cycle
Each of the following sections covers one of the areas listed above.
5.3 Code parsing and generation
ExAgent code is embedded in Elixir files and is compiled along them to form
the final compiled artifact. When compiled, ExAgent code goes through several
steps; parsing, tokenisation, and conversion to intermediate Elixir code and data
structures. These steps are performed by ExAgent compiler as a pre-compilation
step that happens before the compilation of the Elixir file. The intermediate Elixir
code, that resulted from compiling ExAgent code, is mixed with the rest of the
54
Elixir code in the source file and is sent for final compilation. Before going deeper
in the compilation process, let’s take a look at a simple ExAgent agent definition
snippet.
defagent FullAgent doinitial_beliefs do
a_belief ("some value ")end
initialize do+new_belief (1, 2)!some_goal
end
rule (+! some_goal when belief1 (..) &&belief2 (...)) do end
message (:inform , sender , do_something (...)when belief1 (..) &&belief2 (...)) do end
recovery (+! some_goal when belief1 (..) &&belief2 (...)) do end
end
defagent is a keyword introduced by ExAgent that is used to create agents.
This keyword, behind the scene, is implemented as an Elixir macro that receives
two parameters; the name of the agent (”FullAgent” in the above example) and
the the agent definition block. The definition block contains the instructions
that define the agent behaviour, such as its initial beliefs, rule handlers, message
handlers and recovery handlers.
The agent definition block is passed to defagent as an AST. Syntax trees
in Elixir are represented with native types such as lists, atoms, structures and
tuples. Normally, the syntax tree for an Elixir instruction is represented as a
tuple with tree parameters; the function name for the operation, the line number
of the operation in the source file, and the parameters passed to the operation. A
function similar to add(1, 2), when passed to a macro, would result to an AST
that looks like {:add, [line: _], [1, 2]}.
55
Representing the AST as a native tuple has many advantages; for starters, It
can be easily representable in Elixir code and does not require special consideration
and data structures to hold it. Second, it is easy to parse by Elixir functions and
can be pattern matched. Third, it can be read and understood by developers
without the need of special tools. Finally, it is highly scalable since it relies on
tuples and arrays that can scale without bounds.
For instance, the skeleton agent definition block shown in the listing above can
be represented as an AST composed of three items tuples.
{: __aliases__ , _, [: FullAgent ]}{:__block__ , [],[{: initial_beliefs , _,
[[do: {:a_belief , _, ["some value "]}]]} ,{:initialize , _,[[do: {:__block__ , [],
[{:+, _, [{: new_belief , _, [...]}]} ,{:!, _, [{: some_goal , _, nil }]}]}]]} ,
{:rule , _,[{:when , _,
[{:+, _, [{:!, _, [{: some_goal , _, nil }]}]} ,{:&&, _,[{: belief1 , _, [....]} , {:belief2 , _, [...]}]}]} ,
[do: ...]]} ,{:message , _,[:inform , {:sender , _, nil},{:when , _,[{: do_something , _, [...]} ,{:&&, _,[{: belief1 , _, [...]} , {:belief2 , _, [...]}]}]} ,
[do: ...]]} ,{:recovery , _,[{:when , _,
[{:+, _, [{:!, _, [{: some_goal , _, nil }]}]} ,{:&&, _,[{: belief1 , _, [...]} , {:belief2 , _, [...]}]}]} ,
[do: ...]]}]}
This AST follows the same three items tuple system of { operation, line
number, parameters } (note that line number have been removed from the listing
to declutter it). The agent definition AST above contains all the agent information.
It is processed inside defagent which parses and recursively converts it to create
the appropriate data structures that will then be used to compose the agent model.
56
The AST passes through a pipeline of parsers that recursively understand and
convert the raw syntax nodes to the higher abstracted ExAgent data structures.
Firstly, InitialBeliefs parser reads the initial_beliefs section of the AST
and converts it to a data structure that contains the initial beliefs the agent will
have. Rule parser then parses the rules and atomic rules sections from the AST.
It converts and decomposes them to the rule head, that contains the rule trigger
and the rule context, and the rule body, that contains the instructions the rule
will execute. Similarly, MessageHandler will parse the agent’s message sections
and converts them to message head, that defines the message performative, the
sender name and the message context, and the message body, which is analogous
to the plan body. Finally, the recovery handlers will also be parsed and converted
in a process that resembles the plan rules and message handlers conversions.
After the AST is converted into the appropriate data structures, defagent
creates a new Elixir module with the name of the agent. This newly created
module contains functions that return the agent’s initial beliefs, initialization
actions, plan rules, message handlers, and recovery handlers. This Elixir module
can be seen as the agent class in OOP term.
ExAgent additionally provides defrole; which is used to create a new role as
opposed to an agent. A role represents a responsibility that an agent can play.
For example, when modeling a banking AOP solution the teller can be seen as
a role. Multiple different types of agents can have this role, and hence ExAgent
defrole can help build responsibility abstractions.
defrole is almost identical to defagent, in that both of them are implemented
as Elixir macros and both of them convert AST to agent components data
structures. The difference between a role and an agent is that a role cannot
be started; a role has to be added to an agent by using the roles keyword. The
following listing shows the definition of a role and an agent inheriting that role.
57
defrole Teller do...
end
defagent FullAgent doroles do
Tellerend
end
The FullAgent agent is inheriting the Teller role by stating that in its roles
section. When starting FullAgent agent the initial beliefs, plan rules, message
and recovery handlers from the Teller role are made available to the FullAgent
as a consequence of having Teller in the roles section.
5.4 Agent creation and state handling
When defagent gets executed, it creates a new Elixir module that becomes
available to the application. This Elixir module contains the agent definition
information that was passed to defagent; such as the initial beliefs and goals, the
plan rules, the message handlers and the recovery handlers.
When starting the agent, the state of the agent is created by ExAgent. The
agent state data structure consists of these components; beliefs, plan_rules,
recovery_handlers, message_handlers, intents, events, name, module and
messages.
The agent holds the current beliefs in its state. When first initialized, the
value of the beliefs is the one specified in defagent. As the agent receives more
internal events, such as requests to achieve goals or add and remove beliefs, or
external events, such as messages sent from other agents, its state is updated with
the new beliefs. At each point in time, the beliefs held by the agent are reflected
within the agent state data structure.
The agent state also contains a set of values in its data structure that
are immutable. These are; a) the plan handlers, stored as plan_rules,
recovery_handlers and message_handlers, b) the agent display name stored
58
as name, and c) the agent Elixir module name, stored as module. Since there
is no way for an agent to acquire new plan handlers or to change its name
and module, the values for these keys are set at start time and are kept
unchanged during the execution of the agent. The origin of these values are
self-explanatory; the name and module are derived from the agent start function
call ExAget.start_agent(AgentModuleName, "agent_display_name"), and the
plan handlers are derived from the values that are parsed from the agent definition
block.
The instructions executed by the agent, such as adding and removing beliefs
or achieving goals generate new events. These events are collected and stored
in the events entry of the agent state data structure. In each agent reasoning
cycle, the events are processed and consumed. From these events (and from the
currently executing instruction) the reasoner creates or updates an intent which
is used to execute instructions. This newly created intent is added to the agent’s
partially-executed intents that form the agent intents list. The list of intents is
stored in the intents key of the agent state.
The final key stored in the agent state data structure is the list of messages.
This entry of the state holds all the unhandled messages that the agent has
received. At each agent reasoning cycle, the reasoner checks the agent message box
for newly received messages. These messages are read, processed, and converted
to events that are used by the reasoner to select a message handler. The message
entry in the agent state holds the list of messages that are ready to be consumed
by the reasoner.
The message, events and intents play a big role in determining the agent
behavior and runtime. The next section covers their role and describes the agent
reasoning cycle.
5.5 Agent reasoning cycle
After starting the agent, its run loop starts operating. The run loop can be
considered as the agent’s brain where it processes the events perceived from the
59
environment, and it processes the messages sent to it by other agents. Each
iteration of the run loop is called a reasoning cycle. In each cycle, the agent state
is analysed, events are processed, intents are created, and a new agent state is
produced and stored in the agent.
In ExAgent the Reasoner module is responsible for the reasoner cycle. In each
cycle, Reasoner.reason_cycle is called with the agent current state and a new
state is generated. Several operations are performed in this cycle. In the following
sections, we cover each of these steps. Reasoning cycle process flow diagram (5.1)
shows the overall reasoning process.
5.5.1 Message processing and Event selection
When agents send messages between each other, these messages ends in their
respective mailboxes. Each agent has its own separate mailbox that contains all
the unprocessed messages sent to it. These messages stay on hold in the mailbox
waiting for a reasoning cycle to read and process them. The processing of the
messages happen as the first step of the agent reasoning cycle and is done in the
Reasoner.Message module’s process_messages function.
In process_messages function all the messages in the agent’s mailbox are
read and converted to agent received_message event type. As the messages are
converted to the appropriate events they are removed from the agent’s mailbox.
As a result of this function, the mailbox of the agent is cleared and new events for
each message is generated and prepared to be processed by the next step of the
reasoning cycle.
The events produced by process_messages are merged together with the
agent’s yet unprocessed events to form the full list of the agent events. The
merging of the event happens in Reasoner.Event.merge_events. The agent list
of events is then passed through the events selection process. In this process,
Reasoner.Event.select_event function decides which event is selected to be
the current event that is processed in the current agent reasoner cycle.
In the case that an agent mailbox is empty, i.e it didn’t receive any message, and
the agent event backlog is also empty, the event selection produces a :no_event
60
result. In this case, the plan selection step of the reasoner is skipped and the
intent selection step is executed.
5.5.2 Plan selection
Now that an event has been identified and selected, the reasoner in Reasoner.Plan
module’s select_handler function attempts to find a plan handler that can
handles that specific event type.
The plan selection strategy depends on the event type. In case of received_message
events, the MessageHandlerSelection plan selection strategy is used. In this
strategy, a list of relevant plans is composed by conducting a series of filtration on
the agent’s message handlers. First, all the message handlers that have the same
message performative as the event being handled are selected. From this list,
a unification logic is applied on this list to unify the message handler’s message
content with the event content. The unification produces a list of variables binding
which will be used in selecting the applicable plans.
If the event type is one of added_belief, removed_belief, or added_goal
then the relevant plans are found by iterating the agents plan rule handlers and
selecting those handlers that have a plan handler trigger that matches one of the
three event types listed above. From this list of plans, only those plans that plan
trigger’s can be unified with the event trigger type are select. As with the message
handler plan selection, a list of variables bindings is computed and prepared to be
used in the selection of the applicable plans.
After deciding the relevant plans, the reasoner will identify and select the
applicable plans. Applicable plans are those plans that plan context matches the
agent’s current beliefs. In order to check the plan context validity, the relevant
plans are passed through a filtering process that unifies the plan’s context tests
with the agent’s current beliefs. Once the applicable plan selection step is finalized,
the list of applicable plans is identified and the first applicable plan is selected to
be used in the intent processing reasoner step.
61
5.5.3 Intent processing
An intent is a data structure that contains one or more intent execution stacks.
When first created, an intent contains a single intent execution stack. As the
reasoner processes more events, the execution stack can grow based on the
executed event type.
Each intent_execution is a data structure that describes a partially applied
plan. This data structure contains four keys; a) a list of instruction that the
originating plan had in its body, stored as instructions, b) the list of variable
bindings generated from the unification of earlier beliefs or plan context, stored as
bindings, c) the originating plan stored as plan, and d) the event that originated
the intent, stored as event.
The intent processing reasoning step starts by processing the selected plan. The
processing of the plan depends on the event that resulted in the plan selection;
If the event is an added_belief, removed_belief or received_message a new
intent is created and appended to the current list of intents that the agent holds
in its state. On the other hand, if the event is an added_goal, a new intent
execution stack structure is appended to the intent execution stack for the intent
that generated the event.
Once a new intent is created (or updated with a new execution stack), the
reasoner selects an intent from the list of intents that the agent currently holds.
The selection mechanism makes sure that execution of the intents interleave with
each other; this has been naively implemented in ExAgent by making sure that
the intent selected in a reasoning cycle ends up being added to the tail of the agent
intents. By appending it to the tail, intents will be processed in an interleaving
fashion.
Next, the selected intent is passed to the Reasoner.Intent.execute_intent
function. In this function, the first instruction in the stack of the intent
instructions is selected for execution. Depending on the instruction type, the
list of the intent variable bindings might be updated, the agent beliefs might be
altered (by adding or removing beliefs), and new events might be generated. These
new beliefs and events will be later used to form the new agent state.
62
The Reasoner.Intent.execute_intent function is also able to execute
atomic plans. When executing an atomic plan, all the instructions of that plan
(which are now part of the intent instructions) are executed at once. For atomic
plans, the intent will be later removed as all of it instructions are guaranteed to
be executed in the same reasoning cycle.
When instructions are executing, errors and exceptions can occur. In this case,
the intent execution step will signal the reasoner about the error. The reasoner will
also be informed about the failing intent, the failing instruction, and the failing
event that generated the intent. This information, especially the failing event, will
be used by the reasoner to find a recovery handler in the handler_recovery step.
If all the instructions in an intent have been executed, the intent will be
removed from the list of intents. The updated list of intents will be later used to
update the agent state.
5.5.4 Error handling
In the event of an exception while executing an instruction, the reasoner will
perform a recovery handler selection step. This step is done in the
Reasoner.Plan.select_recovery_handler function. When an error occurs, the
reasoner will receive an execution_error result from the instruction execution
step. The error contains metadata information such as the failing instruction and
the failing event that caused the error. In order to find an applicable recovery
handler, the failing event is used to select a recovery handler from the agent’s list
of recover_handlers. The process for selecting a recovery handler is analogous
to selecting a plan handler with the only distinction being the source of handlers
which is the agent’s recover_handlers list.
5.5.5 New state generation
The final step of the reasoning cycle is updating the agent’s state. In this step,
Reasoner.AgentState.update_state function prepares a new state and replaces
the agent’s state with it. This new state is built by updating some of the values
63
of the previous state with new ones produced from the reasoning cycle’s intent
execution step.
First, since all the pending messages are always processed and flushed in the
reasoning cycle, the list of messages in the old agent’s state is replaced with an
empty list. Next, the agent’s beliefs are updated with the result of executing an
added_belief or removed_belief instruction. The new set of beliefs replaces the
old set completely. After that, the list of agent’s intents is updated by appending
the currently executed intent to the end of the old list of intents. This is done to
achieve intent execution interleaving. Finally, the new list of events is calculated
by adding any new events generated in the intent execution step and is stored in
the agent state.
If the agent has processed all its events and executed all of the intents, the new
agent state will have an empty list of intents and events. When that happens,
the agent is set into a hold state. In this state, the agent reasoning cycle will
be paused and only resumed if the agent receives new external events such as
message_received event types.
64
Listing 5.1: ”ExAgent reasoner main function”
def reason(agent_state , beliefs , plan_rules , message_handlers ,
recovery_handlers , events , messages , intents) do
message_event = Reasoner.Message.process_messages(messages)
all_events = Reasoner.Event.merge_events(message_event , events)
{event , rest_events} = Reasoner.Event.select_event(all_events)
with {:ok , plan , binding} <-
Reasoner.Plan.select_handler ...,
{:ok, new_intents} <-
Reasoner.Intent.process_intents ...,
{:ok, selected_intent , rest_intents} <-
Reasoner.Intent.select_intent ...,
{:ok, new_event , new_intent , new_beliefs} <-
Reasoner.Intent.execute_intent ... do
new_state = Reasoner.AgentState.update_state ...
{:changed , new_state}
else
:no_intent when event != :no_event ->
log_event_removed(event , rest_events)
{:changed , Reasoner.AgentState.update_agent_events ...}
:no_intent ->
Logger.info "No intents left , No Events left"
{: not_changed , agent_state}
:halt_agent ->
Logger.info "Halting agent received"
{: halt_agent , agent_state}
{: execution_error , f_intent , f_instruction , f_event} ->
select_recovery_plan(agent_state , beliefs , recovery_handlers ,
events , f_event , intents , f_intent)
unexpected ->
Logger.info "Unexpected result received\n#{ unexpected }"
end
end
65
Figure 5.1: Reasoning cycle process flow diagram
66
CHAPTER
SIX
EVALUATING EXAGENT
In this chapter, ExAgent’s performance and feature set are validated and evaluated
against JASON AOP language. JASON language was selected for the evaluation
since it’s one of the most popular AOP languages at the moment of writing.
Finally, we conclude the chapter with an experimental work-in-progress library
- ExAgent.Web - that helps to automatically generate a JSON Application
Programming Interface (API) web server for ExAgent agents.
6.1 Performance evaluation
A comparative performance evaluation was conducted to compare ExAgent with
JASON. When selecting which AOP language to profile, JASON was chosen as
is one of the most prominent agent programming language available. In addition,
JASON was one of the main inspiration for creating ExAgent, and hence, the
feature set of ExAgent is comparable with it.
All of the tests were performed on a Mac OS machine running the latest macOS
Sierra operating system (version 10.12.3 (16D32)). The machine had a 2.2 GHz
Intel Core i7 processor and had a supply of 16 GB DDR3 ram running at a clock
of 1600 MHz. Each of the tests was conducted three times. The average value of
these three iterations was used as the final result for the test.
67
Two tests were performed to profile the performance of ExAgent; a counter
test which spawns a number of counter agents to test the performance of ExAgent
reasoner and ability to maintain a large number of agents operational, and a token
ring test which tests ExAgent’s communication infrastructure performance.
6.1.1 Counter Agent
In this performance evaluation test, an incremental number of counter agents are
spawned. Each of these agents will count down from 10. When the count reaches
zero, the agent job is concluded. When all of the agents conclude their operations
the test is finished and the runtime performance is calculated.
A series of tests, with 1, 10, 100, 1000 and 10000 spawned agents are executed.
Below are the listing of the agents used and the final tests results.
Listing 6.1: ”JASON Counter Agent Test”
counter (10)!startcount.
!count: counter (0) <-+done.
!count: counter(X) <--+counter(X - 1);!count.
68
Listing 6.2: ”ExAgent Counter Agent Test”
defagent MyAgent do
initial_beliefs docounter (10)
end
initialize do!count
end
rule (+! count when counter (0)) do+done
end
rule (+! count when counter(X)) do-+counter(X)!count
endend
Table 6.1: ExAgent vs JASON counter agent performance
Number Of Agents JASON Execution Time (ms) ExAgent Execution Time (ms)
1 24 20
10 450 510.339
100 8030 9203
1000 40184 96662.55
10000 not measurable 940123.4
The results show that JASON is faster than ExAgent in all tests except for
when 10000 agents are used. When profiling ExAgent the major performance
drawbacks were found in the unification and plan selection functionality. The
main bottlenecks were found to be the Reasoner.reason function that took 60%
of the run time. The time spent in this function was evenly split between event
processing, plan selection, and intent execution. Another 15% of the time was
spent on Unifier.do_unifiy which is the main unification function that unifies
69
the agent’s beliefs with the plan’s context. These two functions, the reasoning and
the unification, contain the most computationally complicated logic of ExAgent
runtime. The degradation of ExAgent performance is caused by the fact that
ExAgent current version was not written with optimisation in mind. Future
improvements and corrections to ExAgent’s implementation should be able to
decrease the footprint of these logical operations.
One important aspect of the comparison can be observed when spawning
10000 or more agents; it was not possible to profile JASON as the JVM reached
the thread limit and threw a java.lang.OutOfMemoryError with the reason of
Unable to create new native thread. ExAgent, on the other hand, while not
being very performant (with test taking 940 seconds) was able to run the test
with 10000 agents. This is promising as it shows that ExAgent, with some logical
improvements in the unification and reasoning logic, can scale up to 10000 and
more agents.
6.1.2 Token ring
The second evaluation experiment conducted on ExAgent is to evaluate the com-
munication infrastructure performance. In this experiment, the communication
process is tested by starting a set of agents and have them send a message to each
other. The test was conducted with 10, 100, 1000 and 10000 agents.
When the experiment starts, an initiator agent creates the required number of
participant agents. After that, the initiator sends an initial message to the first
agent. Upon receiving the message, this agent sends it to the next one, which
sends it to the one after, and so on until the last agent is reached.
70
Listing 6.3: ”ExAgent Token Ring Test”
use ExAgent
defagent Initiator do
initialize do&send(Participant.agent_name ("ag1"), :inform , msg (1))
end
end
defagent Participant do
message (:inform , sender , msg(X)) do&send(Participant.agent_name ("ag#{X}"),
:inform , msg(X + 1))end
end
Enum.map (1..10000 , fn i ->ag1 = start_agent(Participant , "ag#{i}", false)
end)
ag2 = start_agent(Initiator , "ag1")
Table 6.2: ExAgent vs JASON token ring performance
Number Of Agents JASON Execution Time (mag-
nitude)
ExAgent Execution Time (ms)
10 430 2.624
100 6120 14.027
1000 34521 137.498
10000 not measurable 1434.302
The results show that in this area ExAgent is an order of magnitude more
performant than JASON. This is due to the fact that ExAgent uses Erlang’s
message sending capabilities which are known to be performant and scalable.
71
In addition, as with the counter agent test, JASON was not able to run 10000
agents as it hit the java.lang.OutOfMemoryError exception and was unable to
continue the test.
6.2 Feature set evaluation
The next area of evaluation is regarding the feature set that ExAgent provides. As
with the performance evaluation, we will use JASON as a baseline for comparing
ExAgent features.
In this section, we examined three areas of functionality; agent language inter-
operability, plan recovery and error handling, and code sharing and reusability.
6.2.1 Agent language interoperability
In order to use utilise a wider range of libraries and already-proven solutions, AOP
languages provide interoperability with other popular programming languages.
Both JASON and ExAgent implement this piece of functionality differently;
JASON provides interoperability with classes and libraries written in Java, which
comes as a result of it being written and implemented in Java. On the other
hand, ExAgent, as it was written in Elixir, provides interoperability with Elixir
and Erlang.
A major driver that shape the way Jason and ExAgent interoperate with
the host language is how these AOP languages are implemented; since JASON
language provides its own external compiler in order to utilise Java code in
a JASON program, the developer has to wrap their Java code in a JASON
provided interface. As an example, a piece of functionality can be wrapped in a
DefaultInternalAction which makes it accessible as an internal action in JASON
code. In comparison, since ExAgent is implemented as a pre-compilation step to
the Elixir compiler, the developer can use any Elixir and Erlang modules and
functions in his agent code without the need to wrap it.
Another difference is how JASON and ExAgent projects are structured. A
JASON project requires its own project structure that contains the multi-agent
72
system definition file (mas2j) and the agent definition files (asl). That makes
the adoption of JASON in a Java project an involved task as the developer has
to think how to repackage his Java classes in order to be usable in the JASON
program. ExAgent follows a different design methodology and project structure.
ExAgent can be mixed in an already established Elixir project. The developer can
gradually introduce ExAgent agents to the Elixir project by using the defagent
macro. This help reduces the time and lowers the adoption barrier. It also gives
the developer the possibility to experiment and prototype his solutions using AOP
concepts without drastic alterations.
In order to illustrate the different approaches in interoperability between
JASON and ExAgent, following are a set of listings that illustrate the way JASON
and ExAgent interoperate with Java and Elixir by reading the system clock time.
As explained at the start of this section, in order to use Java code,
JASON requires the code to be repacked as an internal action by subclassing
DefaultInternalAction.
73
Listing 6.4: ”JASON Java code to read system time”
package date;
import jason.asSemantics .*;import jason.asSyntax .*;
import java.util.Calendar;import java.util.GregorianCalendar;import java.util.logging.Logger;
public class get_date extends DefaultInternalAction {
@Overridepublic Object execute(TransitionSystem ts,
Unifier un,Term[] args) throws Exception {
try {Term t =new ObjectTermImpl(new GregorianCalendar ());return un.unifies(args[0], t);
} catch (Exception e) { }return false;
}
}
Listing 6.5: ”JASON agent code to read system time”
!start.+!start : true <-
date.getDate(T);.print (" Today=", T).
In contrast, since ExAgent code is bundled inside the Elixir host application,
agents can access Elixir code without a need for the code to be changed. The
following listing illustrates how to access the system time in ExAgent.
74
Listing 6.6: ”ExAgent code to read system time”
use ExAgent
defagent ExagentCounter doinitialize do! start
end
rule (+! start) doX = System.os_time ()&print(X)
endend
In the ExAgent listing, System.os_time() is a call to an Elixir standard library
method. The same works for user specific functions and modules created by the
developer in his project.
6.2.2 Plan recovery and error handling
In each reasoning cycle, the agent receives external events that come from the
environment or as messages sent from other agents. And internal events, such as
adding and removing beliefs or receiving requests to achieve goals. These events,
when processed, can result in plan rule selection that gets turned into intents for
the agent to execute. In the intent execution step, the agent selects an instruction
to be executed. Some of these instructions may fail due to the current state of the
agent or the environment.
When failure happens, the agent reasoner drops the intent that contains
the plan rule that failed and tries to activate a plan rule to recover from any
inconsistent state that might have been caused by the execution of the failing
plan rule.
Both JASON and ExAgent provide failure handling; in JASON when a failure
happens in a plan rule, the interpreter checks the plan triggering event. If the
plan triggering event is an achieve-goal event it generates a goal-deletion event.
This event is used to select a recovery plan rule that is capable of handling the
75
failure. However, if the failure triggering event is an added or removed belief
event, JASON does not provide a recovery mechanism for it. Instead, it drops the
executing intent and fails silently.
With ExAgent, we went for a different approach. As with the message plan
handlers, ExAgent provides a first-class keyword to introduce plan recovery rules.
These recovery rules can be defined using the recover keyword. The following is
an illustration of the use of the recovery mechanism.
Listing 6.7: ”ExAgent plan recovery mechanism”
defagent AgentWithRecovery douse ExAgent.Mod
rule(+ belief1) doquery(some_belief (1))
end
recovery (+ belief1) doquery(price)
end
rule (+! goal1(X)) do...
end
recovery (+goal1) do...
end
start()end
By having recovery plans as first-class constructs, ExAgent allows the definition
of recovery plans for not only achieve goals events, but it also allows the definition
of recovery processes for failures in plan rules that originated from added or
removed beliefs events. Listing 6.7 shows a recovery plan for a failed belief
event introduced with recovery (+belief1) and an achieve goal recovery plan
introduced with recovery (+goal1).
76
We argue that by introducing the recover plan handler concept, ExAgent
provides an easier, more clear, and more consistent way to define recovery plans
that will prove to be useful in creating and maintaining large MAS projects.
6.2.3 Code sharing and reusability
As software projects get larger, the number of components, concepts, and entities
increase. As components increase in size, it becomes apparent that there are
some common cross-component functionalities in the project. Additionally, some
of the component tasks can be organised into groups that describe a larger role
the component is trying to accomplish. For these cases, there should be a way to
organise and reuse the code between the program different components.
OOP languages provide code sharing and reuse by the mean of class inheri-
tance; classes contains the methods and the state that are needed to represent
a particular role. Other classes can then inherit from these role classes and get
the behaviour and the state required to accomplish that role. Class inheritance
is one of the major reasons that helped make the OOP paradigm popular as it
simplified analysing, designing, and solving programming problems. Inheritance
not only solves the problem of code reuse but also help to organise the code into
abstractions that make it easier to understand and organize big projects.
A similar concept for code reuse in AOP systems was suggested by the
Gaia methodology for agent-oriented analysis and design [49]. This methodology
describes the systematic steps to progress from the gathering of the requirements
to the final detailed designs that can be used to implement the MAS project.
77
Each step in the previous diagram allows the analyst to move from an abstract
ideation to a concrete system design that has enough information and details to
be implemented. These steps are divided into three phases; requirements phase,
analysis phase, and design phase.
Gaia most interesting step is the roles model which is part of the analysis
phase. This model describes the roles the MAS project contains and it views each
agent as a collection of roles. A role can be described as an abstract description
of an entity’s expected functionality. What makes roles interesting is that they
define functionalities that do not have to map one-to-one to agents in the MAS.
Code reuse functionality in ExAgent was heavily inspired by the Gaia role
model step. In ExAgent, roles can be created by using the defrole keyword.
Each role contains its own set of initial_beliefs necessary for the role and any
plan rule, message handler and recovery handler that define the role behaviour.
As an example, the following listing creates a PingRole, that defines a role that
78
when receiving a ping message will reply with a pong message to the sender. And
a PongRole that does the same but replies with a ping message.
Listing 6.8: ”ExAgent defining roles”
defrole PingRole domessage (:inform , Sender , ping) do
&print ("PING")&send(Sender , :inform , pong)
endend
defrole PongRole domessage (:inform , Sender , pong) do
&print ("PONG")&send(Sender , :inform , ping)
endend
Agents can then leverage these roles by stating they intend to use them in the
agent definition block. That is done by including the roles in the agent’s roles
definition section.
Listing 6.9: ”ExAgent using roles in agents”
defagent PingAgent doroles do
PingRoleend
end
defagent PongAgent doroles do
PongRoleend
initialize do&send(PingTestRespMacroAgent.agent_name ("ag1"),
:inform , ping)end
end
When an agent is created, ExAgent interpreter will read the list of roles that it
listed under the roles section. For each of the roles, the interpreter will read the
79
initial beliefs, plan rules, message handlers and recovery handlers for that role and
append them to the corresponding list in the agent. In the example listed above,
the PingAgent agent, when created, will not only have the message handlers it
defined, but also the ones defined in the PingRole.
By introducing the defrole keyword to create roles in ExAgent, and by having
roles a first-class citizen in the language, we think it will have a positive effect on
the overall developer experience. Especially in these three major areas.
• Defining roles for common agent behaviours and cross-functional responsi-
bilities can reduce code duplication.
In some MAS projects, there might exist a shared or common responsibility
that is implemented by multiple agents. The set of beliefs, plan rules, mes-
sage handlers and recovery handlers that are needed for this responsibility
can be grouped into a role. Agents that want to provide this responsibility
can then use this role instead of duplicating the plans and beliefs in their
definition block.
• Defining the key roles in the MAS project help increase the abstraction level
in the system.
When AOP projects grow in size, the number of agents and the number
of responsibilities held by those agents increase. When that happens, it
becomes more difficult to reason about these agents behaviours. Extracting
each agent responsibility into its own role and giving this role a descriptive
name helps to decrease the conceptual overload when thinking about agents
and helps to create higher abstractions in the system. After defining each
responsibility in a role, agents will state that they are playing the role by
adding it in their agent roles definition section.
By extracting the main responsibilities into roles, big monolith agent
definitions are divided into multiple cohesive roles each representing a single
responsibility. The agent can then be viewed as a collection of these well-
defined roles.
80
• Defining roles for repeating agent patterns help create a library of sharable
and reusable roles.
When developing AOP programs - as when developing programs with other
programming methodologies like OOP - whether these programs are aimed
at a specific or generic domain, a group of coupled beliefs and plan rules
patterns emerge. Some of these patterns may not only be useful for the
current program, but they may also be generic enough to make them useful
for other MAS programs.
When these generic patterns are identified, they can be captured as roles and
published as libraries that other MAS programs can use. A good candidate
can be the contract net protocol [50]; developers can create generic roles
for manager and contractor nodes and package them in libraries. These
libraries can be documented and distributed in code repositories like GitHub.
When other AOP developers from the community need to use the contract
net protocol, instead of writing their own, they can download and use the
protocol shared as a library on GitHub.
Having a library of readily made patterns and protocols implemented as
roles and shared in a code repository, will not only improve the overall
AOP development experiment, but it will also drastically decrease the time
needed to build production ready MAS applications. The time saved from
having to implement these common protocols can then be used to solve more
challenging tasks or in the advancement of the AOP field.
6.3 Generating a JSON API server for ExAgent
agents
As a final evaluation criterium, we experimented with implementing a web
interface for ExAgent. The goal of this experiment is to provide a quick way
to generate an application programming interface (API) for agents. The API
generated is capable of returning javascript object notion (JSON) to be consumed
81
by the clients. This web interface functionality is still experimental and the main
implementation is still a work in progress.
In this experimental implementation, we used Elixir’s Plug [51] HTTP
networking interface. Plug is an Elixir library that provides a specification for
composable modules that define web interfaces. They provide Elixir developer
with a powerful, high-level DSL to create modern web applications that work on
the Erlang VM.
The following code listing describes the minimal code required to create a web
server that returns an HTTP response with the status code of 200 and the ”Hello
world” textual content using Plug.
Listing 6.10: ”Elixir Plug sample code”
defmodule MyPlug doimport Plug.Conn
def init(options), do: options
def call(conn , _opts) doconn|> put_resp_content_type ("text/plain")|> send_resp (200, "Hello world")
endend
As part of the experiment, we introduced a new Elixir module, ExAgent.Web,
that takes an agent and creates a web interface for it. ExAgent.Web does not
require any change to be done on the agent code for it to function. In order
to illustrate how ExAgent.Web works, let’s consider an example of adding a web
interface to a CarDealer agent that is responsible for holding a list of cars and
returning cars queries. The following listing shows the agent definition code for
CarDealer agent.
82
Listing 6.11: ”ExAgent CarDealer agent definition”
use ExAgent
defagent CarDealer domessage (:inform , Sender , new_car(Name , Cost , Color)) do
+car(Name , Cost , Color)end
message (:request , Sender , get_cars ()) doall(car(Name , Cost , Color), Res)&send(Sender , :inform , {Res})
end
message (:request , Sender , get_cars(Name)when car(Name , Cost , Color )) do
all(car(Name , Cost , Color), Res)&send(Sender , :inform , {Res})
end
message (:request , Sender , get_cars(Color)when car(Name , Cost , Color )) do
&send(Sender , :inform , {Name , Cost , Color })end
message (:request , Sender , get_cars(Cost)when car(Name , Cost , Color )) do
&send(Sender , :inform , {Name , Cost , Color })end
message (:request , Sender , get_cars(Name , Cost , Color)when car(Name , Cost , Color )) do
&send(Sender , :inform , {Name , Cost , Color })end
end
CarDealer defines six message handlers:
• new_car(Car, Cost, Color) message receives the car name, cost, and color
in the payload. These parameters are used to add a new car to the CarDealer
agent beliefs.
• get_cars message takes no parameters and returns all the cars that are
currently held in the CarDealer belief base. After fetching the cars they are
sent back to the message caller with &send(Sender, :inform, Res).
83
• get_cars(Name) message takes the car name and returns a car with
a matching name. The fetched car is sent back to the caller with
&send(Sender, :inform, Res).
• get_cars(Color) message returns a car with a color matching the color sent
in the message payload. As with the previous two messages. The result is
sent back to the caller.
• get_cars(Cost) message returns a car with the cost matching the cost
passed. The result is sent back to the caller.
• get_cars(Name, Color, Cost) message searches for a car that exactly
matches the parameters sent in the message content. The result is sent
back to the caller.
To create a web interface for CarDealer we call webify macro that is provided
by ExAgent.Web module. The call to the macro expands to a sequence of Elixir
Plug invocations that define a web server interface for the passed agent.
Before explaining how ExAgent.Web works, consider the following listing that
shows how to use ExAgent.Web to define a web server for CarDealer agent.
Listing 6.12: ”ExAgent using ExAgent.Web.webify method”
use ExAgent.Web
defmodule WebServer dowebify(CarDealer , "car_dealer ")
end
ExAgent.Web.webify takes the agent module and name as parameters. It
reads through the agent definition code and finds the message handlers the agent
defines. For each of the message handlers, ExAgent.Web creates an API endpoint
that returns a JSON response. For CarDealer ExAgent.Web defines the following
HTTP API endpoints.
84
• /car_dealer/new_car endpoint takes three get URL parameters; name,
cost and color. It then sends a message to CarDealer agent to add a
car to its beliefs.
• /car_dealer/get_cars endpoint takes no parameters and returns a JSON
list of all the cars that are currently held by the agent.
• /car_dealer/get_cars_with_name endpoint takes name as a get URL
parameter and searches for any car with that name.
• /car_dealer/get_cars_with_color endpoint takes color as a get URL
parameter and searches for any car with that color.
• /car_dealer/get_cars_with_cost endpoint takes cost as a get URL
parameter and searches for any car with that cost.
• /car_dealer/get_cars_with_name_cost_color endpoint takes three get
parameters; car, cost and color and searches for a car that matches those
parameters.
In addition, ExAgent.Web creates a metadata JSON endpoint that returns a
JSON description of all the endpoints CarDealer responds to. The path for this
metadata endpoint is /car_dealer/__all__. Following is a listing that shows the
JSON output of this endpoint.
85
Listing 6.13: ”ExAgent.Web JSON metadata API endpoint”
{"name": "car_dealer","module ": "Elixir.CarDealer","api": [
{"params ": [
"name","cost","color"
],"endpoint ": "/ car_dealer/new_car"
},{
"params ": [],"endpoint ": "/ car_dealer/get_cars"
},{
"params ": ["name"
],"endpoint ": "/ car_dealer/get_cars_with_name"
},{
"params ": ["color"
],"endpoint ": "/ car_dealer/get_cars_with_color"
},{
"params ": ["cost"
],"endpoint ": "/ car_dealer/get_cars_with_cost"
},{
"params ": ["name","cost","color"
],"endpoint ":"/ car_dealer/get_cars_with_name_cost_color"
}]
}
86
To provide a way to inspect the generated web server, we deployed the server
to http://107.170.17.86:9090. For example, to get the metadata information
we can navigate to http://107.170.17.86:9090/car_dealer/__all__ and for
listing all the cars we can head to http://107.170.17.86:9090/car_dealer/
get_cars.
The JSON API server generated by ExAgent.Web is controlled by CarDealer
message handlers;
• The message’s performative defines whether ExAgent.Web waits for a
response or not. :inform performatives are considered fire-and-forget, while
with :request performatives, ExAgent.Web will expect a response back from
CarDealer. This response is in the form of a send message that is sent back
to the receiver. In this case ExAgent.Web will wait for CarDealer to inform
it about the request’s response.
• The message content’s payload defines the API endpoint path and the get
URL parameters; the API generated for a message handler is in the form of
”agent name/MessagePayload(list of parameters)?values for each parameter”.
As an example, if the message payload is ”Message(Param1, Param2)” the
generated endpoint looks like
”agent name/message with param1 param2?param1=Val1¶m2=Val2”.
In order to provide a more visual example, we created a website that interacts
with the CarDealer agent. The website is a thin client that consumes the
JSON API generated with ExAgent.Web and consists of three pages; 1) The
add a car page consists of an HTML form to submit a new car. It can
be reached by visiting http://107.170.17.86:9090/add-car. 2) List all cars
page that displays all the cars added to the agent. This page is located at
http://107.170.17.86:9090/show-cars. And 3) Show a specific car page
which can be visited by clicking on a car from the car listing page or by
filling the following URL with the correct parameters http://107.170.17.86:
9090/show-car?name=BMW&cost=cost&color=the-color.
87
It is important to note that ExAgent.Web is a generic Elixir library and will
work with any agent that defines message handlers. It only requires a small
modification to the agent code; namely, the agent message handler has to be
updated with a send call to provide a response back to ExAgent.Web. This
response is converted to JSON and returned as the HTTP response for the current
request.
By providing a quick and easy way to create a web interface for agents we hope
to expand the way agents are used in real-life production applications.
Finally, ExAgent.Web is still a work in progress and is provided as an external
module library that can be found at https://github.com/nsomar/ex_agent_
web_server and is not yet packaged inside ExAgent main repository.
88
CHAPTER
SEVEN
CONCLUSIONS AND FUTURE WORK
7.1 Future work
ExAgent is still in its early development stages. We think that there are many
areas of improvement and research.
• Improve the performance of ExAgent reasoner and unifier. That requires
to spend time profiling ExAgent reasoning cycle to discover the bottlenecks
that cause the performance degradation.
• At the moment, the communication between agents in ExAgent is not
structured and does not follow any specification. We want to formalise the
agent communication to follow the FIPA agent communication specification.
• One of the reasons why we chose Elixir to build ExAgent and not plain
Erlang is that Elixir contains a number of practical projects that we wanted
to test ExAgent on. Namely, Phoenix, which is a highly performant web
server, and Nerves, which is a project that allows Elixir project to run on
embedded systems. We want to experiment and research on ways of using
ExAgent AOP code to help solve some of the problems in those two domains.
• In this dissertation, we stated that ExAgent would be able to interact with
EIS-enabled environments as part of the objectives. Being a Java library,
89
EIS posed a language barrier when interacting with Elixir and ExAgent. We
successfully interacted with EIS using the Erlang-Java bridge jinterface
[52]. The initial implementation is reported publicly on our GitHub repo
[53]. Due to time limitations, we were not able to finalise the ExAgent-EIS
integration and decided to keep it out of the scope for this dissertation.
In future research, we want to finalise this functionality and complete the
EIS integration. By doing that, ExAgent will be able to benefit from the
ever-growing list of EIS-enabled domains and applications.
• Finally, we want to continue working on and improving ExAgent.Web project.
Having an easy way to generate an HTTP interface for agents will open new
doors and provide new use cases for using ExAgent and AOP.
7.2 Conclusion and final remarks
As part of the dissertation, we reviewed some of the most popular AOP languages
and framework. Most of these languages are built with Java which is an object
oriented language. We started with a believe that other programming languages,
namely those that provide the actor model of computation are more fit to be
used to develop AOP languages. To strengthen that sentiment, we provided a
comparison between actors and agents, which are the building blocks of actor-
oriented language and agent oriented languages respectively. In addition, in
order to justify our decision to choose Erlang and Elixir as the base languages,
we provided a section that describes the benefits of choosing them for agent
development.
We then presented ExAgent: an agent programming language written in Elixir
that works on the Erlang’s BEAM VM. ExAgent was built using Elixir procedural
macros, which are a special family of macros that receive the abstract syntax tree
(AST) sent to them, process it, and return a new AST representing the final code
that the compiler will compile. This kind of macros is considered a powerful tool
to prototype and create domain specific languages that can be used with normal
Elixir code.
90
ExAgent AOP language was developed to be used by Elixir and Erlang
developers as a starter language to experiment the agent programming model.
For that reason, the first version of ExAgent presented in this dissertation has the
following features and characteristics.
• It implements the BDI agent mental model architecture.
• It has a reasoner architecture based on the PRS reasoning model.
• It can be imported in any Elixir project and can be adopted in the project
gradually.
• It has a syntax that is familiar to Elixir and Erlang developer. Most of
ExAgent’s keywords are similar to Elixir’s ones. Such as; defagent and
defrole which are inline with Elixir’s defmodule and def keywords.
• It can interoperate with Erlang and Elixir code without the need to wrap
the code in ExAgent specific syntax and wrappers.
• It has a special syntax to define roles that handle incoming messages and a
special syntax to define recovery plan handlers.
We finally evaluated ExAgent against JASON. JASON was chosen as it’s one
of the most popular AOP languages that is a subject of continuous research
and development. The evaluation was divided into three parts; performance
evaluation, feature set evaluation, and practical evaluation.
In the performance evaluation, we noticed that JASON reasoner is more
performant than ExAgent’s. We think the difference in performance is due to some
parts of ExAgent’s reasoner and unifier were developed in a way that are easier
to understand and read and performance was not an area we focused on. We also
noticed that ExAgent performed better than JASON in creating and maintaining
more agents; JASON was limited to less than 10000 agents, while ExAgent was
able to start up to 100000 on the same machine. Due to these results, we think
that with more profiling and refactoring we can improve the reasoner performance
and speed it up to match JAONS’s.
91
In the feature set evaluation, we compared some of the features offered by
ExAgent to JASON features; such as agent languages interoperability, plan
recovery and error handling, and code sharing and reusability. We think that these
three areas make ExAgent easy to adopt by new developers. First, any agent code
written in ExAgent has full access to the Elixir and Erlang code in the program. In
contrast, accessing Java code from JASON requires that code be wrapped and the
project to be restructured. Second, having explicit recovery plan handlers in the
agent code will make it easier for developers to reason about their agent programs.
That will help them write more resilient and maintainable agent software. Finally,
ExAgent allows developers to write roles that encapsulate a common responsibility
or task. That will not only help to build higher abstractions in the agent program,
but it also opens the door to create a global library of common agent roles and
responsibilities that can be shared between ExAgent developers.
For the practical evaluation, we presented ExAgent.Web which is an ex-
perimental, work-in-progress library that automatically generates a JSON API
web interface for ExAgent. ExAgent.Web provides an easy way to expose the
functionalities of an agent as an HTTP API endpoint. While it’s still an early
project, we think that ExAgent.Web will prove to be an important feature for
ExAgent in the future.
We ultimately think that ExAgent has enough high-level and low-level features
and capabilities to be useful as a starter AOP language to be used by Elixir and
Erlang developers. We also think that allowing ExAgent programs to interoperate
easily with Elixir and Erlang code lowers the adoption cost and makes ExAgent
an even more approachable AOP language.
92
References
93
REFERENCES
[1] Yoav Shoham, “Agent-oriented programming”, Artificial intelligence, vol.
60, no. 1, pp. 51–92, 1993. 1, 7, 9, 35
[2] Rafael H Bordini, Lars Braubach, Mehdi Dastani, A El F Seghrouchni, Jorge J
Gomez-Sanz, Joao Leite, Gregory O’Hare, Alexander Pokahr, and Alessandro
Ricci, “A survey of programming languages and platforms for multi-agent
systems”, Informatica, vol. 30, no. 1, 2006. 1, 2, 11
[3] Antonella Di Stefano and Corrado Santoro, “Designing collaborative agents
with exat”, in Enabling Technologies: Infrastructure for Collaborative
Enterprises, 2004. WET ICE 2004. 13th IEEE International Workshops on.
IEEE, 2004, pp. 15–20. 2, 16, 32
[4] Dinh Doan Van Bien, “Agentspotter: a mas profiling system for agent
factory”, 2008. 2
[5] Jason language, ”, 2017. 2, 13
[6] JAVA Agent DEvelopment Framework, ”, 2017. 2, 14
[7] Dejan Mitrovic, Mirjana Ivanovic, and Zoran Budimac, “Erlang and scala for
agent development”, 2013. 2, 19, 34
[8] Corrado Santoro, “An erlang framework for autonomous mobile robots”, in
Proceedings of the 2007 SIGPLAN workshop on ERLANG Workshop. ACM,
2007, pp. 85–92. 2, 30
94
[9] Erlang language, ”, 2017. 2
[10] Elixir language, ”, 2017. 2, 21
[11] Stuart Russell, Peter Norvig, and Artificial Intelligence, “A modern
approach”, Artificial Intelligence. Prentice-Hall, Egnlewood Cliffs, vol. 25,
pp. 27, 1995. 6
[12] Michael Wooldridge and Nicholas R Jennings, “Intelligent agents: Theory
and practice”, The knowledge engineering review, vol. 10, no. 02, pp. 115–
152, 1995. 7, 8
[13] What Is An Agent?, ”, 2017. 7
[14] Daniel Clement Dennett, The intentional stance, MIT press, 1989. 8
[15] Michael E Bratman, David J Israel, and Martha E Pollack, “Plans and
resource-bounded practical reasoning”, Computational intelligence, vol. 4,
no. 3, pp. 349–355, 1988. 9
[16] Anand S Rao, Michael P Georgeff, et al., “Bdi agents: From theory to
practice.”, in ICMAS, 1995, vol. 95, pp. 312–319. 9, 35
[17] Anand S Rao and Michael P Georgeff, “Decision procedures for bdi logics”,
Journal of logic and computation, vol. 8, no. 3, pp. 293–343, 1998. 9, 35
[18] Michael P Georgeff and Amy L Lansky, “Reactive reasoning and planning.”,
in AAAI, 1987, vol. 87, pp. 677–682. 10, 36
[19] Nils J. Nilsson, “Teleo-reactive programs for agent control”, JAIR, vol. 1,
pp. 139–158, 1994. 10, 36
[20] Anand S Rao, “Agentspeak (l): Bdi agents speak out in a logical computable
language”, in European Workshop on Modelling Autonomous Agents in a
Multi-Agent World. Springer, 1996, pp. 42–55. 11, 12
[21] Yoav Shoham, “Agent0: A simple agent language and its interpreter.”, in
AAAI, 1991, vol. 91, p. 704. 11
95
[22] Rafael H Bordini, Jomi Fred Hubner, and Michael Wooldridge, Programming
multi-agent systems in AgentSpeak using Jason, vol. 8, John Wiley & Sons,
2007. 11, 13
[23] ASTRA language, ”, 2017. 11, 13, 37
[24] Koen V Hindriks, Frank S De Boer, Wiebe Van Der Hoek, and John-Jules Ch
Meyer, “Agent programming with declarative goals”, in International
Workshop on Agent Theories, Architectures, and Languages. Springer, 2000,
pp. 228–243. 12
[25] Rafael H Bordini and Jomi F Hubner, “A java-based interpreter for an
extended version of agentspeak”, University of Durham, Universidade
Regional de Blumenau, 2007. 13
[26] Rem W Collier, Sean Russell, and David Lillis, “Reflecting on agent
programming with agentspeak (l)”, in International Conference on Principles
and Practice of Multi-Agent Systems. Springer, 2015, pp. 351–366. 14
[27] Fabio Bellifemine, Federico Bergenti, Giovanni Caire, and Agostino Poggi,
“Jadea java agent development framework”, in Multi-Agent Programming,
pp. 125–147. Springer, 2005. 14
[28] FIPA ACL, ”, 2017. 14
[29] Alexander Pokahr, Lars Braubach, and Winfried Lamersdorf, “Jadex: A bdi
reasoning engine”, in Multi-agent programming, pp. 149–174. Springer, 2005.
15
[30] Aaron Helsinger, Michael Thome, and Todd Wright, “Cougaar: a scalable,
distributed multi-agent architecture”, in Systems, Man and Cybernetics, 2004
IEEE International Conference on. IEEE, 2004, vol. 2, pp. 1910–1917. 15
[31] Joe Armstrong, “The development of erlang”, in ACM SIGPLAN Notices.
ACM, 1997, vol. 32, pp. 196–203. 17, 19
96
[32] Carl Hewitt, Peter Bishop, and Richard Steiger, “A universal modular actor
formalism for artificial intelligence”, in Proceedings of the 3rd international
joint conference on Artificial intelligence. Morgan Kaufmann Publishers Inc.,
1973, pp. 235–245. 17
[33] Alessandro Ricci and Andrea Santi, “From actors and concurrent objects to
agent-oriented programming in simpal”, in Concurrent Objects and Beyond,
pp. 408–445. Springer, 2014. 19, 34
[34] Alvaro Fernandez Dıaz, Clara Benac Earle, and Lars-Ake Fredlund, “Erlang
as an implementation platform for bdi languages”, in Proceedings of the
eleventh ACM SIGPLAN workshop on Erlang workshop. ACM, 2012, pp. 1–
10. 19, 34
[35] Alvaro Fernandez Dıaz, Clara Benac Earle, and Lars-Ake Fredlund, “ejason:
an implementation of jason in erlang”, in International Workshop on
Programming Multi-Agent Systems. Springer, 2012, pp. 1–16. 19, 32, 34
[36] Machado Pereira, “Tardis: A proposal for an agent-oriented program
language based on scheme”, 2010. 19, 34
[37] Amara Keller, Martin Kelly, and Aaron Todd, “Casesim usability and an
actor-based framework for multi-agent system simulations”, 2010. 19, 34
[38] Dejan Mitrovic, Mirjana Ivanovic, and Zoran Budimac, “Towards an
agent-oriented programming language based on scala”, in NUMERICAL
ANALYSIS AND APPLIED MATHEMATICS ICNAAM 2012: International
Conference of Numerical Analysis and Applied Mathematics. AIP Publishing,
2012, vol. 1479, pp. 478–481. 19, 31, 34
[39] Joe Armstrong, “A history of erlang”, in Proceedings of the third ACM
SIGPLAN conference on History of programming languages. ACM, 2007, pp.
6–1. 19
[40] Haakan Mattsson, Hans Nilsson, and Claes Wikstrom, “Mnesiaa distributed
robust dbms for telecommunications applications”, in International
97
Symposium on Practical Aspects of Declarative Languages. Springer, 1999,
pp. 152–163. 21
[41] Phoenix Framework, ”, 2017. 22
[42] Nerves Project, ”, 2017. 22
[43] Carlos Varela, Carlos Abalde, Laura Castro, and Jose Gulias, “On modelling
agent systems with erlang”, in Proceedings of the 2004 ACM SIGPLAN
workshop on Erlang. ACM, 2004, pp. 65–70. 31
[44] Antonella Di Stefano, Francesca Gangemi, and Corrado Santoro, “Eresye:
artificial intelligence in erlang programs”, in Proceedings of the 2005 ACM
SIGPLAN workshop on Erlang. ACM, 2005, pp. 62–71. 31
[45] Antonella Di Stefano and Corrado Santoro, “Supporting agent development
in erlang through the exat platform”, in Software Agent-Based Applications,
Platforms and Development Kits, pp. 47–71. Springer, 2005. 32
[46] EIS github repo, ”, 2017. 34
[47] eXAT github repo, ”, 2017. 34
[48] eJason github repo, ”, 2017. 34
[49] Michael Wooldridge, Nicholas R Jennings, and David Kinny, “The gaia
methodology for agent-oriented analysis and design”, Autonomous Agents
and multi-agent systems, vol. 3, no. 3, pp. 285–312, 2000. 77
[50] Contract net protocol, ”, 2017. 81
[51] Elixir Plug, ”, 2017. 82
[52] The Jinterface Package, ”, 2017. 90
[53] Java Erlang Integration using JInterface (OtpErlang), ”, 2017. 90
98
Appendix
99
APPENDIX
A
BUILDING EXAGENT FROM SOURCE
This section describes the steps needed to install ExAgent on your machine. Since
ExAgent is built using Elixir it has the following requirements:
• Elixir 1.2 and up
• Erlang/OTP version 19 and up
To install Elixir and Erlang, follow the instructions on Elixir website
http://elixir-lang.org/install.html. Since Elixir also depends on Erlang.
Getting Elixir 1.2 on your machine should be enough to start using ExAgent.
ExAgent source code is hosted on GitHub under the MIT code sharing licence.
To clone it on your machine head to https://github.com/nsomar/ex_agent and
perform a git clone
git clone https :// github.com/nsomar/ex_agent
Now that you have the complete source code, fetch the dependencies using
mix.
mix deps.get
After the dependencies are fetched successfully, you can run ExAgent tests
using the following command.
100
mix test
If everything is successful you should see an output that ends with the
following.
................
Finished in 7.9 seconds311 tests , 0 failures
ExAgent comes with three sample projects:
• Counter Agent https://github.com/nsomar/ex_agent_counter
• Ping Pong Agent https://github.com/nsomar/ex_agent_ping_pong
• Car Dealer agent web server https://github.com/nsomar/ex_agent_web_
server
A.1 Counter Agent
To run the counter agent execute the following commands.
git clone https :// github.com/nsomar/ex_agent_countermix deps.getmix compilemix run_agent
You should see an output similar to:
101
Counter Value 10Counter Value 9Counter Value 8Counter Value 7Counter Value 6Counter Value 5Counter Value 4Counter Value 3Counter Value 2Counter Value 1Done!** (EXIT from #PID <0.70.0 >) 0
A.2 Ping Pong Agent
Similarly, to get the ping pong agent execute the following commands.
git clone https :// github.com/nsomar/ex_agent_ping_pongmix deps.getmix compilemix run_agent
You should see an output similar to:
PINGPONGPINGPONGPINGPONGPINGPONG
102
APPENDIX
B
CREATING AN EXAGENT COUNTER AGENT
The following section describes the steps needed to create and run an ExAgent
counter agent from scratch. Check appendix A to see how to setup your machine
to run Elixir projects correctly.
First, we need to create an empty Elixir project. We will use mix to create
it.
mix new counter_agent
This command creates a new directory in the current directory. To proceed
we need to cd into the directory.
cd counter_agent
Next, we need to add ExAgent as a dependency to our created project. To do
that we alter mix.exs to add the dependency. (- are the lines we removed, + are
the lines we added).
defp deps do- []+ [{: exagent , git:+ "[email protected]:nsomar/ex_agent.git",+ branch: "master "}]
end
Next, we need to fetch the dependency by running the following command.
103
mix deps.get
After fetching ExAgent, we can proceed at writing our first agent. Open
lib/counter_agent.ex and replace its contents with the following.
use ExAgent
defagent CounterAgent do
initialize do+counter (10)!count
end
rule (+! count when counter (0)) do&print ("Done !")&exit
end
rule (+! count when counter(X)) do-+counter(X - 1)&print (" Counter Value #{X}")!count
end
end
The above defines a counter agent. In order to run it, we need to create a new
mix task. Append the following to the end of lib/counter_agent.ex.
defmodule Mix.Tasks.RunAgent douse Mix.Task
def run(_) doExAgent.start_agent(CounterAgent , "agent1 ")Process.sleep (10000)
endend
Finally, to run the agent execute mix run_agent from a terminal window. The
following output should be printed to the console.
104
Counter Value 10Counter Value 9Counter Value 8Counter Value 7Counter Value 6Counter Value 5Counter Value 4Counter Value 3Counter Value 2Counter Value 1Done!** (EXIT from #PID <0.70.0 >) 0
The full source code for this example can be found on GitHub https:
//github.com/nsomar/ex_agent_counter
105