跳到主要內容

構建模型提供商插件

模型提供商插件聲明一個推理後端——一個兼容 OpenAI 的端點、Anthropic Messages 服務器、Codex 風格的 Responses API 或 Bedrock 原生接口——Hermes 可以通過該後端路由 AIAgent 調用。每個內置提供商(OpenRouter、Anthropic、GMI、DeepSeek、Nvidia 等)都作為此類插件之一提供。第三方可以通過在 $HERMES_HOME/plugins/model-providers/ 下放置一個目錄來添加自己的插件,無需對倉庫進行任何更改。

提示

模型提供商插件是第三種提供商插件。另外兩種是記憶提供商插件(跨會話知識)和上下文引擎插件(上下文壓縮策略)。這三者都遵循相同的“放置目錄、聲明配置文件、無需編輯倉庫”模式。

發現機制的工作原理

providers/__init__.py._discover_providers() 會在首次有代碼調用 get_provider_profile()list_providers() 時惰性運行。發現順序如下:

  1. 捆綁插件<repo>/plugins/model-providers/<name>/ — 隨 Hermes 一起發佈
  2. 用戶插件$HERMES_HOME/plugins/model-providers/<name>/ — 放入任意目錄;後續會話無需重啟即可生效
  3. 遺留單文件<repo>/providers/<name>.py — 用於樹外可編輯安裝的向後兼容

用戶插件會覆蓋同名的捆綁插件,因為 register_provider() 採用最後寫入者優先策略。只需放置一個 $HERMES_HOME/plugins/model-providers/gmi/ 目錄,即可替換內置的 GMI 配置文件,而無需觸碰倉庫。

目錄結構

plugins/model-providers/my-provider/
├── __init__.py # Calls register_provider(profile) at module-level
├── plugin.yaml # kind: model-provider + metadata (optional but recommended)
└── README.md # Setup instructions (optional)

唯一必需的文件是 __init__.pyplugin.yamlhermes plugins 用於內省,並由通用 PluginManager 用於將插件路由到正確的加載器;如果沒有它,通用加載器將回退到源文本啟發式方法。

最小示例 — 簡單的 API 密鑰提供商

# plugins/model-providers/acme-inference/__init__.py
from providers import register_provider
from providers.base import ProviderProfile

acme = ProviderProfile(
name="acme-inference",
aliases=("acme",),
display_name="Acme Inference",
description="Acme — OpenAI-compatible direct API",
signup_url="https://acme.example.com/keys",
env_vars=("ACME_API_KEY", "ACME_BASE_URL"),
base_url="https://api.acme.example.com/v1",
auth_type="api_key",
default_aux_model="acme-small-fast",
fallback_models=(
"acme-large-v3",
"acme-medium-v3",
"acme-small-fast",
),
)

register_provider(acme)
# plugins/model-providers/acme-inference/plugin.yaml
name: acme-inference
kind: model-provider
version: 1.0.0
description: Acme Inference — OpenAI-compatible direct API
author: Your Name

就是這樣。放置這兩個文件後,以下功能將自動連接,無需其他編輯:

集成位置獲得的功能
憑證解析hermes_cli/auth.pyPROVIDER_REGISTRY["acme-inference"] 從配置文件中填充
--provider CLI 標誌hermes_cli/main.py接受 acme-inference
hermes model 選擇器hermes_cli/models.py出現在 CANONICAL_PROVIDERS 中,模型列表從 {base_url}/models 獲取
hermes doctorhermes_cli/doctor.pyACME_API_KEY + {base_url}/models 探測進行健康檢查
hermes setuphermes_cli/config.pyACME_API_KEY 出現在 OPTIONAL_ENV_VARS 和設置嚮導中
URL 反向映射agent/model_metadata.py主機名 → 提供商名稱,用於自動檢測
輔助模型agent/auxiliary_client.py使用 default_aux_model 進行壓縮/摘要
運行時解析hermes_cli/runtime_provider.py返回正確的 base_urlapi_keyapi_mode
傳輸層agent/transports/chat_completions.py配置文件路徑通過 prepare_messages / build_extra_body / build_api_kwargs_extras 生成 kwargs

ProviderProfile 字段

完整定義位於 providers/base.py。最常用的字段如下:

