Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

仓库地图:五层架构速览

本章核心源码hermes_cli/main.pyrun_agent.pymodel_tools.pyhermes_state.pygateway/run.py

定位:本章建立源码空间感,将 hermes-agent 仓库按五层划分,标注每层关键文件与职责。 前置依赖:无。适用场景:初次接触 Hermes 源码,需要快速建立全局地图。

为什么需要一张地图

Hermes Agent 的代码库不小:十余个顶层 Python 文件、二十多个 agent 核心模块、数十个工具实现(含子目录)、17 个 Gateway 平台类型、数十个 CLI 模块、400+ 个测试文件。如果按目录树从上到下浏览,你会迷失在细节里。

但这些代码不是杂乱堆砌的。它们按照一个清晰的分层模型组织:入口层、编排层、能力层、状态层、平台层。每一层有明确的职责边界,层与层之间的依赖方向是单向的——上层依赖下层,下层不感知上层的存在。

理解这张地图,后续每一章你都知道自己在哪个位置。

五层架构总览

graph TB
    subgraph "入口层 — 用户如何触达 Agent"
        CLI["hermes_cli/main.py<br/>CLI 命令入口"]
        TUI["cli.py (8736行)<br/>交互式 TUI"]
        GW["gateway/run.py<br/>消息平台网关"]
        BATCH["batch_runner.py<br/>批量轨迹生成"]
        CRON["cron/scheduler.py<br/>定时调度"]
    end

    subgraph "编排层 — Agent 的大脑"
        AGENT["run_agent.py (9431行)<br/>AIAgent 核心编排器"]
        TOOLS["model_tools.py<br/>工具发现与调度"]
        AGENT_MOD["agent/ (25个模块)<br/>编排支撑"]
    end

    subgraph "能力层 — Agent 能做什么"
        REGISTRY["tools/registry.py<br/>工具注册表"]
        TOOL_IMPL["tools/ (69个 .py, 含子目录)<br/>工具实现"]
        SKILLS["skills/ (26类)<br/>过程性知识"]
        MCP["tools/mcp_tool.py<br/>MCP 外部能力"]
        PLUGINS["plugins/<br/>插件系统"]
    end

    subgraph "状态层 — Agent 记住什么"
        STATE["hermes_state.py<br/>SessionDB (SQLite)"]
        MEM_MGR["agent/memory_manager.py<br/>记忆编排"]
        MEM_PROV["agent/memory_provider.py<br/>记忆抽象"]
        COMPRESS["agent/context_compressor.py<br/>上下文压缩"]
    end

    subgraph "平台层 — Agent 如何到达用户"
        PLATFORMS["gateway/platforms/ (17个)<br/>消息平台适配"]
        SESSION["gateway/session.py<br/>会话管理"]
        ACP["acp_adapter/<br/>Agent Client Protocol"]
    end

    CLI --> AGENT
    TUI --> AGENT
    GW --> AGENT
    BATCH --> AGENT
    CRON --> AGENT

    AGENT --> TOOLS
    AGENT --> AGENT_MOD
    TOOLS --> REGISTRY
    REGISTRY --> TOOL_IMPL
    AGENT --> MEM_MGR
    AGENT --> COMPRESS

    MEM_MGR --> MEM_PROV
    MEM_MGR --> STATE

    GW --> PLATFORMS
    GW --> SESSION

    style AGENT fill:#f96,stroke:#333,stroke-width:3px

图中高亮的 run_agent.py(9431 行)是整个系统的心脏,几乎所有入口最终都调用到它。

设计选择:为什么是五层而不是其他分法

在分析 Hermes 的代码组织之前,值得先说明这个五层模型的来源和选择理由。

