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

106
docs/setup/01-github.md Normal file
View File

@@ -0,0 +1,106 @@
# Setup 01 — GitHub
## 1. Crear el repositorio
1. Ve a [github.com/new](https://github.com/new)
2. Nombre: `carlospalanca.es`
3. Visibilidad: **Public** (recomendado para web personal) o Private
4. **No** inicialices con README (ya tienes el código)
5. Clic en "Create repository"
## 2. Push inicial
Desde tu máquina local en la carpeta del proyecto:
```bash
git init
git add .
git commit -m "feat: initial project setup with agents"
git branch -M main
git remote add origin https://github.com/TU_USUARIO/carlospalanca.es.git
git push -u origin main
```
## 3. Crear el GitHub Fine-Grained PAT
Este token lo usarán los agentes para crear ramas y PRs.
1. Ve a [github.com/settings/tokens](https://github.com/settings/tokens)
2. Clic en **"Fine-grained tokens"** → **"Generate new token"**
3. Configura:
- **Token name:** `carlospalanca-agents`
- **Expiration:** 1 año (o "No expiration")
- **Resource owner:** Tu usuario
- **Repository access:** Only selected repositories → selecciona `carlospalanca.es`
4. En **Repository permissions**, activa:
- **Contents:** Read and write
- **Pull requests:** Read and write
5. Clic en **"Generate token"**
6. **Copia el token** (empieza por `github_pat_...`) — solo lo verás una vez
> Guarda este token como `GITHUB_TOKEN` en el `.env` de los agentes.
## 4. Añadir GitHub Secrets
Estos secretos los usa el workflow de deploy.
1. Ve a tu repo → **Settings****Secrets and variables****Actions**
2. Clic en **"New repository secret"** y añade:
| Nombre | Valor |
|--------|-------|
| `VPS_SSH_PRIVATE_KEY` | Contenido completo de la clave privada SSH del usuario `deploy` |
| `VPS_HOST` | IP pública del VPS (ej: `123.456.789.0`) |
| `VPS_USER` | `deploy` |
> Para obtener la clave privada: ejecuta `cat ~/.ssh/id_ed25519` en el VPS (o la ruta donde generaste la keypair del usuario deploy).
## 5. Crear Labels
1. Ve a tu repo → **Issues****Labels**
2. Crea estos labels (o ve a la URL: `https://github.com/TU_USUARIO/carlospalanca.es/labels`):
| Label | Color | Descripción |
|-------|-------|-------------|
| `agent-created` | `#0075ca` | Contenido creado por un agente de IA |
| `needs-review` | `#e4e669` | Requiere revisión humana antes de mergear |
| `approved` | `#0e8a16` | Aprobado por Carlos, listo para mergear |
## 6. Activar Branch Protection en `main`
1. Ve a **Settings****Branches****Add branch ruleset** (o "Add rule")
2. Branch name pattern: `main`
3. Activa:
-**Require a pull request before merging**
- ✅ Require approvals: 0 (eres tú solo, no necesitas aprobador extra)
-**Require status checks to pass before merging**
- Busca y añade: `Build & Validate` (el job del ci.yml)
-**Do not allow bypassing the above settings**
4. Clic en **"Create"**
> Esto garantiza que ni tú ni los agentes puedan hacer push directo a `main`.
## 7. Verificar que el CI funciona
1. Crea un branch de prueba:
```bash
git checkout -b test/ci-check
echo "test" >> README.md
git add README.md
git commit -m "test: verify CI pipeline"
git push origin test/ci-check
```
2. Abre un PR en GitHub
3. Verifica que el workflow `CI - Build Check` se ejecuta y pasa
4. Verifica que comenta "✅ Build exitoso" en el PR
5. Puedes cerrar el PR sin mergear
## Checklist
- [ ] Repositorio creado en GitHub
- [ ] Push inicial completado
- [ ] Fine-Grained PAT creado y copiado
- [ ] 3 GitHub Secrets añadidos (VPS_SSH_PRIVATE_KEY, VPS_HOST, VPS_USER)
- [ ] Labels creados (agent-created, needs-review, approved)
- [ ] Branch protection activada en `main`
- [ ] CI workflow verificado en PR de prueba

187
docs/setup/02-vps.md Normal file
View File

@@ -0,0 +1,187 @@
# Setup 02 — VPS
## 1. Contratar el VPS
**Recomendado:** Hetzner CX22 (~5€/mes)
- 2 vCPU, 4 GB RAM, 40 GB SSD
- Suficiente para: web estática + OpenWebUI + 9 bots Docker
**Alternativas:**
- DigitalOcean Droplet 2GB (~12$/mes)
- Linode Nanode 2GB (~12$/mes)
- Oracle Cloud Free Tier (gratuito, 4 vCPU ARM, 24 GB RAM)
**Durante la creación:**
- OS: Ubuntu 24.04 LTS
- Añade tu clave SSH pública para acceder sin contraseña
- Anota la IP pública del VPS
## 2. Acceder al VPS
```bash
ssh root@<IP_DEL_VPS>
```
## 3. Actualizar el sistema
```bash
apt-get update && apt-get upgrade -y
apt-get install -y curl wget git ufw fail2ban
```
## 4. Configurar el firewall (UFW)
```bash
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
```
## 5. Crear el usuario `deploy`
El usuario `deploy` es el que GitHub Actions usará para subir la web.
```bash
# Crear usuario
useradd -m -s /bin/bash deploy
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
# Generar keypair para GitHub Actions
ssh-keygen -t ed25519 -f /home/deploy/.ssh/github_actions -N "" -C "github-actions@carlospalanca.es"
# Autorizar la clave pública en el VPS
cat /home/deploy/.ssh/github_actions.pub >> /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
# Ver la clave privada (cópiala como GitHub Secret VPS_SSH_PRIVATE_KEY)
cat /home/deploy/.ssh/github_actions
```
> **IMPORTANTE:** Copia el output del último comando completo (incluyendo `-----BEGIN OPENSSH PRIVATE KEY-----` y `-----END OPENSSH PRIVATE KEY-----`) y pégalo como el secret `VPS_SSH_PRIVATE_KEY` en GitHub.
## 6. Crear directorio de la web
```bash
mkdir -p /var/www/carlospalanca.es
chown deploy:deploy /var/www/carlospalanca.es
```
## 7. Instalar nginx
```bash
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
```
## 8. Instalar Docker y Docker Compose
```bash
# Instalar Docker
curl -fsSL https://get.docker.com | sh
# Añadir el usuario root y deploy al grupo docker
usermod -aG docker root
usermod -aG docker deploy
# Verificar
docker --version
docker compose version
```
## 9. Instalar Certbot (SSL)
```bash
apt-get install -y certbot python3-certbot-nginx
```
## 10. Configurar DNS
En tu proveedor de DNS (Cloudflare, Namecheap, etc.), añade estos registros:
| Tipo | Nombre | Valor |
|------|--------|-------|
| A | `carlospalanca.es` | `<IP_DEL_VPS>` |
| A | `www.carlospalanca.es` | `<IP_DEL_VPS>` |
| A | `ai.carlospalanca.es` | `<IP_DEL_VPS>` |
> Los DNS pueden tardar hasta 24h en propagarse. Verifica con: `ping carlospalanca.es`
## 11. Configurar nginx
```bash
# Copiar la config (desde tu máquina local)
scp vps/nginx/carlospalanca.conf root@<IP_DEL_VPS>:/etc/nginx/sites-available/carlospalanca.es
# En el VPS:
ln -sf /etc/nginx/sites-available/carlospalanca.es /etc/nginx/sites-enabled/
# Eliminar la config por defecto
rm -f /etc/nginx/sites-enabled/default
# Verificar sintaxis
nginx -t
# Recargar
systemctl reload nginx
```
## 12. Obtener certificados SSL
> Espera a que el DNS haya propagado antes de este paso.
```bash
certbot --nginx \
-d carlospalanca.es \
-d www.carlospalanca.es \
-d ai.carlospalanca.es \
--agree-tos \
--email tu@email.com
```
Certbot actualizará automáticamente la config de nginx con los certificados y configurará la renovación automática.
## 13. Verificar
```bash
# Comprobar que nginx está activo
systemctl status nginx
# Comprobar que el SSL funciona
curl -I https://carlospalanca.es
# Ver los logs de nginx si hay error
tail -f /var/log/nginx/error.log
```
## 14. Permitir a `deploy` recargar nginx sin contraseña
El workflow de deploy ejecuta `sudo systemctl reload nginx`. Necesitas configurar sudoers para esto:
```bash
echo "deploy ALL=(ALL) NOPASSWD: /bin/systemctl reload nginx, /usr/sbin/nginx" \
>> /etc/sudoers.d/deploy-nginx
chmod 440 /etc/sudoers.d/deploy-nginx
```
## Checklist
- [ ] VPS contratado (Ubuntu 24.04)
- [ ] Acceso SSH verificado
- [ ] Sistema actualizado
- [ ] Firewall UFW configurado (80, 443, 22)
- [ ] Usuario `deploy` creado con SSH key
- [ ] Clave privada copiada como GitHub Secret
- [ ] Directorio `/var/www/carlospalanca.es/` creado
- [ ] nginx instalado
- [ ] Docker + Docker Compose instalados
- [ ] Certbot instalado
- [ ] DNS apuntando al VPS (carlospalanca.es, www, ai)
- [ ] nginx configurado y recargado
- [ ] Certificados SSL obtenidos
- [ ] `deploy` puede recargar nginx sin contraseña (sudoers)
- [ ] `https://carlospalanca.es` responde (aunque esté vacío)

132
docs/setup/03-openwebui.md Normal file
View File

@@ -0,0 +1,132 @@
# Setup 03 — OpenWebUI
OpenWebUI es el gateway de LLM. Los 9 agentes lo llaman vía API en lugar de llamar directamente a OpenAI/Anthropic. Esto centraliza el modelo y permite cambiarlo sin redeployar los bots.
## 1. Subir el archivo de configuración al VPS
Desde tu máquina local:
```bash
# Crear el directorio en el VPS
ssh root@<IP_VPS> "mkdir -p /opt/openwebui"
# Subir el docker-compose
scp vps/docker-compose.openwebui.yml root@<IP_VPS>:/opt/openwebui/docker-compose.yml
```
## 2. Crear el archivo `.env` en el VPS
```bash
ssh root@<IP_VPS>
cd /opt/openwebui
```
Crea el archivo `/opt/openwebui/.env`:
```bash
cat > .env << 'EOF'
# Genera con: openssl rand -hex 32
WEBUI_SECRET_KEY=CAMBIA_ESTO_POR_STRING_ALEATORIO
# Modelo por defecto que verán los usuarios en la UI
DEFAULT_MODEL=gpt-4o
# Pon SOLO la clave del proveedor que vayas a usar:
# OpenAI (GPT-4o, GPT-4, etc.)
OPENAI_API_KEY=sk-...
# Anthropic (Claude 3.5 Sonnet, Claude 3 Opus, etc.)
# ANTHROPIC_API_KEY=sk-ant-...
EOF
```
**Para generar un WEBUI_SECRET_KEY seguro:**
```bash
openssl rand -hex 32
```
## 3. Levantar OpenWebUI
```bash
cd /opt/openwebui
docker compose up -d
# Verificar que está corriendo
docker compose ps
docker compose logs -f openwebui
```
Espera a que aparezca `Application startup complete` en los logs.
## 4. Verificar acceso
Abre en el navegador: `https://ai.carlospalanca.es`
Deberías ver la pantalla de login de OpenWebUI.
1. Crea una cuenta de administrador (la primera cuenta es admin automáticamente)
2. Verifica que puedes chatear (Settings → Models → selecciona un modelo)
## 5. Crear el API Key para los agentes
Los agentes necesitan un API key para autenticarse contra OpenWebUI.
1. Inicia sesión en `https://ai.carlospalanca.es`
2. Ve a **Settings** (icono de usuario arriba a la derecha)
3. **Account****API Keys**
4. Clic en **"Create new secret key"**
5. Nombre: `agents-key`
6. **Copia el token** (empieza por `sk-...`) — solo lo verás una vez
> Guarda este token como `OPENWEBUI_API_KEY` en el `.env` de los agentes.
## 6. Verificar el API con curl
```bash
curl -X POST https://ai.carlospalanca.es/api/chat/completions \
-H "Authorization: Bearer <TU_OPENWEBUI_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Responde solo: OK"}]
}'
```
Debería responder algo como: `{"choices":[{"message":{"content":"OK",...`
## 7. (Opcional) Añadir modelos de Ollama para uso local
Si quieres usar modelos locales (Llama 3.1, Mistral, etc.) además de los de OpenAI:
En `docker-compose.yml`, descomenta la sección de `ollama` y la variable `OLLAMA_BASE_URL`.
Luego:
```bash
docker compose up -d
# Descargar un modelo (ej: llama3.1:8b)
docker exec -it ollama ollama pull llama3.1:8b
```
> Los modelos locales requieren más RAM. Para el CX22 de Hetzner (4 GB), usa solo modelos de 7-8B.
## Cambiar el modelo que usan los agentes
Para cambiar de GPT-4o a Claude (sin tocar código):
1. En `/opt/agents/.env`, cambia:
```bash
OPENWEBUI_MODEL=claude-3-5-sonnet-20241022
```
2. `docker compose restart` en la carpeta de agentes
## Checklist
- [ ] `/opt/openwebui/` creado en el VPS
- [ ] `docker-compose.yml` subido
- [ ] `.env` creado con las API keys
- [ ] `docker compose up -d` ejecutado
- [ ] `https://ai.carlospalanca.es` accesible
- [ ] Cuenta de admin creada
- [ ] API Key creada y copiada
- [ ] Verificado con curl que el API responde

125
docs/setup/04-discord.md Normal file
View File

@@ -0,0 +1,125 @@
# Setup 04 — Discord Apps y Bots
Necesitas crear **9 Discord Applications** separadas (una por agente). Cada una tiene su propio bot token.
## 1. Activar Developer Mode en Discord
1. Abre Discord → **Ajustes** (icono de rueda)
2. **Avanzado** → activa **"Modo desarrollador"**
Esto te permitirá hacer click derecho en canales y copiar sus IDs.
## 2. Crear los 9 canales en tu servidor Discord
Ve a tu servidor → crea estos canales de texto:
| Canal | Agente |
|-------|--------|
| `el-trono-de-hierro` | Tyrion (orquestador) |
| `el-pajarillo` | Varys |
| `la-ciudadela` | Samwell |
| `el-banco-de-hierro` | Bronn |
| `el-muro` | Bran |
| `desembarco-del-rey` | Davos |
| `cara-sin-nombre` | Arya |
| `poniente-en-llamas` | Daenerys |
| `la-guardia-de-la-noche` | Jon |
**Recomendado:** Crea una categoría llamada `LA MANO DEL REY` y agrupa todos los canales ahí.
## 3. Copiar los Channel IDs
Para cada canal:
1. Click derecho en el canal
2. Clic en **"Copiar ID del canal"**
3. Guárdalo — lo necesitarás para el `.env` de los agentes
## 4. Crear las 9 Discord Applications
Repite este proceso 9 veces (una por agente):
### Paso a paso para CADA bot:
1. Ve a [discord.com/developers/applications](https://discord.com/developers/applications)
2. Clic en **"New Application"**
3. Nombre según la tabla:
| Application Name | Variable del token |
|-----------------|-------------------|
| `Tyrion Lannister` | `DISCORD_TOKEN_TYRION` |
| `Varys` | `DISCORD_TOKEN_VARYS` |
| `Samwell Tarly` | `DISCORD_TOKEN_SAMWELL` |
| `Bronn` | `DISCORD_TOKEN_BRONN` |
| `Bran Stark` | `DISCORD_TOKEN_BRAN` |
| `Davos Seaworth` | `DISCORD_TOKEN_DAVOS` |
| `Arya Stark` | `DISCORD_TOKEN_ARYA` |
| `Daenerys Targaryen` | `DISCORD_TOKEN_DAENERYS` |
| `Jon Snow` | `DISCORD_TOKEN_JON` |
4. Acepta los Terms of Service → **"Create"**
5. Ve a la pestaña **"Bot"** (menú izquierdo)
6. Activa los **Privileged Gateway Intents**:
-**Message Content Intent** (OBLIGATORIO)
- Los otros son opcionales
7. Clic en **"Reset Token"** → **"Yes, do it"** → **Copia el token**
> Guárdalo inmediatamente, solo se muestra una vez.
8. Ve a **OAuth2****URL Generator**:
- Scopes: ✅ `bot`
- Bot permissions: ✅ `Read Messages/View Channels`, ✅ `Send Messages`, ✅ `Read Message History`
- Copia la URL generada
9. Abre la URL en el navegador → selecciona tu servidor → **"Autorizar"**
Repite los pasos 2-9 para los 9 agentes.
## 5. Verificar que los bots están en el servidor
En tu servidor Discord, en la lista de miembros o en los canales, deberías ver los 9 bots como miembros (aparecerán como offline hasta que los levantes).
## 6. (Opcional) Personalizar los bots
Para que cada bot tenga avatar y descripción:
1. En la app de Discord Developer → **General Information**
2. Sube una foto de perfil (imagen del personaje de GoT)
3. Añade descripción
## Checklist
- [ ] Developer Mode activado en Discord
- [ ] 9 canales creados en el servidor
- [ ] 9 Channel IDs copiados
- [ ] 9 Discord Applications creadas
- [ ] "Message Content Intent" activado en las 9
- [ ] 9 Bot Tokens copiados
- [ ] Los 9 bots invitados al servidor
## Resumen de variables `.env` de esta fase
```bash
# Tokens (de Discord Developer Portal)
DISCORD_TOKEN_TYRION=<token>
DISCORD_TOKEN_VARYS=<token>
DISCORD_TOKEN_SAMWELL=<token>
DISCORD_TOKEN_BRONN=<token>
DISCORD_TOKEN_BRAN=<token>
DISCORD_TOKEN_DAVOS=<token>
DISCORD_TOKEN_ARYA=<token>
DISCORD_TOKEN_DAENERYS=<token>
DISCORD_TOKEN_JON=<token>
# Channel IDs (click derecho en canal > Copiar ID)
DISCORD_CHANNEL_TRONO=<id> # #el-trono-de-hierro
DISCORD_CHANNEL_VARYS=<id> # #el-pajarillo
DISCORD_CHANNEL_SAMWELL=<id> # #la-ciudadela
DISCORD_CHANNEL_BRONN=<id> # #el-banco-de-hierro
DISCORD_CHANNEL_BRAN=<id> # #el-muro
DISCORD_CHANNEL_DAVOS=<id> # #desembarco-del-rey
DISCORD_CHANNEL_ARYA=<id> # #cara-sin-nombre
DISCORD_CHANNEL_DAENERYS=<id> # #poniente-en-llamas
DISCORD_CHANNEL_JON=<id> # #la-guardia-de-la-noche
```

210
docs/setup/05-agents.md Normal file
View File

@@ -0,0 +1,210 @@
# Setup 05 — Despliegue de Agentes
Este es el último paso. Asegúrate de haber completado los setups 01-04 antes de continuar.
## Prerequisitos
Antes de empezar, debes tener:
- ✅ GitHub PAT creado (`GITHUB_TOKEN` y `GITHUB_REPO`)
- ✅ OpenWebUI funcionando (`OPENWEBUI_URL` y `OPENWEBUI_API_KEY`)
- ✅ 9 Discord bot tokens (`DISCORD_TOKEN_*`)
- ✅ 9 Discord channel IDs (`DISCORD_CHANNEL_*`)
## 1. Subir el código de agentes al VPS
Desde tu máquina local:
```bash
# Opción A: rsync (recomendado)
rsync -avz --exclude '.env' agents/ root@<IP_VPS>:/opt/agents/
# Opción B: desde el VPS, clonar el repo directamente
ssh root@<IP_VPS>
git clone https://github.com/TU_USUARIO/carlospalanca.es.git /tmp/repo
cp -r /tmp/repo/agents /opt/agents
```
## 2. Crear el archivo `.env` en el VPS
```bash
ssh root@<IP_VPS>
cp /opt/agents/.env.example /opt/agents/.env
nano /opt/agents/.env
```
Rellena TODOS los valores. El archivo debe quedar así:
```bash
# OpenWebUI
OPENWEBUI_URL=https://ai.carlospalanca.es
OPENWEBUI_API_KEY=sk-... # Del paso 03
OPENWEBUI_MODEL=gpt-4o # O: claude-3-5-sonnet-20241022
# GitHub
GITHUB_TOKEN=github_pat_... # Del paso 01
GITHUB_REPO=TU_USUARIO/carlospalanca.es
# Discord bot tokens (del paso 04)
DISCORD_TOKEN_TYRION=...
DISCORD_TOKEN_VARYS=...
DISCORD_TOKEN_SAMWELL=...
DISCORD_TOKEN_BRONN=...
DISCORD_TOKEN_BRAN=...
DISCORD_TOKEN_DAVOS=...
DISCORD_TOKEN_ARYA=...
DISCORD_TOKEN_DAENERYS=...
DISCORD_TOKEN_JON=...
# Discord channel IDs (del paso 04)
DISCORD_CHANNEL_TRONO=...
DISCORD_CHANNEL_VARYS=...
DISCORD_CHANNEL_SAMWELL=...
DISCORD_CHANNEL_BRONN=...
DISCORD_CHANNEL_BRAN=...
DISCORD_CHANNEL_DAVOS=...
DISCORD_CHANNEL_ARYA=...
DISCORD_CHANNEL_DAENERYS=...
DISCORD_CHANNEL_JON=...
```
## 3. Construir y levantar los agentes
```bash
cd /opt/agents
# Construir las imágenes (la primera vez tarda 2-5 minutos)
docker compose build
# Levantar todos los bots
docker compose up -d
# Verificar que todos están corriendo
docker compose ps
```
Deberías ver los 9 servicios con estado `running`.
## 4. Verificar logs de cada agente
```bash
# Ver logs de Tyrion
docker compose logs -f tyrion
# Ver logs de todos
docker compose logs -f
```
Deberías ver para cada bot:
```
[TYRION] Conectado como Tyrion Lannister#1234
[SAMWELL] Conectado como Samwell Tarly#5678
...
```
## 5. Verificar en Discord
En tu servidor Discord, los 9 bots deberían aparecer ahora como **online** (punto verde).
## 6. Test del flujo completo
### Test 1: Tyrion responde
Escribe en `#el-trono-de-hierro`:
```
Tyrion, ¿estás ahí?
```
Tyrion debería responder en el canal.
### Test 2: Enrutamiento a Varys
Escribe en `#el-trono-de-hierro`:
```
Tyrion, necesito los mejores títulos para un vídeo sobre Docker Compose
```
Deberías ver:
1. Tyrion responde en `#el-trono-de-hierro` confirmando que delega a Varys
2. En `#el-pajarillo` aparece el mensaje con la tarea
3. Varys procesa y responde con los títulos
### Test 3: Samwell crea un PR
Escribe en `#el-trono-de-hierro`:
```
Tyrion, pide a Samwell que cree un artículo de blog corto sobre qué es un contenedor Docker
```
Deberías ver:
1. Tyrion delega a Samwell
2. Samwell genera el artículo y crea un PR en GitHub
3. Samwell responde en `#la-ciudadela` con la URL del PR
4. En GitHub aparece el PR con etiquetas `agent-created` y `needs-review`
5. El workflow CI comenta en el PR si el build pasa
## 7. Comandos útiles de mantenimiento
```bash
cd /opt/agents
# Reiniciar un agente específico
docker compose restart tyrion
# Reiniciar todos
docker compose restart
# Ver logs en tiempo real de un agente
docker compose logs -f samwell
# Parar todos los agentes
docker compose stop
# Actualizar después de cambios en el código
git pull # En el repo clonado, o rsync de nuevo
docker compose build
docker compose up -d
# Ver uso de recursos
docker stats
```
## 8. Actualizar los system prompts sin rebuilder
Los `prompt.txt` se leen en el arranque del bot. Para actualizar un prompt:
```bash
# Editar el prompt
nano /opt/agents/samwell/prompt.txt
# Reiniciar solo ese bot (sin rebuild)
docker compose restart samwell
```
## Troubleshooting
**El bot no responde en Discord:**
```bash
docker compose logs tyrion
# Busca errores de autenticación Discord (token incorrecto)
# o errores de conexión a OpenWebUI
```
**Error "Message Content Intent not enabled":**
→ Ve a Discord Developer Portal → la app del bot → Bot → activa "Message Content Intent"
→ Reinicia: `docker compose restart <agente>`
**Error "401 Unauthorized" al llamar OpenWebUI:**
→ Verifica que `OPENWEBUI_API_KEY` en `.env` es correcto
→ Verifica que `OPENWEBUI_URL` no tiene barra al final
**Samwell no crea el PR:**
→ Verifica `GITHUB_TOKEN` y `GITHUB_REPO` en `.env`
→ Verifica que el token tiene permisos `Contents: Write` y `Pull requests: Write`
→ Verifica que el repositorio existe y tiene rama `main`
## Checklist
- [ ] Código de agentes subido a `/opt/agents/` en el VPS
- [ ] `.env` creado con todos los tokens y channel IDs
- [ ] `docker compose build` completado sin errores
- [ ] `docker compose up -d` ejecutado
- [ ] Los 9 servicios aparecen como `running` en `docker compose ps`
- [ ] Los 9 bots aparecen online en Discord
- [ ] Test 1: Tyrion responde en `#el-trono-de-hierro`
- [ ] Test 2: Tyrion enruta a Varys
- [ ] Test 3: Samwell crea un PR en GitHub
- [ ] CI workflow comenta en el PR