#!/usr/bin/env python3 # -*- coding: utf-8 -*- import asyncio import sys import requests import json from pathlib import Path from slixmpp import ClientXMPP # Ajouter /opt/agent au path pour importer les skills sys.path.insert(0, "/opt/agent") from skills.loader import load_skills, run_skills # ── CONFIG ─────────────────────────────────────────────────────────────── CONFIG_DIR = Path("/opt/agent/config") CONFIG_FILE = CONFIG_DIR / "config.json" PROMPT_FILE = CONFIG_DIR / "system_prompt.txt" def load_config(): with open(CONFIG_FILE, "r", encoding="utf-8") as f: return json.load(f) def load_system_prompt(): with open(PROMPT_FILE, "r", encoding="utf-8") as f: return f.read() cfg = load_config() OLLAMA_URL = cfg["ollama_url"] MODEL = cfg["model"] XMPP_JID = cfg["xmpp_jid"] XMPP_PASS = cfg["xmpp_pass"] ADMIN_JID = cfg["admin_jid"] SYSTEM_PROMPT = load_system_prompt() # Charger les skills au démarrage load_skills() conversation_history = [] # ── LLM ────────────────────────────────────────────────────────────────── def call_ollama(messages: list) -> str: payload = { "model" : MODEL, "messages": messages, "stream" : False, "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 try: # Boucle agentique : le LLM peut enchaîner plusieurs skills MAX_STEPS = 5 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}) 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}) return reply except Exception as e: error_reply = "Erreur : " + str(e) conversation_history.append({"role": "assistant", "content": error_reply}) return error_reply # ── BOT XMPP ───────────────────────────────────────────────────────────── class AgentBot(ClientXMPP): def __init__(self): ClientXMPP.__init__(self, XMPP_JID, XMPP_PASS) self.add_event_handler("session_start", self.session_start) self.add_event_handler("message", self.message) self.register_plugin('xep_0030') self.register_plugin('xep_0199') 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') async def message(self, msg): if msg['type'] not in ('chat', 'normal'): return if str(msg['from']).split('/')[0] != ADMIN_JID: return user_input = msg['body'].strip() if user_input == "!reset": conversation_history.clear() self.send_message(mto=ADMIN_JID, mbody="Conversation reinitialisee.", mtype='chat') return loop = asyncio.get_event_loop() reply = await loop.run_in_executor(None, ask_llm, user_input) self.send_message(mto=ADMIN_JID, mbody=reply, mtype='chat') # ── MAIN ───────────────────────────────────────────────────────────────── if __name__ == "__main__": bot = AgentBot() bot.connect() bot.loop.run_forever()