🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

双路搜索 Minokun/dsh-serper-search

DeepSeek Harness
Minokun11 天前有提交已提供中文文档扫描:含敏感能力

同时检索 DeepSeek 与 Google 索引并合并去重结果

30.8/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/20 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Minokun/dsh-serper-search
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · search
装得上吗
实装验证未通过(unknown),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 11 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
⚠︎ 实装验证未通过(unknown · 2026/10/21) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 2 处,展示前 2 条)
  • 读取本地文件
    lib/index.js:155
    const PKG_VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
  • 发起网络请求
    lib/client.js:250
    const response = await fetch(`${PREFIX}${path}`, {
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

README

由 DeepSeek 最新模型翻译生成
dsh-serper-search DeepSeek Search API(官方自研搜索引擎)+ serper.dev(Google 搜索)双引擎互补的搜索插件,接入 DeepSeek Harness 的 web 能力缝(ctx.web)。安装后,DSH 内置的 web_search 每次只发两次直连检索请求(native:true 且两路可用时;单路缺 key 或 native:false 时只发一次,无辅助模型调用),结果按 URL 去重后以固定比例交错(默认 2:1)合并,让模型同时拿到 DeepSeek 自有索引与 Google 索引。 为什么互补:实测同一组 query 下,DeepSeek Search API 与 Google 的结果 URL 重叠仅 1.3%(同后端重复调用噪声地板 43%–100%,故为真实索引差异)。DeepSeek 侧更快、更贴中文,Google 侧覆盖面更广,两者互补而不互相替代。 v3 不再调用 DeepSeek 原生 Messages 搜索(旧版那一半每次要一整轮模型调用 5.3s / 0.019 元,且返回的是无摘要裸链接)。
展开完整文档(README 剩余 1 节)
dsh-serper-search

DeepSeek Search API(官方自研搜索引擎)+ serper.dev(Google 搜索)双引擎互补的搜索插件,接入 DeepSeek Harness 的 web 能力缝(ctx.web)。安装后,DSH 内置的 web_search 每次只发两次直连检索请求(native:true 且两路可用时;单路缺 key 或 native:false 时只发一次,无辅助模型调用),结果按 URL 去重后以固定比例交错(默认 2:1)合并,让模型同时拿到 DeepSeek 自有索引与 Google 索引。

为什么互补:实测同一组 query 下,DeepSeek Search API 与 Google 的结果 URL 重叠仅 1.3%(同后端重复调用噪声地板 43%–100%,故为真实索引差异)。DeepSeek 侧更快、更贴中文,Google 侧覆盖面更广,两者互补而不互相替代。

v3 不再调用 DeepSeek 原生 Messages 搜索(旧版那一半每次要一整轮模型调用 5.3s / 0.019 元,且返回的是无摘要裸链接)。

v0.4.0 起:成功结果默认缓存 5 分钟(同一 query 重复搜索 0 上游请求)、单路失败按类型冷却并在降级时给模型一行 Note:、设置页多出「测试双路 / 检查更新 / 设为本机默认搜索 / 恢复部署默认」四个按钮;安装后不再需要手改 cordis.patch.yml——首次启动会自动把 searchProvider 指向本插件(写入位置与撤销方式见卸载与恢复)。

截图

设置页:测试双路的结果行

一次真实搜索:双路合并后的 8 条来源

市场详情页的画廊由仓库根的 screenshots.json 声明、夜间构建自动抓取(无需向市场仓库提 PR);图片本身是 assets/shot-settings.png 与 assets/shot-merged.png。

给 AI 的一句话(最快安装)

把这句话原样发给你的 DSH:

安装 dsh-serper-search 并跑一次双路自检,把结果告诉我

Agent 可以按下面五步执行(这也是本节存在的意义:把「装完怎么确认生效」变成一段可复制的话):

1. 安装:dsh plugin --profile web add dsh-serper-search;
2. 配 key(没有 serper key 也能装,只是退化为官方单路):~/.dsh/.credentials.yaml 的 refs: 下加 SERPER_API_KEY: <key> 与 DEEPSEEK_API_KEY: <key>;
3. 重启 dsh web(首次启动会写 pin),再确认:dsh --profile web --dump-config | grep -A4 "^- id: web$",应看到 searchProvider: serper;
4. 新会话调用一次 web_search,确认返回 ≥1 条来源;日志(前缀 web-search-serper:)里应有 merged= 与 cache=store;
5. 「测试双路」是 GUI 里的按钮(POST /api/dsh-serper-search/probe):打开 设置 → 插件 → dsh-serper-search 点一下,把两行结果(1234ms · 3)或错误分类贴回来。Agent 若不能操作浏览器,第 4 步的真实搜索就是验收。

人工验收同样适用:装 → 搜 → 点自检,三步内跑通。若第 4 步结果里出现 Note: serper auth failed ...,说明 serper key 没配好——在卡片里补填或改 key 后冷却立即清除,不需要等冷却到期(auth/quota 10 分钟、rate-limit/server 60 秒、timeout/network 30 秒)。

安装

dsh plugin --profile web add dsh-serper-search

或打开 设置 → 插件市场,搜索 dsh-serper-search 一键安装。

1. 配置两个 key(推荐 credentials 域,GUI 卡片也可填 serper key):
- ~/.dsh/.credentials.yaml 的 refs: 下加 SERPER_API_KEY: <serper key> 与 DEEPSEEK_API_KEY: <deepseek key>(后者通常已有);
- 或启动环境变量 SERPER_API_KEY / DEEPSEEK_API_KEY。
2. 重启 dsh web。首次启动时,如果用户层没有对 searchProvider 表过态、且当前值仍是部署默认(未设置,或等于部署默认 id;旧构建兜底 deepseek-official),插件会自动把 searchProvider: serper 写进 ~/.dsh/profiles/web/cordis.patch.yml,并在自己的条目里写 takeOverProvider: false(一次性标记)。这两行是预期行为,见卸载与恢复。
3. 确认生效:

dsh --profile web --dump-config | grep -A4 "^- id: web$"
看到 searchProvider: serper 即本插件生效

4. 可选:调插件参数(同一个 patch 文件):

- id: web-search-serper
config:
serperTimeoutMs: 10000 # serper 单路超时;本机实测 1.6–11s,默认 4500 偏紧 cacheMinutes: 0 # 关闭结果缓存(默认 5 分钟)

用户层已经写了别的 provider(例如在 GUI 里开过「启用新版 Web 搜索 API」)时,插件不会抢,只打一行 warn;要切回本插件见 FAQ。

环境变量注意:插件自动写入的 searchProvider 落在用户 patch 的 config 层,而 DSH 的选路语义是 config 优先、环境变量仅作兜底——所以这条自动 pin 优先于启动环境里的 $DSH_WEB_SEARCH_PROVIDER。反过来,如果启动时显式设置了 $DSH_WEB_SEARCH_PROVIDER,插件把它当作运维已表态:值为 serper 时才继续接管(必要时补 pin,否则只写标记);是其它 provider 时只打一行 warn 并写一次性标记,不改你的选路。

卸载与恢复

先明确接管写了什么。首次启动的接管会改 ~/.dsh/profiles/<profile>/cordis.patch.yml 里的两处:

- id: web
config:
searchProvider: serper
fetchProvider: http # ← 一并冻结进用户层(值没变,只是随整个 config 被写入)
- id: web-search-serper
config:
takeOverProvider: false # ← 一次性标记:以后不再自动接管

fetchProvider: http 一起被写进用户层是 configEditor.edit() 写完整 config 的副作用,值没有变化,保留默认即可。因为有 takeOverProvider: false 这个标记,下面三条恢复路径重启后都不会被重新接管:

1. 设置 → 网页搜索,在官方设置页切换 provider(切到 deepseek-official 或 deepseek-search-api);
2. 插件卡片 →「恢复部署默认」:清掉用户层的 searchProvider({op:'unset'}),回到继承值;
3. 手删:删掉 - id: web 里的 searchProvider 行,或整行 - id: web。

想重新用本插件:卡片点「设为本机默认搜索」,或手写 searchProvider: serper(也可以把 takeOverProvider 改回 true 让下次启动重新判定一次)。

卸载/禁用:

- 推荐顺序:先点「恢复部署默认」,再 dsh plugin --profile web remove dsh-serper-search → 搜索照常走官方;
- 直接删插件(pin 还留在 patch 里)→ 启动/搜索报 provider "serper" is not registered。这是已知状态,插件没有卸载钩子、不承诺自愈;删掉 searchProvider 那行(或整行 - id: web)即恢复;
- 只禁用条目(bundle 还在、pin 已写)同理;
- 卸载后重装:卸载不会删除用户 patch 里本插件条目的 takeOverProvider: false(一次性标记仍有效),所以重装后不会自动接管;点卡片「设为本机默认搜索」,或删掉该字段后重启,才会重新判定。

老用户注意:0.4.0 之前手动 pin 过 serper 的 profile,升级后会被识别为「用户已表态」——插件不改你的 pin,只补写 takeOverProvider: false。也就是说以后你删掉这行 pin,插件不会再自动接管(这正是 0.4.0 的设计:恢复过的状态不被抢回)。

GUI 设置页

Plugins → dsh-serper-search bundle 详情页提供基础配置:serper API key(写入 credentials 域,不落设置文件)、接口地址、gl、hl、每次结果数、是否合并 DeepSeek Search API(native)。

卡片底部是 0.4.0 新增的运行时面板,四个按钮:

| 按钮 | 做什么 | 说明 |
| 测试双路 | 并行探测两路,各发 1 次真实请求(每路 3 条) | 成功显示 1234ms · 3;失败显示分类 + HTTP 状态(如 auth (HTTP 401))。两路都成功才显示「两路均正常」。每次消耗 1 次 serper + 1 次 DeepSeek 搜索;并发点击共享同一次探测(单飞),两路各自的请求预算临时置为 8s(并行,最坏约 8s);探测成功会清该路冷却,失败不写冷却 |
| 检查更新 | 由部署端读 registry.npmjs.org,与本机版本比 semver | 结果显示「已是最新」或 有新版本 0.3.4 → 0.4.0 · npm(链接可点);结果缓存 10 分钟;不走浏览器,无 CORS;不做一键升级(会破坏 link: 开发安装) |
| 设为本机默认搜索 | 写 searchProvider: serper(并写一次性标记) | 立即生效,无需重启 |
| 恢复部署默认 | {op:'unset'} 清掉 pin(并写一次性标记) | 清掉后重启也不会被重新接管 |

- 仅用 DeepSeek 账号登录(没有 API key)时,官方那一路在 HTTP 路由里没有 initiator,会显示专属文案「账号登录态只在对话内生效」,不算 auth 失败;
- 部署缺少 webServer / connection / settings 任一服务时,插件不注册这三条路由,面板显示「当前部署未提供自检接口」;
- 三条路由都是 POST-only、走 DSH 自带的 connection fence(Host/Origin + 登录 cookie);只有 /provider 读请求体(JSON 对象,仅接受 {action:"take"} / {action:"release"};上限 64 KiB,超限 413、坏 JSON 或非法 action 400 且不写设置),响应里不含任何凭据。

ratio、primary、summaryMaxChars、TimeoutMs、autoLocale、minAuthority、maxRetries、cacheMinutes 等高级项不进 GUI,通过 profile 的 cordis.patch.yml 覆盖:

- id: web-search-serper
config:
ratio: 1.5 # 5 主 + 3 补(默认 2 = 6 主 + 2 补) summaryMaxChars: 800 # 官方长摘要注入 budget;0 = 关闭 autoLocale: true # 纯英文 query 自动 gl=us/hl=en primary: deepseek # 或 serper serperTimeoutMs: 4500 cacheMinutes: 5 # 0 = 关闭结果缓存

配置项(settings 命名空间 web-search-serper)

| 字段 | 默认 | 说明 |
| apiKey | — | serper 明文 key(volatile,优先于 credentials) |
| apiKeyEnv | SERPER_API_KEY | serper key 的 credentials 引用名 |
| baseURL | https://google.serper.dev | 也可用环境变量 SERPER_BASE_URL |
| gl / hl | cn / zh-cn | Google 国家/地区与界面语言 |
| autoLocale | true | 纯英文 query 自动切 gl=us, hl=en |
| num | 10 | serper 每次请求条数下限(1–100) |
| serperTimeoutMs | 4500 | serper 单路超时(冷态折中值) |
| deepseekApiKey / deepseekApiKeyEnv | — / DEEPSEEK_API_KEY | 官方 API key 或凭据引用 |
| deepseekBaseURL | https://api.deepseek.com/v1/tools | 可用 DEEPSEEK_SEARCH_API_BASE_URL 覆盖 |
| deepseekCount | 10 | 官方每次请求条数下限(1–50) |
| deepseekTimeoutMs | 4500 | 官方单路超时的硬 deadline:首次请求 + 重试退避共用这一预算,单次搜索总耗时 ≤ timeoutMs |
| summaryMaxChars | 600 | 官方 summary 注入 snippet 的最大字符数;0 关闭 |
| primary | deepseek | 主导路(决定交错前排与去重保留优先级) |
| ratio | 2 | 槽位比例,五档白名单 1 / 1.5 / 2 / 3 / 4 |
| minAuthority | -1 | 官方结果 authority 下限(-1 不过滤) |
| maxPerHost | 2 | 单域名最多占几条(0 = 不限制);保持来源多样性 |
| demoteSocial | true | reddit / x.com / HN / YouTube 等社交视频源排到编辑类来源之后 |
| maxRetries | 1 | 官方 429/5xx 重试次数 |
| logLevel | summary | off / summary / verbose |
| native | true | 兼容字段:v3 中含义为「是否合并 DeepSeek Search API」;false = 纯 serper |
| cacheMinutes | 5 | 成功结果缓存分钟数(0–5);0 关闭。降级结果的缓存时长取 1/5(不低于 60s),abort 与失败不缓存 |
| takeOverProvider | true | true = 下次启动允许一次性接管 searchProvider;接管判定后插件自动写 false。改回 true 可再允许一次 |
| nativeApiKey / nativeApiKeyEnv / nativeBaseURL / nativeModel / nativeMaxUses | — | v0.2.x 兼容保留:新字段未配置时 key 作为 fallback,其余运行时忽略 |

工作原理

- 注册一个 id 为 serper 的复合 WebSearchProvider(id 保持兼容,现有 profile 无需改配置),内部两路直连 HTTP后端并行:
- DeepSeek Search API(主路):POST ${deepseekBaseURL}/search(默认 https://api.deepseek.com/v1/tools/search),鉴权为 Authorization: Bearer <DEEPSEEK_API_KEY>;若是 DeepSeek Account 路由会话则改发 x-dsh-auth-token(与 DSH 内置 provider 一致);
- serper.dev(副路):POST ${baseURL}/search,请求头 X-API-KEY,body {q, gl, hl, num};answerBox 直接答案映射为结果级 content。
- 映射增强:
- 官方 summary(实测均长 1893 字符)折叠空白后截断到 summaryMaxChars(默认 600)写入 snippet,把官方内置插件丢弃的长摘要捡回来;设 0 则回退原始短 snippet(231 字符);
- 官方 date → publishedAt;serper 中文日期(2026年8月13日)→ ISO;
- 官方 authority 支持 minAuthority 过滤(保留 -1 未知站点);
- 纯英文/代码 query(autoLocale 默认开启、gl=cn 时)自动切到 gl=us, hl=en:实测英文 query 延迟从 3.6–10.5s 降到 1.0–1.7s。
- 合并:按归一化 URL 去重(去掉 fragment / utm_ / spm_ / 百度百科等追踪参数;chapter、channel、from=20 等业务参数不误删)→ v3.1 质量规则(单域名最多 maxPerHost: 2 条、同页 /de/ /fr/ 本地化变体折叠、reddit/x.com/HN/YouTube 等社交视频源降权到编辑类来源之后)→ 按固定比例模式交错 → 截到 maxResults(工具层默认 8/12)→ 返回 truncated: false(单 query 时避免工具给模型追加 "Refine the query for more" 诱导重搜的尾注;多 query 时工具层轮转仍可能自行追加,插件无法控制)。
- 降级:任一路失败/超时/缺 key,另一路独立成答;两路都失败才报错。每路独立超时(官方 4500ms / serper 4500ms,重试与首次请求共享同一预算),官方 429/5xx 有 1s→2s 指数退避重试;超时/取消覆盖到响应体读取阶段。降级时 content 顶部会多一行模型可见的 Note:(见下),UI 卡片会把 content 当作答案显示,所以这行也会出现在界面上——已知且接受。
- 结果缓存(v0.4.0):成功结果进 50 条 LRU,默认 5 分钟(cacheMinutes,0 关闭)。键覆盖所有影响输出的设置(query / maxResults / primary / ratio / maxPerHost / demoteSocial / gl / hl / autoLocale / num / native / deepseekCount / summaryMaxChars / minAuthority / serper.baseURL / deepseek.baseURL),命中时 0 上游请求并在日志打 cache=hit len=N;未命中打完请求后打 cache=store。降级结果只缓存 max(60s, TTL/5),避免把"暂时坏掉的一路"钉住 5 分钟;冷却被清除(改 key、探测成功等)时缓存一并失效,abort 与失败永不入缓存。
- 失败分类与冷却(v0.4.0):非 2xx / 超时 / 网络错误先分类再决定是否静音该路;某路冷却期间由另一路成答,content 顶部加一行 Note:,日志打 cooldown <lane>=<class> until=+Ns fp=xxxxxxxxxxxx。两路同时冷却时 fail-open 仍请求两路(宁可慢,也不让搜索彻底不可用)。

| 分类 | 触发 | 冷却 |
| auth | 401 / 403 / 凭据缺失 | 10 分钟 |
| quota | 402,或 429 且正文含 quota/credit/balance/insufficient | 10 分钟 |
| rate-limit | 429 且不是 quota | 60 秒 |
| server | 5xx | 60 秒 |
| timeout | 本路预算耗尽 | 30 秒 |
| network | fetch 抛错(DNS / 连接重置 / 证书) | 30 秒 |
| invalid-response | JSON 解析失败 / 结构不符 / 表外 4xx | 不冷却 |
| empty | 200 但 0 条 sources | 不冷却 |

冷却在成功、配置指纹变化(字面 key / apiKeyEnv / baseURL 任一变化)、credentials/reference-updated 事件(在卡片里改 key、补填缺失 key 走这条)、探测成功(「测试双路」)或到期时清除。降级 Note 形如:

Note: serper auth failed (HTTP 401), using deepseek.
Note: serper timed out after 4500ms, using deepseek.
Note: deepseek quota exhausted, using serper.

- 比例档位(8 槽位最终构成):4 → 7主+1补、3 → 6+2、2 → 6+2(默认)、1.5 → 5+3、1 → 4+4,可在 patch 里配置。

FAQ

Q:装完搜索没变化,还是走官方? 先 dsh --profile web --dump-config | grep -A4 "^- id: web$" 看 searchProvider。如果是别的 id,说明用户层已经表过态(例如开过 GUI 的「启用新版 Web 搜索 API」),插件按设计不抢,启动日志里会有一行 searchProvider is set to "..." in your profile patch; leaving it alone.。改法:点卡片「设为本机默认搜索」,或手写:

- id: web
config:
searchProvider: serper
fetchProvider: http

Q:为什么只有一半来源,或者结果顶上有一行 Note:? 那一路失败了(key 错、额度用尽、超时、网络),另一路单独成答并在 content 顶部加了说明。修好之后冷却会立即清除:在卡片里改/补 key 触发 credentials/reference-updated;或点「测试双路」探测成功;或等冷却到期(auth/quota 10 分钟、rate-limit/server 60 秒、timeout/network 30 秒)。

Q:重复搜同一个 query,结果一模一样而且很快? 命中了结果缓存(默认 5 分钟)。要每次都实时检索就设 cacheMinutes: 0。降级结果只缓存约 1 分钟,不会把你钉在"半边结果"上。

Q:卸载后搜索报 provider "serper" is not registered?
见卸载与恢复:删掉 searchProvider: serper 那行即可。这是接管把 pin 写进你自己的 patch 文件带来的已知状态,不是搜索链路本身的问题。

Q:需要关掉内置的搜索 provider 吗?
不需要,见与内置搜索 provider 共存。

Q:怎么临时关掉自动接管? 在 - id: web-search-serper 条目里设 takeOverProvider: false;改回 true 会在下次启动重新判定一次(用户层已表过态时仍然不抢)。

Q:0.4.0 之前我是手动 pin 的,升级后要做什么? 什么都不用做。插件识别到用户层已有 pin,只补写 takeOverProvider: false,不覆盖你的值——代价是以后你删掉 pin,它不会再自动接管(想再用就点「设为本机默认搜索」)。

与 DSH 内置官方 provider 的关系

DSH 0.2.1+ 自带 deepseek-search-api(官方 Search API 单飞)与 deepseek-fetch-api。不需要 Google 源时直接用内置 provider 更省事:

- id: web
config:
searchProvider: deepseek-search-api
fetchProvider: http

本插件的价值仅在「官方 + Google 双索引互补」:默认 8 条来源中 6 条来自官方、2 条来自 Google,质量抽样与纯官方持平(1.792 vs 1.792),但多出 2 个 Google 独家源。若把本插件从 profile 移除,记得删掉指向 serper 的 searchProvider 覆盖。

与内置搜索 provider 共存(不需要关闭任何东西)

DSH 的 ctx.web 是「多 provider 注册、按配置选一个」。内置的 deepseek-official(原生 Messages 搜索)与 deepseek-search-api(官方 Search API)即使不卸载也只是注册在 seam 里,不参与选路——不会被调用、不产生费用、也不与本插件冲突。所以安装本插件后:

- 不需要卸载或禁用 @deepseek-ai/dsh-web-search-deepseek / dsh-web-deepseek-api(关掉反而会让 GUI 的网页搜索设置页/切换逻辑指向不存在的 id,报 WEB_PROVIDER_CONFIGURED_MISSING);
- 必须保留 @deepseek-ai/dsh-tool-web:web_search / web_fetch 工具本体在那里,本插件只提供搜索源,关掉就没有可调用的工具了;
- fetchProvider 保持默认 http 即可(本插件不提供抓取;官方 Fetch API 的 Markdown 带反斜杠转义,DSH 内置 fetch provider 不做反转义);
- GUI「插件 → 内置插件配置 → 网页搜索」里的「启用新版 Web 搜索 API」开关写入的是用户级覆盖:打开切到 deepseek-search-api,关掉切到 deepseek-official。0.4.0 起插件不会把你切回来(一次性接管标记已写),要回来用卡片「设为本机默认搜索」或手写 searchProvider: serper(见 FAQ)。

来源条数上限(8 → 12/N,为什么不在插件里)

web_search 一次调用最终保留几条来源,由工具层 @deepseek-ai/dsh-tool-web 的 searchMaxResults 决定(默认 8),而 tool-web 是挂在 agent preset(默认 standard)里的,不在本插件配置里——插件只按工具层传入的 maxResults 返回结果,无法自行突破。

为什么不能只加一行配置:profile patch 对某个插件的 config 是整体替换、不能深合并嵌套字段,所以要覆盖整个 preset-standard。仓库自带生成脚本(从当前 dsh 的 --dump-config 重建覆盖块,写入 profile patch 的标记区):

node scripts/sync-dsh-preset-cap.mjs 12 # 或 npm run sync-preset-cap -- 12

- 脚本会在 ~/.dsh/profiles/web/cordis.patch.yml 里写入/替换 # >>> dsh preset-standard override >>> 标记区(169 行,含完整 preset 插件列表);
- DSH 升级后重跑一次,避免 pin 住的旧 preset 缺少新插件;
- 只支持 standard preset;若你在 GUI 切换了其它 preset(ptc / cordis),那边的 tool-web 仍是 8 条。

| N | 8 槽位构成 | 每次搜索增量成本 | 适用 |
| 8(默认) | 6 官方 + 2 Google | — | 日常 |
| 10 | 7 + 3 | 约 +0.3k 上下文 token | 轻微加广 |
| 12(推荐) | 8 + 4 | 约 +0.5k token | 调研 |
| 16 | 11 + 5 | 约 +1k token | 深调研;第 13 名后噪声上升 |
| 20+ | 14+ + 6+ | +1.5k 以上 | 不建议 |

关键事实:两个搜索 API 都按请求次数计费,count 条数不影响单价、也不显著影响延迟——扩大 N 只增加模型上下文 token,不增加 API 费用。serper 单次最多返回 10 条,N≤20 时副路槽位够用。
生效条件:重启 dsh web,并且 agent preset 在 Agent 创建时读取 → 只对新会话生效(已存在的会话保持旧上限)。不想 pin preset 时,也可以保持默认 8 条,把调研拆成两次搜索(API 费翻倍)。

合并质量规则(v3.1,来自两个真实调研会话的复盘)

对「DeepSeek V4.1 Flash 排名调研」与「聊天软件对比」两个会话做了来源归因复盘后,加入三条 merge 规则(均可配):

| 规则 | 默认 | 解决的实测问题 |
| maxPerHost 单域名上限 | 2 | 某排名调研里 artificialanalysis.ai 的 4 个本地化 URL(中/葡/德/英)占了 4/12 席;现在实测 max/host = 2 |
| 本地化变体去重 | 开 | 同页 /de/blog/x 与 /fr/blog/x 折叠为一个(仅当两者都带 locale 前缀,白名单匹配,不误伤 /id/… 业务路径) |
| demoteSocial 社交降权 | true | 英文技术 query 前排常被 reddit/x.com/HN/YouTube 占据;现在它们排在编辑类与官方来源之后,只填剩余席位 |

复盘结论:serper 的独家价值主要在官方文档/模型页(企业微信开发者文档、钉钉 Stream、core.telegram.org、HuggingFace 模型页等,DeepSeek 索引没有),弱点正是上述三类噪声——v3.1 针对的是噪声,不动互补比例。

成本与延迟(实测,2026-10-17)

| 方案 | 每次搜索 | 延迟 p50 | 说明 |
| 本插件 v3(官方主 + serper 补) | 0.0122 元(官方 0.005 + serper 0.0072) | 1.8–2.9s(serper 决定;英文 query ~1.5s) | 8 条 = 6 官方 + 2 Google |
| 官方 Search API 单飞(内置 provider) | 0.005 元 | 0.34s | 8 条全官方,无 Google 源 |
| 纯 serper(native: false) | 0.0072 元 | 1.9–2.9s | 8 条全 Google |
| v0.2.2(serper + 原生 Messages) | 0.0263 元 | 5.3s | 8 条里 2 条裸链接 |

- 5 分钟窗口内重复同一 query = 0 次上游请求(结果缓存,cacheMinutes 默认 5、可设 0–5;降级结果缓存更短):那一次检索的成本与尾延迟都归零;
- 官方 Search API 计费:5 元 / 1000 次成功请求(失败/空结果/429 不计费),QPS 5/账号;来源条数(8/12/16)不影响 API 单价,只影响上下文 token(12 条约 +0.5k/次);
- 本机 serper 延迟方差极大(实测 1.6–11s):serperTimeoutMs 默认 4500ms 下,慢网/冷启动时 serper 会超时降级为纯官方(结果仍可用,但拿不到 2 条 Google 源);需要更高 Google 源命中率可调到 6000–8000ms,代价是尾部延迟同比例上升。超时后该路静音 30 秒,不会每次都等满超时;
- 2026-11-01 起:用 DeepSeek 模型 API 时「服务端代调 Search/Fetch」的隐形免费链路也开始按次收费;本插件走直连接口,成本从第一天起可精确预算。

搜索纪律(建议写进全局 ~/.dsh/AGENTS.md)

每次 web_search 都会真实消耗官方 API 额度与 serper credit(成本 = 查询词数 × 0.0122 元,缓存命中除外):

1. 一次调用承载一个调研角度:把该角度的查询词合并进一次调用(最多 4 个),不要为同义改写拆成多次调用。
2. 查询词之间必须有实质差异(不同语言/平台/技术栈/子问题);同义改写的 Google 结果重叠率不足 7%,属于纯浪费。
3. 先规划后搜索:动手前想清楚需要哪几个角度,简单事实类问题只搜 1 次;只有明确「调研」时才多角度多轮搜索。
4. 够答就停:来源足够时不再追加搜索,不要因为「再确认一下」重复检索。

开发

pnpm install
pnpm link-harness # 把类型/依赖对齐到本机已安装的 dsh
pnpm build # 构建 client(含 client-id 一致性门禁)
pnpm typecheck
pnpm test # node --test(纯函数 + 合并/降级/重试/abort/缓存/冷却/接管/路由单测)
pnpm sync-preset-cap -- 12 # 可选:把 web_search 来源上限提到 12(改 profile patch 的 preset 覆盖)

先跑离线断言(缓存命中 / 降级 Note / 冷却,注入 stub、不需要 key、不花额度)
node scripts/verify-live.mjs

再加真实 API 冒烟(需要两个 key,会产生少量费用)
set -a; . /tmp/keys.env; set +a
node scripts/verify-live.mjs

Host 端 lib/index.js 与 lib/merge.js 是手写纯 ESM,不经 tsdown 编译;tsdown/tsc 只处理浏览器端 src/client/ → lib/client.js。改 Host 逻辑后跑 node --check lib/index.js + pnpm test。

0.4.0 的 Host 侧还包含 lib/cache.js(LRU + TTL)、lib/failure.js(分类/冷却/指纹/脱敏)、lib/routes.js(三条运行时路由 + 更新检查),浏览器侧新增 src/client/runtime-panel.tsx。

npm 包的 package.json files 是显式白名单(六个 Host/browser JS + cordis.patch.yml + README.md + CHANGELOG.md),不含 lib/client.js.map;以后新增随包发布的 lib 文件要同步加进 files。

变更记录见 CHANGELOG.md;市场分类 PR 的标题/正文/改动行见仓库根的 category-browser-PR.md(仅供人工提交,不随 npm 包发布)。

从 v0.2.x 升级

- provider id、cordis.patch.yml 的 searchProvider: serper 均不变,原地升级、无需改 profile;
- native 语义变为「是否合并 DeepSeek Search API」(false = 纯 serper,行为等同于旧版 native: false);
- 旧 nativeApiKey / nativeApiKeyEnv 在新 deepseekApiKey* 未配置时作为 fallback;
- nativeBaseURL(旧版指向 /anthropic/v1)不再使用,官方端点默认 https://api.deepseek.com/v1/tools;
- 不再需要 nativeModel / nativeMaxUses(v3 不调用模型)。

从 v0.3.x 升级(0.4.0)

- 无需改配置档:provider id、pin 方式都不变;若你曾手动 pin,插件只补写 takeOverProvider: false,不动你的值;
- 新配置都有默认值(cacheMinutes: 5、takeOverProvider: true),不配置即生效;
- 新的运行时路由要求部署带 webServer / connection / settings(正常 dsh web 部署都有);缺任何一个时面板提示“不支持自检”,不影响搜索;
- 刷新浏览器页面让新的 client bundle(含运行时面板)加载。

DeepSeek Harness 合并式网页搜索,v0.4:一个 ctx.web provider(serper)并行跑两路直接 HTTP 检索——DeepSeek Search API(/v1/tools/search,主路)和 serper.dev 的 Google 结果(副路)——按归一化 URL 去重,按固定比例交错(默认 2:1 → 工具层八个槽位里六个 DeepSeek 源加两个 Google 源),再截断到 maxResults,并置 truncated: false。不额外跑模型回合:每次搜索、每个后端只发一个请求。

v0.4 在这套合并之上加了三项行为:结果缓存(50 条 LRU,默认五分钟,cacheMinutes: 0 关闭——窗口内重复查询不产生任何上游请求;命中时日志打 cache=hit)、失败分类与冷却(鉴权/配额 10 分钟,限流/服务端 60 秒,超时/网络 30 秒;一路失败就静音,另一路照常作答,模型可见的 content 多一行 Note:,例如 Note: serper auth failed (HTTP 401), using deepseek.)、以及设置自检(测两路、查 npm 有无更新、pin 或释放本机的 searchProvider)。冷却在以下情况清除:成功、凭据指纹变化、收到 credentials/reference-updated 事件、探测成功、到期;只要有人工干预清掉冷却,缓存结果一并丢弃,降级答案不会比修复活得更久。两路都在冷却时按 fail-open 处理。

安装:dsh plugin --profile web add dsh-serper-search,在 ~/.dsh/.credentials.yaml 的 refs: 下加 SERPER_API_KEY 和 DEEPSEEK_API_KEY(或在启动环境里设置),然后重启 dsh web。不再需要手动 pin:首次启动时若发现 web.searchProvider 未设置(或仍是部署默认值),插件会往配置档自己的 cordis.patch.yml 写入 searchProvider: serper,并在自己的条目里记下 takeOverProvider: false,这样以后重置也不会被再次接管。若你的用户层已经 pin 了别的 provider,插件不动它,只打一条警告。环境变量方面注意:DSH 里配置优先于环境变量(环境变量只是兜底),所以自动 pin 会覆盖 $DSH_WEB_SEARCH_PROVIDER;反过来,显式设置的 $DSH_WEB_SEARCH_PROVIDER 视为运维决定——只有它是 serper 时插件才继续接管(必要时 pin),否则打警告、只写一次性标记,选择保持原样。用 dsh --profile web --dump-config | grep -A4 "^- id: web$" 验证。
卸载 / 恢复:接管会把 searchProvider: serper 写进你的配置档补丁,同时写入整个 web 配置,因此 fetchProvider: http 也会一并被冻结——值不变。想改回去,有三种办法:在 GUI 搜索设置里切换服务商,点插件卡片上的「restore deployment default」,或者手动删掉 searchProvider 这一行;因为有一次性标记,这三种操作都不会再次触发接管。如果删掉插件却留着这条固定项,会报 provider "serper" is not registered——这是已知状态:删掉那一行即可。想重新启用这个插件,点「use as this machine's default search」,或者手动写入固定项。卸载后重装时,如果用户补丁里还留着 takeOverProvider: false,就不会再次接管(该标记在卸载后依然存在):点「use as this machine's default search」,或者删掉这个字段再重启。

你不需要禁用或卸载内置搜索服务商。 ctx.web 会注册多个服务商,并使用 searchProvider 固定的那一个;内置的 deepseek-official / deepseek-search-api 服务商保持挂载但空闲(不调用、不产生费用)。保留 @deepseek-ai/dsh-tool-web 的安装(它负责 web_search / web_fetch 工具),并让 fetchProvider: http 保持不变。

重启 dsh web。GUI 卡片保留六个基础控件加四个运行时按钮;高级设置(ratio、primary、summaryMaxChars、超时、autoLocale、minAuthority、maxRetries、cacheMinutes)通过 cordis.patch.yml 打补丁。单次搜索的来源上限(默认 8)属于智能体预设里的 dsh-tool-web.searchMaxResults,不属于这个插件;scripts/sync-dsh-preset-cap.mjs 12 会重新生成 preset-standard 覆盖块,把上限提到 12(需要重启并新建会话;API 费用按请求计,所以上限只影响上下文 token)。

费用:每次搜索 0.0122 CNY(DeepSeek 0.005 + serper 约 0.0072),延迟主要来自 serper(1.8–2.9 s);缓存窗口内重复查询不花钱。如果不需要 Google 的覆盖范围,内置的 deepseek-search-api 服务商更快(0.34 s)也更便宜;只有想合并两个索引时才用这个插件。

v3.1 新增了从两次真实研究会话中测得的合并质量规则:maxPerHost(默认 2)阻止单个站点霸占结果列表(/de/… 与 /fr/… 这类本地化变体会先合并),demoteSocial(默认 true)把 reddit/x.com/HN/YouTube 这类站点排到编辑内容和官方来源之后。两者都是带默认值的仅补丁设置,升级无需改配置。

开发:pnpm build、pnpm typecheck、pnpm test(node --test),以及 scripts/verify-live.mjs 用于真实 API 冒烟检查(它的缓存/冷却/降级断言跑在离线 stub 传输上,不需要密钥;只有 live 部分消耗额度)。宿主文件 lib/index.js 和 lib/merge.js 是手写的 ESM(不由 tsdown 构建);改完它们后运行 node --check 加单元测试。

如果这个插件对你有用,欢迎给个 ⭐ star——这是最省事的支持。
预览分享卡片长什么样 ↓Minokun/dsh-serper-search
分享 · DeepSeek Harness Hub
Minokun/dsh-serper-search

同时检索 DeepSeek 与 Google 索引并合并去重结果

回到顶部