$npx -y skills add axoviq-ai/synthadoc --skill web_searchSearch the web and ingest results as wiki pages
| 1 | # Web Search Skill |
| 2 | |
| 3 | Accepts a natural language query, calls the Tavily AI search API, and |
| 4 | returns the top matching URLs. Your agent receives those URLs and decides |
| 5 | what to do with them — fetch each one, display them, pass them to another |
| 6 | skill, etc. |
| 7 | |
| 8 | ## Setup |
| 9 | |
| 10 | **1. Install the dependency:** |
| 11 | ```bash |
| 12 | pip install tavily-python |
| 13 | ``` |
| 14 | |
| 15 | **2. Set your Tavily API key** (free tier: 1,000 searches/month — sign up at |
| 16 | https://tavily.com, no credit card required): |
| 17 | ```bash |
| 18 | # macOS / Linux |
| 19 | export TAVILY_API_KEY="tvly-your-key-here" |
| 20 | |
| 21 | # Windows (Command Prompt) |
| 22 | set TAVILY_API_KEY=tvly-your-key-here |
| 23 | |
| 24 | # Windows (PowerShell) |
| 25 | $env:TAVILY_API_KEY = "tvly-your-key-here" |
| 26 | ``` |
| 27 | |
| 28 | **3. Optional — cap the number of results** (default: 20): |
| 29 | ```bash |
| 30 | export SYNTHADOC_WEB_SEARCH_MAX_RESULTS=10 |
| 31 | ``` |
| 32 | |
| 33 | ## Standalone usage |
| 34 | |
| 35 | ```python |
| 36 | import asyncio |
| 37 | from synthadoc.skills.web_search.scripts.main import WebSearchSkill |
| 38 | |
| 39 | skill = WebSearchSkill() |
| 40 | |
| 41 | async def main(): |
| 42 | result = await skill.extract("search for: transformer architecture papers") |
| 43 | urls = result.metadata["child_sources"] # list[str] — top matching URLs |
| 44 | query = result.metadata["query"] # "transformer architecture papers" |
| 45 | print(f"Found {len(urls)} URLs for '{query}':") |
| 46 | for url in urls: |
| 47 | print(" ", url) |
| 48 | |
| 49 | asyncio.run(main()) |
| 50 | ``` |
| 51 | |
| 52 | `result.text` is always empty — the skill is a discovery step that returns |
| 53 | URLs, not page content. Pass the URLs to the `url` or `youtube` skill (or |
| 54 | your own HTTP client) to fetch content. |
| 55 | |
| 56 | ## Intent prefixes |
| 57 | |
| 58 | The skill strips a leading intent phrase before sending the query to Tavily: |
| 59 | |
| 60 | | Input | Query sent to Tavily | |
| 61 | |---|---| |
| 62 | | `search for: RAG evaluation` | `RAG evaluation` | |
| 63 | | `find on the web: LLM benchmarks` | `LLM benchmarks` | |
| 64 | | `look up quantum computing` | `quantum computing` | |
| 65 | | `youtube: Karpathy transformers` | `Karpathy transformers` (YouTube only) | |
| 66 | | `搜索: 深度学习架构` | `深度学习架构` | |
| 67 | |
| 68 | YouTube-specific prefixes (`youtube:`, `search youtube:`, `youtube video:`, |
| 69 | etc.) restrict the Tavily search to `youtube.com` and `youtu.be`. |
| 70 | |
| 71 | CJK intent phrases supported: 查找, 搜索, 网络搜索, 在网上查, 查一下 |
| 72 | |
| 73 | ## Domain filtering |
| 74 | |
| 75 | A built-in blocklist skips sites that block automated HTTP clients: |
| 76 | `reddit.com`, `medium.com`, `quora.com`, `twitter.com`/`x.com`, |
| 77 | `linkedin.com`, `wikipedia.org`, IEEE Xplore, ACM DL, and common |
| 78 | subscription-only academic publishers. |
| 79 | |
| 80 | If `SYNTHADOC_WIKI_ROOT` is set, the skill also loads |
| 81 | `$SYNTHADOC_WIKI_ROOT/.synthadoc/blocked_domains.json` (a JSON array of |
| 82 | domain strings) to extend the blocklist at runtime. |
| 83 | |
| 84 | ## Scripts |
| 85 | |
| 86 | - `scripts/main.py` — `WebSearchSkill`: intent parsing, domain filtering, |
| 87 | returns `child_sources` in metadata |
| 88 | - `scripts/fetcher.py` — thin async wrapper around `AsyncTavilyClient` |
| 89 | |
| 90 | ## Assets |
| 91 | |
| 92 | - `assets/search-providers.json` — search provider registry (currently Tavily) |
| 93 | |
| 94 | ## Using with full Synthadoc |
| 95 | |
| 96 | When running inside Synthadoc, the Orchestrator reads `child_sources` from |
| 97 | the result metadata and automatically enqueues each URL as a separate ingest |
| 98 | job, which are then processed by the `url` or `youtube` skill. No additional |
| 99 | setup is required beyond the env vars above. |