Skip to content

架構總覽

源码版本v2026.7.20

Hermes Agent 是 Nous Research 開源(MIT)的桌面端 AI agent,核心定位是「與你一起成長的持久 agent」:一則訊息進來,從 Telegram/Discord/Slack 等任意界面接入,經核心迴圈呼叫模型+工具 (tool) 完成任務,並把經驗沉澱進記憶與技能,供下次複用。

分層視圖

各層一句話

  • 界面層:TUI / CLI / Web / ACP(編輯器協議),只是入口,不持有業務。
  • 網關層 (gateway layer):gateway/run.py 統一接入,各平台是轉接器 (adapter),會話與投遞分離,串流 (stream) 與 hooks 可插拔。
  • 核心迴圈:run_conversation 編排「模型呼叫 → 工具呼叫 → 結果回填 → 預算 (budget) 遞減」,失敗走回退 (fallback)。
  • 能力層:Tool 是原子函式(tools/registry.py 註冊),Skill 是更高階的可演化流程(被學習閉環改寫),Provider/MCP/Plugins 提供模型與外部能力。
  • 排程擴展:Cron 定時驅動、Subagent 隔離派生(零上下文 (context) 成本)、6 種沙箱後端隔離執行。
  • 學習閉環:把會話經驗沉澱成技能與使用者模型,形成「越用越好用」的正向循環。

由上而下:每一層的代表性簽名

要理解 Hermes 的分層,最快的辦法是看每一層在程式碼裡露出什麼「簽名」。

界面層 → 核心迴圈的轉發器。所有殼都呼叫 AIAgent.run_conversation,但它只是個轉發(run_agent.py:6350):

python
def run_conversation(self, user_message, system_message=None,
                     conversation_history=None, ..., moa_config=None) -> Dict[str, Any]:
    """Forwarder — see ``agent.conversation_loop.run_conversation``."""
    from agent.conversation_loop import run_conversation
    token = set_conversation_context(self._conversation_root_id())
    with scoped_runtime_main({}):
        return run_conversation(self, user_message, ...)

會話 ID 透過 ContextVar 推下去——主迴圈內的所有 LLM 呼叫(壓縮 (compression)、視覺、MoA slot、背景審視 fork)都自動帶標籤。

網關層 → 平台轉接器的總管GatewayRunner 用 mixin 拼出授權/看板/斜線命令能力(gateway/run.py:22392),入口 start_gateway(replace=True) 會先殺舊實例——為 systemd 重啟時舊程序沒退乾淨留的後門:

python
class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, GatewaySlashCommandsMixin):
    """Main gateway controller. Manages the lifecycle of all platform adapters
    and routes messages to/from the agent."""

核心迴圈 → 真正幹活的地方run_conversationagent/conversation_loop.py,編排「模型呼叫 → 工具呼叫 → 結果回填 → 預算遞減」,失敗走回退;簽名見上。

能力層 → 工具註冊。每個 tools/*.py 在模組匯入時呼叫 registry.register(tools/registry.py:365),override=True 才允許插件覆蓋內建工具:

python
def register(self, name, toolset, schema, handler, check_fn=None,
             requires_env=None, is_async=False, description="", emoji="",
             max_result_size_chars=None, dynamic_schema_overrides=None, override=False):
    """Register a tool.  Called at module-import time by each tool file.
    ``override=True`` is an explicit opt-in for plugins ... Without it,
    registrations that would shadow an existing tool are rejected."""

插件想替換內建工具得顯式 override=True 並通過 allow_tool_override 配置——防止插件悄悄把 browser 工具換掉。MCP 工具同名覆蓋走單獨通道。

排程擴展 → cron 的 ticktick(cron/scheduler.py:3888)是檔案鎖保護的單次掃描,gateway 每 60 秒從背景執行緒呼叫一次;adapters 參數讓 cron 任務複用 gateway 已建好的平台連線,不用自己重連。

學習閉環 → 學習圖匯總build_learning_graph(agent/learning_graph.py:248-328)把學到(非內建)的技能和記憶拼成節點/邊。它先呼叫 build_skill_nodes(_skill_roots()) 掃 base 和 profile 兩個根,然後過濾 source != "base"created_by == "agent"use_count > 0 的技能當「學到的」——內建技能不進學習圖,避免雜訊蓋過演化訊號。

分層動機

為什麼這麼分層?一句話:讓能改的層不污染不能改的層

  • 界面層換殼頻繁(TUI/Web/IDE 協議各有各的迭代節奏),不能讓換殼逼著改業務;所以殼只掛回呼(step_callback/event_callback),不進 run_conversation 內部。
  • 網關層接入新平台是常態(QQbot、企業微信、新 Discord 風格),但核心迴圈不應感知平台——所以平台邏輯全是轉接器,網關只把訊息和 agent 呼叫解耦。
  • 能力層是「原子」,學習閉環是「演化」——原子不能被演化路徑污染(否則背景審視一個 bug 把工具改壞了,主迴圈也跟著崩),所以 learning_mutations 改的是 skills/ 而不是 tools/
  • 排程擴展是橫切:Cron 和 Subagent 都是「在主迴圈之外觸發主迴圈」,共享 run_conversation 但不直接持有 agent 狀態。

常見誤讀

  • 「Hermes 是個 agent 框架」——不是。它是一個具體的桌面 agent 實作,核心迴圈、工具集、技能目錄都是 Hermes 自帶的。框架化能力(MCP、Plugin、Provider 抽象)是它為了能擴展而暴露的邊界,不是它的本體。
  • 「學習閉環會自動優化模型提示」——不會。它只改 skills/ 下的 SKILL.md 和 ~/.hermes/skills/ 下的記憶,不改系統提示 (prompt) 模板。提示模板是倉庫裡的固定檔案,改它要靠發版。
  • 「Subagent 是分散式」——不是。Subagent 在同一程序內 fork agent(共享 runtime),沙箱是執行隔離(程式碼跑在 Docker/SSH/Modal/Daytona),不是 agent 狀態的分散式。
  • 「網關層 = 訊息平台接入」——只對了一半。網關還管會話狀態(session/)、投遞去重(delivery_ledger)、串流分發 (dispatch)(stream_*)、斜線命令、kanban watchers——這些都是「訊息平台 + agent 之間」的職責,不只是接入。

閱讀順序建議

  1. 啟動與入口 — 一條命令如何變成執行中的 agent
  2. Agent 主迴圈 — 訊息進來後發生什麼
  3. 能力層 — 工具與技能的區別
  4. 網關層 — 多平台如何投遞
  5. 排程與擴展 — 自動化與隔離
  6. 學習閉環 — 自我進化機制

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