# agents/specialized.py from core.llm_client import llm from utils.prompts import get_today_prefix, get_chat_style from agents.registry import AGENT_REGISTRY from core.file_builder import build_docx, build_excel import re AGENT_ROLES = { "manager": """ Eres el gerente principal. Cuando no haya delegación, responde de forma natural, amigable y útil al usuario. No necesitas generar JSON aquí, solo contesta directamente. """, "image_agent": """ Eres el agente de imágenes. Cuando se te pida generar o buscar imágenes, responde SOLO con: {"image_queries": ["término en inglés 1", "término en inglés 2", "término en inglés 3"]} Los términos deben ser específicos y en inglés para mejores resultados. """, "backend_dev": """ Eres programador backend senior. REGLAS ABSOLUTAS: 1. Entrega SOLO el código pedido, sin explicaciones innecesarias. 2. Para excel/planilla → responde con EXCEL_TEMPLATE:{"title":"...","sheet_name":"...","headers":[...],"sample_rows":[[...],...]} 3. Python → entrega código Python puro y funcional. 4. Groovy/Jenkins → entrega el script completo. 5. Si hay frontend_dev en el equipo, TÚ haces servidor/backend, él hace HTML. 6. Si la tarea no requiere backend → responde: {"skip":"no backend needed"} """, "writer": """ Eres redactor experto. Escribe SOLO contenido real y extenso (500+ palabras). Sin placeholders. Usa ## para secciones y ### para subsecciones. Secciones: ## Resumen Ejecutivo, ### Introducción, ### Desarrollo, ### Hallazgos, ### Conclusiones, ### Recomendaciones """, "analyst": """ Eres analista de negocios. REGLAS: 1. Solo haz lo que el manager delegó: revisar documentos, evaluar viabilidad, analizar riesgos. 2. NUNCA describas imágenes ni hagas trabajo de otros agentes. 3. Si la tarea no requiere análisis → responde: {"skip":"no analysis needed"} """, "frontend_dev": """ Eres desarrollador frontend senior. REGLAS ABSOLUTAS: 1. Entrega SOLO código HTML/CSS/JS pedido, sin explicaciones innecesarias. 2. Si hay backend_dev, TÚ haces HTML/interfaz, él hace servidor/lógica. 3. Si la tarea NO requiere frontend → responde: {"skip":"no frontend needed"} 4. Entrega siempre HTML completo y funcional con los estilos incluidos. """ } async def run_specialized_agent(agent_key: str, task: str, context: dict = None) -> dict: if agent_key not in AGENT_ROLES: return {"error": f"Agente {agent_key} no implementado aún", "success": False} agent = AGENT_REGISTRY.get(agent_key, {}) today = get_today_prefix() role = AGENT_ROLES[agent_key] full_prompt = today + role + get_chat_style() + f"\n\nTarea asignada: {task}" # Agregar contexto si viene de otros agentes if context: full_prompt += "\n\nContexto de otros agentes:\n" + "\n".join([f"[{k.upper()}] {v[:300]}..." for k, v in context.items()]) messages = [{"role": "user", "content": full_prompt}] try: response = await llm.call(agent_key, messages, temperature=0.45) result = { "agent": agent_key, "response": response, "success": True, "file_path": None, "file_type": None } # Generar archivo según agente if agent_key == "writer" and len(response.strip()) > 300: title = task[:60].strip('"') or "Informe generado" try: file_path = build_docx(title, response, images=None) if file_path: result["file_path"] = file_path.name result["file_type"] = "docx" except Exception as e: result["file_error"] = str(e) elif agent_key == "backend_dev": # Detectar EXCEL_TEMPLATE match = re.search(r'EXCEL_TEMPLATE:\s*(\{.*\})', response, re.DOTALL | re.IGNORECASE) if match: try: import json excel_data = json.loads(match.group(1)) file_path = build_excel(task, excel_data) if file_path: result["file_path"] = file_path.name result["file_type"] = "xlsx" # Limpiar respuesta para no mostrar el template crudo result["response"] = response.split("EXCEL_TEMPLATE:")[0].strip() except Exception as e: result["file_error"] = str(e) return result except Exception as e: return { "agent": agent_key, "error": str(e), "success": False }