Translates the full English docs corpus (335 files) into Simplified Chinese under website/i18n/zh-Hans/. Combined with PR #31895 (cross- locale link fix), the 简体中文 locale toggle now serves a complete Chinese site with working cross-page navigation. Pipeline: - Claude Sonnet 4.6 via OpenRouter, 8-way concurrent - Preserves frontmatter keys, code blocks, MDX/JSX, link URLs, brand names, and technical jargon (prompt/token/hook/MCP/ACP/etc.) - Translates only frontmatter title/description and prose - Two largest files (configuration.md 93KB, research-paper-writing.md 107KB) retried with 64K max_tokens after initial fence-drift - 3 manual post-fixes for MDX edge cases the model didn't escape: < in optional-skills-catalog table, double-quotes in an alt= tag, and a bare URL adjacent to a full-width period Cost: ~$30 total (Sonnet 4.6 input $3/M + output $15/M). Verified `npm run build` succeeds for both en and zh-Hans locales, no double-prefixed /docs/zh-Hans/docs/ URLs in rendered output, all in-page navigation resolves correctly. Translations are machine-generated and may need human review on specific pages — but they're an enormous improvement over the previous state (3 zh-Hans pages out of 335).
11 KiB
| sidebar_position | title | description |
|---|---|---|
| 12 | 网页搜索提供商插件 | 如何为 Hermes Agent 构建网页搜索/提取/爬取后端插件 |
构建网页搜索提供商插件
网页搜索提供商插件注册一个后端,用于处理 web_search、web_extract 以及(可选的)深度爬取工具调用。内置提供商——Firecrawl、SearXNG、Tavily、Exa、Parallel、Brave Search(免费层)和 DDGS——均以插件形式存放于 plugins/web/<name>/ 目录下。你可以在该目录旁新建一个目录来添加新提供商,或覆盖已有的内置提供商。
:::tip 网页搜索是 Hermes 支持的多种后端插件之一。其他插件(各有其 ABC)包括:图像生成提供商插件、视频生成提供商插件、记忆提供商插件、上下文引擎插件和模型提供商插件。通用工具/hook/CLI 插件请参阅构建 Hermes 插件。 :::
发现机制
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 中的注册表。各能力的活跃提供商由配置决定:
| 能力 | 配置键 | 回退至 |
|---|---|---|
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 的清单文件
brave_free/ 和 ddgs/ 是代码库中最小的参考实现——brave_free 是需要 API key 的纯搜索提供商,ddgs 是无需 key 且懒加载 SDK 的提供商。
WebSearchProvider ABC
继承 agent.web_search_provider.WebSearchProvider。唯一必须实现的成员是 name、is_available(),以及你所实现的 search() / extract() / crawl() 中的相应方法。
# 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 supports_crawl(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)}
# 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", []))
],
},
}
# 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
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_env |
在 hermes plugins install 期间进行交互式凭据提示(富格式说明参见构建 Hermes 插件) |
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 路由的能力标志 |
supports_crawl() |
— | False |
深度爬取模式的能力标志 |
search(query, limit) |
条件必须 | 抛出异常 | 当 supports_search() 返回 True 时必须实现 |
extract(urls, **kwargs) |
条件必须 | 抛出异常 | 当 supports_extract() 返回 True 时必须实现 |
crawl(url, **kwargs) |
条件必须 | 抛出异常 | 当 supports_crawl() 返回 True 时必须实现 |
提供商可以在单个类中声明多种能力——Firecrawl、Tavily、Exa 和 Parallel 均实现了搜索/提取/爬取三种能力。Brave Search 和 DDGS 仅支持搜索;SearXNG 也仅支持搜索,并有文档说明的"与提取提供商配对使用"工作流。
响应格式
工具包装器期望固定的响应信封(envelope),以避免在不同后端之间进行转换。
搜索成功:
{
"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, # optional
"error": str, # optional, only on per-URL failure
},
...
],
}
任意能力,失败时:
{"success": False, "error": "human-readable message"}
search() 和 extract() 均可定义为 async def——调度器通过 inspect.iscoroutinefunction 检测协程函数并相应地进行 await。对于小型后端,执行阻塞 I/O(HTTP、SDK 调用)的同步实现也完全可行;调度器会处理线程调度。
能力标志
Hermes 根据 supports_* 标志将调用路由至正确的提供商。一种常见的多提供商配置:
# ~/.hermes/config.yaml
web:
search_backend: "brave-free" # 纯搜索,速度快,每月免费 2k 次
extract_backend: "firecrawl" # 提取 + 爬取,付费配额
当 web.search_backend 或 web.extract_backend 未设置时,均回退至 web.backend。若该项也未设置,Hermes 将根据环境变量的存在情况,选取第一个支持所请求能力的可用提供商。
如果你的提供商只支持一种能力,将其他标志保持默认值(False)即可,注册表会在对应工具调用时跳过它——当用户仅将 X 用于搜索而要求 agent 进行提取时,不会看到误导性的"提供商 X 失败"错误。
Hermes 如何将其接入工具
web_search 和 web_extract 工具位于 tools/web_tools.py。调用时执行以下步骤:
- 读取相关配置键(
web_search对应web.search_backend,web_extract对应web.extract_backend) - 向注册表查询具有该
name的提供商 - 检查
is_available()及对应的supports_*()标志 - 调度至
search()/extract()/crawl(),若方法为协程则进行 await - 将响应信封 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 分发
# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-backend-web = "my_backend_web_package"
my_backend_web_package 必须暴露顶层 register 函数。完整配置说明参见通用插件指南中的通过 pip 分发。
相关页面
- 网页搜索 — 面向用户的功能文档及各后端配置说明
- 插件概览 — 所有插件类型一览
- 构建 Hermes 插件 — 通用工具/hook/斜杠命令指南