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

320
docs/agents-reference.md Normal file
View File

@@ -0,0 +1,320 @@
# Referencia de Agentes
Todos los agentes comparten la misma arquitectura base pero tienen roles, canales y system prompts distintos.
---
## Arquitectura Común
**Tecnología:** Python 3.12, discord.py 2.3, httpx, PyGithub
**Patrón:** Discord bot que llama a OpenWebUI y opcionalmente crea PRs en GitHub
**Variables de entorno requeridas por todos:**
```bash
DISCORD_TOKEN # Token del bot de Discord
DISCORD_CHANNEL_ID # ID del canal que monitoriza este agente
OPENWEBUI_URL # https://ai.carlospalanca.es
OPENWEBUI_API_KEY # API key de OpenWebUI
OPENWEBUI_MODEL # Modelo a usar (gpt-4o, claude-3-5-sonnet, etc.)
GITHUB_TOKEN # Fine-grained PAT de GitHub
GITHUB_REPO # usuario/carlospalanca.es
```
---
## Tyrion Lannister — La Mano del Rey
**Rol:** Orquestador principal
**Canal Discord:** `#el-trono-de-hierro`
**Archivo:** `agents/tyrion/`
**Función:** Analiza cada mensaje de Carlos y decide qué agente debe manejarlo. Publica la tarea delegada en el canal del agente destino.
**Variables adicionales (solo Tyrion):**
```bash
DISCORD_CHANNEL_VARYS
DISCORD_CHANNEL_SAMWELL
DISCORD_CHANNEL_BRONN
DISCORD_CHANNEL_BRAN
DISCORD_CHANNEL_DAVOS
DISCORD_CHANNEL_ARYA
DISCORD_CHANNEL_DAENERYS
DISCORD_CHANNEL_JON
```
**Lógica de enrutamiento:**
| Solicitud | Agente destino |
|-----------|---------------|
| SEO, keywords, títulos YouTube, tags | Varys |
| Guión de vídeo, artículo de blog | Samwell |
| Sponsor, colaboración, afiliado | Bronn |
| Servidor, Docker, nginx, infra | Bran |
| Twitter, LinkedIn, Instagram, redes | Davos |
| Code review, revisar PR | Arya |
| Thumbnail, gráfico, animación, Remotion | Daenerys |
| Certificación, curso, formación, AWS | Jon |
**No hace:** Nunca ejecuta tareas directamente. Solo coordina.
---
## Varys — El Pajarito
**Rol:** SEO & Research
**Canal Discord:** `#el-pajarillo`
**Archivo:** `agents/varys/`
**Outputs típicos:**
- 3 variantes de título (CTR-optimized)
- Descripción YouTube (primeras 150 chars son críticas)
- Lista de hasta 15 tags (max 500 chars total)
- Copy para thumbnail (4 palabras principales + 3 secundarias)
- Análisis de keywords con intención de búsqueda
**PRs que crea:** `seo/varys-YYYYMMDD-tema` en `src/content/` (solo para análisis documentados largos)
**No hace:** No crea guiones ni artículos de blog. Solo SEO y research.
---
## Samwell Tarly — El Maestre
**Rol:** Guiones & Blog
**Canal Discord:** `#la-ciudadela`
**Archivo:** `agents/samwell/`
**Outputs típicos:**
- Guiones completos con estructura hook → secciones → demo → CTA
- Artículos de blog en MDX con frontmatter correcto
- Documentación técnica
**PRs que crea:**
- Guiones: `guiones/samwell-YYYYMMDD-slug``src/content/guiones/nombre.md`
- Blog: `blog/samwell-YYYYMMDD-slug``src/content/blog/YYYY-MM-DD-nombre.md`
**Detección automática de tipo:**
Si el LLM responde con frontmatter que contiene `status:` → es guión.
Si contiene `pubDate:` → es blog post.
**Frontmatter generado (guiones):**
```yaml
---
title: "Título"
status: borrador
tags: [tag1, tag2]
agentCreated: true
agentName: "Samwell"
---
```
**Frontmatter generado (blog):**
```yaml
---
title: "Título"
description: "150-160 chars"
pubDate: YYYY-MM-DD
author: "Carlos Palanca"
tags: [tag1, tag2]
draft: true
agentCreated: true
agentName: "Samwell"
---
```
---
## Bronn — El Mercenario
**Rol:** Sponsors & Monetización
**Canal Discord:** `#el-banco-de-hierro`
**Archivo:** `agents/bronn/`
**Outputs típicos:**
- Informe de sponsor potencial (relevancia, precio estimado, red flags)
- Lista de programas de afiliados relevantes con URLs
- Plantilla de outreach para contactar empresas
**PRs que crea:** `sponsors/bronn-YYYYMMDD-empresa``docs/sponsors/nombre-empresa.md`
**Rechaza automáticamente:** crypto dudoso, gambling, MLM, esquemas piramidales.
**Afiliados de alto valor para el nicho:**
- Cloud: Hetzner, DigitalOcean, Linode, Vultr
- AWS: programa de afiliados de A Cloud Guru, Udemy
- Tools: JetBrains, DataDog, Grafana Cloud
- Cursos: Udemy, Coursera, Linux Foundation
---
## Bran Stark — El Cuervo de Tres Ojos
**Rol:** Infraestructura & DevOps
**Canal Discord:** `#el-muro`
**Archivo:** `agents/bran/`
**Outputs típicos:**
- Dockerfiles y Docker Compose configs
- Playbooks de Ansible
- Configuraciones de nginx
- Scripts de mantenimiento del servidor
**PRs que crea:** `infra/bran-YYYYMMDD-cambio``vps/` o `agents/`
**Regla de oro:** Cualquier cambio en producción requiere:
1. PR con descripción de impacto
2. Plan de rollback explícito
3. Comandos de verificación post-deploy
4. Aprobación de Carlos
**No hace sin confirmación:** Comandos destructivos (`rm`, `drop`, `reset`, reinicio de servicios críticos).
---
## Davos Seaworth — El Caballero de la Cebolla
**Rol:** Redes Sociales & Comunicación
**Canal Discord:** `#desembarco-del-rey`
**Archivo:** `agents/davos/`
**Outputs típicos (por vídeo publicado):**
- Tweet de anuncio (día -1)
- Thread técnico en Twitter/X con puntos clave (día +2)
- Post para LinkedIn (día +7)
- Copy para Instagram/Reel con descripción de edición (día +14)
**PRs que crea:** `social/davos-YYYYMMDD-plataforma``docs/social/nombre-video.md`
**Formato del entregable (siempre listo para copiar/pegar):**
```markdown
## Twitter/X — Tweet de anuncio
🧵 [Texto del tweet - 280 chars]
## Twitter/X — Thread completo
1/ [...]
2/ [...]
## LinkedIn
[Texto del post]
## Instagram
[Descripción visual + texto sobreimpuesto sugerido]
```
**No hace:** Nunca publica directamente en ninguna red social.
---
## Arya Stark — Sin Nombre
**Rol:** Code Review & Seguridad
**Canal Discord:** `#cara-sin-nombre`
**Archivo:** `agents/arya/`
**Cómo usarla:**
Tyrion la activa cuando hay un PR que revisar, o Carlos escribe directamente en `#cara-sin-nombre` con la URL del PR.
**Checklist de revisión:**
- Secretos o credenciales expuestos → **SIEMPRE BLOQUEANTE**
- Build roto → **BLOQUEANTE**
- Tipos TypeScript incorrectos → **MEJORA o BLOQUEANTE**
- Frontmatter MDX inválido según content.config.ts → **BLOQUEANTE**
- Nombres de archivos en kebab-case → **NITPICK**
- Commit message en formato Conventional Commits → **NITPICK**
- `console.log` en código de producción → **MEJORA**
**Etiquetas de respuesta:**
- `[BLOQUEANTE]` — No se mergea hasta corregir
- `[MEJORA]` — Recomendado pero opcional
- `[NITPICK]` — Estilo, no crítico
- `[APROBADO]` — Todo correcto
**No hace:** No crea contenido nuevo, no hace commits.
---
## Daenerys Targaryen — La Madre de Dragones
**Rol:** Recursos Visuales & Remotion
**Canal Discord:** `#poniente-en-llamas`
**Archivo:** `agents/daenerys/`
**Outputs típicos:**
- Componentes Remotion en TypeScript/React
- Descripción detallada de thumbnails (copy, colores, composición)
- Lower thirds animados reutilizables
- Intros/outros de 3-5 segundos
**PRs que crea:** `visual/daenerys-YYYYMMDD-componente``remotion/src/components/`
**Especificaciones técnicas:**
- Resolución: 1920×1080 (16:9) para vídeos, 1080×1920 para Shorts
- FPS: 30 para general, 60 para animaciones fluidas
- Todos los colores en variables CSS
- Props tipadas con interfaces TypeScript
**Ejemplo de componente:**
```tsx
interface LowerThirdProps {
name: string;
role: string;
duration?: number; // frames
}
export const LowerThird: React.FC<LowerThirdProps> = ({ name, role, duration = 90 }) => {
// ...
};
```
---
## Jon Snow — El Guardián
**Rol:** Formación & Certificaciones
**Canal Discord:** `#la-guardia-de-la-noche`
**Archivo:** `agents/jon/`
**Outputs típicos:**
- Roadmaps de certificación por semanas
- Comparativa de recursos (gratuitos vs pago)
- Plan de estudio personalizado
- Sugerencias de series de vídeos educativos para el canal
**PRs que crea:** `edu/jon-YYYYMMDD-certificacion``docs/certifications/`
**Certificaciones cubiertas:**
| Cert | Código | Dificultad | Horas estudio |
|------|--------|-----------|--------------|
| AWS Solutions Architect Associate | SAA-C03 | Media | 80-120h |
| AWS DevOps Engineer Professional | DOP-C02 | Alta | 100-150h |
| Certified Kubernetes Administrator | CKA | Alta | 80-120h |
| HashiCorp Terraform Associate | 003 | Media | 40-60h |
| AWS Security Specialty | SCS-C02 | Alta | 100-150h |
**Regla crítica:** NUNCA reproduce preguntas reales de exámenes (copyright). Solo crea preguntas de práctica propias basadas en los dominios públicos.
---
## `agents/shared/github_client.py`
Módulo compartido por todos los agentes que crean contenido.
**Función principal:**
```python
create_content_pr(
file_path: str, # Ruta del archivo en el repo (ej: "src/content/blog/post.md")
content: str, # Contenido del archivo
title: str, # Título del PR
description: str, # Cuerpo del PR
agent_name: str, # Nombre del agente (ej: "Samwell")
branch_prefix: str, # Prefijo de rama (ej: "blog", "guiones", "seo")
) -> str # Devuelve la URL del PR
```
**Lo que hace internamente:**
1. Obtiene el SHA del último commit de `main`
2. Crea una rama: `{branch_prefix}/{agent_name}-{timestamp}-{slug}`
3. Hace commit del archivo en esa rama
4. Abre un PR con etiquetas `agent-created` y `needs-review`
5. Devuelve la URL del PR
**Nunca hace commit a `main`.**

