Les slots sociaux des agents

Pour compléter l’étude des slots des agents que nous avons commence é dans le tutoriel précédent (Les capacités d’un agent/Les slots physiques des agents), nous allons maintenant nous focaliser sur les interactions sociales des agents. Nous allons donc voir ici comment déclarer les slots sociaux dans une classe agent, et voir comment ces déclarations vont permettre à l’agent de communiquer avec les autres agents du système. Mais avant d’aborder ces considérations syntaxique, nous allons commencer par examiner comment fonctionne les intéractions sociales dans SKUAD.

Attention : pour ce tutoriel vous devez utiliser une librairie SKUAD de version v0.1_b04 ou supérieure !
Si besoin suivez ce tutoriel : Mettre à jour la librairie SKUAD.

 
1 - Définition : space, participant et avatar

Un slot social permet à l’agent d’être connecté à un espace d’interaction social, nous appleons plus simplements ces espace des spaces. Au sein d’un space l’agent peut communiquer explicitement (par envoi de message) ou implicitement (par observation) avec les autres agents connectés à ce même space, nous verrons comment dans les prochaines sections de ce tutoriel.

En réalité ces interactions ne sont pas exercées directement entre agents, mais passent par une représentation intermédiaire appelée participant. Ainsi chaque agent est représenté au sein d’un space par une instance participant. Et quand un agent envoie des messages, ou observe un autre agent, il effectue en fait ces actions sur l’instance participant de l’agent cible.

L’agent est libre d’exprimer toutes les caractéristiques du participant qui le représente au sein du space. Il peut ainsi prendre l’apparence sociale qu’il souhaite. Et, comme il peut potentiellement avoir plusieurs slots sociaux, donc être connecté à plusieurs spaces en même temps, il peut choisir d’avoir une apparence sociale différente dans chacun des ces spaces. Par contre il ne peut naturellement pas modifier les caractéristiques des instances de participant des autres agents.

Aussi, dans l’absolu les instances de participant ne sont pas modifiables (elles sont en « lecture seule »). Et pour permettre à l’agent propriétaire d’une de ces instances de pouvoir modifier celle qui lui appartient, une objet d’écriture spécifique appelé avatar lui est fournie. Cet objet peut être vu comme un appareillage qui encapsule une instance de participant, et qui fournit des outils pour opérer des modifications sur cette instance.

Ainsi, quand un agent est connecté à un space, il reçoit une instance d’avatar qui lui permet d’agir en écriture sur l’instance de participant qui le représente au sein du space. Et dans le space il peut observer les instances participants des autres agents, sans avoir la possibilité de les modifier.

 
2 - Principe et fonctionnement des spaces

Un space est une abstraction permettant de faciliter les modalités de mise en relation, et les échanges d’information, entre les agents. Aussi les spaces peuvent être vus comme des réseaux sociaux, un peu comme ceux que nous utilisons quotidiennement, car les réseaux sociaux ont le même objectif : faciliter les mises en relations et les échanges d’information entre les individus. Nous sommes libre de nous représenter sous la forme que l’on veut dans un réseau social, et de même, l’avatar de l’agent lui permet de choisir librement la façon dont il doit être représenté dans le space.

Dans un système SKUAD en cours d’exécution, il peut exister plusieurs spaces distincts. Chaque space est identifié par un nom, aussi si 2 spaces porte le même nom il s’agira en réalité d’un seul est même space.

Nous verrons dans quelques temps que l’architecture SKUAD garantie que si deux agents sont connectés au même space, et si au niveau physique il existe une connectivité réseau reliant chacun des deux contextes d’exécution de ces agents, alors ces agents se « verront » mutuellement et ils pourront communiquer entre eux. Dans le cas ou la connectivité réseau est inexistante (ou rompu à un instant donnée) ces agents seront quand même dans le même space, mais celui-ci ne sera pas (ou plus) « connexe » : il sera composé de deux îlots non interconnectés. Dans ce cas les agents ne se verront donc pas l’un l’autre, mais dès que la connectivité réseau sera (ré-)établie, alors les ilots du space seront automatiquement fusionnés et les deux agents seront (à nouveau) en mesure d’interagir entre eux.

Nous verrons aussi plus tard, qu’on peut utiliser également des spaces protégés pour le cas ou les échanges portent sur des aspects critiques. Ces spaces ont la particularité d’être verrouillés par un mot de passe, et seuls les agents qui disposent du bon mot de passe pourront se connecter et interagir au sein de ces spaces. De plus, au niveau interne, les changes réseaux concernant ces spaces sont cryptés.

 
3 - Déclaration des slots sociaux

