Интеграция JADE и Jess

Автор: Henrique Lopes Cardoso (Университет Порто) - hlc@fe.up.pt
Дата: Март 2007

Источник: http://jade.tilab.com/doc/tutorials/jade-jess/jade_jess.html
Перевод на русский: Пронькин В.Г.


Содержание

  1. Введение
  2. Разработка JADE-агентов с использованием Jess
  3. Выбор имплементаций
  4. Благодарности

1. Введение

Это краткое руководство объясняет, как интегрировать компоненты Jess в JADE-агенты.

С домашней страницы Jess: "Jess - это движок, основанный на правилах и сценариях окружающей среды, полностью написанный на языке Java компании Sun Эрнестом Фридменом-Хиллом в Национальной Лаборатории Sandia в Ливерморе, штат Калифорния. Используя Jess, вы можете построить Java приложение, которое которое обладает способностью "размышлять" используя знания, представленные вами в виде декларативных правил. Jess является маленьким, легким и одим из самых быстрых и доступных движков на основе правил."

Jess был первоначально задуман как инструмент для экспертных систем по строительству. В мире многоагентных систем он может использоваться как агентный компонент решения, который построен декларативным путем (Рис. 1).

Figure 1. Агент и его окружение: JADE и Jess интеграция (адаптировано Russel & Norvig, "Искусственный интеллект: Современный Подход", Прентиц Холл).

Другая часть руководства посвящена реализации модуля решения Jess и его интеграции с кодом JADE-агента. Это руководство не указывает, почему вы должны использовать Jess, подробно не описывает функционирование механизма Jess, и не охватывает при этом проблемы установки Jess. Для этого читайте документацию Jess.

Это руководство основано на JADE 3.4.1 и Jess 7.0p1, хотя оно может применяться и к более ранним версиям этих инструментальных средств.


2. Разработка JADE-агентов с использованием Jess

Прежде, чем интегрировать оба инструментальных средства, важно иметь в виду некоторые главные проблемы функционирования JADE-агентов и механизмов Jess. Кроме того, существует множество различных подходов, которые дают возможность вам выбрать количество программ агента, которые вы хотите реализовать в Jess.

2.1 Важные замечания

Прежде всего, стоит помнить, что, в принципе, агент JADE является однопоточным. Это означает, что нужно быть внимательным, если агент должен взаимодействовать со своей средой, рассуждая и принимая решения. Это не связанный Jess, все же. Используете ли вы Jess, эту проблему нужно принимать во внимание.

Будучи механизмом правил, Jess включает специальный класс под названием Rete, который осуществляет основанный на правилах механизм логического вывода. Чтобы внедрить Jess в приложение Java (такое как агент JADE), вы просто должны создать Jess. Rete отвечает и управляет им соответственно. Далее, один из методов в этом классе, который позволяет вам выполнять механизм логического вывода Rete.run(). Этот метод заставит механизм последовательно запустить соответствующие правила, и вернуться только, когда не будет больше сработавших правил, то есть, когда механизм останавливается; поэтому, тем временем тематика запроса будет блокирована. (Если вы хотите понять, как применены правила, то смотрите документацию Jess).

Теперь, если мы заблокируем вызов потока, то мы блокируем всего однопоточного агента. Это может относиться к делу или нет, в зависимости от того, как долго будет работать основанное на правиле рассуждение. К счастью, jess.Rete класс включает другой метод выполнения, который позволяет нам определять максимальное число циклов, которые должен выполнить механизм. Это позволит нам чередовать Jess на основе рассуждений с другими действиями агента.

2.2 Осуществление поведения Jess

Принимая во внимание вышеупомянутые соображения, предложенная реализация содержит вставку экземпляра класса механизма Jess в поведение. Так как мы хотим, чтобы агент был в состоянии непрерывно рассуждать, мы осуществляем CyclicBehaviour, действие которого будет состоять из выполнения механизма Jess. Однако, мы будем бояться заблокировать поведение другого агента при значительном количестве времени.

