dsh-web-search-lite
Identified★ 1Keyless Web Search for DeepSeek Harness: Aggregates four search engines and reads full web page content. It also allows switching the built-in web_search from the official pay-per-use search to free engines.
dsh-web-search-lite
给 DeepSeek Harness 装一套联网搜索:模型可以调用搜索引擎查当前信息,也可以打开搜索结果里的网页读正文。
装好就能用,不需要任何 API Key。默认同时查询 DuckDuckGo、Bing 网页、百度、360 搜索四家,再合并去重:单看一家的排序只是一种意见,而四家分歧最大的,恰好就是值得搜的那些查询。愿意配一家搜索 API 的话,可以单独选它,结果更稳更快。
插件提供的是两个新工具(web_search_api、web_fetch_api),不接管、不修改 DSH 自带的 web_search。内置搜索怎么配、用哪个提供方,仍然由官方的「插件 → 网页搜索」页管。
换引擎、填密钥、调条数都在 设置 → 网络搜索 里,保存后下一条消息生效,不用重启。设置页还有一个测试连接按钮,用已保存的配置跑一次真实查询。
它给模型什么
| 工具 | 作用 |
|---|---|
web_search_api | 一次搜索,返回排序后的来源列表(标题、URL、摘要、发布时间)。默认走全部引擎聚合;支持临时指定 provider、language、maxResults,不写入设置。 |
web_fetch_api | 把一个 URL 读成纯文本(去掉标签、脚本和样式),用于打开搜索命中的网页。设置页有开关可以关掉它。 |
两个工具出错时都会说清下一步:抓取型被反爬会建议换引擎或配 API Key,解析不到结果会说明可能是引擎改了页面结构,401/403 提示密钥无效,429 提示限流或额度用尽,超时提示调大超时,而不是只抛一个状态码。
支持的引擎
免密钥(开箱即用)
provider | 引擎 | 怎么拿结果 |
|---|---|---|
all | 全部引擎(默认) | 四个一起查,轮转合并 + 按 URL 去重 |
duckduckgo | DuckDuckGo | 读 html.duckduckgo.com 结果页,uddg 参数还原真实链接 |
bingweb | Bing 网页 | 读 bing.com/search 结果页,ck/a 跳转里的 base64 还原真实链接 |
baidu | 百度 | 读 baidu.com/s 结果页,容器的 mu 属性取真实链接,过滤推广位 |
so360 | 360 搜索 | 读 so.com/s 结果页,data-mdurl 取真实链接,过滤广告位 |
searxng | SearXNG(自建) | 调用实例的 format=json 接口,必须填服务地址;配了地址后也会加入 all |
provider 默认 all。
聚合模式与 all
aggregate(聚合模式,默认)和 all 用的是同一套合并逻辑,区别只在查哪些引擎:
| 模式 | 查什么 |
|---|---|
aggregate 聚合模式 | 四家免密钥引擎 + SearXNG(配了地址时)+ 所有填了 Key 的 API 提供方 |
all 免密钥聚合 | 只有免密钥那几家,永远不碰 API |
没填 Key 的 API 会被跳过而不是硬调:调用它们只会拿到 401,让合并结果看起来像是坏了。所以聚合模式在零配置时和 all 完全一样,配了 Key 之后才多出那几家。
密钥按提供方分开存(apiKeys),所以可以同时配 Tavily 和 Brave,聚合模式会一起调——这也是把单个 apiKey 改成 apiKeys 的原因。设置页在聚合模式下会列出每一家的输入框。
query
├─ DuckDuckGo ─┐
├─ Bing 网页 ─┤ 并发,各自取 max(count,6) 条
├─ 百度 ─┤
└─ 360 ─┘
↓
轮转交错(每个引擎轮流贡献一条)
↓
URL 去重(忽略 utm_* / spm / fbclid 等跟踪参数、末尾斜杠、#fragment)
↓
截断到 maxResults
几个刻意的选择:
- 轮转,不拼接。直接首尾相接的话,谁先返回、谁返回得多,谁就霸占列表前面。轮转让每家的最佳结果都进前几位。
- 去重保留先访问到的那一份。四个引擎返回的热门页面大量重合,不去重的话列表会被同一批链接占满。
- 用
allSettled,不用all。一家被限流或改版是常态,不能让它拖垮整次搜索。成功的照常合并,失败的按引擎报告原因。 - 部分失败会写进结果。工具输出里有
来源:DuckDuckGo 8 · Bing 网页 8 · 百度 8和未返回:360 搜索(…),模型知道哪家没答,而不是以为结果本来就少。
一次实测(查「DeepSeek Harness 怎么安装」,maxResults: 8):
耗时 2685 ms · 合并后 8 条
引擎分布: DuckDuckGo 8 · Bing 网页 8 · 百度 8 · 360 搜索(失败)
耗时接近最慢的那家,因为四家是并发查的。返回的 8 条覆盖了 runoob、CSDN、知乎、deepseek.com 等不同来源,单用一家通常拿不到这么分散。
需要 API Key
provider | 服务 | 密钥申请 | 备注 |
|---|---|---|---|
tavily | Tavily | https://app.tavily.com/home | 返回网页正文,可带生成式摘要 |
serper | Serper(Google 结果) | https://serper.dev/api-key | 结果最接近 Google,可带 answerBox |
brave | Brave Search | https://api.search.brave.com/app/keys | 独立索引 |
exa | Exa | https://dashboard.exa.ai/api-keys | 语义搜索,直接返回正文片段 |
bing | Bing Web Search API | https://portal.azure.com/ | 在 Azure 里创建 Bing Search 资源 |
加一家新的:在 index.js 的 PROVIDERS 加一条描述,在 SEARCHERS 加一个把该家响应映射成 { answer?, sources[] } 的函数。去重、截断、错误文案、渲染都是共享的。
关于「抓取型」引擎
免密钥引擎没有官方接口,插件读的是它们公开的结果页,和用浏览器打开那个页面看到的一样。这意味着:
- 不用注册、不用付费、不会被额度卡住,开箱即用。
- 页面结构属于引擎。它们改版、加反爬,或者对自动化请求返回验证页时,解析就会失败。失败时工具会明确报告「没有解析出结果」或「被拒绝访问」,并建议换引擎,而不是假装「没有搜索结果」。
- 抓取时带上桌面浏览器的 User-Agent 和
Accept-Language,因为用插件自己的标识去请求会被直接挡掉;API 请求仍然如实标注身份。 - 向引擎要的结果数比实际需要的多(
count × 3,至少 12 条),因为结果页里混着答案卡片、图片卡、推广位,能放进来源的通常不到一半。
实测这几个引擎在你当前网络下都可用(DuckDuckGo 约 2s,其余 0.4–1.5s)。试过但没能用的:Mojeek(直接返回验证码页)、搜狗(返回反爬验证页)、Ecosia(403 防火墙)、Brave 网页版(429)。这些没放进插件,免得选了就用不了。
有一个例外要说清楚:360 搜索对密集请求很敏感。正常使用(几分钟一次)没问题,但短时间内连续搜十几次会开始返回「访问异常页面」。这是 HTTP 200 的拦截页,不是搜索结果。插件会认出它并报「返回了验证/拦截页面,通常是请求过多」,而不是含糊地说「解析失败」。聚合模式下它挂了不影响其他三个,结果里会写 未返回:360 搜索(…)。
需要稳定、可预期的结果时(比如每天固定跑几十次查询),还是建议配一家 API。
安装
先确认装的是哪一个
npm 上有一个同类的 dsh-web-search-pro(作者 anweat),功能是另一套实现:多引擎路由 + SQLite 缓存 + Playwright 渲染,并依赖 @anweat/dsh-browser。本包叫 dsh-web-search-lite,两者没有关系。
只看代码不装包的话,按仓库名找:仓库、npm 包、文档里的名字都是 dsh-web-search-lite。只有组合行 id 还是 web-search-pro(它同时是设置目录名和接口前缀,改了会让已有配置失效)。
所以如果你在 DSH 里看到这样的提示:
dsh-web-search-pro@0.2.3 与 DSH 0.2.2-rc.1 不兼容(要求 @deepseek-ai/dsh-tools>=0.2.0-rc.2 <0.2.1-0 || …
那是 dsh-web-search-pro 的 0.2.3 版本,不是本插件。它的 peerDependencies 把上界写成了 <0.2.2-0,而 DSH 现在跑的是 0.2.2-rc.1,落在范围外,所以被拒绝。本包的声明是 ^0.2.0-rc.1,在 DSH 的判定方式(includePrerelease: true)下覆盖 0.2.2-rc.1:
✓ @deepseek-ai/cordis 实际 4.1.0 声明 ^4.0.2
✓ @deepseek-ai/dsh-tools 实际 0.2.2-rc.1 声明 ^0.2.0-rc.1
✓ @deepseek-ai/schemastery 实际 3.19.0 声明 ^3.18.4
要装本插件,用下面这些方式之一,不要输入 dsh-web-search-pro 这个名字。
装法
从 npm 装(已发布,最省事):
包在 npm 上是 dsh-web-search-lite,走 registry,不需要代理。三种装法任选:
- 打开 插件 → 发现,找到「网络搜索 Lite」点安装(本仓库的索引已配好)
- 在对话里说一句「装 dsh-web-search-lite」,让它调
plugin_manager - 若
dshCLI 已在 PATH 里:dsh plugin add dsh-web-search-lite(桌面版默认不装这个命令)
从仓库装(要改代码、或想跟 main 分支时):
仓库根目录就是插件包本身(package.json 里有 dsh.bundle.patch 与 dsh.client);_dev/ 只有开发与测试脚本,不会被装进 profile。
git clone https://github.com/leiming2333/dsh-web-search-lite.git
cd dsh-web-search-lite
node _dev/install.mjs --dry-run # 先看要改什么
node _dev/install.mjs # 备份后写入 desktop profile
node _dev/install.mjs --profile web # 换一个 profile
node _dev/install.mjs --uninstall # 回滚
脚本只做三件可回滚的事:把包复制到 <profile>/plugins/ 与 <profile>/node_modules/,在 profile 的 package.json 里补 dependencies(file: 本地引用)与 dsh.profile.bundles,改写前备份为 package.json.bak-<时间戳>。它不碰 cordis.patch.yml,那是你自己的层。
也可以在对话里让 DSH 自己装:「把 /path/to/dsh-web-search-lite 这个 bundle 装上」,它会用 plugin_manager 的 install_bundle。
装完什么时候要重启
首次安装必须重启 DeepSeek Harness:profile 的组合列表变了,那是在启动时读的。之后只改插件文件时,DSH 的文件监听会重载宿主半身(host-status.json 里的 applyStartedAt 会跟着更新),浏览器半身仍需刷新页面。
不碰你现有的搜索配置。 插件只注册两个新工具(
web_search_api、web_fetch_api)和一个设置页,内置web_search的提供方选择、官方的「插件 → 网页搜索」页都保持原样。
两者的加载时机不同,容易误导人:刷新页面就能让 设置 → 网络搜索 出现,但那条
/api/web-search-pro/settings请求需要宿主进程里跑着插件。如果设置页出现了却报HTTP 404,先看~/.dsh/web-search-pro/host-status.json:没有这个文件说明宿主没加载过,需要完全退出应用再启动(不是刷新页面)。
配置
设置页里的每一项都存在 <DSH_HOME>/web-search-pro/settings.json(默认 ~/.dsh/web-search-pro/settings.json),改完即时生效,不用重启。
目录名和路由前缀用的是组合行 id web-search-pro,不随包名变化;改包名不会动这个文件。
| 字段 | 默认 | 说明 |
|---|---|---|
provider | aggregate | 用哪个引擎。aggregate(聚合模式)同时查四家免密钥引擎和所有填了 Key 的 API;all 只查免密钥那几家;也可以选单个引擎。 |
apiKeys | 空 | 按提供方分开存的密钥,如 { "tavily": "tvly-…" }。只有填了 Key 的 API 才会被聚合模式调用。 |
endpoint | 空 | 自定义地址,留空用官方地址。设置页给了各家的官方地址作预设,可以直接选,也可以手填(换成中转地址)。聚合模式下它只作用于 SearXNG 和免密钥引擎——各家 API 始终用自己的地址。 |
maxResults | 8 | 每次结果条数(1–50,另受各家上限约束)。 |
timeoutMs | 20000 | 单次请求超时(1000–120000)。 |
language | 空 | 语言提示,如 zh-CN。 |
includeAnswer | true | 是否请求生成式摘要。传给 Tavily;Serper 的 answerBox 本来就随结果返回。 |
enableFetch | true | 是否注册 web_fetch_api。 |
fetchMaxChars | 20000 | 单页保留字符数(1000–200000)。 |
所有值在写入前都会做类型与范围归一化,手改 JSON 也不会把插件带进非法状态。
验证
设置页底部有「测试连接」:用已保存的配置真跑一次搜索,显示提供方、结果条数、耗时和前三条来源;失败就把提供方返回的原因原样显示出来。
也可以回到对话说一句「搜索一下今天的科技新闻」。
架构(以及为什么这么写)
┌─ Host: index.js(零外部依赖,只用 node:fs/os/path)──────┐
│ settings.json ← 原子写入 │
│ ↓ │
│ web_search_api ─→ runSearch ─→ SEARCHERS[id] │
│ web_fetch_api ─→ runFetch ─→ htmlToText │
│ │
│ ctx.inject(['webServer'], …) │
│ └─ /api/web-search-pro/{settings,test} │
└──────────────────────┬───────────────────────────────────┘
│ 同源 JSON
┌──────────────────────┴───────────────────────────────────┐
│ Client: client.js(只 require('react')) │
│ ctx.slots.register('settings.section', …) │
│ 设置 → 网络搜索(读 /settings,写 /settings,/test) │
└──────────────────────────────────────────────────────────┘
关键决定:这个插件不 import 任何 @deepseek-ai/* 包。
原因是加载机制。DSH 加载 profile 里的 bundle 时用「双锚点」解析名字(先 dsh 安装目录,再 profile 目录),但 bundle 内部的 import 由 Node 自己从 bundle 所在位置向上找 node_modules。从 ~/.dsh/profiles/<p>/node_modules/… 出发,DSH 安装目录里的包不在解析链上:
cd ~/.dsh/profiles/desktop
node --input-type=module -e "console.log(import.meta.resolve('@deepseek-ai/dsh-tools'))"
# ERR_MODULE_NOT_FOUND
所以 import { defineTool } from '@deepseek-ai/dsh-tools' 会在加载期抛错,插件加载不起来。本机那个第三方插件 dsh-our-free-model 也是同样的零依赖写法。
由此带来的两个替代:
defineTool是对普通对象字面量的校验/包装。localDefineTool复刻了这份契约:参数 DSL → 对象根 JSON Schema、必填与类型在执行前校验、output.render负责呈现。注册表本身只把它当普通对象读。- 配置不走
settings/configForms那套 schema 投影(那需要 schemastery 的dict/meta结构),改由插件自己的 JSON 文件 +/api/*路由承担。代价是密钥存在自己的状态文件里(见下),收益是设置页能真正做到「测试连接」这种需要在 Host 侧发网络请求的操作。
其他约束:
- 不
require('@deepseek-ai/dsh-client-ui-primitives')(同样解析不到,且规范也禁止)。控件全部自己画,只用--dsw-alias-*主题变量并带 fallback,亮/暗主题都跟着宿主走。 web_fetch_api用when按模型步骤动态准入,而不是在apply时决定;否则关掉开关要重启才生效。- 设置接口做同源校验(
Origin与Host比对),防止第三方页面改写「密钥发往哪个地址」。
安全说明
- 密钥明文保存在
~/.dsh/web-search-pro/settings.json,权限跟随用户目录。没有走 DSH 的 credentials 保管库:那个域只回答「是否存在」,而设置页需要回显掩码预览;更重要的是 credentials 服务不在零依赖可达范围内。密钥不会通过/api/settings回传给浏览器,页面只拿到hasApiKey和abcd…wxyz形式的掩码。 - 清除密钥只能显式点「清除密钥」;输入框留空表示保持原值,避免误清。
web_fetch_api只允许http/https,跟随重定向,非 2xx 作为结果返回(附状态码),不执行页面脚本。- 所有请求都由本地 Host 发起,浏览器不直连搜索 API,所以没有 CORS 问题,密钥也不出现在页面里。
排错
| 现象 | 原因 / 处理 |
|---|---|
| 设置页里没有「网络搜索」这一栏 | 浏览器半身没加载。确认 profile package.json 的 dsh.profile.bundles 含 dsh-web-search-lite,重启并刷新页面。 |
| 设置页显示「读取设置失败」 | 宿主没挂 /api/web-search-pro。先看 DSH 日志里以 web-search-pro: 开头的行,或 ~/.dsh/web-search-pro/host-status.json(也可以跑 node _dev/diagnose.mjs),确认宿主半身跑到了哪一步;常见原因是 Web 服务未启动(该组合下工具仍可用,只是没有设置页)。 |
| 工具报「未知的搜索提供方」 | provider 写了不支持的值,改成错误信息里列出的 id。 |
| 401 / 403 | 密钥无效,或该密钥没有这个接口的权限。 |
| 429 | 额度用尽,或被限流。 |
| 一直超时 | 检查 endpoint;国内直连 Tavily/Brave 可能需要代理,可以把 endpoint 指向自己的转发地址。 |
日志里出现 ERR_MODULE_NOT_FOUND | 有人在插件里加了 @deepseek-ai/* 的 import,见上面的架构说明。 |
测试
node _dev/run-tests.mjs
94 项检查,不需要任何桩包,因为插件本来就没有外部依赖:
- Host(68):清单与 patch 结构、只 import node 内置、两个工具的声明与准入、
localDefineTool的校验、五家 API 与四个抓取引擎的响应解析(含去重/截断/单次覆盖/跳转还原/推广位过滤/实体解码)、聚合的轮转合并与 URL 去重、部分失败与全失败、错误文案分级、HTML→文本、渲染输出、设置的归一化/原子读写/损坏降级、密钥掩码与不回传、挂载自诊断,以及/api的读写/测试/同源拒绝/坏 JSON/前缀剥离。 - Client(26):字典键一致性、
settings.section注册、引擎分组、loading/error/ready 三态渲染、404 时的重启指引与手动配置兜底、免密钥引擎下密钥字段自动禁用、密钥徽标与清除按钮、脏检查与按钮启用、测试结果展示,以及pick/submitted/changesBetween/callApi。
文件
dsh-web-search-lite/ # 仓库根目录就是这个包
├── package.json # bundle 清单 + dsh.client 声明
├── cordis.patch.yml # 插入 web-search-pro 这一行
├── index.js # Host:设置存取、两个工具、provider 适配、HTTP API
├── client.js # Client:设置 → 网络搜索
├── locale/{en,zh}.json # 插件清单里的标题与描述
├── icon.svg # 原创图标
├── LICENSE # MIT
├── CHANGELOG.md # 版本变更
├── README.md # 本文件
├── README.en.md # 英文说明
└── _dev/ # 开发与测试,不进 profile、不进 npm 包
├── install.mjs # 安装 / 卸载 / 预演
├── run-tests.mjs # 跑两个套件
├── live-check.mjs # 对真实网络验证四个免密钥引擎
├── diagnose.mjs # 诊断设置页为什么读不到数据
├── smoke.mjs # Host 套件
└── client-smoke.mjs # 浏览器套件
Comments
Loading…
From the same category
DeepSeek Harness plugin for Reactive Resume: bridges your resumes and job applications into a Harness session over MCP.
★ 41.7k
↓ 121/wk
MIT
Aug 24, 2026
dsh plugin --profile web add dsh-plugin-reactive-resumeby dsh-market
The plugin market inside DeepSeek Harness — browse, search, one-click install · DSH 可视化插件市场
★ 11.6k
↓ 321.4k/wk
MIT
TypeScript
Aug 25, 2026
dsh plugin --profile web add dshmarketby Tencent
Let AI agents use your real, logged-in browser without interrupting your work. CLI + extension for browser automation across any shell-capable AI agent.
★ 8.6k
↓ 15.4k/wk
MIT
TypeScript
Aug 25, 2026
dsh plugin --profile terminal add @wxg-prc-cpg/browser-skill-dsh-pluginby superdesigndev
OpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn
★ 7.8k
NOASSERTION
Python
Aug 25, 2026
dsh plugin --profile web add treg-dshby yjh051108
dsh-routing-suite — injector + router-standard kit: install the runtime injector first, then the task-aware reasoning-mode router preset (measured P1-P23).
★ 7k
MIT
JavaScript
Aug 24, 2026
dsh plugin --profile web add @dsh-external/dsh-super-injectorby Q00
Agent OS: the agent gets smarter on its own. We just hold the line: Interview-gated, staged evaluation, budgeted evolution loop. MCP server, 14 runtimes: Claude Code, Codex CLI, Gemini CLI, OpenCode,
★ 6.2k
MIT
Python
Aug 25, 2026