Punto de entrada y flujo de arranque
Responsabilidad
Convertir «un comando hermes / un mensaje del gateway» en «un agent corriendo en memoria». La capa de entrada se mantiene deliberadamente fina: run_agent.py solo reenvía y ensambla el CLI; la inyección de dependencias real ocurre en agent_init.py. Así, gateway, TUI, CLI, ACP y cron comparten la misma ruta de inicialización.
Motivo de diseño
La capa de entrada es fina y el ensamblaje está centralizado para que todas las rutas de invocación aprendan un solo camino. Si AIAgent.__init__ resolviera por su cuenta el provider, fusionara extra_body y calculara el umbral de compresión (compression), entonces gateway, TUI y cron tendrían que replicar esa lógica cada uno; tocar una cosa exigiría tocar varias. Al reducir __init__ a un mero forwarder y concentrar las decisiones reales en init_agent, añadir un nuevo frontend (p. ej. un daemon MCP futuro) se reduce a construir un AIAgent y obtener toda la capacidad, sin conocer el ensamblaje interno.
hermes_bootstrap.py va al principio por la misma razón: la corrección de UTF-8 en Windows es «cuanto antes, mejor»; si se espera a que un subproceso se haya derivado ya es tarde para remediarlo. Por eso es un módulo aparte que todos los puntos de entrada importan en su primera línea.
Archivos clave
gateway main:22965— entradamain()del proceso gatewayclase AIAgent:400-497— clase de entrada ligera;__init__solo guarda parámetrosAIAgent.__init__ reenvía:497-560— el comentario dice literalmente "Forwarder — see agent.agent_init.init_agent"método run_conversation:6350— método de instanciaAIAgent.run_conversation, que llama alrun_conversation a nivel de módulo:588CLI main:6434— entrada del CLIinit_agent:276-400— función principal de inyección de dependencias_resolve_compression_threshold:93-127— ejemplo de resolución de configuración en el arranquehermes_bootstrap.py— guía de entorno/rutas más temprana (raíz del repo)
Flujo de datos
- El usuario ejecuta
hermes(CLI) o el gateway arranca conmain()(run_agent.py:22965). - El proceso pasa por
hermes_bootstrap.pypara ajustar rutas/entorno, y entra alCLI main:6434o al bucle del gateway. - Se construye una instancia de
AIAgent:400; su__init__reenvía ainit_agent:276. init_agentresuelve provider, toolsets, umbral de compresión,extra_bodyde provider personalizado, etc., y ensambla un objeto agent ejecutable.- El gateway, dentro de
GatewayRunnerengateway/run.py, mantiene ese agent y le pasa cada mensaje entrante arun_conversation:588.
El cuerpo de AIAgent.__init__ es literalmente la palabra «forwarder» — todos los parámetros de construcción se pasan intactos a init_agent, sin ningún análisis propio:
"""Forwarder — see ``agent.agent_init.init_agent``."""
from agent.agent_init import init_agent
init_agent(
self,
base_url=base_url,
api_key=api_key,
provider=provider,
api_mode=api_mode,
...
)Y hermes_bootstrap.py, lo primero que toca el proceso, solo hace el arreglo de codificación en Windows; en POSIX pasa de largo. Es corto:
if not _IS_WINDOWS:
return False
if _bootstrap_applied:
return False
os.environ.setdefault("PYTHONUTF8", "1")
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
for stream_name in ("stdout", "stderr"):
stream = getattr(sys, stream_name, None)
...
reconfigure(encoding="utf-8", errors="replace")Que sea setdefault en lugar de = es deliberado: el usuario puede poner PYTHONUTF8=0 en el entorno de antemano para hacer opt-out y no se sobrescribe.
Límites y fallos
- Entry invocada directamente: alguien se salta
hermes_bootstrapy arranca el proceso en Windows con unpython -m gateway.rundirecto; stdio sigue siendo cp1252 y una salida no ASCII levantaUnicodeEncodeError. La posición del bootstrap es convención, no imposición; la documentación exige que todos los puntos de entrada lo importen en su primera línea. AIAgent.__init__lanza antes deinit_agent: como el cuerpo de__init__es solo import + llamada, cualquier fallo viene de dentro deinit_agenty la traza se ve «desplazada». Al depurar, la última frame del traceback esinit_agent; que el comentario de__init__diga «forwarder» no significa que el forwarder tenga el bug.- El import en
init_agentes perezoso:from agent.agent_init import init_agentestá dentro del cuerpo de__init__y no en la cima del módulo, para evitar import circular —agent_inita su vez importarun_agent. El coste es un import extra (~milisegundos) la primera vez que se construye unAIAgent; después se cachea en el módulo. reconfiguredehermes_bootstrappuede fallar: si stdout fue reemplazado por unBytesIO(escenarios de test) o ya está cerrado,reconfigurelanzaOSError/ValueError; el código lo atrapa y sigue. La vía del entorno sigue funcionando, así que los subprocesos no se ven afectados.
Resumen
La capa de entrada es «reenviar + ensamblar», sin lógica de negocio. AIAgent es una cáscara fina; init_agent es el centro de ensamblaje. Tras entender esta cadena, el bucle principal del agente responde a «qué hace cuando recibe un mensaje».