|
このページは大阪弁化フィルタによって翻訳生成されたんですわ。 |
Other methods are provided to govern and refine your agent. For
instance, every agent is equipped with lookup modules, which give your agent
the capability to investigate its network surroundings. There are also modules
designed to work specifically with specific infrastructure components such as
matchmakers and logging agents. We will explain how to work with these events
Arial'>
Every agent is configured with one or more file-logging modules. These modules
provide detailed information to external entities as to the functioning of the
agent. The file-logging module allows an agent to stream internal events to a
Installation Instructions, below, for how to manage the behavior of logging
modules). All log-files are created by the agent in a directory with the name
of the day and month on which the agent was started.
All agents built with the
AFC maintain PID files in the RETSINA system directory. The PID provides for
the following functions:
1) It assists agents in
identifying other agents running on the same platform. If it is programmed to
communicate with a user via a voice, for example, an Interface agent should be
able to find a SpeechAgent running on the same system.
2) It allows agent
management tools to rapidly see what agents are running and what agents have
crashed, by providing a comparison of the file entries with the list of agents
actually running on an ANS server.
To use the RETSINA Agent
Foundation Classes you will need
The
version of the RETSINA Agent Foundation Classes as described in
this manual requires that the following applications are present prior to
installation:
1. Visual Studio 6.0 (this has to have been run at least once prior to AFC
installation)
2.
Java 1.2 or higher (runtime environment)
To run agents on your own
computer only, you do not need to be connected to a networked domain. To
section below), you will need a live Ethernet connection.
When we refer to Agent Name
Servers below, we mean an agent infrastructure component that can reside
locally on your machine. You can register with an ANS server on your own
machine; you do not need to be connected to a specific network to connect to an
ANS Server, but in order to find and communicate with other agents, you will
need to find and register with non-local ANS servers using Discovery.
1.
2.
3. 
4.p; Please read and
accept the CMU licensing agreement.
5.p; The Read-me file
will appear. It contains information on the latest updates, which may not be
reflected in this manual. It will be stored in the directory for the software.
margin-left:63.0pt;margin-bottom:.0001pt;text-indent:-63.0pt;mso-list:l20
level1 lfo1;
tab-stops:list .5in'>6.p; The next GUI is
for setting the installation path. This path designates the root location where
all the libraries, files and examples will be stored. The default path is
C:\program files\RETSINA. You can change this path, but we recommended that you
do not.
7.p; The next GUI is
margin-left:63.0pt;margin-bottom:.0001pt;text-indent:-63.0pt;mso-list:l20
level2 lfo1;
tab-stops:list 1.0in'>a.p; Administrator
(for WinNT 2000 and XP, for installation of multiple users).
b.p; Compact:
installs the smallest configuration necessary to build agents: for users with
limited drive space or who do not need agent examples. Not recommended for
first-time users.
c.p; Custom: To
choose components. For experienced users.
d.p; Typical: To
install complete set of files. This is the default and recommended installation
setting.
8.
When you don't specify
anything at this point, but instead just click 'OK', the
visual logging module will be initialized with a default name. This is normally
'DemoDisplay'. After you've selected a different name for the target agent,
click
on 'Commit'. This will ensure that information is propagated to the agent code.
If no ANS server was specified using either one of
the configuration files or command line parameters, the agent will pop-up a
dialog box. You can use this window to register with an Agent Name Server.
Choose a server from the list, or enter a new one. Then
press 'register' and the agent should inform you whether or not the
registration process was successful. Use the 'unregister' button if you
accidentally register with the wrong server. This process will not affect the
already-running agent. When all goes well, the dialog should look like the
dialog box in the second figure, below. The list of agents you will see in the
drop-down box is obtained from the RETSINA system directory. We provide more
information on this in the following sections.



After Successful Registration
PART
II: EXAMPLES
Now that the AFC software is
installed, you should now be able to test your agent system by running the most
basic agent examples. The test will verify that the system is properly
installed, while also demonstrating a basic communication between agents.
Note:
the RETSINA agents will function without the DemoDisplay, but you will not
be able to easily verify the functioning of the agents.

AgentB will appear with AgentA on the DemoDisplay:

