1. Agentes


1.1 Definición de agente JADEarriba.png


Programar un agente JADE consiste en:
  • Definir una clase Java que representa al agente (la cual debe heredar de la clase jade.core.Agent).
  • Implementar los comportamientos que va a manisfestar.

Un agente JADE cumple las siguientes características:
  • Tiene un nombre único en el entorno de ejecución.
  • Se implementa como un único hilo de ejecución (single-threaded).
  • Tiene un metodo de inicio (setup) y otro de fin (takeDown).
    • El método protegido setup() sirve para inicializar el agente incluyendo instrucciones que especificarán la ontología a utilizar y los comportamientos asociados al agente. Se invoca al comenzar la ejecución del agente.
    • El método protegido takeDown() sirve para liberar recursos antes de la eliminación del agente. Este método es invocado cuando se realiza una llamada al método doDelete(), que es el que realmente da por finalizada la ejecución del agente.
    • Ambos métodos deben ser sobreescritos.
  • En su implementación se define una clase interna por cada uno de los comportamientos asociados al agente. Estos comportamientos se utilizan básicamente para el envío y recepción de mensajes, aunque también se pueden utilizar para realizar otras tareas.
Podemos encontrar una descripción completa de la API de jade.core.Agent aquí.
import jade.core.Agent;
 
