Initial commit : agent XMPP avec système de skills
- agent1.py : bot XMPP connecté à Ollama avec boucle agentique - skills/web_search.py : recherche DuckDuckGo (ddgs) - skills/web_read.py : lecture et extraction de pages web - skills/memory.py : mémoire persistante SQLite (REMEMBER/RECALL) - skills/loader.py : chargement dynamique des skills
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user