Hermes-Agent源码学习大纲

2 阅读8分钟

Hermes Agent 项目学习要点

目标:快速建立对 Hermes Agent 的代码地图、核心流程和常见改动入口的理解。 配套阅读:learn/hermes-architecture.md 偏架构全景;本文偏学习路线与开发抓手。


1. 先建立一个整体心智模型

Hermes Agent 可以理解成四层:

  1. 入口层:CLI、TUI、Gateway、Dashboard、Python library。
  2. 核心 Agent 层AIAgent 负责组 prompt、调用模型、处理 tool call、维护会话循环。
  3. 能力层:tools、toolsets、skills、memory、MCP、plugins、cron、delegation。
  4. 持久化与运行环境层:SQLite 会话库、~/.hermes 配置目录、profile 隔离、日志、终端后端。

最重要的一句话:用户输入最终都会被整理成 OpenAI 风格 messages,送进 AIAgent.run_conversation();模型返回 tool calls 时,经 model_tools.handle_function_call() 分发到具体工具。


2. 推荐阅读顺序

第一轮:能跑起来

  1. README.zh-CN.md:了解产品定位、安装、主要命令。
  2. pyproject.toml:看依赖分层、extras、命令入口。
  3. hermes_cli/main.py:理解 hermes 命令如何进入不同子命令。
  4. cli.py:理解交互式 CLI 的主流程。

重点问题:

  • hermeshermes-agenthermes-acp 分别从哪里进入?
  • CLI 命令和 slash command 是怎么分发的?
  • 哪些依赖是 core,哪些是按功能懒加载或 extra?

第二轮:看懂一次对话

  1. run_agent.py
    • AIAgent.__init__
    • AIAgent.run_conversation
    • AIAgent.chat
  2. model_tools.py
    • handle_function_call
    • tool schema 获取与工具调用分发
  3. tools/registry.py
    • discover_builtin_tools
    • ToolRegistry.register
    • get_definitions
  4. toolsets.py
    • _HERMES_CORE_TOOLS
    • toolset 暴露规则

重点问题:

  • system prompt、history、memory、skills 在什么时候拼进 messages?
  • 模型返回 tool_calls 后,代码如何找到对应 handler?
  • 为什么一个 tool 文件自动注册后,还需要出现在 toolset 中才会暴露给 Agent?

第三轮:理解状态与配置

  1. hermes_cli/config.py
    • DEFAULT_CONFIG
    • OPTIONAL_ENV_VARS
    • load_config
  2. cli.py
    • load_cli_config
    • save_config_value
  3. gateway/run.pygateway/config.py
    • Gateway 对配置的读取路径不同于 CLI。
  4. hermes_constants.py
    • get_hermes_home
    • display_hermes_home
  5. hermes_state.py
    • SessionDB
    • SQLite + FTS5 会话存储。

重点问题:

  • config.yaml.env 各自应该放什么?
  • profile 隔离如何影响 ~/.hermes 路径?
  • CLI、普通子命令、Gateway 三条配置加载路径有什么差异?

第四轮:看扩展系统

  1. skills/optional-skills/
    • 内置技能与可选技能的区别。
  2. tools/skills_hub.py
    • optional skills 的安装来源。
  3. hermes_cli/plugins.py
    • general plugin discovery。
  4. plugins/model-providers/
    • provider 插件如何注册 ProviderProfile
  5. agent/memory_manager.pyplugins/memory/
    • memory provider 抽象与内置后端。
  6. mcp_serve.py 与 MCP 相关工具
    • 外部 MCP 工具如何接入。

重点问题:

  • 普通自定义能力优先走 plugin,什么时候才改 core tools?
  • skill 是如何以用户消息形式注入,而不是 system prompt?
  • model provider 插件为什么是独立 discovery,而不是普通 PluginManager 直接 import?

第五轮:看界面与多平台

  1. hermes_cli/commands.py
    • COMMAND_REGISTRY 是 slash command 的单一事实源。
  2. ui-tui/src/
    • Ink TUI 前端。
  3. tui_gateway/server.py
    • TUI 与 Python Agent 的 JSON-RPC 桥。
  4. gateway/run.py
    • 消息网关主调度。
  5. gateway/platforms/
    • Telegram、Discord、Slack 等平台适配器。
  6. web/hermes_cli/web_server.py
    • Dashboard,其中 /chat 嵌入真实 hermes --tui,不要另写一套聊天界面。

重点问题:

  • 一个 slash command 如何同时影响 CLI、Gateway help、Telegram menu、Slack routing 和自动补全?
  • Dashboard 为什么复用 TUI,而不是在 React 里重写 chat?
  • 平台消息如何转成统一事件再交给核心 Agent?

