File size: 6,152 Bytes
831e835 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
import json
import logging
import os
import sys
import time
import uuid
from groq import Groq
# Importa nossos módulos customizados
from .handlers import ImageHandler
from .tts import TTSPlayer
from .utils import slim_history
from .shorestone import ShoreStoneMemory
from .curator_heuristic import MemoryCuratorHeuristic
# Configura o logger principal
logging.basicConfig(level=logging.INFO, format="%(asctime)s - JADE - %(levelname)s - %(message)s")
class JadeAgent:
def __init__(self, config_path="jade/config.json"):
# Carrega configurações
# Try to load from absolute path first, then relative
try:
with open(config_path) as f:
self.cfg = json.load(f)
except FileNotFoundError:
# Fallback: try to find it relative to this file
base_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(base_dir, "config.json")
with open(config_path) as f:
self.cfg = json.load(f)
# --- Configuração da API Groq ---
logging.info("Iniciando J.A.D.E. em modo API (Groq)...")
self.api_key = self._get_api_key()
self.client = Groq(api_key=self.api_key)
self.model_name = self.cfg.get("groq_model", "meta-llama/llama-4-maverick-17b-128e-instruct")
# System Prompt Base
self.system_prompt = {"role": "system", "content": "Você é J.A.D.E., uma IA multimodal calma e inteligente. Seja direta. Responda de forma concisa e natural. NÃO explique seu processo de pensamento. Apenas responda à pergunta."}
# --- Inicialização dos Módulos ---
logging.info("Carregando módulos de percepção e memória...")
# Visão e Fala
self.image_handler = ImageHandler(self.cfg.get("caption_model", "Salesforce/blip-image-captioning-large"))
self.tts = TTSPlayer(lang=self.cfg.get("language", "pt"))
# 1. Memória ShoreStone (Persistente)
self.memory = ShoreStoneMemory()
# Inicializa com sessão padrão, mas será trocada dinamicamente no respond()
self.memory.load_or_create_session("sessao_padrao_gabriel")
# 2. Curador Heurístico (Manutenção Automática)
self.curator = MemoryCuratorHeuristic(shorestone_memory=self.memory)
self.response_count = 0
self.maintenance_interval = 10 # Executar a manutenção a cada 10 interações
logging.info(f"J.A.D.E. pronta e conectada ao modelo {self.model_name}.")
def _get_api_key(self):
"""Recupera a chave da API do ambiente de forma segura."""
key = os.getenv("GROQ_API_KEY")
if not key:
logging.error("Chave GROQ_API_KEY não encontrada nas variáveis de ambiente.")
# For development, try to warn but not crash if possible, but Groq needs it.
# raise RuntimeError("❌ GROQ_API_KEY não encontrada. Defina a variável de ambiente.")
print("WARNING: GROQ_API_KEY not found.")
return key
def _chat(self, messages):
"""Envia as mensagens para a Groq e retorna a resposta."""
try:
chat = self.client.chat.completions.create(
messages=messages,
model=self.model_name,
temperature=0.7, # Criatividade balanceada
max_tokens=1024 # Limite de resposta razoável
)
return chat.choices[0].message.content.strip()
except Exception as e:
logging.error(f"Erro na comunicação com a Groq: {e}")
return "Desculpe, tive um problema ao me conectar com meu cérebro na nuvem."
def respond(self, history, user_input, user_id="default", vision_context=None):
"""Processo principal de raciocínio: Lembrar -> Ver -> Responder -> Memorizar -> Manter."""
# TROCA A SESSÃO DA MEMÓRIA PARA O USUÁRIO ATUAL
session_name = f"user_{user_id}"
self.memory.load_or_create_session(session_name)
messages = history[:]
# 1. Lembrar (Recuperação de Contexto)
memories = self.memory.remember(user_input)
if memories:
memory_context = f"--- MEMÓRIAS RELEVANTES (ShoreStone) ---\n{memories}\n--- FIM DAS MEMÓRIAS ---"
# Inserimos as memórias como contexto de sistema para guiar a resposta
messages.append({"role": "system", "content": memory_context})
# 2. Ver (Contexto Visual)
if vision_context:
messages.append({"role": "system", "content": f"Contexto visual da imagem que o usuário enviou: {vision_context}"})
# Adiciona a pergunta atual ao histórico temporário e ao prompt
history.append({"role": "user", "content": user_input})
messages.append({"role": "user", "content": user_input})
# 3. Responder (Geração)
resposta = self._chat(messages)
# Atualiza histórico
history.append({"role": "assistant", "content": resposta})
history = slim_history(history, keep=self.cfg.get("max_context", 12))
# 4. Memorizar (Armazenamento Persistente)
self.memory.memorize(user_input, resposta)
print(f"\n🤖 J.A.D.E.: {resposta}")
# Falar (TTS) - Modified for Backend compatibility
audio_path = None
try:
# Uses the TTSPlayer from tts.py which has save_audio_to_file
audio_path = self.tts.save_audio_to_file(resposta)
except Exception as e:
logging.warning(f"TTS falhou (silenciado): {e}")
# 5. Manter (Ciclo de Curadoria Automática)
self.response_count += 1
if self.response_count % self.maintenance_interval == 0:
logging.info(f"Ciclo de manutenção agendado (interação {self.response_count}). Verificando saúde da memória...")
try:
self.curator.run_maintenance_cycle()
except Exception as e:
logging.error(f"Erro no Curador de Memória: {e}")
return resposta, audio_path, history
|