Files
carlospalanca.es/docs/architecture.md

8.9 KiB

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