Bộ sưu tập

Lập trình agent với JADE cho người mới bắt đầu (phần 2)


https://codersontrang.com/2013/01/10/lap-trinh-agent-voi-jade-cho-nguoi-moi-bat-dau-phan-2/

Lớp Behaviour

Như đã nói ở phần trước, công việc thực sự của một agent sẽ được thực hiện bên trong “các hành vi” của chúng. Một hành vi biểu diễn một nhiệm vụ mà một agent thực hiện và được cài đặt như một đối tượng của lớp kế thừa lớp jade.core.behaviours.Behaviour. Để có thể làm cho agent thực hiện các nhiệm vụ được cài đặt bởi đối tượng của lớp Behaviour, chúng ta phải thêm đối tượng này vào agent bằng cách gọi phương thức addBehaviour() của lớp Agent. Các hành vi có thể được thêm bất cứ lúc nào: khi mà một agent khởi động (trong phương thức setup()) hoặc là khi agent đang thực hiện một hành vi khác (bên trong các hành vi khác).

Một lớp kết thừa từ lớp Behaviour phải cài đặt phương thức action() cái định nghĩa ra kịch bản của một hành vi, và phương thức done() (trả về giá trị kiểu boolean) là phương thức chỉ ra hành vi này đã hoàn thành hay chưa khi mà lần đầu tiên phương thức action thực hiện xong. Nếu giá trả về là true, hành vi này sẽ bị xóa khỏi danh sách các hành vi mà agent đang thực hiện.

Việc lập lịch và thực hiện các hành vi

Một agent có thể thực hiện nhiều hành vi một cách đồng thời. Việc lập lịch cho các hành vi của agent ở đây không phải là đề cập tới việc ưu tiên thứ tự thực hiện giữa các hành vi, mà là các hành vi sẽ hợp tác với nhau như thế nào. Điều này có nghĩa là khi một hành vi được lập lịch, nó sẽ thực hiện phương thức action() từ đầu đến cuối và sẽ không có sự can thiệp nào vào quá trình thực hiện hành vi đó. Vì thế công việc của chúng ta chỉ là chỉ định ra hành vi thực hiện tiếp theo khi một hành vi được hoàn thành.

Cách tiếp cận này có một vài điểm thuận lợi:

  • Chỉ có một Java thread (tuyến) trên một agent (điều này khá quan trọng, đặc biệt là trong môi trường mà ở đó tài nguyên bị giới hạn, chẳng hạn như điện thoại di động)
  • Cải thiện hiệu năng bởi vì việc chuyển đổi giữa các hành vi sẽ nhanh hơn rất nhiều so với việc chuyển đổi giữa các Java thread
  • Loại bỏ tất cả các vấn đề về đồng bộ hóa giữa các hành vi đang đồng thời truy cập vào cùng một tài nguyên (điều này cũng làm cải thiện hiệu năng) bởi vì tất cả các hành vi chỉ được thực hiện trên một Java thread duy nhất.

Hình dưới đây miêu tả sự thực hiện của một agent thread(1)

jade_beginner_p2_1

Với cơ chế lập lịch trên, một điều quan trọng chúng ta phải nhận ra đó là một hành vi giống như dưới đây sẽ không cho phép các hành vi khác sau nó thực hiện bởi vì phương thức action của nó sẽ không bao giờ kết thức được:


public class OverbearingBehaviour extends Behaviour{
	public void action(){
		while(true){
			//do something
		}
	}
	
	public boolean done(){
		return true;
	}
}

Khi không còn hành vi nào để thực hiện, thread của agent sẽ chuyển về trạng thái ngủ (sleep) để không làm tiêu tốn thời gian của CPU. Khi có một hành vi mới cần thực hiện, nó sẽ được đánh thức.

Các loại hành vi
Có 3 loại hành vi

Hành vi loại One-shot
Một hành vi loại one-shot là hành vi mà phương thức action của nó chỉ thực hiện duy nhất một lần. Lớp jade.core.behaviours.OneShotBehaviour đã cài đặt sẵn phương thức done bằng việc trả về giá trị là true. Các hành vi thuộc loại one-shot sẽ kế thừa từ lớp này.


