9 JASON: AMBIENTE DE DESARROLLO . .

Anuncio
9
JASON: AMBIENTE DE DESARROLLO
Jason [3, 5, 44, 6] es una implementación en Java de AgentSpeak(L) con su semántica
operacional completa y extendida. Las extensiones incluyen comunicación basada en
actos de habla [44] y herramientas para simulación social [7]. En este capítulo introduciremos el ambiente de desarrollo en torno a Jason y ejemplificaremos su uso con
un caso de estudio que se incluye en la distribución de este lenguaje. El lenguaje se
encuentra disponible con sus fuentes abiertas bajo licencia GNU LGPL.
�.�
�����������
�.�
�������� �� ��������� ������ �� �����
�.�
�������������� �� ������ ���������
Jason puede descargarse desde su página principal en sourceforge1 , donde además
encontrarán una descripción del lenguaje, documentación, ejemplos, demos y proyectos relacionados. Tambien hay una liga a la página del libro2 Programando Sistemas
Multi-Agentes en AgentSpeak(L) usando Jason, la mejor fuente documental de este
lenguaje.
Al descargar el archivo de instalación, obtendrán un directorio como el mostrado
en la Figure ??. Lo importante es que el ejecutable, en este caso Jason.app esté en
algún sitio accesible. Por ejemplo, el folder completo Jason-1.3.2 está en el folder
de aplicaciones de mi MacBook Pro. Observen que el código fuente está disponible
en src y que los ejemplos y demos del sitio web del lenguaje están includos en los
folders examples y demos respectivamente. La documentación en doc incluye algunos
artículos relevantes y la descripción del API de Jason. Como se menciona en el README
es necesario tener instalado Java 1.5 para comenzar a trabajar.
Si ejecutan Jason.app tendrán acceso a la ventana principal de un ambiente de desarrollo basado en jEdit (Figure ??.
Los agentes están situados en su medio ambiente y los lenguajes de programación
orientados a agentes deberían proveer una noción explícita de éste. Aunque esto no
pareciera ser mandatorio en el caso de los agentes puramente comunicativos, recuerden que los actos de habla buscan ajustar el medio ambiente indirectamente a los
1 http://jason.sourceforge.net/JasonWebSite/Jason %20Home.app
2 http://jason.sourceforge.net/jBook/jBookWebSite/Home.php
117
118
�����: �������� �� ����������
Figura 9.1: El directorio principal de Jason.
Figura 9.2: La ventana principal de Jason.
�.� �������������� �� ������ ���������
Agent
Architecture
User
Environment
getPercepts
executeAction
change
percepts
Figura 9.3: Interacción entre la implementación del ambiente y la arquitectura del agente.
estados Intencionales del agente; y que las creencias son representaciones del agente
ajustadas al medio ambiente (ver Sección ??, pág. ??).
En el caso de agentes situados en medios ambientes reales, aunque la simulación
no es mandatoria, tiene algunas ventajas a saber: Los agentes y los Sistemas MultiAgentes son sistemas distribuidos de un alto grado de complejidad. Aunque existen
herramientas formales para la verificación de estos sistemas, la validación mediante
simulación sigue siendo una práctica muy extendida.
En todo caso, simulado o real, el acceso de Jason al medio ambiente se da a través
de Java. Y es muy probable que los programadores quieran tener un modelo Java
del mundo real. De esta forma, situar a los agentes en su medio ambiene real tras
las simulaciones, no es en extremo difícil gracias a que la arquitectura general de los
agentes es configurable. La idea es que los métodos que actuaban y percibían sobre
el ambiente simulado, sean substituidos por métodos que actuan y perciben en el
ambiente real.
�.�.� Soporte para la definición de ambientes simulados
Todo agente tendrá que interaccionar con su medio ambiente. La arquitectura general
de un agente incluirá los métodos Java que definen esta interacción, como se muestra
en diagrama de secuencia UML de la Figura 9.3.
La arquitectura existente de un agente utiliza el método getPercepts para recuperar, del ambiente simulado, las percepciones a las cuales el agente tiene acceso. Estas
pueden verse como propiedades del ambiente accesibles al agente. A partir de esta
información el agente actualiza sus creencias, normalmente cuando su ciclo de razonamiento está en el estado ProcMsg.
Ahora bien, cuando el agente ejecuta una acción a llevar a cabo una de sus intenciones, la arquitectura solicita al ambiente la ejecución de la acción y suspende la
intención asociada hasta que el ambiente provee retroalimentación sobre la ejecución
de la acción, normalmente, que la acción ha sido ejecutada. La verificación de si los
efectos esperados de la acción se cumplieron o no, está asociada normalmente a la
percepción y no a esta retroalimentación.
Observen que el ciclo del razonamiento del agente continua mientras la intención
asociada a la acción ejecutada está suspendida. Esto tiene un efecto similar a si el
119
120
�����: �������� �� ����������
Environment
- globalPercepts : List<Literal>
- agPercepts : Map<String,List<Literal>>
+ init(String[] args)
+ stop()
User Environment
+ getPercepts(String ag) : List<Literal>
+ executeAction(String ag, Structure action) : boolean
+ init(String[] args)
+ executeAction(String ag, Structure action) : boolean
+ addPercept(Literal p)
+ addPercept(String ag, Literal p)
+ removePercept(Literal p)
+ removePercept(String ag, Literal p)
+ clearPercepts()
+ clearPercepts(String ag)
Figura 9.4: Implementación de un ambiente extendiendo la clase Environment.
método executeAction fuese invocado de forma asíncrona. Si el ambiente está siendo
ejecutado en otra máquina, el lapso de esta suspensión puede ser considerable.
La clase Environment provista por Jason soporta la percepción individual de los
agentes, facilitando la tarea de asociar ciertas percepciones a ciertos agentes. La clase
mantiene estructuras de datos que almacenan las percepciones a las que cada agente tiene acceso, así como percepciones globales accesibles a todos los agentes en el
ambiente. El método getPercepts busca en estas estructuras, de modo que todas las
percepciones accesibles al agente que llama al método son recuperadas.
Para implementar un ambiente en Jason, el programador normalmente extiende la
clase Environment y sobre carga los métodos executeAction e init. La Figura 9.4
muestra un diagrama de clase mostrando esta relación. Una implementación de la
clase ambiente extendida suele tener la estructura mostrada en el Cuadro 7.
El método init puede usarse para recibir parámetros para la clase del ambiente.
También suele usarse para iniciar la lista de percepciones con aquellas propiedades del
ambiente que serán accesibles a los agentes cuando el sistema inicie su ejecución. Las
percepciones son más fáciles de crear con el método parseLiteral de la clase Literal,
pero el método preferido actualmente es usar createLiteral de la clase ASSyntax.
Es posible incluir literales positivas y negativas en la lista de percepciones de un
ambiente. Las percepciones negativas son literales sujetas al operador de negación
fuerte.
El Cuadro 8 resume los métodos de Java que pueden usarse para programar un
ambiente Jason. Solo objetos de la clase Literal, que es parte del paquete jason
pueden agregarse a las listas de percepciones mantenidas por la clase Environment.
En esta parte no debería considerarse agregar anotaciones a las literales, pues todas
son anotadas automáticamente con source(percept).
La mayor parte del código relacionado con la implementación de ambientes debe
ser referenciado en el cuerpo del método executeAction, que debe declararse tal
y como se muestra en el Cuadro 7. Siempre que un agente trata de ejecutar una
acción en el ambiente, el nombre del agente y una estructura representando la acción
solicitada son enviadas a este método como parámetros. El código en executeAction
suele verificar la estructura à la Prolog que representa la acción y el agente que intenta
ejecutar la acción. Luego, para cada combinación acción/agente que sea relevante,
el código hace lo necesario en el modelo del ambiente. Normalmente esto incluye
cambiar ciertas percepciones. Observen que la ejecución de una acción es booleana y
�.� �������������� �� ������ ���������
1
2
import jason.asSyntax.*;
import jason.environment.*;
3
4
public class <EnvironmentName> extends Environment {
5
// Los miembros de la clase necesarios...
6
7
@Override public void init(String[] args) {
// Inicializar percepciones globales ...
addPercept(Literal.parseLiteral("p(a)"));
// Si se usa la modalidad open-world, puede haber
// literales negadas, como ...
addPercept(Literal.parseLiteral("~q(b)"));
// Si lo que sigue solo lo percibe ag1
addPercept("ag1", Literal.parseLiteral("p(a)"));
}
8
9
10
11
12
13
14
15
16
17
@Override public void stop() {
// Cualquier cosa que se tenga que ejecutar en el ambiente
// cuando el sistema se detenga...
}
18
19
20
21
22
@Override public boolean executeAction(String ag, Structure act) {
// Este es el método más importante, donde los
// efetos de las acciones sobre las percepciones
// en el ambiente son definidas...
}
23
24
25
26
27
28
}
Cuadro 7: Implementación del ambiente del usuario.
Método
addPercept(L)
addPercept(A,L)
removePercept(L)
removePercept(A,L)
clearPercepts()
clearPercepts(A)
Semántica
Agrega la literal L a la lista global de percepciones.
Agrega la literal L a la lista de percepciones del agente A.
Remueve la literal L de la lista global de percepciones
Remueve la literal L de la lista de percepciones del agente A.
Borra las percepciones de la lista global.
Borra las percepciones del agente A.
Cuadro 8: Métodos Java para programar ambientes Jason.
121
122
�����: �������� �� ����������
regresa falso si la solicitud de ejecución al ambiente fallo. Un plan falla si alguna de
sus acciones falla al ser ejecutada.
La percepción y la actualización de creencias no son procesos equivalentes. Esta posible confusión es causa de algunos errores al implementar ambientes y su interacción
con los agentes mediante las clases y métodos definidos en Jason. Se suele esperar que
los agentes mantengan en su estado mental las percepciones aún cuando estás solo
estén presentes durante un ciclo de razonamiento. Esto es falso. Si un agente necesita
recordar percepciones pasadas que ya no se dan en el ambiente, es necesario que cree
notas mentales al percibir la propiedad en cuestión a través de sus planes. Las notas
mentales se recuerdan hasta que explícitamente son olvidadas. Las creencias asociadas a una percepción son eliminadas en cuanto la percepción se deja de observar en
el ambiente. También es posible que una percepción desaparezca como efecto de la
ejecución de una acción, antes de que el agente pueda formar una creencia acerca de
ella. Aunque consideren que el proceso de actualización de creencias genera eventos
asociados a agregar y borrar creencias.
Antes de pasar a un ejemplo de mayor complejidad, consideremos un caso sencillo
para ilustrar el uso de los métodos mencionados al implementar ambientes en Jason.
El ejemplo gira en torno a un agente aspiradora que puede moverse a su izquierda y
a su derecha, así como aspirar basura en cualquiera de las posiciones donde puede
estar (a la izquierda o a la derecha). De forma que las acciones del agente serán izq,
der y aspirar. Las percepciones del agente incluyen cuando su posición actual está
sucia y cual es su posición actual: pos(i) ó pos(d). Observen que hay cierto grado de
indeterminismo en este ambiente simple: sucia solo se percibe si la posición actual lo
está. El Cuadro 9 muestra el código parcial del ambiente en cuestión (no se consideran
aún las acciones del agente).
�.�.� Creación de Ambientes
Los agentes pueden estar situados en ambientes reales o simulados. En el primer caso, el usuario tendría que personalizar la “arquitectura de agente general”, como será
descrito en la siguiente subsección; mientras que en el último caso, debe proporcionar una implementación del ambiente simulado a través de una clase en Java, que
extiende la clase Environment. Como ejemplo, a continuación se presenta el ambiente
simulado para el escenario de los robots limpiadores que se presentó en la sección
??. Las líneas 1–5 importan los elementos de Jason necesarios. Observen que estamos especializando una clase de ambiente llamada grid. Las líneas 7–11 importan los
componentes de Java necesarios para la creación de nuestro ambiente. Observen que
es necesario declarar los términos y literales que usaremos al definir la clase (líneas
18–23). La definición de este ambiente sigue un esquema vista-modelo (líneas 27–28).
,
1
2
3
4
5
6
7
import
import
import
import
import
jason.asSyntax.∗;
jason.environment.Environment;
jason.environment.grid.GridWorldModel;
jason.environment.grid.GridWorldView;
jason.environment.grid.Location;
import java.awt.Color;
�.� �������������� �� ������ ���������
1
// Environment code for project aspiradora.mas2j
2
3
4
5
6
7
import
import
import
import
import
jason.asSyntax.*;
static jason.asSyntax.ASSyntax.*;
jason.environment.*;
java.util.logging.*;
java.util.*;
8
9
public class aspiradoraEnv extends Environment {
10
private Logger logger = Logger.getLogger("aspiradora.mas2j."+aspiradoraEnv.
class.getName());
11
12
// basura[0] es la localidad izq, basura[1] es la der
boolean[] basura = { true, true };
13
14
15
// la aspiradora está a la izquierda
char posAspiradora = 0;
16
17
18
Random r = new Random();
19
20
// Called before the MAS execution with the args informed in .mas2j
@Override public void init(String[] args) {
updatePercepts();
}
21
22
23
24
25
private void updatePercepts() {
// Agregar basura dinámicamente con 0.2 de probabilidad
if (r.nextInt(100) < 20) {
basura[r.nextInt(2)] = true;
}
clearPercepts();
if (basura[posAspiradora]) {
addPercept(Literal.parseLiteral("sucio"));
} else {
addPercept(Literal.parseLiteral("limpio"));
}
if (posAspiradora==0) {
addPercept(createLiteral("pos", createAtom("izq")));
} else {
addPercept(createLiteral("pos", createAtom("der")));
}
}
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
}
Cuadro 9: Actualización de creencias en el ejemplo de la aspiradora.
123
124
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
�����: �������� �� ����������
import
import
import
import
java.awt.Font;
java.awt.Graphics;
java.util.Random;
java.util.logging.Logger;
public class MarsEnv extends Environment {
public static final int GSize = 7; // grid size
public static final int GARB = 16; // garbage code in grid model
public
public
public
public
public
public
static
static
static
static
static
static
final
final
final
final
final
final
Term ns = Literal.parseLiteral("next(slot)");
Term pg = Literal.parseLiteral("pick(garb)");
Term dg = Literal.parseLiteral("drop(garb)");
Term bg = Literal.parseLiteral("burn(garb)");
Literal g1 = Literal.parseLiteral("garbage(r1)");
Literal g2 = Literal.parseLiteral("garbage(r2)");
static Logger logger = Logger.getLogger(MarsEnv.class.getName());
private MarsModel model;
private MarsView view;
@Override
public void init(String[] args) {
model = new MarsModel();
view = new MarsView(model);
model.setView(view);
updatePercepts();
}
@Override
public boolean executeAction(String ag, Structure action) {
logger.info(ag+" doing: "+ action);
try {
if (action.equals(ns)) {
model.nextSlot();
} else if (action.getFunctor().equals("move_towards")) {
int x = (int)((NumberTerm)action.getTerm(0)).solve();
int y = (int)((NumberTerm)action.getTerm(1)).solve();
model.moveTowards(x,y);
} else if (action.equals(pg)) {
model.pickGarb();
} else if (action.equals(dg)) {
model.dropGarb();
} else if (action.equals(bg)) {
model.burnGarb();
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
}
updatePercepts();
try {
Thread.sleep(200);
} catch (Exception e) {}
return true;
}
/** creates the agents perception based on the MarsModel */
void updatePercepts() {
clearPercepts();
Location r1Loc = model.getAgPos(0);
Location r2Loc = model.getAgPos(1);
74
75
Literal pos1 =
Literal.parseLiteral("pos(r1," + r1Loc.x + "," + r1Loc.y + ")");
Literal pos2 =
Literal.parseLiteral("pos(r2," + r2Loc.x + "," + r2Loc.y + ")");
76
77
78
79
80
addPercept(pos1);
addPercept(pos2);
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
}
if (model.hasObject(GARB, r1Loc)) {
addPercept(g1);
}
if (model.hasObject(GARB, r2Loc)) {
addPercept(g2);
}
class MarsModel extends GridWorldModel {
public static final int MErr = 2; // max error in pick garb
int nerr; // number of tries of pick garb
boolean r1HasGarb = false; // whether r1 is carrying garbage or not
Random random = new Random(System.currentTimeMillis());
private MarsModel() {
super(GSize, GSize, 2);
// initial location of agents
try {
setAgPos(0, 0, 0);
Location r2Loc = new Location(GSize/2, GSize/2);
setAgPos(1, r2Loc);
} catch (Exception e) {
e.printStackTrace();
}
// initial location of garbage
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
}
132
135
136
137
138
139
3, 0);
GSize-1, 0);
1, 2);
0, GSize-2);
GSize-1, GSize-1);
// finished searching the whole grid
131
134
add(GARB,
add(GARB,
add(GARB,
add(GARB,
add(GARB,
void nextSlot() throws Exception {
Location r1 = getAgPos(0);
r1.x++;
if (r1.x == getWidth()) {
r1.x = 0;
r1.y++;
}
130
133
�.� �������������� �� ������ ���������
}
if (r1.y == getHeight()) {
return;
}
setAgPos(0, r1);
setAgPos(1, getAgPos(1)); // just to draw it in the view
void moveTowards(int x, int y) throws Exception {
Location r1 = getAgPos(0);
if (r1.x < x)
r1.x++;
125
126
140
�����: �������� �� ����������
141
142
143
144
145
146
147
}
148
149
void pickGarb() {
150
151
// sometimes the "picking" action doesn’t work
// but never more than MErr times
153
154
if (random.nextBoolean() || nerr == MErr) {
remove(GARB, getAgPos(0));
nerr = 0;
r1HasGarb = true;
} else {
nerr++;
}
155
156
157
158
159
160
161
}
}
void dropGarb() {
if (r1HasGarb) {
r1HasGarb = false;
add(GARB, getAgPos(0));
}
}
void burnGarb() {
162
163
164
165
166
167
168
169
170
// r2 location has garbage
171
172
173
174
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// r1 location has garbage
if (model.hasObject(GARB, getAgPos(0))) {
152
175
else if (r1.x > x)
r1.x--;
if (r1.y < y)
r1.y++;
else if (r1.y > y)
r1.y--;
setAgPos(0, r1);
setAgPos(1, getAgPos(1)); // just to draw it in the view
}
}
if (model.hasObject(GARB, getAgPos(1))) {
remove(GARB, getAgPos(1));
}
class MarsView extends GridWorldView {
public MarsView(MarsModel model) {
super(model, "Mars World", 600);
defaultFont = new Font("Arial", Font.BOLD, 18);
// change default font
}
setVisible(true);
repaint();
/** draw application objects */
@Override
public void draw(Graphics g, int x, int y, int object) {
switch (object) {
case MarsEnv.GARB: drawGarb(g, x, y); break;
}
}
@Override
public void drawAgent(Graphics g, int x, int y, Color c, int id) {
String label = "R"+(id+1);
c = Color.blue;
if (id == 0) {
c = Color.yellow;
if (((MarsModel)model).r1HasGarb) {
label += " - G";
c = Color.orange;
}
�.� �������������� �� ������ ���������
206
207
208
209
210
211
212
213
}
214
215
216
217
218
219
220
221
222
223
}
}
}
super.drawAgent(g, x, y, c, -1);
if (id == 0) {
g.setColor(Color.black);
} else {
g.setColor(Color.white);
}
super.drawString(g, x, y, defaultFont, label);
127
public void drawGarb(Graphics g, int x, int y) {
super.drawObstacle(g, x, y);
g.setColor(Color.white);
drawString(g, x, y, defaultFont, "G");
}
Siempre que un agente intente ejecutar una acción básica (aquella que cambia el
estado del ambiente), el nombre de este agente y un Structure que representa la acción
elegida se enviarán como parámetros al método executeAction.
En el ejemplo, se puede apreciar que executeAction ejecuta las acciones básicas:
avanzar un paso (model.nextSlot()), moverse hacia donde se encuentra el otro agente
(model.moveTowards(x,y)), recoger la basura (model.pickGarb()), tirar la basura (model.dropGarb())
y quemar la basura (model.burnGarb()). Por último, este método actualiza las percepciones de los agentes (updatePercepts()), al indicarles su nueva localización (addPercept(pos1),
addPercept(pos2)), y si ésta tiene basura (addPercept(g1), addPercept(g2)).
Las dos clases que aparencen al final del ambiente, MarsModel y MarsView, se encargan de implementar las acciones básicas de los agentes y de recrear la animación
de este ambiente, respectivamente.
�.�.� Definición de Nuevas Acciones Internas
Las acciones internas permiten que los agentes AgentSpeak permanezcan en un nivel
de abstracción alto, además proporcionan una forma fácil de extender el código y de
usar código heredado. Las acciones internas que son distribuidas con Jason (como
parte de la librería estándar de acciones internas) comienzan con un ’.’. Las acciones
internas que son implementadas por los usuarios deben ser organizadas en librerías
específicas. En el programa AgentSpeak, la acción se accesa por el nombre de la librería, seguido de un ’.’ y luego por el nombre de la acción. Las librerías se definen como
paquetes en Java y cada acción en la librería debe ser una clase en Java, el paquete y
la clase se nombrarán de la misma forma como la librería y la acción serán referidas
en los programas AgentSpeak.
En el caso del ejemplo, las acciones internas se definen al extender la clase GridWorldModel
para definir la clase MarsModel (líneas 92–176).
Descargar