新增 MiMo (小米) LLM Provider 适配器,支持 mimo-v2.5-pro 和 mimo-v2.5 两个文本模型,复用 OpenAI 兼容 SSE 流式解析,支持 Thinking 模式和 Function Calling。同步校准全量 docs 文档与 README 使其与实际代码一致。 主要变更: - 新增 mimo.adapter.ts 适配器(SSE + thinking.type + max_completion_tokens) - 修复 thinking 逻辑 bug:禁用思考时未传 temperature/top_p - 补全 sse-stream.ts 的 MiMo 缓存字段映射(prompt_tokens_details.cached_tokens) - 补全 sse-stream.ts 的 finish_reason 映射(repetition_truncation) - 注册 MiMo 适配器到 adapters/index.ts、main.ts 工厂 - handlers.ts 添加 mimo.contextWindow 热重载触发 - database.service.ts seed 添加 mimo 默认配置 - SettingsModal/OnboardingWizard/Header 添加 MiMo Provider UI - constants.ts PROVIDER_LABELS 添加 mimo - .env.example 添加 MIMO_API_KEY/MIMO_BASE_URL - 反向修改 4 个 docs HTML 设计文档(工具数量/版本日期/适配器列表/数据库表) - 反向修改 Agent网络工具通用设计-v2.md 附录 B 文件索引 - 完全重写 README.md(v0.3.4、27 工具、4 适配器、9 表)
953 lines
44 KiB
Markdown
953 lines
44 KiB
Markdown
# 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+ 命名实体(` `、`<`、`&`、`…`、`—` 等)+ 全部数字实体(`{`、``)
|
||
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:关键文件索引(原始实现参考)
|
||
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| `electron/harness/tools/built-in/browser.ts` | 浏览器控制核心(9 个 action 路由) |
|
||
| `electron/harness/tools/built-in/browser-window-manager.ts` | 浏览器窗口状态管理 + 页面操作(单例 + 隔离会话) |
|
||
| `electron/harness/tools/built-in/web-search.ts` | web_search 工具(SearXNG / 内置四引擎双模式) |
|
||
| `electron/harness/tools/built-in/web-fetch.ts` | web_fetch 工具(三阶段回退抓取) |
|
||
| `electron/harness/tools/built-in/network-utils.ts` | 网络工具函数(LRU 缓存、UA 轮换、反爬请求头、拦截检测、HTML 转文本、SearXNG 认证) |
|
||
| `electron/harness/tools/built-in/http-request.ts` | HTTP/REST API 请求工具 |
|
||
| `electron/harness/tools/built-in/file-guard.ts` | 文件保护(工作空间边界 + MEMORY.md 保护) |
|
||
| `electron/harness/tools/registry.ts` | 工具注册表 + PolicyEngine 策略引擎 + truncateResult |
|
||
| `electron/harness/sandbox/permissions.ts` | PolicyEngine(三级权限 + 频率限制 + 通配符策略) |
|
||
| `electron/harness/agent-loop/engine.ts` | Agent Loop 引擎(ReAct 状态机 + 工具超时配置) |
|
||
| `electron/ipc/handlers.ts` | IPC 通道处理(50+ 通道,含 `searxng:testConnection`) |
|
||
| `electron/main.ts` | 应用生命周期(启动流程 + browserClose on quit) |
|
||
| `src/components/settings/SettingsModal.tsx` | 设置面板(含 SearXNG 配置 Tab) |
|
||
| `src/stores/agent-store.ts` | Agent 状态管理(Zustand) |
|
||
| `src/hooks/useAgentStream.ts` | 流式事件监听 Hook |
|
||
|
||
## 附录 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 | 备选 |
|