Automatización matutina con IA sin n8n ni agentes

Vistas: 128
0:00 / 0:00
Automatización matutina con IA sin n8n ni agentes

Últimamente he estado dándole muchas vueltas al tema de los agentes de IA. En los últimos episodios te he hablado de Hermes, de los subagentes, de las skills y de los MCP. Y sí, son herramientas brutales para según qué cosas. Pero no para todo. De hecho, cuanto más los uso, más me doy cuenta de que para muchas tareas cotidianas un agente es una exageración. Un agente carga con un montón de contexto, con un montón de herramientas, con skills, con MCPs… y todo eso consume recursos. Si lo que necesitas es una automatización sencilla y repetitiva, no necesitas un agente. Necesitas un script, un timer y una notificación. Tres capas. Nada más.

Y de esto precisamente va el episodio de hoy. Te voy a contar cómo he montado mi nightly-runner, un script que cada madrugada recopila información de cuatro fuentes distintas, la procesa con un modelo de lenguaje local y me envía un resumen matutino a Telegram. Todo sin n8n, sin agentes, sin servicios externos de pago y sin complicarme la vida.

El problema: 15 minutos perdidos cada mañana

Piénsalo. ¿Qué haces cuando te levantas? Seguramente mirar el tiempo para saber qué ropa ponerte, ojear las noticias para ver si ha pasado algo relevante, comprobar si hay alguna oferta de ese producto que llevas tiempo queriendo comprar y echar un vistazo al estado de tu servidor para asegurarte de que todo sigue funcionando.

Son cuatro cosas. En apariencia no es mucho, pero entre abrir el móvil, buscar cada página, esperar a que cargue, leer, procesar la información y pasar a la siguiente, se te van fácilmente 10 o 15 minutos. Cada día. 365 días al año. Esto son más de 90 horas al año perdidas en tareas que podrías tener automatizadas.

Y aquí es donde mucha gente piensa en montar un agente. Le dices a Hermes que cada mañana te haga un resumen y listo. Pero Hermes carga con todo su ecosistema de herramientas, skills y MCPs. Para hacer cuatro peticiones HTTP y generar un texto, estás pagando un coste de contexto desproporcionado. Es como usar un camión para llevarte una bolsa de la compra a casa.

La solución: tres capas

La alternativa es mucho más sencilla. Se basa en un patrón de tres capas que puedes aplicar a cualquier automatización, independientemente de lo que quieras hacer. Da igual si vas a monitorizar el tiempo, las noticias, los precios de un producto o el estado de tu servidor. El patrón es siempre el mismo.

La primera capa es el script. Un programa que hace el trabajo pesado: consulta APIs, hace scraping, recopila datos, los procesa y los guarda. En mi caso lo he hecho en Python porque es rápido de prototipar y porque es el lenguaje que mejor se me da para este tipo de cosas, pero podría ser en Rust, en Bash, en Go o en el lenguaje que prefieras. La clave es que el script sea modular, que cada tarea sea independiente y que los resultados se guarden en un formato estructurado como JSON.

La segunda capa es el timer. El encargado de ejecutar el script a una hora determinada. En Linux tienes dos opciones clásicas: cron y systemd timers. Yo te recomiendo systemd timers, como ya te conté en el episodio 808, porque te dan mucho más control. Con systemd timers tienes logs estructurados, puedes establecer dependencias (por ejemplo, que espere a que la red esté disponible), y tienes la opción Persistent=true que ejecuta las tareas pendientes si el equipo estaba apagado.

La tercera capa es la notificación. De nada sirve que el script se ejecute y recopile datos si no te llega la información. Aquí tienes muchas opciones: Telegram con un bot, Matrix, un mensaje en el escritorio con notify-send, un correo electrónico, un webhook, lo que prefieras. La clave es que la notificación sea útil y no intrusiva. Que te dé justo la información que necesitas, ni más ni menos.

El script: nightly-runner

Mi script se llama nightly-runner y está organizado en módulos independientes. Cada uno se encarga de una fuente de datos y genera un archivo JSON. Son totalmente independientes entre sí, de manera que si uno falla los demás siguen funcionando. Esto es importante porque no quiero que un fallo en la obtención del tiempo me impida recibir las noticias o las ofertas.

La estructura de directorios es muy sencilla. El script principal, un directorio para los módulos, otro para los JSONs de caché y otro para los logs. Todo limpio y ordenado.

