建立網頁搜尋 Provider 外掛
網頁搜尋 provider 外掛註冊一個後端來處理 web_search、web_extract 和(選擇性的)深層爬蟲工具呼叫。內建的 provider — Firecrawl、SearXNG、Tavily、Exa、Parallel、Brave Search(免費層級)、xAI 和 DDGS — 都以外掛形式發佈在 plugins/web/<name>/ 下。你可以透過在它們旁邊放入一個目錄來新增一個新的,或覆寫一個已有的。
提示
網頁搜尋是 Hermes 支援的多種後端外掛之一。其他類型(具有各自的 ABC)包括 Image Generation Provider Plugins、Video Generation Provider Plugins、Memory Provider Plugins、Context Engine Plugins 和 Model Provider Plugins。通用的工具/鉤子/CLI 外掛請見 Build a Hermes Plugin。
發現機制如何運作
Hermes 在三個位置掃描網頁搜尋後端:
- 內建 —
<repo>/plugins/web/<name>/(自動載入,kind: backend,始終可用) - 使用者 —
~/.hermes/plugins/web/<name>/(透過plugins.enabled或hermes plugins enable <name>選擇性啟用) - Pip — 宣告
hermes_agent.plugins入口點的套件
每個外掛的 register(ctx) 函式呼叫 ctx.register_web_search_provider(...) — 這會將實例放入 agent/web_search_registry.py 的 registry 中。每個功能的啟用 provider 由設定選擇:
| 功能 | 設定金鑰 | 回退到 |
|---|---|---|
web_search | web.search_backend | web.backend |
web_extract | web.extract_backend | web.backend |
web_extract 內的深層爬蟲模式 | web.extract_backend | web.backend |
當兩個金鑰都未設定時,Hermes 從環境中存在的 API key/URL 自動偵測後端。hermes tools 引導使用者進行選擇。
目錄結構
plugins/web/my-backend/
├── __init__.py # register() 入口點
├── provider.py # WebSearchProvider 子類別
└── plugin.yaml # 帶有 kind: backend 和 provides_web_providers 的 Manifest
brave_free/ 和 ddgs/ 是最小的樹內參考 — brave_free 用於 API key 門控的純搜尋 provider,ddgs 用於無需金鑰的 provider,它會延遲安裝其 SDK。
WebSearchProvider ABC
繼承 agent.web_search_provider.WebSearchProvider。唯一必要的成員是 name、is_available() 和你實作的 search() / extract() 中的任何一個。(深層爬蟲不是單獨的方法 — 它是 extract() 的一種模式。)
# 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):
"""針對 My Backend HTTP API 的最小搜尋專用 provider。"""
@property
def name(self) -> str:
# web.search_backend / web.extract_backend / web.backend
# 設定金鑰中使用的穩定 ID。小寫,不含空格;允許連字元。
return "my-backend"
@property
def display_name(self) -> str:
# `hermes tools` 中顯示的人類標籤。預設為 `name`。
return "My Backend"
def is_available(self) -> bool:
# 低成本檢查 — 環境變數存在、選用依賴可匯入等。
# 不能有網路呼叫(在每次 `hermes tools` 重繪時運行)。
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(
"https://api.example.com/search",
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)}
# 回應格式是固定的 — 見下方「回應格式」。
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", []))
],
},
}
# plugins/web/my-backend/__init__.py
from plugins.web.my_backend.provider import MyBackendWebSearchProvider
def register(ctx) -> None:
"""外掛入口點 — 在載入時呼叫一次。"""
ctx.register_web_search_provider(MyBackendWebSearchProvider())
plugin.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 | 此外掛註冊的 provider name 列表 — 載入器在 register() 執行前就用它來在 hermes tools 中廣告此外掛 |
requires_env | hermes plugins install 期間的互動式認證提示(見 Build a Hermes Plugin 了解完整格式) |
ABC 參考
完整契約在 agent/web_search_provider.py 中。你可能覆寫的方法:
| 成員 | 必要 | 預設值 | 用途 |
|---|---|---|---|
name | ✅ | — | web.*_backend 設定中使用的穩定 ID |
display_name | — | name | hermes tools 中顯示的標籤 |
is_available() | ✅ | — | 低成本可用性閘門 — 環境變數、選用依賴 |
supports_search() | — | True | web_search 路由的功能標誌 |
supports_extract() | — | False | web_extract 路由的功能標誌 |
search(query, limit) | 有條件 | 引發異常 | 當 supports_search() 回傳 True 時必要 |
extract(urls, **kwargs) | 有條件 | 引發異常 | 當 supports_extract() 回傳 True 時必要 |
Provider 可以從單個類別廣告多種功能 — Firecrawl、Tavily、Exa 和 Parallel 都同時實作了搜尋和擷取。Brave Search 和 DDGS 是純搜尋;SearXNG 是純搜尋,有文件記錄的「與擷取 provider 配對使用」工作流程。
回應格式
工具包裝器期望一個固定的信封,這樣它就不必在後端之間翻譯。
搜尋成功:
{
"success": True,
"data": {
"web": [
{"title": str, "url": str, "description": str, "position": int},
...
],
},
}
擷取成功:
{
"success": True,
"data": [
{
"url": str,
"title": str,
"content": str,
"raw_content": str,
"metadata": dict, # 選用
"error": str, # 選用,僅在每個 URL 失敗時
},
...
],
}
任一功能失敗時:
{"success": False, "error": "human-readable message"}
search() 和 extract() 都可以是 async def — 分派器透過 inspect.iscoroutinefunction 偵測協程函式並相應地等待。執行阻塞 I/O(HTTP、SDK 呼叫)的同步實作對小型後端來說沒問題;分派器會處理執行緒。
功能標誌
Hermes 根據 supports_* 標誌將呼叫路由到正確的 provider。常見的多 provider 設定:
# ~/.hermes/config.yaml
web:
search_backend: "brave-free" # 純搜尋,快速,免費 2k/月
extract_backend: "firecrawl" # 擷取 + 爬蟲,付費額度
當 web.search_backend 或 web.extract_backend 未設定時,兩者都會回退到 web.backend。如果那也未設定,Hermes 會根據環境變數的存在選擇第一個支援請求功能的可用 provider。
如果你的 provider 只支援一種功能,將另一個標誌保持在預設值(False),registry 會為該工具跳過它 — 當使用者只使用 X 進行搜尋而要求代理程式擷取時,不會看到誤導性的「provider X 失敗」錯誤。
Hermes 如何將其連接到工具
web_search 和 web_extract 工具位於 tools/web_tools.py 中。在呼叫時它們:
- 讀取相關的設定金鑰(
web_search的web.search_backend、web_extract的web.extract_backend) - 向 registry 請求具有該
name的 provider - 檢查
is_available()和匹配的supports_*()標誌 - 分派到
search()/extract()(深層爬蟲作為extract()內的一種模式運行),如果方法是協程則等待 - 將回應信封 JSON 序列化並交回 LLM
錯誤作為工具結果呈現;LLM 決定如何解釋它們。如果沒有已註冊的 provider(或每個可用的都未通過功能閘門),工具會回傳一個有用的錯誤,指向 hermes tools。
延遲安裝選用依賴
如果你的 provider 封裝了第三方 SDK(像 DDGS 封裝 ddgs 套件),不要在模組頂層 import 它。在 is_available() 或 search() 內使用 tools.lazy_deps.ensure(...) — Hermes 會在首次使用時安裝套件,由 security.allow_lazy_installs 門控。安全模型請見 Build a Hermes Plugin → Lazy-install。
參考實作
plugins/web/brave_free/— 小型、API key 門控、純搜尋的 HTTP provider。良好的起始模板。plugins/web/ddgs/— 無需金鑰、延遲安裝其 SDK 的 provider。對封裝 Python 套件的後端有用的模式。plugins/web/firecrawl/— 完整的多功能 provider(搜尋 + 擷取 + 爬蟲)帶有多種格式模式。plugins/web/searxng/— 自架設、URL 設定的後端,無需認證。plugins/web/xai/— 透過 Grok 伺服端web_search工具的 LLM 支援搜尋。展示如何重用現有的 OAuth/環境變數認證介面(tools/xai_http.py)而不新增環境變數,以及如何編寫遵循無網路契約的低成本is_available()。
透過 pip 分發
# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-backend-web = "my_backend_web_package"
my_backend_web_package 必須暴露一個頂層的 register 函式。完整設定請見通用外掛指南中的 Distribute via pip。
相關頁面
- Web Search — 面向使用者的功能文件和每個後端的設定
- Plugins overview — 所有外掛類型概覽
- Build a Hermes Plugin — 通用工具/鉤子/斜線命令指南