Skip to content

跳到主要内容

本页总览

网页搜索提供商插件注册一个后端,用于处理 web_searchweb_extract 以及(可选的)深度爬取工具调用。内置提供商——Firecrawl、SearXNG、Tavily、Exa、Parallel、Brave Search(免费层)和 DDGS——均以插件形式存放于 plugins/web/<name>/ 目录下。你可以在该目录旁新建一个目录来添加新提供商,或覆盖已有的内置提供商。

提示

网页搜索是 Hermes 支持的多种 后端插件 之一。其他插件(各有其 ABC)包括: 图像生成提供商插件视频生成提供商插件记忆提供商插件上下文引擎插件模型提供商插件。通用工具/hook/CLI 插件请参阅 构建 Hermes 插件

发现机制

Hermes 在三个位置扫描网页搜索后端:

  1. 内置<repo>/plugins/web/<name>/(以 kind: backend 自动加载,始终可用)
  2. 用户~/.hermes/plugins/web/<name>/(通过 plugins.enabledhermes plugins enable <name> 按需启用)
  3. Pip — 声明了 hermes_agent.plugins 入口点的包

每个插件的 register(ctx) 函数调用 ctx.register_web_search_provider(...) ——将实例注册到 agent/web_search_registry.py 中的注册表。各能力的活跃提供商由配置决定:

能力配置键回退至
web_searchweb.search_backendweb.backend
web_extractweb.extract_backendweb.backend
web_extract 内的深度爬取模式web.extract_backendweb.backend

若两个键均未设置,Hermes 将根据环境中存在的 API key/URL 自动检测后端。hermes tools 会引导用户完成选择。

目录结构

text
plugins/web/my-backend/

├── __init__.py     # register() 入口点

├── provider.py     # WebSearchProvider 子类

└── plugin.yaml     # 包含 kind: backend 和 provides_web_providers 的清单文件

brave_free/ddgs/ 是代码库中最小的参考实现——brave_free 是需要 API key 的纯搜索提供商,ddgs 是无需 key 且懒加载 SDK 的提供商。

WebSearchProvider ABC

继承 agent.web_search_provider.WebSearchProvider。唯一必须实现的成员是 nameis_available(),以及你所实现的 search() / extract() / crawl() 中的相应方法。

python
# plugins/web/my-backend/provider.py

from __future__ import annotations

import os

from typing import Any, Dict, List

from agent.web_search_provider import WebSearchProvider

class MyBackendWebSearchProvider(WebSearchProvider):

    """Minimal search-only provider against the My Backend HTTP API."""

    @property

    def name(self) -> str:

        # Stable id used in web.search_backend / web.extract_backend / web.backend

        # config keys. Lowercase, no spaces; hyphens permitted.

        return "my-backend"

    @property

    def display_name(self) -> str:

        # Human label shown in `hermes tools`. Defaults to `name`.

        return "My Backend"

    def is_available(self) -> bool:

        # Cheap check — env var present, optional dep importable, etc.

        # MUST NOT make network calls (runs on every `hermes tools` paint).

        return bool(os.getenv("MY_BACKEND_API_KEY", "").strip())

    def supports_search(self) -> bool:

        return True

    def supports_extract(self) -> bool:

        return False

    def search(self, query: str, limit: int = 5) -> Dict[str, Any]:

        import httpx

        api_key = os.environ["MY_BACKEND_API_KEY"]

        try:

            resp = httpx.get(

                "

                params={"q": query, "count": max(1, min(int(limit), 20))},

                headers={"Authorization": f"Bearer {api_key}"},

                timeout=15,

            )

            resp.raise_for_status()

            data = resp.json()

        except httpx.HTTPError as exc:

            return {"success": False, "error": str(exc)}

        # Response shape is fixed — see "Response shape" below.

        return {

            "success": True,

            "data": {

                "web": [\
\
                    {\
\
                        "title": item.get("title", ""),\
\
                        "url": item.get("url", ""),\
\
                        "description": item.get("snippet", ""),\
\
                        "position": idx + 1,\
\
                    }\
\
                    for idx, item in enumerate(data.get("results", []))\
\
                ],

            },

        }
python
# plugins/web/my-backend/__init__.py

from plugins.web.my_backend.provider import MyBackendWebSearchProvider

def register(ctx) -> None:

    """Plugin entry point — called once at load time."""

    ctx.register_web_search_provider(MyBackendWebSearchProvider())

plugin.yaml

yaml
name: web-my-backend

version: 1.0.0

description: "My Backend web search — Bearer-auth REST API"

author: Your Name

kind: backend

provides_web_providers:

  - my-backend

requires_env:

  - MY_BACKEND_API_KEY