235
docs/architecture.md Normal file
View File

@@ -0,0 +1,235 @@
# Arquitectura del Sistema
## Visión General
`carlospalanca.es` es un sistema en tres capas que conecta un canal de YouTube tech con una web personal, un sistema de agentes de IA y un pipeline de CI/CD automatizado.
```
┌─────────────────────────────────────────────────────────────┐
│ Carlos (humano) │
│ │
│ Escribe en Discord ──► Revisa PRs en GitHub │
└────────────┬────────────────────────┬───────────────────────┘
│ │
▼ ▼
┌────────────────────┐ ┌───────────────────────┐
│ Discord Server │ │ GitHub Repository │
│ │ │ carlospalanca.es │
│ #el-trono-de- │ │ │
│ hierro (Tyrion) │ │ main ──► CI/CD ──► │
│ #la-ciudadela │ │ PRs de agentes │
│ #el-pajarillo │ │ Branch protection │
│ #el-muro │ └──────────┬────────────┘
│ ... (9 canales) │ │
└────────┬───────────┘ │ merge
│ ▼
│ bot events ┌──────────────────────┐
▼ │ GitHub Actions │
┌────────────────────┐ │ │
│ VPS (Hetzner) │ │ ci.yml: build check │
│ │ │ deploy.yml: rsync │
│ ┌──────────────┐ │ └──────────┬───────────┘
│ │ 9 Discord │ │ │
│ │ Bot Agents │◄─┤ ◄────────────┘
│ │ (Docker) │ │ │ rsync dist/
│ └──────┬───────┘ │ ▼
│ │ │ ┌──────────────────────┐
│ │ HTTP API │ │ /var/www/ │
│ ▼ │ │ carlospalanca.es/ │
│ ┌──────────────┐ │ │ (archivos estáticos) │
│ │ OpenWebUI │ │ └──────────┬────────────┘
│ │ :3000 │ │ │
│ └──────────────┘ │ │ nginx sirve
│ │ │ ▼
│ ▼ │ ┌──────────────────────┐
│ ┌──────────────┐ │ │ https:// │
│ │ Nginx │ │ │ carlospalanca.es │
│ │ (proxy/TLS) │ │ │ (web pública) │
│ └──────────────┘ │ └──────────────────────┘
└────────────────────┘
```
---
## Componentes
### 1. Web (Astro)
**Tecnología:** Astro 5, MDX, sitemap, RSS
**Tipo:** Sitio estático generado (SSG)
**Contenido gestionado por:**
- Carlos directamente
- Agentes vía Pull Requests (nunca commits directos)
**Colecciones de contenido:**
| Colección | Ruta | Creador |
|-----------|------|---------|
| `blog` | `src/content/blog/` | Samwell / Carlos |
| `guiones` | `src/content/guiones/` | Samwell |
---
### 2. CI/CD (GitHub Actions)
**`ci.yml`** — Se ejecuta en cada Pull Request:
1. Checkout del código
2. `npm ci` — instala dependencias
3. `astro check` — type checking
4. `npm run build` — construye el sitio
5. Comenta en el PR si pasa o falla
**`deploy.yml`** — Se ejecuta al mergear a `main`:
1. Build del sitio (`dist/`)
2. rsync de `dist/` al VPS vía SSH
3. `nginx -t && systemctl reload nginx`
**GitHub Secrets necesarios:**
| Secret | Valor |
|--------|-------|
| `VPS_SSH_PRIVATE_KEY` | Clave privada SSH del usuario `deploy` |
| `VPS_HOST` | IP o hostname del VPS |
| `VPS_USER` | `deploy` |
---
### 3. VPS
**OS:** Ubuntu 24.04 LTS
**Proveedor recomendado:** Hetzner CX22 (~5€/mes, 2 vCPU, 4 GB RAM)
**Servicios corriendo:**
| Servicio | Puerto | Acceso |
|----------|--------|--------|
| nginx | 80, 443 | Público |
| OpenWebUI | 3000 | Solo localhost (proxificado por nginx) |
| 9 Discord bots | — | Outbound only |
**Estructura de directorios en el VPS:**
```
/var/www/carlospalanca.es/ ← Web estática (desplegada por GitHub Actions)
/opt/openwebui/ ← Docker Compose de OpenWebUI
/opt/agents/ ← Docker Compose de los 9 agentes
/etc/nginx/sites-available/ ← Config de nginx
/etc/letsencrypt/ ← Certificados SSL (Let's Encrypt)
```
---
### 4. OpenWebUI
**Imagen:** `ghcr.io/open-webui/open-webui:main`
**Función:** Gateway de LLM. Los agentes hacen llamadas HTTP a su API compatible con OpenAI.
**Endpoint que usan los agentes:**
```
POST https://ai.carlospalanca.es/api/chat/completions
Authorization: Bearer <OPENWEBUI_API_KEY>
```
**Ventaja del gateway centralizado:**
- Cambiar de GPT-4o a Claude o Llama solo requiere cambiar `OPENWEBUI_MODEL` en `.env`
- Dashboard web para monitorizar el uso de tokens
- No hay que redeployar los agentes para cambiar de modelo
---
### 5. Sistema de Agentes
**Patrón:** Cada agente es un Discord bot en Python (discord.py) que:
1. Escucha mensajes en su canal de Discord asignado
2. Llama a OpenWebUI con su system prompt + el mensaje del usuario
3. Ejecuta la acción correspondiente (crear PR, responder texto, etc.)
**Comunicación entre agentes:**
```
Carlos ──► #el-trono-de-hierro
Tyrion analiza y enruta
├──► #la-ciudadela (Samwell)
├──► #el-pajarillo (Varys)
├──► #el-muro (Bran)
└──► ... (resto de agentes)
```
Los agentes NO se llaman entre sí por API. Toda la comunicación es vía Discord.
---
### 6. GitHub Integration
**Token:** Fine-Grained PAT con scope mínimo:
- `Contents: Write` — crear ramas y archivos
- `Pull requests: Write` — abrir y comentar PRs
- Scoped **solo** al repo `carlospalanca.es`
**Flujo de creación de contenido:**
```
Agente genera contenido
agents/shared/github_client.py
1. Crea rama: tipo/agente-YYYYMMDD-slug
2. Commit del archivo en esa rama
3. Abre PR con etiquetas agent-created, needs-review
GitHub Actions ejecuta ci.yml
→ Build check
→ Comenta en el PR
Carlos revisa y mergea
GitHub Actions ejecuta deploy.yml
→ rsync a VPS
→ Web actualizada
```
---
## Flujo Completo de Ejemplo
**Solicitud:** "Tyrion, necesito un guión para un vídeo sobre Kubernetes Ingress"
```
1. Carlos escribe en #el-trono-de-hierro
2. Tyrion (bot) recibe el mensaje
→ Llama a OpenWebUI con su prompt de orquestador
→ Responde: "Entendido. He enviado la misión a Samwell en la Ciudadela."
→ Publica en #la-ciudadela: "[Delegado por Tyrion] Crear guión sobre Kubernetes Ingress..."
3. Samwell (bot) recibe la tarea en #la-ciudadela
→ Llama a OpenWebUI con su prompt de escritor + la tarea
→ Genera el guión completo en formato MDX con frontmatter
→ Llama a github_client.create_content_pr()
- Crea rama: guiones/samwell-20240321-kubernetes-ingress
- Hace commit de src/content/guiones/kubernetes-ingress.md
- Abre PR: "[Samwell] Guión: Kubernetes Ingress"
→ Responde en #la-ciudadela: "PR creado: https://github.com/..."
4. GitHub Actions (ci.yml) se activa
→ npm run build → OK
→ Comenta en el PR: "✅ Build exitoso"
5. Carlos revisa el PR, edita si necesita, hace merge
6. GitHub Actions (deploy.yml) se activa
→ Build → rsync → nginx reload
→ Web actualizada con el nuevo guión
```
---
## Seguridad
| Medida | Implementación |
|--------|---------------|
| GitHub token scope mínimo | Fine-grained PAT, solo este repo, solo contents+PRs |
| OpenWebUI solo en localhost | Bind a `127.0.0.1:3000`, no expuesto directamente |
| Nginx como única entrada | TLS termination en nginx, HTTPS forzado |
| Branch protection en main | Requiere PR + CI verde antes de mergear |
| Agentes sin acceso a main | Solo pueden crear ramas y abrir PRs |
| Secretos en `.env` | `.env` en `.gitignore`, nunca en el repo |