The agents will automatically register with the ANS server, and will begin to
pass a series of messages to each other based on a simple pattern: AgentB will
AgentA will wait two seconds and reply with B2 +1. AgentB will wait
three seconds and reply with A2+1, etc, until you quit one of the
agents.
7. Double click on the ANS server icon in the system tray (in the Windows-only
version of the ANS server only) to check the registration of the agents. A
window like the one below should appear, which shows the Hostname and port, the
type="#_x0000_t75" style='width:354pt;height:306pt'> 
Now that you have run the
first example of a RETSINA agent system, we will
show you how to build that
example using the Agent Foundation Classes and
Visual Studio. In this example,
difficulty, you can always refer to the agent code in the actual examples
provided.
Both AgentA and AgentB are
identical in that they take in a number wait for the number of seconds
indicated, add one to the number and send it back to the receiver. The only
difference between A and B is that A starts the sequence. This means that
AgentA needs some additional code to begin the dialog with AgentB.
We will go through the
example by showing what parts were added to the files generated by the
workspace.
Once you have the full set of agents as used in step 1, we will explain how the
added code works together with the AFC to create the small agent system. Let's
mso-position-vertical-relative:text'> When clicking ok you should see a dialog where you can choose what sort
of graphical user interface style you would like to use. We strongly suggest that you construct these agents with the use of a
Visual C++ guide. We chose to create a Dialog based agent for this example. The
agent is labeled AgentA. Once you致e navigated through the configuration dialogs you will end up
with a screen similar to the figure above (AgentA Workspace). It shows the
newly created agent project and an empty dialog window that can be used by the
agent. A status bar has already been included, which will show all the messages
generated by the AFC components and modules. These files mirror the messages
logged to disk. With the AgentA project a number of files were created. Most of these
files are particular to Microsoft Visual C++ and can be used to connect any
visual code to the agent code. The files you should be seeing in the file pane
are: c_AgentA.cpp, AgentA.cpp, AgentA.rc, AgentADlg.cpp and StdAfx.cpp. These
are the basic source files. The actual agent code is contained in c_AgentA.cpp.
It contains the implementation of an agent derived from CBasicAgent. Comments
are included to explain the behavior of the example code. In a previous section we
called AgentA, which is directly derived from our basic agent. As we progress,
you will become more familiar with the different kinds of agent derivations and
their functionality. We will introduce information agents and middle agents.
All of these classes are based on the basic agent and you will therefore need
to understand how to develop with this class. If you do not already have
style='mso-special-character:line-break'> Note: All agent-related files start with 'c_'. This is
graphical-interface-specific functionality. The Agent Application Wizard
created two files for you that encapsulate the actual agent. For AgentA these
should be: c_AgentA.cpp and c_AgentA.h. Open up the file c_AgentA.cpp in the
editor. You will see a large number of comments. These comments indicate what
particular part of the agent is active at any one time. Open the file c_AgentA.h. In
this file you will be able to see what is needed to build an agent. For our
example, we only need to add the declaration of two variables. Add the
following code to your class: Open the file c_AgentA.cpp
and find the constructor definition for this class. Change the content of the
constructor until it looks like this: BOOL CAgentB::process_message (char *data) The basic agent calls this
method when a message arrives. As you can see, it is left empty by the Agent
Application Wizard. Add code as the content of this method, so that the final
font-family:"Courier New"'>BOOL CAgentB::process_message (char *data) } Let's examine the additions
we have just made. The first thing you should notice is that the method returns
a Boolean value. This is important when you start to build more complex agents
or when you build agents that other people will build upon. If your agent code
returns a TRUE value to the basic agent, this indicates that the method
processed the message. In other words it tells the developer who uses your
agent class that the message was meant for this class, and not for the derived
agent class. Next, we enable our agent to
(data) If this method fails the
parser and tells the basic agent that it did not consume the message. If it was
able to parse the message, it needs to find two important fields: sender and
content. The sender field will tell our agent where to send the reply and the
content will give our agent the value of the number. (Remember that AgentA and
AgentB are identical, so the code you see here is also found in AgentB). Our
agent checks to see if the sender string is not NULL and then proceeds by
parsing the content field. One thing to remember about
the Agent Communication Language is that any field can contain a number of
other fields. In this case the content field contains the number field we
created in the timer method. We create a new parser called r_parser and we call
(content) If this method succeeds, we
should be able to retrieve the number field from the r_parser. Look for a line
that says: This will retrieve a pointer
to a string called number from the parser. If we constructed our message
properly the number string should point to a text representation of our number.
The last task we do is to convert the text representation into our own variable
change the step value We have changed the step
variable and now the agent can wait the amount of seconds this variable
indicates. You should now be able to
build AgentB. The only difference between the two agents is that the
constructor for AgentB looks slightly different than for AgentA. Here is the
font-family:"Courier New"'>CAgentB::CAgentB(char *a_name) : CBasicAgent
(a_name) In Example 1 we demonstrated
a basic multi-agent system consisting of two agents, both of the same type. In
the RETSINA architecture we define 4 basic agent types: Note: There are four main ways of soliciting information
from Information Agents in the RETSINA agent community, each with their
corresponding Information agent behaviors: In this example, we use the
versions of AgentA and AgentB as found in the Step 2 folder). AgentA sends a message to
DateTimeAgent to start-up the active monitor query. The monitor query is set at
20 second intervals, but the programmer can set the value at any interval, to
as low as 1 second. Every 20 seconds, the information agent informs AgentA of
the current time. A-B messages are interrupted by the time monitor replies. This
sets the second counter in AgentA to zero. AgentA and B communicate as in the
above example (message+1). First we will demonstrate
how to create the new Information Agent. Then we will show you how to integrate
this new agent into the scenario. Start by re-creating AgentA
and AgentB, or copy the two projects to a new directory. Create a new workspace with
the RETSINA Application Wizard, naming the project DateTimeAgent. This should
produce a new workspace with the files: c_DateTimeAgent.cpp and c_DateTimeAgent.h As in the first example,
look at the header file that holds the new agent痴 (Information Agent)
declaration. Open the file called c_DateTimeAgent.h. This file will appear to
be very similar to that of the other agents you have built so far. To make the
agent an Information Agent, you need to change the base class to look like
this: class CDateTimeAgent : public CinfoAgentBase The
new agent will have all of the normal event methods as defined by the basic
agent, and will have the additional capabilities of the Information Agent. When
we are dealing with specific agent types we do not need most of these methods.
In fact, in our example we can remove all of the methods and replace them with
one single event method. The Information Agent as defined by the RETSINA
will call the external query function. Add
an entry to your agent in the protected area and call it: char
*external_queryfunction (CLList *); b_message
of type string. In your code this should look something like: private: Open up the file
c_DateTimeAgent.cpp. Since we are dealing with a
more specialized agent here, we do not need a lot of the overhead we used in
the other agents. In fact we only need to add code to three methods. First of
all we need to initialize the string we will use to communicate the result of a
query. Find the constructor of the agent and add the following: This code cleans up the
memory that was used to create the replies. So far, we have introduced basic task agents
(A and B), and an information agent (DateTimeAgent). We have tested and built
these agents, and observed their communications with each other. We will now
introduce one of the most important components of the RETSINA MAS, the
Matchmaker. The Matchmaker is an agent that helps make connections between
agents that request services and agents that provide services. The Matchmaker
serves as a "yellow pages" of agent capabilities, matching service
providers with service requestors based on agent capability descriptions. The
Matchmaker system allows agents to find each other by providing a mechanism for
registering each agent's capabilities. An agent's registration information is
stored as an "advertisement," which provides a short description of
the agent, a sample query, input and output parameter declarations, and other
constraints. In this example, AgentA does not know the
name and location of the DateTimeAgent, and will have to find it, using the
Matchmaker. The Matchmaker will find the DateTimeAgent in response to a request
from AgentA for an agent with date/time capabilities. It deliver the requested
agent capability in a reply to AgentA. This example will build on the agent
scenario from Step 2. In order to demonstrate the functionality of the
Matchmaker, we will have to start a different version of the task agent, one
that does not know the DateTimeAgent (i.e., does not have hard-coded
information on the DateTimeAgent in its cache). Be sure to use the AgentA and
AgentB versions as found in Step 3. 1. Start the ANS server. 2. Start the DemoDisplay. 3. Start the Matchmaker: Program files\RETSINA\tools\java
GinMatchmaker 4. Start the DateTimeAgent. The DateTimeAgent will
advertise its capabilities with the Matchmaker. (This passing of this
advertisement will not be discernable on the DemoDisplay). 5. Start AgentA (from the step 3 directory). Upon
initialization, AgentA will query the Matchmaker for an agent that can provide
the date and/or time, as shown below on the DemoDisplay: This query starts the monitor query as in
Step 2. 6. Start AgentB (from the step 3 directory). AgentA and
AgentB will communicate as in earlier steps, interrupted by the DateTimeAgent,
which resets sequence as in step 2. Copy the projects and files from step 2 into
a new location. We will use these projects and files to build upon and extend
your agent's capabilities, so that it can use a middle agent. We need only make changes in order to extend
our basic agent痴 capabilities to include the capability of using of a middle
agent. Open the file c_AgentA.cpp and find the
process_init method. In step 2 the agent used this method to initialize a
monitor query with an information agent. In this step, the agent will request
that the Matchmaker deliver information about any agents that can provide the
time/date. Clean out the content of the process_init
method and replace it with the following code: Now that Matchmaker is aware that an agent
is available conforming to the request sent by AgentA, it will reply to AgentA
with the name and advertisement of the DateTimeAgent. In order for AgentA to
process this reply we add the following code at the very top of the
process_message method: CMatchmakerClient
*mmaker=get_mm_module (); This module will be able to tell us whether
the Matchmaker has sent a reply to the task agent. The following line -- First, we check to see if the client has
received a new advertisement, or in other words, news of a new agent: This example should serve to get you started
with basic Matchmaker interaction. As
agent-based applications move beyond simple test-case scenarios, the truly
dynamic and unreliable nature of the agent world becomes apparent. Peer agents
can act erratically, middle agents and infrastructure services may become
temporarily unavailable, and various aspects of the environment that the
programmer assumed would be constant, turn out to be unpredictable. While the
robustness of the agent code handles some of these difficulties, the
infrastructure of the agent community should help with agent adaptation to
ad-hoc and dynamic environments. As
we have shown, the RETSINA MAS utilizes middle agents (especially ANS server
and Matchmaker) to facilitate agent interactions. In addition to providing this
middle agent infrastructure, we have provided agents with an enhanced means of
locating and gaining access to them. A key technology that allows agents to
Using Discovery, agents and servers can automatically maintain dynamically
updated lists of available agents and servers. As agents, ANS servers and
Matchmakers come and go from the network, these internal lists are expanded and
contracted automatically. Agents can be initiated before an ANS server is
online, and instead of failing, they will register with an ANS server when one
becomes available and is discovered. ANS servers can be updated with knowledge
about agents from other servers, because these servers were able to discover
their peer ANS servers to provide redundancy. RETSINA
agent services utilize the Simple Service Discovery Protocol (SSDP) that was
developed as part of the Universal Plug-n-Play (UPnP) consortium痴 efforts to
support small/home and ad-hoc networking. This protocol is utilized at the core
services levels within the agent software libraries, to ensure that required
available, their presence is made known throughout the community.
Infrastructure services also use the Discovery protocols to coordinate
interactions between each other, to ensure that agent information is
appropriately replicated, load balanced, and/or accessible. We
will briefly describe the SSDP protocol, and then proceed to discuss the
within the Agent Foundation Classes (AFC) are described. Finally, we demonstrate
some of these features in action. The
Simple Service Discovery Protocol (SSDP) utilizes multicast transmissions to
allow systems to communicate with other nearby systems, without prior knowledge
of their existence or their specific locations (other than the standard
broadcast messages to tell other systems that they are 1) alive and available,
or, 2) leaving and no longer available. SSDP clients (systems that are seeking
to find services that advertise themselves via SSDP) will utilize multicast
messages to search for providers that offer a specific (or all) service(s).
SSDP service providers that receive the multicast search-request will send a
unicast message (one-way, non-multicast) to the requesting client, using the
return address that the client provided in its search. Unlike
other Discovery protocols (such as SLP, Jini, etc.) the SSDP architecture is
extremely lightweight. Responses to search requests are URL-style strings. A
problem with multicast transmissions is that many routers and firewalls limit
or prohibit their transmission. Given this limitation, the Discovery process
traveling any more than three hops along the network. This restriction
precludes problems that may arise from systems divulging internal numbering or
architecture information to malicious packet-voyeurs on the public
footnote'>[6] The
Agent Name Service was the first RETSINA infrastructure component to support
Discovery. As
some simple string-based pattern. Agents can choose to communicate with other
specific agents on the network in many ways, but they will ultimately request
that their agent communications modules create a network link to the remote
concerned with the specifics of the ANS client, just that it works). The
Discovery process, as described in the previous section, is composed of clients
and service providers, and their interactions. The Agent Name Service
implements various combinations of processes between the Discovery
latter feature allows ANS servers to discover each other in order to provide
various levels of peer information sharing. And finally, the ANS client (that
is part of every Agent) acts as a Discovery client, so that it also can
discover the available ANS servers. The
ANS client also implements both service and client Discovery interfaces to
susceptible to errors due to periodic outages of ANS servers, network links, or
from other routing problems. It also allows agent applications to begin
functioning without the existence of an ANS server, in case the startup
procedure sequence (start ANS server, start Matchmaker, start other middle
agents, then start agent applications) doesn稚 progress as anticipated. Once an
ANS server comes online, the auto-register feature of agent痴 ANS client will
Classes, a number of Discovery-based facilities allow agents to find each other
without prior existence of desired lookup services on the network. Each agent
is fitted with an ANS client and a Discovery client that act as part of the
AFC痴 lookup modules. These two lookup modules are used by the Communicator to
fill and maintain a common location lookup table. This table reflects the
agent痴 view of the network. When an agent wishes to send a message to another
agent, it will give the message to the Communicator and indicate the target
agent. The Communicator in turn will either directly send the message, if the
target痴 location information is available, or temporarily store the message,
and send out a request for the target痴 location information. This location
request is handed to all available AFC location modules. When an answer is
obtained and the location lookup table has been updated, the original message
will be sent. Since all available lookup modules work in parallel, and since
they all use the same data-structure, the dependence on a specific lookup
client diminishes. As long as there is at least one lookup client active, the
location lookup table will be refreshed. Discovery is an inherent component of the AFC. In some cases, however,
agent developers will want to disable Discovery modules. For example, a group
may be running sensitive experiments or demonstrations with a group of agents,
and will not want the ANS Server and/or the agents to be discoverable to
outsiders. You can configure the usage of both Discovery and ANS lookup in
agents.
You can also disable Discovery in ANS Servers. By default, both Discovery lookup and ANS lookup are enabled in the AFC
agents. But, you can override one or both of them by calling the method set_lookup_config and the proper parameters. The set_lookup_config overrides the defaults and allows the developer to set the specific
parameters desired for the functions. If you want to enable Discovery lookup only,
you would call the method and set the parameter: If you want to enable ANS lookup only, you would call the method as
follows: set_lookup_config (LOOKUP_ANS); If you want to enable both lookups, you would call the method as
follows: set_lookup_config (LOOKUP_DISCOVERY |
LOOKUP_ANS); The settings for agent ANS or Discovery lookup parameters also control
the enabling/disabling of an agent痴 discoverability by other agents. Thus, an
agent that has disabled Discovery lookup is also non-discoverable by other
agents. You can change the usage of lookup modules while the agent is running.
Every lookup module is based on the CLookupModule class. This class has the
following access methods: - Java ANS 2.7 - Java ANS 2.7 (no discovery).Managing a RETSINA ANS Server: ANS Server GUI Beginning with version 2.8,
other ANS servers. The GUI Screen has six
interlinked panels as depicted in the table to the right. Since an ANS server may know
about other ANS servers, you can, once connected to an ANS server, browse the
List and the Hierarchy Partner List are both lists of ANS servers maintained by
an ANS server. Both lists are preloaded from static files on server startup.
The difference between them is that the Discovery/Peer Servers List is
dynamically updated by the discovery mechanism after startup. The Hierarchy
Partner List is the permanent list maintained in the cache of the ANS server
for partners with which it regularly shares information. Entries in the
Discovery/Peer List are typically dynamic, and servers are removed if they
cannot be reached. Both are described more fully in the ANS v.8 document
entitled, "javaANS.PDF." (included
on CD distribution and on-line at: http://www.cs.cmu.edu/~softagents/ans/ANSv2.9.PDF) Once an entry appears
in one of these fields, clicking on it once will populate the New ANS Server
are provided, as well as to request that the server send out a new discovery
message ("ReDiscover"). The Agent Information panel
will have its name displayed in the "Agent Information" field. Double
clicking on agents in the "Registered Agent Names" list will perform
a lookup operation for the selected agent name, which will fill in the rest of
the boxes in the Agent Information panel (Hostname, Port/Socket #, Parameters).
Parameters include such agent information as the name; ttl= (Time to Live--the
number of seconds remaining in this registration's lease--a relative time);
expires= (the time stamp when the server will discard this registration or no
longer recognize it as valid -- in milliseconds of actual server time since a
certain starting point); type= (for agent type, such as: retsina:Matchmaker);
key= ( public key of agent); cert= (PKI X.509 certificate for agent). When a
lookup command cannot be resolved locally, the entries of ANS servers in the
Discovery/Peer Servers List will be queried
server, and the text box below it will show the actual server response before
it is parsed into appropriate GUI fields. You can enter any console command
manually and hit enter, and see the results from the server. The differences between the
two modes -- attached as part of a specific ANS server versus running as a
stand-alone management tool -- are apparent when moving towards a
manually initiate a server connection. In this example, we
demonstrate Discovery; agents discover the DemoDisplay, and each other, without
the help of an ANS server. The use of an ANS location module is disabled within
the agents. Their ability to find each other and is made possible by the
Discovery process. As we have mentioned, each
agent in AFC is fitted with a SSDP Discovery module. This module lives side by
side with the ANS module in the basic agent. The Discovery and ANS modules use
a common table to store location information. When there is no ANS module, only
the Discovery module will fill this table. The Discovery client will populate
the table with the replies to the look-ups that it sent out to the ANS Service
environment (received and replied to by agent service modules). The result is
that your agent will function quite happily without any lookup services on the
local network. This example is identical to
the previous example except that we added a line of code to each agent's
'Create' method, which disables the use of an ANS client module. Use the
agents from Step 3. 1. Compile the agents and
start the sequence as before. 2. Start the ANS server.
(The ANS server is needed for the DemoDisplay to visualize the agents. However
our agents will no longer use the ANS. No messages will pass to and from the
ANS). 3. In both AgentA and AgentB
locate the 'Create' method. Change the content (which should be empty) to: The AFC provides a complete
set of libraries that allow an agent to connect to MAS infrastructure
components and communicate with other agents. Through the AFC the interaction
with the infrastructure and other agents in the agent world is highly efficient
and fully automated. However it is up to the agent to make decisions on whether
and when to initiate a conversation with other agents. The AFC provides facilities
that allow the introduction of a problem-solving engine in the agent code, in
order to control the actions of the agent in an intelligent way. The task of
the programmer is twofold: 1. To link the agent code to a problem solving engine by
deriving the problem solver module from the class CProblemSolver. This class
provides some hooks that give easy access to the internals of the agent such as
the BeliefDB and the Communicator. 2. To implement the actions that will allow the agent to
operate in its environment. The class CPSActionCodes already provides some
basic agent oriented actions. More actions can be added by deriving a new class
from CPSActionCodes. The distinction between the
problem-solver class and actions class adds flexibility to the agent
architecture, because it allows the implementation of agents with exactly the
same action code, but different problem-solving engines. Thus these agents can
act differently because they think differently, and not because they have
different capabilities. On the other hand, the AFC allows the implementation of
agents that employ the same problem-solving engine but have different actions.
These agents think in the same way, but act differently because of the way they
perform their tasks. The class CProblemSolver
provides the basic methods that have to be overloaded to link problem solvers
to AFC-based agents. This is an abstract class that cannot be instantiated by
itself. To make use of the functionality of this class the problem-solving
engine used must be in a class derived from CProblemSolver. With the usual
constructor and destructor methods that should be implemented to provide access
to the problem-solving engine, CProblemSolver provides methods that allow
access to the main facilities of the AFC. 1. BOOL GenerateSolution() This is a pure virtual
method that must be defined in the child class and is used by the agent to
activate the problem-solver. In a typical agent this method would either contain
the core problem-solving algorithm or make calls to it seamlessly. 2. BOOL ExecuteActions() This is also a pure virtual
method that must be defined in the child class and is used by the agent to
execute the actions selected by the problem solver. This method basically
implements an execution engine that transforms the problem-solver
representation of the actions to the actual actions that can change the agent痴
feedback to the problem-solver, based on the success or failure of the actions. The AFC is not committed to
any particular relation between the problem solving and the execution. This is
left to the programmer who can choose to follow the traditional sequence of
first generating solutions followed by their execution, or a more sophisticated
interleaving of problem solving and execution. 3. CBelieveDB *GetBeliefDB() This method gives the
problem-solver access to the general knowledge base used by the agent to
mso-bidi-font-size:12.0pt;font-family:"Courier New"'>4. SetBeliefDB(CbelieveDB *) The internal AFC framework
calls this method to set the BeliefDB in the CproblemSolver class. The
programmer
can also call this method if the instance of the beliefDB ever needs to be
changed or removed. 5. CCommunicator *GetCommunicator() This method retrieves a
reference to the AFC Communicator to allow for any message that may need to be
passed to other agents in the MAS. The AFC framework sets the Communicator
tab-stops:list .25in'>6. SetCommunicator(CCommunicator *) The internal AFC framework
calls this method to set an instance of the communicator in the CproblemSolver
class. This allows the problem-solving engine to access the communication
facilities of the agent without the need for saving pointers to the main agent
shell. The agent programmer can also call this method in case the instance of
the Communicator needs to be changed or removed. 7. CPSActionCodes *GetActionCodes() This method provides access
to the action codes that may be used by the agent. This is a pointer to the
CPSActionCodes class (see below). 8. SetActionCode(CPSActionCodes *) The internal AFC framework calls
this method to set the action codes that may be used by the planner. The base
class for action codes is provided (CPSActionCodes), which has some basic
actions codes that may be called by the agent. The class CPSActionCodes allows
the programmer to implement actions that the agent can perform. A few actions
are provided that the agent can use to interact with other agents within the
MAS. More actions can easily be added by simply deriving a new action codes
class from CPSActionCodes. The basic actions provided are: 1. char *SendMessageToAgent(char *pszAgentName, char
*pszContent) This method sends a message
to another agent in the MAS. The return value is a string that indicates the
error message if there was an error in sending the message. The first argument
is the agent name and the second argument is the content of the message. 2. char *CPSActionCodes::SendMessageToAgent(char *,
char *, char *, char*, char*) This is an overloaded method
that can be used to send a message to an agent with more control over the
header. The arguments are a. Performative: This is the performative used in the
header. b. Ontology: This is the ontology descriptor used in the
message. c. Language: This is the language descriptor used in the
message. d. AgentName: This is the name of the agent that is the
recipient of the message e. Content: This is the content of the message. This example illustrates the
classes and their relationship in a simple agent that uses the facilities
provided by the CProblemSolver class. This agent will be called the
used to generate the agent workspace in Visual Studio, then the inheritance
will need to be changed from CBasicAgent to CPlanningAgent. The class for our
"c_afc.h" //////////////////////////////////////////// // CReasoningAgent Class Definition file used for
Agent // ReasoningAgent class CReasoningAgent : public CPlanningAgent { public: protected: }; The constructor of our
reasoning agent will contain the following code: CReasoningAgent::CReasoningAgent() } Assuming that our agent uses
a planner called MyNicePlanner, in a class derived from CproblemSolver, the
class for our planner will be as follows: #include
"c_afc.h" // CMyNicePlanner Class Definition file class CMyNicePlanner : public CProblemSolver { public: }; The
GenerateSolution() method of MyNicePlanner will be as follows: BOOL CMyNicePlanner::GenerateSolution() { //TRUE is returned. //if Planning fails then
FALSE is returned //The belief DB can also
be used while planning //and that can be obtained
by calling GetBeliefDB() } BOOL
CMyNicePlanner::GenerateSolution() //Use the plans generated
by the GenerateSolution() //method to execute them. //Action can be executed
by selecting appropriate //from the set of action
codes provided by the AFC. //This can be obtained
from the GetActionCodes() method. //GetActionCodes()->SendMessageToAgent(...) } Deriving
the Agent class from the CPlanningAgent gives the programmer the advantage of
having any incoming message from the agent space passed directly to the
planner. In other words the process_message() method of CPlanningAgent calls
the GenerateSolution() method of the CProblemSolver class every time a new
message comes in from the agent space. This
allows the agent to immediately reason about any messages that arrive from
other agents in the MAS. If the main agent is not derived from CPlanningAgent
(but from CBasicAgent), then the programmer will need to add code to route the
messages to the problem-solving engine, code that calls
CProblemSolver::GenerateSolution(). In the following example, we
show agents interoperate and negotiate in the process of an auction. This demo
shows how developers, using the AFC toolkit, can deploy a fairly sophisticated
and user-friendly set of agents and scenarios, as applied to a real-world
market setting, without having to develop the underlying agent architecture and
infrastructure. The negotiation protocol as demonstrated in this example is a
simple one, but developers can modify the protocol as the situation warrants
it. Premises underlying the
demo: In all the example so far,
we have assumed that you have been running all of the agents and infrastructure
components on a single machine. The ANS, DemoDisplay and agents were compiled
and started in sequence on the same CPU. However, for various reasons,
including limitations of either memory or CPU power, you may need additional
resources to execute all components at once. Since we are building multi-agent
systems, we should be able to distribute the agents over a number of machines. In this section we will show
you how to setup a number of computers to run your agent system. We will use
three systems to distribute the agents from example 1. Below is an overview of
the intended setup: In
example 1. we use the following infrastructure components and agents: 1. Agent Name Server 2. DemoDisplay 3. AgentA 4. AgentB The
list above also indicates the starting order for this particular example. Our
objective is to keep the ANS and DemoDisplay on System C and move Agents A and
B to systems A and B, respectively. We will not need to change the settings for
the ANS and DemoDisplay since they will connect to the machine they reside on.
However, we need to tell AgentA and AgentB to register with the ANS on System
C. Before
you can edit the configuration of those two agents, the following must be in
place: 1. The AFC must be installed on all host machines 2. You need the IP address of System C. The
first step is described at the beginning of this manual. The
second step will need a bit more explanation. Every machine on the network has
an IP address that uniquely identifies that system world-wide. You will need
this address to connect to an ANS on a remote system. Go to the machine that
you have designated System C that holds ANS. If you are running windows NT,2000
or XP start a command shell and type: ipconfig, at the prompt: In
this case the IP address is 128.2.213.149. If
you are using the AFC under Windows 95 or Windows 98 then you will need to type
winipcfg to obtain the same information. If you do you will see a dialog box
that looks like: Now
that you know the address of the machine that runs the ANS, you will need to
change both AgentA and AgentB so that they use that address to connect to the
lookup service. Navigate
to the RETSINA directory (on Systems A and B, for Agents A and B,
respectively). You should find a subdirectory called: Examples\Steps\Step1\AgentA. In
this directory you should find a .bat file called run.bat. This the generic
name for the start scripts that we use to run our agents. Open that file in a
text editor such as notepad. You do you should see the following text: AgentA.exe -name AgentA
-port 6673 -ans 127.0.0.1 -ansport 6677 -ddp DemoDisplay You
should notice that the ans field is set to 127.0.0.1. This address is a
reserved for a local host, i.e., the machine on which the agent is currently
running. If you want to have the agent use a different ANS, you need to fill in
the IP address that we obtained from the steps listed above. Change the IP
address
and save the file. Do the same for AgentB, which you should be able to find
under: Examples\Steps\Step1\AgentB\Debug. Of course, you need to edit
the file for AgentA on system A, and the file of AgentB on system B. Once all
of these files are complete, start the scenario as explained in 摘xample
10.0pt'> PART
III: Examining Your Agents Each agent goes through a
number of phases or lifecycles. These lifecycles are illustrated in the code.
You can use them to activate and manage your agent as it becomes active in a
multi-agent system. What you should notice when you look at the code is that
you are given events at every point in the source. Events are generated for
incoming messages, for timers and even for certain startup procedures. Here is
a brief overview of the events you will see in your agent: Agent construction. This is basically your agent
constructor. Use this as you would any normal constructor. Be aware however
called: CAgentA ( ) At this point in the agent's lifecycle the
arguments for the basic agent have already been processed and you now have the
opportunity to handle custom parameters. You are given these parameters in the
form of a list of CParameter objects. If you want you an also retrieve the
called: void handle_parse_args (CCommandLine *a_commandline) When this event is
triggered, the basic agent will create a number of important objects. For
example, the Communicator object is created and initialized (but not started).
The BelieveDB (belief system) is created and filled with basic information like
agent name and location. All of the core event modules have been created and
assigned to the Communicator. The main agent logfile is created and a timestamp
is set. You can find this file in the system directory under you RETSINA path.
The Communicator has been given a number of lookup modules to assist it in
finding agent locations through multiple sources. (You will learn more about
process_create (void) Now that we have all the
components in place internally, we can begin to start the agent. When you have
arrived at this point in the code the following events will have taken place: The Communicator was started and you are now
registered with a lookup service. b. A number of event modules are now active and have
registered with other agents if applicable. For example. the DemoDisplay module
will have contacted a visualization client or a logging server, depending on
the visualization setup. If a Matchmaker module was configured, it will have
advertised the agent's capabilities with one or more Matchmakers. Each agent is given a one
second resolution timer. This timer is triggered for the agent so that it can
do maintenance. For example, it is used by the information agent base code to
font-family:"Courier New"'>Method called: void process_timer (void) Your agent has been told to
shutdown. This could have been done through a variety of means, such the user
interface, or through a message from other agents or agent facilities. Certain
emergency shutdowns will also trigger this event. When you arrive at this point
in the agent's lifecycle your agent will still have access to other agents and
agent facilities. Be careful what actions you take when your agent is in this
stage. In such a scenario you might not be able to rely on communications. For
example, your agent may not be able to inform other agents and/or facilities
that
it is going to shut down. Method called: void process_shutdown (void) All information regarding
agent location, agent type and advertisements are collected and stored in what
is called the network beliefdb. The network beliefdb is the database that
represents the agent痴 beliefs about its environment. This network beliefdb is
a part of the global beliefdb as provided by the AFC. Remember that the AFC does
not place any restrictions on what a belief should look like. As we will show
in the following example, it only provides means to maintain and manage
beliefs. Understanding the structure of this dataset will greatly enhance the
capabilities of your agent. First, let's take a look at
a simple code fragment that lists all the agents that your agent is aware of: Method called: ~CAgentA ( ) AFC contains code for
detection of newly arrived agents and detection of agent shutdowns. In order to
enable the function, add the following method in your main path, which will be
called every time the network beliefdb is changed: In the AFC we represent the
description of an external agent in a CServiceInfo class. This class contains
all information needed to use this agent. The network beliefdb is an
enumeration of CServiceInfo instances. For every agent on the network of which
your agent is aware, there will be one such service object. Each of those
objects contains a status parameter, which indicates whether it was recently
created or whether it will be destroyed. If the status flag indicates that the
object was just created, then the agent it represents just arrived on the
network. If the status indicates that the object will be destroyed in the next
main cycle, then you know that the remote agent either crashed or shutdown.
Remember that the removal of an agent is not synonymous with a remote agent
shutdown. Internal leases and expiration mechanisms can also trigger the
removal of a CServiceInfo instance. In the previous section you
may have noticed a line in the example code that looked like: The AFC provides a number of
mechanisms do facilitate persistent connections. This capability was previously
undocumented since it had not passed tests that were successfully completed on
demonstrate the steps to take to set up a persistent connection. At this point, we advise
against overloading the four methods listed above, at least until you are
familiar with the workings of the CLogFacility class. We've included the detailed information here
to give you better insight into the inner workings, in case things go wrong in
your agent. You should be able to determine from the logfile the state that the
agent is in and how your client is responding to those events. Open-network MASs face
security threats from malicious agents. These agents may try to unregister
their competitors from Agent Name Servers and Matchmakers, eavesdrop on
supposedly private communications, and spoof other agents and agents and the
humans who deploy them. System integrity demands that agent users be held
accountable for problems caused by misbehaving agents. While in a future release of
our ANS, the security architecture we are developing will counteract these
threats by binding each agent to a unique Agent ID (or AID) (see JavaANS), in
the current release of the AFC agents and ANS, we rely on the integrity of the
agent users in the community to prevent such malfeasance. To prevent agent spoofing or
the agent name would be miker.areolis.ri.cimds.cmu.edu Note that the agent need not
be running at this location. The agent name is merely used to signify that the
agent user痴 name and originating domain, not the location at which the agent
integrated into the agent communities, it is necessary that all agent users
adhere to the above-referenced naming convention. This is especially the case
for those users/agents enabling Discovery of/by other agents and agent systems.
(See section on Discovery for enabling/disenabling Discovery). The other users of the agent
community will regard users who choose to ignore or subvert the agent naming
convention as hostile and will treat them accordingly. Users who purposefully
unregister or register agents not belonging to them will also be regarded as
hostile to the agent community. We have added an additional
command line parameter to the BasicAgent, which will allow you to make your
agent name unique. If you start your agent the normal way then the name you
specify on the command line or hardcode in your agent will be used in
registrations 'as is'. However, if you specify the following parameter: PART
IV: VISUALIZATION TOOLS Before you can use any of
registered itself with one of the ANS servers. (See the documentation of 鄭NS
mso-position-horizontal:left' coordorigin="7200,12960" coordsize="3133,1461"
wrapcoords="-207 -223 -207 14029 21703 14029 21703 -223 -207 -223"> First of all we need to give
use. The following example is of an agent for local use only). The listening
port does not have to be globally unique, but you cannot use the same port as
other
applications on your machine. By default the port is set to 6678. In this example we set our
agent name to AgentA, as shown in Figure 3. Next we select an Agent Name
properly set the In this section we will
demonstrate how to send messages to other agents. As we have mentioned above,
the top part of the application痴 window is dedicated to message sending and
receiving. On the left are controls to create the messages and on the right are
two message boxes that will show different views of the messages coming in. In the instructions that
follow we assume that you have registered at least two agents with an Agent
Name Server, and that you have launched at least two Win32KQMLCenter
information. Configure the
"KQML" Section to send a message to another agent. The
"KQML" Section consists of the following fields as shown in Table 1: Performative The set of permissible
operations that agents may attempt on each other's knowledge and goal stores.
Examples include: "tell," "send" and "insert." Receiver The name of the agent that
will receive the message. Content The content of the
message. Reply-with A string of automatically
generated characters. Each message generates a unique identifier. Pressing
"generated string" will generate a different message ID. In-reply-to A blank field for entering
a message's unique identifier. This field can be used to reply to specific
messages. Ontology The ontology that the
agents will use to communicate. Examples include: "satellites" and
"stocks." Language The type of parsing
language (e.g. gin 1.0) that the agents will use. blank template A menu for selecting
different kinds of generic messages (e.g., advertisements). This menu item is
not implemented. send message The button that sends the
message to the agent specified in the "Receiver" field. The required fieldsare: Performative, Receiver, and Content. The optional fields are
Reply-with, In-reply-to, Ontology, Language and blank template. The default
settings for the optional fields are sufficient to test message formats to
agents. Scattered across the
application痴 window are a number of small tools that can give you information
about the environment and the internals of the application. Figure 11 shows
three buttons that are used to display system information about the software
that was used to build the message tool. It is available in every agent and was
designed to obtain low-level information about an agent痴 state and condition. Figure 12 is the
Communicator info window. It can be used to quickly obtain your network
settings like IP address and local listening port. When reporting a bug within
the Agent Foundation Classes or Communicator code, please provide the version
number listed in this dialog box. PART V: Data Structures, Tools and Utilities The Agent Foundation Classes
support all basic concepts of software engineering. A variety of well-known
structures and concepts are provided with agent development in mind. In this
section, we will introduce each of the provided data structures and demonstrate
how to use them. We will also show how they fit into the agent paradigm and
what other important tools within the AFC depend on them. First of all, we need to
describe and explain a basic concept of the AFC, known as the CListElement. The
name is deceiving; the CListElement is not an element designed to be used in a
list. In fact, it is used as the basis of virtually every class within the AFC.
While there are a number of other classes beneath the CListElement class, they
safely substitute CListElement for their class names. The CListElement supports
a number of elementary methods used by all classes derived from it. These
methods are: These pointers are private
and can only be obtained and changed through the access methods. All point to
objects of the same CListElement type. Following the access methods
are two methods to manage a content pointer. The actual content is not stored
in the object but rather a pointer to the location of the content. This means
that when a CListElement object is destroyed the memory that the object points
to will not be freed-up. You will have to manage this memory yourself. However,
certain classes derived from CListElement cast this pointer to specific data
types that are destroyed when the destructor is called. The CParameter class,
for example, assumes the content pointer points to a string. The last two methods are the
basis of what we call Collapsible Data Structures. These methods enable an
object to collapse itself into an ACL-formatted string. The methods can also be
used for recreating the object itself from a string. For now it is important to
know that every class derived from a CListElement has this behavior. There are two more important
methods that define the basic behavior of the CListElement. These methods are
not listed above because they are inherited from the CDNA class. However for
all intents and purposes they are part of the CListElement. The declaration of
the methods is as follows: Every class has a label that
can be accessed by these methods. It is not always necessary to give an object
a label and omitting one will not interfere with the functioning of the object.
The label/name is stored as a private string and cannot be accessed directly.
Certain derived objects will not allow you to change the label once it has been
set by the constructor. This characteristic protects the persistence of certain
objects. There is one final method
that can be used to obtain certain low-level information about the object. If
you decide to construct your own basic types then you will have to become
familiar with the other methods that related to this set of functions: Every object in the AFC has
a base type. This is either a namespace string or a numerical ID. In the cases
of the most fundamental types, a number expresses the type. The method above
can be used to obtain this type. There is a finite set of types defined by the
AFC, which lists the most basic types of data-structures. These are: Another component similar to
the CListElement is the CTreeElement. This element can be used in binary trees,
listing above, the tree element is designed to be used in a binary tree. (We do
not provide more complex trees and tree representation, since we do not want to
dictate to developers what these structures should look like). Every tree element contains
three nodes of a similar type, to represent the tree. These are: - A parent node - A left leaf node - A right leaf node Two other commonly used
datastructures are available, the queue and the stack. Each of these classes
are based on the CLList class and will therefore have all the functionality of
that class. First let痴 take a look at the queue termed CQueue in the AFC. Only
four methods are needed to turn an AFC list into a queue. We need a way of
fixing the size of the queue and we need to add and remove elements from the
style='font-family:"Courier New"'> In this section, we will
discuss a number of tools available within the AFC libraries that considerably
facilitate agent-based development. CGUID *uuid=new CGUID; printf ("Newly generated ID:
[%s]\n",uuid->get_guid ()); delete uuid; #include "c_afc.h" CGUID *uuid=new CGUID; delete uuid; When using the class you
will notice that the id's the objects generate are like Windows registry keys.
This was done intentionally because it is much easier for a developer to strip
the outer parenthesis than it is to add them after the string has been created.
Let's take a look at an example on how to convert a GUID to a general uuid
string: CUtils utils; CGUID *uuid=new CGUID; printf ("Newly generated ID:
[%s]\n",uuid->get_guid ()); char *stripped=uuid->get_guid (); printf ("Stripped ID:
[%s]\n",utils.remove_curly_brackets (stripped)); delete uuid; The output of this code
should look something like this: Newly generated ID:
[{8D831E25_1DEE_11D5_A944_F95168027CA4}] Stripped ID:#include "c_afc.h" CUtils utils; Obtaining the RETSINA
variable and home directory of the agents As you may have noticed, the
RETSINA agents depend on an environment variable called RETSINA. This variable
path from the RETINSA variable. if (home==NULL) else CLList *filelist=utils.file_list
(".","*.txt"); if (filelist==NULL) } while (temp!=NULL) { delete filelist; The benefit here is that the
files are stored within a CLList as CListElements. You can use the tools that
operate on these objects to accomplish more complex tasks. We致e separated the listing
of files from the listing of directories to rule out any confusion about what
is maintained in the listing. Also, the AFC has to compile on a variety of
platforms, and not all platforms support a physical file-system; a directory
listing may mean something completely different on a mobile phone. The example
below demonstrates how to obtain a listing of all sub-directories in the
current directory. CFileBuffer filebuffer; The CFileBuffer class (shown
above) may seem a bit strange at first. It is the first version of a class that
will be managed by something called CIOBuffer. This class will present a
virtual io layer to the agent, which allows it to load resources using URL's.
The CIOBuffer will then instantiate the appropriate base class to do the actual
work. The AFC contains tools and
utilities that are not necessarily designed for agents but will nonetheless
assist and expedite development and research. In this section we will explain
how to use the CDBFileIO class, with the accompanying class: CDBRecord. These
tools were initially designed to give agents quick access to flat text-based
database files. One of the later versions of the C++ used these classes to
maintain a permanent cache file of known agent registrations for persistence
purposes. Later, when the AFC started adopting the 'to_string' and 'from_string'
technologies, another capability was added. Every database record and every
database using those records can be collapsed into an ACL formatted string,
which can be sent to another agent and expanded into an internal
data-structure. Before we explain how the
database class works, we need to demonstrate how a record is defined and used
within the AFC. The public appearance of this class is: You will need to create a
new socket class based on one of the pre-defined AFC sockets. The possible
socket base types are: The CSocketBase and
CDataGramSocket behave identically and support the same API. For the third
type, you need to add two more methods to configure it: Since
MASs are largely characterized by the use of messages being exchanged between
agents in text format, we provide a number of tools to make development easier.
The following tools demonstrate utilities to manipulate strings. When working with KQML or
XML messages, it is useful to know whether or not the content of a field
contains readable characters. You can use the code shown below to determine
whether a string contains white space only. In the AFC the parser,
classes will use string tools to create strings from lists and lists from
strings. Any CLList object containing objects derived from CListElement can be
expressed in a string, and any string containing elements separated by a
elements. The code below demonstrates the creation of a list from a string of
("retsina.agent.middleagent.matchmaker",".",result); CListElement *temp=NULL; An ACL-formatted string
encodes assumptions about the contents of the field stored in the string. Let
us assume that it does not matter whether or not the fields are stored as
uppercase or lowercase, but that the internal matching does depend on
uppercase. It may then useful to convert
an entire list to uppercase. The following code fragment demonstrates how this
can be achieved. The resulting labels of the elements in the list will all be
in uppercase. CListElement *temp=NULL; Below is a fragment of code
you can use to display the contents of the labels of a list: CListElement *temp=result->get_first_element (); String Manipulation IMPORTANT! The methods
listed above work directly on the strings you provide. This means you cannot
use statically declared strings as parameters. Be careful with these methods The AFC contains two main
classes to facilitate web-related development: the URL class and a web-socket
class. First, we will show the URL class. Then we move to a web-socket class
that can be used to obtain the contents of a URL or URI. Let us first look at
an example of a simple URL operation: The CURL class is based on
an unfinished CURI class, which is more generic than the URL and does not
understand concepts such as port number and CGI scripts. Now that we have an
object we can use to represent the location of resources on the web, we can
start to use the CHTTPSocket class to retrieve this data. You can provide a
pointer to either a CURL object, or to a string containing the formatted URL. JADE RETSINA Bee-gent Objective FIPA-Standard Middle agents allow
heterogeneous agent types to interoperate successfully. To provide a coordination
mechanism for already-existing applications and databases Version JADE 2.4 and LEAP 2.0 AFC SDK 1.15 Bee-gent 2.1 Organization CSELT and LEAP Project Robotics Institute, CMU TOSHIBA Corp. Language Java C++,C,Java Java Platform / OS Any platform where Java VM
is available Windows, SunOS, Linux Any platform where Java VM
is available Agent
Intelligence Interaction
Protocol Useful behavioral patterns
are provided. Also, FIPA-defined protocols are implemented in advance. A specific Interaction
Protocol handling module cannot be found. IPs are described in UML
state diagram and sequence diagram in the visual editor. Also, some patterns
are provided. Planning facility Provided by JESS (Java
shell of CLIPS) Generic planning facilities
are provided Provided. It's similar to
JACK. Reasoning facility Provided by JESS (Java
shell of CLIPS) It seems to be there, but
not in AFC SDK. Provided. It's similar to
JACK. BDI model Agents' mental states are
implicitly expressed at the execution of Interaction Protocol. Belief DB is present for
agents and developers to use. Provided. It's similar to
JACK. Agent utilities /
tools Federated FIPA-DF with GUI ANS (currently not
federated), UPnP discovery as alternative to ANS LDAP wrapper is provided. Others JSP support Matchmaker, User profile
management, GPS tool, NLP, Grid display, Servlet support Message transport IIOP (CORBA), Http (SOAP is
not yet), TCP/IP socket for inter-platforms. RMI for intra-platform. SMTP and
WAP are under development. TCP/IP socket Http (SOAP is not yet) Security feature
(tampering and leakage of communication) " Today no security
aspect has been implemented in JADE. Good work is however ongoing and we
expect to introduce security at the end of 2001. " No specific security
component implemented. Provisions are present for future implementations. Safety feature I cannot find the
description about safety. I cannot find the
description about safety. Snapshots of agents to be
taken at any time Mobility support Possible within a platform Planned in future versions Possible Device support LEAP (a subset of JADE) is
for J2ME-CLDC and pJava. Smallest agent ever built:
32K for PalmOS Now implementing for cellar
phones which installed J2ME-CLDC Architectural
feature FIPA architecture, i.e.,
AMS, DF, MTP, etc. Containment of Agent Shell,
Agent Container and Modules. Pre-defined Agent Shells are shipped with AFC. Wrapper architecture.
Stationary agents can wrap the applications and mobile agents go around them. Standard spec. FIPA None FIPA (agent message only) Development,
Monitoring and Management Development tools The following monitoring
agents can be also used for debugging use. Integrated into Visual C++
6.0 for Windows Interaction Protocol and Rules
for Planning and Reasoning can be described in the visual editor. Monitoring tools Platform administration
agent, message inspection agent, and message tracking agent with a graphical
interface. - MessageCenter allows you
to send custom messages, debug ACL messages, and interact with an ANS.
DemoDisplay allows visualization of agent interaction. MatchMaker Tools allow
investigation of middle agents Web browsers-based
monitoring and management tool for agent current status and messages Documents Administrator's Guide,
Programmer's Guide, (lots of) Tutorials, Java API document, FAQ, etc. AFC Developers Guide Getting Started, Tutorial,
Tools Manual, Java API document, FAQ, etc. Source code Yes No No Samples 14 examples (Ping to JESS) 16 example (GUI agent to
Matchmaker to Auction to Planning) 11 examples (Hello world to
Planning) RETSINA Software License CARNEGIE MELLON UNIVERSITY NON-EXCLUSIVE END-USER SOFTWARE
shall be void. 6. Loan, distribute, rent, lease, give, sublicense or
otherwise transfer the CMU Software (or any copy of the CMU Software), in whole
or in part, to any other person or entity. 7. Alter, translate, decompile, disassemble, reverse
notices or startup messages contained in the CMU Software. 9. Export the CMU Software or the product components in
violation of any United States export laws. Title to the CMU Software, including the ownership of all
copyrights, patents, trademarks and all other intellectual property rights
subsisting in the foregoing, and all adaptations to and modifications of the
foregoing shall at all times remain with CMU. CMU retains all rights not
expressly licensed under this Agreement. The CMU Software, including any images, graphics,
photographs, animation, video, audio, music and text incorporated therein is
owned by CMU or its suppliers and is protected by United States copyright laws
waiver of CMU's rights under United States copyright law. This Agreement and your rights are governed by the laws of
Agreement shall continue in full force and effect. THIS LICENSE SHALL TERMINATE AUTOMATICALLY if you fail to
acknowledge and agree that providing such feedback will not obligate CMU to
may be requested to load and run CMU Software for the purposes of executing
WARRANTY ON CMU SOFTWARE You expressly acknowledge and agree that your use of the CMU
Software is at your sole risk. THE CMU SOFTWARE IS PROVIDED "AS IS" AND WITHOUT
INCLUDING NEGLIGENCE, SHALL CMU BE LIABLE UNDER ANY THEORY OR FOR ANY DAMAGES
INCLUDING WITHOUT LIMITATION, DIRECT, INDIRECT, GENERAL, SPECIAL,
CONSEQUENTIAL, INCIDENTAL, EXEMPLARY OR OTHER DAMAGES [1] We define an
Agent as an autonomous, (preferably) intelligent, communicative, collaborative,
adaptive computational entity. Here, intelligence is the ability to infer and
execute needed actions, and seek and incorporate relevant information, given
certain goals. article by technology writer Jim Crane, which appeared in numerous
newspapers and internet news sources, including USA Today, 鄭I: Latest
The complete list of publications of the Intelligent Software Agents Lab is
available at http://www.cs.cmu.edu/~softagents/publications.html.
Most papers are accessible electronically. [4] For a
description of the RETSINA Multi-Agent Infrastructure and its implications for
nts.ri.cmu.edu/ans/ANSv2.9.PDF">http://www.cs.cmu.edu/~softagents/ans/ANSv2.9.PDF. [6] We consider
the ANS server and ANS client as part of an Agent Name Service (ANS) package.


AgentA Workspace

Example Two: Adding an Information Agent
The agents we used in the first example can be considered task agents. However,
since we did not need our agents to perform complicated tasks, we used the most
basic agent form from the AFC.
We will now add a new agent to the scenario that is based on the AFC
Information Agent. The agent we will add can tell us the time of the local
system. In other words, when we ask it, it will tell us the date and time of
the system on which it is running. Since we are running all the agents on the
same system, we will be receiving the time of the local system. The agent that
provides the time and date is named the DateTimeAgent.
1. Single shot query: The requesting a gent asks for information once;
the service provider implicitly de-commits to providing the service/information
again after the first reply, or upon a timeout.
2. Active monitor query: The requesting agent asks the information agent
to actively monitor an information source and to provide information, typically
on a periodic basis (e.g. every 60 seconds). The Information Agent
acknowledges the request, informing the requester how to end the service.
The service-providing info agent continues to provide the service until it
receives an explicit message from the requester asking it not to provide the
service any more.
3. Passive monitor query: The requesting agent asks that the
service-providing agent notify it of the occurrence of an event or condition,
for example, a change in stock prices; the recognition of an explosion, enemy
"query" to another information agent, asking it to update a database
record or external archive.
Building the Second Example Agents
We need one more addition to complete the agent; add a private variable
called
This is all that is needed to setup the
class of Information Agent.
b_message=NULL;
This will make sure the agent does not
delete memory that it doesn稚 use.
Next find the destructor of the agent and
font-weight:normal'>
All that is left to do now is to fill in the content of the external query
function. You will manually have to add the method to your file, since the
Agent Application Wizard did not add this method for us. When you are finished,
your file should have the following method:
char *CDateTimeAgent::external_queryfunction (CLList
font-weight:normal'>
}
Note that this method returns a string.
This is how the agent provides the result of the query. In the example above
the query will always fail, because a NULL is returned. Change the content of
the method to reflect the following code:
debug ("<CDateTimeAgent> processing external
query function ...");
CParameter *temp=(CParameter *)
request->get_first_element ();
A parameter is a class that has a name and
a content field. The content is always a string. In every query that an
Information Agent receives there will be a parameter called
"primary-keys". This is borrowed from database technologies, and you
will see that most of the queries resemble database queries. In our example the
agent code checks to see whether the current parameter that was obtained from
the list is the primary key. It accomplishes this by using the following piece
mso-bidi-font-size:12.0pt;font-family:"Courier New";font-weight:normal'>if
(strcmp (temp->get_name (),"primary-keys")==0)
Once the Information Agent finds the
primary key, it will need to examine its contents, which will tell it if the
mso-bidi-font-size:12.0pt;font-family:"Courier New";font-weight:normal'>char
*content=(char *) temp->get_content ();
We need to obtain a pointer to the content
field in the parameter object. Parameters are designed to hold a number of
different data types. In our case we use strings exclusively, so we will cast
the content to a string. As the content in this case is "time," the
DateTimeAgent will process the request and send back the current time. To enable
(b_message,"tell :time (%s)",string);
We have now built our first Information
Agent. However, in order to make use of it, we need to integrate it into our
agent scenario. In order to do this we modify some code in AgentA. Open up the
mso-bidi-font-size:12.0pt;font-family:"Courier New";font-weight:normal'>void
CAgentA::process_init (void)
If you do not have this method then add it
to your source file and header file as either a protected method or a public
("tell",
char
*content =c_parser->find_content ();
Below this line add:
This will retrieve an ontology field from
your message. The ontology indicates the subject of conversation. We will use
it here to see if the message is coming from AgentB, or from the DateTimeAgent.
The code below is the only additional code we add to our agent to have the
from the DateTimeAgent
Example Three: Using the
Matchmaker


Building the Third Example Agents
CMatchmakerClient
*mmaker=get_mm_module ();
if (mmaker!=NULL)
{
CFileBuffer *file=new CFileBuffer;
char *buffer=file->load_a_file ("target-schema.txt");
if (buffer!=NULL)
{
char *agent_monitor=new char [strlen (buffer)+1];
strcpy (agent_monitor,buffer);
if (mmaker!=NULL)
mmaker->mm_monitorAdvertisements (agent_monitor);
delete [] agent_monitor;
}
else
debug ("<CAgentA> Unable to load the target information
agent
advertisement template needed for advertisement monitoring!");
delete file;
}
With this code fragment, we load an
advertisement into a file object. Then, we assign the file object to the
contents and format of the advertisement. It is a small advertisement that
tells the Matchmaker to look for similar capability advertisements from other
agents. The actual request in the above code consists of two lines:
if
(mmaker!=NULL)
mmaker->mm_monitorAdvertisements (agent_monitor);
These lines direct a task agent client module
dedicated to the Matchmaker to tell the Matchmaker to look for the
advertisement given as a file object. The Matchmaker will tell AgentA whether
or not any agents with such capabilities are available.
In the test example, the DateTimeAgent
advertised with the Matchmaker. Putting a file named adv-schema.txt in the
directory from which the information agent starts creates this communication.
The contents of this file is a capability advertisement like the one used in
the code fragment above, which told the Matchmaker what capabilities our task
agent is looking for. The content of this advertisement is written in an
advertisement language called GIN.
CMatchmakerClient
*mmaker=get_mm_module ();
if (mmaker!=NULL)
{
if (mmaker->get_updated ()==TRUE) // we received an answer from the
Matchmaker
{
debug ("<CAgentA> Processing change in Matchmaker
module");
if (mmaker->get_last_operation ()==__MM_OP_NEWAD__)
{
CServiceInfo
*service=mmaker->get_last_service ();
if (service!=NULL)
{
// next see if the
advertisement is a device ontology
CGINAdvertisement
*ad=(CGINAdvertisement *)
service->get_first_element ();
if (ad!=NULL)
{
char
*reply=Communicator->comm_sendmessage ("tell",
p;
ad->get_agentname (),
p;
"default-language",
p;
"default-ontology",
p;
NULL,
p;
NULL,
p;
NULL,
p;
"objective :name \"getInformation\"
:parameters (listof (pval \"primary-keys\" \"time\") (pval
\"trigger\"
\"any-change\") (pval \"period\"
\"20000\"))",
p;
NULL);
if (reply!=NULL)
debug (reply);
else
debug ("Message sent to Agent");
}
}
else
debug ("<CAgentA> Unable to
obtain new agent info");
// done handling message from
Matchmaker -------------------------------------------------
}
mmaker->set_updated (FALSE); // tell the Matchmaker we noticed
the change
return (TRUE);
}
}
As you can see from the code above, we first
obtain a pointer to the Matchmaker client module.
if
(mmaker->get_updated ()==TRUE)
-- indicates that a message came in and that
indeed something changed within the Matchmaker. Now AgentA need only learn
whether or not the Matchmaker has the name of an Information Agent that matches
the capability requested.
if
(mmaker->get_last_operation ()==__MM_OP_NEWAD__)
(Since we only have one Information Agent running,
we know that this must
be a match for AgentA痴 request. We obtain a pointer to the service description
the Matchmaker client can provide us):
CServiceInfo
agent it seeks. Below is the code that will extract the advertisement from the
service description:
CGINAdvertisement
*ad=(CGINAdvertisement *) service->get_first_element ();
A service might have more than one
advertisement, but since we are only
looking for one capability we use the first advertisement in the list.
Below, we show the difference between the code used by AgentA in step 2, and
that used by AgentA in step 3. The difference is that we can now obtain the
name of the DateTimeAgent without supplying it in our code. The string
"DateTimeAgent"ad->get_agentname
()
in step 3.
Example Four:
Using Discovery
All of our demonstrations thus far have assumed
a stable environment in which our agents live. In this example, we demonstrate
a means by which agents can continue to function, even when their environment
is changing, and when key components of the system come and go. Before testing
this example, however, we discuss the features employed to make this possible,
and the reasons for their development. You can skip to the instructions for
testing, if you want to see these features in action before, or in lieu of,
reading about them.Simple
Service Discovery Protocol
Agent
Discovery
Disabling
Discovery Modules
set_lookup_config
(LOOKUP_DISCOVERY);
If you want your agent to be completely
standalone, you can call the method as follows:
set_lookup_config
(LOOKUP_NONE);
void enable (BOOL);
BOOL is_enabled (void);
Use this method to enable or disable one of the
lookup modules at runtime. In order for you to call the methods on the lookup
modules, you will need to obtain a pointer to one of these lookup facilities.
The following methods are available in the Communicator to do that:
p;
-----------
CANSClient
*retrieve_ans_object (void);
CDiscovery *retrieve_dsc_object (void);
Remember that both the CANSClient and
the CDiscovery classes are based
on the CLookupModule class.
To control the settings of the Discovery
parameters of ANS Servers, we have provided an alternative menu item in Start|Programs|RETSINA|Tools. The two options are:
The Screen
Version 2.8 of the ANS server will return
status and server startup help screens to any attached user that requests
into the ANS server console, the GUI will be reactivated if it has been closed.Server
Console vs. Stand-Alone Modes
void CAgentA::process_create (void)
{
if (Communicator!=NULL)
Communicator->comm_disable_ans (); Example Five: Integrating
Third-Party Reasoning Modules
The CProblemSolver Class
Specifically, the class provides the following methods: The CPSActionCodes Class
Example Five, Continued:
Deriving an Agent that Uses the CProblemSolver Class
The ExecuteActions() method of MyNicePlanner will be as follows:Class Hierarchy Diagram
The hierarchical relationship between the
classes used is shown in the class hierarchy diagram below.

Class
Hierarchy diagram for the problem solver classes
Example Six:
Auction Demo
Example Seven: Distributing Your Agents Over a
Number of Mchines.
![]()
![]()
![]()




As you can see from the screenshot, the IP address
for this particular system is: 128.2.178.76. Commandline parameter handing
Agent creation
Agent Initialization
Agent message processing
Your agent is running and fully active at this point. There is no one specific
event associated with this stage. Instead, multiple events will be recorded.
Each event indicates that either the environment changed or that a message
arrived from another agent. This is the part of the agent you will be working
font-family:"Courier New"'>Method called: BOOL process_message (char *data)Agent timer events
Agent shutdown
Network BeliefDB Data Structures
//
first find the network beliefdb within the total beliefdb
CBelief *lookup=(CBelief *) BeliefDB->find_element
("lookup");
if (lookup!=NULL)
{
// we know that the lookup table is a list so we can safely
convert
CListBelief *network_lookup=(CListBelief *) lookup;
CLList *services=network_lookup->get_value ();
CServiceInfo *info=(CServiceInfo *) services->get_first_element ();
while (info!=NULL)
{
if (info->get_type ()==SERVICETYPE_MATCHMAKER)
{
AfxMessageBox (info->get_name ());
}
info=(CServiceInfo *) info->get_next ();
}
}
This code will traverse the lookup table
and display a dialog box with the name of an agent or service for every entry
found. As you can see, it does not merely retrieve the name, but a full object
instead. This object, called the 'CServiceInfo', contains information regarding
one particular agent. The public appearance of this class is listed below:
class
CServiceInfo : public CLList
{
public:
CServiceInfo
(void);
virtual ~CServiceInfo (void);
void set_location (char *); // url formatted
void set_location (CURL *); // url object
CURL *get_location (void); // pointer to internal location
void set_expiration (int);
int get_expiration (void);
void set_type
(int);
int get_type
(void);
};
As is apparent, the ServiceInfo class is
derived from the AFC-defined linked list class. This means that the name of the
agent can be obtained by calling get_name ();, since the linked list depends on the CListElement
class, which the lookup modules will use to store the agent name. The reason
for using a linked list as the basis for our class is that every agent might
contain one or more advertisements. That is, we used a linked list so that the
agent can retrieve all of
the advertisements for a particular agent, which are associated with its
name or unique ID.
Each advertisement is added to the list and can be retrieved by using the
standard methods for accessing an AFC linked list. You should also notice that
the CServiceInfo class uses URLs to specify the network location. You will have
to use the access methods within the URL object to obtain parameters such as
hostname and port number. (For more information on the URL object, see the
chapter on tools and utilities). Next we see two methods that will either set
or get an expiration time from the service information object. This expiration
time is given in seconds and is primarily used internally for leasing purposes.
If you want this entry to be persistent regardless of the actual presence of
the agent, then use the access method to set this value to: -1. The last two
methods are used to obtain or change the infrastructure type of an entry in the
network beliefdb. The AFC uses the following defines to identify the role an
agent or service has within an MAS:
#define
SERVICETYPE_AGENT 1
#define SERVICETYPE_MATCHMAKER 2
#define SERVICETYPE_DHARMASERVER 3
#define SERVICETYPE_ANS_SERVER 4
#define SERVICETYPE_DEMODISPLAY 5
#define SERVICETYPE_RECOMA_SERVER 6
The following code is an example of how to use
the service type to find all Matchmakers currently known to the agent:
//
first find the network beliefdb within the total beliefdb
CBelief *lookup=(CBelief *) BeliefDB->find_element
("lookup");
if (lookup!=NULL)
{
// we know that the lookup table is a list so we can safely convert
CListBelief *services=(CListBelief *) temp;
CServiceInfo *info=(CServiceInfo *) services->get_first_element ();
while (info!=NULL)
{
if (info->get_type ()==SERVICETYPE_MATCHMAKER)
{
AfxMessageBox (info->get_name ());
}
info=(CServiceInfo *) info->get_next ();
}
}
As you can see, we re-used the sources from our
first example and added
a simple test on the agent type. If an agent is identified as a Matchmaker, its
name will be displayed in a dialog box.Agent Destruction
Processing Updates to the Agent Environment
virtual
void process_environment_change (void);
In future updates you will be able to get very
detailed information about an agent's view of its environment. For now we will
show you how to learn whether an agent has been recently added to the network
beliefdb, or whether it will be removed shortly, because it is no longer
present on the network.
Now that you have added to your agent a way of being informed of environmental
changes, you will need a bit of code to investigate what actually happened.
Below is a small example of code that will traverse the network beliefdb and
report on what changes occurred:
//
-------------------------------------------------------------------------
void CExampleAgent::process_environment_change (void)
{
debug ("process_environment_change ()"); // just so we can see
where we are
if (state!=__AGENT_STATE_RUNNING__)
{
debug ("Agent not ready yet to process environment changes");
return;
}
// search through the network beliefdb to see what happened
CLList *network_db=obtain_network_db (); // this is an AFC core method
if (network_db!=NULL)
{
CServiceInfo *service=(CServiceInfo *) network_db->get_first_element
();
while (service!=NULL)
{
if (service->get_new ()==TRUE)
{
// this agent just arrived
}
if (service->get_gone ()==TRUE)
{
// this agent just left and the entry will be removed after
the
// method exists
}
service=(CServiceInfo *) service->get_next ();
}
}
else
debug ("No network belief db available");
}
// -------------------------------------------------------------------------
As you can see from the code above, you can
obtain the state of a service and see if it will be removed. If you want to
have a service object forcefully removed, then call the following method:
service->set_gone
(TRUE);
Keep in mind, however, that this is only a hint
towards the management system
that maintains the internal state of the network belief db. If a remote agent
indicates that it is still alive, a new CServiceInfo instance will be created.
(You can try to change your agent痴 mind about its external environment, but
you cannot get it to permanently deny the reality of other agents that actually
exist).
The addition of the
'process_environment_change' method will also allow
you to add more refined awareness of the coming and goings of infrastructure
components in a multi-agent system. For example, your agent may want to
register with every Matchmaker that it becomes aware of. The following code
demonstrates how to learn whether or not a new Matchmaker has started somewhere
on your local network:
//
-------------------------------------------------------------------------
void CExampleAgent::process_environment_change (void)
{
debug ("process_environment_change ()"); // just so we can see
where we are
if (state!=__AGENT_STATE_RUNNING__)
{
debug ("Agent not ready yet to process environment changes");
return;
}
// search through the network beliefdb to see what happened
CLList *network_db=obtain_network_db (); // this is an AFC core method
if (network_db!=NULL)
{
CServiceInfo *service=(CServiceInfo *) network_db->get_first_element
();
while (service!=NULL)
{
if (service->get_new ()==TRUE)
{
// this agent just arrived
if (service->get_type ()==SERVICETYPE_MATCHMAKER)
{
// a new matchmaker just arrived
}
}
service=(CServiceInfo *) service->get_next ();
}
}
else
debug ("No network belief db available");
}
// -------------------------------------------------------------------------
The example above only demonstrates that
a new infrastructure component of the Matchmaker type was found. The following
constants will allow you to check for basic infrastructure components:
#define
SERVICETYPE_AGENT 1
#define SERVICETYPE_MATCHMAKER 2
#define SERVICETYPE_DHARMASERVER 3
#define SERVICETYPE_ANS_SERVER 4
#define SERVICETYPE_DEMODISPLAY 5
#define SERVICETYPE_RECOMA_SERVER 6
#define SERVICETYPE_UNKNOWN 7
Now that you know that a new Matchmaker was
found, you may want to register with it. The following example uses the same
code as listed above but adds the capability to register a new client with the
Communicator.
//
-------------------------------------------------------------------------
void CExampleAgent::process_environment_change (void)
{
debug ("process_environment_change ()"); // just so we can see
where we are
if (state!=__AGENT_STATE_RUNNING__)
{
debug ("Agent not ready yet to process environment changes");
return;
}
// search through the network beliefdb to see what happened
CLList *network_db=obtain_network_db (); // this is an AFC core method
if (network_db!=NULL)
{
CServiceInfo *service=(CServiceInfo *) network_db->get_first_element
();
while (service!=NULL)
{
if (service->get_new ()==TRUE)
{
// this agent just arrived
if (service->get_type ()==SERVICETYPE_MATCHMAKER)
{
// a new matchmaker just arrived
CMatchMakerClient *mm_client=new CMatchMakerClient
(service->get_name
(),BeliefDB,Communicator,0);
Communicator->add_display (mm_client); // this will
add it as a custom
client
mm_client->set_logger
(DemoLogger); // make sure we can log to disk
mm_client->parse_args (m_argc,m_argv); // allow the
client to process
out custom settings
// The following methods are normally called by the
Communicator, so
// be careful !! They will start the client and add it
to the agent's
internal management
mm_client->change_state (__CREATE__);
mm_client->set_registered (FALSE);
}
}
service=(CServiceInfo *) service->get_next ();
}
}
else
debug ("No network belief db available");
}
// -------------------------------------------------------------------------
A couple of notes on the code above. First of
all, you probably noticed that there is no advertisement assigned. In this
example we assume that you use the default "adv-schema.txt" file in
the agent's directory. Secondly, you can see that there is a fair amount of
additional management you need to do to actually add the module to the agent.
In future versions of the AFC, the code above will be replaced by a single API
call and the above example will be reserved for situations in which you want to
add custom clients to your agent.Working With Top-Level Agent States
if
(state!=__AGENT_STATE_RUNNING__)
{
debug ("Agent not ready yet to process environment changes");
return;
}
Each agent will go through a number of
states during its execution life-cycle. These states dictate what events can
occur within the agent and they also drive a number of important events. The
events currently defined within the AFC are:
#define
__AGENT_STATE_CONSTRUCTOR__ 0
#define
__AGENT_STATE_INIT__
1
#define __AGENT_STATE_CREATE__
2
#define __AGENT_STATE_RUNNING__ 3
#define __AGENT_STATE_SHUTDOWN__ 4
#define __AGENT_STATE_DESTRUCTOR__ 5
#define __AGENT_STATE_TOP_LEVEL_END__ 6
The current state of your agent can be obtained
by examining the 'state' variable present in every class derived from
CBasicAgent. Each state is set after certain methods are completed. You will
have seen these methods described earlier in the manual.
The state variable is a block of memory that is protected by the agent core.
You can set the state yourself by defining a new state:
#define
__MY_AGENT_STATE_TOP_LEVEL_END__ __AGENT_STATE_TOP_LEVEL_END__ + 1
This code will define a new state, which you
are free to use if the top-level state is in: __AGENT_STATE_RUNNING__ If the agent detects that a state is set to a custom
setting in a top-level state other than
__AGENT_STATE_RUNNING__, then the agent
will forcefully change the state. It does this to protect numerous internal
modules that maintain your agent. For example, the garbage collector (that is
responsible for cleaning up the network belief db) will behave with slight
differences in each of the top level states.Forcing Global Lookup Refresh
Normally you would look at the network beliefdb
to get an overview of what agents are registered on the network. Sometimes
however, you may want to forcefully refresh the lookup table to be absolutely
sure that all the registrations are valid. You can call the following method
from within your agent:
if
(Communicator!=NULL)
Communicator->listall_agents ();
Remember that this method resides in the
Communicator and you will therefore have to obtain a pointer if you want to
call the method outside of your main agent class. When you call the method
listed above, a number of things happen simultaneously. One, the Communicator
locks the network beliefdb. You will not be able to directly modify any entries
in that area of memory. Two, the Communicator will iterate through all
registered lookup modules and activate their 'list-all' method. If you have all
types of lookup mechanisms enabled and if the agent has instantiated multiple
copies of these modules (one for each active infrastructure component e.g.,
multiple ANSs), then it might take quite some time for the 'listall_agents'
method to return. Also, certain lookup methods will not wait for a direct reply
but instead assume that answers to lookup requests will come in asynchronously.
This may result in environment updates being generated for every agent that was
found through this asynchronous mechanism. See Section 1 for information on how
to process environment updates.Client Module
The client class is one of the mechanisms available to developers to create a
persistent dialogue with other agents. This class represents the base class for
all classes involved in setting up registrations with other agents. For
example, the AFC uses the client class as the basis for interactions with
middle-agent clients. These clients advertise the agent's capabilities with a
middle agent. First, we will examine the basic client class and its
capabilities:
class
CClientBase : public CLogFacility
{
public:
CClientBase (char *,
p;
CBeliefDB *,
p;
CCommunicator *,
p;
unsigned int);
void set_ontology (char
*);
char *get_ontology (void);
void set_performative (char *);
char *get_performative (void);
void set_language (char
*);
char *get_language (void);
void set_create_string (char *);
char *get_create_string (void);
void set_destroy_string (char *);
char *get_destroy_string (void);
};
There are three important sections to be
mentioned in the class definition above.
A. Constructor
B. Envelope Configuration
C. Event Configuration
A. Constructor
The constructor takes a number of pointers to objects it needs to function
independently in the background. The first parameter is a string that holds the
name of the agent with which you wish to have a persistent connection. Next is
a pointer to the BeliefDB, which can be obtained from within your agent code.
Then, there is a pointer to the Communicator, which can also be obtained from
any class derived from CBasicAgent. The last parameter is a flag that is used
by the base class under certain conditions. This last parameter can be safely
set to 0.
B. Envelope configuration
When your agent uses the object that results from using the client classes, it
will ask the client to send messages at specific times. When a message is sent
by your client module it might need additional
information such as a performative and/or ontology etc. There are three methods
available to configure these message settings. By default the following methods
are called if no other preferences are specified. Every time your client module
sends out a message it will use these variables:
set_performative
certain times, in response to external events or internal signals, the basic
agent will generate events. All clients must be able to respond appropriately
to these events to ensure proper functionality at all times. As we have written
above, there are a fair number of events that can be generated at any time
during an agent's execution lifecycle. It is important to recall the basic
events, since you will see them occur when you start to build your own derived
client classes or log modules:
#define
__IDLE__ 0
#define __PLANNING__ 1
#define __ADVERTISE__ 2
#define __CREATE__ 3
#define __DESTROY__ 4
There are a number of additional methods that
you will need to know if you want to add more detailed interaction between the
agent and your client module:
public:
virtual BOOL change_state
(unsigned int);
virtual void process_timer (void);
virtual void process_message (char *,int);
virtual void process_create (void);
These methods are actually derived from the CLogFacility class and are used within your client for
maintenance and connection management. When the basic agent generates a __CREATE__ event, the method 'change_state' is called in your client module to indicate that it
will need to register with the server (middle agent for example). If a __DESTROY__
event is detected, the client module
will be notified again using the 'change_state' method, but this time it will trigger the unregister
method.
Below is sample code that demonstrates how a client module can be assigned to
an agent. We advise that you do this in the 'process_create' method, but it can be added at any point if the
agent is in either the '__AGENT_STATE_CREATE__' state or the
'__AGENT_STATE_RUNNING__' state.
void
CExampleAgent::process_create (void)
{
CClientBase *client=new CClientBase
("Server",BeliefDB,Communicator,0);
client->set_create_string ("(register)");
client->set_destroy_string ("(unregister)");
client->set_logger (DemoLogger);
client->parse_args (m_argc,m_argv);
Communicator->add_display (client);
}
The following steps were taken in the
code above:
1.
CClientBase
*client=new CClientBase ("Server",BeliefDB,Communicator,0);
Create a new client and provide it with the
proper parameters. In this case the client will try to connect to an agent
called 'Server'. The BeliefDB and Communicator pointers were obtained from the
basic agent and the last parameter was set to 0.
2.
client->set_create_string
("(register)");
client->set_destroy_string ("(unregister)");
We now configure the subscription and
unsubscription behavior by providing a registration string and unregistration
string. These two strings will be sent within the content field of the actual
messages. The client will trigger subscription and unsubscription events when
it detects that its agent either shuts down, crashes or boots.
3.
If you want your client to log messages to the global logfile then you may want
to add the following line of code to the agent:
client->set_logger
(DemoLogger);
This code will allow you to call the 'debug'
method if you decide to overload the base client to build more refined classes.
4.
client->parse_args
(m_argc,m_argv);
If you want to process command line arguments
within your class, or if you know that the client class takes specific command
line parameters, then you will need to call this method in the client. This
method is a virtual method and can be used in your own overloaded classes to
process specific parameters for your class.
5.
Communicator->add_display
(client);
This code will tell the Communicator that there
is a new client module that needs to be added to the total list of background
client modules. In doing this, you make sure that your client's background
management code is called at appropriate times.
From this point, you do not have to manage the client; the basic agent will do
this for you. The agent's core code also takes care of deleting the object when
your agent shuts down; you should not do so.Agent User Behavior, Agent Naming Convention
-unique yes
then the agent will append a globally unique ID to your agent name and use that
during it's execution life-cycle.
Using The KQML MESSAGE Sender

Introduction
No development environment or toolkit is
complete without its set of testing utilities. The RETSINA architecture has its
own set of tools, one of which is the KQML message-sending tool. This tool
allows you to send customized messages to an agent and to examine the
responses. Besides the basic message sending and receiving functionality, the
tool offers testing sets to test the RETSINA visualization system and Agent
Name Servers.Main WindowThe window is divided into
three main areas dedicated to specific agent tasks. The top portion is
dedicated to message generation and message inspection. In the middle you can
see the controls available to manage and work with an Agent Name Server.
Finally there is the visualization tool set at the bottom. Each of those areas
will be discussed in detail in the following sections.
Figure 1

Figure 2
In the next 5 steps we will demonstrate how to
configure the message sender to represent an agent and register it with an ANS.![]()
![]()


If an error occurs you will see a message in the status bar at the bottom of
the window and a dialog box will appear informing you of the specifics of the
failure.
Most failures in registration
occur because the listening port that was specified is already in use by
another agent. Simply change the port number and try again.
Figure 5
Message Management

Parsing Messages
![]()
Choose one Win32KQMLCenter window to send a message
to another agent.
Table 1
Miscellaneous Tools

Data Structures
char *get_name (void);
int get_btype (void);
Each of these nodes can be individually
assigned and retrieved. Under most circumstances, however, the tree classes
mechanism.
Be aware that changing the size of an
existing queue containing a number of elements might produce unwanted effects,
if the size of the new queue is smaller than the number of elements currently
present in the queue. By default, the queue size is set to 100 from with the
CQueue
access functions. The figure below shows the methods within the CStack class,
and their interfaces.Tools and Utilities
Generating and using GUIDs
The Agent Foundation classes fully support the
generation of Globally Unique Identifiers (GUIDs). When you created your agent
with the AFC Wizard, the AFC headers were incorporated in your agent, which
automatically gave your agent GUID capability. Here is an example on how to
generate a GUID:
#include "c_afc.h"
You can use an object instantiated from the CGUID class as a placeholder for
one ID, or you can use the object to keep generating new ones. In the following
example, we show how to generate 10 IDs:
While it is not useful from a
developer痴 perspective, the class also contains a method to set the internal
uuid to a specific string. This was implemented for internal use only. You can
set the internal string by calling:
#include "c_afc.h"
char *home=utils.get_home ();
CLList
*dirlist=utils.dir_list (".");
Database File Access
class
CDBRecord : public CLList
{
public:
CDBRecord (void);
virtual ~CDBRecord (void);
BOOL from_string (char *);
char *to_string (void);
};
As you can see, the basic record class does not
contain any specific references to data types held within the record. It does
not contain an index either. The class listed above was designed to allow
developers to construct their own records. Currently, the record assumes that
its contents are a list of CListElement objects (See documentation on the
CListElement class, above). The record uses the 'name' variable to store the
content of a record field. No translation or casting is done on the data and
the developer is responsible for refining this behavior. As you can see we have
two ACL management methods defined in the record class:
BOOL
from_string (char *);
char *to_string (void);
You can use the 'from_string' method to fill a
new record with data from an ACL formatted string. Any existing data within the
record will be deleted. Here is an example of what this might look like:
CDBRecord
*record=new CDBRercord ();
BOOL ret=record->from_string ("(record :element (first) :element
(second))");
if (ret==FALSE)
printf ("Error expanding ACL string");
else
printf ("Successfully filled record");
After the operations listed above the
contents of the record would be:
"first"
"second"
When you want to send the contents of a record
to another agent, you can use the code below to collapse the data in the record
into an ACL formatted string:
char
*string=record->to_string ();
if (string==NULL)
printf ("Unable to collapse data into ACL string");
else
printf ("Collapsed data into: %s",string);
Now that we have described how a record is
defined, we can proceed with the documentation of the database class. The
public face of this class is defined as:
class
CDBFileIO : public CFileBuffer
{
public:
CDBFileIO (void);
virtual ~CDBFileIO (void);
BOOL load_records (char *);
BOOL save_records (char *);
BOOL from_string (char *);
char *to_string (void);
int get_nr_columns (void);
int get_nr_rows (void);
BOOL add_record (CDBRecord *);
void reset_db (void);
};
As you can see, the constructor does not
take any parameters. Creating an object of this class will create an empty
database. You can either start adding records by hand or load them from a file.
Keep in mind that this particular class is the base class for all databases in
the AFC. As such it represents a flat view of a database. Records are organized
in a matrix representation whereby the first record contains the keys for the
database. To clarify further how this flat database view works, let's look at
an example:
//
create empty database
CDBFileIO *database=new CDBFileIO ();
// create the first record that will hold the list of keys
CDBRecord *record=new CDBRecord ();
// add a number of keys to the record ...
CListElement *key1=new CListElement ();
key1->set_name ("SSNR");
CListElement
*key2=new CListElement ();
key2->set_name ("First Name");
CListElement *key3=new CListElement ();
key3->set_name ("Last Name");
record->add_element (key1);
record->add_element (key2);
record->add_element (key3);
if database->add_record (record)==FALSE)
printf ("Unable to add record to database");
The code shown above sets up a new database
using code. Now that you have a formatted database, you can start adding
fields. This works exactly the same way as adding the keys to the database.
Below is a small fragment that demonstrates this:
//
create the first record that will hold the list of keys
CDBRecord *record=new CDBRecord ();
// add a number of keys to the record ...
CListElement *field1=new CListElement ();
field1->set_name ("123455652");
CListElement *field2=new CListElement ();
field2->set_name ("Martin");
CListElement *field3=new CListElement ();
field3->set_name ("van Velsen");
record->add_element (field1);
record->add_element (field2);
record->add_element (field3);
if database->add_record (record)==FALSE)
printf ("Unable to add record to database");
The database base class as described here was
designed to give agent researchers a quick and easy tool to take their
experimental results and stream them to a database file for future examination.
Interaction with the actual files is achieved through two methods:
BOOL load_records (char *);
BOOL save_records (char *);
We assume here that your files will be dos or
unix text formatted files with TAB separations between columns. Use the
'load_records' method to fill a newly created database object with the contents
of a file. The parameter that this method takes is the name of a file that
holds the database. Subsequently you can save a database by calling the method
'save_records ()' with the name of a file to be saved as a parameter. In the
case you want to flush databases using the last used filename, you can use the
method 'save_records' with no file parameter. This will call 'save_records
methods is:
BOOL from_string (char *);
char *to_string (void);
(Note: If you want to store your database as a
KQML string on disk, you will have to maintain your own file pointers).
After you have loaded or filled a database you
can obtain some basic information from it by using:
int get_nr_columns (void);
int get_nr_rows (void);
These methods ultimately calculate the extend
of the database matrix and return the result.
In case you want to completely clear an
existing database object, you can call:
void reset_db (void);
This will:
Wildcard
Matching Support
The purpose of this class is to store and manage a number of wildcard
descriptions. Matching methods can be used to match a string to a set of
wildcards. The wildcard class does not assume a file system model, although it
can be used for that. Below is the public part of the wildcard class:
class
CWildCard : public CLList
{
public:
CWildCard (void);
~CWildCard (void);
BOOL add_wildcard (char *);
BOOL match (char *);
BOOL match_nocase (char *);
BOOL from_string (char *);
char *to_string (void);
};
As you can see, the class is derived from a
linked list. This allows a wildcard object to store multiple variations of the
wildcard. No additional initialization is needed. Once the object is created,
you can add one or more wildcard definitions. For example:
CWildCard *wildcards=new CWildCard ();
wildcards->add_wildcard ("infoagent*");
wildcards->add_wildcard ("infoentity*");
wildcards->add_wildcard ("info*");
After you have configured the object with a
number of examples, you can give it a string to examine. There are two methods
available to inspect a sample string:
BOOL match (char *);
BOOL match_nocase (char *);
Either method will return TRUE if the string
matches any of the patterns and FALSE if it matches none. Use the second method
to disregard any case matching between wildcards and input string.
As with most AFC classes, you can use the
'from_string' and 'to_string' methods to collapse the data into an ACL
formatted string. In the CWildCard class, these methods are declared as:
BOOL from_string (char *);
char *to_string (void);
The resulting ACL string describes the list of
wildcard patterns stored in that particular object.
Adding Custom Sockets to Your Agent
It is possible to add your own socket to an AFC
agent. This might be useful in cases where the traffic going through the socket
is of a type not supported by any existing AFC socket. What follows are
instructions for adding a custom socket to your agent.
CSocketBase
// basic TCP/IP socket
CDataGramSocket // modification on the previous one that supports UDP
CMulticastSocket // multicast implementation of CDataGramSocket
void
set_group_ip (char *);
char *get_group_ip
(void);
void set_group_port
(int);
int get_group_port
(void);
These methods allow you to configure the
multicast group and port. The AFC already supports multicast, but this socket
is pre-configured to use the UPnP group. In an example below we will
demonstrate how to properly use these methods. But first, there is one more
method that is crucial for a custom socket:
set_sockettype
(__MY_SOCKET__);
This method will identify your socket instance
as a custom socket. Whenever
data arrives on this channel, your agent will be informed through the
CBasicAgent method:
virtual
void process_custom (CSocketBase *);When this method
is called for your agent, you will be given a pointer to a socket base class.
This is in actuality a pointer to an instance of your socket type, which you
will have to cast to the proper type. Below is a full example of an agent that
incorporates a custom multicast socket.
#ifndef
__CUSTOM_SOCKET__
#define __CUSTOM_SOCKET__
#include "c_afc.h"
#define __MY_SOCKET__ _USER_+1
class CMySocket : public CMulticastSocket
{
public:
CMySocket (CWnd *);
~CMySocket (void);
};
#endif // __CUSTOM_SOCKET__
Below is the implementation of our new socket.
We only call three methods to
configure our instance. The third one is mandatory, since this will properly
identify our socket as a new type:
/*--------------------------------------------------------------------------
-----------*/
CMySocket::CMySocket (CWnd *a_wnd) : CMulticastSocket (a_wnd)
{
set_group_ip ("239.192.0.14");
set_group_port (1900);
set_sockettype (__MY_SOCKET__);
}
/*--------------------------------------------------------------------------
-----------*/
CMySocket::~CMySocket (void)
{
}
/*--------------------------------------------------------------------------
-----------*/
Now that we have the layout of our custom socket, we can add it at runtime
to our agent. Make sure you add the socket at the appropriate time in your
agent. We need a running Communicator, which limits the place to insert our
socket to either the 'process_create' method or one of the event
methods that can occur when the agent is in the __AGENT_STATE_RUNNING__ state.
/*--------------------------------------------------------------------------
-----------*/
void CExampleAgent::process_create (void)
{
// create new socket and give a pointer to our message handler 'handler'
CMySocket *simcast=new CMySocket (handler);
// add the socket to our agent ...
add_alternative_socket (simcast);
// since this socket is a multicast socket, we need to join the multicast
group
int ret=simcast->JoinGroup (get_group_ip (),get_group_port
(),3,FALSE);
// see what happened ...
if (ret==TRUE)
debug ("<CExampleAgent> Sucessfully initialized custom
socket");
else
debug ("<CExampleAgent> Error initializing custom
socket");
}
/*--------------------------------------------------------------------------
-----------*/
We now have a new socket type running in our agent. Remember that sockets
in the AFC are always fully duplex. The Communicator assumes that it will only
use one socket to send and receive to an agent. There is no little amount of
code present to guarantee that no more sockets than necessary are used to talk
to an agent. You can freely send data over this socket using the 'mfc_send'
method. When data arrives on the custom channel your agent will be notified
using the:
virtual void
process_custom (CSocketBase *);
method. If you override this method you will need to implement the
necessary code to handle the data ready in the socket. Below is an example of
how this can be done using our custom socket from the previous code fragments:
/*--------------------------------------------------------------------------
-----------*/
void CExampleAgent::process_custom (CSocketBase *a_socket)
{
char message [1024];
if (a_socket==NULL)
{
debug ("<CExampleAgent> Empty socket");
return;
}
CMySocket *target=(CMySocket *) a_socket;
AfxMessageBox (target->get_data (1));
}
/*--------------------------------------------------------------------------
-----------*/Miscellaneous
Utilities
Again, make
sure you include the Agent Foundation Classes header and add an object of type
CUtils:White Space

Tokenizing
Since most agents communicate by exchanging
strings, we provide a number of tools to manipulate and manage agent-specific
strings. Most of the tools were developed to accommodate the easy development
of code dealing with ACL fragments. The following related operations are
example demonstrates how to remove parentheses. The same code can similarly be
used to remove other characters.![Text Box: char *string=new char [strlen ("(hello
world)")+1];
strcpy (string,"(hello
world)");
char *formatted=utils.remove_parenthesis
(string);
printf ("Formatted string:
[%s]\n",formatted);](./Manual_rev_15_files/image087.gif)
URLs and Web Development
As you can
see, the name of the URL object always contains the completely formatted URL.
You can access the different fields and change them to point the URL to a
different location. Be aware, however, that some of the fields can be set to
NULL. For example, if the URL does not contain a reference to a CGI script,
then you will get a NULL back when you attempt to access that field.
Appendix
A: Comparison of Agent-Building Systems
Decision-making for route planning and team coordination, Mail, Speech, Movie
Appendix B: RETSINA Software License