Hermes 没有在代码中显式声明分层——不存在一个 layers.py 或分层配置文件。五层划分是从代码的依赖方向和职责边界中自然浮现的:

  • 为什么不是三层?(表现层/业务层/数据层)因为 Hermes 的"表现层"实际上包含了两个截然不同的关注点:CLI 交互和消息平台适配。它们的代码量、复杂度和抽象模式完全不同,硬塞进一层会掩盖结构。
  • 为什么不是洋葱架构? Hermes 不是一个 Web 服务——它没有 HTTP 请求/响应的生命周期。它的核心循环是 模型调用 → 工具执行 → 模型调用,这个循环不适合用 middleware/handler 链来描述。
  • 为什么要区分编排层和能力层? 因为编排层(run_agent.py)控制的是"什么时候做什么",能力层(tools/)控制的是"具体怎么做"。两者的变化频率不同:添加一个新工具不需要改动编排逻辑,改变对话策略不需要改动工具实现。

这个分层模型最大的价值是让你快速判断"某个问题属于哪一层"——定位到层之后,搜索范围立即缩小到几个文件。

入口层:用户如何触达 Agent

入口层回答一个问题:用户的消息从哪里进入系统?

Hermes 有五个入口,它们共享同一个编排层,但各自处理不同的交互模式:

入口文件行数职责
CLI 命令hermes_cli/main.pyhermes modelhermes toolshermes config 等子命令入口
交互式 TUIcli.py8736prompt_toolkit 驱动的终端交互界面,支持多行编辑、流式输出、slash command
消息网关gateway/run.py同时连接 Telegram、Discord、Slack 等平台,接收消息并路由到 Agent
批量运行batch_runner.py批量生成对话轨迹,用于 RL 训练和模型评估
定时调度cron/scheduler.pycroniter 驱动的定时任务,支持投递到消息平台

关键设计:这五个入口不是五个不同的 agent,而是同一个 AIAgent 的五种触发方式。它们通过不同的回调(callback)适配各自的 IO 模式,但核心编排逻辑完全共享。这就是为什么 cli.pygateway/run.py 各有近万行代码——它们不是薄壳,而是各自领域的完整交互层。

详见第 13 章(CLI)、第 14 章(Gateway)、第 15 章(Cron)。

编排层:Agent 的大脑

编排层包含两个核心文件和一个支撑模块目录,它们构成整个系统的重心。

run_agent.py — AIAgent 核心编排器

run_agent.py 是全项目最大的单文件(9431 行),包含 AIAgent 类(定义在第 416 行)。它负责:

  • 初始化决策链(config → memory → prompt → model client → session)
  • run_conversation() 大循环:构建消息 → 调用模型 → 解析工具调用 → 执行工具 → 循环
  • Iteration budget 管理(防止 agent 无限循环)
  • Fallback provider 切换(主模型不可用时自动降级)
  • 11 个回调接口(tool_progress_callbackstream_delta_callbackclarify_callback 等,让同一个内核适配 CLI、Gateway、Batch 三种场景)
  • Token 使用量追踪与成本估算

详见第 4 章。

model_tools.py — 工具发现与调度