3. 核心调用链

CLI 对话链路

hermes
  -> hermes_cli/main.py
  -> cli.py / HermesCLI
  -> AIAgent.run_conversation()
  -> model client chat completion
  -> model_tools.handle_function_call()
  -> tools/registry.py 中的 handler
  -> tool result append 回 messages
  -> 继续模型调用或输出 final response

TUI 链路

hermes --tui
  -> Node / Ink UI
  -> stdio JSON-RPC
  -> tui_gateway/server.py
  -> AIAgent + tools + sessions
  -> events 回传给 Ink 渲染

Gateway 链路

platform message
  -> gateway/platforms/<platform>
  -> gateway/run.py
  -> session / user / platform context
  -> AIAgent.run_conversation()
  -> platform adapter send response

Tool 注册与暴露链路

tools/registry.py
  <- tools/*.py 顶层 registry.register()
  <- model_tools.py 触发 discover_builtin_tools()
  <- toolsets.py 决定哪些工具集暴露
  <- run_agent.py 把 tool schemas 传给模型

4. 常见开发任务入口

新增 slash command

改动入口:

  • hermes_cli/commands.py:添加 CommandDef
  • cli.py:在 HermesCLI.process_command() 中处理。
  • gateway/run.py:如果消息平台也支持,添加 gateway handler。

注意点:

  • alias 只需要加在 CommandDef.aliases
  • help、autocomplete、Telegram menu、Slack mapping 都从 COMMAND_REGISTRY 派生。
  • cli_onlygateway_onlygateway_config_gate 决定不同入口是否可见。

新增 core tool

优先判断:如果只是本地自用或第三方能力,优先写 plugin,不要改 core。

必须改 core 时:

  • tools/<name>.py:顶层调用 registry.register()
  • toolsets.py:把工具挂到对应 toolset 或 _HERMES_CORE_TOOLS

注意点:

  • handler 必须返回 JSON 字符串。
  • schema 中涉及 Hermes home 路径时,用 display_hermes_home()
  • 持久化状态路径用 get_hermes_home(),不要硬编码 ~/.hermes
  • check_fn 会被 TTL 缓存,配置变更后必要时调用 cache invalidation。

新增配置项

改动入口:

  • hermes_cli/config.pyDEFAULT_CONFIG
  • 如为 secret/API key,加到 OPTIONAL_ENV_VARS,放 .env
  • 如为普通设置,放 config.yaml,不要放 .env
  • Gateway 需要读取时,检查 gateway/run.pygateway/config.py 是否走了同一套默认值。

注意点:

  • 添加普通新 key 通常不需要 bump _config_version
  • 只有重命名、结构迁移、语义迁移时才需要 bump。

新增 model provider

改动入口:

  • plugins/model-providers/<name>/__init__.py
  • 注册 ProviderProfile(...)

注意点:

  • model provider 是懒 discovery。
  • 用户同名 provider 插件可覆盖 bundled provider。
  • 普通 PluginManager 不直接 import model provider,避免重复注册。

新增 memory provider

原则:

  • 不再新增 in-tree plugins/memory/<name>/
  • 新 memory backend 应作为独立 plugin repo 安装到 ~/.hermes/plugins/ 或通过 pip entry point 暴露。

需要理解:

  • agent/memory_provider.py 定义 ABC。
  • agent/memory_manager.py 负责 orchestrate。
  • post_setup() 可接入 setup wizard。

修改 Dashboard Chat

关键规则:

  • 主聊天体验属于 embedded hermes --tui
  • Dashboard /chat 通过 PTY bridge 嵌入真实 TUI。
  • 不要在 React 中重写 transcript、composer、slash command 流程。

可做的:

  • 侧边栏、状态面板、工具调用 inspector、模型选择弹窗等辅助 UI。
  • 新聊天能力优先扩展 Ink TUI,让 Dashboard 自动受益。

5. 测试与验证抓手

常用命令:

scripts/run_tests.sh
python -m pytest tests/ -q
python -m pytest tests/<target_test>.py -q
cd ui-tui && npm run type-check
cd ui-tui && npm test
cd ui-tui && npm run build

选择测试范围:

  • 改 tool registry / toolsets:跑 registry、model_tools、tool exposure 相关测试。
  • 改 slash command:跑 commands、CLI dispatch、gateway command 相关测试。
  • 改 config:跑 config merge、migration、gateway config 相关测试。
  • 改 TUI:跑 ui-tui 的 type-check、vitest、build。
  • 改 Gateway:跑目标 platform adapter 测试,再跑 shared gateway tests。

开发时优先用 rg 定位:

rg -n "COMMAND_REGISTRY|resolve_command|process_command"
rg -n "registry.register|handle_function_call|get_definitions"
rg -n "DEFAULT_CONFIG|load_config|load_cli_config"
rg -n "get_hermes_home|display_hermes_home"

6. 容易踩坑的地方

  1. 工具自动发现不等于自动暴露。 tools/*.py 被 import 后会注册 schema,但 agent 是否看到这个 tool 由 toolset 决定。

  2. CLI 与 Gateway 配置加载路径不同。 CLI 常走 load_cli_config()load_config();Gateway 还会直接读 YAML。新增配置时两边都要核对。

  3. .env 只放 secrets。 超时、路径、开关、阈值这类非 secret 应进入 config.yaml

  4. profile-aware path 很重要。 代码里不要直接写 Path.home() / ".hermes",应使用 get_hermes_home()

  5. 依赖策略要保守。 当前 pyproject.toml 里 core 依赖使用精确 pin,并通过 uv.lock 固化解析结果。新增依赖前先判断是否真的属于 core;provider/search/TTS/image/messaging 等可选能力通常应该放 extras 或 lazy install。

  6. 插件不要硬改 core。 如果 plugin 需要 core 没暴露的能力,优先扩展通用 plugin surface,而不是把某个插件名字写进 run_agent.pycli.pygateway/run.py

  7. Dashboard chat 不要重复造。 主聊天在 TUI,Dashboard 嵌入 TUI。React 只做辅助结构化界面。

  8. 跨平台代码少用 POSIX 假设。 优先用 pathlibtempfilepsutil 等跨平台方案,避免 /tmpos.kill(pid, 0)fcntltermios 等直接扩散。


7. 代码地图速查

主题入口文件
Agent 主循环run_agent.py
工具发现与调用model_tools.py, tools/registry.py
工具集定义toolsets.py
CLI 主体cli.py
CLI argparse 入口hermes_cli/main.py
slash command 注册hermes_cli/commands.py
配置默认值hermes_cli/config.py
Hermes home/profile 路径hermes_constants.py
日志hermes_logging.py
会话存储hermes_state.py
Gateway 主循环gateway/run.py
平台适配器gateway/platforms/
TUI 前端ui-tui/src/
TUI Python 桥tui_gateway/server.py
Dashboard APIhermes_cli/web_server.py
Dashboard 前端web/src/
Croncron/jobs.py, cron/scheduler.py
Skillsskills/, optional-skills/, agent/skill_commands.py
Pluginshermes_cli/plugins.py, plugins/
Model providersplugins/model-providers/, providers/
Memoryagent/memory_manager.py, agent/memory_provider.py, plugins/memory/
Terminal backendstools/environments/
Teststests/

8. 建议的学习练习

练习 1:追踪 /model

目标:理解 slash command 到模型切换的完整链路。

  1. hermes_cli/commands.py 搜索 CommandDef("model"
  2. cli.py 搜索 canonical == "model"
  3. 继续追踪 provider/model 如何写入 session 或 config。
  4. 看 Gateway 中 /model 是否复用相同 resolver。

练习 2:追踪一个工具

目标:理解 tool schema 与 handler。

  1. tools/ 中选一个简单工具,搜索 registry.register
  2. 找到 schema、handler、check_fn。
  3. toolsets.py 找它属于哪个 toolset。
  4. model_tools.py 追踪调用时如何解析 args 与包装结果。

练习 3:追踪一次 TUI 输入

目标:理解 Ink 与 Python 后端边界。

  1. ui-tui/src/app.tsx 找 prompt submit。
  2. 看 JSON-RPC method 名称。
  3. tui_gateway/server.py 搜索对应 method。
  4. 追踪它如何进入 AIAgent,又如何把 delta/event 发回 UI。

练习 4:新增一个只读配置项

目标:熟悉配置默认值和读取路径。

  1. DEFAULT_CONFIG 中加一个 harmless key。
  2. 找 CLI 与 Gateway 是否都能看到。
  3. 写一个小测试验证 deep merge 行为。
  4. 确认不需要 bump _config_version

9. 一句话总结

学习 Hermes Agent 不要从所有文件平均扫起。先抓住三条主线:

  • 对话主线run_agent.py -> model_tools.py -> tools/registry.py
  • 入口主线hermes_cli/main.py / cli.py / gateway/run.py / tui_gateway/server.py
  • 扩展主线toolsets.py / skills/ / plugins/ / hermes_cli/config.py

这三条线通了,后面的 Gateway、TUI、Cron、Memory、Provider、Dashboard 都是在同一个核心循环外面接能力。