Suivi temps réel des agents : MQTT status/retain + injection dynamique dans le prompt
- on_mqtt_status : dict AGENTS_ONLINE + agents_online.json + notif XMPP si changement - _get_agents_context() : liste agents avec statut [EN LIGNE/hors ligne] à chaque LLM call - system_prompt : retrait liste hardcodée, agents injectés dynamiquement Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,7 +40,26 @@ SYSTEM_PROMPT = load_system_prompt()
|
|||||||
load_skills()
|
load_skills()
|
||||||
|
|
||||||
conversation_history = []
|
conversation_history = []
|
||||||
xmpp_bot = None # référence globale pour répondre via XMPP depuis MQTT
|
xmpp_bot = None # référence globale pour répondre via XMPP depuis MQTT
|
||||||
|
AGENTS_ONLINE = {} # {agent_name: {status, jid, mqtt_inbox, last_seen}}
|
||||||
|
|
||||||
|
REGISTRY_FILE = CONFIG_DIR / "agents_registry.json"
|
||||||
|
AGENTS_ONLINE_FILE = CONFIG_DIR / "agents_online.json"
|
||||||
|
|
||||||
|
def _get_agents_context() -> str:
|
||||||
|
"""Construit dynamiquement la liste des agents (registre + statut en ligne)."""
|
||||||
|
try:
|
||||||
|
registry = json.loads(REGISTRY_FILE.read_text(encoding="utf-8"))
|
||||||
|
except Exception:
|
||||||
|
registry = {}
|
||||||
|
if not registry:
|
||||||
|
return "Aucun agent enregistré."
|
||||||
|
lines = ["Agents disponibles :"]
|
||||||
|
for name, info in registry.items():
|
||||||
|
online = name in AGENTS_ONLINE and AGENTS_ONLINE[name].get("status") == "online"
|
||||||
|
status = "[EN LIGNE]" if online else "[hors ligne]"
|
||||||
|
lines.append("- {} {} : {}".format(name, status, info.get("speciality", "")))
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
# ── LLM ──────────────────────────────────────────────────────────────────
|
# ── LLM ──────────────────────────────────────────────────────────────────
|
||||||
def call_ollama(messages: list) -> str:
|
def call_ollama(messages: list) -> str:
|
||||||
@@ -57,7 +76,8 @@ def ask_llm(user_message: str, history: list = None) -> str:
|
|||||||
if history is None:
|
if history is None:
|
||||||
history = conversation_history
|
history = conversation_history
|
||||||
history.append({"role": "user", "content": user_message})
|
history.append({"role": "user", "content": user_message})
|
||||||
messages = [{"role": "system", "content": SYSTEM_PROMPT}] + history
|
full_prompt = SYSTEM_PROMPT + "\n\n" + _get_agents_context()
|
||||||
|
messages = [{"role": "system", "content": full_prompt}] + history
|
||||||
try:
|
try:
|
||||||
MAX_STEPS = 10
|
MAX_STEPS = 10
|
||||||
for _ in range(MAX_STEPS):
|
for _ in range(MAX_STEPS):
|
||||||
@@ -133,6 +153,34 @@ def on_mqtt_notification(client, userdata, msg):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("[MQTT] Erreur parsing notification scheduler : {}".format(e))
|
print("[MQTT] Erreur parsing notification scheduler : {}".format(e))
|
||||||
|
|
||||||
|
def on_mqtt_status(client, userdata, msg):
|
||||||
|
"""Suit le statut en ligne/hors-ligne des agents (LWT + retain)."""
|
||||||
|
import time
|
||||||
|
try:
|
||||||
|
data = json.loads(msg.payload.decode(errors="replace"))
|
||||||
|
agent = data.get("agent", "?")
|
||||||
|
status = data.get("status", "?")
|
||||||
|
|
||||||
|
was_online = AGENTS_ONLINE.get(agent, {}).get("status") == "online"
|
||||||
|
AGENTS_ONLINE[agent] = {**data, "last_seen": time.time()}
|
||||||
|
|
||||||
|
# Sauvegarder pour la skill agents_online
|
||||||
|
AGENTS_ONLINE_FILE.write_text(
|
||||||
|
json.dumps(AGENTS_ONLINE, indent=2, ensure_ascii=False), encoding="utf-8")
|
||||||
|
|
||||||
|
# Notifier sylvain uniquement si le statut change
|
||||||
|
is_online = status == "online"
|
||||||
|
if is_online == was_online:
|
||||||
|
return
|
||||||
|
emoji = "[EN LIGNE]" if is_online else "[HORS LIGNE]"
|
||||||
|
print("[STATUS] {} → {}".format(agent, status))
|
||||||
|
if xmpp_bot:
|
||||||
|
xmpp_bot.send_message(mto=ADMIN_JID,
|
||||||
|
mbody="{} {}".format(emoji, agent),
|
||||||
|
mtype='chat')
|
||||||
|
except Exception as e:
|
||||||
|
print("[MQTT] Erreur parsing status : {}".format(e))
|
||||||
|
|
||||||
def on_mqtt_register(client, userdata, msg):
|
def on_mqtt_register(client, userdata, msg):
|
||||||
"""Reçoit les déclarations de mise en ligne des agents et met à jour le registre."""
|
"""Reçoit les déclarations de mise en ligne des agents et met à jour le registre."""
|
||||||
try:
|
try:
|
||||||
@@ -183,6 +231,7 @@ def start_mqtt_listener():
|
|||||||
sub.message_callback_add("agents/errors", on_mqtt_error)
|
sub.message_callback_add("agents/errors", on_mqtt_error)
|
||||||
sub.message_callback_add("agents/scheduler/notifications", on_mqtt_notification)
|
sub.message_callback_add("agents/scheduler/notifications", on_mqtt_notification)
|
||||||
sub.message_callback_add("agents/register", on_mqtt_register)
|
sub.message_callback_add("agents/register", on_mqtt_register)
|
||||||
|
sub.message_callback_add("agents/status/+", on_mqtt_status)
|
||||||
sub.on_message = on_mqtt_message # fallback
|
sub.on_message = on_mqtt_message # fallback
|
||||||
sub.connect(MQTT_HOST, MQTT_PORT)
|
sub.connect(MQTT_HOST, MQTT_PORT)
|
||||||
sub.subscribe([
|
sub.subscribe([
|
||||||
@@ -190,8 +239,9 @@ def start_mqtt_listener():
|
|||||||
("agents/errors", 0),
|
("agents/errors", 0),
|
||||||
("agents/scheduler/notifications", 0),
|
("agents/scheduler/notifications", 0),
|
||||||
("agents/register", 0),
|
("agents/register", 0),
|
||||||
|
("agents/status/+", 0),
|
||||||
])
|
])
|
||||||
print("[MQTT] Agent1 écoute sur {}, agents/errors, agents/scheduler/notifications, agents/register".format(MQTT_INBOX))
|
print("[MQTT] Agent1 écoute sur {}, agents/errors, agents/status/+, agents/register".format(MQTT_INBOX))
|
||||||
sub.loop_forever()
|
sub.loop_forever()
|
||||||
|
|
||||||
# ── BOT XMPP ─────────────────────────────────────────────────────────────
|
# ── BOT XMPP ─────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -2,24 +2,23 @@ Tu es agent1, chef d'orchestre d'un réseau d'agents autonomes spécialisés.
|
|||||||
Tu reçois les instructions de sylvain (via XMPP ou CLI) et tu décides de les traiter toi-même ou de les déléguer.
|
Tu reçois les instructions de sylvain (via XMPP ou CLI) et tu décides de les traiter toi-même ou de les déléguer.
|
||||||
Les agents ne peuvent pas travailler en parallèle : tu exécutes les tâches séquentiellement.
|
Les agents ne peuvent pas travailler en parallèle : tu exécutes les tâches séquentiellement.
|
||||||
|
|
||||||
Agents disponibles sous tes ordres :
|
La liste des agents disponibles et leur statut (EN LIGNE / hors ligne) est injectée dynamiquement
|
||||||
|
à la suite de ce prompt. Utilise ces informations pour choisir quel agent solliciter.
|
||||||
- agent2_debian13 : Administration Debian (apt, systemd, conteneurs LXC/Docker, KVM, réseau, sécurité, exécution de commandes système)
|
|
||||||
|
|
||||||
Commandes disponibles :
|
Commandes disponibles :
|
||||||
|
|
||||||
DELEGATE: <agent> | <tâche>
|
DELEGATE: <agent> | <tâche>
|
||||||
→ Déléguer une tâche unique à un agent spécialisé
|
→ Déléguer une tâche unique à un agent spécialisé
|
||||||
→ Exemple : DELEGATE: agent2_debian13 | Vérifie l'espace disque
|
→ Exemple : DELEGATE: trouducul | Vérifie l'espace disque
|
||||||
|
|
||||||
PLAN: <agent> | <tâche1> ;; <agent> | <tâche2> ;; ...
|
PLAN: <agent> | <tâche1> ;; <agent> | <tâche2> ;; ...
|
||||||
→ Exécuter un plan de tâches séquentiel (le résultat de chaque étape est transmis à la suivante)
|
→ Exécuter un plan de tâches séquentiel (le résultat de chaque étape est transmis à la suivante)
|
||||||
→ Exemple : PLAN: agent2_debian13 | apt update ;; agent2_debian13 | apt upgrade -y
|
→ Exemple : PLAN: trouducul | apt update ;; trouducul | apt upgrade -y
|
||||||
|
|
||||||
SCHEDULE: <fréquence> | <agent> | <tâche>
|
SCHEDULE: <fréquence> | <agent> | <tâche>
|
||||||
→ Planifier une tâche récurrente
|
→ Planifier une tâche récurrente
|
||||||
→ Fréquences : daily HH:MM | every Xh | every Xmin | weekly <lun|mar|mer|jeu|ven|sam|dim> HH:MM
|
→ Fréquences : daily HH:MM | every Xh | every Xmin | weekly <lun|mar|mer|jeu|ven|sam|dim> HH:MM
|
||||||
→ Exemple : SCHEDULE: daily 03:00 | agent2_debian13 | apt update && apt upgrade -y
|
→ Exemple : SCHEDULE: daily 03:00 | trouducul | apt update && apt upgrade -y
|
||||||
|
|
||||||
PLAN_LIST:
|
PLAN_LIST:
|
||||||
→ Afficher toutes les tâches planifiées
|
→ Afficher toutes les tâches planifiées
|
||||||
@@ -49,7 +48,8 @@ RECALL: <clé>
|
|||||||
→ Récupérer une information mémorisée
|
→ Récupérer une information mémorisée
|
||||||
|
|
||||||
⚠ RÈGLES :
|
⚠ RÈGLES :
|
||||||
- Tâche Debian/système → DELEGATE: agent2_debian13 (ou PLAN: pour plusieurs étapes)
|
- Utilise le nom exact de l'agent tel qu'il apparaît dans la liste ci-dessous pour DELEGATE
|
||||||
|
- Délègue uniquement à un agent [EN LIGNE] — si l'agent est hors ligne, informe sylvain
|
||||||
- Tâche récurrente → SCHEDULE:
|
- Tâche récurrente → SCHEDULE:
|
||||||
- Actualité/info récente → SEARCH:
|
- Actualité/info récente → SEARCH:
|
||||||
- Un seul agent à la fois (pas de parallélisme)
|
- Un seul agent à la fois (pas de parallélisme)
|
||||||
|
|||||||
Reference in New Issue
Block a user