<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Provider | 有志者事竟成</title><link>https://www.liwenshen.com/tags/provider/</link><atom:link href="https://www.liwenshen.com/tags/provider/index.xml" rel="self" type="application/rss+xml"/><description>Provider</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>zh</language><lastBuildDate>Sat, 04 Jul 2026 12:00:00 +0800</lastBuildDate><image><url>https://www.liwenshen.com/media/icon_hu_dd5d76fef920c49e.png</url><title>Provider</title><link>https://www.liwenshen.com/tags/provider/</link></image><item><title>Provider 抽象层：让 Hermes 同时驾驭 30+ 个 LLM 提供商</title><link>https://www.liwenshen.com/note/hermes-agent-engineering/09-provider-abstraction/</link><pubDate>Sat, 04 Jul 2026 12:00:00 +0800</pubDate><guid>https://www.liwenshen.com/note/hermes-agent-engineering/09-provider-abstraction/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;系列：通过 Hermes 探秘 Agent 工程 | 第 9 篇&lt;/strong&gt;
上一篇：&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/08-gateway/"&gt;Gateway 网关：连接 20+ 平台的统一消息路由&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="问题一个-agent30-个大脑"&gt;问题：一个 Agent，30+ 个大脑&lt;/h2&gt;
&lt;p&gt;Hermes 可以接入的 LLM 提供商超过 30 个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;商业 API&lt;/strong&gt;：OpenAI、Anthropic、Google Gemini、xAI Grok、DeepSeek、Kimi&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;聚合器&lt;/strong&gt;：OpenRouter、HuggingFace、Kilo Code、Novita&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本地推理&lt;/strong&gt;：Ollama、LM Studio、vLLM、llama.cpp&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;企业/云&lt;/strong&gt;：Azure Foundry、AWS Bedrock、NVIDIA GMI Cloud&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自定义&lt;/strong&gt;：任何 OpenAI 兼容端点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个提供商的差异很大：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;传输协议&lt;/strong&gt;：OpenAI Chat Completions、Anthropic Messages API、OpenAI Codex Responses API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;认证方式&lt;/strong&gt;：API Key、OAuth Device Code、OAuth External、外部进程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;请求格式&lt;/strong&gt;：extra_body 字段位置、reasoning 配置方式、温度参数处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型列表&lt;/strong&gt;：有的有 REST 端点、有的只能静态配置&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hermes 的 Provider 抽象层用一个&lt;strong&gt;声明式 + 插件化&lt;/strong&gt;的架构统一了这些差异。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="providerprofile声明式描述一切"&gt;ProviderProfile：声明式描述一切&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;providers/base.py&lt;/code&gt; 里的 &lt;code&gt;ProviderProfile&lt;/code&gt; 是一个 dataclass，用声明式的方式描述一个 Provider 的所有行为特征：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@dataclass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ProviderProfile&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; api_mode: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;chat_completions&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 传输协议&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; aliases: tuple &lt;span style="color:#f92672"&gt;=&lt;/span&gt; () &lt;span style="color:#75715e"&gt;# 别名（如 &amp;#34;kimi&amp;#34; → &amp;#34;kimi-coding&amp;#34;）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 认证 &amp;amp; 端点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; env_vars: tuple &lt;span style="color:#f92672"&gt;=&lt;/span&gt; () &lt;span style="color:#75715e"&gt;# 环境变量名（按优先级）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; base_url: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 推理端点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth_type: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;api_key&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# api_key | oauth_device_code | oauth_external | external_process&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 视觉支持&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; supports_vision: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; supports_vision_tool_messages: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 模型目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fallback_models: tuple &lt;span style="color:#f92672"&gt;=&lt;/span&gt; () &lt;span style="color:#75715e"&gt;# 静态模型列表（live fetch 失败时用）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 客户端怪癖&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; default_headers: dict &lt;span style="color:#f92672"&gt;=&lt;/span&gt; field(default_factory&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dict)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fixed_temperature: Any &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#75715e"&gt;# None=调用方默认, OMIT_TEMPERATURE=不发送&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; default_max_tokens: int &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; default_aux_model: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 辅助任务用的便宜模型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 钩子&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;prepare_messages&lt;/span&gt;(self, messages): &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;build_extra_body&lt;/span&gt;(self, &lt;span style="color:#f92672"&gt;*&lt;/span&gt;, session_id, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;context): &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;build_api_kwargs_extras&lt;/span&gt;(self, &lt;span style="color:#f92672"&gt;*&lt;/span&gt;, reasoning_config, &lt;span style="color:#f92672"&gt;**&lt;/span&gt;context): &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_max_tokens&lt;/span&gt;(self, model): &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fetch_models&lt;/span&gt;(self, &lt;span style="color:#f92672"&gt;*&lt;/span&gt;, api_key, base_url, timeout): &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键设计：&lt;strong&gt;Profile 是声明式的&lt;/strong&gt;——它描述 Provider 的行为，但不拥有客户端构造、凭证轮换或流式处理。那些职责在 &lt;code&gt;AIAgent&lt;/code&gt; 层。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="插件化注册发现"&gt;插件化注册发现&lt;/h2&gt;
&lt;p&gt;Provider Profile 通过&lt;strong&gt;插件目录&lt;/strong&gt;注册，有两层位置：&lt;/p&gt;
&lt;h3 id="1-内置插件bundled"&gt;1. 内置插件（bundled）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;plugins/model-providers/&amp;lt;name&amp;gt;/&lt;/code&gt; 目录，随 Hermes 一起发布：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;plugins/model-providers/
├── anthropic/
│ ├── __init__.py # 调用 register_provider(profile)
│ └── plugin.yaml # 清单文件
├── openai-codex/
├── deepseek/
├── gemini/
├── ollama/
├── openrouter/
├── nous/
└── ...（30+ 个）
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2-用户插件user"&gt;2. 用户插件（user）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;$HERMES_HOME/plugins/model-providers/&amp;lt;name&amp;gt;/&lt;/code&gt;，用户自定义或第三方覆盖。&lt;/p&gt;
&lt;h3 id="发现顺序"&gt;发现顺序&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;_discover_providers()&lt;/code&gt; 按顺序导入：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;内置插件&lt;/strong&gt; → 先加载，提供基础覆盖&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户插件&lt;/strong&gt; → 后加载，同名覆盖内置（last-writer-wins）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;旧版单文件&lt;/strong&gt; → &lt;code&gt;providers/&amp;lt;name&amp;gt;.py&lt;/code&gt;，向后兼容&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户可以&lt;strong&gt;覆盖任何内置 Profile&lt;/strong&gt;（比如修改 &lt;code&gt;base_url&lt;/code&gt; 指向私有代理）&lt;/li&gt;
&lt;li&gt;第三方可&lt;strong&gt;通过插件添加新 Provider&lt;/strong&gt;，无需修改 Hermes 源码&lt;/li&gt;
&lt;li&gt;旧版单文件 Profile 仍然可用（平滑迁移）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="三种传输协议"&gt;三种传输协议&lt;/h2&gt;
&lt;p&gt;Hermes 支持三种 API 传输协议，Provider 通过 &lt;code&gt;api_mode&lt;/code&gt; 声明：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;api_mode&lt;/th&gt;
&lt;th&gt;协议&lt;/th&gt;
&lt;th&gt;典型 Provider&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chat_completions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI Chat Completions API&lt;/td&gt;
&lt;td&gt;OpenRouter、DeepSeek、Kimi、Ollama、自定义&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;anthropic_messages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anthropic Messages API&lt;/td&gt;
&lt;td&gt;Anthropic 原生、MiniMax、Kimi Code（/coding 路由）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codex_responses&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI Codex Responses API&lt;/td&gt;
&lt;td&gt;OpenAI Codex、xAI、OpenAI API（GPT-5.x）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="自动检测"&gt;自动检测&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;runtime_provider.py&lt;/code&gt; 的 &lt;code&gt;_detect_api_mode_for_url()&lt;/code&gt; 可以根据 &lt;code&gt;base_url&lt;/code&gt; 自动推断协议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;api.openai.com&lt;/code&gt; → &lt;code&gt;codex_responses&lt;/code&gt;（GPT-5.x 需要 Responses API）&lt;/li&gt;
&lt;li&gt;URL 路径以 &lt;code&gt;/anthropic&lt;/code&gt; 结尾 → &lt;code&gt;anthropic_messages&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api.kimi.com/coding&lt;/code&gt; → &lt;code&gt;anthropic_messages&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="请求级怪癖"&gt;请求级怪癖&lt;/h3&gt;
&lt;p&gt;不同 Provider 的请求格式差异通过两个钩子处理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;build_extra_body()&lt;/code&gt;：Provider 特定的 extra_body 字段（如 OpenRouter 的 reasoning 配置）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build_api_kwargs_extras()&lt;/code&gt;：返回 &lt;code&gt;(extra_body_additions, top_level_kwargs)&lt;/code&gt; 元组，因为有些 Provider 把 reasoning 配置放在 extra_body，有些放在顶层 api_kwargs&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="四种认证路径"&gt;四种认证路径&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;auth_type&lt;/code&gt; 字段决定了如何获取凭证：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;auth_type&lt;/th&gt;
&lt;th&gt;机制&lt;/th&gt;
&lt;th&gt;典型 Provider&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api_key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;从环境变量读取 API Key&lt;/td&gt;
&lt;td&gt;OpenRouter、DeepSeek、Gemini&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oauth_device_code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OAuth 2.0 Device Code 流程&lt;/td&gt;
&lt;td&gt;Nous Portal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;oauth_external&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;外部浏览器 OAuth 流程&lt;/td&gt;
&lt;td&gt;OpenAI Codex、xAI、Qwen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;external_process&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;外部进程获取凭证&lt;/td&gt;
&lt;td&gt;GitHub Copilot ACP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="api-key-凭证隔离"&gt;API Key 凭证隔离&lt;/h3&gt;
&lt;p&gt;Hermes 有一个重要的安全设计：&lt;strong&gt;每个 Provider 的 API Key 只发往自己的 base URL&lt;/strong&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 不会把 OPENROUTER_API_KEY 发到 api.openai.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 不会把 OPENAI_API_KEY 发到 api.openai.com.attacker.test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;runtime_provider.py&lt;/code&gt; 的 &lt;code&gt;_host_derived_api_key()&lt;/code&gt; 会根据 base URL 的 hostname 自动推导对应的环境变量名（如 &lt;code&gt;api.deepseek.com&lt;/code&gt; → &lt;code&gt;DEEPSEEK_API_KEY&lt;/code&gt;），同时防御了&lt;strong&gt;域名仿冒攻击&lt;/strong&gt;（&lt;code&gt;api.deepseek.com.attacker.test&lt;/code&gt; 会推导出 &lt;code&gt;ATTACKER_API_KEY&lt;/code&gt;，而不是 &lt;code&gt;DEEPSEEK_API_KEY&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 id="oauth-device-code-流程"&gt;OAuth Device Code 流程&lt;/h3&gt;
&lt;p&gt;以 Nous Portal 为例：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hermes 向 Portal 请求 device code&lt;/li&gt;
&lt;li&gt;用户浏览器打开 Portal 页面，输入 user code&lt;/li&gt;
&lt;li&gt;Hermes 轮询 Portal 获取 access_token&lt;/li&gt;
&lt;li&gt;Token 持久化到 &lt;code&gt;~/.hermes/auth.json&lt;/code&gt;，带文件锁保护&lt;/li&gt;
&lt;li&gt;过期前 120 秒自动刷新&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="credential-pool"&gt;Credential Pool&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;agent/credential_pool.py&lt;/code&gt; 实现了&lt;strong&gt;凭证池&lt;/strong&gt;——同一个 Provider 可以有多个 API Key，按轮询或失败率自动切换。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="运行时解析"&gt;运行时解析&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;hermes_cli/runtime_provider.py&lt;/code&gt; 是 CLI、Gateway、Cron、ACP 共享的解析入口。&lt;/p&gt;
&lt;h3 id="解析优先级"&gt;解析优先级&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;显式 CLI/运行时请求&lt;/strong&gt;（如 &lt;code&gt;hermes chat --provider anthropic&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;config.yaml 的 model/provider 配置&lt;/strong&gt;（用户通过 &lt;code&gt;hermes model&lt;/code&gt; 保存的选择）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;环境变量&lt;/strong&gt;（如 &lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provider 默认值或自动解析&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;config.yaml 优先于环境变量&lt;/strong&gt;——这防止了一个过期的 shell export 静默覆盖用户在 &lt;code&gt;hermes model&lt;/code&gt; 里选择的端点。&lt;/p&gt;
&lt;h3 id="解析结果"&gt;解析结果&lt;/h3&gt;
&lt;p&gt;运行时解析返回一个包含以下字段的数据结构：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;provider&lt;/code&gt;：Provider ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api_mode&lt;/code&gt;：传输协议&lt;/li&gt;
&lt;li&gt;&lt;code&gt;base_url&lt;/code&gt;：推理端点&lt;/li&gt;
&lt;li&gt;&lt;code&gt;api_key&lt;/code&gt;：凭证&lt;/li&gt;
&lt;li&gt;&lt;code&gt;source&lt;/code&gt;：凭证来源（env / config / auth_store）&lt;/li&gt;
&lt;li&gt;Provider 特定的元数据（如 token 过期时间）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="fallback-provider-链"&gt;Fallback Provider 链&lt;/h2&gt;
&lt;p&gt;Hermes 支持配置一个 &lt;strong&gt;Fallback Provider 列表&lt;/strong&gt;——当主 Provider 出错时，按顺序尝试下一个。&lt;/p&gt;
&lt;h3 id="触发点"&gt;触发点&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;_try_activate_fallback()&lt;/code&gt; 在三个场景被调用：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;API 响应无效（None choices、missing content）且重试耗尽&lt;/li&gt;
&lt;li&gt;不可重试的客户端错误（HTTP 401、403、404）&lt;/li&gt;
&lt;li&gt;瞬态错误（HTTP 429、500、502、503）且重试耗尽&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="激活流程"&gt;激活流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;检查是否已激活（&lt;code&gt;_fallback_activated&lt;/code&gt; 标志，防止重复触发）&lt;/li&gt;
&lt;li&gt;调用 &lt;code&gt;resolve_provider_client()&lt;/code&gt; 构建新客户端&lt;/li&gt;
&lt;li&gt;确定 &lt;code&gt;api_mode&lt;/code&gt;（anthropic → &lt;code&gt;anthropic_messages&lt;/code&gt;，codex → &lt;code&gt;codex_responses&lt;/code&gt;，其他 → &lt;code&gt;chat_completions&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原地替换&lt;/strong&gt;：&lt;code&gt;self.model&lt;/code&gt;、&lt;code&gt;self.provider&lt;/code&gt;、&lt;code&gt;self.base_url&lt;/code&gt;、&lt;code&gt;self.api_mode&lt;/code&gt;、&lt;code&gt;self.client&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;重新评估 prompt caching（Claude 模型在 OpenRouter 上启用）&lt;/li&gt;
&lt;li&gt;重置重试计数为 0，继续循环&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="限制"&gt;限制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;子 Agent 不继承 fallback 配置&lt;/strong&gt;——&lt;code&gt;delegate_tool&lt;/code&gt; 只继承 Provider，不继承 fallback 链&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;辅助任务不走 fallback&lt;/strong&gt;——它们有独立的 Provider 自动检测链&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cron Job 支持 fallback&lt;/strong&gt;——&lt;code&gt;run_job()&lt;/code&gt; 读取 &lt;code&gt;config.yaml&lt;/code&gt; 的 &lt;code&gt;fallback_providers&lt;/code&gt; 并传给 AIAgent&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="辅助模型路由"&gt;辅助模型路由&lt;/h2&gt;
&lt;p&gt;辅助任务（视觉、上下文压缩、技能操作、MCP 辅助、记忆刷新）可以使用&lt;strong&gt;与主对话不同的 Provider/Model&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;配置方式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;auxiliary&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;provider&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;openrouter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;model&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;google/gemini-2.0-flash-001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当 &lt;code&gt;auxiliary.provider&lt;/code&gt; 设为 &lt;code&gt;main&lt;/code&gt; 时，辅助任务走与主对话相同的运行时解析路径。这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;环境变量驱动的自定义端点仍然生效&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hermes model&lt;/code&gt; 保存的自定义端点也生效&lt;/li&gt;
&lt;li&gt;辅助路由能区分&amp;quot;真正的自定义端点&amp;quot;和&amp;quot;OpenRouter fallback&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="hermesoverlayhermes-特有的元数据"&gt;HermesOverlay：Hermes 特有的元数据&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;hermes_cli/providers.py&lt;/code&gt; 定义了 &lt;code&gt;HermesOverlay&lt;/code&gt;，补充了 models.dev 目录不覆盖的 Hermes 特有信息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@dataclass&lt;/span&gt;(frozen&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HermesOverlay&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; transport: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;openai_chat&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 传输协议&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; is_aggregator: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 是否为聚合器（如 OpenRouter）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; auth_type: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;api_key&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 认证类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; extra_env_vars: tuple &lt;span style="color:#f92672"&gt;=&lt;/span&gt; () &lt;span style="color:#75715e"&gt;# models.dev 不追踪的额外环境变量&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; base_url_override: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 覆盖 models.dev 的 base_url&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; base_url_env_var: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# 自定义 base URL 的环境变量名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 Overlay 让 Hermes 可以在不修改上游 models.dev 目录的前提下，为每个 Provider 添加 Hermes 特有的配置。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;Hermes 的 Provider 抽象层是一个&lt;strong&gt;声明式 + 插件化&lt;/strong&gt;的架构：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层&lt;/th&gt;
&lt;th&gt;职责&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ProviderProfile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;声明式描述 Provider 行为（协议、认证、怪癖）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;插件注册&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;内置 → 用户 → 旧版，last-writer-wins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;运行时解析&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;共享解析器，config.yaml 优先于 env&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;传输适配&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;三种协议（chat_completions / anthropic_messages / codex_responses）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;认证系统&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;四种路径（api_key / oauth_device / oauth_external / external_process）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fallback 链&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;主 Provider 失败时自动切换&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;辅助路由&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;辅助任务可独立配置 Provider&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这套设计让 Hermes 能用一个统一接口对接 30+ 个 LLM 提供商，同时保持高度的可扩展性——添加新 Provider 只需要放一个插件目录，修改内置 Profile 只需要放一个同名用户插件。&lt;/p&gt;
&lt;p&gt;下一篇，也是本系列的最后一篇，我们将深入 Hermes 的沙箱与代码执行系统——&lt;code&gt;execute_code&lt;/code&gt; 工具如何在隔离环境中安全运行代码。&lt;/p&gt;</description></item></channel></rss>