Web search
Web search answers questions outside Atamaia's stores — current docs, error messages, public pages. It is not memory search, not the code graph, and not facts.
One implementation serves both the platform route and agent/MCP tools on purpose. Two consumers of one idea drift apart; a dead primary that only one path noticed is how search stayed "fine" for weeks while quality collapsed.
All endpoints require authentication. Responses are wrapped in ApiEnvelope<T> on REST. Canonical API host: https://api.atamaia.ai.
What it is (and is not)
Is: a meta-search call that returns titles, URLs, snippets, and which provider answered.
Is not:
- Grounding that is automatically written to memory (caller decides what to keep).
- A guarantee of engine ranking quality — that depends on upstream engines behind SearXNG.
- Google Programmable Search as the product primary (that era ended; see below).
Why SearXNG replaced what came before
Previous order: Google Programmable Search Engine, falling back to DuckDuckGo HTML scraping.
What broke (service remarks, 2026-08-12):
- Google discontinued PSE. Prod's configured key returned HTTP 400 "API key not valid."
- Failure fell through to the scraper silently. Agents still got results; nothing said the primary was dead.
- Worse: prod carried
External__Google__ApiKey=CHANGE_ME.IsNullOrEmptytreated it as set, so the tool sent the literal placeholder to Google. That looked like an expired key rather than a key that had never been configured — scraper from day one, invisible.
SearXNG is now first:
- Self-hosted per box (
deploy/searxng), bound to loopback - No API key, no cloud quota
- Queries many engines at once — one upstream blocking degrades quality instead of breaking search
- Default URL:
http://127.0.0.1:8888(External:SearXNG:Url)
Fallbacks remain but are loud: Warning logs name the provider; the response provider field is part of the contract so callers can see degradation without reading logs.
Provider chain
| Order | provider value |
Notes |
|---|---|---|
| 1 | searxng |
Healthy path |
| 2 | google-pse |
Only if real key + cx; placeholders skipped |
| 3 | duckduckgo-scrape |
HTML regex parse — brittle; last resort |
| — | none |
Every provider failed; empty results |
Caller rule: if provider is anything other than searxng, treat results as degraded — usable under caution, not equivalent to the primary.
Live probe (2026-08-13): query Atamaia AI platform → provider: "searxng", three hits, engines listing e.g. google cse, duckduckgo (upstream engines inside SearXNG, not the platform fallback names).
How a caller uses it
REST
GET /api/system/web-search?query={q}&maxResults={n}
Aliases: q for query. maxResults optional (service default 8).
Response (WebSearchResponse):
| Field | Type | Meaning |
|---|---|---|
query |
string | Echo of the request |
provider |
string | searxng | google-pse | duckduckgo-scrape | none |
results |
array | Hits |
Hit (WebSearchHit):
| Field | Meaning |
|---|---|
title |
Result title |
url |
Link |
snippet |
Short text |
engines |
Upstream engines (SearXNG only; empty on fallbacks) |
MCP / tools
MCP tool web_search (and the agent WebSearchTool) call the same IWebSearchService. Same provider field, same degradation contract.
Listed as a hot tool in MCP packaging — an assistant that cannot look anything up is hobbled for current facts.
Example (shape)
{
"query": "Atamaia AI platform",
"provider": "searxng",
"results": [
{
"title": "Atamaia — The platform for AI identity, memory, and context",
"url": "https://atamaia.ai/",
"snippet": "Give your AI a mind of its own…",
"engines": ["google cse", "duckduckgo"]
}
]
}
Ops notes that bite
- JSON is off by default in SearXNG. Without
formats: [html, json]insettings.yml, the web UI works and the API returns 403. Browser check "is it up?" passes while Atamaia gets nothing. Seedeploy/searxng/settings.yml. - Loopback only — the platform reaches SearXNG on the same host; remote callers use Atamaia's API, not SearXNG directly.
- Placeholders are not credentials. Values containing
CHANGE,PLACEHOLDER,YOUR_, or equal toTODO/xxxskip Google PSE with a Warning. Absent is better than fake-configured. - Timeouts are 15s per provider attempt in the service.
Relationship to other search
| Need | Use |
|---|---|
| Identity/work memories | memory_search — see docs-memory-surfaces.md |
| Code / internal docs symbols | graph_search — see docs-code-graph.md |
| Project facts | fact_search |
| Public web | this — /api/system/web-search |
Do not file web snippets into work memory as Asserted without labelling provenance; prefer Reported/Inferred when recording.
Endpoint catalog
GET /api/system/web-search
Implementation: Atamaia.Services.Search.WebSearchService.
Contract: Atamaia.Core.Services.IWebSearchService.
Deploy: deploy/searxng/.
Rough edges
- DuckDuckGo fallback depends on HTML class names (
result__a,result__snippet) — markup change → empty parse + Warning. - Google PSE retained only as a historical safety net; discontinued upstream.
- No caching layer described in the service — repeated identical queries hit SearXNG again.
- Agent tool may expose extra args (e.g. focus) that the REST route does not; REST is the thin shared core.