Neste post será descrito como criar e manipular comportamentos, além de adicionar os mesmos aos agentes.

De uma forma geral, comportamento é uma forma de estruturar as tarefas do agente de uma maneira sistematizada. Trazendo para a realidade de negócio, esses serviriam como encapsuladores de funcionalidades que satisfazem propriedades de negócios. Um agente pode conter vários comportamentos que são executados de forma concorrente.

A execução de um agente é comporta de três níveis básicos:

  • Inicialização: Consiste na execução do método setup(), que é utilizado para inicializar dados, estruturar informações e demais recursos que serão manipulados pelo agente em toda a sua vida útil;
  • Realização de tarefas: Nesta etapa são executados os comportamentos do agente. Os comportamentos são organizados em duas filas: uma fila de comportamentos que estão em execução, e uma fila de comportamentos bloqueados que já foram finalizados;
  • Limpeza e finalização: Consiste na execução de métodos específicos para finalização do agente. Alguns métodos de finalização são utilizados pelo usuário e outros são privados do Middleware, ou seja, um programa de mediação entre software e demais aplicações.

Execução de Comportamentos

Os comportamentos de agentes são estruturados em duas filas: fila de comportamentos ativos e fila de comportamentos bloqueados. O gerenciamento destas filas é realizado de forma automatizada. Comportamentos de agentes devem ser compostos essencialmente de dois métodos:

  • action(): este método é utilizado para implementação do código referente ao comportamento em si do agente;
  • done(): método utilizado para devolver um valor booleano para indicar se o comportamento foi finalizado ou não.

No momento da execução de comportamentos, o escalonador chama o primeiro comportamento de fila de comportamentos ativos e executa seu método action(). Após executar este método, o escalonador verificar se o comportamento finalizou pela análise do método done(). Caso o comportamento ainda não tenha sido finalizado, este é interrompido e colocado para o final da fila de comportamentos ativos, enquanto o próximo comportamento na fila de comportamentos ativos é selecionado para ser executado.

Caso o comportamento seja finalizado, este é removido da lista de comportamentos ativos e enviado para a lista de comportamentos bloqueados.

A Figura 1 ilustra como está estruturado o ciclo de execução do agente. Conforme pode ser observado, o primeiro método a ser executado é o método setup() e após a execução deste, o escalonador de comportamentos verifica se o agente está em execução. Caso ainda esteja, ele pega o próximo comportamento ativo na lista e coloca em execução (b.action()). Depois o escalonador verifica se o comportamento foi executado completamente (b.done()): em caso negativo, o comportamento é suspenso e colocado no final da lista de comportamentos ativos, mas em caso afirmativo, o comportamento é movido para a lista de comportamentos bloqueados. Caso o agente não esteja mais em execução é chamado o método takeDown() para finalizar o agente.

Ilustração
do ciclo de execução de um agente

Figura 1. Ilustração do ciclo de execução de um agente

De forma prática para implementação, comportamento consiste de uma classe do pacote jade.core.behaviours.Behaviour. Assim, o usuário simplesmente necessita criar uma ou mais classes que descrevam tarefas que serão executadas pelo agente e importar a classe Behaviour para o código-fonte. Nas Listagens 1 e 2 são descritos exemplos de uma classe agente que adiciona e executa um comportamento e a uma classe que importa a classe comportamento, respectivamente.

Listagem 1. Agente com adição de comportamento.

import jade.core.Agent;
  import jade.core.behaviours.Behaviour;
   
  public class HelloWorldAgent extends Agent{
   
  protected void setup() {
   
  System.out.println("Olá mundo! Eu sou um agente!");
  System.out.println("Estou disparando um comportamento");
  addBehaviour(new MeuComportamento(this));               
   
   }
  }

O código fonte apresentado precisa da importação da classe Behaviour e dentro do corpo do método setup() chama-se o método addBehaviour(), passando como parâmetro o construtor ou classe de comportamento criada. É de extrema importância, no momento da execução de construtores, passar como parâmetro o agente para associar o comportamento a um.

O comportamento pode ser utilizado por vários agentes quanto forem necessários em sua aplicação. Vale salientar que o escalonador se encarrega de organizar os comportamentos em forma de lista.

Listagem 2. Ilustração de uma classe comportamento.

import jade.core.Agent;
  import jade.core.behaviours.Behaviour;
   
  public class MeuComportamento extends Behaviour{
   int i =0;
   public MeuComportamento(Agent a) {
    super(a);
   }
   
   public void action() {
    System.out.println("* Ola Mundo! ... Meu nome é " + myAgent.getLocalName( ));
          i=i +1;
    }
    public boolean done () {
     //Caso este método retorne TRUE o comportamento será finalizado
    return i >3;
   }
  }

Para a criação de uma classe de comportamento é necessário importar as classes Agent e Behaviour.

Existem duas formas de construtor de comportamento: construtor sem parâmetros e construtor com parâmetro do tipo agente. Conforme pode ser observado, o construtor da classe “MeuComportamento” recebe como parâmetro um tipo agente que é passado para o construtor pai da classe “MeuComportamento”.

Quando se passa um agente como parâmetro, permite-se manipular os métodos do agente dentro do comportamento. O método action() é utilizado para imprimir no console a mensagem “"* Ola Mundo! ... Meu nome é " + myAgent.getLocalName()”, onde o método “getLocalName()” imprime no console o nome do agente. A expressão “myAgent” é a forma obrigatória para se chamar o agente passado como parâmetro dentro do construtor para manipular o agente dentro do comportamento, além de ter um contador que é incrementado em mais um. O método done() é utilizado para retornar um valor booleano “true” quando a variável “i” for maior que três. Este exemplo de comportamento é utilizado para imprimir quatro vezes na tela do console o que está escrito na função println() dentro do método action().

A Figura 2 ilustra a execução do comportamento “MeuComportamento” utilizado para imprimir na tela “Olá Mundo” e o nome do Agente que é passado como parâmetro pelo construtor.

Ilustração da execução do
comportamento “MeuComportamento”

Figura 2. Ilustração da execução do comportamento “MeuComportamento”.

Bloqueando Comportamentos

Comportamentos também podem ser bloqueados pelo próprio programador manualmente pela implementação de estratégia de bloqueio no próprio código do comportamento. Isto garante que estes sejam utilizados para tratamento e/ou envolvendo eventos como, por exemplo, espera de mensagens, evento ocorrido em alguma interface gráfica, temporização e etc. Quando um comportamento é bloqueado manualmente é movido pelo escalonador de comportamentos da lista de ativos para a lista de comportamentos bloqueados, onde ficará até que algum evento externo ocorra e o comportamento saia da lista de comportamentos bloqueados e retorne para a lista de comportamentos ativos pelo escalonador.

De uma forma geral, um comportamento pode ser bloqueado para espera do acontecimento de algum evento ou por um determinado período de tempo. Vale salientar que o bloqueio de um comportamento não é igual ao método sleep() em uma thread, pois este para a execução da thread no momento em que é chamado, diferentemente do bloqueio de comportamento, que espera a finalização do método action() para bloquear o comportamento. Porém, a chamada de bloqueio pode ser realizada em qualquer corpo de código no método action().

Para fazer bloqueio de um comportamento basta chamar a função block() em qualquer lugar no corpo de código, podendo ser utilizado de duas formas: recebendo como parâmetro uma variável do tipo long ou sem passagem de parâmetros.

As Listagens 3 e 4 são corpo de código que descrevem um agente que possui um comportamento com bloqueio e uma classe de comportamento com bloqueio, respectivamente.

Listagem 3. Descrição de um agente que possui comportamento com bloqueio.


  import jade.core.Agent;
  import jade.core.behaviours.Behaviour;
  public class HelloWorldAgent extends Agent{
   
  protected void setup() {
   
  System.out.println("Olá mundo! Eu sou um agente!");
  System.out.println("Irei executar o meu comportamento com bloqueio");
  addBehaviour(new MeuComportamento(this,5000));               
          }
  }

A Listagem 3 descreve um agente que executa um comportamento que possui o método de bloqueio. Veja que são passados como parâmetros o próprio agente e um valor constante de “5000”.

Listagem 4. Comportamento com bloqueio.


  import jade.core.Agent;
  import jade.core.behaviours.Behaviour;
   
  public class MeuComportamento extends Behaviour{
    int numExecucao=1;
    long delay;
    long tempoInicial = System.currentTimeMillis();
      
    public MeuComportamento(Agent a, long delay) {
      super(a);
      this.delay = delay;
    }
   
    public void action() {
      block(delay);
      System.out.println("# Tempo " + (System.currentTimeMillis() - tempoInicial) + ": Meu nome é " + myAgent.getLocalName());
      numExecucao = numExecucao+1;
    }
   
    public boolean done () {
      return numExecucao>10;
    }
       
  }

A Listagem 4 ilustra o comportamento com bloqueio, pois possui um construtor que recebe como parâmetros um agente e uma variável do tipo long. O método System.currentTimeMillis() serve para capturar o tempo atual que geralmente está associado ao relógio do sistema operacional. O código é utilizado para capturar o tempo inicial em que o comportamento foi inicializado. O método action() é utilizado para imprimir a diferença entre o tempo atual e o tempo inicial do comportamento, o nome do agente e incrementar a variável “numexecucao” em mais um. O método done() é utilizado para finalizar o comportamento do agente quando o valor na variável “numexecucao” for maior que 10. De uma forma geral o objetivo deste comportamento é imprimir o tempo atual de execução e o nome do agente em intervalos de 5000 milissegundos.

A Figura 3 a seguir ilustra a execução na janela de console de um agente executando um comportamento com método de bloqueio. Conforme pode ser observado, a cada 5000 milissegundos (tempo estabelecido para o comportamento ficar bloqueado), o agente imprime o tempo atual e o seu nome até que ele tenha impresso esta informação dez vezes e depois finaliza.

Ilustração da execução do
comportamento com método de bloqueio

Figura 3. Ilustração da execução do comportamento com método de bloqueio.

Também é possível trabalhar com o controle de concorrência na execução de comportamentos dos agentes. A Listagem 5 ilustra um código de agente que simula controle de concorrência. Conforme observado são capturados parâmetros do agente, cujo primeiro parâmetro é convertido no tipo String e depois no tipo long para ser repassado como parâmetro para o construtor da classe “MeuComportamento”. Caso não seja passado nenhum parâmetro pelo agente, é impresso no console “Você não passou argumentos”. Para o código do comportamento basta simplesmente utilizar o código descrito na Listagem 4.

Listagem 5. Código de agente para controle de concorrência.

import jade.core.Agent;
  import jade.core.behaviours.Behaviour;
    public class HelloWorldAgent extends Agent{
    protected void setup() {
     Object[] args = getArguments();
     if(args != null && args.length>0){
   
      long valor = Long.parseLong((String) args[0]);
      System.out.println("Ola! Eu sou um agente impressor!");
      System.out.println("# Vou executar meu comportamento ");
      addBehaviour(new MeuComportamento(this,valor));
     } else {
      System.out.println("Você não passou argumentos");               
    }
   }
  }

No momento da execução dos agentes, passe como parâmetro três agentes como argumentos numéricos referentes aos de bloqueio para seus comportamentos como, por exemplo, o descrito no código a seguir:

Java jade.Boot Andre:AgenteImpressorArgs(200);
  Maria:AgenteImpressorArgs(400);Paulo:AgenteImpressorArgs(600)

Provavelmente a saída do seu console será semelhante a ilustrada na Figura 4 a seguir. Conforme pode ser observado, os agentes executam os seus comportamentos de forma concorrente em tempos diferentes, mas também pode ser observado que existem ocasiões em que executam o seu comportamento no mesmo intervalo de tempo. Por fim, os agentes finalizam os seus comportamentos em intervalos de tempos diferentes.

Comportamentos Pré-Definidos

O JADE possui comportamentos pré-definidos para auxiliar o desenvolvedor na construção de comportamentos, facilitando a sua implementação e agilizando a construção de sistemas multiagentes. Os comportamentos pré-definidos são definidos em quatro grupos:

  • Comportamentos ­one-shot: comportamentos que executam de maneira quase instantânea e apenas uma vez;
  • Comportamentos cíclicos: são comportamentos que nunca finalizam o método action() sempre é executado, pois o método done() sempre retorna valor booleano false;
  • Comportamentos temporais: são comportamentos que possuem uma relação temporal em sua execução;
  • Comportamentos compostos: são comportamentos que modelam situações específicas como comportamentos paralelos, sequenciais e etc.

Comportamentos One-Shot

Para utilizar este tipo de comportamento é necessário importar a classe “OneShotBehaviour” do pacote “jade.core.behaviours”. Neste comportamento o método done() sempre retorna o valor true, fazendo com que seja executado apenas uma vez porque o método não pode ser sobrescrito. A Listagem 6 ilustra um exemplo de comportamento OneShot.

A Listagem 7 ilustra um exemplo de agente que executa um comportamento OneShot. Ambos os códigos são semelhantes aos códigos descritos nas Listagens 2 e 1, respectivamente.

Listagem 6. Modelo do comportamento OneShot.

import jade.core.behaviours.OneShotBehaviour;
  public class ComportamentoOneShot extends OneShotBehaviour 
    public void action(){
      System.out.println("* Ola Mundo! ... Meu nome é " + myAgent.getLocalName( ));
    }
  }

Listagem 7. Exemplo de agente com comportamento OneShot.

import jade.core.Agent;
  import jade.core.behaviours.OneShotBehaviour;
  public class HelloWorldAgent extends Agent{
   
        protected void setup() {
            System.out.println("Olá mundo! Eu sou um agente!");
            System.out.println("Estou disparando um comportamento OneShot");
            addBehaviour (new MeuComportamento(this));               
        }
  }

Comportamentos Cíclicos

Semelhante ao tipo de comportamento OneShot, mas a diferença de que o método done() sempre retorna valor booleano de valor false. Neste caso, o comportamento se mantém ativo enquanto o agente estiver sendo executado na plataforma JADE. Para utilização deste comportamento deve-se importar a classe “jade.core.behaviours.CyclicBehaviour”. A Listagem 8 ilustra um exemplo de agente com comportamento cíclico. Na Listagem 9 é ilustrado um exemplo de comportamento cíclico. Ambos os códigos são semelhantes aos códigos das Listagens 1 e 2, respectivamente.

Listagem 8. Exemplo de agente com comportamento cíclico.

import jade.core.Agent;
  import jade.core.behaviours.CyclicBehaviour;
      public class HelloWorldAgent extends Agent{
   
          protected void setup() {
              System.out.println("Olá mundo! Eu sou um agente!");
              System.out.println("Estou disparando um comportamento");
              addBehaviour(new MeuComportamento(this));               
          }
      }

Listagem 9. Modelo do comportamento cíclico.


  import jade.core.Agent;
  import jade.core.behaviours.CyclicBehaviour;
   
  public class MeuComportamento extends CyclicBehaviour{
      
      public MeuComportamento(Agent a) {
          super(a);
      }
   
      public void action() {
          System.out.println("* Ola Mundo! ... Meu nome e " + myAgent.getLocalName( ));        
      }
  }

Comportamentos Temporais

Estes tipos de comportamentos classificados em WakerBehaviour e TickerBehaviour possuem uma estreita relação com o tempo durante sua execução. Aos serem invocados, ambos aguardam até que se tenha cumprido um tempo definido (time-out) para serem executados. A diferença é que o WakerBehaviour executa apenas uma única vez, enquanto que o TickerBehaviour realiza um comportamento cíclico.

Para utilização do comportamento WakerBehaviour deve-se importar a classe jade.core.behaviours.WakerBehaviour. O método que executa a ação de comportamento do tipo WakerBehaviour é o método onWake(). A Listagem 10 ilustra um agente fazendo uso desse comportamento.

Listagem 10. Exemplo de um agente com comportamento WakeBehaviour.

import jade.core.Agent;
  import jade.core.behaviours.WakerBehaviour;
      public class HelloWorldAgent extends Agent{
   
          protected void setup() {
   
              System.out.println("Adicionando waker behaviour");
              addBehaviour(new WakerBehaviour(this,10000){
                  protected void onWake ( ) {
                      System.out.println("Executanto comportamento Wake Behaviour");
                  }
              });               
          }
      }

O comportamento do tipo TickerBehaviour possui seus métodos action() e done() já implementados e não podem ser sobrescritos, bastando para o desenvolvedor implementar o método onTick(), que nunca termina ao menos que seja removido pelos métodos removeBehaviour() ou stop(). Para utilização desse comportamento é preciso importar a classe “jade.core.behaviours.TickerBehaviour”. A Listagem 11 ilustra um agente que executa um comportamento do tipo TickerBehaviour, exibindo a cada segundo o número de seu ciclo (tick). É possível obter o número de ciclos através do método getTickCount(), que retorna um valor inteiro que representa o ciclo do agente. Observe que após cinco ciclos o comportamento é interrompido com o método stop(). Este também pode ser reiniciado com o método reset(), fazendo com que o comportamento fosse novamente iniciado e o número de ciclos executados seja zerado.

Listagem 11. Agente executando comportamento TickerBehaviour.

import jade.core.Agent;
  import jade.core.behaviours.TickerBehaviour;
      public class HelloWorldAgent extends Agent{
   
          protected void setup() {
   
              System.out.println("Adicionando TickerBehaviour");
              addBehaviour (new TickerBehaviour(this,1000) {
                  protected void onTick() {
                      if (getTickCount()>5) {
                          stop();
                      }else {
                          System.out.println( "Estou realizando meu " +
                          getTickCount() + " tick");
                      }
                  }
              });
          }
      }

A estratégia de comportamentos visa estruturar as tarefas do agente para organização e melhor entendimento das tarefas que os agentes irão desempenhar. Foi possível observar que existem diversos tipos de comportamentos pré-definidos que podem auxiliar os desenvolvedores na hora do desenvolvimento em escolher quais comportamentos são melhores soluções paras atender as regras de negócios.