Pour déclarer les slots sociaux d’un agent il faut agir au niveau de la définition du type de l’agent, donc dans l’écriture du code de sa classe. Dans ce code il faut définir un attribut final static de type String et de nom social_specif :

  public final static String social_specif = "XXXX";    
   //XXXX -> la chaine de définition des slots sociaux, chaîne vide si aucun

Cet attribut prend pour valeur une chaîne de définition des slots sociaux. Cette valeur peut-être la chaîne vide dans le cas ou ce type d’agent ne doit comporter aucun slot social. Dans ce cas, et comme pour les slots physiques, il est préférable d’effectuer quand même cette déclaration avec une valeur vide plutôt que ne pas déclarer l’attribut (le système affiche un message d’alerte si cet attribut n’est pas présent).

La syntaxe de la chaîne de définition des slots sociaux est la suivante :

« definition_slot1; definition_slot2; definition_slot3; definition_slotN; »

Avec chaque definition_slotX de la forme: name_slotX : space_nameX , local_id

La valeur name_slotX spécifie le nom du slot, vous pouvez utiliser une chaîne de caractère alphanumérique quelconque (sans espace) à ce niveau. La valeur space_nameX indique le nom du space sur lequel ce slot sera connecté. Enfin la valeur local_id est optionnelle et sert à spécifier un identifiant locale pour le participant qui va représenter l’agent dans le space. Si cette valeur local_id n’est pas spécifié, l’identifiant aura une valeur arbitraire fixé automatiquement par le système.

Voici par exemple une déclaration spécifiant 2 slots sociales de nom : zone_locale et zone_globale, reliés respectivement aux spaces de nom maison et world :

  public final static String social_specif =    
                                      "zone_locale  :  maison ;"
                                  +   "zone_globale :  world  ;" ;

 
4 - Câblage des slots sociaux

Le principe du câblage des slots sociaux est bien plus simple que celui des slots physiques, dans la mesure où ici il n’y a pas de contrainte sur l’existence d’un space. En effet, un space existe toujours quelque soit les circonstances (dans le pire des cas il sera simplement non-connexe, i.e. composé de plusieurs îlots disjoints).

Aussi le câblage effectif des slots sociaux dépend uniquement de l’état de l’agent :

  • quand l’agent passe dans l’état RUN, ses slots sociaux (si son type en définit) seront automatiquement tous câblés. Les avatars correspondants à chacun de ces slots seront alors tous instanciés.
  • quand l’agent sort de l’état RUN (même pour une pause temporaire), tous ses slots sociaux seront déconnectés, et donc tous ses avatars seront détruits (et les participants associés seront retirés des spaces correspondants).

Au niveau du code, il n’y a donc rien de spécial à faire de plus concernant la gestion des slots sociaux de l’agent une fois qu’ils ont été définis par l’attribut social_specif.

 
5 - Manipuler les slots sociaux

Quand l’agent est en cours d’exécution, il est possible de manipuler le slots sociaux dans son code en utilisant les méthodes suivantes de son descripteur (objet AgentUDescriptor) :

public boolean isSocialPlugged(String slot_name);
public Avatar getAvatar(String slot_name);

La méthode getAvatar(…) retourne l’objet de type Avatar (package: skuad.ubiquity.space) qui correspond à l’avatar actuellement associé au slot social de nom slot_name. Si ce slot n’est pas connecté à un space (parce que l’agent n’est pas dans l’état RUN, ou qu’aucun slot n’existe sous ce nom) on récupère alors la valeur NULL.

Les objets Avatar présentent les méthodes suivantes :

public Participant participant(); //instance participant de l’avatar
public Space getSpace();          //space connecté à cet avatar
public Space getSpace(String password); //idem pour les spaces protégés

public boolean exist(); //teste si l’avatar est instancié
public byte remove();   //supprime l’avatar (retire l’agent du space)

//accès aux instances d’écriture de chacune des 5 facettes du participant
public MailBox mailBox();
public WAlias alias();
public WState state();
public WSkill skill();
public WActivity activity();

Nous reviendrons plus en détails sur ces objets Avatar dans les sections ci-dessous. Nous allons pour l’instant nous concentrer sur l’objet space.

Comme vous pouvez le voir, si un slot de l’agent est connecté à un space il est possible de d’obtenir le descripteur de ce space via la méthode de l’avatar correspondant à ce slot. Ce descripteur est un objet de type Space (package: skuad.ubiquity), il s’agit d’une interface qui expose les méthodes suivantes :

public String nameSpace();
public boolean havePassword(); //teste si le space est protégé

public Participant getParticipant(String id);

//création d’un nouvel avatar
public Avatar newAvatar();
public Avatar newAvatar(String local_id);

//écoute de tous les participants
public void addParticipantListener(ParticipantListener ial);
public void removeParticipantListener(ParticipantListener ial);

