1. 为什么最后要看整体架构?
前面九期已经从使用者角度学习了 Hermes Agent 的主要能力。
第一期讲 Hermes Agent 是什么;
第二期讲安装、配置和第一次运行;
第三期讲 CLI/TUI 使用;
第四期讲工具调用系统;
第五期讲 Memory;
第六期讲 Skills;
第七期讲 Messaging Gateway;
第八期讲 Cron 定时任务;
第九期讲 MCP 集成。
到这里,如果只是作为普通用户,已经可以比较完整地使用 Hermes Agent 了。
但是,如果想真正理解这个项目,光知道"它能做什么"还不够,还要进一步理解:
用户输入一句话之后,Hermes 内部到底发生了什么?
CLI、Gateway、Cron 为什么都能复用同一个 Agent 核心?
Memory 和 Skills 是什么时候进入上下文的?
Tools 是如何被模型选择和调用的?
MCP 工具和内置工具最终在哪里汇合?
会话历史、压缩、持久化又是在哪一层处理的?
这些问题都属于源码结构和系统架构层面的内容。
这一期的目标不是逐行阅读源码,而是先建立一张"架构地图"。有了这张地图,后续再读源码时就不会迷路。
2. Hermes Agent 的整体架构可以怎么理解?
我现在对 Hermes Agent 的整体理解是:
Hermes Agent = 多入口 + 一个 Agent 核心 + 多种能力层 + 长期状态管理
它不是一个单纯的聊天程序,而是一个围绕 AIAgent 核心组织起来的 Agent 运行系统。
可以用下面这张简化图理解:
用户入口层
├── CLI / TUI
├── Messaging Gateway
├── Cron Scheduler
├── ACP / IDE Integration
├── Batch Runner
└── API Server
│
▼
Agent 核心层
└── AIAgent / run_agent.py
│
├── Prompt Builder
├── Provider Resolution
├── Tool Dispatch
├── Conversation History
├── Context Compression
├── Memory Flush
└── Session Persistence
│
▼
能力扩展层
├── Built-in Tools
├── MCP Tools
├── Skills
├── Memory Providers
├── Plugins
└── Context Engines
│
▼
外部环境层
├── 本地文件系统
├── Shell / Docker / SSH / Modal 等运行后端
├── GitHub / 数据库 / 浏览器 / API
├── Telegram / Slack / Discord / Email
└── LLM Provider
这张图里最重要的不是某一个模块,而是中间的 AIAgent。
无论用户是从 CLI 发消息,还是从 Telegram 发消息,还是由 Cron 定时触发任务,最终都会进入 AIAgent 的任务执行流程。
这就是 Hermes 架构的关键:
入口可以很多,但 Agent 核心尽量统一。
3. 入口层:不同入口,不同使用场景
Hermes 有多个入口,但它们的作用不同。
3.1 CLI / TUI
CLI/TUI 是最直接的入口。
用户在终端中输入问题,Hermes 读取输入,调用 Agent 核心,然后把结果显示在终端里。
典型流程是:
User input
→ HermesCLI.process_input()
→ AIAgent.run_conversation()
→ 输出结果
→ 保存会话
CLI 更适合开发者本地使用,例如:
分析项目源码;
运行测试;
调试工具;
查看工具调用过程;
手动控制会话。
3.2 Messaging Gateway
Gateway 是多平台消息入口。
它接收 Telegram、Discord、Slack、Email 等平台的消息,把它们转换成 Hermes 内部消息事件,再交给 Agent 核心处理。
典型流程是:
Platform event
→ Adapter.on_message()
→ GatewayRunner._handle_message()
→ 用户鉴权
→ 解析 session
→ 创建 AIAgent
→ AIAgent.run_conversation()
→ 通过平台 adapter 返回结果
Gateway 的意义是让 Hermes 从本地终端走向长期在线服务。
3.3 Cron Scheduler
Cron 是时间触发入口。
它不是由用户当前输入触发,而是由调度器判断某个任务到了执行时间,然后创建一个新的 Agent 会话执行任务。
典型流程是:
Scheduler tick
→ 读取到期任务
→ 创建 fresh AIAgent
→ 注入绑定 skills
→ 执行 job prompt
→ 投递结果
→ 更新下次运行时间
Cron 和 CLI 最大的区别是:Cron 任务一般运行在 fresh session 中,所以 prompt 必须自包含。
3.4 ACP / IDE Integration
ACP 让 Hermes 可以作为编辑器原生 Agent 使用,例如接入 VS Code、Zed、JetBrains 等开发环境。
这一层可以理解为 Hermes 面向 IDE 的入口。
它本质上仍然是把编辑器事件和用户请求转化成 Agent 可以处理的任务。
3.5 Batch Runner / API Server
Batch Runner 和 API Server 更偏自动化、服务化和程序化调用。
它们说明 Hermes 不只是一个交互式工具,也可以被其他程序调用,或者作为一个 Agent 服务运行。
4. Agent 核心:AIAgent 是整个系统的中枢
Hermes 的核心是 run_agent.py 中的 AIAgent。
它可以理解为整个系统的中枢调度器。
AIAgent 负责的事情很多,包括:
构建系统提示词;
选择模型 provider;
调用模型 API;
解析模型返回;
执行工具调用;
维护对话历史;
处理重试和 fallback;
处理上下文压缩;
保存 session;
刷新 memory;
通过 callbacks 向 CLI/Gateway/ACP 汇报进度。
也就是说,AIAgent 不是单纯"调用一次模型"的封装,而是一个完整的 Agent Loop。
用户的一轮请求,并不一定只调用一次模型。它可能经历多次循环:
模型思考
→ 请求调用工具
→ Hermes 执行工具
→ 工具结果返回模型
→ 模型继续判断
→ 再调用工具
→ 最后生成最终回答
这就是 Agent Loop。
5. 一次对话请求的生命周期
可以把一次 Hermes 对话请求拆成下面几个步骤:
1. 用户输入消息;
2. Hermes 把消息加入 conversation history;
3. Prompt Builder 构建系统提示词;
4. Provider Resolver 确定使用哪个模型和 API 模式;
5. Hermes 组装 API messages;
6. 调用模型;
7. 如果模型返回 tool_calls,则执行工具;
8. 工具结果写回 conversation history;
9. 继续调用模型;
10. 如果模型返回最终文本,则输出结果;
11. 保存 session;
12. 必要时刷新 memory;
13. 如果上下文过长,触发压缩。
这就是 Hermes 最基本的运行闭环。
可以简化成:
User
→ Prompt
→ Model
→ Tool Call?
→ Tool Result
→ Model
→ Final Response
→ Persistence
理解这个流程后,前面几期的内容就都能放进来了。
Tools 发生在第 7 步。
Memory 和 Skills 主要进入第 3 步的 Prompt。
Provider 发生在第 4 步。
Session Persistence 发生在第 11 步。
Compression 发生在上下文压力过大时。
Gateway 和 Cron 只是不同的触发入口。
6. Prompt 系统:Memory 和 Skills 是如何进入模型的?
Agent 能不能稳定工作,很大程度上取决于 prompt 如何组织。
Hermes 的 Prompt 系统不是简单把所有东西拼在一起,而是分层构建。
可以简单理解为三类层次:
stable 层
context 层
volatile 层
6.1 stable 层
stable 层是相对稳定的基础身份和行为指导。
它可能包括:
Agent identity;
工具使用规范;
模型行为约束;
skills index;
环境提示;
平台提示。
这一层比较稳定,适合缓存。
6.2 context 层
context 层来自当前调用环境和项目文件。
例如:
用户额外传入的 system_message;
项目中的 AGENTS.md;
CLAUDE.md;
.hermes.md;
.cursorrules。
这一层决定当前项目或当前任务有哪些特殊约定。
例如某个项目规定:
运行测试必须使用 pytest;
提交前必须运行 make lint;
不要修改 generated 目录。
这类内容就应该进入 context 层。
6.3 volatile 层
volatile 层是更容易变化的上下文,例如:
MEMORY.md 快照;
USER.md 用户画像;
外部 memory provider 返回的内容;
当前时间;
session id;
当前模型和 provider 信息。
这一层虽然叫 volatile,但在一次会话中通常是作为快照注入的,不是每一轮随意变动。
这就解释了第五期 Memory 中提到的问题:
Memory 写入磁盘后,不一定立即改变当前会话的系统提示,而通常在新会话中以新的快照进入。
7. Provider Resolution:Hermes 如何决定调用哪个模型?
Hermes 支持多个模型 provider。
用户可能使用 Nous Portal、OpenAI、OpenRouter、Anthropic 或其他兼容接口。
但不同 provider 的 API 模式并不完全一样。
因此,Hermes 需要 Provider Resolution 层来做统一解析。
它大致负责:
读取用户配置;
解析 provider 和 model;
确定 API mode;
找到 API key;
确定 base_url;
处理 provider alias;
处理 OAuth 或 credential pool;
在主模型失败时选择 fallback。
从 Agent 角度看,理想状态是:上层只关心"我要调用一个模型",而不用关心每个 provider 的细节。
Provider Resolution 的作用就是把不同模型服务封装成一个可统一调用的运行时配置。
可以这样理解:
Prompt Builder 负责"给模型什么上下文";
Provider Resolver 负责"调用哪个模型以及怎么调用";
AIAgent 负责"把模型调用组织成 Agent Loop"。
8. Tool System:工具是如何被注册和调用的?
第四期已经讲过工具调用,这里从源码结构角度再总结一次。
Hermes 的工具系统核心是工具注册表。
每个工具文件在导入时向 registry 注册自己。
registry 收集工具名称、参数 schema、handler、toolset、可用性检查等信息。
模型需要工具时,Hermes 把可用工具 schema 提供给模型。
模型返回 tool call 后,Hermes 根据工具名找到 handler 并执行。
简化流程如下:
tools/*.py
→ registry.register()
→ model_tools.py 收集工具 schema
→ 模型看到可用工具
→ 模型返回 tool_calls
→ handle_function_call()
→ 执行 handler
→ 工具结果写回 conversation history
这解释了为什么 Hermes 可以很容易扩展工具:
新增一个工具文件,只要按规范注册,就可以进入工具系统。
9. MCP 工具在哪里接入?
第九期讲过 MCP。
从整体架构看,MCP 工具最终也会进入 Hermes 的工具系统。
区别在于:
内置工具:工具实现写在 Hermes tools/ 目录中;
MCP 工具:工具实现存在于外部 MCP server 中。
但对模型来说,它们最终都会表现成可调用工具。
简化理解:
MCP server
→ Hermes MCP client 发现工具
→ 转换成 Hermes tool schema
→ 注册到工具系统
→ 模型选择调用
→ Hermes 转发调用给 MCP server
→ MCP server 返回结果
→ 结果写回模型上下文
所以 MCP 不是另起一套 Agent Loop,而是扩展了 Hermes 的工具来源。
10. Memory、Session 和 Context Compression 的关系
Hermes 中有三个容易混淆的状态管理概念:
Memory;
Session;
Context Compression。
10.1 Memory
Memory 保存长期稳定信息。
例如:
用户偏好;
项目长期背景;
环境习惯;
已经确认的事实。
它更像跨会话长期记忆。
10.2 Session
Session 保存一次会话的历史轨迹。
例如:
用户说过什么;
Agent 回答了什么;
调用过哪些工具;
工具返回了什么;
当前会话如何发展。
它更像完整聊天和工具调用记录。
10.3 Context Compression
Compression 是为了控制上下文长度。
当对话太长时,Hermes 会把中间部分压缩成摘要,同时保留最近若干条消息和工具调用对,避免超过模型上下文窗口。
这三者的关系可以这样理解:
Memory:长期事实。
Session:对话轨迹。
Compression:长会话压缩机制。
它们解决的问题不同。
Memory 解决"长期不忘"。
Session 解决"可以恢复会话"。
Compression 解决"上下文不能无限增长"。
11. Skills 在源码结构中的位置
Skills 可以看作 Hermes 的"经验库"。
它们不是普通工具,也不是 memory,而是按需加载的能力文档。
从运行过程看,skills 主要在 prompt 阶段发挥作用。
简化流程是:
启动会话
→ 扫描可用 skills
→ 在系统提示词中放入 skills index
→ 模型判断是否需要某个 skill
→ 调用 skill_view 加载完整 SKILL.md
→ 按 skill 指导执行任务
这就是前面第六期讲的 progressive disclosure。
也就是说:
Skills 不会全部塞进上下文;
先给模型一个索引;
需要时再加载完整内容。
这能避免 skills 太多导致上下文污染。
12. Gateway 为什么不直接处理任务?
Messaging Gateway 的作用是接消息、鉴权、管理 session 和投递结果。
它本身不应该承担复杂推理。
如果 Gateway 自己处理 Agent 逻辑,就会导致 CLI、Gateway、Cron 各写一套执行流程,系统很快会混乱。
Hermes 的设计更合理:
Gateway 负责平台差异;
AIAgent 负责智能任务执行。
所以 Gateway 要做的是:
把 Telegram/Slack/Email 消息转成统一 MessageEvent;
判断用户有没有权限;
确定当前消息属于哪个 session;
把任务交给 AIAgent;
把结果发回原平台。
这就是"平台无关核心"的思想。
不同入口尽量只处理入口自己的差异,不复制 Agent 核心逻辑。
13. Cron 为什么使用 fresh AIAgent?
Cron 任务和普通聊天不同。
普通聊天依赖当前会话历史。
Cron 任务通常是一个独立任务,到时间就执行。
因此,Cron 会创建一个 fresh AIAgent,不带普通聊天历史,只注入任务 prompt 和绑定 skills。
这样做有几个好处:
任务更独立;
不会被旧聊天污染;
调度更稳定;
执行结果更可复现;
适合长期自动化。
但它也带来一个要求:
Cron prompt 必须自包含。
如果 prompt 写得太省略,fresh session 就无法知道背景。
所以第八期反复强调:Cron 任务不能写"照之前说的做",而要把目标、范围、输出格式和安全限制写完整。
14. Plugin System:Hermes 的另一种扩展方式
除了 MCP 和 Skills,Hermes 还有 Plugin System。
可以简单理解为:
Skills:扩展任务流程;
Tools:扩展具体动作;
MCP:接入外部工具服务器;
Plugins:扩展 Hermes 本体能力和生命周期钩子。
插件可以来自多个位置:
用户级插件目录;
项目级插件目录;
pip entry points。
插件可以注册:
tools;
hooks;
CLI commands。
Hermes 还有专门的 memory provider plugin 和 context engine plugin。
这说明 Hermes 的扩展方式不是单一的。
如果只是写一个任务流程,用 Skill。
如果要接外部 MCP server,用 MCP。
如果要新增本地工具,用 Tool。
如果要改 Hermes 生命周期行为、增加 hook 或深度扩展,用 Plugin。
15. 目录结构应该怎么读?
从源码学习角度看,可以先抓住几个关键目录和文件。
hermes/
├── run_agent.py
├── cli.py
├── model_tools.py
├── agent/
│ ├── prompt_builder.py
│ ├── system_prompt.py
│ ├── context_compressor.py
│ ├── prompt_caching.py
│ └── auxiliary_client.py
├── tools/
│ ├── registry.py
│ └── *.py
├── gateway/
│ ├── run.py
│ ├── session.py
│ ├── delivery.py
│ ├── pairing.py
│ ├── hooks.py
│ └── platforms/
├── cron/
│ ├── jobs.py
│ └── scheduler.py
├── plugins/
│ ├── memory/
│ └── context_engine/
├── skills/
├── optional-skills/
└── tests/
我建议按这个顺序读:
第一步:run_agent.py
第二步:agent/prompt_builder.py
第三步:model_tools.py
第四步:tools/registry.py
第五步:tools/ 下的几个具体工具
第六步:session 相关代码
第七步:gateway/run.py
第八步:cron/scheduler.py
第九步:MCP 配置和工具注册逻辑
第十步:plugins 和 skills 机制
不要一开始就从所有文件横向扫过去。
更好的方式是沿着"一次用户请求如何被处理"这条主线往下追。
16. 一条最适合源码阅读的主线
如果我要真正读 Hermes 源码,我会从这条主线开始:
用户在 CLI 输入一句话
→ cli.py 接收输入
→ 创建或获取 session
→ 调用 AIAgent.run_conversation()
→ prompt_builder 构建系统提示词
→ runtime_provider 解析模型
→ 模型返回 tool_calls
→ model_tools 处理工具调用
→ tools/registry 找到工具 handler
→ 执行工具
→ 工具结果回到 history
→ 模型生成最终回答
→ session 保存
这条主线覆盖了 Hermes 最核心的部分:
入口;
Agent Loop;
Prompt;
Provider;
Tool;
Session。
只要这条链路理解清楚,Gateway、Cron、MCP 都可以看作不同入口或不同工具来源。
17. 从"功能模块"到"系统设计"的理解
经过前十期学习,我对 Hermes Agent 的理解已经从"功能列表"变成了"系统设计"。
最开始看 Hermes,容易只看到它有很多功能:
能聊天;
能调用工具;
能记忆;
能写 skills;
能接 Telegram;
能定时任务;
能接 MCP。
但真正从架构看,它的核心设计更像是:
用统一的 AIAgent 承接多入口任务;
用 Prompt Builder 组织身份、项目、memory、skills;
用 Provider Resolution 屏蔽模型服务差异;
用 Tool Registry 统一内置工具和扩展工具;
用 Session Storage 维持会话连续性;
用 Memory 维持跨会话长期背景;
用 Skills 沉淀可复用流程;
用 Gateway 把 Agent 服务化;
用 Cron 把 Agent 自动化;
用 MCP 把 Agent 接入外部工具生态。
这样看,Hermes 就不是一个"功能堆叠型项目",而是一个围绕长期 Agent 运行需求设计的系统。
18. Hermes 架构中的几个关键设计思想
我认为 Hermes 架构中最值得学习的设计思想有五个。
18.1 平台无关核心
CLI、Gateway、Cron、ACP 都可以进入同一个 AIAgent 核心。
这避免了多个入口各自实现一套 Agent 逻辑。
18.2 Prompt 分层
Hermes 把 identity、skills、context files、memory、profile、timestamp 等内容分层组织,而不是随意拼接。
这有助于稳定上下文,也有助于缓存和长期使用。
18.3 工具注册表
工具通过 registry 统一注册、统一暴露、统一调度。
这让内置工具、插件工具、MCP 工具都能进入同一套工具调用流程。
18.4 长期状态分离
Memory、Session、Compression 解决不同层次的状态问题。
长期事实、会话轨迹和上下文压缩被分开处理,避免混成一团。
18.5 松耦合扩展
MCP、Plugins、Memory Providers、Context Engines 都通过相对松耦合的方式接入。
这让 Hermes 可以扩展,但不会让核心系统过度依赖某个外部能力。
19. 学习 Hermes 源码时要避免什么?
我觉得读 Hermes 这类项目源码,容易犯几个错误。
第一,不要一开始就逐文件阅读。
这样很容易陷入细节,看了很多文件却不知道主线是什么。
第二,不要只看 README。
README 能告诉你它能做什么,但不能告诉你任务是怎么流转的。
第三,不要把 Memory、Session、Skills 混为一谈。
它们虽然都和"长期性"有关,但职责完全不同。
第四,不要忽略入口差异。
CLI、Gateway、Cron 的触发方式不同,但最终会进入相似的 Agent 核心。
第五,不要只看工具数量。
真正重要的是工具如何注册、如何暴露给模型、如何执行、如何返回结果。
第六,不要忽视安全边界。
Agent 一旦能调用工具、接外部平台、跑定时任务,权限管理和确认机制就非常关键。
20. 小结
这一期主要从源码结构和整体架构角度总结了 Hermes Agent。
我的理解是,Hermes Agent 的核心不是某一个功能,而是它围绕 AIAgent 建立了一套统一的 Agent 运行体系。CLI、Gateway、Cron、ACP 等入口最终都会把任务交给 Agent 核心;Agent 核心再负责 prompt 构建、provider 解析、模型调用、工具执行、会话持久化、上下文压缩和 memory 刷新。
从模块关系看:
CLI/TUI 解决本地交互;
Gateway 解决多平台入口;
Cron 解决定时自动化;
Prompt Builder 解决上下文组织;
Provider Resolution 解决模型调用差异;
Tool Registry 解决工具统一管理;
MCP 解决外部工具生态接入;
Memory 解决长期事实保存;
Skills 解决经验流程沉淀;
Session Storage 解决会话恢复;
Compression 解决长上下文压力。
这就是 Hermes Agent 作为长期 Agent 系统的整体运行方式。
到这里,Hermes Agent 学习笔记的前十期已经形成了一个完整闭环:从项目定位,到安装使用,再到工具、记忆、技能、消息网关、定时任务、MCP 和源码架构。
如果后续继续写,我认为可以进入第二阶段:不再按功能介绍,而是做实战型源码解析。例如:
第十一期:如何给 Hermes 添加一个自定义 Tool;
第十二期:如何写一个自己的 Skill;
第十三期:如何配置一个 GitHub MCP 自动化工作流;
第十四期:如何用 Cron + Gateway 做个人技术日报机器人;
第十五期:如何从源码角度分析 AIAgent.run_conversation()。
下一阶段的重点,就可以从"理解 Hermes"转向"改造 Hermes"。