# 4 Mobile Agents

This chapter shows how to program mobile agents in Mozart. We call agent any distributed computation that is organized as a set of tasks. A task is a computation that uses the resources of a single site. By resource we mean the technical definition given in Section 2.1.5, for example, file system, peripherals, networking abilities, operating system access, etc. A task can initiate tasks on other sites, with well-defined specifications of what resources they should use. The distributed behavior of the agent is therefore in first instance decided by the agent itself.

Agents are therefore just resource-aware distributed computations, which can exist on one site or be spread out over more than one site. This definition of agent might seem unnecessarily general, but it is natural in Mozart, where it is as easy for an application to run on one site or a set of sites. For example, here's an agent `A` that concurrently delegates 10 tasks to remote sites and waits until all are done before continuing. The agent servers are represented by `AS0` and `AS9` and the work is represented by functors containing the one-argument procedures `P0` to `P9`. Each procedure binds its argument to the result of its calculation.

> `declare
> A=functor
> define
> X0 ... X9
> {AS0 functor define {P0 X0} end}
> ...
> {AS9 functor define {P9 X9} end}
> {Wait X0} ... {Wait X9}
> ...
> end`

This is efficient. As the distribution model makes clear (see Section 2.1.3), the termination of each remote task is signaled to the original task by exactly one network message, which contains the result of the task's calculation.

The Mozart vision of a universe of agents is a set of fixed places, the agent servers, and a set of evolving computational ``webs'', each potentially covering many places at once. A web is what we call an ``agent''.

## 4.1 An example agent

Let's start with a very simple example of an agent that goes somewhere, interrogates the operating system, and then comes back. We show the example in two parts. First, we show how to install agent servers so that the agent has somewhere to go. Then, we will program the agents.

### 4.1.1 Installing the agent servers

To go to a site, there has to be something at the site that accepts the agent. We call it an agent server. An agent server accepts functors (which represent agents or parts of agents) and applies them on its site. To install an agent server on a site, we use the functor `AgentServer` (see Section 4.1.6, below). When `AgentServer` is installed on a site, then it creates an agent server module `AS` on that site along with the following operations:

- The agent server is accessed by `AS.server`. A calculation is started asynchronously on the agent server by invoking `{AS.server F}`, where `F` is a functor that specifies the calculation and the resources it needs.

- The operation `{AS.publishserver FN}`, which when executed creates a file `FN` that contains a ticket to the site's agent server.

- The operation `{AS.getserver UFN ?AS}`, which when inputted an URL or file name `UFN` to any agent server (even on other sites), gives as output a reference to that agent server `AS`.

Let's say that we know an URL `"http://www.info.ucl.ac.be/~pvr/agents.ozf"` that references the functor `AgentServer`. Then the following code creates a local agent server and makes it accessible through the URL `"http://www.info.ucl.ac.be/~pvr/as1"`:

> `declare GetServer in
> local
> % Get the AgentServer:
> AgentServer={Pickle.load "http://www.info.ucl.ac.be/~pvr/agents.ozf"}
> % Install AgentServer locally: (this creates an agent server)
> [AS1]={Module.apply [AgentServer]}
> % Publish the agent server:
> {AS1.publishserver "/usr/staff/pvr/public_html/as1"}
> in
> GetServer=AS1.getserver
> end`

This code also creates the procedure `GetServer` as defined above.

Let's create a second agent server, a remote one, and make it accessible through the URL `"http://www.info.ucl.ac.be/~pvr/as2"`:

> `local
> RF=functor import Pickle Module export done:D
> define
> AgentServer={Pickle.load "http://www.info.ucl.ac.be/~pvr/agents.ozf"}
> [AS2]={Module.apply [AgentServer]}
> {AS2.publishserver "/usr/staff/pvr/public_html/as2"}
> end
> RM={New Remote.manager init}
> M={RM apply(RF $)}
> in
> skip
> end`

In this case, the three lines of code that do the work of installing the agent server and publishing it are put inside the functor `RF`, and `RF` is installed remotely.

Now let's get access to both of these agent servers. We use the procedure `GetServer` that is defined in the functor `AgentServer`. This procedure can get access to any agent server, not just the one on its site:

