跳转到内容

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_completionsOpenAI 兼容 REST大多数提供商(~16 个)
anthropic_messagesAnthropic Messages APIClaude 原生 API
bedrockAWS Bedrock企业 AWS 生态
codex_responsesOpenAI Codex ResponsesCodex 运行时
codex_app_serverCodex 子进程终端/文件操作在 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
└── ...

三层扫描

  1. 内置:<repo>/plugins/model-providers/<name>/
  2. 用户:$HERMES_HOME/plugins/model-providers/<name>/
  3. 遗留:<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 使用独立配置,不消耗主模型配额

分辨率顺序:

  1. auxiliary 配置节中的显式配置
  2. 从运行时主模型推断
  3. 配置默认值

这确保辅助任务不会意外使用昂贵的主模型,同时也允许用户为每个辅助任务微调 provider/model。

Nous Portal 统一工具网关

Nous Portal 订阅覆盖多个工具后端:

工具后端
搜索Firecrawl
图像生成FAL
TTSOpenAI
浏览器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