01 - 项目架构与模块依赖链
学习目标
本章聚焦 Hermes Agent 的顶层架构与模块依赖关系。你将了解:
- 工具注册的导入链如何实现自动发现
- 核心巨型模块的职责划分
- CLI 界面的 Rich + prompt_toolkit 架构
- 数据驱动的 Skin 引擎如何扩展
前置知识
本章涉及 Agent 架构的通用原理,建议先阅读:
下文直接聚焦 Hermes 的工程实现。
项目实践
文件依赖链:自底向上的自动注册
关键机制:model_tools.py 在模块加载时调用:
discover_builtin_tools():使用 AST 解析找到所有registry.register(...)调用,按需 import 工具文件discover_plugins()(try/except 包裹):发现 bundled / user / project / pip entry-point 四类插件
导入时序陷阱:discover_plugins() 作为 model_tools.py 导入的副作用运行。不导入 model_tools.py 的代码路径无法看到插件工具。入口文件(run_agent.py、cli.py、gateway/run.py 等)都直接 import model_tools.py,确保工具发现被触发。
核心巨型模块
Hermes 选择将核心逻辑集中在少数大型文件中,而非拆分为大量微模块:
| 文件 | 行数 | 职责 |
|---|---|---|
run_agent.py | ~4410 | AIAgent 核心类:对话循环、预算、中断、压缩 |
cli.py | ~15089 | HermesCLI 编排器:Rich 面板 + prompt_toolkit 输入 + 动画 |
gateway/run.py | ~18556 | 消息网关:平台路由、会话管理、命令分发 |
hermes_state.py | 数百行 | SQLite SessionDB + FTS5 全文搜索 |
hermes_constants.py | 数十行 | Profile 感知路径配置 |
hermes_logging.py | 数十行 | Profile 感知日志 |
设计逻辑:核心逻辑集中减少了跨模块依赖追踪的成本。通过 registry 和插件系统实现解耦,而非通过文件拆分。
CLI 界面架构
cli.py 的依赖链:
Rich 面板(输出渲染) ↑prompt_toolkit(交互式输入) ↑KawaiiSpinner(动画组件) ↑KawaiiSpinner 状态反馈(皮肤引擎驱动)CLI 使用 Rich 进行富文本输出(颜色、表格、进度条),prompt_toolkit 处理用户输入(补全、历史、键绑定)。KawaiiSpinner 是自定义的终端动画组件,由皮肤引擎驱动颜色和样式。
数据驱动 Skin 引擎
hermes_cli/skin_engine.py(927 行)实现了数据驱动的主题系统:
内置皮肤(9 种):
| 皮肤 | 风格 |
|---|---|
default | 金色/kawaii |
ares | 红铜色战神 |
mono | 灰度 |
slate | 冷蓝 |
daylight | 亮色主题 |
warm-lightmode | 暖色亮色 |
poseidon | 海洋蓝 |
sisyphus | austere 灰度 |
charizard | 火山橙 |
用户皮肤:~/.hermes/skins/<name>.yaml 放置即用,自动覆盖同名内置皮肤。
SkinConfig 数据类:name、description、colors、spinner、branding、tool_prefix、tool_emojis、banner_logo、banner_hero。
激活方式:/skin <name> CLI 命令或 display.skin 配置项。
问题与规避
巨型模块的维护成本
问题:15000+ 行的 cli.py 和 18000+ 行的 gateway/run.py 难以导航和理解。
对策:通过内部函数分组和类型注解保持可读性。核心逻辑通过 registry 和插件系统解耦,巨型文件主要承担编排职责而非实现细节。
皮肤缺失导致回退
问题:配置了不存在的皮肤名称,导致颜色方案回退到默认。
对策:皮肤引擎在皮肤缺失时 fallback 到 default,并以 WARNING 级别记录日志。
设计取舍
巨型模块 vs 微模块拆分
| 方案 | 优势 | 代价 |
|---|---|---|
| 巨型模块(Hermes) | 减少跨文件跳转、依赖关系清晰、import 链简单 | 文件过大难以导航、合并冲突概率高 |
| 微模块拆分 | 职责清晰、易于测试 | 模块间依赖复杂、import 链难以追踪 |
Hermes 选择巨型模块是因为其目标用户是独立开发者和小型团队,代码可读性主要通过函数分组和类型注解实现,而非文件拆分。
参考来源
- 源码验证:
tools/registry.py、model_tools.py、run_agent.py、cli.py、hermes_cli/skin_engine.py、AGENTS.md