hermes / brain.py
Ayman666's picture
Upload folder using huggingface_hub
6370a5c verified
Raw
History Blame Contribute Delete
3.39 kB
"""
HERMES — il cervello (ciclo agentico con tool-calling).
E qui che Hermes "ragiona": riceve la conversazione, decide DA SOLO se usare
uno strumento (ricerca web, leggere/aggiornare la dashboard, salvare in memoria),
ne osserva il risultato, e continua finche non ha una risposta per Ayman.
Usa le API LLM in cascata: DeepSeek (principale) -> Groq -> OpenAI (riserve),
cosi se un provider e giu o a corto di credito, l'agente continua a funzionare.
"""
from __future__ import annotations
import json
import requests
from config import LLM_PROVIDERS, LLM_TIMEOUT
from skills import TOOLS, dispatch
MAX_STEPS = 6 # quanti giri di ragionamento+strumenti al massimo per messaggio
def _chat(provider: dict, messages: list, use_tools: bool) -> dict | None:
"""Una chiamata a un provider. Restituisce il messaggio dell'assistente o None."""
if not provider.get("api_key"):
return None
payload = {
"model": provider["model"],
"messages": messages,
"temperature": 0.4,
}
if use_tools:
payload["tools"] = TOOLS
payload["tool_choice"] = "auto"
try:
r = requests.post(
f"{provider['base_url']}/chat/completions",
headers={"Authorization": f"Bearer {provider['api_key']}",
"Content-Type": "application/json"},
json=payload, timeout=LLM_TIMEOUT,
)
if r.status_code != 200:
print(f"[brain] {provider['name']} HTTP {r.status_code}: {r.text[:160]}", flush=True)
return None
return r.json()["choices"][0]["message"]
except Exception as e:
print(f"[brain] {provider['name']} errore: {e}", flush=True)
return None
def _chiama_llm(messages: list, use_tools: bool = True) -> dict | None:
"""Prova i provider in cascata finche uno risponde."""
for provider in LLM_PROVIDERS:
msg = _chat(provider, messages, use_tools)
if msg is not None:
return msg
return None
def pensa(messages: list) -> str:
"""Ciclo agentico completo. messages = [system, ...storia..., user]."""
msgs = list(messages)
for _ in range(MAX_STEPS):
msg = _chiama_llm(msgs, use_tools=True)
if msg is None:
return "Scusa, ora non riesco a ragionare (cervello AI non raggiungibile). Riprova tra poco."
tool_calls = msg.get("tool_calls")
if not tool_calls:
return (msg.get("content") or "").strip() or "(nessuna risposta)"
# l'agente ha deciso di usare uno o piu strumenti
msgs.append(msg)
for tc in tool_calls:
fn = tc.get("function", {})
nome = fn.get("name", "")
try:
argomenti = json.loads(fn.get("arguments") or "{}")
except Exception:
argomenti = {}
print(f"[brain] strumento -> {nome}({argomenti})", flush=True)
risultato = dispatch(nome, argomenti)
msgs.append({
"role": "tool",
"tool_call_id": tc.get("id", ""),
"content": risultato,
})
# raggiunto il limite di giri: forza una risposta senza altri strumenti
finale = _chiama_llm(msgs, use_tools=False)
return (finale.get("content") if finale else "") or "Ho fatto un po' di passaggi ma non sono arrivato a una risposta netta. Riformula?"