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. · Privacy · Terms · About