Skip to main content

第1章:架构与入口 —— 一个 Agent 产品为什么要分成这么多运行时

一、先不看文件:Hermes 到底是什么​

一句话:Hermes 是一个把“模型调用”包装成长期可运行工作的 Agent Runtime。

它同时面对三类输入:

  • CLI/TUI 里的同步或交互式输入;
  • Telegram、Discord、Slack、WhatsApp、Signal、Webhook 等平台的异步输入;
  • ACP、API Server、批处理和定时任务产生的程序化输入。

它同时面对三类副作用:调用外部模型、执行本地或远程命令、向外部平台发送消息。于是代码不能只围绕一次函数调用组织,而要围绕状态、恢复、授权、并发和投递组织。

二、五层结构:按“谁拥有哪种状态”来分​

层主要职责关键模块
入口层解析命令、装载配置、建立交互或服务hermes_cli/main.py、cli.py、acp_adapter/entry.py
Agent 层维护模型、消息、工具、上下文和一次运行run_agent.py、agent/
能力层注册工具、加载技能/插件、调用 MCP、委托子 Agenttools/、skills/、plugins/
连接层连接外部平台,做 session 映射、流式投递和路由gateway/
运维层让任务可以远程、定时、批量、可观测地运行cron/、tools/environments/、batch_runner.py

这不是严格的单向分层。比如 Gateway 会创建 Agent,Agent 又通过 gateway.session_context 获得平台上下文;重要的是依赖集中在契约上,而不是每个平台各自复制一套 Agent Loop。

三、入口:为什么 hermes 不是 run_agent.py​

pyproject.toml 的三个 console script 是理解项目的第一把钥匙:

hermes = "hermes_cli.main:main"
hermes-agent = "run_agent:main"
hermes-acp = "acp_adapter.entry:main"

这表示“产品入口”和“可复用 Agent”被有意分开:

  1. hermes_cli.main:main 负责安装后的用户体验、子命令、配置向导、gateway 生命周期和 TUI 选择。
  2. run_agent.AIAgent 是可以被 CLI、Gateway、Cron、批处理和 ACP 共同调用的运行时对象。
  3. acp_adapter.entry:main 把同一个运行时暴露给编辑器协议,避免编辑器客户端必须理解 Hermes 的内部工具和 session 细节。

hermes_cli/main.py 还把 hermes_bootstrap 放在最早的位置,用于 Windows UTF-8 stdio、早期恢复和窗口抑制。这是一个很实用的原则:入口最早做平台修复,业务层不再到处判断平台。

四、一次 CLI 请求的最短路径​

hermes
→ hermes_cli.main.main()
→ CLI/TUI 选择与配置加载
→ hermes_cli.oneshot / cli.py
→ AIAgent(...)
→ AIAgent.run_conversation()
→ agent.conversation_loop.run_conversation()
→ provider transport + tool executor

最值得注意的是 AIAgent 的位置:它不是“单纯的 API client”。在 run_agent.py:409 附近可以看到它集中持有模型信息、会话标识、工具状态、上下文引擎、记忆管理器、执行环境、回调、重试和清理逻辑;run_conversation 在 run_agent.py:7153 附近把这些状态组合成一次运行。

五、为什么不把所有代码放进一个 Agent 类​

Hermes 的拆分大致对应四种变化速度:

  • 模型协议变化:agent/transports/、各种 provider adapter;
  • 工具与安全变化:tools/、agent/tool_guardrails.py、审批与 checkpoint;
  • 上下文策略变化:agent/context_engine.py、context_compressor.py、prompt_builder.py;
  • 入口和投递变化:gateway/、hermes_cli/、acp_adapter/。

如果把它们揉在一起,任意一个平台接入都会改变核心循环;现在核心循环只需要依赖诸如“发送请求”“获得 tool call”“追加结果”“持久化 session”这类边界。

六、从仓库规模反推设计重点​

源码中最大的目录并不是 agent/,而是 tests/:约 2,625 个 Python 测试文件。它说明 Hermes 的复杂度不是来自单一算法,而来自组合状态:平台、模型、工具、压缩、并发、凭证、操作系统和恢复路径的交叉组合。

因此,读 Hermes 的正确单位不是“文件”,而是“生命周期”:

进程启动 → session 建立 → turn 开始 → API 请求 → tool 执行
→ 结果持久化 → 继续/压缩/停止 → 投递 → session 关闭

下一章会从这条生命周期中最核心的中段开始:Agent Loop。