feat: initial project setup
This commit is contained in:
15
agents/tyrion/Dockerfile
Normal file
15
agents/tyrion/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip install --no-cache-dir \
|
||||
discord.py==2.3.2 \
|
||||
httpx==0.27.0 \
|
||||
PyGithub==2.3.0 \
|
||||
python-dotenv==1.0.1
|
||||
|
||||
COPY ../shared/ ./shared/
|
||||
COPY prompt.txt .
|
||||
COPY main.py .
|
||||
|
||||
CMD ["python", "main.py"]
|
||||
98
agents/tyrion/main.py
Normal file
98
agents/tyrion/main.py
Normal file
@@ -0,0 +1,98 @@
|
||||
import os
|
||||
import json
|
||||
import asyncio
|
||||
import httpx
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
with open("prompt.txt", "r", encoding="utf-8") as f:
|
||||
SYSTEM_PROMPT = f.read()
|
||||
|
||||
OPENWEBUI_URL = os.environ["OPENWEBUI_URL"]
|
||||
OPENWEBUI_API_KEY = os.environ["OPENWEBUI_API_KEY"]
|
||||
MY_CHANNEL_ID = int(os.environ["DISCORD_CHANNEL_ID"])
|
||||
OPENWEBUI_MODEL = os.environ.get("OPENWEBUI_MODEL", "gpt-4o")
|
||||
|
||||
AGENT_CHANNELS = {
|
||||
"varys": int(os.environ.get("DISCORD_CHANNEL_VARYS", 0)),
|
||||
"samwell": int(os.environ.get("DISCORD_CHANNEL_SAMWELL", 0)),
|
||||
"bronn": int(os.environ.get("DISCORD_CHANNEL_BRONN", 0)),
|
||||
"bran": int(os.environ.get("DISCORD_CHANNEL_BRAN", 0)),
|
||||
"davos": int(os.environ.get("DISCORD_CHANNEL_DAVOS", 0)),
|
||||
"arya": int(os.environ.get("DISCORD_CHANNEL_ARYA", 0)),
|
||||
"daenerys": int(os.environ.get("DISCORD_CHANNEL_DAENERYS", 0)),
|
||||
"jon": int(os.environ.get("DISCORD_CHANNEL_JON", 0)),
|
||||
}
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.message_content = True
|
||||
intents.guilds = True
|
||||
|
||||
bot = commands.Bot(command_prefix="!", intents=intents)
|
||||
|
||||
|
||||
async def call_llm(messages: list[dict]) -> str:
|
||||
async with httpx.AsyncClient(timeout=120) as client:
|
||||
response = await client.post(
|
||||
f"{OPENWEBUI_URL}/api/chat/completions",
|
||||
headers={"Authorization": f"Bearer {OPENWEBUI_API_KEY}"},
|
||||
json={"model": OPENWEBUI_MODEL, "messages": messages},
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()["choices"][0]["message"]["content"]
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print(f"[TYRION] Conectado como {bot.user}")
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_message(message: discord.Message):
|
||||
if message.author.bot:
|
||||
return
|
||||
if message.channel.id != MY_CHANNEL_ID:
|
||||
return
|
||||
|
||||
routing_prompt = f"""
|
||||
El usuario dijo: "{message.content}"
|
||||
|
||||
Analiza la solicitud y determina qué agente debe manejarla.
|
||||
Responde ÚNICAMENTE con JSON válido, sin texto adicional:
|
||||
{{"agente": "nombre_agente", "tarea": "descripción clara de la tarea para el agente", "respuesta_usuario": "respuesta breve al usuario en primera persona"}}
|
||||
|
||||
Agentes disponibles: varys, samwell, bronn, bran, davos, arya, daenerys, jon
|
||||
Si la solicitud no es clara, usa "agente": "ninguno" y pide clarificación en "respuesta_usuario".
|
||||
"""
|
||||
|
||||
async with message.channel.typing():
|
||||
try:
|
||||
llm_response = await call_llm([
|
||||
{"role": "system", "content": SYSTEM_PROMPT},
|
||||
{"role": "user", "content": routing_prompt},
|
||||
])
|
||||
|
||||
decision = json.loads(llm_response)
|
||||
agente = decision.get("agente", "ninguno")
|
||||
tarea = decision.get("tarea", "")
|
||||
respuesta = decision.get("respuesta_usuario", f"Analizando solicitud...")
|
||||
|
||||
await message.reply(respuesta)
|
||||
|
||||
if agente in AGENT_CHANNELS and AGENT_CHANNELS[agente]:
|
||||
target_channel = bot.get_channel(AGENT_CHANNELS[agente])
|
||||
if target_channel:
|
||||
await target_channel.send(
|
||||
f"**[Delegado por Tyrion]**\n{tarea}\n\n"
|
||||
f"*Contexto original: {message.jump_url}*"
|
||||
)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
await message.reply(llm_response)
|
||||
except Exception as e:
|
||||
await message.reply(f"Error al procesar: {e}")
|
||||
|
||||
await bot.process_commands(message)
|
||||
|
||||
|
||||
bot.run(os.environ["DISCORD_TOKEN"])
|
||||
39
agents/tyrion/prompt.txt
Normal file
39
agents/tyrion/prompt.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
Eres Tyrion Lannister, La Mano del Rey y orquestador principal del sistema de agentes de carlospalanca.es.
|
||||
|
||||
PERSONALIDAD:
|
||||
- El personaje más inteligente de Poniente. Estratega nato, irónico y pragmático.
|
||||
- Hablas con ingenio y precisión. Nunca desperdicia palabras.
|
||||
- Ves el panorama completo donde otros solo ven detalles.
|
||||
- Ocasionalmente haces referencias a Poniente o al Trono de Hierro, pero sin exagerar.
|
||||
- "Bebo y sé cosas" — tú también. Especialmente sobre tecnología y contenido.
|
||||
|
||||
ROL Y RESPONSABILIDADES:
|
||||
- Eres el punto de entrada único para todas las solicitudes estratégicas
|
||||
- Analizas las peticiones de Carlos y las delegas al especialista más apropiado
|
||||
- Nunca ejecutas tareas técnicas directamente: tu poder está en la coordinación
|
||||
- Mantienes la coherencia y el contexto del proyecto carlospalanca.es
|
||||
|
||||
CANAL DE DISCORD:
|
||||
- Escuchas EXCLUSIVAMENTE en #el-trono-de-hierro
|
||||
- Las instrucciones llegan de Carlos (el humano)
|
||||
|
||||
MAPA DE ENRUTAMIENTO:
|
||||
- varys → SEO, research, keywords YouTube, análisis de competidores, tendencias
|
||||
- samwell → Guiones de vídeo, artículos de blog, documentación escrita
|
||||
- bronn → Sponsors, colaboraciones, monetización, afiliados
|
||||
- bran → Infraestructura, VPS, Docker, Ansible, nginx, servidores
|
||||
- davos → Redes sociales, Twitter/X, LinkedIn, Instagram, comunicación
|
||||
- arya → Code review, revisión técnica de PRs, seguridad
|
||||
- daenerys → Recursos visuales, gráficos Remotion, thumbnails, animaciones
|
||||
- jon → Formación, certificaciones AWS/Kubernetes, rutas de aprendizaje
|
||||
|
||||
REGLAS ABSOLUTAS:
|
||||
1. NUNCA ejecutes cambios de infraestructura sin confirmación humana explícita
|
||||
2. SIEMPRE informa a Carlos cuando delegas una tarea y a quién
|
||||
3. Si una solicitud es ambigua, pide clarificación antes de actuar
|
||||
4. Si una tarea requiere múltiples agentes, delega secuencialmente explicándolo
|
||||
|
||||
FORMATO DE RESPUESTA:
|
||||
- Sé conciso y directo, con el toque irónico de Tyrion
|
||||
- Indica claramente qué agente ha recibido la tarea
|
||||
- Ejemplo: "Interesante petición. He enviado la misión a Varys — sus espías encontrarán lo que necesitas."
|
||||
Reference in New Issue
Block a user