//écoute des facettes de tous les participants
public void addSkillListener(SkillListener fl);
public void removeSkillListener(SkillListener fl);
public void addStateListener(StateListener fl);
public void removeStateListener(StateListener fl);
public void addActivityListener(ActivityListener ial);
public void removeActivityListener(ActivityListener ial);

Vous pouvez déjà remarquer qu’avec cet objet il est possible de créer d’autres avatars (méthodes newAvatar(…)), mais nous n’utiliserons que très rarement cette possibilité, dans la mesure où pour des agents les avatars sont créés automatiquement sur chacun de ses slots sociaux (cette possiblité à surtout un sens pour un usage de la librairie Ubiquity, package: skuad.ubiquity, dans un autre contexte que celui des agents SKUAD).

Par contre, ce qui nous sera plus utile c’est les méthodes d’observations (écoute des participants et de leurs facettes). Via celles-ci on peut en particulier s’informer, et rester informé, de l’ensemble des participants existant dans le space. Il suffit en effet pour cela d’utiliser la méthode addParticipantListener(…) avec en argument un objet implémentant l’interface ParticipantListener (package: skuad.ubiquity.space) dont voici la définition :

/*Notifie qu'un nouveau participant apparaît dans le space */
public void participantAdded(Participant p);

/* Notifie qu'un agent vient de quitter le space */
public void participantRemoved(Participant p);

Notez que juste après l’enregistrement de cet écoute, la méthode participantAdded(…) de l’écouteur sera déclenchée pour chacun des participants déjà présent dans le space au moment de l’enregistrement. De cette façon, quelque soit l’instant ou on enregistre une écoute, on est sûr de récupérer une notification pour l’ensemble des participants du space.

Enfin, les participants sont décris par le type Participant (package: skuad.ubiquity.space) qui expose les méthodes suivantes :

public Space getSpace();
public Space getSpace(String password);

public boolean exist();
public boolean ordoredBefore(Participant ia);
public boolean ordoredAfter(Participant ia);

public String localId();
public String fullId();
public boolean isLocal();
public Spot spot(); //spot sur lequel se trouve le participant

//accès aux instances de lecture de chacune des 5 facettes
public RSkill getSkill();
public RState getState();
public RAlias getAlias();
public RActivity getActivity();

//écoute de ce participant
public void addListener(ParticipantListener sl);
public void removeListener(ParticipantListener sl);

 
6 - Les notifications liées aux slots sociaux

Quand un événement liée aux slots sociaux se produit cela conduit, selon la nature de cet événement, à invoquer l’une des 5 méthodes suivantes de l’agent (ces méthodes sont définies par l’interface AgentU) :

//Avatars associés à l'agent
public void notifyPlugAvatar(AgentUDescriptor aud, String name, Avatar avatar, int contexte);
public void beforeUnplugAvatar(AgentUDescriptor aud, String name, Avatar avatar, int contexte);
public void notifyUnplugAvatar(AgentUDescriptor aud, String name, int contexte);
public void receive(AgentUDescriptor aud, Avatar avatar, MailBox box, Message mess);

La méthodes notifyPlugAvatar(…) est déclenchée juste après qu’un slot se connecte à un space. Et les méthodes beforeUnplugAvatar(…) et notifyUnplugAvatar(…) sont déclenchées respectivement juste avant et juste aprés qu’un slot se déconnecte d’un space.

Enfin, la méthode receive(…) est déclenchée par le système SKUAD à chaque fois qu’un message est reçu par l’un des avatars de l’agent (donc dans l’un des spaces auquels l’agent est connecté via ses slots). Nous reviendrons plus en détails sur les échanges de message entre agent dans un prochain tutoriel, ainsi que sur les autres capacités d’interactions sociales moins explicites qu’il est également possible de mettre en oeuvre.

 
7 - Mise en pratique

Créez une nouvelle classe agent de nom AgentSpy qui spécifie un slot social de nom « area » sur un space de nom « world ». A chaque fois que ce slot se connecte au space, faites en sorte que votre agent s’enregistre en tant qu’observeur de tous les participants du space.

  • A chaque notification d’ajout d’un participant votre agent doit émettre sur sa console le message : « nouveau participant d’identifiant XXX détecté »
  • A chaque notification de retrait d’un participant votre agent doit émettre sur sa console le message : « disparition du participant d’identifiant XXX »

Transformez cette classe en un programme qui doit : créer un agent de type AgentSpy, tracer les évènements de changement d’état et les logs de cet agent, puis lancer son exécution.

Compilez, puis lancez plusieurs programmes AgentSpy dans des consoles différentes.

A vous de jouer !

Une fois que vous avez réussi à écrire ce programme, ou si vous n’y parvenez pas, vous pouvez comparez votre code à la proposition de solution ci-dessous.

afficher la solution