Hermes Agent 和 OpenClaw 很容易被放在同一个篮子里讲:都能聊天、都能调工具、都能接入消息平台、都支持长期运行。但源码读下来,它们的重心其实不一样。
OpenClaw 更像一套“平台控制面”:Gateway、Channel、Node、Plugin、Agent Harness 都围绕统一控制面展开。Hermes Agent 更像一套“自我学习型执行内核”:核心是一个厚实的 Python Agent Loop,再把记忆、技能、工具、Gateway、Cron、执行环境和研究训练路径都接到这个 Loop 周围。
这篇基于 NousResearch/hermes-agent 源码分析,基线提交为 a7cdd413,提交时间是 2026-04-27T20:41:36-07:00。对照部分结合 OpenClaw 本地源码快照 4792a50 的 Agent Harness 设计。
01 总览:Hermes 的中心不是 Gateway,而是 Agent Loop
图 1:Hermes Agent 的整体结构。CLI、Gateway、Cron、批处理和 RL 环境最终都会把任务送进同一个 Python Agent Loop。
从目录结构看,Hermes 的模块分层非常直接:
| 模块 | 主要职责 | 关键源码 |
|---|---|---|
| CLI / TUI | 命令入口、模型选择、配置、登录、更新、交互界面启动 | hermes_cli/main.py、cli.py |
| Agent Core | 会话循环、模型调用、工具调度、上下文压缩、费用统计、失败恢复 | run_agent.py |
| Tool Registry | 工具发现、工具 schema 过滤、工具执行、MCP/插件工具接入 | model_tools.py、tools/registry.py |
| Memory / Skills | 文件记忆、外部记忆插件、会话搜索、技能加载与技能自改进 | agent/memory_manager.py、tools/memory_tool.py、agent/skill_commands.py |
| Gateway | Telegram、Discord、Slack、WhatsApp、Signal、Email 等消息入口 | gateway/run.py、gateway/session.py |
| Execution Backends | local、Docker、SSH、Modal、Daytona、Singularity 等终端环境 | tools/terminal_tool.py、tools/environments/ |
| Research / RL | 批量轨迹生成、Atropos 环境、工具调用 parser、训练数据压缩 | environments/、batch_runner.py |
Hermes 的核心判断可以概括成一句话:
Hermes Agent 是一个以
AIAgent为中心的长期运行 Agent 系统,Gateway 和 TUI 是入口,memory/skills/session_search/context compressor 是学习闭环,tools/environments 是行动层。
这和 OpenClaw 有明显差异。OpenClaw 的文章里我已经讲过,OpenClaw 的重心在 Gateway 控制面和嵌入式 Runtime 编排。Hermes 则把复杂度堆在 run_agent.py 这个厚执行核里,再用 Python 模块把工具、记忆和平台接进来。
02 启动路径:CLI 很厚,但真正运行会落到 AIAgent
Hermes 的用户入口看起来很多:
hermes:启动终端 TUI。hermes model:选择 provider 和 model。hermes gateway:启动消息平台网关。hermes setup:跑配置向导。hermes claw migrate:从 OpenClaw 迁移。batch_runner.py/environments/:研究和训练场景。
但这些入口最后基本都绕不开 AIAgent。
hermes_cli/main.py 是一个非常大的命令分发文件,负责模型配置、认证、TUI 构建、Gateway 启动、更新、日志、备份、profile、completion 等命令。它更像“产品壳层”,不是推理内核。
真正的 Agent 执行在 run_agent.py 的 AIAgent 类里。这个类的构造参数非常长,原因不是设计失控,而是它要同时服务 CLI、Gateway、Cron、subagent、ACP、batch eval 等多种场景:
provider/api_mode/model决定模型传输层。enabled_toolsets/disabled_toolsets决定工具面。platform/user_id/chat_id决定消息平台上下文。session_id/session_db决定会话持久化。skip_memory/skip_context_files决定是否加载长期上下文。iteration_budget/parent_session_id决定多 Agent 协作边界。- 各种 callback 把 streaming、tool progress、clarify、status 回传给 CLI 或 Gateway。
这说明 Hermes 没有把“交互界面”和“Agent 内核”分成两个完全独立产品,而是用一个 AIAgent 承载所有入口的实际执行。
03 Agent Loop:它不是一次请求,而是带预算和恢复逻辑的执行机器
图 2:Hermes 的一轮 Agent Loop。每一轮都会经过 prompt 构建、模型调用、工具解析、工具执行、上下文预算和持久化。
Hermes 的 Agent Loop 主要在 run_agent.py 中展开。代码量很大,但核心结构是稳定的:
- 初始化系统提示词、memory snapshot、context files、skills、platform hints。
- 组装当前 messages。
- 调模型接口。
- 解析模型返回的
tool_calls或 raw<tool_call>。 - 执行工具。
- 把工具结果写回 messages。
- 更新 token、费用、上下文压缩状态和 session DB。
- 如果模型还要继续调工具,进入下一轮。
- 如果模型返回最终文本,持久化并退出。
这个循环里最值得注意的不是“会调工具”,而是周边的防御逻辑非常多:
IterationBudget限制最大迭代次数,父 Agent 和子 Agent 可以共享或独立预算。_should_parallelize_tool_batch()判断哪些工具可以并发执行。_repair_tool_call_arguments()尝试修复模型生成的损坏 JSON 参数。- 对
length截断有 continuation retry。 - 对“思考耗尽输出 token”有专门提示。
ContextCompressor在接近上下文窗口时压缩中间消息。StreamingContextScrubber防止 memory context 片段通过 streaming 泄漏到用户侧。- session token 和费用会写入 DB,供
/usage、/insights查询。
Hermes 的执行循环更像一个“容错执行器”,不是简单的 while loop。它的目标不是跑通 demo,而是长期会话里尽可能不因为模型输出异常、工具输出过长、网络失败、上下文爆炸、pipe 断开或平台中断而崩掉。
04 模型层:Hermes 把 provider 差异吸收到 transport 和 adapter
Hermes 支持的模型入口非常多:OpenAI、OpenRouter、Nous Portal、Anthropic、Bedrock、Gemini、Copilot ACP、Kimi、Moonshot、xAI、custom endpoint 等。
源码里没有把所有 provider 分支都写成一堆散落的 if,而是逐步抽象出 transport:
agent/transports/base.pyagent/transports/chat_completions.pyagent/transports/anthropic.pyagent/transports/bedrock.pyagent/transports/codex.py
AIAgent.__init__() 会根据 provider、base_url、model、api_mode 自动判断使用哪条 API 路径:
chat_completionscodex_responsesanthropic_messagesbedrock_converse
这层判断非常工程化。例如 GPT-5/Codex 风格模型通常走 Responses API,但 Azure OpenAI 又是例外;Anthropic 原生和第三方兼容 Anthropic endpoint 又要走不同 adapter;Bedrock 则绕过 OpenAI client,直接走 AWS Converse API。
这套设计的核心不是“支持很多模型”,而是:把模型 API 差异压进 transport 层,让 Agent Loop 继续围绕统一的 message/tool/result 语义工作。
05 工具系统:registry 是核心,toolset 是可治理边界
图 3:Hermes 的工具注册和过滤链路。工具模块自注册,model_tools 负责发现、过滤、schema 动态改写和执行分发。
Hermes 的工具系统由两个文件托底:
tools/registry.py:工具注册表。model_tools.py:工具发现、过滤、schema 输出和调用分发。
工具不是集中写在一个大列表里,而是每个 tools/*.py 模块在 import 时调用 registry.register() 自注册。discover_builtin_tools() 会扫描工具文件 AST,只导入顶层存在 registry.register(...) 的模块,避免把无关 helper 全部拉进来。
get_tool_definitions() 做的事情很多:
- 根据
enabled_toolsets/disabled_toolsets解析有效工具。 - 运行工具可用性检查,例如依赖、环境变量、平台状态。
- 动态重写
execute_codeschema,只暴露当前真实可用的 sandbox tools。 - 动态重写 Discord 工具 schema,隐藏 bot 权限不支持的动作。
- 当
web_search/web_extract不可用时,去掉 browser tool 描述里的交叉引用,降低模型幻觉调用不存在工具的概率。 - 通过
schema_sanitizer清理不同后端不兼容的 JSON schema 形状。
这说明 Hermes 的工具系统不只是“给模型一组 function schema”。它在做一件更重要的事:让模型看到的工具说明尽量贴近当前 session 的真实能力。
工具执行时,model_tools.handle_function_call() 最终会走 registry dispatch。同步/异步桥接也做了专门处理:主线程有持久 event loop,worker 线程有 thread-local loop,避免 asyncio.run() 反复创建和关闭 event loop 导致缓存客户端出错。
这类细节看起来不醒目,但这是长期运行 Agent 的真实工程成本。
06 记忆系统:Hermes 的“自我学习循环”由四部分组成
图 4:Hermes 的记忆闭环。短期 context、文件记忆、会话搜索、外部 memory provider 和技能系统共同构成学习循环。
Hermes README 里强调它有 built-in learning loop。源码里这个 loop 不是一个单点功能,而是由四类机制拼出来的。
第一类是文件记忆。tools/memory_tool.py 管理两个文件:
MEMORY.md:环境事实、项目约定、工具经验、长期可复用观察。USER.md:用户偏好、沟通风格、稳定习惯。
它的设计很克制:使用 § 作为 entry delimiter,有字符上限,写入前扫描 prompt injection 和 exfiltration 模式,读写时加文件锁。最关键的是 frozen snapshot:session 开始时把记忆注入系统提示词,session 中途写入会落盘,但不会立刻改变当前系统提示词。这样可以稳定 prompt cache,也避免当前轮被刚写入的记忆反复扰动。
第二类是 memory provider。agent/memory_manager.py 把内置记忆和外部 provider 统一成 MemoryProvider 接口。内置 provider 永远存在,外部 provider 同时只允许一个,避免工具 schema 膨胀和后端冲突。
外部 provider 位于 plugins/memory/,包括 Honcho、Hindsight、Mem0、ByteRover、OpenViking、Supermemory、RetainDB、Holographic 等。每个 provider 可以实现:
prefetch():每轮前召回相关上下文。sync_turn():每轮后同步对话。get_tool_schemas():向模型暴露专属 memory 工具。on_pre_compress():上下文压缩前抽取重要信息。on_delegation():观察子 Agent 的结果。
第三类是 session search。tools/session_search_tool.py 不直接把历史聊天全塞给模型,而是先用 SQLite FTS5 搜索历史消息,再按 session 聚合,截取匹配附近的上下文,最后用辅助模型做摘要。这是长期记忆里很实用的一层:文件记忆存“稳定事实”,session search 找“过去发生过什么”。
第四类是技能系统。Hermes 的技能不是记忆文件,而是可复用工作流。agent/prompt_builder.py 里的 SKILLS_GUIDANCE 会鼓励模型在复杂任务后保存技能;agent/skill_commands.py 负责把 SKILL.md 变成 slash command;agent/skill_preprocessing.py 支持 ${HERMES_SKILL_DIR}、${HERMES_SESSION_ID} 模板变量和可选 inline shell 展开。
所以 Hermes 的“学习”不是神秘能力,而是几条明确的数据通路:
- 稳定事实进 memory。
- 历史过程进 session DB,再由 session_search 召回。
- 可复用流程进 skill。
- 长上下文过程由 compressor 保留摘要。
- 外部 memory provider 可替换长期用户模型。
07 Context Engine:压缩不是删历史,而是做安全交接
图 5:Hermes 默认 ContextCompressor 的压缩策略。它保护头部和尾部,中间区域经过工具输出裁剪和辅助模型摘要。
agent/context_engine.py 定义了可插拔的 ContextEngine 抽象。默认实现是 agent/context_compressor.py。
它的压缩流程大致是:
- 根据模型上下文长度和阈值判断是否需要压缩。
- 先裁剪旧工具结果,保留一行结构化摘要。
- 保护系统提示词和会话开头。
- 保护最近尾部消息,确保当前任务连续性。
- 对中间消息用辅助模型生成结构化摘要。
- 如果已经压缩过,再做 iterative summary update。
- 把摘要作为“参考信息”插入,而不是当成新的用户指令。
源码里 SUMMARY_PREFIX 的措辞很关键:它明确说明压缩摘要是旧上下文的 handoff,不是当前指令,也不要回答摘要里出现的问题。这解决了一个常见问题:压缩摘要如果写得像任务列表,模型会误以为那些旧任务仍然要执行。
Hermes 还在压缩前做了很多具体处理:
- 多模态内容按图片 token 估算加入预算。
- tool call arguments 用 JSON parse 后裁剪,避免把 JSON 字符串截坏。
- 对 terminal、read_file、search_files、browser、web_search 等工具结果生成有信息量的一行摘要。
- 对摘要失败设置 cooldown,避免每轮都重复失败。
这不是“上下文不够就总结一下”的简单实现,而是把压缩当成会话连续性工程来做。
08 Gateway:多平台入口,但不抢 Agent Core 的位置
图 6:Hermes Gateway。各平台 adapter 把消息规范化为 session event,再交给 GatewayRunner 调用 AIAgent。
Hermes 也有 Gateway,但它和 OpenClaw 的 Gateway 气质不同。
gateway/run.py 的 GatewayRunner 负责平台 adapter、session、delivery、interrupt、slash command、media placeholder、cron ticker 等。gateway/session.py 负责 session key、sender/chat hash、session context prompt、session store、shared multi-user session 等。
Gateway 的核心价值是把多个消息平台变成统一 Agent 输入:
- Telegram、Discord、Slack、WhatsApp、Signal、Email、WeCom、Feishu、Matrix、Mattermost 等平台各自有 adapter。
- 平台消息会被转换成统一 session event。
- 每个平台会注入不同 platform hints,例如 markdown 支持、媒体发送格式、群聊身份等。
/stop、/new、/reset、/model、/skills等命令在 messaging 场景复用 CLI 语义。- gateway session 可以有自己的
platform、user_id、chat_id、thread_id,这些会传给AIAgent。
OpenClaw 的 Gateway 更像系统控制面;Hermes 的 Gateway 更像多平台适配层。它很重要,但它不拥有 Agent Runtime 的底层协议。底层执行仍然是 AIAgent。
这也解释了两者架构差异:OpenClaw 把平台一致性放在 Gateway;Hermes 把执行一致性放在 Agent Loop。
09 执行环境:Hermes 把 terminal 当成可替换后端
Hermes 的 terminal tool 不只是 subprocess.run() 包一层。tools/terminal_tool.py 把执行后端抽象成多种环境:
- local:直接在本机执行。
- docker:容器隔离。
- ssh:远程机器。
- modal:云端 sandbox。
- daytona:持久云开发环境。
- singularity:HPC/集群常用容器。
这和 Hermes 的定位有关。它不是只在本地电脑上做代码助手,而是可以跑在 VPS、云环境、GPU 集群、serverless sandbox 上。terminal backend 的职责不仅是运行命令,还包括:
- foreground/background 任务。
- sudo 和危险命令审批。
- workdir 安全校验。
- interrupt 传播。
- container/VM 生命周期。
- persistent filesystem 与 idle cleanup。
这层能力和 Gateway 结合之后,Hermes 就变成了“你从 Telegram 发消息,它在云 VM 上跑任务”的长期 worker。
10 Subagent:Hermes 用 delegate_task 做上下文隔离和并行
tools/delegate_tool.py 是 Hermes 的子 Agent 实现。它的设计很清楚:
- 子 Agent 是新的
AIAgent实例。 - 子 Agent 不继承父会话历史。
- 每个子 Agent 有自己的
task_id和 terminal session。 - 子 Agent 默认不能再调用
delegate_task,防止递归失控。 - 子 Agent 默认不能写 memory,避免污染共享长期记忆。
- 子 Agent 默认不能
clarify或send_message,避免绕过父 Agent 与用户交互。 - 父 Agent 只看到子 Agent 的最终摘要,不吃下子 Agent 的中间工具轨迹。
这其实是一种很务实的多 Agent 协作:不追求复杂自治社会,而是把子 Agent 当成隔离工作线程。它可以并行处理任务,但上下文成本和副作用被控制在边界内。
这点和 OpenClaw 的多 Agent/Swarm 或 Agent Harness 不是同一层概念。Hermes 的 subagent 是“同一执行内核下的新实例”;OpenClaw 的 harness 是“同一平台 runtime 下可替换底层执行器”。
11 结合 Agent Harness:Hermes 和 OpenClaw 的关键差异
图 7:Hermes 与 OpenClaw Agent Harness 的对照。Hermes 把复杂度放在一个厚 Agent Loop;OpenClaw 把底层执行器抽象成可选择的 harness。
OpenClaw 的 docs/plugins/sdk-agent-harness.md 对 Agent Harness 的定义很明确:harness 是“一个 prepared OpenClaw agent turn 的底层 executor”,不是 provider、channel、tool registry。
源码上对应三个核心文件:
src/agents/harness/types.ts:定义AgentHarness接口,包括supports()、runAttempt()、compact()、reset()、dispose()。src/agents/harness/registry.ts:全局 registry,插件可以注册 harness。src/agents/harness/selection.ts:根据 provider/model/config/session pin 选择 harness。
它的选择策略也很关键:
- 已有 session 记录的 harness id 优先,避免同一个 transcript 中途切换底层 runtime。
OPENCLAW_AGENT_RUNTIME=<id>可以强制选择某个 harness。auto模式下注册 harness 自己判断是否支持 provider/model。- 没有插件 harness 匹配时,默认 fallback 到 PI。
- 如果某个插件 harness 已经 claim 了本轮执行,失败后不会自动重放到 PI,避免重复副作用和语义变化。
这套设计适合 OpenClaw,因为 OpenClaw 要把 Codex app-server、PI、未来其他原生 agent runtime 都挂在同一个平台控制面下面。它需要“同一个 OpenClaw session 可以由不同底层执行器承载”的抽象。
Hermes 当前不是这个结构。Hermes 的 AIAgent 自己就是执行器,provider 差异由 transport/adapter 处理,执行环境差异由 tool backend 处理,消息平台差异由 Gateway adapter 处理。它没有把“底层 agent turn executor”抽象成可替换 harness。
两者取舍如下:
| 维度 | Hermes Agent | OpenClaw Agent Harness |
|---|---|---|
| 执行核心 | 单个厚 AIAgent loop | 平台准备 turn,再选底层 harness |
| provider 差异 | transport/adapter 吸收 | provider 解析后由 harness 可选择性 claim |
| session 连续性 | Hermes session DB + messages + context compressor | OpenClaw session pin harness id,防止 runtime 热切换 |
| 插件扩展点 | tools、memory provider、context engine、CLI/plugin | provider、tool、channel、agent harness、Codex extension |
| Codex 类原生 runtime | 走 provider/API adapter 语义 | 可由 codex harness 接管 native thread/compaction/app-server |
| 架构重心 | 自我学习执行核 | 平台控制面 + 可替换执行器 |
如果把 OpenClaw 的 Agent Harness 思路移植到 Hermes,合理的切入点不是替换 tool registry,也不是替换 Gateway,而是在 AIAgent.run_conversation() 外层定义一个类似 AgentTurnExecutor 的边界:
- Hermes Core 继续负责 config、memory、skills、tools、platform hints、session DB。
- 默认 executor 调用当前
AIAgentloop。 - Codex/ACP/特殊 native agent 可以注册 executor。
- executor claim 后负责底层 thread/resume/compact。
- session 记录 executor id,避免中途切换。
但这会带来明显代价:Hermes 现在的优势正是一个 Python 执行核能看到所有工具、记忆、压缩和回调状态。拆成 harness 后,边界会更清晰,但集成成本和状态同步复杂度会上升。
12 研究训练路径:Hermes 不只是产品 Agent,也在服务数据生成
Hermes 还有一条很重要但容易被忽略的线:研究和训练。
environments/agent_loop.py 实现了可复用的 HermesAgentLoop,用于标准 OpenAI 工具调用格式。它服务于 Atropos/RL 环境,支持:
- server 抽象调用
chat_completion()。 - tool schemas 和 valid tool names。
- raw
<tool_call>fallback parser。 - 每轮 reasoning 抽取。
- tool error 记录。
- 大并发 eval 的 thread pool。
environments/tool_call_parsers/ 里有很多模型格式 parser,例如 Hermes、DeepSeek、GLM、Kimi、Llama、Mistral、Qwen 等。这说明 Hermes 不只是消费工具调用模型,也在为“训练能稳定调工具的模型”准备环境。
batch_runner.py、mini_swe_runner.py、scripts/sample_and_compress.py 这些文件进一步说明它有数据生产和评测用途。产品 Agent 和训练环境共享一部分工具调用语义,这是 Hermes 与普通 CLI Agent 的另一个区别。
13 总结:Hermes 是一套长期运行的学习型 Agent 内核
读完 Hermes 源码后,我觉得它最值得关注的不是“支持很多模型”或“支持很多聊天平台”,而是它把长期运行 Agent 的几个难点放在同一个系统里解决:
- 模型 API 差异:用 transport/adapter 吸收。
- 工具能力膨胀:用 registry + toolset + dynamic schema 控制。
- 长期记忆:用文件记忆、session search、memory provider 拆分职责。
- 可复用经验:用 skills 从任务中沉淀流程。
- 上下文爆炸:用 ContextEngine 和压缩摘要做安全交接。
- 多入口:用 CLI/TUI/Gateway/Cron 共享同一个 Agent Core。
- 多环境:用 terminal backend 支持本机、容器、远程和云 sandbox。
- 多任务:用 delegate_task 隔离子 Agent。
- 研究训练:用 environments 和 parser 把工具调用轨迹变成可评测数据。
和 OpenClaw 放在一起看,Hermes 的路线更像“厚内核 + 多入口”,OpenClaw 的路线更像“强控制面 + 可替换执行器”。Agent Harness 是 OpenClaw 的关键抽象,因为它要把 Codex、PI 或其他 native runtime 接进同一个平台;Hermes 暂时不需要同样的 harness 层,因为它的执行一致性已经集中在 AIAgent 里。
如果未来 Hermes 要进一步接入 Codex app-server、Claude Code、OpenClaw PI 这类带原生 session/compact/thread 语义的 runtime,Agent Harness 会是一个自然演进方向。但在当前源码里,Hermes 的核心竞争力仍然是:一个能长期记忆、能自我沉淀技能、能跨平台运行、能持续调工具的 Python Agent Loop。