diff --git a/config/agents_registry.json b/config/agents_registry.json new file mode 100644 index 0000000..5b964da --- /dev/null +++ b/config/agents_registry.json @@ -0,0 +1,8 @@ +{ + "agent2_debian13": { + "jid" : "agent2_debian13@xmpp.ovh", + "mqtt_inbox" : "agents/agent2_debian13/inbox", + "mqtt_outbox" : "agents/agent1/inbox", + "speciality" : "Administration Debian : apt, dpkg, systemd, conteneurs LXC/Docker, KVM, réseau, sécurité système" + } +} diff --git a/config/system_prompt.txt b/config/system_prompt.txt index 717d0c4..d6915fa 100644 --- a/config/system_prompt.txt +++ b/config/system_prompt.txt @@ -1,9 +1,16 @@ -Tu es un agent autonome de recherche web. -Tu peux chercher des informations sur internet et lire des pages web. -Tu mémorises les informations importantes pour les réutiliser. +Tu es agent1, chef d'orchestre d'un réseau d'agents autonomes spécialisés. +Tu reçois les instructions de sylvain et tu décides de les traiter toi-même ou de les déléguer au bon agent spécialisé. + +Agents disponibles sous tes ordres : + +- agent2_debian13 : Administration Debian (apt, systemd, conteneurs LXC/Docker, KVM, réseau, sécurité système) Formats de commandes disponibles : +DELEGATE: | + → Déléguer une tâche à un agent spécialisé et attendre sa réponse + → Exemple : DELEGATE: agent2_debian13 | Comment mettre à jour les paquets Debian ? + SEARCH: → Recherche web DuckDuckGo (max 5 résultats) @@ -16,19 +23,10 @@ REMEMBER: | RECALL: → Récupérer une information mémorisée -⚠ RÈGLES ABSOLUES : -- Pour toute question sur l'actualité, les événements récents, les prix, - les versions de logiciels, les personnes en poste, la météo, ou tout - fait pouvant avoir changé : utilise TOUJOURS SEARCH: +⚠ RÈGLES : +- Si la demande concerne Debian, Linux, des conteneurs, des VMs ou l'administration système : utilise DELEGATE: agent2_debian13 +- Si la demande concerne l'actualité, des événements récents ou des faits changeants : utilise SEARCH: - Ne JAMAIS répondre de mémoire à une question d'actualité -- Commence TOUJOURS par SEARCH: si la question concerne une information - datée ou changeante -- Ta date de coupure est ancienne : toute info récente DOIT être vérifiée - via SEARCH: -- Si les résultats de recherche ne sont pas suffisants, utilise READ: sur - les URLs prometteuses pour approfondir -- Mémorise les informations importantes avec REMEMBER: -- Synthétise toujours les informations trouvées de façon claire et concise - -Réponds toujours en français. Sois concis mais précis. -Explique ce que tu vas faire avant de le faire. +- Les agents ne peuvent pas travailler en parallèle : délègue une tâche à la fois +- Synthétise et transmets la réponse de l'agent spécialisé à sylvain +- Réponds toujours en français. Sois concis mais précis. diff --git a/skills/delegate.py b/skills/delegate.py new file mode 100644 index 0000000..9c80aa4 --- /dev/null +++ b/skills/delegate.py @@ -0,0 +1,76 @@ +""" +Skill : DELEGATE +Délègue une tâche à un agent spécialisé via MQTT et attend sa réponse. + +Commande : + DELEGATE: | + +Exemple : + DELEGATE: agent2_debian13 | Comment installer Docker sur Debian 13 ? +""" +import json +import time +import threading +from pathlib import Path +import paho.mqtt.client as mqtt + +SKILL_NAME = "delegate" +TRIGGER = "DELEGATE:" + +CONFIG_FILE = Path("/opt/agent/config/config.json") +REGISTRY_FILE = Path("/opt/agent/config/agents_registry.json") +TIMEOUT = 120 # secondes max d'attente + +def _load(): + cfg = json.loads(CONFIG_FILE.read_text()) + registry = json.loads(REGISTRY_FILE.read_text()) + return cfg, registry + +def execute(args: str) -> str: + if "|" not in args: + return "Erreur : format attendu → DELEGATE: | " + + agent_name, _, task = args.partition("|") + agent_name, task = agent_name.strip(), task.strip() + + cfg, registry = _load() + if agent_name not in registry: + available = ", ".join(registry.keys()) + return "Agent inconnu : «{}». Agents disponibles : {}".format(agent_name, available) + + agent = registry[agent_name] + inbox = agent["mqtt_inbox"] + outbox = agent["mqtt_outbox"] + host = cfg.get("mqtt_host", "localhost") + port = int(cfg.get("mqtt_port", 1883)) + + response_received = threading.Event() + response_container = [] + + def on_message(client, userdata, msg): + response_container.append(msg.payload.decode(errors="replace")) + response_received.set() + + # Souscription à la réponse + sub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="agent1_delegate_sub") + sub.on_message = on_message + sub.connect(host, port) + sub.subscribe(outbox) + sub.loop_start() + + # Envoi de la tâche + pub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="agent1_delegate_pub") + pub.connect(host, port) + pub.publish(inbox, task) + pub.disconnect() + + print("[DELEGATE] Tâche envoyée à {} : {}".format(agent_name, task[:80])) + + # Attente de la réponse + received = response_received.wait(timeout=TIMEOUT) + sub.loop_stop() + sub.disconnect() + + if received and response_container: + return "[{}] {}".format(agent_name, response_container[0]) + return "Timeout : {} n'a pas répondu dans les {}s.".format(agent_name, TIMEOUT)