refactor: 移除多余依赖,统一 MUI 为唯一 UI 库

- 移除 radix-ui、clsx、tailwind-merge、class-variance-authority、react-router-dom

- 前后端全面适配 MUI 组件,清理残留 Tailwind 工具类引用

- 优化 Agent Loop 引擎、IPC 处理器、Provider Adapter 等后端模块

- 新增 Agent 网络工具通用设计文档 v2
This commit is contained in:
thzxx
2026-07-05 19:15:48 +08:00
parent ba85328c4f
commit f4532a2bb2
50 changed files with 1474 additions and 2042 deletions
+946
View File
@@ -0,0 +1,946 @@
# 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)签发的固定令牌鉴权
- JWTJSON 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 → 先关闭重建,避免历史状态干扰
- 加载目标 URL30 秒超时
- 可选等待指定 CSS 选择器出现(轮询 300ms,最长 10s)
- 返回页面标题与实际 URL
### 4.4 截图
三种模式:
| 模式 | 触发条件 | 输出 |
|------|---------|------|
| 元素截图 | 指定 selector | 该元素矩形区域 PNGBase64 |
| 全页截图 | fullPage=true | 完整滚动尺寸 PNGBase64 |
| 视口截图 | 默认 | 当前可见区域 PNGBase64 |
### 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 APIquerySelector/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 的老项目,已不太活跃) |
| 注意事项 | 对非文章类页面(如搜索结果页、列表页、数据仪表盘)可能误提取或返回空 |
#### turndownHTML → 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 天)
- **收益**:新闻/博客/百科类页面的提取质量质的飞跃,噪声自动剥离
- **注意事项**:需处理非文章页面的 fallbackreadability 返回空时回退到原有 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 | P1readability 依赖) |
| `turndown` | v7.x | ~2M+/周 | MIT | P2 |
| `puppeteer-extra-plugin-stealth` | 最新版 | ~500K+/周 | MIT | P2 |
| `puppeteer-extra` | 最新版 | ~1M+/周 | MIT | P2stealth 依赖) |
| `lru-cache` | v10.x | ~20M+/周 | ISC | P0 |
| `normalize-url` | 最新版 | ~5M+/周 | MIT | P0 |
| `axios` | ^1.7.x(锁定安全版本) | ~30M+/周 | MIT | 备选 |