构建图像生成提供商插件

图像生成提供商插件注册一个后端,为每次 image_generate 工具调用提供服务——DALL·E、gpt-image、Grok、Flux、Imagen、Stable Diffusion、fal、Replicate、本地 ComfyUI 环境,等等。内置提供商(OpenAI、OpenAI-Codex、xAI)均以插件形式提供。您可以通过将目录放入 plugins/image_gen/<name>/ 来添加新插件,或覆盖已捆绑的插件。

:::tip 图像生成是 Hermes 支持的几种后端插件之一。其他插件(具有更专门的 ABC)包括内存提供商插件上下文引擎插件模型提供商插件。通用工具/钩子/CLI 插件请参阅构建 Hermes 插件。 :::

发现机制

Hermes 在三个位置扫描图像生成后端:

  1. 内置<repo>/plugins/image_gen/<name>/(自动加载 kind: backend,始终可用)
  2. 用户~/.hermes/plugins/image_gen/<name>/(通过 plugins.enabled 选择加入)
  3. Pip — 声明了 hermes_agent.plugins 入口点的包

每个插件的 register(ctx) 函数调用 ctx.register_image_gen_provider(...)——这将其注册到 agent/image_gen_registry.py 中的注册表中。当前激活的提供商由 config.yaml 中的 image_gen.provider 选择;hermes tools 引导用户完成选择。

image_generate 工具包装器向注册表查询激活的提供商并分发到该处。如果未注册提供商,该工具会显示一条有用的错误信息,指向 hermes tools

目录结构

plugins/image_gen/my-backend/
├── __init__.py      # ImageGenProvider 子类 + register()
└── plugin.yaml      # 清单文件,包含 kind: backend

至此,一个内置插件就完整了。位于 ~/.hermes/plugins/image_gen/<name>/ 的用户插件需要添加到 config.yamlplugins.enabled 中(或运行 hermes plugins enable <name>)。

ImageGenProvider 抽象基类

继承 agent.image_gen_provider.ImageGenProvider。唯一必需的成员是 name 属性和 generate() 方法——其他所有内容都有合理的默认值:

# plugins/image_gen/my-backend/__init__.py
from typing import Any, Dict, List, Optional
import os
 
from agent.image_gen_provider import (
    DEFAULT_ASPECT_RATIO,
    ImageGenProvider,
    error_response,
    resolve_aspect_ratio,
    save_b64_image,
    success_response,
)
 
 
class MyBackendImageGenProvider(ImageGenProvider):
    @property
    def name(self) -> str:
        # 在 image_gen.provider 配置中使用的稳定 ID。小写,无空格。
        return "my-backend"
 
    @property
    def display_name(self) -> str:
        # 在 `hermes tools` 中显示的人类可读标签。如果省略,默认为 name.title()。
        return "My Backend"
 
    def is_available(self) -> bool:
        # 如果凭据或依赖缺失,返回 False。
        # 工具的可用性检查在分发前调用此方法。
        if not os.environ.get("MY_BACKEND_API_KEY"):
            return False
        try:
            import my_backend_sdk  # noqa: F401
        except ImportError:
            return False
        return True
 
    def list_models(self) -> List[Dict[str, Any]]:
        # 在 `hermes tools` 模型选择器中显示的目录。
        return [
            {
                "id": "my-model-fast",
                "display": "My Model (Fast)",
                "speed": "~5s",
                "strengths": "快速迭代",
                "price": "$0.01/image",
            },
            {
                "id": "my-model-hq",
                "display": "My Model (HQ)",
                "speed": "~30s",
                "strengths": "最高保真度",
                "price": "$0.04/image",
            },
        ]
 
    def default_model(self) -> Optional[str]:
        return "my-model-fast"
 
    def get_setup_schema(self) -> Dict[str, Any]:
        # 用于 `hermes tools` 选择器的元数据——设置时提示输入的键。
        return {
            "name": "My Backend",
            "badge": "paid",        # 可选;在选择器中显示为短标签
            "tag": "名称下显示的一行描述",
            "env_vars": [
                {
                    "key": "MY_BACKEND_API_KEY",
                    "prompt": "My Backend API 密钥",
                    "url": "https://my-backend.example.com/api-keys",
                },
            ],
        }
 
    def generate(
        self,
        prompt: str,
        aspect_ratio: str = DEFAULT_ASPECT_RATIO,
        **kwargs: Any,
    ) -> Dict[str, Any]:
        prompt = (prompt or "").strip()
        aspect_ratio = resolve_aspect_ratio(aspect_ratio)
 
        if not prompt:
            return error_response(
                error="提示词不能为空",
                error_type="invalid_input",
                provider=self.name,
                prompt="",
                aspect_ratio=aspect_ratio,
            )
 
        # 模型选择优先级:环境变量 → 配置 → 默认值。辅助方法
        # _resolve_model() 在内置的 openai 插件中是一个很好的参考。
        model_id = kwargs.get("model") or self.default_model() or "my-model-fast"
 
        try:
            import my_backend_sdk
            client = my_backend_sdk.Client(api_key=os.environ["MY_BACKEND_API_KEY"])
            result = client.generate(
                prompt=prompt,
                model=model_id,
                aspect_ratio=aspect_ratio,
            )
 
            # 支持两种格式:
            #   - URL 字符串:直接作为 `image` 返回
            #   - base64 数据:通过 save_b64_image() 保存到 $HERMES_HOME/cache/images/
            if result.get("image_b64"):
                path = save_b64_image(
                    result["image_b64"],
                    prefix=self.name,
                    extension="png",
                )
                image = str(path)
            else:
                image = result["image_url"]
 
            return success_response(
                image=image,
                model=model_id,
                prompt=prompt,
                aspect_ratio=aspect_ratio,
                provider=self.name,
            )
        except Exception as exc:
            return error_response(
                error=str(exc),
                error_type=type(exc).__name__,
                provider=self.name,
                model=model_id,
                prompt=prompt,
                aspect_ratio=aspect_ratio,
            )
 
 
