7 — Le protocole Request
Le protocole Request est le plus simple et le plus courant des protocoles FIPA. Il modélise une situation du quotidien : un agent demande à un autre d’effectuer quelque chose et attend sa réponse.
La logique du protocole
Pensez à un client qui passe une commande à un prestataire. Le client formule sa demande, le prestataire peut accepter et livrer, refuser, ou signaler un problème. Le protocole Request formalise exactement ce scénario.
Demandeur Exécuteur
│ │
│──────── REQUEST ───────────────►│ "fais ceci"
│ │
│◄─────── INFORM ─────────────────│ succès, voici le résultat
│ ou REFUSE │ refus, voici pourquoi
│ ou AGREE + INFORM │ accepté, résultat à venir
│ ou AGREE + FAILURE │ accepté, mais échec en cours d'exécution
Le cas le plus fréquent est REQUEST → INFORM : la demande aboutit directement. Les autres cas permettent de gérer les situations plus complexes.
Quand utiliser AGREE ?
Quand le traitement prend du temps, l’exécuteur envoie d’abord un
AGREE pour dire « j’ai reçu ta demande, je m’en occupe » puis, une
fois le travail terminé, envoie l”INFORM avec le résultat.
Sans AGREE, le demandeur ne saurait pas si l’exécuteur a bien reçu
la demande ou si elle s’est perdue en chemin.
Les deux rôles
L’initiateur (
RequestInitiator) — celui qui formule la demande et gère les différentes réponses possiblesLe participant (
RequestParticipant) — celui qui reçoit les demandes, les traite et renvoie une réponse
L’initiateur — RequestInitiator
L’initiateur envoie la requête et attend la réponse. Vous n’implémentez que les méthodes correspondant aux réponses qui vous intéressent.
Constructeur :
RequestInitiator(ag, "mon-nom", "nom-cible", "contenu", "ontologie", timeout_ms)
// │ │ │ │ │ │
// │ │ │ │ │ └ délai max (ms)
// │ │ │ │ └ optionnel
// │ │ │ └ ce que vous demandez
// │ │ └ à qui vous envoyez la demande
// │ └ votre nom
// └ this (l'agent)
Méthodes à surcharger :
Méthode |
Obligatoire |
Appelée quand… |
|---|---|---|
|
Non |
La demande a abouti — |
|
Non |
La demande a été refusée — |
|
Non |
L’exécuteur avait accepté mais a échoué |
|
Non |
L’exécuteur a accepté la demande (résultat à venir) |
|
Non |
Aucune réponse dans le délai imparti |
Exemple — demander un calcul :
#include <gagent/protocols/Request.hpp>
using namespace gagent::protocols;
class DemandeCalcul : public RequestInitiator {
public:
DemandeCalcul(Agent* ag)
: RequestInitiator(
ag,
"alice", // mon nom
"calculateur", // à qui je demande
"6 * 7", // ce que je demande
"", // ontologie (ici vide)
5000 // attendre 5 secondes max
)
{}
void handleInform(const ACLMessage& msg) override {
std::cout << "[Alice] Résultat : " << msg.getContent() << std::endl;
}
void handleRefuse(const ACLMessage& msg) override {
std::cout << "[Alice] Refusé : " << msg.getContent() << std::endl;
}
void handleTimeout() override {
std::cout << "[Alice] Pas de réponse." << std::endl;
}
};
Le participant — RequestParticipant
Le participant tourne en continu, écoute les demandes entrantes, et pour
chacune appelle handleRequest() pour construire la réponse.
Vous n’implémentez qu”une seule méthode : handleRequest(). Elle
reçoit la demande et doit retourner un message de réponse — soit
INFORM avec le résultat, soit REFUSE ou FAILURE selon le cas.
class ServiceCalcul : public RequestParticipant {
public:
ServiceCalcul(Agent* ag)
: RequestParticipant(ag, "calculateur")
{}
ACLMessage handleRequest(const ACLMessage& req) override {
std::string demande = req.getContent(); // ex : "6 * 7"
// Traiter la demande
int resultat = 42; // votre logique ici
// Retourner le résultat
ACLMessage reponse = req.createReply(ACLMessage::Performative::INFORM);
reponse.setSender(AgentIdentifier{"calculateur"});
reponse.setContent(std::to_string(resultat));
return reponse;
}
};
Pour refuser une demande :
ACLMessage handleRequest(const ACLMessage& req) override {
if (/* condition de refus */) {
ACLMessage refus = req.createReply(ACLMessage::Performative::REFUSE);
refus.setSender(AgentIdentifier{"calculateur"});
refus.setContent("opération non supportée");
return refus;
}
// sinon traiter normalement...
}
Cas avec AGREE — traitement long
Quand le traitement prend du temps, le participant peut envoyer un
AGREE immédiatement pour confirmer la réception, puis envoyer
l”INFORM une fois le travail terminé (FIPA SC00026H §3.4).
Il suffit de surcharger deux méthodes dans RequestParticipant :
prepareAgree()— retournertruepour déclencher l’envoi automatique duAGREEhandleRequest()— votre traitement (peut bloquer, leAGREEest déjà parti)
class ServiceLent : public RequestParticipant {
public:
ServiceLent(Agent* ag)
: RequestParticipant(ag, "service-lent") {}
// Retourner true → AGREE envoyé automatiquement avant handleRequest()
bool prepareAgree(const ACLMessage& /*req*/) override {
return true;
}
ACLMessage handleRequest(const ACLMessage& req) override {
// Le demandeur a déjà reçu AGREE — on peut prendre le temps nécessaire
std::this_thread::sleep_for(std::chrono::seconds(3));
ACLMessage rep = req.createReply(ACLMessage::Performative::INFORM);
rep.setSender(AgentIdentifier{"service-lent"});
rep.setContent("traitement terminé");
return rep;
}
};
Côté demandeur, handleAgree() est appelé dès réception du AGREE :
class MonClient : public RequestInitiator {
public:
MonClient(Agent* ag)
: RequestInitiator(ag, "client", "service-lent", "tâche-longue", "", 30000) {}
void handleAgree(const ACLMessage&) override {
std::cout << "[Client] Le service a accepté, résultat en cours...\n";
}
void handleInform(const ACLMessage& msg) override {
std::cout << "[Client] Résultat : " << msg.getContent() << "\n";
}
};
Le flux complet est alors :
Client ServiceLent
│──── REQUEST ──────────────►│
│ │ prepareAgree() retourne true
│◄─── AGREE ─────────────────│ "j'ai reçu, je travaille"
│ │ handleRequest() s'exécute (3s)
│◄─── INFORM ────────────────│ "traitement terminé"
Note
Si prepareAgree() n’est pas surchargée (ou retourne false),
le comportement par défaut est REQUEST → INFORM direct — aucun
AGREE n’est envoyé. C’est le cas le plus fréquent pour les
traitements rapides.
Exemple complet
#include <gagent/core/Agent.hpp>
#include <gagent/core/Behaviour.hpp>
#include <gagent/core/AgentCore.hpp>
#include <gagent/protocols/Request.hpp>
#include <iostream>
using namespace gagent;
using namespace gagent::protocols;
using namespace gagent::messaging;
// ── Le service : répond aux demandes de calcul ───────────────────────────
class ServiceCalcul : public RequestParticipant {
public:
ServiceCalcul(Agent* ag) : RequestParticipant(ag, "calculateur") {}
ACLMessage handleRequest(const ACLMessage& req) override {
std::cout << "[Calculateur] Demande reçue : "
<< req.getContent() << std::endl;
ACLMessage rep = req.createReply(ACLMessage::Performative::INFORM);
rep.setSender(AgentIdentifier{"calculateur"});
rep.setContent("42");
return rep;
}
};
class AgentCalculateur : public Agent {
public:
void setup() override {
addBehaviour(new ServiceCalcul(this));
}
};
// ── Le client : envoie une demande et traite la réponse ──────────────────
class DemandeCalcul : public RequestInitiator {
public:
DemandeCalcul(Agent* ag)
: RequestInitiator(ag, "alice", "calculateur", "6 * 7", "", 5000) {}
void handleInform(const ACLMessage& msg) override {
std::cout << "[Alice] Résultat : " << msg.getContent() << std::endl;
}
void handleRefuse(const ACLMessage& msg) override {
std::cout << "[Alice] Refusé : " << msg.getContent() << std::endl;
}
void handleTimeout() override {
std::cout << "[Alice] Pas de réponse dans les délais." << std::endl;
}
};
class AgentAlice : public Agent {
public:
void setup() override {
addBehaviour(new DemandeCalcul(this));
}
};
// ── main ─────────────────────────────────────────────────────────────────
int main() {
AgentCore::initAgentSystem();
AgentCalculateur calculateur;
AgentAlice alice;
calculateur.init();
alice.init();
AgentCore::syncAgentSystem(); // attend que tous les agents terminent
return 0;
}
Résultat :
[Calculateur] Demande reçue : 6 * 7
[Alice] Résultat : 42
Résumé
Élément |
Rôle |
|---|---|
|
Envoie la demande, gère toutes les réponses possibles via des callbacks |
|
Écoute les demandes en continu, retourne une réponse via |
|
Seule méthode obligatoire côté participant — retourner INFORM, REFUSE ou FAILURE |
|
Côté initiateur — la demande a abouti |
|
Côté initiateur — la demande a été refusée |
|
Côté initiateur — pas de réponse dans le délai |
|
Côté participant — retourner |