from llms.Modelos.Base_model import ModeloABC from llms.Memory.Base_MemoryConv import MemoryConvABC from datetime import datetime from typing import Optional, List, Union class AgenteAI: def __init__( self, modelo: ModeloABC, nombre: str, descripcion: str, system_prompt: str, # <- pertenece al agente rol: str, objetivos: List[str], memoria: Optional[MemoryConvABC] = None, version: str = "1.0.0", tools: Optional[List] = None, output_schema: Optional[dict] = None, # <- nuevo parámetro ): self.modelo = modelo self.memoria = memoria self.tools = tools or [] self.output_schema = output_schema self.nombre = nombre self.descripcion = descripcion self.system_prompt = system_prompt self.rol = rol self.objetivos = objetivos self.version = version self.created_at = datetime.now() self.updated_at = self.created_at self.numero_interacciones = 0 def actualizar_configuracion(self, **kwargs): for clave, valor in kwargs.items(): if hasattr(self, clave): setattr(self, clave, valor) self.updated_at = datetime.now() @property def full_system_prompt(self) -> str: partes = [ f"Tu nombre es: {self.nombre}", f"Tu descripción: {self.descripcion}", f"Tu Rol: {self.rol}", f"Tus Objetivos: {', '.join(self.objetivos)}", "" ] # Incluir herramientas disponibles herramientas = self._obtener_descripcion_tools() if herramientas: partes.append("Estas son tus herramientas disponibles:\n") partes.extend(herramientas) partes.append("Úsalas cuando creas oportuno.\n") partes.append(self.system_prompt) if self.output_schema: partes.append( "SIEMPRE formatea la respuesta final siguiendo estrictamente el siguiente esquema JSON:" ) partes.append(f"```json\n{self.output_schema}\n```") return "\n".join(partes) def _obtener_descripcion_tools(self) -> List[str]: """ Devuelve una lista de strings con el nombre y descripción de cada herramienta recogida desde los servidores MCP conectados. """ descripciones = [] if not hasattr(self, "mcp_servers"): return descripciones for server in self.mcp_servers: if hasattr(server, "tools") and server.tools: for tool in server.tools: # tool puede ser string o dict, depende cómo lo expongas if isinstance(tool, str): descripciones.append(f"- {tool}: [sin descripción]") elif isinstance(tool, dict): nombre = tool.get("name", "¿?") descripcion = tool.get("description", "[sin descripción]") descripciones.append(f"- {nombre}: {descripcion}") elif hasattr(tool, "name"): descripcion = getattr(tool, "description", "[sin descripción]") descripciones.append(f"- {tool.name}: {descripcion}") return descripciones async def interactuar(self, prompt: str) -> str: if not self.modelo: raise ValueError("El agente no tiene un modelo asignado.") historial = [] if self.memoria: historial = self.memoria.cargar_historial_chat() contexto = historial + [{"role": "user", "content": prompt}] prompt_final = self._formatear_prompt(contexto) # Espera la respuesta del modelo de forma asíncrona respuesta = await self.modelo.responder( prompt=prompt_final, system_prompt=self.full_system_prompt ) if self.memoria: self.memoria.guardar_turno("user", prompt) self.memoria.guardar_turno("assistant", respuesta) self.numero_interacciones += 1 self.updated_at = datetime.now() return respuesta def _formatear_prompt(self, mensajes: List[dict]) -> str: return "\n".join([f"{msg['role']}: {msg['content']}" for msg in mensajes])