public class MiAgente extends Agent {
   protected void setup(){ // inicialización de MiAgente }
   protected void takeDown() { // liberación de recursos del agente }
}

1.1.1 Clase Agentarriba.png


La clase Agent:
  • Es una superclase común que permite a los usuarios crear software de agentes.
  • Suministra métodos que permiten ejecutar las tareas básicas de los agentes como:
    • Pasar mensajes utilizando objetos ACLMessage, con correspondencia de patrones.
    • Dar soporte al ciclo de vida de un agente.
    • Planificar y ejecutar múltiples actividades concurrentemente.
Los programadores de aplicaciones basadas en agentes deben escribir sus propios agentes como subclases de Agent, añadiendo tantos comportamientos específicos como sean necesarios y explotando las capacidades de la clase Agent.

1.2 Ciclo de vida de un agentearriba.png


Un agente está sujeto a un ciclo de vida en el que se definen los estados en los cuales se puede encontrar el agente, así como los cambios que se pueden realizar entre los diferentes estados.
El ciclo de vida de un agente JADE sigue el ciclo propuesto por FIPA, es decir, cumple con la propuesta del estándar de interoperabilidad entre agentes más aceptado.

1.2.1. Estados de un agentearriba.png


Un agente puede estar en los siguientes estados:
  • Iniciado: El objeto Agente está creado pero todavía no se ha registrado en el AMS, no tiene nombre ni dirección y tampoco se puede comunicar con otros agentes.
  • Activo: El Agente está registrado en el AMS, tiene un nombre, una dirección y puede acceder a todas las opciones de JADE.
  • Suspendido: El Agente está parado. Su hilo de ejecución está detenido y no ejecuta ningún Comportamiento.
  • En espera: El Agente está bloqueado esperando por algo. Su hilo de ejecución está dormido en un monitor de java y se despertará cuando se cumpla una cierta condición (cuando reciba un mensaje).
  • Desconocido: El Agente ha sido eliminado. El hilo de ejecución ha terminado y se ha eliminado del registro del AMS.
  • Tránsito: Un Agente móvil entra en este estado mientras está migrando a una nueva localización. El sistema sigue guardando los mensajes en el buffer hasta que el agente vuelve a estar activo.

1.2.2 Transiciones entre estados:arriba.png


Un agente puede cambiar de un estado a otro a través de transiciones. Estas transiciones pueden ser ejecutadas a través de métodos disponibles en la clase Agent y ser capturados por métodos que se pueden sobreescribir. Para saber en que estado se encuentra un agente se puede usar el método getAgentState( ) de la clase Agent que devuelve un objeto de la clase AgentState
public AgentState getAgentState()
Acción
Descripción
Método que realiza la acción
Método que captura la acción
Crear
Creación o instalación de un nuevo agente.
Constructor
setup()
Invocar
Invocación de un nuevo agente.


Suspender
Pone un agente en estado suspendido. Puede ser iniciado por el agente o por el AMS.
doSuspend()

Reanudar
Continúa con la ejecución de un agente que se encuentra en estado suspendido. Sólo puede ser iniciado por el AMS.


Esperar
Pone un agente en estado de espera. Sólo puede ser iniciado por el agente.
doWait()

Despertar
Continua con la ejecución de un agente que se encuentra en estado de espera. Sólo puede ser iniciado por el AMS.
doWake()

Mover
Pone un agente en otro contenedor cambiando su estado al de tránsito. Sólo puede ser iniciado por el agente.
doMove()
beforeMove() y afterMove()
Ejecutar
Continúa con la ejecución de un agente que se encuentra en estado de tránsito. Sólo puede ser iniciado por el AMS.


Destruir
La terminación normal o forzosa de un agente. Sólo puede ser iniciado por el AMS y no puede ser ignorado por el agente.
doDelete()
takeDown()

1.2.3 Visión gráficaarriba.png


DEagente.JPG

1.3 Creación de agentesarriba.png


En el momento de crearse un agente se realizan varias tareas de forma automática:
  1. Se llama al constructor del agente.
  2. Se crea un identificador del agente (AID).
  3. Se registra el agente en el AMS.
  4. Se ejecuta el método setup(), que debe contener únicamente el código relativo a las tareas de inicialización.

En el método setup() del agente además puede:
  • Modificar el registro del AMS.
  • Registrar el agente de forma explícita en el DF.
  • Añadir las tareas/comportamientos/behaviors que ejecutará el agente.
  • Etc.

El esqueleto de la creación de un agente es bastante simple. El siguiente código muestra un ejemplo de creación de un agente que lo único que hace es visualizar el texto "El agente se ha iniciado.".
// Esqueleto de un agente JADE
import jade.core.Agent;
 
public class MiAgente extends Agent {
    protected void setup() {
        System.out.println("El agente se ha iniciado.");
    }
}

1.3.1 Detalle de la creación del Agente BookBuyerAgent del ejemplo booktradingarriba.png


El código completo se encuentra en la ruta c:/jade/src/examples/booktrading/BookBuyerAgent.java
// . . . órdenes package e import
 
public class BookBuyerAgent extends Agent {
    private String targetBookTitle; // Almacena el Título del libro que se quiere comprar
    private AID[] sellerAgents;     // Almacena la lista de los agentes conocidos que son vendedores de libros
 
    protected void setup() {        // Código de incialización del agente
        // Escribe un mensaje de bienvenida
        // getName() obtiene el nombre completo del agente (GUID) y devuelve un string (ej.: peter@fipa.org:50)
        // getAID() obtiene el ID privado del agente (que será diferente en cada uno de los agentes registrados
        // en la plataforma). Devuelve un Agent ID completo (GUID, direcciones, decisiones)
 
        System.out.println("Hallo! Buyer-agent "+getAID().getName()+" is ready.");
 
        // . . .  resto del código de inicialización del agente
    } // Fin de setup()
    // . . . resto del código de la clase (otros métodos, comportamientos)
} // Fin de la clase BookBuyerAgent

1.3.2 Detalle de la creación del Agente BookSellerAgent del ejemplo booktradingarriba.png


El código completo se encuentra en la ruta c:/jade/src/examples/booktrading/BookSellerAgent.java
// . . . órdenes package e import
 
public class BookSellerAgent extends Agent {
  private Hashtable catalogue; // El catálogo de libros a vender (relaciona el título del libro con su precio)
  private BookSellerGui myGui; // La interfaz de usuario mediante la cual el usuario puede añadir libros al catálogo
 
  protected void setup() {           // Inicializa el agente
    catalogue = new Hashtable();     // Crea el catálogo
    myGui = new BookSellerGui(this); // Crea la interfaz de usuario
    myGui.show();                    // Muestra la interfaz de usuario
 
    // Registra el servicio "book-selling" (venta de libros) en las páginas amarillas
    DFAgentDescription dfd = new DFAgentDescription();
    dfd.setName(getAID());           // Establece un identificador para el agente (devuelto por getAID())
    ServiceDescription sd = new ServiceDescription();
    sd.setType("book-selling");      // Establece el tipo de servicio
    sd.setName("JADE-book-trading"); // Establece el nombre del servicio
    dfd.addServices(sd);             // Añade la descripción del servicio al agente
    try {
      DFService.register(this, dfd); // Intenta resgitrar al agente con la descripción dada anteriormente
    }
    catch (FIPAException fe) {       // Si se produce una excepción FIPA (NotUndestood, Failure, Refuse,...)
      fe.printStackTrace();          // Dirige la excepción a la salida estándar
    }
 
    //Añade el comportamiento de atender peticiones de los agentes compradores
    addBehaviour(new OfferRequestsServer());
 
    //Añade el comportamiento de atender peticiones de compra de los agentes compradores
    addBehaviour(new PurchaseOrdersServer());
  }
  // . . . Resto del código
}

1.4 Ejecución de agentesarriba.png


Hay dos formas de ejecutar un agente: desde el GUI de JADE y desde la línea de comandos.

1.4.1 Ejecución de un agente desde el GUI de JADEarriba.png


Para ejecutar el GUI de JADE:
  • Situarse en el directorio donde se encuentra el archivo de la clase del agente.
  • Ejecutar la orden java jade.Boot -gui desde la línea de comandos.

Una vez que está disponible el GUI, seguir los pasos mostrados en las siguientes ilustraciones.
RMA_1.png
RMA_2.png

1.4.2 Ejecución de un agente desde la línea de comandosarriba.png


Ejecutar la orden java jade.Boot -container <nombre_del_agente>:<ruta_al_agente.class> desde la línea de comandos.

1.4.3 Ejecución remota de agentesarriba.png


JADE es una plataforma que permite la ejecución de un sistema de agentes en el que éstos puedan estar repartidos entre diversos hosts. Para ejecutar un agente en remoto tendríamos que ejecutar la orden java jade.Boot -container -host nombreHost <nombre_del_agente>:<ruta_al_agente.class> desde la línea de comandos.

También es posible crear un RMA que ponga en contacto la plataforma local con la remota. Esto significa que desde el RMA de ambas plataformas podremos contemplar los cambios producidos en el sistema de agentes, e interactuar con dichos agentes. Para ello tenemos que ejecutar la orden java jade.Boot -container -host nombreHost RMA1:jade.tools.rma.rma desde la línea de comandos.

1.5 Terminar la ejecución de agentesarriba.png


Hay tres formas de terminar la ejecución de un agente: desde el GUI de JADE, mediante la llamada al método doDelete() dentro del código del agente y desde la línea de comandos en la ventana de la consola.

1.5.1 Terminar la ejecución de agentes desde el GUI de JADEarriba.png


Seleccionar la opción Kill del menú contextual tal y como se ve en la imagen.
RMA_3.png

1.5.1 Terminar la ejecución de agentes desde la ventana de la consolaarriba.png


Cerrando la ventana de la consola en la que se está ejecutando el agente o tecleando Ctrl + c cuando dicha ventana está activa.

1.5.2 Terminar la ejecución de agentes en el códigoarriba.png


Llamando al método doDelete() dentro del código del agente.

public void doDelete()
  • Es un método de la clase Agent que realiza una transición del los estados Activo, Suspendido o En espera al estado de Borrado destruyendo al agente.
  • Se suele usar para finalizar agentes que están en ejecución continua.
  • El método doDelete() llama automáticamente al método takeDown().
protected void takeDown()
  • Método que se invoca antes de que el agente termine su ejecución. Se usa normalmente para realizar tareas de "limpieza" como por ejemplo, desregistrar el agente en el DF. Es tarea del programador del agente el sobrescribir el método para proveer al agente de un comportamiento especial antes de su destrucción.

1.5.3.1 Detalle de la terminación del Agente BookBuyerAgent del ejemplo booktradingarriba.png


// . . . órdenes package e import
 
public class BookBuyerAgent extends Agent {
    // . . .
    protected void setup() {
        // . . .
        if (args != null . . .) {
           // . . .
        }
        else { // si no se ha introducido el argumento título del libro finaliza
            System.out.println("No se ha especificado el título del libro");
            doDelete();    // llamada al método para la destrucción del agente
        }
    }
 
    protected void takeDown() { //auí se definen las operaciones de limpieza
        System.out.println("Agente comprador "+getAID().getName()+" terminando."); // sólo realiza una despedida
    }
 
    // . . . resto del código
}

1.5.3.2 Detalle de la terminación del Agente BookSellerAgent del ejemplo booktradingarriba.png


// . . . órdenes package e import
 
public class BookSellerAgent extends Agent {
    // . . .
    protected vod setup() {
       // . . .
    }
 
    protected void takeDown() {       // Aquí se ponen las operaciones de limpieza de recursos
       try {
          DFService.deregister(this); // Intenta darse de baja del resgitro de las páginas amarillas
       }
       catch (FIPAException fe) {     // Captura la excepción FIPA
          fe.printStackTrace();       // Dirige la excepción al terminal de salida
       }
       myGui.dispose();               // Cierra la interfaz de usuario
       System.out.println("Seller-agent "+getAID().getName()+" terminating."); // Se despide
    }
 
    // . . . resto del código
}
 
// el vendedor no llama por sí solo al método doDelete ya que su comportamiento es cíclico
// la llamada a takeDown por parte del método doDelete sólo se produce si se cierra la interfaz o la consola
// Do Delete se utiliza en la clase BookSellerGui
 
// . . . órdenes package e import
 
public class BookSellerGui extends JFrame {
    private BookeSellerAgent myAgent;
    // . . .
    BookSellerGui(BookSellerAgent a) {                // constructor
       // . . .
       myAgent = a;
       // . . .
       addWindowListener (new WindowAdapter() {       // comportamiento de la ventana de interfaz
           public void windowClosing(WindowEvent e) { // recoge el evento de cerrar la ventana
               myAgent.doDelete();                    // llamada al método encargado de la limpieza
           }
       });
    }
    // . . .
}

1.6 Identificador de un agentearriba.png


Para la correcta comunicación, es necesario que cada agente esté identificado de forma única en la plataforma.
Cumpliendo las especificaciones FIPA, cada agente posee un Identificador de Agente (objeto de la clase jade.core.AID), que va a tener la siguiente estructura:
  • nombre_agente@host:puerto/JADE

donde:
  • nombre: nombre del agente.
  • host: máquina donde se está ejecutando.
  • puerto: por defecto es el 1099.

El método getAID() de la clase Agent devuelve el identificador del agente.
import jade.core.Agent;
import jade.core.AID;
 
public class MiAgente extends Agent {
    protected void setup() {
       System.out.println("Hola! El agente "+getAID().getName()+" está listo.");
    }
}
Conociendo el nombre del agente puede obtenerse su AID:
String Nombreagente = "ElAgente";
AID id = new AID(Nombreagente, AID.ISLOCALNAME);

1.7 Paso de argumentosarriba.png


Se le pueden pasar argumentos a un agente desde la línea de comandos y/o desde la interfaz gráfica.
Los argumentos se pueden recuperar usando el método getArguments() de la clase Agent que devuelve un array de objetos (java.lang.Object []).

1.7.1 Detalle de la obtención de los argumentos en el BookBuyerAgent del ejemplo booktradingarriba.png


// . . . órdenes package e import
public class BookBuyerAgent extends Agent {
    private String targetBookTitle;            // Título del libro que se quiere
    private AID[] sellerAgents;                // Lista de los agentes vendedores de libros conocidos
 
    protected void setup() {
       System.out.println("Hallo! Buyer-agent "+getAID().getName()+" is ready.");// Saludo comprador
 
       Object[] args = getArguments();         // Obtiene los argumentos dados en la inicialización del comprador
       if (args != null && args.length > 0) {  // Tiene que haber al menos un argumento
 
           targetBookTitle = (String) args[0]; // Obtiene el título del libro a comprar que se pasó como primer argumento
           System.out.println("Target book is "+targetBookTitle);  // visualiza el título que quiere comprar
 
           // . . . resto del código
 
       }   // cierre del if
   }       // cierre del setup
 
   // . . . resto del código
 
} Cierre de la clase BookBuyerAgent

1.7.2 Paso de argumentos en la línea de comandosarriba.png


Ejemplo con el agente BookBuyerAgent:
java jade.Boot -container agenteComprador:examples.bookTrading.BookBuyerAgent(libro1 "libro 2" libro3)
Nota: Como se puede observar cuando un parámetro es una cadena compuesta por varias palabras, éstas deben estar encerradas con comillas (" "). Además si se quieren incluir varios parámetros éstos deben separase mediante un espacio en blanco.

1.7.3 Paso de argumentos desde la interfaz gráficaarriba.png


Los parámetros se pueden indicar desde la interfaz gráfica en la ventana de creación de un nuevo agente. Cada parámetro se separa por un espacio en blanco. Si se quiere un parámetro con espacios en blanco debe de encerrarse entre comillas dobles, pero estas comillas dobles serán parte del parámetro recibido.
agent.JPG

1.8 Ejerciciosarriba.png


Práctica 1 – Introducción a un SMA en un entorno JADE

La finalidad de este ejercicio es aprender a crear un agente JADE y que este realice tareas.

Objetivo: crear 5 agentes, 4 de ellos con nombres de balnearios (ej. Arnoia) y nombre del servicio, lo que ofrecen (ejemplos: SPA, Alojamiento, Alojamiento_Y_SPA), el quinto agente, se corresponderá con un cliente que desea un listado de los servicios disponibles, (ej. El Balneario Arnoia , ofrece las siguientes posibilidades: Alojamiento_Y_SPA, …).

Para crear estos agentes, serán necesarias dos clases diferentes: una clase para los agentes que ofrecen servicios y otra clase para los agentes que solicitan servicios.

Los agentes que ofrecen servicios deberán realizar las siguientes tareas:
  • Mostrar por pantalla un saludo, su nombre, su AID y su estado.
  • Pasar como argumentos el tipo y nombre de servicio, capturar estos argumentos y asignárselos al servicio.
  • Registrar su servicio en las páginas amarillas.

El agente que solicita servicios deberá realizar las siguientes tareas:
  • Mostrar por pantalla un saludo, su nombre, su AID y su estado.
  • Buscar y listar los tipos y nombres de los servicios que ofrece un agente determinado y mostrar el AID del agente que ofrece ese servicio.

Finalmente los agentes se despiden y eliminan sus registros de las páginas amarillas.


Para realizar el ejercicio se deben seguir los siguientes pasos:
  • Crear una carpeta llamada "Ejercicio" en "C:\jade\src\examples\".
  • Copiar y pegar el código proporcionado en cada una de las plantillas en un nuevo fichero con nombre igual al de la clase (estos archivos se situarán en la carpeta creada anteriormente).
  • Completar el código del ejercicio sustituyendo los " ... " por lo que se considere adecuado.
  • Compilar el código desde una consola en el directorio especificado en el primer punto con la sentencia "javac *.java".
  • Abrir una nueva consola para ejecutar el RMA.
  • Lanzar desde consola los 5 agentes que se piden en el ejercicio.


Código para el agente que ofrece servicios (Servicios.java):


/*  Implementación del agente que se registrará con un
 *  servicio en las páginas amarillas.
 */
 
//Crear una carpeta dentro de examples (c:\jade\src\examples) y ubicar en ella todos los archivos de la práctica
 
package examples.Ejercicio;
 
import jade.core.Agent;
import jade.domain.df;
import jade.domain.FIPAAgentManagement.AMSAgentDescription;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
 
public class Servicios extends Agent {
    // INICIALIZACION DEL AGENTE
    protected void ...... () {
        /* Visualizar nombre, AID y estado del agente  */
        System.out.println("\n");
        System.out.println("El Agente ha sido inicializado. \n");
        System.out.println("Nombre completo del agente: " +  .........  +"\n"); //Obtener el Nombre Completo del Agente
        System.out.println("AID unico del agente: "+ .......... "\n");  // Obtener el AID del Agente
        System.out.println("Estado del agente: "+ ........ +"\n");  //Obtener el estado del Agente
        System.out.println("\n");
 
        /* Registrarse en las paginas amarillas */
 
        DFAgentDescription descripcion = new .......;
        ServiceDescription servicio = new ..........;
 
        // Capturar los parametros para asignarselos al tipo y nombre del servicio
 
        Object[] args = ........;
 
        // Se necesitan dos argumentos , el primero ser‡ el tipo de servicio y el segundo el nombre de dicho servicio
 
        if (args != null && args.length == 2) {
            String tipo = (String) args[0];
            String nombre = (String) args[1];
            ......;   //asignar tipo al servicio
            ......;  //asignar nombre al servicio
            descripcion.addServices(servicio);
            try {
 
                DFService.register(this, .....);// Registrar el agente y su descripcion en el DF
 
            }
 
            catch (Exception fe) {
                fe.printStackTrace();
            }
        }
        else{
 
            System.out.println("ERROR: Se necesitan dos 2 argumentos. Se Finaliza la Ejecuci—n.");
            ......;  // llamada al método para la destruccion del agente
        }
   }
 
 
    /* TERMINAR LA EJECUCION DEL AGENTE */
    // Este método libera los recursos del agente
 
    protected void takeDown() {
        System.out.println("Listado realizado Correctamente. El Agente Cliente "+getAID().getName()+" Termina.");
        try {
                DFService.deregister(....);     // Desregistrar al agente
 
        } catch (FIPAException fe) {
                fe.printStackTrace();
        }
    }
}
 

Código para el agente que solicita servicios (Cliente.java):

/*  Implementación del agente que buscará servicios
 *  en las páginas amarillas.
 */
 
 
//Crear una carpeta dentro de examples (c:\jade\src\examples) y ubicar en ella todos los archivos de la práctica
package examples.Ejercicio;
 
import jade.core.AID;
import jade.core.Agent;
import jade.domain.df;
import jade.domain.FIPAAgentManagement.AMSAgentDescription;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import java.util.Iterator;
 
public class Cliente extends Agent {
    private AID[] agentes; //Sera el array donde se guarden los agentes encontrados
 
    // INICIALIZACION DEL AGENTE
    protected void ..... () {
        /* Visualizar nombre, AID y estado del agente  */
        System.out.println("\n");
        System.out.println("El Agente ha sido inicializado. \n");
        System.out.println("Nombre completo del agente: " + .......+"\n"); //Obtener el Nombre Completo del Agente
        System.out.println("AID unico del agente: "+ ....... +"\n");  // Obtener el AID del Agente
        System.out.println("Estado del agente: "+ ........ +"\n");  //Obtener el estado del Agente
 
        System.out.println("\n");
 
 
        DFAgentDescription descripcion = new .......;
        ServiceDescription servicio = new .......;
        /* Quremos obtener todos los sercicios que hay en el DF,
         * asique se a–ade un servicio a la descripcion sin especificar nombre ni tipo  */
 
        descripcion.addServices(.....);//añadir el servicio a la descripcion
 
        try {
            // Obtener en un array las descripciones de todos los agentes registrados en el DF, cuyo
            // servicio coincida con el anteriormente creado (cualquier nombre, cualquier tipo de servicio).
            DFAgentDescription[] descripciones = DFService.search(this, descripcion);
            System.out.println("Los Agentes obtenidos son los siguientes: \n");
 
            //Obtener los AID de las descripciones
            agentes = new AID[descripciones.length];
            for (int i = 0; i < descripciones.length; ++i) {
                //getName() de la clase DFAgentDescription devuelve un AID
                agentes[i] = descripciones[i].getName();
                //getName() de la clase AID devuelve el nombre del agente
                System.out.println(agentes[i].getName() + " Ofrece los siguientes servicios: \n");
                Iterator<ServiceDescription> it = descripciones[i].getAllServices();
                while (it.hasNext()){
                    ServiceDescription s = it.next();
                    System.out.println(" - El Balneario " + s. ...... + ", ofrece las siguientes posibilidades: " + s. ....... + "\n");
                }
            }
        }
        catch (FIPAException fe) {
            fe.printStackTrace();
        }
 
        ........;  // Llamada al método para eliminar el agente
   }
 
 
 
 
    // Borrar el agente
    // Este método libera los recursos del agente
   protected void .... () {
        System.out.println("Listado realizado Correctamente. \n El Agente Cliente "+getAID(). ...... +" Termina. \n");
 
    }
}