--- sidebar_position: 4 title: "Provider 运行时解析" description: "Hermes 如何在运行时解析 provider、凭据、API 模式及辅助模型" --- # Provider 运行时解析 Hermes 拥有一个共享的 provider 运行时解析器,用于以下场景: - CLI - gateway - cron 任务 - ACP - 辅助模型调用 主要实现: - `hermes_cli/runtime_provider.py` — 凭据解析,`_resolve_custom_runtime()` - `hermes_cli/auth.py` — provider 注册表,`resolve_provider()` - `hermes_cli/model_switch.py` — 共享 `/model` 切换流水线(CLI + gateway) - `agent/auxiliary_client.py` — 辅助模型路由 - `providers/` — ABC + 注册表入口点(`ProviderProfile`、`register_provider`、`get_provider_profile`、`list_providers`) - `plugins/model-providers//` — 每个 provider 的插件(内置),声明 `api_mode`、`base_url`、`env_vars`、`fallback_models` 并在首次访问时将自身注册到注册表。用户插件位于 `$HERMES_HOME/plugins/model-providers//`,会覆盖同名的内置插件。 `providers/` 中的 `get_provider_profile()` 为给定 provider id 返回一个 `ProviderProfile`。`runtime_provider.py` 在解析时调用它,以获取规范的 `base_url`、`env_vars` 优先级列表、`api_mode` 和 `fallback_models`,无需在多个文件中重复这些数据。在 `plugins/model-providers//`(或 `$HERMES_HOME/plugins/model-providers//`)下添加一个调用 `register_provider()` 的新插件,即可让 `runtime_provider.py` 自动识别它——无需在解析器本身中添加分支。 如果你想添加一个新的一等推理 provider,请结合本页阅读 [添加 Provider](./adding-providers.md) 和 [Model Provider 插件指南](./model-provider-plugin.md)。 ## 解析优先级 从高层来看,provider 解析使用以下顺序: 1. 显式 CLI/运行时请求 2. `config.yaml` 中的模型/provider 配置 3. 环境变量 4. provider 特定的默认值或自动解析 该顺序很重要,因为 Hermes 将已保存的模型/provider 选择视为正常运行的真实来源。这可以防止过时的 shell 导出变量悄悄覆盖用户在 `hermes model` 中最后选择的端点。 ## Provider 当前 provider 系列包括(完整内置集合见 `plugins/model-providers/`): - OpenRouter - Nous Portal - OpenAI Codex - Copilot / Copilot ACP - Anthropic(原生) - Google / Gemini(`gemini`、`google-gemini-cli`) - Alibaba / DashScope(`alibaba`、`alibaba-coding-plan`) - DeepSeek - Z.AI - Kimi / Moonshot(`kimi-coding`、`kimi-coding-cn`) - MiniMax(`minimax`、`minimax-cn`、`minimax-oauth`) - Kilo Code - Hugging Face - OpenCode Zen / OpenCode Go - AWS Bedrock - Azure Foundry - NVIDIA NIM - xAI(Grok) - Arcee - GMI Cloud - StepFun - Qwen OAuth - Xiaomi - Ollama Cloud - LM Studio - Tencent TokenHub - Custom(`provider: custom`)— 适用于任何 OpenAI 兼容端点的一等 provider - 命名自定义 provider(`config.yaml` 中的 `custom_providers` 列表) ## 运行时解析的输出 运行时解析器返回的数据包括: - `provider` - `api_mode` - `base_url` - `api_key` - `source` - provider 特定的元数据,如过期/刷新信息 ## 为什么这很重要 该解析器是 Hermes 能够在以下场景之间共享认证/运行时逻辑的主要原因: - `hermes chat` - gateway 消息处理 - 在全新会话中运行的 cron 任务 - ACP 编辑器会话 - 辅助模型任务 ## OpenRouter 与自定义 OpenAI 兼容 base URL Hermes 包含相关逻辑,以避免在存在多个 provider 密钥时(例如同时存在 `OPENROUTER_API_KEY` 和 `OPENAI_API_KEY`)将错误的 API key 泄露给自定义端点。 每个 provider 的 API key 仅作用于其自身的 base URL: - `OPENROUTER_API_KEY` 仅发送至 `openrouter.ai` 端点 - `OPENAI_API_KEY` 用于自定义端点及作为回退 Hermes 还区分以下两种情况: - 用户主动选择的真实自定义端点 - 未配置自定义端点时使用的 OpenRouter 回退路径 这种区分对以下场景尤为重要: - 本地模型服务器 - 非 OpenRouter 的 OpenAI 兼容 API - 无需重新运行 setup 即可切换 provider - 通过 config 保存的自定义端点,即使当前 shell 中未导出 `OPENAI_BASE_URL` 也应正常工作 ## 原生 Anthropic 路径 Anthropic 不再仅限于"通过 OpenRouter"访问。 当 provider 解析选择 `anthropic` 时,Hermes 使用: - `api_mode = anthropic_messages` - 原生 Anthropic Messages API - `agent/anthropic_adapter.py` 进行转换 原生 Anthropic 的凭据解析现在在两者同时存在时,优先使用可刷新的 Claude Code 凭据,而非复制的环境变量 token。实际效果为: - 包含可刷新认证的 Claude Code 凭据文件被视为首选来源 - 手动设置的 `ANTHROPIC_TOKEN` / `CLAUDE_CODE_OAUTH_TOKEN` 值仍可作为显式覆盖 - Hermes 在调用原生 Messages API 前会预检 Anthropic 凭据刷新 - Hermes 在重建 Anthropic 客户端后,仍会在收到 401 时重试一次,作为回退路径 ## OpenAI Codex 路径 Codex 使用独立的 Responses API 路径: - `api_mode = codex_responses` - 专用的凭据解析和认证存储支持 ## 辅助模型路由 辅助任务包括: - 视觉 - 网页提取摘要 - 上下文压缩摘要 - skills hub 操作 - MCP 辅助操作 - 记忆刷新 这些任务可以使用各自独立的 provider/模型路由,而非主对话模型。 当辅助任务配置的 provider 为 `main` 时,Hermes 通过与普通对话相同的共享运行时路径进行解析。实际效果为: - 环境变量驱动的自定义端点仍然有效 - 通过 `hermes model` / `config.yaml` 保存的自定义端点同样有效 - 辅助路由能够区分真实保存的自定义端点与 OpenRouter 回退 ## 回退模型 Hermes 支持配置回退 provider 链——一个按顺序尝试的 `(provider, model)` 条目列表,当主模型遇到错误时依次尝试。旧版单对 `fallback_model` 字典仍被接受以保持向后兼容(并在首次写入时迁移)。 ### 内部工作原理 1. **存储**:`AIAgent.__init__` 存储 `fallback_model` 字典并将 `_fallback_activated` 设为 `False`。 2. **触发点**:`_try_activate_fallback()` 在 `run_agent.py` 主重试循环的三处被调用: - 在无效 API 响应(None choices、缺少 content)达到最大重试次数后 - 在不可重试的客户端错误(HTTP 401、403、404)时 - 在瞬时错误(HTTP 429、500、502、503)达到最大重试次数后 3. **激活流程**(`_try_activate_fallback`): - 若已激活或未配置,立即返回 `False` - 调用 `auxiliary_client.py` 中的 `resolve_provider_client()` 构建带有正确认证的新客户端 - 确定 `api_mode`:openai-codex 使用 `codex_responses`,anthropic 使用 `anthropic_messages`,其余使用 `chat_completions` - 原地替换:`self.model`、`self.provider`、`self.base_url`、`self.api_mode`、`self.client`、`self._client_kwargs` - 对于 anthropic 回退:构建原生 Anthropic 客户端而非 OpenAI 兼容客户端 - 重新评估 prompt 缓存(对 OpenRouter 上的 Claude 模型启用) - 将 `_fallback_activated` 设为 `True`——防止再次触发 - 将重试计数重置为 0 并继续循环 4. **配置流程**: - CLI:`cli.py` 读取 `CLI_CONFIG["fallback_model"]` → 传递给 `AIAgent(fallback_model=...)` - Gateway:`gateway/run.py._load_fallback_model()` 读取 `config.yaml` → 传递给 `AIAgent` - 验证:`provider` 和 `model` 键均须非空,否则回退被禁用 ### 不支持回退的场景 - **子代理委托**(`tools/delegate_tool.py`):子代理继承父代理的 provider,但不继承回退配置 - **辅助任务**:使用各自独立的 provider 自动检测链(见上方辅助模型路由) Cron 任务**支持**回退:`run_job()` 从 `config.yaml` 读取 `fallback_providers`(或旧版 `fallback_model`)并传递给 `AIAgent(fallback_model=...)`,与 gateway 的 `_load_fallback_model()` 模式一致。参见 [Cron 内部机制](./cron-internals.md)。 ### 测试覆盖 参见 `tests/test_fallback_model.py`,其中包含覆盖所有支持 provider、单次触发语义及边界情况的完整测试。 ## 相关文档 - [Agent 循环内部机制](./agent-loop.md) - [ACP 内部机制](./acp-internals.md) - [上下文压缩与 Prompt 缓存](./context-compression-and-caching.md)