public class MyOneShotBehaviour extends OneShotBehaviour{
	public void action(){
		//perform operation X
	}
}

Ở đây, công việc X chỉ được thực hiện duy nhất một lần.

Hành vi loại Cyclic

Một hành vi loại Cyclic sẽ không bao giờ hoàn thành và phương thức action của nó sẽ được thực hiện công việc giống nhau mỗi lần nó được gọi. Lớp jade.core.behaviours.CyclicBehaviour cài đặt sẵn phương thức done bằng việc trả về giá trị là false. Các hành vi loại Cyclic sẽ kế thừa lớp này.


public class MyCyclicBehaviour extends CyclicBehaviour{
	public void action(){
		//perform operation Y
	}
}

Công việc Y ở đây sẽ được thực hiện lặp đi lặp lại mãi mãi (đến khi mà agent thực hiện hành vi trên kết thúc).

Hành vi loại Generic

Một hành vi loại Generic sẽ nhúng một trạng thái vào nó và thực hiện các công việc khác nhau tùy thuộc vào trạng thái đó. Hành vi được hoàn thành khi mà một điều kiện nào đó được thỏa mãn.


public class MyThreeStepBehaviour extends Behaviour{
	private int step = 0;
	public void action(){
		switch(step){
			case 0:
				//perform operation X
				step++;
				break;
			case 1:
				//perform operation Y
				step++;
				break;
			case 2:
				//perform operation Z
				step++;
				break;
		}
	}
	
	public boolean done(){
		return step == 3;
	}
}

Công việc X, Y và Z được thực hiện lần lượt và sau đó hành vi hoàn thành.

JADE cung cấp khả năng để kết hợp các hành vi đơn giản vào với nhau để tạo ra các hành vi phức tạp. Tuy nhiên, chúng ta không đề cập đến trong loạt bài viết này. Chúng ta có thể tham khảo các tài liệu có liên quan đến SequentialBehaviour, ParallelBehaviour và FSMBehaviour để biết thêm chi tiết.

Lập lịch công việc tại một thời điểm nhất định

JADE cung cấp cho chúng ta hai lớp (đều nằm trong lớp jade.core.behaviours) để cài đặt các hành vi và thực hiện chúng vào một thời điểm xác định.

Lớp WakerBehaviour, các phương thức action() và done() của nó đều đã được cài đặt sẵn và công việc của chúng ta chỉ là cài đặt phương thức trừu tượng handleElapsedTimeout(). Phương thức này sẽ được gọi sau một khoảng thời gian nào đó kể từ lúc hành vi bắt đầu. Sau khi thực hiện xong phương thức này, hành vi sẽ hoàn thành.


public class MyAgent extends Agent{
	protected void setup(){
		System.out.println("Adding waker behaviour");
		addBehaviour(new WakerBehaviour(this, 10000){
			protected void handleElapsedTimeout(){
				//perform operation X
			}
		});
	}
}

Công việc X sẽ được thực hiện sau 10 giây kể từ khi lời nhắn “Adding waker behaviour” được in ra.

Lớp TickerBehaviour, các phương thức action() và done() của nó cũng đều đã được cài đặt. Chúng ta phải cài đặt phương thức trừu tượng là onTick() và phương thức này sẽ được gọi lặp đi lặp lại sau một khoảng thời gian nhất định. TickerBehaviour không bao giờ hoàn thành.


public class MyAgent extends Agent{
	protected void setup(){
		addBehaviour(new TicketBehaviour(this, 10000){
			protected void onTick(){
				//perform operation Y
			}
		});
	}
}

Công việc Y được thực hiện cứ sau mỗi 10 giây kể từ khi hành vi được bắt đầu.

Các hành vi được yêu cầu trong ví dụ book trading
Như vậy, chúng ta đã biết về các loại hành vi cơ bản, bây giờ chúng ta sẽ phân tích các loại hành vi sẽ được sử dụng trong ví dụ book trading của chúng ta.

