Skip to content

Entry & Boot Flow

源码版本v2026.7.20

Responsibility

Turn "a hermes command / a gateway message" into "an agent running in memory". The entry layer is deliberately thin — run_agent.py only forwards and assembles the CLI; the real dependency injection lives in agent_init.py. This way the gateway, TUI, CLI, ACP, and cron all share the same initialization path.

Design Motivation

The entry layer is kept thin and assembly centralized so that every caller path only has to learn one thing. If AIAgent.__init__ itself parsed providers, merged extra_body, and computed compression thresholds, then the gateway, TUI, and cron would each have to replicate that logic — one change would need to land in many places. By reducing __init__ to a forwarder and concentrating real decisions in the single init_agent function, a new frontend (say a future MCP daemon) just constructs AIAgent to get the full feature set without knowing any assembly details.

hermes_bootstrap.py is placed first for the same reason: Windows UTF-8 is an "the earlier the better" environment fix. Once a child process is forked it's too late to patch, so it stands alone as a module that every entry point imports on its very first line.

Key Files

Data Flow

  1. The user runs hermes (CLI) or the gateway main() starts (run_agent.py:22965).
  2. The process goes through hermes_bootstrap.py for path/environment fixes, then enters CLI main:6434 or the gateway loop.
  3. An AIAgent:400 instance is constructed; its __init__ forwards to init_agent:276.
  4. init_agent resolves providers, toolsets, compression thresholds, custom provider extra_body, etc., assembling a runnable agent object.
  5. The gateway holds this agent inside GatewayRunner in gateway/run.py and feeds each inbound message to run_conversation:588.

The body of AIAgent.__init__ is the literal embodiment of "forwarder" — every constructor argument is passed through to init_agent unchanged, with no parsing of its own:

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,
    ...
)

And hermes_bootstrap.py, the first thing the process touches, only does encoding fallback on Windows — POSIX passes straight through. The logic is short:

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")

setdefault instead of = is deliberate: a user can pre-set PYTHONUTF8=0 in the environment to opt out, and it won't be overwritten.

Boundaries and Failure

  • Entry constructed directly: If someone bypasses hermes_bootstrap and runs python -m gateway.run directly on Windows, stdio is still cp1252 and non-ASCII output raises UnicodeEncodeError. The bootstrap's position is convention, not enforcement — docs require every entry point to import it on the first line.
  • AIAgent.__init__ throws before init_agent: Since __init__'s body is just an import + a call, any failure originates inside init_agent, but the stack frame looks "misaligned". When debugging, the last frame on the traceback is init_agent; don't be misled by the __init__ comment into thinking "the forwarder itself has a bug".
  • init_agent import is lazy: from agent.agent_init import init_agent lives inside __init__'s body rather than at module top, to avoid a circular import — agent_init in turn imports run_agent. The cost is one extra import (~ms-level) the first time an AIAgent is constructed; after that the module cache kicks in.
  • hermes_bootstrap's reconfigure can fail: If stdout has been swapped for a BytesIO (test scenarios) or is already closed, reconfigure raises OSError/ValueError; the code catches and silently skips. The environment-variable path still applies, so child processes are unaffected.

Summary

The entry layer is "forward + assemble" and holds no business logic. AIAgent is a thin shell; init_agent is the assembly center. Once you understand this chain, the Agent Main Loop is just "what happens when a message is fed in".

Unofficial community learning site. Content based on the MIT-licensed NousResearch/hermes-agent source.