8. 配置管理:分层配置系统
Hermes Agent 的配置系统设计遵循 渐进覆盖 原则——用户只需要覆盖想要改变的设置,其余自动使用合理的默认值。hermes_cli/config.py 实现了一个四层配置架构,支持环境变量、YAML 配置文件、.env 密钥管理和 Profile 隔离。
8.1. 配置层级(4 层)
Hermes 的配置解析遵循严格的优先级链,高优先级的值始终覆盖低优先级的值:
优先级 |
来源 |
说明 |
|---|---|---|
1(最高) |
环境变量 |
|
2 |
|
|
3 |
|
|
4(最低) |
|
代码中的默认值字典,约 800 行 |
环境变量是最高优先级,这意味着用户可以通过 HERMES_FOO=bar hermes chat 临时覆盖任何设置,无需修改配置文件。这一设计在 CI/CD、Docker 和 NixOS 部署场景中尤为重要。
flowchart TD
A[读取配置值] --> B{环境变量已设置?}
B -->|是| C[使用环境变量值 最高优先级]
B -->|否| D{.env 文件中存在?}
D -->|是| E[load_env 返回值]
D -->|否| F{config.yaml 中存在?}
F -->|是| G[load_config → _deep_merge]
F -->|否| H[使用 DEFAULT_CONFIG 中的默认值]
classDef start fill:#dbeafe,stroke:#3b82f6,color:#1e3a8a
classDef success fill:#dcfce7,stroke:#16a34a,color:#166534
classDef warn fill:#fef9c3,stroke:#ca8a04,color:#854d0e
classDef fail fill:#fee2e2,stroke:#dc2626,color:#991b1b
classDef info fill:#f1f5f9,stroke:#64748b,color:#334155
class A start
class C fail
class E warn
class G success
class H info
8.2. DEFAULT_CONFIG 结构
DEFAULT_CONFIG 是一个约 800 行的 Python 字典,定义了 Hermes 的所有可配置选项及其默认值。以下是主要配置区块的概览:
8.2.1. 模型与 Provider
DEFAULT_CONFIG = {
"model": "", # 默认模型(空字符串 = 未配置)
"providers": {}, # 自定义 Provider 配置
"fallback_providers": [], # 回退 Provider 列表
"toolsets": ["hermes-cli"], # 启用的工具集
8.2.2. Agent 行为
"agent": {
"max_turns": 90, # 单次会话最大轮次
"gateway_timeout": 1800, # 网关不活跃超时(秒)
"tool_use_enforcement": "auto", # 工具调用强制模式
"gateway_timeout_warning": 900, # 超时前警告阈值
"gateway_notify_interval": 600, # "仍在工作"通知间隔
},
8.2.3. 终端与沙箱
"terminal": {
"backend": "local", # local | docker | singularity | modal | daytona | ssh
"timeout": 180, # 命令执行超时
"docker_image": "nikolaik/python-nodejs:python3.11-nodejs20",
"container_cpu": 1,
"container_memory": 5120, # MB
"container_persistent": True,
"persistent_shell": True, # 跨命令保持 shell 状态
},
8.2.4. 浏览器
"browser": {
"inactivity_timeout": 120,
"command_timeout": 30,
"record_sessions": False,
"allow_private_urls": False,
"cdp_url": "", # Chrome DevTools Protocol 端点
},
8.2.5. 上下文压缩
"compression": {
"enabled": True,
"threshold": 0.50, # 上下文使用率超过 50% 时触发
"target_ratio": 0.20, # 压缩到阈值的 20%
"protect_last_n": 20, # 保留最近 20 条消息不压缩
},
8.2.6. 辅助任务配置
辅助任务(auxiliary)是配置系统中最细致的部分,每个辅助任务都有独立的 Provider、模型、Base URL、API Key 和超时设置:
"auxiliary": {
"vision": {
"provider": "auto",
"model": "",
"base_url": "",
"api_key": "",
"timeout": 120,
},
"compression": { ... },
"web_extract": { "timeout": 360 },
"session_search": { ... },
"skills_hub": { ... },
"approval": { ... },
"mcp": { ... },
"flush_memories": { ... },
"title_generation": { ... },
},
8.2.7. 审批系统
"approvals": {
"mode": "manual", # manual | smart | off
"timeout": 60, # 审批等待超时
"cron_mode": "deny", # deny | approve(cron 中的危险命令)
},
8.2.8. 安全配置
"security": {
"redact_secrets": True, # 在工具输出中过滤密钥
"tirith_enabled": True, # 预执行安全扫描
"tirith_timeout": 5,
"tirith_fail_open": True, # 扫描超时时允许执行
},
8.2.9. 显示与交互
"display": {
"compact": False,
"personality": "kawaii",
"language": "", # UI 语言,空字符串 = 跟随系统
"streaming": False,
"inline_diffs": True,
"show_cost": False,
"skin": "default",
},
8.2.10. 配置版本控制
"_config_version": 19,
配置版本号 _config_version 用于配置迁移——当 Hermes 升级引入新的配置项时,版本号递增触发迁移流程。
8.3. load_config() 深度合并
load_config() 是配置加载的主入口函数。它执行以下步骤:
确保 HERMES_HOME 存在 :
ensure_hermes_home()创建~/.hermes/及其子目录深拷贝默认配置 :
copy.deepcopy(DEFAULT_CONFIG)加载用户配置 :从
~/.hermes/config.yaml读取 YAML向后兼容处理 :将旧的顶层
max_turns迁移到agent.max_turns深度合并 :
_deep_merge(config, user_config)规范化 :
_normalize_root_model_keys()和_normalize_max_turns_config()环境变量展开 :
_expand_env_vars()缓存 :将展开后的配置存入
_LAST_EXPANDED_CONFIG_BY_PATH
8.3.1. 深度合并逻辑
_deep_merge() 是一个递归合并函数,确保用户的局部覆盖不会丢失默认值:
def _deep_merge(base: dict, override: dict) -> dict:
result = base.copy()
for key, value in override.items():
if (key in result
and isinstance(result[key], dict)
and isinstance(value, dict)):
result[key] = _deep_merge(result[key], value)
else:
result[key] = value
return result
例如,如果用户在 config.yaml 中只设置了:
display:
compact: true
合并后的 display 字典将包含 compact: True (来自用户配置)以及 personality: "kawaii" 、streaming: False 等所有默认值(来自 DEFAULT_CONFIG)。
8.3.2. 环境变量展开
_expand_env_vars() 在配置值中展开 ${VAR} 格式的环境变量引用。这使得用户可以在 config.yaml 中使用动态值,而无需硬编码。
8.3.3. 配置路径
Hermes 的所有配置文件存储在 HERMES_HOME 目录(默认 ~/.hermes/)中:
config.yaml— 主配置文件.env— API Key 和密钥auth.json— OAuth Token 和 Provider 状态context_length_cache.yaml— 上下文长度缓存models_dev_cache.json— models.dev 注册表缓存SOUL.md— Agent 人格文件sessions/— 会话历史logs/— 日志文件memories/— 持久化记忆cron/— 定时任务定义
8.4. 环境变量管理
Hermes 的环境变量管理分为两个层面:加载 (从 .env 文件读取到 os.environ)和 保存 (将 API Key 等写入 .env 文件)。
8.4.1. load_env() 加载
load_env() 从 ~/.hermes/.env 读取环境变量:
读取文件所有行
_sanitize_env_lines()清理损坏的行(如合并的 KEY=VALUE 对)解析
KEY=VALUE格式(跳过注释和空行)返回
Dict[str, str]
清理逻辑处理以下边界情况:
合并行 :单行包含多个
KEY=VALUE对(如文件损坏导致)过期占位符 :值为
***或changeme等无效占位符引号处理 :正确剥离单引号和双引号
8.4.2. save_env_value() 保存
save_env_value() 将单个环境变量写入 .env 文件:
验证键名 :
_ENV_VAR_NAME_RE正则检查合法的变量名清理值 :移除换行符,检查非 ASCII 字符(API Key 应为纯 ASCII)
读取现有文件 :保留未修改的行
更新或追加 :如果键已存在则替换,否则追加到末尾
原子写入 :使用临时文件 +
os.replace()确保写入不会损坏文件
def save_env_value(key: str, value: str):
if not _ENV_VAR_NAME_RE.match(key):
raise ValueError(f"Invalid environment variable name: {key!r}")
value = value.replace("\n", "").replace("\r", "")
value = _check_non_ascii_credential(key, value)
# ... 读取、更新、原子写入 ...
8.4.3. get_env_value() 查询
get_env_value() 按优先级查找值:
``os.environ`` — 运行时环境变量
.env 文件 — 通过
load_env()读取
def get_env_value(key: str) -> Optional[str]:
if key in os.environ:
return os.environ[key]
env_vars = load_env()
return env_vars.get(key)
8.4.4. 可选环境变量注册表
OPTIONAL_ENV_VARS 字典定义了 Hermes 支持的所有可选环境变量,包含元数据:
description:变量用途描述prompt:设置向导中的提示文本url:获取 API Key 的链接password:是否为敏感值(显示时脱敏)tools:哪些工具需要此变量category:分类(provider / tool / messaging)advanced:是否为高级选项
这个注册表用于:
hermes setup向导中展示可选配置配置迁移时检测新增的环境变量
hermes config命令展示当前配置
8.5. Profile 系统
Hermes 支持通过 --profile 标志创建隔离的配置环境。每个 Profile 拥有独立的 HERMES_HOME 目录,包含自己的配置文件、认证状态和会话历史。
8.5.1. HERMES_HOME 覆盖
Profile 的核心机制是 HERMES_HOME 目录的覆盖:
默认:
~/.hermes/Profile:
~/.hermes/profiles/<profile_name>/
当用户使用 --profile work 启动 Hermes 时,所有配置文件的读写都指向 ~/.hermes/profiles/work/ 目录,完全与默认配置隔离。
8.5.2. 隔离的资源
每个 Profile 包含以下独立资源:
config.yaml— 配置文件(不同的 Provider、模型、工具集).env— API Key(不同的凭证)auth.json— OAuth Token(不同的认证状态)sessions/— 会话历史(独立的会话存储)memories/— 持久化记忆(不同的 Agent 记忆)logs/— 日志文件
Profile 的典型使用场景:
工作/个人分离 :使用不同的 Provider 和模型配置
多租户部署 :每个租户使用独立的 Profile
测试环境 :不影响生产配置的实验环境
区域隔离 :中国/国际使用不同的 Provider 配置
8.6. 配置迁移
Hermes 的配置系统随版本演进。_config_version 字段跟踪配置版本,当用户从旧版本升级时触发迁移流程。
8.6.1. 版本追踪
ENV_VARS_BY_VERSION 字典记录了每个版本引入的环境变量:
ENV_VARS_BY_VERSION = {
3: ["FIRECRAWL_API_KEY", "BROWSERBASE_API_KEY"],
4: ["VOICE_TOOLS_OPENAI_KEY", "ELEVENLABS_API_KEY"],
5: ["WHATSAPP_ENABLED", "SLACK_BOT_TOKEN", ...],
10: ["TAVILY_API_KEY"],
11: ["TERMINAL_MODAL_MODE"],
}
8.6.2. 迁移流程
当 Hermes 检测到用户的 _config_version 低于当前版本时:
识别新增变量 :计算当前版本与用户版本之间的差异
提示用户 :在设置向导中展示新增的可选变量
更新版本号 :将
_config_version更新为当前版本
8.6.3. 向后兼容处理
配置系统包含多个向后兼容适配:
顶层 ``max_turns`` :旧版本将
max_turns放在配置顶层,新版本移到agent.max_turns。load_config()自动迁移。根级 ``model`` :支持
model: "gpt-4o"的简写格式和model: {default: "gpt-4o", provider: "openrouter"}的完整格式。环境变量合并 :
.env文件中的变量在启动时加载到os.environ,与运行时环境变量合并。
8.6.4. Managed 模式
当 Hermes 运行在 NixOS 或 Homebrew 管理模式下(通过 HERMES_MANAGED 环境变量或 .managed 标记文件检测),配置管理有以下限制:
配置文件由包管理器拥有,不允许直接编辑
目录权限由激活脚本设置(setgid + group-writable, 2770)
使用
umask(0o007)确保新文件是 group-writable (0660)升级通过
nixos-rebuild switch或brew upgrade执行
8.6.5. 容器感知
在 Docker/Podman 容器中运行时,配置系统有以下特殊处理:
跳过文件权限设置(
_is_container()检测/.dockerenv或/proc/1/cgroup)支持
HERMES_SKIP_CHMOD=1强制跳过权限设置容器执行模式通过
~/.hermes/.container-mode配置文件管理
8.7. 国际化(i18n)支持
Hermes Agent 内置了国际化框架(agent/i18n.py),支持将 UI 静态消息翻译为多种语言。
该框架基于 YAML 语言包文件,通过 display.language 配置选项控制当前界面语言。
8.7.1. 支持的语言
语言代码 |
语言 |
文件路径 |
|---|---|---|
|
English(默认) |
|
|
中文 |
|
|
日本語 |
|
|
Deutsch |
|
|
Español |
|
|
Français |
|
|
Türkçe |
|
|
Українська |
|
8.7.2. 配置方法
在 config.yaml 中设置 display.language 即可切换 UI 语言:
display:
language: zh # 将界面语言切换为中文
当 language 为空字符串(默认值)时,Hermes 会自动检测系统语言并使用匹配的语言包;
如果没有匹配的语言包,则回退到英语。
8.7.3. i18n 框架实现
agent/i18n.py 提供了以下核心能力:
语言包加载 :启动时读取
locales/<lang>.yaml,解析为键值对字典消息翻译 :通过翻译键(如
"welcome_message")获取当前语言的翻译文本回退机制 :当目标语言缺少某个翻译键时,自动回退到英语(
en.yaml)动态切换 :支持运行时根据
display.language配置变更语言
YAML 语言包采用嵌套键结构:
# locales/zh.yaml 示例
welcome: "欢迎使用 Hermes"
agent:
thinking: "思考中..."
waiting: "等待响应..."
errors:
timeout: "请求超时"
auth_failed: "认证失败"
8.8. Model Aliases 配置
Hermes 支持通过 config.yaml 中的 model.aliases 字段定义自定义模型名称映射:
model:
aliases:
fast: "openrouter/anthropic/claude-3.5-haiku"
smart: "openrouter/anthropic/claude-4-opus"
vision: "openrouter/google/gemini-2.0-flash"
local: "ollama/llama3.1:8b"
定义后,用户可以直接使用别名:
hermes chat --model fast
别名解析在 load_config() 流程中完成,发生在深度合并之后。
别名可以被环境变量覆盖,遵循标准的配置优先级链。
8.9. 安全与权限
8.9.1. 文件权限
Hermes 对配置文件和目录实施严格的权限控制:
~/.hermes/:0700(仅所有者可读写执行)config.yaml、.env、auth.json:0600(仅所有者可读写)在容器和 managed 模式下跳过权限设置
8.9.2. 密钥脱敏
redact_key() 函数用于显示时脱敏 API Key:
def redact_key(key: str) -> str:
if not key:
return "(not set)"
if len(key) < 12:
return "***"
return f"{key[:4]}...{key[-4:]}"
8.9.3. 非 ASCII 凭证检查
_check_non_ascii_credential() 在保存 API Key 时检查非 ASCII 字符,因为 API Key 应该只包含 ASCII 字符。非 ASCII 字符可能导致认证失败,且通常表示复制粘贴错误。
8.10. 总结
Hermes 的配置管理系统是一个精心设计的分层架构,其核心原则是:
渐进覆盖 :四层优先级链确保用户只需覆盖想要改变的设置
深度合并 :递归合并保留未覆盖的默认值
安全存储 :API Key 存储在权限受限的 .env 文件中
Profile 隔离 :通过目录隔离实现完全独立的配置环境
向后兼容 :自动迁移旧版配置格式,平滑升级路径
容器友好 :在 Docker/NixOS 环境中自动适配权限和行为
国际化 :内置 i18n 框架,支持 8 种语言的 UI 翻译
模型别名 :通过
model.aliases简化模型指定,提升工作效率