字段類型用途
namestr規範 ID — 與 config.yaml 中的 model.provider--provider 標誌匹配
aliasestuple[str, ...]get_provider_profile() 解析的替代名稱(例如 grokxai
api_modestrchat_completions | codex_responses | anthropic_messages | bedrock_converse
display_namestrhermes model 選擇器中顯示的人類可讀標籤
descriptionstr選擇器副標題
signup_urlstr在首次運行設置期間顯示(“在此獲取 API 密鑰”)
env_varstuple[str, ...]按優先級排列的 API 密鑰環境變量;最後一個 *_BASE_URL 條目用作用戶基礎 URL 覆蓋
base_urlstr默認推理端點
models_urlstr顯式目錄 URL(回退到 {base_url}/models
auth_typestrapi_key | oauth_device_code | oauth_external | copilot | aws_sdk | external_process
fallback_modelstuple[str, ...]當實時目錄獲取失敗時顯示的精選列表
default_headersdict[str, str]在每個請求中發送(例如 Copilot 的 Editor-Version
fixed_temperatureAnyNone = 使用調用者的值;OMIT_TEMPERATURE 哨兵值 = 根本不發送 temperature(Kimi)
default_max_tokensint | None提供商級別的 max_tokens 上限(Nvidia:16384)
default_aux_modelstr用於輔助任務(壓縮、視覺、摘要)的廉價模型

可覆蓋鉤子

對於非平凡的怪癖,子類化 ProviderProfile

from typing import Any
from providers.base import ProviderProfile

class AcmeProfile(ProviderProfile):
def prepare_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""Provider-specific message preprocessing. Runs after codex
sanitization, before developer-role swap. Default: pass-through."""
# Example: Qwen normalizes plain-text content to a list-of-parts
# array and injects cache_control; Kimi rewrites tool-call JSON
return messages

def build_extra_body(self, *, session_id=None, **context) -> dict:
"""Provider-specific extra_body fields merged into the API call.
Context includes: session_id, provider_preferences, model, base_url,
reasoning_config. Default: empty dict."""
# Example: OpenRouter's provider-preferences block,
# Gemini's thinking_config translation.
return {}

def build_api_kwargs_extras(self, *, reasoning_config=None, **context):
"""Returns (extra_body_additions, top_level_kwargs). Needed when some
fields go top-level (Kimi's reasoning_effort, OpenRouter's verbosity for
adaptive Anthropic models) and some go in extra_body (OpenRouter's
reasoning dict). Default: ({}, {})."""
return {}, {}

def fetch_models(self, *, api_key=None, timeout=8.0) -> list[str] | None:
"""Live catalog fetch. Default hits {models_url or base_url}/models with
Bearer auth. Override for: custom auth (Anthropic), no REST endpoint
(Bedrock → None), or public/unauthenticated catalogs (OpenRouter)."""
return super().fetch_models(api_key=api_key, timeout=timeout)

鉤子參考示例

查看這些捆綁插件以瞭解慣用法:

插件為何值得關注
plugins/model-providers/openrouter/聚合器,支持提供商偏好設置和公開模型目錄
plugins/model-providers/gemini/thinking_config 轉換(原生形式 + OpenAI 兼容的嵌套形式)
plugins/model-providers/kimi-coding/OMIT_TEMPERATUREextra_body.thinking、頂層 reasoning_effort
plugins/model-providers/qwen-oauth/消息規範化、cache_control 注入、VL 高分辨率支持
plugins/model-providers/nous/歸屬標籤,“禁用時省略推理”
plugins/model-providers/custom/Ollama num_ctx + think: false 的特殊行為
plugins/model-providers/bedrock/api_mode="bedrock_converse"fetch_models 返回 None(無 REST 端點)

用戶覆蓋 — 無需編輯倉庫即可替換內置插件

假設你想讓 gmi 指向你的私有暫存端點以進行測試。創建 ~/.hermes/plugins/model-providers/gmi/__init__.py

from providers import register_provider
from providers.base import ProviderProfile

register_provider(ProviderProfile(
name="gmi",
aliases=("gmi-cloud", "gmicloud"),
env_vars=("GMI_API_KEY",),
base_url="https://gmi-staging.internal.example.com/v1",
auth_type="api_key",
default_aux_model="google/gemini-3.1-flash-lite-preview",
))

在下一次會話中,get_provider_profile("gmi").base_url 將返回暫存 URL。無需修補倉庫,也無需重新構建。由於用戶插件在捆綁插件之後被發現,因此用戶的 register_provider() 調用將生效。

api_mode 選擇

識別以下四個值。Hermes 基於以下規則進行選擇:

  1. 用戶顯式覆蓋(如果設置了 config.yaml 中的 model.api_mode
  2. OpenCode 的每模型分發(Zen 和 Go 使用 opencode_model_api_mode
  3. URL 自動檢測 — /anthropic 後綴 → anthropic_messagesapi.openai.comcodex_responsesapi.x.aicodex_responses,Kimi 域名上的 /codingchat_completions
  4. 配置文件 api_mode 作為 URL 檢測未找到任何內容時的回退選項
  5. 默認值 chat_completions

設置 profile.api_mode 以匹配你的提供商默認的 API 模式 — 它作為一個提示。用戶 URL 覆蓋仍然優先。

認證類型

auth_type含義使用者
api_key單個環境變量攜帶靜態 API 密鑰大多數提供商
oauth_device_code設備代碼 OAuth 流程
oauth_external用戶在別處登錄,令牌存入 auth.jsonAnthropic OAuth、MiniMax OAuth、Gemini Cloud Code、Qwen Portal、Nous Portal
copilotGitHub Copilot 令牌刷新週期copilot 插件
aws_sdkAWS SDK 憑證鏈(IAM 角色、配置文件、環境變量)bedrock 插件
external_process由代理產生的子進程處理認證copilot-acp 插件

auth_type 決定了哪些代碼路徑將你的提供商視為“簡單 API 密鑰提供商” — 如果它不是 api_key,PluginManager 仍然會記錄清單,但 Hermes 的 CLI 級自動化(doctor 檢查、--provider 標誌、設置嚮導委託)可能會跳過它。

發現時機

提供商發現是惰性的 — 由進程中的第一次 get_provider_profile()list_providers() 調用觸發。實際上,這發生在啟動早期(auth.py 模塊加載時會急切地擴展 PROVIDER_REGISTRY)。如果你需要驗證插件是否已加載,請運行:

hermes doctor

— 成功的 auth_type="api_key" 配置文件將出現在 Provider Connectivity 部分,並帶有 /models 探測。

用於程序化檢查:

from providers import list_providers
for p in list_providers():
print(p.name, p.base_url, p.api_mode)

測試你的插件

HERMES_HOME 指向一個臨時目錄,以免汙染你的真實配置:

export HERMES_HOME=/tmp/hermes-plugin-test
mkdir -p $HERMES_HOME/plugins/model-providers/my-provider
cat > $HERMES_HOME/plugins/model-providers/my-provider/__init__.py <<'EOF'
from providers import register_provider
from providers.base import ProviderProfile
register_provider(ProviderProfile(
name="my-provider",
env_vars=("MY_API_KEY",),
base_url="https://api.my-provider.example.com/v1",
auth_type="api_key",
))
EOF

export MY_API_KEY=your-test-key
hermes -z "hello" --provider my-provider -m some-model

通用 PluginManager 集成

通用 PluginManagerhermes plugins 操作的對象)可以看到模型提供商插件,但不會導入它們 — providers/__init__.py 負責它們的生命週期。管理器記錄清單以供內省,並按 kind: model-provider 進行分類。當你將一個未標記的用戶插件放入 $HERMES_HOME/plugins/ 且該插件恰好使用 ProviderProfile 調用 register_provider 時,管理器會通過源文本啟發式方法自動將其強制轉換為 kind: model-provider — 因此即使沒有 plugin.yaml,插件也能正確路由。

通過 pip 分發

像任何 Hermes 插件一樣,模型提供商可以作為 pip 包發佈。在你的 pyproject.toml 中添加一個入口點:

[project.entry-points."hermes_agent.plugins"]
acme-inference = "acme_hermes_plugin:register"

…其中 acme_hermes_plugin:register 是一個調用 register_provider(profile) 的函數。通用 PluginManager 在 discover_and_load() 期間拾取入口點插件。對於 kind: model-provider 的 pip 插件,你仍然需要在清單中聲明種類(或依賴源文本啟發式方法)。

請參閱 構建 Hermes 插件 以獲取完整的入口點設置。