dsh-search-tools
为 DeepSeek Harness 提供可插拔的网页搜索与正文抓取:支持 SearXNG 搜索、Readability 正文提取,全部在 Web 设置页完成配置。
- Stars
- 1
- Language
- TypeScript
- Created
- Oct 22, 2026
- Updated
- Oct 25, 2026
Introduction
dsh-search-tools
为 DeepSeek Harness(DSH)提供 adapter-first 的网页搜索与正文抓取。所有配置都在 DSH Web GUI 的 设置 → 搜索工具 完成,不要求用户手改 YAML。
仓库版本:0.1.9(2026-10-24)。 本版为 Local Direct 增加 Brave Search 与 360 Search 两个可选 HTML 引擎,并把执行模型改为每引擎独立 worker:一个慢引擎不会吞掉另一个引擎的成功结果。默认引擎仍是 DuckDuckGo + Bing;解析、去重、RRF 融合、域名多样性和缓存都在本地完成,不需要 SearXNG、API key、Reader、浏览器或第三方聚合服务。查询会直接发往启用的公网搜索引擎,但不会经过 SearXNG/Reader;不执行页面 JavaScript。默认搜索仍是
searxng,稳定 provider id 和工具 schema 不变。详细边界见 Local Direct 搜索配置 与 v0.1.9 发布清单。
目录
它解决什么问题
DSH 自带 web_search 和 web_fetch,但默认后端不一定适合每个人:有人已经有自己的 SearXNG,有人需要把搜索请求走代理,有人希望抓取结果直接是干净的正文,而不是整页 HTML。
dsh-search-tools 把“搜索/抓取后端”从 DSH 核心中解耦出来:
- 用户可以在 Web 设置页选择后端、填写实例、测试连接;
- 插件向 DSH 注册稳定 provider id,切换后端不会改变 DSH 的
web.searchProvider; - 新增后端只需要注册 adapter,设置页按 manifest 自动渲染,不需要改页面代码;
- 额外提供
web_research:一次调用完成“搜索 + 抓取前 N 条正文”,适合需要页面内容而非只拿链接的模型回合。
它不做这些事:不提供公共搜索 API,不绕过目标站点的登录/付费墙,也不替代 DSH 的权限与沙箱模型。渲染能力取决于抓取 adapter:readable-http 不执行页面 JavaScript;可选的 wire-http 在本地做浏览器级 HTTP/TLS 指纹和 PDF 文本提取,但同样不执行页面 JavaScript;可选的 jina-reader 会把公网 URL 交给 Reader 服务做浏览器渲染(自托管时留在本机),三者都不登录、不绕过付费墙、不破解验证码。
界面预览
以下截图使用 demo 数据(https://searxng.example.com,无真实凭据):




功能一览
| 能力 | 说明 |
|---|---|
| 稳定的搜索/抓取 provider | dsh-search-tools、dsh-search-tools-fetch 永不随后端变化 |
| 内置搜索 adapter | SearXNG:多实例、顺序/轮询、故障转移、JSON/HTML、GET/POST、Bearer/Basic、代理、缓存、分页、域名过滤 |
| 本地直连搜索 adapter(可选) | Local Direct:本机直接查询 DuckDuckGo + Bing HTML 首页,可选 Brave Search / 360;本地解析/去重/RRF 融合/域名多样性/缓存;不需要服务、Key、Reader 或浏览器,不执行 JS |
| 内置抓取 adapter | Readability HTTP:复用 DSH 安全 HTTP,Readability 提取正文,失败可回退原文 |
| 本地强抓取 adapter(可选) | Wire HTTP:impit 本地浏览器级 TLS/HTTP2 指纹、逐跳公网出口 pinning、Readability、PDF 文本;不需要 Reader/Key/浏览器/容器,不执行 JS |
| 可选渲染抓取 adapter | Jina Reader:浏览器渲染、PDF/Office、CSS 选择器、iframe/Shadow DOM、代理选项;目标必须是公网地址,Reader 可托管或自托管 |
| 研究工具 | web_research:搜索、按并发抓取前 N 页、按页/总预算截断、返回来源与正文 |
| Web 设置页 | 概览、搜索、抓取、研究、诊断五个页签;Connection / Search behavior 子页、实例卡、内联测试、正文 reader、预算条、字段级错误、诊断、缓存清理、键盘保存 |
| 凭据隔离 | 搜索 token 写入 DSH credentials,不落 YAML、不进日志、不出现在诊断文本 |
| 接管与恢复 | 可选让内置 web_search / web_fetch 使用本插件;关闭时恢复之前的 provider |
| 外部 adapter | 通过 ctx.searchTools 注册新后端;设置页自动渲染 manifest 字段 |
工作原理
DSH ctx.web
searchProvider = "dsh-search-tools" (稳定)
fetchProvider = "dsh-search-tools-fetch" (稳定)
│
▼
SearchDispatcher / FetchDispatcher
│ 读取 config.search.adapter / config.fetch.adapter
▼
ctx.searchTools(adapter 注册表)
├── searxng 内置搜索 adapter(默认)
├── local-direct 内置搜索 adapter(本机直连 DDG + Bing)
├── readable-http 内置抓取 adapter(默认,DSH 安全 HTTP)
├── wire-http 内置抓取 adapter(可选,本地浏览器级 HTTP + PDF)
├── jina-reader 内置抓取 adapter(可选,Reader 渲染)
└── 未来由独立包注册的 adapter
关键设计:
- provider id 稳定,adapter 可换。 加一个后端不会改
web.searchProvider,也不会要求 DSH 重启后重新注册 provider。 - 配置按 section 开放。
search.adapters和fetch.adapters是开放字典,adapter 自己拥有默认值与校验逻辑;核心 schema 不需要为每个后端加字段。 - manifest 驱动设置页。 Host 路由把 adapter manifest 发给浏览器,页面按
fields通用渲染。新增 adapter 不需要改 React 代码。 - 配置在调用时解析。 Dispatcher 每次按当前配置选择 adapter;切换后不需要重新注册 provider。
架构细节见 docs/architecture.md。
环境要求
| 项目 | 要求 |
|---|---|
| DSH | >=0.2.1-rc.2 <0.3.0(当前按 0.2.1-rc.2 验证) |
| Node.js | `^22.19.0 |
| 搜索后端 | Local Direct(不需要部署,直接查 DDG/Bing)或一个可访问的 SearXNG 实例;也可暂时保持 DSH 默认搜索 |
| 抓取后端 | 公网 http(s) 页面;readable-http 由 DSH SSRF 防护拦截内网,wire-http 在本地逐跳 pin 公网地址,jina-reader 在发出前做公网目标守卫 |
| Reader(可选) | r.jina.ai 的 API key,或一个可访问的自托管 Jina Reader 端点;不配置时继续用 readable-http 或 wire-http |
| 浏览器 | DSH Web GUI(设置页只在 Web 端注册) |
DSH 本身仍是 preview 软件,接口可能变化。插件把 peer 范围锁在已验证的 0.2.1-rc.2 线,升级 DSH 前请先看 CHANGELOG。
安装与升级
方式一:Web UI(推荐)
-
打开 DSH Web GUI。
-
进入 插件(Plugins)。
-
点击 添加插件(Add plugin)。
-
输入包名:
dsh-search-tools -
安装完成后启用这个 bundle。
-
打开 设置 → 搜索工具。
设置页出现,说明 Host 与浏览器两半都已加载。
方式二:CLI
dsh plugin --profile <profile> add dsh-search-tools
<profile> 换成实际 profile 名。安装后启动该 profile:
dsh --profile <profile>
方式三:离线 tarball
从 GitHub Releases 下载最新可用的 dsh-search-tools-<version>.tgz,然后:
dsh plugin --profile <profile> add ./dsh-search-tools-<version>.tgz
发布资产与 npm 产物字节一致,适合内网或无法访问 npm 的环境。
升级
DSH 插件目前不支持自动更新。升级步骤:
- 先在 设置 → 搜索工具 → 概览 关闭搜索/抓取接管(如果已开启)。
- 在 插件 页卸载旧版本。
- 重新添加
dsh-search-tools或新的 tarball。 - 重新启用,检查设置页配置是否保留(配置在 profile 的 YAML patch 中,通常不会因重装丢失)。
- 重新测试搜索与抓取。
CLI 对应:
dsh plugin --profile <profile> remove dsh-search-tools
dsh plugin --profile <profile> add dsh-search-tools
不要在接管仍开启时直接卸载。内置
web_search会指向一个不存在的 provider,直到你重装插件或手动恢复web.searchProvider。
安装后检查
- 设置 → 搜索工具 能看到五个页签。
- 诊断 里能看到版本号、配置文件路径、抓取/研究运行状态和缓存统计。
- 如果模型工具已启用,会话里应能看到
web_research。 - 搜索测试通过后,才建议开启搜索接管。
快速开始
1. 准备搜索来源
有两条零代码路径:
- Local Direct(推荐先试):不需要准备任何服务。它在本机直接请求 DuckDuckGo 和 Bing 的公开 HTML 结果页,解析和融合都在本机完成。可额外启用 Brave Search 或 360;Brave 内部使用 Firefox HTTP 指纹,其他引擎遵循
profile设置。查询会离开本机发往启用的引擎;首次使用请确认所在网络能访问它们。 - SearXNG:需要一个可访问的 SearXNG 实例。实例地址是
http(s)绝对 URL,例如https://searx.example.com;JSON API 需要search.formats包含json,HTML-only 也可以;有认证时准备 Bearer token 或username:password;有限流时准备多个实例或代理。插件会自动去掉末尾/search、query 和 hash,实例 URL 里不能带用户名/密码。
2. 在设置页配置
- 打开 设置 → 搜索工具 → 搜索。
- 在 后端 选择
Local Direct或SearXNG。 - Local Direct:勾选 DuckDuckGo / Bing / Brave Search / 360 搜索 中至少一个,按需选市场、安全搜索、结果数;不需要实例或密钥。点 测试搜索引擎 可检查可达性。
- SearXNG:点击 添加实例,填写实例地址;备注名可选;按需设置引擎、分类、语言、安全搜索、分页、结果数、超时;需要认证时选择
Bearer/Basic并保存密钥。 - 点击 测试,确认返回结构化来源;Local Direct 还会显示可达引擎。
- 点击 保存并应用。
3. 配置抓取(可选)
- 打开 抓取 页签。
- 先保持 Readability HTTP,用默认值测试一个公网页面,例如
https://example.com。 - 如果目标站点是 SPA、PDF/Office,或静态 HTTP 抓不到正文,改选 Jina Reader:
- 托管服务填
https://r.jina.ai/,在 Jina API key 保存密钥; - 自托管填
http://127.0.0.1:3000/(仅 loopback 允许明文 HTTP)或 HTTPS 反代地址; - 需要动态内容时把 Fetch engine 设为
browser;需要新鲜内容时打开 Bypass Reader cache。
- 托管服务填
- 保存前看 form 顶部的数据边界 warning;测试 通过后保存并应用。
web_fetch工具默认 30 秒;把 Reader 超时调到 30 秒以上时,必须同时调高 DSHtool-web.fetchTimeoutMs,否则会先收到TOOL_TIMEOUT。
4. 开启接管(可选)
在 概览:
- 接管内置搜索:让 DSH 的
web_search使用本插件的搜索 provider。开启前会先用当前配置跑一次搜索测试,失败则不会接管。 - 接管内置抓取:让 DSH 的
web_fetch使用本插件的抓取 provider。
接管状态会写入 DSH 的 web.searchProvider / web.fetchProvider。关闭时插件会恢复之前记录的 provider(activation.previousSearchProvider / previousFetchProvider)。
如果只想用 web_research,不一定要开启接管。web_research 调用的是当前 DSH 的搜索/抓取 provider;不接管时就使用 DSH 默认后端。
5. 使用模型工具
模型会话中可以直接使用:
web_search:DSH 内置工具,接管后走本插件;web_fetch:DSH 内置工具,接管后走本插件;web_research:本插件注册的研究工具,不依赖接管,但会使用当前 provider。
web_research 示例参数:
{
"query": "DeepSeek Harness release notes",
"max_results": 8,
"fetch_top": 3,
"max_chars": 12000
}
返回内容包含 sources、fetched 和 truncated;会话卡片会显示来源数、成功抓取数、抓取详情和截断标记。
设置页说明
概览(Overview)
- 当前搜索 provider / 抓取 provider;
- 接管状态与快捷开关;
- 搜索后端是否可用、搜索来源数(SearXNG 实例或 Local Direct 引擎)、缓存条数;
- 快速搜索测试与抓取预览;
- 没有来源时的引导卡片。
搜索(Search)
- 卡片式 adapter picker:显示 adapter 描述、capabilities、配置状态;
- SearXNG 实例编辑器:添加、删除、上移、下移、启用/停用、单实例测试;
- Local Direct 引擎多选、市场、安全搜索、每站结果数;不需要实例;
- 测试搜索引擎:使用固定探测词,不写缓存,直接列出可达引擎,无实例也能用;
- 由 manifest 自动渲染的字段;
- 内联测试:显示耗时、来源标题/链接/摘要、
Search notes(例如单个引擎被限流或解析为空); - 校验错误会定位到具体字段,搜索草稿的错误不会阻塞抓取测试。
抓取(Fetch)
- 抓取 adapter 选择与字段;
- 输入公网 URL 做一次抓取预览;
- 选择
wire-http时显示本地边界 notice、浏览器指纹/超时/PDF 字段;它不需要 Key,也不执行页面 JavaScript; - 选择
jina-reader时显示数据边界 warning、API key 和浏览器/Reader 选项;readable-http仍保留原有字段; - 显示状态码、内容类型、字符数、截断标记和正文预览;
- 内网地址会被
readable-http的 SSRF 防护或jina-reader的公网目标守卫拒绝,这是预期行为。
研究(Research)
- 启用/关闭
web_research; - 默认结果数、最大抓取条数、默认抓取条数、单页/总字符预算、并发、超时;
- 模型看到的工具提示 预览:描述、参数名、默认值与范围都来自当前草稿;可一键复制;
- 关闭
web_research后,保存后模型将看不到这个工具。
诊断(Diagnostics)
- 配置文件路径;
- 搜索后端、抓取后端、研究工具的真实运行状态(
fetch.available/research.registered); - 缓存条数与命中/未命中统计;
- 清空缓存;
- 最近错误(环形缓冲);
- 复制诊断信息(已脱敏)与清空错误;
- 当前版本。
键盘与状态
Cmd/Ctrl+S:保存并应用当前草稿(仅在配置有效且有未保存更改时)。- 页签支持
←/→/Home/End。 - 保存状态、测试结果、诊断操作都通过
aria-live播报。 - 配置被更高层覆盖时页面只读;配置在别处修改时提示冲突,需重新加载。
配置参考
配置存放位置
配置通过 DSH 原生 settings 服务写入当前 profile 的 YAML patch:
$DSH_HOME/profiles/<profile>/cordis.patch.yml
结构大致如下:
- id: dsh-search-tools
name: dsh-search-tools
config:
search:
adapter: searxng
adapters:
searxng:
enabled: true
instances:
- url: https://searx.example.com
enabled: true
label: primary
engines: []
categories: [general]
language: all
safesearch: 1
pages: 1
maxResults: 10
strategy: ordered
method: GET
format: auto
auth:
type: none
requestTimeoutMs: 12000
deadlineMs: 45000
retries: 1
cacheTtlMs: 300000
cacheMaxEntries: 200
userAgent: dsh-search-tools/0.1.9 (+https://github.com/ThzxxArt/dsh-search-tools)
proxy: ""
includeDomains: []
excludeDomains: []
local-direct:
enabled: true
engines: [duckduckgo, bing]
market: auto
safeSearch: moderate
maxResults: 10
maxPerHost: 2
profile: chrome
requestTimeoutMs: 8000
deadlineMs: 15000
retries: 1
minIntervalMs: 1000
cacheTtlMs: 300000
cacheMaxEntries: 200
maxResponseBytes: 2000000
proxy: ""
includeDomains: []
excludeDomains: []
fetch:
adapter: readable-http
adapters:
readable-http:
enabled: true
readability: true
minArticleChars: 200
maxArticleChars: 200000
maxResponseBytes: 5000000
maxBodyChars: 2000000
timeoutMs: 20000
maxRedirects: 5
userAgent: dsh-search-tools/0.1.9 (+https://github.com/ThzxxArt/dsh-search-tools)
failOpen: true
wire-http:
enabled: true
profile: chrome
timeoutMs: 25000
maxRedirects: 5
locale: ""
maxResponseBytes: 5000000
readability: true
minArticleChars: 200
maxArticleChars: 200000
failOpen: true
pdf: true
maxPdfPages: 10
maxBodyChars: 2000000
jina-reader:
enabled: true
endpoint: https://r.jina.ai/
engine: auto
responseMode: markdown
timeoutMs: 25000
maxResponseBytes: 5000000
maxBodyChars: 200000
waitForSelector: ""
targetSelector: ""
removeSelector: ""
includeIframes: false
includeShadowDom: false
noCache: false
respectRobots: true
retainImages: none
retainLinks: all
upstreamProxy: none
locale: ""
userAgent: ""
research:
enabled: true
maxResults: 8
maxFetch: 8
defaultFetchTop: 3
maxCharsPerPage: 12000
maxTotalChars: 40000
concurrency: 3
timeoutMs: 120000
activation:
previousSearchProvider: ""
previousFetchProvider: ""
不要手改这个文件来配置插件。 设置页会处理校验、section 级 diff、密钥隔离和冲突检测。手改容易写坏 YAML,也可能被更高层 overlay 覆盖。
密钥不进入 YAML。插件通过 DSH credentials 读取:
| 凭据引用 | 用途 | 格式 |
|---|---|---|
SEARXNG_TOKEN | Bearer 认证 | token 原文 |
SEARXNG_BASIC_AUTH | Basic 认证 | username:password |
JINA_API_KEY | Jina Reader Bearer 认证(可选) | token 原文;自托管无认证时留空 |
设置页的密钥字段是 write-only:只显示“已配置/未配置”和来源,不回显值。测试使用已保存的密钥;未保存的输入不会发送。
SearXNG 搜索配置
| 字段 | 默认 | 范围/取值 | 说明 |
|---|---|---|---|
enabled | true | boolean | 关闭后该 adapter 不可用 |
instances | [] | 列表 | 实例按顺序尝试;每项有 url、enabled、label |
engines | [] | 逗号分隔 | 留空使用实例默认引擎;例如 google, bing |
categories | ["general"] | 逗号分隔 | 留空不发送该参数,使用实例默认;通常 general |
language | all | 字符串 | 例如 zh-CN、en-US |
safesearch | 1 | 0 / 1 / 2 | 安全搜索等级 |
timeRange | 空 | day / week / month / year | 时间范围 |
pages | 1 | 1–3 | 并发请求的分页数,结果合并去重 |
maxResults | 10 | 1–20 | 每次搜索最多返回条数 |
strategy | ordered | ordered / round-robin | 多实例顺序优先或轮询 |
method | GET | GET / POST | SearXNG 请求方法 |
format | auto | auto / json / html | auto 先 JSON 后 HTML |
auth.type | none | none / bearer / basic | 认证方式 |
requestTimeoutMs | 12000 | 1000–120000 | 单次 HTTP 请求超时 |
deadlineMs | 45000 | 1000–300000 | 整次搜索总预算,不能小于单次超时 |
retries | 1 | 0–5 | 每个实例的重试次数,只重试可重试错误 |
cacheTtlMs | 300000 | 0–86400000 | 搜索结果缓存 TTL;0 关闭 |
cacheMaxEntries | 200 | 0–10000 | 缓存最大条数;0 关闭 |
userAgent | 带版本号 | 非空、无换行 | 请求 UA |
proxy | 空 | http(s) URL | 代理;不能包含用户名/密码 |
includeDomains | [] | 域名列表 | 只保留匹配域名,支持子域名 |
excludeDomains | [] | 域名列表 | 排除匹配域名,支持子域名 |
行为说明:
- 多实例故障转移:一个实例失败会尝试下一个;全部失败才报错。
- 重试与预算:单实例重试受
retries和deadlineMs共同约束;预算耗尽后不再尝试新实例。429/5xx 的Retry-After(秒或 HTTP 日期)优先于指数退避,且同样不超过剩余 deadline。 - host 校验:搜索/抓取 dispatcher 在 live 配置、页面草稿、probe 和 preview 前调用 adapter 的
validate();非法数字、枚举、数组或跨字段配置返回SEARCH_CONFIG_INVALID/FETCH_CONFIG_INVALID,不会静默 clamp。 - 预览与 probe:Search 测试预览使用当前草稿的
maxResults;搜索 probe 使用requestTimeoutMs和配置的GET/POST方法。 - JSON / HTML:
auto先请求 JSON;遇到 403/415/406 或 JSON 解析失败时,同实例再请求一次 HTML。401 仍按认证失败处理,不会误判成格式问题。 - Search notes:SearXNG 的
unresponsive_engines会被保留为提示。看到 0 条结果时先看这里,可能是引擎被限流/CAPTCHA,而不是查询真的没有结果。 - 缓存键:包含查询、实例列表、引擎、分类、语言、安全搜索、时间范围、分页、策略、方法、格式、认证类型、代理、UA 和域名过滤;修改这些会自然失效。凭据轮换不会自动改键,建议清空缓存。
- 域名过滤:在结果进入
sources前过滤,支持example.com匹配www.example.com。 - 分页部分成功:一页失败不会丢弃其他成功页;失败页写入
notes,全部失败才报错。
Local Direct 搜索配置
本地边界: Select
Local Direct后,查询由 Host 进程直接发往启用的固定公开 HTML 端点(默认html.duckduckgo.com、www.bing.com;可选search.brave.com、www.so.com);解析、去重、RRF 融合、域名多样性和缓存都在本机完成。不需要 SearXNG、API key、Reader、浏览器、Docker 或第三方聚合服务。查询会离开本机;它不执行页面 JavaScript,不登录、不绕过付费墙或验证码。引擎 HTML 结构变化可能导致解析为空,此时会显式报错或写入notes,不会静默 fallback 到 SearXNG。
| 字段 | 默认 | 范围/取值 | 说明 |
|---|---|---|---|
enabled | true | boolean | 关闭后该 adapter 不可用 |
engines | ["duckduckgo","bing"] | duckduckgo / bing / brave / so360,至少一个 | 启用的公开 HTML 引擎;结果在本地融合。Brave 由 Firefox HTTP 指纹请求;360 面向中文市场 |
market | auto | auto / us-en / cn-zh / gb-en / jp-ja / de-de / fr-fr | 传给 DDG kl 和 Bing setlang/cc 的市场 |
safeSearch | moderate | off / moderate / strict | 映射到 DDG kp、Bing adlt 和 Brave safeSearch |
maxResults | 10 | 1–20 | 融合后返回条数上限 |
maxPerHost | 2 | 1–5 | 同一 hostname 最多保留条数,避免单站刷满 |
profile | chrome | chrome / firefox | Impit TLS/HTTP2 指纹;不是浏览器 |
requestTimeoutMs | 8000 | 1000–30000 | 单个引擎请求超时 |
deadlineMs | 15000 | 1000–60000 | 整次搜索总预算,不能小于单次超时 |
retries | 1 | 0–2 | 失败引擎的重试轮数 |
minIntervalMs | 1000 | 0–10000 | 每个引擎的最小请求间隔,避免并发搜索打爆引擎 |
cacheTtlMs | 300000 | 0–86400000 | 完整成功结果的缓存 TTL;0 关闭 |
cacheMaxEntries | 200 | 0–1000 | 缓存条数 |
maxResponseBytes | 2000000 | 65536–10485760 | 单引擎 HTML 上限;超限该引擎失败 |
proxy | 空 | http(s) URL | 可选代理;不能包含用户名/密码 |
includeDomains | [] | 域名列表 | 只保留匹配域名,支持子域名 |
excludeDomains | [] | 域名列表 | 排除匹配域名,支持子域名 |
行为与失败语义:
- 固定 origin,无用户可控 URL。 引擎 URL 由代码构造,只有查询、市场、安全搜索等参数变化;配置里没有自定义 endpoint。测试固定 origin,防止把 adapter 变成开放代理。
- 不跟随重定向。 Impit 使用
followRedirects:false+redirect:'manual';3xx 视为该引擎失败,不会跟随到未知 host。 - 无 Cookie / Authorization / JS。 每次搜索新建一个 Impit worker;不发送登录态,不执行页面脚本;worker 在成功、失败、abort、deadline 后都会终止。
- 本地 RRF。 每个引擎按排名贡献
1/(60+rank+1);同 URL 合并更长标题/摘要;再按 hostname 多样性截断。查询词不参与第二阶段 LLM 重排。 - 部分成功。 一个引擎失败时返回另一个引擎的结果,并在
notes写明失败引擎与原因;全部失败抛LOCAL_DIRECT_ALL_FAILED,不会自动 fallback 到 SearXNG/Jina。若 200 页面的结果全部被includeDomains/excludeDomains过滤掉,该引擎仍算成功(空结果 + note),不会误报全部失败。 - 缓存只写完整成功。 只有所有启用引擎都成功、没有失败 note、且至少有结果时才写缓存;部分成功/空结果不缓存,下一次会重试失败引擎。
- 探测。 Search 页的 测试搜索引擎 使用固定查询
dsh-search-tools probe,不写缓存;返回可达引擎列表。 - 不解析广告。 DDG 的
result--ad和 Bing 的li.b_ad(即使同时带b_algo)广告项会被丢弃;DDGuddg和 Bingck/a?u=a1...的跳转链接在本地解包为真实结果 URL。 - 引擎可用性不是 SLA。 每个引擎都可能限流、返回 202/403 或改版;这是公开 HTML 路径的固有风险。出现空结果时先看
notes和 测试搜索引擎,必要时切回 SearXNG。
Readability HTTP 抓取配置
| 字段 | 默认 | 范围 | 说明 |
|---|---|---|---|
enabled | true | boolean | 关闭后该抓取 adapter 不可用 |
readability | true | boolean | 是否用 Readability 提取正文 |
minArticleChars | 200 | 0–100000 | 低于此长度不视为文章正文 |
maxArticleChars | 200000 | 1000–5000000 | 提取后正文的文本最大字符数,不是 HTML 长度;不能小于 minArticleChars,也不能大于 maxBodyChars |
maxResponseBytes | 5000000 | 1024–50000000 | 响应体最大字节数 |
maxBodyChars | 2000000 | 1024–20000000 | 解码后最大字符数,不能小于正文下限 |
timeoutMs | 20000 | 1000–120000 | 抓取超时 |
maxRedirects | 5 | 0–10 | 最大重定向次数 |
userAgent | 带版本号 | 非空、无换行 | 请求 UA |
failOpen | true | boolean | 正文提取失败时回退到原始内容 |
readable-http 的行为:
- 复用 DSH 的安全 HTTP provider,目标是阻止 SSRF 和访问内网地址;
- 只处理
http(s)URL; - 只解析文本/HTML;PDF、图片、二进制不会变成正文;
- 不执行 JavaScript,因此纯前端渲染页面可能抓不到实际内容;
maxArticleChars限制的是提取后的正文文本,不是原始 HTML;truncated由提取器返回,短正文配长 HTML 不会再误报;- 需要截断 HTML 时按块级节点处理;无法安全截断时退回纯文本,保证不会返回半截标签;
- Readability 失败且
failOpen=true时返回原始文本/HTML,而不是让整个抓取失败; readable-http的/probe会真实发起一次抓取并返回 HTTP 状态/内容类型。
Wire HTTP 抓取配置
本地边界:
wire-http在本地通过 loopback 出口守卫抓取,不把目标 URL 发给第三方 Reader,也不需要 API key、浏览器、Docker 或系统库。它使用impit的 Chrome/Firefox TLS/HTTP2 指纹;不执行页面 JavaScript,所以 SPA 仍应使用jina-reader。它不会登录、绕过付费墙或破解验证码。
| 字段 | 默认 | 范围/取值 | 说明 |
|---|---|---|---|
enabled | true | boolean | 关闭后该 adapter 不可用 |
profile | chrome | chrome / firefox | 浏览器 TLS/HTTP2 指纹;不是浏览器引擎 |
timeoutMs | 25000 | 1000–180000 | worker 与出口守卫的硬超时;默认低于 DSH web_fetch 的 30 秒工具超时 |
maxRedirects | 5 | 0–10 | 每一跳重定向都重新经过公网出口守卫 |
locale | 空 | BCP 47 | 可选 Accept-Language,例如 zh-CN |
maxResponseBytes | 5000000 | 1024–50000000 | 原始响应字节上限;超限显式失败 |
readability | true | boolean | 用 Readability 提取 HTML 正文;关闭时返回原始 HTML |
minArticleChars | 200 | 0–100000 | 正文最小字符数 |
maxArticleChars | 200000 | 1000–5000000 | 提取后正文文本上限;不能小于下限,也不能大于 maxBodyChars |
failOpen | true | boolean | 提取失败时回退原始 HTML;关闭时返回 WIRE_UNREADABLE |
pdf | true | boolean | 本地 PDF 文本提取 |
maxPdfPages | 10 | 1–20 | 只读前 N 页;超过上限标记 truncated |
maxBodyChars | 2000000 | 1024–20000000 | 解码后字符上限,HTML/文本/PDF 共用 |
行为与失败语义:
- 初始目标先解析并校验,随后每一跳由本地出口代理重新解析;任何 DNS 答案包含私网/loopback/link-local/CGNAT/ULA/IPv4-mapped/6to4/Teredo/已知 NAT64 前缀(
64:ff9b::/96、64:ff9b:1::/48)都会被拒绝;网络专用 NAT64 前缀无法在通用运行时中穷举,仍被文档标为残余边界; - 只允许
http(s)、80/443,不允许 URL 凭据;代理连接到已验证的 IP,不会再次解析主机名; impit运行在单次请求 worker 中,成功、失败、取消或超时都会终止 worker;原生 client 不会跨请求残留;- 响应按
Content-Type/magic 分流:PDF → 本地 PDF.js 文本;HTML → Readability;文本/JSON → 直接文本;二进制 →WIRE_UNSUPPORTED_CONTENT; - 2xx 空 body 返回
WIRE_EMPTY_BODY;4xx/5xx 的空 body 保留状态码;PDF 无文本层返回WIRE_PDF_UNREADABLE,损坏 PDF 返回WIRE_PDF_INVALID; - 不自动 fallback 到 Jina 或 readable-http,避免把被本地守卫拒绝的 URL 静默发给第三方;
- 不需要
JINA_API_KEY;设置页不显示 secret 字段。
Jina Reader 抓取配置
数据边界:
jina-reader不是readable-http的替代默认值。托管https://r.jina.ai/时,目标 URL 会发给 Jina;只有自托管 Reader 才把页面留在本机。Reader 可能缓存页面,需要新鲜内容时打开noCache。它不会登录、绕过付费墙或破解验证码。
| 字段 | 默认 | 范围/取值 | 说明 |
|---|---|---|---|
enabled | true | boolean | 关闭后该 adapter 不可用 |
endpoint | https://r.jina.ai/ | http(s) URL | 托管服务或自托管 Reader;远程必须 HTTPS,http 仅允许 loopback |
engine | auto | auto / browser / curl | browser 强制无头浏览器;curl 使用轻量 HTTP,适合静态页 |
responseMode | markdown | markdown / html / text | html 会映射为 body.kind=html,交给本地 Readability |
timeoutMs | 25000 | 2000–180000 | 本地硬超时;发给 Reader 的 X-Timeout 会少 1 秒作为等待预算,最小值 2000 保证它仍短于本地截止。超过 30000 还需调高 DSH tool-web.fetchTimeoutMs |
maxResponseBytes | 5000000 | 1024–50000000 | Reader 返回的 JSON 包体上限;超限返回 JINA_RESPONSE_TOO_LARGE |
maxBodyChars | 200000 | 1024–20000000 | 按响应模式截断 data.content / data.html / data.text,并置 truncated=true |
waitForSelector | 空 | CSS 选择器 | 等元素出现后再提取 |
targetSelector | 空 | CSS 选择器 | 只提取匹配内容 |
removeSelector | 空 | CSS 选择器 | 提取前移除导航/广告等 |
includeIframes | false | boolean | X-With-Iframe: true |
includeShadowDom | false | boolean | X-With-Shadow-Dom: true |
noCache | false | boolean | X-No-Cache: true,要求实时抓取 |
respectRobots | true | boolean | X-Robots-Txt: dsh-search-tools |
retainImages | none | none / all | 默认移除图片,省 token |
retainLinks | all | none / all | 是否保留链接 |
upstreamProxy | none | none / auto | auto 需要 Reader 支持且通常需要 API key |
locale | 空 | BCP 47 | 例如 zh-CN;控制 Reader 浏览器 locale |
userAgent | 空 | 字符串 | 作为 X-User-Agent 发给目标页;空则 Reader 默认 |
行为与错误:
- 请求固定为
POST <endpoint>/,Content-Type: application/x-www-form-urlencoded,body 为url=<目标>;Accept: application/json,并带X-Respond-With、X-Timeout等头。Markdown 请求markdown;HTML/text 请求<mode>,content,因为 Jina 把目标站错误warning 放在 content 分支。 - 目标 URL 在发出前经过公网目标守卫:只允许
http(s)、禁止 URL 内凭据、拒绝 loopback/私有/链路本地地址及 IPv4-compatible 等过渡地址,并要求 DNS 解析结果全部为公网地址。错误码为JINA_INVALID_TARGET/JINA_TARGET_BLOCKED/JINA_TARGET_UNRESOLVED。 - 401/403 →
JINA_AUTH;429 →JINA_RATE_LIMIT(有Retry-After时带retryAfterMs,最多 1 小时);5xx/其他 →JINA_HTTP;网络失败 →JINA_NETWORK;超时 →JINA_TIMEOUT;非法 JSON/缺少当前模式的字段(data.content、data.html或data.text)→JINA_BAD_RESPONSE(若目标状态已是非 2xx,则返回空正文并保留状态码);超限 →JINA_RESPONSE_TOO_LARGE。 - Reader 返回的目标页 404/5xx 优先从
data.warning恢复,其次读官方data.httpStatus;HTML/text 不再附加 content companion,直接依赖这两个官方字段。可选的data.statusCode作为自托管兼容字段最后兜底,且 warning/httpStatus 优先;只有三者都缺失时才无法恢复目标状态。 - 残余风险: Reader 服务端会重新解析目标 DNS,并可能跟随目标页重定向/浏览器跳转;本插件无法像
HttpFetchProvider那样 pin 住连接,所以这是 best-effort 守卫,不是 DSH 级别的 SSRF 保证。自托管时请限制 Reader 容器的网络出口。 - HTML 模式的
maxBodyChars截断优先落在完整标签边界,但仍可能留下不完整文档;需要 token 预算时优先用 Markdown/text。 - 自托管: Jina Reader 开源版可运行在本机/内网;把
endpoint设为http://127.0.0.1:3000/(或 HTTPS 反代)。托管 Jina 的 API key 只发给你配置的 endpoint,并保存在 DSH credentials。
web_research 配置
| 字段 | 默认 | 范围 | 说明 |
|---|---|---|---|
enabled | true | boolean | 关闭后模型看不到该工具 |
maxResults | 8 | 1–20 | 返回来源数上限,也是 max_results 的上界 |
maxFetch | 8 | 1–10 | 抓取页数上限,也是 fetch_top 的上界 |
defaultFetchTop | 3 | 0–10 | 模型未指定 fetch_top 时的默认抓取数;不能大于 maxFetch |
maxCharsPerPage | 12000 | 1000–200000 | 单页字符上限,也是 max_chars 的上界 |
maxTotalChars | 40000 | 1000–500000 | 所有页面合计字符预算;不能小于单页上限 |
concurrency | 3 | 1–8 | 并发抓取数 |
timeoutMs | 120000 | 5000–600000 | 工具超时预算 |
web_research 会把模型参数夹到这些边界内,并按 maxTotalChars 在多个页面之间分配剩余预算。defaultFetchTop > maxFetch 或 maxCharsPerPage > maxTotalChars 会在执行前返回 RESEARCH_CONFIG_INVALID。某个页面抓取失败不会让整次调用失败;失败页会带 error 字段,卡片仍会显示来源和成功抓取数。
超时字段说明:
timeoutMs同时进入工具注册的声明超时和runResearch的内部 signal;到点后搜索/抓取会被取消并返回WEB_RESEARCH_TIMEOUT,不再只是提示词里的展示值。
接管配置
接管不是必选项。
| 配置 | 作用 |
|---|---|
web.searchProvider = dsh-search-tools | 内置 web_search 使用本插件搜索 provider |
web.fetchProvider = dsh-search-tools-fetch | 内置 web_fetch 使用本插件抓取 provider |
activation.previousSearchProvider | 开启接管前记录的搜索 provider,关闭时恢复 |
activation.previousFetchProvider | 开启接管前记录的抓取 provider,关闭时恢复 |
规则:
- 开启搜索接管前,页面会先用已保存配置跑一次搜索测试;失败则不接管。
- 抓取接管的开关不强制先测试;建议先在 抓取 页手动测试一个公网页面。
- 关闭接管时,如果之前没有记录 provider,会 unset 对应配置,回到 DSH 默认。
- 停用/卸载 bundle 前先关闭接管;插件在插件详情页会显示接管警告和“关闭接管”入口。
模型工具
内置 web_search / web_fetch
这两个是 DSH 自带工具。插件不重新发明它们,只通过稳定 provider 接管底层实现:
- 不开接管:它们继续用 DSH 原来的 provider,本插件不影响;
- 开接管:请求进入本插件的 Dispatcher,再路由到当前 adapter;
- 关闭接管:恢复开启前记录的 provider。
这样模型看到的工具名、参数和权限模型都不变。
web_research
插件注册的模型工具,用于“我要页面内容,而不只是链接”的场景。
参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
query | string | 是 | 搜索查询 |
max_results | number | 否 | 返回来源数,受配置 maxResults 限制 |
fetch_top | number | 否 | 抓取前 N 条;0 表示只返回来源 |
max_chars | number | 否 | 单页字符预算,受配置 maxCharsPerPage 限制 |
返回结构:
{
"query": "example",
"sources": [
{ "url": "https://example.com", "title": "Example", "snippet": "..." }
],
"fetched": [
{
"url": "https://example.com",
"title": "Example",
"content": " readable markdown ...",
"truncated": false
}
],
"truncated": false
}
行为:
- 搜索走当前
ctx.web.search;抓取走当前ctx.web.fetch; - 抓取前
fetch_top条结果,按concurrency并发; - HTML 页面会尝试 Readability 提取,再转 Markdown;纯文本直接使用;
- 单页按
max_chars截断,多页共享maxTotalChars预算; - 单页失败带
error,不影响其他页; - 会话卡片显示来源、成功抓取数、失败数、字符数和截断标记;
- 外部页面内容是不可信数据,不是指令。模型和用户都不应执行页面里的“指令”。
安全模型
- 设置路由有认证栅栏。 所有
/api/dsh-search-tools/*路由都经过 DSH 的connection.requestRejection,未认证请求返回 401/403。 - 请求体有上限。 JSON body 限制 64 KiB,非 JSON 返回 415。
- SSRF 边界按 adapter 区分。
readable-http复用 DSH 的HttpFetchProvider,内网/保留地址会被拒绝;wire-http在本地 loopback 出口代理中对每个 CONNECT/HTTP 目标和每一跳重定向重新解析 DNS,要求所有答案都是公网,并连接到已验证的 IP(连接级 pinning);jina-reader在发出前做公网目标守卫,但 Reader 服务端会重新解析 DNS,并且可能跟随目标页重定向/浏览器跳转,因此不能提供与 DSH 相同的连接级 pinning。托管 Jina 时目标 URL 会离开本机;自托管时页面留在 Reader 服务内,但仍应限制 Reader 容器的网络出口。 - Wire HTTP 不执行页面 JavaScript。 它模仿浏览器 TLS/HTTP2 协议指纹,不是浏览器;SPA 请用
jina-reader。PDF 只在本地用 PDF.js 提取文本,isEvalSupported: false,不执行 PDF 内嵌脚本。 - Wire HTTP worker 没有持久凭据。 不发送 Cookie、Authorization 或登录态;不登录、不绕过付费墙、不破解验证码。
- Reader 不是匿名代理。 托管 Reader 可能缓存页面,
noCache只要求实时抓取;自托管也不能绕过登录、付费墙或验证码。 - 搜索重定向不跟随。 SearXNG 请求使用
redirect: 'error',认证头不会跟随跨源重定向。Local Direct 使用followRedirects:false+redirect:'manual',3xx 直接视为该引擎失败。 - 搜索出口按 adapter 区分。
searxng把查询发给用户配置的实例(可私有/loopback);local-direct只把查询发给启用的固定公网 origin(默认 DuckDuckGo/Bing,可选 Brave/360),没有用户可控 endpoint,不发送 Cookie/Authorization,也不执行 JS。web_research的抓取阶段仍只接受公网页面;搜索结果中的本地/私有 URL 不会被 fetch adapter 读取。 - 凭据不落 YAML。 Bearer/Basic/Jina API key 写入 DSH credentials;YAML 只保存非敏感配置。诊断文本会脱敏,不包含 token。
- 代理不带凭据。
proxy必须是http(s)URL,且不能包含用户名/密码,避免把代理凭据写进配置。 - 错误信息有限。 实例/目标主机名可能出现在诊断中,但查询字符串和凭据会被去掉。
- 无遥测。 插件不向作者发送使用数据;SearXNG 搜索请求只发往你配置的实例,Local Direct 请求只发往启用的固定公网引擎,
jina-reader请求只发往你配置的 Reader endpoint(托管 Jina 时该 endpoint 是第三方)。Local Direct 的查询文本会到这些引擎,不会发给本站或其他聚合服务。
安全边界仍然取决于 DSH 本身和你的部署。不要把设置页暴露在不可信网络;不要在共享 profile 中把密钥当公共配置。
适配器开发
外部 adapter contract 在 0.1.x 标记为实验性。接口可能在小版本中调整;依赖它时请锁定
dsh-search-tools版本并做集成测试。
一个 adapter 包大致做三件事:
- 依赖
ctx.searchTools服务; - 提供 manifest(设置页渲染用)和 runtime(实际搜索/抓取);
- 用
ctx.effect注册,确保卸载时自动清理。
import type { Context } from '@deepseek-ai/cordis'
export const inject = ['searchTools']
export function apply(ctx: Context): void {
ctx.effect(
() => ctx.searchTools.registerSearchAdapter(myAdapter),
'my-search-adapter',
)
}
myAdapter 的结构:
const myAdapter = {
manifest: {
kind: 'search',
id: 'my-search',
label: { en: 'My Search', zh: '我的搜索' },
description: { en: '...', zh: '...' },
defaults: { endpoint: 'https://search.example.com' },
capabilities: ['json', 'probe'],
groups: [
{ id: 'connection', label: { en: 'Connection', zh: '连接' } },
{ id: 'advanced', label: { en: 'Advanced', zh: '高级' }, advanced: true },
],
fields: [
{ kind: 'text', key: 'endpoint', label: { en: 'Endpoint', zh: '地址' }, group: 'connection' },
{ kind: 'number', key: 'timeoutMs', label: { en: 'Timeout', zh: '超时' }, group: 'advanced', unit: 'ms' },
],
},
validate(rawConfig) {
return { ok: true, issues: [] }
},
create(ctx, getConfig) {
return {
available: () => true,
async search(request, signal) {
return { sources: [], truncated: false }
},
}
},
}
约定:
- 搜索 adapter 的
id必须唯一;kind必须是search或fetch。 - 配置存在
config.search.adapters.<id>(或fetch.adapters.<id>),adapter 自己 normalize/validate。 fields支持text、url、password、number、boolean、select、tags、checkboxes、instances;可选group、advanced、unit只影响展示,不改变配置结构。checkboxes声明固定options,用default给出多选默认值。groups可选;不声明时页面渲染单个 Configuration 分组。字段的group不存在时回退到第一个分组,advanced: true的字段自动进入折叠区。- 需要密钥时用
secretRefs声明引用,值走 DSH credentials,不要写进 manifest defaults;secret 也可声明group/advanced。 - 新增 adapter 不需要改设置页代码;页面按 manifest 渲染,声明 groups 后自动获得分组与折叠。
- v0.1.x 不支持 adapter 自带完全自定义的设置 UI;如有需要,可以未来注册自己的
settings.section,但那就不再是统一页面的一部分。 - 外部 adapter 的本地化目前以 manifest 的
label/description原文为准;内置 adapter 由插件字典提供中英文。
ctx.effect 返回 disposer;不要在 apply 外保存全局注册状态。重复注册同一 id 会被拒绝。
本地开发
pnpm install --frozen-lockfile --registry=https://registry.npmjs.org
pnpm typecheck
pnpm test
pnpm build
pnpm release
pnpm build 产出:
lib/index.js Host 半侧
lib/wire-worker.js wire-http 单次请求 worker
lib/client.js 浏览器 lazy-CJS factory
lib/types/** TypeScript 声明
测试与检查:
| 命令 | 内容 |
|---|---|
pnpm typecheck | TypeScript 类型检查 |
pnpm test | Vitest:Host 单测、设置页/研究卡片组件测试、pipeline、publint 策略 |
pnpm coverage | 同上 + V8 覆盖率门禁;statements / branches / functions / lines 均不得低于 85% |
pnpm build | Host、浏览器、类型声明 |
pnpm pack:check | build + pnpm pack --dry-run + publint |
pnpm release | typecheck + coverage + pack:check |
CI 在 Ubuntu/Windows × Node 22/24 上跑 typecheck、coverage、build、pack dry-run、publint。publint 只忽略 lib/client.js 的 FILE_INVALID_FORMAT:DSH 把它当浏览器 CJS 工厂加载,不是 Node ESM 入口;其他诊断仍然失败。
项目结构
src/core/ adapter 类型、注册表、稳定 dispatcher、诊断
src/search/adapters/ SearXNG + Local Direct adapter(manifest/schema/parser/fusion/worker)
src/fetch/adapters/ Readability HTTP / Wire HTTP / Jina Reader adapters
src/research/ web_research pipeline、prompt、render、tool
src/host/ 认证的设置路由
src/client/ 设置 → 搜索工具 + 研究卡片
src/shared/ config、校验、diff、URL、公网目标守卫、限制
tests/ Host 单测 + 设置页/研究卡片组件测试
docs/ 架构、设计、M0 验收、发布清单
为什么用 linkedom 而不是 jsdom
Readability 运行在 Host 内。jsdom 的 tr46 依赖会调用 createRequire(...).resolve.paths,而 DSH 的 profile resolution hook 没有实现它;linkedom 更小,也能在 Host 内工作。jsdom 只作为组件测试的 dev 依赖。
发布流程
发布前确认:
package.json版本是0.1.9;CHANGELOG.md有对应条目;pnpm release全绿;- 工作区干净;
- npm 认证可用,或仓库配置了
NPM_TOKENsecret。
本地检查:
pnpm install --frozen-lockfile --registry=https://registry.npmjs.org
pnpm release
pnpm pack --pack-destination dist
打 tag 并推送:
git tag -a v0.1.9 -m "dsh-search-tools v0.1.9"
git push origin main --follow-tags
推送 v* tag 会触发 .github/workflows/release.yml:
- 在 Ubuntu/Windows × Node 22/24 上先跑完整验证矩阵(含
check:package); - 校验 tag 名与
package.json版本一致,且 tag commit 在main上; - 重复
pnpm release; - 检查该版本是否已发布,已存在则跳过;
- 用
NPM_TOKEN发布到 npm(provenance); - 把 tarball 附加到 GitHub Release,并使用
docs/release-v<version>.md作为发布说明。
手动发布(不产生 provenance;正式发布走 tag workflow):
npm publish --access public
发布后做一次全新 profile 冒烟:安装、启用、分别用 local-direct 和 searxng 做搜索测试、分别用 readable-http、wire-http 和 jina-reader 做抓取测试、一次 web_research 调用。详细清单见 v0.1.9 发布清单。
故障排查
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 设置页没有“搜索工具” | bundle 未启用;DSH 版本不匹配;客户端加载失败 | 在插件页启用;确认 DSH 在 >=0.2.1-rc.2 <0.3.0;看浏览器控制台/插件日志 |
| 搜索测试 401/403 | 实例需要认证;凭据未保存;Basic 格式不是 username:password | 在认证字段选择正确类型,保存密钥后重试;确认 Source: env 是否符合预期 |
| 搜索测试 429 | 实例/引擎限流 | 降低频率、换实例或引擎、增加缓存 TTL,或改用其他后端 |
| Local Direct 全部失败 | 网络无法访问引擎;引擎返回 202/403/429;解析为空 | 点 测试搜索引擎 看可达性;换网络/代理;稍后重试;必要时切回 SearXNG |
| Local Direct 结果偏少 | 只启用了单引擎;每站上限;引擎只返回首页 | 多启用一个引擎;提高 maxPerHost/maxResults;看 Search notes |
| 返回 0 条但测试“成功” | SearXNG 默认引擎被限流/CAPTCHA | 看 Search notes;指定 engines(例如 bing)或换实例 |
| JSON 解析失败 | 实例未启用 JSON 格式 | 在 SearXNG 的 search.formats 加 json;或把格式设为 html |
web_research is disabled | 研究工具关闭 | 设置 → 研究 → 启用 web_research 并保存 |
| 研究只返回来源、不抓正文 | fetch_top=0;抓取失败;目标页面不可读 | 提高 fetch_top;看抓取详情的 error;手动在抓取页测试 URL |
| 内网 URL 抓取失败 | readable-http 的 DSH SSRF 防护;jina-reader 的公网目标守卫 | 预期行为;不要尝试绕过 |
| Jina 抓取返回 401/403 | API key 未配置/无效;匿名 IP 被限流 | 在设置页保存 JINA_API_KEY;或改用自托管 Reader |
| Jina 抓取返回 429 | Reader 限流 | 稍后重试、降低频率、配置 key/配额,或改回 readable-http |
| Jina 超时 | Reader 浏览器渲染慢;DSH web_fetch 30 秒工具超时先到 | 降低 timeoutMs 到 25 秒内,或调高 DSH tool-web.fetchTimeoutMs |
| 接管后内置搜索报错 | bundle 被停用/卸载但接管仍指向插件 | 重新启用插件,或关闭接管;设置页/插件详情页有恢复入口 |
| 配置保存冲突 | 配置在别处被修改;更高层覆盖 | 点 重新加载;检查 profile 的 cordis.patch.yml 和更高层 overlay |
| 配置只读 | 被更高层配置覆盖 | 页面顶部会说明;到覆盖层修改,或移除覆盖 |
| 实例 URL 重复 | 规范化后相同 | 去掉重复实例;URL 会去掉末尾 /search、query、hash |
| 代理配置报错 | 不是 http(s),或包含用户名/密码 | 改成无凭据的 http(s)://host:port |
| 缓存结果不更新 | TTL 未到 | 在诊断页清空缓存,或等 TTL 过期 |
安装后没有 web_research | 研究工具关闭;模型未刷新工具列表 | 启用并保存;开新会话或重新加载页面 |
| npm 安装失败 | registry/网络/peer 不匹配 | 检查 DSH 版本、Node 版本、npm registry;用 tarball 离线安装 |
诊断顺序建议:
- 看 设置 → 诊断 → 最近错误;
- 看 Search notes(引擎级错误);
- 用 测试 复现,不要只看模型回合;
- 确认是否开启了接管,以及当前 provider 是哪一个;
- 最后再查 DSH 日志和实例日志。
已知限制
- 内置两个搜索 adapter 和三个抓取 adapter。 搜索默认仍是
searxng;local-direct是零部署的本地直连后端,默认查询 DuckDuckGo/Bing,并可选用 Brave Search / 360 公开 HTML,不执行 JS,也不保证引擎永不改版/限流。抓取默认readable-http;wire-http是本地浏览器级 HTTP + PDF 后端;jina-reader是可选 Reader 后端。0.1.x 不附带 Google/Exa 等需要 API key 的商业搜索后端;Brave/360 走公开 HTML 页面,不需要 API key。 - 没有自动更新。 升级需要卸载再安装。
- 外部 adapter contract 是实验性的。 0.1.x 可能调整;生产集成请锁版本。
- 浏览器渲染只属于
jina-reader。readable-http和wire-http都不执行 JS;wire-http强化的是 TLS/HTTP2 指纹、出口 pinning 和 PDF,不是浏览器引擎。Jina 需要公网 Reader(托管或自托管),且不处理登录态、付费墙或验证码。SPA 能渲染不等于能登录。 readable-http只处理文本/HTML。 PDF、图片、音视频不会转成正文;wire-http可本地提取 PDF 文本;需要 Office/浏览器渲染时用jina-reader。Jina 的 PDF/Office 转换取决于 Reader 服务能力。web_research不是爬虫。 只抓前 N 条结果,不做站点遍历、去重图谱或深度抓取。- 研究提示词预览可复制。 工具描述保持英文,与模型实际看到的内容一致;预览数值跟随当前草稿。
- 接管是全局 provider 切换。 它会影响该 DSH profile 中所有使用
ctx.web的调用,不只本插件。 - 缓存是进程内内存。 重启后丢失;多进程不共享。
- DSH preview 兼容范围窄。 当前锁在
0.2.1-rc.2线,升级 DSH 前先跑回归。
许可证与第三方组件
本项目使用 MIT 许可证,见 LICENSE。
第三方组件与许可证说明见 THIRD_PARTY_NOTICES.md。主要运行时依赖包括 @mozilla/readability、linkedom、cheerio、turndown、@joplin/turndown-plugin-gfm、undici、lru-cache、normalize-url、ipaddr.js、impit(Apache-2.0,平台原生二进制)和 pdfjs-dist(Apache-2.0)。jina-reader 只调用用户配置的 Reader 服务,不打包 Jina 的代码;自托管 Reader 的许可证与运行要求由 Reader 项目提供。wire-http 不打包浏览器、Reader 或 Jina 代码。
如果这个插件对你有帮助,欢迎在 GitHub 提 issue 或 discussion。安全问题请不要公开贴 token、实例密码或完整配置。