用途
kind: backend将插件路由至后端加载路径
provides_web_providers该插件注册的提供商 name 列表——在 register() 运行之前,加载器即可通过此字段在 hermes tools 中公示插件
requires_envhermes plugins install 期间进行交互式凭据提示(富格式说明参见 构建 Hermes 插件

ABC 参考

完整契约位于 agent/web_search_provider.py。可覆盖的方法如下:

成员必须默认值用途
nameweb.*_backend 配置中使用的稳定 id
display_namenamehermes tools 中显示的标签
is_available()轻量可用性检查——环境变量、可选依赖等
supports_search()Trueweb_search 路由的能力标志
supports_extract()Falseweb_extract 路由的能力标志
search(query, limit)条件必须抛出异常supports_search() 返回 True 时必须实现
extract(urls, **kwargs)条件必须抛出异常supports_extract() 返回 True 时必须实现

提供商可以在单个类中声明多种能力——Firecrawl、Tavily、Exa 和 Parallel 均实现了搜索和提取两种能力。Brave Search 和 DDGS 仅支持搜索;SearXNG 也仅支持搜索,并有文档说明的"与提取提供商配对使用"工作流。

响应格式

工具包装器期望固定的响应信封(envelope),以避免在不同后端之间进行转换。

搜索成功:

python
{

    "success": True,

    "data": {

        "web": [\
\
            {"title": str, "url": str, "description": str, "position": int},\
\
            ...\
\
        ],

    },

}

提取成功:

python
{

    "success": True,

    "data": [\
\
        {\
\
            "url": str,\
\
            "title": str,\
\
            "content": str,\
\
            "raw_content": str,\
\
            "metadata": dict,    # optional\
\
            "error": str,        # optional, only on per-URL failure\
\
        },\
\
        ...\
\
    ],

}

任意能力,失败时:

python
{"success": False, "error": "human-readable message"}

search()extract() 均可定义为 async def——调度器通过 inspect.iscoroutinefunction 检测协程函数并相应地进行 await。对于小型后端,执行阻塞 I/O(HTTP、SDK 调用)的同步实现也完全可行;调度器会处理线程调度。

能力标志

Hermes 根据 supports_* 标志将调用路由至正确的提供商。一种常见的多提供商配置:

yaml
# ~/.hermes/config.yaml

web:

  search_backend: "brave-free"     # 纯搜索,速度快,每月免费 2k 次

  extract_backend: "firecrawl"     # 提取 + 爬取,付费配额

web.search_backendweb.extract_backend 未设置时,均回退至 web.backend。若该项也未设置,Hermes 将根据环境变量的存在情况,选取第一个支持所请求能力的可用提供商。

如果你的提供商只支持一种能力,将其他标志保持默认值(False)即可,注册表会在对应工具调用时跳过它——当用户仅将 X 用于搜索而要求 agent 进行提取时,不会看到误导性的"提供商 X 失败"错误。

Hermes 如何将其接入工具

web_searchweb_extract 工具位于 tools/web_tools.py。调用时执行以下步骤:

  1. 读取相关配置键(web_search 对应 web.search_backendweb_extract 对应 web.extract_backend
  2. 向注册表查询具有该 name 的提供商
  3. 检查 is_available() 及对应的 supports_*() 标志
  4. 调度至 search() / extract() / crawl(),若方法为协程则进行 await
  5. 将响应信封 JSON 序列化后返回给 LLM

错误以工具结果的形式呈现;LLM 决定如何解释。若没有提供商被注册(或所有可用提供商均未通过能力检查),工具将返回一条指向 hermes tools 的友好错误信息。

懒加载可选依赖

如果你的提供商封装了第三方 SDK(如 DDGS 封装了 ddgs 包),请勿在模块顶层 import。在 is_available()search() 内部使用 tools.lazy_deps.ensure(...) ——Hermes 将在首次使用时安装该包,并受 security.allow_lazy_installs 控制。安全模型详见 构建 Hermes 插件 → 懒加载

参考实现

  • plugins/web/brave_free/ — 小型、需要 API key 的纯搜索 HTTP 提供商。适合作为起始模板。
  • plugins/web/ddgs/ — 无需 key、懒加载 SDK 的提供商。适用于封装 Python 包的后端。
  • plugins/web/firecrawl/ — 完整的多能力提供商(搜索 + 提取 + 爬取),支持多种格式模式。
  • plugins/web/searxng/ — 自托管、通过 URL 配置、无需认证的后端。
  • plugins/web/xai/ — 通过 Grok 服务端 web_search 工具实现的 LLM 驱动搜索。展示了如何复用现有的 OAuth/环境变量凭据(tools/xai_http.py)而无需新增环境变量,以及如何编写遵守无网络调用约定的轻量 is_available()

通过 pip 分发

toml
# pyproject.toml

[project.entry-points."hermes_agent.plugins"]

my-backend-web = "my_backend_web_package"

my_backend_web_package 必须暴露顶层 register 函数。完整配置说明参见通用插件指南中的 通过 pip 分发

相关页面

Unofficial documentation. Source: hermes-agent.nousresearch.com