> `declare
> Server1={GetServer "http://www.info.ucl.ac.be/~pvr/as1"}
> Server2={GetServer "http://www.info.ucl.ac.be/~pvr/as2"}`

### 4.1.2 Programming agents

Now that the agent servers are installed, we're ready to let the agents work.

A first example creates an agent on the remote site. The agent queries the operating system and returns the value of the `OS.time` operation. Let's first execute the agent interactively:

> `declare D1 in
> {Server2 functor import OS define D1={OS.time} end}
> {Browse D1}`

This first creates variable `D1` and then executes an agent on the remote site. The agent needs only one resource, `OS`, a module that gives access to some operating system functions. The agent is created asynchronously, which means that `D1` will usually still be unbound when the `Browse` is called. In most cases this is not a problem. The dataflow semantics of Oz mean that most operations that need `D1` will wait before continuing. If the caller wants to be sure that `D1` is bound before continuing, it just needs to do a `{Wait D1}`.

Now let's slightly modify the first example to get a standalone agent, i.e., the agent is itself a functor that can be compiled and executed standalone or by another application. We assume that the functor `AgentServer` is available at a standard place.

> `functor
> import System AgentServer
> define
> GetServer=AgentServer.getserver
> Server2={GetServer "http://www.info.ucl.ac.be/~pvr/as2"}
> D1
> {Server2 functor import OS define D1={OS.time} end}
> {System.show D1}
> end`

This is almost the same as the previous example; it just extends it with a call to `GetServer` to access the agent server. The agent first gets access to the remote agent server `Server2` and then starts a calculation there.

If the functor `AgentServer` is not available in a standard place, then the standalone agent has to get it explicitly:

> `functor
> import System Pickle Module
> define
> AgentServer={Pickle.load "http://www.info.ucl.ac.be/~pvr/agents.ozf"}
> [AS]={Module.apply [AgentServer]}
> GetServer=AS.getserver
> Server2={GetServer "http://www.info.ucl.ac.be/~pvr/as2"}
> D1
> {Server2 functor import OS define D1={OS.time} end}
> {System.show D1}
> end`

### 4.1.3 There and back again

We give an example of an agent that goes to a remote site, does a calculation there, and comes back with the result. Surprise, surprise, the calculation takes almost no local CPU time.

### 4.1.4 Round and round it goes, where it stops nobody knows

We give an example of an agent that visits a set of sites repeatedly. It chooses dynamically the next site to visit. In fact, during its execution it can even get access to new sites that it has never heard of before and visit them.

### 4.1.5 Barrier synchronization

An agent can delegate work by creating tasks dynamically, and then wait until all tasks are done. This is easy by using logic variables to synchronize on the termination.

### 4.1.6 Definition of `AgentServer`

The basic tool giving agent functionality is the functor `AgentServer`, which is defined as follows. When installed, this functor creates an agent server, a procedure to publish the agent server, and a procedure to get access to any published agent server.

> `functor
> import Module Connection Pickle
> export
> server:AS
> publishserver:PublishServer
> getserver:GetServer
> define
> S P={NewPort S}
> proc {InstallFunctors S}
> case S
> of F|S2 then
> try
> [_] = {Module.apply [F]}
> catch _ then skip end
> {InstallFunctors S2}
> else skip end
> end
> thread {InstallFunctors S} end
> proc {AS F} {Send P F} end
> T={Connection.offerUnlimited AS}
> % Make agent server available through file FN:
> % Note that the agent server is asynchronous.
> proc {PublishServer FN}
> {Pickle.save T FN}
> end
> % Get access to a server that is at file/URL UFN:
> proc {GetServer UFN AS}
> try
> T={Pickle.load UFN}
> in
> AS={Connection.take T}
> catch _ then
> raise serverUnavailable end
> end
> end
> end`

One way to use `AgentServer` is to compile it with the standalone compiler and make the resulting `ozf` file globally-accessible by putting it in a `public_html` directory. This can also be done in the interactive user interface:

> `declare
> AgentServer=
> functor
> ... (body of functor definition)
> end
> {Pickle.save AgentServer "/usr/staff/pvr/public_html/agents.ozf"}`