Hermes Agent 源码解析:七大功能模块是怎么工作的
Hermes Agent 是 Nous Research 开源的一个 AI Agent,MIT 协议,代码在 GitHub 上。它的核心卖点是"闭环学习"——简单说就是 Agent 能越用越聪明,用过的经验会自动变成下次的技能。
这篇文章直接从源码出发,把它的七大模块讲清楚。不跟别的产品比,就讲它自己。
一、对话循环:Agent 的心脏
代码位置:agent/conversation_loop.py
所有 Agent 干活的方式都差不多——用户说一句话,Agent 想一想,需要用工具就用工具,用完了把结果拿回来再想,想清楚了再回复用户。这个"想→做→看结果→再想"的循环,有个学名叫 ReAct 模式。
Hermes 的对话循环核心就是一个 while 循环(第526行):
while (api_call_count < agent.max_iterations and agent.iteration_budget.remaining > 0):
每轮循环做的事:调用大模型 → 模型如果返回了 tool_calls 就执行工具 → 把工具结果喂回给模型 → 继续下一轮。直到模型不再调工具了,就把最终的文字回复给用户。
基础流程就这些,没什么花哨的。Hermes 做得比较扎实的是循环外面的几层保护机制:
1. 迭代预算——防止 Agent 无限转圈
Agent 每循环一轮就要调一次大模型,轮次有限。用 IterationBudget 类来限制最多转多少轮。但有些工具调用很轻量——比如 execute_code,Agent 写一段 Python 脚本一次串好几步操作,结果一次性返回,不需要再绕回模型推理。这种轻量调用不该占跟重活一样的轮次配额,所以执行完会把配额退回来。
2. 上下文压缩——防止对话太长撑爆模型记忆
大模型的上下文窗口是有限的,聊久了历史消息就会超。代码在 agent/conversation_compression.py,当 token 用量到了上下文窗口的75%时,Hermes 会用一个辅助模型把之前的对话历史压缩成一段摘要,然后把新对话接着往下聊。压缩完会把 SQLite 数据库里的会话记录分成新旧两段。
这个压缩引擎可以替换——如果你有更好的压缩方案,可以通过插件接入,因为 context_engine.py 定义了标准的接口。
3. 工具调用自修复——防止模型犯蠢
大模型有时候会"幻觉",比如调用一个根本不存在的工具名,或者传的参数是乱码 JSON。Hermes 做了几层处理:
- 工具名拼错了,
_repair_tool_call会尝试模糊匹配,帮你纠正 - JSON 参数格式错了,最多重试3次让模型重新生成
- 如果参数被截断了(模型输出到一半就断了),直接拒绝执行,不拿残缺的参数乱跑
4. 推理内容保留——让模型不用重复思考
有些模型(比如 GLM-5.1、QwQ、DeepSeek R1)会先"思考"再回答,思考过程放在 reasoning_content 字段里。Hermes 会把这些思考内容保留下来,下一轮对话时传回去,这样模型就不用从头再想一遍。
5. 系统提示词分三层——为了缓存命中
系统提示词就是告诉 Agent "你是谁、你能做什么"的那段话。Hermes 把它分成了三层:
- 稳定层:Agent 身份、工具说明、技能列表——这些跨轮次不变的东西放一起,让模型的缓存一直有效
- 上下文层:项目相关的文件(比如 AGENTS.md)——这些跟着项目走
- 易变层:记忆快照、用户画像、当前时间——这些每轮都可能变
分层的目的是:不变的东西不要每轮重新传,省 token 也省时间。
二、消息网关:一个 Agent 接十五个聊天平台
代码位置:gateway/
这是 Hermes 跟大多数 Agent 不太一样的地方。一般 Agent 就一个终端界面,Hermes 可以同时接入 15 个以上的聊天平台:Telegram、Discord、Slack、WhatsApp、Signal、微信、企业微信、QQ、飞书、钉钉、Email、SMS、Matrix、Webhook,还有字节的元宝。
怎么做到的?所有平台适配器都继承自同一个基类 BasePlatformAdapter,每个适配器只做三件事:
- 监听平台的消息,转成 Hermes 内部的统一格式
- 把消息丢给 Agent 处理,拿到回复
- 把回复转回平台能识别的格式,发出去
网关主进程 GatewayRunner 管理所有适配器的启停,内部有一个 Agent 实例缓存——最多128个,空闲超过1小时的自动清理。这样你在手机 Telegram 上聊到一半,回到电脑上用终端可以接着同一个对话继续。
还有一个 HTTP API(api_server.py),让其他应用也能调 Hermes。
好处很明显:随时随地、任何平台都能用。代价是要维护 15+ 个适配器,哪个平台的 API 改了都得跟着改。你可能想问:能不能用 MCP 协议来统一接入,不用每个平台写一个适配器?不太行。MCP 是一问一答的工具协议,但消息平台需要长连接实时推送、处理图片语音按钮点击等各种事件、每个平台还有自己的特有功能——这些不是"调个工具拿个返回值"能搞定的,硬塞进 MCP 反而更复杂。
三、工具系统:Agent 的手脚
代码位置:tools/
Agent 光会想不行,还得能动手。Hermes 内置了 60 多个工具,覆盖面很广:
- 写代码的:终端执行、代码沙箱、文件读写、GUI 自动化(能操控桌面应用)
- 上网的:Playwright 浏览器、CDP 协议浏览器、隐身浏览器
- 跟其他 AI 协作的:MCP 协议客户端、子代理委派(把活分给小 Agent 干)、多代理混合
- 自动化的:定时任务、看板管理
- 搞内容的:图片生成、视频生成、语音合成、网页搜索
工具怎么注册进去的?每个工具文件写一行 registry.register(),声明自己的名字、参数格式、执行函数。启动的时候,discover_builtin_tools() 会扫描 tools/ 目录下所有 Python 文件,用 AST 解析找出包含 registry.register() 的文件,自动导入注册。这种做法的好处是加新工具只需要加一个文件,不用改主程序。
工具按功能分组叫"工具集"(toolset),每个工具有一个可用性检查。比如终端工具需要 Docker 环境,浏览器工具需要 Playwright,环境不满足就自动隐藏这个工具,不让模型看到。检查结果缓存30秒,不用每次都去探测。
MCP 客户端做得比较完整。MCP 是一个让外部工具接入 Agent 的协议,Hermes 的客户端支持三种传输方式(stdio、HTTP、SSE),有自动重连、凭证脱敏、超时控制,还能让 MCP 服务端反过来请求 Agent 的 LLM 能力。
安全方面叠了好几层:危险操作要审批、路径穿越要防护、注入攻击要检测(threat_patterns.py 这个扫描器在上下文文件、MCP 结果、定时任务提示词里都在用)、运行时还能拦截危险的工具调用。
不过工具多也有代价——60多个工具的参数定义要占 20-30K token,长对话里这块开销不小。
四、技能系统:Agent 怎么学会新本事
代码位置:skills/ 目录和 tools/skills_*.py
这是 Hermes 最核心的差异点。
技能就是一个目录,里面有个 SKILL.md 文件,长这样:
---
name: plan
description: "规划模式:只写计划不执行"
version: 2.0.0
platforms: [linux, macos, windows]
---
# 规划模式
当用户要的是计划而不是执行时,使用此技能...
前面 --- 之间是元数据(名字、描述、支持的平台),后面是技能的详细指令。这个格式兼容 agentskills.io 标准,意味着技能可以在不同 Agent 之间共享。
加载技能时不是一股脑全塞进去,而是分三步:
skills_list—— 只看名字和描述(省 token)skill_view—— 需要的时候才加载完整内容- 按需加载 references/ 目录下的参考文档
这叫"渐进式披露",几十个技能如果全加载,上下文窗口就撑爆了。
但技能系统真正厉害的地方是:Agent 可以自己创建和修改技能。
skill_manager_tool.py 提供了 create、edit、patch、delete 操作。Agent 完成一个复杂任务后,可以把有效的做法提炼成技能存下来。下次遇到类似任务,直接调技能就行,不用从头摸索。
还有一套策展系统(agent/curator.py),默认每7天跑一次,在 Agent 空闲时触发。它会 fork 一个后台 Agent,用辅助模型审查所有 Agent 自创的技能:哪些该固定(pin)、哪些该归档、哪些可以合并、哪些需要修补。几条铁律:只碰 Agent 自己创建的技能,不动用户手写的和内置的;只归档不删除(归档了还能恢复);被 pin 的技能不自动操作。
安全方面,skills_guard.py 扫描技能内容防注入,skills_ast_audit.py 做 AST 级别的审计,skills_hub.py 可以从社区技能中心同步技能。
五、记忆与调度:让 Agent 记住你是谁
代码位置:agent/memory_manager.py、agent/background_review.py、cron/
技能是"怎么做某类事",记忆是"你是谁、你喜欢什么"。
MemoryManager 是记忆的管理中心,四个核心操作:
- 回合开始前:
prefetch_all—— 预取跟你当前问题相关的记忆 - 构建提示词时:
build_system_prompt—— 把记忆注入到 Agent 能看到的地方 - 回合结束后:
sync_all—— 把新发现的信息同步到记忆里 - 异步预取:
queue_prefetch_all—— 提前准备下一轮可能需要的记忆
记忆是怎么触发的?每轮对话结束后,background_review.py 会 fork 一个后台 Agent 来审查这轮对话,专门找两类信息:用户透露的个人偏好,以及用户对 Agent 行为的期望。找到了就用 memory 工具存下来。
同一轮后台审查还会检查技能——看有没有可以创建或更新的技能。提示词写得很直白:"什么都不做是错过的学习机会。"它要求 Agent 优先更新已有技能而不是创建新技能,避免技能库变成一堆碎片。
后台审查用独立的辅助模型客户端,工具白名单只有 memory 和 skill_manage,绝不碰主会话——这样后台干活不会影响你正在进行的对话。
调度系统(cron/ 目录)是内置的定时任务管理器。任务存在 ~/.hermes/cron/jobs.json,用 croniter 库解析 cron 表达式(就是 Linux 下那种 0 9 * * * 表示每天9点的语法)。任务可以绑定技能,执行时自动加载。创建任务时有安全扫描,提示词里出现"忽略之前的指令"这种注入模式会被拦截。
把记忆、技能、策展合在一起看,闭环学习的完整链路是:
后台审查对话 → 提取记忆和技能 → 策展器定期优化 → 下次对话用上改进后的能力
记忆存的是事实和偏好(“我喜欢简洁的回答”),技能存的是做事的方法论(“怎么系统地调试 Python 代码”),两者配合才构成完整的"学习"。
六、模型适配层:支持十几种大模型
代码位置:agent/ 下的各个适配器文件
Hermes 不绑定某一家模型,支持的提供商很多:Anthropic Claude、Google Gemini、AWS Bedrock、OpenAI Codex、LM Studio 本地模型,还有国产的小米 MiMo、z.ai/GLM、Kimi、MiniMax 等。
每个提供商需要一个专门的适配器文件,因为各家 API 的格式都不一样——工具调用的字段名不同、推理内容的处理方式不同、速率限制的策略也不同。conversation_loop.py 单文件将近4000行,很大一部分在处理这些差异。
凭证管理分三层:
credential_sources.py—— 从环境变量、配置文件、OAuth 等地方获取密钥credential_pool.py—— 多个 key 池化,自动轮换,避免单个 key 被限速credential_persistence.py—— 密钥持久化存储
速率限制方面,rate_limit_tracker.py 跟踪各家的限制,nous_rate_guard.py 是 Nous Portal(Hermes 自己的模型网关)的专用保护。
模型切换用 hermes model 命令一行搞定,不用改代码。
七、交互界面:终端和桌面
代码位置:tui_gateway/、ui-tui/、apps/desktop/
终端界面用 WebSocket 架构——server.py 是 WebSocket 服务器,连接前端和 Agent。前端在 ui-tui/,支持多行编辑、流式输出、斜杠命令自动补全。
桌面应用在 apps/desktop/,跟终端界面共享 apps/shared/ 的逻辑。可以连本地 Agent,也可以连远程部署的实例。
为什么终端界面也要走 WebSocket?因为桌面客户端、远程接入、本地 CLI 都走同一个协议,一套代码服务多个入口。不过在纯本地用终端的场景下,这层确实多了一点开销。
总结
Hermes 的七个模块,简单说就是:
- 对话循环——Agent 的心脏,决定了基本工作方式,外加预算控制、上下文压缩、自修复等保护机制
- 消息网关——让 Agent 能在 Telegram、微信、飞书等 15+ 个平台上用,一个进程全搞定
- 工具系统——Agent 的手脚,60+ 内置工具,自注册架构,加工具只需加一个文件
- 技能系统——Agent 可以自己创建和改进技能,策展器定期优化,这是闭环学习的一半
- 记忆与调度——Agent 主动记住你的偏好,定时任务自动执行,这是闭环学习的另一半
- 模型适配——支持十几种大模型,一行命令切换,代价是适配代码量大
- 交互界面——终端和桌面客户端,够用但深度有限
最值得关注的是闭环学习:后台审查 → 提取记忆和技能 → 策展器优化 → 下次用上。这套机制能不能真的让 Agent 越用越聪明,取决于模型能力和策展质量,方向有价值,效果还得多用才知道。
更多推荐



所有评论(0)