Hermes 101|14|Provider Runtime

Provider Runtime 把 provider、model、base_url、api_mode、凭证、路由和 fallback 解析成一次可执行的模型调用配置。

Hermes 101|14|Provider Runtime

很多 Agent 框架把模型配置理解成一个字符串:model=gpt-4provider=openai。真实系统不是这样。一个模型调用背后还包括 endpoint、API mode、凭证来源、credential pool、provider routing、fallback、上下文长度、流式能力和工具调用兼容性。

Hermes 把这一层称为 Provider Runtime

Provider Runtime 的解析优先级

读完本文,你应该能回答

  • Provider Runtime 为什么不只是 model 字符串?
  • provider、model、base_url、api_mode、凭证和 fallback 如何解析成一次调用?
  • Provider routing 和 Hermes fallback 有什么区别?
  • mini-agent-harness 如何支持多模型而不污染 Agent Loop?

本篇在系列中的位置

这是本轮系列的收束篇。前面讲 Agent 如何执行、记忆、扩展、调度和分工;本篇回到每一轮都绕不开的模型调用,把 provider 差异收束成可维护的运行时配置。

贯穿案例

贯穿这个系列,可以一直带着同一个任务来读:用户说“帮我修复这个 repo 里的 failing tests”。不同章节会回答同一个任务在运行时的不同问题:入口怎样进入、上下文怎样准备、模型怎样决定下一步、工具怎样执行、状态怎样保存、失败后怎样恢复。

定义

Provider Runtime 是 Hermes 在运行时解析模型调用配置的机制。它把 provider、model、base_url、api_key、api_mode、凭证池、路由和 fallback 统一成一次可执行的 LLM request。

它不是 provider 列表,也不是配置文件读取器。它是 CLI、Gateway、Cron、ACP、Delegation、Auxiliary tasks 共享的模型运行时解析层。

为什么需要 Runtime

不同 provider 不只是 base_url 不同。OpenAI-compatible API、Anthropic Messages API、Codex Responses API、Bedrock Converse API 的请求结构、工具格式、流式事件和错误语义都不同。

如果这些差异散落在 Agent Loop 里,整个系统会很快不可维护。Provider Runtime 的作用,是在进入模型调用前把差异收束成明确的 runtime config。

解析来源

Hermes 的 provider/runtime 可能来自多个入口:命令行参数、/model 切换、config.yaml、环境变量、OAuth auth store、credential pool、自定义 provider、插件 provider、cron job 覆盖、delegation 覆盖。

Resolver 的职责不是简单“谁有值用谁”,而是保证优先级、凭证隔离和 API mode 正确。比如 OpenRouter key 不能泄漏到 custom endpoint;Anthropic native 不应该走 OpenAI chat completions;Codex Responses 不应该注入 OpenRouter provider routing。

Routing 与 Fallback

这两个概念很容易混淆。

Provider routing 是单个 provider 内部的路由。例如 OpenRouter 可以设置 only、ignore、order、sort、require_parameters,让 OpenRouter 在内部选择供应商。

Fallback providers 是 Hermes 层面的跨 provider failover。primary provider 失败后,Hermes 可以切到 Anthropic、OpenAI-compatible、自定义 endpoint 或本地模型。下一轮新的用户消息再尝试恢复 primary。

Provider Routing 与 Fallback 分层

一个在 provider 内,一个在 Hermes 外层;一个影响 extra_body.provider,一个替换整个 runtime。

Provider 解析优先级表

Provider Runtime 的复杂度来自多个入口同时能影响模型调用。一个可维护实现应该明确优先级。

来源 典型例子 优先级含义
本轮显式参数 --provider--model、delegation override 只影响当前请求或子任务
会话内切换 /model 影响当前 session 后续 turn
Job 级配置 cron job model override 影响该 job 的每次运行
全局配置 config.yaml default provider/model 默认值
环境变量 / auth store key、OAuth、credential pool 提供凭证,不应随便跨 provider 复用
provider adapter api_mode、base_url、streaming 能力 决定下游协议

优先级越高,作用域应该越窄;作用域越宽,越应该稳定。这能避免一次临时模型切换污染整个运行时。

API Mode

Provider Runtime 最重要的输出之一是 api_mode。它告诉下游 adapter 应该用哪种协议发请求。

type RuntimeProvider = {
  provider: string
  model: string
  baseUrl: string
  apiKey: string
  apiMode: "chat_completions" | "anthropic_messages" | "codex_responses" | "bedrock_converse"
}

Agent Loop 不应该到处判断“如果是 Anthropic 就怎样”。更好的方式是先解析 runtime,再把消息交给对应 adapter。

可迁移伪实现:Provider Runtime

下面的伪代码是机制抽象,不对应 Hermes 的真实 API 或文件结构。学习框架的最小版本可以这样写:

function resolveRuntime(requested?: string, targetModel?: string): RuntimeProvider {
  const provider = requested ?? config.model.provider ?? env("HERMES_INFERENCE_PROVIDER") ?? "openrouter"
  const model = targetModel ?? config.model.default

  if (provider === "openrouter") {
    return { provider, model, baseUrl: OPENROUTER_URL, apiKey: env("OPENROUTER_KEY"), apiMode: "chat_completions" }
  }
  if (provider === "anthropic") {
    return { provider, model, baseUrl: ANTHROPIC_URL, apiKey: env("ANTHROPIC_KEY"), apiMode: "anthropic_messages" }
  }
  if (provider === "custom") {
    return { provider, model, baseUrl: config.model.baseUrl, apiKey: config.model.apiKey, apiMode: detectApiMode(config.model.baseUrl) }
  }
  throw new Error(`unknown provider: ${provider}`)
}

class Agent {
  runtime = resolveRuntime()
  primary = this.runtime

  async runTurn(message: string) {
    try {
      return await callModel(this.runtime, message)
    } catch (err) {
      if (this.activateFallback()) return callModel(this.runtime, message)
      throw err
    }
  }
}

第一版不用实现所有 provider,但必须保留两条边界:runtime config 一次性解析;fallback 替换整个 runtime,而不是只改 model 字符串。

小结

Provider Runtime 让 Hermes 不被某一个模型供应商绑定。它把“选择模型”升级成“解析一次可执行的模型运行时”。这也是 Agent 框架能支持多入口、多 provider、fallback、自定义 endpoint 和 provider 插件的基础。

参考资料