El problema
Quería un asistente de verdad: que investigara en internet por mí, me guardara notas y listas, leyera mi correo y me redactara respuestas, y que me mandara cada mañana un resumen del día. Todo desde Telegram y en un servidor mío.
El riesgo en un sistema así no es que alguien entre en el servidor. Es que el agente lea una página o un correo con instrucciones escondidas dirigidas a él, y haga algo que yo no pedí. La página se sirve con normalidad, el agente la lee como está diseñado y, si esa misma pieza tiene acceso a mis datos y capacidad de mandar correos, el desastre está servido.
Un sistema de agentes se vuelve peligroso cuando una misma pieza reúne tres cosas: acceso a datos que importan, exposición a contenido escrito por desconocidos y capacidad de comunicarse hacia fuera. Todo mi diseño parte de ahí.
La solución: zonas de confianza
En vez de intentar detectar inyecciones de prompt filtrando patrones (el lenguaje natural tiene infinitas formas de decir lo mismo), quito el efecto: partí el sistema en zonas para que ninguna pieza reúna las tres cosas a la vez.
| Zona | Agentes | Lee contenido de fuera | Escribe en disco | Actúa hacia fuera |
|---|---|---|---|---|
| Roja | Investigador, lector de correo | Sí | No | No |
| Ámbar | Archivero, memoria | No | Solo en la bóveda | No |
| Verde | Cartero | No | No | Sí, con código TOTP |
Además, el orquestador no tiene ninguna herramienta, el redactor tampoco, y el envío de correo no lo decide un modelo: lo ejecuta código determinista después de que yo lo autorice con un código de 6 dígitos de mi móvil.
Zonas de confianza
La vista previa es exactamente lo que se envía.
Qué hace
/investiga: busca, lee hasta tres páginas y resume con fuentes. Rechaza direcciones de la red interna, protocolos raros y puertos que no son web antes de conectar, incluida la IP pública del propio servidor.- Notas y memoria: una bóveda de ficheros Markdown y una memoria donde cada recuerdo guarda su procedencia (lo dije yo o lo dedujo el modelo) y su fecha, con borrado exacto.
- Correo: resume lo recibido en las últimas 24 horas y redacta correos por mí a partir de un recado en bruto («dile que el martes no puedo»). La vista previa es exactamente lo que se envía, y nada sale sin mi código TOTP.
- Voz: le hablo con notas de voz (transcritas con Groq) y me contesta con voz generada en el propio servidor con Piper.
- Aviso diario a las 08:00, que llega igual aunque falle una de sus partes.
/tools: enciendo y apago grupos de herramientas. Los más expuestos arrancan apagados en cada reinicio.
Cómo lo verifico
La regla del proyecto es «el objetivo no es que funcione: es poder explicarlo». De ahí salen cinco normas de verificación que documenté en un ADR:
- Mutación en los controles críticos. Rompo el control a mano y compruebo que algún test se pone en rojo. Un test que nunca has visto fallar es una hipótesis, no una comprobación.
- Probar en real siempre. Con la batería en verde, uso el bot de verdad en el servidor. Así encontré cuatro fallos que ningún test veía, entre ellos un
/olvidoque contestaba «He borrado la información» sin borrar nada. - Ninguna afirmación técnica sin fuente y fecha. Antes de elegir proveedores los comprobé uno a uno con peticiones HTTP reales, y tres de ellos no eran lo que decía internet: uno estaba retirado (HTTP 410), otro exigía tarjeta desde el registro y la capa gratuita del tercero no aparecía en su documentación oficial.
- Cadena de suministro cerrada. Las dependencias se instalan solo desde un lockfile con hashes, y en producción no se instala nada de desarrollo.
- Auditorías. Lo reviso con mi propio kit-iso (ISO 27001, 5055, 25010, 27701 y CRA) y lo audité con mi propio agente auditor de código. Cada hallazgo lo reproduje antes de corregirlo.
Stack desglosado
- Orquestación: LangGraph con estado persistente en SQLite (sobrevive a un reinicio a mitad de conversación).
- Modelos: Ollama Cloud con cadena de respaldo entre proveedores; Groq para transcribir voz y Piper (local) para sintetizarla.
- Canal:
python-telegram-bot, restringido a un único usuario y solo en privado. A cualquier otro no le responde ni le da pistas de que el bot existe. - Seguridad: TOTP con
pyotp, validación con Pydantic, guardián de red anti-SSRF y registro de metadatos sin contenido. - Programación de tareas: APScheduler para el aviso diario.
- Despliegue: servidor VPS propio, con copias de seguridad verificadas al restaurar.
Resultados y aprendizajes
Lo que funcionó: diseñar la seguridad por estructura y no por filtros. No tengo que ganar una carrera contra cada nueva forma de inyección: aunque el investigador se crea una instrucción maliciosa, no tiene con qué ejecutarla.
Lo que aprendí: el valor de verificar en vez de fiarse. De los informes de IA, de los blogs y de mis propios tests. Casi todos los fallos serios que encontré aparecieron al usar el bot de verdad o al romper un control a propósito, no leyendo el código.
Lo que sigue: cerrar la fase 14 (los hallazgos de la auditoría con mi agente auditor), y las cifras finales de la entrega.