View File

199
docs/cost-roi.md Normal file
View File

@@ -0,0 +1,199 @@
# Coste, Rendimiento y Ganancia del Sistema
Análisis honesto de lo que cuesta cada pieza, cuánto rinde y qué ganas (o pierdes) con ella.
> **Contexto:** Tienes suscripción Claude Pro activa ($20/mes). Esto cambia significativamente el análisis.
---
## Lo primero: Claude Pro vs. Anthropic API — no son lo mismo
Este es el punto más importante antes de calcular cualquier coste.
| | Claude Pro ($20/mes) | Anthropic API (por tokens) |
|--|---------------------|---------------------------|
| **Acceso** | claude.ai (web/apps) | Llamadas HTTP programáticas |
| **Quién lo usa** | Tú, manualmente | Los agentes automáticamente |
| **Facturación** | Cuota fija mensual | Por tokens consumidos |
| **¿Sirve para los bots?** | ❌ No directamente | ✅ Sí |
**El resumen:** Claude Pro es para tu uso personal en claude.ai. Los agentes necesitan la Anthropic API, que se factura aparte por tokens.
**La buena noticia:** Ya conoces la calidad de Claude. Los agentes usarán exactamente el mismo Claude Sonnet que usas tú, solo que vía API. La experiencia de output será la misma que ya te ha convencido.
---
## Resumen de costes mensuales (con tu situación real)
| Componente | Coste/mes | Tipo | Notas |
|------------|-----------|------|-------|
| Claude Pro | $20 (~18 €) | Fijo | Ya lo pagas. Sunk cost. |
| VPS Hetzner CX22 | ~5 € | Fijo | Nuevo coste |
| Dominio carlospalanca.es | ~1 € | Fijo (amortizado) | Si no lo tienes ya |
| Anthropic API | 1-8 € | Variable | Ver tabla de consumo abajo |
| Discord | 0 € | Gratuito | |
| GitHub | 0 € | Gratuito | |
| Let's Encrypt | 0 € | Gratuito | |
| **Coste nuevo total** | **~6-14 €/mes** | | Sobre lo que ya pagas |
| **Coste total real** | **~24-32 €/mes** | | Incluyendo Claude Pro |
El sistema completo te cuesta **~6-14 € más al mes** sobre lo que ya gastas en Claude Pro.
---
## Claude Pro: ¿qué ya tienes cubierto?
Con tu suscripción Pro ya puedes hacer manualmente todo lo que harán los agentes. El sistema lo que hace es **automatizar y quitar fricción** a ese trabajo, no añadir una capacidad que no tenías.
**Lo que cubres con Pro (uso manual):**
- Pedir guiones, artículos, SEO a Claude en claude.ai
- Subir archivos, analizar documentos, proyectos prolongados
- Acceso a Claude Opus, Sonnet y Haiku
**Lo que añade el sistema de agentes:**
- Los agentes hacen esas mismas peticiones automáticamente cuando escribes en Discord
- El resultado llega directamente como PR en GitHub, listo para revisar
- No tienes que abrir el navegador, copiar/pegar, crear el archivo, commitear...
---
## Coste de la Anthropic API para los agentes
**Precios de referencia (marzo 2025):**
| Modelo | Input | Output | Mejor para |
|--------|-------|--------|-----------|
| Claude 3.5 Sonnet | $3.00 / 1M tokens | $15.00 / 1M tokens | Guiones, artículos largos |
| Claude 3.5 Haiku | $0.80 / 1M tokens | $4.00 / 1M tokens | SEO, redes sociales, tareas cortas |
| Claude 3 Opus | $15.00 / 1M tokens | $75.00 / 1M tokens | No necesario para este uso |
**¿Cuánto gasta cada agente por tarea?**
| Agente | Tarea típica | Tokens estimados | Coste Sonnet | Coste Haiku |
|--------|-------------|-----------------|--------------|-------------|
| Samwell | Guión completo 15 min | ~5.000 output | ~0.075 € | ~0.020 € |
| Samwell | Artículo de blog | ~3.000 output | ~0.045 € | ~0.012 € |
| Varys | SEO completo (títulos + tags + descripción) | ~800 output | ~0.012 € | ~0.003 € |
| Davos | Posts para 3 plataformas | ~1.000 output | ~0.015 € | ~0.004 € |
| Bronn | Informe de sponsor | ~1.200 output | ~0.018 € | ~0.005 € |
| Jon | Guía de certificación | ~4.000 output | ~0.060 € | ~0.016 € |
| Tyrion | Enrutamiento (respuesta corta) | ~200 output | ~0.003 € | ~0.001 € |
**Estimación mensual por escenario (2 vídeos/semana = 8 al mes):**
| Escenario | Agentes usados | Tokens/mes | Coste Sonnet | Coste Haiku |
|-----------|---------------|------------|--------------|-------------|
| Mínimo (solo Samwell + Varys) | 2 | ~500K | ~3 € | ~0.8 € |
| Moderado (todos los agentes) | 9 | ~2M | ~8 € | ~2 € |
| Intensivo (mucha experimentación) | 9 | ~5M | ~20 € | ~5 € |
**Recomendación con tu setup:**
- **Samwell, Jon, Daenerys** (tareas largas y creativas): usa **Claude 3.5 Sonnet** — misma calidad que tienes en Pro
- **Varys, Davos, Bronn, Arya, Bran** (tareas cortas y estructuradas): usa **Claude 3.5 Haiku** — más de 3× más barato, calidad suficiente para SEO y social
Esto se configura por agente en `agents/docker-compose.yml` cambiando `OPENWEBUI_MODEL` en cada servicio.
---
## La estrategia óptima: mezcla Pro + API
Dado que ya tienes Pro, el flujo más rentable es:
**Para trabajo creativo propio:** usa claude.ai (cubierto por tu Pro)
- Brainstorming de ideas de vídeo
- Revisión y ajuste de los guiones que genera Samwell
- Conversaciones largas de estrategia
**Para automatización repetitiva:** usa la API vía agentes (coste variable bajo)
- SEO de cada vídeo → Varys (Haiku, ~0.003 € por vídeo)
- Guión base → Samwell (Sonnet, ~0.075 € por vídeo)
- Posts de redes → Davos (Haiku, ~0.004 € por vídeo)
- Búsqueda de sponsors → Bronn (Haiku, puntual)
**Coste real por vídeo publicado con esta estrategia: ~0.10-0.15 €**
---
## VPS — 5 €/mes (Hetzner CX22)
**Coste:** ~5 €/mes (2 vCPU, 4 GB RAM, 40 GB SSD)
**Qué corre dentro:**
- nginx (web estática, ~0 recursos)
- OpenWebUI (~300 MB RAM en idle)
- 9 bots Discord (~50 MB RAM × 9 = ~450 MB)
- Total estimado: ~1.5 GB RAM de 4 GB disponibles
**¿Es suficiente?** Sí, con margen. Como no vas a usar Ollama (ya tienes Claude vía API), el CX22 es más que suficiente indefinidamente.
**Alternativa gratuita:** Oracle Cloud Free Tier (4 vCPU ARM, 24 GB RAM). Funciona, pero la fiabilidad es menor y el setup más complejo.
---
## ROI: ¿Qué ganas vs. qué cuesta de más?
### Tiempo ahorrado por vídeo
| Tarea | Tiempo manual (con claude.ai) | Con agentes | Ahorro |
|-------|------------------------------|-------------|--------|
| SEO (abrir claude.ai, pedir, copiar a YouTube) | 20 min | 2 min (revisar) | 18 min |
| Guión base (pedir, copiar, formatear, subir a repo) | 40 min | 10 min (revisar PR) | 30 min |
| Artículo de blog (pedir, formatear MDX, crear archivo, commit) | 35 min | 5 min (revisar PR) | 30 min |
| Posts de redes (pedir, copiar a cada plataforma) | 20 min | 3 min (revisar) | 17 min |
| **Total por vídeo** | **115 min** | **20 min** | **95 min (83%)** |
> Nota: el tiempo manual ya es bajo porque usas Claude Pro. El ahorro del sistema es principalmente en el **copy-paste, formateo y commit** — la parte mecánica que queda después de tener la IA.
Con 2 vídeos/semana: **~3 horas ahorradas/semana = ~12 horas/mes**
### Valoración
- 12 horas/mes × 30 €/h = **360 € en tiempo recuperado**
- Coste adicional del sistema: **~6-14 €/mes**
- **ROI: ~25-60x sobre el coste nuevo**
O dicho de otra forma: el sistema amortiza su coste mensual en **menos de 1 hora de trabajo recuperado**.
---
## ¿Dónde NO merece la pena gastar?
### OpenAI API además de Anthropic API
Ya tienes Claude Pro y conoces la calidad. No hay razón para pagar también OpenAI. Usa Claude para todo.
### Claude Pro Max ($100/mes)
Para este caso de uso, Pro a $20 es más que suficiente. Max está pensado para uso intensivo personal en claude.ai, no para el sistema de agentes.
### Ollama en VPS
Con acceso a Claude Sonnet vía API a ~0.10 €/vídeo, no tiene ningún sentido gastar 15-20 €/mes extra en un VPS más grande para correr modelos locales de peor calidad.
### GitHub Pro
No llegarás al límite de Actions gratuitas. No hace falta.
### Discord Nitro
Los bots funcionan igual sin Nitro.
---
## Escalado: ¿qué cambia si el canal crece?
| Escenario | Cambio necesario | Coste adicional |
|-----------|-----------------|----------------|
| Más vídeos/semana (4-5) | Ninguno | ~0.50-1 €/semana más en API |
| Tráfico web alto (50K visitas/mes) | CDN Cloudflare (gratis) | 0 € |
| Añadir agentes nuevos | Nuevo bot Discord + container Docker | ~1-2 €/mes en tokens |
| Equipo colaborando | GitHub repo privado + más canales Discord | 0 €/mes |
---
## Decisión rápida según tu situación
Dado que ya tienes Claude Pro activo:
1. **Arrancar (coste nuevo: ~5-6 €/mes):**
Solo el VPS + dominio. Solo Tyrion + Samwell + Varys. Suficiente para automatizar el 80% del trabajo de un vídeo.
2. **Sistema completo (coste nuevo: ~8-12 €/mes):**
Los 9 agentes. Añade Haiku para los agentes de tareas cortas y Sonnet para Samwell y Jon.
**Recomendación:** Ve al punto 2 directamente. La diferencia entre 1 y 2 es de ~4-6 €/mes y tienes los 9 agentes desde el primer día. Con lo que ya gastas en Claude Pro, es marginal.

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