Các hành vi của agent mua
Như đã nói ở phần trước, agent mua sẽ gửi yêu cầu về quyển sách nó cần đến các agent bán. Như vậy, chúng ta cần sử dụng một TickerBehaviour, sao cho mỗi lần tick, nó sẽ thực hiện các hành vi để gửi yêu cầu đến các agent bán. Như vậy, chúng ta thay đổi phương thức setup trong lớp BookBuyerAgent như sau:


package examples.bookTrading;

import jade.core.Agent;
import jade.core.AID;

public class BookBuyerAgent extends Agent{
	//The title of the book to buy
	private String targetBookTitle;
	//The list of known seller agents
	private AID[] sellerAgents = {new AID("seller1", AID.ISLOCALNAME),
								  new AID("seller2", AID.ISLOCALNAME)};
	//Put agent initialization here
	protected void setup(){
		//Printout a welcome message
		System.out.println("Hello! Buyer-agent "+getAID().getName() + " is ready.");
		
		//Get the title of the book to buy as a start-up argument
		Object[] args = getArguments();
		if(args != null && args.length > 0){
			targetBookTitle = (String) args[0];
			System.out.println("Trying to buy "+targetBookTitle);
			
			// Add a TickerBehaviour that schedules a request to seller agents every minute
			addBehaviour(new TickerBehaviour(this, 60000){
				protected void onTick(){
					myAgent.addBehaviour(new RequestPerformer());
				}
			});
		}else{
			//Make the agent terminate immediately
			System.out.println("No book title specified");
			doDelete();
		}
	}
	
	//Put agent clean-up operations here
	protected void takeDown(){
		//Printout a dismissal message
		System.out.println("Buyer-agent "+getAID().getName()+" terminating.");
	}
}

Trong đoạn mã trên, chúng ta sử dụng một biến protected là myAgent. Mỗi một hành vi sẽ có một con trỏ tham chiếu đến cái agent đang thực hiện nó và đó là myAgent.

Hành vi RequestPerformer sẽ làm công việc gửi yêu cầu đến các agent bán và chúng ta sẽ nói đến nó trong phần sau.

Các hành vi của agent bán
Cũng như đã miêu tả trước đây, agent bán sẽ đợi các yêu cầu được gửi đến từ các agent mua và phục vụ chúng. Các yêu cầu này có thể là yêu cầu cho một sự cung ứng thông tin sách hoặc yêu cầu thanh toán hóa đơn. Như vậy, chúng ta có thể thiết kế agent bán để cho nó thực hiện hai hành vi loại Cyclic: một cái để phục vụ các yêu cầu cho sự cung ứng thông tin sách đến các agent mua, cái còn lại phục vụ các yêu cầu cho việc thanh toán hóa đơn. Cách yêu cầu từ các agent mua đến các agent bán như thế nào chúng ta sẽ tìm hiểu tiếp trong phần sau. Thêm vào nữa, agent bán còn phải thực hiện một hành vi loại one-shot để cập nhật lại thư mục sách khi mà người bán hàng thêm quyển sách mới vào từ GUI. Dưới đây là lớp BookSellerAgent (các lớp OfferRequestsServer và PurchaseOrdersServer sẽ được miêu ta ở phần sau)


package examples.bookTrading;

import jade.core.Agent;
import jade.core.behaviours.*;

import java.util.*;

public class BookSellerAgent extends Agent{
	//The catalogue of books for sale (maps the title of a book to its price)
	private Hashtable catalogue;
	// The GUI by means of which the user can add books in the catalogue
	private BookSellerGui myGui;
	
	//Put agent initializations here
	protected void setup(){
		//Create the catalogue
		catalogue = new Hashtable();
		
		// Create and show the GUI
		myGui = new BookSellerGui(this);
		myGui.show();
		
		// Add the behaviour serving requests for offer from buyer agents
		addBehaviour(new OfferRequestsServer());
		
		// Add the behaviour serving purchase orders from buyer agents
		addBehaviour(new PurchaseOrdersServer());
	}
	
