把自定义模型 Provider 打包成 pip 插件,Hermes 装上就能用

想象一下这个场景:你们团队内部搭了一个统一的推理网关,跑着公司自己的微调模型,也代理了几个第三方模型。每次有新模型上线,你都得手动编辑 Hermes 的 config.yaml,把 endpoint、API key、模型名一项项填进去——然后其他同事还得在各自的机器上重复一遍同样的操作。要是能把“如何接入我们公司的模型”打包成一个文件,让任何人 pip install 一下就能用,该多好?8 月 13 日合并的 PR #85504 把这个想法变成了现实:模型 Provider 现在可以做成普通的 Python 包,通过 entry points 自动注册。装好之后,Hermes 启动时就能发现它,包里的模型直接出现在模型注册表里,跟内置 Provider 一样用。
机制:一个 entry point 搞定注册
Hermes 的 Provider 是“如何连上一个模型服务”的接入层:它知道 endpoint 长什么样、API key 填哪里、请求怎么发。以前 Provider 只能通过文件系统插件或内置方式注册;PR #85504 在 providers/__init__.py 的发现流程里加了“第 0 步”——扫描所有已安装 Python 包的 entry points,看有没有人声明自己是 Provider。
做法很简单:在你自己的 Python 包里,于 pyproject.toml 中声明一个 entry point,指向一个零参数的注册函数:
[project.entry-points."hermes_agent.plugins"]
acme-inference = "acme_hermes_plugin:register"
对应的 register() 函数构造一个 ProviderProfile 并调用 register_provider() 完成注册:
# acme_hermes_plugin.py
from providers import register_provider
from providers.base import ProviderProfile
def register():
register_provider(
ProviderProfile(
name="acme-inference",
display_name="Acme Inference",
base_url="https://inference.internal.acme.com/v1",
env_vars=("ACME_API_KEY",),
auth_type="api_key",
)
)
ProviderProfile 的字段就是“接入一个模型服务”所需的全部信息:名字、显示名、默认 endpoint、API key 的环境变量名、认证方式,以及可选的回退模型列表(fallback_models)等。官方文档中 Provider 插件的目录式写法($HERMES_HOME/plugins/model-providers/<name>/__init__.py)用的就是同一套 API——entry point 机制只是把“放进目录”换成了“pip 安装”,注册代码本身一模一样。
除了 module:func 这种“可调用对象”形式,entry point 也可以只写模块名(acme-inference = "acme_hermes_plugin")——Hermes 会直接导入这个模块,靠模块级的 register_provider 调用完成注册,这和文件系统插件的 __init__.py 约定完全一致。装好包、按下面的门槛开启后,Hermes 启动时就会自动调用它,你的模型就出现在 hermes model 之类的模型列表里了。
关键门槛:装了 ≠ 启用了
这是整个机制里最重要的一条规则,一定要理解:pip 安装了 ≠ 就会加载。entry point 扫描和通用插件管理器共用同一套开关——plugins.enabled 白名单和 plugins.disabled 黑名单:
plugins:
enabled:
- acme-inference # 只有列在这里的 entry point 才会被加载
disabled:
- some-other-plugin # 黑名单优先级更高,永远赢
如果 plugins.enabled 里没有你的包名,Hermes 连导入都不会做——一个 pip 包永远不会“仅仅因为被安装了”就获得运行权限。这在安全上意义重大:你 pip install 的任何一个包都可能偷偷声明 hermes_agent.plugins entry point,但只有你明确列入白名单的那个才会真正生效。
设计上的安全细节
除了上面的白名单门槛,这个机制还有几层防护,理解它们能帮你避坑:
- Provider 专属通道:
hermes_agent.plugins这个 entry point 组是共享的,通用插件(比如注册界面、注册工具的插件)也用它。区别在于通用插件的注册函数带参数(register(ctx)),而 Provider 注册函数约定为零参数。扫描器会跳过所有需要参数的 callable,所以通用插件不会被误当成 Provider 调用,也不会刷一堆 TypeError 警告。 - 单个失败不拖垮全局:某个第三方包写坏了,它的 entry point 加载失败只会在日志里记一条 warning,整个 Provider 发现流程继续跑——坏包不会让 Hermes 起不来。
- 内置 Provider 永远优先:entry point 扫描排在发现流程的最前面,而
register_provider()是“后写覆盖”语义——这意味着同名的包永远盖不过内置 Provider 和$HERMES_HOME下的插件。一个 pip 包不可能劫持 Hermes 自己的 Provider 名字,顶多是注册一个全新的名字。
什么时候值得用
- 团队共享一套模型接入:把公司推理网关封装成一个 pip 包,同事
pip install+ 一行白名单配置就完成接入,不用再各自手改 endpoint。 - 私有/自托管模型:公司内部微调模型、自建 vLLM 集群,都可以封装成 Provider 包,版本跟着 pip 走,升级就是
pip install -U。 - 想发布给全世界:你的 Provider 接入方式有通用价值,直接发到 PyPI——任何人装完白名单一配就能用。
如果你已经在用 MCP 扩展 Hermes 的工具能力(见 MCP 配置与上下文变量完全指南),Provider 插件正好补上另一块拼图:MCP 管“工具”,Provider 管“模型”——两者都是声明式接入,但一个走 MCP 协议,一个走 entry points。插件系统的完整命令参考在 hermes plugins 命令页;另外提醒一句,多 Profile 环境下各 Profile 的 plugins.enabled 是独立配置的(多实例玩法见 Profile 多实例完全指南),别在一个 Profile 里开好白名单就以为全局都生效了。
一句话总结:Provider 插件化让“接入一个新模型服务”从“手改配置”变成“pip install + 白名单一行”——门槛在安全上(白名单必须显式开启),便利在工程上(打包、共享、随版本走)。