nightly-runner/
├── nightly-runner.py
├── modulos/
│   ├── tiempo.py
│   ├── noticias.py
│   ├── zapatillas.py
│   └── sistema.py
└── datos/
    ├── tiempo.json
    ├── noticias.json
    ├── zapatillas.json
    └── sistema.json

El script se ejecuta con flags para seleccionar qué módulos quieres activar:

python3 nightly-runner.py --tiempo --noticias --zapatillas --sistema

O si quieres todo de una vez:

python3 nightly-runner.py --all

Y como es idempotente, si falla un módulo y se reintenta más tarde, no se duplica nada. Cada ejecución sobrescribe su JSON correspondiente.

El tiempo con wttr.in

Para la información meteorológica he usado wttr.in, un servicio que te devuelve el tiempo en formato JSON con una simple petición curl. Es gratuito, no requiere API key y funciona de maravilla.

curl "https://wttr.in/Madrid?format=j1"

El JSON que devuelve incluye temperatura, sensación térmica, viento, humedad, probabilidad de lluvia, rayos ultravioleta y mucho más. Todo en unos pocos cientos de bytes, perfecto para que un modelo de lenguaje lo procese.

Si quieres el tiempo de varias ciudades, simplemente haces varias peticiones y las agregas. El script lo guarda todo en tiempo.json con la fecha del día.

Noticias con scraping inteligente

Para las noticias he usado el mismo enfoque que te conté en el episodio 813. En lugar de hacer scraping tradicional con selectores CSS que se rompen cada dos por tres, le paso el HTML directamente a un modelo de lenguaje y le pido que extraiga los titulares relevantes.

El proceso es sencillo: para cada fuente de noticias que tengas configurada, el script descarga el HTML, lo limpia eliminando scripts, menús y footers, y se lo pasa a Ollama con un prompt de extracción. El modelo devuelve un JSON estructurado con el título, la fuente, la relevancia (alta, media o baja) y la URL.

[
  {
    "titulo": "Nueva vulnerabilidad en systemd afecta a millones de servidores",
    "fuente": "Linux Magazine",
    "relevancia": "alta",
    "url": "https://..."
  }
]

El resumen matutino selecciona un máximo de tres noticias, priorizando las de relevancia alta. Si no hay nada destacable, simplemente dice que no hay novedades.

Zapatillas: comparativa con caché

Este módulo es el mismo que te conté en el episodio 813 adaptado para ejecución automática. Hace scraping de varias tiendas online, extrae el nombre del producto, el precio y la disponibilidad, y lo compara con los precios de la noche anterior.

El sistema de caché es clave aquí. Cada noche se guarda el precio en un histórico, de manera que el script puede detectar si ha bajado o subido respecto al día anterior. Si encuentra una oferta buena, el resumen te dice directamente compra ya. Si el precio ha bajado ligeramente, te avisa. Si no hay cambios, no lo menciona.

En la demo del episodio, el script encontró unas Brook Glittering a 108 euros, unas Night Pegasus a 79 y unas Asics a 32 euros. Todo en cuestión de segundos, sin tener que abrir una sola página web.

Sistema: df, free, uptime y ps

Para la información del sistema, el script ejecuta comandos estándar de Linux y parsea la salida. Con df obtienes el uso del disco, con free la memoria disponible, con uptime el tiempo que lleva encendido el equipo y con ps aux los procesos más pesados.

Toda esta información se guarda en sistema.json con un formato como este:

{
  "disco": {"total": 256, "usado": 187, "libre": 69, "porcentaje": 73},
  "memoria": {"total": 16, "libre": 2.3, "porcentaje": 85},
  "uptime": "14 días, 6 horas",
  "procesos": [{"pid": 1234, "nombre": "ollama", "cpu": 45, "memoria": 8.2}]
}

Con estos datos, el script puede establecer condiciones claras para las alertas. Si el disco está por encima del 80%, te avisa. Si supera el 90%, te pone un mensaje de warning. Si quedan menos de 2 GB de RAM libres, te lo dice.

El resumen con Llama 3.2

Una vez que los cuatro módulos han terminado y tenemos los cuatro JSONs, llega el momento de generar el resumen. Aquí es donde entra el modelo de lenguaje.

El script lee los cuatro archivos, los mete en un prompt y se lo envía a Ollama con Llama 3.2. El prompt es algo así:

Eres un asistente de resúmenes matutinos. Analiza los datos en JSON y genera un mensaje para Telegram con formato Markdown. Máximo 15 líneas. Tono cercano. Prioriza ofertas y alertas. Si falta un dato, dilo con naturalidad. No inventes nada que no esté en los datos.

El modelo devuelve un texto como este:

Buenos días ☀️

Hoy en Madrid hace 28°C y cielo despejado. Perfecto para salir a correr.

📰 Noticias destacadas: Nueva vulnerabilidad en systemd (alta relevancia).

👟 Oferta: Night Pegasus a 79€ (10€ menos que ayer). Cómpralas ya.

🖥️ Servidor: disco al 73%, RAM al 85%. Todo ok.

¡Buen jueves!

Todo en un solo mensaje, con la información que realmente te importa, sin ruido.

Systemd timer: el corazón de la automatización

Para que el script se ejecute automáticamente cada madrugada, uso un timer de systemd. Te expliqué esto en detalle en el episodio 808, pero te hago un resumen rápido.

Necesitas dos archivos. El primero es el servicio:

[Unit]
Description=Nightly runner

[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /home/usuario/nightly-runner.py --all

Y el segundo es el timer:

[Unit]
Description=Nightly runner timer

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

La clave está en Persistent=true. Si tu equipo estaba apagado a las 4 de la mañana (que es cuando está programado el timer), en cuanto se encienda, systemd ejecutará el script pendiente. Así nunca te pierdes el resumen matutino, aunque hayas tenido el ordenador apagado.

Tolerancia a fallos

Una automatización que falla silenciosamente es peor que no tener automatización. Por eso cada capa del nightly-runner está diseñada para ser tolerante a fallos.

Si wttr.in está caído, el script guarda un JSON vacío y el resumen dice no hay información meteorológica disponible. Si Ollama no responde porque el modelo está cargándose, el script reintenta hasta tres veces antes de rendirse. Si Telegram está caído, el script guarda el mensaje en un archivo local para enviarlo más tarde.

Además, cada módulo es independiente. Si el de las zapatillas falla porque una tienda cambió su HTML, los demás módulos siguen funcionando sin problemas. No hay un único punto de fallo.

El modelo de lenguaje: por qué Llama 3.2 es suficiente

Puede que te estés preguntando si realmente necesitas un modelo de lenguaje para esto. La respuesta es que no, pero una vez que lo pruebas no quieres volver atrás. Sin modelo de lenguaje, tendrías que formatear los datos a mano, construir el mensaje con concatenación de strings y lidiar con los casos bordes tú mismo.

Con Llama 3.2, el proceso es mucho más elegante. Le pasas los JSONs en bruto y él se encarga de interpretarlos, priorizar la información relevante y redactar el mensaje con un tono natural. Y lo hace con un modelo de 3B parámetros que corre en CPU en cuestión de segundos. No necesitas una GPU ni pagar por una API.

El prompt es el mismo cada día. Lo único que cambian son los datos. Y como el modelo es determinista con temperatura baja, el resultado es consistente: mismo tono, mismo formato, misma estructura. Lo que cambia es el contenido en función de los datos del día.

La notificación: Telegram y alternativas

Para las notificaciones uso Telegram, pero no es la única opción. Si prefieres algo más privado, puedes usar Matrix, que además te permite tener tu propio servidor de mensajería. Si quieres algo más simple, notify-send te muestra el mensaje en el escritorio de Linux. Y si necesitas compatibilidad máxima, un correo electrónico siempre funciona.

La clave es que la notificación sea fiable. Da igual lo bonito que sea el resumen si no te llega. Por eso el script tiene un sistema de reintentos para la notificación, con un máximo de tres intentos y un intervalo de espera entre ellos.

El coste: cero euros

Una de las cosas que más me gusta de esta solución es que no cuesta dinero. El script corre en tu propio hardware. El modelo de lenguaje corre en local con Ollama. El timer es de systemd, que ya viene instalado en cualquier distribución Linux. Y la notificación a Telegram es gratuita.

El único coste es el tiempo que inviertes en montarlo. Pero como te he dicho, si usas un modelo de lenguaje para que te genere el script, ese tiempo se reduce a unos minutos. Y a partir de ahí, el ahorro de tiempo es diario.

Menos es más

Llegados a este punto, igual te estás preguntando: vale, todo esto está muy bien, pero yo no sé Python. ¿Cómo lo monto?

Pues la respuesta es más sencilla de lo que parece. Usa un modelo de lenguaje para que te genere el script. Abre Open Code, Gemini o el modelo que prefieras y explícale exactamente lo que quieres:

Necesito un script en Python que cada mañana haga lo siguiente: consultar el tiempo en wttr.in para mi ciudad, extraer noticias de estas tres fuentes, buscar ofertas de zapatillas de running en estas dos tiendas y comprobar el estado del disco y la memoria de mi sistema. Quiero que cada módulo guarde su resultado en un JSON independiente y que luego todos se pasen a Ollama con Llama 3.2 para generar un resumen en lenguaje natural. El resultado final debe enviarse a Telegram.

En cuestión de segundos, el modelo te genera el script completo. Luego solo tienes que crear el timer de systemd (que también te lo puede generar el modelo) y ponerlo en marcha. Incluso puedes pedirle que te genere el servicio y el timer de systemd, con las rutas ajustadas a tu sistema.

Si no te fías del resultado o quieres retocarlo, siempre puedes pedirle cambios. Cambia las fuentes de noticias por estas otras, añade una ciudad más para el tiempo, que me avise también si el precio de las zapatillas baja de 50 euros. El modelo entiende lenguaje natural, así que no necesitas aprender una sintaxis específica.

No necesitas ser un experto en Python para montar una automatización como esta. Necesitas saber lo que quieres y ser capaz de explicarlo. Y para eso, los modelos de lenguaje son perfectos.

Menos es más

Al final, la gran lección de este episodio es que no todo necesita un agente. De hecho, la mayoría de las automatizaciones cotidianas se resuelven mejor con un script bien hecho, un timer bien configurado y una notificación bien dirigida que con un agente hipervitaminado.

Un agente tiene sentido cuando necesitas que tome decisiones complejas en tiempo real, cuando tiene que interactuar con múltiples herramientas de forma dinámica, cuando el flujo no está definido de antemano. Pero para una automatización fija y repetitiva como un resumen matutino, las tres capas son más que suficientes.

Y lo mejor de todo es que tienes el control total. No dependes de un servicio externo (excepto para la notificación, si usas Telegram). No pagas por API calls. No compartes tus datos con nadie. Todo corre en tu máquina, con tu modelo local, bajo tu control.

El script nightly-runner.py lo dejaré en las notas del episodio para que puedas trastear con él. Y si tienes ideas para mejorarlo, ya sabes dónde encontrarme. En el grupo de Telegram de atareao con Linux siempre estamos dándole vueltas a este tipo de cosas.

Más allá del resumen matutino

Lo mejor de este patrón de tres capas es que no se limita al resumen matutino. Puedes aplicarlo a cualquier automatización que se te ocurra. Un recordatorio para regar las plantas cuando el sensor de humedad del suelo baje de cierto umbral. Un aviso cuando el precio de un vuelo baje de 100 euros. Una alerta cuando el certificado SSL de tu web esté a punto de caducar. Un informe semanal del tráfico de tu blog. Cualquier cosa que se pueda reducir a recopilar datos, procesarlos y notificar el resultado encaja en este patrón.

Puedes añadir más módulos al nightly-runner sin tocar los existentes. Un módulo que lea tu calendario y te recuerde las citas del día. Un módulo que consulte la API de tu gestor de tareas y te diga qué tienes pendiente. Un módulo que revise los logs de tu servidor en busca de errores. Cada módulo es independiente, cada uno genera su JSON, y el resumen los agrega todos. El modelo de lenguaje se encarga de darle coherencia al conjunto.

La clave está en mantener cada capa independiente. El script no sabe cuándo se ejecuta ni cómo se notifica el resultado. El timer no sabe qué hace el script ni cómo se notifica. La notificación no sabe qué datos se recopilaron ni cuándo. Esta separación de responsabilidades hace que el sistema sea fácil de mantener, de depurar y de ampliar.

Y si en algún momento necesitas algo más complejo, siempre puedes añadir más capas. Un sistema de colas si tienes muchas tareas. Una base de datos si necesitas histórico. Una interfaz web si quieres consultar los datos manualmente. Pero empieza simple. Las tres capas básicas son suficientes para la mayoría de los casos.

Espero que te haya gustado el episodio, que lo hayas disfrutado y que te animes a montar tu propia automatización. La vida son dos días y uno ya ha pasado, así que mejor automatizar lo que podamos y dedicar el tiempo a lo que realmente importa.


Más información

Deja una respuesta