feat: add Exa AI-powered search skill#74
Conversation
Adds a self-contained search skill at memory/exa_search.py + a matching memory/exa_search_sop.md. The skill wraps exa-py with sensible defaults and a typed ExaResult, letting agents do semantic web search without pre-baking a new atomic tool. - exa_search.search(): search + content retrieval with category / domain / text / date filters; search types: auto, neural, fast, instant, deep, deep-lite, deep-reasoning (no deprecated 'keyword') - find_similar() and get_contents() helpers - ExaResult with snippet that cascades highlights -> summary -> text - API key loads from EXA_API_KEY env var, falling back to keychain - Sets x-exa-integration header for usage attribution - 22 unit tests covering snippet fallback, kwargs wiring, client construction, and error paths (mock exa_py; no live API calls)
|
Repeated use of a specific search API (such as EXA) can continuously influence an agent’s decision-making through that service’s data coverage and ranking logic, leading to the accumulation of bias over time. Therefore, rather than relying on a single service, it is more desirable to leverage multiple sources or design a structure that allows direct control over potential biases. |
|
@LEE-Kyungjae thanks for raising this. Worth noting the skill is additive and opt-in - |
Summary
Adds a self-contained skill (not a new atomic tool) for semantic web search via Exa. The skill lives entirely under
memory/alongside existing skills likekeychain.py,skill_search/, andprocmem_scanner.py, so it respects the project's "don't preload skills — evolve them" philosophy: the 9 atomic tools +code_runstay untouched, and the agent opts in viafrom exa_search import searchonly when a task calls for it.Why Exa?
web_scan/web_execute_jsare great for browser-driven scraping, but for "research this topic" / "find similar pages" / "filter by category or date", a semantic API returns structured, deduplicated results in one call instead of multi-turn Google scraping.What's in the skill
memory/exa_search.py— wrapsexa-pywith a typedExaResultdataclass and three functions:search(query, ...)— search + content retrievalfind_similar(url, ...)— semantic neighborsget_contents(urls, ...)— fetch page text for known URLsauto(default),neural,fast,instant,deep,deep-lite,deep-reasoning(no deprecatedkeyword)category,include_domains,exclude_domains,include_text,exclude_text, date rangestext+highlights+summarycan all be passed together)snippetcascades throughhighlights → summary → textso callers don't have to null-check three fieldsEXA_API_KEYenv var, falling back tokeychain.keys.exa_api_keyx-exa-integration: generic-agentheader for usage attributionmemory/exa_search_sop.md— SOP doc matching the style of other skill SOPs: triggers/禁用, 最简调用, API 签名, 典型场景, 避坑tests/test_exa_search.py— 22 unit tests covering snippet fallback, kwargs wiring, client singleton, integration header, and error paths (all mocked; no live API calls).gitignore— whitelist entries for the two newmemory/files (following the project's pattern)Usage
Files changed
memory/exa_search.py(new, 170 lines) — skill implementationmemory/exa_search_sop.md(new) — SOP doctests/test_exa_search.py(new, 22 tests) — unit tests.gitignore— whitelist the two new memory filesTest plan
python -m pytest tests/test_exa_search.py -v→ 22 passed_convert: full result + defaults when fields are missingsearch()kwargs: defaults, all filters forwarded, content types combine, disabled highlights, empty results, missing.resultsattrfind_similar()andget_contents()wiringupstream/mainindependently)EXA_API_KEY; not run in CI, but reproducible viapython memory/exa_search.py "agent frameworks 2025" 5Notes
exa-py>=2.0.0is imported lazily inside_get_client(), so the skill is free to stay out of the core install flow — users who don't need Exa never see the dependency. Install on demand:pip install exa-py. Happy to add it to a central requirements list if one is introduced later.ga.py,agent_loop.py,llmcore.py, or the 9-tool atomic set.