第四章
从入口开始:程序是如何启动的
🎧 语音播放
追踪从命令行到 Agent 初始化的完整流程
🎯 本章目标
- 理解 Hermes-Agent 的启动流程
- 掌握命令行参数如何被解析
- 理解各模块的初始化顺序
📚 前置知识
- 第三章的架构认知
- Python 基础(import、类初始化)
💡 学完收获
- 能追踪一条用户消息的完整处理路径
- 理解各模块的初始化顺序和依赖关系
- 为调试和扩展打下基础
4.1 启动入口:hermes 命令
当你运行 hermes 时,发生了什么?
启动流程 Startup Flow
flowchart TD
A["⌨️ hermes 命令"] --> B["🔧 hermes_cli/main.py"]
B --> C["📂 加载环境变量 ~/.hermes/.env"]
C --> D["🎛️ HermesCLI.__init__"]
D --> E["🧠 AIAgent.__init__"]
E --> F["🔍 工具自动发现"]
E --> G["📝 构建系统 Prompt"]
E --> H["💾 初始化状态存储"]
F --> I["✅ 就绪,等待用户输入"]
style A fill:#dbeafe,stroke:#2563eb
style I fill:#d1fae5,stroke:#059669
1. 命令解析
首先,hermes 命令被 hermes_cli/main.py 处理:
hermes_cli/main.py
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
subparsers.add_command("model", ...)
subparsers.add_command("gateway", ...)
# ...
# 无子命令时 → 启动 CLI
if __name__ == "__main__":
hermes_cli()
2. 环境加载
在初始化任何功能之前,Hermes 先加载环境变量:
run_agent.py (行 48-59)
# Load .env from ~/.hermes/.env first, then project root as dev fallback.
from hermes_cli.env_loader import load_hermes_dotenv
_hermes_home = get_hermes_home()
_project_env = Path(__file__).parent / '.env'
_loaded_env_paths = load_hermes_dotenv(
hermes_home=_hermes_home,
project_env=_project_env
)
💡 为什么先加载 .env?
Hermes 需要 API 密钥才能工作,所以必须最早加载环境变量。后面的模块初始化可能已经需要调用 LLM 了。
4.2 核心类初始化
AIAgent.__init__ 流程
当你创建一个 AIAgent 实例时,初始化顺序是:
1
IterationBudget
设置最大迭代次数(默认 90)
2
OpenAI 客户端
配置 API 密钥和 base_url
3
加载工具
调用 get_tool_definitions()
4
构建 Prompt
调用 prompt_builder
5
初始化组件
ContextCompressor 等
关键代码片段
run_agent.py (简化)
class AIAgent:
def __init__(self, model="anthropic/claude-opus-4.6",
max_iterations=90, ...):
# 1. 初始化迭代预算
self.iteration_budget = IterationBudget(max_iterations)
# 2. 创建模型客户端
self.client = OpenAI(api_key=api_key, base_url=base_url)
# 3. 获取工具定义
tool_schemas = get_tool_definitions(
enabled_toolsets=enabled_toolsets,
disabled_toolsets=disabled_toolsets
)
# 4. 构建系统 Prompt
system_prompt = self._build_system_prompt()
# 5. 初始化压缩器
self.context_compressor = ContextCompressor(model=model)
4.3 工具的自动发现
Hermes-Agent 使用自动发现机制找到所有工具:
工具发现机制 Tool Discovery
flowchart LR
A["📦 model_tools.py 加载"] --> B["🔄 _discover_tools()"]
B --> C["📥 import tools.web_tools"]
B --> D["📥 import tools.terminal_tool"]
B --> E["📥 import tools.file_tools"]
B --> F["📥 ..."]
C --> G["📝 registry.register()"]
D --> G
E --> G
F --> G
G --> H["🗂️ 工具注册表"]
style B fill:#dbeafe,stroke:#2563eb
style H fill:#ede9fe,stroke:#7c3aed
4.4 命令行处理流程
当你输入一条消息时,CLI 的处理流程是:
用户输入消息
→
HermesCLI.process_message()
验证命令(如 /help、/model)
→
AIAgent.run_conversation()
开始 Agent 循环
→
LLM 调用 + 工具执行
→
返回最终响应
→
HermesCLI.display_response()
显示给用户
4.5 配置加载
Hermes 使用多层配置,优先级从高到低:
命令行参数
最高优先级
环境变量
~/.hermes/.env
配置文件
~/.hermes/config.yaml
↓
默认配置
最低优先级
4.6 工具集(Toolsets)机制
Hermes 使用"工具集"来组织工具:
web
web_search, web_extract
terminal
terminal, process
file
read/write/patch/search
browser
浏览器自动化
skills
技能管理
cronjob
定时任务
💡 为什么要用工具集?
想象你做一个纯研究的 Agent,只需要网络搜索功能;做一个编程的 Agent,需要文件和终端功能。工具集让按需启用成为可能。
4.7 易错点:新手常见问题
⚠️ 问题 1:API 密钥没生效
如果提示 "No API key found",检查:
~/.hermes/.env文件是否存在- 文件格式是否正确(
KEY=VALUE,每行一个) - 是否有多余的空格或引号
⚠️ 问题 2:工具未找到
如果某个工具不可用,检查:
- 对应的 toolset 是否在
enabled_toolsets中 - 是否有缺少的环境变量(如
SEARCH_API_KEY)
📚 本章总结
本章重点:
- 启动流程:
hermes→HermesCLI→AIAgent - 环境加载:最早加载
.env文件 - 工具发现:模块 import 时自动注册
- 配置优先级:命令行 > 环境变量 > 配置文件 > 默认值
一句话记忆点: Hermes 的启动就像做一杯咖啡:先准备好豆子(env),再启动机器(Agent),最后出品(响应)
延伸思考
- 为什么 Hermes 选择在模块 import 时注册工具,而不是用配置文件?
- 如果需要支持"热加载"新工具(即不重启添加工具),你会如何设计?
- 配置优先级这样设计的原因是什么?有什么优缺点?