Skip to content

Punto de entrada y flujo de arranque

源码版本v2026.7.20

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

Flujo de datos

  1. El usuario ejecuta hermes (CLI) o el gateway arranca con main() (run_agent.py:22965).
  2. El proceso pasa por hermes_bootstrap.py para ajustar rutas/entorno, y entra al CLI main:6434 o al bucle del gateway.
  3. Se construye una instancia de AIAgent:400; su __init__ reenvía a init_agent:276.
  4. init_agent resuelve provider, toolsets, umbral de compresión, extra_body de provider personalizado, etc., y ensambla un objeto agent ejecutable.
  5. El gateway, dentro de GatewayRunner en gateway/run.py, mantiene ese agent y le pasa cada mensaje entrante a run_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:

python
"""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:

python
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_bootstrap y arranca el proceso en Windows con un python -m gateway.run directo; stdio sigue siendo cp1252 y una salida no ASCII levanta UnicodeEncodeError. 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 de init_agent: como el cuerpo de __init__ es solo import + llamada, cualquier fallo viene de dentro de init_agent y la traza se ve «desplazada». Al depurar, la última frame del traceback es init_agent; que el comentario de __init__ diga «forwarder» no significa que el forwarder tenga el bug.
  • El import en init_agent es perezoso: from agent.agent_init import init_agent está dentro del cuerpo de __init__ y no en la cima del módulo, para evitar import circular — agent_init a su vez importa run_agent. El coste es un import extra (~milisegundos) la primera vez que se construye un AIAgent; después se cachea en el módulo.
  • reconfigure de hermes_bootstrap puede fallar: si stdout fue reemplazado por un BytesIO (escenarios de test) o ya está cerrado, reconfigure lanza OSError/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».

Sitio de aprendizaje comunitario no oficial. Basado en el código fuente de NousResearch/hermes-agent (licencia MIT).