Следующий фрагмент кода показывает реализацию:

 1 class JessBehaviour extends CyclicBehaviour {
 2     // Jess-движок
 3     private jess.Rete jess;
 4     // максимальное число проходов 
которые выполнение Jess может выполнить прежде, чем отдать контроль агенту
 5     private static final int MAX_JESS_PASSES = 1;
 6     
 7     JessBehaviour(Agent agent, String jessFile) {
 8         super(agent);
 9         // создание Jess-движка
10         jess = new jess.Rete();
11         // загрузка Jess-файла
12         try {
13             // открытие Jess-файла
14             FileReader fr = new FileReader(jessFile);
15             // создание просмотрщика файла
16             jess.Jesp j = new jess.Jesp(fr, jess);
17             // просмотр входного файла в движке
18             try {
19                 j.parse(false);
20             } catch (jess.JessException je) {
21                 je.printStackTrace();
22             }
23             fr.close();
24         } catch (IOException ioe) {
25             System.err.println("Error loading Jess file - 
engine is empty");
26         }
27     }
28     
29     public void action() {
30         // количество проходов
31         int executedPasses = -1;
32         // запуск jess
33         try {
34             // запуск для максимального числа шагов
35             executedPasses = jess.run(MAX_JESS_PASSES);
36         } catch (JessException je) {
37             je.printStackTrace();
38         }
39         // если движок остановлен, блокировать это поведение
40         if(executedPasses < MAX_JESS_PASSES)
41             block();
42             // поведение должно быть открыто вызовом restart()
43     }
44     
..     ...
64 } // конец JessBehaviour класса

Поведенческий конструктор (строки 7-27) сначала создает экземпляр класса механизма Jess (строка 10), и затем загружает ранее созданный кодовый файл Jess, используя синтаксический анализатор Jess (строки 16, 19). Собственно выполнение кода поведения (строки 29-43) выполняет механизм Jess индикации максимального количества проходов, которые он должно выполнить (строка 35). Метод выполнения возвращает фактическое число выполняемых проходов, которые могут быть от нуля до значения параметра. После выполнения механизма эти числа (максимум и фактически выполненные проходы) сравниваются (строка 40) и определяется, остановился ли механизм, т.е. закончились ли срабатывающие правила. Если да, то это поведение блокируется (строка 41), так как последующий вызов при выполненении (в следующем цикле) не имел бы никакого эффекта вообще.

Эта реализация предполагает, что у вас будет возможность задавать новую информацию в механизм Jess, и возможность перезапустить JessBehaviour (см. комментарий в строке 42). Один из способов сделать это, обеспечивая доступ к JessBehaviour экземпляру класса, и осуществляя соответствующие методы, которые будут ответственны за обе задачи (утверждение информации и пробуждение). Вот пример метода для JessBehaviour класса:

45     boolean addFact(String jessFact) {
46         // добавление факта в механизм Jess
47         try {
48             jess.assertString(jessFact);
49         } catch(JessException je) {
50             return false;
51         }
52         // если блокировано, то разбудить!
53         if(!isRunnable()) restart();
54         // сообщение подтверждения
55         return true;
56     }
57     

Метод получает конструктор факта Jess как параметр и утверждает его в Jess (строка 48). ( assertString (String) метод - один из методов, доступных в jess.Rete , чтобы создать утверждения). Поведение будет перезапущено, если оно было блокировано (строка 53). Это возымеет эффект при выполнения поведения (и выполнении механизма Jess) в следующем возможном цикле. Вы можете также рассмотреть методы, в которых вместо того, чтобы получать непосредственно факт Jess как строку String, можно получить что-нибудь еще (например, ACLMessage) и собрать Jess код. Это имеет смысл, если Вы хотите изолировать использование Jess в JessBehaviour. Один из тех методов может быть:

58     boolean newMsg(ACLMessage msg) {
59         String jf = ... // используется msg для сборки Jess конструктора
60         // в Jess движке
61         return addFact(jf);
62     }
63     

Строка 59 - то место, где вы встроили бы конструкцию Jess, которая будет утверждаться в Jess. Эта конструкция была бы функцией ACLMessage параметра, например, основанного на информационном наполнении сообщения.

Типичные (хотя не принудительные) средства интегрирования этого подхода при взаимодействии агента должны осуществить поведения, которые обрабатывают входящие сообщения, и используют JessBehaviour методы для того, чтобы добавлять соответствующие факты в механизм Jess. Вот отрывок примера для сообщения, обрабатывающего поведение:

 1 class MsgListening extends CyclicBehaviour {
 2     // ссылки на экземпляр класса JessBehaviour
 3     private JessBehaviour jessBeh;
 4     
 5     MsgListening(Agent agent, JessBehaviour jessBeh) {
 6         super(agent);
 7         // сохранение ссылки на экземпляр JessBehaviour
 8         this.jessBeh = jessBeh;
 9     }
10     
11     public void action() {
12         MessageTemplate mt = ... // некоторый шаблон
13         ACLMessage msg = myAgent.receive(mt);
14         if (msg != null) {
15             // положить в Jess-движок
16             if(jessBeh.newMsg(msg))
17                 ... // делать что-то
18             else
19                 ... // иначе делать то-то
20         } else
21             block();
22     }
23     
24 } //конец класса MsgListening

Конструктор поведения (строки 5-9) хранит ссылку на экземпляр класса JessBehaviour(строка 8). Выполнение поведения (строки 11-22) получает входящее сообщение (строка 13), и добавляет его в механизм Jess, вызывая соответствующий метод (строка 16).


3. Выбор имплементаций

Интеграция JADE и Jess может быть сделана множеством различных способов (см. раздел руководства Jess "Дизайн Приложений Jess" для возможной интеграции Jess и Java). В этом разделе выделены несколько опций.

3.1 От перцепции до Jess

Оглядываясь на Рисунок 1, пока мы имели дело с осуществлением части программы агента, используя Jess, и информацией, выходящей из объектов перцепции агента (то есть, сообщения) на механизм Jess. Даже в этой части процесса, у нас могла быть более сложная интеграция JADE и Jess, позволяющая механизму Jess обратиться к целой структуре сообщения Ассоциаций по вычислительной лингвистике. Есть два способа сделать это.

One way is to define a Jess ACLMessage template that maps the ACLMessage class. Something like: Один способ состоит в том, чтобы определить шаблон Jess ACLMessage, который отображает ACLMessage класс. Вот так:

(deftemplate ACLMessage
    (slot communicative-act)
    (slot sender)
    (multislot receiver)
    (slot content)
    (slot reply-with)
    (slot in-reply-to)
    ...)

В этом способе мы только нуждаемся в методе, который переводит объект ACLMessage в конструкцию Jess для факта ACLMessage. Правила Jess могут непосредственно обработать эти факты. Предостережение нужно взять, преобразовывая слот информационного наполнения. Это из-за того, что в сообщении ACL у Вас может быть информационное наполнение как строка, включая места, которые не будут подготавливаться как правильное значение слота в Jess (см. Языковые основы Jess). Решение может окружать строку информационного наполнения двойными кавычками (").

Другой более интересный и простой подход должен был бы использовать в своих интересах Jess-интеграцию Java, которая позволяет Jess обращаться к любому объекту Java (читайте руководство Jess на теневых фактах для деталей). Этим способом мы больше не должны транслировать в Java конструкции Jess от объекта ACLMessage. Вы определяете соответствующий шаблон Jess для класса:

(deftemplate ACLMessage
    (declare(from-class ACLMessage)))

Вы можете добавить объект ACLMessage в рабочую память механизма Jess, используя Rete.add(ACLMessage), и обратиться к его полям непосредственно в правилах Jess. Например, так:

(defrule incomming-msg
    (ACLMessage (sender ?s))
    =>
    (printout t "Just received a message from " (?s getLocalName) crlf))

К сожалению, объекты ACLMessage, которые включают содержимое строки (которые большую часть времени, начиная с использования объектов содержимого не подходящие FIPA), плохо работают, определяя их как теневые факты в Jess. Я полагаю, что это происходит из-за несовпадения этого класса со стандартными определениями дизайна JavaBeans , используется в java.beans.Introspector (и через это в Jess). Параметр информационного наполнения определен как StringBuffer, в то время как его методы возврата/получения установки реализованы как String.

Чтобы решить эту проблему, вам, вероятно, придется реализовать класс ACLMessageBeanInfo. Альтернативное решение состоит в том, чтобы расширить класс ACLMessage и отменить использование параметра информационного наполнения. Например, так:

public class MyACLMessage extends ACLMessage {
    private String content;
    
    MyACLMessage(int perf) {
        super(perf);
    }
    
    public String getContent() {
        return content;
    }
    
    public void setContent(String content) {
        this.content = content;
    }
}

Тогда вы располагаете ваш шаблон Jess ACLMessage в класс MyACLMessagefrom-class). Вы можете, однако, освободить некоторые из функциональных возможностей текущей реализации ACLMessage. Полезно также добавить конструктор, который берет ACLMessage как параметр, и используется, когда Вы хотите утвердить сообщение в Jess (если все Ваши агенты не используют MyACLMessage).

3.2 От Jess к действию

Действующая часть процесса иллюстрирована в Рисунок 1, где действие состоит из посылки сообщения, и обсуждается в этом разделе.

Пример включен в раздел руководства Jess " Внедрение Jess в Java-приложение". Он показывает, как получить результаты механизма логического вывода после того, как он остановлен. Подобный подход мог быть осуществлен в методе JessBehaviour. action(): после остановки механизма Jess была бы выполнена процедура для того, чтобы собрать определенные типы фактов, которые представляют результат процесса рассуждения, и те факты могут тогда использоваться, например, чтобы создать сообщение ответа. Однако, это бы не использовало в своих интересах более тесную интеграцию с JADE.

Посылаемое сообщение может быть также сделано Jess непосредственно. Снова, в зависимости от намеченного уровня интеграции, есть по крайней мере два способа выполнить эту задачу. Один способ состоит в том, чтобы использовать функции пользователя Jess. Эта особенность дает вам возможность осуществить в Java функцию, которая должна быть вызвана в вашем коде Jess. Поэтому, вы можете реализовать jess.Userfunction для того, чтобы позволить посылку сообщения от Jess:

 1 public class JessSend implements jess.Userfunction {
 2     private Agent myAgent;
 3     
 4     public JessSend(Agent a) {
 5         myAgent = a;
 6     }
 7     
 8     // имя функции используется Jess
 9     public String getName() {
10         return ("send");
11     }
12     
13     // вызов (send...) в запущенной Jess
14     public Value call(ValueVector vv, Context context) 
throws JessException {
15         // получение аргументов функции
16         ... vv.get(...
17         ...
18         
19         // подготовка сообщения к отправке
20         ACLMessage msg = new ACLMessage(...);
21         msg.set...
22         ...
23         
24         // отправка сообщения
25         myAgent.send(msg);
26         
27         return Funcall.TRUE;
28     }
29     
30 } // конец класса JessSend

And add this function to the Jess engine at the JessBehaviour constructor:

jess.addUserfunction(new JessSend(myAgent));

Всякий раз, когда Jess находит вызов функции (send ...), он будет использовать вышеупомянутый метод JessSend.call(ValueVector, Context) (строки 14-28). В нем, и в зависимости от различной информации вашиш циклов выполнения в функции send, вы получаете аргументы функции (строки 15-17) и используете их так или иначе, чтобы составить сообщение для отсылки (строки 20-22). В зависимости от вашего выбора в разделе 3.1 вы можете пропустить функцию send Jess конструкцию ACLMessage или даже объект ACLMessage.

Другой способ послать сообщения от Jess, с более тесной интеграцией JADE-Jess, состоит в том, чтобы предоставить доступ к объекту Agent, используя Rete.add(Agent). Метод Agent.send(ACLMessage) - тогда непосредственно получается из кода Jess .


4. Благодарности

Часть кода, представленного в этом руководстве, основана на примере Jess, связанного теперь с JADE. Ваши пожелания отправляйте к Фабио Баллифемин.