Vista de Arquitectura
Hermes Agent es un agente de escritorio de código abierto (MIT) de Nous Research, posicionado como "un agente persistente que crece contigo": llega un mensaje desde Telegram/Discord/Slack/etc, el bucle principal llama al modelo y a las herramientas (tool) para completar la tarea, y la experiencia se destila en memoria y habilidades que se reutilizan la próxima vez.
Vista por capas
Cada capa en una frase
- Capa UI: TUI / CLI / Web / ACP (protocolo de editor) — solo puntos de entrada, sin lógica de negocio.
- Gateway:
gateway/run.pyunifica el acceso; las plataformas son adaptadores (adapter); sesión (session)/entrega separadas; streams y hooks conectables. - Bucle principal:
run_conversationorquesta "llamada al modelo → llamada a herramienta → devolución del resultado → decremento de presupuesto (budget)", con respaldo (fallback) ante fallos. - Capacidades: Las herramientas son funciones atómicas (
tools/registry.py); las Skills son flujos de orden superior evolutivos (reescritos por el bucle de aprendizaje); Provider/MCP/Plugins aportan modelos y capacidades externas. - Planificación: Cron impulsa tareas programadas, Subagent delega en aislamiento (coste de contexto cero), 6 backends de sandbox aíslan la ejecución.
- Bucle de aprendizaje: Destila la experiencia de la conversación en habilidades y un modelo de usuario — lo que lo hace "mejor cuanto más se usa".
De arriba a abajo: la firma representativa de cada capa
La forma más rápida de entender la estratificación de Hermes es mirar qué «firma» expone cada capa en el código.
Capa UI → forwarder hacia el bucle principal. Todas las shells llaman a AIAgent.run_conversation, pero este es un mero reenviador (run_agent.py:6350):
def run_conversation(self, user_message, system_message=None,
conversation_history=None, ..., moa_config=None) -> Dict[str, Any]:
"""Forwarder — see ``agent.conversation_loop.run_conversation``."""
from agent.conversation_loop import run_conversation
token = set_conversation_context(self._conversation_root_id())
with scoped_runtime_main({}):
return run_conversation(self, user_message, ...)El ID de conversación se propaga por un ContextVar; así, todas las llamadas LLM dentro del bucle principal (compresión (compression), visión, slot MoA, fork de revisión en segundo plano) quedan etiquetadas automáticamente.
Gateway → el coordinador de adaptadores de plataforma. GatewayRunner mezcla capacidades de autorización/kanban/slash vía mixins (gateway/run.py:22392); la entrada start_gateway(replace=True) mata primero la instancia vieja — una trampilla para los reinicios de systemd cuando el proceso anterior no terminó limpio:
class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, GatewaySlashCommandsMixin):
"""Main gateway controller. Manages the lifecycle of all platform adapters
and routes messages to/from the agent."""Bucle principal → donde de verdad se trabaja. run_conversation en agent/conversation_loop.py orquesta «llamada al modelo → llamada a herramienta → devolución del resultado → decremento de presupuesto», con fallback ante fallos; la firma está arriba.
Capacidades → registro (registry) de herramientas. Cada tools/*.py llama a registry.register al importarse el módulo (tools/registry.py:365); override=True es lo único que permite a un plugin sobrescribir una herramienta interna:
def register(self, name, toolset, schema, handler, check_fn=None,
requires_env=None, is_async=False, description="", emoji="",
max_result_size_chars=None, dynamic_schema_overrides=None, override=False):
"""Register a tool. Called at module-import time by each tool file.
``override=True`` is an explicit opt-in for plugins ... Without it,
registrations that would shadow an existing tool are rejected."""Un plugin que quiera reemplazar una herramienta interna tiene que pedir override=True explícito y pasar por la config allow_tool_override — evita que un plugin reemplace silenciosamente la herramienta browser. Las colisiones entre herramientas MCP con el mismo nombre van por un canal aparte.
Planificación → el tick del cron. tick (cron/scheduler.py:3888) es una única pasada de escaneo protegida por un file lock; el gateway la invoca desde un hilo en segundo plano cada 60 segundos. El parámetro adapters permite que las tareas cron reutilicen las conexiones de plataforma que el gateway ya estableció, sin reconectar por su cuenta.
Bucle de aprendizaje → agregación en el grafo de aprendizaje. build_learning_graph (agent/learning_graph.py:248-328) reúne las habilidades y memorias aprendidas (no las integradas) en nodos y aristas. Primero llama a build_skill_nodes(_skill_roots()) para escanear tanto la raíz base como la del profile, y luego filtra las habilidades con source != "base" y created_by == "agent" o use_count > 0 como «aprendidas» — las habilidades integradas no entran al grafo, para que el ruido no tape la señal de evolución.
Motivo de las capas
¿Por qué esta segmentación? En una frase: que las capas que cambian no contaminen las que no cambian.
- La capa UI cambia de shell a menudo (TUI/Web/protocolos de IDE tienen ritmos distintos); no puede obligar a tocar el negocio cada vez que se cambia de shell. Por eso la shell solo cuelga callbacks (
step_callback/event_callback) y no entra dentro derun_conversation. - En el gateway, añadir plataformas es lo habitual (QQbot, WeChat enterprise, nuevos estilos de Discord), pero el bucle principal no debería enterarse de la plataforma. Toda la lógica de plataforma vive en adaptadores; el gateway solo desacopla el mensaje de la llamada al agent.
- La capa de capacidades son «átomos», el bucle de aprendizaje es «evolución» — los átomos no pueden ser contaminados por la ruta evolutiva (si un bug en la revisión en segundo plano corrompiera una herramienta, el bucle principal caería con ella). Por eso
learning_mutationstocaskills/y notools/. - La planificación es transversal: cron y subagentes «disparan el bucle principal desde fuera del bucle principal», comparten
run_conversationpero no tienen estado propio del agent.
Lecturas erróneas
- «Hermes es un framework de agentes» — no. Es una implementación concreta de un agent de escritorio; el bucle principal, el conjunto de herramientas y el directorio de habilidades son los que Hermes trae de fábrica. Las capacidades de framework (MCP, Plugin, abstracción de Provider) son los bordes que expone para poder extenderse, no su esencia.
- «El bucle de aprendizaje optimiza automáticamente el prompt del modelo» — no. Solo modifica los
SKILL.mddeskills/y las memorias bajo~/.hermes/skills/; no toca las plantillas de system prompt. Las plantillas son archivos fijos del repo; cambiarlas requiere release. - «Subagent es distribución (dispatch)» — no. Subagent hace fork del agent dentro del mismo proceso (comparte runtime); el sandbox es aislamiento de ejecución (el código corre en Docker/SSH/Modal/Daytona), no distribución del estado del agent.
- «Gateway = entrada de plataformas de mensajería» — solo a medias. El gateway también gestiona estado de sesión (
session/), deduplicación de entrega (delivery_ledger), distribución de streams (stream_*), slash commands, kanban watchers — todo eso es responsabilidad «entre la plataforma de mensajería y el agent», no solo entrada.
Orden de lectura sugerido
- Inicio y entrada — cómo un comando se convierte en un agente en ejecución
- Bucle principal — qué ocurre al llegar un mensaje
- Capacidades — la diferencia entre herramientas y habilidades
- Gateway — cómo funciona la entrega multiplataforma
- Planificación — automatización y aislamiento
- Bucle de aprendizaje — el mecanismo de automejora