Skip to content

feat: add SerpBase (Google) search engine via REST API - #323

Open
gefsikatsinelou wants to merge 1 commit into
lanl:mainfrom
gefsikatsinelou:feat/add-serpbase-engine
Open

gefsikatsinelou wants to merge 1 commit into
lanl:mainfrom
gefsikatsinelou:feat/add-serpbase-engine

Conversation

@gefsikatsinelou

Copy link
Copy Markdown

Summary

Adds an optional Google search backend to WebSearchAgent via the SerpBase REST API, with a graceful fallback to the existing DuckDuckGo (DDGS) path. No new dependencies.

Background

WebSearchAgent._search currently relies solely on ddgs (DuckDuckGo). In practice DuckDuckGo rate-limits aggressively for agent-style bursts (and is blocked from some datacenter IPs), so research runs that need Google coverage have no option today. SerpBase is a lightweight Google Search Results API (structured organic_results JSON, no scraping, no headless browser) that slots into the existing _search → _materialize flow without touching the acquisition graph.

Changes

  • Updated: src/ursa/agents/acquisition_agents.py
    • WebSearchAgent.__init__ reads SERPBASE_API_KEY from the environment.
    • New _serpbase_search() — GET https://api.serpbase.dev/google/search?q=...&num=<max_results>, maps organic_results → the same {title, href, body, position} dict shape DDGS produces (so _id, _materialize, and _citation work unchanged).
    • _search() prefers SerpBase when the key is set; falls back to DDGS when the key is missing, the API errors, or returns no results.
    • Added module-level logger (used by the fallback warning); no behavioral change elsewhere.
  • Updated: docs/agents/acquisition/web-search.md — documents the new env var, the fallback behavior, and that no new dependency is required.

Design decisions

Decision Rationale
SERPBASE_API_KEY via env var Matches existing convention (UNPAYWALL_EMAIL, URSA_TEXT_EXTENSIONS are read from os.environ).
Graceful fallback to DDGS Key unset / API failure / empty results → existing behavior, so nothing breaks for users without a key.
Reuses requests Already imported and used throughout acquisition_agents.py; no new dependency.
Result dict shape identical to DDGS _id/_materialize/_citation operate on href/title/body — zero changes needed downstream.

Testing

  • python -m py_compile src/ursa/agents/acquisition_agents.py passes.
  • With SERPBASE_API_KEY set: _search returns Google organic_results mapped to {title, href, body, position}.
  • With key unset: _search behaves exactly as before (DDGS only).
  • API error/empty response: logs a warning, falls back to DDGS — other agents and the acquisition graph are unaffected.
WebSearchAgent now reads SERPBASE_API_KEY and queries the SerpBase Google
Search API when set, falling back to DuckDuckGo (DDGS) when the key is
missing or the API call fails. No new dependencies; reuses requests.
Docs updated in docs/agents/acquisition/web-search.md.

@mikegros mikegros left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great and having approaches and fallbacks are a great idea. I need to test it out more, but wanted to bring up 2 things:

  1. There was some stuff removed and I am not clear why (see my comment).

  2. It would be really great to also have Tavily as an optional search if there is an API key set for it. So that for the user, the precedence went Serp -> Tavily -> DDGS. This would be nice from the perspective of giving the user the ability to use different search backends without affecting current behavior.

I could always implement (2) in the future if you would rather just get this merged, but I'd like clarity on (1) first.

"duckduckgo-search (DDGS) is required for WebSearchAgentGeneric."
)

def _id(self, hit_or_item: dict[str, Any]) -> str:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are _id and _citation being removed here? I think they are used in _materialize? Is there a reason they are removed?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants