• Aucun résultat trouvé

Actors and Agents

5.1 The Actor Model

Newell [1962] pointed out that with conventional AI, a single agent appears to be wandering over a goal net much as an explorer wanders over the globe. The agent has a single context that it takes with it wherever it goes. This single agent view focuses attention on the internal process of search with a single locus of control and attention.

This leads to preoccupation with control structures such as goal stacks and queues for making decisions and changing contexts. Rather than a sequence of choices made by a decision maker on a web of choice points, Hewitt [1977] envisages control as a pattern of messages among a collection of computational agents he called Actors.

According to Hewitt [1985], incomplete knowledge is typical of AI and as such requires an approach that allows continuous acquisition, refinement and toleration of inconsistency. Hewitt claims open systems uncover important limitations in current approaches to AI. Such systems require an approach more like organizational behavior embodied in general systems theory [Skyttner, 1996]. Minsky [1986] has claimed that intelligence in humans is a result of the interaction of a very large and complex assembly of loosely connected subunits operating much like a society but within a single individual. More generally, Hewitt argues that the agents in an organization are open in the sense that they are embedded in an environment with which they interact asynchronously. Open systems are not totally in control of their fate. They consist of agents, conceptually parallel threads, which communicate with each other and co-operatively or competitively respond to events that occur indeterministicly in real-time.

Refinement of these ideas produced the Actor language [Agha, 1986] that attempted to address the needs of distributed AI. The example of a stack is used to introduce Actors in [Agha, 1986] using the Simple Actor Syntax:

def node(Item, Link) [case operation of

push: (NewItem) pop: (Customer) end case ]

if operation = push then

let L = new node(Item, Link) become node(NewItem, L) fi

if operation = pop then send Item to Customer become forward(Link) fi

end def

The case statement clearly corresponds to GDC guards. Besides the conditional, actors are defined inductively with four primitive actions: send; become; create and forward. The arrival order of messages is nondeterministic but the underlying message passing system is assumed to guarantee eventual delivery. To send a message, the identity (mail address) of the recipient needs to be specified. The become directive specifies the subsequent behavior of the actor. The send action causes a message to be put in the recipient’s mailbox (message queue). The create primitive (let and new) is to Actors what procedural abstraction is to sequential programming. Newly created actors are autonomous and have unique mail addresses specified in the create command.

The forward primitive actor passes on received messages to the mailbox named in its argument. It is left to a garbage collector to detect and finesse forwarders. Both channels, such as Item and message queues, such as Link, are named in the language.

The forwarder can be understood in GDC as shorting two streams as in merge. To better understand the Actor language, a GDC program for the stack example corresponding closely to the Actor program is given:

//node(Item, Task ,Link)

node(Item, push(NewItem, Task),Link) :- true | node(Item, L, Link), node(NewItem, Task,L) node(Item,pop(Customer,Task),Link) :- true

| send(Task, Link) send(Item, Customer)

where send(Message, Channel) is used as a synonym for Channel=Message.

The first clause can be represented pictorially as in Figure 5.1.1 and the second clause as in Figure 5.1.2. The essential difference between this and the previous program is that Actors names message queues and channels, while GDC names only channels. In Actors, variables are local variables and only message queues are shared. The behavioral identity of an actor is ephemeral, as are GDC goals and lasts only one reduction. Actor destruction is thus implicit.

Item

node

node node

Link push

L NewTask NewItem

Fig. 5.1.1 First node clause

Item

send send

node

Link pop

Customer NewTask

Fig. 5.1.2 Second node clause

A scenario illustrating how the stack grows and collapses is the following.

:- node(empty, Task, nil), send(push(m1,Task1),Task),

send(push(m2,Task2),Task1), send(pop(Top,Task3),Task2).

:- node(empty,push(m1,Task1),nil), send(push(m2,Task2),Task1), send(pop(Top,Task3),Task2).

:- node(empty,push(m1,push(m2,Task2)),nil), send(pop(Top,Task3),Task2).

:- node(empty, push(m1,push(m2,pop(Top,Task3))),nil).

:- node(m1,push(m2,pop(Top, pop(Top,Task3))),L1), node(nil,L1,nil).

:- node(m2,pop(Top,Task3),L2), node(m1,L2,L1), node(empty,L1,nil).

:- node(m1,L2,L1), node(empty,L1,nil), send(m2,Top), send(Task3,L2).

:- node(m1,Task3,L1), node(empty,L1,nil), send(m2,Top).

. . .

Before the pop operation the initial node(empty,Task,nil) has evolved into three nodes as in Figure 5.1.3.

node

m2 m1 empty

nil

Fig. 5.1.3 Node evolution

Here the identity of the stack is inherited from the initial node(empty,Task,nil).

That is, agent identity is emergent, in the sense of general systems theory and not exhibited by a single node.

In the conditional semantics of GDC (described in Chapter 4):

a1, a2,...an ← R ← B ← I

lends itself to this idea of agent identity. If an actor ai in the initial network reduces to a set of actors:

b1, b2,... bm

the parent relation conveys a connotation of identity.

In the Actor interpretation of GDC, actors are defined inductively using a single primitive agent send. The action is one of substitution of an actor by a network of actors specified in the body of a clause. As actors in GDC are ephemeral, there is no need for fowarders. This is summarized in the following table:

Table 5.1.4 Actor interpretation

behavior named set of condition action pairs (guarded clauses) condition (guard) node(Item, Task, Link)

if receive(push(NewItem,NextTask),Task) action – substitution

by network of agents

become node(NewItem, NextTask, L)

| node(Item,L,Link)

Yet again, the syntax of GDC has been changed to illustrate the actor interpretation.

The notation | for the concurrent composition of actors is borrowed from CCS [Milner, 1980].

One problem with Actors is the combinatorial explosion in their number of actors.

Constructing message queues using data structures gives more flexibility and avoids some of the explosion:

//node(Task, Stack) node(Task, Stack)

if receive(push(NewItem, Task),Task)

become send(stack(NewItem, Stack),St1) | node(Task,St1).

node(Task, stack(Item, Stack))

if receive(pop(Customer, Task), Task),Item) become send(Item, Customer) | node(Task, Stack)

In this revised implementation, a stack is formed by a message queue and node becomes a server of the message queue. In the first behavior for node, the node sends itself a message. This can be finessed:

node(Task,Stack) if receive(push(NewItem, Task),Item) become node(Task, stack(Item, Stack)).

node(pop(Customer, Task),stack(Top, Stack)) if receive(pop(Customer, Task), Task),Item) become send(Item, Customer) | node(Task, Top)

Figure 5.1.5 illustrates the revised first behavior.

node node stack

push NewItem

Stack Task

Fig. 5.1.5 Revised node behavior

The diagram for the second behavior is similar. This solution is not possible in Actors because, essentially, it requires two message queues.