hermes-anysearch
AnySearch web search and content extraction for Hermes Agent, plus two real-time fallback chains that keep the web tools working when a primary backend fails.
| Provider | What it does | Select with |
|---|---|---|
anysearch |
AnySearch alone (REST /v1/search + /v1/extract) |
web.backend or per-capability keys |
exa-anysearch |
Exa first → AnySearch on any Exa failure | web.search_backend |
firecrawl-anysearch |
Firecrawl first → AnySearch on any Firecrawl failure | web.extract_backend |
Why the chains
Hermes' built-in runtime rescue only rescues a failed call to its keyless anonymous ring, which is heavily throttled. AnySearch's free tier is 1,000 requests/day (20 QPS with a key; anonymous access works without one), which makes it a strong second backend:
- Search: Exa's free credits drain under burst use; the chain retries Exa on every call, so a reset quota is picked up automatically with no manual switch-back.
- Extract: in a batch replay of 20 URLs that had previously failed on Firecrawl, most failures were backend-level (unconfigured, 60s scrape timeouts) rather than hard pages, and AnySearch recovered 65% of them. Firecrawl stays first because it clears anti-bot walls AnySearch cannot (npmjs, WeChat, z-lib).
Install
-
Clone into your Hermes plugin directory:
git clone https://github.com/<owner>/hermes-anysearch "$HERMES_HOME/plugins/anysearch" -
Enable the plugin (Hermes requires explicit opt-in for user plugins):
hermes plugins enable anysearch -
Optional but recommended — add an AnySearch API key. A free key raises the quota to 1,000 requests/day; get one at https://www.anysearch.com/console/api-keys.
# $HERMES_HOME/.env ANYSEARCH_API_KEY=as_sk_... -
Point the web tools at the chains (or pick them via
hermes tools):# $HERMES_HOME/config.yaml web: search_backend: exa-anysearch extract_backend: firecrawl-anysearch -
Fully restart Hermes Desktop — plugins and config load at startup.
Multiple profiles
Plugins are per-profile ($HERMES_HOME/plugins), so naively this directory
would need a copy per profile and drift. Instead, make every other profile a
directory junction (Windows) or symlink (Unix) to the single checkout:
python scripts/link-to-profiles.py # create/repair links
python scripts/link-to-profiles.py --verify # check only
Hermes' plugin scanner follows junctions/symlinks, so one checkout serves every
profile. Note that plugins.enabled and .env stay per-profile: a new profile
still needs hermes plugins enable anysearch and its own ANYSEARCH_API_KEY.
Notes from testing against the live API
/v1/searchreturns bothsnippetandcontent, butcontentis an abstract. Withformat: "markdown"it becomes a structured digest (roughly 2x the default length), never the full page — full text requires/v1/extract. This provider requestsformat: "markdown"and mapscontentto the result description.POST /v1/extractis the official REST endpoint and measured about 2x faster than the MCPextracttool (0.45s vs 0.91s for the same page). The MCP path is kept only as a fallback; itsresult.content[].textis a JSON string that needs a second parse.- With an
Authorizationheader present but the key invalid, the API returns 401/403 — it does not silently fall back to anonymous.
Tests
Run with the Hermes venv Python:
<hermes-agent>/venv/Scripts/python tests/test_provider.py # providers + simulated failover
<hermes-agent>/venv/Scripts/python tests/test_chains.py # extract chain
<hermes-agent>/venv/Scripts/python tests/test_e2e.py normal # through tools.web_tools
<hermes-agent>/venv/Scripts/python tests/test_e2e.py broken # real failures → rescue
License
MIT