一个 Agent 从 Demo 走向长期使用后,模型、端点、工具开关、压缩阈值、辅助模型和委派凭据都会变成配置。继续把它们散落在代码里,每次切换环境都像一次小型改版。
Hermes 的配置系统关注的不是“能填多少参数”,而是默认值、覆盖关系、秘密和运行上下文如何各归其位。
这篇只回答一个问题:Agent 参数越来越多时,怎样让配置可修改、可继承、可隔离,同时避免密钥泄露?
Hermes 给出的答案是:Hermes 用完整默认配置定义基线,用户 YAML 只覆盖关心字段,深度合并保留未覆盖项,密钥放入独立 .env,并用 Profile 隔离运行上下文。
完整默认值,让用户只描述差异
Hermes 在代码中维护一份完整 DEFAULT_CONFIG。它既给每个选项提供默认值,也定义系统承认哪些配置位置。用户文件不用复制整份模板,只写自己想修改的字段。
这种设计降低了首次配置成本,也让新增选项可以在不改旧用户文件的情况下获得默认行为。产品界面对应的不是一张必须填满的表,而是一套“继承基线、局部覆盖”的状态。
嵌套配置必须深度合并
普通 dict.update 会把整个子对象替换掉。用户只修改 compression.threshold,可能意外丢掉同层 enabled 等字段。Hermes 使用深度合并,让覆盖发生在叶子节点。
这类错误最难排查,因为配置文件看起来只改了一项,运行结果却像关闭了另一项。配置产品因此需要展示最终生效值,而不只是用户显式填写的值。
结构化配置与秘密分开保存
模型名称、超时和功能开关适合进入 config.yaml;API key 只放 .env。前者需要可读、可迁移,后者需要避免提交、分享和日志泄露。
Hermes 还允许视觉、压缩、搜索和审批等辅助任务使用不同模型。它们不是主模型的附属字符串,而是各自具有 provider、model、endpoint、key 和 timeout 的执行单元。
Profile 不是主题切换,而是运行上下文隔离
写代码和写文章可能使用不同人设、记忆、工具集、模型和凭据。如果只切换一两个参数,剩余状态仍可能串场。Hermes 用 Profile 隔离整套上下文。
配置结构还带版本号,用于未来迁移。没有版本管理,系统升级后只能猜用户文件属于哪个时代,默认值和废弃字段也无法可靠处理。
配置最终如何变成运行参数
Hermes 启动时先取得完整 DEFAULT_CONFIG,再读取当前 Profile 的 config.yaml。两者通过深度合并得到结构化配置:用户明确写过的叶子字段覆盖默认值,其余字段继续继承基线。
接下来系统读取 .env,解析配置中的环境变量引用。这样,模型名称、阈值和开关可以留在 YAML,API key 则始终从秘密文件进入运行环境。
配置带有 _config_version。旧版本文件进入新版本程序时,需要先迁移再使用,避免新增字段缺失或旧字段语义变化。辅助模型与委派模型也在同一配置体系中分别声明,不必被迫沿用主模型。
所以配置加载不是“读一个文件”,而是一条 默认值 → Profile 覆盖 → 秘密注入 → 版本迁移 → 运行实例 的链路。产品界面如果只展示用户填写值,而不展示最终生效值,排障仍然会回到猜测。
三个容易走偏的地方
- 把 API key 写进 YAML,配置分享或提交时一起泄露。
- 用浅层 update 覆盖嵌套对象,造成未修改字段意外消失。
- 多个 Profile 共用秘密或记忆目录,表面切换、实际串场。
产品经理可以怎样评审
评审这项能力,可以先检查三条:
- 配置页应展示“来源值”和“最终生效值”,否则用户无法理解覆盖链。
- 秘密、结构化参数和运行时状态应采用不同存储与权限策略。
- Profile 的验收标准是上下文完整隔离,不是多一个下拉选择框。
原材料给出配置系统的核心结构,本文不枚举全部字段,也不推断未说明的加密、审计和企业密钥管理能力。
