Docs / 00. Por qué separar Core e Interfaces

Por qué separar los agentes de IA en core e interfaces

Cómo desacoplar el ciclo de ejecución de la heterogeneidad de los canales de entrada y dejar de reinventar la fontanería en cada agente desarrollado.

🎙️
Escucha el artículo elaborado con IA AUDIO IA
Escucha el análisis arquitectónico completo elaborado en alta fidelidad
Por qué separar los agentes de IA en core e interfaces

"A lo que hoy llaman 'orquestación agéntica reflexiva', en mi época de estudiante lo llamaban 'autómata finito con realimentación y control de errores'. La teoría no ha cambiado; lo que ha cambiado son las herramientas y la velocidad a la que se construye."


Cuando empecé a trastear en serio con modelos de lenguaje y agentes de IA, seguí el camino canónico de casi todo el mundo:

  1. Monté el típico chatbot básico que respondía preguntas.
  2. Le conecté un RAG para consultar documentos propios.
  3. Le añadí tools para que pudiera interactuar con el entorno.

Hasta ahí, el camino habitual. Pero en cuanto quise utilizar aquello para automatizaciones reales, apareció la fricción: para cada nuevo proyecto tenía que volver a empezar prácticamente desde cero.

Unas veces el problema era la máquina de estados: un caso de uso conversacional requería un flujo de tres pasos, mientras que una tarea de operaciones exigía un grafo de cinco nodos con validaciones estrictas.

Otras veces —la gran mayoría— el dolor de cabeza era la heterogeneidad de los canales. En una ocasión la entrada venía de eventos y audio de una centralita telefónica Asterisk (PBX); en otra, de mensajes asíncronos en Telegram; en otra, de webhooks JSON emitidos por GitLab, o de un simple formulario web.

El choque fue inmediato: cuando integré Asterisk con el primer prototipo, tuve que incrustar la lógica de captura de eventos y el parseo de tramas dentro del mismo script que decidía las llamadas a las herramientas. Semanas después, al querer reutilizar ese mismo razonamiento para un bot de Telegram, cambiar el formato del mensaje obligaba a reescribir las transiciones del grafo. Y a la inversa: si ajustaba un paso de verificación en el flujo cognitivo, rompía la compatibilidad con el webhook de GitLab. El acoplamiento era total y estaba reinventando la fontanería en cada iteración.


La respuesta: abstraer el runtime y desacoplar las interfaces

Esto no se soluciona afinando prompts; se soluciona con abstracción de sistemas y separación estricta de responsabilidades.

El diseño pedía dividir la arquitectura en dos planos independientes conectados por contratos canónicos:

+-----------------------------------------------------------------------+
|        CANALES HETEROGÉNEOS (Asterisk PBX / Telegram / Webhooks)       |
+-----------------------------------------------------------------------+
                                    |
                                    | Formatos dispares (Audio, JSON, etc.)
                                    v
+-----------------------------------------------------------------------+
|              CONTROL PLANE: jg_dashboard (Laravel / PHP)              |
|                                                                       |
|  * Adaptadores: normalizan entradas a contrato canónico (AgentInput)  |
|  * Gobernanza HITL: lockForUpdate transaccional y Reaper con TTL      |
|  * Seguridad M2M: firma criptográfica HMAC-SHA256 con X-Nonce         |
+-----------------------------------------------------------------------+
                                    |
                                    | Petición firmada (AgentInput)
                                    v
+-----------------------------------------------------------------------+
|          EXECUTION PLANE: jg_agente (Python / FastAPI / LangGraph)    |
|                                                                       |
|  * Microkernel agnóstico: ciclo formal de estados determinista:       |
|    Observar -> Planificar -> Evaluar Riesgo ->                        |
|    -> Ejecutar -> Verificar -> Compensar                              |
|  * SecurityGuard determinista: DNS Pinning TCP, Jail y Fail-Closed    |
+-----------------------------------------------------------------------+
                                    |
                                    | Retorno estructurado auditado
                                    v
+-----------------------------------------------------------------------+
|             RESULTADOS AUDITADOS: AgentOutput / Telemetría            |
+-----------------------------------------------------------------------+
  • Canales de entrada heterogéneos: Telefonía PBX (Asterisk), mensajería (Telegram) y webhooks de eventos CI/CD.
  • Control Plane (jg_dashboard en Laravel/PHP): Actúa como pasarela de entrada. Sus adaptadores transforman los eventos dispares en contratos normalizados (AgentInput), gestionan la gobernanza y compuertas de decisión humana (HITL con bloqueos pesimistas en base de datos) y firman criptográficamente cada petición inter-servicio hacia el runtime mediante HMAC-SHA256 con ventana anti-repetición.
  • Execution Plane (jg_agente en Python/FastAPI/LangGraph): Opera como un runtime mínimo y agnóstico. Su trabajo se limita a ejecutar un ciclo formal de estados: Observar → Planificar → Evaluar Riesgo → Ejecutar → Verificar → Compensar. Al núcleo no le importa el origen de la orden: únicamente procesa el contrato estructurado, gobierna las herramientas y devuelve un resultado auditado (AgentOutput).