def register(ctx) -> None:
    """插件入口点——加载时调用一次。"""
    ctx.register_image_gen_provider(MyBackendImageGenProvider())

plugin.yaml

name: my-backend
version: 1.0.0
description: 我的图像后端——通过 My Backend SDK 实现文本到图像
author: Your Name
kind: backend
requires_env:
  - MY_BACKEND_API_KEY

kind: backend 将插件路由到图像生成的注册路径。requires_envhermes plugins install 期间会提示输入。

抽象基类参考

完整契约见 agent/image_gen_provider.py。您通常会覆盖的方法:

成员是否必需默认值用途
nameimage_gen.provider 配置中使用的稳定 ID
display_namename.title()hermes tools 中显示的标签
is_available()True检查凭据/依赖是否缺失的门控
list_models()[]hermes tools 模型选择器使用的目录
default_model()list_models() 的第一个未配置模型时的回退
get_setup_schema()最小化选择器元数据 + 环境变量提示
generate(prompt, aspect_ratio, **kwargs)实际调用

响应格式

generate() 必须返回通过 success_response()error_response() 构建的字典。两者均位于 agent/image_gen_provider.py

成功:

success_response(
    image=<url--绝对路径>,
    model=<model-id>,
    prompt=<回显的提示词>,
    aspect_ratio="landscape" | "square" | "portrait",
    provider=<您的提供商名称>,
    extra={...},  # 可选的提供商特有字段
)

错误:

error_response(
    error="人类可读的错误消息",
    error_type="provider_error" | "invalid_input" | "<异常类名>",
    provider=<您的提供商名称>,
    model=<model-id>,
    prompt=<提示词>,
    aspect_ratio=<解析后的宽高比>,
)

工具包装器将字典 JSON 序列化后交给 LLM。错误以工具结果的形式呈现;LLM 决定如何向用户解释。

处理 base64 与 URL 输出

某些后端返回图像 URL(fal、Replicate);其他返回 base64 数据(OpenAI gpt-image-2)。对于 base64 情况,使用 save_b64_image()——它将图像写入 $HERMES_HOME/cache/images/<prefix>_<timestamp>_<uuid>.<ext> 并返回绝对 Path。将该路径(以 str 形式)作为 image= 传递给 success_response()。网关交付(Telegram 照片气泡、Discord 附件)能识别 URL 和绝对路径。

用户覆盖

将用户插件放入 ~/.hermes/plugins/image_gen/<name>/,使用与内置插件相同的 name 属性,并通过 hermes plugins enable <name> 启用——注册表采用后写者胜出策略,因此您的版本会替换内置版本。这对于将 openai 插件指向私有代理,或替换自定义模型目录非常有用。

测试

export HERMES_HOME=/tmp/hermes-imggen-test
mkdir -p $HERMES_HOME/plugins/image_gen/my-backend
# …将 __init__.py + plugin.yaml 复制到该目录…
 
export MY_BACKEND_API_KEY=your-test-key
hermes plugins enable my-backend
 
# 将其选为激活的提供商
echo "image_gen:" >> $HERMES_HOME/config.yaml
echo "  provider: my-backend" >> $HERMES_HOME/config.yaml
 
# 运行测试
hermes -z "Generate an image of a corgi in a spacesuit"

或者交互式操作:hermes tools → “Image Generation” → 选择 my-backend → 如提示输入 API 密钥。

参考实现

  • plugins/image_gen/openai/__init__.py — gpt-image-2 分为低/中/高三个等级,作为三个虚拟模型 ID 共享一个 API 模型,使用不同的 quality 参数。在单个后端下实现分层模型 + config.yaml 优先级链的良好示例。
  • plugins/image_gen/xai/__init__.py — 通过 xAI 实现的 Grok Imagine。不同的格式(URL 输出,更简单的目录)。
  • plugins/image_gen/openai-codex/__init__.py — Codex 风格 Responses API 变体,复用 OpenAI SDK,使用不同的路由基础 URL。

通过 pip 分发

# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-backend-imggen = "my_backend_imggen_package"

my_backend_imggen_package 必须暴露一个顶级 register 函数。有关完整设置,请参阅通用插件指南中的通过 pip 分发

相关页面