# Agent 网络工具通用设计（增强版）

> 本文档描述 Agent 网络工具链的整体设计与优化方案，涵盖 SearXNG 元搜索配置、web_search 搜索、web_fetch 抓取、browser 浏览器控制四个核心模块。文档聚焦架构、配置与接口，不绑定具体编程语言与框架实现，并给出各环节可引入的成熟第三方库建议。

## 目录

1. [SearXNG 配置设计](#1-searxng-配置设计)
2. [web_search 搜索设计](#2-web_search-搜索设计)
3. [web_fetch 抓取设计](#3-web_fetch-抓取设计)
4. [browser 浏览器设计](#4-browser-浏览器设计)
5. [第三方库选型指南](#5-第三方库选型指南)
6. [优化增强路线图](#6-优化增强路线图)

---

## 1. SearXNG 配置设计

### 1.1 架构概览

SearXNG 是开源元搜索引擎，支持 70+ 搜索引擎聚合，不追踪用户、不记录隐私。本设计将 SearXNG 作为**可选替代方案**：启用后通过 SearXNG 的 JSON 或 HTML API 进行搜索；关闭时回退到内置四引擎 HTML 解析方案（Bing + 百度 + 搜狗 + 360 搜索）。

整体分为三层。**UI 层**负责配置模态框的交互与表单同步；**数据层**负责通过本地数据库读写配置项，键值对形式持久化；**执行层**位于主进程，在搜索请求到来时读取配置，决定走 SearXNG 通道还是内置引擎通道，并完成参数拼装、认证注入、请求发送与结果解析。

```
┌─────────────────────────────────────────────────────┐
│                    UI 层（Renderer）                   │
│  配置模态框 / 表单控件 / 状态指示                      │
├─────────────────────────────────────────────────────┤
│              数据层（SQLite / LocalStorage）           │
│  键值对持久化 / 运行时缓存 / 配置校验                  │
├─────────────────────────────────────────────────────┤
│              执行层（Main Process）                    │
│  参数拼装 → 认证注入 → API 调用 → 结果解析             │
│  双模式切换：SearXNG / 内置四引擎                     │
└─────────────────────────────────────────────────────┘
```

### 1.2 配置项定义

SearXNG 相关配置共 12 项，各配置项的默认值与含义如下：

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `enabled` | boolean | `false` | 是否启用 SearXNG |
| `url` | string | `''` | SearXNG 实例根地址（不含 `/search` 路径） |
| `engines` | string | `''` | 引擎列表，逗号分隔，留空则使用实例默认引擎 |
| `language` | string | `'zh-CN'` | 搜索语言 |
| `safesearch` | number | `1` | 安全搜索级别（0=关闭，1=中等，2=严格） |
| `time_range` | string | `''` | 时间范围过滤（day/week/month/year） |
| `max_results` | number | `0` | 单次最大结果数，0 表示使用默认 |
| `auth_key` | string | `''` | 认证密钥，内容随认证类型不同而不同 |
| `auth_type` | string | `'bearer'` | 认证类型（bearer 或 basic） |
| `format` | string | `'json'` | 返回格式（json 或 html） |
| `fetch_count` | number | `0` | 自动抓取条数，0 表示由 AI 决定 |
| `fetch_mode` | string | `'sequential'` | 抓取类型（sequential 顺序或 random 随机） |

### 1.3 持久化存储

配置通过本地数据库以键值对形式持久化。写入时逐项调用 `saveSetting(key, value)`，读取时通过 `getSetting(key, default)` 回填到运行时缓存。对应的存储键名汇总如下：

| 存储键名 | 对应配置项 | 默认值 |
|----------|-----------|--------|
| `searxng_enabled` | enabled | `false` |
| `searxng_url` | url | `''` |
| `searxng_engines` | engines | `''` |
| `searxng_language` | language | `'zh-CN'` |
| `searxng_safesearch` | safesearch | `1` |
| `searxng_time_range` | time_range | `''` |
| `searxng_max_results` | max_results | `0` |
| `searxng_auth_key` | auth_key | `''` |
| `searxng_auth_type` | auth_type | `'bearer'` |
| `searxng_format` | format | `'json'` |
| `fetch_count` | fetch_count | `0` |
| `fetch_mode` | fetch_mode | `'sequential'` |

### 1.4 UI 模态框

配置界面以模态框形式呈现，包含以下交互控件。启用开关为实时保存，切换即写入数据库；其余字段在点击保存按钮时统一校验并落库。URL 字段需通过非空校验与 `http/https` 格式校验。

- **启用开关**：实时切换 SearXNG 启用状态，立即持久化
- **API 地址**：实例根地址，必填，需为 `http/https` 格式
- **搜索引擎**：逗号分隔的引擎名称，留空使用实例默认
- **语言选择**：下拉项含 zh-CN / zh-TW / en / ja / ko / 自动检测
- **安全搜索**：下拉项含 0 关闭 / 1 中等 / 2 严格
- **时间范围**：下拉项含 不限 / 一天 / 一周 / 一月 / 一年
- **结果数量**：数字输入，0 表示使用默认
- **返回格式**：下拉项含 JSON 结构化解析 / HTML 原始网页
- **自动抓取条数**：数字输入，范围 1–8
- **抓取类型**：下拉项含 顺序抓取 / 随机抓取
- **认证设置**：认证类型（bearer / basic）+ 认证密钥（密码型输入框）
- **保存按钮**：校验后批量持久化全部字段
- **连接测试按钮**（增强）：一键验证 URL 可达性与认证有效性

工具栏入口按钮上设状态指示圆点：启用时为绿色，未启用时为灰色；模态框内同时显示文字状态徽章（已启用 / 未启用）。

### 1.5 SearXNG 认证方式详解

SearXNG 实例可配置鉴权，本设计支持两种认证类型，由 `auth_type` 配置项决定，认证密钥统一存放在 `auth_key` 中。两种方式的核心区别在于请求头的构造方式与密钥的填写格式。

#### Bearer Token 认证（`auth_type = 'bearer'`，默认值）

当认证类型为 bearer 时，系统在 HTTP 请求头中添加：

```
Authorization: Bearer <auth_key>
```

密钥原样放入 token 位置，不做任何编码转换。`auth_key` 应直接填写 SearXNG 实例签发的访问令牌本身。

**适用场景**：
- SearXNG 实例通过反向代理（如 Nginx/Caddy）签发的固定令牌鉴权
- JWT（JSON Web Token）格式的令牌
- OAuth2 Bearer Token
- 任何基于 Token 的简单鉴权方案

**安全要求**：建议配合 HTTPS 使用，防止令牌在网络传输中被截获。

#### Basic 认证（`auth_type = 'basic'`）

当认证类型为 basic 时，系统将 `auth_key` 整体进行 Base64 编码，再在请求头中添加：

```
Authorization: Basic <Base64(auth_key)>
```

`auth_key` 应填写 `用户名:密码` 格式的明文字符串，由系统负责编码。

**适用场景**：
- SearXNG 实例配置了 HTTP Basic Auth 的场景
- Nginx/Caddy 反向代理的 `auth_basic` 指令保护
- 任何标准用户名密码鉴权网关

**安全要求**：必须配合 HTTPS 使用。Basic Auth 以 Base64 编码传输凭据（非加密），明文网络可还原。

#### 两种方式对比

| 维度 | Bearer | Basic |
|------|--------|-------|
| 请求头格式 | `Authorization: Bearer <token>` | `Authorization: Basic <Base64>` |
| 密钥填写内容 | 令牌原值 | `用户名:密码` 明文串 |
| 是否编码 | 否，原样透传 | 是，整体 Base64 编码 |
| 适用场景 | 令牌/JWT/OAuth2 鉴权 | 用户名密码鉴权 |
| 安全要求 | 建议 HTTPS | 必须 HTTPS |
| 凭据可复用性 | 高（无状态令牌） | 低（每次携带凭据） |
| 撤销方式 | 令牌失效/黑名单 | 修改密码 |

无论哪种方式，仅当 `auth_key` 非空时才会注入 `Authorization` 头；`auth_key` 为空则视为匿名访问，不附加任何认证信息。认证逻辑对 JSON 与 HTML 两种返回格式均生效。

### 1.6 主进程搜索调用流程

搜索请求到达主进程后，执行层按以下步骤处理：

1. **读取配置**：从数据库加载全部 SearXNG 相关配置项
2. **构建参数**：
   - 查询词、引擎列表、语言、安全搜索级别拼入查询串
   - 时间范围优先使用调用方传入值，缺省时回退到配置项默认值
   - 最大结果数取配置项与入参的较大有效值
3. **认证注入**：根据 `auth_type` 构造对应的 `Authorization` 请求头
4. **发送请求**：向 `<url>/search?<参数>` 发起 HTTP GET 请求
5. **解析结果**：
   - JSON 模式：结构化解析每条结果的标题、URL、摘要、来源引擎
   - HTML 模式：转为纯文本交由 AI 自行分析
6. **统计引擎状态**：记录各引擎命中条数与异常原因

### 1.7 双模式切换逻辑

在统一的搜索入口处，通过读取 `searxng_enabled` 判断走哪条通道：

- **SearXNG 启用时**：
  - 自动抓取条数从面板配置读取，取配置项与默认下限的较大值，限制在最大抓取上限内
  - 抓取类型为顺序或随机
  - 日志标记来源为 `[SearXNG]`
- **未启用时**：
  - 走内置四引擎并行搜索通道
  - 日志标记来源为 `[内置]`

---

## 2. web_search 搜索设计

### 2.1 架构概览

web_search 是搜索工具的总入口，内部根据 SearXNG 开关分流到两条通道。SearXNG 通道走元搜索 API；内置通道则并行调度四个搜索引擎，经合并去重、可达性预检、智能排序、摘要增强与缓存后输出结果。无论哪条通道，最终都会进入自动抓取阶段，对前 N 条结果抓取完整内容。

```
web_search 入口
    ├── searxng_enabled?
    │   ├── 是 → SearXNG API 搜索
    │   └── 否 → 内置四引擎并行搜索
    │              ├── Bing (权重90)
    │              ├── 百度 (权重80)
    │              ├── 搜狗 (权重75)
    │              └── 360搜索 (权重75)
    │         → Promise.allSettled 并行容错
    │         → 合并去重（URL 标准化）
    │         → 可达性预检（并发5）
    │         → 智能排序（可达性+权重+摘要质量）
    │         → 摘要自动增强（web_fetch top3）
    │         → LRU 缓存（5分钟TTL）
    └── 自动抓取前N条完整内容
        → 相关性过滤 → 逐条抓取 → 失败随机补充
```

### 2.2 函数参数

web_search 接受以下参数：

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `query` | string | 必填 | 搜索关键词 |
| `max_results` | number | 30 | 最大结果数，上限 30 |
| `time_range` | string | — | 时间过滤（day/week/month/year 等，支持中英文别名） |
| `enhance_snippets` | boolean | true | 是否自动增强过短摘要 |
| `fetch_top` | number | 5 | 自动抓取前 N 条完整内容，范围 3–8 |

### 2.3 内置四引擎并行搜索

内置通道调度四个搜索引擎，每个引擎带有权重，用于后续排序。四个引擎并行执行，采用容错并发模型，任一引擎失败不影响其余引擎。

| 引擎 | 权重 | 请求地址 | 特殊处理 |
|------|------|---------|---------|
| Bing | 90 | `bing.com/search` | 支持 freshness 参数映射时间范围 |
| 百度 | 80 | `baidu.com/s` | 从 `data-url` 属性提取真实 URL |
| 搜狗 | 75 | `sogou.com/web` | 过滤 sogou.com 自身链接 |
| 360 搜索 | 75 | `so.com/s` | 过滤 so.com 自身链接 |

时间范围参数映射规则：

| 用户输入 | Bing 参数 | 说明 |
|----------|----------|------|
| `day` / `1d` / `一天` | `Day` | 最近一天 |
| `week` / `1w` / `一周` | `Week` | 最近一周 |
| `month` / `1m` / `一月` | `Month` | 最近一月 |

### 2.4 搜索引擎 HTML 解析

每个引擎返回的是 HTML 页面，需要专门的解析器从中提取标题、URL、摘要三要素。四个解析器的提取策略各有差异，但整体思路一致：先以块级结构切分结果条目，再从条目内提取标题链接与摘要文本，最后过滤掉引擎自身的导航类链接。

- **Bing 解析**：按 `<li class="b_algo">` 块切分，从锚点提取标题与 URL，从 `<p>` 或 `.b_caption` 提取描述
- **百度解析**：按 `<div class="result">` 块切分，标题取自 `<h3 class="t">` 锚点，真实 URL 取自 `data-url` 属性，摘要取自 `.c-abstract` 或 `.content-right_*`
- **搜狗解析**：按 `.vrwrap` 或 `.rb` 块切分，摘要取自多个候选区域（`.star-wiki` / `.space-txt` / `.str_info`），过滤 sogou.com 自身链接
- **360 搜索解析**：按 `.res-list` 或 `.result` 块切分，摘要取自 `.res-desc` / `.res-rich` / `.res-summary` / `<dd>`，过滤 so.com 自身链接

> **[增强建议]** 各引擎解析器可迁移至基于 CSS 选择器的 DOM 解析方案（如 Cheerio），替代当前的正则匹配，提升鲁棒性与维护性。详见第 5 章「第三方库选型指南」。

### 2.5 智能排序算法

合并去重后，对结果计算综合评分并排序。评分由三部分加权构成：

$$
\text{score} = \underbrace{\frac{\text{weight}}{100} \times 50}_{\text{引擎权重 50\%}} + \underbrace{\text{可达性加分}}_{\text{可达 +30 / 不可达 -20}} + \underbrace{\frac{\min(\text{snippetLen}, 100)}{100} \times 20}_{\text{摘要质量 20%（上限100字符）}}
$$

最终按评分降序排列。

### 2.6 URL 可达性预检

为剔除死链，排序前对候选结果做可达性预检。采用并发批次控制策略：

- **并发数**：每批 5 个 URL
- **超时**：每个请求 3 秒
- **判断标准**：仅判断响应是否成功（HTTP 2xx），不读取正文
- **结果回填**：写入结果的 `reachable` 字段（`true` / `false`）

### 2.7 摘要自动增强

当 `enhance_snippets` 开启时，对排序前列中摘要过短（< 30 字符）且可达的结果，调用 web_fetch 补充抓取其正文，取前 200 字符替换原摘要。该增强最多对前 3 条执行，避免过度请求。增强后的结果打上 `_enhanced: true` 标记。

### 2.8 自动抓取完整内容

搜索完成后，对前 N 条结果自动抓取完整正文，供 AI 综合分析。完整流程：

1. **相关性过滤**：从查询词中提取中文双字/三字片段与英文单词，在标题与摘要中匹配计分，得分上限 100，得分为 0 的结果直接过滤
2. **构建抓取列表**：
   - sequential 模式：按相关性降序取前 N 条
   - random 模式：Fisher-Yates 洗牌后取 N 条
3. **逐条抓取**：调用 web_fetch，失败时从剩余未抓取结果中随机选 1 条补充重试
4. **结果存入**：`_fetched` 数组 + `_fetched_count` 计数

### 2.9 LRU 搜索缓存

为降低重复查询的开销，搜索结果带 LRU 缓存。

| 参数 | 值 |
|------|-----|
| 最大容量 | 200 条 |
| 过期时间 | 5 分钟（300,000ms） |
| 缓存键 | 查询词（归一化后） |
| 淘汰策略 | 最早写入淘汰 |
| 命中时行为 | 校验时效，过期删除并返回未命中 |

> **[增强建议]** 可替换为成熟的 `lru-cache` 库，支持 TTL、大小限制、统计指标等开箱能力。详见第 5 章。

### 2.10 响应格式

web_search 返回结构化结果，主要字段如下：

```json
{
  "success": true,
  "query": "搜索关键词",
  "results": [
    {
      "title": "标题",
      "url": "https://...",
      "snippet": "摘要文本",
      "engine": "bing",
      "weight": 90,
      "reachable": true,
      "_score": 68.5,
      "_enhanced": false
    }
  ],
  "total": 15,
  "formatted": "人类可读文本汇总",
  "structured": [...],
  "from_cache": false,
  "engine_stats": { "bing": "8 条", "百度": "5 条" },
  "_mode": "builtin | searxng",
  "_fetched": [{ "url", "title", "content" }],
  "_fetched_count": 5
}
```

---

## 3. web_fetch 抓取设计

### 3.1 架构概览

web_fetch 采用**三阶段回退策略**，确保最大概率获取网页内容：

```
Phase 1: HTTP 抓取
├── UA 轮换池（5种）+ Accept-Language 轮换池（3种）
├── 指数退避重试（2s→4s→6s + jitter ±60%）
├── 反爬请求头（Sec-Fetch-* / DNT / Referer 等）
├── 流式读取（10MB 上限）
└── 拦截检测（10 种特征模式）
    ↓ 失败/被拦截/内容过短
Phase 2: SPA 自动升级
└── HTML 内容 < 200 字符 → 升级浏览器渲染
    ↓ 失败
Phase 3: 浏览器回退
├── 打开隐藏窗口 → 等待 2.5s JS 渲染
├── 提取页面正文 → 关闭窗口
└── LRU 缓存（100 条, 10 分钟 TTL）
```

### 3.2 函数参数

web_fetch 接受以下参数：

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `url` | string | 必填 | 目标 URL，仅支持 http/https |
| `max_chars` | number | — | 最大字符数，0 表示不截断 |
| `extract_mode` | string | — | 提取模式 |
| `mobile_ua` | boolean | — | 是否使用移动端 UA |
| `retry` | boolean | true | 是否启用重试 |

### 3.3 反爬请求头

为规避基础反爬，HTTP 抓取阶段采用请求头轮换策略。

**UA 轮换池（5 种）**：覆盖桌面 Chrome、Edge、Safari、Firefox 与移动端 Chrome，模拟主流浏览器指纹。

**Accept-Language 轮换池（3 种）**：中英混合、中文优先、英文优先三种语言偏好。

每次重试按尝试序号取模轮换 UA 与语言，同时附带完整的反爬请求头集合：

| 请求头 | 值 | 用途 |
|--------|-----|------|
| `User-Agent` | 轮换池取模 | 浏览器指纹伪装 |
| `Accept` | `text/html,application/xhtml+xml,...` | 声明接受内容类型 |
| `Accept-Language` | 轮换池取模 | 语言偏好伪装 |
| `Accept-Encoding` | `gzip, deflate, br` | 声明压缩格式 |
| `Cache-Control` | `no-cache` | 禁止缓存 |
| `DNT` | `1` | Do Not Track |
| `Referer` | 目标站点根地址 | 来源页伪装 |
| `Sec-Fetch-Dest` | `document` | 导航目标声明 |
| `Sec-Fetch-Mode` | `navigate` | 请求模式声明 |
| `Sec-Fetch-Site` | `none` | 来源站点声明 |
| `Sec-Fetch-User` | `?1` | 用户触发声明 |
| `Pragma` | `no-cache` | HTTP/1.0 兼容 |

> **[增强建议]** 可引入 `got-scraping` 库，其内置了更完善的浏览器指纹模拟、Cookie Jar 管理、HTTP/2 支持与代理集成能力，详见第 5 章。

### 3.4 重试配置

| 参数 | 值 |
|------|-----|
| 最大重试次数 | 3 次（Phase 1 内部） |
| 基础退避间隔 | 2s → 4s → 6s |
| Jitter 范围 | 基础延迟 × [0%, +60%] 随机 |
| 直接跳过重试的状态码 | 403 / 503 / 429 / 502 |
| 可重试状态码 | 5xx（除上述外） |

### 3.5 HTML 转文本引擎

抓取到的 HTML 需转为纯文本。处理流水线：

1. **移除噪声标签及内容**：script、style、noscript、nav、header、footer、aside、iframe、svg
2. **移除 HTML 注释**
3. **块级标签转行**：p、div、h1-h6、li、tr、blockquote、section、article、pre、br、hr → `\n`
4. **表格单元制表符**：td、th → `\t`
5. **移除剩余标签**
6. **解码 HTML 实体**：30+ 命名实体（`&nbsp;`、`&lt;`、`&amp;`、`&hellip;`、`&mdash;` 等）+ 全部数字实体（`&#123;`、`&#x1F;`）
7. **清理空白**：合并连续空白、清理首尾空行

> **[增强建议]** 可引入 `@mozilla/readability` 作为前置步骤，先提取文章主体内容再做文本转换，大幅减少导航/广告/侧边栏等噪声；或使用 `turndown` 将 HTML 转 Markdown 保留结构。详见第 5 章。

### 3.6 拦截页面检测

维护一组拦截特征模式，命中任一特征即判定为拦截页，触发浏览器回退：

| 特征模式 | 对应防护 |
|----------|---------|
| `Just a moment...` / Cloudflare 标题 | Cloudflare JS Challenge |
| `Attention Required!` | Cloudflare 托管挑战 |
| `challenge-platform` | Cloudflare 平台标识 |
| `.cf-challenge-` 类名 | Cloudflare Challenge 页面 |
| `Access Denied` 标题 | WAF 拒绝 |
| `403 Forbidden` 标题 | HTTP 403 拦截 |
| 请启用 JavaScript | JS 强制检测 |
| Please enable JavaScript | JS 强制检测（英文） |
| Checking your browser | 浏览器环境检测 |
| DDoS protection | DDoS 防护页 |
| 正文长度 < 80 字符 | 空白/空壳页面 |

### 3.7 内容过短自动升级

对于成功返回但正文过短（< 200 字符）的页面——通常是 SPA 单页应用或空壳页面，其有效内容依赖 JS 渲染——自动升级到浏览器渲染通道。若渲染成功则用渲染结果替换，方法标记为 `browser`。

### 3.8 浏览器回退实现

浏览器回退通道的完整流程：

1. **查缓存**：LRU 缓存（100 条，10 分钟 TTL），命中则截断返回
2. **打开窗口**：隐藏 BrowserWindow 加载目标 URL
3. **等待渲染**：2.5秒让 SPA/动态内容完成 JS 渲染
4. **提取正文**：移除非内容标签后取 innerText
5. **关闭窗口**：确保资源释放
6. **写缓存**：正文 ≥ 100 字符时写入缓存

异常保障：任何步骤出错均确保调用关闭函数，避免窗口泄漏。

### 3.9 触发浏览器回退的条件

| 条件 | 说明 | Phase 1 → Phase 3 |
|------|------|-------------------|
| HTTP 403/503/429/502 | 反爬/限流/拦截信号 | ✅ 直接跳转 |
| 拦截特征匹配 | Cloudflare/验证码/JS 要求等 | ✅ 直接跳转 |
| 内容 < 200 字符 | SPA 或空壳页面 | ✅ 经 Phase 2 升级 |
| 请求超时 | 超过 HTTP 超时阈值 | ✅ 直接跳转 |
| 所有重试均失败 | 网络不可达等 | ✅ 直接跳转 |

### 3.10 流式读取与大文件保护

| 参数 | 值 |
|------|-----|
| 硬上限 | 10 MB |
| 无 Content-Length 时 | 流式读取，累计字节数 |
| 超限处理 | 中止下载，返回错误 |
| 有 Content-Length 时 | 预判是否超限 |

### 3.11 超时封装

HTTP 抓取通过带超时的封装发起请求：创建 AbortController，设定超时定时器，超时即中止请求。该封装同时被搜索阶段的可达性预检复用。

---

## 4. browser 浏览器设计

### 4.1 架构概览

browser 模块基于隐藏浏览器窗口实现，提供完整的网页加载、截图、JS 执行、内容提取与交互操作能力。整个模块封装为**单例窗口**，包含 9 个核心函数。

| 函数 | 职责 | 关键参数 |
|------|------|---------|
| `browserOpen` | 打开 URL | url, waitSelector |
| `browserScreenshot` | 截图 | fullPage, selector（视口/元素/全页三模式） |
| `browserEvaluate` | 执行 JS | js（任意代码） |
| `browserExtract` | 提取内容 | selector, maxChars |
| `browserClick` | 点击元素 | selector, wait |
| `browserType` | 输入文本 | selector, text, clear, submit |
| `browserScroll` | 滚动页面 | direction, selector |
| `browserWait` | 等待条件 | selector, timeMs |
| `browserClose` | 关闭窗口 | — |

**窗口默认参数**：1280×800、不可见（show:false）、上下文隔离、沙箱模式、页面加载超时 30s。

### 4.2 核心状态管理

| 维度 | 设计决策 |
|------|---------|
| 实例模式 | 单例懒加载，首次调用时创建 |
| 销毁方式 | browserClose() 显式销毁 |
| 应用退出 | 统一调用 browserClose() 清理 |
| 就绪检查 | 操作前校验窗口存在且已加载页面 |
| 安全配置 | 禁用 Node.js 集成、上下文隔离、沙箱、允许跨域（截图需要） |

### 4.3 打开 URL

- 若已有窗口加载了不同 URL → 先关闭重建，避免历史状态干扰
- 加载目标 URL，30 秒超时
- 可选等待指定 CSS 选择器出现（轮询 300ms，最长 10s）
- 返回页面标题与实际 URL

### 4.4 截图

三种模式：

| 模式 | 触发条件 | 输出 |
|------|---------|------|
| 元素截图 | 指定 selector | 该元素矩形区域 PNG（Base64） |
| 全页截图 | fullPage=true | 完整滚动尺寸 PNG（Base64） |
| 视口截图 | 默认 | 当前可见区域 PNG（Base64） |

### 4.5 执行 JavaScript

接收任意 JS 在页面上下文中执行。字符串原样返回，其余类型序列化为格式化 JSON。独立超时控制。

### 4.6 提取页面内容

- 克隆目标根节点，移除 script/style/iframe/svg 后取 innerText
- 支持 CSS 选择器限定提取区域
- 默认上限 15000 字符
- 同时提取最多 50 个有效链接（http 开头 + 有可见文本）

### 4.7 点击元素

可选等待元素出现 → 滚动到视口中央 → 触发点击 → 等待 500ms。元素不存在则报错。

### 4.8 输入文本

聚焦 → clear 决定清空/追加 → 赋值 → 触发 input + change 事件（兼容 React/Vue）→ submit 决定是否提交（form.submit 或 Enter 键）。输入后等 300ms（提交等 1000ms）。

### 4.9 滚动页面

- 指定 selector → scrollIntoView 到中央
- 方向滚动：down(+500px) / up(-500px) / top(回顶) / bottom(到底)

### 4.10 等待条件

- 选择器等待：轮询 300ms，默认 10s（可由 timeMs 覆盖）
- 定时等待：按 timeMs 固定延迟，默认 1s

### 4.11 关闭浏览器

销毁单例窗口并置空引用。幂等操作——窗口已销毁时为空操作。

### 4.12 工具调度映射

所有 browser 函数通过统一工具执行通道调度：

| 调用名 | 映射函数 | 入参 |
|--------|---------|------|
| `browser_open` | browserOpen | url, wait_selector |
| `browser_screenshot` | browserScreenshot | full_page, selector |
| `browser_evaluate` | browserEvaluate | js |
| `browser_extract` | browserExtract | selector, max_chars |
| `browser_click` | browserClick | selector, wait |
| `browser_type` | browserType | selector, text, clear, submit |
| `browser_scroll` | browserScroll | direction, selector |
| `browser_wait` | browserWait | selector, time_ms |
| `browser_close` | browserClose | — |

### 4.13 浏览器与 web_fetch 的协作关系

```
web_fetch 三阶段回退:
  Phase 1 (HTTP) 失败
    ├─ 403/503/429/502? → browserFallback(url)
    ├─ Cloudflare 拦截?   → browserFallback(url)
    ├─ 内容 < 200 字符?   → browserFallback(url) [经 Phase 2]
    ├─ 超时?              → browserFallback(url)
    └─ 所有重试失败?       → browserFallback(url)

browserFallback:
  browserOpen(url) → 等待 2.5s → browserExtract() → browserClose()
  结果进入 LRU 缓存 (100条, 10分钟TTL)
```

### 4.14 超时配置汇总

| 工具 | 默认超时 | 最大超时 | 说明 |
|------|---------|---------|------|
| `web_search` | 3,000ms/引擎 | 300,000ms | 每引擎独立超时 |
| `web_fetch` | 20,000ms | 600,000ms | HTTP 抓取超时 |
| `browser_extract` | 10,000ms | 300,000ms | 页面提取超时 |
| `browser_evaluate` | 8,000ms | — | JS 执行超时 |
| `browser_screenshot` | — | 60,000ms | 截图超时 |
| `browser_open` | 30,000ms | — | 页面加载超时 |

---

## 5. 第三方库选型指南

本章针对工具链各环节，给出经过筛选的成熟第三方库推荐，涵盖 HTTP 客户端、HTML 解析、文章提取、浏览器反检测、缓存、URL 处理等领域。所有推荐库均为 npm 生态中活跃维护、广泛使用的项目。

### 5.1 HTTP 客户端层

当前实现使用原生 `fetch` + 手动请求头构造。以下库可在对应层面提供增强：

#### got-scraping（⭐ 重点推荐）

| 维度 | 详情 |
|------|------|
| 包名 | `got-scraping` |
| 定位 | 专为网页抓取设计的 HTTP 客户端，基于 `got` 封装 |
| 核心优势 | 内置浏览器指纹轮换、Cookie Jar 自动管理、HTTP/2 支持、代理集成、流式处理 |
| 指纹模拟 | 自动轮换 Header Order、TLS 指纹、HTTP/2 SETTINGS 帧，比手动构造请求头更难被识别 |
| 代理支持 | 原生支持 HTTP/SOCKS5 代理，可配置代理轮换 |
| Cookie 管理 | 自动 Cookie Jar，跨请求保持会话状态 |
| 重试机制 | 内置指数退避重试，可自定义重试条件 |
| 适用环节 | 替代 web_fetch Phase 1 的原生 fetch，显著降低被反爬识别的概率 |
| 替代方案 | `axios`（通用 HTTP 客户端）、`undici`（Node.js 原生 HTTP/1.1 客户端） |

**为什么选 got-scraping 而非 axios/got**：

`axios` 是通用 HTTP 客户端，不具备抓取专用能力；`got` 是底层 HTTP 库，功能强大但需自行组装抓取特性；`got-scraping` 在 `got` 之上专门为抓取场景做了封装，开箱即用的指纹模拟、Cookie 管理与代理支持正好覆盖 web_fetch Phase 1 的全部需求。

#### axios（备选）

| 维度 | 详情 |
|------|------|
| 包名 | `axios` |
| 定位 | 最流行的 Node.js/Browser 通用 HTTP 客户端 |
| 核心优势 | 拦截器机制、请求/响应转换、取消令牌、CSRF 保护、JSON 自动处理 |
| 周边生态 | `axios-retry`（自动重试）、`cache-interceptor`（缓存拦截器） |
| 适用环节 | 如不需要抓取专用特性而偏向通用性，可作为基础 HTTP 层 |
| 注意事项 | 不自带指纹模拟，需手动构造请求头；2025 年曾发生供应链攻击事件，锁定版本 |

### 5.2 HTML 解析层

当前实现使用正则表达式解析搜索引擎 HTML 结果。以下库可提供更健壮的 DOM 级解析能力：

#### cheerio（⭐ 重点推荐）

| 维度 | 详情 |
|------|------|
| 包名 | `cheerio` |
| 定位 | 服务端 jQuery 核心子集实现，轻量快速 DOM 解析 |
| 核心优势 | jQuery 风格 API（`$('.b_algo')`）、极快速度（约比 jsdom 快 8 倍）、零依赖、API 熟悉度高 |
| 适用环节 | 替代四引擎解析器中的正则匹配，改用 CSS 选择器精确提取标题/URL/摘要 |
| 示例场景 | `$('li.b_algo').each((i, el) => { const $el = $(el); ... })` |
| 版本注意 | Cheerio v1（最新版）API 有变化，需参考新版文档 |

#### jsdom（特定场景备选）

| 维度 | 详情 |
|------|------|
| 包名 | `jsdom` |
| 定位 | 纯 JavaScript 实现的完整 DOM 环境 |
| 核心优势 | 完整 DOM API（querySelector/getComputedStyle 等）、脚本执行、兼容依赖 DOM 的库 |
| 适用环节 | 需要 DOM 兼容性的复杂解析场景（如运行依赖 DOM 的第三方提取脚本） |
| 权衡 | 比 cheerio 慢且重量大，一般场景 cheerio 已足够 |

#### parse5（底层备选）

| 维度 | 详情 |
|------|------|
| 包名 | `parse5` |
| 定位 | WHATWG HTML Living Standard 合规的 HTML 解析/序列化工具集 |
| 核心优势 | 严格符合 HTML5 规范、可处理畸形 HTML、提供 serialize 功能 |
| 适用环节 | 需要规范合规 HTML 解析的底层场景；或作为 cheerio/jsdom 的底层解析引擎 |

### 5.3 文章内容提取层

当前 htmlToText 函数做简单的标签移除与文本提取。以下库可大幅提升文章类页面的提取质量：

#### @mozilla/readability（⭐ 重点推荐）

| 维度 | 详情 |
|------|------|
| 包名 | `@mozilla/readability` |
| 定位 | Mozilla Firefox Reader View 的算法移植，自动提取文章主体内容 |
| 核心优势 | 基于 Mozilla 成熟算法，自动识别文章正文区、剥离导航/广告/评论/侧边栏噪声 |
| 输入要求 | 需配合 `jsdom` 提供 DOM 环境 |
| 输出 | `{ title, content, textContent, length }` |
| 适用环节 | web_fetch 抓取后作为 htmlToText 的前置步骤：先 readability 提取主体，再转文本 |
| 效果预期 | 对新闻/博客/百科/论坛帖子类页面效果极佳，可直接替代手动噪声标签列表 |
| 依赖 | 需搭配 `jsdom` 使用 |
| 同类项目 | `node-readability`（封装了 request + @mozilla/readability 的老项目，已不太活跃） |
| 注意事项 | 对非文章类页面（如搜索结果页、列表页、数据仪表盘）可能误提取或返回空 |

#### turndown（HTML → Markdown）

| 维度 | 详情 |
|------|------|
| 包名 | `turndown` |
| 定位 | HTML 到 Markdown 的转换器 |
| 核心优势 | 保留文档结构（标题层级、表格、列表、代码块、链接），输出格式友好 |
| 适用环节 | 当需要保留 HTML 结构而非纯文本时，作为 htmlToText 的替代或补充 |
| 使用方式 | `TurndownService().turndown(htmlString)` → Markdown 文本 |

### 5.4 浏览器反检测层

当前 browser 模块使用 Electron 原生 BrowserWindow，无额外反检测措施。以下插件可显著降低被网站识别为自动化脚本的概率：

#### puppeteer-extra-plugin-stealth（⭐ 重点推荐）

| 维度 | 详情 |
|------|------|
| 包名 | `puppeteer-extra-plugin-stealth` |
| 定位 | Puppeteer 反检测插件，隐藏无头浏览器自动化痕迹 |
| 核心优势 | **18 种独立的反检测技术模块**，系统性覆盖所有主要检测维度 |
| 检测维度覆盖 | 见下方详细列表 |
| 架构设计 | 微内核 + 插件化，每种技术封装为独立模块，可单独启停 |
| 依赖关系 | 需配合 `puppeteer-extra` 使用 |
| 适用环节 | browser 模块的 BrowserWindow 创建时注入 stealth 脚本，或在 browser 回退通道中使用 puppeteer-extra 替代原生 BrowserWindow |

**18 种反检测技术模块清单**：

| # | 模块名 | 解决的检测点 |
|---|--------|-------------|
| 1 | `navigator.webdriver` | 移除 webdriver=true 标志 |
| 2 | `chrome.runtime` | 模拟 chrome.runtime 对象 |
| 3 | `chrome.loadTimes` | 模拟已废弃但仍被检测的 API |
| 4 | `chrome.csi` | 模拟 Chrome CSI 对象 |
| 5 | `iframe.contentWindow` | 修复 iframe contentWindow 检测 |
| 6 | `media.codecs` | 补全媒体编解码器支持列表 |
| 7 | `navigator.plugins` | 模拟浏览器插件信息 |
| 8 | `navigator.languages` | 修正语言设置 |
| 9 | `navigator.platform` | 修正平台标识 |
| 10 | `navigator.hardwareConcurrency` | 模拟真实的 CPU 核心数 |
| 11 | `webgl.vendor` | 伪装 WebGL 渲染厂商/型号 |
| 12 | `window.outerdimensions` | 修正窗口尺寸关系 |
| 13 | `sourceurl` | 移除源码 URL 泄露 |
| 14 | `user-agent-override` | 同步 navigator.userAgent 与请求头 |
| 15 | `permissions` | 修正 Permissions API 行为 |
| 16 | `runtime.enable` | 修复 chrome.runtime.enable |
| 17 | `eof` | 修复 EOF 检测 |
| 18 | `preload` | 修复脚本 preload 检测 |

> **集成建议**：由于本项目基于 Electron（非独立 Puppeteer），有两种集成路径：(A) 将 stealth 插件的各模块 JS 脚本提取出来，通过 `webContents.executeJavaScript` 在 BrowserWindow 加载时注入；(B) 在 browser 回退通道中使用 `puppeteer-extra` + stealth 替代原生 BrowserWindow。路径 A 更轻量，路径 B 更彻底。

### 5.5 缓存层

当前使用手写 Map 实现 LRU 缓存。以下库提供更完善的能力：

#### lru-cache（⭐ 推荐）

| 维度 | 详情 |
|------|------|
| 包名 | `lru-cache` |
| 定位 | 高性能 LRU 缓存实现，支持 TTL、大小限制、统计 |
| 核心优势 | TTL 自动过期、 maxSize 按 count 或 byte 限制、hit/miss/stale 统计、异步 dispose 回调 |
| 适用环节 | 替代搜索缓存（200 条/5min）和浏览器回退缓存（100 条/10min）的手写实现 |
| API 示例 | `new LRUMax({ max: 200, ttl: 300_000 })` |

#### cacheable（备选）

| 维度 | 详情 |
|------|------|
| 包名 | `cacheable` |
| 定位 | 支持多后端（内存/Redis/文件）的统一缓存抽象 |
| 核心优势 | 可无缝切换存储后端，适合未来分布式部署需求 |
| 适用环节 | 如果未来需要跨进程共享缓存（如主进程 + 多个渲染进程） |

### 5.6 URL 处理层

当前 URL 去重依赖简单标准化。以下库可提供更强的 URL 归一化能力：

#### normalize-url（推荐）

| 维度 | 详情 |
|------|------|
| 包名 | `normalize-url` |
| 定位 | URL 归一化工具 |
| 核心优势 | 自动去除追踪参数（utm_*）、统一协议/路径/查询串/片段格式、强制小写、去除默认端口 |
| 适用环节 | 搜索结果合并去重阶段的 URL 标准化，减少因 URL 变体导致的重复结果 |

### 5.7 SearXNG 客户端层

当前 SearXNG 调用通过手动拼接 URL 参数 + fetch 发送。以下方案可考虑：

#### 手动封装（当前方案，合理）

SearXNG API 本身非常简单（GET `/search?q=...&format=json`），手动拼接完全够用。不建议引入额外的客户端库增加复杂度。如未来需要更多功能（如实例健康检查、引擎状态查询），可考虑封装一个轻量内部模块即可。

### 5.8 库选型总览

| 环节 | 当前方案 | ⭐ 推荐替代 | 核心收益 | 引入成本 |
|------|---------|------------|---------|---------|
| HTTP 抓取 | 原生 fetch + 手动请求头 | **got-scraping** | 指纹模拟、Cookie管理、HTTP/2、代理 | 中（替换 fetch 调用） |
| HTML 解析（搜索引擎） | 正则匹配 | **cheerio** | CSS选择器、DOM遍历、鲁棒性 | 中（重写4个解析器） |
| 文章内容提取 | 标签移除 + 纯文本 | **@mozilla/readability** | 自动识别正文、剥离噪声 | 中（需jsdom依赖） |
| HTML → 结构化文本 | 自定义 htmlToText | **turndown**（可选） | 保留Markdown结构 | 低（新增转换通道） |
| 浏览器反检测 | 无 | **puppeteer-extra-plugin-stealth** | 18种反检测、绕过Cloudflare | 高（需适配Electron） |
| 缓存 | 手写 Map LRU | **lru-cache** | TTL、统计、dispose回调 | 低（替换数据结构） |
| URL 归一化 | 简单标准化 | **normalize-url** | 去追踪参数、统一格式 | 低（替换标准化函数） |

---

## 6. 优化增强路线图

本章按优先级排列可执行的优化项，每项标注预期收益、复杂度与依赖关系。

### 6.1 P0 — 高优先级（立即可做）

#### 6.1.1 引入 lru-cache 替换手写缓存

- **改动范围**：搜索缓存 + 浏览器回退缓存
- **工作量**：小（约 2-3 小时）
- **收益**：消除手写 LRU 的边界 bug 风险，获得 TTL 精确过期与统计指标
- **依赖**：安装 `lru-cache` 包

#### 6.1.2 引入 cheerio 重写四引擎解析器

- **改动范围**：Bing/百度/搜狗/360 四个 HTML 解析函数
- **工作量**：中（约 1-2 天）
- **收益**：正则 → CSS 选择器，解析鲁棒性大幅提升，应对搜索引擎 HTML 变更的成本降低
- **依赖**：安装 `cheerio` 包

#### 6.1.3 引入 normalize-url 强化去重

- **改动范围**：搜索结果合并去重的 URL 标准化步骤
- **工作量**：小（约 1-2 小时）
- **收益**：减少因 utm_* 参数、尾部斜杠、协议差异导致的重复结果
- **依赖**：安装 `normalize-url` 包

### 6.2 P1 — 中优先级（短期规划）

#### 6.2.1 引入 got-scraping 替换 web_fetch Phase 1

- **改动范围**：web_fetch 的 HTTP 抓取阶段
- **工作量**：中（约 2-3 天）
- **收益**：内置指纹轮换、Cookie Jar、HTTP/2、代理支持，被反爬识别概率显著降低
- **风险**：got-scraping 基于 got，需确认与 Electron 进程的兼容性；必要时可用 `got` 核心自行封装
- **依赖**：安装 `got-scraping` 包

#### 6.2.2 引入 @mozilla/readability 提升文章提取质量

- **改动范围**：web_fetch 的 HTML 转文本流程，新增 readability 前置步骤
- **工作量**：中（约 1-2 天）
- **收益**：新闻/博客/百科类页面的提取质量质的飞跃，噪声自动剥离
- **注意事项**：需处理非文章页面的 fallback（readability 返回空时回退到原有 htmlToText）
- **依赖**：安装 `@mozilla/readability` + `jsdom`

#### 6.2.3 UI 增加「连接测试」按钮

- **改动范围**：SearXNG 配置模态框
- **工作量**：小（约 半天）
- **收益**：用户保存配置前可一键验证 URL 可达性与认证有效性，降低配置错误率
- **实现要点**：向 `${url}/search?q=test&format=json` 发送带认证头的测试请求，展示响应状态与耗时

### 6.3 P2 — 中低优先级（中期规划）

#### 6.3.1 集成 stealth 反检测

- **改动范围**：browser 模块的 BrowserWindow 创建 / browser 回退通道
- **工作量**：大（约 3-5 天）
- **收益**：绕过 Cloudflare 等反爬检测的概率大幅提升，减少浏览器回退触发频率
- **推荐路径**：提取 stealth 插件的 JS 脚本，通过 `webContents.executeJavaScript` 注入 BrowserWindow
- **依赖**：分析 `puppeteer-extra-plugin-stealth` 源码，提取各 evasions 模块

#### 6.3.2 引入 turndown 新增 Markdown 输出模式

- **改动范围**：web_fetch / browserExtract 的内容输出
- **工作量**：小（约 1 天）
- **收益**：AI 收到的网页内容保留结构（标题层级、表格、列表、代码块），提升理解质量
- **实现要点**：新增 `format: 'markdown'` 参数选项，默认保持现有纯文本行为不变

#### 6.3.3 代理支持

- **改动范围**：web_fetch HTTP 抓取阶段 + SearXNG 调用
- **工作量**：中（约 2-3 天）
- **收益**：企业网络环境下可通过代理访问外网；高频抓取时可轮换 IP 降低封禁风险
- **实现要点**：配置项新增 `proxy_url` / `proxy_list`，got-scraping 原生支持代理

### 6.4 P3 — 低优先级（长期规划）

#### 6.4.1 分布式缓存后端

- **前提**：P0 缓存改造完成
- **方向**：使用 `cacheable` 或类似方案，支持 Redis / SQLite / 文件后端
- **收益**：多实例部署时缓存共享，避免重复抓取

#### 6.4.2 搜索引擎解析器热更新

- **方向**：将四引擎解析规则外部化为配置文件或脚本，支持不重启生效
- **收益**：搜索引擎 HTML 结构变更时无需发版，运维可快速响应

#### 6.4.3 抓取速率自适应限速

- **方向**：根据目标站点的响应时间与错误率动态调整请求间隔
- **收益**：尊重目标站点，减少被封禁概率，体现负责任的抓取行为

---

## 附录 A：工具联动模式

四种模块在实际任务中的典型协作流程：

**标准搜索流程**：
```
web_search(query) → 分析结果 → web_fetch(topN URLs) → 综合回答
```

**浏览器交互流程**：
```
browser_open(url) → browser_wait(selector) → browser_extract()
→ browser_screenshot() → browser_click() / browser_type() → browser_close()
```

**自动降级流程**：
```
web_fetch(url) → [HTTP失败] → browser_open(url) → browser_extract() → browser_close()
```

**SearXNG + 自动抓取流程**：
```
web_search(query) → [SearXNG模式] → JSON结果 → 相关性过滤
→ applyAutoFetch(topN) → 逐条web_fetch() → 带全文的回答
```

## 附录 B：关键文件索引（原始实现参考）

| 文件 | 说明 |
|------|------|
| `src/main/browser.ts` | 浏览器控制核心（9 个函数） |
| `src/main/tool-handlers-system.ts` | web_fetch + web_search + SearXNG（核心实现） |
| `src/main/ipc.ts` | IPC 工具调度 |
| `src/main/main.ts` | 应用生命周期（browserClose on quit） |
| `src/renderer/components/searxng-modal.ts` | SearXNG 配置模态框 |
| `src/renderer/index.html` | SearXNG 模态框 HTML |
| `src/renderer/styles/style.css` | SearXNG 样式 |
| `src/renderer/services/tool-registry.ts` | 工具定义和参数声明 |
| `src/renderer/services/agent-engine.ts` | 工具超时配置、并行/串行调度 |

## 附录 C：第三方库速查表

| 库名 | 版本建议 | npm 周下载量级 | 许可证 | 引入优先级 |
|------|---------|--------------|--------|-----------|
| `got-scraping` | 最新稳定版 | ~500K+/周 | MIT | P1 |
| `cheerio` | v1.x | ~10M+/周 | MIT | P0 |
| `@mozilla/readability` | 最新版 | ~2M+/周 | Apache-2.0 | P1 |
| `jsdom` | 最新版 | ~5M+/周 | MIT | P1（readability 依赖） |
| `turndown` | v7.x | ~2M+/周 | MIT | P2 |
| `puppeteer-extra-plugin-stealth` | 最新版 | ~500K+/周 | MIT | P2 |
| `puppeteer-extra` | 最新版 | ~1M+/周 | MIT | P2（stealth 依赖） |
| `lru-cache` | v10.x | ~20M+/周 | ISC | P0 |
| `normalize-url` | 最新版 | ~5M+/周 | MIT | P0 |
| `axios` | ^1.7.x（锁定安全版本） | ~30M+/周 | MIT | 备选 |
