feat: initial project setup

This commit is contained in:
2026-03-21 08:49:51 +01:00
commit 0ae87f16c7
88 changed files with 10755 additions and 0 deletions

15
agents/tyrion/Dockerfile Normal file
View 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
View 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
View 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."