Al separar los planos, la arquitectura dejó de ser un monolito frágil. Pero en cuanto permites que un modelo invoque herramientas que modifican recursos reales (reiniciar servicios, alterar bases de datos o consultar la red), saltan los problemas de sistemas de toda la vida:

  • Concurrencia en la supervisión humana (HITL): Si una acción crítica requiere aprobación de un operador, un botón asíncrono en una interfaz no es suficiente. Si dos operadores interactúan al mismo tiempo o la red parpadea, se requiere coordinación transaccional con bloqueos pesimistas, control de idempotencia y procesos reconciliadores por si el worker cae durante la llamada HTTP.
  • Seguridad de red a bajo nivel: Si el modelo utiliza herramientas de consulta web, confiar en que el prompt no pida una IP interna es ingenuo. Debería solucionarse atando la conexión TCP a la IP ya validada.
  • Persistencia y compensación distribuida: Si un plan de cuatro pasos falla en el tercero tras haber mutado infraestructura, el sistema no puede quedar inconsistente. Requiere persistencia transaccional duradera en PostgreSQL (aislando la E/S síncrona del bucle asíncrono) y contratos con funciones de compensación inversa obligatorias (patrón Saga).

La IA como herramienta de implementación

Quiero ser completamente transparente en esto: yo solo, picando código a mano en mis ratos libres, jamás habría levantado una plataforma con este nivel de cobertura en este plazo.

Los humanos tenemos limitaciones biológicas evidentes: tiempo finito, fatiga y un ancho de banda limitado para mantener miles de líneas de sintaxis repetitiva en la cabeza. La IA ha sido un multiplicador de implementación: acelerando el andamiaje inicial (scaffolding), generando pruebas unitarias y contrastando esquemas.

Mi papel no ha sido escribir manualmente cada línea ni delegar las decisiones técnicas a ciegas. Ha sido definir la arquitectura, establecer los contratos entre componentes, identificar los vectores de riesgo y exigir pruebas que permitan verificar las decisiones de diseño.

Acelerar la generación de código no elimina la necesidad de decidir qué debe hacer el sistema, qué no debe permitir y cómo demostrar que se comporta como esperamos.


Una cura de humildad: construirlo no es lo mismo que validarlo

Quiero dejar claro un límite fundamental: yo no puedo afirmar que esta plataforma esté preparada para soportar cargas masivas en un entorno corporativo ni que sus garantías estén demostradas bajo fuego real.

El sistema cuenta con una batería de más de 400 pruebas automatizadas (pruebas unitarias de aislamiento, integración del grafo LangGraph y validaciones adversariales de seguridad contra SSRF, traversal e inyección de comandos), y corre actualmente desplegado en un entorno propio sobre contenedores Docker.

Estas pruebas aportan evidencia sobre los escenarios concretos que cubren y ayudan a detectar regresiones. No demuestran, por sí solas, que las mitigaciones sean exhaustivas ni que el sistema esté preparado para cualquier escenario operativo.

Lo que tengo para mostrar son decisiones de diseño razonadas, la implementación que las respalda y el código disponible para su análisis técnico en https://gitlab.com/javiergalvez-ia.


La tesis de esta serie

La idea central que articula todo este trabajo es sencilla: el modelo de lenguaje propone intenciones; el sistema determinista dispone la ejecución y gobierna los límites.

La IA nos da una potencia asombrosa, pero la robustez sigue dependiendo de la ingeniería de siempre.

En los artículos que publicaré en las siguientes semanas intentaré desgranar cómo he abordado estos desafíos pieza por pieza: desde la mitigación de SSRF en la capa de socket TCP hasta el manejo transaccional de decisiones humanas y las sagas de rollback.

Metadatos del documento
Título Por qué separar los agentes de IA en core e interfaces
Archivo docs/02_jg_agent/articulos/00_why.md
Descripción Cómo desacoplar el ciclo de ejecución de la heterogeneidad de los canales de entrada y dejar de reinventar la fontanería en cada agente desarrollado.
Autor Javier Gálvez
Fecha 2026-10-17
Etiquetas
#Artículos #Arquitectura #Core e Interfaces #Runtime #Canales Heterogéneos #Desacoplamiento