# Web Search

> 统一搜索/抓取路由引擎 — 整合搜索发现 + 内容抓取 + 浏览器降级 + 深度调查。替代 web-search-chain / multi-engine-search / web-research。

- Skill: `yyyyyhhhhh0639/web-search` (Agent Skill, multi-file: 18 files)
- Install (CLI): `npx skillmds@latest add yyyyyhhhhh0639/web-search`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yyyyyhhhhh0639/web-search/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Research & Search
- Author: yyyyyhhhhh0639 (https://skillmd.com/u/yyyyyhhhhh0639)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/yyyyyhhhhh0639/web-search

---


# web-search — 统一搜索/抓取路由引擎

> **Phase 2 整合产物** — 合并 `web-search-chain`（抓取链路）、`multi-engine-search`（搜索策略）、`web-research`（深度调查）三份技能。

---

## 快速决策树

```
你要做什么？
  ├─ 搜搜索引擎找 URL          → web_search（web-ddgs）→ 见「搜索指南」
  │   └─ 引擎失败/中文专有词    → cn.bing.com / 编码回退
  ├─ 抓取已知 URL 的内容       → router.fetch(url)     → 见「抓取指南」
  ├─ 做深度调查               → GitHub Issue 深潜     → 见「深度调查」
  ├─ 理解官方文档              → F1/F2/F3 推导         → 见「文档分析」
  └─ 搜本地文件               → file-search skill
```

---

## 核心能力

### 自动路由引擎（SearchRouter）

`~/.hermes/scripts/search_router.py` — 自动选最优抓取/搜索路径，逐级降级。

```python
from search_router import SearchRouter
router = SearchRouter()

# 自动抓取（从最轻量工具开始，失败后自动降级）
result = router.fetch("https://example.com")
# → {"success": True, "content": "...", "tool_used": "terminal-curl"}

# 指定优先使用浏览器（强反爬站点）
result = router.fetch("https://nowsecure.nl", prefer_browser=True)
# → {"success": True, "tool_used": "flaresolverr"}

# 查看各层工具健康状态
report = router.report()
```

**降级路径**：`web-extract`（builtin）→ `terminal-curl`（L1）→ `cloudscraper`（L1~3）→ `FlareSolverr`（L1~3）→ `CloakBrowser`（L1~5）

详见：[SearchRouter 架构](references/search-router-architecture.md) | [工具能力声明](references/tool-capabilities/schema.yaml)

### 统一反爬适配器（AntiCrawlAdapter）

`~/.hermes/scripts/anticrawl_adapter.py` — 自动处理三大陷阱：

| 功能 | API | 说明 |
|:----|:----|:-----|
| 代理绕过 | `no_proxy_opener()` | localhost 请求自动跳过 `HTTP_PROXY` |
| 编码回退 | `decode_with_fallback(raw)` | UTF-8 → gbk → gb2312 → gb18030 → latin-1 |
| 指数退避 | `fetch(url, retries=2)` | timeout/ConnectionError 自动重试（1s→2s→4s） |
| 健康检查 | `check_service(name)` | flaressolverr / everything 一键检测 |

### 工具能力声明

8 个工具注册在 `references/tool-capabilities/` 目录下，SearchRouter 自动加载：

| 工具 | 类型 | 反爬覆盖 | 成本 |
|:----|:----:|:--------:|:----:|
| web-ddgs | 搜索 | L1~2 | 低 |
| bili-cli | 搜索 | L1~2 | 低 |
| web-extract | 抓取 | L1~2 | 中（key 取决于所选 provider） |
| terminal-curl | 抓取 | L1 | 极低 |
| cloudscraper | 抓取 | L1~3 | 低 |
| flaresolverr | 抓取 | L1~3 | 高 |
| cloakbrowser | 抓取 | L1~5 | 高（未安装） |
| everything-http | 本地 | — | 极低 |

---

## 搜索指南

### 搜索引擎选择

| 引擎 | 适用场景 | 反爬风险 |
|:----|:---------|:--------:|
| **ddgs**（DuckDuckGo） | 通用搜索，无需 API key | 低（无 CAPTCHA）；需装 `ddgs` 包 |
| **Bing 国际版**（www.bing.com） | 英文通用 | 中 |
| **Bing 中国版**（cn.bing.com）🔥 | **中文技术/连字符词精确匹配** | 低 |

> `web_search` 工具**不等于** DuckDuckGo：它由 provider 插件（tavily / exa / … / ddgs）提供服务，谁是当前引擎由配置与探测决定 —— 见下节。

### web_search / web_extract 的 provider 机制

随包注册 10 个 provider：`tavily`、`firecrawl`、`exa`、`parallel`、`perplexity`、`keenable`、`brave-free`、`searxng`、`ddgs`、`xai`（各自 `plugins/web/*/plugin.yaml`）。

选择优先级（`agent/web_search_registry.py`）：

1. `web.search_backend` / `web.extract_backend` / `web.backend` 显式配置 —— 命中即用；配置项失败**不会静默换 provider**，但会触发**一次性 keyless 救捞**（结果带 `rescued_from` / `backend_error` 标注，下次调用仍用配置项；`web.keyless_rescue: false` 关闭）
2. 唯一「已注册且可用」的 provider
3. 传统顺序：`firecrawl → parallel → tavily → perplexity → exa → searxng → brave-free → ddgs`
4. keyless 匿名免费档轮询：`exa / parallel / firecrawl / keenable`（关闭：`web.keyless_fallback: false`）

**查证当前实际走谁（唯一可靠方法）**：

```bash
# tools/web_tools.py 每次调用都会打 INFO 日志
grep 'Web search via' "$HERMES_HOME/logs/agent.log" | tail -5
grep 'Web extract via' "$HERMES_HOME/logs/agent.log" | tail -5
```

- **验证 pin 是否真生效（受控实验）**：改 `web.search_backend` 后直接看 provider 自己打的 LOG —— `Tavily search: …` / `web_search backend 'exa' failed (…)`，能直接分辨路由到了谁（`hermes config set` 会重写 config.yaml 并丢掉文件尾部注释块，改后记得 `diff` 备份）
- `plugins.enabled` 里列了某插件 ≠ 它在生效；provider 的 `is_available()` 才是开关（如 `ddgs` 必须先能 `import ddgs`）
- 搜索与抓取可各钉一个 provider；换源后旧 provider 仍在注册表，可随时回退

### 中文搜索技巧

- **连字符处理**：`cn.bing.com` 保留 `scope-recall` 为专有名词，`www.bing.com` 分词
- **编码回退**：Bing 中文页可能返回 GBK → 用 `decode_with_fallback()`
- **HTML 提取**：Bing 用 `b_algo` 类名；Google 用 `g` 类
- 详见 `multi-engine-search` skill（旧版，已被替换）的参考文件

### B站/内容平台搜索

| 方式 | 工具 | 优点 | 缺点 |
|:----|:-----|:----|:----|
| **bili-cli CLI** | `bili search --yaml` | 绕过 CAPTCHA，无频率限制 | 需安装（`uv tool install bilibili-cli`） |
| **浏览器搜索** | `browser_navigate` | 无需安装 | 10+ 次后触发 CAPTCHA |

策略：先 bili-cli（API 直连），失败后浏览器兜底。

- 参考：[bili-cli 搜索指南](references/bili-cli-search.md) | [浏览器 B站搜索](references/bilibili-search.md)

### GitHub 深度调查

6 步调查法：**Issue body → 所有评论 → Timeline → 关联 Issue → 官方文档 → 代码搜索**

```bash
# API 调用示例
curl -s "https://api.github.com/repos/owner/repo/issues/N"
curl -s "https://api.github.com/repos/owner/repo/issues/N/comments"
curl -s "https://api.github.com/repos/owner/repo/issues/N/timeline"
curl -s "https://api.github.com/search/issues?q=KEYWORD+repo:owner/repo"
```

注意事项：API 有 rate limit（未认证 60/h），timeline 包含 label/close/reopen 事件。

### 文档深度分析（F1/F2/F3 推导）

不逐行通读文档，而是：
1. **DOM 精确提取** — `browser_console` JS 查询定位关键内容
2. **跨页交叉引用** — 架构 + 功能 + 参考 + 最佳实践 + 排障
3. **F1/F2/F3 推导** — 从不可约减事实逐层推理

详见：[文档深度分析](references/doc-deep-dive.md)

### AI 市场研究

提取平台（build.nvidia.com / OpenRouter / HuggingFace）的定价 / 限流 / 试用信息。
详见：[AI 市场研究](references/ai-marketplace-research.md)

---

## 抓取指南

### 无反爬/API 站点 → terminal curl

```bash
curl -sL "URL" -H "User-Agent: Mozilla/5.0"
```

### Cloudflare 站点 → cloudscraper（推荐）→ FlareSolverr（兜底）

```python
import cloudscraper
scraper = cloudscraper.create_scraper(browser='chrome')
resp = scraper.get('https://target.com', timeout=15)
# 失败后自动 router.fetch() 会降级到 FlareSolverr
```

### 强反爬/行为检测 → CloakBrowser（需安装）

```bash
pip install cloakbrowser
```

详见：[反爬五层对抗模型](references/anti-crawling-layer-framework.md)

### 本地文件搜索 → file-search skill

```bash
skill_view(name="file-search")
```

---

## 浏览器降级策略

当终端网络被阻断时（curl 超时 / CAPTCHA），使用 browser 工具：

```python
browser_navigate(url="https://www.bing.com/search?q=TOPIC")
browser_snapshot(full=True)
browser_scroll(direction="down")
browser_click(ref="eXX")
```

触发条件：`curl` exit code 124/28（timeout），或返回 CAPTCHA 页面。

---

## 环境信息

### 代理配置

```bash
HTTP_PROXY=http://127.0.0.1:7890      # Clash Verge 注入
NO_PROXY=localhost,127.0.0.1,::1,...  # 已配置 ✅
```

- **MSYS2 curl**：受 `NO_PROXY` 保护，localhost 请求正常
- **Python urllib**：不读 shell `NO_PROXY`，需显式 `ProxyHandler({})`
- **统一处理**：用 `anticrawl_adapter.no_proxy_opener()`

### 工具状态表

| 工具 | 状态 | 说明 |
|:----|:----:|:-----|
| cloudscraper | 🟢 v1.2.71 | Hermes venv 预装，nowsecure.nl ✅ |
| primp | 🟢 v2.0.0 | ddgs 依赖（随 ddgs 安装），TLS 伪装 L1~2 |
| ddgs | 🟢 v9.16.0 | web-ddgs 插件；`uv pip install ddgs` 后 `is_available()=True`，但探测顺序里排最后，需 `web.search_backend: ddgs` 才会优先 |
| FlareSolverr | 🟢 v3.5.0 | :8191 运行中，.bashrc 惰性启动 |
| Everything HTTP | 🟢 :13538 | 97K+ 条索引 |
| CloakBrowser | 🟢 v0.4.10 | 已安装，L5 行为对抗 |
| web_extract | 🟢 可用 | `web.extract_backend` 为空时走自动探测（取决于 provider key）；调用日志见 provider 机制一节 |
| bili-cli | 🟢 已验证 | `uv tool install bilibili-cli`，B站 API 搜索 |

---

## 参考文件索引

| 文件 | 来源 | 内容 |
|:----|:----|:-----|
| `references/search-router-architecture.md` | web-search-chain | SearchRouter 三层解耦架构 |
| `references/tool-capabilities/schema.yaml` | web-search-chain | 工具能力声明 YAML 格式 |
| `references/tool-capabilities/*.yaml` (8个) | web-search-chain | 各工具注册声明 |
| `references/anti-crawling-layer-framework.md` | multi-engine-search | 五层对抗模型 + 工具映射 |
| `references/bili-cli-search.md` | multi-engine-search | bili-cli 完整指南 |
| `references/bilibili-search.md` | web-research | 浏览器 B站搜索 |
| `references/ai-marketplace-research.md` | web-research | AI 市场研究 |
| `references/deli-autoresearch.md` | web-research | Deli AutoResearch 参考 |
| `references/doc-deep-dive.md` | web-research | F1/F2/F3 文档推导 |
| `references/tool-capabilities-authoring.md` | 会话产出 | YAML 编写指南 + health check 格式规则 + 常见陷阱 |
| `scripts/search_router.py` | web-search-chain | SearchRouter 路由引擎 |
| `scripts/anticrawl_adapter.py` | P0.3 | 反爬适配器 |

---

## 迁移记录

| 旧 skill | 状态 | 合并日期 |
|:---------|:----:|:--------:|
| `web-search-chain` | ✅ 已合并至此 | 2026-07-16 |
| `multi-engine-search` | ✅ 已合并至此 | 2026-07-16 |
| `web-research` | ✅ 已合并至此 | 2026-07-16 |

