1. 从「会用」到「看懂」:Hermes Agent 源码结构到底解决什么问题
Hermes Agent 是一个开源 AI Agent 框架,能聊天、能调工具、能记忆、能接 Telegram、能跑定时任务。但当你用了九期之后,大概率会遇到一个瓶颈:功能都会用了,可一旦想改点什么——加个自定义工具、调一下 prompt 顺序、让 Cron 任务复用某个 skill——就不知道从哪下手。这就是源码结构和整体架构要解决的问题。
这篇笔记面向的是想读懂开源 AI Agent 框架的开发者。不逐行读源码,而是先建立一张「架构地图」:用户输入一句话之后,Hermes 内部到底发生了什么?CLI、Gateway、Cron 为什么能复用同一个 Agent 核心?Memory 和 Skills 在哪个环节进入上下文?Tools 是怎么被模型选中并执行的?MCP 工具和内置工具最终在哪里汇合?
把这些问题搞清楚,后面再读任何 Agent 框架的源码,你都能快速定位到关键链路。Hermes 的整体架构可以概括成一句话:多入口 + 一个 Agent 核心 + 多种能力层 + 长期状态管理。入口可以很多,但 Agent 核心尽量统一,这是它最核心的设计取舍。
2. 前置准备:本地跑通 Hermes Agent 并拿到模型调用凭证
在开始追源码之前,得先让 Hermes 在本地跑起来,否则你只能静态看代码,没法验证调用链。Hermes 支持多种模型 provider,配置方式是通过环境变量或配置文件指定 Base URL、API Key 和 Model ID。这里我用 TaoToken 作为模型接入层来演示,因为它兼容 OpenAI 风格的接口,配置简单,适合边读源码边验证请求。
第一步,拿到 API Key。打开 https://taotoken.net/api-keys ,注册后创建一个 Key,复制保存。注意这个 Key 只在创建时显示一次,丢了就得重新生成。
第二步,确认你要用的模型 ID。在 https://taotoken.net/models 可以看到当前支持的模型列表,选一个你熟悉的,比如 claude-sonnet 系列或 gpt 系列。记下准确的 Model ID,后面配置要用。
第三步,配置 Hermes 的模型接入。Hermes 读取模型配置的方式通常是环境变量或项目级配置文件。以环境变量为例,你需要在 shell 里设置:
export HERMES_PROVIDER=openai-compatible export HERMES_BASE_URL=https://taotoken.net/api export HERMES_API_KEY=sk-你的Key export HERMES_MODEL=claude-sonnet-4-20250514如果你用的是 Hermes 的配置文件方式,可以在项目根目录创建.hermes/config.toml:
[provider] name = "openai-compatible" base_url = "https://taotoken.net/api" api_key = "sk-你的Key" model = "claude-sonnet-4-20250514" api_mode = "chat_completions"这里api_mode是关键参数。Hermes 的 Provider Resolution 层会根据这个字段决定用哪种请求格式。OpenAI 兼容接口一般用chat_completions,Anthropic 原生接口用anthropic_messages。配错了会在调用时报 400 或 404。
第四步,验证配置是否生效。运行:
hermes --version hermes config showconfig show会打印当前解析到的 provider、base_url、model。确认 base_url 是https://taotoken.net/api,model 是你选的那个。如果这里显示的还是默认值,说明环境变量没被读到,检查 shell 是否 source 了配置文件。
3. 可复制配置:把 Agent Loop 主链路拆成可验证的模块
配置好模型接入之后,下一步是理解 Hermes 的目录结构和核心模块调用关系。我建议先抓住几个关键文件和目录,不要一上来就横向扫所有文件。
Hermes 的源码目录大致是这样的:
hermes/ ├── run_agent.py # AIAgent 核心,Agent Loop 主入口 ├── cli.py # CLI/TUI 入口 ├── model_tools.py # 工具 schema 收集与模型调用桥接 ├── agent/ │ ├── prompt_builder.py # 系统提示词分层构建 │ ├── system_prompt.py # stable 层身份与行为规范 │ ├── context_compressor.py # 上下文压缩 │ ├── prompt_caching.py # prompt 缓存 │ └── auxiliary_client.py # 辅助模型调用 ├── tools/ │ ├── registry.py # 工具注册表 │ └── *.py # 各内置工具实现 ├── gateway/ │ ├── run.py # Gateway 主循环 │ ├── session.py # 会话管理 │ ├── delivery.py # 结果投递 │ ├── pairing.py # 用户鉴权配对 │ ├── hooks.py # 生命周期钩子 │ └── platforms/ # Telegram/Slack/Discord 适配器 ├── cron/ │ ├── jobs.py # 任务定义 │ └── scheduler.py # 调度器 ├── plugins/ │ ├── memory/ # memory provider 插件 │ └── context_engine/ # context engine 插件 ├── skills/ # 内置 skills ├── optional-skills/ # 可选 skills └── tests/读源码的顺序我建议这样:先看run_agent.py里的AIAgent.run_conversation(),这是整个 Agent Loop 的主函数。然后看agent/prompt_builder.py,理解系统提示词是怎么分层的。接着看model_tools.py和tools/registry.py,搞清楚工具是怎么注册和暴露给模型的。最后再看gateway/run.py和cron/scheduler.py,理解不同入口是怎么把任务交给 AIAgent 的。
如果你想在本地验证这条链路,可以在run_agent.py的run_conversation()入口加一行日志:
import logging logging.basicConfig(level=logging.DEBUG) # 在 run_conversation 开头 logger.debug("Agent Loop start: session=%s, model=%s", session_id, self.model)然后跑一次 CLI 对话,观察日志输出。你会看到 prompt 构建、provider 解析、模型调用、工具执行、结果写回这几个阶段的顺序。这比静态读代码直观得多。
4. 验证请求:一次对话请求的完整生命周期与成功结果
配置和目录都清楚了,现在跑一次完整的请求,验证 Agent Loop 的每个环节。启动 Hermes CLI:
hermes chat输入一句需要调用工具的话,比如「帮我看看当前目录下有哪些 Python 文件」。观察终端输出和日志。
一次完整的 Hermes 对话请求会经历这些步骤:
- 用户在 CLI 输入消息,
cli.py接收输入。 - Hermes 把消息加入 conversation history。
prompt_builder.py构建系统提示词,分 stable、context、volatile 三层。- Provider Resolver 根据配置确定模型和 API 模式。
- Hermes 组装 API messages,调用模型。
- 模型返回
tool_calls,请求调用文件列表工具。 model_tools.py根据工具名找到 handler 并执行。- 工具结果写回 conversation history。
- 继续调用模型,模型生成最终回答。
- 输出结果到终端,保存 session。
- 必要时刷新 memory,上下文过长时触发压缩。
简化成一条链路就是:
User → Prompt → Model → Tool Call? → Tool Result → Model → Final Response → Persistence验证成功的标志是:终端先显示工具调用过程(比如「正在执行 list_files...」),然后显示模型基于工具结果生成的回答。日志里能看到tool_calls的解析和handle_function_call的执行记录。
如果你在model_tools.py里加一行日志:
logger.debug("Tool call received: %s, args: %s", tool_name, tool_args)就能清楚看到模型请求了哪个工具、传了什么参数。这一步验证通过,说明你的模型接入配置和 Agent Loop 主链路都是通的。
5. 常见报错排查:401、local proxy failed、reading choices、OAuth
跑通之后,你可能会在改配置或换模型时遇到一些报错。这里整理几个高频问题和排查方法。
401 Unauthorized:最常见的原因是 API Key 没配好或过期。检查HERMES_API_KEY是否设置正确,Key 有没有多余空格。如果用的是配置文件,确认api_key字段没有引号包裹问题。另外,TaoToken 的 Key 是绑定账号的,如果账号欠费或 Key 被删除,也会返回 401。去 https://taotoken.net/api-keys 重新生成一个 Key 试试。
local proxy failed / connection refused:这个报错通常出现在 base_url 配置错误时。确认HERMES_BASE_URL是https://taotoken.net/api,不要多加/v1或漏掉/api。Hermes 的 Provider Resolution 层会根据api_mode拼接具体路径,手动加路径反而会导致 404。另外检查本地网络是否能正常访问该地址,可以用curl https://taotoken.net/api/models测试连通性。
reading choices / index out of range:这个报错一般出现在模型返回格式和 Hermes 预期不一致时。比如你用了anthropic_messages模式但实际接口返回的是 OpenAI 格式。检查api_mode是否和模型匹配。OpenAI 兼容接口统一用chat_completions。如果换了模型 ID 但没改api_mode,也会出现这个错。
OAuth / credential pool 相关报错:Hermes 支持 OAuth 和 credential pool 做多凭证轮换。如果你没配置 OAuth 但代码走了 OAuth 分支,会报缺少 token。检查配置文件里是否误开了oauth = true。如果用的是 credential pool,确认池里至少有一个有效凭证。对于 TaoToken 这种 API Key 接入方式,不需要 OAuth,把oauth设为false即可。
排查时建议打开 DEBUG 日志,观察 Provider Resolution 阶段解析出的 base_url、api_mode、model 三个值是否符合预期。大部分报错都源于这三个值和实际接口不匹配。
6. 从架构地图到改造 Hermes:下一步怎么走
把 Agent Loop 主链路跑通、报错排查清楚之后,你对 Hermes 的整体架构应该有了具体认知。回到开头那张架构地图:入口层有 CLI、Gateway、Cron、ACP、Batch Runner、API Server;核心层是 AIAgent,负责 prompt 构建、provider 解析、工具调度、会话历史、上下文压缩、memory 刷新、session 持久化;能力层有内置工具、MCP 工具、Skills、Memory Providers、Plugins、Context Engines;外部环境层是文件系统、Shell、GitHub、数据库、消息平台、LLM Provider。
这张图里最重要的不是某个模块,而是中间的 AIAgent。无论用户从 CLI 发消息、从 Telegram 发消息,还是 Cron 定时触发,最终都会进入AIAgent.run_conversation()。这就是「平台无关核心」的设计思想:入口可以很多,但 Agent 核心尽量统一。
理解了这条主线,Gateway 和 Cron 就只是不同的触发入口,MCP 只是扩展了工具来源,Skills 只是在 prompt 阶段按需加载的能力文档。Memory、Session、Compression 分别解决长期事实、会话轨迹、上下文长度三个不同层次的状态问题。
下一步如果你想改造 Hermes,可以从这几个方向入手:给tools/目录加一个自定义工具文件,按 registry 规范注册;写一个自己的 Skill,放在skills/目录下;配置一个 MCP server,观察它如何转换成 Hermes tool schema;用 Cron + Gateway 做一个定时任务机器人。这些改造的前提,都是你已经清楚 Agent Loop 的主链路和各个模块的职责边界。
如果你在配置模型接入或排查报错时需要更详细的接口说明,可以查 https://taotoken.net/doc 。想直接验证模型对话效果,去 https://taotoken.net/chat 。长期跑编码类 Agent 任务的话, https://taotoken.net/coding-plan 有更合适的方案。