	// Put agent clean-up operations here
	protected void takeDown(){
		// Close the GUI
		myGui.dispose();
		//Printout a dismissal message
		System.out.println("Seller-agent"+getAID().getName()+" terminating.");
	}
	
	/**
	 This is invoked by the GUI when the user adds a new book for sale
	*/
	public void updateCatalogue(final String title, final int price){
		addBehaviour(new OneShotBehaviour(){
			public void action(){
				catalogue.put(title, new Integer(price));
			}
		});
	}
}

Lớp BookSellerGui sẽ tạo nên một giao diện cho phép người bán sách thêm sách mới vào thư mục. Nó nhận một con trỏ đến một agent bán và các phương thức của agent bán sẽ được gọi tương ứng với các sự kiện xảy ra trên giao diện này


package examples.bookTrading;

import jade.core.AID;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
  @author Giovanni Caire - TILAB
 */
class BookSellerGui extends JFrame {	
	private BookSellerAgent myAgent;
	
	private JTextField titleField, priceField;
	
	BookSellerGui(BookSellerAgent a) {
		super(a.getLocalName());
		
		myAgent = a;
		
		JPanel p = new JPanel();
		p.setLayout(new GridLayout(2, 2));
		p.add(new JLabel("Book title:"));
		titleField = new JTextField(15);
		p.add(titleField);
		p.add(new JLabel("Price:"));
		priceField = new JTextField(15);
		p.add(priceField);
		getContentPane().add(p, BorderLayout.CENTER);
		
		JButton addButton = new JButton("Add");
		addButton.addActionListener( new ActionListener() {
			public void actionPerformed(ActionEvent ev) {
				try {
					String title = titleField.getText().trim();
					String price = priceField.getText().trim();
					myAgent.updateCatalogue(title, Integer.parseInt(price));
					titleField.setText("");
					priceField.setText("");
				}
				catch (Exception e) {
					JOptionPane.showMessageDialog(BookSellerGui.this, "Invalid values. "+e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 
				}
			}
		} );
		p = new JPanel();
		p.add(addButton);
		getContentPane().add(p, BorderLayout.SOUTH);
		
		// Make the agent terminate when the user closes 
		// the GUI using the button on the upper right corner	
		addWindowListener(new	WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				myAgent.doDelete();
			}
		} );
		
		setResizable(false);
	}
	
	public void showGui() {
		pack();
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		int centerX = (int)screenSize.getWidth() / 2;
		int centerY = (int)screenSize.getHeight() / 2;
		setLocation(centerX - getWidth() / 2, centerY - getHeight() / 2);
		super.setVisible(true);
	}	
}


Như vậy kết thúc phần này, chúng ta hiểu được agent thực hiện các hành vi của nó như thế nào. Trong phần sau chúng ta sẽ tìm hiểu cách để các agent có thể giao tiếp được với nhau.

Chú ý

(1): Trong JADE, mặc định sẽ có một Java thread trong một agent. Tuy nhiên, chúng ta hoàn toàn có thể khởi động các thread mới trong trường hợp chúng ta thấy cần. Nếu như vậy, nhớ rằng các thuận lợi đã nêu sẽ không còn đúng nữa.

Nguồn: Giovanni Caire – Jade tutorial, Jade programming for beginners

Advertisements

2 comments on “Lập trình agent với JADE cho người mới bắt đầu (phần 2)

  1. Excellent blog! Do you have any hints for aspiring writers?
    I’m planning to start my own blog soon but I’m a little lost on everything.
    Would you recommend starting with a free platform like
    Wordpress or go for a paid option? There are so many choices out there that I’m totally overwhelmed
    .. Any ideas? Appreciate it!

    Số lượt thích

    • I see WordPress is so fine and enough for me to get on the flight with words.
      Wordpress is not free at all, it has paid packages that provide many other services to users. You could start with free option then upgrade your site whenever you feel a need. Moreover, in my opinion, what you write is more important than what kind of papers and pens you use to write 🙂

      Số lượt thích

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s