Skip to content

入口與啟動流程

源码版本v2026.7.20

職責

把「一條 hermes 命令 / 一個網關 (gateway) 訊息」變成「記憶體裡跑起來的 agent」。入口層刻意做得很薄——run_agent.py 只負責轉發和 CLI 裝配,真正的依賴注入在 agent_init.py。這樣網關、TUI、CLI、ACP、cron 能共用同一套初始化路徑。

設計動機

入口層薄、裝配集中,是為了讓所有呼叫路徑只學一套。如果 AIAgent.__init__ 自己解析 provider、合併 extra_body、算壓縮 (compression) 閾值,那網關 (gateway)、TUI、cron 想跑 agent 就得各自複刻這套邏輯,改一處要改多處。把 __init__ 退化成轉發器,真正決策收斂在 init_agent 一個函式裡,新接入一個前端(比如未來的 MCP 守護程式)只要建構 AIAgent 就拿到完整能力,不必知道裝配細節。

hermes_bootstrap.py 放在最前面也是同樣的思路:Windows UTF-8 是「越早越好」的環境修正,等到子程序派生以後再補救就來不及,所以它獨立成模組,所有入口點第一行就 import 它。

關鍵檔案

資料流

  1. 使用者執行 hermes(CLI)或網關 main() 啟動(run_agent.py:22965)。
  2. 行程經 hermes_bootstrap.py 做路徑/環境修正,進入 CLI main:6434 或網關迴圈。
  3. 建構 AIAgent:400 實例,其 __init__ 轉發到 init_agent:276
  4. init_agent 解析 provider、toolsets、壓縮閾值、自訂 provider extra_body 等,裝配出可執行的 agent 物件。
  5. 網關在 gateway/run.pyGatewayRunner 裡持有該 agent,把每條入站訊息餵給 run_conversation:588

AIAgent.__init__ 的函式體就是「轉發器」三個字的字面意思——建構參數一個不漏地透傳給 init_agent,自己不做任何解析:

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

而最早接觸程序的 hermes_bootstrap.py 只在 Windows 上做編碼兜底,POSIX 直接放行,邏輯很短:

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 而不是 = 是故意的:使用者可以提前把 PYTHONUTF8=0 放進環境變數來主動 opt-out,不會被覆蓋。

邊界與失敗

  • 入口被直接建構:有人繞過 hermes_bootstrap,直接 python -m gateway.run 在 Windows 上起程序,stdio 仍是 cp1252,非 ASCII 輸出會 UnicodeEncodeError。bootstrap 的位置是約定,不是強制,文件要求所有入口點第一行 import。
  • AIAgent.__init__init_agent 之前拋例外:由於 __init__ 函式體只是 import + 呼叫,任何失敗都來自 init_agent 內部,堆疊幀會顯得「錯位」。除錯時看 traceback 的最後一幀是 init_agent,不要被 __init__ 的註解誤導成「轉發器本身有 bug」。
  • init_agent 的匯入是惰性的:from agent.agent_init import init_agent 寫在 __init__ 函式體裡而不是模組頂部,是為了避免循環匯入——agent_init 反過來要 import run_agent。代價是首次建構 AIAgent 多一次 import 開銷(~毫秒級),之後走模組快取。
  • hermes_bootstrap 的 reconfigure 可能失敗:如果 stdout 被替換成 BytesIO(測試場景)或已經關閉,reconfigure 會拋 OSError/ValueError,程式碼 catch 後靜默跳過。環境變數那條路徑仍然生效,所以子程序不受影響。

小結

入口層是「轉發 + 裝配」,不持業務邏輯。AIAgent 是瘦殼,init_agent 才是裝配中心。理解這條鏈路後,後面的Agent 主迴圈就是「被餵訊息後做什麼」。

非官方社群學習站,內容以 MIT 授權的 NousResearch/hermes-agent 原始碼為依據。