0
docs/social/.gitkeep Normal file
View File

0
docs/sponsors/.gitkeep Normal file
View File

245
docs/stack-explained.md Normal file
View File

@@ -0,0 +1,245 @@
# Qué es cada pieza del sistema y para qué sirve
Una guía rápida para entender por qué existe cada tecnología y qué pasaría si no la tuvieras.
---
## Astro
**¿Qué es?**
Un framework para construir sitios web. Genera HTML estático a partir de componentes y archivos Markdown/MDX.
**¿Para qué lo usamos?**
Es la web pública `carlospalanca.es`. Sirve el blog, los artículos y el contenido del canal.
**¿Por qué Astro y no WordPress/otro?**
- El output es HTML puro — se sirve desde nginx sin ningún proceso Node corriendo en el servidor
- Los agentes pueden crear contenido nuevo simplemente añadiendo archivos `.md` vía Pull Request
- Muy rápido y barato de hostear (solo archivos estáticos)
**¿Qué pasaría sin él?**
No tendrías web donde publicar el contenido que generan los agentes.
---
## GitHub
**¿Qué es?**
Plataforma de control de versiones y colaboración de código.
**¿Para qué lo usamos?**
- Almacena todo el código del proyecto
- Es donde los agentes crean Pull Requests con contenido nuevo
- Es el punto de partida del pipeline de despliegue (push a main → web actualizada)
- Sirve como historial y trazabilidad de todo lo que hacen los agentes
**¿Por qué es la "fuente de verdad"?**
Porque todo cambio pasa por aquí: el agente crea la rama, tú revisas el PR, lo mergeas, y la web se actualiza sola. Nada llega a producción sin pasar por GitHub.
**Fine-Grained PAT (token de GitHub):**
Es una llave con permisos muy limitados que usan los agentes para crear ramas y PRs. Al tener scope mínimo (solo este repo, solo contents + pull_requests), aunque un agente se comporte mal no puede tocar nada fuera de su scope.
**¿Qué pasaría sin él?**
Los agentes no tendrían dónde guardar el contenido que generan ni forma de enviártelo para revisión.
---
## GitHub Actions
**¿Qué es?**
El sistema de automatización de GitHub. Ejecuta scripts automáticamente cuando ocurren eventos (push, PR, merge).
**¿Para qué lo usamos?**
Dos workflows:
| Workflow | Cuándo se ejecuta | Qué hace |
|----------|------------------|----------|
| `ci.yml` | En cada PR | Construye el sitio y comenta si el build pasa o falla |
| `deploy.yml` | Al mergear a main | Construye el sitio y lo sube al VPS via rsync |
**¿Por qué es importante el CI en los PRs?**
Cuando Samwell crea un PR con un artículo de blog, puede tener un error en el frontmatter (YAML mal escrito, campo que falta) que rompa el build. El CI lo detecta antes de que llegue a producción y te lo avisa con un comentario en el PR.
**¿Qué pasaría sin él?**
Tendrías que hacer el build y el deploy manualmente cada vez, y podrías subir contenido roto a producción sin saberlo.
---
## VPS (Servidor Virtual Privado)
**¿Qué es?**
Un servidor Linux en la nube que está encendido 24/7 y es accesible desde internet.
**¿Para qué lo usamos?**
- Sirve la web estática de Astro (via nginx)
- Corre OpenWebUI (el gateway de los LLMs)
- Corre los 9 Discord bots (via Docker)
**¿Por qué no usar Vercel/Netlify para la web y nada más?**
Porque también necesitas hostear OpenWebUI y los 9 bots, que son procesos Docker de larga duración. Vercel solo sirve para webs estáticas. Con un VPS tienes todo en un sitio.
**¿Qué pasaría sin él?**
No tendrías dónde correr los bots ni OpenWebUI.
---
## nginx
**¿Qué es?**
Un servidor web y proxy inverso. Recibe las peticiones HTTP/HTTPS y las dirige al sitio correcto.
**¿Para qué lo usamos?**
Dos funciones en este proyecto:
1. **Servir la web:** cuando alguien abre `carlospalanca.es`, nginx lee los archivos de `/var/www/carlospalanca.es/` y los devuelve.
2. **Proxy a OpenWebUI:** cuando alguien abre `ai.carlospalanca.es`, nginx redirige la petición a `localhost:3000` donde corre OpenWebUI — sin exponer el puerto directamente a internet.
**¿Por qué no exponer OpenWebUI directamente en el puerto 3000?**
Porque nginx gestiona el SSL (HTTPS). Sin nginx, OpenWebUI estaría en `http://` sin cifrado. También permite tener múltiples servicios en el mismo servidor con distintos dominios.
**¿Qué pasaría sin él?**
La web no sería accesible y OpenWebUI no tendría HTTPS.
---
## Let's Encrypt / Certbot
**¿Qué es?**
Un servicio gratuito que emite certificados SSL. Certbot es la herramienta que los instala y renueva automáticamente.
**¿Para qué lo usamos?**
Para que la web use HTTPS (`https://carlospalanca.es`) en lugar de HTTP sin cifrado. Sin certificado, los navegadores muestran "Conexión no segura".
**¿Qué pasaría sin él?**
La web funcionaría solo en HTTP. Nadie confiaría en ella y Google la penalizaría en SEO.
---
## Docker y Docker Compose
**¿Qué es?**
Docker empaqueta aplicaciones en contenedores — entornos aislados con todo lo que necesitan para funcionar. Docker Compose orquesta múltiples contenedores.
**¿Para qué lo usamos?**
- Cada Discord bot es un contenedor Docker separado
- OpenWebUI corre como contenedor Docker
- Esto significa que puedes levantar los 9 bots con un solo comando: `docker compose up -d`
**Ventajas:**
- Si un bot falla, no afecta a los demás
- `restart: unless-stopped` los reinicia automáticamente si crashean
- Fácil de actualizar: `docker compose build && docker compose up -d`
- El entorno es idéntico en tu máquina y en el VPS
**¿Qué pasaría sin él?**
Tendrías que instalar Python, dependencias y gestionar 9 procesos manualmente en el VPS. Docker lo simplifica enormemente.
---
## OpenWebUI
**¿Qué es?**
Una interfaz web open-source para interactuar con modelos de lenguaje (LLMs). Tiene una API compatible con el formato de OpenAI.
**¿Para qué lo usamos?**
Es el **gateway centralizado de LLM**. Los 9 bots no llaman directamente a OpenAI o Anthropic — llaman a OpenWebUI, que a su vez llama al proveedor.
**¿Por qué este intermediario?**
| Sin OpenWebUI | Con OpenWebUI |
|---------------|---------------|
| Cambiar de GPT-4o a Claude requiere editar el código de los 9 bots y redeployar | Cambias `OPENWEBUI_MODEL=claude-3-5-sonnet` en `.env` y reinicias |
| No tienes visibilidad del consumo de tokens por agente | Dashboard web con historial de conversaciones y uso |
| No puedes chatear manualmente con los modelos | Tienes una UI en `ai.carlospalanca.es` para uso personal |
**¿Qué pasaría sin él?**
Seguiría funcionando si llamas directamente a la API de OpenAI, pero perderías la flexibilidad de cambiar de modelo y el dashboard de control.
---
## Discord
**¿Qué es?**
Plataforma de mensajería con soporte para bots programables.
**¿Para qué lo usamos?**
Es la **interfaz de control del sistema**. En lugar de tener una web especial para hablar con los agentes, usas Discord. Cada agente tiene su propio canal.
**¿Por qué Discord y no Telegram, Slack u otro?**
- Permite múltiples bots con identidades visuales separadas (cada bot tiene su nombre y avatar)
- Tyrion aparece como "Tyrion Lannister", Samwell como "Samwell Tarly", etc.
- Los canales organizan las conversaciones por agente de forma natural
- Gratis para este uso
**¿Por qué 9 aplicaciones Discord separadas y no una sola?**
Porque si fuera un solo bot, todos los mensajes aparecerían del mismo bot. Así cada agente tiene su propia identidad visual en Discord, lo que hace mucho más claro quién está haciendo qué.
**¿Qué pasaría sin él?**
Necesitarías otra interfaz para dar instrucciones a los agentes (una web propia, Telegram, etc.).
---
## discord.py
**¿Qué es?**
Librería de Python para crear bots de Discord.
**¿Para qué lo usamos?**
Es el núcleo de cada agente. Gestiona la conexión con Discord, escucha mensajes en el canal asignado y envía respuestas.
**¿Qué pasaría sin él?**
Habría que usar la API de Discord directamente con HTTP puro, mucho más complejo.
---
## PyGithub
**¿Qué es?**
Librería de Python que envuelve la API REST de GitHub.
**¿Para qué lo usamos?**
En `agents/shared/github_client.py` para crear ramas, hacer commits y abrir Pull Requests desde Python con pocas líneas de código.
**¿Qué pasaría sin él?**
Habría que hacer las llamadas HTTP a la API de GitHub manualmente.
---
## Resumen visual
```
TÚ (Carlos)
│ escribes en
Discord ──────────────────────────────────────────────────────┐
│ │
│ discord.py escucha │
▼ │
9 Bots Python (Docker en VPS) │
│ │
│ llaman vía HTTP │
▼ │
OpenWebUI (Docker en VPS, detrás de nginx con HTTPS) │
│ │
│ llama a │
▼ │
OpenAI / Anthropic / Ollama (LLM) │
│ │
│ respuesta │
▼ │
Bot crea PR con PyGithub │
│ │
▼ │
GitHub ◄──────────────────── tú revisas y mergeas ────────────┘
│ merge a main dispara
GitHub Actions
│ build + rsync
VPS (/var/www/carlospalanca.es/) ◄── nginx sirve
https://carlospalanca.es (web pública con Let's Encrypt)
```