02 - 多模型传输层:插件化传输适配
学习目标
本章聚焦 Hermes Agent 的多模型传输层架构。你将了解:
ProviderTransport抽象基类的四步数据路径api_mode如何调度到不同传输协议- 辅助模型自动路由如何为 curator、vision、embedding 等任务分配小模型
- Model Provider 插件的懒发现与用户覆盖机制
前置知识
本章涉及多模型适配的通用原理,建议先阅读:
下文直接聚焦 Hermes 的传输层具体实现。
项目实践
传输层插件:数据路径抽象
Hermes 将多模型支持拆分为两个独立的关注点:
Transport 插件(agent/transports/)只负责数据路径转换:
class ProviderTransport: def convert_messages(messages) -> ... # 格式转换 def convert_tools(tools) -> ... # 工具 schema 转换 def build_kwargs(model, messages, ...) -> dict # 构建 API 请求参数 def normalize_response(response) -> ... # 标准化为 NormalizedResponse关键设计:Transport 不拥有客户端生命周期、不管理流式连接、不处理凭证刷新。它纯粹是一个数据转换器。
自动发现:_discover_transports() 按需 import 传输模块(anthropic、chat_completions、bedrock、codex),避免启动时加载所有依赖。
api_mode 调度
Hermes 通过 api_mode 字段决定传输路径:
| api_mode | 传输 | 适用场景 |
|---|---|---|
chat_completions | OpenAI 兼容 REST | 大多数提供商(~16 个) |
anthropic_messages | Anthropic Messages API | Claude 原生 API |
bedrock | AWS Bedrock | 企业 AWS 生态 |
codex_responses | OpenAI Codex Responses | Codex 运行时 |
codex_app_server | Codex 子进程 | 终端/文件操作在 Codex 内运行 |
chat_completions 传输层内置了对特殊提供商的处理,例如:
- Gemini 的
thinkingConfig特殊处理 - Moonshot/Kimi 的严格 JSON Schema 要求
这通过 ProviderProfile 模式实现,替代了旧的 flag-based kwargs 组装。
Model Provider 插件:元数据与认证
每个推理后端(openrouter、anthropic、gmi、deepseek、nvidia 等)是一个插件目录:
plugins/model-providers/├── openrouter/__init__.py # register_provider(ProviderProfile(...))├── anthropic/__init__.py├── gmi/__init__.py└── ...三层扫描:
- 内置:
<repo>/plugins/model-providers/<name>/ - 用户:
$HERMES_HOME/plugins/model-providers/<name>/ - 遗留:
<repo>/providers/<name>.py(向后兼容)
用户覆盖:同名用户插件覆盖内置插件(last-writer-wins),允许第三方无需 fork 仓库即可替换配置。
懒发现:首次 get_provider_profile() 或 list_providers() 时才扫描目录,不是模块导入时。
辅助模型自动路由
agent/auxiliary_client.py 为辅助任务分配独立的小模型:
set_runtime_main(provider, model) # 设置运行时主模型
# 辅助任务(curator、vision、embedding、title、session_search)# 通过 auxiliary_client 使用独立配置,不消耗主模型配额分辨率顺序:
auxiliary配置节中的显式配置- 从运行时主模型推断
- 配置默认值
这确保辅助任务不会意外使用昂贵的主模型,同时也允许用户为每个辅助任务微调 provider/model。
Nous Portal 统一工具网关
Nous Portal 订阅覆盖多个工具后端:
| 工具 | 后端 |
|---|---|
| 搜索 | Firecrawl |
| 图像生成 | FAL |
| TTS | OpenAI |
| 浏览器 | Browser Use |
一个命令 hermes setup --portal 通过 OAuth 登录并自动配置所有工具后端。
问题与规避
提供商 API 差异
问题:虽然都声称兼容 OpenAI API,但实际存在细微差异(参数名、错误格式、流式事件)。
对策:chat_completions 传输层内部通过 ProviderProfile 处理每个提供商的差异。例如 Gemini 需要 thinkingConfig、Moonshot 需要严格 JSON Schema。
辅助模型配置漂移
问题:用户更改了主模型但忘记更新辅助模型配置,导致辅助任务使用不兼容的模型。
对策:set_runtime_main() 在每次 turn 开始时更新运行时主模型,辅助客户端可以据此推断合适的辅助模型。
Transport 插件的导入时机
问题:discover_plugins() 只作为导入 model_tools.py 的副作用运行。不导入 model_tools.py 的代码路径必须先显式调用 discover_plugins()。
对策:这是已知的导入时序陷阱。插件作者在读取插件状态时需确保 model_tools.py 已被导入。
设计取舍
传输层只负责数据路径
| 方案 | 优势 | 代价 |
|---|---|---|
| 纯数据转换(Hermes) | Transport 可复用、测试简单、不耦合凭证管理 | 需要外部管理客户端生命周期 |
| 全包式 Transport | 单一组件完成所有工作 | 每个 Transport 重复实现连接池、重试、凭证刷新 |
Hermes 选择将连接管理、凭证刷新、重试逻辑放在传输层之上,Transport 仅做格式转换。这使得新增传输协议只需实现四个方法。
用户插件覆盖内置插件
| 方案 | 优势 | 代价 |
|---|---|---|
| 用户覆盖(last-writer-wins) | 第三方无需 fork 仓库即可替换配置 | 用户可能无意中覆盖了更好的内置配置 |
| 独立命名空间 | 不会覆盖内置 | 需要显式优先级配置 |
Hermes 允许用户替换任何内置 provider profile,这对于需要自定义认证 URL 或添加企业代理的场景很重要。
参考来源
- 源码验证:
agent/transports/、plugins/model-providers/、agent/auxiliary_client.py