Orchestration complète : planning, scheduling, CLI
- agent1.py : listener MQTT (agents/agent1/inbox), MAX_STEPS 10 - skills/plan.py : exécution séquentielle PLAN: avec contexte entre étapes - skills/schedule_tasks.py : SCHEDULE: / PLAN_LIST: / PLAN_CANCEL: via APScheduler - cli.py : interface CLI rich (MQTT, multi-agents, /plans, /agent) - system_prompt.txt : mis à jour avec tous les nouveaux skills Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,14 +3,14 @@
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import threading
|
||||
import requests
|
||||
import json
|
||||
from pathlib import Path
|
||||
from slixmpp import ClientXMPP
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
# Ajouter /opt/agent au path pour importer les skills
|
||||
sys.path.insert(0, "/opt/agent")
|
||||
|
||||
from skills.loader import load_skills, run_skills
|
||||
|
||||
# ── CONFIG ───────────────────────────────────────────────────────────────
|
||||
@@ -32,12 +32,15 @@ MODEL = cfg["model"]
|
||||
XMPP_JID = cfg["xmpp_jid"]
|
||||
XMPP_PASS = cfg["xmpp_pass"]
|
||||
ADMIN_JID = cfg["admin_jid"]
|
||||
MQTT_HOST = cfg.get("mqtt_host", "localhost")
|
||||
MQTT_PORT = int(cfg.get("mqtt_port", 1883))
|
||||
MQTT_INBOX = "agents/agent1/inbox"
|
||||
SYSTEM_PROMPT = load_system_prompt()
|
||||
|
||||
# Charger les skills au démarrage
|
||||
load_skills()
|
||||
|
||||
conversation_history = []
|
||||
xmpp_bot = None # référence globale pour répondre via XMPP depuis MQTT
|
||||
|
||||
# ── LLM ──────────────────────────────────────────────────────────────────
|
||||
def call_ollama(messages: list) -> str:
|
||||
@@ -48,38 +51,71 @@ def call_ollama(messages: list) -> str:
|
||||
"options" : {"temperature": 0.3}
|
||||
}
|
||||
response = requests.post(OLLAMA_URL, json=payload, timeout=180)
|
||||
data = response.json()
|
||||
return data["message"]["content"]
|
||||
|
||||
def ask_llm(user_message: str) -> str:
|
||||
conversation_history.append({"role": "user", "content": user_message})
|
||||
messages = [{"role": "system", "content": SYSTEM_PROMPT}] + conversation_history
|
||||
return response.json()["message"]["content"]
|
||||
|
||||
def ask_llm(user_message: str, history: list = None) -> str:
|
||||
if history is None:
|
||||
history = conversation_history
|
||||
history.append({"role": "user", "content": user_message})
|
||||
messages = [{"role": "system", "content": SYSTEM_PROMPT}] + history
|
||||
try:
|
||||
# Boucle agentique : le LLM peut enchaîner plusieurs skills
|
||||
MAX_STEPS = 5
|
||||
MAX_STEPS = 10
|
||||
for _ in range(MAX_STEPS):
|
||||
reply = call_ollama(messages)
|
||||
skill_triggered, result = run_skills(reply)
|
||||
|
||||
if not skill_triggered:
|
||||
# Réponse finale sans commande
|
||||
conversation_history.append({"role": "assistant", "content": reply})
|
||||
history.append({"role": "assistant", "content": reply})
|
||||
return reply
|
||||
|
||||
# Injecter le résultat du skill et relancer le LLM
|
||||
messages.append({"role": "assistant", "content": reply})
|
||||
messages.append({"role": "user", "content": "[Résultat skill]\n" + result})
|
||||
|
||||
# Sécurité : trop d'étapes
|
||||
reply = call_ollama(messages)
|
||||
conversation_history.append({"role": "assistant", "content": reply})
|
||||
history.append({"role": "assistant", "content": reply})
|
||||
return reply
|
||||
|
||||
except Exception as e:
|
||||
error_reply = "Erreur : " + str(e)
|
||||
conversation_history.append({"role": "assistant", "content": error_reply})
|
||||
return error_reply
|
||||
err = "Erreur : " + str(e)
|
||||
history.append({"role": "assistant", "content": err})
|
||||
return err
|
||||
|
||||
# ── MQTT LISTENER (pour CLI) ──────────────────────────────────────────────
|
||||
mqtt_pub_client = None
|
||||
|
||||
def mqtt_publish(topic: str, message: str):
|
||||
if mqtt_pub_client:
|
||||
mqtt_pub_client.publish(topic, message)
|
||||
|
||||
def on_mqtt_message(client, userdata, msg):
|
||||
raw = msg.payload.decode(errors="replace")
|
||||
|
||||
# Support JSON avec reply_to optionnel
|
||||
reply_to = "agents/cli/outbox"
|
||||
task = raw
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
task = data.get("task", raw)
|
||||
reply_to = data.get("reply_to", reply_to)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
print("[MQTT] Message CLI reçu : {}".format(task[:80]))
|
||||
mqtt_history = []
|
||||
reply = ask_llm(task, history=mqtt_history)
|
||||
mqtt_publish(reply_to, reply)
|
||||
print("[MQTT] Réponse envoyée sur {}".format(reply_to))
|
||||
|
||||
def start_mqtt_listener():
|
||||
global mqtt_pub_client
|
||||
|
||||
mqtt_pub_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
|
||||
client_id="agent1_pub")
|
||||
mqtt_pub_client.connect(MQTT_HOST, MQTT_PORT)
|
||||
mqtt_pub_client.loop_start()
|
||||
|
||||
sub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="agent1_sub")
|
||||
sub.on_message = on_mqtt_message
|
||||
sub.connect(MQTT_HOST, MQTT_PORT)
|
||||
sub.subscribe(MQTT_INBOX)
|
||||
print("[MQTT] Agent1 écoute sur {}".format(MQTT_INBOX))
|
||||
sub.loop_forever()
|
||||
|
||||
# ── BOT XMPP ─────────────────────────────────────────────────────────────
|
||||
class AgentBot(ClientXMPP):
|
||||
@@ -93,7 +129,7 @@ class AgentBot(ClientXMPP):
|
||||
async def session_start(self, event):
|
||||
self.send_presence()
|
||||
await self.get_roster()
|
||||
self.send_message(mto=ADMIN_JID, mbody="Agent en ligne !", mtype='chat')
|
||||
self.send_message(mto=ADMIN_JID, mbody="Agent1 (orchestrateur) en ligne !", mtype='chat')
|
||||
|
||||
async def message(self, msg):
|
||||
if msg['type'] not in ('chat', 'normal'):
|
||||
@@ -114,6 +150,9 @@ class AgentBot(ClientXMPP):
|
||||
|
||||
# ── MAIN ─────────────────────────────────────────────────────────────────
|
||||
if __name__ == "__main__":
|
||||
bot = AgentBot()
|
||||
bot.connect()
|
||||
bot.loop.run_forever()
|
||||
mqtt_thread = threading.Thread(target=start_mqtt_listener, daemon=True)
|
||||
mqtt_thread.start()
|
||||
|
||||
xmpp_bot = AgentBot()
|
||||
xmpp_bot.connect()
|
||||
xmpp_bot.loop.run_forever()
|
||||
|
||||
Reference in New Issue
Block a user