model_tools.py 是 agent 与工具系统之间的桥梁。它的核心职责:

  • _discover_tools():在启动时导入所有工具模块,触发自注册(model_tools.py:132
  • get_tool_definitions():根据 enabled/disabled toolset 过滤工具 schema(model_tools.py:234
  • handle_function_call():接收模型返回的工具调用,路由到正确的 handler(model_tools.py:459
  • _run_async():async→sync 桥接,让异步工具在同步编排器中运行(model_tools.py:81

详见第 6 章。

agent/ — 编排支撑模块

agent/ 目录是编排层的"工具箱"——25 个模块提供 prompt 构建、记忆管理、模型适配、重试策略等横切关注点。这些模块本身不驱动对话循环,而是被 AIAgent 在循环的不同阶段调用。

agent/
├── prompt_builder.py         # System prompt 多块拼装
├── prompt_caching.py         # Prompt cache 优化
├── memory_manager.py         # 记忆编排器
├── memory_provider.py        # MemoryProvider ABC (17 个方法)
├── builtin_memory_provider.py  # 内置记忆(MEMORY.md + USER.md)
├── context_compressor.py     # 上下文压缩
├── context_engine.py         # 可插拔上下文引擎 ABC
├── model_metadata.py         # 模型能力探测与缓存
├── usage_pricing.py          # Token 成本追踪
├── retry_utils.py            # 重试与退避策略
├── skill_utils.py            # 技能发现与解析
├── smart_model_routing.py    # 智能模型路由
├── anthropic_adapter.py      # Anthropic API 适配
├── credential_pool.py        # API key 池管理
├── context_references.py     # 上下文引用追踪
├── redact.py                 # 敏感信息脱敏
└── ... (共 25 个文件)

之所以把 agent/ 放在编排层而非状态层,是因为它的模块绝大多数服务于编排逻辑——prompt 构建、模型路由、重试策略都是"如何驱动对话"的一部分。其中 memory_manager.pymemory_provider.pycontext_compressor.py 虽然处理状态,但它们的调用者是编排层的 AIAgent,从依赖方向看属于编排层的组成部分。

各模块的深入分析分散在第 4-12 章和第 18-19 章。

能力层:Agent 能做什么

能力层是 Hermes 最"宽"的一层,包含三个子系统:工具、技能、插件。

工具系统 — tools/

tools/
├── registry.py          # ToolRegistry 单例 + ToolEntry 元数据
├── terminal_tool.py     # 命令执行(6 种后端)
├── file_tools.py        # 文件读写
├── browser_tool.py      # 浏览器自动化
├── delegate_tool.py     # 子代理委托
├── mcp_tool.py          # MCP 外部能力接入
├── skills_tool.py       # 技能检索与管理
├── code_execution_tool.py  # Python/JS 代码执行
├── session_search_tool.py  # 跨会话搜索
├── process_registry.py  # 后台进程追踪
├── browser_providers/   # 浏览器后端(Browserbase, Firecrawl 等)
└── ... (共数十个 .py 文件,含子目录)

工具系统的核心抽象是 ToolRegistrytools/registry.py:48)和 ToolEntrytools/registry.py:24)。每个工具在模块加载时调用 registry.register() 自注册,但对仓库内置工具来说,仍然需要把模块接入 _discover_tools() 并纳入 toolsets.py,这样它才会真正暴露给模型。

数十个工具相关文件被分组为若干 toolset(如 terminalwebmemory),通过 toolsets.py 管理可用性。用户可以按 toolset 粒度启用/禁用工具。

详见第 6、7 章。

技能系统 — skills/

skills/
├── apple/               # macOS 自动化
├── autonomous-ai-agents/  # 自主代理模式
├── creative/            # 创意写作
├── data-science/        # 数据分析
├── devops/              # 运维自动化
├── diagramming/         # 图表生成
├── domain/              # 领域知识
└── ... (共 26 个类别)

技能是 Hermes "self-improving" 理念的核心。它们不是静态文档,而是 Markdown + YAML frontmatter 格式的过程性知识,agent 可以在工作中自主创建和改进。技能的发现、索引和条件加载由 agent/skill_utils.py 处理。

详见第 8 章。

插件系统 — plugins/

plugins/
└── memory/
    ├── honcho/          # Plastic Labs 用户建模
    ├── hindsight/       # 时序滑动窗口记忆
    ├── mem0/            # 向量数据库后端
    ├── holographic/     # 压缩全息存储
    ├── openviking/      # 语义嵌入记忆
    ├── retaindb/        # 保留策略记忆
    ├── supermemory/     # 多源聚合记忆
    └── byterover/       # 替代向量存储

当前代码库里最成熟的插件生态确实集中在 memory provider,提供 8 个可选的外部记忆后端。每个 provider 实现 MemoryProvider ABC(agent/memory_provider.py,定义了 17 个方法);在运行时,外部 provider 由 MemoryManager 编排,而内置记忆仍主要走 MemoryStore 的直接接线。

详见第 11 章。

状态层:Agent 记住什么

状态层的核心是 hermes_state.py,它实现了基于 SQLite 的会话持久化。

组件文件职责
SessionDBhermes_state.py (1304 行)SQLite 持久化:sessions 表 + messages 表 + messages_fts 虚拟表(FTS5 全文检索)
WAL 并发安全hermes_state.py:164-214BEGIN IMMEDIATE 事务 + 15 次 jitter retry,支持 Gateway 多平台并发写入
FTS5 检索hermes_state.py跨会话全文搜索,支撑 session_search 工具和 LLM 摘要回忆

SessionDB 类(hermes_state.py:115)是一个精简的 SQLite 封装,不使用 ORM。这个选择是有意的:Hermes 需要在 $5 VPS 上运行,SQLite 的零配置和单文件部署比 PostgreSQL 更适合这个场景。WAL 模式让 Gateway 的多个平台可以并发读取,而写入通过 application-level jitter retry 解决锁竞争。

详见第 10 章。

平台层:Agent 如何到达用户

gateway/platforms/ — 17 个平台类型

gateway/platforms/
├── base.py              # BasePlatformAdapter ABC
├── telegram.py          # Telegram(webhook + polling)
├── telegram_network.py  # Telegram 网络传输层(telegram.py 的辅助模块)
├── discord.py           # Discord(多 guild、线程、反应)
├── slack.py             # Slack(多 workspace、线程)
├── whatsapp.py          # WhatsApp(Twilio 桥接)
├── signal.py            # Signal(signal-cli 桥接)
├── matrix.py            # Matrix(E2E、spaces)
├── email.py             # Email(IMAP/SMTP)
├── feishu.py            # 飞书
├── wecom.py             # 企业微信
├── weixin.py            # 个人微信
├── dingtalk.py          # 钉钉
├── api_server.py        # REST API 端点
├── homeassistant.py     # Home Assistant IoT
├── mattermost.py        # Mattermost
├── sms.py               # SMS(Twilio)
└── webhook.py           # 自定义 Webhook

所有平台适配器继承 BasePlatformAdapter ABC(gateway/platforms/base.py:470),实现 connect()disconnect()send() 等核心方法。Gateway 进程可以同时连接多个平台,每个平台的会话通过 SessionSource / SessionContextgateway/session.py)独立管理。

详见第 14 章。

acp_adapter/ — Agent Client Protocol

acp_adapter/
├── entry.py       # 入口模块
├── server.py      # ACP HTTP 服务器
├── session.py     # ACP 会话管理
├── tools.py       # ACP 工具暴露
├── auth.py        # 认证
├── permissions.py # 权限控制
└── events.py      # SSE 事件流

ACP Adapter 让外部系统通过 Agent Client Protocol 标准接口与 Hermes 交互。目前已实现核心的 HTTP 服务、会话管理、SSE 事件流、认证和权限模块。

详见第 23 章。

源码走读:从入口到编排的调用链

光看目录结构还是抽象的。下面用一个最简单的例子——用户在 CLI 中发送一条消息——追踪代码如何从入口层流入编排层。

第一步:CLI 入口

用户运行 hermes 命令,hermes_cli/main.pymain() 函数被调用。对于交互式会话,它转发到 cli.pyrun_cli() 函数:

# cli.py 中的核心调用(简化)
agent = AIAgent(
    config=config,
    session_db=session_db,
    # ... 回调适配
    stream_delta_callback=on_stream_delta,
    clarify_callback=on_clarify,
)
response = agent.run_conversation(user_message)

关键观察:cli.py 构造 AIAgent 时注入了 TUI 专用的回调函数(如 stream_delta_callback 用于流式渲染、clarify_callback 用于交互式确认)。这些回调是入口层与编排层之间的适配接口

第二步:Gateway 入口(对比)

Gateway 走的是另一条路径,但最终也构造同一个 AIAgent

# gateway/run.py 中的核心调用(简化)
agent = AIAgent(
    config=config,
    session_db=session_db,
    # ... 不同的回调适配
    stream_delta_callback=on_platform_stream,
    status_callback=on_platform_status,
)
response = agent.run_conversation(platform_message)

相同的 AIAgent,不同的回调——这就是"共享编排层 + 可插拔入口"的工程实现。第 3 章会完整追踪这条调用链从 run_conversation() 到模型调用再到工具执行的全过程。

第三步:编排层分发

进入 AIAgent.run_conversation() 后,编排层通过 model_tools.py 将工具调用分发到能力层:

# model_tools.py:459(简化)
def handle_function_call(name, arguments, ...):
    entry = registry.get(name)  # 从 ToolRegistry 查找
    if entry.is_async:
        return _run_async(entry.handler(**arguments))
    return entry.handler(**arguments)

registry.get(name) 查找的是能力层的 ToolRegistryentry.handler 指向具体工具的实现函数。编排层不知道工具的具体逻辑,只负责调用和收集结果。

第 3 章将完整展开这条调用链。

测试目录:架构的另一面镜子

tests/
├── run_agent/     → 编排层(AIAgent 大循环)
├── tools/         → 能力层(工具系统)
├── skills/        → 能力层(技能系统)
├── agent/         → 编排支撑(agent/ 模块)
├── gateway/       → 平台层(Gateway + 平台适配)
├── hermes_cli/    → 入口层(CLI 命令)
├── cli/           → 入口层(TUI 交互)
├── cron/          → 入口层(定时调度)
├── acp/           → 平台层(ACP 适配)
├── plugins/       → 能力层(插件系统)
├── honcho_plugin/ → 能力层(Honcho 记忆插件)
├── environments/  → 研究(RL 环境)
├── e2e/           → 端到端集成测试
├── integration/   → 集成测试
└── fakes/         → 测试用 mock 对象

测试目录的结构几乎是生产代码的镜像。如果你想了解某个子系统的行为,往往从对应的测试目录入手比直接读源码更高效——测试用例展示了"这个模块应该怎么被使用"。

400+ 个测试文件共享一个关键基础设施:tests/conftest.py 中的 _isolate_hermes_home fixture(标记为 autouse=True)。它在每个测试运行前将 HERMES_HOME 重定向到临时目录,确保测试之间完全隔离——不会读到用户的真实配置,也不会写入用户的 ~/.hermes。这个设计让 400+ 个测试可以安全地并行运行。

详见第 22 章。

支撑文件:不在五层之内但不可忽略

文件职责
toolsets.py工具分组定义,控制 toolset 可用性
toolset_distributions.pyToolset 分发配置
hermes_constants.py全局常量
hermes_logging.py日志配置
hermes_time.py时间工具函数
utils.py通用工具函数
mcp_serve.pyMCP 服务器模式(Hermes 作为 MCP server 暴露能力)
mini_swe_runner.py小型 SWE benchmark 运行器
rl_cli.pyRL 训练 CLI
trajectory_compressor.py轨迹压缩(训练数据处理)

设计启示

Hermes 的五层分层服务于一个核心目标:让同一个 agent 内核能同时服务于五种不同的触发方式。这个设计带来三个直接收益:

  1. Bug fix 自动传播:编排层的修复(如 retry 逻辑改进)自动惠及 CLI、Gateway、Cron 所有入口
  2. 平台扩展解耦:新增消息平台只需实现 BasePlatformAdapter,不需要改动编排层或能力层
  3. 测试分层独立:每一层可以独立测试,不依赖其他层的真实实现

第 3 章将从编排层的 run_conversation() 入手,展开一次完整请求在系统中的旅程。


设计赌注回扣:本章呈现的五层架构直接服务于 Run Anywhere 赌注——正因为编排层与平台层彻底分离,Hermes 才能同时跑在终端、Telegram、Discord 和定时任务中。


版本演化说明

本章核心分析基于 Hermes Agent v0.8.0(2026 年 4 月)。 目录结构和文件数量可能随版本迭代变化,但五层架构模型和层间依赖方向在可预见的未来不会改变。